# # $Id$ # ===================== Copyright and License ===================== For Copyright and License of the source code, see the header in libi2c.c. ========= Overview ======== This directory contains a general I2C/SPI API library. It offers a standard API to I2C or SPI based device drivers, abstracting the low level driver (dealing with the I2C/SPI controller hardware of the board) from the high-level drivers (dealing with devices connected to the I2C or SPI bus). In most cases throughout this document, i2c and spi devices are handled in a similar way. Therefore spi will not be explicitly named in every location. ========= Features ========= + supports multiple i2c and/or spi busses + supports multiple devices connected to each i2c/spi bus + handles bus and device registration to the I/O manager ========= Structure ========= This library defines a layered API to i2c and spi devices. The layering is: +----------------------------------------+ 6| Application | +----------------------------------------+ 5| RTEMS I/O Manager | +----------------------------------------+ 4|** libi2c OS adaption layer **| +----------------------------------------+ 3| high level i2c device driver | | (EEPROM, RTC, ...) | | (e.g. in c/src/libchip/i2c) | +----------------------------------------+ 2|** libi2c low level abstraction layer **| +----------------------------------------+ 1| i2c controller driver | | (in BSP) | +----------------------------------------+ This document will describe the following interfaces in separate sections: + the interface between the RTEMS I/O Manager and the libi2c OS interface (5<->4) + the interface between the libi2c OS interface and the high level i2c device driver (4<->3) + the interface between the high level i2c device driver and the libi2c low level abstraction layer (3<->2) + the interface between the libi2c low level abstraction layer and the i2c controller driver (2<->1) =================================== Differences between i2c and spi bus =================================== SPI and I2C has many similarities, but also some differences: - I2C uses inband addressing (the first bits sent select, which slave device is addressed) while SPI uses dedicated select lines to address a slave device - SPI supports combined full duplex read-write transactions while I2C either sends or receives data from a slave device - SPI supports a varity of per-slave options, which include: - number of bits per character to transfer - polarity and phase of clock wrt data - clock frequency The libi2c API defines a superset of functions to handle both flavours of serial data transmission, but care should be taken not to use features dedicated to the wrong type of serial bus. ====================== Library Initialization ====================== Before any libi2c API is used, the library must be initialized. This is achived with a call to function rtems_libi2c_initialize (). It creates a global mutex to lock internal data structures and registers the OS adaption layer to the RTEMS I/O manager. Any subsequent call to this function will be silently ignored. Typically the BSP startup code will perform this initialization. A proper place for initializing the i2c layer and populating it with busses and device drivers (see 'Bus Registration' and 'Device/Driver Registration' below) is the 'predriver_hook' where most facilities (such as malloc, libio) are already available. Note, however, that 'stdio' is not yet functional at this point and all i2c bus and device drivers should carefully avoid using stdio so that other drivers which may build on top of i2c devices may be initialized properly (this may happen just after 'predriver_hook' when stdio is still not available). E.g., drivers listed in the configuration table are initialized during this step. Note that while 'libi2c' could be initialized from the rtems configuration table like other drivers there is no easy way of populating the i2c framework with bus- and device- drivers at this point (unless a special 'i2c' configuration table describing the bus layout is implemented in the future). For the time being, we must rely on the BSP (predriver_hook) to initialize the i2c system if it is used by other drivers (e.g., the RTC driver may have to use a i2c device). =================== Bus Registration =================== Each i2c and/or spi bus available must be registerd with a call to int rtems_libi2c_register_bus (char *name, rtems_libi2c_bus_t * bus) It registers the bus to the libi2c internal data structures and creates a device node in the RTEMS filesystem with the given name. If no name is given (name==NULL), then the default name "/dev/i2c" is used instead. With the second calling parameter "rtems_libi2c_bus_t * bus" the caller passes in a set of function pointers, which define the entries into the i2c controller driver (defined in the BSP). This call returns an integer bus number, which can be used in subsequent calls to register devices attached to this bus (see below). Typically the BSP startup code will perform this registration for each bus available on the board. ========================== Device/Driver Registration ========================== Each device attached to an i2c or spi bus must be registered with a call to int rtems_libi2c_register_drv (char *name, rtems_libi2c_drv_t * drvtbl, unsigned bus, unsigned i2caddr); With this call, libi2c is informed, that: - a device is attached to the given "bus" number (which in fact is the return value received from a previous rtems_libi2c_register_bus() call) with the address "i2caddr" - the device is managed by a driver, who's entry functions are listed in "drvtbl" - the device should be registered with the given "name" in the device tree of the filesystem. The call will create a proper minor device number, which has the bus number and i2c_address encoded. This minor number is the return value of the call and is also associated with the filesystem node created for this device. Note: If you have multiple devices of the same type, you must register each of them through a separate call (with the same "drvtbl", but different name/bus/i2caddr). ==================================================================== (5<->4) RTEMS I/O Manager and the libi2c OS adaption layer IF ==================================================================== The RTEMS I/O Manager regards the libi2c OS adaption layer as a normal RTEMS Device Driver with one unique major number and a set of minor numbers, one for each bus and one for each device attached to one of the busses. Therefore the libi2c OS adaption layer provides the standard calls: static rtems_driver_address_table libi2c_io_ops = { initialization_entry: i2c_init, open_entry: i2c_open, close_entry: i2c_close, read_entry: i2c_read, write_entry: i2c_write, control_entry: i2c_ioctl }; These calls perform some parameter checking and then call the appropriate high level i2c device driver function, if available, according to the entries in the "drvtbl" passed in the rtems_libi2c_register_drv() call. There are two exceptions: when i2c_read or i2c_write is called with a minor number specifying a bus (and not a device attached to the bus), then the respective transfer is performed as a raw byte stream transfer to the bus. The main reason for the libi2c OS adaption layer is, that it dispatches the RTEMS I/O Manager calls to the proper device driver according to the minor number used. ==================================================================== libi2c OS adaption layer and the high level i2c device driver IF ==================================================================== Each high level i2c device driver provides a set of functions in the rtems_libi2c_drv_t data structure passed the libi2c when the device is registered (see "Device registration" above). These function directly match the RTEMS I/O Mangers calls "open", "close", "read", "write", "control", and they are passed the same arguments. Functions not needed may be ommited (and replaced by a NULL pointer in rtems_libi2c_drv_t). ====================================================================== high level i2c device driver and libi2c low level abstraction layer IF ====================================================================== libi2c provides a set of functions for the high level drivers. These functions are: rtems_libi2c_send_start(); rtems_libi2c_send_stop(); rtems_libi2c_send_addr(); rtems_libi2c_read_bytes(); rtems_libi2c_write_bytes(); rtems_libi2c_start_read_bytes(); rtems_libi2c_start_write_bytes(); rtems_libi2c_ioctl(); Please look into libi2c.h for the proper parameters and return codes. These functions perform the proper i2c operations when called. A typical access sequence for the I2C bus would be: rtems_libi2c_send_start(); rtems_libi2c_send_addr(); rtems_libi2c_write_bytes(); rtems_libi2c_send_stop(); Alternatively, the rtems_libi2c_write_bytes() call could be relpaced with a rtems_libi2c_read_bytes() call or a sequence of multiple calls. Note: rtems_libi2c_send_start() locks the i2c/spi bus used, so no other device can use this i2c/spi bus, until rtems_libi2c_send_stop() function is called for the same device. Special provisions for SPI devices: =================================== For SPI devices and their drivers, the libi2c interface is used slightly differently: rtems_libi2c_send_start() will lock access to the SPI bus, but has no effect on the hardware bus interface. rtems_libi2c_ioctl(...,RTEMS_LIBI2C_IOCTL_SET_TFRMODE,...) will set the transfer mode (bit rate, clock phase and polaritiy, bits per char...) according to the rtems_libi2c_tfr_mode_t structure passed in. rtems_libi2c_send_addr() will activate the proper select line to address a certain SPI device. The correspondance between an address and the select line pulled is BSP specific. rtems_libi2c_send_stop(); will deactivate the address line and unlock the bus. A typical access sequence for the SPI bus would be: rtems_libi2c_send_start(); rtems_libi2c_ioctl(...,RTEMS_LIBI2C_IOCTL_SET_TFRMODE,...); rtems_libi2c_send_addr(); rtems_libi2c_write_bytes(); rtems_libi2c_send_stop(); Alternatively, the rtems_libi2c_write_bytes() call could be relpaced with a rtems_libi2c_read_bytes() or a rtems_libi2c_ioctl(...,RTEMS_LIBI2C_IOCTL_READ_WRITE,...) call or a sequence of multiple calls. ==================================================================== libi2c low level abstraction layer and i2c controller driver IF ==================================================================== Each low level i2c/spi driver must provide a set of bus_ops functions as defined in the rtems_libi2c_bus_ops_t structure. typedef struct rtems_libi2c_bus_ops_ { /* Initialize the bus; might be called again to reset the bus driver */ rtems_status_code (*init) (rtems_libi2c_bus_t * bushdl); /* Send start condition */ rtems_status_code (*send_start) (rtems_libi2c_bus_t * bushdl); /* Send stop condition */ rtems_status_code (*send_stop) (rtems_libi2c_bus_t * bushdl); /* initiate transfer from (rw!=0) or to a device */ rtems_status_code (*send_addr) (rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw); /* read a number of bytes */ int (*read_bytes) (rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes); /* write a number of bytes */ int (*write_bytes) (rtems_libi2c_bus_t * bushdl, unsigned char *bytes, int nbytes); /* ioctl misc functions */ int (*ioctl) (rtems_libi2c_bus_t * bushdl, int cmd, void *buffer; ); } rtems_libi2c_bus_ops_t; Each of these functions performs the corresponding function to the i2c bus. Special provisions for SPI devices: =================================== For SPI busses, special behaviour is required: (*send_start) (rtems_libi2c_bus_t * bushdl) normally is an empty function. (*send_addr) (rtems_libi2c_bus_t * bushdl, uint32_t addr, int rw) will activate the SPI select line matching to addr. (*send_stop) (rtems_libi2c_bus_t * bushdl) will deactivate the SPI select line (*ioctl(...,RTEMS_LIBI2C_IOCTL_SET_TFRMODE,...) will set the transfer mode (bit rate, clock phase and polaritiy, bits per char...) according to the rtems_libi2c_tfr_mode_t structure passed in. (*ioctl(...,RTEMS_LIBI2C_IOCTL_READ_WRITE,...) will send and receive data at the same time. Note: - low-level I2C drivers normally are specific to the master device, but independent from the board hardware. So in many cases they can totally reside in libcpu or libchip. - low-level SPI drivers are mostly board independent, but the addressing is board/BSP dependent. Therefore the (*send_start), (*send_addr) and (*send_stop) functions are typically defined in the BSP. The rest of the functions can reside in libcpu or libchip.