162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Driver for most of the SPI EEPROMs, such as Atmel AT25 models
462306a36Sopenharmony_ci * and Cypress FRAMs FM25 models.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2006 David Brownell
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/bits.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/device.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/property.h>
1562306a36Sopenharmony_ci#include <linux/sched.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/spi/eeprom.h>
1962306a36Sopenharmony_ci#include <linux/spi/spi.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <linux/nvmem-provider.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/*
2462306a36Sopenharmony_ci * NOTE: this is an *EEPROM* driver. The vagaries of product naming
2562306a36Sopenharmony_ci * mean that some AT25 products are EEPROMs, and others are FLASH.
2662306a36Sopenharmony_ci * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver,
2762306a36Sopenharmony_ci * not this one!
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * EEPROMs that can be used with this driver include, for example:
3062306a36Sopenharmony_ci *   AT25M02, AT25128B
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define	FM25_SN_LEN	8		/* serial number length */
3462306a36Sopenharmony_ci#define EE_MAXADDRLEN	3		/* 24 bit addresses, up to 2 MBytes */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistruct at25_data {
3762306a36Sopenharmony_ci	struct spi_eeprom	chip;
3862306a36Sopenharmony_ci	struct spi_device	*spi;
3962306a36Sopenharmony_ci	struct mutex		lock;
4062306a36Sopenharmony_ci	unsigned		addrlen;
4162306a36Sopenharmony_ci	struct nvmem_config	nvmem_config;
4262306a36Sopenharmony_ci	struct nvmem_device	*nvmem;
4362306a36Sopenharmony_ci	u8 sernum[FM25_SN_LEN];
4462306a36Sopenharmony_ci	u8 command[EE_MAXADDRLEN + 1];
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define	AT25_WREN	0x06		/* latch the write enable */
4862306a36Sopenharmony_ci#define	AT25_WRDI	0x04		/* reset the write enable */
4962306a36Sopenharmony_ci#define	AT25_RDSR	0x05		/* read status register */
5062306a36Sopenharmony_ci#define	AT25_WRSR	0x01		/* write status register */
5162306a36Sopenharmony_ci#define	AT25_READ	0x03		/* read byte(s) */
5262306a36Sopenharmony_ci#define	AT25_WRITE	0x02		/* write byte(s)/sector */
5362306a36Sopenharmony_ci#define	FM25_SLEEP	0xb9		/* enter sleep mode */
5462306a36Sopenharmony_ci#define	FM25_RDID	0x9f		/* read device ID */
5562306a36Sopenharmony_ci#define	FM25_RDSN	0xc3		/* read S/N */
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define	AT25_SR_nRDY	0x01		/* nRDY = write-in-progress */
5862306a36Sopenharmony_ci#define	AT25_SR_WEN	0x02		/* write enable (latched) */
5962306a36Sopenharmony_ci#define	AT25_SR_BP0	0x04		/* BP for software writeprotect */
6062306a36Sopenharmony_ci#define	AT25_SR_BP1	0x08
6162306a36Sopenharmony_ci#define	AT25_SR_WPEN	0x80		/* writeprotect enable */
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci#define	AT25_INSTR_BIT3	0x08		/* additional address bit in instr */
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#define	FM25_ID_LEN	9		/* ID length */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/*
6862306a36Sopenharmony_ci * Specs often allow 5ms for a page write, sometimes 20ms;
6962306a36Sopenharmony_ci * it's important to recover from write timeouts.
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_ci#define	EE_TIMEOUT	25
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#define	io_limit	PAGE_SIZE	/* bytes */
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int at25_ee_read(void *priv, unsigned int offset,
7862306a36Sopenharmony_ci			void *val, size_t count)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct at25_data *at25 = priv;
8162306a36Sopenharmony_ci	char *buf = val;
8262306a36Sopenharmony_ci	size_t max_chunk = spi_max_transfer_size(at25->spi);
8362306a36Sopenharmony_ci	unsigned int msg_offset = offset;
8462306a36Sopenharmony_ci	size_t bytes_left = count;
8562306a36Sopenharmony_ci	size_t segment;
8662306a36Sopenharmony_ci	u8			*cp;
8762306a36Sopenharmony_ci	ssize_t			status;
8862306a36Sopenharmony_ci	struct spi_transfer	t[2];
8962306a36Sopenharmony_ci	struct spi_message	m;
9062306a36Sopenharmony_ci	u8			instr;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (unlikely(offset >= at25->chip.byte_len))
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	if ((offset + count) > at25->chip.byte_len)
9562306a36Sopenharmony_ci		count = at25->chip.byte_len - offset;
9662306a36Sopenharmony_ci	if (unlikely(!count))
9762306a36Sopenharmony_ci		return -EINVAL;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	do {
10062306a36Sopenharmony_ci		segment = min(bytes_left, max_chunk);
10162306a36Sopenharmony_ci		cp = at25->command;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		instr = AT25_READ;
10462306a36Sopenharmony_ci		if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)
10562306a36Sopenharmony_ci			if (msg_offset >= BIT(at25->addrlen * 8))
10662306a36Sopenharmony_ci				instr |= AT25_INSTR_BIT3;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		mutex_lock(&at25->lock);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		*cp++ = instr;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		/* 8/16/24-bit address is written MSB first */
11362306a36Sopenharmony_ci		switch (at25->addrlen) {
11462306a36Sopenharmony_ci		default:	/* case 3 */
11562306a36Sopenharmony_ci			*cp++ = msg_offset >> 16;
11662306a36Sopenharmony_ci			fallthrough;
11762306a36Sopenharmony_ci		case 2:
11862306a36Sopenharmony_ci			*cp++ = msg_offset >> 8;
11962306a36Sopenharmony_ci			fallthrough;
12062306a36Sopenharmony_ci		case 1:
12162306a36Sopenharmony_ci		case 0:	/* can't happen: for better code generation */
12262306a36Sopenharmony_ci			*cp++ = msg_offset >> 0;
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		spi_message_init(&m);
12662306a36Sopenharmony_ci		memset(t, 0, sizeof(t));
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci		t[0].tx_buf = at25->command;
12962306a36Sopenharmony_ci		t[0].len = at25->addrlen + 1;
13062306a36Sopenharmony_ci		spi_message_add_tail(&t[0], &m);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		t[1].rx_buf = buf;
13362306a36Sopenharmony_ci		t[1].len = segment;
13462306a36Sopenharmony_ci		spi_message_add_tail(&t[1], &m);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		status = spi_sync(at25->spi, &m);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		mutex_unlock(&at25->lock);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		if (status)
14162306a36Sopenharmony_ci			return status;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		msg_offset += segment;
14462306a36Sopenharmony_ci		buf += segment;
14562306a36Sopenharmony_ci		bytes_left -= segment;
14662306a36Sopenharmony_ci	} while (bytes_left > 0);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	dev_dbg(&at25->spi->dev, "read %zu bytes at %d\n",
14962306a36Sopenharmony_ci		count, offset);
15062306a36Sopenharmony_ci	return 0;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/* Read extra registers as ID or serial number */
15462306a36Sopenharmony_cistatic int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command,
15562306a36Sopenharmony_ci			 int len)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	int status;
15862306a36Sopenharmony_ci	struct spi_transfer t[2];
15962306a36Sopenharmony_ci	struct spi_message m;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	spi_message_init(&m);
16262306a36Sopenharmony_ci	memset(t, 0, sizeof(t));
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	t[0].tx_buf = at25->command;
16562306a36Sopenharmony_ci	t[0].len = 1;
16662306a36Sopenharmony_ci	spi_message_add_tail(&t[0], &m);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	t[1].rx_buf = buf;
16962306a36Sopenharmony_ci	t[1].len = len;
17062306a36Sopenharmony_ci	spi_message_add_tail(&t[1], &m);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	mutex_lock(&at25->lock);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	at25->command[0] = command;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	status = spi_sync(at25->spi, &m);
17762306a36Sopenharmony_ci	dev_dbg(&at25->spi->dev, "read %d aux bytes --> %d\n", len, status);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	mutex_unlock(&at25->lock);
18062306a36Sopenharmony_ci	return status;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic ssize_t sernum_show(struct device *dev, struct device_attribute *attr, char *buf)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct at25_data *at25;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	at25 = dev_get_drvdata(dev);
18862306a36Sopenharmony_ci	return sysfs_emit(buf, "%*ph\n", (int)sizeof(at25->sernum), at25->sernum);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(sernum);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic struct attribute *sernum_attrs[] = {
19362306a36Sopenharmony_ci	&dev_attr_sernum.attr,
19462306a36Sopenharmony_ci	NULL,
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ciATTRIBUTE_GROUPS(sernum);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic int at25_ee_write(void *priv, unsigned int off, void *val, size_t count)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct at25_data *at25 = priv;
20162306a36Sopenharmony_ci	size_t maxsz = spi_max_transfer_size(at25->spi);
20262306a36Sopenharmony_ci	const char *buf = val;
20362306a36Sopenharmony_ci	int			status = 0;
20462306a36Sopenharmony_ci	unsigned		buf_size;
20562306a36Sopenharmony_ci	u8			*bounce;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (unlikely(off >= at25->chip.byte_len))
20862306a36Sopenharmony_ci		return -EFBIG;
20962306a36Sopenharmony_ci	if ((off + count) > at25->chip.byte_len)
21062306a36Sopenharmony_ci		count = at25->chip.byte_len - off;
21162306a36Sopenharmony_ci	if (unlikely(!count))
21262306a36Sopenharmony_ci		return -EINVAL;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	/* Temp buffer starts with command and address */
21562306a36Sopenharmony_ci	buf_size = at25->chip.page_size;
21662306a36Sopenharmony_ci	if (buf_size > io_limit)
21762306a36Sopenharmony_ci		buf_size = io_limit;
21862306a36Sopenharmony_ci	bounce = kmalloc(buf_size + at25->addrlen + 1, GFP_KERNEL);
21962306a36Sopenharmony_ci	if (!bounce)
22062306a36Sopenharmony_ci		return -ENOMEM;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/*
22362306a36Sopenharmony_ci	 * For write, rollover is within the page ... so we write at
22462306a36Sopenharmony_ci	 * most one page, then manually roll over to the next page.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	mutex_lock(&at25->lock);
22762306a36Sopenharmony_ci	do {
22862306a36Sopenharmony_ci		unsigned long	timeout, retries;
22962306a36Sopenharmony_ci		unsigned	segment;
23062306a36Sopenharmony_ci		unsigned	offset = off;
23162306a36Sopenharmony_ci		u8		*cp = bounce;
23262306a36Sopenharmony_ci		int		sr;
23362306a36Sopenharmony_ci		u8		instr;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci		*cp = AT25_WREN;
23662306a36Sopenharmony_ci		status = spi_write(at25->spi, cp, 1);
23762306a36Sopenharmony_ci		if (status < 0) {
23862306a36Sopenharmony_ci			dev_dbg(&at25->spi->dev, "WREN --> %d\n", status);
23962306a36Sopenharmony_ci			break;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		instr = AT25_WRITE;
24362306a36Sopenharmony_ci		if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)
24462306a36Sopenharmony_ci			if (offset >= BIT(at25->addrlen * 8))
24562306a36Sopenharmony_ci				instr |= AT25_INSTR_BIT3;
24662306a36Sopenharmony_ci		*cp++ = instr;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci		/* 8/16/24-bit address is written MSB first */
24962306a36Sopenharmony_ci		switch (at25->addrlen) {
25062306a36Sopenharmony_ci		default:	/* case 3 */
25162306a36Sopenharmony_ci			*cp++ = offset >> 16;
25262306a36Sopenharmony_ci			fallthrough;
25362306a36Sopenharmony_ci		case 2:
25462306a36Sopenharmony_ci			*cp++ = offset >> 8;
25562306a36Sopenharmony_ci			fallthrough;
25662306a36Sopenharmony_ci		case 1:
25762306a36Sopenharmony_ci		case 0:	/* can't happen: for better code generation */
25862306a36Sopenharmony_ci			*cp++ = offset >> 0;
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		/* Write as much of a page as we can */
26262306a36Sopenharmony_ci		segment = buf_size - (offset % buf_size);
26362306a36Sopenharmony_ci		if (segment > count)
26462306a36Sopenharmony_ci			segment = count;
26562306a36Sopenharmony_ci		if (segment > maxsz)
26662306a36Sopenharmony_ci			segment = maxsz;
26762306a36Sopenharmony_ci		memcpy(cp, buf, segment);
26862306a36Sopenharmony_ci		status = spi_write(at25->spi, bounce,
26962306a36Sopenharmony_ci				segment + at25->addrlen + 1);
27062306a36Sopenharmony_ci		dev_dbg(&at25->spi->dev, "write %u bytes at %u --> %d\n",
27162306a36Sopenharmony_ci			segment, offset, status);
27262306a36Sopenharmony_ci		if (status < 0)
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci		/*
27662306a36Sopenharmony_ci		 * REVISIT this should detect (or prevent) failed writes
27762306a36Sopenharmony_ci		 * to read-only sections of the EEPROM...
27862306a36Sopenharmony_ci		 */
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci		/* Wait for non-busy status */
28162306a36Sopenharmony_ci		timeout = jiffies + msecs_to_jiffies(EE_TIMEOUT);
28262306a36Sopenharmony_ci		retries = 0;
28362306a36Sopenharmony_ci		do {
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci			sr = spi_w8r8(at25->spi, AT25_RDSR);
28662306a36Sopenharmony_ci			if (sr < 0 || (sr & AT25_SR_nRDY)) {
28762306a36Sopenharmony_ci				dev_dbg(&at25->spi->dev,
28862306a36Sopenharmony_ci					"rdsr --> %d (%02x)\n", sr, sr);
28962306a36Sopenharmony_ci				/* at HZ=100, this is sloooow */
29062306a36Sopenharmony_ci				msleep(1);
29162306a36Sopenharmony_ci				continue;
29262306a36Sopenharmony_ci			}
29362306a36Sopenharmony_ci			if (!(sr & AT25_SR_nRDY))
29462306a36Sopenharmony_ci				break;
29562306a36Sopenharmony_ci		} while (retries++ < 3 || time_before_eq(jiffies, timeout));
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		if ((sr < 0) || (sr & AT25_SR_nRDY)) {
29862306a36Sopenharmony_ci			dev_err(&at25->spi->dev,
29962306a36Sopenharmony_ci				"write %u bytes offset %u, timeout after %u msecs\n",
30062306a36Sopenharmony_ci				segment, offset,
30162306a36Sopenharmony_ci				jiffies_to_msecs(jiffies -
30262306a36Sopenharmony_ci					(timeout - EE_TIMEOUT)));
30362306a36Sopenharmony_ci			status = -ETIMEDOUT;
30462306a36Sopenharmony_ci			break;
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci		off += segment;
30862306a36Sopenharmony_ci		buf += segment;
30962306a36Sopenharmony_ci		count -= segment;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	} while (count > 0);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	mutex_unlock(&at25->lock);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	kfree(bounce);
31662306a36Sopenharmony_ci	return status;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	u32 val;
32462306a36Sopenharmony_ci	int err;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	strscpy(chip->name, "at25", sizeof(chip->name));
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	err = device_property_read_u32(dev, "size", &val);
32962306a36Sopenharmony_ci	if (err)
33062306a36Sopenharmony_ci		err = device_property_read_u32(dev, "at25,byte-len", &val);
33162306a36Sopenharmony_ci	if (err) {
33262306a36Sopenharmony_ci		dev_err(dev, "Error: missing \"size\" property\n");
33362306a36Sopenharmony_ci		return err;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	chip->byte_len = val;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	err = device_property_read_u32(dev, "pagesize", &val);
33862306a36Sopenharmony_ci	if (err)
33962306a36Sopenharmony_ci		err = device_property_read_u32(dev, "at25,page-size", &val);
34062306a36Sopenharmony_ci	if (err) {
34162306a36Sopenharmony_ci		dev_err(dev, "Error: missing \"pagesize\" property\n");
34262306a36Sopenharmony_ci		return err;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	chip->page_size = val;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	err = device_property_read_u32(dev, "address-width", &val);
34762306a36Sopenharmony_ci	if (err) {
34862306a36Sopenharmony_ci		err = device_property_read_u32(dev, "at25,addr-mode", &val);
34962306a36Sopenharmony_ci		if (err) {
35062306a36Sopenharmony_ci			dev_err(dev, "Error: missing \"address-width\" property\n");
35162306a36Sopenharmony_ci			return err;
35262306a36Sopenharmony_ci		}
35362306a36Sopenharmony_ci		chip->flags = (u16)val;
35462306a36Sopenharmony_ci	} else {
35562306a36Sopenharmony_ci		switch (val) {
35662306a36Sopenharmony_ci		case 9:
35762306a36Sopenharmony_ci			chip->flags |= EE_INSTR_BIT3_IS_ADDR;
35862306a36Sopenharmony_ci			fallthrough;
35962306a36Sopenharmony_ci		case 8:
36062306a36Sopenharmony_ci			chip->flags |= EE_ADDR1;
36162306a36Sopenharmony_ci			break;
36262306a36Sopenharmony_ci		case 16:
36362306a36Sopenharmony_ci			chip->flags |= EE_ADDR2;
36462306a36Sopenharmony_ci			break;
36562306a36Sopenharmony_ci		case 24:
36662306a36Sopenharmony_ci			chip->flags |= EE_ADDR3;
36762306a36Sopenharmony_ci			break;
36862306a36Sopenharmony_ci		default:
36962306a36Sopenharmony_ci			dev_err(dev,
37062306a36Sopenharmony_ci				"Error: bad \"address-width\" property: %u\n",
37162306a36Sopenharmony_ci				val);
37262306a36Sopenharmony_ci			return -ENODEV;
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci		if (device_property_present(dev, "read-only"))
37562306a36Sopenharmony_ci			chip->flags |= EE_READONLY;
37662306a36Sopenharmony_ci	}
37762306a36Sopenharmony_ci	return 0;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct at25_data *at25 = container_of(chip, struct at25_data, chip);
38362306a36Sopenharmony_ci	u8 sernum[FM25_SN_LEN];
38462306a36Sopenharmony_ci	u8 id[FM25_ID_LEN];
38562306a36Sopenharmony_ci	int i;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	strscpy(chip->name, "fm25", sizeof(chip->name));
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Get ID of chip */
39062306a36Sopenharmony_ci	fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN);
39162306a36Sopenharmony_ci	if (id[6] != 0xc2) {
39262306a36Sopenharmony_ci		dev_err(dev, "Error: no Cypress FRAM (id %02x)\n", id[6]);
39362306a36Sopenharmony_ci		return -ENODEV;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	/* Set size found in ID */
39662306a36Sopenharmony_ci	if (id[7] < 0x21 || id[7] > 0x26) {
39762306a36Sopenharmony_ci		dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]);
39862306a36Sopenharmony_ci		return -ENODEV;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024;
40262306a36Sopenharmony_ci	if (chip->byte_len > 64 * 1024)
40362306a36Sopenharmony_ci		chip->flags |= EE_ADDR3;
40462306a36Sopenharmony_ci	else
40562306a36Sopenharmony_ci		chip->flags |= EE_ADDR2;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	if (id[8]) {
40862306a36Sopenharmony_ci		fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN);
40962306a36Sopenharmony_ci		/* Swap byte order */
41062306a36Sopenharmony_ci		for (i = 0; i < FM25_SN_LEN; i++)
41162306a36Sopenharmony_ci			at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i];
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	chip->page_size = PAGE_SIZE;
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic const struct of_device_id at25_of_match[] = {
41962306a36Sopenharmony_ci	{ .compatible = "atmel,at25" },
42062306a36Sopenharmony_ci	{ .compatible = "cypress,fm25" },
42162306a36Sopenharmony_ci	{ }
42262306a36Sopenharmony_ci};
42362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, at25_of_match);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_cistatic const struct spi_device_id at25_spi_ids[] = {
42662306a36Sopenharmony_ci	{ .name = "at25" },
42762306a36Sopenharmony_ci	{ .name = "fm25" },
42862306a36Sopenharmony_ci	{ }
42962306a36Sopenharmony_ci};
43062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, at25_spi_ids);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic int at25_probe(struct spi_device *spi)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct at25_data	*at25 = NULL;
43562306a36Sopenharmony_ci	int			err;
43662306a36Sopenharmony_ci	int			sr;
43762306a36Sopenharmony_ci	struct spi_eeprom *pdata;
43862306a36Sopenharmony_ci	bool is_fram;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	/*
44162306a36Sopenharmony_ci	 * Ping the chip ... the status register is pretty portable,
44262306a36Sopenharmony_ci	 * unlike probing manufacturer IDs. We do expect that system
44362306a36Sopenharmony_ci	 * firmware didn't write it in the past few milliseconds!
44462306a36Sopenharmony_ci	 */
44562306a36Sopenharmony_ci	sr = spi_w8r8(spi, AT25_RDSR);
44662306a36Sopenharmony_ci	if (sr < 0 || sr & AT25_SR_nRDY) {
44762306a36Sopenharmony_ci		dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr);
44862306a36Sopenharmony_ci		return -ENXIO;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	at25 = devm_kzalloc(&spi->dev, sizeof(*at25), GFP_KERNEL);
45262306a36Sopenharmony_ci	if (!at25)
45362306a36Sopenharmony_ci		return -ENOMEM;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	mutex_init(&at25->lock);
45662306a36Sopenharmony_ci	at25->spi = spi;
45762306a36Sopenharmony_ci	spi_set_drvdata(spi, at25);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	is_fram = fwnode_device_is_compatible(dev_fwnode(&spi->dev), "cypress,fm25");
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/* Chip description */
46262306a36Sopenharmony_ci	pdata = dev_get_platdata(&spi->dev);
46362306a36Sopenharmony_ci	if (pdata) {
46462306a36Sopenharmony_ci		at25->chip = *pdata;
46562306a36Sopenharmony_ci	} else {
46662306a36Sopenharmony_ci		if (is_fram)
46762306a36Sopenharmony_ci			err = at25_fram_to_chip(&spi->dev, &at25->chip);
46862306a36Sopenharmony_ci		else
46962306a36Sopenharmony_ci			err = at25_fw_to_chip(&spi->dev, &at25->chip);
47062306a36Sopenharmony_ci		if (err)
47162306a36Sopenharmony_ci			return err;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* For now we only support 8/16/24 bit addressing */
47562306a36Sopenharmony_ci	if (at25->chip.flags & EE_ADDR1)
47662306a36Sopenharmony_ci		at25->addrlen = 1;
47762306a36Sopenharmony_ci	else if (at25->chip.flags & EE_ADDR2)
47862306a36Sopenharmony_ci		at25->addrlen = 2;
47962306a36Sopenharmony_ci	else if (at25->chip.flags & EE_ADDR3)
48062306a36Sopenharmony_ci		at25->addrlen = 3;
48162306a36Sopenharmony_ci	else {
48262306a36Sopenharmony_ci		dev_dbg(&spi->dev, "unsupported address type\n");
48362306a36Sopenharmony_ci		return -EINVAL;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM;
48762306a36Sopenharmony_ci	at25->nvmem_config.name = dev_name(&spi->dev);
48862306a36Sopenharmony_ci	at25->nvmem_config.dev = &spi->dev;
48962306a36Sopenharmony_ci	at25->nvmem_config.read_only = at25->chip.flags & EE_READONLY;
49062306a36Sopenharmony_ci	at25->nvmem_config.root_only = true;
49162306a36Sopenharmony_ci	at25->nvmem_config.owner = THIS_MODULE;
49262306a36Sopenharmony_ci	at25->nvmem_config.compat = true;
49362306a36Sopenharmony_ci	at25->nvmem_config.base_dev = &spi->dev;
49462306a36Sopenharmony_ci	at25->nvmem_config.reg_read = at25_ee_read;
49562306a36Sopenharmony_ci	at25->nvmem_config.reg_write = at25_ee_write;
49662306a36Sopenharmony_ci	at25->nvmem_config.priv = at25;
49762306a36Sopenharmony_ci	at25->nvmem_config.stride = 1;
49862306a36Sopenharmony_ci	at25->nvmem_config.word_size = 1;
49962306a36Sopenharmony_ci	at25->nvmem_config.size = at25->chip.byte_len;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config);
50262306a36Sopenharmony_ci	if (IS_ERR(at25->nvmem))
50362306a36Sopenharmony_ci		return PTR_ERR(at25->nvmem);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n",
50662306a36Sopenharmony_ci		 (at25->chip.byte_len < 1024) ?
50762306a36Sopenharmony_ci			at25->chip.byte_len : (at25->chip.byte_len / 1024),
50862306a36Sopenharmony_ci		 (at25->chip.byte_len < 1024) ? "Byte" : "KByte",
50962306a36Sopenharmony_ci		 at25->chip.name, is_fram ? "fram" : "eeprom",
51062306a36Sopenharmony_ci		 (at25->chip.flags & EE_READONLY) ? " (readonly)" : "",
51162306a36Sopenharmony_ci		 at25->chip.page_size);
51262306a36Sopenharmony_ci	return 0;
51362306a36Sopenharmony_ci}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci/*-------------------------------------------------------------------------*/
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic struct spi_driver at25_driver = {
51862306a36Sopenharmony_ci	.driver = {
51962306a36Sopenharmony_ci		.name		= "at25",
52062306a36Sopenharmony_ci		.of_match_table = at25_of_match,
52162306a36Sopenharmony_ci		.dev_groups	= sernum_groups,
52262306a36Sopenharmony_ci	},
52362306a36Sopenharmony_ci	.probe		= at25_probe,
52462306a36Sopenharmony_ci	.id_table	= at25_spi_ids,
52562306a36Sopenharmony_ci};
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cimodule_spi_driver(at25_driver);
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ciMODULE_DESCRIPTION("Driver for most SPI EEPROMs");
53062306a36Sopenharmony_ciMODULE_AUTHOR("David Brownell");
53162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
53262306a36Sopenharmony_ciMODULE_ALIAS("spi:at25");
533