205 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * sys-spi-flash.c
 | 
						|
 *
 | 
						|
 * Copyright(c) 2007-2018 Jianjun Jiang <8192542@qq.com>
 | 
						|
 * Official site: http://xboot.org
 | 
						|
 * Mobile phone: +86-18665388956
 | 
						|
 * QQ: 8192542
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation; either version 2 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software Foundation,
 | 
						|
 * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <types.h>
 | 
						|
#include <string.h>
 | 
						|
#include <io.h>
 | 
						|
 | 
						|
enum {
 | 
						|
	SPI_GCR	= 0x04,
 | 
						|
	SPI_TCR	= 0x08,
 | 
						|
	SPI_IER	= 0x10,
 | 
						|
	SPI_ISR	= 0x14,
 | 
						|
	SPI_FCR	= 0x18,
 | 
						|
	SPI_FSR	= 0x1c,
 | 
						|
	SPI_WCR	= 0x20,
 | 
						|
	SPI_CCR	= 0x24,
 | 
						|
	SPI_MBC	= 0x30,
 | 
						|
	SPI_MTC	= 0x34,
 | 
						|
	SPI_BCC	= 0x38,
 | 
						|
	SPI_TXD	= 0x200,
 | 
						|
	SPI_RXD	= 0x300,
 | 
						|
};
 | 
						|
 | 
						|
void sys_spi_flash_init(void)
 | 
						|
{
 | 
						|
	virtual_addr_t addr;
 | 
						|
	uint32_t val;
 | 
						|
 | 
						|
	/* Config GPIOC0, GPIOC1, GPIOC2 and GPIOC3 */
 | 
						|
	addr = 0x01c20848 + 0x00;
 | 
						|
	val = read32(addr);
 | 
						|
	val &= ~(0xf << ((0 & 0x7) << 2));
 | 
						|
	val |= ((0x2 & 0x7) << ((0 & 0x7) << 2));
 | 
						|
	write32(addr, val);
 | 
						|
 | 
						|
	val = read32(addr);
 | 
						|
	val &= ~(0xf << ((1 & 0x7) << 2));
 | 
						|
	val |= ((0x2 & 0x7) << ((1 & 0x7) << 2));
 | 
						|
	write32(addr, val);
 | 
						|
 | 
						|
	val = read32(addr);
 | 
						|
	val &= ~(0xf << ((2 & 0x7) << 2));
 | 
						|
	val |= ((0x2 & 0x7) << ((2 & 0x7) << 2));
 | 
						|
	write32(addr, val);
 | 
						|
 | 
						|
	val = read32(addr);
 | 
						|
	val &= ~(0xf << ((3 & 0x7) << 2));
 | 
						|
	val |= ((0x2 & 0x7) << ((3 & 0x7) << 2));
 | 
						|
	write32(addr, val);
 | 
						|
 | 
						|
	/* Deassert spi0 reset */
 | 
						|
	addr = 0x01c202c0;
 | 
						|
	val = read32(addr);
 | 
						|
	val |= (1 << 20);
 | 
						|
	write32(addr, val);
 | 
						|
 | 
						|
	/* Open the spi0 bus gate */
 | 
						|
	addr = 0x01c20000 + 0x60;
 | 
						|
	val = read32(addr);
 | 
						|
	val |= (1 << 20);
 | 
						|
	write32(addr, val);
 | 
						|
 | 
						|
	/* Set spi clock rate control register, divided by 4 */
 | 
						|
	addr = 0x01c05000;
 | 
						|
	write32(addr + SPI_CCR, 0x00001001);
 | 
						|
 | 
						|
	/* Enable spi0 and do a soft reset */
 | 
						|
	addr = 0x01c05000;
 | 
						|
	val = read32(addr + SPI_GCR);
 | 
						|
	val |= (1 << 31) | (1 << 7) | (1 << 1) | (1 << 0);
 | 
						|
	write32(addr + SPI_GCR, val);
 | 
						|
	while(read32(addr + SPI_GCR) & (1 << 31));
 | 
						|
 | 
						|
	val = read32(addr + SPI_TCR);
 | 
						|
	val &= ~(0x3 << 0);
 | 
						|
	val |= (1 << 6) | (1 << 2);
 | 
						|
	write32(addr + SPI_TCR, val);
 | 
						|
 | 
						|
	val = read32(addr + SPI_FCR);
 | 
						|
	val |= (1 << 31) | (1 << 15);
 | 
						|
	write32(addr + SPI_FCR, val);
 | 
						|
}
 | 
						|
 | 
						|
