All of lore.kernel.org
 help / color / mirror / Atom feed
From: Guenter Roeck <linux@roeck-us.net>
To: Wolfram Sang <wsa@the-dreams.de>
Cc: linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org,
	Guenter Roeck <linux@roeck-us.net>
Subject: [PATCH] eeprom: at24: Add support for large EEPROMs connected to SMBus adapters
Date: Wed,  4 Feb 2015 08:23:37 -0800	[thread overview]
Message-ID: <1423067017-27607-1-git-send-email-linux@roeck-us.net> (raw)

Large EEPROMS (24c32 and larger) require a two-byte data address
instead of just a single byte. Implement support for such EEPROMs
with SMBus commands.

Support has limitations (reads are not multi-master safe) and is slow,
but it works. Practical use is for a system with 24c32 connected to
Intel 82801I (ICH9).

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
 drivers/misc/eeprom/at24.c | 52 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 2d3db81..057d35c 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -198,6 +198,8 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
 	case I2C_SMBUS_BYTE_DATA:
 		count = 1;
 		break;
+	case I2C_SMBUS_BYTE:
+		break;
 	default:
 		/*
 		 * When we have a better choice than SMBus calls, use a
@@ -249,6 +251,27 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
 				status = count;
 			}
 			break;
+		case I2C_SMBUS_BYTE:
+			/*
+			 * 16-bit data address. Write data address as separate
+			 * write operation, then read data without setting
+			 * the address again. This is not multi-master safe,
+			 * but the best we can do.
+			 */
+			status = i2c_smbus_write_byte_data(client,
+							   offset >> 8,
+							   offset & 0xff);
+			if (status < 0)
+				break;
+			for (i = 0; i < count; i++) {
+				status = i2c_smbus_read_byte(client);
+				if (status < 0)
+					break;
+				buf[i] = status;
+			}
+			if (status >= 0)
+				status = count;
+			break;
 		default:
 			status = i2c_transfer(client->adapter, msg, 2);
 			if (status == 2)
@@ -372,6 +395,16 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
 				status = i2c_smbus_write_i2c_block_data(client,
 						offset, count, buf);
 				break;
+			case I2C_SMBUS_WORD_DATA:
+				/*
+				 * 16-bit data address. Transmit data address
+				 * MSB as SMBus command, data address LSB as
+				 * first data byte.
+				 */
+				status = i2c_smbus_write_word_data(client,
+					offset >> 8,
+					(offset & 0xff) | (buf[0] << 8));
+				break;
 			case I2C_SMBUS_BYTE_DATA:
 				status = i2c_smbus_write_byte_data(client,
 						offset, buf[0]);
