Message ID | 1584665829-4881-4-git-send-email-alex.nemirovsky@cortina-access.com |
---|---|
State | New |
Headers | show |
Series | Cortina Access Drivers Package 2 | expand |
Hello Alex, Am 20.03.2020 um 01:57 schrieb Alex Nemirovsky: > From: Arthur Li <arthur.li at cortina-access.com> > > Add I2C controller support for Cortina Access CAxxxx SoCs > > Signed-off-by: Arthur Li <arthur.li at cortina-access.com> > Signed-off-by: Alex Nemirovsky <alex.nemirovsky at cortina-access.com> > CC: Heiko Schocher <hs at denx.de> > --- > > Changes in v3: None > Changes in v2: None > > MAINTAINERS | 4 + > drivers/i2c/Kconfig | 7 + > drivers/i2c/Makefile | 1 + > drivers/i2c/i2c-cortina.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++ > drivers/i2c/i2c-cortina.h | 92 ++++++++++++ > 5 files changed, 450 insertions(+) > create mode 100644 drivers/i2c/i2c-cortina.c > create mode 100644 drivers/i2c/i2c-cortina.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index bb45d3c..b147faa 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -181,6 +181,8 @@ F: drivers/gpio/cortina_gpio.c > F: drivers/watchdog/cortina_wdt.c > F: drivers/serial/serial_cortina.c > F: drivers/mmc/ca_dw_mmc.c > +F: drivers/i2c/i2c-cortina.c > +F: drivers/i2c/i2c-cortina.h > > ARM/CZ.NIC TURRIS MOX SUPPORT > M: Marek Behun <marek.behun at nic.cz> > @@ -672,6 +674,8 @@ F: drivers/gpio/cortina_gpio.c > F: drivers/watchdog/cortina_wdt.c > F: drivers/serial/serial_cortina.c > F: drivers/mmc/ca_dw_mmc.c > +F: drivers/i2c/i2c-cortina.c > +F: drivers/i2c/i2c-cortina.h > > MIPS MSCC > M: Gregory CLEMENT <gregory.clement at bootlin.com> > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig > index 03d2fed..b98a4aa 100644 > --- a/drivers/i2c/Kconfig > +++ b/drivers/i2c/Kconfig > @@ -85,6 +85,13 @@ config SYS_I2C_CADENCE > Say yes here to select Cadence I2C Host Controller. This controller is > e.g. used by Xilinx Zynq. > > +config SYS_I2C_CA > + tristate "Cortina-Access I2C Controller" > + depends on DM_I2C && CORTINA_PLATFORM > + default n > + help > + Say yes here to select Cortina-Access I2C Host Controller. > + > config SYS_I2C_DAVINCI > bool "Davinci I2C Controller" > depends on (ARCH_KEYSTONE || ARCH_DAVINCI) > diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile > index f5a471f..5d18cf7 100644 > --- a/drivers/i2c/Makefile > +++ b/drivers/i2c/Makefile > @@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o > obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o > obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o > obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o > +obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o > obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o > obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o > ifdef CONFIG_DM_PCI > diff --git a/drivers/i2c/i2c-cortina.c b/drivers/i2c/i2c-cortina.c > new file mode 100644 > index 0000000..99c63f3 > --- /dev/null > +++ b/drivers/i2c/i2c-cortina.c > @@ -0,0 +1,346 @@ > +// SPDX-License-Identifier: GPL-2.0+ > +/* > + * (C) Copyright 2020 > + * Arthur Li, Cortina Access, arthur.li at cortina-access.com. > + */ > + > +#include <common.h> > +#include <i2c.h> > +#include <asm/io.h> > +#include <dm.h> > +#include <mapmem.h> > +#include "i2c-cortina.h" > + > +static void set_speed(struct i2c_regs *regs, int i2c_spd) > +{ > + union ca_biw_cfg i2c_cfg; > + > + i2c_cfg.wrd = readl(®s->i2c_cfg); > + i2c_cfg.bf.core_en = 0; > + writel(i2c_cfg.wrd, ®s->i2c_cfg); > + > + switch (i2c_spd) { > + case IC_SPEED_MODE_MAX: > + i2c_cfg.bf.prer = > + CORTINA_PER_IO_FREQ / (5 * I2C_MAX_SPEED) - 1; > + break; > + > + case IC_SPEED_MODE_STANDARD: > + i2c_cfg.bf.prer = > + CORTINA_PER_IO_FREQ / (5 * I2C_STANDARD_SPEED) - 1; > + break; > + > + case IC_SPEED_MODE_FAST: > + default: > + i2c_cfg.bf.prer = > + CORTINA_PER_IO_FREQ / (5 * I2C_FAST_SPEED) - 1; > + break; > + } > + > + i2c_cfg.bf.core_en = 1; > + writel(i2c_cfg.wrd, ®s->i2c_cfg); > +} > + > +static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) > +{ > + struct ca_i2c *priv = dev_get_priv(bus); > + int i2c_spd; > + > + if (speed >= I2C_MAX_SPEED) { > + i2c_spd = IC_SPEED_MODE_MAX; > + priv->speed = I2C_MAX_SPEED; > + } else if (speed >= I2C_FAST_SPEED) { > + i2c_spd = IC_SPEED_MODE_FAST; > + priv->speed = I2C_FAST_SPEED; > + } else { > + i2c_spd = IC_SPEED_MODE_STANDARD; > + priv->speed = I2C_STANDARD_SPEED; > + } > + > + set_speed(priv->regs, i2c_spd); > + > + return 0; > +} > + > +static int ca_i2c_get_bus_speed(struct udevice *bus) > +{ > + struct ca_i2c *priv = dev_get_priv(bus); > + > + return priv->speed; > +} > + > +static void ca_i2c_init(struct i2c_regs *regs) > +{ > + union ca_biw_cfg i2c_cfg; > + > + i2c_cfg.wrd = readl(®s->i2c_cfg); > + i2c_cfg.bf.core_en = 0; > + i2c_cfg.bf.biw_soft_reset = 1; > + writel(i2c_cfg.wrd, ®s->i2c_cfg); > + mdelay(10); > + i2c_cfg.bf.biw_soft_reset = 0; > + writel(i2c_cfg.wrd, ®s->i2c_cfg); > + > + set_speed(regs, IC_SPEED_MODE_STANDARD); > + > + i2c_cfg.wrd = readl(®s->i2c_cfg); > + i2c_cfg.bf.core_en = 1; > + writel(i2c_cfg.wrd, ®s->i2c_cfg); > +} > + > +static int i2c_wait_complete(struct i2c_regs *regs) > +{ > + union ca_biw_ctrl i2c_ctrl; > + unsigned long start_time_bb = get_timer(0); > + > + i2c_ctrl.wrd = readl(®s->i2c_ctrl); > + > + while (i2c_ctrl.bf.biwdone == 0) { > + i2c_ctrl.wrd = readl(®s->i2c_ctrl); > + > + if (get_timer(start_time_bb) > > + (unsigned long)(I2C_BYTE_TO_BB)) { > + printf("%s not done!!!\n", __func__); > + return 1; Please use -ETIMEDOUT > + } > + } > + > + /* Clear done bit */ > + writel(i2c_ctrl.wrd, ®s->i2c_ctrl); > + > + return 0; > +} > + > +static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr, > + int write_read) > +{ > + writel(i2c_addr | write_read, ®s->i2c_txr); > + > + writel(BIW_CTRL_START | BIW_CTRL_WRITE, > + ®s->i2c_ctrl); > + > + i2c_wait_complete(regs); > +} > + > +static int i2c_wait_for_bus_busy(struct i2c_regs *regs) > +{ > + union ca_biw_ack i2c_ack; > + unsigned long start_time_bb = get_timer(0); > + > + i2c_ack.wrd = readl(®s->i2c_ack); > + > + while (i2c_ack.bf.biw_busy) { > + i2c_ack.wrd = readl(®s->i2c_ack); > + > + if (get_timer(start_time_bb) > > + (unsigned long)(I2C_BYTE_TO_BB)) { > + printf("%s: timeout!\n", __func__); > + return 1; Here too. > + } > + } > + > + return 0; > +} > + > +static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr, > + int alen, int write_read) > +{ > + int addr_len = alen; > + > + if (i2c_wait_for_bus_busy(regs)) > + return 1; Add a blank line please > + /* First cycle must write addr + offset */ > + chip = ((chip & 0x7F) << 1); > + if (alen == 0 && write_read == I2C_CMD_RD) > + i2c_setaddress(regs, chip, I2C_CMD_RD); > + else > + i2c_setaddress(regs, chip, I2C_CMD_WT); > + > + while (alen) { > + alen--; > + writel(addr, ®s->i2c_txr); > + if (write_read == I2C_CMD_RD) > + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, > + ®s->i2c_ctrl); > + else > + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); > + i2c_wait_complete(regs); > + } > + > + /* Send address again with Read flag if it's read command */ > + if (write_read == I2C_CMD_RD && addr_len > 0) > + i2c_setaddress(regs, chip, I2C_CMD_RD); > + > + return 0; > +} > + > +static int i2c_xfer_finish(struct i2c_regs *regs) > +{ > + /* Dummy read makes bus free */ > + writel(BIW_CTRL_READ | BIW_CTRL_STOP, ®s->i2c_ctrl); > + i2c_wait_complete(regs); > + > + if (i2c_wait_for_bus_busy(regs)) { > + printf("Timed out waiting for bus\n"); > + return 1; return -ETIMEDOUT > + } > + > + return 0; > +} > + > +static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr, > + int alen, uint8_t *buffer, int len) > +{ > + unsigned long start_time_rx; > + int rc = 0; > + > + if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD)) > + return 1; please return the return value from i2c_xfer_init() > + > + start_time_rx = get_timer(0); > + while (len) { > + /* ACK_IN is ack value to send during read. > + * ack high only on the very last byte! > + */ > + if (len == 1) { > + writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP, > + ®s->i2c_ctrl); > + } else { > + writel(BIW_CTRL_READ, ®s->i2c_ctrl); > + } remove the brackets, they are not needed. > + > + rc = i2c_wait_complete(regs); > + udelay(1); > + > + if (rc == 0) { > + *buffer++ = > + (uchar) readl(®s->i2c_rxr); > + len--; > + start_time_rx = get_timer(0); > + > + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { > + return 1; return -ETIMEDOUT > + } > + } > + i2c_xfer_finish(regs); > + return rc; > +} > + > +static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr, > + int alen, uint8_t *buffer, int len) > +{ > + int rc, nb = len; > + unsigned long start_time_tx; > + > + if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT)) > + return 1; please return the return value from i2c_xfer_init > + > + start_time_tx = get_timer(0); > + while (len) { > + writel(*buffer, ®s->i2c_txr); > + if (len == 1) { > + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, > + ®s->i2c_ctrl); > + } else { > + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); > + } also here, the brackets are not needed. > + > + rc = i2c_wait_complete(regs); > + > + if (rc == 0) { > + len--; > + buffer++; > + start_time_tx = get_timer(0); > + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { > + printf("Timed out. i2c write Failed\n"); > + return 1; return -ETIMEDOUT > + } > + } > + > + return 0; > +} > + > +static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr, > + uint chip_flags) > +{ > + struct ca_i2c *priv = dev_get_priv(bus); > + int ret; > + u32 tmp; > + > + /* Try to read the first location of the chip */ > + ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1); > + if (ret) > + ca_i2c_init(priv->regs); > + > + return ret; > +} > + > +static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) > +{ > + struct ca_i2c *priv = dev_get_priv(bus); > + int ret; > + > + debug("i2c_xfer: %d messages\n", nmsgs); > + for (; nmsgs > 0; nmsgs--, msg++) { > + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); > + if (msg->flags & I2C_M_RD) { > + ret = ca_i2c_read(priv->regs, msg->addr, 0, 0, > + msg->buf, msg->len); > + } else { > + ret = ca_i2c_write(priv->regs, msg->addr, 0, 0, > + msg->buf, msg->len); > + } here too, brackets are not needed. > + > + if (ret) { > + debug("i2c_write: error sending\n"); wrong printf when you did a read. > + return -EREMOTEIO; > + } > + } > + > + return 0; > +} > + > +static const struct dm_i2c_ops ca_i2c_ops = { > + .xfer = ca_i2c_xfer, > + .probe_chip = ca_i2c_probe_chip, > + .set_bus_speed = ca_i2c_set_bus_speed, > + .get_bus_speed = ca_i2c_get_bus_speed, > +}; > + > +static const struct udevice_id ca_i2c_ids[] = { > + { .compatible = "cortina,ca-i2c", }, > + { } > +}; > + > +static int ca_i2c_probe(struct udevice *bus) > +{ > + struct ca_i2c *priv = dev_get_priv(bus); > + > + ca_i2c_init(priv->regs); > + > + return 0; > +} > + > +static int ca_i2c_ofdata_to_platdata(struct udevice *bus) > +{ > + struct ca_i2c *priv = dev_get_priv(bus); > + > + priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs)); > + if (!priv->regs) { > + printf("I2C: base address is invalid\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +U_BOOT_DRIVER(i2c_cortina) = { > + .name = "i2c_cortina", > + .id = UCLASS_I2C, > + .of_match = ca_i2c_ids, > + .ofdata_to_platdata = ca_i2c_ofdata_to_platdata, > + .probe = ca_i2c_probe, > + .priv_auto_alloc_size = sizeof(struct ca_i2c), > + .ops = &ca_i2c_ops, > + .flags = DM_FLAG_PRE_RELOC, > +}; > diff --git a/drivers/i2c/i2c-cortina.h b/drivers/i2c/i2c-cortina.h > new file mode 100644 > index 0000000..f1c3dc7 > --- /dev/null > +++ b/drivers/i2c/i2c-cortina.h > @@ -0,0 +1,92 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * (C) Copyright 2019 > + * Cortina Access, <www.cortina-access.com> > + */ > + > +#ifndef __CA_I2C_H_ > +#define __CA_I2C_H_ > + > +#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__) > +struct i2c_regs { > + u32 i2c_cfg; > + u32 i2c_ctrl; > + u32 i2c_txr; > + u32 i2c_rxr; > + u32 i2c_ack; > + u32 i2c_ie0; > + u32 i2c_int0; > + u32 i2c_ie1; > + u32 i2c_int1; > + u32 i2c_stat; > +}; > + > +union ca_biw_cfg { > + struct biw_cfg { > + u32 core_en : 1; > + u32 biw_soft_reset : 1; > + u32 busywait_en : 1; > + u32 stretch_en : 1; > + u32 arb_en : 1; > + u32 clksync_en : 1; > + u32 rsrvd1 : 2; > + u32 spike_cnt : 4; > + u32 rsrvd2 : 4; > + u32 prer : 16; > + } bf; > + unsigned int wrd; > +}; > + > +union ca_biw_ctrl { > + struct biw_ctrl { > + u32 biwdone : 1; > + u32 rsrvd1 : 2; > + u32 ack_in : 1; > + u32 write : 1; > + u32 read : 1; > + u32 stop : 1; > + u32 start : 1; > + u32 rsrvd2 : 24; > + } bf; > + unsigned int wrd; > +}; > + > +union ca_biw_ack { > + struct biw_ack { > + u32 al :1; > + u32 biw_busy :1; > + u32 ack_out :1; > + u32 rsrvd1 :29; > + } bf; > + unsigned int wrd; > +}; > +#endif /* !__ASSEMBLER__*/ > + > +struct ca_i2c { > + struct i2c_regs *regs; > + unsigned int speed; > +}; > + > +#define I2C_CMD_WT 0 > +#define I2C_CMD_RD 1 > + > +#define BIW_CTRL_DONE BIT(0) > +#define BIW_CTRL_ACK_IN BIT(3) > +#define BIW_CTRL_WRITE BIT(4) > +#define BIW_CTRL_READ BIT(5) > +#define BIW_CTRL_STOP BIT(6) > +#define BIW_CTRL_START BIT(7) > + > +#define I2C_BYTE_TO (CONFIG_SYS_HZ / 500) > +#define I2C_STOPDET_TO (CONFIG_SYS_HZ / 500) > +#define I2C_BYTE_TO_BB (10) > + > +#define IC_SPEED_MODE_STANDARD 1 > +#define IC_SPEED_MODE_FAST 2 > +#define IC_SPEED_MODE_MAX 3 > + > +#define I2C_MAX_SPEED 1000000 > +#define I2C_FAST_SPEED 400000 > +#define I2C_STANDARD_SPEED 100000 > + Not needed, could you please use: https://gitlab.denx.de/u-boot/u-boot/-/blob/master/include/i2c.h#L44 > +#endif /* __CA_I2C_H_ */ > Thanks! bye, Heiko
diff --git a/MAINTAINERS b/MAINTAINERS index bb45d3c..b147faa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -181,6 +181,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun <marek.behun at nic.cz> @@ -672,6 +674,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h MIPS MSCC M: Gregory CLEMENT <gregory.clement at bootlin.com> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..b98a4aa 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -85,6 +85,13 @@ config SYS_I2C_CADENCE Say yes here to select Cadence I2C Host Controller. This controller is e.g. used by Xilinx Zynq. +config SYS_I2C_CA + tristate "Cortina-Access I2C Controller" + depends on DM_I2C && CORTINA_PLATFORM + default n + help + Say yes here to select Cortina-Access I2C Host Controller. + config SYS_I2C_DAVINCI bool "Davinci I2C Controller" depends on (ARCH_KEYSTONE || ARCH_DAVINCI) diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..5d18cf7 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o +obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o ifdef CONFIG_DM_PCI diff --git a/drivers/i2c/i2c-cortina.c b/drivers/i2c/i2c-cortina.c new file mode 100644 index 0000000..99c63f3 --- /dev/null +++ b/drivers/i2c/i2c-cortina.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 + * Arthur Li, Cortina Access, arthur.li at cortina-access.com. + */ + +#include <common.h> +#include <i2c.h> +#include <asm/io.h> +#include <dm.h> +#include <mapmem.h> +#include "i2c-cortina.h" + +static void set_speed(struct i2c_regs *regs, int i2c_spd) +{ + union ca_biw_cfg i2c_cfg; + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 0; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + + switch (i2c_spd) { + case IC_SPEED_MODE_MAX: + i2c_cfg.bf.prer = + CORTINA_PER_IO_FREQ / (5 * I2C_MAX_SPEED) - 1; + break; + + case IC_SPEED_MODE_STANDARD: + i2c_cfg.bf.prer = + CORTINA_PER_IO_FREQ / (5 * I2C_STANDARD_SPEED) - 1; + break; + + case IC_SPEED_MODE_FAST: + default: + i2c_cfg.bf.prer = + CORTINA_PER_IO_FREQ / (5 * I2C_FAST_SPEED) - 1; + break; + } + + i2c_cfg.bf.core_en = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); +} + +static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int i2c_spd; + + if (speed >= I2C_MAX_SPEED) { + i2c_spd = IC_SPEED_MODE_MAX; + priv->speed = I2C_MAX_SPEED; + } else if (speed >= I2C_FAST_SPEED) { + i2c_spd = IC_SPEED_MODE_FAST; + priv->speed = I2C_FAST_SPEED; + } else { + i2c_spd = IC_SPEED_MODE_STANDARD; + priv->speed = I2C_STANDARD_SPEED; + } + + set_speed(priv->regs, i2c_spd); + + return 0; +} + +static int ca_i2c_get_bus_speed(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + return priv->speed; +} + +static void ca_i2c_init(struct i2c_regs *regs) +{ + union ca_biw_cfg i2c_cfg; + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 0; + i2c_cfg.bf.biw_soft_reset = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + mdelay(10); + i2c_cfg.bf.biw_soft_reset = 0; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + + set_speed(regs, IC_SPEED_MODE_STANDARD); + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); +} + +static int i2c_wait_complete(struct i2c_regs *regs) +{ + union ca_biw_ctrl i2c_ctrl; + unsigned long start_time_bb = get_timer(0); + + i2c_ctrl.wrd = readl(®s->i2c_ctrl); + + while (i2c_ctrl.bf.biwdone == 0) { + i2c_ctrl.wrd = readl(®s->i2c_ctrl); + + if (get_timer(start_time_bb) > + (unsigned long)(I2C_BYTE_TO_BB)) { + printf("%s not done!!!\n", __func__); + return 1; + } + } + + /* Clear done bit */ + writel(i2c_ctrl.wrd, ®s->i2c_ctrl); + + return 0; +} + +static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr, + int write_read) +{ + writel(i2c_addr | write_read, ®s->i2c_txr); + + writel(BIW_CTRL_START | BIW_CTRL_WRITE, + ®s->i2c_ctrl); + + i2c_wait_complete(regs); +} + +static int i2c_wait_for_bus_busy(struct i2c_regs *regs) +{ + union ca_biw_ack i2c_ack; + unsigned long start_time_bb = get_timer(0); + + i2c_ack.wrd = readl(®s->i2c_ack); + + while (i2c_ack.bf.biw_busy) { + i2c_ack.wrd = readl(®s->i2c_ack); + + if (get_timer(start_time_bb) > + (unsigned long)(I2C_BYTE_TO_BB)) { + printf("%s: timeout!\n", __func__); + return 1; + } + } + + return 0; +} + +static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, int write_read) +{ + int addr_len = alen; + + if (i2c_wait_for_bus_busy(regs)) + return 1; + /* First cycle must write addr + offset */ + chip = ((chip & 0x7F) << 1); + if (alen == 0 && write_read == I2C_CMD_RD) + i2c_setaddress(regs, chip, I2C_CMD_RD); + else + i2c_setaddress(regs, chip, I2C_CMD_WT); + + while (alen) { + alen--; + writel(addr, ®s->i2c_txr); + if (write_read == I2C_CMD_RD) + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, + ®s->i2c_ctrl); + else + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); + i2c_wait_complete(regs); + } + + /* Send address again with Read flag if it's read command */ + if (write_read == I2C_CMD_RD && addr_len > 0) + i2c_setaddress(regs, chip, I2C_CMD_RD); + + return 0; +} + +static int i2c_xfer_finish(struct i2c_regs *regs) +{ + /* Dummy read makes bus free */ + writel(BIW_CTRL_READ | BIW_CTRL_STOP, ®s->i2c_ctrl); + i2c_wait_complete(regs); + + if (i2c_wait_for_bus_busy(regs)) { + printf("Timed out waiting for bus\n"); + return 1; + } + + return 0; +} + +static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len) +{ + unsigned long start_time_rx; + int rc = 0; + + if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD)) + return 1; + + start_time_rx = get_timer(0); + while (len) { + /* ACK_IN is ack value to send during read. + * ack high only on the very last byte! + */ + if (len == 1) { + writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP, + ®s->i2c_ctrl); + } else { + writel(BIW_CTRL_READ, ®s->i2c_ctrl); + } + + rc = i2c_wait_complete(regs); + udelay(1); + + if (rc == 0) { + *buffer++ = + (uchar) readl(®s->i2c_rxr); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + return 1; + } + } + i2c_xfer_finish(regs); + return rc; +} + +static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len) +{ + int rc, nb = len; + unsigned long start_time_tx; + + if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT)) + return 1; + + start_time_tx = get_timer(0); + while (len) { + writel(*buffer, ®s->i2c_txr); + if (len == 1) { + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, + ®s->i2c_ctrl); + } else { + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); + } + + rc = i2c_wait_complete(regs); + + if (rc == 0) { + len--; + buffer++; + start_time_tx = get_timer(0); + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + printf("Timed out. i2c write Failed\n"); + return 1; + } + } + + return 0; +} + +static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr, + uint chip_flags) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int ret; + u32 tmp; + + /* Try to read the first location of the chip */ + ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1); + if (ret) + ca_i2c_init(priv->regs); + + return ret; +} + +static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int ret; + + debug("i2c_xfer: %d messages\n", nmsgs); + for (; nmsgs > 0; nmsgs--, msg++) { + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) { + ret = ca_i2c_read(priv->regs, msg->addr, 0, 0, + msg->buf, msg->len); + } else { + ret = ca_i2c_write(priv->regs, msg->addr, 0, 0, + msg->buf, msg->len); + } + + if (ret) { + debug("i2c_write: error sending\n"); + return -EREMOTEIO; + } + } + + return 0; +} + +static const struct dm_i2c_ops ca_i2c_ops = { + .xfer = ca_i2c_xfer, + .probe_chip = ca_i2c_probe_chip, + .set_bus_speed = ca_i2c_set_bus_speed, + .get_bus_speed = ca_i2c_get_bus_speed, +}; + +static const struct udevice_id ca_i2c_ids[] = { + { .compatible = "cortina,ca-i2c", }, + { } +}; + +static int ca_i2c_probe(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + ca_i2c_init(priv->regs); + + return 0; +} + +static int ca_i2c_ofdata_to_platdata(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs)); + if (!priv->regs) { + printf("I2C: base address is invalid\n"); + return -EINVAL; + } + + return 0; +} + +U_BOOT_DRIVER(i2c_cortina) = { + .name = "i2c_cortina", + .id = UCLASS_I2C, + .of_match = ca_i2c_ids, + .ofdata_to_platdata = ca_i2c_ofdata_to_platdata, + .probe = ca_i2c_probe, + .priv_auto_alloc_size = sizeof(struct ca_i2c), + .ops = &ca_i2c_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/i2c/i2c-cortina.h b/drivers/i2c/i2c-cortina.h new file mode 100644 index 0000000..f1c3dc7 --- /dev/null +++ b/drivers/i2c/i2c-cortina.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 + * Cortina Access, <www.cortina-access.com> + */ + +#ifndef __CA_I2C_H_ +#define __CA_I2C_H_ + +#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__) +struct i2c_regs { + u32 i2c_cfg; + u32 i2c_ctrl; + u32 i2c_txr; + u32 i2c_rxr; + u32 i2c_ack; + u32 i2c_ie0; + u32 i2c_int0; + u32 i2c_ie1; + u32 i2c_int1; + u32 i2c_stat; +}; + +union ca_biw_cfg { + struct biw_cfg { + u32 core_en : 1; + u32 biw_soft_reset : 1; + u32 busywait_en : 1; + u32 stretch_en : 1; + u32 arb_en : 1; + u32 clksync_en : 1; + u32 rsrvd1 : 2; + u32 spike_cnt : 4; + u32 rsrvd2 : 4; + u32 prer : 16; + } bf; + unsigned int wrd; +}; + +union ca_biw_ctrl { + struct biw_ctrl { + u32 biwdone : 1; + u32 rsrvd1 : 2; + u32 ack_in : 1; + u32 write : 1; + u32 read : 1; + u32 stop : 1; + u32 start : 1; + u32 rsrvd2 : 24; + } bf; + unsigned int wrd; +}; + +union ca_biw_ack { + struct biw_ack { + u32 al :1; + u32 biw_busy :1; + u32 ack_out :1; + u32 rsrvd1 :29; + } bf; + unsigned int wrd; +}; +#endif /* !__ASSEMBLER__*/ + +struct ca_i2c { + struct i2c_regs *regs; + unsigned int speed; +}; + +#define I2C_CMD_WT 0 +#define I2C_CMD_RD 1 + +#define BIW_CTRL_DONE BIT(0) +#define BIW_CTRL_ACK_IN BIT(3) +#define BIW_CTRL_WRITE BIT(4) +#define BIW_CTRL_READ BIT(5) +#define BIW_CTRL_STOP BIT(6) +#define BIW_CTRL_START BIT(7) + +#define I2C_BYTE_TO (CONFIG_SYS_HZ / 500) +#define I2C_STOPDET_TO (CONFIG_SYS_HZ / 500) +#define I2C_BYTE_TO_BB (10) + +#define IC_SPEED_MODE_STANDARD 1 +#define IC_SPEED_MODE_FAST 2 +#define IC_SPEED_MODE_MAX 3 + +#define I2C_MAX_SPEED 1000000 +#define I2C_FAST_SPEED 400000 +#define I2C_STANDARD_SPEED 100000 + +#endif /* __CA_I2C_H_ */