HID: intel-thc-hid: intel-quickspi: Add THC QuickSPI driver skeleton

Create intel-quickspi folder and add Kconfig and Makefile for THC
QuickSPI driver. Add basic device structure, definitions and probe/remove
functions for QuickSPI driver.

Co-developed-by: Even Xu <even.xu@intel.com>
Signed-off-by: Even Xu <even.xu@intel.com>
Signed-off-by: Xinpeng Sun <xinpeng.sun@intel.com>
Tested-by: Rui Zhang <rui1.zhang@intel.com>
Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Tested-by: Aaron Ma <aaron.ma@canonical.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
Xinpeng Sun 2025-01-06 10:31:40 +08:00 committed by Jiri Kosina
parent 4228966def
commit c8f3027dd2
4 changed files with 362 additions and 0 deletions

View File

@ -17,4 +17,15 @@ config INTEL_THC_HID
Say Y/M here if you want to support Intel THC. If unsure, say N.
config INTEL_QUICKSPI
tristate "Intel QuickSPI driver based on Intel Touch Host Controller"
depends on INTEL_THC_HID
help
Intel QuickSPI, based on Touch Host Controller (THC), implements
HIDSPI (HID over SPI) protocol. It configures THC to work at SPI
mode, and controls THC hardware sequencer to accelerate HIDSPI
transaction flow.
Say Y/M here if you want to support Intel QuickSPI. If unsure, say N.
endmenu

View File

@ -9,4 +9,7 @@ obj-$(CONFIG_INTEL_THC_HID) += intel-thc.o
intel-thc-objs += intel-thc/intel-thc-dev.o
intel-thc-objs += intel-thc/intel-thc-dma.o
obj-$(CONFIG_INTEL_QUICKSPI) += intel-quickspi.o
intel-quickspi-objs += intel-quickspi/pci-quickspi.o
ccflags-y += -I $(src)/intel-thc

View File