@@ -540,10 +573,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	/* Use I2C operations unless we're stuck with SMBus extensions. */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		if (chip.flags & AT24_FLAG_ADDR16)
-			return -EPFNOSUPPORT;
-
-		if (i2c_check_functionality(client->adapter,
+		if (chip.flags & AT24_FLAG_ADDR16) {
+			if (!i2c_check_functionality(client->adapter,
+						I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
+						I2C_FUNC_SMBUS_READ_BYTE))
+				return -EPFNOSUPPORT;
+			use_smbus = I2C_SMBUS_BYTE;
+		} else if (i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
 			use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
 		} else if (i2c_check_functionality(client->adapter,
@@ -559,7 +595,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	/* Use I2C operations unless we're stuck with SMBus extensions. */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		if (i2c_check_functionality(client->adapter,
+		if (chip.flags & AT24_FLAG_ADDR16) {
+			if (i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_WRITE_WORD_DATA)) {
+				use_smbus_write = I2C_SMBUS_WORD_DATA;
+				chip.page_size = 1;
+			}
+		} else if (i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
 			use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
 		} else if (i2c_check_functionality(client->adapter,
-- 
2.1.0


WARNING: multiple messages have this Message-ID (diff)
From: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
To: Wolfram Sang <wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org>
Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
Subject: [PATCH] eeprom: at24: Add support for large EEPROMs connected to SMBus adapters
Date: Wed,  4 Feb 2015 08:23:37 -0800	[thread overview]
Message-ID: <1423067017-27607-1-git-send-email-linux@roeck-us.net> (raw)

Large EEPROMS (24c32 and larger) require a two-byte data address
instead of just a single byte. Implement support for such EEPROMs
with SMBus commands.

Support has limitations (reads are not multi-master safe) and is slow,
but it works. Practical use is for a system with 24c32 connected to
Intel 82801I (ICH9).

Signed-off-by: Guenter Roeck <linux-0h96xk9xTtrk1uMJSBkQmQ@public.gmane.org>
---
 drivers/misc/eeprom/at24.c | 52 +++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 5 deletions(-)

diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 2d3db81..057d35c 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -198,6 +198,8 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
 	case I2C_SMBUS_BYTE_DATA:
 		count = 1;
 		break;
+	case I2C_SMBUS_BYTE:
+		break;
 	default:
 		/*
 		 * When we have a better choice than SMBus calls, use a
@@ -249,6 +251,27 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
 				status = count;
 			}
 			break;
+		case I2C_SMBUS_BYTE:
+			/*
+			 * 16-bit data address. Write data address as separate
+			 * write operation, then read data without setting
+			 * the address again. This is not multi-master safe,
+			 * but the best we can do.
+			 */
+			status = i2c_smbus_write_byte_data(client,
+							   offset >> 8,
+							   offset & 0xff);
+			if (status < 0)
+				break;
+			for (i = 0; i < count; i++) {
+				status = i2c_smbus_read_byte(client);
+				if (status < 0)
+					break;
+				buf[i] = status;
+			}
+			if (status >= 0)
+				status = count;
+			break;
 		default:
 			status = i2c_transfer(client->adapter, msg, 2);
 			if (status == 2)
@@ -372,6 +395,16 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
 				status = i2c_smbus_write_i2c_block_data(client,
 						offset, count, buf);
 				break;
+			case I2C_SMBUS_WORD_DATA:
+				/*
+				 * 16-bit data address. Transmit data address
+				 * MSB as SMBus command, data address LSB as
+				 * first data byte.
+				 */
+				status = i2c_smbus_write_word_data(client,
+					offset >> 8,
+					(offset & 0xff) | (buf[0] << 8));
+				break;
 			case I2C_SMBUS_BYTE_DATA:
 				status = i2c_smbus_write_byte_data(client,
 						offset, buf[0]);
@@ -540,10 +573,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	/* Use I2C operations unless we're stuck with SMBus extensions. */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		if (chip.flags & AT24_FLAG_ADDR16)
-			return -EPFNOSUPPORT;
-
-		if (i2c_check_functionality(client->adapter,
+		if (chip.flags & AT24_FLAG_ADDR16) {
+			if (!i2c_check_functionality(client->adapter,
+						I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
+						I2C_FUNC_SMBUS_READ_BYTE))
+				return -EPFNOSUPPORT;
+			use_smbus = I2C_SMBUS_BYTE;
+		} else if (i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
 			use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
 		} else if (i2c_check_functionality(client->adapter,
@@ -559,7 +595,13 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
 
 	/* Use I2C operations unless we're stuck with SMBus extensions. */
 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
-		if (i2c_check_functionality(client->adapter,
+		if (chip.flags & AT24_FLAG_ADDR16) {
+			if (i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_WRITE_WORD_DATA)) {
+				use_smbus_write = I2C_SMBUS_WORD_DATA;
+				chip.page_size = 1;
+			}
+		} else if (i2c_check_functionality(client->adapter,
 				I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
 			use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
 		} else if (i2c_check_functionality(client->adapter,
-- 
2.1.0

             reply	other threads:[~2015-02-04 16:23 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-04 16:23 Guenter Roeck [this message]
2015-02-04 16:23 ` [PATCH] eeprom: at24: Add support for large EEPROMs connected to SMBus adapters Guenter Roeck
2015-02-04 17:47 ` Wolfram Sang
2015-02-04 19:08   ` Guenter Roeck
2015-02-04 19:08     ` Guenter Roeck
2015-02-04 23:35     ` Wolfram Sang
2015-02-05  0:26       ` Guenter Roeck
2015-02-05  0:26         ` Guenter Roeck
2015-02-05 14:40         ` Wolfram Sang
2015-02-05 17:53           ` Guenter Roeck
2015-02-12  4:01             ` Guenter Roeck
2015-02-12  4:01               ` Guenter Roeck
2015-02-16 12:09               ` Wolfram Sang
2015-02-16 15:37                 ` Guenter Roeck
2015-03-17  4:20                 ` Guenter Roeck
2015-03-17  4:20                   ` Guenter Roeck
2015-03-18 13:27                   ` Wolfram Sang
2015-03-19  3:24                     ` Guenter Roeck
2015-03-19  8:16                       ` Wolfram Sang
2015-03-19  8:16                         ` Wolfram Sang
2015-03-19 13:30                         ` Guenter Roeck
2015-03-19 13:30                           ` Guenter Roeck
2015-03-19 17:43                         ` Guenter Roeck
2015-03-19 17:43                           ` Guenter Roeck
2015-03-19 21:39                           ` Wolfram Sang
2015-03-25 14:11                             ` Guenter Roeck
2015-03-25 16:15                               ` Wolfram Sang
2015-03-25 16:15                                 ` Wolfram Sang
2015-03-25 16:37                                 ` Guenter Roeck
2015-03-25 16:37                                   ` Guenter Roeck
2015-03-27  8:09                               ` Wolfram Sang
2015-03-27 12:51                                 ` Guenter Roeck
2015-03-27 13:01                                   ` Wolfram Sang
2015-03-27 13:14                                     ` Guenter Roeck
2015-03-27 15:27                                       ` Wolfram Sang
2015-03-27 15:42                                         ` Guenter Roeck
2015-03-27 15:42                                           ` Guenter Roeck
2015-02-04 20:33   ` Guenter Roeck

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=1423067017-27607-1-git-send-email-linux@roeck-us.net \
    --to=linux@roeck-us.net \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=wsa@the-dreams.de \
    /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.