u-boot-brain/drivers/spi/ath79_spi.c
Simon Glass 41575d8e4c dm: treewide: Rename auto_alloc_size members to be shorter
This construct is quite long-winded. In earlier days it made some sense
since auto-allocation was a strange concept. But with driver model now
used pretty universally, we can shorten this to 'auto'. This reduces
verbosity and makes it easier to read.

Coincidentally it also ensures that every declaration is on one line,
thus making dtoc's job easier.

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

230 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_platdata(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,
};