Hi Masahiro, On 22 December 2014 at 03:58, Masahiro Yamada <yamad...@jp.panasonic.com> wrote: > This commit adds on-chip I2C driver used on newer SoCs of Panasonic > UniPhier platform. > > Signed-off-by: Masahiro Yamada <yamad...@jp.panasonic.com>
For driver model bits: Reviewed-by: Simon Glass <s...@chromium.org> A few comments below. > --- > > drivers/i2c/Kconfig | 8 + > drivers/i2c/Makefile | 1 + > drivers/i2c/i2c-uniphier-f.c | 355 > +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 364 insertions(+) > create mode 100644 drivers/i2c/i2c-uniphier-f.c > > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig > index 6a479ef..e75c1ff 100644 > --- a/drivers/i2c/Kconfig > +++ b/drivers/i2c/Kconfig > @@ -12,3 +12,11 @@ config SYS_I2C_UNIPHIER > help > Support for Panasonic UniPhier I2C controller driver. This I2C > controller is used on PH1-LD4, PH1-sLD8 or older UniPhier SoCs. > + > +config SYS_I2C_UNIPHIER_F > + bool "UniPhier I2C with FIFO driver" > + depends on ARCH_UNIPHIER && DM_I2C > + default y > + help > + Support for Panasonic UniPhier I2C with FIFO controller driver. > + This I2C controller is used on PH1-Pro4 or newer UniPhier SoCs. > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile > index e2fcd24..0e4c9f4 100644 > --- a/drivers/i2c/Makefile > +++ b/drivers/i2c/Makefile > @@ -32,4 +32,5 @@ 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_UNIPHIER) += i2c-uniphier.o > +obj-$(CONFIG_SYS_I2C_UNIPHIER_F) += i2c-uniphier-f.o > obj-$(CONFIG_SYS_I2C_ZYNQ) += zynq_i2c.o > diff --git a/drivers/i2c/i2c-uniphier-f.c b/drivers/i2c/i2c-uniphier-f.c > new file mode 100644 > index 0000000..14a7f1c > --- /dev/null > +++ b/drivers/i2c/i2c-uniphier-f.c > @@ -0,0 +1,355 @@ > +/* > + * Copyright (C) 2014 Panasonic Corporation > + * Author: Masahiro Yamada <yamad...@jp.panasonic.com> > + * > + * SPDX-License-Identifier: GPL-2.0+ > + */ > + > +/* #define DEBUG */ > + > +#include <common.h> > +#include <linux/types.h> > +#include <asm/io.h> > +#include <asm/errno.h> > +#include <dm/device.h> > +#include <dm/root.h> > +#include <i2c.h> > +#include <fdtdec.h> > + > +DECLARE_GLOBAL_DATA_PTR; > + > +#define I2C_CR 0x00 /* control register */ struct i2c_reg { u32 cr; u32 fifo; ... } > +#define I2C_CR_MST (1 << 3) /* master mode */ > +#define I2C_CR_STA (1 << 2) /* start condition */ > +#define I2C_CR_STO (1 << 1) /* stop condition */ > +#define I2C_CR_NACK (1 << 0) /* not ACK */ > + > +#define I2C_DTTX 0x04 /* send FIFO */ > +#define I2C_DTRX 0x04 /* receive FIFO */ > +#define I2C_DTTX_CMD (1 << 8) /* send command > (slave addr) */ > +#define I2C_DTTX_RD (1 << 0) /* read */ > +#define I2C_SLAD 0x0c /* slave address */ > +#define I2C_CYC 0x10 /* clock cycle control */ > +#define I2C_LCTL 0x14 /* clock low period control */ > +#define I2C_SSUT 0x18 /* restart/stop setup time control */ > +#define I2C_DSUT 0x1c /* data setup time control */ > +#define I2C_INT 0x20 /* interrupt status */ > +#define I2C_IE 0x24 /* interrupt enable */ > +#define I2C_IC 0x28 /* interrupt clear */ > +#define I2C_INT_TE (1 << 9) /* TX FIFO empty */ > +#define I2C_INT_RB (1 << 4) /* received specified > bytes */ > +#define I2C_INT_NA (1 << 2) /* no answer */ > +#define I2C_INT_AL (1 << 1) /* arbitration lost */ > +#define I2C_SR 0x2c /* status register */ > +#define I2C_SR_DB (1 << 12) /* device busy */ > +#define I2C_SR_BB (1 << 8) /* bus busy */ > +#define I2C_SR_RFF (1 << 3) /* Rx FIFO full */ > +#define I2C_SR_RNE (1 << 2) /* Rx FIFO not empty > */ > +#define I2C_SR_TNF (1 << 1) /* Tx FIFO not full */ > +#define I2C_SR_TFE (1 << 0) /* Tx FIFO empty */ > +#define I2C_RST 0x34 /* reset control */ > +#define I2C_RST_TBRST (1 << 2) /* clear Tx FIFO */ > +#define I2C_RST_RBRST (1 << 1) /* clear Rx FIFO */ > +#define I2C_RST_RST (1 << 0) /* forcible bus reset > */ > +#define I2C_TBC 0x40 > +#define I2C_RBC 0x44 > +#define I2C_TBCM 0x48 > +#define I2C_RBCM 0x4c > +#define I2C_BRST 0x50 /* bus reset */ > +#define I2C_BRST_FOEN (1 << 1) /* normal operation */ > +#define I2C_BRST_RSCLO (1 << 0) /* release SCL low > fixing */ > + > +#define FIOCLK 50000000 > + > +struct uniphier_fi2c_dev { > + void __iomem *base; /* register base */ Should use register access in U-Boot. > + unsigned long fioclk; /* internal operation clock */ > + unsigned long wait_us; /* wait for every byte transfer (us) > */ This is really a timeout isn't it? > +}; > + > +static int poll_status(struct uniphier_fi2c_dev *dev, int offset, u32 flag) > +{ > + int wait = 1000000; /* 1 sec */ > + > + while (readl(dev->base + offset) & flag) { > + if (wait-- < 0) > + return -EREMOTEIO; > + udelay(1); > + } > + > + return 0; > +} > + > +static int uniphier_fi2c_probe(struct udevice *dev) > +{ > + fdt_addr_t addr; > + fdt_size_t size; > + struct uniphier_fi2c_dev *priv = dev_get_priv(dev); > + int ret; > + > + addr = fdtdec_get_addr_size(gd->fdt_blob, dev->of_offset, "reg", > + &size); > + > + priv->base = map_sysmem(addr, size); > + > + if (!priv->base) > + return -ENOMEM; > + > + priv->fioclk = FIOCLK; > + > + /* bus forcible reset */ > + writel(I2C_RST_RST, priv->base + I2C_RST); > + ret = poll_status(priv, I2C_RST, I2C_RST_RST); > + if (ret < 0) { > + debug("error: fail to reset I2C controller\n"); > + return ret; > + } > + > + writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, priv->base + I2C_BRST); > + > + return 0; > +} > + > +static int uniphier_fi2c_remove(struct udevice *dev) > +{ > + struct uniphier_fi2c_dev *priv = dev_get_priv(dev); > + > + unmap_sysmem(priv->base); > + > + return 0; > +} > + > +static int uniphier_fi2c_child_pre_probe(struct udevice *dev) > +{ > + struct dm_i2c_chip *i2c_chip = dev_get_parentdata(dev); > + > + if (dev->of_offset == -1) > + return 0; > + return i2c_chip_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, > + i2c_chip); > +} > + > +static int wait_for_irq(struct uniphier_fi2c_dev *dev, u32 flags, > + bool *stop) > +{ > + u32 irq; > + unsigned long wait = dev->wait_us; > + int ret = -EREMOTEIO; > + > + do { > + udelay(1); > + irq = readl(dev->base + I2C_INT); > + } while (!(irq & flags) && wait--); > + > + if (wait < 0) { > + debug("error: time out\n"); > + return ret; > + } > + > + if (irq & I2C_INT_AL) { > + debug("error: arbitration lost\n"); > + *stop = false; > + return ret; > + } > + > + if (irq & I2C_INT_NA) { > + debug("error: no answer\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int uniphier_fi2c_transmit(struct uniphier_fi2c_dev *dev, > + uint addr, uint len, const u8 *buf) > +{ > + int ret, ret2; > + const u32 irq_flags = I2C_INT_TE | I2C_INT_NA | I2C_INT_AL; > + bool stop = true; > + > + debug("%s: addr = %x, len = %d\n", __func__, addr, len); > + > + ret = poll_status(dev, I2C_SR, I2C_SR_DB); > + if (ret < 0) { > + debug("error: device busy\n"); > + return ret; > + } > + > + writel(I2C_DTTX_CMD | addr << 1, dev->base + I2C_DTTX); > + > + writel(irq_flags, dev->base + I2C_IE); > + writel(irq_flags, dev->base + I2C_IC); > + > + debug("start condition\n"); > + writel(I2C_CR_MST | I2C_CR_STA, dev->base + I2C_CR); > + > + ret = wait_for_irq(dev, irq_flags, &stop); > + if (ret < 0) > + goto error; > + > + while (len--) { > + debug("sending %x\n", *buf); > + writel(*buf++, dev->base + I2C_DTTX); > + > + writel(irq_flags, dev->base + I2C_IC); > + > + ret = wait_for_irq(dev, irq_flags, &stop); > + if (ret < 0) > + goto error; > + } > + > +error: > + if (stop) { > + debug("stop condition\n"); > + writel(I2C_CR_MST | I2C_CR_STO, dev->base + I2C_CR); > + } > + > + writel(irq_flags, dev->base + I2C_IC); > + > + ret2 = poll_status(dev, I2C_SR, I2C_SR_DB); > + if (ret2 < 0) { > + debug("error: device busy after operation\n"); > + ret = ret ? ret : ret2; > + } > + > + return ret; > +} > + > +static int uniphier_fi2c_receive(struct uniphier_fi2c_dev *dev, > + uint addr, uint len, u8 *buf) > +{ > + int ret, ret2; > + const u32 irq_flags = I2C_INT_RB | I2C_INT_NA | I2C_INT_AL; > + bool stop = true; > + > + debug("%s: addr = %x, len = %d\n", __func__, addr, len); > + > + /* > + * In case 'len == 0', only the slave address should be sent > + * for probing, which is covered by the transmit function. > + */ > + if (len == 0) > + return uniphier_fi2c_transmit(dev, addr, len, buf); > + > + ret = poll_status(dev, I2C_SR, I2C_SR_DB); > + if (ret < 0) { > + debug("error: device busy\n"); > + return ret; > + } > + > + writel(I2C_DTTX_CMD | I2C_DTTX_RD | addr << 1, dev->base + I2C_DTTX); > + > + writel(0, dev->base + I2C_RBC); > + writel(irq_flags, dev->base + I2C_IE); > + writel(irq_flags, dev->base + I2C_IC); > + > + debug("start condition\n"); > + writel(I2C_CR_MST | I2C_CR_STA | (len == 1 ? I2C_CR_NACK : 0), > + dev->base + I2C_CR); > + > + while (len--) { > + ret = wait_for_irq(dev, irq_flags, &stop); > + if (ret < 0) > + goto error; > + > + *buf++ = readl(dev->base + I2C_DTRX); > + debug("received %x\n", *(buf - 1)); > + > + if (len == 1) > + writel(I2C_CR_MST | I2C_CR_NACK, dev->base + I2C_CR); > + > + writel(irq_flags, dev->base + I2C_IC); > + } > + > +error: > + if (stop) { > + debug("stop condition\n"); > + writel(I2C_CR_MST | I2C_CR_STO, dev->base + I2C_CR); > + } > + > + writel(irq_flags, dev->base + I2C_IC); > + > + ret2 = poll_status(dev, I2C_SR, I2C_SR_DB); > + if (ret2 < 0) { > + debug("error: device busy after operation\n"); > + ret = ret ? ret : ret2; > + } > + > + return ret; > +} > + > +static int uniphier_fi2c_xfer(struct udevice *bus, struct i2c_msg *msg, > + int nmsgs) > +{ > + int ret = 0; > + struct uniphier_fi2c_dev *dev = dev_get_priv(bus); > + > + for (; nmsgs > 0; nmsgs--, msg++) { > + if (msg->flags & I2C_M_RD) > + ret = uniphier_fi2c_receive(dev, msg->addr, > + msg->len, msg->buf); > + else > + ret = uniphier_fi2c_transmit(dev, msg->addr, > + msg->len, msg->buf); > + > + if (ret < 0) > + break; > + } > + > + return ret; > +} > + > +static int uniphier_fi2c_set_bus_speed(struct udevice *bus, unsigned int > speed) > +{ > + struct uniphier_fi2c_dev *dev = dev_get_priv(bus); > + unsigned int clk_count; > + int ret; > + > + /* max supported frequency is 400 kHz */ > + if (speed > 400000) > + return -EINVAL; > + > + ret = poll_status(dev, I2C_SR, I2C_SR_DB); > + if (ret < 0) { > + debug("error: device busy\n"); > + return ret; > + } > + > + /* make sure the bus is idle when change the freqency */ > + writel(I2C_BRST_RSCLO, dev->base + I2C_BRST); > + > + clk_count = dev->fioclk / speed; > + > + writel(clk_count, dev->base + I2C_CYC); > + writel(clk_count / 2, dev->base + I2C_LCTL); > + writel(clk_count / 2, dev->base + I2C_SSUT); > + writel(clk_count / 16, dev->base + I2C_DSUT); > + > + writel(I2C_BRST_FOEN | I2C_BRST_RSCLO, dev->base + I2C_BRST); > + > + dev->wait_us = 20000000 / speed; > + > + return 0; > +} > + > +static const struct dm_i2c_ops uniphier_fi2c_ops = { > + .xfer = uniphier_fi2c_xfer, > + .set_bus_speed = uniphier_fi2c_set_bus_speed, > +}; > + > +static const struct udevice_id uniphier_fi2c_of_match[] = { > + { .compatible = "panasonic,uniphier-fi2c" }, > + {}, > +}; > + > +U_BOOT_DRIVER(uniphier_fi2c) = { > + .name = "uniphier-fi2c", > + .id = UCLASS_I2C, > + .of_match = uniphier_fi2c_of_match, > + .probe = uniphier_fi2c_probe, > + .remove = uniphier_fi2c_remove, > + .per_child_auto_alloc_size = sizeof(struct dm_i2c_chip), > + .child_pre_probe = uniphier_fi2c_child_pre_probe, > + .priv_auto_alloc_size = sizeof(struct uniphier_fi2c_dev), > + .ops = &uniphier_fi2c_ops, > +}; > -- > 1.9.1 Regards, Simon _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot