u-boot/drivers/spi/ath79_spi.c
Simon Glass caa4daa2ae dm: treewide: Rename 'platdata' variables to just 'plat'
We use 'priv' for private data but often use 'platdata' for platform data.
We can't really use 'pdata' since that is ambiguous (it could mean private
or platform data).

Rename some of the latter variables to end with 'plat' for consistency.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-12-13 16:51:08 -07:00

229 lines
4.9 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
*/
#include <common.h>
#include <clock_legacy.h>
#include <spi.h>
#include <dm.h>
#include <div64.h>
#include <errno.h>
#include <time.h>
#include <asm/io.h>
#include <asm/addrspace.h>
#include <asm/types.h>
#include <dm/pinctrl.h>
#include <mach/ar71xx_regs.h>
/* CLOCK_DIVIDER = 3 (SPI clock = 200 / 8 ~ 25 MHz) */
#define ATH79_SPI_CLK_DIV(x) (((x) >> 1) - 1)
#define ATH79_SPI_RRW_DELAY_FACTOR 12000
#define ATH79_SPI_MHZ (1000 * 1000)
struct ath79_spi_priv {
void __iomem *regs;
u32 rrw_delay;
};
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct ath79_spi_priv *priv = dev_get_priv(bus);
writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct ath79_spi_priv *priv = dev_get_priv(bus);
writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC);
writel(0, priv->regs + AR71XX_SPI_REG_FS);
}
static int ath79_spi_claim_bus(struct udevice *dev)
{
return 0;
}
static int ath79_spi_release_bus(struct udevice *dev)
{
return 0;
}
static int ath79_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct ath79_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave = dev_get_parent_plat(dev);
u8 *rx = din;
const u8 *tx = dout;
u8 curbyte, curbitlen, restbits;
u32 bytes = bitlen / 8;
u32 out, in;
u64 tick;
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
restbits = (bitlen % 8);
if (restbits)
bytes++;
out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs));
while (bytes > 0) {
bytes--;
curbyte = 0;
if (tx)
curbyte = *tx++;
if (restbits && !bytes) {
curbitlen = restbits;
curbyte <<= 8 - restbits;
} else {
curbitlen = 8;
}
for (curbyte <<= (8 - curbitlen); curbitlen; curbitlen--) {
if (curbyte & 0x80)
out |= AR71XX_SPI_IOC_DO;
else
out &= ~(AR71XX_SPI_IOC_DO);
writel(out, priv->regs + AR71XX_SPI_REG_IOC);
/* delay for low level */
if (priv->rrw_delay) {
tick = get_ticks() + priv->rrw_delay;
while (get_ticks() < tick)
/*NOP*/;
}
writel(out | AR71XX_SPI_IOC_CLK,
priv->regs + AR71XX_SPI_REG_IOC);
/* delay for high level */
if (priv->rrw_delay) {
tick = get_ticks() + priv->rrw_delay;
while (get_ticks() < tick)
/*NOP*/;
}
curbyte <<= 1;
}
if (!bytes)
writel(out, priv->regs + AR71XX_SPI_REG_IOC);
in = readl(priv->regs + AR71XX_SPI_REG_RDS);
if (rx) {
if (restbits && !bytes)
*rx++ = (in << (8 - restbits));
else
*rx++ = in;
}
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
return 0;
}
static int ath79_spi_set_speed(struct udevice *bus, uint speed)
{
struct ath79_spi_priv *priv = dev_get_priv(bus);
u32 val, div = 0;
u64 time;
if (speed)
div = get_bus_freq(0) / speed;
if (div > 63)
div = 63;
if (div < 5)
div = 5;
/* calculate delay */
time = get_tbclk();
do_div(time, speed / 2);
val = get_bus_freq(0) / ATH79_SPI_MHZ;
val = ATH79_SPI_RRW_DELAY_FACTOR / val;
if (time > val)
priv->rrw_delay = time - val + 1;
else
priv->rrw_delay = 0;
writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
clrsetbits_be32(priv->regs + AR71XX_SPI_REG_CTRL,
AR71XX_SPI_CTRL_DIV_MASK,
ATH79_SPI_CLK_DIV(div));
writel(0, priv->regs + AR71XX_SPI_REG_FS);
return 0;
}
static int ath79_spi_set_mode(struct udevice *bus, uint mode)
{
return 0;
}
static int ath79_spi_probe(struct udevice *bus)
{
struct ath79_spi_priv *priv = dev_get_priv(bus);
fdt_addr_t addr;
addr = dev_read_addr(bus);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = map_physmem(addr,
AR71XX_SPI_SIZE,
MAP_NOCACHE);
/* Init SPI Hardware, disable remap, set clock */
writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
writel(AR71XX_SPI_CTRL_RD | ATH79_SPI_CLK_DIV(8),
priv->regs + AR71XX_SPI_REG_CTRL);
writel(0, priv->regs + AR71XX_SPI_REG_FS);
return 0;
}
static int ath79_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
{
/* Always allow activity on CS 0/1/2 */
if (cs >= 3)
return -EINVAL;
return 0;
}
static const struct dm_spi_ops ath79_spi_ops = {
.claim_bus = ath79_spi_claim_bus,
.release_bus = ath79_spi_release_bus,
.xfer = ath79_spi_xfer,
.set_speed = ath79_spi_set_speed,
.set_mode = ath79_spi_set_mode,
.cs_info = ath79_cs_info,
};
static const struct udevice_id ath79_spi_ids[] = {
{ .compatible = "qca,ar7100-spi" },
{}
};
U_BOOT_DRIVER(ath79_spi) = {
.name = "ath79_spi",
.id = UCLASS_SPI,
.of_match = ath79_spi_ids,
.ops = &ath79_spi_ops,
.priv_auto = sizeof(struct ath79_spi_priv),
.probe = ath79_spi_probe,
};