| From 2f53b727b2ba07ec39bc552f1b24f8bb33d2bb17 Mon Sep 17 00:00:00 2001 |
| From: popcornmix <popcornmix@gmail.com> |
| Date: Wed, 17 Jun 2015 15:44:08 +0100 |
| Subject: [PATCH] Add Chris Boot's i2c driver |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| i2c-bcm2708: fixed baudrate |
| |
| Fixed issue where the wrong CDIV value was set for baudrates below 3815 Hz (for 250MHz bus clock). |
| In that case the computed CDIV value was more than 0xffff. However the CDIV register width is only 16 bits. |
| This resulted in incorrect setting of CDIV and higher baudrate than intended. |
| Example: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0x1704 -> 42430Hz |
| After correction: 3500Hz -> CDIV=0x11704 -> CDIV(16bit)=0xffff -> 3815Hz |
| The correct baudrate is shown in the log after the cdiv > 0xffff correction. |
| |
| Perform I2C combined transactions when possible |
| |
| Perform I2C combined transactions whenever possible, within the |
| restrictions of the Broadcomm Serial Controller. |
| |
| Disable DONE interrupt during TA poll |
| |
| Prevent interrupt from being triggered if poll is missed and transfer |
| starts and finishes. |
| |
| i2c: Make combined transactions optional and disabled by default |
| |
| i2c: bcm2708: add device tree support |
| |
| Add DT support to driver and add to .dtsi file. |
| Setup pins in .dts file. |
| i2c is disabled by default. |
| |
| Signed-off-by: Noralf Tronnes <notro@tronnes.org> |
| |
| bcm2708: don't register i2c controllers when using DT |
| |
| The devices for the i2c controllers are in the Device Tree. |
| Only register devices when not using DT. |
| |
| Signed-off-by: Noralf Tronnes <notro@tronnes.org> |
| |
| I2C: Only register the I2C device for the current board revision |
| |
| i2c_bcm2708: Fix clock reference counting |
| |
| Fix grabbing lock from atomic context in i2c driver |
| |
| 2 main changes: |
| - check for timeouts in the bcm2708_bsc_setup function as indicated by this comment: |
| /* poll for transfer start bit (should only take 1-20 polls) */ |
| This implies that the setup function can now fail so account for this everywhere it's called |
| - Removed the clk_get_rate call from inside the setup function as it locks a mutex and that's not ok since we call it from under a spin lock. |
| |
| i2c-bcm2708: When using DT, leave the GPIO setup to pinctrl |
| |
| i2c-bcm2708: Increase timeouts to allow larger transfers |
| |
| Use the timeout value provided by the I2C_TIMEOUT ioctl when waiting |
| for completion. The default timeout is 1 second. |
| |
| See: https://github.com/raspberrypi/linux/issues/260 |
| |
| i2c-bcm2708/BCM270X_DT: Add support for I2C2 |
| |
| The third I2C bus (I2C2) is normally reserved for HDMI use. Careless |
| use of this bus can break an attached display - use with caution. |
| |
| It is recommended to disable accesses by VideoCore by setting |
| hdmi_ignore_edid=1 or hdmi_edid_file=1 in config.txt. |
| |
| The interface is disabled by default - enable using the |
| i2c2_iknowwhatimdoing DT parameter. |
| |
| bcm2708-spi: Don't use static pin configuration with DT |
| |
| Also remove superfluous error checking - the SPI framework ensures the |
| validity of the chip_select value. |
| |
| i2c-bcm2708: Remove non-DT support |
| |
| Signed-off-by: Noralf Trønnes <noralf@tronnes.org> |
| |
| Set the BSC_CLKT clock streching timeout to 35ms as per SMBus specs. |
| |
| Fixes i2c_bcm2708: Write to FIFO correctly - v2 (#1574) |
| |
| * i2c: fix i2c_bcm2708: Clear FIFO before sending data |
| |
| Make sure FIFO gets cleared before trying to send |
| data in case of a repeated start (COMBINED=Y). |
| |
| * i2c: fix i2c_bcm2708: Only write to FIFO when not full |
| |
| Check if FIFO can accept data before writing. |
| To avoid a peripheral read on the last iteration of a loop, |
| both bcm2708_bsc_fifo_fill and ~drain are changed as well. |
| --- |
| drivers/i2c/busses/Kconfig | 19 ++ |
| drivers/i2c/busses/Makefile | 2 + |
| drivers/i2c/busses/i2c-bcm2708.c | 512 +++++++++++++++++++++++++++++++ |
| 3 files changed, 533 insertions(+) |
| create mode 100644 drivers/i2c/busses/i2c-bcm2708.c |
| |
| --- a/drivers/i2c/busses/Kconfig |
| +++ b/drivers/i2c/busses/Kconfig |
| @@ -9,6 +9,25 @@ menu "I2C Hardware Bus support" |
| comment "PC SMBus host controller drivers" |
| depends on PCI |
| |
| +config I2C_BCM2708 |
| + tristate "BCM2708 BSC" |
| + depends on ARCH_BCM2835 |
| + 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_ALI1535 |
| tristate "ALI 1535" |
| depends on PCI |
| --- a/drivers/i2c/busses/Makefile |
| +++ b/drivers/i2c/busses/Makefile |
| @@ -3,6 +3,8 @@ |
| # Makefile for the i2c bus drivers. |
| # |
| |
| +obj-$(CONFIG_I2C_BCM2708) += i2c-bcm2708.o |
| + |
| # ACPI drivers |
| obj-$(CONFIG_I2C_SCMI) += i2c-scmi.o |
| |
| --- /dev/null |
| +++ b/drivers/i2c/busses/i2c-bcm2708.c |
| @@ -0,0 +1,512 @@ |
| +/* |
| + * 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/of.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_WAIT_LOOP_COUNT 200 |
| + |
| +#define DRV_NAME "bcm2708_i2c" |
| + |
| +static unsigned int baudrate; |
| +module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); |
| +MODULE_PARM_DESC(baudrate, "The I2C baudrate"); |
| + |
| +static bool combined = false; |
| +module_param(combined, bool, 0644); |
| +MODULE_PARM_DESC(combined, "Use combined transactions"); |
| + |
| +struct bcm2708_i2c { |
| + struct i2c_adapter adapter; |
| + |
| + spinlock_t lock; |
| + void __iomem *base; |
| + int irq; |
| + struct clk *clk; |
| + u32 cdiv; |
| + u32 clk_tout; |
| + |
| + struct completion done; |
| + |
| + struct i2c_msg *msg; |
| + int pos; |
| + int nmsgs; |
| + bool error; |
| +}; |
| + |
| +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 ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_RXD)) |
| + bi->msg->buf[bi->pos++] = bcm2708_rd(bi, BSC_FIFO); |
| +} |
| + |
| +static inline void bcm2708_bsc_fifo_fill(struct bcm2708_i2c *bi) |
| +{ |
| + while ((bi->pos < bi->msg->len) && (bcm2708_rd(bi, BSC_S) & BSC_S_TXD)) |
| + bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); |
| +} |
| + |
| +static inline int bcm2708_bsc_setup(struct bcm2708_i2c *bi) |
| +{ |
| + u32 cdiv, s, clk_tout; |
| + u32 c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_ST | BSC_C_CLEAR_1; |
| + int wait_loops = I2C_WAIT_LOOP_COUNT; |
| + |
| + /* Can't call clk_get_rate as it locks a mutex and here we are spinlocked. |
| + * Use the value that we cached in the probe. |
| + */ |
| + cdiv = bi->cdiv; |
| + clk_tout = bi->clk_tout; |
| + |
| + if (bi->msg->flags & I2C_M_RD) |
| + c |= BSC_C_INTR | BSC_C_READ; |
| + else |
| + c |= BSC_C_INTT; |
| + |
| + bcm2708_wr(bi, BSC_CLKT, clk_tout); |
| + bcm2708_wr(bi, BSC_DIV, cdiv); |
| + bcm2708_wr(bi, BSC_A, bi->msg->addr); |
| + bcm2708_wr(bi, BSC_DLEN, bi->msg->len); |
| + if (combined) |
| + { |
| + /* Do the next two messages meet combined transaction criteria? |
| + - Current message is a write, next message is a read |
| + - Both messages to same slave address |
| + - Write message can fit inside FIFO (16 bytes or less) */ |
| + if ( (bi->nmsgs > 1) && |
| + !(bi->msg[0].flags & I2C_M_RD) && (bi->msg[1].flags & I2C_M_RD) && |
| + (bi->msg[0].addr == bi->msg[1].addr) && (bi->msg[0].len <= 16)) { |
| + |
| + /* Clear FIFO */ |
| + bcm2708_wr(bi, BSC_C, BSC_C_CLEAR_1); |
| + |
| + /* Fill FIFO with entire write message (16 byte FIFO) */ |
| + while (bi->pos < bi->msg->len) { |
| + bcm2708_wr(bi, BSC_FIFO, bi->msg->buf[bi->pos++]); |
| + } |
| + /* Start write transfer (no interrupts, don't clear FIFO) */ |
| + bcm2708_wr(bi, BSC_C, BSC_C_I2CEN | BSC_C_ST); |
| + |
| + /* poll for transfer start bit (should only take 1-20 polls) */ |
| + do { |
| + s = bcm2708_rd(bi, BSC_S); |
| + } while (!(s & (BSC_S_TA | BSC_S_ERR | BSC_S_CLKT | BSC_S_DONE)) && --wait_loops >= 0); |
| + |
| + /* did we time out or some error occured? */ |
| + if (wait_loops < 0 || (s & (BSC_S_ERR | BSC_S_CLKT))) { |
| + return -1; |
| + } |
| + |
| + /* Send next read message before the write transfer finishes. */ |
| + bi->nmsgs--; |
| + bi->msg++; |
| + bi->pos = 0; |
| + bcm2708_wr(bi, BSC_DLEN, bi->msg->len); |
| + c = BSC_C_I2CEN | BSC_C_INTD | BSC_C_INTR | BSC_C_ST | BSC_C_READ; |
| + } |
| + } |
| + bcm2708_wr(bi, BSC_C, c); |
| + |
| + return 0; |
| +} |
| + |
| +static irqreturn_t bcm2708_i2c_interrupt(int irq, void *dev_id) |
| +{ |
| + struct bcm2708_i2c *bi = dev_id; |
| + bool handled = true; |
| + u32 s; |
| + int ret; |
| + |
| + 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; |
| + |
| + bi->msg = 0; /* to inform the that all work is done */ |
| + bi->nmsgs = 0; |
| + /* 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; |
| + ret = bcm2708_bsc_setup(bi); |
| + if (ret < 0) { |
| + bcm2708_bsc_reset(bi); |
| + bi->error = true; |
| + bi->msg = 0; /* to inform the that all work is done */ |
| + bi->nmsgs = 0; |
| + /* wake up our bh */ |
| + complete(&bi->done); |
| + goto early_exit; |
| + } |
| + } else { |
| + bi->msg = 0; /* to inform the that all work is done */ |
| + bi->nmsgs = 0; |
| + /* 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); |
| + |
| + reinit_completion(&bi->done); |
| + bi->msg = msgs; |
| + bi->pos = 0; |
| + bi->nmsgs = num; |
| + bi->error = false; |
| + |
| + ret = bcm2708_bsc_setup(bi); |
| + |
| + spin_unlock_irqrestore(&bi->lock, flags); |
| + |
| + /* check the result of the setup */ |
| + if (ret < 0) |
| + { |
| + dev_err(&adap->dev, "transfer setup timed out\n"); |
| + goto error_timeout; |
| + } |
| + |
| + ret = wait_for_completion_timeout(&bi->done, adap->timeout); |
| + if (ret == 0) { |
| + dev_err(&adap->dev, "transfer timed out\n"); |
| + goto error_timeout; |
| + } |
| + |
| + ret = bi->error ? -EIO : num; |
| + return ret; |
| + |
| +error_timeout: |
| + spin_lock_irqsave(&bi->lock, flags); |
| + bcm2708_bsc_reset(bi); |
| + bi->msg = 0; /* to inform the interrupt handler that there's nothing else to be done */ |
| + bi->nmsgs = 0; |
| + spin_unlock_irqrestore(&bi->lock, flags); |
| + return -ETIMEDOUT; |
| +} |
| + |
| +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; |
| + unsigned long bus_hz; |
| + u32 cdiv, clk_tout; |
| + u32 baud; |
| + |
| + baud = CONFIG_I2C_BCM2708_BAUDRATE; |
| + |
| + if (pdev->dev.of_node) { |
| + u32 bus_clk_rate; |
| + pdev->id = of_alias_get_id(pdev->dev.of_node, "i2c"); |
| + if (pdev->id < 0) { |
| + dev_err(&pdev->dev, "alias is missing\n"); |
| + return -EINVAL; |
| + } |
| + if (!of_property_read_u32(pdev->dev.of_node, |
| + "clock-frequency", &bus_clk_rate)) |
| + baud = bus_clk_rate; |
| + else |
| + dev_warn(&pdev->dev, |
| + "Could not read clock-frequency property\n"); |
| + } |
| + |
| + if (baudrate) |
| + baud = baudrate; |
| + |
| + 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); |
| + } |
| + |
| + err = clk_prepare_enable(clk); |
| + if (err) { |
| + dev_err(&pdev->dev, "could not enable clk: %d\n", err); |
| + goto out_clk_put; |
| + } |
| + |
| + bi = kzalloc(sizeof(*bi), GFP_KERNEL); |
| + if (!bi) |
| + goto out_clk_disable; |
| + |
| + 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)); |
| + adap->dev.of_node = pdev->dev.of_node; |
| + |
| + switch (pdev->id) { |
| + case 0: |
| + adap->class = I2C_CLASS_HWMON; |
| + break; |
| + case 1: |
| + adap->class = I2C_CLASS_DDC; |
| + break; |
| + case 2: |
| + adap->class = I2C_CLASS_DDC; |
| + break; |
| + default: |
| + dev_err(&pdev->dev, "can only bind to BSC 0, 1 or 2\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; |
| + } |
| + |
| + bus_hz = clk_get_rate(bi->clk); |
| + cdiv = bus_hz / baud; |
| + if (cdiv > 0xffff) { |
| + cdiv = 0xffff; |
| + baud = bus_hz / cdiv; |
| + } |
| + |
| + clk_tout = 35/1000*baud; //35ms timeout as per SMBus specs. |
| + if (clk_tout > 0xffff) |
| + clk_tout = 0xffff; |
| + |
| + bi->cdiv = cdiv; |
| + bi->clk_tout = clk_tout; |
| + |
| + dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n", |
| + pdev->id, (unsigned long)regs->start, irq, baud); |
| + |
| + return 0; |
| + |
| +out_free_irq: |
| + free_irq(bi->irq, bi); |
| +out_iounmap: |
| + iounmap(bi->base); |
| +out_free_bi: |
| + kfree(bi); |
| +out_clk_disable: |
| + clk_disable_unprepare(clk); |
| +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_unprepare(bi->clk); |
| + clk_put(bi->clk); |
| + kfree(bi); |
| + |
| + return 0; |
| +} |
| + |
| +static const struct of_device_id bcm2708_i2c_of_match[] = { |
| + { .compatible = "brcm,bcm2708-i2c" }, |
| + {}, |
| +}; |
| +MODULE_DEVICE_TABLE(of, bcm2708_i2c_of_match); |
| + |
| +static struct platform_driver bcm2708_i2c_driver = { |
| + .driver = { |
| + .name = DRV_NAME, |
| + .owner = THIS_MODULE, |
| + .of_match_table = bcm2708_i2c_of_match, |
| + }, |
| + .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); |