diff options
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | drivers/spi/Makefile | 2 | ||||
-rw-r--r-- | drivers/spi/spi-amd-pci.c | 70 | ||||
-rw-r--r-- | drivers/spi/spi-amd.c | 78 | ||||
-rw-r--r-- | drivers/spi/spi-amd.h | 44 | ||||
-rw-r--r-- | drivers/spi/spi-qpic-snand.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi-xcomm.c | 8 | ||||
-rw-r--r-- | drivers/spi/spi.c | 10 | ||||
-rw-r--r-- | include/linux/spi/spi.h | 37 |
9 files changed, 177 insertions, 76 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 96b827049501..cda27131bb1b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -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> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c3a1a47b3bf4..4ea89f6fc531 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -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 index 000000000000..e1ecab755fe9 --- /dev/null +++ b/drivers/spi/spi-amd-pci.c @@ -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"); diff --git a/drivers/spi/spi-amd.c b/drivers/spi/spi-amd.c index 17fc0b17e756..90b1f7daa072 100644 --- a/drivers/spi/spi-amd.c +++ b/drivers/spi/spi-amd.c @@ -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) @@ -81,18 +83,6 @@ #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 index 000000000000..5f39ce7b5587 --- /dev/null +++ b/drivers/spi/spi-amd.h @@ -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 */ diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 17eb67e19132..a2af731049d4 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -261,7 +261,7 @@ static int qcom_spi_ecc_init_ctx_pipelined(struct nand_device *nand) ecc_cfg = kzalloc(sizeof(*ecc_cfg), GFP_KERNEL); if (!ecc_cfg) return -ENOMEM; - snandc->qspi->oob_buf = kzalloc(mtd->writesize + mtd->oobsize, + snandc->qspi->oob_buf = kmalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); if (!snandc->qspi->oob_buf) { kfree(ecc_cfg); diff --git a/drivers/spi/spi-xcomm.c b/drivers/spi/spi-xcomm.c index 3bd0149d8f4e..1a40c4866ce1 100644 --- a/drivers/spi/spi-xcomm.c +++ b/drivers/spi/spi-xcomm.c @@ -44,8 +44,8 @@ struct spi_xcomm { u8 buf[63]; }; -static void spi_xcomm_gpio_set_value(struct gpio_chip *chip, - unsigned int offset, int val) +static int spi_xcomm_gpio_set_value(struct gpio_chip *chip, + unsigned int offset, int val) { struct spi_xcomm *spi_xcomm = gpiochip_get_data(chip); unsigned char buf[2]; @@ -53,7 +53,7 @@ static void spi_xcomm_gpio_set_value(struct gpio_chip *chip, buf[0] = SPI_XCOMM_CMD_GPIO_SET; buf[1] = !!val; - i2c_master_send(spi_xcomm->i2c, buf, 2); + return i2c_master_send(spi_xcomm->i2c, buf, 2); } static int spi_xcomm_gpio_get_direction(struct gpio_chip *chip, @@ -70,7 +70,7 @@ static int spi_xcomm_gpio_add(struct spi_xcomm *spi_xcomm) return 0; spi_xcomm->gc.get_direction = spi_xcomm_gpio_get_direction; - spi_xcomm->gc.set = spi_xcomm_gpio_set_value; + spi_xcomm->gc.set_rv = spi_xcomm_gpio_set_value; spi_xcomm->gc.can_sleep = 1; spi_xcomm->gc.base = -1; spi_xcomm->gc.ngpio = 1; diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 90e27729ef6b..00b81d81c09a 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1076,10 +1076,8 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) * Avoid calling into the driver (or doing delays) if the chip select * isn't actually changing from the last time this was called. */ - if (!force && ((enable && spi->controller->last_cs_index_mask == spi->cs_index_mask && - spi_is_last_cs(spi)) || - (!enable && spi->controller->last_cs_index_mask == spi->cs_index_mask && - !spi_is_last_cs(spi))) && + if (!force && (enable == spi_is_last_cs(spi)) && + (spi->controller->last_cs_index_mask == spi->cs_index_mask) && (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH))) return; @@ -1088,9 +1086,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force) spi->controller->last_cs_index_mask = spi->cs_index_mask; for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi, 0) : SPI_INVALID_CS; - spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; - if (spi->mode & SPI_CS_HIGH) + spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH; + if (spi->controller->last_cs_mode_high) enable = !enable; /* diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 0ba5e49bace4..e10f0ebb8250 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -136,13 +136,6 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * @max_speed_hz: Maximum clock rate to be used with this chip * (on this board); may be changed by the device's driver. * The spi_transfer.speed_hz can override this for each transfer. - * @chip_select: Array of physical chipselect, spi->chipselect[i] gives - * the corresponding physical CS for logical CS i. - * @mode: The spi mode defines how data is clocked out and in. - * This may be changed by the device's driver. - * The "active low" default for chipselect mode can be overridden - * (by specifying SPI_CS_HIGH) as can the "MSB first" default for - * each word in a transfer (by specifying SPI_LSB_FIRST). * @bits_per_word: Data transfers involve one or more words; word sizes * like eight or 12 bits are common. In-memory wordsizes are * powers of two bytes (e.g. 20 bit samples use 32 bits). @@ -150,6 +143,11 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * default (0) indicating protocol words are eight bit bytes. * The spi_transfer.bits_per_word can override this for each transfer. * @rt: Make the pump thread real time priority. + * @mode: The spi mode defines how data is clocked out and in. + * This may be changed by the device's driver. + * The "active low" default for chipselect mode can be overridden + * (by specifying SPI_CS_HIGH) as can the "MSB first" default for + * each word in a transfer (by specifying SPI_LSB_FIRST). * @irq: Negative, or the number passed to request_irq() to receive * interrupts from this device. * @controller_state: Controller's runtime state @@ -162,8 +160,7 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * the device will bind to the named driver and only the named driver. * Do not set directly, because core frees it; use driver_set_override() to * set or clear it. - * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines - * (optional, NULL when not using a GPIO line) + * @pcpu_statistics: statistics for the spi_device * @word_delay: delay to be inserted between consecutive * words of a transfer * @cs_setup: delay to be introduced by the controller after CS is asserted @@ -171,8 +168,11 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg, * @cs_inactive: delay to be introduced by the controller after CS is * deasserted. If @cs_change_delay is used from @spi_transfer, then the * two delays will be added up. - * @pcpu_statistics: statistics for the spi_device + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives + * the corresponding physical CS for logical CS i. * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array + * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines + * (optional, NULL when not using a GPIO line) * * A @spi_device is used to interchange data between an SPI target device * (usually a discrete chip) and CPU memory. @@ -187,7 +187,6 @@ struct spi_device { struct device dev; struct spi_controller *controller; u32 max_speed_hz; - u8 chip_select[SPI_CS_CNT_MAX]; u8 bits_per_word; bool rt; #define SPI_NO_TX BIT(31) /* No transmit wire */ @@ -218,23 +217,29 @@ struct spi_device { void *controller_data; char modalias[SPI_NAME_SIZE]; const char *driver_override; - struct gpio_desc *cs_gpiod[SPI_CS_CNT_MAX]; /* Chip select gpio desc */ + + /* The statistics */ + struct spi_statistics __percpu *pcpu_statistics; + struct spi_delay word_delay; /* Inter-word delay */ + /* CS delays */ struct spi_delay cs_setup; struct spi_delay cs_hold; struct spi_delay cs_inactive; - /* The statistics */ - struct spi_statistics __percpu *pcpu_statistics; + u8 chip_select[SPI_CS_CNT_MAX]; - /* Bit mask of the chipselect(s) that the driver need to use from - * the chipselect array.When the controller is capable to handle + /* + * Bit mask of the chipselect(s) that the driver need to use from + * the chipselect array. When the controller is capable to handle * multiple chip selects & memories are connected in parallel * then more than one bit need to be set in cs_index_mask. */ u32 cs_index_mask : SPI_CS_CNT_MAX; + struct gpio_desc *cs_gpiod[SPI_CS_CNT_MAX]; /* Chip select gpio desc */ + /* * Likely need more hooks for more protocol options affecting how * the controller talks to each chip, like: |