spi: omap3: Convert to driver model
After this conversion the driver will able to support both dm and non-dm and code is more extensible like we can remove the non-dm part simply without touching anycode if all the boards which are using this driver become dm driven. Cc: Tom Rini <trini@konsulko.com> Reviewed-by: Simon Glass <sjg@chromium.org> Acked-by: Christophe Ricard <christophe-h.ricard@st.com> Tested-by: Christophe Ricard <christophe-h.ricard@st.com> Signed-off-by: Jagan Teki <jteki@openedev.com> [Set priv->wordlen, Add Kconfig entry and file credit for dm conversion] Signed-off-by: Christophe Ricard <christophe-h.ricard@st.com>
This commit is contained in:
		
							parent
							
								
									03661d85f0
								
							
						
					
					
						commit
						77b8d04854
					
				|  | @ -155,6 +155,13 @@ config ZYNQ_QSPI | ||||||
| 	  Zynq QSPI IP core. This IP is used to connect the flash in | 	  Zynq QSPI IP core. This IP is used to connect the flash in | ||||||
| 	  4-bit qspi, 8-bit dual stacked and shared 4-bit dual parallel. | 	  4-bit qspi, 8-bit dual stacked and shared 4-bit dual parallel. | ||||||
| 
 | 
 | ||||||
|  | config OMAP3_SPI | ||||||
|  | 	bool "McSPI driver for OMAP" | ||||||
|  | 	help | ||||||
|  | 	  SPI master controller for OMAP24XX and later Multichannel SPI | ||||||
|  | 	  (McSPI). This driver be used to access SPI chips on platforms | ||||||
|  | 	  embedding this OMAP3 McSPI IP core. | ||||||
|  | 
 | ||||||
| endif # if DM_SPI | endif # if DM_SPI | ||||||
| 
 | 
 | ||||||
