[U-Boot] PATCH 3/8 Multi-adapter multi-bus I2C

Signed-off-by: Sergey Kubushyn ksi@koi8.net --- diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index da6cec1..f0c1771 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,8 @@ /* + * Copyright (c) 2009 Sergey Kubushyn ksi@koi8.net + * + * Changes for multibus/multiadapter I2C support. + * * (C) Copyright 2001, 2002 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. * @@ -72,375 +76,493 @@ DECLARE_GLOBAL_DATA_PTR; #define PRINTD(fmt,args...) #endif
-#if defined(CONFIG_I2C_MULTI_BUS) -static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; -#endif /* CONFIG_I2C_MULTI_BUS */ - /*----------------------------------------------------------------------- * Local functions */ -#if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +#ifndef I2C_INIT +#define I2C_INIT do {} while(0) #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int); - -#if !defined(CONFIG_SYS_I2C_INIT_BOARD) -/*----------------------------------------------------------------------- - * Send a reset sequence consisting of 9 clocks with the data signal high - * to clock any confused device back into an idle state. Also send a - * <stop> at the end of the sequence for belts & suspenders. - */ -static void send_reset(void) -{ - I2C_SOFT_DECLARATIONS /* intentional without ';' */ - int j; - - I2C_SCL(1); - I2C_SDA(1); -#ifdef I2C_INIT - I2C_INIT; +#ifndef I2C_INIT0 +#define I2C_INIT0 do {} while(0) #endif - I2C_TRISTATE; - for(j = 0; j < 9; j++) { - I2C_SCL(0); - I2C_DELAY; - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - I2C_DELAY; - } - send_stop(); - I2C_TRISTATE; -} +#ifndef I2C_INIT1 +#define I2C_INIT1 do {} while(0) +#endif +#ifndef I2C_INIT2 +#define I2C_INIT2 do {} while(0) #endif +#ifndef I2C_INIT3 +#define I2C_INIT3 do {} while(0) +#endif +
/*----------------------------------------------------------------------- * START: High -> Low on SDA while SCL is High */ -static void send_start(void) -{ - I2C_SOFT_DECLARATIONS /* intentional without ';' */ - - I2C_DELAY; - I2C_SDA(1); - I2C_ACTIVE; - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - I2C_SDA(0); - I2C_DELAY; +#define I2C_SOFT_SEND_START(n) \ +static void send_start##n(void) \ +{ \ + I2C_SOFT_DECLARATIONS##n \ + I2C_DELAY##n; \ + I2C_SDA##n(1); \ + I2C_ACTIVE##n; \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + I2C_SDA##n(0); \ + I2C_DELAY##n; \ }
+ /*----------------------------------------------------------------------- * STOP: Low -> High on SDA while SCL is High */ -static void send_stop(void) -{ - I2C_SOFT_DECLARATIONS /* intentional without ';' */ - - I2C_SCL(0); - I2C_DELAY; - I2C_SDA(0); - I2C_ACTIVE; - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - I2C_SDA(1); - I2C_DELAY; - I2C_TRISTATE; +#define I2C_SOFT_SEND_STOP(n) \ +static void send_stop##n(void) \ +{ \ + I2C_SOFT_DECLARATIONS##n \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_SDA##n(0); \ + I2C_ACTIVE##n; \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + I2C_SDA##n(1); \ + I2C_DELAY##n; \ + I2C_TRISTATE##n; \ }
/*----------------------------------------------------------------------- * ack should be I2C_ACK or I2C_NOACK */ -static void send_ack(int ack) -{ - I2C_SOFT_DECLARATIONS /* intentional without ';' */ - - I2C_SCL(0); - I2C_DELAY; - I2C_ACTIVE; - I2C_SDA(ack); - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - I2C_DELAY; - I2C_SCL(0); - I2C_DELAY; +#define I2C_SOFT_SEND_ACK(n) \ +static void send_ack##n(int ack) \ +{ \ + I2C_SOFT_DECLARATIONS##n \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_ACTIVE##n; \ + I2C_SDA##n(ack); \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + I2C_DELAY##n; \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ }
/*----------------------------------------------------------------------- - * Send 8 bits and look for an acknowledgement. - */ -static int write_byte(uchar data) -{ - I2C_SOFT_DECLARATIONS /* intentional without ';' */ - int j; - int nack; - - I2C_ACTIVE; - for(j = 0; j < 8; j++) { - I2C_SCL(0); - I2C_DELAY; - I2C_SDA(data & 0x80); - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - I2C_DELAY; - - data <<= 1; - } - - /* - * Look for an <ACK>(negative logic) and return it. - */ - I2C_SCL(0); - I2C_DELAY; - I2C_SDA(1); - I2C_TRISTATE; - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - I2C_DELAY; - nack = I2C_READ; - I2C_SCL(0); - I2C_DELAY; - I2C_ACTIVE; - - return(nack); /* not a nack is an ack */ -} - -#if defined(CONFIG_I2C_MULTI_BUS) -/* - * Functions for multiple I2C bus handling + * Send a reset sequence consisting of 9 clocks with the data signal high + * to clock any confused device back into an idle state. Also send a + * <stop> at the end of the sequence for belts & suspenders. */ -unsigned int i2c_get_bus_num(void) -{ - return i2c_bus_num; +#define I2C_SOFT_SEND_RESET(n) \ +static void send_reset##n(void) \ +{ \ + I2C_SOFT_DECLARATIONS##n \ + int j; \ + I2C_SCL##n(1); \ + I2C_SDA##n(1); \ + I2C_INIT##n; \ + I2C_TRISTATE##n; \ + for(j = 0; j < 9; j++) { \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + I2C_DELAY##n; \ + } \ + send_stop##n(); \ + I2C_TRISTATE##n; \ }
-int i2c_set_bus_num(unsigned int bus) -{ -#if defined(CONFIG_I2C_MUX) - if (bus < CONFIG_SYS_MAX_I2C_BUS) { - i2c_bus_num = bus; - } else { - int ret; - - ret = i2x_mux_select_mux(bus); - if (ret == 0) - i2c_bus_num = bus; - else - return ret; - } -#else - if (bus >= CONFIG_SYS_MAX_I2C_BUS) - return -1; - i2c_bus_num = bus; -#endif - return 0; -}
-/* TODO: add 100/400k switching */ -unsigned int i2c_get_bus_speed(void) -{ - return CONFIG_SYS_I2C_SPEED; +/*----------------------------------------------------------------------- + * Send 8 bits and look for an acknowledgement. + */ +#define I2C_SOFT_WRITE_BYTE(n) \ +static int write_byte##n(uchar data) \ +{ \ + I2C_SOFT_DECLARATIONS##n \ + int j; \ + int nack; \ + I2C_ACTIVE##n; \ + for(j = 0; j < 8; j++) { \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_SDA##n(data & 0x80); \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + I2C_DELAY##n; \ + data <<= 1; \ + } \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_SDA##n(1); \ + I2C_TRISTATE##n; \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + I2C_DELAY##n; \ + nack = I2C_READ##n; \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_ACTIVE##n; \ + return(nack); \ }
-int i2c_set_bus_speed(unsigned int speed) -{ - if (speed != CONFIG_SYS_I2C_SPEED) - return -1; - - return 0; -} -#endif
/*----------------------------------------------------------------------- * if ack == I2C_ACK, ACK the byte so can continue reading, else * send I2C_NOACK to end the read. */ -static uchar read_byte(int ack) -{ - I2C_SOFT_DECLARATIONS /* intentional without ';' */ - int data; - int j; - - /* - * Read 8 bits, MSB first. - */ - I2C_TRISTATE; - I2C_SDA(1); - data = 0; - for(j = 0; j < 8; j++) { - I2C_SCL(0); - I2C_DELAY; - I2C_SCL(1); - I2C_DELAY; - data <<= 1; - data |= I2C_READ; - I2C_DELAY; - } - send_ack(ack); - - return(data); +#define I2C_SOFT_READ_BYTE(n) \ +static uchar read_byte##n(int ack) \ +{ \ + I2C_SOFT_DECLARATIONS##n \ + int data; \ + int j; \ + I2C_TRISTATE##n; \ + I2C_SDA##n(1); \ + data = 0; \ + for(j = 0; j < 8; j++) { \ + I2C_SCL##n(0); \ + I2C_DELAY##n; \ + I2C_SCL##n(1); \ + I2C_DELAY##n; \ + data <<= 1; \ + data |= I2C_READ##n; \ + I2C_DELAY##n; \ + } \ + send_ack##n(ack); \ + return(data); \ }
-/*=====================================================================*/ -/* Public Functions */ -/*=====================================================================*/
-/*----------------------------------------------------------------------- +/*============================================================*/ +/* I2C Ops */ +/*============================================================*/ + +/*-------------------------------------------------------------- * Initialization */ -void i2c_init (int speed, int slaveaddr) -{ -#if defined(CONFIG_SYS_I2C_INIT_BOARD) - /* call board specific i2c bus reset routine before accessing the */ - /* environment, which might be in a chip on that bus. For details */ - /* about this problem see doc/I2C_Edge_Conditions. */ - i2c_init_board(); + +#ifdef CONFIG_SYS_I2C_INIT_BOARD +#define I2C_SOFT_INIT_ADAPTER(n) \ +static void soft_i2c_init##n(int speed, int slaveaddr) \ +{ \ + /* call board specific i2c bus reset routine before accessing the */ \ + /* environment, which might be in a chip on that bus. For details */ \ + /* about this problem see doc/I2C_Edge_Conditions. */ \ + SOFT_I2C_INIT_BOARD; \ +} #else - /* - * WARNING: Do NOT save speed in a static variable: if the - * I2C routines are called before RAM is initialized (to read - * the DIMM SPD, for instance), RAM won't be usable and your - * system will crash. - */ - send_reset (); -#endif +#define I2C_SOFT_INIT_ADAPTER(n) \ +static void soft_i2c_init##n(int speed, int slaveaddr) \ +{ \ + /* WARNING: Do NOT save speed in a static variable: if the */ \ + /* I2C routines are called before RAM is initialized (to read */ \ + /* the DIMM SPD, for instance), RAM won't be usable and your */ \ + /* system will crash. */ \ + send_reset##n(); \ } +#endif +
/*----------------------------------------------------------------------- * Probe to see if a chip is present. Also good for checking for the * completion of EEPROM writes since the chip stops responding until * the write completes (typically 10mSec). */ -int i2c_probe(uchar addr) -{ - int rc; - - /* - * perform 1 byte write transaction with just address byte - * (fake write) - */ - send_start(); - rc = write_byte ((addr << 1) | 0); - send_stop(); - - return (rc ? 1 : 0); +#define I2C_SOFT_PROBE(n) \ +static int soft_i2c_probe##n(uchar addr) \ +{ \ + int rc; \ + /* perform 1 byte write transaction with just address byte */ \ + /* (fake write) */ \ + send_start##n(); \ + rc = write_byte##n((addr << 1) | 0); \ + send_stop##n(); \ + return (rc ? 1 : 0); \ }
+ /*----------------------------------------------------------------------- * Read bytes */ -int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) -{ - int shift; - PRINTD("i2c_read: chip %02X addr %02X alen %d buffer %p len %d\n", - chip, addr, alen, buffer, len); +#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START +#define RRSS_STOP 0 +#else +#define RRSS_STOP 1 +#endif
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW - /* - * EEPROM chips that implement "address overflow" are ones - * like Catalyst 24WC04/08/16 which has 9/10/11 bits of - * address and the extra bits end up in the "chip address" - * bit slots. This makes a 24WC08 (1Kbyte) chip look like - * four 256 byte chips. - * - * Note that we consider the length of the address field to - * still be one byte because the extra address bits are - * hidden in the chip address. - */ - chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); - - PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n", - chip, addr); -#endif +#define DO_EEAD_OVF chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); \ + PRINTD("%s: fix addr_overflow: chip %02X addr %02X\n", \ + __FUNCTION__, chip, addr);
- /* - * Do the addressing portion of a write cycle to set the - * chip's address pointer. If the address length is zero, - * don't do the normal write cycle to set the address pointer, - * there is no address pointer in this chip. - */ - send_start(); - if(alen > 0) { - if(write_byte(chip << 1)) { /* write cycle */ - send_stop(); - PRINTD("i2c_read, no chip responded %02X\n", chip); - return(1); - } - shift = (alen-1) * 8; - while(alen-- > 0) { - if(write_byte(addr >> shift)) { - PRINTD("i2c_read, address not <ACK>ed\n"); - return(1); - } - shift -= 8; - } - - /* Some I2C chips need a stop/start sequence here, - * other chips don't work with a full stop and need - * only a start. Default behaviour is to send the - * stop/start sequence. - */ -#ifdef CONFIG_SOFT_I2C_READ_REPEATED_START - send_start(); #else - send_stop(); - send_start(); +#define DO_EEAD_OVF #endif - } - /* - * Send the chip address again, this time for a read cycle. - * Then read the data. On the last byte, we do a NACK instead - * of an ACK(len == 0) to terminate the read. - */ - write_byte((chip << 1) | 1); /* read cycle */ - while(len-- > 0) { - *buffer++ = read_byte(len == 0); - } - send_stop(); - return(0); + +#define I2C_SOFT_READ(n) \ +static int soft_i2c_read##n(uchar chip, uint addr, int alen, uchar *buffer, int len) \ +{ \ + int shift; \ + PRINTD("%s: chip %02X addr %02X alen %d buffer %p len %d\n", \ + __FUNCTION__, chip, addr, alen, buffer, len); \ + /* EEPROM chips that implement "address overflow" are ones */ \ + /* like Catalyst 24WC04/08/16 which has 9/10/11 bits of */ \ + /* address and the extra bits end up in the "chip address" */ \ + /* bit slots. This makes a 24WC08 (1Kbyte) chip look like */ \ + /* four 256 byte chips. */ \ + /* */ \ + /* Note that we consider the length of the address field to */ \ + /* still be one byte because the extra address bits are */ \ + /* hidden in the chip address. */ \ + DO_EEAD_OVF \ + /* Do the addressing portion of a write cycle to set the */ \ + /* chip's address pointer. If the address length is zero, */ \ + /* don't do the normal write cycle to set the address pointer, */ \ + /* there is no address pointer in this chip. */ \ + send_start##n(); \ + if(alen > 0) { \ + if(write_byte##n(chip << 1)) { /* write cycle */ \ + send_stop##n(); \ + PRINTD("%s, no chip responded %02X\n", __FUNCTION__, chip); \ + return(1); \ + } \ + shift = (alen-1) * 8; \ + while(alen-- > 0) { \ + if(write_byte##n(addr >> shift)) { \ + PRINTD("%s, address not <ACK>ed\n", __FUNCTION__); \ + return(1); \ + } \ + shift -= 8; \ + } \ + if(RRSS_STOP) { \ + send_stop##n(); /* reportedly some chips need a full stop */ \ + } \ + send_start##n(); \ + } \ + /* Send the chip address again, this time for a read cycle. */ \ + /* Then read the data. On the last byte, we do a NACK instead */ \ + /* of an ACK(len == 0) to terminate the read. */ \ + write_byte##n((chip << 1) | 1); /* read cycle */ \ + while(len-- > 0) { \ + *buffer++ = read_byte##n(len == 0); \ + } \ + send_stop##n(); \ + return(0); \ }
+ /*----------------------------------------------------------------------- * Write bytes */ -int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) -{ - int shift, failures = 0; - - PRINTD("i2c_write: chip %02X addr %02X alen %d buffer %p len %d\n", - chip, addr, alen, buffer, len); - - send_start(); - if(write_byte(chip << 1)) { /* write cycle */ - send_stop(); - PRINTD("i2c_write, no chip responded %02X\n", chip); - return(1); - } - shift = (alen-1) * 8; - while(alen-- > 0) { - if(write_byte(addr >> shift)) { - PRINTD("i2c_write, address not <ACK>ed\n"); - return(1); - } - shift -= 8; - } - - while(len-- > 0) { - if(write_byte(*buffer++)) { - failures++; - } - } - send_stop(); - return(failures); +#define I2C_SOFT_WRITE(n) \ +static int soft_i2c_write##n(uchar chip, uint addr, int alen, uchar *buffer, int len) \ +{ \ + int shift, failures = 0; \ + PRINTD("%s: chip %02X addr %02X alen %d buffer %p len %d\n", \ + __FUNCTION__, chip, addr, alen, buffer, len); \ + send_start##n(); \ + if(write_byte##n(chip << 1)) { /* write cycle */ \ + send_stop##n(); \ + PRINTD("%s, no chip responded %02X\n", __FUNCTION__, chip); \ + return(1); \ + } \ + shift = (alen-1) * 8; \ + while(alen-- > 0) { \ + if(write_byte##n(addr >> shift)) { \ + PRINTD("%s, address not <ACK>ed\n", __FUNCTION__); \ + return(1); \ + } \ + shift -= 8; \ + } \ + while(len-- > 0) { \ + if(write_byte##n(*buffer++)) { \ + failures++; \ + } \ + } \ + send_stop##n(); \ + return(failures); \ +} + + +#define I2C_SOFT_GET_BUS_SPEED(n) \ +static unsigned int soft_i2c_get_bus_speed##n(void) \ +{ \ + return CONFIG_SYS_SOFT_I2C##n##_SPEED; \ +} + + +#define I2C_SOFT_SET_BUS_SPEED(n) \ +static unsigned int soft_i2c_set_bus_speed##n(unsigned int speed) \ +{ \ + if (speed != CONFIG_SYS_SOFT_I2C##n##_SPEED) \ + return -1; \ + return 0; \ } + + +/*============================================================*/ +/* Here comes the real stuff */ +/*============================================================*/ +#if defined(I2C_SOFT_DECLARATIONS) +I2C_SOFT_SEND_START() +I2C_SOFT_SEND_STOP() +I2C_SOFT_SEND_ACK() +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET() +#endif +I2C_SOFT_WRITE_BYTE() +I2C_SOFT_READ_BYTE() +I2C_SOFT_INIT_ADAPTER() +I2C_SOFT_PROBE() +I2C_SOFT_READ() +I2C_SOFT_WRITE() +I2C_SOFT_GET_BUS_SPEED() +I2C_SOFT_SET_BUS_SPEED() +#elif defined(I2C_SOFT_DECLARATIONS0) +I2C_SOFT_SEND_START(0) +I2C_SOFT_SEND_STOP(0) +I2C_SOFT_SEND_ACK(0) +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET(0) +#endif +I2C_SOFT_WRITE_BYTE(0) +I2C_SOFT_READ_BYTE(0) +I2C_SOFT_INIT_ADAPTER(0) +I2C_SOFT_PROBE(0) +I2C_SOFT_READ(0) +I2C_SOFT_WRITE(0) +I2C_SOFT_GET_BUS_SPEED(0) +I2C_SOFT_SET_BUS_SPEED(0) +#endif + +#if defined(I2C_SOFT_DECLARATIONS1) +I2C_SOFT_SEND_START(1) +I2C_SOFT_SEND_STOP(1) +I2C_SOFT_SEND_ACK(1) +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET(1) +#endif +I2C_SOFT_WRITE_BYTE(1) +I2C_SOFT_READ_BYTE(1) +I2C_SOFT_INIT_ADAPTER(1) +I2C_SOFT_PROBE(1) +I2C_SOFT_READ(1) +I2C_SOFT_WRITE(1) +I2C_SOFT_GET_BUS_SPEED(1) +I2C_SOFT_SET_BUS_SPEED(1) +#endif + +#if defined(I2C_SOFT_DECLARATIONS2) +I2C_SOFT_SEND_START(2) +I2C_SOFT_SEND_STOP(2) +I2C_SOFT_SEND_ACK(2) +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET(2) +#endif +I2C_SOFT_WRITE_BYTE(2) +I2C_SOFT_READ_BYTE(2) +I2C_SOFT_INIT_ADAPTER(2) +I2C_SOFT_PROBE(2) +I2C_SOFT_READ(2) +I2C_SOFT_WRITE(2) +I2C_SOFT_GET_BUS_SPEED(2) +I2C_SOFT_SET_BUS_SPEED(2) +#endif + +#if defined(I2C_SOFT_DECLARATIONS3) +I2C_SOFT_SEND_START(3) +I2C_SOFT_SEND_STOP(3) +I2C_SOFT_SEND_ACK(3) +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET(3) +#endif +I2C_SOFT_WRITE_BYTE(3) +I2C_SOFT_READ_BYTE(3) +I2C_SOFT_INIT_ADAPTER(3) +I2C_SOFT_PROBE(3) +I2C_SOFT_READ(3) +I2C_SOFT_WRITE(3) +I2C_SOFT_GET_BUS_SPEED(3) +I2C_SOFT_SET_BUS_SPEED(3) +#endif + +i2c_adap_t soft_i2c_adap[] = { + { +#if defined(I2C_SOFT_DECLARATIONS) + .init = soft_i2c_init, + .probe = soft_i2c_probe, + .read = soft_i2c_read, + .write = soft_i2c_write, + .set_bus_speed = soft_i2c_set_bus_speed, + .get_bus_speed = soft_i2c_get_bus_speed, + .speed = CONFIG_SYS_SOFT_I2C_SPEED, + .slaveaddr = CONFIG_SYS_SOFT_I2C_SLAVE, + .init_done = 0, + .name = "soft-i2c" +#elif defined(I2C_SOFT_DECLARATIONS0) + .init = soft_i2c_init0, + .probe = soft_i2c_probe0, + .read = soft_i2c_read0, + .write = soft_i2c_write0, + .set_bus_speed = soft_i2c_set_bus_speed0, + .get_bus_speed = soft_i2c_get_bus_speed0, + .speed = CONFIG_SYS_SOFT_I2C0_SPEED, + .slaveaddr = CONFIG_SYS_SOFT_I2C0_SLAVE, + .init_done = 0, + .name = "soft-i2c#0" +#endif + }, +#if defined(I2C_SOFT_DECLARATIONS1) + { + .init = soft_i2c_init1, + .probe = soft_i2c_probe1, + .read = soft_i2c_read1, + .write = soft_i2c_write1, + .set_bus_speed = soft_i2c_set_bus_speed1, + .get_bus_speed = soft_i2c_get_bus_speed1, + .speed = CONFIG_SYS_SOFT_I2C1_SPEED, + .slaveaddr = CONFIG_SYS_SOFT_I2C1_SLAVE, + .init_done = 0, + .name = "soft-i2c#1" + }, +#endif +#if defined(I2C_SOFT_DECLARATIONS2) + { + .init = soft_i2c_init2, + .probe = soft_i2c_probe2, + .read = soft_i2c_read2, + .write = soft_i2c_write2, + .set_bus_speed = soft_i2c_set_bus_speed2, + .get_bus_speed = soft_i2c_get_bus_speed2, + .speed = CONFIG_SYS_SOFT_I2C2_SPEED, + .slaveaddr = CONFIG_SYS_SOFT_I2C2_SLAVE, + .init_done = 0, + .name = "soft-i2c#2" + }, +#endif +#if defined(I2C_SOFT_DECLARATIONS3) + { + .init = soft_i2c_init3, + .probe = soft_i2c_probe3, + .read = soft_i2c_read3, + .write = soft_i2c_write3, + .set_bus_speed = soft_i2c_set_bus_speed3, + .get_bus_speed = soft_i2c_get_bus_speed3, + .speed = CONFIG_SYS_SOFT_I2C3_SPEED, + .slaveaddr = CONFIG_SYS_SOFT_I2C3_SLAVE, + .init_done = 0, + .name = "soft-i2c#3" + }, +#endif +};

Hello ksi,
ksi@koi8.net wrote:
Signed-off-by: Sergey Kubushyn ksi@koi8.net
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index da6cec1..f0c1771 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,8 @@ /*
- Copyright (c) 2009 Sergey Kubushyn ksi@koi8.net
- Changes for multibus/multiadapter I2C support.
- (C) Copyright 2001, 2002
- Wolfgang Denk, DENX Software Engineering, wd@denx.de.
@@ -72,375 +76,493 @@ DECLARE_GLOBAL_DATA_PTR; #define PRINTD(fmt,args...) #endif
-#if defined(CONFIG_I2C_MULTI_BUS) -static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; -#endif /* CONFIG_I2C_MULTI_BUS */
/*-----------------------------------------------------------------------
- Local functions
*/ -#if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +#ifndef I2C_INIT +#define I2C_INIT do {} while(0) #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
-#if !defined(CONFIG_SYS_I2C_INIT_BOARD) -/*-----------------------------------------------------------------------
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
- */
-static void send_reset(void) -{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
- I2C_SCL(1);
- I2C_SDA(1);
-#ifdef I2C_INIT
- I2C_INIT;
+#ifndef I2C_INIT0 +#define I2C_INIT0 do {} while(0) #endif
- I2C_TRISTATE;
- for(j = 0; j < 9; j++) {
I2C_SCL(0);
I2C_DELAY;
I2C_DELAY;
I2C_SCL(1);
I2C_DELAY;
I2C_DELAY;
- }
- send_stop();
- I2C_TRISTATE;
-} +#ifndef I2C_INIT1 +#define I2C_INIT1 do {} while(0) +#endif +#ifndef I2C_INIT2 +#define I2C_INIT2 do {} while(0) #endif +#ifndef I2C_INIT3 +#define I2C_INIT3 do {} while(0) +#endif
/*-----------------------------------------------------------------------
- START: High -> Low on SDA while SCL is High
*/ -static void send_start(void) -{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- I2C_DELAY;
- I2C_SDA(1);
- I2C_ACTIVE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_SDA(0);
- I2C_DELAY;
+#define I2C_SOFT_SEND_START(n) \ +static void send_start##n(void) \ +{ \
- I2C_SOFT_DECLARATIONS##n \
- I2C_DELAY##n; \
- I2C_SDA##n(1); \
- I2C_ACTIVE##n; \
- I2C_DELAY##n; \
- I2C_SCL##n(1); \
- I2C_DELAY##n; \
- I2C_SDA##n(0); \
- I2C_DELAY##n; \
}
[...]
- PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n",
chip, addr);
-#endif +#define DO_EEAD_OVF chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); \
Line too long.
PRINTD("%s: fix addr_overflow: chip %02X addr %02X\n", \
__FUNCTION__, chip, addr);
- /*
* Do the addressing portion of a write cycle to set the
* chip's address pointer. If the address length is zero,
* don't do the normal write cycle to set the address pointer,
* there is no address pointer in this chip.
[...]
+#if defined(I2C_SOFT_DECLARATIONS3) +I2C_SOFT_SEND_START(3) +I2C_SOFT_SEND_STOP(3) +I2C_SOFT_SEND_ACK(3) +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET(3) +#endif +I2C_SOFT_WRITE_BYTE(3) +I2C_SOFT_READ_BYTE(3) +I2C_SOFT_INIT_ADAPTER(3) +I2C_SOFT_PROBE(3) +I2C_SOFT_READ(3) +I2C_SOFT_WRITE(3) +I2C_SOFT_GET_BUS_SPEED(3) +I2C_SOFT_SET_BUS_SPEED(3) +#endif
Hmm... are this lots of defines really necessary? Couldn't we add something like
int hw_adapnr; /* hardware adapter number */
to the i2c_adap_t struct, and have an pointer (cur_i2c_adap?) to the current used i2c_adap_t? Then you have where you need it, your hw_adapnr and need not all of this defines.
For example you need in the config for MPC8548CDS.h just this define:
#define I2C_SDA(bit) (printf("HW adap: %d sda: %d", cur_i2c_adap->hw_adapnr, bit))
and not I2C_SDA(bit) and I2C_SDA1(bit)
+i2c_adap_t soft_i2c_adap[] = {
- {
+#if defined(I2C_SOFT_DECLARATIONS)
.init = soft_i2c_init,
.probe = soft_i2c_probe,
.read = soft_i2c_read,
.write = soft_i2c_write,
.set_bus_speed = soft_i2c_set_bus_speed,
.get_bus_speed = soft_i2c_get_bus_speed,
.speed = CONFIG_SYS_SOFT_I2C_SPEED,
.slaveaddr = CONFIG_SYS_SOFT_I2C_SLAVE,
.init_done = 0,
.name = "soft-i2c"
+#elif defined(I2C_SOFT_DECLARATIONS0)
.init = soft_i2c_init0,
.probe = soft_i2c_probe0,
.read = soft_i2c_read0,
.write = soft_i2c_write0,
.set_bus_speed = soft_i2c_set_bus_speed0,
.get_bus_speed = soft_i2c_get_bus_speed0,
.speed = CONFIG_SYS_SOFT_I2C0_SPEED,
.slaveaddr = CONFIG_SYS_SOFT_I2C0_SLAVE,
.init_done = 0,
.name = "soft-i2c#0"
+#endif
- },
+#if defined(I2C_SOFT_DECLARATIONS1)
- {
.init = soft_i2c_init1,
.probe = soft_i2c_probe1,
.read = soft_i2c_read1,
.write = soft_i2c_write1,
.set_bus_speed = soft_i2c_set_bus_speed1,
.get_bus_speed = soft_i2c_get_bus_speed1,
.speed = CONFIG_SYS_SOFT_I2C1_SPEED,
.slaveaddr = CONFIG_SYS_SOFT_I2C1_SLAVE,
.init_done = 0,
.name = "soft-i2c#1"
- },
+#endif +#if defined(I2C_SOFT_DECLARATIONS2)
- {
.init = soft_i2c_init2,
.probe = soft_i2c_probe2,
.read = soft_i2c_read2,
.write = soft_i2c_write2,
.set_bus_speed = soft_i2c_set_bus_speed2,
.get_bus_speed = soft_i2c_get_bus_speed2,
.speed = CONFIG_SYS_SOFT_I2C2_SPEED,
.slaveaddr = CONFIG_SYS_SOFT_I2C2_SLAVE,
.init_done = 0,
.name = "soft-i2c#2"
- },
+#endif +#if defined(I2C_SOFT_DECLARATIONS3)
- {
.init = soft_i2c_init3,
.probe = soft_i2c_probe3,
.read = soft_i2c_read3,
.write = soft_i2c_write3,
.set_bus_speed = soft_i2c_set_bus_speed3,
.get_bus_speed = soft_i2c_get_bus_speed3,
.speed = CONFIG_SYS_SOFT_I2C3_SPEED,
.slaveaddr = CONFIG_SYS_SOFT_I2C3_SLAVE,
.init_done = 0,
.name = "soft-i2c#3"
- },
+#endif +};
bye Heiko

On Mon, 9 Feb 2009, Heiko Schocher wrote:
Hello ksi,
ksi@koi8.net wrote:
Signed-off-by: Sergey Kubushyn ksi@koi8.net
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index da6cec1..f0c1771 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,8 @@ /*
- Copyright (c) 2009 Sergey Kubushyn ksi@koi8.net
- Changes for multibus/multiadapter I2C support.
- (C) Copyright 2001, 2002
- Wolfgang Denk, DENX Software Engineering, wd@denx.de.
@@ -72,375 +76,493 @@ DECLARE_GLOBAL_DATA_PTR; #define PRINTD(fmt,args...) #endif
-#if defined(CONFIG_I2C_MULTI_BUS) -static unsigned int i2c_bus_num __attribute__ ((section (".data"))) = 0; -#endif /* CONFIG_I2C_MULTI_BUS */
/*-----------------------------------------------------------------------
- Local functions
*/ -#if !defined(CONFIG_SYS_I2C_INIT_BOARD) -static void send_reset (void); +#ifndef I2C_INIT +#define I2C_INIT do {} while(0) #endif -static void send_start (void); -static void send_stop (void); -static void send_ack (int); -static int write_byte (uchar byte); -static uchar read_byte (int);
-#if !defined(CONFIG_SYS_I2C_INIT_BOARD) -/*-----------------------------------------------------------------------
- Send a reset sequence consisting of 9 clocks with the data signal high
- to clock any confused device back into an idle state. Also send a
- <stop> at the end of the sequence for belts & suspenders.
- */
-static void send_reset(void) -{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- int j;
- I2C_SCL(1);
- I2C_SDA(1);
-#ifdef I2C_INIT
- I2C_INIT;
+#ifndef I2C_INIT0 +#define I2C_INIT0 do {} while(0) #endif
- I2C_TRISTATE;
- for(j = 0; j < 9; j++) {
I2C_SCL(0);
I2C_DELAY;
I2C_DELAY;
I2C_SCL(1);
I2C_DELAY;
I2C_DELAY;
- }
- send_stop();
- I2C_TRISTATE;
-} +#ifndef I2C_INIT1 +#define I2C_INIT1 do {} while(0) +#endif +#ifndef I2C_INIT2 +#define I2C_INIT2 do {} while(0) #endif +#ifndef I2C_INIT3 +#define I2C_INIT3 do {} while(0) +#endif
/*-----------------------------------------------------------------------
- START: High -> Low on SDA while SCL is High
*/ -static void send_start(void) -{
- I2C_SOFT_DECLARATIONS /* intentional without ';' */
- I2C_DELAY;
- I2C_SDA(1);
- I2C_ACTIVE;
- I2C_DELAY;
- I2C_SCL(1);
- I2C_DELAY;
- I2C_SDA(0);
- I2C_DELAY;
+#define I2C_SOFT_SEND_START(n) \ +static void send_start##n(void) \ +{ \
- I2C_SOFT_DECLARATIONS##n \
- I2C_DELAY##n; \
- I2C_SDA##n(1); \
- I2C_ACTIVE##n; \
- I2C_DELAY##n; \
- I2C_SCL##n(1); \
- I2C_DELAY##n; \
- I2C_SDA##n(0); \
- I2C_DELAY##n; \
}
[...]
- PRINTD("i2c_read: fix addr_overflow: chip %02X addr %02X\n",
chip, addr);
-#endif +#define DO_EEAD_OVF chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); \
Line too long.
No problems, fixed.
PRINTD("%s: fix addr_overflow: chip %02X addr %02X\n", \
__FUNCTION__, chip, addr);
- /*
* Do the addressing portion of a write cycle to set the
* chip's address pointer. If the address length is zero,
* don't do the normal write cycle to set the address pointer,
* there is no address pointer in this chip.
[...]
+#if defined(I2C_SOFT_DECLARATIONS3) +I2C_SOFT_SEND_START(3) +I2C_SOFT_SEND_STOP(3) +I2C_SOFT_SEND_ACK(3) +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +I2C_SOFT_SEND_RESET(3) +#endif +I2C_SOFT_WRITE_BYTE(3) +I2C_SOFT_READ_BYTE(3) +I2C_SOFT_INIT_ADAPTER(3) +I2C_SOFT_PROBE(3) +I2C_SOFT_READ(3) +I2C_SOFT_WRITE(3) +I2C_SOFT_GET_BUS_SPEED(3) +I2C_SOFT_SET_BUS_SPEED(3) +#endif
Hmm... are this lots of defines really necessary? Couldn't we add something like
int hw_adapnr; /* hardware adapter number */
to the i2c_adap_t struct, and have an pointer (cur_i2c_adap?) to the current used i2c_adap_t? Then you have where you need it, your hw_adapnr and need not all of this defines.
For example you need in the config for MPC8548CDS.h just this define:
#define I2C_SDA(bit) (printf("HW adap: %d sda: %d", cur_i2c_adap->hw_adapnr, bit))
and not I2C_SDA(bit) and I2C_SDA1(bit)
Eh, those are _NOT_ defines, those are _INSTANCES_.
First of all, you need real functions to make pointers to them at compile time.
Second, SOFT_I2C is special; it is _NOT_ possible to make generic paratemerized functions. Each, e.g., I2C_SDA is different and those config file defines are _MACROS_, not defines. One can have one I2C_SOFT adapter made of a couple of on-SoC GPIOs and another one constructed of, e.g., unused GPIOs from PCI bridge or whatever. That means that _ALL_ those I2C_* macros will be totally different for those 2 adapters thus making 2 sets of access functions that have absolutely nothing in common. You can not parameterize this...
I agree that those 4 #ifdef'ed instances are not the prettiest and 4 is an arbitrary number but I'm not THAT good in CPP trickery to come up with a generic template that would be good for arbitrary number of instances if it can be done at all...
And that template allows for using existing SOFT_I2C macros in existing config files without any changes to them.
Also let's not forget that all those function sets are instantiated at _COMPILE_ time so they can be run from ROM.
I would like to hear suggestions on that from real CPP gurus. That would've made the code prettier and I would've learned new neat tricks...
--- ****************************************************************** * KSI@home KOI8 Net < > The impossible we do immediately. * * Las Vegas NV, USA < > Miracles require 24-hour notice. * ******************************************************************

Hello ksi,
ksi@koi8.net wrote:
On Mon, 9 Feb 2009, Heiko Schocher wrote:
Hello ksi,
ksi@koi8.net wrote:
Signed-off-by: Sergey Kubushyn ksi@koi8.net
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index da6cec1..f0c1771 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -1,4 +1,8 @@ /*
- Copyright (c) 2009 Sergey Kubushyn ksi@koi8.net
[...]
+#endif +I2C_SOFT_WRITE_BYTE(3) +I2C_SOFT_READ_BYTE(3) +I2C_SOFT_INIT_ADAPTER(3) +I2C_SOFT_PROBE(3) +I2C_SOFT_READ(3) +I2C_SOFT_WRITE(3) +I2C_SOFT_GET_BUS_SPEED(3) +I2C_SOFT_SET_BUS_SPEED(3) +#endif
Hmm... are this lots of defines really necessary? Couldn't we add something like
int hw_adapnr; /* hardware adapter number */
to the i2c_adap_t struct, and have an pointer (cur_i2c_adap?) to the current used i2c_adap_t? Then you have where you need it, your hw_adapnr and need not all of this defines.
For example you need in the config for MPC8548CDS.h just this define:
#define I2C_SDA(bit) (printf("HW adap: %d sda: %d", cur_i2c_adap->hw_adapnr, bit))
and not I2C_SDA(bit) and I2C_SDA1(bit)
Eh, those are _NOT_ defines, those are _INSTANCES_.
First of all, you need real functions to make pointers to them at compile time.
Obvious.
Second, SOFT_I2C is special; it is _NOT_ possible to make generic paratemerized functions. Each, e.g., I2C_SDA is different and those config file defines are _MACROS_, not defines. One can have one I2C_SOFT adapter made of a couple of on-SoC GPIOs and another one constructed of, e.g., unused GPIOs from PCI bridge or whatever. That means that _ALL_ those I2C_* macros will be totally different for those 2 adapters thus making 2 sets of access functions that have absolutely nothing in common. You can not parameterize this...
For example soft_i2c_read():
static int soft_i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) { [...] send_start(); if(alen > 0) { if(write_byte(chip << 1)) { /* write cycle */ [...] }
This is now a real function you can make a pointer for
i2c_adap_t soft_i2c_adap[] = { { .read = soft_i2c_read, } }
In soft_i2c_read() there are calls for example for send_start().
static void send_start(void) { [...] I2C_DELAY; I2C_SDA(1); I2C_ACTIVE; I2C_DELAY; }
and in this function, there are the calls for I2C_SDA(), I2C_ACTIVE,... which are look as I described. So where is the problem?
And we have not to change aprox. all lines of code from this driver!
I agree that those 4 #ifdef'ed instances are not the prettiest and 4 is an arbitrary number but I'm not THAT good in CPP trickery to come up with a generic template that would be good for arbitrary number of instances if it can be done at all...
And that template allows for using existing SOFT_I2C macros in existing config files without any changes to them.
So, I think, on my suggestion to.
Also let's not forget that all those function sets are instantiated at _COMPILE_ time so they can be run from ROM.
Why should my functions not run from ROM?
I would like to hear suggestions on that from real CPP gurus. That would've made the code prettier and I would've learned new neat tricks...
Hmm.. I am not a CPP Guru, but it should work without these "define monster".
I look for a little time to try out my suggestion.
bye Heiko
participants (2)
-
Heiko Schocher
-
ksi@koi8.net