[U-Boot-Users] [Patch 4/9 Try 3]U-boot-V2:I2C: Introduce i2c-dev

Menon, Nishanth x0nishan at ti.com
Wed Jun 25 03:48:14 CEST 2008


I2C by default does not expose a device interface
for the adapters registering in the system. This
is usually not needed unless we need to debug
something. in which case, this is a handy little
layer when used in conjunction with the i2c
commands. This layer has changed

New fixes include not using map_base etc and
similar review comments from Sascha.

Signed-off-by: Nishanth Menon <x0nishan at ti.com>

---
 drivers/i2c/Kconfig     |   18 +
 drivers/i2c/Makefile    |    1
 drivers/i2c/i2c-core.c  |   12
 drivers/i2c/i2c-core.h  |    4
 drivers/i2c/i2c-dev.c   |  828 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c-dev.h |   43 ++
 6 files changed, 906 insertions(+)

Index: u-boot-v2.git/include/linux/i2c-dev.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ u-boot-v2.git/include/linux/i2c-dev.h       2008-06-24 19:38:02.000000000 -0500
@@ -0,0 +1,43 @@
+/**
+ * @file
+ * @brief i2c-bus driver, "char device" interface
+ *
+ * FileName: include/linux/i2c-dev.h
+ *
+ */
+/* Originally from Linux kernel. Ported to U-boot-v2.
+ * (C) Copyright 2008
+ * Texas Instruments, <www.ti.com>
+ * Nishanth Menon <x0nishan at ti.com>
+ */
+/*
+    Copyright (C) 1995-97 Simon G. Vogl
+    Copyright (C) 1998-99 Frodo Looijaard <frodol at dds.nl>
+
+    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef _LINUX_I2C_DEV_H
+#define _LINUX_I2C_DEV_H
+
+/* For command access */
+#define I2C_SLAVE_ADD  0x0703  /* Create a new slave device */
+#define I2C_SLAVE_REM  0x0706  /* Remove a new slave device */
+
+/******* For the platforms to use *********/
+int i2cdev_new(unsigned int, unsigned int);
+int i2cdev_remove(unsigned int, unsigned int);
+
+#endif /* _LINUX_I2C_DEV_H */
Index: u-boot-v2.git/drivers/i2c/i2c-dev.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ u-boot-v2.git/drivers/i2c/i2c-dev.c 2008-06-24 19:42:35.000000000 -0500
@@ -0,0 +1,828 @@
+/**
+ * @file
+ * @brief i2c-bus driver, "char device" interface
+ *
+ * FileName: drivers/i2c/i2c-dev.c
+ *
+ * It is very important to note the following:
+ * read to an i2cbus device is a 0 byte read operation. writes
+ * are not supported to i2cbus device.
+ *
+ * i2cdev devices on the other hand translate all read/write to
+ * 0 byte read/write operations as per i2c spec.
+ *
+ * Note: both of these can confuse certain devices on I2C bus.
+ * USE WITH CAUTION.
+ *
+ * NOTE: We dont support 10 bit addressing
+ *
+ */
+/* Originally from Linux kernel. Ported to U-boot-v2.
+ *
+ * Note: even though we borrow i2c-dev concept from linux, there are
+ * major differences in operations.
+ * I have retained original copyrights for the heavy influence it
+ * has on the current code.
+ *
+ * This implementation is heavily influenced by the valuable inputs from
+ * Sascha Hauer <s.hauer at pengutronix.de>
+ *
+ * (C) Copyright 2008
+ * Texas Instruments, <www.ti.com>
+ * Nishanth Menon <x0nishan at ti.com>
+ */
+/*
+ * Copyright (C) 1995-97 Simon G. Vogl
+ * Copyright (C) 1998-99 Frodo Looijaard <frodol at dds.nl>
+ * Copyright (C) 2003 Greg Kroah-Hartman <greg at kroah.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
+ * But I have used so much of his original code and ideas that it seems
+ * only fair to recognize him as co-author -- Frodo
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <list.h>
+#include <init.h>
+#include <driver.h>
+#include <malloc.h>
+#include <string.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+/* Get some compatibility macros */
+#include <linux/mtd/compat.h>
+
+#include "i2c-core.h"
+
+#ifdef CONFIG_I2C_DEBUG_DEV
+#define dbg_entry(FORMAT, ARGS...) fprintf(stdout,\
+               "i2cdev: %s:%d:Entry:"FORMAT"\n",\
+               __func__, __LINE__, ARGS)
+#else
+#define dbg_entry(FORMAT, ARGS...)
+#endif
+
+#define dev_err(ARGS...) fprintf(stderr, ARGS);
+
+#define I2C_BUS_NAME "i2cbus"
+#define I2C_DEV_NAME "i2cdev"
+
+/** My dummy driver */
+static struct i2c_driver i2cdev_driver;
+
+/**
+ * An i2c_bus represents an i2c_adapter ... an I2C or SMBus master, not a
+ * slave (i2c_client) with which messages will be exchanged.  It's coupled
+ * with a character special file which is accessed by user mode drivers.
+ *
+ */
+struct i2cdev_bus {
+       struct list_head list;
+       struct i2c_adapter *adap;
+       struct device_d dev;
+};
+static LIST_HEAD(i2c_bus_list);
+
+/**
+ * contains the internal structure for a device node this is the i2c device
+ */
+struct i2cdev_dev {
+       struct list_head list;
+       unsigned short daddr;
+       struct i2cdev_bus *i2cdev_bus;
+       struct device_d dev;
+};
+static LIST_HEAD(i2c_dev_list);
+
+/** A structure to store the various functions supported by the adapter */
+struct func {
+       long value;
+       const char *name;
+};
+static const struct func all_func[] = {
+       {.value = I2C_FUNC_I2C,
+        .name = "I2C"},
+       {.value = 0,
+        .name = ""}
+};
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief search and get a bus given a bus adapter index.
+ *
+ * @param index  adapter index
+ *
+ * @return bus node if found
+ */
+static struct i2cdev_bus *i2c_bus_from_busid(unsigned index)
+{
+       struct i2cdev_bus *i2cdev_bus;
+
+       dbg_entry("index=%x", index);
+
+       list_for_each_entry(i2cdev_bus, &i2c_bus_list, list) {
+               if (i2cdev_bus->adap->nr == index)
+                       goto found;
+       }
+       i2cdev_bus = NULL;
+found:
+       return i2cdev_bus;
+}
+
+/**
+ * @brief get a free i2cbus node
+ * allocates memory and links up to bus node list.
+ *
+ * @param adap adapter to allocate a node for
+ *
+ * @return if success, return node, else NULL
+ */
+static struct i2cdev_bus *get_free_i2c_bus(struct i2c_adapter *adap)
+{
+       struct i2cdev_bus *i2cdev_bus;
+       dbg_entry("adap=%x", (u32) adap);
+
+       /* if we already have a registered bus id..
+        * need to check for ensuring uniqueness */
+       i2cdev_bus = i2c_bus_from_busid(adap->nr);
+       if (i2cdev_bus)
+               return NULL;
+
+       i2cdev_bus = calloc(1, sizeof(*i2cdev_bus));
+       if (!i2cdev_bus)
+               return NULL;
+       i2cdev_bus->adap = adap;
+
+       list_add_tail(&i2cdev_bus->list, &i2c_bus_list);
+       return i2cdev_bus;
+}
+
+/**
+ * @brief releases the i2cdev_bus list.
+ *
+ * @param i2cdev_bus item to be released
+ *
+ * @return none
+ */
+static void return_i2c_bus(struct i2cdev_bus *i2cdev_bus)
+{
+       dbg_entry("i2cdev_bus=%x", (u32) i2cdev_bus);
+       list_del(&i2cdev_bus->list);
+       free(i2cdev_bus);
+}
+
+/**
+ * @brief tiny little helper to check if address is proper.
+ *
+ * @param dev  device node
+ * @param addrp address
+ *
+ * @return 0 is no driver already linked, else -EBUSY
+ */
+static int i2cdev_check(struct device_d *dev, void *addrp)
+{
+       struct i2c_client *client = i2c_verify_client(dev);
+       dbg_entry("dev=%x addrp=%x", (u32) dev, (u32) addrp);
+
+       if (!client || client->addr != *(unsigned int *)addrp)
+               return 0;
+
+       return dev->driver ? -EBUSY : 0;
+}
+
+/**
+ * @brief This address checking function differs from the one in i2c-core
+ * in that it considers an address with a registered device, but no driver
+ * bound to it, as NOT busy.
+ *
+ * @param adapter  adapter to attach to
+ * @param addr device address
+ *
+ * @return device present or not.
+ */
+static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr)
+{
+       int ret = 0;
+       struct device_d *child;
+       dbg_entry("adapter=%x addr=%x", (u32) adapter, (u32) addr);
+       device_for_each_child((&(adapter->dev)), child) {
+               ret = i2cdev_check(child, &addr);
+               if (ret)
+                       break;
+       }
+       return ret;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief read function for a bus.
+ *
+ * This allows for a functionality like i2cdetect.
+ * Offset of each byte corresponds to the device address
+ * each offset without a device will be set with 0xFF indicating empty.
+ * (This is coz 0x0 is a valid address..)
+ * any offset with data will have that address filled with corresponding dev
+ * address
+ *
+ * @param dev bus device.
+ * @param buffer buffer to return data
+ * @param count bytes to read
+ * @param offset device address
+ * @param flags any special flags?
+ *
+ * @return reult of operation
+ */
+static ssize_t i2cbus_read(struct device_d *dev, void *buffer, size_t count,
+                          ulong offset, ulong flags)
+{
+       struct i2c_client *client = (struct i2c_client *)dev->priv;
+       unsigned char addr = 0;
+       unsigned char reg_address = 0;
+       unsigned char tmp = 0;
+       char *buf = (char *)buffer;
+       int idx = 0;
+       struct i2c_msg msgs[2] = {
+               {0, 0, 1, &addr},
+               /* Attempt a 0 byte read */
+               {0, 0 | I2C_M_RD, 0, &tmp}
+       };
+       dbg_entry("dev=%x buffer=%x count=%x offset=%x flags=%x", (u32) dev,
+                 (u32) buffer, (u32) count, (u32) offset, (u32) flags);
+
+       if (offset > dev->size) {
+               dev_err("Request=%d size=%d!\n", offset, dev->size);
+               return -EINVAL;
+       }
+       count = min(count, dev->size - offset);
+       /* Set all defaults to 0xFF */
+       memset(buf, 0xFF, count);
+       while (idx < count) {
+               reg_address = (unsigned char)offset + idx;
+               msgs[0].addr = msgs[1].addr = reg_address;
+
+               /* is this address being used? */
+               if (i2cdev_check_addr(client->adapter, reg_address))
+                       /* Client driver present.. probable that device exists!
+                        * we dare not access it though :( - dunno how it's
+                        * gonna behave
+                        */
+                       *buf = reg_address;
+               else if (i2c_transfer(client->adapter, msgs, 2) >= 0)
+                       *buf = reg_address;
+               buf++;
+               idx++;
+       }
+       return idx;
+}
+
+/**
+ * @brief display a short summary of the adapter
+ *
+ * @param dev -dev
+ *
+ * @return none
+ */
+static void i2cbus_shortinfo(struct device_d *dev)
+{
+       struct i2c_adapter *adap;
+       struct i2cdev_bus *i2cdev_bus = (struct i2cdev_bus *)dev->platform_data;
+       adap = i2cdev_bus->adap;
+       fprintf(stdout, "Adapter Name: %s\n", adap->name);
+}
+
+/**
+ * @brief show detailed info of the adapter
+ *
+ * @param dev system dev
+ *
+ * @return none
+ */
+static void i2cbus_showinfo(struct device_d *dev)
+{
+       struct i2c_adapter *adap;
+       struct i2cdev_bus *i2cdev_bus = (struct i2cdev_bus *)dev->platform_data;
+       unsigned long funcs;
+       int i;
+
+       adap = i2cdev_bus->adap;
+       /* Display basic info */
+       fprintf(stdout,
+               "Adapter Name: %s\n"
+               "Adapter ID  : %d\n"
+               "Adapter Nr  : %d\n"
+               "Has Algo?   : %s\n"
+               "Timeout     : %d\n"
+               "Retries     : %d\n"
+               "----------------\n"
+               "Functionalities:\n",
+               adap->name,
+               adap->id,
+               adap->nr,
+               (adap->algo) ? "yes" : "no", adap->timeout, adap->retries);
+
+       funcs = i2c_get_functionality(adap);
+       for (i = 0; all_func[i].value; i++) {
+               fprintf(stdout, "%-32s %s\n", all_func[i].name,
+                       (funcs & all_func[i].value) ? "yes" : "no");
+       }
+}
+
+/**
+ * @brief ioctls for the bus
+ *
+ * @param dev system dev
+ * @param cmd
+ * @param argument
+ *
+ * @return ioctl result
+ */
+static int i2cbus_ioctl(struct device_d *dev, int cmd, void *argument)
+{
+       struct i2c_client *client = (struct i2c_client *)dev->priv;
+       unsigned long arg = (unsigned long)argument;
+       int ret = 0;
+       dbg_entry("dev=%x cmd=%x arg=%x", (u32) dev, (u32) cmd, (u32) arg);
+
+       if (arg > 0x7f) {
+               fprintf(stderr, "arg %x>0x7f\n", arg);
+               return -EINVAL;
+       }
+       switch (cmd) {
+       case I2C_SLAVE_ADD:
+               if (i2cdev_check_addr(client->adapter, arg)) {
+                       fprintf(stderr, "There is already a "
+                               "client on that address\n");
+                       return -EBUSY;
+               }
+               ret = i2cdev_new(client->adapter->nr, arg);
+               break;
+       case I2C_SLAVE_REM:
+               /* we dont attach to normal i2c client struct..
+                * so call our integral search func */
+               ret = i2cdev_remove(client->adapter->nr, arg);
+               break;
+       default:
+               ret = -ENOTTY;
+       }
+       return ret;
+}
+
+/**
+ * @brief open a bus device
+ *
+ * @param dev system dev
+ * @param file
+ *
+ * @return open success or not
+ */
+static int i2cbus_open(struct device_d *dev, struct filep *file)
+{
+       struct i2cdev_bus *i2cdev_bus = (struct i2cdev_bus *)dev->platform_data;
+       struct i2c_client *client;
+       struct i2c_adapter *adap;
+       unsigned long funcs;
+
+       dbg_entry("dev=%x file=%x", (u32) dev, (u32) file);
+
+       /* I am not multi-user */
+       if (dev->priv) {
+               dev_err("I am already in use\n");
+               return -EBUSY;
+       }
+       /* In case someone else registered with me.. do a basic check
+        * at least
+        */
+       BUG_ON(!i2cdev_bus);
+
+       /* Reserve the adapter for ourself */
+       adap = i2c_get_adapter(i2cdev_bus->adap->nr);
+       if (!adap) {
+               dev_err("Bus being used by someone else?\n");
+               return -EBUSY;
+       }
+
+       /* Check i2c functionality */
+       funcs = i2c_get_functionality(i2cdev_bus->adap);
+
+       if (!(funcs & I2C_FUNC_I2C)) {
+               dev_err("I2C support is missing!");
+               return -EINVAL;
+       }
+
+       /* This creates an anonymous i2c_client, which may later be
+        * given an address later on
+        *
+        * This client is ** NEVER REGISTERED ** with the driver model
+        * or I2C core code!!  It just holds private copies of addressing
+        * information and maybe a PEC flag.
+        */
+       client = calloc(1, sizeof(*client));
+       if (!client) {
+               i2c_put_adapter(adap);
+               return -ENOMEM;
+       }
+
+       sprintf(client->name, "i2c-bus %d", adap->nr);
+
+       /* Attach a dummy driver */
+       client->driver = &i2cdev_driver;
+
+       client->adapter = adap;
+       dev->priv = client;
+
+       return 0;
+}
+
+/**
+ * @brief done using the bus device. now release it.
+ *
+ * @param dev system dev
+ * @param file
+ *
+ * @return
+ */
+static int i2cbus_release(struct device_d *dev, struct filep *file)
+{
+       struct i2c_client *client = (struct i2c_client *)dev->priv;
+
+       dbg_entry("dev=%x file=%x, client=%x", (u32) dev, (u32) file,
+                 (u32) client);
+
+       i2c_put_adapter(client->adapter);
+       free(client);
+       dev->priv = NULL;
+
+       return 0;
+}
+
+static struct driver_d i2cbus_fops = {
+       .name = I2C_BUS_NAME,
+       .probe = dummy_probe,
+       .ioctl = i2cbus_ioctl,
+       .read = i2cbus_read,
+       .lseek = dev_lseek_default,
+       .open = i2cbus_open,
+       .remove = dummy_probe,
+       .close = i2cbus_release,
+       .info = i2cbus_showinfo,
+       .shortinfo = i2cbus_shortinfo,
+       .type = DEVICE_TYPE_I2CDEV,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief attach a new adapter to the device architecture
+ *
+ * @param adap adapter name
+ *
+ * @return
+ */
+int i2cdev_attach_adapter(struct i2c_adapter *adap)
+{
+       struct device_d *dev;
+       struct i2cdev_bus *i2cdev_bus;
+       int res;
+       dbg_entry("adap=%x", (u32) adap);
+       i2cdev_bus = get_free_i2c_bus(adap);
+       if (!i2cdev_bus) {
+               dev_err("Did not get free dev-adap %d\n", adap->nr);
+               return -ENOMEM;
+       }
+       dev = &i2cdev_bus->dev;
+       /* Store my structure in platform_data
+        * dev->priv will be used to store i2c_client
+        */
+       dev->platform_data = (void *)i2cdev_bus;
+       dev->size = 0x80;
+       /* my i2cdev type */
+       sprintf((char *)&dev->name, "%s", I2C_BUS_NAME);
+       sprintf((char *)&dev->id, "%s%d", I2C_BUS_NAME, adap->nr);
+       dev->type = DEVICE_TYPE_I2CDEV;
+
+       res = register_device(dev);
+       if (res < 0) {
+               dev_err("Device %d registration failed %d\n", adap->nr, res);
+               perror(dev->id);
+               return_i2c_bus(i2cdev_bus);
+               return res;
+       }
+       return res;
+}
+EXPORT_SYMBOL(i2cdev_attach_adapter);
+
+/**
+ * @brief detach the adapter from the device tree
+ *
+ * @param adap -adapter
+ *
+ * @return result
+ */
+int i2cdev_detach_adapter(struct i2c_adapter *adap)
+{
+       struct i2cdev_bus *i2cdev_bus;
+       int ret = 0;
+       dbg_entry("adap=%x", (u32) adap);
+       i2cdev_bus = i2c_bus_from_busid(adap->nr);
+       if (!i2cdev_bus) {
+               dev_err("not registered adapter %d with me!\n", adap->nr);
+               return -ENODEV;
+       }
+       ret = unregister_device(&(i2cdev_bus->dev));
+       if (ret) {
+               dev_err("Adapter %d dev is in use(%d)?\n", adap->nr, ret);
+               return ret;
+       }
+       if (i2cdev_bus)
+               return_i2c_bus(i2cdev_bus);
+       return ret;
+}
+EXPORT_SYMBOL(i2cdev_attach_adapter);
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief read an i2cdev device ->byte by byte.. no multibyte reads..
+ *
+ * @param dev  device
+ * @param buffer data buffer to put data
+ * @param count how much data to read
+ * @param offset which offset to get data
+ * @param flags the usual
+ *
+ * @return operation result
+ */
+static ssize_t i2cdev_read(struct device_d *dev, void *buffer, size_t count,
+                          ulong offset, ulong flags)
+{
+       struct i2c_client *client = (struct i2c_client *)dev->priv;
+       char *buf = (char *)buffer;
+       int ret;
+       int idx = 0;
+       char dev_offset = (char)offset;
+       /* it is simpler to do a multibyte read, but that would not work
+        * on all devices. */
+       struct i2c_msg msgs[2] = {
+               /* register address */
+               {client->addr, 0, 1, &dev_offset},
+               /* Attempt a 0 byte read */
+               {client->addr, 0 | I2C_M_RD, 1, NULL}
+       };
+       dbg_entry("dev=%x buffer=%x count=%x offset=%x flags=%x"
+               " client=%x addr=%x", (u32) dev, (u32) buffer,
+               (u32) count, (u32) offset, (u32) flags,
+               (u32) client, client->addr);
+       count = min(count, dev->size - offset);
+       while (idx < count) {
+               msgs[1].buf = buf;
+               ret = i2c_transfer(client->adapter, msgs, 2);
+               if (ret <= 0)
+                       return ret;
+               buf++;
+               idx++;
+               dev_offset++;
+       }
+       return idx;
+}
+
+/**
+ * @brief write to an i2cdev device ->byte by byte.. no multibyte writes..
+ *
+ * @param dev  device
+ * @param buffer data buffer to take data
+ * @param count how much data to write
+ * @param offset which offset to put data
+ * @param flags the usual
+ *
+ * @return operation result
+ */
+static ssize_t i2cdev_write(struct device_d *dev, const void *buffer,
+                           size_t count, ulong offset, ulong flags)
+{
+       struct i2c_client *client = (struct i2c_client *)dev->priv;
+       char *buf = (char *)buffer;
+       int ret;
+       int idx = 0;
+       char buff[2];
+       /* it is simpler to do a multibyte write, but that would not work
+        * on all devices. */
+       struct i2c_msg msgs[1] = {
+               /* register address and data go together */
+               {client->addr, 0, 2, buff},
+       };
+
+       dbg_entry("dev=%x buffer=%x count=%x offset=%x flags=%x", (u32) dev,
+                 (u32) buffer, (u32) count, (u32) offset, (u32) flags);
+
+       count = min(count, dev->size - offset);
+       while (idx < count) {
+               buff[0] = (char)(offset + idx);
+               buff[1] = *buf;
+               ret = i2c_transfer(client->adapter, msgs, 1);
+               if (ret <= 0)
+                       return ret;
+               buf++;
+               idx++;
+       }
+       return idx;
+}
+
+/**
+ * @brief open the device
+ * we reuse most of i2cbus open functionality here.
+ *
+ * Then, since i2cbus_release kills the i2c_client, we reuse it on close
+ *
+ * @param dev dev
+ * @param file file
+ *
+ * @return result value
+ */
+static int i2cdev_open(struct device_d *dev, struct filep *file)
+{
+       int ret;
+       struct i2c_client *client;
+       struct i2cdev_dev *d = (struct i2cdev_dev *)dev->platform_data;
+       /* This is small piece of hack. we want to do what ever
+        * i2cbus_open does, so put the platform_data as what it expects
+        * and then put ours back.
+        */
+       dev->platform_data = d->i2cdev_bus;
+       /* All the stuff we want checked and done is done in bus!
+        * reuse it! */
+       ret = i2cbus_open(dev, file);
+       dev->platform_data = (void *)d;
+       if (ret)
+               return ret;
+
+       /* retrieve the client and customize a few things */
+       client = (struct i2c_client *)dev->priv;
+       client->addr = d->daddr;
+       sprintf(client->name, "i2cdev_%x_%x", client->adapter->nr,
+               client->addr);
+
+       return 0;
+}
+
+/**
+ * @brief Do a device probe. if there are other client drivers, we will not
+ * hook on.
+ *
+ * @param dev dev
+ *
+ * @return 0 if all things ok, else -EINVAL or error val
+ */
+static int i2cdev_probe(struct device_d *dev)
+{
+       struct i2c_adapter *adap;
+       struct i2cdev_dev *d = (struct i2cdev_dev *)dev->platform_data;
+       int ret;
+
+       /* Check if someone else is registering with my driver */
+       BUG_ON(!d);
+       BUG_ON(!d->i2cdev_bus);
+       adap = d->i2cdev_bus->adap;
+       BUG_ON(!adap);
+
+       /* if some other client driver exists.. we will not hook on! */
+       ret = i2cdev_check_addr(adap, d->daddr);
+
+       return ret;
+}
+
+static struct driver_d i2cdev_fops = {
+       .name = I2C_DEV_NAME,
+       .probe = i2cdev_probe,
+       .remove = dummy_probe,
+       .read = i2cdev_read,
+       .write = i2cdev_write,
+       .lseek = dev_lseek_default,
+       .open = i2cdev_open,
+       .close = i2cbus_release,
+       .type = DEVICE_TYPE_I2CDEV,
+};
+
+/* ------------------------------------------------------------------------- */
+
+/**
+ * @brief attach a new device to the device tree
+ *
+ * I can be called from either a platform file OR by sending ioctls to i2cbus
+ * device.
+ *
+ * @param adapter_number adapter number
+ * @param device_address device address
+ *
+ * @return -result
+ */
+int i2cdev_new(unsigned int adapter_number, unsigned int device_address)
+{
+       struct i2cdev_bus *i2cdev_bus;
+       struct device_d *dev;
+       struct i2cdev_dev *d;
+       int res;
+
+       if (device_address > 0x7F) {
+               dev_err("bad dev address %x!\n", device_address);
+               return -EINVAL;
+       }
+
+       i2cdev_bus = i2c_bus_from_busid(adapter_number);
+       if (!i2cdev_bus) {
+               dev_err("not registered adapter %d with me!\n", adapter_number);
+               return -ENODEV;
+       }
+       d = calloc(1, sizeof(*d));
+       if (!d) {
+               dev_err("unable to allocate memory\n");
+               return -ENOMEM;
+       }
+       /* I hate searching again.. so store the bus pointer */
+       d->i2cdev_bus = i2cdev_bus;
+       d->daddr = device_address;
+       dev = &d->dev;
+       /* Store my master structure for retrieval
+        * dev->priv will be used for i2c_client
+        */
+       dev->platform_data = (void *)d;
+       dev->size = 256;
+
+       sprintf((char *)&dev->name, "%s", I2C_DEV_NAME);
+       sprintf((char *)&dev->id, "%s%d_%x", I2C_BUS_NAME, adapter_number,
+               device_address);
+       dev->type = DEVICE_TYPE_I2CDEV;
+       res = register_device(dev);
+       if (res < 0) {
+               dev_err("Bus %d Device 0x%x registration failed %d\n",
+                       adapter_number, device_address, res);
+               perror(dev->id);
+               free(d);
+               return res;
+       }
+       list_add_tail(&d->list, &i2c_dev_list);
+       return res;
+}
+EXPORT_SYMBOL(i2cdev_new);
+
+/**
+ * @brief remove the device
+ *
+ * I can be called from either a platform file OR by sending ioctls to i2cbus
+ * device.
+ *
+ * @param adapter_number adapter numbe adapter numberr
+ * @param device_address device address
+ *
+ * @return -ENODEV if there is no such device in the system
+ */
+int i2cdev_remove(unsigned int adapter_number, unsigned int device_address)
+{
+       struct device_d *dev;
+       struct i2cdev_dev *d;
+       char id[MAX_DRIVER_NAME];
+       sprintf(id, "%s%d_%x", I2C_BUS_NAME, adapter_number, device_address);
+       list_for_each_entry(d, &i2c_dev_list, list) {
+               if (!strcmp(d->dev.id, id))
+                       goto found;
+       }
+       return -ENODEV;
+found:
+       dev = &d->dev;
+       unregister_device(dev);
+       list_del(&d->list);
+       free(d);
+       return 0;
+}
+EXPORT_SYMBOL(i2cdev_remove);
+
+/* ------------------------------------------------------------------------- */
+
+static int i2cdev_init(void)
+{
+
+       int res = 0;
+       dbg_entry("%s", "none");
+       res = register_driver(&i2cbus_fops);
+       if (res)
+               return res;
+       res = register_driver(&i2cdev_fops);
+       return res;
+}
+device_initcall(i2cdev_init);
Index: u-boot-v2.git/drivers/i2c/Kconfig
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/Kconfig      2008-06-24 19:37:44.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/Kconfig   2008-06-24 19:38:02.000000000 -0500
@@ -19,6 +19,16 @@

 if I2C

+config I2C_CHARDEV
+       tristate "I2C device interface"
+       help
+         Say Y here to use i2c-* device files, usually found in the /dev
+         directory on your system.  They make it possible to have applications
+         to use the I2C bus.
+
+         This support is also available as a module.  If so, the module
+         will be called i2c-dev.
+
 source drivers/i2c/busses/Kconfig

 config I2C_DEBUG_CORE
@@ -28,6 +38,14 @@
          messages to the system log.  Select this if you are having a
          problem with I2C support and want to see more of what is going on.

+config I2C_DEBUG_DEV
+       bool "I2C Dev debugging messages"
+       depends on I2C_CHARDEV
+       help
+         Say Y here if you want the I2C dev to produce a bunch of debug
+         messages to the system log.  Select this if you are having a
+         problem with I2C support and want to see more of what is going on.
+
 config I2C_DEBUG_BUS
        bool "I2C Bus debugging messages"
        help
Index: u-boot-v2.git/drivers/i2c/Makefile
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/Makefile     2008-06-24 19:37:44.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/Makefile  2008-06-24 19:38:02.000000000 -0500
@@ -3,6 +3,7 @@
 #

 obj-$(CONFIG_I2C)              += i2c-core.o
+obj-$(CONFIG_I2C_CHARDEV)      += i2c-dev.o
 obj-y                          += busses/

 ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
Index: u-boot-v2.git/drivers/i2c/i2c-core.c
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/i2c-core.c   2008-06-24 19:37:58.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/i2c-core.c        2008-06-24 19:38:02.000000000 -0500
@@ -94,6 +94,10 @@

        debug("adapter [%s] registered\n", adap->name);

+#ifdef CONFIG_I2C_CHARDEV
+       i2cdev_attach_adapter(adap);
+#endif
+
 out_unlock:
        return res;

@@ -214,6 +218,14 @@
                goto out_unlock;
        }

+#ifdef CONFIG_I2C_CHARDEV
+       res = i2cdev_detach_adapter(adap);
+       if (res) {
+               dev_err("adapter in use\n");
+               goto out_unlock;
+       }
+#endif
+
        /* detach any active clients. This must be done first, because
         * it can fail; in which case we give up. */
        list_for_each_safe(item, _n, &adap->clients) {
Index: u-boot-v2.git/drivers/i2c/i2c-core.h
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/i2c-core.h   2008-06-24 19:37:44.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/i2c-core.h        2008-06-24 19:38:02.000000000 -0500
@@ -34,4 +34,8 @@
 extern struct list_head        __i2c_board_list;
 extern int             __i2c_first_dynamic_bus_num;

+/* Automatically called by i2c-Core..*/
+int i2cdev_attach_adapter(struct i2c_adapter *adap);
+int i2cdev_detach_adapter(struct i2c_adapter *adap);
+
 #endif         /*  __i2C_CORE_H */




More information about the U-Boot mailing list