nds32: spi: Support spi dm driver.

Support spi driver and can detect MX25U1635E flash on AE3XX board.

Verification:

sf probe 0:0 50000000 0
spi_flash_std_probe(sf_Probr.c)
	spi_flash_probe_slave(sf_Probr.c)
	SF: Detected mx25u1635e with page size 256 Bytes, erase size 4 KiB, total 2 MiB
	NDS32 # sf test 0x100000 0x1000
	SPI flash test:
	0 erase: 34 ticks, 117 KiB/s 0.936 Mbps
	1 check: 15 ticks, 266 KiB/s 2.128 Mbps
	2 write: 21 ticks, 190 KiB/s 1.520 Mbps
	3 read: 11 ticks, 363 KiB/s 2.904 Mbps
	Test passed
	0 erase: 34 ticks, 117 KiB/s 0.936 Mbps
	1 check: 15 ticks, 266 KiB/s 2.128 Mbps
	2 write: 21 ticks, 190 KiB/s 1.520 Mbps
	3 read: 11 ticks, 363 KiB/s 2.904 Mbps

Signed-off-by: rick <rick@andestech.com>
master
rick 7 years ago committed by Andes
parent 7b1a50b7b6
commit 7155cd2e6e
  1. 7
      drivers/spi/Kconfig
  2. 1
      drivers/spi/Makefile
  3. 499
      drivers/spi/nds_ae3xx_spi.c

@ -210,6 +210,13 @@ config FSL_QSPI
used to access the SPI NOR flash on platforms embedding this
Freescale IP core.
config NDS_AE3XX_SPI
bool "Andestech AE3XX SPI driver"
help
Enable the Andestech AE3XX SPI driver. This driver can be
used to access the SPI flash on platforms embedding this
Andestech IP core.
config TI_QSPI
bool "TI QSPI driver"
help

@ -35,6 +35,7 @@ obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
obj-$(CONFIG_MXC_SPI) += mxc_spi.o
obj-$(CONFIG_MXS_SPI) += mxs_spi.o
obj-$(CONFIG_NDS_AE3XX_SPI) += nds_ae3xx_spi.o
obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o

