[PATCH v6 17/19] i2c: thunderx: Add i2c driver for ThunderX SOC
The ThunderX SOC uses the same i2c block as the Octeon SOC. The main difference is that on ThunderX the device is a PCI device so device probing is done via PCI, interrupts are MSIX and the clock is taken from device tree. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile| 2 + drivers/i2c/busses/i2c-cavium.h| 17 ++- drivers/i2c/busses/i2c-thunderx-core.c | 269 + 4 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index faa8e68..92d23de 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -953,6 +953,16 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI && !CAVIUM_OCTEON_SOC + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + config I2C_XILINX tristate "Xilinx I2C Controller" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 282f781..a32ff14 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -93,6 +93,8 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +i2c-thunderx-objs := i2c-cavium.o i2c-thunderx-core.o +obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index e17f2dc..d8d4e0b 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -8,9 +8,15 @@ #include /* Register offsets */ -#define SW_TWSI0x00 -#define TWSI_INT 0x10 -#define SW_TWSI_EXT0x18 +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + #define SW_TWSI 0x1000 + #define TWSI_INT0x1010 + #define SW_TWSI_EXT 0x1018 +#else + #define SW_TWSI 0x00 + #define TWSI_INT0x10 + #define SW_TWSI_EXT 0x18 +#endif /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ @@ -92,6 +98,7 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; + struct clk *clk; int irq; int hlc_irq;/* For cn7890 only */ u32 twsi_freq; @@ -107,6 +114,10 @@ struct octeon_i2c { void (*hlc_int_dis)(struct octeon_i2c *); atomic_t int_en_cnt; atomic_t hlc_int_en_cnt; + +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + struct msix_entry i2c_msix; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c new file mode 100644 index 000..99006a4 --- /dev/null +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -0,0 +1,269 @@ +/* + * Cavium ThunderX i2c driver. + * + * Copyright (C) 2015,2016 Cavium Inc. + * Authors: Fred Martin + * Jan Glauber + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +#define DRV_NAME "i2c-thunderx" + +#define PCI_CFG_REG_BAR_NUM0 +#define PCI_DEVICE_ID_THUNDER_TWSI 0xa012 + +#define TWSI_DFL_RATE 10 +#define SYS_FREQ_DEFAULT 8 + +#define TWSI_INT_ENA_W1C 0x1028 +#define TWSI_INT_ENA_W1S 0x1030 + +/* + * Enable the CORE interrupt. + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void thunder_i2c_int_enable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_CORE_INT, i2c->twsi_base + TWSI_INT_ENA_W1S); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +/* + * Disable the CORE interrupt. + */ +static void thunder_i2c_int_disable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_CORE_INT, i2c->twsi_base + TWSI_INT_ENA_W1C); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_ST_INT | TWSI_INT_TS_INT, +i2c->twsi_base + TWSI_INT_ENA_W1S); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +static void t
[PATCH v6 15/19] i2c: octeon: Rename driver to prepare for split
This is an intermediate commit in preparation of the driver split. The module rename in this commit will be reverted in the next patch, this is just done to make the series bisectible. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile| 2 +- drivers/i2c/busses/{i2c-octeon.c => i2c-octeon-core.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/i2c/busses/{i2c-octeon.c => i2c-octeon-core.c} (100%) diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819..3405286 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,7 @@ obj-$(CONFIG_I2C_UNIPHIER)+= i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon-core.c similarity index 100% rename from drivers/i2c/busses/i2c-octeon.c rename to drivers/i2c/busses/i2c-octeon-core.c -- 1.9.1
[PATCH v6 11/19] i2c: octeon: Flush TWSI writes with readback
From: Peter Swain Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 21 + 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index fff1ac0..8d54fc9 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -127,6 +127,12 @@ struct octeon_i2c { atomic_t hlc_int_en_cnt; }; +static void writeqflush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + /** * octeon_i2c_reg_write - write an I2C core register * @i2c: The struct octeon_i2c @@ -197,8 +203,7 @@ static u64 octeon_i2c_read_int(struct octeon_i2c *i2c) */ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) { - __raw_writeq(data, i2c->twsi_base + TWSI_INT); - __raw_readq(i2c->twsi_base + TWSI_INT); + writeqflush(data, i2c->twsi_base + TWSI_INT); } /** @@ -487,7 +492,7 @@ static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) else cmd |= SW_TWSI_OP_7; - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) goto err; @@ -541,10 +546,10 @@ static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) ext |= (u64) msgs[0].buf[j] << (8 * i); - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); } - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) goto err; @@ -589,7 +594,7 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -649,10 +654,10 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg set_ext = true; } if (set_ext) - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) -- 1.9.1
[PATCH v6 07/19] i2c: octeon: Use i2c recovery framework
Switch to the i2c bus recovery framework using generic SCL recovery. If this fails try to reset the hardware. The recovery is triggered during START on timeout of the interrupt or failure to reach the START / repeated-START condition. The START function is moved to xfer and while at it: - removed xfer debug message (i2c core already provides debugging) - removed length is zero check Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 178 +--- 1 file changed, 111 insertions(+), 67 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 483c8a3..88c9686 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -90,6 +90,8 @@ #define TWSI_INT_CORE_EN BIT_ULL(6) #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) struct octeon_i2c { wait_queue_head_t queue; @@ -152,6 +154,18 @@ static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) #define octeon_i2c_stat_read(i2c) \ octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) + +/** + * octeon_i2c_write_int - read the TWSI_INT register + * @i2c: The struct octeon_i2c + * + * Returns the value of the register. + */ +static u64 octeon_i2c_read_int(struct octeon_i2c *i2c) +{ + return __raw_readq(i2c->twsi_base + TWSI_INT); +} + /** * octeon_i2c_write_int - write the TWSI_INT register * @i2c: The struct octeon_i2c @@ -182,33 +196,6 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } -/** - * octeon_i2c_unblock - unblock the bus - * @i2c: The struct octeon_i2c - * - * If there was a reset while a device was driving 0 to bus, bus is blocked. - * We toggle it free manually by some clock cycles and send a stop. - */ -static void octeon_i2c_unblock(struct octeon_i2c *i2c) -{ - int i; - - dev_dbg(i2c->dev, "%s\n", __func__); - - for (i = 0; i < 9; i++) { - octeon_i2c_write_int(i2c, 0); - udelay(5); - octeon_i2c_write_int(i2c, TWSI_INT_SCL_OVR); - udelay(5); - } - /* hand-crank a STOP */ - octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR | TWSI_INT_SCL_OVR); - udelay(5); - octeon_i2c_write_int(i2c, TWSI_INT_SDA_OVR); - udelay(5); - octeon_i2c_write_int(i2c, 0); -} - /* interrupt service routine */ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { @@ -369,6 +356,17 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) return -EIO; } +static int octeon_i2c_recovery(struct octeon_i2c *i2c) +{ + int ret; + + ret = i2c_recover_bus(>adap); + if (ret) + /* recover failed, try hardware re-init */ + ret = octeon_i2c_init_lowlevel(i2c); + return ret; +} + /** * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c @@ -377,34 +375,34 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) */ static int octeon_i2c_start(struct octeon_i2c *i2c) { - int result; - u8 data; - - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); - - result = octeon_i2c_wait(i2c); - if (result) { - if (octeon_i2c_stat_read(i2c) == STAT_IDLE) { - /* -* Controller refused to send start flag May -* be a client is holding SDA low - let's try -* to free it. -*/ - octeon_i2c_unblock(i2c); - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); - result = octeon_i2c_wait(i2c); + int ret, retries = 2; + u8 stat; + +retry: + if (retries > 0) { + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); + ret = octeon_i2c_wait(i2c); + if (ret) { + /* timeout error, try to recover */ + ret = octeon_i2c_recovery(i2c); + if (ret) + return ret; + retries--; + goto retry; } - if (result) - return result; - } + } else + return -EAGAIN; - data = octeon_i2c_stat_read(i2c); - if ((data != STAT_START) && (data != STAT_RSTART)) { - dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); - return -EIO; - } + stat = octeon_i2c_stat_read(i2c); + if (stat == STAT_START || stat == STAT_REP_START) + /* START successful, bail out */ + return 0; - return 0; + /* START failed, try t
[PATCH v6 08/19] i2c: octeon: Enable High-Level Controller
From: David Daney Use High-Level Controller (HLC) when possible. The HLC can read/write up to 8 bytes and is completely optional. The most important difference of the HLC is that it only requires one interrupt for a transfer (up to 8 bytes) where the low-level read/write requires 2 interrupts plus one interrupt per transferred byte. Since the interrupts are costly using the HLC improves the performance. Also, the HLC provides improved error handling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 355 ++-- 1 file changed, 345 insertions(+), 10 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 88c9686..0bb9f0b 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -29,13 +29,23 @@ /* Register offsets */ #define SW_TWSI0x00 #define TWSI_INT 0x10 +#define SW_TWSI_EXT0x18 /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIABIT_ULL(61) /* Extended internal address */ #define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ /* Controller opcode word (bits 60:57) */ #define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA(1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_TWSI_CLK(4ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ @@ -48,7 +58,7 @@ #define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) /* Controller command and status bits */ -#define TWSI_CTL_CE0x80 +#define TWSI_CTL_CE0x80/* High level controller enable */ #define TWSI_CTL_ENAB 0x40/* Bus enable */ #define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ #define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ @@ -87,6 +97,11 @@ #define STAT_IDLE 0xF8 /* TWSI_INT values */ +#define TWSI_INT_ST_INTBIT_ULL(0) +#define TWSI_INT_TS_INTBIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) #define TWSI_INT_CORE_EN BIT_ULL(6) #define TWSI_INT_SDA_OVR BIT_ULL(8) #define TWSI_INT_SCL_OVR BIT_ULL(9) @@ -101,6 +116,7 @@ struct octeon_i2c { int sys_freq; void __iomem *twsi_base; struct device *dev; + bool hlc_enabled; }; /** @@ -196,6 +212,47 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } +/* + * Cleanup low-level state & enable high-level controller. + */ +static void octeon_i2c_hlc_enable(struct octeon_i2c *i2c) +{ + int try = 0; + u64 val; + + if (i2c->hlc_enabled) + return; + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_ctl_read(i2c); + if (!(val & (TWSI_CTL_STA | TWSI_CTL_STP))); + break; + + /* clear IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } + octeon_i2c_ctl_write(i2c, TWSI_CTL_CE | TWSI_CTL_AAK | TWSI_CTL_ENAB); +} + +static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); +} + /* interrupt service routine */ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { @@ -293,6 +350,253 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) } } +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) +{ + u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); + + return (val & SW_TWSI_V) == 0; +} + +static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, TWSI_INT_ST_EN); +} + +static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) +{ + /* clear ST/TS events, listen for neither */ + octeon_i2c_write_int(i2c, TWSI_INT_ST
[PATCH v6 09/19] dt-bindings: i2c: Add Octeon cn78xx TWSI
Add compatible string for Cavium Octeon cn78XX SOCs TWSI. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Jan Glauber Acked-by: David Daney --- Documentation/devicetree/bindings/i2c/i2c-octeon.txt | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt index dced82e..872d485 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt @@ -4,6 +4,12 @@ Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. + or + + compatible: "cavium,octeon-7890-twsi" + + Compatibility with cn78XX SOCs. + - reg: The base address of the TWSI/I2C bus controller register bank. - #address-cells: Must be <1>. -- 1.9.1
[PATCH v6 16/19] i2c: octeon: Split the driver into two parts
Move common functionality into a separate file in preparation of the re-use from the ThunderX i2c driver. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile | 3 +- drivers/i2c/busses/i2c-cavium.c | 805 + drivers/i2c/busses/i2c-cavium.h | 200 +++ drivers/i2c/busses/i2c-octeon-core.c | 972 +-- 4 files changed, 1008 insertions(+), 972 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3405286..282f781 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,8 @@ obj-$(CONFIG_I2C_UNIPHIER)+= i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o +i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.c b/drivers/i2c/busses/i2c-cavium.c new file mode 100644 index 000..9f4edaa --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.c @@ -0,0 +1,805 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) +{ + u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); + + return (val & SW_TWSI_V) == 0; +} + +static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) +{ + /* clear ST/TS events, listen for neither */ + octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); +} + +/* + * Cleanup low-level state & enable high-level controller. + */ +static void octeon_i2c_hlc_enable(struct octeon_i2c *i2c) +{ + int try = 0; + u64 val; + + if (i2c->hlc_enabled) + return; + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_ctl_read(i2c); + if (!(val & (TWSI_CTL_STA | TWSI_CTL_STP))); + break; + + /* clear IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } + octeon_i2c_ctl_write(i2c, TWSI_CTL_CE | TWSI_CTL_AAK | TWSI_CTL_ENAB); +} + +static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); +} + +#define I2C_OCTEON_IFLG_WAIT 80/* microseconds */ + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + long time_left; + int first = 1; + + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64(
[PATCH v6 13/19] i2c: octeon: Add workaround for broken irqs on CN3860
From: David Daney CN3860 does not interrupt the CPU when the i2c status changes. If we get a timeout, and see the status has in fact changed, we know we have this problem, and drop back to polling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 55 +++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index bad49cf..bfaee37 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -119,6 +119,8 @@ struct octeon_i2c { void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + bool broken_irq_mode; + bool broken_irq_check; void (*int_en)(struct octeon_i2c *); void (*int_dis)(struct octeon_i2c *); void (*hlc_int_en)(struct octeon_i2c *); @@ -378,10 +380,33 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) long time_left; int first = 1; + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + i2c->int_en(i2c); time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -477,17 +502,40 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int time_left; + if (i2c->broken_irq_mode) { + /* +* Some cn38xx boards did not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_ready(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; + } + i2c->hlc_int_en(i2c); time_left = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); i2c->hlc_int_dis(i2c); - if (!time_left) { + if (!time_left) octeon_i2c_hlc_int_clear(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_hlc_test_ready(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; } - if (time_left < 0) { dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); return time_left; @@ -1143,6 +1191,9 @@ static int octeon_i2c_probe(struct platform_device *pdev) goto out; } + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) + i2c->broken_irq_check = true; + result = octeon_i2c_init_lowlevel(i2c); if (result) { dev_err(i2c->dev, "init low level failed\n"); -- 1.9.1
[PATCH v6 03/19] i2c: octeon: Rename [read|write]_sw to reg_[read|write]
octeon_i2c_read_sw -> octeon_i2c_reg_read octeon_i2c_write_sw -> octeon_i2c_reg_write Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 52 - 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index f647667..43498a4 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -80,14 +80,14 @@ struct octeon_i2c { }; /** - * octeon_i2c_write_sw - write an I2C core register + * octeon_i2c_reg_write - write an I2C core register * @i2c: The struct octeon_i2c * @eop_reg: Register selector * @data: Value to be written * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +static void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data) { u64 tmp; @@ -98,7 +98,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) } /** - * octeon_i2c_read_sw - read lower bits of an I2C core register + * octeon_i2c_reg_read - read lower bits of an I2C core register * @i2c: The struct octeon_i2c * @eop_reg: Register selector * @@ -106,7 +106,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) { u64 tmp; @@ -189,7 +189,7 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; } /** @@ -252,8 +252,8 @@ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) } } } - octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); + octeon_i2c_reg_write(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); } static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) @@ -262,14 +262,14 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) int tries; /* disable high level controller, enable bus access */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); /* reset controller */ - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); for (tries = 10; tries; tries--) { udelay(1); - status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + status = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); if (status == STAT_IDLE) return 0; } @@ -288,19 +288,19 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) int result; u8 data; - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); if (result) { - if (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + if (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { /* * Controller refused to send start flag May * be a client is holding SDA low - let's try * to free it. */ octeon_i2c_unblock(i2c); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); } @@ -308,7 +308,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) return result; } - data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + data = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); if ((data != STAT_START) && (data != STAT_RSTART)) { dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); return -EIO; @@ -320,7 +320,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) /* send STOP to the bus */ static void octeon_i2c_stop(struct octeon_i2c *i2c) { - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL,
[PATCH v6 02/19] i2c: octeon: Move set-clock and init-lowlevel upward
No functional change, just moving the functions upward in preparation of improving the recovery. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 126 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index c4abf16..f647667 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -214,6 +214,69 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } +/* calculate and set clock divisors */ +static void octeon_i2c_set_clock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 100; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* +* An mdiv value of less than 2 seems to not work well +* with ds1337 RTCs, so we constrain it to larger values. +*/ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* +* For given ndiv and mdiv values check the +* two closest thp values. +*/ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); +} + +static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) +{ + u8 status; + int tries; + + /* disable high level controller, enable bus access */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if (status == STAT_IDLE) + return 0; + } + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); + return -EIO; +} + /** * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c @@ -428,69 +491,6 @@ static struct i2c_adapter octeon_i2c_ops = { .algo = _i2c_algo, }; -/* calculate and set clock divisors */ -static void octeon_i2c_set_clock(struct octeon_i2c *i2c) -{ - int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; - int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 100; - - for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { - /* -* An mdiv value of less than 2 seems to not work well -* with ds1337 RTCs, so we constrain it to larger values. -*/ - for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { - /* -* For given ndiv and mdiv values check the -* two closest thp values. -*/ - tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; - tclk *= (1 << ndiv_idx); - thp_base = (i2c->sys_freq / (tclk * 2)) - 1; - - for (inc = 0; inc <= 1; inc++) { - thp_idx = thp_base + inc; - if (thp_idx < 5 || thp_idx > 0xff) - continue; - - foscl = i2c->sys_freq / (2 * (thp_idx + 1)); - foscl = foscl / (1 << ndiv_idx); - foscl = foscl / (mdiv_idx + 1) / 10; - diff = abs(foscl - i2c->twsi_freq); - i
[PATCH v6 04/19] i2c: octeon: Introduce helper functions for register access
Add helper functions for control, data and status register access. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 56 +++-- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 43498a4..b25c491 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -97,6 +97,11 @@ static void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data) } while ((tmp & SW_TWSI_V) != 0); } +#define octeon_i2c_ctl_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, val) +#define octeon_i2c_data_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, val) + /** * octeon_i2c_reg_read - read lower bits of an I2C core register * @i2c: The struct octeon_i2c @@ -118,6 +123,13 @@ static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) return tmp & 0xFF; } +#define octeon_i2c_ctl_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) +#define octeon_i2c_data_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA) +#define octeon_i2c_stat_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) + /** * octeon_i2c_write_int - write the TWSI_INT register * @i2c: The struct octeon_i2c @@ -189,7 +201,7 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG) != 0; } /** @@ -262,14 +274,14 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) int tries; /* disable high level controller, enable bus access */ - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); /* reset controller */ octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_RST, 0); for (tries = 10; tries; tries--) { udelay(1); - status = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); + status = octeon_i2c_stat_read(i2c); if (status == STAT_IDLE) return 0; } @@ -288,27 +300,25 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) int result; u8 data; - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STA); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); if (result) { - if (octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) == STAT_IDLE) { + if (octeon_i2c_stat_read(i2c) == STAT_IDLE) { /* * Controller refused to send start flag May * be a client is holding SDA low - let's try * to free it. */ octeon_i2c_unblock(i2c); - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STA); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); result = octeon_i2c_wait(i2c); } if (result) return result; } - data = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); + data = octeon_i2c_stat_read(i2c); if ((data != STAT_START) && (data != STAT_RSTART)) { dev_err(i2c->dev, "%s: bad status (0x%x)\n", __func__, data); return -EIO; @@ -320,8 +330,7 @@ static int octeon_i2c_start(struct octeon_i2c *i2c) /* send STOP to the bus */ static void octeon_i2c_stop(struct octeon_i2c *i2c) { - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STP); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STP); } /** @@ -345,15 +354,15 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, if (result) return result; - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + octeon_i2c_data_write(i2c, target << 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); result = octeon_i2c_wait(i2c); if (result) return result; for (i = 0; i < length; i++) { - tmp = octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT); + tmp = octeon_i2c_stat_read(i2c);
[PATCH v6 05/19] i2c: octeon: Remove superfluous check in octeon_i2c_test_iflg
Remove superfluous check and stray newline. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index b25c491..4275007 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -198,10 +198,9 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } - static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); } /** -- 1.9.1
[PATCH v6 00/19] i2c-octeon and i2c-thunderx drivers
Hi Wolfram, in the meantime I've converted the octeon driver to use the i2c recovery framework. I thing this makes the code easier to follow. I've also cleaned up some other aspects, like the register access and fixed some bugs I found while going over the code again. This series for the Octeon i2c driver is an attempt to upstream some bug fixes and features that accumulated for some time. On top of the Octeon changes a i2c driver for the ThunderX SOC is added which uses the same functional block as the Octeon driver. Patches are on top of 4.6-rc3 and were tested on OCTEON, OCTEON-78 and ThunderX. Changes to v5: - Switch to i2c recovery framework - Clean-up register access, introduce new helper functions - Fixed ready bit check in combined write - Fixed IFLG clear in hlc_enable - Removed complicated last phase logic, not needed when we send START for every message part Changes to v4: - Splitted the High-Level Controller patch into several patches - Reworded some commit messages Changes to v3: - Added more functionality flags for SMBUS - Removed both module parameters - Make xfer return also other errors than EGAIN - Return EPROTO on invalid SMBUS block length - Use devm_ioremap_resource - Added rename-only patch - Removed kerneldoc patch from series - Improved defines Changes to v2: - Split clenaup patch into several patches - Strictly moved functional changes to later patches - Fixed do-while checkpatch errors - Moved defines to the patches that use them - Use BIT_ULL macro - Split ThunderX patch into 2 patches Changes to v1: - Fixed compile error on x86_64 - Disabled thunderx driver on MIPS - Re-ordered some thunderx probe functions for readability - Fix missing of_irq.h and i2c-smbus.h includes - Use IS_ENABLED for CONFIG options Jan - David Daney (3): i2c: octeon: Enable High-Level Controller i2c: octeon: Add support for cn78xx chips i2c: octeon: Add workaround for broken irqs on CN3860 Jan Glauber (14): i2c: octeon: Increase retry default and use fixed timeout value i2c: octeon: Move set-clock and init-lowlevel upward i2c: octeon: Rename [read|write]_sw to reg_[read|write] i2c: octeon: Introduce helper functions for register access i2c: octeon: Remove superfluous check in octeon_i2c_test_iflg i2c: octeon: Improve error status checking i2c: octeon: Use i2c recovery framework dt-bindings: i2c: Add Octeon cn78xx TWSI i2c: octeon: Move read function before write i2c: octeon: Rename driver to prepare for split i2c: octeon: Split the driver into two parts i2c: thunderx: Add i2c driver for ThunderX SOC i2c: octeon,thunderx: Move register offsets to struct i2c: thunderx: Add smbus alert support Peter Swain (2): i2c: octeon: Flush TWSI writes with readback i2c: octeon: Faster operation when IFLG signals late .../devicetree/bindings/i2c/i2c-octeon.txt | 6 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile| 3 + drivers/i2c/busses/i2c-cavium.c| 805 + drivers/i2c/busses/i2c-cavium.h| 218 ++ drivers/i2c/busses/i2c-octeon-core.c | 288 drivers/i2c/busses/i2c-octeon.c| 600 --- drivers/i2c/busses/i2c-thunderx-core.c | 308 8 files changed, 1638 insertions(+), 600 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c -- 1.9.1
Re: [PATCH 2/4] arm64: pmu: add fallback probe table
On Fri, Apr 08, 2016 at 04:57:05PM -0500, Jeremy Linton wrote: > From: Mark Salter > > In preparation for ACPI support, add a pmu_probe_info table to > the arm_pmu_device_probe() call. This table gets used when > probing in the absence of a devicetree node for PMU. > > Signed-off-by: Mark Salter > Signed-off-by: Jeremy Linton > --- > arch/arm64/kernel/perf_event.c | 10 +- > include/linux/perf/arm_pmu.h | 3 +++ > 2 files changed, 12 insertions(+), 1 deletion(-) > > diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c > index f419a7c..8f12eac 100644 > --- a/arch/arm64/kernel/perf_event.c > +++ b/arch/arm64/kernel/perf_event.c > @@ -867,9 +867,17 @@ static const struct of_device_id > armv8_pmu_of_device_ids[] = { > {}, > }; > > +static const struct pmu_probe_info armv8_pmu_probe_table[] = { > + ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A53, armv8_a53_pmu_init), > + ARMV8_PMU_PART_PROBE(ARM_CPU_PART_CORTEX_A57, armv8_a57_pmu_init), > + PMU_PROBE(0, 0, armv8_pmuv3_init), /* if all else fails... */ > + { /* sentinel value */ } > +}; > + Hi Jeremy, with 4.6 ThunderX PMU support was added, so I think above table is missing a line like: ARMV8_PMU_PART_PROBE(CAVIUM_CPU_PART_THUNDERX, armv8_thunder_pmu_init) Thanks, Jan
Re: [PATCH 0/6 v4] arm64/perf: Add ACPI support
Hi Jeremy, I've tested the patches on ThunderX and got perf running with ACPI, so you can add my Tested-by if you like. Thanks, Jan On Tue, Apr 12, 2016 at 03:21:05PM -0500, Jeremy Linton wrote: > v3->v4: > Correct build issues with ARM (!ARM64) kernels. > Add ThunderX to list of PMU types. > > v3: > Enable ARM performance monitoring units on ACPI/arm64 machines. > > This patch expands and reworks the patches published by Mark Salter > in order to clean up a few of the previous review comments, as well as > add support for newer CPUs and big/little configurations. > > I've been testing this patch in concert with an assortment of ACPI > patches to enable things like PCIe. Its been tested on juno, seattle > and some xgene systems. > > Thanks, > > *** BLURB HERE *** > > Jeremy Linton (3): > arm: arm64: Add routine to determine cpuid of other cpus > arm64: pmu: Add ACPI support for A72 and ThunderX > arm64: pmu: Detect multiple PMU types in an ACPI system > > Mark Salter (3): > arm: pmu: Fix non-devicetree probing > arm64: pmu: add fallback probe table > arm64: pmu: Add support for probing with ACPI > > arch/arm/include/asm/cputype.h | 4 + > arch/arm64/include/asm/cputype.h | 4 + > arch/arm64/kernel/perf_event.c | 12 ++- > arch/arm64/kernel/smp.c | 5 + > drivers/perf/Kconfig | 4 + > drivers/perf/Makefile| 1 + > drivers/perf/arm_pmu.c | 51 -- > drivers/perf/arm_pmu_acpi.c | 213 > +++ > include/linux/perf/arm_pmu.h | 10 ++ > 9 files changed, 293 insertions(+), 11 deletions(-) > create mode 100644 drivers/perf/arm_pmu_acpi.c > > -- > 2.4.3
[PATCH] arm64: Reduce verbosity on SMP CPU stop
When CPUs are stopped during an abnormal operation like panic for each CPU a line is printed and the stack trace is dumped. This information is only interesting for the aborting CPU and on systems with many CPUs it only makes it harder to debug if after the aborting CPU the log is flooded with data about all other CPUs too. Therefore remove the stack dump and printk of other CPUs and only print a single line that the other CPUs are going to be stopped. Signed-off-by: Jan Glauber --- arch/arm64/kernel/smp.c | 11 +++ 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index b2d5f4e..e6c2eb1 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -770,14 +770,6 @@ static DEFINE_RAW_SPINLOCK(stop_lock); */ static void ipi_cpu_stop(unsigned int cpu) { - if (system_state == SYSTEM_BOOTING || - system_state == SYSTEM_RUNNING) { - raw_spin_lock(_lock); - pr_crit("CPU%u: stopping\n", cpu); - dump_stack(); - raw_spin_unlock(_lock); - } - set_cpu_online(cpu, false); local_irq_disable(); @@ -872,6 +864,9 @@ void smp_send_stop(void) cpumask_copy(, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), ); + if (system_state == SYSTEM_BOOTING || + system_state == SYSTEM_RUNNING) + pr_crit("SMP: stopping secondary CPUs\n"); smp_cross_call(, IPI_CPU_STOP); } -- 1.9.1
[PATCH v2] arm64: Reduce verbosity on SMP CPU stop
On Fri, Apr 15, 2016 at 12:37:06PM +0100, Will Deacon wrote: > You can remove stop_lock altogether now, right? I also wonder whether > it would be worth printing out which CPUs are still online in the case where > we fail to stop all the secondaries? Sorry, I've been a bit offline. Yes, the stop_lock can also be removed. How about below patch that prints the CPU ids for all CPUs that failed to stop? Jan When CPUs are stopped during an abnormal operation like panic for each CPU a line is printed and the stack trace is dumped. This information is only interesting for the aborting CPU and on systems with many CPUs it only makes it harder to debug if after the aborting CPU the log is flooded with data about all other CPUs too. Therefore remove the stack dump and printk of other CPUs and only print a single line that the other CPUs are going to be stopped and, in case any CPUs remain online list them. Signed-off-by: Jan Glauber --- arch/arm64/kernel/smp.c | 16 +--- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index b2d5f4e..29f4e37 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c @@ -763,21 +763,11 @@ void arch_irq_work_raise(void) } #endif -static DEFINE_RAW_SPINLOCK(stop_lock); - /* * ipi_cpu_stop - handle IPI from smp_send_stop() */ static void ipi_cpu_stop(unsigned int cpu) { - if (system_state == SYSTEM_BOOTING || - system_state == SYSTEM_RUNNING) { - raw_spin_lock(_lock); - pr_crit("CPU%u: stopping\n", cpu); - dump_stack(); - raw_spin_unlock(_lock); - } - set_cpu_online(cpu, false); local_irq_disable(); @@ -872,6 +862,9 @@ void smp_send_stop(void) cpumask_copy(, cpu_online_mask); cpumask_clear_cpu(smp_processor_id(), ); + if (system_state == SYSTEM_BOOTING || + system_state == SYSTEM_RUNNING) + pr_crit("SMP: stopping secondary CPUs\n"); smp_cross_call(, IPI_CPU_STOP); } @@ -881,7 +874,8 @@ void smp_send_stop(void) udelay(1); if (num_online_cpus() > 1) - pr_warning("SMP: failed to stop secondary CPUs\n"); + pr_warning("SMP: failed to stop secondary CPUs %*pbl\n", + cpumask_pr_args(cpu_online_mask)); } /* -- 1.9.1
Re: [PATCH v2 0/5] Cavium ThunderX uncore PMU support
Hi Mark, can you have a look at these patches? Thanks, Jan On Wed, Mar 09, 2016 at 05:21:02PM +0100, Jan Glauber wrote: > This patch series provides access to various counters on the ThunderX SOC. > > For details of the uncore implementation see patch #1. > > Patches #2-5 add the various ThunderX specific PMUs. > > As suggested I've put the files under drivers/perf/uncore. I would > prefer this location over drivers/bus because not all of the uncore > drivers are bus related. > > Changes to v1: > - Added NUMA support > - Fixed CPU hotplug by pmu migration > - Moved files to drivers/perf/uncore > - Removed OCX FRC and LNE drivers, these will fit better into a edac driver > - improved comments abount overflow interrupts > - removed max device limit > - trimmed include files > > Feedback welcome! > Jan > > - > > Jan Glauber (5): > arm64/perf: Basic uncore counter support for Cavium ThunderX > arm64/perf: Cavium ThunderX L2C TAD uncore support > arm64/perf: Cavium ThunderX L2C CBC uncore support > arm64/perf: Cavium ThunderX LMC uncore support > arm64/perf: Cavium ThunderX OCX TLK uncore support > > drivers/perf/Makefile | 1 + > drivers/perf/uncore/Makefile| 5 + > drivers/perf/uncore/uncore_cavium.c | 314 +++ > drivers/perf/uncore/uncore_cavium.h | 95 + > drivers/perf/uncore/uncore_cavium_l2c_cbc.c | 237 +++ > drivers/perf/uncore/uncore_cavium_l2c_tad.c | 600 > > drivers/perf/uncore/uncore_cavium_lmc.c | 196 + > drivers/perf/uncore/uncore_cavium_ocx_tlk.c | 380 ++ > 8 files changed, 1828 insertions(+) > create mode 100644 drivers/perf/uncore/Makefile > create mode 100644 drivers/perf/uncore/uncore_cavium.c > create mode 100644 drivers/perf/uncore/uncore_cavium.h > create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_cbc.c > create mode 100644 drivers/perf/uncore/uncore_cavium_l2c_tad.c > create mode 100644 drivers/perf/uncore/uncore_cavium_lmc.c > create mode 100644 drivers/perf/uncore/uncore_cavium_ocx_tlk.c > > -- > 1.9.1
Re: [PATCH v4 03/14] i2c-octeon: Change adapter timeout and retry default values
On Wed, Mar 23, 2016 at 08:55:18PM +0100, Wolfram Sang wrote: > On Fri, Mar 18, 2016 at 09:46:28AM +0100, Jan Glauber wrote: > > Convert the adapter timeout to 2 ms instead of a fixed number of > > jiffies and set retries to 10. > > You describe what you change, but not why this is needed. Why 10 > retries? And shouldn't that be 20ms seeing the HZ/50 ? The timeout value is not changed, i2c-octeon is bound to Octeon SOC which has CONFIG_HZ=100. I would prefer to use an absolute value for a timeout that should not change with the value of CONFIG_HZ. For the retries, I'll change it to 5 which is what many i2c drivers use. I thought the reason to use a non-zero value for retries might be obvious, like "retry in case of a failed operation" ?! With the updated driver I do not see retry attempts, but it might not hurt the robustness of the driver to benefit from i2c core retry logic, or am I missing something? > Also, please use "i2c: octeon: " as prefix in the subject. OK. Thanks, Jan > Thanks, > >Wolfram > > > > > Signed-off-by: Jan Glauber > > --- > > drivers/i2c/busses/i2c-octeon.c | 3 ++- > > 1 file changed, 2 insertions(+), 1 deletion(-) > > > > diff --git a/drivers/i2c/busses/i2c-octeon.c > > b/drivers/i2c/busses/i2c-octeon.c > > index 9240037..e616e4c 100644 > > --- a/drivers/i2c/busses/i2c-octeon.c > > +++ b/drivers/i2c/busses/i2c-octeon.c > > @@ -414,7 +414,6 @@ static struct i2c_adapter octeon_i2c_ops = { > > .owner = THIS_MODULE, > > .name = "OCTEON adapter", > > .algo = _i2c_algo, > > - .timeout = HZ / 50, > > }; > > > > /* calculate and set clock divisors */ > > @@ -541,6 +540,8 @@ static int octeon_i2c_probe(struct platform_device > > *pdev) > > octeon_i2c_set_clock(i2c); > > > > i2c->adap = octeon_i2c_ops; > > + i2c->adap.timeout = msecs_to_jiffies(2); > > + i2c->adap.retries = 10; > > i2c->adap.dev.parent = >dev; > > i2c->adap.dev.of_node = node; > > i2c_set_adapdata(>adap, i2c); > > -- > > 1.9.1 > >
Re: [PATCH v4 05/14] i2c-octeon: Enable high-level controller and improve on bus contention
On Wed, Mar 23, 2016 at 09:32:15PM +0100, Wolfram Sang wrote: > On Fri, Mar 18, 2016 at 09:46:30AM +0100, Jan Glauber wrote: > > From: David Daney > > > > Use High Level Controller when possible. > > Can you give me a one line description what this Controller is? I'd > assume it can do simple write-then-read messages with less setup? Of course, I'll add this to the patch description too. The HLC can read/write up to 8 bytes and is completely optional. The most important difference of the HLC is that it only requires one interrupt for a transfer (up to 8 bytes) where the low-level read/write requires 2 interrupts plus one interrupt per transferred byte. Since the interrupts are costly using the HLC improves the performance. Also, the HLC provides improved error handling. > > i2c-octeon was reacting badly to bus contention: when in > > direct-access mode (for transfers > 8 bytes, which cannot use the > > high-level controller) some !ACK or arbitration-loss states were > > not causing the current transfer to be aborted, and the bus released. > > So, what does this patch do? Enable HLC for transfers < 8 byte? And for > all other transfers we still suffer from the same problem? I think the patch description was misleading, which is my fault because I merged several incremental patches into one. The HLC is used when possible (up to 8 bytes). For bigger transfers the handling is improved and special treatment is done for the first and last part of a transfer. > Such information should be here, too. It helps reviewing when I already > have the big picture. > > > There's one place in i2c protocol that !ACK is an acceptable > > response: in the final byte of a read cycle. In this case the > > destination is not saying that the transfer failed, just that it > > doesn't want more data. > > Ehrm, no? For reads, the MASTER is saying it doesn't need any more data. > And an I2C eeprom can legally NACK a write, e.g. when it is still > processing the previous write. Also, NACK is a valid response after the > address phase, meaning there is no device listening. > > Does the implementation cover the above cases? > > > This enables correct behavior of ACK on final byte of non-final read > > msgs too. > > The patch is huge and very hard to review. Maybe it needs to be split > up. Brainstorming example: a) move functions like octeon_i2c_set_clock() > upwards, b) change them if needed, c) implement HLC functions, d) add > switching logic to use HLC or non-HLC functions... I was reluctant to split the patch because of the high risk of breaking the bi-sectability, but your proposal makes sense. I've seperated the error handling changes from the HLC feature now (plus seperate patches for the moved functions). Thanks, Jan > But first we need to be clear on the big picture view. > > Thanks, > >Wolfram >
[PATCH v5 13/14] i2c: thunderx: Add i2c driver for ThunderX SOC
The ThunderX SOC uses the same i2c block as the Octeon SOC. The main difference is that on ThunderX the device is a PCI device so device probing is done via PCI, interrupts are MSIX and the clock is taken from device tree. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile| 2 + drivers/i2c/busses/i2c-cavium.h| 17 ++- drivers/i2c/busses/i2c-thunderx-core.c | 268 + 4 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index faa8e68..92d23de 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -953,6 +953,16 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI && !CAVIUM_OCTEON_SOC + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + config I2C_XILINX tristate "Xilinx I2C Controller" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 282f781..a32ff14 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -93,6 +93,8 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +i2c-thunderx-objs := i2c-cavium.o i2c-thunderx-core.o +obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index 7f78bf0..31608c4 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -8,9 +8,15 @@ #include /* Register offsets */ -#define SW_TWSI0x00 -#define TWSI_INT 0x10 -#define SW_TWSI_EXT0x18 +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + #define SW_TWSI 0x1000 + #define TWSI_INT0x1010 + #define SW_TWSI_EXT 0x1018 +#else + #define SW_TWSI 0x00 + #define TWSI_INT0x10 + #define SW_TWSI_EXT 0x18 +#endif /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ @@ -92,6 +98,7 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; + struct clk *clk; int irq; int hlc_irq;/* For cn7890 only */ u32 twsi_freq; @@ -107,6 +114,10 @@ struct octeon_i2c { void (*hlc_int_dis)(struct octeon_i2c *); atomic_t int_en_cnt; atomic_t hlc_int_en_cnt; + +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + struct msix_entry i2c_msix; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c new file mode 100644 index 000..2ce985b --- /dev/null +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -0,0 +1,268 @@ +/* + * Cavium ThunderX i2c driver. + * + * Copyright (C) 2015,2016 Cavium Inc. + * Authors: Fred Martin + * Jan Glauber + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +#define DRV_NAME "i2c-thunderx" + +#define PCI_CFG_REG_BAR_NUM0 +#define PCI_DEVICE_ID_THUNDER_TWSI 0xa012 + +#define TWSI_DFL_RATE 10 +#define SYS_FREQ_DEFAULT 8 + +#define TWSI_INT_ENA_W1C 0x1028 +#define TWSI_INT_ENA_W1S 0x1030 + +/* + * Enable the CORE interrupt. + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void thunder_i2c_int_enable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_CORE_INT, i2c->twsi_base + TWSI_INT_ENA_W1S); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +/* + * Disable the CORE interrupt. + */ +static void thunder_i2c_int_disable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_CORE_INT, i2c->twsi_base + TWSI_INT_ENA_W1C); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_ST_INT | TWSI_INT_TS_INT, +i2c->twsi_base + TWSI_INT_ENA_W1S); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +static void t
[PATCH v5 11/14] i2c: octeon: Rename driver to prepare for split
This is just an intermediate commit in preparation of the driver split. The module rename in this commit will be reverted in the next patch, this is just done to make the series bisectible. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile| 2 +- drivers/i2c/busses/{i2c-octeon.c => i2c-octeon-core.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/i2c/busses/{i2c-octeon.c => i2c-octeon-core.c} (100%) diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819..3405286 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,7 @@ obj-$(CONFIG_I2C_UNIPHIER)+= i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon-core.c similarity index 100% rename from drivers/i2c/busses/i2c-octeon.c rename to drivers/i2c/busses/i2c-octeon-core.c -- 1.9.1
[PATCH v5 04/14] i2c: octeon: Enable High-Level Controller
From: David Daney Use High-Level Controller (HLC) when possible. The HLC can read/write up to 8 bytes and is completely optional. The most important difference of the HLC is that it only requires one interrupt for a transfer (up to 8 bytes) where the low-level read/write requires 2 interrupts plus one interrupt per transferred byte. Since the interrupts are costly using the HLC improves the performance. Also, the HLC provides improved error handling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 397 ++-- 1 file changed, 380 insertions(+), 17 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index a037245..bc0ed20 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -29,13 +29,23 @@ /* Register offsets */ #define SW_TWSI0x00 #define TWSI_INT 0x10 +#define SW_TWSI_EXT0x18 /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIABIT_ULL(61) /* Extended internal address */ #define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ /* Controller opcode word (bits 60:57) */ #define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA(1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_TWSI_CLK(4ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ @@ -48,7 +58,7 @@ #define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) /* Controller command and status bits */ -#define TWSI_CTL_CE0x80 +#define TWSI_CTL_CE0x80/* High level controller enable */ #define TWSI_CTL_ENAB 0x40/* Bus enable */ #define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ #define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ @@ -87,9 +97,16 @@ #define STAT_IDLE 0xF8 /* TWSI_INT values */ +#define TWSI_INT_ST_INTBIT_ULL(0) +#define TWSI_INT_TS_INTBIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) #define TWSI_INT_CORE_EN BIT_ULL(6) #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) struct octeon_i2c { wait_queue_head_t queue; @@ -99,6 +116,7 @@ struct octeon_i2c { int sys_freq; void __iomem *twsi_base; struct device *dev; + bool hlc_enabled; }; static int reset_how; @@ -111,7 +129,7 @@ static int reset_how; * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u32 data) { u64 tmp; @@ -122,7 +140,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) } /** - * octeon_i2c_read_sw - read lower bits of an I2C core register + * octeon_i2c_read_sw64 - read an I2C core register * @i2c: The struct octeon_i2c * @eop_reg: Register selector * @@ -130,7 +148,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +static u64 octeon_i2c_read_sw64(struct octeon_i2c *i2c, u64 eop_reg) { u64 tmp; @@ -139,7 +157,21 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) tmp = __raw_readq(i2c->twsi_base + SW_TWSI); } while ((tmp & SW_TWSI_V) != 0); - return tmp & 0xFF; + return tmp; +} + +/** + * octeon_i2c_read_sw - read lower bits of an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) +{ + return octeon_i2c_read_sw64(i2c, eop_reg) & 0xFF; } /** @@ -172,6 +204,15 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, 0); } +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc
[PATCH v5 08/14] i2c: octeon: Faster operation when IFLG signals late
From: Peter Swain Some versions can deliver low-level twsi irq before twsi_ctl.iflg is set, leading to timeout-driven i/o. When an irq signals event, but woken task does not see the expected twsi_ctl.iflg, re-check about 80uS later. EEPROM reads on 100kHz i2c now measure ~5.2kB/s, about 1/2 what's achievable, and much better than the worst-case 100 bytes/sec before. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 25 - 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 44ce9d4..cc1fe51 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -342,6 +342,28 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } +#define I2C_OCTEON_IFLG_WAIT 80/* microseconds */ + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -351,9 +373,10 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; + int first = 1; i2c->int_en(i2c); - time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); if (!time_left) { -- 1.9.1
[PATCH v5 14/14] i2c: thunderx: Add smbus alert support
Add smbus alert interrupt support. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-cavium.h| 6 ++ drivers/i2c/busses/i2c-thunderx-core.c | 35 ++ 2 files changed, 41 insertions(+) diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index 31608c4..f934624 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,11 @@ struct octeon_i2c { #if IS_ENABLED(CONFIG_I2C_THUNDERX) struct msix_entry i2c_msix; #endif + +#if IS_ENABLED(CONFIG_I2C_SMBUS) + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c index 2ce985b..339378a 100644 --- a/drivers/i2c/busses/i2c-thunderx-core.c +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include "i2c-cavium.h" @@ -108,6 +110,35 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) devm_clk_put(dev, clk); } +static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c, + struct device_node *node) +{ +#if IS_ENABLED(CONFIG_I2C_SMBUS) + u32 type; + + i2c->alert_data.irq = irq_of_parse_and_map(node, 0); + if (!i2c->alert_data.irq) + return -EINVAL; + + type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq)); + i2c->alert_data.alert_edge_triggered = + (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0; + + i2c->ara = i2c_setup_smbus_alert(>adap, >alert_data); + if (!i2c->ara) + return -ENODEV; +#endif + return 0; +} + +static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c) +{ +#if IS_ENABLED(CONFIG_I2C_SMBUS) + if (i2c->ara) + i2c_unregister_device(i2c->ara); +#endif +} + static void thunder_i2c_set_name(struct pci_dev *pdev, struct octeon_i2c *i2c, char *name) { @@ -207,6 +238,9 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, goto out_irq; } + ret = thunder_i2c_smbus_setup(i2c, node); + if (ret < 0) + dev_err(dev, "Failed to setup smbus alert\n"); dev_info(i2c->dev, "probed\n"); return 0; @@ -237,6 +271,7 @@ static void thunder_i2c_remove_pci(struct pci_dev *pdev) dev = i2c->dev; thunder_i2c_clock_disable(dev, i2c->clk); + thunder_i2c_smbus_remove(i2c); i2c_del_adapter(>adap); devm_free_irq(dev, i2c->i2c_msix.vector, i2c); pci_disable_msix(pdev); -- 1.9.1
[PATCH v5 12/14] i2c: octeon: Split the driver into two parts
Move common functionality into a separate file in preparation of the re-use from the ThunderX i2c driver. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile | 3 +- drivers/i2c/busses/i2c-cavium.c | 822 + drivers/i2c/busses/i2c-cavium.h | 195 +++ drivers/i2c/busses/i2c-octeon-core.c | 983 +-- 4 files changed, 1020 insertions(+), 983 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3405286..282f781 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,8 @@ obj-$(CONFIG_I2C_UNIPHIER)+= i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o +i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.c b/drivers/i2c/busses/i2c-cavium.c new file mode 100644 index 000..9ad1f23 --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.c @@ -0,0 +1,822 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +static int reset_how; + +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + +#define I2C_OCTEON_IFLG_WAIT 80/* microseconds */ + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + long time_left; + int first = 1; + + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->int_en(i2c); + time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), + i2c->adap.timeout); + i2c->int_dis(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + if (!time_left) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Cleanup low-level state & enable high-level. + * Returns -EAGAIN if low-level state could not be cleaned. + */ +static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) +{ + int try = 0, ret = 0; + u64 val; + + if (i2c->hlc_enabled) + return 0; + i2c->
[PATCH v5 10/14] i2c: octeon: Move read function before write
Just sorting the functions to be consistent with the other read/write variants. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 94 - 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 9621b66..95cd301 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -928,53 +928,6 @@ static int octeon_i2c_start(struct octeon_i2c *i2c, int first) } /** - * octeon_i2c_write - send data to the bus via low-level controller - * @i2c: The struct octeon_i2c - * @target: Target address - * @data: Pointer to the data to be sent - * @length: Length of the data - * @last: is last msg in combined operation? - * - * The address is sent over the bus, then the data. - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_write(struct octeon_i2c *i2c, int target, - const u8 *data, int length, int first, int last) -{ - int i, result; - - result = octeon_i2c_start(i2c, first); - if (result) - return result; - - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - - for (i = 0; i < length; i++) { - result = check_arb(i2c, false); - if (result) - return result; - - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); - octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - result = check_arb(i2c, false); - if (result) - return result; - } - - return 0; -} - -/** * octeon_i2c_read - receive data from the bus via low-level controller * @i2c: The struct octeon_i2c * @target: Target address @@ -1040,6 +993,53 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, } /** + * octeon_i2c_write - send data to the bus via low-level controller + * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the data to be sent + * @length: Length of the data + * @last: is last msg in combined operation? + * + * The address is sent over the bus, then the data. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length, int first, int last) +{ + int i, result; + + result = octeon_i2c_start(i2c, first); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, target << 1); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + for (i = 0; i < length; i++) { + result = check_arb(i2c, false); + if (result) + return result; + + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_DATA, data[i]); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + result = check_arb(i2c, false); + if (result) + return result; + } + + return 0; +} + +/** * octeon_i2c_xfer - The driver's master_xfer function * @adap: Pointer to the i2c_adapter structure * @msgs: Pointer to the messages to be processed -- 1.9.1
[PATCH v5 09/14] i2c: octeon: Add workaround for broken irqs on CN3860
From: David Daney CN3860 does not interrupt the CPU when the i2c status changes. If we get a timeout, and see the status has in fact changed, we know we have this problem, and drop back to polling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 55 +++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index cc1fe51..9621b66 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -119,6 +119,8 @@ struct octeon_i2c { void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + bool broken_irq_mode; + bool broken_irq_check; void (*int_en)(struct octeon_i2c *); void (*int_dis)(struct octeon_i2c *); void (*hlc_int_en)(struct octeon_i2c *); @@ -375,10 +377,33 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) long time_left; int first = 1; + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + i2c->int_en(i2c); time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -512,17 +537,40 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int time_left; + if (i2c->broken_irq_mode) { + /* +* Some cn38xx boards did not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_ready(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; + } + i2c->hlc_int_en(i2c); time_left = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); i2c->hlc_int_dis(i2c); - if (!time_left) { + if (!time_left) octeon_i2c_hlc_int_clear(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_hlc_test_ready(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; } - if (time_left < 0) { dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); return time_left; @@ -1154,6 +1202,9 @@ static int octeon_i2c_probe(struct platform_device *pdev) goto out; } + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) + i2c->broken_irq_check = true; + result = octeon_i2c_init_lowlevel(i2c); if (result) { dev_err(i2c->dev, "init low level failed\n"); -- 1.9.1
[PATCH v5 05/14] dt-bindings: i2c: Add Octeon cn78xx TWSI
Add compatible string for Cavium Octeon cn78XX SOCs TWSI. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Jan Glauber Acked-by: David Daney --- Documentation/devicetree/bindings/i2c/i2c-octeon.txt | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt index dced82e..872d485 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt @@ -4,6 +4,12 @@ Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. + or + + compatible: "cavium,octeon-7890-twsi" + + Compatibility with cn78XX SOCs. + - reg: The base address of the TWSI/I2C bus controller register bank. - #address-cells: Must be <1>. -- 1.9.1
[PATCH v5 06/14] i2c: octeon: Add support for cn78xx chips
From: David Daney cn78xx has a different interrupt architecture, so we have to manage the interrupts differently. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 131 1 file changed, 120 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index bc0ed20..bd5915d 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -11,6 +11,7 @@ * warranty of any kind, whether express or implied. */ +#include #include #include #include @@ -112,11 +113,18 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; int irq; + int hlc_irq;/* For cn7890 only */ u32 twsi_freq; int sys_freq; void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + void (*int_en)(struct octeon_i2c *); + void (*int_dis)(struct octeon_i2c *); + void (*hlc_int_en)(struct octeon_i2c *); + void (*hlc_int_dis)(struct octeon_i2c *); + atomic_t int_en_cnt; + atomic_t hlc_int_en_cnt; }; static int reset_how; @@ -214,6 +222,58 @@ static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) } /** + * octeon_i2c_int_enable78 - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(>int_en_cnt); + enable_irq(i2c->irq); +} + +static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +{ + int count; + + /* +* The interrupt can be disabled in two places, but we only +* want to make the disable_irq_nosync() call once, so keep +* track with the atomic variable. +*/ + count = atomic_dec_if_positive(cnt); + if (count >= 0) + disable_irq_nosync(irq); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(>int_en_cnt, i2c->irq); +} + +/** + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(>hlc_int_en_cnt); + enable_irq(i2c->hlc_irq); +} + +/* disable the ST interrupt */ +static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(>hlc_int_en_cnt, i2c->hlc_irq); +} + +/** * octeon_i2c_unblock - unblock the bus * @i2c: The struct octeon_i2c * @@ -250,7 +310,18 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { struct octeon_i2c *i2c = dev_id; - octeon_i2c_int_disable(i2c); + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +/* HLC interrupt service routine */ +static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->hlc_int_dis(i2c); wake_up(>queue); return IRQ_HANDLED; @@ -276,10 +347,10 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; - octeon_i2c_int_enable(i2c); + i2c->int_en(i2c); time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->int_dis(i2c); if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -413,11 +484,11 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int time_left; - octeon_i2c_hlc_int_enable(i2c); + i2c->hlc_int_en(i2c); time_left = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->hlc_int_dis(i2c); if (!time_left) { octeon_i2c_hlc_int_clear(i2c); dev_dbg(i2c->dev, "%s: timeout\n", __func__); @@ -969,14 +1040,26 @@ static struct i2c_adapter octeon_i2c_ops = { static int octeon_i2c_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + int irq, result = 0, hlc_irq = 0; struct resource *res_mem; struct octeon_i2c *i2c; - int irq, result = 0; - - /* All adaptors have an irq. */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + bool cn78xx_style; + + cn78xx_style = of_device_is_c
[PATCH v5 07/14] i2c: octeon: Flush TWSI writes with readback
From: Peter Swain Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 19 --- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index bd5915d..44ce9d4 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -129,6 +129,12 @@ struct octeon_i2c { static int reset_how; +static void writeqflush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -189,8 +195,7 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) */ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) { - __raw_writeq(data, i2c->twsi_base + TWSI_INT); - __raw_readq(i2c->twsi_base + TWSI_INT); + writeqflush(data, i2c->twsi_base + TWSI_INT); } /** @@ -576,10 +581,10 @@ static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) ext |= (u64) msgs[0].buf[j] << (8 * i); - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); } - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -625,7 +630,7 @@ static int octeon_i2c_ia_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -685,10 +690,10 @@ static int octeon_i2c_ia_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) set_ext = true; } if (set_ext) - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) -- 1.9.1
[PATCH v5 01/14] i2c: octeon: Increase retry default and use fixed timeout value
Convert the adapter timeout to 2 ms independently of depending on CONFIG_HZ. CONFIG_HZ is 100 for MIPS Cavium-Octeon so the timeout value is not changed. Also set retries to 5 to improve robustness. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 46fb6c4..c4abf16 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -426,7 +426,6 @@ static struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = _i2c_algo, - .timeout = HZ / 50, }; /* calculate and set clock divisors */ @@ -553,6 +552,8 @@ static int octeon_i2c_probe(struct platform_device *pdev) octeon_i2c_set_clock(i2c); i2c->adap = octeon_i2c_ops; + i2c->adap.timeout = msecs_to_jiffies(2); + i2c->adap.retries = 5; i2c->adap.dev.parent = >dev; i2c->adap.dev.of_node = node; i2c_set_adapdata(>adap, i2c); -- 1.9.1
[PATCH v5 02/14] i2c: octeon: Move set-clock and init-lowlevel upward
No functional change, just moving the functions upward in preparation of improving the recovery. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 126 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index c4abf16..f647667 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -214,6 +214,69 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } +/* calculate and set clock divisors */ +static void octeon_i2c_set_clock(struct octeon_i2c *i2c) +{ + int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; + int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 100; + + for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { + /* +* An mdiv value of less than 2 seems to not work well +* with ds1337 RTCs, so we constrain it to larger values. +*/ + for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { + /* +* For given ndiv and mdiv values check the +* two closest thp values. +*/ + tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; + tclk *= (1 << ndiv_idx); + thp_base = (i2c->sys_freq / (tclk * 2)) - 1; + + for (inc = 0; inc <= 1; inc++) { + thp_idx = thp_base + inc; + if (thp_idx < 5 || thp_idx > 0xff) + continue; + + foscl = i2c->sys_freq / (2 * (thp_idx + 1)); + foscl = foscl / (1 << ndiv_idx); + foscl = foscl / (mdiv_idx + 1) / 10; + diff = abs(foscl - i2c->twsi_freq); + if (diff < delta_hz) { + delta_hz = diff; + thp = thp_idx; + mdiv = mdiv_idx; + ndiv = ndiv_idx; + } + } + } + } + octeon_i2c_write_sw(i2c, SW_TWSI_OP_TWSI_CLK, thp); + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); +} + +static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) +{ + u8 status; + int tries; + + /* disable high level controller, enable bus access */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); + + /* reset controller */ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_RST, 0); + + for (tries = 10; tries; tries--) { + udelay(1); + status = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); + if (status == STAT_IDLE) + return 0; + } + dev_err(i2c->dev, "%s: TWSI_RST failed! (0x%x)\n", __func__, status); + return -EIO; +} + /** * octeon_i2c_start - send START to the bus * @i2c: The struct octeon_i2c @@ -428,69 +491,6 @@ static struct i2c_adapter octeon_i2c_ops = { .algo = _i2c_algo, }; -/* calculate and set clock divisors */ -static void octeon_i2c_set_clock(struct octeon_i2c *i2c) -{ - int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; - int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 100; - - for (ndiv_idx = 0; ndiv_idx < 8 && delta_hz != 0; ndiv_idx++) { - /* -* An mdiv value of less than 2 seems to not work well -* with ds1337 RTCs, so we constrain it to larger values. -*/ - for (mdiv_idx = 15; mdiv_idx >= 2 && delta_hz != 0; mdiv_idx--) { - /* -* For given ndiv and mdiv values check the -* two closest thp values. -*/ - tclk = i2c->twsi_freq * (mdiv_idx + 1) * 10; - tclk *= (1 << ndiv_idx); - thp_base = (i2c->sys_freq / (tclk * 2)) - 1; - - for (inc = 0; inc <= 1; inc++) { - thp_idx = thp_base + inc; - if (thp_idx < 5 || thp_idx > 0xff) - continue; - - foscl = i2c->sys_freq / (2 * (thp_idx + 1)); - foscl = foscl / (1 << ndiv_idx); - foscl = foscl / (mdiv_idx + 1) / 10; - diff = abs(foscl - i2c->twsi_freq); - i
[PATCH v5 03/14] i2c: octeon: Improve error handling
From: Peter Swain Consider more status codes and improve error handling. Distinguish handling for first and last part of a message. TODO: Convert to use the i2c recovery framework. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 244 +--- 1 file changed, 178 insertions(+), 66 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index f647667..a037245 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -56,12 +56,34 @@ #define TWSI_CTL_AAK 0x04/* Assert ACK */ /* Some status values */ +#define STAT_ERROR 0x00 #define STAT_START 0x08 #define STAT_RSTART0x10 #define STAT_TXADDR_ACK0x18 +#define STAT_TXADDR_NAK0x20 #define STAT_TXDATA_ACK0x28 +#define STAT_TXDATA_NAK0x30 +#define STAT_LOST_ARB_38 0x38 #define STAT_RXADDR_ACK0x40 +#define STAT_RXADDR_NAK0x48 #define STAT_RXDATA_ACK0x50 +#define STAT_RXDATA_NAK0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 #define STAT_IDLE 0xF8 /* TWSI_INT values */ @@ -79,6 +101,8 @@ struct octeon_i2c { struct device *dev; }; +static int reset_how; + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -186,7 +210,6 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } - static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) & TWSI_CTL_IFLG) != 0; @@ -214,6 +237,66 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } +static int octeon_i2c_lost_arb(u8 code, int final_read) +{ + switch (code) { + /* Arbitration lost */ + case STAT_LOST_ARB_38: + case STAT_LOST_ARB_68: + case STAT_LOST_ARB_78: + case STAT_LOST_ARB_B0: + return -EAGAIN; + + /* Being addressed as slave, should back off & listen */ + case STAT_SLAVE_60: + case STAT_SLAVE_70: + case STAT_GENDATA_ACK: + case STAT_GENDATA_NAK: + return -EIO; + + /* Core busy as slave */ + case STAT_SLAVE_80: + case STAT_SLAVE_88: + case STAT_SLAVE_A0: + case STAT_SLAVE_A8: + case STAT_SLAVE_LOST: + case STAT_SLAVE_NAK: + case STAT_SLAVE_ACK: + return -EIO; + + /* ACK allowed on pre-terminal bytes only */ + case STAT_RXDATA_ACK: + if (!final_read) + return 0; + return -EAGAIN; + + /* NAK allowed on terminal byte only */ + case STAT_RXDATA_NAK: + if (final_read) + return 0; + return -EAGAIN; + case STAT_TXDATA_NAK: + case STAT_TXADDR_NAK: + case STAT_RXADDR_NAK: + case STAT_AD2W_NAK: + return -EAGAIN; + } + return 0; +} + +static int check_arb(struct octeon_i2c *i2c, int final_read) +{ + return octeon_i2c_lost_arb(octeon_i2c_read_sw(i2c, + SW_TWSI_EOP_TWSI_STAT), final_read); +} + +/* send STOP to the bus */ +static void octeon_i2c_stop(struct octeon_i2c *i2c) +{ + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB | TWSI_CTL_STP); +} + /* calculate and set clock divisors */ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) { @@ -277,71 +360,103 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) return -EIO; } +/* + * TWSI state seems stuck. Not sure if it's TWSI-engine state or something + * else on bus. The initial _stop() is always harmless, it just resets state + * machine, does not _transmit_ STOP unless engine was active. + */ +static int start_unstick(struct octeon_i2c *i2c) +{ + octeon_i2c_stop(i2c); + + /* +* Response is escalated over successive calls, +* as EAGAIN provokes retries from i2c/core. +*/ + switch (reset_how++ % 4) { + case 0: + /* just the stop above */ + break; + case 1: + /* +* Controller refused to send start flag. May be a +* client is holding SDA low? L
[PATCH v5 00/14] i2c-octeon and i2c-thunderx drivers
This series for the Octeon i2c driver is an attempt to upstream some bug fixes and features that accumulated for some time. On top of the Octeon changes a i2c driver for the ThunderX SOC is added which uses the same functional block as the Octeon driver. Patches #1-9 are forward-ports of Octeon features and bugfixes. Patches #10-12 prepare for the driver split. Patches #13-14 add the ThunderX driver. Patches are on top of 4.6-rc1 and were tested on OCTEON, OCTEON-78 and ThunderX. Changes to v4: - Splitted the High-Level Controller patch into several patches - Reworded some commit messages Changes to v3: - Added more functionality flags for SMBUS - Removed both module parameters - Make xfer return also other errors than EGAIN - Return EPROTO on invalid SMBUS block length - Use devm_ioremap_resource - Added rename-only patch - Removed kerneldoc patch from series - Improved defines Changes to v2: - Split clenaup patch into several patches - Strictly moved functional changes to later patches - Fixed do-while checkpatch errors - Moved defines to the patches that use them - Use BIT_ULL macro - Split ThunderX patch into 2 patches Changes to v1: - Fixed compile error on x86_64 - Disabled thunderx driver on MIPS - Re-ordered some thunderx probe functions for readability - Fix missing of_irq.h and i2c-smbus.h includes - Use IS_ENABLED for CONFIG options Jan - David Daney (3): i2c: octeon: Enable High-Level Controller i2c: octeon: Add support for cn78xx chips i2c: octeon: Add workaround for broken irqs on CN3860 Jan Glauber (8): i2c: octeon: Increase retry default and use fixed timeout value i2c: octeon: Move set-clock and init-lowlevel upward dt-bindings: i2c: Add Octeon cn78xx TWSI i2c: octeon: Move read function before write i2c: octeon: Rename driver to prepare for split i2c: octeon: Split the driver into two parts i2c: thunderx: Add i2c driver for ThunderX SOC i2c: thunderx: Add smbus alert support Peter Swain (3): i2c: octeon: Improve error handling i2c: octeon: Flush TWSI writes with readback i2c: octeon: Faster operation when IFLG signals late .../devicetree/bindings/i2c/i2c-octeon.txt | 6 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile| 3 + drivers/i2c/busses/i2c-cavium.c| 822 + drivers/i2c/busses/i2c-cavium.h| 212 ++ drivers/i2c/busses/i2c-octeon-core.c | 283 +++ drivers/i2c/busses/i2c-octeon.c| 600 --- drivers/i2c/busses/i2c-thunderx-core.c | 303 8 files changed, 1639 insertions(+), 600 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c -- 1.9.1
[PATCH 4/6] spi: octeon: Move include file from arch/mips to drivers/spi
Move the register definitions to the drivers directory because they are only used there. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill --- .../cvmx-mpi-defs.h => drivers/spi/spi-cavium.h| 32 +- drivers/spi/spi-octeon.c | 3 +- 2 files changed, 3 insertions(+), 32 deletions(-) rename arch/mips/include/asm/octeon/cvmx-mpi-defs.h => drivers/spi/spi-cavium.h (84%) diff --git a/arch/mips/include/asm/octeon/cvmx-mpi-defs.h b/drivers/spi/spi-cavium.h similarity index 84% rename from arch/mips/include/asm/octeon/cvmx-mpi-defs.h rename to drivers/spi/spi-cavium.h index 4615b10..d41dba5 100644 --- a/arch/mips/include/asm/octeon/cvmx-mpi-defs.h +++ b/drivers/spi/spi-cavium.h @@ -1,32 +1,4 @@ -/***license start*** - * Author: Cavium Networks - * - * Contact: supp...@caviumnetworks.com - * This file is part of the OCTEON SDK - * - * Copyright (c) 2003-2012 Cavium Networks - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, Version 2, as - * published by the Free Software Foundation. - * - * This file is distributed in the hope that it will be useful, but - * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or - * NONINFRINGEMENT. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License - * along with this file; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * or visit http://www.gnu.org/licenses/. - * - * This file may also be available under a different license from Cavium. - * Contact Cavium Networks for more information - ***license end**/ - -#ifndef __CVMX_MPI_DEFS_H__ -#define __CVMX_MPI_DEFS_H__ +/* MPI register descriptions */ #define CVMX_MPI_CFG (CVMX_ADD_IO_SEG(0x000107001000ull)) #define CVMX_MPI_DATX(offset) (CVMX_ADD_IO_SEG(0x000107001080ull) + ((offset) & 15) * 8) @@ -324,5 +296,3 @@ union cvmx_mpi_tx { struct cvmx_mpi_tx_s cn66xx; struct cvmx_mpi_tx_cn61xx cnf71xx; }; - -#endif diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 209eddc..2180176 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -15,7 +15,8 @@ #include #include -#include + +#include "spi-cavium.h" #define OCTEON_SPI_MAX_BYTES 9 -- 2.9.0.rc0.21.g322
[PATCH 0/6] SPI ThunderX driver
Hi Mark, This series adds support for SPI on Cavium's ThunderX (arm64). The SPI hardware is the same as on MIPS Octeon, the only difference is that the device appears as a PCI device. To avoid copy and paste of the Octeon driver I've moved the common parts into a shared file. Patches #1-5 prepare the Octeon driver for re-use. Patch #6 adds the ThunderX driver. The series was tested on MIPS (Edge Router PRO and cn71xx) and ThunderX. Feedback welcome! thanks, Jan Jan Glauber (5): spi: octeon: Store system clock freqency in struct octeon_spi spi: octeon: Put register offsets into a struct spi: octeon: Move include file from arch/mips to drivers/spi spi: octeon: Split driver into Octeon specific and common parts spi: octeon: Add thunderx driver Steven J. Hill (1): spi: octeon: Convert driver to use readq()/writeq() functions drivers/spi/Kconfig| 7 + drivers/spi/Makefile | 3 + drivers/spi/spi-cavium-octeon.c| 104 + drivers/spi/spi-cavium-thunderx.c | 158 + drivers/spi/spi-cavium.c | 151 .../cvmx-mpi-defs.h => drivers/spi/spi-cavium.h| 62 ++--- drivers/spi/spi-octeon.c | 255 - 7 files changed, 456 insertions(+), 284 deletions(-) create mode 100644 drivers/spi/spi-cavium-octeon.c create mode 100644 drivers/spi/spi-cavium-thunderx.c create mode 100644 drivers/spi/spi-cavium.c rename arch/mips/include/asm/octeon/cvmx-mpi-defs.h => drivers/spi/spi-cavium.h (84%) delete mode 100644 drivers/spi/spi-octeon.c -- 2.9.0.rc0.21.g322
[PATCH 3/6] spi: octeon: Put register offsets into a struct
Instead of hard-coding the register offsets put them into a struct and set them in the probe function. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill --- drivers/spi/spi-octeon.c | 41 +++-- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index e722040..209eddc 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -17,22 +17,30 @@ #include #include -#define OCTEON_SPI_CFG 0 -#define OCTEON_SPI_STS 0x08 -#define OCTEON_SPI_TX 0x10 -#define OCTEON_SPI_DAT0 0x80 - #define OCTEON_SPI_MAX_BYTES 9 #define OCTEON_SPI_MAX_CLOCK_HZ 1600 +struct octeon_spi_regs { + int config; + int status; + int tx; + int data; +}; + struct octeon_spi { void __iomem *register_base; u64 last_cfg; u64 cs_enax; int sys_freq; + struct octeon_spi_regs regs; }; +#define OCTEON_SPI_CFG(x) (x->regs.config) +#define OCTEON_SPI_STS(x) (x->regs.status) +#define OCTEON_SPI_TX(x) (x->regs.tx) +#define OCTEON_SPI_DAT0(x) (x->regs.data) + static void octeon_spi_wait_ready(struct octeon_spi *p) { union cvmx_mpi_sts mpi_sts; @@ -41,7 +49,7 @@ static void octeon_spi_wait_ready(struct octeon_spi *p) do { if (loops++) __delay(500); - mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS); + mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p)); } while (mpi_sts.s.busy); } @@ -83,7 +91,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, if (mpi_cfg.u64 != p->last_cfg) { p->last_cfg = mpi_cfg.u64; - writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG); + writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p)); } tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; @@ -95,19 +103,19 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); } mpi_tx.u64 = 0; mpi_tx.s.csid = spi->chip_select; mpi_tx.s.leavecs = 1; mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); *rx_buf++ = (u8)v; } len -= OCTEON_SPI_MAX_BYTES; @@ -119,7 +127,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); } mpi_tx.u64 = 0; @@ -130,12 +138,12 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, mpi_tx.s.leavecs = !xfer->cs_change; mpi_tx.s.txnum = tx_buf ? len : 0; mpi_tx.s.totnum = len; - writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < len; i++) { - u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); *rx_buf++ = (u8)v; } @@ -194,6 +202,11 @@ static int octeon_spi_probe(struct platform_device *pdev) p->register_base = reg_base; p->sys_freq = octeon_get_io_clock_rate(); + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + master->num_chipselect = 4; master->mode_bits = SPI_CPHA | SPI_CPOL | @@ -226,7 +239,7 @@ static int octeon_spi_remove(struct platform_device *pdev) struct octeon_spi *p = spi_master_get_devdata(master); /* Clear the CSENA* and put everything in a known state. */ - writeq(0, p->register_base + OCTEON_SPI_CFG); + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); return 0; } -- 2.9.0.rc0.21.g322
[PATCH 6/6] spi: octeon: Add thunderx driver
Add ThunderX SPI driver using the shared part from the Octeon driver. The main difference of the ThunderX driver is that it is a PCI device so probing is different. The system clock settings can be specified in device tree. Signed-off-by: Jan Glauber --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 2 + drivers/spi/spi-cavium-thunderx.c | 158 ++ drivers/spi/spi-cavium.h | 3 + 4 files changed, 170 insertions(+) create mode 100644 drivers/spi/spi-cavium-thunderx.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b931ec..db02ba7 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -630,6 +630,13 @@ config SPI_TEGRA20_SLINK help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. +config SPI_THUNDERX + tristate "Cavium ThunderX SPI controller" + depends on 64BIT && PCI && !CAVIUM_OCTEON_SOC + help + SPI host driver for the hardware found on Cavium ThunderX + SOCs. + config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" depends on PCI && (X86_32 || MIPS || COMPILE_TEST) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 185367e..133364b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_SPI_TEGRA114)+= spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK)+= spi-tegra20-slink.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o +spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o +obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM)+= spi-xcomm.o diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c new file mode 100644 index 000..7eb9141 --- /dev/null +++ b/drivers/spi/spi-cavium-thunderx.c @@ -0,0 +1,158 @@ +/* + * Cavium ThunderX SPI driver. + * + * Copyright (C) 2016 Cavium Inc. + * Authors: Jan Glauber + */ + +#include +#include +#include +#include +#include + +#include "spi-cavium.h" + +#define DRV_NAME "spi-thunderx" + +#define SYS_FREQ_DEFAULT 7 + +static void thunderx_spi_clock_enable(struct device *dev, struct octeon_spi *p) +{ + int ret; + + p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(p->clk)) { + p->clk = NULL; + goto skip; + } + + ret = clk_prepare_enable(p->clk); + if (ret) + goto skip; + p->sys_freq = clk_get_rate(p->clk); + +skip: + if (!p->sys_freq) + p->sys_freq = SYS_FREQ_DEFAULT; + + dev_info(dev, "Set system clock to %u\n", p->sys_freq); +} + +static void thunderx_spi_clock_disable(struct device *dev, struct clk *clk) +{ + if (!clk) + return; + clk_disable_unprepare(clk); + devm_clk_put(dev, clk); +} + +static int thunderx_spi_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = >dev; + struct spi_master *master; + struct octeon_spi *p; + int ret = -ENOENT; + + master = spi_alloc_master(dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "Failed to enable PCI device\n"); + goto out_free; + } + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) { + dev_err(dev, "PCI request regions failed 0x%x\n", ret); + goto out_disable; + } + + p->register_base = pci_ioremap_bar(pdev, 0); + if (!p->register_base) { + dev_err(dev, "Cannot map reg base\n"); + ret = -EINVAL; + goto out_region; + } + + p->regs.config = 0x1000; + p->regs.status = 0x1008; + p->regs.tx = 0x1010; + p->regs.data = 0x1080; + + thunderx_spi_clock_enable(dev, p); + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST | SPI_3WIRE; + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + master->dev.of_node = pdev->dev.of_node; + + pci_set_drvdata(pdev, master); + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(>
[PATCH 2/6] spi: octeon: Store system clock freqency in struct octeon_spi
Storing the system clock frequency in struct octeon_spi avoids calling the MIPS specific octeon_get_io_clock_rate() for every transfer. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill --- drivers/spi/spi-octeon.c | 7 +++ 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index b53ba53..e722040 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -30,6 +30,7 @@ struct octeon_spi { void __iomem *register_base; u64 last_cfg; u64 cs_enax; + int sys_freq; }; static void octeon_spi_wait_ready(struct octeon_spi *p) @@ -53,7 +54,6 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, union cvmx_mpi_cfg mpi_cfg; union cvmx_mpi_tx mpi_tx; unsigned int clkdiv; - unsigned int speed_hz; int mode; bool cpha, cpol; const u8 *tx_buf; @@ -65,9 +65,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, cpha = mode & SPI_CPHA; cpol = mode & SPI_CPOL; - speed_hz = xfer->speed_hz; - - clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz); + clkdiv = p->sys_freq / (2 * xfer->speed_hz); mpi_cfg.u64 = 0; @@ -194,6 +192,7 @@ static int octeon_spi_probe(struct platform_device *pdev) } p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); master->num_chipselect = 4; master->mode_bits = SPI_CPHA | -- 2.9.0.rc0.21.g322
[PATCH 1/6] spi: octeon: Convert driver to use readq()/writeq() functions
From: "Steven J. Hill" Remove all calls to cvmx_read_csr()/cvmx_write_csr() and use the portable readq()/writeq() functions. Signed-off-by: Steven J. Hill Signed-off-by: Jan Glauber --- drivers/spi/spi-octeon.c | 23 +++ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 3b17009..b53ba53 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -27,7 +27,7 @@ #define OCTEON_SPI_MAX_CLOCK_HZ 1600 struct octeon_spi { - u64 register_base; + void __iomem *register_base; u64 last_cfg; u64 cs_enax; }; @@ -40,7 +40,7 @@ static void octeon_spi_wait_ready(struct octeon_spi *p) do { if (loops++) __delay(500); - mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS); + mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS); } while (mpi_sts.s.busy); } @@ -85,7 +85,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, if (mpi_cfg.u64 != p->last_cfg) { p->last_cfg = mpi_cfg.u64; - cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64); + writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG); } tx_buf = xfer->tx_buf; rx_buf = xfer->rx_buf; @@ -97,19 +97,19 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); + writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); } mpi_tx.u64 = 0; mpi_tx.s.csid = spi->chip_select; mpi_tx.s.leavecs = 1; mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); *rx_buf++ = (u8)v; } len -= OCTEON_SPI_MAX_BYTES; @@ -121,7 +121,7 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, d = *tx_buf++; else d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); + writeq(d, p->register_base + OCTEON_SPI_DAT0 + (8 * i)); } mpi_tx.u64 = 0; @@ -132,12 +132,12 @@ static int octeon_spi_do_transfer(struct octeon_spi *p, mpi_tx.s.leavecs = !xfer->cs_change; mpi_tx.s.txnum = tx_buf ? len : 0; mpi_tx.s.totnum = len; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX); octeon_spi_wait_ready(p); if (rx_buf) for (i = 0; i < len; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); + u64 v = readq(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); *rx_buf++ = (u8)v; } @@ -193,7 +193,7 @@ static int octeon_spi_probe(struct platform_device *pdev) goto fail; } - p->register_base = (u64)reg_base; + p->register_base = reg_base; master->num_chipselect = 4; master->mode_bits = SPI_CPHA | @@ -225,10 +225,9 @@ static int octeon_spi_remove(struct platform_device *pdev) { struct spi_master *master = platform_get_drvdata(pdev); struct octeon_spi *p = spi_master_get_devdata(master); - u64 register_base = p->register_base; /* Clear the CSENA* and put everything in a known state. */ - cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); + writeq(0, p->register_base + OCTEON_SPI_CFG); return 0; } -- 2.9.0.rc0.21.g322
[PATCH 5/6] spi: octeon: Split driver into Octeon specific and common parts
Separate driver probing from SPI transfer functions. Signed-off-by: Jan Glauber Tested-by: Steven J. Hill --- drivers/spi/Makefile | 1 + drivers/spi/spi-cavium-octeon.c| 104 + drivers/spi/{spi-octeon.c => spi-cavium.c} | 120 + drivers/spi/spi-cavium.h | 31 4 files changed, 138 insertions(+), 118 deletions(-) create mode 100644 drivers/spi/spi-cavium-octeon.c rename drivers/spi/{spi-octeon.c => spi-cavium.c} (55%) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3c74d00..185367e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX)+= spi-mt65xx.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o +spi-octeon-objs:= spi-cavium.o spi-cavium-octeon.o obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K)+= spi-omap-100k.o diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c new file mode 100644 index 000..ee4703e --- /dev/null +++ b/drivers/spi/spi-cavium-octeon.c @@ -0,0 +1,104 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include +#include +#include +#include +#include + +#include + +#include "spi-cavium.h" + +static int octeon_spi_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + void __iomem *reg_base; + struct spi_master *master; + struct octeon_spi *p; + int err = -ENOENT; + + master = spi_alloc_master(>dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + platform_set_drvdata(pdev, master); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(>dev, res_mem); + if (IS_ERR(reg_base)) { + err = PTR_ERR(reg_base); + goto fail; + } + + p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); + + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | + SPI_CPOL | + SPI_CS_HIGH | + SPI_LSB_FIRST | + SPI_3WIRE; + + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + + master->dev.of_node = pdev->dev.of_node; + err = devm_spi_register_master(>dev, master); + if (err) { + dev_err(>dev, "register master failed: %d\n", err); + goto fail; + } + + dev_info(>dev, "OCTEON SPI bus driver\n"); + + return 0; +fail: + spi_master_put(master); + return err; +} + +static int octeon_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct octeon_spi *p = spi_master_get_devdata(master); + + /* Clear the CSENA* and put everything in a known state. */ + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); + + return 0; +} + +static const struct of_device_id octeon_spi_match[] = { + { .compatible = "cavium,octeon-3010-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_spi_match); + +static struct platform_driver octeon_spi_driver = { + .driver = { + .name = "spi-octeon", + .of_match_table = octeon_spi_match, + }, + .probe = octeon_spi_probe, + .remove = octeon_spi_remove, +}; + +module_platform_driver(octeon_spi_driver); + +MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-cavium.c similarity index 55% rename from drivers/spi/spi-octeon.c rename to drivers/spi/spi-cavium.c index 2180176..5aaf215 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-cavium.c @@ -6,42 +6,13 @@ * Copyright (C) 2011, 2012 Cavium, Inc. */ -#include -#include #include #include #include #include -#include - -#include #include "spi-cavium.h" -#define OCTEON_SPI_MAX_BYTES 9 - -#define OCTEON_SPI_MAX_CLOC
Re: [PATCH v2] spi: octeon: Add thunderx driver
On Wed, Jul 27, 2016 at 07:08:24PM +0100, Mark Brown wrote: > On Mon, Jul 25, 2016 at 07:56:22PM +0200, Jan Glauber wrote: > > Add ThunderX SPI driver using the shared part from the Octeon > > driver. The main difference of the ThunderX driver is that it > > is a PCI device so probing is different. The system clock settings > > can be specified in device tree. > > Don't send individual patches in reply to the middle of threads, it > makes it really confusing what's going on. I now have multiple patches > from you for this driver completely unthreaded in my inbox with no > indication of ordering or anything. Please resend anything that's > pending as a proper patch series. With multiple being exactly two. I thought it to be easier this way around and the ordering to be obvious (if you use threading), but of course I can resend the two patches as a new series.
[PATCH v2 0/2] SPI ThunderX driver
This series adds support for SPI on Cavium's ThunderX (arm64). The SPI hardware is the same as on MIPS Octeon, the only difference is that the device appears as a PCI device. To avoid copy and paste of the Octeon driver I've moved the common parts into a shared file. The series was tested on MIPS (Edge Router PRO and cn71xx) and ThunderX. Changes to v1: - Changed Kconfig depencency for ThunderX - Merged clock setup functions with main probe/remove - Fail if SPI DT entry misses clock reference - Removed debug prints at end of probe - Removed unneeded includes from spi-cavium.c - Dropped merged patches thanks, Jan -- Jan Glauber (2): spi: octeon: Split driver into Octeon specific and common parts spi: octeon: Add thunderx driver drivers/spi/Kconfig| 7 ++ drivers/spi/Makefile | 3 + drivers/spi/spi-cavium-octeon.c| 102 + drivers/spi/spi-cavium-thunderx.c | 140 + drivers/spi/{spi-octeon.c => spi-cavium.c} | 122 + drivers/spi/spi-cavium.h | 34 +++ 6 files changed, 288 insertions(+), 120 deletions(-) create mode 100644 drivers/spi/spi-cavium-octeon.c create mode 100644 drivers/spi/spi-cavium-thunderx.c rename drivers/spi/{spi-octeon.c => spi-cavium.c} (54%) -- 2.9.0.rc0.21.g322
[PATCH v2 2/2] spi: octeon: Add thunderx driver
Add ThunderX SPI driver using the shared part from the Octeon driver. The main difference of the ThunderX driver is that it is a PCI device so probing is different. The system clock settings can be specified in device tree. Signed-off-by: Jan Glauber --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 2 + drivers/spi/spi-cavium-thunderx.c | 140 ++ drivers/spi/spi-cavium.h | 3 + 4 files changed, 152 insertions(+) create mode 100644 drivers/spi/spi-cavium-thunderx.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b931ec..e0ee112 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -630,6 +630,13 @@ config SPI_TEGRA20_SLINK help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. +config SPI_THUNDERX + tristate "Cavium ThunderX SPI controller" + depends on (ARM64 || CONFIG_TEST) && 64BIT && PCI + help + SPI host driver for the hardware found on Cavium ThunderX + SOCs. + config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" depends on PCI && (X86_32 || MIPS || COMPILE_TEST) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 185367e..133364b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_SPI_TEGRA114)+= spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK)+= spi-tegra20-slink.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o +spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o +obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM)+= spi-xcomm.o diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c new file mode 100644 index 000..28c3dcc --- /dev/null +++ b/drivers/spi/spi-cavium-thunderx.c @@ -0,0 +1,140 @@ +/* + * Cavium ThunderX SPI driver. + * + * Copyright (C) 2016 Cavium Inc. + * Authors: Jan Glauber + */ + +#include +#include +#include +#include +#include + +#include "spi-cavium.h" + +#define DRV_NAME "spi-thunderx" + +#define SYS_FREQ_DEFAULT 7 /* 700 Mhz */ + +static int thunderx_spi_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = >dev; + struct spi_master *master; + struct octeon_spi *p; + int ret = -ENOENT; + + master = spi_alloc_master(dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "Failed to enable PCI device\n"); + goto out_free; + } + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) { + dev_err(dev, "PCI request regions failed 0x%x\n", ret); + goto out_disable; + } + + p->register_base = pci_ioremap_bar(pdev, 0); + if (!p->register_base) { + dev_err(dev, "Cannot map reg base\n"); + ret = -EINVAL; + goto out_region; + } + + p->regs.config = 0x1000; + p->regs.status = 0x1008; + p->regs.tx = 0x1010; + p->regs.data = 0x1080; + + p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(p->clk)) + goto out_unmap; + + ret = clk_prepare_enable(p->clk); + if (ret) + goto out_clock_devm; + + p->sys_freq = clk_get_rate(p->clk); + if (!p->sys_freq) + p->sys_freq = SYS_FREQ_DEFAULT; + dev_info(dev, "Set system clock to %u\n", p->sys_freq); + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST | SPI_3WIRE; + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + master->dev.of_node = pdev->dev.of_node; + + pci_set_drvdata(pdev, master); + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(>dev, "Register master failed: %d\n", ret); + goto out_clock; + } + + return 0; + +out_clock: + clk_disable_unprepare(p->clk); +out_clock_devm: + devm_clk_put(dev, p->clk); +out_unmap: + iounmap(p->register_base); +out_region: + pci_release_regions(pdev); +out_disable: + pci_disable_device(pdev); +out_fr
[PATCH v2 1/2] spi: octeon: Split driver into Octeon specific and common parts
Separate driver probing from SPI transfer functions. Signed-off-by: Jan Glauber --- drivers/spi/Makefile | 1 + drivers/spi/spi-cavium-octeon.c| 102 drivers/spi/{spi-octeon.c => spi-cavium.c} | 122 + drivers/spi/spi-cavium.h | 31 4 files changed, 136 insertions(+), 120 deletions(-) create mode 100644 drivers/spi/spi-cavium-octeon.c rename drivers/spi/{spi-octeon.c => spi-cavium.c} (54%) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3c74d00..185367e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX)+= spi-mt65xx.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o +spi-octeon-objs:= spi-cavium.o spi-cavium-octeon.o obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K)+= spi-omap-100k.o diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c new file mode 100644 index 000..97310c1 --- /dev/null +++ b/drivers/spi/spi-cavium-octeon.c @@ -0,0 +1,102 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include +#include +#include +#include +#include + +#include + +#include "spi-cavium.h" + +static int octeon_spi_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + void __iomem *reg_base; + struct spi_master *master; + struct octeon_spi *p; + int err = -ENOENT; + + master = spi_alloc_master(>dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + platform_set_drvdata(pdev, master); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(>dev, res_mem); + if (IS_ERR(reg_base)) { + err = PTR_ERR(reg_base); + goto fail; + } + + p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); + + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | + SPI_CPOL | + SPI_CS_HIGH | + SPI_LSB_FIRST | + SPI_3WIRE; + + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + + master->dev.of_node = pdev->dev.of_node; + err = devm_spi_register_master(>dev, master); + if (err) { + dev_err(>dev, "register master failed: %d\n", err); + goto fail; + } + + return 0; +fail: + spi_master_put(master); + return err; +} + +static int octeon_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct octeon_spi *p = spi_master_get_devdata(master); + + /* Clear the CSENA* and put everything in a known state. */ + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); + + return 0; +} + +static const struct of_device_id octeon_spi_match[] = { + { .compatible = "cavium,octeon-3010-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_spi_match); + +static struct platform_driver octeon_spi_driver = { + .driver = { + .name = "spi-octeon", + .of_match_table = octeon_spi_match, + }, + .probe = octeon_spi_probe, + .remove = octeon_spi_remove, +}; + +module_platform_driver(octeon_spi_driver); + +MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-cavium.c similarity index 54% rename from drivers/spi/spi-octeon.c rename to drivers/spi/spi-cavium.c index 2180176..8857e7d 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-cavium.c @@ -6,42 +6,11 @@ * Copyright (C) 2011, 2012 Cavium, Inc. */ -#include -#include #include -#include #include -#include -#include - -#include #include "spi-cavium.h" -#define OCTEON_SPI_MAX_BYTES 9 - -#define OCTEON_SPI_MAX_CLOCK_HZ 1600 - -struct octeon_spi_regs { - int config; - int status; - int
Re: [PATCH 5/6] spi: octeon: Split driver into Octeon specific and common parts
On Sun, Jul 24, 2016 at 02:38:11PM -0400, Paul Gortmaker wrote: > On Sat, Jul 23, 2016 at 6:42 AM, Jan Glauber wrote: > > Separate driver probing from SPI transfer functions. > > > > Signed-off-by: Jan Glauber > > Tested-by: Steven J. Hill > > --- > > drivers/spi/Makefile | 1 + > > drivers/spi/spi-cavium-octeon.c| 104 + > > drivers/spi/{spi-octeon.c => spi-cavium.c} | 120 > > + > > drivers/spi/spi-cavium.h | 31 > > 4 files changed, 138 insertions(+), 118 deletions(-) > > create mode 100644 drivers/spi/spi-cavium-octeon.c > > rename drivers/spi/{spi-octeon.c => spi-cavium.c} (55%) > > > > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > > index 3c74d00..185367e 100644 > > --- a/drivers/spi/Makefile > > +++ b/drivers/spi/Makefile > > @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX)+= spi-mt65xx.o > > obj-$(CONFIG_SPI_MXS) += spi-mxs.o > > obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o > > obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o > > +spi-octeon-objs:= spi-cavium.o > > spi-cavium-octeon.o > > obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o > > obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o > > obj-$(CONFIG_SPI_OMAP_100K)+= spi-omap-100k.o > > diff --git a/drivers/spi/spi-cavium-octeon.c > > b/drivers/spi/spi-cavium-octeon.c > > new file mode 100644 > > index 000..ee4703e > > --- /dev/null > > +++ b/drivers/spi/spi-cavium-octeon.c > > @@ -0,0 +1,104 @@ > > +/* > > + * This file is subject to the terms and conditions of the GNU General > > Public > > + * License. See the file "COPYING" in the main directory of this archive > > + * for more details. > > + * > > + * Copyright (C) 2011, 2012 Cavium, Inc. > > + */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > [...] > > > +MODULE_DEVICE_TABLE(of, octeon_spi_match); > > + > > +static struct platform_driver octeon_spi_driver = { > > + .driver = { > > + .name = "spi-octeon", > > + .of_match_table = octeon_spi_match, > > + }, > > + .probe = octeon > _spi_probe, > > + .remove = octeon_spi_remove, > > +}; > > + > > +module_platform_driver(octeon_spi_driver); > > + > > +MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); > > +MODULE_AUTHOR("David Daney"); > > +MODULE_LICENSE("GPL"); > > diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-cavium.c > > similarity index 55% > > rename from drivers/spi/spi-octeon.c > > rename to drivers/spi/spi-cavium.c > > index 2180176..5aaf215 100644 > > --- a/drivers/spi/spi-octeon.c > > +++ b/drivers/spi/spi-cavium.c > > @@ -6,42 +6,13 @@ > > * Copyright (C) 2011, 2012 Cavium, Inc. > > */ > > > > -#include > > -#include > > #include > > #include > > It almost looks like all the modular stuff got moved to the new file and > maybe the above module.h isn't needed in the original file anymore? > > Paul. > -- Yes, that can be removed. Also io.h isn't needed there. thanks, Jan > > #include > > #include > > -#include > > - > > -#include > > > > #include "spi-cavium.h" > > [...]
Re: [PATCH 5/6] spi: octeon: Split driver into Octeon specific and common parts
On Sun, Jul 24, 2016 at 09:54:16PM +0100, Mark Brown wrote: > On Sat, Jul 23, 2016 at 12:42:54PM +0200, Jan Glauber wrote: > > > + dev_info(>dev, "OCTEON SPI bus driver\n"); > > This is just noise, remove it. I'll remove these in both drivers. Thanks, Jan
Re: [PATCH 6/6] spi: octeon: Add thunderx driver
On Sun, Jul 24, 2016 at 10:04:52PM +0100, Mark Brown wrote: > On Sat, Jul 23, 2016 at 12:42:55PM +0200, Jan Glauber wrote: > > > +config SPI_THUNDERX > > + tristate "Cavium ThunderX SPI controller" > > + depends on 64BIT && PCI && !CAVIUM_OCTEON_SOC > > This is a *weird* and most likely broken set of dependencies - why > exclude this if we're on Octeon (or Octeon happens to have been enabled > in a config)? I agree that it looks weird, the reasoning is that we would like to avoid making the driver depend on something like ARCH_THUNDER. So I made the driver depend on the things it actually uses (PCI for probing and 64BIT because of readq/writeq) and don't care if it compiles on other platforms too (like x86). That said, I can remove the !CAVIUM_OCTEON_SOC, it compiles without errors on MIPS too. Would that be ok? > > +static void thunderx_spi_clock_enable(struct device *dev, struct > > octeon_spi *p) > > +{ > > + int ret; > > + > > + p->clk = devm_clk_get(dev, NULL); > > + if (IS_ERR(p->clk)) { > > + p->clk = NULL; > > + goto skip; > > + } > > This is really not clever - we should be requesting clocks on probe, not > only when we're trying to enable them, and using devm_ outside of probe > paths is usually a warning sign too. Now, this is actually called from > probe so it works out fine but obviously it'd be better to improve the > power management to only enable the clock when needed and at that point > this function will be used and we'll fall into a bad pattern. > > Given how tiny this function is and that we've not bothered splitting > out any of the other resource acquisition it's probably better to just > inline it into probe. OK, I'll merge it into the probe function. > > + dev_info(>dev, "Cavium SPI bus driver probed\n"); > > Again, this is just adding noise to the boot log. > > > +#define PCI_DEVICE_ID_THUNDERX_SPI 0xa00b > > + > > +static const struct pci_device_id thunderx_spi_pci_id_table[] = { > > + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDERX_SPI) }, > > + { 0, } > > +}; > > The define for the device ID doesn't seem to be adding much here. I find it more readable instead of PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xa00b), or did I miss your point? thanks, Jan
[PATCH v2] spi: octeon: Split driver into Octeon specific and common parts
Separate driver probing from SPI transfer functions. Signed-off-by: Jan Glauber --- drivers/spi/Makefile | 1 + drivers/spi/spi-cavium-octeon.c| 102 drivers/spi/{spi-octeon.c => spi-cavium.c} | 122 + drivers/spi/spi-cavium.h | 31 4 files changed, 136 insertions(+), 120 deletions(-) create mode 100644 drivers/spi/spi-cavium-octeon.c rename drivers/spi/{spi-octeon.c => spi-cavium.c} (54%) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3c74d00..185367e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX)+= spi-mt65xx.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o +spi-octeon-objs:= spi-cavium.o spi-cavium-octeon.o obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K)+= spi-omap-100k.o diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c new file mode 100644 index 000..97310c1 --- /dev/null +++ b/drivers/spi/spi-cavium-octeon.c @@ -0,0 +1,102 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include +#include +#include +#include +#include + +#include + +#include "spi-cavium.h" + +static int octeon_spi_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + void __iomem *reg_base; + struct spi_master *master; + struct octeon_spi *p; + int err = -ENOENT; + + master = spi_alloc_master(>dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + platform_set_drvdata(pdev, master); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(>dev, res_mem); + if (IS_ERR(reg_base)) { + err = PTR_ERR(reg_base); + goto fail; + } + + p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); + + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | + SPI_CPOL | + SPI_CS_HIGH | + SPI_LSB_FIRST | + SPI_3WIRE; + + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + + master->dev.of_node = pdev->dev.of_node; + err = devm_spi_register_master(>dev, master); + if (err) { + dev_err(>dev, "register master failed: %d\n", err); + goto fail; + } + + return 0; +fail: + spi_master_put(master); + return err; +} + +static int octeon_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct octeon_spi *p = spi_master_get_devdata(master); + + /* Clear the CSENA* and put everything in a known state. */ + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); + + return 0; +} + +static const struct of_device_id octeon_spi_match[] = { + { .compatible = "cavium,octeon-3010-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_spi_match); + +static struct platform_driver octeon_spi_driver = { + .driver = { + .name = "spi-octeon", + .of_match_table = octeon_spi_match, + }, + .probe = octeon_spi_probe, + .remove = octeon_spi_remove, +}; + +module_platform_driver(octeon_spi_driver); + +MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-cavium.c similarity index 54% rename from drivers/spi/spi-octeon.c rename to drivers/spi/spi-cavium.c index 2180176..8857e7d 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-cavium.c @@ -6,42 +6,11 @@ * Copyright (C) 2011, 2012 Cavium, Inc. */ -#include -#include #include -#include #include -#include -#include - -#include #include "spi-cavium.h" -#define OCTEON_SPI_MAX_BYTES 9 - -#define OCTEON_SPI_MAX_CLOCK_HZ 1600 - -struct octeon_spi_regs { - int config; - int status; - int
[PATCH v2] spi: octeon: Add thunderx driver
Add ThunderX SPI driver using the shared part from the Octeon driver. The main difference of the ThunderX driver is that it is a PCI device so probing is different. The system clock settings can be specified in device tree. Signed-off-by: Jan Glauber --- drivers/spi/Kconfig | 7 ++ drivers/spi/Makefile | 2 + drivers/spi/spi-cavium-thunderx.c | 140 ++ drivers/spi/spi-cavium.h | 3 + 4 files changed, 152 insertions(+) create mode 100644 drivers/spi/spi-cavium-thunderx.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b931ec..e0ee112 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -630,6 +630,13 @@ config SPI_TEGRA20_SLINK help SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface. +config SPI_THUNDERX + tristate "Cavium ThunderX SPI controller" + depends on (ARM64 || CONFIG_TEST) && 64BIT && PCI + help + SPI host driver for the hardware found on Cavium ThunderX + SOCs. + config SPI_TOPCLIFF_PCH tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) SPI" depends on PCI && (X86_32 || MIPS || COMPILE_TEST) diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 185367e..133364b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -91,6 +91,8 @@ obj-$(CONFIG_SPI_TEGRA114)+= spi-tegra114.o obj-$(CONFIG_SPI_TEGRA20_SFLASH) += spi-tegra20-sflash.o obj-$(CONFIG_SPI_TEGRA20_SLINK)+= spi-tegra20-slink.o obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o +spi-thunderx-objs := spi-cavium.o spi-cavium-thunderx.o +obj-$(CONFIG_SPI_THUNDERX) += spi-thunderx.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o obj-$(CONFIG_SPI_TXX9) += spi-txx9.o obj-$(CONFIG_SPI_XCOMM)+= spi-xcomm.o diff --git a/drivers/spi/spi-cavium-thunderx.c b/drivers/spi/spi-cavium-thunderx.c new file mode 100644 index 000..28c3dcc --- /dev/null +++ b/drivers/spi/spi-cavium-thunderx.c @@ -0,0 +1,140 @@ +/* + * Cavium ThunderX SPI driver. + * + * Copyright (C) 2016 Cavium Inc. + * Authors: Jan Glauber + */ + +#include +#include +#include +#include +#include + +#include "spi-cavium.h" + +#define DRV_NAME "spi-thunderx" + +#define SYS_FREQ_DEFAULT 7 /* 700 Mhz */ + +static int thunderx_spi_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = >dev; + struct spi_master *master; + struct octeon_spi *p; + int ret = -ENOENT; + + master = spi_alloc_master(dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "Failed to enable PCI device\n"); + goto out_free; + } + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) { + dev_err(dev, "PCI request regions failed 0x%x\n", ret); + goto out_disable; + } + + p->register_base = pci_ioremap_bar(pdev, 0); + if (!p->register_base) { + dev_err(dev, "Cannot map reg base\n"); + ret = -EINVAL; + goto out_region; + } + + p->regs.config = 0x1000; + p->regs.status = 0x1008; + p->regs.tx = 0x1010; + p->regs.data = 0x1080; + + p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(p->clk)) + goto out_unmap; + + ret = clk_prepare_enable(p->clk); + if (ret) + goto out_clock_devm; + + p->sys_freq = clk_get_rate(p->clk); + if (!p->sys_freq) + p->sys_freq = SYS_FREQ_DEFAULT; + dev_info(dev, "Set system clock to %u\n", p->sys_freq); + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | + SPI_LSB_FIRST | SPI_3WIRE; + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + master->dev.of_node = pdev->dev.of_node; + + pci_set_drvdata(pdev, master); + ret = devm_spi_register_master(dev, master); + if (ret) { + dev_err(>dev, "Register master failed: %d\n", ret); + goto out_clock; + } + + return 0; + +out_clock: + clk_disable_unprepare(p->clk); +out_clock_devm: + devm_clk_put(dev, p->clk); +out_unmap: + iounmap(p->register_base); +out_region: + pci_release_regions(pdev); +out_disable: + pci_disable_device(pdev); +out_fr
[PATCH] i2c: Prevent endless uevent loop with dev_dbg
After enabling CONFIG_I2C_DEBUG_CORE my system was broken (no network, console login not possible). System log was flooded with the this message: ... [ 608.052077] rtc-ds1307 0-0068: uevent [ 608.052500] rtc-ds1307 0-0068: uevent [ 608.052925] rtc-ds1307 0-0068: uevent ... The culprit is the dev_dbg printk in the i2c uevent handler. If this is activated (for instance by CONFIG_I2C_DEBUG_CORE) it results in an endless loop with systemd-journald. This happens if user-space scans the system log and reads the uevent file to get information about a newly created device, which seems fair use to me. Unfortunately reading the "uevent" file uses the same function that runs for creating the uevent for a new device, generating the next syslog entry. Ideally user-space would implement a recursion detection and after reading the same device file for the 1000th time call it a day, but nevertheless I think we should avoid this problem by removing the debug print completly or using another print variant. The same problem seems to be reported here: https://bugs.freedesktop.org/show_bug.cgi?id=76886 Signed-off-by: Jan Glauber --- drivers/i2c/i2c-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 0f2f848..84165d9 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -539,7 +539,7 @@ static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "MODALIAS=%s%s", I2C_MODULE_PREFIX, client->name)) return -ENOMEM; - dev_dbg(dev, "uevent\n"); + pr_debug("uevent: device: %s\n", dev_name(dev)); return 0; } -- 1.9.1
Re: [PATCH] i2c: Prevent endless uevent loop with dev_dbg
On Wed, Mar 23, 2016 at 05:50:33PM +0100, Wolfram Sang wrote: > On Wed, Mar 23, 2016 at 04:50:47PM +0100, Jan Glauber wrote: > > After enabling CONFIG_I2C_DEBUG_CORE my system was broken > > (no network, console login not possible). System log was > > flooded with the this message: > > > > ... > > [ 608.052077] rtc-ds1307 0-0068: uevent > > [ 608.052500] rtc-ds1307 0-0068: uevent > > [ 608.052925] rtc-ds1307 0-0068: uevent > > ... > > > > The culprit is the dev_dbg printk in the i2c uevent handler. > > If this is activated (for instance by CONFIG_I2C_DEBUG_CORE) > > it results in an endless loop with systemd-journald. > > > > This happens if user-space scans the system log and reads the uevent > > file to get information about a newly created device, which seems fair > > use to me. Unfortunately reading the "uevent" file uses the same > > function that runs for creating the uevent for a new device, > > generating the next syslog entry. > > > > Ideally user-space would implement a recursion detection and > > after reading the same device file for the 1000th time call it a > > day, but nevertheless I think we should avoid this problem by > > removing the debug print completly or using another print variant. > > Thanks for pointing out this problem. I think this debug can go. I also > think the function can be cleaned up some more. Can you test this patch? Works for me! Thanks, Jan > Thanks, > >Wolfram > > diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c > index 0f2f8484e8ec1f..e584d88ee337f6 100644 > --- a/drivers/i2c/i2c-core.c > +++ b/drivers/i2c/i2c-core.c > @@ -525,22 +525,16 @@ static int i2c_device_match(struct device *dev, struct > device_driver *drv) > return 0; > } > > - > -/* uevent helps with hotplug: modprobe -q $(MODALIAS) */ > static int i2c_device_uevent(struct device *dev, struct kobj_uevent_env *env) > { > - struct i2c_client *client = to_i2c_client(dev); > + struct i2c_client *client = to_i2c_client(dev); > int rc; > > rc = acpi_device_uevent_modalias(dev, env); > if (rc != -ENODEV) > return rc; > > - if (add_uevent_var(env, "MODALIAS=%s%s", > -I2C_MODULE_PREFIX, client->name)) > - return -ENOMEM; > - dev_dbg(dev, "uevent\n"); > - return 0; > + return add_uevent_var(env, "MODALIAS=%s%s", I2C_MODULE_PREFIX, > client->name); > } > > /* i2c bus recovery routines */ >
Re: [PATCH v2 0/5] Cavium ThunderX uncore PMU support
On Tue, Jun 28, 2016 at 11:24:20AM +0100, Will Deacon wrote: > Hi Jan, > > On Wed, Mar 09, 2016 at 05:21:02PM +0100, Jan Glauber wrote: > > This patch series provides access to various counters on the ThunderX SOC. > > > > For details of the uncore implementation see patch #1. > > > > Patches #2-5 add the various ThunderX specific PMUs. > > > > As suggested I've put the files under drivers/perf/uncore. I would > > prefer this location over drivers/bus because not all of the uncore > > drivers are bus related. > > What's the status of these patches? Were you planning to send a new > version? > > Will Hi Will, I was half-way through with addressing Mark's review comments when got side-tracked. The principle question these patches raised remains open though in my opinion, how to determine the socket a device belongs to. There is no first-class interface to ask a device or the firmware which socket the device lives on. The options I see are: A) Using NUMA node information, depends on CONFIG_NUMA B) Decoding the socket bits of the PCI BAR address C) Using PCI topology information A is what I tried, but I agree that depending on CONFIG_NUMA is not a good solution. B would be easy but looks not very future-proof. So option C is what is left... thanks, Jan
[PATCH v4 13/14] i2c-thunderx: Add i2c driver for ThunderX SOC
The ThunderX SOC uses the same i2c block as the Octeon SOC. The main difference is that on ThunderX the device is a PCI device so device probing is done via PCI, interrupts are MSIX and the clock is taken from device tree. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile| 2 + drivers/i2c/busses/i2c-cavium.h| 17 ++- drivers/i2c/busses/i2c-thunderx-core.c | 268 + 4 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 0299dfa..d1e7341 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -953,6 +953,16 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI && !CAVIUM_OCTEON_SOC + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + config I2C_XILINX tristate "Xilinx I2C Controller" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 282f781..a32ff14 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -93,6 +93,8 @@ obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +i2c-thunderx-objs := i2c-cavium.o i2c-thunderx-core.o +obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index 7f78bf0..31608c4 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -8,9 +8,15 @@ #include /* Register offsets */ -#define SW_TWSI0x00 -#define TWSI_INT 0x10 -#define SW_TWSI_EXT0x18 +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + #define SW_TWSI 0x1000 + #define TWSI_INT0x1010 + #define SW_TWSI_EXT 0x1018 +#else + #define SW_TWSI 0x00 + #define TWSI_INT0x10 + #define SW_TWSI_EXT 0x18 +#endif /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ @@ -92,6 +98,7 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; + struct clk *clk; int irq; int hlc_irq;/* For cn7890 only */ u32 twsi_freq; @@ -107,6 +114,10 @@ struct octeon_i2c { void (*hlc_int_dis)(struct octeon_i2c *); atomic_t int_en_cnt; atomic_t hlc_int_en_cnt; + +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + struct msix_entry i2c_msix; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c new file mode 100644 index 000..6168517 --- /dev/null +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -0,0 +1,268 @@ +/* + * Cavium ThunderX i2c driver. + * + * Copyright (C) 2015,2016 Cavium Inc. + * Authors: Fred Martin + * Jan Glauber + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +#define DRV_NAME "i2c-thunderx" + +#define PCI_CFG_REG_BAR_NUM0 +#define PCI_DEVICE_ID_THUNDER_TWSI 0xa012 + +#define TWSI_DFL_RATE 10 +#define SYS_FREQ_DEFAULT 8 + +#define TWSI_INT_ENA_W1C 0x1028 +#define TWSI_INT_ENA_W1S 0x1030 + +/* + * Enable the CORE interrupt. + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void thunder_i2c_int_enable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_CORE_INT, i2c->twsi_base + TWSI_INT_ENA_W1S); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +/* + * Disable the CORE interrupt. + */ +static void thunder_i2c_int_disable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_CORE_INT, i2c->twsi_base + TWSI_INT_ENA_W1C); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + __raw_writeq(TWSI_INT_ST_INT | TWSI_INT_TS_INT, +i2c->twsi_base + TWSI_INT_ENA_W1S); + __raw_readq(i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +static void t
[PATCH v4 07/14] i2c-octeon: Add support for cn78xx chips
From: David Daney cn78xx has a different interrupt architecture, so we have to manage the interrupts differently. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 131 1 file changed, 120 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 0e3611a..f409f8f 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -11,6 +11,7 @@ * warranty of any kind, whether express or implied. */ +#include #include #include #include @@ -112,11 +113,18 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; int irq; + int hlc_irq;/* For cn7890 only */ u32 twsi_freq; int sys_freq; void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + void (*int_en)(struct octeon_i2c *); + void (*int_dis)(struct octeon_i2c *); + void (*hlc_int_en)(struct octeon_i2c *); + void (*hlc_int_dis)(struct octeon_i2c *); + atomic_t int_en_cnt; + atomic_t hlc_int_en_cnt; }; static int reset_how; @@ -214,6 +222,58 @@ static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) } /** + * octeon_i2c_int_enable78 - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(>int_en_cnt); + enable_irq(i2c->irq); +} + +static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +{ + int count; + + /* +* The interrupt can be disabled in two places, but we only +* want to make the disable_irq_nosync() call once, so keep +* track with the atomic variable. +*/ + count = atomic_dec_if_positive(cnt); + if (count >= 0) + disable_irq_nosync(irq); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(>int_en_cnt, i2c->irq); +} + +/** + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(>hlc_int_en_cnt); + enable_irq(i2c->hlc_irq); +} + +/* disable the ST interrupt */ +static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(>hlc_int_en_cnt, i2c->hlc_irq); +} + +/** * octeon_i2c_unblock - unblock the bus * @i2c: The struct octeon_i2c * @@ -250,7 +310,18 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { struct octeon_i2c *i2c = dev_id; - octeon_i2c_int_disable(i2c); + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +/* HLC interrupt service routine */ +static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->hlc_int_dis(i2c); wake_up(>queue); return IRQ_HANDLED; @@ -276,10 +347,10 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; - octeon_i2c_int_enable(i2c); + i2c->int_en(i2c); time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->int_dis(i2c); if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -413,11 +484,11 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int time_left; - octeon_i2c_hlc_int_enable(i2c); + i2c->hlc_int_en(i2c); time_left = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->hlc_int_dis(i2c); if (!time_left) { octeon_i2c_hlc_int_clear(i2c); dev_dbg(i2c->dev, "%s: timeout\n", __func__); @@ -969,14 +1040,26 @@ static struct i2c_adapter octeon_i2c_ops = { static int octeon_i2c_probe(struct platform_device *pdev) { struct device_node *node = pdev->dev.of_node; + int irq, result = 0, hlc_irq = 0; struct resource *res_mem; struct octeon_i2c *i2c; - int irq, result = 0; - - /* All adaptors have an irq. */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + bool cn78xx_style; + + cn78xx_style = of_device_is_c
[PATCH v4 06/14] dt-bindings: i2c: Add Octeon cn78xx TWSI
Add compatible string for Cavium Octeon cn78XX SOCs TWSI. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Jan Glauber Acked-by: David Daney --- Documentation/devicetree/bindings/i2c/i2c-octeon.txt | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt index dced82e..872d485 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt @@ -4,6 +4,12 @@ Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. + or + + compatible: "cavium,octeon-7890-twsi" + + Compatibility with cn78XX SOCs. + - reg: The base address of the TWSI/I2C bus controller register bank. - #address-cells: Must be <1>. -- 1.9.1
[PATCH v4 00/14] i2c-octeon and i2c-thunderx drivers
This series for the Octeon i2c driver is an attempt to upstream some bug fixes and features that accumulated for some time. On top of the Octeon changes a i2c driver for the ThunderX SOC is added which uses the same functional block as the Octeon driver. Patches #1-2 are cleanups. Patches #3-10 are forward-ports of Octeon features and bugfixes. Patch #11-12 prepare for the driver split Patches #13-14 add the ThunderX driver. Patches are on top of 4.5 + i2c-octeon-kerneldoc-patch and were tested on OCTEON, OCTEON-78 and ThunderX. Changes to v3: - added more functionality flags for SMBUS - removed both module parameters - make xfer return also other errors than EGAIN - return EPROTO on invalid SMBUS block length - use devm_ioremap_resource - added rename-only patch - removed kerneldoc patch from series - improved defines Changes to v2: - Split clenaup patch into several patches - Strictly moved functional changes to later patches - Fixed do-while checkpatch errors - Moved defines to the patches that use them - Use BIT_ULL macro - Split ThunderX patch into 2 patches Changes to v1: - Fixed compile error on x86_64 - Disabled thunderx driver on MIPS - Re-ordered some thunderx probe functions for readability - Fix missing of_irq.h and i2c-smbus.h includes - Use IS_ENABLED for CONFIG options Jan - David Daney (4): i2c-octeon: Support I2C_M_RECV_LEN i2c-octeon: Enable high-level controller and improve on bus contention i2c-octeon: Add support for cn78xx chips i2c-octeon: Add workaround for broken irqs on CN3860 Jan Glauber (8): i2c-octeon: Cleanup i2c-octeon driver i2c-octeon: Cleanup resource allocation code i2c-octeon: Change adapter timeout and retry default values dt-bindings: i2c: Add Octeon cn78xx TWSI i2c-octeon: Rename driver to prepare for split i2c-octeon: Split the driver into two parts i2c-thunderx: Add i2c driver for ThunderX SOC i2c-thunderx: Add smbus alert support Peter Swain (2): i2c-octeon: Flush TWSI writes with readback i2c-octeon: Faster operation when IFLG signals late .../devicetree/bindings/i2c/i2c-octeon.txt | 6 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile| 3 + drivers/i2c/busses/i2c-cavium.c| 822 + drivers/i2c/busses/i2c-cavium.h| 212 ++ drivers/i2c/busses/i2c-octeon-core.c | 283 +++ drivers/i2c/busses/i2c-octeon.c| 622 drivers/i2c/busses/i2c-thunderx-core.c | 303 8 files changed, 1639 insertions(+), 622 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c -- 1.9.1
[PATCH v4 12/14] i2c-octeon: Split the driver into two parts
Move common functionality into a separate file in preparation of the re-use from the ThunderX i2c driver. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile | 3 +- drivers/i2c/busses/i2c-cavium.c | 822 + drivers/i2c/busses/i2c-cavium.h | 195 +++ drivers/i2c/busses/i2c-octeon-core.c | 983 +-- 4 files changed, 1020 insertions(+), 983 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 3405286..282f781 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,8 @@ obj-$(CONFIG_I2C_UNIPHIER)+= i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o +i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.c b/drivers/i2c/busses/i2c-cavium.c new file mode 100644 index 000..9ad1f23 --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.c @@ -0,0 +1,822 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +static int reset_how; + +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + +#define I2C_OCTEON_IFLG_WAIT 80/* microseconds */ + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + long time_left; + int first = 1; + + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->int_en(i2c); + time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), + i2c->adap.timeout); + i2c->int_dis(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + if (!time_left) { + dev_dbg(i2c->dev, "%s: timeout\n", __func__); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Cleanup low-level state & enable high-level. + * Returns -EAGAIN if low-level state could not be cleaned. + */ +static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) +{ + int try = 0, ret = 0; + u64 val; + + if (i2c->hlc_enabled) + return 0; + i2c->
[PATCH v4 02/14] i2c-octeon: Cleanup resource allocation code
Remove resource values from struct i2c_octeon and use devm_ioremap_resource helper. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 18 +++--- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 9787379..9240037 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -75,9 +75,7 @@ struct octeon_i2c { int irq; u32 twsi_freq; int sys_freq; - resource_size_t twsi_phys; void __iomem *twsi_base; - resource_size_t regsize; struct device *dev; }; @@ -502,14 +500,11 @@ static int octeon_i2c_probe(struct platform_device *pdev) i2c->dev = >dev; res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - if (res_mem == NULL) { - dev_err(i2c->dev, "found no memory resource\n"); - result = -ENXIO; + i2c->twsi_base = devm_ioremap_resource(>dev, res_mem); + if (IS_ERR(i2c->twsi_base)) { + result = PTR_ERR(i2c->twsi_base); goto out; } - i2c->twsi_phys = res_mem->start; - i2c->regsize = resource_size(res_mem); /* * "clock-rate" is a legacy binding, the official binding is @@ -526,13 +521,6 @@ static int octeon_i2c_probe(struct platform_device *pdev) i2c->sys_freq = octeon_get_io_clock_rate(); - if (!devm_request_mem_region(>dev, i2c->twsi_phys, i2c->regsize, -res_mem->name)) { - dev_err(i2c->dev, "request_mem_region failed\n"); - goto out; - } - i2c->twsi_base = devm_ioremap(>dev, i2c->twsi_phys, i2c->regsize); - init_waitqueue_head(>queue); i2c->irq = irq; -- 1.9.1
[PATCH v4 08/14] i2c-octeon: Flush TWSI writes with readback
From: Peter Swain Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 19 --- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index f409f8f..aa676ce 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -129,6 +129,12 @@ struct octeon_i2c { static int reset_how; +static void writeqflush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -189,8 +195,7 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) */ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) { - __raw_writeq(data, i2c->twsi_base + TWSI_INT); - __raw_readq(i2c->twsi_base + TWSI_INT); + writeqflush(data, i2c->twsi_base + TWSI_INT); } /** @@ -576,10 +581,10 @@ static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) ext |= (u64) msgs[0].buf[j] << (8 * i); - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); } - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -625,7 +630,7 @@ static int octeon_i2c_ia_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -685,10 +690,10 @@ static int octeon_i2c_ia_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) set_ext = true; } if (set_ext) - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) -- 1.9.1
[PATCH v4 01/14] i2c-octeon: Cleanup i2c-octeon driver
Cleanup only without functional change. - removed DRV_VERSION - defines: use defines instead of plain values, use BIT_ULL macro, add comments - rename waitqueue return value to time_left - sort local variables by length - fix indentation and whitespace errors - make function return void if the result is not used (octeon_i2c_stop, octeon_i2c_set_clock) - remove debug code from octeon_i2c_stop - renamed some functions for readability - update copyright Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 190 ++-- 1 file changed, 84 insertions(+), 106 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 4a08418..9787379 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -2,7 +2,7 @@ * (C) Copyright 2009-2010 * Nokia Siemens Networks, michael.lawnick@nsn.com * - * Portions Copyright (C) 2010, 2011 Cavium Networks, Inc. + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. * * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. * @@ -26,39 +26,48 @@ #define DRV_NAME "i2c-octeon" -/* The previous out-of-tree version was implicitly version 1.0. */ -#define DRV_VERSION"2.0" - -/* register offsets */ -#define SW_TWSI 0x00 -#define TWSI_INT 0x10 +/* Register offsets */ +#define SW_TWSI0x00 +#define TWSI_INT 0x10 /* Controller command patterns */ -#define SW_TWSI_V 0x8000ull -#define SW_TWSI_EOP_TWSI_DATA 0x0C01ull -#define SW_TWSI_EOP_TWSI_CTL0x0C02ull -#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C03ull -#define SW_TWSI_EOP_TWSI_STAT 0x0C03ull -#define SW_TWSI_EOP_TWSI_RST0x0C07ull -#define SW_TWSI_OP_TWSI_CLK 0x0800ull -#define SW_TWSI_R 0x0100ull +#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ + +/* Controller opcode word (bits 60:57) */ +#define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_TWSI_CLK(4ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ + +/* Controller extended opcode word (bits 34:32) */ +#define SW_TWSI_EOP_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CLKCTL(SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) /* Controller command and status bits */ -#define TWSI_CTL_CE 0x80 -#define TWSI_CTL_ENAB 0x40 -#define TWSI_CTL_STA 0x20 -#define TWSI_CTL_STP 0x10 -#define TWSI_CTL_IFLG 0x08 -#define TWSI_CTL_AAK 0x04 +#define TWSI_CTL_CE0x80 +#define TWSI_CTL_ENAB 0x40/* Bus enable */ +#define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ +#define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ +#define TWSI_CTL_IFLG 0x08/* HW event, SW writes 0 to ACK */ +#define TWSI_CTL_AAK 0x04/* Assert ACK */ /* Some status values */ -#define STAT_START 0x08 -#define STAT_RSTART 0x10 -#define STAT_TXADDR_ACK 0x18 -#define STAT_TXDATA_ACK 0x28 -#define STAT_RXADDR_ACK 0x40 -#define STAT_RXDATA_ACK 0x50 -#define STAT_IDLE 0xF8 +#define STAT_START 0x08 +#define STAT_RSTART0x10 +#define STAT_TXADDR_ACK0x18 +#define STAT_TXDATA_ACK0x28 +#define STAT_RXADDR_ACK0x40 +#define STAT_RXDATA_ACK0x50 +#define STAT_IDLE 0xF8 + +/* TWSI_INT values */ +#define TWSI_INT_CORE_EN BIT_ULL(6) +#define TWSI_INT_SDA_OVR BIT_ULL(8) +#define TWSI_INT_SCL_OVR BIT_ULL(9) struct octeon_i2c { wait_queue_head_t queue; @@ -80,9 +89,7 @@ struct octeon_i2c { * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, - u64 eop_reg, - u8 data) +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) { u64 tmp; @@ -93,7 +100,7 @@ static void octeon_i2c_write_sw(struct octeon_i2c *i2c, } /** - * octeon_i2c_read_sw - write an I2C core register + * octeon_i2c_read_sw - read lower bits of an I2C core register * @i2c: The struct octeon_i2c * @eop_reg: Register selector * @@ -133,12 +140,13 @@ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) */ static void octeon_i2c_int_enable(struct octeon_i2c *i2c) { - oct
[PATCH v4 10/14] i2c-octeon: Add workaround for broken irqs on CN3860
From: David Daney CN3860 does not interrupt the CPU when the i2c status changes. If we get a timeout, and see the status has in fact changed, we know we have this problem, and drop back to polling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 55 +++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index d3a3e2e..444e8ed 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -119,6 +119,8 @@ struct octeon_i2c { void __iomem *twsi_base; struct device *dev; bool hlc_enabled; + bool broken_irq_mode; + bool broken_irq_check; void (*int_en)(struct octeon_i2c *); void (*int_dis)(struct octeon_i2c *); void (*hlc_int_en)(struct octeon_i2c *); @@ -375,10 +377,33 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) long time_left; int first = 1; + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + i2c->int_en(i2c); time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -512,17 +537,40 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int time_left; + if (i2c->broken_irq_mode) { + /* +* Some cn38xx boards did not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_ready(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; + } + i2c->hlc_int_en(i2c); time_left = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); i2c->hlc_int_dis(i2c); - if (!time_left) { + if (!time_left) octeon_i2c_hlc_int_clear(i2c); + + if (time_left <= 0 && i2c->broken_irq_check && + octeon_i2c_hlc_test_ready(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; } - if (time_left < 0) { dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); return time_left; @@ -1154,6 +1202,9 @@ static int octeon_i2c_probe(struct platform_device *pdev) goto out; } + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) + i2c->broken_irq_check = true; + result = octeon_i2c_init_lowlevel(i2c); if (result) { dev_err(i2c->dev, "init low level failed\n"); -- 1.9.1
[PATCH v4 09/14] i2c-octeon: Faster operation when IFLG signals late
From: Peter Swain Some versions can deliver low-level twsi irq before twsi_ctl.iflg is set, leading to timeout-driven i/o. When an irq signals event, but woken task does not see the expected twsi_ctl.iflg, re-check about 80uS later. EEPROM reads on 100kHz i2c now measure ~5.2kB/s, about 1/2 what's achievable, and much better than the worst-case 100 bytes/sec before. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 25 - 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index aa676ce..d3a3e2e 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -342,6 +342,28 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } +#define I2C_OCTEON_IFLG_WAIT 80/* microseconds */ + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(I2C_OCTEON_IFLG_WAIT, 2 * I2C_OCTEON_IFLG_WAIT); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -351,9 +373,10 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; + int first = 1; i2c->int_en(i2c); - time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); if (!time_left) { -- 1.9.1
[PATCH v4 14/14] i2c-thunderx: Add smbus alert support
Add smbus alert interrupt support. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-cavium.h| 6 ++ drivers/i2c/busses/i2c-thunderx-core.c | 35 ++ 2 files changed, 41 insertions(+) diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index 31608c4..f934624 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -118,6 +119,11 @@ struct octeon_i2c { #if IS_ENABLED(CONFIG_I2C_THUNDERX) struct msix_entry i2c_msix; #endif + +#if IS_ENABLED(CONFIG_I2C_SMBUS) + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c index 6168517..93bb64b 100644 --- a/drivers/i2c/busses/i2c-thunderx-core.c +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include "i2c-cavium.h" @@ -108,6 +110,35 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) devm_clk_put(dev, clk); } +static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c, + struct device_node *node) +{ +#if IS_ENABLED(CONFIG_I2C_SMBUS) + u32 type; + + i2c->alert_data.irq = irq_of_parse_and_map(node, 0); + if (!i2c->alert_data.irq) + return -EINVAL; + + type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq)); + i2c->alert_data.alert_edge_triggered = + (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0; + + i2c->ara = i2c_setup_smbus_alert(>adap, >alert_data); + if (!i2c->ara) + return -ENODEV; +#endif + return 0; +} + +static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c) +{ +#if IS_ENABLED(CONFIG_I2C_SMBUS) + if (i2c->ara) + i2c_unregister_device(i2c->ara); +#endif +} + static void thunder_i2c_set_name(struct pci_dev *pdev, struct octeon_i2c *i2c, char *name) { @@ -207,6 +238,9 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, goto out_irq; } + ret = thunder_i2c_smbus_setup(i2c, node); + if (ret < 0) + dev_err(dev, "Failed to setup smbus alert\n"); dev_info(i2c->dev, "probed\n"); return 0; @@ -237,6 +271,7 @@ static void thunder_i2c_remove_pci(struct pci_dev *pdev) dev = i2c->dev; thunder_i2c_clock_disable(dev, i2c->clk); + thunder_i2c_smbus_remove(i2c); i2c_del_adapter(>adap); devm_free_irq(dev, i2c->i2c_msix.vector, i2c); pci_disable_msix(pdev); -- 1.9.1
[PATCH v4 05/14] i2c-octeon: Enable high-level controller and improve on bus contention
From: David Daney Use High Level Controller when possible. i2c-octeon was reacting badly to bus contention: when in direct-access mode (for transfers > 8 bytes, which cannot use the high-level controller) some !ACK or arbitration-loss states were not causing the current transfer to be aborted, and the bus released. There's one place in i2c protocol that !ACK is an acceptable response: in the final byte of a read cycle. In this case the destination is not saying that the transfer failed, just that it doesn't want more data. This enables correct behavior of ACK on final byte of non-final read msgs too. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 783 1 file changed, 629 insertions(+), 154 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index c21d102..0e3611a 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -29,13 +29,23 @@ /* Register offsets */ #define SW_TWSI0x00 #define TWSI_INT 0x10 +#define SW_TWSI_EXT0x18 /* Controller command patterns */ #define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIABIT_ULL(61) /* Extended internal address */ #define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ /* Controller opcode word (bits 60:57) */ #define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA(1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_TWSI_CLK(4ULL << SW_TWSI_OP_SHIFT) #define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ @@ -48,7 +58,7 @@ #define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) /* Controller command and status bits */ -#define TWSI_CTL_CE0x80 +#define TWSI_CTL_CE0x80/* High level controller enable */ #define TWSI_CTL_ENAB 0x40/* Bus enable */ #define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ #define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ @@ -56,18 +66,47 @@ #define TWSI_CTL_AAK 0x04/* Assert ACK */ /* Some status values */ +#define STAT_ERROR 0x00 #define STAT_START 0x08 #define STAT_RSTART0x10 #define STAT_TXADDR_ACK0x18 +#define STAT_TXADDR_NAK0x20 #define STAT_TXDATA_ACK0x28 +#define STAT_TXDATA_NAK0x30 +#define STAT_LOST_ARB_38 0x38 #define STAT_RXADDR_ACK0x40 +#define STAT_RXADDR_NAK0x48 #define STAT_RXDATA_ACK0x50 +#define STAT_RXDATA_NAK0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 #define STAT_IDLE 0xF8 /* TWSI_INT values */ +#define TWSI_INT_ST_INTBIT_ULL(0) +#define TWSI_INT_TS_INTBIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) #define TWSI_INT_CORE_EN BIT_ULL(6) #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) struct octeon_i2c { wait_queue_head_t queue; @@ -77,8 +116,11 @@ struct octeon_i2c { int sys_freq; void __iomem *twsi_base; struct device *dev; + bool hlc_enabled; }; +static int reset_how; + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -87,7 +129,7 @@ struct octeon_i2c { * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u32 data) { u64 tmp; @@ -98,7 +140,7 @@ static void oc
[PATCH v4 03/14] i2c-octeon: Change adapter timeout and retry default values
Convert the adapter timeout to 2 ms instead of a fixed number of jiffies and set retries to 10. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 9240037..e616e4c 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -414,7 +414,6 @@ static struct i2c_adapter octeon_i2c_ops = { .owner = THIS_MODULE, .name = "OCTEON adapter", .algo = _i2c_algo, - .timeout = HZ / 50, }; /* calculate and set clock divisors */ @@ -541,6 +540,8 @@ static int octeon_i2c_probe(struct platform_device *pdev) octeon_i2c_set_clock(i2c); i2c->adap = octeon_i2c_ops; + i2c->adap.timeout = msecs_to_jiffies(2); + i2c->adap.retries = 10; i2c->adap.dev.parent = >dev; i2c->adap.dev.of_node = node; i2c_set_adapdata(>adap, i2c); -- 1.9.1
[PATCH v4 11/14] i2c-octeon: Rename driver to prepare for split
This is just an intermediate commit in preparation of the driver split. The module rename in this commit will be reverted in the next patch, this is just done to make the series bisectible. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Makefile |2 +- drivers/i2c/busses/i2c-octeon-core.c | 1264 ++ drivers/i2c/busses/i2c-octeon.c | 1264 -- 3 files changed, 1265 insertions(+), 1265 deletions(-) create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819..3405286 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,7 @@ obj-$(CONFIG_I2C_UNIPHIER)+= i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o -obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +obj-$(CONFIG_I2C_OCTEON) += i2c-octeon-core.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-octeon-core.c b/drivers/i2c/busses/i2c-octeon-core.c new file mode 100644 index 000..444e8ed --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -0,0 +1,1264 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DRV_NAME "i2c-octeon" + +/* Register offsets */ +#define SW_TWSI0x00 +#define TWSI_INT 0x10 +#define SW_TWSI_EXT0x18 + +/* Controller command patterns */ +#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIABIT_ULL(61) /* Extended internal address */ +#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ + +/* Controller opcode word (bits 60:57) */ +#define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA(1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_TWSI_CLK(4ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ + +/* Controller extended opcode word (bits 34:32) */ +#define SW_TWSI_EOP_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CLKCTL(SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) + +/* Controller command and status bits */ +#define TWSI_CTL_CE0x80/* High level controller enable */ +#define TWSI_CTL_ENAB 0x40/* Bus enable */ +#define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ +#define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ +#define TWSI_CTL_IFLG 0x08/* HW event, SW writes 0 to ACK */ +#define TWSI_CTL_AAK 0x04/* Assert ACK */ + +/* Some status values */ +#define STAT_ERROR 0x00 +#define STAT_START 0x08 +#define STAT_RSTART0x10 +#define STAT_TXADDR_ACK0x18 +#define STAT_TXADDR_NAK0x20 +#define STAT_TXDATA_ACK0x28 +#define STAT_TXDATA_NAK0x30 +#define STAT_LOST_ARB_38 0x38 +#define STAT_RXADDR_ACK0x40 +#define STAT_RXADDR_NAK0x48 +#define STAT_RXDATA_ACK0x50 +#define STAT_RXDATA_NAK0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT
[PATCH v4 04/14] i2c-octeon: Support I2C_M_RECV_LEN
From: David Daney If I2C_M_RECV_LEN is set consider the length byte. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 22 +- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index e616e4c..c21d102 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -315,16 +315,17 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, * @i2c: The struct octeon_i2c * @target: Target address * @data: Pointer to the location to store the data - * @length: Length of the data + * @rlength: Length of the data + * @recv_len: flag for length byte * * The address is sent over the bus, then the data is read. * * Returns 0 on success, otherwise a negative errno. */ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, - u8 *data, int length) + u8 *data, u16 *rlength, bool recv_len) { - int i, result; + int i, result, length = *rlength; u8 tmp; if (length < 1) @@ -363,7 +364,17 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, return result; data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + if (recv_len && i == 0) { + if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(i2c->dev, + "%s: read len > I2C_SMBUS_BLOCK_MAX %d\n", + __func__, data[i]); + return -EPROTO; + } + length += data[i]; + } } + *rlength = length; return 0; } @@ -390,7 +401,7 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, pmsg->len, pmsg->addr, i + 1, num); if (pmsg->flags & I2C_M_RD) ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, - pmsg->len); + >len, pmsg->flags & I2C_M_RECV_LEN); else ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, pmsg->len); @@ -402,7 +413,8 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, static u32 octeon_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; } static const struct i2c_algorithm octeon_i2c_algo = { -- 1.9.1
[Resend PATCH 00/10] i2c-octeon and i2c-thunderx drivers
I'm resending both patch series as one since the thunderx driver depends on the octeon driver patches. Also (subtly) dropping the RFC in the hope of getting the patches reviewed. This series for the Octeon i2c driver is an attempt to upstream some bug fixes and features that accumulated for some time. I tried to split the cleanup from the other patches and updated the patch descriptions according to my humble i2c knowledge. Patches are on top of 4.5-rc6 and were tested on OCTEON, OCTEON-78 and ThunderX. Jan -- David Daney (4): i2c-octeon: Support I2C_M_RECV_LEN i2c-octeon: Enable high-level controller and improve on bus contention i2c-octeon: Add support for cn78XX chips i2c-octeon: Add workaround for chips with broken irqs Jan Glauber (4): i2c-octeon: Cleanup i2c-octeon driver dt-bindings: i2c: add Octeon cn78xx TWSI i2c: split i2c-octeon driver and add ThunderX support i2c: thunderx: add smbus support Peter Swain (2): i2c-octeon: Flush TWSI writes with readback i2c-octeon: Faster operation when IFLG signals late .../devicetree/bindings/i2c/i2c-octeon.txt | 6 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile| 3 + drivers/i2c/busses/i2c-cavium.c| 828 + drivers/i2c/busses/i2c-cavium.h| 191 + drivers/i2c/busses/i2c-octeon-core.c | 302 drivers/i2c/busses/i2c-octeon.c| 633 drivers/i2c/busses/i2c-thunderx-core.c | 294 8 files changed, 1634 insertions(+), 633 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c -- 1.9.1
[Resend PATCH 09/10] i2c: split i2c-octeon driver and add ThunderX support
The ThunderX SOC uses the same i2c block as the Octeon SOC. The main difference is that on ThunderX the device is a PCI device so device probing is done via PCI. Split the current Octeon driver into an Octeon and a common part and add the ThunderX support. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile|3 + drivers/i2c/busses/i2c-cavium.c| 828 + drivers/i2c/busses/i2c-cavium.h| 187 + drivers/i2c/busses/i2c-octeon-core.c | 302 drivers/i2c/busses/i2c-octeon.c| 1283 drivers/i2c/busses/i2c-thunderx-core.c | 263 +++ 7 files changed, 1593 insertions(+), 1283 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 7b0aa82..97614b3 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -952,6 +952,16 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + config I2C_XILINX tristate "Xilinx I2C Controller" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819..a32ff14 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,10 @@ obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o +i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +i2c-thunderx-objs := i2c-cavium.o i2c-thunderx-core.o +obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.c b/drivers/i2c/busses/i2c-cavium.c new file mode 100644 index 000..93d8c0f --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.c @@ -0,0 +1,828 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +static int reset_how; + +/* + * On some hardware IFLG is not visible in TWSI_CTL until after low-level IRQ, + * so re-sample CTL a short time later to avoid stalls. + */ +static int irq_early_us = 80; +module_param(irq_early_us, int, 0644); +MODULE_PARM_DESC(irq_early_us, "Re-poll for IFLG after IRQ (us)"); + +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(irq_early_us, 2 * irq_early_us); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + int first = 1; + long result; + + if (i2c->broken_irq_mode) { +
[Resend PATCH 01/10] i2c-octeon: Cleanup i2c-octeon driver
Cleanup only without functional change. Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 442 +--- 1 file changed, 230 insertions(+), 212 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 32914ab..1f14094 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -2,7 +2,7 @@ * (C) Copyright 2009-2010 * Nokia Siemens Networks, michael.lawnick@nsn.com * - * Portions Copyright (C) 2010, 2011 Cavium Networks, Inc. + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. * * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. * @@ -24,99 +24,154 @@ #include -#define DRV_NAME "i2c-octeon" +#define DRV_NAME "i2c-octeon" -/* The previous out-of-tree version was implicitly version 1.0. */ -#define DRV_VERSION"2.0" +/* Register offsets */ +#define SW_TWSI0x00 +#define TWSI_INT 0x10 +#define SW_TWSI_EXT0x18 -/* register offsets */ -#define SW_TWSI 0x00 -#define TWSI_INT 0x10 +#define INT_ENA_ST 0x1 +#define INT_ENA_TS 0x2 +#define INT_ENA_CORE 0x4 /* Controller command patterns */ -#define SW_TWSI_V 0x8000ull -#define SW_TWSI_EOP_TWSI_DATA 0x0C01ull -#define SW_TWSI_EOP_TWSI_CTL0x0C02ull -#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C03ull -#define SW_TWSI_EOP_TWSI_STAT 0x0C03ull -#define SW_TWSI_EOP_TWSI_RST0x0C07ull -#define SW_TWSI_OP_TWSI_CLK 0x0800ull -#define SW_TWSI_R 0x0100ull +#define SW_TWSI_V (1ULL << 63) +#define SW_TWSI_EIA(1ULL << 61) +#define SW_TWSI_R (1ULL << 56) +#define SW_TWSI_SOVR (1ULL << 55) +#define SW_TWSI_OP_7 (0ULL << 57) +#define SW_TWSI_OP_7_IA(1ULL << 57) +#define SW_TWSI_OP_10 (2ULL << 57) +#define SW_TWSI_OP_10_IA (3ULL << 57) +#define SW_TWSI_OP_TWSI_CLK(1ULL << 59) +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_A_SHIFT40 +#define SW_TWSI_IA_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA 0x0C01ULL +#define SW_TWSI_EOP_TWSI_CTL 0x0C02ULL +#define SW_TWSI_EOP_TWSI_CLKCTL0x0C03ULL +#define SW_TWSI_EOP_TWSI_STAT 0x0C03ULL +#define SW_TWSI_EOP_TWSI_RST 0x0C07ULL /* Controller command and status bits */ -#define TWSI_CTL_CE 0x80 -#define TWSI_CTL_ENAB 0x40 -#define TWSI_CTL_STA 0x20 -#define TWSI_CTL_STP 0x10 -#define TWSI_CTL_IFLG 0x08 -#define TWSI_CTL_AAK 0x04 +#define TWSI_CTL_CE0x80/* High level controller enable */ +#define TWSI_CTL_ENAB 0x40/* Bus enable */ +#define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ +#define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ +#define TWSI_CTL_IFLG 0x08/* HW event, SW writes 0 to ACK */ +#define TWSI_CTL_AAK 0x04/* Assert ACK */ /* Some status values */ -#define STAT_START 0x08 -#define STAT_RSTART 0x10 -#define STAT_TXADDR_ACK 0x18 -#define STAT_TXDATA_ACK 0x28 -#define STAT_RXADDR_ACK 0x40 -#define STAT_RXDATA_ACK 0x50 -#define STAT_IDLE 0xF8 +#define STAT_ERROR 0x00 +#define STAT_START 0x08 +#define STAT_RSTART0x10 +#define STAT_TXADDR_ACK0x18 +#define STAT_TXADDR_NAK0x20 +#define STAT_TXDATA_ACK0x28 +#define STAT_TXDATA_NAK0x30 +#define STAT_LOST_ARB_38 0x38 +#define STAT_RXADDR_ACK0x40 +#define STAT_RXADDR_NAK0x48 +#define STAT_RXDATA_ACK0x50 +#define STAT_RXDATA_NAK0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 +#define STAT_IDLE 0xF8 + +/* TWSI_INT values */ +#define ST_INT 0x01 +#define TS_INT 0x02 +#define CORE_INT 0x04 +#define ST_EN 0x10 +#define TS_EN 0x20 +#define CORE_EN0x40 +#define SDA_OVR0x100 +#define SCL_OVR
[Resend PATCH 02/10] i2c-octeon: Support I2C_M_RECV_LEN
From: David Daney If I2C_M_RECV_LEN is set consider the length byte. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 15 +-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 1f14094..fa4d439 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -392,13 +392,14 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, * @target: Target address * @data: Pointer to the location to store the data * @rlength: Length of the data + * @recv_len: flag for length byte * * The address is sent over the bus, then the data is read. * * Returns 0 on success, otherwise a negative errno. */ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, - u16 *rlength) + u16 *rlength, bool recv_len) { int length = *rlength; int i, result; @@ -438,6 +439,15 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, return result; data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + if (recv_len && i == 0) { + if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(i2c->dev, + "%s: read len > I2C_SMBUS_BLOCK_MAX %d\n", + __func__, data[i]); + return -EIO; + } + length += data[i]; + } } *rlength = length; return 0; @@ -466,7 +476,8 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, pmsg->len, pmsg->addr, i + 1, num); if (pmsg->flags & I2C_M_RD) ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, - >len); + >len, + pmsg->flags & I2C_M_RECV_LEN); else ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, pmsg->len); -- 1.9.1
[Resend PATCH 03/10] i2c-octeon: Enable high-level controller and improve on bus contention
From: David Daney Use High Level Controller when possible. i2c-octeon was reacting badly to bus contention: when in direct-access mode (for transfers > 8 bytes, which cannot use the high-level controller) some !ACK or arbitration-loss states were not causing the current transfer to be aborted, and the bus released. There's one place in i2c protocol that !ACK is an acceptable response: in the final byte of a read cycle. In this case the destination is not saying that the transfer failed, just that it doesn't want more data. This enables correct behavior of ACK on final byte of non-final read msgs too. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 583 ++-- 1 file changed, 504 insertions(+), 79 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index fa4d439..d48456a 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -113,8 +113,15 @@ struct octeon_i2c { int sys_freq; void __iomem*twsi_base; struct device *dev; + boolhlc_enabled; }; +static int reset_how; + +static int timeout = 2; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Low-level device timeout (ms)"); + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -123,7 +130,7 @@ struct octeon_i2c { * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u32 data) { u64 tmp; @@ -165,7 +172,7 @@ static u64 octeon_i2c_read_sw64(struct octeon_i2c *i2c, u64 eop_reg) */ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) { - return octeon_i2c_read_sw64(i2c, eop_reg); + return (u8) octeon_i2c_read_sw64(i2c, eop_reg); } /** @@ -198,6 +205,15 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, TS_INT | ST_INT); } +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + /** * bitbang_unblock - unblock the bus * @i2c: The struct octeon_i2c @@ -207,13 +223,18 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) */ static void bitbang_unblock(struct octeon_i2c *i2c) { - int i; + int state, i; + octeon_i2c_disable_hlc(i2c); dev_dbg(i2c->dev, "%s\n", __func__); + /* cycle 8+1 clocks with SDA high */ for (i = 0; i < 9; i++) { octeon_i2c_write_int(i2c, 0); udelay(5); + state = __raw_readq(i2c->twsi_base + TWSI_INT); + if (state & (SDA | SCL)) + break; octeon_i2c_write_int(i2c, SCL_OVR); udelay(5); } @@ -236,10 +257,14 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static u64 octeon_i2c_read_ctl(struct octeon_i2c *i2c) +{ + return octeon_i2c_read_sw64(i2c, SW_TWSI_EOP_TWSI_CTL); +} + static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) - & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } /** @@ -263,76 +288,208 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } -/* send STOP to the bus */ -static void octeon_i2c_stop(struct octeon_i2c *i2c) +/* + * Cleanup low-level state & enable high-level. + * + * Returns -EAGAIN if low-level state could not be cleaned + */ +static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) { - u8 data; + int try = 0, ret = 0; + u64 val; + + if (i2c->hlc_enabled) + return 0; + else + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_read_ctl(i2c) & (TWSI_CTL_STA | TWSI_CTL_STP); + if (!val) + break; + + /* clear _IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + ret = -EAGAIN; + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STP); +
[Resend PATCH 10/10] i2c: thunderx: add smbus support
Add smbus alert interrupt support. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-cavium.h| 4 drivers/i2c/busses/i2c-thunderx-core.c | 31 +++ 2 files changed, 35 insertions(+) diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index db80c0f..8357997 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -119,6 +119,10 @@ struct octeon_i2c { #ifdef CONFIG_ARCH_THUNDER struct msix_entry i2c_msix; #endif +#ifdef CONFIG_I2C_SMBUS + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c index 0a95891..396c8bc 100644 --- a/drivers/i2c/busses/i2c-thunderx-core.c +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,34 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) devm_clk_put(dev, clk); } +static void thunder_i2c_smbus_setup(struct octeon_i2c *i2c, + struct device_node *node) +{ +#ifdef CONFIG_I2C_SMBUS + u32 type; + + i2c->alert_data.irq = irq_of_parse_and_map(node, 0); + if (!i2c->alert_data.irq) + return; + + type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq)); + i2c->alert_data.alert_edge_triggered = + (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0; + + i2c->ara = i2c_setup_smbus_alert(>adap, >alert_data); + if (!i2c->ara) + dev_err(dev, "Failed to setup smbalert\n"); +#endif +} + +static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c) +{ +#ifdef CONFIG_I2C_SMBUS + if (i2c->ara) + i2c_unregister_device(i2c->ara); +#endif +} + static void thunder_i2c_set_name(struct pci_dev *pdev, struct octeon_i2c *i2c, char *name) { @@ -202,6 +231,7 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, goto out_irq; } + thunder_i2c_smbus_setup(i2c, node); dev_info(i2c->dev, "probed\n"); return 0; @@ -232,6 +262,7 @@ static void thunder_i2c_remove_pci(struct pci_dev *pdev) dev = i2c->dev; thunder_i2c_clock_disable(dev, i2c->clk); + thunder_i2c_smbus_remove(i2c); i2c_del_adapter(>adap); devm_free_irq(dev, i2c->i2c_msix.vector, i2c); pci_disable_msix(pdev); -- 1.9.1
[Resend PATCH 06/10] i2c-octeon: Flush TWSI writes with readback
From: Peter Swain Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 19 --- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 0b28d8c..bb15a9c 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -130,6 +130,12 @@ static int timeout = 2; module_param(timeout, int, 0444); MODULE_PARM_DESC(timeout, "Low-level device timeout (ms)"); +static void writeqflush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -190,8 +196,7 @@ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) */ static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) { - __raw_writeq(data, i2c->twsi_base + TWSI_INT); - __raw_readq(i2c->twsi_base + TWSI_INT); + writeqflush(data, i2c->twsi_base + TWSI_INT); } /** @@ -797,10 +802,10 @@ static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) ext |= (u64) msgs[0].buf[j] << (8 * i); - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); } - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -846,7 +851,7 @@ static int octeon_i2c_ia_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) cmd |= (u64) msgs[0].buf[0] << SW_TWSI_IA_SHIFT; octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) @@ -906,10 +911,10 @@ static int octeon_i2c_ia_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) set_ext = true; } if (set_ext) - __raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT); + writeqflush(ext, i2c->twsi_base + SW_TWSI_EXT); octeon_i2c_hlc_int_clear(i2c); - __raw_writeq(cmd, i2c->twsi_base + SW_TWSI); + writeqflush(cmd, i2c->twsi_base + SW_TWSI); ret = octeon_i2c_hlc_wait(i2c); if (ret) -- 1.9.1
[Resend PATCH 04/10] dt-bindings: i2c: add Octeon cn78xx TWSI
Add compatible string for Cavium Octeon cn78XX SOCs TWSI. Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Jan Glauber Acked-by: David Daney --- Documentation/devicetree/bindings/i2c/i2c-octeon.txt | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt index dced82e..872d485 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-octeon.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-octeon.txt @@ -4,6 +4,12 @@ Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs. + or + + compatible: "cavium,octeon-7890-twsi" + + Compatibility with cn78XX SOCs. + - reg: The base address of the TWSI/I2C bus controller register bank. - #address-cells: Must be <1>. -- 1.9.1
[Resend PATCH 08/10] i2c-octeon: Add workaround for chips with broken irqs
From: David Daney CN3860 does not interrupt the CPU when the i2c status changes. If we get a timeout, and see the status has in fact changed, we know we have this problem, and drop back to polling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 46 + 1 file changed, 46 insertions(+) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index e3552e5..3c2f848 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -115,6 +115,7 @@ struct octeon_i2c { int sys_freq; void __iomem*twsi_base; struct device *dev; + int broken_irq_mode; boolhlc_enabled; void(*int_en) (struct octeon_i2c *); void(*int_dis) (struct octeon_i2c *); @@ -382,10 +383,33 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) int first = 1; long result; + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + i2c->int_en(i2c); result = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); + + if (result <= 0 && OCTEON_IS_MODEL(OCTEON_CN38XX) && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = 1; + return 0; + } if (!result) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -740,6 +764,21 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int result; + if (i2c->broken_irq_mode) { + /* +* Some cn38xx boards did not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_ready(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; + } + i2c->hlc_int_en(i2c); result = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); @@ -747,6 +786,13 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) if (!result) octeon_i2c_hlc_int_clear(i2c); + if (result <= 0 && OCTEON_IS_MODEL(OCTEON_CN38XX) && + octeon_i2c_hlc_test_ready(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = 1; + return 0; + } + if (result < 0) { dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); return result; -- 1.9.1
[Resend PATCH 05/10] i2c-octeon: Add support for cn78XX chips
From: David Daney cn78XX has a different interrupt architecture, so we have to manage the interrupts differently. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 156 ++-- 1 file changed, 136 insertions(+), 20 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index d48456a..0b28d8c 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -109,11 +110,18 @@ struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; int irq; + int hlc_irq;/* For cn7890 only */ u32 twsi_freq; int sys_freq; void __iomem*twsi_base; struct device *dev; boolhlc_enabled; + void(*int_en) (struct octeon_i2c *); + void(*int_dis) (struct octeon_i2c *); + void(*hlc_int_en) (struct octeon_i2c *); + void(*hlc_int_dis) (struct octeon_i2c *); + atomic_tint_en_cnt; + atomic_thlc_int_en_cnt; }; static int reset_how; @@ -215,6 +223,58 @@ static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) } /** + * octeon_i2c_int_enable78 - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(>int_en_cnt); + enable_irq(i2c->irq); +} + +static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +{ + int count; + + /* +* The interrupt can be disabled in two places, but we only +* want to make the disable_irq_nosync() call once, so keep +* track with the atomic variable. +*/ + count = atomic_dec_if_positive(cnt); + if (count >= 0) + disable_irq_nosync(irq); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(>int_en_cnt, i2c->irq); +} + +/** + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(>hlc_int_en_cnt); + enable_irq(i2c->hlc_irq); +} + +/* disable the ST interrupt */ +static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(>hlc_int_en_cnt, i2c->hlc_irq); +} + +/** * bitbang_unblock - unblock the bus * @i2c: The struct octeon_i2c * @@ -251,7 +311,18 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { struct octeon_i2c *i2c = dev_id; - octeon_i2c_int_disable(i2c); + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +/* interrupt service routine */ +static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->hlc_int_dis(i2c); wake_up(>queue); return IRQ_HANDLED; @@ -277,10 +348,10 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) { long result; - octeon_i2c_int_enable(i2c); + i2c->int_en(i2c); result = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + i2c->int_dis(i2c); if (!result) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -300,8 +371,8 @@ static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) if (i2c->hlc_enabled) return 0; - else - i2c->hlc_enabled = true; + + i2c->hlc_enabled = true; while (1) { val = octeon_i2c_read_ctl(i2c) & (TWSI_CTL_STA | TWSI_CTL_STP); @@ -635,11 +706,10 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int result; - octeon_i2c_hlc_int_enable(i2c); + i2c->hlc_int_en(i2c); result = wait_event_interruptible_timeout(i2c->queue, - octeon_i2c_hlc_test_ready(i2c), - i2c->adap.timeout); - octeon_i2c_int_disable(i2c); + octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); + i2c->hlc_int_dis(i2c); if (!result) oc
[Resend PATCH 07/10] i2c-octeon: Faster operation when IFLG signals late
From: Peter Swain Some versions can deliver low-level twsi irq before twsi_ctl.iflg is set, leading to timeout-driven i/o. When an irq signals event, but woken task does not see the expected twsi_ctl.iflg, re-check about 80uS later. EEPROM reads on 100kHz i2c now measure ~5.2kB/s, about 1/2 what's achievable, and much better than the worst-case 100 bytes/sec before. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 31 ++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index bb15a9c..e3552e5 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -130,6 +130,14 @@ static int timeout = 2; module_param(timeout, int, 0444); MODULE_PARM_DESC(timeout, "Low-level device timeout (ms)"); +/* + * On some hardware IFLG is not visible in TWSI_CTL until after low-level IRQ, + * so re-sample CTL a short time later to avoid stalls. + */ +static int irq_early_us = 80; +module_param(irq_early_us, int, 0644); +MODULE_PARM_DESC(irq_early_us, "Re-poll for IFLG after IRQ (us)"); + static void writeqflush(u64 val, void __iomem *addr) { __raw_writeq(val, addr); @@ -343,6 +351,26 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(irq_early_us, 2 * irq_early_us); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -351,10 +379,11 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) */ static int octeon_i2c_wait(struct octeon_i2c *i2c) { + int first = 1; long result; i2c->int_en(i2c); - result = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + result = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); if (!result) { -- 1.9.1
Re: [PATCH v4 4/5] arm64/perf: Enable PMCR long cycle counter bit
On Mon, Feb 29, 2016 at 03:39:35PM +, Will Deacon wrote: > Hi Jan, > > I've queued this lot on my perf/updates branch, but I just noticed an > oddity whilst dealing with some potential conflicts with the kvm tree. > > On Thu, Feb 18, 2016 at 05:50:13PM +0100, Jan Glauber wrote: > > With the long cycle counter bit (LC) disabled the cycle counter is not > > working on ThunderX SOC (ThunderX only implements Aarch64). > > Also, according to documentation LC == 0 is deprecated. > > > > To keep the code simple the patch does not introduce 64 bit wide counter > > functions. Instead writing the cycle counter always sets the upper > > 32 bits so overflow interrupts are generated as before. > > > > Original patch from Andrew Pinksi > > > > Signed-off-by: Jan Glauber > > --- > > arch/arm64/kernel/perf_event.c | 21 - > > 1 file changed, 16 insertions(+), 5 deletions(-) > > > > diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c > > index 0ed05f6..c68fa98 100644 > > --- a/arch/arm64/kernel/perf_event.c > > +++ b/arch/arm64/kernel/perf_event.c > > @@ -405,6 +405,7 @@ static const struct attribute_group > > *armv8_pmuv3_attr_groups[] = { > > #define ARMV8_PMCR_D (1 << 3) /* CCNT counts every 64th cpu > > cycle */ > > #define ARMV8_PMCR_X (1 << 4) /* Export to ETM */ > > #define ARMV8_PMCR_DP (1 << 5) /* Disable CCNT if > > non-invasive debug*/ > > +#define ARMV8_PMCR_LC (1 << 6) /* Overflow on 64 bit cycle > > counter */ > > #defineARMV8_PMCR_N_SHIFT 11 /* Number of counters > > supported */ > > #defineARMV8_PMCR_N_MASK 0x1f > > #defineARMV8_PMCR_MASK 0x3f /* Mask for writable bits */ > > You haven't extended this mask to cover the LC bit, so it will be ignored > by armv8pmu_pmcr_write afaict. This is weird. I've double checked and I missed this mask. Annoying. Nevertheless it works for me without the LC bit set. > How did you test this? I can easily update the mask, but it would be > good to know that it doesn't end up cause a breakage. For testing I used: - perf top and perf record & report - looked at interrupt numbers in /proc/interrupts Without the patch _no_ samples at all are recorded and the interrupt does not occur. With the patch I get samples and see a reasonable number of interrupts. Extending the mask so the LC bit is covered would make sense, I'm going to test this now. Jan > Will
Re: [PATCH v3 02/14] i2c-octeon: Cleanup i2c-octeon driver
On Sat, Mar 12, 2016 at 04:35:00PM +0100, Wolfram Sang wrote: > On Mon, Mar 07, 2016 at 04:10:45PM +0100, Jan Glauber wrote: > > Cleanup only without functional change. > > I like most of the changes, but there are still some functional changes > left. > > > -static int octeon_i2c_stop(struct octeon_i2c *i2c) > > +/* send STOP to the bus */ > > +static void octeon_i2c_stop(struct octeon_i2c *i2c) > > { > > u8 data; > > > > @@ -266,11 +259,8 @@ static int octeon_i2c_stop(struct octeon_i2c *i2c) > > > > data = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_STAT); > > > > - if (data != STAT_IDLE) { > > + if (data != STAT_IDLE) > > dev_err(i2c->dev, "%s: bad status(0x%x)\n", __func__, data); > > - return -EIO; > > - } > > - return 0; > > Why this change? I don't know what SW_TWSI_EOP_TWSI_STAT tells, but this > is surely not a cleanup. It is no functional change because the return value of octeon_i2c_stop() was ignored anyway. That said, the whole read-back of the status and the dev_err looks like debug code to me and is removed in a later patch anyway. I'll incoporate this in the cleanup, so octeon_i2c_stop() will only do the write. > > octeon_i2c_stop(i2c); > > > > - return (ret != 0) ? ret : num; > > + return ret ? -EAGAIN : num; > > This is also not a cleanup and looks wrong. -EAGAIN is for lost > arbitration only. I agree, this looks like an over-simplification and drops the EINVAL/ETIMEDOUT/EIO errors. I'll drop that completely. > > > > -static struct of_device_id octeon_i2c_match[] = { > > - { > > - .compatible = "cavium,octeon-3860-twsi", > > - }, > > +static const struct of_device_id octeon_i2c_match[] = { > > + { .compatible = "cavium,octeon-3860-twsi",}, > > Nit: I'd prefer no tabs within the curly braces. Agreed. thanks, Jan > Thanks, > >Wolfram >
Re: [PATCH v3 03/14] i2c-octeon: Cleanup resource allocation code
On Sat, Mar 12, 2016 at 04:37:15PM +0100, Wolfram Sang wrote: > On Mon, Mar 07, 2016 at 04:10:46PM +0100, Jan Glauber wrote: > > From: David Daney > > > > Use resource start and size directly. > > > > Signed-off-by: David Daney > > Signed-off-by: Jan Glauber > > What about converting to devm_ioremap_resource()? > Nice. I'll use that.
Re: [PATCH v3 05/14] i2c-octeon: Make adapter timeout tunable
On Sat, Mar 12, 2016 at 04:46:12PM +0100, Wolfram Sang wrote: > On Mon, Mar 07, 2016 at 04:10:48PM +0100, Jan Glauber wrote: > > From: Peter Swain > > > > Make the i2c adapter timeout a module parameter to allow upper-level > > target device drivers to retry with their own logic before their own > > timeouts abort operations. > > > > For example, at24 eeprom driver retries for 25ms when -EAGAIN > > indicates that an eeprom has gone unresponsive while committing > > a newly written page (5ms on typical devices). > > > > Signed-off-by: Peter Swain > > Signed-off-by: Jan Glauber > > i2c-dev has IOCTLs for timeout and retries. Can't you use those? > Yes, with these IOCTLs we don't need to add a module parameter so I'll drop that.
[PATCH v2 1/5] arm64/perf: Rename Cortex A57 events
The implemented Cortex A57 events are not A57 specific. They are recommended by ARM and can be found on other ARMv8 SOCs like Cavium ThunderX too. Therefore move these events to the common PMUv3 table. Signed-off-by: Jan Glauber --- arch/arm64/kernel/perf_event.c | 28 ++-- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index f7ab14c..32fe656 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -87,17 +87,17 @@ #define ARMV8_PMUV3_PERFCTR_L2D_TLB0x2F #define ARMV8_PMUV3_PERFCTR_L21_TLB0x30 +/* Recommended events. */ +#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS_LD0x40 +#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS_ST0x41 +#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL_LD0x42 +#define ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL_ST0x43 +#define ARMV8_PMUV3_PERFCTR_DTLB_REFILL_LD 0x4C +#define ARMV8_PMUV3_PERFCTR_DTLB_REFILL_ST 0x4D + /* ARMv8 Cortex-A53 specific event types. */ #define ARMV8_A53_PERFCTR_PREFETCH_LINEFILL0xC2 -/* ARMv8 Cortex-A57 and Cortex-A72 specific event types. */ -#define ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_LD 0x40 -#define ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_ST 0x41 -#define ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_LD 0x42 -#define ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_ST 0x43 -#define ARMV8_A57_PERFCTR_DTLB_REFILL_LD 0x4c -#define ARMV8_A57_PERFCTR_DTLB_REFILL_ST 0x4d - /* PMUv3 HW events mapping. */ static const unsigned armv8_pmuv3_perf_map[PERF_COUNT_HW_MAX] = { PERF_MAP_ALL_UNSUPPORTED, @@ -174,16 +174,16 @@ static const unsigned armv8_a57_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = { PERF_CACHE_MAP_ALL_UNSUPPORTED, - [C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_LD, - [C(L1D)][C(OP_READ)][C(RESULT_MISS)]= ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_LD, - [C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV8_A57_PERFCTR_L1_DCACHE_ACCESS_ST, - [C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_A57_PERFCTR_L1_DCACHE_REFILL_ST, + [C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS_LD, + [C(L1D)][C(OP_READ)][C(RESULT_MISS)]= ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL_LD, + [C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS_ST, + [C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL_ST, [C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS, [C(L1I)][C(OP_READ)][C(RESULT_MISS)]= ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL, - [C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_A57_PERFCTR_DTLB_REFILL_LD, - [C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_A57_PERFCTR_DTLB_REFILL_ST, + [C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_DTLB_REFILL_LD, + [C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_DTLB_REFILL_ST, [C(ITLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_ITLB_REFILL, -- 1.9.1
[PATCH v2 4/5] arm64/perf: Enable PMCR long cycle counter bit
With the long cycle counter bit (LC) disabled the cycle counter is not working on ThunderX SOC (ThunderX only implements Aarch64). Also, according to documentation LC == 0 is deprecated. To keep the code simple the patch does not introduce 64 bit wide counter functions. Instead writing the cycle counter always sets the upper 32 bits so overflow interrupts are generated as before. Original patch from Andrew Pinksi Signed-off-by: Jan Glauber --- arch/arm64/kernel/perf_event.c | 21 - 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index c038e4e..5e4275e 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -405,6 +405,7 @@ static const struct attribute_group *armv8_pmuv3_attr_groups[] = { #define ARMV8_PMCR_D (1 << 3) /* CCNT counts every 64th cpu cycle */ #define ARMV8_PMCR_X (1 << 4) /* Export to ETM */ #define ARMV8_PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/ +#define ARMV8_PMCR_LC (1 << 6) /* Overflow on 64 bit cycle counter */ #defineARMV8_PMCR_N_SHIFT 11 /* Number of counters supported */ #defineARMV8_PMCR_N_MASK 0x1f #defineARMV8_PMCR_MASK 0x3f /* Mask for writable bits */ @@ -494,9 +495,16 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u32 value) if (!armv8pmu_counter_valid(cpu_pmu, idx)) pr_err("CPU%u writing wrong counter %d\n", smp_processor_id(), idx); - else if (idx == ARMV8_IDX_CYCLE_COUNTER) - asm volatile("msr pmccntr_el0, %0" :: "r" (value)); - else if (armv8pmu_select_counter(idx) == idx) + else if (idx == ARMV8_IDX_CYCLE_COUNTER) { + /* +* Set the upper 32bits as this is a 64bit counter but we only +* count using the lower 32bits and we want an interrupt when +* it overflows. +*/ + u64 value64 = 0xULL | value; + + asm volatile("msr pmccntr_el0, %0" :: "r" (value64)); + } else if (armv8pmu_select_counter(idx) == idx) asm volatile("msr pmxevcntr_el0, %0" :: "r" (value)); } @@ -768,8 +776,11 @@ static void armv8pmu_reset(void *info) armv8pmu_disable_intens(idx); } - /* Initialize & Reset PMNC: C and P bits. */ - armv8pmu_pmcr_write(ARMV8_PMCR_P | ARMV8_PMCR_C); + /* +* Initialize & Reset PMNC. Request overflow on 64 bit but +* cheat in armv8pmu_write_counter(). +*/ + armv8pmu_pmcr_write(ARMV8_PMCR_P | ARMV8_PMCR_C | ARMV8_PMCR_LC); } static int armv8_pmuv3_map_event(struct perf_event *event) -- 1.9.1
[PATCH v2 2/5] arm64/perf: Add Cavium ThunderX PMU support
Support PMU events on Caviums ThunderX SOC. ThunderX supports some additional counters compared to the default ARMv8 PMUv3: - branch instructions counter - stall frontend & backend counters - L1 dcache load & store counters - L1 icache counters - iTLB & dTLB counters - L1 dcache & icache prefetch counters Signed-off-by: Jan Glauber --- arch/arm64/kernel/perf_event.c | 69 +- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 32fe656..c038e4e 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -94,10 +94,19 @@ #define ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL_ST0x43 #define ARMV8_PMUV3_PERFCTR_DTLB_REFILL_LD 0x4C #define ARMV8_PMUV3_PERFCTR_DTLB_REFILL_ST 0x4D +#define ARMV8_PMUV3_PERFCTR_DTLB_ACCESS_LD 0x4E +#define ARMV8_PMUV3_PERFCTR_DTLB_ACCESS_ST 0x4F /* ARMv8 Cortex-A53 specific event types. */ #define ARMV8_A53_PERFCTR_PREFETCH_LINEFILL0xC2 +/* ARMv8 Cavium Thunder specific event types. */ +#define ARMV8_THUNDER_PERFCTR_L1_DCACHE_MISS_ST0xE9 +#define ARMV8_THUNDER_PERFCTR_L1_DCACHE_PREF_ACCESS0xEA +#define ARMV8_THUNDER_PERFCTR_L1_DCACHE_PREF_MISS 0xEB +#define ARMV8_THUNDER_PERFCTR_L1_ICACHE_PREF_ACCESS0xEC +#define ARMV8_THUNDER_PERFCTR_L1_ICACHE_PREF_MISS 0xED + /* PMUv3 HW events mapping. */ static const unsigned armv8_pmuv3_perf_map[PERF_COUNT_HW_MAX] = { PERF_MAP_ALL_UNSUPPORTED, @@ -131,6 +140,18 @@ static const unsigned armv8_a57_perf_map[PERF_COUNT_HW_MAX] = { [PERF_COUNT_HW_BUS_CYCLES] = ARMV8_PMUV3_PERFCTR_BUS_CYCLES, }; +static const unsigned armv8_thunder_perf_map[PERF_COUNT_HW_MAX] = { + PERF_MAP_ALL_UNSUPPORTED, + [PERF_COUNT_HW_CPU_CYCLES] = ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES, + [PERF_COUNT_HW_INSTRUCTIONS]= ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED, + [PERF_COUNT_HW_CACHE_REFERENCES]= ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS, + [PERF_COUNT_HW_CACHE_MISSES]= ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL, + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = ARMV8_PMUV3_PERFCTR_PC_WRITE, + [PERF_COUNT_HW_BRANCH_MISSES] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED, + [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = ARMV8_PMUV3_PERFCTR_STALL_FRONTEND, + [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = ARMV8_PMUV3_PERFCTR_STALL_BACKEND, +}; + static const unsigned armv8_pmuv3_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = { @@ -193,6 +214,36 @@ static const unsigned armv8_a57_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] [C(BPU)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED, }; +static const unsigned armv8_thunder_perf_cache_map[PERF_COUNT_HW_CACHE_MAX] + [PERF_COUNT_HW_CACHE_OP_MAX] + [PERF_COUNT_HW_CACHE_RESULT_MAX] = { + PERF_CACHE_MAP_ALL_UNSUPPORTED, + + [C(L1D)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS_LD, + [C(L1D)][C(OP_READ)][C(RESULT_MISS)]= ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL_LD, + [C(L1D)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS_ST, + [C(L1D)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_THUNDER_PERFCTR_L1_DCACHE_MISS_ST, + [C(L1D)][C(OP_PREFETCH)][C(RESULT_ACCESS)] = ARMV8_THUNDER_PERFCTR_L1_DCACHE_PREF_ACCESS, + [C(L1D)][C(OP_PREFETCH)][C(RESULT_MISS)] = ARMV8_THUNDER_PERFCTR_L1_DCACHE_PREF_MISS, + + [C(L1I)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS, + [C(L1I)][C(OP_READ)][C(RESULT_MISS)]= ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL, + [C(L1I)][C(OP_PREFETCH)][C(RESULT_ACCESS)] = ARMV8_THUNDER_PERFCTR_L1_ICACHE_PREF_ACCESS, + [C(L1I)][C(OP_PREFETCH)][C(RESULT_MISS)] = ARMV8_THUNDER_PERFCTR_L1_ICACHE_PREF_MISS, + + [C(DTLB)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_DTLB_ACCESS_LD, + [C(DTLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_DTLB_REFILL_LD, + [C(DTLB)][C(OP_WRITE)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_DTLB_ACCESS_ST, + [C(DTLB)][C(OP_WRITE)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_DTLB_REFILL_ST, + + [C(ITLB)][C(OP_READ)][C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_ITLB_REFILL, + + [C(BPU)][C(OP_READ)][C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED, + [C(BPU)][C(OP_READ)][C(RESULT_MISS)]= ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED, + [C(BPU)][C(OP_WRITE)]
[PATCH v2 5/5] arm64/perf: Extend event mask for ARMv8.1
ARMv8.1 increases the PMU event number space. Detect the presence of this PMUv3 type and extend the event mask. The event mask is moved to struct arm_pmu so different event masks can exist, depending on the PMU type. Signed-off-by: Jan Glauber --- arch/arm64/kernel/perf_event.c | 33 +++-- drivers/perf/arm_pmu.c | 5 +++-- include/linux/perf/arm_pmu.h | 4 ++-- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c index 5e4275e..78b24cb 100644 --- a/arch/arm64/kernel/perf_event.c +++ b/arch/arm64/kernel/perf_event.c @@ -419,7 +419,7 @@ static const struct attribute_group *armv8_pmuv3_attr_groups[] = { /* * PMXEVTYPER: Event selection reg */ -#defineARMV8_EVTYPE_MASK 0xc80003ff /* Mask for writable bits */ +#defineARMV8_EVTYPE_FLT_MASK 0xc800 /* Writable filter bits */ #defineARMV8_EVTYPE_EVENT 0x3ff /* Mask for EVENT bits */ /* @@ -510,10 +510,8 @@ static inline void armv8pmu_write_counter(struct perf_event *event, u32 value) static inline void armv8pmu_write_evtype(int idx, u32 val) { - if (armv8pmu_select_counter(idx) == idx) { - val &= ARMV8_EVTYPE_MASK; + if (armv8pmu_select_counter(idx) == idx) asm volatile("msr pmxevtyper_el0, %0" :: "r" (val)); - } } static inline int armv8pmu_enable_counter(int idx) @@ -570,6 +568,7 @@ static void armv8pmu_enable_event(struct perf_event *event) struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct pmu_hw_events *events = this_cpu_ptr(cpu_pmu->hw_events); int idx = hwc->idx; + u32 val; /* * Enable counter and interrupt, and set the counter to count @@ -585,7 +584,8 @@ static void armv8pmu_enable_event(struct perf_event *event) /* * Set event (if destined for PMNx counters). */ - armv8pmu_write_evtype(idx, hwc->config_base); + val = hwc->config_base & (ARMV8_EVTYPE_FLT_MASK | cpu_pmu->event_mask); + armv8pmu_write_evtype(idx, val); /* * Enable interrupt for this counter @@ -716,7 +716,7 @@ static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc, int idx; struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = >hw; - unsigned long evtype = hwc->config_base & ARMV8_EVTYPE_EVENT; + unsigned long evtype = hwc->config_base & cpu_pmu->event_mask; /* Always place a cycle counter into the cycle counter. */ if (evtype == ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES) { @@ -786,29 +786,25 @@ static void armv8pmu_reset(void *info) static int armv8_pmuv3_map_event(struct perf_event *event) { return armpmu_map_event(event, _pmuv3_perf_map, - _pmuv3_perf_cache_map, - ARMV8_EVTYPE_EVENT); + _pmuv3_perf_cache_map); } static int armv8_a53_map_event(struct perf_event *event) { return armpmu_map_event(event, _a53_perf_map, - _a53_perf_cache_map, - ARMV8_EVTYPE_EVENT); + _a53_perf_cache_map); } static int armv8_a57_map_event(struct perf_event *event) { return armpmu_map_event(event, _a57_perf_map, - _a57_perf_cache_map, - ARMV8_EVTYPE_EVENT); + _a57_perf_cache_map); } static int armv8_thunder_map_event(struct perf_event *event) { return armpmu_map_event(event, _thunder_perf_map, - _thunder_perf_cache_map, - ARMV8_EVTYPE_EVENT); + _thunder_perf_cache_map); } static void armv8pmu_read_num_pmnc_events(void *info) @@ -831,6 +827,8 @@ static int armv8pmu_probe_num_events(struct arm_pmu *arm_pmu) static void armv8_pmu_init(struct arm_pmu *cpu_pmu) { + u64 id; + cpu_pmu->handle_irq = armv8pmu_handle_irq, cpu_pmu->enable = armv8pmu_enable_event, cpu_pmu->disable= armv8pmu_disable_event, @@ -842,6 +840,13 @@ static void armv8_pmu_init(struct arm_pmu *cpu_pmu) cpu_pmu->reset = armv8pmu_reset, cpu_pmu->max_period = (1LLU << 32) - 1, cpu_pmu->set_event_filter = armv8pmu_set_event_filter; + + /* detect ARMv8.1 PMUv3 with extended event mask */ + id = read_cpuid(ID_AA64DFR0_EL1); + if (((id >> 8) & 0xf) == 4) + cpu_pmu->event_mask = 0x; /* ARMv8.1 extended events */ + else + cpu_pmu->event_mask = ARMV8_EVTYPE_EVENT; } static int armv8_pmuv3_init(struct arm_pmu *cpu_
[PATCH v2 3/5] arm64: dts: Add Cavium ThunderX specific PMU
Add a compatible string for the Cavium ThunderX PMU. Signed-off-by: Jan Glauber --- Documentation/devicetree/bindings/arm/pmu.txt | 1 + arch/arm64/boot/dts/cavium/thunder-88xx.dtsi | 5 + 2 files changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/pmu.txt b/Documentation/devicetree/bindings/arm/pmu.txt index 5651883..d3999a1 100644 --- a/Documentation/devicetree/bindings/arm/pmu.txt +++ b/Documentation/devicetree/bindings/arm/pmu.txt @@ -25,6 +25,7 @@ Required properties: "qcom,scorpion-pmu" "qcom,scorpion-mp-pmu" "qcom,krait-pmu" + "cavium,thunder-pmu" - interrupts : 1 combined interrupt or 1 per core. If the interrupt is a per-cpu interrupt (PPI) then 1 interrupt should be specified. diff --git a/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi b/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi index 9cb7cf9..2eb9b22 100644 --- a/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi +++ b/arch/arm64/boot/dts/cavium/thunder-88xx.dtsi @@ -360,6 +360,11 @@ <1 10 0xff01>; }; + pmu { + compatible = "cavium,thunder-pmu", "arm,armv8-pmuv3"; + interrupts = <1 7 4>; + }; + soc { compatible = "simple-bus"; #address-cells = <2>; -- 1.9.1
[PATCH v2 0/5] Cavium ThunderX PMU support
Hi Mark & Will, I'm resending the arm64 PMU patches. The only difference to the first version is that I dropped the x on thunder in order to be consistent with the existing device tree name. Thanks, Jan Jan Glauber (5): arm64/perf: Rename Cortex A57 events arm64/perf: Add Cavium ThunderX PMU support arm64: dts: Add Cavium ThunderX specific PMU arm64/perf: Enable PMCR long cycle counter bit arm64/perf: Extend event mask for ARMv8.1 Documentation/devicetree/bindings/arm/pmu.txt | 1 + arch/arm64/boot/dts/cavium/thunder-88xx.dtsi | 5 + arch/arm64/kernel/perf_event.c| 145 -- drivers/perf/arm_pmu.c| 5 +- include/linux/perf/arm_pmu.h | 4 +- 5 files changed, 125 insertions(+), 35 deletions(-) -- 1.9.1
[PATCH v2 5/5] arm64/perf: Extend event mask for ARMv8.1
ARMv8.1 increases the PMU event number space. Detect the presence of this PMUv3 type and extend the event mask. The event mask is moved to struct arm_pmu so different event masks can exist, depending on the PMU type. Signed-off-by: Jan Glauber --- arch/arm/kernel/perf_event_v6.c | 6 -- arch/arm/kernel/perf_event_v7.c | 29 +++-- arch/arm/kernel/perf_event_xscale.c | 4 +++- arch/arm64/kernel/perf_event.c | 33 +++-- drivers/perf/arm_pmu.c | 5 +++-- include/linux/perf/arm_pmu.h| 4 ++-- 6 files changed, 50 insertions(+), 31 deletions(-) diff --git a/arch/arm/kernel/perf_event_v6.c b/arch/arm/kernel/perf_event_v6.c index 09413e7..d6769f5 100644 --- a/arch/arm/kernel/perf_event_v6.c +++ b/arch/arm/kernel/perf_event_v6.c @@ -481,7 +481,7 @@ static void armv6mpcore_pmu_disable_event(struct perf_event *event) static int armv6_map_event(struct perf_event *event) { return armpmu_map_event(event, _perf_map, - _perf_cache_map, 0xFF); + _perf_cache_map); } static void armv6pmu_init(struct arm_pmu *cpu_pmu) @@ -494,6 +494,7 @@ static void armv6pmu_init(struct arm_pmu *cpu_pmu) cpu_pmu->get_event_idx = armv6pmu_get_event_idx; cpu_pmu->start = armv6pmu_start; cpu_pmu->stop = armv6pmu_stop; + cpu_pmu->event_mask = 0xFF; cpu_pmu->map_event = armv6_map_event; cpu_pmu->num_events = 3; cpu_pmu->max_period = (1LLU << 32) - 1; @@ -531,7 +532,7 @@ static int armv6_1176_pmu_init(struct arm_pmu *cpu_pmu) static int armv6mpcore_map_event(struct perf_event *event) { return armpmu_map_event(event, _perf_map, - _perf_cache_map, 0xFF); + _perf_cache_map); } static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu) @@ -545,6 +546,7 @@ static int armv6mpcore_pmu_init(struct arm_pmu *cpu_pmu) cpu_pmu->get_event_idx = armv6pmu_get_event_idx; cpu_pmu->start = armv6pmu_start; cpu_pmu->stop = armv6pmu_stop; + cpu_pmu->event_mask = 0xFF; cpu_pmu->map_event = armv6mpcore_map_event; cpu_pmu->num_events = 3; cpu_pmu->max_period = (1LLU << 32) - 1; diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c index 4152158..8aab098 100644 --- a/arch/arm/kernel/perf_event_v7.c +++ b/arch/arm/kernel/perf_event_v7.c @@ -1042,7 +1042,7 @@ static int armv7pmu_get_event_idx(struct pmu_hw_events *cpuc, int idx; struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); struct hw_perf_event *hwc = >hw; - unsigned long evtype = hwc->config_base & ARMV7_EVTYPE_EVENT; + unsigned long evtype = hwc->config_base & cpu_pmu->event_mask; /* Always place a cycle counter into the cycle counter. */ if (evtype == ARMV7_PERFCTR_CPU_CYCLES) { @@ -1109,55 +1109,55 @@ static void armv7pmu_reset(void *info) static int armv7_a8_map_event(struct perf_event *event) { return armpmu_map_event(event, _a8_perf_map, - _a8_perf_cache_map, 0xFF); + _a8_perf_cache_map); } static int armv7_a9_map_event(struct perf_event *event) { return armpmu_map_event(event, _a9_perf_map, - _a9_perf_cache_map, 0xFF); + _a9_perf_cache_map); } static int armv7_a5_map_event(struct perf_event *event) { return armpmu_map_event(event, _a5_perf_map, - _a5_perf_cache_map, 0xFF); + _a5_perf_cache_map); } static int armv7_a15_map_event(struct perf_event *event) { return armpmu_map_event(event, _a15_perf_map, - _a15_perf_cache_map, 0xFF); + _a15_perf_cache_map); } static int armv7_a7_map_event(struct perf_event *event) { return armpmu_map_event(event, _a7_perf_map, - _a7_perf_cache_map, 0xFF); + _a7_perf_cache_map); } static int armv7_a12_map_event(struct perf_event *event) { return armpmu_map_event(event, _a12_perf_map, - _a12_perf_cache_map, 0xFF); + _a12_perf_cache_map); } static int krait_map_event(struct perf_event *event) { return armpmu_map_event(event, _perf_map, - _perf_cache_map, 0xF); + _perf_cache_map); } static int krait_map_event_no_branch(struct perf_event *event) { return armpmu_map_event(event, _perf_map_no_branch, - _perf_cache_map, 0xF); + _
Re: [PATCH v2 5/5] arm64/perf: Extend event mask for ARMv8.1
On Fri, Jan 29, 2016 at 01:33:35AM +0800, kbuild test robot wrote: > Hi Jan, > > [auto build test ERROR on robh/for-next] > [also build test ERROR on v4.5-rc1 next-20160128] > [cannot apply to tip/perf/core] > [if your patch is applied to the wrong git tree, please drop us a note to > help improving the system] > > url: > https://github.com/0day-ci/linux/commits/Jan-Glauber/Cavium-ThunderX-PMU-support/20160128-225855 > base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux for-next > config: arm-corgi_defconfig (attached as .config) > reproduce: > wget > https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross > -O ~/bin/make.cross > chmod +x ~/bin/make.cross > # save the attached .config to linux build tree > make.cross ARCH=arm > > All errors (new ones prefixed by >>): > >arch/arm/kernel/perf_event_xscale.c: In function 'xscale_map_event': > >> arch/arm/kernel/perf_event_xscale.c:360:9: error: too many arguments to > >> function 'armpmu_map_event' > return armpmu_map_event(event, _perf_map, > ^ I forgot the arm32 parts of this patch. I'll resend this patch only, the other patches don't touch arm32. --Jan
[PATCH v2 07/10] i2c-octeon: Faster operation when IFLG signals late
From: Peter Swain Some versions can deliver low-level twsi irq before twsi_ctl.iflg is set, leading to timeout-driven i/o. When an irq signals event, but woken task does not see the expected twsi_ctl.iflg, re-check about 80uS later. EEPROM reads on 100kHz i2c now measure ~5.2kB/s, about 1/2 what's achievable, and much better than the worst-case 100 bytes/sec before. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 31 ++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index bb15a9c..e3552e5 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -130,6 +130,14 @@ static int timeout = 2; module_param(timeout, int, 0444); MODULE_PARM_DESC(timeout, "Low-level device timeout (ms)"); +/* + * On some hardware IFLG is not visible in TWSI_CTL until after low-level IRQ, + * so re-sample CTL a short time later to avoid stalls. + */ +static int irq_early_us = 80; +module_param(irq_early_us, int, 0644); +MODULE_PARM_DESC(irq_early_us, "Re-poll for IFLG after IRQ (us)"); + static void writeqflush(u64 val, void __iomem *addr) { __raw_writeq(val, addr); @@ -343,6 +351,26 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(irq_early_us, 2 * irq_early_us); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -351,10 +379,11 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) */ static int octeon_i2c_wait(struct octeon_i2c *i2c) { + int first = 1; long result; i2c->int_en(i2c); - result = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + result = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); if (!result) { -- 1.9.1
[PATCH v2 00/10] i2c-octeon and i2c-thunderx drivers
This series for the Octeon i2c driver is an attempt to upstream some bug fixes and features that accumulated for some time. I tried to split the cleanup from the other patches and updated the patch descriptions according to my humble i2c knowledge. Patches are on top of 4.5-rc6 and were tested on OCTEON, OCTEON-78 and ThunderX. Changes to v1: - Fixed compile error on x86_64 - Disabled thunderx driver on MIPS - Re-ordered some thunderx probe functions for readability - Fix missing of_irq.h and i2c-smbus.h includes - Use IS_ENABLED for CONFIG options Comments welcome. Thanks, Jan -- David Daney (4): i2c-octeon: Support I2C_M_RECV_LEN i2c-octeon: Enable high-level controller and improve on bus contention i2c-octeon: Add support for cn78XX chips i2c-octeon: Add workaround for chips with broken irqs Jan Glauber (4): i2c-octeon: Cleanup i2c-octeon driver dt-bindings: i2c: add Octeon cn78xx TWSI i2c: split i2c-octeon driver and add ThunderX support i2c: thunderx: add smbus support Peter Swain (2): i2c-octeon: Flush TWSI writes with readback i2c-octeon: Faster operation when IFLG signals late .../devicetree/bindings/i2c/i2c-octeon.txt | 6 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile| 3 + drivers/i2c/busses/i2c-cavium.c| 828 + drivers/i2c/busses/i2c-cavium.h| 191 + drivers/i2c/busses/i2c-octeon-core.c | 302 drivers/i2c/busses/i2c-octeon.c| 633 drivers/i2c/busses/i2c-thunderx-core.c | 301 8 files changed, 1641 insertions(+), 633 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c -- 1.9.1
[PATCH v2 03/10] i2c-octeon: Enable high-level controller and improve on bus contention
From: David Daney Use High Level Controller when possible. i2c-octeon was reacting badly to bus contention: when in direct-access mode (for transfers > 8 bytes, which cannot use the high-level controller) some !ACK or arbitration-loss states were not causing the current transfer to be aborted, and the bus released. There's one place in i2c protocol that !ACK is an acceptable response: in the final byte of a read cycle. In this case the destination is not saying that the transfer failed, just that it doesn't want more data. This enables correct behavior of ACK on final byte of non-final read msgs too. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 583 ++-- 1 file changed, 504 insertions(+), 79 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index fa4d439..d48456a 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -113,8 +113,15 @@ struct octeon_i2c { int sys_freq; void __iomem*twsi_base; struct device *dev; + boolhlc_enabled; }; +static int reset_how; + +static int timeout = 2; +module_param(timeout, int, 0444); +MODULE_PARM_DESC(timeout, "Low-level device timeout (ms)"); + /** * octeon_i2c_write_sw - write an I2C core register * @i2c: The struct octeon_i2c @@ -123,7 +130,7 @@ struct octeon_i2c { * * The I2C core registers are accessed indirectly via the SW_TWSI CSR. */ -static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +static void octeon_i2c_write_sw(struct octeon_i2c *i2c, u64 eop_reg, u32 data) { u64 tmp; @@ -165,7 +172,7 @@ static u64 octeon_i2c_read_sw64(struct octeon_i2c *i2c, u64 eop_reg) */ static u8 octeon_i2c_read_sw(struct octeon_i2c *i2c, u64 eop_reg) { - return octeon_i2c_read_sw64(i2c, eop_reg); + return (u8) octeon_i2c_read_sw64(i2c, eop_reg); } /** @@ -198,6 +205,15 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) octeon_i2c_write_int(i2c, TS_INT | ST_INT); } +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + /** * bitbang_unblock - unblock the bus * @i2c: The struct octeon_i2c @@ -207,13 +223,18 @@ static void octeon_i2c_int_disable(struct octeon_i2c *i2c) */ static void bitbang_unblock(struct octeon_i2c *i2c) { - int i; + int state, i; + octeon_i2c_disable_hlc(i2c); dev_dbg(i2c->dev, "%s\n", __func__); + /* cycle 8+1 clocks with SDA high */ for (i = 0; i < 9; i++) { octeon_i2c_write_int(i2c, 0); udelay(5); + state = __raw_readq(i2c->twsi_base + TWSI_INT); + if (state & (SDA | SCL)) + break; octeon_i2c_write_int(i2c, SCL_OVR); udelay(5); } @@ -236,10 +257,14 @@ static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static u64 octeon_i2c_read_ctl(struct octeon_i2c *i2c) +{ + return octeon_i2c_read_sw64(i2c, SW_TWSI_EOP_TWSI_CTL); +} + static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return (octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_CTL) - & TWSI_CTL_IFLG) != 0; + return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } /** @@ -263,76 +288,208 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) return 0; } -/* send STOP to the bus */ -static void octeon_i2c_stop(struct octeon_i2c *i2c) +/* + * Cleanup low-level state & enable high-level. + * + * Returns -EAGAIN if low-level state could not be cleaned + */ +static int octeon_i2c_enable_hlc(struct octeon_i2c *i2c) { - u8 data; + int try = 0, ret = 0; + u64 val; + + if (i2c->hlc_enabled) + return 0; + else + i2c->hlc_enabled = true; + + while (1) { + val = octeon_i2c_read_ctl(i2c) & (TWSI_CTL_STA | TWSI_CTL_STP); + if (!val) + break; + + /* clear _IFLG event */ + if (val & TWSI_CTL_IFLG) + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, + TWSI_CTL_ENAB); + + if (try++ > 100) { + pr_err("%s: giving up\n", __func__); + ret = -EAGAIN; + break; + } + + /* spin until any start/stop has finished */ + udelay(10); + } octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, - TWSI_CTL_ENAB | TWSI_CTL_STP); +
[PATCH v2 10/10] i2c: thunderx: add smbus support
Add smbus alert interrupt support. Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-cavium.h| 6 ++ drivers/i2c/busses/i2c-thunderx-core.c | 35 ++ 2 files changed, 41 insertions(+) diff --git a/drivers/i2c/busses/i2c-cavium.h b/drivers/i2c/busses/i2c-cavium.h index aadd4f5..062bc022 100644 --- a/drivers/i2c/busses/i2c-cavium.h +++ b/drivers/i2c/busses/i2c-cavium.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -117,6 +118,11 @@ struct octeon_i2c { #if IS_ENABLED(CONFIG_I2C_THUNDERX) struct msix_entry i2c_msix; #endif + +#if IS_ENABLED(CONFIG_I2C_SMBUS) + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; +#endif }; static inline void writeqflush(u64 val, void __iomem *addr) diff --git a/drivers/i2c/busses/i2c-thunderx-core.c b/drivers/i2c/busses/i2c-thunderx-core.c index e14838a..cdfadee 100644 --- a/drivers/i2c/busses/i2c-thunderx-core.c +++ b/drivers/i2c/busses/i2c-thunderx-core.c @@ -9,10 +9,12 @@ #include #include #include +#include #include #include #include +#include #include #include "i2c-cavium.h" @@ -107,6 +109,35 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) devm_clk_put(dev, clk); } +static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c, + struct device_node *node) +{ +#if IS_ENABLED(CONFIG_I2C_SMBUS) + u32 type; + + i2c->alert_data.irq = irq_of_parse_and_map(node, 0); + if (!i2c->alert_data.irq) + return -EINVAL; + + type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq)); + i2c->alert_data.alert_edge_triggered = + (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0; + + i2c->ara = i2c_setup_smbus_alert(>adap, >alert_data); + if (!i2c->ara) + return -ENODEV; +#endif + return 0; +} + +static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c) +{ +#if IS_ENABLED(CONFIG_I2C_SMBUS) + if (i2c->ara) + i2c_unregister_device(i2c->ara); +#endif +} + static void thunder_i2c_set_name(struct pci_dev *pdev, struct octeon_i2c *i2c, char *name) { @@ -205,6 +236,9 @@ static int thunder_i2c_probe_pci(struct pci_dev *pdev, goto out_irq; } + ret = thunder_i2c_smbus_setup(i2c, node); + if (ret < 0) + dev_err(dev, "Failed to setup smbus alert\n"); dev_info(i2c->dev, "probed\n"); return 0; @@ -235,6 +269,7 @@ static void thunder_i2c_remove_pci(struct pci_dev *pdev) dev = i2c->dev; thunder_i2c_clock_disable(dev, i2c->clk); + thunder_i2c_smbus_remove(i2c); i2c_del_adapter(>adap); devm_free_irq(dev, i2c->i2c_msix.vector, i2c); pci_disable_msix(pdev); -- 1.9.1
[PATCH v2 09/10] i2c: split i2c-octeon driver and add ThunderX support
The ThunderX SOC uses the same i2c block as the Octeon SOC. The main difference is that on ThunderX the device is a PCI device so device probing is done via PCI. Split the current Octeon driver into an Octeon and a common part and add the ThunderX support. Signed-off-by: Jan Glauber --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile|3 + drivers/i2c/busses/i2c-cavium.c| 828 + drivers/i2c/busses/i2c-cavium.h| 185 + drivers/i2c/busses/i2c-octeon-core.c | 302 drivers/i2c/busses/i2c-octeon.c| 1283 drivers/i2c/busses/i2c-thunderx-core.c | 266 +++ 7 files changed, 1594 insertions(+), 1283 deletions(-) create mode 100644 drivers/i2c/busses/i2c-cavium.c create mode 100644 drivers/i2c/busses/i2c-cavium.h create mode 100644 drivers/i2c/busses/i2c-octeon-core.c delete mode 100644 drivers/i2c/busses/i2c-octeon.c create mode 100644 drivers/i2c/busses/i2c-thunderx-core.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 7b0aa82..6069401 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -952,6 +952,16 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI && !CAVIUM_OCTEON_SOC + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + config I2C_XILINX tristate "Xilinx I2C Controller" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819..a32ff14 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,10 @@ obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE)+= i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o +i2c-octeon-objs := i2c-cavium.o i2c-octeon-core.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +i2c-thunderx-objs := i2c-cavium.o i2c-thunderx-core.o +obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-cavium.c b/drivers/i2c/busses/i2c-cavium.c new file mode 100644 index 000..93d8c0f --- /dev/null +++ b/drivers/i2c/busses/i2c-cavium.c @@ -0,0 +1,828 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#include "i2c-cavium.h" + +static int reset_how; + +/* + * On some hardware IFLG is not visible in TWSI_CTL until after low-level IRQ, + * so re-sample CTL a short time later to avoid stalls. + */ +static int irq_early_us = 80; +module_param(irq_early_us, int, 0644); +MODULE_PARM_DESC(irq_early_us, "Re-poll for IFLG after IRQ (us)"); + +static void octeon_i2c_disable_hlc(struct octeon_i2c *i2c) +{ + if (!i2c->hlc_enabled) + return; + + i2c->hlc_enabled = false; + octeon_i2c_write_sw(i2c, SW_TWSI_EOP_TWSI_CTL, TWSI_CTL_ENAB); +} + +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->int_dis(i2c); + wake_up(>queue); + + return IRQ_HANDLED; +} + +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(irq_early_us, 2 * irq_early_us); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + +/** + * octeon_i2c_wait - wait for the IFLG to be set + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_wait(struct octeon_i2c *i2c) +{ + int first = 1; + long result; + + if (i2c->broken_irq_mod
[PATCH v2 08/10] i2c-octeon: Add workaround for chips with broken irqs
From: David Daney CN3860 does not interrupt the CPU when the i2c status changes. If we get a timeout, and see the status has in fact changed, we know we have this problem, and drop back to polling. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 46 + 1 file changed, 46 insertions(+) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index e3552e5..3c2f848 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -115,6 +115,7 @@ struct octeon_i2c { int sys_freq; void __iomem*twsi_base; struct device *dev; + int broken_irq_mode; boolhlc_enabled; void(*int_en) (struct octeon_i2c *); void(*int_dis) (struct octeon_i2c *); @@ -382,10 +383,33 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) int first = 1; long result; + if (i2c->broken_irq_mode) { + /* +* Some chip revisions seem to not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + i2c->int_en(i2c); result = wait_event_timeout(i2c->queue, poll_iflg(i2c, ), i2c->adap.timeout); i2c->int_dis(i2c); + + if (result <= 0 && OCTEON_IS_MODEL(OCTEON_CN38XX) && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, + "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = 1; + return 0; + } if (!result) { dev_dbg(i2c->dev, "%s: timeout\n", __func__); return -ETIMEDOUT; @@ -740,6 +764,21 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { int result; + if (i2c->broken_irq_mode) { + /* +* Some cn38xx boards did not assert the irq in +* the interrupt controller. So we must poll for the +* IFLG change. +*/ + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_hlc_test_ready(i2c) && + time_before64(get_jiffies_64(), end)) + udelay(50); + + return octeon_i2c_hlc_test_ready(i2c) ? 0 : -ETIMEDOUT; + } + i2c->hlc_int_en(i2c); result = wait_event_interruptible_timeout(i2c->queue, octeon_i2c_hlc_test_ready(i2c), i2c->adap.timeout); @@ -747,6 +786,13 @@ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) if (!result) octeon_i2c_hlc_int_clear(i2c); + if (result <= 0 && OCTEON_IS_MODEL(OCTEON_CN38XX) && + octeon_i2c_hlc_test_ready(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = 1; + return 0; + } + if (result < 0) { dev_dbg(i2c->dev, "%s: wait interrupted\n", __func__); return result; -- 1.9.1
[PATCH v2 01/10] i2c-octeon: Cleanup i2c-octeon driver
Cleanup only without functional change. Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 442 +--- 1 file changed, 230 insertions(+), 212 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 32914ab..1f14094 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -2,7 +2,7 @@ * (C) Copyright 2009-2010 * Nokia Siemens Networks, michael.lawnick@nsn.com * - * Portions Copyright (C) 2010, 2011 Cavium Networks, Inc. + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. * * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. * @@ -24,99 +24,154 @@ #include -#define DRV_NAME "i2c-octeon" +#define DRV_NAME "i2c-octeon" -/* The previous out-of-tree version was implicitly version 1.0. */ -#define DRV_VERSION"2.0" +/* Register offsets */ +#define SW_TWSI0x00 +#define TWSI_INT 0x10 +#define SW_TWSI_EXT0x18 -/* register offsets */ -#define SW_TWSI 0x00 -#define TWSI_INT 0x10 +#define INT_ENA_ST 0x1 +#define INT_ENA_TS 0x2 +#define INT_ENA_CORE 0x4 /* Controller command patterns */ -#define SW_TWSI_V 0x8000ull -#define SW_TWSI_EOP_TWSI_DATA 0x0C01ull -#define SW_TWSI_EOP_TWSI_CTL0x0C02ull -#define SW_TWSI_EOP_TWSI_CLKCTL 0x0C03ull -#define SW_TWSI_EOP_TWSI_STAT 0x0C03ull -#define SW_TWSI_EOP_TWSI_RST0x0C07ull -#define SW_TWSI_OP_TWSI_CLK 0x0800ull -#define SW_TWSI_R 0x0100ull +#define SW_TWSI_V (1ULL << 63) +#define SW_TWSI_EIA(1ULL << 61) +#define SW_TWSI_R (1ULL << 56) +#define SW_TWSI_SOVR (1ULL << 55) +#define SW_TWSI_OP_7 (0ULL << 57) +#define SW_TWSI_OP_7_IA(1ULL << 57) +#define SW_TWSI_OP_10 (2ULL << 57) +#define SW_TWSI_OP_10_IA (3ULL << 57) +#define SW_TWSI_OP_TWSI_CLK(1ULL << 59) +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_A_SHIFT40 +#define SW_TWSI_IA_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA 0x0C01ULL +#define SW_TWSI_EOP_TWSI_CTL 0x0C02ULL +#define SW_TWSI_EOP_TWSI_CLKCTL0x0C03ULL +#define SW_TWSI_EOP_TWSI_STAT 0x0C03ULL +#define SW_TWSI_EOP_TWSI_RST 0x0C07ULL /* Controller command and status bits */ -#define TWSI_CTL_CE 0x80 -#define TWSI_CTL_ENAB 0x40 -#define TWSI_CTL_STA 0x20 -#define TWSI_CTL_STP 0x10 -#define TWSI_CTL_IFLG 0x08 -#define TWSI_CTL_AAK 0x04 +#define TWSI_CTL_CE0x80/* High level controller enable */ +#define TWSI_CTL_ENAB 0x40/* Bus enable */ +#define TWSI_CTL_STA 0x20/* Master-mode start, HW clears when done */ +#define TWSI_CTL_STP 0x10/* Master-mode stop, HW clears when done */ +#define TWSI_CTL_IFLG 0x08/* HW event, SW writes 0 to ACK */ +#define TWSI_CTL_AAK 0x04/* Assert ACK */ /* Some status values */ -#define STAT_START 0x08 -#define STAT_RSTART 0x10 -#define STAT_TXADDR_ACK 0x18 -#define STAT_TXDATA_ACK 0x28 -#define STAT_RXADDR_ACK 0x40 -#define STAT_RXDATA_ACK 0x50 -#define STAT_IDLE 0xF8 +#define STAT_ERROR 0x00 +#define STAT_START 0x08 +#define STAT_RSTART0x10 +#define STAT_TXADDR_ACK0x18 +#define STAT_TXADDR_NAK0x20 +#define STAT_TXDATA_ACK0x28 +#define STAT_TXDATA_NAK0x30 +#define STAT_LOST_ARB_38 0x38 +#define STAT_RXADDR_ACK0x40 +#define STAT_RXADDR_NAK0x48 +#define STAT_RXDATA_ACK0x50 +#define STAT_RXDATA_NAK0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 +#define STAT_IDLE 0xF8 + +/* TWSI_INT values */ +#define ST_INT 0x01 +#define TS_INT 0x02 +#define CORE_INT 0x04 +#define ST_EN 0x10 +#define TS_EN 0x20 +#define CORE_EN0x40 +#define SDA_OVR0x100 +#define SCL_OVR
[PATCH v2 02/10] i2c-octeon: Support I2C_M_RECV_LEN
From: David Daney If I2C_M_RECV_LEN is set consider the length byte. Signed-off-by: David Daney Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 15 +-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 1f14094..fa4d439 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -392,13 +392,14 @@ static int octeon_i2c_write(struct octeon_i2c *i2c, int target, * @target: Target address * @data: Pointer to the location to store the data * @rlength: Length of the data + * @recv_len: flag for length byte * * The address is sent over the bus, then the data is read. * * Returns 0 on success, otherwise a negative errno. */ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, - u16 *rlength) + u16 *rlength, bool recv_len) { int length = *rlength; int i, result; @@ -438,6 +439,15 @@ static int octeon_i2c_read(struct octeon_i2c *i2c, int target, u8 *data, return result; data[i] = octeon_i2c_read_sw(i2c, SW_TWSI_EOP_TWSI_DATA); + if (recv_len && i == 0) { + if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) { + dev_err(i2c->dev, + "%s: read len > I2C_SMBUS_BLOCK_MAX %d\n", + __func__, data[i]); + return -EIO; + } + length += data[i]; + } } *rlength = length; return 0; @@ -466,7 +476,8 @@ static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, pmsg->len, pmsg->addr, i + 1, num); if (pmsg->flags & I2C_M_RD) ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, - >len); + >len, + pmsg->flags & I2C_M_RECV_LEN); else ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, pmsg->len); -- 1.9.1