All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiri Prchal <jiri.prchal-cKCO0sOKHLPtwjQa/ONI9g@public.gmane.org>
To: linux-spi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
	wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org,
	Jiri Prchal <jiri.prchal-cKCO0sOKHLPtwjQa/ONI9g@public.gmane.org>
Subject: [PATCH] misc: eeprom: at25: add Cypress FRAM functionality
Date: Tue, 21 Oct 2014 12:37:04 +0200	[thread overview]
Message-ID: <7b9c6d95cd40e257db4f10f0a41c3d15ee4350ad.1413887159.git.jiri.prchal@aksignal.cz> (raw)

This patch adds functionality for Cypress FRAMs on SPI bus, such as FM25V05,
FM25V10 etc.
Added to at25 driver:
- reading device ID and choose size and addr len from it
- serial number reading and exporting it to sysfs
- new compatible string

Signed-off-by: Jiri Prchal <jiri.prchal-cKCO0sOKHLPtwjQa/ONI9g@public.gmane.org>
---
Moved changes back to at25.c as Wolfram Sang coment "too much copied code".

 drivers/misc/eeprom/Kconfig |   5 +-
 drivers/misc/eeprom/at25.c  | 205 ++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 189 insertions(+), 21 deletions(-)

diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9536852f..130a538 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -28,10 +28,11 @@ config EEPROM_AT24
 	  will be called at24.

 config EEPROM_AT25
-	tristate "SPI EEPROMs from most vendors"
+	tristate "SPI EEPROMs (FRAMs) from most vendors"
 	depends on SPI && SYSFS
 	help
-	  Enable this driver to get read/write support to most SPI EEPROMs,
+	  Enable this driver to get read/write support to most SPI EEPROMs
+	  and Cypress FRAMs,
 	  after you configure the board init code to know about each eeprom
 	  on your target board.

diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 634f729..7bb9a6d 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -1,7 +1,9 @@
 /*
  * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models
+ *	     and Cypress FRAMs FM25 models
  *
  * Copyright (C) 2006 David Brownell
+ *		 2014 Jiri Prchal
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,6 +21,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/eeprom.h>
 #include <linux/of.h>
+#include <linux/of_device.h>

 /*
  * NOTE: this is an *EEPROM* driver.  The vagaries of product naming
@@ -34,6 +37,7 @@ struct at25_data {
 	struct spi_eeprom	chip;
 	struct bin_attribute	bin;
 	unsigned		addrlen;
+	int			has_sernum;
 };

 #define	AT25_WREN	0x06		/* latch the write enable */
@@ -42,6 +46,9 @@ struct at25_data {
 #define	AT25_WRSR	0x01		/* write status register */
 #define	AT25_READ	0x03		/* read byte(s) */
 #define	AT25_WRITE	0x02		/* write byte(s)/sector */
+#define	FM25_SLEEP	0xb9		/* enter sleep mode */
+#define	FM25_RDID	0x9f		/* read device ID */
+#define	FM25_RDSN	0xc3		/* read S/N */

 #define	AT25_SR_nRDY	0x01		/* nRDY = write-in-progress */
 #define	AT25_SR_WEN	0x02		/* write enable (latched) */
@@ -51,6 +58,9 @@ struct at25_data {

 #define	AT25_INSTR_BIT3	0x08		/* Additional address bit in instr */

+#define	FM25_ID_LEN	9		/* ID lenght */
+#define	FM25_SN_LEN	8		/* serial number lenght */
+
 #define EE_MAXADDRLEN	3		/* 24 bit addresses, up to 2 MBytes */

 /* Specs often allow 5 msec for a page write, sometimes 20 msec;
@@ -58,6 +68,9 @@ struct at25_data {
  */
 #define	EE_TIMEOUT	25

+#define	IS_EEPROM	0
+#define	IS_FRAM		1
+
 /*-------------------------------------------------------------------------*/

 #define	io_limit	PAGE_SIZE	/* bytes */
@@ -132,6 +145,83 @@ at25_ee_read(
 }

 static ssize_t
+fm25_id_read(struct at25_data *at25, char *buf)
+{
+	u8			command = FM25_RDID;
+	ssize_t			status;
+	struct spi_transfer	t[2];
+	struct spi_message	m;
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof t);
+
+	t[0].tx_buf = &command;
+	t[0].len = 1;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].rx_buf = buf;
+	t[1].len = FM25_ID_LEN;
+	spi_message_add_tail(&t[1], &m);
+
+	mutex_lock(&at25->lock);
+
+	status = spi_sync(at25->spi, &m);
+	dev_dbg(&at25->spi->dev,
+		"read %Zd bytes of ID --> %d\n",
+	 FM25_ID_LEN, (int) status);
+
+	mutex_unlock(&at25->lock);
+	return status ? status : FM25_ID_LEN;
+}
+
+static ssize_t
+fm25_sernum_read(struct at25_data *at25, char *buf)
+{
+	u8			command = FM25_RDSN;
+	ssize_t			status;
+	struct spi_transfer	t[2];
+	struct spi_message	m;
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof t);
+
+	t[0].tx_buf = &command;
+	t[0].len = 1;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].rx_buf = buf;
+	t[1].len = FM25_SN_LEN;
+	spi_message_add_tail(&t[1], &m);
+
+	mutex_lock(&at25->lock);
+
+	status = spi_sync(at25->spi, &m);
+	dev_dbg(&at25->spi->dev,
+		"read %Zd bytes of serial number --> %d\n",
+		FM25_SN_LEN, (int) status);
+
+	mutex_unlock(&at25->lock);
+	return status ? status : FM25_SN_LEN;
+}
+
+static ssize_t
+sernum_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	char			binbuf[FM25_SN_LEN];
+	struct at25_data	*at25;
+	int			i;
+	char			*pbuf = buf;
+
+	at25 = dev_get_drvdata(dev);
+	fm25_sernum_read(at25, binbuf);
+	for (i = 0; i < FM25_SN_LEN; i++)
+		pbuf += sprintf(pbuf, "%02x ", binbuf[i]);
+	sprintf(--pbuf, "\n");
+	return (3 * i);
+}
+static const DEVICE_ATTR_RO(sernum);
+
+static ssize_t
 at25_bin_read(struct file *filp, struct kobject *kobj,
 	      struct bin_attribute *bin_attr,
 	      char *buf, loff_t off, size_t count)
