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

Sascha Hauer s.hauer at pengutronix.de
Tue Jun 24 10:05:25 CEST 2008


On Fri, Jun 20, 2008 at 09:34:47PM -0500, Menon, Nishanth wrote:
> 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
> 
> This is based on the discussion here:
> http://www.nabble.com/-Patch-0-9--U-boot-V2%3A-
> Introduce-I2C-support-for-SDP3430-to17981717.html
>  #a18029740
> 
> Patches 4 and 5 in the previous series have
> changed, 6 is redundant and removed from the
> series.
> 
> 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   |  813 ++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/i2c-dev.h |   43 ++
>  6 files changed, 883 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-20 21:09:57.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-20 21:16:25.000000000 -0500
> @@ -0,0 +1,813 @@
> +/**
> + * @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
> + * 1 byte read/write operations as per i2c spec.
> + *
> + * Note: both of these can confuse certain devices on I2C bus.
> + * USE WITH CAUTION.
> + */
> +/* Originally from Linux kernel. Ported to U-boot-v2.
> + * Note: even though we borrow i2c-dev from linux, there are
> + * major differences in operations.
> + *
> + * (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>
> +
> +#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);

Note to self: We need a better set of debug macros. debug is ok, but the
name is poorly chosen and not sufficient. Drivers shouldn't add their
own macros.

> +
> +#define I2C_BUS_NAME "i2cbus"
> +#define I2C_DEV_NAME "i2cdev"
> +
> +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;
> +};
> +
> +/**
> + * contains the internal structure for a device node this is the i2c device
> + */
> +struct i2cdev_dev {
> +       struct list_head list;
> +       struct device_d dev;
> +};
> +
> +#define I2C_MINORS     256

This seems to be an artificial limitation.

> +static LIST_HEAD(i2c_bus_list);
> +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_get_by_minor(unsigned index)
> +{

Better get_by_bus_id()?

> +       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 (adap->nr >= I2C_MINORS) {
> +               dev_err("i2c-dev: Out of device minors (%d)\n", adap->nr);
> +               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)) {
> +                       /* No client driver? aha! we shalt access thee! */
> +                       if (i2c_transfer(client->adapter, msgs, 2) >= 0)
> +                               *buf = reg_address;
> +               } else
> +                       /* Client driver present.. probable that device exists!
> +                        * we dare not access it though :( - dunno how it's
> +                        * gonna behave
> +                        */
> +                       *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;
> +       i2cdev_bus = i2c_bus_get_by_minor(dev->map_base);
> +       if (!i2cdev_bus) {
> +               dev_err("did not get bus\n");
> +               return;
> +       }
> +       adap = i2c_get_adapter(i2cdev_bus->adap->nr);
> +       if (!adap) {
> +               dev_err("No such adapter!\n");
> +               return;
> +       }
> +       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;
> +       unsigned long funcs;
> +       int i;
> +       i2cdev_bus = i2c_bus_get_by_minor(dev->map_base);

The function is called get_by_minor, the argument name in the
declaration indicates that it is passed an index and here it gets passed
map_base. There is something mixed up here.
*dev should lead you directly to the bus without having to use the help
of a function which loops over busses.

> +       if (!i2cdev_bus) {
> +               dev_err("did not get bus\n");
> +               return;
> +       }
> +       adap = i2c_get_adapter(i2cdev_bus->adap->nr);

I guess i2c_get_adapter() returns nothing different than
i2cdev_bus->adap.
i2cdev_bus and i2c_adapter seem to have a 1:1 relationship,
so just move the fields into one struct.

> +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 adapter number in mapbase for retrieval */
> +       dev->map_base = adap->nr;

Ah ok, I understand. Please do not hijack this field for other uses than
it was intended to.

Regards,
  Sascha

-- 
Pengutronix e.K. - Linux Solutions for Science and Industry
-----------------------------------------------------------
Kontakt-Informationen finden Sie im Header dieser Mail oder
auf der Webseite -> http://www.pengutronix.de/impressum/ <-




More information about the U-Boot mailing list