diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/Makefile | 4 | ||||
-rw-r--r-- | drivers/i2c/adi_i2c.c | 305 | ||||
-rw-r--r-- | drivers/i2c/bfin-twi_i2c.c | 379 | ||||
-rw-r--r-- | drivers/i2c/fsl_i2c.c | 4 | ||||
-rw-r--r-- | drivers/i2c/i2c-emul-uclass.c | 14 | ||||
-rw-r--r-- | drivers/i2c/i2c-uclass.c | 466 | ||||
-rw-r--r-- | drivers/i2c/i2c_core.c | 16 | ||||
-rw-r--r-- | drivers/i2c/mxc_i2c.c | 22 | ||||
-rw-r--r-- | drivers/i2c/ppc4xx_i2c.c | 30 | ||||
-rw-r--r-- | drivers/i2c/rcar_i2c.c | 4 | ||||
-rw-r--r-- | drivers/i2c/sandbox_i2c.c | 111 | ||||
-rw-r--r-- | drivers/i2c/tegra_i2c.c | 366 |
12 files changed, 1065 insertions, 656 deletions
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index d067897244..6f3c86c038 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -4,8 +4,9 @@ # # SPDX-License-Identifier: GPL-2.0+ # +obj-$(CONFIG_DM_I2C) += i2c-uclass.o -obj-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o +obj-$(CONFIG_SYS_I2C_ADI) += adi_i2c.o obj-$(CONFIG_I2C_MV) += mv_i2c.o obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o @@ -26,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o +obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o diff --git a/drivers/i2c/adi_i2c.c b/drivers/i2c/adi_i2c.c new file mode 100644 index 0000000000..20495b1d7f --- /dev/null +++ b/drivers/i2c/adi_i2c.c @@ -0,0 +1,305 @@ +/* + * i2c.c - driver for ADI TWI/I2C + * + * Copyright (c) 2006-2014 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <i2c.h> + +#include <asm/clock.h> +#include <asm/twi.h> +#include <asm/io.h> + +static struct twi_regs *i2c_get_base(struct i2c_adapter *adap); + +/* Every register is 32bit aligned, but only 16bits in size */ +#define ureg(name) u16 name; u16 __pad_##name; +struct twi_regs { + ureg(clkdiv); + ureg(control); + ureg(slave_ctl); + ureg(slave_stat); + ureg(slave_addr); + ureg(master_ctl); + ureg(master_stat); + ureg(master_addr); + ureg(int_stat); + ureg(int_mask); + ureg(fifo_ctl); + ureg(fifo_stat); + char __pad[0x50]; + ureg(xmt_data8); + ureg(xmt_data16); + ureg(rcv_data8); + ureg(rcv_data16); +}; +#undef ureg + +#ifdef TWI_CLKDIV +#define TWI0_CLKDIV TWI_CLKDIV +# ifdef CONFIG_SYS_MAX_I2C_BUS +# undef CONFIG_SYS_MAX_I2C_BUS +# endif +#define CONFIG_SYS_MAX_I2C_BUS 1 +#endif + +/* + * The way speed is changed into duty often results in integer truncation + * with 50% duty, so we'll force rounding up to the next duty by adding 1 + * to the max. In practice this will get us a speed of something like + * 385 KHz. The other limit is easy to handle as it is only 8 bits. + */ +#define I2C_SPEED_MAX 400000 +#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) +#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) +#define I2C_DUTY_MIN 0xff /* 8 bit limited */ +#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED) +/* Note: duty is inverse of speed, so the comparisons below are correct */ +#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN +# error "The I2C hardware can only operate 20KHz - 400KHz" +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u8 flags; +#define I2C_M_COMBO 0x4 +#define I2C_M_STOP 0x2 +#define I2C_M_READ 0x1 + int len; /* msg length */ + u8 *buf; /* pointer to msg data */ + int alen; /* addr length */ + u8 *abuf; /* addr buffer */ +}; + +/* Allow msec timeout per ~byte transfer */ +#define I2C_TIMEOUT 10 + +/** + * wait_for_completion - manage the actual i2c transfer + * @msg: the i2c msg + */ +static int wait_for_completion(struct twi_regs *twi, struct i2c_msg *msg) +{ + u16 int_stat, ctl; + ulong timebase = get_timer(0); + + do { + int_stat = readw(&twi->int_stat); + + if (int_stat & XMTSERV) { + writew(XMTSERV, &twi->int_stat); + if (msg->alen) { + writew(*(msg->abuf++), &twi->xmt_data8); + --msg->alen; + } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { + writew(*(msg->buf++), &twi->xmt_data8); + --msg->len; + } else { + ctl = readw(&twi->master_ctl); + if (msg->flags & I2C_M_COMBO) + writew(ctl | RSTART | MDIR, + &twi->master_ctl); + else + writew(ctl | STOP, &twi->master_ctl); + } + } + if (int_stat & RCVSERV) { + writew(RCVSERV, &twi->int_stat); + if (msg->len) { + *(msg->buf++) = readw(&twi->rcv_data8); + --msg->len; + } else if (msg->flags & I2C_M_STOP) { + ctl = readw(&twi->master_ctl); + writew(ctl | STOP, &twi->master_ctl); + } + } + if (int_stat & MERR) { + writew(MERR, &twi->int_stat); + return msg->len; + } + if (int_stat & MCOMP) { + writew(MCOMP, &twi->int_stat); + if (msg->flags & I2C_M_COMBO && msg->len) { + ctl = readw(&twi->master_ctl); + ctl = (ctl & ~RSTART) | + (min(msg->len, 0xff) << 6) | MEN | MDIR; + writew(ctl, &twi->master_ctl); + } else + break; + } + + /* If we were able to do something, reset timeout */ + if (int_stat) + timebase = get_timer(0); + + } while (get_timer(timebase) < I2C_TIMEOUT); + + return msg->len; +} + +static int i2c_transfer(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len, uint8_t flags) +{ + struct twi_regs *twi = i2c_get_base(adap); + int ret; + u16 ctl; + uchar addr_buffer[] = { + (addr >> 0), + (addr >> 8), + (addr >> 16), + }; + struct i2c_msg msg = { + .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), + .buf = buffer, + .len = len, + .abuf = addr_buffer, + .alen = alen, + }; + + /* wait for things to settle */ + while (readw(&twi->master_stat) & BUSBUSY) + if (ctrlc()) + return 1; + + /* Set Transmit device address */ + writew(chip, &twi->master_addr); + + /* Clear the FIFO before starting things */ + writew(XMTFLUSH | RCVFLUSH, &twi->fifo_ctl); + writew(0, &twi->fifo_ctl); + + /* prime the pump */ + if (msg.alen) { + len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len; + writew(*(msg.abuf++), &twi->xmt_data8); + --msg.alen; + } else if (!(msg.flags & I2C_M_READ) && msg.len) { + writew(*(msg.buf++), &twi->xmt_data8); + --msg.len; + } + + /* clear int stat */ + writew(-1, &twi->master_stat); + writew(-1, &twi->int_stat); + writew(0, &twi->int_mask); + + /* Master enable */ + ctl = readw(&twi->master_ctl); + ctl = (ctl & FAST) | (min(len, 0xff) << 6) | MEN | + ((msg.flags & I2C_M_READ) ? MDIR : 0); + writew(ctl, &twi->master_ctl); + + /* process the rest */ + ret = wait_for_completion(twi, &msg); + + if (ret) { + ctl = readw(&twi->master_ctl) & ~MEN; + writew(ctl, &twi->master_ctl); + ctl = readw(&twi->control) & ~TWI_ENA; + writew(ctl, &twi->control); + ctl = readw(&twi->control) | TWI_ENA; + writew(ctl, &twi->control); + } + + return ret; +} + +static uint adi_i2c_setspeed(struct i2c_adapter *adap, uint speed) +{ + struct twi_regs *twi = i2c_get_base(adap); + u16 clkdiv = I2C_SPEED_TO_DUTY(speed); + + /* Set TWI interface clock */ + if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) + return -1; + clkdiv = (clkdiv << 8) | (clkdiv & 0xff); + writew(clkdiv, &twi->clkdiv); + + /* Don't turn it on */ + writew(speed > 100000 ? FAST : 0, &twi->master_ctl); + + return 0; +} + +static void adi_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct twi_regs *twi = i2c_get_base(adap); + u16 prescale = ((get_i2c_clk() / 1000 / 1000 + 5) / 10) & 0x7F; + + /* Set TWI internal clock as 10MHz */ + writew(prescale, &twi->control); + + /* Set TWI interface clock as specified */ + i2c_set_bus_speed(speed); + + /* Enable it */ + writew(TWI_ENA | prescale, &twi->control); +} + +static int adi_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, int len) +{ + return i2c_transfer(adap, chip, addr, alen, buffer, + len, alen ? I2C_M_COMBO : I2C_M_READ); +} + +static int adi_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, int len) +{ + return i2c_transfer(adap, chip, addr, alen, buffer, len, 0); +} + +static int adi_i2c_probe(struct i2c_adapter *adap, uint8_t chip) +{ + u8 byte; + return adi_i2c_read(adap, chip, 0, 0, &byte, 1); +} + +static struct twi_regs *i2c_get_base(struct i2c_adapter *adap) +{ + switch (adap->hwadapnr) { +#if CONFIG_SYS_MAX_I2C_BUS > 2 + case 2: + return (struct twi_regs *)TWI2_CLKDIV; +#endif +#if CONFIG_SYS_MAX_I2C_BUS > 1 + case 1: + return (struct twi_regs *)TWI1_CLKDIV; +#endif + case 0: + return (struct twi_regs *)TWI0_CLKDIV; + + default: + printf("wrong hwadapnr: %d\n", adap->hwadapnr); + } + + return NULL; +} + +U_BOOT_I2C_ADAP_COMPLETE(adi_i2c0, adi_i2c_init, adi_i2c_probe, + adi_i2c_read, adi_i2c_write, + adi_i2c_setspeed, + CONFIG_SYS_I2C_SPEED, + 0, + 0) + +#if CONFIG_SYS_MAX_I2C_BUS > 1 +U_BOOT_I2C_ADAP_COMPLETE(adi_i2c1, adi_i2c_init, adi_i2c_probe, + adi_i2c_read, adi_i2c_write, + adi_i2c_setspeed, + CONFIG_SYS_I2C_SPEED, + 0, + 1) +#endif + +#if CONFIG_SYS_MAX_I2C_BUS > 2 +U_BOOT_I2C_ADAP_COMPLETE(adi_i2c2, adi_i2c_init, adi_i2c_probe, + adi_i2c_read, adi_i2c_write, + adi_i2c_setspeed, + CONFIG_SYS_I2C_SPEED, + 0, + 2) +#endif diff --git a/drivers/i2c/bfin-twi_i2c.c b/drivers/i2c/bfin-twi_i2c.c deleted file mode 100644 index cfab064dfa..0000000000 --- a/drivers/i2c/bfin-twi_i2c.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * i2c.c - driver for Blackfin on-chip TWI/I2C - * - * Copyright (c) 2006-2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. - */ - -#include <common.h> -#include <i2c.h> - -#include <asm/blackfin.h> -#include <asm/clock.h> -#include <asm/mach-common/bits/twi.h> - -/* Every register is 32bit aligned, but only 16bits in size */ -#define ureg(name) u16 name; u16 __pad_##name; -struct twi_regs { - ureg(clkdiv); - ureg(control); - ureg(slave_ctl); - ureg(slave_stat); - ureg(slave_addr); - ureg(master_ctl); - ureg(master_stat); - ureg(master_addr); - ureg(int_stat); - ureg(int_mask); - ureg(fifo_ctl); - ureg(fifo_stat); - char __pad[0x50]; - ureg(xmt_data8); - ureg(xmt_data16); - ureg(rcv_data8); - ureg(rcv_data16); -}; -#undef ureg - -/* U-Boot I2C framework allows only one active device at a time. */ -#ifdef TWI_CLKDIV -#define TWI0_CLKDIV TWI_CLKDIV -#endif -static volatile struct twi_regs *twi = (void *)TWI0_CLKDIV; - -#ifdef DEBUG -# define dmemset(s, c, n) memset(s, c, n) -#else -# define dmemset(s, c, n) -#endif -#define debugi(fmt, args...) \ - debug( \ - "MSTAT:0x%03x FSTAT:0x%x ISTAT:0x%02x\t%-20s:%-3i: " fmt "\n", \ - twi->master_stat, twi->fifo_stat, twi->int_stat, \ - __func__, __LINE__, ## args) - -#ifdef CONFIG_TWICLK_KHZ -# error do not define CONFIG_TWICLK_KHZ ... use CONFIG_SYS_I2C_SPEED -#endif - -/* - * The way speed is changed into duty often results in integer truncation - * with 50% duty, so we'll force rounding up to the next duty by adding 1 - * to the max. In practice this will get us a speed of something like - * 385 KHz. The other limit is easy to handle as it is only 8 bits. - */ -#define I2C_SPEED_MAX 400000 -#define I2C_SPEED_TO_DUTY(speed) (5000000 / (speed)) -#define I2C_DUTY_MAX (I2C_SPEED_TO_DUTY(I2C_SPEED_MAX) + 1) -#define I2C_DUTY_MIN 0xff /* 8 bit limited */ -#define SYS_I2C_DUTY I2C_SPEED_TO_DUTY(CONFIG_SYS_I2C_SPEED) -/* Note: duty is inverse of speed, so the comparisons below are correct */ -#if SYS_I2C_DUTY < I2C_DUTY_MAX || SYS_I2C_DUTY > I2C_DUTY_MIN -# error "The Blackfin I2C hardware can only operate 20KHz - 400KHz" -#endif - -/* All transfers are described by this data structure */ -struct i2c_msg { - u8 flags; -#define I2C_M_COMBO 0x4 -#define I2C_M_STOP 0x2 -#define I2C_M_READ 0x1 - int len; /* msg length */ - u8 *buf; /* pointer to msg data */ - int alen; /* addr length */ - u8 *abuf; /* addr buffer */ -}; - -/* Allow msec timeout per ~byte transfer */ -#define I2C_TIMEOUT 10 - -/** - * wait_for_completion - manage the actual i2c transfer - * @msg: the i2c msg - */ -static int wait_for_completion(struct i2c_msg *msg) -{ - uint16_t int_stat; - ulong timebase = get_timer(0); - - do { - int_stat = twi->int_stat; - - if (int_stat & XMTSERV) { - debugi("processing XMTSERV"); - twi->int_stat = XMTSERV; - SSYNC(); - if (msg->alen) { - twi->xmt_data8 = *(msg->abuf++); - --msg->alen; - } else if (!(msg->flags & I2C_M_COMBO) && msg->len) { - twi->xmt_data8 = *(msg->buf++); - --msg->len; - } else { - twi->master_ctl |= (msg->flags & I2C_M_COMBO) ? RSTART | MDIR : STOP; - SSYNC(); - } - } - if (int_stat & RCVSERV) { - debugi("processing RCVSERV"); - twi->int_stat = RCVSERV; - SSYNC(); - if (msg->len) { - *(msg->buf++) = twi->rcv_data8; - --msg->len; - } else if (msg->flags & I2C_M_STOP) { - twi->master_ctl |= STOP; - SSYNC(); - } - } - if (int_stat & MERR) { - debugi("processing MERR"); - twi->int_stat = MERR; - SSYNC(); - return msg->len; - } - if (int_stat & MCOMP) { - debugi("processing MCOMP"); - twi->int_stat = MCOMP; - SSYNC(); - if (msg->flags & I2C_M_COMBO && msg->len) { - twi->master_ctl = (twi->master_ctl & ~RSTART) | - (min(msg->len, 0xff) << 6) | MEN | MDIR; - SSYNC(); - } else - break; - } - - /* If we were able to do something, reset timeout */ - if (int_stat) - timebase = get_timer(0); - - } while (get_timer(timebase) < I2C_TIMEOUT); - - return msg->len; -} - -/** - * i2c_transfer - setup an i2c transfer - * @return: 0 if things worked, non-0 if things failed - * - * Here we just get the i2c stuff all prepped and ready, and then tail off - * into wait_for_completion() for all the bits to go. - */ -static int i2c_transfer(uchar chip, uint addr, int alen, uchar *buffer, int len, u8 flags) -{ - uchar addr_buffer[] = { - (addr >> 0), - (addr >> 8), - (addr >> 16), - }; - struct i2c_msg msg = { - .flags = flags | (len >= 0xff ? I2C_M_STOP : 0), - .buf = buffer, - .len = len, - .abuf = addr_buffer, - .alen = alen, - }; - int ret; - - dmemset(buffer, 0xff, len); - debugi("chip=0x%x addr=0x%02x alen=%i buf[0]=0x%02x len=%i flags=0x%02x[%s] ", - chip, addr, alen, buffer[0], len, flags, (flags & I2C_M_READ ? "rd" : "wr")); - - /* wait for things to settle */ - while (twi->master_stat & BUSBUSY) - if (ctrlc()) - return 1; - - /* Set Transmit device address */ - twi->master_addr = chip; - - /* Clear the FIFO before starting things */ - twi->fifo_ctl = XMTFLUSH | RCVFLUSH; - SSYNC(); - twi->fifo_ctl = 0; - SSYNC(); - - /* prime the pump */ - if (msg.alen) { - len = (msg.flags & I2C_M_COMBO) ? msg.alen : msg.alen + len; - debugi("first byte=0x%02x", *msg.abuf); - twi->xmt_data8 = *(msg.abuf++); - --msg.alen; - } else if (!(msg.flags & I2C_M_READ) && msg.len) { - debugi("first byte=0x%02x", *msg.buf); - twi->xmt_data8 = *(msg.buf++); - --msg.len; - } - - /* clear int stat */ - twi->master_stat = -1; - twi->int_stat = -1; - twi->int_mask = 0; - SSYNC(); - - /* Master enable */ - twi->master_ctl = - (twi->master_ctl & FAST) | - (min(len, 0xff) << 6) | MEN | - ((msg.flags & I2C_M_READ) ? MDIR : 0); - SSYNC(); - debugi("CTL=0x%04x", twi->master_ctl); - - /* process the rest */ - ret = wait_for_completion(&msg); - debugi("ret=%d", ret); - - if (ret) { - twi->master_ctl &= ~MEN; - twi->control &= ~TWI_ENA; - SSYNC(); - twi->control |= TWI_ENA; - SSYNC(); - } - - return ret; -} - -/** - * i2c_set_bus_speed - set i2c bus speed - * @speed: bus speed (in HZ) - */ -int i2c_set_bus_speed(unsigned int speed) -{ - u16 clkdiv = I2C_SPEED_TO_DUTY(speed); - - /* Set TWI interface clock */ - if (clkdiv < I2C_DUTY_MAX || clkdiv > I2C_DUTY_MIN) - return -1; - twi->clkdiv = (clkdiv << 8) | (clkdiv & 0xff); - - /* Don't turn it on */ - twi->master_ctl = (speed > 100000 ? FAST : 0); - - return 0; -} - -/** - * i2c_get_bus_speed - get i2c bus speed - * @speed: bus speed (in HZ) - */ -unsigned int i2c_get_bus_speed(void) -{ - /* 10 MHz / (2 * CLKDIV) -> 5 MHz / CLKDIV */ - return 5000000 / (twi->clkdiv & 0xff); -} - -/** - * i2c_init - initialize the i2c bus - * @speed: bus speed (in HZ) - * @slaveaddr: address of device in slave mode (0 - not slave) - * - * Slave mode isn't actually implemented. It'll stay that way until - * we get a real request for it. - */ -void i2c_init(int speed, int slaveaddr) -{ - uint8_t prescale = ((get_i2c_clk() / 1000 / 1000 + 5) / 10) & 0x7F; - - /* Set TWI internal clock as 10MHz */ - twi->control = prescale; - - /* Set TWI interface clock as specified */ - i2c_set_bus_speed(speed); - - /* Enable it */ - twi->control = TWI_ENA | prescale; - SSYNC(); - - debugi("CONTROL:0x%04x CLKDIV:0x%04x", twi->control, twi->clkdiv); - -#if CONFIG_SYS_I2C_SLAVE -# error I2C slave support not tested/supported - /* If they want us as a slave, do it */ - if (slaveaddr) { - twi->slave_addr = slaveaddr; - twi->slave_ctl = SEN; - } -#endif -} - -/** - * i2c_probe - test if a chip exists at a given i2c address - * @chip: i2c chip addr to search for - * @return: 0 if found, non-0 if not found - */ -int i2c_probe(uchar chip) -{ - u8 byte; - return i2c_read(chip, 0, 0, &byte, 1); -} - -/** - * i2c_read - read data from an i2c device - * @chip: i2c chip addr - * @addr: memory (register) address in the chip - * @alen: byte size of address - * @buffer: buffer to store data read from chip - * @len: how many bytes to read - * @return: 0 on success, non-0 on failure - */ -int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) -{ - return i2c_transfer(chip, addr, alen, buffer, len, (alen ? I2C_M_COMBO : I2C_M_READ)); -} - -/** - * i2c_write - write data to an i2c device - * @chip: i2c chip addr - * @addr: memory (register) address in the chip - * @alen: byte size of address - * @buffer: buffer holding data to write to chip - * @len: how many bytes to write - * @return: 0 on success, non-0 on failure - */ -int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) -{ - return i2c_transfer(chip, addr, alen, buffer, len, 0); -} - -/** - * i2c_set_bus_num - change active I2C bus - * @bus: bus index, zero based - * @returns: 0 on success, non-0 on failure - */ -int i2c_set_bus_num(unsigned int bus) -{ - switch (bus) { -#if CONFIG_SYS_MAX_I2C_BUS > 0 - case 0: twi = (void *)TWI0_CLKDIV; return 0; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 1 - case 1: twi = (void *)TWI1_CLKDIV; return 0; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 2 - case 2: twi = (void *)TWI2_CLKDIV; return 0; -#endif - default: return -1; - } -} - -/** - * i2c_get_bus_num - returns index of active I2C bus - */ -unsigned int i2c_get_bus_num(void) -{ - switch ((unsigned long)twi) { -#if CONFIG_SYS_MAX_I2C_BUS > 0 - case TWI0_CLKDIV: return 0; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 1 - case TWI1_CLKDIV: return 1; -#endif -#if CONFIG_SYS_MAX_I2C_BUS > 2 - case TWI2_CLKDIV: return 2; -#endif - default: return -1; - } -} diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 811033b0b8..ff7f25a0ef 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -38,7 +38,7 @@ * generic value. */ #ifndef CONFIG_I2C_TIMEOUT -#define CONFIG_I2C_TIMEOUT 10000 +#define CONFIG_I2C_TIMEOUT 100000 #endif #define I2C_READ_BIT 1 @@ -127,7 +127,7 @@ static const struct { static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev, unsigned int i2c_clk, unsigned int speed) { - unsigned short divider = min(i2c_clk / speed, (unsigned short) -1); + unsigned short divider = min(i2c_clk / speed, (unsigned int)USHRT_MAX); /* * We want to choose an FDR/DFSR that generates an I2C bus speed that diff --git a/drivers/i2c/i2c-emul-uclass.c b/drivers/i2c/i2c-emul-uclass.c new file mode 100644 index 0000000000..aa89f95953 --- /dev/null +++ b/drivers/i2c/i2c-emul-uclass.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <i2c.h> + +UCLASS_DRIVER(i2c_emul) = { + .id = UCLASS_I2C_EMUL, + .name = "i2c_emul", +}; diff --git a/drivers/i2c/i2c-uclass.c b/drivers/i2c/i2c-uclass.c new file mode 100644 index 0000000000..005bf8662f --- /dev/null +++ b/drivers/i2c/i2c-uclass.c @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define I2C_MAX_OFFSET_LEN 4 + +/** + * i2c_setup_offset() - Set up a new message with a chip offset + * + * @chip: Chip to use + * @offset: Byte offset within chip + * @offset_buf: Place to put byte offset + * @msg: Message buffer + * @return 0 if OK, -EADDRNOTAVAIL if the offset length is 0. In that case the + * message is still set up but will not contain an offset. + */ +static int i2c_setup_offset(struct dm_i2c_chip *chip, uint offset, + uint8_t offset_buf[], struct i2c_msg *msg) +{ + int offset_len; + + msg->addr = chip->chip_addr; + msg->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; + msg->len = chip->offset_len; + msg->buf = offset_buf; + if (!chip->offset_len) + return -EADDRNOTAVAIL; + assert(chip->offset_len <= I2C_MAX_OFFSET_LEN); + offset_len = chip->offset_len; + while (offset_len--) + *offset_buf++ = offset >> (8 * offset_len); + + return 0; +} + +static int i2c_read_bytewise(struct udevice *dev, uint offset, + uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[2], *ptr; + uint8_t offset_buf[I2C_MAX_OFFSET_LEN]; + int ret; + int i; + + for (i = 0; i < len; i++) { + if (i2c_setup_offset(chip, offset + i, offset_buf, msg)) + return -EINVAL; + ptr = msg + 1; + ptr->addr = chip->chip_addr; + ptr->flags = msg->flags | I2C_M_RD; + ptr->len = 1; + ptr->buf = &buffer[i]; + ptr++; + + ret = ops->xfer(bus, msg, ptr - msg); + if (ret) + return ret; + } + + return 0; +} + +static int i2c_write_bytewise(struct udevice *dev, uint offset, + const uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[1]; + uint8_t buf[I2C_MAX_OFFSET_LEN + 1]; + int ret; + int i; + + for (i = 0; i < len; i++) { + if (i2c_setup_offset(chip, offset + i, buf, msg)) + return -EINVAL; + buf[msg->len++] = buffer[i]; + + ret = ops->xfer(bus, msg, 1); + if (ret) + return ret; + } + + return 0; +} + +int i2c_read(struct udevice *dev, uint offset, uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[2], *ptr; + uint8_t offset_buf[I2C_MAX_OFFSET_LEN]; + int msg_count; + + if (!ops->xfer) + return -ENOSYS; + if (chip->flags & DM_I2C_CHIP_RD_ADDRESS) + return i2c_read_bytewise(dev, offset, buffer, len); + ptr = msg; + if (!i2c_setup_offset(chip, offset, offset_buf, ptr)) + ptr++; + + if (len) { + ptr->addr = chip->chip_addr; + ptr->flags = chip->flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; + ptr->flags |= I2C_M_RD; + ptr->len = len; + ptr->buf = buffer; + ptr++; + } + msg_count = ptr - msg; + + return ops->xfer(bus, msg, msg_count); +} + +int i2c_write(struct udevice *dev, uint offset, const uint8_t *buffer, int len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct udevice *bus = dev_get_parent(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[1]; + + if (!ops->xfer) + return -ENOSYS; + + if (chip->flags & DM_I2C_CHIP_WR_ADDRESS) + return i2c_write_bytewise(dev, offset, buffer, len); + /* + * The simple approach would be to send two messages here: one to + * set the offset and one to write the bytes. However some drivers + * will not be expecting this, and some chips won't like how the + * driver presents this on the I2C bus. + * + * The API does not support separate offset and data. We could extend + * it with a flag indicating that there is data in the next message + * that needs to be processed in the same transaction. We could + * instead add an additional buffer to each message. For now, handle + * this in the uclass since it isn't clear what the impact on drivers + * would be with this extra complication. Unfortunately this means + * copying the message. + * + * Use the stack for small messages, malloc() for larger ones. We + * need to allow space for the offset (up to 4 bytes) and the message + * itself. + */ + if (len < 64) { + uint8_t buf[I2C_MAX_OFFSET_LEN + len]; + + i2c_setup_offset(chip, offset, buf, msg); + msg->len += len; + memcpy(buf + chip->offset_len, buffer, len); + + return ops->xfer(bus, msg, 1); + } else { + uint8_t *buf; + int ret; + + buf = malloc(I2C_MAX_OFFSET_LEN + len); + if (!buf) + return -ENOMEM; + i2c_setup_offset(chip, offset, buf, msg); + msg->len += len; + memcpy(buf + chip->offset_len, buffer, len); + + ret = ops->xfer(bus, msg, 1); + free(buf); + return ret; + } +} + +/** + * i2c_probe_chip() - probe for a chip on a bus + * + * @bus: Bus to probe + * @chip_addr: Chip address to probe + * @flags: Flags for the chip + * @return 0 if found, -ENOSYS if the driver is invalid, -EREMOTEIO if the chip + * does not respond to probe + */ +static int i2c_probe_chip(struct udevice *bus, uint chip_addr, + enum dm_i2c_chip_flags chip_flags) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct i2c_msg msg[1]; + int ret; + + if (ops->probe_chip) { + ret = ops->probe_chip(bus, chip_addr, chip_flags); + if (!ret || ret != -ENOSYS) + return ret; + } + + if (!ops->xfer) + return -ENOSYS; + + /* Probe with a zero-length message */ + msg->addr = chip_addr; + msg->flags = chip_flags & DM_I2C_CHIP_10BIT ? I2C_M_TEN : 0; + msg->len = 0; + msg->buf = NULL; + + return ops->xfer(bus, msg, 1); +} + +static int i2c_bind_driver(struct udevice *bus, uint chip_addr, + struct udevice **devp) +{ + struct dm_i2c_chip chip; + char name[30], *str; + struct udevice *dev; + int ret; + + snprintf(name, sizeof(name), "generic_%x", chip_addr); + str = strdup(name); + ret = device_bind_driver(bus, "i2c_generic_chip_drv", str, &dev); + debug("%s: device_bind_driver: ret=%d\n", __func__, ret); + if (ret) + goto err_bind; + + /* Tell the device what we know about it */ + memset(&chip, '\0', sizeof(chip)); + chip.chip_addr = chip_addr; + chip.offset_len = 1; /* we assume */ + ret = device_probe_child(dev, &chip); + debug("%s: device_probe_child: ret=%d\n", __func__, ret); + if (ret) + goto err_probe; + + *devp = dev; + return 0; + +err_probe: + device_unbind(dev); +err_bind: + free(str); + return ret; +} + +int i2c_get_chip(struct udevice *bus, uint chip_addr, struct udevice **devp) +{ + struct udevice *dev; + + debug("%s: Searching bus '%s' for address %02x: ", __func__, + bus->name, chip_addr); + for (device_find_first_child(bus, &dev); dev; + device_find_next_child(&dev)) { + struct dm_i2c_chip store; + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + int ret; + + if (!chip) { + chip = &store; + i2c_chip_ofdata_to_platdata(gd->fdt_blob, + dev->of_offset, chip); + } + if (chip->chip_addr == chip_addr) { + ret = device_probe(dev); + debug("found, ret=%d\n", ret); + if (ret) + return ret; + *devp = dev; + return 0; + } + } + debug("not found\n"); + return i2c_bind_driver(bus, chip_addr, devp); +} + +int i2c_get_chip_for_busnum(int busnum, int chip_addr, struct udevice **devp) +{ + struct udevice *bus; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus); + if (ret) { + debug("Cannot find I2C bus %d\n", busnum); + return ret; + } + ret = i2c_get_chip(bus, chip_addr, devp); + if (ret) { + debug("Cannot find I2C chip %02x on bus %d\n", chip_addr, + busnum); + return ret; + } + + return 0; +} + +int i2c_probe(struct udevice *bus, uint chip_addr, uint chip_flags, + struct udevice **devp) +{ + int ret; + + *devp = NULL; + + /* First probe that chip */ + ret = i2c_probe_chip(bus, chip_addr, chip_flags); + debug("%s: bus='%s', address %02x, ret=%d\n", __func__, bus->name, + chip_addr, ret); + if (ret) + return ret; + + /* The chip was found, see if we have a driver, and probe it */ + ret = i2c_get_chip(bus, chip_addr, devp); + debug("%s: i2c_get_chip: ret=%d\n", __func__, ret); + + return ret; +} + +int i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct dm_i2c_bus *i2c = bus->uclass_priv; + int ret; + + /* + * If we have a method, call it. If not then the driver probably wants + * to deal with speed changes on the next transfer. It can easily read + * the current speed from this uclass + */ + if (ops->set_bus_speed) { + ret = ops->set_bus_speed(bus, speed); + if (ret) + return ret; + } + i2c->speed_hz = speed; + + return 0; +} + +/* + * i2c_get_bus_speed: + * + * Returns speed of selected I2C bus in Hz + */ +int i2c_get_bus_speed(struct udevice *bus) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + struct dm_i2c_bus *i2c = bus->uclass_priv; + + if (!ops->get_bus_speed) + return i2c->speed_hz; + + return ops->get_bus_speed(bus); +} + +int i2c_set_chip_flags(struct udevice *dev, uint flags) +{ + struct udevice *bus = dev->parent; + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + struct dm_i2c_ops *ops = i2c_get_ops(bus); + int ret; + + if (ops->set_flags) { + ret = ops->set_flags(dev, flags); + if (ret) + return ret; + } + chip->flags = flags; + + return 0; +} + +int i2c_get_chip_flags(struct udevice *dev, uint *flagsp) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + + *flagsp = chip->flags; + + return 0; +} + +int i2c_set_chip_offset_len(struct udevice *dev, uint offset_len) +{ + struct dm_i2c_chip *chip = dev_get_parentdata(dev); + + if (offset_len > I2C_MAX_OFFSET_LEN) + return -EINVAL; + chip->offset_len = offset_len; + + return 0; +} + +int i2c_deblock(struct udevice *bus) +{ + struct dm_i2c_ops *ops = i2c_get_ops(bus); + + /* + * We could implement a software deblocking here if we could get + * access to the GPIOs used by I2C, and switch them to GPIO mode + * and then back to I2C. This is somewhat beyond our powers in + * driver model at present, so for now just fail. + * + * See https://patchwork.ozlabs.org/patch/399040/ + */ + if (!ops->deblock) + return -ENOSYS; + + return ops->deblock(bus); +} + +int i2c_chip_ofdata_to_platdata(const void *blob, int node, + struct dm_i2c_chip *chip) +{ + chip->offset_len = 1; /* default */ + chip->flags = 0; + chip->chip_addr = fdtdec_get_int(gd->fdt_blob, node, "reg", -1); + if (chip->chip_addr == -1) { + debug("%s: I2C Node '%s' has no 'reg' property\n", __func__, + fdt_get_name(blob, node, NULL)); + return -EINVAL; + } + + return 0; +} + +static int i2c_post_probe(struct udevice *dev) +{ + struct dm_i2c_bus *i2c = dev->uclass_priv; + + i2c->speed_hz = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "clock-frequency", 100000); + + return i2c_set_bus_speed(dev, i2c->speed_hz); +} + +int i2c_post_bind(struct udevice *dev) +{ + /* Scan the bus for devices */ + return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); +} + +UCLASS_DRIVER(i2c) = { + .id = UCLASS_I2C, + .name = "i2c", + .per_device_auto_alloc_size = sizeof(struct dm_i2c_bus), + .post_bind = i2c_post_bind, + .post_probe = i2c_post_probe, +}; + +UCLASS_DRIVER(i2c_generic) = { + .id = UCLASS_I2C_GENERIC, + .name = "i2c_generic", +}; + +U_BOOT_DRIVER(i2c_generic_chip_drv) = { + .name = "i2c_generic_chip_drv", + .id = UCLASS_I2C_GENERIC, +}; diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index 18d6736601..41cc3b8fa4 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -174,11 +174,11 @@ static int i2c_mux_set_all(void) return 0; } -static int i2c_mux_disconnet_all(void) +static int i2c_mux_disconnect_all(void) { struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; int i; - uint8_t buf; + uint8_t buf = 0; if (I2C_ADAP->init_done == 0) return 0; @@ -197,7 +197,7 @@ static int i2c_mux_disconnet_all(void) ret = I2C_ADAP->write(I2C_ADAP, chip, 0, 0, &buf, 1); if (ret != 0) { - printf("i2c: mux diconnect error\n"); + printf("i2c: mux disconnect error\n"); return ret; } } while (i > 0); @@ -229,11 +229,9 @@ static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) } /* implement possible board specific board init */ -static void __def_i2c_init_board(void) +__weak void i2c_init_board(void) { } -void i2c_init_board(void) - __attribute__((weak, alias("__def_i2c_init_board"))); /* * i2c_init_all(): @@ -295,7 +293,7 @@ int i2c_set_bus_num(unsigned int bus) } #ifndef CONFIG_SYS_I2C_DIRECT_BUS - i2c_mux_disconnet_all(); + i2c_mux_disconnect_all(); #endif gd->cur_i2c_bus = bus; @@ -395,9 +393,7 @@ void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val) i2c_write(addr, reg, 1, &val, 1); } -void __i2c_init(int speed, int slaveaddr) +__weak void i2c_init(int speed, int slaveaddr) { i2c_init_bus(i2c_get_bus_num(), speed, slaveaddr); } -void i2c_init(int speed, int slaveaddr) - __attribute__((weak, alias("__i2c_init"))); diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 021b2fe511..fc5ee35a1a 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -402,17 +402,6 @@ int bus_i2c_write(void *base, uchar chip, uint addr, int alen, return ret; } -struct i2c_parms { - void *base; - void *idle_bus_data; - int (*idle_bus_fn)(void *p); -}; - -struct sram_data { - unsigned curr_i2c_bus; - struct i2c_parms i2c_data[3]; -}; - static void * const i2c_bases[] = { #if defined(CONFIG_MX25) (void *)IMX_I2C_BASE, @@ -439,6 +428,17 @@ static void * const i2c_bases[] = { #endif }; +struct i2c_parms { + void *base; + void *idle_bus_data; + int (*idle_bus_fn)(void *p); +}; + +struct sram_data { + unsigned curr_i2c_bus; + struct i2c_parms i2c_data[ARRAY_SIZE(i2c_bases)]; +}; + void *i2c_get_base(struct i2c_adapter *adap) { return i2c_bases[adap->hwadapnr]; diff --git a/drivers/i2c/ppc4xx_i2c.c b/drivers/i2c/ppc4xx_i2c.c index e7a15ba644..df8888550b 100644 --- a/drivers/i2c/ppc4xx_i2c.c +++ b/drivers/i2c/ppc4xx_i2c.c @@ -158,8 +158,7 @@ static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) * * Typical case is a Write of an addr followd by a Read. The * IBM FAQ does not cover this. On the last byte of the write - * we don't set the creg CHT bit, and on the first bytes of the - * read we set the RPST bit. + * we don't set the creg CHT bit but the RPST bit. * * It does not support address only transfers, there must be * a data part. If you want to write the address yourself, put @@ -247,6 +246,10 @@ static int _i2c_transfer(struct i2c_adapter *adap, if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt)) creg |= IIC_CNTL_CHT; + /* last part of address, prepare for repeated start on read */ + if (cmd_type && (ptr == addr) && ((tran + bc) == cnt)) + creg |= IIC_CNTL_RPST; + if (reading) { creg |= IIC_CNTL_READ; } else { @@ -286,6 +289,27 @@ static int _i2c_transfer(struct i2c_adapter *adap, /* Transfer aborted? */ if (status & IIC_EXTSTS_XFRA) result = IIC_NOK_XFRA; + /* Is bus free? + * If error happened during combined xfer + * IIC interface is usually stuck in some strange + * state without a valid stop condition. + * Brute, but working: generate stop, then soft reset. + */ + if ((status & IIC_EXTSTS_BCS_MASK) + != IIC_EXTSTS_BCS_FREE){ + u8 mdcntl = in_8(&i2c->mdcntl); + + /* Generate valid stop condition */ + out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST); + out_8(&i2c->directcntl, IIC_DIRCNTL_SCC); + udelay(10); + out_8(&i2c->directcntl, + IIC_DIRCNTL_SCC | IIC_DIRCNTL_SDAC); + out_8(&i2c->xtcntlss, 0); + + ppc4xx_i2c_init(adap, (mdcntl & IIC_MDCNTL_FSM) + ? 400000 : 100000, 0); + } } else if ( status & IIC_STS_PT) { result = IIC_NOK_TOUT; } @@ -314,8 +338,6 @@ static int _i2c_transfer(struct i2c_adapter *adap, cnt = data_len; tran = 0; reading = cmd_type; - if (reading) - creg = IIC_CNTL_RPST; } } return result; diff --git a/drivers/i2c/rcar_i2c.c b/drivers/i2c/rcar_i2c.c index 50cebd622b..90ad116a98 100644 --- a/drivers/i2c/rcar_i2c.c +++ b/drivers/i2c/rcar_i2c.c @@ -119,10 +119,10 @@ rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr) /* set slave address, receive */ writel((chip << 1) | 1, &dev->icmar); - /* clear status */ - writel(0, &dev->icmsr); /* start master receive */ writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + /* clear status */ + writel(0, &dev->icmsr); while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR)) != (MSR_MAT | MSR_MDR)) diff --git a/drivers/i2c/sandbox_i2c.c b/drivers/i2c/sandbox_i2c.c new file mode 100644 index 0000000000..f0e9f51a1f --- /dev/null +++ b/drivers/i2c/sandbox_i2c.c @@ -0,0 +1,111 @@ +/* + * Simulate an I2C port + * + * Copyright (c) 2014 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <i2c.h> +#include <asm/test.h> +#include <dm/lists.h> +#include <dm/device-internal.h> +#include <dm/root.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dm_sandbox_i2c_emul_priv { + struct udevice *emul; +}; + +static int get_emul(struct udevice *dev, struct udevice **devp, + struct dm_i2c_ops **opsp) +{ + struct dm_i2c_chip *priv; + int ret; + + *devp = NULL; + *opsp = NULL; + priv = dev_get_parentdata(dev); + if (!priv->emul) { + ret = dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, + false); + if (ret) + return ret; + + ret = device_get_child(dev, 0, &priv->emul); + if (ret) + return ret; + } + *devp = priv->emul; + *opsp = i2c_get_ops(priv->emul); + + return 0; +} + +static int sandbox_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) +{ + struct dm_i2c_bus *i2c = bus->uclass_priv; + struct dm_i2c_ops *ops; + struct udevice *emul, *dev; + bool is_read; + int ret; + + /* Special test code to return success but with no emulation */ + if (msg->addr == SANDBOX_I2C_TEST_ADDR) + return 0; + + ret = i2c_get_chip(bus, msg->addr, &dev); + if (ret) + return ret; + + ret = get_emul(dev, &emul, &ops); + if (ret) + return ret; + + /* + * For testing, don't allow writing above 100KHz for writes and + * 400KHz for reads + */ + is_read = nmsgs > 1; + if (i2c->speed_hz > (is_read ? 400000 : 100000)) + return -EINVAL; + return ops->xfer(emul, msg, nmsgs); +} + +static const struct dm_i2c_ops sandbox_i2c_ops = { + .xfer = sandbox_i2c_xfer, +}; + +static int sandbox_i2c_child_pre_probe(struct udevice *dev) +{ + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); + + /* Ignore our test address */ + if (i2c_chip->chip_addr == SANDBOX_I2C_TEST_ADDR) + return 0; + if (dev->of_offset == -1) + return 0; + + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, + i2c_chip); +} + +static const struct udevice_id sandbox_i2c_ids[] = { + { .compatible = "sandbox,i2c" }, + { } +}; + +U_BOOT_DRIVER(i2c_sandbox) = { + .name = "i2c_sandbox", + .id = UCLASS_I2C, + .of_match = sandbox_i2c_ids, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_pre_probe = sandbox_i2c_child_pre_probe, + .ops = &sandbox_i2c_ops, +}; diff --git a/drivers/i2c/tegra_i2c.c b/drivers/i2c/tegra_i2c.c index 562211e7de..87290c3127 100644 --- a/drivers/i2c/tegra_i2c.c +++ b/drivers/i2c/tegra_i2c.c @@ -7,6 +7,8 @@ */ #include <common.h> +#include <dm.h> +#include <errno.h> #include <fdtdec.h> #include <i2c.h> #include <asm/io.h> @@ -19,6 +21,12 @@ DECLARE_GLOBAL_DATA_PTR; +enum i2c_type { + TYPE_114, + TYPE_STD, + TYPE_DVC, +}; + /* Information about i2c controller */ struct i2c_bus { int id; @@ -27,20 +35,17 @@ struct i2c_bus { int pinmux_config; struct i2c_control *control; struct i2c_ctlr *regs; - int is_dvc; /* DVC type, rather than I2C */ - int is_scs; /* single clock source (T114+) */ + enum i2c_type type; int inited; /* bus is inited */ }; -static struct i2c_bus i2c_controllers[TEGRA_I2C_NUM_CONTROLLERS]; - static void set_packet_mode(struct i2c_bus *i2c_bus) { u32 config; config = I2C_CNFG_NEW_MASTER_FSM_MASK | I2C_CNFG_PACKET_MODE_MASK; - if (i2c_bus->is_dvc) { + if (i2c_bus->type == TYPE_DVC) { struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; writel(config, &dvc->cnfg); @@ -65,6 +70,9 @@ static void i2c_reset_controller(struct i2c_bus *i2c_bus) static void i2c_init_controller(struct i2c_bus *i2c_bus) { + if (!i2c_bus->speed) + return; + debug("%s: speed=%d\n", __func__, i2c_bus->speed); /* * Use PLLP - DP-04508-001_v06 datasheet indicates a divisor of 8 * here, in section 23.3.1, but in fact we seem to need a factor of @@ -73,7 +81,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, i2c_bus->speed * 2 * 8); - if (i2c_bus->is_scs) { + if (i2c_bus->type == TYPE_114) { /* * T114 I2C went to a single clock source for standard/fast and * HS clock speeds. The new clock rate setting calculation is: @@ -98,7 +106,7 @@ static void i2c_init_controller(struct i2c_bus *i2c_bus) i2c_reset_controller(i2c_bus); /* Configure I2C controller. */ - if (i2c_bus->is_dvc) { /* only for DVC I2C */ + if (i2c_bus->type == TYPE_DVC) { /* only for DVC I2C */ struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; setbits_le32(&dvc->ctrl3, DVC_CTRL_REG3_I2C_HW_SW_PROG_MASK); @@ -272,7 +280,7 @@ exit: return error; } -static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, +static int tegra_i2c_write_data(struct i2c_bus *i2c_bus, u32 addr, u8 *data, u32 len, bool end_with_repeated_start) { int error; @@ -286,14 +294,14 @@ static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, trans_info.num_bytes = len; trans_info.is_10bit_address = 0; - error = send_recv_packets(bus, &trans_info); + error = send_recv_packets(i2c_bus, &trans_info); if (error) debug("tegra_i2c_write_data: Error (%d) !!!\n", error); return error; } -static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, +static int tegra_i2c_read_data(struct i2c_bus *i2c_bus, u32 addr, u8 *data, u32 len) { int error; @@ -305,52 +313,32 @@ static int tegra_i2c_read_data(struct i2c_bus *bus, u32 addr, u8 *data, trans_info.num_bytes = len; trans_info.is_10bit_address = 0; - error = send_recv_packets(bus, &trans_info); + error = send_recv_packets(i2c_bus, &trans_info); if (error) debug("tegra_i2c_read_data: Error (%d) !!!\n", error); return error; } -#ifndef CONFIG_OF_CONTROL -#error "Please enable device tree support to use this driver" -#endif - -/** - * Check that a bus number is valid and return a pointer to it - * - * @param bus_num Bus number to check / return - * @return pointer to bus, if valid, else NULL - */ -static struct i2c_bus *tegra_i2c_get_bus(struct i2c_adapter *adap) +static int tegra_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) { - struct i2c_bus *bus; + struct i2c_bus *i2c_bus = dev_get_priv(dev); - bus = &i2c_controllers[adap->hwadapnr]; - if (!bus->inited) { - debug("%s: Bus %u not available\n", __func__, adap->hwadapnr); - return NULL; - } - - return bus; -} - -static unsigned int tegra_i2c_set_bus_speed(struct i2c_adapter *adap, - unsigned int speed) -{ - struct i2c_bus *bus; - - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 0; - bus->speed = speed; - i2c_init_controller(bus); + i2c_bus->speed = speed; + i2c_init_controller(i2c_bus); return 0; } -static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) +static int tegra_i2c_probe(struct udevice *dev) { + struct i2c_bus *i2c_bus = dev_get_priv(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + bool is_dvc; + + i2c_bus->id = dev->seq; + i2c_bus->type = dev_get_of_data(dev); i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); /* @@ -358,7 +346,6 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) * far no one needs anything other than the default. */ i2c_bus->pinmux_config = FUNCMUX_DEFAULT; - i2c_bus->speed = fdtdec_get_int(blob, node, "clock-frequency", 0); i2c_bus->periph_id = clock_decode_periph_id(blob, node); /* @@ -371,107 +358,25 @@ static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA; */ if (i2c_bus->periph_id == -1) - return -FDT_ERR_NOTFOUND; + return -EINVAL; - return 0; -} - -/* - * Process a list of nodes, adding them to our list of I2C ports. - * - * @param blob fdt blob - * @param node_list list of nodes to process (any <=0 are ignored) - * @param count number of nodes to process - * @param is_dvc 1 if these are DVC ports, 0 if standard I2C - * @param is_scs 1 if this HW uses a single clock source (T114+) - * @return 0 if ok, -1 on error - */ -static int process_nodes(const void *blob, int node_list[], int count, - int is_dvc, int is_scs) -{ - struct i2c_bus *i2c_bus; - int i; - - /* build the i2c_controllers[] for each controller */ - for (i = 0; i < count; i++) { - int node = node_list[i]; - - if (node <= 0) - continue; - - i2c_bus = &i2c_controllers[i]; - i2c_bus->id = i; - - if (i2c_get_config(blob, node, i2c_bus)) { - printf("i2c_init_board: failed to decode bus %d\n", i); - return -1; - } - - i2c_bus->is_scs = is_scs; - - i2c_bus->is_dvc = is_dvc; - if (is_dvc) { - i2c_bus->control = - &((struct dvc_ctlr *)i2c_bus->regs)->control; - } else { - i2c_bus->control = &i2c_bus->regs->control; - } - debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", - is_dvc ? "dvc" : "i2c", i, i2c_bus->regs, - i2c_bus->periph_id, i2c_bus->speed); - i2c_init_controller(i2c_bus); - debug("ok\n"); - i2c_bus->inited = 1; - - /* Mark position as used */ - node_list[i] = -1; + is_dvc = dev_get_of_data(dev) == TYPE_DVC; + if (is_dvc) { + i2c_bus->control = + &((struct dvc_ctlr *)i2c_bus->regs)->control; + } else { + i2c_bus->control = &i2c_bus->regs->control; } + i2c_init_controller(i2c_bus); + debug("%s: controller bus %d at %p, periph_id %d, speed %d: ", + is_dvc ? "dvc" : "i2c", dev->seq, i2c_bus->regs, + i2c_bus->periph_id, i2c_bus->speed); return 0; } -/* Sadly there is no error return from this function */ -void i2c_init_board(void) -{ - int node_list[TEGRA_I2C_NUM_CONTROLLERS]; - const void *blob = gd->fdt_blob; - int count; - - /* First check for newer (T114+) I2C ports */ - count = fdtdec_find_aliases_for_id(blob, "i2c", - COMPAT_NVIDIA_TEGRA114_I2C, node_list, - TEGRA_I2C_NUM_CONTROLLERS); - if (process_nodes(blob, node_list, count, 0, 1)) - return; - - /* Now get the older (T20/T30) normal I2C ports */ - count = fdtdec_find_aliases_for_id(blob, "i2c", - COMPAT_NVIDIA_TEGRA20_I2C, node_list, - TEGRA_I2C_NUM_CONTROLLERS); - if (process_nodes(blob, node_list, count, 0, 0)) - return; - - /* Now look for dvc ports */ - count = fdtdec_add_aliases_for_id(blob, "i2c", - COMPAT_NVIDIA_TEGRA20_DVC, node_list, - TEGRA_I2C_NUM_CONTROLLERS); - if (process_nodes(blob, node_list, count, 1, 0)) - return; -} - -static void tegra_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) -{ - /* No i2c support prior to relocation */ - if (!(gd->flags & GD_FLG_RELOC)) - return; - - /* This will override the speed selected in the fdt for that port */ - debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); - i2c_set_bus_speed(speed); -} - /* i2c write version without the register address */ -static int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, +static int i2c_write_data(struct i2c_bus *i2c_bus, uchar chip, uchar *buffer, int len, bool end_with_repeated_start) { int rc; @@ -484,7 +389,7 @@ static int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, debug("\n"); /* Shift 7-bit address over for lower-level i2c functions */ - rc = tegra_i2c_write_data(bus, chip << 1, buffer, len, + rc = tegra_i2c_write_data(i2c_bus, chip << 1, buffer, len, end_with_repeated_start); if (rc) debug("i2c_write_data(): rc=%d\n", rc); @@ -493,14 +398,14 @@ static int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, } /* i2c read version without the register address */ -static int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, - int len) +static int i2c_read_data(struct i2c_bus *i2c_bus, uchar chip, uchar *buffer, + int len) { int rc; debug("inside i2c_read_data():\n"); /* Shift 7-bit address over for lower-level i2c functions */ - rc = tegra_i2c_read_data(bus, chip << 1, buffer, len); + rc = tegra_i2c_read_data(i2c_bus, chip << 1, buffer, len); if (rc) { debug("i2c_read_data(): rc=%d\n", rc); return rc; @@ -516,132 +421,99 @@ static int i2c_read_data(struct i2c_bus *bus, uchar chip, uchar *buffer, } /* Probe to see if a chip is present. */ -static int tegra_i2c_probe(struct i2c_adapter *adap, uchar chip) +static int tegra_i2c_probe_chip(struct udevice *bus, uint chip_addr, + uint chip_flags) { - struct i2c_bus *bus; + struct i2c_bus *i2c_bus = dev_get_priv(bus); int rc; - uchar reg; - - debug("i2c_probe: addr=0x%x\n", chip); - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 1; - reg = 0; - rc = i2c_write_data(bus, chip, ®, 1, false); - if (rc) { - debug("Error probing 0x%x.\n", chip); - return 1; - } - return 0; -} + u8 reg; -static int i2c_addr_ok(const uint addr, const int alen) -{ - /* We support 7 or 10 bit addresses, so one or two bytes each */ - return alen == 1 || alen == 2; + /* Shift 7-bit address over for lower-level i2c functions */ + rc = tegra_i2c_write_data(i2c_bus, chip_addr << 1, ®, sizeof(reg), + false); + + return rc; } -/* Read bytes */ -static int tegra_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, - int alen, uchar *buffer, int len) +static int tegra_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, + int nmsgs) { - struct i2c_bus *bus; - uint offset; - int i; - - debug("i2c_read: chip=0x%x, addr=0x%x, alen=0x%x len=0x%x\n", - chip, addr, alen, len); - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 1; - if (!i2c_addr_ok(addr, alen)) { - debug("i2c_read: Bad address %x.%d.\n", addr, alen); - return 1; - } - for (offset = 0; offset < len; offset++) { - if (alen) { - uchar data[alen]; - for (i = 0; i < alen; i++) { - data[alen - i - 1] = - (addr + offset) >> (8 * i); - } - if (i2c_write_data(bus, chip, data, alen, true)) { - debug("i2c_read: error sending (0x%x)\n", - addr); - return 1; - } + struct i2c_bus *i2c_bus = dev_get_priv(bus); + int ret; + + debug("i2c_xfer: %d messages\n", nmsgs); + for (; nmsgs > 0; nmsgs--, msg++) { + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); + + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) { + ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, + msg->len); + } else { + ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, + msg->len, next_is_read); } - if (i2c_read_data(bus, chip, buffer + offset, 1)) { - debug("i2c_read: error reading (0x%x)\n", addr); - return 1; + if (ret) { + debug("i2c_write: error sending\n"); + return -EREMOTEIO; } } return 0; } -/* Write bytes */ -static int tegra_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, - int alen, uchar *buffer, int len) +int tegra_i2c_get_dvc_bus(struct udevice **busp) { - struct i2c_bus *bus; - uint offset; - int i; - - debug("i2c_write: chip=0x%x, addr=0x%x, alen=0x%x len=0x%x\n", - chip, addr, alen, len); - bus = tegra_i2c_get_bus(adap); - if (!bus) - return 1; - if (!i2c_addr_ok(addr, alen)) { - debug("i2c_write: Bad address %x.%d.\n", addr, alen); - return 1; - } - for (offset = 0; offset < len; offset++) { - uchar data[alen + 1]; - for (i = 0; i < alen; i++) - data[alen - i - 1] = (addr + offset) >> (8 * i); - data[alen] = buffer[offset]; - if (i2c_write_data(bus, chip, data, alen + 1, false)) { - debug("i2c_write: error sending (0x%x)\n", addr); - return 1; + struct udevice *bus; + + for (uclass_first_device(UCLASS_I2C, &bus); + bus; + uclass_next_device(&bus)) { + if (dev_get_of_data(bus) == TYPE_DVC) { + *busp = bus; + return 0; } } - return 0; + return -ENODEV; } -int tegra_i2c_get_dvc_bus_num(void) -{ - int i; +static const struct dm_i2c_ops tegra_i2c_ops = { + .xfer = tegra_i2c_xfer, + .probe_chip = tegra_i2c_probe_chip, + .set_bus_speed = tegra_i2c_set_bus_speed, +}; - for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; i++) { - struct i2c_bus *bus = &i2c_controllers[i]; +static int tegra_i2c_child_pre_probe(struct udevice *dev) +{ + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); - if (bus->inited && bus->is_dvc) - return i; - } + if (dev->of_offset == -1) + return 0; + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, + i2c_chip); +} - return -1; +static int tegra_i2c_ofdata_to_platdata(struct udevice *dev) +{ + return 0; } -/* - * Register soft i2c adapters - */ -U_BOOT_I2C_ADAP_COMPLETE(tegra0, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 0) -U_BOOT_I2C_ADAP_COMPLETE(tegra1, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 1) -U_BOOT_I2C_ADAP_COMPLETE(tegra2, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 2) -U_BOOT_I2C_ADAP_COMPLETE(tegra3, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 3) -#if TEGRA_I2C_NUM_CONTROLLERS > 4 -U_BOOT_I2C_ADAP_COMPLETE(tegra4, tegra_i2c_init, tegra_i2c_probe, - tegra_i2c_read, tegra_i2c_write, - tegra_i2c_set_bus_speed, 100000, 0, 4) -#endif +static const struct udevice_id tegra_i2c_ids[] = { + { .compatible = "nvidia,tegra114-i2c", .data = TYPE_114 }, + { .compatible = "nvidia,tegra20-i2c", .data = TYPE_STD }, + { .compatible = "nvidia,tegra20-i2c-dvc", .data = TYPE_DVC }, + { } +}; + +U_BOOT_DRIVER(i2c_tegra) = { + .name = "i2c_tegra", + .id = UCLASS_I2C, + .of_match = tegra_i2c_ids, + .ofdata_to_platdata = tegra_i2c_ofdata_to_platdata, + .probe = tegra_i2c_probe, + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), + .child_pre_probe = tegra_i2c_child_pre_probe, + .priv_auto_alloc_size = sizeof(struct i2c_bus), + .ops = &tegra_i2c_ops, +}; |