All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michal Simek <monstr@monstr.eu>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 1/2] spi: microblaze: Adds driver for Xilinx SPI controller
Date: Tue, 24 Jul 2012 12:56:52 +0200	[thread overview]
Message-ID: <500E7F74.308@monstr.eu> (raw)
In-Reply-To: <1342218605-20280-1-git-send-email-linz@li-pro.net>

On 07/14/2012 12:30 AM, Stephan Linz wrote:
> This is an improved version of the driver patch original
> submitted by Graeme Smecher <graeme.smecher@mail.mcgill.ca>
>
> 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.patch

not interesting in description.


>
> Fix manual:
>      drivers/spi/Makefile

this too.

>
> Signed-off-by: Stephan Linz <linz@li-pro.net>
> ---
>   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
>
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index c967d87..3ae38e5 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -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)
> diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
> new file mode 100644
> index 0000000..4d83bd3
> --- /dev/null
> +++ b/drivers/spi/xilinx_spi.c
> @@ -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 <thomas@wytron.com.tw>
> + * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
> + * Copyright (c) 2012 Stephan Linz <linz@li-pro.net>
> + *
> + * 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;
> +
> +__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

> +	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

> +	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;
> +	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>

> +	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;
> +}
> diff --git a/drivers/spi/xilinx_spi.h b/drivers/spi/xilinx_spi.h
> new file mode 100644
> index 0000000..32610d2
> --- /dev/null
> +++ b/drivers/spi/xilinx_spi.h
> @@ -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 <thomas@wytron.com.tw>
> + * Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
> + * Copyright (c) 2012 Stephan Linz <linz@li-pro.net>
> + *
> + * 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;
> +	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


-- 
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

  parent reply	other threads:[~2012-07-24 10:56 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-07-13 22:30 [U-Boot] [PATCH 1/2] spi: microblaze: Adds driver for Xilinx SPI controller Stephan Linz
2012-07-13 22:30 ` [U-Boot] [PATCH 2/2] microblaze: Wire up SPI driver Stephan Linz
2012-07-24 10:56 ` Michal Simek [this message]
2012-07-28 19:19   ` [U-Boot] [PATCH 1/2] spi: microblaze: Adds driver for Xilinx SPI controller Stephan Linz
2012-07-28 22:25 Stephan Linz
2012-07-31  5:18 ` Michal Simek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=500E7F74.308@monstr.eu \
    --to=monstr@monstr.eu \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.