Skip to contentSkip to conversation

spi: Add driver for Routerboard RB4xx boards

PATCH Superseded Bert Vermeulen 4 participants 5 messages Mar 30, 2015 – Mar 31, 2015

Subsystems (2)

v2 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v2 cover spi: Add driver for Routerboard RB4xx boards

Changes in v2:
This is a near complete rewrite of the original OpenWrt driver. All comments were taken into account, and the spi_transfer.fast_write flag is gone. Instead, the cs_change flag is used. It's not too bad a hack, as it really does use CS.

The CPLD module will be submitted as an MFD driver, as suggested.

Bert Vermeulen (1):
spi: Add SPI driver for Mikrotik RB4xx series boards

 drivers/spi/Kconfig     |   6 ++
 drivers/spi/Makefile    |   1 +
 drivers/spi/spi-rb4xx.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 drivers/spi/spi-rb4xx.c
Signature · 1 line
1.9.1
#1 lore raw

PATCH v2 1/1 spi: Add SPI driver for Mikrotik RB4xx series boards

This driver mediates access between the connected CPLD and other devices on the bus.

The m25p80-compatible boot flash and (some models) MMC use regular SPI, bitbanged as required by the SoC. However the SPI-connected CPLD has a "fast write" mode, in which two bits are transferred by SPI clock cycle. The second bit is transmitted with the SoC's CS2 pin.

Protocol drivers using this fast write facility signal this by setting the cs_change flag on transfers.

---
 drivers/spi/Kconfig     |   6 ++
 drivers/spi/Makefile    |   1 +
 drivers/spi/spi-rb4xx.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 drivers/spi/spi-rb4xx.c
drivers/spi/Kconfig +6 -0
index ab8dfbe..aa76ce5 100644
@@ -429,6 +429,12 @@ config SPI_ROCKCHIP
 	  The main usecase of this controller is to use spi flash as boot
 	  device.
 
+config SPI_RB4XX
+	tristate "Mikrotik RB4XX SPI master"
+	depends on SPI_MASTER && ATH79_MACH_RB4XX
+	help
+	  SPI controller driver for the Mikrotik RB4xx series boards.
+
 config SPI_RSPI
 	tristate "Renesas RSPI/QSPI controller"
 	depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
drivers/spi/Makefile +1 -0
index d8cbf65..0218f39 100644
@@ -66,6 +66,7 @@ obj-$(CONFIG_SPI_PXA2XX)		+= spi-pxa2xx-platform.o
 obj-$(CONFIG_SPI_PXA2XX_PCI)		+= spi-pxa2xx-pci.o
 obj-$(CONFIG_SPI_QUP)			+= spi-qup.o
 obj-$(CONFIG_SPI_ROCKCHIP)		+= spi-rockchip.o
+obj-$(CONFIG_SPI_RB4XX)			+= spi-rb4xx.o
 obj-$(CONFIG_SPI_RSPI)			+= spi-rspi.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y			:= spi-s3c24xx.o
drivers/spi/spi-rb4xx.c new file +241 -0

241 lines not on this page · raw message

Signature · 1 line
1.9.1
#2 lore raw

Re: [PATCH v2 1/1] spi: Add SPI driver for Mikrotik RB4xx series boards

Quoted 10 lines · Bert Vermeulen

This driver mediates access between the connected CPLD and other devices on the bus.

The m25p80-compatible boot flash and (some models) MMC use regular SPI, bitbanged as required by the SoC. However the SPI-connected CPLD has a "fast write" mode, in which two bits are transferred by SPI clock cycle. The second bit is transmitted with the SoC's CS2 pin.

Protocol drivers using this fast write facility signal this by setting the cs_change flag on transfers.

Looks nicer, still few comments below.

Quoted 44 lines
---
 drivers/spi/Kconfig     |   6 ++
 drivers/spi/Makefile    |   1 +
 drivers/spi/spi-rb4xx.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 248 insertions(+)
 create mode 100644 drivers/spi/spi-rb4xx.c
drivers/spi/Kconfig +6 -0
index ab8dfbe..aa76ce5 100644
@@ -429,6 +429,12 @@ config SPI_ROCKCHIP
          The main usecase of this controller is to use spi flash as boot
          device.
 
