Patchwork [U-Boot] andes_spi: add andes_spi interface

login
register
mail settings
Submitter Macpaul Lin
Date April 22, 2011, 7:20 a.m.
Message ID <1303456847-7215-1-git-send-email-macpaul@andestech.com>
Download mbox | patch
Permalink /patch/92503/
State Superseded
Headers show

Comments

Macpaul Lin - April 22, 2011, 7:20 a.m.
andes_spi is an spi interface developed by Andes Tech.

Signed-off-by: Macpaul Lin <macpaul@andestech.com>
---
 drivers/spi/Makefile    |    1 +
 drivers/spi/andes_spi.c |  302 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/andes_spi.h |  128 ++++++++++++++++++++
 3 files changed, 431 insertions(+), 0 deletions(-)
 create mode 100644 drivers/spi/andes_spi.c
 create mode 100644 drivers/spi/andes_spi.h
Mike Frysinger - April 22, 2011, 9:04 p.m.
On Fri, Apr 22, 2011 at 3:20 AM, Macpaul Lin wrote:
> +#ifdef DEBUG
> +#define debug(fmt, args...)    printf(fmt, ##args)
> +#else
> +#define debug(fmt, args...)
> +#endif

common.h already provides this for you

> +void andes_spi_spit_en(struct andes_spi_slave *ds)
> +{
> +       debug("%s: dcr: %x, write value: %x\n",
> +                       __func__, readl(&ds->regs->dcr),
> +                       readl(&ds->regs->dcr) | ANDES_SPI_DCR_SPIT);
> +       writel((readl(&ds->regs->dcr) | ANDES_SPI_DCR_SPIT), &ds->regs->dcr);
> +}

putting io accessors into debug() is generally a bad idea.  i'd
suggest you store this in a local variable and then pass that to
everything else.  it'd also make the code cleaner.
unsigned long dcr = readl(&ds->regs->dcr);

