Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding at24 driver patch to kernel 4.14 #929

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
From 573150c3c36bbe71f99bb3102f7e168c1ff10a73 Mon Sep 17 00:00:00 2001
From: Raywat Polpakar <[email protected]>
Date: Fri, 10 Feb 2023 17:44:04 +0700
Subject: [PATCH] revise for supporting 16bits addr

---
drivers/misc/eeprom/at24.c | 119 +++++++++++++++++++++++++++++++++----
1 file changed, 108 insertions(+), 11 deletions(-)

diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index f8cfcd06..0f38e114 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2005-2007 David Brownell
* Copyright (C) 2008 Wolfram Sang, Pengutronix
+ * Copyright (C) 2015 Extreme Engineering Solutions, Inc.
*
* 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
@@ -50,7 +51,7 @@
* Other than binding model, current differences from "eeprom" driver are
* that this one handles write access and isn't restricted to 24c02 devices.
* It also handles larger devices (32 kbit and up) with two-byte addresses,
- * which won't work on pure SMBus systems.
+ * which don't work without risks on pure SMBus systems.
*/

struct at24_data {
@@ -168,6 +169,87 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids);

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

+/*
+ * Write a byte to an AT24 device using SMBus cycles.
+ */
+static inline s32 at24_smbus_write_byte_data(struct at24_data *at24,
+ struct i2c_client *client, u16 offset, u8 value)
+{
+ if (!(at24->chip.flags & AT24_FLAG_ADDR16))
+ return i2c_smbus_write_byte_data(client, offset, value);
+
+ /*
+ * Emulate I2C multi-byte write by using SMBus "write word"
+ * cycle. We split up the 16-bit offset among the "command"
+ * byte and the first data byte.
+ */
+ return i2c_smbus_write_word_data(client,
+ offset >> 8, (value << 8) | (offset & 0xff));
+}
+
+/*
+ * Write block data to an AT24 device using SMBus cycles.
+ */
+static inline s32 at24_smbus_write_i2c_block_data(struct at24_data *at24,
+ const struct i2c_client *client, u16 off, u8 len, const u8 *vals)
+{
+ s32 res;
+
+ if (!(at24->chip.flags & AT24_FLAG_ADDR16))
+ return i2c_smbus_write_i2c_block_data(client, off, len, vals);
+
+ /* Insert extra address byte into data stream */
+ at24->writebuf[0] = off & 0xff;
+ memcpy(&at24->writebuf[1], vals, len);
+
+ res = i2c_smbus_write_i2c_block_data(client,
+ off >> 8, len + 1, at24->writebuf);
+
+ return res;
+}
+
+/*
+ * Read block data from an AT24 device using SMBus cycles.
+ */
+static inline s32 at24_smbus_read_block_data(struct at24_data *at24,
+ const struct i2c_client *client, u16 off, u8 len, u8 *vals)
+{
+ int count;
+ s32 res;
+
+ if (!(at24->chip.flags & AT24_FLAG_ADDR16))
+ return i2c_smbus_read_i2c_block_data_or_emulated(client,
+ off, len, vals);
+
+ /*
+ * Emulate I2C multi-byte read by using SMBus "write byte" and
+ * "receive byte". This is slightly unsafe since there is an
+ * additional STOP involved, which exposes the SMBus and (this
+ * device!) to takeover by another bus master. However, it's the
+ * only way to work on SMBus-only controllers when talking to
+ * EEPROMs with multi-byte addresses.
+ */
+
+ /* Address "dummy" write */
+ res = i2c_smbus_write_byte_data(client, off >> 8, off & 0xff);
+ if (res < 0)
+ return res;
+
+ count = 0;
+ do {
+ /* Current Address Read */
+ res = i2c_smbus_read_byte(client);
+ if (res < 0)
+ break;
+
+ *(vals++) = res;
+ count++;
+ len--;
+ } while (len > 0);
+
+ return count;
+}
+
/*
* This routine supports chips which consume multiple I2C addresses. It
* computes the addressing information to be used for a given r/w request.
@@ -226,9 +308,7 @@ static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf,
*/
read_time = jiffies;

- status = i2c_smbus_read_i2c_block_data_or_emulated(client,
- offset,
- count, buf);
+ status = at24_smbus_read_block_data(at24, client, offset, count, buf);

dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n",
count, offset, status, jiffies);
@@ -448,8 +528,8 @@ static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24,
*/
write_time = jiffies;

- status = i2c_smbus_write_i2c_block_data(client,
- offset, count, buf);
+ status = at24_smbus_write_i2c_block_data(at24,
+ client, offset, count, buf);
if (status == 0)
status = count;

@@ -483,7 +563,7 @@ static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24,
*/
write_time = jiffies;

- status = i2c_smbus_write_byte_data(client, offset, buf[0]);
+ status = at24_smbus_write_byte_data(at24, client, offset, buf[0]);
if (status == 0)
status = count;

@@ -715,8 +795,12 @@ 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)
+ if ((chip.flags & AT24_FLAG_ADDR16) &&
+ (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_WRITE_BYTE_DATA))) {
return -EPFNOSUPPORT;
+ }

if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
@@ -734,7 +818,17 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
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,
+ } else if ((chip.flags & AT24_FLAG_ADDR16) &&
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA)) {
+ /*
+ * We need SMBUS_WRITE_WORD_DATA to implement
+ * byte writes for 16-bit address devices.
+ */
+ use_smbus_write = I2C_SMBUS_BYTE_DATA;
+ chip.page_size = 1;
+ } else if (!(chip.flags & AT24_FLAG_ADDR16) &&
+ i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) {
use_smbus_write = I2C_SMBUS_BYTE_DATA;
chip.page_size = 1;
@@ -787,11 +881,14 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!use_smbus || use_smbus_write) {

unsigned write_max = chip.page_size;
+ unsigned smbus_max = (chip.flags & AT24_FLAG_ADDR16) ?
+ I2C_SMBUS_BLOCK_MAX - 1 :
+ I2C_SMBUS_BLOCK_MAX;

if (write_max > io_limit)
write_max = io_limit;
- if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX)
- write_max = I2C_SMBUS_BLOCK_MAX;
+ if (use_smbus && write_max > smbus_max)
+ write_max = smbus_max;
at24->write_max = write_max;

/* buffer (data + address at the beginning) */
--
2.25.1

1 change: 1 addition & 0 deletions packages/base/any/kernels/4.14-lts/patches/series
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ driver-hwmon-max6620.patch
driver-hwmon-max6620-fix-rpm-calc.patch
driver-hwmon-max6620-update.patch
HID-cp2112-Fix-I2C_BLOCK_DATA-transactions.patch
at24-supporting-16bits-addr-smbus.patch