18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for 93xx46 EEPROMs 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/mutex.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/of_device.h> 168c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 198c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h> 208c2ecf20Sopenharmony_ci#include <linux/eeprom_93xx46.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define OP_START 0x4 238c2ecf20Sopenharmony_ci#define OP_WRITE (OP_START | 0x1) 248c2ecf20Sopenharmony_ci#define OP_READ (OP_START | 0x2) 258c2ecf20Sopenharmony_ci#define ADDR_EWDS 0x00 268c2ecf20Sopenharmony_ci#define ADDR_ERAL 0x20 278c2ecf20Sopenharmony_ci#define ADDR_EWEN 0x30 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct eeprom_93xx46_devtype_data { 308c2ecf20Sopenharmony_ci unsigned int quirks; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct eeprom_93xx46_devtype_data atmel_at93c46d_data = { 348c2ecf20Sopenharmony_ci .quirks = EEPROM_93XX46_QUIRK_SINGLE_WORD_READ | 358c2ecf20Sopenharmony_ci EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic const struct eeprom_93xx46_devtype_data microchip_93lc46b_data = { 398c2ecf20Sopenharmony_ci .quirks = EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE, 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistruct eeprom_93xx46_dev { 438c2ecf20Sopenharmony_ci struct spi_device *spi; 448c2ecf20Sopenharmony_ci struct eeprom_93xx46_platform_data *pdata; 458c2ecf20Sopenharmony_ci struct mutex lock; 468c2ecf20Sopenharmony_ci struct nvmem_config nvmem_config; 478c2ecf20Sopenharmony_ci struct nvmem_device *nvmem; 488c2ecf20Sopenharmony_ci int addrlen; 498c2ecf20Sopenharmony_ci int size; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic inline bool has_quirk_single_word_read(struct eeprom_93xx46_dev *edev) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return edev->pdata->quirks & EEPROM_93XX46_QUIRK_SINGLE_WORD_READ; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic inline bool has_quirk_instruction_length(struct eeprom_93xx46_dev *edev) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci return edev->pdata->quirks & EEPROM_93XX46_QUIRK_INSTRUCTION_LENGTH; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic inline bool has_quirk_extra_read_cycle(struct eeprom_93xx46_dev *edev) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci return edev->pdata->quirks & EEPROM_93XX46_QUIRK_EXTRA_READ_CYCLE; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int eeprom_93xx46_read(void *priv, unsigned int off, 688c2ecf20Sopenharmony_ci void *val, size_t count) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev = priv; 718c2ecf20Sopenharmony_ci char *buf = val; 728c2ecf20Sopenharmony_ci int err = 0; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (unlikely(off >= edev->size)) 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci if ((off + count) > edev->size) 778c2ecf20Sopenharmony_ci count = edev->size - off; 788c2ecf20Sopenharmony_ci if (unlikely(!count)) 798c2ecf20Sopenharmony_ci return count; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci mutex_lock(&edev->lock); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (edev->pdata->prepare) 848c2ecf20Sopenharmony_ci edev->pdata->prepare(edev); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci while (count) { 878c2ecf20Sopenharmony_ci struct spi_message m; 888c2ecf20Sopenharmony_ci struct spi_transfer t[2] = { { 0 } }; 898c2ecf20Sopenharmony_ci u16 cmd_addr = OP_READ << edev->addrlen; 908c2ecf20Sopenharmony_ci size_t nbytes = count; 918c2ecf20Sopenharmony_ci int bits; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (edev->addrlen == 7) { 948c2ecf20Sopenharmony_ci cmd_addr |= off & 0x7f; 958c2ecf20Sopenharmony_ci bits = 10; 968c2ecf20Sopenharmony_ci if (has_quirk_single_word_read(edev)) 978c2ecf20Sopenharmony_ci nbytes = 1; 988c2ecf20Sopenharmony_ci } else { 998c2ecf20Sopenharmony_ci cmd_addr |= (off >> 1) & 0x3f; 1008c2ecf20Sopenharmony_ci bits = 9; 1018c2ecf20Sopenharmony_ci if (has_quirk_single_word_read(edev)) 1028c2ecf20Sopenharmony_ci nbytes = 2; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n", 1068c2ecf20Sopenharmony_ci cmd_addr, edev->spi->max_speed_hz); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (has_quirk_extra_read_cycle(edev)) { 1098c2ecf20Sopenharmony_ci cmd_addr <<= 1; 1108c2ecf20Sopenharmony_ci bits += 1; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci spi_message_init(&m); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci t[0].tx_buf = (char *)&cmd_addr; 1168c2ecf20Sopenharmony_ci t[0].len = 2; 1178c2ecf20Sopenharmony_ci t[0].bits_per_word = bits; 1188c2ecf20Sopenharmony_ci spi_message_add_tail(&t[0], &m); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci t[1].rx_buf = buf; 1218c2ecf20Sopenharmony_ci t[1].len = count; 1228c2ecf20Sopenharmony_ci t[1].bits_per_word = 8; 1238c2ecf20Sopenharmony_ci spi_message_add_tail(&t[1], &m); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci err = spi_sync(edev->spi, &m); 1268c2ecf20Sopenharmony_ci /* have to wait at least Tcsl ns */ 1278c2ecf20Sopenharmony_ci ndelay(250); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (err) { 1308c2ecf20Sopenharmony_ci dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n", 1318c2ecf20Sopenharmony_ci nbytes, (int)off, err); 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci buf += nbytes; 1368c2ecf20Sopenharmony_ci off += nbytes; 1378c2ecf20Sopenharmony_ci count -= nbytes; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (edev->pdata->finish) 1418c2ecf20Sopenharmony_ci edev->pdata->finish(edev); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci mutex_unlock(&edev->lock); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct spi_message m; 1518c2ecf20Sopenharmony_ci struct spi_transfer t; 1528c2ecf20Sopenharmony_ci int bits, ret; 1538c2ecf20Sopenharmony_ci u16 cmd_addr; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci cmd_addr = OP_START << edev->addrlen; 1568c2ecf20Sopenharmony_ci if (edev->addrlen == 7) { 1578c2ecf20Sopenharmony_ci cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1; 1588c2ecf20Sopenharmony_ci bits = 10; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS); 1618c2ecf20Sopenharmony_ci bits = 9; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (has_quirk_instruction_length(edev)) { 1658c2ecf20Sopenharmony_ci cmd_addr <<= 2; 1668c2ecf20Sopenharmony_ci bits += 2; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci dev_dbg(&edev->spi->dev, "ew%s cmd 0x%04x, %d bits\n", 1708c2ecf20Sopenharmony_ci is_on ? "en" : "ds", cmd_addr, bits); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci spi_message_init(&m); 1738c2ecf20Sopenharmony_ci memset(&t, 0, sizeof(t)); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci t.tx_buf = &cmd_addr; 1768c2ecf20Sopenharmony_ci t.len = 2; 1778c2ecf20Sopenharmony_ci t.bits_per_word = bits; 1788c2ecf20Sopenharmony_ci spi_message_add_tail(&t, &m); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci mutex_lock(&edev->lock); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (edev->pdata->prepare) 1838c2ecf20Sopenharmony_ci edev->pdata->prepare(edev); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ret = spi_sync(edev->spi, &m); 1868c2ecf20Sopenharmony_ci /* have to wait at least Tcsl ns */ 1878c2ecf20Sopenharmony_ci ndelay(250); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci dev_err(&edev->spi->dev, "erase/write %sable error %d\n", 1908c2ecf20Sopenharmony_ci is_on ? "en" : "dis", ret); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci if (edev->pdata->finish) 1938c2ecf20Sopenharmony_ci edev->pdata->finish(edev); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci mutex_unlock(&edev->lock); 1968c2ecf20Sopenharmony_ci return ret; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic ssize_t 2008c2ecf20Sopenharmony_cieeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev, 2018c2ecf20Sopenharmony_ci const char *buf, unsigned off) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct spi_message m; 2048c2ecf20Sopenharmony_ci struct spi_transfer t[2]; 2058c2ecf20Sopenharmony_ci int bits, data_len, ret; 2068c2ecf20Sopenharmony_ci u16 cmd_addr; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci cmd_addr = OP_WRITE << edev->addrlen; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (edev->addrlen == 7) { 2118c2ecf20Sopenharmony_ci cmd_addr |= off & 0x7f; 2128c2ecf20Sopenharmony_ci bits = 10; 2138c2ecf20Sopenharmony_ci data_len = 1; 2148c2ecf20Sopenharmony_ci } else { 2158c2ecf20Sopenharmony_ci cmd_addr |= (off >> 1) & 0x3f; 2168c2ecf20Sopenharmony_ci bits = 9; 2178c2ecf20Sopenharmony_ci data_len = 2; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci spi_message_init(&m); 2238c2ecf20Sopenharmony_ci memset(t, 0, sizeof(t)); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci t[0].tx_buf = (char *)&cmd_addr; 2268c2ecf20Sopenharmony_ci t[0].len = 2; 2278c2ecf20Sopenharmony_ci t[0].bits_per_word = bits; 2288c2ecf20Sopenharmony_ci spi_message_add_tail(&t[0], &m); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci t[1].tx_buf = buf; 2318c2ecf20Sopenharmony_ci t[1].len = data_len; 2328c2ecf20Sopenharmony_ci t[1].bits_per_word = 8; 2338c2ecf20Sopenharmony_ci spi_message_add_tail(&t[1], &m); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci ret = spi_sync(edev->spi, &m); 2368c2ecf20Sopenharmony_ci /* have to wait program cycle time Twc ms */ 2378c2ecf20Sopenharmony_ci mdelay(6); 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic int eeprom_93xx46_write(void *priv, unsigned int off, 2428c2ecf20Sopenharmony_ci void *val, size_t count) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev = priv; 2458c2ecf20Sopenharmony_ci char *buf = val; 2468c2ecf20Sopenharmony_ci int i, ret, step = 1; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (unlikely(off >= edev->size)) 2498c2ecf20Sopenharmony_ci return -EFBIG; 2508c2ecf20Sopenharmony_ci if ((off + count) > edev->size) 2518c2ecf20Sopenharmony_ci count = edev->size - off; 2528c2ecf20Sopenharmony_ci if (unlikely(!count)) 2538c2ecf20Sopenharmony_ci return count; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* only write even number of bytes on 16-bit devices */ 2568c2ecf20Sopenharmony_ci if (edev->addrlen == 6) { 2578c2ecf20Sopenharmony_ci step = 2; 2588c2ecf20Sopenharmony_ci count &= ~1; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* erase/write enable */ 2628c2ecf20Sopenharmony_ci ret = eeprom_93xx46_ew(edev, 1); 2638c2ecf20Sopenharmony_ci if (ret) 2648c2ecf20Sopenharmony_ci return ret; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci mutex_lock(&edev->lock); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (edev->pdata->prepare) 2698c2ecf20Sopenharmony_ci edev->pdata->prepare(edev); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci for (i = 0; i < count; i += step) { 2728c2ecf20Sopenharmony_ci ret = eeprom_93xx46_write_word(edev, &buf[i], off + i); 2738c2ecf20Sopenharmony_ci if (ret) { 2748c2ecf20Sopenharmony_ci dev_err(&edev->spi->dev, "write failed at %d: %d\n", 2758c2ecf20Sopenharmony_ci (int)off + i, ret); 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (edev->pdata->finish) 2818c2ecf20Sopenharmony_ci edev->pdata->finish(edev); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci mutex_unlock(&edev->lock); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* erase/write disable */ 2868c2ecf20Sopenharmony_ci eeprom_93xx46_ew(edev, 0); 2878c2ecf20Sopenharmony_ci return ret; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct eeprom_93xx46_platform_data *pd = edev->pdata; 2938c2ecf20Sopenharmony_ci struct spi_message m; 2948c2ecf20Sopenharmony_ci struct spi_transfer t; 2958c2ecf20Sopenharmony_ci int bits, ret; 2968c2ecf20Sopenharmony_ci u16 cmd_addr; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci cmd_addr = OP_START << edev->addrlen; 2998c2ecf20Sopenharmony_ci if (edev->addrlen == 7) { 3008c2ecf20Sopenharmony_ci cmd_addr |= ADDR_ERAL << 1; 3018c2ecf20Sopenharmony_ci bits = 10; 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci cmd_addr |= ADDR_ERAL; 3048c2ecf20Sopenharmony_ci bits = 9; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (has_quirk_instruction_length(edev)) { 3088c2ecf20Sopenharmony_ci cmd_addr <<= 2; 3098c2ecf20Sopenharmony_ci bits += 2; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci dev_dbg(&edev->spi->dev, "eral cmd 0x%04x, %d bits\n", cmd_addr, bits); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci spi_message_init(&m); 3158c2ecf20Sopenharmony_ci memset(&t, 0, sizeof(t)); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci t.tx_buf = &cmd_addr; 3188c2ecf20Sopenharmony_ci t.len = 2; 3198c2ecf20Sopenharmony_ci t.bits_per_word = bits; 3208c2ecf20Sopenharmony_ci spi_message_add_tail(&t, &m); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mutex_lock(&edev->lock); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (edev->pdata->prepare) 3258c2ecf20Sopenharmony_ci edev->pdata->prepare(edev); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ret = spi_sync(edev->spi, &m); 3288c2ecf20Sopenharmony_ci if (ret) 3298c2ecf20Sopenharmony_ci dev_err(&edev->spi->dev, "erase error %d\n", ret); 3308c2ecf20Sopenharmony_ci /* have to wait erase cycle time Tec ms */ 3318c2ecf20Sopenharmony_ci mdelay(6); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (pd->finish) 3348c2ecf20Sopenharmony_ci pd->finish(edev); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci mutex_unlock(&edev->lock); 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic ssize_t eeprom_93xx46_store_erase(struct device *dev, 3418c2ecf20Sopenharmony_ci struct device_attribute *attr, 3428c2ecf20Sopenharmony_ci const char *buf, size_t count) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev); 3458c2ecf20Sopenharmony_ci int erase = 0, ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci sscanf(buf, "%d", &erase); 3488c2ecf20Sopenharmony_ci if (erase) { 3498c2ecf20Sopenharmony_ci ret = eeprom_93xx46_ew(edev, 1); 3508c2ecf20Sopenharmony_ci if (ret) 3518c2ecf20Sopenharmony_ci return ret; 3528c2ecf20Sopenharmony_ci ret = eeprom_93xx46_eral(edev); 3538c2ecf20Sopenharmony_ci if (ret) 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci ret = eeprom_93xx46_ew(edev, 0); 3568c2ecf20Sopenharmony_ci if (ret) 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci return count; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_cistatic DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic void select_assert(void *context) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev = context; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(edev->pdata->select, 1); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void select_deassert(void *context) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev = context; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(edev->pdata->select, 0); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic const struct of_device_id eeprom_93xx46_of_table[] = { 3788c2ecf20Sopenharmony_ci { .compatible = "eeprom-93xx46", }, 3798c2ecf20Sopenharmony_ci { .compatible = "atmel,at93c46d", .data = &atmel_at93c46d_data, }, 3808c2ecf20Sopenharmony_ci { .compatible = "microchip,93lc46b", .data = µchip_93lc46b_data, }, 3818c2ecf20Sopenharmony_ci {} 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, eeprom_93xx46_of_table); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic int eeprom_93xx46_probe_dt(struct spi_device *spi) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci const struct of_device_id *of_id = 3888c2ecf20Sopenharmony_ci of_match_device(eeprom_93xx46_of_table, &spi->dev); 3898c2ecf20Sopenharmony_ci struct device_node *np = spi->dev.of_node; 3908c2ecf20Sopenharmony_ci struct eeprom_93xx46_platform_data *pd; 3918c2ecf20Sopenharmony_ci u32 tmp; 3928c2ecf20Sopenharmony_ci int ret; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci pd = devm_kzalloc(&spi->dev, sizeof(*pd), GFP_KERNEL); 3958c2ecf20Sopenharmony_ci if (!pd) 3968c2ecf20Sopenharmony_ci return -ENOMEM; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, "data-size", &tmp); 3998c2ecf20Sopenharmony_ci if (ret < 0) { 4008c2ecf20Sopenharmony_ci dev_err(&spi->dev, "data-size property not found\n"); 4018c2ecf20Sopenharmony_ci return ret; 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (tmp == 8) { 4058c2ecf20Sopenharmony_ci pd->flags |= EE_ADDR8; 4068c2ecf20Sopenharmony_ci } else if (tmp == 16) { 4078c2ecf20Sopenharmony_ci pd->flags |= EE_ADDR16; 4088c2ecf20Sopenharmony_ci } else { 4098c2ecf20Sopenharmony_ci dev_err(&spi->dev, "invalid data-size (%d)\n", tmp); 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "read-only")) 4148c2ecf20Sopenharmony_ci pd->flags |= EE_READONLY; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci pd->select = devm_gpiod_get_optional(&spi->dev, "select", 4178c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 4188c2ecf20Sopenharmony_ci if (IS_ERR(pd->select)) 4198c2ecf20Sopenharmony_ci return PTR_ERR(pd->select); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci pd->prepare = select_assert; 4228c2ecf20Sopenharmony_ci pd->finish = select_deassert; 4238c2ecf20Sopenharmony_ci gpiod_direction_output(pd->select, 0); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (of_id->data) { 4268c2ecf20Sopenharmony_ci const struct eeprom_93xx46_devtype_data *data = of_id->data; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci pd->quirks = data->quirks; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci spi->dev.platform_data = pd; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int eeprom_93xx46_probe(struct spi_device *spi) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci struct eeprom_93xx46_platform_data *pd; 4398c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev; 4408c2ecf20Sopenharmony_ci int err; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci if (spi->dev.of_node) { 4438c2ecf20Sopenharmony_ci err = eeprom_93xx46_probe_dt(spi); 4448c2ecf20Sopenharmony_ci if (err < 0) 4458c2ecf20Sopenharmony_ci return err; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci pd = spi->dev.platform_data; 4498c2ecf20Sopenharmony_ci if (!pd) { 4508c2ecf20Sopenharmony_ci dev_err(&spi->dev, "missing platform data\n"); 4518c2ecf20Sopenharmony_ci return -ENODEV; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci edev = devm_kzalloc(&spi->dev, sizeof(*edev), GFP_KERNEL); 4558c2ecf20Sopenharmony_ci if (!edev) 4568c2ecf20Sopenharmony_ci return -ENOMEM; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (pd->flags & EE_ADDR8) 4598c2ecf20Sopenharmony_ci edev->addrlen = 7; 4608c2ecf20Sopenharmony_ci else if (pd->flags & EE_ADDR16) 4618c2ecf20Sopenharmony_ci edev->addrlen = 6; 4628c2ecf20Sopenharmony_ci else { 4638c2ecf20Sopenharmony_ci dev_err(&spi->dev, "unspecified address type\n"); 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci mutex_init(&edev->lock); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci edev->spi = spi; 4708c2ecf20Sopenharmony_ci edev->pdata = pd; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci edev->size = 128; 4738c2ecf20Sopenharmony_ci edev->nvmem_config.type = NVMEM_TYPE_EEPROM; 4748c2ecf20Sopenharmony_ci edev->nvmem_config.name = dev_name(&spi->dev); 4758c2ecf20Sopenharmony_ci edev->nvmem_config.dev = &spi->dev; 4768c2ecf20Sopenharmony_ci edev->nvmem_config.read_only = pd->flags & EE_READONLY; 4778c2ecf20Sopenharmony_ci edev->nvmem_config.root_only = true; 4788c2ecf20Sopenharmony_ci edev->nvmem_config.owner = THIS_MODULE; 4798c2ecf20Sopenharmony_ci edev->nvmem_config.compat = true; 4808c2ecf20Sopenharmony_ci edev->nvmem_config.base_dev = &spi->dev; 4818c2ecf20Sopenharmony_ci edev->nvmem_config.reg_read = eeprom_93xx46_read; 4828c2ecf20Sopenharmony_ci edev->nvmem_config.reg_write = eeprom_93xx46_write; 4838c2ecf20Sopenharmony_ci edev->nvmem_config.priv = edev; 4848c2ecf20Sopenharmony_ci edev->nvmem_config.stride = 4; 4858c2ecf20Sopenharmony_ci edev->nvmem_config.word_size = 1; 4868c2ecf20Sopenharmony_ci edev->nvmem_config.size = edev->size; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci edev->nvmem = devm_nvmem_register(&spi->dev, &edev->nvmem_config); 4898c2ecf20Sopenharmony_ci if (IS_ERR(edev->nvmem)) 4908c2ecf20Sopenharmony_ci return PTR_ERR(edev->nvmem); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci dev_info(&spi->dev, "%d-bit eeprom %s\n", 4938c2ecf20Sopenharmony_ci (pd->flags & EE_ADDR8) ? 8 : 16, 4948c2ecf20Sopenharmony_ci (pd->flags & EE_READONLY) ? "(readonly)" : ""); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (!(pd->flags & EE_READONLY)) { 4978c2ecf20Sopenharmony_ci if (device_create_file(&spi->dev, &dev_attr_erase)) 4988c2ecf20Sopenharmony_ci dev_err(&spi->dev, "can't create erase interface\n"); 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci spi_set_drvdata(spi, edev); 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int eeprom_93xx46_remove(struct spi_device *spi) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci if (!(edev->pdata->flags & EE_READONLY)) 5108c2ecf20Sopenharmony_ci device_remove_file(&spi->dev, &dev_attr_erase); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci return 0; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic struct spi_driver eeprom_93xx46_driver = { 5168c2ecf20Sopenharmony_ci .driver = { 5178c2ecf20Sopenharmony_ci .name = "93xx46", 5188c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(eeprom_93xx46_of_table), 5198c2ecf20Sopenharmony_ci }, 5208c2ecf20Sopenharmony_ci .probe = eeprom_93xx46_probe, 5218c2ecf20Sopenharmony_ci .remove = eeprom_93xx46_remove, 5228c2ecf20Sopenharmony_ci}; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_cimodule_spi_driver(eeprom_93xx46_driver); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 5278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Driver for 93xx46 EEPROMs"); 5288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); 5298c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:93xx46"); 5308c2ecf20Sopenharmony_ciMODULE_ALIAS("spi:eeprom-93xx46"); 531