mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-10-31 08:21:36 +01:00 
			
		
		
		
	This patch returns back support for old ep93xx processors family Signed-off-by: Sergey Kostanbaev <sergey.kostanbaev@gmail.com> Cc: albert.u.boot@aribaud.net
		
			
				
	
	
		
			275 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * SPI Driver for EP93xx
 | |
|  *
 | |
|  * Copyright (C) 2013 Sergey Kostanabev <sergey.kostanbaev <at> fairwaves.ru>
 | |
|  *
 | |
|  * Inspired form linux kernel driver and atmel uboot driver
 | |
|  *
 | |
|  * SPDX-License-Identifier:	GPL-2.0+
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <spi.h>
 | |
| #include <malloc.h>
 | |
| 
 | |
| #include <asm/io.h>
 | |
| 
 | |
| #include <asm/arch/ep93xx.h>
 | |
| 
 | |
| 
 | |
| #define BIT(x)			(1<<(x))
 | |
| #define SSPBASE			SPI_BASE
 | |
| 
 | |
| #define SSPCR0			0x0000
 | |
| #define SSPCR0_MODE_SHIFT	6
 | |
| #define SSPCR0_SCR_SHIFT	8
 | |
| #define SSPCR0_SPH		BIT(7)
 | |
| #define SSPCR0_SPO		BIT(6)
 | |
| #define SSPCR0_FRF_SPI		0
 | |
| #define SSPCR0_DSS_8BIT		7
 | |
| 
 | |
| #define SSPCR1			0x0004
 | |
| #define SSPCR1_RIE		BIT(0)
 | |
| #define SSPCR1_TIE		BIT(1)
 | |
| #define SSPCR1_RORIE		BIT(2)
 | |
| #define SSPCR1_LBM		BIT(3)
 | |
| #define SSPCR1_SSE		BIT(4)
 | |
| #define SSPCR1_MS		BIT(5)
 | |
| #define SSPCR1_SOD		BIT(6)
 | |
| 
 | |
| #define SSPDR			0x0008
 | |
| 
 | |
| #define SSPSR			0x000c
 | |
| #define SSPSR_TFE		BIT(0)
 | |
| #define SSPSR_TNF		BIT(1)
 | |
| #define SSPSR_RNE		BIT(2)
 | |
| #define SSPSR_RFF		BIT(3)
 | |
| #define SSPSR_BSY		BIT(4)
 | |
| #define SSPCPSR			0x0010
 | |
| 
 | |
| #define SSPIIR			0x0014
 | |
| #define SSPIIR_RIS		BIT(0)
 | |
| #define SSPIIR_TIS		BIT(1)
 | |
| #define SSPIIR_RORIS		BIT(2)
 | |
| #define SSPICR			SSPIIR
 | |
| 
 | |
| #define SSPCLOCK		14745600
 | |
| #define SSP_MAX_RATE		(SSPCLOCK / 2)
 | |
| #define SSP_MIN_RATE		(SSPCLOCK / (254 * 256))
 | |
| 
 | |
| /* timeout in milliseconds */
 | |
| #define SPI_TIMEOUT		5
 | |
| /* maximum depth of RX/TX FIFO */
 | |
| #define SPI_FIFO_SIZE		8
 | |
| 
 | |
| struct ep93xx_spi_slave {
 | |
| 	struct spi_slave slave;
 | |
| 
 | |
| 	unsigned sspcr0;
 | |
| 	unsigned sspcpsr;
 | |
| };
 | |
| 
 | |
| static inline struct ep93xx_spi_slave *to_ep93xx_spi(struct spi_slave *slave)
 | |
| {
 | |
| 	return container_of(slave, struct ep93xx_spi_slave, slave);
 | |
| }
 | |
| 
 | |
| void spi_init()
 | |
| {
 | |
| }
 | |
| 
 | |
| static inline void ep93xx_spi_write_u8(u16 reg, u8 value)
 | |
