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

Menon, Nishanth x0nishan at ti.com
Thu Jun 19 17:12:06 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.

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

---
 drivers/i2c/Kconfig     |   10 +
 drivers/i2c/Makefile    |    1
 drivers/i2c/i2c-core.c  |   12 +
 drivers/i2c/i2c-core.h  |    4
 drivers/i2c/i2c-dev.c   |  439 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/i2c-dev.h |   71 +++++++
 6 files changed, 537 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-19 08:42:58.000000000 -0500
@@ -0,0 +1,71 @@
+/**
+ * @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
+
+#include <linux/types.h>
+
+/* /dev/i2c-X ioctl commands.  The ioctl's parameter is always an
+ * unsigned long, except for:
+ *     - I2C_FUNCS, takes pointer to an unsigned long
+ *     - I2C_RDWR, takes pointer to struct i2c_rdwr_ioctl_data
+ *     - I2C_SMBUS, takes pointer to struct i2c_smbus_ioctl_data
+ */
+#define I2C_RETRIES    0x0701  /* number of times a device address should
+                                  be polled when not acknowledging */
+#define I2C_TIMEOUT    0x0702  /* set timeout in jiffies - call with int */
+
+/* NOTE: Slave address is 7 or 10 bits, but 10-bit addresses
+ * are NOT supported! (due to code brokenness)
+ */
+#define I2C_SLAVE      0x0703  /* Use this slave address */
+#define I2C_SLAVE_FORCE        0x0706  /* Use this slave address, even if it
+                                  is already in use by a driver! */
+#define I2C_TENBIT     0x0704  /* 0 for 7 bit addrs, != 0 for 10 bit */
+
+#define I2C_FUNCS      0x0705  /* Get the adapter functionality mask */
+
+#define I2C_RDWR       0x0707  /* Combined R/W transfer (one STOP only) */
+
+#define I2C_PEC                0x0708  /* != 0 to use PEC with SMBus */
+
+/* This is the structure as used in the I2C_RDWR ioctl call */
+struct i2c_rdwr_ioctl_data {
+       struct i2c_msg *msgs;   /* pointers to i2c_msgs */
+       u32 nmsgs;                      /* number of i2c_msgs */
+};
+
+#define  I2C_RDRW_IOCTL_MAX_MSGS       42
+
+#define I2C_DEV_NAME "i2cd"
+
+#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-19 08:42:58.000000000 -0500
@@ -0,0 +1,439 @@
+/**
+ * @file
+ * @brief i2c-bus driver, "char device" interface
+ *
+ * FileName: drivers/i2c/i2c-dev.c
+ *
+ */
+/* 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>
+    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 */
+
+/* The I2C_RDWR ioctl code is written by Kolja Waschk <waschk at telos.de> */
+
+#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>
+
+#include "i2c-core.h"
+
+#ifdef CONFIG_I2C_DEBUG_DEV
+#define DBG_MODULE_NAME "i2cdev"
+#define dev_dbg(ARGS...) fprintf(stdout, ARGS);
+#define dev_warn(ARGS...) fprintf(stderr, ARGS);
+#define dbg_entry(FORMAT, ARGS...) fprintf(stdout,\
+               DBG_MODULE_NAME"%s:%d:Entry:"FORMAT"\n",\
+               __func__, __LINE__, ARGS)
+#define dbg_exit(FORMAT, ARGS...) fprintf(stdout,\
+               DBG_MODULE_NAME"%s:%d:Exit:"FORMAT"\n",\
+               __func__, __LINE__, ARGS)
+#else
+#define dev_dbg(ARGS...)
+#define dev_warn(ARGS...)
+#define dbg_entry(FORMAT, ARGS...)
+#define dbg_exit(FORMAT, ARGS...)
+#endif
+#define dev_err(ARGS...) fprintf(stderr, ARGS);
+
+static struct i2c_driver i2cdev_driver;
+
+/*
+ * An i2c_dev 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.
+ *
+ * The list of i2c_dev structures is parallel to the i2c_adapter lists
+ * maintained by the driver model, and is updated using notifications
+ * delivered to the i2cdev_driver.
+ */
+struct i2c_dev {
+       struct list_head list;
+       struct i2c_adapter *adap;
+       struct device_d dev;
+};
+
+#define I2C_MINORS     256
+static LIST_HEAD(i2c_dev_list);
+
+static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
+{
+       struct i2c_dev *i2c_dev;
+
+       dbg_entry("index=%x", index);
+
+       list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
+               if (i2c_dev->adap->nr == index)
+                       goto found;
+       }
+       i2c_dev = NULL;
+found:
+       return i2c_dev;
+}
+
+static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
+{
+       struct i2c_dev *i2c_dev;
+       dbg_entry("adap=%x", (u32) adap);
+
+       if (adap->nr >= I2C_MINORS) {
+               dev_err("i2c-dev: Out of device minors (%d)\n", adap->nr);
+               return NULL;
+       }
+
+       i2c_dev = calloc(1, sizeof(*i2c_dev));
+       if (!i2c_dev)
+               return NULL;
+       i2c_dev->adap = adap;
+
+       list_add_tail(&i2c_dev->list, &i2c_dev_list);
+       return i2c_dev;
+}
+
+static void return_i2c_dev(struct i2c_dev *i2c_dev)
+{
+       dbg_entry("i2c_dev=%x", (u32) i2c_dev);
+       list_del(&i2c_dev->list);
+       free(i2c_dev);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * After opening an instance of this character special file, a file
+ * descriptor starts out associated only with an i2c_adapter (and bus).
+ *
+ * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg
+ * traffic to any devices on the bus used by that adapter.  That's because
+ * the i2c_msg vectors embed all the addressing information they need, and
+ * are submitted directly to an i2c_adapter.  However, SMBus-only adapters
+ * don't support that interface.
+ *
+ * To use read()/write() system calls on that file descriptor, or to use
+ * SMBus interfaces (and work with SMBus-only hosts!), you must first issue
+ * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl.  That configures an anonymous
+ * (never registered) i2c_client so it holds the addressing information
+ * needed by those system calls and by this SMBus interface.
+ */
+
+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;
+
+       dbg_entry("dev=%x buffer=%x count=%x offset=%x flags=%x", (u32) dev,
+                 (u32) buffer, (u32) count, (u32) offset, (u32) flags);
+       return i2c_master_recv(client, buffer, count);
+}
+
+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;
+
+       dbg_entry("dev=%x buffer=%x count=%x offset=%x flags=%x", (u32) dev,
+                 (u32) buffer, (u32) count, (u32) offset, (u32) flags);
+       return i2c_master_send(client, buffer, count);
+}
+
+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;
+}
+
+/* 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. */
+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;
+}
+
+static int i2cdev_ioctl_rdrw(struct i2c_client *client, unsigned long arg)
+{
+       struct i2c_rdwr_ioctl_data rdwr_arg;
+       struct i2c_msg *rdwr_pa;
+       u8 **data_ptrs;
+       int i, res;
+
+       dbg_entry("client=%x arg=%x", (u32) client, (u32) arg);
+       memcpy(&rdwr_arg, (struct i2c_rdwr_ioctl_data *)arg, sizeof(rdwr_arg));
+
+       /* Put an arbitrary limit on the number of messages that can
+        * be sent at once */
+       if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
+               return -EINVAL;
+
+       rdwr_pa = (struct i2c_msg *)
+           malloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg));
+       if (!rdwr_pa)
+               return -ENOMEM;
+
+       memcpy(rdwr_pa, rdwr_arg.msgs, rdwr_arg.nmsgs * sizeof(struct i2c_msg));
+
+       data_ptrs = malloc(rdwr_arg.nmsgs * sizeof(u8 *));
+       if (data_ptrs == NULL) {
+               free(rdwr_pa);
+               return -ENOMEM;
+       }
+
+       res = 0;
+       for (i = 0; i < rdwr_arg.nmsgs; i++) {
+               /* Limit the size of the message to a sane amount;
+                * and don't let length change either. */
+               if ((rdwr_pa[i].len > 8192) ||
+                   (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {
+                       res = -EINVAL;
+                       break;
+               }
+               data_ptrs[i] = (u8 *) rdwr_pa[i].buf;
+               rdwr_pa[i].buf = malloc(rdwr_pa[i].len);
+               if (rdwr_pa[i].buf == NULL) {
+                       res = -ENOMEM;
+                       break;
+               }
+               memcpy(rdwr_pa[i].buf, data_ptrs[i], rdwr_pa[i].len);
+       }
+       if (res < 0) {
+               int j;
+               for (j = 0; j < i; ++j)
+                       free(rdwr_pa[j].buf);
+               free(data_ptrs);
+               free(rdwr_pa);
+               return res;
+       }
+
+       res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);
+       while (i-- > 0) {
+               if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD))
+                       memcpy(data_ptrs[i], rdwr_pa[i].buf, rdwr_pa[i].len);
+               free(rdwr_pa[i].buf);
+       }
+       free(data_ptrs);
+       free(rdwr_pa);
+       return res;
+}
+
+
+static int i2cdev_ioctl(struct device_d *dev, int cmd, void *argument)
+{
+       struct i2c_client *client = (struct i2c_client *)dev->priv;
+       unsigned long funcs;
+       unsigned long arg = (unsigned long)argument;
+
+       dbg_entry("dev=%x cmd=%x arg=%x", (u32) dev, (u32) cmd, (u32) arg);
+       dev_dbg("ioctl, cmd=0x%02x, arg=0x%04x\n", cmd, arg);
+
+       switch (cmd) {
+       case I2C_SLAVE:
+       case I2C_SLAVE_FORCE:
+               if ((arg > 0x3ff) ||
+                   (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
+                       return -EINVAL;
+               if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
+                       return -EBUSY;
+               /* REVISIT: address could become busy later */
+               client->addr = arg;
+               return 0;
+       case I2C_TENBIT:
+               if (arg)
+                       client->flags |= I2C_M_TEN;
+               else
+                       client->flags &= ~I2C_M_TEN;
+               return 0;
+       case I2C_PEC:
+               if (arg)
+                       client->flags |= I2C_CLIENT_PEC;
+               else
+                       client->flags &= ~I2C_CLIENT_PEC;
+               return 0;
+       case I2C_FUNCS:
+               funcs = i2c_get_functionality(client->adapter);
+               if (!arg)
+                       return -ENOMEM;
+
+               *((unsigned long *)arg) = funcs;
+               return 0;
+
+       case I2C_RDWR:
+               return i2cdev_ioctl_rdrw(client, arg);
+
+       case I2C_RETRIES:
+               client->adapter->retries = arg;
+               break;
+       case I2C_TIMEOUT:
+               client->adapter->timeout = arg;
+               break;
+       default:
+               return -ENOTTY;
+       }
+       return 0;
+}
+
+static int i2cdev_open(struct device_d *dev, struct filep *file)
+{
+       struct i2c_client *client;
+       struct i2c_adapter *adap;
+       struct i2c_dev *i2c_dev;
+
+       dbg_entry("dev=%x file=%x", (u32) dev, (u32) file);
+       i2c_dev = i2c_dev_get_by_minor(dev->map_base);
+       if (!i2c_dev)
+               return -ENODEV;
+
+       adap = i2c_get_adapter(i2c_dev->adap->nr);
+       if (!adap)
+               return -ENODEV;
+
+       /* This creates an anonymous i2c_client, which may later be
+        * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE.
+        *
+        * 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-dev %d", adap->nr);
+       client->driver = &i2cdev_driver;
+
+       client->adapter = adap;
+       dev->priv = client;
+
+       return 0;
+}
+
+static int i2cdev_release(struct device_d *dev, struct filep *file)
+{
+       struct i2c_client *client = dev->priv;
+
+       dbg_entry("dev=%x file=%x", (u32) dev, (u32) file);
+       i2c_put_adapter(client->adapter);
+       free(client);
+       dev->priv = NULL;
+
+       return 0;
+}
+
+static struct driver_d i2cdev_fops = {
+       .name = I2C_DEV_NAME,
+       .read = i2cdev_read,
+       .probe = dummy_probe,
+       .write = i2cdev_write,
+       .ioctl = i2cdev_ioctl,
+       .open = i2cdev_open,
+       .close = i2cdev_release,
+       .type = DEVICE_TYPE_I2CDEV,
+};
+
+static int i2c_dev_init(void)
+{
+
+       int res = 0;
+       dbg_entry("%s", "none");
+       res = register_driver(&i2cdev_fops);
+       dbg_exit("res=%d", res);
+       return res;
+}
+
+device_initcall(i2c_dev_init);
+
+/* ------------------------------------------------------------------------- */
+
+int i2cdev_attach_adapter(struct i2c_adapter *adap)
+{
+       struct device_d *dev;
+       struct i2c_dev *i2c_dev;
+       int res;
+       dbg_entry("adap=%x", (u32) adap);
+       i2c_dev = get_free_i2c_dev(adap);
+       if (!i2c_dev) {
+               dev_err("Did not get free dev-adap %d\n", adap->nr);
+               return -ENOMEM;
+       }
+       dev = &i2c_dev->dev;
+       /* Store my adapter number in mapbase for retrieval */
+       dev->map_base = adap->nr;
+       /* my i2cdev type */
+       sprintf((char *)&dev->name, "%s", I2C_DEV_NAME);
+       sprintf((char *)&dev->id, "%s%d", I2C_DEV_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_dev(i2c_dev);
+               return res;
+       }
+       return res;
+}
+EXPORT_SYMBOL(i2cdev_attach_adapter);
+
+int i2cdev_detach_adapter(struct i2c_adapter *adap)
+{
+       struct i2c_dev *i2c_dev;
+       int ret = 0;
+       dbg_entry("adap=%x", (u32) adap);
+       i2c_dev = i2c_dev_get_by_minor(adap->nr);
+       if (!i2c_dev) {
+               dev_err("not registered adapter %d with me!\n", adap->nr);
+               return -ENODEV;
+       }
+       ret = unregister_device(&(i2c_dev->dev));
+       if (ret) {
+               dev_err("Adapter %d dev is in use(%d)?\n", adap->nr, ret);
+               return ret;
+       }
+       if (i2c_dev)
+               return_i2c_dev(i2c_dev);
+       return ret;
+}
+EXPORT_SYMBOL(i2cdev_attach_adapter);
Index: u-boot-v2.git/drivers/i2c/Kconfig
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/Kconfig      2008-06-19 08:42:46.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/Kconfig   2008-06-19 08:42:58.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
Index: u-boot-v2.git/drivers/i2c/Makefile
===================================================================
--- u-boot-v2.git.orig/drivers/i2c/Makefile     2008-06-19 08:42:46.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/Makefile  2008-06-19 08:42:58.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-19 08:42:46.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/i2c-core.c        2008-06-19 08:42:58.000000000 -0500
@@ -102,6 +102,10 @@

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

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

@@ -222,6 +226,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-19 08:42:46.000000000 -0500
+++ u-boot-v2.git/drivers/i2c/i2c-core.h        2008-06-19 08:42:58.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