|
1 | 1 | /* SPDX-License-Identifier: GPL-2.0 */
|
2 | 2 | /* Copyright (c) 2024 Intel Corporation */
|
3 | 3 |
|
| 4 | +#include <linux/acpi.h> |
4 | 5 | #include <linux/device.h>
|
5 | 6 | #include <linux/dma-mapping.h>
|
6 | 7 | #include <linux/err.h>
|
|
9 | 10 | #include <linux/pci.h>
|
10 | 11 |
|
11 | 12 | #include "intel-thc-dev.h"
|
| 13 | +#include "intel-thc-hw.h" |
12 | 14 |
|
13 | 15 | #include "quicki2c-dev.h"
|
14 | 16 |
|
| 17 | +/* THC QuickI2C ACPI method to get device properties */ |
| 18 | +/* HIDI2C device method */ |
| 19 | +static guid_t i2c_hid_guid = |
| 20 | + GUID_INIT(0x3cdff6f7, 0x4267, 0x4555, 0xad, 0x05, 0xb3, 0x0a, 0x3d, 0x89, 0x38, 0xde); |
| 21 | + |
| 22 | +/* platform method */ |
| 23 | +static guid_t thc_platform_guid = |
| 24 | + GUID_INIT(0x84005682, 0x5b71, 0x41a4, 0x8d, 0x66, 0x81, 0x30, 0xf7, 0x87, 0xa1, 0x38); |
| 25 | + |
| 26 | +/** |
| 27 | + * quicki2c_acpi_get_dsm_property - Query device ACPI DSM parameter |
| 28 | + * |
| 29 | + * @adev: point to ACPI device |
| 30 | + * @guid: ACPI method's guid |
| 31 | + * @rev: ACPI method's revision |
| 32 | + * @func: ACPI method's function number |
| 33 | + * @type: ACPI parameter's data type |
| 34 | + * @prop_buf: point to return buffer |
| 35 | + * |
| 36 | + * This is a helper function for device to query its ACPI DSM parameters. |
| 37 | + * |
| 38 | + * Return: 0 if success or ENODEV on failed. |
| 39 | + */ |
| 40 | +static int quicki2c_acpi_get_dsm_property(struct acpi_device *adev, const guid_t *guid, |
| 41 | + u64 rev, u64 func, acpi_object_type type, void *prop_buf) |
| 42 | +{ |
| 43 | + acpi_handle handle = acpi_device_handle(adev); |
| 44 | + union acpi_object *obj; |
| 45 | + |
| 46 | + obj = acpi_evaluate_dsm_typed(handle, guid, rev, func, NULL, type); |
| 47 | + if (!obj) { |
| 48 | + acpi_handle_err(handle, |
| 49 | + "Error _DSM call failed, rev: %d, func: %d, type: %d\n", |
| 50 | + (int)rev, (int)func, (int)type); |
| 51 | + return -ENODEV; |
| 52 | + } |
| 53 | + |
| 54 | + if (type == ACPI_TYPE_INTEGER) |
| 55 | + *(u32 *)prop_buf = (u32)obj->integer.value; |
| 56 | + else if (type == ACPI_TYPE_BUFFER) |
| 57 | + memcpy(prop_buf, obj->buffer.pointer, obj->buffer.length); |
| 58 | + |
| 59 | + ACPI_FREE(obj); |
| 60 | + |
| 61 | + return 0; |
| 62 | +} |
| 63 | + |
| 64 | +/** |
| 65 | + * quicki2c_acpi_get_dsd_property - Query device ACPI DSD parameter |
| 66 | + * |
| 67 | + * @adev: point to ACPI device |
| 68 | + * @dsd_method_name: ACPI method's property name |
| 69 | + * @type: ACPI parameter's data type |
| 70 | + * @prop_buf: point to return buffer |
| 71 | + * |
| 72 | + * This is a helper function for device to query its ACPI DSD parameters. |
| 73 | + * |
| 74 | + * Return: 0 if success or ENODEV on failed. |
| 75 | + */ |
| 76 | +static int quicki2c_acpi_get_dsd_property(struct acpi_device *adev, acpi_string dsd_method_name, |
| 77 | + acpi_object_type type, void *prop_buf) |
| 78 | +{ |
| 79 | + acpi_handle handle = acpi_device_handle(adev); |
| 80 | + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; |
| 81 | + union acpi_object obj = { .type = type }; |
| 82 | + struct acpi_object_list arg_list = { |
| 83 | + .count = 1, |
| 84 | + .pointer = &obj, |
| 85 | + }; |
| 86 | + union acpi_object *ret_obj; |
| 87 | + acpi_status status; |
| 88 | + |
| 89 | + status = acpi_evaluate_object(handle, dsd_method_name, &arg_list, &buffer); |
| 90 | + if (ACPI_FAILURE(status)) { |
| 91 | + acpi_handle_err(handle, |
| 92 | + "Can't evaluate %s method: %d\n", dsd_method_name, status); |
| 93 | + return -ENODEV; |
| 94 | + } |
| 95 | + |
| 96 | + ret_obj = buffer.pointer; |
| 97 | + |
| 98 | + memcpy(prop_buf, ret_obj->buffer.pointer, ret_obj->buffer.length); |
| 99 | + |
| 100 | + return 0; |
| 101 | +} |
| 102 | + |
| 103 | +/** |
| 104 | + * quicki2c_get_acpi_resources - Query all quicki2c devices' ACPI parameters |
| 105 | + * |
| 106 | + * @qcdev: point to quicki2c device |
| 107 | + * |
| 108 | + * This function gets all quicki2c devices' ACPI resource. |
| 109 | + * |
| 110 | + * Return: 0 if success or error code on failed. |
| 111 | + */ |
| 112 | +static int quicki2c_get_acpi_resources(struct quicki2c_device *qcdev) |
| 113 | +{ |
| 114 | + struct acpi_device *adev = ACPI_COMPANION(qcdev->dev); |
| 115 | + struct quicki2c_subip_acpi_parameter i2c_param; |
| 116 | + struct quicki2c_subip_acpi_config i2c_config; |
| 117 | + int ret = -EINVAL; |
| 118 | + |
| 119 | + if (!adev) { |
| 120 | + dev_err(qcdev->dev, "Invalid acpi device pointer\n"); |
| 121 | + return ret; |
| 122 | + } |
| 123 | + |
| 124 | + qcdev->acpi_dev = adev; |
| 125 | + |
| 126 | + ret = quicki2c_acpi_get_dsm_property(adev, &i2c_hid_guid, |
| 127 | + QUICKI2C_ACPI_REVISION_NUM, |
| 128 | + QUICKI2C_ACPI_FUNC_NUM_HID_DESC_ADDR, |
| 129 | + ACPI_TYPE_INTEGER, |
| 130 | + &qcdev->hid_desc_addr); |
| 131 | + if (ret) |
| 132 | + return ret; |
| 133 | + |
| 134 | + ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid, |
| 135 | + QUICKI2C_ACPI_REVISION_NUM, |
| 136 | + QUICKI2C_ACPI_FUNC_NUM_ACTIVE_LTR_VAL, |
| 137 | + ACPI_TYPE_INTEGER, |
| 138 | + &qcdev->active_ltr_val); |
| 139 | + if (ret) |
| 140 | + return ret; |
| 141 | + |
| 142 | + ret = quicki2c_acpi_get_dsm_property(adev, &thc_platform_guid, |
| 143 | + QUICKI2C_ACPI_REVISION_NUM, |
| 144 | + QUICKI2C_ACPI_FUNC_NUM_LP_LTR_VAL, |
| 145 | + ACPI_TYPE_INTEGER, |
| 146 | + &qcdev->low_power_ltr_val); |
| 147 | + if (ret) |
| 148 | + return ret; |
| 149 | + |
| 150 | + ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ICRS, |
| 151 | + ACPI_TYPE_BUFFER, &i2c_param); |
| 152 | + if (ret) |
| 153 | + return ret; |
| 154 | + |
| 155 | + if (i2c_param.addressing_mode != HIDI2C_ADDRESSING_MODE_7BIT) |
| 156 | + return -EOPNOTSUPP; |
| 157 | + |
| 158 | + qcdev->i2c_slave_addr = i2c_param.device_address; |
| 159 | + |
| 160 | + ret = quicki2c_acpi_get_dsd_property(adev, QUICKI2C_ACPI_METHOD_NAME_ISUB, |
| 161 | + ACPI_TYPE_BUFFER, &i2c_config); |
| 162 | + if (ret) |
| 163 | + return ret; |
| 164 | + |
| 165 | + if (i2c_param.connection_speed > 0 && |
| 166 | + i2c_param.connection_speed <= QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED) { |
| 167 | + qcdev->i2c_speed_mode = THC_I2C_STANDARD; |
| 168 | + qcdev->i2c_clock_hcnt = i2c_config.SMHX; |
| 169 | + qcdev->i2c_clock_lcnt = i2c_config.SMLX; |
| 170 | + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_STANDARD_MODE_MAX_SPEED && |
| 171 | + i2c_param.connection_speed <= QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED) { |
| 172 | + qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS; |
| 173 | + qcdev->i2c_clock_hcnt = i2c_config.FMHX; |
| 174 | + qcdev->i2c_clock_lcnt = i2c_config.FMLX; |
| 175 | + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FAST_MODE_MAX_SPEED && |
| 176 | + i2c_param.connection_speed <= QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED) { |
| 177 | + qcdev->i2c_speed_mode = THC_I2C_FAST_AND_PLUS; |
| 178 | + qcdev->i2c_clock_hcnt = i2c_config.FPHX; |
| 179 | + qcdev->i2c_clock_lcnt = i2c_config.FPLX; |
| 180 | + } else if (i2c_param.connection_speed > QUICKI2C_SUBIP_FASTPLUS_MODE_MAX_SPEED && |
| 181 | + i2c_param.connection_speed <= QUICKI2C_SUBIP_HIGH_SPEED_MODE_MAX_SPEED) { |
| 182 | + qcdev->i2c_speed_mode = THC_I2C_HIGH_SPEED; |
| 183 | + qcdev->i2c_clock_hcnt = i2c_config.HMHX; |
| 184 | + qcdev->i2c_clock_lcnt = i2c_config.HMLX; |
| 185 | + } else { |
| 186 | + return -EOPNOTSUPP; |
| 187 | + } |
| 188 | + |
| 189 | + return 0; |
| 190 | +} |
| 191 | + |
15 | 192 | /**
|
16 | 193 | * quicki2c_irq_quick_handler - The ISR of the quicki2c driver
|
17 | 194 | *
|
@@ -92,12 +269,25 @@ static struct quicki2c_device *quicki2c_dev_init(struct pci_dev *pdev, void __io
|
92 | 269 | return ERR_PTR(ret);
|
93 | 270 | }
|
94 | 271 |
|
| 272 | + ret = quicki2c_get_acpi_resources(qcdev); |
| 273 | + if (ret) { |
| 274 | + dev_err_once(dev, "Get ACPI resources failed, ret = %d\n", ret); |
| 275 | + return ERR_PTR(ret); |
| 276 | + } |
| 277 | + |
95 | 278 | ret = thc_port_select(qcdev->thc_hw, THC_PORT_TYPE_I2C);
|
96 | 279 | if (ret) {
|
97 | 280 | dev_err_once(dev, "Failed to select THC port, ret = %d.\n", ret);
|
98 | 281 | return ERR_PTR(ret);
|
99 | 282 | }
|
100 | 283 |
|
| 284 | + ret = thc_i2c_subip_init(qcdev->thc_hw, qcdev->i2c_slave_addr, |
| 285 | + qcdev->i2c_speed_mode, |
| 286 | + qcdev->i2c_clock_hcnt, |
| 287 | + qcdev->i2c_clock_lcnt); |
| 288 | + if (ret) |
| 289 | + return ERR_PTR(ret); |
| 290 | + |
101 | 291 | thc_interrupt_config(qcdev->thc_hw);
|
102 | 292 |
|
103 | 293 | thc_interrupt_enable(qcdev->thc_hw, true);
|
|
0 commit comments