[U-Boot] [RFC PATCH 0/4] arm: sunxi: Allwinner SPI driver

Hi,
The following patches implement a working SPI driver based on driver model for Allwinner (sunxi) devices. We have tested the driver on the A20 OLinuXino LIME 2 (sun7i) with a Winbond w25q128bv chip loading and booting a FIT image from the chip. The sun6i/sun8i/sun9i/sun50i code also builds properly, but still has to be tested on actual devices.
The additional SPI controllers (SPI1, SPI2, SPI3) are not yet supported by this driver because the clock gating code only sets up the clock for SPI0. We are going to implement this in the near future. It might make sense to migrate the clock code to a separate driver or at least to mach-sunxi.
We would like to hear some feedback to further improve these patches for mainline integration.
Yours sincerely, Stephan.
S.J.R. van Schaik (4): arch-sunxi: added missing AHB_GATE_OFFSET_SPIx defines for sun6i/sun9i arch-sunxi: added SPI register definitions for sun4i/sun7i arch-sunxi: added SPI register definitions for sun6i/sun8i/sun9i/sun50i sunxi-spi: added SPI driver for Allwinner devices (sunxi)
arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 2 + arch/arm/include/asm/arch-sunxi/clock_sun9i.h | 4 + arch/arm/include/asm/arch-sunxi/spi.h | 29 +++ arch/arm/include/asm/arch-sunxi/spi_sun4i.h | 53 +++++ arch/arm/include/asm/arch-sunxi/spi_sun6i.h | 56 +++++ drivers/spi/Kconfig | 5 + drivers/spi/Makefile | 1 + drivers/spi/sunxi_spi.c | 329 ++++++++++++++++++++++++++ 8 files changed, 479 insertions(+) create mode 100644 arch/arm/include/asm/arch-sunxi/spi.h create mode 100644 arch/arm/include/asm/arch-sunxi/spi_sun4i.h create mode 100644 arch/arm/include/asm/arch-sunxi/spi_sun6i.h create mode 100644 drivers/spi/sunxi_spi.c

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(+)
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 3f87672c62..fd778ddd50 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -255,6 +255,8 @@ struct sunxi_ccm_reg { #define AHB_GATE_OFFSET_USB_EHCI0 26 #endif #define AHB_GATE_OFFSET_USB0 24 +#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 diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun9i.h b/arch/arm/include/asm/arch-sunxi/clock_sun9i.h index 0aeb6408d8..224fc47798 100644 --- 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

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
diff --git a/arch/arm/include/asm/arch-sunxi/spi.h b/arch/arm/include/asm/arch-sunxi/spi.h new file mode 100644 index 0000000000..bbc0ee1550 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/spi.h @@ -0,0 +1,29 @@ +/* + * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V. + * Stephan van Schaik stephan@whiteboxsystems.nl + * Merlijn 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 */ diff --git a/arch/arm/include/asm/arch-sunxi/spi_sun4i.h b/arch/arm/include/asm/arch-sunxi/spi_sun4i.h new file mode 100644 index 0000000000..b876d3695d --- /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. + * Stephan van Schaik stephan@whiteboxsystems.nl + * Merlijn 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 */

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
diff --git a/arch/arm/include/asm/arch-sunxi/spi_sun6i.h b/arch/arm/include/asm/arch-sunxi/spi_sun6i.h new file mode 100644 index 0000000000..379561d021 --- /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. + * Stephan van Schaik stephan@whiteboxsystems.nl + * Merlijn 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 0xff +#define SUNXI_SPI_FIFO_RF_CNT_BITS 0 +#define SUNXI_SPI_FIFO_TF_CNT_MASK 0xff +#define SUNXI_SPI_FIFO_TF_CNT_BITS 16 + +#endif /* _SUNXI_SPI_SUN6I_H */

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 | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 335 insertions(+) create mode 100644 drivers/spi/sunxi_spi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f3f7dbe089..d243e69a3c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -132,6 +132,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 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index fa9a1d2496..b2011f4da3 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o 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 diff --git a/drivers/spi/sunxi_spi.c b/drivers/spi/sunxi_spi.c new file mode 100644 index 0000000000..721d5167cd --- /dev/null +++ b/drivers/spi/sunxi_spi.c @@ -0,0 +1,329 @@ +/* + * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V. + * Stephan van Schaik stephan@whiteboxsystems.nl + * Merlijn 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_function) +{ + unsigned int pin; + + for (pin = SUNXI_GPC(0); pin <= SUNXI_GPC(2); pin++) + sunxi_gpio_set_cfgpin(pin, pin_function); + + if (IS_ENABLED(CONFIG_MACH_SUN4I) || IS_ENABLED(CONFIG_MACH_SUN7I)) { + sunxi_gpio_set_cfgpin(SUNXI_GPC(23), pin_function); + } else { + sunxi_gpio_set_cfgpin(SUNXI_GPC(3), pin_function); + } +} + +static void sunxi_spi_enable_clock(struct udevice *bus) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg * const)SUNXI_CCM_BASE; + + 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)); +} + +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 = bus->of_offset; + + plat->regs = (struct sunxi_spi_regs *)dev_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); + + debug("%s: claiming bus\n", __func__); + + sunxi_spi_setup_pinmux(SUNXI_GPC_SPI0); + 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); + 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); + + if (IS_ENABLED(CONFIG_GEN_SUN6I)) + while (readl(&priv->regs->glb_ctl) & SUNXI_SPI_CTL_SRST) + ; + + 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_ENABLE); + sunxi_spi_disable_clock(); + + return 0; +} + +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); + + if (IS_ENABLED(CONFIG_SUNXI_GEN_SUN6I)) + writel(SUNXI_SPI_BURST_CNT(nbytes), + &priv->regs->burst_ctl); + + if (!tx_buf) { + writel(0, &priv->regs->xmit_cnt); + } else { + writel(SUNXI_SPI_XMIT_CNT(nbytes), + &priv->regs->xmit_cnt); + + for (i = 0; i < nbytes; ++i) { + byte = tx_buf ? *tx_buf++ : 0; + writeb(byte, &priv->regs->tx_data); + } + } + + 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" }, + { } +}; + +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, +};
participants (1)
-
S.J.R. van Schaik