* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
@ 2020-05-14 7:23 Stefan Roese
2020-05-19 6:07 ` Heiko Schocher
` (2 more replies)
0 siblings, 3 replies; 9+ messages in thread
From: Stefan Roese @ 2020-05-14 7:23 UTC (permalink / raw)
To: u-boot
From: Suneel Garapati <sgarapati@marvell.com>
Add support for I2C controllers found on Octeon II/III and Octeon TX
TX2 SoC platforms.
Signed-off-by: Aaron Williams <awilliams@marvell.com>
Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
Cc: Heiko Schocher <hs@denx.de>
Cc: Simon Glass <sjg@chromium.org>
Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
Cc: Aaron Williams <awilliams@marvell.com>
Cc: Chandrakala Chavva <cchavva@marvell.com>
---
RFC -> v1 (Stefan):
- Separated this patch from the OcteonTX/TX2 RFC patch series into a
single patch. This is useful, as the upcoming MIPS Octeon support will
use this I2C driver.
- Added MIPS Octeon II/III support (big endian). Rename driver and its
function names from "octeontx" to "octeon" to better match all Octeon
platforms.
- Moved from union to defines / bitmasks as suggested by Simon. This makes
the driver usage on little- and big-endian platforms much easier.
- Enhanced Kconfig text
- Removed all clock macros (use values from DT)
- Removed long driver debug strings. This is only available when a debug
version of this driver is built. The user / developer can lookup the
descriptive error messages in the driver in this case anyway.
- Removed static "last_id"
- Dropped misc blank lines. Misc reformatting.
- Dropped "!= 0"
- Added missing function comments
- Added missing strut comments
- Changed comment style
- Renames "result" to "ret"
- Hex numbers uppercase
- Minor other changes
- Reword commit text and subject
drivers/i2c/Kconfig | 10 +
drivers/i2c/Makefile | 1 +
drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
3 files changed, 814 insertions(+)
create mode 100644 drivers/i2c/octeon_i2c.c
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index e42b6516bf..1330b36698 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
bus. Devices can be attached to the bus using the device tree
which specifies the driver to use. See sandbox.dts as an example.
+config SYS_I2C_OCTEON
+ bool "Octeon II/III/TX/TX2 I2C driver"
+ depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
+ default y
+ help
+ Add support for the Marvell Octeon I2C driver. This is used with
+ various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
+ chips have several I2C ports and all are provided, controlled by
+ the device tree.
+
config SYS_I2C_S3C24X0
bool "Samsung I2C driver"
depends on ARCH_EXYNOS4 && DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 62935b7ebc..2b58aae892 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
+obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
new file mode 100644
index 0000000000..210f98655e
--- /dev/null
+++ b/drivers/i2c/octeon_i2c.c
@@ -0,0 +1,803 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Marvell International Ltd.
+ */
+
+#include <common.h>
+#include <i2c.h>
+#include <dm.h>
+#include <pci_ids.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <linux/bitfield.h>
+
+/*
+ * Octeon II/III (MIPS) have different register offsets than the ARM based
+ * Octeon TX/TX2 SoCs
+ */
+#if defined(CONFIG_ARCH_OCTEON)
+#define REG_OFFS 0x0000
+#else
+#define REG_OFFS 0x1000
+#endif
+
+#define TWSI_SW_TWSI (REG_OFFS + 0x00)
+#define TWSI_TWSI_SW (REG_OFFS + 0x08)
+#define TWSI_INT (REG_OFFS + 0x10)
+#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
+
+#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
+#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
+#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
+#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
+#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
+#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
+#define TWSI_SW_SOVR BIT_ULL(55)
+#define TWSI_SW_R BIT_ULL(56)
+#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
+#define TWSI_SW_EIA GENMASK_ULL(61)
+#define TWSI_SW_SLONLY BIT_ULL(62)
+#define TWSI_SW_V BIT_ULL(63)
+
+#define TWSI_INT_SDA_OVR BIT_ULL(8)
+#define TWSI_INT_SCL_OVR BIT_ULL(9)
+#define TWSI_INT_SDA BIT_ULL(10)
+#define TWSI_INT_SCL BIT_ULL(11)
+
+enum {
+ TWSI_OP_WRITE = 0,
+ TWSI_OP_READ = 1,
+};
+
+enum {
+ TWSI_EOP_SLAVE_ADDR = 0,
+ TWSI_EOP_CLK_CTL = 3,
+ TWSI_SW_EOP_IA = 6,
+};
+
+enum {
+ TWSI_SLAVEADD = 0,
+ TWSI_DATA = 1,
+ TWSI_CTL = 2,
+ TWSI_CLKCTL = 3,
+ TWSI_STAT = 3,
+ TWSI_SLAVEADD_EXT = 4,
+ TWSI_RST = 7,
+};
+
+enum {
+ TWSI_CTL_AAK = BIT(2),
+ TWSI_CTL_IFLG = BIT(3),
+ TWSI_CTL_STP = BIT(4),
+ TWSI_CTL_STA = BIT(5),
+ TWSI_CTL_ENAB = BIT(6),
+ TWSI_CTL_CE = BIT(7),
+};
+
+/*
+ * Internal errors. When debugging is enabled, the driver will report the
+ * error number and the user / developer can check the table below for the
+ * detailed error description.
+ */
+enum {
+ /** Bus error */
+ TWSI_STAT_BUS_ERROR = 0x00,
+ /** Start condition transmitted */
+ TWSI_STAT_START = 0x08,
+ /** Repeat start condition transmitted */
+ TWSI_STAT_RSTART = 0x10,
+ /** Address + write bit transmitted, ACK received */
+ TWSI_STAT_TXADDR_ACK = 0x18,
+ /** Address + write bit transmitted, /ACK received */
+ TWSI_STAT_TXADDR_NAK = 0x20,
+ /** Data byte transmitted in master mode, ACK received */
+ TWSI_STAT_TXDATA_ACK = 0x28,
+ /** Data byte transmitted in master mode, ACK received */
+ TWSI_STAT_TXDATA_NAK = 0x30,
+ /** Arbitration lost in address or data byte */
+ TWSI_STAT_TX_ARB_LOST = 0x38,
+ /** Address + read bit transmitted, ACK received */
+ TWSI_STAT_RXADDR_ACK = 0x40,
+ /** Address + read bit transmitted, /ACK received */
+ TWSI_STAT_RXADDR_NAK = 0x48,
+ /** Data byte received in master mode, ACK transmitted */
+ TWSI_STAT_RXDATA_ACK_SENT = 0x50,
+ /** Data byte received, NACK transmitted */
+ TWSI_STAT_RXDATA_NAK_SENT = 0x58,
+ /** Slave address received, sent ACK */
+ TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
+ /**
+ * Arbitration lost in address as master, slave address + write bit
+ * received, ACK transmitted
+ */
+ TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
+ /** General call address received, ACK transmitted */
+ TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
+ /**
+ * Arbitration lost in address as master, general call address
+ * received, ACK transmitted
+ */
+ TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
+ /** Data byte received after slave address received, ACK transmitted */
+ TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
+ /** Data byte received after slave address received, /ACK transmitted */
+ TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
+ /**
+ * Data byte received after general call address received, ACK
+ * transmitted
+ */
+ TWSI_STAT_GEN_RXADDR_ACK = 0x90,
+ /**
+ * Data byte received after general call address received, /ACK
+ * transmitted
+ */
+ TWSI_STAT_GEN_RXADDR_NAK = 0x98,
+ /** STOP or repeated START condition received in slave mode */
+ TWSI_STAT_STOP_MULTI_START = 0xa0,
+ /** Slave address + read bit received, ACK transmitted */
+ TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
+ /**
+ * Arbitration lost in address as master, slave address + read bit
+ * received, ACK transmitted
+ */
+ TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
+ /** Data byte transmitted in slave mode, ACK received */
+ TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
+ /** Data byte transmitted in slave mode, /ACK received */
+ TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
+ /** Last byte transmitted in slave mode, ACK received */
+ TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
+ /** Second address byte + write bit transmitted, ACK received */
+ TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
+ /** Second address byte + write bit transmitted, /ACK received */
+ TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
+ /** No relevant status information */
+ TWSI_STAT_IDLE = 0xf8
+};
+
+#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
+# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
+#endif
+
+/**
+ * struct octeon_twsi - Private data of this driver
+ *
+ * @base: Base address of i2c registers
+ */
+struct octeon_twsi {
+ void __iomem *base;
+};
+
+static void twsi_unblock(void *base);
+static int twsi_stop(void *base);
+
+#if defined(CONFIG_ARCH_OCTEON)
+static int get_io_clock(void)
+{
+ return octeon_get_io_clock();
+}
+#else
+static int get_io_clock(void)
+{
+ return octeontx_get_io_clock();
+}
+#endif
+
+static int twsi_thp(void)
+{
+ if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
+ return 24;
+ else
+ return 3;
+}
+
+/**
+ * Returns true if we lost arbitration
+ *
+ * @code status code
+ * @final_read true if this is the final read operation
+ * @return true if arbitration has been lost, false if it hasn't been lost.
+ */
+static int twsi_i2c_lost_arb(u8 code, int final_read)
+{
+ switch (code) {
+ case TWSI_STAT_TX_ARB_LOST:
+ case TWSI_STAT_TX_ACK_ARB_LOST:
+ case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
+ case TWSI_STAT_RXDATA_ACK_ARB_LOST:
+ /* Arbitration lost */
+ return -EAGAIN;
+
+ case TWSI_STAT_SLAVE_RXADDR_ACK:
+ case TWSI_STAT_RX_GEN_ADDR_ACK:
+ case TWSI_STAT_GEN_RXADDR_ACK:
+ case TWSI_STAT_GEN_RXADDR_NAK:
+ /* Being addressed as slave, should back off and listen */
+ return -EIO;
+
+ case TWSI_STAT_SLAVE_RXDATA_ACK:
+ case TWSI_STAT_SLAVE_RXDATA_NAK:
+ case TWSI_STAT_STOP_MULTI_START:
+ case TWSI_STAT_SLAVE_RXADDR2_ACK:
+ case TWSI_STAT_SLAVE_TXDATA_ACK:
+ case TWSI_STAT_SLAVE_TXDATA_NAK:
+ case TWSI_STAT_SLAVE_TXDATA_END_ACK:
+ /* Core busy as slave */
+ return -EIO;
+
+ case TWSI_STAT_RXDATA_ACK_SENT:
+ /* Ack allowed on pre-terminal bytes only */
+ if (!final_read)
+ return 0;
+ return -EAGAIN;
+
+ case TWSI_STAT_RXDATA_NAK_SENT:
+ /* NAK allowed on terminal byte only */
+ if (!final_read)
+ return 0;
+ return -EAGAIN;
+
+ case TWSI_STAT_TXDATA_NAK:
+ case TWSI_STAT_TXADDR_NAK:
+ case TWSI_STAT_RXADDR_NAK:
+ case TWSI_STAT_TXADDR2DATA_NAK:
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+#define RST_BOOT_PNR_MUL(val) (((val) >> 33) & 0x1F)
+
+/**
+ * Writes to the MIO_TWS(0..5)_SW_TWSI register
+ *
+ * @base Base address of i2c registers
+ * @val value to write
+ * @return 0 for success, otherwise error
+ */
+static u64 twsi_write_sw(void __iomem *base, u64 val)
+{
+ unsigned long start = get_timer(0);
+
+ val &= ~TWSI_SW_R;
+ val |= TWSI_SW_V;
+
+ debug("%s(%p, 0x%llx)\n", __func__, base, val);
+ writeq(val, base + TWSI_SW_TWSI);
+ do {
+ val = readq(base + TWSI_SW_TWSI);
+ } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
+
+ if (val & TWSI_SW_V)
+ debug("%s: timed out\n", __func__);
+ return val;
+}
+
+/**
+ * Reads the MIO_TWS(0..5)_SW_TWSI register
+ *
+ * @base Base address of i2c registers
+ * @val value for eia and op, etc. to read
+ * @return value of the register
+ */
+static u64 twsi_read_sw(void __iomem *base, u64 val)
+{
+ unsigned long start = get_timer(0);
+
+ val |= TWSI_SW_R | TWSI_SW_V;
+
+ debug("%s(%p, 0x%llx)\n", __func__, base, val);
+ writeq(val, base + TWSI_SW_TWSI);
+
+ do {
+ val = readq(base + TWSI_SW_TWSI);
+ } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
+
+ if (val & TWSI_SW_V)
+ debug("%s: Error writing 0x%llx\n", __func__, val);
+
+ debug("%s: Returning 0x%llx\n", __func__, val);
+ return val;
+}
+
+/**
+ * Write control register
+ *
+ * @base Base address for i2c registers
+ * @data data to write
+ */
+static void twsi_write_ctl(void __iomem *base, u8 data)
+{
+ u64 val;
+
+ debug("%s(%p, 0x%x)\n", __func__, base, data);
+ val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+ twsi_write_sw(base, val);
+}
+
+/**
+ * Reads the TWSI Control Register
+ *
+ * @base Base address for i2c
+ * @return 8-bit TWSI control register
+ */
+static u8 twsi_read_ctl(void __iomem *base)
+{
+ u64 val;
+
+ val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+ val = twsi_read_sw(base, val);
+
+ debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
+ return (u8)val;
+}
+
+/**
+ * Read i2c status register
+ *
+ * @base Base address of i2c registers
+ * @return value of status register
+ */
+static u8 twsi_read_status(void __iomem *base)
+{
+ u64 val;
+
+ val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+
+ return twsi_read_sw(base, val);
+}
+
+/**
+ * Waits for an i2c operation to complete
+ *
+ * @param base Base address of registers
+ * @return 0 for success, 1 if timeout
+ */
+static int twsi_wait(void __iomem *base)
+{
+ unsigned long start = get_timer(0);
+ u8 twsi_ctl;
+
+ debug("%s(%p)\n", __func__, base);
+ do {
+ twsi_ctl = twsi_read_ctl(base);
+ twsi_ctl &= TWSI_CTL_IFLG;
+ } while (!twsi_ctl && get_timer(start) < 50);
+
+ debug(" return: %u\n", !twsi_ctl);
+ return !twsi_ctl;
+}
+
+/**
+ * Unsticks the i2c bus
+ *
+ * @base base address of registers
+ */
+static int twsi_start_unstick(void __iomem *base)
+{
+ twsi_stop(base);
+ twsi_unblock(base);
+
+ return 0;
+}
+
+/**
+ * Sends an i2c start condition
+ *
+ * @base base address of registers
+ * @return 0 for success, otherwise error
+ */
+static int twsi_start(void __iomem *base)
+{
+ int ret;
+ u8 stat;
+
+ debug("%s(%p)\n", __func__, base);
+ twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
+ ret = twsi_wait(base);
+ if (ret) {
+ stat = twsi_read_status(base);
+ debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
+ switch (stat) {
+ case TWSI_STAT_START:
+ case TWSI_STAT_RSTART:
+ return 0;
+ case TWSI_STAT_RXADDR_ACK:
+ default:
+ return twsi_start_unstick(base);
+ }
+ }
+
+ debug("%s: success\n", __func__);
+ return 0;
+}
+
+/**
+ * Sends an i2c stop condition
+ *
+ * @base register base address
+ * @return 0 for success, -1 if error
+ */
+static int twsi_stop(void __iomem *base)
+{
+ u8 stat;
+
+ twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
+
+ stat = twsi_read_status(base);
+ if (stat != TWSI_STAT_IDLE) {
+ debug("%s: Bad status on bus@%p\n", __func__, base);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Writes data to the i2c bus
+ *
+ * @base register base address
+ * @slave_addr address of slave to write to
+ * @buffer Pointer to buffer to write
+ * @length Number of bytes in buffer to write
+ * @return 0 for success, otherwise error
+ */
+static int twsi_write_data(void __iomem *base, u8 slave_addr,
+ u8 *buffer, unsigned int length)
+{
+ unsigned int curr = 0;
+ u64 val;
+ int ret;
+
+ debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
+ buffer, length);
+ ret = twsi_start(base);
+ if (ret) {
+ debug("%s: Could not start BUS transaction\n", __func__);
+ return -1;
+ }
+
+ ret = twsi_wait(base);
+ if (ret) {
+ debug("%s: wait failed\n", __func__);
+ return ret;
+ }
+
+ val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
+ FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+ twsi_write_sw(base, val);
+ twsi_write_ctl(base, TWSI_CTL_ENAB);
+
+ debug("%s: Waiting\n", __func__);
+ ret = twsi_wait(base);
+ if (ret) {
+ debug("%s: Timed out writing slave address 0x%x to target\n",
+ __func__, slave_addr);
+ return ret;
+ }
+
+ ret = twsi_read_status(base);
+ debug("%s: status: 0x%x\n", __func__, ret);
+ if (ret != TWSI_STAT_TXADDR_ACK) {
+ debug("%s: status: 0x%x\n", __func__, ret);
+ twsi_stop(base);
+ return twsi_i2c_lost_arb(ret, 0);
+ }
+
+ while (curr < length) {
+ val = buffer[curr++] |
+ FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+ twsi_write_sw(base, val);
+ twsi_write_ctl(base, TWSI_CTL_ENAB);
+
+ debug("%s: Writing 0x%llx\n", __func__, val);
+
+ ret = twsi_wait(base);
+ if (ret) {
+ debug("%s: Timed out writing data to 0x%x\n",
+ __func__, slave_addr);
+ return ret;
+ }
+ ret = twsi_read_status(base);
+ debug("%s: status: 0x%x\n", __func__, ret);
+ }
+
+ debug("%s: Stopping\n", __func__);
+ return twsi_stop(base);
+}
+
+/**
+ * Manually clear the I2C bus and send a stop
+ *
+ * @base register base address
+ */
+static void twsi_unblock(void __iomem *base)
+{
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ writeq(0, base + TWSI_INT);
+ udelay(5);
+ writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
+ udelay(5);
+ }
+ writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
+ udelay(5);
+ writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
+ udelay(5);
+ writeq(0, base + TWSI_INT);
+ udelay(5);
+}
+
+/**
+ * Performs a read transaction on the i2c bus
+ *
+ * @base Base address of twsi registers
+ * @slave_addr i2c bus address to read from
+ * @buffer buffer to read into
+ * @length number of bytes to read
+ * @return 0 for success, otherwise error
+ */
+static int twsi_read_data(void __iomem *base, u8 slave_addr,
+ u8 *buffer, unsigned int length)
+{
+ unsigned int curr = 0;
+ u64 val;
+ int ret;
+
+ debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
+ buffer, length);
+ ret = twsi_start(base);
+ if (ret) {
+ debug("%s: start failed\n", __func__);
+ return ret;
+ }
+
+ ret = twsi_wait(base);
+ if (ret) {
+ debug("%s: wait failed\n", __func__);
+ return ret;
+ }
+
+ val = (u32)(slave_addr << 1) | TWSI_OP_READ |
+ FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
+ twsi_write_sw(base, val);
+ twsi_write_ctl(base, TWSI_CTL_ENAB);
+
+ ret = twsi_wait(base);
+ if (ret) {
+ debug("%s: waiting for sending addr failed\n", __func__);
+ return ret;
+ }
+
+ ret = twsi_read_status(base);
+ debug("%s: status: 0x%x\n", __func__, ret);
+ if (ret != TWSI_STAT_RXADDR_ACK) {
+ debug("%s: status: 0x%x\n", __func__, ret);
+ twsi_stop(base);
+ return twsi_i2c_lost_arb(ret, 0);
+ }
+
+ while (curr < length) {
+ twsi_write_ctl(base, TWSI_CTL_ENAB |
+ ((curr < length - 1) ? TWSI_CTL_AAK : 0));
+
+ ret = twsi_wait(base);
+ if (ret) {
+ debug("%s: waiting for data failed\n", __func__);
+ return ret;
+ }
+
+ val = twsi_read_sw(base, val);
+ buffer[curr++] = (u8)val;
+ }
+
+ twsi_stop(base);
+
+ return 0;
+}
+
+/**
+ * Calculate the divisor values
+ *
+ * @speed Speed to set
+ * @m_div Pointer to M divisor
+ * @n_div Pointer to N divisor
+ * @return 0 for success, otherwise error
+ */
+static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
+{
+ int io_clock_hz;
+ int tclk, fsamp;
+ int ndiv, mdiv;
+
+#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
+ io_clock_hz = get_io_clock();
+ tclk = io_clock_hz / (2 * (twsi_thp() + 1));
+#elif defined(CONFIG_ARCH_OCTEONTX2)
+ /* Refclk src in mode register defaults to 100MHz clock */
+ io_clock_hz = 100000000; /* 100 Mhz */
+ tclk = io_clock_hz / (twsi_thp() + 2);
+#endif
+ debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
+
+ /*
+ * Compute the clocks M divider:
+ *
+ * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
+ * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
+ *
+ * For OcteonTX2 -
+ * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
+ * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
+ */
+ for (ndiv = 0; ndiv < 8; ndiv++) {
+ fsamp = tclk / (1 << ndiv);
+ mdiv = fsamp / speed / 10;
+ mdiv -= 1;
+ if (mdiv < 16)
+ break;
+ }
+
+ *m_div = mdiv;
+ *n_div = ndiv;
+}
+
+/**
+ * Init I2C controller
+ *
+ * @base Base address of twsi registers
+ * @slave_addr I2C slave address to configure this controller to
+ * @return 0 for success, otherwise error
+ */
+static int twsi_init(void __iomem *base, int slaveaddr)
+{
+ u64 val;
+
+ debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
+
+ val = slaveaddr << 1 |
+ FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
+ TWSI_SW_V;
+ twsi_write_sw(base, val);
+
+ /* Set slave address */
+ val = slaveaddr |
+ FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
+ TWSI_SW_V;
+ twsi_write_sw(base, val);
+
+ return 0;
+}
+
+/**
+ * Transfers data over the i2c bus
+ *
+ * @bus i2c bus to transfer data over
+ * @msg Array of i2c messages
+ * @nmsgs Number of messages to send/receive
+ * @return 0 for success, otherwise error
+ */
+static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
+ int nmsgs)
+{
+ struct octeon_twsi *twsi = dev_get_priv(bus);
+ int ret;
+ int i;
+
+ debug("%s: %d messages\n", __func__, nmsgs);
+ for (i = 0; i < nmsgs; i++, msg++) {
+ debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
+ msg->len);
+
+ if (msg->flags & I2C_M_RD) {
+ debug("%s: Reading data\n", __func__);
+ ret = twsi_read_data(twsi->base, msg->addr,
+ msg->buf, msg->len);
+ } else {
+ debug("%s: Writing data\n", __func__);
+ ret = twsi_write_data(twsi->base, msg->addr,
+ msg->buf, msg->len);
+ }
+ if (ret) {
+ debug("%s: error sending\n", __func__);
+ return -EREMOTEIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Set I2C bus speed
+ *
+ * @bus i2c bus to transfer data over
+ * @speed Speed in Hz to set
+ * @return 0 for success, otherwise error
+ */
+static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct octeon_twsi *twsi = dev_get_priv(bus);
+ int m_div, n_div;
+ u64 val;
+
+ debug("%s(%p, %u)\n", __func__, bus, speed);
+
+ twsi_calc_div(speed, &m_div, &n_div);
+ if (m_div >= 16)
+ return -1;
+
+ val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
+ FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
+ FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
+ TWSI_SW_V;
+ /* Only init non-slave ports */
+ writeq(val, twsi->base + TWSI_SW_TWSI);
+
+ debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
+ return 0;
+}
+
+/**
+ * Driver probe function
+ *
+ * @dev I2C device to probe
+ * @return 0 for success, otherwise error
+ */
+static int octeon_pci_i2c_probe(struct udevice *dev)
+{
+ struct octeon_twsi *twsi = dev_get_priv(dev);
+#if !defined(CONFIG_ARCH_OCTEON)
+ pci_dev_t bdf = dm_pci_get_bdf(dev);
+
+ debug("TWSI PCI device: %x\n", bdf);
+ dev->req_seq = PCI_FUNC(bdf);
+
+ twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
+ PCI_REGION_MEM);
+#else
+ twsi->base = dev_remap_addr(dev);
+#endif
+ debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
+
+ /* Start with standard speed, real speed set via DT or cmd */
+ return twsi_init(twsi->base, CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
+}
+
+static const struct dm_i2c_ops octeon_i2c_ops = {
+ .xfer = octeon_i2c_xfer,
+ .set_bus_speed = octeon_i2c_set_bus_speed,
+};
+
+static const struct udevice_id octeon_i2c_ids[] = {
+ { .compatible = "cavium,thunder-8890-twsi" },
+ { .compatible = "cavium,octeon-7890-twsi" },
+ { }
+};
+
+U_BOOT_DRIVER(octeon_pci_twsi) = {
+ .name = "i2c_octeon",
+ .id = UCLASS_I2C,
+ .of_match = octeon_i2c_ids,
+ .probe = octeon_pci_i2c_probe,
+ .priv_auto_alloc_size = sizeof(struct octeon_twsi),
+ .ops = &octeon_i2c_ops,
+};
+
+#if !defined(CONFIG_ARCH_OCTEON)
+static struct pci_device_id octeon_twsi_supported[] = {
+ { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI) },
+ { },
+};
+
+U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);
+#endif
--
2.26.2
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-05-14 7:23 [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon Stefan Roese
@ 2020-05-19 6:07 ` Heiko Schocher
2020-05-26 6:49 ` Stefan Roese
2020-05-19 12:32 ` Simon Glass
2020-06-03 5:15 ` Rayagonda Kokatanur
2 siblings, 1 reply; 9+ messages in thread
From: Heiko Schocher @ 2020-05-19 6:07 UTC (permalink / raw)
To: u-boot
Hello Stefan,
Am 14.05.2020 um 09:23 schrieb Stefan Roese:
> From: Suneel Garapati <sgarapati@marvell.com>
>
> Add support for I2C controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
>
> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> Cc: Aaron Williams <awilliams@marvell.com>
> Cc: Chandrakala Chavva <cchavva@marvell.com>
> ---
> RFC -> v1 (Stefan):
> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
> single patch. This is useful, as the upcoming MIPS Octeon support will
> use this I2C driver.
> - Added MIPS Octeon II/III support (big endian). Rename driver and its
> function names from "octeontx" to "octeon" to better match all Octeon
> platforms.
> - Moved from union to defines / bitmasks as suggested by Simon. This makes
> the driver usage on little- and big-endian platforms much easier.
> - Enhanced Kconfig text
> - Removed all clock macros (use values from DT)
> - Removed long driver debug strings. This is only available when a debug
> version of this driver is built. The user / developer can lookup the
> descriptive error messages in the driver in this case anyway.
> - Removed static "last_id"
> - Dropped misc blank lines. Misc reformatting.
> - Dropped "!= 0"
> - Added missing function comments
> - Added missing strut comments
> - Changed comment style
> - Renames "result" to "ret"
> - Hex numbers uppercase
> - Minor other changes
> - Reword commit text and subject
>
> drivers/i2c/Kconfig | 10 +
> drivers/i2c/Makefile | 1 +
> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 814 insertions(+)
> create mode 100644 drivers/i2c/octeon_i2c.c
nitpick only ...
Please add a documentation of the device tree bindings.
[...]
> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
> new file mode 100644
> index 0000000000..210f98655e
> --- /dev/null
> +++ b/drivers/i2c/octeon_i2c.c
> @@ -0,0 +1,803 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Marvell International Ltd.
> + */
> +
> +#include <common.h>
> +#include <i2c.h>
> +#include <dm.h>
> +#include <pci_ids.h>
> +#include <asm/io.h>
> +#include <asm/arch/clock.h>
> +#include <linux/bitfield.h>
> +
> +/*
> + * Octeon II/III (MIPS) have different register offsets than the ARM based
> + * Octeon TX/TX2 SoCs
> + */
> +#if defined(CONFIG_ARCH_OCTEON)
> +#define REG_OFFS 0x0000
> +#else
> +#define REG_OFFS 0x1000
> +#endif
> +
> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
Is there no clearer name ?
Just wonderung about "TWSI" .. we already have an i2c driver with TWSI defines:
https://gitlab.denx.de/u-boot/u-boot/-/blob/master/drivers/i2c/mvtwsi.c
But it seems they have no common parts.
[...]
> +#if defined(CONFIG_ARCH_OCTEON)
> +static int get_io_clock(void)
> +{
> + return octeon_get_io_clock();
> +}
> +#else
> +static int get_io_clock(void)
> +{
> + return octeontx_get_io_clock();
> +}
> +#endif
Here would be good to have the clk framework...
> +
> +static int twsi_thp(void)
> +{
> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
> + return 24;
> + else
> + return 3;
> +}
May you can add here some comments for this magic numbers?
[...]
> +#define RST_BOOT_PNR_MUL(val) (((val) >> 33) & 0x1F)
not used define, please remove.
[...]
bye,
Heiko
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-52 Fax: +49-8142-66989-80 Email: hs at denx.de
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-05-14 7:23 [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon Stefan Roese
2020-05-19 6:07 ` Heiko Schocher
@ 2020-05-19 12:32 ` Simon Glass
2020-05-26 10:55 ` Stefan Roese
2020-06-03 5:15 ` Rayagonda Kokatanur
2 siblings, 1 reply; 9+ messages in thread
From: Simon Glass @ 2020-05-19 12:32 UTC (permalink / raw)
To: u-boot
Hi,
On Thu, 14 May 2020 at 01:23, Stefan Roese <sr@denx.de> wrote:
>
> From: Suneel Garapati <sgarapati@marvell.com>
>
> Add support for I2C controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
>
> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> Cc: Aaron Williams <awilliams@marvell.com>
> Cc: Chandrakala Chavva <cchavva@marvell.com>
> ---
> RFC -> v1 (Stefan):
> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
> single patch. This is useful, as the upcoming MIPS Octeon support will
> use this I2C driver.
> - Added MIPS Octeon II/III support (big endian). Rename driver and its
> function names from "octeontx" to "octeon" to better match all Octeon
> platforms.
> - Moved from union to defines / bitmasks as suggested by Simon. This makes
> the driver usage on little- and big-endian platforms much easier.
> - Enhanced Kconfig text
> - Removed all clock macros (use values from DT)
> - Removed long driver debug strings. This is only available when a debug
> version of this driver is built. The user / developer can lookup the
> descriptive error messages in the driver in this case anyway.
> - Removed static "last_id"
> - Dropped misc blank lines. Misc reformatting.
> - Dropped "!= 0"
> - Added missing function comments
> - Added missing strut comments
> - Changed comment style
> - Renames "result" to "ret"
> - Hex numbers uppercase
> - Minor other changes
> - Reword commit text and subject
>
> drivers/i2c/Kconfig | 10 +
> drivers/i2c/Makefile | 1 +
> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 814 insertions(+)
> create mode 100644 drivers/i2c/octeon_i2c.c
>
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index e42b6516bf..1330b36698 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
> bus. Devices can be attached to the bus using the device tree
> which specifies the driver to use. See sandbox.dts as an example.
>
> +config SYS_I2C_OCTEON
> + bool "Octeon II/III/TX/TX2 I2C driver"
> + depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
> + default y
> + help
> + Add support for the Marvell Octeon I2C driver. This is used with
> + various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
> + chips have several I2C ports and all are provided, controlled by
> + the device tree.
> +
> config SYS_I2C_S3C24X0
> bool "Samsung I2C driver"
> depends on ARCH_EXYNOS4 && DM_I2C
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index 62935b7ebc..2b58aae892 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
> obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
> obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
> obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
> +obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
> obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
> obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
> obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
> new file mode 100644
> index 0000000000..210f98655e
> --- /dev/null
> +++ b/drivers/i2c/octeon_i2c.c
> @@ -0,0 +1,803 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Marvell International Ltd.
> + */
> +
> +#include <common.h>
> +#include <i2c.h>
> +#include <dm.h>
> +#include <pci_ids.h>
> +#include <asm/io.h>
> +#include <asm/arch/clock.h>
> +#include <linux/bitfield.h>
> +
> +/*
> + * Octeon II/III (MIPS) have different register offsets than the ARM based
> + * Octeon TX/TX2 SoCs
> + */
> +#if defined(CONFIG_ARCH_OCTEON)
> +#define REG_OFFS 0x0000
> +#else
> +#define REG_OFFS 0x1000
> +#endif
> +
> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
> +#define TWSI_INT (REG_OFFS + 0x10)
> +#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
> +
> +#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
> +#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
> +#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
> +#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
> +#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
> +#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
> +#define TWSI_SW_SOVR BIT_ULL(55)
> +#define TWSI_SW_R BIT_ULL(56)
> +#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
> +#define TWSI_SW_EIA GENMASK_ULL(61)
> +#define TWSI_SW_SLONLY BIT_ULL(62)
> +#define TWSI_SW_V BIT_ULL(63)
> +
> +#define TWSI_INT_SDA_OVR BIT_ULL(8)
> +#define TWSI_INT_SCL_OVR BIT_ULL(9)
> +#define TWSI_INT_SDA BIT_ULL(10)
> +#define TWSI_INT_SCL BIT_ULL(11)
> +
> +enum {
> + TWSI_OP_WRITE = 0,
> + TWSI_OP_READ = 1,
> +};
> +
> +enum {
> + TWSI_EOP_SLAVE_ADDR = 0,
> + TWSI_EOP_CLK_CTL = 3,
> + TWSI_SW_EOP_IA = 6,
> +};
> +
> +enum {
> + TWSI_SLAVEADD = 0,
> + TWSI_DATA = 1,
> + TWSI_CTL = 2,
> + TWSI_CLKCTL = 3,
> + TWSI_STAT = 3,
> + TWSI_SLAVEADD_EXT = 4,
> + TWSI_RST = 7,
> +};
> +
> +enum {
> + TWSI_CTL_AAK = BIT(2),
> + TWSI_CTL_IFLG = BIT(3),
> + TWSI_CTL_STP = BIT(4),
> + TWSI_CTL_STA = BIT(5),
> + TWSI_CTL_ENAB = BIT(6),
> + TWSI_CTL_CE = BIT(7),
> +};
> +
> +/*
> + * Internal errors. When debugging is enabled, the driver will report the
> + * error number and the user / developer can check the table below for the
> + * detailed error description.
> + */
> +enum {
> + /** Bus error */
> + TWSI_STAT_BUS_ERROR = 0x00,
> + /** Start condition transmitted */
> + TWSI_STAT_START = 0x08,
> + /** Repeat start condition transmitted */
> + TWSI_STAT_RSTART = 0x10,
> + /** Address + write bit transmitted, ACK received */
> + TWSI_STAT_TXADDR_ACK = 0x18,
> + /** Address + write bit transmitted, /ACK received */
> + TWSI_STAT_TXADDR_NAK = 0x20,
> + /** Data byte transmitted in master mode, ACK received */
> + TWSI_STAT_TXDATA_ACK = 0x28,
> + /** Data byte transmitted in master mode, ACK received */
> + TWSI_STAT_TXDATA_NAK = 0x30,
> + /** Arbitration lost in address or data byte */
> + TWSI_STAT_TX_ARB_LOST = 0x38,
> + /** Address + read bit transmitted, ACK received */
> + TWSI_STAT_RXADDR_ACK = 0x40,
> + /** Address + read bit transmitted, /ACK received */
> + TWSI_STAT_RXADDR_NAK = 0x48,
> + /** Data byte received in master mode, ACK transmitted */
> + TWSI_STAT_RXDATA_ACK_SENT = 0x50,
> + /** Data byte received, NACK transmitted */
> + TWSI_STAT_RXDATA_NAK_SENT = 0x58,
> + /** Slave address received, sent ACK */
> + TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
> + /**
> + * Arbitration lost in address as master, slave address + write bit
> + * received, ACK transmitted
> + */
> + TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
> + /** General call address received, ACK transmitted */
> + TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
> + /**
> + * Arbitration lost in address as master, general call address
> + * received, ACK transmitted
> + */
> + TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
> + /** Data byte received after slave address received, ACK transmitted */
> + TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
> + /** Data byte received after slave address received, /ACK transmitted */
> + TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
> + /**
> + * Data byte received after general call address received, ACK
> + * transmitted
> + */
> + TWSI_STAT_GEN_RXADDR_ACK = 0x90,
> + /**
> + * Data byte received after general call address received, /ACK
> + * transmitted
> + */
> + TWSI_STAT_GEN_RXADDR_NAK = 0x98,
> + /** STOP or repeated START condition received in slave mode */
> + TWSI_STAT_STOP_MULTI_START = 0xa0,
> + /** Slave address + read bit received, ACK transmitted */
> + TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
> + /**
> + * Arbitration lost in address as master, slave address + read bit
> + * received, ACK transmitted
> + */
> + TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
> + /** Data byte transmitted in slave mode, ACK received */
> + TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
> + /** Data byte transmitted in slave mode, /ACK received */
> + TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
> + /** Last byte transmitted in slave mode, ACK received */
> + TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
> + /** Second address byte + write bit transmitted, ACK received */
> + TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
> + /** Second address byte + write bit transmitted, /ACK received */
> + TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
> + /** No relevant status information */
> + TWSI_STAT_IDLE = 0xf8
> +};
> +
> +#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
> +# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
This should be a device-tree property.
> +#endif
> +
> +/**
> + * struct octeon_twsi - Private data of this driver
> + *
> + * @base: Base address of i2c registers
> + */
> +struct octeon_twsi {
> + void __iomem *base;
> +};
> +
> +static void twsi_unblock(void *base);
> +static int twsi_stop(void *base);
> +
> +#if defined(CONFIG_ARCH_OCTEON)
Needs a clock driver.
> +static int get_io_clock(void)
> +{
> + return octeon_get_io_clock();
> +}
> +#else
> +static int get_io_clock(void)
> +{
> + return octeontx_get_io_clock();
> +}
> +#endif
> +
> +static int twsi_thp(void)
> +{
> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
> + return 24;
> + else
> + return 3;
> +}
> +
[..]
> +/**
> + * Calculate the divisor values
> + *
> + * @speed Speed to set
> + * @m_div Pointer to M divisor
> + * @n_div Pointer to N divisor
> + * @return 0 for success, otherwise error
> + */
> +static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
> +{
> + int io_clock_hz;
> + int tclk, fsamp;
> + int ndiv, mdiv;
> +
> +#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
> + io_clock_hz = get_io_clock();
> + tclk = io_clock_hz / (2 * (twsi_thp() + 1));
> +#elif defined(CONFIG_ARCH_OCTEONTX2)
> + /* Refclk src in mode register defaults to 100MHz clock */
> + io_clock_hz = 100000000; /* 100 Mhz */
> + tclk = io_clock_hz / (twsi_thp() + 2);
> +#endif
Needs a clock driver. If different logic is needed it should use the
compatible string / driver data rather than #ifdefs.
> + debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
> +
> + /*
> + * Compute the clocks M divider:
> + *
> + * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
> + * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
> + *
> + * For OcteonTX2 -
> + * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
> + * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
> + */
> + for (ndiv = 0; ndiv < 8; ndiv++) {
> + fsamp = tclk / (1 << ndiv);
> + mdiv = fsamp / speed / 10;
> + mdiv -= 1;
> + if (mdiv < 16)
> + break;
> + }
> +
> + *m_div = mdiv;
> + *n_div = ndiv;
> +}
> +
> +/**
[..]
> +/**
> + * Driver probe function
> + *
> + * @dev I2C device to probe
> + * @return 0 for success, otherwise error
> + */
> +static int octeon_pci_i2c_probe(struct udevice *dev)
> +{
> + struct octeon_twsi *twsi = dev_get_priv(dev);
> +#if !defined(CONFIG_ARCH_OCTEON)
Again should use dev_get_driver_data()
> + pci_dev_t bdf = dm_pci_get_bdf(dev);
> +
> + debug("TWSI PCI device: %x\n", bdf);
> + dev->req_seq = PCI_FUNC(bdf);
> +
> + twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> + PCI_REGION_MEM);
> +#else
> + twsi->base = dev_remap_addr(dev);
> +#endif
> + debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
> +
> + /* Start with standard speed, real speed set via DT or cmd */
> + return twsi_init(twsi->base, CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
> +}
> +
> +static const struct dm_i2c_ops octeon_i2c_ops = {
> + .xfer = octeon_i2c_xfer,
> + .set_bus_speed = octeon_i2c_set_bus_speed,
> +};
> +
> +static const struct udevice_id octeon_i2c_ids[] = {
> + { .compatible = "cavium,thunder-8890-twsi" },
> + { .compatible = "cavium,octeon-7890-twsi" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(octeon_pci_twsi) = {
> + .name = "i2c_octeon",
> + .id = UCLASS_I2C,
> + .of_match = octeon_i2c_ids,
> + .probe = octeon_pci_i2c_probe,
> + .priv_auto_alloc_size = sizeof(struct octeon_twsi),
> + .ops = &octeon_i2c_ops,
> +};
> +
> +#if !defined(CONFIG_ARCH_OCTEON)
> +static struct pci_device_id octeon_twsi_supported[] = {
> + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI) },
> + { },
> +};
> +
> +U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);
> +#endif
> --
> 2.26.2
>
Regards,
Simon
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-05-19 6:07 ` Heiko Schocher
@ 2020-05-26 6:49 ` Stefan Roese
0 siblings, 0 replies; 9+ messages in thread
From: Stefan Roese @ 2020-05-26 6:49 UTC (permalink / raw)
To: u-boot
Hi Heiko,
On 19.05.20 08:07, Heiko Schocher wrote:
> Hello Stefan,
>
> Am 14.05.2020 um 09:23 schrieb Stefan Roese:
>> From: Suneel Garapati <sgarapati@marvell.com>
>>
>> Add support for I2C controllers found on Octeon II/III and Octeon TX
>> TX2 SoC platforms.
>>
>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Heiko Schocher <hs@denx.de>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>> Cc: Aaron Williams <awilliams@marvell.com>
>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>> ---
>> RFC -> v1 (Stefan):
>> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
>> ?? single patch. This is useful, as the upcoming MIPS Octeon support will
>> ?? use this I2C driver.
>> - Added MIPS Octeon II/III support (big endian). Rename driver and its
>> ?? function names from "octeontx" to "octeon" to better match all Octeon
>> ?? platforms.
>> - Moved from union to defines / bitmasks as suggested by Simon. This
>> makes
>> ?? the driver usage on little- and big-endian platforms much easier.
>> - Enhanced Kconfig text
>> - Removed all clock macros (use values from DT)
>> - Removed long driver debug strings. This is only available when a debug
>> ?? version of this driver is built. The user / developer can lookup the
>> ?? descriptive error messages in the driver in this case anyway.
>> - Removed static "last_id"
>> - Dropped misc blank lines. Misc reformatting.
>> - Dropped "!= 0"
>> - Added missing function comments
>> - Added missing strut comments
>> - Changed comment style
>> - Renames "result" to "ret"
>> - Hex numbers uppercase
>> - Minor other changes
>> - Reword commit text and subject
>>
>> ? drivers/i2c/Kconfig????? |? 10 +
>> ? drivers/i2c/Makefile???? |?? 1 +
>> ? drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
>> ? 3 files changed, 814 insertions(+)
>> ? create mode 100644 drivers/i2c/octeon_i2c.c
>
> nitpick only ...
>
> Please add a documentation of the device tree bindings.
Okay.
> [...]
>> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
>> new file mode 100644
>> index 0000000000..210f98655e
>> --- /dev/null
>> +++ b/drivers/i2c/octeon_i2c.c
>> @@ -0,0 +1,803 @@
>> +// SPDX-License-Identifier:??? GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Marvell International Ltd.
>> + */
>> +
>> +#include <common.h>
>> +#include <i2c.h>
>> +#include <dm.h>
>> +#include <pci_ids.h>
>> +#include <asm/io.h>
>> +#include <asm/arch/clock.h>
>> +#include <linux/bitfield.h>
>> +
>> +/*
>> + * Octeon II/III (MIPS) have different register offsets than the ARM
>> based
>> + * Octeon TX/TX2 SoCs
>> + */
>> +#if defined(CONFIG_ARCH_OCTEON)
>> +#define REG_OFFS??????? 0x0000
>> +#else
>> +#define REG_OFFS??????? 0x1000
>> +#endif
>> +
>> +#define TWSI_SW_TWSI??????? (REG_OFFS + 0x00)
>> +#define TWSI_TWSI_SW??????? (REG_OFFS + 0x08)
>
> Is there no clearer name ?
>
> Just wonderung about "TWSI" .. we already have an i2c driver with TWSI
> defines:
>
> https://gitlab.denx.de/u-boot/u-boot/-/blob/master/drivers/i2c/mvtwsi.c
>
> But it seems they have no common parts.
TWSI is another abbreviation for I2C: Two Wire Serial Interface. I
would like to keep these macros as they reflect the register names in
the Cavium / Marvell manuals.
> [...]
>> +#if defined(CONFIG_ARCH_OCTEON)
>> +static int get_io_clock(void)
>> +{
>> +??? return octeon_get_io_clock();
>> +}
>> +#else
>> +static int get_io_clock(void)
>> +{
>> +??? return octeontx_get_io_clock();
>> +}
>> +#endif
>
> Here would be good to have the clk framework...
Yes, it would be great. I'm thinking about adding a minimal clk driver
for Octeon - not sure, if I get it done shortly though.
>> +
>> +static int twsi_thp(void)
>> +{
>> +??? if (IS_ENABLED(CONFIG_ARCH_OCTEON) ||
>> IS_ENABLED(CONFIG_ARCH_OCTEONTX))
>> +??????? return 24;
>> +??? else
>> +??????? return 3;
>> +}
>
> May you can add here some comments for this magic numbers?
I didn't write the initial version of this driver, so its hard for
me to come up with details here. But I'll try.
> [...]
>> +#define RST_BOOT_PNR_MUL(val)? (((val) >> 33) & 0x1F)
>
> not used define, please remove.
Ah, thanks for spotting.
Thanks,
Stefan
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-05-19 12:32 ` Simon Glass
@ 2020-05-26 10:55 ` Stefan Roese
0 siblings, 0 replies; 9+ messages in thread
From: Stefan Roese @ 2020-05-26 10:55 UTC (permalink / raw)
To: u-boot
Hi Simon,
On 19.05.20 14:32, Simon Glass wrote:
> Hi,
>
> On Thu, 14 May 2020 at 01:23, Stefan Roese <sr@denx.de> wrote:
>>
>> From: Suneel Garapati <sgarapati@marvell.com>
>>
>> Add support for I2C controllers found on Octeon II/III and Octeon TX
>> TX2 SoC platforms.
>>
>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Heiko Schocher <hs@denx.de>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>> Cc: Aaron Williams <awilliams@marvell.com>
>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>> ---
>> RFC -> v1 (Stefan):
>> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
>> single patch. This is useful, as the upcoming MIPS Octeon support will
>> use this I2C driver.
>> - Added MIPS Octeon II/III support (big endian). Rename driver and its
>> function names from "octeontx" to "octeon" to better match all Octeon
>> platforms.
>> - Moved from union to defines / bitmasks as suggested by Simon. This makes
>> the driver usage on little- and big-endian platforms much easier.
>> - Enhanced Kconfig text
>> - Removed all clock macros (use values from DT)
>> - Removed long driver debug strings. This is only available when a debug
>> version of this driver is built. The user / developer can lookup the
>> descriptive error messages in the driver in this case anyway.
>> - Removed static "last_id"
>> - Dropped misc blank lines. Misc reformatting.
>> - Dropped "!= 0"
>> - Added missing function comments
>> - Added missing strut comments
>> - Changed comment style
>> - Renames "result" to "ret"
>> - Hex numbers uppercase
>> - Minor other changes
>> - Reword commit text and subject
>>
>> drivers/i2c/Kconfig | 10 +
>> drivers/i2c/Makefile | 1 +
>> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 814 insertions(+)
>> create mode 100644 drivers/i2c/octeon_i2c.c
>>
>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>> index e42b6516bf..1330b36698 100644
>> --- a/drivers/i2c/Kconfig
>> +++ b/drivers/i2c/Kconfig
>> @@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
>> bus. Devices can be attached to the bus using the device tree
>> which specifies the driver to use. See sandbox.dts as an example.
>>
>> +config SYS_I2C_OCTEON
>> + bool "Octeon II/III/TX/TX2 I2C driver"
>> + depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
>> + default y
>> + help
>> + Add support for the Marvell Octeon I2C driver. This is used with
>> + various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
>> + chips have several I2C ports and all are provided, controlled by
>> + the device tree.
>> +
>> config SYS_I2C_S3C24X0
>> bool "Samsung I2C driver"
>> depends on ARCH_EXYNOS4 && DM_I2C
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index 62935b7ebc..2b58aae892 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
>> obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
>> obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
>> obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
>> +obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
>> obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
>> obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
>> obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
>> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
>> new file mode 100644
>> index 0000000000..210f98655e
>> --- /dev/null
>> +++ b/drivers/i2c/octeon_i2c.c
>> @@ -0,0 +1,803 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Marvell International Ltd.
>> + */
>> +
>> +#include <common.h>
>> +#include <i2c.h>
>> +#include <dm.h>
>> +#include <pci_ids.h>
>> +#include <asm/io.h>
>> +#include <asm/arch/clock.h>
>> +#include <linux/bitfield.h>
>> +
>> +/*
>> + * Octeon II/III (MIPS) have different register offsets than the ARM based
>> + * Octeon TX/TX2 SoCs
>> + */
>> +#if defined(CONFIG_ARCH_OCTEON)
>> +#define REG_OFFS 0x0000
>> +#else
>> +#define REG_OFFS 0x1000
>> +#endif
>> +
>> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
>> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
>> +#define TWSI_INT (REG_OFFS + 0x10)
>> +#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
>> +
>> +#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
>> +#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
>> +#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
>> +#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
>> +#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
>> +#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
>> +#define TWSI_SW_SOVR BIT_ULL(55)
>> +#define TWSI_SW_R BIT_ULL(56)
>> +#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
>> +#define TWSI_SW_EIA GENMASK_ULL(61)
>> +#define TWSI_SW_SLONLY BIT_ULL(62)
>> +#define TWSI_SW_V BIT_ULL(63)
>> +
>> +#define TWSI_INT_SDA_OVR BIT_ULL(8)
>> +#define TWSI_INT_SCL_OVR BIT_ULL(9)
>> +#define TWSI_INT_SDA BIT_ULL(10)
>> +#define TWSI_INT_SCL BIT_ULL(11)
>> +
>> +enum {
>> + TWSI_OP_WRITE = 0,
>> + TWSI_OP_READ = 1,
>> +};
>> +
>> +enum {
>> + TWSI_EOP_SLAVE_ADDR = 0,
>> + TWSI_EOP_CLK_CTL = 3,
>> + TWSI_SW_EOP_IA = 6,
>> +};
>> +
>> +enum {
>> + TWSI_SLAVEADD = 0,
>> + TWSI_DATA = 1,
>> + TWSI_CTL = 2,
>> + TWSI_CLKCTL = 3,
>> + TWSI_STAT = 3,
>> + TWSI_SLAVEADD_EXT = 4,
>> + TWSI_RST = 7,
>> +};
>> +
>> +enum {
>> + TWSI_CTL_AAK = BIT(2),
>> + TWSI_CTL_IFLG = BIT(3),
>> + TWSI_CTL_STP = BIT(4),
>> + TWSI_CTL_STA = BIT(5),
>> + TWSI_CTL_ENAB = BIT(6),
>> + TWSI_CTL_CE = BIT(7),
>> +};
>> +
>> +/*
>> + * Internal errors. When debugging is enabled, the driver will report the
>> + * error number and the user / developer can check the table below for the
>> + * detailed error description.
>> + */
>> +enum {
>> + /** Bus error */
>> + TWSI_STAT_BUS_ERROR = 0x00,
>> + /** Start condition transmitted */
>> + TWSI_STAT_START = 0x08,
>> + /** Repeat start condition transmitted */
>> + TWSI_STAT_RSTART = 0x10,
>> + /** Address + write bit transmitted, ACK received */
>> + TWSI_STAT_TXADDR_ACK = 0x18,
>> + /** Address + write bit transmitted, /ACK received */
>> + TWSI_STAT_TXADDR_NAK = 0x20,
>> + /** Data byte transmitted in master mode, ACK received */
>> + TWSI_STAT_TXDATA_ACK = 0x28,
>> + /** Data byte transmitted in master mode, ACK received */
>> + TWSI_STAT_TXDATA_NAK = 0x30,
>> + /** Arbitration lost in address or data byte */
>> + TWSI_STAT_TX_ARB_LOST = 0x38,
>> + /** Address + read bit transmitted, ACK received */
>> + TWSI_STAT_RXADDR_ACK = 0x40,
>> + /** Address + read bit transmitted, /ACK received */
>> + TWSI_STAT_RXADDR_NAK = 0x48,
>> + /** Data byte received in master mode, ACK transmitted */
>> + TWSI_STAT_RXDATA_ACK_SENT = 0x50,
>> + /** Data byte received, NACK transmitted */
>> + TWSI_STAT_RXDATA_NAK_SENT = 0x58,
>> + /** Slave address received, sent ACK */
>> + TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
>> + /**
>> + * Arbitration lost in address as master, slave address + write bit
>> + * received, ACK transmitted
>> + */
>> + TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
>> + /** General call address received, ACK transmitted */
>> + TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
>> + /**
>> + * Arbitration lost in address as master, general call address
>> + * received, ACK transmitted
>> + */
>> + TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
>> + /** Data byte received after slave address received, ACK transmitted */
>> + TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
>> + /** Data byte received after slave address received, /ACK transmitted */
>> + TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
>> + /**
>> + * Data byte received after general call address received, ACK
>> + * transmitted
>> + */
>> + TWSI_STAT_GEN_RXADDR_ACK = 0x90,
>> + /**
>> + * Data byte received after general call address received, /ACK
>> + * transmitted
>> + */
>> + TWSI_STAT_GEN_RXADDR_NAK = 0x98,
>> + /** STOP or repeated START condition received in slave mode */
>> + TWSI_STAT_STOP_MULTI_START = 0xa0,
>> + /** Slave address + read bit received, ACK transmitted */
>> + TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
>> + /**
>> + * Arbitration lost in address as master, slave address + read bit
>> + * received, ACK transmitted
>> + */
>> + TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
>> + /** Data byte transmitted in slave mode, ACK received */
>> + TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
>> + /** Data byte transmitted in slave mode, /ACK received */
>> + TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
>> + /** Last byte transmitted in slave mode, ACK received */
>> + TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
>> + /** Second address byte + write bit transmitted, ACK received */
>> + TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
>> + /** Second address byte + write bit transmitted, /ACK received */
>> + TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
>> + /** No relevant status information */
>> + TWSI_STAT_IDLE = 0xf8
>> +};
>> +
>> +#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
>> +# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
>
> This should be a device-tree property.
Okay, will move to DT.
>> +#endif
>> +
>> +/**
>> + * struct octeon_twsi - Private data of this driver
>> + *
>> + * @base: Base address of i2c registers
>> + */
>> +struct octeon_twsi {
>> + void __iomem *base;
>> +};
>> +
>> +static void twsi_unblock(void *base);
>> +static int twsi_stop(void *base);
>> +
>> +#if defined(CONFIG_ARCH_OCTEON)
>
> Needs a clock driver.
Right. I'll try to add clock driver support with the first Octeon base
port.
>> +static int get_io_clock(void)
>> +{
>> + return octeon_get_io_clock();
>> +}
>> +#else
>> +static int get_io_clock(void)
>> +{
>> + return octeontx_get_io_clock();
>> +}
>> +#endif
>> +
>> +static int twsi_thp(void)
>> +{
>> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
>> + return 24;
>> + else
>> + return 3;
>> +}
>> +
>
> [..]
>
>> +/**
>> + * Calculate the divisor values
>> + *
>> + * @speed Speed to set
>> + * @m_div Pointer to M divisor
>> + * @n_div Pointer to N divisor
>> + * @return 0 for success, otherwise error
>> + */
>> +static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
>> +{
>> + int io_clock_hz;
>> + int tclk, fsamp;
>> + int ndiv, mdiv;
>> +
>> +#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
>> + io_clock_hz = get_io_clock();
>> + tclk = io_clock_hz / (2 * (twsi_thp() + 1));
>> +#elif defined(CONFIG_ARCH_OCTEONTX2)
>> + /* Refclk src in mode register defaults to 100MHz clock */
>> + io_clock_hz = 100000000; /* 100 Mhz */
>> + tclk = io_clock_hz / (twsi_thp() + 2);
>> +#endif
>
> Needs a clock driver. If different logic is needed it should use the
> compatible string / driver data rather than #ifdefs.
Yes, will update.
>> + debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
>> +
>> + /*
>> + * Compute the clocks M divider:
>> + *
>> + * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
>> + * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
>> + *
>> + * For OcteonTX2 -
>> + * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
>> + * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
>> + */
>> + for (ndiv = 0; ndiv < 8; ndiv++) {
>> + fsamp = tclk / (1 << ndiv);
>> + mdiv = fsamp / speed / 10;
>> + mdiv -= 1;
>> + if (mdiv < 16)
>> + break;
>> + }
>> +
>> + *m_div = mdiv;
>> + *n_div = ndiv;
>> +}
>> +
>> +/**
>
> [..]
>
>> +/**
>> + * Driver probe function
>> + *
>> + * @dev I2C device to probe
>> + * @return 0 for success, otherwise error
>> + */
>> +static int octeon_pci_i2c_probe(struct udevice *dev)
>> +{
>> + struct octeon_twsi *twsi = dev_get_priv(dev);
>> +#if !defined(CONFIG_ARCH_OCTEON)
>
> Again should use dev_get_driver_data()
Yes, will update in v2.
Thanks,
Stefan
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-05-14 7:23 [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon Stefan Roese
2020-05-19 6:07 ` Heiko Schocher
2020-05-19 12:32 ` Simon Glass
@ 2020-06-03 5:15 ` Rayagonda Kokatanur
2020-06-03 5:20 ` Stefan Roese
2 siblings, 1 reply; 9+ messages in thread
From: Rayagonda Kokatanur @ 2020-06-03 5:15 UTC (permalink / raw)
To: u-boot
Hi Stefan,
Few minor comments,
On Thu, May 14, 2020 at 12:53 PM Stefan Roese <sr@denx.de> wrote:
>
> From: Suneel Garapati <sgarapati@marvell.com>
>
> Add support for I2C controllers found on Octeon II/III and Octeon TX
> TX2 SoC platforms.
>
> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> Signed-off-by: Stefan Roese <sr@denx.de>
> Cc: Heiko Schocher <hs@denx.de>
> Cc: Simon Glass <sjg@chromium.org>
> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> Cc: Aaron Williams <awilliams@marvell.com>
> Cc: Chandrakala Chavva <cchavva@marvell.com>
> ---
> RFC -> v1 (Stefan):
> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
> single patch. This is useful, as the upcoming MIPS Octeon support will
> use this I2C driver.
> - Added MIPS Octeon II/III support (big endian). Rename driver and its
> function names from "octeontx" to "octeon" to better match all Octeon
> platforms.
> - Moved from union to defines / bitmasks as suggested by Simon. This makes
> the driver usage on little- and big-endian platforms much easier.
> - Enhanced Kconfig text
> - Removed all clock macros (use values from DT)
> - Removed long driver debug strings. This is only available when a debug
> version of this driver is built. The user / developer can lookup the
> descriptive error messages in the driver in this case anyway.
> - Removed static "last_id"
> - Dropped misc blank lines. Misc reformatting.
> - Dropped "!= 0"
> - Added missing function comments
> - Added missing strut comments
> - Changed comment style
> - Renames "result" to "ret"
> - Hex numbers uppercase
> - Minor other changes
> - Reword commit text and subject
>
> drivers/i2c/Kconfig | 10 +
> drivers/i2c/Makefile | 1 +
> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 814 insertions(+)
> create mode 100644 drivers/i2c/octeon_i2c.c
>
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index e42b6516bf..1330b36698 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
> bus. Devices can be attached to the bus using the device tree
> which specifies the driver to use. See sandbox.dts as an example.
>
> +config SYS_I2C_OCTEON
> + bool "Octeon II/III/TX/TX2 I2C driver"
> + depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
> + default y
> + help
> + Add support for the Marvell Octeon I2C driver. This is used with
> + various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
> + chips have several I2C ports and all are provided, controlled by
> + the device tree.
> +
> config SYS_I2C_S3C24X0
> bool "Samsung I2C driver"
> depends on ARCH_EXYNOS4 && DM_I2C
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index 62935b7ebc..2b58aae892 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
> obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
> obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
> obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
> +obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
> obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
> obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
> obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
> new file mode 100644
> index 0000000000..210f98655e
> --- /dev/null
> +++ b/drivers/i2c/octeon_i2c.c
> @@ -0,0 +1,803 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2018 Marvell International Ltd.
Should it be 2020 ?
> + */
> +
> +#include <common.h>
> +#include <i2c.h>
> +#include <dm.h>
> +#include <pci_ids.h>
> +#include <asm/io.h>
> +#include <asm/arch/clock.h>
> +#include <linux/bitfield.h>
> +
> +/*
> + * Octeon II/III (MIPS) have different register offsets than the ARM based
> + * Octeon TX/TX2 SoCs
> + */
> +#if defined(CONFIG_ARCH_OCTEON)
> +#define REG_OFFS 0x0000
> +#else
> +#define REG_OFFS 0x1000
> +#endif
How about getting this offset from dt ?
> +
> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
> +#define TWSI_INT (REG_OFFS + 0x10)
> +#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
> +
> +#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
> +#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
> +#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
> +#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
> +#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
> +#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
> +#define TWSI_SW_SOVR BIT_ULL(55)
> +#define TWSI_SW_R BIT_ULL(56)
> +#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
> +#define TWSI_SW_EIA GENMASK_ULL(61)
> +#define TWSI_SW_SLONLY BIT_ULL(62)
> +#define TWSI_SW_V BIT_ULL(63)
> +
> +#define TWSI_INT_SDA_OVR BIT_ULL(8)
> +#define TWSI_INT_SCL_OVR BIT_ULL(9)
> +#define TWSI_INT_SDA BIT_ULL(10)
> +#define TWSI_INT_SCL BIT_ULL(11)
> +
> +enum {
> + TWSI_OP_WRITE = 0,
> + TWSI_OP_READ = 1,
> +};
> +
> +enum {
> + TWSI_EOP_SLAVE_ADDR = 0,
> + TWSI_EOP_CLK_CTL = 3,
> + TWSI_SW_EOP_IA = 6,
> +};
> +
> +enum {
> + TWSI_SLAVEADD = 0,
> + TWSI_DATA = 1,
> + TWSI_CTL = 2,
> + TWSI_CLKCTL = 3,
> + TWSI_STAT = 3,
> + TWSI_SLAVEADD_EXT = 4,
> + TWSI_RST = 7,
> +};
> +
> +enum {
> + TWSI_CTL_AAK = BIT(2),
> + TWSI_CTL_IFLG = BIT(3),
> + TWSI_CTL_STP = BIT(4),
> + TWSI_CTL_STA = BIT(5),
> + TWSI_CTL_ENAB = BIT(6),
> + TWSI_CTL_CE = BIT(7),
> +};
> +
> +/*
> + * Internal errors. When debugging is enabled, the driver will report the
> + * error number and the user / developer can check the table below for the
> + * detailed error description.
> + */
> +enum {
> + /** Bus error */
> + TWSI_STAT_BUS_ERROR = 0x00,
> + /** Start condition transmitted */
> + TWSI_STAT_START = 0x08,
> + /** Repeat start condition transmitted */
> + TWSI_STAT_RSTART = 0x10,
> + /** Address + write bit transmitted, ACK received */
> + TWSI_STAT_TXADDR_ACK = 0x18,
> + /** Address + write bit transmitted, /ACK received */
> + TWSI_STAT_TXADDR_NAK = 0x20,
> + /** Data byte transmitted in master mode, ACK received */
> + TWSI_STAT_TXDATA_ACK = 0x28,
> + /** Data byte transmitted in master mode, ACK received */
> + TWSI_STAT_TXDATA_NAK = 0x30,
> + /** Arbitration lost in address or data byte */
> + TWSI_STAT_TX_ARB_LOST = 0x38,
> + /** Address + read bit transmitted, ACK received */
> + TWSI_STAT_RXADDR_ACK = 0x40,
> + /** Address + read bit transmitted, /ACK received */
> + TWSI_STAT_RXADDR_NAK = 0x48,
> + /** Data byte received in master mode, ACK transmitted */
> + TWSI_STAT_RXDATA_ACK_SENT = 0x50,
> + /** Data byte received, NACK transmitted */
> + TWSI_STAT_RXDATA_NAK_SENT = 0x58,
> + /** Slave address received, sent ACK */
> + TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
> + /**
> + * Arbitration lost in address as master, slave address + write bit
> + * received, ACK transmitted
> + */
> + TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
> + /** General call address received, ACK transmitted */
> + TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
> + /**
> + * Arbitration lost in address as master, general call address
> + * received, ACK transmitted
> + */
> + TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
> + /** Data byte received after slave address received, ACK transmitted */
> + TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
> + /** Data byte received after slave address received, /ACK transmitted */
> + TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
> + /**
> + * Data byte received after general call address received, ACK
> + * transmitted
> + */
> + TWSI_STAT_GEN_RXADDR_ACK = 0x90,
> + /**
> + * Data byte received after general call address received, /ACK
> + * transmitted
> + */
> + TWSI_STAT_GEN_RXADDR_NAK = 0x98,
> + /** STOP or repeated START condition received in slave mode */
> + TWSI_STAT_STOP_MULTI_START = 0xa0,
> + /** Slave address + read bit received, ACK transmitted */
> + TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
> + /**
> + * Arbitration lost in address as master, slave address + read bit
> + * received, ACK transmitted
> + */
> + TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
> + /** Data byte transmitted in slave mode, ACK received */
> + TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
> + /** Data byte transmitted in slave mode, /ACK received */
> + TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
> + /** Last byte transmitted in slave mode, ACK received */
> + TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
> + /** Second address byte + write bit transmitted, ACK received */
> + TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
> + /** Second address byte + write bit transmitted, /ACK received */
> + TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
> + /** No relevant status information */
> + TWSI_STAT_IDLE = 0xf8
> +};
> +
> +#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
> +# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
> +#endif
> +
> +/**
> + * struct octeon_twsi - Private data of this driver
> + *
> + * @base: Base address of i2c registers
> + */
> +struct octeon_twsi {
> + void __iomem *base;
> +};
> +
> +static void twsi_unblock(void *base);
> +static int twsi_stop(void *base);
> +
> +#if defined(CONFIG_ARCH_OCTEON)
> +static int get_io_clock(void)
> +{
> + return octeon_get_io_clock();
> +}
> +#else
> +static int get_io_clock(void)
> +{
> + return octeontx_get_io_clock();
> +}
> +#endif
> +
> +static int twsi_thp(void)
> +{
> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
> + return 24;
> + else
> + return 3;
> +}
> +
> +/**
> + * Returns true if we lost arbitration
> + *
> + * @code status code
> + * @final_read true if this is the final read operation
> + * @return true if arbitration has been lost, false if it hasn't been lost.
Instead of true/false, better to mention 0 on success and -ve on failure.
> + */
> +static int twsi_i2c_lost_arb(u8 code, int final_read)
> +{
> + switch (code) {
> + case TWSI_STAT_TX_ARB_LOST:
> + case TWSI_STAT_TX_ACK_ARB_LOST:
> + case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
> + case TWSI_STAT_RXDATA_ACK_ARB_LOST:
> + /* Arbitration lost */
> + return -EAGAIN;
> +
> + case TWSI_STAT_SLAVE_RXADDR_ACK:
> + case TWSI_STAT_RX_GEN_ADDR_ACK:
> + case TWSI_STAT_GEN_RXADDR_ACK:
> + case TWSI_STAT_GEN_RXADDR_NAK:
> + /* Being addressed as slave, should back off and listen */
> + return -EIO;
> +
> + case TWSI_STAT_SLAVE_RXDATA_ACK:
> + case TWSI_STAT_SLAVE_RXDATA_NAK:
> + case TWSI_STAT_STOP_MULTI_START:
> + case TWSI_STAT_SLAVE_RXADDR2_ACK:
> + case TWSI_STAT_SLAVE_TXDATA_ACK:
> + case TWSI_STAT_SLAVE_TXDATA_NAK:
> + case TWSI_STAT_SLAVE_TXDATA_END_ACK:
> + /* Core busy as slave */
> + return -EIO;
> +
> + case TWSI_STAT_RXDATA_ACK_SENT:
> + /* Ack allowed on pre-terminal bytes only */
> + if (!final_read)
> + return 0;
> + return -EAGAIN;
> +
> + case TWSI_STAT_RXDATA_NAK_SENT:
> + /* NAK allowed on terminal byte only */
> + if (!final_read)
> + return 0;
> + return -EAGAIN;
> +
> + case TWSI_STAT_TXDATA_NAK:
> + case TWSI_STAT_TXADDR_NAK:
> + case TWSI_STAT_RXADDR_NAK:
> + case TWSI_STAT_TXADDR2DATA_NAK:
> + return -EAGAIN;
> + }
> +
> + return 0;
> +}
> +
> +#define RST_BOOT_PNR_MUL(val) (((val) >> 33) & 0x1F)
Better to move this up where all macros are defined.
> +
> +/**
> + * Writes to the MIO_TWS(0..5)_SW_TWSI register
> + *
> + * @base Base address of i2c registers
> + * @val value to write
> + * @return 0 for success, otherwise error
> + */
> +static u64 twsi_write_sw(void __iomem *base, u64 val)
> +{
> + unsigned long start = get_timer(0);
> +
> + val &= ~TWSI_SW_R;
> + val |= TWSI_SW_V;
> +
> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
> + writeq(val, base + TWSI_SW_TWSI);
> + do {
> + val = readq(base + TWSI_SW_TWSI);
> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
> +
> + if (val & TWSI_SW_V)
> + debug("%s: timed out\n", __func__);
> + return val;
newline before return, please fix globally.
> +}
> +
> +/**
> + * Reads the MIO_TWS(0..5)_SW_TWSI register
> + *
> + * @base Base address of i2c registers
> + * @val value for eia and op, etc. to read
> + * @return value of the register
> + */
> +static u64 twsi_read_sw(void __iomem *base, u64 val)
> +{
> + unsigned long start = get_timer(0);
> +
> + val |= TWSI_SW_R | TWSI_SW_V;
> +
> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
> + writeq(val, base + TWSI_SW_TWSI);
> +
> + do {
> + val = readq(base + TWSI_SW_TWSI);
> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
> +
> + if (val & TWSI_SW_V)
> + debug("%s: Error writing 0x%llx\n", __func__, val);
> +
> + debug("%s: Returning 0x%llx\n", __func__, val);
> + return val;
> +}
> +
> +/**
> + * Write control register
> + *
> + * @base Base address for i2c registers
> + * @data data to write
> + */
> +static void twsi_write_ctl(void __iomem *base, u8 data)
> +{
> + u64 val;
> +
> + debug("%s(%p, 0x%x)\n", __func__, base, data);
> + val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> + twsi_write_sw(base, val);
> +}
> +
> +/**
> + * Reads the TWSI Control Register
> + *
> + * @base Base address for i2c
> + * @return 8-bit TWSI control register
> + */
> +static u8 twsi_read_ctl(void __iomem *base)
> +{
> + u64 val;
> +
> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> + val = twsi_read_sw(base, val);
> +
> + debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
> + return (u8)val;
> +}
> +
> +/**
> + * Read i2c status register
> + *
> + * @base Base address of i2c registers
> + * @return value of status register
> + */
> +static u8 twsi_read_status(void __iomem *base)
> +{
> + u64 val;
> +
> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> +
> + return twsi_read_sw(base, val);
> +}
> +
> +/**
> + * Waits for an i2c operation to complete
> + *
> + * @param base Base address of registers
> + * @return 0 for success, 1 if timeout
> + */
> +static int twsi_wait(void __iomem *base)
> +{
> + unsigned long start = get_timer(0);
> + u8 twsi_ctl;
> +
> + debug("%s(%p)\n", __func__, base);
> + do {
> + twsi_ctl = twsi_read_ctl(base);
> + twsi_ctl &= TWSI_CTL_IFLG;
> + } while (!twsi_ctl && get_timer(start) < 50);
> +
> + debug(" return: %u\n", !twsi_ctl);
> + return !twsi_ctl;
> +}
> +
> +/**
> + * Unsticks the i2c bus
> + *
> + * @base base address of registers
> + */
> +static int twsi_start_unstick(void __iomem *base)
> +{
> + twsi_stop(base);
> + twsi_unblock(base);
> +
> + return 0;
> +}
> +
> +/**
> + * Sends an i2c start condition
> + *
> + * @base base address of registers
> + * @return 0 for success, otherwise error
> + */
> +static int twsi_start(void __iomem *base)
> +{
> + int ret;
> + u8 stat;
> +
> + debug("%s(%p)\n", __func__, base);
> + twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
> + ret = twsi_wait(base);
> + if (ret) {
> + stat = twsi_read_status(base);
> + debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
> + switch (stat) {
> + case TWSI_STAT_START:
> + case TWSI_STAT_RSTART:
> + return 0;
> + case TWSI_STAT_RXADDR_ACK:
> + default:
> + return twsi_start_unstick(base);
> + }
> + }
> +
> + debug("%s: success\n", __func__);
> + return 0;
> +}
> +
> +/**
> + * Sends an i2c stop condition
> + *
> + * @base register base address
> + * @return 0 for success, -1 if error
> + */
> +static int twsi_stop(void __iomem *base)
> +{
> + u8 stat;
> +
> + twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
> +
> + stat = twsi_read_status(base);
> + if (stat != TWSI_STAT_IDLE) {
> + debug("%s: Bad status on bus@%p\n", __func__, base);
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * Writes data to the i2c bus
> + *
> + * @base register base address
> + * @slave_addr address of slave to write to
> + * @buffer Pointer to buffer to write
> + * @length Number of bytes in buffer to write
> + * @return 0 for success, otherwise error
> + */
> +static int twsi_write_data(void __iomem *base, u8 slave_addr,
> + u8 *buffer, unsigned int length)
This should be properly aligned.
> +{
> + unsigned int curr = 0;
> + u64 val;
> + int ret;
> +
> + debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
> + buffer, length);
same alignment.
> + ret = twsi_start(base);
> + if (ret) {
> + debug("%s: Could not start BUS transaction\n", __func__);
> + return -1;
> + }
> +
> + ret = twsi_wait(base);
> + if (ret) {
> + debug("%s: wait failed\n", __func__);
> + return ret;
> + }
> +
> + val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> + twsi_write_sw(base, val);
> + twsi_write_ctl(base, TWSI_CTL_ENAB);
> +
> + debug("%s: Waiting\n", __func__);
> + ret = twsi_wait(base);
> + if (ret) {
> + debug("%s: Timed out writing slave address 0x%x to target\n",
> + __func__, slave_addr);
> + return ret;
> + }
> +
> + ret = twsi_read_status(base);
> + debug("%s: status: 0x%x\n", __func__, ret);
> + if (ret != TWSI_STAT_TXADDR_ACK) {
> + debug("%s: status: 0x%x\n", __func__, ret);
> + twsi_stop(base);
> + return twsi_i2c_lost_arb(ret, 0);
> + }
> +
> + while (curr < length) {
> + val = buffer[curr++] |
> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> + twsi_write_sw(base, val);
> + twsi_write_ctl(base, TWSI_CTL_ENAB);
> +
> + debug("%s: Writing 0x%llx\n", __func__, val);
> +
> + ret = twsi_wait(base);
> + if (ret) {
> + debug("%s: Timed out writing data to 0x%x\n",
> + __func__, slave_addr);
> + return ret;
> + }
> + ret = twsi_read_status(base);
> + debug("%s: status: 0x%x\n", __func__, ret);
> + }
> +
> + debug("%s: Stopping\n", __func__);
> + return twsi_stop(base);
> +}
> +
> +/**
> + * Manually clear the I2C bus and send a stop
> + *
> + * @base register base address
> + */
> +static void twsi_unblock(void __iomem *base)
> +{
> + int i;
> +
> + for (i = 0; i < 9; i++) {
> + writeq(0, base + TWSI_INT);
> + udelay(5);
> + writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
> + udelay(5);
> + }
> + writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
> + udelay(5);
> + writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
> + udelay(5);
> + writeq(0, base + TWSI_INT);
> + udelay(5);
> +}
> +
> +/**
> + * Performs a read transaction on the i2c bus
> + *
> + * @base Base address of twsi registers
> + * @slave_addr i2c bus address to read from
> + * @buffer buffer to read into
> + * @length number of bytes to read
> + * @return 0 for success, otherwise error
> + */
> +static int twsi_read_data(void __iomem *base, u8 slave_addr,
> + u8 *buffer, unsigned int length)
> +{
> + unsigned int curr = 0;
> + u64 val;
> + int ret;
> +
> + debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
> + buffer, length);
> + ret = twsi_start(base);
> + if (ret) {
> + debug("%s: start failed\n", __func__);
> + return ret;
> + }
> +
> + ret = twsi_wait(base);
> + if (ret) {
> + debug("%s: wait failed\n", __func__);
> + return ret;
> + }
> +
> + val = (u32)(slave_addr << 1) | TWSI_OP_READ |
> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> + twsi_write_sw(base, val);
> + twsi_write_ctl(base, TWSI_CTL_ENAB);
> +
> + ret = twsi_wait(base);
> + if (ret) {
> + debug("%s: waiting for sending addr failed\n", __func__);
> + return ret;
> + }
> +
> + ret = twsi_read_status(base);
> + debug("%s: status: 0x%x\n", __func__, ret);
> + if (ret != TWSI_STAT_RXADDR_ACK) {
> + debug("%s: status: 0x%x\n", __func__, ret);
> + twsi_stop(base);
> + return twsi_i2c_lost_arb(ret, 0);
> + }
> +
> + while (curr < length) {
> + twsi_write_ctl(base, TWSI_CTL_ENAB |
> + ((curr < length - 1) ? TWSI_CTL_AAK : 0));
> +
> + ret = twsi_wait(base);
> + if (ret) {
> + debug("%s: waiting for data failed\n", __func__);
> + return ret;
> + }
> +
> + val = twsi_read_sw(base, val);
> + buffer[curr++] = (u8)val;
> + }
> +
> + twsi_stop(base);
> +
> + return 0;
> +}
> +
> +/**
> + * Calculate the divisor values
> + *
> + * @speed Speed to set
> + * @m_div Pointer to M divisor
> + * @n_div Pointer to N divisor
> + * @return 0 for success, otherwise error
> + */
> +static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
> +{
> + int io_clock_hz;
> + int tclk, fsamp;
> + int ndiv, mdiv;
> +
> +#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
> + io_clock_hz = get_io_clock();
> + tclk = io_clock_hz / (2 * (twsi_thp() + 1));
> +#elif defined(CONFIG_ARCH_OCTEONTX2)
> + /* Refclk src in mode register defaults to 100MHz clock */
> + io_clock_hz = 100000000; /* 100 Mhz */
> + tclk = io_clock_hz / (twsi_thp() + 2);
> +#endif
> + debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
> +
> + /*
> + * Compute the clocks M divider:
> + *
> + * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
> + * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
> + *
> + * For OcteonTX2 -
> + * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
> + * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
> + */
> + for (ndiv = 0; ndiv < 8; ndiv++) {
> + fsamp = tclk / (1 << ndiv);
> + mdiv = fsamp / speed / 10;
> + mdiv -= 1;
> + if (mdiv < 16)
> + break;
> + }
> +
> + *m_div = mdiv;
> + *n_div = ndiv;
> +}
> +
> +/**
> + * Init I2C controller
> + *
> + * @base Base address of twsi registers
> + * @slave_addr I2C slave address to configure this controller to
> + * @return 0 for success, otherwise error
> + */
> +static int twsi_init(void __iomem *base, int slaveaddr)
> +{
> + u64 val;
> +
> + debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
> +
> + val = slaveaddr << 1 |
> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
> + TWSI_SW_V;
> + twsi_write_sw(base, val);
> +
> + /* Set slave address */
> + val = slaveaddr |
> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
> + TWSI_SW_V;
> + twsi_write_sw(base, val);
> +
> + return 0;
> +}
> +
> +/**
> + * Transfers data over the i2c bus
> + *
> + * @bus i2c bus to transfer data over
> + * @msg Array of i2c messages
> + * @nmsgs Number of messages to send/receive
> + * @return 0 for success, otherwise error
> + */
> +static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
> + int nmsgs)
> +{
> + struct octeon_twsi *twsi = dev_get_priv(bus);
> + int ret;
> + int i;
> +
> + debug("%s: %d messages\n", __func__, nmsgs);
> + for (i = 0; i < nmsgs; i++, msg++) {
> + debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
> + msg->len);
> +
> + if (msg->flags & I2C_M_RD) {
> + debug("%s: Reading data\n", __func__);
> + ret = twsi_read_data(twsi->base, msg->addr,
> + msg->buf, msg->len);
> + } else {
> + debug("%s: Writing data\n", __func__);
> + ret = twsi_write_data(twsi->base, msg->addr,
> + msg->buf, msg->len);
> + }
> + if (ret) {
> + debug("%s: error sending\n", __func__);
> + return -EREMOTEIO;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * Set I2C bus speed
> + *
> + * @bus i2c bus to transfer data over
> + * @speed Speed in Hz to set
> + * @return 0 for success, otherwise error
> + */
> +static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
> +{
> + struct octeon_twsi *twsi = dev_get_priv(bus);
> + int m_div, n_div;
> + u64 val;
> +
> + debug("%s(%p, %u)\n", __func__, bus, speed);
> +
> + twsi_calc_div(speed, &m_div, &n_div);
> + if (m_div >= 16)
> + return -1;
> +
> + val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
> + TWSI_SW_V;
> + /* Only init non-slave ports */
> + writeq(val, twsi->base + TWSI_SW_TWSI);
> +
> + debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
> + return 0;
> +}
> +
> +/**
> + * Driver probe function
> + *
> + * @dev I2C device to probe
> + * @return 0 for success, otherwise error
> + */
> +static int octeon_pci_i2c_probe(struct udevice *dev)
> +{
> + struct octeon_twsi *twsi = dev_get_priv(dev);
> +#if !defined(CONFIG_ARCH_OCTEON)
> + pci_dev_t bdf = dm_pci_get_bdf(dev);
> +
> + debug("TWSI PCI device: %x\n", bdf);
> + dev->req_seq = PCI_FUNC(bdf);
> +
> + twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> + PCI_REGION_MEM);
> +#else
> + twsi->base = dev_remap_addr(dev);
> +#endif
> + debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
> +
> + /* Start with standard speed, real speed set via DT or cmd */
> + return twsi_init(twsi->base, CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
> +}
> +
> +static const struct dm_i2c_ops octeon_i2c_ops = {
> + .xfer = octeon_i2c_xfer,
> + .set_bus_speed = octeon_i2c_set_bus_speed,
> +};
> +
> +static const struct udevice_id octeon_i2c_ids[] = {
> + { .compatible = "cavium,thunder-8890-twsi" },
> + { .compatible = "cavium,octeon-7890-twsi" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(octeon_pci_twsi) = {
> + .name = "i2c_octeon",
> + .id = UCLASS_I2C,
> + .of_match = octeon_i2c_ids,
> + .probe = octeon_pci_i2c_probe,
> + .priv_auto_alloc_size = sizeof(struct octeon_twsi),
> + .ops = &octeon_i2c_ops,
> +};
> +
> +#if !defined(CONFIG_ARCH_OCTEON)
> +static struct pci_device_id octeon_twsi_supported[] = {
> + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI) },
> + { },
> +};
> +
> +U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);
> +#endif
> --
> 2.26.2
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-06-03 5:15 ` Rayagonda Kokatanur
@ 2020-06-03 5:20 ` Stefan Roese
2020-06-03 5:37 ` Rayagonda Kokatanur
0 siblings, 1 reply; 9+ messages in thread
From: Stefan Roese @ 2020-06-03 5:20 UTC (permalink / raw)
To: u-boot
Hi Rayagonda,
v1 is superseeded. Please review the latest version, v2:
https://patchwork.ozlabs.org/project/uboot/patch/20200526121307.2735-1-sr at denx.de/
Thanks,
Stefan
On 03.06.20 07:15, Rayagonda Kokatanur wrote:
> Hi Stefan,
>
> Few minor comments,
>
> On Thu, May 14, 2020 at 12:53 PM Stefan Roese <sr@denx.de> wrote:
>>
>> From: Suneel Garapati <sgarapati@marvell.com>
>>
>> Add support for I2C controllers found on Octeon II/III and Octeon TX
>> TX2 SoC platforms.
>>
>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>> Signed-off-by: Stefan Roese <sr@denx.de>
>> Cc: Heiko Schocher <hs@denx.de>
>> Cc: Simon Glass <sjg@chromium.org>
>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>> Cc: Aaron Williams <awilliams@marvell.com>
>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>> ---
>> RFC -> v1 (Stefan):
>> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
>> single patch. This is useful, as the upcoming MIPS Octeon support will
>> use this I2C driver.
>> - Added MIPS Octeon II/III support (big endian). Rename driver and its
>> function names from "octeontx" to "octeon" to better match all Octeon
>> platforms.
>> - Moved from union to defines / bitmasks as suggested by Simon. This makes
>> the driver usage on little- and big-endian platforms much easier.
>> - Enhanced Kconfig text
>> - Removed all clock macros (use values from DT)
>> - Removed long driver debug strings. This is only available when a debug
>> version of this driver is built. The user / developer can lookup the
>> descriptive error messages in the driver in this case anyway.
>> - Removed static "last_id"
>> - Dropped misc blank lines. Misc reformatting.
>> - Dropped "!= 0"
>> - Added missing function comments
>> - Added missing strut comments
>> - Changed comment style
>> - Renames "result" to "ret"
>> - Hex numbers uppercase
>> - Minor other changes
>> - Reword commit text and subject
>>
>> drivers/i2c/Kconfig | 10 +
>> drivers/i2c/Makefile | 1 +
>> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 814 insertions(+)
>> create mode 100644 drivers/i2c/octeon_i2c.c
>>
>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>> index e42b6516bf..1330b36698 100644
>> --- a/drivers/i2c/Kconfig
>> +++ b/drivers/i2c/Kconfig
>> @@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
>> bus. Devices can be attached to the bus using the device tree
>> which specifies the driver to use. See sandbox.dts as an example.
>>
>> +config SYS_I2C_OCTEON
>> + bool "Octeon II/III/TX/TX2 I2C driver"
>> + depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
>> + default y
>> + help
>> + Add support for the Marvell Octeon I2C driver. This is used with
>> + various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
>> + chips have several I2C ports and all are provided, controlled by
>> + the device tree.
>> +
>> config SYS_I2C_S3C24X0
>> bool "Samsung I2C driver"
>> depends on ARCH_EXYNOS4 && DM_I2C
>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>> index 62935b7ebc..2b58aae892 100644
>> --- a/drivers/i2c/Makefile
>> +++ b/drivers/i2c/Makefile
>> @@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
>> obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
>> obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
>> obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
>> +obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
>> obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
>> obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
>> obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
>> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
>> new file mode 100644
>> index 0000000000..210f98655e
>> --- /dev/null
>> +++ b/drivers/i2c/octeon_i2c.c
>> @@ -0,0 +1,803 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (C) 2018 Marvell International Ltd.
>
> Should it be 2020 ?
>> + */
>> +
>> +#include <common.h>
>> +#include <i2c.h>
>> +#include <dm.h>
>> +#include <pci_ids.h>
>> +#include <asm/io.h>
>> +#include <asm/arch/clock.h>
>> +#include <linux/bitfield.h>
>> +
>> +/*
>> + * Octeon II/III (MIPS) have different register offsets than the ARM based
>> + * Octeon TX/TX2 SoCs
>> + */
>> +#if defined(CONFIG_ARCH_OCTEON)
>> +#define REG_OFFS 0x0000
>> +#else
>> +#define REG_OFFS 0x1000
>> +#endif
>
> How about getting this offset from dt ?
>> +
>> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
>> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
>> +#define TWSI_INT (REG_OFFS + 0x10)
>> +#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
>> +
>> +#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
>> +#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
>> +#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
>> +#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
>> +#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
>> +#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
>> +#define TWSI_SW_SOVR BIT_ULL(55)
>> +#define TWSI_SW_R BIT_ULL(56)
>> +#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
>> +#define TWSI_SW_EIA GENMASK_ULL(61)
>> +#define TWSI_SW_SLONLY BIT_ULL(62)
>> +#define TWSI_SW_V BIT_ULL(63)
>> +
>> +#define TWSI_INT_SDA_OVR BIT_ULL(8)
>> +#define TWSI_INT_SCL_OVR BIT_ULL(9)
>> +#define TWSI_INT_SDA BIT_ULL(10)
>> +#define TWSI_INT_SCL BIT_ULL(11)
>> +
>> +enum {
>> + TWSI_OP_WRITE = 0,
>> + TWSI_OP_READ = 1,
>> +};
>> +
>> +enum {
>> + TWSI_EOP_SLAVE_ADDR = 0,
>> + TWSI_EOP_CLK_CTL = 3,
>> + TWSI_SW_EOP_IA = 6,
>> +};
>> +
>> +enum {
>> + TWSI_SLAVEADD = 0,
>> + TWSI_DATA = 1,
>> + TWSI_CTL = 2,
>> + TWSI_CLKCTL = 3,
>> + TWSI_STAT = 3,
>> + TWSI_SLAVEADD_EXT = 4,
>> + TWSI_RST = 7,
>> +};
>> +
>> +enum {
>> + TWSI_CTL_AAK = BIT(2),
>> + TWSI_CTL_IFLG = BIT(3),
>> + TWSI_CTL_STP = BIT(4),
>> + TWSI_CTL_STA = BIT(5),
>> + TWSI_CTL_ENAB = BIT(6),
>> + TWSI_CTL_CE = BIT(7),
>> +};
>> +
>> +/*
>> + * Internal errors. When debugging is enabled, the driver will report the
>> + * error number and the user / developer can check the table below for the
>> + * detailed error description.
>> + */
>> +enum {
>> + /** Bus error */
>> + TWSI_STAT_BUS_ERROR = 0x00,
>> + /** Start condition transmitted */
>> + TWSI_STAT_START = 0x08,
>> + /** Repeat start condition transmitted */
>> + TWSI_STAT_RSTART = 0x10,
>> + /** Address + write bit transmitted, ACK received */
>> + TWSI_STAT_TXADDR_ACK = 0x18,
>> + /** Address + write bit transmitted, /ACK received */
>> + TWSI_STAT_TXADDR_NAK = 0x20,
>> + /** Data byte transmitted in master mode, ACK received */
>> + TWSI_STAT_TXDATA_ACK = 0x28,
>> + /** Data byte transmitted in master mode, ACK received */
>> + TWSI_STAT_TXDATA_NAK = 0x30,
>> + /** Arbitration lost in address or data byte */
>> + TWSI_STAT_TX_ARB_LOST = 0x38,
>> + /** Address + read bit transmitted, ACK received */
>> + TWSI_STAT_RXADDR_ACK = 0x40,
>> + /** Address + read bit transmitted, /ACK received */
>> + TWSI_STAT_RXADDR_NAK = 0x48,
>> + /** Data byte received in master mode, ACK transmitted */
>> + TWSI_STAT_RXDATA_ACK_SENT = 0x50,
>> + /** Data byte received, NACK transmitted */
>> + TWSI_STAT_RXDATA_NAK_SENT = 0x58,
>> + /** Slave address received, sent ACK */
>> + TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
>> + /**
>> + * Arbitration lost in address as master, slave address + write bit
>> + * received, ACK transmitted
>> + */
>> + TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
>> + /** General call address received, ACK transmitted */
>> + TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
>> + /**
>> + * Arbitration lost in address as master, general call address
>> + * received, ACK transmitted
>> + */
>> + TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
>> + /** Data byte received after slave address received, ACK transmitted */
>> + TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
>> + /** Data byte received after slave address received, /ACK transmitted */
>> + TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
>> + /**
>> + * Data byte received after general call address received, ACK
>> + * transmitted
>> + */
>> + TWSI_STAT_GEN_RXADDR_ACK = 0x90,
>> + /**
>> + * Data byte received after general call address received, /ACK
>> + * transmitted
>> + */
>> + TWSI_STAT_GEN_RXADDR_NAK = 0x98,
>> + /** STOP or repeated START condition received in slave mode */
>> + TWSI_STAT_STOP_MULTI_START = 0xa0,
>> + /** Slave address + read bit received, ACK transmitted */
>> + TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
>> + /**
>> + * Arbitration lost in address as master, slave address + read bit
>> + * received, ACK transmitted
>> + */
>> + TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
>> + /** Data byte transmitted in slave mode, ACK received */
>> + TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
>> + /** Data byte transmitted in slave mode, /ACK received */
>> + TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
>> + /** Last byte transmitted in slave mode, ACK received */
>> + TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
>> + /** Second address byte + write bit transmitted, ACK received */
>> + TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
>> + /** Second address byte + write bit transmitted, /ACK received */
>> + TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
>> + /** No relevant status information */
>> + TWSI_STAT_IDLE = 0xf8
>> +};
>> +
>> +#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
>> +# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
>> +#endif
>> +
>> +/**
>> + * struct octeon_twsi - Private data of this driver
>> + *
>> + * @base: Base address of i2c registers
>> + */
>> +struct octeon_twsi {
>> + void __iomem *base;
>> +};
>> +
>> +static void twsi_unblock(void *base);
>> +static int twsi_stop(void *base);
>> +
>> +#if defined(CONFIG_ARCH_OCTEON)
>> +static int get_io_clock(void)
>> +{
>> + return octeon_get_io_clock();
>> +}
>> +#else
>> +static int get_io_clock(void)
>> +{
>> + return octeontx_get_io_clock();
>> +}
>> +#endif
>> +
>> +static int twsi_thp(void)
>> +{
>> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
>> + return 24;
>> + else
>> + return 3;
>> +}
>> +
>> +/**
>> + * Returns true if we lost arbitration
>> + *
>> + * @code status code
>> + * @final_read true if this is the final read operation
>> + * @return true if arbitration has been lost, false if it hasn't been lost.
>
> Instead of true/false, better to mention 0 on success and -ve on failure.
>> + */
>> +static int twsi_i2c_lost_arb(u8 code, int final_read)
>> +{
>> + switch (code) {
>> + case TWSI_STAT_TX_ARB_LOST:
>> + case TWSI_STAT_TX_ACK_ARB_LOST:
>> + case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
>> + case TWSI_STAT_RXDATA_ACK_ARB_LOST:
>> + /* Arbitration lost */
>> + return -EAGAIN;
>> +
>> + case TWSI_STAT_SLAVE_RXADDR_ACK:
>> + case TWSI_STAT_RX_GEN_ADDR_ACK:
>> + case TWSI_STAT_GEN_RXADDR_ACK:
>> + case TWSI_STAT_GEN_RXADDR_NAK:
>> + /* Being addressed as slave, should back off and listen */
>> + return -EIO;
>> +
>> + case TWSI_STAT_SLAVE_RXDATA_ACK:
>> + case TWSI_STAT_SLAVE_RXDATA_NAK:
>> + case TWSI_STAT_STOP_MULTI_START:
>> + case TWSI_STAT_SLAVE_RXADDR2_ACK:
>> + case TWSI_STAT_SLAVE_TXDATA_ACK:
>> + case TWSI_STAT_SLAVE_TXDATA_NAK:
>> + case TWSI_STAT_SLAVE_TXDATA_END_ACK:
>> + /* Core busy as slave */
>> + return -EIO;
>> +
>> + case TWSI_STAT_RXDATA_ACK_SENT:
>> + /* Ack allowed on pre-terminal bytes only */
>> + if (!final_read)
>> + return 0;
>> + return -EAGAIN;
>> +
>> + case TWSI_STAT_RXDATA_NAK_SENT:
>> + /* NAK allowed on terminal byte only */
>> + if (!final_read)
>> + return 0;
>> + return -EAGAIN;
>> +
>> + case TWSI_STAT_TXDATA_NAK:
>> + case TWSI_STAT_TXADDR_NAK:
>> + case TWSI_STAT_RXADDR_NAK:
>> + case TWSI_STAT_TXADDR2DATA_NAK:
>> + return -EAGAIN;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +#define RST_BOOT_PNR_MUL(val) (((val) >> 33) & 0x1F)
>
> Better to move this up where all macros are defined.
>
>> +
>> +/**
>> + * Writes to the MIO_TWS(0..5)_SW_TWSI register
>> + *
>> + * @base Base address of i2c registers
>> + * @val value to write
>> + * @return 0 for success, otherwise error
>> + */
>> +static u64 twsi_write_sw(void __iomem *base, u64 val)
>> +{
>> + unsigned long start = get_timer(0);
>> +
>> + val &= ~TWSI_SW_R;
>> + val |= TWSI_SW_V;
>> +
>> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
>> + writeq(val, base + TWSI_SW_TWSI);
>> + do {
>> + val = readq(base + TWSI_SW_TWSI);
>> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
>> +
>> + if (val & TWSI_SW_V)
>> + debug("%s: timed out\n", __func__);
>> + return val;
>
> newline before return, please fix globally.
>> +}
>> +
>> +/**
>> + * Reads the MIO_TWS(0..5)_SW_TWSI register
>> + *
>> + * @base Base address of i2c registers
>> + * @val value for eia and op, etc. to read
>> + * @return value of the register
>> + */
>> +static u64 twsi_read_sw(void __iomem *base, u64 val)
>> +{
>> + unsigned long start = get_timer(0);
>> +
>> + val |= TWSI_SW_R | TWSI_SW_V;
>> +
>> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
>> + writeq(val, base + TWSI_SW_TWSI);
>> +
>> + do {
>> + val = readq(base + TWSI_SW_TWSI);
>> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
>> +
>> + if (val & TWSI_SW_V)
>> + debug("%s: Error writing 0x%llx\n", __func__, val);
>> +
>> + debug("%s: Returning 0x%llx\n", __func__, val);
>> + return val;
>> +}
>> +
>> +/**
>> + * Write control register
>> + *
>> + * @base Base address for i2c registers
>> + * @data data to write
>> + */
>> +static void twsi_write_ctl(void __iomem *base, u8 data)
>> +{
>> + u64 val;
>> +
>> + debug("%s(%p, 0x%x)\n", __func__, base, data);
>> + val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>> + twsi_write_sw(base, val);
>> +}
>> +
>> +/**
>> + * Reads the TWSI Control Register
>> + *
>> + * @base Base address for i2c
>> + * @return 8-bit TWSI control register
>> + */
>> +static u8 twsi_read_ctl(void __iomem *base)
>> +{
>> + u64 val;
>> +
>> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>> + val = twsi_read_sw(base, val);
>> +
>> + debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
>> + return (u8)val;
>> +}
>> +
>> +/**
>> + * Read i2c status register
>> + *
>> + * @base Base address of i2c registers
>> + * @return value of status register
>> + */
>> +static u8 twsi_read_status(void __iomem *base)
>> +{
>> + u64 val;
>> +
>> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>> +
>> + return twsi_read_sw(base, val);
>> +}
>> +
>> +/**
>> + * Waits for an i2c operation to complete
>> + *
>> + * @param base Base address of registers
>> + * @return 0 for success, 1 if timeout
>> + */
>> +static int twsi_wait(void __iomem *base)
>> +{
>> + unsigned long start = get_timer(0);
>> + u8 twsi_ctl;
>> +
>> + debug("%s(%p)\n", __func__, base);
>> + do {
>> + twsi_ctl = twsi_read_ctl(base);
>> + twsi_ctl &= TWSI_CTL_IFLG;
>> + } while (!twsi_ctl && get_timer(start) < 50);
>> +
>> + debug(" return: %u\n", !twsi_ctl);
>> + return !twsi_ctl;
>> +}
>> +
>> +/**
>> + * Unsticks the i2c bus
>> + *
>> + * @base base address of registers
>> + */
>> +static int twsi_start_unstick(void __iomem *base)
>> +{
>> + twsi_stop(base);
>> + twsi_unblock(base);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Sends an i2c start condition
>> + *
>> + * @base base address of registers
>> + * @return 0 for success, otherwise error
>> + */
>> +static int twsi_start(void __iomem *base)
>> +{
>> + int ret;
>> + u8 stat;
>> +
>> + debug("%s(%p)\n", __func__, base);
>> + twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + stat = twsi_read_status(base);
>> + debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
>> + switch (stat) {
>> + case TWSI_STAT_START:
>> + case TWSI_STAT_RSTART:
>> + return 0;
>> + case TWSI_STAT_RXADDR_ACK:
>> + default:
>> + return twsi_start_unstick(base);
>> + }
>> + }
>> +
>> + debug("%s: success\n", __func__);
>> + return 0;
>> +}
>> +
>> +/**
>> + * Sends an i2c stop condition
>> + *
>> + * @base register base address
>> + * @return 0 for success, -1 if error
>> + */
>> +static int twsi_stop(void __iomem *base)
>> +{
>> + u8 stat;
>> +
>> + twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
>> +
>> + stat = twsi_read_status(base);
>> + if (stat != TWSI_STAT_IDLE) {
>> + debug("%s: Bad status on bus@%p\n", __func__, base);
>> + return -1;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Writes data to the i2c bus
>> + *
>> + * @base register base address
>> + * @slave_addr address of slave to write to
>> + * @buffer Pointer to buffer to write
>> + * @length Number of bytes in buffer to write
>> + * @return 0 for success, otherwise error
>> + */
>> +static int twsi_write_data(void __iomem *base, u8 slave_addr,
>> + u8 *buffer, unsigned int length)
>
> This should be properly aligned.
>
>> +{
>> + unsigned int curr = 0;
>> + u64 val;
>> + int ret;
>> +
>> + debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
>> + buffer, length);
>
> same alignment.
>
>> + ret = twsi_start(base);
>> + if (ret) {
>> + debug("%s: Could not start BUS transaction\n", __func__);
>> + return -1;
>> + }
>> +
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + debug("%s: wait failed\n", __func__);
>> + return ret;
>> + }
>> +
>> + val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>> + twsi_write_sw(base, val);
>> + twsi_write_ctl(base, TWSI_CTL_ENAB);
>> +
>> + debug("%s: Waiting\n", __func__);
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + debug("%s: Timed out writing slave address 0x%x to target\n",
>> + __func__, slave_addr);
>> + return ret;
>> + }
>> +
>> + ret = twsi_read_status(base);
>> + debug("%s: status: 0x%x\n", __func__, ret);
>> + if (ret != TWSI_STAT_TXADDR_ACK) {
>> + debug("%s: status: 0x%x\n", __func__, ret);
>> + twsi_stop(base);
>> + return twsi_i2c_lost_arb(ret, 0);
>> + }
>> +
>> + while (curr < length) {
>> + val = buffer[curr++] |
>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>> + twsi_write_sw(base, val);
>> + twsi_write_ctl(base, TWSI_CTL_ENAB);
>> +
>> + debug("%s: Writing 0x%llx\n", __func__, val);
>> +
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + debug("%s: Timed out writing data to 0x%x\n",
>> + __func__, slave_addr);
>> + return ret;
>> + }
>> + ret = twsi_read_status(base);
>> + debug("%s: status: 0x%x\n", __func__, ret);
>> + }
>> +
>> + debug("%s: Stopping\n", __func__);
>> + return twsi_stop(base);
>> +}
>> +
>> +/**
>> + * Manually clear the I2C bus and send a stop
>> + *
>> + * @base register base address
>> + */
>> +static void twsi_unblock(void __iomem *base)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < 9; i++) {
>> + writeq(0, base + TWSI_INT);
>> + udelay(5);
>> + writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
>> + udelay(5);
>> + }
>> + writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
>> + udelay(5);
>> + writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
>> + udelay(5);
>> + writeq(0, base + TWSI_INT);
>> + udelay(5);
>> +}
>> +
>> +/**
>> + * Performs a read transaction on the i2c bus
>> + *
>> + * @base Base address of twsi registers
>> + * @slave_addr i2c bus address to read from
>> + * @buffer buffer to read into
>> + * @length number of bytes to read
>> + * @return 0 for success, otherwise error
>> + */
>> +static int twsi_read_data(void __iomem *base, u8 slave_addr,
>> + u8 *buffer, unsigned int length)
>> +{
>> + unsigned int curr = 0;
>> + u64 val;
>> + int ret;
>> +
>> + debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
>> + buffer, length);
>> + ret = twsi_start(base);
>> + if (ret) {
>> + debug("%s: start failed\n", __func__);
>> + return ret;
>> + }
>> +
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + debug("%s: wait failed\n", __func__);
>> + return ret;
>> + }
>> +
>> + val = (u32)(slave_addr << 1) | TWSI_OP_READ |
>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>> + twsi_write_sw(base, val);
>> + twsi_write_ctl(base, TWSI_CTL_ENAB);
>> +
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + debug("%s: waiting for sending addr failed\n", __func__);
>> + return ret;
>> + }
>> +
>> + ret = twsi_read_status(base);
>> + debug("%s: status: 0x%x\n", __func__, ret);
>> + if (ret != TWSI_STAT_RXADDR_ACK) {
>> + debug("%s: status: 0x%x\n", __func__, ret);
>> + twsi_stop(base);
>> + return twsi_i2c_lost_arb(ret, 0);
>> + }
>> +
>> + while (curr < length) {
>> + twsi_write_ctl(base, TWSI_CTL_ENAB |
>> + ((curr < length - 1) ? TWSI_CTL_AAK : 0));
>> +
>> + ret = twsi_wait(base);
>> + if (ret) {
>> + debug("%s: waiting for data failed\n", __func__);
>> + return ret;
>> + }
>> +
>> + val = twsi_read_sw(base, val);
>> + buffer[curr++] = (u8)val;
>> + }
>> +
>> + twsi_stop(base);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Calculate the divisor values
>> + *
>> + * @speed Speed to set
>> + * @m_div Pointer to M divisor
>> + * @n_div Pointer to N divisor
>> + * @return 0 for success, otherwise error
>> + */
>> +static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
>> +{
>> + int io_clock_hz;
>> + int tclk, fsamp;
>> + int ndiv, mdiv;
>> +
>> +#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
>> + io_clock_hz = get_io_clock();
>> + tclk = io_clock_hz / (2 * (twsi_thp() + 1));
>> +#elif defined(CONFIG_ARCH_OCTEONTX2)
>> + /* Refclk src in mode register defaults to 100MHz clock */
>> + io_clock_hz = 100000000; /* 100 Mhz */
>> + tclk = io_clock_hz / (twsi_thp() + 2);
>> +#endif
>> + debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
>> +
>> + /*
>> + * Compute the clocks M divider:
>> + *
>> + * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
>> + * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
>> + *
>> + * For OcteonTX2 -
>> + * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
>> + * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
>> + */
>> + for (ndiv = 0; ndiv < 8; ndiv++) {
>> + fsamp = tclk / (1 << ndiv);
>> + mdiv = fsamp / speed / 10;
>> + mdiv -= 1;
>> + if (mdiv < 16)
>> + break;
>> + }
>> +
>> + *m_div = mdiv;
>> + *n_div = ndiv;
>> +}
>> +
>> +/**
>> + * Init I2C controller
>> + *
>> + * @base Base address of twsi registers
>> + * @slave_addr I2C slave address to configure this controller to
>> + * @return 0 for success, otherwise error
>> + */
>> +static int twsi_init(void __iomem *base, int slaveaddr)
>> +{
>> + u64 val;
>> +
>> + debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
>> +
>> + val = slaveaddr << 1 |
>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
>> + TWSI_SW_V;
>> + twsi_write_sw(base, val);
>> +
>> + /* Set slave address */
>> + val = slaveaddr |
>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
>> + TWSI_SW_V;
>> + twsi_write_sw(base, val);
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Transfers data over the i2c bus
>> + *
>> + * @bus i2c bus to transfer data over
>> + * @msg Array of i2c messages
>> + * @nmsgs Number of messages to send/receive
>> + * @return 0 for success, otherwise error
>> + */
>> +static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
>> + int nmsgs)
>> +{
>> + struct octeon_twsi *twsi = dev_get_priv(bus);
>> + int ret;
>> + int i;
>> +
>> + debug("%s: %d messages\n", __func__, nmsgs);
>> + for (i = 0; i < nmsgs; i++, msg++) {
>> + debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
>> + msg->len);
>> +
>> + if (msg->flags & I2C_M_RD) {
>> + debug("%s: Reading data\n", __func__);
>> + ret = twsi_read_data(twsi->base, msg->addr,
>> + msg->buf, msg->len);
>> + } else {
>> + debug("%s: Writing data\n", __func__);
>> + ret = twsi_write_data(twsi->base, msg->addr,
>> + msg->buf, msg->len);
>> + }
>> + if (ret) {
>> + debug("%s: error sending\n", __func__);
>> + return -EREMOTEIO;
>> + }
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +/**
>> + * Set I2C bus speed
>> + *
>> + * @bus i2c bus to transfer data over
>> + * @speed Speed in Hz to set
>> + * @return 0 for success, otherwise error
>> + */
>> +static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
>> +{
>> + struct octeon_twsi *twsi = dev_get_priv(bus);
>> + int m_div, n_div;
>> + u64 val;
>> +
>> + debug("%s(%p, %u)\n", __func__, bus, speed);
>> +
>> + twsi_calc_div(speed, &m_div, &n_div);
>> + if (m_div >= 16)
>> + return -1;
>> +
>> + val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
>> + TWSI_SW_V;
>> + /* Only init non-slave ports */
>> + writeq(val, twsi->base + TWSI_SW_TWSI);
>> +
>> + debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
>> + return 0;
>> +}
>> +
>> +/**
>> + * Driver probe function
>> + *
>> + * @dev I2C device to probe
>> + * @return 0 for success, otherwise error
>> + */
>> +static int octeon_pci_i2c_probe(struct udevice *dev)
>> +{
>> + struct octeon_twsi *twsi = dev_get_priv(dev);
>> +#if !defined(CONFIG_ARCH_OCTEON)
>> + pci_dev_t bdf = dm_pci_get_bdf(dev);
>> +
>> + debug("TWSI PCI device: %x\n", bdf);
>> + dev->req_seq = PCI_FUNC(bdf);
>> +
>> + twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>> + PCI_REGION_MEM);
>> +#else
>> + twsi->base = dev_remap_addr(dev);
>> +#endif
>> + debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
>> +
>> + /* Start with standard speed, real speed set via DT or cmd */
>> + return twsi_init(twsi->base, CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
>> +}
>> +
>> +static const struct dm_i2c_ops octeon_i2c_ops = {
>> + .xfer = octeon_i2c_xfer,
>> + .set_bus_speed = octeon_i2c_set_bus_speed,
>> +};
>> +
>> +static const struct udevice_id octeon_i2c_ids[] = {
>> + { .compatible = "cavium,thunder-8890-twsi" },
>> + { .compatible = "cavium,octeon-7890-twsi" },
>> + { }
>> +};
>> +
>> +U_BOOT_DRIVER(octeon_pci_twsi) = {
>> + .name = "i2c_octeon",
>> + .id = UCLASS_I2C,
>> + .of_match = octeon_i2c_ids,
>> + .probe = octeon_pci_i2c_probe,
>> + .priv_auto_alloc_size = sizeof(struct octeon_twsi),
>> + .ops = &octeon_i2c_ops,
>> +};
>> +
>> +#if !defined(CONFIG_ARCH_OCTEON)
>> +static struct pci_device_id octeon_twsi_supported[] = {
>> + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI) },
>> + { },
>> +};
>> +
>> +U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);
>> +#endif
>> --
>> 2.26.2
>>
Viele Gr??e,
Stefan
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-06-03 5:20 ` Stefan Roese
@ 2020-06-03 5:37 ` Rayagonda Kokatanur
2020-06-03 5:39 ` Stefan Roese
0 siblings, 1 reply; 9+ messages in thread
From: Rayagonda Kokatanur @ 2020-06-03 5:37 UTC (permalink / raw)
To: u-boot
On Wed, Jun 3, 2020 at 10:50 AM Stefan Roese <sr@denx.de> wrote:
>
> Hi Rayagonda,
>
> v1 is superseeded. Please review the latest version, v2:
>
> https://patchwork.ozlabs.org/project/uboot/patch/20200526121307.2735-1-sr at denx.de/
Thank you, I missed that v2.
Anyway please ignore my comments, all of them are fixed in v2.
Thanks,
Rayagonda
>
> Thanks,
> Stefan
>
> On 03.06.20 07:15, Rayagonda Kokatanur wrote:
> > Hi Stefan,
> >
> > Few minor comments,
> >
> > On Thu, May 14, 2020 at 12:53 PM Stefan Roese <sr@denx.de> wrote:
> >>
> >> From: Suneel Garapati <sgarapati@marvell.com>
> >>
> >> Add support for I2C controllers found on Octeon II/III and Octeon TX
> >> TX2 SoC platforms.
> >>
> >> Signed-off-by: Aaron Williams <awilliams@marvell.com>
> >> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
> >> Signed-off-by: Stefan Roese <sr@denx.de>
> >> Cc: Heiko Schocher <hs@denx.de>
> >> Cc: Simon Glass <sjg@chromium.org>
> >> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
> >> Cc: Aaron Williams <awilliams@marvell.com>
> >> Cc: Chandrakala Chavva <cchavva@marvell.com>
> >> ---
> >> RFC -> v1 (Stefan):
> >> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
> >> single patch. This is useful, as the upcoming MIPS Octeon support will
> >> use this I2C driver.
> >> - Added MIPS Octeon II/III support (big endian). Rename driver and its
> >> function names from "octeontx" to "octeon" to better match all Octeon
> >> platforms.
> >> - Moved from union to defines / bitmasks as suggested by Simon. This makes
> >> the driver usage on little- and big-endian platforms much easier.
> >> - Enhanced Kconfig text
> >> - Removed all clock macros (use values from DT)
> >> - Removed long driver debug strings. This is only available when a debug
> >> version of this driver is built. The user / developer can lookup the
> >> descriptive error messages in the driver in this case anyway.
> >> - Removed static "last_id"
> >> - Dropped misc blank lines. Misc reformatting.
> >> - Dropped "!= 0"
> >> - Added missing function comments
> >> - Added missing strut comments
> >> - Changed comment style
> >> - Renames "result" to "ret"
> >> - Hex numbers uppercase
> >> - Minor other changes
> >> - Reword commit text and subject
> >>
> >> drivers/i2c/Kconfig | 10 +
> >> drivers/i2c/Makefile | 1 +
> >> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
> >> 3 files changed, 814 insertions(+)
> >> create mode 100644 drivers/i2c/octeon_i2c.c
> >>
> >> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> >> index e42b6516bf..1330b36698 100644
> >> --- a/drivers/i2c/Kconfig
> >> +++ b/drivers/i2c/Kconfig
> >> @@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
> >> bus. Devices can be attached to the bus using the device tree
> >> which specifies the driver to use. See sandbox.dts as an example.
> >>
> >> +config SYS_I2C_OCTEON
> >> + bool "Octeon II/III/TX/TX2 I2C driver"
> >> + depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
> >> + default y
> >> + help
> >> + Add support for the Marvell Octeon I2C driver. This is used with
> >> + various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
> >> + chips have several I2C ports and all are provided, controlled by
> >> + the device tree.
> >> +
> >> config SYS_I2C_S3C24X0
> >> bool "Samsung I2C driver"
> >> depends on ARCH_EXYNOS4 && DM_I2C
> >> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> >> index 62935b7ebc..2b58aae892 100644
> >> --- a/drivers/i2c/Makefile
> >> +++ b/drivers/i2c/Makefile
> >> @@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
> >> obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
> >> obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
> >> obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
> >> +obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
> >> obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
> >> obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
> >> obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
> >> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
> >> new file mode 100644
> >> index 0000000000..210f98655e
> >> --- /dev/null
> >> +++ b/drivers/i2c/octeon_i2c.c
> >> @@ -0,0 +1,803 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Copyright (C) 2018 Marvell International Ltd.
> >
> > Should it be 2020 ?
> >> + */
> >> +
> >> +#include <common.h>
> >> +#include <i2c.h>
> >> +#include <dm.h>
> >> +#include <pci_ids.h>
> >> +#include <asm/io.h>
> >> +#include <asm/arch/clock.h>
> >> +#include <linux/bitfield.h>
> >> +
> >> +/*
> >> + * Octeon II/III (MIPS) have different register offsets than the ARM based
> >> + * Octeon TX/TX2 SoCs
> >> + */
> >> +#if defined(CONFIG_ARCH_OCTEON)
> >> +#define REG_OFFS 0x0000
> >> +#else
> >> +#define REG_OFFS 0x1000
> >> +#endif
> >
> > How about getting this offset from dt ?
> >> +
> >> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
> >> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
> >> +#define TWSI_INT (REG_OFFS + 0x10)
> >> +#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
> >> +
> >> +#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
> >> +#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
> >> +#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
> >> +#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
> >> +#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
> >> +#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
> >> +#define TWSI_SW_SOVR BIT_ULL(55)
> >> +#define TWSI_SW_R BIT_ULL(56)
> >> +#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
> >> +#define TWSI_SW_EIA GENMASK_ULL(61)
> >> +#define TWSI_SW_SLONLY BIT_ULL(62)
> >> +#define TWSI_SW_V BIT_ULL(63)
> >> +
> >> +#define TWSI_INT_SDA_OVR BIT_ULL(8)
> >> +#define TWSI_INT_SCL_OVR BIT_ULL(9)
> >> +#define TWSI_INT_SDA BIT_ULL(10)
> >> +#define TWSI_INT_SCL BIT_ULL(11)
> >> +
> >> +enum {
> >> + TWSI_OP_WRITE = 0,
> >> + TWSI_OP_READ = 1,
> >> +};
> >> +
> >> +enum {
> >> + TWSI_EOP_SLAVE_ADDR = 0,
> >> + TWSI_EOP_CLK_CTL = 3,
> >> + TWSI_SW_EOP_IA = 6,
> >> +};
> >> +
> >> +enum {
> >> + TWSI_SLAVEADD = 0,
> >> + TWSI_DATA = 1,
> >> + TWSI_CTL = 2,
> >> + TWSI_CLKCTL = 3,
> >> + TWSI_STAT = 3,
> >> + TWSI_SLAVEADD_EXT = 4,
> >> + TWSI_RST = 7,
> >> +};
> >> +
> >> +enum {
> >> + TWSI_CTL_AAK = BIT(2),
> >> + TWSI_CTL_IFLG = BIT(3),
> >> + TWSI_CTL_STP = BIT(4),
> >> + TWSI_CTL_STA = BIT(5),
> >> + TWSI_CTL_ENAB = BIT(6),
> >> + TWSI_CTL_CE = BIT(7),
> >> +};
> >> +
> >> +/*
> >> + * Internal errors. When debugging is enabled, the driver will report the
> >> + * error number and the user / developer can check the table below for the
> >> + * detailed error description.
> >> + */
> >> +enum {
> >> + /** Bus error */
> >> + TWSI_STAT_BUS_ERROR = 0x00,
> >> + /** Start condition transmitted */
> >> + TWSI_STAT_START = 0x08,
> >> + /** Repeat start condition transmitted */
> >> + TWSI_STAT_RSTART = 0x10,
> >> + /** Address + write bit transmitted, ACK received */
> >> + TWSI_STAT_TXADDR_ACK = 0x18,
> >> + /** Address + write bit transmitted, /ACK received */
> >> + TWSI_STAT_TXADDR_NAK = 0x20,
> >> + /** Data byte transmitted in master mode, ACK received */
> >> + TWSI_STAT_TXDATA_ACK = 0x28,
> >> + /** Data byte transmitted in master mode, ACK received */
> >> + TWSI_STAT_TXDATA_NAK = 0x30,
> >> + /** Arbitration lost in address or data byte */
> >> + TWSI_STAT_TX_ARB_LOST = 0x38,
> >> + /** Address + read bit transmitted, ACK received */
> >> + TWSI_STAT_RXADDR_ACK = 0x40,
> >> + /** Address + read bit transmitted, /ACK received */
> >> + TWSI_STAT_RXADDR_NAK = 0x48,
> >> + /** Data byte received in master mode, ACK transmitted */
> >> + TWSI_STAT_RXDATA_ACK_SENT = 0x50,
> >> + /** Data byte received, NACK transmitted */
> >> + TWSI_STAT_RXDATA_NAK_SENT = 0x58,
> >> + /** Slave address received, sent ACK */
> >> + TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
> >> + /**
> >> + * Arbitration lost in address as master, slave address + write bit
> >> + * received, ACK transmitted
> >> + */
> >> + TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
> >> + /** General call address received, ACK transmitted */
> >> + TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
> >> + /**
> >> + * Arbitration lost in address as master, general call address
> >> + * received, ACK transmitted
> >> + */
> >> + TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
> >> + /** Data byte received after slave address received, ACK transmitted */
> >> + TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
> >> + /** Data byte received after slave address received, /ACK transmitted */
> >> + TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
> >> + /**
> >> + * Data byte received after general call address received, ACK
> >> + * transmitted
> >> + */
> >> + TWSI_STAT_GEN_RXADDR_ACK = 0x90,
> >> + /**
> >> + * Data byte received after general call address received, /ACK
> >> + * transmitted
> >> + */
> >> + TWSI_STAT_GEN_RXADDR_NAK = 0x98,
> >> + /** STOP or repeated START condition received in slave mode */
> >> + TWSI_STAT_STOP_MULTI_START = 0xa0,
> >> + /** Slave address + read bit received, ACK transmitted */
> >> + TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
> >> + /**
> >> + * Arbitration lost in address as master, slave address + read bit
> >> + * received, ACK transmitted
> >> + */
> >> + TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
> >> + /** Data byte transmitted in slave mode, ACK received */
> >> + TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
> >> + /** Data byte transmitted in slave mode, /ACK received */
> >> + TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
> >> + /** Last byte transmitted in slave mode, ACK received */
> >> + TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
> >> + /** Second address byte + write bit transmitted, ACK received */
> >> + TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
> >> + /** Second address byte + write bit transmitted, /ACK received */
> >> + TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
> >> + /** No relevant status information */
> >> + TWSI_STAT_IDLE = 0xf8
> >> +};
> >> +
> >> +#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
> >> +# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
> >> +#endif
> >> +
> >> +/**
> >> + * struct octeon_twsi - Private data of this driver
> >> + *
> >> + * @base: Base address of i2c registers
> >> + */
> >> +struct octeon_twsi {
> >> + void __iomem *base;
> >> +};
> >> +
> >> +static void twsi_unblock(void *base);
> >> +static int twsi_stop(void *base);
> >> +
> >> +#if defined(CONFIG_ARCH_OCTEON)
> >> +static int get_io_clock(void)
> >> +{
> >> + return octeon_get_io_clock();
> >> +}
> >> +#else
> >> +static int get_io_clock(void)
> >> +{
> >> + return octeontx_get_io_clock();
> >> +}
> >> +#endif
> >> +
> >> +static int twsi_thp(void)
> >> +{
> >> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
> >> + return 24;
> >> + else
> >> + return 3;
> >> +}
> >> +
> >> +/**
> >> + * Returns true if we lost arbitration
> >> + *
> >> + * @code status code
> >> + * @final_read true if this is the final read operation
> >> + * @return true if arbitration has been lost, false if it hasn't been lost.
> >
> > Instead of true/false, better to mention 0 on success and -ve on failure.
> >> + */
> >> +static int twsi_i2c_lost_arb(u8 code, int final_read)
> >> +{
> >> + switch (code) {
> >> + case TWSI_STAT_TX_ARB_LOST:
> >> + case TWSI_STAT_TX_ACK_ARB_LOST:
> >> + case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
> >> + case TWSI_STAT_RXDATA_ACK_ARB_LOST:
> >> + /* Arbitration lost */
> >> + return -EAGAIN;
> >> +
> >> + case TWSI_STAT_SLAVE_RXADDR_ACK:
> >> + case TWSI_STAT_RX_GEN_ADDR_ACK:
> >> + case TWSI_STAT_GEN_RXADDR_ACK:
> >> + case TWSI_STAT_GEN_RXADDR_NAK:
> >> + /* Being addressed as slave, should back off and listen */
> >> + return -EIO;
> >> +
> >> + case TWSI_STAT_SLAVE_RXDATA_ACK:
> >> + case TWSI_STAT_SLAVE_RXDATA_NAK:
> >> + case TWSI_STAT_STOP_MULTI_START:
> >> + case TWSI_STAT_SLAVE_RXADDR2_ACK:
> >> + case TWSI_STAT_SLAVE_TXDATA_ACK:
> >> + case TWSI_STAT_SLAVE_TXDATA_NAK:
> >> + case TWSI_STAT_SLAVE_TXDATA_END_ACK:
> >> + /* Core busy as slave */
> >> + return -EIO;
> >> +
> >> + case TWSI_STAT_RXDATA_ACK_SENT:
> >> + /* Ack allowed on pre-terminal bytes only */
> >> + if (!final_read)
> >> + return 0;
> >> + return -EAGAIN;
> >> +
> >> + case TWSI_STAT_RXDATA_NAK_SENT:
> >> + /* NAK allowed on terminal byte only */
> >> + if (!final_read)
> >> + return 0;
> >> + return -EAGAIN;
> >> +
> >> + case TWSI_STAT_TXDATA_NAK:
> >> + case TWSI_STAT_TXADDR_NAK:
> >> + case TWSI_STAT_RXADDR_NAK:
> >> + case TWSI_STAT_TXADDR2DATA_NAK:
> >> + return -EAGAIN;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +#define RST_BOOT_PNR_MUL(val) (((val) >> 33) & 0x1F)
> >
> > Better to move this up where all macros are defined.
> >
> >> +
> >> +/**
> >> + * Writes to the MIO_TWS(0..5)_SW_TWSI register
> >> + *
> >> + * @base Base address of i2c registers
> >> + * @val value to write
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static u64 twsi_write_sw(void __iomem *base, u64 val)
> >> +{
> >> + unsigned long start = get_timer(0);
> >> +
> >> + val &= ~TWSI_SW_R;
> >> + val |= TWSI_SW_V;
> >> +
> >> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
> >> + writeq(val, base + TWSI_SW_TWSI);
> >> + do {
> >> + val = readq(base + TWSI_SW_TWSI);
> >> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
> >> +
> >> + if (val & TWSI_SW_V)
> >> + debug("%s: timed out\n", __func__);
> >> + return val;
> >
> > newline before return, please fix globally.
> >> +}
> >> +
> >> +/**
> >> + * Reads the MIO_TWS(0..5)_SW_TWSI register
> >> + *
> >> + * @base Base address of i2c registers
> >> + * @val value for eia and op, etc. to read
> >> + * @return value of the register
> >> + */
> >> +static u64 twsi_read_sw(void __iomem *base, u64 val)
> >> +{
> >> + unsigned long start = get_timer(0);
> >> +
> >> + val |= TWSI_SW_R | TWSI_SW_V;
> >> +
> >> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
> >> + writeq(val, base + TWSI_SW_TWSI);
> >> +
> >> + do {
> >> + val = readq(base + TWSI_SW_TWSI);
> >> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
> >> +
> >> + if (val & TWSI_SW_V)
> >> + debug("%s: Error writing 0x%llx\n", __func__, val);
> >> +
> >> + debug("%s: Returning 0x%llx\n", __func__, val);
> >> + return val;
> >> +}
> >> +
> >> +/**
> >> + * Write control register
> >> + *
> >> + * @base Base address for i2c registers
> >> + * @data data to write
> >> + */
> >> +static void twsi_write_ctl(void __iomem *base, u8 data)
> >> +{
> >> + u64 val;
> >> +
> >> + debug("%s(%p, 0x%x)\n", __func__, base, data);
> >> + val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> >> + twsi_write_sw(base, val);
> >> +}
> >> +
> >> +/**
> >> + * Reads the TWSI Control Register
> >> + *
> >> + * @base Base address for i2c
> >> + * @return 8-bit TWSI control register
> >> + */
> >> +static u8 twsi_read_ctl(void __iomem *base)
> >> +{
> >> + u64 val;
> >> +
> >> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> >> + val = twsi_read_sw(base, val);
> >> +
> >> + debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
> >> + return (u8)val;
> >> +}
> >> +
> >> +/**
> >> + * Read i2c status register
> >> + *
> >> + * @base Base address of i2c registers
> >> + * @return value of status register
> >> + */
> >> +static u8 twsi_read_status(void __iomem *base)
> >> +{
> >> + u64 val;
> >> +
> >> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> >> +
> >> + return twsi_read_sw(base, val);
> >> +}
> >> +
> >> +/**
> >> + * Waits for an i2c operation to complete
> >> + *
> >> + * @param base Base address of registers
> >> + * @return 0 for success, 1 if timeout
> >> + */
> >> +static int twsi_wait(void __iomem *base)
> >> +{
> >> + unsigned long start = get_timer(0);
> >> + u8 twsi_ctl;
> >> +
> >> + debug("%s(%p)\n", __func__, base);
> >> + do {
> >> + twsi_ctl = twsi_read_ctl(base);
> >> + twsi_ctl &= TWSI_CTL_IFLG;
> >> + } while (!twsi_ctl && get_timer(start) < 50);
> >> +
> >> + debug(" return: %u\n", !twsi_ctl);
> >> + return !twsi_ctl;
> >> +}
> >> +
> >> +/**
> >> + * Unsticks the i2c bus
> >> + *
> >> + * @base base address of registers
> >> + */
> >> +static int twsi_start_unstick(void __iomem *base)
> >> +{
> >> + twsi_stop(base);
> >> + twsi_unblock(base);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Sends an i2c start condition
> >> + *
> >> + * @base base address of registers
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int twsi_start(void __iomem *base)
> >> +{
> >> + int ret;
> >> + u8 stat;
> >> +
> >> + debug("%s(%p)\n", __func__, base);
> >> + twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + stat = twsi_read_status(base);
> >> + debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
> >> + switch (stat) {
> >> + case TWSI_STAT_START:
> >> + case TWSI_STAT_RSTART:
> >> + return 0;
> >> + case TWSI_STAT_RXADDR_ACK:
> >> + default:
> >> + return twsi_start_unstick(base);
> >> + }
> >> + }
> >> +
> >> + debug("%s: success\n", __func__);
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Sends an i2c stop condition
> >> + *
> >> + * @base register base address
> >> + * @return 0 for success, -1 if error
> >> + */
> >> +static int twsi_stop(void __iomem *base)
> >> +{
> >> + u8 stat;
> >> +
> >> + twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
> >> +
> >> + stat = twsi_read_status(base);
> >> + if (stat != TWSI_STAT_IDLE) {
> >> + debug("%s: Bad status on bus@%p\n", __func__, base);
> >> + return -1;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Writes data to the i2c bus
> >> + *
> >> + * @base register base address
> >> + * @slave_addr address of slave to write to
> >> + * @buffer Pointer to buffer to write
> >> + * @length Number of bytes in buffer to write
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int twsi_write_data(void __iomem *base, u8 slave_addr,
> >> + u8 *buffer, unsigned int length)
> >
> > This should be properly aligned.
> >
> >> +{
> >> + unsigned int curr = 0;
> >> + u64 val;
> >> + int ret;
> >> +
> >> + debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
> >> + buffer, length);
> >
> > same alignment.
> >
> >> + ret = twsi_start(base);
> >> + if (ret) {
> >> + debug("%s: Could not start BUS transaction\n", __func__);
> >> + return -1;
> >> + }
> >> +
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + debug("%s: wait failed\n", __func__);
> >> + return ret;
> >> + }
> >> +
> >> + val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
> >> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> >> + twsi_write_sw(base, val);
> >> + twsi_write_ctl(base, TWSI_CTL_ENAB);
> >> +
> >> + debug("%s: Waiting\n", __func__);
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + debug("%s: Timed out writing slave address 0x%x to target\n",
> >> + __func__, slave_addr);
> >> + return ret;
> >> + }
> >> +
> >> + ret = twsi_read_status(base);
> >> + debug("%s: status: 0x%x\n", __func__, ret);
> >> + if (ret != TWSI_STAT_TXADDR_ACK) {
> >> + debug("%s: status: 0x%x\n", __func__, ret);
> >> + twsi_stop(base);
> >> + return twsi_i2c_lost_arb(ret, 0);
> >> + }
> >> +
> >> + while (curr < length) {
> >> + val = buffer[curr++] |
> >> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> >> + twsi_write_sw(base, val);
> >> + twsi_write_ctl(base, TWSI_CTL_ENAB);
> >> +
> >> + debug("%s: Writing 0x%llx\n", __func__, val);
> >> +
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + debug("%s: Timed out writing data to 0x%x\n",
> >> + __func__, slave_addr);
> >> + return ret;
> >> + }
> >> + ret = twsi_read_status(base);
> >> + debug("%s: status: 0x%x\n", __func__, ret);
> >> + }
> >> +
> >> + debug("%s: Stopping\n", __func__);
> >> + return twsi_stop(base);
> >> +}
> >> +
> >> +/**
> >> + * Manually clear the I2C bus and send a stop
> >> + *
> >> + * @base register base address
> >> + */
> >> +static void twsi_unblock(void __iomem *base)
> >> +{
> >> + int i;
> >> +
> >> + for (i = 0; i < 9; i++) {
> >> + writeq(0, base + TWSI_INT);
> >> + udelay(5);
> >> + writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
> >> + udelay(5);
> >> + }
> >> + writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
> >> + udelay(5);
> >> + writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
> >> + udelay(5);
> >> + writeq(0, base + TWSI_INT);
> >> + udelay(5);
> >> +}
> >> +
> >> +/**
> >> + * Performs a read transaction on the i2c bus
> >> + *
> >> + * @base Base address of twsi registers
> >> + * @slave_addr i2c bus address to read from
> >> + * @buffer buffer to read into
> >> + * @length number of bytes to read
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int twsi_read_data(void __iomem *base, u8 slave_addr,
> >> + u8 *buffer, unsigned int length)
> >> +{
> >> + unsigned int curr = 0;
> >> + u64 val;
> >> + int ret;
> >> +
> >> + debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
> >> + buffer, length);
> >> + ret = twsi_start(base);
> >> + if (ret) {
> >> + debug("%s: start failed\n", __func__);
> >> + return ret;
> >> + }
> >> +
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + debug("%s: wait failed\n", __func__);
> >> + return ret;
> >> + }
> >> +
> >> + val = (u32)(slave_addr << 1) | TWSI_OP_READ |
> >> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
> >> + twsi_write_sw(base, val);
> >> + twsi_write_ctl(base, TWSI_CTL_ENAB);
> >> +
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + debug("%s: waiting for sending addr failed\n", __func__);
> >> + return ret;
> >> + }
> >> +
> >> + ret = twsi_read_status(base);
> >> + debug("%s: status: 0x%x\n", __func__, ret);
> >> + if (ret != TWSI_STAT_RXADDR_ACK) {
> >> + debug("%s: status: 0x%x\n", __func__, ret);
> >> + twsi_stop(base);
> >> + return twsi_i2c_lost_arb(ret, 0);
> >> + }
> >> +
> >> + while (curr < length) {
> >> + twsi_write_ctl(base, TWSI_CTL_ENAB |
> >> + ((curr < length - 1) ? TWSI_CTL_AAK : 0));
> >> +
> >> + ret = twsi_wait(base);
> >> + if (ret) {
> >> + debug("%s: waiting for data failed\n", __func__);
> >> + return ret;
> >> + }
> >> +
> >> + val = twsi_read_sw(base, val);
> >> + buffer[curr++] = (u8)val;
> >> + }
> >> +
> >> + twsi_stop(base);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Calculate the divisor values
> >> + *
> >> + * @speed Speed to set
> >> + * @m_div Pointer to M divisor
> >> + * @n_div Pointer to N divisor
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
> >> +{
> >> + int io_clock_hz;
> >> + int tclk, fsamp;
> >> + int ndiv, mdiv;
> >> +
> >> +#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
> >> + io_clock_hz = get_io_clock();
> >> + tclk = io_clock_hz / (2 * (twsi_thp() + 1));
> >> +#elif defined(CONFIG_ARCH_OCTEONTX2)
> >> + /* Refclk src in mode register defaults to 100MHz clock */
> >> + io_clock_hz = 100000000; /* 100 Mhz */
> >> + tclk = io_clock_hz / (twsi_thp() + 2);
> >> +#endif
> >> + debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
> >> +
> >> + /*
> >> + * Compute the clocks M divider:
> >> + *
> >> + * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
> >> + * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
> >> + *
> >> + * For OcteonTX2 -
> >> + * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
> >> + * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
> >> + */
> >> + for (ndiv = 0; ndiv < 8; ndiv++) {
> >> + fsamp = tclk / (1 << ndiv);
> >> + mdiv = fsamp / speed / 10;
> >> + mdiv -= 1;
> >> + if (mdiv < 16)
> >> + break;
> >> + }
> >> +
> >> + *m_div = mdiv;
> >> + *n_div = ndiv;
> >> +}
> >> +
> >> +/**
> >> + * Init I2C controller
> >> + *
> >> + * @base Base address of twsi registers
> >> + * @slave_addr I2C slave address to configure this controller to
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int twsi_init(void __iomem *base, int slaveaddr)
> >> +{
> >> + u64 val;
> >> +
> >> + debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
> >> +
> >> + val = slaveaddr << 1 |
> >> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
> >> + TWSI_SW_V;
> >> + twsi_write_sw(base, val);
> >> +
> >> + /* Set slave address */
> >> + val = slaveaddr |
> >> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
> >> + TWSI_SW_V;
> >> + twsi_write_sw(base, val);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Transfers data over the i2c bus
> >> + *
> >> + * @bus i2c bus to transfer data over
> >> + * @msg Array of i2c messages
> >> + * @nmsgs Number of messages to send/receive
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
> >> + int nmsgs)
> >> +{
> >> + struct octeon_twsi *twsi = dev_get_priv(bus);
> >> + int ret;
> >> + int i;
> >> +
> >> + debug("%s: %d messages\n", __func__, nmsgs);
> >> + for (i = 0; i < nmsgs; i++, msg++) {
> >> + debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
> >> + msg->len);
> >> +
> >> + if (msg->flags & I2C_M_RD) {
> >> + debug("%s: Reading data\n", __func__);
> >> + ret = twsi_read_data(twsi->base, msg->addr,
> >> + msg->buf, msg->len);
> >> + } else {
> >> + debug("%s: Writing data\n", __func__);
> >> + ret = twsi_write_data(twsi->base, msg->addr,
> >> + msg->buf, msg->len);
> >> + }
> >> + if (ret) {
> >> + debug("%s: error sending\n", __func__);
> >> + return -EREMOTEIO;
> >> + }
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Set I2C bus speed
> >> + *
> >> + * @bus i2c bus to transfer data over
> >> + * @speed Speed in Hz to set
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
> >> +{
> >> + struct octeon_twsi *twsi = dev_get_priv(bus);
> >> + int m_div, n_div;
> >> + u64 val;
> >> +
> >> + debug("%s(%p, %u)\n", __func__, bus, speed);
> >> +
> >> + twsi_calc_div(speed, &m_div, &n_div);
> >> + if (m_div >= 16)
> >> + return -1;
> >> +
> >> + val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
> >> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
> >> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
> >> + TWSI_SW_V;
> >> + /* Only init non-slave ports */
> >> + writeq(val, twsi->base + TWSI_SW_TWSI);
> >> +
> >> + debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
> >> + return 0;
> >> +}
> >> +
> >> +/**
> >> + * Driver probe function
> >> + *
> >> + * @dev I2C device to probe
> >> + * @return 0 for success, otherwise error
> >> + */
> >> +static int octeon_pci_i2c_probe(struct udevice *dev)
> >> +{
> >> + struct octeon_twsi *twsi = dev_get_priv(dev);
> >> +#if !defined(CONFIG_ARCH_OCTEON)
> >> + pci_dev_t bdf = dm_pci_get_bdf(dev);
> >> +
> >> + debug("TWSI PCI device: %x\n", bdf);
> >> + dev->req_seq = PCI_FUNC(bdf);
> >> +
> >> + twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
> >> + PCI_REGION_MEM);
> >> +#else
> >> + twsi->base = dev_remap_addr(dev);
> >> +#endif
> >> + debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
> >> +
> >> + /* Start with standard speed, real speed set via DT or cmd */
> >> + return twsi_init(twsi->base, CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
> >> +}
> >> +
> >> +static const struct dm_i2c_ops octeon_i2c_ops = {
> >> + .xfer = octeon_i2c_xfer,
> >> + .set_bus_speed = octeon_i2c_set_bus_speed,
> >> +};
> >> +
> >> +static const struct udevice_id octeon_i2c_ids[] = {
> >> + { .compatible = "cavium,thunder-8890-twsi" },
> >> + { .compatible = "cavium,octeon-7890-twsi" },
> >> + { }
> >> +};
> >> +
> >> +U_BOOT_DRIVER(octeon_pci_twsi) = {
> >> + .name = "i2c_octeon",
> >> + .id = UCLASS_I2C,
> >> + .of_match = octeon_i2c_ids,
> >> + .probe = octeon_pci_i2c_probe,
> >> + .priv_auto_alloc_size = sizeof(struct octeon_twsi),
> >> + .ops = &octeon_i2c_ops,
> >> +};
> >> +
> >> +#if !defined(CONFIG_ARCH_OCTEON)
> >> +static struct pci_device_id octeon_twsi_supported[] = {
> >> + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI) },
> >> + { },
> >> +};
> >> +
> >> +U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);
> >> +#endif
> >> --
> >> 2.26.2
> >>
>
>
> Viele Gr??e,
> Stefan
>
> --
> DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon
2020-06-03 5:37 ` Rayagonda Kokatanur
@ 2020-06-03 5:39 ` Stefan Roese
0 siblings, 0 replies; 9+ messages in thread
From: Stefan Roese @ 2020-06-03 5:39 UTC (permalink / raw)
To: u-boot
On 03.06.20 07:37, Rayagonda Kokatanur wrote:
> On Wed, Jun 3, 2020 at 10:50 AM Stefan Roese <sr@denx.de> wrote:
>>
>> Hi Rayagonda,
>>
>> v1 is superseeded. Please review the latest version, v2:
>>
>> https://patchwork.ozlabs.org/project/uboot/patch/20200526121307.2735-1-sr at denx.de/
>
> Thank you, I missed that v2.
> Anyway please ignore my comments, all of them are fixed in v2.
Good. Could you please send a Reviewed-by tag then?
Thanks,
Stefan
> Thanks,
> Rayagonda
>>
>> Thanks,
>> Stefan
>>
>> On 03.06.20 07:15, Rayagonda Kokatanur wrote:
>>> Hi Stefan,
>>>
>>> Few minor comments,
>>>
>>> On Thu, May 14, 2020 at 12:53 PM Stefan Roese <sr@denx.de> wrote:
>>>>
>>>> From: Suneel Garapati <sgarapati@marvell.com>
>>>>
>>>> Add support for I2C controllers found on Octeon II/III and Octeon TX
>>>> TX2 SoC platforms.
>>>>
>>>> Signed-off-by: Aaron Williams <awilliams@marvell.com>
>>>> Signed-off-by: Suneel Garapati <sgarapati@marvell.com>
>>>> Signed-off-by: Stefan Roese <sr@denx.de>
>>>> Cc: Heiko Schocher <hs@denx.de>
>>>> Cc: Simon Glass <sjg@chromium.org>
>>>> Cc: Daniel Schwierzeck <daniel.schwierzeck@gmail.com>
>>>> Cc: Aaron Williams <awilliams@marvell.com>
>>>> Cc: Chandrakala Chavva <cchavva@marvell.com>
>>>> ---
>>>> RFC -> v1 (Stefan):
>>>> - Separated this patch from the OcteonTX/TX2 RFC patch series into a
>>>> single patch. This is useful, as the upcoming MIPS Octeon support will
>>>> use this I2C driver.
>>>> - Added MIPS Octeon II/III support (big endian). Rename driver and its
>>>> function names from "octeontx" to "octeon" to better match all Octeon
>>>> platforms.
>>>> - Moved from union to defines / bitmasks as suggested by Simon. This makes
>>>> the driver usage on little- and big-endian platforms much easier.
>>>> - Enhanced Kconfig text
>>>> - Removed all clock macros (use values from DT)
>>>> - Removed long driver debug strings. This is only available when a debug
>>>> version of this driver is built. The user / developer can lookup the
>>>> descriptive error messages in the driver in this case anyway.
>>>> - Removed static "last_id"
>>>> - Dropped misc blank lines. Misc reformatting.
>>>> - Dropped "!= 0"
>>>> - Added missing function comments
>>>> - Added missing strut comments
>>>> - Changed comment style
>>>> - Renames "result" to "ret"
>>>> - Hex numbers uppercase
>>>> - Minor other changes
>>>> - Reword commit text and subject
>>>>
>>>> drivers/i2c/Kconfig | 10 +
>>>> drivers/i2c/Makefile | 1 +
>>>> drivers/i2c/octeon_i2c.c | 803 +++++++++++++++++++++++++++++++++++++++
>>>> 3 files changed, 814 insertions(+)
>>>> create mode 100644 drivers/i2c/octeon_i2c.c
>>>>
>>>> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
>>>> index e42b6516bf..1330b36698 100644
>>>> --- a/drivers/i2c/Kconfig
>>>> +++ b/drivers/i2c/Kconfig
>>>> @@ -374,6 +374,16 @@ config SYS_I2C_SANDBOX
>>>> bus. Devices can be attached to the bus using the device tree
>>>> which specifies the driver to use. See sandbox.dts as an example.
>>>>
>>>> +config SYS_I2C_OCTEON
>>>> + bool "Octeon II/III/TX/TX2 I2C driver"
>>>> + depends on (ARCH_OCTEON || ARCH_OCTEONTX || ARCH_OCTEONTX2) && DM_I2C
>>>> + default y
>>>> + help
>>>> + Add support for the Marvell Octeon I2C driver. This is used with
>>>> + various Octeon parts such as Octeon II/III and OcteonTX/TX2. All
>>>> + chips have several I2C ports and all are provided, controlled by
>>>> + the device tree.
>>>> +
>>>> config SYS_I2C_S3C24X0
>>>> bool "Samsung I2C driver"
>>>> depends on ARCH_EXYNOS4 && DM_I2C
>>>> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
>>>> index 62935b7ebc..2b58aae892 100644
>>>> --- a/drivers/i2c/Makefile
>>>> +++ b/drivers/i2c/Makefile
>>>> @@ -27,6 +27,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
>>>> obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
>>>> obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
>>>> obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
>>>> +obj-$(CONFIG_SYS_I2C_OCTEON) += octeon_i2c.o
>>>> obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
>>>> obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
>>>> obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
>>>> diff --git a/drivers/i2c/octeon_i2c.c b/drivers/i2c/octeon_i2c.c
>>>> new file mode 100644
>>>> index 0000000000..210f98655e
>>>> --- /dev/null
>>>> +++ b/drivers/i2c/octeon_i2c.c
>>>> @@ -0,0 +1,803 @@
>>>> +// SPDX-License-Identifier: GPL-2.0
>>>> +/*
>>>> + * Copyright (C) 2018 Marvell International Ltd.
>>>
>>> Should it be 2020 ?
>>>> + */
>>>> +
>>>> +#include <common.h>
>>>> +#include <i2c.h>
>>>> +#include <dm.h>
>>>> +#include <pci_ids.h>
>>>> +#include <asm/io.h>
>>>> +#include <asm/arch/clock.h>
>>>> +#include <linux/bitfield.h>
>>>> +
>>>> +/*
>>>> + * Octeon II/III (MIPS) have different register offsets than the ARM based
>>>> + * Octeon TX/TX2 SoCs
>>>> + */
>>>> +#if defined(CONFIG_ARCH_OCTEON)
>>>> +#define REG_OFFS 0x0000
>>>> +#else
>>>> +#define REG_OFFS 0x1000
>>>> +#endif
>>>
>>> How about getting this offset from dt ?
>>>> +
>>>> +#define TWSI_SW_TWSI (REG_OFFS + 0x00)
>>>> +#define TWSI_TWSI_SW (REG_OFFS + 0x08)
>>>> +#define TWSI_INT (REG_OFFS + 0x10)
>>>> +#define TWSI_SW_TWSI_EXT (REG_OFFS + 0x18)
>>>> +
>>>> +#define TWSI_SW_DATA_MASK GENMASK_ULL(31, 0)
>>>> +#define TWSI_SW_EOP_IA_MASK GENMASK_ULL(34, 32)
>>>> +#define TWSI_SW_IA_MASK GENMASK_ULL(39, 35)
>>>> +#define TWSI_SW_ADDR_MASK GENMASK_ULL(49, 40)
>>>> +#define TWSI_SW_SCR_MASK GENMASK_ULL(51, 50)
>>>> +#define TWSI_SW_SIZE_MASK GENMASK_ULL(54, 52)
>>>> +#define TWSI_SW_SOVR BIT_ULL(55)
>>>> +#define TWSI_SW_R BIT_ULL(56)
>>>> +#define TWSI_SW_OP_MASK GENMASK_ULL(60, 57)
>>>> +#define TWSI_SW_EIA GENMASK_ULL(61)
>>>> +#define TWSI_SW_SLONLY BIT_ULL(62)
>>>> +#define TWSI_SW_V BIT_ULL(63)
>>>> +
>>>> +#define TWSI_INT_SDA_OVR BIT_ULL(8)
>>>> +#define TWSI_INT_SCL_OVR BIT_ULL(9)
>>>> +#define TWSI_INT_SDA BIT_ULL(10)
>>>> +#define TWSI_INT_SCL BIT_ULL(11)
>>>> +
>>>> +enum {
>>>> + TWSI_OP_WRITE = 0,
>>>> + TWSI_OP_READ = 1,
>>>> +};
>>>> +
>>>> +enum {
>>>> + TWSI_EOP_SLAVE_ADDR = 0,
>>>> + TWSI_EOP_CLK_CTL = 3,
>>>> + TWSI_SW_EOP_IA = 6,
>>>> +};
>>>> +
>>>> +enum {
>>>> + TWSI_SLAVEADD = 0,
>>>> + TWSI_DATA = 1,
>>>> + TWSI_CTL = 2,
>>>> + TWSI_CLKCTL = 3,
>>>> + TWSI_STAT = 3,
>>>> + TWSI_SLAVEADD_EXT = 4,
>>>> + TWSI_RST = 7,
>>>> +};
>>>> +
>>>> +enum {
>>>> + TWSI_CTL_AAK = BIT(2),
>>>> + TWSI_CTL_IFLG = BIT(3),
>>>> + TWSI_CTL_STP = BIT(4),
>>>> + TWSI_CTL_STA = BIT(5),
>>>> + TWSI_CTL_ENAB = BIT(6),
>>>> + TWSI_CTL_CE = BIT(7),
>>>> +};
>>>> +
>>>> +/*
>>>> + * Internal errors. When debugging is enabled, the driver will report the
>>>> + * error number and the user / developer can check the table below for the
>>>> + * detailed error description.
>>>> + */
>>>> +enum {
>>>> + /** Bus error */
>>>> + TWSI_STAT_BUS_ERROR = 0x00,
>>>> + /** Start condition transmitted */
>>>> + TWSI_STAT_START = 0x08,
>>>> + /** Repeat start condition transmitted */
>>>> + TWSI_STAT_RSTART = 0x10,
>>>> + /** Address + write bit transmitted, ACK received */
>>>> + TWSI_STAT_TXADDR_ACK = 0x18,
>>>> + /** Address + write bit transmitted, /ACK received */
>>>> + TWSI_STAT_TXADDR_NAK = 0x20,
>>>> + /** Data byte transmitted in master mode, ACK received */
>>>> + TWSI_STAT_TXDATA_ACK = 0x28,
>>>> + /** Data byte transmitted in master mode, ACK received */
>>>> + TWSI_STAT_TXDATA_NAK = 0x30,
>>>> + /** Arbitration lost in address or data byte */
>>>> + TWSI_STAT_TX_ARB_LOST = 0x38,
>>>> + /** Address + read bit transmitted, ACK received */
>>>> + TWSI_STAT_RXADDR_ACK = 0x40,
>>>> + /** Address + read bit transmitted, /ACK received */
>>>> + TWSI_STAT_RXADDR_NAK = 0x48,
>>>> + /** Data byte received in master mode, ACK transmitted */
>>>> + TWSI_STAT_RXDATA_ACK_SENT = 0x50,
>>>> + /** Data byte received, NACK transmitted */
>>>> + TWSI_STAT_RXDATA_NAK_SENT = 0x58,
>>>> + /** Slave address received, sent ACK */
>>>> + TWSI_STAT_SLAVE_RXADDR_ACK = 0x60,
>>>> + /**
>>>> + * Arbitration lost in address as master, slave address + write bit
>>>> + * received, ACK transmitted
>>>> + */
>>>> + TWSI_STAT_TX_ACK_ARB_LOST = 0x68,
>>>> + /** General call address received, ACK transmitted */
>>>> + TWSI_STAT_RX_GEN_ADDR_ACK = 0x70,
>>>> + /**
>>>> + * Arbitration lost in address as master, general call address
>>>> + * received, ACK transmitted
>>>> + */
>>>> + TWSI_STAT_RX_GEN_ADDR_ARB_LOST = 0x78,
>>>> + /** Data byte received after slave address received, ACK transmitted */
>>>> + TWSI_STAT_SLAVE_RXDATA_ACK = 0x80,
>>>> + /** Data byte received after slave address received, /ACK transmitted */
>>>> + TWSI_STAT_SLAVE_RXDATA_NAK = 0x88,
>>>> + /**
>>>> + * Data byte received after general call address received, ACK
>>>> + * transmitted
>>>> + */
>>>> + TWSI_STAT_GEN_RXADDR_ACK = 0x90,
>>>> + /**
>>>> + * Data byte received after general call address received, /ACK
>>>> + * transmitted
>>>> + */
>>>> + TWSI_STAT_GEN_RXADDR_NAK = 0x98,
>>>> + /** STOP or repeated START condition received in slave mode */
>>>> + TWSI_STAT_STOP_MULTI_START = 0xa0,
>>>> + /** Slave address + read bit received, ACK transmitted */
>>>> + TWSI_STAT_SLAVE_RXADDR2_ACK = 0xa8,
>>>> + /**
>>>> + * Arbitration lost in address as master, slave address + read bit
>>>> + * received, ACK transmitted
>>>> + */
>>>> + TWSI_STAT_RXDATA_ACK_ARB_LOST = 0xb0,
>>>> + /** Data byte transmitted in slave mode, ACK received */
>>>> + TWSI_STAT_SLAVE_TXDATA_ACK = 0xb8,
>>>> + /** Data byte transmitted in slave mode, /ACK received */
>>>> + TWSI_STAT_SLAVE_TXDATA_NAK = 0xc0,
>>>> + /** Last byte transmitted in slave mode, ACK received */
>>>> + TWSI_STAT_SLAVE_TXDATA_END_ACK = 0xc8,
>>>> + /** Second address byte + write bit transmitted, ACK received */
>>>> + TWSI_STAT_TXADDR2DATA_ACK = 0xd0,
>>>> + /** Second address byte + write bit transmitted, /ACK received */
>>>> + TWSI_STAT_TXADDR2DATA_NAK = 0xd8,
>>>> + /** No relevant status information */
>>>> + TWSI_STAT_IDLE = 0xf8
>>>> +};
>>>> +
>>>> +#ifndef CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR
>>>> +# define CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR 0x77
>>>> +#endif
>>>> +
>>>> +/**
>>>> + * struct octeon_twsi - Private data of this driver
>>>> + *
>>>> + * @base: Base address of i2c registers
>>>> + */
>>>> +struct octeon_twsi {
>>>> + void __iomem *base;
>>>> +};
>>>> +
>>>> +static void twsi_unblock(void *base);
>>>> +static int twsi_stop(void *base);
>>>> +
>>>> +#if defined(CONFIG_ARCH_OCTEON)
>>>> +static int get_io_clock(void)
>>>> +{
>>>> + return octeon_get_io_clock();
>>>> +}
>>>> +#else
>>>> +static int get_io_clock(void)
>>>> +{
>>>> + return octeontx_get_io_clock();
>>>> +}
>>>> +#endif
>>>> +
>>>> +static int twsi_thp(void)
>>>> +{
>>>> + if (IS_ENABLED(CONFIG_ARCH_OCTEON) || IS_ENABLED(CONFIG_ARCH_OCTEONTX))
>>>> + return 24;
>>>> + else
>>>> + return 3;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Returns true if we lost arbitration
>>>> + *
>>>> + * @code status code
>>>> + * @final_read true if this is the final read operation
>>>> + * @return true if arbitration has been lost, false if it hasn't been lost.
>>>
>>> Instead of true/false, better to mention 0 on success and -ve on failure.
>>>> + */
>>>> +static int twsi_i2c_lost_arb(u8 code, int final_read)
>>>> +{
>>>> + switch (code) {
>>>> + case TWSI_STAT_TX_ARB_LOST:
>>>> + case TWSI_STAT_TX_ACK_ARB_LOST:
>>>> + case TWSI_STAT_RX_GEN_ADDR_ARB_LOST:
>>>> + case TWSI_STAT_RXDATA_ACK_ARB_LOST:
>>>> + /* Arbitration lost */
>>>> + return -EAGAIN;
>>>> +
>>>> + case TWSI_STAT_SLAVE_RXADDR_ACK:
>>>> + case TWSI_STAT_RX_GEN_ADDR_ACK:
>>>> + case TWSI_STAT_GEN_RXADDR_ACK:
>>>> + case TWSI_STAT_GEN_RXADDR_NAK:
>>>> + /* Being addressed as slave, should back off and listen */
>>>> + return -EIO;
>>>> +
>>>> + case TWSI_STAT_SLAVE_RXDATA_ACK:
>>>> + case TWSI_STAT_SLAVE_RXDATA_NAK:
>>>> + case TWSI_STAT_STOP_MULTI_START:
>>>> + case TWSI_STAT_SLAVE_RXADDR2_ACK:
>>>> + case TWSI_STAT_SLAVE_TXDATA_ACK:
>>>> + case TWSI_STAT_SLAVE_TXDATA_NAK:
>>>> + case TWSI_STAT_SLAVE_TXDATA_END_ACK:
>>>> + /* Core busy as slave */
>>>> + return -EIO;
>>>> +
>>>> + case TWSI_STAT_RXDATA_ACK_SENT:
>>>> + /* Ack allowed on pre-terminal bytes only */
>>>> + if (!final_read)
>>>> + return 0;
>>>> + return -EAGAIN;
>>>> +
>>>> + case TWSI_STAT_RXDATA_NAK_SENT:
>>>> + /* NAK allowed on terminal byte only */
>>>> + if (!final_read)
>>>> + return 0;
>>>> + return -EAGAIN;
>>>> +
>>>> + case TWSI_STAT_TXDATA_NAK:
>>>> + case TWSI_STAT_TXADDR_NAK:
>>>> + case TWSI_STAT_RXADDR_NAK:
>>>> + case TWSI_STAT_TXADDR2DATA_NAK:
>>>> + return -EAGAIN;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +#define RST_BOOT_PNR_MUL(val) (((val) >> 33) & 0x1F)
>>>
>>> Better to move this up where all macros are defined.
>>>
>>>> +
>>>> +/**
>>>> + * Writes to the MIO_TWS(0..5)_SW_TWSI register
>>>> + *
>>>> + * @base Base address of i2c registers
>>>> + * @val value to write
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static u64 twsi_write_sw(void __iomem *base, u64 val)
>>>> +{
>>>> + unsigned long start = get_timer(0);
>>>> +
>>>> + val &= ~TWSI_SW_R;
>>>> + val |= TWSI_SW_V;
>>>> +
>>>> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
>>>> + writeq(val, base + TWSI_SW_TWSI);
>>>> + do {
>>>> + val = readq(base + TWSI_SW_TWSI);
>>>> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
>>>> +
>>>> + if (val & TWSI_SW_V)
>>>> + debug("%s: timed out\n", __func__);
>>>> + return val;
>>>
>>> newline before return, please fix globally.
>>>> +}
>>>> +
>>>> +/**
>>>> + * Reads the MIO_TWS(0..5)_SW_TWSI register
>>>> + *
>>>> + * @base Base address of i2c registers
>>>> + * @val value for eia and op, etc. to read
>>>> + * @return value of the register
>>>> + */
>>>> +static u64 twsi_read_sw(void __iomem *base, u64 val)
>>>> +{
>>>> + unsigned long start = get_timer(0);
>>>> +
>>>> + val |= TWSI_SW_R | TWSI_SW_V;
>>>> +
>>>> + debug("%s(%p, 0x%llx)\n", __func__, base, val);
>>>> + writeq(val, base + TWSI_SW_TWSI);
>>>> +
>>>> + do {
>>>> + val = readq(base + TWSI_SW_TWSI);
>>>> + } while ((val & TWSI_SW_V) && (get_timer(start) < 50));
>>>> +
>>>> + if (val & TWSI_SW_V)
>>>> + debug("%s: Error writing 0x%llx\n", __func__, val);
>>>> +
>>>> + debug("%s: Returning 0x%llx\n", __func__, val);
>>>> + return val;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Write control register
>>>> + *
>>>> + * @base Base address for i2c registers
>>>> + * @data data to write
>>>> + */
>>>> +static void twsi_write_ctl(void __iomem *base, u8 data)
>>>> +{
>>>> + u64 val;
>>>> +
>>>> + debug("%s(%p, 0x%x)\n", __func__, base, data);
>>>> + val = data | FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>>>> + twsi_write_sw(base, val);
>>>> +}
>>>> +
>>>> +/**
>>>> + * Reads the TWSI Control Register
>>>> + *
>>>> + * @base Base address for i2c
>>>> + * @return 8-bit TWSI control register
>>>> + */
>>>> +static u8 twsi_read_ctl(void __iomem *base)
>>>> +{
>>>> + u64 val;
>>>> +
>>>> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CTL) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>>>> + val = twsi_read_sw(base, val);
>>>> +
>>>> + debug("%s(%p): 0x%x\n", __func__, base, (u8)val);
>>>> + return (u8)val;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Read i2c status register
>>>> + *
>>>> + * @base Base address of i2c registers
>>>> + * @return value of status register
>>>> + */
>>>> +static u8 twsi_read_status(void __iomem *base)
>>>> +{
>>>> + u64 val;
>>>> +
>>>> + val = FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_STAT) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>>>> +
>>>> + return twsi_read_sw(base, val);
>>>> +}
>>>> +
>>>> +/**
>>>> + * Waits for an i2c operation to complete
>>>> + *
>>>> + * @param base Base address of registers
>>>> + * @return 0 for success, 1 if timeout
>>>> + */
>>>> +static int twsi_wait(void __iomem *base)
>>>> +{
>>>> + unsigned long start = get_timer(0);
>>>> + u8 twsi_ctl;
>>>> +
>>>> + debug("%s(%p)\n", __func__, base);
>>>> + do {
>>>> + twsi_ctl = twsi_read_ctl(base);
>>>> + twsi_ctl &= TWSI_CTL_IFLG;
>>>> + } while (!twsi_ctl && get_timer(start) < 50);
>>>> +
>>>> + debug(" return: %u\n", !twsi_ctl);
>>>> + return !twsi_ctl;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Unsticks the i2c bus
>>>> + *
>>>> + * @base base address of registers
>>>> + */
>>>> +static int twsi_start_unstick(void __iomem *base)
>>>> +{
>>>> + twsi_stop(base);
>>>> + twsi_unblock(base);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Sends an i2c start condition
>>>> + *
>>>> + * @base base address of registers
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int twsi_start(void __iomem *base)
>>>> +{
>>>> + int ret;
>>>> + u8 stat;
>>>> +
>>>> + debug("%s(%p)\n", __func__, base);
>>>> + twsi_write_ctl(base, TWSI_CTL_STA | TWSI_CTL_ENAB);
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + stat = twsi_read_status(base);
>>>> + debug("%s: ret: 0x%x, status: 0x%x\n", __func__, ret, stat);
>>>> + switch (stat) {
>>>> + case TWSI_STAT_START:
>>>> + case TWSI_STAT_RSTART:
>>>> + return 0;
>>>> + case TWSI_STAT_RXADDR_ACK:
>>>> + default:
>>>> + return twsi_start_unstick(base);
>>>> + }
>>>> + }
>>>> +
>>>> + debug("%s: success\n", __func__);
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Sends an i2c stop condition
>>>> + *
>>>> + * @base register base address
>>>> + * @return 0 for success, -1 if error
>>>> + */
>>>> +static int twsi_stop(void __iomem *base)
>>>> +{
>>>> + u8 stat;
>>>> +
>>>> + twsi_write_ctl(base, TWSI_CTL_STP | TWSI_CTL_ENAB);
>>>> +
>>>> + stat = twsi_read_status(base);
>>>> + if (stat != TWSI_STAT_IDLE) {
>>>> + debug("%s: Bad status on bus@%p\n", __func__, base);
>>>> + return -1;
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Writes data to the i2c bus
>>>> + *
>>>> + * @base register base address
>>>> + * @slave_addr address of slave to write to
>>>> + * @buffer Pointer to buffer to write
>>>> + * @length Number of bytes in buffer to write
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int twsi_write_data(void __iomem *base, u8 slave_addr,
>>>> + u8 *buffer, unsigned int length)
>>>
>>> This should be properly aligned.
>>>
>>>> +{
>>>> + unsigned int curr = 0;
>>>> + u64 val;
>>>> + int ret;
>>>> +
>>>> + debug("%s(%p, 0x%x, %p, 0x%x)\n", __func__, base, slave_addr,
>>>> + buffer, length);
>>>
>>> same alignment.
>>>
>>>> + ret = twsi_start(base);
>>>> + if (ret) {
>>>> + debug("%s: Could not start BUS transaction\n", __func__);
>>>> + return -1;
>>>> + }
>>>> +
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + debug("%s: wait failed\n", __func__);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + val = (u32)(slave_addr << 1) | TWSI_OP_WRITE |
>>>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>>>> + twsi_write_sw(base, val);
>>>> + twsi_write_ctl(base, TWSI_CTL_ENAB);
>>>> +
>>>> + debug("%s: Waiting\n", __func__);
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + debug("%s: Timed out writing slave address 0x%x to target\n",
>>>> + __func__, slave_addr);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = twsi_read_status(base);
>>>> + debug("%s: status: 0x%x\n", __func__, ret);
>>>> + if (ret != TWSI_STAT_TXADDR_ACK) {
>>>> + debug("%s: status: 0x%x\n", __func__, ret);
>>>> + twsi_stop(base);
>>>> + return twsi_i2c_lost_arb(ret, 0);
>>>> + }
>>>> +
>>>> + while (curr < length) {
>>>> + val = buffer[curr++] |
>>>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>>>> + twsi_write_sw(base, val);
>>>> + twsi_write_ctl(base, TWSI_CTL_ENAB);
>>>> +
>>>> + debug("%s: Writing 0x%llx\n", __func__, val);
>>>> +
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + debug("%s: Timed out writing data to 0x%x\n",
>>>> + __func__, slave_addr);
>>>> + return ret;
>>>> + }
>>>> + ret = twsi_read_status(base);
>>>> + debug("%s: status: 0x%x\n", __func__, ret);
>>>> + }
>>>> +
>>>> + debug("%s: Stopping\n", __func__);
>>>> + return twsi_stop(base);
>>>> +}
>>>> +
>>>> +/**
>>>> + * Manually clear the I2C bus and send a stop
>>>> + *
>>>> + * @base register base address
>>>> + */
>>>> +static void twsi_unblock(void __iomem *base)
>>>> +{
>>>> + int i;
>>>> +
>>>> + for (i = 0; i < 9; i++) {
>>>> + writeq(0, base + TWSI_INT);
>>>> + udelay(5);
>>>> + writeq(TWSI_INT_SCL_OVR, base + TWSI_INT);
>>>> + udelay(5);
>>>> + }
>>>> + writeq(TWSI_INT_SCL_OVR | TWSI_INT_SDA_OVR, base + TWSI_INT);
>>>> + udelay(5);
>>>> + writeq(TWSI_INT_SDA_OVR, base + TWSI_INT);
>>>> + udelay(5);
>>>> + writeq(0, base + TWSI_INT);
>>>> + udelay(5);
>>>> +}
>>>> +
>>>> +/**
>>>> + * Performs a read transaction on the i2c bus
>>>> + *
>>>> + * @base Base address of twsi registers
>>>> + * @slave_addr i2c bus address to read from
>>>> + * @buffer buffer to read into
>>>> + * @length number of bytes to read
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int twsi_read_data(void __iomem *base, u8 slave_addr,
>>>> + u8 *buffer, unsigned int length)
>>>> +{
>>>> + unsigned int curr = 0;
>>>> + u64 val;
>>>> + int ret;
>>>> +
>>>> + debug("%s(%p, 0x%x, %p, %u)\n", __func__, base, slave_addr,
>>>> + buffer, length);
>>>> + ret = twsi_start(base);
>>>> + if (ret) {
>>>> + debug("%s: start failed\n", __func__);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + debug("%s: wait failed\n", __func__);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + val = (u32)(slave_addr << 1) | TWSI_OP_READ |
>>>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_DATA) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA);
>>>> + twsi_write_sw(base, val);
>>>> + twsi_write_ctl(base, TWSI_CTL_ENAB);
>>>> +
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + debug("%s: waiting for sending addr failed\n", __func__);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + ret = twsi_read_status(base);
>>>> + debug("%s: status: 0x%x\n", __func__, ret);
>>>> + if (ret != TWSI_STAT_RXADDR_ACK) {
>>>> + debug("%s: status: 0x%x\n", __func__, ret);
>>>> + twsi_stop(base);
>>>> + return twsi_i2c_lost_arb(ret, 0);
>>>> + }
>>>> +
>>>> + while (curr < length) {
>>>> + twsi_write_ctl(base, TWSI_CTL_ENAB |
>>>> + ((curr < length - 1) ? TWSI_CTL_AAK : 0));
>>>> +
>>>> + ret = twsi_wait(base);
>>>> + if (ret) {
>>>> + debug("%s: waiting for data failed\n", __func__);
>>>> + return ret;
>>>> + }
>>>> +
>>>> + val = twsi_read_sw(base, val);
>>>> + buffer[curr++] = (u8)val;
>>>> + }
>>>> +
>>>> + twsi_stop(base);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Calculate the divisor values
>>>> + *
>>>> + * @speed Speed to set
>>>> + * @m_div Pointer to M divisor
>>>> + * @n_div Pointer to N divisor
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static void twsi_calc_div(unsigned int speed, int *m_div, int *n_div)
>>>> +{
>>>> + int io_clock_hz;
>>>> + int tclk, fsamp;
>>>> + int ndiv, mdiv;
>>>> +
>>>> +#if defined(CONFIG_ARCH_OCTEON) || defined(CONFIG_ARCH_OCTEONTX)
>>>> + io_clock_hz = get_io_clock();
>>>> + tclk = io_clock_hz / (2 * (twsi_thp() + 1));
>>>> +#elif defined(CONFIG_ARCH_OCTEONTX2)
>>>> + /* Refclk src in mode register defaults to 100MHz clock */
>>>> + io_clock_hz = 100000000; /* 100 Mhz */
>>>> + tclk = io_clock_hz / (twsi_thp() + 2);
>>>> +#endif
>>>> + debug("%s( io_clock %u tclk %u)\n", __func__, io_clock_hz, tclk);
>>>> +
>>>> + /*
>>>> + * Compute the clocks M divider:
>>>> + *
>>>> + * TWSI freq = (core freq) / (10 x (M+1) x 2 * (thp+1) x 2^N)
>>>> + * M = ((core freq) / (10 x (TWSI freq) x 2 * (thp+1) x 2^N)) - 1
>>>> + *
>>>> + * For OcteonTX2 -
>>>> + * TWSI freq = (core freq) / (10 x (M+1) x (thp+2) x 2^N)
>>>> + * M = ((core freq) / (10 x (TWSI freq) x (thp+2) x 2^N)) - 1
>>>> + */
>>>> + for (ndiv = 0; ndiv < 8; ndiv++) {
>>>> + fsamp = tclk / (1 << ndiv);
>>>> + mdiv = fsamp / speed / 10;
>>>> + mdiv -= 1;
>>>> + if (mdiv < 16)
>>>> + break;
>>>> + }
>>>> +
>>>> + *m_div = mdiv;
>>>> + *n_div = ndiv;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Init I2C controller
>>>> + *
>>>> + * @base Base address of twsi registers
>>>> + * @slave_addr I2C slave address to configure this controller to
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int twsi_init(void __iomem *base, int slaveaddr)
>>>> +{
>>>> + u64 val;
>>>> +
>>>> + debug("%s (%p, 0x%x)\n", __func__, base, slaveaddr);
>>>> +
>>>> + val = slaveaddr << 1 |
>>>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, 0) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
>>>> + TWSI_SW_V;
>>>> + twsi_write_sw(base, val);
>>>> +
>>>> + /* Set slave address */
>>>> + val = slaveaddr |
>>>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_EOP_SLAVE_ADDR) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
>>>> + TWSI_SW_V;
>>>> + twsi_write_sw(base, val);
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Transfers data over the i2c bus
>>>> + *
>>>> + * @bus i2c bus to transfer data over
>>>> + * @msg Array of i2c messages
>>>> + * @nmsgs Number of messages to send/receive
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int octeon_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
>>>> + int nmsgs)
>>>> +{
>>>> + struct octeon_twsi *twsi = dev_get_priv(bus);
>>>> + int ret;
>>>> + int i;
>>>> +
>>>> + debug("%s: %d messages\n", __func__, nmsgs);
>>>> + for (i = 0; i < nmsgs; i++, msg++) {
>>>> + debug("%s: chip=0x%x, len=0x%x\n", __func__, msg->addr,
>>>> + msg->len);
>>>> +
>>>> + if (msg->flags & I2C_M_RD) {
>>>> + debug("%s: Reading data\n", __func__);
>>>> + ret = twsi_read_data(twsi->base, msg->addr,
>>>> + msg->buf, msg->len);
>>>> + } else {
>>>> + debug("%s: Writing data\n", __func__);
>>>> + ret = twsi_write_data(twsi->base, msg->addr,
>>>> + msg->buf, msg->len);
>>>> + }
>>>> + if (ret) {
>>>> + debug("%s: error sending\n", __func__);
>>>> + return -EREMOTEIO;
>>>> + }
>>>> + }
>>>> +
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Set I2C bus speed
>>>> + *
>>>> + * @bus i2c bus to transfer data over
>>>> + * @speed Speed in Hz to set
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int octeon_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
>>>> +{
>>>> + struct octeon_twsi *twsi = dev_get_priv(bus);
>>>> + int m_div, n_div;
>>>> + u64 val;
>>>> +
>>>> + debug("%s(%p, %u)\n", __func__, bus, speed);
>>>> +
>>>> + twsi_calc_div(speed, &m_div, &n_div);
>>>> + if (m_div >= 16)
>>>> + return -1;
>>>> +
>>>> + val = (u32)(((m_div & 0xf) << 3) | ((n_div & 0x7) << 0)) |
>>>> + FIELD_PREP(TWSI_SW_EOP_IA_MASK, TWSI_CLKCTL) |
>>>> + FIELD_PREP(TWSI_SW_OP_MASK, TWSI_SW_EOP_IA) |
>>>> + TWSI_SW_V;
>>>> + /* Only init non-slave ports */
>>>> + writeq(val, twsi->base + TWSI_SW_TWSI);
>>>> +
>>>> + debug("%s: Wrote 0x%llx to sw_twsi\n", __func__, val);
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/**
>>>> + * Driver probe function
>>>> + *
>>>> + * @dev I2C device to probe
>>>> + * @return 0 for success, otherwise error
>>>> + */
>>>> +static int octeon_pci_i2c_probe(struct udevice *dev)
>>>> +{
>>>> + struct octeon_twsi *twsi = dev_get_priv(dev);
>>>> +#if !defined(CONFIG_ARCH_OCTEON)
>>>> + pci_dev_t bdf = dm_pci_get_bdf(dev);
>>>> +
>>>> + debug("TWSI PCI device: %x\n", bdf);
>>>> + dev->req_seq = PCI_FUNC(bdf);
>>>> +
>>>> + twsi->base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0,
>>>> + PCI_REGION_MEM);
>>>> +#else
>>>> + twsi->base = dev_remap_addr(dev);
>>>> +#endif
>>>> + debug("TWSI bus %d at %p\n", dev->seq, twsi->base);
>>>> +
>>>> + /* Start with standard speed, real speed set via DT or cmd */
>>>> + return twsi_init(twsi->base, CONFIG_SYS_I2C_OCTEON_SLAVE_ADDR);
>>>> +}
>>>> +
>>>> +static const struct dm_i2c_ops octeon_i2c_ops = {
>>>> + .xfer = octeon_i2c_xfer,
>>>> + .set_bus_speed = octeon_i2c_set_bus_speed,
>>>> +};
>>>> +
>>>> +static const struct udevice_id octeon_i2c_ids[] = {
>>>> + { .compatible = "cavium,thunder-8890-twsi" },
>>>> + { .compatible = "cavium,octeon-7890-twsi" },
>>>> + { }
>>>> +};
>>>> +
>>>> +U_BOOT_DRIVER(octeon_pci_twsi) = {
>>>> + .name = "i2c_octeon",
>>>> + .id = UCLASS_I2C,
>>>> + .of_match = octeon_i2c_ids,
>>>> + .probe = octeon_pci_i2c_probe,
>>>> + .priv_auto_alloc_size = sizeof(struct octeon_twsi),
>>>> + .ops = &octeon_i2c_ops,
>>>> +};
>>>> +
>>>> +#if !defined(CONFIG_ARCH_OCTEON)
>>>> +static struct pci_device_id octeon_twsi_supported[] = {
>>>> + { PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_TWSI) },
>>>> + { },
>>>> +};
>>>> +
>>>> +U_BOOT_PCI_DEVICE(octeon_pci_twsi, octeon_twsi_supported);
>>>> +#endif
>>>> --
>>>> 2.26.2
>>>>
>>
>>
>> Viele Gr??e,
>> Stefan
>>
>> --
>> DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
>> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
>> Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
Viele Gr??e,
Stefan
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2020-06-03 5:39 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-14 7:23 [PATCH v1] i2c: octeon_i2c: Add I2C controller driver for Octeon Stefan Roese
2020-05-19 6:07 ` Heiko Schocher
2020-05-26 6:49 ` Stefan Roese
2020-05-19 12:32 ` Simon Glass
2020-05-26 10:55 ` Stefan Roese
2020-06-03 5:15 ` Rayagonda Kokatanur
2020-06-03 5:20 ` Stefan Roese
2020-06-03 5:37 ` Rayagonda Kokatanur
2020-06-03 5:39 ` Stefan Roese
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.