summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.com>2026-06-16 22:56:57 +0300
committerJiri Kosina <jkosina@suse.com>2026-06-16 22:56:57 +0300
commit50d9413e70de18af16bea74fe5d4cb01fa18cec1 (patch)
tree6f0ac060384b9f09e5e16fb626af2d51b9fe73ed
parent2f67fc40034a3b557450c50b3d6e027867d04b5a (diff)
parentefab84c398c17d2575e8a308c243915baae3affa (diff)
downloadlinux-50d9413e70de18af16bea74fe5d4cb01fa18cec1.tar.xz
Merge branch 'for-7.2/cp2112' into for-linus
- fwnode support for cp2112 (Danny Kaehn) - fix for cp2112 firmware-based speed configuration, if available (Danny Kaehn)
-rw-r--r--drivers/hid/hid-cp2112.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 803b883ae875..04379db93571 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
@@ -29,6 +29,18 @@
#include <linux/usb/ch9.h>
#include "hid-ids.h"
+/**
+ * enum cp2112_child_acpi_cell_addrs - Child ACPI addresses for CP2112 sub-functions
+ * Note that the enum values are explicitly defined, as this defines the interface
+ * between ACPI and Linux
+ * @CP2112_I2C_ADR: Address for I2C node
+ * @CP2112_GPIO_ADR: Address for GPIO node
+ */
+enum cp2112_child_acpi_cell_addrs {
+ CP2112_I2C_ADR = 0,
+ CP2112_GPIO_ADR = 1,
+};
+
#define CP2112_REPORT_MAX_LENGTH 64
#define CP2112_GPIO_CONFIG_LENGTH 5
#define CP2112_GPIO_GET_LENGTH 2
@@ -1208,7 +1220,11 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
struct cp2112_device *dev;
u8 buf[3];
struct cp2112_smbus_config_report config;
+ struct fwnode_handle *cp2112_fwnode;
+ struct fwnode_handle *child;
struct gpio_irq_chip *girq;
+ struct i2c_timings timings;
+ u32 addr;
int ret;
dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -1226,6 +1242,28 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
+ cp2112_fwnode = dev_fwnode(&hdev->dev);
+ if (is_acpi_device_node(cp2112_fwnode)) {
+ fwnode_for_each_child_node(cp2112_fwnode, child) {
+ ret = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), &addr);
+ if (ret)
+ continue;
+
+ switch (addr) {
+ case CP2112_I2C_ADR:
+ device_set_node(&dev->adap.dev, child);
+ break;
+ case CP2112_GPIO_ADR:
+ dev->gc.fwnode = child;
+ break;
+ }
+ }
+ } else if (is_of_node(cp2112_fwnode)) {
+ child = fwnode_get_named_child_node(cp2112_fwnode, "i2c");
+ device_set_node(&dev->adap.dev, child);
+ fwnode_handle_put(child);
+ }
+
ret = hid_parse(hdev);
if (ret) {
hid_err(hdev, "parse failed\n");
@@ -1271,6 +1309,9 @@ static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_power_normal;
}
+ i2c_parse_fw_timings(&dev->adap.dev, &timings, true);
+
+ config.clock_speed = cpu_to_be32(timings.bus_freq_hz);
config.retry_time = cpu_to_be16(1);
ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config),