parent
7801e8d2bc
commit
083320dbe2
@ -0,0 +1,134 @@ |
||||
Subject: [PATCH 2/3] I2C: at24: add kernel interface for reading/writing EEPROM
|
||||
Date: Monday 25 August 2008
|
||||
From: Kevin Hilman <khilman@deeprootsystems.com>
|
||||
To: davinci-linux-open-source@linux.davincidsp.com
|
||||
|
||||
This patch adds an interface by which other kernel code can read/write
|
||||
detected EEPROM.
|
||||
|
||||
The platform code registers a 'setup' callback with the
|
||||
at24_platform_data. When the at24 driver detects an EEPROM, it fills
|
||||
out the read and write functions of at24_iface and calls the setup
|
||||
callback. The platform code can then use the read/write functions in
|
||||
the at24_iface struct for reading and writing the EEPROM.
|
||||
|
||||
Original idea, review and updates by David Brownell <david-b@pacbell.net>
|
||||
|
||||
Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
|
||||
---
|
||||
drivers/i2c/chips/at24.c | 42 +++++++++++++++++++++++++++++++++++-------
|
||||
include/linux/i2c/at24.h | 10 ++++++++++
|
||||
2 files changed, 45 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/i2c/chips/at24.c
|
||||
+++ b/drivers/i2c/chips/at24.c
|
||||
@@ -53,6 +53,7 @@
|
||||
|
||||
struct at24_data {
|
||||
struct at24_platform_data chip;
|
||||
+ struct at24_iface iface;
|
||||
bool use_smbus;
|
||||
|
||||
/*
|
||||
@@ -264,13 +265,6 @@ static ssize_t at24_bin_read(struct kobj
|
||||
|
||||
|
||||
/*
|
||||
- * REVISIT: export at24_bin{read,write}() to let other kernel code use
|
||||
- * eeprom data. For example, it might hold a board's Ethernet address, or
|
||||
- * board-specific calibration data generated on the manufacturing floor.
|
||||
- */
|
||||
-
|
||||
-
|
||||
-/*
|
||||
* 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.
|
||||
@@ -386,6 +380,30 @@ static ssize_t at24_bin_write(struct kob
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
+/*
|
||||
+ * 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.
|
||||
+ */
|
||||
+
|
||||
+static ssize_t at24_iface_read(struct at24_iface *iface, char *buf,
|
||||
+ off_t offset, size_t count)
|
||||
+{
|
||||
+ struct at24_data *at24 = container_of(iface, struct at24_data, iface);
|
||||
+
|
||||
+ return at24_eeprom_read(at24, buf, offset, count);
|
||||
+}
|
||||
+
|
||||
+static ssize_t at24_iface_write(struct at24_iface *iface, char *buf,
|
||||
+ off_t offset, size_t count)
|
||||
+{
|
||||
+ struct at24_data *at24 = container_of(iface, struct at24_data, iface);
|
||||
+
|
||||
+ return at24_eeprom_write(at24, buf, offset, count);
|
||||
+}
|
||||
+
|
||||
+/*-------------------------------------------------------------------------*/
|
||||
+
|
||||
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct at24_platform_data chip;
|
||||
@@ -413,6 +431,9 @@ static int at24_probe(struct i2c_client
|
||||
* is recommended anyhow.
|
||||
*/
|
||||
chip.page_size = 1;
|
||||
+
|
||||
+ chip.setup = NULL;
|
||||
+ chip.context = NULL;
|
||||
}
|
||||
|
||||
if (!is_power_of_2(chip.byte_len))
|
||||
@@ -449,6 +470,9 @@ static int at24_probe(struct i2c_client
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
+ at24->iface.read = at24_iface_read;
|
||||
+ at24->iface.write = at24_iface_write;
|
||||
+
|
||||
mutex_init(&at24->lock);
|
||||
at24->use_smbus = use_smbus;
|
||||
at24->chip = chip;
|
||||
@@ -521,6 +545,10 @@ static int at24_probe(struct i2c_client
|
||||
at24->write_max,
|
||||
use_smbus ? ", use_smbus" : "");
|
||||
|
||||
+ /* export data to kernel code */
|
||||
+ if (chip.setup)
|
||||
+ chip.setup(&at24->iface, chip.context);
|
||||
+
|
||||
return 0;
|
||||
|
||||
err_clients:
|
||||
--- a/include/linux/i2c/at24.h
|
||||
+++ b/include/linux/i2c/at24.h
|
||||
@@ -15,6 +15,13 @@
|
||||
* is bigger than what the chip actually supports!
|
||||
*/
|
||||
|
||||
+struct at24_iface {
|
||||
+ ssize_t (*read)(struct at24_iface *, char *buf, off_t offset,
|
||||
+ size_t count);
|
||||
+ ssize_t (*write)(struct at24_iface *, char *buf, off_t offset,
|
||||
+ size_t count);
|
||||
+};
|
||||
+
|
||||
struct at24_platform_data {
|
||||
u32 byte_len; /* size (sum of all addr) */
|
||||
u16 page_size; /* for writes */
|
||||
@@ -23,6 +30,9 @@ struct at24_platform_data {
|
||||
#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) */
|
||||
+
|
||||
+ int (*setup)(struct at24_iface *, void *context);
|
||||
+ void *context;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_AT24_H */
|
@ -0,0 +1,11 @@ |
||||
--- a/arch/arm/mach-ixp4xx/include/mach/hardware.h
|
||||
+++ b/arch/arm/mach-ixp4xx/include/mach/hardware.h
|
||||
@@ -18,7 +18,7 @@
|
||||
#define __ASM_ARCH_HARDWARE_H__
|
||||
|
||||
#define PCIBIOS_MIN_IO 0x00001000
|
||||
-#define PCIBIOS_MIN_MEM (cpu_is_ixp43x() ? 0x40000000 : 0x48000000)
|
||||
+#define PCIBIOS_MIN_MEM 0x48000000
|
||||
|
||||
/*
|
||||
* We override the standard dma-mask routines for bouncing.
|
@ -1,183 +0,0 @@ |
||||
--- a/drivers/i2c/chips/eeprom.c
|
||||
+++ b/drivers/i2c/chips/eeprom.c
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mutex.h>
|
||||
+#include <linux/notifier.h>
|
||||
+#include <linux/eeprom.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x50, 0x51, 0x52, 0x53, 0x54,
|
||||
@@ -35,25 +37,7 @@ static const unsigned short normal_i2c[]
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD_1(eeprom);
|
||||
|
||||
-
|
||||
-/* Size of EEPROM in bytes */
|
||||
-#define EEPROM_SIZE 256
|
||||
-
|
||||
-/* possible types of eeprom devices */
|
||||
-enum eeprom_nature {
|
||||
- UNKNOWN,
|
||||
- VAIO,
|
||||
-};
|
||||
-
|
||||
-/* Each client has this additional data */
|
||||
-struct eeprom_data {
|
||||
- struct mutex update_lock;
|
||||
- u8 valid; /* bitfield, bit!=0 if slice is valid */
|
||||
- unsigned long last_updated[8]; /* In jiffies, 8 slices */
|
||||
- u8 data[EEPROM_SIZE]; /* Register values */
|
||||
- enum eeprom_nature nature;
|
||||
-};
|
||||
-
|
||||
+ATOMIC_NOTIFIER_HEAD(eeprom_chain);
|
||||
|
||||
static void eeprom_update_client(struct i2c_client *client, u8 slice)
|
||||
{
|
||||
@@ -178,6 +162,7 @@ static int eeprom_probe(struct i2c_clien
|
||||
i2c_set_clientdata(client, data);
|
||||
mutex_init(&data->update_lock);
|
||||
data->nature = UNKNOWN;
|
||||
+ data->attr = &eeprom_attr;
|
||||
|
||||
/* Detect the Vaio nature of EEPROMs.
|
||||
We use the "PCG-" or "VGN-" prefix as the signature. */
|
||||
@@ -202,6 +187,9 @@ static int eeprom_probe(struct i2c_clien
|
||||
if (err)
|
||||
goto exit_kfree;
|
||||
|
||||
+ /* call the notifier chain */
|
||||
+ atomic_notifier_call_chain(&eeprom_chain, EEPROM_REGISTER, data);
|
||||
+
|
||||
return 0;
|
||||
|
||||
exit_kfree:
|
||||
@@ -236,6 +224,41 @@ static struct i2c_driver eeprom_driver =
|
||||
.address_data = &addr_data,
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * register_eeprom_notifier - register a 'user' of EEPROM devices.
|
||||
+ * @nb: pointer to notifier info structure
|
||||
+ *
|
||||
+ * Registers a callback function to be called upon detection
|
||||
+ * of an EEPROM device. Detection invokes the 'add' callback
|
||||
+ * with the kobj of the mutex and a bin_attribute which allows
|
||||
+ * read from the EEPROM. The intention is that the notifier
|
||||
+ * will be able to read system configuration from the notifier.
|
||||
+ *
|
||||
+ * Only EEPROMs detected *after* the addition of the notifier
|
||||
+ * are notified. I.e. EEPROMs already known to the system
|
||||
+ * will not be notified - add the notifier from board level
|
||||
+ * code!
|
||||
+ */
|
||||
+int register_eeprom_notifier(struct notifier_block *nb)
|
||||
+{
|
||||
+ return atomic_notifier_chain_register(&eeprom_chain, nb);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * unregister_eeprom_notifier - unregister a 'user' of EEPROM devices.
|
||||
+ * @old: pointer to notifier info structure
|
||||
+ *
|
||||
+ * Removes a callback function from the list of 'users' to be
|
||||
+ * notified upon detection of EEPROM devices.
|
||||
+ */
|
||||
+int unregister_eeprom_notifier(struct notifier_block *nb)
|
||||
+{
|
||||
+ return atomic_notifier_chain_unregister(&eeprom_chain, nb);
|
||||
+}
|
||||
+
|
||||
+EXPORT_SYMBOL_GPL(register_eeprom_notifier);
|
||||
+EXPORT_SYMBOL_GPL(unregister_eeprom_notifier);
|
||||
+
|
||||
static int __init eeprom_init(void)
|
||||
{
|
||||
return i2c_add_driver(&eeprom_driver);
|
||||
--- /dev/null
|
||||
+++ b/include/linux/eeprom.h
|
||||
@@ -0,0 +1,71 @@
|
||||
+#ifndef _LINUX_EEPROM_H
|
||||
+#define _LINUX_EEPROM_H
|
||||
+/*
|
||||
+ * EEPROM notifier header
|
||||
+ *
|
||||
+ * Copyright (C) 2006 John Bowler
|
||||
+ */
|
||||
+
|
||||
+/*
|
||||
+ * 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.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU General Public License
|
||||
+ * along with this program; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
+ */
|
||||
+
|
||||
+#ifndef __KERNEL__
|
||||
+#error This is a kernel header
|
||||
+#endif
|
||||
+
|
||||
+#include <linux/list.h>
|
||||
+#include <linux/kobject.h>
|
||||
+#include <linux/sysfs.h>
|
||||
+
|
||||
+/* Size of EEPROM in bytes */
|
||||
+#define EEPROM_SIZE 256
|
||||
+
|
||||
+/* possible types of eeprom devices */
|
||||
+enum eeprom_nature {
|
||||
+ UNKNOWN,
|
||||
+ VAIO,
|
||||
+};
|
||||
+
|
||||
+/* Each client has this additional data */
|
||||
+struct eeprom_data {
|
||||
+ struct i2c_client client;
|
||||
+ struct mutex update_lock;
|
||||
+ u8 valid; /* bitfield, bit!=0 if slice is valid */
|
||||
+ unsigned long last_updated[8]; /* In jiffies, 8 slices */
|
||||
+ u8 data[EEPROM_SIZE]; /* Register values */
|
||||
+ enum eeprom_nature nature;
|
||||
+ struct bin_attribute *attr;
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * This is very basic.
|
||||
+ *
|
||||
+ * If an EEPROM is detected on the I2C bus (this only works for
|
||||
+ * I2C EEPROMs) the notifier chain is called with
|
||||
+ * both the I2C information and the kobject for the sysfs
|
||||
+ * device which has been registers. It is then possible to
|
||||
+ * read from the device via the bin_attribute::read method
|
||||
+ * to extract configuration information.
|
||||
+ *
|
||||
+ * Register the notifier in the board level code, there is no
|
||||
+ * need to unregister it but you can if you want (it will save
|
||||
+ * a little bit or kernel memory to do so).
|
||||
+ */
|
||||
+
|
||||
+extern int register_eeprom_notifier(struct notifier_block *nb);
|
||||
+extern int unregister_eeprom_notifier(struct notifier_block *nb);
|
||||
+
|
||||
+#endif /* _LINUX_EEPROM_H */
|
||||
--- a/include/linux/notifier.h
|
||||
+++ b/include/linux/notifier.h
|
||||
@@ -256,5 +256,8 @@ extern struct blocking_notifier_head reb
|
||||
#define VT_UPDATE 0x0004 /* A bigger update occurred */
|
||||
#define VT_PREWRITE 0x0005 /* A char is about to be written to the console */
|
||||
|
||||
+/* eeprom notifier chain */
|
||||
+#define EEPROM_REGISTER 0x0001
|
||||
+
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_NOTIFIER_H */
|
@ -1,43 +0,0 @@ |
||||
--- a/arch/arm/mach-ixp4xx/avila-setup.c
|
||||
+++ b/arch/arm/mach-ixp4xx/avila-setup.c
|
||||
@@ -132,6 +132,31 @@ static struct platform_device avila_pata
|
||||
.resource = avila_pata_resources,
|
||||
};
|
||||
|
||||
+/* Built-in 10/100 Ethernet MAC interfaces */
|
||||
+static struct eth_plat_info avila_npeb_data = {
|
||||
+ .phy = 0,
|
||||
+ .rxq = 3,
|
||||
+ .txreadyq = 20,
|
||||
+};
|
||||
+
|
||||
+static struct eth_plat_info avila_npec_data = {
|
||||
+ .phy = 1,
|
||||
+ .rxq = 4,
|
||||
+ .txreadyq = 21,
|
||||
+};
|
||||
+
|
||||
+static struct platform_device avila_npeb_device = {
|
||||
+ .name = "ixp4xx_eth",
|
||||
+ .id = IXP4XX_ETH_NPEB,
|
||||
+ .dev.platform_data = &avila_npeb_data,
|
||||
+};
|
||||
+
|
||||
+static struct platform_device avila_npec_device = {
|
||||
+ .name = "ixp4xx_eth",
|
||||
+ .id = IXP4XX_ETH_NPEC,
|
||||
+ .dev.platform_data = &avila_npec_data,
|
||||
+};
|
||||
+
|
||||
static struct platform_device *avila_devices[] __initdata = {
|
||||
&avila_i2c_gpio,
|
||||
&avila_flash,
|
||||
@@ -159,6 +184,8 @@ static void __init avila_init(void)
|
||||
|
||||
platform_device_register(&avila_pata);
|
||||
|
||||
+ platform_device_register(avila_npeb_device);
|
||||
+ platform_device_register(avila_npec_device);
|
||||
}
|
||||
|
||||
MACHINE_START(AVILA, "Gateworks Avila Network Platform")
|
Loading…
Reference in new issue