| {
 | |
| 	writel(value, (unsigned int *)(SSPBASE + reg));
 | |
| }
 | |
| 
 | |
| static inline u8 ep93xx_spi_read_u8(u16 reg)
 | |
| {
 | |
| 	return readl((unsigned int *)(SSPBASE + reg));
 | |
| }
 | |
| 
 | |
| static inline void ep93xx_spi_write_u16(u16 reg, u16 value)
 | |
| {
 | |
| 	writel(value, (unsigned int *)(SSPBASE + reg));
 | |
| }
 | |
| 
 | |
| static inline u16 ep93xx_spi_read_u16(u16 reg)
 | |
| {
 | |
| 	return (u16)readl((unsigned int *)(SSPBASE + reg));
 | |
| }
 | |
| 
 | |
| static int ep93xx_spi_init_hw(unsigned int rate, unsigned int mode,
 | |
| 				struct ep93xx_spi_slave *slave)
 | |
| {
 | |
| 	unsigned cpsr, scr;
 | |
| 
 | |
| 	if (rate > SSP_MAX_RATE)
 | |
| 		rate = SSP_MAX_RATE;
 | |
| 
 | |
| 	if (rate < SSP_MIN_RATE)
 | |
| 		return -1;
 | |
| 
 | |
| 	/* Calculate divisors so that we can get speed according the
 | |
| 	 * following formula:
 | |
| 	 *	rate = spi_clock_rate / (cpsr * (1 + scr))
 | |
| 	 *
 | |
| 	 * cpsr must be even number and starts from 2, scr can be any number
 | |
| 	 * between 0 and 255.
 | |
| 	 */
 | |
| 	for (cpsr = 2; cpsr <= 254; cpsr += 2) {
 | |
| 		for (scr = 0; scr <= 255; scr++) {
 | |
| 			if ((SSPCLOCK / (cpsr * (scr + 1))) <= rate) {
 | |
| 				/* Set CHPA and CPOL, SPI format and 8bit */
 | |
| 				unsigned sspcr0 = (scr << SSPCR0_SCR_SHIFT) |
 | |
| 					SSPCR0_FRF_SPI | SSPCR0_DSS_8BIT;
 | |
| 				if (mode & SPI_CPHA)
 | |
| 					sspcr0 |= SSPCR0_SPH;
 | |
| 				if (mode & SPI_CPOL)
 | |
| 					sspcr0 |= SSPCR0_SPO;
 | |
| 
 | |
| 				slave->sspcr0 = sspcr0;
 | |
| 				slave->sspcpsr = cpsr;
 | |
| 				return 0;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| void spi_set_speed(struct spi_slave *slave, unsigned int hz)
 | |
| {
 | |
| 	struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
 | |
| 
 | |
| 	unsigned int mode = 0;
 | |
| 	if (as->sspcr0 & SSPCR0_SPH)
 | |
| 		mode |= SPI_CPHA;
 | |
| 	if (as->sspcr0 & SSPCR0_SPO)
 | |
| 		mode |= SPI_CPOL;
 | |
| 
 | |
| 	ep93xx_spi_init_hw(hz, mode, as);
 | |
| }
 | |
| 
 | |
| struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 | |
| 			unsigned int max_hz, unsigned int mode)
 | |
| {
 | |
| 	struct ep93xx_spi_slave	*as;
 | |
| 
 | |
| 	if (!spi_cs_is_valid(bus, cs))
 | |
| 		return NULL;
 | |
| 
 | |
| 	as = spi_alloc_slave(struct ep93xx_spi_slave, bus, cs);
 | |
| 	if (!as)
 | |
| 		return NULL;
 | |
| 
 | |
| 	if (ep93xx_spi_init_hw(max_hz, mode, as)) {
 | |
| 		free(as);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	return &as->slave;
 | |
| }
 | |
| 
 | |
| void spi_free_slave(struct spi_slave *slave)
 | |
| {
 | |
| 	struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
 | |
| 
 | |
| 	free(as);
 | |
| }
 | |
| 
 | |
| int spi_claim_bus(struct spi_slave *slave)
 | |
| {
 | |
| 	struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
 | |
| 
 | |
| 	/* Enable the SPI hardware */
 | |
| 	ep93xx_spi_write_u8(SSPCR1, SSPCR1_SSE);
 | |
| 
 | |
| 
 | |
| 	ep93xx_spi_write_u8(SSPCPSR, as->sspcpsr);
 | |
| 	ep93xx_spi_write_u16(SSPCR0, as->sspcr0);
 | |
| 
 | |
| 	debug("Select CS:%d SSPCPSR=%02x SSPCR0=%04x\n",
 | |
| 	      slave->cs, as->sspcpsr, as->sspcr0);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void spi_release_bus(struct spi_slave *slave)
 | |
| {
 | |
| 	/* Disable the SPI hardware */
 | |
| 	ep93xx_spi_write_u8(SSPCR1, 0);
 | |
| }
 | |
| 
 | |
| int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 | |
| 		const void *dout, void *din, unsigned long flags)
 | |
| {
 | |
| 	unsigned int	len_tx;
 | |
| 	unsigned int	len_rx;
 | |
| 	unsigned int	len;
 | |
| 	u32		status;
 | |
| 	const u8	*txp = dout;
 | |
| 	u8		*rxp = din;
 | |
| 	u8		value;
 | |
| 
 | |
| 	debug("spi_xfer: slave %u:%u dout %p din %p bitlen %u\n",
 | |
| 	      slave->bus, slave->cs, (uint *)dout, (uint *)din, bitlen);
 | |
| 
 | |
| 
 | |
| 	if (bitlen == 0)
 | |
| 		/* Finish any previously submitted transfers */
 | |
| 		goto out;
 | |
| 
 | |
| 	if (bitlen % 8) {
 | |
| 		/* Errors always terminate an ongoing transfer */
 | |
| 		flags |= SPI_XFER_END;
 | |
| 		goto out;
 | |
| 	}
 | |
| 
 | |
| 	len = bitlen / 8;
 | |
| 
 | |
| 
 | |
| 	if (flags & SPI_XFER_BEGIN) {
 | |
| 		/* Empty RX FIFO */
 | |
| 		while ((ep93xx_spi_read_u8(SSPSR) & SSPSR_RNE))
 | |
| 			ep93xx_spi_read_u8(SSPDR);
 | |
| 
 | |
| 		spi_cs_activate(slave);
 | |
| 	}
 | |
| 
 | |
| 	for (len_tx = 0, len_rx = 0; len_rx < len; ) {
 | |
| 		status = ep93xx_spi_read_u8(SSPSR);
 | |
| 
 | |
| 		if ((len_tx < len) && (status & SSPSR_TNF)) {
 | |
| 			if (txp)
 | |
| 				value = *txp++;
 | |
| 			else
 | |
| 				value = 0xff;
 | |
| 
 | |
| 			ep93xx_spi_write_u8(SSPDR, value);
 | |
| 			len_tx++;
 | |
| 		}
 | |
| 
 | |
| 		if (status & SSPSR_RNE) {
 | |
| 			value = ep93xx_spi_read_u8(SSPDR);
 | |
| 
 | |
| 			if (rxp)
 | |
| 				*rxp++ = value;
 | |
| 			len_rx++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| out:
 | |
| 	if (flags & SPI_XFER_END) {
 | |
| 		/*
 | |
| 		 * Wait until the transfer is completely done before
 | |
| 		 * we deactivate CS.
 | |
| 		 */
 | |
| 		do {
 | |
| 			status = ep93xx_spi_read_u8(SSPSR);
 | |
| 		} while (status & SSPSR_BSY);
 | |
| 
 | |
| 		spi_cs_deactivate(slave);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 |