* [PATCH V3] i2c: add bcm2835 driver
@ 2013-02-09 3:52 Stephen Warren
[not found] ` <1360381978-26092-1-git-send-email-swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
0 siblings, 1 reply; 4+ messages in thread
From: Stephen Warren @ 2013-02-09 3:52 UTC (permalink / raw)
To: Wolfram Sang, Ben Dooks
Cc: Grant Likely, Rob Herring, linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Stephen Warren
This implements a very basic I2C host driver for the BCM2835 SoC. Missing
features so far are:
* 10-bit addressing.
* DMA.
Reviewed-by: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
Signed-off-by: Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
---
v3:
* Return i2c_add_numbered_adapter() from probe() directly.
v2:
* Implemented clock divider configuration based on desired bus rate.
* Make use of module_platform_driver().
* Removed use of devinit.
---
.../devicetree/bindings/i2c/brcm,bcm2835-i2c.txt | 20 ++
drivers/i2c/busses/Kconfig | 12 +
drivers/i2c/busses/Makefile | 1 +
drivers/i2c/busses/i2c-bcm2835.c | 340 ++++++++++++++++++++
4 files changed, 373 insertions(+)
create mode 100644 Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
create mode 100644 drivers/i2c/busses/i2c-bcm2835.c
diff --git a/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
new file mode 100644
index 0000000..e9de375
--- /dev/null
+++ b/Documentation/devicetree/bindings/i2c/brcm,bcm2835-i2c.txt
@@ -0,0 +1,20 @@
+Broadcom BCM2835 I2C controller
+
+Required properties:
+- compatible : Should be "brcm,bcm2835-i2c".
+- reg: Should contain register location and length.
+- interrupts: Should contain interrupt.
+- clocks : The clock feeding the I2C controller.
+
+Recommended properties:
+- clock-frequency : desired I2C bus clock frequency in Hz.
+
+Example:
+
+i2c@20205000 {
+ compatible = "brcm,bcm2835-i2c";
+ reg = <0x7e205000 0x1000>;
+ interrupts = <2 21>;
+ clocks = <&clk_i2c>;
+ clock-frequency = <100000>;
+};
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 8bb810e..3cc40ab 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -319,6 +319,18 @@ config I2C_AU1550
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
+config I2C_BCM2835
+ tristate "Broadcom BCM2835 I2C controller"
+ depends on ARCH_BCM2835
+ help
+ If you say yes to this option, support will be included for the
+ BCM2835 I2C controller.
+
+ If you don't know what to do here, say N.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-bcm2835.
+
config I2C_BLACKFIN_TWI
tristate "Blackfin TWI I2C support"
depends on BLACKFIN
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index 6181f3f..a52a891 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
# Embedded system I2C/SMBus host controller drivers
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
+obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o
obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
diff --git a/drivers/i2c/busses/i2c-bcm2835.c b/drivers/i2c/busses/i2c-bcm2835.c
new file mode 100644
index 0000000..1a399c3
--- /dev/null
+++ b/drivers/i2c/busses/i2c-bcm2835.c
@@ -0,0 +1,340 @@
+/*
+ * BCM2835 master mode driver
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define BCM2835_I2C_C 0x0
+#define BCM2835_I2C_S 0x4
+#define BCM2835_I2C_DLEN 0x8
+#define BCM2835_I2C_A 0xc
+#define BCM2835_I2C_FIFO 0x10
+#define BCM2835_I2C_DIV 0x14
+#define BCM2835_I2C_DEL 0x18
+#define BCM2835_I2C_CLKT 0x1c
+
+#define BCM2835_I2C_C_READ BIT(0)
+#define BCM2835_I2C_C_CLEAR BIT(4) /* bits 4 and 5 both clear */
+#define BCM2835_I2C_C_ST BIT(7)
+#define BCM2835_I2C_C_INTD BIT(8)
+#define BCM2835_I2C_C_INTT BIT(9)
+#define BCM2835_I2C_C_INTR BIT(10)
+#define BCM2835_I2C_C_I2CEN BIT(15)
+
+#define BCM2835_I2C_S_TA BIT(0)
+#define BCM2835_I2C_S_DONE BIT(1)
+#define BCM2835_I2C_S_TXW BIT(2)
+#define BCM2835_I2C_S_RXR BIT(3)
+#define BCM2835_I2C_S_TXD BIT(4)
+#define BCM2835_I2C_S_RXD BIT(5)
+#define BCM2835_I2C_S_TXE BIT(6)
+#define BCM2835_I2C_S_RXF BIT(7)
+#define BCM2835_I2C_S_ERR BIT(8)
+#define BCM2835_I2C_S_CLKT BIT(9)
+#define BCM2835_I2C_S_LEN BIT(10) /* Fake bit for SW error reporting */
+
+#define BCM2835_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+struct bcm2835_i2c_dev {
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *clk;
+ struct i2c_adapter adapter;
+ struct completion completion;
+ u32 msg_err;
+ u8 *msg_buf;
+ size_t msg_buf_remaining;
+};
+
+static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
+ u32 reg, u32 val)
+{
+ writel(val, i2c_dev->regs + reg);
+}
+
+static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
+{
+ return readl(i2c_dev->regs + reg);
+}
+
+static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
+{
+ u32 val;
+
+ for (;;) {
+ if (!i2c_dev->msg_buf_remaining)
+ return;
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ if (!(val & BCM2835_I2C_S_TXD))
+ break;
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO,
+ *i2c_dev->msg_buf);
+ i2c_dev->msg_buf++;
+ i2c_dev->msg_buf_remaining--;
+ }
+}
+
+static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
+{
+ u32 val;
+
+ for (;;) {
+ if (!i2c_dev->msg_buf_remaining)
+ return;
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ if (!(val & BCM2835_I2C_S_RXD))
+ break;
+ *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev,
+ BCM2835_I2C_FIFO);
+ i2c_dev->msg_buf++;
+ i2c_dev->msg_buf_remaining--;
+ }
+}
+
+static irqreturn_t bcm2835_i2c_isr(int this_irq, void *data)
+{
+ struct bcm2835_i2c_dev *i2c_dev = data;
+ u32 val, err;
+
+ val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_S, val);
+
+ err = val & (BCM2835_I2C_S_CLKT | BCM2835_I2C_S_ERR);
+ if (err) {
+ i2c_dev->msg_err = err;
+ complete(&i2c_dev->completion);
+ return IRQ_HANDLED;
+ }
+
+ if (val & BCM2835_I2C_S_RXD) {
+ bcm2835_drain_rxfifo(i2c_dev);
+ if (!(val & BCM2835_I2C_S_DONE))
+ return IRQ_HANDLED;
+ }
+
+ if (val & BCM2835_I2C_S_DONE) {
+ if (i2c_dev->msg_buf_remaining)
+ i2c_dev->msg_err = BCM2835_I2C_S_LEN;
+ else
+ i2c_dev->msg_err = 0;
+ complete(&i2c_dev->completion);
+ return IRQ_HANDLED;
+ }
+
+ if (val & BCM2835_I2C_S_TXD) {
+ bcm2835_fill_txfifo(i2c_dev);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int bcm2835_i2c_xfer_msg(struct bcm2835_i2c_dev *i2c_dev,
+ struct i2c_msg *msg)
+{
+ u32 c;
+ int ret;
+
+ i2c_dev->msg_buf = msg->buf;
+ i2c_dev->msg_buf_remaining = msg->len;
+ INIT_COMPLETION(i2c_dev->completion);
+
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+
+ if (msg->flags & I2C_M_RD) {
+ c = BCM2835_I2C_C_READ | BCM2835_I2C_C_INTR;
+ } else {
+ c = BCM2835_I2C_C_INTT;
+ bcm2835_fill_txfifo(i2c_dev);
+ }
+ c |= BCM2835_I2C_C_ST | BCM2835_I2C_C_INTD | BCM2835_I2C_C_I2CEN;
+
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_A, msg->addr);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DLEN, msg->len);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, c);
+
+ ret = wait_for_completion_timeout(&i2c_dev->completion,
+ BCM2835_I2C_TIMEOUT);
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
+ if (WARN_ON(ret == 0)) {
+ dev_err(i2c_dev->dev, "i2c transfer timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ if (likely(!i2c_dev->msg_err))
+ return 0;
+
+ if ((i2c_dev->msg_err & BCM2835_I2C_S_ERR) &&
+ (msg->flags & I2C_M_IGNORE_NAK))
+ return 0;
+
+ dev_err(i2c_dev->dev, "i2c transfer failed: %x\n", i2c_dev->msg_err);
+
+ if (i2c_dev->msg_err & BCM2835_I2C_S_ERR)
+ return -EREMOTEIO;
+ else
+ return -EIO;
+}
+
+static int bcm2835_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct bcm2835_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < num; i++) {
+ ret = bcm2835_i2c_xfer_msg(i2c_dev, &msgs[i]);
+ if (ret)
+ break;
+ }
+
+ return ret ?: i;
+}
+
+static u32 bcm2835_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm bcm2835_i2c_algo = {
+ .master_xfer = bcm2835_i2c_xfer,
+ .functionality = bcm2835_i2c_func,
+};
+
+static int bcm2835_i2c_probe(struct platform_device *pdev)
+{
+ struct bcm2835_i2c_dev *i2c_dev;
+ struct resource *mem, *requested, *irq;
+ u32 bus_clk_rate, divider;
+ int ret;
+ struct i2c_adapter *adap;
+
+ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
+ if (!i2c_dev) {
+ dev_err(&pdev->dev, "Cannot allocate i2c_dev\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, i2c_dev);
+ i2c_dev->dev = &pdev->dev;
+ init_completion(&i2c_dev->completion);
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No mem resource\n");
+ return -ENODEV;
+ }
+
+ requested = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem),
+ dev_name(&pdev->dev));
+ if (!requested) {
+ dev_err(&pdev->dev, "Could not claim register region\n");
+ return -EBUSY;
+ }
+
+ i2c_dev->regs = devm_ioremap(&pdev->dev, mem->start,
+ resource_size(mem));
+ if (!i2c_dev->regs) {
+ dev_err(&pdev->dev, "Could not map registers\n");
+ return -ENOMEM;
+ }
+
+ i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2c_dev->clk)) {
+ dev_err(&pdev->dev, "Could not get clock\n");
+ return PTR_ERR(i2c_dev->clk);
+ }
+
+ ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency",
+ &bus_clk_rate);
+ if (ret < 0) {
+ dev_warn(&pdev->dev,
+ "Could not read clock-frequency property\n");
+ bus_clk_rate = 100000;
+ }
+
+ divider = DIV_ROUND_UP(clk_get_rate(i2c_dev->clk), bus_clk_rate);
+ /*
+ * Per the datasheet, the register is always interpreted as an even
+ * number, by rounding down. In other words, the LSB is ignored. So,
+ * if the LSB is set, increment the divider to avoid any issue.
+ */
+ if (divider & 1)
+ divider++;
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_DIV, divider);
+
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return -ENODEV;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq->start, bcm2835_i2c_isr,
+ IRQF_SHARED, dev_name(&pdev->dev), i2c_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return -ENODEV;
+ }
+
+ adap = &i2c_dev->adapter;
+ i2c_set_adapdata(adap, i2c_dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
+ adap->algo = &bcm2835_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+ adap->nr = -1;
+
+ bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
+
+ return i2c_add_numbered_adapter(adap);
+}
+
+static int bcm2835_i2c_remove(struct platform_device *pdev)
+{
+ struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
+
+ i2c_del_adapter(&i2c_dev->adapter);
+
+ return 0;
+}
+
+static const struct of_device_id bcm2835_i2c_of_match[] = {
+ { .compatible = "brcm,bcm2835-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bcm2835_i2c_of_match);
+
+static struct platform_driver bcm2835_i2c_driver = {
+ .probe = bcm2835_i2c_probe,
+ .remove = bcm2835_i2c_remove,
+ .driver = {
+ .name = "i2c-bcm2835",
+ .owner = THIS_MODULE,
+ .of_match_table = bcm2835_i2c_of_match,
+ },
+};
+module_platform_driver(bcm2835_i2c_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>");
+MODULE_DESCRIPTION("BCM2835 I2C bus adapter");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:i2c-bcm2835");
--
1.7.10.4
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH V3] i2c: add bcm2835 driver
[not found] ` <1360381978-26092-1-git-send-email-swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
@ 2013-02-11 22:52 ` Wolfram Sang
[not found] ` <20130211225208.GA9893-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org>
0 siblings, 1 reply; 4+ messages in thread
From: Wolfram Sang @ 2013-02-11 22:52 UTC (permalink / raw)
To: Stephen Warren
Cc: Wolfram Sang, Ben Dooks, Grant Likely, Rob Herring,
linux-i2c-u79uwXL29TY76Z2rM5mHXA,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ
Hi Stephen,
On Fri, Feb 08, 2013 at 08:52:58PM -0700, Stephen Warren wrote:
> This implements a very basic I2C host driver for the BCM2835 SoC. Missing
> features so far are:
>
> * 10-bit addressing.
> * DMA.
>
> Reviewed-by: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
> Signed-off-by: Stephen Warren <swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
> ---
...
> +struct bcm2835_i2c_dev {
> + struct device *dev;
> + void __iomem *regs;
> + struct clk *clk;
> + struct i2c_adapter adapter;
> + struct completion completion;
> + u32 msg_err;
> + u8 *msg_buf;
> + size_t msg_buf_remaining;
> +};
> +
> +static inline void bcm2835_i2c_writel(struct bcm2835_i2c_dev *i2c_dev,
> + u32 reg, u32 val)
> +{
> + writel(val, i2c_dev->regs + reg);
> +}
> +
> +static inline u32 bcm2835_i2c_readl(struct bcm2835_i2c_dev *i2c_dev, u32 reg)
> +{
> + return readl(i2c_dev->regs + reg);
> +}
Hmm, not sure if those functions add readability, but no need to change.
> +
> +static void bcm2835_fill_txfifo(struct bcm2835_i2c_dev *i2c_dev)
> +{
> + u32 val;
> +
> + for (;;) {
> + if (!i2c_dev->msg_buf_remaining)
> + return;
> + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
> + if (!(val & BCM2835_I2C_S_TXD))
> + break;
> + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_FIFO,
> + *i2c_dev->msg_buf);
> + i2c_dev->msg_buf++;
> + i2c_dev->msg_buf_remaining--;
> + }
while (i2c_dev->msg_buf_remaining) ...
?
> +}
> +
> +static void bcm2835_drain_rxfifo(struct bcm2835_i2c_dev *i2c_dev)
> +{
> + u32 val;
> +
> + for (;;) {
> + if (!i2c_dev->msg_buf_remaining)
> + return;
> + val = bcm2835_i2c_readl(i2c_dev, BCM2835_I2C_S);
> + if (!(val & BCM2835_I2C_S_RXD))
> + break;
> + *i2c_dev->msg_buf = bcm2835_i2c_readl(i2c_dev,
> + BCM2835_I2C_FIFO);
> + i2c_dev->msg_buf++;
> + i2c_dev->msg_buf_remaining--;
> + }
ditto?
...
> + ret = wait_for_completion_timeout(&i2c_dev->completion,
> + BCM2835_I2C_TIMEOUT);
> + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
> + if (WARN_ON(ret == 0)) {
> + dev_err(i2c_dev->dev, "i2c transfer timed out\n");
> + return -ETIMEDOUT;
> + }
I'd suggest to skip the WARN_ON. Timeout is the expected case when you
want to read from an EEPROM which is just in the process of
erasing/writing data from the previous command.
...
> + adap = &i2c_dev->adapter;
> + i2c_set_adapdata(adap, i2c_dev);
> + adap->owner = THIS_MODULE;
> + adap->class = I2C_CLASS_HWMON;
Do you really need the class?
> + strlcpy(adap->name, "bcm2835 I2C adapter", sizeof(adap->name));
> + adap->algo = &bcm2835_i2c_algo;
> + adap->dev.parent = &pdev->dev;
> + adap->nr = -1;
If you are using '-1' anyhow, you could skip setting it and simply call
i2c_add_adapter(). Or you can init it here to pdev->id. Whatever you
prefer.
> +
> + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
> +
> + return i2c_add_numbered_adapter(adap);
> +}
> +
> +static int bcm2835_i2c_remove(struct platform_device *pdev)
> +{
> + struct bcm2835_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
> +
> + i2c_del_adapter(&i2c_dev->adapter);
Is it guaranteed that interrupts cannot occur anymore? Otherwise OOPS
might happen, since the adapter is cleared here already but devm will
remove the interrupt only a little later.
Thanks,
Wolfram
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH V3] i2c: add bcm2835 driver
[not found] ` <20130211225208.GA9893-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org>
@ 2013-02-12 2:24 ` Stephen Warren
[not found] ` <5119A7D0.20000-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
0 siblings, 1 reply; 4+ messages in thread
From: Stephen Warren @ 2013-02-12 2:24 UTC (permalink / raw)
To: Wolfram Sang
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Wolfram Sang,
Rob Herring, Grant Likely,
linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ben Dooks,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-i2c-u79uwXL29TY76Z2rM5mHXA
On 02/11/2013 03:52 PM, Wolfram Sang wrote:
> Hi Stephen,
>
> On Fri, Feb 08, 2013 at 08:52:58PM -0700, Stephen Warren wrote:
>> This implements a very basic I2C host driver for the BCM2835 SoC. Missing
>> features so far are:
>>
>> * 10-bit addressing.
>> * DMA.
...
>> + ret = wait_for_completion_timeout(&i2c_dev->completion,
>> + BCM2835_I2C_TIMEOUT);
>> + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
>> + if (WARN_ON(ret == 0)) {
>> + dev_err(i2c_dev->dev, "i2c transfer timed out\n");
>> + return -ETIMEDOUT;
>> + }
>
> I'd suggest to skip the WARN_ON. Timeout is the expected case when you
> want to read from an EEPROM which is just in the process of
> erasing/writing data from the previous command.
I copied that from Tegra. Should that driver be changed too?
>> + adap = &i2c_dev->adapter;
>> + i2c_set_adapdata(adap, i2c_dev);
>> + adap->owner = THIS_MODULE;
>> + adap->class = I2C_CLASS_HWMON;
>
> Do you really need the class?
If I grep for I2C_CLASS_HWMON, I see a ton of hits. I think I'll keep it
unless you strongly object, since it seems typical.
I'll fix up the other points you mentioned.
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH V3] i2c: add bcm2835 driver
[not found] ` <5119A7D0.20000-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
@ 2013-02-12 10:27 ` Wolfram Sang
0 siblings, 0 replies; 4+ messages in thread
From: Wolfram Sang @ 2013-02-12 10:27 UTC (permalink / raw)
To: Stephen Warren
Cc: devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ, Rob Herring,
Grant Likely, linux-rpi-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Ben Dooks, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-i2c-u79uwXL29TY76Z2rM5mHXA
> >> + ret = wait_for_completion_timeout(&i2c_dev->completion,
> >> + BCM2835_I2C_TIMEOUT);
> >> + bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, BCM2835_I2C_C_CLEAR);
> >> + if (WARN_ON(ret == 0)) {
> >> + dev_err(i2c_dev->dev, "i2c transfer timed out\n");
> >> + return -ETIMEDOUT;
> >> + }
> >
> > I'd suggest to skip the WARN_ON. Timeout is the expected case when you
> > want to read from an EEPROM which is just in the process of
> > erasing/writing data from the previous command.
>
> I copied that from Tegra. Should that driver be changed too?
The suggestion applies to all drivers, yes :)
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2013-02-12 10:27 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-09 3:52 [PATCH V3] i2c: add bcm2835 driver Stephen Warren
[not found] ` <1360381978-26092-1-git-send-email-swarren-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-02-11 22:52 ` Wolfram Sang
[not found] ` <20130211225208.GA9893-8EAEigeeuNG034pCzgS/Qg7AFbiQbgqx@public.gmane.org>
2013-02-12 2:24 ` Stephen Warren
[not found] ` <5119A7D0.20000-3lzwWm7+Weoh9ZMKESR00Q@public.gmane.org>
2013-02-12 10:27 ` Wolfram Sang
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).