diff --git a/Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt b/Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt new file mode 100644 index 000000000000..79de379f4dc0 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/microchip,spi-pic32.txt @@ -0,0 +1,34 @@ +Microchip PIC32 SPI Master controller + +Required properties: +- compatible: Should be "microchip,pic32mzda-spi". +- reg: Address and length of register space for the device. +- interrupts: Should contain all three spi interrupts in sequence + of , , . +- interrupt-names: Should be "fault", "rx", "tx" in order. +- clocks: Phandle of the clock generating SPI clock on the bus. +- clock-names: Should be "mck0". +- cs-gpios: Specifies the gpio pins to be used for chipselects. + See: Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional properties: +- dmas: Two or more DMA channel specifiers following the convention outlined + in Documentation/devicetree/bindings/dma/dma.txt +- dma-names: Names for the dma channels. There must be at least one channel + named "spi-tx" for transmit and named "spi-rx" for receive. + +Example: + +spi1: spi@1f821000 { + compatible = "microchip,pic32mzda-spi"; + reg = <0x1f821000 0x200>; + interrupts = <109 IRQ_TYPE_LEVEL_HIGH>, + <110 IRQ_TYPE_LEVEL_HIGH>, + <111 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "fault", "rx", "tx"; + clocks = <&PBCLK2>; + clock-names = "mck0"; + cs-gpios = <&gpio3 4 GPIO_ACTIVE_LOW>; + dmas = <&dma 134>, <&dma 135>; + dma-names = "spi-rx", "spi-tx"; +}; diff --git a/Documentation/devicetree/bindings/spi/sqi-pic32.txt b/Documentation/devicetree/bindings/spi/sqi-pic32.txt new file mode 100644 index 000000000000..c82d021bce50 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/sqi-pic32.txt @@ -0,0 +1,18 @@ +Microchip PIC32 Quad SPI controller +----------------------------------- +Required properties: +- compatible: Should be "microchip,pic32mzda-sqi". +- reg: Address and length of SQI controller register space. +- interrupts: Should contain SQI interrupt. +- clocks: Should contain phandle of two clocks in sequence, one that drives + clock on SPI bus and other that drives SQI controller. +- clock-names: Should be "spi_ck" and "reg_ck" in order. + +Example: + sqi1: spi@1f8e2000 { + compatible = "microchip,pic32mzda-sqi"; + reg = <0x1f8e2000 0x200>; + clocks = <&rootclk REF2CLK>, <&rootclk PB5CLK>; + clock-names = "spi_ck", "reg_ck"; + interrupts = <169 IRQ_TYPE_LEVEL_HIGH>; + }; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 28147c8031c6..b189508ccf92 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -431,10 +431,23 @@ config SPI_OMAP_100K config SPI_ORION tristate "Orion SPI master" - depends on PLAT_ORION || COMPILE_TEST + depends on PLAT_ORION || ARCH_MVEBU || COMPILE_TEST help This enables using the SPI master controller on the Orion chips. +config SPI_PIC32 + tristate "Microchip PIC32 series SPI" + depends on MACH_PIC32 || COMPILE_TEST + help + SPI driver for Microchip PIC32 SPI master controller. + +config SPI_PIC32_SQI + tristate "Microchip PIC32 Quad SPI driver" + depends on MACH_PIC32 || COMPILE_TEST + depends on HAS_DMA + help + SPI driver for PIC32 Quad SPI controller. + config SPI_PL022 tristate "ARM AMBA PL022 SSP controller" depends on ARM_AMBA diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index fbb255c5a608..3c74d003535b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -62,6 +62,8 @@ obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o obj-$(CONFIG_SPI_TI_QSPI) += spi-ti-qspi.o obj-$(CONFIG_SPI_ORION) += spi-orion.o +obj-$(CONFIG_SPI_PIC32) += spi-pic32.o +obj-$(CONFIG_SPI_PIC32_SQI) += spi-pic32-sqi.o obj-$(CONFIG_SPI_PL022) += spi-pl022.o obj-$(CONFIG_SPI_PPC4xx) += spi-ppc4xx.o spi-pxa2xx-platform-objs := spi-pxa2xx.o spi-pxa2xx-dma.o diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c index 07e4ce8273df..3b170093989f 100644 --- a/drivers/spi/spi-octeon.c +++ b/drivers/spi/spi-octeon.c @@ -175,6 +175,7 @@ err: static int octeon_spi_probe(struct platform_device *pdev) { struct resource *res_mem; + void __iomem *reg_base; struct spi_master *master; struct octeon_spi *p; int err = -ENOENT; @@ -186,19 +187,13 @@ static int octeon_spi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, master); res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(reg_base)) { + err = PTR_ERR(reg_base); + goto fail; + } - if (res_mem == NULL) { - dev_err(&pdev->dev, "found no memory resource\n"); - err = -ENXIO; - goto fail; - } - if (!devm_request_mem_region(&pdev->dev, res_mem->start, - resource_size(res_mem), res_mem->name)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - goto fail; - } - p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start, - resource_size(res_mem)); + p->register_base = (u64)reg_base; master->num_chipselect = 4; master->mode_bits = SPI_CPHA | diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 0caa3c8bef46..1d237e93a289 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include @@ -103,9 +102,6 @@ struct omap2_mcspi_dma { struct dma_chan *dma_tx; struct dma_chan *dma_rx; - int dma_tx_sync_dev; - int dma_rx_sync_dev; - struct completion dma_tx_completion; struct completion dma_rx_completion; @@ -964,8 +960,7 @@ static int omap2_mcspi_request_dma(struct spi_device *spi) struct spi_master *master = spi->master; struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - dma_cap_mask_t mask; - unsigned sig; + int ret = 0; mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; @@ -973,34 +968,25 @@ static int omap2_mcspi_request_dma(struct spi_device *spi) init_completion(&mcspi_dma->dma_rx_completion); init_completion(&mcspi_dma->dma_tx_completion); - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - sig = mcspi_dma->dma_rx_sync_dev; - - mcspi_dma->dma_rx = - dma_request_slave_channel_compat(mask, omap_dma_filter_fn, - &sig, &master->dev, - mcspi_dma->dma_rx_ch_name); - if (!mcspi_dma->dma_rx) - goto no_dma; - - sig = mcspi_dma->dma_tx_sync_dev; - mcspi_dma->dma_tx = - dma_request_slave_channel_compat(mask, omap_dma_filter_fn, - &sig, &master->dev, - mcspi_dma->dma_tx_ch_name); - - if (!mcspi_dma->dma_tx) { - dma_release_channel(mcspi_dma->dma_rx); + mcspi_dma->dma_rx = dma_request_chan(&master->dev, + mcspi_dma->dma_rx_ch_name); + if (IS_ERR(mcspi_dma->dma_rx)) { + ret = PTR_ERR(mcspi_dma->dma_rx); mcspi_dma->dma_rx = NULL; goto no_dma; } - return 0; + mcspi_dma->dma_tx = dma_request_chan(&master->dev, + mcspi_dma->dma_tx_ch_name); + if (IS_ERR(mcspi_dma->dma_tx)) { + ret = PTR_ERR(mcspi_dma->dma_tx); + mcspi_dma->dma_tx = NULL; + dma_release_channel(mcspi_dma->dma_rx); + mcspi_dma->dma_rx = NULL; + } no_dma: - dev_warn(&spi->dev, "not using DMA for McSPI\n"); - return -EAGAIN; + return ret; } static int omap2_mcspi_setup(struct spi_device *spi) @@ -1039,8 +1025,9 @@ static int omap2_mcspi_setup(struct spi_device *spi) if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) { ret = omap2_mcspi_request_dma(spi); - if (ret < 0 && ret != -EAGAIN) - return ret; + if (ret) + dev_warn(&spi->dev, "not using DMA for McSPI (%d)\n", + ret); } ret = pm_runtime_get_sync(mcspi->dev); @@ -1434,42 +1421,8 @@ static int omap2_mcspi_probe(struct platform_device *pdev) } for (i = 0; i < master->num_chipselect; i++) { - char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name; - char *dma_tx_ch_name = mcspi->dma_channels[i].dma_tx_ch_name; - struct resource *dma_res; - - sprintf(dma_rx_ch_name, "rx%d", i); - if (!pdev->dev.of_node) { - dma_res = - platform_get_resource_byname(pdev, - IORESOURCE_DMA, - dma_rx_ch_name); - if (!dma_res) { - dev_dbg(&pdev->dev, - "cannot get DMA RX channel\n"); - status = -ENODEV; - break; - } - - mcspi->dma_channels[i].dma_rx_sync_dev = - dma_res->start; - } - sprintf(dma_tx_ch_name, "tx%d", i); - if (!pdev->dev.of_node) { - dma_res = - platform_get_resource_byname(pdev, - IORESOURCE_DMA, - dma_tx_ch_name); - if (!dma_res) { - dev_dbg(&pdev->dev, - "cannot get DMA TX channel\n"); - status = -ENODEV; - break; - } - - mcspi->dma_channels[i].dma_tx_sync_dev = - dma_res->start; - } + sprintf(mcspi->dma_channels[i].dma_rx_ch_name, "rx%d", i); + sprintf(mcspi->dma_channels[i].dma_tx_ch_name, "tx%d", i); } if (status < 0) diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c new file mode 100644 index 000000000000..ca3c8d94b290 --- /dev/null +++ b/drivers/spi/spi-pic32-sqi.c @@ -0,0 +1,727 @@ +/* + * PIC32 Quad SPI controller driver. + * + * Purna Chandra Mandal + * Copyright (c) 2016, Microchip Technology Inc. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SQI registers */ +#define PESQI_XIP_CONF1_REG 0x00 +#define PESQI_XIP_CONF2_REG 0x04 +#define PESQI_CONF_REG 0x08 +#define PESQI_CTRL_REG 0x0C +#define PESQI_CLK_CTRL_REG 0x10 +#define PESQI_CMD_THRES_REG 0x14 +#define PESQI_INT_THRES_REG 0x18 +#define PESQI_INT_ENABLE_REG 0x1C +#define PESQI_INT_STAT_REG 0x20 +#define PESQI_TX_DATA_REG 0x24 +#define PESQI_RX_DATA_REG 0x28 +#define PESQI_STAT1_REG 0x2C +#define PESQI_STAT2_REG 0x30 +#define PESQI_BD_CTRL_REG 0x34 +#define PESQI_BD_CUR_ADDR_REG 0x38 +#define PESQI_BD_BASE_ADDR_REG 0x40 +#define PESQI_BD_STAT_REG 0x44 +#define PESQI_BD_POLL_CTRL_REG 0x48 +#define PESQI_BD_TX_DMA_STAT_REG 0x4C +#define PESQI_BD_RX_DMA_STAT_REG 0x50 +#define PESQI_THRES_REG 0x54 +#define PESQI_INT_SIGEN_REG 0x58 + +/* PESQI_CONF_REG fields */ +#define PESQI_MODE 0x7 +#define PESQI_MODE_BOOT 0 +#define PESQI_MODE_PIO 1 +#define PESQI_MODE_DMA 2 +#define PESQI_MODE_XIP 3 +#define PESQI_MODE_SHIFT 0 +#define PESQI_CPHA BIT(3) +#define PESQI_CPOL BIT(4) +#define PESQI_LSBF BIT(5) +#define PESQI_RXLATCH BIT(7) +#define PESQI_SERMODE BIT(8) +#define PESQI_WP_EN BIT(9) +#define PESQI_HOLD_EN BIT(10) +#define PESQI_BURST_EN BIT(12) +#define PESQI_CS_CTRL_HW BIT(15) +#define PESQI_SOFT_RESET BIT(16) +#define PESQI_LANES_SHIFT 20 +#define PESQI_SINGLE_LANE 0 +#define PESQI_DUAL_LANE 1 +#define PESQI_QUAD_LANE 2 +#define PESQI_CSEN_SHIFT 24 +#define PESQI_EN BIT(23) + +/* PESQI_CLK_CTRL_REG fields */ +#define PESQI_CLK_EN BIT(0) +#define PESQI_CLK_STABLE BIT(1) +#define PESQI_CLKDIV_SHIFT 8 +#define PESQI_CLKDIV 0xff + +/* PESQI_INT_THR/CMD_THR_REG */ +#define PESQI_TXTHR_MASK 0x1f +#define PESQI_TXTHR_SHIFT 8 +#define PESQI_RXTHR_MASK 0x1f +#define PESQI_RXTHR_SHIFT 0 + +/* PESQI_INT_EN/INT_STAT/INT_SIG_EN_REG */ +#define PESQI_TXEMPTY BIT(0) +#define PESQI_TXFULL BIT(1) +#define PESQI_TXTHR BIT(2) +#define PESQI_RXEMPTY BIT(3) +#define PESQI_RXFULL BIT(4) +#define PESQI_RXTHR BIT(5) +#define PESQI_BDDONE BIT(9) /* BD processing complete */ +#define PESQI_PKTCOMP BIT(10) /* packet processing complete */ +#define PESQI_DMAERR BIT(11) /* error */ + +/* PESQI_BD_CTRL_REG */ +#define PESQI_DMA_EN BIT(0) /* enable DMA engine */ +#define PESQI_POLL_EN BIT(1) /* enable polling */ +#define PESQI_BDP_START BIT(2) /* start BD processor */ + +/* PESQI controller buffer descriptor */ +struct buf_desc { + u32 bd_ctrl; /* control */ + u32 bd_status; /* reserved */ + u32 bd_addr; /* DMA buffer addr */ + u32 bd_nextp; /* next item in chain */ +}; + +/* bd_ctrl */ +#define BD_BUFLEN 0x1ff +#define BD_CBD_INT_EN BIT(16) /* Current BD is processed */ +#define BD_PKT_INT_EN BIT(17) /* All BDs of PKT processed */ +#define BD_LIFM BIT(18) /* last data of pkt */ +#define BD_LAST BIT(19) /* end of list */ +#define BD_DATA_RECV BIT(20) /* receive data */ +#define BD_DDR BIT(21) /* DDR mode */ +#define BD_DUAL BIT(22) /* Dual SPI */ +#define BD_QUAD BIT(23) /* Quad SPI */ +#define BD_LSBF BIT(25) /* LSB First */ +#define BD_STAT_CHECK BIT(27) /* Status poll */ +#define BD_DEVSEL_SHIFT 28 /* CS */ +#define BD_CS_DEASSERT BIT(30) /* de-assert CS after current BD */ +#define BD_EN BIT(31) /* BD owned by H/W */ + +/** + * struct ring_desc - Representation of SQI ring descriptor + * @list: list element to add to free or used list. + * @bd: PESQI controller buffer descriptor + * @bd_dma: DMA address of PESQI controller buffer descriptor + * @xfer_len: transfer length + */ +struct ring_desc { + struct list_head list; + struct buf_desc *bd; + dma_addr_t bd_dma; + u32 xfer_len; +}; + +/* Global constants */ +#define PESQI_BD_BUF_LEN_MAX 256 +#define PESQI_BD_COUNT 256 /* max 64KB data per spi message */ + +struct pic32_sqi { + void __iomem *regs; + struct clk *sys_clk; + struct clk *base_clk; /* drives spi clock */ + struct spi_master *master; + int irq; + struct completion xfer_done; + struct ring_desc *ring; + void *bd; + dma_addr_t bd_dma; + struct list_head bd_list_free; /* free */ + struct list_head bd_list_used; /* allocated */ + struct spi_device *cur_spi; + u32 cur_speed; + u8 cur_mode; +}; + +static inline void pic32_setbits(void __iomem *reg, u32 set) +{ + writel(readl(reg) | set, reg); +} + +static inline void pic32_clrbits(void __iomem *reg, u32 clr) +{ + writel(readl(reg) & ~clr, reg); +} + +static int pic32_sqi_set_clk_rate(struct pic32_sqi *sqi, u32 sck) +{ + u32 val, div; + + /* div = base_clk / (2 * spi_clk) */ + div = clk_get_rate(sqi->base_clk) / (2 * sck); + div &= PESQI_CLKDIV; + + val = readl(sqi->regs + PESQI_CLK_CTRL_REG); + /* apply new divider */ + val &= ~(PESQI_CLK_STABLE | (PESQI_CLKDIV << PESQI_CLKDIV_SHIFT)); + val |= div << PESQI_CLKDIV_SHIFT; + writel(val, sqi->regs + PESQI_CLK_CTRL_REG); + + /* wait for stability */ + return readl_poll_timeout(sqi->regs + PESQI_CLK_CTRL_REG, val, + val & PESQI_CLK_STABLE, 1, 5000); +} + +static inline void pic32_sqi_enable_int(struct pic32_sqi *sqi) +{ + u32 mask = PESQI_DMAERR | PESQI_BDDONE | PESQI_PKTCOMP; + + writel(mask, sqi->regs + PESQI_INT_ENABLE_REG); + /* INT_SIGEN works as interrupt-gate to INTR line */ + writel(mask, sqi->regs + PESQI_INT_SIGEN_REG); +} + +static inline void pic32_sqi_disable_int(struct pic32_sqi *sqi) +{ + writel(0, sqi->regs + PESQI_INT_ENABLE_REG); + writel(0, sqi->regs + PESQI_INT_SIGEN_REG); +} + +static irqreturn_t pic32_sqi_isr(int irq, void *dev_id) +{ + struct pic32_sqi *sqi = dev_id; + u32 enable, status; + + enable = readl(sqi->regs + PESQI_INT_ENABLE_REG); + status = readl(sqi->regs + PESQI_INT_STAT_REG); + + /* check spurious interrupt */ + if (!status) + return IRQ_NONE; + + if (status & PESQI_DMAERR) { + enable = 0; + goto irq_done; + } + + if (status & PESQI_TXTHR) + enable &= ~(PESQI_TXTHR | PESQI_TXFULL | PESQI_TXEMPTY); + + if (status & PESQI_RXTHR) + enable &= ~(PESQI_RXTHR | PESQI_RXFULL | PESQI_RXEMPTY); + + if (status & PESQI_BDDONE) + enable &= ~PESQI_BDDONE; + + /* packet processing completed */ + if (status & PESQI_PKTCOMP) { + /* mask all interrupts */ + enable = 0; + /* complete trasaction */ + complete(&sqi->xfer_done); + } + +irq_done: + /* interrupts are sticky, so mask when handled */ + writel(enable, sqi->regs + PESQI_INT_ENABLE_REG); + + return IRQ_HANDLED; +} + +static struct ring_desc *ring_desc_get(struct pic32_sqi *sqi) +{ + struct ring_desc *rdesc; + + if (list_empty(&sqi->bd_list_free)) + return NULL; + + rdesc = list_first_entry(&sqi->bd_list_free, struct ring_desc, list); + list_del(&rdesc->list); + list_add_tail(&rdesc->list, &sqi->bd_list_used); + return rdesc; +} + +static void ring_desc_put(struct pic32_sqi *sqi, struct ring_desc *rdesc) +{ + list_del(&rdesc->list); + list_add(&rdesc->list, &sqi->bd_list_free); +} + +static int pic32_sqi_one_transfer(struct pic32_sqi *sqi, + struct spi_message *mesg, + struct spi_transfer *xfer) +{ + struct spi_device *spi = mesg->spi; + struct scatterlist *sg, *sgl; + struct ring_desc *rdesc; + struct buf_desc *bd; + int nents, i; + u32 bd_ctrl; + u32 nbits; + + /* Device selection */ + bd_ctrl = spi->chip_select << BD_DEVSEL_SHIFT; + + /* half-duplex: select transfer buffer, direction and lane */ + if (xfer->rx_buf) { + bd_ctrl |= BD_DATA_RECV; + nbits = xfer->rx_nbits; + sgl = xfer->rx_sg.sgl; + nents = xfer->rx_sg.nents; + } else { + nbits = xfer->tx_nbits; + sgl = xfer->tx_sg.sgl; + nents = xfer->tx_sg.nents; + } + + if (nbits & SPI_NBITS_QUAD) + bd_ctrl |= BD_QUAD; + else if (nbits & SPI_NBITS_DUAL) + bd_ctrl |= BD_DUAL; + + /* LSB first */ + if (spi->mode & SPI_LSB_FIRST) + bd_ctrl |= BD_LSBF; + + /* ownership to hardware */ + bd_ctrl |= BD_EN; + + for_each_sg(sgl, sg, nents, i) { + /* get ring descriptor */ + rdesc = ring_desc_get(sqi); + if (!rdesc) + break; + + bd = rdesc->bd; + + /* BD CTRL: length */ + rdesc->xfer_len = sg_dma_len(sg); + bd->bd_ctrl = bd_ctrl; + bd->bd_ctrl |= rdesc->xfer_len; + + /* BD STAT */ + bd->bd_status = 0; + + /* BD BUFFER ADDRESS */ + bd->bd_addr = sg->dma_address; + } + + return 0; +} + +static int pic32_sqi_prepare_hardware(struct spi_master *master) +{ + struct pic32_sqi *sqi = spi_master_get_devdata(master); + + /* enable spi interface */ + pic32_setbits(sqi->regs + PESQI_CONF_REG, PESQI_EN); + /* enable spi clk */ + pic32_setbits(sqi->regs + PESQI_CLK_CTRL_REG, PESQI_CLK_EN); + + return 0; +} + +static bool pic32_sqi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *x) +{ + /* Do DMA irrespective of transfer size */ + return true; +} + +static int pic32_sqi_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct spi_device *spi = msg->spi; + struct ring_desc *rdesc, *next; + struct spi_transfer *xfer; + struct pic32_sqi *sqi; + int ret = 0, mode; + u32 val; + + sqi = spi_master_get_devdata(master); + + reinit_completion(&sqi->xfer_done); + msg->actual_length = 0; + + /* We can't handle spi_transfer specific "speed_hz", "bits_per_word" + * and "delay_usecs". But spi_device specific speed and mode change + * can be handled at best during spi chip-select switch. + */ + if (sqi->cur_spi != spi) { + /* set spi speed */ + if (sqi->cur_speed != spi->max_speed_hz) { + sqi->cur_speed = spi->max_speed_hz; + ret = pic32_sqi_set_clk_rate(sqi, spi->max_speed_hz); + if (ret) + dev_warn(&spi->dev, "set_clk, %d\n", ret); + } + + /* set spi mode */ + mode = spi->mode & (SPI_MODE_3 | SPI_LSB_FIRST); + if (sqi->cur_mode != mode) { + val = readl(sqi->regs + PESQI_CONF_REG); + val &= ~(PESQI_CPOL | PESQI_CPHA | PESQI_LSBF); + if (mode & SPI_CPOL) + val |= PESQI_CPOL; + if (mode & SPI_LSB_FIRST) + val |= PESQI_LSBF; + val |= PESQI_CPHA; + writel(val, sqi->regs + PESQI_CONF_REG); + + sqi->cur_mode = mode; + } + sqi->cur_spi = spi; + } + + /* prepare hardware desc-list(BD) for transfer(s) */ + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + ret = pic32_sqi_one_transfer(sqi, msg, xfer); + if (ret) { + dev_err(&spi->dev, "xfer %p err\n", xfer); + goto xfer_out; + } + } + + /* BDs are prepared and chained. Now mark LAST_BD, CS_DEASSERT at last + * element of the list. + */ + rdesc = list_last_entry(&sqi->bd_list_used, struct ring_desc, list); + rdesc->bd->bd_ctrl |= BD_LAST | BD_CS_DEASSERT | + BD_LIFM | BD_PKT_INT_EN; + + /* set base address BD list for DMA engine */ + rdesc = list_first_entry(&sqi->bd_list_used, struct ring_desc, list); + writel(rdesc->bd_dma, sqi->regs + PESQI_BD_BASE_ADDR_REG); + + /* enable interrupt */ + pic32_sqi_enable_int(sqi); + + /* enable DMA engine */ + val = PESQI_DMA_EN | PESQI_POLL_EN | PESQI_BDP_START; + writel(val, sqi->regs + PESQI_BD_CTRL_REG); + + /* wait for xfer completion */ + ret = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ); + if (ret <= 0) { + dev_err(&sqi->master->dev, "wait timedout/interrupted\n"); + ret = -EIO; + msg->status = ret; + } else { + /* success */ + msg->status = 0; + ret = 0; + } + + /* disable DMA */ + writel(0, sqi->regs + PESQI_BD_CTRL_REG); + + pic32_sqi_disable_int(sqi); + +xfer_out: + list_for_each_entry_safe_reverse(rdesc, next, + &sqi->bd_list_used, list) { + /* Update total byte transferred */ + msg->actual_length += rdesc->xfer_len; + /* release ring descr */ + ring_desc_put(sqi, rdesc); + } + spi_finalize_current_message(spi->master); + + return ret; +} + +static int pic32_sqi_unprepare_hardware(struct spi_master *master) +{ + struct pic32_sqi *sqi = spi_master_get_devdata(master); + + /* disable clk */ + pic32_clrbits(sqi->regs + PESQI_CLK_CTRL_REG, PESQI_CLK_EN); + /* disable spi */ + pic32_clrbits(sqi->regs + PESQI_CONF_REG, PESQI_EN); + + return 0; +} + +static int ring_desc_ring_alloc(struct pic32_sqi *sqi) +{ + struct ring_desc *rdesc; + struct buf_desc *bd; + int i; + + /* allocate coherent DMAable memory for hardware buffer descriptors. */ + sqi->bd = dma_zalloc_coherent(&sqi->master->dev, + sizeof(*bd) * PESQI_BD_COUNT, + &sqi->bd_dma, GFP_DMA32); + if (!sqi->bd) { + dev_err(&sqi->master->dev, "failed allocating dma buffer\n"); + return -ENOMEM; + } + + /* allocate software ring descriptors */ + sqi->ring = kcalloc(PESQI_BD_COUNT, sizeof(*rdesc), GFP_KERNEL); + if (!sqi->ring) { + dma_free_coherent(&sqi->master->dev, + sizeof(*bd) * PESQI_BD_COUNT, + sqi->bd, sqi->bd_dma); + return -ENOMEM; + } + + bd = (struct buf_desc *)sqi->bd; + + INIT_LIST_HEAD(&sqi->bd_list_free); + INIT_LIST_HEAD(&sqi->bd_list_used); + + /* initialize ring-desc */ + for (i = 0, rdesc = sqi->ring; i < PESQI_BD_COUNT; i++, rdesc++) { + INIT_LIST_HEAD(&rdesc->list); + rdesc->bd = &bd[i]; + rdesc->bd_dma = sqi->bd_dma + (void *)&bd[i] - (void *)bd; + list_add_tail(&rdesc->list, &sqi->bd_list_free); + } + + /* Prepare BD: chain to next BD(s) */ + for (i = 0, rdesc = sqi->ring; i < PESQI_BD_COUNT - 1; i++) + bd[i].bd_nextp = rdesc[i + 1].bd_dma; + bd[PESQI_BD_COUNT - 1].bd_nextp = 0; + + return 0; +} + +static void ring_desc_ring_free(struct pic32_sqi *sqi) +{ + dma_free_coherent(&sqi->master->dev, + sizeof(struct buf_desc) * PESQI_BD_COUNT, + sqi->bd, sqi->bd_dma); + kfree(sqi->ring); +} + +static void pic32_sqi_hw_init(struct pic32_sqi *sqi) +{ + unsigned long flags; + u32 val; + + /* Soft-reset of PESQI controller triggers interrupt. + * We are not yet ready to handle them so disable CPU + * interrupt for the time being. + */ + local_irq_save(flags); + + /* assert soft-reset */ + writel(PESQI_SOFT_RESET, sqi->regs + PESQI_CONF_REG); + + /* wait until clear */ + readl_poll_timeout_atomic(sqi->regs + PESQI_CONF_REG, val, + !(val & PESQI_SOFT_RESET), 1, 5000); + + /* disable all interrupts */ + pic32_sqi_disable_int(sqi); + + /* Now it is safe to enable back CPU interrupt */ + local_irq_restore(flags); + + /* tx and rx fifo interrupt threshold */ + val = readl(sqi->regs + PESQI_CMD_THRES_REG); + val &= ~(PESQI_TXTHR_MASK << PESQI_TXTHR_SHIFT); + val &= ~(PESQI_RXTHR_MASK << PESQI_RXTHR_SHIFT); + val |= (1U << PESQI_TXTHR_SHIFT) | (1U << PESQI_RXTHR_SHIFT); + writel(val, sqi->regs + PESQI_CMD_THRES_REG); + + val = readl(sqi->regs + PESQI_INT_THRES_REG); + val &= ~(PESQI_TXTHR_MASK << PESQI_TXTHR_SHIFT); + val &= ~(PESQI_RXTHR_MASK << PESQI_RXTHR_SHIFT); + val |= (1U << PESQI_TXTHR_SHIFT) | (1U << PESQI_RXTHR_SHIFT); + writel(val, sqi->regs + PESQI_INT_THRES_REG); + + /* default configuration */ + val = readl(sqi->regs + PESQI_CONF_REG); + + /* set mode: DMA */ + val &= ~PESQI_MODE; + val |= PESQI_MODE_DMA << PESQI_MODE_SHIFT; + writel(val, sqi->regs + PESQI_CONF_REG); + + /* DATAEN - SQIID0-ID3 */ + val |= PESQI_QUAD_LANE << PESQI_LANES_SHIFT; + + /* burst/INCR4 enable */ + val |= PESQI_BURST_EN; + + /* CSEN - all CS */ + val |= 3U << PESQI_CSEN_SHIFT; + writel(val, sqi->regs + PESQI_CONF_REG); + + /* write poll count */ + writel(0, sqi->regs + PESQI_BD_POLL_CTRL_REG); + + sqi->cur_speed = 0; + sqi->cur_mode = -1; +} + +static int pic32_sqi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct pic32_sqi *sqi; + struct resource *reg; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*sqi)); + if (!master) + return -ENOMEM; + + sqi = spi_master_get_devdata(master); + sqi->master = master; + + reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sqi->regs = devm_ioremap_resource(&pdev->dev, reg); + if (IS_ERR(sqi->regs)) { + ret = PTR_ERR(sqi->regs); + goto err_free_master; + } + + /* irq */ + sqi->irq = platform_get_irq(pdev, 0); + if (sqi->irq < 0) { + dev_err(&pdev->dev, "no irq found\n"); + ret = sqi->irq; + goto err_free_master; + } + + /* clocks */ + sqi->sys_clk = devm_clk_get(&pdev->dev, "reg_ck"); + if (IS_ERR(sqi->sys_clk)) { + ret = PTR_ERR(sqi->sys_clk); + dev_err(&pdev->dev, "no sys_clk ?\n"); + goto err_free_master; + } + + sqi->base_clk = devm_clk_get(&pdev->dev, "spi_ck"); + if (IS_ERR(sqi->base_clk)) { + ret = PTR_ERR(sqi->base_clk); + dev_err(&pdev->dev, "no base clk ?\n"); + goto err_free_master; + } + + ret = clk_prepare_enable(sqi->sys_clk); + if (ret) { + dev_err(&pdev->dev, "sys clk enable failed\n"); + goto err_free_master; + } + + ret = clk_prepare_enable(sqi->base_clk); + if (ret) { + dev_err(&pdev->dev, "base clk enable failed\n"); + clk_disable_unprepare(sqi->sys_clk); + goto err_free_master; + } + + init_completion(&sqi->xfer_done); + + /* initialize hardware */ + pic32_sqi_hw_init(sqi); + + /* allocate buffers & descriptors */ + ret = ring_desc_ring_alloc(sqi); + if (ret) { + dev_err(&pdev->dev, "ring alloc failed\n"); + goto err_disable_clk; + } + + /* install irq handlers */ + ret = request_irq(sqi->irq, pic32_sqi_isr, 0, + dev_name(&pdev->dev), sqi); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq(%d), failed\n", sqi->irq); + goto err_free_ring; + } + + /* register master */ + master->num_chipselect = 2; + master->max_speed_hz = clk_get_rate(sqi->base_clk); + master->dma_alignment = 32; + master->max_dma_len = PESQI_BD_BUF_LEN_MAX; + master->dev.of_node = of_node_get(pdev->dev.of_node); + master->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_TX_DUAL | + SPI_RX_DUAL | SPI_TX_QUAD | SPI_RX_QUAD; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->can_dma = pic32_sqi_can_dma; + master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32); + master->transfer_one_message = pic32_sqi_one_message; + master->prepare_transfer_hardware = pic32_sqi_prepare_hardware; + master->unprepare_transfer_hardware = pic32_sqi_unprepare_hardware; + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&master->dev, "failed registering spi master\n"); + free_irq(sqi->irq, sqi); + goto err_free_ring; + } + + platform_set_drvdata(pdev, sqi); + + return 0; + +err_free_ring: + ring_desc_ring_free(sqi); + +err_disable_clk: + clk_disable_unprepare(sqi->base_clk); + clk_disable_unprepare(sqi->sys_clk); + +err_free_master: + spi_master_put(master); + return ret; +} + +static int pic32_sqi_remove(struct platform_device *pdev) +{ + struct pic32_sqi *sqi = platform_get_drvdata(pdev); + + /* release resources */ + free_irq(sqi->irq, sqi); + ring_desc_ring_free(sqi); + + /* disable clk */ + clk_disable_unprepare(sqi->base_clk); + clk_disable_unprepare(sqi->sys_clk); + + return 0; +} + +static const struct of_device_id pic32_sqi_of_ids[] = { + {.compatible = "microchip,pic32mzda-sqi",}, + {}, +}; +MODULE_DEVICE_TABLE(of, pic32_sqi_of_ids); + +static struct platform_driver pic32_sqi_driver = { + .driver = { + .name = "sqi-pic32", + .of_match_table = of_match_ptr(pic32_sqi_of_ids), + }, + .probe = pic32_sqi_probe, + .remove = pic32_sqi_remove, +}; + +module_platform_driver(pic32_sqi_driver); + +MODULE_AUTHOR("Purna Chandra Mandal "); +MODULE_DESCRIPTION("Microchip SPI driver for PIC32 SQI controller."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c new file mode 100644 index 000000000000..73db87f805a1 --- /dev/null +++ b/drivers/spi/spi-pic32.c @@ -0,0 +1,878 @@ +/* + * Microchip PIC32 SPI controller driver. + * + * Purna Chandra Mandal + * Copyright (c) 2016, Microchip Technology Inc. + * + * This program is free software; you can distribute it and/or modify it + * under the terms of the GNU General Public License (Version 2) as + * published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SPI controller registers */ +struct pic32_spi_regs { + u32 ctrl; + u32 ctrl_clr; + u32 ctrl_set; + u32 ctrl_inv; + u32 status; + u32 status_clr; + u32 status_set; + u32 status_inv; + u32 buf; + u32 dontuse[3]; + u32 baud; + u32 dontuse2[3]; + u32 ctrl2; + u32 ctrl2_clr; + u32 ctrl2_set; + u32 ctrl2_inv; +}; + +/* Bit fields of SPI Control Register */ +#define CTRL_RX_INT_SHIFT 0 /* Rx interrupt generation */ +#define RX_FIFO_EMTPY 0 +#define RX_FIFO_NOT_EMPTY 1 /* not empty */ +#define RX_FIFO_HALF_FULL 2 /* full by half or more */ +#define RX_FIFO_FULL 3 /* completely full */ + +#define CTRL_TX_INT_SHIFT 2 /* TX interrupt generation */ +#define TX_FIFO_ALL_EMPTY 0 /* completely empty */ +#define TX_FIFO_EMTPY 1 /* empty */ +#define TX_FIFO_HALF_EMPTY 2 /* empty by half or more */ +#define TX_FIFO_NOT_FULL 3 /* atleast one empty */ + +#define CTRL_MSTEN BIT(5) /* enable master mode */ +#define CTRL_CKP BIT(6) /* active low */ +#define CTRL_CKE BIT(8) /* Tx on falling edge */ +#define CTRL_SMP BIT(9) /* Rx at middle or end of tx */ +#define CTRL_BPW_MASK 0x03 /* bits per word/sample */ +#define CTRL_BPW_SHIFT 10 +#define PIC32_BPW_8 0 +#define PIC32_BPW_16 1 +#define PIC32_BPW_32 2 +#define CTRL_SIDL BIT(13) /* sleep when idle */ +#define CTRL_ON BIT(15) /* enable macro */ +#define CTRL_ENHBUF BIT(16) /* enable enhanced buffering */ +#define CTRL_MCLKSEL BIT(23) /* select clock source */ +#define CTRL_MSSEN BIT(28) /* macro driven /SS */ +#define CTRL_FRMEN BIT(31) /* enable framing mode */ + +/* Bit fields of SPI Status Register */ +#define STAT_RF_EMPTY BIT(5) /* RX Fifo empty */ +#define STAT_RX_OV BIT(6) /* err, s/w needs to clear */ +#define STAT_TX_UR BIT(8) /* UR in Framed SPI modes */ +#define STAT_FRM_ERR BIT(12) /* Multiple Frame Sync pulse */ +#define STAT_TF_LVL_MASK 0x1F +#define STAT_TF_LVL_SHIFT 16 +#define STAT_RF_LVL_MASK 0x1F +#define STAT_RF_LVL_SHIFT 24 + +/* Bit fields of SPI Baud Register */ +#define BAUD_MASK 0x1ff + +/* Bit fields of SPI Control2 Register */ +#define CTRL2_TX_UR_EN BIT(10) /* Enable int on Tx under-run */ +#define CTRL2_RX_OV_EN BIT(11) /* Enable int on Rx over-run */ +#define CTRL2_FRM_ERR_EN BIT(12) /* Enable frame err int */ + +/* Minimum DMA transfer size */ +#define PIC32_DMA_LEN_MIN 64 + +struct pic32_spi { + dma_addr_t dma_base; + struct pic32_spi_regs __iomem *regs; + int fault_irq; + int rx_irq; + int tx_irq; + u32 fifo_n_byte; /* FIFO depth in bytes */ + struct clk *clk; + struct spi_master *master; + /* Current controller setting */ + u32 speed_hz; /* spi-clk rate */ + u32 mode; + u32 bits_per_word; + u32 fifo_n_elm; /* FIFO depth in words */ +#define PIC32F_DMA_PREP 0 /* DMA chnls configured */ + unsigned long flags; + /* Current transfer state */ + struct completion xfer_done; + /* PIO transfer specific */ + const void *tx; + const void *tx_end; + const void *rx; + const void *rx_end; + int len; + void (*rx_fifo)(struct pic32_spi *); + void (*tx_fifo)(struct pic32_spi *); +}; + +static inline void pic32_spi_enable(struct pic32_spi *pic32s) +{ + writel(CTRL_ON | CTRL_SIDL, &pic32s->regs->ctrl_set); +} + +static inline void pic32_spi_disable(struct pic32_spi *pic32s) +{ + writel(CTRL_ON | CTRL_SIDL, &pic32s->regs->ctrl_clr); + + /* avoid SPI registers read/write at immediate next CPU clock */ + ndelay(20); +} + +static void pic32_spi_set_clk_rate(struct pic32_spi *pic32s, u32 spi_ck) +{ + u32 div; + + /* div = (clk_in / 2 * spi_ck) - 1 */ + div = DIV_ROUND_CLOSEST(clk_get_rate(pic32s->clk), 2 * spi_ck) - 1; + + writel(div & BAUD_MASK, &pic32s->regs->baud); +} + +static inline u32 pic32_rx_fifo_level(struct pic32_spi *pic32s) +{ + u32 sr = readl(&pic32s->regs->status); + + return (sr >> STAT_RF_LVL_SHIFT) & STAT_RF_LVL_MASK; +} + +static inline u32 pic32_tx_fifo_level(struct pic32_spi *pic32s) +{ + u32 sr = readl(&pic32s->regs->status); + + return (sr >> STAT_TF_LVL_SHIFT) & STAT_TF_LVL_MASK; +} + +/* Return the max entries we can fill into tx fifo */ +static u32 pic32_tx_max(struct pic32_spi *pic32s, int n_bytes) +{ + u32 tx_left, tx_room, rxtx_gap; + + tx_left = (pic32s->tx_end - pic32s->tx) / n_bytes; + tx_room = pic32s->fifo_n_elm - pic32_tx_fifo_level(pic32s); + + /* + * Another concern is about the tx/rx mismatch, we + * though to use (pic32s->fifo_n_byte - rxfl - txfl) as + * one maximum value for tx, but it doesn't cover the + * data which is out of tx/rx fifo and inside the + * shift registers. So a ctrl from sw point of + * view is taken. + */ + rxtx_gap = ((pic32s->rx_end - pic32s->rx) - + (pic32s->tx_end - pic32s->tx)) / n_bytes; + return min3(tx_left, tx_room, (u32)(pic32s->fifo_n_elm - rxtx_gap)); +} + +/* Return the max entries we should read out of rx fifo */ +static u32 pic32_rx_max(struct pic32_spi *pic32s, int n_bytes) +{ + u32 rx_left = (pic32s->rx_end - pic32s->rx) / n_bytes; + + return min_t(u32, rx_left, pic32_rx_fifo_level(pic32s)); +} + +#define BUILD_SPI_FIFO_RW(__name, __type, __bwl) \ +static void pic32_spi_rx_##__name(struct pic32_spi *pic32s) \ +{ \ + __type v; \ + u32 mx = pic32_rx_max(pic32s, sizeof(__type)); \ + for (; mx; mx--) { \ + v = read##__bwl(&pic32s->regs->buf); \ + if (pic32s->rx_end - pic32s->len) \ + *(__type *)(pic32s->rx) = v; \ + pic32s->rx += sizeof(__type); \ + } \ +} \ + \ +static void pic32_spi_tx_##__name(struct pic32_spi *pic32s) \ +{ \ + __type v; \ + u32 mx = pic32_tx_max(pic32s, sizeof(__type)); \ + for (; mx ; mx--) { \ + v = (__type)~0U; \ + if (pic32s->tx_end - pic32s->len) \ + v = *(__type *)(pic32s->tx); \ + write##__bwl(v, &pic32s->regs->buf); \ + pic32s->tx += sizeof(__type); \ + } \ +} + +BUILD_SPI_FIFO_RW(byte, u8, b); +BUILD_SPI_FIFO_RW(word, u16, w); +BUILD_SPI_FIFO_RW(dword, u32, l); + +static void pic32_err_stop(struct pic32_spi *pic32s, const char *msg) +{ + /* disable all interrupts */ + disable_irq_nosync(pic32s->fault_irq); + disable_irq_nosync(pic32s->rx_irq); + disable_irq_nosync(pic32s->tx_irq); + + /* Show err message and abort xfer with err */ + dev_err(&pic32s->master->dev, "%s\n", msg); + if (pic32s->master->cur_msg) + pic32s->master->cur_msg->status = -EIO; + complete(&pic32s->xfer_done); +} + +static irqreturn_t pic32_spi_fault_irq(int irq, void *dev_id) +{ + struct pic32_spi *pic32s = dev_id; + u32 status; + + status = readl(&pic32s->regs->status); + + /* Error handling */ + if (status & (STAT_RX_OV | STAT_TX_UR)) { + writel(STAT_RX_OV, &pic32s->regs->status_clr); + writel(STAT_TX_UR, &pic32s->regs->status_clr); + pic32_err_stop(pic32s, "err_irq: fifo ov/ur-run\n"); + return IRQ_HANDLED; + } + + if (status & STAT_FRM_ERR) { + pic32_err_stop(pic32s, "err_irq: frame error"); + return IRQ_HANDLED; + } + + if (!pic32s->master->cur_msg) { + pic32_err_stop(pic32s, "err_irq: no mesg"); + return IRQ_NONE; + } + + return IRQ_NONE; +} + +static irqreturn_t pic32_spi_rx_irq(int irq, void *dev_id) +{ + struct pic32_spi *pic32s = dev_id; + + pic32s->rx_fifo(pic32s); + + /* rx complete ? */ + if (pic32s->rx_end == pic32s->rx) { + /* disable all interrupts */ + disable_irq_nosync(pic32s->fault_irq); + disable_irq_nosync(pic32s->rx_irq); + + /* complete current xfer */ + complete(&pic32s->xfer_done); + } + + return IRQ_HANDLED; +} + +static irqreturn_t pic32_spi_tx_irq(int irq, void *dev_id) +{ + struct pic32_spi *pic32s = dev_id; + + pic32s->tx_fifo(pic32s); + + /* tx complete? disable tx interrupt */ + if (pic32s->tx_end == pic32s->tx) + disable_irq_nosync(pic32s->tx_irq); + + return IRQ_HANDLED; +} + +static void pic32_spi_dma_rx_notify(void *data) +{ + struct pic32_spi *pic32s = data; + + complete(&pic32s->xfer_done); +} + +static int pic32_spi_dma_transfer(struct pic32_spi *pic32s, + struct spi_transfer *xfer) +{ + struct spi_master *master = pic32s->master; + struct dma_async_tx_descriptor *desc_rx; + struct dma_async_tx_descriptor *desc_tx; + dma_cookie_t cookie; + int ret; + + if (!master->dma_rx || !master->dma_tx) + return -ENODEV; + + desc_rx = dmaengine_prep_slave_sg(master->dma_rx, + xfer->rx_sg.sgl, + xfer->rx_sg.nents, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_rx) { + ret = -EINVAL; + goto err_dma; + } + + desc_tx = dmaengine_prep_slave_sg(master->dma_tx, + xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc_tx) { + ret = -EINVAL; + goto err_dma; + } + + /* Put callback on the RX transfer, that should finish last */ + desc_rx->callback = pic32_spi_dma_rx_notify; + desc_rx->callback_param = pic32s; + + cookie = dmaengine_submit(desc_rx); + ret = dma_submit_error(cookie); + if (ret) + goto err_dma; + + cookie = dmaengine_submit(desc_tx); + ret = dma_submit_error(cookie); + if (ret) + goto err_dma_tx; + + dma_async_issue_pending(master->dma_rx); + dma_async_issue_pending(master->dma_tx); + + return 0; + +err_dma_tx: + dmaengine_terminate_all(master->dma_rx); +err_dma: + return ret; +} + +static int pic32_spi_dma_config(struct pic32_spi *pic32s, u32 dma_width) +{ + int buf_offset = offsetof(struct pic32_spi_regs, buf); + struct spi_master *master = pic32s->master; + struct dma_slave_config cfg; + int ret; + + cfg.device_fc = true; + cfg.src_addr = pic32s->dma_base + buf_offset; + cfg.dst_addr = pic32s->dma_base + buf_offset; + cfg.src_maxburst = pic32s->fifo_n_elm / 2; /* fill one-half */ + cfg.dst_maxburst = pic32s->fifo_n_elm / 2; /* drain one-half */ + cfg.src_addr_width = dma_width; + cfg.dst_addr_width = dma_width; + /* tx channel */ + cfg.slave_id = pic32s->tx_irq; + cfg.direction = DMA_MEM_TO_DEV; + ret = dmaengine_slave_config(master->dma_tx, &cfg); + if (ret) { + dev_err(&master->dev, "tx channel setup failed\n"); + return ret; + } + /* rx channel */ + cfg.slave_id = pic32s->rx_irq; + cfg.direction = DMA_DEV_TO_MEM; + ret = dmaengine_slave_config(master->dma_rx, &cfg); + if (ret) + dev_err(&master->dev, "rx channel setup failed\n"); + + return ret; +} + +static int pic32_spi_set_word_size(struct pic32_spi *pic32s, u8 bits_per_word) +{ + enum dma_slave_buswidth dmawidth; + u32 buswidth, v; + + switch (bits_per_word) { + case 8: + pic32s->rx_fifo = pic32_spi_rx_byte; + pic32s->tx_fifo = pic32_spi_tx_byte; + buswidth = PIC32_BPW_8; + dmawidth = DMA_SLAVE_BUSWIDTH_1_BYTE; + break; + case 16: + pic32s->rx_fifo = pic32_spi_rx_word; + pic32s->tx_fifo = pic32_spi_tx_word; + buswidth = PIC32_BPW_16; + dmawidth = DMA_SLAVE_BUSWIDTH_2_BYTES; + break; + case 32: + pic32s->rx_fifo = pic32_spi_rx_dword; + pic32s->tx_fifo = pic32_spi_tx_dword; + buswidth = PIC32_BPW_32; + dmawidth = DMA_SLAVE_BUSWIDTH_4_BYTES; + break; + default: + /* not supported */ + return -EINVAL; + } + + /* calculate maximum number of words fifos can hold */ + pic32s->fifo_n_elm = DIV_ROUND_UP(pic32s->fifo_n_byte, + bits_per_word / 8); + /* set word size */ + v = readl(&pic32s->regs->ctrl); + v &= ~(CTRL_BPW_MASK << CTRL_BPW_SHIFT); + v |= buswidth << CTRL_BPW_SHIFT; + writel(v, &pic32s->regs->ctrl); + + /* re-configure dma width, if required */ + if (test_bit(PIC32F_DMA_PREP, &pic32s->flags)) + pic32_spi_dma_config(pic32s, dmawidth); + + return 0; +} + +static int pic32_spi_prepare_hardware(struct spi_master *master) +{ + struct pic32_spi *pic32s = spi_master_get_devdata(master); + + pic32_spi_enable(pic32s); + + return 0; +} + +static int pic32_spi_prepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct pic32_spi *pic32s = spi_master_get_devdata(master); + struct spi_device *spi = msg->spi; + u32 val; + + /* set device specific bits_per_word */ + if (pic32s->bits_per_word != spi->bits_per_word) { + pic32_spi_set_word_size(pic32s, spi->bits_per_word); + pic32s->bits_per_word = spi->bits_per_word; + } + + /* device specific speed change */ + if (pic32s->speed_hz != spi->max_speed_hz) { + pic32_spi_set_clk_rate(pic32s, spi->max_speed_hz); + pic32s->speed_hz = spi->max_speed_hz; + } + + /* device specific mode change */ + if (pic32s->mode != spi->mode) { + val = readl(&pic32s->regs->ctrl); + /* active low */ + if (spi->mode & SPI_CPOL) + val |= CTRL_CKP; + else + val &= ~CTRL_CKP; + /* tx on rising edge */ + if (spi->mode & SPI_CPHA) + val &= ~CTRL_CKE; + else + val |= CTRL_CKE; + + /* rx at end of tx */ + val |= CTRL_SMP; + writel(val, &pic32s->regs->ctrl); + pic32s->mode = spi->mode; + } + + return 0; +} + +static bool pic32_spi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct pic32_spi *pic32s = spi_master_get_devdata(master); + + /* skip using DMA on small size transfer to avoid overhead.*/ + return (xfer->len >= PIC32_DMA_LEN_MIN) && + test_bit(PIC32F_DMA_PREP, &pic32s->flags); +} + +static int pic32_spi_one_transfer(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *transfer) +{ + struct pic32_spi *pic32s; + bool dma_issued = false; + int ret; + + pic32s = spi_master_get_devdata(master); + + /* handle transfer specific word size change */ + if (transfer->bits_per_word && + (transfer->bits_per_word != pic32s->bits_per_word)) { + ret = pic32_spi_set_word_size(pic32s, transfer->bits_per_word); + if (ret) + return ret; + pic32s->bits_per_word = transfer->bits_per_word; + } + + /* handle transfer specific speed change */ + if (transfer->speed_hz && (transfer->speed_hz != pic32s->speed_hz)) { + pic32_spi_set_clk_rate(pic32s, transfer->speed_hz); + pic32s->speed_hz = transfer->speed_hz; + } + + reinit_completion(&pic32s->xfer_done); + + /* transact by DMA mode */ + if (transfer->rx_sg.nents && transfer->tx_sg.nents) { + ret = pic32_spi_dma_transfer(pic32s, transfer); + if (ret) { + dev_err(&spi->dev, "dma submit error\n"); + return ret; + } + + /* DMA issued */ + dma_issued = true; + } else { + /* set current transfer information */ + pic32s->tx = (const void *)transfer->tx_buf; + pic32s->rx = (const void *)transfer->rx_buf; + pic32s->tx_end = pic32s->tx + transfer->len; + pic32s->rx_end = pic32s->rx + transfer->len; + pic32s->len = transfer->len; + + /* transact by interrupt driven PIO */ + enable_irq(pic32s->fault_irq); + enable_irq(pic32s->rx_irq); + enable_irq(pic32s->tx_irq); + } + + /* wait for completion */ + ret = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ); + if (ret <= 0) { + dev_err(&spi->dev, "wait error/timedout\n"); + if (dma_issued) { + dmaengine_terminate_all(master->dma_rx); + dmaengine_terminate_all(master->dma_rx); + } + ret = -ETIMEDOUT; + } else { + ret = 0; + } + + return ret; +} + +static int pic32_spi_unprepare_message(struct spi_master *master, + struct spi_message *msg) +{ + /* nothing to do */ + return 0; +} + +static int pic32_spi_unprepare_hardware(struct spi_master *master) +{ + struct pic32_spi *pic32s = spi_master_get_devdata(master); + + pic32_spi_disable(pic32s); + + return 0; +} + +/* This may be called multiple times by same spi dev */ +static int pic32_spi_setup(struct spi_device *spi) +{ + if (!spi->max_speed_hz) { + dev_err(&spi->dev, "No max speed HZ parameter\n"); + return -EINVAL; + } + + /* PIC32 spi controller can drive /CS during transfer depending + * on tx fifo fill-level. /CS will stay asserted as long as TX + * fifo is non-empty, else will be deasserted indicating + * completion of the ongoing transfer. This might result into + * unreliable/erroneous SPI transactions. + * To avoid that we will always handle /CS by toggling GPIO. + */ + if (!gpio_is_valid(spi->cs_gpio)) + return -EINVAL; + + gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); + + return 0; +} + +static void pic32_spi_cleanup(struct spi_device *spi) +{ + /* de-activate cs-gpio */ + gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); +} + +static void pic32_spi_dma_prep(struct pic32_spi *pic32s, struct device *dev) +{ + struct spi_master *master = pic32s->master; + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + master->dma_rx = dma_request_slave_channel_compat(mask, NULL, NULL, + dev, "spi-rx"); + if (!master->dma_rx) { + dev_warn(dev, "RX channel not found.\n"); + goto out_err; + } + + master->dma_tx = dma_request_slave_channel_compat(mask, NULL, NULL, + dev, "spi-tx"); + if (!master->dma_tx) { + dev_warn(dev, "TX channel not found.\n"); + goto out_err; + } + + if (pic32_spi_dma_config(pic32s, DMA_SLAVE_BUSWIDTH_1_BYTE)) + goto out_err; + + /* DMA chnls allocated and prepared */ + set_bit(PIC32F_DMA_PREP, &pic32s->flags); + + return; + +out_err: + if (master->dma_rx) + dma_release_channel(master->dma_rx); + + if (master->dma_tx) + dma_release_channel(master->dma_tx); +} + +static void pic32_spi_dma_unprep(struct pic32_spi *pic32s) +{ + if (!test_bit(PIC32F_DMA_PREP, &pic32s->flags)) + return; + + clear_bit(PIC32F_DMA_PREP, &pic32s->flags); + if (pic32s->master->dma_rx) + dma_release_channel(pic32s->master->dma_rx); + + if (pic32s->master->dma_tx) + dma_release_channel(pic32s->master->dma_tx); +} + +static void pic32_spi_hw_init(struct pic32_spi *pic32s) +{ + u32 ctrl; + + /* disable hardware */ + pic32_spi_disable(pic32s); + + ctrl = readl(&pic32s->regs->ctrl); + /* enable enhanced fifo of 128bit deep */ + ctrl |= CTRL_ENHBUF; + pic32s->fifo_n_byte = 16; + + /* disable framing mode */ + ctrl &= ~CTRL_FRMEN; + + /* enable master mode while disabled */ + ctrl |= CTRL_MSTEN; + + /* set tx fifo threshold interrupt */ + ctrl &= ~(0x3 << CTRL_TX_INT_SHIFT); + ctrl |= (TX_FIFO_HALF_EMPTY << CTRL_TX_INT_SHIFT); + + /* set rx fifo threshold interrupt */ + ctrl &= ~(0x3 << CTRL_RX_INT_SHIFT); + ctrl |= (RX_FIFO_NOT_EMPTY << CTRL_RX_INT_SHIFT); + + /* select clk source */ + ctrl &= ~CTRL_MCLKSEL; + + /* set manual /CS mode */ + ctrl &= ~CTRL_MSSEN; + + writel(ctrl, &pic32s->regs->ctrl); + + /* enable error reporting */ + ctrl = CTRL2_TX_UR_EN | CTRL2_RX_OV_EN | CTRL2_FRM_ERR_EN; + writel(ctrl, &pic32s->regs->ctrl2_set); +} + +static int pic32_spi_hw_probe(struct platform_device *pdev, + struct pic32_spi *pic32s) +{ + struct resource *mem; + int ret; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pic32s->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(pic32s->regs)) + return PTR_ERR(pic32s->regs); + + pic32s->dma_base = mem->start; + + /* get irq resources: err-irq, rx-irq, tx-irq */ + pic32s->fault_irq = platform_get_irq_byname(pdev, "fault"); + if (pic32s->fault_irq < 0) { + dev_err(&pdev->dev, "fault-irq not found\n"); + return pic32s->fault_irq; + } + + pic32s->rx_irq = platform_get_irq_byname(pdev, "rx"); + if (pic32s->rx_irq < 0) { + dev_err(&pdev->dev, "rx-irq not found\n"); + return pic32s->rx_irq; + } + + pic32s->tx_irq = platform_get_irq_byname(pdev, "tx"); + if (pic32s->tx_irq < 0) { + dev_err(&pdev->dev, "tx-irq not found\n"); + return pic32s->tx_irq; + } + + /* get clock */ + pic32s->clk = devm_clk_get(&pdev->dev, "mck0"); + if (IS_ERR(pic32s->clk)) { + dev_err(&pdev->dev, "clk not found\n"); + ret = PTR_ERR(pic32s->clk); + goto err_unmap_mem; + } + + ret = clk_prepare_enable(pic32s->clk); + if (ret) + goto err_unmap_mem; + + pic32_spi_hw_init(pic32s); + + return 0; + +err_unmap_mem: + dev_err(&pdev->dev, "%s failed, err %d\n", __func__, ret); + return ret; +} + +static int pic32_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct pic32_spi *pic32s; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*pic32s)); + if (!master) + return -ENOMEM; + + pic32s = spi_master_get_devdata(master); + pic32s->master = master; + + ret = pic32_spi_hw_probe(pdev, pic32s); + if (ret) + goto err_master; + + master->dev.of_node = of_node_get(pdev->dev.of_node); + master->mode_bits = SPI_MODE_3 | SPI_MODE_0 | SPI_CS_HIGH; + master->num_chipselect = 1; /* single chip-select */ + master->max_speed_hz = clk_get_rate(pic32s->clk); + master->setup = pic32_spi_setup; + master->cleanup = pic32_spi_cleanup; + master->flags = SPI_MASTER_MUST_TX | SPI_MASTER_MUST_RX; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) | + SPI_BPW_MASK(32); + master->transfer_one = pic32_spi_one_transfer; + master->prepare_message = pic32_spi_prepare_message; + master->unprepare_message = pic32_spi_unprepare_message; + master->prepare_transfer_hardware = pic32_spi_prepare_hardware; + master->unprepare_transfer_hardware = pic32_spi_unprepare_hardware; + + /* optional DMA support */ + pic32_spi_dma_prep(pic32s, &pdev->dev); + if (test_bit(PIC32F_DMA_PREP, &pic32s->flags)) + master->can_dma = pic32_spi_can_dma; + + init_completion(&pic32s->xfer_done); + pic32s->mode = -1; + + /* install irq handlers (with irq-disabled) */ + irq_set_status_flags(pic32s->fault_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, pic32s->fault_irq, + pic32_spi_fault_irq, IRQF_NO_THREAD, + dev_name(&pdev->dev), pic32s); + if (ret < 0) { + dev_err(&pdev->dev, "request fault-irq %d\n", pic32s->rx_irq); + goto err_bailout; + } + + /* receive interrupt handler */ + irq_set_status_flags(pic32s->rx_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, pic32s->rx_irq, + pic32_spi_rx_irq, IRQF_NO_THREAD, + dev_name(&pdev->dev), pic32s); + if (ret < 0) { + dev_err(&pdev->dev, "request rx-irq %d\n", pic32s->rx_irq); + goto err_bailout; + } + + /* transmit interrupt handler */ + irq_set_status_flags(pic32s->tx_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(&pdev->dev, pic32s->tx_irq, + pic32_spi_tx_irq, IRQF_NO_THREAD, + dev_name(&pdev->dev), pic32s); + if (ret < 0) { + dev_err(&pdev->dev, "request tx-irq %d\n", pic32s->tx_irq); + goto err_bailout; + } + + /* register master */ + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&master->dev, "failed registering spi master\n"); + goto err_bailout; + } + + platform_set_drvdata(pdev, pic32s); + + return 0; + +err_bailout: + clk_disable_unprepare(pic32s->clk); +err_master: + spi_master_put(master); + return ret; +} + +static int pic32_spi_remove(struct platform_device *pdev) +{ + struct pic32_spi *pic32s; + + pic32s = platform_get_drvdata(pdev); + pic32_spi_disable(pic32s); + clk_disable_unprepare(pic32s->clk); + pic32_spi_dma_unprep(pic32s); + + return 0; +} + +static const struct of_device_id pic32_spi_of_match[] = { + {.compatible = "microchip,pic32mzda-spi",}, + {}, +}; +MODULE_DEVICE_TABLE(of, pic32_spi_of_match); + +static struct platform_driver pic32_spi_driver = { + .driver = { + .name = "spi-pic32", + .of_match_table = of_match_ptr(pic32_spi_of_match), + }, + .probe = pic32_spi_probe, + .remove = pic32_spi_remove, +}; + +module_platform_driver(pic32_spi_driver); + +MODULE_AUTHOR("Purna Chandra Mandal "); +MODULE_DESCRIPTION("Microchip SPI driver for PIC32 SPI controller."); +MODULE_LICENSE("GPL v2");