| config FSL_ESPI | config FSL_ESPI | ||||||
|  |  | ||||||
|  | @ -1,4 +1,7 @@ | ||||||
| /*
 | /*
 | ||||||
|  |  * Copyright (C) 2016 Jagan Teki <jteki@openedev.com> | ||||||
|  |  *		      Christophe Ricard <christophe.ricard@gmail.com> | ||||||
|  |  * | ||||||
|  * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> |  * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> | ||||||
|  * |  * | ||||||
|  * Driver for McSPI controller on OMAP3. Based on davinci_spi.c |  * Driver for McSPI controller on OMAP3. Based on davinci_spi.c | ||||||
|  | @ -15,10 +18,13 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <common.h> | #include <common.h> | ||||||
|  | #include <dm.h> | ||||||
| #include <spi.h> | #include <spi.h> | ||||||
| #include <malloc.h> | #include <malloc.h> | ||||||
| #include <asm/io.h> | #include <asm/io.h> | ||||||
| 
 | 
 | ||||||
|  | DECLARE_GLOBAL_DATA_PTR; | ||||||
|  | 
 | ||||||
| #if defined(CONFIG_AM33XX) || defined(CONFIG_AM43XX) | #if defined(CONFIG_AM33XX) || defined(CONFIG_AM43XX) | ||||||
| #define OMAP3_MCSPI1_BASE	0x48030100 | #define OMAP3_MCSPI1_BASE	0x48030100 | ||||||
| #define OMAP3_MCSPI2_BASE	0x481A0100 | #define OMAP3_MCSPI2_BASE	0x481A0100 | ||||||
|  | @ -65,6 +71,8 @@ | ||||||
| #define OMAP3_MCSPI_CHCTRL_DIS		(0 << 0) | #define OMAP3_MCSPI_CHCTRL_DIS		(0 << 0) | ||||||
| 
 | 
 | ||||||
| #define OMAP3_MCSPI_WAKEUPENABLE_WKEN	BIT(0) | #define OMAP3_MCSPI_WAKEUPENABLE_WKEN	BIT(0) | ||||||
|  | #define MCSPI_PINDIR_D0_IN_D1_OUT	0 | ||||||
|  | #define MCSPI_PINDIR_D0_OUT_D1_IN	1 | ||||||
| 
 | 
 | ||||||
| #define OMAP3_MCSPI_MAX_FREQ		48000000 | #define OMAP3_MCSPI_MAX_FREQ		48000000 | ||||||
| #define SPI_WAIT_TIMEOUT		10 | #define SPI_WAIT_TIMEOUT		10 | ||||||
|  | @ -94,54 +102,398 @@ struct mcspi { | ||||||
| 	/* channel3: 0x68 - 0x78, bus 0 */ | 	/* channel3: 0x68 - 0x78, bus 0 */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| struct omap3_spi_slave { | struct omap3_spi_priv { | ||||||
| 	struct spi_slave slave; |  | ||||||
| 	struct mcspi *regs; | 	struct mcspi *regs; | ||||||
|  | 	unsigned int cs; | ||||||
| 	unsigned int freq; | 	unsigned int freq; | ||||||
| 	unsigned int mode; | 	unsigned int mode; | ||||||
|  | 	unsigned int wordlen; | ||||||
|  | 	unsigned int pin_dir:1; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static void omap3_spi_write_chconf(struct omap3_spi_priv *priv, int val) | ||||||
|  | { | ||||||
|  | 	writel(val, &priv->regs->channel[priv->cs].chconf); | ||||||
|  | 	/* Flash post writes to make immediate effect */ | ||||||
|  | 	readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void omap3_spi_set_enable(struct omap3_spi_priv *priv, int enable) | ||||||
|  | { | ||||||
|  | 	writel(enable, &priv->regs->channel[priv->cs].chctrl); | ||||||
|  | 	/* Flash post writes to make immediate effect */ | ||||||
|  | 	readl(&priv->regs->channel[priv->cs].chctrl); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int omap3_spi_write(struct omap3_spi_priv *priv, unsigned int len, | ||||||
|  | 			   const void *txp, unsigned long flags) | ||||||
|  | { | ||||||
|  | 	ulong start; | ||||||
|  | 	int i, chconf; | ||||||
|  | 
 | ||||||
|  | 	chconf = readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | 
 | ||||||
|  | 	/* Enable the channel */ | ||||||
|  | 	omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); | ||||||
|  | 
 | ||||||
|  | 	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); | ||||||
|  | 	chconf |= (priv->wordlen - 1) << 7; | ||||||
|  | 	chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; | ||||||
|  | 	chconf |= OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 	omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < len; i++) { | ||||||
|  | 		/* wait till TX register is empty (TXS == 1) */ | ||||||
|  | 		start = get_timer(0); | ||||||
|  | 		while (!(readl(&priv->regs->channel[priv->cs].chstat) & | ||||||
|  | 			 OMAP3_MCSPI_CHSTAT_TXS)) { | ||||||
|  | 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { | ||||||
|  | 				printf("SPI TXS timed out, status=0x%08x\n", | ||||||
|  | 					readl(&priv->regs->channel[priv->cs].chstat)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		/* Write the data */ | ||||||
|  | 		unsigned int *tx = &priv->regs->channel[priv->cs].tx; | ||||||
|  | 		if (priv->wordlen > 16) | ||||||
|  | 			writel(((u32 *)txp)[i], tx); | ||||||
|  | 		else if (priv->wordlen > 8) | ||||||
|  | 			writel(((u16 *)txp)[i], tx); | ||||||
|  | 		else | ||||||
|  | 			writel(((u8 *)txp)[i], tx); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* wait to finish of transfer */ | ||||||
|  | 	while ((readl(&priv->regs->channel[priv->cs].chstat) & | ||||||
|  | 			(OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) != | ||||||
|  | 			(OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) | ||||||
|  | 		; | ||||||
|  | 
 | ||||||
|  | 	/* Disable the channel otherwise the next immediate RX will get affected */ | ||||||
|  | 	omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); | ||||||
|  | 
 | ||||||
|  | 	if (flags & SPI_XFER_END) { | ||||||
|  | 
 | ||||||
|  | 		chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 		omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int omap3_spi_read(struct omap3_spi_priv *priv, unsigned int len, | ||||||
|  | 			  void *rxp, unsigned long flags) | ||||||
|  | { | ||||||
|  | 	int i, chconf; | ||||||
|  | 	ulong start; | ||||||
|  | 
 | ||||||
|  | 	chconf = readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | 
 | ||||||
|  | 	/* Enable the channel */ | ||||||
|  | 	omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); | ||||||
|  | 
 | ||||||
|  | 	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); | ||||||
|  | 	chconf |= (priv->wordlen - 1) << 7; | ||||||
|  | 	chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; | ||||||
|  | 	chconf |= OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 	omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 
 | ||||||
|  | 	writel(0, &priv->regs->channel[priv->cs].tx); | ||||||
|  | 
 | ||||||
|  | 	for (i = 0; i < len; i++) { | ||||||
|  | 		start = get_timer(0); | ||||||
|  | 		/* Wait till RX register contains data (RXS == 1) */ | ||||||
|  | 		while (!(readl(&priv->regs->channel[priv->cs].chstat) & | ||||||
|  | 			 OMAP3_MCSPI_CHSTAT_RXS)) { | ||||||
|  | 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { | ||||||
|  | 				printf("SPI RXS timed out, status=0x%08x\n", | ||||||
|  | 					readl(&priv->regs->channel[priv->cs].chstat)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/* Disable the channel to prevent furher receiving */ | ||||||
|  | 		if (i == (len - 1)) | ||||||
|  | 			omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); | ||||||
|  | 
 | ||||||
|  | 		/* Read the data */ | ||||||
|  | 		unsigned int *rx = &priv->regs->channel[priv->cs].rx; | ||||||
|  | 		if (priv->wordlen > 16) | ||||||
|  | 			((u32 *)rxp)[i] = readl(rx); | ||||||
|  | 		else if (priv->wordlen > 8) | ||||||
|  | 			((u16 *)rxp)[i] = (u16)readl(rx); | ||||||
|  | 		else | ||||||
|  | 			((u8 *)rxp)[i] = (u8)readl(rx); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (flags & SPI_XFER_END) { | ||||||
|  | 		chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 		omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /*McSPI Transmit Receive Mode*/ | ||||||
|  | static int omap3_spi_txrx(struct omap3_spi_priv *priv, unsigned int len, | ||||||
|  | 			  const void *txp, void *rxp, unsigned long flags) | ||||||
|  | { | ||||||
|  | 	ulong start; | ||||||
|  | 	int chconf, i = 0; | ||||||
|  | 
 | ||||||
|  | 	chconf = readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | 
 | ||||||
|  | 	/*Enable SPI channel*/ | ||||||
|  | 	omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); | ||||||
|  | 
 | ||||||
|  | 	/*set TRANSMIT-RECEIVE Mode*/ | ||||||
|  | 	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); | ||||||
|  | 	chconf |= (priv->wordlen - 1) << 7; | ||||||
|  | 	chconf |= OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 	omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 
 | ||||||
|  | 	/*Shift in and out 1 byte at time*/ | ||||||
|  | 	for (i=0; i < len; i++){ | ||||||
|  | 		/* Write: wait for TX empty (TXS == 1)*/ | ||||||
|  | 		start = get_timer(0); | ||||||
|  | 		while (!(readl(&priv->regs->channel[priv->cs].chstat) & | ||||||
|  | 			 OMAP3_MCSPI_CHSTAT_TXS)) { | ||||||
|  | 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { | ||||||
|  | 				printf("SPI TXS timed out, status=0x%08x\n", | ||||||
|  | 					readl(&priv->regs->channel[priv->cs].chstat)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		/* Write the data */ | ||||||
|  | 		unsigned int *tx = &priv->regs->channel[priv->cs].tx; | ||||||
|  | 		if (priv->wordlen > 16) | ||||||
|  | 			writel(((u32 *)txp)[i], tx); | ||||||
|  | 		else if (priv->wordlen > 8) | ||||||
|  | 			writel(((u16 *)txp)[i], tx); | ||||||
|  | 		else | ||||||
|  | 			writel(((u8 *)txp)[i], tx); | ||||||
|  | 
 | ||||||
|  | 		/*Read: wait for RX containing data (RXS == 1)*/ | ||||||
|  | 		start = get_timer(0); | ||||||
|  | 		while (!(readl(&priv->regs->channel[priv->cs].chstat) & | ||||||
|  | 			 OMAP3_MCSPI_CHSTAT_RXS)) { | ||||||
|  | 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { | ||||||
|  | 				printf("SPI RXS timed out, status=0x%08x\n", | ||||||
|  | 					readl(&priv->regs->channel[priv->cs].chstat)); | ||||||
|  | 				return -1; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		/* Read the data */ | ||||||
|  | 		unsigned int *rx = &priv->regs->channel[priv->cs].rx; | ||||||
|  | 		if (priv->wordlen > 16) | ||||||
|  | 			((u32 *)rxp)[i] = readl(rx); | ||||||
|  | 		else if (priv->wordlen > 8) | ||||||
|  | 			((u16 *)rxp)[i] = (u16)readl(rx); | ||||||
|  | 		else | ||||||
|  | 			((u8 *)rxp)[i] = (u8)readl(rx); | ||||||
|  | 	} | ||||||
|  | 	/* Disable the channel */ | ||||||
|  | 	omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); | ||||||
|  | 
 | ||||||
|  | 	/*if transfer must be terminated disable the channel*/ | ||||||
|  | 	if (flags & SPI_XFER_END) { | ||||||
|  | 		chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 		omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int _spi_xfer(struct omap3_spi_priv *priv, unsigned int bitlen, | ||||||
|  | 		     const void *dout, void *din, unsigned long flags) | ||||||
|  | { | ||||||
|  | 	unsigned int	len; | ||||||
|  | 	int ret = -1; | ||||||
|  | 
 | ||||||
|  | 	if (priv->wordlen < 4 || priv->wordlen > 32) { | ||||||
|  | 		printf("omap3_spi: invalid wordlen %d\n", priv->wordlen); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (bitlen % priv->wordlen) | ||||||
|  | 		return -1; | ||||||
|  | 
 | ||||||
|  | 	len = bitlen / priv->wordlen; | ||||||
|  | 
 | ||||||
|  | 	if (bitlen == 0) {	 /* only change CS */ | ||||||
|  | 		int chconf = readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | 
 | ||||||
|  | 		if (flags & SPI_XFER_BEGIN) { | ||||||
|  | 			omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN); | ||||||
|  | 			chconf |= OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 			omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 		} | ||||||
|  | 		if (flags & SPI_XFER_END) { | ||||||
|  | 			chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; | ||||||
|  | 			omap3_spi_write_chconf(priv, chconf); | ||||||
|  | 			omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS); | ||||||
|  | 		} | ||||||
|  | 		ret = 0; | ||||||
|  | 	} else { | ||||||
|  | 		if (dout != NULL && din != NULL) | ||||||
|  | 			ret = omap3_spi_txrx(priv, len, dout, din, flags); | ||||||
|  | 		else if (dout != NULL) | ||||||
|  | 			ret = omap3_spi_write(priv, len, dout, flags); | ||||||
|  | 		else if (din != NULL) | ||||||
|  | 			ret = omap3_spi_read(priv, len, din, flags); | ||||||
|  | 	} | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void _omap3_spi_set_speed(struct omap3_spi_priv *priv) | ||||||
|  | { | ||||||
|  | 	uint32_t confr, div = 0; | ||||||
|  | 
 | ||||||
|  | 	confr = readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | 
 | ||||||
|  | 	/* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ | ||||||
|  | 	if (priv->freq) { | ||||||
|  | 		while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) | ||||||
|  | 					> priv->freq) | ||||||
|  | 			div++; | ||||||
|  | 	} else { | ||||||
|  | 		 div = 0xC; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* set clock divisor */ | ||||||
|  | 	confr &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; | ||||||
|  | 	confr |= div << 2; | ||||||
|  | 
 | ||||||
|  | 	omap3_spi_write_chconf(priv, confr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void _omap3_spi_set_mode(struct omap3_spi_priv *priv) | ||||||
|  | { | ||||||
|  | 	uint32_t confr; | ||||||
|  | 
 | ||||||
|  | 	confr = readl(&priv->regs->channel[priv->cs].chconf); | ||||||
|  | 
 | ||||||
|  | 	/* standard 4-wire master mode:  SCK, MOSI/out, MISO/in, nCS
 | ||||||
|  | 	 * REVISIT: this controller could support SPI_3WIRE mode. | ||||||
|  | 	 */ | ||||||
|  | 	if (priv->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) { | ||||||
|  | 		confr &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); | ||||||
|  | 		confr |= OMAP3_MCSPI_CHCONF_DPE0; | ||||||
|  | 	} else { | ||||||
|  | 		confr &= ~OMAP3_MCSPI_CHCONF_DPE0; | ||||||
|  | 		confr |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* set SPI mode 0..3 */ | ||||||
|  | 	confr &= ~(OMAP3_MCSPI_CHCONF_POL | OMAP3_MCSPI_CHCONF_PHA); | ||||||
|  | 	if (priv->mode & SPI_CPHA) | ||||||
|  | 		confr |= OMAP3_MCSPI_CHCONF_PHA; | ||||||
|  | 	if (priv->mode & SPI_CPOL) | ||||||
|  | 		confr |= OMAP3_MCSPI_CHCONF_POL; | ||||||
|  | 
 | ||||||
|  | 	/* set chipselect polarity; manage with FORCE */ | ||||||
|  | 	if (!(priv->mode & SPI_CS_HIGH)) | ||||||
|  | 		confr |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ | ||||||
|  | 	else | ||||||
|  | 		confr &= ~OMAP3_MCSPI_CHCONF_EPOL; | ||||||
|  | 
 | ||||||
|  | 	/* Transmit & receive mode */ | ||||||
|  | 	confr &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; | ||||||
|  | 
 | ||||||
|  | 	omap3_spi_write_chconf(priv, confr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void _omap3_spi_set_wordlen(struct omap3_spi_priv *priv) | ||||||
|  | { | ||||||
|  | 	unsigned int confr; | ||||||
|  | 
 | ||||||
|  | 	/* McSPI individual channel configuration */ | ||||||
|  | 	confr = readl(&priv->regs->channel[priv->wordlen].chconf); | ||||||
|  | 
 | ||||||
|  | 	/* wordlength */ | ||||||
|  | 	confr &= ~OMAP3_MCSPI_CHCONF_WL_MASK; | ||||||
|  | 	confr |= (priv->wordlen - 1) << 7; | ||||||
|  | 
 | ||||||
|  | 	omap3_spi_write_chconf(priv, confr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void spi_reset(struct mcspi *regs) | ||||||
|  | { | ||||||
|  | 	unsigned int tmp; | ||||||
|  | 
 | ||||||
|  | 	writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, ®s->sysconfig); | ||||||
|  | 	do { | ||||||
|  | 		tmp = readl(®s->sysstatus); | ||||||
|  | 	} while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); | ||||||
|  | 
 | ||||||
|  | 	writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | | ||||||
|  | 	       OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | | ||||||
|  | 	       OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, ®s->sysconfig); | ||||||
|  | 
 | ||||||
|  | 	writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, ®s->wakeupenable); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void _omap3_spi_claim_bus(struct omap3_spi_priv *priv) | ||||||
|  | { | ||||||
|  | 	unsigned int conf; | ||||||
|  | 
 | ||||||
|  | 	spi_reset(priv->regs); | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * setup when switching from (reset default) slave mode | ||||||
|  | 	 * to single-channel master mode | ||||||
|  | 	 */ | ||||||
|  | 	conf = readl(&priv->regs->modulctrl); | ||||||
|  | 	conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); | ||||||
|  | 	conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; | ||||||
|  | 
 | ||||||
|  | 	writel(conf, &priv->regs->modulctrl); | ||||||
|  | 
 | ||||||
|  | 	_omap3_spi_set_mode(priv); | ||||||
|  | 	_omap3_spi_set_speed(priv); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifndef CONFIG_DM_SPI | ||||||
|  | 
 | ||||||
|  | struct omap3_spi_slave { | ||||||
|  | 	struct spi_slave	 slave; | ||||||
|  | 	struct omap3_spi_priv   spi_priv; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct omap3_spi_priv *priv; | ||||||
|  | 
 | ||||||
| static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave) | static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave) | ||||||
| { | { | ||||||
| 	return container_of(slave, struct omap3_spi_slave, slave); | 	return container_of(slave, struct omap3_spi_slave, slave); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void spi_reset(struct omap3_spi_slave *ds) | void spi_init(void) | ||||||
| { |  | ||||||
| 	unsigned int tmp; |  | ||||||
| 
 |  | ||||||
| 	writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig); |  | ||||||
| 	do { |  | ||||||
| 		tmp = readl(&ds->regs->sysstatus); |  | ||||||
| 	} while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); |  | ||||||
| 
 |  | ||||||
| 	writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | |  | ||||||
| 				 OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | |  | ||||||
| 				 OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, |  | ||||||
| 				 &ds->regs->sysconfig); |  | ||||||
| 
 |  | ||||||
| 	writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void omap3_spi_write_chconf(struct omap3_spi_slave *ds, int val) |  | ||||||
| { |  | ||||||
| 	writel(val, &ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| 	/* Flash post writes to make immediate effect */ |  | ||||||
| 	readl(&ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void omap3_spi_set_enable(struct omap3_spi_slave *ds, int enable) |  | ||||||
| { |  | ||||||
| 	writel(enable, &ds->regs->channel[ds->slave.cs].chctrl); |  | ||||||
| 	/* Flash post writes to make immediate effect */ |  | ||||||
| 	readl(&ds->regs->channel[ds->slave.cs].chctrl); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void spi_init() |  | ||||||
| { | { | ||||||
| 	/* do nothing */ | 	/* do nothing */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void spi_free_slave(struct spi_slave *slave) | ||||||
|  | { | ||||||
|  | 	struct omap3_spi_slave *ds = to_omap3_spi(slave); | ||||||
|  | 
 | ||||||
|  | 	free(ds); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int spi_claim_bus(struct spi_slave *slave) | ||||||
|  | { | ||||||
|  | 	_omap3_spi_claim_bus(priv); | ||||||
|  | 	_omap3_spi_set_wordlen(priv); | ||||||
|  | 	_omap3_spi_set_mode(priv); | ||||||
|  | 	_omap3_spi_set_speed(priv); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void spi_release_bus(struct spi_slave *slave) | ||||||
|  | { | ||||||
|  | 	/* Reset the SPI hardware */ | ||||||
|  | 	spi_reset(priv->regs); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | ||||||
| 				     unsigned int max_hz, unsigned int mode) | 				     unsigned int max_hz, unsigned int mode) | ||||||
| { | { | ||||||
|  | @ -177,8 +529,7 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | ||||||
| 		 break; | 		 break; | ||||||
| #endif | #endif | ||||||
| 	default: | 	default: | ||||||
| 		printf("SPI error: unsupported bus %i. \
 | 		 printf("SPI error: unsupported bus %i.  Supported busses 0 - 3\n", bus); | ||||||
| 			Supported busses 0 - 3\n", bus); |  | ||||||
| 		 return NULL; | 		 return NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -186,14 +537,12 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | ||||||
| 	    ((bus == 1) && (cs > 1)) || | 	    ((bus == 1) && (cs > 1)) || | ||||||
| 	    ((bus == 2) && (cs > 1)) || | 	    ((bus == 2) && (cs > 1)) || | ||||||
| 	    ((bus == 3) && (cs > 0))) { | 	    ((bus == 3) && (cs > 0))) { | ||||||
| 		printf("SPI error: unsupported chip select %i \
 | 		printf("SPI error: unsupported chip select %i on bus %i\n", cs, bus); | ||||||
| 			on bus %i\n", cs, bus); |  | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (max_hz > OMAP3_MCSPI_MAX_FREQ) { | 	if (max_hz > OMAP3_MCSPI_MAX_FREQ) { | ||||||
| 		printf("SPI error: unsupported frequency %i Hz. \
 | 		printf("SPI error: unsupported frequency %i Hz. Max frequency is 48 Mhz\n", max_hz); | ||||||
| 			Max frequency is 48 Mhz\n", max_hz); |  | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -208,332 +557,122 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ds->regs = regs; | 	priv = &ds->spi_priv; | ||||||
| 	ds->freq = max_hz; | 
 | ||||||
| 	ds->mode = mode; | 	priv->regs = regs; | ||||||
|  | 	priv->cs = cs; | ||||||
|  | 	priv->freq = max_hz; | ||||||
|  | 	priv->mode = mode; | ||||||
|  | 	priv->wordlen = ds->slave.wordlen; | ||||||
|  | #ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED | ||||||
|  | 	priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN; | ||||||
|  | #endif | ||||||
| 
 | 
 | ||||||
| 	return &ds->slave; | 	return &ds->slave; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void spi_free_slave(struct spi_slave *slave) |  | ||||||
| { |  | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); |  | ||||||
| 
 |  | ||||||
| 	free(ds); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int spi_claim_bus(struct spi_slave *slave) |  | ||||||
| { |  | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); |  | ||||||
| 	unsigned int conf, div = 0; |  | ||||||
| 
 |  | ||||||
| 	/* McSPI global module configuration */ |  | ||||||
| 
 |  | ||||||
| 	/*
 |  | ||||||
| 	 * setup when switching from (reset default) slave mode |  | ||||||
| 	 * to single-channel master mode |  | ||||||
| 	 */ |  | ||||||
| 	spi_reset(ds); |  | ||||||
| 	conf = readl(&ds->regs->modulctrl); |  | ||||||
| 	conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); |  | ||||||
| 	conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; |  | ||||||
| 	writel(conf, &ds->regs->modulctrl); |  | ||||||
| 
 |  | ||||||
| 	/* McSPI individual channel configuration */ |  | ||||||
| 
 |  | ||||||
| 	/* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ |  | ||||||
| 	if (ds->freq) { |  | ||||||
| 		while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) |  | ||||||
| 					 > ds->freq) |  | ||||||
| 			div++; |  | ||||||
| 	} else |  | ||||||
| 		div = 0xC; |  | ||||||
| 
 |  | ||||||
| 	conf = readl(&ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| 
 |  | ||||||
| 	/* standard 4-wire master mode:	SCK, MOSI/out, MISO/in, nCS
 |  | ||||||
| 	 * REVISIT: this controller could support SPI_3WIRE mode. |  | ||||||
| 	 */ |  | ||||||
| #ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED |  | ||||||
| 	/*
 |  | ||||||
| 	 * Some boards have D0 wired as MOSI / D1 as MISO instead of |  | ||||||
| 	 * The normal D0 as MISO / D1 as MOSI. |  | ||||||
| 	 */ |  | ||||||
| 	conf &= ~OMAP3_MCSPI_CHCONF_DPE0; |  | ||||||
| 	conf |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1; |  | ||||||
| #else |  | ||||||
| 	conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); |  | ||||||
| 	conf |= OMAP3_MCSPI_CHCONF_DPE0; |  | ||||||
| #endif |  | ||||||
| 
 |  | ||||||
| 	/* wordlength */ |  | ||||||
| 	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; |  | ||||||
| 	conf |= (ds->slave.wordlen - 1) << 7; |  | ||||||
| 
 |  | ||||||
| 	/* set chipselect polarity; manage with FORCE */ |  | ||||||
| 	if (!(ds->mode & SPI_CS_HIGH)) |  | ||||||
| 		conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ |  | ||||||
| 	else |  | ||||||
| 		conf &= ~OMAP3_MCSPI_CHCONF_EPOL; |  | ||||||
| 
 |  | ||||||
| 	/* set clock divisor */ |  | ||||||
| 	conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; |  | ||||||
| 	conf |= div << 2; |  | ||||||
| 
 |  | ||||||
| 	/* set SPI mode 0..3 */ |  | ||||||
| 	if (ds->mode & SPI_CPOL) |  | ||||||
| 		conf |= OMAP3_MCSPI_CHCONF_POL; |  | ||||||
| 	else |  | ||||||
| 		conf &= ~OMAP3_MCSPI_CHCONF_POL; |  | ||||||
| 	if (ds->mode & SPI_CPHA) |  | ||||||
| 		conf |= OMAP3_MCSPI_CHCONF_PHA; |  | ||||||
| 	else |  | ||||||
| 		conf &= ~OMAP3_MCSPI_CHCONF_PHA; |  | ||||||
| 
 |  | ||||||
| 	/* Transmit & receive mode */ |  | ||||||
| 	conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; |  | ||||||
| 
 |  | ||||||
| 	omap3_spi_write_chconf(ds,conf); |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void spi_release_bus(struct spi_slave *slave) |  | ||||||
| { |  | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); |  | ||||||
| 
 |  | ||||||
| 	/* Reset the SPI hardware */ |  | ||||||
| 	spi_reset(ds); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int omap3_spi_write(struct spi_slave *slave, unsigned int len, |  | ||||||
| 			   const void *txp, unsigned long flags) |  | ||||||
| { |  | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); |  | ||||||
| 	int i; |  | ||||||
| 	ulong start; |  | ||||||
| 	int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| 
 |  | ||||||
| 	/* Enable the channel */ |  | ||||||
| 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); |  | ||||||
| 
 |  | ||||||
| 	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |  | ||||||
| 	chconf |= (ds->slave.wordlen - 1) << 7; |  | ||||||
| 	chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; |  | ||||||
| 	chconf |= OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 	omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < len; i++) { |  | ||||||
| 		/* wait till TX register is empty (TXS == 1) */ |  | ||||||
| 		start = get_timer(0); |  | ||||||
| 		while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |  | ||||||
| 			 OMAP3_MCSPI_CHSTAT_TXS)) { |  | ||||||
| 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { |  | ||||||
| 				printf("SPI TXS timed out, status=0x%08x\n", |  | ||||||
| 				       readl(&ds->regs->channel[ds->slave.cs].chstat)); |  | ||||||
| 				return -1; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		/* Write the data */ |  | ||||||
| 		unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx; |  | ||||||
| 		if (ds->slave.wordlen > 16) |  | ||||||
| 			writel(((u32 *)txp)[i], tx); |  | ||||||
| 		else if (ds->slave.wordlen > 8) |  | ||||||
| 			writel(((u16 *)txp)[i], tx); |  | ||||||
| 		else |  | ||||||
| 			writel(((u8 *)txp)[i], tx); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* wait to finish of transfer */ |  | ||||||
| 	while ((readl(&ds->regs->channel[ds->slave.cs].chstat) & |  | ||||||
| 			 (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) != |  | ||||||
| 			 (OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)); |  | ||||||
| 
 |  | ||||||
| 	/* Disable the channel otherwise the next immediate RX will get affected */ |  | ||||||
| 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); |  | ||||||
| 
 |  | ||||||
| 	if (flags & SPI_XFER_END) { |  | ||||||
| 
 |  | ||||||
| 		chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 		omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 	} |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int omap3_spi_read(struct spi_slave *slave, unsigned int len, |  | ||||||
| 			  void *rxp, unsigned long flags) |  | ||||||
| { |  | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); |  | ||||||
| 	int i; |  | ||||||
| 	ulong start; |  | ||||||
| 	int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| 
 |  | ||||||
| 	/* Enable the channel */ |  | ||||||
| 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); |  | ||||||
| 
 |  | ||||||
| 	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |  | ||||||
| 	chconf |= (ds->slave.wordlen - 1) << 7; |  | ||||||
| 	chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; |  | ||||||
| 	chconf |= OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 	omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 
 |  | ||||||
| 	writel(0, &ds->regs->channel[ds->slave.cs].tx); |  | ||||||
| 
 |  | ||||||
| 	for (i = 0; i < len; i++) { |  | ||||||
| 		start = get_timer(0); |  | ||||||
| 		/* Wait till RX register contains data (RXS == 1) */ |  | ||||||
| 		while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |  | ||||||
| 			 OMAP3_MCSPI_CHSTAT_RXS)) { |  | ||||||
| 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { |  | ||||||
| 				printf("SPI RXS timed out, status=0x%08x\n", |  | ||||||
| 				       readl(&ds->regs->channel[ds->slave.cs].chstat)); |  | ||||||
| 				return -1; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		/* Disable the channel to prevent furher receiving */ |  | ||||||
| 		if(i == (len - 1)) |  | ||||||
| 			omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); |  | ||||||
| 
 |  | ||||||
| 		/* Read the data */ |  | ||||||
| 		unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx; |  | ||||||
| 		if (ds->slave.wordlen > 16) |  | ||||||
| 			((u32 *)rxp)[i] = readl(rx); |  | ||||||
| 		else if (ds->slave.wordlen > 8) |  | ||||||
| 			((u16 *)rxp)[i] = (u16)readl(rx); |  | ||||||
| 		else |  | ||||||
| 			((u8 *)rxp)[i] = (u8)readl(rx); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (flags & SPI_XFER_END) { |  | ||||||
| 		chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 		omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /*McSPI Transmit Receive Mode*/ |  | ||||||
| static int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, |  | ||||||
| 			  const void *txp, void *rxp, unsigned long flags) |  | ||||||
| { |  | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); |  | ||||||
| 	ulong start; |  | ||||||
| 	int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| 	int i=0; |  | ||||||
| 
 |  | ||||||
| 	/*Enable SPI channel*/ |  | ||||||
| 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); |  | ||||||
| 
 |  | ||||||
| 	/*set TRANSMIT-RECEIVE Mode*/ |  | ||||||
| 	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK); |  | ||||||
| 	chconf |= (ds->slave.wordlen - 1) << 7; |  | ||||||
| 	chconf |= OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 	omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 
 |  | ||||||
| 	/*Shift in and out 1 byte at time*/ |  | ||||||
| 	for (i=0; i < len; i++){ |  | ||||||
| 		/* Write: wait for TX empty (TXS == 1)*/ |  | ||||||
| 		start = get_timer(0); |  | ||||||
| 		while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |  | ||||||
| 			 OMAP3_MCSPI_CHSTAT_TXS)) { |  | ||||||
| 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { |  | ||||||
| 				printf("SPI TXS timed out, status=0x%08x\n", |  | ||||||
| 				       readl(&ds->regs->channel[ds->slave.cs].chstat)); |  | ||||||
| 				return -1; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		/* Write the data */ |  | ||||||
| 		unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx; |  | ||||||
| 		if (ds->slave.wordlen > 16) |  | ||||||
| 			writel(((u32 *)txp)[i], tx); |  | ||||||
| 		else if (ds->slave.wordlen > 8) |  | ||||||
| 			writel(((u16 *)txp)[i], tx); |  | ||||||
| 		else |  | ||||||
| 			writel(((u8 *)txp)[i], tx); |  | ||||||
| 
 |  | ||||||
| 		/*Read: wait for RX containing data (RXS == 1)*/ |  | ||||||
| 		start = get_timer(0); |  | ||||||
| 		while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & |  | ||||||
| 			 OMAP3_MCSPI_CHSTAT_RXS)) { |  | ||||||
| 			if (get_timer(start) > SPI_WAIT_TIMEOUT) { |  | ||||||
| 				printf("SPI RXS timed out, status=0x%08x\n", |  | ||||||
| 				       readl(&ds->regs->channel[ds->slave.cs].chstat)); |  | ||||||
| 				return -1; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		/* Read the data */ |  | ||||||
| 		unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx; |  | ||||||
| 		if (ds->slave.wordlen > 16) |  | ||||||
| 			((u32 *)rxp)[i] = readl(rx); |  | ||||||
| 		else if (ds->slave.wordlen > 8) |  | ||||||
| 			((u16 *)rxp)[i] = (u16)readl(rx); |  | ||||||
| 		else |  | ||||||
| 			((u8 *)rxp)[i] = (u8)readl(rx); |  | ||||||
| 	} |  | ||||||
| 	/* Disable the channel */ |  | ||||||
| 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); |  | ||||||
| 
 |  | ||||||
| 	/*if transfer must be terminated disable the channel*/ |  | ||||||
| 	if (flags & SPI_XFER_END) { |  | ||||||
| 		chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 		omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | ||||||
| 	     const void *dout, void *din, unsigned long flags) | 	     const void *dout, void *din, unsigned long flags) | ||||||
|  | { return _spi_xfer(priv, bitlen, dout, din, flags); } | ||||||
|  | 
 | ||||||
|  | #else | ||||||
|  | 
 | ||||||
|  | static int omap3_spi_claim_bus(struct udevice *dev) | ||||||
| { | { | ||||||
| 	struct omap3_spi_slave *ds = to_omap3_spi(slave); | 	struct udevice *bus = dev->parent; | ||||||
| 	unsigned int	len; | 	struct omap3_spi_priv *priv = dev_get_priv(bus); | ||||||
| 	int ret = -1; | 	struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | ||||||
| 
 | 
 | ||||||
| 	if (ds->slave.wordlen < 4 || ds->slave.wordlen > 32) { | 	priv->cs = slave_plat->cs; | ||||||
| 		printf("omap3_spi: invalid wordlen %d\n", ds->slave.wordlen); | 	priv->mode = slave_plat->mode; | ||||||
| 		return -1; | 	priv->freq = slave_plat->max_hz; | ||||||
| 	} | 	_omap3_spi_claim_bus(priv); | ||||||
| 
 | 
 | ||||||
| 	if (bitlen % ds->slave.wordlen) | 	return 0; | ||||||
| 		return -1; |  | ||||||
| 
 |  | ||||||
| 	len = bitlen / ds->slave.wordlen; |  | ||||||
| 
 |  | ||||||
| 	if (bitlen == 0) {	 /* only change CS */ |  | ||||||
| 		int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); |  | ||||||
| 
 |  | ||||||
| 		if (flags & SPI_XFER_BEGIN) { |  | ||||||
| 			omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN); |  | ||||||
| 			chconf |= OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 			omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 		} |  | ||||||
| 		if (flags & SPI_XFER_END) { |  | ||||||
| 			chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; |  | ||||||
| 			omap3_spi_write_chconf(ds,chconf); |  | ||||||
| 			omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS); |  | ||||||
| 		} |  | ||||||
| 		ret = 0; |  | ||||||
| 	} else { |  | ||||||
| 		if (dout != NULL && din != NULL) |  | ||||||
| 			ret = omap3_spi_txrx(slave, len, dout, din, flags); |  | ||||||
| 		else if (dout != NULL) |  | ||||||
| 			ret = omap3_spi_write(slave, len, dout, flags); |  | ||||||
| 		else if (din != NULL) |  | ||||||
| 			ret = omap3_spi_read(slave, len, din, flags); |  | ||||||
| 	} |  | ||||||
| 	return ret; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| int spi_cs_is_valid(unsigned int bus, unsigned int cs) | static int omap3_spi_release_bus(struct udevice *dev) | ||||||
| { | { | ||||||
| 	return 1; | 	struct udevice *bus = dev->parent; | ||||||
|  | 	struct omap3_spi_priv *priv = dev_get_priv(bus); | ||||||
|  | 
 | ||||||
|  | 	/* Reset the SPI hardware */ | ||||||
|  | 	spi_reset(priv->regs); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void spi_cs_activate(struct spi_slave *slave) | static int omap3_spi_set_wordlen(struct udevice *dev, unsigned int wordlen) | ||||||
| { | { | ||||||
|  | 	struct udevice *bus = dev->parent; | ||||||
|  | 	struct omap3_spi_priv *priv = dev_get_priv(bus); | ||||||
|  | 	struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); | ||||||
|  | 
 | ||||||
|  | 	priv->cs = slave_plat->cs; | ||||||
|  | 	priv->wordlen = wordlen; | ||||||
|  | 	_omap3_spi_set_wordlen(priv); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void spi_cs_deactivate(struct spi_slave *slave) | static int omap3_spi_probe(struct udevice *dev) | ||||||
| { | { | ||||||
|  | 	struct omap3_spi_priv *priv = dev_get_priv(dev); | ||||||
|  | 	const void *blob = gd->fdt_blob; | ||||||
|  | 	int node = dev->of_offset; | ||||||
|  | 
 | ||||||
|  | 	priv->regs = (struct mcspi *)dev_get_addr(dev); | ||||||
|  | 	priv->pin_dir = fdtdec_get_uint(blob, node, "ti,pindir-d0-out-d1-in", | ||||||
|  | 					    MCSPI_PINDIR_D0_IN_D1_OUT); | ||||||
|  | 	priv->wordlen = SPI_DEFAULT_WORDLEN; | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static int omap3_spi_xfer(struct udevice *dev, unsigned int bitlen, | ||||||
|  | 			    const void *dout, void *din, unsigned long flags) | ||||||
|  | { | ||||||
|  | 	struct udevice *bus = dev->parent; | ||||||
|  | 	struct omap3_spi_priv *priv = dev_get_priv(bus); | ||||||
|  | 
 | ||||||
|  | 	return _spi_xfer(priv, bitlen, dout, din, flags); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int omap3_spi_set_speed(struct udevice *bus, unsigned int speed) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int omap3_spi_set_mode(struct udevice *bus, uint mode) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct dm_spi_ops omap3_spi_ops = { | ||||||
|  | 	.claim_bus      = omap3_spi_claim_bus, | ||||||
|  | 	.release_bus    = omap3_spi_release_bus, | ||||||
|  | 	.set_wordlen    = omap3_spi_set_wordlen, | ||||||
|  | 	.xfer	    = omap3_spi_xfer, | ||||||
|  | 	.set_speed      = omap3_spi_set_speed, | ||||||
|  | 	.set_mode	= omap3_spi_set_mode, | ||||||
|  | 	/*
 | ||||||
|  | 	 * cs_info is not needed, since we require all chip selects to be | ||||||
|  | 	 * in the device tree explicitly | ||||||
|  | 	 */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct udevice_id omap3_spi_ids[] = { | ||||||
|  | 	{ .compatible = "ti,omap2-mcspi" }, | ||||||
|  | 	{ .compatible = "ti,omap4-mcspi" }, | ||||||
|  | 	{ } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(omap3_spi) = { | ||||||
|  | 	.name   = "omap3_spi", | ||||||
|  | 	.id     = UCLASS_SPI, | ||||||
|  | 	.of_match = omap3_spi_ids, | ||||||
|  | 	.probe = omap3_spi_probe, | ||||||
|  | 	.ops    = &omap3_spi_ops, | ||||||
|  | 	.priv_auto_alloc_size = sizeof(struct omap3_spi_priv), | ||||||
|  | 	.probe = omap3_spi_probe, | ||||||
|  | }; | ||||||
|  | #endif | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue