]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
spi: microchip: rename driver file and internal identifiers
authorPrajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>
Fri, 14 Nov 2025 10:45:43 +0000 (10:45 +0000)
committerMark Brown <broonie@kernel.org>
Fri, 14 Nov 2025 13:54:41 +0000 (13:54 +0000)
The spi-microchip-core.c driver provides support for the Microchip
PolarFire SoC (MPFS) "hard" SPI controller. It was originally named
"core" with the expectation that it might also cover Microchip's
CoreSPI "soft" IP, but that never materialized.

The CoreSPI IP cannot be supported by this driver because its register
layout differs substantially from the MPFS SPI controller. In practice
most of the code would need to be replaced to handle those differences
so keeping the drivers separate is the simpler approach.

The file and internal symbols are renamed to reflect MPFS support and
to free up "spi-microchip-core.c" for CoreSPI driver.

Fixes: 9ac8d17694b6 ("spi: add support for microchip fpga spi controllers")
Signed-off-by: Prajna Rajendra Kumar <prajna.rajendrakumar@microchip.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Link: https://patch.msgid.link/20251114104545.284765-2-prajna.rajendrakumar@microchip.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi-microchip-core.c [deleted file]
drivers/spi/spi-mpfs.c [new file with mode: 0644]

index 4d8f00c850c1453845680192007342221e80da86..d53798036076d80835fd95d4e199a9af9ea75731 100644 (file)
@@ -706,15 +706,6 @@ config SPI_MESON_SPIFC
          This enables master mode support for the SPIFC (SPI flash
          controller) available in Amlogic Meson SoCs.
 
-config SPI_MICROCHIP_CORE
-       tristate "Microchip FPGA SPI controllers"
-       depends on SPI_MASTER
-       help
-         This enables the SPI driver for Microchip FPGA SPI controllers.
-         Say Y or M here if you want to use the "hard" controllers on
-         PolarFire SoC.
-         If built as a module, it will be called spi-microchip-core.
-
 config SPI_MICROCHIP_CORE_QSPI
        tristate "Microchip FPGA QSPI controllers"
        depends on SPI_MASTER
@@ -871,6 +862,16 @@ config SPI_PL022
          controller. If you have an embedded system with an AMBA(R)
          bus and a PL022 controller, say Y or M here.
 
+config SPI_POLARFIRE_SOC
+
+       tristate "Microchip FPGA SPI controllers"
+       depends on SPI_MASTER && ARCH_MICROCHIP
+       help
+         This enables the SPI driver for Microchip FPGA SPI controllers.
+         Say Y or M here if you want to use the "hard" controllers on
+         PolarFire SoC.
+         If built as a module, it will be called spi-mpfs.
+
 config SPI_PPC4xx
        tristate "PPC4xx SPI Controller"
        depends on PPC32 && 4xx
index 8ff74a13faaa88399723f9e944f9198076c3e543..1f7c06a3091d92f801c9c9a4cd0c553378c1d792 100644 (file)
@@ -86,7 +86,6 @@ obj-$(CONFIG_SPI_LOONGSON_PLATFORM)   += spi-loongson-plat.o
 obj-$(CONFIG_SPI_LP8841_RTC)           += spi-lp8841-rtc.o
 obj-$(CONFIG_SPI_MESON_SPICC)          += spi-meson-spicc.o
 obj-$(CONFIG_SPI_MESON_SPIFC)          += spi-meson-spifc.o
-obj-$(CONFIG_SPI_MICROCHIP_CORE)       += spi-microchip-core.o
 obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI)  += spi-microchip-core-qspi.o
 obj-$(CONFIG_SPI_MPC512x_PSC)          += spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)          += spi-mpc52xx-psc.o
@@ -97,6 +96,7 @@ obj-$(CONFIG_SPI_MTK_NOR)             += spi-mtk-nor.o
 obj-$(CONFIG_SPI_MTK_SNFI)             += spi-mtk-snfi.o
 obj-$(CONFIG_SPI_MXIC)                 += spi-mxic.o
 obj-$(CONFIG_SPI_MXS)                  += spi-mxs.o
+obj-$(CONFIG_SPI_POLARFIRE_SOC)                += spi-mpfs.o
 obj-$(CONFIG_SPI_WPCM_FIU)             += spi-wpcm-fiu.o
 obj-$(CONFIG_SPI_NPCM_FIU)             += spi-npcm-fiu.o
 obj-$(CONFIG_SPI_NPCM_PSPI)            += spi-npcm-pspi.o
