I2C Drivers, Part I

The I2C bus helps you monitor the health of your system. Here's how to develop a driver that will get you all the hardware info you need to know.

In the June and August 2003 issues of Linux Journal, my column covered the Linux kernel driver model, and the I2C subsystem was used as an example. This month, we discuss what the I2C subsystem does and how to write a driver for it.

I2C is the name for a two-wire serial bus protocol originally developed by Phillips. It commonly is used in embedded systems so different components can communicate; PC motherboards use I2C to talk to different sensor chips. Those sensors typically report back fan speeds, processor temperatures and a whole raft of system hardware information. The protocol also is used in some RAM chips to report information about the DIMM itself back to the operating system.

The I2C kernel code has lived outside of the main kernel tree for much of its development life—it originally was written back in the 2.0 days. The 2.4 kernel contains a bit of I2C support, mainly for some video drivers. With the 2.6 kernel, a large portion of the I2C code has made it into the main kernel tree, thanks to the effort of a number of kernel developers who changed the interfaces to be more acceptable to the kernel community. A few drivers still live only in the external CVS tree and have not been moved into the main kernel.org tree, but it is only a matter of time before they, too, are ported.

The I2C kernel code is broken up into a number of logical pieces: the I2C core, I2C bus drivers, I2C algorithm drivers and I2C chip drivers. We ignore how the I2C core operates in this article and focus instead on how to write a bus and algorithm driver. In Part II, we will cover how to write an I2C chip driver.

I2C Bus Drivers

An I2C bus driver is described by a struct named i2c_adapter, which is defined in the include/linux/i2c.h file. Only the following fields need to be set up by the bus driver:

  • struct module *owner; —set to the value (THIS_MODULE) that allows the proper module reference counting.

  • unsigned int class; —the type of I2C class devices that this driver supports. Usually this is set to the value I2C_ADAP_CLASS_SMBUS.

  • struct i2c_algorithm *algo; —a pointer to the struct i2c_algorithm structure that describes the way data is transferred through this I2C bus controller. More information on this structure is provided below.

  • char name[I2C_NAME_SIZE]; —set to a descriptive name of the I2C bus driver. This value shows up in the sysfs filename associated with this I2C adapter.

The code below comes from an example I2C adapter driver called tiny_i2c_adap.c, available from the Linux Journal FTP site [ftp.linuxjournal.com/pub/lj/listings/issue116/7136.tgz] and shows how the struct i2c_adapter is set up:

static struct i2c_adapter tiny_adapter = {
    .owner  = THIS_MODULE,
    .class  = I2C_ADAP_CLASS_SMBUS,
    .algo   = &tiny_algorithm,
    .name   = "tiny adapter",

To register this I2C adapter, the driver calls the function i2c_add_adapter with a pointer to the struct i2c_adapter:

retval = i2c_add_adapter(&tiny_adapter);

If the I2C adapter lives on a type of device that has a struct device associated with it, such as a PCI or USB device, then before the call to i2c_add_adapter, the adapter device's parent pointer should be set to that device. This pointer configuration can be seen in the following line from the drivers/i2c/busses/i2c-piix4.c driver:

/* set up sysfs linkage to our parent device */
piix4_adapter.dev.parent = &dev->dev;

If this parent pointer is not set up, the I2C adapter is positioned on the legacy bus and shows up in the sysfs tree at /sys/devices/legacy. Here is what happens to our example driver when it is registered:

$ tree /sys/devices/legacy/
|-- detach_state
|-- floppy0
|   |-- detach_state
|   `-- power
|       `-- state
|-- i2c-0
|   |-- detach_state
|   |-- name
|   `-- power
|       `-- state
`-- power
    `-- state

As discussed in the previous kernel driver model columns, the I2C adapter also shows up in the /sys/class/i2c-adapter directory:

$ tree /sys/class/i2c-adapter/
`-- i2c-0
    |-- device -> ../../../devices/legacy/i2c-0
    `-- driver -> ../../../bus/i2c/drivers/i2c_adapter

To unregister an I2C adapter, the driver should call the function i2c_del_adapter with a pointer to the struct i2c_adapter, like this:




Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.


Anonymous's picture

I am tried building the examples from parts 1 and 2, and got errors. I am running version 2.6.21. The example from part 1 tried to include linux/config.h, which does not seem to exist in 2.6. I removed the include, but I still get errors. They start with the spinlock header

include/linux/spinlock.h: At top level:
include/linux/spinlock.h:290: error: expected declaration specifiers or '...' before 'bool'
include/linux/spinlock.h: In function 'double_spin_lock':
include/linux/spinlock.h:294: error: 'l1_first' undeclared (first use in this function)
include/linux/spinlock.h:294: error: (Each undeclared identifier is reported only once
include/linux/spinlock.h:294: error: for each function it appears in.)
include/linux/spinlock.h:295: warning: implicit declaration of function 'current_thread_info'
include/linux/spinlock.h:295: error: invalid type argument of '->' (have 'int')
include/linux/spinlock.h:295: warning: implicit declaration of function 'barrier'
include/linux/spinlock.h:296: error: invalid type argument of '->' (have 'int')
include/linux/spinlock.h:298: error: invalid type argument of '->' (have 'int')
include/linux/spinlock.h:299: error: invalid type argument of '->' (have 'int')
include/linux/spinlock.h: At top level:
include/linux/spinlock.h:309: error: expected declaration specifiers or '...' before 'bool'
include/linux/spinlock.h: In function 'double_spin_unlock':
include/linux/spinlock.h:313: error: 'l1_taken_first' undeclared (first use in this function)
include/linux/spinlock.h:314: error: invalid type argument of '->' (have 'int')
include/linux/spinlock.h:314: warning: implicit declaration of function 'unlikely'
include/linux/spinlock.h:314: warning: implicit declaration of function 'test_thread_flag'
include/linux/spinlock.h:314: error: 'TIF_NEED_RESCHED' undeclared (first use in this function

Is there something I need to change to make this example compatible with 2.6?

trying to write a driver for an audio chip

kamou's picture

hello !

I'm trying to write a driver for an audio chip, this chip can be controlled via i2c bus (i2c-0)

the problem is that I don't understand how I can "open" the i2c-0 in kernel space...
I tried to do as le lm75 sensor driver...but it's not better..
I know that I have to register my driver as an i2c driver with the i2c_add_driver function

does this:
static int
chip_attach_adapter(struct i2c_adapter *adapter)
return i2c_detect(adapter, &addr_data,

probes every i2c bus on my board ?
and I don't understand what addr_data is or what it does,
I know it is in the SENSORS_INSMOD_1 macro
but I'm not writing a driver for a sensor, but for an audio chip...

can anyone help me understand better all this ?

Geek Guide
The DevOps Toolbox

Tools and Technologies for Scale and Reliability
by Linux Journal Editor Bill Childers

Get your free copy today

Sponsored by IBM

8 Signs You're Beyond Cron

Scheduling Crontabs With an Enterprise Scheduler
On Demand
Moderated by Linux Journal Contributor Mike Diehl

Sign up and watch now

Sponsored by Skybot