diff options
Diffstat (limited to 'qemu/roms/u-boot/drivers/i2c')
31 files changed, 11503 insertions, 0 deletions
diff --git a/qemu/roms/u-boot/drivers/i2c/Makefile b/qemu/roms/u-boot/drivers/i2c/Makefile new file mode 100644 index 000000000..e33586d8a --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/Makefile @@ -0,0 +1,31 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_BFIN_TWI_I2C) += bfin-twi_i2c.o +obj-$(CONFIG_DW_I2C) += designware_i2c.o +obj-$(CONFIG_I2C_MVTWSI) += mvtwsi.o +obj-$(CONFIG_I2C_MV) += mv_i2c.o +obj-$(CONFIG_I2C_MXS) += mxs_i2c.o +obj-$(CONFIG_PCA9564_I2C) += pca9564_i2c.o +obj-$(CONFIG_TSI108_I2C) += tsi108_i2c.o +obj-$(CONFIG_U8500_I2C) += u8500_i2c.o +obj-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o +obj-$(CONFIG_SYS_I2C) += i2c_core.o +obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o +obj-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o +obj-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o +obj-$(CONFIG_SYS_I2C_KONA) += kona_i2c.o +obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o +obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o +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_SH) += sh_i2c.o +obj-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o +obj-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o +obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o diff --git a/qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c b/qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c new file mode 100644 index 000000000..cfab064df --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/bfin-twi_i2c.c @@ -0,0 +1,379 @@ +/* + * 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/qemu/roms/u-boot/drivers/i2c/davinci_i2c.c b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.c new file mode 100644 index 000000000..9ca99c4ab --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.c @@ -0,0 +1,384 @@ +/* + * TI DaVinci (TMS320DM644x) I2C driver. + * + * (C) Copyright 2012-2014 + * Texas Instruments Incorporated, <www.ti.com> + * (C) Copyright 2007 Sergey Kubushyn <ksi@koi8.net> + * -------------------------------------------------------- + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <asm/arch/hardware.h> +#include <asm/arch/i2c_defs.h> +#include <asm/io.h> +#include "davinci_i2c.h" + +#define CHECK_NACK() \ + do {\ + if (tmp & (I2C_TIMEOUT | I2C_STAT_NACK)) {\ + REG(&(i2c_base->i2c_con)) = 0;\ + return 1;\ + } \ + } while (0) + +static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap); + +static int wait_for_bus(struct i2c_adapter *adap) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + int stat, timeout; + + REG(&(i2c_base->i2c_stat)) = 0xffff; + + for (timeout = 0; timeout < 10; timeout++) { + stat = REG(&(i2c_base->i2c_stat)); + if (!((stat) & I2C_STAT_BB)) { + REG(&(i2c_base->i2c_stat)) = 0xffff; + return 0; + } + + REG(&(i2c_base->i2c_stat)) = stat; + udelay(50000); + } + + REG(&(i2c_base->i2c_stat)) = 0xffff; + return 1; +} + + +static int poll_i2c_irq(struct i2c_adapter *adap, int mask) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + int stat, timeout; + + for (timeout = 0; timeout < 10; timeout++) { + udelay(1000); + stat = REG(&(i2c_base->i2c_stat)); + if (stat & mask) + return stat; + } + + REG(&(i2c_base->i2c_stat)) = 0xffff; + return stat | I2C_TIMEOUT; +} + +static void flush_rx(struct i2c_adapter *adap) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + + while (1) { + if (!(REG(&(i2c_base->i2c_stat)) & I2C_STAT_RRDY)) + break; + + REG(&(i2c_base->i2c_drr)); + REG(&(i2c_base->i2c_stat)) = I2C_STAT_RRDY; + udelay(1000); + } +} + +static uint davinci_i2c_setspeed(struct i2c_adapter *adap, uint speed) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + uint32_t div, psc; + + psc = 2; + /* SCLL + SCLH */ + div = (CONFIG_SYS_HZ_CLOCK / ((psc + 1) * speed)) - 10; + REG(&(i2c_base->i2c_psc)) = psc; /* 27MHz / (2 + 1) = 9MHz */ + REG(&(i2c_base->i2c_scll)) = (div * 50) / 100; /* 50% Duty */ + REG(&(i2c_base->i2c_sclh)) = div - REG(&(i2c_base->i2c_scll)); + + adap->speed = speed; + return 0; +} + +static void davinci_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + + if (REG(&(i2c_base->i2c_con)) & I2C_CON_EN) { + REG(&(i2c_base->i2c_con)) = 0; + udelay(50000); + } + + davinci_i2c_setspeed(adap, speed); + + REG(&(i2c_base->i2c_oa)) = slaveadd; + REG(&(i2c_base->i2c_cnt)) = 0; + + /* Interrupts must be enabled or I2C module won't work */ + REG(&(i2c_base->i2c_ie)) = I2C_IE_SCD_IE | I2C_IE_XRDY_IE | + I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | I2C_IE_NACK_IE; + + /* Now enable I2C controller (get it out of reset) */ + REG(&(i2c_base->i2c_con)) = I2C_CON_EN; + + udelay(1000); +} + +static int davinci_i2c_probe(struct i2c_adapter *adap, uint8_t chip) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + int rc = 1; + + if (chip == REG(&(i2c_base->i2c_oa))) + return rc; + + REG(&(i2c_base->i2c_con)) = 0; + if (wait_for_bus(adap)) + return 1; + + /* try to read one byte from current (or only) address */ + REG(&(i2c_base->i2c_cnt)) = 1; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_STP); + udelay(50000); + + if (!(REG(&(i2c_base->i2c_stat)) & I2C_STAT_NACK)) { + rc = 0; + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + } else { + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_con)) |= I2C_CON_STP; + udelay(20000); + if (wait_for_bus(adap)) + return 1; + } + + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_cnt)) = 0; + return rc; +} + +static int davinci_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint32_t addr, int alen, uint8_t *buf, int len) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + uint32_t tmp; + int i; + + if ((alen < 0) || (alen > 2)) { + printf("%s(): bogus address length %x\n", __func__, alen); + return 1; + } + + if (wait_for_bus(adap)) + return 1; + + if (alen != 0) { + /* Start address phase */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX; + REG(&(i2c_base->i2c_cnt)) = alen; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = tmp; + + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + switch (alen) { + case 2: + /* Send address MSByte */ + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = (addr >> 8) & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + /* No break, fall through */ + case 1: + /* Send address LSByte */ + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = addr & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | + I2C_STAT_NACK | I2C_STAT_ARDY); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_ARDY)) { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + } + } + + /* Address phase is over, now read 'len' bytes and stop */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP; + REG(&(i2c_base->i2c_cnt)) = len & 0xffff; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = tmp; + + for (i = 0; i < len; i++) { + tmp = poll_i2c_irq(adap, I2C_STAT_RRDY | I2C_STAT_NACK | + I2C_STAT_ROVR); + + CHECK_NACK(); + + if (tmp & I2C_STAT_RRDY) { + buf[i] = REG(&(i2c_base->i2c_drr)); + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + } + + tmp = poll_i2c_irq(adap, I2C_STAT_SCD | I2C_STAT_NACK); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_SCD)) { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_cnt)) = 0; + REG(&(i2c_base->i2c_con)) = 0; + + return 0; +} + +static int davinci_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint32_t addr, int alen, uint8_t *buf, int len) +{ + struct i2c_regs *i2c_base = davinci_get_base(adap); + uint32_t tmp; + int i; + + if ((alen < 0) || (alen > 2)) { + printf("%s(): bogus address length %x\n", __func__, alen); + return 1; + } + if (len < 0) { + printf("%s(): bogus length %x\n", __func__, len); + return 1; + } + + if (wait_for_bus(adap)) + return 1; + + /* Start address phase */ + tmp = I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_TRX | I2C_CON_STP; + REG(&(i2c_base->i2c_cnt)) = (alen == 0) ? + len & 0xffff : (len & 0xffff) + alen; + REG(&(i2c_base->i2c_sa)) = chip; + REG(&(i2c_base->i2c_con)) = tmp; + + switch (alen) { + case 2: + /* Send address MSByte */ + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = (addr >> 8) & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + /* No break, fall through */ + case 1: + /* Send address LSByte */ + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) { + REG(&(i2c_base->i2c_dxr)) = addr & 0xff; + } else { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + } + + for (i = 0; i < len; i++) { + tmp = poll_i2c_irq(adap, I2C_STAT_XRDY | I2C_STAT_NACK); + + CHECK_NACK(); + + if (tmp & I2C_STAT_XRDY) + REG(&(i2c_base->i2c_dxr)) = buf[i]; + else + return 1; + } + + tmp = poll_i2c_irq(adap, I2C_STAT_SCD | I2C_STAT_NACK); + + CHECK_NACK(); + + if (!(tmp & I2C_STAT_SCD)) { + REG(&(i2c_base->i2c_con)) = 0; + return 1; + } + + flush_rx(adap); + REG(&(i2c_base->i2c_stat)) = 0xffff; + REG(&(i2c_base->i2c_cnt)) = 0; + REG(&(i2c_base->i2c_con)) = 0; + + return 0; +} + +static struct i2c_regs *davinci_get_base(struct i2c_adapter *adap) +{ + switch (adap->hwadapnr) { +#if I2C_BUS_MAX >= 3 + case 2: + return (struct i2c_regs *)I2C2_BASE; +#endif +#if I2C_BUS_MAX >= 2 + case 1: + return (struct i2c_regs *)I2C1_BASE; +#endif + case 0: + return (struct i2c_regs *)I2C_BASE; + + default: + printf("wrong hwadapnr: %d\n", adap->hwadapnr); + } + + return NULL; +} + +U_BOOT_I2C_ADAP_COMPLETE(davinci_0, davinci_i2c_init, davinci_i2c_probe, + davinci_i2c_read, davinci_i2c_write, + davinci_i2c_setspeed, + CONFIG_SYS_DAVINCI_I2C_SPEED, + CONFIG_SYS_DAVINCI_I2C_SLAVE, + 0) + +#if I2C_BUS_MAX >= 2 +U_BOOT_I2C_ADAP_COMPLETE(davinci_1, davinci_i2c_init, davinci_i2c_probe, + davinci_i2c_read, davinci_i2c_write, + davinci_i2c_setspeed, + CONFIG_SYS_DAVINCI_I2C_SPEED1, + CONFIG_SYS_DAVINCI_I2C_SLAVE1, + 1) +#endif + +#if I2C_BUS_MAX >= 3 +U_BOOT_I2C_ADAP_COMPLETE(davinci_2, davinci_i2c_init, davinci_i2c_probe, + davinci_i2c_read, davinci_i2c_write, + davinci_i2c_setspeed, + CONFIG_SYS_DAVINCI_I2C_SPEED2, + CONFIG_SYS_DAVINCI_I2C_SLAVE2, + 2) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/davinci_i2c.h b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.h new file mode 100644 index 000000000..20d43424b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/davinci_i2c.h @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2004-2014 + * Texas Instruments, <www.ti.com> + * + * Some changes copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _DAVINCI_I2C_H_ +#define _DAVINCI_I2C_H_ + +#define I2C_WRITE 0 +#define I2C_READ 1 + +struct i2c_regs { + u32 i2c_oa; + u32 i2c_ie; + u32 i2c_stat; + u32 i2c_scll; + u32 i2c_sclh; + u32 i2c_cnt; + u32 i2c_drr; + u32 i2c_sa; + u32 i2c_dxr; + u32 i2c_con; + u32 i2c_iv; + u32 res_2c; + u32 i2c_psc; +}; + +/* I2C masks */ + +/* I2C Interrupt Enable Register (I2C_IE): */ +#define I2C_IE_SCD_IE (1 << 5) /* Stop condition detect interrupt enable */ +#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */ +#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */ +#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */ +#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Status Register (I2C_STAT): */ + +#define I2C_STAT_BB (1 << 12) /* Bus busy */ +#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define I2C_STAT_SCD (1 << 5) /* Stop condition detect */ +#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Interrupt Code Register (I2C_INTCODE): */ + +#define I2C_INTCODE_MASK 7 +#define I2C_INTCODE_NONE 0 +#define I2C_INTCODE_AL 1 /* Arbitration lost */ +#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */ +#define I2C_INTCODE_ARDY 3 /* Register access ready */ +#define I2C_INTCODE_RRDY 4 /* Rcv data ready */ +#define I2C_INTCODE_XRDY 5 /* Xmit data ready */ +#define I2C_INTCODE_SCD 6 /* Stop condition detect */ + +/* I2C Configuration Register (I2C_CON): */ + +#define I2C_CON_EN (1 << 5) /* I2C module enable */ +#define I2C_CON_STB (1 << 4) /* Start byte mode (master mode only) */ +#define I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define I2C_CON_TRX (1 << 9) /* Tx/Rx mode (master mode only) */ +#define I2C_CON_XA (1 << 8) /* Expand address */ +#define I2C_CON_STP (1 << 11) /* Stop condition (master mode only) */ +#define I2C_CON_STT (1 << 13) /* Start condition (master mode only) */ +#define I2C_CON_FREE (1 << 14) /* Free run on emulation */ + +#define I2C_TIMEOUT 0xffff0000 /* Timeout mask for poll_i2c_irq() */ + +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/designware_i2c.c b/qemu/roms/u-boot/drivers/i2c/designware_i2c.c new file mode 100644 index 000000000..c891ebd39 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/designware_i2c.c @@ -0,0 +1,436 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include "designware_i2c.h" + +#ifdef CONFIG_I2C_MULTI_BUS +static unsigned int bus_initialized[CONFIG_SYS_I2C_BUS_MAX]; +static unsigned int current_bus = 0; +#endif + +static struct i2c_regs *i2c_regs_p = + (struct i2c_regs *)CONFIG_SYS_I2C_BASE; + +/* + * set_speed - Set the i2c speed mode (standard, high, fast) + * @i2c_spd: required i2c speed mode + * + * Set the i2c speed mode (standard, high, fast) + */ +static void set_speed(int i2c_spd) +{ + unsigned int cntl; + unsigned int hcnt, lcnt; + unsigned int enbl; + + /* to set speed cltr must be disabled */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK)); + + switch (i2c_spd) { + case IC_SPEED_MODE_MAX: + cntl |= IC_CON_SPD_HS; + hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_hs_scl_hcnt); + lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_hs_scl_lcnt); + break; + + case IC_SPEED_MODE_STANDARD: + cntl |= IC_CON_SPD_SS; + hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_ss_scl_hcnt); + lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_ss_scl_lcnt); + break; + + case IC_SPEED_MODE_FAST: + default: + cntl |= IC_CON_SPD_FS; + hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); + lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); + break; + } + + writel(cntl, &i2c_regs_p->ic_con); + + /* Enable back i2c now speed set */ + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); +} + +/* + * i2c_set_bus_speed - Set the i2c speed + * @speed: required i2c speed + * + * Set the i2c speed. + */ +int i2c_set_bus_speed(int speed) +{ + if (speed >= I2C_MAX_SPEED) + set_speed(IC_SPEED_MODE_MAX); + else if (speed >= I2C_FAST_SPEED) + set_speed(IC_SPEED_MODE_FAST); + else + set_speed(IC_SPEED_MODE_STANDARD); + + return 0; +} + +/* + * i2c_get_bus_speed - Gets the i2c speed + * + * Gets the i2c speed. + */ +int i2c_get_bus_speed(void) +{ + u32 cntl; + + cntl = (readl(&i2c_regs_p->ic_con) & IC_CON_SPD_MSK); + + if (cntl == IC_CON_SPD_HS) + return I2C_MAX_SPEED; + else if (cntl == IC_CON_SPD_FS) + return I2C_FAST_SPEED; + else if (cntl == IC_CON_SPD_SS) + return I2C_STANDARD_SPEED; + + return 0; +} + +/* + * i2c_init - Init function + * @speed: required i2c speed + * @slaveadd: slave address for the device + * + * Initialization function. + */ +void i2c_init(int speed, int slaveadd) +{ + unsigned int enbl; + + /* Disable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_regs_p->ic_con); + writel(IC_RX_TL, &i2c_regs_p->ic_rx_tl); + writel(IC_TX_TL, &i2c_regs_p->ic_tx_tl); + i2c_set_bus_speed(speed); + writel(IC_STOP_DET, &i2c_regs_p->ic_intr_mask); + writel(slaveadd, &i2c_regs_p->ic_sar); + + /* Enable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + +#ifdef CONFIG_I2C_MULTI_BUS + bus_initialized[current_bus] = 1; +#endif +} + +/* + * i2c_setaddress - Sets the target slave address + * @i2c_addr: target i2c address + * + * Sets the target slave address. + */ +static void i2c_setaddress(unsigned int i2c_addr) +{ + unsigned int enbl; + + /* Disable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl &= ~IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); + + writel(i2c_addr, &i2c_regs_p->ic_tar); + + /* Enable i2c */ + enbl = readl(&i2c_regs_p->ic_enable); + enbl |= IC_ENABLE_0B; + writel(enbl, &i2c_regs_p->ic_enable); +} + +/* + * i2c_flush_rxfifo - Flushes the i2c RX FIFO + * + * Flushes the i2c RX FIFO + */ +static void i2c_flush_rxfifo(void) +{ + while (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) + readl(&i2c_regs_p->ic_cmd_data); +} + +/* + * i2c_wait_for_bb - Waits for bus busy + * + * Waits for bus busy + */ +static int i2c_wait_for_bb(void) +{ + unsigned long start_time_bb = get_timer(0); + + while ((readl(&i2c_regs_p->ic_status) & IC_STATUS_MA) || + !(readl(&i2c_regs_p->ic_status) & IC_STATUS_TFE)) { + + /* Evaluate timeout */ + if (get_timer(start_time_bb) > (unsigned long)(I2C_BYTE_TO_BB)) + return 1; + } + + return 0; +} + +static int i2c_xfer_init(uchar chip, uint addr, int alen) +{ + if (i2c_wait_for_bb()) + return 1; + + i2c_setaddress(chip); + while (alen) { + alen--; + /* high byte address going out first */ + writel((addr >> (alen * 8)) & 0xff, + &i2c_regs_p->ic_cmd_data); + } + return 0; +} + +static int i2c_xfer_finish(void) +{ + ulong start_stop_det = get_timer(0); + + while (1) { + if ((readl(&i2c_regs_p->ic_raw_intr_stat) & IC_STOP_DET)) { + readl(&i2c_regs_p->ic_clr_stop_det); + break; + } else if (get_timer(start_stop_det) > I2C_STOPDET_TO) { + break; + } + } + + if (i2c_wait_for_bb()) { + printf("Timed out waiting for bus\n"); + return 1; + } + + i2c_flush_rxfifo(); + + return 0; +} + +/* + * i2c_read - Read from i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Read from i2c memory. + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + unsigned long start_time_rx; + +#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); + addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8)); + + debug("%s: fix addr_overflow: chip %02x addr %02x\n", __func__, chip, + addr); +#endif + + if (i2c_xfer_init(chip, addr, alen)) + return 1; + + start_time_rx = get_timer(0); + while (len) { + if (len == 1) + writel(IC_CMD | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(IC_CMD, &i2c_regs_p->ic_cmd_data); + + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) { + *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_write - Write to i2c memory + * @chip: target i2c address + * @addr: address to read from + * @alen: + * @buffer: buffer for read data + * @len: no of bytes to be read + * + * Write to i2c memory. + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int nb = len; + unsigned long start_time_tx; + +#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); + addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8)); + + debug("%s: fix addr_overflow: chip %02x addr %02x\n", __func__, chip, + addr); +#endif + + if (i2c_xfer_init(chip, addr, alen)) + return 1; + + start_time_tx = get_timer(0); + while (len) { + if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) { + if (--len == 0) + writel(*buffer | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(*buffer, &i2c_regs_p->ic_cmd_data); + buffer++; + start_time_tx = get_timer(0); + + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + printf("Timed out. i2c write Failed\n"); + return 1; + } + } + + return i2c_xfer_finish(); +} + +/* + * i2c_probe - Probe the i2c chip + */ +int i2c_probe(uchar chip) +{ + u32 tmp; + int ret; + + /* + * Try to read the first location of the chip. + */ + ret = i2c_read(chip, 0, 1, (uchar *)&tmp, 1); + if (ret) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return ret; +} + +#ifdef CONFIG_I2C_MULTI_BUS +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { + case 0: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE; + break; +#ifdef CONFIG_SYS_I2C_BASE1 + case 1: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE1; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + case 2: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE2; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + case 3: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE3; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + case 4: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE4; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + case 5: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE5; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE6 + case 6: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE6; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE7 + case 7: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE7; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE8 + case 8: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE8; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE9 + case 9: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE9; + break; +#endif + default: + printf("Bad bus: %d\n", bus); + return -1; + } + + current_bus = bus; + + if (!bus_initialized[current_bus]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/designware_i2c.h b/qemu/roms/u-boot/drivers/i2c/designware_i2c.h new file mode 100644 index 000000000..19b09dfa5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/designware_i2c.h @@ -0,0 +1,133 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __DW_I2C_H_ +#define __DW_I2C_H_ + +struct i2c_regs { + u32 ic_con; + u32 ic_tar; + u32 ic_sar; + u32 ic_hs_maddr; + u32 ic_cmd_data; + u32 ic_ss_scl_hcnt; + u32 ic_ss_scl_lcnt; + u32 ic_fs_scl_hcnt; + u32 ic_fs_scl_lcnt; + u32 ic_hs_scl_hcnt; + u32 ic_hs_scl_lcnt; + u32 ic_intr_stat; + u32 ic_intr_mask; + u32 ic_raw_intr_stat; + u32 ic_rx_tl; + u32 ic_tx_tl; + u32 ic_clr_intr; + u32 ic_clr_rx_under; + u32 ic_clr_rx_over; + u32 ic_clr_tx_over; + u32 ic_clr_rd_req; + u32 ic_clr_tx_abrt; + u32 ic_clr_rx_done; + u32 ic_clr_activity; + u32 ic_clr_stop_det; + u32 ic_clr_start_det; + u32 ic_clr_gen_call; + u32 ic_enable; + u32 ic_status; + u32 ic_txflr; + u32 ix_rxflr; + u32 reserved_1; + u32 ic_tx_abrt_source; +}; + +#if !defined(IC_CLK) +#define IC_CLK 166 +#endif +#define NANO_TO_MICRO 1000 + +/* High and low times in different speed modes (in ns) */ +#define MIN_SS_SCL_HIGHTIME 4000 +#define MIN_SS_SCL_LOWTIME 4700 +#define MIN_FS_SCL_HIGHTIME 600 +#define MIN_FS_SCL_LOWTIME 1300 +#define MIN_HS_SCL_HIGHTIME 60 +#define MIN_HS_SCL_LOWTIME 160 + +/* Worst case timeout for 1 byte is kept as 2ms */ +#define I2C_BYTE_TO (CONFIG_SYS_HZ/500) +#define I2C_STOPDET_TO (CONFIG_SYS_HZ/500) +#define I2C_BYTE_TO_BB (I2C_BYTE_TO * 16) + +/* i2c control register definitions */ +#define IC_CON_SD 0x0040 +#define IC_CON_RE 0x0020 +#define IC_CON_10BITADDRMASTER 0x0010 +#define IC_CON_10BITADDR_SLAVE 0x0008 +#define IC_CON_SPD_MSK 0x0006 +#define IC_CON_SPD_SS 0x0002 +#define IC_CON_SPD_FS 0x0004 +#define IC_CON_SPD_HS 0x0006 +#define IC_CON_MM 0x0001 + +/* i2c target address register definitions */ +#define TAR_ADDR 0x0050 + +/* i2c slave address register definitions */ +#define IC_SLAVE_ADDR 0x0002 + +/* i2c data buffer and command register definitions */ +#define IC_CMD 0x0100 +#define IC_STOP 0x0200 + +/* i2c interrupt status register definitions */ +#define IC_GEN_CALL 0x0800 +#define IC_START_DET 0x0400 +#define IC_STOP_DET 0x0200 +#define IC_ACTIVITY 0x0100 +#define IC_RX_DONE 0x0080 +#define IC_TX_ABRT 0x0040 +#define IC_RD_REQ 0x0020 +#define IC_TX_EMPTY 0x0010 +#define IC_TX_OVER 0x0008 +#define IC_RX_FULL 0x0004 +#define IC_RX_OVER 0x0002 +#define IC_RX_UNDER 0x0001 + +/* fifo threshold register definitions */ +#define IC_TL0 0x00 +#define IC_TL1 0x01 +#define IC_TL2 0x02 +#define IC_TL3 0x03 +#define IC_TL4 0x04 +#define IC_TL5 0x05 +#define IC_TL6 0x06 +#define IC_TL7 0x07 +#define IC_RX_TL IC_TL0 +#define IC_TX_TL IC_TL0 + +/* i2c enable register definitions */ +#define IC_ENABLE_0B 0x0001 + +/* i2c status register definitions */ +#define IC_STATUS_SA 0x0040 +#define IC_STATUS_MA 0x0020 +#define IC_STATUS_RFF 0x0010 +#define IC_STATUS_RFNE 0x0008 +#define IC_STATUS_TFE 0x0004 +#define IC_STATUS_TFNF 0x0002 +#define IC_STATUS_ACT 0x0001 + +/* Speed Selection */ +#define IC_SPEED_MODE_STANDARD 1 +#define IC_SPEED_MODE_FAST 2 +#define IC_SPEED_MODE_MAX 3 + +#define I2C_MAX_SPEED 3400000 +#define I2C_FAST_SPEED 400000 +#define I2C_STANDARD_SPEED 100000 + +#endif /* __DW_I2C_H_ */ diff --git a/qemu/roms/u-boot/drivers/i2c/fsl_i2c.c b/qemu/roms/u-boot/drivers/i2c/fsl_i2c.c new file mode 100644 index 000000000..aa159f8d4 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/fsl_i2c.c @@ -0,0 +1,541 @@ +/* + * Copyright 2006,2009 Freescale Semiconductor, Inc. + * + * 2012, Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <i2c.h> /* Functional interface */ +#include <asm/io.h> +#include <asm/fsl_i2c.h> /* HW definitions */ + +/* The maximum number of microseconds we will wait until another master has + * released the bus. If not defined in the board header file, then use a + * generic value. + */ +#ifndef CONFIG_I2C_MBB_TIMEOUT +#define CONFIG_I2C_MBB_TIMEOUT 100000 +#endif + +/* The maximum number of microseconds we will wait for a read or write + * operation to complete. If not defined in the board header file, then use a + * generic value. + */ +#ifndef CONFIG_I2C_TIMEOUT +#define CONFIG_I2C_TIMEOUT 10000 +#endif + +#define I2C_READ_BIT 1 +#define I2C_WRITE_BIT 0 + +DECLARE_GLOBAL_DATA_PTR; + +static const struct fsl_i2c *i2c_dev[2] = { + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET), +#ifdef CONFIG_SYS_FSL_I2C2_OFFSET + (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET) +#endif +}; + +/* I2C speed map for a DFSR value of 1 */ + +/* + * Map I2C frequency dividers to FDR and DFSR values + * + * This structure is used to define the elements of a table that maps I2C + * frequency divider (I2C clock rate divided by I2C bus speed) to a value to be + * programmed into the Frequency Divider Ratio (FDR) and Digital Filter + * Sampling Rate (DFSR) registers. + * + * The actual table should be defined in the board file, and it must be called + * fsl_i2c_speed_map[]. + * + * The last entry of the table must have a value of {-1, X}, where X is same + * FDR/DFSR values as the second-to-last entry. This guarantees that any + * search through the array will always find a match. + * + * The values of the divider must be in increasing numerical order, i.e. + * fsl_i2c_speed_map[x+1].divider > fsl_i2c_speed_map[x].divider. + * + * For this table, the values are based on a value of 1 for the DFSR + * register. See the application note AN2919 "Determining the I2C Frequency + * Divider Ratio for SCL" + * + * ColdFire I2C frequency dividers for FDR values are different from + * PowerPC. The protocol to use the I2C module is still the same. + * A different table is defined and are based on MCF5xxx user manual. + * + */ +static const struct { + unsigned short divider; + u8 fdr; +} fsl_i2c_speed_map[] = { +#ifdef __M68K__ + {20, 32}, {22, 33}, {24, 34}, {26, 35}, + {28, 0}, {28, 36}, {30, 1}, {32, 37}, + {34, 2}, {36, 38}, {40, 3}, {40, 39}, + {44, 4}, {48, 5}, {48, 40}, {56, 6}, + {56, 41}, {64, 42}, {68, 7}, {72, 43}, + {80, 8}, {80, 44}, {88, 9}, {96, 41}, + {104, 10}, {112, 42}, {128, 11}, {128, 43}, + {144, 12}, {160, 13}, {160, 48}, {192, 14}, + {192, 49}, {224, 50}, {240, 15}, {256, 51}, + {288, 16}, {320, 17}, {320, 52}, {384, 18}, + {384, 53}, {448, 54}, {480, 19}, {512, 55}, + {576, 20}, {640, 21}, {640, 56}, {768, 22}, + {768, 57}, {960, 23}, {896, 58}, {1024, 59}, + {1152, 24}, {1280, 25}, {1280, 60}, {1536, 26}, + {1536, 61}, {1792, 62}, {1920, 27}, {2048, 63}, + {2304, 28}, {2560, 29}, {3072, 30}, {3840, 31}, + {-1, 31} +#endif +}; + +/** + * Set the I2C bus speed for a given I2C device + * + * @param dev: the I2C device + * @i2c_clk: I2C bus clock frequency + * @speed: the desired speed of the bus + * + * The I2C device must be stopped before calling this function. + * + * The return value is the actual bus speed that is set. + */ +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); + + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. That means that we + * want the first divider that is equal to or greater than the + * calculated divider. + */ +#ifdef __PPC__ + u8 dfsr, fdr = 0x31; /* Default if no FDR found */ + /* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */ + unsigned short a, b, ga, gb; + unsigned long c_div, est_div; + +#ifdef CONFIG_FSL_I2C_CUSTOM_DFSR + dfsr = CONFIG_FSL_I2C_CUSTOM_DFSR; +#else + /* Condition 1: dfsr <= 50/T */ + dfsr = (5 * (i2c_clk / 1000)) / 100000; +#endif +#ifdef CONFIG_FSL_I2C_CUSTOM_FDR + fdr = CONFIG_FSL_I2C_CUSTOM_FDR; + speed = i2c_clk / divider; /* Fake something */ +#else + debug("Requested speed:%d, i2c_clk:%d\n", speed, i2c_clk); + if (!dfsr) + dfsr = 1; + + est_div = ~0; + for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) { + for (gb = 0; gb < 8; gb++) { + b = 16 << gb; + c_div = b * (a + ((3*dfsr)/b)*2); + if ((c_div > divider) && (c_div < est_div)) { + unsigned short bin_gb, bin_ga; + + est_div = c_div; + bin_gb = gb << 2; + bin_ga = (ga & 0x3) | ((ga & 0x4) << 3); + fdr = bin_gb | bin_ga; + speed = i2c_clk / est_div; + debug("FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x, " + "a:%d, b:%d, speed:%d\n", + fdr, est_div, ga, gb, a, b, speed); + /* Condition 2 not accounted for */ + debug("Tr <= %d ns\n", + (b - 3 * dfsr) * 1000000 / + (i2c_clk / 1000)); + } + } + if (a == 20) + a += 2; + if (a == 24) + a += 4; + } + debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr); + debug("FDR:0x%.2x, speed:%d\n", fdr, speed); +#endif + writeb(dfsr, &dev->dfsrr); /* set default filter */ + writeb(fdr, &dev->fdr); /* set bus speed */ +#else + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(fsl_i2c_speed_map); i++) + if (fsl_i2c_speed_map[i].divider >= divider) { + u8 fdr; + + fdr = fsl_i2c_speed_map[i].fdr; + speed = i2c_clk / fsl_i2c_speed_map[i].divider; + writeb(fdr, &dev->fdr); /* set bus speed */ + + break; + } +#endif + return speed; +} + +static unsigned int get_i2c_clock(int bus) +{ + if (bus) + return gd->arch.i2c2_clk; /* I2C2 clock */ + else + return gd->arch.i2c1_clk; /* I2C1 clock */ +} + +static int fsl_i2c_fixup(const struct fsl_i2c *dev) +{ + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval = 0; + int ret = -1; + unsigned int flags = 0; + +#ifdef CONFIG_SYS_FSL_ERRATUM_I2C_A004447 + unsigned int svr = get_svr(); + if ((SVR_SOC_VER(svr) == SVR_8548 && IS_SVR_REV(svr, 3, 1)) || + (SVR_REV(svr) <= CONFIG_SYS_FSL_A004447_SVR_REV)) + flags = I2C_CR_BIT6; +#endif + + writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MBB)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + + if (readb(&dev->sr) & I2C_SR_MAL) { + /* SDA is stuck low */ + writeb(0, &dev->cr); + udelay(100); + writeb(I2C_CR_MSTA | flags, &dev->cr); + writeb(I2C_CR_MEN | I2C_CR_MSTA | flags, &dev->cr); + } + + readb(&dev->dr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MIF)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + ret = 0; + +err: + writeb(I2C_CR_MEN | flags, &dev->cr); + writeb(0, &dev->sr); + udelay(100); + + return ret; +} + +static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + const struct fsl_i2c *dev; + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval; + +#ifdef 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(); +#endif + dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(0, &dev->cr); /* stop I2C controller */ + udelay(5); /* let it shutdown in peace */ + set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed); + writeb(slaveadd << 1, &dev->adr);/* write slave address */ + writeb(0x0, &dev->sr); /* clear status register */ + writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */ + + timeval = get_ticks(); + while (readb(&dev->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) < timeout) + continue; + + if (fsl_i2c_fixup(dev)) + debug("i2c_init: BUS#%d failed to init\n", + adap->hwadapnr); + + break; + } + +#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT + /* Call board specific i2c bus reset routine AFTER the bus has been + * initialized. Use either this callpoint or i2c_init_board; + * which is called before i2c_init operations. + * For details about this problem see doc/I2C_Edge_Conditions. + */ + i2c_board_late_init(); +#endif +} + +static int +i2c_wait4bus(struct i2c_adapter *adap) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + unsigned long long timeval = get_ticks(); + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + + while (readb(&dev->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) > timeout) + return -1; + } + + return 0; +} + +static __inline__ int +i2c_wait(struct i2c_adapter *adap, int write) +{ + u32 csr; + unsigned long long timeval = get_ticks(); + const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT); + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + do { + csr = readb(&dev->sr); + if (!(csr & I2C_SR_MIF)) + continue; + /* Read again to allow register to stabilise */ + csr = readb(&dev->sr); + + writeb(0x0, &dev->sr); + + if (csr & I2C_SR_MAL) { + debug("i2c_wait: MAL\n"); + return -1; + } + + if (!(csr & I2C_SR_MCF)) { + debug("i2c_wait: unfinished\n"); + return -1; + } + + if (write == I2C_WRITE_BIT && (csr & I2C_SR_RXAK)) { + debug("i2c_wait: No RXACK\n"); + return -1; + } + + return 0; + } while ((get_ticks() - timeval) < timeout); + + debug("i2c_wait: timed out\n"); + return -1; +} + +static __inline__ int +i2c_write_addr(struct i2c_adapter *adap, u8 dev, u8 dir, int rsta) +{ + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX + | (rsta ? I2C_CR_RSTA : 0), + &device->cr); + + writeb((dev << 1) | dir, &device->dr); + + if (i2c_wait(adap, I2C_WRITE_BIT) < 0) + return 0; + + return 1; +} + +static __inline__ int +__i2c_write(struct i2c_adapter *adap, u8 *data, int length) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < length; i++) { + writeb(data[i], &dev->dr); + + if (i2c_wait(adap, I2C_WRITE_BIT) < 0) + break; + } + + return i; +} + +static __inline__ int +__i2c_read(struct i2c_adapter *adap, u8 *data, int length) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0), + &dev->cr); + + /* dummy read */ + readb(&dev->dr); + + for (i = 0; i < length; i++) { + if (i2c_wait(adap, I2C_READ_BIT) < 0) + break; + + /* Generate ack on last next to last byte */ + if (i == length - 2) + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK, + &dev->cr); + + /* Do not generate stop on last byte */ + if (i == length - 1) + writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX, + &dev->cr); + + data[i] = readb(&dev->dr); + } + + return i; +} + +static int +fsl_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr, int alen, u8 *data, + int length) +{ + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i = -1; /* signal error */ + u8 *a = (u8*)&addr; + int len = alen * -1; + + if (i2c_wait4bus(adap) < 0) + return -1; + + /* To handle the need of I2C devices that require to write few bytes + * (more than 4 bytes of address as in the case of else part) + * of data before reading, Negative equivalent of length(bytes to write) + * is passed, but used the +ve part of len for writing data + */ + if (alen < 0) { + /* Generate a START and send the Address and + * the Tx Bytes to the slave. + * "START: Address: Write bytes data[len]" + * IF part supports writing any number of bytes in contrast + * to the else part, which supports writing address offset + * of upto 4 bytes only. + * bytes that need to be written are passed in + * "data", which will eventually keep the data READ, + * after writing the len bytes out of it + */ + if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0) + i = __i2c_write(adap, data, len); + + if (i != len) + return -1; + + if (length && i2c_write_addr(adap, dev, I2C_READ_BIT, 1) != 0) + i = __i2c_read(adap, data, length); + } else { + if ((!length || alen > 0) && + i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + __i2c_write(adap, &a[4 - alen], alen) == alen) + i = 0; /* No error so far */ + + if (length && + i2c_write_addr(adap, dev, I2C_READ_BIT, alen ? 1 : 0) != 0) + i = __i2c_read(adap, data, length); + } + + writeb(I2C_CR_MEN, &device->cr); + + if (i2c_wait4bus(adap)) /* Wait until STOP */ + debug("i2c_read: wait4bus timed out\n"); + + if (i == length) + return 0; + + return -1; +} + +static int +fsl_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, int alen, + u8 *data, int length) +{ + struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + int i = -1; /* signal error */ + u8 *a = (u8*)&addr; + + if (i2c_wait4bus(adap) < 0) + return -1; + + if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + __i2c_write(adap, &a[4 - alen], alen) == alen) { + i = __i2c_write(adap, data, length); + } + + writeb(I2C_CR_MEN, &device->cr); + if (i2c_wait4bus(adap)) /* Wait until STOP */ + debug("i2c_write: wait4bus timed out\n"); + + if (i == length) + return 0; + + return -1; +} + +static int +fsl_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + /* For unknow reason the controller will ACK when + * probing for a slave with the same address, so skip + * it. + */ + if (chip == (readb(&dev->adr) >> 1)) + return -1; + + return fsl_i2c_read(adap, chip, 0, 0, NULL, 0); +} + +static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr]; + + writeb(0, &dev->cr); /* stop controller */ + set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed); + writeb(I2C_CR_MEN, &dev->cr); /* start controller */ + + return 0; +} + +/* + * Register fsl i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C_SPEED, CONFIG_SYS_FSL_I2C_SLAVE, + 0) +#ifdef CONFIG_SYS_FSL_I2C2_OFFSET +U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read, + fsl_i2c_write, fsl_i2c_set_bus_speed, + CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE, + 1) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/fti2c010.c b/qemu/roms/u-boot/drivers/i2c/fti2c010.c new file mode 100644 index 000000000..68d9a4291 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/fti2c010.c @@ -0,0 +1,346 @@ +/* + * Faraday I2C Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <i2c.h> + +#include "fti2c010.h" + +#ifndef CONFIG_SYS_I2C_SPEED +#define CONFIG_SYS_I2C_SPEED 5000 +#endif + +#ifndef CONFIG_SYS_I2C_SLAVE +#define CONFIG_SYS_I2C_SLAVE 0 +#endif + +#ifndef CONFIG_FTI2C010_CLOCK +#define CONFIG_FTI2C010_CLOCK clk_get_rate("I2C") +#endif + +#ifndef CONFIG_FTI2C010_TIMEOUT +#define CONFIG_FTI2C010_TIMEOUT 10 /* ms */ +#endif + +/* 7-bit dev address + 1-bit read/write */ +#define I2C_RD(dev) ((((dev) << 1) & 0xfe) | 1) +#define I2C_WR(dev) (((dev) << 1) & 0xfe) + +struct fti2c010_chip { + struct fti2c010_regs *regs; +}; + +static struct fti2c010_chip chip_list[] = { + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE, + }, +#ifdef CONFIG_FTI2C010_BASE1 + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE1, + }, +#endif +#ifdef CONFIG_FTI2C010_BASE2 + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE2, + }, +#endif +#ifdef CONFIG_FTI2C010_BASE3 + { + .regs = (struct fti2c010_regs *)CONFIG_FTI2C010_BASE3, + }, +#endif +}; + +static int fti2c010_reset(struct fti2c010_chip *chip) +{ + ulong ts; + int ret = -1; + struct fti2c010_regs *regs = chip->regs; + + writel(CR_I2CRST, ®s->cr); + for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) { + if (!(readl(®s->cr) & CR_I2CRST)) { + ret = 0; + break; + } + } + + if (ret) + printf("fti2c010: reset timeout\n"); + + return ret; +} + +static int fti2c010_wait(struct fti2c010_chip *chip, uint32_t mask) +{ + int ret = -1; + uint32_t stat, ts; + struct fti2c010_regs *regs = chip->regs; + + for (ts = get_timer(0); get_timer(ts) < CONFIG_FTI2C010_TIMEOUT; ) { + stat = readl(®s->sr); + if ((stat & mask) == mask) { + ret = 0; + break; + } + } + + return ret; +} + +static unsigned int set_i2c_bus_speed(struct fti2c010_chip *chip, + unsigned int speed) +{ + struct fti2c010_regs *regs = chip->regs; + unsigned int clk = CONFIG_FTI2C010_CLOCK; + unsigned int gsr = 0; + unsigned int tsr = 32; + unsigned int div, rate; + + for (div = 0; div < 0x3ffff; ++div) { + /* SCLout = PCLK/(2*(COUNT + 2) + GSR) */ + rate = clk / (2 * (div + 2) + gsr); + if (rate <= speed) + break; + } + + writel(TGSR_GSR(gsr) | TGSR_TSR(tsr), ®s->tgsr); + writel(CDR_DIV(div), ®s->cdr); + + return rate; +} + +/* + * Initialization, must be called once on start up, may be called + * repeatedly to change the speed and slave addresses. + */ +static void fti2c010_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + + if (adap->init_done) + return; + +#ifdef 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(); +#endif + + /* master init */ + + fti2c010_reset(chip); + + set_i2c_bus_speed(chip, speed); + + /* slave init, don't care */ + +#ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT + /* Call board specific i2c bus reset routine AFTER the bus has been + * initialized. Use either this callpoint or i2c_init_board; + * which is called before fti2c010_init operations. + * For details about this problem see doc/I2C_Edge_Conditions. + */ + i2c_board_late_init(); +#endif +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +static int fti2c010_probe(struct i2c_adapter *adap, u8 dev) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + struct fti2c010_regs *regs = chip->regs; + int ret; + + /* 1. Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_WR(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* 2. Select device register */ + writel(0, ®s->dr); + writel(CR_ENABLE | CR_TBEN, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + + return ret; +} + +static void to_i2c_addr(u8 *buf, uint32_t addr, int alen) +{ + int i, shift; + + if (!buf || alen <= 0) + return; + + /* MSB first */ + i = 0; + shift = (alen - 1) * 8; + while (alen-- > 0) { + buf[i] = (u8)(addr >> shift); + shift -= 8; + } +} + +static int fti2c010_read(struct i2c_adapter *adap, + u8 dev, uint addr, int alen, uchar *buf, int len) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + struct fti2c010_regs *regs = chip->regs; + int ret, pos; + uchar paddr[4] = { 0 }; + + to_i2c_addr(paddr, addr, alen); + + /* + * Phase A. Set register address + */ + + /* A.1 Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_WR(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* A.2 Select device register */ + for (pos = 0; pos < alen; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + writel(paddr[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + } + + /* + * Phase B. Get register data + */ + + /* B.1 Select slave device (7bits Address + 1bit R/W) */ + writel(I2C_RD(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* B.2 Get register data */ + for (pos = 0; pos < len; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + uint32_t stat = SR_DR; + + if (pos == len - 1) { + ctrl |= CR_NAK | CR_STOP; + stat |= SR_ACK; + } + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, stat); + if (ret) + break; + buf[pos] = (uchar)(readl(®s->dr) & 0xFF); + } + + return ret; +} + +static int fti2c010_write(struct i2c_adapter *adap, + u8 dev, uint addr, int alen, u8 *buf, int len) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + struct fti2c010_regs *regs = chip->regs; + int ret, pos; + uchar paddr[4] = { 0 }; + + to_i2c_addr(paddr, addr, alen); + + /* + * Phase A. Set register address + * + * A.1 Select slave device (7bits Address + 1bit R/W) + */ + writel(I2C_WR(dev), ®s->dr); + writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + + /* A.2 Select device register */ + for (pos = 0; pos < alen; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + writel(paddr[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + return ret; + } + + /* + * Phase B. Set register data + */ + for (pos = 0; pos < len; ++pos) { + uint32_t ctrl = CR_ENABLE | CR_TBEN; + + if (pos == len - 1) + ctrl |= CR_STOP; + writel(buf[pos], ®s->dr); + writel(ctrl, ®s->cr); + ret = fti2c010_wait(chip, SR_DT); + if (ret) + break; + } + + return ret; +} + +static unsigned int fti2c010_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct fti2c010_chip *chip = chip_list + adap->hwadapnr; + int ret; + + fti2c010_reset(chip); + ret = set_i2c_bus_speed(chip, speed); + + return ret; +} + +/* + * Register i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(i2c_0, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 0) +#ifdef CONFIG_FTI2C010_BASE1 +U_BOOT_I2C_ADAP_COMPLETE(i2c_1, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 1) +#endif +#ifdef CONFIG_FTI2C010_BASE2 +U_BOOT_I2C_ADAP_COMPLETE(i2c_2, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 2) +#endif +#ifdef CONFIG_FTI2C010_BASE3 +U_BOOT_I2C_ADAP_COMPLETE(i2c_3, fti2c010_init, fti2c010_probe, fti2c010_read, + fti2c010_write, fti2c010_set_bus_speed, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE, + 3) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/fti2c010.h b/qemu/roms/u-boot/drivers/i2c/fti2c010.h new file mode 100644 index 000000000..b9d0eb74a --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/fti2c010.h @@ -0,0 +1,80 @@ +/* + * Faraday I2C Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __FTI2C010_H +#define __FTI2C010_H + +/* + * FTI2C010 registers + */ +struct fti2c010_regs { + uint32_t cr; /* 0x00: control register */ + uint32_t sr; /* 0x04: status register */ + uint32_t cdr; /* 0x08: clock division register */ + uint32_t dr; /* 0x0c: data register */ + uint32_t sar; /* 0x10: slave address register */ + uint32_t tgsr;/* 0x14: time & glitch suppression register */ + uint32_t bmr; /* 0x18: bus monitor register */ + uint32_t rsvd[5]; + uint32_t revr;/* 0x30: revision register */ +}; + +/* + * control register + */ +#define CR_ALIRQ 0x2000 /* arbitration lost interrupt (master) */ +#define CR_SAMIRQ 0x1000 /* slave address match interrupt (slave) */ +#define CR_STOPIRQ 0x800 /* stop condition interrupt (slave) */ +#define CR_NAKRIRQ 0x400 /* NACK response interrupt (master) */ +#define CR_DRIRQ 0x200 /* rx interrupt (both) */ +#define CR_DTIRQ 0x100 /* tx interrupt (both) */ +#define CR_TBEN 0x80 /* tx enable (both) */ +#define CR_NAK 0x40 /* NACK (both) */ +#define CR_STOP 0x20 /* stop (master) */ +#define CR_START 0x10 /* start (master) */ +#define CR_GCEN 0x8 /* general call support (slave) */ +#define CR_SCLEN 0x4 /* enable clock out (master) */ +#define CR_I2CEN 0x2 /* enable I2C (both) */ +#define CR_I2CRST 0x1 /* reset I2C (both) */ +#define CR_ENABLE \ + (CR_ALIRQ | CR_NAKRIRQ | CR_DRIRQ | CR_DTIRQ | CR_SCLEN | CR_I2CEN) + +/* + * status register + */ +#define SR_CLRAL 0x400 /* clear arbitration lost */ +#define SR_CLRGC 0x200 /* clear general call */ +#define SR_CLRSAM 0x100 /* clear slave address match */ +#define SR_CLRSTOP 0x80 /* clear stop */ +#define SR_CLRNAKR 0x40 /* clear NACK respond */ +#define SR_DR 0x20 /* rx ready */ +#define SR_DT 0x10 /* tx done */ +#define SR_BB 0x8 /* bus busy */ +#define SR_BUSY 0x4 /* chip busy */ +#define SR_ACK 0x2 /* ACK/NACK received */ +#define SR_RW 0x1 /* set when master-rx or slave-tx mode */ + +/* + * clock division register + */ +#define CDR_DIV(n) ((n) & 0x3ffff) + +/* + * time & glitch suppression register + */ +#define TGSR_GSR(n) (((n) & 0x7) << 10) +#define TGSR_TSR(n) ((n) & 0x3ff) + +/* + * bus monitor register + */ +#define BMR_SCL 0x2 /* SCL is pull-up */ +#define BMR_SDA 0x1 /* SDA is pull-up */ + +#endif /* __FTI2C010_H */ diff --git a/qemu/roms/u-boot/drivers/i2c/i2c_core.c b/qemu/roms/u-boot/drivers/i2c/i2c_core.c new file mode 100644 index 000000000..18d673660 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/i2c_core.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2009 Sergey Kubushyn <ksi@koi8.net> + * + * (C) Copyright 2012 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * + * Multibus/multiadapter I2C core functions (wrappers) + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#include <common.h> +#include <i2c.h> + +struct i2c_adapter *i2c_get_adapter(int index) +{ + struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter, + i2c); + int max = ll_entry_count(struct i2c_adapter, i2c); + int i; + + if (index >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + index, max); + return i2c_adap_p; + } + if (index == 0) + return i2c_adap_p; + + for (i = 0; i < index; i++) + i2c_adap_p++; + + return i2c_adap_p; +} + +#if !defined(CONFIG_SYS_I2C_DIRECT_BUS) +struct i2c_bus_hose i2c_bus[CONFIG_SYS_NUM_I2C_BUSES] = + CONFIG_SYS_I2C_BUSES; +#endif + +DECLARE_GLOBAL_DATA_PTR; + +void i2c_reloc_fixup(void) +{ +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + struct i2c_adapter *i2c_adap_p = ll_entry_start(struct i2c_adapter, + i2c); + struct i2c_adapter *tmp = i2c_adap_p; + int max = ll_entry_count(struct i2c_adapter, i2c); + int i; + unsigned long addr; + + if (gd->reloc_off == 0) + return; + + for (i = 0; i < max; i++) { + /* i2c_init() */ + addr = (unsigned long)i2c_adap_p->init; + addr += gd->reloc_off; + i2c_adap_p->init = (void *)addr; + /* i2c_probe() */ + addr = (unsigned long)i2c_adap_p->probe; + addr += gd->reloc_off; + i2c_adap_p->probe = (void *)addr; + /* i2c_read() */ + addr = (unsigned long)i2c_adap_p->read; + addr += gd->reloc_off; + i2c_adap_p->read = (void *)addr; + /* i2c_write() */ + addr = (unsigned long)i2c_adap_p->write; + addr += gd->reloc_off; + i2c_adap_p->write = (void *)addr; + /* i2c_set_bus_speed() */ + addr = (unsigned long)i2c_adap_p->set_bus_speed; + addr += gd->reloc_off; + i2c_adap_p->set_bus_speed = (void *)addr; + /* name */ + addr = (unsigned long)i2c_adap_p->name; + addr += gd->reloc_off; + i2c_adap_p->name = (char *)addr; + tmp++; + i2c_adap_p = tmp; + } +#endif +} + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS +/* + * i2c_mux_set() + * ------------- + * + * This turns on the given channel on I2C multiplexer chip connected to + * a given I2C adapter directly or via other multiplexers. In the latter + * case the entire multiplexer chain must be initialized first starting + * with the one connected directly to the adapter. When disabling a chain + * muxes must be programmed in reverse order, starting with the one + * farthest from the adapter. + * + * mux_id is the multiplexer chip type from defined in i2c.h. So far only + * NXP (Philips) PCA954x multiplexers are supported. Switches are NOT + * supported (anybody uses them?) + */ + +static int i2c_mux_set(struct i2c_adapter *adap, int mux_id, int chip, + int channel) +{ + uint8_t buf; + int ret; + + /* channel < 0 - turn off the mux */ + if (channel < 0) { + buf = 0; + ret = adap->write(adap, chip, 0, 0, &buf, 1); + if (ret) + printf("%s: Could not turn off the mux.\n", __func__); + return ret; + } + + switch (mux_id) { + case I2C_MUX_PCA9540_ID: + case I2C_MUX_PCA9542_ID: + if (channel > 1) + return -1; + buf = (uint8_t)((channel & 0x01) | (1 << 2)); + break; + case I2C_MUX_PCA9544_ID: + if (channel > 3) + return -1; + buf = (uint8_t)((channel & 0x03) | (1 << 2)); + break; + case I2C_MUX_PCA9547_ID: + if (channel > 7) + return -1; + buf = (uint8_t)((channel & 0x07) | (1 << 3)); + break; + case I2C_MUX_PCA9548_ID: + if (channel > 7) + return -1; + buf = (uint8_t)(0x01 << channel); + break; + default: + printf("%s: wrong mux id: %d\n", __func__, mux_id); + return -1; + } + + ret = adap->write(adap, chip, 0, 0, &buf, 1); + if (ret) + printf("%s: could not set mux: id: %d chip: %x channel: %d\n", + __func__, mux_id, chip, channel); + return ret; +} + +static int i2c_mux_set_all(void) +{ + struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; + int i; + + /* Connect requested bus if behind muxes */ + if (i2c_bus_tmp->next_hop[0].chip != 0) { + /* Set all muxes along the path to that bus */ + for (i = 0; i < CONFIG_SYS_I2C_MAX_HOPS; i++) { + int ret; + + if (i2c_bus_tmp->next_hop[i].chip == 0) + break; + + ret = i2c_mux_set(I2C_ADAP, + i2c_bus_tmp->next_hop[i].mux.id, + i2c_bus_tmp->next_hop[i].chip, + i2c_bus_tmp->next_hop[i].channel); + if (ret != 0) + return ret; + } + } + return 0; +} + +static int i2c_mux_disconnet_all(void) +{ + struct i2c_bus_hose *i2c_bus_tmp = &i2c_bus[I2C_BUS]; + int i; + uint8_t buf; + + if (I2C_ADAP->init_done == 0) + return 0; + + /* Disconnect current bus (turn off muxes if any) */ + if ((i2c_bus_tmp->next_hop[0].chip != 0) && + (I2C_ADAP->init_done != 0)) { + i = CONFIG_SYS_I2C_MAX_HOPS; + do { + uint8_t chip; + int ret; + + chip = i2c_bus_tmp->next_hop[--i].chip; + if (chip == 0) + continue; + + ret = I2C_ADAP->write(I2C_ADAP, chip, 0, 0, &buf, 1); + if (ret != 0) { + printf("i2c: mux diconnect error\n"); + return ret; + } + } while (i > 0); + } + + return 0; +} +#endif + +/* + * i2c_init_bus(): + * --------------- + * + * Initializes one bus. Will initialize the parent adapter. No current bus + * changes, no mux (if any) setup. + */ +static void i2c_init_bus(unsigned int bus_no, int speed, int slaveaddr) +{ + if (bus_no >= CONFIG_SYS_NUM_I2C_BUSES) + return; + + I2C_ADAP->init(I2C_ADAP, speed, slaveaddr); + + if (gd->flags & GD_FLG_RELOC) { + I2C_ADAP->init_done = 1; + I2C_ADAP->speed = speed; + I2C_ADAP->slaveaddr = slaveaddr; + } +} + +/* implement possible board specific board init */ +static void __def_i2c_init_board(void) +{ +} +void i2c_init_board(void) + __attribute__((weak, alias("__def_i2c_init_board"))); + +/* + * i2c_init_all(): + * + * not longer needed, will deleted. Actual init the SPD_BUS + * for compatibility. + * i2c_adap[] must be initialized beforehead with function pointers and + * data, including speed and slaveaddr. + */ +void i2c_init_all(void) +{ + i2c_init_board(); + i2c_set_bus_num(CONFIG_SYS_SPD_BUS_NUM); + return; +} + +/* + * i2c_get_bus_num(): + * ------------------ + * + * Returns index of currently active I2C bus. Zero-based. + */ +unsigned int i2c_get_bus_num(void) +{ + return gd->cur_i2c_bus; +} + +/* + * i2c_set_bus_num(): + * ------------------ + * + * Change the active I2C bus. Subsequent read/write calls will + * go to this one. Sets all of the muxes in a proper condition + * if that bus is behind muxes. + * If previously selected bus is behind the muxes turns off all the + * muxes along the path to that bus. + * + * bus - bus index, zero based + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_set_bus_num(unsigned int bus) +{ + int max; + + if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) + return 0; + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + if (bus >= CONFIG_SYS_NUM_I2C_BUSES) + return -1; +#endif + + max = ll_entry_count(struct i2c_adapter, i2c); + if (I2C_ADAPTER(bus) >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + I2C_ADAPTER(bus), max); + return -2; + } + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + i2c_mux_disconnet_all(); +#endif + + gd->cur_i2c_bus = bus; + if (I2C_ADAP->init_done == 0) + i2c_init_bus(bus, I2C_ADAP->speed, I2C_ADAP->slaveaddr); + +#ifndef CONFIG_SYS_I2C_DIRECT_BUS + i2c_mux_set_all(); +#endif + return 0; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uint8_t chip) +{ + return I2C_ADAP->probe(I2C_ADAP, chip); +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uint8_t chip, unsigned int addr, int alen, + uint8_t *buffer, int len) +{ + return I2C_ADAP->read(I2C_ADAP, chip, addr, alen, buffer, len); +} + +int i2c_write(uint8_t chip, unsigned int addr, int alen, + uint8_t *buffer, int len) +{ + return I2C_ADAP->write(I2C_ADAP, chip, addr, alen, buffer, len); +} + +unsigned int i2c_set_bus_speed(unsigned int speed) +{ + unsigned int ret; + + if (I2C_ADAP->set_bus_speed == NULL) + return 0; + ret = I2C_ADAP->set_bus_speed(I2C_ADAP, speed); + if (gd->flags & GD_FLG_RELOC) + I2C_ADAP->speed = (ret == 0) ? speed : 0; + + return ret; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct i2c_adapter *cur = I2C_ADAP; + return cur->speed; +} + +uint8_t i2c_reg_read(uint8_t addr, uint8_t reg) +{ + uint8_t buf; + +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + /* maybe it is now the time for it ... */ + i2c_set_bus_num(i2c_get_bus_num()); +#endif + i2c_read(addr, reg, 1, &buf, 1); + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_get_bus_num(), addr, reg, buf); +#endif + + return buf; +} + +void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val) +{ +#ifdef CONFIG_8xx + /* MPC8xx needs this. Maybe one day we can get rid of it. */ + /* maybe it is now the time for it ... */ + i2c_set_bus_num(i2c_get_bus_num()); +#endif + +#ifdef DEBUG + printf("%s: bus=%d addr=0x%02x, reg=0x%02x, val=0x%02x\n", + __func__, i2c_get_bus_num(), addr, reg, val); +#endif + + i2c_write(addr, reg, 1, &val, 1); +} + +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/qemu/roms/u-boot/drivers/i2c/kona_i2c.c b/qemu/roms/u-boot/drivers/i2c/kona_i2c.c new file mode 100644 index 000000000..0b1715abf --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/kona_i2c.c @@ -0,0 +1,730 @@ +/* + * Copyright 2013 Broadcom Corporation. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/sysmap.h> +#include <asm/kona-common/clk.h> +#include <i2c.h> + +/* Hardware register offsets and field defintions */ +#define CS_OFFSET 0x00000020 +#define CS_ACK_SHIFT 3 +#define CS_ACK_MASK 0x00000008 +#define CS_ACK_CMD_GEN_START 0x00000000 +#define CS_ACK_CMD_GEN_RESTART 0x00000001 +#define CS_CMD_SHIFT 1 +#define CS_CMD_CMD_NO_ACTION 0x00000000 +#define CS_CMD_CMD_START_RESTART 0x00000001 +#define CS_CMD_CMD_STOP 0x00000002 +#define CS_EN_SHIFT 0 +#define CS_EN_CMD_ENABLE_BSC 0x00000001 + +#define TIM_OFFSET 0x00000024 +#define TIM_PRESCALE_SHIFT 6 +#define TIM_P_SHIFT 3 +#define TIM_NO_DIV_SHIFT 2 +#define TIM_DIV_SHIFT 0 + +#define DAT_OFFSET 0x00000028 + +#define TOUT_OFFSET 0x0000002c + +#define TXFCR_OFFSET 0x0000003c +#define TXFCR_FIFO_FLUSH_MASK 0x00000080 +#define TXFCR_FIFO_EN_MASK 0x00000040 + +#define IER_OFFSET 0x00000044 +#define IER_READ_COMPLETE_INT_MASK 0x00000010 +#define IER_I2C_INT_EN_MASK 0x00000008 +#define IER_FIFO_INT_EN_MASK 0x00000002 +#define IER_NOACK_EN_MASK 0x00000001 + +#define ISR_OFFSET 0x00000048 +#define ISR_RESERVED_MASK 0xffffff60 +#define ISR_CMDBUSY_MASK 0x00000080 +#define ISR_READ_COMPLETE_MASK 0x00000010 +#define ISR_SES_DONE_MASK 0x00000008 +#define ISR_ERR_MASK 0x00000004 +#define ISR_TXFIFOEMPTY_MASK 0x00000002 +#define ISR_NOACK_MASK 0x00000001 + +#define CLKEN_OFFSET 0x0000004c +#define CLKEN_AUTOSENSE_OFF_MASK 0x00000080 +#define CLKEN_M_SHIFT 4 +#define CLKEN_N_SHIFT 1 +#define CLKEN_CLKEN_MASK 0x00000001 + +#define FIFO_STATUS_OFFSET 0x00000054 +#define FIFO_STATUS_RXFIFO_EMPTY_MASK 0x00000004 +#define FIFO_STATUS_TXFIFO_EMPTY_MASK 0x00000010 + +#define HSTIM_OFFSET 0x00000058 +#define HSTIM_HS_MODE_MASK 0x00008000 +#define HSTIM_HS_HOLD_SHIFT 10 +#define HSTIM_HS_HIGH_PHASE_SHIFT 5 +#define HSTIM_HS_SETUP_SHIFT 0 + +#define PADCTL_OFFSET 0x0000005c +#define PADCTL_PAD_OUT_EN_MASK 0x00000004 + +#define RXFCR_OFFSET 0x00000068 +#define RXFCR_NACK_EN_SHIFT 7 +#define RXFCR_READ_COUNT_SHIFT 0 +#define RXFIFORDOUT_OFFSET 0x0000006c + +/* Locally used constants */ +#define MAX_RX_FIFO_SIZE 64U /* bytes */ +#define MAX_TX_FIFO_SIZE 64U /* bytes */ + +#define I2C_TIMEOUT 100000 /* usecs */ + +#define WAIT_INT_CHK 100 /* usecs */ +#if I2C_TIMEOUT % WAIT_INT_CHK +#error I2C_TIMEOUT must be a multiple of WAIT_INT_CHK +#endif + +/* Operations that can be commanded to the controller */ +enum bcm_kona_cmd_t { + BCM_CMD_NOACTION = 0, + BCM_CMD_START, + BCM_CMD_RESTART, + BCM_CMD_STOP, +}; + +enum bus_speed_index { + BCM_SPD_100K = 0, + BCM_SPD_400K, + BCM_SPD_1MHZ, +}; + +/* Internal divider settings for standard mode, fast mode and fast mode plus */ +struct bus_speed_cfg { + uint8_t time_m; /* Number of cycles for setup time */ + uint8_t time_n; /* Number of cycles for hold time */ + uint8_t prescale; /* Prescale divider */ + uint8_t time_p; /* Timing coefficient */ + uint8_t no_div; /* Disable clock divider */ + uint8_t time_div; /* Post-prescale divider */ +}; + +static const struct bus_speed_cfg std_cfg_table[] = { + [BCM_SPD_100K] = {0x01, 0x01, 0x03, 0x06, 0x00, 0x02}, + [BCM_SPD_400K] = {0x05, 0x01, 0x03, 0x05, 0x01, 0x02}, + [BCM_SPD_1MHZ] = {0x01, 0x01, 0x03, 0x01, 0x01, 0x03}, +}; + +struct bcm_kona_i2c_dev { + void *base; + uint speed; + const struct bus_speed_cfg *std_cfg; +}; + +/* Keep these two defines in sync */ +#define DEF_SPD 100000 +#define DEF_SPD_ENUM BCM_SPD_100K + +#define DEF_DEVICE(num) \ +{(void *)CONFIG_SYS_I2C_BASE##num, DEF_SPD, &std_cfg_table[DEF_SPD_ENUM]} + +static struct bcm_kona_i2c_dev g_i2c_devs[CONFIG_SYS_MAX_I2C_BUS] = { +#ifdef CONFIG_SYS_I2C_BASE0 + DEF_DEVICE(0), +#endif +#ifdef CONFIG_SYS_I2C_BASE1 + DEF_DEVICE(1), +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + DEF_DEVICE(2), +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + DEF_DEVICE(3), +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + DEF_DEVICE(4), +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + DEF_DEVICE(5), +#endif +}; + +#define I2C_M_TEN 0x0010 /* ten bit address */ +#define I2C_M_RD 0x0001 /* read data */ +#define I2C_M_NOSTART 0x4000 /* no restart between msgs */ + +struct i2c_msg { + uint16_t addr; + uint16_t flags; + uint16_t len; + uint8_t *buf; +}; + +static void bcm_kona_i2c_send_cmd_to_ctrl(struct bcm_kona_i2c_dev *dev, + enum bcm_kona_cmd_t cmd) +{ + debug("%s, %d\n", __func__, cmd); + + switch (cmd) { + case BCM_CMD_NOACTION: + writel((CS_CMD_CMD_NO_ACTION << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_START: + writel((CS_ACK_CMD_GEN_START << CS_ACK_SHIFT) | + (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_RESTART: + writel((CS_ACK_CMD_GEN_RESTART << CS_ACK_SHIFT) | + (CS_CMD_CMD_START_RESTART << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + case BCM_CMD_STOP: + writel((CS_CMD_CMD_STOP << CS_CMD_SHIFT) | + (CS_EN_CMD_ENABLE_BSC << CS_EN_SHIFT), + dev->base + CS_OFFSET); + break; + + default: + printf("Unknown command %d\n", cmd); + } +} + +static void bcm_kona_i2c_enable_clock(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) | CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_disable_clock(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_CLKEN_MASK, + dev->base + CLKEN_OFFSET); +} + +/* Wait until at least one of the mask bit(s) are set */ +static unsigned long wait_for_int_timeout(struct bcm_kona_i2c_dev *dev, + unsigned long time_left, + uint32_t mask) +{ + uint32_t status; + + while (time_left) { + status = readl(dev->base + ISR_OFFSET); + + if ((status & ~ISR_RESERVED_MASK) == 0) { + debug("Bogus I2C interrupt 0x%x\n", status); + continue; + } + + /* Must flush the TX FIFO when NAK detected */ + if (status & ISR_NOACK_MASK) + writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK, + dev->base + TXFCR_OFFSET); + + writel(status & ~ISR_RESERVED_MASK, dev->base + ISR_OFFSET); + + if (status & mask) { + /* We are done since one of the mask bits are set */ + return time_left; + } + udelay(WAIT_INT_CHK); + time_left -= WAIT_INT_CHK; + } + return 0; +} + +/* Send command to I2C bus */ +static int bcm_kona_send_i2c_cmd(struct bcm_kona_i2c_dev *dev, + enum bcm_kona_cmd_t cmd) +{ + int rc = 0; + unsigned long time_left = I2C_TIMEOUT; + + /* Send the command */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, cmd); + + /* Wait for transaction to finish or timeout */ + time_left = wait_for_int_timeout(dev, time_left, IER_I2C_INT_EN_MASK); + + if (!time_left) { + printf("controller timed out\n"); + rc = -ETIMEDOUT; + } + + /* Clear command */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION); + + return rc; +} + +/* Read a single RX FIFO worth of data from the i2c bus */ +static int bcm_kona_i2c_read_fifo_single(struct bcm_kona_i2c_dev *dev, + uint8_t *buf, unsigned int len, + unsigned int last_byte_nak) +{ + unsigned long time_left = I2C_TIMEOUT; + + /* Start the RX FIFO */ + writel((last_byte_nak << RXFCR_NACK_EN_SHIFT) | + (len << RXFCR_READ_COUNT_SHIFT), dev->base + RXFCR_OFFSET); + + /* Wait for FIFO read to complete */ + time_left = + wait_for_int_timeout(dev, time_left, IER_READ_COMPLETE_INT_MASK); + + if (!time_left) { + printf("RX FIFO time out\n"); + return -EREMOTEIO; + } + + /* Read data from FIFO */ + for (; len > 0; len--, buf++) + *buf = readl(dev->base + RXFIFORDOUT_OFFSET); + + return 0; +} + +/* Read any amount of data using the RX FIFO from the i2c bus */ +static int bcm_kona_i2c_read_fifo(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned int bytes_to_read = MAX_RX_FIFO_SIZE; + unsigned int last_byte_nak = 0; + unsigned int bytes_read = 0; + int rc; + + uint8_t *tmp_buf = msg->buf; + + while (bytes_read < msg->len) { + if (msg->len - bytes_read <= MAX_RX_FIFO_SIZE) { + last_byte_nak = 1; /* NAK last byte of transfer */ + bytes_to_read = msg->len - bytes_read; + } + + rc = bcm_kona_i2c_read_fifo_single(dev, tmp_buf, bytes_to_read, + last_byte_nak); + if (rc < 0) + return -EREMOTEIO; + + bytes_read += bytes_to_read; + tmp_buf += bytes_to_read; + } + + return 0; +} + +/* Write a single byte of data to the i2c bus */ +static int bcm_kona_i2c_write_byte(struct bcm_kona_i2c_dev *dev, uint8_t data, + unsigned int nak_expected) +{ + unsigned long time_left = I2C_TIMEOUT; + unsigned int nak_received; + + /* Clear pending session done interrupt */ + writel(ISR_SES_DONE_MASK, dev->base + ISR_OFFSET); + + /* Send one byte of data */ + writel(data, dev->base + DAT_OFFSET); + + time_left = wait_for_int_timeout(dev, time_left, IER_I2C_INT_EN_MASK); + + if (!time_left) { + debug("controller timed out\n"); + return -ETIMEDOUT; + } + + nak_received = readl(dev->base + CS_OFFSET) & CS_ACK_MASK ? 1 : 0; + + if (nak_received ^ nak_expected) { + debug("unexpected NAK/ACK\n"); + return -EREMOTEIO; + } + + return 0; +} + +/* Write a single TX FIFO worth of data to the i2c bus */ +static int bcm_kona_i2c_write_fifo_single(struct bcm_kona_i2c_dev *dev, + uint8_t *buf, unsigned int len) +{ + int k; + unsigned long time_left = I2C_TIMEOUT; + unsigned int fifo_status; + + /* Write data into FIFO */ + for (k = 0; k < len; k++) + writel(buf[k], (dev->base + DAT_OFFSET)); + + /* Wait for FIFO to empty */ + do { + time_left = + wait_for_int_timeout(dev, time_left, + (IER_FIFO_INT_EN_MASK | + IER_NOACK_EN_MASK)); + fifo_status = readl(dev->base + FIFO_STATUS_OFFSET); + } while (time_left && !(fifo_status & FIFO_STATUS_TXFIFO_EMPTY_MASK)); + + /* Check if there was a NAK */ + if (readl(dev->base + CS_OFFSET) & CS_ACK_MASK) { + printf("unexpected NAK\n"); + return -EREMOTEIO; + } + + /* Check if a timeout occured */ + if (!time_left) { + printf("completion timed out\n"); + return -EREMOTEIO; + } + + return 0; +} + +/* Write any amount of data using TX FIFO to the i2c bus */ +static int bcm_kona_i2c_write_fifo(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned int bytes_to_write = MAX_TX_FIFO_SIZE; + unsigned int bytes_written = 0; + int rc; + + uint8_t *tmp_buf = msg->buf; + + while (bytes_written < msg->len) { + if (msg->len - bytes_written <= MAX_TX_FIFO_SIZE) + bytes_to_write = msg->len - bytes_written; + + rc = bcm_kona_i2c_write_fifo_single(dev, tmp_buf, + bytes_to_write); + if (rc < 0) + return -EREMOTEIO; + + bytes_written += bytes_to_write; + tmp_buf += bytes_to_write; + } + + return 0; +} + +/* Send i2c address */ +static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev, + struct i2c_msg *msg) +{ + unsigned char addr; + + if (msg->flags & I2C_M_TEN) { + /* First byte is 11110XX0 where XX is upper 2 bits */ + addr = 0xf0 | ((msg->addr & 0x300) >> 7); + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + + /* Second byte is the remaining 8 bits */ + addr = msg->addr & 0xff; + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + + if (msg->flags & I2C_M_RD) { + /* For read, send restart command */ + if (bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART) < 0) + return -EREMOTEIO; + + /* Then re-send the first byte with the read bit set */ + addr = 0xf0 | ((msg->addr & 0x300) >> 7) | 0x01; + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + } + } else { + addr = msg->addr << 1; + + if (msg->flags & I2C_M_RD) + addr |= 1; + + if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0) + return -EREMOTEIO; + } + + return 0; +} + +static void bcm_kona_i2c_enable_autosense(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + CLKEN_OFFSET) & ~CLKEN_AUTOSENSE_OFF_MASK, + dev->base + CLKEN_OFFSET); +} + +static void bcm_kona_i2c_config_timing(struct bcm_kona_i2c_dev *dev) +{ + writel(readl(dev->base + HSTIM_OFFSET) & ~HSTIM_HS_MODE_MASK, + dev->base + HSTIM_OFFSET); + + writel((dev->std_cfg->prescale << TIM_PRESCALE_SHIFT) | + (dev->std_cfg->time_p << TIM_P_SHIFT) | + (dev->std_cfg->no_div << TIM_NO_DIV_SHIFT) | + (dev->std_cfg->time_div << TIM_DIV_SHIFT), + dev->base + TIM_OFFSET); + + writel((dev->std_cfg->time_m << CLKEN_M_SHIFT) | + (dev->std_cfg->time_n << CLKEN_N_SHIFT) | + CLKEN_CLKEN_MASK, dev->base + CLKEN_OFFSET); +} + +/* Master transfer function */ +static int bcm_kona_i2c_xfer(struct bcm_kona_i2c_dev *dev, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + int rc = 0; + int i; + + /* Enable pad output */ + writel(0, dev->base + PADCTL_OFFSET); + + /* Enable internal clocks */ + bcm_kona_i2c_enable_clock(dev); + + /* Send start command */ + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_START); + if (rc < 0) { + printf("Start command failed rc = %d\n", rc); + goto xfer_disable_pad; + } + + /* Loop through all messages */ + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + + /* Send restart for subsequent messages */ + if ((i != 0) && ((pmsg->flags & I2C_M_NOSTART) == 0)) { + rc = bcm_kona_send_i2c_cmd(dev, BCM_CMD_RESTART); + if (rc < 0) { + printf("restart cmd failed rc = %d\n", rc); + goto xfer_send_stop; + } + } + + /* Send slave address */ + if (!(pmsg->flags & I2C_M_NOSTART)) { + rc = bcm_kona_i2c_do_addr(dev, pmsg); + if (rc < 0) { + debug("NAK from addr %2.2x msg#%d rc = %d\n", + pmsg->addr, i, rc); + goto xfer_send_stop; + } + } + + /* Perform data transfer */ + if (pmsg->flags & I2C_M_RD) { + rc = bcm_kona_i2c_read_fifo(dev, pmsg); + if (rc < 0) { + printf("read failure\n"); + goto xfer_send_stop; + } + } else { + rc = bcm_kona_i2c_write_fifo(dev, pmsg); + if (rc < 0) { + printf("write failure"); + goto xfer_send_stop; + } + } + } + + rc = num; + +xfer_send_stop: + /* Send a STOP command */ + bcm_kona_send_i2c_cmd(dev, BCM_CMD_STOP); + +xfer_disable_pad: + /* Disable pad output */ + writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET); + + /* Stop internal clock */ + bcm_kona_i2c_disable_clock(dev); + + return rc; +} + +static uint bcm_kona_i2c_assign_bus_speed(struct bcm_kona_i2c_dev *dev, + uint speed) +{ + switch (speed) { + case 100000: + dev->std_cfg = &std_cfg_table[BCM_SPD_100K]; + break; + case 400000: + dev->std_cfg = &std_cfg_table[BCM_SPD_400K]; + break; + case 1000000: + dev->std_cfg = &std_cfg_table[BCM_SPD_1MHZ]; + break; + default: + printf("%d hz bus speed not supported\n", speed); + return -EINVAL; + } + dev->speed = speed; + return 0; +} + +static void bcm_kona_i2c_init(struct bcm_kona_i2c_dev *dev) +{ + /* Parse bus speed */ + bcm_kona_i2c_assign_bus_speed(dev, dev->speed); + + /* Enable internal clocks */ + bcm_kona_i2c_enable_clock(dev); + + /* Configure internal dividers */ + bcm_kona_i2c_config_timing(dev); + + /* Disable timeout */ + writel(0, dev->base + TOUT_OFFSET); + + /* Enable autosense */ + bcm_kona_i2c_enable_autosense(dev); + + /* Enable TX FIFO */ + writel(TXFCR_FIFO_FLUSH_MASK | TXFCR_FIFO_EN_MASK, + dev->base + TXFCR_OFFSET); + + /* Mask all interrupts */ + writel(0, dev->base + IER_OFFSET); + + /* Clear all pending interrupts */ + writel(ISR_CMDBUSY_MASK | + ISR_READ_COMPLETE_MASK | + ISR_SES_DONE_MASK | + ISR_ERR_MASK | + ISR_TXFIFOEMPTY_MASK | ISR_NOACK_MASK, dev->base + ISR_OFFSET); + + /* Enable the controller but leave it idle */ + bcm_kona_i2c_send_cmd_to_ctrl(dev, BCM_CMD_NOACTION); + + /* Disable pad output */ + writel(PADCTL_PAD_OUT_EN_MASK, dev->base + PADCTL_OFFSET); +} + +/* + * uboot layer + */ +struct bcm_kona_i2c_dev *kona_get_dev(struct i2c_adapter *adap) +{ + return &g_i2c_devs[adap->hwadapnr]; +} + +static void kona_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + + if (clk_bsc_enable(dev->base)) + return; + + bcm_kona_i2c_init(dev); +} + +static int kona_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + /* msg[0] writes the addr, msg[1] reads the data */ + struct i2c_msg msg[2]; + unsigned char msgbuf0[64]; + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + + msg[0].addr = chip; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = msgbuf0; /* msgbuf0 contains incrementing reg addr */ + + msg[1].addr = chip; + msg[1].flags = I2C_M_RD; + /* msg[1].buf dest ptr increments each read */ + + msgbuf0[0] = (unsigned char)addr; + msg[1].buf = buffer; + msg[1].len = len; + if (bcm_kona_i2c_xfer(dev, msg, 2) < 0) { + /* Sending 2 i2c messages */ + kona_i2c_init(adap, adap->speed, adap->slaveaddr); + debug("I2C read: I/O error\n"); + return -EIO; + } + return 0; +} + +static int kona_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c_msg msg[0]; + unsigned char msgbuf0[64]; + unsigned int i; + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + + msg[0].addr = chip; + msg[0].flags = 0; + msg[0].len = 2; /* addr byte plus data */ + msg[0].buf = msgbuf0; + + for (i = 0; i < len; i++) { + msgbuf0[0] = addr++; + msgbuf0[1] = buffer[i]; + if (bcm_kona_i2c_xfer(dev, msg, 1) < 0) { + kona_i2c_init(adap, adap->speed, adap->slaveaddr); + debug("I2C write: I/O error\n"); + return -EIO; + } + } + return 0; +} + +static int kona_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + uchar tmp; + + /* + * read addr 0x0 of the given chip. + */ + return kona_i2c_read(adap, chip, 0x0, 1, &tmp, 1); +} + +static uint kona_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed) +{ + struct bcm_kona_i2c_dev *dev = kona_get_dev(adap); + return bcm_kona_i2c_assign_bus_speed(dev, speed); +} + +/* + * Register kona i2c adapters. Keep the order below so + * that the bus number matches the adapter number. + */ +#define DEF_ADAPTER(num) \ +U_BOOT_I2C_ADAP_COMPLETE(kona##num, kona_i2c_init, kona_i2c_probe, \ + kona_i2c_read, kona_i2c_write, \ + kona_i2c_set_bus_speed, DEF_SPD, 0x00, num) + +#ifdef CONFIG_SYS_I2C_BASE0 + DEF_ADAPTER(0) +#endif +#ifdef CONFIG_SYS_I2C_BASE1 + DEF_ADAPTER(1) +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + DEF_ADAPTER(2) +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + DEF_ADAPTER(3) +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + DEF_ADAPTER(4) +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + DEF_ADAPTER(5) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/mv_i2c.c b/qemu/roms/u-boot/drivers/i2c/mv_i2c.c new file mode 100644 index 000000000..dac346334 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mv_i2c.c @@ -0,0 +1,471 @@ +/* + * (C) Copyright 2000 + * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it + * + * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * (C) Copyright 2003 Pengutronix e.K. + * Robert Schwebel <r.schwebel@pengutronix.de> + * + * (C) Copyright 2011 Marvell Inc. + * Lei Wen <leiwen@marvell.com> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include <common.h> +#include <asm/io.h> + +#ifdef CONFIG_HARD_I2C +#include <i2c.h> +#include "mv_i2c.h" + +#ifdef DEBUG_I2C +#define PRINTD(x) printf x +#else +#define PRINTD(x) +#endif + +/* All transfers are described by this data structure */ +struct i2c_msg { + u8 condition; + u8 acknack; + u8 direction; + u8 data; +}; + +struct mv_i2c { + u32 ibmr; + u32 pad0; + u32 idbr; + u32 pad1; + u32 icr; + u32 pad2; + u32 isr; + u32 pad3; + u32 isar; +}; + +static struct mv_i2c *base; +static void i2c_board_init(struct mv_i2c *base) +{ +#ifdef CONFIG_SYS_I2C_INIT_BOARD + u32 icr; + /* + * 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. + * + * disable I2C controller first, otherwhise it thinks we want to + * talk to the slave port... + */ + icr = readl(&base->icr); + writel(readl(&base->icr) & ~(ICR_SCLE | ICR_IUE), &base->icr); + + i2c_init_board(); + + writel(icr, &base->icr); +#endif +} + +#ifdef CONFIG_I2C_MULTI_BUS +static u32 i2c_regs[CONFIG_MV_I2C_NUM] = CONFIG_MV_I2C_REG; +static unsigned int bus_initialized[CONFIG_MV_I2C_NUM]; +static unsigned int current_bus; + +int i2c_set_bus_num(unsigned int bus) +{ + if ((bus < 0) || (bus >= CONFIG_MV_I2C_NUM)) { + printf("Bad bus: %d\n", bus); + return -1; + } + + base = (struct mv_i2c *)i2c_regs[bus]; + current_bus = bus; + + if (!bus_initialized[current_bus]) { + i2c_board_init(base); + bus_initialized[current_bus] = 1; + } + + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif + +/* + * i2c_reset: - reset the host controller + * + */ +static void i2c_reset(void) +{ + writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */ + writel(readl(&base->icr) | ICR_UR, &base->icr); /* reset the unit */ + udelay(100); + writel(readl(&base->icr) & ~ICR_IUE, &base->icr); /* disable unit */ + + i2c_clk_enable(); + + writel(CONFIG_SYS_I2C_SLAVE, &base->isar); /* set our slave address */ + writel(I2C_ICR_INIT, &base->icr); /* set control reg values */ + writel(I2C_ISR_INIT, &base->isr); /* set clear interrupt bits */ + writel(readl(&base->icr) | ICR_IUE, &base->icr); /* enable unit */ + udelay(100); +} + +/* + * i2c_isr_set_cleared: - wait until certain bits of the I2C status register + * are set and cleared + * + * @return: 1 in case of success, 0 means timeout (no match within 10 ms). + */ +static int i2c_isr_set_cleared(unsigned long set_mask, + unsigned long cleared_mask) +{ + int timeout = 1000, isr; + + do { + isr = readl(&base->isr); + udelay(10); + if (timeout-- < 0) + return 0; + } while (((isr & set_mask) != set_mask) + || ((isr & cleared_mask) != 0)); + + return 1; +} + +/* + * i2c_transfer: - Transfer one byte over the i2c bus + * + * This function can tranfer a byte over the i2c bus in both directions. + * It is used by the public API functions. + * + * @return: 0: transfer successful + * -1: message is empty + * -2: transmit timeout + * -3: ACK missing + * -4: receive timeout + * -5: illegal parameters + * -6: bus is busy and couldn't be aquired + */ +int i2c_transfer(struct i2c_msg *msg) +{ + int ret; + + if (!msg) + goto transfer_error_msg_empty; + + switch (msg->direction) { + case I2C_WRITE: + /* check if bus is not busy */ + if (!i2c_isr_set_cleared(0, ISR_IBB)) + goto transfer_error_bus_busy; + + /* start transmission */ + writel(readl(&base->icr) & ~ICR_START, &base->icr); + writel(readl(&base->icr) & ~ICR_STOP, &base->icr); + writel(msg->data, &base->idbr); + if (msg->condition == I2C_COND_START) + writel(readl(&base->icr) | ICR_START, &base->icr); + if (msg->condition == I2C_COND_STOP) + writel(readl(&base->icr) | ICR_STOP, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDNAK) + writel(readl(&base->icr) | ICR_ACKNAK, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDACK) + writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr); + writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr); + writel(readl(&base->icr) | ICR_TB, &base->icr); + + /* transmit register empty? */ + if (!i2c_isr_set_cleared(ISR_ITE, 0)) + goto transfer_error_transmit_timeout; + + /* clear 'transmit empty' state */ + writel(readl(&base->isr) | ISR_ITE, &base->isr); + + /* wait for ACK from slave */ + if (msg->acknack == I2C_ACKNAK_WAITACK) + if (!i2c_isr_set_cleared(0, ISR_ACKNAK)) + goto transfer_error_ack_missing; + break; + + case I2C_READ: + + /* check if bus is not busy */ + if (!i2c_isr_set_cleared(0, ISR_IBB)) + goto transfer_error_bus_busy; + + /* start receive */ + writel(readl(&base->icr) & ~ICR_START, &base->icr); + writel(readl(&base->icr) & ~ICR_STOP, &base->icr); + if (msg->condition == I2C_COND_START) + writel(readl(&base->icr) | ICR_START, &base->icr); + if (msg->condition == I2C_COND_STOP) + writel(readl(&base->icr) | ICR_STOP, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDNAK) + writel(readl(&base->icr) | ICR_ACKNAK, &base->icr); + if (msg->acknack == I2C_ACKNAK_SENDACK) + writel(readl(&base->icr) & ~ICR_ACKNAK, &base->icr); + writel(readl(&base->icr) & ~ICR_ALDIE, &base->icr); + writel(readl(&base->icr) | ICR_TB, &base->icr); + + /* receive register full? */ + if (!i2c_isr_set_cleared(ISR_IRF, 0)) + goto transfer_error_receive_timeout; + + msg->data = readl(&base->idbr); + + /* clear 'receive empty' state */ + writel(readl(&base->isr) | ISR_IRF, &base->isr); + break; + default: + goto transfer_error_illegal_param; + } + + return 0; + +transfer_error_msg_empty: + PRINTD(("i2c_transfer: error: 'msg' is empty\n")); + ret = -1; goto i2c_transfer_finish; + +transfer_error_transmit_timeout: + PRINTD(("i2c_transfer: error: transmit timeout\n")); + ret = -2; goto i2c_transfer_finish; + +transfer_error_ack_missing: + PRINTD(("i2c_transfer: error: ACK missing\n")); + ret = -3; goto i2c_transfer_finish; + +transfer_error_receive_timeout: + PRINTD(("i2c_transfer: error: receive timeout\n")); + ret = -4; goto i2c_transfer_finish; + +transfer_error_illegal_param: + PRINTD(("i2c_transfer: error: illegal parameters\n")); + ret = -5; goto i2c_transfer_finish; + +transfer_error_bus_busy: + PRINTD(("i2c_transfer: error: bus is busy\n")); + ret = -6; goto i2c_transfer_finish; + +i2c_transfer_finish: + PRINTD(("i2c_transfer: ISR: 0x%04x\n", readl(&base->isr))); + i2c_reset(); + return ret; +} + +/* ------------------------------------------------------------------------ */ +/* API Functions */ +/* ------------------------------------------------------------------------ */ +void i2c_init(int speed, int slaveaddr) +{ +#ifdef CONFIG_I2C_MULTI_BUS + current_bus = 0; + base = (struct mv_i2c *)i2c_regs[current_bus]; +#else + base = (struct mv_i2c *)CONFIG_MV_I2C_REG; +#endif + + i2c_board_init(base); +} + +/* + * i2c_probe: - Test if a chip answers for a given i2c address + * + * @chip: address of the chip which is searched for + * @return: 0 if a chip was found, -1 otherwhise + */ +int i2c_probe(uchar chip) +{ + struct i2c_msg msg; + + i2c_reset(); + + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1) + 1; + if (i2c_transfer(&msg)) + return -1; + + msg.condition = I2C_COND_STOP; + msg.acknack = I2C_ACKNAK_SENDNAK; + msg.direction = I2C_READ; + msg.data = 0x00; + if (i2c_transfer(&msg)) + return -1; + + return 0; +} + +/* + * i2c_read: - Read multiple bytes from an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be read + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to write the data + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct i2c_msg msg; + u8 addr_bytes[3]; /* lowest...highest byte of data address */ + + PRINTD(("i2c_read(chip=0x%02x, addr=0x%02x, alen=0x%02x, " + "len=0x%02x)\n", chip, addr, alen, len)); + + i2c_reset(); + + /* dummy chip address write */ + PRINTD(("i2c_read: dummy chip address write\n")); + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1); + msg.data &= 0xFE; + if (i2c_transfer(&msg)) + return -1; + + /* + * send memory address bytes; + * alen defines how much bytes we have to send. + */ + /*addr &= ((1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)-1); */ + addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF); + addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF); + addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF); + + while (--alen >= 0) { + PRINTD(("i2c_read: send memory word address byte %1d\n", alen)); + msg.condition = I2C_COND_NORMAL; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = addr_bytes[alen]; + if (i2c_transfer(&msg)) + return -1; + } + + /* start read sequence */ + PRINTD(("i2c_read: start read sequence\n")); + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1); + msg.data |= 0x01; + if (i2c_transfer(&msg)) + return -1; + + /* read bytes; send NACK at last byte */ + while (len--) { + if (len == 0) { + msg.condition = I2C_COND_STOP; + msg.acknack = I2C_ACKNAK_SENDNAK; + } else { + msg.condition = I2C_COND_NORMAL; + msg.acknack = I2C_ACKNAK_SENDACK; + } + + msg.direction = I2C_READ; + msg.data = 0x00; + if (i2c_transfer(&msg)) + return -1; + + *buffer = msg.data; + PRINTD(("i2c_read: reading byte (0x%08x)=0x%02x\n", + (unsigned int)buffer, *buffer)); + buffer++; + } + + i2c_reset(); + + return 0; +} + +/* + * i2c_write: - Write multiple bytes to an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be written + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to find the data to be written + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct i2c_msg msg; + u8 addr_bytes[3]; /* lowest...highest byte of data address */ + + PRINTD(("i2c_write(chip=0x%02x, addr=0x%02x, alen=0x%02x, " + "len=0x%02x)\n", chip, addr, alen, len)); + + i2c_reset(); + + /* chip address write */ + PRINTD(("i2c_write: chip address write\n")); + msg.condition = I2C_COND_START; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = (chip << 1); + msg.data &= 0xFE; + if (i2c_transfer(&msg)) + return -1; + + /* + * send memory address bytes; + * alen defines how much bytes we have to send. + */ + addr_bytes[0] = (u8)((addr >> 0) & 0x000000FF); + addr_bytes[1] = (u8)((addr >> 8) & 0x000000FF); + addr_bytes[2] = (u8)((addr >> 16) & 0x000000FF); + + while (--alen >= 0) { + PRINTD(("i2c_write: send memory word address\n")); + msg.condition = I2C_COND_NORMAL; + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = addr_bytes[alen]; + if (i2c_transfer(&msg)) + return -1; + } + + /* write bytes; send NACK at last byte */ + while (len--) { + PRINTD(("i2c_write: writing byte (0x%08x)=0x%02x\n", + (unsigned int)buffer, *buffer)); + + if (len == 0) + msg.condition = I2C_COND_STOP; + else + msg.condition = I2C_COND_NORMAL; + + msg.acknack = I2C_ACKNAK_WAITACK; + msg.direction = I2C_WRITE; + msg.data = *(buffer++); + + if (i2c_transfer(&msg)) + return -1; + } + + i2c_reset(); + + return 0; +} +#endif /* CONFIG_HARD_I2C */ diff --git a/qemu/roms/u-boot/drivers/i2c/mv_i2c.h b/qemu/roms/u-boot/drivers/i2c/mv_i2c.h new file mode 100644 index 000000000..ae27c447b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mv_i2c.h @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2011 + * Marvell Inc, <www.marvell.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MV_I2C_H_ +#define _MV_I2C_H_ +extern void i2c_clk_enable(void); + +/* Shall the current transfer have a start/stop condition? */ +#define I2C_COND_NORMAL 0 +#define I2C_COND_START 1 +#define I2C_COND_STOP 2 + +/* Shall the current transfer be ack/nacked or being waited for it? */ +#define I2C_ACKNAK_WAITACK 1 +#define I2C_ACKNAK_SENDACK 2 +#define I2C_ACKNAK_SENDNAK 4 + +/* Specify who shall transfer the data (master or slave) */ +#define I2C_READ 0 +#define I2C_WRITE 1 + +#if (CONFIG_SYS_I2C_SPEED == 400000) +#define I2C_ICR_INIT (ICR_FM | ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD \ + | ICR_SCLE) +#else +#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) +#endif + +#define I2C_ISR_INIT 0x7FF +/* ----- Control register bits ---------------------------------------- */ + +#define ICR_START 0x1 /* start bit */ +#define ICR_STOP 0x2 /* stop bit */ +#define ICR_ACKNAK 0x4 /* send ACK(0) or NAK(1) */ +#define ICR_TB 0x8 /* transfer byte bit */ +#define ICR_MA 0x10 /* master abort */ +#define ICR_SCLE 0x20 /* master clock enable, mona SCLEA */ +#define ICR_IUE 0x40 /* unit enable */ +#define ICR_GCD 0x80 /* general call disable */ +#define ICR_ITEIE 0x100 /* enable tx interrupts */ +#define ICR_IRFIE 0x200 /* enable rx interrupts, mona: DRFIE */ +#define ICR_BEIE 0x400 /* enable bus error ints */ +#define ICR_SSDIE 0x800 /* slave STOP detected int enable */ +#define ICR_ALDIE 0x1000 /* enable arbitration interrupt */ +#define ICR_SADIE 0x2000 /* slave address detected int enable */ +#define ICR_UR 0x4000 /* unit reset */ +#define ICR_FM 0x8000 /* Fast Mode */ + +/* ----- Status register bits ----------------------------------------- */ + +#define ISR_RWM 0x1 /* read/write mode */ +#define ISR_ACKNAK 0x2 /* ack/nak status */ +#define ISR_UB 0x4 /* unit busy */ +#define ISR_IBB 0x8 /* bus busy */ +#define ISR_SSD 0x10 /* slave stop detected */ +#define ISR_ALD 0x20 /* arbitration loss detected */ +#define ISR_ITE 0x40 /* tx buffer empty */ +#define ISR_IRF 0x80 /* rx buffer full */ +#define ISR_GCAD 0x100 /* general call address detected */ +#define ISR_SAD 0x200 /* slave address detected */ +#define ISR_BED 0x400 /* bus error no ACK/NAK */ + +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/mvtwsi.c b/qemu/roms/u-boot/drivers/i2c/mvtwsi.c new file mode 100644 index 000000000..5ba0e0386 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mvtwsi.c @@ -0,0 +1,394 @@ +/* + * Driver for the TWSI (i2c) controller found on the Marvell + * orion5x and kirkwood SoC families. + * + * Author: Albert Aribaud <albert.u.boot@aribaud.net> + * Copyright (c) 2010 Albert Aribaud. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <asm/errno.h> +#include <asm/io.h> + +/* + * include a file that will provide CONFIG_I2C_MVTWSI_BASE + * and possibly other settings + */ + +#if defined(CONFIG_ORION5X) +#include <asm/arch/orion5x.h> +#elif defined(CONFIG_KIRKWOOD) +#include <asm/arch/kirkwood.h> +#else +#error Driver mvtwsi not supported by SoC or board +#endif + +/* + * TWSI register structure + */ + +struct mvtwsi_registers { + u32 slave_address; + u32 data; + u32 control; + union { + u32 status; /* when reading */ + u32 baudrate; /* when writing */ + }; + u32 xtnd_slave_addr; + u32 reserved[2]; + u32 soft_reset; +}; + +/* + * Control register fields + */ + +#define MVTWSI_CONTROL_ACK 0x00000004 +#define MVTWSI_CONTROL_IFLG 0x00000008 +#define MVTWSI_CONTROL_STOP 0x00000010 +#define MVTWSI_CONTROL_START 0x00000020 +#define MVTWSI_CONTROL_TWSIEN 0x00000040 +#define MVTWSI_CONTROL_INTEN 0x00000080 + +/* + * Status register values -- only those expected in normal master + * operation on non-10-bit-address devices; whatever status we don't + * expect in nominal conditions (bus errors, arbitration losses, + * missing ACKs...) we just pass back to the caller as an error + * code. + */ + +#define MVTWSI_STATUS_START 0x08 +#define MVTWSI_STATUS_REPEATED_START 0x10 +#define MVTWSI_STATUS_ADDR_W_ACK 0x18 +#define MVTWSI_STATUS_DATA_W_ACK 0x28 +#define MVTWSI_STATUS_ADDR_R_ACK 0x40 +#define MVTWSI_STATUS_ADDR_R_NAK 0x48 +#define MVTWSI_STATUS_DATA_R_ACK 0x50 +#define MVTWSI_STATUS_DATA_R_NAK 0x58 +#define MVTWSI_STATUS_IDLE 0xF8 + +/* + * The single instance of the controller we'll be dealing with + */ + +static struct mvtwsi_registers *twsi = + (struct mvtwsi_registers *) CONFIG_I2C_MVTWSI_BASE; + +/* + * Returned statuses are 0 for success and nonzero otherwise. + * Currently, cmd_i2c and cmd_eeprom do not interpret an error status. + * Thus to ease debugging, the return status contains some debug info: + * - bits 31..24 are error class: 1 is timeout, 2 is 'status mismatch'. + * - bits 23..16 are the last value of the control register. + * - bits 15..8 are the last value of the status register. + * - bits 7..0 are the expected value of the status register. + */ + +#define MVTWSI_ERROR_WRONG_STATUS 0x01 +#define MVTWSI_ERROR_TIMEOUT 0x02 + +#define MVTWSI_ERROR(ec, lc, ls, es) (((ec << 24) & 0xFF000000) | \ + ((lc << 16) & 0x00FF0000) | ((ls<<8) & 0x0000FF00) | (es & 0xFF)) + +/* + * Wait for IFLG to raise, or return 'timeout'; then if status is as expected, + * return 0 (ok) or return 'wrong status'. + */ +static int twsi_wait(int expected_status) +{ + int control, status; + int timeout = 1000; + + do { + control = readl(&twsi->control); + if (control & MVTWSI_CONTROL_IFLG) { + status = readl(&twsi->status); + if (status == expected_status) + return 0; + else + return MVTWSI_ERROR( + MVTWSI_ERROR_WRONG_STATUS, + control, status, expected_status); + } + udelay(10); /* one clock cycle at 100 kHz */ + } while (timeout--); + status = readl(&twsi->status); + return MVTWSI_ERROR( + MVTWSI_ERROR_TIMEOUT, control, status, expected_status); +} + +/* + * These flags are ORed to any write to the control register + * They allow global setting of TWSIEN and ACK. + * By default none are set. + * twsi_start() sets TWSIEN (in case the controller was disabled) + * twsi_recv() sets ACK or resets it depending on expected status. + */ +static u8 twsi_control_flags = MVTWSI_CONTROL_TWSIEN; + +/* + * Assert the START condition, either in a single I2C transaction + * or inside back-to-back ones (repeated starts). + */ +static int twsi_start(int expected_status) +{ + /* globally set TWSIEN in case it was not */ + twsi_control_flags |= MVTWSI_CONTROL_TWSIEN; + /* assert START */ + writel(twsi_control_flags | MVTWSI_CONTROL_START, &twsi->control); + /* wait for controller to process START */ + return twsi_wait(expected_status); +} + +/* + * Send a byte (i2c address or data). + */ +static int twsi_send(u8 byte, int expected_status) +{ + /* put byte in data register for sending */ + writel(byte, &twsi->data); + /* clear any pending interrupt -- that'll cause sending */ + writel(twsi_control_flags, &twsi->control); + /* wait for controller to receive byte and check ACK */ + return twsi_wait(expected_status); +} + +/* + * Receive a byte. + * Global mvtwsi_control_flags variable says if we should ack or nak. + */ +static int twsi_recv(u8 *byte) +{ + int expected_status, status; + + /* compute expected status based on ACK bit in global control flags */ + if (twsi_control_flags & MVTWSI_CONTROL_ACK) + expected_status = MVTWSI_STATUS_DATA_R_ACK; + else + expected_status = MVTWSI_STATUS_DATA_R_NAK; + /* acknowledge *previous state* and launch receive */ + writel(twsi_control_flags, &twsi->control); + /* wait for controller to receive byte and assert ACK or NAK */ + status = twsi_wait(expected_status); + /* if we did receive expected byte then store it */ + if (status == 0) + *byte = readl(&twsi->data); + /* return status */ + return status; +} + +/* + * Assert the STOP condition. + * This is also used to force the bus back in idle (SDA=SCL=1). + */ +static int twsi_stop(int status) +{ + int control, stop_status; + int timeout = 1000; + + /* assert STOP */ + control = MVTWSI_CONTROL_TWSIEN | MVTWSI_CONTROL_STOP; + writel(control, &twsi->control); + /* wait for IDLE; IFLG won't rise so twsi_wait() is no use. */ + do { + stop_status = readl(&twsi->status); + if (stop_status == MVTWSI_STATUS_IDLE) + break; + udelay(10); /* one clock cycle at 100 kHz */ + } while (timeout--); + control = readl(&twsi->control); + if (stop_status != MVTWSI_STATUS_IDLE) + if (status == 0) + status = MVTWSI_ERROR( + MVTWSI_ERROR_TIMEOUT, + control, status, MVTWSI_STATUS_IDLE); + return status; +} + +/* + * Ugly formula to convert m and n values to a frequency comes from + * TWSI specifications + */ + +#define TWSI_FREQUENCY(m, n) \ + (CONFIG_SYS_TCLK / (10 * (m + 1) * (1 << n))) + +/* + * Reset controller. + * Called at end of i2c_init unsuccessful i2c transactions. + * Controller reset also resets the baud rate and slave address, so + * re-establish them. + */ +static void twsi_reset(u8 baud_rate, u8 slave_address) +{ + /* ensure controller will be enabled by any twsi*() function */ + twsi_control_flags = MVTWSI_CONTROL_TWSIEN; + /* reset controller */ + writel(0, &twsi->soft_reset); + /* wait 2 ms -- this is what the Marvell LSP does */ + udelay(20000); + /* set baud rate */ + writel(baud_rate, &twsi->baudrate); + /* set slave address even though we don't use it */ + writel(slave_address, &twsi->slave_address); + writel(0, &twsi->xtnd_slave_addr); + /* assert STOP but don't care for the result */ + (void) twsi_stop(0); +} + +/* + * I2C init called by cmd_i2c when doing 'i2c reset'. + * Sets baud to the highest possible value not exceeding requested one. + */ +void i2c_init(int requested_speed, int slaveadd) +{ + int tmp_speed, highest_speed, n, m; + int baud = 0x44; /* baudrate at controller reset */ + + /* use actual speed to collect progressively higher values */ + highest_speed = 0; + /* compute m, n setting for highest speed not above requested speed */ + for (n = 0; n < 8; n++) { + for (m = 0; m < 16; m++) { + tmp_speed = TWSI_FREQUENCY(m, n); + if ((tmp_speed <= requested_speed) + && (tmp_speed > highest_speed)) { + highest_speed = tmp_speed; + baud = (m << 3) | n; + } + } + } + /* reset controller */ + twsi_reset(baud, slaveadd); +} + +/* + * Begin I2C transaction with expected start status, at given address. + * Common to i2c_probe, i2c_read and i2c_write. + * Expected address status will derive from direction bit (bit 0) in addr. + */ +static int i2c_begin(int expected_start_status, u8 addr) +{ + int status, expected_addr_status; + + /* compute expected address status from direction bit in addr */ + if (addr & 1) /* reading */ + expected_addr_status = MVTWSI_STATUS_ADDR_R_ACK; + else /* writing */ + expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK; + /* assert START */ + status = twsi_start(expected_start_status); + /* send out the address if the start went well */ + if (status == 0) + status = twsi_send(addr, expected_addr_status); + /* return ok or status of first failure to caller */ + return status; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +int i2c_probe(uchar chip) +{ + u8 dummy_byte; + int status; + + /* begin i2c read */ + status = i2c_begin(MVTWSI_STATUS_START, (chip << 1) | 1); + /* dummy read was accepted: receive byte but NAK it. */ + if (status == 0) + status = twsi_recv(&dummy_byte); + /* Stop transaction */ + twsi_stop(0); + /* return 0 or status of first failure */ + return status; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + * + * NOTE: some EEPROMS want a stop right before the second start, while + * some will choke if it is there. Deciding which we should do is eeprom + * stuff, not i2c, but at the moment the APIs won't let us put it in + * cmd_eeprom, so we have to choose here, and for the moment that'll be + * a repeated start without a preceding stop. + */ +int i2c_read(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int status; + + /* begin i2c write to send the address bytes */ + status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + /* send addr bytes */ + while ((status == 0) && alen--) + status = twsi_send(addr >> (8*alen), + MVTWSI_STATUS_DATA_W_ACK); + /* begin i2c read to receive eeprom data bytes */ + if (status == 0) + status = i2c_begin( + MVTWSI_STATUS_REPEATED_START, (dev << 1) | 1); + /* prepare ACK if at least one byte must be received */ + if (length > 0) + twsi_control_flags |= MVTWSI_CONTROL_ACK; + /* now receive actual bytes */ + while ((status == 0) && length--) { + /* reset NAK if we if no more to read now */ + if (length == 0) + twsi_control_flags &= ~MVTWSI_CONTROL_ACK; + /* read current byte */ + status = twsi_recv(data++); + } + /* Stop transaction */ + status = twsi_stop(status); + /* return 0 or status of first failure */ + return status; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +int i2c_write(u8 dev, uint addr, int alen, u8 *data, int length) +{ + int status; + + /* begin i2c write to send the eeprom adress bytes then data bytes */ + status = i2c_begin(MVTWSI_STATUS_START, (dev << 1)); + /* send addr bytes */ + while ((status == 0) && alen--) + status = twsi_send(addr >> (8*alen), + MVTWSI_STATUS_DATA_W_ACK); + /* send data bytes */ + while ((status == 0) && (length-- > 0)) + status = twsi_send(*(data++), MVTWSI_STATUS_DATA_W_ACK); + /* Stop transaction */ + status = twsi_stop(status); + /* return 0 or status of first failure */ + return status; +} + +/* + * Bus set routine: we only support bus 0. + */ +int i2c_set_bus_num(unsigned int bus) +{ + if (bus > 0) { + return -1; + } + return 0; +} + +/* + * Bus get routine: hard-return bus 0. + */ +unsigned int i2c_get_bus_num(void) +{ + return 0; +} diff --git a/qemu/roms/u-boot/drivers/i2c/mxc_i2c.c b/qemu/roms/u-boot/drivers/i2c/mxc_i2c.c new file mode 100644 index 000000000..48468d74b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mxc_i2c.c @@ -0,0 +1,549 @@ +/* + * i2c driver for Freescale i.MX series + * + * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> + * (c) 2011 Marek Vasut <marek.vasut@gmail.com> + * + * Based on i2c-imx.c from linux kernel: + * Copyright (C) 2005 Torsten Koschorrek <koschorrek at synertronixx.de> + * Copyright (C) 2005 Matthias Blaschke <blaschke at synertronixx.de> + * Copyright (C) 2007 RightHand Technologies, Inc. + * Copyright (C) 2008 Darius Augulis <darius.augulis at teltonika.lt> + * + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <i2c.h> +#include <watchdog.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef I2C_QUIRK_REG +struct mxc_i2c_regs { + uint8_t iadr; + uint8_t ifdr; + uint8_t i2cr; + uint8_t i2sr; + uint8_t i2dr; +}; +#else +struct mxc_i2c_regs { + uint32_t iadr; + uint32_t ifdr; + uint32_t i2cr; + uint32_t i2sr; + uint32_t i2dr; +}; +#endif + +#define I2CR_IIEN (1 << 6) +#define I2CR_MSTA (1 << 5) +#define I2CR_MTX (1 << 4) +#define I2CR_TX_NO_AK (1 << 3) +#define I2CR_RSTA (1 << 2) + +#define I2SR_ICF (1 << 7) +#define I2SR_IBB (1 << 5) +#define I2SR_IAL (1 << 4) +#define I2SR_IIF (1 << 1) +#define I2SR_RX_NO_AK (1 << 0) + +#ifdef I2C_QUIRK_REG +#define I2CR_IEN (0 << 7) +#define I2CR_IDIS (1 << 7) +#define I2SR_IIF_CLEAR (1 << 1) +#else +#define I2CR_IEN (1 << 7) +#define I2CR_IDIS (0 << 7) +#define I2SR_IIF_CLEAR (0 << 1) +#endif + +#if defined(CONFIG_HARD_I2C) && !defined(CONFIG_SYS_I2C_BASE) +#error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver" +#endif + +#ifdef I2C_QUIRK_REG +static u16 i2c_clk_div[60][2] = { + { 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 }, + { 28, 0x04 }, { 30, 0x05 }, { 32, 0x09 }, { 34, 0x06 }, + { 36, 0x0A }, { 40, 0x07 }, { 44, 0x0C }, { 48, 0x0D }, + { 52, 0x43 }, { 56, 0x0E }, { 60, 0x45 }, { 64, 0x12 }, + { 68, 0x0F }, { 72, 0x13 }, { 80, 0x14 }, { 88, 0x15 }, + { 96, 0x19 }, { 104, 0x16 }, { 112, 0x1A }, { 128, 0x17 }, + { 136, 0x4F }, { 144, 0x1C }, { 160, 0x1D }, { 176, 0x55 }, + { 192, 0x1E }, { 208, 0x56 }, { 224, 0x22 }, { 228, 0x24 }, + { 240, 0x1F }, { 256, 0x23 }, { 288, 0x5C }, { 320, 0x25 }, + { 384, 0x26 }, { 448, 0x2A }, { 480, 0x27 }, { 512, 0x2B }, + { 576, 0x2C }, { 640, 0x2D }, { 768, 0x31 }, { 896, 0x32 }, + { 960, 0x2F }, { 1024, 0x33 }, { 1152, 0x34 }, { 1280, 0x35 }, + { 1536, 0x36 }, { 1792, 0x3A }, { 1920, 0x37 }, { 2048, 0x3B }, + { 2304, 0x3C }, { 2560, 0x3D }, { 3072, 0x3E }, { 3584, 0x7A }, + { 3840, 0x3F }, { 4096, 0x7B }, { 5120, 0x7D }, { 6144, 0x7E }, +}; +#else +static u16 i2c_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; +#endif + + +#ifndef CONFIG_SYS_MXC_I2C1_SPEED +#define CONFIG_SYS_MXC_I2C1_SPEED 100000 +#endif +#ifndef CONFIG_SYS_MXC_I2C2_SPEED +#define CONFIG_SYS_MXC_I2C2_SPEED 100000 +#endif +#ifndef CONFIG_SYS_MXC_I2C3_SPEED +#define CONFIG_SYS_MXC_I2C3_SPEED 100000 +#endif + +#ifndef CONFIG_SYS_MXC_I2C1_SLAVE +#define CONFIG_SYS_MXC_I2C1_SLAVE 0 +#endif +#ifndef CONFIG_SYS_MXC_I2C2_SLAVE +#define CONFIG_SYS_MXC_I2C2_SLAVE 0 +#endif +#ifndef CONFIG_SYS_MXC_I2C3_SLAVE +#define CONFIG_SYS_MXC_I2C3_SLAVE 0 +#endif + + +/* + * Calculate and set proper clock divider + */ +static uint8_t i2c_imx_get_clk(unsigned int rate) +{ + unsigned int i2c_clk_rate; + unsigned int div; + u8 clk_div; + +#if defined(CONFIG_MX31) + struct clock_control_regs *sc_regs = + (struct clock_control_regs *)CCM_BASE; + + /* start the required I2C clock */ + writel(readl(&sc_regs->cgr0) | (3 << CONFIG_SYS_I2C_CLK_OFFSET), + &sc_regs->cgr0); +#endif + + /* Divider value calculation */ + i2c_clk_rate = mxc_get_clock(MXC_I2C_CLK); + div = (i2c_clk_rate + rate - 1) / rate; + if (div < i2c_clk_div[0][0]) + clk_div = 0; + else if (div > i2c_clk_div[ARRAY_SIZE(i2c_clk_div) - 1][0]) + clk_div = ARRAY_SIZE(i2c_clk_div) - 1; + else + for (clk_div = 0; i2c_clk_div[clk_div][0] < div; clk_div++) + ; + + /* Store divider value */ + return clk_div; +} + +/* + * Set I2C Bus speed + */ +static int bus_i2c_set_bus_speed(void *base, int speed) +{ + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; + u8 clk_idx = i2c_imx_get_clk(speed); + u8 idx = i2c_clk_div[clk_idx][1]; + + /* Store divider value */ + writeb(idx, &i2c_regs->ifdr); + + /* Reset module */ + writeb(I2CR_IDIS, &i2c_regs->i2cr); + writeb(0, &i2c_regs->i2sr); + return 0; +} + +#define ST_BUS_IDLE (0 | (I2SR_IBB << 8)) +#define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8)) +#define ST_IIF (I2SR_IIF | (I2SR_IIF << 8)) + +static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state) +{ + unsigned sr; + ulong elapsed; + ulong start_time = get_timer(0); + for (;;) { + sr = readb(&i2c_regs->i2sr); + if (sr & I2SR_IAL) { +#ifdef I2C_QUIRK_REG + writeb(sr | I2SR_IAL, &i2c_regs->i2sr); +#else + writeb(sr & ~I2SR_IAL, &i2c_regs->i2sr); +#endif + printf("%s: Arbitration lost sr=%x cr=%x state=%x\n", + __func__, sr, readb(&i2c_regs->i2cr), state); + return -ERESTART; + } + if ((sr & (state >> 8)) == (unsigned char)state) + return sr; + WATCHDOG_RESET(); + elapsed = get_timer(start_time); + if (elapsed > (CONFIG_SYS_HZ / 10)) /* .1 seconds */ + break; + } + printf("%s: failed sr=%x cr=%x state=%x\n", __func__, + sr, readb(&i2c_regs->i2cr), state); + return -ETIMEDOUT; +} + +static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte) +{ + int ret; + + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + writeb(byte, &i2c_regs->i2dr); + ret = wait_for_sr_state(i2c_regs, ST_IIF); + if (ret < 0) + return ret; + if (ret & I2SR_RX_NO_AK) + return -ENODEV; + return 0; +} + +/* + * Stop I2C transaction + */ +static void i2c_imx_stop(struct mxc_i2c_regs *i2c_regs) +{ + int ret; + unsigned int temp = readb(&i2c_regs->i2cr); + + temp &= ~(I2CR_MSTA | I2CR_MTX); + writeb(temp, &i2c_regs->i2cr); + ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE); + if (ret < 0) + printf("%s:trigger stop failed\n", __func__); +} + +/* + * Send start signal, chip address and + * write register address + */ +static int i2c_init_transfer_(struct mxc_i2c_regs *i2c_regs, + uchar chip, uint addr, int alen) +{ + unsigned int temp; + int ret; + + /* Enable I2C controller */ +#ifdef I2C_QUIRK_REG + if (readb(&i2c_regs->i2cr) & I2CR_IDIS) { +#else + if (!(readb(&i2c_regs->i2cr) & I2CR_IEN)) { +#endif + writeb(I2CR_IEN, &i2c_regs->i2cr); + /* Wait for controller to be stable */ + udelay(50); + } + if (readb(&i2c_regs->iadr) == (chip << 1)) + writeb((chip << 1) ^ 2, &i2c_regs->iadr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE); + if (ret < 0) + return ret; + + /* Start I2C transaction */ + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_MSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = wait_for_sr_state(i2c_regs, ST_BUS_BUSY); + if (ret < 0) + return ret; + + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + + /* write slave address */ + ret = tx_byte(i2c_regs, chip << 1); + if (ret < 0) + return ret; + + while (alen--) { + ret = tx_byte(i2c_regs, (addr >> (alen * 8)) & 0xff); + if (ret < 0) + return ret; + } + return 0; +} + +static int i2c_idle_bus(void *base); + +static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs, + uchar chip, uint addr, int alen) +{ + int retry; + int ret; + for (retry = 0; retry < 3; retry++) { + ret = i2c_init_transfer_(i2c_regs, chip, addr, alen); + if (ret >= 0) + return 0; + i2c_imx_stop(i2c_regs); + if (ret == -ENODEV) + return ret; + + printf("%s: failed for chip 0x%x retry=%d\n", __func__, chip, + retry); + if (ret != -ERESTART) + /* Disable controller */ + writeb(I2CR_IDIS, &i2c_regs->i2cr); + udelay(100); + if (i2c_idle_bus(i2c_regs) < 0) + break; + } + printf("%s: give up i2c_regs=%p\n", __func__, i2c_regs); + return ret; +} + +/* + * Read data from I2C device + */ +int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf, + int len) +{ + int ret; + unsigned int temp; + int i; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; + + ret = i2c_init_transfer(i2c_regs, chip, addr, alen); + if (ret < 0) + return ret; + + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_RSTA; + writeb(temp, &i2c_regs->i2cr); + + ret = tx_byte(i2c_regs, (chip << 1) | 1); + if (ret < 0) { + i2c_imx_stop(i2c_regs); + return ret; + } + + /* setup bus to read data */ + temp = readb(&i2c_regs->i2cr); + temp &= ~(I2CR_MTX | I2CR_TX_NO_AK); + if (len == 1) + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + readb(&i2c_regs->i2dr); /* dummy read to clear ICF */ + + /* read data */ + for (i = 0; i < len; i++) { + ret = wait_for_sr_state(i2c_regs, ST_IIF); + if (ret < 0) { + i2c_imx_stop(i2c_regs); + return ret; + } + + /* + * It must generate STOP before read I2DR to prevent + * controller from generating another clock cycle + */ + if (i == (len - 1)) { + i2c_imx_stop(i2c_regs); + } else if (i == (len - 2)) { + temp = readb(&i2c_regs->i2cr); + temp |= I2CR_TX_NO_AK; + writeb(temp, &i2c_regs->i2cr); + } + writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr); + buf[i] = readb(&i2c_regs->i2dr); + } + i2c_imx_stop(i2c_regs); + return 0; +} + +/* + * Write data to I2C device + */ +int bus_i2c_write(void *base, uchar chip, uint addr, int alen, + const uchar *buf, int len) +{ + int ret; + int i; + struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; + + ret = i2c_init_transfer(i2c_regs, chip, addr, alen); + if (ret < 0) + return ret; + + for (i = 0; i < len; i++) { + ret = tx_byte(i2c_regs, buf[i]); + if (ret < 0) + break; + } + i2c_imx_stop(i2c_regs); + 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, + (void *)IMX_I2C2_BASE, + (void *)IMX_I2C3_BASE +#elif defined(CONFIG_MX27) + (void *)IMX_I2C1_BASE, + (void *)IMX_I2C2_BASE +#elif defined(CONFIG_MX31) || defined(CONFIG_MX35) || \ + defined(CONFIG_MX51) || defined(CONFIG_MX53) || \ + defined(CONFIG_MX6) + (void *)I2C1_BASE_ADDR, + (void *)I2C2_BASE_ADDR, + (void *)I2C3_BASE_ADDR +#elif defined(CONFIG_VF610) + (void *)I2C0_BASE_ADDR +#else +#error "architecture not supported" +#endif +}; + +void *i2c_get_base(struct i2c_adapter *adap) +{ + return i2c_bases[adap->hwadapnr]; +} + +static struct i2c_parms *i2c_get_parms(void *base) +{ + struct sram_data *srdata = (void *)gd->srdata; + int i = 0; + struct i2c_parms *p = srdata->i2c_data; + while (i < ARRAY_SIZE(srdata->i2c_data)) { + if (p->base == base) + return p; + p++; + i++; + } + printf("Invalid I2C base: %p\n", base); + return NULL; +} + +static int i2c_idle_bus(void *base) +{ + struct i2c_parms *p = i2c_get_parms(base); + if (p && p->idle_bus_fn) + return p->idle_bus_fn(p->idle_bus_data); + return 0; +} + +static int mxc_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, + int len) +{ + return bus_i2c_read(i2c_get_base(adap), chip, addr, alen, buffer, len); +} + +static int mxc_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, + int len) +{ + return bus_i2c_write(i2c_get_base(adap), chip, addr, alen, buffer, len); +} + +/* + * Test if a chip at a given address responds (probe the chip) + */ +static int mxc_i2c_probe(struct i2c_adapter *adap, uint8_t chip) +{ + return bus_i2c_write(i2c_get_base(adap), chip, 0, 0, NULL, 0); +} + +void bus_i2c_init(void *base, int speed, int unused, + int (*idle_bus_fn)(void *p), void *idle_bus_data) +{ + struct sram_data *srdata = (void *)gd->srdata; + int i = 0; + struct i2c_parms *p = srdata->i2c_data; + if (!base) + return; + for (;;) { + if (!p->base || (p->base == base)) { + p->base = base; + if (idle_bus_fn) { + p->idle_bus_fn = idle_bus_fn; + p->idle_bus_data = idle_bus_data; + } + break; + } + p++; + i++; + if (i >= ARRAY_SIZE(srdata->i2c_data)) + return; + } + bus_i2c_set_bus_speed(base, speed); +} + +/* + * Init I2C Bus + */ +static void mxc_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + bus_i2c_init(i2c_get_base(adap), speed, slaveaddr, NULL, NULL); +} + +/* + * Set I2C Speed + */ +static uint mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed) +{ + return bus_i2c_set_bus_speed(i2c_get_base(adap), speed); +} + +/* + * Register mxc i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(mxc0, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C1_SPEED, + CONFIG_SYS_MXC_I2C1_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(mxc1, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C2_SPEED, + CONFIG_SYS_MXC_I2C2_SLAVE, 1) +#if defined(CONFIG_MX31) || defined(CONFIG_MX35) ||\ + defined(CONFIG_MX51) || defined(CONFIG_MX53) ||\ + defined(CONFIG_MX6) +U_BOOT_I2C_ADAP_COMPLETE(mxc2, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C3_SPEED, + CONFIG_SYS_MXC_I2C3_SLAVE, 2) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/mxs_i2c.c b/qemu/roms/u-boot/drivers/i2c/mxs_i2c.c new file mode 100644 index 000000000..de3b19402 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/mxs_i2c.c @@ -0,0 +1,299 @@ +/* + * Freescale i.MX28 I2C Driver + * + * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com> + * on behalf of DENX Software Engineering GmbH + * + * Partly based on Linux kernel i2c-mxs.c driver: + * Copyright (C) 2011 Wolfram Sang, Pengutronix e.K. + * + * Which was based on a (non-working) driver which was: + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <malloc.h> +#include <i2c.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/sys_proto.h> + +#define MXS_I2C_MAX_TIMEOUT 1000000 + +static void mxs_i2c_reset(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + int ret; + int speed = i2c_get_bus_speed(); + + ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg); + if (ret) { + debug("MXS I2C: Block reset timeout\n"); + return; + } + + writel(I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | I2C_CTRL1_NO_SLAVE_ACK_IRQ | + I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ | + I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ, + &i2c_regs->hw_i2c_ctrl1_clr); + + writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set); + + i2c_set_bus_speed(speed); +} + +static void mxs_i2c_setup_read(uint8_t chip, int len) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + + writel(I2C_QUEUECMD_RETAIN_CLOCK | I2C_QUEUECMD_PRE_SEND_START | + I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | + (1 << I2C_QUEUECMD_XFER_COUNT_OFFSET), + &i2c_regs->hw_i2c_queuecmd); + + writel((chip << 1) | 1, &i2c_regs->hw_i2c_data); + + writel(I2C_QUEUECMD_SEND_NAK_ON_LAST | I2C_QUEUECMD_MASTER_MODE | + (len << I2C_QUEUECMD_XFER_COUNT_OFFSET) | + I2C_QUEUECMD_POST_SEND_STOP, &i2c_regs->hw_i2c_queuecmd); + + writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set); +} + +static int mxs_i2c_write(uchar chip, uint addr, int alen, + uchar *buf, int blen, int stop) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t data, tmp; + int i, remain, off; + int timeout = MXS_I2C_MAX_TIMEOUT; + + if ((alen > 4) || (alen == 0)) { + debug("MXS I2C: Invalid address length\n"); + return -EINVAL; + } + + if (stop) + stop = I2C_QUEUECMD_POST_SEND_STOP; + + writel(I2C_QUEUECMD_PRE_SEND_START | + I2C_QUEUECMD_MASTER_MODE | I2C_QUEUECMD_DIRECTION | + ((blen + alen + 1) << I2C_QUEUECMD_XFER_COUNT_OFFSET) | stop, + &i2c_regs->hw_i2c_queuecmd); + + data = (chip << 1) << 24; + + for (i = 0; i < alen; i++) { + data >>= 8; + data |= ((char *)&addr)[alen - i - 1] << 24; + if ((i & 3) == 2) + writel(data, &i2c_regs->hw_i2c_data); + } + + off = i; + for (; i < off + blen; i++) { + data >>= 8; + data |= buf[i - off] << 24; + if ((i & 3) == 2) + writel(data, &i2c_regs->hw_i2c_data); + } + + remain = 24 - ((i & 3) * 8); + if (remain) + writel(data >> remain, &i2c_regs->hw_i2c_data); + + writel(I2C_QUEUECTRL_QUEUE_RUN, &i2c_regs->hw_i2c_queuectrl_set); + + while (--timeout) { + tmp = readl(&i2c_regs->hw_i2c_queuestat); + if (tmp & I2C_QUEUESTAT_WR_QUEUE_EMPTY) + break; + } + + if (!timeout) { + debug("MXS I2C: Failed transmitting data!\n"); + return -EINVAL; + } + + return 0; +} + +static int mxs_i2c_wait_for_ack(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t tmp; + int timeout = MXS_I2C_MAX_TIMEOUT; + + for (;;) { + tmp = readl(&i2c_regs->hw_i2c_ctrl1); + if (tmp & I2C_CTRL1_NO_SLAVE_ACK_IRQ) { + debug("MXS I2C: No slave ACK\n"); + goto err; + } + + if (tmp & ( + I2C_CTRL1_EARLY_TERM_IRQ | I2C_CTRL1_MASTER_LOSS_IRQ | + I2C_CTRL1_SLAVE_STOP_IRQ | I2C_CTRL1_SLAVE_IRQ)) { + debug("MXS I2C: Error (CTRL1 = %08x)\n", tmp); + goto err; + } + + if (tmp & I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ) + break; + + if (!timeout--) { + debug("MXS I2C: Operation timed out\n"); + goto err; + } + + udelay(1); + } + + return 0; + +err: + mxs_i2c_reset(); + return 1; +} + +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t tmp = 0; + int timeout = MXS_I2C_MAX_TIMEOUT; + int ret; + int i; + + ret = mxs_i2c_write(chip, addr, alen, NULL, 0, 0); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + ret = mxs_i2c_wait_for_ack(); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + mxs_i2c_setup_read(chip, len); + ret = mxs_i2c_wait_for_ack(); + if (ret) { + debug("MXS I2C: Failed reading address\n"); + return ret; + } + + for (i = 0; i < len; i++) { + if (!(i & 3)) { + while (--timeout) { + tmp = readl(&i2c_regs->hw_i2c_queuestat); + if (!(tmp & I2C_QUEUESTAT_RD_QUEUE_EMPTY)) + break; + } + + if (!timeout) { + debug("MXS I2C: Failed receiving data!\n"); + return -ETIMEDOUT; + } + + tmp = readl(&i2c_regs->hw_i2c_queuedata); + } + buffer[i] = tmp & 0xff; + tmp >>= 8; + } + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int ret; + ret = mxs_i2c_write(chip, addr, alen, buffer, len, 1); + if (ret) { + debug("MXS I2C: Failed writing address\n"); + return ret; + } + + ret = mxs_i2c_wait_for_ack(); + if (ret) + debug("MXS I2C: Failed writing address\n"); + + return ret; +} + +int i2c_probe(uchar chip) +{ + int ret; + ret = mxs_i2c_write(chip, 0, 1, NULL, 0, 1); + if (!ret) + ret = mxs_i2c_wait_for_ack(); + mxs_i2c_reset(); + return ret; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + /* + * The timing derivation algorithm. There is no documentation for this + * algorithm available, it was derived by using the scope and fiddling + * with constants until the result observed on the scope was good enough + * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be + * possible to assume the algorithm works for other frequencies as well. + * + * Note it was necessary to cap the frequency on both ends as it's not + * possible to configure completely arbitrary frequency for the I2C bus + * clock. + */ + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t base = ((clk / speed) - 38) / 2; + uint16_t high_count = base + 3; + uint16_t low_count = base - 3; + uint16_t rcv_count = (high_count * 3) / 4; + uint16_t xmit_count = low_count / 4; + + if (speed > 540000) { + printf("MXS I2C: Speed too high (%d Hz)\n", speed); + return -EINVAL; + } + + if (speed < 12000) { + printf("MXS I2C: Speed too low (%d Hz)\n", speed); + return -EINVAL; + } + + writel((high_count << 16) | rcv_count, &i2c_regs->hw_i2c_timing0); + writel((low_count << 16) | xmit_count, &i2c_regs->hw_i2c_timing1); + + writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing2); + + return 0; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t timing0; + + timing0 = readl(&i2c_regs->hw_i2c_timing0); + /* + * This is a reverse version of the algorithm presented in + * i2c_set_bus_speed(). Please refer there for details. + */ + return clk / ((((timing0 >> 16) - 3) * 2) + 38); +} + +void i2c_init(int speed, int slaveadd) +{ + mxs_i2c_reset(); + i2c_set_bus_speed(speed); + + return; +} diff --git a/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c new file mode 100644 index 000000000..a39b5917e --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.c @@ -0,0 +1,680 @@ +/* + * Basic I2C functions + * + * Copyright (c) 2004 Texas Instruments + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the license found in the file + * named COPYING that should have accompanied this file. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Author: Jian Zhang jzhang@ti.com, Texas Instruments + * + * Copyright (c) 2003 Wolfgang Denk, wd@denx.de + * Rewritten to fit into the current U-Boot framework + * + * Adapted for OMAP2420 I2C, r-woodruff2@ti.com + * + * Copyright (c) 2013 Lubomir Popov <lpopov@mm-sol.com>, MM Solutions + * New i2c_read, i2c_write and i2c_probe functions, tested on OMAP4 + * (4430/60/70), OMAP5 (5430) and AM335X (3359); should work on older + * OMAPs and derivatives as well. The only anticipated exception would + * be the OMAP2420, which shall require driver modification. + * - Rewritten i2c_read to operate correctly with all types of chips + * (old function could not read consistent data from some I2C slaves). + * - Optimized i2c_write. + * - New i2c_probe, performs write access vs read. The old probe could + * hang the system under certain conditions (e.g. unconfigured pads). + * - The read/write/probe functions try to identify unconfigured bus. + * - Status functions now read irqstatus_raw as per TRM guidelines + * (except for OMAP243X and OMAP34XX). + * - Driver now supports up to I2C5 (OMAP5). + * + * Copyright (c) 2014 Hannes Petermaier <oe5hpm@oevsv.at>, B&R + * - Added support for set_speed + * + */ + +#include <common.h> +#include <i2c.h> + +#include <asm/arch/i2c.h> +#include <asm/io.h> + +#include "omap24xx_i2c.h" + +DECLARE_GLOBAL_DATA_PTR; + +#define I2C_TIMEOUT 1000 + +/* Absolutely safe for status update at 100 kHz I2C: */ +#define I2C_WAIT 200 + +static int wait_for_bb(struct i2c_adapter *adap); +static struct i2c *omap24_get_base(struct i2c_adapter *adap); +static u16 wait_for_event(struct i2c_adapter *adap); +static void flush_fifo(struct i2c_adapter *adap); +static int omap24_i2c_findpsc(u32 *pscl, u32 *psch, uint speed) +{ + unsigned int sampleclk, prescaler; + int fsscll, fssclh; + + speed <<= 1; + prescaler = 0; + /* + * some divisors may cause a precission loss, but shouldn't + * be a big thing, because i2c_clk is then allready very slow. + */ + while (prescaler <= 0xFF) { + sampleclk = I2C_IP_CLK / (prescaler+1); + + fsscll = sampleclk / speed; + fssclh = fsscll; + fsscll -= I2C_FASTSPEED_SCLL_TRIM; + fssclh -= I2C_FASTSPEED_SCLH_TRIM; + + if (((fsscll > 0) && (fssclh > 0)) && + ((fsscll <= (255-I2C_FASTSPEED_SCLL_TRIM)) && + (fssclh <= (255-I2C_FASTSPEED_SCLH_TRIM)))) { + if (pscl) + *pscl = fsscll; + if (psch) + *psch = fssclh; + + return prescaler; + } + prescaler++; + } + return -1; +} +static uint omap24_i2c_setspeed(struct i2c_adapter *adap, uint speed) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int psc, fsscll = 0, fssclh = 0; + int hsscll = 0, hssclh = 0; + u32 scll = 0, sclh = 0; + + if (speed >= OMAP_I2C_HIGH_SPEED) { + /* High speed */ + psc = I2C_IP_CLK / I2C_INTERNAL_SAMPLING_CLK; + psc -= 1; + if (psc < I2C_PSC_MIN) { + printf("Error : I2C unsupported prescaler %d\n", psc); + return -1; + } + + /* For first phase of HS mode */ + fsscll = I2C_INTERNAL_SAMPLING_CLK / (2 * speed); + + fssclh = fsscll; + + fsscll -= I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM; + fssclh -= I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + puts("Error : I2C initializing first phase clock\n"); + return -1; + } + + /* For second phase of HS mode */ + hsscll = hssclh = I2C_INTERNAL_SAMPLING_CLK / (2 * speed); + + hsscll -= I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM; + hssclh -= I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM; + if (((fsscll < 0) || (fssclh < 0)) || + ((fsscll > 255) || (fssclh > 255))) { + puts("Error : I2C initializing second phase clock\n"); + return -1; + } + + scll = (unsigned int)hsscll << 8 | (unsigned int)fsscll; + sclh = (unsigned int)hssclh << 8 | (unsigned int)fssclh; + + } else { + /* Standard and fast speed */ + psc = omap24_i2c_findpsc(&scll, &sclh, speed); + if (0 > psc) { + puts("Error : I2C initializing clock\n"); + return -1; + } + } + + adap->speed = speed; + adap->waitdelay = (10000000 / speed) * 2; /* wait for 20 clkperiods */ + writew(0, &i2c_base->con); + writew(psc, &i2c_base->psc); + writew(scll, &i2c_base->scll); + writew(sclh, &i2c_base->sclh); + writew(I2C_CON_EN, &i2c_base->con); + writew(0xFFFF, &i2c_base->stat); /* clear all pending status */ + + return 0; +} +static void omap24_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int timeout = I2C_TIMEOUT; + + if (readw(&i2c_base->con) & I2C_CON_EN) { + writew(0, &i2c_base->con); + udelay(50000); + } + + writew(0x2, &i2c_base->sysc); /* for ES2 after soft reset */ + udelay(1000); + + writew(I2C_CON_EN, &i2c_base->con); + while (!(readw(&i2c_base->syss) & I2C_SYSS_RDONE) && timeout--) { + if (timeout <= 0) { + puts("ERROR: Timeout in soft-reset\n"); + return; + } + udelay(1000); + } + + if (0 != omap24_i2c_setspeed(adap, speed)) { + printf("ERROR: failed to setup I2C bus-speed!\n"); + return; + } + + /* own address */ + writew(slaveadd, &i2c_base->oa); + +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + /* + * Have to enable interrupts for OMAP2/3, these IPs don't have + * an 'irqstatus_raw' register and we shall have to poll 'stat' + */ + writew(I2C_IE_XRDY_IE | I2C_IE_RRDY_IE | I2C_IE_ARDY_IE | + I2C_IE_NACK_IE | I2C_IE_AL_IE, &i2c_base->ie); +#endif + udelay(1000); + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); +} + +static void flush_fifo(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + u16 stat; + + /* + * note: if you try and read data when its not there or ready + * you get a bus error + */ + while (1) { + stat = readw(&i2c_base->stat); + if (stat == I2C_STAT_RRDY) { + readb(&i2c_base->data); + writew(I2C_STAT_RRDY, &i2c_base->stat); + udelay(1000); + } else + break; + } +} + +/* + * i2c_probe: Use write access. Allows to identify addresses that are + * write-only (like the config register of dual-port EEPROMs) + */ +static int omap24_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct i2c *i2c_base = omap24_get_base(adap); + u16 status; + int res = 1; /* default = fail */ + + if (chip == readw(&i2c_base->oa)) + return res; + + /* Wait until bus is free */ + if (wait_for_bb(adap)) + return res; + + /* No data transfer, slave addr only */ + writew(chip, &i2c_base->sa); + /* Stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + status = wait_for_event(adap); + + if ((status & ~I2C_STAT_XRDY) == 0 || (status & I2C_STAT_AL)) { + /* + * With current high-level command implementation, notifying + * the user shall flood the console with 127 messages. If + * silent exit is desired upon unconfigured bus, remove the + * following 'if' section: + */ + if (status == I2C_STAT_XRDY) + printf("i2c_probe: pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + + goto pr_exit; + } + + /* Check for ACK (!NAK) */ + if (!(status & I2C_STAT_NACK)) { + res = 0; /* Device found */ + udelay(adap->waitdelay);/* Required by AM335X in SPL */ + /* Abort transfer (force idle state) */ + writew(I2C_CON_MST | I2C_CON_TRX, &i2c_base->con); /* Reset */ + udelay(1000); + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); /* STP */ + } +pr_exit: + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); + return res; +} + +/* + * i2c_read: Function now uses a single I2C read transaction with bulk transfer + * of the requested number of bytes (note that the 'i2c md' command + * limits this to 16 bytes anyway). If CONFIG_I2C_REPEATED_START is + * defined in the board config header, this transaction shall be with + * Repeated Start (Sr) between the address and data phases; otherwise + * Stop-Start (P-S) shall be used (some I2C chips do require a P-S). + * The address (reg offset) may be 0, 1 or 2 bytes long. + * Function now reads correctly from chips that return more than one + * byte of data per addressed register (like TI temperature sensors), + * or that do not need a register address at all (such as some clock + * distributors). + */ +static int omap24_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int i2c_error = 0; + u16 status; + + if (alen < 0) { + puts("I2C read: addr len < 0\n"); + return 1; + } + if (len < 0) { + puts("I2C read: data len < 0\n"); + return 1; + } + if (buffer == NULL) { + puts("I2C read: NULL pointer passed\n"); + return 1; + } + + if (alen > 2) { + printf("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > (1 << 16)) { + puts("I2C read: address out of range\n"); + return 1; + } + + /* Wait until bus not busy */ + if (wait_for_bb(adap)) + return 1; + + /* Zero, one or two bytes reg address (offset) */ + writew(alen, &i2c_base->cnt); + /* Set slave address */ + writew(chip, &i2c_base->sa); + + if (alen) { + /* Must write reg offset first */ +#ifdef CONFIG_I2C_REPEATED_START + /* No stop bit, use Repeated Start (Sr) */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | + I2C_CON_TRX, &i2c_base->con); +#else + /* Stop - Start (P-S) */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP | + I2C_CON_TRX, &i2c_base->con); +#endif + /* Send register offset */ + while (1) { + status = wait_for_event(adap); + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_read (addr phase): pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + goto rd_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + printf("i2c_read: error waiting for addr ACK (status=0x%x)\n", + status); + goto rd_exit; + } + if (alen) { + if (status & I2C_STAT_XRDY) { + alen--; + /* Do we have to use byte access? */ + writeb((addr >> (8 * alen)) & 0xff, + &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + } + /* Set slave address */ + writew(chip, &i2c_base->sa); + /* Read len bytes from slave */ + writew(len, &i2c_base->cnt); + /* Need stop bit here */ + writew(I2C_CON_EN | I2C_CON_MST | + I2C_CON_STT | I2C_CON_STP, + &i2c_base->con); + + /* Receive data */ + while (1) { + status = wait_for_event(adap); + /* + * Try to identify bus that is not padconf'd for I2C. This + * state could be left over from previous transactions if + * the address phase is skipped due to alen=0. + */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_read (data phase): pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + goto rd_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + goto rd_exit; + } + if (status & I2C_STAT_RRDY) { + *buffer++ = readb(&i2c_base->data); + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + } + +rd_exit: + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); + return i2c_error; +} + +/* i2c_write: Address (reg offset) may be 0, 1 or 2 bytes long. */ +static int omap24_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int i; + u16 status; + int i2c_error = 0; + int timeout = I2C_TIMEOUT; + + if (alen < 0) { + puts("I2C write: addr len < 0\n"); + return 1; + } + + if (len < 0) { + puts("I2C write: data len < 0\n"); + return 1; + } + + if (buffer == NULL) { + puts("I2C write: NULL pointer passed\n"); + return 1; + } + + if (alen > 2) { + printf("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (addr + len > (1 << 16)) { + printf("I2C write: address 0x%x + 0x%x out of range\n", + addr, len); + return 1; + } + + /* Wait until bus not busy */ + if (wait_for_bb(adap)) + return 1; + + /* Start address phase - will write regoffset + len bytes data */ + writew(alen + len, &i2c_base->cnt); + /* Set slave address */ + writew(chip, &i2c_base->sa); + /* Stop bit needed here */ + writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | + I2C_CON_STP, &i2c_base->con); + + while (alen) { + /* Must write reg offset (one or two bytes) */ + status = wait_for_event(adap); + /* Try to identify bus that is not padconf'd for I2C */ + if (status == I2C_STAT_XRDY) { + i2c_error = 2; + printf("i2c_write: pads on bus %d probably not configured (status=0x%x)\n", + adap->hwadapnr, status); + goto wr_exit; + } + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + printf("i2c_write: error waiting for addr ACK (status=0x%x)\n", + status); + goto wr_exit; + } + if (status & I2C_STAT_XRDY) { + alen--; + writeb((addr >> (8 * alen)) & 0xff, &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printf("i2c_write: bus not ready for addr Tx (status=0x%x)\n", + status); + goto wr_exit; + } + } + /* Address phase is over, now write data */ + for (i = 0; i < len; i++) { + status = wait_for_event(adap); + if (status == 0 || (status & I2C_STAT_NACK)) { + i2c_error = 1; + printf("i2c_write: error waiting for data ACK (status=0x%x)\n", + status); + goto wr_exit; + } + if (status & I2C_STAT_XRDY) { + writeb(buffer[i], &i2c_base->data); + writew(I2C_STAT_XRDY, &i2c_base->stat); + } else { + i2c_error = 1; + printf("i2c_write: bus not ready for data Tx (i=%d)\n", + i); + goto wr_exit; + } + } + /* + * poll ARDY bit for making sure that last byte really has been + * transferred on the bus. + */ + do { + status = wait_for_event(adap); + } while (!(status & I2C_STAT_ARDY) && timeout--); + if (timeout <= 0) + printf("i2c_write: timed out writig last byte!\n"); + +wr_exit: + flush_fifo(adap); + writew(0xFFFF, &i2c_base->stat); + return i2c_error; +} + +/* + * Wait for the bus to be free by checking the Bus Busy (BB) + * bit to become clear + */ +static int wait_for_bb(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + int timeout = I2C_TIMEOUT; + u16 stat; + + writew(0xFFFF, &i2c_base->stat); /* clear current interrupts...*/ +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + while ((stat = readw(&i2c_base->stat) & I2C_STAT_BB) && timeout--) { +#else + /* Read RAW status */ + while ((stat = readw(&i2c_base->irqstatus_raw) & + I2C_STAT_BB) && timeout--) { +#endif + writew(stat, &i2c_base->stat); + udelay(adap->waitdelay); + } + + if (timeout <= 0) { + printf("Timed out in wait_for_bb: status=%04x\n", + stat); + return 1; + } + writew(0xFFFF, &i2c_base->stat); /* clear delayed stuff*/ + return 0; +} + +/* + * Wait for the I2C controller to complete current action + * and update status + */ +static u16 wait_for_event(struct i2c_adapter *adap) +{ + struct i2c *i2c_base = omap24_get_base(adap); + u16 status; + int timeout = I2C_TIMEOUT; + + do { + udelay(adap->waitdelay); +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) + status = readw(&i2c_base->stat); +#else + /* Read RAW status */ + status = readw(&i2c_base->irqstatus_raw); +#endif + } while (!(status & + (I2C_STAT_ROVR | I2C_STAT_XUDF | I2C_STAT_XRDY | + I2C_STAT_RRDY | I2C_STAT_ARDY | I2C_STAT_NACK | + I2C_STAT_AL)) && timeout--); + + if (timeout <= 0) { + printf("Timed out in wait_for_event: status=%04x\n", + status); + /* + * If status is still 0 here, probably the bus pads have + * not been configured for I2C, and/or pull-ups are missing. + */ + printf("Check if pads/pull-ups of bus %d are properly configured\n", + adap->hwadapnr); + writew(0xFFFF, &i2c_base->stat); + status = 0; + } + + return status; +} + +static struct i2c *omap24_get_base(struct i2c_adapter *adap) +{ + switch (adap->hwadapnr) { + case 0: + return (struct i2c *)I2C_BASE1; + break; + case 1: + return (struct i2c *)I2C_BASE2; + break; +#if (I2C_BUS_MAX > 2) + case 2: + return (struct i2c *)I2C_BASE3; + break; +#if (I2C_BUS_MAX > 3) + case 3: + return (struct i2c *)I2C_BASE4; + break; +#if (I2C_BUS_MAX > 4) + case 4: + return (struct i2c *)I2C_BASE5; + break; +#endif +#endif +#endif + default: + printf("wrong hwadapnr: %d\n", adap->hwadapnr); + break; + } + return NULL; +} + +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED1) +#define CONFIG_SYS_OMAP24_I2C_SPEED1 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE1) +#define CONFIG_SYS_OMAP24_I2C_SLAVE1 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_0, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed, + CONFIG_SYS_OMAP24_I2C_SPEED, + CONFIG_SYS_OMAP24_I2C_SLAVE, + 0) +U_BOOT_I2C_ADAP_COMPLETE(omap24_1, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, omap24_i2c_setspeed, + CONFIG_SYS_OMAP24_I2C_SPEED1, + CONFIG_SYS_OMAP24_I2C_SLAVE1, + 1) +#if (I2C_BUS_MAX > 2) +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED2) +#define CONFIG_SYS_OMAP24_I2C_SPEED2 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE2) +#define CONFIG_SYS_OMAP24_I2C_SLAVE2 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_2, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, NULL, + CONFIG_SYS_OMAP24_I2C_SPEED2, + CONFIG_SYS_OMAP24_I2C_SLAVE2, + 2) +#if (I2C_BUS_MAX > 3) +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED3) +#define CONFIG_SYS_OMAP24_I2C_SPEED3 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE3) +#define CONFIG_SYS_OMAP24_I2C_SLAVE3 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_3, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, NULL, + CONFIG_SYS_OMAP24_I2C_SPEED3, + CONFIG_SYS_OMAP24_I2C_SLAVE3, + 3) +#if (I2C_BUS_MAX > 4) +#if !defined(CONFIG_SYS_OMAP24_I2C_SPEED4) +#define CONFIG_SYS_OMAP24_I2C_SPEED4 CONFIG_SYS_OMAP24_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_OMAP24_I2C_SLAVE4) +#define CONFIG_SYS_OMAP24_I2C_SLAVE4 CONFIG_SYS_OMAP24_I2C_SLAVE +#endif + +U_BOOT_I2C_ADAP_COMPLETE(omap24_4, omap24_i2c_init, omap24_i2c_probe, + omap24_i2c_read, omap24_i2c_write, NULL, + CONFIG_SYS_OMAP24_I2C_SPEED4, + CONFIG_SYS_OMAP24_I2C_SLAVE4, + 4) +#endif +#endif +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h new file mode 100644 index 000000000..3dae295e5 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/omap24xx_i2c.h @@ -0,0 +1,154 @@ +/* + * (C) Copyright 2004-2010 + * Texas Instruments, <www.ti.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#ifndef _OMAP2PLUS_I2C_H_ +#define _OMAP2PLUS_I2C_H_ + +/* I2C masks */ + +/* I2C Interrupt Enable Register (I2C_IE): */ +#define I2C_IE_GC_IE (1 << 5) +#define I2C_IE_XRDY_IE (1 << 4) /* Transmit data ready interrupt enable */ +#define I2C_IE_RRDY_IE (1 << 3) /* Receive data ready interrupt enable */ +#define I2C_IE_ARDY_IE (1 << 2) /* Register access ready interrupt enable */ +#define I2C_IE_NACK_IE (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_IE_AL_IE (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Status Register (I2C_STAT): */ + +#define I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define I2C_STAT_BB (1 << 12) /* Bus busy */ +#define I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define I2C_STAT_GC (1 << 5) +#define I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define I2C_STAT_NACK (1 << 1) /* No acknowledgment interrupt enable */ +#define I2C_STAT_AL (1 << 0) /* Arbitration lost interrupt enable */ + +/* I2C Interrupt Code Register (I2C_INTCODE): */ + +#define I2C_INTCODE_MASK 7 +#define I2C_INTCODE_NONE 0 +#define I2C_INTCODE_AL 1 /* Arbitration lost */ +#define I2C_INTCODE_NAK 2 /* No acknowledgement/general call */ +#define I2C_INTCODE_ARDY 3 /* Register access ready */ +#define I2C_INTCODE_RRDY 4 /* Rcv data ready */ +#define I2C_INTCODE_XRDY 5 /* Xmit data ready */ + +/* I2C Buffer Configuration Register (I2C_BUF): */ + +#define I2C_BUF_RDMA_EN (1 << 15) /* Receive DMA channel enable */ +#define I2C_BUF_XDMA_EN (1 << 7) /* Transmit DMA channel enable */ + +/* I2C Configuration Register (I2C_CON): */ + +#define I2C_CON_EN (1 << 15) /* I2C module enable */ +#define I2C_CON_BE (1 << 14) /* Big endian mode */ +#define I2C_CON_STB (1 << 11) /* Start byte mode (master mode only) */ +#define I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define I2C_CON_TRX (1 << 9) /* Transmitter/receiver mode */ + /* (master mode only) */ +#define I2C_CON_XA (1 << 8) /* Expand address */ +#define I2C_CON_STP (1 << 1) /* Stop condition (master mode only) */ +#define I2C_CON_STT (1 << 0) /* Start condition (master mode only) */ + +/* I2C System Test Register (I2C_SYSTEST): */ + +#define I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define I2C_SYSTEST_FREE (1 << 14) /* Free running mode, on brkpoint) */ +#define I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +#define I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense input value */ +#define I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive output value */ +#define I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense input value */ +#define I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive output value */ + +/* I2C System Status Register (I2C_SYSS): */ + +#define I2C_SYSS_RDONE (1 << 0) /* Internel reset monitoring */ + +#define I2C_SCLL_SCLL 0 +#define I2C_SCLL_SCLL_M 0xFF +#define I2C_SCLL_HSSCLL 8 +#define I2C_SCLH_HSSCLL_M 0xFF +#define I2C_SCLH_SCLH 0 +#define I2C_SCLH_SCLH_M 0xFF +#define I2C_SCLH_HSSCLH 8 +#define I2C_SCLH_HSSCLH_M 0xFF + +#define OMAP_I2C_STANDARD 100000 +#define OMAP_I2C_FAST_MODE 400000 +#define OMAP_I2C_HIGH_SPEED 3400000 + +#define SYSTEM_CLOCK_12 12000000 +#define SYSTEM_CLOCK_13 13000000 +#define SYSTEM_CLOCK_192 19200000 +#define SYSTEM_CLOCK_96 96000000 + +/* Use the reference value of 96MHz if not explicitly set by the board */ +#ifndef I2C_IP_CLK +#define I2C_IP_CLK SYSTEM_CLOCK_96 +#endif + +/* + * The reference minimum clock for high speed is 19.2MHz. + * The linux 2.6.30 kernel uses this value. + * The reference minimum clock for fast mode is 9.6MHz + * The reference minimum clock for standard mode is 4MHz + * In TRM, the value of 12MHz is used. + */ +#ifndef I2C_INTERNAL_SAMPLING_CLK +#define I2C_INTERNAL_SAMPLING_CLK 19200000 +#endif + +/* + * The equation for the low and high time is + * tlow = scll + scll_trim = (sampling clock * tlow_duty) / speed + * thigh = sclh + sclh_trim = (sampling clock * (1 - tlow_duty)) / speed + * + * If the duty cycle is 50% + * + * tlow = scll + scll_trim = sampling clock / (2 * speed) + * thigh = sclh + sclh_trim = sampling clock / (2 * speed) + * + * In TRM + * scll_trim = 7 + * sclh_trim = 5 + * + * The linux 2.6.30 kernel uses + * scll_trim = 6 + * sclh_trim = 6 + * + * These are the trim values for standard and fast speed + */ +#ifndef I2C_FASTSPEED_SCLL_TRIM +#define I2C_FASTSPEED_SCLL_TRIM 6 +#endif +#ifndef I2C_FASTSPEED_SCLH_TRIM +#define I2C_FASTSPEED_SCLH_TRIM 6 +#endif + +/* These are the trim values for high speed */ +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_ONE_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLL_TRIM I2C_FASTSPEED_SCLL_TRIM +#endif +#ifndef I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM +#define I2C_HIGHSPEED_PHASE_TWO_SCLH_TRIM I2C_FASTSPEED_SCLH_TRIM +#endif + +#define I2C_PSC_MAX 0x0f +#define I2C_PSC_MIN 0x00 + +#endif /* _OMAP24XX_I2C_H_ */ diff --git a/qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c b/qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c new file mode 100644 index 000000000..313288dd1 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/pca9564_i2c.c @@ -0,0 +1,176 @@ +/* + * File: drivers/i2c/pca9564.c + * Based on: drivers/i2c/s3c44b0_i2c.c + * Author: + * + * Created: 2009-06-23 + * Description: PCA9564 i2c bridge driver + * + * Modified: + * Copyright 2009 CJSC "NII STT", http://www.niistt.ru/ + * + * Bugs: + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <pca9564.h> +#include <asm/io.h> + +#define PCA_STA (CONFIG_PCA9564_BASE + 0) +#define PCA_TO (CONFIG_PCA9564_BASE + 0) +#define PCA_DAT (CONFIG_PCA9564_BASE + (1 << 2)) +#define PCA_ADR (CONFIG_PCA9564_BASE + (2 << 2)) +#define PCA_CON (CONFIG_PCA9564_BASE + (3 << 2)) + +static unsigned char pca_read_reg(unsigned int reg) +{ + return readb((void *)reg); +} + +static void pca_write_reg(unsigned int reg, unsigned char value) +{ + writeb(value, (void *)reg); +} + +static int pca_wait_busy(void) +{ + unsigned int timeout = 10000; + + while (!(pca_read_reg(PCA_CON) & PCA_CON_SI) && --timeout) + udelay(1); + + if (timeout == 0) + debug("I2C timeout!\n"); + + debug("CON = 0x%02x, STA = 0x%02x\n", pca_read_reg(PCA_CON), + pca_read_reg(PCA_STA)); + + return timeout ? 0 : 1; +} + +/*=====================================================================*/ +/* Public Functions */ +/*=====================================================================*/ + +/*----------------------------------------------------------------------- + * Initialization + */ +void i2c_init(int speed, int slaveaddr) +{ + pca_write_reg(PCA_CON, PCA_CON_ENSIO | speed); +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ + +int i2c_probe(uchar chip) +{ + unsigned char res; + + pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO); + pca_wait_busy(); + + pca_write_reg(PCA_CON, PCA_CON_STA | PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1) | 1); + res = pca_wait_busy(); + + if ((res == 0) && (pca_read_reg(PCA_STA) == 0x48)) + res = 1; + + pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO); + + return res; +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1)); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + if (alen > 0) { + pca_write_reg(PCA_DAT, addr); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO); + + udelay(500); + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, (chip << 1) | 1); + pca_wait_busy(); + + for (i = 0; i < len; ++i) { + if (i == len - 1) + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + else + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_AA); + + pca_wait_busy(); + buffer[i] = pca_read_reg(PCA_DAT); + + } + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STO); + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + + pca_write_reg(PCA_CON, PCA_CON_ENSIO | PCA_CON_STA); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + pca_write_reg(PCA_DAT, chip << 1); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + + if (alen > 0) { + pca_write_reg(PCA_DAT, addr); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + for (i = 0; i < len; ++i) { + pca_write_reg(PCA_DAT, buffer[i]); + pca_wait_busy(); + pca_write_reg(PCA_CON, PCA_CON_ENSIO); + } + + pca_write_reg(PCA_CON, PCA_CON_STO | PCA_CON_ENSIO); + + return 0; +} diff --git a/qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c b/qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c new file mode 100644 index 000000000..e7a15ba64 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/ppc4xx_i2c.c @@ -0,0 +1,419 @@ +/* + * (C) Copyright 2007-2009 + * Stefan Roese, DENX Software Engineering, sr@denx.de. + * + * based on work by Anne Sophie Harnois <anne-sophie.harnois@nextream.fr> + * + * (C) Copyright 2001 + * Bill Hunter, Wave 7 Optics, williamhunter@mediaone.net + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/ppc4xx.h> +#include <asm/ppc4xx-i2c.h> +#include <i2c.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +static inline struct ppc4xx_i2c *ppc4xx_get_i2c(int hwadapnr) +{ + unsigned long base; + +#if defined(CONFIG_440EP) || defined(CONFIG_440GR) || \ + defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \ + defined(CONFIG_460EX) || defined(CONFIG_460GT) + base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000700 + (hwadapnr * 0x100); +#elif defined(CONFIG_440) || defined(CONFIG_405EX) +/* all remaining 440 variants */ + base = CONFIG_SYS_PERIPHERAL_BASE + 0x00000400 + (hwadapnr * 0x100); +#else +/* all 405 variants */ + base = 0xEF600500 + (hwadapnr * 0x100); +#endif + return (struct ppc4xx_i2c *)base; +} + +static void _i2c_bus_reset(struct i2c_adapter *adap) +{ + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); + int i; + u8 dc; + + /* Reset status register */ + /* write 1 in SCMP and IRQA to clear these fields */ + out_8(&i2c->sts, 0x0A); + + /* write 1 in IRQP IRQD LA ICT XFRA to clear these fields */ + out_8(&i2c->extsts, 0x8F); + + /* Place chip in the reset state */ + out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST); + + /* Check if bus is free */ + dc = in_8(&i2c->directcntl); + if (!DIRCTNL_FREE(dc)){ + /* Try to set bus free state */ + out_8(&i2c->directcntl, IIC_DIRCNTL_SDAC | IIC_DIRCNTL_SCC); + + /* Wait until we regain bus control */ + for (i = 0; i < 100; ++i) { + dc = in_8(&i2c->directcntl); + if (DIRCTNL_FREE(dc)) + break; + + /* Toggle SCL line */ + dc ^= IIC_DIRCNTL_SCC; + out_8(&i2c->directcntl, dc); + udelay(10); + dc ^= IIC_DIRCNTL_SCC; + out_8(&i2c->directcntl, dc); + } + } + + /* Remove reset */ + out_8(&i2c->xtcntlss, 0); +} + +static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); + int val, divisor; + +#ifdef 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(); +#endif + + /* Handle possible failed I2C state */ + /* FIXME: put this into i2c_init_board()? */ + _i2c_bus_reset(adap); + + /* clear lo master address */ + out_8(&i2c->lmadr, 0); + + /* clear hi master address */ + out_8(&i2c->hmadr, 0); + + /* clear lo slave address */ + out_8(&i2c->lsadr, 0); + + /* clear hi slave address */ + out_8(&i2c->hsadr, 0); + + /* Clock divide Register */ + /* set divisor according to freq_opb */ + divisor = (get_OPB_freq() - 1) / 10000000; + if (divisor == 0) + divisor = 1; + out_8(&i2c->clkdiv, divisor); + + /* no interrupts */ + out_8(&i2c->intrmsk, 0); + + /* clear transfer count */ + out_8(&i2c->xfrcnt, 0); + + /* clear extended control & stat */ + /* write 1 in SRC SRS SWC SWS to clear these fields */ + out_8(&i2c->xtcntlss, 0xF0); + + /* Mode Control Register + Flush Slave/Master data buffer */ + out_8(&i2c->mdcntl, IIC_MDCNTL_FSDB | IIC_MDCNTL_FMDB); + + val = in_8(&i2c->mdcntl); + + /* Ignore General Call, slave transfers are ignored, + * disable interrupts, exit unknown bus state, enable hold + * SCL 100kHz normaly or FastMode for 400kHz and above + */ + + val |= IIC_MDCNTL_EUBS | IIC_MDCNTL_HSCL; + if (speed >= 400000) + val |= IIC_MDCNTL_FSM; + out_8(&i2c->mdcntl, val); + + /* clear control reg */ + out_8(&i2c->cntl, 0x00); +} + +/* + * This code tries to use the features of the 405GP i2c + * controller. It will transfer up to 4 bytes in one pass + * on the loop. It only does out_8((u8 *)lbz) to the buffer when it + * is possible to do out16(lhz) transfers. + * + * cmd_type is 0 for write 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + * + * 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. + * + * It does not support address only transfers, there must be + * a data part. If you want to write the address yourself, put + * it in the data pointer. + * + * It does not support transfer to/from address 0. + * + * It does not check XFRCNT. + */ +static int _i2c_transfer(struct i2c_adapter *adap, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + struct ppc4xx_i2c *i2c = ppc4xx_get_i2c(adap->hwadapnr); + u8 *ptr; + int reading; + int tran, cnt; + int result; + int status; + int i; + u8 creg; + + if (data == 0 || data_len == 0) { + /* Don't support data transfer of no length or to address 0 */ + printf( "i2c_transfer: bad call\n" ); + return IIC_NOK; + } + if (addr && addr_len) { + ptr = addr; + cnt = addr_len; + reading = 0; + } else { + ptr = data; + cnt = data_len; + reading = cmd_type; + } + + /* Clear Stop Complete Bit */ + out_8(&i2c->sts, IIC_STS_SCMP); + + /* Check init */ + i = 10; + do { + /* Get status */ + status = in_8(&i2c->sts); + i--; + } while ((status & IIC_STS_PT) && (i > 0)); + + if (status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + return(result); + } + + /* flush the Master/Slave Databuffers */ + out_8(&i2c->mdcntl, in_8(&i2c->mdcntl) | + IIC_MDCNTL_FMDB | IIC_MDCNTL_FSDB); + + /* need to wait 4 OPB clocks? code below should take that long */ + + /* 7-bit adressing */ + out_8(&i2c->hmadr, 0); + out_8(&i2c->lmadr, chip); + + tran = 0; + result = IIC_OK; + creg = 0; + + while (tran != cnt && (result == IIC_OK)) { + int bc,j; + + /* + * Control register = + * Normal transfer, 7-bits adressing, Transfer up to + * bc bytes, Normal start, Transfer is a sequence of transfers + */ + creg |= IIC_CNTL_PT; + + bc = (cnt - tran) > 4 ? 4 : cnt - tran; + creg |= (bc - 1) << 4; + /* if the real cmd type is write continue trans */ + if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt)) + creg |= IIC_CNTL_CHT; + + if (reading) { + creg |= IIC_CNTL_READ; + } else { + for(j = 0; j < bc; j++) { + /* Set buffer */ + out_8(&i2c->mdbuf, ptr[tran + j]); + } + } + out_8(&i2c->cntl, creg); + + /* + * Transfer is in progress + * we have to wait for upto 5 bytes of data + * 1 byte chip address+r/w bit then bc bytes + * of data. + * udelay(10) is 1 bit time at 100khz + * Doubled for slop. 20 is too small. + */ + i = 2 * 5 * 8; + do { + /* Get status */ + status = in_8(&i2c->sts); + udelay(10); + i--; + } while ((status & IIC_STS_PT) && !(status & IIC_STS_ERR) && + (i > 0)); + + if (status & IIC_STS_ERR) { + result = IIC_NOK; + status = in_8(&i2c->extsts); + /* Lost arbitration? */ + if (status & IIC_EXTSTS_LA) + result = IIC_NOK_LA; + /* Incomplete transfer? */ + if (status & IIC_EXTSTS_ICT) + result = IIC_NOK_ICT; + /* Transfer aborted? */ + if (status & IIC_EXTSTS_XFRA) + result = IIC_NOK_XFRA; + } else if ( status & IIC_STS_PT) { + result = IIC_NOK_TOUT; + } + + /* Command is reading => get buffer */ + if ((reading) && (result == IIC_OK)) { + /* Are there data in buffer */ + if (status & IIC_STS_MDBS) { + /* + * even if we have data we have to wait 4OPB + * clocks for it to hit the front of the FIFO, + * after that we can just read. We should check + * XFCNT here and if the FIFO is full there is + * no need to wait. + */ + udelay(1); + for (j = 0; j < bc; j++) + ptr[tran + j] = in_8(&i2c->mdbuf); + } else + result = IIC_NOK_DATA; + } + creg = 0; + tran += bc; + if (ptr == addr && tran == cnt) { + ptr = data; + cnt = data_len; + tran = 0; + reading = cmd_type; + if (reading) + creg = IIC_CNTL_RPST; + } + } + return result; +} + +static int ppc4xx_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + uchar buf[1]; + + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was <ACK>ed (i.e. there was a chip at that address which + * drove the data line low). + */ + return (_i2c_transfer(adap, 1, chip << 1, 0, 0, buf, 1) != 0); +} + +static int ppc4xx_i2c_transfer(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len, int read) +{ + uchar xaddr[4]; + int ret; + + if (alen > 4) { + printf("I2C: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + + +#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. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + ret = _i2c_transfer(adap, read, chip << 1, &xaddr[4 - alen], alen, + buffer, len); + if (ret) { + printf("I2C %s: failed %d\n", read ? "read" : "write", ret); + return 1; + } + + return 0; +} + +static int ppc4xx_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 1); +} + +static int ppc4xx_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + return ppc4xx_i2c_transfer(adap, chip, addr, alen, buffer, len, 0); +} + +static unsigned int ppc4xx_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + if (speed != adap->speed) + return -1; + return speed; +} + +/* + * Register ppc4xx i2c adapters + */ +#ifdef CONFIG_SYS_I2C_PPC4XX_CH0 +U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_0, ppc4xx_i2c_init, ppc4xx_i2c_probe, + ppc4xx_i2c_read, ppc4xx_i2c_write, + ppc4xx_i2c_set_bus_speed, + CONFIG_SYS_I2C_PPC4XX_SPEED_0, + CONFIG_SYS_I2C_PPC4XX_SLAVE_0, 0) +#endif +#ifdef CONFIG_SYS_I2C_PPC4XX_CH1 +U_BOOT_I2C_ADAP_COMPLETE(ppc4xx_1, ppc4xx_i2c_init, ppc4xx_i2c_probe, + ppc4xx_i2c_read, ppc4xx_i2c_write, + ppc4xx_i2c_set_bus_speed, + CONFIG_SYS_I2C_PPC4XX_SPEED_1, + CONFIG_SYS_I2C_PPC4XX_SLAVE_1, 1) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/rcar_i2c.c b/qemu/roms/u-boot/drivers/i2c/rcar_i2c.c new file mode 100644 index 000000000..50cebd622 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/rcar_i2c.c @@ -0,0 +1,290 @@ +/* + * drivers/i2c/rcar_i2c.c + * + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct rcar_i2c { + u32 icscr; + u32 icmcr; + u32 icssr; + u32 icmsr; + u32 icsier; + u32 icmier; + u32 icccr; + u32 icsar; + u32 icmar; + u32 icrxdtxd; + u32 icccr2; + u32 icmpr; + u32 ichpr; + u32 iclpr; +}; + +#define MCR_MDBS 0x80 /* non-fifo mode switch */ +#define MCR_FSCL 0x40 /* override SCL pin */ +#define MCR_FSDA 0x20 /* override SDA pin */ +#define MCR_OBPC 0x10 /* override pins */ +#define MCR_MIE 0x08 /* master if enable */ +#define MCR_TSBE 0x04 +#define MCR_FSB 0x02 /* force stop bit */ +#define MCR_ESG 0x01 /* en startbit gen. */ + +#define MSR_MASK 0x7f +#define MSR_MNR 0x40 /* nack received */ +#define MSR_MAL 0x20 /* arbitration lost */ +#define MSR_MST 0x10 /* sent a stop */ +#define MSR_MDE 0x08 +#define MSR_MDT 0x04 +#define MSR_MDR 0x02 +#define MSR_MAT 0x01 /* slave addr xfer done */ + +static const struct rcar_i2c *i2c_dev[CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS] = { + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C0_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C1_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C2_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C3_BASE, +}; + +static void rcar_i2c_raw_rw_common(struct rcar_i2c *dev, u8 chip, uint addr) +{ + /* set slave address */ + writel(chip << 1, &dev->icmar); + /* set register address */ + writel(addr, &dev->icrxdtxd); + /* clear status */ + writel(0, &dev->icmsr); + /* start master send */ + writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + + while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE)) + != (MSR_MAT | MSR_MDE)) + udelay(10); + + /* clear ESG */ + writel(MCR_MDBS | MCR_MIE, &dev->icmcr); + /* start SCLclk */ + writel(~(MSR_MAT | MSR_MDE), &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDE)) + udelay(10); +} + +static void rcar_i2c_raw_rw_finish(struct rcar_i2c *dev) +{ + while (!(readl(&dev->icmsr) & MSR_MST)) + udelay(10); + + writel(0, &dev->icmcr); +} + +static int +rcar_i2c_raw_write(struct rcar_i2c *dev, u8 chip, uint addr, u8 *val, int size) +{ + rcar_i2c_raw_rw_common(dev, chip, addr); + + /* set send date */ + writel(*val, &dev->icrxdtxd); + /* start SCLclk */ + writel(~MSR_MDE, &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDE)) + udelay(10); + + /* set stop condition */ + writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr); + /* start SCLclk */ + writel(~MSR_MDE, &dev->icmsr); + + rcar_i2c_raw_rw_finish(dev); + + return 0; +} + +static u8 +rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr) +{ + u8 ret; + + rcar_i2c_raw_rw_common(dev, chip, 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); + + while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR)) + != (MSR_MAT | MSR_MDR)) + udelay(10); + + /* clear ESG */ + writel(MCR_MDBS | MCR_MIE, &dev->icmcr); + /* prepare stop condition */ + writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr); + /* start SCLclk */ + writel(~(MSR_MAT | MSR_MDR), &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDR)) + udelay(10); + + /* get receive data */ + ret = (u8)readl(&dev->icrxdtxd); + /* start SCLclk */ + writel(~MSR_MDR, &dev->icmsr); + + rcar_i2c_raw_rw_finish(dev); + + return ret; +} + +/* + * SCL = iicck / (20 + SCGD * 8 + F[(ticf + tr + intd) * iicck]) + * iicck : I2C internal clock < 20 MHz + * ticf : I2C SCL falling time: 35 ns + * tr : I2C SCL rising time: 200 ns + * intd : LSI internal delay: I2C0: 50 ns I2C1-3: 5 + * F[n] : n rounded up to an integer + */ +static u32 rcar_clock_gen(int i2c_no, u32 bus_speed) +{ + u32 iicck, f, scl, scgd; + u32 intd = 5; + + int bit = 0, cdf_width = 3; + for (bit = 0; bit < (1 << cdf_width); bit++) { + iicck = CONFIG_HP_CLK_FREQ / (1 + bit); + if (iicck < 20000000) + break; + } + + if (bit > (1 << cdf_width)) { + puts("rcar-i2c: Can not get CDF\n"); + return 0; + } + + if (i2c_no == 0) + intd = 50; + + f = (35 + 200 + intd) * (iicck / 1000000000); + + for (scgd = 0; scgd < 0x40; scgd++) { + scl = iicck / (20 + (scgd * 8) + f); + if (scl <= bus_speed) + break; + } + + if (scgd > 0x40) { + puts("rcar-i2c: Can not get SDGB\n"); + return 0; + } + + debug("%s: scl: %d\n", __func__, scl); + debug("%s: bit %x\n", __func__, bit); + debug("%s: scgd %x\n", __func__, scgd); + debug("%s: iccr %x\n", __func__, (scgd << (cdf_width) | bit)); + + return scgd << (cdf_width) | bit; +} + +static void +rcar_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + u32 icccr = 0; + + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + + /* + * reset slave mode. + * slave mode is not used on this driver + */ + writel(0, &dev->icsier); + writel(0, &dev->icsar); + writel(0, &dev->icscr); + writel(0, &dev->icssr); + + /* reset master mode */ + writel(0, &dev->icmier); + writel(0, &dev->icmcr); + writel(0, &dev->icmsr); + writel(0, &dev->icmar); + + icccr = rcar_clock_gen(adap->hwadapnr, adap->speed); + if (icccr == 0) + puts("I2C: Init failed\n"); + else + writel(icccr, &dev->icccr); +} + +static int rcar_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, u8 *data, int len) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < len; i++) + data[i] = rcar_i2c_raw_read(dev, chip, addr + i); + + return 0; +} + +static int rcar_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, u8 *data, int len) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + return rcar_i2c_raw_write(dev, chip, addr, data, len); +} + +static int +rcar_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + return rcar_i2c_read(adap, dev, 0, 0, NULL, 0); +} + +static unsigned int rcar_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + u32 icccr; + int ret = 0; + + rcar_i2c_raw_rw_finish(dev); + + icccr = rcar_clock_gen(adap->hwadapnr, speed); + if (icccr == 0) { + puts("I2C: Init failed\n"); + ret = -1; + } else { + writel(icccr, &dev->icccr); + } + return ret; +} + +/* + * Register RCAR i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(rcar_0, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C0_SPEED, 0, 0) +U_BOOT_I2C_ADAP_COMPLETE(rcar_1, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C1_SPEED, 0, 1) +U_BOOT_I2C_ADAP_COMPLETE(rcar_2, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C2_SPEED, 0, 2) +U_BOOT_I2C_ADAP_COMPLETE(rcar_3, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C3_SPEED, 0, 3) diff --git a/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c new file mode 100644 index 000000000..fd328f054 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.c @@ -0,0 +1,1249 @@ +/* + * (C) Copyright 2002 + * David Mueller, ELSOFT AG, d.mueller@elsoft.ch + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* This code should work for both the S3C2400 and the S3C2410 + * as they seem to have the same I2C controller inside. + * The different address mapping is handled by the s3c24xx.h files below. + */ + +#include <common.h> +#include <fdtdec.h> +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/pinmux.h> +#else +#include <asm/arch/s3c24x0_cpu.h> +#endif +#include <asm/io.h> +#include <i2c.h> +#include "s3c24x0_i2c.h" + +#define I2C_WRITE 0 +#define I2C_READ 1 + +#define I2C_OK 0 +#define I2C_NOK 1 +#define I2C_NACK 2 +#define I2C_NOK_LA 3 /* Lost arbitration */ +#define I2C_NOK_TOUT 4 /* time out */ + +/* HSI2C specific register description */ + +/* I2C_CTL Register bits */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) /* Write/Send */ +#define HSI2C_TXCHON (1u << 7) /* Read/Receive */ +#define HSI2C_SW_RST (1u << 31) + +/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4) + +/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xff) + +/* I2C_INT_EN Register bits */ +#define HSI2C_TX_UNDERRUN_EN (1u << 2) +#define HSI2C_TX_OVERRUN_EN (1u << 3) +#define HSI2C_RX_UNDERRUN_EN (1u << 4) +#define HSI2C_RX_OVERRUN_EN (1u << 5) +#define HSI2C_INT_TRAILING_EN (1u << 6) +#define HSI2C_INT_I2C_EN (1u << 9) + +#define HSI2C_INT_ERROR_MASK (HSI2C_TX_UNDERRUN_EN |\ + HSI2C_TX_OVERRUN_EN |\ + HSI2C_RX_UNDERRUN_EN |\ + HSI2C_RX_OVERRUN_EN |\ + HSI2C_INT_TRAILING_EN) + +/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29) + +/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31) + +/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31) + +/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) +#define HSI2C_TIMEOUT_AUTO (1u << 4) +#define HSI2C_NO_DEV (1u << 3) +#define HSI2C_NO_DEV_ACK (1u << 2) +#define HSI2C_TRANS_ABORT (1u << 1) +#define HSI2C_TRANS_SUCCESS (1u << 0) +#define HSI2C_TRANS_ERROR_MASK (HSI2C_TIMEOUT_AUTO |\ + HSI2C_NO_DEV | HSI2C_NO_DEV_ACK |\ + HSI2C_TRANS_ABORT) +#define HSI2C_TRANS_FINISHED_MASK (HSI2C_TRANS_ERROR_MASK | HSI2C_TRANS_SUCCESS) + + +/* I2C_FIFO_STAT Register bits */ +#define HSI2C_RX_FIFO_EMPTY (1u << 24) +#define HSI2C_RX_FIFO_FULL (1u << 23) +#define HSI2C_TX_FIFO_EMPTY (1u << 8) +#define HSI2C_TX_FIFO_FULL (1u << 7) +#define HSI2C_RX_FIFO_LEVEL(x) (((x) >> 16) & 0x7f) +#define HSI2C_TX_FIFO_LEVEL(x) ((x) & 0x7f) + +#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) + +/* S3C I2C Controller bits */ +#define I2CSTAT_BSY 0x20 /* Busy bit */ +#define I2CSTAT_NACK 0x01 /* Nack bit */ +#define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ +#define I2CCON_IRPND 0x10 /* Interrupt pending bit */ +#define I2C_MODE_MT 0xC0 /* Master Transmit Mode */ +#define I2C_MODE_MR 0x80 /* Master Receive Mode */ +#define I2C_START_STOP 0x20 /* START / STOP */ +#define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ + +#define I2C_TIMEOUT_MS 1000 /* 1 second */ + +#define HSI2C_TIMEOUT_US 100000 /* 100 ms, finer granularity */ + + +/* To support VCMA9 boards and other who dont define max_i2c_num */ +#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 1 +#endif + +/* + * For SPL boot some boards need i2c before SDRAM is initialised so force + * variables to live in SRAM + */ +static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM] + __attribute__((section(".data"))); + +/** + * Get a pointer to the given bus index + * + * @bus_idx: Bus index to look up + * @return pointer to bus, or NULL if invalid or not available + */ +static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) +{ + if (bus_idx < ARRAY_SIZE(i2c_bus)) { + struct s3c24x0_i2c_bus *bus; + + bus = &i2c_bus[bus_idx]; + if (bus->active) + return bus; + } + + debug("Undefined bus: %d\n", bus_idx); + return NULL; +} + +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) +static int GetI2CSDA(void) +{ + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +#ifdef CONFIG_S3C2410 + return (readl(&gpio->gpedat) & 0x8000) >> 15; +#endif +#ifdef CONFIG_S3C2400 + return (readl(&gpio->pgdat) & 0x0020) >> 5; +#endif +} + +static void SetI2CSCL(int x) +{ + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); + +#ifdef CONFIG_S3C2410 + writel((readl(&gpio->gpedat) & ~0x4000) | + (x & 1) << 14, &gpio->gpedat); +#endif +#ifdef CONFIG_S3C2400 + writel((readl(&gpio->pgdat) & ~0x0040) | (x & 1) << 6, &gpio->pgdat); +#endif +} +#endif + +/* + * Wait til the byte transfer is completed. + * + * @param i2c- pointer to the appropriate i2c register bank. + * @return I2C_OK, if transmission was ACKED + * I2C_NACK, if transmission was NACKED + * I2C_NOK_TIMEOUT, if transaction did not complete in I2C_TIMEOUT_MS + */ + +static int WaitForXfer(struct s3c24x0_i2c *i2c) +{ + ulong start_time = get_timer(0); + + do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS); + + return I2C_NOK_TOUT; +} + +/* + * Wait for transfer completion. + * + * This function reads the interrupt status register waiting for the INT_I2C + * bit to be set, which indicates copletion of a transaction. + * + * @param i2c: pointer to the appropriate register bank + * + * @return: I2C_OK in case of successful completion, I2C_NOK_TIMEOUT in case + * the status bits do not get set in time, or an approrpiate error + * value in case of transfer errors. + */ +static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c) +{ + int i = HSI2C_TIMEOUT_US; + + while (i-- > 0) { + u32 int_status = readl(&i2c->usi_int_stat); + + if (int_status & HSI2C_INT_I2C_EN) { + u32 trans_status = readl(&i2c->usi_trans_status); + + /* Deassert pending interrupt. */ + writel(int_status, &i2c->usi_int_stat); + + if (trans_status & HSI2C_NO_DEV_ACK) { + debug("%s: no ACK from device\n", __func__); + return I2C_NACK; + } + if (trans_status & HSI2C_NO_DEV) { + debug("%s: no device\n", __func__); + return I2C_NOK; + } + if (trans_status & HSI2C_TRANS_ABORT) { + debug("%s: arbitration lost\n", __func__); + return I2C_NOK_LA; + } + if (trans_status & HSI2C_TIMEOUT_AUTO) { + debug("%s: device timed out\n", __func__); + return I2C_NOK_TOUT; + } + return I2C_OK; + } + udelay(1); + } + debug("%s: transaction timeout!\n", __func__); + return I2C_NOK_TOUT; +} + +static void ReadWriteByte(struct s3c24x0_i2c *i2c) +{ + writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); +} + +static struct s3c24x0_i2c *get_base_i2c(int bus) +{ +#ifdef CONFIG_EXYNOS4 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + + (EXYNOS4_I2C_SPACING + * bus)); + return i2c; +#elif defined CONFIG_EXYNOS5 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + + (EXYNOS5_I2C_SPACING + * bus)); + return i2c; +#else + return s3c24x0_get_base_i2c(); +#endif +} + +static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) +{ + ulong freq, pres = 16, div; +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + freq = get_i2c_clk(); +#else + freq = get_PCLK(); +#endif + /* calculate prescaler and divisor values */ + if ((freq / pres / (16 + 1)) > speed) + /* set prescaler to 512 */ + pres = 512; + + div = 0; + while ((freq / pres / (div + 1)) > speed) + div++; + + /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */ + writel((div & 0x0F) | 0xA0 | ((pres == 512) ? 0x40 : 0), &i2c->iiccon); + + /* init to SLAVE REVEIVE and set slaveaddr */ + writel(0, &i2c->iicstat); + writel(slaveadd, &i2c->iicadd); + /* program Master Transmit (and implicit STOP) */ + writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); +} + +static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + ulong clkin; + unsigned int op_clk = i2c_bus->clock_frequency; + unsigned int i = 0, utemp0 = 0, utemp1 = 0; + unsigned int t_ftl_cycle; + +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + clkin = get_i2c_clk(); +#else + clkin = get_PCLK(); +#endif + /* FPCLK / FI2C = + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE + * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + * uTemp1 = (TSCLK_L + TSCLK_H + 2) + * uTemp2 = TSCLK_L + TSCLK_H + */ + t_ftl_cycle = (readl(&hsregs->usi_conf) >> 16) & 0x7; + utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle; + + /* CLK_DIV max is 256 */ + for (i = 0; i < 256; i++) { + utemp1 = utemp0 / (i + 1); + if ((utemp1 < 512) && (utemp1 > 4)) { + i2c_bus->clk_cycle = utemp1 - 2; + i2c_bus->clk_div = i; + return 0; + } + } + return -1; +} + +static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + unsigned int t_sr_release; + unsigned int n_clkdiv; + unsigned int t_start_su, t_start_hd; + unsigned int t_stop_su; + unsigned int t_data_su, t_data_hd; + unsigned int t_scl_l, t_scl_h; + u32 i2c_timing_s1; + u32 i2c_timing_s2; + u32 i2c_timing_s3; + u32 i2c_timing_sla; + + n_clkdiv = i2c_bus->clk_div; + t_scl_l = i2c_bus->clk_cycle / 2; + t_scl_h = i2c_bus->clk_cycle / 2; + t_start_su = t_scl_l; + t_start_hd = t_scl_l; + t_stop_su = t_scl_l; + t_data_su = t_scl_l / 2; + t_data_hd = t_scl_l / 2; + t_sr_release = i2c_bus->clk_cycle; + + i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8; + i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0; + i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0; + i2c_timing_sla = t_data_hd << 0; + + writel(HSI2C_TRAILING_COUNT, &hsregs->usi_trailing_ctl); + + /* Clear to enable Timeout */ + clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0); + + /* set AUTO mode */ + writel(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf); + + /* Enable completion conditions' reporting. */ + writel(HSI2C_INT_I2C_EN, &hsregs->usi_int_en); + + /* Enable FIFOs */ + writel(HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN, &hsregs->usi_fifo_ctl); + + /* Currently operating in Fast speed mode. */ + writel(i2c_timing_s1, &hsregs->usi_timing_fs1); + writel(i2c_timing_s2, &hsregs->usi_timing_fs2); + writel(i2c_timing_s3, &hsregs->usi_timing_fs3); + writel(i2c_timing_sla, &hsregs->usi_timing_sla); +} + +/* SW reset for the high speed bus */ +static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *i2c = i2c_bus->hsregs; + u32 i2c_ctl; + + /* Set and clear the bit for reset */ + i2c_ctl = readl(&i2c->usi_ctl); + i2c_ctl |= HSI2C_SW_RST; + writel(i2c_ctl, &i2c->usi_ctl); + + i2c_ctl = readl(&i2c->usi_ctl); + i2c_ctl &= ~HSI2C_SW_RST; + writel(i2c_ctl, &i2c->usi_ctl); + + /* Initialize the configure registers */ + hsi2c_ch_init(i2c_bus); +} + +static void s3c24x0_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct s3c24x0_i2c *i2c; + struct s3c24x0_i2c_bus *bus; + +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); +#endif + ulong start_time = get_timer(0); + + /* By default i2c channel 0 is the current bus */ + i2c = get_base_i2c(adap->hwadapnr); + + /* + * In case the previous transfer is still going, wait to give it a + * chance to finish. + */ + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + printf("%s: I2C bus busy for %p\n", __func__, + &i2c->iicstat); + return; + } + } + +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + int i; + + if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { +#ifdef CONFIG_S3C2410 + ulong old_gpecon = readl(&gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + ulong old_gpecon = readl(&gpio->pgcon); +#endif + /* bus still busy probably by (most) previously interrupted + transfer */ + +#ifdef CONFIG_S3C2410 + /* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */ + writel((readl(&gpio->gpecon) & ~0xF0000000) | 0x10000000, + &gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + /* set I2CSDA and I2CSCL (PG5, PG6) to GPIO */ + writel((readl(&gpio->pgcon) & ~0x00003c00) | 0x00001000, + &gpio->pgcon); +#endif + + /* toggle I2CSCL until bus idle */ + SetI2CSCL(0); + udelay(1000); + i = 10; + while ((i > 0) && (GetI2CSDA() != 1)) { + SetI2CSCL(1); + udelay(1000); + SetI2CSCL(0); + udelay(1000); + i--; + } + SetI2CSCL(1); + udelay(1000); + + /* restore pin functions */ +#ifdef CONFIG_S3C2410 + writel(old_gpecon, &gpio->gpecon); +#endif +#ifdef CONFIG_S3C2400 + writel(old_gpecon, &gpio->pgcon); +#endif + } +#endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */ + i2c_ch_init(i2c, speed, slaveadd); + + bus = &i2c_bus[adap->hwadapnr]; + bus->active = true; + bus->regs = i2c; +} + +/* + * Poll the appropriate bit of the fifo status register until the interface is + * ready to process the next byte or timeout expires. + * + * In addition to the FIFO status register this function also polls the + * interrupt status register to be able to detect unexpected transaction + * completion. + * + * When FIFO is ready to process the next byte, this function returns I2C_OK. + * If in course of polling the INT_I2C assertion is detected, the function + * returns I2C_NOK. If timeout happens before any of the above conditions is + * met - the function returns I2C_NOK_TOUT; + + * @param i2c: pointer to the appropriate i2c register bank. + * @param rx_transfer: set to True if the receive transaction is in progress. + * @return: as described above. + */ +static unsigned hsi2c_poll_fifo(struct exynos5_hsi2c *i2c, bool rx_transfer) +{ + u32 fifo_bit = rx_transfer ? HSI2C_RX_FIFO_EMPTY : HSI2C_TX_FIFO_FULL; + int i = HSI2C_TIMEOUT_US; + + while (readl(&i2c->usi_fifo_stat) & fifo_bit) { + if (readl(&i2c->usi_int_stat) & HSI2C_INT_I2C_EN) { + /* + * There is a chance that assertion of + * HSI2C_INT_I2C_EN and deassertion of + * HSI2C_RX_FIFO_EMPTY happen simultaneously. Let's + * give FIFO status priority and check it one more + * time before reporting interrupt. The interrupt will + * be reported next time this function is called. + */ + if (rx_transfer && + !(readl(&i2c->usi_fifo_stat) & fifo_bit)) + break; + return I2C_NOK; + } + if (!i--) { + debug("%s: FIFO polling timeout!\n", __func__); + return I2C_NOK_TOUT; + } + udelay(1); + } + return I2C_OK; +} + +/* + * Preapre hsi2c transaction, either read or write. + * + * Set up transfer as described in section 27.5.1.2 'I2C Channel Auto Mode' of + * the 5420 UM. + * + * @param i2c: pointer to the appropriate i2c register bank. + * @param chip: slave address on the i2c bus (with read/write bit exlcuded) + * @param len: number of bytes expected to be sent or received + * @param rx_transfer: set to true for receive transactions + * @param: issue_stop: set to true if i2c stop condition should be generated + * after this transaction. + * @return: I2C_NOK_TOUT in case the bus remained busy for HSI2C_TIMEOUT_US, + * I2C_OK otherwise. + */ +static int hsi2c_prepare_transaction(struct exynos5_hsi2c *i2c, + u8 chip, + u16 len, + bool rx_transfer, + bool issue_stop) +{ + u32 conf; + + conf = len | HSI2C_MASTER_RUN; + + if (issue_stop) + conf |= HSI2C_STOP_AFTER_TRANS; + + /* Clear to enable Timeout */ + writel(readl(&i2c->usi_timeout) & ~HSI2C_TIMEOUT_EN, &i2c->usi_timeout); + + /* Set slave address */ + writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr); + + if (rx_transfer) { + /* i2c master, read transaction */ + writel((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* read up to len bytes, stop after transaction is finished */ + writel(conf | HSI2C_READ_WRITE, &i2c->usi_auto_conf); + } else { + /* i2c master, write transaction */ + writel((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* write up to len bytes, stop after transaction is finished */ + writel(conf, &i2c->usi_auto_conf); + } + + /* Reset all pending interrupt status bits we care about, if any */ + writel(HSI2C_INT_I2C_EN, &i2c->usi_int_stat); + + return I2C_OK; +} + +/* + * Wait while i2c bus is settling down (mostly stop gets completed). + */ +static int hsi2c_wait_while_busy(struct exynos5_hsi2c *i2c) +{ + int i = HSI2C_TIMEOUT_US; + + while (readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY) { + if (!i--) { + debug("%s: bus busy\n", __func__); + return I2C_NOK_TOUT; + } + udelay(1); + } + return I2C_OK; +} + +static int hsi2c_write(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + unsigned char data[], + unsigned short len, + bool issue_stop) +{ + int i, rv = 0; + + if (!(len + alen)) { + /* Writes of zero length not supported in auto mode. */ + debug("%s: zero length writes not supported\n", __func__); + return I2C_NOK; + } + + rv = hsi2c_prepare_transaction + (i2c, chip, len + alen, false, issue_stop); + if (rv != I2C_OK) + return rv; + + /* Move address, if any, and the data, if any, into the FIFO. */ + for (i = 0; i < alen; i++) { + rv = hsi2c_poll_fifo(i2c, false); + if (rv != I2C_OK) { + debug("%s: address write failed\n", __func__); + goto write_error; + } + writel(addr[i], &i2c->usi_txdata); + } + + for (i = 0; i < len; i++) { + rv = hsi2c_poll_fifo(i2c, false); + if (rv != I2C_OK) { + debug("%s: data write failed\n", __func__); + goto write_error; + } + writel(data[i], &i2c->usi_txdata); + } + + rv = hsi2c_wait_for_trx(i2c); + + write_error: + if (issue_stop) { + int tmp_ret = hsi2c_wait_while_busy(i2c); + if (rv == I2C_OK) + rv = tmp_ret; + } + + writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */ + return rv; +} + +static int hsi2c_read(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + unsigned char data[], + unsigned short len) +{ + int i, rv, tmp_ret; + bool drop_data = false; + + if (!len) { + /* Reads of zero length not supported in auto mode. */ + debug("%s: zero length read adjusted\n", __func__); + drop_data = true; + len = 1; + } + + if (alen) { + /* Internal register adress needs to be written first. */ + rv = hsi2c_write(i2c, chip, addr, alen, NULL, 0, false); + if (rv != I2C_OK) + return rv; + } + + rv = hsi2c_prepare_transaction(i2c, chip, len, true, true); + + if (rv != I2C_OK) + return rv; + + for (i = 0; i < len; i++) { + rv = hsi2c_poll_fifo(i2c, true); + if (rv != I2C_OK) + goto read_err; + if (drop_data) + continue; + data[i] = readl(&i2c->usi_rxdata); + } + + rv = hsi2c_wait_for_trx(i2c); + + read_err: + tmp_ret = hsi2c_wait_while_busy(i2c); + if (rv == I2C_OK) + rv = tmp_ret; + + writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */ + return rv; +} + +static unsigned int s3c24x0_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct s3c24x0_i2c_bus *i2c_bus; + + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + + i2c_bus->clock_frequency = speed; + + if (i2c_bus->is_highspeed) { + if (hsi2c_get_clk_details(i2c_bus)) + return -1; + hsi2c_ch_init(i2c_bus); + } else { + i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, + CONFIG_SYS_I2C_S3C24X0_SLAVE); + } + + return 0; +} + +#ifdef CONFIG_EXYNOS5 +static void exynos_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) +{ + /* This will override the speed selected in the fdt for that port */ + debug("i2c_init(speed=%u, slaveaddr=0x%x)\n", speed, slaveaddr); + if (i2c_set_bus_speed(speed)) + printf("i2c_init: failed to init bus %d for speed = %d\n", + adap->hwadapnr, speed); +} +#endif + +/* + * cmd_type is 0 for write, 1 for read. + * + * addr_len can take any value from 0-255, it is only limited + * by the char, we could make it larger if needed. If it is + * 0 we skip the address write cycle. + */ +static int i2c_transfer(struct s3c24x0_i2c *i2c, + unsigned char cmd_type, + unsigned char chip, + unsigned char addr[], + unsigned char addr_len, + unsigned char data[], + unsigned short data_len) +{ + int i = 0, result; + ulong start_time = get_timer(0); + + if (data == 0 || data_len == 0) { + /*Don't support data transfer of no length or to address 0 */ + debug("i2c_transfer: bad call\n"); + return I2C_NOK; + } + + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) + return I2C_NOK_TOUT; + } + + writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); + + /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + if ((cmd_type == I2C_WRITE) || (addr && addr_len)) + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + else + writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + + /* Wait for chip address to transmit. */ + result = WaitForXfer(i2c); + if (result != I2C_OK) + goto bailout; + + /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + } + i = 0; + if (result != I2C_OK) + goto bailout; + } + + switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + } + break; + + case I2C_READ: + if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ + writel(chip, &i2c->iicds); + + /* Generate a re-START. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + + if (result != I2C_OK) + goto bailout; + } + + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + writel(readl(&i2c->iiccon) + & ~I2CCON_ACKGEN, + &i2c->iiccon); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + data[i++] = readl(&i2c->iicds); + } + if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ + break; + + default: + debug("i2c_transfer: bad call\n"); + result = I2C_NOK; + break; + } + +bailout: + /* Send STOP. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(i2c); + + return result; +} + +static int s3c24x0_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct s3c24x0_i2c_bus *i2c_bus; + uchar buf[1]; + int ret; + + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + buf[0] = 0; + + /* + * What is needed is to send the chip address and verify that the + * address was <ACK>ed (i.e. there was a chip at that address which + * drove the data line low). + */ + if (i2c_bus->is_highspeed) { + ret = hsi2c_read(i2c_bus->hsregs, + chip, 0, 0, buf, 1); + } else { + ret = i2c_transfer(i2c_bus->regs, + I2C_READ, chip << 1, 0, 0, buf, 1); + } + + return ret != I2C_OK; +} + +static int s3c24x0_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct s3c24x0_i2c_bus *i2c_bus; + uchar xaddr[4]; + int ret; + + if (alen > 4) { + debug("I2C read: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } + +#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. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + + if (i2c_bus->is_highspeed) + ret = hsi2c_read(i2c_bus->hsregs, chip, &xaddr[4 - alen], + alen, buffer, len); + else + ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len); + + if (ret) { + if (i2c_bus->is_highspeed) + exynos5_i2c_reset(i2c_bus); + debug("I2c read failed %d\n", ret); + return 1; + } + return 0; +} + +static int s3c24x0_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct s3c24x0_i2c_bus *i2c_bus; + uchar xaddr[4]; + int ret; + + if (alen > 4) { + debug("I2C write: addr len %d not supported\n", alen); + return 1; + } + + if (alen > 0) { + xaddr[0] = (addr >> 24) & 0xFF; + xaddr[1] = (addr >> 16) & 0xFF; + xaddr[2] = (addr >> 8) & 0xFF; + xaddr[3] = addr & 0xFF; + } +#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. + */ + if (alen > 0) + chip |= ((addr >> (alen * 8)) & + CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); +#endif + i2c_bus = get_bus(adap->hwadapnr); + if (!i2c_bus) + return -1; + + if (i2c_bus->is_highspeed) + ret = hsi2c_write(i2c_bus->hsregs, chip, &xaddr[4 - alen], + alen, buffer, len, true); + else + ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len); + + if (ret != 0) { + if (i2c_bus->is_highspeed) + exynos5_i2c_reset(i2c_bus); + return 1; + } else { + return 0; + } +} + +#ifdef CONFIG_OF_CONTROL +static void process_nodes(const void *blob, int node_list[], int count, + int is_highspeed) +{ + struct s3c24x0_i2c_bus *bus; + int i; + + for (i = 0; i < count; i++) { + int node = node_list[i]; + + if (node <= 0) + continue; + + bus = &i2c_bus[i]; + bus->active = true; + bus->is_highspeed = is_highspeed; + + if (is_highspeed) + bus->hsregs = (struct exynos5_hsi2c *) + fdtdec_get_addr(blob, node, "reg"); + else + bus->regs = (struct s3c24x0_i2c *) + fdtdec_get_addr(blob, node, "reg"); + + bus->id = pinmux_decode_periph_id(blob, node); + bus->clock_frequency = fdtdec_get_int(blob, node, + "clock-frequency", + CONFIG_SYS_I2C_S3C24X0_SPEED); + bus->node = node; + bus->bus_num = i; + exynos_pinmux_config(bus->id, 0); + + /* Mark position as used */ + node_list[i] = -1; + } +} + +void board_i2c_init(const void *blob) +{ + int node_list[CONFIG_MAX_I2C_NUM]; + int count; + + /* First get the normal i2c ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_S3C2440_I2C, node_list, + CONFIG_MAX_I2C_NUM); + process_nodes(blob, node_list, count, 0); + + /* Now look for high speed i2c ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_EXYNOS5_I2C, node_list, + CONFIG_MAX_I2C_NUM); + process_nodes(blob, node_list, count, 1); + +} + +int i2c_get_bus_num_fdt(int node) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(i2c_bus); i++) { + if (node == i2c_bus[i].node) + return i; + } + + debug("%s: Can't find any matched I2C bus\n", __func__); + return -1; +} + +int i2c_reset_port_fdt(const void *blob, int node) +{ + struct s3c24x0_i2c_bus *i2c_bus; + int bus; + + bus = i2c_get_bus_num_fdt(node); + if (bus < 0) { + debug("could not get bus for node %d\n", node); + return -1; + } + + i2c_bus = get_bus(bus); + if (!i2c_bus) { + debug("get_bus() failed for node node %d\n", node); + return -1; + } + + if (i2c_bus->is_highspeed) { + if (hsi2c_get_clk_details(i2c_bus)) + return -1; + hsi2c_ch_init(i2c_bus); + } else { + i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, + CONFIG_SYS_I2C_S3C24X0_SLAVE); + } + + return 0; +} +#endif + +/* + * Register s3c24x0 i2c adapters + */ +#if defined(CONFIG_EXYNOS5420) +U_BOOT_I2C_ADAP_COMPLETE(i2c00, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(i2c01, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 1) +U_BOOT_I2C_ADAP_COMPLETE(i2c02, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 2) +U_BOOT_I2C_ADAP_COMPLETE(i2c03, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 3) +U_BOOT_I2C_ADAP_COMPLETE(i2c04, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 4) +U_BOOT_I2C_ADAP_COMPLETE(i2c05, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 5) +U_BOOT_I2C_ADAP_COMPLETE(i2c06, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 6) +U_BOOT_I2C_ADAP_COMPLETE(i2c07, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 7) +U_BOOT_I2C_ADAP_COMPLETE(i2c08, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 8) +U_BOOT_I2C_ADAP_COMPLETE(i2c09, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 9) +U_BOOT_I2C_ADAP_COMPLETE(i2c10, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 10) +#elif defined(CONFIG_EXYNOS5250) +U_BOOT_I2C_ADAP_COMPLETE(i2c00, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(i2c01, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 1) +U_BOOT_I2C_ADAP_COMPLETE(i2c02, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 2) +U_BOOT_I2C_ADAP_COMPLETE(i2c03, exynos_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 3) +U_BOOT_I2C_ADAP_COMPLETE(i2c04, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 4) +U_BOOT_I2C_ADAP_COMPLETE(i2c05, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 5) +U_BOOT_I2C_ADAP_COMPLETE(i2c06, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 6) +U_BOOT_I2C_ADAP_COMPLETE(i2c07, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 7) +U_BOOT_I2C_ADAP_COMPLETE(i2c08, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 8) +U_BOOT_I2C_ADAP_COMPLETE(i2c09, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 9) +U_BOOT_I2C_ADAP_COMPLETE(s3c10, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 10) +#elif defined(CONFIG_EXYNOS4) +U_BOOT_I2C_ADAP_COMPLETE(i2c00, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(i2c01, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 1) +U_BOOT_I2C_ADAP_COMPLETE(i2c02, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 2) +U_BOOT_I2C_ADAP_COMPLETE(i2c03, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 3) +U_BOOT_I2C_ADAP_COMPLETE(i2c04, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 4) +U_BOOT_I2C_ADAP_COMPLETE(i2c05, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 5) +U_BOOT_I2C_ADAP_COMPLETE(i2c06, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 6) +U_BOOT_I2C_ADAP_COMPLETE(i2c07, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 7) +U_BOOT_I2C_ADAP_COMPLETE(i2c08, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 8) +#else +U_BOOT_I2C_ADAP_COMPLETE(s3c0, s3c24x0_i2c_init, s3c24x0_i2c_probe, + s3c24x0_i2c_read, s3c24x0_i2c_write, + s3c24x0_i2c_set_bus_speed, + CONFIG_SYS_I2C_S3C24X0_SPEED, + CONFIG_SYS_I2C_S3C24X0_SLAVE, 0) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h new file mode 100644 index 000000000..1ae73d277 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/s3c24x0_i2c.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _S3C24X0_I2C_H +#define _S3C24X0_I2C_H + +struct s3c24x0_i2c { + u32 iiccon; + u32 iicstat; + u32 iicadd; + u32 iicds; + u32 iiclc; +}; + +struct exynos5_hsi2c { + u32 usi_ctl; + u32 usi_fifo_ctl; + u32 usi_trailing_ctl; + u32 usi_clk_ctl; + u32 usi_clk_slot; + u32 spi_ctl; + u32 uart_ctl; + u32 res1; + u32 usi_int_en; + u32 usi_int_stat; + u32 usi_modem_stat; + u32 usi_error_stat; + u32 usi_fifo_stat; + u32 usi_txdata; + u32 usi_rxdata; + u32 res2; + u32 usi_conf; + u32 usi_auto_conf; + u32 usi_timeout; + u32 usi_manual_cmd; + u32 usi_trans_status; + u32 usi_timing_hs1; + u32 usi_timing_hs2; + u32 usi_timing_hs3; + u32 usi_timing_fs1; + u32 usi_timing_fs2; + u32 usi_timing_fs3; + u32 usi_timing_sla; + u32 i2c_addr; +}; + +struct s3c24x0_i2c_bus { + bool active; /* port is active and available */ + int node; /* device tree node */ + int bus_num; /* i2c bus number */ + struct s3c24x0_i2c *regs; + struct exynos5_hsi2c *hsregs; + int is_highspeed; /* High speed type, rather than I2C */ + unsigned clock_frequency; + int id; + unsigned clk_cycle; + unsigned clk_div; +}; +#endif /* _S3C24X0_I2C_H */ diff --git a/qemu/roms/u-boot/drivers/i2c/sh_i2c.c b/qemu/roms/u-boot/drivers/i2c/sh_i2c.c new file mode 100644 index 000000000..e7e96921d --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/sh_i2c.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2011, 2013 Renesas Solutions Corp. + * Copyright (C) 2011, 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <asm/io.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Every register is 32bit aligned, but only 8bits in size */ +#define ureg(name) u8 name; u8 __pad_##name##0; u16 __pad_##name##1; +struct sh_i2c { + ureg(icdr); + ureg(iccr); + ureg(icsr); + ureg(icic); + ureg(iccl); + ureg(icch); +}; +#undef ureg + +/* ICCR */ +#define SH_I2C_ICCR_ICE (1 << 7) +#define SH_I2C_ICCR_RACK (1 << 6) +#define SH_I2C_ICCR_RTS (1 << 4) +#define SH_I2C_ICCR_BUSY (1 << 2) +#define SH_I2C_ICCR_SCP (1 << 0) + +/* ICSR / ICIC */ +#define SH_IC_BUSY (1 << 4) +#define SH_IC_TACK (1 << 2) +#define SH_IC_WAIT (1 << 1) +#define SH_IC_DTE (1 << 0) + +#ifdef CONFIG_SH_I2C_8BIT +/* store 8th bit of iccl and icch in ICIC register */ +#define SH_I2C_ICIC_ICCLB8 (1 << 7) +#define SH_I2C_ICIC_ICCHB8 (1 << 6) +#endif + +static const struct sh_i2c *i2c_dev[CONFIG_SYS_I2C_SH_NUM_CONTROLLERS] = { + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE0, +#ifdef CONFIG_SYS_I2C_SH_BASE1 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE1, +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE2 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE2, +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE3 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE3, +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE4 + (struct sh_i2c *)CONFIG_SYS_I2C_SH_BASE4, +#endif +}; + +static u16 iccl, icch; + +#define IRQ_WAIT 1000 + +static void sh_irq_dte(struct sh_i2c *dev) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (SH_IC_DTE & readb(&dev->icsr)) + break; + udelay(10); + } +} + +static int sh_irq_dte_with_tack(struct sh_i2c *dev) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (SH_IC_DTE & readb(&dev->icsr)) + break; + if (SH_IC_TACK & readb(&dev->icsr)) + return -1; + udelay(10); + } + return 0; +} + +static void sh_irq_busy(struct sh_i2c *dev) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (!(SH_IC_BUSY & readb(&dev->icsr))) + break; + udelay(10); + } +} + +static int sh_i2c_set_addr(struct sh_i2c *dev, u8 chip, u8 addr, int stop) +{ + u8 icic = SH_IC_TACK; + + debug("%s: chip: %x, addr: %x iccl: %x, icch %x\n", + __func__, chip, addr, iccl, icch); + clrbits_8(&dev->iccr, SH_I2C_ICCR_ICE); + setbits_8(&dev->iccr, SH_I2C_ICCR_ICE); + + writeb(iccl & 0xff, &dev->iccl); + writeb(icch & 0xff, &dev->icch); +#ifdef CONFIG_SH_I2C_8BIT + if (iccl > 0xff) + icic |= SH_I2C_ICIC_ICCLB8; + if (icch > 0xff) + icic |= SH_I2C_ICIC_ICCHB8; +#endif + writeb(icic, &dev->icic); + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &dev->iccr); + sh_irq_dte(dev); + + clrbits_8(&dev->icsr, SH_IC_TACK); + writeb(chip << 1, &dev->icdr); + if (sh_irq_dte_with_tack(dev) != 0) + return -1; + + writeb(addr, &dev->icdr); + if (stop) + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS), &dev->iccr); + + if (sh_irq_dte_with_tack(dev) != 0) + return -1; + return 0; +} + +static void sh_i2c_finish(struct sh_i2c *dev) +{ + writeb(0, &dev->icsr); + clrbits_8(&dev->iccr, SH_I2C_ICCR_ICE); +} + +static int +sh_i2c_raw_write(struct sh_i2c *dev, u8 chip, uint addr, u8 val) +{ + int ret = -1; + if (sh_i2c_set_addr(dev, chip, addr, 0) != 0) + goto exit0; + udelay(10); + + writeb(val, &dev->icdr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + + writeb((SH_I2C_ICCR_ICE | SH_I2C_ICCR_RTS), &dev->iccr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + sh_irq_busy(dev); + ret = 0; + +exit0: + sh_i2c_finish(dev); + return ret; +} + +static int sh_i2c_raw_read(struct sh_i2c *dev, u8 chip, u8 addr) +{ + int ret = -1; + +#if defined(CONFIG_SH73A0) + if (sh_i2c_set_addr(dev, chip, addr, 0) != 0) + goto exit0; +#else + if (sh_i2c_set_addr(dev, chip, addr, 1) != 0) + goto exit0; + udelay(100); +#endif + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RTS|SH_I2C_ICCR_BUSY), &dev->iccr); + sh_irq_dte(dev); + + writeb(chip << 1 | 0x01, &dev->icdr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_SCP), &dev->iccr); + if (sh_irq_dte_with_tack(dev) != 0) + goto exit0; + + ret = readb(&dev->icdr) & 0xff; + + writeb((SH_I2C_ICCR_ICE|SH_I2C_ICCR_RACK), &dev->iccr); + readb(&dev->icdr); /* Dummy read */ + sh_irq_busy(dev); + +exit0: + sh_i2c_finish(dev); + + return ret; +} + +static void +sh_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + int num, denom, tmp; + + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + + /* + * Calculate the value for iccl. From the data sheet: + * iccl = (p-clock / transfer-rate) * (L / (L + H)) + * where L and H are the SCL low and high ratio. + */ + num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_LOW; + denom = speed * (CONFIG_SH_I2C_DATA_HIGH + CONFIG_SH_I2C_DATA_LOW); + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + iccl = (u16)((num/denom) + 1); + else + iccl = (u16)(num/denom); + + /* Calculate the value for icch. From the data sheet: + icch = (p clock / transfer rate) * (H / (L + H)) */ + num = CONFIG_SH_I2C_CLOCK * CONFIG_SH_I2C_DATA_HIGH; + tmp = num * 10 / denom; + if (tmp % 10 >= 5) + icch = (u16)((num/denom) + 1); + else + icch = (u16)(num/denom); + + debug("clock: %d, speed %d, iccl: %x, icch: %x\n", + CONFIG_SH_I2C_CLOCK, speed, iccl, icch); +} + +static int sh_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, u8 *data, int len) +{ + int ret, i; + struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr]; + + for (i = 0; i < len; i++) { + ret = sh_i2c_raw_read(dev, chip, addr + i); + if (ret < 0) + return -1; + + data[i] = ret & 0xff; + debug("%s: data[%d]: %02x\n", __func__, i, data[i]); + } + + return 0; +} + +static int sh_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, u8 *data, int len) +{ + struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < len; i++) { + debug("%s: data[%d]: %02x\n", __func__, i, data[i]); + if (sh_i2c_raw_write(dev, chip, addr + i, data[i]) != 0) + return -1; + } + return 0; +} + +static int +sh_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + u8 dummy[1]; + + return sh_i2c_read(adap, dev, 0, 0, dummy, sizeof dummy); +} + +static unsigned int sh_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct sh_i2c *dev = (struct sh_i2c *)i2c_dev[adap->hwadapnr]; + + sh_i2c_finish(dev); + sh_i2c_init(adap, speed, 0); + + return 0; +} + +/* + * Register RCAR i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(sh_0, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED0, 0, 0) +#ifdef CONFIG_SYS_I2C_SH_BASE1 +U_BOOT_I2C_ADAP_COMPLETE(sh_1, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED1, 0, 1) +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE2 +U_BOOT_I2C_ADAP_COMPLETE(sh_2, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED2, 0, 2) +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE3 +U_BOOT_I2C_ADAP_COMPLETE(sh_3, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED3, 0, 3) +#endif +#ifdef CONFIG_SYS_I2C_SH_BASE4 +U_BOOT_I2C_ADAP_COMPLETE(sh_4, sh_i2c_init, sh_i2c_probe, sh_i2c_read, + sh_i2c_write, sh_i2c_set_bus_speed, CONFIG_SYS_I2C_SH_SPEED4, 0, 4) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c b/qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c new file mode 100644 index 000000000..6c2f221fe --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/sh_sh7734_i2c.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2012 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com> + * Copyright (C) 2012 Renesas Solutions Corp. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <i2c.h> +#include <asm/io.h> + +struct sh_i2c { + u8 iccr1; + u8 iccr2; + u8 icmr; + u8 icier; + u8 icsr; + u8 sar; + u8 icdrt; + u8 icdrr; + u8 nf2cyc; + u8 __pad0; + u8 __pad1; +}; + +static struct sh_i2c *base; +static u8 iccr1_cks, nf2cyc; + +/* ICCR1 */ +#define SH_I2C_ICCR1_ICE (1 << 7) +#define SH_I2C_ICCR1_RCVD (1 << 6) +#define SH_I2C_ICCR1_MST (1 << 5) +#define SH_I2C_ICCR1_TRS (1 << 4) +#define SH_I2C_ICCR1_MTRS \ + (SH_I2C_ICCR1_MST | SH_I2C_ICCR1_TRS) + +/* ICCR1 */ +#define SH_I2C_ICCR2_BBSY (1 << 7) +#define SH_I2C_ICCR2_SCP (1 << 6) +#define SH_I2C_ICCR2_SDAO (1 << 5) +#define SH_I2C_ICCR2_SDAOP (1 << 4) +#define SH_I2C_ICCR2_SCLO (1 << 3) +#define SH_I2C_ICCR2_IICRST (1 << 1) + +#define SH_I2C_ICIER_TIE (1 << 7) +#define SH_I2C_ICIER_TEIE (1 << 6) +#define SH_I2C_ICIER_RIE (1 << 5) +#define SH_I2C_ICIER_NAKIE (1 << 4) +#define SH_I2C_ICIER_STIE (1 << 3) +#define SH_I2C_ICIER_ACKE (1 << 2) +#define SH_I2C_ICIER_ACKBR (1 << 1) +#define SH_I2C_ICIER_ACKBT (1 << 0) + +#define SH_I2C_ICSR_TDRE (1 << 7) +#define SH_I2C_ICSR_TEND (1 << 6) +#define SH_I2C_ICSR_RDRF (1 << 5) +#define SH_I2C_ICSR_NACKF (1 << 4) +#define SH_I2C_ICSR_STOP (1 << 3) +#define SH_I2C_ICSR_ALOVE (1 << 2) +#define SH_I2C_ICSR_AAS (1 << 1) +#define SH_I2C_ICSR_ADZ (1 << 0) + +#define IRQ_WAIT 1000 + +static void sh_i2c_send_stop(struct sh_i2c *base) +{ + clrbits_8(&base->iccr2, SH_I2C_ICCR2_BBSY | SH_I2C_ICCR2_SCP); +} + +static int check_icsr_bits(struct sh_i2c *base, u8 bits) +{ + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (bits & readb(&base->icsr)) + return 0; + udelay(10); + } + + return 1; +} + +static int check_stop(struct sh_i2c *base) +{ + int ret = check_icsr_bits(base, SH_I2C_ICSR_STOP); + clrbits_8(&base->icsr, SH_I2C_ICSR_STOP); + + return ret; +} + +static int check_tend(struct sh_i2c *base, int stop) +{ + int ret = check_icsr_bits(base, SH_I2C_ICSR_TEND); + + if (stop) { + clrbits_8(&base->icsr, SH_I2C_ICSR_STOP); + sh_i2c_send_stop(base); + } + + clrbits_8(&base->icsr, SH_I2C_ICSR_TEND); + return ret; +} + +static int check_tdre(struct sh_i2c *base) +{ + return check_icsr_bits(base, SH_I2C_ICSR_TDRE); +} + +static int check_rdrf(struct sh_i2c *base) +{ + return check_icsr_bits(base, SH_I2C_ICSR_RDRF); +} + +static int check_bbsy(struct sh_i2c *base) +{ + int i; + + for (i = 0 ; i < IRQ_WAIT ; i++) { + if (!(SH_I2C_ICCR2_BBSY & readb(&base->iccr2))) + return 0; + udelay(10); + } + return 1; +} + +static int check_ackbr(struct sh_i2c *base) +{ + int i; + + for (i = 0 ; i < IRQ_WAIT ; i++) { + if (!(SH_I2C_ICIER_ACKBR & readb(&base->icier))) + return 0; + udelay(10); + } + + return 1; +} + +static void sh_i2c_reset(struct sh_i2c *base) +{ + setbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST); + + udelay(100); + + clrbits_8(&base->iccr2, SH_I2C_ICCR2_IICRST); +} + +static int i2c_set_addr(struct sh_i2c *base, u8 id, u8 reg) +{ + if (check_bbsy(base)) { + puts("i2c bus busy\n"); + goto fail; + } + + setbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS); + clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY); + + writeb((id << 1), &base->icdrt); + + if (check_tend(base, 0)) { + puts("TEND check fail...\n"); + goto fail; + } + + if (check_ackbr(base)) { + check_tend(base, 0); + sh_i2c_send_stop(base); + goto fail; + } + + writeb(reg, &base->icdrt); + + if (check_tdre(base)) { + puts("TDRE check fail...\n"); + goto fail; + } + + if (check_tend(base, 0)) { + puts("TEND check fail...\n"); + goto fail; + } + + return 0; +fail: + + return 1; +} + +static int +i2c_raw_write(struct sh_i2c *base, u8 id, u8 reg, u8 *val, int size) +{ + int i; + + if (i2c_set_addr(base, id, reg)) { + puts("Fail set slave address\n"); + return 1; + } + + for (i = 0; i < size; i++) { + writeb(val[i], &base->icdrt); + check_tdre(base); + } + + check_tend(base, 1); + check_stop(base); + + udelay(100); + + clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS); + clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE); + sh_i2c_reset(base); + + return 0; +} + +static u8 i2c_raw_read(struct sh_i2c *base, u8 id, u8 reg) +{ + u8 ret = 0; + + if (i2c_set_addr(base, id, reg)) { + puts("Fail set slave address\n"); + goto fail; + } + + clrsetbits_8(&base->iccr2, SH_I2C_ICCR2_SCP, SH_I2C_ICCR2_BBSY); + writeb((id << 1) | 1, &base->icdrt); + + if (check_tend(base, 0)) + puts("TDRE check fail...\n"); + + clrsetbits_8(&base->iccr1, SH_I2C_ICCR1_TRS, SH_I2C_ICCR1_MST); + clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE); + setbits_8(&base->icier, SH_I2C_ICIER_ACKBT); + setbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD); + + /* read data (dummy) */ + ret = readb(&base->icdrr); + + if (check_rdrf(base)) { + puts("check RDRF error\n"); + goto fail; + } + + clrbits_8(&base->icsr, SH_I2C_ICSR_STOP); + udelay(1000); + + sh_i2c_send_stop(base); + + if (check_stop(base)) { + puts("check STOP error\n"); + goto fail; + } + + clrbits_8(&base->iccr1, SH_I2C_ICCR1_MTRS); + clrbits_8(&base->icsr, SH_I2C_ICSR_TDRE); + + /* data read */ + ret = readb(&base->icdrr); + +fail: + clrbits_8(&base->iccr1, SH_I2C_ICCR1_RCVD); + + return ret; +} + +#ifdef CONFIG_I2C_MULTI_BUS +static unsigned int current_bus; + +/** + * 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) { + case 0: + base = (void *)CONFIG_SH_I2C_BASE0; + break; + case 1: + base = (void *)CONFIG_SH_I2C_BASE1; + break; + default: + printf("Bad bus: %d\n", bus); + return -1; + } + + current_bus = bus; + + return 0; +} + +/** + * i2c_get_bus_num - returns index of active I2C bus + */ +unsigned int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif + +void i2c_init(int speed, int slaveaddr) +{ +#ifdef CONFIG_I2C_MULTI_BUS + current_bus = 0; +#endif + base = (struct sh_i2c *)CONFIG_SH_I2C_BASE0; + + if (speed == 400000) + iccr1_cks = 0x07; + else + iccr1_cks = 0x0F; + + nf2cyc = 1; + + /* Reset */ + sh_i2c_reset(base); + + /* ICE enable and set clock */ + writeb(SH_I2C_ICCR1_ICE | iccr1_cks, &base->iccr1); + writeb(nf2cyc, &base->nf2cyc); +} + +/* + * i2c_read: - Read multiple bytes from an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be read + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to write the data + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_read(u8 chip, u32 addr, int alen, u8 *buffer, int len) +{ + int i = 0; + for (i = 0; i < len; i++) + buffer[i] = i2c_raw_read(base, chip, addr + i); + + return 0; +} + +/* + * i2c_write: - Write multiple bytes to an i2c device + * + * The higher level routines take into account that this function is only + * called with len < page length of the device (see configuration file) + * + * @chip: address of the chip which is to be written + * @addr: i2c data address within the chip + * @alen: length of the i2c data address (1..2 bytes) + * @buffer: where to find the data to be written + * @len: how much byte do we want to read + * @return: 0 in case of success + */ +int i2c_write(u8 chip, u32 addr, int alen, u8 *buffer, int len) +{ + return i2c_raw_write(base, chip, addr, buffer, len); +} + +/* + * i2c_probe: - Test if a chip answers for a given i2c address + * + * @chip: address of the chip which is searched for + * @return: 0 if a chip was found, -1 otherwhise + */ +int i2c_probe(u8 chip) +{ + u8 byte; + return i2c_read(chip, 0, 0, &byte, 1); +} diff --git a/qemu/roms/u-boot/drivers/i2c/soft_i2c.c b/qemu/roms/u-boot/drivers/i2c/soft_i2c.c new file mode 100644 index 000000000..db9b4026b --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/soft_i2c.c @@ -0,0 +1,475 @@ +/* + * (C) Copyright 2009 + * Heiko Schocher, DENX Software Engineering, hs@denx.de. + * Changes for multibus/multiadapter I2C support. + * + * (C) Copyright 2001, 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This has been changed substantially by Gerald Van Baren, Custom IDEAS, + * vanbaren@cideas.com. It was heavily influenced by LiMon, written by + * Neil Russell. + */ + +#include <common.h> +#ifdef CONFIG_MPC8260 /* only valid for MPC8260 */ +#include <ioports.h> +#include <asm/io.h> +#endif +#if defined(CONFIG_AVR32) +#include <asm/arch/portmux.h> +#endif +#if defined(CONFIG_AT91FAMILY) +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/arch/at91_pio.h> +#ifdef CONFIG_ATMEL_LEGACY +#include <asm/arch/gpio.h> +#endif +#endif +#if defined(CONFIG_MPC852T) || defined(CONFIG_MPC866) +#include <asm/io.h> +#endif +#include <i2c.h> + +#if defined(CONFIG_SOFT_I2C_GPIO_SCL) +# include <asm/gpio.h> + +# ifndef I2C_GPIO_SYNC +# define I2C_GPIO_SYNC +# endif + +# ifndef I2C_INIT +# define I2C_INIT \ + do { \ + gpio_request(CONFIG_SOFT_I2C_GPIO_SCL, "soft_i2c"); \ + gpio_request(CONFIG_SOFT_I2C_GPIO_SDA, "soft_i2c"); \ + } while (0) +# endif + +# ifndef I2C_ACTIVE +# define I2C_ACTIVE do { } while (0) +# endif + +# ifndef I2C_TRISTATE +# define I2C_TRISTATE do { } while (0) +# endif + +# ifndef I2C_READ +# define I2C_READ gpio_get_value(CONFIG_SOFT_I2C_GPIO_SDA) +# endif + +# ifndef I2C_SDA +# define I2C_SDA(bit) \ + do { \ + if (bit) \ + gpio_direction_input(CONFIG_SOFT_I2C_GPIO_SDA); \ + else \ + gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SDA, 0); \ + I2C_GPIO_SYNC; \ + } while (0) +# endif + +# ifndef I2C_SCL +# define I2C_SCL(bit) \ + do { \ + gpio_direction_output(CONFIG_SOFT_I2C_GPIO_SCL, bit); \ + I2C_GPIO_SYNC; \ + } while (0) +# endif + +# ifndef I2C_DELAY +# define I2C_DELAY udelay(5) /* 1/4 I2C clock duration */ +# endif + +#endif + +/* #define DEBUG_I2C */ + +DECLARE_GLOBAL_DATA_PTR; + +#ifndef I2C_SOFT_DECLARATIONS +# if defined(CONFIG_MPC8260) +# define I2C_SOFT_DECLARATIONS volatile ioport_t *iop = \ + ioport_addr((immap_t *)CONFIG_SYS_IMMR, I2C_PORT); +# elif defined(CONFIG_8xx) +# define I2C_SOFT_DECLARATIONS volatile immap_t *immr = \ + (immap_t *)CONFIG_SYS_IMMR; +# else +# define I2C_SOFT_DECLARATIONS +# endif +#endif + +#if !defined(CONFIG_SYS_I2C_SOFT_SPEED) +#define CONFIG_SYS_I2C_SOFT_SPEED CONFIG_SYS_I2C_SPEED +#endif +#if !defined(CONFIG_SYS_I2C_SOFT_SLAVE) +#define CONFIG_SYS_I2C_SOFT_SLAVE CONFIG_SYS_I2C_SLAVE +#endif + +/*----------------------------------------------------------------------- + * Definitions + */ +#define RETRIES 0 + +#define I2C_ACK 0 /* PD_SDA level to ack a byte */ +#define I2C_NOACK 1 /* PD_SDA level to noack a byte */ + + +#ifdef DEBUG_I2C +#define PRINTD(fmt,args...) do { \ + printf (fmt ,##args); \ + } while (0) +#else +#define PRINTD(fmt,args...) +#endif + +/*----------------------------------------------------------------------- + * Local functions + */ +#if !defined(CONFIG_SYS_I2C_INIT_BOARD) +static void send_reset (void); +#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; +#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; +} +#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; +} + +/*----------------------------------------------------------------------- + * 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; +} + +/*----------------------------------------------------------------------- + * 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; +} + +/*----------------------------------------------------------------------- + * 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 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); +} + +/*----------------------------------------------------------------------- + * Initialization + */ +static void soft_i2c_init(struct i2c_adapter *adap, 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(); +#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 +} + +/*----------------------------------------------------------------------- + * 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). + */ +static int soft_i2c_probe(struct i2c_adapter *adap, uint8_t 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); +} + +/*----------------------------------------------------------------------- + * Read bytes + */ +static int soft_i2c_read(struct i2c_adapter *adap, 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_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 + + /* + * 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(); +#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); +} + +/*----------------------------------------------------------------------- + * Write bytes + */ +static int soft_i2c_write(struct i2c_adapter *adap, 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); +} + +/* + * Register soft i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(soft0, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED, CONFIG_SYS_I2C_SOFT_SLAVE, + 0) +#if defined(I2C_SOFT_DECLARATIONS2) +U_BOOT_I2C_ADAP_COMPLETE(soft1, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_2, + CONFIG_SYS_I2C_SOFT_SLAVE_2, + 1) +#endif +#if defined(I2C_SOFT_DECLARATIONS3) +U_BOOT_I2C_ADAP_COMPLETE(soft2, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_3, + CONFIG_SYS_I2C_SOFT_SLAVE_3, + 2) +#endif +#if defined(I2C_SOFT_DECLARATIONS4) +U_BOOT_I2C_ADAP_COMPLETE(soft3, soft_i2c_init, soft_i2c_probe, + soft_i2c_read, soft_i2c_write, NULL, + CONFIG_SYS_I2C_SOFT_SPEED_4, + CONFIG_SYS_I2C_SOFT_SLAVE_4, + 3) +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/tegra_i2c.c b/qemu/roms/u-boot/drivers/i2c/tegra_i2c.c new file mode 100644 index 000000000..594e5ddeb --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/tegra_i2c.c @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Copyright (c) 2010-2011 NVIDIA Corporation + * NVIDIA Corporation <www.nvidia.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <fdtdec.h> +#include <i2c.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch/gpio.h> +#include <asm/arch/pinmux.h> +#include <asm/arch-tegra/clk_rst.h> +#include <asm/arch-tegra/tegra_i2c.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Information about i2c controller */ +struct i2c_bus { + int id; + enum periph_id periph_id; + int speed; + 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+) */ + 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) { + struct dvc_ctlr *dvc = (struct dvc_ctlr *)i2c_bus->regs; + + writel(config, &dvc->cnfg); + } else { + writel(config, &i2c_bus->regs->cnfg); + /* + * program I2C_SL_CNFG.NEWSL to ENABLE. This fixes probe + * issues, i.e., some slaves may be wrongly detected. + */ + setbits_le32(&i2c_bus->regs->sl_cnfg, I2C_SL_CNFG_NEWSL_MASK); + } +} + +static void i2c_reset_controller(struct i2c_bus *i2c_bus) +{ + /* Reset I2C controller. */ + reset_periph(i2c_bus->periph_id, 1); + + /* re-program config register to packet mode */ + set_packet_mode(i2c_bus); +} + +static void i2c_init_controller(struct i2c_bus *i2c_bus) +{ + /* + * 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 + * 16 to get the right frequency. + */ + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, + i2c_bus->speed * 2 * 8); + + if (i2c_bus->is_scs) { + /* + * T114 I2C went to a single clock source for standard/fast and + * HS clock speeds. The new clock rate setting calculation is: + * SCL = CLK_SOURCE.I2C / + * (CLK_MULT_STD_FAST_MODE * (I2C_CLK_DIV_STD_FAST_MODE+1) * + * I2C FREQUENCY DIVISOR) as per the T114 TRM (sec 30.3.1). + * + * NOTE: We do this here, after the initial clock/pll start, + * because if we read the clk_div reg before the controller + * is running, we hang, and we need it for the new calc. + */ + int clk_div_stdfst_mode = readl(&i2c_bus->regs->clk_div) >> 16; + debug("%s: CLK_DIV_STD_FAST_MODE setting = %d\n", __func__, + clk_div_stdfst_mode); + + clock_start_periph_pll(i2c_bus->periph_id, CLOCK_ID_PERIPH, + CLK_MULT_STD_FAST_MODE * (clk_div_stdfst_mode + 1) * + i2c_bus->speed * 2); + } + + /* Reset I2C controller. */ + i2c_reset_controller(i2c_bus); + + /* Configure I2C controller. */ + if (i2c_bus->is_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); + } + + funcmux_select(i2c_bus->periph_id, i2c_bus->pinmux_config); +} + +static void send_packet_headers( + struct i2c_bus *i2c_bus, + struct i2c_trans_info *trans, + u32 packet_id) +{ + u32 data; + + /* prepare header1: Header size = 0 Protocol = I2C, pktType = 0 */ + data = PROTOCOL_TYPE_I2C << PKT_HDR1_PROTOCOL_SHIFT; + data |= packet_id << PKT_HDR1_PKT_ID_SHIFT; + data |= i2c_bus->id << PKT_HDR1_CTLR_ID_SHIFT; + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 1 sent (0x%x)\n", data); + + /* prepare header2 */ + data = (trans->num_bytes - 1) << PKT_HDR2_PAYLOAD_SIZE_SHIFT; + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 2 sent (0x%x)\n", data); + + /* prepare IO specific header: configure the slave address */ + data = trans->address << PKT_HDR3_SLAVE_ADDR_SHIFT; + + /* Enable Read if it is not a write transaction */ + if (!(trans->flags & I2C_IS_WRITE)) + data |= PKT_HDR3_READ_MODE_MASK; + + /* Write I2C specific header */ + writel(data, &i2c_bus->control->tx_fifo); + debug("pkt header 3 sent (0x%x)\n", data); +} + +static int wait_for_tx_fifo_empty(struct i2c_control *control) +{ + u32 count; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + count = (readl(&control->fifo_status) & TX_FIFO_EMPTY_CNT_MASK) + >> TX_FIFO_EMPTY_CNT_SHIFT; + if (count == I2C_FIFO_DEPTH) + return 1; + udelay(10); + timeout_us -= 10; + } + + return 0; +} + +static int wait_for_rx_fifo_notempty(struct i2c_control *control) +{ + u32 count; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + count = (readl(&control->fifo_status) & TX_FIFO_FULL_CNT_MASK) + >> TX_FIFO_FULL_CNT_SHIFT; + if (count) + return 1; + udelay(10); + timeout_us -= 10; + } + + return 0; +} + +static int wait_for_transfer_complete(struct i2c_control *control) +{ + int int_status; + int timeout_us = I2C_TIMEOUT_USEC; + + while (timeout_us >= 0) { + int_status = readl(&control->int_status); + if (int_status & I2C_INT_NO_ACK_MASK) + return -int_status; + if (int_status & I2C_INT_ARBITRATION_LOST_MASK) + return -int_status; + if (int_status & I2C_INT_XFER_COMPLETE_MASK) + return 0; + + udelay(10); + timeout_us -= 10; + } + + return -1; +} + +static int send_recv_packets(struct i2c_bus *i2c_bus, + struct i2c_trans_info *trans) +{ + struct i2c_control *control = i2c_bus->control; + u32 int_status; + u32 words; + u8 *dptr; + u32 local; + uchar last_bytes; + int error = 0; + int is_write = trans->flags & I2C_IS_WRITE; + + /* clear status from previous transaction, XFER_COMPLETE, NOACK, etc. */ + int_status = readl(&control->int_status); + writel(int_status, &control->int_status); + + send_packet_headers(i2c_bus, trans, 1); + + words = DIV_ROUND_UP(trans->num_bytes, 4); + last_bytes = trans->num_bytes & 3; + dptr = trans->buf; + + while (words) { + u32 *wptr = (u32 *)dptr; + + if (is_write) { + /* deal with word alignment */ + if ((unsigned)dptr & 3) { + memcpy(&local, dptr, sizeof(u32)); + writel(local, &control->tx_fifo); + debug("pkt data sent (0x%x)\n", local); + } else { + writel(*wptr, &control->tx_fifo); + debug("pkt data sent (0x%x)\n", *wptr); + } + if (!wait_for_tx_fifo_empty(control)) { + error = -1; + goto exit; + } + } else { + if (!wait_for_rx_fifo_notempty(control)) { + error = -1; + goto exit; + } + /* + * for the last word, we read into our local buffer, + * in case that caller did not provide enough buffer. + */ + local = readl(&control->rx_fifo); + if ((words == 1) && last_bytes) + memcpy(dptr, (char *)&local, last_bytes); + else if ((unsigned)dptr & 3) + memcpy(dptr, &local, sizeof(u32)); + else + *wptr = local; + debug("pkt data received (0x%x)\n", local); + } + words--; + dptr += sizeof(u32); + } + + if (wait_for_transfer_complete(control)) { + error = -1; + goto exit; + } + return 0; +exit: + /* error, reset the controller. */ + i2c_reset_controller(i2c_bus); + + return error; +} + +static int tegra_i2c_write_data(struct i2c_bus *bus, u32 addr, u8 *data, + u32 len) +{ + int error; + struct i2c_trans_info trans_info; + + trans_info.address = addr; + trans_info.buf = data; + trans_info.flags = I2C_IS_WRITE; + trans_info.num_bytes = len; + trans_info.is_10bit_address = 0; + + error = send_recv_packets(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, + u32 len) +{ + int error; + struct i2c_trans_info trans_info; + + trans_info.address = addr | 1; + trans_info.buf = data; + trans_info.flags = 0; + trans_info.num_bytes = len; + trans_info.is_10bit_address = 0; + + error = send_recv_packets(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) +{ + struct i2c_bus *bus; + + 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); + + return 0; +} + +static int i2c_get_config(const void *blob, int node, struct i2c_bus *i2c_bus) +{ + i2c_bus->regs = (struct i2c_ctlr *)fdtdec_get_addr(blob, node, "reg"); + + /* + * We don't have a binding for pinmux yet. Leave it out for now. So + * 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); + + /* + * We can't specify the pinmux config in the fdt, so I2C2 will not + * work on Seaboard. It normally has no devices on it anyway. + * You could add in this little hack if you need to use it. + * The correct solution is a pinmux binding in the fdt. + * + * if (i2c_bus->periph_id == PERIPH_ID_I2C2) + * i2c_bus->pinmux_config = FUNCMUX_I2C2_PTA; + */ + if (i2c_bus->periph_id == -1) + return -FDT_ERR_NOTFOUND; + + 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; + } + + 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 */ +int i2c_write_data(struct i2c_bus *bus, uchar chip, uchar *buffer, int len) +{ + int rc; + + debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len); + debug("write_data: "); + /* use rc for counter */ + for (rc = 0; rc < len; ++rc) + debug(" 0x%02x", buffer[rc]); + debug("\n"); + + /* Shift 7-bit address over for lower-level i2c functions */ + rc = tegra_i2c_write_data(bus, chip << 1, buffer, len); + if (rc) + debug("i2c_write_data(): rc=%d\n", rc); + + return rc; +} + +/* i2c read version without the register address */ +int i2c_read_data(struct i2c_bus *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); + if (rc) { + debug("i2c_read_data(): rc=%d\n", rc); + return rc; + } + + debug("i2c_read_data: "); + /* reuse rc for counter*/ + for (rc = 0; rc < len; ++rc) + debug(" 0x%02x", buffer[rc]); + debug("\n"); + + return 0; +} + +/* Probe to see if a chip is present. */ +static int tegra_i2c_probe(struct i2c_adapter *adap, uchar chip) +{ + struct i2c_bus *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); + if (rc) { + debug("Error probing 0x%x.\n", chip); + return 1; + } + return 0; +} + +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; +} + +/* Read bytes */ +static int tegra_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c_bus *bus; + uint offset; + int i; + + debug("i2c_read: chip=0x%x, addr=0x%x, len=0x%x\n", + chip, addr, 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)) { + debug("i2c_read: error sending (0x%x)\n", + addr); + return 1; + } + } + if (i2c_read_data(bus, chip, buffer + offset, 1)) { + debug("i2c_read: error reading (0x%x)\n", addr); + return 1; + } + } + + return 0; +} + +/* Write bytes */ +static int tegra_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr, + int alen, uchar *buffer, int len) +{ + struct i2c_bus *bus; + uint offset; + int i; + + debug("i2c_write: chip=0x%x, addr=0x%x, len=0x%x\n", + chip, addr, 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)) { + debug("i2c_write: error sending (0x%x)\n", addr); + return 1; + } + } + + return 0; +} + +int tegra_i2c_get_dvc_bus_num(void) +{ + int i; + + for (i = 0; i < TEGRA_I2C_NUM_CONTROLLERS; i++) { + struct i2c_bus *bus = &i2c_controllers[i]; + + if (bus->inited && bus->is_dvc) + return i; + } + + return -1; +} + +/* + * 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 diff --git a/qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c b/qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c new file mode 100644 index 000000000..c0779079a --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/tsi108_i2c.c @@ -0,0 +1,273 @@ +/* + * (C) Copyright 2004 Tundra Semiconductor Corp. + * Author: Alex Bounine + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <config.h> +#include <common.h> + +#include <tsi108.h> + +#if defined(CONFIG_CMD_I2C) + +#define I2C_DELAY 100000 +#undef DEBUG_I2C + +#ifdef DEBUG_I2C +#define DPRINT(x) printf (x) +#else +#define DPRINT(x) +#endif + +/* All functions assume that Tsi108 I2C block is the only master on the bus */ +/* I2C read helper function */ + +void i2c_init(int speed, int slaveaddr) +{ + /* + * The TSI108 has a fixed I2C clock rate and doesn't support slave + * operation. This function only exists as a stub to fit into the + * U-Boot I2C API. + */ +} + +static int i2c_read_byte ( + uint i2c_chan, /* I2C channel number: 0 - main, 1 - SDC SPD */ + uchar chip_addr,/* I2C device address on the bus */ + uint byte_addr, /* Byte address within I2C device */ + uchar * buffer /* pointer to data buffer */ + ) +{ + u32 temp; + u32 to_count = I2C_DELAY; + u32 op_status = TSI108_I2C_TIMEOUT_ERR; + u32 chan_offset = TSI108_I2C_OFFSET; + + DPRINT (("I2C read_byte() %d 0x%02x 0x%02x\n", + i2c_chan, chip_addr, byte_addr)); + + if (0 != i2c_chan) + chan_offset = TSI108_I2C_SDRAM_OFFSET; + + /* Check if I2C operation is in progress */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | + I2C_CNTRL2_START))) { + /* Set device address and operation (read = 0) */ + temp = (byte_addr << 16) | ((chip_addr & 0x07) << 8) | + ((chip_addr >> 3) & 0x0F); + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL1) = + temp; + + /* Issue the read command + * (at this moment all other parameters are 0 + * (size = 1 byte, lane = 0) + */ + + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2) = + (I2C_CNTRL2_START); + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + chan_offset + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_START))) { + if (0 == (temp & + (I2C_CNTRL2_I2C_CFGERR | + I2C_CNTRL2_I2C_TO_ERR)) + ) { + op_status = TSI108_I2C_SUCCESS; + + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + + chan_offset + + I2C_RD_DATA); + + *buffer = (u8) (temp & 0xFF); + } else { + /* report HW error */ + op_status = TSI108_I2C_IF_ERROR; + + DPRINT (("I2C HW error reported: 0x%02x\n", temp)); + } + + break; + } + } while (to_count--); + } else { + op_status = TSI108_I2C_IF_BUSY; + + DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); + } + + DPRINT (("I2C read_byte() status: 0x%02x\n", op_status)); + return op_status; +} + +/* + * I2C Read interface as defined in "include/i2c.h" : + * chip_addr: I2C chip address, range 0..127 + * (to read from SPD channel EEPROM use (0xD0 ... 0xD7) + * NOTE: The bit 7 in the chip_addr serves as a channel select. + * This hack is for enabling "i2c sdram" command on Tsi108 boards + * without changes to common code. Used for I2C reads only. + * byte_addr: Memory or register address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Pointer to destination buffer for data to be read + * len: How many bytes to read + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_read (uchar chip_addr, uint byte_addr, int alen, + uchar * buffer, int len) +{ + u32 op_status = TSI108_I2C_PARAM_ERR; + u32 i2c_if = 0; + + /* Hack to support second (SPD) I2C controller (SPD EEPROM read only).*/ + if (0xD0 == (chip_addr & ~0x07)) { + i2c_if = 1; + chip_addr &= 0x7F; + } + /* Check for valid I2C address */ + if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { + while (len--) { + op_status = i2c_read_byte(i2c_if, chip_addr, byte_addr++, buffer++); + + if (TSI108_I2C_SUCCESS != op_status) { + DPRINT (("I2C read_byte() failed: 0x%02x (%d left)\n", op_status, len)); + + break; + } + } + } + + DPRINT (("I2C read() status: 0x%02x\n", op_status)); + return op_status; +} + +/* I2C write helper function */ + +static int i2c_write_byte (uchar chip_addr,/* I2C device address on the bus */ + uint byte_addr, /* Byte address within I2C device */ + uchar * buffer /* pointer to data buffer */ + ) +{ + u32 temp; + u32 to_count = I2C_DELAY; + u32 op_status = TSI108_I2C_TIMEOUT_ERR; + + /* Check if I2C operation is in progress */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_RD_STATUS | I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { + /* Place data into the I2C Tx Register */ + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_TX_DATA) = (u32) * buffer; + + /* Set device address and operation */ + temp = + I2C_CNTRL1_I2CWRITE | (byte_addr << 16) | + ((chip_addr & 0x07) << 8) | ((chip_addr >> 3) & 0x0F); + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_CNTRL1) = temp; + + /* Issue the write command (at this moment all other parameters + * are 0 (size = 1 byte, lane = 0) + */ + + *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + + I2C_CNTRL2) = (I2C_CNTRL2_START); + + op_status = TSI108_I2C_TIMEOUT_ERR; + + /* Wait until operation completed */ + do { + /* Read I2C operation status */ + temp = *(u32 *) (CONFIG_SYS_TSI108_CSR_BASE + TSI108_I2C_OFFSET + I2C_CNTRL2); + + if (0 == (temp & (I2C_CNTRL2_WR_STATUS | I2C_CNTRL2_START))) { + if (0 == (temp & + (I2C_CNTRL2_I2C_CFGERR | + I2C_CNTRL2_I2C_TO_ERR))) { + op_status = TSI108_I2C_SUCCESS; + } else { + /* report detected HW error */ + op_status = TSI108_I2C_IF_ERROR; + + DPRINT (("I2C HW error reported: 0x%02x\n", temp)); + } + + break; + } + + } while (to_count--); + } else { + op_status = TSI108_I2C_IF_BUSY; + + DPRINT (("I2C Transaction start failed: 0x%02x\n", temp)); + } + + return op_status; +} + +/* + * I2C Write interface as defined in "include/i2c.h" : + * chip_addr: I2C chip address, range 0..127 + * byte_addr: Memory or register address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Pointer to data to be written + * len: How many bytes to write + * + * Returns: 0 on success, not 0 on failure + */ + +int i2c_write (uchar chip_addr, uint byte_addr, int alen, uchar * buffer, + int len) +{ + u32 op_status = TSI108_I2C_PARAM_ERR; + + /* Check for valid I2C address */ + if (chip_addr <= 0x7F && (byte_addr + len) <= (0x01 << (alen * 8))) { + while (len--) { + op_status = + i2c_write_byte (chip_addr, byte_addr++, buffer++); + + if (TSI108_I2C_SUCCESS != op_status) { + DPRINT (("I2C write_byte() failed: 0x%02x (%d left)\n", op_status, len)); + + break; + } + } + } + + return op_status; +} + +/* + * I2C interface function as defined in "include/i2c.h". + * Probe the given I2C chip address by reading single byte from offset 0. + * Returns 0 if a chip responded, not 0 on failure. + */ + +int i2c_probe (uchar chip) +{ + u32 tmp; + + /* + * Try to read the first location of the chip. + * The Tsi108 HW doesn't support sending just the chip address + * and checkong for an <ACK> back. + */ + return i2c_read (chip, 0, 1, (uchar *)&tmp, 1); +} + +#endif diff --git a/qemu/roms/u-boot/drivers/i2c/u8500_i2c.c b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.c new file mode 100644 index 000000000..81ffb8ed7 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.c @@ -0,0 +1,601 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * Basic U-Boot I2C interface for STn8500/DB8500 + * Author: Michael Brandt <Michael.Brandt@stericsson.com> for ST-Ericsson + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +/* + * Only 7-bit I2C device addresses are supported. + */ + +#include <common.h> +#include <i2c.h> + +#include "u8500_i2c.h" +#include <asm/io.h> +#include <asm/arch/clock.h> + +#define U8500_I2C_ENDAD_COUNTER (CONFIG_SYS_HZ/100) /* I2C bus timeout */ +#define U8500_I2C_FIFO_FLUSH_COUNTER 500000 /* flush "timeout" */ +#define U8500_I2C_SCL_FREQ 100000 /* I2C bus clock freq */ +#define U8500_I2C_INPUT_FREQ 48000000 /* Input clock freq */ +#define TX_FIFO_THRESHOLD 0x4 +#define RX_FIFO_THRESHOLD 0x4 +#define SLAVE_SETUP_TIME 14 /* Slave data setup time, 250ns for 48MHz i2c_clk */ + +#define WRITE_FIELD(var, mask, shift, value) \ + (var = ((var & ~(mask)) | ((value) << (shift)))) + +static unsigned int bus_initialized[CONFIG_SYS_U8500_I2C_BUS_MAX]; +static unsigned int i2c_bus_num; +static unsigned int i2c_bus_speed[] = { + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED, + CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SPEED +}; +static struct u8500_i2c_regs *i2c_dev[] = { + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C0_BASE, + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C1_BASE, + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C2_BASE, + (struct u8500_i2c_regs *)CONFIG_SYS_U8500_I2C3_BASE, +}; + +static struct { + int periph; + int pcken; + int kcken; +} i2c_clock_bits[] = { + {3, 3, 3}, /* I2C0 */ + {1, 2, 2}, /* I2C1 */ + {1, 6, 6}, /* I2C2 */ + {2, 0, 0}, /* I2C3 */ +}; + +static void i2c_set_bit(void *reg, u32 mask) +{ + writel(readl(reg) | mask, reg); +} + +static void i2c_clr_bit(void *reg, u32 mask) +{ + writel(readl(reg) & ~mask, reg); +} + +static void i2c_write_field(void *reg, u32 mask, uint shift, u32 value) +{ + writel((readl(reg) & ~mask) | (value << shift), reg); +} + +static int __i2c_set_bus_speed(unsigned int speed) +{ + u32 value; + struct u8500_i2c_regs *i2c_regs; + + i2c_regs = i2c_dev[i2c_bus_num]; + + /* Select standard (100 kbps) speed mode */ + i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_SM, + U8500_I2C_CR_SHIFT_SM, 0x0); + + /* + * Set the Baud Rate Counter 2 value + * Baud rate (standard) = fi2cclk / ( (BRCNT2 x 2) + Foncycle ) + * Foncycle = 0 (no digital filtering) + */ + value = (u32) (U8500_I2C_INPUT_FREQ / (speed * 2)); + i2c_write_field(&i2c_regs->brcr, U8500_I2C_BRCR_BRCNT2, + U8500_I2C_BRCR_SHIFT_BRCNT2, value); + + /* ensure that BRCNT value is zero */ + i2c_write_field(&i2c_regs->brcr, U8500_I2C_BRCR_BRCNT1, + U8500_I2C_BRCR_SHIFT_BRCNT1, 0); + + return U8500_I2C_INPUT_FREQ/(value * 2); +} + +/* + * i2c_init - initialize the i2c bus + * + * speed: bus speed (in HZ) + * slaveaddr: address of device in slave mode + * + * Slave mode is not implemented. + */ +void i2c_init(int speed, int slaveaddr) +{ + struct u8500_i2c_regs *i2c_regs; + + debug("i2c_init bus %d, speed %d\n", i2c_bus_num, speed); + + u8500_clock_enable(i2c_clock_bits[i2c_bus_num].periph, + i2c_clock_bits[i2c_bus_num].pcken, + i2c_clock_bits[i2c_bus_num].kcken); + + i2c_regs = i2c_dev[i2c_bus_num]; + + /* Disable the controller */ + i2c_clr_bit(&i2c_regs->cr, U8500_I2C_CR_PE); + + /* Clear registers */ + writel(0, &i2c_regs->cr); + writel(0, &i2c_regs->scr); + writel(0, &i2c_regs->hsmcr); + writel(0, &i2c_regs->tftr); + writel(0, &i2c_regs->rftr); + writel(0, &i2c_regs->dmar); + + i2c_bus_speed[i2c_bus_num] = __i2c_set_bus_speed(speed); + + /* + * Set our own address. + * Set slave address mode to 7 bit addressing mode + */ + i2c_clr_bit(&i2c_regs->cr, U8500_I2C_CR_SAM); + i2c_write_field(&i2c_regs->scr, U8500_I2C_SCR_ADDR, + U8500_I2C_SCR_SHIFT_ADDR, slaveaddr); + /* Slave Data Set up Time */ + i2c_write_field(&i2c_regs->scr, U8500_I2C_SCR_DATA_SETUP_TIME, + U8500_I2C_SCR_SHIFT_DATA_SETUP_TIME, SLAVE_SETUP_TIME); + + /* Disable the DMA sync logic */ + i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_DMA_SLE, + U8500_I2C_CR_SHIFT_DMA_SLE, 0); + + /* Disable interrupts */ + writel(0, &i2c_regs->imscr); + + /* Configure bus master mode */ + i2c_write_field(&i2c_regs->cr, U8500_I2C_CR_OM, U8500_I2C_CR_SHIFT_OM, + U8500_I2C_BUS_MASTER_MODE); + /* Set FIFO threshold values */ + writel(TX_FIFO_THRESHOLD, &i2c_regs->tftr); + writel(RX_FIFO_THRESHOLD, &i2c_regs->rftr); + + /* Enable the I2C Controller */ + i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_PE); + + bus_initialized[i2c_bus_num] = 1; +} + + +/* + * loop_till_bit_clear - polls on a bit till it clears + * ioreg: register where you want to check status + * mask: bit mask for the bit you wish to check + * timeout: timeout in ticks/s + */ +static int loop_till_bit_clear(void *io_reg, u32 mask, unsigned long timeout) +{ + unsigned long timebase = get_timer(0); + + do { + if ((readl(io_reg) & mask) == 0x0UL) + return 0; + } while (get_timer(timebase) < timeout); + + debug("loop_till_bit_clear timed out\n"); + return -1; +} + +/* + * loop_till_bit_set - polls on a bit till it is set. + * ioreg: register where you want to check status + * mask: bit mask for the bit you wish to check + * timeout: timeout in ticks/s + */ +static int loop_till_bit_set(void *io_reg, u32 mask, unsigned long timeout) +{ + unsigned long timebase = get_timer(0); + + do { + if ((readl(io_reg) & mask) != 0x0UL) + return 0; + } while (get_timer(timebase) < timeout); + + debug("loop_till_bit_set timed out\n"); + return -1; +} + +/* + * flush_fifo - flush the I2C TX and RX FIFOs + */ +static void flush_fifo(struct u8500_i2c_regs *i2c_regs) +{ + int counter = U8500_I2C_FIFO_FLUSH_COUNTER; + + /* Flush Tx FIFO */ + i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_FTX); + /* Flush Rx FIFO */ + i2c_set_bit(&i2c_regs->cr, U8500_I2C_CR_FRX); + while (counter--) { + if (!(readl(&i2c_regs->cr) & + (U8500_I2C_CR_FTX | U8500_I2C_CR_FRX))) + break; + } + return; +} + +#ifdef DEBUG +static void print_abort_reason(struct u8500_i2c_regs *i2c_regs) +{ + int cause; + + printf("abort: risr %08x, sr %08x\n", i2c_regs->risr, i2c_regs->sr); + cause = (readl(&i2c_regs->sr) & U8500_I2C_SR_CAUSE) >> + U8500_I2C_SR_SHIFT_CAUSE; + switch (cause) { + case U8500_I2C_NACK_ADDR: + printf("No Ack received after Slave Address xmission\n"); + break; + case U8500_I2C_NACK_DATA: + printf("Valid for MASTER_WRITE: No Ack received " + "during data phase\n"); + break; + case U8500_I2C_ACK_MCODE: + printf("Master recv ack after xmission of master code" + "in hs mode\n"); + break; + case U8500_I2C_ARB_LOST: + printf("Master Lost arbitration\n"); + break; + case U8500_I2C_BERR_START: + printf("Slave restarts\n"); + break; + case U8500_I2C_BERR_STOP: + printf("Slave reset\n"); + break; + case U8500_I2C_OVFL: + printf("Overflow\n"); + break; + default: + printf("Unknown error type\n"); + } +} +#endif + +/* + * i2c_abort - called when a I2C transaction failed + */ +static void i2c_abort(struct u8500_i2c_regs *i2c_regs) +{ +#ifdef DEBUG + print_abort_reason(i2c_regs); +#endif + /* flush RX and TX fifos */ + flush_fifo(i2c_regs); + + /* Acknowledge the Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* Acknowledge the Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + i2c_init(i2c_bus_speed[i2c_bus_num], CONFIG_SYS_I2C_SLAVE); +} + +/* + * write addr, alias index, to I2C bus. + */ +static int i2c_write_addr(struct u8500_i2c_regs *i2c_regs, uint addr, int alen) +{ + while (alen--) { + /* Wait until the Tx Fifo is not full */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_TXFF, + U8500_I2C_ENDAD_COUNTER)) { + i2c_abort(i2c_regs); + return -1; + } + + /* MSB first */ + writeb((addr >> (alen * 8)) & 0xff, &i2c_regs->tfr); + } + + return 0; +} + +/* + * Internal simplified read function: + * i2c_regs: Pointer to I2C registers for current bus + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one register) + * value: Where to put the data + * + * Returns: 0 on success, not 0 on failure + */ +static int i2c_read_byte(struct u8500_i2c_regs *i2c_regs, uchar chip, + uint addr, int alen, uchar *value) +{ + u32 mcr = 0; + + /* Set the address mode to 7 bit */ + WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1); + + /* Store the slave address in the master control register */ + WRITE_FIELD(mcr, U8500_I2C_MCR_A7, U8500_I2C_MCR_SHIFT_A7, chip); + + if (alen != 0) { + /* Master write operation */ + mcr &= ~(U8500_I2C_MCR_OP); + + /* Configure the Frame length to one byte */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, + U8500_I2C_MCR_SHIFT_LENGTH, 1); + + /* Repeated start, no stop */ + mcr &= ~(U8500_I2C_MCR_STOP); + + /* Write Master Control Register */ + writel(mcr, &i2c_regs->mcr); + + /* send addr/index */ + if (i2c_write_addr(i2c_regs, addr, alen) != 0) + return -1; + + /* Check for the Master Transaction Done Without Stop */ + if (loop_till_bit_set((void *)&i2c_regs->risr, + U8500_I2C_INT_MTDWS, + U8500_I2C_ENDAD_COUNTER)) { + return -1; + } + + /* Acknowledge the Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + } + + /* Master control configuration for read operation */ + mcr |= U8500_I2C_MCR_OP; + + /* Configure the STOP condition, we read only one byte */ + mcr |= U8500_I2C_MCR_STOP; + + /* Set the frame length to one byte, we support only 1 byte reads */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, 1); + + i2c_write_field(&i2c_regs->mcr, U8500_I2C_MCR_LENGTH_STOP_OP, + U8500_I2C_MCR_SHIFT_LENGTH_STOP_OP, mcr); + + /* + * receive_data_polling + */ + + /* Wait until the Rx FIFO is not empty */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_RXFE, + U8500_I2C_ENDAD_COUNTER)) + return -1; + + /* Read the data byte from Rx FIFO */ + *value = readb(&i2c_regs->rfr); + + /* Wait until the work is done */ + if (loop_till_bit_set((void *)&i2c_regs->risr, U8500_I2C_INT_MTD, + U8500_I2C_ENDAD_COUNTER)) + return -1; + + /* Acknowledge the Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* If MTD is set, Master Transaction Done Without Stop is set too */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + return 0; +} + +/* + * Internal simplified write function: + * i2c_regs: Pointer to I2C registers for current bus + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one register) + * data: Where to read the data + * len: How many bytes to write + * + * Returns: 0 on success, not 0 on failure + */ +static int __i2c_write(struct u8500_i2c_regs *i2c_regs, u8 chip, uint addr, + int alen, u8 *data, int len) +{ + int i; + u32 mcr = 0; + + /* Set the address mode to 7 bit */ + WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1); + + /* Store the slave address in the master control register */ + WRITE_FIELD(mcr, U8500_I2C_MCR_A7, U8500_I2C_MCR_SHIFT_A7, chip); + + /* Write operation */ + mcr &= ~(U8500_I2C_MCR_OP); + + /* Current transaction is terminated by STOP condition */ + mcr |= U8500_I2C_MCR_STOP; + + /* Frame length: addr byte + len */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, + (alen + len)); + + /* Write MCR register */ + writel(mcr, &i2c_regs->mcr); + + if (i2c_write_addr(i2c_regs, addr, alen) != 0) + return -1; + + for (i = 0; i < len; i++) { + /* Wait until the Tx FIFO is not full */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_TXFF, + U8500_I2C_ENDAD_COUNTER)) + return -1; + + /* it is a 32 bit register with upper 24 reserved R/O */ + writeb(data[i], &i2c_regs->tfr); + } + + /* Check for Master Transaction Done */ + if (loop_till_bit_set((void *)&i2c_regs->risr, + U8500_I2C_INT_MTD, + U8500_I2C_ENDAD_COUNTER)) { + printf("i2c_write_byte error2: risr %08x\n", + i2c_regs->risr); + return -1; + } + + /* Acknowledge Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* Acknowledge Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + return 0; +} + +/* + * Probe the given I2C chip address. Returns 0 if a chip responded, + * not 0 on failure. + */ +int i2c_probe(uchar chip) +{ + u32 mcr = 0; + struct u8500_i2c_regs *i2c_regs; + + if (chip == CONFIG_SYS_I2C_SLAVE) + return 1; + + i2c_regs = i2c_dev[i2c_bus_num]; + + /* Set the address mode to 7 bit */ + WRITE_FIELD(mcr, U8500_I2C_MCR_AM, U8500_I2C_MCR_SHIFT_AM, 1); + + /* Store the slave address in the master control register */ + WRITE_FIELD(mcr, U8500_I2C_MCR_A10, U8500_I2C_MCR_SHIFT_A7, chip); + + /* Read operation */ + mcr |= U8500_I2C_MCR_OP; + + /* Set the frame length to one byte */ + WRITE_FIELD(mcr, U8500_I2C_MCR_LENGTH, U8500_I2C_MCR_SHIFT_LENGTH, 1); + + /* Current transaction is terminated by STOP condition */ + mcr |= U8500_I2C_MCR_STOP; + + /* Write MCR register */ + writel(mcr, &i2c_regs->mcr); + + /* Wait until the Rx Fifo is not empty */ + if (loop_till_bit_clear((void *)&i2c_regs->risr, + U8500_I2C_INT_RXFE, + U8500_I2C_ENDAD_COUNTER)) { + i2c_abort(i2c_regs); + return -1; + } + + flush_fifo(i2c_regs); + + /* Acknowledge the Master Transaction Done */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTD); + + /* Acknowledge the Master Transaction Done Without Stop */ + i2c_set_bit(&i2c_regs->icr, U8500_I2C_INT_MTDWS); + + return 0; +} + +/* + * Read/Write interface: + * chip: I2C chip address, range 0..127 + * addr: Memory (register) address within the chip + * alen: Number of bytes to use for addr (typically 1, 2 for larger + * memories, 0 for register type devices with only one + * register) + * buffer: Where to read/write the data + * len: How many bytes to read/write + * + * Returns: 0 on success, not 0 on failure + */ +int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int i; + int rc; + struct u8500_i2c_regs *i2c_regs; + + if (alen > 2) { + debug("I2C read: addr len %d not supported\n", alen); + return 1; + } + + i2c_regs = i2c_dev[i2c_bus_num]; + + for (i = 0; i < len; i++) { + rc = i2c_read_byte(i2c_regs, chip, addr + i, alen, &buffer[i]); + if (rc != 0) { + debug("I2C read: I/O error: %d\n", rc); + i2c_abort(i2c_regs); + return rc; + } + } + + return 0; +} + +int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) +{ + int rc; + struct u8500_i2c_regs *i2c_regs; + i2c_regs = i2c_dev[i2c_bus_num]; + + rc = __i2c_write(i2c_regs, chip, addr, alen, buffer, + len); + if (rc != 0) { + debug("I2C write: I/O error\n"); + i2c_abort(i2c_regs); + return rc; + } + return 0; +} + +int i2c_set_bus_num(unsigned int bus) +{ + if (bus > ARRAY_SIZE(i2c_dev) - 1) { + debug("i2c_set_bus_num: only up to bus %d supported\n", + ARRAY_SIZE(i2c_dev)-1); + return -1; + } + + i2c_bus_num = bus; + + if (!bus_initialized[i2c_bus_num]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + + if (speed > U8500_I2C_MAX_STANDARD_SCL) { + debug("i2c_set_bus_speed: only up to %d supported\n", + U8500_I2C_MAX_STANDARD_SCL); + return -1; + } + + /* sets as side effect i2c_bus_speed[i2c_bus_num] */ + i2c_init(speed, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +unsigned int i2c_get_bus_num(void) +{ + return i2c_bus_num; +} + +unsigned int i2c_get_bus_speed(void) +{ + return i2c_bus_speed[i2c_bus_num]; +} diff --git a/qemu/roms/u-boot/drivers/i2c/u8500_i2c.h b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.h new file mode 100644 index 000000000..ceecdeede --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/u8500_i2c.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) ST-Ericsson SA 2009 + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _U8500_I2C_H_ +#define _U8500_I2C_H_ + +#include <asm/types.h> +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/arch/u8500.h> + +struct u8500_i2c_regs { + u32 cr; /* Control Register 0x00 */ + u32 scr; /* Slave Address Register 0x04 */ + u32 hsmcr; /* HS Master code Register 0x08 */ + u32 mcr; /* Master Control Register 0x0C */ + u32 tfr; /* Transmit Fifo Register 0x10 */ + u32 sr; /* Status Register 0x14 */ + u32 rfr; /* Receiver Fifo Register 0x18 */ + u32 tftr; /* Transmit Fifo Threshold Register 0x1C */ + u32 rftr; /* Receiver Fifo Threshold Register 0x20 */ + u32 dmar; /* DMA register 0x24 */ + u32 brcr; /* Baud Rate Counter Register 0x28 */ + u32 imscr; /* Interrupt Mask Set and Clear Register 0x2C */ + u32 risr; /* Raw interrupt status register 0x30 */ + u32 misr; /* Masked interrupt status register 0x34 */ + u32 icr; /* Interrupt Set and Clear Register 0x38 */ + u32 reserved_1[(0xFE0 - 0x3c) >> 2]; /* Reserved 0x03C to 0xFE0 */ + u32 periph_id_0; /* peripheral ID 0 0xFE0 */ + u32 periph_id_1; /* peripheral ID 1 0xFE4 */ + u32 periph_id_2; /* peripheral ID 2 0xFE8 */ + u32 periph_id_3; /* peripheral ID 3 0xFEC */ + u32 cell_id_0; /* I2C cell ID 0 0xFF0 */ + u32 cell_id_1; /* I2C cell ID 1 0xFF4 */ + u32 cell_id_2; /* I2C cell ID 2 0xFF8 */ + u32 cell_id_3; /* I2C cell ID 3 0xFFC */ +}; + + +/* Control Register */ + +/* Mask values for control register mask */ +#define U8500_I2C_CR_PE 0x0001 /* Peripheral enable */ +#define U8500_I2C_CR_OM 0x0006 /* Operation mode */ +#define U8500_I2C_CR_SAM 0x0008 /* Slave Addressing mode */ +#define U8500_I2C_CR_SM 0x0030 /* Speed mode */ +#define U8500_I2C_CR_SGCM 0x0040 /* Slave General call mode */ +#define U8500_I2C_CR_FTX 0x0080 /* Flush Transmit */ +#define U8500_I2C_CR_FRX 0x0100 /* Flush Receive */ +#define U8500_I2C_CR_DMA_TX_EN 0x0200 /* DMA TX Enable */ +#define U8500_I2C_CR_DMA_RX_EN 0x0400 /* DMA Rx Enable */ +#define U8500_I2C_CR_DMA_SLE 0x0800 /* DMA Synchronization Logic enable */ +#define U8500_I2C_CR_LM 0x1000 /* Loop back mode */ +#define U8500_I2C_CR_FON 0x6000 /* Filtering On */ + +/* shift valus for control register bit fields */ +#define U8500_I2C_CR_SHIFT_PE 0 /* Peripheral enable */ +#define U8500_I2C_CR_SHIFT_OM 1 /* Operation mode */ +#define U8500_I2C_CR_SHIFT_SAM 3 /* Slave Addressing mode */ +#define U8500_I2C_CR_SHIFT_SM 4 /* Speed mode */ +#define U8500_I2C_CR_SHIFT_SGCM 6 /* Slave General call mode */ +#define U8500_I2C_CR_SHIFT_FTX 7 /* Flush Transmit */ +#define U8500_I2C_CR_SHIFT_FRX 8 /* Flush Receive */ +#define U8500_I2C_CR_SHIFT_DMA_TX_EN 9 /* DMA TX Enable */ +#define U8500_I2C_CR_SHIFT_DMA_RX_EN 10 /* DMA Rx Enable */ +#define U8500_I2C_CR_SHIFT_DMA_SLE 11 /* DMA Synch Logic enable */ +#define U8500_I2C_CR_SHIFT_LM 12 /* Loop back mode */ +#define U8500_I2C_CR_SHIFT_FON 13 /* Filtering On */ + +/* bus operation modes */ +#define U8500_I2C_BUS_SLAVE_MODE 0 +#define U8500_I2C_BUS_MASTER_MODE 1 +#define U8500_I2C_BUS_MASTER_SLAVE_MODE 2 + + +/* Slave control register*/ + +/* Mask values slave control register */ +#define U8500_I2C_SCR_ADDR 0x3FF +#define U8500_I2C_SCR_DATA_SETUP_TIME 0xFFFF0000 + +/* Shift values for Slave control register */ +#define U8500_I2C_SCR_SHIFT_ADDR 0 +#define U8500_I2C_SCR_SHIFT_DATA_SETUP_TIME 16 + + +/* Master Control Register */ + +/* Mask values for Master control register */ +#define U8500_I2C_MCR_OP 0x00000001 /* Operation */ +#define U8500_I2C_MCR_A7 0x000000FE /* LSB bits of Address */ +#define U8500_I2C_MCR_EA10 0x00000700 /* Extended Address */ +#define U8500_I2C_MCR_SB 0x00000800 /* Start byte procedure */ +#define U8500_I2C_MCR_AM 0x00003000 /* Address type */ +#define U8500_I2C_MCR_STOP 0x00004000 /* stop condition */ +#define U8500_I2C_MCR_LENGTH 0x03FF8000 /* Frame length */ +#define U8500_I2C_MCR_A10 0x000007FE /* Enable 10 bit address */ +/* mask for length field,stop and operation */ +#define U8500_I2C_MCR_LENGTH_STOP_OP 0x3FFC001 + +/* Shift values for Master control values */ +#define U8500_I2C_MCR_SHIFT_OP 0 /* Operation */ +#define U8500_I2C_MCR_SHIFT_A7 1 /* LSB bits of Address */ +#define U8500_I2C_MCR_SHIFT_EA10 8 /* Extended Address */ +#define U8500_I2C_MCR_SHIFT_SB 11 /* Start byte procedure */ +#define U8500_I2C_MCR_SHIFT_AM 12 /* Address type */ +#define U8500_I2C_MCR_SHIFT_STOP 14 /* stop condition */ +#define U8500_I2C_MCR_SHIFT_LENGTH 15 /* Frame length */ +#define U8500_I2C_MCR_SHIFT_A10 1 /* Enable 10 bit address */ + +#define U8500_I2C_MCR_SHIFT_LENGTH_STOP_OP 0 + + +/* Status Register */ + +/* Mask values for Status register */ +#define U8500_I2C_SR_OP 0x00000003 /* Operation */ +#define U8500_I2C_SR_STATUS 0x0000000C /* Controller Status */ +#define U8500_I2C_SR_CAUSE 0x00000070 /* Abort Cause */ +#define U8500_I2C_SR_TYPE 0x00000180 /* Receive Type */ +#define U8500_I2C_SR_LENGTH 0x000FF700 /* Transfer length */ + +/* Shift values for Status register */ +#define U8500_I2C_SR_SHIFT_OP 0 /* Operation */ +#define U8500_I2C_SR_SHIFT_STATUS 2 /* Controller Status */ +#define U8500_I2C_SR_SHIFT_CAUSE 4 /* Abort Cause */ +#define U8500_I2C_SR_SHIFT_TYPE 7 /* Receive Type */ +#define U8500_I2C_SR_SHIFT_LENGTH 9 /* Transfer length */ + +/* abort cause */ +#define U8500_I2C_NACK_ADDR 0 +#define U8500_I2C_NACK_DATA 1 +#define U8500_I2C_ACK_MCODE 2 +#define U8500_I2C_ARB_LOST 3 +#define U8500_I2C_BERR_START 4 +#define U8500_I2C_BERR_STOP 5 +#define U8500_I2C_OVFL 6 + + +/* Baud rate counter registers */ + +/* Mask values for Baud rate counter register */ +#define U8500_I2C_BRCR_BRCNT2 0xFFFF /* Baud Rate Cntr BRCR for HS */ +#define U8500_I2C_BRCR_BRCNT1 0xFFFF0000 /* BRCR for Standard and Fast */ + +/* Shift values for the Baud rate counter register */ +#define U8500_I2C_BRCR_SHIFT_BRCNT2 0 +#define U8500_I2C_BRCR_SHIFT_BRCNT1 16 + + +/* Interrupt Register */ + +/* Mask values for Interrupt registers */ +#define U8500_I2C_INT_TXFE 0x00000001 /* Tx fifo empty */ +#define U8500_I2C_INT_TXFNE 0x00000002 /* Tx Fifo nearly empty */ +#define U8500_I2C_INT_TXFF 0x00000004 /* Tx Fifo Full */ +#define U8500_I2C_INT_TXFOVR 0x00000008 /* Tx Fifo over run */ +#define U8500_I2C_INT_RXFE 0x00000010 /* Rx Fifo Empty */ +#define U8500_I2C_INT_RXFNF 0x00000020 /* Rx Fifo nearly empty */ +#define U8500_I2C_INT_RXFF 0x00000040 /* Rx Fifo Full */ +#define U8500_I2C_INT_RFSR 0x00010000 /* Read From slave request */ +#define U8500_I2C_INT_RFSE 0x00020000 /* Read from slave empty */ +#define U8500_I2C_INT_WTSR 0x00040000 /* Write to Slave request */ +#define U8500_I2C_INT_MTD 0x00080000 /* Master Transcation Done*/ +#define U8500_I2C_INT_STD 0x00100000 /* Slave Transaction Done */ +#define U8500_I2C_INT_MAL 0x01000000 /* Master Arbitation Lost */ +#define U8500_I2C_INT_BERR 0x02000000 /* Bus Error */ +#define U8500_I2C_INT_MTDWS 0x10000000 /* Master Tran Done wo/ Stop */ + +/* Max clocks (Hz) */ +#define U8500_I2C_MAX_STANDARD_SCL 100000 +#define U8500_I2C_MAX_FAST_SCL 400000 +#define U8500_I2C_MAX_HIGH_SPEED_SCL 3400000 + +#endif /* _U8500_I2C_H_ */ diff --git a/qemu/roms/u-boot/drivers/i2c/zynq_i2c.c b/qemu/roms/u-boot/drivers/i2c/zynq_i2c.c new file mode 100644 index 000000000..b3264af45 --- /dev/null +++ b/qemu/roms/u-boot/drivers/i2c/zynq_i2c.c @@ -0,0 +1,307 @@ +/* + * Driver for the Zynq-7000 PS I2C controller + * IP from Cadence (ID T-CS-PE-0007-100, Version R1p10f2) + * + * Author: Joe Hershberger <joe.hershberger@ni.com> + * Copyright (c) 2012 Joe Hershberger. + * + * Copyright (c) 2012-2013 Xilinx, Michal Simek + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <i2c.h> +#include <asm/errno.h> +#include <asm/arch/hardware.h> + +/* i2c register set */ +struct zynq_i2c_registers { + u32 control; + u32 status; + u32 address; + u32 data; + u32 interrupt_status; + u32 transfer_size; + u32 slave_mon_pause; + u32 time_out; + u32 interrupt_mask; + u32 interrupt_enable; + u32 interrupt_disable; +}; + +/* Control register fields */ +#define ZYNQ_I2C_CONTROL_RW 0x00000001 +#define ZYNQ_I2C_CONTROL_MS 0x00000002 +#define ZYNQ_I2C_CONTROL_NEA 0x00000004 +#define ZYNQ_I2C_CONTROL_ACKEN 0x00000008 +#define ZYNQ_I2C_CONTROL_HOLD 0x00000010 +#define ZYNQ_I2C_CONTROL_SLVMON 0x00000020 +#define ZYNQ_I2C_CONTROL_CLR_FIFO 0x00000040 +#define ZYNQ_I2C_CONTROL_DIV_B_SHIFT 8 +#define ZYNQ_I2C_CONTROL_DIV_B_MASK 0x00003F00 +#define ZYNQ_I2C_CONTROL_DIV_A_SHIFT 14 +#define ZYNQ_I2C_CONTROL_DIV_A_MASK 0x0000C000 + +/* Status register values */ +#define ZYNQ_I2C_STATUS_RXDV 0x00000020 +#define ZYNQ_I2C_STATUS_TXDV 0x00000040 +#define ZYNQ_I2C_STATUS_RXOVF 0x00000080 +#define ZYNQ_I2C_STATUS_BA 0x00000100 + +/* Interrupt register fields */ +#define ZYNQ_I2C_INTERRUPT_COMP 0x00000001 +#define ZYNQ_I2C_INTERRUPT_DATA 0x00000002 +#define ZYNQ_I2C_INTERRUPT_NACK 0x00000004 +#define ZYNQ_I2C_INTERRUPT_TO 0x00000008 +#define ZYNQ_I2C_INTERRUPT_SLVRDY 0x00000010 +#define ZYNQ_I2C_INTERRUPT_RXOVF 0x00000020 +#define ZYNQ_I2C_INTERRUPT_TXOVF 0x00000040 +#define ZYNQ_I2C_INTERRUPT_RXUNF 0x00000080 +#define ZYNQ_I2C_INTERRUPT_ARBLOST 0x00000200 + +#define ZYNQ_I2C_FIFO_DEPTH 16 +#define ZYNQ_I2C_TRANSFERT_SIZE_MAX 255 /* Controller transfer limit */ + +static struct zynq_i2c_registers *i2c_select(struct i2c_adapter *adap) +{ + return adap->hwadapnr ? + /* Zynq PS I2C1 */ + (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR1 : + /* Zynq PS I2C0 */ + (struct zynq_i2c_registers *)ZYNQ_I2C_BASEADDR0; +} + +/* I2C init called by cmd_i2c when doing 'i2c reset'. */ +static void zynq_i2c_init(struct i2c_adapter *adap, int requested_speed, + int slaveadd) +{ + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */ + writel((16 << ZYNQ_I2C_CONTROL_DIV_B_SHIFT) | + (2 << ZYNQ_I2C_CONTROL_DIV_A_SHIFT), &zynq_i2c->control); + + /* Enable master mode, ack, and 7-bit addressing */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_MS | + ZYNQ_I2C_CONTROL_ACKEN | ZYNQ_I2C_CONTROL_NEA); +} + +#ifdef DEBUG +static void zynq_i2c_debug_status(struct zynq_i2c_registers *zynq_i2c) +{ + int int_status; + int status; + int_status = readl(&zynq_i2c->interrupt_status); + + status = readl(&zynq_i2c->status); + if (int_status || status) { + debug("Status: "); + if (int_status & ZYNQ_I2C_INTERRUPT_COMP) + debug("COMP "); + if (int_status & ZYNQ_I2C_INTERRUPT_DATA) + debug("DATA "); + if (int_status & ZYNQ_I2C_INTERRUPT_NACK) + debug("NACK "); + if (int_status & ZYNQ_I2C_INTERRUPT_TO) + debug("TO "); + if (int_status & ZYNQ_I2C_INTERRUPT_SLVRDY) + debug("SLVRDY "); + if (int_status & ZYNQ_I2C_INTERRUPT_RXOVF) + debug("RXOVF "); + if (int_status & ZYNQ_I2C_INTERRUPT_TXOVF) + debug("TXOVF "); + if (int_status & ZYNQ_I2C_INTERRUPT_RXUNF) + debug("RXUNF "); + if (int_status & ZYNQ_I2C_INTERRUPT_ARBLOST) + debug("ARBLOST "); + if (status & ZYNQ_I2C_STATUS_RXDV) + debug("RXDV "); + if (status & ZYNQ_I2C_STATUS_TXDV) + debug("TXDV "); + if (status & ZYNQ_I2C_STATUS_RXOVF) + debug("RXOVF "); + if (status & ZYNQ_I2C_STATUS_BA) + debug("BA "); + debug("TS%d ", readl(&zynq_i2c->transfer_size)); + debug("\n"); + } +} +#endif + +/* Wait for an interrupt */ +static u32 zynq_i2c_wait(struct zynq_i2c_registers *zynq_i2c, u32 mask) +{ + int timeout, int_status; + + for (timeout = 0; timeout < 100; timeout++) { + udelay(100); + int_status = readl(&zynq_i2c->interrupt_status); + if (int_status & mask) + break; + } +#ifdef DEBUG + zynq_i2c_debug_status(zynq_i2c); +#endif + /* Clear interrupt status flags */ + writel(int_status & mask, &zynq_i2c->interrupt_status); + + return int_status & mask; +} + +/* + * I2C probe called by cmd_i2c when doing 'i2c probe'. + * Begin read, nak data byte, end. + */ +static int zynq_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* Attempt to read a byte */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_RW); + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + writel(0xFF, &zynq_i2c->interrupt_status); + writel(dev, &zynq_i2c->address); + writel(1, &zynq_i2c->transfer_size); + + return (zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP | + ZYNQ_I2C_INTERRUPT_NACK) & + ZYNQ_I2C_INTERRUPT_COMP) ? 0 : -ETIMEDOUT; +} + +/* + * I2C read called by cmd_i2c when doing 'i2c read' and by cmd_eeprom.c + * Begin write, send address byte(s), begin read, receive data bytes, end. + */ +static int zynq_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr, + int alen, u8 *data, int length) +{ + u32 status; + u32 i = 0; + u8 *cur_data = data; + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* Check the hardware can handle the requested bytes */ + if ((length < 0) || (length > ZYNQ_I2C_TRANSFERT_SIZE_MAX)) + return -EINVAL; + + /* Write the register address */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_HOLD); + /* + * Temporarily disable restart (by clearing hold) + * It doesn't seem to work. + */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + writel(0xFF, &zynq_i2c->interrupt_status); + if (alen) { + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW); + writel(dev, &zynq_i2c->address); + while (alen--) + writel(addr >> (8 * alen), &zynq_i2c->data); + + /* Wait for the address to be sent */ + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Device acked address\n"); + } + + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_RW); + /* Start reading data */ + writel(dev, &zynq_i2c->address); + writel(length, &zynq_i2c->transfer_size); + + /* Wait for data */ + do { + status = zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP | + ZYNQ_I2C_INTERRUPT_DATA); + if (!status) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Read %d bytes\n", + length - readl(&zynq_i2c->transfer_size)); + for (; i < length - readl(&zynq_i2c->transfer_size); i++) + *(cur_data++) = readl(&zynq_i2c->data); + } while (readl(&zynq_i2c->transfer_size) != 0); + /* All done... release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + +#ifdef DEBUG + zynq_i2c_debug_status(zynq_i2c); +#endif + return 0; +} + +/* + * I2C write called by cmd_i2c when doing 'i2c write' and by cmd_eeprom.c + * Begin write, send address byte(s), send data bytes, end. + */ +static int zynq_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, + int alen, u8 *data, int length) +{ + u8 *cur_data = data; + struct zynq_i2c_registers *zynq_i2c = i2c_select(adap); + + /* Write the register address */ + setbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_CLR_FIFO | + ZYNQ_I2C_CONTROL_HOLD); + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_RW); + writel(0xFF, &zynq_i2c->interrupt_status); + writel(dev, &zynq_i2c->address); + if (alen) { + while (alen--) + writel(addr >> (8 * alen), &zynq_i2c->data); + /* Start the tranfer */ + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + debug("Device acked address\n"); + } + + while (length--) { + writel(*(cur_data++), &zynq_i2c->data); + if (readl(&zynq_i2c->transfer_size) == ZYNQ_I2C_FIFO_DEPTH) { + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) { + /* Release the bus */ + clrbits_le32(&zynq_i2c->control, + ZYNQ_I2C_CONTROL_HOLD); + return -ETIMEDOUT; + } + } + } + + /* All done... release the bus */ + clrbits_le32(&zynq_i2c->control, ZYNQ_I2C_CONTROL_HOLD); + /* Wait for the address and data to be sent */ + if (!zynq_i2c_wait(zynq_i2c, ZYNQ_I2C_INTERRUPT_COMP)) + return -ETIMEDOUT; + return 0; +} + +static unsigned int zynq_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + if (speed != 1000000) + return -EINVAL; + + return 0; +} + +U_BOOT_I2C_ADAP_COMPLETE(zynq_0, zynq_i2c_init, zynq_i2c_probe, zynq_i2c_read, + zynq_i2c_write, zynq_i2c_set_bus_speed, + CONFIG_SYS_I2C_ZYNQ_SPEED, CONFIG_SYS_I2C_ZYNQ_SLAVE, + 0) +U_BOOT_I2C_ADAP_COMPLETE(zynq_1, zynq_i2c_init, zynq_i2c_probe, zynq_i2c_read, + zynq_i2c_write, zynq_i2c_set_bus_speed, + CONFIG_SYS_I2C_ZYNQ_SPEED, CONFIG_SYS_I2C_ZYNQ_SLAVE, + 1) |