@ -0,0 +1,287 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/pci.h>
#include "intel-thc-dev.h"
#include "quickspi-dev.h"
struct quickspi_driver_data mtl = {
.max_packet_size_value = MAX_PACKET_SIZE_VALUE_MTL,
};
struct quickspi_driver_data lnl = {
.max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL,
};
struct quickspi_driver_data ptl = {
.max_packet_size_value = MAX_PACKET_SIZE_VALUE_LNL,
};
/**
* quickspi_irq_quick_handler - The ISR of the quickspi driver
*
* @irq: The irq number
* @dev_id: pointer to the device structure
*
* Return: IRQ_WAKE_THREAD if further process needed.
*/
static irqreturn_t quickspi_irq_quick_handler(int irq, void *dev_id)
{
struct quickspi_device *qsdev = dev_id;
if (qsdev->state == QUICKSPI_DISABLED)
return IRQ_HANDLED;
/* Disable THC interrupt before current interrupt be handled */
thc_interrupt_enable(qsdev->thc_hw, false);
return IRQ_WAKE_THREAD;
}
/**
* quickspi_irq_thread_handler - IRQ thread handler of quickspi driver
*
* @irq: The IRQ number
* @dev_id: pointer to the quickspi device structure
*
* Return: IRQ_HANDLED to finish this handler.
*/
static irqreturn_t quickspi_irq_thread_handler(int irq, void *dev_id)
{
struct quickspi_device *qsdev = dev_id;
int int_mask;
if (qsdev->state == QUICKSPI_DISABLED)
return IRQ_HANDLED;
int_mask = thc_interrupt_handler(qsdev->thc_hw);
thc_interrupt_enable(qsdev->thc_hw, true);
return IRQ_HANDLED;
}
/**
* quickspi_dev_init - Initialize quickspi device
*
* @pdev: pointer to the thc pci device
* @mem_addr: The pointer of MMIO memory address
* @id: point to pci_device_id structure
*
* Alloc quickspi device structure and initialized THC device,
* then configure THC to HIDSPI mode.
*
* If success, enable THC hardware interrupt.
*
* Return: pointer to the quickspi device structure if success
* or NULL on failed.
*/
static struct quickspi_device *quickspi_dev_init(struct pci_dev *pdev, void __iomem *mem_addr,
const struct pci_device_id *id)
{
struct device *dev = &pdev->dev;
struct quickspi_device *qsdev;
int ret;
qsdev = devm_kzalloc(dev, sizeof(struct quickspi_device), GFP_KERNEL);
if (!qsdev)
return ERR_PTR(-ENOMEM);
qsdev->pdev = pdev;
qsdev->dev = dev;
qsdev->mem_addr = mem_addr;
qsdev->driver_data = (struct quickspi_driver_data *)id->driver_data;
/* thc hw init */
qsdev->thc_hw = thc_dev_init(qsdev->dev, qsdev->mem_addr);
if (IS_ERR(qsdev->thc_hw)) {
ret = PTR_ERR(qsdev->thc_hw);
dev_err(dev, "Failed to initialize THC device context, ret = %d.\n", ret);
return ERR_PTR(ret);
}
ret = thc_port_select(qsdev->thc_hw, THC_PORT_TYPE_SPI);
if (ret) {
dev_err(dev, "Failed to select THC port, ret = %d.\n", ret);
return ERR_PTR(ret);
}
thc_interrupt_config(qsdev->thc_hw);
thc_interrupt_enable(qsdev->thc_hw, true);
return qsdev;
}
/**
* quickspi_dev_deinit - De-initialize quickspi device
*
* @qsdev: pointer to the quickspi device structure
*
* Disable THC interrupt and deinitilize THC.
*/
static void quickspi_dev_deinit(struct quickspi_device *qsdev)
{
thc_interrupt_enable(qsdev->thc_hw, false);
}
/*
* quickspi_probe: Quickspi driver probe function
*
* @pdev: point to pci device
* @id: point to pci_device_id structure
*
* Return 0 if success or error code on failure.
*/
static int quickspi_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct quickspi_device *qsdev;
void __iomem *mem_addr;
int ret;
ret = pcim_enable_device(pdev);
if (ret) {
dev_err(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret);
return ret;
}
pci_set_master(pdev);
ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME);
if (ret) {
dev_err(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret);
goto disable_pci_device;
}
mem_addr = pcim_iomap_table(pdev)[0];
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "No usable DMA configuration %d\n", ret);
goto unmap_io_region;
}
}
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES);
if (ret < 0) {
dev_err(&pdev->dev,
"Failed to allocate IRQ vectors. ret = %d\n", ret);
goto unmap_io_region;
}
pdev->irq = pci_irq_vector(pdev, 0);
qsdev = quickspi_dev_init(pdev, mem_addr, id);
if (IS_ERR(qsdev)) {
dev_err(&pdev->dev, "QuickSPI device init failed\n");
ret = PTR_ERR(qsdev);
goto unmap_io_region;
}
pci_set_drvdata(pdev, qsdev);
ret = devm_request_threaded_irq(&pdev->dev, pdev->irq,
quickspi_irq_quick_handler,
quickspi_irq_thread_handler,
IRQF_ONESHOT, KBUILD_MODNAME,
qsdev);
if (ret) {
dev_err(&pdev->dev,
"Failed to request threaded IRQ, irq = %d.\n", pdev->irq);
goto dev_deinit;
}
return 0;
dev_deinit:
quickspi_dev_deinit(qsdev);
unmap_io_region:
pcim_iounmap_regions(pdev, BIT(0));
disable_pci_device:
pci_clear_master(pdev);
return ret;
}
/**
* quickspi_remove - Device Removal Routine
*
* @pdev: PCI device structure
*
* This is called by the PCI subsystem to alert the driver
* that it should release a PCI device.
*/
static void quickspi_remove(struct pci_dev *pdev)
{
struct quickspi_device *qsdev;
qsdev = pci_get_drvdata(pdev);
if (!qsdev)
return;
quickspi_dev_deinit(qsdev);
pcim_iounmap_regions(pdev, BIT(0));
pci_clear_master(pdev);
}
/**
* quickspi_shutdown - Device Shutdown Routine
*
* @pdev: PCI device structure
*
* This is called from the reboot notifier
* it's a simplified version of remove so we go down
* faster.
*/
static void quickspi_shutdown(struct pci_dev *pdev)
{
struct quickspi_device *qsdev;
qsdev = pci_get_drvdata(pdev);
if (!qsdev)
return;
quickspi_dev_deinit(qsdev);
}
static const struct pci_device_id quickspi_pci_tbl[] = {
{PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT1, &mtl), },
{PCI_DEVICE_DATA(INTEL, THC_MTL_DEVICE_ID_SPI_PORT2, &mtl), },
{PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_SPI_PORT1, &lnl), },
{PCI_DEVICE_DATA(INTEL, THC_LNL_DEVICE_ID_SPI_PORT2, &lnl), },
{PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_SPI_PORT1, &ptl), },
{PCI_DEVICE_DATA(INTEL, THC_PTL_H_DEVICE_ID_SPI_PORT2, &ptl), },
{PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_SPI_PORT1, &ptl), },
{PCI_DEVICE_DATA(INTEL, THC_PTL_U_DEVICE_ID_SPI_PORT2, &ptl), },
{}
};
MODULE_DEVICE_TABLE(pci, quickspi_pci_tbl);
static struct pci_driver quickspi_driver = {
.name = KBUILD_MODNAME,
.id_table = quickspi_pci_tbl,
.probe = quickspi_probe,
.remove = quickspi_remove,
.shutdown = quickspi_shutdown,
.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
};
module_pci_driver(quickspi_driver);
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
MODULE_DESCRIPTION("Intel(R) QuickSPI Driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("INTEL_THC");

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */
#ifndef _QUICKSPI_DEV_H_
#define _QUICKSPI_DEV_H_
#define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT1 0x7E49
#define PCI_DEVICE_ID_INTEL_THC_MTL_DEVICE_ID_SPI_PORT2 0x7E4B
#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT1 0xA849
#define PCI_DEVICE_ID_INTEL_THC_LNL_DEVICE_ID_SPI_PORT2 0xA84B
#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_SPI_PORT1 0xE349
#define PCI_DEVICE_ID_INTEL_THC_PTL_H_DEVICE_ID_SPI_PORT2 0xE34B
#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT1 0xE449
#define PCI_DEVICE_ID_INTEL_THC_PTL_U_DEVICE_ID_SPI_PORT2 0xE44B
/* Packet size value, the unit is 16 bytes */
#define DEFAULT_MIN_PACKET_SIZE_VALUE 4
#define MAX_PACKET_SIZE_VALUE_MTL 128
#define MAX_PACKET_SIZE_VALUE_LNL 256
enum quickspi_dev_state {
QUICKSPI_NONE,
QUICKSPI_RESETING,
QUICKSPI_RESETED,
QUICKSPI_INITED,
QUICKSPI_ENABLED,
QUICKSPI_DISABLED,
};
/**
* struct quickspi_driver_data - Driver specific data for quickspi device
* @max_packet_size_value: identify max packet size, unit is 16 bytes
*/
struct quickspi_driver_data {
u32 max_packet_size_value;
};
struct device;
struct pci_dev;
struct thc_device;
/**
* struct quickspi_device - THC QuickSpi device struct
* @dev: point to kernel device
* @pdev: point to PCI device
* @thc_hw: point to THC device
* @driver_data: point to quickspi specific driver data
* @state: THC SPI device state
* @mem_addr: MMIO memory address
*/
struct quickspi_device {
struct device *dev;
struct pci_dev *pdev;
struct thc_device *thc_hw;
struct quickspi_driver_data *driver_data;
enum quickspi_dev_state state;
void __iomem *mem_addr;
};
#endif /* _QUICKSPI_DEV_H_ */