aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc/eeprom
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/eeprom')
-rw-r--r--drivers/misc/eeprom/Kconfig51
-rw-r--r--drivers/misc/eeprom/Makefile3
-rw-r--r--drivers/misc/eeprom/at24.c92
-rw-r--r--drivers/misc/eeprom/at25.c160
-rw-r--r--drivers/misc/eeprom/digsy_mtc_eeprom.c85
-rw-r--r--drivers/misc/eeprom/eeprom.c17
-rw-r--r--drivers/misc/eeprom/eeprom_93cx6.c88
-rw-r--r--drivers/misc/eeprom/eeprom_93xx46.c398
-rw-r--r--drivers/misc/eeprom/max6875.c15
-rw-r--r--drivers/misc/eeprom/sunxi_sid.c157
10 files changed, 946 insertions, 120 deletions
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 9118613af32..9536852fd4c 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -1,13 +1,14 @@
menu "EEPROM support"
config EEPROM_AT24
- tristate "I2C EEPROMs from most vendors"
+ tristate "I2C EEPROMs / RAMs / ROMs from most vendors"
depends on I2C && SYSFS
help
- Enable this driver to get read/write support to most I2C EEPROMs,
- after you configure the driver to know about each EEPROM on
- your target board. Use these generic chip names, instead of
- vendor-specific ones like at24c64 or 24lc02:
+ Enable this driver to get read/write support to most I2C EEPROMs
+ and compatible devices like FRAMs, SRAMs, ROMs etc. After you
+ configure the driver to know about each chip on your target
+ board. Use these generic chip names, instead of vendor-specific
+ ones like at24c64, 24lc02 or fm24c04:
24c00, 24c01, 24c02, spd (readonly 24c02), 24c04, 24c08,
24c16, 24c32, 24c64, 24c128, 24c256, 24c512, 24c1024
@@ -50,7 +51,7 @@ config EEPROM_LEGACY
config EEPROM_MAX6875
tristate "Maxim MAX6874/5 power supply supervisor"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get read-only support for the user EEPROM of
the Maxim MAX6874/5 EEPROM-programmable, quad power-supply
@@ -70,4 +71,42 @@ config EEPROM_93CX6
If unsure, say N.
+config EEPROM_93XX46
+ tristate "Microwire EEPROM 93XX46 support"
+ depends on SPI && SYSFS
+ help
+ Driver for the microwire EEPROM chipsets 93xx46x. The driver
+ supports both read and write commands and also the command to
+ erase the whole EEPROM.
+
+ This driver can also be built as a module. If so, the module
+ will be called eeprom_93xx46.
+
+ If unsure, say N.
+
+config EEPROM_DIGSY_MTC_CFG
+ bool "DigsyMTC display configuration EEPROMs device"
+ depends on GPIO_MPC5200 && SPI_GPIO
+ help
+ This option enables access to display configuration EEPROMs
+ on digsy_mtc board. You have to additionally select Microwire
+ EEPROM 93XX46 driver. sysfs entries will be created for that
+ EEPROM allowing to read/write the configuration data or to
+ erase the whole EEPROM.
+
+ If unsure, say N.
+
+config EEPROM_SUNXI_SID
+ tristate "Allwinner sunxi security ID support"
+ depends on ARCH_SUNXI && SYSFS
+ help
+ This is a driver for the 'security ID' available on various Allwinner
+ devices.
+
+ Due to the potential risks involved with changing e-fuses,
+ this driver is read-only.
+
+ This driver can also be built as a module. If so, the module
+ will be called sunxi_sid.
+
endmenu
diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile
index df3d68ffa9d..9507aec95e9 100644
--- a/drivers/misc/eeprom/Makefile
+++ b/drivers/misc/eeprom/Makefile
@@ -3,3 +3,6 @@ obj-$(CONFIG_EEPROM_AT25) += at25.o
obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o
obj-$(CONFIG_EEPROM_MAX6875) += max6875.o
obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o
+obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o
+obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o
+obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 559b0b3c16c..d87f77f790d 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -20,8 +20,9 @@
#include <linux/log2.h>
#include <linux/bitops.h>
#include <linux/jiffies.h>
+#include <linux/of.h>
#include <linux/i2c.h>
-#include <linux/i2c/at24.h>
+#include <linux/platform_data/at24.h>
/*
* I2C EEPROMs from most vendors are inexpensive and mostly interchangeable.
@@ -427,6 +428,9 @@ static ssize_t at24_bin_write(struct file *filp, struct kobject *kobj,
{
struct at24_data *at24;
+ if (unlikely(off >= attr->size))
+ return -EFBIG;
+
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
@@ -457,6 +461,27 @@ static ssize_t at24_macc_write(struct memory_accessor *macc, const char *buf,
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_OF
+static void at24_get_ofdata(struct i2c_client *client,
+ 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);
+ }
+}
+#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)
{
struct at24_platform_data chip;
@@ -470,10 +495,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->dev.platform_data) {
chip = *(struct at24_platform_data *)client->dev.platform_data;
} else {
- if (!id->driver_data) {
- err = -ENODEV;
- goto err_out;
- }
+ if (!id->driver_data)
+ return -ENODEV;
+
magic = id->driver_data;
chip.byte_len = BIT(magic & AT24_BITMASK(AT24_SIZE_BYTELEN));
magic >>= AT24_SIZE_BYTELEN;
@@ -485,6 +509,9 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
*/
chip.page_size = 1;
+ /* update chipdata if OF is present */
+ at24_get_ofdata(client, &chip);
+
chip.setup = NULL;
chip.context = NULL;
}
@@ -492,16 +519,19 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!is_power_of_2(chip.byte_len))
dev_warn(&client->dev,
"byte_len looks suspicious (no power of 2)!\n");
+ if (!chip.page_size) {
+ dev_err(&client->dev, "page_size must not be 0!\n");
+ return -EINVAL;
+ }
if (!is_power_of_2(chip.page_size))
dev_warn(&client->dev,
"page_size looks suspicious (no power of 2)!\n");
/* 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) {
- err = -EPFNOSUPPORT;
- goto err_out;
- }
+ if (chip.flags & AT24_FLAG_ADDR16)
+ return -EPFNOSUPPORT;
+
if (i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
use_smbus = I2C_SMBUS_I2C_BLOCK_DATA;
@@ -512,8 +542,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
use_smbus = I2C_SMBUS_BYTE_DATA;
} else {
- err = -EPFNOSUPPORT;
- goto err_out;
+ return -EPFNOSUPPORT;
}
}
@@ -523,12 +552,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
num_addresses = DIV_ROUND_UP(chip.byte_len,
(chip.flags & AT24_FLAG_ADDR16) ? 65536 : 256);
- at24 = kzalloc(sizeof(struct at24_data) +
+ at24 = devm_kzalloc(&client->dev, sizeof(struct at24_data) +
num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
- if (!at24) {
- err = -ENOMEM;
- goto err_out;
- }
+ if (!at24)
+ return -ENOMEM;
mutex_init(&at24->lock);
at24->use_smbus = use_smbus;
@@ -566,11 +593,10 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
at24->write_max = write_max;
/* buffer (data + address at the beginning) */
- at24->writebuf = kmalloc(write_max + 2, GFP_KERNEL);
- if (!at24->writebuf) {
- err = -ENOMEM;
- goto err_struct;
- }
+ at24->writebuf = devm_kzalloc(&client->dev,
+ write_max + 2, GFP_KERNEL);
+ if (!at24->writebuf)
+ return -ENOMEM;
} else {
dev_warn(&client->dev,
"cannot write due to controller restrictions.");
@@ -597,19 +623,15 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
i2c_set_clientdata(client, at24);
- dev_info(&client->dev, "%zu byte %s EEPROM %s\n",
+ dev_info(&client->dev, "%zu byte %s EEPROM, %s, %u bytes/write\n",
at24->bin.size, client->name,
- writable ? "(writable)" : "(read-only)");
+ writable ? "writable" : "read-only", at24->write_max);
if (use_smbus == I2C_SMBUS_WORD_DATA ||
use_smbus == I2C_SMBUS_BYTE_DATA) {
dev_notice(&client->dev, "Falling back to %s reads, "
"performance will suffer\n", use_smbus ==
I2C_SMBUS_WORD_DATA ? "word" : "byte");
}
- dev_dbg(&client->dev,
- "page_size %d, num_addresses %d, write_max %d, use_smbus %d\n",
- chip.page_size, num_addresses,
- at24->write_max, use_smbus);
/* export data to kernel code */
if (chip.setup)
@@ -622,15 +644,10 @@ err_clients:
if (at24->client[i])
i2c_unregister_device(at24->client[i]);
- kfree(at24->writebuf);
-err_struct:
- kfree(at24);
-err_out:
- dev_dbg(&client->dev, "probe error %d\n", err);
return err;
}
-static int __devexit at24_remove(struct i2c_client *client)
+static int at24_remove(struct i2c_client *client)
{
struct at24_data *at24;
int i;
@@ -641,8 +658,6 @@ static int __devexit at24_remove(struct i2c_client *client)
for (i = 1; i < at24->num_addresses; i++)
i2c_unregister_device(at24->client[i]);
- kfree(at24->writebuf);
- kfree(at24);
return 0;
}
@@ -654,12 +669,17 @@ static struct i2c_driver at24_driver = {
.owner = THIS_MODULE,
},
.probe = at24_probe,
- .remove = __devexit_p(at24_remove),
+ .remove = at24_remove,
.id_table = at24_ids,
};
static int __init at24_init(void)
{
+ if (!io_limit) {
+ pr_err("at24: io_limit must not be 0!\n");
+ return -EINVAL;
+ }
+
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver);
}
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index c627e4174cc..634f72929e1 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
@@ -19,7 +18,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
-
+#include <linux/of.h>
/*
* NOTE: this is an *EEPROM* driver. The vagaries of product naming
@@ -50,6 +49,7 @@ struct at25_data {
#define AT25_SR_BP1 0x08
#define AT25_SR_WPEN 0x80 /* writeprotect enable */
+#define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */
#define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */
@@ -75,6 +75,7 @@ at25_ee_read(
ssize_t status;
struct spi_transfer t[2];
struct spi_message m;
+ u8 instr;
if (unlikely(offset >= at25->bin.size))
return 0;
@@ -84,7 +85,12 @@ at25_ee_read(
return count;
cp = command;
- *cp++ = AT25_READ;
+
+ instr = AT25_READ;
+ if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)
+ if (offset >= (1U << (at25->addrlen * 8)))
+ instr |= AT25_INSTR_BIT3;
+ *cp++ = instr;
/* 8/16/24-bit address is written MSB first */
switch (at25->addrlen) {
@@ -167,14 +173,14 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
/* For write, rollover is within the page ... so we write at
* most one page, then manually roll over to the next page.
*/
- bounce[0] = AT25_WRITE;
mutex_lock(&at25->lock);
do {
unsigned long timeout, retries;
unsigned segment;
unsigned offset = (unsigned) off;
- u8 *cp = bounce + 1;
+ u8 *cp = bounce;
int sr;
+ u8 instr;
*cp = AT25_WREN;
status = spi_write(at25->spi, cp, 1);
@@ -184,6 +190,12 @@ at25_ee_write(struct at25_data *at25, const char *buf, loff_t off,
break;
}
+ instr = AT25_WRITE;
+ if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR)
+ if (offset >= (1U << (at25->addrlen * 8)))
+ instr |= AT25_INSTR_BIT3;
+ *cp++ = instr;
+
/* 8/16/24-bit address is written MSB first */
switch (at25->addrlen) {
default: /* case 3 */
@@ -289,33 +301,93 @@ 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)
+{
+ u32 val;
+
+ memset(chip, 0, sizeof(*chip));
+ strncpy(chip->name, np->name, sizeof(chip->name));
+
+ if (of_property_read_u32(np, "size", &val) == 0 ||
+ of_property_read_u32(np, "at25,byte-len", &val) == 0) {
+ chip->byte_len = val;
+ } else {
+ dev_err(dev, "Error: missing \"size\" property\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "pagesize", &val) == 0 ||
+ of_property_read_u32(np, "at25,page-size", &val) == 0) {
+ chip->page_size = (u16)val;
+ } else {
+ dev_err(dev, "Error: missing \"pagesize\" property\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "at25,addr-mode", &val) == 0) {
+ chip->flags = (u16)val;
+ } else {
+ if (of_property_read_u32(np, "address-width", &val)) {
+ dev_err(dev,
+ "Error: missing \"address-width\" property\n");
+ return -ENODEV;
+ }
+ switch (val) {
+ case 8:
+ chip->flags |= EE_ADDR1;
+ break;
+ case 16:
+ chip->flags |= EE_ADDR2;
+ break;
+ case 24:
+ chip->flags |= EE_ADDR3;
+ break;
+ default:
+ dev_err(dev,
+ "Error: bad \"address-width\" property: %u\n",
+ val);
+ return -ENODEV;
+ }
+ if (of_find_property(np, "read-only", NULL))
+ chip->flags |= EE_READONLY;
+ }
+ return 0;
+}
+
static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
- const struct spi_eeprom *chip;
+ struct spi_eeprom chip;
+ struct device_node *np = spi->dev.of_node;
int err;
int sr;
int addrlen;
/* Chip description */
- chip = spi->dev.platform_data;
- if (!chip) {
- dev_dbg(&spi->dev, "no chip description\n");
- err = -ENODEV;
- goto fail;
- }
+ if (!spi->dev.platform_data) {
+ if (np) {
+ err = at25_np_to_chip(&spi->dev, np, &chip);
+ if (err)
+ return err;
+ } else {
+ dev_err(&spi->dev, "Error: no chip description\n");
+ return -ENODEV;
+ }
+ } else
+ chip = *(struct spi_eeprom *)spi->dev.platform_data;
/* For now we only support 8/16/24 bit addressing */
- if (chip->flags & EE_ADDR1)
+ if (chip.flags & EE_ADDR1)
addrlen = 1;
- else if (chip->flags & EE_ADDR2)
+ else if (chip.flags & EE_ADDR2)
addrlen = 2;
- else if (chip->flags & EE_ADDR3)
+ else if (chip.flags & EE_ADDR3)
addrlen = 3;
else {
dev_dbg(&spi->dev, "unsupported address type\n");
- err = -EINVAL;
- goto fail;
+ return -EINVAL;
}
/* Ping the chip ... the status register is pretty portable,
@@ -325,19 +397,17 @@ static int at25_probe(struct spi_device *spi)
sr = spi_w8r8(spi, AT25_RDSR);
if (sr < 0 || sr & AT25_SR_nRDY) {
dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr);
- err = -ENXIO;
- goto fail;
+ return -ENXIO;
}
- if (!(at25 = kzalloc(sizeof *at25, GFP_KERNEL))) {
- err = -ENOMEM;
- goto fail;
- }
+ at25 = devm_kzalloc(&spi->dev, sizeof(struct at25_data), GFP_KERNEL);
+ if (!at25)
+ return -ENOMEM;
mutex_init(&at25->lock);
- at25->chip = *chip;
+ at25->chip = chip;
at25->spi = spi_dev_get(spi);
- dev_set_drvdata(&spi->dev, at25);
+ spi_set_drvdata(spi, at25);
at25->addrlen = addrlen;
/* Export the EEPROM bytes through sysfs, since that's convenient.
@@ -356,7 +426,7 @@ static int at25_probe(struct spi_device *spi)
at25->mem.read = at25_mem_read;
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.attr.mode |= S_IWUSR;
at25->mem.write = at25_mem_write;
@@ -364,10 +434,10 @@ static int at25_probe(struct spi_device *spi)
err = sysfs_create_bin_file(&spi->dev.kobj, &at25->bin);
if (err)
- goto fail;
+ return err;
- if (chip->setup)
- chip->setup(&at25->mem, chip->context);
+ if (chip.setup)
+ chip.setup(&at25->mem, chip.context);
dev_info(&spi->dev, "%Zd %s %s eeprom%s, pagesize %u\n",
(at25->bin.size < 1024)
@@ -375,47 +445,39 @@ static int at25_probe(struct spi_device *spi)
: (at25->bin.size / 1024),
(at25->bin.size < 1024) ? "Byte" : "KByte",
at25->chip.name,
- (chip->flags & EE_READONLY) ? " (readonly)" : "",
+ (chip.flags & EE_READONLY) ? " (readonly)" : "",
at25->chip.page_size);
return 0;
-fail:
- dev_dbg(&spi->dev, "probe err %d\n", err);
- kfree(at25);
- return err;
}
-static int __devexit at25_remove(struct spi_device *spi)
+static int at25_remove(struct spi_device *spi)
{
struct at25_data *at25;
- at25 = dev_get_drvdata(&spi->dev);
+ at25 = spi_get_drvdata(spi);
sysfs_remove_bin_file(&spi->dev.kobj, &at25->bin);
- kfree(at25);
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",
.owner = THIS_MODULE,
+ .of_match_table = at25_of_match,
},
.probe = at25_probe,
- .remove = __devexit_p(at25_remove),
+ .remove = at25_remove,
};
-static int __init at25_init(void)
-{
- return spi_register_driver(&at25_driver);
-}
-module_init(at25_init);
-
-static void __exit at25_exit(void)
-{
- spi_unregister_driver(&at25_driver);
-}
-module_exit(at25_exit);
+module_spi_driver(at25_driver);
MODULE_DESCRIPTION("Driver for most SPI EEPROMs");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/misc/eeprom/digsy_mtc_eeprom.c b/drivers/misc/eeprom/digsy_mtc_eeprom.c
new file mode 100644
index 00000000000..66d9e1baeae
--- /dev/null
+++ b/drivers/misc/eeprom/digsy_mtc_eeprom.c
@@ -0,0 +1,85 @@
+/*
+ * EEPROMs access control driver for display configuration EEPROMs
+ * on DigsyMTC board.
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_gpio.h>
+#include <linux/eeprom_93xx46.h>
+
+#define GPIO_EEPROM_CLK 216
+#define GPIO_EEPROM_CS 210
+#define GPIO_EEPROM_DI 217
+#define GPIO_EEPROM_DO 249
+#define GPIO_EEPROM_OE 255
+#define EE_SPI_BUS_NUM 1
+
+static void digsy_mtc_op_prepare(void *p)
+{
+ /* enable */
+ gpio_set_value(GPIO_EEPROM_OE, 0);
+}
+
+static void digsy_mtc_op_finish(void *p)
+{
+ /* disable */
+ gpio_set_value(GPIO_EEPROM_OE, 1);
+}
+
+struct eeprom_93xx46_platform_data digsy_mtc_eeprom_data = {
+ .flags = EE_ADDR8,
+ .prepare = digsy_mtc_op_prepare,
+ .finish = digsy_mtc_op_finish,
+};
+
+static struct spi_gpio_platform_data eeprom_spi_gpio_data = {
+ .sck = GPIO_EEPROM_CLK,
+ .mosi = GPIO_EEPROM_DI,
+ .miso = GPIO_EEPROM_DO,
+ .num_chipselect = 1,
+};
+
+static struct platform_device digsy_mtc_eeprom = {
+ .name = "spi_gpio",
+ .id = EE_SPI_BUS_NUM,
+ .dev = {
+ .platform_data = &eeprom_spi_gpio_data,
+ },
+};
+
+static struct spi_board_info digsy_mtc_eeprom_info[] __initdata = {
+ {
+ .modalias = "93xx46",
+ .max_speed_hz = 1000000,
+ .bus_num = EE_SPI_BUS_NUM,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ .controller_data = (void *)GPIO_EEPROM_CS,
+ .platform_data = &digsy_mtc_eeprom_data,
+ },
+};
+
+static int __init digsy_mtc_eeprom_devices_init(void)
+{
+ int ret;
+
+ ret = gpio_request_one(GPIO_EEPROM_OE, GPIOF_OUT_INIT_HIGH,
+ "93xx46 EEPROMs OE");
+ if (ret) {
+ pr_err("can't request gpio %d\n", GPIO_EEPROM_OE);
+ return ret;
+ }
+ spi_register_board_info(digsy_mtc_eeprom_info,
+ ARRAY_SIZE(digsy_mtc_eeprom_info));
+ return platform_device_register(&digsy_mtc_eeprom);
+}
+device_initcall(digsy_mtc_eeprom_devices_init);
diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c
index 45060ddc4e5..33f8673d23a 100644
--- a/drivers/misc/eeprom/eeprom.c
+++ b/drivers/misc/eeprom/eeprom.c
@@ -3,7 +3,7 @@
* Philip Edelbrock <phil@netroedge.com>
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
* Copyright (C) 2003 IBM Corp.
- * Copyright (C) 2004 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2004 Jean Delvare <jdelvare@suse.de>
*
* 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
@@ -17,7 +17,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
@@ -229,22 +228,10 @@ static struct i2c_driver eeprom_driver = {
.address_list = normal_i2c,
};
-static int __init eeprom_init(void)
-{
- return i2c_add_driver(&eeprom_driver);
-}
-
-static void __exit eeprom_exit(void)
-{
- i2c_del_driver(&eeprom_driver);
-}
-
+module_i2c_driver(eeprom_driver);
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com> and "
"Greg Kroah-Hartman <greg@kroah.com>");
MODULE_DESCRIPTION("I2C EEPROM driver");
MODULE_LICENSE("GPL");
-
-module_init(eeprom_init);
-module_exit(eeprom_exit);
diff --git a/drivers/misc/eeprom/eeprom_93cx6.c b/drivers/misc/eeprom/eeprom_93cx6.c
index 7b33de95c4b..0ff4b02177b 100644
--- a/drivers/misc/eeprom/eeprom_93cx6.c
+++ b/drivers/misc/eeprom/eeprom_93cx6.c
@@ -63,6 +63,7 @@ static void eeprom_93cx6_startup(struct eeprom_93cx6 *eeprom)
eeprom->reg_data_out = 0;
eeprom->reg_data_clock = 0;
eeprom->reg_chip_select = 1;
+ eeprom->drive_data = 1;
eeprom->register_write(eeprom);
/*
@@ -101,6 +102,7 @@ static void eeprom_93cx6_write_bits(struct eeprom_93cx6 *eeprom,
*/
eeprom->reg_data_in = 0;
eeprom->reg_data_out = 0;
+ eeprom->drive_data = 1;
/*
* Start writing all bits.
@@ -140,6 +142,7 @@ static void eeprom_93cx6_read_bits(struct eeprom_93cx6 *eeprom,
*/
eeprom->reg_data_in = 0;
eeprom->reg_data_out = 0;
+ eeprom->drive_data = 0;
/*
* Start reading all bits.
@@ -231,3 +234,88 @@ void eeprom_93cx6_multiread(struct eeprom_93cx6 *eeprom, const u8 word,
}
EXPORT_SYMBOL_GPL(eeprom_93cx6_multiread);
+/**
+ * eeprom_93cx6_wren - set the write enable state
+ * @eeprom: Pointer to eeprom structure
+ * @enable: true to enable writes, otherwise disable writes
+ *
+ * Set the EEPROM write enable state to either allow or deny
+ * writes depending on the @enable value.
+ */
+void eeprom_93cx6_wren(struct eeprom_93cx6 *eeprom, bool enable)
+{
+ u16 command;
+
+ /* start the command */
+ eeprom_93cx6_startup(eeprom);
+
+ /* create command to enable/disable */
+
+ command = enable ? PCI_EEPROM_EWEN_OPCODE : PCI_EEPROM_EWDS_OPCODE;
+ command <<= (eeprom->width - 2);
+
+ eeprom_93cx6_write_bits(eeprom, command,
+ PCI_EEPROM_WIDTH_OPCODE + eeprom->width);
+
+ eeprom_93cx6_cleanup(eeprom);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_wren);
+
+/**
+ * eeprom_93cx6_write - write data to the EEPROM
+ * @eeprom: Pointer to eeprom structure
+ * @addr: Address to write data to.
+ * @data: The data to write to address @addr.
+ *
+ * Write the @data to the specified @addr in the EEPROM and
+ * waiting for the device to finish writing.
+ *
+ * Note, since we do not expect large number of write operations
+ * we delay in between parts of the operation to avoid using excessive
+ * amounts of CPU time busy waiting.
+ */
+void eeprom_93cx6_write(struct eeprom_93cx6 *eeprom, u8 addr, u16 data)
+{
+ int timeout = 100;
+ u16 command;
+
+ /* start the command */
+ eeprom_93cx6_startup(eeprom);
+
+ command = PCI_EEPROM_WRITE_OPCODE << eeprom->width;
+ command |= addr;
+
+ /* send write command */
+ eeprom_93cx6_write_bits(eeprom, command,
+ PCI_EEPROM_WIDTH_OPCODE + eeprom->width);
+
+ /* send data */
+ eeprom_93cx6_write_bits(eeprom, data, 16);
+
+ /* get ready to check for busy */
+ eeprom->drive_data = 0;
+ eeprom->reg_chip_select = 1;
+ eeprom->register_write(eeprom);
+
+ /* wait at-least 250ns to get DO to be the busy signal */
+ usleep_range(1000, 2000);
+
+ /* wait for DO to go high to signify finish */
+
+ while (true) {
+ eeprom->register_read(eeprom);
+
+ if (eeprom->reg_data_out)
+ break;
+
+ usleep_range(1000, 2000);
+
+ if (--timeout <= 0) {
+ printk(KERN_ERR "%s: timeout\n", __func__);
+ break;
+ }
+ }
+
+ eeprom_93cx6_cleanup(eeprom);
+}
+EXPORT_SYMBOL_GPL(eeprom_93cx6_write);
diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c
new file mode 100644
index 00000000000..9ebeacdb8ec
--- /dev/null
+++ b/drivers/misc/eeprom/eeprom_93xx46.c
@@ -0,0 +1,398 @@
+/*
+ * Driver for 93xx46 EEPROMs
+ *
+ * (C) 2011 DENX Software Engineering, Anatolij Gustschin <agust@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/eeprom_93xx46.h>
+
+#define OP_START 0x4
+#define OP_WRITE (OP_START | 0x1)
+#define OP_READ (OP_START | 0x2)
+#define ADDR_EWDS 0x00
+#define ADDR_ERAL 0x20
+#define ADDR_EWEN 0x30
+
+struct eeprom_93xx46_dev {
+ struct spi_device *spi;
+ struct eeprom_93xx46_platform_data *pdata;
+ struct bin_attribute bin;
+ struct mutex lock;
+ int addrlen;
+};
+
+static ssize_t
+eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct eeprom_93xx46_dev *edev;
+ struct device *dev;
+ struct spi_message m;
+ struct spi_transfer t[2];
+ int bits, ret;
+ u16 cmd_addr;
+
+ dev = container_of(kobj, struct device, kobj);
+ edev = dev_get_drvdata(dev);
+
+ if (unlikely(off >= edev->bin.size))
+ return 0;
+ if ((off + count) > edev->bin.size)
+ count = edev->bin.size - off;
+ if (unlikely(!count))
+ return count;
+
+ cmd_addr = OP_READ << edev->addrlen;
+
+ if (edev->addrlen == 7) {
+ cmd_addr |= off & 0x7f;
+ bits = 10;
+ } else {
+ cmd_addr |= off & 0x3f;
+ bits = 9;
+ }
+
+ dev_dbg(&edev->spi->dev, "read cmd 0x%x, %d Hz\n",
+ cmd_addr, edev->spi->max_speed_hz);
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = (char *)&cmd_addr;
+ t[0].len = 2;
+ t[0].bits_per_word = bits;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].rx_buf = buf;
+ t[1].len = count;
+ t[1].bits_per_word = 8;
+ spi_message_add_tail(&t[1], &m);
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ ret = spi_sync(edev->spi, &m);
+ /* have to wait at least Tcsl ns */
+ ndelay(250);
+ if (ret) {
+ dev_err(&edev->spi->dev, "read %zu bytes at %d: err. %d\n",
+ count, (int)off, ret);
+ }
+
+ if (edev->pdata->finish)
+ edev->pdata->finish(edev);
+
+ mutex_unlock(&edev->lock);
+ return ret ? : count;
+}
+
+static int eeprom_93xx46_ew(struct eeprom_93xx46_dev *edev, int is_on)
+{
+ struct spi_message m;
+ struct spi_transfer t;
+ int bits, ret;
+ u16 cmd_addr;
+
+ cmd_addr = OP_START << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS) << 1;
+ bits = 10;
+ } else {
+ cmd_addr |= (is_on ? ADDR_EWEN : ADDR_EWDS);
+ bits = 9;
+ }
+
+ dev_dbg(&edev->spi->dev, "ew cmd 0x%04x\n", cmd_addr);
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = &cmd_addr;
+ t.len = 2;
+ t.bits_per_word = bits;
+ spi_message_add_tail(&t, &m);
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ ret = spi_sync(edev->spi, &m);
+ /* have to wait at least Tcsl ns */
+ ndelay(250);
+ if (ret)
+ dev_err(&edev->spi->dev, "erase/write %sable error %d\n",
+ is_on ? "en" : "dis", ret);
+
+ if (edev->pdata->finish)
+ edev->pdata->finish(edev);
+
+ mutex_unlock(&edev->lock);
+ return ret;
+}
+
+static ssize_t
+eeprom_93xx46_write_word(struct eeprom_93xx46_dev *edev,
+ const char *buf, unsigned off)
+{
+ struct spi_message m;
+ struct spi_transfer t[2];
+ int bits, data_len, ret;
+ u16 cmd_addr;
+
+ cmd_addr = OP_WRITE << edev->addrlen;
+
+ if (edev->addrlen == 7) {
+ cmd_addr |= off & 0x7f;
+ bits = 10;
+ data_len = 1;
+ } else {
+ cmd_addr |= off & 0x3f;
+ bits = 9;
+ data_len = 2;
+ }
+
+ dev_dbg(&edev->spi->dev, "write cmd 0x%x\n", cmd_addr);
+
+ spi_message_init(&m);
+ memset(t, 0, sizeof(t));
+
+ t[0].tx_buf = (char *)&cmd_addr;
+ t[0].len = 2;
+ t[0].bits_per_word = bits;
+ spi_message_add_tail(&t[0], &m);
+
+ t[1].tx_buf = buf;
+ t[1].len = data_len;
+ t[1].bits_per_word = 8;
+ spi_message_add_tail(&t[1], &m);
+
+ ret = spi_sync(edev->spi, &m);
+ /* have to wait program cycle time Twc ms */
+ mdelay(6);
+ return ret;
+}
+
+static ssize_t
+eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct eeprom_93xx46_dev *edev;
+ struct device *dev;
+ int i, ret, step = 1;
+
+ dev = container_of(kobj, struct device, kobj);
+ edev = dev_get_drvdata(dev);
+
+ if (unlikely(off >= edev->bin.size))
+ return -EFBIG;
+ if ((off + count) > edev->bin.size)
+ count = edev->bin.size - off;
+ if (unlikely(!count))
+ return count;
+
+ /* only write even number of bytes on 16-bit devices */
+ if (edev->addrlen == 6) {
+ step = 2;
+ count &= ~1;
+ }
+
+ /* erase/write enable */
+ ret = eeprom_93xx46_ew(edev, 1);
+ if (ret)
+ return ret;
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ for (i = 0; i < count; i += step) {
+ ret = eeprom_93xx46_write_word(edev, &buf[i], off + i);
+ if (ret) {
+ dev_err(&edev->spi->dev, "write failed at %d: %d\n",
+ (int)off + i, ret);
+ break;
+ }
+ }
+
+ if (edev->pdata->finish)
+ edev->pdata->finish(edev);
+
+ mutex_unlock(&edev->lock);
+
+ /* erase/write disable */
+ eeprom_93xx46_ew(edev, 0);
+ return ret ? : count;
+}
+
+static int eeprom_93xx46_eral(struct eeprom_93xx46_dev *edev)
+{
+ struct eeprom_93xx46_platform_data *pd = edev->pdata;
+ struct spi_message m;
+ struct spi_transfer t;
+ int bits, ret;
+ u16 cmd_addr;
+
+ cmd_addr = OP_START << edev->addrlen;
+ if (edev->addrlen == 7) {
+ cmd_addr |= ADDR_ERAL << 1;
+ bits = 10;
+ } else {
+ cmd_addr |= ADDR_ERAL;
+ bits = 9;
+ }
+
+ spi_message_init(&m);
+ memset(&t, 0, sizeof(t));
+
+ t.tx_buf = &cmd_addr;
+ t.len = 2;
+ t.bits_per_word = bits;
+ spi_message_add_tail(&t, &m);
+
+ mutex_lock(&edev->lock);
+
+ if (edev->pdata->prepare)
+ edev->pdata->prepare(edev);
+
+ ret = spi_sync(edev->spi, &m);
+ if (ret)
+ dev_err(&edev->spi->dev, "erase error %d\n", ret);
+ /* have to wait erase cycle time Tec ms */
+ mdelay(6);
+
+ if (pd->finish)
+ pd->finish(edev);
+
+ mutex_unlock(&edev->lock);
+ return ret;
+}
+
+static ssize_t eeprom_93xx46_store_erase(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct eeprom_93xx46_dev *edev = dev_get_drvdata(dev);
+ int erase = 0, ret;
+
+ sscanf(buf, "%d", &erase);
+ if (erase) {
+ ret = eeprom_93xx46_ew(edev, 1);
+ if (ret)
+ return ret;
+ ret = eeprom_93xx46_eral(edev);
+ if (ret)
+ return ret;
+ ret = eeprom_93xx46_ew(edev, 0);
+ if (ret)
+ return ret;
+ }
+ return count;
+}
+static DEVICE_ATTR(erase, S_IWUSR, NULL, eeprom_93xx46_store_erase);
+
+static int eeprom_93xx46_probe(struct spi_device *spi)
+{
+ struct eeprom_93xx46_platform_data *pd;
+ struct eeprom_93xx46_dev *edev;
+ int err;
+
+ pd = spi->dev.platform_data;
+ if (!pd) {
+ dev_err(&spi->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ edev = kzalloc(sizeof(*edev), GFP_KERNEL);
+ if (!edev)
+ return -ENOMEM;
+
+ if (pd->flags & EE_ADDR8)
+ edev->addrlen = 7;
+ else if (pd->flags & EE_ADDR16)
+ edev->addrlen = 6;
+ else {
+ dev_err(&spi->dev, "unspecified address type\n");
+ err = -EINVAL;
+ goto fail;
+ }
+
+ mutex_init(&edev->lock);
+
+ edev->spi = spi_dev_get(spi);
+ edev->pdata = pd;
+
+ sysfs_bin_attr_init(&edev->bin);
+ edev->bin.attr.name = "eeprom";
+ edev->bin.attr.mode = S_IRUSR;
+ edev->bin.read = eeprom_93xx46_bin_read;
+ edev->bin.size = 128;
+ if (!(pd->flags & EE_READONLY)) {
+ edev->bin.write = eeprom_93xx46_bin_write;
+ edev->bin.attr.mode |= S_IWUSR;
+ }
+
+ err = sysfs_create_bin_file(&spi->dev.kobj, &edev->bin);
+ if (err)
+ goto fail;
+
+ dev_info(&spi->dev, "%d-bit eeprom %s\n",
+ (pd->flags & EE_ADDR8) ? 8 : 16,
+ (pd->flags & EE_READONLY) ? "(readonly)" : "");
+
+ if (!(pd->flags & EE_READONLY)) {
+ if (device_create_file(&spi->dev, &dev_attr_erase))
+ dev_err(&spi->dev, "can't create erase interface\n");
+ }
+
+ spi_set_drvdata(spi, edev);
+ return 0;
+fail:
+ kfree(edev);
+ return err;
+}
+
+static int eeprom_93xx46_remove(struct spi_device *spi)
+{
+ struct eeprom_93xx46_dev *edev = spi_get_drvdata(spi);
+
+ if (!(edev->pdata->flags & EE_READONLY))
+ device_remove_file(&spi->dev, &dev_attr_erase);
+
+ sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
+ kfree(edev);
+ return 0;
+}
+
+static struct spi_driver eeprom_93xx46_driver = {
+ .driver = {
+ .name = "93xx46",
+ .owner = THIS_MODULE,
+ },
+ .probe = eeprom_93xx46_probe,
+ .remove = eeprom_93xx46_remove,
+};
+
+module_spi_driver(eeprom_93xx46_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Driver for 93xx46 EEPROMs");
+MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>");
+MODULE_ALIAS("spi:93xx46");
diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c
index 5653a3ce051..580ff9df552 100644
--- a/drivers/misc/eeprom/max6875.c
+++ b/drivers/misc/eeprom/max6875.c
@@ -27,7 +27,6 @@
*/
#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
@@ -208,20 +207,8 @@ static struct i2c_driver max6875_driver = {
.id_table = max6875_id,
};
-static int __init max6875_init(void)
-{
- return i2c_add_driver(&max6875_driver);
-}
-
-static void __exit max6875_exit(void)
-{
- i2c_del_driver(&max6875_driver);
-}
-
+module_i2c_driver(max6875_driver);
MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_DESCRIPTION("MAX6875 driver");
MODULE_LICENSE("GPL");
-
-module_init(max6875_init);
-module_exit(max6875_exit);
diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c
new file mode 100644
index 00000000000..3f2b625b203
--- /dev/null
+++ b/drivers/misc/eeprom/sunxi_sid.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl>
+ * http://www.linux-sunxi.org
+ *
+ * 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This driver exposes the Allwinner security ID, efuses exported in byte-
+ * sized chunks.
+ */
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+
+#define DRV_NAME "sunxi-sid"
+
+struct sunxi_sid_data {
+ void __iomem *reg_base;
+ unsigned int keysize;
+};
+
+/* We read the entire key, due to a 32 bit read alignment requirement. Since we
+ * want to return the requested byte, this results in somewhat slower code and
+ * uses 4 times more reads as needed but keeps code simpler. Since the SID is
+ * only very rarely probed, this is not really an issue.
+ */
+static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data,
+ const unsigned int offset)
+{
+ u32 sid_key;
+
+ if (offset >= sid_data->keysize)
+ return 0;
+
+ sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4));
+ sid_key >>= (offset % 4) * 8;
+
+ return sid_key; /* Only return the last byte */
+}
+
+static ssize_t sid_read(struct file *fd, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev;
+ struct sunxi_sid_data *sid_data;
+ int i;
+
+ pdev = to_platform_device(kobj_to_dev(kobj));
+ sid_data = platform_get_drvdata(pdev);
+
+ if (pos < 0 || pos >= sid_data->keysize)
+ return 0;
+ if (size > sid_data->keysize - pos)
+ size = sid_data->keysize - pos;
+
+ for (i = 0; i < size; i++)
+ buf[i] = sunxi_sid_read_byte(sid_data, pos + i);
+
+ return i;
+}
+
+static struct bin_attribute sid_bin_attr = {
+ .attr = { .name = "eeprom", .mode = S_IRUGO, },
+ .read = sid_read,
+};
+
+static int sunxi_sid_remove(struct platform_device *pdev)
+{
+ device_remove_bin_file(&pdev->dev, &sid_bin_attr);
+ dev_dbg(&pdev->dev, "driver unloaded\n");
+
+ return 0;
+}
+
+static const struct of_device_id sunxi_sid_of_match[] = {
+ { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16},
+ { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512},
+ {/* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, sunxi_sid_of_match);
+
+static int sunxi_sid_probe(struct platform_device *pdev)
+{
+ struct sunxi_sid_data *sid_data;
+ struct resource *res;
+ const struct of_device_id *of_dev_id;
+ u8 *entropy;
+ unsigned int i;
+
+ sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data),
+ GFP_KERNEL);
+ if (!sid_data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(sid_data->reg_base))
+ return PTR_ERR(sid_data->reg_base);
+
+ of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev);
+ if (!of_dev_id)
+ return -ENODEV;
+ sid_data->keysize = (int)of_dev_id->data;
+
+ platform_set_drvdata(pdev, sid_data);
+
+ sid_bin_attr.size = sid_data->keysize;
+ if (device_create_bin_file(&pdev->dev, &sid_bin_attr))
+ return -ENODEV;
+
+ entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL);
+ for (i = 0; i < sid_data->keysize; i++)
+ entropy[i] = sunxi_sid_read_byte(sid_data, i);
+ add_device_randomness(entropy, sid_data->keysize);
+ kfree(entropy);
+
+ dev_dbg(&pdev->dev, "loaded\n");
+
+ return 0;
+}
+
+static struct platform_driver sunxi_sid_driver = {
+ .probe = sunxi_sid_probe,
+ .remove = sunxi_sid_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = sunxi_sid_of_match,
+ },
+};
+module_platform_driver(sunxi_sid_driver);
+
+MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>");
+MODULE_DESCRIPTION("Allwinner sunxi security id driver");
+MODULE_LICENSE("GPL");