Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C driver skeleton

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

Co-developed-by: Xinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: Xinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: Even Xu <even.xu@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>

authored by

Even Xu and committed by
Jiri Kosina
61bb2714 6912aaf3

+332
+11
drivers/hid/intel-thc-hid/Kconfig
··· 28 28 29 29 Say Y/M here if you want to support Intel QuickSPI. If unsure, say N. 30 30 31 + config INTEL_QUICKI2C 32 + tristate "Intel QuickI2C driver based on Intel Touch Host Controller" 33 + depends on INTEL_THC_HID 34 + help 35 + Intel QuickI2C, uses Touch Host Controller (THC) hardware, implements 36 + HIDI2C (HID over I2C) protocol. It configures THC to work in I2C 37 + mode, and controls THC hardware sequencer to accelerate HIDI2C 38 + transaction flow. 39 + 40 + Say Y/M here if you want to support Intel QuickI2C. If unsure, say N. 41 + 31 42 endmenu
+3
drivers/hid/intel-thc-hid/Makefile
··· 14 14 intel-quickspi-objs += intel-quickspi/quickspi-hid.o 15 15 intel-quickspi-objs += intel-quickspi/quickspi-protocol.o 16 16 17 + obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o 18 + intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o 19 + 17 20 ccflags-y += -I $(src)/intel-thc
+270
drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (c) 2024 Intel Corporation */ 3 + 4 + #include <linux/device.h> 5 + #include <linux/dma-mapping.h> 6 + #include <linux/err.h> 7 + #include <linux/interrupt.h> 8 + #include <linux/irqreturn.h> 9 + #include <linux/pci.h> 10 + 11 + #include "intel-thc-dev.h" 12 + 13 + #include "quicki2c-dev.h" 14 + 15 + /** 16 + * quicki2c_irq_quick_handler - The ISR of the quicki2c driver 17 + * 18 + * @irq: The irq number 19 + * @dev_id: pointer to the device structure 20 + * 21 + * Return: IRQ_WAKE_THREAD if further process needed. 22 + */ 23 + static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id) 24 + { 25 + struct quicki2c_device *qcdev = dev_id; 26 + 27 + if (qcdev->state == QUICKI2C_DISABLED) 28 + return IRQ_HANDLED; 29 + 30 + /* Disable THC interrupt before current interrupt be handled */ 31 + thc_interrupt_enable(qcdev->thc_hw, false); 32 + 33 + return IRQ_WAKE_THREAD; 34 + } 35 + 36 + /** 37 + * quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver 38 + * 39 + * @irq: The IRQ number 40 + * @dev_id: pointer to the quicki2c device structure 41 + * 42 + * Return: IRQ_HANDLED to finish this handler. 43 + */ 44 + static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id) 45 + { 46 + struct quicki2c_device *qcdev = dev_id; 47 + int int_mask; 48 + 49 + if (qcdev->state == QUICKI2C_DISABLED) 50 + return IRQ_HANDLED; 51 + 52 + int_mask = thc_interrupt_handler(qcdev->thc_hw); 53 + 54 + thc_interrupt_enable(qcdev->thc_hw, true); 55 + 56 + return IRQ_HANDLED; 57 + } 58 + 59 + /** 60 + * quicki2c_dev_init - Initialize quicki2c device 61 + * 62 + * @pdev: pointer to the thc pci device 63 + * @mem_addr: The pointer of MMIO memory address 64 + * 65 + * Alloc quicki2c device structure and initialized THC device, 66 + * then configure THC to HIDI2C mode. 67 + * 68 + * If success, enable THC hardware interrupt. 69 + * 70 + * Return: pointer to the quicki2c device structure if success 71 + * or NULL on failed. 72 + */ 73 + static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr) 74 + { 75 + struct device *dev = &pdev->dev; 76 + struct quicki2c_device *qcdev; 77 + int ret; 78 + 79 + qcdev = devm_kzalloc(dev, sizeof(struct quicki2c_device), GFP_KERNEL); 80 + if (!qcdev) 81 + return ERR_PTR(-ENOMEM); 82 + 83 + qcdev->pdev = pdev; 84 + qcdev->dev = dev; 85 + qcdev->mem_addr = mem_addr; 86 + 87 + /* thc hw init */ 88 + qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr); 89 + if (IS_ERR(qcdev->thc_hw)) { 90 + ret = PTR_ERR(qcdev->thc_hw); 91 + dev_err_once(dev, "Failed to initialize THC device context, ret = %d.\n", ret); 92 + return ERR_PTR(ret); 93 + } 94 + 95 + ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C); 96 + if (ret) { 97 + dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret); 98 + return ERR_PTR(ret); 99 + } 100 + 101 + thc_interrupt_config(qcdev->thc_hw); 102 + 103 + thc_interrupt_enable(qcdev->thc_hw, true); 104 + 105 + return qcdev; 106 + } 107 + 108 + /** 109 + * quicki2c_dev_deinit - De-initialize quicki2c device 110 + * 111 + * @qcdev: pointer to the quicki2c device structure 112 + * 113 + * Disable THC interrupt and deinitilize THC. 114 + */ 115 + static void quicki2c_dev_deinit(struct quicki2c_device *qcdev) 116 + { 117 + thc_interrupt_enable(qcdev->thc_hw, false); 118 + } 119 + 120 + /* 121 + * quicki2c_probe: Quicki2c driver probe function 122 + * 123 + * @pdev: point to pci device 124 + * @id: point to pci_device_id structure 125 + * 126 + * Return 0 if success or error code on failed. 127 + */ 128 + static int quicki2c_probe(struct pci_dev *pdev, 129 + const struct pci_device_id *id) 130 + { 131 + struct quicki2c_device *qcdev; 132 + void __iomem *mem_addr; 133 + int ret; 134 + 135 + ret = pcim_enable_device(pdev); 136 + if (ret) { 137 + dev_err_once(&pdev->dev, "Failed to enable PCI device, ret = %d.\n", ret); 138 + return ret; 139 + } 140 + 141 + pci_set_master(pdev); 142 + 143 + ret = pcim_iomap_regions(pdev, BIT(0), KBUILD_MODNAME); 144 + if (ret) { 145 + dev_err_once(&pdev->dev, "Failed to get PCI regions, ret = %d.\n", ret); 146 + goto disable_pci_device; 147 + } 148 + 149 + mem_addr = pcim_iomap_table(pdev)[0]; 150 + 151 + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 152 + if (ret) { 153 + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 154 + if (ret) { 155 + dev_err_once(&pdev->dev, "No usable DMA configuration %d\n", ret); 156 + goto unmap_io_region; 157 + } 158 + } 159 + 160 + ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_ALL_TYPES); 161 + if (ret < 0) { 162 + dev_err_once(&pdev->dev, 163 + "Failed to allocate IRQ vectors. ret = %d\n", ret); 164 + goto unmap_io_region; 165 + } 166 + 167 + pdev->irq = pci_irq_vector(pdev, 0); 168 + 169 + qcdev = quicki2c_dev_init(pdev, mem_addr); 170 + if (IS_ERR(qcdev)) { 171 + dev_err_once(&pdev->dev, "QuickI2C device init failed\n"); 172 + ret = PTR_ERR(qcdev); 173 + goto unmap_io_region; 174 + } 175 + 176 + pci_set_drvdata(pdev, qcdev); 177 + 178 + ret = devm_request_threaded_irq(&pdev->dev, pdev->irq, 179 + quicki2c_irq_quick_handler, 180 + quicki2c_irq_thread_handler, 181 + IRQF_ONESHOT, KBUILD_MODNAME, 182 + qcdev); 183 + if (ret) { 184 + dev_err_once(&pdev->dev, 185 + "Failed to request threaded IRQ, irq = %d.\n", pdev->irq); 186 + goto dev_deinit; 187 + } 188 + 189 + return 0; 190 + 191 + dev_deinit: 192 + quicki2c_dev_deinit(qcdev); 193 + unmap_io_region: 194 + pcim_iounmap_regions(pdev, BIT(0)); 195 + disable_pci_device: 196 + pci_clear_master(pdev); 197 + 198 + return ret; 199 + } 200 + 201 + /** 202 + * quicki2c_remove - Device Removal Routine 203 + * 204 + * @pdev: PCI device structure 205 + * 206 + * This is called by the PCI subsystem to alert the driver 207 + * that it should release a PCI device. 208 + */ 209 + static void quicki2c_remove(struct pci_dev *pdev) 210 + { 211 + struct quicki2c_device *qcdev; 212 + 213 + qcdev = pci_get_drvdata(pdev); 214 + if (!qcdev) 215 + return; 216 + 217 + quicki2c_dev_deinit(qcdev); 218 + 219 + pcim_iounmap_regions(pdev, BIT(0)); 220 + pci_clear_master(pdev); 221 + } 222 + 223 + /** 224 + * quicki2c_shutdown - Device Shutdown Routine 225 + * 226 + * @pdev: PCI device structure 227 + * 228 + * This is called from the reboot notifier 229 + * it's a simplified version of remove so we go down 230 + * faster. 231 + */ 232 + static void quicki2c_shutdown(struct pci_dev *pdev) 233 + { 234 + struct quicki2c_device *qcdev; 235 + 236 + qcdev = pci_get_drvdata(pdev); 237 + if (!qcdev) 238 + return; 239 + 240 + quicki2c_dev_deinit(qcdev); 241 + } 242 + 243 + static const struct pci_device_id quicki2c_pci_tbl[] = { 244 + {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1), }, 245 + {PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2), }, 246 + {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1), }, 247 + {PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2), }, 248 + {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1), }, 249 + {PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2), }, 250 + {} 251 + }; 252 + MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl); 253 + 254 + static struct pci_driver quicki2c_driver = { 255 + .name = KBUILD_MODNAME, 256 + .id_table = quicki2c_pci_tbl, 257 + .probe = quicki2c_probe, 258 + .remove = quicki2c_remove, 259 + .shutdown = quicki2c_shutdown, 260 + .driver.probe_type = PROBE_PREFER_ASYNCHRONOUS, 261 + }; 262 + 263 + module_pci_driver(quicki2c_driver); 264 + 265 + MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>"); 266 + MODULE_AUTHOR("Even Xu <even.xu@intel.com>"); 267 + 268 + MODULE_DESCRIPTION("Intel(R) QuickI2C Driver"); 269 + MODULE_LICENSE("GPL"); 270 + MODULE_IMPORT_NS("INTEL_THC");
+48
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* Copyright (c) 2024 Intel Corporation */ 3 + 4 + #ifndef _QUICKI2C_DEV_H_ 5 + #define _QUICKI2C_DEV_H_ 6 + 7 + #define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848 8 + #define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A 9 + #define THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348 10 + #define THC_PTL_H_DEVICE_ID_I2C_PORT2 0xE34A 11 + #define THC_PTL_U_DEVICE_ID_I2C_PORT1 0xE448 12 + #define THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A 13 + 14 + /* Packet size value, the unit is 16 bytes */ 15 + #define MAX_PACKET_SIZE_VALUE_LNL 256 16 + 17 + enum quicki2c_dev_state { 18 + QUICKI2C_NONE, 19 + QUICKI2C_RESETING, 20 + QUICKI2C_RESETED, 21 + QUICKI2C_INITED, 22 + QUICKI2C_ENABLED, 23 + QUICKI2C_DISABLED, 24 + }; 25 + 26 + struct device; 27 + struct pci_dev; 28 + struct thc_device; 29 + 30 + /** 31 + * struct quicki2c_device - THC QuickI2C device struct 32 + * @dev: point to kernel device 33 + * @pdev: point to PCI device 34 + * @thc_hw: point to THC device 35 + * @driver_data: point to quicki2c specific driver data 36 + * @state: THC I2C device state 37 + * @mem_addr: MMIO memory address 38 + */ 39 + struct quicki2c_device { 40 + struct device *dev; 41 + struct pci_dev *pdev; 42 + struct thc_device *thc_hw; 43 + enum quicki2c_dev_state state; 44 + 45 + void __iomem *mem_addr; 46 + }; 47 + 48 + #endif /* _QUICKI2C_DEV_H_ */