1d2912cb1SThomas 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 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 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 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 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 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 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 252*693d2f62SUwe Kleine-König static void 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 259f02f8aeeSAriel D'Alessandro static const struct of_device_id lpc18xx_eeprom_of_match[] = { 260f02f8aeeSAriel D'Alessandro { .compatible = "nxp,lpc1857-eeprom" }, 261f02f8aeeSAriel D'Alessandro { }, 262f02f8aeeSAriel D'Alessandro }; 263f02f8aeeSAriel D'Alessandro MODULE_DEVICE_TABLE(of, lpc18xx_eeprom_of_match); 264f02f8aeeSAriel D'Alessandro 265f02f8aeeSAriel D'Alessandro static struct platform_driver lpc18xx_eeprom_driver = { 266f02f8aeeSAriel D'Alessandro .probe = lpc18xx_eeprom_probe, 267*693d2f62SUwe Kleine-König .remove_new = lpc18xx_eeprom_remove, 268f02f8aeeSAriel D'Alessandro .driver = { 269f02f8aeeSAriel D'Alessandro .name = "lpc18xx-eeprom", 270f02f8aeeSAriel D'Alessandro .of_match_table = lpc18xx_eeprom_of_match, 271f02f8aeeSAriel D'Alessandro }, 272f02f8aeeSAriel D'Alessandro }; 273f02f8aeeSAriel D'Alessandro 274f02f8aeeSAriel D'Alessandro module_platform_driver(lpc18xx_eeprom_driver); 275f02f8aeeSAriel D'Alessandro 276f02f8aeeSAriel D'Alessandro MODULE_AUTHOR("Ariel D'Alessandro <ariel@vanguardiasur.com.ar>"); 277f02f8aeeSAriel D'Alessandro MODULE_DESCRIPTION("NXP LPC18xx EEPROM memory Driver"); 278f02f8aeeSAriel D'Alessandro MODULE_LICENSE("GPL v2"); 279