ramips: use transfer_one instead of transfer_one_message on rt2880 spi

* use kernel buildin transfer_one_message. we only need to implement
transfer_one and set_cs function
* should support use gpio as cs pin
* deselected the spi device when setup and add debug info
* only reset device when first driver probe

Signed-off-by: Michael Lee <igvtee@gmail.com>

SVN-Revision: 47579
This commit is contained in:
John Crispin 2015-11-22 11:49:04 +00:00
parent 95aa28da81
commit a941b34008

View file

@ -41,7 +41,7 @@ Acked-by: John Crispin <blogic@openwrt.org>
spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
--- /dev/null --- /dev/null
+++ b/drivers/spi/spi-rt2880.c +++ b/drivers/spi/spi-rt2880.c
@@ -0,0 +1,533 @@ @@ -0,0 +1,530 @@
+/* +/*
+ * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver + * spi-rt2880.c -- Ralink RT288x/RT305x SPI controller driver
+ * + *
@ -66,10 +66,9 @@ Acked-by: John Crispin <blogic@openwrt.org>
+#include <linux/reset.h> +#include <linux/reset.h>
+#include <linux/spi/spi.h> +#include <linux/spi/spi.h>
+#include <linux/platform_device.h> +#include <linux/platform_device.h>
+#include <linux/gpio.h>
+ +
+#define DRIVER_NAME "spi-rt2880" +#define DRIVER_NAME "spi-rt2880"
+/* only one slave is supported*/
+#define RALINK_NUM_CHIPSELECTS 1
+ +
+#define RAMIPS_SPI_STAT 0x00 +#define RAMIPS_SPI_STAT 0x00
+#define RAMIPS_SPI_CFG 0x10 +#define RAMIPS_SPI_CFG 0x10
@ -169,6 +168,8 @@ Acked-by: John Crispin <blogic@openwrt.org>
+#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \ +#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | \
+ SPI_CS_HIGH) + SPI_CS_HIGH)
+ +
+static atomic_t hw_reset_count = ATOMIC_INIT(0);
+
+struct rt2880_spi { +struct rt2880_spi {
+ struct spi_master *master; + struct spi_master *master;
+ void __iomem *base; + void __iomem *base;
@ -248,12 +249,14 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ return offset; + return offset;
+} +}
+ +
+static void rt2880_spi_set_cs(struct rt2880_spi *rs, int enable) +static void rt2880_spi_set_cs(struct spi_device *spi, bool enable)
+{ +{
+ struct rt2880_spi *rs = spidev_to_rt2880_spi(spi);
+
+ if (enable) + if (enable)
+ rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
+ else
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA); + rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
+ else
+ rt2880_spi_clrbits(rs, RAMIPS_SPI_CTL, SPICTL_SPIENA);
+} +}
+ +
+static int rt2880_spi_wait_ready(struct rt2880_spi *rs, int len) +static int rt2880_spi_wait_ready(struct rt2880_spi *rs, int len)
@ -269,22 +272,41 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ return -ETIMEDOUT; + return -ETIMEDOUT;
+} +}
+ +
+static unsigned int +static void rt2880_dump_reg(struct spi_master *master)
+rt2880_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
+{ +{
+ struct rt2880_spi *rs = spidev_to_rt2880_spi(spi); + struct rt2880_spi *rs = spi_master_get_devdata(master);
+ unsigned count = 0;
+ u8 *rx = xfer->rx_buf;
+ const u8 *tx = xfer->tx_buf;
+ int err;
+ +
+ dev_dbg(&spi->dev, "read (%d): %s %s\n", xfer->len, + dev_dbg(&master->dev, "stat: %08x, cfg: %08x, ctl: %08x, " \
+ (tx != NULL) ? "tx" : " ", + "data: %08x, arb: %08x\n",
+ (rx != NULL) ? "rx" : " "); + rt2880_spi_read(rs, RAMIPS_SPI_STAT),
+ rt2880_spi_read(rs, RAMIPS_SPI_CFG),
+ rt2880_spi_read(rs, RAMIPS_SPI_CTL),
+ rt2880_spi_read(rs, RAMIPS_SPI_DATA),
+ rt2880_spi_read(rs, get_arbiter_offset(master)));
+}
+
+static int rt2880_spi_transfer_one(struct spi_master *master,
+ struct spi_device *spi, struct spi_transfer *xfer)
+{
+ struct rt2880_spi *rs = spi_master_get_devdata(master);
+ unsigned len;
+ const u8 *tx = xfer->tx_buf;
+ u8 *rx = xfer->rx_buf;
+ int err = 0;
+
+ /* change clock speed */
+ if (unlikely(rs->speed != xfer->speed_hz)) {
+ u32 reg;
+ reg = rt2880_spi_read(rs, RAMIPS_SPI_CFG);
+ reg &= ~SPICFG_SPICLK_PRESCALE_MASK;
+ reg |= rt2880_spi_baudrate_get(spi, xfer->speed_hz);
+ rt2880_spi_write(rs, RAMIPS_SPI_CFG, reg);
+ }
+ +
+ if (tx) { + if (tx) {
+ for (count = 0; count < xfer->len; count++) { + len = xfer->len;
+ rt2880_spi_write(rs, RAMIPS_SPI_DATA, tx[count]); + while (len-- > 0) {
+ rt2880_spi_write(rs, RAMIPS_SPI_DATA, *tx++);
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR); + rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTWR);
+ err = rt2880_spi_wait_ready(rs, 1); + err = rt2880_spi_wait_ready(rs, 1);
+ if (err) { + if (err) {
@ -295,63 +317,32 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ } + }
+ +
+ if (rx) { + if (rx) {
+ for (count = 0; count < xfer->len; count++) { + len = xfer->len;
+ while (len-- > 0) {
+ rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD); + rt2880_spi_setbits(rs, RAMIPS_SPI_CTL, SPICTL_STARTRD);
+ err = rt2880_spi_wait_ready(rs, 1); + err = rt2880_spi_wait_ready(rs, 1);
+ if (err) { + if (err) {
+ dev_err(&spi->dev, "RX failed, err=%d\n", err); + dev_err(&spi->dev, "RX failed, err=%d\n", err);
+ goto out; + goto out;
+ } + }
+ rx[count] = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA); + *rx++ = (u8) rt2880_spi_read(rs, RAMIPS_SPI_DATA);
+ } + }
+ } + }
+ +
+out: +out:
+ return count; + return err;
+} +}
+ +
+static int rt2880_spi_transfer_one_message(struct spi_master *master, +/* copy from spi.c */
+ struct spi_message *m) +static void spi_set_cs(struct spi_device *spi, bool enable)
+{ +{
+ struct rt2880_spi *rs = spi_master_get_devdata(master); + if (spi->mode & SPI_CS_HIGH)
+ struct spi_device *spi = m->spi; + enable = !enable;
+ struct spi_transfer *t = NULL;
+ int status = 0;
+ int cs_active = 0;
+ +
+ list_for_each_entry(t, &m->transfers, transfer_list) { + if (spi->cs_gpio >= 0)
+ if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + gpio_set_value(spi->cs_gpio, !enable);
+ dev_err(&spi->dev, + else if (spi->master->set_cs)
+ "message rejected: invalid transfer data buffers\n"); + spi->master->set_cs(spi, !enable);
+ status = -EIO;
+ goto msg_done;
+ }
+
+ if (!cs_active) {
+ rt2880_spi_set_cs(rs, 1);
+ cs_active = 1;
+ }
+
+ if (t->len)
+ m->actual_length += rt2880_spi_write_read(spi, t);
+
+ if (t->delay_usecs)
+ udelay(t->delay_usecs);
+
+ if (t->cs_change) {
+ rt2880_spi_set_cs(rs, 0);
+ cs_active = 0;
+ }
+ }
+
+msg_done:
+ if (cs_active)
+ rt2880_spi_set_cs(rs, 0);
+
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+} +}
+ +
+static int rt2880_spi_setup(struct spi_device *spi) +static int rt2880_spi_setup(struct spi_device *spi)
@ -410,6 +401,11 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ if (reg != old_reg) + if (reg != old_reg)
+ rt2880_spi_write(rs, arbit_off, reg); + rt2880_spi_write(rs, arbit_off, reg);
+ +
+ /* deselected the spi device */
+ spi_set_cs(spi, false);
+
+ rt2880_dump_reg(master);
+
+ return 0; + return 0;
+} +}
+ +
@ -508,8 +504,8 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ master->flags = SPI_MASTER_HALF_DUPLEX; + master->flags = SPI_MASTER_HALF_DUPLEX;
+ master->setup = rt2880_spi_setup; + master->setup = rt2880_spi_setup;
+ master->prepare_message = rt2880_spi_prepare_message; + master->prepare_message = rt2880_spi_prepare_message;
+ master->transfer_one_message = rt2880_spi_transfer_one_message; + master->set_cs = rt2880_spi_set_cs;
+ master->num_chipselect = RALINK_NUM_CHIPSELECTS; + master->transfer_one = rt2880_spi_transfer_one,
+ +
+ dev_set_drvdata(&pdev->dev, master); + dev_set_drvdata(&pdev->dev, master);
+ +
@ -518,9 +514,9 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ rs->base = base; + rs->base = base;
+ rs->clk = clk; + rs->clk = clk;
+ +
+ if (atomic_inc_return(&hw_reset_count) == 1)
+ device_reset(&pdev->dev); + device_reset(&pdev->dev);
+ +
+
+ ret = devm_spi_register_master(&pdev->dev, master); + ret = devm_spi_register_master(&pdev->dev, master);
+ if (ret < 0) { + if (ret < 0) {
+ dev_err(&pdev->dev, "devm_spi_register_master error.\n"); + dev_err(&pdev->dev, "devm_spi_register_master error.\n");
@ -547,6 +543,7 @@ Acked-by: John Crispin <blogic@openwrt.org>
+ rs = spi_master_get_devdata(master); + rs = spi_master_get_devdata(master);
+ +
+ clk_disable_unprepare(rs->clk); + clk_disable_unprepare(rs->clk);
+ atomic_dec(&hw_reset_count);
+ +
+ return 0; + return 0;
+} +}