Commit 5282e45c authored by Even Xu's avatar Even Xu Committed by Jiri Kosina
Browse files

HID: intel-thc-hid: intel-quicki2c: Add THC QuickI2C ACPI interfaces



Add functions to query QuickI2C ACPI DSM/DSD parameters and use these
APIs to access all QuickI2C ACPI resources.

Co-developed-by: default avatarXinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: default avatarXinpeng Sun <xinpeng.sun@intel.com>
Signed-off-by: default avatarEven Xu <even.xu@intel.com>
Tested-by: default avatarRui Zhang <rui1.zhang@intel.com>
Tested-by: default avatarMark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: default avatarSrinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: default avatarMark Pearson <mpearson-lenovo@squebb.ca>
Tested-by: default avatarAaron Ma <aaron.ma@canonical.com>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.com>
parent ba38d7f8
Loading
Loading
Loading
Loading
+190 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (c) 2024 Intel Corporation */

#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
@@ -9,9 +10,185 @@
#include <linux/pci.h>

#include "intel-thc-dev.h"
#include "intel-thc-hw.h"

#include "quicki2c-dev.h"

/* THC QuickI2C ACPI method to get device properties */
/* HIDI2C device method */
static guid_t i2c_hid_guid =
	GUID_INIT(0x3cdff6f7, 0x4267, 0x4555, 0xad, 0x05, 0xb3, 0x0a, 0x3d, 0x89, 0x38, 0xde);

/* platform method */
static guid_t thc_platform_guid =
	GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38);

/**
 * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter
 *
 * @adev: point to ACPI device
 * @guid: ACPI method's guid
 * @rev: ACPI method's revision
 * @func: ACPI method's function number
 * @type: ACPI parameter's data type
 * @prop_buf: point to return buffer
 *
 * This is a helper function for device to query its ACPI DSM parameters.
 *
 * Return: 0 if success or ENODEV on failed.
 */
static int quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t *guid,
					  u64 rev, u64 func, acpi_object_type type, void *prop_buf)
{
	acpi_handle handle = acpi_device_handle(adev);
	union acpi_object *obj;

	obj = acpi_evaluate_dsm_typed(handle, guid, rev, func, NULL, type);
	if (!obj) {
		acpi_handle_err(handle,
				"Error _DSM call failed, rev: %d, func: %d, type: %d\n",
				(int)rev, (int)func, (int)type);
		return -ENODEV;
	}

	if (type == ACPI_TYPE_INTEGER)
		*(u32 *)prop_buf = (u32)obj->integer.value;
	else if (type == ACPI_TYPE_BUFFER)
		memcpy(prop_buf, obj->buffer.pointer, obj->buffer.length);

	ACPI_FREE(obj);

	return 0;
}

/**
 * quicki2c_acpi_get_dsd_property - Query device ACPI DSD parameter
 *
 * @adev: point to ACPI device
 * @dsd_method_name: ACPI method's property name
 * @type: ACPI parameter's data type
 * @prop_buf: point to return buffer
 *
 * This is a helper function for device to query its ACPI DSD parameters.
 *
 * Return: 0 if success or ENODEV on failed.
 */
static int quicki2c_acpi_get_dsd_property(struct acpi_device *adev, acpi_string dsd_method_name,
					  acpi_object_type type, void *prop_buf)
{
	acpi_handle handle = acpi_device_handle(adev);
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object obj = { .type = type };
	struct acpi_object_list arg_list = {
		.count = 1,
		.pointer = &obj,
	};
	union acpi_object *ret_obj;
	acpi_status status;

	status = acpi_evaluate_object(handle, dsd_method_name, &arg_list, &buffer);
	if (ACPI_FAILURE(status)) {
		acpi_handle_err(handle,
				"Can't evaluate %s method: %d\n", dsd_method_name, status);
		return -ENODEV;
	}

	ret_obj = buffer.pointer;

	memcpy(prop_buf, ret_obj->buffer.pointer, ret_obj->buffer.length);

	return 0;
}

/**
 * quicki2c_get_acpi_resources - Query all quicki2c devices' ACPI parameters
 *
 * @qcdev: point to quicki2c device
 *
 * This function gets all quicki2c devices' ACPI resource.
 *
 * Return: 0 if success or error code on failed.
 */
