This adds SPI flash support for the sunxi SoCs to U-boot.
This code is taken from here:
https://github.com/StephanvanSchaik/u-boot/tree/sunxi-spi
Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
--- /dev/null
+--- a/drivers/mtd/spi/Makefile
++++ b/drivers/mtd/spi/Makefile
+@@ -12,7 +12,9 @@ obj-$(CONFIG_SPL_SPI_BOOT) += fsl_espi_s
+ obj-$(CONFIG_SPL_SPI_SUNXI) += sunxi_spi_spl.o
+ endif
+
++ifndef CONFIG_SPL_SPI_SUNXI
+ obj-$(CONFIG_SPI_FLASH) += sf_probe.o spi_flash.o spi_flash_ids.o sf.o
+ obj-$(CONFIG_SPI_FLASH_DATAFLASH) += sf_dataflash.o
+ obj-$(CONFIG_SPI_FLASH_MTD) += sf_mtd.o
+ obj-$(CONFIG_SPI_FLASH_SANDBOX) += sandbox.o
++endif
--- /dev/null
+From bc7fe28b76b7e99a0bfc23bdb7dbcecc82069fa9 Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@synkhronix.com>
+Date: Fri, 10 Feb 2017 12:04:17 +0000
+Subject: sunxi: add missing AHB_GATE_OFFSET_SPIx defines for sun6i/sun9i
+
+Added missing AHB_GATE_OFFSET_SPIx defines to enable/disable clock gating for
+SPI on the sun6i and sun9i platforms.
+
+Signed-off-by: S.J.R. van Schaik <stephan@whiteboxsystems.nl>
+---
+ arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 ++
+ arch/arm/include/asm/arch-sunxi/clock_sun9i.h | 4 ++++
+ 2 files changed, 6 insertions(+)
+
+--- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
++++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h
+@@ -290,6 +290,8 @@ struct sunxi_ccm_reg {
+ #define AHB_GATE_OFFSET_USB0 25
+ #define AHB_GATE_OFFSET_SATA 24
+ #endif
++#define AHB_GATE_OFFSET_SPI1 21
++#define AHB_GATE_OFFSET_SPI0 20
+ #define AHB_GATE_OFFSET_MCTL 14
+ #define AHB_GATE_OFFSET_GMAC 17
+ #define AHB_GATE_OFFSET_NAND0 13
+--- a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
++++ b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h
+@@ -195,6 +195,10 @@ struct sunxi_ccm_reg {
+
+ /* ahb gate1 field */
+ #define AHB_GATE_OFFSET_DMA 24
++#define AHB_GATE_OFFSET_SPI3 23
++#define AHB_GATE_OFFSET_SPI2 22
++#define AHB_GATE_OFFSET_SPI1 21
++#define AHB_GATE_OFFSET_SPI0 20
+
+ /* apb1_gate fields */
+ #define APB1_GATE_UART_SHIFT 16
--- /dev/null
+From 14b309992da44fab80fe4552b8f5bdef630f6f1f Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@synkhronix.com>
+Date: Fri, 10 Feb 2017 11:24:37 +0000
+Subject: sunxi: add SPI register definitions for sun4i/sun7i
+
+Introduces SPI registers for sun4i/sun7i by adding struct sunxi_spi_regs
+and flags.
+
+Signed-off-by: S.J.R. van Schaik <stephan@whiteboxsystems.nl>
+---
+ arch/arm/include/asm/arch-sunxi/spi.h | 29 ++++++++++++++++
+ arch/arm/include/asm/arch-sunxi/spi_sun4i.h | 53 +++++++++++++++++++++++++++++
+ 2 files changed, 82 insertions(+)
+ create mode 100644 arch/arm/include/asm/arch-sunxi/spi.h
+ create mode 100644 arch/arm/include/asm/arch-sunxi/spi_sun4i.h
+
+--- /dev/null
++++ b/arch/arm/include/asm/arch-sunxi/spi.h
+@@ -0,0 +1,29 @@
++/*
++ * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V.
++ * S.J.R. van Schaik <stephan@whiteboxsystems.nl>
++ * M.B.W. Wajer <merlijn@whiteboxsystems.nl>
++ *
++ * SPDX-License-Identifier: GPL-2.0+
++ */
++
++#ifndef _SUNXI_SPI_H
++#define _SUNXI_SPI_H
++
++#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) || \
++ defined(CONFIG_MACH_SUN9I) || defined(CONFIG_MACH_SUN50I)
++#include <asm/arch/spi_sun6i.h>
++#else
++#include <asm/arch/spi_sun4i.h>
++#endif
++
++#define SUNXI_SPI_BURST_CNT(cnt) ((cnt) & 0xffffff)
++#define SUNXI_SPI_XMIT_CNT(cnt) ((cnt) & 0xffffff)
++
++#define SUNXI_SPI_CLK_CTL_CDR2_MASK 0xff
++#define SUNXI_SPI_CLK_CTL_CDR2(div) ((div) & SUNXI_SPI_CLK_CTL_CDR2_MASK)
++#define SUNXI_SPI_CLK_CTL_CDR1_MASK 0xf
++#define SUNXI_SPI_CLK_CTL_CDR1(div) \
++ (((div) & SUNXI_SPI_CLK_CTL_CDR1_MASK) << 8)
++#define SUNXI_SPI_CLK_CTL_DRS BIT(12)
++
++#endif /* _SUNXI_SPI_H */
+--- /dev/null
++++ b/arch/arm/include/asm/arch-sunxi/spi_sun4i.h
+@@ -0,0 +1,53 @@
++/*
++ * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V.
++ * S.J.R. van Schaik <stephan@whiteboxsystems.nl>
++ * M.B.W. Wajer <merlijn@whiteboxsystems.nl>
++ *
++ * SPDX-License-Identifier: GPL-2.0+
++ */
++
++#ifndef _SUNXI_SPI_SUN4I_H
++#define _SUNXI_SPI_SUN4I_H
++
++struct sunxi_spi_regs {
++ uint32_t rx_data; /* 0x00 */
++ uint32_t tx_data; /* 0x04 */
++ union {
++ uint32_t glb_ctl;
++ uint32_t xfer_ctl;
++ uint32_t fifo_ctl;
++ uint32_t burst_ctl;
++ }; /* 0x08 */
++ uint32_t int_ctl; /* 0x0c */
++ uint32_t int_sta; /* 0x10 */
++ uint32_t dma_ctl; /* 0x14 */
++ uint32_t wait; /* 0x18 */
++ uint32_t clk_ctl; /* 0x1c */
++ uint32_t burst_cnt; /* 0x20 */
++ uint32_t xmit_cnt; /* 0x24 */
++ uint32_t fifo_sta; /* 0x28 */
++};
++
++#define SUNXI_SPI_CTL_SRST 0
++
++#define SUNXI_SPI_CTL_ENABLE BIT(0)
++#define SUNXI_SPI_CTL_MASTER BIT(1)
++#define SUNXI_SPI_CTL_CPHA BIT(2)
++#define SUNXI_SPI_CTL_CPOL BIT(3)
++#define SUNXI_SPI_CTL_CS_ACTIVE_LOW BIT(4)
++#define SUNXI_SPI_CTL_TF_RST BIT(8)
++#define SUNXI_SPI_CTL_RF_RST BIT(9)
++#define SUNXI_SPI_CTL_XCH BIT(10)
++#define SUNXI_SPI_CTL_CS_MASK 0x3000
++#define SUNXI_SPI_CTL_CS(cs) (((cs) << 12) & SUNXI_SPI_CTL_CS_MASK)
++#define SUNXI_SPI_CTL_DHB BIT(15)
++#define SUNXI_SPI_CTL_CS_MANUAL BIT(16)
++#define SUNXI_SPI_CTL_CS_LEVEL BIT(17)
++#define SUNXI_SPI_CTL_TP BIT(18)
++
++#define SUNXI_SPI_FIFO_RF_CNT_MASK 0x7f
++#define SUNXI_SPI_FIFO_RF_CNT_BITS 0
++#define SUNXI_SPI_FIFO_TF_CNT_MASK 0x7f
++#define SUNXI_SPI_FIFO_TF_CNT_BITS 16
++
++#endif /* _SUNXI_SPI_SUN4I_H */
--- /dev/null
+From 2be081c6d037107dd8bc2e824ae61d4429aa64a1 Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@synkhronix.com>
+Date: Fri, 10 Feb 2017 11:25:56 +0000
+Subject: sunxi: add SPI register definitions for sun6i/sun8i/sun9i/sun50i
+
+Introduces SPI registers for sun6i/sun8i/sun9i/sun50i by adding struct
+sunxi_spi_regs and flags.
+
+Signed-off-by: S.J.R. van Schaik <stephan@whiteboxsystems.nl>
+---
+ arch/arm/include/asm/arch-sunxi/spi_sun6i.h | 56 +++++++++++++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+ create mode 100644 arch/arm/include/asm/arch-sunxi/spi_sun6i.h
+
+--- /dev/null
++++ b/arch/arm/include/asm/arch-sunxi/spi_sun6i.h
+@@ -0,0 +1,56 @@
++/*
++ * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V.
++ * S.J.R. van Schaik <stephan@whiteboxsystems.nl>
++ * M.B.W. Wajer <merlijn@whiteboxsystems.nl>
++ *
++ * SPDX-License-Identifier: GPL-2.0+
++ */
++
++#ifndef _SUNXI_SPI_SUN6I_H
++#define _SUNXI_SPI_SUN6I_H
++
++struct sunxi_spi_regs {
++ uint32_t unused0[1];
++ uint32_t glb_ctl; /* 0x04 */
++ uint32_t xfer_ctl; /* 0x08 */
++ uint32_t unused1[1];
++ uint32_t int_ctl; /* 0x10 */
++ uint32_t int_sta; /* 0x14 */
++ uint32_t fifo_ctl; /* 0x18 */
++ uint32_t fifo_sta; /* 0x1c */
++ uint32_t wait; /* 0x20 */
++ uint32_t clk_ctl; /* 0x24 */
++ uint32_t unused2[2];
++ uint32_t burst_cnt; /* 0x30 */
++ uint32_t xmit_cnt; /* 0x34 */
++ uint32_t burst_ctl; /* 0x38 */
++ uint32_t unused3[113];
++ uint32_t tx_data; /* 0x200 */
++ uint32_t unused4[63];
++ uint32_t rx_data; /* 0x300 */
++};
++
++#define SUNXI_SPI_CTL_ENABLE BIT(0)
++#define SUNXI_SPI_CTL_MASTER BIT(1)
++#define SUNXI_SPI_CTL_TP BIT(7)
++#define SUNXI_SPI_CTL_SRST BIT(31)
++
++#define SUNXI_SPI_CTL_CPHA BIT(0)
++#define SUNXI_SPI_CTL_CPOL BIT(1)
++#define SUNXI_SPI_CTL_CS_ACTIVE_LOW BIT(2)
++#define SUNXI_SPI_CTL_CS_MASK 0x30
++#define SUNXI_SPI_CTL_CS(cs) (((cs) << 4) & SUNXI_SPI_CTL_CS_MASK)
++#define SUNXI_SPI_CTL_CS_MANUAL BIT(6)
++#define SUNXI_SPI_CTL_CS_LEVEL BIT(7)
++#define SUNXI_SPI_CTL_DHB BIT(8)
++#define SUNXI_SPI_CTL_XCH BIT(31)
++
++#define SUNXI_SPI_CTL_RF_RST BIT(15)
++#define SUNXI_SPI_CTL_TF_RST BIT(31)
++
++#define SUNXI_SPI_FIFO_RF_CNT_MASK 0x7f
++#define SUNXI_SPI_FIFO_RF_CNT_BITS 0
++#define SUNXI_SPI_FIFO_TF_CNT_MASK 0x7f
++#define SUNXI_SPI_FIFO_TF_CNT_BITS 16
++
++#endif /* _SUNXI_SPI_SUN6I_H */
--- /dev/null
+From 34e5d3bc4e50233cdd445ef371f06d33ce71e4b0 Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@synkhronix.com>
+Date: Fri, 10 Feb 2017 12:32:49 +0000
+Subject: sunxi: add SPI driver for Allwinner devices (sunxi)
+
+Implements a driver model SPI driver for Allwinner devices (sunxi).
+
+Signed-off-by: S.J.R. van Schaik <stephan@whiteboxsystems.nl>
+---
+ drivers/spi/Kconfig | 5 +
+ drivers/spi/Makefile | 1 +
+ drivers/spi/sunxi_spi.c | 355 ++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 361 insertions(+)
+ create mode 100644 drivers/spi/sunxi_spi.c
+
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -148,6 +148,11 @@ config STM32_QSPI
+ used to access the SPI NOR flash chips on platforms embedding
+ this ST IP core.
+
++config SUNXI_SPI
++ bool "Allwinner SPI driver"
++ help
++ Enable the Allwinner SPI driver.
++
+ config TEGRA114_SPI
+ bool "nVidia Tegra114 SPI driver"
+ help
+--- a/drivers/spi/Makefile
++++ b/drivers/spi/Makefile
+@@ -44,6 +44,7 @@ obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi
+ obj-$(CONFIG_SH_SPI) += sh_spi.o
+ obj-$(CONFIG_SH_QSPI) += sh_qspi.o
+ obj-$(CONFIG_STM32_QSPI) += stm32_qspi.o
++obj-$(CONFIG_SUNXI_SPI) += sunxi_spi.o
+ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
+ obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
+ obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
+--- /dev/null
++++ b/drivers/spi/sunxi_spi.c
+@@ -0,0 +1,355 @@
++/*
++ * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V.
++ * S.J.R. van Schaik <stephan@whiteboxsystems.nl>
++ * M.B.W. Wajer <merlijn@whiteboxsystems.nl>
++ *
++ * SPDX-License-Identifier: GPL-2.0+
++ */
++
++#include <common.h>
++#include <dm.h>
++#include <errno.h>
++#include <spi.h>
++
++#include <asm/bitops.h>
++#include <asm/gpio.h>
++#include <asm/io.h>
++
++#include <asm/arch/clock.h>
++#include <asm/arch/spi.h>
++
++#define SUNXI_SPI_MAX_RATE (24 * 1000 * 1000)
++#define SUNXI_SPI_MIN_RATE (3 * 1000)
++
++struct sunxi_spi_platdata {
++ struct sunxi_spi_regs *regs;
++ unsigned int activate_delay_us;
++ unsigned int deactivate_delay_us;
++ uint32_t freq;
++};
++
++struct sunxi_spi_priv {
++ struct sunxi_spi_regs *regs;
++ unsigned int max_freq;
++ unsigned int last_transaction_us;
++};
++
++DECLARE_GLOBAL_DATA_PTR;
++
++static void sunxi_spi_setup_pinmux(unsigned int pin_func)
++{
++ unsigned int pin;
++
++ for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++)
++ sunxi_gpio_set_cfgpin(pin, pin_func);
++
++ if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I)) {
++ sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_func);
++ } else {
++ sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_func);
++ }
++}
++
++static void sunxi_spi_enable_clock(struct udevice *bus)
++{
++ struct sunxi_ccm_reg * const ccm =
++ (struct sunxi_ccm_reg * const)SUNXI_CCM_BASE;
++
++#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) || \
++ defined(CONFIG_MACH_SUN9I) || defined(CONFIG_MACH_SUN50I)
++ setbits_le32(&ccm->ahb_reset0_cfg,
++ (1 << AHB_GATE_OFFSET_SPI0));
++#endif
++
++ setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_SPI0));
++ writel((1 << 31), &ccm->spi0_clk_cfg);
++}
++
++static void sunxi_spi_disable_clock(void)
++{
++ struct sunxi_ccm_reg * const ccm =
++ (struct sunxi_ccm_reg * const)SUNXI_CCM_BASE;
++
++ writel(0, &ccm->spi0_clk_cfg);
++ clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_SPI0));
++
++#if defined(CONFIG_MACH_SUN6I) || defined(CONFIG_MACH_SUN8I) || \
++ defined(CONFIG_MACH_SUN9I) || defined(CONFIG_MACH_SUN50I)
++ clrbits_le32(&ccm->ahb_reset0_cfg,
++ (1 << AHB_GATE_OFFSET_SPI0));
++#endif
++}
++
++static void sunxi_spi_cs_activate(struct udevice *dev, unsigned int cs)
++{
++ struct udevice *bus = dev->parent;
++ struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ uint32_t reg;
++
++ /* If it is too soon to perform another transaction, wait. */
++ if (plat->deactivate_delay_us && priv->last_transaction_us) {
++ unsigned int delay_us;
++
++ delay_us = timer_get_us() - priv->last_transaction_us;
++
++ if (delay_us < plat->deactivate_delay_us)
++ udelay(plat->deactivate_delay_us - delay_us);
++ }
++
++ debug("%s: activate cs: %u, bus: '%s'\n", __func__, cs, bus->name);
++
++ reg = readl(&priv->regs->xfer_ctl);
++ reg &= ~(SUNXI_SPI_CTL_CS_MASK | SUNXI_SPI_CTL_CS_LEVEL);
++ reg |= SUNXI_SPI_CTL_CS(cs);
++ writel(reg, &priv->regs->xfer_ctl);
++
++ if (plat->activate_delay_us)
++ udelay(plat->activate_delay_us);
++}
++
++static void sunxi_spi_cs_deactivate(struct udevice *dev, unsigned int cs)
++{
++ struct udevice *bus = dev->parent;
++ struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ uint32_t reg;
++
++ debug("%s: deactivate cs: %u, bus: '%s'\n", __func__, cs, bus->name);
++
++ reg = readl(&priv->regs->xfer_ctl);
++ reg &= ~SUNXI_SPI_CTL_CS_MASK;
++ reg |= SUNXI_SPI_CTL_CS_LEVEL;
++ writel(reg, &priv->regs->xfer_ctl);
++
++ /*
++ * Remember the time of this transaction so that we can honour the bus
++ * delay.
++ */
++ if (plat->deactivate_delay_us)
++ priv->last_transaction_us = timer_get_us();
++}
++
++static int sunxi_spi_ofdata_to_platdata(struct udevice *bus)
++{
++ struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
++ const void *blob = gd->fdt_blob;
++ int node = dev_of_offset(bus);
++
++ plat->regs = (struct sunxi_spi_regs *)devfdt_get_addr(bus);
++ plat->activate_delay_us = fdtdec_get_int(
++ blob, node, "spi-activate_delay", 0);
++ plat->deactivate_delay_us = fdtdec_get_int(
++ blob, node, "spi-deactivate-delay", 0);
++
++ debug("%s: regs=%p, activate-delay=%u, deactivate-delay=%u\n",
++ __func__, plat->regs, plat->activate_delay_us,
++ plat->deactivate_delay_us);
++
++ return 0;
++}
++
++static int sunxi_spi_probe(struct udevice *bus)
++{
++ struct sunxi_spi_platdata *plat = dev_get_platdata(bus);
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++
++ debug("%s: probe\n", __func__);
++
++ priv->regs = plat->regs;
++ priv->last_transaction_us = timer_get_us();
++
++ return 0;
++}
++
++static int sunxi_spi_claim_bus(struct udevice *dev)
++{
++ struct udevice *bus = dev->parent;
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ unsigned int pin_func = SUNXI_GPC_SPI0;
++
++ debug("%s: claiming bus\n", __func__);
++
++ if (IS_ENABLED(CONFIG_MACH_SUN50I))
++ pin_func = SUN50I_GPC_SPI0;
++
++ sunxi_spi_setup_pinmux(pin_func);
++ sunxi_spi_enable_clock(bus);
++ setbits_le32(&priv->regs->glb_ctl, SUNXI_SPI_CTL_MASTER |
++ SUNXI_SPI_CTL_ENABLE | SUNXI_SPI_CTL_TP | SUNXI_SPI_CTL_SRST);
++
++ if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
++ while (readl(&priv->regs->glb_ctl) & SUNXI_SPI_CTL_SRST)
++ ;
++
++ setbits_le32(&priv->regs->xfer_ctl, SUNXI_SPI_CTL_CS_MANUAL |
++ SUNXI_SPI_CTL_CS_LEVEL);
++ setbits_le32(&priv->regs->fifo_ctl, SUNXI_SPI_CTL_RF_RST |
++ SUNXI_SPI_CTL_TF_RST);
++
++ return 0;
++}
++
++static int sunxi_spi_release_bus(struct udevice *dev)
++{
++ struct udevice *bus = dev->parent;
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++
++ debug("%s: releasing bus\n", __func__);
++
++ clrbits_le32(&priv->regs->glb_ctl, SUNXI_SPI_CTL_MASTER |
++ SUNXI_SPI_CTL_ENABLE);
++ sunxi_spi_disable_clock();
++
++ return 0;
++}
++
++static void sunxi_spi_write(struct udevice *dev, const char *tx_buf,
++ size_t nbytes)
++{
++ struct udevice *bus = dev->parent;
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ size_t i;
++ char byte;
++
++ if (!tx_buf)
++ nbytes = 0;
++
++ writel(SUNXI_SPI_XMIT_CNT(nbytes), &priv->regs->xmit_cnt);
++
++ if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I))
++ writel(SUNXI_SPI_BURST_CNT(nbytes), &priv->regs->burst_ctl);
++
++ for (i = 0; i < nbytes; ++i) {
++ byte = tx_buf ? *tx_buf++ : 0;
++ writeb(byte, &priv->regs->tx_data);
++ }
++}
++
++static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen,
++ const void *dout, void *din, unsigned long flags)
++{
++ struct udevice *bus = dev->parent;
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
++ const char *tx_buf = dout;
++ char *rx_buf = din;
++ size_t len = bitlen / 8;
++ size_t i, nbytes;
++ char byte;
++
++ if (bitlen % 8) {
++ debug("%s: non byte-aligned SPI transfer.\n", __func__);
++ return -1;
++ }
++
++ if (flags & SPI_XFER_BEGIN)
++ sunxi_spi_cs_activate(dev, slave_plat->cs);
++
++ while (len) {
++ nbytes = min(len, (size_t)64 - 1);
++
++ writel(SUNXI_SPI_BURST_CNT(nbytes), &priv->regs->burst_cnt);
++ sunxi_spi_write(dev, tx_buf, nbytes);
++ setbits_le32(&priv->regs->xfer_ctl, SUNXI_SPI_CTL_XCH);
++
++ while (((readl(&priv->regs->fifo_sta) &
++ SUNXI_SPI_FIFO_RF_CNT_MASK) >>
++ SUNXI_SPI_FIFO_RF_CNT_BITS) < nbytes)
++ ;
++
++ for (i = 0; i < nbytes; ++i) {
++ byte = readb(&priv->regs->rx_data);
++
++ if (rx_buf)
++ *rx_buf++ = byte;
++ }
++
++ len -= nbytes;
++ }
++
++ if (flags & SPI_XFER_END)
++ sunxi_spi_cs_deactivate(dev, slave_plat->cs);
++
++ return 0;
++}
++
++static int sunxi_spi_set_speed(struct udevice *bus, uint speed)
++{
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ unsigned int div;
++ uint32_t reg;
++
++ speed = min(speed, (unsigned int)SUNXI_SPI_MAX_RATE);
++ speed = max((unsigned int)SUNXI_SPI_MIN_RATE, speed);
++
++ div = SUNXI_SPI_MAX_RATE / (2 * speed);
++
++ if (div <= (SUNXI_SPI_CLK_CTL_CDR2_MASK + 1)) {
++ if (div > 0)
++ div--;
++
++ reg = SUNXI_SPI_CLK_CTL_CDR2(div) | SUNXI_SPI_CLK_CTL_DRS;
++ } else {
++ div = __ilog2(SUNXI_SPI_MAX_RATE) - __ilog2(speed);
++ reg = SUNXI_SPI_CLK_CTL_CDR1(div);
++ }
++
++ writel(reg, &priv->regs->clk_ctl);
++
++ debug("%s: speed=%u\n", __func__, speed);
++
++ return 0;
++}
++
++static int sunxi_spi_set_mode(struct udevice *bus, uint mode)
++{
++ struct sunxi_spi_priv *priv = dev_get_priv(bus);
++ uint32_t reg;
++
++ reg = readl(&priv->regs->xfer_ctl);
++ reg &= ~(SUNXI_SPI_CTL_CPOL | SUNXI_SPI_CTL_CPHA |
++ SUNXI_SPI_CTL_CS_ACTIVE_LOW);
++
++ if (mode & SPI_CPOL)
++ reg |= SUNXI_SPI_CTL_CPOL;
++
++ if (mode & SPI_CPHA)
++ reg |= SUNXI_SPI_CTL_CPHA;
++
++ if (!(mode & SPI_CS_HIGH))
++ reg |= SUNXI_SPI_CTL_CS_ACTIVE_LOW;
++
++ writel(reg, &priv->regs->xfer_ctl);
++
++ debug("%s: mode=%d\n", __func__, mode);
++
++ return 0;
++}
++
++static const struct dm_spi_ops sunxi_spi_ops = {
++ .claim_bus = sunxi_spi_claim_bus,
++ .release_bus = sunxi_spi_release_bus,
++ .xfer = sunxi_spi_xfer,
++ .set_speed = sunxi_spi_set_speed,
++ .set_mode = sunxi_spi_set_mode,
++};
++
++static const struct udevice_id sunxi_spi_ids[] = {
++ { .compatible = "allwinner,sun4i-a10-spi" },
++ { .compatible = "allwinner,sun6i-a31-spi" },
++ { .compatible = "allwinner,sun8i-h3-spi" },
++ { .compatible = "allwinner,sun50i-a64-spi" },
++ { }
++};
++
++U_BOOT_DRIVER(sunxi_spi) = {
++ .name = "sunxi_spi",
++ .id = UCLASS_SPI,
++ .of_match = sunxi_spi_ids,
++ .ops = &sunxi_spi_ops,
++ .ofdata_to_platdata = sunxi_spi_ofdata_to_platdata,
++ .platdata_auto_alloc_size = sizeof(struct sunxi_spi_platdata),
++ .priv_auto_alloc_size = sizeof(struct sunxi_spi_priv),
++ .probe = sunxi_spi_probe,
++};
--- /dev/null
+From 051559e7cb1ad072277dfeafcebf91632582da1a Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Sun, 12 Feb 2017 14:53:15 +0000
+Subject: introduce and use sunxi_gpio_parse_pin_name()
+
+---
+ arch/arm/include/asm/arch-sunxi/gpio.h | 2 ++
+ arch/arm/mach-sunxi/pinmux.c | 16 ++++++++++++++++
+ drivers/net/sun8i_emac.c | 7 ++-----
+ 3 files changed, 20 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/include/asm/arch-sunxi/gpio.h
++++ b/arch/arm/include/asm/arch-sunxi/gpio.h
+@@ -241,4 +241,6 @@ int axp_gpio_init(void);
+ static inline int axp_gpio_init(void) { return 0; }
+ #endif
+
++int sunxi_gpio_parse_pin_name(const char *pin_name);
++
+ #endif /* _SUNXI_GPIO_H */
+--- a/arch/arm/mach-sunxi/pinmux.c
++++ b/arch/arm/mach-sunxi/pinmux.c
+@@ -69,3 +69,19 @@ int sunxi_gpio_set_pull(u32 pin, u32 val
+
+ return 0;
+ }
++
++int sunxi_gpio_parse_pin_name(const char *pin_name)
++{
++ int pin;
++
++ if (pin_name[0] != 'P')
++ return -1;
++
++ if (pin_name[1] < 'A' || pin_name[1] > 'Z')
++ return -1;
++
++ pin = (pin_name[1] - 'A') << 5;
++ pin += simple_strtol(&pin_name[2], NULL, 10);
++
++ return pin;
++}
+--- a/drivers/net/sun8i_emac.c
++++ b/drivers/net/sun8i_emac.c
+@@ -475,12 +475,9 @@ static int parse_phy_pins(struct udevice
+ "allwinner,pins", i, NULL);
+ if (!pin_name)
+ break;
+- if (pin_name[0] != 'P')
++ pin = sunxi_gpio_parse_pin_name(pin_name);
++ if (pin < 0)
+ continue;
+- pin = (pin_name[1] - 'A') << 5;
+- if (pin >= 26 << 5)
+- continue;
+- pin += simple_strtol(&pin_name[2], NULL, 10);
+
+ sunxi_gpio_set_cfgpin(pin, SUN8I_GPD8_GMAC);
+ sunxi_gpio_set_drv(pin, drive);
--- /dev/null
+From fa5767ca356bcdfdb1bfa055851579a858a5ed25 Mon Sep 17 00:00:00 2001
+From: Andre Przywara <andre.przywara@arm.com>
+Date: Sun, 12 Feb 2017 14:53:16 +0000
+Subject: introduce and use sunxi_gpio_setup_dt_pins()
+
+---
+ arch/arm/include/asm/arch-sunxi/gpio.h | 2 ++
+ arch/arm/mach-sunxi/pinmux.c | 61 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 63 insertions(+)
+
+--- a/arch/arm/include/asm/arch-sunxi/gpio.h
++++ b/arch/arm/include/asm/arch-sunxi/gpio.h
+@@ -242,5 +242,7 @@ static inline int axp_gpio_init(void) {
+ #endif
+
+ int sunxi_gpio_parse_pin_name(const char *pin_name);
++int sunxi_gpio_setup_dt_pins(const void * volatile fdt_blob, int node,
++ const char * mux_name, int mux_sel);
+
+ #endif /* _SUNXI_GPIO_H */
+--- a/arch/arm/mach-sunxi/pinmux.c
++++ b/arch/arm/mach-sunxi/pinmux.c
+@@ -9,6 +9,9 @@
+ #include <common.h>
+ #include <asm/io.h>
+ #include <asm/arch/gpio.h>
++#include <fdtdec.h>
++#include <fdt_support.h>
++#include <dt-bindings/pinctrl/sun4i-a10.h>
+
+ void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val)
+ {
+@@ -85,3 +88,61 @@ int sunxi_gpio_parse_pin_name(const char
+
+ return pin;
+ }
++
++int sunxi_gpio_setup_dt_pins(const void * volatile fdt_blob, int node,
++ const char * mux_name, int mux_sel)
++{
++ int drive, pull, pin, i;
++ const char *pin_name;
++ int offset;
++
++ offset = fdtdec_lookup_phandle(fdt_blob, node, "pinctrl-0");
++ if (offset < 0)
++ return offset;
++
++ drive = fdt_getprop_u32_default_node(fdt_blob, offset, 0,
++ "drive-strength", 0);
++ if (drive) {
++ if (drive <= 10)
++ drive = SUN4I_PINCTRL_10_MA;
++ else if (drive <= 20)
++ drive = SUN4I_PINCTRL_20_MA;
++ else if (drive <= 30)
++ drive = SUN4I_PINCTRL_30_MA;
++ else
++ drive = SUN4I_PINCTRL_40_MA;
++ } else {
++ drive = fdt_getprop_u32_default_node(fdt_blob, offset, 0,
++ "allwinner,drive", 4);
++ }
++
++ if (fdt_get_property(fdt_blob, offset, "bias-pull-up", NULL))
++ pull = SUN4I_PINCTRL_PULL_UP;
++ else if (fdt_get_property(fdt_blob, offset, "bias-disable", NULL))
++ pull = SUN4I_PINCTRL_NO_PULL;
++ else if (fdt_get_property(fdt_blob, offset, "bias-pull-down", NULL))
++ pull = SUN4I_PINCTRL_PULL_DOWN;
++ else
++ pull = fdt_getprop_u32_default_node(fdt_blob, offset, 0,
++ "allwinner,pull", 0);
++
++ for (i = 0; ; i++) {
++ pin_name = fdt_stringlist_get(fdt_blob, offset,
++ "allwinner,pins", i, NULL);
++ if (!pin_name) {
++ pin_name = fdt_stringlist_get(fdt_blob, offset,
++ "pins", i, NULL);
++ if (!pin_name)
++ break;
++ }
++ pin = sunxi_gpio_parse_pin_name(pin_name);
++ if (pin < 0)
++ continue;
++
++ sunxi_gpio_set_cfgpin(pin, mux_sel);
++ sunxi_gpio_set_drv(pin, drive);
++ sunxi_gpio_set_pull(pin, pull);
++ }
++
++ return i;
++}
--- /dev/null
+From 0c87fe5938e5c7c089b84962a7c7ee3505910ffc Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@whiteboxsystems.nl>
+Date: Thu, 23 Feb 2017 12:11:42 +0000
+Subject: sunxi: spi: set up GPIO pins using pinctrl
+
+---
+ drivers/spi/sunxi_spi.c | 31 +++++++++++++++++++------------
+ 1 file changed, 19 insertions(+), 12 deletions(-)
+
+--- a/drivers/spi/sunxi_spi.c
++++ b/drivers/spi/sunxi_spi.c
+@@ -36,18 +36,28 @@ struct sunxi_spi_priv {
+
+ DECLARE_GLOBAL_DATA_PTR;
+
+-static void sunxi_spi_setup_pinmux(unsigned int pin_func)
++static int sunxi_spi_parse_pins(struct udevice *bus)
+ {
+- unsigned int pin;
++ unsigned int pin_func = SUNXI_GPC_SPI0;
++ int ret;
++
++ if (IS_ENABLED(CONFIG_MACH_SUN50I))
++ pin_func = SUN50I_GPC_SPI0;
++
++ ret = sunxi_gpio_setup_dt_pins(gd->fdt_blob, dev_of_offset(bus), NULL,
++ pin_func);
+
+- for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++)
+- sunxi_gpio_set_cfgpin(pin, pin_func);
++ if (ret < 0) {
++ printf("WARNING: sunxi-spi: cannot find pinctrl-0 node\n");
++ return ret;
++ }
+
+- if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I)) {
+- sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_func);
+- } else {
+- sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_func);
++ if (!ret) {
++ printf("WARNING: sunxi-spi: cannot find pins property\n");
++ return -2;
+ }
++
++ return ret;
+ }
+
+ static void sunxi_spi_enable_clock(struct udevice *bus)
+@@ -170,10 +180,7 @@ static int sunxi_spi_claim_bus(struct ud
+
+ debug("%s: claiming bus\n", __func__);
+
+- if (IS_ENABLED(CONFIG_MACH_SUN50I))
+- pin_func = SUN50I_GPC_SPI0;
+-
+- sunxi_spi_setup_pinmux(pin_func);
++ sunxi_spi_parse_pins(bus);
+ sunxi_spi_enable_clock(bus);
+ setbits_le32(&priv->regs->glb_ctl, SUNXI_SPI_CTL_MASTER |
+ SUNXI_SPI_CTL_ENABLE | SUNXI_SPI_CTL_TP | SUNXI_SPI_CTL_SRST);
--- /dev/null
+From 835dd2f28d3f5c88d226622fef283e6ca48f8818 Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@synkhronix.com>
+Date: Thu, 9 Feb 2017 21:42:16 +0000
+Subject: sunxi: dts: enable SPI0 controller for A20 OLinuXino LIME 2
+
+---
+ arch/arm/dts/sun7i-a20-olinuxino-lime2.dts | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/arch/arm/dts/sun7i-a20-olinuxino-lime2.dts
++++ b/arch/arm/dts/sun7i-a20-olinuxino-lime2.dts
+@@ -54,6 +54,7 @@
+
+ aliases {
+ serial0 = &uart0;
++ spi0 = &spi0;
+ };
+
+ chosen {
+@@ -81,6 +82,19 @@
+ };
+ };
+
++&pio {
++ spi0_pins: pinctrl {
++ pins = "PC0", "PC1", "PC2", "PC23";
++ drive-strength = <10>;
++ bias-disable;
++ };
++};
++
++&spi0 {
++ pinctrl-0 = <&spi0_pins>;
++ status = "okay";
++};
++
+ &ahci {
+ target-supply = <®_ahci_5v>;
+ status = "okay";
--- /dev/null
+From eb2fee739650a8fe5534d6b38d84713817f83c6d Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@whiteboxsystems.nl>
+Date: Thu, 23 Feb 2017 11:49:43 +0000
+Subject: sunxi: dts: enable SPI0 controller for Orange Pi Zero
+
+---
+ arch/arm/dts/sun8i-h2-plus-orangepi-zero.dts | 14 ++++++++++++++
+ arch/arm/dts/sun8i-h3.dtsi | 8 ++++++++
+ 2 files changed, 22 insertions(+)
+
+--- a/arch/arm/dts/sun8i-h2-plus-orangepi-zero.dts
++++ b/arch/arm/dts/sun8i-h2-plus-orangepi-zero.dts
+@@ -59,6 +59,7 @@
+ serial0 = &uart0;
+ /* ethernet0 is the H3 emac, defined in sun8i-h3.dtsi */
+ ethernet1 = &xr819;
++ spi0 = &spi0;
+ };
+
+ chosen {
+@@ -95,6 +96,19 @@
+ };
+ };
+
++&pio {
++ spi0_pins: pinctrl {
++ pins = "PC0", "PC1", "PC2", "PC3";
++ drive-strength = <10>;
++ bias-disable;
++ };
++};
++
++&spi0 {
++ pinctrl-0 = <&spi0_pins>;
++ status = "okay";
++};
++
+ &ehci1 {
+ status = "okay";
+ };
+--- a/arch/arm/dts/sun8i-h3.dtsi
++++ b/arch/arm/dts/sun8i-h3.dtsi
+@@ -158,6 +158,14 @@
+ #dma-cells = <1>;
+ };
+
++ spi0: spi@01c68000 {
++ compatible = "allwinner,sun8i-h3-spi";
++ reg = <0x01c68000 0x1000>;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
++
+ mmc0: mmc@01c0f000 {
+ compatible = "allwinner,sun7i-a20-mmc",
+ "allwinner,sun5i-a13-mmc";
--- /dev/null
+From 04bfe04a88b8c491e6567d9f779b4d62932b13ad Mon Sep 17 00:00:00 2001
+From: "S.J.R. van Schaik" <stephan@whiteboxsystems.nl>
+Date: Thu, 23 Feb 2017 11:50:04 +0000
+Subject: sunxi: dts: enable SPI0 controller for Pine64+
+
+---
+ arch/arm/dts/sun50i-a64-orangepi-win.dts | 14 ++++++++++++++
+ arch/arm/dts/sun50i-a64.dtsi | 8 ++++++++
+ 2 files changed, 22 insertions(+)
+
+--- a/arch/arm/dts/sun50i-a64-orangepi-win.dts
++++ b/arch/arm/dts/sun50i-a64-orangepi-win.dts
+@@ -52,6 +52,7 @@
+
+ aliases {
+ serial0 = &uart0;
++ spi0 = &spi0;
+ };
+
+ chosen {
+@@ -70,6 +71,19 @@
+ status = "okay";
+ };
+
++&pio {
++ spi0_pins: pinctrl {
++ pins = "PC0", "PC1", "PC2", "PC3";
++ drive-strength = <10>;
++ bias-disabled;
++ };
++};
++
++&spi0 {
++ pinctrl-0 = <&spi0_pins>;
++ status = "okay";
++};
++
+ &mmc0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&mmc0_pins>;
+--- a/arch/arm/dts/sun50i-a64.dtsi
++++ b/arch/arm/dts/sun50i-a64.dtsi
+@@ -445,5 +445,13 @@
+ interrupt-controller;
+ #interrupt-cells = <3>;
+ };
++
++ spi0: spi@01c68000 {
++ compatible = "allwinner,sun50i-a64-spi";
++ reg = <0x01c68000 0x1000>;
++ status = "disabled";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ };
+ };
+ };
--- /dev/null
+From 9e101257c1bc1243495559b255ebdd33929f4fa3 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sun, 14 Jan 2018 18:58:47 +0100
+Subject: sunxi: set bootcmd when spi boot is activated
+
+---
+ include/configs/sunxi-common.h | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+--- a/include/configs/sunxi-common.h
++++ b/include/configs/sunxi-common.h
+@@ -447,6 +447,11 @@ extern int soft_i2c_gpio_scl;
+ #define BOOTCMD_SUNXI_COMPAT
+ #endif
+
++#if CONFIG_SPI_BOOT
++/* u-boot env in serial flash, by default is bus 0 and cs 0 */
++#define CONFIG_ENV_SECT_SIZE 0x010000
++#endif
++
+ #include <config_distro_bootcmd.h>
+
+ #ifdef CONFIG_USB_KEYBOARD