mirror of
https://github.com/torvalds/linux.git
synced 2025-04-12 06:49:52 +00:00
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>
This commit is contained in:
parent
6912aaf3fd
commit
61bb2714dc
@ -28,4 +28,15 @@ config INTEL_QUICKSPI
|
||||
|
||||
Say Y/M here if you want to support Intel QuickSPI. If unsure, say N.
|
||||
|
||||
config INTEL_QUICKI2C
|
||||
tristate "Intel QuickI2C driver based on Intel Touch Host Controller"
|
||||
depends on INTEL_THC_HID
|
||||
help
|
||||
Intel QuickI2C, uses Touch Host Controller (THC) hardware, implements
|
||||
HIDI2C (HID over I2C) protocol. It configures THC to work in I2C
|
||||
mode, and controls THC hardware sequencer to accelerate HIDI2C
|
||||
transaction flow.
|
||||
|
||||
Say Y/M here if you want to support Intel QuickI2C. If unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
@ -14,4 +14,7 @@ intel-quickspi-objs += intel-quickspi/pci-quickspi.o
|
||||
intel-quickspi-objs += intel-quickspi/quickspi-hid.o
|
||||
intel-quickspi-objs += intel-quickspi/quickspi-protocol.o
|
||||
|
||||
obj-$(CONFIG_INTEL_QUICKI2C) += intel-quicki2c.o
|
||||
intel-quicki2c-objs += intel-quicki2c/pci-quicki2c.o
|
||||
|
||||
ccflags-y += -I $(src)/intel-thc
|
||||
|
270
drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
Normal file
270
drivers/hid/intel-thc-hid/intel-quicki2c/pci-quicki2c.c
Normal file
@ -0,0 +1,270 @@
|
||||
/* 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 "quicki2c-dev.h"
|
||||
|
||||
/**
|
||||
* quicki2c_irq_quick_handler - The ISR of the quicki2c driver
|
||||
*
|
||||
* @irq: The irq number
|
||||
* @dev_id: pointer to the device structure
|
||||
*
|
||||
* Return: IRQ_WAKE_THREAD if further process needed.
|
||||
*/
|
||||
static irqreturn_t quicki2c_irq_quick_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct quicki2c_device *qcdev = dev_id;
|
||||
|
||||
if (qcdev->state == QUICKI2C_DISABLED)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* Disable THC interrupt before current interrupt be handled */
|
||||
thc_interrupt_enable(qcdev->thc_hw, false);
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
/**
|
||||
* quicki2c_irq_thread_handler - IRQ thread handler of quicki2c driver
|
||||
*
|
||||
* @irq: The IRQ number
|
||||
* @dev_id: pointer to the quicki2c device structure
|
||||
*
|
||||
* Return: IRQ_HANDLED to finish this handler.
|
||||
*/
|
||||
static irqreturn_t quicki2c_irq_thread_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct quicki2c_device *qcdev = dev_id;
|
||||
int int_mask;
|
||||
|
||||
if (qcdev->state == QUICKI2C_DISABLED)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
int_mask = thc_interrupt_handler(qcdev->thc_hw);
|
||||
|
||||
thc_interrupt_enable(qcdev->thc_hw, true);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* quicki2c_dev_init - Initialize quicki2c device
|
||||
*
|
||||
* @pdev: pointer to the thc pci device
|
||||
* @mem_addr: The pointer of MMIO memory address
|
||||
*
|
||||
* Alloc quicki2c device structure and initialized THC device,
|
||||
* then configure THC to HIDI2C mode.
|
||||
*
|
||||
* If success, enable THC hardware interrupt.
|
||||
*
|
||||
* Return: pointer to the quicki2c device structure if success
|
||||
* or NULL on failed.
|
||||
*/
|
||||
static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __iomem *mem_addr)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct quicki2c_device *qcdev;
|
||||
int ret;
|
||||
|
||||
qcdev = devm_kzalloc(dev, sizeof(struct quicki2c_device), GFP_KERNEL);
|
||||
if (!qcdev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
qcdev->pdev = pdev;
|
||||
qcdev->dev = dev;
|
||||
qcdev->mem_addr = mem_addr;
|
||||
|
||||
/* thc hw init */
|
||||
qcdev->thc_hw = thc_dev_init(qcdev->dev, qcdev->mem_addr);
|
||||
if (IS_ERR(qcdev->thc_hw)) {
|
||||
ret = PTR_ERR(qcdev->thc_hw);
|
||||
dev_err_once(dev, "Failed to initialize THC device context, ret = %d.\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C);
|
||||
if (ret) {
|
||||
dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
thc_interrupt_config(qcdev->thc_hw);
|
||||
|
||||
thc_interrupt_enable(qcdev->thc_hw, true);
|
||||
|
||||
return qcdev;
|
||||
}
|
||||
|
||||
/**
|
||||
* quicki2c_dev_deinit - De-initialize quicki2c device
|
||||
*
|
||||
* @qcdev: pointer to the quicki2c device structure
|
||||
*
|
||||
* Disable THC interrupt and deinitilize THC.
|
||||
*/
|
||||
static void quicki2c_dev_deinit(struct quicki2c_device *qcdev)
|
||||
{
|
||||
thc_interrupt_enable(qcdev->thc_hw, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* quicki2c_probe: Quicki2c driver probe function
|
||||
*
|
||||
* @pdev: point to pci device
|
||||
* @id: point to pci_device_id structure
|
||||
*
|
||||
* Return 0 if success or error code on failed.
|
||||
*/
|
||||
static int quicki2c_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct quicki2c_device *qcdev;
|
||||
void __iomem *mem_addr;
|
||||
int ret;
|
||||
|
||||
ret = pcim_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err_once(&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_once(&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_once(&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_once(&pdev->dev,
|
||||
"Failed to allocate IRQ vectors. ret = %d\n", ret);
|
||||
goto unmap_io_region;
|
||||
}
|
||||
|
||||
pdev->irq = pci_irq_vector(pdev, 0);
|
||||
|
||||
qcdev = quicki2c_dev_init(pdev, mem_addr);
|
||||
if (IS_ERR(qcdev)) {
|
||||
dev_err_once(&pdev->dev, "QuickI2C device init failed\n");
|
||||
ret = PTR_ERR(qcdev);
|
||||
goto unmap_io_region;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, qcdev);
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, pdev->irq,
|
||||
quicki2c_irq_quick_handler,
|
||||
quicki2c_irq_thread_handler,
|
||||
IRQF_ONESHOT, KBUILD_MODNAME,
|
||||
qcdev);
|
||||
if (ret) {
|
||||
dev_err_once(&pdev->dev,
|
||||
"Failed to request threaded IRQ, irq = %d.\n", pdev->irq);
|
||||
goto dev_deinit;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dev_deinit:
|
||||
quicki2c_dev_deinit(qcdev);
|
||||
unmap_io_region:
|
||||
pcim_iounmap_regions(pdev, BIT(0));
|
||||
disable_pci_device:
|
||||
pci_clear_master(pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* quicki2c_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 quicki2c_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct quicki2c_device *qcdev;
|
||||
|
||||
qcdev = pci_get_drvdata(pdev);
|
||||
if (!qcdev)
|
||||
return;
|
||||
|
||||
quicki2c_dev_deinit(qcdev);
|
||||
|
||||
pcim_iounmap_regions(pdev, BIT(0));
|
||||
pci_clear_master(pdev);
|
||||
}
|
||||
|
||||
/**
|
||||
* quicki2c_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 quicki2c_shutdown(struct pci_dev *pdev)
|
||||
{
|
||||
struct quicki2c_device *qcdev;
|
||||
|
||||
qcdev = pci_get_drvdata(pdev);
|
||||
if (!qcdev)
|
||||
return;
|
||||
|
||||
quicki2c_dev_deinit(qcdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id quicki2c_pci_tbl[] = {
|
||||
{PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT1), },
|
||||
{PCI_VDEVICE(INTEL, THC_LNL_DEVICE_ID_I2C_PORT2), },
|
||||
{PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT1), },
|
||||
{PCI_VDEVICE(INTEL, THC_PTL_H_DEVICE_ID_I2C_PORT2), },
|
||||
{PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT1), },
|
||||
{PCI_VDEVICE(INTEL, THC_PTL_U_DEVICE_ID_I2C_PORT2), },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, quicki2c_pci_tbl);
|
||||
|
||||
static struct pci_driver quicki2c_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.id_table = quicki2c_pci_tbl,
|
||||
.probe = quicki2c_probe,
|
||||
.remove = quicki2c_remove,
|
||||
.shutdown = quicki2c_shutdown,
|
||||
.driver.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
};
|
||||
|
||||
module_pci_driver(quicki2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Xinpeng Sun <xinpeng.sun@intel.com>");
|
||||
MODULE_AUTHOR("Even Xu <even.xu@intel.com>");
|
||||
|
||||
MODULE_DESCRIPTION("Intel(R) QuickI2C Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("INTEL_THC");
|
48
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
Normal file
48
drivers/hid/intel-thc-hid/intel-quicki2c/quicki2c-dev.h
Normal file
@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2024 Intel Corporation */
|
||||
|
||||
#ifndef _QUICKI2C_DEV_H_
|
||||
#define _QUICKI2C_DEV_H_
|
||||
|
||||
#define THC_LNL_DEVICE_ID_I2C_PORT1 0xA848
|
||||
#define THC_LNL_DEVICE_ID_I2C_PORT2 0xA84A
|
||||
#define THC_PTL_H_DEVICE_ID_I2C_PORT1 0xE348
|
||||
#define THC_PTL_H_DEVICE_ID_I2C_PORT2 0xE34A
|
||||
#define THC_PTL_U_DEVICE_ID_I2C_PORT1 0xE448
|
||||
#define THC_PTL_U_DEVICE_ID_I2C_PORT2 0xE44A
|
||||
|
||||
/* Packet size value, the unit is 16 bytes */
|
||||
#define MAX_PACKET_SIZE_VALUE_LNL 256
|
||||
|
||||
enum quicki2c_dev_state {
|
||||
QUICKI2C_NONE,
|
||||
QUICKI2C_RESETING,
|
||||
QUICKI2C_RESETED,
|
||||
QUICKI2C_INITED,
|
||||
QUICKI2C_ENABLED,
|
||||
QUICKI2C_DISABLED,
|
||||
};
|
||||
|
||||
struct device;
|
||||
struct pci_dev;
|
||||
struct thc_device;
|
||||
|
||||
/**
|
||||
* struct quicki2c_device - THC QuickI2C device struct
|
||||
* @dev: point to kernel device
|
||||
* @pdev: point to PCI device
|
||||
* @thc_hw: point to THC device
|
||||
* @driver_data: point to quicki2c specific driver data
|
||||
* @state: THC I2C device state
|
||||
* @mem_addr: MMIO memory address
|
||||
*/
|
||||
struct quicki2c_device {
|
||||
struct device *dev;
|
||||
struct pci_dev *pdev;
|
||||
struct thc_device *thc_hw;
|
||||
enum quicki2c_dev_state state;
|
||||
|
||||
void __iomem *mem_addr;
|
||||
};
|
||||
|
||||
#endif /* _QUICKI2C_DEV_H_ */
|
Loading…
x
Reference in New Issue
Block a user