summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorOctav Zlatior <octav.zlatior@theobroma-systems.com>2015-07-08 16:46:40 +0200
committerKlaus Goger <klaus.goger@theobroma-systems.com>2016-09-18 15:57:26 +0200
commit7b465e83280d38b0675ad96ae13fbee4be541eeb (patch)
tree660b45e7878f82ae8aced52c50600af7816e723a /drivers
parent5097e3b196211eda8456368de7a6dac523605141 (diff)
ARM: sun6i: spi: implements SPI driver
The driver currently only supports sun6i. Signed-off-by: Octav Zlatior <octav.zlatior@theobroma-systems.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/sunxi_spi.c430
-rw-r--r--drivers/spi/sunxi_spi.h143
4 files changed, 581 insertions, 0 deletions
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 <octav.zlatior@theobroma-systems.com>
+ * Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
+ *
+ * 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 <common.h>
+#include <dm.h>
+#include <spi.h>
+#include <fdtdec.h>
+#include <asm/errno.h>
+#include <configs/sun6i.h>
+#include <asm/io.h>
+#include <asm/arch-sunxi/gpio.h>
+#include <asm/arch-sunxi/clock_sun6i.h>
+
+#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_bytes<MAX_SPI_BYTES)
+ blk_size = n_bytes;
+ else
+ blk_size = MAX_SPI_BYTES;
+
+ cnt = blk_size;
+
+ // start XCHG
+ writel(blk_size, &spi->MBC);
+ 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 <octav.zlatior@theobroma-systems.com>
+ * Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
+ *
+ * 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__