diff --git a/arch/arm64/configs/seco_px30_d23-linux_defconfig b/arch/arm64/configs/seco_px30_d23-linux_defconfig index f4a1ce68f84af5e8dc369c306585de7845e90236..0533200246e8cd873f0a928257a52f42060ba91c 100644 --- a/arch/arm64/configs/seco_px30_d23-linux_defconfig +++ b/arch/arm64/configs/seco_px30_d23-linux_defconfig @@ -170,6 +170,7 @@ CONFIG_ZRAM=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=1 +CONFIG_EEPROM_AT24=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_BLK_DEV_SR=y diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index f112c5bc082a57b1c5402f1d2a21907f80b36637..f2ba87db7e5eb84da2e6493de49310225fd89e60 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -15,14 +15,14 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/mutex.h> -#include <linux/sysfs.h> #include <linux/mod_devicetable.h> #include <linux/log2.h> #include <linux/bitops.h> #include <linux/jiffies.h> -#include <linux/of.h> +#include <linux/property.h> #include <linux/acpi.h> #include <linux/i2c.h> +#include <linux/nvmem-provider.h> #include <linux/platform_data/at24.h> /* @@ -55,21 +55,31 @@ struct at24_data { struct at24_platform_data chip; - struct memory_accessor macc; int use_smbus; int use_smbus_write; + ssize_t (*read_func)(struct at24_data *, char *, unsigned int, size_t); + ssize_t (*write_func)(struct at24_data *, + const char *, unsigned int, size_t); + /* * Lock protects against activities from other Linux tasks, * but not from changes by other I2C masters. */ struct mutex lock; - struct bin_attribute bin; u8 *writebuf; unsigned write_max; unsigned num_addresses; + struct nvmem_config nvmem_config; + struct nvmem_device *nvmem; + struct nvmem_device *mac_nvmem; + struct nvmem_device *serial_nvmem; + + struct nvmem_config mac_nvmem_config; + struct nvmem_config serial_nvmem_config; + struct nvmem_config user_nvmem_config; /* * Some chips tie up multiple I2C addresses; dummy devices reserve * them for us, and we'll use them with SMBus calls. @@ -98,6 +108,12 @@ static unsigned write_timeout = 25; module_param(write_timeout, uint, 0); MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); +#define SECO_MAC_OFFSET 0 +#define SECO_MAC_CHAR_LEN 12 +#define SECO_SERIAL_OFFSET 6 +#define SECO_SERIAL_CHAR_LEN 9 +#define SECO_RESERVED_BYTE 32 + #define AT24_SIZE_BYTELEN 5 #define AT24_SIZE_FLAGS 8 @@ -108,25 +124,66 @@ MODULE_PARM_DESC(write_timeout, "Time (in ms) to try writes (default 25)"); ((1 << AT24_SIZE_FLAGS | (_flags)) \ << AT24_SIZE_BYTELEN | ilog2(_len)) +/* + * Both reads and writes fail if the previous write didn't complete yet. This + * macro loops a few times waiting at least long enough for one entire page + * write to work while making sure that at least one iteration is run before + * checking the break condition. + * + * It takes two parameters: a variable in which the future timeout in jiffies + * will be stored and a temporary variable holding the time of the last + * iteration of processing the request. Both should be unsigned integers + * holding at least 32 bits. + */ +#define loop_until_timeout(tout, op_time) \ + for (tout = jiffies + msecs_to_jiffies(write_timeout), op_time = 0; \ + op_time ? time_before(op_time, tout) : true; \ + usleep_range(1000, 1500), op_time = jiffies) + static const struct i2c_device_id at24_ids[] = { + { "24aa025", AT24_DEVICE_MAGIC(2048 / 8, AT24_FLAG_SECO_PARTITION) }, /* needs 8 addresses as A0-A2 are ignored */ - { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, + { "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) }, /* old variants can't be handled with this generic entry! */ - { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, - { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + { "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) }, + { "24cs01", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) }, + { "24cs02", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24mac402", AT24_DEVICE_MAGIC(48 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY) }, + { "24mac602", AT24_DEVICE_MAGIC(64 / 8, + AT24_FLAG_MAC | AT24_FLAG_READONLY) }, /* spd is a 24c02 in memory DIMMs */ - { "spd", AT24_DEVICE_MAGIC(2048 / 8, - AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, - { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + { "spd", AT24_DEVICE_MAGIC(2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, + { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, + { "24cs04", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, /* 24rf08 quirk is handled at i2c-core */ - { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, - { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, - { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, - { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, - { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, - { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, - { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, - { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24cs08", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, + { "24cs16", AT24_DEVICE_MAGIC(16, + AT24_FLAG_SERIAL | AT24_FLAG_READONLY) }, + { "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) }, + { "24cs32", AT24_DEVICE_MAGIC(16, + AT24_FLAG_ADDR16 | + AT24_FLAG_SERIAL | + AT24_FLAG_READONLY) }, + { "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) }, + { "24cs64", AT24_DEVICE_MAGIC(16, + AT24_FLAG_ADDR16 | + AT24_FLAG_SERIAL | + AT24_FLAG_READONLY) }, + { "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) }, + { "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) }, + { "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) }, + { "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) }, + { "fm24c04", AT24_DEVICE_MAGIC(4096 / 8, AT24_FLAG_TAKE8ADDR) }, + { "fm24lc16", AT24_DEVICE_MAGIC(16384 / 8, AT24_FLAG_TAKE8ADDR) }, { "at24", 0 }, { /* END OF LIST */ } }; @@ -144,9 +201,22 @@ MODULE_DEVICE_TABLE(acpi, at24_acpi_ids); * This routine supports chips which consume multiple I2C addresses. It * computes the addressing information to be used for a given r/w request. * Assumes that sanity checks for offset happened at sysfs-layer. + * + * Slave address and byte offset derive from the offset. Always + * set the byte address; on a multi-master board, another master + * may have changed the chip's "current" address pointer. + * + * REVISIT some multi-address chips don't rollover page reads to + * the next slave address, so we may need to truncate the count. + * Those chips might need another quirk flag. + * + * If the real hardware used four adjacent 24c02 chips and that + * were misconfigured as one 24c08, that would be a similar effect: + * one "eeprom" file not four, but larger reads would fail when + * they crossed certain pages. */ static struct i2c_client *at24_translate_offset(struct at24_data *at24, - unsigned *offset) + unsigned int *offset) { unsigned i; @@ -161,165 +231,234 @@ static struct i2c_client *at24_translate_offset(struct at24_data *at24, return at24->client[i]; } -static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, - unsigned offset, size_t count) +static ssize_t at24_eeprom_read_smbus(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + int status; + + client = at24_translate_offset(at24, &offset); + + if (count > io_limit) + count = io_limit; + + /* Smaller eeproms can work given some SMBus extension calls */ + if (count > I2C_SMBUS_BLOCK_MAX) + count = I2C_SMBUS_BLOCK_MAX; + + loop_until_timeout(timeout, read_time) { + status = i2c_smbus_read_i2c_block_data_or_emulated(client, + offset, + count, buf); + + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_seco_read(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) { - struct i2c_msg msg[2]; - u8 msgbuf[2]; struct i2c_client *client; unsigned long timeout, read_time; + struct i2c_msg msg[2]; int status, i; + u8 msgbuf[2]; memset(msg, 0, sizeof(msg)); + client = at24_translate_offset(at24, &offset); + if (count > io_limit) + count = io_limit; /* - * REVISIT some multi-address chips don't rollover page reads to - * the next slave address, so we may need to truncate the count. - * Those chips might need another quirk flag. - * - * If the real hardware used four adjacent 24c02 chips and that - * were misconfigured as one 24c08, that would be a similar effect: - * one "eeprom" file not four, but larger reads would fail when - * they crossed certain pages. + * When we have a better choice than SMBus calls, use a combined I2C + * message. Write address; then read up to io_limit data bytes. Note + * that read page rollover helps us here (unlike writes). msgbuf is + * u8 and will cast to our needs. */ + i = 0; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msgbuf[i++] = offset >> 8; + msgbuf[i++] = offset; - /* - * Slave address and byte offset derive from the offset. Always - * set the byte address; on a multi-master board, another master - * may have changed the chip's "current" address pointer. - */ - client = at24_translate_offset(at24, &offset); + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; - if (count > io_limit) - count = io_limit; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; - if (at24->use_smbus) { - /* Smaller eeproms can work given some SMBus extension calls */ - if (count > I2C_SMBUS_BLOCK_MAX) - count = I2C_SMBUS_BLOCK_MAX; - } else { - /* - * When we have a better choice than SMBus calls, use a - * combined I2C message. Write address; then read up to - * io_limit data bytes. Note that read page rollover helps us - * here (unlike writes). msgbuf is u8 and will cast to our - * needs. - */ - i = 0; - if (at24->chip.flags & AT24_FLAG_ADDR16) - msgbuf[i++] = offset >> 8; - msgbuf[i++] = offset; - - msg[0].addr = client->addr; - msg[0].buf = msgbuf; - msg[0].len = i; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].buf = buf; - msg[1].len = count; + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; } + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_read_i2c(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + int status, i; + u8 msgbuf[2]; + + + memset(msg, 0, sizeof(msg)); + client = at24_translate_offset(at24, &offset); + if (count > io_limit) + count = io_limit; + /* - * Reads fail if the previous write didn't complete yet. We may - * loop a few times until this one succeeds, waiting at least - * long enough for one entire page write to work. + * When we have a better choice than SMBus calls, use a combined I2C + * message. Write address; then read up to io_limit data bytes. Note + * that read page rollover helps us here (unlike writes). msgbuf is + * u8 and will cast to our needs. */ - timeout = jiffies + msecs_to_jiffies(write_timeout); - do { - read_time = jiffies; - if (at24->use_smbus) { - status = i2c_smbus_read_i2c_block_data_or_emulated(client, offset, - count, buf); - } else { - status = i2c_transfer(client->adapter, msg, 2); - if (status == 2) - status = count; - } + i = 0; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msgbuf[i++] = offset >> 8; + msgbuf[i++] = offset; + + msg[0].addr = client->addr; + msg[0].buf = msgbuf; + msg[0].len = i; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + status = count; + dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies); if (status == count) return count; - - /* REVISIT: at HZ=100, this is sloooow */ - msleep(1); - } while (time_before(read_time, timeout)); + } return -ETIMEDOUT; } -static ssize_t at24_read(struct at24_data *at24, - char *buf, loff_t off, size_t count) +static ssize_t at24_eeprom_read_serial(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) { - ssize_t retval = 0; + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + u8 addrbuf[2]; + int status; - if (unlikely(!count)) - return count; + client = at24_translate_offset(at24, &offset); - if (off + count > at24->chip.byte_len) - return -EINVAL; + memset(msg, 0, sizeof(msg)); + msg[0].addr = client->addr; + msg[0].buf = addrbuf; /* - * Read data from chip, protecting against concurrent updates - * from this host, but not from other I2C masters. + * The address pointer of the device is shared between the regular + * EEPROM array and the serial number block. The dummy write (part of + * the sequential read protocol) ensures the address pointer is reset + * to the desired position. */ - mutex_lock(&at24->lock); + if (at24->chip.flags & AT24_FLAG_ADDR16) { + /* + * For 16 bit address pointers, the word address must contain + * a '10' sequence in bits 11 and 10 regardless of the + * intended position of the address pointer. + */ + addrbuf[0] = 0x08; + addrbuf[1] = offset; + msg[0].len = 2; + } else { + /* + * Otherwise the word address must begin with a '10' sequence, + * regardless of the intended address. + */ + addrbuf[0] = 0x80 + offset; + msg[0].len = 1; + } - while (count) { - ssize_t status; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; - status = at24_eeprom_read(at24, buf, off, count); - if (status <= 0) { - if (retval == 0) - retval = status; - break; - } - buf += status; - off += status; - count -= status; - retval += status; + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + return count; } - mutex_unlock(&at24->lock); - - return retval; + return -ETIMEDOUT; } -static ssize_t at24_bin_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static ssize_t at24_eeprom_read_mac(struct at24_data *at24, char *buf, + unsigned int offset, size_t count) { - struct at24_data *at24; + unsigned long timeout, read_time; + struct i2c_client *client; + struct i2c_msg msg[2]; + u8 addrbuf[2]; + int status; - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); - return at24_read(at24, buf, off, count); -} + client = at24_translate_offset(at24, &offset); + memset(msg, 0, sizeof(msg)); + msg[0].addr = client->addr; + msg[0].buf = addrbuf; + /* EUI-48 starts from 0x9a, EUI-64 from 0x98 */ + addrbuf[0] = 0xa0 - at24->chip.byte_len + offset; + msg[0].len = 1; + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = count; + + loop_until_timeout(timeout, read_time) { + status = i2c_transfer(client->adapter, msg, 2); + if (status == 2) + return count; + } + + return -ETIMEDOUT; +} /* * Note that if the hardware write-protect pin is pulled high, the whole * chip is normally write protected. But there are plenty of product * variants here, including OTP fuses and partial chip protect. * - * We only use page mode writes; the alternative is sloooow. This routine - * writes at most one page. + * We only use page mode writes; the alternative is sloooow. These routines + * write at most one page. */ -static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf, - unsigned offset, size_t count) + +static size_t at24_adjust_write_count(struct at24_data *at24, + unsigned int offset, size_t count) { - struct i2c_client *client; - struct i2c_msg msg; - ssize_t status = 0; - unsigned long timeout, write_time; unsigned next_page; - if (offset + count > at24->chip.byte_len) - return -EINVAL; - - /* Get corresponding I2C address and adjust offset */ - client = at24_translate_offset(at24, &offset); - /* write_max is at most a page */ if (count > at24->write_max) count = at24->write_max; @@ -329,153 +468,374 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf, if (offset + count > next_page) count = next_page - offset; - /* If we'll use I2C calls for I/O, set up the message */ - if (!at24->use_smbus) { - int i = 0; + return count; +} + +static ssize_t at24_eeprom_write_smbus_block(struct at24_data *at24, + const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + ssize_t status = 0; + + client = at24_translate_offset(at24, &offset); + count = at24_adjust_write_count(at24, offset, count); - msg.addr = client->addr; - msg.flags = 0; + loop_until_timeout(timeout, write_time) { + status = i2c_smbus_write_i2c_block_data(client, + offset, count, buf); + if (status == 0) + status = count; - /* msg.buf is u8 and casts will mask the values */ - msg.buf = at24->writebuf; - if (at24->chip.flags & AT24_FLAG_ADDR16) - msg.buf[i++] = offset >> 8; + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); - msg.buf[i++] = offset; - memcpy(&msg.buf[i], buf, count); - msg.len = i + count; + if (status == count) + return count; } - /* - * Writes fail if the previous one didn't complete yet. We may - * loop a few times until this one succeeds, waiting at least - * long enough for one entire page write to work. - */ - timeout = jiffies + msecs_to_jiffies(write_timeout); - do { - write_time = jiffies; - if (at24->use_smbus_write) { - switch (at24->use_smbus_write) { - case I2C_SMBUS_I2C_BLOCK_DATA: - status = i2c_smbus_write_i2c_block_data(client, - offset, count, buf); - break; - case I2C_SMBUS_BYTE_DATA: - status = i2c_smbus_write_byte_data(client, - offset, buf[0]); - break; - } - - if (status == 0) - status = count; - } else { - status = i2c_transfer(client->adapter, &msg, 1); - if (status == 1) - status = count; - } + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_write_smbus_byte(struct at24_data *at24, + const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + ssize_t status = 0; + + client = at24_translate_offset(at24, &offset); + + loop_until_timeout(timeout, write_time) { + status = i2c_smbus_write_byte_data(client, offset, buf[0]); + if (status == 0) + status = count; + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", count, offset, status, jiffies); if (status == count) return count; + } + + return -ETIMEDOUT; +} + +static ssize_t at24_eeprom_seco_write(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + struct i2c_msg msg; + ssize_t status = 0; + int i = 0; + + client = at24_translate_offset(at24, &offset); + count = at24_adjust_write_count(at24, offset, count); + + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = at24->writebuf; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msg.buf[i++] = offset >> 8; - /* REVISIT: at HZ=100, this is sloooow */ - msleep(1); - } while (time_before(write_time, timeout)); + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + + loop_until_timeout(timeout, write_time) { + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + + + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } + + return -ETIMEDOUT; +} +static ssize_t at24_eeprom_write_i2c(struct at24_data *at24, const char *buf, + unsigned int offset, size_t count) +{ + unsigned long timeout, write_time; + struct i2c_client *client; + struct i2c_msg msg; + ssize_t status = 0; + int i = 0; + + client = at24_translate_offset(at24, &offset); + count = at24_adjust_write_count(at24, offset, count); + + msg.addr = client->addr; + msg.flags = 0; + + /* msg.buf is u8 and casts will mask the values */ + msg.buf = at24->writebuf; + if (at24->chip.flags & AT24_FLAG_ADDR16) + msg.buf[i++] = offset >> 8; + + msg.buf[i++] = offset; + memcpy(&msg.buf[i], buf, count); + msg.len = i + count; + + loop_until_timeout(timeout, write_time) { + status = i2c_transfer(client->adapter, &msg, 1); + if (status == 1) + status = count; + + dev_dbg(&client->dev, "write %zu@%d --> %zd (%ld)\n", + count, offset, status, jiffies); + + if (status == count) + return count; + } return -ETIMEDOUT; } -static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off, - size_t count) +static int at24_serial_read(void *priv, unsigned int off, void *val, size_t count) { - ssize_t retval = 0; + struct at24_data *at24 = priv; + char *buf = val; - if (unlikely(!count)) - return count; + off = 0; /* - * Write data to chip, protecting against concurrent updates + * Read data from chip, protecting against concurrent updates * from this host, but not from other I2C masters. */ mutex_lock(&at24->lock); while (count) { - ssize_t status; + int status; - status = at24_eeprom_write(at24, buf, off, count); - if (status <= 0) { - if (retval == 0) - retval = status; - break; + status = at24->read_func(at24, buf, 6, 9); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; } buf += status; off += status; count -= status; - retval += status; } mutex_unlock(&at24->lock); - return retval; + return 0; } -static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t off, size_t count) +static int at24_mac_read(void *priv, unsigned int off, void *val, size_t count) { - struct at24_data *at24; + struct at24_data *at24 = priv; + char *buf = val; + char buf1[2]; + char buf2[6]; + int i; + + off = 0; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + int status; + + status = at24->read_func(at24, buf2, 0, 6); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + buf += status; + off += status; + count -= status; + } + + mutex_unlock(&at24->lock); + + for (i=0; i < 6; i++) { + sprintf(buf1, "%x%x", (buf2[i] & 0xF0) >> 4, buf2[i] & 0xF); + if (i == 0) + sprintf(buf, "%s", buf1); + else + sprintf(buf, "%s%s", buf, buf1); + } + sprintf(val, "%s", buf); - at24 = dev_get_drvdata(container_of(kobj, struct device, kobj)); - return at24_write(at24, buf, off, count); + return 0; } -/*-------------------------------------------------------------------------*/ +static int at24_read(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24 = priv; + char *buf = val; -/* - * This lets other kernel code access the eeprom data. For example, it - * might hold a board's Ethernet address, or board-specific calibration - * data generated on the manufacturing floor. - */ + if (unlikely(!count)) + return count; + + if (off + count > at24->chip.byte_len) + return -EINVAL; + + /* + * Read data from chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + while (count) { + int status; + + status = at24->read_func(at24, buf, off, count); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + buf += status; + off += status; + count -= status; + } + + mutex_unlock(&at24->lock); -static ssize_t at24_macc_read(struct memory_accessor *macc, char *buf, - off_t offset, size_t count) + return 0; +} + +static int at24_serial_write(void *priv, unsigned int off, void *val, size_t count) { - struct at24_data *at24 = container_of(macc, struct at24_data, macc); + struct at24_data *at24 = priv; + char *buf = val; + int status; + + if (count != (SECO_SERIAL_CHAR_LEN)) + return -EINVAL; - return at24_read(at24, buf, offset, count); + if (buf[SECO_SERIAL_CHAR_LEN] != '\0') + return -EINVAL; + + if ((buf[0] != 'k') && (buf[0] != 'K')) + return -EINVAL; + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); + + status = at24->write_func(at24, buf, SECO_SERIAL_OFFSET, SECO_SERIAL_CHAR_LEN); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + mutex_unlock(&at24->lock); + + return 0; } -static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf, - off_t offset, size_t count) +static int at24_mac_write(void *priv, unsigned int off, void *val, size_t count) { - struct at24_data *at24 = container_of(macc, struct at24_data, macc); + struct at24_data *at24 = priv; + char *buf = val; + char buf1[2]; + char buf2[6]; + int i, status; + + if (count != (SECO_MAC_CHAR_LEN)) + return -EINVAL; + + if (buf[SECO_MAC_CHAR_LEN] != '\0') + return -EINVAL; + + for (i=0; i < 6; i++) { + sprintf(buf1, "%c%c", buf[i*2], buf[1 + i*2]); + + if (!(((buf[i*2] >= '0') && (buf[i*2] <= '9')) || ((buf[i*2] >= 0x61) && (buf[i*2] <= 0x66)) || ((buf[i*2] >= 0x41) && (buf[i*2] <= 0x46)))) + return -EINVAL; + if (!(((buf[1 + i*2] >= '0') && (buf[1 + i*2] <= '9')) || ((buf[1 + i*2] >= 'a') && (buf[1 + i*2] <= 'f')) || ((buf[1 + i*2] >= 'A') && (buf[1 + i*2] <= 'F')))) + return -EINVAL; + buf2[i] = (unsigned int)simple_strtoul(buf1, NULL, 16); + } + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); - return at24_write(at24, buf, offset, count); + status = at24->write_func(at24, buf2, SECO_MAC_OFFSET, SECO_MAC_CHAR_LEN/2); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + mutex_unlock(&at24->lock); + + return 0; } -/*-------------------------------------------------------------------------*/ +static int at24_write(void *priv, unsigned int off, void *val, size_t count) +{ + struct at24_data *at24 = priv; + char *buf = val; + + if (unlikely(!count)) + return -EINVAL; + + if (off + count > at24->chip.byte_len) + return -EINVAL; + + /* + * Write data to chip, protecting against concurrent updates + * from this host, but not from other I2C masters. + */ + mutex_lock(&at24->lock); -#ifdef CONFIG_OF -static void at24_get_ofdata(struct i2c_client *client, - struct at24_platform_data *chip) + while (count) { + int status; + + status = at24->write_func(at24, buf, off, count); + if (status < 0) { + mutex_unlock(&at24->lock); + return status; + } + buf += status; + off += status; + count -= status; + } + + mutex_unlock(&at24->lock); + + return 0; +} + +static void at24_get_pdata(struct device *dev, struct at24_platform_data *chip) { - const __be32 *val; - struct device_node *node = client->dev.of_node; - - if (node) { - if (of_get_property(node, "read-only", NULL)) - chip->flags |= AT24_FLAG_READONLY; - val = of_get_property(node, "pagesize", NULL); - if (val) - chip->page_size = be32_to_cpup(val); + int err; + u32 val; + + if (device_property_present(dev, "read-only")) + chip->flags |= AT24_FLAG_READONLY; + + err = device_property_read_u32(dev, "pagesize", &val); + if (!err) { + chip->page_size = val; + } else { + /* + * This is slow, but we can't know all eeproms, so we better + * play safe. Specifying custom eeprom-types via platform_data + * is recommended anyhow. + */ + chip->page_size = 1; } } -#else -static void at24_get_ofdata(struct i2c_client *client, - struct at24_platform_data *chip) -{ } -#endif /* CONFIG_OF */ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -487,6 +847,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) struct at24_data *at24; int err; unsigned i, num_addresses; + u8 test_byte; if (client->dev.platform_data) { chip = *(struct at24_platform_data *)client->dev.platform_data; @@ -506,15 +867,8 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN)); magic >>= AT24_SIZE_BYTELEN; chip.flags = magic & AT24_BITMASK(AT24_SIZE_FLAGS); - /* - * This is slow, but we can't know all eeproms, so we better - * play safe. Specifying custom eeprom-types via platform_data - * is recommended anyhow. - */ - chip.page_size = 1; - /* update chipdata if OF is present */ - at24_get_ofdata(client, &chip); + at24_get_pdata(&client->dev, &chip); chip.setup = NULL; chip.context = NULL; @@ -531,6 +885,16 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_warn(&client->dev, "page_size looks suspicious (no power of 2)!\n"); + /* + * REVISIT: the size of the EUI-48 byte array is 6 in at24mac402, while + * the call to ilog2() in AT24_DEVICE_MAGIC() rounds it down to 4. + * + * Eventually we'll get rid of the magic values altoghether in favor of + * real structs, but for now just manually set the right size. + */ + if (chip.flags & AT24_FLAG_MAC && chip.byte_len == 4) + chip.byte_len = 6; + /* 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) @@ -548,10 +912,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) } else { return -EPFNOSUPPORT; } - } - /* 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, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) { use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA; @@ -579,17 +940,34 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) at24->chip = chip; at24->num_addresses = num_addresses; - /* - * Export the EEPROM bytes through sysfs, since that's convenient. - * By default, only root should see the data (maybe passwords etc) - */ - sysfs_bin_attr_init(&at24->bin); - at24->bin.attr.name = "eeprom"; - at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR; - at24->bin.read = at24_bin_read; - at24->bin.size = chip.byte_len; + if ((chip.flags & AT24_FLAG_SERIAL) && (chip.flags & AT24_FLAG_MAC)) { + dev_err(&client->dev, + "invalid device data - cannot have both AT24_FLAG_SERIAL & AT24_FLAG_MAC."); + return -EINVAL; + } - at24->macc.read = at24_macc_read; + if (chip.flags & AT24_FLAG_SERIAL) { + at24->read_func = at24_eeprom_read_serial; + } else if (chip.flags & AT24_FLAG_MAC) { + at24->read_func = at24_eeprom_read_mac; + } else if (chip.flags & AT24_FLAG_SECO_PARTITION) { + at24->read_func = at24_eeprom_seco_read; + } else { + at24->read_func = at24->use_smbus ? at24_eeprom_read_smbus + : at24_eeprom_read_i2c; + } + + if (at24->use_smbus) { + if (at24->use_smbus_write == I2C_SMBUS_I2C_BLOCK_DATA) + at24->write_func = at24_eeprom_write_smbus_block; + else + at24->write_func = at24_eeprom_write_smbus_byte; + } else { + if (chip.flags & AT24_FLAG_SECO_PARTITION) + at24->write_func = at24_eeprom_seco_write; + else + at24->write_func = at24_eeprom_write_i2c; + } writable = !(chip.flags & AT24_FLAG_READONLY); if (writable) { @@ -597,11 +975,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) unsigned write_max = chip.page_size; - at24->macc.write = at24_macc_write; - - at24->bin.write = at24_bin_write; - at24->bin.attr.mode |= S_IWUSR; - if (write_max > io_limit) write_max = io_limit; if (use_smbus && write_max > I2C_SMBUS_BLOCK_MAX) @@ -633,14 +1006,89 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) } } - err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); - if (err) - goto err_clients; - i2c_set_clientdata(client, at24); - dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n", - at24->bin.size, client->name, + /* + * Perform a one-byte test read to verify that the + * chip is functional. + */ + err = at24_read(at24, 0, &test_byte, 1); + if (err) { + err = -ENODEV; + goto err_clients; + } + + if (chip.flags & AT24_FLAG_SECO_PARTITION) { + at24->serial_nvmem_config.name = "serial_seco"; + at24->serial_nvmem_config.dev = &client->dev; + at24->serial_nvmem_config.read_only = false; + at24->serial_nvmem_config.root_only = true; + at24->serial_nvmem_config.owner = THIS_MODULE; + at24->serial_nvmem_config.compat = false; + at24->serial_nvmem_config.base_dev = &client->dev; + at24->serial_nvmem_config.reg_read = at24_serial_read; + at24->serial_nvmem_config.reg_write = at24_serial_write; + at24->serial_nvmem_config.priv = at24; + at24->serial_nvmem_config.stride = 1; + at24->serial_nvmem_config.word_size = 1; + at24->serial_nvmem_config.size = SECO_SERIAL_CHAR_LEN; + + + at24->serial_nvmem = nvmem_register(&at24->serial_nvmem_config); + + + if (IS_ERR(at24->nvmem)) { + err = PTR_ERR(at24->nvmem); + goto err_clients; + } + + at24->mac_nvmem_config.name = "mac_seco"; + at24->mac_nvmem_config.dev = &client->dev; + at24->mac_nvmem_config.read_only = false; + at24->mac_nvmem_config.root_only = true; + at24->mac_nvmem_config.owner = THIS_MODULE; + at24->mac_nvmem_config.compat = false; + at24->mac_nvmem_config.base_dev = &client->dev; + at24->mac_nvmem_config.reg_read = at24_mac_read; + at24->mac_nvmem_config.reg_write = at24_mac_write; + at24->mac_nvmem_config.priv = at24; + at24->mac_nvmem_config.stride = 1; + at24->mac_nvmem_config.word_size = 1; + at24->mac_nvmem_config.size = SECO_MAC_CHAR_LEN; + + + at24->mac_nvmem = nvmem_register(&at24->mac_nvmem_config); + + + if (IS_ERR(at24->mac_nvmem)) { + err = PTR_ERR(at24->mac_nvmem); + goto err_clients; + } + } else { + at24->nvmem_config.name = dev_name(&client->dev); + at24->nvmem_config.dev = &client->dev; + at24->nvmem_config.read_only = !writable; + at24->nvmem_config.root_only = true; + at24->nvmem_config.owner = THIS_MODULE; + at24->nvmem_config.compat = true; + at24->nvmem_config.base_dev = &client->dev; + at24->nvmem_config.reg_read = at24_read; + at24->nvmem_config.reg_write = at24_write; + at24->nvmem_config.priv = at24; + at24->nvmem_config.stride = 1; + at24->nvmem_config.word_size = 1; + at24->nvmem_config.size = chip.byte_len; + + at24->nvmem = nvmem_register(&at24->nvmem_config); + + if (IS_ERR(at24->nvmem)) { + err = PTR_ERR(at24->nvmem); + goto err_clients; + } + } + + dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n", + chip.byte_len, client->name, writable ? "writable" : "read-only", at24->write_max); if (use_smbus == I2C_SMBUS_WORD_DATA || use_smbus == I2C_SMBUS_BYTE_DATA) { @@ -651,7 +1099,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) /* export data to kernel code */ if (chip.setup) - chip.setup(&at24->macc, chip.context); + chip.setup(at24->nvmem, chip.context); return 0; @@ -669,7 +1117,8 @@ static int at24_remove(struct i2c_client *client) int i; at24 = i2c_get_clientdata(client); - sysfs_remove_bin_file(&client->dev.kobj, &at24->bin); + + nvmem_unregister(at24->nvmem); for (i = 1; i < at24->num_addresses; i++) i2c_unregister_device(at24->client[i]); diff --git a/include/linux/platform_data/at24.h b/include/linux/platform_data/at24.h index c42aa89d34eeb46ff0ec5f4e68229ad1fa0fad2a..aa3c96189fae781120f20bf15a2097e842c3eb68 100644 --- a/include/linux/platform_data/at24.h +++ b/include/linux/platform_data/at24.h @@ -9,7 +9,8 @@ #define _LINUX_AT24_H #include <linux/types.h> -#include <linux/memory.h> +#include <linux/nvmem-consumer.h> +#include <linux/bitops.h> /** * struct at24_platform_data - data to set up at24 (generic eeprom) driver @@ -17,7 +18,7 @@ * @page_size: number of byte which can be written in one go * @flags: tunable options, check AT24_FLAG_* defines * @setup: an optional callback invoked after eeprom is probed; enables kernel - code to access eeprom via memory_accessor, see example + code to access eeprom via nvmem, see example * @context: optional parameter passed to setup() * * If you set up a custom eeprom type, please double-check the parameters. @@ -26,13 +27,13 @@ * * An example in pseudo code for a setup() callback: * - * void get_mac_addr(struct memory_accessor *mem_acc, void *context) + * void get_mac_addr(struct nvmem_device *nvmem, void *context) * { * u8 *mac_addr = ethernet_pdata->mac_addr; * off_t offset = context; * * // Read MAC addr from EEPROM - * if (mem_acc->read(mem_acc, mac_addr, offset, ETH_ALEN) == ETH_ALEN) + * if (nvmem_device_read(nvmem, offset, ETH_ALEN, mac_addr) == ETH_ALEN) * pr_info("Read MAC addr from EEPROM: %pM\n", mac_addr); * } * @@ -43,13 +44,18 @@ struct at24_platform_data { u32 byte_len; /* size (sum of all addr) */ u16 page_size; /* for writes */ u8 flags; -#define AT24_FLAG_ADDR16 0x80 /* address pointer is 16 bit */ -#define AT24_FLAG_READONLY 0x40 /* sysfs-entry will be read-only */ -#define AT24_FLAG_IRUGO 0x20 /* sysfs-entry will be world-readable */ -#define AT24_FLAG_TAKE8ADDR 0x10 /* take always 8 addresses (24c00) */ +#define AT24_FLAG_ADDR16 BIT(7) /* address pointer is 16 bit */ +#define AT24_FLAG_READONLY BIT(6) /* sysfs-entry will be read-only */ +#define AT24_FLAG_IRUGO BIT(5) /* sysfs-entry will be world-readable */ +#define AT24_FLAG_TAKE8ADDR BIT(4) /* take always 8 addresses (24c00) */ +#define AT24_FLAG_SERIAL BIT(3) /* factory-programmed serial number */ +#define AT24_FLAG_MAC BIT(2) /* factory-programmed mac address */ +#define AT24_FLAG_SECO_PARTITION BIT(1) /* Seco eeprom partitioning reserve first 32 byte */ - void (*setup)(struct memory_accessor *, void *context); + + void (*setup)(struct nvmem_device *nvmem, void *context); void *context; }; #endif /* _LINUX_AT24_H */ +