1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f02f8aeeSAriel D'Alessandro /*
3f02f8aeeSAriel D'Alessandro * NXP LPC18xx/LPC43xx EEPROM memory NVMEM driver
4f02f8aeeSAriel D'Alessandro *
5f02f8aeeSAriel D'Alessandro * Copyright (c) 2015 Ariel D'Alessandro <ariel@vanguardiasur.com>
6f02f8aeeSAriel D'Alessandro */
7f02f8aeeSAriel D'Alessandro
8f02f8aeeSAriel D'Alessandro #include <linux/clk.h>
9f02f8aeeSAriel D'Alessandro #include <linux/device.h>
10f02f8aeeSAriel D'Alessandro #include <linux/delay.h>
11f02f8aeeSAriel D'Alessandro #include <linux/err.h>
12f02f8aeeSAriel D'Alessandro #include <linux/io.h>
13f02f8aeeSAriel D'Alessandro #include <linux/module.h>
14ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
15f02f8aeeSAriel D'Alessandro #include <linux/nvmem-provider.h>
16f02f8aeeSAriel D'Alessandro #include <linux/platform_device.h>
17f02f8aeeSAriel D'Alessandro #include <linux/reset.h>
18f02f8aeeSAriel D'Alessandro
19f02f8aeeSAriel D'Alessandro /* Registers */
20f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_AUTOPROG 0x00c
21f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_AUTOPROG_WORD 0x1
22f02f8aeeSAriel D'Alessandro
23f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_CLKDIV 0x014
24f02f8aeeSAriel D'Alessandro
25f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_PWRDWN 0x018
26f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_PWRDWN_NO 0x0
27f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_PWRDWN_YES 0x1
28f02f8aeeSAriel D'Alessandro
29f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_INTSTAT 0xfe0
30f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_INTSTAT_END_OF_PROG BIT(2)
31f02f8aeeSAriel D'Alessandro
32f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_INTSTATCLR 0xfe8
33f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_INTSTATCLR_PROG_CLR_ST BIT(2)
34f02f8aeeSAriel D'Alessandro
35f02f8aeeSAriel D'Alessandro /* Fixed page size (bytes) */
36f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_PAGE_SIZE 0x80
37f02f8aeeSAriel D'Alessandro
38f02f8aeeSAriel D'Alessandro /* EEPROM device requires a ~1500 kHz clock (min 800 kHz, max 1600 kHz) */
39f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_CLOCK_HZ 1500000
40f02f8aeeSAriel D'Alessandro
41f02f8aeeSAriel D'Alessandro /* EEPROM requires 3 ms of erase/program time between each writing */
42f02f8aeeSAriel D'Alessandro #define LPC18XX_EEPROM_PROGRAM_TIME 3
43f02f8aeeSAriel D'Alessandro
44f02f8aeeSAriel D'Alessandro struct lpc18xx_eeprom_dev {
45f02f8aeeSAriel D'Alessandro struct clk *clk;
46f02f8aeeSAriel D'Alessandro void __iomem *reg_base;
47f02f8aeeSAriel D'Alessandro void __iomem *mem_base;
48f02f8aeeSAriel D'Alessandro struct nvmem_device *nvmem;
49f02f8aeeSAriel D'Alessandro unsigned reg_bytes;
50f02f8aeeSAriel D'Alessandro unsigned val_bytes;
512e8d0733SSrinivas Kandagatla int size;
52f02f8aeeSAriel D'Alessandro };
53f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_writel(struct lpc18xx_eeprom_dev * eeprom,u32 reg,u32 val)54f02f8aeeSAriel D'Alessandro static inline void lpc18xx_eeprom_writel(struct lpc18xx_eeprom_dev *eeprom,
55f02f8aeeSAriel D'Alessandro u32 reg, u32 val)
56f02f8aeeSAriel D'Alessandro {
57f02f8aeeSAriel D'Alessandro writel(val, eeprom->reg_base + reg);
58f02f8aeeSAriel D'Alessandro }
59f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_readl(struct lpc18xx_eeprom_dev * eeprom,u32 reg)60f02f8aeeSAriel D'Alessandro static inline u32 lpc18xx_eeprom_readl(struct lpc18xx_eeprom_dev *eeprom,
61f02f8aeeSAriel D'Alessandro u32 reg)
62f02f8aeeSAriel D'Alessandro {
63f02f8aeeSAriel D'Alessandro return readl(eeprom->reg_base + reg);
64f02f8aeeSAriel D'Alessandro }
65f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev * eeprom)66f02f8aeeSAriel D'Alessandro static int lpc18xx_eeprom_busywait_until_prog(struct lpc18xx_eeprom_dev *eeprom)
67f02f8aeeSAriel D'Alessandro {
68f02f8aeeSAriel D'Alessandro unsigned long end;
69f02f8aeeSAriel D'Alessandro u32 val;
70f02f8aeeSAriel D'Alessandro
71f02f8aeeSAriel D'Alessandro /* Wait until EEPROM program operation has finished */
72f02f8aeeSAriel D'Alessandro end = jiffies + msecs_to_jiffies(LPC18XX_EEPROM_PROGRAM_TIME * 10);
73f02f8aeeSAriel D'Alessandro
74f02f8aeeSAriel D'Alessandro while (time_is_after_jiffies(end)) {
75f02f8aeeSAriel D'Alessandro val = lpc18xx_eeprom_readl(eeprom, LPC18XX_EEPROM_INTSTAT);
76f02f8aeeSAriel D'Alessandro
77f02f8aeeSAriel D'Alessandro if (val & LPC18XX_EEPROM_INTSTAT_END_OF_PROG) {
78f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_INTSTATCLR,
79f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_INTSTATCLR_PROG_CLR_ST);
80f02f8aeeSAriel D'Alessandro return 0;
81f02f8aeeSAriel D'Alessandro }
82f02f8aeeSAriel D'Alessandro
83f02f8aeeSAriel D'Alessandro usleep_range(LPC18XX_EEPROM_PROGRAM_TIME * USEC_PER_MSEC,
84f02f8aeeSAriel D'Alessandro (LPC18XX_EEPROM_PROGRAM_TIME + 1) * USEC_PER_MSEC);
85f02f8aeeSAriel D'Alessandro }
86f02f8aeeSAriel D'Alessandro
87f02f8aeeSAriel D'Alessandro return -ETIMEDOUT;
88f02f8aeeSAriel D'Alessandro }
89f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_gather_write(void * context,unsigned int reg,void * val,size_t bytes)902e8d0733SSrinivas Kandagatla static int lpc18xx_eeprom_gather_write(void *context, unsigned int reg,
912e8d0733SSrinivas Kandagatla void *val, size_t bytes)
92f02f8aeeSAriel D'Alessandro {
93f02f8aeeSAriel D'Alessandro struct lpc18xx_eeprom_dev *eeprom = context;
942e8d0733SSrinivas Kandagatla unsigned int offset = reg;
95f02f8aeeSAriel D'Alessandro int ret;
96f02f8aeeSAriel D'Alessandro
972e8d0733SSrinivas Kandagatla /*
982e8d0733SSrinivas Kandagatla * The last page contains the EEPROM initialization data and is not
992e8d0733SSrinivas Kandagatla * writable.
1002e8d0733SSrinivas Kandagatla */
1012e8d0733SSrinivas Kandagatla if ((reg > eeprom->size - LPC18XX_EEPROM_PAGE_SIZE) ||
1022e8d0733SSrinivas Kandagatla (reg + bytes > eeprom->size - LPC18XX_EEPROM_PAGE_SIZE))
103f02f8aeeSAriel D'Alessandro return -EINVAL;
104f02f8aeeSAriel D'Alessandro
1052e8d0733SSrinivas Kandagatla
106f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
107f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_PWRDWN_NO);
108f02f8aeeSAriel D'Alessandro
109f02f8aeeSAriel D'Alessandro /* Wait 100 us while the EEPROM wakes up */
110f02f8aeeSAriel D'Alessandro usleep_range(100, 200);
111f02f8aeeSAriel D'Alessandro
1122e8d0733SSrinivas Kandagatla while (bytes) {
113f02f8aeeSAriel D'Alessandro writel(*(u32 *)val, eeprom->mem_base + offset);
114f02f8aeeSAriel D'Alessandro ret = lpc18xx_eeprom_busywait_until_prog(eeprom);
115f02f8aeeSAriel D'Alessandro if (ret < 0)
116f02f8aeeSAriel D'Alessandro return ret;
117f02f8aeeSAriel D'Alessandro
1182e8d0733SSrinivas Kandagatla bytes -= eeprom->val_bytes;
119f02f8aeeSAriel D'Alessandro val += eeprom->val_bytes;
120f02f8aeeSAriel D'Alessandro offset += eeprom->val_bytes;
121f02f8aeeSAriel D'Alessandro }
122f02f8aeeSAriel D'Alessandro
123f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
124f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_PWRDWN_YES);
125f02f8aeeSAriel D'Alessandro
126f02f8aeeSAriel D'Alessandro return 0;
127f02f8aeeSAriel D'Alessandro }
128f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_read(void * context,unsigned int offset,void * val,size_t bytes)1292e8d0733SSrinivas Kandagatla static int lpc18xx_eeprom_read(void *context, unsigned int offset,
1302e8d0733SSrinivas Kandagatla void *val, size_t bytes)
131f02f8aeeSAriel D'Alessandro {
132f02f8aeeSAriel D'Alessandro struct lpc18xx_eeprom_dev *eeprom = context;
133f02f8aeeSAriel D'Alessandro
134f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
135f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_PWRDWN_NO);
136f02f8aeeSAriel D'Alessandro
137f02f8aeeSAriel D'Alessandro /* Wait 100 us while the EEPROM wakes up */
138f02f8aeeSAriel D'Alessandro usleep_range(100, 200);
139f02f8aeeSAriel D'Alessandro
1402e8d0733SSrinivas Kandagatla while (bytes) {
141f02f8aeeSAriel D'Alessandro *(u32 *)val = readl(eeprom->mem_base + offset);
1422e8d0733SSrinivas Kandagatla bytes -= eeprom->val_bytes;
143f02f8aeeSAriel D'Alessandro val += eeprom->val_bytes;
144f02f8aeeSAriel D'Alessandro offset += eeprom->val_bytes;
145f02f8aeeSAriel D'Alessandro }
146f02f8aeeSAriel D'Alessandro
147f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
148f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_PWRDWN_YES);
149f02f8aeeSAriel D'Alessandro
150f02f8aeeSAriel D'Alessandro return 0;
151f02f8aeeSAriel D'Alessandro }
152f02f8aeeSAriel D'Alessandro
153f02f8aeeSAriel D'Alessandro
154f02f8aeeSAriel D'Alessandro static struct nvmem_config lpc18xx_nvmem_config = {
155f02f8aeeSAriel D'Alessandro .name = "lpc18xx-eeprom",
1562e8d0733SSrinivas Kandagatla .stride = 4,
1572e8d0733SSrinivas Kandagatla .word_size = 4,
1582e8d0733SSrinivas Kandagatla .reg_read = lpc18xx_eeprom_read,
1592e8d0733SSrinivas Kandagatla .reg_write = lpc18xx_eeprom_gather_write,
160f02f8aeeSAriel D'Alessandro };
161f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_probe(struct platform_device * pdev)162f02f8aeeSAriel D'Alessandro static int lpc18xx_eeprom_probe(struct platform_device *pdev)
163f02f8aeeSAriel D'Alessandro {
164f02f8aeeSAriel D'Alessandro struct lpc18xx_eeprom_dev *eeprom;
165f02f8aeeSAriel D'Alessandro struct device *dev = &pdev->dev;
166f02f8aeeSAriel D'Alessandro struct reset_control *rst;
167f02f8aeeSAriel D'Alessandro unsigned long clk_rate;
168f02f8aeeSAriel D'Alessandro struct resource *res;
169f02f8aeeSAriel D'Alessandro int ret;
170f02f8aeeSAriel D'Alessandro
171f02f8aeeSAriel D'Alessandro eeprom = devm_kzalloc(dev, sizeof(*eeprom), GFP_KERNEL);
172f02f8aeeSAriel D'Alessandro if (!eeprom)
173f02f8aeeSAriel D'Alessandro return -ENOMEM;
174f02f8aeeSAriel D'Alessandro
175f02f8aeeSAriel D'Alessandro res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg");
176f02f8aeeSAriel D'Alessandro eeprom->reg_base = devm_ioremap_resource(dev, res);
177f02f8aeeSAriel D'Alessandro if (IS_ERR(eeprom->reg_base))
178f02f8aeeSAriel D'Alessandro return PTR_ERR(eeprom->reg_base);
179f02f8aeeSAriel D'Alessandro
180f02f8aeeSAriel D'Alessandro res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem");
181f02f8aeeSAriel D'Alessandro eeprom->mem_base = devm_ioremap_resource(dev, res);
182f02f8aeeSAriel D'Alessandro if (IS_ERR(eeprom->mem_base))
183f02f8aeeSAriel D'Alessandro return PTR_ERR(eeprom->mem_base);
184f02f8aeeSAriel D'Alessandro
185f02f8aeeSAriel D'Alessandro eeprom->clk = devm_clk_get(&pdev->dev, "eeprom");
186f02f8aeeSAriel D'Alessandro if (IS_ERR(eeprom->clk)) {
187f02f8aeeSAriel D'Alessandro dev_err(&pdev->dev, "failed to get eeprom clock\n");
188f02f8aeeSAriel D'Alessandro return PTR_ERR(eeprom->clk);
189f02f8aeeSAriel D'Alessandro }
190f02f8aeeSAriel D'Alessandro
191f02f8aeeSAriel D'Alessandro ret = clk_prepare_enable(eeprom->clk);
192f02f8aeeSAriel D'Alessandro if (ret < 0) {
193f02f8aeeSAriel D'Alessandro dev_err(dev, "failed to prepare/enable eeprom clk: %d\n", ret);
194f02f8aeeSAriel D'Alessandro return ret;
195f02f8aeeSAriel D'Alessandro }
196f02f8aeeSAriel D'Alessandro
197aed0c46eSPhilipp Zabel rst = devm_reset_control_get_exclusive(dev, NULL);
198f02f8aeeSAriel D'Alessandro if (IS_ERR(rst)) {
199f02f8aeeSAriel D'Alessandro dev_err(dev, "failed to get reset: %ld\n", PTR_ERR(rst));
200f02f8aeeSAriel D'Alessandro ret = PTR_ERR(rst);
201f02f8aeeSAriel D'Alessandro goto err_clk;
202f02f8aeeSAriel D'Alessandro }
203f02f8aeeSAriel D'Alessandro
204f02f8aeeSAriel D'Alessandro ret = reset_control_assert(rst);
205f02f8aeeSAriel D'Alessandro if (ret < 0) {
206f02f8aeeSAriel D'Alessandro dev_err(dev, "failed to assert reset: %d\n", ret);
207f02f8aeeSAriel D'Alessandro goto err_clk;
208f02f8aeeSAriel D'Alessandro }
209f02f8aeeSAriel D'Alessandro
2102e8d0733SSrinivas Kandagatla eeprom->val_bytes = 4;
2112e8d0733SSrinivas Kandagatla eeprom->reg_bytes = 4;
212f02f8aeeSAriel D'Alessandro
213f02f8aeeSAriel D'Alessandro /*
214f02f8aeeSAriel D'Alessandro * Clock rate is generated by dividing the system bus clock by the
215f02f8aeeSAriel D'Alessandro * division factor, contained in the divider register (minus 1 encoded).
216f02f8aeeSAriel D'Alessandro */
217f02f8aeeSAriel D'Alessandro clk_rate = clk_get_rate(eeprom->clk);
218f02f8aeeSAriel D'Alessandro clk_rate = DIV_ROUND_UP(clk_rate, LPC18XX_EEPROM_CLOCK_HZ) - 1;
219f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_CLKDIV, clk_rate);
220f02f8aeeSAriel D'Alessandro
221f02f8aeeSAriel D'Alessandro /*
222f02f8aeeSAriel D'Alessandro * Writing a single word to the page will start the erase/program cycle
223f02f8aeeSAriel D'Alessandro * automatically
224f02f8aeeSAriel D'Alessandro */
225f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_AUTOPROG,
226f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_AUTOPROG_WORD);
227f02f8aeeSAriel D'Alessandro
228f02f8aeeSAriel D'Alessandro lpc18xx_eeprom_writel(eeprom, LPC18XX_EEPROM_PWRDWN,
229f02f8aeeSAriel D'Alessandro LPC18XX_EEPROM_PWRDWN_YES);
230f02f8aeeSAriel D'Alessandro
2312e8d0733SSrinivas Kandagatla eeprom->size = resource_size(res);
2322e8d0733SSrinivas Kandagatla lpc18xx_nvmem_config.size = resource_size(res);
233f02f8aeeSAriel D'Alessandro lpc18xx_nvmem_config.dev = dev;
2342e8d0733SSrinivas Kandagatla lpc18xx_nvmem_config.priv = eeprom;
235f02f8aeeSAriel D'Alessandro
236226014d1SBartosz Golaszewski eeprom->nvmem = devm_nvmem_register(dev, &lpc18xx_nvmem_config);
237f02f8aeeSAriel D'Alessandro if (IS_ERR(eeprom->nvmem)) {
238f02f8aeeSAriel D'Alessandro ret = PTR_ERR(eeprom->nvmem);
239f02f8aeeSAriel D'Alessandro goto err_clk;
240f02f8aeeSAriel D'Alessandro }
241f02f8aeeSAriel D'Alessandro
242f02f8aeeSAriel D'Alessandro platform_set_drvdata(pdev, eeprom);
243f02f8aeeSAriel D'Alessandro
244f02f8aeeSAriel D'Alessandro return 0;
245f02f8aeeSAriel D'Alessandro
246f02f8aeeSAriel D'Alessandro err_clk:
247f02f8aeeSAriel D'Alessandro clk_disable_unprepare(eeprom->clk);
248f02f8aeeSAriel D'Alessandro
249f02f8aeeSAriel D'Alessandro return ret;
250f02f8aeeSAriel D'Alessandro }
251f02f8aeeSAriel D'Alessandro
lpc18xx_eeprom_remove(struct platform_device * pdev)252f02f8aeeSAriel D'Alessandro static int lpc18xx_eeprom_remove(struct platform_device *pdev)
253f02f8aeeSAriel D'Alessandro {
254f02f8aeeSAriel D'Alessandro struct lpc18xx_eeprom_dev *eeprom = platform_get_drvdata(pdev);
255f02f8aeeSAriel D'Alessandro
256f02f8aeeSAriel D'Alessandro clk_disable_unprepare(eeprom->clk);
257f02f8aeeSAriel D'Alessandro
258f02f8aeeSAriel D'Alessandro return 0;
259f02f8aeeSAriel D'Alessandro }
260f02f8aeeSAriel D'Alessandro
261f02f8aeeSAriel D'Alessandro static const struct of_device_id lpc18xx_eeprom_of_match[] = {
262f02f8aeeSAriel D'Alessandro { .compatible = "nxp,lpc1857-eeprom" },
263f02f8aeeSAriel D'Alessandro { },
264f02f8aeeSAriel D'Alessandro };
265f02f8aeeSAriel D'Alessandro MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_of_match);
266f02f8aeeSAriel D'Alessandro
267f02f8aeeSAriel D'Alessandro static struct platform_driver lpc18xx_eeprom_driver = {
268f02f8aeeSAriel D'Alessandro .probe = lpc18xx_eeprom_probe,
269f02f8aeeSAriel D'Alessandro .remove = lpc18xx_eeprom_remove,
270f02f8aeeSAriel D'Alessandro .driver = {
271f02f8aeeSAriel D'Alessandro .name = "lpc18xx-eeprom",
272f02f8aeeSAriel D'Alessandro .of_match_table = lpc18xx_eeprom_of_match,
273f02f8aeeSAriel D'Alessandro },
274f02f8aeeSAriel D'Alessandro };
275f02f8aeeSAriel D'Alessandro
276f02f8aeeSAriel D'Alessandro module_platform_driver(lpc18xx_eeprom_driver);
277f02f8aeeSAriel D'Alessandro
278f02f8aeeSAriel D'Alessandro MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>");
279f02f8aeeSAriel D'Alessandro MODULE_DESCRIPTION("NXP LPC18xx EEPROM memory Driver");
280f02f8aeeSAriel D'Alessandro MODULE_LICENSE("GPL v2");
281