void sys_spi_flash_exit(void)
 | 
						|
{
 | 
						|
	virtual_addr_t addr = 0x01c05000;
 | 
						|
	uint32_t val;
 | 
						|
 | 
						|
	/* Disable the spi0 controller */
 | 
						|
	val = read32(addr + SPI_GCR);
 | 
						|
	val &= ~((1 << 1) | (1 << 0));
 | 
						|
	write32(addr + SPI_GCR, val);
 | 
						|
}
 | 
						|
 | 
						|
static void sys_spi_select(void)
 | 
						|
{
 | 
						|
	virtual_addr_t addr = 0x01c05000;
 | 
						|
	uint32_t val;
 | 
						|
 | 
						|
	val = read32(addr + SPI_TCR);
 | 
						|
	val &= ~((0x3 << 4) | (0x1 << 7));
 | 
						|
	val |= ((0 & 0x3) << 4) | (0x0 << 7);
 | 
						|
	write32(addr + SPI_TCR, val);
 | 
						|
}
 | 
						|
 | 
						|
static void sys_spi_deselect(void)
 | 
						|
{
 | 
						|
	virtual_addr_t addr = 0x01c05000;
 | 
						|
	uint32_t val;
 | 
						|
 | 
						|
	val = read32(addr + SPI_TCR);
 | 
						|
	val &= ~((0x3 << 4) | (0x1 << 7));
 | 
						|
	val |= ((0 & 0x3) << 4) | (0x1 << 7);
 | 
						|
	write32(addr + SPI_TCR, val);
 | 
						|
}
 | 
						|
 | 
						|
static void sys_spi_write_txbuf(uint8_t * buf, int len)
 | 
						|
{
 | 
						|
	virtual_addr_t addr = 0x01c05000;
 | 
						|
	int i;
 | 
						|
 | 
						|
	if(!buf)
 | 
						|
		len = 0;
 | 
						|
 | 
						|
	write32(addr + SPI_MTC, len & 0xffffff);
 | 
						|
	write32(addr + SPI_BCC, len & 0xffffff);
 | 
						|
	for(i = 0; i < len; ++i)
 | 
						|
		write8(addr + SPI_TXD, *buf++);
 | 
						|
}
 | 
						|
 | 
						|
static int sys_spi_transfer(void * txbuf, void * rxbuf, int len)
 | 
						|
{
 | 
						|
	virtual_addr_t addr = 0x01c05000;
 | 
						|
	int count = len;
 | 
						|
	uint8_t * tx = txbuf;
 | 
						|
	uint8_t * rx = rxbuf;
 | 
						|
	uint8_t val;
 | 
						|
	unsigned int n, i;
 | 
						|
 | 
						|
	while(count > 0)
 | 
						|
	{
 | 
						|
		n = (count <= 64) ? count : 64;
 | 
						|
		write32(addr + SPI_MBC, n);
 | 
						|
		sys_spi_write_txbuf(tx, n);
 | 
						|
		write32(addr + SPI_TCR, read32(addr + SPI_TCR) | (1 << 31));
 | 
						|
 | 
						|
		while((read32(addr + SPI_FSR) & 0xff) < n);
 | 
						|
		for(i = 0; i < n; i++)
 | 
						|
		{
 | 
						|
			val = read8(addr + SPI_RXD);
 | 
						|
			if(rx)
 | 
						|
				*rx++ = val;
 | 
						|
		}
 | 
						|
 | 
						|
		if(tx)
 | 
						|
			tx += n;
 | 
						|
		count -= n;
 | 
						|
	}
 | 
						|
	return len;
 | 
						|
}
 | 
						|
 | 
						|
static int sys_spi_write_then_read(void * txbuf, int txlen, void * rxbuf, int rxlen)
 | 
						|
{
 | 
						|
	if(sys_spi_transfer(txbuf, NULL, txlen) != txlen)
 | 
						|
		return -1;
 | 
						|
	if(sys_spi_transfer(NULL, rxbuf, rxlen) != rxlen)
 | 
						|
		return -1;
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void sys_spi_flash_read(int addr, void * buf, int count)
 | 
						|
{
 | 
						|
	uint8_t tx[4];
 | 
						|
 | 
						|
	tx[0] = 0x03;
 | 
						|
	tx[1] = (uint8_t)(addr >> 16);
 | 
						|
	tx[2] = (uint8_t)(addr >> 8);
 | 
						|
	tx[3] = (uint8_t)(addr >> 0);
 | 
						|
	sys_spi_select();
 | 
						|
	sys_spi_write_then_read(tx, 4, buf, count);
 | 
						|
	sys_spi_deselect();
 | 
						|
}
 |