spi: spi_amd: Add PCI-based driver for AMD HID2 SPI controller
authorRaju Rangoju <Raju.Rangoju@amd.com>
Wed, 2 Apr 2025 12:15:14 +0000 (17:45 +0530)
committerMark Brown <broonie@kernel.org>
Sun, 6 Apr 2025 22:26:10 +0000 (23:26 +0100)
Register a new driver(spi_amd_pci) for the HID2 SPI controller using the
PCI ID of the LPC bridge device.

Add a new common probe function in spi_amd driver to encapsulate the
code required for registering the controller driver. This function will be
utilized by both the existing ACPI driver and the newly introduced
PCI-based driver for the HID2 SPI controller. The MMIO register base
address of the HID2 SPI controller can be obtained from the PCI LPC bridge
registers.

By implementing these changes, the DMA buffer will be correctly associated
with the LPC bridge device, preventing IO_PAGE_FAULT caused by IOMMU when
the LPC bridge attempts DMA operations on addresses owned by the HID2
SPI controller.

Co-developed-by: Krishnamoorthi M <krishnamoorthi.m@amd.com>
Signed-off-by: Krishnamoorthi M <krishnamoorthi.m@amd.com>
Co-developed-by: Akshata MukundShetty <akshata.mukundshetty@amd.com>
Signed-off-by: Akshata MukundShetty <akshata.mukundshetty@amd.com>
Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com>
Link: https://patch.msgid.link/20250402121514.2941334-1-Raju.Rangoju@amd.com
Signed-off-by: Mark Brown <broonie@kernel.org>
MAINTAINERS
drivers/spi/Makefile
drivers/spi/spi-amd-pci.c [new file with mode: 0644]
drivers/spi/spi-amd.c
drivers/spi/spi-amd.h [new file with mode: 0644]

index 96b82704950184bd71623ff41fc4df31e4c7fe87..cda27131bb1bffd9b2de42e86d70782965290fa5 100644 (file)
@@ -1216,7 +1216,9 @@ AMD SPI DRIVER
 M:     Raju Rangoju <Raju.Rangoju@amd.com>
 L:     linux-spi@vger.kernel.org
 S:     Supported
+F:     drivers/spi/spi-amd-pci.c
 F:     drivers/spi/spi-amd.c
+F:     drivers/spi/spi-amd.h
 
 AMD XDNA DRIVER
 M:     Min Ma <min.ma@amd.com>
index c3a1a47b3bf47b06eb74aaaf9c26295e010ca43a..4ea89f6fc531625060255ecff237470927e1f041 100644 (file)
@@ -162,7 +162,7 @@ obj-$(CONFIG_SPI_XLP)                       += spi-xlp.o
 obj-$(CONFIG_SPI_XTENSA_XTFPGA)                += spi-xtensa-xtfpga.o
 obj-$(CONFIG_SPI_ZYNQ_QSPI)            += spi-zynq-qspi.o
 obj-$(CONFIG_SPI_ZYNQMP_GQSPI)         += spi-zynqmp-gqspi.o
-obj-$(CONFIG_SPI_AMD)                  += spi-amd.o
+obj-$(CONFIG_SPI_AMD)                  += spi-amd.o spi-amd-pci.o
 
 # SPI slave protocol handlers
 obj-$(CONFIG_SPI_SLAVE_TIME)           += spi-slave-time.o
