diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/Kconfig | 13 | ||||
-rw-r--r-- | drivers/i2c/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/at91_i2c.c | 24 | ||||
-rw-r--r-- | drivers/i2c/meson_i2c.c | 31 | ||||
-rw-r--r-- | drivers/i2c/mxc_i2c.c | 25 | ||||
-rw-r--r-- | drivers/i2c/rcar_iic.c | 271 |
6 files changed, 323 insertions, 42 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 1989f8eb572..0630712e4a4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -141,7 +141,12 @@ config SYS_I2C_MESON bool "Amlogic Meson I2C driver" depends on DM_I2C && ARCH_MESON help - Add support for the Amlogic Meson I2C driver. + Add support for the I2C controller available in Amlogic Meson + SoCs. The controller supports programmable bus speed including + standard (100kbits/s) and fast (400kbit/s) speed and allows the + software to define a flexible format of the bit streams. It has an + internal buffer holding up to 8 bytes for transfers and supports + both 7-bit and 10-bit addresses. config SYS_I2C_MXC bool "NXP i.MX I2C driver" @@ -157,6 +162,12 @@ config SYS_I2C_OMAP24XX help Add support for the OMAP2+ I2C driver. +config SYS_I2C_RCAR_IIC + bool "Renesas RCar Gen3 IIC driver" + depends on RCAR_GEN3 && DM_I2C + help + Support for Renesas RCar Gen3 IIC controller. + config SYS_I2C_ROCKCHIP bool "Rockchip I2C driver" depends on DM_I2C diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 733cd3e92fa..169a2f1d7a9 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o obj-$(CONFIG_SYS_I2C_MXS) += mxs_i2c.o obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o +obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o exynos_hs_i2c.o obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o diff --git a/drivers/i2c/at91_i2c.c b/drivers/i2c/at91_i2c.c index d394044f80d..7917ca12312 100644 --- a/drivers/i2c/at91_i2c.c +++ b/drivers/i2c/at91_i2c.c @@ -72,6 +72,8 @@ static int at91_i2c_xfer_msg(struct at91_i2c_bus *bus, struct i2c_msg *msg) } else { writel(msg->buf[0], ®->thr); + ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY); + for (i = 1; !ret && (i < msg->len); i++) { writel(msg->buf[i], ®->thr); ret = at91_wait_for_xfer(bus, TWI_SR_TXRDY); @@ -199,27 +201,6 @@ static int at91_i2c_enable_clk(struct udevice *dev) return 0; } -static int at91_i2c_probe_chip(struct udevice *dev, uint chip, uint chip_flags) -{ - struct at91_i2c_bus *bus = dev_get_priv(dev); - struct at91_i2c_regs *reg = bus->regs; - int ret; - - ret = at91_i2c_enable_clk(dev); - if (ret) - return ret; - - writel(TWI_CR_SWRST, ®->cr); - - at91_calc_i2c_clock(dev, bus->clock_frequency); - - writel(bus->cwgr_val, ®->cwgr); - writel(TWI_CR_MSEN, ®->cr); - writel(TWI_CR_SVDIS, ®->cr); - - return 0; -} - static int at91_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) { struct at91_i2c_bus *bus = dev_get_priv(dev); @@ -254,7 +235,6 @@ static int at91_i2c_ofdata_to_platdata(struct udevice *dev) static const struct dm_i2c_ops at91_i2c_ops = { .xfer = at91_i2c_xfer, - .probe_chip = at91_i2c_probe_chip, .set_bus_speed = at91_i2c_set_bus_speed, .get_bus_speed = at91_i2c_get_bus_speed, }; diff --git a/drivers/i2c/meson_i2c.c b/drivers/i2c/meson_i2c.c index 2434d9ed538..4f37d2f3168 100644 --- a/drivers/i2c/meson_i2c.c +++ b/drivers/i2c/meson_i2c.c @@ -9,7 +9,7 @@ #include <dm.h> #include <i2c.h> -#define I2C_TIMEOUT_MS 500 +#define I2C_TIMEOUT_MS 100 /* Control register fields */ #define REG_CTRL_START BIT(0) @@ -44,12 +44,12 @@ struct i2c_regs { struct meson_i2c { struct i2c_regs *regs; - struct i2c_msg *msg; - bool last; - uint count; - uint pos; - u32 tokens[2]; - uint num_tokens; + struct i2c_msg *msg; /* Current I2C message */ + bool last; /* Whether the message is the last */ + uint count; /* Number of bytes in the current transfer */ + uint pos; /* Position of current transfer in message */ + u32 tokens[2]; /* Sequence of tokens to be written */ + uint num_tokens; /* Number of tokens to be written */ }; static void meson_i2c_reset_tokens(struct meson_i2c *i2c) @@ -69,6 +69,10 @@ static void meson_i2c_add_token(struct meson_i2c *i2c, int token) i2c->num_tokens++; } +/* + * Retrieve data for the current transfer (which can be at most 8 + * bytes) from the device internal buffer. + */ static void meson_i2c_get_data(struct meson_i2c *i2c, u8 *buf, int len) { u32 rdata0, rdata1; @@ -86,6 +90,10 @@ static void meson_i2c_get_data(struct meson_i2c *i2c, u8 *buf, int len) *buf++ = (rdata1 >> (i - 4) * 8) & 0xff; } +/* + * Write data for the current transfer (which can be at most 8 bytes) + * to the device internal buffer. + */ static void meson_i2c_put_data(struct meson_i2c *i2c, u8 *buf, int len) { u32 wdata0 = 0, wdata1 = 0; @@ -103,6 +111,11 @@ static void meson_i2c_put_data(struct meson_i2c *i2c, u8 *buf, int len) debug("meson i2c: write data %08x %08x len %d\n", wdata0, wdata1, len); } +/* + * Prepare the next transfer: pick the next 8 bytes in the remaining + * part of message and write tokens and data (if needed) to the + * device. + */ static void meson_i2c_prepare_xfer(struct meson_i2c *i2c) { bool write = !(i2c->msg->flags & I2C_M_RD); @@ -178,7 +191,7 @@ static int meson_i2c_xfer_msg(struct meson_i2c *i2c, struct i2c_msg *msg, if (readl(&i2c->regs->ctrl) & REG_CTRL_ERROR) { debug("meson i2c: error\n"); - return -ENXIO; + return -EREMOTEIO; } if ((msg->flags & I2C_M_RD) && i2c->count) { @@ -200,7 +213,7 @@ static int meson_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, for (i = 0; i < nmsgs; i++) { ret = meson_i2c_xfer_msg(i2c, msg + i, i == nmsgs - 1); if (ret) - return -EREMOTEIO; + return ret; } return 0; diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index abf1da2ae3e..205274e9476 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -317,16 +317,19 @@ static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip, temp |= I2CR_MTX | I2CR_TX_NO_AK; writeb(temp, base + (I2CR << reg_shift)); - /* write slave address */ - ret = tx_byte(i2c_bus, chip << 1); - if (ret < 0) - return ret; - - while (alen--) { - ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff); + if (alen >= 0) { + /* write slave address */ + ret = tx_byte(i2c_bus, chip << 1); if (ret < 0) return ret; + + while (alen--) { + ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff); + if (ret < 0) + return ret; + } } + return 0; } @@ -537,9 +540,11 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, if (ret < 0) return ret; - temp = readb(base + (I2CR << reg_shift)); - temp |= I2CR_RSTA; - writeb(temp, base + (I2CR << reg_shift)); + if (alen >= 0) { + temp = readb(base + (I2CR << reg_shift)); + temp |= I2CR_RSTA; + writeb(temp, base + (I2CR << reg_shift)); + } ret = tx_byte(i2c_bus, (chip << 1) | 1); if (ret < 0) { diff --git a/drivers/i2c/rcar_iic.c b/drivers/i2c/rcar_iic.c new file mode 100644 index 00000000000..57ae2f51fca --- /dev/null +++ b/drivers/i2c/rcar_iic.c @@ -0,0 +1,271 @@ +/* + * Renesas RCar IIC driver + * + * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com> + * + * Based on + * 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 <clk.h> +#include <dm.h> +#include <i2c.h> +#include <asm/io.h> + +struct rcar_iic_priv { + void __iomem *base; + struct clk clk; + u8 iccl; + u8 icch; +}; + +#define RCAR_IIC_ICDR 0x00 +#define RCAR_IIC_ICCR 0x04 +#define RCAR_IIC_ICSR 0x08 +#define RCAR_IIC_ICIC 0x0c +#define RCAR_IIC_ICCL 0x10 +#define RCAR_IIC_ICCH 0x14 + +/* ICCR */ +#define RCAR_IIC_ICCR_ICE BIT(7) +#define RCAR_IIC_ICCR_RACK BIT(6) +#define RCAR_IIC_ICCR_RTS BIT(4) +#define RCAR_IIC_ICCR_BUSY BIT(2) +#define RCAR_IIC_ICCR_SCP BIT(0) + +/* ICSR / ICIC */ +#define RCAR_IC_BUSY BIT(4) +#define RCAR_IC_TACK BIT(2) +#define RCAR_IC_DTE BIT(0) + +#define IRQ_WAIT 1000 + +static void sh_irq_dte(struct udevice *dev) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (RCAR_IC_DTE & readb(priv->base + RCAR_IIC_ICSR)) + break; + udelay(10); + } +} + +static int sh_irq_dte_with_tack(struct udevice *dev) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (RCAR_IC_DTE & readb(priv->base + RCAR_IIC_ICSR)) + break; + if (RCAR_IC_TACK & readb(priv->base + RCAR_IIC_ICSR)) + return -ETIMEDOUT; + udelay(10); + } + return 0; +} + +static void sh_irq_busy(struct udevice *dev) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < IRQ_WAIT; i++) { + if (!(RCAR_IC_BUSY & readb(priv->base + RCAR_IIC_ICSR))) + break; + udelay(10); + } +} + +static int rcar_iic_set_addr(struct udevice *dev, u8 chip, u8 read) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + + clrbits_8(priv->base + RCAR_IIC_ICCR, RCAR_IIC_ICCR_ICE); + setbits_8(priv->base + RCAR_IIC_ICCR, RCAR_IIC_ICCR_ICE); + + writeb(priv->iccl, priv->base + RCAR_IIC_ICCL); + writeb(priv->icch, priv->base + RCAR_IIC_ICCH); + writeb(RCAR_IC_TACK, priv->base + RCAR_IIC_ICIC); + + writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_RTS | RCAR_IIC_ICCR_BUSY, + priv->base + RCAR_IIC_ICCR); + sh_irq_dte(dev); + + clrbits_8(priv->base + RCAR_IIC_ICSR, RCAR_IC_TACK); + writeb(chip << 1 | read, priv->base + RCAR_IIC_ICDR); + return sh_irq_dte_with_tack(dev); +} + +static void rcar_iic_finish(struct udevice *dev) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + + writeb(0, priv->base + RCAR_IIC_ICSR); + clrbits_8(priv->base + RCAR_IIC_ICCR, RCAR_IIC_ICCR_ICE); +} + +static int rcar_iic_read_common(struct udevice *dev, struct i2c_msg *msg) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int i, ret = -EREMOTEIO; + + if (rcar_iic_set_addr(dev, msg->addr, 1) != 0) + goto err; + + udelay(10); + + writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_SCP, + priv->base + RCAR_IIC_ICCR); + + for (i = 0; i < msg->len; i++) { + if (sh_irq_dte_with_tack(dev) != 0) + goto err; + + msg->buf[i] = readb(priv->base + RCAR_IIC_ICDR) & 0xff; + + if (msg->len - 1 == i) { + writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_RACK, + priv->base + RCAR_IIC_ICCR); + } + } + + sh_irq_busy(dev); + ret = 0; + +err: + rcar_iic_finish(dev); + return ret; +} + +static int rcar_iic_write_common(struct udevice *dev, struct i2c_msg *msg) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int i, ret = -EREMOTEIO; + + if (rcar_iic_set_addr(dev, msg->addr, 0) != 0) + goto err; + + udelay(10); + + for (i = 0; i < msg->len; i++) { + writeb(msg->buf[i], priv->base + RCAR_IIC_ICDR); + if (sh_irq_dte_with_tack(dev) != 0) + goto err; + } + + if (msg->flags & I2C_M_STOP) { + writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_RTS, + priv->base + RCAR_IIC_ICCR); + if (sh_irq_dte_with_tack(dev) != 0) + goto err; + } + + sh_irq_busy(dev); + ret = 0; + +err: + rcar_iic_finish(dev); + return ret; +} + +static int rcar_iic_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) +{ + int ret; + + for (; nmsgs > 0; nmsgs--, msg++) { + if (msg->flags & I2C_M_RD) + ret = rcar_iic_read_common(dev, msg); + else + ret = rcar_iic_write_common(dev, msg); + + if (ret) + return -EREMOTEIO; + } + + return ret; +} + +static int rcar_iic_set_speed(struct udevice *dev, uint speed) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + const unsigned int ratio_high = 4; + const unsigned int ratio_low = 5; + int clkrate, denom; + + clkrate = clk_get_rate(&priv->clk); + if (clkrate < 0) + return clkrate; + + /* + * Calculate the value for ICCL and ICCH. From the data sheet: + * iccl = (p-clock / transfer-rate) * (L / (L + H)) + * icch = (p clock / transfer rate) * (H / (L + H)) + * where L and H are the SCL low and high ratio. + */ + denom = speed * (ratio_high + ratio_low); + priv->iccl = DIV_ROUND_CLOSEST(clkrate * ratio_low, denom); + priv->icch = DIV_ROUND_CLOSEST(clkrate * ratio_high, denom); + + return 0; +} + +static int rcar_iic_probe_chip(struct udevice *dev, uint addr, uint flags) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int ret; + + rcar_iic_set_addr(dev, addr, 1); + writeb(RCAR_IIC_ICCR_ICE | RCAR_IIC_ICCR_SCP, + priv->base + RCAR_IIC_ICCR); + ret = sh_irq_dte_with_tack(dev); + rcar_iic_finish(dev); + + return ret; +} + +static int rcar_iic_probe(struct udevice *dev) +{ + struct rcar_iic_priv *priv = dev_get_priv(dev); + int ret; + + priv->base = dev_read_addr_ptr(dev); + + ret = clk_get_by_index(dev, 0, &priv->clk); + if (ret) + return ret; + + ret = clk_enable(&priv->clk); + if (ret) + return ret; + + rcar_iic_finish(dev); + + return rcar_iic_set_speed(dev, 100000); +} + +static const struct dm_i2c_ops rcar_iic_ops = { + .xfer = rcar_iic_xfer, + .probe_chip = rcar_iic_probe_chip, + .set_bus_speed = rcar_iic_set_speed, +}; + +static const struct udevice_id rcar_iic_ids[] = { + { .compatible = "renesas,rmobile-iic" }, + { } +}; + +U_BOOT_DRIVER(iic_rcar) = { + .name = "iic_rcar", + .id = UCLASS_I2C, + .of_match = rcar_iic_ids, + .probe = rcar_iic_probe, + .priv_auto_alloc_size = sizeof(struct rcar_iic_priv), + .ops = &rcar_iic_ops, +}; |