u-boot/drivers/spi/tegra20_slink.c
Svyatoslav Ryhel 09ca4d8028 spi: tegra20_slink: accept any word length
Original t20 slink could work with commands only
fully divisible by 8. This patch removes such
restriction, so commands of any bitlength now
can be passed and processed.

Tested-by: Andreas Westman Dorcsak <hedmoo@yahoo.com> # ASUS TF600T T30
Tested-by: Svyatoslav Ryhel <clamor95@gmail.com> # LG P895 T30
Tested-by: Thierry Reding <treding@nvidia.com> # T30 and T124
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Tom <twarren@nvidia.com>
2023-02-23 12:55:36 -07:00

383 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* NVIDIA Tegra SPI-SLINK controller
*
* Copyright (c) 2010-2013 NVIDIA Corporation
*/
#include <common.h>
#include <dm.h>
#include <log.h>
#include <time.h>
#include <asm/global_data.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch-tegra/clk_rst.h>
#include <spi.h>
#include <fdtdec.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include "tegra_spi.h"
DECLARE_GLOBAL_DATA_PTR;
/* COMMAND */
#define SLINK_CMD_ENB BIT(31)
#define SLINK_CMD_GO BIT(30)
#define SLINK_CMD_M_S BIT(28)
#define SLINK_CMD_IDLE_SCLK_DRIVE_LOW (0 << 24)
#define SLINK_CMD_IDLE_SCLK_DRIVE_HIGH BIT(24)
#define SLINK_CMD_IDLE_SCLK_PULL_LOW (2 << 24)
#define SLINK_CMD_IDLE_SCLK_PULL_HIGH (3 << 24)
#define SLINK_CMD_IDLE_SCLK_MASK (3 << 24)
#define SLINK_CMD_CK_SDA BIT(21)
#define SLINK_CMD_CS_POL BIT(13)
#define SLINK_CMD_CS_VAL BIT(12)
#define SLINK_CMD_CS_SOFT BIT(11)
#define SLINK_CMD_BIT_LENGTH BIT(4)
#define SLINK_CMD_BIT_LENGTH_MASK GENMASK(4, 0)
/* COMMAND2 */
#define SLINK_CMD2_TXEN BIT(30)
#define SLINK_CMD2_RXEN BIT(31)
#define SLINK_CMD2_SS_EN BIT(18)
#define SLINK_CMD2_SS_EN_SHIFT 18
#define SLINK_CMD2_SS_EN_MASK GENMASK(19, 18)
#define SLINK_CMD2_CS_ACTIVE_BETWEEN BIT(17)
/* STATUS */
#define SLINK_STAT_BSY BIT(31)
#define SLINK_STAT_RDY BIT(30)
#define SLINK_STAT_ERR BIT(29)
#define SLINK_STAT_RXF_FLUSH BIT(27)
#define SLINK_STAT_TXF_FLUSH BIT(26)
#define SLINK_STAT_RXF_OVF BIT(25)
#define SLINK_STAT_TXF_UNR BIT(24)
#define SLINK_STAT_RXF_EMPTY BIT(23)
#define SLINK_STAT_RXF_FULL BIT(22)
#define SLINK_STAT_TXF_EMPTY BIT(21)
#define SLINK_STAT_TXF_FULL BIT(20)
#define SLINK_STAT_TXF_OVF BIT(19)
#define SLINK_STAT_RXF_UNR BIT(18)
#define SLINK_STAT_CUR_BLKCNT BIT(15)
/* STATUS2 */
#define SLINK_STAT2_RXF_FULL_CNT BIT(16)
#define SLINK_STAT2_TXF_FULL_CNT BIT(0)
#define SPI_TIMEOUT 1000
#define TEGRA_SPI_MAX_FREQ 52000000
struct spi_regs {
u32 command; /* SLINK_COMMAND_0 register */
u32 command2; /* SLINK_COMMAND2_0 reg */
u32 status; /* SLINK_STATUS_0 register */
u32 reserved; /* Reserved offset 0C */
u32 mas_data; /* SLINK_MAS_DATA_0 reg */
u32 slav_data; /* SLINK_SLAVE_DATA_0 reg */
u32 dma_ctl; /* SLINK_DMA_CTL_0 register */
u32 status2; /* SLINK_STATUS2_0 reg */
u32 rsvd[56]; /* 0x20 to 0xFF reserved */
u32 tx_fifo; /* SLINK_TX_FIFO_0 reg off 100h */
u32 rsvd2[31]; /* 0x104 to 0x17F reserved */
u32 rx_fifo; /* SLINK_RX_FIFO_0 reg off 180h */
};
struct tegra30_spi_priv {
struct spi_regs *regs;
unsigned int freq;
unsigned int mode;
int periph_id;
int valid;
int last_transaction_us;
};
struct tegra_spi_slave {
struct spi_slave slave;
struct tegra30_spi_priv *ctrl;
};
static int tegra30_spi_of_to_plat(struct udevice *bus)
{
struct tegra_spi_plat *plat = dev_get_plat(bus);
const void *blob = gd->fdt_blob;
int node = dev_of_offset(bus);
plat->base = dev_read_addr(bus);
plat->periph_id = clock_decode_periph_id(bus);
if (plat->periph_id == PERIPH_ID_NONE) {
debug("%s: could not decode periph id %d\n", __func__,
plat->periph_id);
return -FDT_ERR_NOTFOUND;
}
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
__func__, plat->base, plat->periph_id, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int tegra30_spi_probe(struct udevice *bus)
{
struct tegra_spi_plat *plat = dev_get_plat(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
priv->regs = (struct spi_regs *)plat->base;
priv->last_transaction_us = timer_get_us();
priv->freq = plat->frequency;
priv->periph_id = plat->periph_id;
/* Change SPI clock to correct frequency, PLLP_OUT0 source */
clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
priv->freq);
return 0;
}
static int tegra30_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra30_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg;
/* Change SPI clock to correct frequency, PLLP_OUT0 source */
clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
priv->freq);
/* Clear stale status here */
reg = SLINK_STAT_RDY | SLINK_STAT_RXF_FLUSH | SLINK_STAT_TXF_FLUSH | \
SLINK_STAT_RXF_UNR | SLINK_STAT_TXF_OVF;
writel(reg, &regs->status);
debug("%s: STATUS = %08x\n", __func__, readl(&regs->status));
/* Set master mode and sw controlled CS */
reg = readl(&regs->command);
reg |= SLINK_CMD_M_S | SLINK_CMD_CS_SOFT;
writel(reg, &regs->command);
debug("%s: COMMAND = %08x\n", __func__, readl(&regs->command));
return 0;
}
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_plat *pdata = dev_get_plat(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
/* If it's too soon to do another transaction, wait */
if (pdata->deactivate_delay_us &&
priv->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - priv->last_transaction_us;
if (delay_us < pdata->deactivate_delay_us)
udelay(pdata->deactivate_delay_us - delay_us);
}
/* CS is negated on Tegra, so drive a 1 to get a 0 */
setbits_le32(&priv->regs->command, SLINK_CMD_CS_VAL);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_plat *pdata = dev_get_plat(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
/* CS is negated on Tegra, so drive a 0 to get a 1 */
clrbits_le32(&priv->regs->command, SLINK_CMD_CS_VAL);
/* Remember time of this transaction so we can honour the bus delay */
if (pdata->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
}
static int tegra30_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *data_out, void *data_in,
unsigned long flags)
{
struct udevice *bus = dev->parent;
struct tegra30_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg, tmpdout, tmpdin = 0;
const u8 *dout = data_out;
u8 *din = data_in;
int num_bytes, overflow;
int ret = 0;
debug("%s: slave %u:%u dout %p din %p bitlen %u\n",
__func__, dev_seq(bus), spi_chip_select(dev), dout, din, bitlen);
num_bytes = DIV_ROUND_UP(bitlen, 8);
overflow = bitlen % 8;
reg = readl(&regs->status);
writel(reg, &regs->status); /* Clear all SPI events via R/W */
debug("%s entry: STATUS = %08x\n", __func__, reg);
reg = readl(&regs->status2);
writel(reg, &regs->status2); /* Clear all STATUS2 events via R/W */
debug("%s entry: STATUS2 = %08x\n", __func__, reg);
debug("%s entry: COMMAND = %08x\n", __func__, readl(&regs->command));
clrsetbits_le32(&regs->command2, SLINK_CMD2_SS_EN_MASK,
SLINK_CMD2_TXEN | SLINK_CMD2_RXEN |
(spi_chip_select(dev) << SLINK_CMD2_SS_EN_SHIFT));
debug("%s entry: COMMAND2 = %08x\n", __func__, readl(&regs->command2));
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
/* handle data in 32-bit chunks */
while (num_bytes > 0) {
int bytes;
int is_read = 0;
int tm, i;
tmpdout = 0;
bytes = (num_bytes > 4) ? 4 : num_bytes;
if (dout != NULL) {
for (i = 0; i < bytes; ++i)
tmpdout = (tmpdout << 8) | dout[i];
dout += bytes;
}
num_bytes -= bytes;
if (overflow && !num_bytes)
clrsetbits_le32(&regs->command, SLINK_CMD_BIT_LENGTH_MASK,
(bytes - 1) * 8 + overflow - 1);
else
clrsetbits_le32(&regs->command, SLINK_CMD_BIT_LENGTH_MASK,
bytes * 8 - 1);
writel(tmpdout, &regs->tx_fifo);
setbits_le32(&regs->command, SLINK_CMD_GO);
/*
* Wait for SPI transmit FIFO to empty, or to time out.
* The RX FIFO status will be read and cleared last
*/
for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) {
u32 status;
status = readl(&regs->status);
/* We can exit when we've had both RX and TX activity */
if (is_read && (status & SLINK_STAT_TXF_EMPTY))
break;
if ((status & (SLINK_STAT_BSY | SLINK_STAT_RDY)) !=
SLINK_STAT_RDY)
tm++;
else if (!(status & SLINK_STAT_RXF_EMPTY)) {
tmpdin = readl(&regs->rx_fifo);
is_read = 1;
/* swap bytes read in */
if (din != NULL) {
for (i = bytes - 1; i >= 0; --i) {
din[i] = tmpdin & 0xff;
tmpdin >>= 8;
}
din += bytes;
}
}
}
if (tm >= SPI_TIMEOUT)
ret = tm;
/* clear ACK RDY, etc. bits */
writel(readl(&regs->status), &regs->status);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("%s: transfer ended. Value=%08x, status = %08x\n",
__func__, tmpdin, readl(&regs->status));
if (ret) {
printf("%s: timeout during SPI transfer, tm %d\n",
__func__, ret);
return -1;
}
return 0;
}
static int tegra30_spi_set_speed(struct udevice *bus, uint speed)
{
struct tegra_spi_plat *plat = dev_get_plat(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
if (speed > plat->frequency)
speed = plat->frequency;
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int tegra30_spi_set_mode(struct udevice *bus, uint mode)
{
struct tegra30_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg;
reg = readl(&regs->command);
/* Set CPOL and CPHA */
reg &= ~(SLINK_CMD_IDLE_SCLK_MASK | SLINK_CMD_CK_SDA);
if (mode & SPI_CPHA)
reg |= SLINK_CMD_CK_SDA;
if (mode & SPI_CPOL)
reg |= SLINK_CMD_IDLE_SCLK_DRIVE_HIGH;
else
reg |= SLINK_CMD_IDLE_SCLK_DRIVE_LOW;
writel(reg, &regs->command);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops tegra30_spi_ops = {
.claim_bus = tegra30_spi_claim_bus,
.xfer = tegra30_spi_xfer,
.set_speed = tegra30_spi_set_speed,
.set_mode = tegra30_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id tegra30_spi_ids[] = {
{ .compatible = "nvidia,tegra20-slink" },
{ }
};
U_BOOT_DRIVER(tegra30_spi) = {
.name = "tegra20_slink",
.id = UCLASS_SPI,
.of_match = tegra30_spi_ids,
.ops = &tegra30_spi_ops,
.of_to_plat = tegra30_spi_of_to_plat,
.plat_auto = sizeof(struct tegra_spi_plat),
.priv_auto = sizeof(struct tegra30_spi_priv),
.probe = tegra30_spi_probe,
};