Blame view
drivers/nvmem/mxs-ocotp.c
4.03 KB
c942fddf8 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-or-later |
c01e9a11a nvmem: add driver... |
2 3 4 5 6 7 |
/* * Freescale MXS On-Chip OTP driver * * Copyright (C) 2015 Stefan Wahren <stefan.wahren@i2se.com> * * Based on the driver from Huang Shijie and Christoph G. Baumann |
c01e9a11a nvmem: add driver... |
8 9 10 11 12 13 14 15 16 17 |
*/ #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/io.h> #include <linux/module.h> #include <linux/nvmem-provider.h> #include <linux/of_device.h> #include <linux/platform_device.h> |
c01e9a11a nvmem: add driver... |
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <linux/slab.h> #include <linux/stmp_device.h> /* OCOTP registers and bits */ #define BM_OCOTP_CTRL_RD_BANK_OPEN BIT(12) #define BM_OCOTP_CTRL_ERROR BIT(9) #define BM_OCOTP_CTRL_BUSY BIT(8) #define OCOTP_TIMEOUT 10000 #define OCOTP_DATA_OFFSET 0x20 struct mxs_ocotp { struct clk *clk; void __iomem *base; struct nvmem_device *nvmem; }; static int mxs_ocotp_wait(struct mxs_ocotp *otp) { int timeout = OCOTP_TIMEOUT; unsigned int status = 0; while (timeout--) { status = readl(otp->base); if (!(status & (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR))) break; cpu_relax(); } if (status & BM_OCOTP_CTRL_BUSY) return -EBUSY; else if (status & BM_OCOTP_CTRL_ERROR) return -EIO; return 0; } |
7d8867d71 nvmem: mxs-ocotp:... |
57 58 |
static int mxs_ocotp_read(void *context, unsigned int offset, void *val, size_t bytes) |
c01e9a11a nvmem: add driver... |
59 60 |
{ struct mxs_ocotp *otp = context; |
c01e9a11a nvmem: add driver... |
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
u32 *buf = val; int ret; ret = clk_enable(otp->clk); if (ret) return ret; writel(BM_OCOTP_CTRL_ERROR, otp->base + STMP_OFFSET_REG_CLR); ret = mxs_ocotp_wait(otp); if (ret) goto disable_clk; /* open OCOTP banks for read */ writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_SET); /* approximately wait 33 hclk cycles */ udelay(1); ret = mxs_ocotp_wait(otp); if (ret) goto close_banks; |
7d8867d71 nvmem: mxs-ocotp:... |
83 |
while (bytes) { |
c01e9a11a nvmem: add driver... |
84 85 |
if ((offset < OCOTP_DATA_OFFSET) || (offset % 16)) { /* fill up non-data register */ |
7d8867d71 nvmem: mxs-ocotp:... |
86 |
*buf++ = 0; |
c01e9a11a nvmem: add driver... |
87 |
} else { |
7d8867d71 nvmem: mxs-ocotp:... |
88 |
*buf++ = readl(otp->base + offset); |
c01e9a11a nvmem: add driver... |
89 |
} |
7d8867d71 nvmem: mxs-ocotp:... |
90 91 |
bytes -= 4; offset += 4; |
c01e9a11a nvmem: add driver... |
92 93 94 95 96 97 98 99 100 101 102 |
} close_banks: /* close banks for power saving */ writel(BM_OCOTP_CTRL_RD_BANK_OPEN, otp->base + STMP_OFFSET_REG_CLR); disable_clk: clk_disable(otp->clk); return ret; } |
c01e9a11a nvmem: add driver... |
103 104 |
static struct nvmem_config ocotp_config = { .name = "mxs-ocotp", |
7d8867d71 nvmem: mxs-ocotp:... |
105 106 |
.stride = 16, .word_size = 4, |
7d8867d71 nvmem: mxs-ocotp:... |
107 |
.reg_read = mxs_ocotp_read, |
c01e9a11a nvmem: add driver... |
108 |
}; |
7d8867d71 nvmem: mxs-ocotp:... |
109 110 |
struct mxs_data { int size; |
c01e9a11a nvmem: add driver... |
111 |
}; |
7d8867d71 nvmem: mxs-ocotp:... |
112 113 |
static const struct mxs_data imx23_data = { .size = 0x220, |
c01e9a11a nvmem: add driver... |
114 |
}; |
7d8867d71 nvmem: mxs-ocotp:... |
115 116 |
static const struct mxs_data imx28_data = { .size = 0x2a0, |
c01e9a11a nvmem: add driver... |
117 118 119 |
}; static const struct of_device_id mxs_ocotp_match[] = { |
7d8867d71 nvmem: mxs-ocotp:... |
120 121 |
{ .compatible = "fsl,imx23-ocotp", .data = &imx23_data }, { .compatible = "fsl,imx28-ocotp", .data = &imx28_data }, |
c01e9a11a nvmem: add driver... |
122 123 124 |
{ /* sentinel */}, }; MODULE_DEVICE_TABLE(of, mxs_ocotp_match); |
bbde5709e nvmem: mxs-ocotp:... |
125 126 127 128 |
static void mxs_ocotp_action(void *data) { clk_unprepare(data); } |
c01e9a11a nvmem: add driver... |
129 130 131 |
static int mxs_ocotp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; |
7d8867d71 nvmem: mxs-ocotp:... |
132 |
const struct mxs_data *data; |
c01e9a11a nvmem: add driver... |
133 |
struct mxs_ocotp *otp; |
c01e9a11a nvmem: add driver... |
134 |
const struct of_device_id *match; |
c01e9a11a nvmem: add driver... |
135 136 137 138 139 140 141 142 143 |
int ret; match = of_match_device(dev->driver->of_match_table, dev); if (!match || !match->data) return -EINVAL; otp = devm_kzalloc(dev, sizeof(*otp), GFP_KERNEL); if (!otp) return -ENOMEM; |
794a1e229 nvmem: mxs-ocotp:... |
144 |
otp->base = devm_platform_ioremap_resource(pdev, 0); |
c01e9a11a nvmem: add driver... |
145 146 147 148 149 150 151 152 153 154 155 156 157 |
if (IS_ERR(otp->base)) return PTR_ERR(otp->base); otp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(otp->clk)) return PTR_ERR(otp->clk); ret = clk_prepare(otp->clk); if (ret < 0) { dev_err(dev, "failed to prepare clk: %d ", ret); return ret; } |
bbde5709e nvmem: mxs-ocotp:... |
158 159 160 |
ret = devm_add_action_or_reset(&pdev->dev, mxs_ocotp_action, otp->clk); if (ret) return ret; |
7d8867d71 nvmem: mxs-ocotp:... |
161 |
data = match->data; |
c01e9a11a nvmem: add driver... |
162 |
|
7d8867d71 nvmem: mxs-ocotp:... |
163 164 |
ocotp_config.size = data->size; ocotp_config.priv = otp; |
c01e9a11a nvmem: add driver... |
165 |
ocotp_config.dev = dev; |
7d9f9f24f nvmem: mxs-ocotp:... |
166 |
otp->nvmem = devm_nvmem_register(dev, &ocotp_config); |
bbde5709e nvmem: mxs-ocotp:... |
167 168 |
if (IS_ERR(otp->nvmem)) return PTR_ERR(otp->nvmem); |
c01e9a11a nvmem: add driver... |
169 170 171 172 |
platform_set_drvdata(pdev, otp); return 0; |
c01e9a11a nvmem: add driver... |
173 174 175 176 |
} static struct platform_driver mxs_ocotp_driver = { .probe = mxs_ocotp_probe, |
c01e9a11a nvmem: add driver... |
177 178 179 180 181 182 183 |
.driver = { .name = "mxs-ocotp", .of_match_table = mxs_ocotp_match, }, }; module_platform_driver(mxs_ocotp_driver); |
5fb812293 nvmem: mxs-ocotp:... |
184 |
MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net"); |
c01e9a11a nvmem: add driver... |
185 186 |
MODULE_DESCRIPTION("driver for OCOTP in i.MX23/i.MX28"); MODULE_LICENSE("GPL v2"); |