[U-Boot] [PATCH v4 01/16] spi: Add zynq qspi controller driver

Siva Durga Prasad Paladugu siva.durga.paladugu at xilinx.com
Thu Sep 3 09:26:15 CEST 2015


Hi,

> -----Original Message-----
> From: Jagan Teki [mailto:jteki at openedev.com]
> Sent: Tuesday, September 01, 2015 11:42 AM
> To: u-boot at lists.denx.de
> Cc: Jagan Teki; Michal Simek; Siva Durga Prasad Paladugu
> Subject: [PATCH v4 01/16] spi: Add zynq qspi controller driver
> 
> Added zynq qspi controller driver for Xilinx Zynq APSOC, this driver is driver-
> model driven with devicetree support.
> 
> => sf probe
> SF: Detected S25FL128S_64K with page size 256 Bytes, erase size 64 KiB, total
> 16 MiB => mw.b 0x100 0xCC 0x1000000 => sf update 0x100 0x0 0x1000000
> device 0 whole chip
> 16777216 bytes written, 0 bytes skipped in 59.842s, speed 289262 B/s => sf
> read 0x3000000 0x0 0x1000000 device 0 whole chip
> SF: 16777216 bytes @ 0x0 Read: OK
> => cmp.b 0x3000000 0x100 0x1000000
> Total of 16777216 byte(s) were the same
> 
> Signed-off-by: Jagan Teki <jteki at openedev.com>
> Reviewed-by: Simon Glass <sjg at chromium.org>
> Cc: Michal Simek <michal.simek at xilinx.com>
> Cc: Siva Durga Prasad Paladugu <sivadur at xilinx.com>
> Tested-by: Jagan Teki <jteki at openedev.com>
Acked-by: Siva Durga Prasad Paladugu <sivadur at xilinx.com>