diff --git a/drivers/spi/spi-microchip-core.c b/drivers/spi/spi-microchip-core.c
deleted file mode 100644 (file)
index 9128b86..0000000
+++ /dev/null
@@ -1,625 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0)
-/*
- * Microchip CoreSPI SPI controller driver
- *
- * Copyright (c) 2018-2022 Microchip Technology Inc. and its subsidiaries
- *
- * Author: Daire McNamara <daire.mcnamara@microchip.com>
- * Author: Conor Dooley <conor.dooley@microchip.com>
- *
- */
-
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/spi/spi.h>
-
-#define MAX_LEN                                (0xffff)
-#define MAX_CS                         (1)
-#define DEFAULT_FRAMESIZE              (8)
-#define FIFO_DEPTH                     (32)
-#define CLK_GEN_MODE1_MAX              (255)
-#define CLK_GEN_MODE0_MAX              (15)
-#define CLK_GEN_MIN                    (0)
-#define MODE_X_MASK_SHIFT              (24)
-
-#define CONTROL_ENABLE                 BIT(0)
-#define CONTROL_MASTER                 BIT(1)
-#define CONTROL_RX_DATA_INT            BIT(4)
-#define CONTROL_TX_DATA_INT            BIT(5)
-#define CONTROL_RX_OVER_INT            BIT(6)
-#define CONTROL_TX_UNDER_INT           BIT(7)
-#define CONTROL_SPO                    BIT(24)
-#define CONTROL_SPH                    BIT(25)
-#define CONTROL_SPS                    BIT(26)
-#define CONTROL_FRAMEURUN              BIT(27)
-#define CONTROL_CLKMODE                        BIT(28)
-#define CONTROL_BIGFIFO                        BIT(29)
-#define CONTROL_OENOFF                 BIT(30)
-#define CONTROL_RESET                  BIT(31)
-
-#define CONTROL_MODE_MASK              GENMASK(3, 2)
-#define  MOTOROLA_MODE                 (0)
-#define CONTROL_FRAMECNT_MASK          GENMASK(23, 8)
-#define CONTROL_FRAMECNT_SHIFT         (8)
-
-#define STATUS_ACTIVE                  BIT(14)
-#define STATUS_SSEL                    BIT(13)
-#define STATUS_FRAMESTART              BIT(12)
-#define STATUS_TXFIFO_EMPTY_NEXT_READ  BIT(11)
-#define STATUS_TXFIFO_EMPTY            BIT(10)
-#define STATUS_TXFIFO_FULL_NEXT_WRITE  BIT(9)
-#define STATUS_TXFIFO_FULL             BIT(8)
-#define STATUS_RXFIFO_EMPTY_NEXT_READ  BIT(7)
-#define STATUS_RXFIFO_EMPTY            BIT(6)
-#define STATUS_RXFIFO_FULL_NEXT_WRITE  BIT(5)
-#define STATUS_RXFIFO_FULL             BIT(4)
-#define STATUS_TX_UNDERRUN             BIT(3)
-#define STATUS_RX_OVERFLOW             BIT(2)
-#define STATUS_RXDAT_RXED              BIT(1)
-#define STATUS_TXDAT_SENT              BIT(0)
-
-#define INT_TXDONE                     BIT(0)
-#define INT_RXRDY                      BIT(1)
-#define INT_RX_CHANNEL_OVERFLOW                BIT(2)
-#define INT_TX_CHANNEL_UNDERRUN                BIT(3)
-
-#define INT_ENABLE_MASK (CONTROL_RX_OVER_INT | CONTROL_TX_UNDER_INT)
-
-#define REG_CONTROL            (0x00)
-#define REG_FRAME_SIZE         (0x04)
-#define  FRAME_SIZE_MASK       GENMASK(5, 0)
-#define REG_STATUS             (0x08)
-#define REG_INT_CLEAR          (0x0c)
-#define REG_RX_DATA            (0x10)
-#define REG_TX_DATA            (0x14)
-#define REG_CLK_GEN            (0x18)
-#define REG_SLAVE_SELECT       (0x1c)
-#define  SSEL_MASK             GENMASK(7, 0)
-#define  SSEL_DIRECT           BIT(8)
-#define  SSELOUT_SHIFT         9
-#define  SSELOUT               BIT(SSELOUT_SHIFT)
-#define REG_MIS                        (0x20)
-#define REG_RIS                        (0x24)
-#define REG_CONTROL2           (0x28)
-#define REG_COMMAND            (0x2c)
-#define  COMMAND_CLRFRAMECNT   BIT(4)
-#define  COMMAND_TXFIFORST             BIT(3)
-#define  COMMAND_RXFIFORST             BIT(2)
-#define REG_PKTSIZE            (0x30)
-#define REG_CMD_SIZE           (0x34)
-#define REG_HWSTATUS           (0x38)
-#define REG_STAT8              (0x3c)
-#define REG_CTRL2              (0x48)
-#define REG_FRAMESUP           (0x50)
-
-struct mchp_corespi {
-       void __iomem *regs;
-       struct clk *clk;
-       const u8 *tx_buf;
-       u8 *rx_buf;
-       u32 clk_gen; /* divider for spi output clock generated by the controller */
-       u32 clk_mode;
-       u32 pending_slave_select;
-       int irq;
-       int tx_len;
-       int rx_len;
-       int n_bytes;
-};
-
-static inline u32 mchp_corespi_read(struct mchp_corespi *spi, unsigned int reg)
-{
-       return readl(spi->regs + reg);
-}
-
-static inline void mchp_corespi_write(struct mchp_corespi *spi, unsigned int reg, u32 val)
-{
-       writel(val, spi->regs + reg);
-}
-
-static inline void mchp_corespi_disable(struct mchp_corespi *spi)
-{
-       u32 control = mchp_corespi_read(spi, REG_CONTROL);
-
-       control &= ~CONTROL_ENABLE;
-
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static inline void mchp_corespi_read_fifo(struct mchp_corespi *spi, int fifo_max)
-{
-       for (int i = 0; i < fifo_max; i++) {
-               u32 data;
-
-               while (mchp_corespi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
-                       ;
-
-               data = mchp_corespi_read(spi, REG_RX_DATA);
-
-               spi->rx_len -= spi->n_bytes;
-
-               if (!spi->rx_buf)
-                       continue;
-
-               if (spi->n_bytes == 4)
-                       *((u32 *)spi->rx_buf) = data;
-               else if (spi->n_bytes == 2)
-                       *((u16 *)spi->rx_buf) = data;
-               else
-                       *spi->rx_buf = data;
-
-               spi->rx_buf += spi->n_bytes;
-       }
-}
-
-static void mchp_corespi_enable_ints(struct mchp_corespi *spi)
-{
-       u32 control = mchp_corespi_read(spi, REG_CONTROL);
-
-       control |= INT_ENABLE_MASK;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static void mchp_corespi_disable_ints(struct mchp_corespi *spi)
-{
-       u32 control = mchp_corespi_read(spi, REG_CONTROL);
-
-       control &= ~INT_ENABLE_MASK;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static inline void mchp_corespi_set_xfer_size(struct mchp_corespi *spi, int len)
-{
-       u32 control;
-       u32 lenpart;
-       u32 frames = mchp_corespi_read(spi, REG_FRAMESUP);
-
-       /*
-        * Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking
-        * a shortcut requires an explicit clear.
-        */
-       if (frames == len) {
-               mchp_corespi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
-               return;
-       }
-
-       /*
-        * The lower 16 bits of the frame count are stored in the control reg
-        * for legacy reasons, but the upper 16 written to a different register:
-        * FRAMESUP. While both the upper and lower bits can be *READ* from the
-        * FRAMESUP register, writing to the lower 16 bits is (supposedly) a NOP.
-        *
-        * The driver used to disable the controller while modifying the frame
-        * count, and mask off the lower 16 bits of len while writing to
-        * FRAMES_UP. When the driver was changed to disable the controller as
-        * infrequently as possible, it was discovered that the logic of
-        * lenpart = len & 0xffff_0000
-        * write(REG_FRAMESUP, lenpart)
-        * would actually write zeros into the lower 16 bits on an mpfs250t-es,
-        * despite documentation stating these bits were read-only.
-        * Writing len unmasked into FRAMES_UP ensures those bits aren't zeroed
-        * on an mpfs250t-es and will be a NOP for the lower 16 bits on hardware
-        * that matches the documentation.
-        */
-       lenpart = len & 0xffff;
-       control = mchp_corespi_read(spi, REG_CONTROL);
-       control &= ~CONTROL_FRAMECNT_MASK;
-       control |= lenpart << CONTROL_FRAMECNT_SHIFT;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-       mchp_corespi_write(spi, REG_FRAMESUP, len);
-}
-
-static inline void mchp_corespi_write_fifo(struct mchp_corespi *spi, int fifo_max)
-{
-       int i = 0;
-
-       mchp_corespi_set_xfer_size(spi, fifo_max);
-
-       while ((i < fifo_max) && !(mchp_corespi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
-               u32 word;
-
-               if (spi->n_bytes == 4)
-                       word = spi->tx_buf ? *((u32 *)spi->tx_buf) : 0xaa;
-               else if (spi->n_bytes == 2)
-                       word = spi->tx_buf ? *((u16 *)spi->tx_buf) : 0xaa;
-               else
-                       word = spi->tx_buf ? *spi->tx_buf : 0xaa;
-
-               mchp_corespi_write(spi, REG_TX_DATA, word);
-               if (spi->tx_buf)
-                       spi->tx_buf += spi->n_bytes;
-               i++;
-       }
-
-       spi->tx_len -= i * spi->n_bytes;
-}
-
-static inline void mchp_corespi_set_framesize(struct mchp_corespi *spi, int bt)
-{
-       u32 frame_size = mchp_corespi_read(spi, REG_FRAME_SIZE);
-       u32 control;
-
-       if ((frame_size & FRAME_SIZE_MASK) == bt)
-               return;
-
-       /*
-        * Disable the SPI controller. Writes to the frame size have
-        * no effect when the controller is enabled.
-        */
-       control = mchp_corespi_read(spi, REG_CONTROL);
-       control &= ~CONTROL_ENABLE;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-
-       mchp_corespi_write(spi, REG_FRAME_SIZE, bt);
-
-       control |= CONTROL_ENABLE;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static void mchp_corespi_set_cs(struct spi_device *spi, bool disable)
-{
-       u32 reg;
-       struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
-
-       reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
-       reg &= ~BIT(spi_get_chipselect(spi, 0));
-       reg |= !disable << spi_get_chipselect(spi, 0);
-       corespi->pending_slave_select = reg;
-
-       /*
-        * Only deassert chip select immediately. Writing to some registers
-        * requires the controller to be disabled, which results in the
-        * output pins being tristated and can cause the SCLK and MOSI lines
-        * to transition. Therefore asserting the chip select is deferred
-        * until just before writing to the TX FIFO, to ensure the device
-        * doesn't see any spurious clock transitions whilst CS is enabled.
-        */
-       if (((spi->mode & SPI_CS_HIGH) == 0) == disable)
-               mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
-}
-
-static int mchp_corespi_setup(struct spi_device *spi)
-{
-       struct mchp_corespi *corespi = spi_controller_get_devdata(spi->controller);
-       u32 reg;
-
-       if (spi_is_csgpiod(spi))
-               return 0;
-
-       /*
-        * Active high targets need to be specifically set to their inactive
-        * states during probe by adding them to the "control group" & thus
-        * driving their select line low.
-        */
-       if (spi->mode & SPI_CS_HIGH) {
-               reg = mchp_corespi_read(corespi, REG_SLAVE_SELECT);
-               reg |= BIT(spi_get_chipselect(spi, 0));
-               corespi->pending_slave_select = reg;
-               mchp_corespi_write(corespi, REG_SLAVE_SELECT, reg);
-       }
-       return 0;
-}
-
-static void mchp_corespi_init(struct spi_controller *host, struct mchp_corespi *spi)
-{
-       unsigned long clk_hz;
-       u32 control = mchp_corespi_read(spi, REG_CONTROL);
-
-       control &= ~CONTROL_ENABLE;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-
-       control |= CONTROL_MASTER;
-       control &= ~CONTROL_MODE_MASK;
-       control |= MOTOROLA_MODE;
-
-       /*
-        * The controller must be configured so that it doesn't remove Chip
-        * Select until the entire message has been transferred, even if at
-        * some points TX FIFO becomes empty.
-        *
-        * BIGFIFO mode is also enabled, which sets the fifo depth to 32 frames
-        * for the 8 bit transfers that this driver uses.
-        */
-       control |= CONTROL_SPS | CONTROL_BIGFIFO;
-
-       mchp_corespi_write(spi, REG_CONTROL, control);
-
-       mchp_corespi_set_framesize(spi, DEFAULT_FRAMESIZE);
-
-       /* max. possible spi clock rate is the apb clock rate */
-       clk_hz = clk_get_rate(spi->clk);
-       host->max_speed_hz = clk_hz;
-
-       mchp_corespi_enable_ints(spi);
-
-       /*
-        * It is required to enable direct mode, otherwise control over the chip
-        * select is relinquished to the hardware. SSELOUT is enabled too so we
-        * can deal with active high targets.
-        */
-       spi->pending_slave_select = SSELOUT | SSEL_DIRECT;
-       mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
-
-       control = mchp_corespi_read(spi, REG_CONTROL);
-
-       control &= ~CONTROL_RESET;
-       control |= CONTROL_ENABLE;
-
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static inline void mchp_corespi_set_clk_gen(struct mchp_corespi *spi)
-{
-       u32 control;
-
-       control = mchp_corespi_read(spi, REG_CONTROL);
-       if (spi->clk_mode)
-               control |= CONTROL_CLKMODE;
-       else
-               control &= ~CONTROL_CLKMODE;
-
-       mchp_corespi_write(spi, REG_CLK_GEN, spi->clk_gen);
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static inline void mchp_corespi_set_mode(struct mchp_corespi *spi, unsigned int mode)
-{
-       u32 mode_val;
-       u32 control = mchp_corespi_read(spi, REG_CONTROL);
-
-       switch (mode & SPI_MODE_X_MASK) {
-       case SPI_MODE_0:
-               mode_val = 0;
-               break;
-       case SPI_MODE_1:
-               mode_val = CONTROL_SPH;
-               break;
-       case SPI_MODE_2:
-               mode_val = CONTROL_SPO;
-               break;
-       case SPI_MODE_3:
-               mode_val = CONTROL_SPH | CONTROL_SPO;
-               break;
-       }
-
-       /*
-        * Disable the SPI controller. Writes to the frame protocol have
-        * no effect when the controller is enabled.
-        */
-
-       control &= ~CONTROL_ENABLE;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-
-       control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT);
-       control |= mode_val;
-
-       mchp_corespi_write(spi, REG_CONTROL, control);
-
-       control |= CONTROL_ENABLE;
-       mchp_corespi_write(spi, REG_CONTROL, control);
-}
-
-static irqreturn_t mchp_corespi_interrupt(int irq, void *dev_id)
-{
-       struct spi_controller *host = dev_id;
-       struct mchp_corespi *spi = spi_controller_get_devdata(host);
-       u32 intfield = mchp_corespi_read(spi, REG_MIS) & 0xf;
-       bool finalise = false;
-
-       /* Interrupt line may be shared and not for us at all */
-       if (intfield == 0)
-               return IRQ_NONE;
-
-       if (intfield & INT_RX_CHANNEL_OVERFLOW) {
-               mchp_corespi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
-               finalise = true;
-               dev_err(&host->dev,
-                       "%s: RX OVERFLOW: rxlen: %d, txlen: %d\n", __func__,
-                       spi->rx_len, spi->tx_len);
-       }
-
-       if (intfield & INT_TX_CHANNEL_UNDERRUN) {
-               mchp_corespi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
-               finalise = true;
-               dev_err(&host->dev,
-                       "%s: TX UNDERFLOW: rxlen: %d, txlen: %d\n", __func__,
-                       spi->rx_len, spi->tx_len);
-       }
-
-       if (finalise)
-               spi_finalize_current_transfer(host);
-
-       return IRQ_HANDLED;
-}
-
-static int mchp_corespi_calculate_clkgen(struct mchp_corespi *spi,
-                                        unsigned long target_hz)
-{
-       unsigned long clk_hz, spi_hz, clk_gen;
-
-       clk_hz = clk_get_rate(spi->clk);
-       if (!clk_hz)
-               return -EINVAL;
-       spi_hz = min(target_hz, clk_hz);
-
-       /*
-        * There are two possible clock modes for the controller generated
-        * clock's division ratio:
-        * CLK_MODE = 0: 1 / (2^(CLK_GEN + 1)) where CLK_GEN = 0 to 15.
-        * CLK_MODE = 1: 1 / (2 * CLK_GEN + 1) where CLK_GEN = 0 to 255.
-        * First try mode 1, fall back to 0 and if we have tried both modes and
-        * we /still/ can't get a good setting, we then throw the toys out of
-        * the pram and give up
-        * clk_gen is the register name for the clock divider on MPFS.
-        */
-       clk_gen = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1;
-       if (clk_gen > CLK_GEN_MODE1_MAX || clk_gen <= CLK_GEN_MIN) {
-               clk_gen = DIV_ROUND_UP(clk_hz, spi_hz);
-               clk_gen = fls(clk_gen) - 1;
-
-               if (clk_gen > CLK_GEN_MODE0_MAX)
-                       return -EINVAL;
-
-               spi->clk_mode = 0;
-       } else {
-               spi->clk_mode = 1;
-       }
-
-       spi->clk_gen = clk_gen;
-       return 0;
-}
-
-static int mchp_corespi_transfer_one(struct spi_controller *host,
-                                    struct spi_device *spi_dev,
-                                    struct spi_transfer *xfer)
-{
-       struct mchp_corespi *spi = spi_controller_get_devdata(host);
-       int ret;
-
-       ret = mchp_corespi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
-       if (ret) {
-               dev_err(&host->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz);
-               return ret;
-       }
-
-       mchp_corespi_set_clk_gen(spi);
-
-       spi->tx_buf = xfer->tx_buf;
-       spi->rx_buf = xfer->rx_buf;
-       spi->tx_len = xfer->len;
-       spi->rx_len = xfer->len;
-       spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE));
-
-       mchp_corespi_set_framesize(spi, xfer->bits_per_word);
-
-       mchp_corespi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);
-
-       mchp_corespi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
-
-       while (spi->tx_len) {
-               int fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
-
-               mchp_corespi_write_fifo(spi, fifo_max);
-               mchp_corespi_read_fifo(spi, fifo_max);
-       }
-
-       spi_finalize_current_transfer(host);
-       return 1;
-}
-
-static int mchp_corespi_prepare_message(struct spi_controller *host,
-                                       struct spi_message *msg)
-{
-       struct spi_device *spi_dev = msg->spi;
-       struct mchp_corespi *spi = spi_controller_get_devdata(host);
-
-       mchp_corespi_set_mode(spi, spi_dev->mode);
-
-       return 0;
-}
-
-static int mchp_corespi_probe(struct platform_device *pdev)
-{
-       struct spi_controller *host;
-       struct mchp_corespi *spi;
-       struct resource *res;
-       u32 num_cs;
-       int ret = 0;
-
-       host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi));
-       if (!host)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, host);
-
-       if (of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs))
-               num_cs = MAX_CS;
-
-       host->num_chipselect = num_cs;
-       host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
-       host->use_gpio_descriptors = true;
-       host->setup = mchp_corespi_setup;
-       host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
-       host->transfer_one = mchp_corespi_transfer_one;
-       host->prepare_message = mchp_corespi_prepare_message;
-       host->set_cs = mchp_corespi_set_cs;
-       host->dev.of_node = pdev->dev.of_node;
-
-       spi = spi_controller_get_devdata(host);
-
-       spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
-       if (IS_ERR(spi->regs))
-               return PTR_ERR(spi->regs);
-
-       spi->irq = platform_get_irq(pdev, 0);
-       if (spi->irq < 0)
-               return spi->irq;
-
-       ret = devm_request_irq(&pdev->dev, spi->irq, mchp_corespi_interrupt,
-                              IRQF_SHARED, dev_name(&pdev->dev), host);
-       if (ret)
-               return dev_err_probe(&pdev->dev, ret,
-                                    "could not request irq\n");
-
-       spi->clk = devm_clk_get_enabled(&pdev->dev, NULL);
-       if (IS_ERR(spi->clk))
-               return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
-                                    "could not get clk\n");
-
-       mchp_corespi_init(host, spi);
-
-       ret = devm_spi_register_controller(&pdev->dev, host);
-       if (ret) {
-               mchp_corespi_disable(spi);
-               return dev_err_probe(&pdev->dev, ret,
-                                    "unable to register host for SPI controller\n");
-       }
-
-       dev_info(&pdev->dev, "Registered SPI controller %d\n", host->bus_num);
-
-       return 0;
-}
-
-static void mchp_corespi_remove(struct platform_device *pdev)
-{
-       struct spi_controller *host  = platform_get_drvdata(pdev);
-       struct mchp_corespi *spi = spi_controller_get_devdata(host);
-
-       mchp_corespi_disable_ints(spi);
-       mchp_corespi_disable(spi);
-}
-
-#define MICROCHIP_SPI_PM_OPS (NULL)
-
-/*
- * Platform driver data structure
- */
-
-#if defined(CONFIG_OF)
-static const struct of_device_id mchp_corespi_dt_ids[] = {
-       { .compatible = "microchip,mpfs-spi" },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mchp_corespi_dt_ids);
-#endif
-
-static struct platform_driver mchp_corespi_driver = {
-       .probe = mchp_corespi_probe,
-       .driver = {
-               .name = "microchip-corespi",
-               .pm = MICROCHIP_SPI_PM_OPS,
-               .of_match_table = of_match_ptr(mchp_corespi_dt_ids),
-       },
-       .remove = mchp_corespi_remove,
-};
-module_platform_driver(mchp_corespi_driver);
-MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver");
-MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
-MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/spi/spi-mpfs.c b/drivers/spi/spi-mpfs.c
new file mode 100644 (file)
index 0000000..9a14d17
--- /dev/null
@@ -0,0 +1,626 @@
+// SPDX-License-Identifier: (GPL-2.0)
+/*
+ * Microchip CoreSPI SPI controller driver
+ *
+ * Copyright (c) 2018-2022 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Daire McNamara <daire.mcnamara@microchip.com>
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#define MAX_LEN                                (0xffff)
+#define MAX_CS                         (1)
+#define DEFAULT_FRAMESIZE              (8)
+#define FIFO_DEPTH                     (32)
+#define CLK_GEN_MODE1_MAX              (255)
+#define CLK_GEN_MODE0_MAX              (15)
+#define CLK_GEN_MIN                    (0)
+#define MODE_X_MASK_SHIFT              (24)
+
+#define CONTROL_ENABLE                 BIT(0)
+#define CONTROL_MASTER                 BIT(1)
+#define CONTROL_RX_DATA_INT            BIT(4)
+#define CONTROL_TX_DATA_INT            BIT(5)
+#define CONTROL_RX_OVER_INT            BIT(6)
+#define CONTROL_TX_UNDER_INT           BIT(7)
+#define CONTROL_SPO                    BIT(24)
+#define CONTROL_SPH                    BIT(25)
+#define CONTROL_SPS                    BIT(26)
+#define CONTROL_FRAMEURUN              BIT(27)
+#define CONTROL_CLKMODE                        BIT(28)
+#define CONTROL_BIGFIFO                        BIT(29)
+#define CONTROL_OENOFF                 BIT(30)
+#define CONTROL_RESET                  BIT(31)
+
+#define CONTROL_MODE_MASK              GENMASK(3, 2)
+#define  MOTOROLA_MODE                 (0)
+#define CONTROL_FRAMECNT_MASK          GENMASK(23, 8)
+#define CONTROL_FRAMECNT_SHIFT         (8)
+
+#define STATUS_ACTIVE                  BIT(14)
+#define STATUS_SSEL                    BIT(13)
+#define STATUS_FRAMESTART              BIT(12)
+#define STATUS_TXFIFO_EMPTY_NEXT_READ  BIT(11)
+#define STATUS_TXFIFO_EMPTY            BIT(10)
+#define STATUS_TXFIFO_FULL_NEXT_WRITE  BIT(9)
+#define STATUS_TXFIFO_FULL             BIT(8)
+#define STATUS_RXFIFO_EMPTY_NEXT_READ  BIT(7)
+#define STATUS_RXFIFO_EMPTY            BIT(6)
+#define STATUS_RXFIFO_FULL_NEXT_WRITE  BIT(5)
+#define STATUS_RXFIFO_FULL             BIT(4)
+#define STATUS_TX_UNDERRUN             BIT(3)
+#define STATUS_RX_OVERFLOW             BIT(2)
+#define STATUS_RXDAT_RXED              BIT(1)
+#define STATUS_TXDAT_SENT              BIT(0)
+
+#define INT_TXDONE                     BIT(0)
+#define INT_RXRDY                      BIT(1)
+#define INT_RX_CHANNEL_OVERFLOW                BIT(2)
+#define INT_TX_CHANNEL_UNDERRUN                BIT(3)
+
+#define INT_ENABLE_MASK (CONTROL_RX_OVER_INT | CONTROL_TX_UNDER_INT)
+
+#define REG_CONTROL            (0x00)
+#define REG_FRAME_SIZE         (0x04)
+#define  FRAME_SIZE_MASK       GENMASK(5, 0)
+#define REG_STATUS             (0x08)
+#define REG_INT_CLEAR          (0x0c)
+#define REG_RX_DATA            (0x10)
+#define REG_TX_DATA            (0x14)
+#define REG_CLK_GEN            (0x18)
+#define REG_SLAVE_SELECT       (0x1c)
+#define  SSEL_MASK             GENMASK(7, 0)
+#define  SSEL_DIRECT           BIT(8)
+#define  SSELOUT_SHIFT         9
+#define  SSELOUT               BIT(SSELOUT_SHIFT)
+#define REG_MIS                        (0x20)
+#define REG_RIS                        (0x24)
+#define REG_CONTROL2           (0x28)
+#define REG_COMMAND            (0x2c)
+#define  COMMAND_CLRFRAMECNT   BIT(4)
+#define  COMMAND_TXFIFORST             BIT(3)
+#define  COMMAND_RXFIFORST             BIT(2)
+#define REG_PKTSIZE            (0x30)
+#define REG_CMD_SIZE           (0x34)
+#define REG_HWSTATUS           (0x38)
+#define REG_STAT8              (0x3c)
+#define REG_CTRL2              (0x48)
+#define REG_FRAMESUP           (0x50)
+
+struct mpfs_spi {
+       void __iomem *regs;
+       struct clk *clk;
+       const u8 *tx_buf;
+       u8 *rx_buf;
+       u32 clk_gen; /* divider for spi output clock generated by the controller */
+       u32 clk_mode;
+       u32 pending_slave_select;
+       int irq;
+       int tx_len;
+       int rx_len;
+       int n_bytes;
+};
+
+static inline u32 mpfs_spi_read(struct mpfs_spi *spi, unsigned int reg)
+{
+       return readl(spi->regs + reg);
+}
+
+static inline void mpfs_spi_write(struct mpfs_spi *spi, unsigned int reg, u32 val)
+{
+       writel(val, spi->regs + reg);
+}
+
+static inline void mpfs_spi_disable(struct mpfs_spi *spi)
+{
+       u32 control = mpfs_spi_read(spi, REG_CONTROL);
+
+       control &= ~CONTROL_ENABLE;
+
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static inline void mpfs_spi_read_fifo(struct mpfs_spi *spi, int fifo_max)
+{
+       for (int i = 0; i < fifo_max; i++) {
+               u32 data;
+
+               while (mpfs_spi_read(spi, REG_STATUS) & STATUS_RXFIFO_EMPTY)
+                       ;
+
+               data = mpfs_spi_read(spi, REG_RX_DATA);
+
+               spi->rx_len -= spi->n_bytes;
+
+               if (!spi->rx_buf)
+                       continue;
+
+               if (spi->n_bytes == 4)
+                       *((u32 *)spi->rx_buf) = data;
+               else if (spi->n_bytes == 2)
+                       *((u16 *)spi->rx_buf) = data;
+               else
+                       *spi->rx_buf = data;
+
+               spi->rx_buf += spi->n_bytes;
+       }
+}
+
+static void mpfs_spi_enable_ints(struct mpfs_spi *spi)
+{
+       u32 control = mpfs_spi_read(spi, REG_CONTROL);
+
+       control |= INT_ENABLE_MASK;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static void mpfs_spi_disable_ints(struct mpfs_spi *spi)
+{
+       u32 control = mpfs_spi_read(spi, REG_CONTROL);
+
+       control &= ~INT_ENABLE_MASK;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static inline void mpfs_spi_set_xfer_size(struct mpfs_spi *spi, int len)
+{
+       u32 control;
+       u32 lenpart;
+       u32 frames = mpfs_spi_read(spi, REG_FRAMESUP);
+
+       /*
+        * Writing to FRAMECNT in REG_CONTROL will reset the frame count, taking
+        * a shortcut requires an explicit clear.
+        */
+       if (frames == len) {
+               mpfs_spi_write(spi, REG_COMMAND, COMMAND_CLRFRAMECNT);
+               return;
+       }
+
+       /*
+        * The lower 16 bits of the frame count are stored in the control reg
+        * for legacy reasons, but the upper 16 written to a different register:
+        * FRAMESUP. While both the upper and lower bits can be *READ* from the
+        * FRAMESUP register, writing to the lower 16 bits is (supposedly) a NOP.
+        *
+        * The driver used to disable the controller while modifying the frame
+        * count, and mask off the lower 16 bits of len while writing to
+        * FRAMES_UP. When the driver was changed to disable the controller as
+        * infrequently as possible, it was discovered that the logic of
+        * lenpart = len & 0xffff_0000
+        * write(REG_FRAMESUP, lenpart)
+        * would actually write zeros into the lower 16 bits on an mpfs250t-es,
+        * despite documentation stating these bits were read-only.
+        * Writing len unmasked into FRAMES_UP ensures those bits aren't zeroed
+        * on an mpfs250t-es and will be a NOP for the lower 16 bits on hardware
+        * that matches the documentation.
+        */
+       lenpart = len & 0xffff;
+       control = mpfs_spi_read(spi, REG_CONTROL);
+       control &= ~CONTROL_FRAMECNT_MASK;
+       control |= lenpart << CONTROL_FRAMECNT_SHIFT;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+       mpfs_spi_write(spi, REG_FRAMESUP, len);
+}
+
+static inline void mpfs_spi_write_fifo(struct mpfs_spi *spi, int fifo_max)
+{
+       int i = 0;
+
+       mpfs_spi_set_xfer_size(spi, fifo_max);
+
+       while ((i < fifo_max) && !(mpfs_spi_read(spi, REG_STATUS) & STATUS_TXFIFO_FULL)) {
+               u32 word;
+
+               if (spi->n_bytes == 4)
+                       word = spi->tx_buf ? *((u32 *)spi->tx_buf) : 0xaa;
+               else if (spi->n_bytes == 2)
+                       word = spi->tx_buf ? *((u16 *)spi->tx_buf) : 0xaa;
+               else
+                       word = spi->tx_buf ? *spi->tx_buf : 0xaa;
+
+               mpfs_spi_write(spi, REG_TX_DATA, word);
+               if (spi->tx_buf)
+                       spi->tx_buf += spi->n_bytes;
+               i++;
+       }
+
+       spi->tx_len -= i * spi->n_bytes;
+}
+
+static inline void mpfs_spi_set_framesize(struct mpfs_spi *spi, int bt)
+{
+       u32 frame_size = mpfs_spi_read(spi, REG_FRAME_SIZE);
+       u32 control;
+
+       if ((frame_size & FRAME_SIZE_MASK) == bt)
+               return;
+
+       /*
+        * Disable the SPI controller. Writes to the frame size have
+        * no effect when the controller is enabled.
+        */
+       control = mpfs_spi_read(spi, REG_CONTROL);
+       control &= ~CONTROL_ENABLE;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+
+       mpfs_spi_write(spi, REG_FRAME_SIZE, bt);
+
+       control |= CONTROL_ENABLE;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static void mpfs_spi_set_cs(struct spi_device *spi, bool disable)
+{
+       u32 reg;
+       struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller);
+
+       reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT);
+       reg &= ~BIT(spi_get_chipselect(spi, 0));
+       reg |= !disable << spi_get_chipselect(spi, 0);
+       mspi->pending_slave_select = reg;
+
+       /*
+        * Only deassert chip select immediately. Writing to some registers
+        * requires the controller to be disabled, which results in the
+        * output pins being tristated and can cause the SCLK and MOSI lines
+        * to transition. Therefore asserting the chip select is deferred
+        * until just before writing to the TX FIFO, to ensure the device
+        * doesn't see any spurious clock transitions whilst CS is enabled.
+        */
+       if (((spi->mode & SPI_CS_HIGH) == 0) == disable)
+               mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg);
+}
+
+static int mpfs_spi_setup(struct spi_device *spi)
+{
+       struct mpfs_spi *mspi = spi_controller_get_devdata(spi->controller);
+       u32 reg;
+
+       if (spi_is_csgpiod(spi))
+               return 0;
+
+       /*
+        * Active high targets need to be specifically set to their inactive
+        * states during probe by adding them to the "control group" & thus
+        * driving their select line low.
+        */
+       if (spi->mode & SPI_CS_HIGH) {
+               reg = mpfs_spi_read(mspi, REG_SLAVE_SELECT);
+               reg |= BIT(spi_get_chipselect(spi, 0));
+               mspi->pending_slave_select = reg;
+               mpfs_spi_write(mspi, REG_SLAVE_SELECT, reg);
+       }
+       return 0;
+}
+
+static void mpfs_spi_init(struct spi_controller *host, struct mpfs_spi *spi)
+{
+       unsigned long clk_hz;
+       u32 control = mpfs_spi_read(spi, REG_CONTROL);
+
+       control &= ~CONTROL_ENABLE;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+
+       control |= CONTROL_MASTER;
+       control &= ~CONTROL_MODE_MASK;
+       control |= MOTOROLA_MODE;
+
+       /*
+        * The controller must be configured so that it doesn't remove Chip
+        * Select until the entire message has been transferred, even if at
+        * some points TX FIFO becomes empty.
+        *
+        * BIGFIFO mode is also enabled, which sets the fifo depth to 32 frames
+        * for the 8 bit transfers that this driver uses.
+        */
+       control |= CONTROL_SPS | CONTROL_BIGFIFO;
+
+       mpfs_spi_write(spi, REG_CONTROL, control);
+
+       mpfs_spi_set_framesize(spi, DEFAULT_FRAMESIZE);
+
+       /* max. possible spi clock rate is the apb clock rate */
+       clk_hz = clk_get_rate(spi->clk);
+       host->max_speed_hz = clk_hz;
+
+       mpfs_spi_enable_ints(spi);
+
+       /*
+        * It is required to enable direct mode, otherwise control over the chip
+        * select is relinquished to the hardware. SSELOUT is enabled too so we
+        * can deal with active high targets.
+        */
+       spi->pending_slave_select = SSELOUT | SSEL_DIRECT;
+       mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
+
+       control = mpfs_spi_read(spi, REG_CONTROL);
+
+       control &= ~CONTROL_RESET;
+       control |= CONTROL_ENABLE;
+
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static inline void mpfs_spi_set_clk_gen(struct mpfs_spi *spi)
+{
+       u32 control;
+
+       control = mpfs_spi_read(spi, REG_CONTROL);
+       if (spi->clk_mode)
+               control |= CONTROL_CLKMODE;
+       else
+               control &= ~CONTROL_CLKMODE;
+
+       mpfs_spi_write(spi, REG_CLK_GEN, spi->clk_gen);
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static inline void mpfs_spi_set_mode(struct mpfs_spi *spi, unsigned int mode)
+{
+       u32 mode_val;
+       u32 control = mpfs_spi_read(spi, REG_CONTROL);
+
+       switch (mode & SPI_MODE_X_MASK) {
+       case SPI_MODE_0:
+               mode_val = 0;
+               break;
+       case SPI_MODE_1:
+               mode_val = CONTROL_SPH;
+               break;
+       case SPI_MODE_2:
+               mode_val = CONTROL_SPO;
+               break;
+       case SPI_MODE_3:
+               mode_val = CONTROL_SPH | CONTROL_SPO;
+               break;
+       }
+
+       /*
+        * Disable the SPI controller. Writes to the frame protocol have
+        * no effect when the controller is enabled.
+        */
+
+       control &= ~CONTROL_ENABLE;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+
+       control &= ~(SPI_MODE_X_MASK << MODE_X_MASK_SHIFT);
+       control |= mode_val;
+
+       mpfs_spi_write(spi, REG_CONTROL, control);
+
+       control |= CONTROL_ENABLE;
+       mpfs_spi_write(spi, REG_CONTROL, control);
+}
+
+static irqreturn_t mpfs_spi_interrupt(int irq, void *dev_id)
+{
+       struct spi_controller *host = dev_id;
+       struct mpfs_spi *spi = spi_controller_get_devdata(host);
+       u32 intfield = mpfs_spi_read(spi, REG_MIS) & 0xf;
+       bool finalise = false;
+
+       /* Interrupt line may be shared and not for us at all */
+       if (intfield == 0)
+               return IRQ_NONE;
+
+       if (intfield & INT_RX_CHANNEL_OVERFLOW) {
+               mpfs_spi_write(spi, REG_INT_CLEAR, INT_RX_CHANNEL_OVERFLOW);
+               finalise = true;
+               dev_err(&host->dev,
+                       "%s: RX OVERFLOW: rxlen: %d, txlen: %d\n", __func__,
+                       spi->rx_len, spi->tx_len);
+       }
+
+       if (intfield & INT_TX_CHANNEL_UNDERRUN) {
+               mpfs_spi_write(spi, REG_INT_CLEAR, INT_TX_CHANNEL_UNDERRUN);
+               finalise = true;
+               dev_err(&host->dev,
+                       "%s: TX UNDERFLOW: rxlen: %d, txlen: %d\n", __func__,
+                       spi->rx_len, spi->tx_len);
+       }
+
+       if (finalise)
+               spi_finalize_current_transfer(host);
+
+       return IRQ_HANDLED;
+}
+
+static int mpfs_spi_calculate_clkgen(struct mpfs_spi *spi,
+                                    unsigned long target_hz)
+{
+       unsigned long clk_hz, spi_hz, clk_gen;
+
+       clk_hz = clk_get_rate(spi->clk);
+       if (!clk_hz)
+               return -EINVAL;
+       spi_hz = min(target_hz, clk_hz);
+
+       /*
+        * There are two possible clock modes for the controller generated
+        * clock's division ratio:
+        * CLK_MODE = 0: 1 / (2^(CLK_GEN + 1)) where CLK_GEN = 0 to 15.
+        * CLK_MODE = 1: 1 / (2 * CLK_GEN + 1) where CLK_GEN = 0 to 255.
+        * First try mode 1, fall back to 0 and if we have tried both modes and
+        * we /still/ can't get a good setting, we then throw the toys out of
+        * the pram and give up
+        * clk_gen is the register name for the clock divider on MPFS.
+        */
+       clk_gen = DIV_ROUND_UP(clk_hz, 2 * spi_hz) - 1;
+       if (clk_gen > CLK_GEN_MODE1_MAX || clk_gen <= CLK_GEN_MIN) {
+               clk_gen = DIV_ROUND_UP(clk_hz, spi_hz);
+               clk_gen = fls(clk_gen) - 1;
+
+               if (clk_gen > CLK_GEN_MODE0_MAX)
+                       return -EINVAL;
+
+               spi->clk_mode = 0;
+       } else {
+               spi->clk_mode = 1;
+       }
+
+       spi->clk_gen = clk_gen;
+       return 0;
+}
+
+static int mpfs_spi_transfer_one(struct spi_controller *host,
+                                struct spi_device *spi_dev,
+                                struct spi_transfer *xfer)
+{
+       struct mpfs_spi *spi = spi_controller_get_devdata(host);
+       int ret;
+
+       ret = mpfs_spi_calculate_clkgen(spi, (unsigned long)xfer->speed_hz);
+       if (ret) {
+               dev_err(&host->dev, "failed to set clk_gen for target %u Hz\n", xfer->speed_hz);
+               return ret;
+       }
+
+       mpfs_spi_set_clk_gen(spi);
+
+       spi->tx_buf = xfer->tx_buf;
+       spi->rx_buf = xfer->rx_buf;
+       spi->tx_len = xfer->len;
+       spi->rx_len = xfer->len;
+       spi->n_bytes = roundup_pow_of_two(DIV_ROUND_UP(xfer->bits_per_word, BITS_PER_BYTE));
+
+       mpfs_spi_set_framesize(spi, xfer->bits_per_word);
+
+       mpfs_spi_write(spi, REG_COMMAND, COMMAND_RXFIFORST | COMMAND_TXFIFORST);
+
+       mpfs_spi_write(spi, REG_SLAVE_SELECT, spi->pending_slave_select);
+
+       while (spi->tx_len) {
+               int fifo_max = DIV_ROUND_UP(min(spi->tx_len, FIFO_DEPTH), spi->n_bytes);
+
+               mpfs_spi_write_fifo(spi, fifo_max);
+               mpfs_spi_read_fifo(spi, fifo_max);
+       }
+
+       spi_finalize_current_transfer(host);
+       return 1;
+}
+
+static int mpfs_spi_prepare_message(struct spi_controller *host,
+                                   struct spi_message *msg)
+{
+       struct spi_device *spi_dev = msg->spi;
+       struct mpfs_spi *spi = spi_controller_get_devdata(host);
+
+       mpfs_spi_set_mode(spi, spi_dev->mode);
+
+       return 0;
+}
+
+static int mpfs_spi_probe(struct platform_device *pdev)
+{
+       struct spi_controller *host;
+       struct mpfs_spi *spi;
+       struct resource *res;
+       u32 num_cs;
+       int ret = 0;
+
+       host = devm_spi_alloc_host(&pdev->dev, sizeof(*spi));
+       if (!host)
+               return dev_err_probe(&pdev->dev, -ENOMEM,
+                                    "unable to allocate host for SPI controller\n");
+
+       platform_set_drvdata(pdev, host);
+
+       if (of_property_read_u32(pdev->dev.of_node, "num-cs", &num_cs))
+               num_cs = MAX_CS;
+
+       host->num_chipselect = num_cs;
+       host->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+       host->use_gpio_descriptors = true;
+       host->setup = mpfs_spi_setup;
+       host->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 32);
+       host->transfer_one = mpfs_spi_transfer_one;
+       host->prepare_message = mpfs_spi_prepare_message;
+       host->set_cs = mpfs_spi_set_cs;
+       host->dev.of_node = pdev->dev.of_node;
+
+       spi = spi_controller_get_devdata(host);
+
+       spi->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+       if (IS_ERR(spi->regs))
+               return PTR_ERR(spi->regs);
+
+       spi->irq = platform_get_irq(pdev, 0);
+       if (spi->irq < 0)
+               return spi->irq;
+
+       ret = devm_request_irq(&pdev->dev, spi->irq, mpfs_spi_interrupt,
+                              IRQF_SHARED, dev_name(&pdev->dev), host);
+       if (ret)
+               return dev_err_probe(&pdev->dev, ret,
+                                    "could not request irq\n");
+
+       spi->clk = devm_clk_get_enabled(&pdev->dev, NULL);
+       if (IS_ERR(spi->clk))
+               return dev_err_probe(&pdev->dev, PTR_ERR(spi->clk),
+                                    "could not get clk\n");
+
+       mpfs_spi_init(host, spi);
+
+       ret = devm_spi_register_controller(&pdev->dev, host);
+       if (ret) {
+               mpfs_spi_disable(spi);
+               return dev_err_probe(&pdev->dev, ret,
+                                    "unable to register host for SPI controller\n");
+       }
+
+       dev_info(&pdev->dev, "Registered SPI controller %d\n", host->bus_num);
+
+       return 0;
+}
+
+static void mpfs_spi_remove(struct platform_device *pdev)
+{
+       struct spi_controller *host  = platform_get_drvdata(pdev);
+       struct mpfs_spi *spi = spi_controller_get_devdata(host);
+
+       mpfs_spi_disable_ints(spi);
+       mpfs_spi_disable(spi);
+}
+
+#define MICROCHIP_SPI_PM_OPS (NULL)
+
+/*
+ * Platform driver data structure
+ */
+
+#if defined(CONFIG_OF)
+static const struct of_device_id mpfs_spi_dt_ids[] = {
+       { .compatible = "microchip,mpfs-spi" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mpfs_spi_dt_ids);
+#endif
+
+static struct platform_driver mpfs_spi_driver = {
+       .probe = mpfs_spi_probe,
+       .driver = {
+               .name = "microchip-spi",
+               .pm = MICROCHIP_SPI_PM_OPS,
+               .of_match_table = of_match_ptr(mpfs_spi_dt_ids),
+       },
+       .remove = mpfs_spi_remove,
+};
+module_platform_driver(mpfs_spi_driver);
+MODULE_DESCRIPTION("Microchip coreSPI SPI controller driver");
+MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_LICENSE("GPL");