Skip to content

Commit 61bb271

Browse files
EvenxfJiri Kosina
authored andcommitted
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>
1 parent 6912aaf commit 61bb271

File tree

4 files changed

+332
-0
lines changed

4 files changed

+332
-0
lines changed

drivers/hid/intel-thc-hid/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,15 @@ config INTEL_QUICKSPI
2828

2929
Say Y/M here if you want to support Intel QuickSPI. If unsure, say N.
3030

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+
3142
endmenu

drivers/hid/intel-thc-hid/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,7 @@ intel-quickspi-objs += intel-quickspi/pci-quickspi.o
1414
intel-quickspi-objs += intel-quickspi/quickspi-hid.o
1515
intel-quickspi-objs += intel-quickspi/quickspi-protocol.o
1616

17+
obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o
18+
intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o
19+
1720
ccflags-y += -I $(src)/intel-thc
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
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");
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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_ */

0 commit comments

Comments
 (0)