From mboxrd@z Thu Jan 1 00:00:00 1970 From: Weijie Gao Date: Mon, 9 Nov 2020 10:10:33 +0800 Subject: [PATCH v2 16/21] spi: add spi controller support for MediaTek MT7620 SoC In-Reply-To: References: Message-ID: <1604887833.18736.38.camel@mcddlt001> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de On Mon, 2020-11-02 at 11:30 +0530, Jagan Teki wrote: > On Fri, Oct 30, 2020 at 3:05 PM Weijie Gao 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 > > --- > > 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 > > + * > > + * Generic SPI driver for MediaTek MT7620 SoC > > + */ > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#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. ok. > > > + > > + /* 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. ok. > > > + > > + 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.