mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
spi: nuvoton: add NPCM PSPI controller driver
Add Nuvoton NPCM BMC Peripheral SPI controller driver. NPCM750 include two general-purpose SPI interface. Signed-off-by: Jim Liu <JJLIU0@nuvoton.com> Reviewed-by: Jagan Teki <jagan@amarulasolutions.com>
This commit is contained in:
parent
8bc87a4c55
commit
c1dc8473fa
3 changed files with 232 additions and 0 deletions
|
@ -321,6 +321,11 @@ config NPCM_FIU_SPI
|
|||
This enables support for the Flash Interface Unit SPI controller
|
||||
in master mode.
|
||||
|
||||
config NPCM_PSPI
|
||||
bool "PSPI driver for Nuvoton NPCM SoC"
|
||||
help
|
||||
PSPI driver for NPCM SoC
|
||||
|
||||
config NXP_FSPI
|
||||
bool "NXP FlexSPI driver"
|
||||
depends on SPI_MEM
|
||||
|
|
|
@ -52,6 +52,7 @@ obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
|
|||
obj-$(CONFIG_MXC_SPI) += mxc_spi.o
|
||||
obj-$(CONFIG_MXS_SPI) += mxs_spi.o
|
||||
obj-$(CONFIG_NPCM_FIU_SPI) += npcm_fiu_spi.o
|
||||
obj-$(CONFIG_NPCM_PSPI) += npcm_pspi.o
|
||||
obj-$(CONFIG_NXP_FSPI) += nxp_fspi.o
|
||||
obj-$(CONFIG_ATCSPI200_SPI) += atcspi200_spi.o
|
||||
obj-$(CONFIG_OCTEON_SPI) += octeon_spi.o
|
||||
|
|
226
drivers/spi/npcm_pspi.c
Normal file
226
drivers/spi/npcm_pspi.c
Normal file
|
@ -0,0 +1,226 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2021 Nuvoton Technology.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <spi.h>
|
||||
#include <clk.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, 0);
|
||||
}
|
||||
|
||||
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, 1);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
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", 0);
|
||||
gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0,
|
||||
&priv->cs_gpio, GPIOD_IS_OUT);
|
||||
|
||||
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,
|
||||
};
|
Loading…
Reference in a new issue