|
@@ -30,6 +30,7 @@
|
|
|
|
|
|
struct at25_data {
|
|
struct at25_data {
|
|
struct spi_device *spi;
|
|
struct spi_device *spi;
|
|
|
|
+ struct memory_accessor mem;
|
|
struct mutex lock;
|
|
struct mutex lock;
|
|
struct spi_eeprom chip;
|
|
struct spi_eeprom chip;
|
|
struct bin_attribute bin;
|
|
struct bin_attribute bin;
|
|
@@ -75,6 +76,13 @@ at25_ee_read(
|
|
struct spi_transfer t[2];
|
|
struct spi_transfer t[2];
|
|
struct spi_message m;
|
|
struct spi_message m;
|
|
|
|
|
|
|
|
+ if (unlikely(offset >= at25->bin.size))
|
|
|
|
+ return 0;
|
|
|
|
+ if ((offset + count) > at25->bin.size)
|
|
|
|
+ count = at25->bin.size - offset;
|
|
|
|
+ if (unlikely(!count))
|
|
|
|
+ return count;
|
|
|
|
+
|
|
cp = command;
|
|
cp = command;
|
|
*cp++ = AT25_READ;
|
|
*cp++ = AT25_READ;
|
|
|
|
|
|
@@ -127,13 +135,6 @@ at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr,
|
|
dev = container_of(kobj, struct device, kobj);
|
|
dev = container_of(kobj, struct device, kobj);
|
|
at25 = dev_get_drvdata(dev);
|
|
at25 = dev_get_drvdata(dev);
|
|
|
|
|
|
- if (unlikely(off >= at25->bin.size))
|
|
|
|
- return 0;
|
|
|
|
- if ((off + count) > at25->bin.size)
|
|
|
|
- count = at25->bin.size - off;
|
|
|
|
- if (unlikely(!count))
|
|
|
|
- return count;
|
|
|
|
-
|
|
|
|
return at25_ee_read(at25, buf, off, count);
|
|
return at25_ee_read(at25, buf, off, count);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -146,6 +147,13 @@ at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count)
|
|
unsigned buf_size;
|
|
unsigned buf_size;
|
|
u8 *bounce;
|
|
u8 *bounce;
|
|
|
|
|
|
|
|
+ if (unlikely(off >= at25->bin.size))
|
|
|
|
+ return -EFBIG;
|
|
|
|
+ if ((off + count) > at25->bin.size)
|
|
|
|
+ count = at25->bin.size - off;
|
|
|
|
+ if (unlikely(!count))
|
|
|
|
+ return count;
|
|
|
|
+
|
|
/* Temp buffer starts with command and address */
|
|
/* Temp buffer starts with command and address */
|
|
buf_size = at25->chip.page_size;
|
|
buf_size = at25->chip.page_size;
|
|
if (buf_size > io_limit)
|
|
if (buf_size > io_limit)
|
|
@@ -253,18 +261,31 @@ at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr,
|
|
dev = container_of(kobj, struct device, kobj);
|
|
dev = container_of(kobj, struct device, kobj);
|
|
at25 = dev_get_drvdata(dev);
|
|
at25 = dev_get_drvdata(dev);
|
|
|
|
|
|
- if (unlikely(off >= at25->bin.size))
|
|
|
|
- return -EFBIG;
|
|
|
|
- if ((off + count) > at25->bin.size)
|
|
|
|
- count = at25->bin.size - off;
|
|
|
|
- if (unlikely(!count))
|
|
|
|
- return count;
|
|
|
|
-
|
|
|
|
return at25_ee_write(at25, buf, off, count);
|
|
return at25_ee_write(at25, buf, off, count);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
+/* Let in-kernel code access the eeprom data. */
|
|
|
|
+
|
|
|
|
+static ssize_t at25_mem_read(struct memory_accessor *mem, char *buf,
|
|
|
|
+ off_t offset, size_t count)
|
|
|
|
+{
|
|
|
|
+ struct at25_data *at25 = container_of(mem, struct at25_data, mem);
|
|
|
|
+
|
|
|
|
+ return at25_ee_read(at25, buf, offset, count);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static ssize_t at25_mem_write(struct memory_accessor *mem, char *buf,
|
|
|
|
+ off_t offset, size_t count)
|
|
|
|
+{
|
|
|
|
+ struct at25_data *at25 = container_of(mem, struct at25_data, mem);
|
|
|
|
+
|
|
|
|
+ return at25_ee_write(at25, buf, offset, count);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*-------------------------------------------------------------------------*/
|
|
|
|
+
|
|
static int at25_probe(struct spi_device *spi)
|
|
static int at25_probe(struct spi_device *spi)
|
|
{
|
|
{
|
|
struct at25_data *at25 = NULL;
|
|
struct at25_data *at25 = NULL;
|
|
@@ -317,6 +338,10 @@ static int at25_probe(struct spi_device *spi)
|
|
at25->addrlen = addrlen;
|
|
at25->addrlen = addrlen;
|
|
|
|
|
|
/* Export the EEPROM bytes through sysfs, since that's convenient.
|
|
/* 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
|
|
|
|
+ * manufacturing floor.
|
|
|
|
+ *
|
|
* Default to root-only access to the data; EEPROMs often hold data
|
|
* Default to root-only access to the data; EEPROMs often hold data
|
|
* that's sensitive for read and/or write, like ethernet addresses,
|
|
* that's sensitive for read and/or write, like ethernet addresses,
|
|
* security codes, board-specific manufacturing calibrations, etc.
|
|
* security codes, board-specific manufacturing calibrations, etc.
|
|
@@ -324,17 +349,22 @@ static int at25_probe(struct spi_device *spi)
|
|
at25->bin.attr.name = "eeprom";
|
|
at25->bin.attr.name = "eeprom";
|
|
at25->bin.attr.mode = S_IRUSR;
|
|
at25->bin.attr.mode = S_IRUSR;
|
|
at25->bin.read = at25_bin_read;
|
|
at25->bin.read = at25_bin_read;
|
|
|
|
+ at25->mem.read = at25_mem_read;
|
|
|
|
|
|
at25->bin.size = at25->chip.byte_len;
|
|
at25->bin.size = at25->chip.byte_len;
|
|
if (!(chip->flags & EE_READONLY)) {
|
|
if (!(chip->flags & EE_READONLY)) {
|
|
at25->bin.write = at25_bin_write;
|
|
at25->bin.write = at25_bin_write;
|
|
at25->bin.attr.mode |= S_IWUSR;
|
|
at25->bin.attr.mode |= S_IWUSR;
|
|
|
|
+ at25->mem.write = at25_mem_write;
|
|
}
|
|
}
|
|
|
|
|
|
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
|
|
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
|
|
if (err)
|
|
if (err)
|
|
goto fail;
|
|
goto fail;
|
|
|
|
|
|
|
|
+ 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 eeprom%s, pagesize %u\n",
|
|
(at25->bin.size < 1024)
|
|
(at25->bin.size < 1024)
|
|
? at25->bin.size
|
|
? at25->bin.size
|