tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

I2C device drivers in NetBSD


For the past month I've been working on porting NetBSD to Openmoko GTA02
phone. The port is mostly based on existing s3c2xx0 platform already
supported by NetBSD. One of the sub-components of S3C2440 SOC not
supported by the existing port is iic controller. GTA02 has at least 3
components attached to this bus so it's a rather critical to have a
working iic bus. Writing a driver for the controller is not a very
complicated thing to do, I actually have it working already, but there
are a couple of issues I have encountered.

When reading trough the sources of some existing iic bus drivers I've
noticed that all of them transfer the data synchronously. In most cases
data is being written to the transmitter, start condition is generated,
some loop is entered in which the driver actively waits for hardware
event indicating either acknowledge or transmit failure. The loop
usually has a limited number of iterations to handle transmit timeouts.
This whole scheme seems fine if you consider most of the i2c device
drivers NetBSD currently supports (eproms, rtcs). One of the components
of GTA02 is a slightly more complicated piece of silicon - NXP/Philips
PCF50633 [1]. Servicing this device in some circumstances might require
several i2c transactions per second. For such workloads it's probably
better idea to use interrupt based transfers. The question is how to
implement support for such transfers using current iic driver interface?

My current approach uses standard NetBSD iic interface (iic_acquire_bus,
iic_exec, iic_release_bus). The only difference is that I've added
additional flag I2C_F_INTR. When I2C_F_POLL is set the driver works in a
way described above. When I2C_F_POLL is set the underlying driver:
1. Populates a work queue with simple operations describing iic
transactions that have to be performed.
2. Acquires a spin mutex (IPL SERIAL priority, same as interrupt handler
for the driver).
3. Initiates the transfer by filling tx register with slave address,
enabling interrupts and generating start condition.
4. Starts waiting on a condition variable, cv_timedwait (mutex from step 2).

Now all work is being done by the interrupt handler. Single run of this
handler retrieves new task from the work queue (step 1), sets up the
hardware to perform it and exits. The hardware is set in a way which
triggers another interrupt when transfer has been finished or an error
occurred. After all the tasks have been performed the interrupt handler
disables interrupts, signals the condition variable and exits. Then it's
just a matter of retrieving the results of all the iic transactions
performed and returning them to the caller of iic_exec.

This scheme works, but:
1. I'm not exactly sure if condition variable is the best
synchronization mechanism for this particular use case. I don't know all
the facilities NetBSD has to offer (I'm pretty new to NetBSD kernel
programming). Is there something else that would be better for this
particular job?
2. Condition variables (according to manual pages) put some constrains
on where they can be used. Those will obviously also apply to iic_exec
calls with with I2C_F_INTR flag. I feel that at least some of those
constrains already apply to iic_exec interface. (I wouldn't call it from
any interrupt handler for example). The question is if it puts any new
constrains on the iic_exec?
3. Right now to indicate that the driver can sleep/use interrupt based
transfers I use I2C_F_INTR flag. The question is if a new flag is
necessary? I2C_F_POLL is used to tell the driver not to sleep, but
actively wait for the transfer to finish. When not specified we could
treat it as an indication that interrupt based transfer is
allowed/desired. Should a new flag be added?



Home | Main Index | Thread Index | Old Index