diff --git a/target/linux/brcm63xx/patches-2.6.32/240-spi.patch b/target/linux/brcm63xx/patches-2.6.32/240-spi.patch new file mode 100644 index 0000000000..f68f25a85d --- /dev/null +++ b/target/linux/brcm63xx/patches-2.6.32/240-spi.patch @@ -0,0 +1,910 @@ +--- a/arch/mips/bcm63xx/cpu.c ++++ b/arch/mips/bcm63xx/cpu.c +@@ -55,6 +55,7 @@ static const unsigned long bcm96338_regs + + static const int bcm96338_irqs[] = { + [IRQ_TIMER] = BCM_6338_TIMER_IRQ, ++ [IRQ_SPI] = BCM_6338_SPI_IRQ, + [IRQ_UART0] = BCM_6338_UART0_IRQ, + [IRQ_DSL] = BCM_6338_DSL_IRQ, + [IRQ_ENET0] = BCM_6338_ENET0_IRQ, +@@ -127,6 +128,7 @@ static const unsigned long bcm96348_regs + + static const int bcm96348_irqs[] = { + [IRQ_TIMER] = BCM_6348_TIMER_IRQ, ++ [IRQ_SPI] = BCM_6348_SPI_IRQ, + [IRQ_UART0] = BCM_6348_UART0_IRQ, + [IRQ_DSL] = BCM_6348_DSL_IRQ, + [IRQ_ENET0] = BCM_6348_ENET0_IRQ, +@@ -169,6 +171,7 @@ static const unsigned long bcm96358_regs + + static const int bcm96358_irqs[] = { + [IRQ_TIMER] = BCM_6358_TIMER_IRQ, ++ [IRQ_SPI] = BCM_6358_SPI_IRQ, + [IRQ_UART0] = BCM_6358_UART0_IRQ, + [IRQ_DSL] = BCM_6358_DSL_IRQ, + [IRQ_ENET0] = BCM_6358_ENET0_IRQ, +--- /dev/null ++++ b/arch/mips/bcm63xx/dev-spi.c +@@ -0,0 +1,60 @@ ++/* ++ * 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 ++ * for more details. ++ * ++ * Copyright (C) 2009 Florian Fainelli ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static struct resource spi_resources[] = { ++ { ++ .start = -1, /* filled at runtime */ ++ .end = -1, /* filled at runtime */ ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = -1, /* filled at runtime */ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct bcm63xx_spi_pdata spi_pdata = { ++ .bus_num = 0, ++ .num_chipselect = 4, ++ .speed_hz = 50000000, /* Fclk */ ++}; ++ ++static struct platform_device bcm63xx_spi_device = { ++ .name = "bcm63xx-spi", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(spi_resources), ++ .resource = spi_resources, ++ .dev = { ++ .platform_data = &spi_pdata, ++ }, ++}; ++ ++int __init bcm63xx_spi_register(void) ++{ ++ spi_resources[0].start = bcm63xx_regset_address(RSET_SPI); ++ spi_resources[0].end = spi_resources[0].start; ++ spi_resources[0].end += RSET_SPI_SIZE - 1; ++ spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI); ++ ++ /* Fill in platform data */ ++ if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) ++ spi_pdata.fifo_size = SPI_BCM_6338_SPI_MSG_DATA_SIZE; ++ ++ if (BCMCPU_IS_6358()) ++ spi_pdata.fifo_size = SPI_BCM_6358_SPI_MSG_DATA_SIZE; ++ ++ return platform_device_register(&bcm63xx_spi_device); ++} +--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h ++++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h +@@ -428,6 +428,7 @@ static inline unsigned long bcm63xx_regs + */ + enum bcm63xx_irq { + IRQ_TIMER = 0, ++ IRQ_SPI, + IRQ_UART0, + IRQ_DSL, + IRQ_UDC0, +@@ -493,6 +494,7 @@ enum bcm63xx_irq { + * 6348 irqs + */ + #define BCM_6348_TIMER_IRQ (IRQ_INTERNAL_BASE + 0) ++#define BCM_6348_SPI_IRQ (IRQ_INTERNAL_BASE + 1) + #define BCM_6348_UART0_IRQ (IRQ_INTERNAL_BASE + 2) + #define BCM_6348_DSL_IRQ (IRQ_INTERNAL_BASE + 4) + #define BCM_6348_UDC0_IRQ (IRQ_INTERNAL_BASE + 6) +@@ -517,6 +519,7 @@ enum bcm63xx_irq { + * 6358 irqs + */ + #define BCM_6358_TIMER_IRQ (IRQ_INTERNAL_BASE + 0) ++#define BCM_6358_SPI_IRQ (IRQ_INTERNAL_BASE + 1) + #define BCM_6358_UART0_IRQ (IRQ_INTERNAL_BASE + 2) + #define BCM_6358_OHCI0_IRQ (IRQ_INTERNAL_BASE + 5) + #define BCM_6358_ENET1_IRQ (IRQ_INTERNAL_BASE + 6) +--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h ++++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h +@@ -769,5 +769,117 @@ + #define DMIPSPLLCFG_N2_SHIFT 29 + #define DMIPSPLLCFG_N2_MASK (0x7 << DMIPSPLLCFG_N2_SHIFT) + ++/************************************************************************* ++ * _REG relative to RSET_SPI ++ *************************************************************************/ ++ ++/* BCM 6338 SPI core */ ++#define SPI_BCM_6338_SPI_CMD 0x00 /* 16-bits register */ ++#define SPI_BCM_6338_SPI_INT_STATUS 0x02 ++#define SPI_BCM_6338_SPI_MASK_INT_ST 0x03 ++#define SPI_BCM_6338_SPI_INT_MASK 0x04 ++#define SPI_BCM_6338_SPI_ST 0x05 ++#define SPI_BCM_6338_SPI_CLK_CFG 0x06 ++#define SPI_BCM_6338_SPI_FILL_BYTE 0x07 ++#define SPI_BCM_6338_SPI_MSG_TAIL 0x09 ++#define SPI_BCM_6338_SPI_RX_TAIL 0x0b ++#define SPI_BCM_6338_SPI_MSG_CTL 0x40 ++#define SPI_BCM_6338_SPI_MSG_DATA 0x41 ++#define SPI_BCM_6338_SPI_MSG_DATA_SIZE 0x3f ++#define SPI_BCM_6338_SPI_RX_DATA 0x80 ++#define SPI_BCM_6338_SPI_RX_DATA_SIZE 0x3f ++ ++/* BCM 6348 SPI core */ ++#define SPI_BCM_6348_SPI_MASK_INT_ST 0x00 ++#define SPI_BCM_6348_SPI_INT_STATUS 0x01 ++#define SPI_BCM_6348_SPI_CMD 0x02 /* 16-bits register */ ++#define SPI_BCM_6348_SPI_FILL_BYTE 0x04 ++#define SPI_BCM_6348_SPI_CLK_CFG 0x05 ++#define SPI_BCM_6348_SPI_ST 0x06 ++#define SPI_BCM_6348_SPI_INT_MASK 0x07 ++#define SPI_BCM_6348_SPI_RX_TAIL 0x08 ++#define SPI_BCM_6348_SPI_MSG_TAIL 0x10 ++#define SPI_BCM_6348_SPI_MSG_DATA 0x40 ++#define SPI_BCM_6348_SPI_MSG_CTL 0x42 ++#define SPI_BCM_6348_SPI_MSG_DATA_SIZE 0x3f ++#define SPI_BCM_6348_SPI_RX_DATA 0x80 ++#define SPI_BCM_6348_SPI_RX_DATA_SIZE 0x3f ++ ++/* BCM 6358 SPI core */ ++#define SPI_BCM_6358_MSG_CTL 0x00 /* 16-bits register */ ++ ++#define SPI_BCM_6358_SPI_MSG_DATA 0x02 ++#define SPI_BCM_6358_SPI_MSG_DATA_SIZE 0x21e ++ ++#define SPI_BCM_6358_SPI_RX_DATA 0x400 ++#define SPI_BCM_6358_SPI_RX_DATA_SIZE 0x220 ++ ++#define SPI_BCM_6358_SPI_CMD 0x700 /* 16-bits register */ ++ ++#define SPI_BCM_6358_SPI_INT_STATUS 0x702 ++#define SPI_BCM_6358_SPI_MASK_INT_ST 0x703 ++ ++#define SPI_BCM_6358_SPI_INT_MASK 0x704 ++ ++#define SPI_BCM_6358_SPI_STATUS 0x705 ++ ++#define SPI_BCM_6358_SPI_CLK_CFG 0x706 ++ ++#define SPI_BCM_6358_SPI_FILL_BYTE 0x707 ++#define SPI_BCM_6358_SPI_MSG_TAIL 0x709 ++#define SPI_BCM_6358_SPI_RX_TAIL 0x70B ++ ++/* Shared SPI definitions */ ++ ++/* Message configuration */ ++#define SPI_FD_RW 0x00 ++#define SPI_HD_W 0x01 ++#define SPI_HD_R 0x02 ++#define SPI_BYTE_CNT_SHIFT 0 ++#define SPI_MSG_TYPE_SHIFT 14 ++ ++/* Command */ ++#define SPI_CMD_NOOP 0x01 ++#define SPI_CMD_SOFT_RESET 0x02 ++#define SPI_CMD_HARD_RESET 0x04 ++#define SPI_CMD_START_IMMEDIATE 0x08 ++#define SPI_CMD_COMMAND_SHIFT 0 ++#define SPI_CMD_COMMAND_MASK 0x000f ++#define SPI_CMD_DEVICE_ID_SHIFT 4 ++#define SPI_CMD_PREPEND_BYTE_CNT_SHIFT 8 ++#define SPI_CMD_ONE_BYTE_SHIFT 11 ++#define SPI_CMD_ONE_WIRE_SHIFT 12 ++#define SPI_DEV_ID_0 0 ++#define SPI_DEV_ID_1 1 ++#define SPI_DEV_ID_2 2 ++#define SPI_DEV_ID_3 3 ++ ++/* Interrupt mask */ ++#define SPI_INTR_CMD_DONE 0x01 ++#define SPI_INTR_RX_OVERFLOW 0x02 ++#define SPI_INTR_TX_UNDERFLOW 0x04 ++#define SPI_INTR_TX_OVERFLOW 0x08 ++#define SPI_INTR_RX_UNDERFLOW 0x10 ++#define SPI_INTR_CLEAR_ALL 0x1f ++ ++/* Status */ ++#define SPI_RX_EMPTY 0x02 ++#define SPI_CMD_BUSY 0x04 ++#define SPI_SERIAL_BUSY 0x08 ++ ++/* Clock configuration */ ++#define SPI_CLK_20MHZ 0x00 ++#define SPI_CLK_0_391MHZ 0x01 ++#define SPI_CLK_0_781MHZ 0x02 /* default */ ++#define SPI_CLK_1_563MHZ 0x03 ++#define SPI_CLK_3_125MHZ 0x04 ++#define SPI_CLK_6_250MHZ 0x05 ++#define SPI_CLK_12_50MHZ 0x06 ++#define SPI_CLK_25MHZ 0x07 ++#define SPI_CLK_MASK 0x07 ++#define SPI_SSOFFTIME_MASK 0x38 ++#define SPI_SSOFFTIME_SHIFT 3 ++#define SPI_BYTE_SWAP 0x80 ++ + #endif /* BCM63XX_REGS_H_ */ + +--- /dev/null ++++ b/drivers/spi/bcm63xx_spi.c +@@ -0,0 +1,628 @@ ++/* ++ * Broadcom BCM63xx SPI controller support ++ * ++ * Copyright (C) 2009 Florian Fainelli ++ * ++ * 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., 51 Franklin Street, Fifth Floor, ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define PFX KBUILD_MODNAME ++#define DRV_VER "0.1.2" ++ ++enum bcm63xx_regs_spi { ++ SPI_CMD, ++ SPI_INT_STATUS, ++ SPI_INT_MASK_ST, ++ SPI_INT_MASK, ++ SPI_ST, ++ SPI_CLK_CFG, ++ SPI_FILL_BYTE, ++ SPI_MSG_TAIL, ++ SPI_RX_TAIL, ++ SPI_MSG_CTL, ++ SPI_MSG_DATA, ++ 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 ++ return bcm63xx_regs_spi[reg]; ++#else ++#ifdef CONFIG_BCM63XX_CPU_6338 ++switch (reg) { ++ case SPI_CMD: ++ return SPI_BCM_6338_SPI_CMD; ++ case SPI_INT_STATUS: ++ return SPI_BCM_6338_SPI_INT_STATUS; ++ case SPI_INT_MASK_ST: ++ return SPI_BCM_6338_SPI_MASK_INT_ST; ++ case SPI_INT_MASK: ++ return SPI_BCM_6338_SPI_INT_MASK; ++ case SPI_ST: ++ return SPI_BCM_6338_SPI_ST; ++ case SPI_CLK_CFG: ++ return SPI_BCM_6338_SPI_CLK_CFG; ++ case SPI_FILL_BYTE: ++ return SPI_BCM_6338_SPI_FILL_BYTE; ++ case SPI_MSG_TAIL: ++ return SPI_BCM_6338_SPI_MSG_TAIL; ++ case SPI_RX_TAIL: ++ return SPI_BCM_6338_SPI_RX_TAIL; ++ case SPI_MSG_CTL: ++ return SPI_BCM_6338_SPI_MSG_CTL; ++ case SPI_MSG_DATA: ++ return SPI_BCM_6338_SPI_MSG_DATA; ++ case SPI_RX_DATA: ++ return SPI_BCM_6338_SPI_RX_DATA; ++} ++#endif ++#ifdef CONFIG_BCM63XX_CPU_6348 ++switch (reg) { ++ case SPI_CMD: ++ return SPI_BCM_6348_SPI_CMD; ++ case SPI_INT_MASK_ST: ++ return SPI_BCM_6348_SPI_MASK_INT_ST; ++ case SPI_INT_MASK: ++ return SPI_BCM_6348_SPI_INT_MASK; ++ case SPI_INT_STATUS: ++ return SPI_BCM_6348_SPI_INT_STATUS; ++ case SPI_ST: ++ return SPI_BCM_6348_SPI_ST; ++ case SPI_CLK_CFG: ++ return SPI_BCM_6348_SPI_CLK_CFG; ++ case SPI_FILL_BYTE: ++ return SPI_BCM_6348_SPI_FILL_BYTE; ++ case SPI_MSG_TAIL: ++ return SPI_BCM_6348_SPI_MSG_TAIL; ++ case SPI_RX_TAIL: ++ return SPI_BCM_6348_SPI_RX_TAIL; ++ case SPI_MSG_CTL: ++ return SPI_BCM_6348_SPI_MSG_CTL; ++ case SPI_MSG_DATA: ++ return SPI_BCM_6348_SPI_MSG_DATA; ++ case SPI_RX_DATA: ++ return SPI_BCM_6348_SPI_RX_DATA; ++} ++#endif ++#ifdef CONFIG_BCM63XX_CPU_6358 ++switch (reg) { ++ case SPI_CMD: ++ return SPI_BCM_6358_SPI_CMD; ++ case SPI_INT_STATUS: ++ return SPI_BCM_6358_SPI_INT_STATUS; ++ case SPI_INT_MASK_ST: ++ return SPI_BCM_6358_SPI_MASK_INT_ST; ++ case SPI_INT_MASK: ++ return SPI_BCM_6358_SPI_INT_MASK; ++ case SPI_ST: ++ return SPI_BCM_6358_SPI_STATUS; ++ case SPI_CLK_CFG: ++ return SPI_BCM_6358_SPI_CLK_CFG; ++ case SPI_FILL_BYTE: ++ return SPI_BCM_6358_SPI_FILL_BYTE; ++ case SPI_MSG_TAIL: ++ return SPI_BCM_6358_SPI_MSG_TAIL; ++ case SPI_RX_TAIL: ++ return SPI_BCM_6358_SPI_RX_TAIL; ++ case SPI_MSG_CTL: ++ return SPI_BCM_6358_MSG_CTL; ++ case SPI_MSG_DATA: ++ return SPI_BCM_6358_SPI_MSG_DATA; ++ case SPI_RX_DATA: ++ return SPI_BCM_6358_SPI_RX_DATA; ++} ++#endif ++#endif ++ return 0; ++} ++ ++/* ++ * 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 |= (1 << 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_readb(bs->regs, SPI_CMD); ++ cmd |= SPI_CMD_START_IMMEDIATE; ++ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); ++ bcm_spi_writeb(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_readb(bs->regs, SPI_CMD); ++ cmd |= SPI_CMD_START_IMMEDIATE; ++ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); ++ bcm_spi_writeb(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 +@@ -34,6 +34,7 @@ obj-$(CONFIG_SPI_TXX9) += spi_txx9.o + obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o + obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o + obj-$(CONFIG_SPI_STMP3XXX) += spi_stmp.o ++obj-$(CONFIG_SPI_BCM63XX) += bcm63xx_spi.o + # ... add above this line ... + + # SPI protocol drivers (device/link on bus) +--- /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; ++}; ++ ++#endif /* BCM63XX_DEV_SPI_H */ diff --git a/target/linux/brcm63xx/patches-2.6.33/240-spi.patch b/target/linux/brcm63xx/patches-2.6.33/240-spi.patch new file mode 100644 index 0000000000..beef46dd1b --- /dev/null +++ b/target/linux/brcm63xx/patches-2.6.33/240-spi.patch @@ -0,0 +1,910 @@ +--- a/arch/mips/bcm63xx/cpu.c ++++ b/arch/mips/bcm63xx/cpu.c +@@ -55,6 +55,7 @@ static const unsigned long bcm96338_regs + + static const int bcm96338_irqs[] = { + [IRQ_TIMER] = BCM_6338_TIMER_IRQ, ++ [IRQ_SPI] = BCM_6338_SPI_IRQ, + [IRQ_UART0] = BCM_6338_UART0_IRQ, + [IRQ_DSL] = BCM_6338_DSL_IRQ, + [IRQ_ENET0] = BCM_6338_ENET0_IRQ, +@@ -127,6 +128,7 @@ static const unsigned long bcm96348_regs + + static const int bcm96348_irqs[] = { + [IRQ_TIMER] = BCM_6348_TIMER_IRQ, ++ [IRQ_SPI] = BCM_6348_SPI_IRQ, + [IRQ_UART0] = BCM_6348_UART0_IRQ, + [IRQ_DSL] = BCM_6348_DSL_IRQ, + [IRQ_ENET0] = BCM_6348_ENET0_IRQ, +@@ -169,6 +171,7 @@ static const unsigned long bcm96358_regs + + static const int bcm96358_irqs[] = { + [IRQ_TIMER] = BCM_6358_TIMER_IRQ, ++ [IRQ_SPI] = BCM_6358_SPI_IRQ, + [IRQ_UART0] = BCM_6358_UART0_IRQ, + [IRQ_DSL] = BCM_6358_DSL_IRQ, + [IRQ_ENET0] = BCM_6358_ENET0_IRQ, +--- /dev/null ++++ b/arch/mips/bcm63xx/dev-spi.c +@@ -0,0 +1,60 @@ ++/* ++ * 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 ++ * for more details. ++ * ++ * Copyright (C) 2009 Florian Fainelli ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++static struct resource spi_resources[] = { ++ { ++ .start = -1, /* filled at runtime */ ++ .end = -1, /* filled at runtime */ ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = -1, /* filled at runtime */ ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct bcm63xx_spi_pdata spi_pdata = { ++ .bus_num = 0, ++ .num_chipselect = 4, ++ .speed_hz = 50000000, /* Fclk */ ++}; ++ ++static struct platform_device bcm63xx_spi_device = { ++ .name = "bcm63xx-spi", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(spi_resources), ++ .resource = spi_resources, ++ .dev = { ++ .platform_data = &spi_pdata, ++ }, ++}; ++ ++int __init bcm63xx_spi_register(void) ++{ ++ spi_resources[0].start = bcm63xx_regset_address(RSET_SPI); ++ spi_resources[0].end = spi_resources[0].start; ++ spi_resources[0].end += RSET_SPI_SIZE - 1; ++ spi_resources[1].start = bcm63xx_get_irq_number(IRQ_SPI); ++ ++ /* Fill in platform data */ ++ if (BCMCPU_IS_6338() || BCMCPU_IS_6348()) ++ spi_pdata.fifo_size = SPI_BCM_6338_SPI_MSG_DATA_SIZE; ++ ++ if (BCMCPU_IS_6358()) ++ spi_pdata.fifo_size = SPI_BCM_6358_SPI_MSG_DATA_SIZE; ++ ++ return platform_device_register(&bcm63xx_spi_device); ++} +--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h ++++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_cpu.h +@@ -428,6 +428,7 @@ static inline unsigned long bcm63xx_regs + */ + enum bcm63xx_irq { + IRQ_TIMER = 0, ++ IRQ_SPI, + IRQ_UART0, + IRQ_DSL, + IRQ_UDC0, +@@ -493,6 +494,7 @@ enum bcm63xx_irq { + * 6348 irqs + */ + #define BCM_6348_TIMER_IRQ (IRQ_INTERNAL_BASE + 0) ++#define BCM_6348_SPI_IRQ (IRQ_INTERNAL_BASE + 1) + #define BCM_6348_UART0_IRQ (IRQ_INTERNAL_BASE + 2) + #define BCM_6348_DSL_IRQ (IRQ_INTERNAL_BASE + 4) + #define BCM_6348_UDC0_IRQ (IRQ_INTERNAL_BASE + 6) +@@ -517,6 +519,7 @@ enum bcm63xx_irq { + * 6358 irqs + */ + #define BCM_6358_TIMER_IRQ (IRQ_INTERNAL_BASE + 0) ++#define BCM_6358_SPI_IRQ (IRQ_INTERNAL_BASE + 1) + #define BCM_6358_UART0_IRQ (IRQ_INTERNAL_BASE + 2) + #define BCM_6358_OHCI0_IRQ (IRQ_INTERNAL_BASE + 5) + #define BCM_6358_ENET1_IRQ (IRQ_INTERNAL_BASE + 6) +--- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h ++++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h +@@ -769,5 +769,117 @@ + #define DMIPSPLLCFG_N2_SHIFT 29 + #define DMIPSPLLCFG_N2_MASK (0x7 << DMIPSPLLCFG_N2_SHIFT) + ++/************************************************************************* ++ * _REG relative to RSET_SPI ++ *************************************************************************/ ++ ++/* BCM 6338 SPI core */ ++#define SPI_BCM_6338_SPI_CMD 0x00 /* 16-bits register */ ++#define SPI_BCM_6338_SPI_INT_STATUS 0x02 ++#define SPI_BCM_6338_SPI_MASK_INT_ST 0x03 ++#define SPI_BCM_6338_SPI_INT_MASK 0x04 ++#define SPI_BCM_6338_SPI_ST 0x05 ++#define SPI_BCM_6338_SPI_CLK_CFG 0x06 ++#define SPI_BCM_6338_SPI_FILL_BYTE 0x07 ++#define SPI_BCM_6338_SPI_MSG_TAIL 0x09 ++#define SPI_BCM_6338_SPI_RX_TAIL 0x0b ++#define SPI_BCM_6338_SPI_MSG_CTL 0x40 ++#define SPI_BCM_6338_SPI_MSG_DATA 0x41 ++#define SPI_BCM_6338_SPI_MSG_DATA_SIZE 0x3f ++#define SPI_BCM_6338_SPI_RX_DATA 0x80 ++#define SPI_BCM_6338_SPI_RX_DATA_SIZE 0x3f ++ ++/* BCM 6348 SPI core */ ++#define SPI_BCM_6348_SPI_MASK_INT_ST 0x00 ++#define SPI_BCM_6348_SPI_INT_STATUS 0x01 ++#define SPI_BCM_6348_SPI_CMD 0x02 /* 16-bits register */ ++#define SPI_BCM_6348_SPI_FILL_BYTE 0x04 ++#define SPI_BCM_6348_SPI_CLK_CFG 0x05 ++#define SPI_BCM_6348_SPI_ST 0x06 ++#define SPI_BCM_6348_SPI_INT_MASK 0x07 ++#define SPI_BCM_6348_SPI_RX_TAIL 0x08 ++#define SPI_BCM_6348_SPI_MSG_TAIL 0x10 ++#define SPI_BCM_6348_SPI_MSG_DATA 0x40 ++#define SPI_BCM_6348_SPI_MSG_CTL 0x42 ++#define SPI_BCM_6348_SPI_MSG_DATA_SIZE 0x3f ++#define SPI_BCM_6348_SPI_RX_DATA 0x80 ++#define SPI_BCM_6348_SPI_RX_DATA_SIZE 0x3f ++ ++/* BCM 6358 SPI core */ ++#define SPI_BCM_6358_MSG_CTL 0x00 /* 16-bits register */ ++ ++#define SPI_BCM_6358_SPI_MSG_DATA 0x02 ++#define SPI_BCM_6358_SPI_MSG_DATA_SIZE 0x21e ++ ++#define SPI_BCM_6358_SPI_RX_DATA 0x400 ++#define SPI_BCM_6358_SPI_RX_DATA_SIZE 0x220 ++ ++#define SPI_BCM_6358_SPI_CMD 0x700 /* 16-bits register */ ++ ++#define SPI_BCM_6358_SPI_INT_STATUS 0x702 ++#define SPI_BCM_6358_SPI_MASK_INT_ST 0x703 ++ ++#define SPI_BCM_6358_SPI_INT_MASK 0x704 ++ ++#define SPI_BCM_6358_SPI_STATUS 0x705 ++ ++#define SPI_BCM_6358_SPI_CLK_CFG 0x706 ++ ++#define SPI_BCM_6358_SPI_FILL_BYTE 0x707 ++#define SPI_BCM_6358_SPI_MSG_TAIL 0x709 ++#define SPI_BCM_6358_SPI_RX_TAIL 0x70B ++ ++/* Shared SPI definitions */ ++ ++/* Message configuration */ ++#define SPI_FD_RW 0x00 ++#define SPI_HD_W 0x01 ++#define SPI_HD_R 0x02 ++#define SPI_BYTE_CNT_SHIFT 0 ++#define SPI_MSG_TYPE_SHIFT 14 ++ ++/* Command */ ++#define SPI_CMD_NOOP 0x01 ++#define SPI_CMD_SOFT_RESET 0x02 ++#define SPI_CMD_HARD_RESET 0x04 ++#define SPI_CMD_START_IMMEDIATE 0x08 ++#define SPI_CMD_COMMAND_SHIFT 0 ++#define SPI_CMD_COMMAND_MASK 0x000f ++#define SPI_CMD_DEVICE_ID_SHIFT 4 ++#define SPI_CMD_PREPEND_BYTE_CNT_SHIFT 8 ++#define SPI_CMD_ONE_BYTE_SHIFT 11 ++#define SPI_CMD_ONE_WIRE_SHIFT 12 ++#define SPI_DEV_ID_0 0 ++#define SPI_DEV_ID_1 1 ++#define SPI_DEV_ID_2 2 ++#define SPI_DEV_ID_3 3 ++ ++/* Interrupt mask */ ++#define SPI_INTR_CMD_DONE 0x01 ++#define SPI_INTR_RX_OVERFLOW 0x02 ++#define SPI_INTR_TX_UNDERFLOW 0x04 ++#define SPI_INTR_TX_OVERFLOW 0x08 ++#define SPI_INTR_RX_UNDERFLOW 0x10 ++#define SPI_INTR_CLEAR_ALL 0x1f ++ ++/* Status */ ++#define SPI_RX_EMPTY 0x02 ++#define SPI_CMD_BUSY 0x04 ++#define SPI_SERIAL_BUSY 0x08 ++ ++/* Clock configuration */ ++#define SPI_CLK_20MHZ 0x00 ++#define SPI_CLK_0_391MHZ 0x01 ++#define SPI_CLK_0_781MHZ 0x02 /* default */ ++#define SPI_CLK_1_563MHZ 0x03 ++#define SPI_CLK_3_125MHZ 0x04 ++#define SPI_CLK_6_250MHZ 0x05 ++#define SPI_CLK_12_50MHZ 0x06 ++#define SPI_CLK_25MHZ 0x07 ++#define SPI_CLK_MASK 0x07 ++#define SPI_SSOFFTIME_MASK 0x38 ++#define SPI_SSOFFTIME_SHIFT 3 ++#define SPI_BYTE_SWAP 0x80 ++ + #endif /* BCM63XX_REGS_H_ */ + +--- /dev/null ++++ b/drivers/spi/bcm63xx_spi.c +@@ -0,0 +1,628 @@ ++/* ++ * Broadcom BCM63xx SPI controller support ++ * ++ * Copyright (C) 2009 Florian Fainelli ++ * ++ * 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., 51 Franklin Street, Fifth Floor, ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#define PFX KBUILD_MODNAME ++#define DRV_VER "0.1.2" ++ ++enum bcm63xx_regs_spi { ++ SPI_CMD, ++ SPI_INT_STATUS, ++ SPI_INT_MASK_ST, ++ SPI_INT_MASK, ++ SPI_ST, ++ SPI_CLK_CFG, ++ SPI_FILL_BYTE, ++ SPI_MSG_TAIL, ++ SPI_RX_TAIL, ++ SPI_MSG_CTL, ++ SPI_MSG_DATA, ++ 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 ++ return bcm63xx_regs_spi[reg]; ++#else ++#ifdef CONFIG_BCM63XX_CPU_6338 ++switch (reg) { ++ case SPI_CMD: ++ return SPI_BCM_6338_SPI_CMD; ++ case SPI_INT_STATUS: ++ return SPI_BCM_6338_SPI_INT_STATUS; ++ case SPI_INT_MASK_ST: ++ return SPI_BCM_6338_SPI_MASK_INT_ST; ++ case SPI_INT_MASK: ++ return SPI_BCM_6338_SPI_INT_MASK; ++ case SPI_ST: ++ return SPI_BCM_6338_SPI_ST; ++ case SPI_CLK_CFG: ++ return SPI_BCM_6338_SPI_CLK_CFG; ++ case SPI_FILL_BYTE: ++ return SPI_BCM_6338_SPI_FILL_BYTE; ++ case SPI_MSG_TAIL: ++ return SPI_BCM_6338_SPI_MSG_TAIL; ++ case SPI_RX_TAIL: ++ return SPI_BCM_6338_SPI_RX_TAIL; ++ case SPI_MSG_CTL: ++ return SPI_BCM_6338_SPI_MSG_CTL; ++ case SPI_MSG_DATA: ++ return SPI_BCM_6338_SPI_MSG_DATA; ++ case SPI_RX_DATA: ++ return SPI_BCM_6338_SPI_RX_DATA; ++} ++#endif ++#ifdef CONFIG_BCM63XX_CPU_6348 ++switch (reg) { ++ case SPI_CMD: ++ return SPI_BCM_6348_SPI_CMD; ++ case SPI_INT_MASK_ST: ++ return SPI_BCM_6348_SPI_MASK_INT_ST; ++ case SPI_INT_MASK: ++ return SPI_BCM_6348_SPI_INT_MASK; ++ case SPI_INT_STATUS: ++ return SPI_BCM_6348_SPI_INT_STATUS; ++ case SPI_ST: ++ return SPI_BCM_6348_SPI_ST; ++ case SPI_CLK_CFG: ++ return SPI_BCM_6348_SPI_CLK_CFG; ++ case SPI_FILL_BYTE: ++ return SPI_BCM_6348_SPI_FILL_BYTE; ++ case SPI_MSG_TAIL: ++ return SPI_BCM_6348_SPI_MSG_TAIL; ++ case SPI_RX_TAIL: ++ return SPI_BCM_6348_SPI_RX_TAIL; ++ case SPI_MSG_CTL: ++ return SPI_BCM_6348_SPI_MSG_CTL; ++ case SPI_MSG_DATA: ++ return SPI_BCM_6348_SPI_MSG_DATA; ++ case SPI_RX_DATA: ++ return SPI_BCM_6348_SPI_RX_DATA; ++} ++#endif ++#ifdef CONFIG_BCM63XX_CPU_6358 ++switch (reg) { ++ case SPI_CMD: ++ return SPI_BCM_6358_SPI_CMD; ++ case SPI_INT_STATUS: ++ return SPI_BCM_6358_SPI_INT_STATUS; ++ case SPI_INT_MASK_ST: ++ return SPI_BCM_6358_SPI_MASK_INT_ST; ++ case SPI_INT_MASK: ++ return SPI_BCM_6358_SPI_INT_MASK; ++ case SPI_ST: ++ return SPI_BCM_6358_SPI_STATUS; ++ case SPI_CLK_CFG: ++ return SPI_BCM_6358_SPI_CLK_CFG; ++ case SPI_FILL_BYTE: ++ return SPI_BCM_6358_SPI_FILL_BYTE; ++ case SPI_MSG_TAIL: ++ return SPI_BCM_6358_SPI_MSG_TAIL; ++ case SPI_RX_TAIL: ++ return SPI_BCM_6358_SPI_RX_TAIL; ++ case SPI_MSG_CTL: ++ return SPI_BCM_6358_MSG_CTL; ++ case SPI_MSG_DATA: ++ return SPI_BCM_6358_SPI_MSG_DATA; ++ case SPI_RX_DATA: ++ return SPI_BCM_6358_SPI_RX_DATA; ++} ++#endif ++#endif ++ return 0; ++} ++ ++/* ++ * 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 |= (1 << 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_readb(bs->regs, SPI_CMD); ++ cmd |= SPI_CMD_START_IMMEDIATE; ++ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); ++ bcm_spi_writeb(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_readb(bs->regs, SPI_CMD); ++ cmd |= SPI_CMD_START_IMMEDIATE; ++ cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT); ++ bcm_spi_writeb(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 +@@ -43,6 +43,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; ++}; ++ ++#endif /* BCM63XX_DEV_SPI_H */