123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- /*
- * Faraday I2C Controller
- *
- * (C) Copyright 2010 Faraday Technology
- * Dante Su <dantesu@faraday-tech.com>
- *
- * This file is released under the terms of GPL v2 and any later version.
- * See the file COPYING in the root directory of the source tree for details.
- */
- #include <common.h>
- #include <asm/io.h>
- #include <i2c.h>
- #include "fti2c010.h"
- #ifndef CONFIG_HARD_I2C
- #error "fti2c010: CONFIG_HARD_I2C is not defined"
- #endif
- #ifndef CONFIG_SYS_I2C_SPEED
- #define CONFIG_SYS_I2C_SPEED 50000
- #endif
- #ifndef CONFIG_FTI2C010_FREQ
- #define CONFIG_FTI2C010_FREQ clk_get_rate("I2C")
- #endif
- /* command timeout */
- #define CFG_CMD_TIMEOUT 10 /* ms */
- /* 7-bit chip address + 1-bit read/write */
- #define I2C_RD(chip) ((((chip) << 1) & 0xff) | 1)
- #define I2C_WR(chip) (((chip) << 1) & 0xff)
- struct fti2c010_chip {
- void __iomem *regs;
- uint bus;
- uint speed;
- };
- static struct fti2c010_chip chip_list[] = {
- {
- .bus = 0,
- .regs = (void __iomem *)CONFIG_FTI2C010_BASE,
- },
- #ifdef CONFIG_I2C_MULTI_BUS
- # ifdef CONFIG_FTI2C010_BASE1
- {
- .bus = 1,
- .regs = (void __iomem *)CONFIG_FTI2C010_BASE1,
- },
- # endif
- # ifdef CONFIG_FTI2C010_BASE2
- {
- .bus = 2,
- .regs = (void __iomem *)CONFIG_FTI2C010_BASE2,
- },
- # endif
- # ifdef CONFIG_FTI2C010_BASE3
- {
- .bus = 3,
- .regs = (void __iomem *)CONFIG_FTI2C010_BASE3,
- },
- # endif
- #endif /* #ifdef CONFIG_I2C_MULTI_BUS */
- };
- static struct fti2c010_chip *curr = chip_list;
- static int fti2c010_wait(uint32_t mask)
- {
- int ret = -1;
- uint32_t stat, ts;
- struct fti2c010_regs *regs = curr->regs;
- for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
- stat = readl(®s->sr);
- if ((stat & mask) == mask) {
- ret = 0;
- break;
- }
- }
- return ret;
- }
- /*
- * u-boot I2C API
- */
- /*
- * Initialization, must be called once on start up, may be called
- * repeatedly to change the speed and slave addresses.
- */
- void i2c_init(int speed, int slaveaddr)
- {
- if (speed || !curr->speed)
- i2c_set_bus_speed(speed);
- /* if slave mode disabled */
- if (!slaveaddr)
- return;
- /*
- * TODO:
- * Implement slave mode, but is it really necessary?
- */
- }
- /*
- * Probe the given I2C chip address. Returns 0 if a chip responded,
- * not 0 on failure.
- */
- int i2c_probe(uchar chip)
- {
- int ret;
- struct fti2c010_regs *regs = curr->regs;
- i2c_init(0, 0);
- /* 1. Select slave device (7bits Address + 1bit R/W) */
- writel(I2C_WR(chip), ®s->dr);
- writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
- ret = fti2c010_wait(SR_DT);
- if (ret)
- return ret;
- /* 2. Select device register */
- writel(0, ®s->dr);
- writel(CR_ENABLE | CR_TBEN, ®s->cr);
- ret = fti2c010_wait(SR_DT);
- return ret;
- }
- /*
- * 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 *buf, int len)
- {
- int ret, pos;
- uchar paddr[4];
- struct fti2c010_regs *regs = curr->regs;
- i2c_init(0, 0);
- paddr[0] = (addr >> 0) & 0xFF;
- paddr[1] = (addr >> 8) & 0xFF;
- paddr[2] = (addr >> 16) & 0xFF;
- paddr[3] = (addr >> 24) & 0xFF;
- /*
- * Phase A. Set register address
- */
- /* A.1 Select slave device (7bits Address + 1bit R/W) */
- writel(I2C_WR(chip), ®s->dr);
- writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
- ret = fti2c010_wait(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(SR_DT);
- if (ret)
- return ret;
- }
- /*
- * Phase B. Get register data
- */
- /* B.1 Select slave device (7bits Address + 1bit R/W) */
- writel(I2C_RD(chip), ®s->dr);
- writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
- ret = fti2c010_wait(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(stat);
- if (ret)
- break;
- buf[pos] = (uchar)(readl(®s->dr) & 0xFF);
- }
- return ret;
- }
- /*
- * 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_write(uchar chip, uint addr, int alen, uchar *buf, int len)
- {
- int ret, pos;
- uchar paddr[4];
- struct fti2c010_regs *regs = curr->regs;
- i2c_init(0, 0);
- paddr[0] = (addr >> 0) & 0xFF;
- paddr[1] = (addr >> 8) & 0xFF;
- paddr[2] = (addr >> 16) & 0xFF;
- paddr[3] = (addr >> 24) & 0xFF;
- /*
- * Phase A. Set register address
- *
- * A.1 Select slave device (7bits Address + 1bit R/W)
- */
- writel(I2C_WR(chip), ®s->dr);
- writel(CR_ENABLE | CR_TBEN | CR_START, ®s->cr);
- ret = fti2c010_wait(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(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(SR_DT);
- if (ret)
- break;
- }
- return ret;
- }
- /*
- * Functions for setting the current I2C bus and its speed
- */
- #ifdef CONFIG_I2C_MULTI_BUS
- /*
- * i2c_set_bus_num:
- *
- * Change the active I2C bus. Subsequent read/write calls will
- * go to this one.
- *
- * bus - bus index, zero based
- *
- * Returns: 0 on success, not 0 on failure
- */
- int i2c_set_bus_num(uint bus)
- {
- if (bus >= ARRAY_SIZE(chip_list))
- return -1;
- curr = chip_list + bus;
- i2c_init(0, 0);
- return 0;
- }
- /*
- * i2c_get_bus_num:
- *
- * Returns index of currently active I2C bus. Zero-based.
- */
- uint i2c_get_bus_num(void)
- {
- return curr->bus;
- }
- #endif /* #ifdef CONFIG_I2C_MULTI_BUS */
- /*
- * i2c_set_bus_speed:
- *
- * Change the speed of the active I2C bus
- *
- * speed - bus speed in Hz
- *
- * Returns: 0 on success, not 0 on failure
- */
- int i2c_set_bus_speed(uint speed)
- {
- struct fti2c010_regs *regs = curr->regs;
- uint clk = CONFIG_FTI2C010_FREQ;
- uint gsr = 0, tsr = 32;
- uint spd, div;
- if (!speed)
- speed = CONFIG_SYS_I2C_SPEED;
- for (div = 0; div < 0x3ffff; ++div) {
- /* SCLout = PCLK/(2*(COUNT + 2) + GSR) */
- spd = clk / (2 * (div + 2) + gsr);
- if (spd <= speed)
- break;
- }
- if (curr->speed == spd)
- return 0;
- writel(CR_I2CRST, ®s->cr);
- mdelay(100);
- if (readl(®s->cr) & CR_I2CRST) {
- printf("fti2c010: reset timeout\n");
- return -1;
- }
- curr->speed = spd;
- writel(TGSR_GSR(gsr) | TGSR_TSR(tsr), ®s->tgsr);
- writel(CDR_DIV(div), ®s->cdr);
- return 0;
- }
- /*
- * i2c_get_bus_speed:
- *
- * Returns speed of currently active I2C bus in Hz
- */
- uint i2c_get_bus_speed(void)
- {
- return curr->speed;
- }
|