Thanks,
Siva
> ---
>  drivers/spi/Makefile    |   1 +
>  drivers/spi/zynq_qspi.c | 622
> ++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 623 insertions(+)
>  create mode 100644 drivers/spi/zynq_qspi.c
> 
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index
> ee88aa1..f59312b 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -48,3 +48,4 @@ obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
>  obj-$(CONFIG_TI_QSPI) += ti_qspi.o
>  obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
>  obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
> +obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o
> diff --git a/drivers/spi/zynq_qspi.c b/drivers/spi/zynq_qspi.c new file mode
> 100644 index 0000000..0ce6127
> --- /dev/null
> +++ b/drivers/spi/zynq_qspi.c
> @@ -0,0 +1,622 @@
> +/*
> + * (C) Copyright 2013 Xilinx, Inc.
> + * (C) Copyright 2015 Jagan Teki <jteki at openedev.com>
> + *
> + * Xilinx Zynq Quad-SPI(QSPI) controller driver (master mode only)
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <malloc.h>
> +#include <spi.h>
> +#include <asm/io.h>
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +/* zynq qspi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */
> +#define ZYNQ_QSPI_CR_IFMODE_MASK	(1 << 31)	/* Flash
> intrface mode*/
> +#define ZYNQ_QSPI_CR_MSA_MASK		(1 << 15)	/* Manual
> start enb */
> +#define ZYNQ_QSPI_CR_MCS_MASK		(1 << 14)	/* Manual
> chip select */
> +#define ZYNQ_QSPI_CR_PCS_MASK		(1 << 10)	/* Peri chip
> select */
> +#define ZYNQ_QSPI_CR_FW_MASK		(0x3 << 6)	/* FIFO width
> */
> +#define ZYNQ_QSPI_CR_SS_MASK		(0xF << 10)	/* Slave
> Select */
> +#define ZYNQ_QSPI_CR_BAUD_MASK		(0x7 << 3)	/*
> Baud rate div */
> +#define ZYNQ_QSPI_CR_CPHA_MASK		(1 << 2)	/* Clock
> phase */
> +#define ZYNQ_QSPI_CR_CPOL_MASK		(1 << 1)	/* Clock
> polarity */
> +#define ZYNQ_QSPI_CR_MSTREN_MASK	(1 << 0)	/* Mode select */
> +#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK	(1 << 4)	/*
> RX_FIFO_not_empty */
> +#define ZYNQ_QSPI_IXR_TXOW_MASK		(1 << 2)	/*
> TX_FIFO_not_full */
> +#define ZYNQ_QSPI_IXR_ALL_MASK		0x7F		/* All IXR bits
> */
> +#define ZYNQ_QSPI_ENR_SPI_EN_MASK	(1 << 0)	/* SPI Enable */
> +
> +/* zynq qspi Transmit Data Register */
> +#define ZYNQ_QSPI_TXD_00_00_OFFSET	0x1C	/* Transmit 4-byte
> inst */
> +#define ZYNQ_QSPI_TXD_00_01_OFFSET	0x80	/* Transmit 1-byte
> inst */
> +#define ZYNQ_QSPI_TXD_00_10_OFFSET	0x84	/* Transmit 2-byte
> inst */
> +#define ZYNQ_QSPI_TXD_00_11_OFFSET	0x88	/* Transmit 3-byte
> inst */
> +
> +#define ZYNQ_QSPI_TXFIFO_THRESHOLD	1	/* Tx FIFO threshold
> level*/
> +#define ZYNQ_QSPI_RXFIFO_THRESHOLD	32	/* Rx FIFO threshold
> level */
> +
> +#define ZYNQ_QSPI_CR_BAUD_MAX		8	/* Baud rate divisor
> max val */
> +#define ZYNQ_QSPI_CR_BAUD_SHIFT		3	/* Baud rate
> divisor shift */
> +#define ZYNQ_QSPI_CR_SS_SHIFT		10	/* Slave select shift
> */
> +
> +#define ZYNQ_QSPI_FIFO_DEPTH		63
> +#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT
> +#define CONFIG_SYS_ZYNQ_QSPI_WAIT	CONFIG_SYS_HZ/100	/* 10
> ms */
> +#endif
> +
> +/* zynq qspi register set */
> +struct zynq_qspi_regs {
> +	u32 cr;		/* 0x00 */
> +	u32 isr;	/* 0x04 */
> +	u32 ier;	/* 0x08 */
> +	u32 idr;	/* 0x0C */
> +	u32 imr;	/* 0x10 */
> +	u32 enr;	/* 0x14 */
> +	u32 dr;		/* 0x18 */
> +	u32 txd0r;	/* 0x1C */
> +	u32 drxr;	/* 0x20 */
> +	u32 sicr;	/* 0x24 */
> +	u32 txftr;	/* 0x28 */
> +	u32 rxftr;	/* 0x2C */
> +	u32 gpior;	/* 0x30 */
> +	u32 reserved0[19];
> +	u32 txd1r;	/* 0x80 */
> +	u32 txd2r;	/* 0x84 */
> +	u32 txd3r;	/* 0x88 */
> +};
> +
> +/* zynq qspi platform data */
> +struct zynq_qspi_platdata {
> +	struct zynq_qspi_regs *regs;
> +	u32 frequency;          /* input frequency */
> +	u32 speed_hz;
> +};
> +
> +/* zynq qspi priv */
> +struct zynq_qspi_priv {
> +	struct zynq_qspi_regs *regs;
> +	u8 cs;
> +	u8 mode;
> +	u8 fifo_depth;
> +	u32 freq;		/* required frequency */
> +	const void *tx_buf;
> +	void *rx_buf;
> +	unsigned len;
> +	int bytes_to_transfer;
> +	int bytes_to_receive;
> +	unsigned int is_inst;
> +	unsigned cs_change:1;
> +};
> +
> +static int zynq_qspi_ofdata_to_platdata(struct udevice *bus) {
> +	struct zynq_qspi_platdata *plat = bus->platdata;
> +	const void *blob = gd->fdt_blob;
> +	int node = bus->of_offset;
> +
> +	plat->regs = (struct zynq_qspi_regs *)fdtdec_get_addr(blob,
> +							      node, "reg");
> +
> +	/* FIXME: Use 166MHz as a suitable default */
> +	plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
> +					166666666);
> +	plat->speed_hz = plat->frequency / 2;
> +
> +	debug("%s: regs=%p max-frequency=%d\n", __func__,
> +	      plat->regs, plat->frequency);
> +
> +	return 0;
> +}
> +
> +static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv) {
> +	struct zynq_qspi_regs *regs = priv->regs;
> +	u32 confr;
> +
> +	/* Disable QSPI */
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
> +
> +	/* Disable Interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
> +
> +	/* Clear the TX and RX threshold reg */
> +	writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, &regs->txftr);
> +	writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, &regs->rxftr);
> +
> +	/* Clear the RX FIFO */
> +	while (readl(&regs->isr) & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
> +		readl(&regs->drxr);
> +
> +	/* Clear Interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->isr);
> +
> +	/* Manual slave select and Auto start */
> +	confr = readl(&regs->cr);
> +	confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
> +	confr |= ZYNQ_QSPI_CR_IFMODE_MASK |
> ZYNQ_QSPI_CR_MCS_MASK |
> +		ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
> +		ZYNQ_QSPI_CR_MSTREN_MASK;
> +	writel(confr, &regs->cr);
> +
> +	/* Enable SPI */
> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr); }
> +
> +static int zynq_qspi_probe(struct udevice *bus) {
> +	struct zynq_qspi_platdata *plat = dev_get_platdata(bus);
> +	struct zynq_qspi_priv *priv = dev_get_priv(bus);
> +
> +	priv->regs = plat->regs;
> +	priv->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH;
> +
> +	/* init the zynq spi hw */
> +	zynq_qspi_init_hw(priv);
> +
> +	return 0;
> +}
> +
> +/*
> + * zynq_qspi_read_data - Copy data to RX buffer
> + * @zqspi:	Pointer to the zynq_qspi structure
> + * @data:	The 32 bit variable where data is stored
> + * @size:	Number of bytes to be copied from data to RX buffer
> + */
> +static void zynq_qspi_read_data(struct zynq_qspi_priv *priv, u32 data,
> +u8 size) {
> +	u8 byte3;
> +
> +	debug("%s: data 0x%04x rx_buf addr: 0x%08x size %d\n", __func__ ,
> +	      data, (unsigned)(priv->rx_buf), size);
> +
> +	if (priv->rx_buf) {
> +		switch (size) {
> +		case 1:
> +			*((u8 *)priv->rx_buf) = data;
> +			priv->rx_buf += 1;
> +			break;
> +		case 2:
> +			*((u16 *)priv->rx_buf) = data;
> +			priv->rx_buf += 2;
> +			break;
> +		case 3:
> +			*((u16 *)priv->rx_buf) = data;
> +			priv->rx_buf += 2;
> +			byte3 = (u8)(data >> 16);
> +			*((u8 *)priv->rx_buf) = byte3;
> +			priv->rx_buf += 1;
> +			break;
> +		case 4:
> +			/* Can not assume word aligned buffer */
> +			memcpy(priv->rx_buf, &data, size);
> +			priv->rx_buf += 4;
> +			break;
> +		default:
> +			/* This will never execute */
> +			break;
> +		}
> +	}
> +	priv->bytes_to_receive -= size;
> +	if (priv->bytes_to_receive < 0)
> +		priv->bytes_to_receive = 0;
> +}
> +
> +/*
> + * zynq_qspi_write_data - Copy data from TX buffer
> + * @zqspi:	Pointer to the zynq_qspi structure
> + * @data:	Pointer to the 32 bit variable where data is to be copied
> + * @size:	Number of bytes to be copied from TX buffer to data
> + */
> +static void zynq_qspi_write_data(struct  zynq_qspi_priv *priv,
> +		u32 *data, u8 size)
> +{
> +	if (priv->tx_buf) {
> +		switch (size) {
> +		case 1:
> +			*data = *((u8 *)priv->tx_buf);
> +			priv->tx_buf += 1;
> +			*data |= 0xFFFFFF00;
> +			break;
> +		case 2:
> +			*data = *((u16 *)priv->tx_buf);
> +			priv->tx_buf += 2;
> +			*data |= 0xFFFF0000;
> +			break;
> +		case 3:
> +			*data = *((u16 *)priv->tx_buf);
> +			priv->tx_buf += 2;
> +			*data |= (*((u8 *)priv->tx_buf) << 16);
> +			priv->tx_buf += 1;
> +			*data |= 0xFF000000;
> +			break;
> +		case 4:
> +			/* Can not assume word aligned buffer */
> +			memcpy(data, priv->tx_buf, size);
> +			priv->tx_buf += 4;
> +			break;
> +		default:
> +			/* This will never execute */
> +			break;
> +		}
> +	} else {
> +		*data = 0;
> +	}
> +
> +	debug("%s: data 0x%08x tx_buf addr: 0x%08x size %d\n", __func__,
> +	      *data, (u32)priv->tx_buf, size);
> +
> +	priv->bytes_to_transfer -= size;
> +	if (priv->bytes_to_transfer < 0)
> +		priv->bytes_to_transfer = 0;
> +}
> +
> +static void zynq_qspi_chipselect(struct  zynq_qspi_priv *priv, int
> +is_on) {
> +	u32 confr;
> +	struct zynq_qspi_regs *regs = priv->regs;
> +
> +	confr = readl(&regs->cr);
> +
> +	if (is_on) {
> +		/* Select the slave */
> +		confr &= ~ZYNQ_QSPI_CR_SS_MASK;
> +		confr |= (~(1 << priv->cs) << ZYNQ_QSPI_CR_SS_SHIFT) &
> +					ZYNQ_QSPI_CR_SS_MASK;
> +	} else
> +		/* Deselect the slave */
> +		confr |= ZYNQ_QSPI_CR_SS_MASK;
> +
> +	writel(confr, &regs->cr);
> +}
> +
> +/*
> + * zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible
> + * @zqspi:	Pointer to the zynq_qspi structure
> + */
> +static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32
> +size) {
> +	u32 data = 0;
> +	u32 fifocount = 0;
> +	unsigned len, offset;
> +	struct zynq_qspi_regs *regs = priv->regs;
> +	static const unsigned offsets[4] = {
> +		ZYNQ_QSPI_TXD_00_00_OFFSET,
> ZYNQ_QSPI_TXD_00_01_OFFSET,
> +		ZYNQ_QSPI_TXD_00_10_OFFSET,
> ZYNQ_QSPI_TXD_00_11_OFFSET };
> +
> +	while ((fifocount < size) &&
> +			(priv->bytes_to_transfer > 0)) {
> +		if (priv->bytes_to_transfer >= 4) {
> +			if (priv->tx_buf) {
> +				memcpy(&data, priv->tx_buf, 4);
> +				priv->tx_buf += 4;
> +			} else {
> +				data = 0;
> +			}
> +			writel(data, &regs->txd0r);
> +			priv->bytes_to_transfer -= 4;
> +			fifocount++;
> +		} else {
> +			/* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */
> +			if (!(readl(&regs->isr)
> +					& ZYNQ_QSPI_IXR_TXOW_MASK)
> &&
> +					!priv->rx_buf)
> +				return;
> +			len = priv->bytes_to_transfer;
> +			zynq_qspi_write_data(priv, &data, len);
> +			offset = (priv->rx_buf) ? offsets[0] : offsets[len];
> +			writel(data, &regs->cr + (offset / 4));
> +		}
> +	}
> +}
> +
> +/*
> + * zynq_qspi_irq_poll - Interrupt service routine of the QSPI controller
> + * @zqspi:	Pointer to the zynq_qspi structure
> + *
> + * This function handles TX empty and Mode Fault interrupts only.
> + * On TX empty interrupt this function reads the received data from RX
> +FIFO and
> + * fills the TX FIFO if there is any data remaining to be transferred.
> + * On Mode Fault interrupt this function indicates that transfer is
> +completed,
> + * the SPI subsystem will identify the error as the remaining bytes to
> +be
> + * transferred is non-zero.
> + *
> + * returns:	0 for poll timeout
> + *		1 transfer operation complete
> + */
> +static int zynq_qspi_irq_poll(struct zynq_qspi_priv *priv) {
> +	struct zynq_qspi_regs *regs = priv->regs;
> +	u32 rxindex = 0;
> +	u32 rxcount;
> +	u32 status, timeout;
> +
> +	/* Poll until any of the interrupt status bits are set */
> +	timeout = get_timer(0);
> +	do {
> +		status = readl(&regs->isr);
> +	} while ((status == 0) &&
> +		(get_timer(timeout) < CONFIG_SYS_ZYNQ_QSPI_WAIT));
> +
> +	if (status == 0) {
> +		printf("zynq_qspi_irq_poll: Timeout!\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	writel(status, &regs->isr);
> +
> +	/* Disable all interrupts */
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
> +	if ((status & ZYNQ_QSPI_IXR_TXOW_MASK) ||
> +	    (status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)) {
> +		/*
> +		 * This bit is set when Tx FIFO has < THRESHOLD entries. We
> have
> +		 * the THRESHOLD value set to 1, so this bit indicates Tx FIFO
> +		 * is empty
> +		 */
> +		rxcount = priv->bytes_to_receive - priv->bytes_to_transfer;
> +		rxcount = (rxcount % 4) ? ((rxcount/4)+1) : (rxcount/4);
> +		while ((rxindex < rxcount) &&
> +				(rxindex < ZYNQ_QSPI_RXFIFO_THRESHOLD))
> {
> +			/* Read out the data from the RX FIFO */
> +			u32 data;
> +			data = readl(&regs->drxr);
> +
> +			if (priv->bytes_to_receive >= 4) {
> +				if (priv->rx_buf) {
> +					memcpy(priv->rx_buf, &data, 4);
> +					priv->rx_buf += 4;
> +				}
> +				priv->bytes_to_receive -= 4;
> +			} else {
> +				zynq_qspi_read_data(priv, data,
> +						    priv->bytes_to_receive);
> +			}
> +			rxindex++;
> +		}
> +
> +		if (priv->bytes_to_transfer) {
> +			/* There is more data to send */
> +			zynq_qspi_fill_tx_fifo(priv,
> +
> ZYNQ_QSPI_RXFIFO_THRESHOLD);
> +
> +			writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->ier);
> +		} else {
> +			/*
> +			 * If transfer and receive is completed then only send
> +			 * complete signal
> +			 */
> +			if (!priv->bytes_to_receive) {
> +				/* return operation complete */
> +				writel(ZYNQ_QSPI_IXR_ALL_MASK,
> +				       &regs->idr);
> +				return 1;
> +			}
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * zynq_qspi_start_transfer - Initiates the QSPI transfer
> + * @qspi:	Pointer to the spi_device structure
> + * @transfer:	Pointer to the spi_transfer structure which provide
> information
> + *		about next transfer parameters
> + *
> + * This function fills the TX FIFO, starts the QSPI transfer, and waits
> +for the
> + * transfer to be completed.
> + *
> + * returns:	Number of bytes transferred in the last transfer
> + */
> +static int zynq_qspi_start_transfer(struct zynq_qspi_priv *priv) {
> +	u32 data = 0;
> +	struct zynq_qspi_regs *regs = priv->regs;
> +
> +	debug("%s: qspi: 0x%08x transfer: 0x%08x len: %d\n", __func__,
> +	      (u32)priv, (u32)priv, priv->len);
> +
> +	priv->bytes_to_transfer = priv->len;
> +	priv->bytes_to_receive = priv->len;
> +
> +	if (priv->len < 4)
> +		zynq_qspi_fill_tx_fifo(priv, priv->len);
> +	else
> +		zynq_qspi_fill_tx_fifo(priv, priv->fifo_depth);
> +
> +	writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->ier);
> +	/* Start the transfer by enabling manual start bit */
> +
> +	/* wait for completion */
> +	do {
> +		data = zynq_qspi_irq_poll(priv);
> +	} while (data == 0);
> +
> +	return (priv->len) - (priv->bytes_to_transfer); }
> +
> +static int zynq_qspi_transfer(struct zynq_qspi_priv *priv) {
> +	unsigned cs_change = 1;
> +	int status = 0;
> +
> +	while (1) {
> +		/* Select the chip if required */
> +		if (cs_change)
> +			zynq_qspi_chipselect(priv, 1);
> +
> +		cs_change = priv->cs_change;
> +
> +		if (!priv->tx_buf && !priv->rx_buf && priv->len) {
> +			status = -1;
> +			break;
> +		}
> +
> +		/* Request the transfer */
> +		if (priv->len) {
> +			status = zynq_qspi_start_transfer(priv);
> +			priv->is_inst = 0;
> +		}
> +
> +		if (status != priv->len) {
> +			if (status > 0)
> +				status = -EMSGSIZE;
> +			debug("zynq_qspi_transfer:%d len:%d\n",
> +			      status, priv->len);
> +			break;
> +		}
> +		status = 0;
> +
> +		if (cs_change)
> +			/* Deselect the chip */
> +			zynq_qspi_chipselect(priv, 0);
> +
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_claim_bus(struct udevice *dev) {
> +	struct udevice *bus = dev->parent;
> +	struct zynq_qspi_priv *priv = dev_get_priv(bus);
> +	struct zynq_qspi_regs *regs = priv->regs;
> +
> +	writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_release_bus(struct udevice *dev) {
> +	struct udevice *bus = dev->parent;
> +	struct zynq_qspi_priv *priv = dev_get_priv(bus);
> +	struct zynq_qspi_regs *regs = priv->regs;
> +
> +	writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_xfer(struct udevice *dev, unsigned int bitlen,
> +		const void *dout, void *din, unsigned long flags) {
> +	struct udevice *bus = dev->parent;
> +	struct zynq_qspi_priv *priv = dev_get_priv(bus);
> +	struct dm_spi_slave_platdata *slave_plat =
> +dev_get_parent_platdata(dev);
> +
> +	priv->cs = slave_plat->cs;
> +	priv->tx_buf = dout;
> +	priv->rx_buf = din;
> +	priv->len = bitlen / 8;
> +
> +	debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
> +	      bus->seq, slave_plat->cs, bitlen, priv->len, flags);
> +
> +	/*
> +	 * Festering sore.
> +	 * Assume that the beginning of a transfer with bits to
> +	 * transmit must contain a device command.
> +	 */
> +	if (dout && flags & SPI_XFER_BEGIN)
> +		priv->is_inst = 1;
> +	else
> +		priv->is_inst = 0;
> +
> +	if (flags & SPI_XFER_END)
> +		priv->cs_change = 1;
> +	else
> +		priv->cs_change = 0;
> +
> +	zynq_qspi_transfer(priv);
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_set_speed(struct udevice *bus, uint speed) {
> +	struct zynq_qspi_platdata *plat = bus->platdata;
> +	struct zynq_qspi_priv *priv = dev_get_priv(bus);
> +	struct zynq_qspi_regs *regs = priv->regs;
> +	uint32_t confr;
> +	u8 baud_rate_val = 0;
> +
> +	if (speed > plat->frequency)
> +		speed = plat->frequency;
> +
> +	/* Set the clock frequency */
> +	confr = readl(&regs->cr);
> +	if (speed == 0) {
> +		/* Set baudrate x8, if the freq is 0 */
> +		baud_rate_val = 0x2;
> +	} else if (plat->speed_hz != speed) {
> +		while ((baud_rate_val < ZYNQ_QSPI_CR_BAUD_MAX) &&
> +		       ((plat->frequency /
> +		       (2 << baud_rate_val)) > speed))
> +			baud_rate_val++;
> +
> +		plat->speed_hz = speed / (2 << baud_rate_val);
> +	}
> +	confr &= ~ZYNQ_QSPI_CR_BAUD_MASK;
> +	confr |= (baud_rate_val << ZYNQ_QSPI_CR_BAUD_SHIFT);
> +
> +	writel(confr, &regs->cr);
> +	priv->freq = speed;
> +
> +	debug("zynq_spi_set_speed: regs=%p, mode=%d\n", priv->regs,
> +priv->freq);
> +
> +	return 0;
> +}
> +
> +static int zynq_qspi_set_mode(struct udevice *bus, uint mode) {
> +	struct zynq_qspi_priv *priv = dev_get_priv(bus);
> +	struct zynq_qspi_regs *regs = priv->regs;
> +	uint32_t confr;
> +
> +	/* Set the SPI Clock phase and polarities */
> +	confr = readl(&regs->cr);
> +	confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK |
> ZYNQ_QSPI_CR_CPOL_MASK);
> +
> +	if (priv->mode & SPI_CPHA)
> +		confr |= ZYNQ_QSPI_CR_CPHA_MASK;
> +	if (priv->mode & SPI_CPOL)
> +		confr |= ZYNQ_QSPI_CR_CPOL_MASK;
> +
> +	writel(confr, &regs->cr);
> +	priv->mode = mode;
> +
> +	debug("zynq_spi_set_mode: regs=%p, mode=%d\n", priv->regs,
> +priv->mode);
> +
> +	return 0;
> +}
> +
> +static const struct dm_spi_ops zynq_qspi_ops = {
> +	.claim_bus      = zynq_qspi_claim_bus,
> +	.release_bus    = zynq_qspi_release_bus,
> +	.xfer           = zynq_qspi_xfer,
> +	.set_speed      = zynq_qspi_set_speed,
> +	.set_mode       = zynq_qspi_set_mode,
> +};
> +
> +static const struct udevice_id zynq_qspi_ids[] = {
> +	{ .compatible = "xlnx,zynq-qspi-1.0" },
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(zynq_qspi) = {
> +	.name   = "zynq_qspi",
> +	.id     = UCLASS_SPI,
> +	.of_match = zynq_qspi_ids,
> +	.ops    = &zynq_qspi_ops,
> +	.ofdata_to_platdata = zynq_qspi_ofdata_to_platdata,
> +	.platdata_auto_alloc_size = sizeof(struct zynq_qspi_platdata),
> +	.priv_auto_alloc_size = sizeof(struct zynq_qspi_priv),
> +	.probe  = zynq_qspi_probe,
> +};
> --
> 1.9.1



More information about the U-Boot mailing list