[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