From 765cec67866108759c947e87ce6444315154da8e Mon Sep 17 00:00:00 2001 From: Anthony Lee Date: Thu, 13 Dec 2018 19:59:34 +0800 Subject: [PATCH 1/3] sunxi: improved sunxi_spi driver bases on works by Whitebox Systems --- arch/arm/dts/sun8i-h3.dtsi | 9 + arch/arm/include/asm/arch-sunxi/clock_sun6i.h | 3 + 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 | 6 + drivers/spi/Makefile | 1 + drivers/spi/sunxi_spi.c | 483 ++++++++++++++++++ 8 files changed, 640 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 diff --git a/arch/arm/dts/sun8i-h3.dtsi b/arch/arm/dts/sun8i-h3.dtsi index afa60793a235..b67aff03fed2 100644 --- a/arch/arm/dts/sun8i-h3.dtsi +++ b/arch/arm/dts/sun8i-h3.dtsi @@ -52,6 +52,7 @@ aliases { ethernet0 = &emac; + spi0 = &spi0; }; cpus { @@ -478,6 +479,14 @@ status = "disabled"; }; + spi0: spi@01c68000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c68000 0x1000>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + gic: interrupt-controller@01c81000 { compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; reg = <0x01c81000 0x1000>, diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h index 88081a62741f..8f8616d9ee09 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun6i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun6i.h @@ -302,6 +302,9 @@ struct sunxi_ccm_reg { #define AHB_GATE_OFFSET_DMA 6 #define AHB_GATE_OFFSET_SS 5 +#define AHB_GATE_OFFSET_SPI0 20 +#define AHB_GATE_OFFSET_SPI1 21 + /* ahb_gate1 offsets */ #define AHB_GATE_OFFSET_DRC0 25 #define AHB_GATE_OFFSET_DE_FE0 14 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 000000000000..66301b24d5de --- /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 + * M.B.W. Wajer + * + * 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 +#else +#include +#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 000000000000..c94519651fe5 --- /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 + * M.B.W. Wajer + * + * 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 */ 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 000000000000..d9241848f9f6 --- /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 + * M.B.W. Wajer + * + * 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 */ diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 88da9a4c8e71..d473dc48f2e0 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -132,6 +132,12 @@ config STM32_QSPI used to access the SPI NOR flash chips on platforms embedding this ST IP core. +config SUNXI_SPI + bool "Allwinner SoCs SPI controller" + select DM_GPIO + help + SPI driver for Allwinner sun4i, sun5i, sun6i, sun7i and sun8i SoCs + config TEGRA114_SPI bool "nVidia Tegra114 SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ad56203cd6f1..b65d2512cf05 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -42,6 +42,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 000000000000..f34fc4f48eb7 --- /dev/null +++ b/drivers/spi/sunxi_spi.c @@ -0,0 +1,483 @@ +/* + * (C) Copyright 2017 Whitebox Systems / Northend Systems B.V. + * S.J.R. van Schaik + * M.B.W. Wajer + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define SUNXI_SPI_MAX_RATE (24 * 1000 * 1000) +#define SUNXI_SPI_MIN_RATE (3 * 1000) +#define SUNXI_SPI_MAX_CS_COUNT 4 + +#define SPI_CS_GPIOS_PREFERED 0x01 + +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; + unsigned int flags; + struct gpio_desc cs_gpios[SUNXI_SPI_MAX_CS_COUNT]; +}; + +DECLARE_GLOBAL_DATA_PTR; + +static int sunxi_spi_parse_pins(struct udevice *dev) +{ + struct sunxi_spi_priv *priv = dev_get_priv(dev); + const void *fdt = gd->fdt_blob; + const char *pin_name; + const fdt32_t *list; + u32 phandle; + int drive, pull = 0, pin, i, k; + int offset; + int size; + struct gpio_desc desc; + + list = fdt_getprop(fdt, dev_of_offset(dev), "pinctrl-0", &size); + if (!list) { + printf("WARNING: sunxi_spi: cannot find pinctrl-0 node\n"); + return -EINVAL; + } + + while (size) { + phandle = fdt32_to_cpu(*list++); + size -= sizeof(*list); + + offset = fdt_node_offset_by_phandle(fdt, phandle); + if (offset < 0) + return offset; + + drive = fdt_getprop_u32_default_node(fdt, offset, 0, + "drive-strength", 0); + if (drive) { + if (drive <= 10) + drive = 0; + else if (drive <= 20) + drive = 1; + else if (drive <= 30) + drive = 2; + else + drive = 3; + } else { + drive = fdt_getprop_u32_default_node(fdt, offset, 0, + "allwinner,drive", + 0); + drive = min(drive, 3); + } + + if (fdt_get_property(fdt, offset, "bias-disable", NULL)) + pull = 0; + else if (fdt_get_property(fdt, offset, "bias-pull-up", NULL)) + pull = 1; + else if (fdt_get_property(fdt, offset, "bias-pull-down", NULL)) + pull = 2; + else + pull = fdt_getprop_u32_default_node(fdt, offset, 0, + "allwinner,pull", + 0); + pull = min(pull, 2); + + for (i = 0; ; i++) { + pin_name = fdt_stringlist_get(fdt, offset, + "pins", i, NULL); + if (!pin_name) { + pin_name = fdt_stringlist_get(fdt, offset, + "allwinner,pins", + i, NULL); + if (!pin_name) + break; + } + + pin = name_to_gpio(pin_name); + if (pin < 0) + break; + + if (dm_gpio_lookup_name(pin_name, &desc) == 0) { + for (k = 0; k < SUNXI_SPI_MAX_CS_COUNT; k++) { + if (!dm_gpio_is_valid(&priv->cs_gpios[k])) continue; + if (gpio_get_number(&desc) == gpio_get_number(&priv->cs_gpios[k])) break; + } + + if (k < SUNXI_SPI_MAX_CS_COUNT) /* cs-gpio pin */ + continue; + } + + sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0); + sunxi_gpio_set_drv(pin, drive); + sunxi_gpio_set_pull(pin, pull); + } + } + return 0; +} + +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); + + if (priv->flags & SPI_CS_GPIOS_PREFERED) { + if (dm_gpio_is_valid(&priv->cs_gpios[cs])) { + gpio_get_ops(priv->cs_gpios[cs].dev)->direction_output(priv->cs_gpios[cs].dev, priv->cs_gpios[cs].offset, + (priv->cs_gpios[cs].flags & GPIOD_ACTIVE_LOW) ? 1 : 0); + ndelay(150); + } + } + + 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); + + if (priv->flags & SPI_CS_GPIOS_PREFERED) { + if (dm_gpio_is_valid(&priv->cs_gpios[cs])) { + gpio_get_ops(priv->cs_gpios[cs].dev)->direction_output(priv->cs_gpios[cs].dev, priv->cs_gpios[cs].offset, + (priv->cs_gpios[cs].flags & GPIOD_ACTIVE_LOW) ? 0 : 1); + ndelay(150); + } + } + + reg = readl(&priv->regs->xfer_ctl); + reg &= ~SUNXI_SPI_CTL_CS_MASK; + reg |= SUNXI_SPI_CTL_CS(cs); + 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); + int i, err; + + debug("%s: probe\n", __func__); + + priv->regs = plat->regs; + priv->last_transaction_us = timer_get_us(); + + err = gpio_request_list_by_name(bus, "cs-gpios", priv->cs_gpios, + SUNXI_SPI_MAX_CS_COUNT, 0); + if (err > 0) { + priv->flags |= SPI_CS_GPIOS_PREFERED; + + for (i = 0; i < SUNXI_SPI_MAX_CS_COUNT; i++) { + if (dm_gpio_is_valid(&priv->cs_gpios[i])) { + dm_gpio_set_dir_flags(&priv->cs_gpios[i], GPIOD_IS_OUT); + dm_gpio_set_value(&priv->cs_gpios[i], 1); + ndelay(150); + + /* for using GPIO directly in SPI driver */ + dm_gpio_free(bus, &priv->cs_gpios[i]); + } else { + priv->cs_gpios[i].dev = NULL; + } + } + } else { + for (i = 0; i < SUNXI_SPI_MAX_CS_COUNT; i++) { + priv->cs_gpios[i].dev = NULL; + } + } + + 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_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); + + 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; + unsigned long timeout; + int err = 0; + + 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); + + timeout = timer_get_us() + 200000; + while (((readl(&priv->regs->fifo_sta) & + SUNXI_SPI_FIFO_RF_CNT_MASK) >> + SUNXI_SPI_FIFO_RF_CNT_BITS) < nbytes) { + if (time_after(timer_get_us(), timeout)) { + err = -1; + break; + } + } + if (err != 0) break; + + for (i = 0; i < nbytes; ++i) { + byte = readb(&priv->regs->rx_data); + + if (rx_buf) + *rx_buf++ = byte; + } + + len -= nbytes; + tx_buf += nbytes; + } + + if (flags & SPI_XFER_END) + sunxi_spi_cs_deactivate(dev, slave_plat->cs); + + return err; +} + +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, +}; From 3728a887b50f1833faee24caafb8426d9ec504b5 Mon Sep 17 00:00:00 2001 From: Anthony Lee Date: Thu, 13 Dec 2018 20:05:49 +0800 Subject: [PATCH 2/3] sunxi: fixing I2C0/I2C1/I2C2 for sun8i-h3/sun50i-h5 --- arch/arm/dts/sun8i-h3.dtsi | 9 +++++++++ board/sunxi/board.c | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/arm/dts/sun8i-h3.dtsi b/arch/arm/dts/sun8i-h3.dtsi index b67aff03fed2..83e71189dc48 100644 --- a/arch/arm/dts/sun8i-h3.dtsi +++ b/arch/arm/dts/sun8i-h3.dtsi @@ -53,6 +53,7 @@ aliases { ethernet0 = &emac; spi0 = &spi0; + i2c0 = &i2c0; }; cpus { @@ -487,6 +488,14 @@ #size-cells = <0>; }; + i2c0: i2c@01c2ac00 { + compatible = "allwinner,sun6i-a31-i2c"; + reg = <0x01c2ac00 0x400>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <0>; + }; + gic: interrupt-controller@01c81000 { compatible = "arm,cortex-a7-gic", "arm,cortex-a15-gic"; reg = <0x01c81000 0x1000>, diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 9a05d2b3fd02..79afc46e7435 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -94,9 +94,15 @@ void i2c_init_board(void) sunxi_gpio_set_cfgpin(SUNXI_GPH(15), SUN6I_GPH_TWI0); clock_twi_onoff(0, 1); #elif defined(CONFIG_MACH_SUN8I) +#ifndef CONFIG_MACH_SUNXI_H3_H5 sunxi_gpio_set_cfgpin(SUNXI_GPH(2), SUN8I_GPH_TWI0); sunxi_gpio_set_cfgpin(SUNXI_GPH(3), SUN8I_GPH_TWI0); clock_twi_onoff(0, 1); +#else + sunxi_gpio_set_cfgpin(SUNXI_GPA(11), 2); + sunxi_gpio_set_cfgpin(SUNXI_GPA(12), 2); + clock_twi_onoff(0, 1); +#endif /* !CONFIG_MACH_SUNXI_H3_H5 */ #endif #endif @@ -116,9 +122,15 @@ void i2c_init_board(void) sunxi_gpio_set_cfgpin(SUNXI_GPH(17), SUN6I_GPH_TWI1); clock_twi_onoff(1, 1); #elif defined(CONFIG_MACH_SUN8I) +#ifndef CONFIG_MACH_SUNXI_H3_H5 sunxi_gpio_set_cfgpin(SUNXI_GPH(4), SUN8I_GPH_TWI1); sunxi_gpio_set_cfgpin(SUNXI_GPH(5), SUN8I_GPH_TWI1); clock_twi_onoff(1, 1); +#else + sunxi_gpio_set_cfgpin(SUNXI_GPA(18), 3); + sunxi_gpio_set_cfgpin(SUNXI_GPA(19), 3); + clock_twi_onoff(1, 1); +#endif /* !CONFIG_MACH_SUNXI_H3_H5 */ #endif #endif From 683f6db7d8a0d36964778e7c2f257aac63fd1c4e Mon Sep 17 00:00:00 2001 From: Anthony Lee Date: Sun, 30 Dec 2018 00:34:56 +0800 Subject: [PATCH 3/3] sunxi: fix xfer operation of sunxi_spi driver --- drivers/spi/sunxi_spi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/spi/sunxi_spi.c b/drivers/spi/sunxi_spi.c index f34fc4f48eb7..1bb7c30c2363 100644 --- a/drivers/spi/sunxi_spi.c +++ b/drivers/spi/sunxi_spi.c @@ -340,7 +340,7 @@ static void sunxi_spi_write(struct udevice *dev, const char *tx_buf, writel(SUNXI_SPI_BURST_CNT(nbytes), &priv->regs->burst_ctl); for (i = 0; i < nbytes; ++i) { - byte = tx_buf ? *tx_buf++ : 0; + byte = *tx_buf++; writeb(byte, &priv->regs->tx_data); } } @@ -393,7 +393,8 @@ static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen, } len -= nbytes; - tx_buf += nbytes; + if (tx_buf) + tx_buf += nbytes; } if (flags & SPI_XFER_END)