On Wed, Jun 3, 2020 at 10:50 AM Stefan Roese <s...@denx.de> wrote: > > Hi Rayagonda, > > v1 is superseeded. Please review the latest version, v2: > > https://patchwork.ozlabs.org/project/uboot/patch/20200526121307.2735-1...@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 <s...@denx.de> wrote: > >> > >> From: Suneel Garapati <sgarap...@marvell.com> > >> > >> Add support for I2C controllers found on Octeon II/III and Octeon TX > >> TX2 SoC platforms. > >> > >> Signed-off-by: Aaron Williams <awilli...@marvell.com> > >> Signed-off-by: Suneel Garapati <sgarap...@marvell.com> > >> Signed-off-by: Stefan Roese <s...@denx.de> > >> Cc: Heiko Schocher <h...@denx.de> > >> Cc: Simon Glass <s...@chromium.org> > >> Cc: Daniel Schwierzeck <daniel.schwierz...@gmail.com> > >> Cc: Aaron Williams <awilli...@marvell.com> > >> Cc: Chandrakala Chavva <ccha...@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: s...@denx.de