Message ID | 1591041392-3563-1-git-send-email-alex.nemirovsky@cortina-access.com |
---|---|
State | Superseded |
Headers | show |
Series | [v9,1/2] i2c: i2c-cortina: added CAxxxx I2C support | expand |
Hi Heiko, seems that this patch set may have slipped through the cracks. Is there anything keeping it from being merged into master? > On Jun 1, 2020, at 12:56 PM, Alex Nemirovsky <alex.nemirovsky at cortina-access.com> wrote: > > 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> > Reviewed-by: Heiko Schocher <hs at denx.de> > > --- > > Changes in v9: > - specially include bitops.h and delay.h which > were removed from common.h > > Changes in v8: > - No code change > - Split out individual driver from Cortina Package 2 patch series > to help streamline acceptence into master > > Changes in v7: > - Added additional description info in I2C KConfig > > Changes in v6: > - Add I2C DT binding document > > Changes in v4: > - Utilize standard I2C macros from <i2c.h> > - Return ETIMEDOUT in funcs that can timeout > - Return i2c_xfer_init() result to caller of i2c_read() if it > fails within i2c_read() execution > - Fix misc. style guide conformance issues > - Use printf() to report i2c_xfer() runtime errors > instead of debug() > > MAINTAINERS | 4 + > doc/device-tree-bindings/i2c/i2c-cortina.txt | 18 ++ > drivers/i2c/Kconfig | 8 + > drivers/i2c/Makefile | 1 + > drivers/i2c/i2c-cortina.c | 346 +++++++++++++++++++++++++++ > drivers/i2c/i2c-cortina.h | 87 +++++++ > 6 files changed, 464 insertions(+) > create mode 100644 doc/device-tree-bindings/i2c/i2c-cortina.txt > create mode 100644 drivers/i2c/i2c-cortina.c > create mode 100644 drivers/i2c/i2c-cortina.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 8add9d4..ce70ca9 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> > @@ -732,6 +734,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/doc/device-tree-bindings/i2c/i2c-cortina.txt b/doc/device-tree-bindings/i2c/i2c-cortina.txt > new file mode 100644 > index 0000000..59d5235 > --- /dev/null > +++ b/doc/device-tree-bindings/i2c/i2c-cortina.txt > @@ -0,0 +1,18 @@ > +* I2C for Cortina platforms > + > +Required properties : > +- compatible : Must be "cortina,ca-i2c" > +- reg : Offset and length of the register set for the device > + > +Recommended properties : > +- clock-frequency : desired I2C bus clock frequency in Hz. If not specified, > + default value is 100000. Possible values are 100000, > + 400000 and 1000000. > + > +Examples : > + > + i2c: i2c at f4329120 { > + compatible = "cortina,ca-i2c"; > + reg = <0x0 0xf4329120 0x28>; > + clock-frequency = <400000>; > + }; > diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig > index f8b18de..b56e0d9 100644 > --- a/drivers/i2c/Kconfig > +++ b/drivers/i2c/Kconfig > @@ -93,6 +93,14 @@ 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 > + Add support for the Cortina Access I2C host controller. > + 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 62935b7..d2b07ce 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..08b812a > --- /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_FAST_PLUS: > + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / > + (5 * I2C_SPEED_FAST_PLUS_RATE) - 1; > + break; > + > + case IC_SPEED_MODE_STANDARD: > + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / > + (5 * I2C_SPEED_STANDARD_RATE) - 1; > + break; > + > + case IC_SPEED_MODE_FAST: > + default: > + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / > + (5 * I2C_SPEED_FAST_RATE) - 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_SPEED_FAST_PLUS_RATE) { > + i2c_spd = IC_SPEED_MODE_FAST_PLUS; > + priv->speed = I2C_SPEED_FAST_PLUS_RATE; > + } else if (speed >= I2C_SPEED_FAST_RATE) { > + i2c_spd = IC_SPEED_MODE_FAST; > + priv->speed = I2C_SPEED_FAST_RATE; > + } else { > + i2c_spd = IC_SPEED_MODE_STANDARD; > + priv->speed = I2C_SPEED_STANDARD_RATE; > + } > + > + 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 -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 -ETIMEDOUT; > + } > + } > + > + 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 -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; > + > + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD); > + if (rc) > + return rc; > + > + 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 -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; > + > + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT); > + if (rc) > + return rc; > + > + 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)) { > + 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); > + > + if (ret) { > + printf("i2c_xfer: %s error\n", > + msg->flags & I2C_M_RD ? "read" : "write"); > + return ret; > + } > + } > + > + 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..7e406b5 > --- /dev/null > +++ b/drivers/i2c/i2c-cortina.h > @@ -0,0 +1,87 @@ > +/* SPDX-License-Identifier: GPL-2.0+ */ > +/* > + * (C) Copyright 2019 > + * Cortina Access, <www.cortina-access.com> > + */ > + > +#ifndef __CA_I2C_H_ > +#define __CA_I2C_H_ > + > +#include <linux/bitops.h> > +#include <linux/delay.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) > + > +#endif /* __CA_I2C_H_ */ > -- > 2.7.4 >
Hi Alex, Am 17.06.2020 um 22:09 schrieb Alex Nemirovsky: > Hi Heiko, Sorry for the late reply! > seems that this patch set may have slipped through the cracks. > Is there anything keeping it from being merged into master? No, but we are so late in release cycle, so I planned it for the next merge window! bye, Heiko > >> On Jun 1, 2020, at 12:56 PM, Alex Nemirovsky <alex.nemirovsky at cortina-access.com> wrote: >> >> 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> >> Reviewed-by: Heiko Schocher <hs at denx.de> >> >> --- >> >> Changes in v9: >> - specially include bitops.h and delay.h which >> were removed from common.h >> >> Changes in v8: >> - No code change >> - Split out individual driver from Cortina Package 2 patch series >> to help streamline acceptence into master >> >> Changes in v7: >> - Added additional description info in I2C KConfig >> >> Changes in v6: >> - Add I2C DT binding document >> >> Changes in v4: >> - Utilize standard I2C macros from <i2c.h> >> - Return ETIMEDOUT in funcs that can timeout >> - Return i2c_xfer_init() result to caller of i2c_read() if it >> fails within i2c_read() execution >> - Fix misc. style guide conformance issues >> - Use printf() to report i2c_xfer() runtime errors >> instead of debug() >> >> MAINTAINERS | 4 + >> doc/device-tree-bindings/i2c/i2c-cortina.txt | 18 ++ >> drivers/i2c/Kconfig | 8 + >> drivers/i2c/Makefile | 1 + >> drivers/i2c/i2c-cortina.c | 346 +++++++++++++++++++++++++++ >> drivers/i2c/i2c-cortina.h | 87 +++++++ >> 6 files changed, 464 insertions(+) >> create mode 100644 doc/device-tree-bindings/i2c/i2c-cortina.txt >> create mode 100644 drivers/i2c/i2c-cortina.c >> create mode 100644 drivers/i2c/i2c-cortina.h >> >> diff --git a/MAINTAINERS b/MAINTAINERS >> index 8add9d4..ce70ca9 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> >> @@ -732,6 +734,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/doc/device-tree-bindings/i2c/i2c-cortina.txt b/doc/device-tree-bindings/i2c/i2c-cortina.txt >> new file mode 100644 >> index 0000000..59d5235 >> --- /dev/null >> +++ b/doc/device-tree-bindings/i2c/i2c-cortina.txt >> @@ -0,0 +1,18 @@ >> +* I2C for Cortina platforms >> + >> +Required properties : >> +- compatible : Must be "cortina,ca-i2c" >> +- reg : Offset and length of the register set for the device >> + >> +Recommended properties : >> +- clock-frequency : desired I2C bus clock frequency in Hz. If not specified, >> + default value is 100000. Possible values are 100000, >> + 400000 and 1000000. >> + >> +Examples : >> + >> + i2c: i2c at f4329120 { >> + compatible = "cortina,ca-i2c"; >> + reg = <0x0 0xf4329120 0x28>; >> + clock-frequency = <400000>; >> + }; >> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig >> index f8b18de..b56e0d9 100644 >> --- a/drivers/i2c/Kconfig >> +++ b/drivers/i2c/Kconfig >> @@ -93,6 +93,14 @@ 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 >> + Add support for the Cortina Access I2C host controller. >> + 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 62935b7..d2b07ce 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..08b812a >> --- /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_FAST_PLUS: >> + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / >> + (5 * I2C_SPEED_FAST_PLUS_RATE) - 1; >> + break; >> + >> + case IC_SPEED_MODE_STANDARD: >> + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / >> + (5 * I2C_SPEED_STANDARD_RATE) - 1; >> + break; >> + >> + case IC_SPEED_MODE_FAST: >> + default: >> + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / >> + (5 * I2C_SPEED_FAST_RATE) - 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_SPEED_FAST_PLUS_RATE) { >> + i2c_spd = IC_SPEED_MODE_FAST_PLUS; >> + priv->speed = I2C_SPEED_FAST_PLUS_RATE; >> + } else if (speed >= I2C_SPEED_FAST_RATE) { >> + i2c_spd = IC_SPEED_MODE_FAST; >> + priv->speed = I2C_SPEED_FAST_RATE; >> + } else { >> + i2c_spd = IC_SPEED_MODE_STANDARD; >> + priv->speed = I2C_SPEED_STANDARD_RATE; >> + } >> + >> + 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 -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 -ETIMEDOUT; >> + } >> + } >> + >> + 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 -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; >> + >> + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD); >> + if (rc) >> + return rc; >> + >> + 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 -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; >> + >> + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT); >> + if (rc) >> + return rc; >> + >> + 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)) { >> + 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); >> + >> + if (ret) { >> + printf("i2c_xfer: %s error\n", >> + msg->flags & I2C_M_RD ? "read" : "write"); >> + return ret; >> + } >> + } >> + >> + 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..7e406b5 >> --- /dev/null >> +++ b/drivers/i2c/i2c-cortina.h >> @@ -0,0 +1,87 @@ >> +/* SPDX-License-Identifier: GPL-2.0+ */ >> +/* >> + * (C) Copyright 2019 >> + * Cortina Access, <www.cortina-access.com> >> + */ >> + >> +#ifndef __CA_I2C_H_ >> +#define __CA_I2C_H_ >> + >> +#include <linux/bitops.h> >> +#include <linux/delay.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) >> + >> +#endif /* __CA_I2C_H_ */ >> -- >> 2.7.4 >> >
diff --git a/MAINTAINERS b/MAINTAINERS index 8add9d4..ce70ca9 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> @@ -732,6 +734,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/doc/device-tree-bindings/i2c/i2c-cortina.txt b/doc/device-tree-bindings/i2c/i2c-cortina.txt new file mode 100644 index 0000000..59d5235 --- /dev/null +++ b/doc/device-tree-bindings/i2c/i2c-cortina.txt @@ -0,0 +1,18 @@ +* I2C for Cortina platforms + +Required properties : +- compatible : Must be "cortina,ca-i2c" +- reg : Offset and length of the register set for the device + +Recommended properties : +- clock-frequency : desired I2C bus clock frequency in Hz. If not specified, + default value is 100000. Possible values are 100000, + 400000 and 1000000. + +Examples : + + i2c: i2c at f4329120 { + compatible = "cortina,ca-i2c"; + reg = <0x0 0xf4329120 0x28>; + clock-frequency = <400000>; + }; diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index f8b18de..b56e0d9 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -93,6 +93,14 @@ 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 + Add support for the Cortina Access I2C host controller. + 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 62935b7..d2b07ce 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..08b812a --- /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_FAST_PLUS: + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / + (5 * I2C_SPEED_FAST_PLUS_RATE) - 1; + break; + + case IC_SPEED_MODE_STANDARD: + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / + (5 * I2C_SPEED_STANDARD_RATE) - 1; + break; + + case IC_SPEED_MODE_FAST: + default: + i2c_cfg.bf.prer = CORTINA_PER_IO_FREQ / + (5 * I2C_SPEED_FAST_RATE) - 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_SPEED_FAST_PLUS_RATE) { + i2c_spd = IC_SPEED_MODE_FAST_PLUS; + priv->speed = I2C_SPEED_FAST_PLUS_RATE; + } else if (speed >= I2C_SPEED_FAST_RATE) { + i2c_spd = IC_SPEED_MODE_FAST; + priv->speed = I2C_SPEED_FAST_RATE; + } else { + i2c_spd = IC_SPEED_MODE_STANDARD; + priv->speed = I2C_SPEED_STANDARD_RATE; + } + + 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 -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 -ETIMEDOUT; + } + } + + 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 -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; + + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD); + if (rc) + return rc; + + 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 -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; + + rc = i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT); + if (rc) + return rc; + + 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)) { + 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); + + if (ret) { + printf("i2c_xfer: %s error\n", + msg->flags & I2C_M_RD ? "read" : "write"); + return ret; + } + } + + 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..7e406b5 --- /dev/null +++ b/drivers/i2c/i2c-cortina.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 + * Cortina Access, <www.cortina-access.com> + */ + +#ifndef __CA_I2C_H_ +#define __CA_I2C_H_ + +#include <linux/bitops.h> +#include <linux/delay.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) + +#endif /* __CA_I2C_H_ */