spi: microblaze: Adds driver for Xilinx SPI controller
Subsystems (2)
PATCH 1/2 spi: microblaze: Adds driver for Xilinx SPI controller
This is an improved version of the driver patch original submitted by Graeme Smecher <[email protected]>
The changes are:
- remove hard coded Xilinx BSP defines (XPAR_SPI_*) and use CONFIG_SYS_SPI_BASE from config.h instead
- add extensive register struct definitions
- remove offset calculation for register access and use the new register struct instead
- move default SPI controller configuration from spi_setup_slave() to spi_claim_bus()
- add spi_set_speed()
- insert SPI controller deactivation in spi_release_bus()
- protect while loops in spi_xfer() with counter / timeouts
- support SPI mode flags: LSB_FIRST, CPHA, CPOL, LOOP
Come from:
http://patchwork.ozlabs.org/patch/71797/
Applied with:
git apply -v --whitespace=fix --reject \
U-Boot-Adds-driver-for-Xilinx-xps_spi-SPI-controller.patchFix manual:
drivers/spi/Makefile
--- drivers/spi/Makefile | 1 + drivers/spi/xilinx_spi.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/xilinx_spi.h | 135 +++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/xilinx_spi.c create mode 100644 drivers/spi/xilinx_spi.h
drivers/spi/Makefile +1 -0
@@ -44,6 +44,7 @@ COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o COBJS-$(CONFIG_TEGRA2_SPI) += tegra2_spi.o +COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c)
drivers/spi/xilinx_spi.c new file +210 -0
210 lines not on this page · raw message
drivers/spi/xilinx_spi.h new file +135 -0
135 lines not on this page · raw message
Signature · 1 line
1.7.0.4
PATCH 2/2 microblaze: Wire up SPI driver
Depending on XILINX_SPI_FLASH_BASEADDR enable SPI flash and environment in SPI flash.
Expected values from xparameters.h are:
- XILINX_SPI_FLASH_BASEADDR
- XILINX_SPI_FLASH_MAX_FREQ
- XILINX_SPI_FLASH_CS
--- include/configs/microblaze-generic.h | 55 ++++++++++++++++++++++++++++++++-- 1 files changed, 52 insertions(+), 3 deletions(-)
include/configs/microblaze-generic.h +52 -3
@@ -36,14 +36,22 @@#define CONFIG_OF_EMBED 1#define CONFIG_DEFAULT_DEVICE_TREE microblaze -/* linear flash memory */ +/* linear and spi flash memory */ #ifdef XILINX_FLASH_START #define FLASH +#undef SPIFLASH#undef RAMENV/* hold environment in flash */ #else +#ifdef XILINX_SPI_FLASH_BASEADDR #undef FLASH +#define SPIFLASH +#undef RAMENV/* hold environment in flash */ +#else +#undef FLASH +#undef SPIFLASH#define RAMENV /* hold environment in RAM */ #endif +#endif /* uart */ #ifdef XILINX_UARTLITE_BASEADDR
@@ -218,20 +226,51 @@# define CONFIG_ENV_SIZE 0x1000# define CONFIG_ENV_ADDR (CONFIG_SYS_MONITOR_BASE - CONFIG_ENV_SIZE) -# else /* !RAMENV */ +# else /* FLASH && !RAMENV */ # define CONFIG_ENV_IS_IN_FLASH 1/* 128K(one sector) for env */ # define CONFIG_ENV_SECT_SIZE 0x20000# define CONFIG_ENV_ADDR \ (CONFIG_SYS_FLASH_BASE + (2 * CONFIG_ENV_SECT_SIZE))# define CONFIG_ENV_SIZE 0x20000 -# endif /* !RAMBOOT */ +# endif /* FLASH && !RAMBOOT */ #else /* !FLASH */ + +#ifdef SPIFLASH +# define CONFIG_SYS_NO_FLASH 1 +# define CONFIG_SYS_SPI_BASE XILINX_SPI_FLASH_BASEADDR +# define CONFIG_XILINX_SPI 1 +# define CONFIG_SPI 1 +# define CONFIG_SPI_FLASH 1 +# define CONFIG_SPI_FLASH_STMICRO 1 +# define CONFIG_SF_DEFAULT_MODE SPI_MODE_3 +# define CONFIG_SF_DEFAULT_SPEED XILINX_SPI_FLASH_MAX_FREQ +# define CONFIG_SF_DEFAULT_CS XILINX_SPI_FLASH_CS + +# ifdef RAMENV +# define CONFIG_ENV_IS_NOWHERE 1 +# define CONFIG_ENV_SIZE 0x1000 +# define CONFIG_ENV_ADDR (CONFIG_SYS_MONITOR_BASE - CONFIG_ENV_SIZE) + +# else /* SPIFLASH && !RAMENV */ +# define CONFIG_ENV_IS_IN_SPI_FLASH 1 +# define CONFIG_ENV_SPI_MODE SPI_MODE_3 +# define CONFIG_ENV_SPI_MAX_HZ CONFIG_SF_DEFAULT_SPEED +# define CONFIG_ENV_SPI_CS CONFIG_SF_DEFAULT_CS +/* 128K(two sectors) for env */ +# define CONFIG_ENV_SECT_SIZE 0x10000 +# define CONFIG_ENV_SIZE (2 * CONFIG_ENV_SECT_SIZE) +/* Warning: adjust the offset in respect of other flash content and size */ +# define CONFIG_ENV_OFFSET (128 * CONFIG_ENV_SECT_SIZE)/* at 8MB */ +# endif /* SPIFLASH && !RAMBOOT */ +#else /* !SPIFLASH */ +/* ENV in RAM */ # define CONFIG_SYS_NO_FLASH 1# define CONFIG_ENV_IS_NOWHERE 1# define CONFIG_ENV_SIZE 0x1000# define CONFIG_ENV_ADDR (CONFIG_SYS_MONITOR_BASE - CONFIG_ENV_SIZE) +#endif /* !SPIFLASH */ #endif /* !FLASH */ /* system ace */
@@ -306,6 +345,15 @@# define CONFIG_CMD_SAVEENV # define CONFIG_CMD_SAVES # endif + +#else +#if defined(SPIFLASH ) +# define CONFIG_CMD_SF + +# if !defined(RAMENV ) +# define CONFIG_CMD_SAVEENV +# define CONFIG_CMD_SAVES +# endif #else # undef CONFIG_CMD_IMLS# undef CONFIG_CMD_FLASH
@@ -313,6 +361,7 @@# undef CONFIG_CMD_UBI# undef CONFIG_CMD_UBIFS#endif +#endif #if defined(CONFIG_CMD_JFFS2 )# define CONFIG_MTD_PARTITIONS
Signature · 1 line
1.7.0.4
PATCH 1/2 spi: microblaze: Adds driver for Xilinx SPI controller
In reply to Stephan LinzQuoted 16 lines · Stephan Linz
This is an improved version of the driver patch original submitted by Graeme Smecher <[email protected]>
The changes are:
- remove hard coded Xilinx BSP defines (XPAR_SPI_*) and useCONFIG_SYS_SPI_BASEfromconfig.hinstead
- add extensive register struct definitions
- remove offset calculation for register access and use the new register struct instead
- move default SPI controller configuration fromspi_setup_slave()tospi_claim_bus()
- addspi_set_speed()
- insert SPI controller deactivation inspi_release_bus()
- protect while loops inspi_xfer()with counter / timeouts
- support SPI mode flags:LSB_FIRST, CPHA, CPOL, LOOP
Quoted 6 lines
Come from:
http://patchwork.ozlabs.org/patch/71797/Applied with:
git apply -v --whitespace=fix --reject \ U-Boot-Adds-driver-for-Xilinx-xps_spi-SPI-controller.patch
not interesting in description.
Quoted 3 lines
Fix manual:
drivers/spi/Makefile
this too.
Quoted 72 lines
Signed-off-by Stephan Linz <[email protected]>--- drivers/spi/Makefile | 1 + drivers/spi/xilinx_spi.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/xilinx_spi.h | 135 +++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+), 0 deletions(-)create mode 100644
drivers/spi/xilinx_spi.c
create mode 100644drivers/spi/xilinx_spi.hdrivers/spi/Makefile +1
-0@@ -44,6 +44,7 @@ COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o COBJS-$(CONFIG_TEGRA2_SPI) += tegra2_spi.o +COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c)drivers/spi/xilinx_spi.c new file +44
-0@@ -0,0 +1,210 @@ +/* +* Xilinx SPI driver +* +* supports 8 bit SPI transfers only, with or w/o FIFO +* +* based on bfin_spi.c, by way of altera_spi.c +* Copyright (c) 2005-2008 Analog Devices Inc. +* Copyright (c) 2010 Thomas Chou <[email protected]> +* Copyright (c) 2010 Graeme Smecher <[email protected]> +* Copyright (c) 2012 Stephan Linz <[email protected]> +* +* Licensed under the GPL-2 or later. +* +* [0]: http://www.xilinx.com/support/documentation +* +* [S]: [0]/ip_documentation/xps_spi.pdf +* [0]/ip_documentation/axi_spi_ds742.pdf +*/ +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <spi.h> + +#include "xilinx_spi.h" + +#ifndef CONFIG_SYS_XILINX_SPI_LIST +#define CONFIG_SYS_XILINX_SPI_LIST { CONFIG_SYS_SPI_BASE } +#endif + +#ifndef CONFIG_XILINX_SPI_IDLE_VAL +#define CONFIG_XILINX_SPI_IDLE_VAL 0xff +#endif + +#define XILSPI_SPICR_DFLT_ON (SPICR_MANUAL_SS | \ + SPICR_MASTER_MODE | \ + SPICR_SPE) + +#define XILSPI_SPICR_DFLT_OFF (SPICR_MASTER_INHIBIT | \ + SPICR_MANUAL_SS) + +#define XILSPI_MAX_XFER_BITS 8 + +static unsigned long xilinx_spi_base_list[] =CONFIG_SYS_XILINX_SPI_LIST ; +
drivers/spi/xilinx_spi.c line 54
Quoted 10 lines
drivers/spi/xilinx_spi.c new file +10
-0+__attribute__((weak)) +int spi_cs_is_valid (unsigned int bus,unsigned int cs) +{ +return bus <ARRAY_SIZE (xilinx_spi_base_list) && cs <32 ; +} + +__attribute__((weak)) +void spi_cs_activate (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave);
newline
drivers/spi/xilinx_spi.c line 61
Quoted 7 lines
drivers/spi/xilinx_spi.c new file +7
-0+writel (SPISSR_ACT (slave->cs), &xilspi->regs->spissr); +} + +__attribute__((weak)) +void spi_cs_deactivate (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave);
newline
Quoted 35 lines
drivers/spi/xilinx_spi.c new file +35
-0+writel (SPISSR_OFF , &xilspi->regs->spissr); +} + +void spi_init (void ) +{ +/* do nothing */ +} + +void spi_set_speed (struct spi_slave *slave,uint hz) +{ +/* xilinx spi core does not support programmable speed */ +} + +struct spi_slave *spi_setup_slave (unsigned int bus,unsigned int cs, +unsigned int max_hz,unsigned int mode) +{ +struct xilinx_spi_slave *xilspi; +struct xilinx_spi_reg *regs; + +if (!spi_cs_is_valid (bus, cs)) { +printf ("XILSPI error: %s: unsupported bus %d / cs %d\n" , + __func__, bus, cs); +return NULL ; + } + + xilspi =malloc (sizeof (*xilspi)); +if (!xilspi) { +printf ("XILSPI error: %s: malloc of SPI structure failed\n" , + __func__); +return NULL ; + } + xilspi->slave.bus = bus; + xilspi->slave.cs = cs; + xilspi->regs = (struct xilinx_spi_reg *)xilinx_spi_base_list[bus]; + xilspi->freq = max_hz;
drivers/spi/xilinx_spi.c line 106
Quoted 10 lines
drivers/spi/xilinx_spi.c new file +10
-0+ xilspi->mode = mode; +debug ("%s: bus:%i cs:%i base:%p mode:%x max_hz:%d\n" , __func__, + bus, cs, xilspi->regs, xilspi->mode, xilspi->freq); + +return &xilspi->slave; +} + +void spi_free_slave (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave);
<newline>
Quoted 236 lines
drivers/spi/xilinx_spi.c new file +104
-0+free (xilspi); +} + +int spi_claim_bus (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave); +u32 spicr; + +debug ("%s: bus:%i cs:%i\n" , __func__, slave->bus, slave->cs); +writel (SPISSR_OFF , &xilspi->regs->spissr); + + spicr =XILSPI_SPICR_DFLT_ON ; +if (xilspi->mode &SPI_LSB_FIRST ) + spicr |=SPICR_LSB_FIRST ; +if (xilspi->mode &SPI_CPHA ) + spicr |=SPICR_CPHA ; +if (xilspi->mode &SPI_CPOL ) + spicr |=SPICR_CPOL ; +if (xilspi->mode &SPI_LOOP ) + spicr |=SPICR_LOOP ; + +writel (spicr, &xilspi->regs->spicr); +return 0 ; +} + +void spi_release_bus (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave); + +debug ("%s: bus:%i cs:%i\n" , __func__, slave->bus, slave->cs); +writel (SPISSR_OFF , &xilspi->regs->spissr); +writel (XILSPI_SPICR_DFLT_OFF , &xilspi->regs->spicr); +} + +int spi_xfer (struct spi_slave *slave,unsigned int bitlen,const void *dout, +void *din,unsigned long flags) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave); +/* assume spi core configured to do 8 bit transfers */ +unsigned int bytes = bitlen /XILSPI_MAX_XFER_BITS ; +const unsigned char *txp = dout; +unsigned char *rxp = din; +unsigned rxecount =17 ;/* max. 16 elements in FIFO, leftover 1 */ + +debug ("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n" , __func__, + slave->bus, slave->cs, bitlen, bytes, flags); +if (bitlen ==0 ) + goto done; + +if (bitlen %XILSPI_MAX_XFER_BITS ) { +printf ("XILSPI warning: %s: Not a multiple of %d bits\n" , + __func__,XILSPI_MAX_XFER_BITS ); + flags |=SPI_XFER_END ; + goto done; + } + +/* empty read buffer */ +while (rxecount && !(readl (&xilspi->regs->spisr) &SPISR_RX_EMPTY )) { +readl (&xilspi->regs->spidrr); + rxecount--; + } + +if (!rxecount) { +printf ("XILSPI error: %s: Rx buffer not empty\n" , __func__); +return -1 ; + } + +if (flags &SPI_XFER_BEGIN ) +spi_cs_activate (slave); + +while (bytes--) { +unsigned timeout =/* at least 1usec or greater, leftover 1 */ + xilspi->freq >XILSPI_MAX_XFER_BITS *1000000 ?2 : + (XILSPI_MAX_XFER_BITS *1000000 / xilspi->freq) +1 ; + +/* get Tx element from data out buffer and count up */ +unsigned char d = txp ? *txp++ :CONFIG_XILINX_SPI_IDLE_VAL ; +debug ("%s: tx:%x " , __func__, d); + +/* write out and wait for processing (receive data) */ +writel (d &SPIDTR_8BIT_MASK , &xilspi->regs->spidtr); +while (timeout &&readl (&xilspi->regs->spisr) + &SPISR_RX_EMPTY ) { + timeout--; +udelay (1 ); + } + +if (!timeout) { +printf ("XILSPI error: %s: Xfer timeout\n" , __func__); +return -1 ; + } + +/* read Rx element and push into data in buffer */ + d =readl (&xilspi->regs->spidrr) &SPIDRR_8BIT_MASK ; +if (rxp) + *rxp++ = d; +debug ("rx:%x\n" , d); + } + done: +if (flags &SPI_XFER_END ) +spi_cs_deactivate (slave); + +return 0 ; +}drivers/spi/xilinx_spi.h new file +125
-0@@ -0,0 +1,135 @@ +/* +* Xilinx SPI driver +* +* XPS/AXI bus interface +* +* based on bfin_spi.c, by way of altera_spi.c +* Copyright (c) 2005-2008 Analog Devices Inc. +* Copyright (c) 2010 Thomas Chou <[email protected]> +* Copyright (c) 2010 Graeme Smecher <[email protected]> +* Copyright (c) 2012 Stephan Linz <[email protected]> +* +* Licensed under the GPL-2 or later. +* +* [0]: http://www.xilinx.com/support/documentation +* +* [S]: [0]/ip_documentation/xps_spi.pdf +* [0]/ip_documentation/axi_spi_ds742.pdf +*/ +#ifndef _XILINX_SPI_ +#define _XILINX_SPI_ + +#include <asm/types.h> +#include <asm/io.h> + +/* + * Xilinx SPI Register Definition + * + * [1]: [0]/ip_documentation/xps_spi.pdf + * page 8, Register Descriptions + * [2]: [0]/ip_documentation/axi_spi_ds742.pdf + * page 7, Register Overview Table + */ +struct xilinx_spi_reg { + u32 __space0__[7]; + u32 dgier; /* Device Global Interrupt Enable Register (DGIER) */ + u32 ipisr; /* IP Interrupt Status Register (IPISR) */ + u32 __space1__; + u32 ipier; /* IP Interrupt Enable Register (IPIER) */ + u32 __space2__[5]; + u32 srr; /* Softare Reset Register (SRR) */ + u32 __space3__[7]; + u32 spicr; /* SPI Control Register (SPICR) */ + u32 spisr; /* SPI Status Register (SPISR) */ + u32 spidtr; /* SPI Data Transmit Register (SPIDTR) */ + u32 spidrr; /* SPI Data Receive Register (SPIDRR) */ + u32 spissr; /* SPI Slave Select Register (SPISSR) */ + u32 spitfor; /* SPI Transmit FIFO Occupancy Register (SPITFOR) */ + u32 spirfor; /* SPI Receive FIFO Occupancy Register (SPIRFOR) */ +}; + +/* Device Global Interrupt Enable Register (dgier), [1] p15, [2] p15 */ +#define DGIER_GIE (1 << 31) + +/* IP Interrupt Status Register (ipisr), [1] p15, [2] p15 */ +#define IPISR_DRR_NOT_EMPTY (1 << 8) +#define IPISR_SLAVE_SELECT (1 << 7) +#define IPISR_TXF_HALF_EMPTY (1 << 6) +#define IPISR_DRR_OVERRUN (1 << 5) +#define IPISR_DRR_FULL (1 << 4) +#define IPISR_DTR_UNDERRUN (1 << 3) +#define IPISR_DTR_EMPTY (1 << 2) +#define IPISR_SLAVE_MODF (1 << 1) +#define IPISR_MODF (1 << 0) + +/* IP Interrupt Enable Register (ipier), [1] p17, [2] p18 */ +#define IPIER_DRR_NOT_EMPTY (1 << 8) +#define IPIER_SLAVE_SELECT (1 << 7) +#define IPIER_TXF_HALF_EMPTY (1 << 6) +#define IPIER_DRR_OVERRUN (1 << 5) +#define IPIER_DRR_FULL (1 << 4) +#define IPIER_DTR_UNDERRUN (1 << 3) +#define IPIER_DTR_EMPTY (1 << 2) +#define IPIER_SLAVE_MODF (1 << 1) +#define IPIER_MODF (1 << 0) + +/* Softare Reset Register (srr), [1] p9, [2] p8 */ +#define SRR_RESET_CODE 0x0000000A + +/* SPI Control Register (spicr), [1] p9, [2] p8 */ +#define SPICR_LSB_FIRST (1 << 9) +#define SPICR_MASTER_INHIBIT (1 << 8) +#define SPICR_MANUAL_SS (1 << 7) +#define SPICR_RXFIFO_RESEST (1 << 6) +#define SPICR_TXFIFO_RESEST (1 << 5) +#define SPICR_CPHA (1 << 4) +#define SPICR_CPOL (1 << 3) +#define SPICR_MASTER_MODE (1 << 2) +#define SPICR_SPE (1 << 1) +#define SPICR_LOOP (1 << 0) + +/* SPI Status Register (spisr), [1] p11, [2] p10 */ +#define SPISR_SLAVE_MODE_SELECT (1 << 5) +#define SPISR_MODF (1 << 4) +#define SPISR_TX_FULL (1 << 3) +#define SPISR_TX_EMPTY (1 << 2) +#define SPISR_RX_FULL (1 << 1) +#define SPISR_RX_EMPTY (1 << 0) + +/* SPI Data Transmit Register (spidtr), [1] p12, [2] p12 */ +#define SPIDTR_8BIT_MASK (0xff << 0) +#define SPIDTR_16BIT_MASK (0xffff << 0) +#define SPIDTR_32BIT_MASK (0xffffffff << 0) + +/* SPI Data Receive Register (spidrr), [1] p12, [2] p12 */ +#define SPIDRR_8BIT_MASK (0xff << 0) +#define SPIDRR_16BIT_MASK (0xffff << 0) +#define SPIDRR_32BIT_MASK (0xffffffff << 0) + +/* SPI Slave Select Register (spissr), [1] p13, [2] p13 */ +#define SPISSR_MASK(cs) (1 << (cs)) +#define SPISSR_ACT(cs) ~SPISSR_MASK(cs) +#define SPISSR_OFF ~0UL + +/* SPI Transmit FIFO Occupancy Register (spitfor), [1] p13, [2] p14 */ +#define SPITFOR_OCYVAL_POS 0 +#define SPITFOR_OCYVAL_MASK (0xf << SPITFOR_OCYVAL_POS) + +/* SPI Receive FIFO Occupancy Register (spirfor), [1] p14, [2] p14 */ +#define SPIRFOR_OCYVAL_POS 0 +#define SPIRFOR_OCYVAL_MASK (0xf << SPIRFOR_OCYVAL_POS) + +struct xilinx_spi_slave { + struct spi_slave slave; + struct xilinx_spi_reg *regs; + unsigned int freq;
drivers/spi/xilinx_spi.h line 135
Quoted 10 lines
drivers/spi/xilinx_spi.h new file +10
-0+unsigned int mode; +}; + +static inline struct xilinx_spi_slave *to_xilinx_spi_slave ( +struct spi_slave *slave) +{ + return container_of(slave, struct xilinx_spi_slave, slave); +} + +#endif /* _XILINX_SPI_ */
The rest looks good. I have also tested it on sp605 and haven't found any problem. We will also tested it on other boards.
Thanks,
Michal
Signature · 4 lines
Michal Simek, Ing. (M.Eng) w: www.monstr.eu p: +42-0-721842854 Maintainer of Linux kernel 2.6 Microblaze Linux - http://www.monstr.eu/fdt/ Microblaze U-BOOT custodian
PATCH 1/2 spi: microblaze: Adds driver for Xilinx SPI controller
Hi Michal,
I'll resubmit a new patch which fixes your notes.
br,
Stephan
Am Dienstag, den 24.07.2012, 12:56 +0200 schrieb Michal Simek:
Quoted 429 lines · Stephan Linz
Quoted 16 lines
This is an improved version of the driver patch original submitted by Graeme Smecher <[email protected]>
The changes are:
- remove hard coded Xilinx BSP defines (XPAR_SPI_*) and useCONFIG_SYS_SPI_BASEfromconfig.hinstead
- add extensive register struct definitions
- remove offset calculation for register access and use the new register struct instead
- move default SPI controller configuration fromspi_setup_slave()tospi_claim_bus()
- addspi_set_speed()
- insert SPI controller deactivation inspi_release_bus()
- protect while loops inspi_xfer()with counter / timeouts
- support SPI mode flags:LSB_FIRST, CPHA, CPOL, LOOPQuoted 6 lines
Come from:
http://patchwork.ozlabs.org/patch/71797/Applied with:
git apply -v --whitespace=fix --reject \ U-Boot-Adds-driver-for-Xilinx-xps_spi-SPI-controller.patchnot interesting in description.
Quoted 3 lines
Fix manual:
drivers/spi/Makefilethis too.
Quoted 72 lines
Signed-off-by Stephan Linz <[email protected]>--- drivers/spi/Makefile | 1 + drivers/spi/xilinx_spi.c | 210 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/xilinx_spi.h | 135 +++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+), 0 deletions(-)create mode 100644
drivers/spi/xilinx_spi.c
create mode 100644drivers/spi/xilinx_spi.hdrivers/spi/Makefile +1
-0@@ -44,6 +44,7 @@ COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o COBJS-$(CONFIG_TEGRA2_SPI) += tegra2_spi.o +COBJS-$(CONFIG_XILINX_SPI) += xilinx_spi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c)drivers/spi/xilinx_spi.c new file +44
-0@@ -0,0 +1,210 @@ +/* +* Xilinx SPI driver +* +* supports 8 bit SPI transfers only, with or w/o FIFO +* +* based on bfin_spi.c, by way of altera_spi.c +* Copyright (c) 2005-2008 Analog Devices Inc. +* Copyright (c) 2010 Thomas Chou <[email protected]> +* Copyright (c) 2010 Graeme Smecher <[email protected]> +* Copyright (c) 2012 Stephan Linz <[email protected]> +* +* Licensed under the GPL-2 or later. +* +* [0]: http://www.xilinx.com/support/documentation +* +* [S]: [0]/ip_documentation/xps_spi.pdf +* [0]/ip_documentation/axi_spi_ds742.pdf +*/ +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <spi.h> + +#include "xilinx_spi.h" + +#ifndef CONFIG_SYS_XILINX_SPI_LIST +#define CONFIG_SYS_XILINX_SPI_LIST { CONFIG_SYS_SPI_BASE } +#endif + +#ifndef CONFIG_XILINX_SPI_IDLE_VAL +#define CONFIG_XILINX_SPI_IDLE_VAL 0xff +#endif + +#define XILSPI_SPICR_DFLT_ON (SPICR_MANUAL_SS | \ + SPICR_MASTER_MODE | \ + SPICR_SPE) + +#define XILSPI_SPICR_DFLT_OFF (SPICR_MASTER_INHIBIT | \ + SPICR_MANUAL_SS) + +#define XILSPI_MAX_XFER_BITS 8 + +static unsigned long xilinx_spi_base_list[] =CONFIG_SYS_XILINX_SPI_LIST ; +drivers/spi/xilinx_spi.c line 54
Quoted 10 lines
drivers/spi/xilinx_spi.c new file +10
-0+__attribute__((weak)) +int spi_cs_is_valid (unsigned int bus,unsigned int cs) +{ +return bus <ARRAY_SIZE (xilinx_spi_base_list) && cs <32 ; +} + +__attribute__((weak)) +void spi_cs_activate (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave);newline
drivers/spi/xilinx_spi.c line 61
Quoted 7 lines
drivers/spi/xilinx_spi.c new file +7
-0+writel (SPISSR_ACT (slave->cs), &xilspi->regs->spissr); +} + +__attribute__((weak)) +void spi_cs_deactivate (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave);newline
Quoted 35 lines
drivers/spi/xilinx_spi.c new file +35
-0+writel (SPISSR_OFF , &xilspi->regs->spissr); +} + +void spi_init (void ) +{ +/* do nothing */ +} + +void spi_set_speed (struct spi_slave *slave,uint hz) +{ +/* xilinx spi core does not support programmable speed */ +} + +struct spi_slave *spi_setup_slave (unsigned int bus,unsigned int cs, +unsigned int max_hz,unsigned int mode) +{ +struct xilinx_spi_slave *xilspi; +struct xilinx_spi_reg *regs; + +if (!spi_cs_is_valid (bus, cs)) { +printf ("XILSPI error: %s: unsupported bus %d / cs %d\n" , + __func__, bus, cs); +return NULL ; + } + + xilspi =malloc (sizeof (*xilspi)); +if (!xilspi) { +printf ("XILSPI error: %s: malloc of SPI structure failed\n" , + __func__); +return NULL ; + } + xilspi->slave.bus = bus; + xilspi->slave.cs = cs; + xilspi->regs = (struct xilinx_spi_reg *)xilinx_spi_base_list[bus]; + xilspi->freq = max_hz;drivers/spi/xilinx_spi.c line 106
Quoted 10 lines
drivers/spi/xilinx_spi.c new file +10
-0+ xilspi->mode = mode; +debug ("%s: bus:%i cs:%i base:%p mode:%x max_hz:%d\n" , __func__, + bus, cs, xilspi->regs, xilspi->mode, xilspi->freq); + +return &xilspi->slave; +} + +void spi_free_slave (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave);<newline>
Quoted 236 lines
drivers/spi/xilinx_spi.c new file +104
-0+free (xilspi); +} + +int spi_claim_bus (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave); +u32 spicr; + +debug ("%s: bus:%i cs:%i\n" , __func__, slave->bus, slave->cs); +writel (SPISSR_OFF , &xilspi->regs->spissr); + + spicr =XILSPI_SPICR_DFLT_ON ; +if (xilspi->mode &SPI_LSB_FIRST ) + spicr |=SPICR_LSB_FIRST ; +if (xilspi->mode &SPI_CPHA ) + spicr |=SPICR_CPHA ; +if (xilspi->mode &SPI_CPOL ) + spicr |=SPICR_CPOL ; +if (xilspi->mode &SPI_LOOP ) + spicr |=SPICR_LOOP ; + +writel (spicr, &xilspi->regs->spicr); +return 0 ; +} + +void spi_release_bus (struct spi_slave *slave) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave); + +debug ("%s: bus:%i cs:%i\n" , __func__, slave->bus, slave->cs); +writel (SPISSR_OFF , &xilspi->regs->spissr); +writel (XILSPI_SPICR_DFLT_OFF , &xilspi->regs->spicr); +} + +int spi_xfer (struct spi_slave *slave,unsigned int bitlen,const void *dout, +void *din,unsigned long flags) +{ +struct xilinx_spi_slave *xilspi =to_xilinx_spi_slave (slave); +/* assume spi core configured to do 8 bit transfers */ +unsigned int bytes = bitlen /XILSPI_MAX_XFER_BITS ; +const unsigned char *txp = dout; +unsigned char *rxp = din; +unsigned rxecount =17 ;/* max. 16 elements in FIFO, leftover 1 */ + +debug ("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n" , __func__, + slave->bus, slave->cs, bitlen, bytes, flags); +if (bitlen ==0 ) + goto done; + +if (bitlen %XILSPI_MAX_XFER_BITS ) { +printf ("XILSPI warning: %s: Not a multiple of %d bits\n" , + __func__,XILSPI_MAX_XFER_BITS ); + flags |=SPI_XFER_END ; + goto done; + } + +/* empty read buffer */ +while (rxecount && !(readl (&xilspi->regs->spisr) &SPISR_RX_EMPTY )) { +readl (&xilspi->regs->spidrr); + rxecount--; + } + +if (!rxecount) { +printf ("XILSPI error: %s: Rx buffer not empty\n" , __func__); +return -1 ; + } + +if (flags &SPI_XFER_BEGIN ) +spi_cs_activate (slave); + +while (bytes--) { +unsigned timeout =/* at least 1usec or greater, leftover 1 */ + xilspi->freq >XILSPI_MAX_XFER_BITS *1000000 ?2 : + (XILSPI_MAX_XFER_BITS *1000000 / xilspi->freq) +1 ; + +/* get Tx element from data out buffer and count up */ +unsigned char d = txp ? *txp++ :CONFIG_XILINX_SPI_IDLE_VAL ; +debug ("%s: tx:%x " , __func__, d); + +/* write out and wait for processing (receive data) */ +writel (d &SPIDTR_8BIT_MASK , &xilspi->regs->spidtr); +while (timeout &&readl (&xilspi->regs->spisr) + &SPISR_RX_EMPTY ) { + timeout--; +udelay (1 ); + } + +if (!timeout) { +printf ("XILSPI error: %s: Xfer timeout\n" , __func__); +return -1 ; + } + +/* read Rx element and push into data in buffer */ + d =readl (&xilspi->regs->spidrr) &SPIDRR_8BIT_MASK ; +if (rxp) + *rxp++ = d; +debug ("rx:%x\n" , d); + } + done: +if (flags &SPI_XFER_END ) +spi_cs_deactivate (slave); + +return 0 ; +}drivers/spi/xilinx_spi.h new file +125
-0@@ -0,0 +1,135 @@ +/* +* Xilinx SPI driver +* +* XPS/AXI bus interface +* +* based on bfin_spi.c, by way of altera_spi.c +* Copyright (c) 2005-2008 Analog Devices Inc. +* Copyright (c) 2010 Thomas Chou <[email protected]> +* Copyright (c) 2010 Graeme Smecher <[email protected]> +* Copyright (c) 2012 Stephan Linz <[email protected]> +* +* Licensed under the GPL-2 or later. +* +* [0]: http://www.xilinx.com/support/documentation +* +* [S]: [0]/ip_documentation/xps_spi.pdf +* [0]/ip_documentation/axi_spi_ds742.pdf +*/ +#ifndef _XILINX_SPI_ +#define _XILINX_SPI_ + +#include <asm/types.h> +#include <asm/io.h> + +/* + * Xilinx SPI Register Definition + * + * [1]: [0]/ip_documentation/xps_spi.pdf + * page 8, Register Descriptions + * [2]: [0]/ip_documentation/axi_spi_ds742.pdf + * page 7, Register Overview Table + */ +struct xilinx_spi_reg { + u32 __space0__[7]; + u32 dgier; /* Device Global Interrupt Enable Register (DGIER) */ + u32 ipisr; /* IP Interrupt Status Register (IPISR) */ + u32 __space1__; + u32 ipier; /* IP Interrupt Enable Register (IPIER) */ + u32 __space2__[5]; + u32 srr; /* Softare Reset Register (SRR) */ + u32 __space3__[7]; + u32 spicr; /* SPI Control Register (SPICR) */ + u32 spisr; /* SPI Status Register (SPISR) */ + u32 spidtr; /* SPI Data Transmit Register (SPIDTR) */ + u32 spidrr; /* SPI Data Receive Register (SPIDRR) */ + u32 spissr; /* SPI Slave Select Register (SPISSR) */ + u32 spitfor; /* SPI Transmit FIFO Occupancy Register (SPITFOR) */ + u32 spirfor; /* SPI Receive FIFO Occupancy Register (SPIRFOR) */ +}; + +/* Device Global Interrupt Enable Register (dgier), [1] p15, [2] p15 */ +#define DGIER_GIE (1 << 31) + +/* IP Interrupt Status Register (ipisr), [1] p15, [2] p15 */ +#define IPISR_DRR_NOT_EMPTY (1 << 8) +#define IPISR_SLAVE_SELECT (1 << 7) +#define IPISR_TXF_HALF_EMPTY (1 << 6) +#define IPISR_DRR_OVERRUN (1 << 5) +#define IPISR_DRR_FULL (1 << 4) +#define IPISR_DTR_UNDERRUN (1 << 3) +#define IPISR_DTR_EMPTY (1 << 2) +#define IPISR_SLAVE_MODF (1 << 1) +#define IPISR_MODF (1 << 0) + +/* IP Interrupt Enable Register (ipier), [1] p17, [2] p18 */ +#define IPIER_DRR_NOT_EMPTY (1 << 8) +#define IPIER_SLAVE_SELECT (1 << 7) +#define IPIER_TXF_HALF_EMPTY (1 << 6) +#define IPIER_DRR_OVERRUN (1 << 5) +#define IPIER_DRR_FULL (1 << 4) +#define IPIER_DTR_UNDERRUN (1 << 3) +#define IPIER_DTR_EMPTY (1 << 2) +#define IPIER_SLAVE_MODF (1 << 1) +#define IPIER_MODF (1 << 0) + +/* Softare Reset Register (srr), [1] p9, [2] p8 */ +#define SRR_RESET_CODE 0x0000000A + +/* SPI Control Register (spicr), [1] p9, [2] p8 */ +#define SPICR_LSB_FIRST (1 << 9) +#define SPICR_MASTER_INHIBIT (1 << 8) +#define SPICR_MANUAL_SS (1 << 7) +#define SPICR_RXFIFO_RESEST (1 << 6) +#define SPICR_TXFIFO_RESEST (1 << 5) +#define SPICR_CPHA (1 << 4) +#define SPICR_CPOL (1 << 3) +#define SPICR_MASTER_MODE (1 << 2) +#define SPICR_SPE (1 << 1) +#define SPICR_LOOP (1 << 0) + +/* SPI Status Register (spisr), [1] p11, [2] p10 */ +#define SPISR_SLAVE_MODE_SELECT (1 << 5) +#define SPISR_MODF (1 << 4) +#define SPISR_TX_FULL (1 << 3) +#define SPISR_TX_EMPTY (1 << 2) +#define SPISR_RX_FULL (1 << 1) +#define SPISR_RX_EMPTY (1 << 0) + +/* SPI Data Transmit Register (spidtr), [1] p12, [2] p12 */ +#define SPIDTR_8BIT_MASK (0xff << 0) +#define SPIDTR_16BIT_MASK (0xffff << 0) +#define SPIDTR_32BIT_MASK (0xffffffff << 0) + +/* SPI Data Receive Register (spidrr), [1] p12, [2] p12 */ +#define SPIDRR_8BIT_MASK (0xff << 0) +#define SPIDRR_16BIT_MASK (0xffff << 0) +#define SPIDRR_32BIT_MASK (0xffffffff << 0) + +/* SPI Slave Select Register (spissr), [1] p13, [2] p13 */ +#define SPISSR_MASK(cs) (1 << (cs)) +#define SPISSR_ACT(cs) ~SPISSR_MASK(cs) +#define SPISSR_OFF ~0UL + +/* SPI Transmit FIFO Occupancy Register (spitfor), [1] p13, [2] p14 */ +#define SPITFOR_OCYVAL_POS 0 +#define SPITFOR_OCYVAL_MASK (0xf << SPITFOR_OCYVAL_POS) + +/* SPI Receive FIFO Occupancy Register (spirfor), [1] p14, [2] p14 */ +#define SPIRFOR_OCYVAL_POS 0 +#define SPIRFOR_OCYVAL_MASK (0xf << SPIRFOR_OCYVAL_POS) + +struct xilinx_spi_slave { + struct spi_slave slave; + struct xilinx_spi_reg *regs; + unsigned int freq;drivers/spi/xilinx_spi.h line 135
Quoted 10 lines
drivers/spi/xilinx_spi.h new file +10
-0+unsigned int mode; +}; + +static inline struct xilinx_spi_slave *to_xilinx_spi_slave ( +struct spi_slave *slave) +{ + return container_of(slave, struct xilinx_spi_slave, slave); +} + +#endif /* _XILINX_SPI_ */ The rest looks good. I have also tested it on sp605 and haven't found any problem. We will also tested it on other boards.
Thanks,
Michal