| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- // 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, 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);
- 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);
- 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,
- };
|