+config SPI_RB4XX
+       tristate "Mikrotik RB4XX SPI master"
+       depends on SPI_MASTER && ATH79_MACH_RB4XX
+       help
+         SPI controller driver for the Mikrotik RB4xx series boards.
+
 config SPI_RSPI
        tristate "Renesas RSPI/QSPI controller"
        depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
drivers/spi/Makefile +1 -0
index d8cbf65..0218f39 100644
@@ -66,6 +66,7 @@ obj-$(CONFIG_SPI_PXA2XX)              += spi-pxa2xx-platform.o
 obj-$(CONFIG_SPI_PXA2XX_PCI)           += spi-pxa2xx-pci.o
 obj-$(CONFIG_SPI_QUP)                  += spi-qup.o
 obj-$(CONFIG_SPI_ROCKCHIP)             += spi-rockchip.o
+obj-$(CONFIG_SPI_RB4XX)                        += spi-rb4xx.o
 obj-$(CONFIG_SPI_RSPI)                 += spi-rspi.o
 obj-$(CONFIG_SPI_S3C24XX)              += spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y                       := spi-s3c24xx.o
drivers/spi/spi-rb4xx.c new file +0 -0
new file mode 100644
index 0000000..9bbec9c

drivers/spi/spi-rb4xx.c line 5

Quoted 5 lines
drivers/spi/spi-rb4xx.c new file +5 -0
@@ -0,0 +1,241 @@
+/*
+ * SPI controller driver for the Mikrotik RB4xx boards
+ *
+ * Copyright (C) 2010 Gabor Juhos <[email protected]>
+ *

There are two authors below but only one old copyright. I guess there is should be another one dated 2015.

Quoted 26 lines
drivers/spi/spi-rb4xx.c new file +26 -0
+ * This file was based on the patches for Linux 2.6.27.39 published by
+ * MikroTik for their RouterBoard 4xx series devices.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+
+#include <asm/mach-ath79/ar71xx_regs.h>
+
+#define SPI_CLOCK_FASTEST      0x40
+#define SPI_HZ                 33333334
+#define CPLD_CMD_WRITE_NAND    0x08 /* bulk write mode */
+
+struct rb4xx_spi {
+       void __iomem            *base;
+       struct spi_master       *master;
+};
+

drivers/spi/spi-rb4xx.c line 41