@@ -303,13 +393,20 @@ static ssize_t at25_mem_write(struct memory_accessor *mem, const char *buf,

 static int at25_np_to_chip(struct device *dev,
 			   struct device_node *np,
-			   struct spi_eeprom *chip)
+			   struct spi_eeprom *chip,
+			   int is_fram)
 {
 	u32 val;

 	memset(chip, 0, sizeof(*chip));
 	strncpy(chip->name, np->name, sizeof(chip->name));

+	if (is_fram) {
+		if (of_find_property(np, "read-only", NULL))
+			chip->flags |= EE_READONLY;
+		return 0;
+	}
+
 	if (of_property_read_u32(np, "size", &val) == 0 ||
 	    of_property_read_u32(np, "at25,byte-len", &val) == 0) {
 		chip->byte_len = val;
@@ -356,6 +453,13 @@ static int at25_np_to_chip(struct device *dev,
 	return 0;
 }

+static const struct of_device_id at25_of_match[] = {
+	{ .compatible = "atmel,at25", .data = (const void *)IS_EEPROM },
+	{ .compatible = "cypress,fm25", .data = (const void *)IS_FRAM },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, at25_of_match);
+
 static int at25_probe(struct spi_device *spi)
 {
 	struct at25_data	*at25 = NULL;
@@ -364,11 +468,18 @@ static int at25_probe(struct spi_device *spi)
 	int			err;
 	int			sr;
 	int			addrlen;
+	char			id[FM25_ID_LEN];
+	const struct of_device_id *match;
+	int			is_fram = 0;
+
+	match = of_match_device(of_match_ptr(at25_of_match), &spi->dev);
+	if (match)
+		is_fram = (int)(uintptr_t)match->data;

 	/* Chip description */
 	if (!spi->dev.platform_data) {
 		if (np) {
-			err = at25_np_to_chip(&spi->dev, np, &chip);
+			err = at25_np_to_chip(&spi->dev, np, &chip, is_fram);
 			if (err)
 				return err;
 		} else {
@@ -379,15 +490,17 @@ static int at25_probe(struct spi_device *spi)
 		chip = *(struct spi_eeprom *)spi->dev.platform_data;

 	/* For now we only support 8/16/24 bit addressing */
-	if (chip.flags & EE_ADDR1)
-		addrlen = 1;
-	else if (chip.flags & EE_ADDR2)
-		addrlen = 2;
-	else if (chip.flags & EE_ADDR3)
-		addrlen = 3;
-	else {
-		dev_dbg(&spi->dev, "unsupported address type\n");
-		return -EINVAL;
+	if (!is_fram) {
+		if (chip.flags & EE_ADDR1)
+			addrlen = 1;
+		else if (chip.flags & EE_ADDR2)
+			addrlen = 2;
+		else if (chip.flags & EE_ADDR3)
+			addrlen = 3;
+		else {
+			dev_dbg(&spi->dev, "unsupported address type\n");
+			return -EINVAL;
+		}
 	}

 	/* Ping the chip ... the status register is pretty portable,
@@ -410,6 +523,56 @@ static int at25_probe(struct spi_device *spi)
 	spi_set_drvdata(spi, at25);
 	at25->addrlen = addrlen;

+	if (is_fram) {
+		/* Get ID of chip */
+		fm25_id_read(at25, id);
+		if (id[6] != 0xc2) {
+			dev_err(&spi->dev,
+				"Error: no Cypress FRAM (id %02x)\n", id[6]);
+			return -ENODEV;
+		}
+		/* set size found in ID */
+		switch (id[7]) {
+			case 0x21:
+				at25->chip.byte_len = 16 * 1024;
+				break;
+			case 0x22:
+				at25->chip.byte_len = 32 * 1024;
+				break;
+			case 0x23:
+				at25->chip.byte_len = 64 * 1024;
+				break;
+			case 0x24:
+				at25->chip.byte_len = 128 * 1024;
+				break;
+			case 0x25:
+				at25->chip.byte_len = 256 * 1024;
+				break;
+			default:
+				dev_err(&spi->dev,
+					"Error: unsupported size (id %02x)\n",
+					id[7]);
+				return -ENODEV;
+				break;
+		}
+
+		if (at25->chip.byte_len > 64 * 1024) {
+			at25->addrlen = 3;
+			at25->chip.flags |= EE_ADDR3;
+		}
+		else {
+			at25->addrlen = 2;
+			at25->chip.flags |= EE_ADDR2;
+		}
+
+		if (id[8])
+			at25->has_sernum = 1;
+		else
+			at25->has_sernum = 0;
+
+		at25->chip.page_size = PAGE_SIZE;
+	}
+
 	/* Export the EEPROM bytes through sysfs, since that's convenient.
 	 * And maybe to other kernel code; it might hold a board's Ethernet
 	 * address, or board-specific calibration data generated on the
@@ -420,7 +583,7 @@ static int at25_probe(struct spi_device *spi)
 	 * security codes, board-specific manufacturing calibrations, etc.
 	 */
 	sysfs_bin_attr_init(&at25->bin);
-	at25->bin.attr.name = "eeprom";
+	at25->bin.attr.name = is_fram ? "fram" : "eeprom";
 	at25->bin.attr.mode = S_IRUSR;
 	at25->bin.read = at25_bin_read;
 	at25->mem.read = at25_mem_read;
@@ -436,15 +599,23 @@ static int at25_probe(struct spi_device *spi)
 	if (err)
 		return err;

+	/* Export the FM25 serial number */
+	if (at25->has_sernum) {
+		err = device_create_file(&spi->dev, &dev_attr_sernum);
+		if (err)
+			return err;
+	}
+
 	if (chip.setup)
 		chip.setup(&at25->mem, chip.context);

-	dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",
+	dev_info(&spi->dev, "%Zd %s %s %s%s, pagesize %u\n",
 		(at25->bin.size < 1024)
 			? at25->bin.size
 			: (at25->bin.size / 1024),
 		(at25->bin.size < 1024) ? "Byte" : "KByte",
 		at25->chip.name,
+		is_fram ? "fram" : "eeprom",
 		(chip.flags & EE_READONLY) ? " (readonly)" : "",
 		at25->chip.page_size);
 	return 0;
@@ -456,17 +627,13 @@ static int at25_remove(struct spi_device *spi)

 	at25 = spi_get_drvdata(spi);
 	sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
+	if (at25->has_sernum)
+		device_remove_file(&spi->dev, &dev_attr_sernum);
 	return 0;
 }

 /*-------------------------------------------------------------------------*/

-static const struct of_device_id at25_of_match[] = {
-	{ .compatible = "atmel,at25", },
-	{ }
-};
-MODULE_DEVICE_TABLE(of, at25_of_match);
-
 static struct spi_driver at25_driver = {
 	.driver = {
 		.name		= "at25",
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

             reply	other threads:[~2014-10-21 10:37 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-21 10:37 Jiri Prchal [this message]
2015-10-07 11:16 [PATCH] misc: eeprom: at25: add Cypress FRAM functionality Jiri Prchal
2015-10-06 14:01 ` Jiri Prchal
     [not found] ` <c7beacd629a47a0bd5023353378a3eda4d7d0f89.1444139729.git.jiri.prchal-cKCO0sOKHLPtwjQa/ONI9g@public.gmane.org>
2015-10-06 14:40   ` Mark Brown
2015-10-07 11:34 ` kbuild test robot
2015-10-07 11:37 ` kbuild test robot
2015-10-07 11:40 ` kbuild test robot
2015-10-07 12:38 ` kbuild test robot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=7b9c6d95cd40e257db4f10f0a41c3d15ee4350ad.1413887159.git.jiri.prchal@aksignal.cz \
    --to=jiri.prchal-ckco0sokhlptwjqa/oni9g@public.gmane.org \
    --cc=broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
    --cc=linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=linux-spi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
    --cc=nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org \
    --cc=wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.