> +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
> +                       unsigned int max_hz, unsigned int mode)
> +{
> +       struct andes_spi_slave  *ds;
> +
> +       if (!spi_cs_is_valid(bus, cs))
> +               return NULL;
> +
> +       ds = malloc(sizeof(*ds));
> +       if (!ds)
> +               return NULL;
> +
> +       ds->slave.bus = bus;
> +       ds->slave.cs = cs;
> +       ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE;
> +       ds->freq = max_hz;

your spi controller has no frequency limit ?  your spi_claim_bus
indicates that there is ...

> +static int andes_spi_read(struct spi_slave *slave, unsigned int len,
> +                           u8 *rxp, unsigned long flags)
> +{
> ....
> +       while (len > 0) {
> +               debug(" ");
> +               if (len / 4 > 0)
> +                       left = 4;
> +               else
> +                       left = len;

seems like you want:
left = max(len, 4);

> +               data = readl(&ds->regs->data);
> +               for (i = 0; i < left; i++) {
> +                       debug("%02x ", data & 0xff);
> +                       *rxp++ = data;
> +                       data >>= 8;
> +                       len--;
> +               }
> +       }

looks to me like you write too much data ... if someone does a spi
read of 1 byte, you still write out 4 ?

> +int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
> +            const void *dout, void *din, unsigned long flags)
> +{
> ...
> +       /*
> +        * It's not clear how non-8-bit-aligned transfers are supposed to be
> +        * represented as a stream of bytes...this is a limitation of
> +        * the current SPI interface - here we terminate on receiving such a
> +        * transfer request.
> +        */
> +
> +       if (bitlen % 8) {
> +               /* Errors always terminate an ongoing transfer */
> +               flags |= SPI_XFER_END;
> +               goto out;
> +       }

this is correct behavior

> --- /dev/null
> +++ b/drivers/spi/andes_spi.h

i cant think of any reason other code would want to include this ...
so please put this header in drivers/spi/
-mike
Macpaul Lin - April 23, 2011, 4:34 a.m.
Hi Mike,

> +       ds->slave.bus = bus;
> > +       ds->slave.cs = cs;
> > +       ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE;
> > +       ds->freq = max_hz;
>
>
your spi controller has no frequency limit ?  your spi_claim_bus
> indicates that there is ...
>
>
Hey, it really strange but I couldn't help.  ;p
According to the hardware datasheet, the only thing related to frequence is
baudrate.
But, any value I set to baudrate was no affect.
It seems that something has done between the hardware itselves.
I've asked the designer and he said the software coder should not care about
this.


>  > +static int andes_spi_read(struct spi_slave *slave, unsigned int len,
> > +                           u8 *rxp, unsigned long flags)
> > +{
> > ....
> > +       while (len > 0) {
> > +               debug(" ");
> > +               if (len / 4 > 0)
> > +                       left = 4;
> > +               else
> > +                       left = len;
>
> seems like you want:
> left = max(len, 4);
>

No. Since the hardware is really designed so strange.
Although the data of SPI should be transfered byte-by-bytes.
But this hardware requires you put the data 32bit (a register's length)
once.
So, If you exactly have a data more then 4 bytes, you must have to divide
them
into 4 bytes a chunk, and write them 4 bytes a time.
That's why left = max(len, 4) doesn't help. The value of left cannot larger
than 4.


> > +               data = readl(&ds->regs->data);
> > +               for (i = 0; i < left; i++) {
> > +                       debug("%02x ", data & 0xff);
> > +                       *rxp++ = data;
> > +                       data >>= 8;
> > +                       len--;
> > +               }
> > +       }
>
> looks to me like you write too much data ... if someone does a spi
> read of 1 byte, you still write out 4 ?
>
>
Same problem here. This SPI interface must read and write 4 byte a time.
The hardware designer thinks they are really smart.
The hardware will strip the padding according to the data lenght in
"WRITEthenREAD" command.

Thanks.
I'll fix the rest of those problem and send patch v2.
Mike Frysinger - April 23, 2011, 5:07 p.m.
On Sat, Apr 23, 2011 at 12:34 AM, Macpaul Lin wrote:
>> > +       ds->slave.bus = bus;
>> > +       ds->slave.cs = cs;
>> > +       ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE;
>> > +       ds->freq = max_hz;
>>
>> your spi controller has no frequency limit ?  your spi_claim_bus
>> indicates that there is ...
>
> Hey, it really strange but I couldn't help.  ;p
> According to the hardware datasheet, the only thing related to frequence is
> baudrate.
> But, any value I set to baudrate was no affect.
> It seems that something has done between the hardware itselves.
> I've asked the designer and he said the software coder should not care about
> this.

unfortunately your designer is mistaken.  software must be aware of it
considering SPI slaves often have frequency limits, and board designs
sometimes arent that great.

even looking at the baud setup code, there is math there that overflows.

>> > +static int andes_spi_read(struct spi_slave *slave, unsigned int len,
>> > +                           u8 *rxp, unsigned long flags)
>> > +{
>> > ....
>> > +       while (len > 0) {
>> > +               debug(" ");
>> > +               if (len / 4 > 0)
>> > +                       left = 4;
>> > +               else
>> > +                       left = len;
>>
>> seems like you want:
>> left = max(len, 4);
>
> No. Since the hardware is really designed so strange.

so you want:
left = min(len, 4);
-mike

Patch

diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d582fbb..8ee8f68 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -26,6 +26,7 @@  include $(TOPDIR)/config.mk
 LIB	:= $(obj)libspi.o
 
 COBJS-$(CONFIG_ALTERA_SPI) += altera_spi.o
+COBJS-$(CONFIG_ANDES_SPI) += andes_spi.o
 COBJS-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
 COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o
 COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o
diff --git a/drivers/spi/andes_spi.c b/drivers/spi/andes_spi.c
new file mode 100644
index 0000000..a03f5c5
--- /dev/null
+++ b/drivers/spi/andes_spi.c
@@ -0,0 +1,302 @@ 
+/*
+ * Driver of Andes SPI Controller
+ *
+ * (C) Copyright 2011 Andes Technology
+ * Macpaul Lin <macpaul@andestech.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+
+#include <asm/io.h>
+#include "andes_spi.h"
+
+#ifdef DEBUG
+#define debug(fmt, args...)	printf(fmt, ##args)
+#else
+#define debug(fmt, args...)
+#endif
+
+void spi_init()
+{
+	/* do nothing */
+}
+
+void andes_spi_spit_en(struct andes_spi_slave *ds)
+{
+	debug("%s: dcr: %x, write value: %x\n",
+			__func__, readl(&ds->regs->dcr),
+			readl(&ds->regs->dcr) | ANDES_SPI_DCR_SPIT);
+	writel((readl(&ds->regs->dcr) | ANDES_SPI_DCR_SPIT), &ds->regs->dcr);
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+			unsigned int max_hz, unsigned int mode)
+{
+	struct andes_spi_slave	*ds;
+
+	if (!spi_cs_is_valid(bus, cs))
+		return NULL;
+
+	ds = malloc(sizeof(*ds));
+	if (!ds)
+		return NULL;
+
+	ds->slave.bus = bus;
+	ds->slave.cs = cs;
+	ds->regs = (struct andes_spi_regs *)CONFIG_SYS_SPI_BASE;
+	ds->freq = max_hz;
+
+	return &ds->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+	struct andes_spi_slave *ds = to_andes_spi(slave);
+
+	free(ds);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	struct andes_spi_slave *ds = to_andes_spi(slave);
+	unsigned int scalar;
+
+	/* Enable the SPI hardware */
+	writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr);
+	udelay(1000);
+
+	/* setup format */
+	scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF;
+
+	/* no interrupts */
+	writel(0, &ds->regs->ie);
+
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	struct andes_spi_slave *ds = to_andes_spi(slave);
+
+	/* Disable the SPI hardware */
+	writel(ANDES_SPI_CR_SPIRST, &ds->regs->cr);
+}
+
+static int andes_spi_read(struct spi_slave *slave, unsigned int len,
+			    u8 *rxp, unsigned long flags)
+{
+	struct andes_spi_slave *ds = to_andes_spi(slave);
+	unsigned int i = 0, left = 0;
+	unsigned int data = 0;
+
+	debug("%s: slave: %x, len: %d, rxp: %x, flags: %d\n",
+		__func__, slave, len, rxp, flags);
+
+	debug("%s: data: ", __func__);
+	while (len > 0) {
+		debug(" ");
+		if (len / 4 > 0)
+			left = 4;
+		else
+			left = len;
+
+		data = readl(&ds->regs->data);
+		for (i = 0; i < left; i++) {
+			debug("%02x ", data & 0xff);
+			*rxp++ = data;
+			data >>= 8;
+			len--;
+		}
+	}
+	debug("\n");
+
+	return 0;
+}
+
+static int andes_spi_write(struct spi_slave *slave, unsigned int wlen,
+			unsigned int rlen, const u8 *txp, unsigned long flags)
+{
+	struct andes_spi_slave *ds = to_andes_spi(slave);
+	unsigned int data = 0;
+	unsigned int i, left;
+	unsigned int spit_enabled = 0;
+
+	debug("%s: slave: %x, wlen: %d, rlen: %d, txp: %x, flags: %x\n",
+		__func__, slave, wlen, rlen, txp, flags);
+
+	/* The value of wlen and rlen wrote to register must minus 1 */
+	if (rlen == 0)					/* write only */
+		writel(ANDES_SPI_DCR_MODE_WO | ANDES_SPI_DCR_WCNT(wlen-1) |
+				ANDES_SPI_DCR_RCNT(0), &ds->regs->dcr);
+	else						/* write then read */
+		writel(ANDES_SPI_DCR_MODE_WR | ANDES_SPI_DCR_WCNT(wlen-1) |
+				ANDES_SPI_DCR_RCNT(rlen-1), &ds->regs->dcr);
+
+	/* wait till SPIBSY is cleared */
+	while (readl(&ds->regs->st) & ANDES_SPI_ST_SPIBSY)
+		;
+
+	/* data write process */
+	debug("%s: txp: ", __func__);
+	while (wlen > 0) {
+		/* data are usually be read 32bits a time */
+		if (wlen / 4 > 0)
+			left = 4;
+		else
+			left = wlen;
+
+		for (i = 0; i < left; i++) {
+			debug("%x ", *txp);
+			data |= *txp++ << (i * 8);
+			wlen--;
+		}
+		debug("\n");
+
+		debug("data: %08x\n", data);
+		debug("streg before write: %08x\n", readl(&ds->regs->st));
+		/* wait till TXFULL is deasserted */
+		while (readl(&ds->regs->st) & ANDES_SPI_ST_TXFEL)
+			;
+		writel(data, &ds->regs->data);
+		debug("streg after write: %08x\n", readl(&ds->regs->st));
+
+		data = 0;
+
+		if (spit_enabled == 0) {
+			/* enable SPIT bit -  trigger the tx and rx progress */
+			andes_spi_spit_en(ds);
+			spit_enabled = 1;
+		}
+
+	}
+	debug("\n");
+
+	return 0;
+}
+
+/*
+ * spi_xfer:
+ *	Since andes_spi doesn't support independent command transaction,
+ *	that is, write and than read must be operated in continuous
+ *	execution, there is no need to set dcr and trigger spit again in
+ *	RX process.
+ */
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+	     const void *dout, void *din, unsigned long flags)
+{
+	unsigned int len;
+	static int op_nextime;
+	static u8 tmp_cmd[5];
+	static int tmp_wlen;
+	unsigned int i;
+
+	if (bitlen == 0)
+		/* Finish any previously submitted transfers */
+		goto out;
+
+	/*
+	 * It's not clear how non-8-bit-aligned transfers are supposed to be
+	 * represented as a stream of bytes...this is a limitation of
+	 * the current SPI interface - here we terminate on receiving such a
+	 * transfer request.
+	 */
+	if (bitlen % 8) {
+		/* Errors always terminate an ongoing transfer */
+		flags |= SPI_XFER_END;
+		goto out;
+	}
+
+	len = bitlen / 8;
+
+	debug("%s: slave: %08x, bitlen: %d, dout: "
+		"%08x, din: %08x, flags: %d, len: %d\n",
+		__func__, slave, bitlen, dout, din, flags, len);
+
+	/*
+	 * Important:
+	 *	andes_spi's hardware doesn't support 2 data channel. The read
+	 *	and write cmd/data share the same register (data).
+	 *
+	 *	If a command has write and read transaction, you cannot do write
+	 *	this time and then do read on next time.
+	 *
+	 *	A command writes first with a read response must indicating
+	 *	the read length in write operation. Hence the write action must
+	 *	be stored temporary and wait until the next read action has been
+	 *	arrived. Then we flush the write and read action out together.
+	 */
+	if (!dout) {
+		if (op_nextime == 1) {
+			/* flags should be SPI_XFER_END, value is 2 */
+			op_nextime = 0;
+			andes_spi_write(slave, tmp_wlen, len, tmp_cmd, flags);
+		}
+		return andes_spi_read(slave, len, din, flags);
+	} else if (!din) {
+		if (flags == SPI_XFER_BEGIN) {
+			/* store the write command and do operation next time */
+			op_nextime = 1;
+			memset(tmp_cmd, 0, sizeof(tmp_cmd));
+			memcpy(tmp_cmd, dout, len);
+
+			debug("%s: tmp_cmd: ", __func__);
+			for (i = 0; i < len; i++)
+				debug("%x ", *(tmp_cmd + i));
+			debug("\n");
+
+			tmp_wlen = len;
+		} else {
+			/*
+			 * flags should be (SPI_XFER_BEGIN | SPI_XFER_END),
+			 * value is 3i.
+			 */
+			if (op_nextime == 1) {
+				/* flags should be SPI_XFER_END, value is 2 */
+				op_nextime = 0;
+				/* flags 3 implies write only */
+				andes_spi_write(slave, tmp_wlen, 0, tmp_cmd, 3);
+			}
+
+			debug("flags: %x\n", flags);
+			return andes_spi_write(slave, len, 0, dout, flags);
+		}
+	}
+
+out:
+	return 0;
+}
+
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+	return bus == 0 && cs == 0;
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+	/* do nothing */
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+	/* do nothing */
+}
diff --git a/drivers/spi/andes_spi.h b/drivers/spi/andes_spi.h
new file mode 100644
index 0000000..844bdba
--- /dev/null
+++ b/drivers/spi/andes_spi.h
@@ -0,0 +1,128 @@ 
+/*
+ * Register definitions for the Andes SPI Controller
+ *
+ * (C) Copyright 2011 Andes Technology
+ * Macpaul Lin <macpaul@andestech.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef __ANDES_SPI_H
+#define __ANDES_SPI_H
+
+struct andes_spi_regs {
+	unsigned int	apb;		/* 0x00 - APB SPI interface setting */
+	unsigned int	pio;		/* 0x04 - PIO reg */
+	unsigned int	cr;		/* 0x08 - SPI Control reg */
+	unsigned int	st;		/* 0x0c - SPI Status reg */
+	unsigned int	ie;		/* 0x10 - Interrupt Enable reg */
+	unsigned int	ist;		/* 0x14 - Interrupt Status reg */
+	unsigned int	dcr;		/* 0x18 - data control reg */
+	unsigned int	data;		/* 0x1c - data register */
+	unsigned int	ahb;		/* 0x20 - AHB SPI interface setting */
+	unsigned int	ver;		/* 0x3c - SPI version reg */
+};
+
+#define BIT(x)			(1 << (x))
+
+/* 0x00 - APB SPI interface setting register */
+#define ANDES_SPI_APB_BAUD(x)	(((x) & 0xff) < 0)
+#define ANDES_SPI_APB_CSHT(x)	(((x) & 0xf) < 16)
+#define ANDES_SPI_APB_SPNTS	BIT(20)		/* 0: normal, 1: delay */
+#define ANDES_SPI_APB_CPHA	BIT(24)		/* 0: Sampling at odd edges */
+#define ANDES_SPI_APB_CPOL	BIT(25)		/* 0: SCK low, 1: SCK high */
+#define ANDES_SPI_APB_MSSL	BIT(26)		/* 0: SPI Master, 1: slave */
+
+/* 0x04 - PIO register */
+#define ANDES_SPI_PIO_MISO	BIT(0)		/* input value of pin MISO */
+#define ANDES_SPI_PIO_MOSI	BIT(1)		/* I/O value of pin MOSI */
+#define ANDES_SPI_PIO_SCK	BIT(2)		/* I/O value of pin SCK */
+#define ANDES_SPI_PIO_CS	BIT(3)		/* I/O value of pin CS */
+#define ANDES_SPI_PIO_PIOE	BIT(4)		/* Programming IO Enable */
+
+/* 0x08 - SPI Control register */
+#define ANDES_SPI_CR_SPIRST	BIT(0)		/* SPI mode reset */
+#define ANDES_SPI_CR_RXFRST	BIT(1)		/* RxFIFO reset */
+#define ANDES_SPI_CR_TXFRST	BIT(2)		/* TxFIFO reset */
+#define ANDES_SPI_CR_RXFTH(x)	(((x) & 0x1f) << 10)	/* RxFIFO Threshold */
+#define ANDES_SPI_CR_TXFTH(x)	(((x) & 0x1f) << 18)	/* TxFIFO Threshold */
+
+/* 0x0c - SPI Status register */
+#define ANDES_SPI_ST_SPIBSY	BIT(0)		/* SPI Transfer is active */
+#define ANDES_SPI_ST_RXFEM	BIT(8)		/* RxFIFO Empty Flag */
+#define ANDES_SPI_ST_RXFEL	BIT(9)		/* RxFIFO Full Flag */
+#define ANDES_SPI_ST_RXFVE(x)	(((x) >> 10) & 0x1f)
+#define ANDES_SPI_ST_TXFEM	BIT(16)		/* TxFIFO Empty Flag */
+#define ANDES_SPI_ST_TXFEL	BIT(7)		/* TxFIFO Full Flag */
+#define ANDES_SPI_ST_TXFVE(x)	(((x) >> 18) & 0x1f)
+
+/* 0x10 - Interrupt Enable register */
+#define ANDES_SPI_IE_RXFORIE	BIT(0)		/* RxFIFO overrun intr */
+#define ANDES_SPI_IE_TXFURIE	BIT(1)		/* TxFOFO underrun intr */
+#define ANDES_SPI_IE_RXFTHIE	BIT(2)		/* RxFIFO threshold intr */
+#define ANDES_SPI_IE_TXFTHIE	BIT(3)		/* TxFIFO threshold intr */
+#define ANDES_SPI_IE_SPIEIE	BIT(4)		/* SPI transmit END intr */
+#define ANDES_SPI_IE_SPCFIE	BIT(5)		/* AHB/APB TxReq conflict */
+
+/* 0x14 - Interrupt Status Register */
+#define ANDES_SPI_IST_RXFORI	BIT(0)		/* has RxFIFO overrun */
+#define ANDES_SPI_IST_TXFURI	BIT(1)		/* has TxFOFO underrun */
+#define ANDES_SPI_IST_RXFTHI	BIT(2)		/* has RxFIFO threshold */
+#define ANDES_SPI_IST_TXFTHI	BIT(3)		/* has TxFIFO threshold */
+#define ANDES_SPI_IST_SPIEI	BIT(4)		/* has SPI transmit END */
+#define ANDES_SPI_IST_SPCFI	BIT(5)		/* has AHB/APB TxReq conflict */
+
+/* 0x18 - Data Control Register */
+#define ANDES_SPI_DCR_RCNT(x)		(((x) & 0x3ff) << 0)
+#define ANDES_SPI_DCR_DYCNT(x)		(((x) & 0x7) << 12)
+#define ANDES_SPI_DCR_WCNT(x)		(((x) & 0x3ff) << 16)
+#define ANDES_SPI_DCR_TRAMODE(x)	(((x) & 0x7) << 28)
+#define ANDES_SPI_DCR_SPIT		BIT(31)		/* SPI bus trigger */
+
+#define ANDES_SPI_DCR_MODE_WRCON	ANDES_SPI_DCR_TRAMODE(0)	/* w/r at the same time */
+#define ANDES_SPI_DCR_MODE_WO		ANDES_SPI_DCR_TRAMODE(1)	/* write only		*/
+#define ANDES_SPI_DCR_MODE_RO		ANDES_SPI_DCR_TRAMODE(2)	/* read only		*/
+#define ANDES_SPI_DCR_MODE_WR		ANDES_SPI_DCR_TRAMODE(3)	/* write, read		*/
+#define ANDES_SPI_DCR_MODE_RW		ANDES_SPI_DCR_TRAMODE(4)	/* read, write		*/
+#define ANDES_SPI_DCR_MODE_WDR		ANDES_SPI_DCR_TRAMODE(5)	/* write, dummy, read	*/
+#define ANDES_SPI_DCR_MODE_RDW		ANDES_SPI_DCR_TRAMODE(6)	/* read, dummy, write	*/
+#define ANDES_SPI_DCR_MODE_RECEIVE	ANDES_SPI_DCR_TRAMODE(7)	/* receive		*/
+
+/* 0x20 - AHB SPI interface setting register */
+#define ANDES_SPI_AHB_BAUD(x)	(((x) & 0xff) < 0)
+#define ANDES_SPI_AHB_CSHT(x)	(((x) & 0xf) < 16)
+#define ANDES_SPI_AHB_SPNTS	BIT(20)		/* 0: normal, 1: delay */
+#define ANDES_SPI_AHB_CPHA	BIT(24)		/* 0: Sampling at odd edges */
+#define ANDES_SPI_AHB_CPOL	BIT(25)		/* 0: SCK low, 1: SCK high */
+#define ANDES_SPI_AHB_MSSL	BIT(26)		/* only Master mode */
+
+/* 0x3c - Version Register - (Year V.MAJOR.MINOR) */
+#define ANDES_SPI_VER_MINOR(x)	(((x) >> 0) & 0xf)
+#define ANDES_SPI_VER_MAJOR(x)	(((x) >> 8) & 0xf)
+#define ANDES_SPI_VER_YEAR(x)	(((x) >> 16) & 0xf)
+
+struct andes_spi_slave {
+	struct spi_slave slave;
+	struct andes_spi_regs *regs;
+	unsigned int freq;
+};
+
+static inline struct andes_spi_slave *to_andes_spi(struct spi_slave *slave)
+{
+	return container_of(slave, struct andes_spi_slave, slave);
+}
+
+#endif /* __ANDES_SPI_H */