mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-24 12:03:39 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
335 lines
7.8 KiB
C
335 lines
7.8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
*
|
|
* (C) Copyright 2000-2003
|
|
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
|
*
|
|
* Copyright (C) 2004-2009 Freescale Semiconductor, Inc.
|
|
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <spi.h>
|
|
#include <malloc.h>
|
|
#include <asm/immap.h>
|
|
|
|
struct cf_spi_slave {
|
|
struct spi_slave slave;
|
|
uint baudrate;
|
|
int charbit;
|
|
};
|
|
|
|
extern void cfspi_port_conf(void);
|
|
extern int cfspi_claim_bus(uint bus, uint cs);
|
|
extern void cfspi_release_bus(uint bus, uint cs);
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
#ifndef CONFIG_SPI_IDLE_VAL
|
|
#if defined(CONFIG_SPI_MMC)
|
|
#define CONFIG_SPI_IDLE_VAL 0xFFFF
|
|
#else
|
|
#define CONFIG_SPI_IDLE_VAL 0x0
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(CONFIG_CF_DSPI)
|
|
/* DSPI specific mode */
|
|
#define SPI_MODE_MOD 0x00200000
|
|
#define SPI_DBLRATE 0x00100000
|
|
|
|
static inline struct cf_spi_slave *to_cf_spi_slave(struct spi_slave *slave)
|
|
{
|
|
return container_of(slave, struct cf_spi_slave, slave);
|
|
}
|
|
|
|
static void cfspi_init(void)
|
|
{
|
|
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
|
|
|
|
cfspi_port_conf(); /* port configuration */
|
|
|
|
dspi->mcr = DSPI_MCR_MSTR | DSPI_MCR_CSIS7 | DSPI_MCR_CSIS6 |
|
|
DSPI_MCR_CSIS5 | DSPI_MCR_CSIS4 | DSPI_MCR_CSIS3 |
|
|
DSPI_MCR_CSIS2 | DSPI_MCR_CSIS1 | DSPI_MCR_CSIS0 |
|
|
DSPI_MCR_CRXF | DSPI_MCR_CTXF;
|
|
|
|
/* Default setting in platform configuration */
|
|
#ifdef CONFIG_SYS_DSPI_CTAR0
|
|
dspi->ctar[0] = CONFIG_SYS_DSPI_CTAR0;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR1
|
|
dspi->ctar[1] = CONFIG_SYS_DSPI_CTAR1;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR2
|
|
dspi->ctar[2] = CONFIG_SYS_DSPI_CTAR2;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR3
|
|
dspi->ctar[3] = CONFIG_SYS_DSPI_CTAR3;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR4
|
|
dspi->ctar[4] = CONFIG_SYS_DSPI_CTAR4;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR5
|
|
dspi->ctar[5] = CONFIG_SYS_DSPI_CTAR5;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR6
|
|
dspi->ctar[6] = CONFIG_SYS_DSPI_CTAR6;
|
|
#endif
|
|
#ifdef CONFIG_SYS_DSPI_CTAR7
|
|
dspi->ctar[7] = CONFIG_SYS_DSPI_CTAR7;
|
|
#endif
|
|
}
|
|
|
|
static void cfspi_tx(u32 ctrl, u16 data)
|
|
{
|
|
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
|
|
|
|
while ((dspi->sr & 0x0000F000) >= 4) ;
|
|
|
|
dspi->tfr = (ctrl | data);
|
|
}
|
|
|
|
static u16 cfspi_rx(void)
|
|
{
|
|
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
|
|
|
|
while ((dspi->sr & 0x000000F0) == 0) ;
|
|
|
|
return (dspi->rfr & 0xFFFF);
|
|
}
|
|
|
|
static int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout,
|
|
void *din, ulong flags)
|
|
{
|
|
struct cf_spi_slave *cfslave = to_cf_spi_slave(slave);
|
|
u16 *spi_rd16 = NULL, *spi_wr16 = NULL;
|
|
u8 *spi_rd = NULL, *spi_wr = NULL;
|
|
static u32 ctrl = 0;
|
|
uint len = bitlen >> 3;
|
|
|
|
if (cfslave->charbit == 16) {
|
|
bitlen >>= 1;
|
|
spi_wr16 = (u16 *) dout;
|
|
spi_rd16 = (u16 *) din;
|
|
} else {
|
|
spi_wr = (u8 *) dout;
|
|
spi_rd = (u8 *) din;
|
|
}
|
|
|
|
if ((flags & SPI_XFER_BEGIN) == SPI_XFER_BEGIN)
|
|
ctrl |= DSPI_TFR_CONT;
|
|
|
|
ctrl = (ctrl & 0xFF000000) | ((1 << slave->cs) << 16);
|
|
|
|
if (len > 1) {
|
|
int tmp_len = len - 1;
|
|
while (tmp_len--) {
|
|
if (dout != NULL) {
|
|
if (cfslave->charbit == 16)
|
|
cfspi_tx(ctrl, *spi_wr16++);
|
|
else
|
|
cfspi_tx(ctrl, *spi_wr++);
|
|
cfspi_rx();
|
|
}
|
|
|
|
if (din != NULL) {
|
|
cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL);
|
|
if (cfslave->charbit == 16)
|
|
*spi_rd16++ = cfspi_rx();
|
|
else
|
|
*spi_rd++ = cfspi_rx();
|
|
}
|
|
}
|
|
|
|
len = 1; /* remaining byte */
|
|
}
|
|
|
|
if ((flags & SPI_XFER_END) == SPI_XFER_END)
|
|
ctrl &= ~DSPI_TFR_CONT;
|
|
|
|
if (len) {
|
|
if (dout != NULL) {
|
|
if (cfslave->charbit == 16)
|
|
cfspi_tx(ctrl, *spi_wr16);
|
|
else
|
|
cfspi_tx(ctrl, *spi_wr);
|
|
cfspi_rx();
|
|
}
|
|
|
|
if (din != NULL) {
|
|
cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL);
|
|
if (cfslave->charbit == 16)
|
|
*spi_rd16 = cfspi_rx();
|
|
else
|
|
*spi_rd = cfspi_rx();
|
|
}
|
|
} else {
|
|
/* dummy read */
|
|
cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL);
|
|
cfspi_rx();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave,
|
|
uint mode)
|
|
{
|
|
/*
|
|
* bit definition for mode:
|
|
* bit 31 - 28: Transfer size 3 to 16 bits
|
|
* 27 - 26: PCS to SCK delay prescaler
|
|
* 25 - 24: After SCK delay prescaler
|
|
* 23 - 22: Delay after transfer prescaler
|
|
* 21 : Allow overwrite for bit 31-22 and bit 20-8
|
|
* 20 : Double baud rate
|
|
* 19 - 16: PCS to SCK delay scaler
|
|
* 15 - 12: After SCK delay scaler
|
|
* 11 - 8: Delay after transfer scaler
|
|
* 7 - 0: SPI_CPHA, SPI_CPOL, SPI_LSB_FIRST
|
|
*/
|
|
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
|
|
int prescaler[] = { 2, 3, 5, 7 };
|
|
int scaler[] = {
|
|
2, 4, 6, 8,
|
|
16, 32, 64, 128,
|
|
256, 512, 1024, 2048,
|
|
4096, 8192, 16384, 32768
|
|
};
|
|
int i, j, pbrcnt, brcnt, diff, tmp, dbr = 0;
|
|
int best_i, best_j, bestmatch = 0x7FFFFFFF, baud_speed;
|
|
u32 bus_setup = 0;
|
|
|
|
tmp = (prescaler[3] * scaler[15]);
|
|
/* Maximum and minimum baudrate it can handle */
|
|
if ((cfslave->baudrate > (gd->bus_clk >> 1)) ||
|
|
(cfslave->baudrate < (gd->bus_clk / tmp))) {
|
|
printf("Exceed baudrate limitation: Max %d - Min %d\n",
|
|
(int)(gd->bus_clk >> 1), (int)(gd->bus_clk / tmp));
|
|
return NULL;
|
|
}
|
|
|
|
/* Activate Double Baud when it exceed 1/4 the bus clk */
|
|
if ((CONFIG_SYS_DSPI_CTAR0 & DSPI_CTAR_DBR) ||
|
|
(cfslave->baudrate > (gd->bus_clk / (prescaler[0] * scaler[0])))) {
|
|
bus_setup |= DSPI_CTAR_DBR;
|
|
dbr = 1;
|
|
}
|
|
|
|
if (mode & SPI_CPOL)
|
|
bus_setup |= DSPI_CTAR_CPOL;
|
|
if (mode & SPI_CPHA)
|
|
bus_setup |= DSPI_CTAR_CPHA;
|
|
if (mode & SPI_LSB_FIRST)
|
|
bus_setup |= DSPI_CTAR_LSBFE;
|
|
|
|
/* Overwrite default value set in platform configuration file */
|
|
if (mode & SPI_MODE_MOD) {
|
|
|
|
if ((mode & 0xF0000000) == 0)
|
|
bus_setup |=
|
|
dspi->ctar[cfslave->slave.bus] & 0x78000000;
|
|
else
|
|
bus_setup |= ((mode & 0xF0000000) >> 1);
|
|
|
|
/*
|
|
* Check to see if it is enabled by default in platform
|
|
* config, or manual setting passed by mode parameter
|
|
*/
|
|
if (mode & SPI_DBLRATE) {
|
|
bus_setup |= DSPI_CTAR_DBR;
|
|
dbr = 1;
|
|
}
|
|
bus_setup |= (mode & 0x0FC00000) >> 4; /* PSCSCK, PASC, PDT */
|
|
bus_setup |= (mode & 0x000FFF00) >> 4; /* CSSCK, ASC, DT */
|
|
} else
|
|
bus_setup |= (dspi->ctar[cfslave->slave.bus] & 0x78FCFFF0);
|
|
|
|
cfslave->charbit =
|
|
((dspi->ctar[cfslave->slave.bus] & 0x78000000) ==
|
|
0x78000000) ? 16 : 8;
|
|
|
|
pbrcnt = sizeof(prescaler) / sizeof(int);
|
|
brcnt = sizeof(scaler) / sizeof(int);
|
|
|
|
/* baudrate calculation - to closer value, may not be exact match */
|
|
for (best_i = 0, best_j = 0, i = 0; i < pbrcnt; i++) {
|
|
baud_speed = gd->bus_clk / prescaler[i];
|
|
for (j = 0; j < brcnt; j++) {
|
|
tmp = (baud_speed / scaler[j]) * (1 + dbr);
|
|
|
|
if (tmp > cfslave->baudrate)
|
|
diff = tmp - cfslave->baudrate;
|
|
else
|
|
diff = cfslave->baudrate - tmp;
|
|
|
|
if (diff < bestmatch) {
|
|
bestmatch = diff;
|
|
best_i = i;
|
|
best_j = j;
|
|
}
|
|
}
|
|
}
|
|
bus_setup |= (DSPI_CTAR_PBR(best_i) | DSPI_CTAR_BR(best_j));
|
|
dspi->ctar[cfslave->slave.bus] = bus_setup;
|
|
|
|
return &cfslave->slave;
|
|
}
|
|
#endif /* CONFIG_CF_DSPI */
|
|
|
|
#ifdef CONFIG_CMD_SPI
|
|
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
|
{
|
|
if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8)))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void spi_init(void)
|
|
{
|
|
cfspi_init();
|
|
}
|
|
|
|
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
|
unsigned int max_hz, unsigned int mode)
|
|
{
|
|
struct cf_spi_slave *cfslave;
|
|
|
|
if (!spi_cs_is_valid(bus, cs))
|
|
return NULL;
|
|
|
|
cfslave = spi_alloc_slave(struct cf_spi_slave, bus, cs);
|
|
if (!cfslave)
|
|
return NULL;
|
|
|
|
cfslave->baudrate = max_hz;
|
|
|
|
/* specific setup */
|
|
return cfspi_setup_slave(cfslave, mode);
|
|
}
|
|
|
|
void spi_free_slave(struct spi_slave *slave)
|
|
{
|
|
struct cf_spi_slave *cfslave = to_cf_spi_slave(slave);
|
|
|
|
free(cfslave);
|
|
}
|
|
|
|
int spi_claim_bus(struct spi_slave *slave)
|
|
{
|
|
return cfspi_claim_bus(slave->bus, slave->cs);
|
|
}
|
|
|
|
void spi_release_bus(struct spi_slave *slave)
|
|
{
|
|
cfspi_release_bus(slave->bus, slave->cs);
|
|
}
|
|
|
|
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
|
void *din, unsigned long flags)
|
|
{
|
|
return cfspi_xfer(slave, bitlen, dout, din, flags);
|
|
}
|
|
#endif /* CONFIG_CMD_SPI */
|