Quoted 10 lines
drivers/spi/spi-rb4xx.c new file +10 -0
+static inline void do_spi_clk(void __iomem *base, u32 spi_ioc, int value)
+{
+       u32 regval;
+
+       regval = spi_ioc;
+       if (value & BIT(0))
+               regval |= AR71XX_SPI_IOC_DO;
+
+       __raw_writel(regval, base + AR71XX_SPI_REG_IOC);
+       __raw_writel(regval | AR71XX_SPI_IOC_CLK, base + AR71XX_SPI_REG_IOC);

So, why to avoid IO accessor helpers?

drivers/spi/spi-rb4xx.c line 48

Quoted 7 lines
drivers/spi/spi-rb4xx.c new file +7 -0
+}
+
+static void do_spi_byte(void __iomem *base, u32 spi_ioc, u8 byte)
+{
+       int i;
+
+       for (i = 7; i >= 0; i--)

Just a matter of style, but you can use while-loop here as well.

Quoted 14 lines
drivers/spi/spi-rb4xx.c new file +14 -0
+               do_spi_clk(base, spi_ioc, byte >> i);
+}
+
+static inline void do_spi_clk_fast(void __iomem *base, u32 spi_ioc, u8 value)
+{
+       u32 regval;
+
+       regval = spi_ioc;
+       if (value & BIT(1))
+               regval |= AR71XX_SPI_IOC_DO;
+       if (value & BIT(0))
+               regval |= AR71XX_SPI_IOC_CS2;
+
+       __raw_writel(regval, base + AR71XX_SPI_REG_IOC);

drivers/spi/spi-rb4xx.c line 72

Quoted 10 lines
drivers/spi/spi-rb4xx.c new file +10 -0
+       __raw_writel(regval | AR71XX_SPI_IOC_CLK, base + AR71XX_SPI_REG_IOC);
+}
+
+/* Two bits at a time, msb first */
+static void do_spi_byte_fast(void __iomem *base, u32 spi_ioc, u8 byte)
+{
+       do_spi_clk_fast(base, spi_ioc, byte >> 6);
+       do_spi_clk_fast(base, spi_ioc, byte >> 4);
+       do_spi_clk_fast(base, spi_ioc, byte >> 2);
+       do_spi_clk_fast(base, spi_ioc, byte);

Just a nitpick, what if explicitly show >> 0?

Quoted 53 lines
drivers/spi/spi-rb4xx.c new file +53 -0
+}
+
+static void set_cs(struct spi_device *spi, bool enable)
+{
+       struct rb4xx_spi *rbspi = spi_master_get_devdata(spi->master);
+
+       /*
+        * Setting CS is done along with bitbanging the actual values,
+        * since it's all on the same hardware register. However the
+        * CPLD needs CS deselected after every command.
+        */
+       if (!enable)
+               __raw_writel(AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1,
+                            rbspi->base + AR71XX_SPI_REG_IOC);
+}
+
+/* Deselect CS0 and CS1. */
+static int unprep(struct spi_master *master)
+{
+       struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
+
+       __raw_writel(AR71XX_SPI_IOC_CS0 | AR71XX_SPI_IOC_CS1,
+                    rbspi->base + AR71XX_SPI_REG_IOC);
+
+       return 0;
+}
+
+static int rb4xx_transfer_one(struct spi_master *master,
+                             struct spi_device *spi, struct spi_transfer *t)
+{
+       struct rb4xx_spi *rbspi = spi_master_get_devdata(master);
+       int i;
+       u32 spi_ioc;
+       u8 *rx_buf;
+       const u8 *tx_buf;
+       unsigned char out;
+
+       /*
+        * Prime the SPI register with the SPI device selected. The m25p80 boot
+        * flash and CPLD share the CS0 pin. This works because the CPLD's
+        * command set was designed to almost not clash with that of the
+        * boot flash.
+        */
+       if (spi->chip_select == 2)
+               spi_ioc = AR71XX_SPI_IOC_CS0;
+       else
+               spi_ioc = AR71XX_SPI_IOC_CS1;
+
+       tx_buf = t->tx_buf;
+       rx_buf = t->rx_buf;
+       for (i = 0; i < t->len; ++i) {
+               out = tx_buf ? tx_buf[i] : 0x00;
+               if (spi->chip_select == 1 && t->cs_change) {

drivers/spi/spi-rb4xx.c line 135

Quoted 10 lines
drivers/spi/spi-rb4xx.c new file +10 -0
+                       /* CPLD in bulk write mode gets two bits per clock */
+                       do_spi_byte_fast(rbspi->base, spi_ioc, out);
+                       /* Don't want the real CS toggled */
+                       t->cs_change = 0;
+               } else {
+                       do_spi_byte(rbspi->base, spi_ioc, out);
+               }
+               if (!rx_buf)
+                       continue;
+               rx_buf[i] = __raw_readl(rbspi->base + AR71XX_SPI_REG_RDS) & 0xff;

Since you have u8 type you don need to & 0xff.

drivers/spi/spi-rb4xx.c line 144

Quoted 9 lines
drivers/spi/spi-rb4xx.c new file +9 -0
+       }
+       spi_finalize_current_transfer(master);
+
+       return 0;
+}
+
+static int rb4xx_spi_setup(struct spi_device *spi)
+{
+       if (spi->mode & ~(SPI_CS_HIGH)) {

Redundant parens.

Quoted 22 lines
drivers/spi/spi-rb4xx.c new file +22 -0
+               dev_err(&spi->dev, "mode %x not supported\n", spi->mode);
+               return -EINVAL;
+       }
+
+       if (spi->bits_per_word != 8 && spi->bits_per_word != 0) {
+               dev_err(&spi->dev, "bits_per_word %u not supported\n",
+                       spi->bits_per_word);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int rb4xx_spi_probe(struct platform_device *pdev)
+{
+       struct spi_master *master;
+       struct clk *ahb_clk;
+       struct rb4xx_spi *rbspi;
+       struct resource *r;
+       int err = 0;
+
+       ahb_clk = devm_clk_get(&pdev->dev, "ahb");

drivers/spi/spi-rb4xx.c line 176

Quoted 10 lines
drivers/spi/spi-rb4xx.c new file +10 -0
+       if (IS_ERR(ahb_clk))
+               return PTR_ERR(ahb_clk);
+
+       err = clk_prepare_enable(ahb_clk);
+       if (err)
+               return err;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r)
+               return -ENOENT;

(a)

Quoted 11 lines
drivers/spi/spi-rb4xx.c new file +11 -0
+
+       master = spi_alloc_master(&pdev->dev, sizeof(*rbspi));
+       if (!master)
+               return -ENOMEM;
+
+       master->bus_num = 0;
+       master->num_chipselect = 3;
+       master->setup = rb4xx_spi_setup;
+       master->transfer_one = rb4xx_transfer_one;
+       master->unprepare_transfer_hardware = unprep;
+       master->set_cs = set_cs;

drivers/spi/spi-rb4xx.c line 197

Quoted 10 lines
drivers/spi/spi-rb4xx.c new file +10 -0
+
+       rbspi = spi_master_get_devdata(master);
+
+       platform_set_drvdata(pdev, rbspi);
+
+       rbspi->base = devm_ioremap_resource(&pdev->dev, r);
+       if (!rbspi->base) {
+               err = -ENXIO;
+               goto err_put_master;
+       }

(b)

First of all you don't need to check for return code in (a) since (b) will do this for you. Second, you have to propagate the actual error code in (b). And third, since you are using devm_* approach you may move this before master allocation and save few lines.

Quoted 15 lines
drivers/spi/spi-rb4xx.c new file +15 -0
+
+       rbspi->master = master;
+
+       err = spi_register_master(master);
+       if (err) {
+               dev_err(&pdev->dev, "failed to register SPI master\n");
+               goto err_put_master;
+       }
+
+       /* Enable SPI */
+       __raw_writel(AR71XX_SPI_FS_GPIO, rbspi->base + AR71XX_SPI_REG_FS);
+
+       return 0;
+
+err_put_master:

drivers/spi/spi-rb4xx.c line 222

Quoted 10 lines
drivers/spi/spi-rb4xx.c new file +10 -0
+       spi_master_put(master);
+
+       return err;
+}
+
+static int rb4xx_spi_remove(struct platform_device *pdev)
+{
+       struct rb4xx_spi *rbspi = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);

This is redundant.

Quoted 22 lines
drivers/spi/spi-rb4xx.c new file +19 -1
+       spi_master_put(rbspi->master);
+
+       return 0;
+}
+
+static struct platform_driver rb4xx_spi_drv = {
+       .probe          = rb4xx_spi_probe,
+       .remove         = rb4xx_spi_remove,
+       .driver         = {
+               .name   = "rb4xx-spi",
+       },
+};
+
+module_platform_driver(rb4xx_spi_drv);
+
+MODULE_DESCRIPTION("Mikrotik RB4xx SPI controller driver");
+MODULE_AUTHOR("Gabor Juhos <[email protected]>");
+MODULE_AUTHOR("Bert Vermeulen <[email protected]>");
+MODULE_LICENSE("GPL v2");
--

