Skip to content

Day19 - I2C Sensor Driver (Kernel)

Overview

In this lab, we implemented a Linux I2C sensor driver using:

  • STM32 as a fake I2C slave device
  • Raspberry Pi as I2C master
  • Linux kernel i2c_driver framework

Key topics:

  • I2C driver probe/remove lifecycle
  • Device Tree binding
  • Register-based device abstraction
  • sysfs interface design
  • Synchronization using mutex

System Architecture

STM32 (I2C slave)
Register Map (fake sensor)
Linux I2C Driver
sysfs (/sys/bus/i2c/devices/1-0048/)
User Space (cat / echo)

Register-Based Device Model

The sensor is designed as a register-based device.

Address Name Description
0x00 CHIP_ID Device ID
0x01 CONFIG Configuration
0x02 TEMP_MSB Temperature MSB
0x03 TEMP_LSB Temperature LSB
0x04 STATUS Status flags
0x05 UPDATE_PERIOD Update interval
0x06 COMMAND Command register

I2C Driver Lifecycle

Probe

static int myi2c_sensor_probe(struct i2c_client *client)
  • Called when device is matched via Device Tree
  • Initialize driver context
  • Verify CHIP_ID
  • Create sysfs attributes

Remove

static void myi2c_sensor_remove(struct i2c_client *client)
  • Remove sysfs group
  • Release allocated resources

Device Tree Binding

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2712";

    fragment@0 {
        target = <&i2c1>;
        __overlay__ {
            status = "okay";

            myi2c_sensor@48 {
                compatible = "myvendor,myi2c-sensor";
                reg = <0x48>;
            };
        };
    };
};

Sysfs Interface Design

Data Plane vs Control Plane

This driver separates:

  • Data plane → sensor readings
  • Control plane → configuration / commands

Sysfs Attributes

Attribute Type Description
chip_id RO Device ID
temp_raw RO Raw sensor value
temp_input RO Temperature (milli-degree Celsius)
update_once WO Trigger measurement
config RW Configuration register
status RO Status register
update_period RW Update interval

Temperature Representation

The driver exports temperature in milli-degree Celsius (m°C).

Example:

  • raw value = 2529 → 25.29°C
  • temp_input = 25290

Reason:

  • sysfs is a stable kernel ABI
  • values must be machine-readable
  • formatting should be done in user space

Synchronization

All I2C operations are protected using a mutex:

mutex_lock(&data->lock);
/* I2C access */
mutex_unlock(&data->lock);

This prevents race conditions between concurrent sysfs accesses.


Key Design Decisions

  • Use register abstraction (real hardware model)
  • Separate control/data plane
  • Use sysfs instead of ioctl
  • Use milli-degree for standardization
  • Use mutex for safe access

Known Limitations

  • No interrupt-based data ready
  • No caching mechanism
  • Not integrated with hwmon subsystem
  • Polling-based update

Next Step

  • Convert driver to hwmon subsystem
  • Support standard interface:
/sys/class/hwmon/hwmonX/temp1_input