static int quicki2c_get_acpi_resources(struct quicki2c_device *qcdev)
{
	struct acpi_device *adev = ACPI_COMPANION(qcdev->dev);
	struct quicki2c_subip_acpi_parameter i2c_param;
	struct quicki2c_subip_acpi_config i2c_config;
	int ret = -EINVAL;

	if (!adev) {
		dev_err(qcdev->dev, "Invalid acpi device pointer\n");
		return ret;
	}

	qcdev->acpi_dev = adev;

	ret = quicki2c_acpi_get_dsm_property(adev, &i2c_hid_guid,
					     QUICKI2C_ACPI_REVISION_NUM,
					     QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR,
					     ACPI_TYPE_INTEGER,
					     &qcdev->hid_desc_addr);
	if (ret)
		return ret;

	ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid,
					     QUICKI2C_ACPI_REVISION_NUM,
					     QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL,
					     ACPI_TYPE_INTEGER,
					     &qcdev->active_ltr_val);
	if (ret)
		return ret;

	ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid,
					     QUICKI2C_ACPI_REVISION_NUM,
					     QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL,
					     ACPI_TYPE_INTEGER,
					     &qcdev->low_power_ltr_val);
	if (ret)
		return ret;

	ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ICRS,
					     ACPI_TYPE_BUFFER, &i2c_param);
	if (ret)
		return ret;

	if (i2c_param.addressing_mode != HIDI2C_ADDRESSING_MODE_7BIT)
		return -EOPNOTSUPP;

	qcdev->i2c_slave_addr = i2c_param.device_address;

	ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ISUB,
					     ACPI_TYPE_BUFFER, &i2c_config);
	if (ret)
		return ret;

	if (i2c_param.connection_speed > 0 &&
	    i2c_param.connection_speed <= QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED) {
		qcdev->i2c_speed_mode = THC_I2C_STANDARD;
		qcdev->i2c_clock_hcnt = i2c_config.SMHX;
		qcdev->i2c_clock_lcnt = i2c_config.SMLX;
	} else if (i2c_param.connection_speed > QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED &&
		   i2c_param.connection_speed <= QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED) {
		qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS;
		qcdev->i2c_clock_hcnt = i2c_config.FMHX;
		qcdev->i2c_clock_lcnt = i2c_config.FMLX;
	} else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED &&
		   i2c_param.connection_speed <= QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED) {
		qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS;
		qcdev->i2c_clock_hcnt = i2c_config.FPHX;
		qcdev->i2c_clock_lcnt = i2c_config.FPLX;
	} else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED &&
		   i2c_param.connection_speed <= QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED) {
		qcdev->i2c_speed_mode = THC_I2C_HIGH_SPEED;
		qcdev->i2c_clock_hcnt = i2c_config.HMHX;
		qcdev->i2c_clock_lcnt = i2c_config.HMLX;
	} else {
		return -EOPNOTSUPP;
	}

	return 0;
}

/**
 * quicki2c_irq_quick_handler - The ISR of the quicki2c driver
 *
@@ -92,12 +269,25 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
		return ERR_PTR(ret);
	}

	ret = quicki2c_get_acpi_resources(qcdev);
	if (ret) {
		dev_err_once(dev, "Get ACPI resources failed, 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);
	}

	ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr,
				 qcdev->i2c_speed_mode,
				 qcdev->i2c_clock_hcnt,
				 qcdev->i2c_clock_lcnt);
	if (ret)
		return ERR_PTR(ret);

	thc_interrupt_config(qcdev->thc_hw);

	thc_interrupt_enable(qcdev->thc_hw, true);
+107 −0
Original line number Diff line number Diff line
@@ -16,6 +16,25 @@
/* Packet size value, the unit is 16 bytes */
#define MAX_PACKET_SIZE_VALUE_LNL			256

/* HIDI2C special ACPI parameters DSD name */
#define QUICKI2C_ACPI_METHOD_NAME_ICRS		"ICRS"
#define QUICKI2C_ACPI_METHOD_NAME_ISUB		"ISUB"

/* HIDI2C special ACPI parameters DSM methods */
#define QUICKI2C_ACPI_REVISION_NUM		1
#define QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR	1
#define QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL	1
#define QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL	2

#define QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED		100000
#define QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED		400000
#define QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED		1000000
#define QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED	3400000

#define QUICKI2C_DEFAULT_ACTIVE_LTR_VALUE	5
#define QUICKI2C_DEFAULT_LP_LTR_VALUE		500
#define QUICKI2C_RPM_TIMEOUT_MS			500