1.9.1

Signature · 6 lines
With Best Regards,
Andy Shevchenko
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
maintainer
#3 lore raw
In reply to Bert Vermeulen
Quoted 5 lines · Bert Vermeulen

Changes in v2:
This is a near complete rewrite of the original OpenWrt driver. All comments were taken into account, and the spi_transfer.fast_write flag is gone. Instead, the cs_change flag is used. It's not too bad a hack, as it really does use CS.

Don't send cover letters for single patches, if there's anything that needs saying it should either be in the changelog or after the ---. A separate cover letter adds to the mail volume and probably means that the patch itself is inadequately described.

#4 lore raw
Quoted 11 lines · Mark Brown
Quoted 5 lines · Bert Vermeulen

Changes in v2:
This is a near complete rewrite of the original OpenWrt driver. All comments were taken into account, and the spi_transfer.fast_write flag is gone. Instead, the cs_change flag is used. It's not too bad a hack, as it really does use CS.

Don't send cover letters for single patches, if there's anything that needs saying it should either be in the changelog or after the ---. A separate cover letter adds to the mail volume and probably means that the patch itself is inadequately described.

My personal grief with cover letters is that patchwork only collects patches but cover letters themselves aren't patches. Which means working through patchwork I might miss important information. Maybe I should request an enhancement to patchwork.

Ralf