summaryrefslogtreecommitdiff
path: root/target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch')
-rw-r--r--target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch452
1 files changed, 452 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch b/target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch
new file mode 100644
index 0000000..531ae69
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.10/015-bcm2708-i2c-driver.patch
@@ -0,0 +1,452 @@
+diff -urN linux-3.10/drivers/i2c/busses/i2c-bcm2708.c linux-rpi-3.10.y/drivers/i2c/busses/i2c-bcm2708.c
+--- linux-3.10/drivers/i2c/busses/i2c-bcm2708.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-rpi-3.10.y/drivers/i2c/busses/i2c-bcm2708.c 2013-07-06 15:25:50.000000000 +0100
+@@ -0,0 +1,408 @@
++/*
++ * Driver for Broadcom BCM2708 BSC Controllers
++ *
++ * Copyright (C) 2012 Chris Boot & Frank Buss
++ *
++ * This driver is inspired by:
++ * i2c-ocores.c, by Peter Korsgaard <jacmet@sunsite.dk>
++ *
++ * 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; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * 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.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/spinlock.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/wait.h>
++
++/* BSC register offsets */
++#define BSC_C 0x00
++#define BSC_S 0x04
++#define BSC_DLEN 0x08
++#define BSC_A 0x0c
++#define BSC_FIFO 0x10
++#define BSC_DIV 0x14
++#define BSC_DEL 0x18
++#define BSC_CLKT 0x1c
++
++/* Bitfields in BSC_C */
++#define BSC_C_I2CEN 0x00008000
++#define BSC_C_INTR 0x00000400
++#define BSC_C_INTT 0x00000200
++#define BSC_C_INTD 0x00000100
++#define BSC_C_ST 0x00000080
++#define BSC_C_CLEAR_1 0x00000020
++#define BSC_C_CLEAR_2 0x00000010
++#define BSC_C_READ 0x00000001
++
++/* Bitfields in BSC_S */
++#define BSC_S_CLKT 0x00000200
++#define BSC_S_ERR 0x00000100
++#define BSC_S_RXF 0x00000080
++#define BSC_S_TXE 0x00000040
++#define BSC_S_RXD 0x00000020
++#define BSC_S_TXD 0x00000010
++#define BSC_S_RXR 0x00000008
++#define BSC_S_TXW 0x00000004
++#define BSC_S_DONE 0x00000002
++#define BSC_S_TA 0x00000001
++
++#define I2C_TIMEOUT_MS 150
++
++#define DRV_NAME "bcm2708_i2c"
++
++static unsigned int baudrate = CONFIG_I2C_BCM2708_BAUDRATE;
++module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
++MODULE_PARM_DESC(baudrate, "The I2C baudrate");
++
++
++struct bcm2708_i2c {
++ struct i2c_adapter adapter;
++
++ spinlock_t lock;
++ void __iomem *base;
++ int irq;
++ struct clk *clk;
++
++ struct completion done;
++
++ struct i2c_msg *msg;
++ int pos;
++ int nmsgs;
++ bool error;
++};
++
++/*
++ * This function sets the ALT mode on the I2C pins so that we can use them with
++ * the BSC hardware.
++ *
++ * FIXME: This is a hack. Use pinmux / pinctrl.
++ */
++static void bcm2708_i2c_init_pinmode(int id)
++{
++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3))
++
++ int pin;
++ u32 *gpio = ioremap(0x20200000, SZ_16K);
++
++ BUG_ON(id != 0 && id != 1);
++ /* BSC0 is on GPIO 0 & 1, BSC1 is on GPIO 2 & 3 */
++ for (pin = id*2+0; pin <= id*2+1; pin++) {
++printk("bcm2708_i2c_init_pinmode(%d,%d)\n", id, pin);
++ INP_GPIO(pin); /* set mode to GPIO input first */
++ SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */
++ }
++
++ iounmap(gpio);
++
++#undef INP_GPIO
++#undef SET_GPIO_ALT
++}
++
++static inline u32 bcm2708_rd(struct bcm2708_i2c *bi, unsigned reg)
++{
++ return readl(bi->base + reg);
++}
++
++static inline void bcm2708_wr(struct bcm2708_i2c *bi, unsigned reg, u32 val)
++{
++ writel(val, bi->base + reg);
++}
++
++static inline void bcm2708_bsc_reset(struct bcm2708_i2c *bi)
++{
++ bcm2708_wr(bi, BSC_C, 0);
++ bcm2708_wr(bi, BSC_S, BSC_S_CLKT | BSC_S_ERR | BSC_S_DONE);
++}
++
++static inline void bcm2708_bsc_fifo_drain(struct bcm2708_i2c *bi)
++{
++ while ((bcm2708_rd(bi, BSC_S) & BSC_S_RXD) && (bi->pos < bi->msg->len))
++ bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO);
++}
++
++static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi)
++{
++ while ((bcm2708_rd(bi, BSC_S) & BSC_S_TXD) && (bi->pos < bi->msg->len))
++ bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]);
++}
++
++static inline void bcm2708_bsc_setup(struct bcm2708_i2c *bi)
++{
++ unsigned long bus_hz;
++ u32 cdiv;
++ u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1;
++
++ bus_hz = clk_get_rate(bi->clk);
++ cdiv = bus_hz / baudrate;
++
++ if (bi->msg->flags & I2C_M_RD)
++ c |= BSC_C_INTR | BSC_C_READ;
++ else
++ c |= BSC_C_INTT;
++
++ bcm2708_wr(bi, BSC_DIV, cdiv);
++ bcm2708_wr(bi, BSC_A, bi->msg->addr);
++ bcm2708_wr(bi, BSC_DLEN, bi->msg->len);
++ bcm2708_wr(bi, BSC_C, c);
++}
++
++static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id)
++{
++ struct bcm2708_i2c *bi = dev_id;
++ bool handled = true;
++ u32 s;
++
++ spin_lock(&bi->lock);
++
++ /* we may see camera interrupts on the "other" I2C channel
++ Just return if we've not sent anything */
++ if (!bi->nmsgs || !bi->msg )
++ goto early_exit;
++
++ s = bcm2708_rd(bi, BSC_S);
++
++ if (s & (BSC_S_CLKT | BSC_S_ERR)) {
++ bcm2708_bsc_reset(bi);
++ bi->error = true;
++
++ /* wake up our bh */
++ complete(&bi->done);
++ } else if (s & BSC_S_DONE) {
++ bi->nmsgs--;
++
++ if (bi->msg->flags & I2C_M_RD)
++ bcm2708_bsc_fifo_drain(bi);
++
++ bcm2708_bsc_reset(bi);
++
++ if (bi->nmsgs) {
++ /* advance to next message */
++ bi->msg++;
++ bi->pos = 0;
++ bcm2708_bsc_setup(bi);
++ } else {
++ /* wake up our bh */
++ complete(&bi->done);
++ }
++ } else if (s & BSC_S_TXW) {
++ bcm2708_bsc_fifo_fill(bi);
++ } else if (s & BSC_S_RXR) {
++ bcm2708_bsc_fifo_drain(bi);
++ } else {
++ handled = false;
++ }
++
++early_exit:
++ spin_unlock(&bi->lock);
++
++ return handled ? IRQ_HANDLED : IRQ_NONE;
++}
++
++static int bcm2708_i2c_master_xfer(struct i2c_adapter *adap,
++ struct i2c_msg *msgs, int num)
++{
++ struct bcm2708_i2c *bi = adap->algo_data;
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&bi->lock, flags);
++
++ INIT_COMPLETION(bi->done);
++ bi->msg = msgs;
++ bi->pos = 0;
++ bi->nmsgs = num;
++ bi->error = false;
++
++ spin_unlock_irqrestore(&bi->lock, flags);
++
++ bcm2708_bsc_setup(bi);
++
++ ret = wait_for_completion_timeout(&bi->done,
++ msecs_to_jiffies(I2C_TIMEOUT_MS));
++ if (ret == 0) {
++ dev_err(&adap->dev, "transfer timed out\n");
++ spin_lock_irqsave(&bi->lock, flags);
++ bcm2708_bsc_reset(bi);
++ spin_unlock_irqrestore(&bi->lock, flags);
++ return -ETIMEDOUT;
++ }
++
++ return bi->error ? -EIO : num;
++}
++
++static u32 bcm2708_i2c_functionality(struct i2c_adapter *adap)
++{
++ return I2C_FUNC_I2C | /*I2C_FUNC_10BIT_ADDR |*/ I2C_FUNC_SMBUS_EMUL;
++}
++
++static struct i2c_algorithm bcm2708_i2c_algorithm = {
++ .master_xfer = bcm2708_i2c_master_xfer,
++ .functionality = bcm2708_i2c_functionality,
++};
++
++static int bcm2708_i2c_probe(struct platform_device *pdev)
++{
++ struct resource *regs;
++ int irq, err = -ENOMEM;
++ struct clk *clk;
++ struct bcm2708_i2c *bi;
++ struct i2c_adapter *adap;
++
++ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!regs) {
++ dev_err(&pdev->dev, "could not get IO memory\n");
++ return -ENXIO;
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ dev_err(&pdev->dev, "could not get IRQ\n");
++ return irq;
++ }
++
++ clk = clk_get(&pdev->dev, NULL);
++ if (IS_ERR(clk)) {
++ dev_err(&pdev->dev, "could not find clk: %ld\n", PTR_ERR(clk));
++ return PTR_ERR(clk);
++ }
++
++ bcm2708_i2c_init_pinmode(pdev->id);
++
++ bi = kzalloc(sizeof(*bi), GFP_KERNEL);
++ if (!bi)
++ goto out_clk_put;
++
++ platform_set_drvdata(pdev, bi);
++
++ adap = &bi->adapter;
++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_DDC;
++ adap->algo = &bcm2708_i2c_algorithm;
++ adap->algo_data = bi;
++ adap->dev.parent = &pdev->dev;
++ adap->nr = pdev->id;
++ strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name));
++
++ switch (pdev->id) {
++ case 0:
++ adap->class = I2C_CLASS_HWMON;
++ break;
++ case 1:
++ adap->class = I2C_CLASS_DDC;
++ break;
++ default:
++ dev_err(&pdev->dev, "can only bind to BSC 0 or 1\n");
++ err = -ENXIO;
++ goto out_free_bi;
++ }
++
++ spin_lock_init(&bi->lock);
++ init_completion(&bi->done);
++
++ bi->base = ioremap(regs->start, resource_size(regs));
++ if (!bi->base) {
++ dev_err(&pdev->dev, "could not remap memory\n");
++ goto out_free_bi;
++ }
++
++ bi->irq = irq;
++ bi->clk = clk;
++
++ err = request_irq(irq, bcm2708_i2c_interrupt, IRQF_SHARED,
++ dev_name(&pdev->dev), bi);
++ if (err) {
++ dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
++ goto out_iounmap;
++ }
++
++ bcm2708_bsc_reset(bi);
++
++ err = i2c_add_numbered_adapter(adap);
++ if (err < 0) {
++ dev_err(&pdev->dev, "could not add I2C adapter: %d\n", err);
++ goto out_free_irq;
++ }
++
++ dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %dk)\n",
++ pdev->id, (unsigned long)regs->start, irq, baudrate/1000);
++
++ return 0;
++
++out_free_irq:
++ free_irq(bi->irq, bi);
++out_iounmap:
++ iounmap(bi->base);
++out_free_bi:
++ kfree(bi);
++out_clk_put:
++ clk_put(clk);
++ return err;
++}
++
++static int bcm2708_i2c_remove(struct platform_device *pdev)
++{
++ struct bcm2708_i2c *bi = platform_get_drvdata(pdev);
++
++ platform_set_drvdata(pdev, NULL);
++
++ i2c_del_adapter(&bi->adapter);
++ free_irq(bi->irq, bi);
++ iounmap(bi->base);
++ clk_disable(bi->clk);
++ clk_put(bi->clk);
++ kfree(bi);
++
++ return 0;
++}
++
++static struct platform_driver bcm2708_i2c_driver = {
++ .driver = {
++ .name = DRV_NAME,
++ .owner = THIS_MODULE,
++ },
++ .probe = bcm2708_i2c_probe,
++ .remove = bcm2708_i2c_remove,
++};
++
++// module_platform_driver(bcm2708_i2c_driver);
++
++
++static int __init bcm2708_i2c_init(void)
++{
++ return platform_driver_register(&bcm2708_i2c_driver);
++}
++
++static void __exit bcm2708_i2c_exit(void)
++{
++ platform_driver_unregister(&bcm2708_i2c_driver);
++}
++
++module_init(bcm2708_i2c_init);
++module_exit(bcm2708_i2c_exit);
++
++
++
++MODULE_DESCRIPTION("BSC controller driver for Broadcom BCM2708");
++MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:" DRV_NAME);
+diff -urN linux-3.10/drivers/i2c/busses/Kconfig linux-rpi-3.10.y/drivers/i2c/busses/Kconfig
+--- linux-3.10/drivers/i2c/busses/Kconfig 2013-06-30 23:13:29.000000000 +0100
++++ linux-rpi-3.10.y/drivers/i2c/busses/Kconfig 2013-07-06 15:25:50.000000000 +0100
+@@ -343,6 +343,25 @@
+ This support is also available as a module. If so, the module
+ will be called i2c-bcm2835.
+
++config I2C_BCM2708
++ tristate "BCM2708 BSC"
++ depends on MACH_BCM2708
++ help
++ Enabling this option will add BSC (Broadcom Serial Controller)
++ support for the BCM2708. BSC is a Broadcom proprietary bus compatible
++ with I2C/TWI/SMBus.
++
++config I2C_BCM2708_BAUDRATE
++ prompt "BCM2708 I2C baudrate"
++ depends on I2C_BCM2708
++ int
++ default 100000
++ help
++ Set the I2C baudrate. This will alter the default value. A
++ different baudrate can be set by using a module parameter as well. If
++ no parameter is provided when loading, this is the value that will be
++ used.
++
+ config I2C_BLACKFIN_TWI
+ tristate "Blackfin TWI I2C support"
+ depends on BLACKFIN
+diff -urN linux-3.10/drivers/i2c/busses/Makefile linux-rpi-3.10.y/drivers/i2c/busses/Makefile
+--- linux-3.10/drivers/i2c/busses/Makefile 2013-06-30 23:13:29.000000000 +0100
++++ linux-rpi-3.10.y/drivers/i2c/busses/Makefile 2013-07-06 15:25:50.000000000 +0100
+@@ -32,6 +32,7 @@
+ obj-$(CONFIG_I2C_AT91) += i2c-at91.o
+ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
+ obj-$(CONFIG_I2C_BCM2835) += i2c-bcm2835.o
++obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.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