[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