diff --git a/drivers/spi/spi-amd-pci.c b/drivers/spi/spi-amd-pci.c
new file mode 100644 (file)
index 0000000..e1ecab7
--- /dev/null
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AMD SPI controller driver
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Authors: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ *          Akshata MukundShetty <akshata.mukundshetty@amd.com>
+ */
+
+#include <linux/init.h>
+#include <linux/spi/spi.h>
+#include <linux/pci.h>
+
+#include "spi-amd.h"
+
+#define AMD_PCI_DEVICE_ID_LPC_BRIDGE           0x1682
+#define AMD_PCI_LPC_SPI_BASE_ADDR_REG          0xA0
+#define AMD_SPI_BASE_ADDR_MASK                 ~0xFF
+#define AMD_HID2_PCI_BAR_OFFSET                        0x00002000
+#define AMD_HID2_MEM_SIZE                      0x200
+
+static struct pci_device_id pci_spi_ids[] = {
+       { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_PCI_DEVICE_ID_LPC_BRIDGE) },
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, pci_spi_ids);
+
+static int amd_spi_pci_probe(struct pci_dev *pdev,
+                            const struct pci_device_id *id)
+{
+       struct device *dev = &pdev->dev;
+       struct spi_controller *host;
+       struct amd_spi *amd_spi;
+       u32 io_base_addr;
+
+       /* Allocate storage for host and driver private data */
+       host = devm_spi_alloc_host(dev, sizeof(struct amd_spi));
+       if (!host)
+               return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n");
+
+       amd_spi = spi_controller_get_devdata(host);
+
+       pci_read_config_dword(pdev, AMD_PCI_LPC_SPI_BASE_ADDR_REG, &io_base_addr);
+       io_base_addr = (io_base_addr & AMD_SPI_BASE_ADDR_MASK) + AMD_HID2_PCI_BAR_OFFSET;
+       amd_spi->io_remap_addr = devm_ioremap(dev, io_base_addr, AMD_HID2_MEM_SIZE);
+
+       if (IS_ERR(amd_spi->io_remap_addr))
+               return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr),
+                               "ioremap of SPI registers failed\n");
+
+       dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
+
+       amd_spi->version = AMD_HID2_SPI;
+       host->bus_num = 2;
+
+       return amd_spi_probe_common(dev, host);
+}
+
+static struct pci_driver amd_spi_pci_driver = {
+       .name = "amd_spi_pci",
+       .id_table = pci_spi_ids,
+       .probe = amd_spi_pci_probe,
+};
+
+module_pci_driver(amd_spi_pci_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("AMD HID2 SPI Controller Driver");
index 17fc0b17e756dd90b07399cf388df269c967d55c..90b1f7daa072c5b0d6412fb8ede761751dc500e1 100644 (file)
@@ -17,6 +17,8 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/spi-mem.h>
 
+#include "spi-amd.h"
+
 #define AMD_SPI_CTRL0_REG      0x00
 #define AMD_SPI_EXEC_CMD       BIT(16)
 #define AMD_SPI_FIFO_CLEAR     BIT(20)
 #define AMD_SPI_OP_READ_1_1_4_4B       0x6c    /* Read data bytes (Quad Output SPI) */
 #define AMD_SPI_OP_READ_1_4_4_4B       0xec    /* Read data bytes (Quad I/O SPI) */
 
-/**
- * enum amd_spi_versions - SPI controller versions
- * @AMD_SPI_V1:                AMDI0061 hardware version
- * @AMD_SPI_V2:                AMDI0062 hardware version
- * @AMD_HID2_SPI:      AMDI0063 hardware version
- */
-enum amd_spi_versions {
-       AMD_SPI_V1 = 1,
-       AMD_SPI_V2,
-       AMD_HID2_SPI,
-};
-
 enum amd_spi_speed {
        F_66_66MHz,
        F_33_33MHz,
@@ -118,22 +108,6 @@ struct amd_spi_freq {
        u32 spd7_val;
 };
 
-/**
- * struct amd_spi - SPI driver instance
- * @io_remap_addr:     Start address of the SPI controller registers
- * @phy_dma_buf:       Physical address of DMA buffer
- * @dma_virt_addr:     Virtual address of DMA buffer
- * @version:           SPI controller hardware version
- * @speed_hz:          Device frequency
- */
-struct amd_spi {
-       void __iomem *io_remap_addr;
-       dma_addr_t phy_dma_buf;
-       void *dma_virt_addr;
-       enum amd_spi_versions version;
-       unsigned int speed_hz;
-};
-
 static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx)
 {
        return readb((u8 __iomem *)amd_spi->io_remap_addr + idx);
@@ -749,30 +723,12 @@ static int amd_spi_setup_hiddma(struct amd_spi *amd_spi, struct device *dev)
        return 0;
 }
 
-static int amd_spi_probe(struct platform_device *pdev)
+int amd_spi_probe_common(struct device *dev, struct spi_controller *host)
 {
-       struct device *dev = &pdev->dev;
-       struct spi_controller *host;
-       struct amd_spi *amd_spi;
+       struct amd_spi *amd_spi = spi_controller_get_devdata(host);
        int err;
 
-       /* Allocate storage for host and driver private data */
-       host = devm_spi_alloc_host(dev, sizeof(struct amd_spi));
-       if (!host)
-               return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n");
-
-       amd_spi = spi_controller_get_devdata(host);
-       amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(amd_spi->io_remap_addr))
-               return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr),
-                                    "ioremap of SPI registers failed\n");
-
-       dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
-
-       amd_spi->version = (uintptr_t) device_get_match_data(dev);
-
        /* Initialize the spi_controller fields */
