Merge git://git.denx.de/u-boot-spi

master
Tom Rini 6 years ago
commit 2dc5165ee0
  1. 130
      drivers/spi/designware_spi.c
  2. 6
      drivers/spi/omap3_spi.c

@ -10,6 +10,7 @@
* SPDX-License-Identifier: GPL-2.0
*/
#include <asm-generic/gpio.h>
#include <common.h>
#include <clk.h>
#include <dm.h>
@ -18,6 +19,7 @@
#include <spi.h>
#include <fdtdec.h>
#include <linux/compat.h>
#include <linux/iopoll.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
@ -97,6 +99,8 @@ struct dw_spi_priv {
struct clk clk;
unsigned long bus_clk_rate;
struct gpio_desc cs_gpio; /* External chip-select gpio */
int bits_per_word;
u8 cs; /* chip select pin */
u8 tmode; /* TR/TO/RO/EEPROM */
@ -110,24 +114,40 @@ struct dw_spi_priv {
void *rx_end;
};
static inline u32 dw_readl(struct dw_spi_priv *priv, u32 offset)
static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset)
{
return __raw_readl(priv->regs + offset);
}
static inline void dw_writel(struct dw_spi_priv *priv, u32 offset, u32 val)
static inline void dw_write(struct dw_spi_priv *priv, u32 offset, u32 val)
{
__raw_writel(val, priv->regs + offset);
}
static inline u16 dw_readw(struct dw_spi_priv *priv, u32 offset)
static int request_gpio_cs(struct udevice *bus)
{
return __raw_readw(priv->regs + offset);
}
#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_SPL_BUILD)
struct dw_spi_priv *priv = dev_get_priv(bus);
int ret;
static inline void dw_writew(struct dw_spi_priv *priv, u32 offset, u16 val)
{
__raw_writew(val, priv->regs + offset);
/* External chip select gpio line is optional */
ret = gpio_request_by_name(bus, "cs-gpio", 0, &priv->cs_gpio, 0);
if (ret == -ENOENT)
return 0;
if (ret < 0) {
printf("Error: %d: Can't get %s gpio!\n", ret, bus->name);
return ret;
}
if (dm_gpio_is_valid(&priv->cs_gpio)) {
dm_gpio_set_dir_flags(&priv->cs_gpio,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
}
debug("%s: used external gpio for CS management\n", __func__);
#endif
return 0;
}
static int dw_spi_ofdata_to_platdata(struct udevice *bus)
@ -144,19 +164,19 @@ static int dw_spi_ofdata_to_platdata(struct udevice *bus)
debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs,
plat->frequency);
return 0;
return request_gpio_cs(bus);
}
static inline void spi_enable_chip(struct dw_spi_priv *priv, int enable)
{
dw_writel(priv, DW_SPI_SSIENR, (enable ? 1 : 0));
dw_write(priv, DW_SPI_SSIENR, (enable ? 1 : 0));
}
/* Restart the controller, disable all interrupts, clean rx fifo */
static void spi_hw_init(struct dw_spi_priv *priv)
{
spi_enable_chip(priv, 0);
dw_writel(priv, DW_SPI_IMR, 0xff);
dw_write(priv, DW_SPI_IMR, 0xff);
spi_enable_chip(priv, 1);
/*
@ -167,13 +187,13 @@ static void spi_hw_init(struct dw_spi_priv *priv)
u32 fifo;
for (fifo = 1; fifo < 256; fifo++) {
dw_writew(priv, DW_SPI_TXFLTR, fifo);
if (fifo != dw_readw(priv, DW_SPI_TXFLTR))
dw_write(priv, DW_SPI_TXFLTR, fifo);
if (fifo != dw_read(priv, DW_SPI_TXFLTR))
break;
}
priv->fifo_len = (fifo == 1) ? 0 : fifo;
dw_writew(priv, DW_SPI_TXFLTR, 0);
dw_write(priv, DW_SPI_TXFLTR, 0);
}
debug("%s: fifo_len=%d\n", __func__, priv->fifo_len);
}
@ -242,7 +262,7 @@ static inline u32 tx_max(struct dw_spi_priv *priv)
u32 tx_left, tx_room, rxtx_gap;
tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3);
tx_room = priv->fifo_len - dw_readw(priv, DW_SPI_TXFLR);
tx_room = priv->fifo_len - dw_read(priv, DW_SPI_TXFLR);
/*
* Another concern is about the tx/rx mismatch, we
@ -263,7 +283,7 @@ static inline u32 rx_max(struct dw_spi_priv *priv)
{
u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3);
return min_t(u32, rx_left, dw_readw(priv, DW_SPI_RXFLR));
return min_t(u32, rx_left, dw_read(priv, DW_SPI_RXFLR));
}
static void dw_writer(struct dw_spi_priv *priv)
@ -279,34 +299,22 @@ static void dw_writer(struct dw_spi_priv *priv)
else
txw = *(u16 *)(priv->tx);
}
dw_writew(priv, DW_SPI_DR, txw);
dw_write(priv, DW_SPI_DR, txw);
debug("%s: tx=0x%02x\n", __func__, txw);
priv->tx += priv->bits_per_word >> 3;
}
}
static int dw_reader(struct dw_spi_priv *priv)
static void dw_reader(struct dw_spi_priv *priv)
{
unsigned start = get_timer(0);
u32 max;
u32 max = rx_max(priv);
u16 rxw;
/* Wait for rx data to be ready */
while (rx_max(priv) == 0) {
if (get_timer(start) > RX_TIMEOUT)
return -ETIMEDOUT;
}
max = rx_max(priv);
while (max--) {
rxw = dw_readw(priv, DW_SPI_DR);
rxw = dw_read(priv, DW_SPI_DR);
debug("%s: rx=0x%02x\n", __func__, rxw);
/*
* Care about rx only if the transfer's original "rx" is
* not null
*/
/* Care about rx if the transfer's original "rx" is not null */
if (priv->rx_end - priv->len) {
if (priv->bits_per_word == 8)
*(u8 *)(priv->rx) = rxw;
@ -315,24 +323,30 @@ static int dw_reader(struct dw_spi_priv *priv)
}
priv->rx += priv->bits_per_word >> 3;
}
return 0;
}
static int poll_transfer(struct dw_spi_priv *priv)
{
int ret;
do {
dw_writer(priv);
ret = dw_reader(priv);
if (ret < 0)
return ret;
dw_reader(priv);
} while (priv->rx_end > priv->rx);
return 0;
}
static void external_cs_manage(struct udevice *dev, bool on)
{
#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_SPL_BUILD)
struct dw_spi_priv *priv = dev_get_priv(dev->parent);
if (!dm_gpio_is_valid(&priv->cs_gpio))
return;
dm_gpio_set_value(&priv->cs_gpio, on ? 1 : 0);
#endif
}
static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
@ -342,6 +356,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
u8 *rx = din;
int ret = 0;
u32 cr0 = 0;
u32 val;
u32 cs;
/* spi core configured to do 8 bit transfers */
@ -350,6 +365,10 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
return -1;
}
/* Start the transaction if necessary. */
if (flags & SPI_XFER_BEGIN)
external_cs_manage(dev, false);
cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) |
(priv->mode << SPI_MODE_OFFSET) |
(priv->tmode << SPI_TMOD_OFFSET);
@ -359,7 +378,11 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
else if (rx)
priv->tmode = SPI_TMOD_RO;
else
priv->tmode = SPI_TMOD_TO;
/*
* In transmit only mode (SPI_TMOD_TO) input FIFO never gets
* any data which breaks our logic in poll_transfer() above.
*/
priv->tmode = SPI_TMOD_TR;
cr0 &= ~SPI_TMOD_MASK;
cr0 |= (priv->tmode << SPI_TMOD_OFFSET);
@ -377,8 +400,8 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
debug("%s: cr0=%08x\n", __func__, cr0);
/* Reprogram cr0 only if changed */
if (dw_readw(priv, DW_SPI_CTRL0) != cr0)
dw_writew(priv, DW_SPI_CTRL0, cr0);
if (dw_read(priv, DW_SPI_CTRL0) != cr0)
dw_write(priv, DW_SPI_CTRL0, cr0);
/*
* Configure the desired SS (slave select 0...3) in the controller
@ -386,7 +409,7 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
* automatically. So no cs_activate() etc is needed in this driver.
*/
cs = spi_chip_select(dev);
dw_writel(priv, DW_SPI_SER, 1 << cs);
dw_write(priv, DW_SPI_SER, 1 << cs);
/* Enable controller after writing control registers */
spi_enable_chip(priv, 1);
@ -394,6 +417,23 @@ static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
/* Start transfer in a polling loop */
ret = poll_transfer(priv);
/*
* Wait for current transmit operation to complete.
* Otherwise if some data still exists in Tx FIFO it can be
* silently flushed, i.e. dropped on disabling of the controller,
* which happens when writing 0 to DW_SPI_SSIENR which happens
* in the beginning of new transfer.
*/
if (readl_poll_timeout(priv->regs + DW_SPI_SR, val,
!(val & SR_TF_EMPT) || (val & SR_BUSY),
RX_TIMEOUT * 1000)) {
ret = -ETIMEDOUT;
}
/* Stop the transaction if necessary */
if (flags & SPI_XFER_END)
external_cs_manage(dev, true);
return ret;
}
@ -412,7 +452,7 @@ static int dw_spi_set_speed(struct udevice *bus, uint speed)
/* clk_div doesn't support odd number */
clk_div = priv->bus_clk_rate / speed;
clk_div = (clk_div + 1) & 0xfffe;
dw_writel(priv, DW_SPI_BAUDR, clk_div);
dw_write(priv, DW_SPI_BAUDR, clk_div);
/* Enable controller after writing control registers */
spi_enable_chip(priv, 1);

@ -630,8 +630,10 @@ static int omap3_spi_probe(struct udevice *dev)
(struct omap2_mcspi_platform_config*)dev_get_driver_data(dev);
priv->regs = (struct mcspi *)(devfdt_get_addr(dev) + data->regs_offset);
priv->pin_dir = fdtdec_get_uint(blob, node, "ti,pindir-d0-out-d1-in",
MCSPI_PINDIR_D0_IN_D1_OUT);
if (fdtdec_get_bool(blob, node, "ti,pindir-d0-out-d1-in"))
priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN;
else
priv->pin_dir = MCSPI_PINDIR_D0_IN_D1_OUT;
priv->wordlen = SPI_DEFAULT_WORDLEN;
return 0;
}

Loading…
Cancel
Save