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 = &microchip_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