mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-07 13:23:20 +09:00
![Simon Glass](/assets/img/avatar_default.png)
The SPI transaction delay is supposed to be measured from the end of one transaction to the start of the next. The code does not work that way, so fix it. Signed-off-by: Simon Glass <sjg@chromium.org> Tested-by: Ajay Kumar <ajaykumar.rs@samsung.com> Reviewed-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com>
584 lines
15 KiB
C
584 lines
15 KiB
C
/*
|
|
* (C) Copyright 2012 SAMSUNG Electronics
|
|
* Padmavathi Venna <padma.v@samsung.com>
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0+
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <spi.h>
|
|
#include <fdtdec.h>
|
|
#include <asm/arch/clk.h>
|
|
#include <asm/arch/clock.h>
|
|
#include <asm/arch/cpu.h>
|
|
#include <asm/arch/gpio.h>
|
|
#include <asm/arch/pinmux.h>
|
|
#include <asm/arch-exynos/spi.h>
|
|
#include <asm/io.h>
|
|
|
|
DECLARE_GLOBAL_DATA_PTR;
|
|
|
|
/* Information about each SPI controller */
|
|
struct spi_bus {
|
|
enum periph_id periph_id;
|
|
s32 frequency; /* Default clock frequency, -1 for none */
|
|
struct exynos_spi *regs;
|
|
int inited; /* 1 if this bus is ready for use */
|
|
int node;
|
|
uint deactivate_delay_us; /* Delay to wait after deactivate */
|
|
};
|
|
|
|
/* A list of spi buses that we know about */
|
|
static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS];
|
|
static unsigned int bus_count;
|
|
|
|
struct exynos_spi_slave {
|
|
struct spi_slave slave;
|
|
struct exynos_spi *regs;
|
|
unsigned int freq; /* Default frequency */
|
|
unsigned int mode;
|
|
enum periph_id periph_id; /* Peripheral ID for this device */
|
|
unsigned int fifo_size;
|
|
int skip_preamble;
|
|
struct spi_bus *bus; /* Pointer to our SPI bus info */
|
|
ulong last_transaction_us; /* Time of last transaction end */
|
|
};
|
|
|
|
static struct spi_bus *spi_get_bus(unsigned dev_index)
|
|
{
|
|
if (dev_index < bus_count)
|
|
return &spi_bus[dev_index];
|
|
debug("%s: invalid bus %d", __func__, dev_index);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave)
|
|
{
|
|
return container_of(slave, struct exynos_spi_slave, slave);
|
|
}
|
|
|
|
/**
|
|
* Setup the driver private data
|
|
*
|
|
* @param bus ID of the bus that the slave is attached to
|
|
* @param cs ID of the chip select connected to the slave
|
|
* @param max_hz Required spi frequency
|
|
* @param mode Required spi mode (clk polarity, clk phase and
|
|
* master or slave)
|
|
* @return new device or NULL
|
|
*/
|
|
struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
|
|
unsigned int max_hz, unsigned int mode)
|
|
{
|
|
struct exynos_spi_slave *spi_slave;
|
|
struct spi_bus *bus;
|
|
|
|
if (!spi_cs_is_valid(busnum, cs)) {
|
|
debug("%s: Invalid bus/chip select %d, %d\n", __func__,
|
|
busnum, cs);
|
|
return NULL;
|
|
}
|
|
|
|
spi_slave = spi_alloc_slave(struct exynos_spi_slave, busnum, cs);
|
|
if (!spi_slave) {
|
|
debug("%s: Could not allocate spi_slave\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
bus = &spi_bus[busnum];
|
|
spi_slave->bus = bus;
|
|
spi_slave->regs = bus->regs;
|
|
spi_slave->mode = mode;
|
|
spi_slave->periph_id = bus->periph_id;
|
|
if (bus->periph_id == PERIPH_ID_SPI1 ||
|
|
bus->periph_id == PERIPH_ID_SPI2)
|
|
spi_slave->fifo_size = 64;
|
|
else
|
|
spi_slave->fifo_size = 256;
|
|
|
|
spi_slave->skip_preamble = 0;
|
|
spi_slave->last_transaction_us = timer_get_us();
|
|
|
|
spi_slave->freq = bus->frequency;
|
|
if (max_hz)
|
|
spi_slave->freq = min(max_hz, spi_slave->freq);
|
|
|
|
return &spi_slave->slave;
|
|
}
|
|
|
|
/**
|
|
* Free spi controller
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
*/
|
|
void spi_free_slave(struct spi_slave *slave)
|
|
{
|
|
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
|
|
|
free(spi_slave);
|
|
}
|
|
|
|
/**
|
|
* Flush spi tx, rx fifos and reset the SPI controller
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
*/
|
|
static void spi_flush_fifo(struct spi_slave *slave)
|
|
{
|
|
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
|
struct exynos_spi *regs = spi_slave->regs;
|
|
|
|
clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
|
|
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
|
|
}
|
|
|
|
/**
|
|
* Initialize the spi base registers, set the required clock frequency and
|
|
* initialize the gpios
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
* @return zero on success else a negative value
|
|
*/
|
|
int spi_claim_bus(struct spi_slave *slave)
|
|
{
|
|
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
|
struct exynos_spi *regs = spi_slave->regs;
|
|
u32 reg = 0;
|
|
int ret;
|
|
|
|
ret = set_spi_clk(spi_slave->periph_id,
|
|
spi_slave->freq);
|
|
if (ret < 0) {
|
|
debug("%s: Failed to setup spi clock\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE);
|
|
|
|
spi_flush_fifo(slave);
|
|
|
|
reg = readl(®s->ch_cfg);
|
|
reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L);
|
|
|
|
if (spi_slave->mode & SPI_CPHA)
|
|
reg |= SPI_CH_CPHA_B;
|
|
|
|
if (spi_slave->mode & SPI_CPOL)
|
|
reg |= SPI_CH_CPOL_L;
|
|
|
|
writel(reg, ®s->ch_cfg);
|
|
writel(SPI_FB_DELAY_180, ®s->fb_clk);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Reset the spi H/W and flush the tx and rx fifos
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
*/
|
|
void spi_release_bus(struct spi_slave *slave)
|
|
{
|
|
spi_flush_fifo(slave);
|
|
}
|
|
|
|
static void spi_get_fifo_levels(struct exynos_spi *regs,
|
|
int *rx_lvl, int *tx_lvl)
|
|
{
|
|
uint32_t spi_sts = readl(®s->spi_sts);
|
|
|
|
*rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
|
|
*tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
|
|
}
|
|
|
|
/**
|
|
* If there's something to transfer, do a software reset and set a
|
|
* transaction size.
|
|
*
|
|
* @param regs SPI peripheral registers
|
|
* @param count Number of bytes to transfer
|
|
* @param step Number of bytes to transfer in each packet (1 or 4)
|
|
*/
|
|
static void spi_request_bytes(struct exynos_spi *regs, int count, int step)
|
|
{
|
|
/* For word address we need to swap bytes */
|
|
if (step == 4) {
|
|
setbits_le32(®s->mode_cfg,
|
|
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
|
|
count /= 4;
|
|
setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
|
|
SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
|
|
SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
|
|
} else {
|
|
/* Select byte access and clear the swap configuration */
|
|
clrbits_le32(®s->mode_cfg,
|
|
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
|
|
writel(0, ®s->swap_cfg);
|
|
}
|
|
|
|
assert(count && count < (1 << 16));
|
|
setbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
|
|
writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);
|
|
}
|
|
|
|
static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
|
|
void **dinp, void const **doutp, unsigned long flags)
|
|
{
|
|
struct exynos_spi *regs = spi_slave->regs;
|
|
uchar *rxp = *dinp;
|
|
const uchar *txp = *doutp;
|
|
int rx_lvl, tx_lvl;
|
|
uint out_bytes, in_bytes;
|
|
int toread;
|
|
unsigned start = get_timer(0);
|
|
int stopping;
|
|
int step;
|
|
|
|
out_bytes = in_bytes = todo;
|
|
|
|
stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) &&
|
|
!(spi_slave->mode & SPI_SLAVE);
|
|
|
|
/*
|
|
* Try to transfer words if we can. This helps read performance at
|
|
* SPI clock speeds above about 20MHz.
|
|
*/
|
|
step = 1;
|
|
if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) &&
|
|
!spi_slave->skip_preamble)
|
|
step = 4;
|
|
|
|
/*
|
|
* If there's something to send, do a software reset and set a
|
|
* transaction size.
|
|
*/
|
|
spi_request_bytes(regs, todo, step);
|
|
|
|
/*
|
|
* Bytes are transmitted/received in pairs. Wait to receive all the
|
|
* data because then transmission will be done as well.
|
|
*/
|
|
toread = in_bytes;
|
|
|
|
while (in_bytes) {
|
|
int temp;
|
|
|
|
/* Keep the fifos full/empty. */
|
|
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
|
|
|
|
/*
|
|
* Don't completely fill the txfifo, since we don't want our
|
|
* rxfifo to overflow, and it may already contain data.
|
|
*/
|
|
while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) {
|
|
if (!txp)
|
|
temp = -1;
|
|
else if (step == 4)
|
|
temp = *(uint32_t *)txp;
|
|
else
|
|
temp = *txp;
|
|
writel(temp, ®s->tx_data);
|
|
out_bytes -= step;
|
|
if (txp)
|
|
txp += step;
|
|
tx_lvl += step;
|
|
}
|
|
if (rx_lvl >= step) {
|
|
while (rx_lvl >= step) {
|
|
temp = readl(®s->rx_data);
|
|
if (spi_slave->skip_preamble) {
|
|
if (temp == SPI_PREAMBLE_END_BYTE) {
|
|
spi_slave->skip_preamble = 0;
|
|
stopping = 0;
|
|
}
|
|
} else {
|
|
if (rxp || stopping) {
|
|
if (step == 4)
|
|
*(uint32_t *)rxp = temp;
|
|
else
|
|
*rxp = temp;
|
|
rxp += step;
|
|
}
|
|
in_bytes -= step;
|
|
}
|
|
toread -= step;
|
|
rx_lvl -= step;
|
|
}
|
|
} else if (!toread) {
|
|
/*
|
|
* We have run out of input data, but haven't read
|
|
* enough bytes after the preamble yet. Read some more,
|
|
* and make sure that we transmit dummy bytes too, to
|
|
* keep things going.
|
|
*/
|
|
assert(!out_bytes);
|
|
out_bytes = in_bytes;
|
|
toread = in_bytes;
|
|
txp = NULL;
|
|
spi_request_bytes(regs, toread, step);
|
|
}
|
|
if (spi_slave->skip_preamble && get_timer(start) > 100) {
|
|
printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
|
|
in_bytes, out_bytes);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
*dinp = rxp;
|
|
*doutp = txp;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Transfer and receive data
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
* @param bitlen No of bits to tranfer or receive
|
|
* @param dout Pointer to transfer buffer
|
|
* @param din Pointer to receive buffer
|
|
* @param flags Flags for transfer begin and end
|
|
* @return zero on success else a negative value
|
|
*/
|
|
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
|
void *din, unsigned long flags)
|
|
{
|
|
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
|
int upto, todo;
|
|
int bytelen;
|
|
int ret = 0;
|
|
|
|
/* spi core configured to do 8 bit transfers */
|
|
if (bitlen % 8) {
|
|
debug("Non byte aligned SPI transfer.\n");
|
|
return -1;
|
|
}
|
|
|
|
/* Start the transaction, if necessary. */
|
|
if ((flags & SPI_XFER_BEGIN))
|
|
spi_cs_activate(slave);
|
|
|
|
/*
|
|
* Exynos SPI limits each transfer to 65535 transfers. To keep
|
|
* things simple, allow a maximum of 65532 bytes. We could allow
|
|
* more in word mode, but the performance difference is small.
|
|
*/
|
|
bytelen = bitlen / 8;
|
|
for (upto = 0; !ret && upto < bytelen; upto += todo) {
|
|
todo = min(bytelen - upto, (1 << 16) - 4);
|
|
ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
/* Stop the transaction, if necessary. */
|
|
if ((flags & SPI_XFER_END) && !(spi_slave->mode & SPI_SLAVE)) {
|
|
spi_cs_deactivate(slave);
|
|
if (spi_slave->skip_preamble) {
|
|
assert(!spi_slave->skip_preamble);
|
|
debug("Failed to complete premable transaction\n");
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Validates the bus and chip select numbers
|
|
*
|
|
* @param bus ID of the bus that the slave is attached to
|
|
* @param cs ID of the chip select connected to the slave
|
|
* @return one on success else zero
|
|
*/
|
|
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
|
{
|
|
return spi_get_bus(bus) && cs == 0;
|
|
}
|
|
|
|
/**
|
|
* Activate the CS by driving it LOW
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
*/
|
|
void spi_cs_activate(struct spi_slave *slave)
|
|
{
|
|
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
|
|
|
/* If it's too soon to do another transaction, wait */
|
|
if (spi_slave->bus->deactivate_delay_us &&
|
|
spi_slave->last_transaction_us) {
|
|
ulong delay_us; /* The delay completed so far */
|
|
delay_us = timer_get_us() - spi_slave->last_transaction_us;
|
|
if (delay_us < spi_slave->bus->deactivate_delay_us)
|
|
udelay(spi_slave->bus->deactivate_delay_us - delay_us);
|
|
}
|
|
|
|
clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
|
|
debug("Activate CS, bus %d\n", spi_slave->slave.bus);
|
|
spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE;
|
|
}
|
|
|
|
/**
|
|
* Deactivate the CS by driving it HIGH
|
|
*
|
|
* @param slave Pointer to spi_slave to which controller has to
|
|
* communicate with
|
|
*/
|
|
void spi_cs_deactivate(struct spi_slave *slave)
|
|
{
|
|
struct exynos_spi_slave *spi_slave = to_exynos_spi(slave);
|
|
|
|
setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT);
|
|
|
|
/* Remember time of this transaction so we can honour the bus delay */
|
|
if (spi_slave->bus->deactivate_delay_us)
|
|
spi_slave->last_transaction_us = timer_get_us();
|
|
|
|
debug("Deactivate CS, bus %d\n", spi_slave->slave.bus);
|
|
}
|
|
|
|
static inline struct exynos_spi *get_spi_base(int dev_index)
|
|
{
|
|
if (dev_index < 3)
|
|
return (struct exynos_spi *)samsung_get_base_spi() + dev_index;
|
|
else
|
|
return (struct exynos_spi *)samsung_get_base_spi_isp() +
|
|
(dev_index - 3);
|
|
}
|
|
|
|
/*
|
|
* Read the SPI config from the device tree node.
|
|
*
|
|
* @param blob FDT blob to read from
|
|
* @param node Node offset to read from
|
|
* @param bus SPI bus structure to fill with information
|
|
* @return 0 if ok, or -FDT_ERR_NOTFOUND if something was missing
|
|
*/
|
|
#ifdef CONFIG_OF_CONTROL
|
|
static int spi_get_config(const void *blob, int node, struct spi_bus *bus)
|
|
{
|
|
bus->node = node;
|
|
bus->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg");
|
|
bus->periph_id = pinmux_decode_periph_id(blob, node);
|
|
|
|
if (bus->periph_id == PERIPH_ID_NONE) {
|
|
debug("%s: Invalid peripheral ID %d\n", __func__,
|
|
bus->periph_id);
|
|
return -FDT_ERR_NOTFOUND;
|
|
}
|
|
|
|
/* Use 500KHz as a suitable default */
|
|
bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
|
|
500000);
|
|
bus->deactivate_delay_us = fdtdec_get_int(blob, node,
|
|
"spi-deactivate-delay", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Process a list of nodes, adding them to our list of SPI ports.
|
|
*
|
|
* @param blob fdt blob
|
|
* @param node_list list of nodes to process (any <=0 are ignored)
|
|
* @param count number of nodes to process
|
|
* @param is_dvc 1 if these are DVC ports, 0 if standard I2C
|
|
* @return 0 if ok, -1 on error
|
|
*/
|
|
static int process_nodes(const void *blob, int node_list[], int count)
|
|
{
|
|
int i;
|
|
|
|
/* build the i2c_controllers[] for each controller */
|
|
for (i = 0; i < count; i++) {
|
|
int node = node_list[i];
|
|
struct spi_bus *bus;
|
|
|
|
if (node <= 0)
|
|
continue;
|
|
|
|
bus = &spi_bus[i];
|
|
if (spi_get_config(blob, node, bus)) {
|
|
printf("exynos spi_init: failed to decode bus %d\n",
|
|
i);
|
|
return -1;
|
|
}
|
|
|
|
debug("spi: controller bus %d at %p, periph_id %d\n",
|
|
i, bus->regs, bus->periph_id);
|
|
bus->inited = 1;
|
|
bus_count++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Set up a new SPI slave for an fdt node
|
|
*
|
|
* @param blob Device tree blob
|
|
* @param node SPI peripheral node to use
|
|
* @return 0 if ok, -1 on error
|
|
*/
|
|
struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node,
|
|
int spi_node)
|
|
{
|
|
struct spi_bus *bus;
|
|
unsigned int i;
|
|
|
|
for (i = 0, bus = spi_bus; i < bus_count; i++, bus++) {
|
|
if (bus->node == spi_node)
|
|
return spi_base_setup_slave_fdt(blob, i, slave_node);
|
|
}
|
|
|
|
debug("%s: Failed to find bus node %d\n", __func__, spi_node);
|
|
return NULL;
|
|
}
|
|
|
|
/* Sadly there is no error return from this function */
|
|
void spi_init(void)
|
|
{
|
|
int count;
|
|
|
|
#ifdef CONFIG_OF_CONTROL
|
|
int node_list[EXYNOS5_SPI_NUM_CONTROLLERS];
|
|
const void *blob = gd->fdt_blob;
|
|
|
|
count = fdtdec_find_aliases_for_id(blob, "spi",
|
|
COMPAT_SAMSUNG_EXYNOS_SPI, node_list,
|
|
EXYNOS5_SPI_NUM_CONTROLLERS);
|
|
if (process_nodes(blob, node_list, count))
|
|
return;
|
|
|
|
#else
|
|
struct spi_bus *bus;
|
|
|
|
for (count = 0; count < EXYNOS5_SPI_NUM_CONTROLLERS; count++) {
|
|
bus = &spi_bus[count];
|
|
bus->regs = get_spi_base(count);
|
|
bus->periph_id = PERIPH_ID_SPI0 + count;
|
|
|
|
/* Although Exynos5 supports upto 50Mhz speed,
|
|
* we are setting it to 10Mhz for safe side
|
|
*/
|
|
bus->frequency = 10000000;
|
|
bus->inited = 1;
|
|
bus->node = 0;
|
|
bus_count = EXYNOS5_SPI_NUM_CONTROLLERS;
|
|
}
|
|
#endif
|
|
}
|