* [PATCH 0/4] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2014-12-10 0:54 ` Ray Jui 2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui ` (3 more replies) 2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui ` (7 subsequent siblings) 8 siblings, 4 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Ray Jui (4): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: mach-bcm: Enable I2C support for iProc ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + arch/arm/mach-bcm/Kconfig | 1 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 503 ++++++++++++++++++++ 6 files changed, 571 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui @ 2014-12-10 0:54 ` Ray Jui 2014-12-10 1:27 ` Varka Bhadram 2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui ` (2 subsequent siblings) 3 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding 2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2014-12-10 1:27 ` Varka Bhadram [not found] ` <5487A16C.5000707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Varka Bhadram @ 2014-12-10 1:27 UTC (permalink / raw) To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree Hi, On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote: > Document the I2C device tree binding for Broadcom iProc family of > SoCs > > Signed-off-by: Ray Jui <rjui@broadcom.com> > Reviewed-by: Scott Branden <sbranden@broadcom.com> > --- > .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ > 1 file changed, 37 insertions(+) > create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt > > diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt > new file mode 100644 > index 0000000..81f982c > --- /dev/null > +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt > @@ -0,0 +1,37 @@ > +Broadcom iProc I2C controller > + > +Required properties: > + > +- compatible: > + Must be "brcm,iproc-i2c" > + > +- reg: > + Define the base and range of the I/O address space that contain the iProc > + I2C controller registers > + > +- interrupts: > + Should contain the I2C interrupt > + > +- clock-frequency: > + This is the I2C bus clock. Need to be either 100000 or 400000 > + > +- #address-cells: > + Always 1 (for I2C addresses) > + > +- #size-cells: > + Always 0 > + All the properties defined with two lines of statements. Why cant they be with single line statement, like: compatible: Must be "brcm,iproc-i2c" reg: Define the base and range of the I/O address space that contain the iProc I2C controller registers .... -- Thanks and Regards, Varka Bhadram. ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <5487A16C.5000707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding [not found] ` <5487A16C.5000707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2014-12-10 1:35 ` Ray Jui [not found] ` <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg@mail.gmail.com> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 1:35 UTC (permalink / raw) To: Varka Bhadram, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 12/9/2014 5:27 PM, Varka Bhadram wrote: > Hi, > > On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote: >> Document the I2C device tree binding for Broadcom iProc family of >> SoCs >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> --- >> .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 >> ++++++++++++++++++++ >> 1 file changed, 37 insertions(+) >> create mode 100644 >> Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt >> >> diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt >> b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt >> new file mode 100644 >> index 0000000..81f982c >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt >> @@ -0,0 +1,37 @@ >> +Broadcom iProc I2C controller >> + >> +Required properties: >> + >> +- compatible: >> + Must be "brcm,iproc-i2c" >> + >> +- reg: >> + Define the base and range of the I/O address space that contain >> the iProc >> + I2C controller registers >> + >> +- interrupts: >> + Should contain the I2C interrupt >> + >> +- clock-frequency: >> + This is the I2C bus clock. Need to be either 100000 or 400000 >> + >> +- #address-cells: >> + Always 1 (for I2C addresses) >> + >> +- #size-cells: >> + Always 0 >> + > > All the properties defined with two lines of statements. > > Why cant they be with single line statement, like: > > compatible: Must be "brcm,iproc-i2c" > reg: Define the base and range of the I/O address space that > contain the iProc I2C controller registers > > .... > > I thought making them two lines are more readable (and obviously that's very subjective, :)). But more importantly, it matches the format of other Broadcom iProc/Cygnus devicetree binding documents that are currently in progress of upstreaming. ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg@mail.gmail.com>]
[parent not found: <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding [not found] ` <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> @ 2014-12-10 3:27 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:27 UTC (permalink / raw) To: Varka Bhadram Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org On 12/9/2014 7:12 PM, Varka Bhadram wrote: > > > On Wed, Dec 10, 2014 at 7:05 AM, Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org > <mailto:rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>> wrote: > > > > On 12/9/2014 5:27 PM, Varka Bhadram wrote: > > Hi, > > On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote: > > Document the I2C device tree binding for Broadcom iProc > family of > SoCs > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org > <mailto:rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org > <mailto:sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>> > --- > .../devicetree/bindings/i2c/__brcm,iproc-i2c.txt | 37 > ++++++++++++++++++++ > 1 file changed, 37 insertions(+) > create mode 100644 > Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt > > diff --git > a/Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt > b/Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt > new file mode 100644 > index 0000000..81f982c > --- /dev/null > +++ > b/Documentation/devicetree/__bindings/i2c/brcm,iproc-i2c.__txt > @@ -0,0 +1,37 @@ > +Broadcom iProc I2C controller > + > +Required properties: > + > +- compatible: > + Must be "brcm,iproc-i2c" > + > +- reg: > + Define the base and range of the I/O address space that > contain > the iProc > + I2C controller registers > + > +- interrupts: > + Should contain the I2C interrupt > + > +- clock-frequency: > + This is the I2C bus clock. Need to be either 100000 or > 400000 > + > +- #address-cells: > + Always 1 (for I2C addresses) > + > +- #size-cells: > + Always 0 > + > > > All the properties defined with two lines of statements. > > Why cant they be with single line statement, like: > > compatible: Must be "brcm,iproc-i2c" > reg: Define the base and range of the I/O address space that > contain the iProc I2C controller registers > > .... > > > I thought making them two lines are more readable (and obviously > that's very subjective, :)). But more importantly, it matches the > format of other Broadcom iProc/Cygnus devicetree binding documents > that are currently in progress of upstreaming. > > > But max of the bindings over the kernel follows single line statements. > > -- > Thanks and Regards, > Varka Bhadram. Is it a requirement for these property descriptions to be one line? If not, I prefer to stick with the way it is now. Thanks. ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2014-12-10 0:54 ` Ray Jui 2014-12-10 1:33 ` Varka Bhadram 2014-12-10 0:54 ` [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui 2014-12-10 0:54 ` [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 3 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 503 ++++++++++++++++++++++++++++++++++++ 3 files changed, 513 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c1351d9..8a2eb7e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,15 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 5e6c822..216e7be 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..0e6e603 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIME_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + + void __iomem *base; + struct i2c_msg *msg; + + struct i2c_adapter adapter; + + struct completion done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *dev = data; + u32 status = readl(dev->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, dev->base + IS_OFFSET); + complete_all(&dev->done); + + return IRQ_HANDLED; +} + +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); + + while (readl(dev->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) { + if (time_after(jiffies, timeout)) { + dev_err(dev->device, "wait for bus idle timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(dev->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = (msg->addr << 1) & 0xfe; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_err(dev->device, "lost bus arbitration\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_ADDR: + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_DATA: + dev_err(dev->device, "NAK data\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_TIMEOUT: + dev_err(dev->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_err(dev->device, "unknown error code=%d\n", val); + return -EREMOTEIO; + } + + return -EREMOTEIO; +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(dev->device, + "supported data length is 1 - %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + dev->msg = msg; + ret = __wait_for_bus_idle(dev); + if (ret) + return ret; + + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, dev->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, dev->base + M_TX_OFFSET); + } + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&dev->done); + + /* + * Enable the "start busy" interrupt, which will be triggered after + * the transaction is done + */ + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, dev->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&dev->done, time_left); + + /* disable all interrupts */ + writel(0, dev->base + IE_OFFSET); + + if (!time_left) { + dev_err(dev->device, "transaction times out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + return -EREMOTEIO; + } + + ret = bcm_iproc_i2c_check_status(dev); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(dev->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); + dev_dbg(dev->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); + if (ret) { + dev_err(dev->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", + &bus_speed); + if (ret < 0) { + dev_err(dev->device, "missing clock-frequency property\n"); + return -ENODEV; + } + + switch (bus_speed) { + case 100000: + speed_bit = 0; + break; + case 400000: + speed_bit = 1; + break; + default: + dev_err(dev->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } + + val = readl(dev->base + TIM_CFG_OFFSET); + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; + writel(val, dev->base + TIM_CFG_OFFSET); + + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + /* put controller in reset */ + val = readl(dev->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, dev->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val = readl(dev->base + CFG_OFFSET); + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, dev->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + val = 0; + writel(val, dev->base + IE_OFFSET); + + /* clear all pending interrupts */ + val = readl(dev->base + IS_OFFSET); + writel(val, dev->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, dev->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, dev->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + dev->device = &pdev->dev; + init_completion(&dev->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + dev->base = devm_ioremap_resource(dev->device, res); + if (IS_ERR(dev->base)) + return -ENOMEM; + + ret = bcm_iproc_i2c_init(dev); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(dev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev->device, "no irq resource\n"); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, + IRQF_SHARED, pdev->name, dev); + if (ret) { + dev_err(dev->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(dev->device, "failed to add adapter\n"); + return ret; + } + + dev_info(dev->device, "device registered successfully\n"); + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + bcm_iproc_i2c_disable(dev); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + {.compatible = "brcm,iproc-i2c",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .owner = THIS_MODULE, + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver 2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2014-12-10 1:33 ` Varka Bhadram [not found] ` <5487A2D6.8070901-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Varka Bhadram @ 2014-12-10 1:33 UTC (permalink / raw) To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote: > Add initial support to the Broadcom iProc I2C controller found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports two bus speeds > including standard mode (100kHz) and fast mode (400kHz) > > Signed-off-by: Ray Jui <rjui@broadcom.com> > Reviewed-by: Scott Branden <sbranden@broadcom.com> > --- > drivers/i2c/busses/Kconfig | 9 + > drivers/i2c/busses/Makefile | 1 + > drivers/i2c/busses/i2c-bcm-iproc.c | 503 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 513 insertions(+) > create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > index c1351d9..8a2eb7e 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > @@ -372,6 +372,15 @@ config I2C_BCM2835 > This support is also available as a module. If so, the module > will be called i2c-bcm2835. > > +config I2C_BCM_IPROC > + tristate "Broadcom iProc I2C controller" > + depends on ARCH_BCM_IPROC > + help > + If you say yes to this option, support will be included for the > + Broadcom iProc I2C controller. > + > + If you don't know what to do here, say N. > + > config I2C_BCM_KONA > tristate "BCM Kona I2C adapter" > depends on ARCH_BCM_MOBILE > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > index 5e6c822..216e7be 100644 > --- a/drivers/i2c/busses/Makefile > +++ b/drivers/i2c/busses/Makefile > @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o > obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o > obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o > obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o > +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o > obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o > obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o > obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o > diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c > new file mode 100644 > index 0000000..0e6e603 > --- /dev/null > +++ b/drivers/i2c/busses/i2c-bcm-iproc.c > @@ -0,0 +1,503 @@ > +/* > + * Copyright (C) 2014 Broadcom Corporation > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation version 2. > + * > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any > + * kind, whether express or implied; without even the implied warranty > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/sched.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/platform_device.h> > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > + > +#define CFG_OFFSET 0x00 > +#define CFG_RESET_SHIFT 31 > +#define CFG_EN_SHIFT 30 > +#define CFG_M_RETRY_CNT_SHIFT 16 > +#define CFG_M_RETRY_CNT_MASK 0x0f > + > +#define TIM_CFG_OFFSET 0x04 > +#define TIME_CFG_MODE_400_SHIFT 31 > + > +#define M_FIFO_CTRL_OFFSET 0x0c > +#define M_FIFO_RX_FLUSH_SHIFT 31 > +#define M_FIFO_TX_FLUSH_SHIFT 30 > +#define M_FIFO_RX_CNT_SHIFT 16 > +#define M_FIFO_RX_CNT_MASK 0x7f > +#define M_FIFO_RX_THLD_SHIFT 8 > +#define M_FIFO_RX_THLD_MASK 0x3f > + > +#define M_CMD_OFFSET 0x30 > +#define M_CMD_START_BUSY_SHIFT 31 > +#define M_CMD_STATUS_SHIFT 25 > +#define M_CMD_STATUS_MASK 0x07 > +#define M_CMD_STATUS_SUCCESS 0x0 > +#define M_CMD_STATUS_LOST_ARB 0x1 > +#define M_CMD_STATUS_NACK_ADDR 0x2 > +#define M_CMD_STATUS_NACK_DATA 0x3 > +#define M_CMD_STATUS_TIMEOUT 0x4 > +#define M_CMD_PROTOCOL_SHIFT 9 > +#define M_CMD_PROTOCOL_MASK 0xf > +#define M_CMD_PROTOCOL_BLK_WR 0x7 > +#define M_CMD_PROTOCOL_BLK_RD 0x8 > +#define M_CMD_PEC_SHIFT 8 > +#define M_CMD_RD_CNT_SHIFT 0 > +#define M_CMD_RD_CNT_MASK 0xff > + > +#define IE_OFFSET 0x38 > +#define IE_M_RX_FIFO_FULL_SHIFT 31 > +#define IE_M_RX_THLD_SHIFT 30 > +#define IE_M_START_BUSY_SHIFT 28 > + > +#define IS_OFFSET 0x3c > +#define IS_M_RX_FIFO_FULL_SHIFT 31 > +#define IS_M_RX_THLD_SHIFT 30 > +#define IS_M_START_BUSY_SHIFT 28 > + > +#define M_TX_OFFSET 0x40 > +#define M_TX_WR_STATUS_SHIFT 31 > +#define M_TX_DATA_SHIFT 0 > +#define M_TX_DATA_MASK 0xff > + > +#define M_RX_OFFSET 0x44 > +#define M_RX_STATUS_SHIFT 30 > +#define M_RX_STATUS_MASK 0x03 > +#define M_RX_PEC_ERR_SHIFT 29 > +#define M_RX_DATA_SHIFT 0 > +#define M_RX_DATA_MASK 0xff > + > +#define I2C_TIMEOUT_MESC 100 > +#define M_TX_RX_FIFO_SIZE 64 > + > +enum bus_speed_index { > + I2C_SPD_100K = 0, > + I2C_SPD_400K, > +}; > + > +struct bcm_iproc_i2c_dev { > + struct device *device; > + > + void __iomem *base; > + struct i2c_msg *msg; > + > + struct i2c_adapter adapter; > + > + struct completion done; > +}; > + > +/* > + * Can be expanded in the future if more interrupt status bits are utilized > + */ > +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) > + > +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > +{ > + struct bcm_iproc_i2c_dev *dev = data; > + u32 status = readl(dev->base + IS_OFFSET); > + > + status &= ISR_MASK; > + > + if (!status) > + return IRQ_NONE; > + > + writel(status, dev->base + IS_OFFSET); > + complete_all(&dev->done); > + > + return IRQ_HANDLED; > +} > + > +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) > +{ > + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); > + > + while (readl(dev->base + M_CMD_OFFSET) & > + (1 << M_CMD_START_BUSY_SHIFT)) { > + if (time_after(jiffies, timeout)) { > + dev_err(dev->device, "wait for bus idle timeout\n"); > + return -ETIMEDOUT; > + } > + } > + > + return 0; > +} > + > +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, > + struct i2c_msg *msg, u8 *addr) > +{ Match open parenthesis.. static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, struct i2c_msg *msg, u8 *addr) > + > + if (msg->flags & I2C_M_TEN) { > + dev_err(dev->device, "no support for 10-bit address\n"); > + return -EINVAL; > + } > + > + *addr = (msg->addr << 1) & 0xfe; > + > + if (msg->flags & I2C_M_RD) > + *addr |= 1; > + > + return 0; > +} > + > +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + val = readl(dev->base + M_CMD_OFFSET); > + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; > + > + switch (val) { > + case M_CMD_STATUS_SUCCESS: > + return 0; > + > + case M_CMD_STATUS_LOST_ARB: > + dev_err(dev->device, "lost bus arbitration\n"); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_NACK_ADDR: > + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_NACK_DATA: > + dev_err(dev->device, "NAK data\n"); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_TIMEOUT: > + dev_err(dev->device, "bus timeout\n"); > + return -ETIMEDOUT; > + > + default: > + dev_err(dev->device, "unknown error code=%d\n", val); > + return -EREMOTEIO; > + } > + > + return -EREMOTEIO; > +} > + > +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, > + struct i2c_msg *msg) > +{ dto... > + int ret, i; > + u8 addr; > + u32 val; > + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); > + > + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { > + dev_err(dev->device, > + "supported data length is 1 - %u bytes\n", > + M_TX_RX_FIFO_SIZE - 1); > + return -EINVAL; > + } > + > + dev->msg = msg; > + ret = __wait_for_bus_idle(dev); > + if (ret) > + return ret; > + > + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); > + if (ret) > + return ret; > + > + /* load slave address into the TX FIFO */ > + writel(addr, dev->base + M_TX_OFFSET); > + > + /* for a write transaction, load data into the TX FIFO */ > + if (!(msg->flags & I2C_M_RD)) { > + for (i = 0; i < msg->len; i++) { > + val = msg->buf[i]; > + > + /* mark the last byte */ > + if (i == msg->len - 1) > + val |= 1 << M_TX_WR_STATUS_SHIFT; > + > + writel(val, dev->base + M_TX_OFFSET); > + } > + } > + > + /* mark as incomplete before starting the transaction */ > + reinit_completion(&dev->done); > + > + /* > + * Enable the "start busy" interrupt, which will be triggered after > + * the transaction is done > + */ > + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); > + > + /* > + * Now we can activate the transfer. For a read operation, specify the > + * number of bytes to read > + */ > + val = 1 << M_CMD_START_BUSY_SHIFT; > + if (msg->flags & I2C_M_RD) { > + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > + (msg->len << M_CMD_RD_CNT_SHIFT); > + } else { > + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > + } > + writel(val, dev->base + M_CMD_OFFSET); > + > + time_left = wait_for_completion_timeout(&dev->done, time_left); > + > + /* disable all interrupts */ > + writel(0, dev->base + IE_OFFSET); > + > + if (!time_left) { > + dev_err(dev->device, "transaction times out\n"); > + > + /* flush FIFOs */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > + (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, dev->base + M_FIFO_CTRL_OFFSET); > + return -EREMOTEIO; > + } > + > + ret = bcm_iproc_i2c_check_status(dev); > + if (ret) { > + /* flush both TX/RX FIFOs */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > + (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, dev->base + M_FIFO_CTRL_OFFSET); > + return ret; > + } > + > + /* > + * For a read operation, we now need to load the data from FIFO > + * into the memory buffer > + */ > + if (msg->flags & I2C_M_RD) { > + for (i = 0; i < msg->len; i++) { > + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> > + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; > + } > + } > + > + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", > + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, > + msg->len); > + dev_dbg(dev->device, "**** data start ****\n"); > + for (i = 0; i < msg->len; i++) > + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); > + dev_dbg(dev->device, "**** data end ****\n"); > + > + return 0; > +} > + > +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, > + struct i2c_msg msgs[], int num) > +{ > + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); > + int ret, i; > + > + /* go through all messages */ > + for (i = 0; i < num; i++) { > + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); > + if (ret) { > + dev_err(dev->device, "xfer failed\n"); > + return ret; > + } > + } > + > + return num; > +} > + > +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) > +{ > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; > +} > + > +static const struct i2c_algorithm bcm_iproc_algo = { > + .master_xfer = bcm_iproc_i2c_xfer, > + .functionality = bcm_iproc_i2c_functionality, > +}; > + > +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) > +{ > + unsigned int bus_speed, speed_bit; > + u32 val; > + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", > + &bus_speed); > + if (ret < 0) { > + dev_err(dev->device, "missing clock-frequency property\n"); > + return -ENODEV; > + } > + > + switch (bus_speed) { > + case 100000: > + speed_bit = 0; > + break; > + case 400000: > + speed_bit = 1; > + break; > + default: > + dev_err(dev->device, "%d Hz bus speed not supported\n", > + bus_speed); > + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); > + return -EINVAL; > + } > + > + val = readl(dev->base + TIM_CFG_OFFSET); > + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); > + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; > + writel(val, dev->base + TIM_CFG_OFFSET); > + > + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); > + > + return 0; > +} > + > +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + /* put controller in reset */ > + val = readl(dev->base + CFG_OFFSET); > + val |= 1 << CFG_RESET_SHIFT; > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, dev->base + CFG_OFFSET); > + > + /* wait 100 usec per spec */ > + udelay(100); > + > + /* bring controller out of reset */ > + val = readl(dev->base + CFG_OFFSET); > + val &= ~(1 << CFG_RESET_SHIFT); > + writel(val, dev->base + CFG_OFFSET); > + > + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, dev->base + M_FIFO_CTRL_OFFSET); > + > + /* disable all interrupts */ > + val = 0; > + writel(val, dev->base + IE_OFFSET); > + > + /* clear all pending interrupts */ > + val = readl(dev->base + IS_OFFSET); > + writel(val, dev->base + IS_OFFSET); > + > + return 0; > +} > + > +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + val = readl(dev->base + CFG_OFFSET); > + val |= 1 << CFG_EN_SHIFT; > + writel(val, dev->base + CFG_OFFSET); > +} > + > +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + val = readl(dev->base + CFG_OFFSET); > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, dev->base + CFG_OFFSET); > +} > + > +static int bcm_iproc_i2c_probe(struct platform_device *pdev) > +{ > + int irq, ret = 0; > + struct bcm_iproc_i2c_dev *dev; > + struct i2c_adapter *adap; > + struct resource *res; > + > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, dev); > + dev->device = &pdev->dev; > + init_completion(&dev->done); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; We can remove this resource check. This checking will happen with devm_ioremap_resource() > + dev->base = devm_ioremap_resource(dev->device, res); > + if (IS_ERR(dev->base)) > + return -ENOMEM; > + > + ret = bcm_iproc_i2c_init(dev); > + if (ret) > + return ret; > + > + ret = bcm_iproc_i2c_cfg_speed(dev); > + if (ret) > + return ret; > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(dev->device, "no irq resource\n"); > + return irq; > + } > + > + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, > + IRQF_SHARED, pdev->name, dev); > + if (ret) { > + dev_err(dev->device, "unable to request irq %i\n", irq); > + return ret; > + } > + > + bcm_iproc_i2c_enable(dev); > + > + adap = &dev->adapter; > + i2c_set_adapdata(adap, dev); > + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); > + adap->algo = &bcm_iproc_algo; > + adap->dev.parent = &pdev->dev; > + adap->dev.of_node = pdev->dev.of_node; > + > + ret = i2c_add_adapter(adap); > + if (ret) { > + dev_err(dev->device, "failed to add adapter\n"); > + return ret; > + } > + > + dev_info(dev->device, "device registered successfully\n"); > + > + return 0; > +} > + > +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > +{ > + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); > + > + i2c_del_adapter(&dev->adapter); > + bcm_iproc_i2c_disable(dev); > + > + return 0; > +} > + > +static const struct of_device_id bcm_iproc_i2c_of_match[] = { > + {.compatible = "brcm,iproc-i2c",}, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); > + > +static struct platform_driver bcm_iproc_i2c_driver = { > + .driver = { > + .name = "bcm-iproc-i2c", > + .owner = THIS_MODULE, No need to update this field. Its updated by module_platform_driver(). > + .of_match_table = bcm_iproc_i2c_of_match, > + }, > + .probe = bcm_iproc_i2c_probe, > + .remove = bcm_iproc_i2c_remove, > +}; > +module_platform_driver(bcm_iproc_i2c_driver); > + > +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); > +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); > +MODULE_LICENSE("GPL v2"); -- Thanks and Regards, Varka Bhadram. ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <5487A2D6.8070901-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <5487A2D6.8070901-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2014-12-10 1:41 ` Ray Jui [not found] ` <CAEUmHyZ+VqjzL4LkQozGJtnictPNXHYWM2qMKvD=LmfQdcT8iQ@mail.gmail.com> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 1:41 UTC (permalink / raw) To: Varka Bhadram, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 12/9/2014 5:33 PM, Varka Bhadram wrote: > > On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote: >> Add initial support to the Broadcom iProc I2C controller found in the >> iProc family of SoCs. >> >> The iProc I2C controller has separate internal TX and RX FIFOs, each has >> a size of 64 bytes. The iProc I2C controller supports two bus speeds >> including standard mode (100kHz) and fast mode (400kHz) >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> --- >> drivers/i2c/busses/Kconfig | 9 + >> drivers/i2c/busses/Makefile | 1 + >> drivers/i2c/busses/i2c-bcm-iproc.c | 503 >> ++++++++++++++++++++++++++++++++++++ >> 3 files changed, 513 insertions(+) >> create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c >> >> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig >> index c1351d9..8a2eb7e 100644 >> --- a/drivers/i2c/busses/Kconfig >> +++ b/drivers/i2c/busses/Kconfig >> @@ -372,6 +372,15 @@ config I2C_BCM2835 >> This support is also available as a module. If so, the module >> will be called i2c-bcm2835. >> +config I2C_BCM_IPROC >> + tristate "Broadcom iProc I2C controller" >> + depends on ARCH_BCM_IPROC >> + help >> + If you say yes to this option, support will be included for the >> + Broadcom iProc I2C controller. >> + >> + If you don't know what to do here, say N. >> + >> config I2C_BCM_KONA >> tristate "BCM Kona I2C adapter" >> depends on ARCH_BCM_MOBILE >> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile >> index 5e6c822..216e7be 100644 >> --- a/drivers/i2c/busses/Makefile >> +++ b/drivers/i2c/busses/Makefile >> @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o >> obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o >> obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o >> obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o >> +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o >> obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o >> obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o >> obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o >> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c >> b/drivers/i2c/busses/i2c-bcm-iproc.c >> new file mode 100644 >> index 0000000..0e6e603 >> --- /dev/null >> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c >> @@ -0,0 +1,503 @@ >> +/* >> + * Copyright (C) 2014 Broadcom Corporation >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License as >> + * published by the Free Software Foundation version 2. >> + * >> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any >> + * kind, whether express or implied; without even the implied warranty >> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include <linux/device.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/sched.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/platform_device.h> >> +#include <linux/clk.h> >> +#include <linux/io.h> >> +#include <linux/slab.h> >> +#include <linux/delay.h> >> + >> +#define CFG_OFFSET 0x00 >> +#define CFG_RESET_SHIFT 31 >> +#define CFG_EN_SHIFT 30 >> +#define CFG_M_RETRY_CNT_SHIFT 16 >> +#define CFG_M_RETRY_CNT_MASK 0x0f >> + >> +#define TIM_CFG_OFFSET 0x04 >> +#define TIME_CFG_MODE_400_SHIFT 31 >> + >> +#define M_FIFO_CTRL_OFFSET 0x0c >> +#define M_FIFO_RX_FLUSH_SHIFT 31 >> +#define M_FIFO_TX_FLUSH_SHIFT 30 >> +#define M_FIFO_RX_CNT_SHIFT 16 >> +#define M_FIFO_RX_CNT_MASK 0x7f >> +#define M_FIFO_RX_THLD_SHIFT 8 >> +#define M_FIFO_RX_THLD_MASK 0x3f >> + >> +#define M_CMD_OFFSET 0x30 >> +#define M_CMD_START_BUSY_SHIFT 31 >> +#define M_CMD_STATUS_SHIFT 25 >> +#define M_CMD_STATUS_MASK 0x07 >> +#define M_CMD_STATUS_SUCCESS 0x0 >> +#define M_CMD_STATUS_LOST_ARB 0x1 >> +#define M_CMD_STATUS_NACK_ADDR 0x2 >> +#define M_CMD_STATUS_NACK_DATA 0x3 >> +#define M_CMD_STATUS_TIMEOUT 0x4 >> +#define M_CMD_PROTOCOL_SHIFT 9 >> +#define M_CMD_PROTOCOL_MASK 0xf >> +#define M_CMD_PROTOCOL_BLK_WR 0x7 >> +#define M_CMD_PROTOCOL_BLK_RD 0x8 >> +#define M_CMD_PEC_SHIFT 8 >> +#define M_CMD_RD_CNT_SHIFT 0 >> +#define M_CMD_RD_CNT_MASK 0xff >> + >> +#define IE_OFFSET 0x38 >> +#define IE_M_RX_FIFO_FULL_SHIFT 31 >> +#define IE_M_RX_THLD_SHIFT 30 >> +#define IE_M_START_BUSY_SHIFT 28 >> + >> +#define IS_OFFSET 0x3c >> +#define IS_M_RX_FIFO_FULL_SHIFT 31 >> +#define IS_M_RX_THLD_SHIFT 30 >> +#define IS_M_START_BUSY_SHIFT 28 >> + >> +#define M_TX_OFFSET 0x40 >> +#define M_TX_WR_STATUS_SHIFT 31 >> +#define M_TX_DATA_SHIFT 0 >> +#define M_TX_DATA_MASK 0xff >> + >> +#define M_RX_OFFSET 0x44 >> +#define M_RX_STATUS_SHIFT 30 >> +#define M_RX_STATUS_MASK 0x03 >> +#define M_RX_PEC_ERR_SHIFT 29 >> +#define M_RX_DATA_SHIFT 0 >> +#define M_RX_DATA_MASK 0xff >> + >> +#define I2C_TIMEOUT_MESC 100 >> +#define M_TX_RX_FIFO_SIZE 64 >> + >> +enum bus_speed_index { >> + I2C_SPD_100K = 0, >> + I2C_SPD_400K, >> +}; >> + >> +struct bcm_iproc_i2c_dev { >> + struct device *device; >> + >> + void __iomem *base; >> + struct i2c_msg *msg; >> + >> + struct i2c_adapter adapter; >> + >> + struct completion done; >> +}; >> + >> +/* >> + * Can be expanded in the future if more interrupt status bits are >> utilized >> + */ >> +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) >> + >> +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) >> +{ >> + struct bcm_iproc_i2c_dev *dev = data; >> + u32 status = readl(dev->base + IS_OFFSET); >> + >> + status &= ISR_MASK; >> + >> + if (!status) >> + return IRQ_NONE; >> + >> + writel(status, dev->base + IS_OFFSET); >> + complete_all(&dev->done); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) >> +{ >> + unsigned long timeout = jiffies + >> msecs_to_jiffies(I2C_TIMEOUT_MESC); >> + >> + while (readl(dev->base + M_CMD_OFFSET) & >> + (1 << M_CMD_START_BUSY_SHIFT)) { >> + if (time_after(jiffies, timeout)) { >> + dev_err(dev->device, "wait for bus idle timeout\n"); >> + return -ETIMEDOUT; >> + } >> + } >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, >> + struct i2c_msg *msg, u8 *addr) >> +{ > > Match open parenthesis.. > > static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, > struct i2c_msg *msg, u8 *addr) > > Okay I can make this change. >> + >> + if (msg->flags & I2C_M_TEN) { >> + dev_err(dev->device, "no support for 10-bit address\n"); >> + return -EINVAL; >> + } >> + >> + *addr = (msg->addr << 1) & 0xfe; >> + >> + if (msg->flags & I2C_M_RD) >> + *addr |= 1; >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + val = readl(dev->base + M_CMD_OFFSET); >> + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; >> + >> + switch (val) { >> + case M_CMD_STATUS_SUCCESS: >> + return 0; >> + >> + case M_CMD_STATUS_LOST_ARB: >> + dev_err(dev->device, "lost bus arbitration\n"); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_NACK_ADDR: >> + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_NACK_DATA: >> + dev_err(dev->device, "NAK data\n"); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_TIMEOUT: >> + dev_err(dev->device, "bus timeout\n"); >> + return -ETIMEDOUT; >> + >> + default: >> + dev_err(dev->device, "unknown error code=%d\n", val); >> + return -EREMOTEIO; >> + } >> + >> + return -EREMOTEIO; >> +} >> + >> +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, >> + struct i2c_msg *msg) >> +{ > > dto... > One more indent? Sure. >> + int ret, i; >> + u8 addr; >> + u32 val; >> + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); >> + >> + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { >> + dev_err(dev->device, >> + "supported data length is 1 - %u bytes\n", >> + M_TX_RX_FIFO_SIZE - 1); >> + return -EINVAL; >> + } >> + >> + dev->msg = msg; >> + ret = __wait_for_bus_idle(dev); >> + if (ret) >> + return ret; >> + >> + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); >> + if (ret) >> + return ret; >> + >> + /* load slave address into the TX FIFO */ >> + writel(addr, dev->base + M_TX_OFFSET); >> + >> + /* for a write transaction, load data into the TX FIFO */ >> + if (!(msg->flags & I2C_M_RD)) { >> + for (i = 0; i < msg->len; i++) { >> + val = msg->buf[i]; >> + >> + /* mark the last byte */ >> + if (i == msg->len - 1) >> + val |= 1 << M_TX_WR_STATUS_SHIFT; >> + >> + writel(val, dev->base + M_TX_OFFSET); >> + } >> + } >> + >> + /* mark as incomplete before starting the transaction */ >> + reinit_completion(&dev->done); >> + >> + /* >> + * Enable the "start busy" interrupt, which will be triggered after >> + * the transaction is done >> + */ >> + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); >> + >> + /* >> + * Now we can activate the transfer. For a read operation, >> specify the >> + * number of bytes to read >> + */ >> + val = 1 << M_CMD_START_BUSY_SHIFT; >> + if (msg->flags & I2C_M_RD) { >> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >> + (msg->len << M_CMD_RD_CNT_SHIFT); >> + } else { >> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >> + } >> + writel(val, dev->base + M_CMD_OFFSET); >> + >> + time_left = wait_for_completion_timeout(&dev->done, time_left); >> + >> + /* disable all interrupts */ >> + writel(0, dev->base + IE_OFFSET); >> + >> + if (!time_left) { >> + dev_err(dev->device, "transaction times out\n"); >> + >> + /* flush FIFOs */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> + (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, dev->base + M_FIFO_CTRL_OFFSET); >> + return -EREMOTEIO; >> + } >> + >> + ret = bcm_iproc_i2c_check_status(dev); >> + if (ret) { >> + /* flush both TX/RX FIFOs */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> + (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, dev->base + M_FIFO_CTRL_OFFSET); >> + return ret; >> + } >> + >> + /* >> + * For a read operation, we now need to load the data from FIFO >> + * into the memory buffer >> + */ >> + if (msg->flags & I2C_M_RD) { >> + for (i = 0; i < msg->len; i++) { >> + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; >> + } >> + } >> + >> + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", >> + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, >> + msg->len); >> + dev_dbg(dev->device, "**** data start ****\n"); >> + for (i = 0; i < msg->len; i++) >> + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); >> + dev_dbg(dev->device, "**** data end ****\n"); >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, >> + struct i2c_msg msgs[], int num) >> +{ >> + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); >> + int ret, i; >> + >> + /* go through all messages */ >> + for (i = 0; i < num; i++) { >> + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); >> + if (ret) { >> + dev_err(dev->device, "xfer failed\n"); >> + return ret; >> + } >> + } >> + >> + return num; >> +} >> + >> +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) >> +{ >> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; >> +} >> + >> +static const struct i2c_algorithm bcm_iproc_algo = { >> + .master_xfer = bcm_iproc_i2c_xfer, >> + .functionality = bcm_iproc_i2c_functionality, >> +}; >> + >> +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) >> +{ >> + unsigned int bus_speed, speed_bit; >> + u32 val; >> + int ret = of_property_read_u32(dev->device->of_node, >> "clock-frequency", >> + &bus_speed); >> + if (ret < 0) { >> + dev_err(dev->device, "missing clock-frequency property\n"); >> + return -ENODEV; >> + } >> + >> + switch (bus_speed) { >> + case 100000: >> + speed_bit = 0; >> + break; >> + case 400000: >> + speed_bit = 1; >> + break; >> + default: >> + dev_err(dev->device, "%d Hz bus speed not supported\n", >> + bus_speed); >> + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); >> + return -EINVAL; >> + } >> + >> + val = readl(dev->base + TIM_CFG_OFFSET); >> + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); >> + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; >> + writel(val, dev->base + TIM_CFG_OFFSET); >> + >> + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + /* put controller in reset */ >> + val = readl(dev->base + CFG_OFFSET); >> + val |= 1 << CFG_RESET_SHIFT; >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, dev->base + CFG_OFFSET); >> + >> + /* wait 100 usec per spec */ >> + udelay(100); >> + >> + /* bring controller out of reset */ >> + val = readl(dev->base + CFG_OFFSET); >> + val &= ~(1 << CFG_RESET_SHIFT); >> + writel(val, dev->base + CFG_OFFSET); >> + >> + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, dev->base + M_FIFO_CTRL_OFFSET); >> + >> + /* disable all interrupts */ >> + val = 0; >> + writel(val, dev->base + IE_OFFSET); >> + >> + /* clear all pending interrupts */ >> + val = readl(dev->base + IS_OFFSET); >> + writel(val, dev->base + IS_OFFSET); >> + >> + return 0; >> +} >> + >> +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + val = readl(dev->base + CFG_OFFSET); >> + val |= 1 << CFG_EN_SHIFT; >> + writel(val, dev->base + CFG_OFFSET); >> +} >> + >> +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + val = readl(dev->base + CFG_OFFSET); >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, dev->base + CFG_OFFSET); >> +} >> + >> +static int bcm_iproc_i2c_probe(struct platform_device *pdev) >> +{ >> + int irq, ret = 0; >> + struct bcm_iproc_i2c_dev *dev; >> + struct i2c_adapter *adap; >> + struct resource *res; >> + >> + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); >> + if (!dev) >> + return -ENOMEM; >> + >> + platform_set_drvdata(pdev, dev); >> + dev->device = &pdev->dev; >> + init_completion(&dev->done); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + if (!res) >> + return -ENODEV; > > We can remove this resource check. This checking will happen with > devm_ioremap_resource() > Don't you need to obtain a valid resource and pass it into devm_ioremap_resource? Without 'res' being assigned a valid resource, devm_ioremap_resource will reject with "invalid resource". >> + dev->base = devm_ioremap_resource(dev->device, res); >> + if (IS_ERR(dev->base)) >> + return -ENOMEM; >> + >> + ret = bcm_iproc_i2c_init(dev); >> + if (ret) >> + return ret; >> + >> + ret = bcm_iproc_i2c_cfg_speed(dev); >> + if (ret) >> + return ret; >> + >> + irq = platform_get_irq(pdev, 0); >> + if (irq < 0) { >> + dev_err(dev->device, "no irq resource\n"); >> + return irq; >> + } >> + >> + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, >> + IRQF_SHARED, pdev->name, dev); >> + if (ret) { >> + dev_err(dev->device, "unable to request irq %i\n", irq); >> + return ret; >> + } >> + >> + bcm_iproc_i2c_enable(dev); >> + >> + adap = &dev->adapter; >> + i2c_set_adapdata(adap, dev); >> + strlcpy(adap->name, "Broadcom iProc I2C adapter", >> sizeof(adap->name)); >> + adap->algo = &bcm_iproc_algo; >> + adap->dev.parent = &pdev->dev; >> + adap->dev.of_node = pdev->dev.of_node; >> + >> + ret = i2c_add_adapter(adap); >> + if (ret) { >> + dev_err(dev->device, "failed to add adapter\n"); >> + return ret; >> + } >> + >> + dev_info(dev->device, "device registered successfully\n"); >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >> +{ >> + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); >> + >> + i2c_del_adapter(&dev->adapter); >> + bcm_iproc_i2c_disable(dev); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id bcm_iproc_i2c_of_match[] = { >> + {.compatible = "brcm,iproc-i2c",}, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); >> + >> +static struct platform_driver bcm_iproc_i2c_driver = { >> + .driver = { >> + .name = "bcm-iproc-i2c", >> + .owner = THIS_MODULE, > > No need to update this field. Its updated by module_platform_driver(). > Okay will get rid of .owner = THIS_MODULES, >> + .of_match_table = bcm_iproc_i2c_of_match, >> + }, >> + .probe = bcm_iproc_i2c_probe, >> + .remove = bcm_iproc_i2c_remove, >> +}; >> +module_platform_driver(bcm_iproc_i2c_driver); >> + >> +MODULE_AUTHOR("Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>"); >> +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); >> +MODULE_LICENSE("GPL v2"); > ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <CAEUmHyZ+VqjzL4LkQozGJtnictPNXHYWM2qMKvD=LmfQdcT8iQ@mail.gmail.com>]
[parent not found: <CAEUmHyZ86r=7KzJzfE9_upv45vN7geW9woqMkaGaBPwfp3xbMQ@mail.gmail.com>]
[parent not found: <CAEUmHyZ86r=7KzJzfE9_upv45vN7geW9woqMkaGaBPwfp3xbMQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <CAEUmHyZ86r=7KzJzfE9_upv45vN7geW9woqMkaGaBPwfp3xbMQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> @ 2014-12-10 3:31 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:31 UTC (permalink / raw) To: Varka Bhadram Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org On 12/9/2014 7:28 PM, Varka Bhadram wrote: > > > On Wed, Dec 10, 2014 at 8:51 AM, Varka Bhadram <varkabhadram-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org > <mailto:varkabhadram-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>> wrote: > > On Wed, Dec 10, 2014 at 7:11 AM, Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org > <mailto:rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>> wrote: > > > > On 12/9/2014 5:33 PM, Varka Bhadram wrote: > > > On Wednesday 10 December 2014 06:24 AM, Ray Jui wrote: > > Add initial support to the Broadcom iProc I2C controller > found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX > FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports > two bus speeds > including standard mode (100kHz) and fast mode (400kHz) > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org > <mailto:rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org > <mailto:sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>> > --- > drivers/i2c/busses/Kconfig | 9 + > drivers/i2c/busses/Makefile | 1 + > drivers/i2c/busses/i2c-bcm-iproc.c | 503 > ++++++++++++++++++++++++++++++++++++ > 3 files changed, 513 insertions(+) > create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c > > (...) > > +static int bcm_iproc_i2c_probe(struct platform_device > *pdev) > +{ > + int irq, ret = 0; > + struct bcm_iproc_i2c_dev *dev; > + struct i2c_adapter *adap; > + struct resource *res; > + > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), > GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, dev); > + dev->device = &pdev->dev; > + init_completion(&dev->done); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENODEV; > > > We can remove this resource check. This checking will happen > with > devm_ioremap_resource() > > Don't you need to obtain a valid resource and pass it into > devm_ioremap_resource? Without 'res' being assigned a valid > resource, devm_ioremap_resource will reject with "invalid resource". > > platform_get_resource() will return a resource, checking on this > resource is happening at > http://lxr.free-electrons.com/source/lib/devres.c#L115. So no need > to check it explicitly. > > If you check here it will be duplication of check with resource. Two > times we are checking on > the resource. No point of doing like that. > > Thanks. Sorry I misunderstood what you meant. Okay I'll get rid of if (!res) check there. Thanks. > > > See this: http://lxr.free-electrons.com/source/lib/devres.c#L102 > > -- > Thanks and Regards, > Varka Bhadram. ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2014-12-10 0:54 ` Ray Jui 2014-12-10 0:54 ` [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Enable I2C driver support for Broadcom iProc family of SoCs by selecting I2C_BCM_IPROC Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/mach-bcm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index aaeec78..86ee90b 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC select ARCH_REQUIRE_GPIOLIB select ARM_AMBA select PINCTRL + select I2C_BCM_IPROC help This enables support for systems based on Broadcom IPROC architected SoCs. The IPROC complex contains one or more ARM CPUs along with common -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui ` (2 preceding siblings ...) 2014-12-10 0:54 ` [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui @ 2014-12-10 0:54 ` Ray Jui 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 0:54 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..f7d6c1d 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 0/4] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui @ 2014-12-10 2:18 ` Ray Jui 2014-12-10 2:18 ` [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui ` (3 more replies) 2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui ` (6 subsequent siblings) 8 siblings, 4 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (4): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: mach-bcm: Enable I2C support for iProc ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + arch/arm/mach-bcm/Kconfig | 1 + drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 502 ++++++++++++++++++++ 6 files changed, 570 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding 2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui @ 2014-12-10 2:18 ` Ray Jui 2014-12-10 2:18 ` [PATCH v2 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui ` (2 subsequent siblings) 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v2 2/4] i2c: iproc: Add Broadcom iProc I2C Driver 2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 2:18 ` [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2014-12-10 2:18 ` Ray Jui [not found] ` <1418177893-22094-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 2:18 ` [PATCH v2 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- drivers/i2c/busses/Kconfig | 9 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 502 ++++++++++++++++++++++++++++++++++++ 3 files changed, 512 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c1351d9..8a2eb7e 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,15 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 5e6c822..216e7be 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..57f5f29 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIME_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + + void __iomem *base; + struct i2c_msg *msg; + + struct i2c_adapter adapter; + + struct completion done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *dev = data; + u32 status = readl(dev->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, dev->base + IS_OFFSET); + complete_all(&dev->done); + + return IRQ_HANDLED; +} + +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); + + while (readl(dev->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) { + if (time_after(jiffies, timeout)) { + dev_err(dev->device, "wait for bus idle timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(dev->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = (msg->addr << 1) & 0xfe; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_err(dev->device, "lost bus arbitration\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_ADDR: + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_DATA: + dev_err(dev->device, "NAK data\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_TIMEOUT: + dev_err(dev->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_err(dev->device, "unknown error code=%d\n", val); + return -EREMOTEIO; + } + + return -EREMOTEIO; +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(dev->device, + "supported data length is 1 - %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + dev->msg = msg; + ret = __wait_for_bus_idle(dev); + if (ret) + return ret; + + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, dev->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, dev->base + M_TX_OFFSET); + } + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&dev->done); + + /* + * Enable the "start busy" interrupt, which will be triggered after + * the transaction is done + */ + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, dev->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&dev->done, time_left); + + /* disable all interrupts */ + writel(0, dev->base + IE_OFFSET); + + if (!time_left) { + dev_err(dev->device, "transaction times out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + return -EREMOTEIO; + } + + ret = bcm_iproc_i2c_check_status(dev); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(dev->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); + dev_dbg(dev->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); + if (ret) { + dev_err(dev->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", + &bus_speed); + if (ret < 0) { + dev_err(dev->device, "missing clock-frequency property\n"); + return -ENODEV; + } + + switch (bus_speed) { + case 100000: + speed_bit = 0; + break; + case 400000: + speed_bit = 1; + break; + default: + dev_err(dev->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } + + val = readl(dev->base + TIM_CFG_OFFSET); + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; + writel(val, dev->base + TIM_CFG_OFFSET); + + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + /* put controller in reset */ + val = readl(dev->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, dev->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val = readl(dev->base + CFG_OFFSET); + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, dev->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + val = 0; + writel(val, dev->base + IE_OFFSET); + + /* clear all pending interrupts */ + val = readl(dev->base + IS_OFFSET); + writel(val, dev->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, dev->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, dev->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + dev->device = &pdev->dev; + init_completion(&dev->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + dev->base = devm_ioremap_resource(dev->device, res); + if (IS_ERR(dev->base)) + return -ENOMEM; + + ret = bcm_iproc_i2c_init(dev); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(dev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev->device, "no irq resource\n"); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, + IRQF_SHARED, pdev->name, dev); + if (ret) { + dev_err(dev->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(dev->device, "failed to add adapter\n"); + return ret; + } + + dev_info(dev->device, "device registered successfully\n"); + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + bcm_iproc_i2c_disable(dev); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + {.compatible = "brcm,iproc-i2c",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1418177893-22094-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc [not found] ` <1418177893-22094-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2014-12-10 2:18 ` Ray Jui [not found] ` <1418177893-22094-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Enable I2C driver support for Broadcom iProc family of SoCs by selecting I2C_BCM_IPROC Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> --- arch/arm/mach-bcm/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig index aaeec78..86ee90b 100644 --- a/arch/arm/mach-bcm/Kconfig +++ b/arch/arm/mach-bcm/Kconfig @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC select ARCH_REQUIRE_GPIOLIB select ARM_AMBA select PINCTRL + select I2C_BCM_IPROC help This enables support for systems based on Broadcom IPROC architected SoCs. The IPROC complex contains one or more ARM CPUs along with common -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1418177893-22094-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc [not found] ` <1418177893-22094-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2014-12-10 2:20 ` Florian Fainelli [not found] ` <5487ADE5.4070705-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Florian Fainelli @ 2014-12-10 2:20 UTC (permalink / raw) To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 09/12/14 18:18, Ray Jui wrote: > Enable I2C driver support for Broadcom iProc family of SoCs by > selecting I2C_BCM_IPROC > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > --- > arch/arm/mach-bcm/Kconfig | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig > index aaeec78..86ee90b 100644 > --- a/arch/arm/mach-bcm/Kconfig > +++ b/arch/arm/mach-bcm/Kconfig > @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC > select ARCH_REQUIRE_GPIOLIB > select ARM_AMBA > select PINCTRL > + select I2C_BCM_IPROC One way to avoid having to modify mach-bcm/Kconfig would be to have your i2c driver Kconfig do this: default ARCH_BCM_IPROC would that work? > help > This enables support for systems based on Broadcom IPROC architected SoCs. > The IPROC complex contains one or more ARM CPUs along with common > ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <5487ADE5.4070705-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc [not found] ` <5487ADE5.4070705-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2014-12-10 2:24 ` Ray Jui [not found] ` <5487AEF0.5010404-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 2:24 UTC (permalink / raw) To: Florian Fainelli, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 12/9/2014 6:20 PM, Florian Fainelli wrote: > On 09/12/14 18:18, Ray Jui wrote: >> Enable I2C driver support for Broadcom iProc family of SoCs by >> selecting I2C_BCM_IPROC >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> --- >> arch/arm/mach-bcm/Kconfig | 1 + >> 1 file changed, 1 insertion(+) >> >> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig >> index aaeec78..86ee90b 100644 >> --- a/arch/arm/mach-bcm/Kconfig >> +++ b/arch/arm/mach-bcm/Kconfig >> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC >> select ARCH_REQUIRE_GPIOLIB >> select ARM_AMBA >> select PINCTRL >> + select I2C_BCM_IPROC > > One way to avoid having to modify mach-bcm/Kconfig would be to have your > i2c driver Kconfig do this: > > default ARCH_BCM_IPROC > > would that work? > Yes. So in which case it is better to select a driver from the architecture specific Kconfig? >> help >> This enables support for systems based on Broadcom IPROC architected SoCs. >> The IPROC complex contains one or more ARM CPUs along with common >> > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <5487AEF0.5010404-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc [not found] ` <5487AEF0.5010404-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2014-12-10 3:20 ` Florian Fainelli [not found] ` <5487BBE0.4000701-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Florian Fainelli @ 2014-12-10 3:20 UTC (permalink / raw) To: Ray Jui, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 09/12/14 18:24, Ray Jui wrote: > > > On 12/9/2014 6:20 PM, Florian Fainelli wrote: >> On 09/12/14 18:18, Ray Jui wrote: >>> Enable I2C driver support for Broadcom iProc family of SoCs by >>> selecting I2C_BCM_IPROC >>> >>> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>> --- >>> arch/arm/mach-bcm/Kconfig | 1 + >>> 1 file changed, 1 insertion(+) >>> >>> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig >>> index aaeec78..86ee90b 100644 >>> --- a/arch/arm/mach-bcm/Kconfig >>> +++ b/arch/arm/mach-bcm/Kconfig >>> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC >>> select ARCH_REQUIRE_GPIOLIB >>> select ARM_AMBA >>> select PINCTRL >>> + select I2C_BCM_IPROC >> >> One way to avoid having to modify mach-bcm/Kconfig would be to have your >> i2c driver Kconfig do this: >> >> default ARCH_BCM_IPROC >> >> would that work? >> > Yes. So in which case it is better to select a driver from the > architecture specific Kconfig? I suppose if your driver/subsystem is critical for system boot, like powering a regulator or something that has a critical purpose, a select is probably more appropriate here. If this is just exposing non-critical devices, I would go with a depends on/default at the driver Kconfig level. This is just how I see things, others would definitively have a different view. > >>> help >>> This enables support for systems based on Broadcom IPROC >>> architected SoCs. >>> The IPROC complex contains one or more ARM CPUs along with >>> common >>> >> ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <5487BBE0.4000701-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc [not found] ` <5487BBE0.4000701-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2014-12-10 3:58 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:58 UTC (permalink / raw) To: Florian Fainelli, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 12/9/2014 7:20 PM, Florian Fainelli wrote: > On 09/12/14 18:24, Ray Jui wrote: >> >> >> On 12/9/2014 6:20 PM, Florian Fainelli wrote: >>> On 09/12/14 18:18, Ray Jui wrote: >>>> Enable I2C driver support for Broadcom iProc family of SoCs by >>>> selecting I2C_BCM_IPROC >>>> >>>> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>>> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>>> --- >>>> arch/arm/mach-bcm/Kconfig | 1 + >>>> 1 file changed, 1 insertion(+) >>>> >>>> diff --git a/arch/arm/mach-bcm/Kconfig b/arch/arm/mach-bcm/Kconfig >>>> index aaeec78..86ee90b 100644 >>>> --- a/arch/arm/mach-bcm/Kconfig >>>> +++ b/arch/arm/mach-bcm/Kconfig >>>> @@ -19,6 +19,7 @@ config ARCH_BCM_IPROC >>>> select ARCH_REQUIRE_GPIOLIB >>>> select ARM_AMBA >>>> select PINCTRL >>>> + select I2C_BCM_IPROC >>> >>> One way to avoid having to modify mach-bcm/Kconfig would be to have your >>> i2c driver Kconfig do this: >>> >>> default ARCH_BCM_IPROC >>> >>> would that work? >>> >> Yes. So in which case it is better to select a driver from the >> architecture specific Kconfig? > > I suppose if your driver/subsystem is critical for system boot, like > powering a regulator or something that has a critical purpose, a select > is probably more appropriate here. If this is just exposing non-critical > devices, I would go with a depends on/default at the driver Kconfig level. > > This is just how I see things, others would definitively have a > different view. > Okay. Thanks. I default the driver to y in patchset v3 just like some other I2C drivers in the same Kconfig. It already depends on ARCH_BCM_IPROC so it makes sense to set the default to y, i.e., it will be enabled by default for ARCH_BCM_IPROC platforms. >> >>>> help >>>> This enables support for systems based on Broadcom IPROC >>>> architected SoCs. >>>> The IPROC complex contains one or more ARM CPUs along with >>>> common >>>> >>> > ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v2 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus 2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui ` (2 preceding siblings ...) [not found] ` <1418177893-22094-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2014-12-10 2:18 ` Ray Jui 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 2:18 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..f7d6c1d 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui @ 2014-12-10 3:57 ` Ray Jui 2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui ` (3 more replies) 2015-01-14 22:23 ` [PATCH v4 " Ray Jui ` (5 subsequent siblings) 8 siblings, 4 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 500 ++++++++++++++++++++ 5 files changed, 568 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding 2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2014-12-10 3:57 ` Ray Jui 2014-12-10 3:57 ` [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui ` (2 subsequent siblings) 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2014-12-10 3:57 ` Ray Jui [not found] ` <1418183832-24793-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 3:57 ` [PATCH v3 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui [not found] ` <548F577E.7020207@broadcom.com> 3 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 500 ++++++++++++++++++++++++++++++++++++ 3 files changed, 511 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index c1351d9..df21366 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC + default y + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 5e6c822..216e7be 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..35ac497 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,500 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIME_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + + void __iomem *base; + struct i2c_msg *msg; + + struct i2c_adapter adapter; + + struct completion done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *dev = data; + u32 status = readl(dev->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, dev->base + IS_OFFSET); + complete_all(&dev->done); + + return IRQ_HANDLED; +} + +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); + + while (readl(dev->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) { + if (time_after(jiffies, timeout)) { + dev_err(dev->device, "wait for bus idle timeout\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(dev->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = (msg->addr << 1) & 0xfe; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_err(dev->device, "lost bus arbitration\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_ADDR: + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_DATA: + dev_err(dev->device, "NAK data\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_TIMEOUT: + dev_err(dev->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_err(dev->device, "unknown error code=%d\n", val); + return -EREMOTEIO; + } + + return -EREMOTEIO; +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(dev->device, + "supported data length is 1 - %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + dev->msg = msg; + ret = __wait_for_bus_idle(dev); + if (ret) + return ret; + + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, dev->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, dev->base + M_TX_OFFSET); + } + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&dev->done); + + /* + * Enable the "start busy" interrupt, which will be triggered after + * the transaction is done + */ + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, dev->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&dev->done, time_left); + + /* disable all interrupts */ + writel(0, dev->base + IE_OFFSET); + + if (!time_left) { + dev_err(dev->device, "transaction times out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + return -EREMOTEIO; + } + + ret = bcm_iproc_i2c_check_status(dev); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(dev->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); + dev_dbg(dev->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); + if (ret) { + dev_err(dev->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", + &bus_speed); + if (ret < 0) { + dev_err(dev->device, "missing clock-frequency property\n"); + return -ENODEV; + } + + switch (bus_speed) { + case 100000: + speed_bit = 0; + break; + case 400000: + speed_bit = 1; + break; + default: + dev_err(dev->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } + + val = readl(dev->base + TIM_CFG_OFFSET); + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; + writel(val, dev->base + TIM_CFG_OFFSET); + + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + /* put controller in reset */ + val = readl(dev->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, dev->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val = readl(dev->base + CFG_OFFSET); + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, dev->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, dev->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + val = 0; + writel(val, dev->base + IE_OFFSET); + + /* clear all pending interrupts */ + val = readl(dev->base + IS_OFFSET); + writel(val, dev->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, dev->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) +{ + u32 val; + + val = readl(dev->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, dev->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *res; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + platform_set_drvdata(pdev, dev); + dev->device = &pdev->dev; + init_completion(&dev->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->base = devm_ioremap_resource(dev->device, res); + if (IS_ERR(dev->base)) + return -ENOMEM; + + ret = bcm_iproc_i2c_init(dev); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(dev); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev->device, "no irq resource\n"); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, + IRQF_SHARED, pdev->name, dev); + if (ret) { + dev_err(dev->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(dev); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(dev->device, "failed to add adapter\n"); + return ret; + } + + dev_info(dev->device, "device registered successfully\n"); + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); + + i2c_del_adapter(&dev->adapter); + bcm_iproc_i2c_disable(dev); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + {.compatible = "brcm,iproc-i2c",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1418183832-24793-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1418183832-24793-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-13 22:50 ` Uwe Kleine-König [not found] ` <20150113225012.GK22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-13 22:50 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Tue, Dec 09, 2014 at 07:57:11PM -0800, Ray Jui wrote: > Add initial support to the Broadcom iProc I2C controller found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports two bus speeds > including standard mode (100kHz) and fast mode (400kHz) > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > --- > drivers/i2c/busses/Kconfig | 10 + > drivers/i2c/busses/Makefile | 1 + > drivers/i2c/busses/i2c-bcm-iproc.c | 500 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 511 insertions(+) > create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > index c1351d9..df21366 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > @@ -372,6 +372,16 @@ config I2C_BCM2835 > This support is also available as a module. If so, the module > will be called i2c-bcm2835. > > +config I2C_BCM_IPROC > + tristate "Broadcom iProc I2C controller" > + depends on ARCH_BCM_IPROC > + default y It would be nice to have the following here to improve compile coverage testing: depends on ARCH_BCM_IPROC || COMPILE_TEST default ARCH_BCM_IPROC > + help > + If you say yes to this option, support will be included for the > + Broadcom iProc I2C controller. > + > + If you don't know what to do here, say N. > + > config I2C_BCM_KONA > tristate "BCM Kona I2C adapter" > depends on ARCH_BCM_MOBILE > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > index 5e6c822..216e7be 100644 > --- a/drivers/i2c/busses/Makefile > +++ b/drivers/i2c/busses/Makefile > @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o > obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o > obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o > obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o > +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o > obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o > obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o > obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o > diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c > new file mode 100644 > index 0000000..35ac497 > --- /dev/null > +++ b/drivers/i2c/busses/i2c-bcm-iproc.c > @@ -0,0 +1,500 @@ > +/* > + * Copyright (C) 2014 Broadcom Corporation > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation version 2. > + * > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any > + * kind, whether express or implied; without even the implied warranty > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/sched.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/platform_device.h> > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > + > +#define CFG_OFFSET 0x00 > +#define CFG_RESET_SHIFT 31 > +#define CFG_EN_SHIFT 30 > +#define CFG_M_RETRY_CNT_SHIFT 16 > +#define CFG_M_RETRY_CNT_MASK 0x0f > + > +#define TIM_CFG_OFFSET 0x04 > +#define TIME_CFG_MODE_400_SHIFT 31 > + > +#define M_FIFO_CTRL_OFFSET 0x0c > +#define M_FIFO_RX_FLUSH_SHIFT 31 > +#define M_FIFO_TX_FLUSH_SHIFT 30 > +#define M_FIFO_RX_CNT_SHIFT 16 > +#define M_FIFO_RX_CNT_MASK 0x7f > +#define M_FIFO_RX_THLD_SHIFT 8 > +#define M_FIFO_RX_THLD_MASK 0x3f > + > +#define M_CMD_OFFSET 0x30 > +#define M_CMD_START_BUSY_SHIFT 31 > +#define M_CMD_STATUS_SHIFT 25 > +#define M_CMD_STATUS_MASK 0x07 > +#define M_CMD_STATUS_SUCCESS 0x0 > +#define M_CMD_STATUS_LOST_ARB 0x1 > +#define M_CMD_STATUS_NACK_ADDR 0x2 > +#define M_CMD_STATUS_NACK_DATA 0x3 > +#define M_CMD_STATUS_TIMEOUT 0x4 > +#define M_CMD_PROTOCOL_SHIFT 9 > +#define M_CMD_PROTOCOL_MASK 0xf > +#define M_CMD_PROTOCOL_BLK_WR 0x7 > +#define M_CMD_PROTOCOL_BLK_RD 0x8 > +#define M_CMD_PEC_SHIFT 8 > +#define M_CMD_RD_CNT_SHIFT 0 > +#define M_CMD_RD_CNT_MASK 0xff > + > +#define IE_OFFSET 0x38 > +#define IE_M_RX_FIFO_FULL_SHIFT 31 > +#define IE_M_RX_THLD_SHIFT 30 > +#define IE_M_START_BUSY_SHIFT 28 > + > +#define IS_OFFSET 0x3c > +#define IS_M_RX_FIFO_FULL_SHIFT 31 > +#define IS_M_RX_THLD_SHIFT 30 > +#define IS_M_START_BUSY_SHIFT 28 > + > +#define M_TX_OFFSET 0x40 > +#define M_TX_WR_STATUS_SHIFT 31 > +#define M_TX_DATA_SHIFT 0 > +#define M_TX_DATA_MASK 0xff > + > +#define M_RX_OFFSET 0x44 > +#define M_RX_STATUS_SHIFT 30 > +#define M_RX_STATUS_MASK 0x03 > +#define M_RX_PEC_ERR_SHIFT 29 > +#define M_RX_DATA_SHIFT 0 > +#define M_RX_DATA_MASK 0xff > + > +#define I2C_TIMEOUT_MESC 100 > +#define M_TX_RX_FIFO_SIZE 64 > + > +enum bus_speed_index { > + I2C_SPD_100K = 0, > + I2C_SPD_400K, > +}; > + > +struct bcm_iproc_i2c_dev { > + struct device *device; > + > + void __iomem *base; > + struct i2c_msg *msg; > + > + struct i2c_adapter adapter; > + > + struct completion done; > +}; > + > +/* > + * Can be expanded in the future if more interrupt status bits are utilized > + */ > +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) > + > +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > +{ > + struct bcm_iproc_i2c_dev *dev = data; > + u32 status = readl(dev->base + IS_OFFSET); > + > + status &= ISR_MASK; > + > + if (!status) > + return IRQ_NONE; > + > + writel(status, dev->base + IS_OFFSET); > + complete_all(&dev->done); > + > + return IRQ_HANDLED; > +} > + > +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) > +{ > + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); > + > + while (readl(dev->base + M_CMD_OFFSET) & > + (1 << M_CMD_START_BUSY_SHIFT)) { > + if (time_after(jiffies, timeout)) { > + dev_err(dev->device, "wait for bus idle timeout\n"); > + return -ETIMEDOUT; > + } Add a call to cpu_relax here. Do you really need a tight loop here? > + } > + > + return 0; > +} > + > +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, > + struct i2c_msg *msg, u8 *addr) > +{ > + > + if (msg->flags & I2C_M_TEN) { > + dev_err(dev->device, "no support for 10-bit address\n"); > + return -EINVAL; > + } > + > + *addr = (msg->addr << 1) & 0xfe; I don't see what difference the & 0xfe makes. I think you can drop that. > + > + if (msg->flags & I2C_M_RD) > + *addr |= 1; > + > + return 0; > +} > + > +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + val = readl(dev->base + M_CMD_OFFSET); > + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; > + > + switch (val) { > + case M_CMD_STATUS_SUCCESS: > + return 0; > + > + case M_CMD_STATUS_LOST_ARB: > + dev_err(dev->device, "lost bus arbitration\n"); I wouldn't dev_err that, only dev_dbg. I'm not sure how usual the errors for the next two cases is, maybe degrade them to dev_dbg, too? > + return -EREMOTEIO; > + > + case M_CMD_STATUS_NACK_ADDR: > + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_NACK_DATA: > + dev_err(dev->device, "NAK data\n"); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_TIMEOUT: > + dev_err(dev->device, "bus timeout\n"); > + return -ETIMEDOUT; > + > + default: > + dev_err(dev->device, "unknown error code=%d\n", val); > + return -EREMOTEIO; > + } > + > + return -EREMOTEIO; > +} > + > +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, > + struct i2c_msg *msg) > +{ > + int ret, i; > + u8 addr; > + u32 val; > + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); > + > + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { > + dev_err(dev->device, > + "supported data length is 1 - %u bytes\n", > + M_TX_RX_FIFO_SIZE - 1); > + return -EINVAL; > + } > + > + dev->msg = msg; > + ret = __wait_for_bus_idle(dev); > + if (ret) > + return ret; > + > + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); > + if (ret) > + return ret; > + > + /* load slave address into the TX FIFO */ > + writel(addr, dev->base + M_TX_OFFSET); > + > + /* for a write transaction, load data into the TX FIFO */ > + if (!(msg->flags & I2C_M_RD)) { > + for (i = 0; i < msg->len; i++) { > + val = msg->buf[i]; > + > + /* mark the last byte */ > + if (i == msg->len - 1) > + val |= 1 << M_TX_WR_STATUS_SHIFT; > + > + writel(val, dev->base + M_TX_OFFSET); > + } > + } > + > + /* mark as incomplete before starting the transaction */ > + reinit_completion(&dev->done); > + > + /* > + * Enable the "start busy" interrupt, which will be triggered after > + * the transaction is done This sound's wrong. I'd expect "start busy" to trigger as soon as the controller gets hold of the bus. > + */ > + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); > + > + /* > + * Now we can activate the transfer. For a read operation, specify the > + * number of bytes to read > + */ > + val = 1 << M_CMD_START_BUSY_SHIFT; > + if (msg->flags & I2C_M_RD) { > + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > + (msg->len << M_CMD_RD_CNT_SHIFT); > + } else { > + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > + } > + writel(val, dev->base + M_CMD_OFFSET); > + > + time_left = wait_for_completion_timeout(&dev->done, time_left); > + > + /* disable all interrupts */ > + writel(0, dev->base + IE_OFFSET); > + > + if (!time_left) { > + dev_err(dev->device, "transaction times out\n"); > + > + /* flush FIFOs */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > + (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, dev->base + M_FIFO_CTRL_OFFSET); > + return -EREMOTEIO; > + } > + > + ret = bcm_iproc_i2c_check_status(dev); > + if (ret) { > + /* flush both TX/RX FIFOs */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > + (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, dev->base + M_FIFO_CTRL_OFFSET); > + return ret; > + } > + > + /* > + * For a read operation, we now need to load the data from FIFO > + * into the memory buffer > + */ > + if (msg->flags & I2C_M_RD) { > + for (i = 0; i < msg->len; i++) { > + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> > + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; > + } > + } > + > + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", > + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, > + msg->len); > + dev_dbg(dev->device, "**** data start ****\n"); > + for (i = 0; i < msg->len; i++) > + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); > + dev_dbg(dev->device, "**** data end ****\n"); > + > + return 0; > +} > + > +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, > + struct i2c_msg msgs[], int num) > +{ > + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); > + int ret, i; > + > + /* go through all messages */ > + for (i = 0; i < num; i++) { > + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); > + if (ret) { > + dev_err(dev->device, "xfer failed\n"); > + return ret; > + } > + } > + > + return num; > +} > + > +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) > +{ > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; > +} > + > +static const struct i2c_algorithm bcm_iproc_algo = { > + .master_xfer = bcm_iproc_i2c_xfer, > + .functionality = bcm_iproc_i2c_functionality, > +}; > + > +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) > +{ > + unsigned int bus_speed, speed_bit; > + u32 val; > + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", > + &bus_speed); Inconsistent coding style. At most other places you use 2 tabs to indent a continued line. (Well you use this style for breaks in function declarations. Using the same style for both cases would be nice.) > + if (ret < 0) { > + dev_err(dev->device, "missing clock-frequency property\n"); > + return -ENODEV; > + } > + > + switch (bus_speed) { > + case 100000: > + speed_bit = 0; > + break; > + case 400000: > + speed_bit = 1; > + break; > + default: > + dev_err(dev->device, "%d Hz bus speed not supported\n", > + bus_speed); > + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); > + return -EINVAL; > + } I'd be more graceful here: if (bus_speed < 100000) error_out; else if (bus_speed < 400000) speed_bit = 0; else /* >= 400000 */ speed_bit = 1; > + > + val = readl(dev->base + TIM_CFG_OFFSET); > + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); > + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; > + writel(val, dev->base + TIM_CFG_OFFSET); > + > + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); > + > + return 0; > +} > + > +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + /* put controller in reset */ > + val = readl(dev->base + CFG_OFFSET); > + val |= 1 << CFG_RESET_SHIFT; > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, dev->base + CFG_OFFSET); > + > + /* wait 100 usec per spec */ > + udelay(100); > + > + /* bring controller out of reset */ > + val = readl(dev->base + CFG_OFFSET); Is it necessary to reread the register value here? > + val &= ~(1 << CFG_RESET_SHIFT); > + writel(val, dev->base + CFG_OFFSET); > + > + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, dev->base + M_FIFO_CTRL_OFFSET); > + > + /* disable all interrupts */ > + val = 0; > + writel(val, dev->base + IE_OFFSET); writel(0, dev->base + IE_OFFSET); > + /* clear all pending interrupts */ > + val = readl(dev->base + IS_OFFSET); > + writel(val, dev->base + IS_OFFSET); writel(0xffffffff, dev->base + IS_OFFSET); ?? > + > + return 0; > +} > + > +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + val = readl(dev->base + CFG_OFFSET); > + val |= 1 << CFG_EN_SHIFT; > + writel(val, dev->base + CFG_OFFSET); > +} > + > +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) > +{ > + u32 val; > + > + val = readl(dev->base + CFG_OFFSET); > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, dev->base + CFG_OFFSET); > +} > + > +static int bcm_iproc_i2c_probe(struct platform_device *pdev) > +{ > + int irq, ret = 0; > + struct bcm_iproc_i2c_dev *dev; "dev" is a misleading name here. I'd call this bcm_iproc_i2c_ddata *ddata; > + struct i2c_adapter *adap; > + struct resource *res; > + > + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); > + if (!dev) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, dev); > + dev->device = &pdev->dev; > + init_completion(&dev->done); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + dev->base = devm_ioremap_resource(dev->device, res); > + if (IS_ERR(dev->base)) > + return -ENOMEM; return PTR_ERR(dev->base); > + > + ret = bcm_iproc_i2c_init(dev); > + if (ret) > + return ret; > + > + ret = bcm_iproc_i2c_cfg_speed(dev); > + if (ret) > + return ret; > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { irq == 0 should be handled as error, too. > + dev_err(dev->device, "no irq resource\n"); > + return irq; > + } > + > + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, > + IRQF_SHARED, pdev->name, dev); > + if (ret) { > + dev_err(dev->device, "unable to request irq %i\n", irq); > + return ret; > + } > + > + bcm_iproc_i2c_enable(dev); > + > + adap = &dev->adapter; > + i2c_set_adapdata(adap, dev); > + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); > + adap->algo = &bcm_iproc_algo; > + adap->dev.parent = &pdev->dev; > + adap->dev.of_node = pdev->dev.of_node; > + > + ret = i2c_add_adapter(adap); > + if (ret) { > + dev_err(dev->device, "failed to add adapter\n"); > + return ret; > + } > + > + dev_info(dev->device, "device registered successfully\n"); This just clutters the boot log. Please remove. > + > + return 0; > +} > + > +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > +{ > + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); > + > + i2c_del_adapter(&dev->adapter); > + bcm_iproc_i2c_disable(dev); I think you have a problem here if bcm_iproc_i2c_remove is called while an irq is still being serviced. I'm not sure how to prevent this properly for a shared interrupt. > + > + return 0; > +} > + > +static const struct of_device_id bcm_iproc_i2c_of_match[] = { > + {.compatible = "brcm,iproc-i2c",}, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); > + > +static struct platform_driver bcm_iproc_i2c_driver = { > + .driver = { > + .name = "bcm-iproc-i2c", > + .of_match_table = bcm_iproc_i2c_of_match, > + }, Inconsistent indention. > + .probe = bcm_iproc_i2c_probe, > + .remove = bcm_iproc_i2c_remove, > +}; > +module_platform_driver(bcm_iproc_i2c_driver); > + > +MODULE_AUTHOR("Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>"); > +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); > +MODULE_LICENSE("GPL v2"); -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150113225012.GK22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150113225012.GK22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-14 2:14 ` Ray Jui 2015-01-14 7:51 ` Uwe Kleine-König [not found] ` <54B5D0F9.8030902-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 2 replies; 106+ messages in thread From: Ray Jui @ 2015-01-14 2:14 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/13/2015 2:50 PM, Uwe Kleine-König wrote: > Hello, > > On Tue, Dec 09, 2014 at 07:57:11PM -0800, Ray Jui wrote: >> Add initial support to the Broadcom iProc I2C controller found in the >> iProc family of SoCs. >> >> The iProc I2C controller has separate internal TX and RX FIFOs, each has >> a size of 64 bytes. The iProc I2C controller supports two bus speeds >> including standard mode (100kHz) and fast mode (400kHz) >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> --- >> drivers/i2c/busses/Kconfig | 10 + >> drivers/i2c/busses/Makefile | 1 + >> drivers/i2c/busses/i2c-bcm-iproc.c | 500 ++++++++++++++++++++++++++++++++++++ >> 3 files changed, 511 insertions(+) >> create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c >> >> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig >> index c1351d9..df21366 100644 >> --- a/drivers/i2c/busses/Kconfig >> +++ b/drivers/i2c/busses/Kconfig >> @@ -372,6 +372,16 @@ config I2C_BCM2835 >> This support is also available as a module. If so, the module >> will be called i2c-bcm2835. >> >> +config I2C_BCM_IPROC >> + tristate "Broadcom iProc I2C controller" >> + depends on ARCH_BCM_IPROC >> + default y > It would be nice to have the following here to improve compile coverage > testing: > > depends on ARCH_BCM_IPROC || COMPILE_TEST > default ARCH_BCM_IPROC > Sure will do! >> + help >> + If you say yes to this option, support will be included for the >> + Broadcom iProc I2C controller. >> + >> + If you don't know what to do here, say N. >> + >> config I2C_BCM_KONA >> tristate "BCM Kona I2C adapter" >> depends on ARCH_BCM_MOBILE >> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile >> index 5e6c822..216e7be 100644 >> --- a/drivers/i2c/busses/Makefile >> +++ b/drivers/i2c/busses/Makefile >> @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o >> obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o >> obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o >> obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o >> +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o >> obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o >> obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o >> obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o >> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c >> new file mode 100644 >> index 0000000..35ac497 >> --- /dev/null >> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c >> @@ -0,0 +1,500 @@ >> +/* >> + * Copyright (C) 2014 Broadcom Corporation >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License as >> + * published by the Free Software Foundation version 2. >> + * >> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any >> + * kind, whether express or implied; without even the implied warranty >> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include <linux/device.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/sched.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/platform_device.h> >> +#include <linux/clk.h> >> +#include <linux/io.h> >> +#include <linux/slab.h> >> +#include <linux/delay.h> >> + >> +#define CFG_OFFSET 0x00 >> +#define CFG_RESET_SHIFT 31 >> +#define CFG_EN_SHIFT 30 >> +#define CFG_M_RETRY_CNT_SHIFT 16 >> +#define CFG_M_RETRY_CNT_MASK 0x0f >> + >> +#define TIM_CFG_OFFSET 0x04 >> +#define TIME_CFG_MODE_400_SHIFT 31 >> + >> +#define M_FIFO_CTRL_OFFSET 0x0c >> +#define M_FIFO_RX_FLUSH_SHIFT 31 >> +#define M_FIFO_TX_FLUSH_SHIFT 30 >> +#define M_FIFO_RX_CNT_SHIFT 16 >> +#define M_FIFO_RX_CNT_MASK 0x7f >> +#define M_FIFO_RX_THLD_SHIFT 8 >> +#define M_FIFO_RX_THLD_MASK 0x3f >> + >> +#define M_CMD_OFFSET 0x30 >> +#define M_CMD_START_BUSY_SHIFT 31 >> +#define M_CMD_STATUS_SHIFT 25 >> +#define M_CMD_STATUS_MASK 0x07 >> +#define M_CMD_STATUS_SUCCESS 0x0 >> +#define M_CMD_STATUS_LOST_ARB 0x1 >> +#define M_CMD_STATUS_NACK_ADDR 0x2 >> +#define M_CMD_STATUS_NACK_DATA 0x3 >> +#define M_CMD_STATUS_TIMEOUT 0x4 >> +#define M_CMD_PROTOCOL_SHIFT 9 >> +#define M_CMD_PROTOCOL_MASK 0xf >> +#define M_CMD_PROTOCOL_BLK_WR 0x7 >> +#define M_CMD_PROTOCOL_BLK_RD 0x8 >> +#define M_CMD_PEC_SHIFT 8 >> +#define M_CMD_RD_CNT_SHIFT 0 >> +#define M_CMD_RD_CNT_MASK 0xff >> + >> +#define IE_OFFSET 0x38 >> +#define IE_M_RX_FIFO_FULL_SHIFT 31 >> +#define IE_M_RX_THLD_SHIFT 30 >> +#define IE_M_START_BUSY_SHIFT 28 >> + >> +#define IS_OFFSET 0x3c >> +#define IS_M_RX_FIFO_FULL_SHIFT 31 >> +#define IS_M_RX_THLD_SHIFT 30 >> +#define IS_M_START_BUSY_SHIFT 28 >> + >> +#define M_TX_OFFSET 0x40 >> +#define M_TX_WR_STATUS_SHIFT 31 >> +#define M_TX_DATA_SHIFT 0 >> +#define M_TX_DATA_MASK 0xff >> + >> +#define M_RX_OFFSET 0x44 >> +#define M_RX_STATUS_SHIFT 30 >> +#define M_RX_STATUS_MASK 0x03 >> +#define M_RX_PEC_ERR_SHIFT 29 >> +#define M_RX_DATA_SHIFT 0 >> +#define M_RX_DATA_MASK 0xff >> + >> +#define I2C_TIMEOUT_MESC 100 >> +#define M_TX_RX_FIFO_SIZE 64 >> + >> +enum bus_speed_index { >> + I2C_SPD_100K = 0, >> + I2C_SPD_400K, >> +}; >> + >> +struct bcm_iproc_i2c_dev { >> + struct device *device; >> + >> + void __iomem *base; >> + struct i2c_msg *msg; >> + >> + struct i2c_adapter adapter; >> + >> + struct completion done; >> +}; >> + >> +/* >> + * Can be expanded in the future if more interrupt status bits are utilized >> + */ >> +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) >> + >> +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) >> +{ >> + struct bcm_iproc_i2c_dev *dev = data; >> + u32 status = readl(dev->base + IS_OFFSET); >> + >> + status &= ISR_MASK; >> + >> + if (!status) >> + return IRQ_NONE; >> + >> + writel(status, dev->base + IS_OFFSET); >> + complete_all(&dev->done); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *dev) >> +{ >> + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); >> + >> + while (readl(dev->base + M_CMD_OFFSET) & >> + (1 << M_CMD_START_BUSY_SHIFT)) { >> + if (time_after(jiffies, timeout)) { >> + dev_err(dev->device, "wait for bus idle timeout\n"); >> + return -ETIMEDOUT; >> + } > Add a call to cpu_relax here. Do you really need a tight loop here? > Yes, thanks! >> + } >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *dev, >> + struct i2c_msg *msg, u8 *addr) >> +{ >> + >> + if (msg->flags & I2C_M_TEN) { >> + dev_err(dev->device, "no support for 10-bit address\n"); >> + return -EINVAL; >> + } >> + >> + *addr = (msg->addr << 1) & 0xfe; > I don't see what difference the & 0xfe makes. I think you can drop that. > Yeah just see this now. Thanks! >> + >> + if (msg->flags & I2C_M_RD) >> + *addr |= 1; >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + val = readl(dev->base + M_CMD_OFFSET); >> + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; >> + >> + switch (val) { >> + case M_CMD_STATUS_SUCCESS: >> + return 0; >> + >> + case M_CMD_STATUS_LOST_ARB: >> + dev_err(dev->device, "lost bus arbitration\n"); > I wouldn't dev_err that, only dev_dbg. I'm not sure how usual the errors > for the next two cases is, maybe degrade them to dev_dbg, too? > These errors are rare, and it's nice to keep them at the dev_err level so the user will be more aware. >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_NACK_ADDR: >> + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_NACK_DATA: >> + dev_err(dev->device, "NAK data\n"); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_TIMEOUT: >> + dev_err(dev->device, "bus timeout\n"); >> + return -ETIMEDOUT; >> + >> + default: >> + dev_err(dev->device, "unknown error code=%d\n", val); >> + return -EREMOTEIO; >> + } >> + >> + return -EREMOTEIO; >> +} >> + >> +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *dev, >> + struct i2c_msg *msg) >> +{ >> + int ret, i; >> + u8 addr; >> + u32 val; >> + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); >> + >> + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { >> + dev_err(dev->device, >> + "supported data length is 1 - %u bytes\n", >> + M_TX_RX_FIFO_SIZE - 1); >> + return -EINVAL; >> + } >> + >> + dev->msg = msg; >> + ret = __wait_for_bus_idle(dev); >> + if (ret) >> + return ret; >> + >> + ret = bcm_iproc_i2c_format_addr(dev, msg, &addr); >> + if (ret) >> + return ret; >> + >> + /* load slave address into the TX FIFO */ >> + writel(addr, dev->base + M_TX_OFFSET); >> + >> + /* for a write transaction, load data into the TX FIFO */ >> + if (!(msg->flags & I2C_M_RD)) { >> + for (i = 0; i < msg->len; i++) { >> + val = msg->buf[i]; >> + >> + /* mark the last byte */ >> + if (i == msg->len - 1) >> + val |= 1 << M_TX_WR_STATUS_SHIFT; >> + >> + writel(val, dev->base + M_TX_OFFSET); >> + } >> + } >> + >> + /* mark as incomplete before starting the transaction */ >> + reinit_completion(&dev->done); >> + >> + /* >> + * Enable the "start busy" interrupt, which will be triggered after >> + * the transaction is done > This sound's wrong. I'd expect "start busy" to trigger as soon as the > controller gets hold of the bus. > Okay maybe the naming is misleading. But this is the real name used in the register description from the datasheet. I'll add more comment in the code to make it more clear. Basically this enables the interrupt that triggers when the controller internal start_busy bit transitions from 1 to 0, i.e., after a transaction finishes. The interrupt won't be triggered when a new transaction starts since it causes the start_busy bit to transit from 0 to 1. >> + */ >> + writel(1 << IE_M_START_BUSY_SHIFT, dev->base + IE_OFFSET); >> + >> + /* >> + * Now we can activate the transfer. For a read operation, specify the >> + * number of bytes to read >> + */ >> + val = 1 << M_CMD_START_BUSY_SHIFT; >> + if (msg->flags & I2C_M_RD) { >> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >> + (msg->len << M_CMD_RD_CNT_SHIFT); >> + } else { >> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >> + } >> + writel(val, dev->base + M_CMD_OFFSET); >> + >> + time_left = wait_for_completion_timeout(&dev->done, time_left); >> + >> + /* disable all interrupts */ >> + writel(0, dev->base + IE_OFFSET); >> + >> + if (!time_left) { >> + dev_err(dev->device, "transaction times out\n"); >> + >> + /* flush FIFOs */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> + (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, dev->base + M_FIFO_CTRL_OFFSET); >> + return -EREMOTEIO; >> + } >> + >> + ret = bcm_iproc_i2c_check_status(dev); >> + if (ret) { >> + /* flush both TX/RX FIFOs */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> + (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, dev->base + M_FIFO_CTRL_OFFSET); >> + return ret; >> + } >> + >> + /* >> + * For a read operation, we now need to load the data from FIFO >> + * into the memory buffer >> + */ >> + if (msg->flags & I2C_M_RD) { >> + for (i = 0; i < msg->len; i++) { >> + msg->buf[i] = (readl(dev->base + M_RX_OFFSET) >> >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; >> + } >> + } >> + >> + dev_dbg(dev->device, "xfer %c, addr=0x%02x, len=%d\n", >> + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, >> + msg->len); >> + dev_dbg(dev->device, "**** data start ****\n"); >> + for (i = 0; i < msg->len; i++) >> + dev_dbg(dev->device, "0x%02x ", msg->buf[i]); >> + dev_dbg(dev->device, "**** data end ****\n"); >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, >> + struct i2c_msg msgs[], int num) >> +{ >> + struct bcm_iproc_i2c_dev *dev = i2c_get_adapdata(adapter); >> + int ret, i; >> + >> + /* go through all messages */ >> + for (i = 0; i < num; i++) { >> + ret = bcm_iproc_i2c_xfer_single_msg(dev, &msgs[i]); >> + if (ret) { >> + dev_err(dev->device, "xfer failed\n"); >> + return ret; >> + } >> + } >> + >> + return num; >> +} >> + >> +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) >> +{ >> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; >> +} >> + >> +static const struct i2c_algorithm bcm_iproc_algo = { >> + .master_xfer = bcm_iproc_i2c_xfer, >> + .functionality = bcm_iproc_i2c_functionality, >> +}; >> + >> +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *dev) >> +{ >> + unsigned int bus_speed, speed_bit; >> + u32 val; >> + int ret = of_property_read_u32(dev->device->of_node, "clock-frequency", >> + &bus_speed); > Inconsistent coding style. At most other places you use 2 tabs to indent > a continued line. (Well you use this style for breaks in function > declarations. Using the same style for both cases would be nice.) > Will try to use this style for both continued lines and function declarations. Thanks! >> + if (ret < 0) { >> + dev_err(dev->device, "missing clock-frequency property\n"); >> + return -ENODEV; >> + } >> + >> + switch (bus_speed) { >> + case 100000: >> + speed_bit = 0; >> + break; >> + case 400000: >> + speed_bit = 1; >> + break; >> + default: >> + dev_err(dev->device, "%d Hz bus speed not supported\n", >> + bus_speed); >> + dev_err(dev->device, "valid speeds are 100khz and 400khz\n"); >> + return -EINVAL; >> + } > I'd be more graceful here: > > if (bus_speed < 100000) > error_out; > else if (bus_speed < 400000) > speed_bit = 0; > else > /* >= 400000 */ > speed_bit = 1; > Okay I can do that. Thanks. >> + >> + val = readl(dev->base + TIM_CFG_OFFSET); >> + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); >> + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; >> + writel(val, dev->base + TIM_CFG_OFFSET); >> + >> + dev_info(dev->device, "bus set to %u Hz\n", bus_speed); >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + /* put controller in reset */ >> + val = readl(dev->base + CFG_OFFSET); >> + val |= 1 << CFG_RESET_SHIFT; >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, dev->base + CFG_OFFSET); >> + >> + /* wait 100 usec per spec */ >> + udelay(100); >> + >> + /* bring controller out of reset */ >> + val = readl(dev->base + CFG_OFFSET); > Is it necessary to reread the register value here? > I guess not, since there's no one else modifies this register. Will get rid of the redundant read here. Thanks! >> + val &= ~(1 << CFG_RESET_SHIFT); >> + writel(val, dev->base + CFG_OFFSET); >> + >> + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, dev->base + M_FIFO_CTRL_OFFSET); >> + >> + /* disable all interrupts */ >> + val = 0; >> + writel(val, dev->base + IE_OFFSET); > writel(0, dev->base + IE_OFFSET); > Right. >> + /* clear all pending interrupts */ >> + val = readl(dev->base + IS_OFFSET); >> + writel(val, dev->base + IS_OFFSET); > writel(0xffffffff, dev->base + IS_OFFSET); ?? > Yes, better. >> + >> + return 0; >> +} >> + >> +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + val = readl(dev->base + CFG_OFFSET); >> + val |= 1 << CFG_EN_SHIFT; >> + writel(val, dev->base + CFG_OFFSET); >> +} >> + >> +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *dev) >> +{ >> + u32 val; >> + >> + val = readl(dev->base + CFG_OFFSET); >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, dev->base + CFG_OFFSET); >> +} >> + >> +static int bcm_iproc_i2c_probe(struct platform_device *pdev) >> +{ >> + int irq, ret = 0; >> + struct bcm_iproc_i2c_dev *dev; > "dev" is a misleading name here. I'd call this > > bcm_iproc_i2c_ddata *ddata; > I will change it to iproc_i2c; >> + struct i2c_adapter *adap; >> + struct resource *res; >> + >> + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); >> + if (!dev) >> + return -ENOMEM; >> + >> + platform_set_drvdata(pdev, dev); >> + dev->device = &pdev->dev; >> + init_completion(&dev->done); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + dev->base = devm_ioremap_resource(dev->device, res); >> + if (IS_ERR(dev->base)) >> + return -ENOMEM; > return PTR_ERR(dev->base); > Okay. Thanks. >> + >> + ret = bcm_iproc_i2c_init(dev); >> + if (ret) >> + return ret; >> + >> + ret = bcm_iproc_i2c_cfg_speed(dev); >> + if (ret) >> + return ret; >> + >> + irq = platform_get_irq(pdev, 0); >> + if (irq < 0) { > irq == 0 should be handled as error, too. > Ah. I thought zero is a valid global interrupt number, and I see other drivers checking against < 0 as well. Is my understanding incorrect? >> + dev_err(dev->device, "no irq resource\n"); >> + return irq; >> + } >> + >> + ret = devm_request_irq(&pdev->dev, irq, bcm_iproc_i2c_isr, >> + IRQF_SHARED, pdev->name, dev); >> + if (ret) { >> + dev_err(dev->device, "unable to request irq %i\n", irq); >> + return ret; >> + } >> + >> + bcm_iproc_i2c_enable(dev); >> + >> + adap = &dev->adapter; >> + i2c_set_adapdata(adap, dev); >> + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); >> + adap->algo = &bcm_iproc_algo; >> + adap->dev.parent = &pdev->dev; >> + adap->dev.of_node = pdev->dev.of_node; >> + >> + ret = i2c_add_adapter(adap); >> + if (ret) { >> + dev_err(dev->device, "failed to add adapter\n"); >> + return ret; >> + } >> + >> + dev_info(dev->device, "device registered successfully\n"); > This just clutters the boot log. Please remove. > Okay. >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >> +{ >> + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); >> + >> + i2c_del_adapter(&dev->adapter); >> + bcm_iproc_i2c_disable(dev); > I think you have a problem here if bcm_iproc_i2c_remove is called while > an irq is still being serviced. I'm not sure how to prevent this > properly for a shared interrupt. > Can I grab i2c_lock_adapter to ensure the bus is locked (so there's no outstanding transactions or IRQs by the time we remove the adapter)? But I see no I2C bus driver does this in their remove function... >> + >> + return 0; >> +} >> + >> +static const struct of_device_id bcm_iproc_i2c_of_match[] = { >> + {.compatible = "brcm,iproc-i2c",}, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); >> + >> +static struct platform_driver bcm_iproc_i2c_driver = { >> + .driver = { >> + .name = "bcm-iproc-i2c", >> + .of_match_table = bcm_iproc_i2c_of_match, >> + }, > Inconsistent indention. > Sorry. Will fix. >> + .probe = bcm_iproc_i2c_probe, >> + .remove = bcm_iproc_i2c_remove, >> +}; >> +module_platform_driver(bcm_iproc_i2c_driver); >> + >> +MODULE_AUTHOR("Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>"); >> +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); >> +MODULE_LICENSE("GPL v2"); > ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-14 2:14 ` Ray Jui @ 2015-01-14 7:51 ` Uwe Kleine-König 2015-01-14 20:05 ` Ray Jui [not found] ` <54B5D0F9.8030902-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 1 sibling, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-14 7:51 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree Hello, On Tue, Jan 13, 2015 at 06:14:17PM -0800, Ray Jui wrote: > >> + irq = platform_get_irq(pdev, 0); > >> + if (irq < 0) { > > irq == 0 should be handled as error, too. > > > Ah. I thought zero is a valid global interrupt number, and I see other > drivers checking against < 0 as well. Is my understanding incorrect? These are wrong, too. 0 should never be a valid interrupt number. There are some exceptions but mostly for historic reasons. The right handling is used for example in drivers/i2c/busses/i2c-efm32.c. > >> + dev_err(dev->device, "no irq resource\n"); > >> + return irq; > >> + } > [...] > >> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > >> +{ > >> + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); > >> + > >> + i2c_del_adapter(&dev->adapter); > >> + bcm_iproc_i2c_disable(dev); > > I think you have a problem here if bcm_iproc_i2c_remove is called while > > an irq is still being serviced. I'm not sure how to prevent this > > properly for a shared interrupt. > > > Can I grab i2c_lock_adapter to ensure the bus is locked (so there's no > outstanding transactions or IRQs by the time we remove the adapter)? But > I see no I2C bus driver does this in their remove function... The problem I pointed out is the reason for some driver authors not to use devm_request_irq. If you use plain request_irq and the matching free_irq in the .remove callback you can be sure that the irq isn't running any more as soon as free_irq returns. BTW, if you use vim, you can add set cinoptions=(,: if has("autocmd") filetype plugin indent on endif to your .vimrc. Then while typing vim does the indention right and consistent, and with the = command you can reindent. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-14 7:51 ` Uwe Kleine-König @ 2015-01-14 20:05 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-14 20:05 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree On 1/13/2015 11:51 PM, Uwe Kleine-König wrote: > Hello, > > On Tue, Jan 13, 2015 at 06:14:17PM -0800, Ray Jui wrote: >>>> + irq = platform_get_irq(pdev, 0); >>>> + if (irq < 0) { >>> irq == 0 should be handled as error, too. >>> >> Ah. I thought zero is a valid global interrupt number, and I see other >> drivers checking against < 0 as well. Is my understanding incorrect? > These are wrong, too. 0 should never be a valid interrupt number. There > are some exceptions but mostly for historic reasons. The right handling > is used for example in drivers/i2c/busses/i2c-efm32.c. > Okay. Will check against <= 0. Thanks. >>>> + dev_err(dev->device, "no irq resource\n"); >>>> + return irq; >>>> + } >> [...] >>>> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >>>> +{ >>>> + struct bcm_iproc_i2c_dev *dev = platform_get_drvdata(pdev); >>>> + >>>> + i2c_del_adapter(&dev->adapter); >>>> + bcm_iproc_i2c_disable(dev); >>> I think you have a problem here if bcm_iproc_i2c_remove is called while >>> an irq is still being serviced. I'm not sure how to prevent this >>> properly for a shared interrupt. >>> >> Can I grab i2c_lock_adapter to ensure the bus is locked (so there's no >> outstanding transactions or IRQs by the time we remove the adapter)? But >> I see no I2C bus driver does this in their remove function... > The problem I pointed out is the reason for some driver authors not to > use devm_request_irq. If you use plain request_irq and the matching > free_irq in the .remove callback you can be sure that the irq isn't > running any more as soon as free_irq returns. > Okay. Will change to use request_irq and make sure that it's freed in the remove function. Also, the interrupt is dedicated to the I2C controller, so I'll remove the IRQF_SHARED flag. > BTW, if you use vim, you can add > > set cinoptions=(,: > if has("autocmd") > filetype plugin indent on > endif > > to your .vimrc. Then while typing vim does the indention right and > consistent, and with the = command you can reindent. > Wow this is excellent! Just tried and it works perfectly. Thanks a lot!!! > Best regards > Uwe > ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54B5D0F9.8030902-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54B5D0F9.8030902-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-15 11:59 ` Wolfram Sang 2015-01-16 22:51 ` Ray Jui 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-01-15 11:59 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 1331 bytes --] > >> + case M_CMD_STATUS_LOST_ARB: > >> + dev_err(dev->device, "lost bus arbitration\n"); > > I wouldn't dev_err that, only dev_dbg. I'm not sure how usual the errors > > for the next two cases is, maybe degrade them to dev_dbg, too? > > > These errors are rare, and it's nice to keep them at the dev_err level > so the user will be more aware. This is wrong. Arbitration lost and NACK is pretty standard stuff on an I2C bus. User doesn't need to know about it, it is just noise in the logs. Timeout is different, you can report that (although I should probably move such a message into the core). Please also use the proper errno codes defined in Documentation/i2c/fault-codes. They should be distinct enough to drop the messages. > > >> + return -EREMOTEIO; > >> + > >> + case M_CMD_STATUS_NACK_ADDR: > >> + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); > >> + return -EREMOTEIO; > >> + > >> + case M_CMD_STATUS_NACK_DATA: > >> + dev_err(dev->device, "NAK data\n"); > >> + return -EREMOTEIO; > >> + > >> + case M_CMD_STATUS_TIMEOUT: > >> + dev_err(dev->device, "bus timeout\n"); > >> + return -ETIMEDOUT; > >> + > >> + default: > >> + dev_err(dev->device, "unknown error code=%d\n", val); > >> + return -EREMOTEIO; > >> + } > >> + > >> + return -EREMOTEIO; > >> +} [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-15 11:59 ` Wolfram Sang @ 2015-01-16 22:51 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-16 22:51 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/15/2015 3:59 AM, Wolfram Sang wrote: >>>> + case M_CMD_STATUS_LOST_ARB: >>>> + dev_err(dev->device, "lost bus arbitration\n"); >>> I wouldn't dev_err that, only dev_dbg. I'm not sure how usual the errors >>> for the next two cases is, maybe degrade them to dev_dbg, too? >>> >> These errors are rare, and it's nice to keep them at the dev_err level >> so the user will be more aware. > > This is wrong. Arbitration lost and NACK is pretty standard stuff on an > I2C bus. User doesn't need to know about it, it is just noise in the > logs. Timeout is different, you can report that (although I should > probably move such a message into the core). Please also use the proper > errno codes defined in Documentation/i2c/fault-codes. They should be > distinct enough to drop the messages. > Okay will do. >> >>>> + return -EREMOTEIO; >>>> + >>>> + case M_CMD_STATUS_NACK_ADDR: >>>> + dev_err(dev->device, "NAK addr:0x%02x\n", dev->msg->addr); >>>> + return -EREMOTEIO; >>>> + >>>> + case M_CMD_STATUS_NACK_DATA: >>>> + dev_err(dev->device, "NAK data\n"); >>>> + return -EREMOTEIO; >>>> + >>>> + case M_CMD_STATUS_TIMEOUT: >>>> + dev_err(dev->device, "bus timeout\n"); >>>> + return -ETIMEDOUT; >>>> + >>>> + default: >>>> + dev_err(dev->device, "unknown error code=%d\n", val); >>>> + return -EREMOTEIO; >>>> + } >>>> + >>>> + return -EREMOTEIO; >>>> +} ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v3 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2014-12-10 3:57 ` [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2014-12-10 3:57 ` Ray Jui [not found] ` <548F577E.7020207@broadcom.com> 3 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-10 3:57 UTC (permalink / raw) To: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..f7d6c1d 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <548F577E.7020207@broadcom.com>]
[parent not found: <548F577E.7020207-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v3 0/3] Add I2C support to Broadcom iProc [not found] ` <548F577E.7020207-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2014-12-15 21:55 ` Wolfram Sang 2014-12-16 0:12 ` Ray Jui 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2014-12-15 21:55 UTC (permalink / raw) To: Ray Jui; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 779 bytes --] Hi, please don't drop the i2c-list. > I guess you are probably extremely busy and might not have had a chance to > look into the iProc I2C driver v3 patch that was submitted on December 9. > I'm not trying to rush it, and am perfectly fine with waiting longer. I > understand subsystem maintainers like you are usually very busy. True, true. > As I'm quite new to the upstreaming process, here I'd just like to find out > in a bit more details on what to expect and how to proceed forward. We are in the merge window currently, and I will start looking at new patches probably around rc2 or so. If you want to speed up things, check the reviews of other new drivers I did in the recent past and check if some of the remarks also apply to your driver. Thanks, Wolfram [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v3 0/3] Add I2C support to Broadcom iProc 2014-12-15 21:55 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Wolfram Sang @ 2014-12-16 0:12 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2014-12-16 0:12 UTC (permalink / raw) To: Wolfram Sang; +Cc: linux-i2c-u79uwXL29TY76Z2rM5mHXA Hi Wolfram, Thanks for the reply. I'll go through your recent reviews and if I find any comment that applies to my driver I'll make the change. Thanks, Ray On 12/15/2014 1:55 PM, Wolfram Sang wrote: > Hi, > > please don't drop the i2c-list. > >> I guess you are probably extremely busy and might not have had a chance to >> look into the iProc I2C driver v3 patch that was submitted on December 9. >> I'm not trying to rush it, and am perfectly fine with waiting longer. I >> understand subsystem maintainers like you are usually very busy. > > True, true. > >> As I'm quite new to the upstreaming process, here I'd just like to find out >> in a bit more details on what to expect and how to proceed forward. > > We are in the merge window currently, and I will start looking at new > patches probably around rc2 or so. > > If you want to speed up things, check the reviews of other new drivers I > did in the recent past and check if some of the remarks also apply to > your driver. > > Thanks, > > Wolfram > ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v4 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> ` (2 preceding siblings ...) 2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-01-14 22:23 ` Ray Jui 2015-01-14 22:23 ` [PATCH v4 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui ` (2 more replies) 2015-01-16 23:42 ` [PATCH v5 0/3] Add I2C support to Broadcom iProc Ray Jui ` (4 subsequent siblings) 8 siblings, 3 replies; 106+ messages in thread From: Ray Jui @ 2015-01-14 22:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v3: - Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms - Improve CPU utilization efficiency in the loop of waiting for bus to idle - Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller - Fix inconsistent coding style and format - Improve the bus speed validation logic in the driver - Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line - Other miscellaneous improvements and fixes Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 503 ++++++++++++++++++++ 5 files changed, 571 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v4 1/3] i2c: iProc: define Broadcom iProc I2C binding 2015-01-14 22:23 ` [PATCH v4 " Ray Jui @ 2015-01-14 22:23 ` Ray Jui [not found] ` <1421274213-3544-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-14 22:23 ` [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-14 22:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1421274213-3544-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1421274213-3544-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-14 22:23 ` Ray Jui [not found] ` <1421274213-3544-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-14 22:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 503 ++++++++++++++++++++++++++++++++++++ 3 files changed, 514 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 31e8308..af76d23 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 56388f6..d93b509 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..7d9ed4e --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,503 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIME_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + int irq; + + void __iomem *base; + struct i2c_msg *msg; + + struct i2c_adapter adapter; + + struct completion done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = data; + u32 status = readl(iproc_i2c->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, iproc_i2c->base + IS_OFFSET); + complete_all(&iproc_i2c->done); + + return IRQ_HANDLED; +} + +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(I2C_TIMEOUT_MESC); + + while (readl(iproc_i2c->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) { + if (time_after(jiffies, timeout)) { + dev_err(iproc_i2c->device, + "wait for bus idle timeout\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + return 0; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = (msg->addr << 1); + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_err(iproc_i2c->device, "lost bus arbitration\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_ADDR: + dev_err(iproc_i2c->device, "NAK addr:0x%02x\n", + iproc_i2c->msg->addr); + return -EREMOTEIO; + + case M_CMD_STATUS_NACK_DATA: + dev_err(iproc_i2c->device, "NAK data\n"); + return -EREMOTEIO; + + case M_CMD_STATUS_TIMEOUT: + dev_err(iproc_i2c->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_err(iproc_i2c->device, "unknown error code=%d\n", val); + return -EREMOTEIO; + } + + return -EREMOTEIO; +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(iproc_i2c->device, + "supported data length is 1 - %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + iproc_i2c->msg = msg; + ret = __wait_for_bus_idle(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, iproc_i2c->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, iproc_i2c->base + M_TX_OFFSET); + } + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&iproc_i2c->done); + + /* + * Enable the "start busy" interrupt, which will be triggered after + * the transaction is done, i.e., the internal start_busy bit + * transitions from 1 to 0 + */ + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, iproc_i2c->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + if (!time_left) { + dev_err(iproc_i2c->device, "transaction times out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return -EREMOTEIO; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(iproc_i2c->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); + dev_dbg(iproc_i2c->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); + if (ret) { + dev_err(iproc_i2c->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_err(iproc_i2c->device, + "missing clock-frequency property\n"); + return -ENODEV; + } + + if (bus_speed < 100000) { + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(iproc_i2c->device, + "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } else if (bus_speed < 400000) { + speed_bit = 0; + } else { + /* bus_speed >= 400000 */ + speed_bit = 1; + } + + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIME_CFG_MODE_400_SHIFT); + val |= speed_bit << TIME_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + /* clear all pending interrupts */ + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + struct resource *res; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + init_completion(&iproc_i2c->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(iproc_i2c->device, "no irq resource\n"); + return irq; + } + iproc_i2c->irq = irq; + + ret = request_irq(irq, bcm_iproc_i2c_isr, 0, pdev->name, iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(iproc_i2c); + + adap = &iproc_i2c->adapter; + i2c_set_adapdata(adap, iproc_i2c); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(iproc_i2c->device, "failed to add adapter\n"); + free_irq(iproc_i2c->irq, iproc_i2c); + return ret; + } + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&iproc_i2c->adapter); + free_irq(iproc_i2c->irq, iproc_i2c); + bcm_iproc_i2c_disable(iproc_i2c); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + {.compatible = "brcm,iproc-i2c",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1421274213-3544-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1421274213-3544-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-15 8:41 ` Uwe Kleine-König [not found] ` <20150115084119.GN22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-15 8:41 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: > +#include <linux/device.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/sched.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/platform_device.h> > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> some of them are not needed. I tested on amd64 and efm32 and could drop linux/device.h, linux/sched.h, linux/clk.h. (BTW, I wonder that you don't need clk handling.) > +#define TIM_CFG_OFFSET 0x04 > +#define TIME_CFG_MODE_400_SHIFT 31 Is the register name and the bit name prefix really different or is this a typo? > +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *iproc_i2c) A bcm_iproc_i2c prefix would be nice here. > +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, > + struct i2c_msg *msg, u8 *addr) > +{ > + > + if (msg->flags & I2C_M_TEN) { > + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); > + return -EINVAL; > + } > + > + *addr = (msg->addr << 1); You can also drop the parentheses. > + switch (val) { > + case M_CMD_STATUS_SUCCESS: > + return 0; > + > + case M_CMD_STATUS_LOST_ARB: > + dev_err(iproc_i2c->device, "lost bus arbitration\n"); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_NACK_ADDR: > + dev_err(iproc_i2c->device, "NAK addr:0x%02x\n", > + iproc_i2c->msg->addr); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_NACK_DATA: > + dev_err(iproc_i2c->device, "NAK data\n"); > + return -EREMOTEIO; > + > + case M_CMD_STATUS_TIMEOUT: > + dev_err(iproc_i2c->device, "bus timeout\n"); > + return -ETIMEDOUT; > + > + default: > + dev_err(iproc_i2c->device, "unknown error code=%d\n", val); > + return -EREMOTEIO; > + } > + > + return -EREMOTEIO; This is not reached. > +} > + > +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, > + struct i2c_msg *msg) > +{ > + int ret, i; > + u8 addr; > + u32 val; > + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); > + > + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { Is the < 1 a hardware or a software limitation? That means your driver doesn't support I2C_SMBUS_QUICK which is used for example by i2cdetect. > + dev_err(iproc_i2c->device, > + "supported data length is 1 - %u bytes\n", > + M_TX_RX_FIFO_SIZE - 1); > + return -EINVAL; > + } > + > + iproc_i2c->msg = msg; Can it happen that iproc_i2c->msg still holds an uncompleted message here or is this serialized by the core? Wolfram? Either here something like: if (iproc_i2c->msg) return -EBUSY; and iproc_i2c->msg = NULL; when a transfer is completed is needed, or the respective code can be dropped from other drivers (e.g. i2c-efm32). On the other hand .msg is only used in bcm_iproc_i2c_check_status() to give a diagnostic message. Maybe you can drop .msg and instead give it as an additional parameter to bcm_iproc_i2c_check_status(). > + ret = __wait_for_bus_idle(iproc_i2c); > + if (ret) > + return ret; I would still prefer to have something like: if (bcm_iproc_i2c_bus_busy()) return -EBUSY; instead of a tight loop here. > + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); > + if (ret) > + return ret; > + > + /* load slave address into the TX FIFO */ > + writel(addr, iproc_i2c->base + M_TX_OFFSET); > + > + /* for a write transaction, load data into the TX FIFO */ > + if (!(msg->flags & I2C_M_RD)) { > + for (i = 0; i < msg->len; i++) { > + val = msg->buf[i]; > + > + /* mark the last byte */ > + if (i == msg->len - 1) > + val |= 1 << M_TX_WR_STATUS_SHIFT; What happens if you don't mark this last byte? Could this be used to support transfers bigger than the fifo size? > + /* > + * Enable the "start busy" interrupt, which will be triggered after > + * the transaction is done, i.e., the internal start_busy bit s/\.,/./ I think > + * transitions from 1 to 0 s/$/./ > + */ > + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); > + > + /* > + * Now we can activate the transfer. For a read operation, specify the > + * number of bytes to read s/$/./ > + */ > + val = 1 << M_CMD_START_BUSY_SHIFT; > + if (msg->flags & I2C_M_RD) { > + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > + (msg->len << M_CMD_RD_CNT_SHIFT); > + } else { > + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > + } > + writel(val, iproc_i2c->base + M_CMD_OFFSET); > + > + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); When the interrupt fires here after the complete timed out and before you disable the irq you still throw the result away. > + > + /* disable all interrupts */ > + writel(0, iproc_i2c->base + IE_OFFSET); > + > + if (!time_left) { > + dev_err(iproc_i2c->device, "transaction times out\n"); s/times/timed/ > +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) > +{ > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; Note that I2C_FUNC_SMBUS_EMUL includes I2C_FUNC_SMBUS_QUICK, so your driver claims to support transfers of length 0. > +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + unsigned int bus_speed, speed_bit; > + u32 val; > + int ret = of_property_read_u32(iproc_i2c->device->of_node, > + "clock-frequency", &bus_speed); > + if (ret < 0) { > + dev_err(iproc_i2c->device, > + "missing clock-frequency property\n"); > + return -ENODEV; Is a missing property the only situation where of_property_read_u32 returns an error? Would it be sane to default to 100 kHz? > +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > +{ > + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); > + > + i2c_del_adapter(&iproc_i2c->adapter); You need to free the irq before i2c_del_adapter. > + free_irq(iproc_i2c->irq, iproc_i2c); > + bcm_iproc_i2c_disable(iproc_i2c); > + > + return 0; > +} > + > +static const struct of_device_id bcm_iproc_i2c_of_match[] = { > + {.compatible = "brcm,iproc-i2c",}, Not sure this is specified to be a must, but I'd add spaces after { and before }. > + {}, It's a good habit to write this as { /* sentinel */ } without trailing comma. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150115084119.GN22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150115084119.GN22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-15 12:07 ` Wolfram Sang 2015-01-15 16:32 ` Uwe Kleine-König 2015-01-16 22:52 ` Ray Jui 2015-01-16 22:09 ` Ray Jui 1 sibling, 2 replies; 106+ messages in thread From: Wolfram Sang @ 2015-01-15 12:07 UTC (permalink / raw) To: Uwe Kleine-König Cc: Ray Jui, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 1188 bytes --] > > + iproc_i2c->msg = msg; > Can it happen that iproc_i2c->msg still holds an uncompleted message > here or is this serialized by the core? Wolfram? Either here something We have per-adapter locks serializing transfers, if you mean that? > > +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) > > +{ > > + unsigned int bus_speed, speed_bit; > > + u32 val; > > + int ret = of_property_read_u32(iproc_i2c->device->of_node, > > + "clock-frequency", &bus_speed); > > + if (ret < 0) { > > + dev_err(iproc_i2c->device, > > + "missing clock-frequency property\n"); > > + return -ENODEV; > Is a missing property the only situation where of_property_read_u32 > returns an error? Would it be sane to default to 100 kHz? Default of 100kHz instead of -ENODEV sounds very reasonable. > > +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > > +{ > > + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); > > + > > + i2c_del_adapter(&iproc_i2c->adapter); > You need to free the irq before i2c_del_adapter. One could also keep using devm_request_irq and disable all interrupts sources here? Thanks for the reviews, Uwe! Wolfram [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-15 12:07 ` Wolfram Sang @ 2015-01-15 16:32 ` Uwe Kleine-König 2015-01-16 22:52 ` Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-15 16:32 UTC (permalink / raw) To: Wolfram Sang Cc: Ray Jui, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Thu, Jan 15, 2015 at 01:07:05PM +0100, Wolfram Sang wrote: > > > + iproc_i2c->msg = msg; > > Can it happen that iproc_i2c->msg still holds an uncompleted message > > here or is this serialized by the core? Wolfram? Either here something > > We have per-adapter locks serializing transfers, if you mean that? ok, so in the efm32 driver the if (ddata->msgs) condition in efm32_i2c_master_xfer can never be true, right? > > > +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > > > +{ > > > + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); > > > + > > > + i2c_del_adapter(&iproc_i2c->adapter); > > You need to free the irq before i2c_del_adapter. > > One could also keep using devm_request_irq and disable all interrupts > sources here? calling devm_free_irq would work or writel(0, iproc_i2c->base + IE_OFFSET) + synchronize_irq(). Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-15 12:07 ` Wolfram Sang 2015-01-15 16:32 ` Uwe Kleine-König @ 2015-01-16 22:52 ` Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-16 22:52 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/15/2015 4:07 AM, Wolfram Sang wrote: >>> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >>> +{ >>> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); >>> + >>> + i2c_del_adapter(&iproc_i2c->adapter); >> You need to free the irq before i2c_del_adapter. > > One could also keep using devm_request_irq and disable all interrupts > sources here? > Okay this makes sense. Will use devm_request_irq and disable interrupt in the remove function. Thanks. > Thanks for the reviews, Uwe! > > Wolfram > ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150115084119.GN22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-15 12:07 ` Wolfram Sang @ 2015-01-16 22:09 ` Ray Jui [not found] ` <54B98C18.4080807-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 1 sibling, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-16 22:09 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: > Hello, > > On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: >> +#include <linux/device.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/sched.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/platform_device.h> >> +#include <linux/clk.h> >> +#include <linux/io.h> >> +#include <linux/slab.h> >> +#include <linux/delay.h> > some of them are not needed. I tested on amd64 and efm32 and could drop > linux/device.h, linux/sched.h, linux/clk.h. (BTW, I wonder that you > don't need clk handling.) Thanks. Will delete redundant header includes. I haven't found any clocks in Cygnus clock data sheet that are labeled i2c. I suspect the I2C clock is derived directly from the crystal and therefore we have no gating control. As you can see, the rates of 100K and 400K are set directly in the I2C block internal registers. That implies the I2C core clock is fixed. > >> +#define TIM_CFG_OFFSET 0x04 >> +#define TIME_CFG_MODE_400_SHIFT 31 > Is the register name and the bit name prefix really different or is this > a typo? > Yeah, typo. Will fix. >> +static int __wait_for_bus_idle(struct bcm_iproc_i2c_dev *iproc_i2c) > A bcm_iproc_i2c prefix would be nice here. > >> +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, >> + struct i2c_msg *msg, u8 *addr) >> +{ >> + >> + if (msg->flags & I2C_M_TEN) { >> + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); >> + return -EINVAL; >> + } >> + >> + *addr = (msg->addr << 1); > You can also drop the parentheses. > Yes >> + switch (val) { >> + case M_CMD_STATUS_SUCCESS: >> + return 0; >> + >> + case M_CMD_STATUS_LOST_ARB: >> + dev_err(iproc_i2c->device, "lost bus arbitration\n"); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_NACK_ADDR: >> + dev_err(iproc_i2c->device, "NAK addr:0x%02x\n", >> + iproc_i2c->msg->addr); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_NACK_DATA: >> + dev_err(iproc_i2c->device, "NAK data\n"); >> + return -EREMOTEIO; >> + >> + case M_CMD_STATUS_TIMEOUT: >> + dev_err(iproc_i2c->device, "bus timeout\n"); >> + return -ETIMEDOUT; >> + >> + default: >> + dev_err(iproc_i2c->device, "unknown error code=%d\n", val); >> + return -EREMOTEIO; >> + } >> + >> + return -EREMOTEIO; > This is not reached. > Will delete. >> +} >> + >> +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, >> + struct i2c_msg *msg) >> +{ >> + int ret, i; >> + u8 addr; >> + u32 val; >> + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); >> + >> + if (msg->len < 1 || msg->len > M_TX_RX_FIFO_SIZE - 1) { > Is the < 1 a hardware or a software limitation? That means your driver > doesn't support I2C_SMBUS_QUICK which is used for example by i2cdetect. > Actually a SW issue. Will fix. >> + dev_err(iproc_i2c->device, >> + "supported data length is 1 - %u bytes\n", >> + M_TX_RX_FIFO_SIZE - 1); >> + return -EINVAL; >> + } >> + >> + iproc_i2c->msg = msg; > Can it happen that iproc_i2c->msg still holds an uncompleted message > here or is this serialized by the core? Wolfram? Either here something > like: > > if (iproc_i2c->msg) > return -EBUSY; > > and > > iproc_i2c->msg = NULL; > > when a transfer is completed is needed, or the respective code can be > dropped from other drivers (e.g. i2c-efm32). > On the other hand .msg is only used in bcm_iproc_i2c_check_status() to > give a diagnostic message. Maybe you can drop .msg and instead give it > as an additional parameter to bcm_iproc_i2c_check_status(). > Yes, I'll drop .msg in iproc_i2c. >> + ret = __wait_for_bus_idle(iproc_i2c); >> + if (ret) >> + return ret; > I would still prefer to have something like: > > if (bcm_iproc_i2c_bus_busy()) > return -EBUSY; > > instead of a tight loop here. > Okay. Will do. >> + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); >> + if (ret) >> + return ret; >> + >> + /* load slave address into the TX FIFO */ >> + writel(addr, iproc_i2c->base + M_TX_OFFSET); >> + >> + /* for a write transaction, load data into the TX FIFO */ >> + if (!(msg->flags & I2C_M_RD)) { >> + for (i = 0; i < msg->len; i++) { >> + val = msg->buf[i]; >> + >> + /* mark the last byte */ >> + if (i == msg->len - 1) >> + val |= 1 << M_TX_WR_STATUS_SHIFT; > What happens if you don't mark this last byte? Could this be used to > support transfers bigger than the fifo size? > I do not think so. According to the iProc I2C block programming guide, one always needs to mark the last byte in a write operation. >> + /* >> + * Enable the "start busy" interrupt, which will be triggered after >> + * the transaction is done, i.e., the internal start_busy bit > s/\.,/./ I think > >> + * transitions from 1 to 0 > s/$/./ Thanks. >> + */ >> + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); >> + >> + /* >> + * Now we can activate the transfer. For a read operation, specify the >> + * number of bytes to read > s/$/./ > >> + */ >> + val = 1 << M_CMD_START_BUSY_SHIFT; >> + if (msg->flags & I2C_M_RD) { >> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >> + (msg->len << M_CMD_RD_CNT_SHIFT); >> + } else { >> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >> + } >> + writel(val, iproc_i2c->base + M_CMD_OFFSET); >> + >> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > > When the interrupt fires here after the complete timed out and before > you disable the irq you still throw the result away. Yes, but then this comes down to the fact that if it has reached the point that is determined to be a timeout condition in the driver, one should really treat it as timeout error. In a normal condition, time_left should never reach zero. >> + >> + /* disable all interrupts */ >> + writel(0, iproc_i2c->base + IE_OFFSET); >> + >> + if (!time_left) { >> + dev_err(iproc_i2c->device, "transaction times out\n"); > s/times/timed/ > Thanks. >> +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) >> +{ >> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; > Note that I2C_FUNC_SMBUS_EMUL includes I2C_FUNC_SMBUS_QUICK, so your > driver claims to support transfers of length 0. > Yes. Fix the driver to support length 0 for slave address query. >> +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + unsigned int bus_speed, speed_bit; >> + u32 val; >> + int ret = of_property_read_u32(iproc_i2c->device->of_node, >> + "clock-frequency", &bus_speed); >> + if (ret < 0) { >> + dev_err(iproc_i2c->device, >> + "missing clock-frequency property\n"); >> + return -ENODEV; > Is a missing property the only situation where of_property_read_u32 > returns an error? Would it be sane to default to 100 kHz? > Okay, agreed with you and Wolfram. Will default to 100 KHz. >> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >> +{ >> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); >> + >> + i2c_del_adapter(&iproc_i2c->adapter); > You need to free the irq before i2c_del_adapter. > Yes. Thanks. Change back to use devm_request_irq, and use disable_irq here before removing the adapter. >> + free_irq(iproc_i2c->irq, iproc_i2c); >> + bcm_iproc_i2c_disable(iproc_i2c); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id bcm_iproc_i2c_of_match[] = { >> + {.compatible = "brcm,iproc-i2c",}, > Not sure this is specified to be a must, but I'd add spaces after { and > before }. > Yes. >> + {}, > It's a good habit to write this as > > { /* sentinel */ } > > without trailing comma. Okay. > > Best regards > Uwe > Thanks for the review! ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54B98C18.4080807-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54B98C18.4080807-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-17 16:01 ` Uwe Kleine-König [not found] ` <20150117160113.GA22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-17 16:01 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Fri, Jan 16, 2015 at 02:09:28PM -0800, Ray Jui wrote: > On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: > > On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: > >> + */ > >> + val = 1 << M_CMD_START_BUSY_SHIFT; > >> + if (msg->flags & I2C_M_RD) { > >> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > >> + (msg->len << M_CMD_RD_CNT_SHIFT); > >> + } else { > >> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > >> + } > >> + writel(val, iproc_i2c->base + M_CMD_OFFSET); > >> + > >> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > > > > When the interrupt fires here after the complete timed out and before > > you disable the irq you still throw the result away. > Yes, but then this comes down to the fact that if it has reached the > point that is determined to be a timeout condition in the driver, one > should really treat it as timeout error. In a normal condition, > time_left should never reach zero. I don't agree here. I'm not sure there is a real technical reason, though. But still if you're in a "success after timeout already over" situation it's IMHO better to interpret it as success, not timeout. > >> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > >> +{ > >> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); > >> + > >> + i2c_del_adapter(&iproc_i2c->adapter); > > You need to free the irq before i2c_del_adapter. > > > Yes. Thanks. Change back to use devm_request_irq, and use disable_irq > here before removing the adapter. The more lightweight approach is to set your device's irq-enable register to zero and call synchronize_irq. (For a shared irq calling disable_irq is even wrong here.) Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150117160113.GA22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150117160113.GA22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-17 19:58 ` Ray Jui [not found] ` <54BABEE9.8070801-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-17 19:58 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/17/2015 8:01 AM, Uwe Kleine-König wrote: > Hello, > > On Fri, Jan 16, 2015 at 02:09:28PM -0800, Ray Jui wrote: >> On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: >>> On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: >>>> + */ >>>> + val = 1 << M_CMD_START_BUSY_SHIFT; >>>> + if (msg->flags & I2C_M_RD) { >>>> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >>>> + (msg->len << M_CMD_RD_CNT_SHIFT); >>>> + } else { >>>> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >>>> + } >>>> + writel(val, iproc_i2c->base + M_CMD_OFFSET); >>>> + >>>> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >>> >>> When the interrupt fires here after the complete timed out and before >>> you disable the irq you still throw the result away. >> Yes, but then this comes down to the fact that if it has reached the >> point that is determined to be a timeout condition in the driver, one >> should really treat it as timeout error. In a normal condition, >> time_left should never reach zero. > I don't agree here. I'm not sure there is a real technical reason, > though. But still if you're in a "success after timeout already over" > situation it's IMHO better to interpret it as success, not timeout. > The thing is, the interrupt should never fire after wait_for_completion_timeout returns zero here. If it does, then the issue is really that the timeout value set in the driver is probably not long enough. I just checked other I2C drivers. I think the way how timeout is handled here is consistent with other I2C drivers. >>>> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >>>> +{ >>>> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); >>>> + >>>> + i2c_del_adapter(&iproc_i2c->adapter); >>> You need to free the irq before i2c_del_adapter. >>> >> Yes. Thanks. Change back to use devm_request_irq, and use disable_irq >> here before removing the adapter. > The more lightweight approach is to set your device's irq-enable > register to zero and call synchronize_irq. (For a shared irq calling > disable_irq is even wrong here.) > The fact that IRQF_SHARED flag is not set indicates this is a dedicated IRQ line, so I thought using disable_irq here makes sense. But if both you and Wolfram think masking all I2C interrupts at the block level + synchronize_irq is a better approach, I can change to that. Thanks! > Best regards > Uwe > ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54BABEE9.8070801-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54BABEE9.8070801-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-17 20:18 ` Uwe Kleine-König [not found] ` <20150117201849.GC22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-17 20:18 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Sat, Jan 17, 2015 at 11:58:33AM -0800, Ray Jui wrote: > On 1/17/2015 8:01 AM, Uwe Kleine-König wrote: > > On Fri, Jan 16, 2015 at 02:09:28PM -0800, Ray Jui wrote: > >> On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: > >>> On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: > >>>> + */ > >>>> + val = 1 << M_CMD_START_BUSY_SHIFT; > >>>> + if (msg->flags & I2C_M_RD) { > >>>> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > >>>> + (msg->len << M_CMD_RD_CNT_SHIFT); > >>>> + } else { > >>>> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > >>>> + } > >>>> + writel(val, iproc_i2c->base + M_CMD_OFFSET); > >>>> + > >>>> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > >>> > >>> When the interrupt fires here after the complete timed out and before > >>> you disable the irq you still throw the result away. > >> Yes, but then this comes down to the fact that if it has reached the > >> point that is determined to be a timeout condition in the driver, one > >> should really treat it as timeout error. In a normal condition, > >> time_left should never reach zero. > > I don't agree here. I'm not sure there is a real technical reason, > > though. But still if you're in a "success after timeout already over" > > situation it's IMHO better to interpret it as success, not timeout. > > > The thing is, the interrupt should never fire after > wait_for_completion_timeout returns zero here. If it does, then the > issue is really that the timeout value set in the driver is probably not > long enough. I just checked other I2C drivers. I think the way how > timeout is handled here is consistent with other I2C drivers. In the presence of Clock stretching there is no (theorethical) upper limit for the time needed to transfer a given message, is there? So (theoretically) you can never be sure not to interrupt an ongoing transfer. And other drivers doing the same is only an excuse to start similar, but not to not improve :-) > >>>> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > >>>> +{ > >>>> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); > >>>> + > >>>> + i2c_del_adapter(&iproc_i2c->adapter); > >>> You need to free the irq before i2c_del_adapter. > >>> > >> Yes. Thanks. Change back to use devm_request_irq, and use disable_irq > >> here before removing the adapter. > > The more lightweight approach is to set your device's irq-enable > > register to zero and call synchronize_irq. (For a shared irq calling > > disable_irq is even wrong here.) > > > The fact that IRQF_SHARED flag is not set indicates this is a dedicated > IRQ line, so I thought using disable_irq here makes sense. But if both > you and Wolfram think masking all I2C interrupts at the block level + > synchronize_irq is a better approach, I can change to that. Thanks! I don't care much. Using synchronize_irq is the more universal approach and so more likely correct for someone copying from your driver. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150117201849.GC22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150117201849.GC22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-17 20:51 ` Ray Jui [not found] ` <54BACB66.6040909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-17 20:51 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/17/2015 12:18 PM, Uwe Kleine-König wrote: > Hello, > > On Sat, Jan 17, 2015 at 11:58:33AM -0800, Ray Jui wrote: >> On 1/17/2015 8:01 AM, Uwe Kleine-König wrote: >>> On Fri, Jan 16, 2015 at 02:09:28PM -0800, Ray Jui wrote: >>>> On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: >>>>> On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: >>>>>> + */ >>>>>> + val = 1 << M_CMD_START_BUSY_SHIFT; >>>>>> + if (msg->flags & I2C_M_RD) { >>>>>> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >>>>>> + (msg->len << M_CMD_RD_CNT_SHIFT); >>>>>> + } else { >>>>>> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >>>>>> + } >>>>>> + writel(val, iproc_i2c->base + M_CMD_OFFSET); >>>>>> + >>>>>> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >>>>> >>>>> When the interrupt fires here after the complete timed out and before >>>>> you disable the irq you still throw the result away. >>>> Yes, but then this comes down to the fact that if it has reached the >>>> point that is determined to be a timeout condition in the driver, one >>>> should really treat it as timeout error. In a normal condition, >>>> time_left should never reach zero. >>> I don't agree here. I'm not sure there is a real technical reason, >>> though. But still if you're in a "success after timeout already over" >>> situation it's IMHO better to interpret it as success, not timeout. >>> >> The thing is, the interrupt should never fire after >> wait_for_completion_timeout returns zero here. If it does, then the >> issue is really that the timeout value set in the driver is probably not >> long enough. I just checked other I2C drivers. I think the way how >> timeout is handled here is consistent with other I2C drivers. > In the presence of Clock stretching there is no (theorethical) upper > limit for the time needed to transfer a given message, is there? So > (theoretically) you can never be sure not to interrupt an ongoing > transfer. > Yes. No theoretical upper limit in the case when clock is stretched by the slave. But how would adding an additional interrupt completion check below help? I assume you want the the check to be like the following? time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); /* disable all interrupts */ writel(0, iproc_i2c->base + IE_OFFSET); if (!time_left && !completion_done()) { dev_err(iproc_i2c->device, "transaction timed out\n"); /* flush FIFOs */ val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); return -ETIMEDOUT; } Does the above code make sense logically? That is, wait_for_completion_timeout has timed out, and we are doing an additional check below to make sure it really has timed out? > And other drivers doing the same is only an excuse to start similar, but > not to not improve :-) > >>>>>> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >>>>>> +{ >>>>>> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); >>>>>> + >>>>>> + i2c_del_adapter(&iproc_i2c->adapter); >>>>> You need to free the irq before i2c_del_adapter. >>>>> >>>> Yes. Thanks. Change back to use devm_request_irq, and use disable_irq >>>> here before removing the adapter. >>> The more lightweight approach is to set your device's irq-enable >>> register to zero and call synchronize_irq. (For a shared irq calling >>> disable_irq is even wrong here.) >>> >> The fact that IRQF_SHARED flag is not set indicates this is a dedicated >> IRQ line, so I thought using disable_irq here makes sense. But if both >> you and Wolfram think masking all I2C interrupts at the block level + >> synchronize_irq is a better approach, I can change to that. Thanks! > I don't care much. Using synchronize_irq is the more universal approach > and so more likely correct for someone copying from your driver. > Sure, more universal approach and a good example for others. It takes care of both cases of dedicated and shared interrupt. I will make that change. Thanks. > Best regards > Uwe > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54BACB66.6040909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54BACB66.6040909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-17 21:10 ` Uwe Kleine-König [not found] ` <20150117211017.GD22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-17 21:10 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Sat, Jan 17, 2015 at 12:51:50PM -0800, Ray Jui wrote: > On 1/17/2015 12:18 PM, Uwe Kleine-König wrote: > > Hello, > > > > On Sat, Jan 17, 2015 at 11:58:33AM -0800, Ray Jui wrote: > >> On 1/17/2015 8:01 AM, Uwe Kleine-König wrote: > >>> On Fri, Jan 16, 2015 at 02:09:28PM -0800, Ray Jui wrote: > >>>> On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: > >>>>> On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: > >>>>>> + */ > >>>>>> + val = 1 << M_CMD_START_BUSY_SHIFT; > >>>>>> + if (msg->flags & I2C_M_RD) { > >>>>>> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > >>>>>> + (msg->len << M_CMD_RD_CNT_SHIFT); > >>>>>> + } else { > >>>>>> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > >>>>>> + } > >>>>>> + writel(val, iproc_i2c->base + M_CMD_OFFSET); > >>>>>> + > >>>>>> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > >>>>> > >>>>> When the interrupt fires here after the complete timed out and before > >>>>> you disable the irq you still throw the result away. > >>>> Yes, but then this comes down to the fact that if it has reached the > >>>> point that is determined to be a timeout condition in the driver, one > >>>> should really treat it as timeout error. In a normal condition, > >>>> time_left should never reach zero. > >>> I don't agree here. I'm not sure there is a real technical reason, > >>> though. But still if you're in a "success after timeout already over" > >>> situation it's IMHO better to interpret it as success, not timeout. > >>> > >> The thing is, the interrupt should never fire after > >> wait_for_completion_timeout returns zero here. If it does, then the > >> issue is really that the timeout value set in the driver is probably not > >> long enough. I just checked other I2C drivers. I think the way how > >> timeout is handled here is consistent with other I2C drivers. > > In the presence of Clock stretching there is no (theorethical) upper > > limit for the time needed to transfer a given message, is there? So > > (theoretically) you can never be sure not to interrupt an ongoing > > transfer. > > > Yes. No theoretical upper limit in the case when clock is stretched by > the slave. But how would adding an additional interrupt completion check > below help? I assume you want the the check to be like the following? > > time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > > /* disable all interrupts */ > writel(0, iproc_i2c->base + IE_OFFSET); > > if (!time_left && !completion_done()) { > dev_err(iproc_i2c->device, "transaction timed out\n"); > > /* flush FIFOs */ > val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > (1 << M_FIFO_TX_FLUSH_SHIFT); > writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); > return -ETIMEDOUT; > } No, I want: time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); if (!transfer_was_complete) { handle_error(); ... } handle_successful_transfer(); and time_left == 0 is not a reliable indicator that the transfer failed. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150117211017.GD22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150117211017.GD22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-17 21:26 ` Ray Jui [not found] ` <54BAD391.9080909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-17 21:26 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/17/2015 1:10 PM, Uwe Kleine-König wrote: > Hello, > > On Sat, Jan 17, 2015 at 12:51:50PM -0800, Ray Jui wrote: >> On 1/17/2015 12:18 PM, Uwe Kleine-König wrote: >>> Hello, >>> >>> On Sat, Jan 17, 2015 at 11:58:33AM -0800, Ray Jui wrote: >>>> On 1/17/2015 8:01 AM, Uwe Kleine-König wrote: >>>>> On Fri, Jan 16, 2015 at 02:09:28PM -0800, Ray Jui wrote: >>>>>> On 1/15/2015 12:41 AM, Uwe Kleine-König wrote: >>>>>>> On Wed, Jan 14, 2015 at 02:23:32PM -0800, Ray Jui wrote: >>>>>>>> + */ >>>>>>>> + val = 1 << M_CMD_START_BUSY_SHIFT; >>>>>>>> + if (msg->flags & I2C_M_RD) { >>>>>>>> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >>>>>>>> + (msg->len << M_CMD_RD_CNT_SHIFT); >>>>>>>> + } else { >>>>>>>> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >>>>>>>> + } >>>>>>>> + writel(val, iproc_i2c->base + M_CMD_OFFSET); >>>>>>>> + >>>>>>>> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >>>>>>> >>>>>>> When the interrupt fires here after the complete timed out and before >>>>>>> you disable the irq you still throw the result away. >>>>>> Yes, but then this comes down to the fact that if it has reached the >>>>>> point that is determined to be a timeout condition in the driver, one >>>>>> should really treat it as timeout error. In a normal condition, >>>>>> time_left should never reach zero. >>>>> I don't agree here. I'm not sure there is a real technical reason, >>>>> though. But still if you're in a "success after timeout already over" >>>>> situation it's IMHO better to interpret it as success, not timeout. >>>>> >>>> The thing is, the interrupt should never fire after >>>> wait_for_completion_timeout returns zero here. If it does, then the >>>> issue is really that the timeout value set in the driver is probably not >>>> long enough. I just checked other I2C drivers. I think the way how >>>> timeout is handled here is consistent with other I2C drivers. >>> In the presence of Clock stretching there is no (theorethical) upper >>> limit for the time needed to transfer a given message, is there? So >>> (theoretically) you can never be sure not to interrupt an ongoing >>> transfer. >>> >> Yes. No theoretical upper limit in the case when clock is stretched by >> the slave. But how would adding an additional interrupt completion check >> below help? I assume you want the the check to be like the following? >> >> time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >> >> /* disable all interrupts */ >> writel(0, iproc_i2c->base + IE_OFFSET); >> >> if (!time_left && !completion_done()) { >> dev_err(iproc_i2c->device, "transaction timed out\n"); >> >> /* flush FIFOs */ >> val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> (1 << M_FIFO_TX_FLUSH_SHIFT); >> writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); >> return -ETIMEDOUT; >> } > No, I want: > > time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > > if (!transfer_was_complete) { > handle_error(); > ... > > } > > handle_successful_transfer(); > > and time_left == 0 is not a reliable indicator that the transfer failed. > > Best regards > Uwe > Okay I'll check both time_left and transfer_was_done: time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); /* disable all interrupts */ writel(0, iproc_i2c->base + IE_OFFSET); if (!time_left && !atomic_read(&iproc_i2c->transfer_is_successful)) { dev_err(iproc_i2c->device, "transaction timed out\n"); /* flush FIFOs */ val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); return -ETIMEDOUT; } -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54BAD391.9080909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54BAD391.9080909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-17 22:40 ` Russell King - ARM Linux [not found] ` <20150117224021.GA26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Russell King - ARM Linux @ 2015-01-17 22:40 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On Sat, Jan 17, 2015 at 01:26:41PM -0800, Ray Jui wrote: > time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > > /* disable all interrupts */ > writel(0, iproc_i2c->base + IE_OFFSET); > > if (!time_left && !atomic_read(&iproc_i2c->transfer_is_successful)) { Why are you using atomic_read() here? -- FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150117224021.GA26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150117224021.GA26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> @ 2015-01-18 0:30 ` Ray Jui 2015-01-19 19:28 ` Russell King - ARM Linux 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-18 0:30 UTC (permalink / raw) To: Russell King - ARM Linux Cc: Uwe Kleine-König, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/17/2015 2:40 PM, Russell King - ARM Linux wrote: > On Sat, Jan 17, 2015 at 01:26:41PM -0800, Ray Jui wrote: >> time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >> >> /* disable all interrupts */ >> writel(0, iproc_i2c->base + IE_OFFSET); >> >> if (!time_left && !atomic_read(&iproc_i2c->transfer_is_successful)) { > > Why are you using atomic_read() here? > transfer_is_successful 1) will be reset to 0 in this function (before kick start the I2C transfer), 2) will be set to 1 in the ISR (to signal completion of the I2C transfer), and 3) will be checked in this function here. I thought that means I should declare it volatile, because it can be modified in both the process context and interrupt context (and I use atomic because I remember Linux checkpatch warns against using volatile)? ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-18 0:30 ` Ray Jui @ 2015-01-19 19:28 ` Russell King - ARM Linux [not found] ` <20150119192805.GF26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Russell King - ARM Linux @ 2015-01-19 19:28 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree On Sat, Jan 17, 2015 at 04:30:33PM -0800, Ray Jui wrote: > > > On 1/17/2015 2:40 PM, Russell King - ARM Linux wrote: > > On Sat, Jan 17, 2015 at 01:26:41PM -0800, Ray Jui wrote: > >> time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > >> > >> /* disable all interrupts */ > >> writel(0, iproc_i2c->base + IE_OFFSET); > >> > >> if (!time_left && !atomic_read(&iproc_i2c->transfer_is_successful)) { > > > > Why are you using atomic_read() here? > > > transfer_is_successful 1) will be reset to 0 in this function (before > kick start the I2C transfer), 2) will be set to 1 in the ISR (to signal > completion of the I2C transfer), and 3) will be checked in this function > here. I thought that means I should declare it volatile, because it can > be modified in both the process context and interrupt context (and I use > atomic because I remember Linux checkpatch warns against using volatile)? You don't need volatile or atomic_t for that. Rather than switching to atomic_t when seeing the checkpatch warning, you'd do better to read Documentation/volatile-considered-harmful.txt to understand why checkpatch issues the warning, and realise that you don't need it for the above. Note that in the above code, the compiler can't make an assumption about iproc_i2c->transfer_is_successful because it can't tell whether a called function (eg, wait_for_completion_timeout()) could modify it. Another possible issue with the above code are these lines: /* disable all interrupts */ writel(0, iproc_i2c->base + IE_OFFSET); It would be nice to think that would hit the hardware immediately, but that's making assumptions about hardware which are not necessary true. Your interrupt handler could even be running on another CPU after you've asked for that register to be written. Depending on what you're trying to achieve here, you may need: /* disable all interrupts */ writel(0, iproc_i2c->base + IE_OFFSET); /* read it back to ensure the write has hit */ readl(iproc_i2c->base + IE_OFFSET); /* make sure the interrupt handler isn't running */ synchronize_irq(...->irq); if what you're trying to do is to ensure that the interrupt handler has finished running. -- FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up according to speedtest.net. ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150119192805.GF26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>]
* Re: [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150119192805.GF26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> @ 2015-01-19 21:25 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 21:25 UTC (permalink / raw) To: Russell King - ARM Linux Cc: Uwe Kleine-König, Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/19/2015 11:28 AM, Russell King - ARM Linux wrote: > On Sat, Jan 17, 2015 at 04:30:33PM -0800, Ray Jui wrote: >> >> >> On 1/17/2015 2:40 PM, Russell King - ARM Linux wrote: >>> On Sat, Jan 17, 2015 at 01:26:41PM -0800, Ray Jui wrote: >>>> time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >>>> >>>> /* disable all interrupts */ >>>> writel(0, iproc_i2c->base + IE_OFFSET); >>>> >>>> if (!time_left && !atomic_read(&iproc_i2c->transfer_is_successful)) { >>> >>> Why are you using atomic_read() here? >>> >> transfer_is_successful 1) will be reset to 0 in this function (before >> kick start the I2C transfer), 2) will be set to 1 in the ISR (to signal >> completion of the I2C transfer), and 3) will be checked in this function >> here. I thought that means I should declare it volatile, because it can >> be modified in both the process context and interrupt context (and I use >> atomic because I remember Linux checkpatch warns against using volatile)? > > You don't need volatile or atomic_t for that. > > Rather than switching to atomic_t when seeing the checkpatch warning, > you'd do better to read Documentation/volatile-considered-harmful.txt > to understand why checkpatch issues the warning, and realise that you > don't need it for the above. > > Note that in the above code, the compiler can't make an assumption > about iproc_i2c->transfer_is_successful because it can't tell whether > a called function (eg, wait_for_completion_timeout()) could modify it. > Got it. Thanks. > Another possible issue with the above code are these lines: > > /* disable all interrupts */ > writel(0, iproc_i2c->base + IE_OFFSET); > > It would be nice to think that would hit the hardware immediately, but > that's making assumptions about hardware which are not necessary true. > Your interrupt handler could even be running on another CPU after you've > asked for that register to be written. > > Depending on what you're trying to achieve here, you may need: > > /* disable all interrupts */ > writel(0, iproc_i2c->base + IE_OFFSET); > /* read it back to ensure the write has hit */ > readl(iproc_i2c->base + IE_OFFSET); > > /* make sure the interrupt handler isn't running */ > synchronize_irq(...->irq); > > if what you're trying to do is to ensure that the interrupt handler has > finished running. > This will be the most robust way of handling this. Given that we've added an additional flag to check to make sure there's no interrupt missed after wait_for_completion_timeout times out, it makes sense to ensure that by the time when we check the flag there's no pending irq. I'll add this to the driver and make 'xfer_is_done' an 'int' instead of 'atomic_t'. I will also add the call to readl to flush the write in the remove function after interrupts are disabled. ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2015-01-14 22:23 ` [PATCH v4 " Ray Jui 2015-01-14 22:23 ` [PATCH v4 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui [not found] ` <1421274213-3544-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-14 22:23 ` Ray Jui [not found] ` <1421274213-3544-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-14 22:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..f7d6c1d 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1421274213-3544-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <1421274213-3544-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-15 8:44 ` Uwe Kleine-König [not found] ` <20150115084456.GO22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-15 8:44 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Wed, Jan 14, 2015 at 02:23:33PM -0800, Ray Jui wrote: > Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep > them disabled there. Individual I2C devices can be enabled in board > specific dts file when I2C slave devices are enabled in the future s/$/./ > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > --- > arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ > 1 file changed, 20 insertions(+) > > diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi > index 5126f9e..f7d6c1d 100644 > --- a/arch/arm/boot/dts/bcm-cygnus.dtsi > +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi > @@ -70,6 +70,26 @@ > }; > }; > > + i2c0: i2c@18008000 { > + compatible = "brcm,iproc-i2c"; in patch 2 you wrote the driver is for a family of SoCs, right? Then I'd make this: compatible = "brcm,$mysoc-iproc-i2c", "brcm,iproc-i2c"; (or maybe s/$mysoc-iproc-i2c/$mysoc-i2c/). Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150115084456.GO22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <20150115084456.GO22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-16 19:24 ` Ray Jui [not found] ` <54B96559.1010007-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-16 19:24 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/15/2015 12:44 AM, Uwe Kleine-König wrote: > Hello, > > On Wed, Jan 14, 2015 at 02:23:33PM -0800, Ray Jui wrote: >> Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep >> them disabled there. Individual I2C devices can be enabled in board >> specific dts file when I2C slave devices are enabled in the future > s/$/./ > >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> --- >> arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ >> 1 file changed, 20 insertions(+) >> >> diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi >> index 5126f9e..f7d6c1d 100644 >> --- a/arch/arm/boot/dts/bcm-cygnus.dtsi >> +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi >> @@ -70,6 +70,26 @@ >> }; >> }; >> >> + i2c0: i2c@18008000 { >> + compatible = "brcm,iproc-i2c"; > in patch 2 you wrote the driver is for a family of SoCs, right? Then I'd > make this: > > compatible = "brcm,$mysoc-iproc-i2c", "brcm,iproc-i2c"; > Sorry could you please help to explain the intention here? Note the iProc I2C IP can be found in various iProc family of SoCs, but to my best knowledge, there hasn't been any changes of the IP in any of those SoCs. Is the compatible ID "brcm,$mysoc-iproc-i2c" only to clarify that it's for a specific SoC? If so, what should the compatible ID array look like? Should it be changed to the following? static const struct of_device_id bcm_iproc_i2c_of_match[] = { { .compatible = "brcm,iproc-i2c" }, { .compatible = "brcm,$mysoc-iproc-i2c" }, {}, }; > (or maybe s/$mysoc-iproc-i2c/$mysoc-i2c/). > > Best regards > Uwe > ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54B96559.1010007-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <54B96559.1010007-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-16 19:48 ` Uwe Kleine-König [not found] ` <20150116194831.GV22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-16 19:48 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Hello, On Fri, Jan 16, 2015 at 11:24:09AM -0800, Ray Jui wrote: > >> + i2c0: i2c@18008000 { > >> + compatible = "brcm,iproc-i2c"; > > in patch 2 you wrote the driver is for a family of SoCs, right? Then I'd > > make this: > > > > compatible = "brcm,$mysoc-iproc-i2c", "brcm,iproc-i2c"; > > > Sorry could you please help to explain the intention here? Note the > iProc I2C IP can be found in various iProc family of SoCs, but to my > best knowledge, there hasn't been any changes of the IP in any of those > SoCs. This is just for making the device tree stable in the future. Consider your gentle hardware engineers "fix" a small issue for the next generation iproc SoC "pony" that needs an incompatible software change. Then you can fix the driver without updating the device trees by switching to the SoC specific compatible string for "pony". And in case the hardware engineers didn't tell you that there is a change and the need for the software change is only detected when the machines are already shipped, you're happy if you can fix your kernel without needing to change the bootloader that provides the dtb. So start already today to add the (for now unused) compatible string. > Is the compatible ID "brcm,$mysoc-iproc-i2c" only to clarify that it's > for a specific SoC? If so, what should the compatible ID array look > like? Should it be changed to the following? > > static const struct of_device_id bcm_iproc_i2c_of_match[] = { > { .compatible = "brcm,iproc-i2c" }, > { .compatible = "brcm,$mysoc-iproc-i2c" }, > {}, > }; No, there is no need, see above. If something is still unclear, don't hesitate to ask. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150116194831.GV22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <20150116194831.GV22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-16 23:18 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-16 23:18 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/16/2015 11:48 AM, Uwe Kleine-König wrote: > Hello, > > On Fri, Jan 16, 2015 at 11:24:09AM -0800, Ray Jui wrote: >>>> + i2c0: i2c@18008000 { >>>> + compatible = "brcm,iproc-i2c"; >>> in patch 2 you wrote the driver is for a family of SoCs, right? Then I'd >>> make this: >>> >>> compatible = "brcm,$mysoc-iproc-i2c", "brcm,iproc-i2c"; >>> >> Sorry could you please help to explain the intention here? Note the >> iProc I2C IP can be found in various iProc family of SoCs, but to my >> best knowledge, there hasn't been any changes of the IP in any of those >> SoCs. > This is just for making the device tree stable in the future. Consider > your gentle hardware engineers "fix" a small issue for the next > generation iproc SoC "pony" that needs an incompatible software change. > > Then you can fix the driver without updating the device trees by > switching to the SoC specific compatible string for "pony". And in case > the hardware engineers didn't tell you that there is a change and the > need for the software change is only detected when the machines are > already shipped, you're happy if you can fix your kernel without needing > to change the bootloader that provides the dtb. > So start already today to add the (for now unused) compatible string. > >> Is the compatible ID "brcm,$mysoc-iproc-i2c" only to clarify that it's >> for a specific SoC? If so, what should the compatible ID array look >> like? Should it be changed to the following? >> >> static const struct of_device_id bcm_iproc_i2c_of_match[] = { >> { .compatible = "brcm,iproc-i2c" }, >> { .compatible = "brcm,$mysoc-iproc-i2c" }, >> {}, >> }; > No, there is no need, see above. > > If something is still unclear, don't hesitate to ask. > > Best regards > Uwe > Okay got it. Thanks for the explanation! -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v5 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> ` (3 preceding siblings ...) 2015-01-14 22:23 ` [PATCH v4 " Ray Jui @ 2015-01-16 23:42 ` Ray Jui [not found] ` <1421451737-7107-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-16 23:42 ` [PATCH v5 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2015-01-19 19:23 ` [PATCH v6 0/3] Add I2C support to Broadcom iProc Ray Jui ` (3 subsequent siblings) 8 siblings, 2 replies; 106+ messages in thread From: Ray Jui @ 2015-01-16 23:42 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v4: - Remove redundant header file includes - Change the logic that waits for the host controller to be idle to simply return -EBUSY - Use proper print level and error codes in the driver - Allow zero length message in the driver to support I2C_SMBUS_QUICK - Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework - Other minor miscellaneous improvements and fixes Changes from v3: - Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms - Improve CPU utilization efficiency in the loop of waiting for bus to idle - Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller - Fix inconsistent coding style and format - Improve the bus speed validation logic in the driver - Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line - Other miscellaneous improvements and fixes Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 493 ++++++++++++++++++++ 5 files changed, 561 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <1421451737-7107-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* [PATCH v5 1/3] i2c: iProc: define Broadcom iProc I2C binding [not found] ` <1421451737-7107-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-16 23:42 ` Ray Jui 2015-01-16 23:42 ` [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-16 23:42 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1421451737-7107-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-16 23:42 ` [PATCH v5 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2015-01-16 23:42 ` Ray Jui [not found] ` <1421451737-7107-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 1 sibling, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-16 23:42 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 493 ++++++++++++++++++++++++++++++++++++ 3 files changed, 504 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 31e8308..af76d23 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 56388f6..d93b509 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..a92d8f5 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIM_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + int irq; + + void __iomem *base; + + struct i2c_adapter adapter; + + struct completion done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = data; + u32 status = readl(iproc_i2c->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, iproc_i2c->base + IS_OFFSET); + complete_all(&iproc_i2c->done); + + return IRQ_HANDLED; +} + +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + if (readl(iproc_i2c->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) + return true; + else + return false; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = msg->addr << 1; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + u32 val; + + val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); + return -EAGAIN; + + case M_CMD_STATUS_NACK_ADDR: + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); + return -ENXIO; + + case M_CMD_STATUS_NACK_DATA: + dev_dbg(iproc_i2c->device, "NAK data\n"); + return -ENXIO; + + case M_CMD_STATUS_TIMEOUT: + dev_dbg(iproc_i2c->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); + return -EIO; + } +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(iproc_i2c->device, + "only support data length up to %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + if (bcm_iproc_i2c_bus_busy(iproc_i2c)) { + dev_warn(iproc_i2c->device, "bus is busy\n"); + return -EBUSY; + } + + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, iproc_i2c->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, iproc_i2c->base + M_TX_OFFSET); + } + + if (msg->len == 0) + writel(1 << M_TX_WR_STATUS_SHIFT, + iproc_i2c->base + M_TX_OFFSET); + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&iproc_i2c->done); + + /* + * Enable the "start busy" interrupt, which will be triggered after the + * transaction is done, i.e., the internal start_busy bit, transitions + * from 1 to 0. + */ + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, iproc_i2c->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + if (!time_left) { + dev_err(iproc_i2c->device, "transaction timed out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return -ETIMEDOUT; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(iproc_i2c->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); + dev_dbg(iproc_i2c->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); + if (ret) { + dev_err(iproc_i2c->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_info(iproc_i2c->device, + "unable to interpret clock-frequency DT property\n"); + bus_speed = 100000; + } + + if (bus_speed < 100000) { + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(iproc_i2c->device, + "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } else if (bus_speed < 400000) { + speed_bit = 0; + } else { + /* bus_speed >= 400000 */ + speed_bit = 1; + } + + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + /* clear all pending interrupts */ + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + struct resource *res; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + init_completion(&iproc_i2c->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(iproc_i2c->device, "no irq resource\n"); + return irq; + } + iproc_i2c->irq = irq; + + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, + pdev->name, iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(iproc_i2c); + + adap = &iproc_i2c->adapter; + i2c_set_adapdata(adap, iproc_i2c); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(iproc_i2c->device, "failed to add adapter\n"); + return ret; + } + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + disable_irq(iproc_i2c->irq); + i2c_del_adapter(&iproc_i2c->adapter); + bcm_iproc_i2c_disable(iproc_i2c); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + { .compatible = "brcm,iproc-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1421451737-7107-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1421451737-7107-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-18 9:14 ` Arend van Spriel [not found] ` <54BB795C.6040402-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Arend van Spriel @ 2015-01-18 9:14 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 01/17/15 00:42, Ray Jui wrote: [...] > +/* > + * Can be expanded in the future if more interrupt status bits are utilized > + */ > +#define ISR_MASK (1<< IS_M_START_BUSY_SHIFT) > + > +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > +{ > + struct bcm_iproc_i2c_dev *iproc_i2c = data; > + u32 status = readl(iproc_i2c->base + IS_OFFSET); > + > + status&= ISR_MASK; > + > + if (!status) > + return IRQ_NONE; > + > + writel(status, iproc_i2c->base + IS_OFFSET); > + complete_all(&iproc_i2c->done); Looking over this code it seems to me there is always a single process waiting for iproc_i2c->done to complete. So using complete() here would suffice. Regards, Arend > + > + return IRQ_HANDLED; > +} -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54BB795C.6040402-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54BB795C.6040402-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-18 9:47 ` Uwe Kleine-König [not found] ` <20150118094741.GE22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-18 9:47 UTC (permalink / raw) To: Arend van Spriel Cc: Ray Jui, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Christian Daudt, Russell King, Scott Branden, Pawel Moll, Ian Campbell, Wolfram Sang, Florian Fainelli, Matt Porter, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Grant Likely, Kumar Gala, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Hello, On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: > On 01/17/15 00:42, Ray Jui wrote: > > [...] > > >+/* > >+ * Can be expanded in the future if more interrupt status bits are utilized > >+ */ > >+#define ISR_MASK (1<< IS_M_START_BUSY_SHIFT) > >+ > >+static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > >+{ > >+ struct bcm_iproc_i2c_dev *iproc_i2c = data; > >+ u32 status = readl(iproc_i2c->base + IS_OFFSET); > >+ > >+ status&= ISR_MASK; > >+ > >+ if (!status) > >+ return IRQ_NONE; > >+ > >+ writel(status, iproc_i2c->base + IS_OFFSET); > >+ complete_all(&iproc_i2c->done); > > Looking over this code it seems to me there is always a single > process waiting for iproc_i2c->done to complete. So using complete() > here would suffice. Yeah, there is always only a single thread waiting. That means both complete and complete_all are suitable. AFAIK there is no reason to pick one over the other in this case. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150118094741.GE22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150118094741.GE22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-18 11:06 ` Wolfram Sang 2015-01-18 11:17 ` Uwe Kleine-König 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-01-18 11:06 UTC (permalink / raw) To: Uwe Kleine-König Cc: Arend van Spriel, Ray Jui, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Christian Daudt, Russell King, Scott Branden, Pawel Moll, Ian Campbell, Florian Fainelli, Matt Porter, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Grant Likely, Kumar Gala, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r [-- Attachment #1: Type: text/plain, Size: 1122 bytes --] On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: > Hello, > > On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: > > On 01/17/15 00:42, Ray Jui wrote: > > > > [...] > > > > >+/* > > >+ * Can be expanded in the future if more interrupt status bits are utilized > > >+ */ > > >+#define ISR_MASK (1<< IS_M_START_BUSY_SHIFT) > > >+ > > >+static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > > >+{ > > >+ struct bcm_iproc_i2c_dev *iproc_i2c = data; > > >+ u32 status = readl(iproc_i2c->base + IS_OFFSET); > > >+ > > >+ status&= ISR_MASK; > > >+ > > >+ if (!status) > > >+ return IRQ_NONE; > > >+ > > >+ writel(status, iproc_i2c->base + IS_OFFSET); > > >+ complete_all(&iproc_i2c->done); > > > > Looking over this code it seems to me there is always a single > > process waiting for iproc_i2c->done to complete. So using complete() > > here would suffice. > Yeah, there is always only a single thread waiting. That means both > complete and complete_all are suitable. AFAIK there is no reason to pick > one over the other in this case. Clarity? [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-18 11:06 ` Wolfram Sang @ 2015-01-18 11:17 ` Uwe Kleine-König [not found] ` <20150118111759.GG22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-18 11:46 ` Arend van Spriel 0 siblings, 2 replies; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-18 11:17 UTC (permalink / raw) To: Wolfram Sang Cc: Arend van Spriel, Ray Jui, Mark Rutland, devicetree, Christian Daudt, Russell King, Scott Branden, Pawel Moll, Ian Campbell, Florian Fainelli, Matt Porter, linux-kernel, Rob Herring, bcm-kernel-feedback-list, linux-i2c, Grant Likely, Kumar Gala, linux-arm-kernel Hello Wolfram, On Sun, Jan 18, 2015 at 12:06:58PM +0100, Wolfram Sang wrote: > On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: > > On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: > > > On 01/17/15 00:42, Ray Jui wrote: > > > >+ complete_all(&iproc_i2c->done); > > > > > > Looking over this code it seems to me there is always a single > > > process waiting for iproc_i2c->done to complete. So using complete() > > > here would suffice. > > Yeah, there is always only a single thread waiting. That means both > > complete and complete_all are suitable. AFAIK there is no reason to pick > > one over the other in this case. > > Clarity? And which do you consider more clear? complete_all might result in the question: "Is there >1 waiter?" and complete might yield to "What about the other waiters?". If you already know there is only one, both are on par on clarity. Might only be me?! I don't care much. Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150118111759.GG22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150118111759.GG22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-18 11:42 ` Wolfram Sang 0 siblings, 0 replies; 106+ messages in thread From: Wolfram Sang @ 2015-01-18 11:42 UTC (permalink / raw) To: Uwe Kleine-König Cc: Arend van Spriel, Ray Jui, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Christian Daudt, Russell King, Scott Branden, Pawel Moll, Ian Campbell, Florian Fainelli, Matt Porter, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Rob Herring, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Grant Likely, Kumar Gala, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r [-- Attachment #1: Type: text/plain, Size: 1277 bytes --] On Sun, Jan 18, 2015 at 12:17:59PM +0100, Uwe Kleine-König wrote: > Hello Wolfram, > > On Sun, Jan 18, 2015 at 12:06:58PM +0100, Wolfram Sang wrote: > > On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: > > > On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: > > > > On 01/17/15 00:42, Ray Jui wrote: > > > > >+ complete_all(&iproc_i2c->done); > > > > > > > > Looking over this code it seems to me there is always a single > > > > process waiting for iproc_i2c->done to complete. So using complete() > > > > here would suffice. > > > Yeah, there is always only a single thread waiting. That means both > > > complete and complete_all are suitable. AFAIK there is no reason to pick > > > one over the other in this case. > > > > Clarity? > And which do you consider more clear? complete_all might result in the > question: "Is there >1 waiter?" and complete might yield to "What about > the other waiters?". If you already know there is only one, both are on > par on clarity. Might only be me?! I don't care much. It is minor, I agree: If I read complete_all, I assume there is something fishy if there is only one waiter. It doesn't match. It might work, but I'll wonder if this is accidently or intentionally. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-18 11:17 ` Uwe Kleine-König [not found] ` <20150118111759.GG22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-18 11:46 ` Arend van Spriel [not found] ` <54BB9D2B.20408-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 1 sibling, 1 reply; 106+ messages in thread From: Arend van Spriel @ 2015-01-18 11:46 UTC (permalink / raw) To: Uwe Kleine-König Cc: Wolfram Sang, Ray Jui, Mark Rutland, devicetree, Christian Daudt, Russell King, Scott Branden, Pawel Moll, Ian Campbell, Florian Fainelli, Matt Porter, linux-kernel, Rob Herring, bcm-kernel-feedback-list, linux-i2c, Grant Likely, Kumar Gala, linux-arm-kernel On 01/18/15 12:17, Uwe Kleine-König wrote: > Hello Wolfram, > > On Sun, Jan 18, 2015 at 12:06:58PM +0100, Wolfram Sang wrote: >> On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: >>> On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: >>>> On 01/17/15 00:42, Ray Jui wrote: >>>>> + complete_all(&iproc_i2c->done); >>>> >>>> Looking over this code it seems to me there is always a single >>>> process waiting for iproc_i2c->done to complete. So using complete() >>>> here would suffice. >>> Yeah, there is always only a single thread waiting. That means both >>> complete and complete_all are suitable. AFAIK there is no reason to pick >>> one over the other in this case. >> >> Clarity? > And which do you consider more clear? complete_all might result in the > question: "Is there>1 waiter?" and complete might yield to "What about > the other waiters?". If you already know there is only one, both are on > par on clarity. Might only be me?! I don't care much. Maybe it is me, but it is not about questions but it is about implicit statements that the code makes (or reader derives from it). When using complete_all you indicate to the reader "there can be more than one waiter". When using complete it indicates "there is only one waiter". If those statements are not true that is a code issue/bug. Regards, Arend > Best regards > Uwe > ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54BB9D2B.20408-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54BB9D2B.20408-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-18 11:56 ` Uwe Kleine-König [not found] ` <20150118115650.GH22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Uwe Kleine-König @ 2015-01-18 11:56 UTC (permalink / raw) To: Arend van Spriel Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Ian Campbell, Florian Fainelli, Russell King, Pawel Moll, Scott Branden, Wolfram Sang, Ray Jui, Christian Daudt, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Matt Porter, Rob Herring, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Kumar Gala, Grant Likely, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r Hello, On Sun, Jan 18, 2015 at 12:46:51PM +0100, Arend van Spriel wrote: > On 01/18/15 12:17, Uwe Kleine-König wrote: > >Hello Wolfram, > > > >On Sun, Jan 18, 2015 at 12:06:58PM +0100, Wolfram Sang wrote: > >>On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: > >>>On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: > >>>>On 01/17/15 00:42, Ray Jui wrote: > >>>>>+ complete_all(&iproc_i2c->done); > >>>> > >>>>Looking over this code it seems to me there is always a single > >>>>process waiting for iproc_i2c->done to complete. So using complete() > >>>>here would suffice. > >>>Yeah, there is always only a single thread waiting. That means both > >>>complete and complete_all are suitable. AFAIK there is no reason to pick > >>>one over the other in this case. > >> > >>Clarity? > >And which do you consider more clear? complete_all might result in the > >question: "Is there>1 waiter?" and complete might yield to "What about > >the other waiters?". If you already know there is only one, both are on > >par on clarity. Might only be me?! I don't care much. > > Maybe it is me, but it is not about questions but it is about > implicit statements that the code makes (or reader derives from it). > When using complete_all you indicate to the reader "there can be > more than one waiter". When using complete it indicates "there is > only one waiter". If those statements are not true that is a code No, complete works just fine in the presence of >1 waiter. It just wakes a single waiter and all others continue to wait. That is, for single-waiter situations there is no semantic difference between complete and complete_all. But there is a difference for multi-waiter queues. I think this is just a matter of your POV in the single-waiter situation: complete might be intuitive because you just completed a single task and complete_all might be intuitive because it signals "I'm completely done, there is noone waiting for me any more.". Best regards Uwe -- Pengutronix e.K. | Uwe Kleine-König | Industrial Linux Solutions | http://www.pengutronix.de/ | -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150118115650.GH22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150118115650.GH22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> @ 2015-01-18 12:13 ` Arend van Spriel [not found] ` <54BBA36A.10608-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Arend van Spriel @ 2015-01-18 12:13 UTC (permalink / raw) To: Uwe Kleine-König Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Ian Campbell, Florian Fainelli, Russell King, Pawel Moll, Scott Branden, Wolfram Sang, Ray Jui, Christian Daudt, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Matt Porter, Rob Herring, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Kumar Gala, Grant Likely, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r On 01/18/15 12:56, Uwe Kleine-König wrote: > Hello, > > On Sun, Jan 18, 2015 at 12:46:51PM +0100, Arend van Spriel wrote: >> On 01/18/15 12:17, Uwe Kleine-König wrote: >>> Hello Wolfram, >>> >>> On Sun, Jan 18, 2015 at 12:06:58PM +0100, Wolfram Sang wrote: >>>> On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: >>>>> On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: >>>>>> On 01/17/15 00:42, Ray Jui wrote: >>>>>>> + complete_all(&iproc_i2c->done); >>>>>> >>>>>> Looking over this code it seems to me there is always a single >>>>>> process waiting for iproc_i2c->done to complete. So using complete() >>>>>> here would suffice. >>>>> Yeah, there is always only a single thread waiting. That means both >>>>> complete and complete_all are suitable. AFAIK there is no reason to pick >>>>> one over the other in this case. >>>> >>>> Clarity? >>> And which do you consider more clear? complete_all might result in the >>> question: "Is there>1 waiter?" and complete might yield to "What about >>> the other waiters?". If you already know there is only one, both are on >>> par on clarity. Might only be me?! I don't care much. >> >> Maybe it is me, but it is not about questions but it is about >> implicit statements that the code makes (or reader derives from it). >> When using complete_all you indicate to the reader "there can be >> more than one waiter". When using complete it indicates "there is >> only one waiter". If those statements are not true that is a code > No, complete works just fine in the presence of>1 waiter. It just wakes > a single waiter and all others continue to wait. Yes. Agree. > That is, for single-waiter situations there is no semantic difference > between complete and complete_all. But there is a difference for > multi-waiter queues. Indeed. > I think this is just a matter of your POV in the single-waiter > situation: complete might be intuitive because you just completed a > single task and complete_all might be intuitive because it signals > "I'm completely done, there is noone waiting for me any more.". Ok. Let's leave it to the author's intuition or to say it differently "sorry for the noise" ;-) Regards, Arend > Best regards > Uwe > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54BBA36A.10608-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54BBA36A.10608-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-19 19:15 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 19:15 UTC (permalink / raw) To: Arend van Spriel, Uwe Kleine-König Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA, Ian Campbell, Florian Fainelli, Russell King, Pawel Moll, Scott Branden, Wolfram Sang, Christian Daudt, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Matt Porter, Rob Herring, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, linux-i2c-u79uwXL29TY76Z2rM5mHXA, Kumar Gala, Grant Likely, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r On 1/18/2015 4:13 AM, Arend van Spriel wrote: > On 01/18/15 12:56, Uwe Kleine-König wrote: >> Hello, >> >> On Sun, Jan 18, 2015 at 12:46:51PM +0100, Arend van Spriel wrote: >>> On 01/18/15 12:17, Uwe Kleine-König wrote: >>>> Hello Wolfram, >>>> >>>> On Sun, Jan 18, 2015 at 12:06:58PM +0100, Wolfram Sang wrote: >>>>> On Sun, Jan 18, 2015 at 10:47:41AM +0100, Uwe Kleine-König wrote: >>>>>> On Sun, Jan 18, 2015 at 10:14:04AM +0100, Arend van Spriel wrote: >>>>>>> On 01/17/15 00:42, Ray Jui wrote: >>>>>>>> + complete_all(&iproc_i2c->done); >>>>>>> >>>>>>> Looking over this code it seems to me there is always a single >>>>>>> process waiting for iproc_i2c->done to complete. So using complete() >>>>>>> here would suffice. >>>>>> Yeah, there is always only a single thread waiting. That means both >>>>>> complete and complete_all are suitable. AFAIK there is no reason >>>>>> to pick >>>>>> one over the other in this case. >>>>> >>>>> Clarity? >>>> And which do you consider more clear? complete_all might result in the >>>> question: "Is there>1 waiter?" and complete might yield to "What about >>>> the other waiters?". If you already know there is only one, both are on >>>> par on clarity. Might only be me?! I don't care much. >>> >>> Maybe it is me, but it is not about questions but it is about >>> implicit statements that the code makes (or reader derives from it). >>> When using complete_all you indicate to the reader "there can be >>> more than one waiter". When using complete it indicates "there is >>> only one waiter". If those statements are not true that is a code >> No, complete works just fine in the presence of>1 waiter. It just wakes >> a single waiter and all others continue to wait. > > Yes. Agree. > >> That is, for single-waiter situations there is no semantic difference >> between complete and complete_all. But there is a difference for >> multi-waiter queues. > > Indeed. > >> I think this is just a matter of your POV in the single-waiter >> situation: complete might be intuitive because you just completed a >> single task and complete_all might be intuitive because it signals >> "I'm completely done, there is noone waiting for me any more.". > > Ok. Let's leave it to the author's intuition or to say it differently > "sorry for the noise" ;-) Will stay with complete_all since I meant to say "after this transfer complete interrupt, there should be no one waiting anymore (although there's currently only one waiter, and will likely stay that way)" Thanks! > > Regards, > Arend > >> Best regards >> Uwe >> > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v5 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2015-01-16 23:42 ` [PATCH v5 0/3] Add I2C support to Broadcom iProc Ray Jui [not found] ` <1421451737-7107-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-16 23:42 ` Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-16 23:42 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..ff5fb6a 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v6 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> ` (4 preceding siblings ...) 2015-01-16 23:42 ` [PATCH v5 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-01-19 19:23 ` Ray Jui [not found] ` <1421695428-19102-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-19 19:23 ` [PATCH v6 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui 2015-01-19 21:51 ` [PATCH v7 0/3] Add I2C support to Broadcom iProc Ray Jui ` (2 subsequent siblings) 8 siblings, 2 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 19:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v5: - Improve the "waiting for transaction to be complete" logic to take care of the corner case when an interrupt fires after wait_for_completion_timeout times out - Improve the logic to disable I2C interrupt in the remove function. Make it more generic so it works for both dedicated and shared interrupt Changes from v4: - Remove redundant header file includes - Change the logic that waits for the host controller to be idle to simply return -EBUSY - Use proper print level and error codes in the driver - Allow zero length message in the driver to support I2C_SMBUS_QUICK - Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework - Other minor miscellaneous improvements and fixes Changes from v3: - Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms - Improve CPU utilization efficiency in the loop of waiting for bus to idle - Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller - Fix inconsistent coding style and format - Improve the bus speed validation logic in the driver - Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line - Other miscellaneous improvements and fixes Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 499 ++++++++++++++++++++ 5 files changed, 567 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <1421695428-19102-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* [PATCH v6 1/3] i2c: iProc: define Broadcom iProc I2C binding [not found] ` <1421695428-19102-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-19 19:23 ` Ray Jui 2015-01-19 19:23 ` [PATCH v6 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 19:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v6 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <1421695428-19102-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-19 19:23 ` [PATCH v6 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2015-01-19 19:23 ` Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 19:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..ff5fb6a 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v6 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-19 19:23 ` [PATCH v6 0/3] Add I2C support to Broadcom iProc Ray Jui [not found] ` <1421695428-19102-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-19 19:23 ` Ray Jui [not found] ` <1421695428-19102-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 1 sibling, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-19 19:23 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 499 ++++++++++++++++++++++++++++++++++++ 3 files changed, 510 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 31e8308..af76d23 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 56388f6..d93b509 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..1ebacd7 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,499 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIM_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + int irq; + + void __iomem *base; + + struct i2c_adapter adapter; + + struct completion done; + atomic_t xfer_is_done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = data; + u32 status = readl(iproc_i2c->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, iproc_i2c->base + IS_OFFSET); + atomic_set(&iproc_i2c->xfer_is_done, 1); + complete_all(&iproc_i2c->done); + + return IRQ_HANDLED; +} + +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + if (readl(iproc_i2c->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) + return true; + else + return false; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = msg->addr << 1; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + u32 val; + + val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); + return -EAGAIN; + + case M_CMD_STATUS_NACK_ADDR: + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); + return -ENXIO; + + case M_CMD_STATUS_NACK_DATA: + dev_dbg(iproc_i2c->device, "NAK data\n"); + return -ENXIO; + + case M_CMD_STATUS_TIMEOUT: + dev_dbg(iproc_i2c->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); + return -EIO; + } +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(iproc_i2c->device, + "only support data length up to %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + if (bcm_iproc_i2c_bus_busy(iproc_i2c)) { + dev_warn(iproc_i2c->device, "bus is busy\n"); + return -EBUSY; + } + + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, iproc_i2c->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, iproc_i2c->base + M_TX_OFFSET); + } + + if (msg->len == 0) + writel(1 << M_TX_WR_STATUS_SHIFT, + iproc_i2c->base + M_TX_OFFSET); + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&iproc_i2c->done); + atomic_set(&iproc_i2c->xfer_is_done, 0); + + /* + * Enable the "start busy" interrupt, which will be triggered after the + * transaction is done, i.e., the internal start_busy bit, transitions + * from 1 to 0. + */ + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, iproc_i2c->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + if (!time_left && !atomic_read(&iproc_i2c->xfer_is_done)) { + dev_err(iproc_i2c->device, "transaction timed out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return -ETIMEDOUT; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(iproc_i2c->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); + dev_dbg(iproc_i2c->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); + if (ret) { + dev_err(iproc_i2c->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_info(iproc_i2c->device, + "unable to interpret clock-frequency DT property\n"); + bus_speed = 100000; + } + + if (bus_speed < 100000) { + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(iproc_i2c->device, + "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } else if (bus_speed < 400000) { + speed_bit = 0; + } else { + /* bus_speed >= 400000 */ + speed_bit = 1; + } + + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + /* clear all pending interrupts */ + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + struct resource *res; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + init_completion(&iproc_i2c->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(iproc_i2c->device, "no irq resource\n"); + return irq; + } + iproc_i2c->irq = irq; + + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, + pdev->name, iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(iproc_i2c); + + adap = &iproc_i2c->adapter; + i2c_set_adapdata(adap, iproc_i2c); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(iproc_i2c->device, "failed to add adapter\n"); + return ret; + } + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + /* make sure there's no pending interrupt when we remove the adapter */ + writel(0, iproc_i2c->base + IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + + i2c_del_adapter(&iproc_i2c->adapter); + bcm_iproc_i2c_disable(iproc_i2c); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + { .compatible = "brcm,iproc-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1421695428-19102-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v6 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1421695428-19102-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-01-19 19:44 ` Russell King - ARM Linux [not found] ` <20150119194420.GG26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Russell King - ARM Linux @ 2015-01-19 19:44 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA To see why atomic_t is pure obfuscation: typedef struct { int counter; } atomic_t; So, counter is a plain int. On Mon, Jan 19, 2015 at 11:23:47AM -0800, Ray Jui wrote: > +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > +{ > + struct bcm_iproc_i2c_dev *iproc_i2c = data; > + u32 status = readl(iproc_i2c->base + IS_OFFSET); > + > + status &= ISR_MASK; > + > + if (!status) > + return IRQ_NONE; > + > + writel(status, iproc_i2c->base + IS_OFFSET); > + atomic_set(&iproc_i2c->xfer_is_done, 1); #define atomic_set(v,i) (((v)->counter) = (i)) So, this is the same as doing: iproc_i2c->xfer_is_done.counter = 1; which is merely setting the 'int' to 1. > + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > + > + /* disable all interrupts */ > + writel(0, iproc_i2c->base + IE_OFFSET); > + > + if (!time_left && !atomic_read(&iproc_i2c->xfer_is_done)) { #define atomic_read(v) ACCESS_ONCE((v)->counter) This is practically the same as: if (!time_left && !iproc_i2c->xfer_is_done.counter) { except that this access will be guaranteed to happen just once at this location (see ACCESS_ONCE() in include/linux/compiler.h). However, complete()..wait_for_completion() ensures that there are barriers in the way: complete takes a spinlock on the waiter, so the write to iproc_i2c->xfer_is_done.counter will be visible by the time wait_for_completion() returns, and wait_for_completion() also does. The same spinlock is also manipulated by wait_for_completion(), which means there's barriers there as well, so it can't cache the value of "counter" across that call. So, the "volatile" access guaranteed by ACCESS_ONCE() isn't even needed here. (It would be needed if you were spinning in a loop, calling no other functions - but then you're supposed to use cpu_relax() in that circumstance, which has a compiler barrier in it, which ensures that it will re-read such a variable each time.) -- FTTC broadband for 0.8mile line: currently at 10.5Mbps down 400kbps up according to speedtest.net. -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150119194420.GG26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>]
* Re: [PATCH v6 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150119194420.GG26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> @ 2015-01-19 21:31 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 21:31 UTC (permalink / raw) To: Russell King - ARM Linux Cc: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 1/19/2015 11:44 AM, Russell King - ARM Linux wrote: > To see why atomic_t is pure obfuscation: > > typedef struct { > int counter; > } atomic_t; > > So, counter is a plain int. > > On Mon, Jan 19, 2015 at 11:23:47AM -0800, Ray Jui wrote: >> +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) >> +{ >> + struct bcm_iproc_i2c_dev *iproc_i2c = data; >> + u32 status = readl(iproc_i2c->base + IS_OFFSET); >> + >> + status &= ISR_MASK; >> + >> + if (!status) >> + return IRQ_NONE; >> + >> + writel(status, iproc_i2c->base + IS_OFFSET); >> + atomic_set(&iproc_i2c->xfer_is_done, 1); > > #define atomic_set(v,i) (((v)->counter) = (i)) > > So, this is the same as doing: > > iproc_i2c->xfer_is_done.counter = 1; > > which is merely setting the 'int' to 1. > >> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >> + >> + /* disable all interrupts */ >> + writel(0, iproc_i2c->base + IE_OFFSET); >> + >> + if (!time_left && !atomic_read(&iproc_i2c->xfer_is_done)) { > > #define atomic_read(v) ACCESS_ONCE((v)->counter) > > This is practically the same as: > > if (!time_left && !iproc_i2c->xfer_is_done.counter) { > > except that this access will be guaranteed to happen just once at this > location (see ACCESS_ONCE() in include/linux/compiler.h). > > However, complete()..wait_for_completion() ensures that there are > barriers in the way: complete takes a spinlock on the waiter, so the > write to iproc_i2c->xfer_is_done.counter will be visible by the time > wait_for_completion() returns, and wait_for_completion() also does. > The same spinlock is also manipulated by wait_for_completion(), which > means there's barriers there as well, so it can't cache the value of > "counter" across that call. > > So, the "volatile" access guaranteed by ACCESS_ONCE() isn't even > needed here. > > (It would be needed if you were spinning in a loop, calling no other > functions - but then you're supposed to use cpu_relax() in that > circumstance, which has a compiler barrier in it, which ensures that > it will re-read such a variable each time.) > I really learned a good lesson here. Thanks for the thorough explanation! -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v7 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> ` (5 preceding siblings ...) 2015-01-19 19:23 ` [PATCH v6 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-01-19 21:51 ` Ray Jui 2015-01-19 21:51 ` [PATCH v7 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui ` (2 more replies) 2015-02-07 1:28 ` [PATCH v8 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-02-08 5:25 ` [PATCH v9 0/3] Add I2C support to Broadcom iProc Ray Jui 8 siblings, 3 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 21:51 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v6: - Get rid of unnecessary atomic variable usage in the driver - Improve the "waiting for transaction to complete" logic further by making sure there's no pending/ongoing interrupt by the time when flag 'xfer_is_done' is checked - After disabling interrupt with 'writel', add 'readl' to the same register to flush the bus to ensure the write has gone through Changes from v5: - Improve the "waiting for transaction to be complete" logic to take care of the corner case when an interrupt fires after wait_for_completion_timeout times out - Improve the logic to disable I2C interrupt in the remove function. Make it more generic so it works for both dedicated and shared interrupt Changes from v4: - Remove redundant header file includes - Change the logic that waits for the host controller to be idle to simply return -EBUSY - Use proper print level and error codes in the driver - Allow zero length message in the driver to support I2C_SMBUS_QUICK - Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework - Other minor miscellaneous improvements and fixes Changes from v3: - Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms - Improve CPU utilization efficiency in the loop of waiting for bus to idle - Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller - Fix inconsistent coding style and format - Improve the bus speed validation logic in the driver - Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line - Other miscellaneous improvements and fixes Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 505 ++++++++++++++++++++ 5 files changed, 573 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v7 1/3] i2c: iProc: define Broadcom iProc I2C binding 2015-01-19 21:51 ` [PATCH v7 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-01-19 21:51 ` Ray Jui 2015-01-19 21:51 ` [PATCH v7 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui 2015-01-19 21:51 ` [PATCH v7 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 21:51 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v7 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-19 21:51 ` [PATCH v7 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-01-19 21:51 ` [PATCH v7 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2015-01-19 21:51 ` Ray Jui 2015-02-06 22:31 ` [v7,2/3] " Kevin Cernekee 2015-01-19 21:51 ` [PATCH v7 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-01-19 21:51 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 505 ++++++++++++++++++++++++++++++++++++ 3 files changed, 516 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 31e8308..af76d23 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 56388f6..d93b509 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..64c622f --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIM_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + int irq; + + void __iomem *base; + + struct i2c_adapter adapter; + + struct completion done; + int xfer_is_done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = data; + u32 status = readl(iproc_i2c->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, iproc_i2c->base + IS_OFFSET); + iproc_i2c->xfer_is_done = 1; + complete_all(&iproc_i2c->done); + + return IRQ_HANDLED; +} + +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + if (readl(iproc_i2c->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) + return true; + else + return false; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, u8 *addr) +{ + + if (msg->flags & I2C_M_TEN) { + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); + return -EINVAL; + } + + *addr = msg->addr << 1; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + u32 val; + + val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); + return -EAGAIN; + + case M_CMD_STATUS_NACK_ADDR: + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); + return -ENXIO; + + case M_CMD_STATUS_NACK_DATA: + dev_dbg(iproc_i2c->device, "NAK data\n"); + return -ENXIO; + + case M_CMD_STATUS_TIMEOUT: + dev_dbg(iproc_i2c->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); + return -EIO; + } +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(iproc_i2c->device, + "only support data length up to %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + if (bcm_iproc_i2c_bus_busy(iproc_i2c)) { + dev_warn(iproc_i2c->device, "bus is busy\n"); + return -EBUSY; + } + + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, iproc_i2c->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, iproc_i2c->base + M_TX_OFFSET); + } + + if (msg->len == 0) + writel(1 << M_TX_WR_STATUS_SHIFT, + iproc_i2c->base + M_TX_OFFSET); + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&iproc_i2c->done); + iproc_i2c->xfer_is_done = 0; + + /* + * Enable the "start busy" interrupt, which will be triggered after the + * transaction is done, i.e., the internal start_busy bit, transitions + * from 1 to 0. + */ + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, iproc_i2c->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + /* read it back to flush the write */ + readl(iproc_i2c->base + IE_OFFSET); + + /* make sure the interrupt handler isn't running */ + synchronize_irq(iproc_i2c->irq); + + if (!time_left && !iproc_i2c->xfer_is_done) { + dev_err(iproc_i2c->device, "transaction timed out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return -ETIMEDOUT; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(iproc_i2c->device, "**** data start ****\n"); + for (i = 0; i < msg->len; i++) + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); + dev_dbg(iproc_i2c->device, "**** data end ****\n"); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); + if (ret) { + dev_err(iproc_i2c->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_info(iproc_i2c->device, + "unable to interpret clock-frequency DT property\n"); + bus_speed = 100000; + } + + if (bus_speed < 100000) { + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(iproc_i2c->device, + "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } else if (bus_speed < 400000) { + speed_bit = 0; + } else { + /* bus_speed >= 400000 */ + speed_bit = 1; + } + + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + /* clear all pending interrupts */ + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + struct resource *res; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + init_completion(&iproc_i2c->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(iproc_i2c->device, "no irq resource\n"); + return irq; + } + iproc_i2c->irq = irq; + + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, + pdev->name, iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(iproc_i2c); + + adap = &iproc_i2c->adapter; + i2c_set_adapdata(adap, iproc_i2c); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(iproc_i2c->device, "failed to add adapter\n"); + return ret; + } + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + /* make sure there's no pending interrupt when we remove the adapter */ + writel(0, iproc_i2c->base + IE_OFFSET); + readl(iproc_i2c->base + IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + + i2c_del_adapter(&iproc_i2c->adapter); + bcm_iproc_i2c_disable(iproc_i2c); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + { .compatible = "brcm,iproc-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [v7,2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-01-19 21:51 ` [PATCH v7 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2015-02-06 22:31 ` Kevin Cernekee [not found] ` <20150206223149.GB345-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Kevin Cernekee @ 2015-02-06 22:31 UTC (permalink / raw) To: Ray Jui Cc: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, dtor On Mon, Jan 19, 2015 at 01:51:49PM -0800, Ray Jui wrote: > Add initial support to the Broadcom iProc I2C controller found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports two bus speeds > including standard mode (100kHz) and fast mode (400kHz) > > Signed-off-by: Ray Jui <rjui <at> broadcom.com> > Reviewed-by: Scott Branden <sbranden <at> broadcom.com> > --- > drivers/i2c/busses/Kconfig | 10 + > drivers/i2c/busses/Makefile | 1 + > drivers/i2c/busses/i2c-bcm-iproc.c | 505 ++++++++++++++++++++++++++++++++++++ > 3 files changed, 516 insertions(+) > create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c > > diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig > index 31e8308..af76d23 100644 > --- a/drivers/i2c/busses/Kconfig > +++ b/drivers/i2c/busses/Kconfig > <at> <at> -372,6 +372,16 <at> <at> config I2C_BCM2835 > This support is also available as a module. If so, the module > will be called i2c-bcm2835. > > +config I2C_BCM_IPROC > + tristate "Broadcom iProc I2C controller" > + depends on ARCH_BCM_IPROC || COMPILE_TEST > + default ARCH_BCM_IPROC > + help > + If you say yes to this option, support will be included for the > + Broadcom iProc I2C controller. > + > + If you don't know what to do here, say N. > + > config I2C_BCM_KONA > tristate "BCM Kona I2C adapter" > depends on ARCH_BCM_MOBILE > diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile > index 56388f6..d93b509 100644 > --- a/drivers/i2c/busses/Makefile > +++ b/drivers/i2c/busses/Makefile > <at> <at> -33,6 +33,7 <at> <at> obj-$(CONFIG_I2C_AT91) += i2c-at91.o > obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o > obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o > obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o > +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o > obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o > obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o > obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o > diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c > new file mode 100644 > index 0000000..64c622f > --- /dev/null > +++ b/drivers/i2c/busses/i2c-bcm-iproc.c > <at> <at> -0,0 +1,505 <at> <at> > +/* > + * Copyright (C) 2014 Broadcom Corporation > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License as > + * published by the Free Software Foundation version 2. > + * > + * This program is distributed "as is" WITHOUT ANY WARRANTY of any > + * kind, whether express or implied; without even the implied warranty > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/platform_device.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> > + > +#define CFG_OFFSET 0x00 > +#define CFG_RESET_SHIFT 31 > +#define CFG_EN_SHIFT 30 > +#define CFG_M_RETRY_CNT_SHIFT 16 > +#define CFG_M_RETRY_CNT_MASK 0x0f > + > +#define TIM_CFG_OFFSET 0x04 > +#define TIM_CFG_MODE_400_SHIFT 31 > + > +#define M_FIFO_CTRL_OFFSET 0x0c > +#define M_FIFO_RX_FLUSH_SHIFT 31 > +#define M_FIFO_TX_FLUSH_SHIFT 30 > +#define M_FIFO_RX_CNT_SHIFT 16 > +#define M_FIFO_RX_CNT_MASK 0x7f > +#define M_FIFO_RX_THLD_SHIFT 8 > +#define M_FIFO_RX_THLD_MASK 0x3f > + > +#define M_CMD_OFFSET 0x30 > +#define M_CMD_START_BUSY_SHIFT 31 > +#define M_CMD_STATUS_SHIFT 25 > +#define M_CMD_STATUS_MASK 0x07 > +#define M_CMD_STATUS_SUCCESS 0x0 > +#define M_CMD_STATUS_LOST_ARB 0x1 > +#define M_CMD_STATUS_NACK_ADDR 0x2 > +#define M_CMD_STATUS_NACK_DATA 0x3 > +#define M_CMD_STATUS_TIMEOUT 0x4 > +#define M_CMD_PROTOCOL_SHIFT 9 > +#define M_CMD_PROTOCOL_MASK 0xf > +#define M_CMD_PROTOCOL_BLK_WR 0x7 > +#define M_CMD_PROTOCOL_BLK_RD 0x8 > +#define M_CMD_PEC_SHIFT 8 > +#define M_CMD_RD_CNT_SHIFT 0 > +#define M_CMD_RD_CNT_MASK 0xff > + > +#define IE_OFFSET 0x38 > +#define IE_M_RX_FIFO_FULL_SHIFT 31 > +#define IE_M_RX_THLD_SHIFT 30 > +#define IE_M_START_BUSY_SHIFT 28 > + > +#define IS_OFFSET 0x3c > +#define IS_M_RX_FIFO_FULL_SHIFT 31 > +#define IS_M_RX_THLD_SHIFT 30 > +#define IS_M_START_BUSY_SHIFT 28 > + > +#define M_TX_OFFSET 0x40 > +#define M_TX_WR_STATUS_SHIFT 31 > +#define M_TX_DATA_SHIFT 0 > +#define M_TX_DATA_MASK 0xff > + > +#define M_RX_OFFSET 0x44 > +#define M_RX_STATUS_SHIFT 30 > +#define M_RX_STATUS_MASK 0x03 > +#define M_RX_PEC_ERR_SHIFT 29 > +#define M_RX_DATA_SHIFT 0 > +#define M_RX_DATA_MASK 0xff > + > +#define I2C_TIMEOUT_MESC 100 > +#define M_TX_RX_FIFO_SIZE 64 > + > +enum bus_speed_index { > + I2C_SPD_100K = 0, > + I2C_SPD_400K, > +}; > + > +struct bcm_iproc_i2c_dev { > + struct device *device; > + int irq; > + > + void __iomem *base; > + > + struct i2c_adapter adapter; > + > + struct completion done; > + int xfer_is_done; > +}; > + > +/* > + * Can be expanded in the future if more interrupt status bits are utilized > + */ > +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) > + > +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) > +{ > + struct bcm_iproc_i2c_dev *iproc_i2c = data; > + u32 status = readl(iproc_i2c->base + IS_OFFSET); > + > + status &= ISR_MASK; > + > + if (!status) > + return IRQ_NONE; > + > + writel(status, iproc_i2c->base + IS_OFFSET); > + iproc_i2c->xfer_is_done = 1; > + complete_all(&iproc_i2c->done); > + > + return IRQ_HANDLED; > +} > + > +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + if (readl(iproc_i2c->base + M_CMD_OFFSET) & > + (1 << M_CMD_START_BUSY_SHIFT)) > + return true; > + else > + return false; > +} > + > +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, > + struct i2c_msg *msg, u8 *addr) > +{ > + > + if (msg->flags & I2C_M_TEN) { > + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); > + return -EINVAL; > + } This looks harmless, but might be redundant since you aren't advertising I2C_FUNC_10BIT_ADDR anyway. > + > + *addr = msg->addr << 1; > + > + if (msg->flags & I2C_M_RD) > + *addr |= 1; > + > + return 0; > +} > + > +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, > + struct i2c_msg *msg) > +{ > + u32 val; > + > + val = readl(iproc_i2c->base + M_CMD_OFFSET); > + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; > + > + switch (val) { > + case M_CMD_STATUS_SUCCESS: > + return 0; > + > + case M_CMD_STATUS_LOST_ARB: > + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); > + return -EAGAIN; > + > + case M_CMD_STATUS_NACK_ADDR: > + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); > + return -ENXIO; > + > + case M_CMD_STATUS_NACK_DATA: > + dev_dbg(iproc_i2c->device, "NAK data\n"); > + return -ENXIO; > + > + case M_CMD_STATUS_TIMEOUT: > + dev_dbg(iproc_i2c->device, "bus timeout\n"); > + return -ETIMEDOUT; > + > + default: > + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); > + return -EIO; > + } > +} > + > +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, > + struct i2c_msg *msg) > +{ > + int ret, i; > + u8 addr; > + u32 val; > + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); > + > + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { > + dev_err(iproc_i2c->device, > + "only support data length up to %u bytes\n", > + M_TX_RX_FIFO_SIZE - 1); > + return -EINVAL; > + } If the FIFO is 64 bytes, are we limited to 63 bytes of data because the slave address consumes 1 byte of FIFO space? If so, it might be helpful to add a comment to that effect. > + > + if (bcm_iproc_i2c_bus_busy(iproc_i2c)) { > + dev_warn(iproc_i2c->device, "bus is busy\n"); > + return -EBUSY; > + } > + > + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); > + if (ret) > + return ret; > + > + /* load slave address into the TX FIFO */ > + writel(addr, iproc_i2c->base + M_TX_OFFSET); > + > + /* for a write transaction, load data into the TX FIFO */ > + if (!(msg->flags & I2C_M_RD)) { > + for (i = 0; i < msg->len; i++) { > + val = msg->buf[i]; > + > + /* mark the last byte */ > + if (i == msg->len - 1) > + val |= 1 << M_TX_WR_STATUS_SHIFT; > + > + writel(val, iproc_i2c->base + M_TX_OFFSET); > + } If msg->len == 1 and msg->buf[0] == 0x00, we will writel(0x80000000, iproc_i2c->base + M_TX_OFFSET); > + > + if (msg->len == 0) > + writel(1 << M_TX_WR_STATUS_SHIFT, > + iproc_i2c->base + M_TX_OFFSET); ...so if msg->len == 0, does that mean this sends a dummy 0x00 data byte out on the wire? If that's a hardware limitation (M_TX_WR_STATUS_SHIFT prohibited on the address byte), it's probably worth leaving a note in the code. > + } > + > + /* mark as incomplete before starting the transaction */ > + reinit_completion(&iproc_i2c->done); > + iproc_i2c->xfer_is_done = 0; > + > + /* > + * Enable the "start busy" interrupt, which will be triggered after the > + * transaction is done, i.e., the internal start_busy bit, transitions > + * from 1 to 0. > + */ > + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); > + > + /* > + * Now we can activate the transfer. For a read operation, specify the > + * number of bytes to read > + */ > + val = 1 << M_CMD_START_BUSY_SHIFT; > + if (msg->flags & I2C_M_RD) { > + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | > + (msg->len << M_CMD_RD_CNT_SHIFT); > + } else { > + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); > + } > + writel(val, iproc_i2c->base + M_CMD_OFFSET); > + > + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); > + > + /* disable all interrupts */ > + writel(0, iproc_i2c->base + IE_OFFSET); > + /* read it back to flush the write */ > + readl(iproc_i2c->base + IE_OFFSET); > + > + /* make sure the interrupt handler isn't running */ > + synchronize_irq(iproc_i2c->irq); > + > + if (!time_left && !iproc_i2c->xfer_is_done) { > + dev_err(iproc_i2c->device, "transaction timed out\n"); > + > + /* flush FIFOs */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > + (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); > + return -ETIMEDOUT; > + } > + > + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); > + if (ret) { > + /* flush both TX/RX FIFOs */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | > + (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); > + return ret; > + } > + > + /* > + * For a read operation, we now need to load the data from FIFO > + * into the memory buffer > + */ > + if (msg->flags & I2C_M_RD) { > + for (i = 0; i < msg->len; i++) { > + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> > + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; > + } > + } > + > + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", > + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, > + msg->len); > + dev_dbg(iproc_i2c->device, "**** data start ****\n"); > + for (i = 0; i < msg->len; i++) > + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); > + dev_dbg(iproc_i2c->device, "**** data end ****\n"); It might be simpler to just do: print_hex_dump_bytes("iproc_i2c:", DUMP_PREFIX_NONE, msg->buf, msg->len); although you'd lose the ability to see the I2C device name. > + > + return 0; > +} > + > +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, > + struct i2c_msg msgs[], int num) > +{ > + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); > + int ret, i; > + > + /* go through all messages */ > + for (i = 0; i < num; i++) { > + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); > + if (ret) { > + dev_err(iproc_i2c->device, "xfer failed\n"); > + return ret; > + } > + } > + > + return num; > +} > + > +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) > +{ > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; > +} > + > +static const struct i2c_algorithm bcm_iproc_algo = { > + .master_xfer = bcm_iproc_i2c_xfer, > + .functionality = bcm_iproc_i2c_functionality, > +}; > + > +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + unsigned int bus_speed, speed_bit; > + u32 val; > + int ret = of_property_read_u32(iproc_i2c->device->of_node, > + "clock-frequency", &bus_speed); > + if (ret < 0) { > + dev_info(iproc_i2c->device, > + "unable to interpret clock-frequency DT property\n"); > + bus_speed = 100000; > + } > + > + if (bus_speed < 100000) { > + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", > + bus_speed); > + dev_err(iproc_i2c->device, > + "valid speeds are 100khz and 400khz\n"); > + return -EINVAL; > + } else if (bus_speed < 400000) { > + speed_bit = 0; > + } else { > + /* bus_speed >= 400000 */ > + speed_bit = 1; > + } > + > + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); > + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); > + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; > + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); > + > + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); The message would be more accurate if it reported 100 kHz or 400 kHz, since the driver isn't able to support arbitrary speeds. Somebody could be surprised if they ask for 200 kHz but the transactions run at 100 kHz. > + > + return 0; > +} > + > +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + u32 val; > + > + /* put controller in reset */ > + val = readl(iproc_i2c->base + CFG_OFFSET); > + val |= 1 << CFG_RESET_SHIFT; > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, iproc_i2c->base + CFG_OFFSET); > + > + /* wait 100 usec per spec */ > + udelay(100); > + > + /* bring controller out of reset */ > + val &= ~(1 << CFG_RESET_SHIFT); > + writel(val, iproc_i2c->base + CFG_OFFSET); > + > + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ > + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); > + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); > + > + /* disable all interrupts */ > + writel(0, iproc_i2c->base + IE_OFFSET); > + > + /* clear all pending interrupts */ > + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); > + > + return 0; > +} > + > +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + u32 val; > + > + val = readl(iproc_i2c->base + CFG_OFFSET); > + val |= 1 << CFG_EN_SHIFT; > + writel(val, iproc_i2c->base + CFG_OFFSET); > +} > + > +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + u32 val; > + > + val = readl(iproc_i2c->base + CFG_OFFSET); > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, iproc_i2c->base + CFG_OFFSET); > +} > + > +static int bcm_iproc_i2c_probe(struct platform_device *pdev) > +{ > + int irq, ret = 0; > + struct bcm_iproc_i2c_dev *iproc_i2c; > + struct i2c_adapter *adap; > + struct resource *res; > + > + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), > + GFP_KERNEL); > + if (!iproc_i2c) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, iproc_i2c); > + iproc_i2c->device = &pdev->dev; > + init_completion(&iproc_i2c->done); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); > + if (IS_ERR(iproc_i2c->base)) > + return PTR_ERR(iproc_i2c->base); > + > + ret = bcm_iproc_i2c_init(iproc_i2c); > + if (ret) > + return ret; > + > + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); > + if (ret) > + return ret; > + > + irq = platform_get_irq(pdev, 0); > + if (irq <= 0) { > + dev_err(iproc_i2c->device, "no irq resource\n"); > + return irq; > + } AFAICT platform_get_irq() can return IRQ 0 on success. Unlike irq_of_parse_and_map(). Other than that it looks fine to me, so for all three patches in the series: Reviewed-by: Kevin Cernekee <cernekee@chromium.org> > + iproc_i2c->irq = irq; > + > + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, > + pdev->name, iproc_i2c); > + if (ret < 0) { > + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); > + return ret; > + } > + > + bcm_iproc_i2c_enable(iproc_i2c); > + > + adap = &iproc_i2c->adapter; > + i2c_set_adapdata(adap, iproc_i2c); > + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); > + adap->algo = &bcm_iproc_algo; > + adap->dev.parent = &pdev->dev; > + adap->dev.of_node = pdev->dev.of_node; > + > + ret = i2c_add_adapter(adap); > + if (ret) { > + dev_err(iproc_i2c->device, "failed to add adapter\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int bcm_iproc_i2c_remove(struct platform_device *pdev) > +{ > + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); > + > + /* make sure there's no pending interrupt when we remove the adapter */ > + writel(0, iproc_i2c->base + IE_OFFSET); > + readl(iproc_i2c->base + IE_OFFSET); > + synchronize_irq(iproc_i2c->irq); > + > + i2c_del_adapter(&iproc_i2c->adapter); > + bcm_iproc_i2c_disable(iproc_i2c); > + > + return 0; > +} > + > +static const struct of_device_id bcm_iproc_i2c_of_match[] = { > + { .compatible = "brcm,iproc-i2c" }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); > + > +static struct platform_driver bcm_iproc_i2c_driver = { > + .driver = { > + .name = "bcm-iproc-i2c", > + .of_match_table = bcm_iproc_i2c_of_match, > + }, > + .probe = bcm_iproc_i2c_probe, > + .remove = bcm_iproc_i2c_remove, > +}; > +module_platform_driver(bcm_iproc_i2c_driver); > + > +MODULE_AUTHOR("Ray Jui <rjui <at> broadcom.com>"); > +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); > +MODULE_LICENSE("GPL v2"); ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <20150206223149.GB345-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org>]
* Re: [v7,2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150206223149.GB345-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> @ 2015-02-06 22:48 ` Dmitry Torokhov [not found] ` <CAE_wzQ-POweLLmTyHoMvs_NESjW5UmPxh2ZQCaW4-W74MsrHag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2015-02-07 0:54 ` Ray Jui 1 sibling, 1 reply; 106+ messages in thread From: Dmitry Torokhov @ 2015-02-06 22:48 UTC (permalink / raw) To: Kevin Cernekee Cc: Ray Jui, Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Dmitry Torokhov On Fri, Feb 6, 2015 at 2:31 PM, Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote: > On Mon, Jan 19, 2015 at 01:51:49PM -0800, Ray Jui wrote: >> + >> + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", >> + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, >> + msg->len); >> + dev_dbg(iproc_i2c->device, "**** data start ****\n"); >> + for (i = 0; i < msg->len; i++) >> + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); >> + dev_dbg(iproc_i2c->device, "**** data end ****\n"); > > It might be simpler to just do: > > print_hex_dump_bytes("iproc_i2c:", DUMP_PREFIX_NONE, msg->buf, msg->len); > > although you'd lose the ability to see the I2C device name. We can also do: dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf); if we are OK with limiting output to 64 bytes. Thanks, Dmitry ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <CAE_wzQ-POweLLmTyHoMvs_NESjW5UmPxh2ZQCaW4-W74MsrHag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>]
* Re: [v7,2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <CAE_wzQ-POweLLmTyHoMvs_NESjW5UmPxh2ZQCaW4-W74MsrHag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> @ 2015-02-06 23:01 ` Kevin Cernekee 0 siblings, 0 replies; 106+ messages in thread From: Kevin Cernekee @ 2015-02-06 23:01 UTC (permalink / raw) To: Dmitry Torokhov Cc: Ray Jui, Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On Fri, Feb 6, 2015 at 2:48 PM, Dmitry Torokhov <dtor-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote: > On Fri, Feb 6, 2015 at 2:31 PM, Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote: >> On Mon, Jan 19, 2015 at 01:51:49PM -0800, Ray Jui wrote: >>> + >>> + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", >>> + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, >>> + msg->len); >>> + dev_dbg(iproc_i2c->device, "**** data start ****\n"); >>> + for (i = 0; i < msg->len; i++) >>> + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); >>> + dev_dbg(iproc_i2c->device, "**** data end ****\n"); >> >> It might be simpler to just do: >> >> print_hex_dump_bytes("iproc_i2c:", DUMP_PREFIX_NONE, msg->buf, msg->len); >> >> although you'd lose the ability to see the I2C device name. > > We can also do: > > dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf); > > if we are OK with limiting output to 64 bytes. msg->len is capped at 63 due to hardware limits, so that should work. ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [v7,2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <20150206223149.GB345-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> 2015-02-06 22:48 ` Dmitry Torokhov @ 2015-02-07 0:54 ` Ray Jui 1 sibling, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-02-07 0:54 UTC (permalink / raw) To: Kevin Cernekee Cc: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, dtor-F7+t8E8rja9g9hUCZPvPmw On 2/6/2015 2:31 PM, Kevin Cernekee wrote: > On Mon, Jan 19, 2015 at 01:51:49PM -0800, Ray Jui wrote: >> Add initial support to the Broadcom iProc I2C controller found in the >> iProc family of SoCs. >> >> The iProc I2C controller has separate internal TX and RX FIFOs, each has >> a size of 64 bytes. The iProc I2C controller supports two bus speeds >> including standard mode (100kHz) and fast mode (400kHz) >> >> Signed-off-by: Ray Jui <rjui <at> broadcom.com> >> Reviewed-by: Scott Branden <sbranden <at> broadcom.com> >> --- >> drivers/i2c/busses/Kconfig | 10 + >> drivers/i2c/busses/Makefile | 1 + >> drivers/i2c/busses/i2c-bcm-iproc.c | 505 ++++++++++++++++++++++++++++++++++++ >> 3 files changed, 516 insertions(+) >> create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c >> >> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig >> index 31e8308..af76d23 100644 >> --- a/drivers/i2c/busses/Kconfig >> +++ b/drivers/i2c/busses/Kconfig >> <at> <at> -372,6 +372,16 <at> <at> config I2C_BCM2835 >> This support is also available as a module. If so, the module >> will be called i2c-bcm2835. >> >> +config I2C_BCM_IPROC >> + tristate "Broadcom iProc I2C controller" >> + depends on ARCH_BCM_IPROC || COMPILE_TEST >> + default ARCH_BCM_IPROC >> + help >> + If you say yes to this option, support will be included for the >> + Broadcom iProc I2C controller. >> + >> + If you don't know what to do here, say N. >> + >> config I2C_BCM_KONA >> tristate "BCM Kona I2C adapter" >> depends on ARCH_BCM_MOBILE >> diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile >> index 56388f6..d93b509 100644 >> --- a/drivers/i2c/busses/Makefile >> +++ b/drivers/i2c/busses/Makefile >> <at> <at> -33,6 +33,7 <at> <at> obj-$(CONFIG_I2C_AT91) += i2c-at91.o >> obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o >> obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o >> obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o >> +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o >> obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o >> obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o >> obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o >> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c >> new file mode 100644 >> index 0000000..64c622f >> --- /dev/null >> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c >> <at> <at> -0,0 +1,505 <at> <at> >> +/* >> + * Copyright (C) 2014 Broadcom Corporation >> + * >> + * This program is free software; you can redistribute it and/or >> + * modify it under the terms of the GNU General Public License as >> + * published by the Free Software Foundation version 2. >> + * >> + * This program is distributed "as is" WITHOUT ANY WARRANTY of any >> + * kind, whether express or implied; without even the implied warranty >> + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/platform_device.h> >> +#include <linux/io.h> >> +#include <linux/slab.h> >> +#include <linux/delay.h> >> + >> +#define CFG_OFFSET 0x00 >> +#define CFG_RESET_SHIFT 31 >> +#define CFG_EN_SHIFT 30 >> +#define CFG_M_RETRY_CNT_SHIFT 16 >> +#define CFG_M_RETRY_CNT_MASK 0x0f >> + >> +#define TIM_CFG_OFFSET 0x04 >> +#define TIM_CFG_MODE_400_SHIFT 31 >> + >> +#define M_FIFO_CTRL_OFFSET 0x0c >> +#define M_FIFO_RX_FLUSH_SHIFT 31 >> +#define M_FIFO_TX_FLUSH_SHIFT 30 >> +#define M_FIFO_RX_CNT_SHIFT 16 >> +#define M_FIFO_RX_CNT_MASK 0x7f >> +#define M_FIFO_RX_THLD_SHIFT 8 >> +#define M_FIFO_RX_THLD_MASK 0x3f >> + >> +#define M_CMD_OFFSET 0x30 >> +#define M_CMD_START_BUSY_SHIFT 31 >> +#define M_CMD_STATUS_SHIFT 25 >> +#define M_CMD_STATUS_MASK 0x07 >> +#define M_CMD_STATUS_SUCCESS 0x0 >> +#define M_CMD_STATUS_LOST_ARB 0x1 >> +#define M_CMD_STATUS_NACK_ADDR 0x2 >> +#define M_CMD_STATUS_NACK_DATA 0x3 >> +#define M_CMD_STATUS_TIMEOUT 0x4 >> +#define M_CMD_PROTOCOL_SHIFT 9 >> +#define M_CMD_PROTOCOL_MASK 0xf >> +#define M_CMD_PROTOCOL_BLK_WR 0x7 >> +#define M_CMD_PROTOCOL_BLK_RD 0x8 >> +#define M_CMD_PEC_SHIFT 8 >> +#define M_CMD_RD_CNT_SHIFT 0 >> +#define M_CMD_RD_CNT_MASK 0xff >> + >> +#define IE_OFFSET 0x38 >> +#define IE_M_RX_FIFO_FULL_SHIFT 31 >> +#define IE_M_RX_THLD_SHIFT 30 >> +#define IE_M_START_BUSY_SHIFT 28 >> + >> +#define IS_OFFSET 0x3c >> +#define IS_M_RX_FIFO_FULL_SHIFT 31 >> +#define IS_M_RX_THLD_SHIFT 30 >> +#define IS_M_START_BUSY_SHIFT 28 >> + >> +#define M_TX_OFFSET 0x40 >> +#define M_TX_WR_STATUS_SHIFT 31 >> +#define M_TX_DATA_SHIFT 0 >> +#define M_TX_DATA_MASK 0xff >> + >> +#define M_RX_OFFSET 0x44 >> +#define M_RX_STATUS_SHIFT 30 >> +#define M_RX_STATUS_MASK 0x03 >> +#define M_RX_PEC_ERR_SHIFT 29 >> +#define M_RX_DATA_SHIFT 0 >> +#define M_RX_DATA_MASK 0xff >> + >> +#define I2C_TIMEOUT_MESC 100 >> +#define M_TX_RX_FIFO_SIZE 64 >> + >> +enum bus_speed_index { >> + I2C_SPD_100K = 0, >> + I2C_SPD_400K, >> +}; >> + >> +struct bcm_iproc_i2c_dev { >> + struct device *device; >> + int irq; >> + >> + void __iomem *base; >> + >> + struct i2c_adapter adapter; >> + >> + struct completion done; >> + int xfer_is_done; >> +}; >> + >> +/* >> + * Can be expanded in the future if more interrupt status bits are utilized >> + */ >> +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) >> + >> +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) >> +{ >> + struct bcm_iproc_i2c_dev *iproc_i2c = data; >> + u32 status = readl(iproc_i2c->base + IS_OFFSET); >> + >> + status &= ISR_MASK; >> + >> + if (!status) >> + return IRQ_NONE; >> + >> + writel(status, iproc_i2c->base + IS_OFFSET); >> + iproc_i2c->xfer_is_done = 1; >> + complete_all(&iproc_i2c->done); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + if (readl(iproc_i2c->base + M_CMD_OFFSET) & >> + (1 << M_CMD_START_BUSY_SHIFT)) >> + return true; >> + else >> + return false; >> +} >> + >> +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, >> + struct i2c_msg *msg, u8 *addr) >> +{ >> + >> + if (msg->flags & I2C_M_TEN) { >> + dev_err(iproc_i2c->device, "no support for 10-bit address\n"); >> + return -EINVAL; >> + } > > This looks harmless, but might be redundant since you aren't advertising I2C_FUNC_10BIT_ADDR anyway. > Okay I'll remove that. >> + >> + *addr = msg->addr << 1; >> + >> + if (msg->flags & I2C_M_RD) >> + *addr |= 1; >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, >> + struct i2c_msg *msg) >> +{ >> + u32 val; >> + >> + val = readl(iproc_i2c->base + M_CMD_OFFSET); >> + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; >> + >> + switch (val) { >> + case M_CMD_STATUS_SUCCESS: >> + return 0; >> + >> + case M_CMD_STATUS_LOST_ARB: >> + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); >> + return -EAGAIN; >> + >> + case M_CMD_STATUS_NACK_ADDR: >> + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); >> + return -ENXIO; >> + >> + case M_CMD_STATUS_NACK_DATA: >> + dev_dbg(iproc_i2c->device, "NAK data\n"); >> + return -ENXIO; >> + >> + case M_CMD_STATUS_TIMEOUT: >> + dev_dbg(iproc_i2c->device, "bus timeout\n"); >> + return -ETIMEDOUT; >> + >> + default: >> + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); >> + return -EIO; >> + } >> +} >> + >> +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, >> + struct i2c_msg *msg) >> +{ >> + int ret, i; >> + u8 addr; >> + u32 val; >> + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); >> + >> + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { >> + dev_err(iproc_i2c->device, >> + "only support data length up to %u bytes\n", >> + M_TX_RX_FIFO_SIZE - 1); >> + return -EINVAL; >> + } > > If the FIFO is 64 bytes, are we limited to 63 bytes of data because the slave address consumes 1 byte of FIFO space? If so, it might be helpful to add a comment to that effect. > Yeah I'll add a comment to explain this. >> + >> + if (bcm_iproc_i2c_bus_busy(iproc_i2c)) { >> + dev_warn(iproc_i2c->device, "bus is busy\n"); >> + return -EBUSY; >> + } >> + >> + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); >> + if (ret) >> + return ret; >> + >> + /* load slave address into the TX FIFO */ >> + writel(addr, iproc_i2c->base + M_TX_OFFSET); >> + >> + /* for a write transaction, load data into the TX FIFO */ >> + if (!(msg->flags & I2C_M_RD)) { >> + for (i = 0; i < msg->len; i++) { >> + val = msg->buf[i]; >> + >> + /* mark the last byte */ >> + if (i == msg->len - 1) >> + val |= 1 << M_TX_WR_STATUS_SHIFT; >> + >> + writel(val, iproc_i2c->base + M_TX_OFFSET); >> + } > > If msg->len == 1 and msg->buf[0] == 0x00, we will writel(0x80000000, iproc_i2c->base + M_TX_OFFSET); > >> + >> + if (msg->len == 0) >> + writel(1 << M_TX_WR_STATUS_SHIFT, >> + iproc_i2c->base + M_TX_OFFSET); > > ...so if msg->len == 0, does that mean this sends a dummy 0x00 data byte out on the wire? > > If that's a hardware limitation (M_TX_WR_STATUS_SHIFT prohibited on the address byte), it's probably worth leaving a note in the code. > In fact, I made a mistake here. The if statement should be completely removed. Thanks for catching this! >> + } >> + >> + /* mark as incomplete before starting the transaction */ >> + reinit_completion(&iproc_i2c->done); >> + iproc_i2c->xfer_is_done = 0; >> + >> + /* >> + * Enable the "start busy" interrupt, which will be triggered after the >> + * transaction is done, i.e., the internal start_busy bit, transitions >> + * from 1 to 0. >> + */ >> + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); >> + >> + /* >> + * Now we can activate the transfer. For a read operation, specify the >> + * number of bytes to read >> + */ >> + val = 1 << M_CMD_START_BUSY_SHIFT; >> + if (msg->flags & I2C_M_RD) { >> + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | >> + (msg->len << M_CMD_RD_CNT_SHIFT); >> + } else { >> + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); >> + } >> + writel(val, iproc_i2c->base + M_CMD_OFFSET); >> + >> + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); >> + >> + /* disable all interrupts */ >> + writel(0, iproc_i2c->base + IE_OFFSET); >> + /* read it back to flush the write */ >> + readl(iproc_i2c->base + IE_OFFSET); >> + >> + /* make sure the interrupt handler isn't running */ >> + synchronize_irq(iproc_i2c->irq); >> + >> + if (!time_left && !iproc_i2c->xfer_is_done) { >> + dev_err(iproc_i2c->device, "transaction timed out\n"); >> + >> + /* flush FIFOs */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> + (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); >> + return -ETIMEDOUT; >> + } >> + >> + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); >> + if (ret) { >> + /* flush both TX/RX FIFOs */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | >> + (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); >> + return ret; >> + } >> + >> + /* >> + * For a read operation, we now need to load the data from FIFO >> + * into the memory buffer >> + */ >> + if (msg->flags & I2C_M_RD) { >> + for (i = 0; i < msg->len; i++) { >> + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; >> + } >> + } >> + >> + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", >> + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, >> + msg->len); >> + dev_dbg(iproc_i2c->device, "**** data start ****\n"); >> + for (i = 0; i < msg->len; i++) >> + dev_dbg(iproc_i2c->device, "0x%02x ", msg->buf[i]); >> + dev_dbg(iproc_i2c->device, "**** data end ****\n"); > > It might be simpler to just do: > > print_hex_dump_bytes("iproc_i2c:", DUMP_PREFIX_NONE, msg->buf, msg->len); > > although you'd lose the ability to see the I2C device name. > Great! I'll change this to: dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf); per discussions between you and Dmitry. >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, >> + struct i2c_msg msgs[], int num) >> +{ >> + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); >> + int ret, i; >> + >> + /* go through all messages */ >> + for (i = 0; i < num; i++) { >> + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); >> + if (ret) { >> + dev_err(iproc_i2c->device, "xfer failed\n"); >> + return ret; >> + } >> + } >> + >> + return num; >> +} >> + >> +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) >> +{ >> + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; >> +} >> + >> +static const struct i2c_algorithm bcm_iproc_algo = { >> + .master_xfer = bcm_iproc_i2c_xfer, >> + .functionality = bcm_iproc_i2c_functionality, >> +}; >> + >> +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + unsigned int bus_speed, speed_bit; >> + u32 val; >> + int ret = of_property_read_u32(iproc_i2c->device->of_node, >> + "clock-frequency", &bus_speed); >> + if (ret < 0) { >> + dev_info(iproc_i2c->device, >> + "unable to interpret clock-frequency DT property\n"); >> + bus_speed = 100000; >> + } >> + >> + if (bus_speed < 100000) { >> + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", >> + bus_speed); >> + dev_err(iproc_i2c->device, >> + "valid speeds are 100khz and 400khz\n"); >> + return -EINVAL; >> + } else if (bus_speed < 400000) { >> + speed_bit = 0; >> + } else { >> + /* bus_speed >= 400000 */ >> + speed_bit = 1; >> + } >> + >> + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); >> + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); >> + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; >> + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); >> + >> + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); > > The message would be more accurate if it reported 100 kHz or 400 kHz, since the driver isn't able to support arbitrary speeds. Somebody could be surprised if they ask for 200 kHz but the transactions run at 100 kHz. > Right. I'll update bus_speed to the real speed that it got set to in the above code. >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + u32 val; >> + >> + /* put controller in reset */ >> + val = readl(iproc_i2c->base + CFG_OFFSET); >> + val |= 1 << CFG_RESET_SHIFT; >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, iproc_i2c->base + CFG_OFFSET); >> + >> + /* wait 100 usec per spec */ >> + udelay(100); >> + >> + /* bring controller out of reset */ >> + val &= ~(1 << CFG_RESET_SHIFT); >> + writel(val, iproc_i2c->base + CFG_OFFSET); >> + >> + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ >> + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); >> + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); >> + >> + /* disable all interrupts */ >> + writel(0, iproc_i2c->base + IE_OFFSET); >> + >> + /* clear all pending interrupts */ >> + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); >> + >> + return 0; >> +} >> + >> +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + u32 val; >> + >> + val = readl(iproc_i2c->base + CFG_OFFSET); >> + val |= 1 << CFG_EN_SHIFT; >> + writel(val, iproc_i2c->base + CFG_OFFSET); >> +} >> + >> +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + u32 val; >> + >> + val = readl(iproc_i2c->base + CFG_OFFSET); >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, iproc_i2c->base + CFG_OFFSET); >> +} >> + >> +static int bcm_iproc_i2c_probe(struct platform_device *pdev) >> +{ >> + int irq, ret = 0; >> + struct bcm_iproc_i2c_dev *iproc_i2c; >> + struct i2c_adapter *adap; >> + struct resource *res; >> + >> + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), >> + GFP_KERNEL); >> + if (!iproc_i2c) >> + return -ENOMEM; >> + >> + platform_set_drvdata(pdev, iproc_i2c); >> + iproc_i2c->device = &pdev->dev; >> + init_completion(&iproc_i2c->done); >> + >> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); >> + if (IS_ERR(iproc_i2c->base)) >> + return PTR_ERR(iproc_i2c->base); >> + >> + ret = bcm_iproc_i2c_init(iproc_i2c); >> + if (ret) >> + return ret; >> + >> + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); >> + if (ret) >> + return ret; >> + >> + irq = platform_get_irq(pdev, 0); >> + if (irq <= 0) { >> + dev_err(iproc_i2c->device, "no irq resource\n"); >> + return irq; >> + } > > AFAICT platform_get_irq() can return IRQ 0 on success. Unlike irq_of_parse_and_map(). > In fact, irq_create_of_mapping (platform_get_irq -> of_irq_get -> irq_create_of_mapping) can return 0 when failed. > Other than that it looks fine to me, so for all three patches in the series: > > Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> > > Thank you so much for the review! >> + iproc_i2c->irq = irq; >> + >> + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, >> + pdev->name, iproc_i2c); >> + if (ret < 0) { >> + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); >> + return ret; >> + } >> + >> + bcm_iproc_i2c_enable(iproc_i2c); >> + >> + adap = &iproc_i2c->adapter; >> + i2c_set_adapdata(adap, iproc_i2c); >> + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); >> + adap->algo = &bcm_iproc_algo; >> + adap->dev.parent = &pdev->dev; >> + adap->dev.of_node = pdev->dev.of_node; >> + >> + ret = i2c_add_adapter(adap); >> + if (ret) { >> + dev_err(iproc_i2c->device, "failed to add adapter\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int bcm_iproc_i2c_remove(struct platform_device *pdev) >> +{ >> + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); >> + >> + /* make sure there's no pending interrupt when we remove the adapter */ >> + writel(0, iproc_i2c->base + IE_OFFSET); >> + readl(iproc_i2c->base + IE_OFFSET); >> + synchronize_irq(iproc_i2c->irq); >> + >> + i2c_del_adapter(&iproc_i2c->adapter); >> + bcm_iproc_i2c_disable(iproc_i2c); >> + >> + return 0; >> +} >> + >> +static const struct of_device_id bcm_iproc_i2c_of_match[] = { >> + { .compatible = "brcm,iproc-i2c" }, >> + { /* sentinel */ } >> +}; >> +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); >> + >> +static struct platform_driver bcm_iproc_i2c_driver = { >> + .driver = { >> + .name = "bcm-iproc-i2c", >> + .of_match_table = bcm_iproc_i2c_of_match, >> + }, >> + .probe = bcm_iproc_i2c_probe, >> + .remove = bcm_iproc_i2c_remove, >> +}; >> +module_platform_driver(bcm_iproc_i2c_driver); >> + >> +MODULE_AUTHOR("Ray Jui <rjui <at> broadcom.com>"); >> +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); >> +MODULE_LICENSE("GPL v2"); -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v7 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2015-01-19 21:51 ` [PATCH v7 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-01-19 21:51 ` [PATCH v7 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2015-01-19 21:51 ` [PATCH v7 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2015-01-19 21:51 ` Ray Jui 2 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-01-19 21:51 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..ff5fb6a 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v8 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> ` (6 preceding siblings ...) 2015-01-19 21:51 ` [PATCH v7 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-02-07 1:28 ` Ray Jui 2015-02-07 1:28 ` [PATCH v8 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui ` (2 more replies) 2015-02-08 5:25 ` [PATCH v9 0/3] Add I2C support to Broadcom iProc Ray Jui 8 siblings, 3 replies; 106+ messages in thread From: Ray Jui @ 2015-02-07 1:28 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Changes from v7: - Remove redundant 10-bit address check in the driver - Fix the driver that accidentally emits 1-byte of data with zero content in the case of SMBUS quick command - Improve debugging prints in the driver - Other minor improvements Changes from v6: - Get rid of unnecessary atomic variable usage in the driver - Improve the "waiting for transaction to complete" logic further by making sure there's no pending/ongoing interrupt by the time when flag 'xfer_is_done' is checked - After disabling interrupt with 'writel', add 'readl' to the same register to flush the bus to ensure the write has gone through Changes from v5: - Improve the "waiting for transaction to be complete" logic to take care of the corner case when an interrupt fires after wait_for_completion_timeout times out - Improve the logic to disable I2C interrupt in the remove function. Make it more generic so it works for both dedicated and shared interrupt Changes from v4: - Remove redundant header file includes - Change the logic that waits for the host controller to be idle to simply return -EBUSY - Use proper print level and error codes in the driver - Allow zero length message in the driver to support I2C_SMBUS_QUICK - Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework - Other minor miscellaneous improvements and fixes Changes from v3: - Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms - Improve CPU utilization efficiency in the loop of waiting for bus to idle - Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller - Fix inconsistent coding style and format - Improve the bus speed validation logic in the driver - Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line - Other miscellaneous improvements and fixes Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 494 ++++++++++++++++++++ 5 files changed, 562 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v8 1/3] i2c: iProc: define Broadcom iProc I2C binding 2015-02-07 1:28 ` [PATCH v8 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-02-07 1:28 ` Ray Jui 2015-02-07 1:28 ` [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui 2015-02-07 1:28 ` [PATCH v8 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-02-07 1:28 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> Reviewed-by: Kevin Cernekee <cernekee@chromium.org> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-07 1:28 ` [PATCH v8 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-02-07 1:28 ` [PATCH v8 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui @ 2015-02-07 1:28 ` Ray Jui [not found] ` <1423272507-18459-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-07 1:28 ` [PATCH v8 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-07 1:28 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> Reviewed-by: Kevin Cernekee <cernekee@chromium.org> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 494 ++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index ab838d9..3d08731 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 56388f6..d93b509 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..5d0a03f --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,494 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/delay.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIM_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + int irq; + + void __iomem *base; + + struct i2c_adapter adapter; + + struct completion done; + int xfer_is_done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = data; + u32 status = readl(iproc_i2c->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, iproc_i2c->base + IS_OFFSET); + iproc_i2c->xfer_is_done = 1; + complete_all(&iproc_i2c->done); + + return IRQ_HANDLED; +} + +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + if (readl(iproc_i2c->base + M_CMD_OFFSET) & + (1 << M_CMD_START_BUSY_SHIFT)) + return true; + else + return false; +} + +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg, u8 *addr) +{ + *addr = msg->addr << 1; + + if (msg->flags & I2C_M_RD) + *addr |= 1; + + return 0; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + u32 val; + + val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); + return -EAGAIN; + + case M_CMD_STATUS_NACK_ADDR: + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); + return -ENXIO; + + case M_CMD_STATUS_NACK_DATA: + dev_dbg(iproc_i2c->device, "NAK data\n"); + return -ENXIO; + + case M_CMD_STATUS_TIMEOUT: + dev_dbg(iproc_i2c->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); + return -EIO; + } +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + /* need to reserve one byte in the FIFO for the slave address */ + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(iproc_i2c->device, + "only support data length up to %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EINVAL; + } + + if (bcm_iproc_i2c_bus_busy(iproc_i2c)) { + dev_warn(iproc_i2c->device, "bus is busy\n"); + return -EBUSY; + } + + ret = bcm_iproc_i2c_format_addr(iproc_i2c, msg, &addr); + if (ret) + return ret; + + /* load slave address into the TX FIFO */ + writel(addr, iproc_i2c->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, iproc_i2c->base + M_TX_OFFSET); + } + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&iproc_i2c->done); + iproc_i2c->xfer_is_done = 0; + + /* + * Enable the "start busy" interrupt, which will be triggered after the + * transaction is done, i.e., the internal start_busy bit, transitions + * from 1 to 0. + */ + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, iproc_i2c->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + /* read it back to flush the write */ + readl(iproc_i2c->base + IE_OFFSET); + + /* make sure the interrupt handler isn't running */ + synchronize_irq(iproc_i2c->irq); + + if (!time_left && !iproc_i2c->xfer_is_done) { + dev_err(iproc_i2c->device, "transaction timed out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return -ETIMEDOUT; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, + msg->len); + dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf); + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); + if (ret) { + dev_dbg(iproc_i2c->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned int bus_speed, speed_bit; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_info(iproc_i2c->device, + "unable to interpret clock-frequency DT property\n"); + bus_speed = 100000; + } + + if (bus_speed < 100000) { + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(iproc_i2c->device, + "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } else if (bus_speed < 400000) { + bus_speed = 100000; + speed_bit = 0; + } else { + bus_speed = 400000; + speed_bit = 1; + } + + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + /* clear all pending interrupts */ + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_EN_SHIFT; + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + struct resource *res; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + init_completion(&iproc_i2c->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(iproc_i2c->device, "no irq resource\n"); + return irq; + } + iproc_i2c->irq = irq; + + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, + pdev->name, iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable(iproc_i2c); + + adap = &iproc_i2c->adapter; + i2c_set_adapdata(adap, iproc_i2c); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(iproc_i2c->device, "failed to add adapter\n"); + return ret; + } + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + /* make sure there's no pending interrupt when we remove the adapter */ + writel(0, iproc_i2c->base + IE_OFFSET); + readl(iproc_i2c->base + IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + + i2c_del_adapter(&iproc_i2c->adapter); + bcm_iproc_i2c_disable(iproc_i2c); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + { .compatible = "brcm,iproc-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1423272507-18459-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1423272507-18459-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-07 17:50 ` Wolfram Sang 2015-02-08 5:08 ` Ray Jui 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-02-07 17:50 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 3332 bytes --] Hi Ray, On Fri, Feb 06, 2015 at 05:28:26PM -0800, Ray Jui wrote: > Add initial support to the Broadcom iProc I2C controller found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports two bus speeds > including standard mode (100kHz) and fast mode (400kHz) Mostly looking good. > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/platform_device.h> > +#include <linux/io.h> > +#include <linux/slab.h> > +#include <linux/delay.h> Please sort the includes. > +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + if (readl(iproc_i2c->base + M_CMD_OFFSET) & > + (1 << M_CMD_START_BUSY_SHIFT)) > + return true; > + else > + return false; > +} Minor: return !!(readl(...))? You decide. > + > +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, > + struct i2c_msg *msg, u8 *addr) > +{ > + *addr = msg->addr << 1; > + > + if (msg->flags & I2C_M_RD) > + *addr |= 1; > + > + return 0; > +} I'd suggest a oneliner. *addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0) Or use !! like above. Don't do an extra function for that. It is only used once and it also doesn't need to be int since it can't fail anyhow. (Note to self: I should make a macro for that in i2c.h) > + /* need to reserve one byte in the FIFO for the slave address */ > + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { > + dev_err(iproc_i2c->device, > + "only support data length up to %u bytes\n", > + M_TX_RX_FIFO_SIZE - 1); > + return -EINVAL; -EOPNOTSUPP Is it really a HW limitation? Could the driver later be extended to continue filling the FIFO if a certain threshold is reached? > + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", > + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, > + msg->len); > + dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf); Not really needed. We have tracing for that. > + if (bus_speed < 100000) { > + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", > + bus_speed); > + dev_err(iproc_i2c->device, > + "valid speeds are 100khz and 400khz\n"); > + return -EINVAL; > + } else if (bus_speed < 400000) { > + bus_speed = 100000; > + speed_bit = 0; > + } else { > + bus_speed = 400000; > + speed_bit = 1; > + } > + > + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); > + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); > + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; val |= (bus_speed == 400000) ... and skip speed_bit? You decide. > +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + u32 val; > + > + val = readl(iproc_i2c->base + CFG_OFFSET); > + val |= 1 << CFG_EN_SHIFT; > + writel(val, iproc_i2c->base + CFG_OFFSET); > +} > + > +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) > +{ > + u32 val; > + > + val = readl(iproc_i2c->base + CFG_OFFSET); > + val &= ~(1 << CFG_EN_SHIFT); > + writel(val, iproc_i2c->base + CFG_OFFSET); > +} Extra functions? They are self explaining and only used once. You decide. Rest looks fine, thanks! [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-07 17:50 ` Wolfram Sang @ 2015-02-08 5:08 ` Ray Jui 2015-02-08 11:03 ` Wolfram Sang 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-08 5:08 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 2/7/2015 9:50 AM, Wolfram Sang wrote: > Hi Ray, > > On Fri, Feb 06, 2015 at 05:28:26PM -0800, Ray Jui wrote: >> Add initial support to the Broadcom iProc I2C controller found in the >> iProc family of SoCs. >> >> The iProc I2C controller has separate internal TX and RX FIFOs, each has >> a size of 64 bytes. The iProc I2C controller supports two bus speeds >> including standard mode (100kHz) and fast mode (400kHz) > > Mostly looking good. > >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/i2c.h> >> +#include <linux/interrupt.h> >> +#include <linux/platform_device.h> >> +#include <linux/io.h> >> +#include <linux/slab.h> >> +#include <linux/delay.h> > > Please sort the includes. > Will do. >> +static bool bcm_iproc_i2c_bus_busy(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + if (readl(iproc_i2c->base + M_CMD_OFFSET) & >> + (1 << M_CMD_START_BUSY_SHIFT)) >> + return true; >> + else >> + return false; >> +} > > Minor: return !!(readl(...))? You decide. > Okay will do that. Will also remove this function since now it becomes one line and is used only once. >> + >> +static int bcm_iproc_i2c_format_addr(struct bcm_iproc_i2c_dev *iproc_i2c, >> + struct i2c_msg *msg, u8 *addr) >> +{ >> + *addr = msg->addr << 1; >> + >> + if (msg->flags & I2C_M_RD) >> + *addr |= 1; >> + >> + return 0; >> +} > > I'd suggest a oneliner. > > *addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0) > > Or use !! like above. > > Don't do an extra function for that. It is only used once and it also > doesn't need to be int since it can't fail anyhow. > > (Note to self: I should make a macro for that in i2c.h) > Yes will change. Thanks. >> + /* need to reserve one byte in the FIFO for the slave address */ >> + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { >> + dev_err(iproc_i2c->device, >> + "only support data length up to %u bytes\n", >> + M_TX_RX_FIFO_SIZE - 1); >> + return -EINVAL; > > -EOPNOTSUPP > > Is it really a HW limitation? Could the driver later be extended to > continue filling the FIFO if a certain threshold is reached? > Will return -EOPNOTSUPP. This really depends on whether or not we expect one sequence of START + SLV ADDR + DATA + STOP per i2c message. I can later extend the driver to refill/re-drain the FIFO for data size >= 64 bytes, if one sequence of SATRT...STOP per message is not a requirement. >> + dev_dbg(iproc_i2c->device, "xfer %c, addr=0x%02x, len=%d\n", >> + (msg->flags & I2C_M_RD) ? 'R' : 'W', msg->addr, >> + msg->len); >> + dev_dbg(iproc_i2c->device, "*** data: %*ph\n", msg->len, msg->buf); > > Not really needed. We have tracing for that. > Will remove. >> + if (bus_speed < 100000) { >> + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", >> + bus_speed); >> + dev_err(iproc_i2c->device, >> + "valid speeds are 100khz and 400khz\n"); >> + return -EINVAL; >> + } else if (bus_speed < 400000) { >> + bus_speed = 100000; >> + speed_bit = 0; >> + } else { >> + bus_speed = 400000; >> + speed_bit = 1; >> + } >> + >> + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); >> + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); >> + val |= speed_bit << TIM_CFG_MODE_400_SHIFT; > > val |= (bus_speed == 400000) ... > > and skip speed_bit? You decide. > Okay, I'll get rid of speed_bit. >> +static void bcm_iproc_i2c_enable(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + u32 val; >> + >> + val = readl(iproc_i2c->base + CFG_OFFSET); >> + val |= 1 << CFG_EN_SHIFT; >> + writel(val, iproc_i2c->base + CFG_OFFSET); >> +} >> + >> +static void bcm_iproc_i2c_disable(struct bcm_iproc_i2c_dev *iproc_i2c) >> +{ >> + u32 val; >> + >> + val = readl(iproc_i2c->base + CFG_OFFSET); >> + val &= ~(1 << CFG_EN_SHIFT); >> + writel(val, iproc_i2c->base + CFG_OFFSET); >> +} > > Extra functions? They are self explaining and only used once. You > decide. In fact I'll keep the function, since it will likely be needed later when we add suspend/resume support to the driver. But I'll combine the two functions and make it a single function called bcm_iproc_i2c_enable_disable. > > Rest looks fine, thanks! > Thanks for the review! Ray -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-08 5:08 ` Ray Jui @ 2015-02-08 11:03 ` Wolfram Sang 2015-02-08 18:10 ` Ray Jui 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-02-08 11:03 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree [-- Attachment #1: Type: text/plain, Size: 662 bytes --] > > Is it really a HW limitation? Could the driver later be extended to > > continue filling the FIFO if a certain threshold is reached? > > > > Will return -EOPNOTSUPP. This really depends on whether or not we expect > one sequence of START + SLV ADDR + DATA + STOP per i2c message. I can > later extend the driver to refill/re-drain the FIFO for data size >= 64 > bytes, if one sequence of SATRT...STOP per message is not a requirement. It is important to have the terminology clear here: One transfer can consist of multiple messages. The transfer uses START/STOP at the beginning/end, the messages within the transfer only REPEATED_START. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-08 11:03 ` Wolfram Sang @ 2015-02-08 18:10 ` Ray Jui [not found] ` <54D7A694.4000903-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-08 18:10 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree On 2/8/2015 3:03 AM, Wolfram Sang wrote: > >>> Is it really a HW limitation? Could the driver later be extended to >>> continue filling the FIFO if a certain threshold is reached? >>> >> >> Will return -EOPNOTSUPP. This really depends on whether or not we expect >> one sequence of START + SLV ADDR + DATA + STOP per i2c message. I can >> later extend the driver to refill/re-drain the FIFO for data size >= 64 >> bytes, if one sequence of SATRT...STOP per message is not a requirement. > > It is important to have the terminology clear here: One transfer can > consist of multiple messages. The transfer uses START/STOP at the > beginning/end, the messages within the transfer only REPEATED_START. > Okay. Let me check with our ASIC engineer to see if there's a way to get the driver extended to support the case when data size is larger than the FIFO size. From my understanding based on the data sheet I have, I don't think that can be done with this controller. But if the ASIC engineers tells me the opposite, I'll add it as a separate patch later. Thanks, Ray ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54D7A694.4000903-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54D7A694.4000903-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-09 10:03 ` Wolfram Sang 0 siblings, 0 replies; 106+ messages in thread From: Wolfram Sang @ 2015-02-09 10:03 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 383 bytes --] > Okay. Let me check with our ASIC engineer to see if there's a way to get > the driver extended to support the case when data size is larger than > the FIFO size. From my understanding based on the data sheet I have, I > don't think that can be done with this controller. But if the ASIC > engineers tells me the opposite, I'll add it as a separate patch later. Perfect, thanks! [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v8 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2015-02-07 1:28 ` [PATCH v8 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-02-07 1:28 ` [PATCH v8 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2015-02-07 1:28 ` [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2015-02-07 1:28 ` Ray Jui 2 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-02-07 1:28 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> Reviewed-by: Kevin Cernekee <cernekee@chromium.org> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..ff5fb6a 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
* [PATCH v9 0/3] Add I2C support to Broadcom iProc [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> ` (7 preceding siblings ...) 2015-02-07 1:28 ` [PATCH v8 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-02-08 5:25 ` Ray Jui 2015-02-08 5:25 ` [PATCH v9 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui [not found] ` <1423373126-30024-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 8 siblings, 2 replies; 106+ messages in thread From: Ray Jui @ 2015-02-08 5:25 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui This patchset contains the initial I2C support for Broadcom iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100 kHz) and fast mode (400 kHz) Synced code base to Linux 3.19-rc7 Changes from v8: - Sort header includes in alphabetical order - Use correct error code - Get rid of redundant functions and combine functions to make driver more slim - Get rid of redundant debugging prints that are already available from the I2C framework - Other minor improvements Changes from v7: - Remove redundant 10-bit address check in the driver - Fix the driver that accidentally emits 1-byte of data with zero content in the case of SMBUS quick command - Improve debugging prints in the driver - Other minor improvements Changes from v6: - Get rid of unnecessary atomic variable usage in the driver - Improve the "waiting for transaction to complete" logic further by making sure there's no pending/ongoing interrupt by the time when flag 'xfer_is_done' is checked - After disabling interrupt with 'writel', add 'readl' to the same register to flush the bus to ensure the write has gone through Changes from v5: - Improve the "waiting for transaction to be complete" logic to take care of the corner case when an interrupt fires after wait_for_completion_timeout times out - Improve the logic to disable I2C interrupt in the remove function. Make it more generic so it works for both dedicated and shared interrupt Changes from v4: - Remove redundant header file includes - Change the logic that waits for the host controller to be idle to simply return -EBUSY - Use proper print level and error codes in the driver - Allow zero length message in the driver to support I2C_SMBUS_QUICK - Change back to use devm_request_irq. Disable interrupt in the remove function so there's no outstanding I2C interrupt when the driver is being removed from the framework - Other minor miscellaneous improvements and fixes Changes from v3: - Add config dependency to COMPILE_TEST to allow the driver to be build tested by other platforms - Improve CPU utilization efficiency in the loop of waiting for bus to idle - Add more comment in the driver to clarify the way how the "start busy" interrupt is triggered from the I2C controller - Fix inconsistent coding style and format - Improve the bus speed validation logic in the driver - Add code to free the interrupt line in driver's remove function. Also change to use non-devm API to request the interrupt line - Other miscellaneous improvements and fixes Changes from v2: - Have the I2C driver default to y so it does not need to be selected from ARCH_BCM_IPROC. This also helps to get rid of one patch. The driver still depends on ARCH_BCM_IPROC - Get rid of redundant check on resource returned by platform_get_resource Changes from v1: - Fix function argument parenthesis - Get rid of redundant driver owner field Ray Jui (3): i2c: iProc: define Broadcom iProc I2C binding i2c: iproc: Add Broadcom iProc I2C Driver ARM: dts: add I2C device nodes for Broadcom Cygnus .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++ arch/arm/boot/dts/bcm-cygnus.dtsi | 20 + drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 461 ++++++++++++++++++++ 5 files changed, 529 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v9 1/3] i2c: iProc: define Broadcom iProc I2C binding 2015-02-08 5:25 ` [PATCH v9 0/3] Add I2C support to Broadcom iProc Ray Jui @ 2015-02-08 5:25 ` Ray Jui [not found] ` <1423373126-30024-2-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> [not found] ` <1423373126-30024-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 1 sibling, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-08 5:25 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree, Ray Jui Document the I2C device tree binding for Broadcom iProc family of SoCs Signed-off-by: Ray Jui <rjui@broadcom.com> Reviewed-by: Scott Branden <sbranden@broadcom.com> Reviewed-by: Kevin Cernekee <cernekee@chromium.org> --- .../devicetree/bindings/i2c/brcm,iproc-i2c.txt | 37 ++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt diff --git a/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt new file mode 100644 index 0000000..81f982c --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/brcm,iproc-i2c.txt @@ -0,0 +1,37 @@ +Broadcom iProc I2C controller + +Required properties: + +- compatible: + Must be "brcm,iproc-i2c" + +- reg: + Define the base and range of the I/O address space that contain the iProc + I2C controller registers + +- interrupts: + Should contain the I2C interrupt + +- clock-frequency: + This is the I2C bus clock. Need to be either 100000 or 400000 + +- #address-cells: + Always 1 (for I2C addresses) + +- #size-cells: + Always 0 + +Example: + i2c0: i2c@18008000 { + compatible = "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + + codec: wm8750@1a { + compatible = "wlf,wm8750"; + reg = <0x1a>; + }; + }; -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1423373126-30024-2-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v9 1/3] i2c: iProc: define Broadcom iProc I2C binding [not found] ` <1423373126-30024-2-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-09 12:09 ` Wolfram Sang 0 siblings, 0 replies; 106+ messages in thread From: Wolfram Sang @ 2015-02-09 12:09 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 409 bytes --] On Sat, Feb 07, 2015 at 09:25:24PM -0800, Ray Jui wrote: > Document the I2C device tree binding for Broadcom iProc family of > SoCs > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> Applied to for-next, thanks! [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <1423373126-30024-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1423373126-30024-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-08 5:25 ` Ray Jui [not found] ` <1423373126-30024-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-08 5:25 ` [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 1 sibling, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-08 5:25 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Add initial support to the Broadcom iProc I2C controller found in the iProc family of SoCs. The iProc I2C controller has separate internal TX and RX FIFOs, each has a size of 64 bytes. The iProc I2C controller supports two bus speeds including standard mode (100kHz) and fast mode (400kHz) Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> --- drivers/i2c/busses/Kconfig | 10 + drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-bcm-iproc.c | 461 ++++++++++++++++++++++++++++++++++++ 3 files changed, 472 insertions(+) create mode 100644 drivers/i2c/busses/i2c-bcm-iproc.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index ab838d9..3d08731 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -372,6 +372,16 @@ config I2C_BCM2835 This support is also available as a module. If so, the module will be called i2c-bcm2835. +config I2C_BCM_IPROC + tristate "Broadcom iProc I2C controller" + depends on ARCH_BCM_IPROC || COMPILE_TEST + default ARCH_BCM_IPROC + help + If you say yes to this option, support will be included for the + Broadcom iProc I2C controller. + + If you don't know what to do here, say N. + config I2C_BCM_KONA tristate "BCM Kona I2C adapter" depends on ARCH_BCM_MOBILE diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 56388f6..d93b509 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_I2C_AT91) += i2c-at91.o obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_AXXIA) += i2c-axxia.o obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o +obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c new file mode 100644 index 0000000..d3c8915 --- /dev/null +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -0,0 +1,461 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#define CFG_OFFSET 0x00 +#define CFG_RESET_SHIFT 31 +#define CFG_EN_SHIFT 30 +#define CFG_M_RETRY_CNT_SHIFT 16 +#define CFG_M_RETRY_CNT_MASK 0x0f + +#define TIM_CFG_OFFSET 0x04 +#define TIM_CFG_MODE_400_SHIFT 31 + +#define M_FIFO_CTRL_OFFSET 0x0c +#define M_FIFO_RX_FLUSH_SHIFT 31 +#define M_FIFO_TX_FLUSH_SHIFT 30 +#define M_FIFO_RX_CNT_SHIFT 16 +#define M_FIFO_RX_CNT_MASK 0x7f +#define M_FIFO_RX_THLD_SHIFT 8 +#define M_FIFO_RX_THLD_MASK 0x3f + +#define M_CMD_OFFSET 0x30 +#define M_CMD_START_BUSY_SHIFT 31 +#define M_CMD_STATUS_SHIFT 25 +#define M_CMD_STATUS_MASK 0x07 +#define M_CMD_STATUS_SUCCESS 0x0 +#define M_CMD_STATUS_LOST_ARB 0x1 +#define M_CMD_STATUS_NACK_ADDR 0x2 +#define M_CMD_STATUS_NACK_DATA 0x3 +#define M_CMD_STATUS_TIMEOUT 0x4 +#define M_CMD_PROTOCOL_SHIFT 9 +#define M_CMD_PROTOCOL_MASK 0xf +#define M_CMD_PROTOCOL_BLK_WR 0x7 +#define M_CMD_PROTOCOL_BLK_RD 0x8 +#define M_CMD_PEC_SHIFT 8 +#define M_CMD_RD_CNT_SHIFT 0 +#define M_CMD_RD_CNT_MASK 0xff + +#define IE_OFFSET 0x38 +#define IE_M_RX_FIFO_FULL_SHIFT 31 +#define IE_M_RX_THLD_SHIFT 30 +#define IE_M_START_BUSY_SHIFT 28 + +#define IS_OFFSET 0x3c +#define IS_M_RX_FIFO_FULL_SHIFT 31 +#define IS_M_RX_THLD_SHIFT 30 +#define IS_M_START_BUSY_SHIFT 28 + +#define M_TX_OFFSET 0x40 +#define M_TX_WR_STATUS_SHIFT 31 +#define M_TX_DATA_SHIFT 0 +#define M_TX_DATA_MASK 0xff + +#define M_RX_OFFSET 0x44 +#define M_RX_STATUS_SHIFT 30 +#define M_RX_STATUS_MASK 0x03 +#define M_RX_PEC_ERR_SHIFT 29 +#define M_RX_DATA_SHIFT 0 +#define M_RX_DATA_MASK 0xff + +#define I2C_TIMEOUT_MESC 100 +#define M_TX_RX_FIFO_SIZE 64 + +enum bus_speed_index { + I2C_SPD_100K = 0, + I2C_SPD_400K, +}; + +struct bcm_iproc_i2c_dev { + struct device *device; + int irq; + + void __iomem *base; + + struct i2c_adapter adapter; + + struct completion done; + int xfer_is_done; +}; + +/* + * Can be expanded in the future if more interrupt status bits are utilized + */ +#define ISR_MASK (1 << IS_M_START_BUSY_SHIFT) + +static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = data; + u32 status = readl(iproc_i2c->base + IS_OFFSET); + + status &= ISR_MASK; + + if (!status) + return IRQ_NONE; + + writel(status, iproc_i2c->base + IS_OFFSET); + iproc_i2c->xfer_is_done = 1; + complete_all(&iproc_i2c->done); + + return IRQ_HANDLED; +} + +static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + u32 val; + + val = readl(iproc_i2c->base + M_CMD_OFFSET); + val = (val >> M_CMD_STATUS_SHIFT) & M_CMD_STATUS_MASK; + + switch (val) { + case M_CMD_STATUS_SUCCESS: + return 0; + + case M_CMD_STATUS_LOST_ARB: + dev_dbg(iproc_i2c->device, "lost bus arbitration\n"); + return -EAGAIN; + + case M_CMD_STATUS_NACK_ADDR: + dev_dbg(iproc_i2c->device, "NAK addr:0x%02x\n", msg->addr); + return -ENXIO; + + case M_CMD_STATUS_NACK_DATA: + dev_dbg(iproc_i2c->device, "NAK data\n"); + return -ENXIO; + + case M_CMD_STATUS_TIMEOUT: + dev_dbg(iproc_i2c->device, "bus timeout\n"); + return -ETIMEDOUT; + + default: + dev_dbg(iproc_i2c->device, "unknown error code=%d\n", val); + return -EIO; + } +} + +static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c, + struct i2c_msg *msg) +{ + int ret, i; + u8 addr; + u32 val; + unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MESC); + + /* need to reserve one byte in the FIFO for the slave address */ + if (msg->len > M_TX_RX_FIFO_SIZE - 1) { + dev_err(iproc_i2c->device, + "only support data length up to %u bytes\n", + M_TX_RX_FIFO_SIZE - 1); + return -EOPNOTSUPP; + } + + /* check if bus is busy */ + if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) & + BIT(M_CMD_START_BUSY_SHIFT))) { + dev_warn(iproc_i2c->device, "bus is busy\n"); + return -EBUSY; + } + + /* format and load slave address into the TX FIFO */ + addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0); + writel(addr, iproc_i2c->base + M_TX_OFFSET); + + /* for a write transaction, load data into the TX FIFO */ + if (!(msg->flags & I2C_M_RD)) { + for (i = 0; i < msg->len; i++) { + val = msg->buf[i]; + + /* mark the last byte */ + if (i == msg->len - 1) + val |= 1 << M_TX_WR_STATUS_SHIFT; + + writel(val, iproc_i2c->base + M_TX_OFFSET); + } + } + + /* mark as incomplete before starting the transaction */ + reinit_completion(&iproc_i2c->done); + iproc_i2c->xfer_is_done = 0; + + /* + * Enable the "start busy" interrupt, which will be triggered after the + * transaction is done, i.e., the internal start_busy bit, transitions + * from 1 to 0. + */ + writel(1 << IE_M_START_BUSY_SHIFT, iproc_i2c->base + IE_OFFSET); + + /* + * Now we can activate the transfer. For a read operation, specify the + * number of bytes to read + */ + val = 1 << M_CMD_START_BUSY_SHIFT; + if (msg->flags & I2C_M_RD) { + val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) | + (msg->len << M_CMD_RD_CNT_SHIFT); + } else { + val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); + } + writel(val, iproc_i2c->base + M_CMD_OFFSET); + + time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + /* read it back to flush the write */ + readl(iproc_i2c->base + IE_OFFSET); + + /* make sure the interrupt handler isn't running */ + synchronize_irq(iproc_i2c->irq); + + if (!time_left && !iproc_i2c->xfer_is_done) { + dev_err(iproc_i2c->device, "transaction timed out\n"); + + /* flush FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return -ETIMEDOUT; + } + + ret = bcm_iproc_i2c_check_status(iproc_i2c, msg); + if (ret) { + /* flush both TX/RX FIFOs */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | + (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + return ret; + } + + /* + * For a read operation, we now need to load the data from FIFO + * into the memory buffer + */ + if (msg->flags & I2C_M_RD) { + for (i = 0; i < msg->len; i++) { + msg->buf[i] = (readl(iproc_i2c->base + M_RX_OFFSET) >> + M_RX_DATA_SHIFT) & M_RX_DATA_MASK; + } + } + + return 0; +} + +static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter); + int ret, i; + + /* go through all messages */ + for (i = 0; i < num; i++) { + ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]); + if (ret) { + dev_dbg(iproc_i2c->device, "xfer failed\n"); + return ret; + } + } + + return num; +} + +static uint32_t bcm_iproc_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm bcm_iproc_algo = { + .master_xfer = bcm_iproc_i2c_xfer, + .functionality = bcm_iproc_i2c_functionality, +}; + +static int bcm_iproc_i2c_cfg_speed(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + unsigned int bus_speed; + u32 val; + int ret = of_property_read_u32(iproc_i2c->device->of_node, + "clock-frequency", &bus_speed); + if (ret < 0) { + dev_info(iproc_i2c->device, + "unable to interpret clock-frequency DT property\n"); + bus_speed = 100000; + } + + if (bus_speed < 100000) { + dev_err(iproc_i2c->device, "%d Hz bus speed not supported\n", + bus_speed); + dev_err(iproc_i2c->device, + "valid speeds are 100khz and 400khz\n"); + return -EINVAL; + } else if (bus_speed < 400000) { + bus_speed = 100000; + } else { + bus_speed = 400000; + } + + val = readl(iproc_i2c->base + TIM_CFG_OFFSET); + val &= ~(1 << TIM_CFG_MODE_400_SHIFT); + val |= (bus_speed == 400000) << TIM_CFG_MODE_400_SHIFT; + writel(val, iproc_i2c->base + TIM_CFG_OFFSET); + + dev_info(iproc_i2c->device, "bus set to %u Hz\n", bus_speed); + + return 0; +} + +static int bcm_iproc_i2c_init(struct bcm_iproc_i2c_dev *iproc_i2c) +{ + u32 val; + + /* put controller in reset */ + val = readl(iproc_i2c->base + CFG_OFFSET); + val |= 1 << CFG_RESET_SHIFT; + val &= ~(1 << CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* wait 100 usec per spec */ + udelay(100); + + /* bring controller out of reset */ + val &= ~(1 << CFG_RESET_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); + + /* flush TX/RX FIFOs and set RX FIFO threshold to zero */ + val = (1 << M_FIFO_RX_FLUSH_SHIFT) | (1 << M_FIFO_TX_FLUSH_SHIFT); + writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET); + + /* disable all interrupts */ + writel(0, iproc_i2c->base + IE_OFFSET); + + /* clear all pending interrupts */ + writel(0xffffffff, iproc_i2c->base + IS_OFFSET); + + return 0; +} + +static void bcm_iproc_i2c_enable_disable(struct bcm_iproc_i2c_dev *iproc_i2c, + bool enable) +{ + u32 val; + + val = readl(iproc_i2c->base + CFG_OFFSET); + if (enable) + val |= BIT(CFG_EN_SHIFT); + else + val &= ~BIT(CFG_EN_SHIFT); + writel(val, iproc_i2c->base + CFG_OFFSET); +} + +static int bcm_iproc_i2c_probe(struct platform_device *pdev) +{ + int irq, ret = 0; + struct bcm_iproc_i2c_dev *iproc_i2c; + struct i2c_adapter *adap; + struct resource *res; + + iproc_i2c = devm_kzalloc(&pdev->dev, sizeof(*iproc_i2c), + GFP_KERNEL); + if (!iproc_i2c) + return -ENOMEM; + + platform_set_drvdata(pdev, iproc_i2c); + iproc_i2c->device = &pdev->dev; + init_completion(&iproc_i2c->done); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iproc_i2c->base = devm_ioremap_resource(iproc_i2c->device, res); + if (IS_ERR(iproc_i2c->base)) + return PTR_ERR(iproc_i2c->base); + + ret = bcm_iproc_i2c_init(iproc_i2c); + if (ret) + return ret; + + ret = bcm_iproc_i2c_cfg_speed(iproc_i2c); + if (ret) + return ret; + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(iproc_i2c->device, "no irq resource\n"); + return irq; + } + iproc_i2c->irq = irq; + + ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0, + pdev->name, iproc_i2c); + if (ret < 0) { + dev_err(iproc_i2c->device, "unable to request irq %i\n", irq); + return ret; + } + + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + + adap = &iproc_i2c->adapter; + i2c_set_adapdata(adap, iproc_i2c); + strlcpy(adap->name, "Broadcom iProc I2C adapter", sizeof(adap->name)); + adap->algo = &bcm_iproc_algo; + adap->dev.parent = &pdev->dev; + adap->dev.of_node = pdev->dev.of_node; + + ret = i2c_add_adapter(adap); + if (ret) { + dev_err(iproc_i2c->device, "failed to add adapter\n"); + return ret; + } + + return 0; +} + +static int bcm_iproc_i2c_remove(struct platform_device *pdev) +{ + struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev); + + /* make sure there's no pending interrupt when we remove the adapter */ + writel(0, iproc_i2c->base + IE_OFFSET); + readl(iproc_i2c->base + IE_OFFSET); + synchronize_irq(iproc_i2c->irq); + + i2c_del_adapter(&iproc_i2c->adapter); + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + + return 0; +} + +static const struct of_device_id bcm_iproc_i2c_of_match[] = { + { .compatible = "brcm,iproc-i2c" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, bcm_iproc_i2c_of_match); + +static struct platform_driver bcm_iproc_i2c_driver = { + .driver = { + .name = "bcm-iproc-i2c", + .of_match_table = bcm_iproc_i2c_of_match, + }, + .probe = bcm_iproc_i2c_probe, + .remove = bcm_iproc_i2c_remove, +}; +module_platform_driver(bcm_iproc_i2c_driver); + +MODULE_AUTHOR("Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>"); +MODULE_DESCRIPTION("Broadcom iProc I2C Driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 106+ messages in thread
[parent not found: <1423373126-30024-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1423373126-30024-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-08 16:29 ` Wolfram Sang 2015-02-08 17:56 ` Ray Jui 2015-02-09 12:10 ` Wolfram Sang 1 sibling, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-02-08 16:29 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 718 bytes --] On Sat, Feb 07, 2015 at 09:25:25PM -0800, Ray Jui wrote: > Add initial support to the Broadcom iProc I2C controller found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports two bus speeds > including standard mode (100kHz) and fast mode (400kHz) > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> Looks good. What kind of tests have you done with exactly this version of the driver (not earlier ones)? [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-08 16:29 ` Wolfram Sang @ 2015-02-08 17:56 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-02-08 17:56 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 2/8/2015 8:29 AM, Wolfram Sang wrote: > On Sat, Feb 07, 2015 at 09:25:25PM -0800, Ray Jui wrote: >> Add initial support to the Broadcom iProc I2C controller found in the >> iProc family of SoCs. >> >> The iProc I2C controller has separate internal TX and RX FIFOs, each has >> a size of 64 bytes. The iProc I2C controller supports two bus speeds >> including standard mode (100kHz) and fast mode (400kHz) >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> > > Looks good. What kind of tests have you done with exactly this version of the > driver (not earlier ones)? > I did build test and ran i2cdetect on Cygnus BCM958300K combo board with the 3.19 rc7 kernel, and then I back ported this driver to our 3.10 version of the production kernel (where we have complete audio driver support), to run some audio playback tests, which involves using the wolfson codec (through i2c). The only line I added when back porting to the 3.10 kernel is of_i2c_register_devices at the end of probe. Thanks, Ray ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <1423373126-30024-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-08 16:29 ` Wolfram Sang @ 2015-02-09 12:10 ` Wolfram Sang 2015-02-10 5:23 ` Ray Jui 1 sibling, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-02-09 12:10 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 735 bytes --] On Sat, Feb 07, 2015 at 09:25:25PM -0800, Ray Jui wrote: > Add initial support to the Broadcom iProc I2C controller found in the > iProc family of SoCs. > > The iProc I2C controller has separate internal TX and RX FIFOs, each has > a size of 64 bytes. The iProc I2C controller supports two bus speeds > including standard mode (100kHz) and fast mode (400kHz) > > Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> > Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> Applied to for-next, thanks! Next time, please send new patches as seperate threads, not as a reply to the old series. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-09 12:10 ` Wolfram Sang @ 2015-02-10 5:23 ` Ray Jui [not found] ` <54D995DA.7040201-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-10 5:23 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 2/9/2015 4:10 AM, Wolfram Sang wrote: > On Sat, Feb 07, 2015 at 09:25:25PM -0800, Ray Jui wrote: >> Add initial support to the Broadcom iProc I2C controller found in the >> iProc family of SoCs. >> >> The iProc I2C controller has separate internal TX and RX FIFOs, each has >> a size of 64 bytes. The iProc I2C controller supports two bus speeds >> including standard mode (100kHz) and fast mode (400kHz) >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> > > Applied to for-next, thanks! > > Next time, please send new patches as seperate threads, not as a reply > to the old series. > Thanks, Wolfram. I think I'm missing something here and would like to get it clarified, so I don't make the same mistake again in the future: I thought I've already sent these patches as a new thread, i.e., [PATCH v9 ...], isn't it? Thanks, Ray ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54D995DA.7040201-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver [not found] ` <54D995DA.7040201-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-10 8:33 ` Wolfram Sang 2015-02-10 17:10 ` Ray Jui 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-02-10 8:33 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA [-- Attachment #1: Type: text/plain, Size: 406 bytes --] > Thanks, Wolfram. I think I'm missing something here and would like to > get it clarified, so I don't make the same mistake again in the future: > I thought I've already sent these patches as a new thread, i.e., [PATCH > v9 ...], isn't it? Yes, but "In-Reply-To" was set and so my MUAs (mutt and Thunderbird) threaded them to your first submission. Most people I know prefer to have this not threaded. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver 2015-02-10 8:33 ` Wolfram Sang @ 2015-02-10 17:10 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-02-10 17:10 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 2/10/2015 12:33 AM, Wolfram Sang wrote: > >> Thanks, Wolfram. I think I'm missing something here and would like to >> get it clarified, so I don't make the same mistake again in the future: >> I thought I've already sent these patches as a new thread, i.e., [PATCH >> v9 ...], isn't it? > > Yes, but "In-Reply-To" was set and so my MUAs (mutt and Thunderbird) > threaded them to your first submission. Most people I know prefer to > have this not threaded. > Okay I got it now. Thanks. Ray ^ permalink raw reply [flat|nested] 106+ messages in thread
* [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <1423373126-30024-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-08 5:25 ` [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui @ 2015-02-08 5:25 ` Ray Jui 2015-02-09 12:11 ` Wolfram Sang 1 sibling, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-08 5:25 UTC (permalink / raw) To: Wolfram Sang, Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King Cc: Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA, Ray Jui Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep them disabled there. Individual I2C devices can be enabled in board specific dts file when I2C slave devices are enabled in the future Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> --- arch/arm/boot/dts/bcm-cygnus.dtsi | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/arch/arm/boot/dts/bcm-cygnus.dtsi b/arch/arm/boot/dts/bcm-cygnus.dtsi index 5126f9e..ff5fb6a 100644 --- a/arch/arm/boot/dts/bcm-cygnus.dtsi +++ b/arch/arm/boot/dts/bcm-cygnus.dtsi @@ -70,6 +70,26 @@ }; }; + i2c0: i2c@18008000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x18008000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 85 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + + i2c1: i2c@1800b000 { + compatible = "brcm,cygnus-iproc-i2c", "brcm,iproc-i2c"; + reg = <0x1800b000 0x100>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 86 IRQ_TYPE_NONE>; + clock-frequency = <100000>; + status = "disabled"; + }; + uart0: serial@18020000 { compatible = "snps,dw-apb-uart"; reg = <0x18020000 0x100>; -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply related [flat|nested] 106+ messages in thread
* Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2015-02-08 5:25 ` [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui @ 2015-02-09 12:11 ` Wolfram Sang 2015-02-10 5:24 ` Ray Jui 0 siblings, 1 reply; 106+ messages in thread From: Wolfram Sang @ 2015-02-09 12:11 UTC (permalink / raw) To: Ray Jui Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c, linux-kernel, linux-arm-kernel, bcm-kernel-feedback-list, devicetree [-- Attachment #1: Type: text/plain, Size: 552 bytes --] On Sat, Feb 07, 2015 at 09:25:26PM -0800, Ray Jui wrote: > Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep > them disabled there. Individual I2C devices can be enabled in board > specific dts file when I2C slave devices are enabled in the future > > Signed-off-by: Ray Jui <rjui@broadcom.com> > Reviewed-by: Scott Branden <sbranden@broadcom.com> > Reviewed-by: Kevin Cernekee <cernekee@chromium.org> I usually don't take DTS patches. They should go via arm-soc. Please say so if there are reasons I should take them. [-- Attachment #2: Digital signature --] [-- Type: application/pgp-signature, Size: 819 bytes --] ^ permalink raw reply [flat|nested] 106+ messages in thread
* Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus 2015-02-09 12:11 ` Wolfram Sang @ 2015-02-10 5:24 ` Ray Jui [not found] ` <54D99606.9070309-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Ray Jui @ 2015-02-10 5:24 UTC (permalink / raw) To: Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Florian Fainelli, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 2/9/2015 4:11 AM, Wolfram Sang wrote: > On Sat, Feb 07, 2015 at 09:25:26PM -0800, Ray Jui wrote: >> Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep >> them disabled there. Individual I2C devices can be enabled in board >> specific dts file when I2C slave devices are enabled in the future >> >> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> > > I usually don't take DTS patches. They should go via arm-soc. Please say > so if there are reasons I should take them. > Okay. I'll send this as an individual patch to arm-soc. Thanks, Ray -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54D99606.9070309-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <54D99606.9070309-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> @ 2015-02-10 5:34 ` Florian Fainelli [not found] ` <54D99848.2080205-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 0 siblings, 1 reply; 106+ messages in thread From: Florian Fainelli @ 2015-02-10 5:34 UTC (permalink / raw) To: Ray Jui, Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA Le 09/02/2015 21:24, Ray Jui a écrit : > > > On 2/9/2015 4:11 AM, Wolfram Sang wrote: >> On Sat, Feb 07, 2015 at 09:25:26PM -0800, Ray Jui wrote: >>> Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep >>> them disabled there. Individual I2C devices can be enabled in board >>> specific dts file when I2C slave devices are enabled in the future >>> >>> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> >> >> I usually don't take DTS patches. They should go via arm-soc. Please say >> so if there are reasons I should take them. >> > Okay. I'll send this as an individual patch to arm-soc. Could you put in in a branch somewhere on github.comù/cygnus-linus.git so it's easy for me to pull from there? -- Florian -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 106+ messages in thread
[parent not found: <54D99848.2080205-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>]
* Re: [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus [not found] ` <54D99848.2080205-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> @ 2015-02-10 5:36 ` Ray Jui 0 siblings, 0 replies; 106+ messages in thread From: Ray Jui @ 2015-02-10 5:36 UTC (permalink / raw) To: Florian Fainelli, Wolfram Sang Cc: Uwe Kleine-König, Arend van Spriel, Kevin Cernekee, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala, Grant Likely, Christian Daudt, Matt Porter, Russell King, Scott Branden, Dmitry Torokhov, linux-i2c-u79uwXL29TY76Z2rM5mHXA, linux-kernel-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, devicetree-u79uwXL29TY76Z2rM5mHXA On 2/9/2015 9:34 PM, Florian Fainelli wrote: > Le 09/02/2015 21:24, Ray Jui a écrit : >> >> >> On 2/9/2015 4:11 AM, Wolfram Sang wrote: >>> On Sat, Feb 07, 2015 at 09:25:26PM -0800, Ray Jui wrote: >>>> Add I2C device nodes and its properties in bcm-cygnus.dtsi but keep >>>> them disabled there. Individual I2C devices can be enabled in board >>>> specific dts file when I2C slave devices are enabled in the future >>>> >>>> Signed-off-by: Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>>> Reviewed-by: Scott Branden <sbranden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> >>>> Reviewed-by: Kevin Cernekee <cernekee-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> >>> >>> I usually don't take DTS patches. They should go via arm-soc. Please say >>> so if there are reasons I should take them. >>> >> Okay. I'll send this as an individual patch to arm-soc. > > Could you put in in a branch somewhere on github.comù/cygnus-linus.git > so it's easy for me to pull from there? > -- > Florian > Hi Florian, It's in branch cygnus-i2c-v9. Thanks! Ray ^ permalink raw reply [flat|nested] 106+ messages in thread
end of thread, other threads:[~2015-02-10 17:10 UTC | newest] Thread overview: 106+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <Ray Jui <rjui@broadcom.com> [not found] ` <Ray Jui <rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 0:54 ` [PATCH 0/4] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 0:54 ` [PATCH 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2014-12-10 1:27 ` Varka Bhadram [not found] ` <5487A16C.5000707-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-12-10 1:35 ` Ray Jui [not found] ` <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg@mail.gmail.com> [not found] ` <CAEUmHyb_wu1kVj-G5LQj8Q_A1Tid1gSGbU=cGeBf4EXZgXChbg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2014-12-10 3:27 ` Ray Jui 2014-12-10 0:54 ` [PATCH 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui 2014-12-10 1:33 ` Varka Bhadram [not found] ` <5487A2D6.8070901-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-12-10 1:41 ` Ray Jui [not found] ` <CAEUmHyZ+VqjzL4LkQozGJtnictPNXHYWM2qMKvD=LmfQdcT8iQ@mail.gmail.com> [not found] ` <CAEUmHyZ86r=7KzJzfE9_upv45vN7geW9woqMkaGaBPwfp3xbMQ@mail.gmail.com> [not found] ` <CAEUmHyZ86r=7KzJzfE9_upv45vN7geW9woqMkaGaBPwfp3xbMQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2014-12-10 3:31 ` Ray Jui 2014-12-10 0:54 ` [PATCH 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui 2014-12-10 0:54 ` [PATCH 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2014-12-10 2:18 ` [PATCH v2 0/4] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 2:18 ` [PATCH v2 1/4] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2014-12-10 2:18 ` [PATCH v2 2/4] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1418177893-22094-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 2:18 ` [PATCH v2 3/4] ARM: mach-bcm: Enable I2C support for iProc Ray Jui [not found] ` <1418177893-22094-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 2:20 ` Florian Fainelli [not found] ` <5487ADE5.4070705-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-12-10 2:24 ` Ray Jui [not found] ` <5487AEF0.5010404-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-10 3:20 ` Florian Fainelli [not found] ` <5487BBE0.4000701-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2014-12-10 3:58 ` Ray Jui 2014-12-10 2:18 ` [PATCH v2 4/4] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2014-12-10 3:57 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Ray Jui 2014-12-10 3:57 ` [PATCH v3 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2014-12-10 3:57 ` [PATCH v3 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1418183832-24793-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-13 22:50 ` Uwe Kleine-König [not found] ` <20150113225012.GK22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-14 2:14 ` Ray Jui 2015-01-14 7:51 ` Uwe Kleine-König 2015-01-14 20:05 ` Ray Jui [not found] ` <54B5D0F9.8030902-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-15 11:59 ` Wolfram Sang 2015-01-16 22:51 ` Ray Jui 2014-12-10 3:57 ` [PATCH v3 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui [not found] ` <548F577E.7020207@broadcom.com> [not found] ` <548F577E.7020207-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2014-12-15 21:55 ` [PATCH v3 0/3] Add I2C support to Broadcom iProc Wolfram Sang 2014-12-16 0:12 ` Ray Jui 2015-01-14 22:23 ` [PATCH v4 " Ray Jui 2015-01-14 22:23 ` [PATCH v4 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui [not found] ` <1421274213-3544-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-14 22:23 ` [PATCH v4 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1421274213-3544-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-15 8:41 ` Uwe Kleine-König [not found] ` <20150115084119.GN22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-15 12:07 ` Wolfram Sang 2015-01-15 16:32 ` Uwe Kleine-König 2015-01-16 22:52 ` Ray Jui 2015-01-16 22:09 ` Ray Jui [not found] ` <54B98C18.4080807-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-17 16:01 ` Uwe Kleine-König [not found] ` <20150117160113.GA22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-17 19:58 ` Ray Jui [not found] ` <54BABEE9.8070801-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-17 20:18 ` Uwe Kleine-König [not found] ` <20150117201849.GC22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-17 20:51 ` Ray Jui [not found] ` <54BACB66.6040909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-17 21:10 ` Uwe Kleine-König [not found] ` <20150117211017.GD22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-17 21:26 ` Ray Jui [not found] ` <54BAD391.9080909-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-17 22:40 ` Russell King - ARM Linux [not found] ` <20150117224021.GA26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> 2015-01-18 0:30 ` Ray Jui 2015-01-19 19:28 ` Russell King - ARM Linux [not found] ` <20150119192805.GF26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> 2015-01-19 21:25 ` Ray Jui 2015-01-14 22:23 ` [PATCH v4 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui [not found] ` <1421274213-3544-4-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-15 8:44 ` Uwe Kleine-König [not found] ` <20150115084456.GO22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-16 19:24 ` Ray Jui [not found] ` <54B96559.1010007-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-16 19:48 ` Uwe Kleine-König [not found] ` <20150116194831.GV22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-16 23:18 ` Ray Jui 2015-01-16 23:42 ` [PATCH v5 0/3] Add I2C support to Broadcom iProc Ray Jui [not found] ` <1421451737-7107-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-16 23:42 ` [PATCH v5 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2015-01-16 23:42 ` [PATCH v5 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1421451737-7107-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-18 9:14 ` Arend van Spriel [not found] ` <54BB795C.6040402-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-18 9:47 ` Uwe Kleine-König [not found] ` <20150118094741.GE22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-18 11:06 ` Wolfram Sang 2015-01-18 11:17 ` Uwe Kleine-König [not found] ` <20150118111759.GG22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-18 11:42 ` Wolfram Sang 2015-01-18 11:46 ` Arend van Spriel [not found] ` <54BB9D2B.20408-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-18 11:56 ` Uwe Kleine-König [not found] ` <20150118115650.GH22880-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org> 2015-01-18 12:13 ` Arend van Spriel [not found] ` <54BBA36A.10608-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-19 19:15 ` Ray Jui 2015-01-16 23:42 ` [PATCH v5 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2015-01-19 19:23 ` [PATCH v6 0/3] Add I2C support to Broadcom iProc Ray Jui [not found] ` <1421695428-19102-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-19 19:23 ` [PATCH v6 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2015-01-19 19:23 ` [PATCH v6 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2015-01-19 19:23 ` [PATCH v6 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1421695428-19102-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-01-19 19:44 ` Russell King - ARM Linux [not found] ` <20150119194420.GG26493-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org> 2015-01-19 21:31 ` Ray Jui 2015-01-19 21:51 ` [PATCH v7 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-01-19 21:51 ` [PATCH v7 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2015-01-19 21:51 ` [PATCH v7 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui 2015-02-06 22:31 ` [v7,2/3] " Kevin Cernekee [not found] ` <20150206223149.GB345-hpIqsD4AKlfQT0dZR+AlfA@public.gmane.org> 2015-02-06 22:48 ` Dmitry Torokhov [not found] ` <CAE_wzQ-POweLLmTyHoMvs_NESjW5UmPxh2ZQCaW4-W74MsrHag-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org> 2015-02-06 23:01 ` Kevin Cernekee 2015-02-07 0:54 ` Ray Jui 2015-01-19 21:51 ` [PATCH v7 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2015-02-07 1:28 ` [PATCH v8 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-02-07 1:28 ` [PATCH v8 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui 2015-02-07 1:28 ` [PATCH v8 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1423272507-18459-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-07 17:50 ` Wolfram Sang 2015-02-08 5:08 ` Ray Jui 2015-02-08 11:03 ` Wolfram Sang 2015-02-08 18:10 ` Ray Jui [not found] ` <54D7A694.4000903-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-09 10:03 ` Wolfram Sang 2015-02-07 1:28 ` [PATCH v8 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2015-02-08 5:25 ` [PATCH v9 0/3] Add I2C support to Broadcom iProc Ray Jui 2015-02-08 5:25 ` [PATCH v9 1/3] i2c: iProc: define Broadcom iProc I2C binding Ray Jui [not found] ` <1423373126-30024-2-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-09 12:09 ` Wolfram Sang [not found] ` <1423373126-30024-1-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-08 5:25 ` [PATCH v9 2/3] i2c: iproc: Add Broadcom iProc I2C Driver Ray Jui [not found] ` <1423373126-30024-3-git-send-email-rjui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-08 16:29 ` Wolfram Sang 2015-02-08 17:56 ` Ray Jui 2015-02-09 12:10 ` Wolfram Sang 2015-02-10 5:23 ` Ray Jui [not found] ` <54D995DA.7040201-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-10 8:33 ` Wolfram Sang 2015-02-10 17:10 ` Ray Jui 2015-02-08 5:25 ` [PATCH v9 3/3] ARM: dts: add I2C device nodes for Broadcom Cygnus Ray Jui 2015-02-09 12:11 ` Wolfram Sang 2015-02-10 5:24 ` Ray Jui [not found] ` <54D99606.9070309-dY08KVG/lbpWk0Htik3J/w@public.gmane.org> 2015-02-10 5:34 ` Florian Fainelli [not found] ` <54D99848.2080205-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> 2015-02-10 5:36 ` Ray Jui
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).