enum quicki2c_dev_state {
	QUICKI2C_NONE,
	QUICKI2C_RESETING,
@@ -25,10 +44,80 @@ enum quicki2c_dev_state {
	QUICKI2C_DISABLED,
};

enum {
	HIDI2C_ADDRESSING_MODE_7BIT,
	HIDI2C_ADDRESSING_MODE_10BIT,
};

/**
 * struct quicki2c_subip_acpi_parameter - QuickI2C ACPI DSD parameters
 * @device_address: I2C device slave address
 * @connection_speed: I2C device expected connection speed
 * @addressing_mode: I2C device slave address mode, 7bit or 10bit
 *
 * Those properties get from QUICKI2C_ACPI_METHOD_NAME_ICRS method, used for
 * Bus parameter.
 */
struct quicki2c_subip_acpi_parameter {
	u16 device_address;
	u64 connection_speed;
	u8 addressing_mode;
} __packed;

/**
 * struct quicki2c_subip_acpi_config - QuickI2C ACPI DSD parameters
 * @SMHX: Standard Mode (100 kbit/s) Serial Clock Line HIGH Period
 * @SMLX: Standard Mode (100 kbit/s) Serial Clock Line LOW Period
 * @SMTD: Standard Mode (100 kbit/s) Serial Data Line Transmit Hold Period
 * @SMRD: Standard Mode (100 kbit/s) Serial Data Receive Hold Period
 * @FMHX: Fast Mode (400 kbit/s) Serial Clock Line HIGH Period
 * @FMLX: Fast Mode (400 kbit/s) Serial Clock Line LOW Period
 * @FMTD: Fast Mode (400 kbit/s) Serial Data Line Transmit Hold Period
 * @FMRD: Fast Mode (400 kbit/s) Serial Data Line Receive Hold Period
 * @FMSL: Maximum length (in ic_clk_cycles) of suppressed spikes
 *        in Standard Mode, Fast Mode and Fast Mode Plus
 * @FPHX: Fast Mode Plus (1Mbit/sec) Serial Clock Line HIGH Period
 * @FPLX: Fast Mode Plus (1Mbit/sec) Serial Clock Line LOW Period
 * @FPTD: Fast Mode Plus (1Mbit/sec) Serial Data Line Transmit HOLD Period
 * @FPRD: Fast Mode Plus (1Mbit/sec) Serial Data Line Receive HOLD Period
 * @HMHX: High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line HIGH Period
 * @HMLX: High Speed Mode Plus (3.4Mbits/sec) Serial Clock Line LOW Period
 * @HMTD: High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Transmit HOLD Period
 * @HMRD: High Speed Mode Plus (3.4Mbits/sec) Serial Data Line Receive HOLD Period
 * @HMSL: Maximum length (in ic_clk_cycles) of suppressed spikes in High Speed Mode
 *
 * Those properties get from QUICKI2C_ACPI_METHOD_NAME_ISUB method, used for
 * I2C timing configure.
 */
struct quicki2c_subip_acpi_config {
	u64 SMHX;
	u64 SMLX;
	u64 SMTD;
	u64 SMRD;

	u64 FMHX;
	u64 FMLX;
	u64 FMTD;
	u64 FMRD;
	u64 FMSL;

	u64 FPHX;
	u64 FPLX;
	u64 FPTD;
	u64 FPRD;

	u64 HMHX;
	u64 HMLX;
	u64 HMTD;
	u64 HMRD;
	u64 HMSL;
};

struct device;
struct pci_dev;
struct thc_device;
struct hid_device;
struct acpi_device;

/**
 * struct quicki2c_device -  THC QuickI2C device struct
@@ -36,10 +125,18 @@ struct hid_device;
 * @pdev: point to PCI device
 * @thc_hw: point to THC device
 * @hid_dev: point to hid device
 * @acpi_dev: point to ACPI device
 * @driver_data: point to quicki2c specific driver data
 * @state: THC I2C device state
 * @mem_addr: MMIO memory address
 * @dev_desc: device descriptor for HIDI2C protocol
 * @i2c_slave_addr: HIDI2C device slave address
 * @hid_desc_addr: Register address for retrieve HID device descriptor
 * @active_ltr_val: THC active LTR value
 * @low_power_ltr_val: THC low power LTR value
 * @i2c_speed_mode: 0 - standard mode, 1 - fast mode, 2 - fast mode plus
 * @i2c_clock_hcnt: I2C CLK high period time (unit in cycle count)
 * @i2c_clock_lcnt: I2C CLK low period time (unit in cycle count)
 * @report_descriptor: store a copy of device report descriptor
 */
struct quicki2c_device {
@@ -47,11 +144,21 @@ struct quicki2c_device {
	struct pci_dev *pdev;
	struct thc_device *thc_hw;
	struct hid_device *hid_dev;
	struct acpi_device *acpi_dev;
	enum quicki2c_dev_state state;

	void __iomem *mem_addr;

	struct hidi2c_dev_descriptor dev_desc;
	u8 i2c_slave_addr;
	u16 hid_desc_addr;

	u32 active_ltr_val;
	u32 low_power_ltr_val;

	u32 i2c_speed_mode;
	u32 i2c_clock_hcnt;
	u32 i2c_clock_lcnt;

	u8 *report_descriptor;
};