diff options
Diffstat (limited to 'qemu/roms/u-boot/drivers/i2c/fti2c010.c')
-rw-r--r-- | qemu/roms/u-boot/drivers/i2c/fti2c010.c | 346 |
1 files changed, 346 insertions, 0 deletions
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 |