diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 13 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-ath79.c | 11 | ||||
-rw-r--r-- | drivers/spi/spi-atmel.c | 26 | ||||
-rw-r--r-- | drivers/spi/spi-au1550.c | 11 | ||||
-rw-r--r-- | drivers/spi/spi-bcm2835.c | 20 | ||||
-rw-r--r-- | drivers/spi/spi-bcm2835aux.c | 512 | ||||
-rw-r--r-- | drivers/spi/spi-bcm53xx.c | 13 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx.c | 210 | ||||
-rw-r--r-- | drivers/spi/spi-bfin-sport.c | 5 | ||||
-rw-r--r-- | drivers/spi/spi-bfin5xx.c | 6 | ||||
-rw-r--r-- | drivers/spi/spi-davinci.c | 22 | ||||
-rw-r--r-- | drivers/spi/spi-imx.c | 7 | ||||
-rw-r--r-- | drivers/spi/spi-meson-spifc.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-mt65xx.c | 53 | ||||
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 28 | ||||
-rw-r--r-- | drivers/spi/spi-pxa2xx.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi-ti-qspi.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-xilinx.c | 38 | ||||
-rw-r--r-- | drivers/spi/spi-xtensa-xtfpga.c | 4 | ||||
-rw-r--r-- | drivers/spi/spi.c | 132 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 3 |
22 files changed, 953 insertions, 170 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4887f31..cac9224 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -88,6 +88,17 @@ config SPI_BCM2835 is for the regular SPI controller. Slave mode operation is not also not supported. +config SPI_BCM2835AUX + tristate "BCM2835 SPI auxiliary controller" + depends on ARCH_BCM2835 || COMPILE_TEST + depends on GPIOLIB + help + This selects a driver for the Broadcom BCM2835 SPI aux master. + + The BCM2835 contains two types of SPI master controller; the + "universal SPI master", and the regular SPI controller. + This driver is for the universal/auxiliary SPI controller. + config SPI_BFIN5XX tristate "SPI controller driver for ADI Blackfin5xx" depends on BLACKFIN && !BF60x @@ -125,7 +136,7 @@ config SPI_BCM53XX config SPI_BCM63XX tristate "Broadcom BCM63xx SPI controller" - depends on BCM63XX + depends on BCM63XX || COMPILE_TEST help Enable support for the SPI controller on the Broadcom BCM63xx SoCs. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 6a7f6f9..31fb7fb 100644 --- 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_BCM2835) += spi-bcm2835.o +obj-$(CONFIG_SPI_BCM2835AUX) += spi-bcm2835aux.o obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o diff --git a/drivers/spi/spi-ath79.c b/drivers/spi/spi-ath79.c index bf1f9b3..6165bf2 100644 --- a/drivers/spi/spi-ath79.c +++ b/drivers/spi/spi-ath79.c @@ -240,14 +240,9 @@ static int ath79_spi_probe(struct platform_device *pdev) sp->bitbang.flags = SPI_CS_HIGH; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (r == NULL) { - ret = -ENOENT; - goto err_put_master; - } - - sp->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); - if (!sp->base) { - ret = -ENXIO; + sp->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(sp->base)) { + ret = PTR_ERR(sp->base); goto err_put_master; } diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index bf9ed38..aebad36 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -773,7 +773,8 @@ static int atmel_spi_next_xfer_dma_submit(struct spi_master *master, *plen = len; - if (atmel_spi_dma_slave_config(as, &slave_config, 8)) + if (atmel_spi_dma_slave_config(as, &slave_config, + xfer->bits_per_word)) goto err_exit; /* Send both scatterlists */ @@ -871,14 +872,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as, * Calculate the lowest divider that satisfies the * constraint, assuming div32/fdiv/mbz == 0. */ - if (xfer->speed_hz) - scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); - else - /* - * This can happend if max_speed is null. - * In this case, we set the lowest possible speed - */ - scbr = 0xff; + scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz); /* * If the resulting divider doesn't fit into the @@ -1300,14 +1294,12 @@ static int atmel_spi_one_transfer(struct spi_master *master, return -EINVAL; } - if (xfer->bits_per_word) { - asd = spi->controller_state; - bits = (asd->csr >> 4) & 0xf; - if (bits != xfer->bits_per_word - 8) { - dev_dbg(&spi->dev, + asd = spi->controller_state; + bits = (asd->csr >> 4) & 0xf; + if (bits != xfer->bits_per_word - 8) { + dev_dbg(&spi->dev, "you can't yet change bits_per_word in transfers\n"); - return -ENOPROTOOPT; - } + return -ENOPROTOOPT; } /* @@ -1720,6 +1712,7 @@ static int atmel_spi_runtime_resume(struct device *dev) return clk_prepare_enable(as->clk); } +#ifdef CONFIG_PM_SLEEP static int atmel_spi_suspend(struct device *dev) { struct spi_master *master = dev_get_drvdata(dev); @@ -1756,6 +1749,7 @@ static int atmel_spi_resume(struct device *dev) return ret; } +#endif static const struct dev_pm_ops atmel_spi_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(atmel_spi_suspend, atmel_spi_resume) diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index f45e085..afd239d 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -233,13 +233,12 @@ static int au1550_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) unsigned bpw, hz; u32 cfg, stat; - bpw = spi->bits_per_word; - hz = spi->max_speed_hz; if (t) { - if (t->bits_per_word) - bpw = t->bits_per_word; - if (t->speed_hz) - hz = t->speed_hz; + bpw = t->bits_per_word; + hz = t->speed_hz; + } else { + bpw = spi->bits_per_word; + hz = spi->max_speed_hz; } if (!hz) diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index e7874a6..cf04960 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -386,14 +386,14 @@ static bool bcm2835_spi_can_dma(struct spi_master *master, /* otherwise we only allow transfers within the same page * to avoid wasting time on dma_mapping when it is not practical */ - if (((size_t)tfr->tx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { + if (((size_t)tfr->tx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { dev_warn_once(&spi->dev, "Unaligned spi tx-transfer bridging page\n"); return false; } - if (((size_t)tfr->rx_buf & PAGE_MASK) + tfr->len > PAGE_SIZE) { + if (((size_t)tfr->rx_buf & (PAGE_SIZE - 1)) + tfr->len > PAGE_SIZE) { dev_warn_once(&spi->dev, - "Unaligned spi tx-transfer bridging page\n"); + "Unaligned spi rx-transfer bridging page\n"); return false; } @@ -777,7 +777,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_master_put; } - bs->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + bs->irq = platform_get_irq(pdev, 0); if (bs->irq <= 0) { dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); err = bs->irq ? bs->irq : -ENODEV; @@ -786,6 +786,12 @@ static int bcm2835_spi_probe(struct platform_device *pdev) clk_prepare_enable(bs->clk); + bcm2835_dma_init(master, &pdev->dev); + + /* initialise the hardware with the default polarities */ + bcm2835_wr(bs, BCM2835_SPI_CS, + BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); + err = devm_request_irq(&pdev->dev, bs->irq, bcm2835_spi_interrupt, 0, dev_name(&pdev->dev), master); if (err) { @@ -793,12 +799,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev) goto out_clk_disable; } - bcm2835_dma_init(master, &pdev->dev); - - /* initialise the hardware with the default polarities */ - bcm2835_wr(bs, BCM2835_SPI_CS, - BCM2835_SPI_CS_CLEAR_RX | BCM2835_SPI_CS_CLEAR_TX); - err = devm_spi_register_master(&pdev->dev, master); if (err) { dev_err(&pdev->dev, "could not register SPI master: %d\n", err); diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c new file mode 100644 index 0000000..7de6f84 --- /dev/null +++ b/drivers/spi/spi-bcm2835aux.c @@ -0,0 +1,512 @@ +/* + * Driver for Broadcom BCM2835 auxiliary SPI Controllers + * + * the driver does not rely on the native chipselects at all + * but only uses the gpio type chipselects + * + * Based on: spi-bcm2835.c + * + * Copyright (C) 2015 Martin Sperl + * + * 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. + */ + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/spinlock.h> + +/* + * spi register defines + * + * note there is garbage in the "official" documentation, + * so some data is taken from the file: + * brcm_usrlib/dag/vmcsx/vcinclude/bcm2708_chip/aux_io.h + * inside of: + * http://www.broadcom.com/docs/support/videocore/Brcm_Android_ICS_Graphics_Stack.tar.gz + */ + +/* SPI register offsets */ +#define BCM2835_AUX_SPI_CNTL0 0x00 +#define BCM2835_AUX_SPI_CNTL1 0x04 +#define BCM2835_AUX_SPI_STAT 0x08 +#define BCM2835_AUX_SPI_PEEK 0x0C +#define BCM2835_AUX_SPI_IO 0x20 +#define BCM2835_AUX_SPI_TXHOLD 0x30 + +/* Bitfields in CNTL0 */ +#define BCM2835_AUX_SPI_CNTL0_SPEED 0xFFF00000 +#define BCM2835_AUX_SPI_CNTL0_SPEED_MAX 0xFFF +#define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT 20 +#define BCM2835_AUX_SPI_CNTL0_CS 0x000E0000 +#define BCM2835_AUX_SPI_CNTL0_POSTINPUT 0x00010000 +#define BCM2835_AUX_SPI_CNTL0_VAR_CS 0x00008000 +#define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH 0x00004000 +#define BCM2835_AUX_SPI_CNTL0_DOUTHOLD 0x00003000 +#define BCM2835_AUX_SPI_CNTL0_ENABLE 0x00000800 +#define BCM2835_AUX_SPI_CNTL0_CPHA_IN 0x00000400 +#define BCM2835_AUX_SPI_CNTL0_CLEARFIFO 0x00000200 +#define BCM2835_AUX_SPI_CNTL0_CPHA_OUT 0x00000100 +#define BCM2835_AUX_SPI_CNTL0_CPOL 0x00000080 +#define BCM2835_AUX_SPI_CNTL0_MSBF_OUT 0x00000040 +#define BCM2835_AUX_SPI_CNTL0_SHIFTLEN 0x0000003F + +/* Bitfields in CNTL1 */ +#define BCM2835_AUX_SPI_CNTL1_CSHIGH 0x00000700 +#define BCM2835_AUX_SPI_CNTL1_IDLE 0x00000080 +#define BCM2835_AUX_SPI_CNTL1_TXEMPTY 0x00000040 +#define BCM2835_AUX_SPI_CNTL1_MSBF_IN 0x00000002 +#define BCM2835_AUX_SPI_CNTL1_KEEP_IN 0x00000001 + +/* Bitfields in STAT */ +#define BCM2835_AUX_SPI_STAT_TX_LVL 0xFF000000 +#define BCM2835_AUX_SPI_STAT_RX_LVL 0x00FF0000 +#define BCM2835_AUX_SPI_STAT_TX_FULL 0x00000400 +#define BCM2835_AUX_SPI_STAT_TX_EMPTY 0x00000200 +#define BCM2835_AUX_SPI_STAT_RX_FULL 0x00000100 +#define BCM2835_AUX_SPI_STAT_RX_EMPTY 0x00000080 +#define BCM2835_AUX_SPI_STAT_BUSY 0x00000040 +#define BCM2835_AUX_SPI_STAT_BITCOUNT 0x0000003F + +/* timeout values */ +#define BCM2835_AUX_SPI_POLLING_LIMIT_US 30 +#define BCM2835_AUX_SPI_POLLING_JIFFIES 2 + +#define BCM2835_AUX_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ + | SPI_NO_CS) + +struct bcm2835aux_spi { + void __iomem *regs; + struct clk *clk; + int irq; + u32 cntl[2]; + const u8 *tx_buf; + u8 *rx_buf; + int tx_len; + int rx_len; + int pending; +}; + +static inline u32 bcm2835aux_rd(struct bcm2835aux_spi *bs, unsigned reg) +{ + return readl(bs->regs + reg); +} + +static inline void bcm2835aux_wr(struct bcm2835aux_spi *bs, unsigned reg, + u32 val) +{ + writel(val, bs->regs + reg); +} + +static inline void bcm2835aux_rd_fifo(struct bcm2835aux_spi *bs) +{ + u32 data; + int count = min(bs->rx_len, 3); + + data = bcm2835aux_rd(bs, BCM2835_AUX_SPI_IO); + if (bs->rx_buf) { + switch (count) { + case 4: + *bs->rx_buf++ = (data >> 24) & 0xff; + /* fallthrough */ + case 3: + *bs->rx_buf++ = (data >> 16) & 0xff; + /* fallthrough */ + case 2: + *bs->rx_buf++ = (data >> 8) & 0xff; + /* fallthrough */ + case 1: + *bs->rx_buf++ = (data >> 0) & 0xff; + /* fallthrough - no default */ + } + } + bs->rx_len -= count; + bs->pending -= count; +} + +static inline void bcm2835aux_wr_fifo(struct bcm2835aux_spi *bs) +{ + u32 data; + u8 byte; + int count; + int i; + + /* gather up to 3 bytes to write to the FIFO */ + count = min(bs->tx_len, 3); + data = 0; + for (i = 0; i < count; i++) { + byte = bs->tx_buf ? *bs->tx_buf++ : 0; + data |= byte << (8 * (2 - i)); + } + + /* and set the variable bit-length */ + data |= (count * 8) << 24; + + /* and decrement length */ + bs->tx_len -= count; + bs->pending += count; + + /* write to the correct TX-register */ + if (bs->tx_len) + bcm2835aux_wr(bs, BCM2835_AUX_SPI_TXHOLD, data); + else + bcm2835aux_wr(bs, BCM2835_AUX_SPI_IO, data); +} + +static void bcm2835aux_spi_reset_hw(struct bcm2835aux_spi *bs) +{ + /* disable spi clearing fifo and interrupts */ + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, 0); + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, + BCM2835_AUX_SPI_CNTL0_CLEARFIFO); +} + +static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + irqreturn_t ret = IRQ_NONE; + + /* check if we have data to read */ + while (bs->rx_len && + (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & + BCM2835_AUX_SPI_STAT_RX_EMPTY))) { + bcm2835aux_rd_fifo(bs); + ret = IRQ_HANDLED; + } + + /* check if we have data to write */ + while (bs->tx_len && + (bs->pending < 12) && + (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & + BCM2835_AUX_SPI_STAT_TX_FULL))) { + bcm2835aux_wr_fifo(bs); + ret = IRQ_HANDLED; + } + + /* and check if we have reached "done" */ + while (bs->rx_len && + (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & + BCM2835_AUX_SPI_STAT_BUSY))) { + bcm2835aux_rd_fifo(bs); + ret = IRQ_HANDLED; + } + + /* and if rx_len is 0 then wake up completion and disable spi */ + if (!bs->rx_len) { + bcm2835aux_spi_reset_hw(bs); + complete(&master->xfer_completion); + } + + /* and return */ + return ret; +} + +static int __bcm2835aux_spi_transfer_one_irq(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + + /* enable interrupts */ + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1] | + BCM2835_AUX_SPI_CNTL1_TXEMPTY | + BCM2835_AUX_SPI_CNTL1_IDLE); + + /* and wait for finish... */ + return 1; +} + +static int bcm2835aux_spi_transfer_one_irq(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + + /* fill in registers and fifos before enabling interrupts */ + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); + + /* fill in tx fifo with data before enabling interrupts */ + while ((bs->tx_len) && + (bs->pending < 12) && + (!(bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT) & + BCM2835_AUX_SPI_STAT_TX_FULL))) { + bcm2835aux_wr_fifo(bs); + } + + /* now run the interrupt mode */ + return __bcm2835aux_spi_transfer_one_irq(master, spi, tfr); +} + +static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + unsigned long timeout; + u32 stat; + + /* configure spi */ + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); + bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); + + /* set the timeout */ + timeout = jiffies + BCM2835_AUX_SPI_POLLING_JIFFIES; + + /* loop until finished the transfer */ + while (bs->rx_len) { + /* read status */ + stat = bcm2835aux_rd(bs, BCM2835_AUX_SPI_STAT); + + /* fill in tx fifo with remaining data */ + if ((bs->tx_len) && (!(stat & BCM2835_AUX_SPI_STAT_TX_FULL))) { + bcm2835aux_wr_fifo(bs); + continue; + } + + /* read data from fifo for both cases */ + if (!(stat & BCM2835_AUX_SPI_STAT_RX_EMPTY)) { + bcm2835aux_rd_fifo(bs); + continue; + } + if (!(stat & BCM2835_AUX_SPI_STAT_BUSY)) { + bcm2835aux_rd_fifo(bs); + continue; + } + + /* there is still data pending to read check the timeout */ + if (bs->rx_len && time_after(jiffies, timeout)) { + dev_dbg_ratelimited(&spi->dev, + "timeout period reached: jiffies: %lu remaining tx/rx: %d/%d - falling back to interrupt mode\n", + jiffies - timeout, + bs->tx_len, bs->rx_len); + /* forward to interrupt handler */ + return __bcm2835aux_spi_transfer_one_irq(master, + spi, tfr); + } + } + + /* Transfer complete - reset SPI HW */ + bcm2835aux_spi_reset_hw(bs); + + /* and return without waiting for completion */ + return 0; +} + +static int bcm2835aux_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *tfr) +{ + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + unsigned long spi_hz, clk_hz, speed; + unsigned long spi_used_hz; + unsigned long long xfer_time_us; + + /* calculate the registers to handle + * + * note that we use the variable data mode, which + * is not optimal for longer transfers as we waste registers + * resulting (potentially) in more interrupts when transferring + * more than 12 bytes + */ + bs->cntl[0] = BCM2835_AUX_SPI_CNTL0_ENABLE | + BCM2835_AUX_SPI_CNTL0_VAR_WIDTH | + BCM2835_AUX_SPI_CNTL0_MSBF_OUT; + bs->cntl[1] = BCM2835_AUX_SPI_CNTL1_MSBF_IN; + + /* set clock */ + spi_hz = tfr->speed_hz; + clk_hz = clk_get_rate(bs->clk); + + if (spi_hz >= clk_hz / 2) { + speed = 0; + } else if (spi_hz) { + speed = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1; + if (speed > BCM2835_AUX_SPI_CNTL0_SPEED_MAX) + speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX; + } else { /* the slowest we can go */ + speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX; + } + bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT; + + spi_used_hz = clk_hz / (2 * (speed + 1)); + + /* handle all the modes */ + if (spi->mode & SPI_CPOL) + bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPOL; + if (spi->mode & SPI_CPHA) + bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPHA_OUT | + BCM2835_AUX_SPI_CNTL0_CPHA_IN; + + /* set transmit buffers and length */ + bs->tx_buf = tfr->tx_buf; + bs->rx_buf = tfr->rx_buf; + bs->tx_len = tfr->len; + bs->rx_len = tfr->len; + bs->pending = 0; + + /* calculate the estimated time in us the transfer runs + * note that there are are 2 idle clocks after each + * chunk getting transferred - in our case the chunk size + * is 3 bytes, so we approximate this by 9 bits/byte + */ + xfer_time_us = tfr->len * 9 * 1000000; + do_div(xfer_time_us, spi_used_hz); + + /* run in polling mode for short transfers */ + if (xfer_time_us < BCM2835_AUX_SPI_POLLING_LIMIT_US) + return bcm2835aux_spi_transfer_one_poll(master, spi, tfr); + + /* run in interrupt mode for all others */ + return bcm2835aux_spi_transfer_one_irq(master, spi, tfr); +} + +static void bcm2835aux_spi_handle_err(struct spi_master *master, + struct spi_message *msg) +{ + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + + bcm2835aux_spi_reset_hw(bs); +} + +static int bcm2835aux_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct bcm2835aux_spi *bs; + struct resource *res; + unsigned long clk_hz; + int err; + + master = spi_alloc_master(&pdev->dev, sizeof(*bs)); + if (!master) { + dev_err(&pdev->dev, "spi_alloc_master() failed\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, master); + master->mode_bits = BCM2835_AUX_SPI_MODE_BITS; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->num_chipselect = -1; + master->transfer_one = bcm2835aux_spi_transfer_one; + master->handle_err = bcm2835aux_spi_handle_err; + master->dev.of_node = pdev->dev.of_node; + + bs = spi_master_get_devdata(master); + + /* the main area */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bs->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(bs->regs)) { + err = PTR_ERR(bs->regs); + goto out_master_put; + } + + bs->clk = devm_clk_get(&pdev->dev, NULL); + if ((!bs->clk) || (IS_ERR(bs->clk))) { + err = PTR_ERR(bs->clk); + dev_err(&pdev->dev, "could not get clk: %d\n", err); + goto out_master_put; + } + + bs->irq = platform_get_irq(pdev, 0); + if (bs->irq <= 0) { + dev_err(&pdev->dev, "could not get IRQ: %d\n", bs->irq); + err = bs->irq ? bs->irq : -ENODEV; + goto out_master_put; + } + + /* this also enables the HW block */ + err = clk_prepare_enable(bs->clk); + if (err) { + dev_err(&pdev->dev, "could not prepare clock: %d\n", err); + goto out_master_put; + } + + /* just checking if the clock returns a sane value */ + clk_hz = clk_get_rate(bs->clk); + if (!clk_hz) { + dev_err(&pdev->dev, "clock returns 0 Hz\n"); + err = -ENODEV; + goto out_clk_disable; + } + + /* reset SPI-HW block */ + bcm2835aux_spi_reset_hw(bs); + + err = devm_request_irq(&pdev->dev, bs->irq, + bcm2835aux_spi_interrupt, + IRQF_SHARED, + dev_name(&pdev->dev), master); + if (err) { + dev_err(&pdev->dev, "could not request IRQ: %d\n", err); + goto out_clk_disable; + } + + err = devm_spi_register_master(&pdev->dev, master); + if (err) { + dev_err(&pdev->dev, "could not register SPI master: %d\n", err); + goto out_clk_disable; + } + + return 0; + +out_clk_disable: + clk_disable_unprepare(bs->clk); +out_master_put: + spi_master_put(master); + return err; +} + +static int bcm2835aux_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + + bcm2835aux_spi_reset_hw(bs); + + /* disable the HW block by releasing the clock */ + clk_disable_unprepare(bs->clk); + + return 0; +} + +static const struct of_device_id bcm2835aux_spi_match[] = { + { .compatible = "brcm,bcm2835-aux-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, bcm2835aux_spi_match); + +static struct platform_driver bcm2835aux_spi_driver = { + .driver = { + .name = "spi-bcm2835aux", + .of_match_table = bcm2835aux_spi_match, + }, + .probe = bcm2835aux_spi_probe, + .remove = bcm2835aux_spi_remove, +}; +module_platform_driver(bcm2835aux_spi_driver); + +MODULE_DESCRIPTION("SPI controller driver for Broadcom BCM2835 aux"); +MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c index 1520554..cc3f938 100644 --- a/drivers/spi/spi-bcm53xx.c +++ b/drivers/spi/spi-bcm53xx.c @@ -247,28 +247,19 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core) if (err) { spi_master_put(master); bcma_set_drvdata(core, NULL); - goto out; + return err; } /* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */ spi_new_device(master, &bcm53xx_info); -out: - return err; -} - -static void bcm53xxspi_bcma_remove(struct bcma_device *core) -{ - struct bcm53xxspi *b53spi = bcma_get_drvdata(core); - - spi_unregister_master(b53spi->master); + return 0; } static struct bcma_driver bcm53xxspi_bcma_driver = { .name = KBUILD_MODNAME, .id_table = bcm53xxspi_bcma_tbl, .probe = bcm53xxspi_bcma_probe, - .remove = bcm53xxspi_bcma_remove, }; /************************************************** diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index e73e2b05..06858e0 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -27,10 +27,117 @@ #include <linux/err.h> #include <linux/pm_runtime.h> -#include <bcm63xx_dev_spi.h> +/* BCM 6338/6348 SPI core */ +#define SPI_6348_RSET_SIZE 64 +#define SPI_6348_CMD 0x00 /* 16-bits register */ +#define SPI_6348_INT_STATUS 0x02 +#define SPI_6348_INT_MASK_ST 0x03 +#define SPI_6348_INT_MASK 0x04 +#define SPI_6348_ST 0x05 +#define SPI_6348_CLK_CFG 0x06 +#define SPI_6348_FILL_BYTE 0x07 +#define SPI_6348_MSG_TAIL 0x09 +#define SPI_6348_RX_TAIL 0x0b +#define SPI_6348_MSG_CTL 0x40 /* 8-bits register */ +#define SPI_6348_MSG_CTL_WIDTH 8 +#define SPI_6348_MSG_DATA 0x41 +#define SPI_6348_MSG_DATA_SIZE 0x3f +#define SPI_6348_RX_DATA 0x80 +#define SPI_6348_RX_DATA_SIZE 0x3f + +/* BCM 3368/6358/6262/6368 SPI core */ +#define SPI_6358_RSET_SIZE 1804 +#define SPI_6358_MSG_CTL 0x00 /* 16-bits register */ +#define SPI_6358_MSG_CTL_WIDTH 16 +#define SPI_6358_MSG_DATA 0x02 +#define SPI_6358_MSG_DATA_SIZE 0x21e +#define SPI_6358_RX_DATA 0x400 +#define SPI_6358_RX_DATA_SIZE 0x220 +#define SPI_6358_CMD 0x700 /* 16-bits register */ +#define SPI_6358_INT_STATUS 0x702 +#define SPI_6358_INT_MASK_ST 0x703 +#define SPI_6358_INT_MASK 0x704 +#define SPI_6358_ST 0x705 +#define SPI_6358_CLK_CFG 0x706 +#define SPI_6358_FILL_BYTE 0x707 +#define SPI_6358_MSG_TAIL 0x709 +#define SPI_6358_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_6348_MSG_TYPE_SHIFT 6 +#define SPI_6358_MSG_TYPE_SHIFT 14 + +/* Command */ +#define SPI_CMD_NOOP 0x00 +#define SPI_CMD_SOFT_RESET 0x01 +#define SPI_CMD_HARD_RESET 0x02 +#define SPI_CMD_START_IMMEDIATE 0x03 +#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_MASK 0x07 +#define SPI_SSOFFTIME_MASK 0x38 +#define SPI_SSOFFTIME_SHIFT 3 +#define SPI_BYTE_SWAP 0x80 + +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, + SPI_MSG_TYPE_SHIFT, + SPI_MSG_CTL_WIDTH, + SPI_MSG_DATA_SIZE, +}; #define BCM63XX_SPI_MAX_PREPEND 15 +#define BCM63XX_SPI_MAX_CS 8 +#define BCM63XX_SPI_BUS_NUM 0 + struct bcm63xx_spi { struct completion done; @@ -38,6 +145,7 @@ struct bcm63xx_spi { int irq; /* Platform data */ + const unsigned long *reg_offsets; unsigned fifo_size; unsigned int msg_type_shift; unsigned int msg_ctl_width; @@ -51,27 +159,35 @@ struct bcm63xx_spi { }; static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs, - unsigned int offset) + unsigned int offset) { - return bcm_readb(bs->regs + bcm63xx_spireg(offset)); + return readb(bs->regs + bs->reg_offsets[offset]); } static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs, unsigned int offset) { - return bcm_readw(bs->regs + bcm63xx_spireg(offset)); +#ifdef CONFIG_CPU_BIG_ENDIAN + return ioread16be(bs->regs + bs->reg_offsets[offset]); +#else + return readw(bs->regs + bs->reg_offsets[offset]); +#endif } static inline void bcm_spi_writeb(struct bcm63xx_spi *bs, u8 value, unsigned int offset) { - bcm_writeb(value, bs->regs + bcm63xx_spireg(offset)); + writeb(value, bs->regs + bs->reg_offsets[offset]); } static inline void bcm_spi_writew(struct bcm63xx_spi *bs, u16 value, unsigned int offset) { - bcm_writew(value, bs->regs + bcm63xx_spireg(offset)); +#ifdef CONFIG_CPU_BIG_ENDIAN + iowrite16be(value, bs->regs + bs->reg_offsets[offset]); +#else + writew(value, bs->regs + bs->reg_offsets[offset]); +#endif } static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = { @@ -122,7 +238,6 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first, struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); u16 msg_ctl; u16 cmd; - u8 rx_tail; unsigned int i, timeout = 0, prepend_len = 0, len = 0; struct spi_transfer *t = first; bool do_rx = false; @@ -314,18 +429,71 @@ static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static const unsigned long bcm6348_spi_reg_offsets[] = { + [SPI_CMD] = SPI_6348_CMD, + [SPI_INT_STATUS] = SPI_6348_INT_STATUS, + [SPI_INT_MASK_ST] = SPI_6348_INT_MASK_ST, + [SPI_INT_MASK] = SPI_6348_INT_MASK, + [SPI_ST] = SPI_6348_ST, + [SPI_CLK_CFG] = SPI_6348_CLK_CFG, + [SPI_FILL_BYTE] = SPI_6348_FILL_BYTE, + [SPI_MSG_TAIL] = SPI_6348_MSG_TAIL, + [SPI_RX_TAIL] = SPI_6348_RX_TAIL, + [SPI_MSG_CTL] = SPI_6348_MSG_CTL, + [SPI_MSG_DATA] = SPI_6348_MSG_DATA, + [SPI_RX_DATA] = SPI_6348_RX_DATA, + [SPI_MSG_TYPE_SHIFT] = SPI_6348_MSG_TYPE_SHIFT, + [SPI_MSG_CTL_WIDTH] = SPI_6348_MSG_CTL_WIDTH, + [SPI_MSG_DATA_SIZE] = SPI_6348_MSG_DATA_SIZE, +}; + +static const unsigned long bcm6358_spi_reg_offsets[] = { + [SPI_CMD] = SPI_6358_CMD, + [SPI_INT_STATUS] = SPI_6358_INT_STATUS, + [SPI_INT_MASK_ST] = SPI_6358_INT_MASK_ST, + [SPI_INT_MASK] = SPI_6358_INT_MASK, + [SPI_ST] = SPI_6358_ST, + [SPI_CLK_CFG] = SPI_6358_CLK_CFG, + [SPI_FILL_BYTE] = SPI_6358_FILL_BYTE, + [SPI_MSG_TAIL] = SPI_6358_MSG_TAIL, + [SPI_RX_TAIL] = SPI_6358_RX_TAIL, + [SPI_MSG_CTL] = SPI_6358_MSG_CTL, + [SPI_MSG_DATA] = SPI_6358_MSG_DATA, + [SPI_RX_DATA] = SPI_6358_RX_DATA, + [SPI_MSG_TYPE_SHIFT] = SPI_6358_MSG_TYPE_SHIFT, + [SPI_MSG_CTL_WIDTH] = SPI_6358_MSG_CTL_WIDTH, + [SPI_MSG_DATA_SIZE] = SPI_6358_MSG_DATA_SIZE, +}; + +static const struct platform_device_id bcm63xx_spi_dev_match[] = { + { + .name = "bcm6348-spi", + .driver_data = (unsigned long)bcm6348_spi_reg_offsets, + }, + { + .name = "bcm6358-spi", + .driver_data = (unsigned long)bcm6358_spi_reg_offsets, + }, + { + }, +}; static int bcm63xx_spi_probe(struct platform_device *pdev) { struct resource *r; + const unsigned long *bcm63xx_spireg; struct device *dev = &pdev->dev; - struct bcm63xx_spi_pdata *pdata = dev_get_platdata(&pdev->dev); int irq; struct spi_master *master; struct clk *clk; struct bcm63xx_spi *bs; int ret; + if (!pdev->id_entry->driver_data) + return -EINVAL; + + bcm63xx_spireg = (const unsigned long *)pdev->id_entry->driver_data; + irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "no irq\n"); @@ -359,7 +527,8 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) bs->irq = irq; bs->clk = clk; - bs->fifo_size = pdata->fifo_size; + bs->reg_offsets = bcm63xx_spireg; + bs->fifo_size = bs->reg_offsets[SPI_MSG_DATA_SIZE]; ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0, pdev->name, master); @@ -368,26 +537,16 @@ static int bcm63xx_spi_probe(struct platform_device *pdev) goto out_err; } - master->bus_num = pdata->bus_num; - master->num_chipselect = pdata->num_chipselect; + master->bus_num = BCM63XX_SPI_BUS_NUM; + master->num_chipselect = BCM63XX_SPI_MAX_CS; master->transfer_one_message = bcm63xx_spi_transfer_one; master->mode_bits = MODEBITS; master->bits_per_word_mask = SPI_BPW_MASK(8); master->auto_runtime_pm = true; - bs->msg_type_shift = pdata->msg_type_shift; - bs->msg_ctl_width = pdata->msg_ctl_width; - bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA)); - bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA)); - - switch (bs->msg_ctl_width) { - case 8: - case 16: - break; - default: - dev_err(dev, "unsupported MSG_CTL width: %d\n", - bs->msg_ctl_width); - goto out_err; - } + bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT]; + bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH]; + bs->tx_io = (u8 *)(bs->regs + bs->reg_offsets[SPI_MSG_DATA]); + bs->rx_io = (const u8 *)(bs->regs + bs->reg_offsets[SPI_RX_DATA]); /* Initialize hardware */ ret = clk_prepare_enable(bs->clk); @@ -467,6 +626,7 @@ static struct platform_driver bcm63xx_spi_driver = { .name = "bcm63xx-spi", .pm = &bcm63xx_spi_pm_ops, }, + .id_table = bcm63xx_spi_dev_match, .probe = bcm63xx_spi_probe, .remove = bcm63xx_spi_remove, }; diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c index a786931..6c96755 100644 --- a/drivers/spi/spi-bfin-sport.c +++ b/drivers/spi/spi-bfin-sport.c @@ -352,10 +352,7 @@ bfin_sport_spi_pump_transfers(unsigned long data) transfer = drv_data->cur_transfer; chip = drv_data->cur_chip; - if (transfer->speed_hz) - transfer_speed = bfin_sport_hz_to_spi_baud(transfer->speed_hz); - else - transfer_speed = chip->baud; + transfer_speed = bfin_sport_hz_to_spi_baud(transfer->speed_hz); bfin_write(&drv_data->regs->tclkdiv, transfer_speed); SSYNC(); diff --git a/drivers/spi/spi-bfin5xx.c b/drivers/spi/spi-bfin5xx.c index a3d65b4..1e91325 100644 --- a/drivers/spi/spi-bfin5xx.c +++ b/drivers/spi/spi-bfin5xx.c @@ -661,11 +661,7 @@ static void bfin_spi_pump_transfers(unsigned long data) message->state = RUNNING_STATE; dma_config = 0; - /* Speed setup (surely valid because already checked) */ - if (transfer->speed_hz) - bfin_write(&drv_data->regs->baud, hz_to_spi_baud(transfer->speed_hz)); - else - bfin_write(&drv_data->regs->baud, chip->baud); + bfin_write(&drv_data->regs->baud, hz_to_spi_baud(transfer->speed_hz)); bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); bfin_spi_cs_active(drv_data, chip); diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index 3cf9faa..7d3af3e 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -215,18 +215,10 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) struct davinci_spi_config *spicfg = spi->controller_data; u8 chip_sel = spi->chip_select; u16 spidat1 = CS_DEFAULT; - bool gpio_chipsel = false; - int gpio; dspi = spi_master_get_devdata(spi->master); pdata = &dspi->pdata; - if (spi->cs_gpio >= 0) { - /* SPI core parse and update master->cs_gpio */ - gpio_chipsel = true; - gpio = spi->cs_gpio; - } - /* program delay transfers if tx_delay is non zero */ if (spicfg->wdelay) spidat1 |= SPIDAT1_WDEL; @@ -235,11 +227,12 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Board specific chip select logic decides the polarity and cs * line for the controller */ - if (gpio_chipsel) { + if (spi->cs_gpio >= 0) { if (value == BITBANG_CS_ACTIVE) - gpio_set_value(gpio, spi->mode & SPI_CS_HIGH); + gpio_set_value(spi->cs_gpio, spi->mode & SPI_CS_HIGH); else - gpio_set_value(gpio, !(spi->mode & SPI_CS_HIGH)); + gpio_set_value(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); } else { if (value == BITBANG_CS_ACTIVE) { spidat1 |= SPIDAT1_CSHOLD_MASK; @@ -992,11 +985,12 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - dspi->irq = platform_get_irq(pdev, 0); - if (dspi->irq <= 0) { + ret = platform_get_irq(pdev, 0); + if (ret == 0) ret = -EINVAL; + if (ret < 0) goto free_master; - } + dspi->irq = ret; ret = devm_request_threaded_irq(&pdev->dev, dspi->irq, davinci_spi_irq, dummy_thread_fn, 0, dev_name(&pdev->dev), dspi); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index f9deb84..0e5723a 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -336,13 +336,20 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx, if (config->mode & SPI_CPHA) cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); + else + cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(config->cs); if (config->mode & SPI_CPOL) { cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs); cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs); + } else { + cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(config->cs); + cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(config->cs); } if (config->mode & SPI_CS_HIGH) cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs); + else + cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs); writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 5468fc7..2465259 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -444,6 +444,7 @@ static const struct of_device_id meson_spifc_dt_match[] = { { .compatible = "amlogic,meson6-spifc", }, { }, }; +MODULE_DEVICE_TABLE(of, meson_spifc_dt_match); static struct platform_driver meson_spifc_driver = { .probe = meson_spifc_probe, diff --git a/drivers/spi/spi-mt65xx.c b/drivers/spi/spi-mt65xx.c index 5f6315c..ecb6c58 100644 --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c @@ -85,7 +85,7 @@ struct mtk_spi { void __iomem *base; u32 state; u32 pad_sel; - struct clk *spi_clk, *parent_clk; + struct clk *parent_clk, *sel_clk, *spi_clk; struct spi_transfer *cur_transfer; u32 xfer_len; struct scatterlist *tx_sgl, *rx_sgl; @@ -173,22 +173,6 @@ static void mtk_spi_config(struct mtk_spi *mdata, writel(mdata->pad_sel, mdata->base + SPI_PAD_SEL_REG); } -static int mtk_spi_prepare_hardware(struct spi_master *master) -{ - struct spi_transfer *trans; - struct mtk_spi *mdata = spi_master_get_devdata(master); - struct spi_message *msg = master->cur_msg; - - trans = list_first_entry(&msg->transfers, struct spi_transfer, - transfer_list); - if (!trans->cs_change) { - mdata->state = MTK_SPI_IDLE; - mtk_spi_reset(mdata); - } - - return 0; -} - static int mtk_spi_prepare_message(struct spi_master *master, struct spi_message *msg) { @@ -228,11 +212,15 @@ static void mtk_spi_set_cs(struct spi_device *spi, bool enable) struct mtk_spi *mdata = spi_master_get_devdata(spi->master); reg_val = readl(mdata->base + SPI_CMD_REG); - if (!enable) + if (!enable) { reg_val |= SPI_CMD_PAUSE_EN; - else + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { reg_val &= ~SPI_CMD_PAUSE_EN; - writel(reg_val, mdata->base + SPI_CMD_REG); + writel(reg_val, mdata->base + SPI_CMD_REG); + mdata->state = MTK_SPI_IDLE; + mtk_spi_reset(mdata); + } } static void mtk_spi_prepare_transfer(struct spi_master *master, @@ -509,7 +497,6 @@ static int mtk_spi_probe(struct platform_device *pdev) master->mode_bits = SPI_CPOL | SPI_CPHA; master->set_cs = mtk_spi_set_cs; - master->prepare_transfer_hardware = mtk_spi_prepare_hardware; master->prepare_message = mtk_spi_prepare_message; master->transfer_one = mtk_spi_transfer_one; master->can_dma = mtk_spi_can_dma; @@ -576,13 +563,6 @@ static int mtk_spi_probe(struct platform_device *pdev) goto err_put_master; } - mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk"); - if (IS_ERR(mdata->spi_clk)) { - ret = PTR_ERR(mdata->spi_clk); - dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); - goto err_put_master; - } - mdata->parent_clk = devm_clk_get(&pdev->dev, "parent-clk"); if (IS_ERR(mdata->parent_clk)) { ret = PTR_ERR(mdata->parent_clk); @@ -590,13 +570,27 @@ static int mtk_spi_probe(struct platform_device *pdev) goto err_put_master; } + mdata->sel_clk = devm_clk_get(&pdev->dev, "sel-clk"); + if (IS_ERR(mdata->sel_clk)) { + ret = PTR_ERR(mdata->sel_clk); + dev_err(&pdev->dev, "failed to get sel-clk: %d\n", ret); + goto err_put_master; + } + + mdata->spi_clk = devm_clk_get(&pdev->dev, "spi-clk"); + if (IS_ERR(mdata->spi_clk)) { + ret = PTR_ERR(mdata->spi_clk); + dev_err(&pdev->dev, "failed to get spi-clk: %d\n", ret); + goto err_put_master; + } + ret = clk_prepare_enable(mdata->spi_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to enable spi_clk (%d)\n", ret); goto err_put_master; } - ret = clk_set_parent(mdata->spi_clk, mdata->parent_clk); + ret = clk_set_parent(mdata->sel_clk, mdata->parent_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to clk_set_parent (%d)\n", ret); goto err_disable_clk; @@ -630,7 +624,6 @@ static int mtk_spi_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); mtk_spi_reset(mdata); - clk_disable_unprepare(mdata->spi_clk); spi_master_put(master); return 0; diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3d09e0b..1f8903d 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1217,6 +1217,33 @@ out: return status; } +static int omap2_mcspi_prepare_message(struct spi_master *master, + struct spi_message *msg) +{ + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); + struct omap2_mcspi_regs *ctx = &mcspi->ctx; + struct omap2_mcspi_cs *cs; + + /* Only a single channel can have the FORCE bit enabled + * in its chconf0 register. + * Scan all channels and disable them except the current one. + * A FORCE can remain from a last transfer having cs_change enabled + */ + list_for_each_entry(cs, &ctx->cs, node) { + if (msg->spi->controller_state == cs) + continue; + + if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE)) { + cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE; + writel_relaxed(cs->chconf0, + cs->base + OMAP2_MCSPI_CHCONF0); + readl_relaxed(cs->base + OMAP2_MCSPI_CHCONF0); + } + } + + return 0; +} + static int omap2_mcspi_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) { @@ -1344,6 +1371,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; + master->prepare_message = omap2_mcspi_prepare_message; master->transfer_one = omap2_mcspi_transfer_one; master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index fdd79197..a8ef38e 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -654,6 +654,10 @@ static irqreturn_t ssp_int(int irq, void *dev_id) if (!(sccr1_reg & SSCR1_TIE)) mask &= ~SSSR_TFS; + /* Ignore RX timeout interrupt if it is disabled */ + if (!(sccr1_reg & SSCR1_TINTE)) + mask &= ~SSSR_TINT; + if (!(status & mask)) return IRQ_NONE; diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index aa6d284..81b8485 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -410,11 +410,10 @@ static int ti_qspi_start_transfer_one(struct spi_master *master, mutex_unlock(&qspi->list_lock); + ti_qspi_write(qspi, qspi->cmd | QSPI_INVAL, QSPI_SPI_CMD_REG); m->status = status; spi_finalize_current_message(master); - ti_qspi_write(qspi, qspi->cmd | QSPI_INVAL, QSPI_SPI_CMD_REG); - return status; } diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index a339c1e..3009121 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -270,6 +270,7 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) while (remaining_words) { int n_words, tx_words, rx_words; + u32 sr; n_words = min(remaining_words, xspi->buffer_size); @@ -284,24 +285,33 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) if (use_irq) { xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); wait_for_completion(&xspi->done); - } else - while (!(xspi->read_fn(xspi->regs + XSPI_SR_OFFSET) & - XSPI_SR_TX_EMPTY_MASK)) - ; - - /* A transmit has just completed. Process received data and - * check for more data to transmit. Always inhibit the - * transmitter while the Isr refills the transmit register/FIFO, - * or make sure it is stopped if we're done. - */ - if (use_irq) + /* A transmit has just completed. Process received data + * and check for more data to transmit. Always inhibit + * the transmitter while the Isr refills the transmit + * register/FIFO, or make sure it is stopped if we're + * done. + */ xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, - xspi->regs + XSPI_CR_OFFSET); + xspi->regs + XSPI_CR_OFFSET); + sr = XSPI_SR_TX_EMPTY_MASK; + } else + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); /* Read out all the data from the Rx FIFO */ rx_words = n_words; - while (rx_words--) - xilinx_spi_rx(xspi); + while (rx_words) { + if ((sr & XSPI_SR_TX_EMPTY_MASK) && (rx_words > 1)) { + xilinx_spi_rx(xspi); + rx_words--; + continue; + } + + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); + if (!(sr & XSPI_SR_RX_EMPTY_MASK)) { + xilinx_spi_rx(xspi); + rx_words--; + } + } remaining_words -= n_words; } diff --git a/drivers/spi/spi-xtensa-xtfpga.c b/drivers/spi/spi-xtensa-xtfpga.c index 2e32ea2..be6155c 100644 --- a/drivers/spi/spi-xtensa-xtfpga.c +++ b/drivers/spi/spi-xtensa-xtfpga.c @@ -34,13 +34,13 @@ struct xtfpga_spi { static inline void xtfpga_spi_write32(const struct xtfpga_spi *spi, unsigned addr, u32 val) { - iowrite32(val, spi->regs + addr); + __raw_writel(val, spi->regs + addr); } static inline unsigned int xtfpga_spi_read32(const struct xtfpga_spi *spi, unsigned addr) { - return ioread32(spi->regs + addr); + return __raw_readl(spi->regs + addr); } static inline void xtfpga_spi_wait_busy(struct xtfpga_spi *xspi) diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 3abb390..f9e92f5 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -123,6 +123,28 @@ SPI_STATISTICS_SHOW(bytes, "%llu"); SPI_STATISTICS_SHOW(bytes_rx, "%llu"); SPI_STATISTICS_SHOW(bytes_tx, "%llu"); +#define SPI_STATISTICS_TRANSFER_BYTES_HISTO(index, number) \ + SPI_STATISTICS_SHOW_NAME(transfer_bytes_histo##index, \ + "transfer_bytes_histo_" number, \ + transfer_bytes_histo[index], "%lu") +SPI_STATISTICS_TRANSFER_BYTES_HISTO(0, "0-1"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(1, "2-3"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(2, "4-7"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(3, "8-15"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(4, "16-31"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(5, "32-63"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(6, "64-127"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(7, "128-255"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(8, "256-511"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(9, "512-1023"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(10, "1024-2047"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(11, "2048-4095"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(12, "4096-8191"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(13, "8192-16383"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535"); +SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+"); + static struct attribute *spi_dev_attrs[] = { &dev_attr_modalias.attr, NULL, @@ -143,6 +165,23 @@ static struct attribute *spi_device_statistics_attrs[] = { &dev_attr_spi_device_bytes.attr, &dev_attr_spi_device_bytes_rx.attr, &dev_attr_spi_device_bytes_tx.attr, + &dev_attr_spi_device_transfer_bytes_histo0.attr, + &dev_attr_spi_device_transfer_bytes_histo1.attr, + &dev_attr_spi_device_transfer_bytes_histo2.attr, + &dev_attr_spi_device_transfer_bytes_histo3.attr, + &dev_attr_spi_device_transfer_bytes_histo4.attr, + &dev_attr_spi_device_transfer_bytes_histo5.attr, + &dev_attr_spi_device_transfer_bytes_histo6.attr, + &dev_attr_spi_device_transfer_bytes_histo7.attr, + &dev_attr_spi_device_transfer_bytes_histo8.attr, + &dev_attr_spi_device_transfer_bytes_histo9.attr, + &dev_attr_spi_device_transfer_bytes_histo10.attr, + &dev_attr_spi_device_transfer_bytes_histo11.attr, + &dev_attr_spi_device_transfer_bytes_histo12.attr, + &dev_attr_spi_device_transfer_bytes_histo13.attr, + &dev_attr_spi_device_transfer_bytes_histo14.attr, + &dev_attr_spi_device_transfer_bytes_histo15.attr, + &dev_attr_spi_device_transfer_bytes_histo16.attr, NULL, }; @@ -168,6 +207,23 @@ static struct attribute *spi_master_statistics_attrs[] = { &dev_attr_spi_master_bytes.attr, &dev_attr_spi_master_bytes_rx.attr, &dev_attr_spi_master_bytes_tx.attr, + &dev_attr_spi_master_transfer_bytes_histo0.attr, + &dev_attr_spi_master_transfer_bytes_histo1.attr, + &dev_attr_spi_master_transfer_bytes_histo2.attr, + &dev_attr_spi_master_transfer_bytes_histo3.attr, + &dev_attr_spi_master_transfer_bytes_histo4.attr, + &dev_attr_spi_master_transfer_bytes_histo5.attr, + &dev_attr_spi_master_transfer_bytes_histo6.attr, + &dev_attr_spi_master_transfer_bytes_histo7.attr, + &dev_attr_spi_master_transfer_bytes_histo8.attr, + &dev_attr_spi_master_transfer_bytes_histo9.attr, + &dev_attr_spi_master_transfer_bytes_histo10.attr, + &dev_attr_spi_master_transfer_bytes_histo11.attr, + &dev_attr_spi_master_transfer_bytes_histo12.attr, + &dev_attr_spi_master_transfer_bytes_histo13.attr, + &dev_attr_spi_master_transfer_bytes_histo14.attr, + &dev_attr_spi_master_transfer_bytes_histo15.attr, + &dev_attr_spi_master_transfer_bytes_histo16.attr, NULL, }; @@ -186,10 +242,15 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats, struct spi_master *master) { unsigned long flags; + int l2len = min(fls(xfer->len), SPI_STATISTICS_HISTO_SIZE) - 1; + + if (l2len < 0) + l2len = 0; spin_lock_irqsave(&stats->lock, flags); stats->transfers++; + stats->transfer_bytes_histo[l2len]++; stats->bytes += xfer->len; if ((xfer->tx_buf) && @@ -270,15 +331,24 @@ EXPORT_SYMBOL_GPL(spi_bus_type); static int spi_drv_probe(struct device *dev) { const struct spi_driver *sdrv = to_spi_driver(dev->driver); + struct spi_device *spi = to_spi_device(dev); int ret; ret = of_clk_set_defaults(dev->of_node, false); if (ret) return ret; + if (dev->of_node) { + spi->irq = of_irq_get(dev->of_node, 0); + if (spi->irq == -EPROBE_DEFER) + return -EPROBE_DEFER; + if (spi->irq < 0) + spi->irq = 0; + } + ret = dev_pm_domain_attach(dev, true); if (ret != -EPROBE_DEFER) { - ret = sdrv->probe(to_spi_device(dev)); + ret = sdrv->probe(spi); if (ret) dev_pm_domain_detach(dev, true); } @@ -308,6 +378,8 @@ static void spi_drv_shutdown(struct device *dev) * spi_register_driver - register a SPI driver * @sdrv: the driver to register * Context: can sleep + * + * Return: zero on success, else a negative error code. */ int spi_register_driver(struct spi_driver *sdrv) { @@ -359,7 +431,7 @@ static DEFINE_MUTEX(board_lock); * needs to discard the spi_device without adding it, then it should * call spi_dev_put() on it. * - * Returns a pointer to the new device, or NULL. + * Return: a pointer to the new device, or NULL. */ struct spi_device *spi_alloc_device(struct spi_master *master) { @@ -418,7 +490,7 @@ static int spi_dev_check(struct device *dev, void *data) * Companion function to spi_alloc_device. Devices allocated with * spi_alloc_device can be added onto the spi bus with this function. * - * Returns 0 on success; negative errno on failure + * Return: 0 on success; negative errno on failure */ int spi_add_device(struct spi_device *spi) { @@ -491,7 +563,7 @@ EXPORT_SYMBOL_GPL(spi_add_device); * this is exported so that for example a USB or parport based adapter * driver could add devices (which it would learn about out-of-band). * - * Returns the new device, or NULL. + * Return: the new device, or NULL. */ struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip) @@ -563,6 +635,8 @@ static void spi_match_master_to_boardinfo(struct spi_master *master, * * The board info passed can safely be __initdata ... but be careful of * any embedded pointers (platform_data, etc), they're copied as-is. + * + * Return: zero on success, else a negative error code. */ int spi_register_board_info(struct spi_board_info const *info, unsigned n) { @@ -597,7 +671,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable) if (spi->mode & SPI_CS_HIGH) enable = !enable; - if (spi->cs_gpio >= 0) + if (gpio_is_valid(spi->cs_gpio)) gpio_set_value(spi->cs_gpio, !enable); else if (spi->master->set_cs) spi->master->set_cs(spi, !enable); @@ -1140,6 +1214,8 @@ static int spi_init_queue(struct spi_master *master) * * If there are more messages in the queue, the next message is returned from * this call. + * + * Return: the next message in the queue, else NULL if the queue is empty. */ struct spi_message *spi_get_next_queued_message(struct spi_master *master) { @@ -1303,6 +1379,8 @@ static int __spi_queued_transfer(struct spi_device *spi, * spi_queued_transfer - transfer function for queued transfers * @spi: spi device which is requesting transfer * @msg: spi message which is to handled is queued to driver queue + * + * Return: zero on success, else a negative error code. */ static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) { @@ -1433,9 +1511,6 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc) } spi->max_speed_hz = value; - /* IRQ */ - spi->irq = irq_of_parse_and_map(nc, 0); - /* Store a pointer to the node in the device structure */ of_node_get(nc); spi->dev.of_node = nc; @@ -1605,13 +1680,13 @@ static struct class spi_master_class = { * only ones directly touching chip registers. It's how they allocate * an spi_master structure, prior to calling spi_register_master(). * - * This must be called from context that can sleep. It returns the SPI - * master structure on success, else NULL. + * This must be called from context that can sleep. * * The caller is responsible for assigning the bus number and initializing * the master's methods before calling spi_register_master(); and (after errors - * adding the device) calling spi_master_put() and kfree() to prevent a memory - * leak. + * adding the device) calling spi_master_put() to prevent a memory leak. + * + * Return: the SPI master structure on success, else NULL. */ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { @@ -1695,6 +1770,8 @@ static int of_spi_register_master(struct spi_master *master) * success, else a negative error code (dropping the master's refcount). * After a successful return, the caller is responsible for calling * spi_unregister_master(). + * + * Return: zero on success, else a negative error code. */ int spi_register_master(struct spi_master *master) { @@ -1788,6 +1865,8 @@ static void devm_spi_unregister(struct device *dev, void *res) * * Register a SPI device as with spi_register_master() which will * automatically be unregister + * + * Return: zero on success, else a negative error code. */ int devm_spi_register_master(struct device *dev, struct spi_master *master) { @@ -1893,6 +1972,8 @@ static int __spi_master_match(struct device *dev, const void *data) * arch init time. It returns a refcounted pointer to the relevant * spi_master (which the caller must release), or NULL if there is * no such master registered. + * + * Return: the SPI master structure on success, else NULL. */ struct spi_master *spi_busnum_to_master(u16 bus_num) { @@ -1946,11 +2027,13 @@ static int __spi_validate_bits_per_word(struct spi_master *master, u8 bits_per_w * that the underlying controller or its driver does not support. For * example, not all hardware supports wire transfers using nine bit words, * LSB-first wire encoding, or active-high chipselects. + * + * Return: zero on success, else a negative error code. */ int spi_setup(struct spi_device *spi) { unsigned bad_bits, ugly_bits; - int status = 0; + int status; /* check mode to prevent that DUAL and QUAD set at the same time */ @@ -1987,17 +2070,18 @@ int spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - if (__spi_validate_bits_per_word(spi->master, spi->bits_per_word)) - return -EINVAL; + status = __spi_validate_bits_per_word(spi->master, spi->bits_per_word); + if (status) + return status; if (!spi->max_speed_hz) spi->max_speed_hz = spi->master->max_speed_hz; - spi_set_cs(spi, false); - if (spi->master->setup) status = spi->master->setup(spi); + spi_set_cs(spi, false); + dev_dbg(&spi->dev, "setup mode %d, %s%s%s%s%u bits/w, %u Hz max --> %d\n", (int) (spi->mode & (SPI_CPOL | SPI_CPHA)), (spi->mode & SPI_CS_HIGH) ? "cs_high, " : "", @@ -2163,6 +2247,8 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message) * no other spi_message queued to that device will be processed. * (This rule applies equally to all the synchronous transfer calls, * which are wrappers around this core asynchronous primitive.) + * + * Return: zero on success, else a negative error code. */ int spi_async(struct spi_device *spi, struct spi_message *message) { @@ -2215,6 +2301,8 @@ EXPORT_SYMBOL_GPL(spi_async); * no other spi_message queued to that device will be processed. * (This rule applies equally to all the synchronous transfer calls, * which are wrappers around this core asynchronous primitive.) + * + * Return: zero on success, else a negative error code. */ int spi_async_locked(struct spi_device *spi, struct spi_message *message) { @@ -2330,7 +2418,7 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, * Also, the caller is guaranteeing that the memory associated with the * message will not be freed before this call returns. * - * It returns zero on success, else a negative error code. + * Return: zero on success, else a negative error code. */ int spi_sync(struct spi_device *spi, struct spi_message *message) { @@ -2352,7 +2440,7 @@ EXPORT_SYMBOL_GPL(spi_sync); * SPI bus. It has to be preceded by a spi_bus_lock call. The SPI bus must * be released by a spi_bus_unlock call when the exclusive access is over. * - * It returns zero on success, else a negative error code. + * Return: zero on success, else a negative error code. */ int spi_sync_locked(struct spi_device *spi, struct spi_message *message) { @@ -2373,7 +2461,7 @@ EXPORT_SYMBOL_GPL(spi_sync_locked); * exclusive access is over. Data transfer must be done by spi_sync_locked * and spi_async_locked calls when the SPI bus lock is held. * - * It returns zero on success, else a negative error code. + * Return: always zero. */ int spi_bus_lock(struct spi_master *master) { @@ -2402,7 +2490,7 @@ EXPORT_SYMBOL_GPL(spi_bus_lock); * This call releases an SPI bus lock previously obtained by an spi_bus_lock * call. * - * It returns zero on success, else a negative error code. + * Return: always zero. */ int spi_bus_unlock(struct spi_master *master) { @@ -2437,6 +2525,8 @@ static u8 *buf; * portable code should never use this for more than 32 bytes. * Performance-sensitive or bulk transfer code should instead use * spi_{async,sync}() calls with dma-safe buffers. + * + * Return: zero on success, else a negative error code. */ int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index fba92a5..ef008e5 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -651,7 +651,8 @@ static int spidev_release(struct inode *inode, struct file *filp) kfree(spidev->rx_buffer); spidev->rx_buffer = NULL; - spidev->speed_hz = spidev->spi->max_speed_hz; + if (spidev->spi) + spidev->speed_hz = spidev->spi->max_speed_hz; /* ... after we unbound from the underlying device? */ spin_lock_irq(&spidev->spi_lock); |