brcm2708: update to v3.18
[openwrt.git] / target / linux / brcm2708 / patches-3.18 / 0019-Add-Chris-Boot-s-i2c-and-spi-drivers.patch
diff --git a/target/linux/brcm2708/patches-3.18/0019-Add-Chris-Boot-s-i2c-and-spi-drivers.patch b/target/linux/brcm2708/patches-3.18/0019-Add-Chris-Boot-s-i2c-and-spi-drivers.patch
new file mode 100755 (executable)
index 0000000..cad4e68
--- /dev/null
@@ -0,0 +1,1352 @@
+From 66ea263246ca6dd9f63dce8fb22157fa83693300 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Mon, 4 Nov 2013 18:56:10 +0000
+Subject: [PATCH 019/114] Add Chris Boot's i2c and spi drivers.
+
+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.
+---
+ arch/arm/configs/bcmrpi_defconfig |   7 +
+ arch/arm/mach-bcm2708/Kconfig     |   7 +
+ arch/arm/mach-bcm2708/bcm2708.c   | 104 ++++++-
+ drivers/i2c/busses/Kconfig        |  19 ++
+ drivers/i2c/busses/Makefile       |   1 +
+ drivers/i2c/busses/i2c-bcm2708.c  | 420 +++++++++++++++++++++++++
+ drivers/spi/Kconfig               |   8 +
+ drivers/spi/Makefile              |   1 +
+ drivers/spi/spi-bcm2708.c         | 626 ++++++++++++++++++++++++++++++++++++++
+ 9 files changed, 1191 insertions(+), 2 deletions(-)
+ create mode 100644 drivers/i2c/busses/i2c-bcm2708.c
+ create mode 100644 drivers/spi/spi-bcm2708.c
+
+diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig
+index 63cb6a4..6d2eae1 100644
+--- a/arch/arm/configs/bcmrpi_defconfig
++++ b/arch/arm/configs/bcmrpi_defconfig
+@@ -195,6 +195,13 @@ CONFIG_SERIAL_AMBA_PL011=y
+ CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+ # CONFIG_HW_RANDOM is not set
+ CONFIG_RAW_DRIVER=y
++CONFIG_I2C=y
++CONFIG_I2C_CHARDEV=m
++CONFIG_I2C_BCM2708=m
++CONFIG_SPI=y
++CONFIG_SPI_BCM2708=m
++CONFIG_SPI_SPIDEV=m
++CONFIG_GPIO_SYSFS=y
+ # CONFIG_HWMON is not set
+ CONFIG_WATCHDOG=y
+ CONFIG_FB=y
+diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig
+index 9355841..e151ed4 100644
+--- a/arch/arm/mach-bcm2708/Kconfig
++++ b/arch/arm/mach-bcm2708/Kconfig
+@@ -31,4 +31,11 @@ config BCM2708_NOL2CACHE
+         help
+           Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt.
++config BCM2708_SPIDEV
++      bool "Bind spidev to SPI0 master"
++      depends on MACH_BCM2708
++      depends on SPI
++      default y
++      help
++        Binds spidev driver to the SPI0 master
+ endmenu
+diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
+index af57d11..82f56fb 100644
+--- a/arch/arm/mach-bcm2708/bcm2708.c
++++ b/arch/arm/mach-bcm2708/bcm2708.c
+@@ -31,6 +31,7 @@
+ #include <linux/cnt32_to_63.h>
+ #include <linux/io.h>
+ #include <linux/module.h>
++#include <linux/spi/spi.h>
+ #include <linux/version.h>
+ #include <linux/clkdev.h>
+@@ -205,7 +206,6 @@ static struct clk osc_clk = {
+ /* warning - the USB needs a clock > 34MHz */
+-#ifdef CONFIG_MMC_BCM2708
+ static struct clk sdhost_clk = {
+ #ifdef CONFIG_ARCH_BCM2708_CHIPIT
+       .rate = 4000000,        /* 4MHz */
+@@ -213,7 +213,6 @@ static struct clk sdhost_clk = {
+       .rate = 250000000,      /* 250MHz */
+ #endif
+ };
+-#endif
+ static struct clk_lookup lookups[] = {
+       {                       /* UART0 */
+@@ -223,6 +222,15 @@ static struct clk_lookup lookups[] = {
+       {                       /* USB */
+        .dev_id = "bcm2708_usb",
+        .clk = &osc_clk,
++       }, {   /* SPI */
++               .dev_id = "bcm2708_spi.0",
++               .clk = &sdhost_clk,
++       }, {   /* BSC0 */
++               .dev_id = "bcm2708_i2c.0",
++               .clk = &sdhost_clk,
++       }, {   /* BSC1 */
++               .dev_id = "bcm2708_i2c.1",
++               .clk = &sdhost_clk,
+        }
+ };
+@@ -455,6 +463,89 @@ static struct platform_device bcm2708_alsa_devices[] = {
+              },
+ };
++static struct resource bcm2708_spi_resources[] = {
++      {
++              .start = SPI0_BASE,
++              .end = SPI0_BASE + SZ_256 - 1,
++              .flags = IORESOURCE_MEM,
++      }, {
++              .start = IRQ_SPI,
++              .end = IRQ_SPI,
++              .flags = IORESOURCE_IRQ,
++      }
++};
++
++
++static u64 bcm2708_spi_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON);
++static struct platform_device bcm2708_spi_device = {
++      .name = "bcm2708_spi",
++      .id = 0,
++      .num_resources = ARRAY_SIZE(bcm2708_spi_resources),
++      .resource = bcm2708_spi_resources,
++      .dev = {
++              .dma_mask = &bcm2708_spi_dmamask,
++              .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON)},
++};
++
++#ifdef CONFIG_BCM2708_SPIDEV
++static struct spi_board_info bcm2708_spi_devices[] = {
++#ifdef CONFIG_SPI_SPIDEV
++      {
++              .modalias = "spidev",
++              .max_speed_hz = 500000,
++              .bus_num = 0,
++              .chip_select = 0,
++              .mode = SPI_MODE_0,
++      }, {
++              .modalias = "spidev",
++              .max_speed_hz = 500000,
++              .bus_num = 0,
++              .chip_select = 1,
++              .mode = SPI_MODE_0,
++      }
++#endif
++};
++#endif
++
++static struct resource bcm2708_bsc0_resources[] = {
++      {
++              .start = BSC0_BASE,
++              .end = BSC0_BASE + SZ_256 - 1,
++              .flags = IORESOURCE_MEM,
++      }, {
++              .start = INTERRUPT_I2C,
++              .end = INTERRUPT_I2C,
++              .flags = IORESOURCE_IRQ,
++      }
++};
++
++static struct platform_device bcm2708_bsc0_device = {
++      .name = "bcm2708_i2c",
++      .id = 0,
++      .num_resources = ARRAY_SIZE(bcm2708_bsc0_resources),
++      .resource = bcm2708_bsc0_resources,
++};
++
++
++static struct resource bcm2708_bsc1_resources[] = {
++      {
++              .start = BSC1_BASE,
++              .end = BSC1_BASE + SZ_256 - 1,
++              .flags = IORESOURCE_MEM,
++      }, {
++              .start = INTERRUPT_I2C,
++              .end = INTERRUPT_I2C,
++              .flags = IORESOURCE_IRQ,
++      }
++};
++
++static struct platform_device bcm2708_bsc1_device = {
++      .name = "bcm2708_i2c",
++      .id = 1,
++      .num_resources = ARRAY_SIZE(bcm2708_bsc1_resources),
++      .resource = bcm2708_bsc1_resources,
++};
++
+ static struct platform_device bcm2835_hwmon_device = {
+       .name = "bcm2835_hwmon",
+ };
+@@ -571,6 +662,10 @@ void __init bcm2708_init(void)
+       for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++)
+               bcm_register_device(&bcm2708_alsa_devices[i]);
++      bcm_register_device(&bcm2708_spi_device);
++      bcm_register_device(&bcm2708_bsc0_device);
++      bcm_register_device(&bcm2708_bsc1_device);
++
+       bcm_register_device(&bcm2835_hwmon_device);
+       bcm_register_device(&bcm2835_thermal_device);
+@@ -580,6 +675,11 @@ void __init bcm2708_init(void)
+       }
+       system_rev = boardrev;
+       system_serial_low = serial;
++
++#ifdef CONFIG_BCM2708_SPIDEV
++      spi_register_board_info(bcm2708_spi_devices,
++                      ARRAY_SIZE(bcm2708_spi_devices));
++#endif
+ }
+ static void timer_set_mode(enum clock_event_mode mode,
+diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
+index 917c358..3d3db41 100644
+--- a/drivers/i2c/busses/Kconfig
++++ b/drivers/i2c/busses/Kconfig
+@@ -371,6 +371,25 @@ config I2C_BCM2835
+         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_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 78d56c5..abe642f 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_BCM2708)     += i2c-bcm2708.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-bcm2708.c b/drivers/i2c/busses/i2c-bcm2708.c
+new file mode 100644
+index 0000000..09203c0
+--- /dev/null
++++ b/drivers/i2c/busses/i2c-bcm2708.c
+@@ -0,0 +1,420 @@
++/*
++ * 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(GPIO_BASE, 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 (cdiv > 0xffff)
++              cdiv = 0xffff;
++
++      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);
++
++      reinit_completion(&bi->done);
++      bi->msg = msgs;
++      bi->pos = 0;
++      bi->nmsgs = num;
++      bi->error = false;
++
++      bcm2708_bsc_setup(bi);
++
++      /* unlockig _after_ the setup to avoid races with the interrupt routine */
++      spin_unlock_irqrestore(&bi->lock, flags);
++
++      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;
++      unsigned long bus_hz;
++      u32 cdiv;
++
++      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;
++      }
++
++      bus_hz = clk_get_rate(bi->clk);
++      cdiv = bus_hz / baudrate;
++      if (cdiv > 0xffff) {
++              cdiv = 0xffff;
++              baudrate = bus_hz / 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_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 --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
+index 84e7c9e..71b4741 100644
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -86,6 +86,14 @@ config SPI_BCM2835
+         is for the regular SPI controller. Slave mode operation is not also
+         not supported.
++config SPI_BCM2708
++      tristate "BCM2708 SPI controller driver (SPI0)"
++      depends on MACH_BCM2708
++      help
++        This selects a driver for the Broadcom BCM2708 SPI master (SPI0). This
++        driver is not compatible with the "Universal SPI Master" or the SPI slave
++        device.
++
+ config SPI_BFIN5XX
+       tristate "SPI controller driver for ADI Blackfin5xx"
+       depends on BLACKFIN && !BF60x
+diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
+index 78f24ca..fdd29d3 100644
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -20,6 +20,7 @@ obj-$(CONFIG_SPI_BCM63XX)            += spi-bcm63xx.o
+ obj-$(CONFIG_SPI_BCM63XX_HSSPI)               += spi-bcm63xx-hsspi.o
+ obj-$(CONFIG_SPI_BFIN5XX)             += spi-bfin5xx.o
+ obj-$(CONFIG_SPI_ADI_V3)                += spi-adi-v3.o
++obj-$(CONFIG_SPI_BCM2708)             += spi-bcm2708.o
+ obj-$(CONFIG_SPI_BFIN_SPORT)          += spi-bfin-sport.o
+ obj-$(CONFIG_SPI_BITBANG)             += spi-bitbang.o
+ obj-$(CONFIG_SPI_BUTTERFLY)           += spi-butterfly.o
+diff --git a/drivers/spi/spi-bcm2708.c b/drivers/spi/spi-bcm2708.c
+new file mode 100644
+index 0000000..b04a57d
+--- /dev/null
++++ b/drivers/spi/spi-bcm2708.c
+@@ -0,0 +1,626 @@
++/*
++ * Driver for Broadcom BCM2708 SPI Controllers
++ *
++ * Copyright (C) 2012 Chris Boot
++ *
++ * This driver is inspired by:
++ * spi-ath79.c, Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
++ * spi-atmel.c, Copyright (C) 2006 Atmel 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; 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/spi/spi.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++#include <linux/log2.h>
++#include <linux/sched.h>
++#include <linux/wait.h>
++
++/* SPI register offsets */
++#define SPI_CS                        0x00
++#define SPI_FIFO              0x04
++#define SPI_CLK                       0x08
++#define SPI_DLEN              0x0c
++#define SPI_LTOH              0x10
++#define SPI_DC                        0x14
++
++/* Bitfields in CS */
++#define SPI_CS_LEN_LONG               0x02000000
++#define SPI_CS_DMA_LEN                0x01000000
++#define SPI_CS_CSPOL2         0x00800000
++#define SPI_CS_CSPOL1         0x00400000
++#define SPI_CS_CSPOL0         0x00200000
++#define SPI_CS_RXF            0x00100000
++#define SPI_CS_RXR            0x00080000
++#define SPI_CS_TXD            0x00040000
++#define SPI_CS_RXD            0x00020000
++#define SPI_CS_DONE           0x00010000
++#define SPI_CS_LEN            0x00002000
++#define SPI_CS_REN            0x00001000
++#define SPI_CS_ADCS           0x00000800
++#define SPI_CS_INTR           0x00000400
++#define SPI_CS_INTD           0x00000200
++#define SPI_CS_DMAEN          0x00000100
++#define SPI_CS_TA             0x00000080
++#define SPI_CS_CSPOL          0x00000040
++#define SPI_CS_CLEAR_RX               0x00000020
++#define SPI_CS_CLEAR_TX               0x00000010
++#define SPI_CS_CPOL           0x00000008
++#define SPI_CS_CPHA           0x00000004
++#define SPI_CS_CS_10          0x00000002
++#define SPI_CS_CS_01          0x00000001
++
++#define SPI_TIMEOUT_MS        150
++
++#define DRV_NAME      "bcm2708_spi"
++
++struct bcm2708_spi {
++      spinlock_t lock;
++      void __iomem *base;
++      int irq;
++      struct clk *clk;
++      bool stopping;
++
++      struct list_head queue;
++      struct workqueue_struct *workq;
++      struct work_struct work;
++      struct completion done;
++
++      const u8 *tx_buf;
++      u8 *rx_buf;
++      int len;
++};
++
++struct bcm2708_spi_state {
++      u32 cs;
++      u16 cdiv;
++};
++
++/*
++ * This function sets the ALT mode on the SPI pins so that we can use them with
++ * the SPI hardware.
++ *
++ * FIXME: This is a hack. Use pinmux / pinctrl.
++ */
++static void bcm2708_init_pinmode(void)
++{
++#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(GPIO_BASE, SZ_16K);
++
++      /* SPI is on GPIO 7..11 */
++      for (pin = 7; pin <= 11; 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_spi *bs, unsigned reg)
++{
++      return readl(bs->base + reg);
++}
++
++static inline void bcm2708_wr(struct bcm2708_spi *bs, unsigned reg, u32 val)
++{
++      writel(val, bs->base + reg);
++}
++
++static inline void bcm2708_rd_fifo(struct bcm2708_spi *bs, int len)
++{
++      u8 byte;
++
++      while (len--) {
++              byte = bcm2708_rd(bs, SPI_FIFO);
++              if (bs->rx_buf)
++                      *bs->rx_buf++ = byte;
++      }
++}
++
++static inline void bcm2708_wr_fifo(struct bcm2708_spi *bs, int len)
++{
++      u8 byte;
++      u16 val;
++
++      if (len > bs->len)
++              len = bs->len;
++
++      if (unlikely(bcm2708_rd(bs, SPI_CS) & SPI_CS_LEN)) {
++              /* LoSSI mode */
++              if (unlikely(len % 2)) {
++                      printk(KERN_ERR"bcm2708_wr_fifo: length must be even, skipping.\n");
++                      bs->len = 0;
++                      return;
++              }
++              while (len) {
++                      if (bs->tx_buf) {
++                              val = *(const u16 *)bs->tx_buf;
++                              bs->tx_buf += 2;
++                      } else
++                              val = 0;
++                      bcm2708_wr(bs, SPI_FIFO, val);
++                      bs->len -= 2;
++                      len -= 2;
++              }
++              return;
++      }
++
++      while (len--) {
++              byte = bs->tx_buf ? *bs->tx_buf++ : 0;
++              bcm2708_wr(bs, SPI_FIFO, byte);
++              bs->len--;
++      }
++}
++
++static irqreturn_t bcm2708_spi_interrupt(int irq, void *dev_id)
++{
++      struct spi_master *master = dev_id;
++      struct bcm2708_spi *bs = spi_master_get_devdata(master);
++      u32 cs;
++
++      spin_lock(&bs->lock);
++
++      cs = bcm2708_rd(bs, SPI_CS);
++
++      if (cs & SPI_CS_DONE) {
++              if (bs->len) { /* first interrupt in a transfer */
++                      /* fill the TX fifo with up to 16 bytes */
++                      bcm2708_wr_fifo(bs, 16);
++              } else { /* transfer complete */
++                      /* disable interrupts */
++                      cs &= ~(SPI_CS_INTR | SPI_CS_INTD);
++                      bcm2708_wr(bs, SPI_CS, cs);
++
++                      /* drain RX FIFO */
++                      while (cs & SPI_CS_RXD) {
++                              bcm2708_rd_fifo(bs, 1);
++                              cs = bcm2708_rd(bs, SPI_CS);
++                      }
++
++                      /* wake up our bh */
++                      complete(&bs->done);
++              }
++      } else if (cs & SPI_CS_RXR) {
++              /* read 12 bytes of data */
++              bcm2708_rd_fifo(bs, 12);
++
++              /* write up to 12 bytes */
++              bcm2708_wr_fifo(bs, 12);
++      }
++
++      spin_unlock(&bs->lock);
++
++      return IRQ_HANDLED;
++}
++
++static int bcm2708_setup_state(struct spi_master *master,
++              struct device *dev, struct bcm2708_spi_state *state,
++              u32 hz, u8 csel, u8 mode, u8 bpw)
++{
++      struct bcm2708_spi *bs = spi_master_get_devdata(master);
++      int cdiv;
++      unsigned long bus_hz;
++      u32 cs = 0;
++
++      bus_hz = clk_get_rate(bs->clk);
++
++      if (hz >= bus_hz) {
++              cdiv = 2; /* bus_hz / 2 is as fast as we can go */
++      } else if (hz) {
++              cdiv = DIV_ROUND_UP(bus_hz, hz);
++
++              /* CDIV must be a power of 2, so round up */
++              cdiv = roundup_pow_of_two(cdiv);
++
++              if (cdiv > 65536) {
++                      dev_dbg(dev,
++                              "setup: %d Hz too slow, cdiv %u; min %ld Hz\n",
++                              hz, cdiv, bus_hz / 65536);
++                      return -EINVAL;
++              } else if (cdiv == 65536) {
++                      cdiv = 0;
++              } else if (cdiv == 1) {
++                      cdiv = 2; /* 1 gets rounded down to 0; == 65536 */
++              }
++      } else {
++              cdiv = 0;
++      }
++
++      switch (bpw) {
++      case 8:
++              break;
++      case 9:
++              /* Reading in LoSSI mode is a special case. See 'BCM2835 ARM Peripherals' datasheet */
++              cs |= SPI_CS_LEN;
++              break;
++      default:
++              dev_dbg(dev, "setup: invalid bits_per_word %u (must be 8 or 9)\n",
++                      bpw);
++              return -EINVAL;
++      }
++
++      if (mode & SPI_CPOL)
++              cs |= SPI_CS_CPOL;
++      if (mode & SPI_CPHA)
++              cs |= SPI_CS_CPHA;
++
++      if (!(mode & SPI_NO_CS)) {
++              if (mode & SPI_CS_HIGH) {
++                      cs |= SPI_CS_CSPOL;
++                      cs |= SPI_CS_CSPOL0 << csel;
++              }
++
++              cs |= csel;
++      } else {
++              cs |= SPI_CS_CS_10 | SPI_CS_CS_01;
++      }
++
++      if (state) {
++              state->cs = cs;
++              state->cdiv = cdiv;
++              dev_dbg(dev, "setup: want %d Hz; "
++                      "bus_hz=%lu / cdiv=%u == %lu Hz; "
++                      "mode %u: cs 0x%08X\n",
++                      hz, bus_hz, cdiv, bus_hz/cdiv, mode, cs);
++      }
++
++      return 0;
++}
++
++static int bcm2708_process_transfer(struct bcm2708_spi *bs,
++              struct spi_message *msg, struct spi_transfer *xfer)
++{
++      struct spi_device *spi = msg->spi;
++      struct bcm2708_spi_state state, *stp;
++      int ret;
++      u32 cs;
++
++      if (bs->stopping)
++              return -ESHUTDOWN;
++
++      if (xfer->bits_per_word || xfer->speed_hz) {
++              ret = bcm2708_setup_state(spi->master, &spi->dev, &state,
++                      xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz,
++                      spi->chip_select, spi->mode,
++                      xfer->bits_per_word ? xfer->bits_per_word :
++                              spi->bits_per_word);
++              if (ret)
++                      return ret;
++
++              stp = &state;
++      } else {
++              stp = spi->controller_state;
++      }
++
++      reinit_completion(&bs->done);
++      bs->tx_buf = xfer->tx_buf;
++      bs->rx_buf = xfer->rx_buf;
++      bs->len = xfer->len;
++
++      cs = stp->cs | SPI_CS_INTR | SPI_CS_INTD | SPI_CS_TA;
++
++      bcm2708_wr(bs, SPI_CLK, stp->cdiv);
++      bcm2708_wr(bs, SPI_CS, cs);
++
++      ret = wait_for_completion_timeout(&bs->done,
++                      msecs_to_jiffies(SPI_TIMEOUT_MS));
++      if (ret == 0) {
++              dev_err(&spi->dev, "transfer timed out\n");
++              return -ETIMEDOUT;
++      }
++
++      if (xfer->delay_usecs)
++              udelay(xfer->delay_usecs);
++
++      if (list_is_last(&xfer->transfer_list, &msg->transfers) ||
++                      xfer->cs_change) {
++              /* clear TA and interrupt flags */
++              bcm2708_wr(bs, SPI_CS, stp->cs);
++      }
++
++      msg->actual_length += (xfer->len - bs->len);
++
++      return 0;
++}
++
++static void bcm2708_work(struct work_struct *work)
++{
++      struct bcm2708_spi *bs = container_of(work, struct bcm2708_spi, work);
++      unsigned long flags;
++      struct spi_message *msg;
++      struct spi_transfer *xfer;
++      int status = 0;
++
++      spin_lock_irqsave(&bs->lock, flags);
++      while (!list_empty(&bs->queue)) {
++              msg = list_first_entry(&bs->queue, struct spi_message, queue);
++              list_del_init(&msg->queue);
++              spin_unlock_irqrestore(&bs->lock, flags);
++
++              list_for_each_entry(xfer, &msg->transfers, transfer_list) {
++                      status = bcm2708_process_transfer(bs, msg, xfer);
++                      if (status)
++                              break;
++              }
++
++              msg->status = status;
++              msg->complete(msg->context);
++
++              spin_lock_irqsave(&bs->lock, flags);
++      }
++      spin_unlock_irqrestore(&bs->lock, flags);
++}
++
++static int bcm2708_spi_setup(struct spi_device *spi)
++{
++      struct bcm2708_spi *bs = spi_master_get_devdata(spi->master);
++      struct bcm2708_spi_state *state;
++      int ret;
++
++      if (bs->stopping)
++              return -ESHUTDOWN;
++
++      if (!(spi->mode & SPI_NO_CS) &&
++                      (spi->chip_select > spi->master->num_chipselect)) {
++              dev_dbg(&spi->dev,
++                      "setup: invalid chipselect %u (%u defined)\n",
++                      spi->chip_select, spi->master->num_chipselect);
++              return -EINVAL;
++      }
++
++      state = spi->controller_state;
++      if (!state) {
++              state = kzalloc(sizeof(*state), GFP_KERNEL);
++              if (!state)
++                      return -ENOMEM;
++
++              spi->controller_state = state;
++      }
++
++      ret = bcm2708_setup_state(spi->master, &spi->dev, state,
++              spi->max_speed_hz, spi->chip_select, spi->mode,
++              spi->bits_per_word);
++      if (ret < 0) {
++              kfree(state);
++              spi->controller_state = NULL;
++                return ret;
++      }
++
++      dev_dbg(&spi->dev,
++              "setup: cd %d: %d Hz, bpw %u, mode 0x%x -> CS=%08x CDIV=%04x\n",
++              spi->chip_select, spi->max_speed_hz, spi->bits_per_word,
++              spi->mode, state->cs, state->cdiv);
++
++      return 0;
++}
++
++static int bcm2708_spi_transfer(struct spi_device *spi, struct spi_message *msg)
++{
++      struct bcm2708_spi *bs = spi_master_get_devdata(spi->master);
++      struct spi_transfer *xfer;
++      int ret;
++      unsigned long flags;
++
++      if (unlikely(list_empty(&msg->transfers)))
++              return -EINVAL;
++
++      if (bs->stopping)
++              return -ESHUTDOWN;
++
++      list_for_each_entry(xfer, &msg->transfers, transfer_list) {
++              if (!(xfer->tx_buf || xfer->rx_buf) && xfer->len) {
++                      dev_dbg(&spi->dev, "missing rx or tx buf\n");
++                      return -EINVAL;
++              }
++
++              if (!xfer->bits_per_word || xfer->speed_hz)
++                      continue;
++
++              ret = bcm2708_setup_state(spi->master, &spi->dev, NULL,
++                      xfer->speed_hz ? xfer->speed_hz : spi->max_speed_hz,
++                      spi->chip_select, spi->mode,
++                      xfer->bits_per_word ? xfer->bits_per_word :
++                              spi->bits_per_word);
++              if (ret)
++                      return ret;
++      }
++
++      msg->status = -EINPROGRESS;
++      msg->actual_length = 0;
++
++      spin_lock_irqsave(&bs->lock, flags);
++      list_add_tail(&msg->queue, &bs->queue);
++      queue_work(bs->workq, &bs->work);
++      spin_unlock_irqrestore(&bs->lock, flags);
++
++      return 0;
++}
++
++static void bcm2708_spi_cleanup(struct spi_device *spi)
++{
++      if (spi->controller_state) {
++              kfree(spi->controller_state);
++              spi->controller_state = NULL;
++      }
++}
++
++static int bcm2708_spi_probe(struct platform_device *pdev)
++{
++      struct resource *regs;
++      int irq, err = -ENOMEM;
++      struct clk *clk;
++      struct spi_master *master;
++      struct bcm2708_spi *bs;
++
++      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_init_pinmode();
++
++      master = spi_alloc_master(&pdev->dev, sizeof(*bs));
++      if (!master) {
++              dev_err(&pdev->dev, "spi_alloc_master() failed\n");
++              goto out_clk_put;
++      }
++
++      /* the spi->mode bits understood by this driver: */
++      master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS;
++
++      master->bus_num = pdev->id;
++      master->num_chipselect = 3;
++      master->setup = bcm2708_spi_setup;
++      master->transfer = bcm2708_spi_transfer;
++      master->cleanup = bcm2708_spi_cleanup;
++      platform_set_drvdata(pdev, master);
++
++      bs = spi_master_get_devdata(master);
++
++      spin_lock_init(&bs->lock);
++      INIT_LIST_HEAD(&bs->queue);
++      init_completion(&bs->done);
++      INIT_WORK(&bs->work, bcm2708_work);
++
++      bs->base = ioremap(regs->start, resource_size(regs));
++      if (!bs->base) {
++              dev_err(&pdev->dev, "could not remap memory\n");
++              goto out_master_put;
++      }
++
++      bs->workq = create_singlethread_workqueue(dev_name(&pdev->dev));
++      if (!bs->workq) {
++              dev_err(&pdev->dev, "could not create workqueue\n");
++              goto out_iounmap;
++      }
++
++      bs->irq = irq;
++      bs->clk = clk;
++      bs->stopping = false;
++
++      err = request_irq(irq, bcm2708_spi_interrupt, 0, dev_name(&pdev->dev),
++                      master);
++      if (err) {
++              dev_err(&pdev->dev, "could not request IRQ: %d\n", err);
++              goto out_workqueue;
++      }
++
++      /* initialise the hardware */
++      clk_enable(clk);
++      bcm2708_wr(bs, SPI_CS, SPI_CS_REN | SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX);
++
++      err = spi_register_master(master);
++      if (err) {
++              dev_err(&pdev->dev, "could not register SPI master: %d\n", err);
++              goto out_free_irq;
++      }
++
++      dev_info(&pdev->dev, "SPI Controller at 0x%08lx (irq %d)\n",
++              (unsigned long)regs->start, irq);
++
++      return 0;
++
++out_free_irq:
++      free_irq(bs->irq, master);
++out_workqueue:
++      destroy_workqueue(bs->workq);
++out_iounmap:
++      iounmap(bs->base);
++out_master_put:
++      spi_master_put(master);
++out_clk_put:
++      clk_put(clk);
++      return err;
++}
++
++static int bcm2708_spi_remove(struct platform_device *pdev)
++{
++      struct spi_master *master = platform_get_drvdata(pdev);
++      struct bcm2708_spi *bs = spi_master_get_devdata(master);
++
++      /* reset the hardware and block queue progress */
++      spin_lock_irq(&bs->lock);
++      bs->stopping = true;
++      bcm2708_wr(bs, SPI_CS, SPI_CS_CLEAR_RX | SPI_CS_CLEAR_TX);
++      spin_unlock_irq(&bs->lock);
++
++      flush_work(&bs->work);
++
++      clk_disable(bs->clk);
++      clk_put(bs->clk);
++      free_irq(bs->irq, master);
++      iounmap(bs->base);
++
++      spi_unregister_master(master);
++
++      return 0;
++}
++
++static struct platform_driver bcm2708_spi_driver = {
++      .driver         = {
++              .name   = DRV_NAME,
++              .owner  = THIS_MODULE,
++      },
++      .probe          = bcm2708_spi_probe,
++      .remove         = bcm2708_spi_remove,
++};
++
++
++static int __init bcm2708_spi_init(void)
++{
++      return platform_driver_probe(&bcm2708_spi_driver, bcm2708_spi_probe);
++}
++module_init(bcm2708_spi_init);
++
++static void __exit bcm2708_spi_exit(void)
++{
++      platform_driver_unregister(&bcm2708_spi_driver);
++}
++module_exit(bcm2708_spi_exit);
++
++
++//module_platform_driver(bcm2708_spi_driver);
++
++MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2708");
++MODULE_AUTHOR("Chris Boot <bootc@bootc.net>");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:" DRV_NAME);
+-- 
+1.8.3.2
+