123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- From dc4e4461802c9dff395a49212e8420a9f586e247 Mon Sep 17 00:00:00 2001
- From: popcornmix <popcornmix@gmail.com>
- Date: Wed, 17 Jun 2015 15:44:08 +0100
- Subject: [PATCH 045/381] 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>
- ---
- drivers/i2c/busses/Kconfig | 21 +-
- drivers/i2c/busses/Makefile | 2 +
- drivers/i2c/busses/i2c-bcm2708.c | 493 +++++++++++++++++++++++++++++++++++++++
- 3 files changed, 515 insertions(+), 1 deletion(-)
- create mode 100644 drivers/i2c/busses/i2c-bcm2708.c
- --- a/drivers/i2c/busses/Kconfig
- +++ b/drivers/i2c/busses/Kconfig
- @@ -8,6 +8,25 @@ menu "I2C Hardware Bus support"
- comment "PC SMBus host controller drivers"
- depends on PCI
-
- +config I2C_BCM2708
- + tristate "BCM2708 BSC"
- + depends on MACH_BCM2708 || MACH_BCM2709 || 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
- @@ -365,7 +384,7 @@ config I2C_AXXIA
-
- config I2C_BCM2835
- tristate "Broadcom BCM2835 I2C controller"
- - depends on ARCH_BCM2835
- + depends on ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709
- help
- If you say yes to this option, support will be included for the
- BCM2835 I2C controller.
- --- a/drivers/i2c/busses/Makefile
- +++ b/drivers/i2c/busses/Makefile
- @@ -2,6 +2,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,493 @@
- +/*
- + * 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 = CONFIG_I2C_BCM2708_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;
- +
- + 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 ((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 int bcm2708_bsc_setup(struct bcm2708_i2c *bi)
- +{
- + u32 cdiv, s;
- + 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;
- +
- + 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);
- + 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)) {
- + /* 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;
- +
- + 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))
- + baudrate = bus_clk_rate;
- + else
- + dev_warn(&pdev->dev,
- + "Could not read clock-frequency property\n");
- + }
- +
- + 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 / baudrate;
- + if (cdiv > 0xffff) {
- + cdiv = 0xffff;
- + baudrate = bus_hz / cdiv;
- + }
- + bi->cdiv = cdiv;
- +
- + dev_info(&pdev->dev, "BSC%d Controller at 0x%08lx (irq %d) (baudrate %d)\n",
- + pdev->id, (unsigned long)regs->start, irq, baudrate);
- +
- + 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);
|