diff mbox series

[v2,16/21] spi: add spi controller support for MediaTek MT7620 SoC

Message ID b00966c6ce87242aa01c5324be7733e0eee8016f.1604047991.git.weijie.gao@mediatek.com
State Superseded
Delegated to: Tom Rini
Headers show
Series Add support for MediaTek MT7620 SoC | expand

Commit Message

Weijie Gao (高惟杰) Oct. 30, 2020, 9:35 a.m. UTC
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.gao@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

Comments

Jagan Teki Nov. 2, 2020, 6 a.m. UTC | #1
On Fri, Oct 30, 2020 at 3:05 PM Weijie Gao <weijie.gao@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.gao@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.gao@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.
Weijie Gao (高惟杰) Nov. 9, 2020, 2:10 a.m. UTC | #2
On Mon, 2020-11-02 at 11:30 +0530, Jagan Teki wrote:
> On Fri, Oct 30, 2020 at 3:05 PM Weijie Gao <weijie.gao@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.gao@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.gao@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.

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.
diff mbox series

Patch

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.gao@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;
+
+	/* 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);
+
+	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);
+
+	return ret;
+}
+
+static int mt7620_spi_probe(struct udevice *dev)
+{
+	struct mt7620_spi *ms = dev_get_priv(dev);
+	struct clk clk;
+	int ret;
+
+	ms->regs = dev_remap_addr(dev);
+	if (!ms->regs)
+		return -EINVAL;
+
+	ms->m[0] = ms->regs + MT7620_SPI_MASTER1_OFF;
+	ms->m[1] = ms->regs + MT7620_SPI_MASTER2_OFF;
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret < 0) {
+		printf("Please provide a clock!\n");
+		return ret;
+	}
+
+	clk_enable(&clk);
+
+	ms->sys_freq = clk_get_rate(&clk);
+	if (!ms->sys_freq) {
+		printf("Please provide a valid bus clock!\n");
+		return -EINVAL;
+	}
+
+	writel(ARB_EN, ms->regs + SPI_ARB);
+
+	return 0;
+}
+
+static const struct dm_spi_ops mt7620_spi_ops = {
+	.set_mode = mt7620_spi_set_mode,
+	.set_speed = mt7620_spi_set_speed,
+	.xfer = mt7620_spi_xfer,
+};
+
+static const struct udevice_id mt7620_spi_ids[] = {
+	{ .compatible = "mediatek,mt7620-spi" },
+	{ }
+};
+
+U_BOOT_DRIVER(mt7620_spi) = {
+	.name = "mt7620_spi",
+	.id = UCLASS_SPI,
+	.of_match = mt7620_spi_ids,
+	.ops = &mt7620_spi_ops,
+	.priv_auto_alloc_size = sizeof(struct mt7620_spi),
+	.probe = mt7620_spi_probe,
+};