From 7b465e83280d38b0675ad96ae13fbee4be541eeb Mon Sep 17 00:00:00 2001 From: Octav Zlatior Date: Wed, 8 Jul 2015 16:46:40 +0200 Subject: ARM: sun6i: spi: implements SPI driver The driver currently only supports sun6i. Signed-off-by: Octav Zlatior --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/sunxi_spi.c | 430 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/sunxi_spi.h | 143 ++++++++++++++++ 4 files changed, 581 insertions(+) create mode 100644 drivers/spi/sunxi_spi.c create mode 100644 drivers/spi/sunxi_spi.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index f0258f84af..2682e71f4b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -99,6 +99,13 @@ config SANDBOX_SPI }; }; +config SUNXI_SPI + bool "sunxi SPI driver" + help + Enable the sunxi SPI driver. This driver can be used to + access the SPI NOR flash on platforms embedding the Allwinner + sunxi SoC. + config TEGRA114_SPI bool "nVidia Tegra114 SPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3eca7456d6..eeac193f66 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MXS_SPI) += mxs_spi.o obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o +obj-$(CONFIG_SUNXI_SPI) += sunxi_spi.o obj-$(CONFIG_SH_SPI) += sh_spi.o obj-$(CONFIG_SH_QSPI) += sh_qspi.o obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o diff --git a/drivers/spi/sunxi_spi.c b/drivers/spi/sunxi_spi.c new file mode 100644 index 0000000000..e9b994a8fe --- /dev/null +++ b/drivers/spi/sunxi_spi.c @@ -0,0 +1,430 @@ +/* + * SPI driver for Allwinner sunxi SoCs + * + * Supported chips + * - Allwinner A31 (sun6i) + * + * Copyright (C) 2015 Theobroma Systems Design und Consulting GmbH + * Octav Zlatior + * Philipp Tomsich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sunxi_spi.h" + +DECLARE_GLOBAL_DATA_PTR; + +// some arrays to make things easier to express +static int sunxi_spi_mosi_pin[4] = {SUNXI_SPI0_MOSI_PIN, + SUNXI_SPI1_MOSI_PIN, SUNXI_SPI2_MOSI_PIN, SUNXI_SPI3_MOSI_PIN}; +static int sunxi_spi_miso_pin[4] = {SUNXI_SPI0_MISO_PIN, + SUNXI_SPI1_MISO_PIN, SUNXI_SPI2_MISO_PIN, SUNXI_SPI3_MISO_PIN}; +static int sunxi_spi_clk_pin[4] = {SUNXI_SPI0_CLK_PIN, + SUNXI_SPI1_CLK_PIN, SUNXI_SPI2_CLK_PIN, SUNXI_SPI3_CLK_PIN}; +static int sunxi_spi_cs0_pin[4] = {SUNXI_SPI0_CS0_PIN, + SUNXI_SPI1_CS0_PIN, SUNXI_SPI2_CS0_PIN, SUNXI_SPI3_CS0_PIN}; +static int sunxi_spi_cs1_pin[4] = {SUNXI_SPI0_CS1_PIN, + SUNXI_SPI1_CS1_PIN, SUNXI_SPI2_CS1_PIN, SUNXI_SPI3_CS1_PIN}; +static int sunxi_spi_mosi_val[4] = {SUNXI_SPI0_MOSI_VAL, + SUNXI_SPI1_MOSI_VAL, SUNXI_SPI2_MOSI_VAL, SUNXI_SPI3_MOSI_VAL}; +static int sunxi_spi_miso_val[4] = {SUNXI_SPI0_MISO_VAL, + SUNXI_SPI1_MISO_VAL, SUNXI_SPI2_MISO_VAL, SUNXI_SPI3_MISO_VAL}; +static int sunxi_spi_clk_val[4] = {SUNXI_SPI0_CLK_VAL, + SUNXI_SPI1_CLK_VAL, SUNXI_SPI2_CLK_VAL, SUNXI_SPI3_CLK_VAL}; +static int sunxi_spi_cs0_val[4] = {SUNXI_SPI0_CS0_VAL, + SUNXI_SPI1_CS0_VAL, SUNXI_SPI2_CS0_VAL, SUNXI_SPI3_CS0_VAL}; +static int sunxi_spi_cs1_val[4] = {SUNXI_SPI0_CS1_VAL, + SUNXI_SPI1_CS1_VAL, SUNXI_SPI2_CS1_VAL, SUNXI_SPI3_CS1_VAL}; + +static int sunxi_spi_ofdata_to_platdata(struct udevice *dev) { + debug("%s: %p\n", __func__, dev); + if (!dev) + return -ENODEV; + + struct sunxi_spi_platdata *plat = dev_get_platdata(dev); + const void *blob = gd->fdt_blob; + int node = dev->of_offset; + u32 data[2]; + int ret; + + ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data)); + if (ret) { + printf("Error: Can't get base address (ret=%d)\n", ret); + return -ENODEV; + } + + plat->base = data[0]; + plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 24000000); + + debug("%s: base=%x, max-frequency=%d\n", + __func__, plat->base, plat->max_hz); + + return 0; +}; + +static int sunxi_spi_init(struct udevice *dev) { + debug("%s: %p\n", __func__, dev); + if (!dev) + return -ENODEV; + + struct sunxi_spi_platdata *plat = dev_get_platdata(dev); + struct sunxi_spi_reg *spi = (struct sunxi_spi_reg *)plat->base; + u32 spi_ver = readl(&spi->VER); + printf("sunxi SPI @0x%08x, version %d.%d\n", + plat->base, spi_ver >> 16, spi_ver & 0xffff); + return 0; +}; + +static int sunxi_spi_probe(struct udevice *dev) { + debug("%s: %p\n", __func__, dev); + if (!dev) + return -ENODEV; + + struct sunxi_spi_privdata *priv = dev_get_priv(dev); + + if (!priv->spi_is_init) { + sunxi_spi_init(dev); + priv->spi_is_init = 1; + } + + return 0; +}; + +static int sunxi_spi_claim_bus(struct udevice *dev) { + debug("%s: %p %p\n", __func__, dev, dev->parent); + if (!dev) + return -ENODEV; + + struct sunxi_spi_platdata *plat = dev_get_platdata(dev->parent); + struct sunxi_spi_privdata *priv = dev_get_priv(dev->parent); + struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev); + + int seq = dev->parent->seq; + if (seq<0 || seq>SUNXI_MAX_SPI_SEQ) + return -ENODEV; + + // cs can be either 0 or 1 + if (slave->cs<0 || slave->cs>1) + return -ENODEV; + // cs can be one only for bus 1 and bus 3 + if ((seq==0 || seq==2) && slave->cs==1) + return -ENODEV; + + struct sunxi_ccm_reg* const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_spi_reg* spi = (struct sunxi_spi_reg *)plat->base; + + sunxi_gpio_set_cfgpin(sunxi_spi_mosi_pin[seq], sunxi_spi_mosi_val[seq]); + sunxi_gpio_set_cfgpin(sunxi_spi_miso_pin[seq], sunxi_spi_miso_val[seq]); + sunxi_gpio_set_cfgpin(sunxi_spi_clk_pin[seq], sunxi_spi_clk_val[seq]); + if (slave->cs==0) + sunxi_gpio_set_cfgpin(sunxi_spi_cs0_pin[seq], sunxi_spi_cs0_val[seq]); + else + sunxi_gpio_set_cfgpin(sunxi_spi_cs1_pin[seq], sunxi_spi_cs1_val[seq]); + + /* Reset FIFOs */ + setbits_le32(&spi->FCR, (1 << 31) | (1 << 15)); + + switch (seq) { + case 0: + setbits_le32(&ccm->spi0_clk_cfg, + (priv->clk_div_m << 0) | (priv->clk_div_n << 16)); + setbits_le32(&ccm->spi0_clk_cfg, (1 << 31)); + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI0); + setbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI0); + break; + case 1: + setbits_le32(&ccm->spi1_clk_cfg, + (priv->clk_div_m << 0) | (priv->clk_div_n << 16)); + setbits_le32(&ccm->spi1_clk_cfg, (1 << 31)); + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI1); + setbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI1); + break; + case 2: + setbits_le32(&ccm->spi2_clk_cfg, + (priv->clk_div_m << 0) | (priv->clk_div_n << 16)); + setbits_le32(&ccm->spi2_clk_cfg, (1 << 31)); + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI2); + setbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI2); + break; + case 3: + setbits_le32(&ccm->spi3_clk_cfg, + (priv->clk_div_m << 0) | (priv->clk_div_n << 16)); + setbits_le32(&ccm->spi3_clk_cfg, (1 << 31)); + setbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI3); + setbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI3); + break; + } + + setbits_le32(&spi->GCR, SUNXI_SPI_GCR_MASTER | SUNXI_SPI_GCR_EN); + setbits_le32(&spi->TCR, priv->clk_pol | priv->clk_pha); + + udelay(10); + + return 0; +}; + +static int sunxi_spi_release_bus(struct udevice *dev) { + debug("%s: %p\n", __func__, dev); + if (!dev) + return -ENODEV; + + struct sunxi_spi_platdata *plat = dev_get_platdata(dev->parent); + int seq = dev->parent->seq; + if (seq<0 || seq>SUNXI_MAX_SPI_SEQ) + return -ENODEV; + + struct sunxi_ccm_reg* const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_spi_reg* spi = (struct sunxi_spi_reg *)plat->base; + + sunxi_gpio_set_cfgpin(sunxi_spi_mosi_pin[seq], SUNXI_PIN_INPUT_DISABLE); + sunxi_gpio_set_cfgpin(sunxi_spi_miso_pin[seq], SUNXI_PIN_INPUT_DISABLE); + sunxi_gpio_set_cfgpin(sunxi_spi_clk_pin[seq], SUNXI_PIN_INPUT_DISABLE); + sunxi_gpio_set_cfgpin(sunxi_spi_cs0_pin[seq], SUNXI_PIN_INPUT_DISABLE); + sunxi_gpio_set_cfgpin(sunxi_spi_cs1_pin[seq], SUNXI_PIN_INPUT_DISABLE); + + switch (seq) { + case 0: + clrbits_le32(&ccm->spi0_clk_cfg, (1 << 31)); + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI0); + clrbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI0); + break; + case 1: + clrbits_le32(&ccm->spi1_clk_cfg, (1 << 31)); + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI1); + clrbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI1); + break; + case 2: + clrbits_le32(&ccm->spi2_clk_cfg, (1 << 31)); + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI2); + clrbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI2); + break; + case 3: + clrbits_le32(&ccm->spi3_clk_cfg, (1 << 31)); + clrbits_le32(&ccm->ahb_gate0, CCM_AHB_GATE_SPI3); + clrbits_le32(&ccm->ahb_reset0_cfg, CCM_AHB_RESET_SPI3); + break; + } + + clrbits_le32(&spi->GCR, SUNXI_SPI_GCR_MASTER | SUNXI_SPI_GCR_EN); + clrbits_le32(&spi->TCR, SUNXI_SPI_CPOL_LOW | SUNXI_SPI_CPOL_LOW); + + return 0; +}; + +static int sunxi_spi_set_wordlen(struct udevice *dev, unsigned int wordlen) { + debug("%s: %p, %d\n", __func__, dev, wordlen); + if (!dev) + return -ENODEV; + + return 0; +}; + +static int sunxi_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + debug("%s: %p, %d, %p, %016lx\n", __func__, dev, bitlen, din, flags); + if (!dev) + return -ENODEV; + + int n_bytes = DIV_ROUND_UP(bitlen, 8); + int ret; + u32 blk_size, cnt; + u8 *p_outbuf = (u8*)dout; + u8 *p_inbuf = (u8*)din; + unsigned char* p; + + struct sunxi_spi_platdata *plat = dev_get_platdata(dev->parent); + struct sunxi_spi_reg* spi = (struct sunxi_spi_reg *)plat->base; + + while (!(readl(&spi->ISR) & (1 << 1) /* RXFIFO empty */ )) + (void)readb(&spi->RXD); + + if (flags & SPI_XFER_BEGIN) { + setbits_le32(&spi->TCR, (1 << 6)); // SS_OWNER + clrbits_le32(&spi->TCR, (1 << 7)); // SS_LEVEL + } + + while (n_bytes>0) { + ret = 0; + + if (n_bytesMBC); + writel(blk_size, &spi->MTC); + writel(blk_size, &spi->BCC); + + p = p_outbuf; + while (cnt--) { + writeb(*p++, &spi->TXD); + } + + setbits_le32(&spi->TCR, (1 << 31)); // XCHG bit + + while (readl(&spi->TCR) & (1 << 31)) + /* wait for the xfer to complete */; + + setbits_le32(&spi->ISR, (1 << 12)); // clear TC + cnt = blk_size; + + p = p_inbuf; + while (cnt--) { + unsigned char c; + c = readb(&spi->RXD); + if (din) { + *p++ = c; + } + } + + if (ret) + return ret; + if (dout) + p_outbuf += blk_size; + if (din) + p_inbuf += blk_size; + n_bytes -= blk_size; + } + + if (flags & SPI_XFER_END) { + setbits_le32(&spi->TCR, (1 << 6)); // SS_OWNER + setbits_le32(&spi->TCR, (1 << 7)); // SS_LEVEL + } + udelay(1); + + return 0; +}; + +static int sunxi_spi_set_speed(struct udevice *bus, unsigned int hz) { + debug("%s: %p, %d\n", __func__, bus, hz); + if (!bus) + return -ENODEV; + + struct sunxi_spi_platdata *plat = dev_get_platdata(bus); + struct sunxi_spi_privdata *priv = dev_get_priv(bus); + + if (hz > plat->max_hz) { + debug("selected speed exceeds maximum of %d, using max instead\n", + plat->max_hz); + hz = plat->max_hz; + } + + // frequency is base / (M * N) + // M = m+1, N = 2^n + unsigned int base_hz = 24000000; + unsigned int M, N, m, n; + unsigned int factor = base_hz / hz; // M * N + + if (factor > 4*16) { + N = 8; + n = 3; + } + else if (factor > 2*16) { + N = 4; + n = 2; + } + else if (factor > 16) { + N = 2; + n = 1; + } + else { + N = 1; + n = 0; + } + + M = factor/N; + if (M>16) + M = 16; + if (M<1) + M = 1; + m = M-1; + + debug("spi frequency: %d, (24 / (%d x %d) )\n", + base_hz / (M * N), M, N); + + priv->clk_div_n = n; + priv->clk_div_m = m; + + return 0; +}; + +static int sunxi_spi_set_mode(struct udevice *bus, unsigned int mode) { + debug("%s: %p, %08x\n", __func__, bus, mode); + if (!bus) + return -ENODEV; + + struct sunxi_spi_privdata *priv = dev_get_priv(bus); + + priv->clk_pol = (mode & SUNXI_SPI_CPOL_LOW); + priv->clk_pha = (mode & SUNXI_SPI_CPHA_LOW); + + return 0; +}; + +static int sunxi_spi_cs_info(struct udevice *bus, unsigned int cs, + struct spi_cs_info *info) +{ + debug("%s: %p, %d\n", __func__, bus, cs); + if (!bus) + return -ENODEV; + if (!info) + return -EFAULT; + + int seq = bus->seq; + + // TODO: for now, spi0 and spi2 support only cs0, while spi1 and spi3 + // support cs0 and cs1; anything else is not supported; do we need + // something fancier here? + if (seq<0 || seq>SUNXI_MAX_SPI_SEQ) + return -ENODEV; + if (cs>1) + return -ENODEV; + if ((seq==0 || seq==2) && (cs!=0)) + return -ENODEV; + + return 0; +}; + +static const struct dm_spi_ops sunxi_spi_ops = { + .claim_bus = sunxi_spi_claim_bus, + .release_bus = sunxi_spi_release_bus, + .set_wordlen = sunxi_spi_set_wordlen, + .xfer = sunxi_spi_xfer, + .set_speed = sunxi_spi_set_speed, + .set_mode = sunxi_spi_set_mode, + .cs_info = sunxi_spi_cs_info, +}; + +static const struct udevice_id sunxi_spi_ids[] = { + { .compatible = "allwinner,sun6i-a31-spi" }, + { } +}; + +U_BOOT_DRIVER(sunxi_spi) = { + .name = "sunxi_spi", + .id = UCLASS_SPI, + .of_match = sunxi_spi_ids, + .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_privdata), + .probe = sunxi_spi_probe, + .ops = &sunxi_spi_ops, +}; diff --git a/drivers/spi/sunxi_spi.h b/drivers/spi/sunxi_spi.h new file mode 100644 index 0000000000..7a25bbe2d6 --- /dev/null +++ b/drivers/spi/sunxi_spi.h @@ -0,0 +1,143 @@ +/* + * SPI driver for Allwinner sunxi SoCs + * + * Supported chips + * - Allwinner A31 (sun6i) + * + * Copyright (C) 2015 Theobroma Systems Design und Consulting GmbH + * Octav Zlatior + * Philipp Tomsich + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef __SUNXI_SPI_H__ +#define __SUNXI_SPI_H__ + +#define MAX_SPI_BYTES 64 + +#define CCM_AHB_GATE_SPI0 (1 << 20) +#define CCM_AHB_GATE_SPI1 (1 << 21) +#define CCM_AHB_GATE_SPI2 (1 << 22) +#define CCM_AHB_GATE_SPI3 (1 << 23) + +#define CCM_AHB_RESET_SPI0 (1 << 20) +#define CCM_AHB_RESET_SPI1 (1 << 21) +#define CCM_AHB_RESET_SPI2 (1 << 22) +#define CCM_AHB_RESET_SPI3 (1 << 23) + +#define SUNXI_MAX_SPI_SEQ 3 + +#define SUNXI_SPI0_MOSI_PIN SUNXI_GPC(0) +#define SUNXI_SPI0_MISO_PIN SUNXI_GPC(1) +#define SUNXI_SPI0_CLK_PIN SUNXI_GPC(2) +#define SUNXI_SPI0_CS0_PIN SUNXI_GPC(27) +#define SUNXI_SPI0_CS1_PIN -1 +#define SUNXI_SPI0_MOSI_VAL 3 +#define SUNXI_SPI0_MISO_VAL 3 +#define SUNXI_SPI0_CLK_VAL 3 +#define SUNXI_SPI0_CS0_VAL 3 +#define SUNXI_SPI0_CS1_VAL -1 + +#define SUNXI_SPI1_MOSI_PIN SUNXI_GPG(15) +#define SUNXI_SPI1_MISO_PIN SUNXI_GPG(16) +#define SUNXI_SPI1_CLK_PIN SUNXI_GPG(14) +#define SUNXI_SPI1_CS0_PIN SUNXI_GPG(13) +#define SUNXI_SPI1_CS1_PIN SUNXI_GPG(12) +#define SUNXI_SPI1_MOSI_VAL 2 +#define SUNXI_SPI1_MISO_VAL 2 +#define SUNXI_SPI1_CLK_VAL 2 +#define SUNXI_SPI1_CS0_VAL 2 +#define SUNXI_SPI1_CS1_VAL 2 + +#define SUNXI_SPI2_MOSI_PIN SUNXI_GPH(11) +#define SUNXI_SPI2_MISO_PIN SUNXI_GPH(12) +#define SUNXI_SPI2_CLK_PIN SUNXI_GPH(10) +#define SUNXI_SPI2_CS0_PIN SUNXI_GPH(9) +#define SUNXI_SPI2_CS1_PIN -1 +#define SUNXI_SPI2_MOSI_VAL 2 +#define SUNXI_SPI2_MISO_VAL 2 +#define SUNXI_SPI2_CLK_VAL 2 +#define SUNXI_SPI2_CS0_VAL 2 +#define SUNXI_SPI2_CS1_VAL -1 + +#define SUNXI_SPI3_MOSI_PIN SUNXI_GPA(23) +#define SUNXI_SPI3_MISO_PIN SUNXI_GPA(24) +#define SUNXI_SPI3_CLK_PIN SUNXI_GPA(22) +#define SUNXI_SPI3_CS0_PIN SUNXI_GPA(21) +#define SUNXI_SPI3_CS1_PIN SUNXI_GPA(25) +#define SUNXI_SPI3_MOSI_VAL 4 +#define SUNXI_SPI3_MISO_VAL 4 +#define SUNXI_SPI3_CLK_VAL 4 +#define SUNXI_SPI3_CS0_VAL 4 +#define SUNXI_SPI3_CS1_VAL 4 + +#define SUNXI_PIN_INPUT_DISABLE 7 + +#define SUNXI_SPI_GCR_MASTER (1 << 1) +#define SUNXI_SPI_GCR_EN (1 << 0) + +#define SUNXI_SPI_CPOL_LOW (1 << 1) +#define SUNXI_SPI_CPHA_LOW (1 << 0) + +#define SUNXI_SPI_CPOL_HIGH (1 << 1) +#define SUNXI_SPI_CPHA_HIGH (1 << 0) + +#define SUNXI_SPI_MOSI_PIN(n) SUNXI_SPI##n##_MOSI_PIN +#define SUNXI_SPI_MOSI_VAL(n) SUNXI_SPI##n##_MOSI_VAL +#define SUNXI_SPI_MISO_PIN(n) SUNXI_SPI##n##_MISO_PIN +#define SUNXI_SPI_MISO_VAL(n) SUNXI_SPI##n##_MISO_VAL +#define SUNXI_SPI_CLK_PIN(n) SUNXI_SPI##n##_CLK_PIN +#define SUNXI_SPI_CLK_VAL(n) SUNXI_SPI##n##_CLK_VAL +#define SUNXI_SPI_CS0_PIN(n) SUNXI_SPI##n##_CS0_PIN +#define SUNXI_SPI_CS0_VAL(n) SUNXI_SPI##n##_CS0_VAL +#define SUNXI_SPI_CS1_PIN(n) SUNXI_SPI##n##_CS1_PIN +#define SUNXI_SPI_CS1_VAL(n) SUNXI_SPI##n##_CS1_VAL + +struct sunxi_spi_platdata { + unsigned int max_hz; + unsigned int base; +}; + +struct sunxi_spi_privdata { + u8 spi_is_init; + u8 clk_pol; + u8 clk_pha; + u8 clk_div_n; + u8 clk_div_m; +}; + +struct sunxi_spi_reg { + u32 VER; /* SPI Version Number register */ + u32 GCR; /* SPI Global Control register */ + u32 TCR; /* SPI Transfer Control register */ + u32 _reserved1; + u32 IER; /* SPI Interrupt Control register */ + u32 ISR; /* SPI Interrupt Status register */ + u32 FCR; /* SPI FIFO Control register */ + u32 FSR; /* SPI FIFO Status register */ + u32 WCR; /* SPI Wait Clock Counter register */ + u32 CCR; /* SPI Clock Rate Control register */ + u32 _reserved2; + u32 _reserved3; + u32 MBC; /* SPI Burst Counter register */ + u32 MTC; /* SPI Transmit Counter register */ + u32 BCC; /* SPI Burst Control register */ + u32 _reserved4[113]; + u32 TXD; /* SPI TX Data register */ + u32 _reserved5[63]; + u32 RXD; /* SPI RX Data register */ +}; + +struct sunxi_spi_slave { + struct spi_slave slave; + struct sunxi_spi_reg* base; + int polarity; + unsigned int max_hz; + unsigned int mode; +}; + +#endif // __SUNXI_SPI_H__ -- cgit v1.2.3