mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-01 08:59:33 +00:00
83d290c56f
When U-Boot started using SPDX tags we were among the early adopters and there weren't a lot of other examples to borrow from. So we picked the area of the file that usually had a full license text and replaced it with an appropriate SPDX-License-Identifier: entry. Since then, the Linux Kernel has adopted SPDX tags and they place it as the very first line in a file (except where shebangs are used, then it's second line) and with slightly different comment styles than us. In part due to community overlap, in part due to better tag visibility and in part for other minor reasons, switch over to that style. This commit changes all instances where we have a single declared license in the tag as both the before and after are identical in tag contents. There's also a few places where I found we did not have a tag and have introduced one. Signed-off-by: Tom Rini <trini@konsulko.com>
227 lines
4.9 KiB
C
227 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 <spi.h>
|
|
#include <dm.h>
|
|
#include <div64.h>
|
|
#include <errno.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 = devfdt_get_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 -ENODEV;
|
|
|
|
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_alloc_size = sizeof(struct ath79_spi_priv),
|
|
.probe = ath79_spi_probe,
|
|
};
|