Zynq spi controller driver supports 2 buses and 3 chipselects on each bus. Signed-off-by: Jagannadha Sutradharudu Teki <jaganna@xilinx.com> Acked-by: Siva Durga Prasad Paladugu <sivadur@xilinx.com> Signed-off-by: Michal Simek <michal.simek@xilinx.com>master
parent
3488850629
commit
1465d055f9
@ -0,0 +1,280 @@ |
||||
/*
|
||||
* (C) Copyright 2013 Inc. |
||||
* |
||||
* Xilinx Zynq PS SPI controller driver (master mode only) |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
#include <common.h> |
||||
#include <malloc.h> |
||||
#include <spi.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/hardware.h> |
||||
|
||||
/* zynq spi register bit masks ZYNQ_SPI_<REG>_<BIT>_MASK */ |
||||
#define ZYNQ_SPI_CR_MSA_MASK (1 << 15) /* Manual start enb */ |
||||
#define ZYNQ_SPI_CR_MCS_MASK (1 << 14) /* Manual chip select */ |
||||
#define ZYNQ_SPI_CR_CS_MASK (0xF << 10) /* Chip select */ |
||||
#define ZYNQ_SPI_CR_BRD_MASK (0x7 << 3) /* Baud rate div */ |
||||
#define ZYNQ_SPI_CR_CPHA_MASK (1 << 2) /* Clock phase */ |
||||
#define ZYNQ_SPI_CR_CPOL_MASK (1 << 1) /* Clock polarity */ |
||||
#define ZYNQ_SPI_CR_MSTREN_MASK (1 << 0) /* Mode select */ |
||||
#define ZYNQ_SPI_IXR_RXNEMPTY_MASK (1 << 4) /* RX_FIFO_not_empty */ |
||||
#define ZYNQ_SPI_IXR_TXOW_MASK (1 << 2) /* TX_FIFO_not_full */ |
||||
#define ZYNQ_SPI_IXR_ALL_MASK 0x7F /* All IXR bits */ |
||||
#define ZYNQ_SPI_ENR_SPI_EN_MASK (1 << 0) /* SPI Enable */ |
||||
|
||||
#define ZYNQ_SPI_FIFO_DEPTH 128 |
||||
#ifndef CONFIG_SYS_ZYNQ_SPI_WAIT |
||||
#define CONFIG_SYS_ZYNQ_SPI_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */ |
||||
#endif |
||||
|
||||
/* zynq spi register set */ |
||||
struct zynq_spi_regs { |
||||
u32 cr; /* 0x00 */ |
||||
u32 isr; /* 0x04 */ |
||||
u32 ier; /* 0x08 */ |
||||
u32 idr; /* 0x0C */ |
||||
u32 imr; /* 0x10 */ |
||||
u32 enr; /* 0x14 */ |
||||
u32 dr; /* 0x18 */ |
||||
u32 txdr; /* 0x1C */ |
||||
u32 rxdr; /* 0x20 */ |
||||
}; |
||||
|
||||
/* zynq spi slave */ |
||||
struct zynq_spi_slave { |
||||
struct spi_slave slave; |
||||
struct zynq_spi_regs *base; |
||||
u8 mode; |
||||
u8 fifo_depth; |
||||
u32 speed_hz; |
||||
u32 input_hz; |
||||
u32 req_hz; |
||||
}; |
||||
|
||||
static inline struct zynq_spi_slave *to_zynq_spi_slave(struct spi_slave *slave) |
||||
{ |
||||
return container_of(slave, struct zynq_spi_slave, slave); |
||||
} |
||||
|
||||
static inline struct zynq_spi_regs *get_zynq_spi_base(int dev) |
||||
{ |
||||
if (dev) |
||||
return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR1; |
||||
else |
||||
return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR0; |
||||
} |
||||
|
||||
static void zynq_spi_init_hw(struct zynq_spi_slave *zslave) |
||||
{ |
||||
u32 confr; |
||||
|
||||
/* Disable SPI */ |
||||
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); |
||||
|
||||
/* Disable Interrupts */ |
||||
writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->idr); |
||||
|
||||
/* Clear RX FIFO */ |
||||
while (readl(&zslave->base->isr) & |
||||
ZYNQ_SPI_IXR_RXNEMPTY_MASK) |
||||
readl(&zslave->base->rxdr); |
||||
|
||||
/* Clear Interrupts */ |
||||
writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->isr); |
||||
|
||||
/* Manual slave select and Auto start */ |
||||
confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK | |
||||
ZYNQ_SPI_CR_MSTREN_MASK; |
||||
confr &= ~ZYNQ_SPI_CR_MSA_MASK; |
||||
writel(confr, &zslave->base->cr); |
||||
|
||||
/* Enable SPI */ |
||||
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); |
||||
} |
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs) |
||||
{ |
||||
/* 2 bus with 3 chipselect */ |
||||
return bus < 2 && cs < 3; |
||||
} |
||||
|
||||
void spi_cs_activate(struct spi_slave *slave) |
||||
{ |
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); |
||||
u32 cr; |
||||
|
||||
debug("spi_cs_activate: 0x%08x\n", (u32)slave); |
||||
|
||||
clrbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); |
||||
cr = readl(&zslave->base->cr); |
||||
/*
|
||||
* CS cal logic: CS[13:10] |
||||
* xxx0 - cs0 |
||||
* xx01 - cs1 |
||||
* x011 - cs2 |
||||
*/ |
||||
cr |= (~(0x1 << slave->cs) << 10) & ZYNQ_SPI_CR_CS_MASK; |
||||
writel(cr, &zslave->base->cr); |
||||
} |
||||
|
||||
void spi_cs_deactivate(struct spi_slave *slave) |
||||
{ |
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); |
||||
|
||||
debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); |
||||
|
||||
setbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); |
||||
} |
||||
|
||||
void spi_init() |
||||
{ |
||||
/* nothing to do */ |
||||
} |
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, |
||||
unsigned int max_hz, unsigned int mode) |
||||
{ |
||||
struct zynq_spi_slave *zslave; |
||||
|
||||
if (!spi_cs_is_valid(bus, cs)) |
||||
return NULL; |
||||
|
||||
zslave = spi_alloc_slave(struct zynq_spi_slave, bus, cs); |
||||
if (!zslave) { |
||||
printf("SPI_error: Fail to allocate zynq_spi_slave\n"); |
||||
return NULL; |
||||
} |
||||
|
||||
zslave->base = get_zynq_spi_base(bus); |
||||
zslave->mode = mode; |
||||
zslave->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; |
||||
zslave->input_hz = 166666700; |
||||
zslave->speed_hz = zslave->input_hz / 2; |
||||
zslave->req_hz = max_hz; |
||||
|
||||
/* init the zynq spi hw */ |
||||
zynq_spi_init_hw(zslave); |
||||
|
||||
return &zslave->slave; |
||||
} |
||||
|
||||
void spi_free_slave(struct spi_slave *slave) |
||||
{ |
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); |
||||
|
||||
debug("spi_free_slave: 0x%08x\n", (u32)slave); |
||||
free(zslave); |
||||
} |
||||
|
||||
int spi_claim_bus(struct spi_slave *slave) |
||||
{ |
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); |
||||
u32 confr = 0; |
||||
u8 baud_rate_val = 0; |
||||
|
||||
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); |
||||
|
||||
/* Set the SPI Clock phase and polarities */ |
||||
confr = readl(&zslave->base->cr); |
||||
confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); |
||||
if (zslave->mode & SPI_CPHA) |
||||
confr |= ZYNQ_SPI_CR_CPHA_MASK; |
||||
if (zslave->mode & SPI_CPOL) |
||||
confr |= ZYNQ_SPI_CR_CPOL_MASK; |
||||
|
||||
/* Set the clock frequency */ |
||||
if (zslave->req_hz == 0) { |
||||
/* Set baudrate x8, if the req_hz is 0 */ |
||||
baud_rate_val = 0x2; |
||||
} else if (zslave->speed_hz != zslave->req_hz) { |
||||
while ((baud_rate_val < 8) && |
||||
((zslave->input_hz / |
||||
(2 << baud_rate_val)) > zslave->req_hz)) |
||||
baud_rate_val++; |
||||
zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val); |
||||
} |
||||
confr &= ~ZYNQ_SPI_CR_BRD_MASK; |
||||
confr |= (baud_rate_val << 3); |
||||
writel(confr, &zslave->base->cr); |
||||
|
||||
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void spi_release_bus(struct spi_slave *slave) |
||||
{ |
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); |
||||
|
||||
debug("spi_release_bus: 0x%08x\n", (u32)slave); |
||||
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); |
||||
} |
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, |
||||
void *din, unsigned long flags) |
||||
{ |
||||
struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); |
||||
u32 len = bitlen / 8; |
||||
u32 tx_len = len, rx_len = len, tx_tvl; |
||||
const u8 *tx_buf = dout; |
||||
u8 *rx_buf = din, buf; |
||||
u32 ts, status; |
||||
|
||||
debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n", |
||||
slave->bus, slave->cs, bitlen, len, flags); |
||||
|
||||
if (bitlen == 0) |
||||
return -1; |
||||
|
||||
if (bitlen % 8) { |
||||
debug("spi_xfer: Non byte aligned SPI transfer\n"); |
||||
return -1; |
||||
} |
||||
|
||||
if (flags & SPI_XFER_BEGIN) |
||||
spi_cs_activate(slave); |
||||
|
||||
while (rx_len > 0) { |
||||
/* Write the data into TX FIFO - tx threshold is fifo_depth */ |
||||
tx_tvl = 0; |
||||
while ((tx_tvl < zslave->fifo_depth) && tx_len) { |
||||
if (tx_buf) |
||||
buf = *tx_buf++; |
||||
else |
||||
buf = 0; |
||||
writel(buf, &zslave->base->txdr); |
||||
tx_len--; |
||||
tx_tvl++; |
||||
} |
||||
|
||||
/* Check TX FIFO completion */ |
||||
ts = get_timer(0); |
||||
status = readl(&zslave->base->isr); |
||||
while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) { |
||||
if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) { |
||||
printf("spi_xfer: Timeout! TX FIFO not full\n"); |
||||
return -1; |
||||
} |
||||
status = readl(&zslave->base->isr); |
||||
} |
||||
|
||||
/* Read the data from RX FIFO */ |
||||
status = readl(&zslave->base->isr); |
||||
while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) { |
||||
buf = readl(&zslave->base->rxdr); |
||||
if (rx_buf) |
||||
*rx_buf++ = buf; |
||||
status = readl(&zslave->base->isr); |
||||
rx_len--; |
||||
} |
||||
} |
||||
|
||||
if (flags & SPI_XFER_END) |
||||
spi_cs_deactivate(slave); |
||||
|
||||
return 0; |
||||
} |
Loading…
Reference in new issue