On Fri, Oct 30, 2020 at 3:05 PM Weijie Gao <weijie....@mediatek.com> wrote: > > This patch adds spi controller support for MediaTek MT7620 SoC. > > The SPI controller supports two chip selects. These two chip selects are > implemented as two separate register groups, but they share the same bus > (DI/DO/CLK), only CS pins are dedicated for each register group. > Appearently these two register groups cannot operates simulataneously so > they are implemented as one controller. > > Signed-off-by: Weijie Gao <weijie....@mediatek.com> > --- > v2 changes: none > --- > drivers/spi/Kconfig | 7 + > drivers/spi/Makefile | 1 + > drivers/spi/mt7620_spi.c | 277 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 285 insertions(+) > create mode 100644 drivers/spi/mt7620_spi.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index f7a9852565..ec50d843ef 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -202,6 +202,13 @@ config MSCC_BB_SPI > Enable MSCC bitbang SPI driver. This driver can be used on > MSCC SOCs. > > +config MT7620_SPI > + bool "MediaTek MT7620 SPI driver" > + depends on SOC_MT7620 > + help > + Enable the MT7620 SPI driver. This driver can be used to access > + generic SPI devices on MediaTek MT7620 SoC. > + > config MT7621_SPI > bool "MediaTek MT7621 SPI driver" > depends on SOC_MT7628 > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index d9b5bd9b79..bfd142d153 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -36,6 +36,7 @@ obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o > obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o > obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o > obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o > +obj-$(CONFIG_MT7620_SPI) += mt7620_spi.o > obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o > obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o > obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o > diff --git a/drivers/spi/mt7620_spi.c b/drivers/spi/mt7620_spi.c > new file mode 100644 > index 0000000000..c0b3d1d8eb > --- /dev/null > +++ b/drivers/spi/mt7620_spi.c > @@ -0,0 +1,277 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2020 MediaTek Inc. > + * > + * Author: Weijie Gao <weijie....@mediatek.com> > + * > + * Generic SPI driver for MediaTek MT7620 SoC > + */ > + > +#include <clk.h> > +#include <dm.h> > +#include <spi.h> > +#include <linux/bitops.h> > +#include <linux/iopoll.h> > +#include <linux/io.h> > +#include <linux/log2.h> > + > +#define MT7620_SPI_NUM_CS 2 > +#define MT7620_SPI_MASTER1_OFF 0x00 > +#define MT7620_SPI_MASTER2_OFF 0x40 > + > +/* SPI_STAT */ > +#define SPI_BUSY BIT(0) > + > +/* SPI_CFG */ > +#define MSB_FIRST BIT(8) > +#define SPI_CLK_POL BIT(6) > +#define RX_CLK_EDGE BIT(5) > +#define TX_CLK_EDGE BIT(4) > +#define SPI_CLK_S 0 > +#define SPI_CLK_M GENMASK(2, 0) > + > +/* SPI_CTL */ > +#define START_WR BIT(2) > +#define START_RD BIT(1) > +#define SPI_HIGH BIT(0) > + > +#define SPI_ARB 0xf0 > +#define ARB_EN BIT(31) > + > +struct mt7620_spi_master_regs { > + u32 stat; > + u32 reserved0[3]; > + u32 cfg; > + u32 ctl; > + u32 reserved1[2]; > + u32 data; > +}; > + > +struct mt7620_spi { > + void __iomem *regs; > + struct mt7620_spi_master_regs *m[MT7620_SPI_NUM_CS]; > + unsigned int sys_freq; > + u32 wait_us; > + uint mode; > + uint speed; > +}; > + > +static void mt7620_spi_master_setup(struct mt7620_spi *ms, int cs) > +{ > + u32 rate, prescale, freq, tmo, cfg; > + > + /* Calculate the clock divsior */ > + rate = DIV_ROUND_UP(ms->sys_freq, ms->speed); > + rate = roundup_pow_of_two(rate); > + > + prescale = ilog2(rate / 2); > + if (prescale > 6) > + prescale = 6; > + > + /* Calculate the real clock, and usecs for one byte transaction */ > + freq = ms->sys_freq >> (prescale + 1); > + tmo = DIV_ROUND_UP(8 * 1000000, freq); > + > + /* 10 times tolerance plus 100us */ > + ms->wait_us = 10 * tmo + 100;
Replace the above magic numbers with meaningful macros. > + > + /* set SPI_CFG */ > + cfg = prescale << SPI_CLK_S; > + > + switch (ms->mode & (SPI_CPOL | SPI_CPHA)) { > + case SPI_MODE_0: > + cfg |= TX_CLK_EDGE; > + break; > + case SPI_MODE_1: > + cfg |= RX_CLK_EDGE; > + break; > + case SPI_MODE_2: > + cfg |= SPI_CLK_POL | RX_CLK_EDGE; > + break; > + case SPI_MODE_3: > + cfg |= SPI_CLK_POL | TX_CLK_EDGE; > + break; > + } > + > + if (!(ms->mode & SPI_LSB_FIRST)) > + cfg |= MSB_FIRST; > + > + writel(cfg, &ms->m[cs]->cfg); > + > + writel(SPI_HIGH, &ms->m[cs]->ctl); > +} > + > +static void mt7620_spi_set_cs(struct mt7620_spi *ms, int cs, int enable) > +{ > + if (enable) > + mt7620_spi_master_setup(ms, cs); > + > + if (ms->mode & SPI_CS_HIGH) > + enable = !enable; > + > + if (enable) > + clrbits_32(&ms->m[cs]->ctl, SPI_HIGH); > + else > + setbits_32(&ms->m[cs]->ctl, SPI_HIGH); > +} > + > +static int mt7620_spi_set_mode(struct udevice *bus, uint mode) > +{ > + struct mt7620_spi *ms = dev_get_priv(bus); > + > + ms->mode = mode; > + > + /* Mode 0 is buggy. Force to use mode 3 */ > + if ((mode & SPI_MODE_3) == SPI_MODE_0) > + ms->mode |= SPI_MODE_3; > + > + return 0; > +} > + > +static int mt7620_spi_set_speed(struct udevice *bus, uint speed) > +{ > + struct mt7620_spi *ms = dev_get_priv(bus); > + > + ms->speed = speed; > + > + return 0; > +} > + > +static inline int mt7620_spi_busy_poll(struct mt7620_spi *ms, int cs) > +{ > + u32 val; > + > + return readl_poll_timeout(&ms->m[cs]->stat, val, !(val & SPI_BUSY), > + ms->wait_us); > +} > + > +static int mt7620_spi_read(struct mt7620_spi *ms, int cs, u8 *buf, size_t > len) > +{ > + int ret; > + > + while (len) { > + setbits_32(&ms->m[cs]->ctl, START_RD); > + > + ret = mt7620_spi_busy_poll(ms, cs); > + if (ret) { > + pr_err("SPI read transaction timeout\n"); > + return ret; > + } > + > + *buf++ = (u8)readl(&ms->m[cs]->data); > + > + len--; > + } > + > + return 0; > +} > + > +static int mt7620_spi_write(struct mt7620_spi *ms, int cs, const u8 *buf, > + size_t len) > +{ > + int ret; > + > + while (len) { > + writel(*buf++, &ms->m[cs]->data); > + setbits_32(&ms->m[cs]->ctl, START_WR); > + > + ret = mt7620_spi_busy_poll(ms, cs); > + if (ret) { > + pr_err("SPI write transaction timeout\n"); > + return ret; > + } > + > + len--; > + } > + > + return 0; > +} > + > +static int mt7620_spi_xfer(struct udevice *dev, unsigned int bitlen, > + const void *dout, void *din, unsigned long flags) > +{ > + struct udevice *bus = dev->parent; > + struct mt7620_spi *ms = dev_get_priv(bus); > + int total_size = bitlen >> 3; > + int cs, ret = 0; > + > + /* > + * This driver only supports half-duplex, so complain and bail out > + * upon full-duplex messages > + */ > + if (dout && din) { > + pr_err("Only half-duplex SPI transfer supported\n"); > + return -EIO; > + } > + > + cs = spi_chip_select(dev); > + if (cs < 0 || cs >= MT7620_SPI_NUM_CS) { > + pr_err("Invalid chip select %d\n", cs); > + return -EINVAL; > + } > + > + if (flags & SPI_XFER_BEGIN) > + mt7620_spi_set_cs(ms, cs, 1); use boolean type for last argument. > + > + if (din) > + ret = mt7620_spi_read(ms, cs, din, total_size); > + else if (dout) > + ret = mt7620_spi_write(ms, cs, dout, total_size); > + > + if (flags & SPI_XFER_END) > + mt7620_spi_set_cs(ms, cs, 0); same as above.