-       host->bus_num = (amd_spi->version == AMD_HID2_SPI) ? 2 : 0;
        host->num_chipselect = 4;
        host->mode_bits = SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
        host->flags = SPI_CONTROLLER_HALF_DUPLEX;
@@ -795,6 +751,32 @@ static int amd_spi_probe(struct platform_device *pdev)
 
        return err;
 }
+EXPORT_SYMBOL_GPL(amd_spi_probe_common);
+
+static int amd_spi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct spi_controller *host;
+       struct amd_spi *amd_spi;
+
+       /* Allocate storage for host and driver private data */
+       host = devm_spi_alloc_host(dev, sizeof(struct amd_spi));
+       if (!host)
+               return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n");
+
+       amd_spi = spi_controller_get_devdata(host);
+       amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(amd_spi->io_remap_addr))
+               return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr),
+                                    "ioremap of SPI registers failed\n");
+
+       dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
+
+       amd_spi->version = (uintptr_t)device_get_match_data(dev);
+       host->bus_num = 0;
+
+       return amd_spi_probe_common(dev, host);
+}
 
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id spi_acpi_match[] = {
diff --git a/drivers/spi/spi-amd.h b/drivers/spi/spi-amd.h
new file mode 100644 (file)
index 0000000..5f39ce7
--- /dev/null
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * AMD SPI controller driver common stuff
+ *
+ * Copyright (c) 2025, Advanced Micro Devices, Inc.
+ * All Rights Reserved.
+ *
+ * Author: Krishnamoorthi M <krishnamoorthi.m@amd.com>
+ */
+
+#ifndef SPI_AMD_H
+#define SPI_AMD_H
+
+/**
+ * enum amd_spi_versions - SPI controller versions
+ * @AMD_SPI_V1:         AMDI0061 hardware version
+ * @AMD_SPI_V2:         AMDI0062 hardware version
+ * @AMD_HID2_SPI:       AMDI0063 hardware version
+ */
+enum amd_spi_versions {
+       AMD_SPI_V1 = 1,
+       AMD_SPI_V2,
+       AMD_HID2_SPI,
+};
+
+/**
+ * struct amd_spi - SPI driver instance
+ * @io_remap_addr:      Start address of the SPI controller registers
+ * @phy_dma_buf:        Physical address of DMA buffer
+ * @dma_virt_addr:      Virtual address of DMA buffer
+ * @version:            SPI controller hardware version
+ * @speed_hz:           Device frequency
+ */
+struct amd_spi {
+       void __iomem *io_remap_addr;
+       dma_addr_t phy_dma_buf;
+       void *dma_virt_addr;
+       enum amd_spi_versions version;
+       unsigned int speed_hz;
+};
+
+int amd_spi_probe_common(struct device *dev, struct spi_controller *host);
+
+#endif /* SPI_AMD_H */