u-boot/drivers/spi/npcm_pspi.c
Tom Rini d678a59d2d Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay
Ethernet"' I failed to notice that b4 noticed it was based on next and
so took that as the base commit and merged that part of next to master.

This reverts commit c8ffd1356d42223cbb8c86280a083cc3c93e6426, reversing
changes made to 2ee6f3a5f7550de3599faef9704e166e5dcace35.

Reported-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Tom Rini <trini@konsulko.com>
2024-05-19 08:16:36 -06:00

241 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021 Nuvoton Technology.
*/
#include <common.h>
#include <dm.h>
#include <spi.h>
#include <clk.h>
#include <reset.h>
#include <asm/gpio.h>
#include <linux/iopoll.h>
#define MAX_DIV 127
/* Register offsets */
#define PSPI_DATA 0
#define PSPI_CTL1 2
#define PSPI_STAT 4
/* PSPI_CTL1 fields */
#define PSPI_CTL1_SPIEN BIT(0)
#define PSPI_CTL1_SCM BIT(7)
#define PSPI_CTL1_SCIDL BIT(8)
#define PSPI_CTL1_SCDV_MASK GENMASK(15, 9)
#define PSPI_CTL1_SCDV_SHIFT 9
/* PSPI_STAT fields */
#define PSPI_STAT_BSY BIT(0)
#define PSPI_STAT_RBF BIT(1)
struct npcm_pspi_priv {
void __iomem *base;
struct clk clk;
struct gpio_desc cs_gpio;
u32 max_hz;
};
static inline void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
dm_gpio_set_value(&priv->cs_gpio, 1);
}
static inline void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
dm_gpio_set_value(&priv->cs_gpio, 0);
}
static inline void npcm_pspi_enable(struct npcm_pspi_priv *priv)
{
u16 val;
val = readw(priv->base + PSPI_CTL1);
val |= PSPI_CTL1_SPIEN;
writew(val, priv->base + PSPI_CTL1);
}
static inline void npcm_pspi_disable(struct npcm_pspi_priv *priv)
{
u16 val;
val = readw(priv->base + PSPI_CTL1);
val &= ~PSPI_CTL1_SPIEN;
writew(val, priv->base + PSPI_CTL1);
}
static int npcm_pspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct npcm_pspi_priv *priv = dev_get_priv(bus);
void __iomem *base = priv->base;
const u8 *tx = dout;
u8 *rx = din;
u32 bytes = bitlen / 8;
u8 tmp;
u32 val;
int i, ret = 0;
npcm_pspi_enable(priv);
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
for (i = 0; i < bytes; i++) {
/* Making sure we can write */
ret = readb_poll_timeout(base + PSPI_STAT, val,
!(val & PSPI_STAT_BSY),
1000000);
if (ret < 0)
break;
if (tx)
writeb(*tx++, base + PSPI_DATA);
else
writeb(0, base + PSPI_DATA);
/* Wait till write completed */
ret = readb_poll_timeout(base + PSPI_STAT, val,
!(val & PSPI_STAT_BSY),
1000000);
if (ret < 0)
break;
/* Wait till read buffer full */
ret = readb_poll_timeout(base + PSPI_STAT, val,
(val & PSPI_STAT_RBF),
1000000);
if (ret < 0)
break;
tmp = readb(base + PSPI_DATA);
if (rx)
*rx++ = tmp;
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("npcm_pspi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
dev->parent->name, dev->name, *(uint *)tx, *(uint *)rx, bitlen);
npcm_pspi_disable(priv);
return ret;
}
static int npcm_pspi_set_speed(struct udevice *bus, uint speed)
{
struct npcm_pspi_priv *priv = dev_get_priv(bus);
ulong apb_clock;
u32 divisor;
u16 val;
apb_clock = clk_get_rate(&priv->clk);
if (!apb_clock)
return -EINVAL;
if (speed > priv->max_hz)
speed = priv->max_hz;
divisor = DIV_ROUND_CLOSEST(apb_clock, (2 * speed)) - 1;
if (divisor > MAX_DIV)
divisor = MAX_DIV;
val = readw(priv->base + PSPI_CTL1);
val &= ~PSPI_CTL1_SCDV_MASK;
val |= divisor << PSPI_CTL1_SCDV_SHIFT;
writew(val, priv->base + PSPI_CTL1);
debug("%s: apb_clock=%lu speed=%d divisor=%u\n",
__func__, apb_clock, speed, divisor);
return 0;
}
static int npcm_pspi_set_mode(struct udevice *bus, uint mode)
{
struct npcm_pspi_priv *priv = dev_get_priv(bus);
u16 pspi_mode, val;
switch (mode & (SPI_CPOL | SPI_CPHA)) {
case SPI_MODE_0:
pspi_mode = 0;
break;
case SPI_MODE_1:
pspi_mode = PSPI_CTL1_SCM;
break;
case SPI_MODE_2:
pspi_mode = PSPI_CTL1_SCIDL;
break;
case SPI_MODE_3:
pspi_mode = PSPI_CTL1_SCIDL | PSPI_CTL1_SCM;
break;
default:
break;
}
val = readw(priv->base + PSPI_CTL1);
val &= ~(PSPI_CTL1_SCIDL | PSPI_CTL1_SCM);
val |= pspi_mode;
writew(val, priv->base + PSPI_CTL1);
debug("%s: mode=%u\n", __func__, mode);
return 0;
}
static int npcm_pspi_probe(struct udevice *bus)
{
struct npcm_pspi_priv *priv = dev_get_priv(bus);
int node = dev_of_offset(bus);
struct reset_ctl reset;
int ret;
ret = clk_get_by_index(bus, 0, &priv->clk);
if (ret < 0)
return ret;
priv->base = dev_read_addr_ptr(bus);
priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 1000000);
gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0,
&priv->cs_gpio, GPIOD_IS_OUT| GPIOD_ACTIVE_LOW);
/* Reset HW */
ret = reset_get_by_index(bus, 0, &reset);
if (!ret) {
reset_assert(&reset);
udelay(5);
reset_deassert(&reset);
}
return 0;
}
static const struct dm_spi_ops npcm_pspi_ops = {
.xfer = npcm_pspi_xfer,
.set_speed = npcm_pspi_set_speed,
.set_mode = npcm_pspi_set_mode,
};
static const struct udevice_id npcm_pspi_ids[] = {
{ .compatible = "nuvoton,npcm845-pspi"},
{ .compatible = "nuvoton,npcm750-pspi"},
{ }
};
U_BOOT_DRIVER(npcm_pspi) = {
.name = "npcm_pspi",
.id = UCLASS_SPI,
.of_match = npcm_pspi_ids,
.ops = &npcm_pspi_ops,
.priv_auto = sizeof(struct npcm_pspi_priv),
.probe = npcm_pspi_probe,
};