diff --git a/target/linux/brcm63xx/patches-2.6.35/240-spi.patch b/target/linux/brcm63xx/patches-2.6.35/240-spi.patch index 835c0df2bc..4661486bb1 100644 --- a/target/linux/brcm63xx/patches-2.6.35/240-spi.patch +++ b/target/linux/brcm63xx/patches-2.6.35/240-spi.patch @@ -26,7 +26,7 @@ [IRQ_DSL] = BCM_6358_DSL_IRQ, --- /dev/null +++ b/arch/mips/bcm63xx/dev-spi.c -@@ -0,0 +1,60 @@ +@@ -0,0 +1,127 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive @@ -43,6 +43,71 @@ +#include +#include + ++#ifdef BCMCPU_RUNTIME_DETECT ++/* ++ * register offsets ++ */ ++static const unsigned long bcm96338_regs_spi[] = { ++ [SPI_CMD] = SPI_BCM_6338_SPI_CMD, ++ [SPI_INT_STATUS] = SPI_BCM_6338_SPI_INT_STATUS, ++ [SPI_INT_MASK_ST] = SPI_BCM_6338_SPI_MASK_INT_ST, ++ [SPI_INT_MASK] = SPI_BCM_6338_SPI_INT_MASK, ++ [SPI_ST] = SPI_BCM_6338_SPI_ST, ++ [SPI_CLK_CFG] = SPI_BCM_6338_SPI_CLK_CFG, ++ [SPI_FILL_BYTE] = SPI_BCM_6338_SPI_FILL_BYTE, ++ [SPI_MSG_TAIL] = SPI_BCM_6338_SPI_MSG_TAIL, ++ [SPI_RX_TAIL] = SPI_BCM_6338_SPI_RX_TAIL, ++ [SPI_MSG_CTL] = SPI_BCM_6338_SPI_MSG_CTL, ++ [SPI_MSG_DATA] = SPI_BCM_6338_SPI_MSG_DATA, ++ [SPI_RX_DATA] = SPI_BCM_6338_SPI_RX_DATA, ++}; ++ ++static const unsigned long bcm96348_regs_spi[] = { ++ [SPI_CMD] = SPI_BCM_6348_SPI_CMD, ++ [SPI_INT_STATUS] = SPI_BCM_6348_SPI_INT_STATUS, ++ [SPI_INT_MASK_ST] = SPI_BCM_6348_SPI_MASK_INT_ST, ++ [SPI_INT_MASK] = SPI_BCM_6348_SPI_INT_MASK, ++ [SPI_ST] = SPI_BCM_6348_SPI_ST, ++ [SPI_CLK_CFG] = SPI_BCM_6348_SPI_CLK_CFG, ++ [SPI_FILL_BYTE] = SPI_BCM_6348_SPI_FILL_BYTE, ++ [SPI_MSG_TAIL] = SPI_BCM_6348_SPI_MSG_TAIL, ++ [SPI_RX_TAIL] = SPI_BCM_6348_SPI_RX_TAIL, ++ [SPI_MSG_CTL] = SPI_BCM_6348_SPI_MSG_CTL, ++ [SPI_MSG_DATA] = SPI_BCM_6348_SPI_MSG_DATA, ++ [SPI_RX_DATA] = SPI_BCM_6348_SPI_RX_DATA, ++}; ++ ++static const unsigned long bcm96358_regs_spi[] = { ++ [SPI_CMD] = SPI_BCM_6358_SPI_CMD, ++ [SPI_INT_STATUS] = SPI_BCM_6358_SPI_INT_STATUS, ++ [SPI_INT_MASK_ST] = SPI_BCM_6358_SPI_MASK_INT_ST, ++ [SPI_INT_MASK] = SPI_BCM_6358_SPI_INT_MASK, ++ [SPI_ST] = SPI_BCM_6358_SPI_STATUS, ++ [SPI_CLK_CFG] = SPI_BCM_6358_SPI_CLK_CFG, ++ [SPI_FILL_BYTE] = SPI_BCM_6358_SPI_FILL_BYTE, ++ [SPI_MSG_TAIL] = SPI_BCM_6358_SPI_MSG_TAIL, ++ [SPI_RX_TAIL] = SPI_BCM_6358_SPI_RX_TAIL, ++ [SPI_MSG_CTL] = SPI_BCM_6358_MSG_CTL, ++ [SPI_MSG_DATA] = SPI_BCM_6358_SPI_MSG_DATA, ++ [SPI_RX_DATA] = SPI_BCM_6358_SPI_RX_DATA, ++}; ++ ++const unsigned long *bcm63xx_regs_spi; ++EXPORT_SYMBOL(bcm63xx_regs_spi); ++ ++static __init void bcm63xx_spi_regs_init(void) ++{ ++ if (BCMCPU_IS_6338()) ++ bcm63xx_regs_spi = bcm96338_regs_spi; ++ if (BCMCPU_IS_6348()) ++ bcm63xx_regs_spi = bcm96348_regs_spi; ++ if (BCMCPU_IS_6358()) ++ bcm63xx_regs_spi = bcm96358_regs_spi; ++} ++#else ++static __init void bcm63xx_spi_regs_init(void) { } ++#endif ++ +static struct resource spi_resources[] = { + { + .start = -1, /* filled at runtime */ @@ -57,7 +122,7 @@ + +static struct bcm63xx_spi_pdata spi_pdata = { + .bus_num = 0, -+ .num_chipselect = 4, ++ .num_chipselect = 8, + .speed_hz = 50000000, /* Fclk */ +}; + @@ -85,6 +150,8 @@ + if (BCMCPU_IS_6358()) + spi_pdata.fifo_size = SPI_BCM_6358_SPI_MSG_DATA_SIZE; + ++ bcm63xx_spi_regs_init(); ++ + return platform_device_register(&bcm63xx_spi_device); +} --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h @@ -251,7 +318,7 @@ #endif /* BCM63XX_REGS_H_ */ --- /dev/null +++ b/drivers/spi/bcm63xx_spi.c -@@ -0,0 +1,628 @@ +@@ -0,0 +1,479 @@ +/* + * Broadcom BCM63xx SPI controller support + * @@ -280,18 +347,501 @@ +#include +#include +#include -+#include -+#include +#include +#include + -+#include -+#include +#include + +#define PFX KBUILD_MODNAME +#define DRV_VER "0.1.2" + ++struct bcm63xx_spi { ++ spinlock_t lock; ++ int stopping; ++ struct completion done; ++ ++ void __iomem *regs; ++ int irq; ++ ++ /* Platform data */ ++ u32 speed_hz; ++ unsigned fifo_size; ++ ++ /* Data buffers */ ++ const unsigned char *tx_ptr; ++ unsigned char *rx_ptr; ++ int remaining_bytes; ++ ++ struct clk *clk; ++ struct platform_device *pdev; ++}; ++ ++static int bcm63xx_spi_setup_transfer(struct spi_device *spi, ++ struct spi_transfer *t) ++{ ++ u8 bits_per_word; ++ u8 clk_cfg; ++ u32 hz; ++ unsigned int div; ++ ++ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); ++ ++ bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; ++ hz = (t) ? t->speed_hz : spi->max_speed_hz; ++ if (bits_per_word != 8) { ++ dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", ++ __func__, bits_per_word); ++ return -EINVAL; ++ } ++ ++ if (spi->chip_select > spi->master->num_chipselect) { ++ dev_err(&spi->dev, "%s, unsupported slave %d\n", ++ __func__, spi->chip_select); ++ return -EINVAL; ++ } ++ ++ /* Check clock setting */ ++ div = (bs->speed_hz / hz); ++ switch (div) { ++ case 2: ++ clk_cfg = SPI_CLK_25MHZ; ++ break; ++ case 4: ++ clk_cfg = SPI_CLK_12_50MHZ; ++ break; ++ case 8: ++ clk_cfg = SPI_CLK_6_250MHZ; ++ break; ++ case 16: ++ clk_cfg = SPI_CLK_3_125MHZ; ++ break; ++ case 32: ++ clk_cfg = SPI_CLK_1_563MHZ; ++ break; ++ case 128: ++ clk_cfg = SPI_CLK_0_781MHZ; ++ break; ++ case 64: ++ default: ++ /* Set to slowest mode for compatibility */ ++ clk_cfg = SPI_CLK_0_781MHZ; ++ break; ++ } ++ ++ bcm_spi_writeb(clk_cfg, SPI_CLK_CFG); ++ dev_dbg(&spi->dev, "Setting clock register to %d (hz %d, cmd %02x)\n", ++ div, hz, clk_cfg); ++ ++ return 0; ++} ++ ++/* the spi->mode bits understood by this driver: */ ++#define MODEBITS (SPI_CPOL | SPI_CPHA) ++ ++static int bcm63xx_spi_setup(struct spi_device *spi) ++{ ++ struct bcm63xx_spi *bs; ++ int retval; ++ ++ bs = spi_master_get_devdata(spi->master); ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ if (!spi->bits_per_word) ++ spi->bits_per_word = 8; ++ ++ if (spi->mode & ~MODEBITS) { ++ dev_err(&spi->dev, "%s, unsupported mode bits %x\n", ++ __func__, spi->mode & ~MODEBITS); ++ return -EINVAL; ++ } ++ ++ retval = bcm63xx_spi_setup_transfer(spi, NULL); ++ if (retval < 0) { ++ dev_err(&spi->dev, "setup: unsupported mode bits %x\n", ++ spi->mode & ~MODEBITS); ++ return retval; ++ } ++ ++ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", ++ __func__, spi->mode & MODEBITS, spi->bits_per_word, 0); ++ ++ return 0; ++} ++ ++/* Fill the TX FIFO with as many bytes as possible */ ++static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) ++{ ++ u8 tail; ++ ++ /* Fill the Tx FIFO with as many bytes as possible */ ++ tail = bcm_spi_readb(SPI_MSG_TAIL); ++ ++ while ((tail < bs->fifo_size) && (bs->remaining_bytes > 0)) { ++ if (bs->tx_ptr) ++ bcm_spi_writeb(*bs->tx_ptr++, SPI_MSG_DATA); ++ else ++ bcm_spi_writeb(0, SPI_MSG_DATA); ++ ++ bs->remaining_bytes--; ++ tail = bcm_spi_readb(SPI_MSG_TAIL); ++ } ++} ++ ++static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) ++{ ++ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); ++ u16 msg_ctl; ++ u16 cmd; ++ ++ dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", ++ t->tx_buf, t->rx_buf, t->len); ++ ++ /* Transmitter is inhibited */ ++ bs->tx_ptr = t->tx_buf; ++ bs->rx_ptr = t->rx_buf; ++ init_completion(&bs->done); ++ ++ if (t->tx_buf) { ++ bs->remaining_bytes = t->len; ++ bcm63xx_spi_fill_tx_fifo(bs); ++ } ++ ++ /* Enable the command done interrupt which ++ * we use to determine completion of a command */ ++ bcm_spi_writeb(SPI_INTR_CMD_DONE, SPI_INT_MASK); ++ ++ /* Fill in the Message control register */ ++ msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT); ++ ++ if (t->rx_buf && t->tx_buf) ++ msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT); ++ else if (t->rx_buf) ++ msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT); ++ else if (t->tx_buf) ++ msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT); ++ ++ bcm_spi_writew(msg_ctl, SPI_MSG_CTL); ++ ++ /* Issue the transfer */ ++ cmd = SPI_CMD_START_IMMEDIATE; ++ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); ++ bcm_spi_writew(cmd, SPI_CMD); ++ wait_for_completion(&bs->done); ++ ++ /* Disable the CMD_DONE interrupt */ ++ bcm_spi_writeb(0, SPI_INT_MASK); ++ ++ return t->len - bs->remaining_bytes; ++} ++ ++static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *msg) ++{ ++ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); ++ struct spi_transfer *xfer; ++ int ret = 0; ++ ++ if (unlikely(list_empty(&msg->transfers))) ++ return -EINVAL; ++ ++ if (bs->stopping) ++ return -ESHUTDOWN; ++ ++ list_for_each_entry(xfer, &msg->transfers, transfer_list) { ++ ret += bcm63xx_txrx_bufs(spi, xfer); ++ } ++ ++ msg->complete(msg->context); ++ ++ return ret; ++} ++ ++/* This driver supports single master mode only. Hence ++ * CMD_DONE is the only interrupt we care about ++ */ ++static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) ++{ ++ struct spi_master *master = (struct spi_master *)dev_id; ++ struct bcm63xx_spi *bs = spi_master_get_devdata(master); ++ u8 intr; ++ u16 cmd; ++ ++ /* Read interupts and clear them immediately */ ++ intr = bcm_spi_readb(SPI_INT_STATUS); ++ bcm_spi_writeb(SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); ++ bcm_spi_writeb(0, SPI_INT_MASK); ++ ++ /* A tansfer completed */ ++ if (intr & SPI_INTR_CMD_DONE) { ++ u8 rx_tail; ++ ++ rx_tail = bcm_spi_readb(SPI_RX_TAIL); ++ ++ /* Read out all the data */ ++ if (rx_tail) { ++ u8 data; ++ u8 i = 0; ++ ++ for(i = 0; i < rx_tail; i++) { ++ data = bcm_spi_readb(SPI_RX_DATA); ++ if (bs->rx_ptr) ++ *bs->rx_ptr++ = data; ++ } ++ } ++ ++ /* See if there is more data to send */ ++ if (bs->remaining_bytes > 0) { ++ bcm63xx_spi_fill_tx_fifo(bs); ++ ++ /* Start the transfer */ ++ bcm_spi_writew(SPI_HD_W << SPI_MSG_TYPE_SHIFT, ++ SPI_MSG_CTL); ++ cmd = bcm_spi_readw(SPI_CMD); ++ cmd |= SPI_CMD_START_IMMEDIATE; ++ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); ++ bcm_spi_writeb(SPI_INTR_CMD_DONE, SPI_INT_MASK); ++ bcm_spi_writew(cmd, SPI_CMD); ++ } else { ++ complete(&bs->done); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++ ++static int __init bcm63xx_spi_probe(struct platform_device *pdev) ++{ ++ struct resource *r; ++ struct device *dev = &pdev->dev; ++ struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; ++ int irq; ++ struct spi_master *master; ++ struct clk *clk; ++ struct bcm63xx_spi *bs; ++ int ret; ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!r) { ++ dev_err(dev, "no iomem\n"); ++ ret = -ENXIO; ++ goto out; ++ } ++ ++ irq = platform_get_irq(pdev, 0); ++ if (irq < 0) { ++ dev_err(dev, "no irq\n"); ++ ret = -ENXIO; ++ goto out; ++ } ++ ++ clk = clk_get(&pdev->dev, "spi"); ++ if (IS_ERR(clk)) { ++ dev_err(dev, "no clock for device\n"); ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(struct bcm63xx_spi)); ++ if (!master) { ++ dev_err(dev, "out of memory\n"); ++ ret = -ENOMEM; ++ goto out_free; ++ } ++ ++ bs = spi_master_get_devdata(master); ++ init_completion(&bs->done); ++ ++ platform_set_drvdata(pdev, master); ++ bs->pdev = pdev; ++ ++ if (!request_mem_region(r->start, ++ r->end - r->start, PFX)) { ++ dev_err(dev, "iomem request failed\n"); ++ ret = -ENXIO; ++ goto out_put_master; ++ } ++ ++ bs->regs = ioremap_nocache(r->start, r->end - r->start); ++ if (!bs->regs) { ++ dev_err(dev, "unable to ioremap regs\n"); ++ ret = -ENOMEM; ++ goto out_put_master; ++ } ++ bs->irq = irq; ++ bs->clk = clk; ++ bs->fifo_size = pdata->fifo_size; ++ ++ ret = request_irq(irq, bcm63xx_spi_interrupt, 0, ++ pdev->name, master); ++ if (ret) { ++ dev_err(dev, "unable to request irq\n"); ++ goto out_unmap; ++ } ++ ++ master->bus_num = pdata->bus_num; ++ master->num_chipselect = pdata->num_chipselect; ++ master->setup = bcm63xx_spi_setup; ++ master->transfer = bcm63xx_transfer; ++ bs->speed_hz = pdata->speed_hz; ++ bs->stopping = 0; ++ spin_lock_init(&bs->lock); ++ ++ /* Initialize hardware */ ++ clk_enable(bs->clk); ++ bcm_spi_writeb(SPI_INTR_CLEAR_ALL, SPI_INT_STATUS); ++ ++ /* register and we are done */ ++ ret = spi_register_master(master); ++ if (ret) { ++ dev_err(dev, "spi register failed\n"); ++ goto out_reset_hw; ++ } ++ ++ dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n", ++ r->start, irq, bs->fifo_size, DRV_VER); ++ ++ return ret; ++ ++out_reset_hw: ++ clk_disable(clk); ++ free_irq(irq, master); ++out_unmap: ++ iounmap(bs->regs); ++out_put_master: ++ spi_master_put(master); ++out_free: ++ clk_put(clk); ++out: ++ return ret; ++} ++ ++static int __exit bcm63xx_spi_remove(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct bcm63xx_spi *bs = spi_master_get_devdata(master); ++ struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ /* reset spi block */ ++ bcm_spi_writeb(0, SPI_INT_MASK); ++ spin_lock(&bs->lock); ++ bs->stopping = 1; ++ ++ /* HW shutdown */ ++ clk_disable(bs->clk); ++ clk_put(bs->clk); ++ ++ spin_unlock(&bs->lock); ++ ++ free_irq(bs->irq, master); ++ iounmap(bs->regs); ++ release_mem_region(r->start, r->end - r->start); ++ platform_set_drvdata(pdev, 0); ++ spi_unregister_master(master); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int bcm63xx_spi_suspend(struct platform_device *pdev, pm_message_t mesg) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct bcm63xx_spi *bs = spi_master_get_devdata(master); ++ ++ clk_disable(bs->clk); ++ ++ return 0; ++} ++ ++static int bcm63xx_spi_resume(struct platform_device *pdev) ++{ ++ struct spi_master *master = platform_get_drvdata(pdev); ++ struct bcm63xx_spi *bs = spi_master_get_devdata(master); ++ ++ clk_enable(bs->clk); ++ ++ return 0; ++} ++#else ++#define bcm63xx_spi_suspend NULL ++#define bcm63xx_spi_resume NULL ++#endif ++ ++static struct platform_driver bcm63xx_spi_driver = { ++ .driver = { ++ .name = "bcm63xx-spi", ++ .owner = THIS_MODULE, ++ }, ++ .probe = bcm63xx_spi_probe, ++ .remove = bcm63xx_spi_remove, ++ .suspend = bcm63xx_spi_suspend, ++ .resume = bcm63xx_spi_resume, ++}; ++ ++ ++static int __init bcm63xx_spi_init(void) ++{ ++ return platform_driver_register(&bcm63xx_spi_driver); ++} ++ ++static void __exit bcm63xx_spi_exit(void) ++{ ++ platform_driver_unregister(&bcm63xx_spi_driver); ++} ++ ++module_init(bcm63xx_spi_init); ++module_exit(bcm63xx_spi_exit); ++ ++MODULE_ALIAS("platform:bcm63xx_spi"); ++MODULE_AUTHOR("Florian Fainelli "); ++MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VER); +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -60,6 +60,12 @@ config SPI_ATMEL + This selects a driver for the Atmel SPI Controller, present on + many AT32 (AVR32) and AT91 (ARM) chips. + ++config SPI_BCM63XX ++ tristate "Broadcom BCM63xx SPI controller" ++ depends on BCM63XX ++ help ++ This is the SPI controller master driver for Broadcom BCM63xx SoC. ++ + config SPI_BFIN + tristate "SPI controller driver for ADI Blackfin5xx" + depends on BLACKFIN +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -48,6 +48,7 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci. + obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o + obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o + obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o ++obj-$(CONFIG_SPI_BCM63XX) += bcm63xx_spi.o + + # special build for s3c24xx spi driver with fiq support + spi_s3c24xx_hw-y := spi_s3c24xx.o +--- /dev/null ++++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h +@@ -0,0 +1,136 @@ ++#ifndef BCM63XX_DEV_SPI_H ++#define BCM63XX_DEV_SPI_H ++ ++#include ++#include ++#include ++ ++int __init bcm63xx_spi_register(void); ++ ++struct bcm63xx_spi_pdata { ++ unsigned int fifo_size; ++ int bus_num; ++ int num_chipselect; ++ u32 speed_hz; ++}; ++ +enum bcm63xx_regs_spi { + SPI_CMD, + SPI_INT_STATUS, @@ -307,74 +857,10 @@ + SPI_RX_DATA, +}; + -+/* -+ * register offsets -+ */ -+static const unsigned long bcm96338_regs_spi[] = { -+ [SPI_CMD] = SPI_BCM_6338_SPI_CMD, -+ [SPI_INT_STATUS] = SPI_BCM_6338_SPI_INT_STATUS, -+ [SPI_INT_MASK_ST] = SPI_BCM_6338_SPI_MASK_INT_ST, -+ [SPI_INT_MASK] = SPI_BCM_6338_SPI_INT_MASK, -+ [SPI_ST] = SPI_BCM_6338_SPI_ST, -+ [SPI_CLK_CFG] = SPI_BCM_6338_SPI_CLK_CFG, -+ [SPI_FILL_BYTE] = SPI_BCM_6338_SPI_FILL_BYTE, -+ [SPI_MSG_TAIL] = SPI_BCM_6338_SPI_MSG_TAIL, -+ [SPI_RX_TAIL] = SPI_BCM_6338_SPI_RX_TAIL, -+ [SPI_MSG_CTL] = SPI_BCM_6338_SPI_MSG_CTL, -+ [SPI_MSG_DATA] = SPI_BCM_6338_SPI_MSG_DATA, -+ [SPI_RX_DATA] = SPI_BCM_6338_SPI_RX_DATA, -+}; -+ -+static const unsigned long bcm96348_regs_spi[] = { -+ [SPI_CMD] = SPI_BCM_6348_SPI_CMD, -+ [SPI_INT_STATUS] = SPI_BCM_6348_SPI_INT_STATUS, -+ [SPI_INT_MASK_ST] = SPI_BCM_6348_SPI_MASK_INT_ST, -+ [SPI_INT_MASK] = SPI_BCM_6348_SPI_INT_MASK, -+ [SPI_ST] = SPI_BCM_6348_SPI_ST, -+ [SPI_CLK_CFG] = SPI_BCM_6348_SPI_CLK_CFG, -+ [SPI_FILL_BYTE] = SPI_BCM_6348_SPI_FILL_BYTE, -+ [SPI_MSG_TAIL] = SPI_BCM_6348_SPI_MSG_TAIL, -+ [SPI_RX_TAIL] = SPI_BCM_6348_SPI_RX_TAIL, -+ [SPI_MSG_CTL] = SPI_BCM_6348_SPI_MSG_CTL, -+ [SPI_MSG_DATA] = SPI_BCM_6348_SPI_MSG_DATA, -+ [SPI_RX_DATA] = SPI_BCM_6348_SPI_RX_DATA, -+}; -+ -+static const unsigned long bcm96358_regs_spi[] = { -+ [SPI_CMD] = SPI_BCM_6358_SPI_CMD, -+ [SPI_INT_STATUS] = SPI_BCM_6358_SPI_INT_STATUS, -+ [SPI_INT_MASK_ST] = SPI_BCM_6358_SPI_MASK_INT_ST, -+ [SPI_INT_MASK] = SPI_BCM_6358_SPI_INT_MASK, -+ [SPI_ST] = SPI_BCM_6358_SPI_STATUS, -+ [SPI_CLK_CFG] = SPI_BCM_6358_SPI_CLK_CFG, -+ [SPI_FILL_BYTE] = SPI_BCM_6358_SPI_FILL_BYTE, -+ [SPI_MSG_TAIL] = SPI_BCM_6358_SPI_MSG_TAIL, -+ [SPI_RX_TAIL] = SPI_BCM_6358_SPI_RX_TAIL, -+ [SPI_MSG_CTL] = SPI_BCM_6358_MSG_CTL, -+ [SPI_MSG_DATA] = SPI_BCM_6358_SPI_MSG_DATA, -+ [SPI_RX_DATA] = SPI_BCM_6358_SPI_RX_DATA, -+}; -+ -+ -+#ifdef BCMCPU_RUNTIME_DETECT -+static const unsigned long *bcm63xx_regs_spi; -+ -+static __init void bcm63xx_spi_regs_init(void) -+{ -+ if (BCMCPU_IS_6338()) -+ bcm63xx_regs_spi = bcm96338_regs_spi; -+ if (BCMCPU_IS_6348()) -+ bcm63xx_regs_spi = bcm96348_regs_spi; -+ if (BCMCPU_IS_6358()) -+ bcm63xx_regs_spi = bcm96358_regs_spi; -+} -+#else -+static __init void bcm63xx_spi_regs_init(void) { } -+#endif -+ +static inline unsigned long bcm63xx_spireg(enum bcm63xx_regs_spi reg) +{ +#ifdef BCMCPU_RUNTIME_DETECT ++ extern const unsigned long *bcm63xx_regs_spi; + return bcm63xx_regs_spi[reg]; +#else +#ifdef CONFIG_BCM63XX_CPU_6338 @@ -468,460 +954,12 @@ +/* + * helpers for the SPI register sets + */ -+#define bcm_spi_readb(b,o) bcm_readb((b) + bcm63xx_spireg(o)) -+#define bcm_spi_readw(b,o) bcm_readw((b) + bcm63xx_spireg(o)) -+#define bcm_spi_writeb(v,b,o) bcm_writeb((v), (b) + bcm63xx_spireg(o)) -+#define bcm_spi_writew(v,b,o) bcm_writew((v), (b) + bcm63xx_spireg(o)) -+ -+struct bcm63xx_spi { -+ /* bitbang has to be first */ -+ struct spi_bitbang bitbang; -+ struct completion done; -+ -+ void __iomem *regs; -+ int irq; -+ -+ /* Platform data */ -+ u32 speed_hz; -+ unsigned fifo_size; -+ -+ /* Data buffers */ -+ const unsigned char *tx_ptr; -+ unsigned char *rx_ptr; -+ int remaining_bytes; -+ -+ struct clk *clk; -+ struct resource *ioarea; -+ struct platform_device *pdev; -+}; -+ -+static void bcm63xx_spi_chipselect(struct spi_device *spi, int is_on) -+{ -+ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); -+ u16 val; -+ -+ val = bcm_spi_readw(bs->regs, SPI_CMD); -+ if (is_on == BITBANG_CS_INACTIVE) -+ val |= SPI_CMD_NOOP; -+ else if (is_on == BITBANG_CS_ACTIVE) -+ val |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT); -+ -+ bcm_spi_writew(val, bs->regs, SPI_CMD); -+} -+ -+static int bcm63xx_spi_setup_transfer(struct spi_device *spi, -+ struct spi_transfer *t) -+{ -+ u8 bits_per_word; -+ u8 clk_cfg; -+ u32 hz; -+ unsigned int div; -+ -+ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); -+ -+ bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; -+ hz = (t) ? t->speed_hz : spi->max_speed_hz; -+ if (bits_per_word != 8) { -+ dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", -+ __func__, bits_per_word); -+ return -EINVAL; -+ } -+ -+ if (spi->chip_select > spi->master->num_chipselect) { -+ dev_err(&spi->dev, "%s, unsupported slave %d\n", -+ __func__, spi->chip_select); -+ return -EINVAL; -+ } -+ -+ /* Check clock setting */ -+ div = (bs->speed_hz / hz); -+ switch (div) { -+ case 2: -+ clk_cfg = SPI_CLK_25MHZ; -+ break; -+ case 4: -+ clk_cfg = SPI_CLK_12_50MHZ; -+ break; -+ case 8: -+ clk_cfg = SPI_CLK_6_250MHZ; -+ break; -+ case 16: -+ clk_cfg = SPI_CLK_3_125MHZ; -+ break; -+ case 32: -+ clk_cfg = SPI_CLK_1_563MHZ; -+ break; -+ case 128: -+ clk_cfg = SPI_CLK_0_781MHZ; -+ break; -+ case 64: -+ default: -+ /* Set to slowest mode for compatibility */ -+ clk_cfg = SPI_CLK_0_781MHZ; -+ break; -+ } -+ -+ bcm_spi_writeb(clk_cfg, bs->regs, SPI_CLK_CFG); -+ dev_dbg(&spi->dev, "Setting clock register to %d (hz %d, cmd %02x)\n", -+ div, hz, clk_cfg); -+ -+ return 0; -+} -+ -+/* the spi->mode bits understood by this driver: */ -+#define MODEBITS (SPI_CPOL | SPI_CPHA) -+ -+static int bcm63xx_spi_setup(struct spi_device *spi) -+{ -+ struct spi_bitbang *bitbang; -+ struct bcm63xx_spi *bs; -+ int retval; -+ -+ bs = spi_master_get_devdata(spi->master); -+ bitbang = &bs->bitbang; -+ -+ if (!spi->bits_per_word) -+ spi->bits_per_word = 8; -+ -+ if (spi->mode & ~MODEBITS) { -+ dev_err(&spi->dev, "%s, unsupported mode bits %x\n", -+ __func__, spi->mode & ~MODEBITS); -+ return -EINVAL; -+ } -+ -+ retval = bcm63xx_spi_setup_transfer(spi, NULL); -+ if (retval < 0) { -+ dev_err(&spi->dev, "setup: unsupported mode bits %x\n", -+ spi->mode & ~MODEBITS); -+ return retval; -+ } -+ -+ dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n", -+ __func__, spi->mode & MODEBITS, spi->bits_per_word, 0); -+ -+ return 0; -+} -+ -+/* Fill the TX FIFO with as many bytes as possible */ -+static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) -+{ -+ u8 tail; -+ -+ /* Fill the Tx FIFO with as many bytes as possible */ -+ tail = bcm_spi_readb(bs->regs, SPI_MSG_TAIL); -+ while ((tail < bs->fifo_size) && (bs->remaining_bytes > 0)) { -+ if (bs->tx_ptr) -+ bcm_spi_writeb(*bs->tx_ptr++, bs->regs, SPI_MSG_DATA); -+ else -+ bcm_spi_writeb(0, bs->regs, SPI_MSG_DATA); -+ bs->remaining_bytes--; -+ tail = bcm_spi_readb(bs->regs, SPI_MSG_TAIL); -+ } -+} -+ -+static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) -+{ -+ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); -+ u8 msg_ctl; -+ u16 cmd; -+ -+ dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", -+ t->tx_buf, t->rx_buf, t->len); -+ -+ /* Transmitter is inhibited */ -+ bs->tx_ptr = t->tx_buf; -+ bs->rx_ptr = t->rx_buf; -+ bs->remaining_bytes = t->len; -+ init_completion(&bs->done); -+ -+ bcm63xx_spi_fill_tx_fifo(bs); -+ -+ /* Enable the command done interrupt which -+ * we use to determine completion of a command */ -+ bcm_spi_writeb(SPI_INTR_CMD_DONE, bs->regs, SPI_INT_MASK); -+ -+ /* Fill in the Message control register */ -+ msg_ctl = bcm_spi_readb(bs->regs, SPI_MSG_CTL); -+ msg_ctl |= (t->len << SPI_BYTE_CNT_SHIFT); -+ msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT); -+ bcm_spi_writeb(msg_ctl, bs->regs, SPI_MSG_CTL); -+ -+ /* Issue the transfer */ -+ cmd = bcm_spi_readw(bs->regs, SPI_CMD); -+ cmd |= SPI_CMD_START_IMMEDIATE; -+ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); -+ bcm_spi_writew(cmd, bs->regs, SPI_CMD); -+ -+ wait_for_completion(&bs->done); -+ -+ /* Disable the CMD_DONE interrupt */ -+ bcm_spi_writeb(~(SPI_INTR_CMD_DONE), bs->regs, SPI_INT_MASK); -+ -+ return t->len - bs->remaining_bytes; -+} -+ -+/* This driver supports single master mode only. Hence -+ * CMD_DONE is the only interrupt we care about -+ */ -+static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) -+{ -+ struct spi_master *master = (struct spi_master *)dev_id; -+ struct bcm63xx_spi *bs = spi_master_get_devdata(master); -+ u8 intr; -+ u16 cmd; -+ -+ /* Read interupts and clear them immediately */ -+ intr = bcm_spi_readb(bs->regs, SPI_INT_STATUS); -+ bcm_spi_writeb(SPI_INTR_CLEAR_ALL, bs->regs, SPI_INT_MASK); -+ -+ /* A tansfer completed */ -+ if (intr & SPI_INTR_CMD_DONE) { -+ u8 rx_empty; -+ -+ rx_empty = bcm_spi_readb(bs->regs, SPI_ST); -+ /* Read out all the data */ -+ while ((rx_empty & SPI_RX_EMPTY) == 0) { -+ u8 data; -+ -+ data = bcm_spi_readb(bs->regs, SPI_RX_DATA); -+ if (bs->rx_ptr) -+ *bs->rx_ptr++ = data; -+ -+ rx_empty = bcm_spi_readb(bs->regs, SPI_RX_EMPTY); -+ } -+ -+ /* See if there is more data to send */ -+ if (bs->remaining_bytes > 0) { -+ bcm63xx_spi_fill_tx_fifo(bs); -+ -+ /* Start the transfer */ -+ cmd = bcm_spi_readw(bs->regs, SPI_CMD); -+ cmd |= SPI_CMD_START_IMMEDIATE; -+ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); -+ bcm_spi_writew(cmd, bs->regs, SPI_CMD); -+ } else -+ complete(&bs->done); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+ -+static int __init bcm63xx_spi_probe(struct platform_device *pdev) -+{ -+ struct resource *r; -+ struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data; -+ int irq; -+ struct spi_master *master; -+ struct clk *clk; -+ struct bcm63xx_spi *bs; -+ int ret; -+ -+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ if (!r) { -+ ret = -ENXIO; -+ goto out; -+ } -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ ret = -ENXIO; -+ goto out; -+ } -+ -+ bcm63xx_spi_regs_init(); -+ -+ clk = clk_get(&pdev->dev, "spi"); -+ if (IS_ERR(clk)) { -+ dev_err(&pdev->dev, "No clock for device\n"); -+ ret = -ENODEV; -+ goto out; -+ } -+ -+ master = spi_alloc_master(&pdev->dev, sizeof(struct bcm63xx_spi)); -+ if (!master) { -+ ret = -ENOMEM; -+ goto out_free; -+ } -+ -+ bs = spi_master_get_devdata(master); -+ bs->bitbang.master = spi_master_get(master); -+ bs->bitbang.chipselect = bcm63xx_spi_chipselect; -+ bs->bitbang.setup_transfer = bcm63xx_spi_setup_transfer; -+ bs->bitbang.txrx_bufs = bcm63xx_txrx_bufs; -+ bs->bitbang.master->setup = bcm63xx_spi_setup; -+ init_completion(&bs->done); -+ -+ platform_set_drvdata(pdev, master); -+ bs->pdev = pdev; -+ -+ if (!request_mem_region(r->start, -+ r->end - r->start, PFX)) { -+ ret = -ENXIO; -+ goto out_free; -+ } -+ -+ bs->regs = ioremap_nocache(r->start, r->end - r->start); -+ if (!bs->regs) { -+ printk(KERN_ERR PFX " unable to ioremap regs\n"); -+ ret = -ENOMEM; -+ goto out_free; -+ } -+ bs->irq = irq; -+ bs->clk = clk; -+ bs->fifo_size = pdata->fifo_size; -+ -+ ret = request_irq(irq, bcm63xx_spi_interrupt, 0, -+ pdev->name, master); -+ if (ret) { -+ printk(KERN_ERR PFX " unable to request irq\n"); -+ goto out_unmap; -+ } -+ -+ master->bus_num = pdata->bus_num; -+ master->num_chipselect = pdata->num_chipselect; -+ bs->speed_hz = pdata->speed_hz; -+ -+ /* Initialize hardware */ -+ clk_enable(bs->clk); -+ bcm_spi_writeb(SPI_INTR_CLEAR_ALL, bs->regs, SPI_INT_MASK); -+ -+ dev_info(&pdev->dev, " at 0x%08x (irq %d, FIFOs size %d) v%s\n", -+ r->start, irq, bs->fifo_size, DRV_VER); -+ -+ ret = spi_bitbang_start(&bs->bitbang); -+ if (ret) { -+ dev_err(&pdev->dev, "spi_bitbang_start FAILED\n"); -+ goto out_reset_hw; -+ } -+ -+ return ret; -+ -+out_reset_hw: -+ clk_disable(clk); -+ free_irq(irq, master); -+out_unmap: -+ iounmap(bs->regs); -+out_free: -+ clk_put(clk); -+ spi_master_put(master); -+out: -+ return ret; -+} -+ -+static int __exit bcm63xx_spi_remove(struct platform_device *pdev) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm63xx_spi *bs = spi_master_get_devdata(master); -+ -+ spi_bitbang_stop(&bs->bitbang); -+ clk_disable(bs->clk); -+ clk_put(bs->clk); -+ free_irq(bs->irq, master); -+ iounmap(bs->regs); -+ platform_set_drvdata(pdev, 0); -+ spi_master_put(bs->bitbang.master); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int bcm63xx_spi_suspend(struct platform_device *pdev, pm_message_t mesg) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm63xx_spi *bs = spi_master_get_devdata(master); -+ -+ clk_disable(bs->clk); -+ -+ return 0; -+} -+ -+static int bcm63xx_spi_resume(struct platform_device *pdev) -+{ -+ struct bcm63xx_spi *bs = spi_master_get_devdata(master); -+ struct bcm63xx_spi *bs = spi_master_get_devdata(master); -+ -+ clk_enable(bs->clk); -+ -+ return 0; -+} -+#else -+#define bcm63xx_spi_suspend NULL -+#define bcm63xx_spi_resume NULL -+#endif -+ -+static struct platform_driver bcm63xx_spi_driver = { -+ .driver = { -+ .name = "bcm63xx-spi", -+ .owner = THIS_MODULE, -+ }, -+ .probe = bcm63xx_spi_probe, -+ .remove = bcm63xx_spi_remove, -+ .suspend = bcm63xx_spi_suspend, -+ .resume = bcm63xx_spi_resume, -+}; -+ -+ -+static int __init bcm63xx_spi_init(void) -+{ -+ return platform_driver_register(&bcm63xx_spi_driver); -+} -+ -+static void __exit bcm63xx_spi_exit(void) -+{ -+ platform_driver_unregister(&bcm63xx_spi_driver); -+} -+ -+module_init(bcm63xx_spi_init); -+module_exit(bcm63xx_spi_exit); -+ -+MODULE_ALIAS("platform:bcm63xx_spi"); -+MODULE_AUTHOR("Florian Fainelli "); -+MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver"); -+MODULE_LICENSE("GPL"); -+MODULE_VERSION(DRV_VER); ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -60,6 +60,13 @@ config SPI_ATMEL - This selects a driver for the Atmel SPI Controller, present on - many AT32 (AVR32) and AT91 (ARM) chips. - -+config SPI_BCM63XX -+ tristate "Broadcom BCM63xx SPI controller" -+ depends on BCM63XX -+ select SPI_BITBANG -+ help -+ This is the SPI controller master driver for Broadcom BCM63xx SoC. -+ - config SPI_BFIN - tristate "SPI controller driver for ADI Blackfin5xx" - depends on BLACKFIN ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -48,6 +48,7 @@ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci. - obj-$(CONFIG_SPI_SH_MSIOF) += spi_sh_msiof.o - obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o - obj-$(CONFIG_SPI_NUC900) += spi_nuc900.o -+obj-$(CONFIG_SPI_BCM63XX) += bcm63xx_spi.o - - # special build for s3c24xx spi driver with fiq support - spi_s3c24xx_hw-y := spi_s3c24xx.o ---- /dev/null -+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_spi.h -@@ -0,0 +1,15 @@ -+#ifndef BCM63XX_DEV_SPI_H -+#define BCM63XX_DEV_SPI_H -+ -+#include -+ -+int __init bcm63xx_spi_register(void); -+ -+struct bcm63xx_spi_pdata { -+ unsigned int fifo_size; -+ int bus_num; -+ int num_chipselect; -+ u32 speed_hz; -+}; ++#define bcm_spi_readb(o) bcm_rset_readb(RSET_SPI, bcm63xx_spireg(o)) ++#define bcm_spi_readw(o) bcm_rset_readw(RSET_SPI, bcm63xx_spireg(o)) ++#define bcm_spi_readl(o) bcm_rset_readl(RSET_SPI, bcm63xx_spireg(o)) ++#define bcm_spi_writeb(v,o) bcm_rset_writeb(RSET_SPI, (v), bcm63xx_spireg(o)) ++#define bcm_spi_writew(v,o) bcm_rset_writew(RSET_SPI, (v), bcm63xx_spireg(o)) ++#define bcm_spi_writel(v,o) bcm_rset_writel(RSET_SPI, (v), bcm63xx_spireg(o)) + +#endif /* BCM63XX_DEV_SPI_H */ --- a/arch/mips/bcm63xx/Makefile @@ -944,13 +982,13 @@ #include #define PFX "board_bcm963xx: " -@@ -1596,6 +1597,9 @@ int __init board_register_devices(void) - if (board.num_spis) - spi_register_board_info(board.spis, board.num_spis); +@@ -1489,6 +1490,9 @@ void __init board_prom_init(void) + + bcm_gpio_writel(val, GPIO_MODE_REG); + if (!BCMCPU_IS_6345()) + bcm63xx_spi_register(); + - /* read base address of boot chip select (0) */ - if (BCMCPU_IS_6345()) - val = 0x1fc00000; + /* Generate MAC address for WLAN and + * register our SPROM */ + #ifdef CONFIG_SSB_PCIHOST