diff options
Diffstat (limited to 'target/linux/brcm63xx/patches-3.3/426-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch')
-rw-r--r-- | target/linux/brcm63xx/patches-3.3/426-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch | 481 |
1 files changed, 0 insertions, 481 deletions
diff --git a/target/linux/brcm63xx/patches-3.3/426-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch b/target/linux/brcm63xx/patches-3.3/426-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch deleted file mode 100644 index 739c2c6..0000000 --- a/target/linux/brcm63xx/patches-3.3/426-SPI-MIPS-BCM63XX-Add-HS-SPI-driver.patch +++ /dev/null @@ -1,481 +0,0 @@ -From 4b27423676485d05bcd6fc6f3809164fb8f9d22d Mon Sep 17 00:00:00 2001 -From: Jonas Gorski <jonas.gorski@gmail.com> -Date: Sat, 12 Nov 2011 12:19:55 +0100 -Subject: [PATCH 30/60] SPI: MIPS: BCM63XX: Add HSSPI driver - -Add a driver for the High Speed SPI controller found on newer BCM63XX SoCs. - -Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> ---- - .../include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h | 2 + - drivers/spi/Kconfig | 7 + - drivers/spi/Makefile | 1 + - drivers/spi/spi-bcm63xx-hsspi.c | 427 ++++++++++++++++++++ - 4 files changed, 437 insertions(+), 0 deletions(-) - create mode 100644 drivers/spi/spi-bcm63xx-hsspi.c - ---- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h -+++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_dev_hsspi.h -@@ -17,4 +17,6 @@ struct bcm63xx_hsspi_pdata { - - #define HSSPI_PLL_HZ_6328 133333333 - -+#define HSSPI_BUFFER_LEN 512 -+ - #endif /* BCM63XX_DEV_HSSPI_H */ ---- a/drivers/spi/Kconfig -+++ b/drivers/spi/Kconfig -@@ -100,6 +100,13 @@ config SPI_BCM63XX - help - Enable support for the SPI controller on the Broadcom BCM63xx SoCs. - -+config SPI_BCM63XX_HSSPI -+ tristate "Broadcom BCM63XX HS SPI controller driver" -+ depends on BCM63XX -+ help -+ This enables support for the High Speed SPI controller present on -+ newer Broadcom BCM63XX SoCs. -+ - config SPI_BITBANG - tristate "Utilities for Bitbanging SPI masters" - help ---- a/drivers/spi/Makefile -+++ b/drivers/spi/Makefile -@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o - obj-$(CONFIG_SPI_ATH79) += spi-ath79.o - obj-$(CONFIG_SPI_AU1550) += spi-au1550.o - obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o -+obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o - obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o - obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o - obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o ---- /dev/null -+++ b/drivers/spi/spi-bcm63xx-hsspi.c -@@ -0,0 +1,427 @@ -+/* -+ * Broadcom BCM63XX High Speed SPI Controller driver -+ * -+ * Copyright 2000-2010 Broadcom Corporation -+ * Copyright 2012 Jonas Gorski <jonas.gorski@gmail.com> -+ * -+ * Licensed under the GNU/GPL. See COPYING for details. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/io.h> -+#include <linux/clk.h> -+#include <linux/module.h> -+#include <linux/platform_device.h> -+#include <linux/delay.h> -+#include <linux/dma-mapping.h> -+#include <linux/err.h> -+#include <linux/interrupt.h> -+#include <linux/spi/spi.h> -+#include <linux/workqueue.h> -+ -+#include <bcm63xx_regs.h> -+#include <bcm63xx_dev_hsspi.h> -+ -+#define HSSPI_OP_CODE_SHIFT 13 -+#define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT) -+#define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT) -+#define HSSPI_OP_WRITE (2 << HSSPI_OP_CODE_SHIFT) -+#define HSSPI_OP_READ (3 << HSSPI_OP_CODE_SHIFT) -+ -+#define HSSPI_MAX_PREPEND_LEN 15 -+ -+#define HSSPI_MAX_SYNC_CLOCK 30000000 -+ -+struct bcm63xx_hsspi { -+ struct completion done; -+ struct spi_transfer *curr_trans; -+ -+ struct platform_device *pdev; -+ struct clk *clk; -+ void __iomem *regs; -+ u8 __iomem *fifo; -+ -+ u32 speed_hz; -+ int irq; -+}; -+ -+static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs, int hz, -+ int profile) -+{ -+ u32 reg; -+ -+ reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz)); -+ bcm_hsspi_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg, -+ HSSPI_PROFILE_CLK_CTRL_REG(profile)); -+ -+ reg = bcm_hsspi_readl(HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); -+ if (hz > HSSPI_MAX_SYNC_CLOCK) -+ reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH; -+ else -+ reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH; -+ bcm_hsspi_writel(reg, HSSPI_PROFILE_SIGNAL_CTRL_REG(profile)); -+} -+ -+static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, -+ struct spi_transfer *t1, -+ struct spi_transfer *t2) -+{ -+ struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master); -+ u8 chip_select = spi->chip_select; -+ u16 opcode = 0; -+ int len, prepend_size = 0; -+ -+ init_completion(&bs->done); -+ -+ bs->curr_trans = t2 ? t2 : t1; -+ bcm63xx_hsspi_set_clk(bs, bs->curr_trans->speed_hz, chip_select); -+ -+ if (t2 && !t2->tx_buf) -+ prepend_size = t1->len; -+ -+ bcm_hsspi_writel(prepend_size << MODE_CTRL_PREPENDBYTE_CNT_SHIFT | -+ 2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT | -+ 2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff, -+ HSSPI_PROFILE_MODE_CTRL_REG(chip_select)); -+ -+ if (t1->rx_buf && t1->tx_buf) -+ opcode = HSSPI_OP_READ_WRITE; -+ else if (t1->rx_buf || (t2 && t2->rx_buf)) -+ opcode = HSSPI_OP_READ; -+ else if (t1->tx_buf) -+ opcode = HSSPI_OP_WRITE; -+ -+ if (opcode == HSSPI_OP_READ && t2) -+ len = t2->len; -+ else -+ len = t1->len; -+ -+ if (t1->tx_buf) { -+ memcpy_toio(bs->fifo + 2, t1->tx_buf, t1->len); -+ if (t2 && t2->tx_buf) { -+ memcpy_toio(bs->fifo + 2 + t1->len, -+ t2->tx_buf, t2->len); -+ len += t2->len; -+ } -+ } -+ -+ opcode |= len; -+ memcpy_toio(bs->fifo, &opcode, sizeof(opcode)); -+ -+ /* enable interrupt */ -+ bcm_hsspi_writel(HSSPI_PING0_CMD_DONE, HSSPI_INT_MASK_REG); -+ -+ /* start the transfer */ -+ bcm_hsspi_writel(chip_select << PINGPONG_CMD_SS_SHIFT | -+ chip_select << PINGPONG_CMD_PROFILE_SHIFT | -+ PINGPONG_COMMAND_START_NOW, -+ HSSPI_PINGPONG_COMMAND_REG(0)); -+ -+ if (wait_for_completion_timeout(&bs->done, HZ) == 0) { -+ dev_err(&bs->pdev->dev, "transfer timed out!\n"); -+ return -ETIMEDOUT; -+ } -+ -+ return t1->len + (t2 ? t2->len : 0); -+} -+ -+static int bcm63xx_hsspi_setup(struct spi_device *spi) -+{ -+ u32 reg; -+ -+ if (spi->bits_per_word != 8) -+ return -EINVAL; -+ -+ if (spi->max_speed_hz == 0) -+ return -EINVAL; -+ -+ reg = bcm_hsspi_readl(HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select)); -+ reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING); -+ if (spi->mode & SPI_CPHA) -+ reg |= SIGNAL_CTRL_LAUNCH_RISING; -+ else -+ reg |= SIGNAL_CTRL_LATCH_RISING; -+ bcm_hsspi_writel(reg, HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select)); -+ -+ return 0; -+} -+ -+static int bcm63xx_hsspi_transfer_one(struct spi_master *master, -+ struct spi_message *msg) -+{ -+ struct spi_transfer *t, *prev = NULL; -+ struct spi_device *spi = msg->spi; -+ u32 reg; -+ int ret = -EINVAL; -+ int len = 0; -+ -+ /* check if we are able to make these transfers */ -+ list_for_each_entry(t, &msg->transfers, transfer_list) { -+ if (!t->tx_buf && !t->rx_buf) -+ goto out; -+ -+ if (t->speed_hz == 0) -+ t->speed_hz = spi->max_speed_hz; -+ -+ if (t->speed_hz > spi->max_speed_hz) -+ goto out; -+ -+ if (t->len > HSSPI_BUFFER_LEN) -+ goto out; -+ -+ /* -+ * This controller does not support keeping the chip select -+ * active between transfers. -+ * This logic currently supports combining: -+ * write then read with no cs_change (e.g. m25p80 RDSR) -+ * write then write with no cs_change (e.g. m25p80 PP) -+ */ -+ if (prev && prev->tx_buf && !prev->cs_change && !t->cs_change) { -+ /* -+ * reject if we have to combine two tx transfers and -+ * their combined length is bigger than the buffer -+ */ -+ if (prev->tx_buf && t->tx_buf && -+ (prev->len + t->len) > HSSPI_BUFFER_LEN) -+ goto out; -+ /* -+ * reject if we need write more than 15 bytes in read -+ * then write. -+ */ -+ if (prev->tx_buf && t->rx_buf && -+ prev->len > HSSPI_MAX_PREPEND_LEN) -+ goto out; -+ } -+ -+ } -+ -+ /* setup clock polarity */ -+ reg = bcm_hsspi_readl(HSSPI_GLOBAL_CTRL_REG); -+ reg &= ~GLOBAL_CTRL_CLK_POLARITY; -+ if (spi->mode & SPI_CPOL) -+ reg |= GLOBAL_CTRL_CLK_POLARITY; -+ bcm_hsspi_writel(reg, HSSPI_GLOBAL_CTRL_REG); -+ -+ list_for_each_entry(t, &msg->transfers, transfer_list) { -+ if (prev && prev->tx_buf && !prev->cs_change && !t->cs_change) { -+ /* combine write with following transfer */ -+ ret = bcm63xx_hsspi_do_txrx(msg->spi, prev, t); -+ if (ret < 0) -+ goto out; -+ -+ len += ret; -+ prev = NULL; -+ continue; -+ } -+ -+ /* write the previous pending transfer */ -+ if (prev != NULL) { -+ ret = bcm63xx_hsspi_do_txrx(msg->spi, prev, NULL); -+ if (ret < 0) -+ goto out; -+ -+ len += ret; -+ } -+ -+ prev = t; -+ } -+ -+ /* do last pending transfer */ -+ if (prev != NULL) { -+ ret = bcm63xx_hsspi_do_txrx(msg->spi, prev, NULL); -+ if (ret < 0) -+ goto out; -+ len += ret; -+ } -+ -+ msg->actual_length = len; -+ ret = 0; -+out: -+ msg->status = ret; -+ spi_finalize_current_message(master); -+ return 0; -+} -+ -+static irqreturn_t bcm63xx_hsspi_interrupt(int irq, void *dev_id) -+{ -+ struct spi_master *master = (struct spi_master *)dev_id; -+ struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); -+ -+ if (bcm_hsspi_readl(HSSPI_INT_STATUS_MASKED_REG) == 0) -+ return IRQ_NONE; -+ -+ bcm_hsspi_writel(HSSPI_INT_CLEAR_ALL, HSSPI_INT_STATUS_REG); -+ bcm_hsspi_writel(0, HSSPI_INT_MASK_REG); -+ -+ if (bs->curr_trans && bs->curr_trans->rx_buf) -+ memcpy_fromio(bs->curr_trans->rx_buf, bs->fifo, -+ bs->curr_trans->len); -+ complete(&bs->done); -+ -+ return IRQ_HANDLED; -+} -+ -+static int __devinit bcm63xx_hsspi_probe(struct platform_device *pdev) -+{ -+ -+ struct spi_master *master; -+ struct bcm63xx_hsspi *bs; -+ struct resource *res_mem; -+ void __iomem *regs; -+ struct device *dev = &pdev->dev; -+ struct bcm63xx_hsspi_pdata *pdata = pdev->dev.platform_data; -+ struct clk *clk; -+ int irq; -+ int ret; -+ -+ irq = platform_get_irq(pdev, 0); -+ if (irq < 0) { -+ dev_err(dev, "no irq\n"); -+ return -ENXIO; -+ } -+ -+ res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -+ regs = devm_request_and_ioremap(dev, res_mem); -+ if (!regs) { -+ dev_err(dev, "unable to ioremap regs\n"); -+ return -ENXIO; -+ } -+ -+ clk = clk_get(dev, "hsspi"); -+ -+ if (IS_ERR(clk)) { -+ ret = PTR_ERR(clk); -+ goto out_release; -+ } -+ -+ clk_prepare_enable(clk); -+ -+ master = spi_alloc_master(&pdev->dev, sizeof(*bs)); -+ if (!master) { -+ ret = -ENOMEM; -+ goto out_disable_clk; -+ } -+ -+ bs = spi_master_get_devdata(master); -+ bs->pdev = pdev; -+ bs->clk = clk; -+ bs->regs = regs; -+ -+ master->bus_num = pdata->bus_num; -+ master->num_chipselect = 8; -+ master->setup = bcm63xx_hsspi_setup; -+ master->transfer_one_message = bcm63xx_hsspi_transfer_one; -+ master->mode_bits = SPI_CPOL | SPI_CPHA; -+ -+ bs->speed_hz = pdata->speed_hz; -+ bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0)); -+ -+ platform_set_drvdata(pdev, master); -+ -+ bs->curr_trans = NULL; -+ -+ /* Initialize the hardware */ -+ bcm_hsspi_writel(0, HSSPI_INT_MASK_REG); -+ -+ /* clean up any pending interrupts */ -+ bcm_hsspi_writel(HSSPI_INT_CLEAR_ALL, HSSPI_INT_STATUS_REG); -+ -+ bcm_hsspi_writel(bcm_hsspi_readl(HSSPI_GLOBAL_CTRL_REG) | -+ GLOBAL_CTRL_CLK_GATE_SSOFF, -+ HSSPI_GLOBAL_CTRL_REG); -+ -+ ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED, -+ pdev->name, master); -+ -+ if (ret) -+ goto out_put_master; -+ -+ /* register and we are done */ -+ ret = spi_register_master(master); -+ if (ret) -+ goto out_free_irq; -+ -+ return 0; -+ -+out_free_irq: -+ devm_free_irq(dev, bs->irq, master); -+out_put_master: -+ spi_master_put(master); -+out_disable_clk: -+ clk_disable_unprepare(clk); -+ clk_put(clk); -+out_release: -+ devm_ioremap_release(dev, regs); -+ -+ return ret; -+} -+ -+ -+static int __exit bcm63xx_hsspi_remove(struct platform_device *pdev) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); -+ -+ spi_unregister_master(master); -+ -+ /* reset the hardware and block queue progress */ -+ bcm_hsspi_writel(0, HSSPI_INT_MASK_REG); -+ clk_disable_unprepare(bs->clk); -+ clk_put(bs->clk); -+ -+ return 0; -+} -+ -+#ifdef CONFIG_PM -+static int bcm63xx_hsspi_suspend(struct platform_device *pdev, -+ pm_message_t mesg) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); -+ -+ spi_master_suspend(master); -+ clk_disable(bs->clk); -+ -+ return 0; -+} -+ -+static int bcm63xx_hsspi_resume(struct platform_device *pdev) -+{ -+ struct spi_master *master = platform_get_drvdata(pdev); -+ struct bcm63xx_hsspi *bs = spi_master_get_devdata(master); -+ -+ clk_enable(bs->clk); -+ spi_master_resume(master); -+ -+ return 0; -+} -+ -+static const struct dev_pm_ops bcm63xx_hsspi_pm_ops = { -+ .suspend = bcm63xx_hsspi_suspend, -+ .resume = bcm63xx_hsspi_resume, -+}; -+ -+#define BCM63XX_HSSPI_PM_OPS (&bcm63xx_hsspi_pm_ops) -+#else -+#define BCM63XX_HSSPI_PM_OPS NULL -+#endif -+ -+ -+ -+static struct platform_driver bcm63xx_hsspi_driver = { -+ .driver = { -+ .name = "bcm63xx-hsspi", -+ .owner = THIS_MODULE, -+ .pm = BCM63XX_HSSPI_PM_OPS, -+ }, -+ .probe = bcm63xx_hsspi_probe, -+ .remove = __exit_p(bcm63xx_hsspi_remove), -+}; -+ -+module_platform_driver(bcm63xx_hsspi_driver); -+ -+MODULE_ALIAS("platform:bcm63xx_hsspi"); -+MODULE_DESCRIPTION("Broadcom BCM63xx HS SPI Controller driver"); -+MODULE_AUTHOR("Jonas Gorski <jonas.gorski@gmail.com>"); -+MODULE_LICENSE("GPL"); |