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