
Hi Masahiro,
On 22 December 2014 at 03:58, Masahiro Yamada yamada.m@jp.panasonic.com wrote:
This commit adds on-chip I2C driver used on newer SoCs of Panasonic UniPhier platform.
Signed-off-by: Masahiro Yamada yamada.m@jp.panasonic.com
For driver model bits:
Reviewed-by: Simon Glass sjg@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 yamada.m@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