@ -0,0 +1,499 @@
/*
* NDS SPI controller driver.
*
* Copyright 2017 Andes Technology, Inc.
* Author: Rick Chen (rick@andestech.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <clk.h>
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
#include <dm.h>
DECLARE_GLOBAL_DATA_PTR;
#define MAX_TRANSFER_LEN 512
#define CHUNK_SIZE 1
#define SPI_TIMEOUT 0x100000
#define SPI0_BUS 0
#define SPI1_BUS 1
#define SPI0_BASE 0xf0b00000
#define SPI1_BASE 0xf0f00000
#define NSPI_MAX_CS_NUM 1
struct ae3xx_spi_regs {
u32 rev;
u32 reserve1[3];
u32 format; /* 0x10 */
#define DATA_LENGTH(x) ((x-1)<<8)
u32 pio;
u32 reserve2[2];
u32 tctrl; /* 0x20 */
#define TRAMODE_OFFSET 24
#define TRAMODE_MASK (0x0F<<TRAMODE_OFFSET)
#define TRAMODE_WR_SYNC (0<<TRAMODE_OFFSET)
#define TRAMODE_WO (1<<TRAMODE_OFFSET)
#define TRAMODE_RO (2<<TRAMODE_OFFSET)
#define TRAMODE_WR (3<<TRAMODE_OFFSET)
#define TRAMODE_RW (4<<TRAMODE_OFFSET)
#define TRAMODE_WDR (5<<TRAMODE_OFFSET)
#define TRAMODE_RDW (6<<TRAMODE_OFFSET)
#define TRAMODE_NONE (7<<TRAMODE_OFFSET)
#define TRAMODE_DW (8<<TRAMODE_OFFSET)
#define TRAMODE_DR (9<<TRAMODE_OFFSET)
#define WCNT_OFFSET 12
#define WCNT_MASK (0x1FF<<WCNT_OFFSET)
#define RCNT_OFFSET 0
#define RCNT_MASK (0x1FF<<RCNT_OFFSET)
u32 cmd;
u32 addr;
u32 data;
u32 ctrl; /* 0x30 */
#define TXFTH_OFFSET 16
#define RXFTH_OFFSET 8
#define TXDMAEN (1<<4)
#define RXDMAEN (1<<3)
#define TXFRST (1<<2)
#define RXFRST (1<<1)
#define SPIRST (1<<0)
u32 status;
#define TXFFL (1<<23)
#define TXEPTY (1<<22)
#define TXFVE_MASK (0x1F<<16)
#define RXFEM (1<<14)
#define RXFVE_OFFSET (8)
#define RXFVE_MASK (0x1F<<RXFVE_OFFSET)
#define SPIBSY (1<<0)
u32 inten;
u32 intsta;
u32 timing; /* 0x40 */
#define SCLK_DIV_MASK 0xFF
};
struct nds_spi_slave {
#ifndef CONFIG_DM_SPI
struct spi_slave slave;
#endif
volatile struct ae3xx_spi_regs *regs;
int to;
unsigned int freq;
ulong clock;
unsigned int mode;
u8 num_cs;
unsigned int mtiming;
size_t cmd_len;
u8 cmd_buf[16];
size_t data_len;
size_t tran_len;
u8 *din;
u8 *dout;
unsigned int max_transfer_length;
};
static int __ae3xx_spi_set_speed(struct nds_spi_slave *ns)
{
u32 tm;
u8 div;
tm = ns->regs->timing;
tm &= ~SCLK_DIV_MASK;
if(ns->freq >= ns->clock)
div =0xff;
else{
for (div = 0; div < 0xff; div++) {
if (ns->freq >= ns->clock / (2 * (div + 1)))
break;
}
}
tm |= div;
ns->regs->timing = tm;
return 0;
}
static int __ae3xx_spi_claim_bus(struct nds_spi_slave *ns)
{
unsigned int format=0;
ns->regs->ctrl |= (TXFRST|RXFRST|SPIRST);
while((ns->regs->ctrl &(TXFRST|RXFRST|SPIRST))&&(ns->to--))
if(!ns->to)
return -EINVAL;
ns->cmd_len = 0;
format = ns->mode|DATA_LENGTH(8);
ns->regs->format = format;
__ae3xx_spi_set_speed(ns);
return 0;
}
static int __ae3xx_spi_release_bus(struct nds_spi_slave *ns)
{
/* do nothing */
return 0;
}
static int __ae3xx_spi_start(struct nds_spi_slave *ns)
{
int i,olen=0;
int tc = ns->regs->tctrl;
tc &= ~(WCNT_MASK|RCNT_MASK|TRAMODE_MASK);
if ((ns->din)&&(ns->cmd_len))
tc |= TRAMODE_WR;
else if (ns->din)
tc |= TRAMODE_RO;
else
tc |= TRAMODE_WO;
if(ns->dout)
olen = ns->tran_len;
tc |= (ns->cmd_len+olen-1) << WCNT_OFFSET;
if(ns->din)
tc |= (ns->tran_len-1) << RCNT_OFFSET;
ns->regs->tctrl = tc;
ns->regs->cmd = 1;
for (i=0;i<ns->cmd_len;i++)
ns->regs->data = ns->cmd_buf[i];
return 0;
}
static int __ae3xx_spi_stop(struct nds_spi_slave *ns)
{
ns->regs->timing = ns->mtiming;
while ((ns->regs->status & SPIBSY)&&(ns->to--))
if (!ns->to)
return -EINVAL;
return 0;
}
static void __nspi_espi_tx(struct nds_spi_slave *ns, const void *dout)
{
ns->regs->data = *(u8 *)dout;
}
static int __nspi_espi_rx(struct nds_spi_slave *ns, void *din, unsigned int bytes)
{
*(u8 *)din = ns->regs->data;
return bytes;
}
static int __ae3xx_spi_xfer(struct nds_spi_slave *ns,
unsigned int bitlen, const void *data_out, void *data_in,
unsigned long flags)
{
unsigned int event, rx_bytes;
const void *dout = NULL;
void *din = NULL;
int num_blks, num_chunks, max_tran_len, tran_len;
int num_bytes;
u8 *cmd_buf = ns->cmd_buf;
size_t cmd_len = ns->cmd_len;
size_t data_len = bitlen / 8;
int rf_cnt;
int ret = 0;
max_tran_len = ns->max_transfer_length;
switch (flags) {
case SPI_XFER_BEGIN:
cmd_len = ns->cmd_len = data_len;
memcpy(cmd_buf, data_out, cmd_len);
return 0;
case 0:
case SPI_XFER_END:
if (bitlen == 0) {
return 0;
}
ns->data_len = data_len;
ns->din = (u8 *)data_in;
ns->dout = (u8 *)data_out;
break;
case SPI_XFER_BEGIN | SPI_XFER_END:
ns->data_len = 0;
ns->din = 0;
ns->dout = 0;
cmd_len = ns->cmd_len = data_len;
memcpy(cmd_buf, data_out, cmd_len);
data_out = 0;
data_len = 0;
__ae3xx_spi_start(ns);
break;
}
debug("spi_xfer: data_out %08X(%p) data_in %08X(%p) data_len %u\n",
*(uint *)data_out, data_out, *(uint *)data_in, data_in, data_len);
num_chunks = DIV_ROUND_UP(data_len, max_tran_len);
din = data_in;
dout = data_out;
while (num_chunks--) {
tran_len = min(data_len, (size_t)max_tran_len);
ns->tran_len = tran_len;
num_blks = DIV_ROUND_UP(tran_len , CHUNK_SIZE);
num_bytes = (tran_len) % CHUNK_SIZE;
if(num_bytes == 0)
num_bytes = CHUNK_SIZE;
__ae3xx_spi_start(ns);
while (num_blks) {
event = in_le32(&ns->regs->status);
if ((event & TXEPTY) && (data_out)) {
__nspi_espi_tx(ns, dout);
num_blks -= CHUNK_SIZE;
dout += CHUNK_SIZE;
}
if ((event & RXFVE_MASK) && (data_in)) {
rf_cnt = ((event & RXFVE_MASK)>> RXFVE_OFFSET);
if (rf_cnt >= CHUNK_SIZE)
rx_bytes = CHUNK_SIZE;
else if (num_blks == 1 && rf_cnt == num_bytes)
rx_bytes = num_bytes;
else
continue;
if (__nspi_espi_rx(ns, din, rx_bytes) == rx_bytes) {
num_blks -= CHUNK_SIZE;
din = (unsigned char *)din + rx_bytes;
}
}
}
data_len -= tran_len;
if(data_len)
{
ns->cmd_buf[1] += ((tran_len>>16)&0xff);
ns->cmd_buf[2] += ((tran_len>>8)&0xff);
ns->cmd_buf[3] += ((tran_len)&0xff);
ns->data_len = data_len;
}
ret = __ae3xx_spi_stop(ns);
}
ret = __ae3xx_spi_stop(ns);
return ret;
}
#ifndef CONFIG_DM_SPI
#define to_nds_spi_slave(s) container_of(s, struct nds_spi_slave, slave)
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct nds_spi_slave *ns;
if (!spi_cs_is_valid(bus, cs))
return NULL;
ns = spi_alloc_slave(struct nds_spi_slave, bus, cs);
switch (bus) {
case SPI0_BUS:
ns->regs = (struct ae3xx_spi_regs *)SPI0_BASE;
break;
case SPI1_BUS:
ns->regs = (struct ae3xx_spi_regs *)SPI1_BASE;
break;
default:
return NULL;
}
ns->freq= max_hz;
ns->mode = mode;
ns->to = SPI_TIMEOUT;
ns->max_transfer_length = MAX_TRANSFER_LEN;
ns->slave.max_write_size = MAX_TRANSFER_LEN;
if (!ns)
return NULL;
return &ns->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct nds_spi_slave *ns = to_nds_spi_slave(slave);
free(ns);
}
void spi_init(void)
{
/* do nothing */
}
int spi_claim_bus(struct spi_slave *slave)
{
struct nds_spi_slave *ns = to_nds_spi_slave(slave);
return __ae3xx_spi_claim_bus(ns);
}
void spi_release_bus(struct spi_slave *slave)
{
struct nds_spi_slave *ns = to_nds_spi_slave(slave);
__ae3xx_spi_release_bus(ns);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out,
void *data_in, unsigned long flags)
{
struct nds_spi_slave *ns = to_nds_spi_slave(slave);
return __ae3xx_spi_xfer(ns, bitlen, data_out, data_in, flags);
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return bus == 0 && cs < NSPI_MAX_CS_NUM;
}
void spi_cs_activate(struct spi_slave *slave)
{
struct nds_spi_slave *ns = to_nds_spi_slave(slave);
__ae3xx_spi_start(ns);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct nds_spi_slave *ns = to_nds_spi_slave(slave);
__ae3xx_spi_stop(ns);
}
#else
static int ae3xx_spi_set_speed(struct udevice *bus, uint max_hz)
{
struct nds_spi_slave *ns = dev_get_priv(bus);
debug("%s speed %u\n", __func__, max_hz);
ns->freq = max_hz;
__ae3xx_spi_set_speed(ns);
return 0;
}
static int ae3xx_spi_set_mode(struct udevice *bus, uint mode)
{
struct nds_spi_slave *ns = dev_get_priv(bus);
debug("%s mode %u\n", __func__, mode);
ns->mode = mode;
return 0;
}
static int ae3xx_spi_claim_bus(struct udevice *dev)
{
struct dm_spi_slave_platdata *slave_plat =
dev_get_parent_platdata(dev);
struct udevice *bus = dev->parent;
struct nds_spi_slave *ns = dev_get_priv(bus);
if (slave_plat->cs >= ns->num_cs) {
printf("Invalid SPI chipselect\n");
return -EINVAL;
}
return __ae3xx_spi_claim_bus(ns);
}
static int ae3xx_spi_release_bus(struct udevice *dev)
{
struct nds_spi_slave *ns = dev_get_priv(dev->parent);
return __ae3xx_spi_release_bus(ns);
}
static int ae3xx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din,
unsigned long flags)
{
struct udevice *bus = dev->parent;
struct nds_spi_slave *ns = dev_get_priv(bus);
return __ae3xx_spi_xfer(ns, bitlen, dout, din, flags);
}
static int ae3xx_spi_get_clk(struct udevice *bus)
{
struct nds_spi_slave *ns = dev_get_priv(bus);
struct clk clk;
ulong clk_rate;
int ret;
ret = clk_get_by_index(bus, 0, &clk);
if (ret)
return -EINVAL;
clk_rate = clk_get_rate(&clk);
if (!clk_rate)
return -EINVAL;
ns->clock = clk_rate;
clk_free(&clk);
return 0;
}
static int ae3xx_spi_probe(struct udevice *bus)
{
struct nds_spi_slave *ns = dev_get_priv(bus);
ns->to = SPI_TIMEOUT;
ns->max_transfer_length = MAX_TRANSFER_LEN;
ns->mtiming = ns->regs->timing;
ae3xx_spi_get_clk(bus);
return 0;
}
static int ae3xx_ofdata_to_platadata(struct udevice *bus)
{
struct nds_spi_slave *ns = dev_get_priv(bus);
const void *blob = gd->fdt_blob;
int node = dev_of_offset(bus);
ns->regs = map_physmem(devfdt_get_addr(bus),
sizeof(struct ae3xx_spi_regs),
MAP_NOCACHE);
if (!ns->regs) {
printf("%s: could not map device address\n", __func__);
return -EINVAL;
}
ns->num_cs = fdtdec_get_int(blob, node, "num-cs", 4);
return 0;
}
static const struct dm_spi_ops ae3xx_spi_ops = {
.claim_bus = ae3xx_spi_claim_bus,
.release_bus = ae3xx_spi_release_bus,
.xfer = ae3xx_spi_xfer,
.set_speed = ae3xx_spi_set_speed,
.set_mode = ae3xx_spi_set_mode,
};
static const struct udevice_id ae3xx_spi_ids[] = {
{ .compatible = "andestech,atcspi200" },
{ }
};
U_BOOT_DRIVER(ae3xx_spi) = {
.name = "ae3xx_spi",
.id = UCLASS_SPI,
.of_match = ae3xx_spi_ids,
.ops = &ae3xx_spi_ops,
.ofdata_to_platdata = ae3xx_ofdata_to_platadata,
.priv_auto_alloc_size = sizeof(struct nds_spi_slave),
.probe = ae3xx_spi_probe,
};
#endif
Loading…
Cancel
Save