From 033234bdc6cfb88a797a16aa3a9df815bbe01a28 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Apr 2025 13:13:33 +0200 Subject: [PATCH 01/16] platform/x86: int3472: Make regulator supply name configurable MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This is a preparation patch for registering multiple regulators, which requires a different supply-name for each regulator. Make supply-name a parameter to skl_int3472_register_regulator() and use con-id to set it so that the existing int3472_gpio_map remapping can be used with it. Since supply-name is a parameter now, drop the fixed skl_int3472_regulator_map_supplies[] array and instead add lower- and upper-case mappings of the passed-in supply-name to the regulator. Signed-off-by: Hans de Goede Reviewed-by: Andy Shevchenko Tested-by: David Heidelberg # Dell Latitude 9440 Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20250417111337.38142-6-hdegoede@redhat.com [ij: GPIO_SUPPPLY_NAME_LENGTH -> GPIO_SUPPLY_NAME_LENGTH] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/int3472/clk_and_regulator.c | 50 ++++++++----------- drivers/platform/x86/intel/int3472/common.h | 8 ++- drivers/platform/x86/intel/int3472/discrete.c | 4 +- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 374a99dea7d1..1d116b6a4efd 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -185,42 +185,37 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472) clk_unregister(int3472->clock.clk); } -/* - * The INT3472 device is going to be the only supplier of a regulator for - * the sensor device. But unlike the clk framework the regulator framework - * does not allow matching by consumer-device-name only. - * - * Ideally all sensor drivers would use "avdd" as supply-id. But for drivers - * where this cannot be changed because another supply-id is already used in - * e.g. DeviceTree files an alias for the other supply-id can be added here. - * - * Do not forget to update GPIO_REGULATOR_SUPPLY_MAP_COUNT when changing this. - */ -static const char * const skl_int3472_regulator_map_supplies[] = { - "avdd", - "AVDD", -}; - -static_assert(ARRAY_SIZE(skl_int3472_regulator_map_supplies) == - GPIO_REGULATOR_SUPPLY_MAP_COUNT); - int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *supply_name, const char *second_sensor) { struct regulator_init_data init_data = { }; + struct int3472_gpio_regulator *regulator; struct regulator_config cfg = { }; int i, j; - for (i = 0, j = 0; i < ARRAY_SIZE(skl_int3472_regulator_map_supplies); i++) { - int3472->regulator.supply_map[j].supply = skl_int3472_regulator_map_supplies[i]; - int3472->regulator.supply_map[j].dev_name = int3472->sensor_name; + if (strlen(supply_name) >= GPIO_SUPPLY_NAME_LENGTH) { + dev_err(int3472->dev, "supply-name '%s' length too long\n", supply_name); + return -E2BIG; + } + + regulator = &int3472->regulator; + string_upper(regulator->supply_name_upper, supply_name); + + /* The below code assume that map-count is 2 (upper- and lower-case) */ + static_assert(GPIO_REGULATOR_SUPPLY_MAP_COUNT == 2); + + for (i = 0, j = 0; i < GPIO_REGULATOR_SUPPLY_MAP_COUNT; i++) { + const char *supply = i ? regulator->supply_name_upper : supply_name; + + regulator->supply_map[j].supply = supply; + regulator->supply_map[j].dev_name = int3472->sensor_name; j++; if (second_sensor) { - int3472->regulator.supply_map[j].supply = - skl_int3472_regulator_map_supplies[i]; - int3472->regulator.supply_map[j].dev_name = second_sensor; + regulator->supply_map[j].supply = supply; + regulator->supply_map[j].dev_name = second_sensor; j++; } } @@ -229,9 +224,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, init_data.consumer_supplies = int3472->regulator.supply_map; init_data.num_consumer_supplies = j; - snprintf(int3472->regulator.regulator_name, - sizeof(int3472->regulator.regulator_name), "%s-regulator", - acpi_dev_name(int3472->adev)); + snprintf(regulator->regulator_name, sizeof(regulator->regulator_name), "%s-%s", + acpi_dev_name(int3472->adev), supply_name); int3472->regulator.rdesc = INT3472_REGULATOR( int3472->regulator.regulator_name, diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 3728f3864a84..4bfd60df06de 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -26,7 +26,11 @@ #define INT3472_PDEV_MAX_NAME_LEN 23 #define INT3472_MAX_SENSOR_GPIOS 3 -#define GPIO_REGULATOR_NAME_LENGTH 21 +/* E.g. "avdd\0" */ +#define GPIO_SUPPLY_NAME_LENGTH 5 +/* 12 chars for acpi_dev_name() + "-", e.g. "ABCD1234:00-" */ +#define GPIO_REGULATOR_NAME_LENGTH (12 + GPIO_SUPPLY_NAME_LENGTH) +/* lower- and upper-case mapping */ #define GPIO_REGULATOR_SUPPLY_MAP_COUNT 2 #define INT3472_LED_MAX_NAME_LEN 32 @@ -85,6 +89,7 @@ struct int3472_discrete_device { struct int3472_gpio_regulator { /* SUPPLY_MAP_COUNT * 2 to make room for second sensor mappings */ struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT * 2]; + char supply_name_upper[GPIO_SUPPLY_NAME_LENGTH]; char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; struct regulator_dev *rdev; struct regulator_desc rdesc; @@ -129,6 +134,7 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *supply_name, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index cbf39459bdf0..f6dae82739e5 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -188,7 +188,7 @@ static void int3472_get_con_id_and_polarity(struct acpi_device *adev, u8 *type, *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_POWER_ENABLE: - *con_id = "power-enable"; + *con_id = "avdd"; *gpio_flags = GPIO_ACTIVE_HIGH; break; default: @@ -311,7 +311,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_POWER_ENABLE: - ret = skl_int3472_register_regulator(int3472, gpio, + ret = skl_int3472_register_regulator(int3472, gpio, con_id, int3472->quirks.avdd_second_sensor); if (ret) err_msg = "Failed to map regulator to sensor\n"; -- 2.51.0 From ccda394e1ef17c3de33e31e7a4d2e647be6a362d Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Apr 2025 13:13:34 +0200 Subject: [PATCH 02/16] platform/x86: int3472: Avoid GPIO regulator spikes MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Avoid the GPIO controlling the avdd regulator being driven low or high for a very short time leading to spikes by adding an enable delay of 2 ms and a minimum off to on delay of also 2 ms. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Tested-by: David Heidelberg # Dell Latitude 9440 Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20250417111337.38142-7-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/int3472/clk_and_regulator.c | 7 ++++--- drivers/platform/x86/intel/int3472/common.h | 13 ++++++++++++- drivers/platform/x86/intel/int3472/discrete.c | 4 +++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 1d116b6a4efd..0a46d48896ed 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -187,6 +187,7 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472) int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + unsigned int enable_time, const char *supply_name, const char *second_sensor) { @@ -227,9 +228,9 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, snprintf(regulator->regulator_name, sizeof(regulator->regulator_name), "%s-%s", acpi_dev_name(int3472->adev), supply_name); - int3472->regulator.rdesc = INT3472_REGULATOR( - int3472->regulator.regulator_name, - &int3472_gpio_regulator_ops); + regulator->rdesc = INT3472_REGULATOR(regulator->regulator_name, + &int3472_gpio_regulator_ops, + enable_time, GPIO_REGULATOR_OFF_ON_DELAY); cfg.dev = &int3472->adev->dev; cfg.init_data = &init_data; diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 4bfd60df06de..2af8dc1ed25e 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -32,17 +32,27 @@ #define GPIO_REGULATOR_NAME_LENGTH (12 + GPIO_SUPPLY_NAME_LENGTH) /* lower- and upper-case mapping */ #define GPIO_REGULATOR_SUPPLY_MAP_COUNT 2 +/* + * Ensure the GPIO is driven low/high for at least 2 ms before changing. + * + * 2 ms has been chosen because it is the minimum time ovXXXX sensors need to + * have their reset line driven logical high to properly register a reset. + */ +#define GPIO_REGULATOR_ENABLE_TIME (2 * USEC_PER_MSEC) +#define GPIO_REGULATOR_OFF_ON_DELAY (2 * USEC_PER_MSEC) #define INT3472_LED_MAX_NAME_LEN 32 #define CIO2_SENSOR_SSDB_MCLKSPEED_OFFSET 86 -#define INT3472_REGULATOR(_name, _ops) \ +#define INT3472_REGULATOR(_name, _ops, _enable_time, _off_on_delay) \ (const struct regulator_desc) { \ .name = _name, \ .type = REGULATOR_VOLTAGE, \ .ops = _ops, \ .owner = THIS_MODULE, \ + .enable_time = _enable_time, \ + .off_on_delay = _off_on_delay, \ } #define to_int3472_clk(hw) \ @@ -134,6 +144,7 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472); int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + unsigned int enable_time, const char *supply_name, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index f6dae82739e5..a2db4fae0e6d 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -311,7 +311,9 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_POWER_ENABLE: - ret = skl_int3472_register_regulator(int3472, gpio, con_id, + ret = skl_int3472_register_regulator(int3472, gpio, + GPIO_REGULATOR_ENABLE_TIME, + con_id, int3472->quirks.avdd_second_sensor); if (ret) err_msg = "Failed to map regulator to sensor\n"; -- 2.51.0 From 4455dcf578ae83a3c7f751c2c9758efe5ba08a38 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Apr 2025 13:13:35 +0200 Subject: [PATCH 03/16] platform/x86: int3472: Prepare for registering more than 1 GPIO regulator MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Prepare the discrete code for registering more than 1 GPIO regulator. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Tested-by: David Heidelberg # Dell Latitude 9440 Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20250417111337.38142-8-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/int3472/clk_and_regulator.c | 21 ++++++++++++------- drivers/platform/x86/intel/int3472/common.h | 20 +++++++++++------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 0a46d48896ed..c85cbfbc16c1 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -196,12 +196,17 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, struct regulator_config cfg = { }; int i, j; + if (int3472->n_regulator_gpios >= INT3472_MAX_REGULATORS) { + dev_err(int3472->dev, "Too many regulators mapped\n"); + return -EINVAL; + } + if (strlen(supply_name) >= GPIO_SUPPLY_NAME_LENGTH) { dev_err(int3472->dev, "supply-name '%s' length too long\n", supply_name); return -E2BIG; } - regulator = &int3472->regulator; + regulator = &int3472->regulators[int3472->n_regulator_gpios]; string_upper(regulator->supply_name_upper, supply_name); /* The below code assume that map-count is 2 (upper- and lower-case) */ @@ -222,7 +227,7 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, } init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS; - init_data.consumer_supplies = int3472->regulator.supply_map; + init_data.consumer_supplies = regulator->supply_map; init_data.num_consumer_supplies = j; snprintf(regulator->regulator_name, sizeof(regulator->regulator_name), "%s-%s", @@ -236,14 +241,16 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, cfg.init_data = &init_data; cfg.ena_gpiod = gpio; - int3472->regulator.rdev = regulator_register(int3472->dev, - &int3472->regulator.rdesc, - &cfg); + regulator->rdev = regulator_register(int3472->dev, ®ulator->rdesc, &cfg); + if (IS_ERR(regulator->rdev)) + return PTR_ERR(regulator->rdev); - return PTR_ERR_OR_ZERO(int3472->regulator.rdev); + int3472->n_regulator_gpios++; + return 0; } void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472) { - regulator_unregister(int3472->regulator.rdev); + for (int i = 0; i < int3472->n_regulator_gpios; i++) + regulator_unregister(int3472->regulators[i].rdev); } diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 2af8dc1ed25e..3d8b5c5ff236 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -25,6 +25,7 @@ #define INT3472_PDEV_MAX_NAME_LEN 23 #define INT3472_MAX_SENSOR_GPIOS 3 +#define INT3472_MAX_REGULATORS 3 /* E.g. "avdd\0" */ #define GPIO_SUPPLY_NAME_LENGTH 5 @@ -88,6 +89,15 @@ struct int3472_discrete_quirks { const char *avdd_second_sensor; }; +struct int3472_gpio_regulator { + /* SUPPLY_MAP_COUNT * 2 to make room for second sensor mappings */ + struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT * 2]; + char supply_name_upper[GPIO_SUPPLY_NAME_LENGTH]; + char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; + struct regulator_dev *rdev; + struct regulator_desc rdesc; +}; + struct int3472_discrete_device { struct acpi_device *adev; struct device *dev; @@ -96,14 +106,7 @@ struct int3472_discrete_device { const struct int3472_sensor_config *sensor_config; - struct int3472_gpio_regulator { - /* SUPPLY_MAP_COUNT * 2 to make room for second sensor mappings */ - struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT * 2]; - char supply_name_upper[GPIO_SUPPLY_NAME_LENGTH]; - char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; - struct regulator_dev *rdev; - struct regulator_desc rdesc; - } regulator; + struct int3472_gpio_regulator regulators[INT3472_MAX_REGULATORS]; struct int3472_clock { struct clk *clk; @@ -125,6 +128,7 @@ struct int3472_discrete_device { unsigned int ngpios; /* how many GPIOs have we seen */ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ + unsigned int n_regulator_gpios; /* how many have we mapped to a regulator */ struct gpiod_lookup_table gpios; }; -- 2.51.0 From c5d0393272048748ace2dd4ff8326fc0bf70b262 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Apr 2025 13:13:36 +0200 Subject: [PATCH 04/16] platform/x86: int3472: Add handshake pin support MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit New Intel Meteor Lake based laptops with IPU6 cameras have a new type 0x12 pin defined in the INT3472 sensor companion device which describes the sensor's GPIOs. This pin is primarily used on designs with a Lattice FPGA chip which is capable of running the sensor independently of the main CPU for features like presence detection. This pin needs to be driven high to make the FPGA run the power-on sequence of the sensor. After driving the pin high, the FPGA "firmware" needs 25ms to complete the power-on sequence. Add support for this modelling the handshake pin as a GPIO driven "dvdd" regulator with a 25 ms enable time. This model was chosen because: 1. Sensor chips don't have a handshake pin, so we need to abstract this in some way which does not require modification to the sensor drivers, sensor drivers using the bulk-regulator API to get avdd + vddio + dvdd is normal. So this will work to get the right value set to the handshake pin without requiring sensor driver modifications. 2. Sensors typically wait only a small time for the sensor to power-on after de-asserting reset. Not the 25ms the Lattice chip requires. Using the regulator framework's enable_time allows hiding the need for this delay from the sensor drivers. Link: https://lore.kernel.org/platform-driver-x86/59f672c3-6d87-4ec7-9b7f-f44fe2cce934@redhat.com/ Link: https://bugzilla.redhat.com/show_bug.cgi?id=2341731 Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Tested-by: David Heidelberg # Dell Latitude 9440 Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20250417111337.38142-9-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/common.h | 1 + drivers/platform/x86/intel/int3472/discrete.c | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/int3472/common.h b/drivers/platform/x86/intel/int3472/common.h index 3d8b5c5ff236..51b818e62a25 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/drivers/platform/x86/intel/int3472/common.h @@ -22,6 +22,7 @@ #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d +#define INT3472_GPIO_TYPE_HANDSHAKE 0x12 #define INT3472_PDEV_MAX_NAME_LEN 23 #define INT3472_MAX_SENSOR_GPIOS 3 diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index a2db4fae0e6d..bcc7bb122a56 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -191,6 +191,10 @@ static void int3472_get_con_id_and_polarity(struct acpi_device *adev, u8 *type, *con_id = "avdd"; *gpio_flags = GPIO_ACTIVE_HIGH; break; + case INT3472_GPIO_TYPE_HANDSHAKE: + *con_id = "dvdd"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; default: *con_id = "unknown"; *gpio_flags = GPIO_ACTIVE_HIGH; @@ -290,6 +294,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: case INT3472_GPIO_TYPE_POWER_ENABLE: + case INT3472_GPIO_TYPE_HANDSHAKE: gpio = skl_int3472_gpiod_get_from_temp_lookup(int3472, agpio, con_id, gpio_flags); if (IS_ERR(gpio)) { ret = PTR_ERR(gpio); @@ -316,7 +321,16 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, con_id, int3472->quirks.avdd_second_sensor); if (ret) - err_msg = "Failed to map regulator to sensor\n"; + err_msg = "Failed to map power-enable to sensor\n"; + + break; + case INT3472_GPIO_TYPE_HANDSHAKE: + /* Setups using a handshake pin need 25 ms enable delay */ + ret = skl_int3472_register_regulator(int3472, gpio, + 25 * USEC_PER_MSEC, + con_id, NULL); + if (ret) + err_msg = "Failed to map handshake to sensor\n"; break; default: /* Never reached */ -- 2.51.0 From 4d1e8c8f11c611db5828e4bae7292bc295eea8ef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 17 Apr 2025 13:13:37 +0200 Subject: [PATCH 05/16] platform/x86: int3472: Debug log when remapping pins MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Debug log when remapping a pin/function because of a int3472_gpio_map[] match. Reviewed-by: Andy Shevchenko Signed-off-by: Hans de Goede Tested-by: David Heidelberg # Dell Latitude 9440 Reviewed-by: Sakari Ailus Link: https://lore.kernel.org/r/20250417111337.38142-10-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index bcc7bb122a56..394975f55d64 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -146,9 +146,10 @@ static const struct int3472_gpio_map int3472_gpio_map[] = { { "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" }, }; -static void int3472_get_con_id_and_polarity(struct acpi_device *adev, u8 *type, +static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3472, u8 *type, const char **con_id, unsigned long *gpio_flags) { + struct acpi_device *adev = int3472->sensor; unsigned int i; for (i = 0; i < ARRAY_SIZE(int3472_gpio_map); i++) { @@ -163,6 +164,9 @@ static void int3472_get_con_id_and_polarity(struct acpi_device *adev, u8 *type, if (!acpi_dev_hid_uid_match(adev, int3472_gpio_map[i].hid, NULL)) continue; + dev_dbg(int3472->dev, "mapping type 0x%02x pin to 0x%02x %s\n", + *type, int3472_gpio_map[i].type_to, int3472_gpio_map[i].con_id); + *type = int3472_gpio_map[i].type_to; *gpio_flags = int3472_gpio_map[i].polarity_low ? GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH; @@ -267,7 +271,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, type = FIELD_GET(INT3472_GPIO_DSM_TYPE, obj->integer.value); - int3472_get_con_id_and_polarity(int3472->sensor, &type, &con_id, &gpio_flags); + int3472_get_con_id_and_polarity(int3472, &type, &con_id, &gpio_flags); pin = FIELD_GET(INT3472_GPIO_DSM_PIN, obj->integer.value); /* Pin field is not really used under Windows and wraps around at 8 bits */ -- 2.51.0 From 217d55ca13d22ba6af7e96ac2d28c2ef6927fc54 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:06 +0200 Subject: [PATCH 06/16] hwmon: (oxp-sensors) Distinguish the X1 variants MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently, the oxp-sensors driver fuzzy matches the X1 variants. Luckily, X1 and X1 mini share most hardware features so this works. However, they are completely different product lines, and there is an expectation that OneXPlayer will release more devices in the X1 line that may have differences. Therefore, distinguish the 3 devices that currently exist in the market. These are the OneXPlayer X1 AMD and Intel variants, and the X1 mini which only has an AMD variant. As far as registers go, all three support the current driver functionality. Reviewed-by: Derek J. Clark Acked-by: Guenter Roeck Reviewed-by: Thomas Weißschuh Reviewed-by: Ilpo Järvinen Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-2-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/hwmon/oxp-sensors.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c index 83730d931824..5a4230ad3757 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/hwmon/oxp-sensors.c @@ -205,7 +205,28 @@ static const struct dmi_system_id dmi_table[] = { { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), - DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 A"), + }, + .driver_data = (void *)oxp_x1, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 i"), + }, + .driver_data = (void *)oxp_x1, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1 mini"), + }, + .driver_data = (void *)oxp_x1, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1Pro"), }, .driver_data = (void *)oxp_x1, }, -- 2.51.0 From 9f4c9ec158fa8fa4afcdbcbff9c9a9a900dc9c2f Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:07 +0200 Subject: [PATCH 07/16] hwmon: (oxp-sensors) Add all OneXFly variants MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently, the driver only has the F1 OneXFly variant, which was based on the 7000 AMD platform. Add its special editions: F1 EVA-01, F1 OLED. F1 OLED might have been a dev unit, but it is supported by OneXConsole with the same features so add it. Then add the F1L variant which is based on the 8000 AMD platform and the F1Pro and its special edition EVA-02. One might ask why not just fuzzy match. Well, EVA-02 is a variant of F1Pro which is a Strix Point handheld, but does not have F1Pro in its name. This makes it risky to fuzzy match, as special variants in the future from different platforms might not have the same feature set or registers. By happenstance, all current devices use the same registers. For the charge limitting feature on this series, only F1Pro/X1 (AMD) were released with it, but OneXPlayer is providing bios updates for F1, F1L, X1 Mini units that use the same register, so treat all of them the same. Acked-by: Guenter Roeck Reviewed-by: Thomas Weißschuh Reviewed-by: Derek J. Clark Reviewed-by: Ilpo Järvinen Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-3-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/hwmon/oxp-sensors.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c index 5a4230ad3757..f7a64fbc8f33 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/hwmon/oxp-sensors.c @@ -188,6 +188,41 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)oxp_fly, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-01"), + }, + .driver_data = (void *)oxp_fly, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 OLED"), + }, + .driver_data = (void *)oxp_fly, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1L"), + }, + .driver_data = (void *)oxp_fly, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1Pro"), + }, + .driver_data = (void *)oxp_fly, + }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1 EVA-02"), + }, + .driver_data = (void *)oxp_fly, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), -- 2.51.0 From 3012bb39001c4cab0b8587b0421ae64b8ca54655 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:08 +0200 Subject: [PATCH 08/16] platform/x86: oxpec: Move hwmon/oxp-sensors to platform/x86 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The EC of OneXPlayer devices used to only control the fan. This is no longer the case, with the EC of OneXPlayer gaining additional functionality (turbo button, turbo led, battery controls). As it will be beneficial from a complexity perspective to retain this driver as a single unit, move it out of hwmon, and into platform/x86. Also, remove the hwmon documentation to prepare moving it to Documentation/ABI/. While at it, add myself to the maintainer's file. Acked-by: Guenter Roeck Reviewed-by: Thomas Weißschuh Reviewed-by: Derek J. Clark Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-4-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/hwmon/index.rst | 1 - Documentation/hwmon/oxp-sensors.rst | 89 ------------------- MAINTAINERS | 7 +- drivers/hwmon/Kconfig | 11 --- drivers/hwmon/Makefile | 1 - drivers/platform/x86/Kconfig | 12 +++ drivers/platform/x86/Makefile | 3 + .../oxp-sensors.c => platform/x86/oxpec.c} | 10 +-- 8 files changed, 23 insertions(+), 111 deletions(-) delete mode 100644 Documentation/hwmon/oxp-sensors.rst rename drivers/{hwmon/oxp-sensors.c => platform/x86/oxpec.c} (98%) diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index f0ddf6222c44..ffe1a756a4f9 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -189,7 +189,6 @@ Hardware Monitoring Kernel Drivers nzxt-kraken3 nzxt-smart2 occ - oxp-sensors pc87360 pc87427 pcf8591 diff --git a/Documentation/hwmon/oxp-sensors.rst b/Documentation/hwmon/oxp-sensors.rst deleted file mode 100644 index 581c4dafbfa1..000000000000 --- a/Documentation/hwmon/oxp-sensors.rst +++ /dev/null @@ -1,89 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0-or-later - -Kernel driver oxp-sensors -========================= - -Authors: - - Derek John Clark - - Joaquín Ignacio Aramendía - -Description: ------------- - -Handheld devices from OneNetbook, AOKZOE, AYANEO, And OrangePi provide fan -readings and fan control through their embedded controllers. - -Currently supports OneXPlayer devices, AOKZOE, AYANEO, and OrangePi -handheld devices. AYANEO devices preceding the AIR and OneXPlayer devices -preceding the Mini A07 are not supportable as the EC model is different -and do not have manual control capabilities. - -Some OneXPlayer and AOKZOE models have a toggle for changing the behaviour -of the "Turbo/Silent" button of the device. It will change the key event -that it triggers with a flip of the `tt_toggle` attribute. See below for -boards that support this function. - -Supported devices ------------------ - -Currently the driver supports the following handhelds: - - - AOKZOE A1 - - AOKZOE A1 PRO - - AYANEO 2 - - AYANEO 2S - - AYANEO AIR - - AYANEO AIR 1S - - AYANEO AIR Plus (Mendocino) - - AYANEO AIR Pro - - AYANEO Flip DS - - AYANEO Flip KB - - AYANEO Geek - - AYANEO Geek 1S - - AYANEO KUN - - OneXPlayer 2 - - OneXPlayer 2 Pro - - OneXPlayer AMD - - OneXPlayer mini AMD - - OneXPlayer mini AMD PRO - - OneXPlayer OneXFly - - OneXPlayer X1 A - - OneXPlayer X1 i - - OneXPlayer X1 mini - - OrangePi NEO-01 - -"Turbo/Silent" button behaviour toggle is only supported on: - - AOK ZOE A1 - - AOK ZOE A1 PRO - - OneXPlayer 2 - - OneXPlayer 2 Pro - - OneXPlayer mini AMD (only with updated alpha BIOS) - - OneXPlayer mini AMD PRO - - OneXPlayer OneXFly - - OneXPlayer X1 A - - OneXPlayer X1 i - - OneXPlayer X1 mini - -Sysfs entries -------------- - -The following attributes are supported: - -fan1_input - Read Only. Reads current fan RPM. - -pwm1_enable - Read Write. Enable manual fan control. Write "1" to set to manual, write "0" - to let the EC control de fan speed. Read this attribute to see current status. - -pwm1 - Read Write. Read this attribute to see current duty cycle in the range [0-255]. - When pwm1_enable is set to "1" (manual) write any value in the range [0-255] - to set fan speed. - -tt_toggle - Read Write. Read this attribute to check the status of the turbo/silent - button behaviour function. Write "1" to activate the switch and "0" to - deactivate it. The specific keycodes and behaviour is specific to the device - both with this function on and off. This attribute is attached to the platform - driver and not to the hwmon driver (/sys/devices/platform/oxp-platform/tt_toggle) diff --git a/MAINTAINERS b/MAINTAINERS index 08b7a6b36b7b..e1754eee9dc7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18014,12 +18014,13 @@ S: Maintained F: drivers/mtd/nand/onenand/ F: include/linux/mtd/onenand*.h -ONEXPLAYER FAN DRIVER +ONEXPLAYER PLATFORM EC DRIVER +M: Antheas Kapenekakis M: Derek John Clark M: Joaquín Ignacio Aramendía -L: linux-hwmon@vger.kernel.org +L: platform-driver-x86@vger.kernel.org S: Maintained -F: drivers/hwmon/oxp-sensors.c +F: drivers/platform/x86/oxpec.c ONIE TLV NVMEM LAYOUT DRIVER M: Miquel Raynal diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f91f713b0105..5fd93aad2d6d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1795,17 +1795,6 @@ config SENSORS_NZXT_SMART2 source "drivers/hwmon/occ/Kconfig" -config SENSORS_OXP - tristate "OneXPlayer EC fan control" - depends on ACPI_EC - depends on X86 - help - If you say yes here you get support for fan readings and control over - OneXPlayer handheld devices. Only OneXPlayer mini AMD handheld variant - boards are supported. - - Can also be built as a module. In that case it will be called oxp-sensors. - config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 766c652ef22b..e3468d024ff3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -183,7 +183,6 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o obj-$(CONFIG_SENSORS_NZXT_KRAKEN3) += nzxt-kraken3.o obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o -obj-$(CONFIG_SENSORS_OXP) += oxp-sensors.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 43407e76476b..739740c4bb53 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -1201,6 +1201,18 @@ config SEL3350_PLATFORM To compile this driver as a module, choose M here: the module will be called sel3350-platform. +config OXP_EC + tristate "OneXPlayer EC platform control" + depends on ACPI_EC + depends on HWMON + depends on X86 + help + Enables support for the platform EC of OneXPlayer and AOKZOE + handheld devices. This includes fan speed, fan controls, and + disabling the default TDP behavior of the device. Due to legacy + reasons, this driver also provides hwmon functionality to Ayaneo + devices and the OrangePi Neo. + endif # X86_PLATFORM_DEVICES config P2SB diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 650dfbebb6c8..38ffc4c98e78 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -154,3 +154,6 @@ obj-$(CONFIG_WINMATE_FM07_KEYS) += winmate-fm07-keys.o # SEL obj-$(CONFIG_SEL3350_PLATFORM) += sel3350-platform.o + +# OneXPlayer +obj-$(CONFIG_OXP_EC) += oxpec.o diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/platform/x86/oxpec.c similarity index 98% rename from drivers/hwmon/oxp-sensors.c rename to drivers/platform/x86/oxpec.c index f7a64fbc8f33..dc3a0871809c 100644 --- a/drivers/hwmon/oxp-sensors.c +++ b/drivers/platform/x86/oxpec.c @@ -1,11 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds - * that expose fan reading and control via hwmon sysfs. - * - * Old OXP boards have the same DMI strings and they are told apart by - * the boot cpu vendor (Intel/AMD). Of these older models only AMD is - * supported. + * Platform driver for OneXPlayer and AOKZOE devices. For the time being, + * it also exposes fan controls for AYANEO, and OrangePi Handhelds via + * hwmon sysfs. * * Fan control is provided via pwm interface in the range [0-255]. * Old AMD boards use [0-100] as range in the EC, the written value is @@ -16,6 +13,7 @@ * * Copyright (C) 2022 Joaquín I. Aramendía * Copyright (C) 2024 Derek J. Clark + * Copyright (C) 2025 Antheas Kapenekakis */ #include -- 2.51.0 From d9c4037fed897ddcb512fdf03bafed9451cb5006 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:09 +0200 Subject: [PATCH 09/16] ABI: testing: sysfs-class-oxp: add missing documentation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add missing documentation about the tt_toggle attribute that was added in kernel 6.5. Fixes: be144ee491272 ("hwmon: (oxp-sensors) Add tt_toggle attribute on supported boards") Reviewed-by: Thomas Weißschuh Reviewed-by: Derek J. Clark Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-5-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/ABI/testing/sysfs-platform-oxp | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-platform-oxp diff --git a/Documentation/ABI/testing/sysfs-platform-oxp b/Documentation/ABI/testing/sysfs-platform-oxp new file mode 100644 index 000000000000..091269ab2c8c --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-oxp @@ -0,0 +1,13 @@ +What: /sys/devices/platform//tt_toggle +Date: Jun 2023 +KernelVersion: 6.5 +Contact: "Antheas Kapenekakis" +Description: + Takeover TDP controls from the device. OneXPlayer devices have a + turbo button that can be used to switch between two TDP modes + (usually 15W and 25W). By setting this attribute to 1, this + functionality is disabled, handing TDP control over to (Windows) + userspace software and the Turbo button turns into a keyboard + shortcut over the AT keyboard of the device. In addition, + using this setting is a prerequisite for PWM control for most + newer models (otherwise it NOOPs). -- 2.51.0 From 3abff549e0ec05649b7a97a141399123de4c104b Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:10 +0200 Subject: [PATCH 10/16] ABI: testing: sysfs-class-oxp: add tt_led attribute documentation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Adds documentation about the tt_led attribute of OneXPlayer devices to the sysfs-class-oxp ABI documentation. Reviewed-by: Thomas Weißschuh Reviewed-by: Derek J. Clark Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-6-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/ABI/testing/sysfs-platform-oxp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-oxp b/Documentation/ABI/testing/sysfs-platform-oxp index 091269ab2c8c..b3f39fc21dfa 100644 --- a/Documentation/ABI/testing/sysfs-platform-oxp +++ b/Documentation/ABI/testing/sysfs-platform-oxp @@ -11,3 +11,15 @@ Description: shortcut over the AT keyboard of the device. In addition, using this setting is a prerequisite for PWM control for most newer models (otherwise it NOOPs). + +What: /sys/devices/platform//tt_led +Date: April 2025 +KernelVersion: 6.16 +Contact: "Antheas Kapenekakis" +Description: + Some OneXPlayer devices (e.g., X1 series) feature a little LED + nested in the Turbo button. This LED is illuminated when the + device is in the higher TDP mode (e.g., 25W). Once tt_toggle + is engaged, this LED is left dangling to its last state. This + attribute allows userspace to control the LED state manually + (either with 1 or 0). Only a subset of devices contain this LED. -- 2.51.0 From b72e0b671ddff48d2fb59e082a9e7f8ada6d7d69 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:11 +0200 Subject: [PATCH 11/16] platform/x86: oxpec: Rename ec group to tt_toggle MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently, the EC group is used for the turbo button. However, the next patch in the series adds support for the LED button in X1 devices, which is only applicable for X1 devices. Therefore, rename it to prepare for adding the second group. And make it const while at it. Reviewed-by: Derek J. Clark Reviewed-by: Thomas Weißschuh Reviewed-by: Ilpo Järvinen Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-7-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index dc3a0871809c..ee37070ec54f 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -681,18 +681,18 @@ static const struct hwmon_channel_info * const oxp_platform_sensors[] = { NULL, }; -static struct attribute *oxp_ec_attrs[] = { +static struct attribute *oxp_tt_toggle_attrs[] = { &dev_attr_tt_toggle.attr, NULL }; -static struct attribute_group oxp_ec_attribute_group = { +static const struct attribute_group oxp_tt_toggle_attribute_group = { .is_visible = tt_toggle_is_visible, - .attrs = oxp_ec_attrs, + .attrs = oxp_tt_toggle_attrs, }; static const struct attribute_group *oxp_ec_groups[] = { - &oxp_ec_attribute_group, + &oxp_tt_toggle_attribute_group, NULL }; -- 2.51.0 From 50623acafb6b1d7b66138dbed7aa0c0c170d9e4b Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:12 +0200 Subject: [PATCH 12/16] platform/x86: oxpec: Add turbo led support to X1 devices MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The X1 and X1 mini lineups feature an LED nested within their turbo button. When turbo takeover is not enabled, the turbo button allows the device to switch from 18W to 25W TDP. When the device is in the 25W TDP mode, the LED is turned on. However, when we engage turbo takeover, the turbo led remains on its last state, which might be illuminated and cannot be currently controlled. Therefore, add the register that controls it under sysfs, to allow userspace to turn it off once engaging turbo takeover and then control it as they wish. 2024 OneXPlayer devices, other than the X1s, do not have a turbo LED. However, earlier models do, so this can be extended to them as well when the register for it is found. Reviewed-by: Thomas Weißschuh Reviewed-by: Derek J. Clark Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-8-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index ee37070ec54f..97594fe61489 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -87,6 +87,12 @@ static enum oxp_board board; #define OXP_TURBO_RETURN_VAL 0x00 /* Common return val */ +/* X1 Turbo LED */ +#define OXP_X1_TURBO_LED_REG 0x57 + +#define OXP_X1_TURBO_LED_OFF 0x01 +#define OXP_X1_TURBO_LED_ON 0x02 + static const struct dmi_system_id dmi_table[] = { { .matches = { @@ -434,6 +440,73 @@ static ssize_t tt_toggle_show(struct device *dev, static DEVICE_ATTR_RW(tt_toggle); +/* Callbacks for turbo LED attribute */ +static umode_t tt_led_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + switch (board) { + case oxp_x1: + return attr->mode; + default: + break; + } + return 0; +} + +static ssize_t tt_led_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u8 reg, val; + bool value; + int ret; + + ret = kstrtobool(buf, &value); + if (ret) + return ret; + + switch (board) { + case oxp_x1: + reg = OXP_X1_TURBO_LED_REG; + val = value ? OXP_X1_TURBO_LED_ON : OXP_X1_TURBO_LED_OFF; + break; + default: + return -EINVAL; + } + + ret = write_to_ec(reg, val); + if (ret) + return ret; + + return count; +} + +static ssize_t tt_led_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + long enval; + long val; + int ret; + u8 reg; + + switch (board) { + case oxp_x1: + reg = OXP_X1_TURBO_LED_REG; + enval = OXP_X1_TURBO_LED_ON; + break; + default: + return -EINVAL; + } + + ret = read_from_ec(reg, 1, &val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", val == enval); +} + +static DEVICE_ATTR_RW(tt_led); + /* PWM enable/disable functions */ static int oxp_pwm_enable(void) { @@ -691,8 +764,19 @@ static const struct attribute_group oxp_tt_toggle_attribute_group = { .attrs = oxp_tt_toggle_attrs, }; +static struct attribute *oxp_tt_led_attrs[] = { + &dev_attr_tt_led.attr, + NULL +}; + +static const struct attribute_group oxp_tt_led_attribute_group = { + .is_visible = tt_led_is_visible, + .attrs = oxp_tt_led_attrs, +}; + static const struct attribute_group *oxp_ec_groups[] = { &oxp_tt_toggle_attribute_group, + &oxp_tt_led_attribute_group, NULL }; -- 2.51.0 From 6b89cf6d3744d150ee2611c9bfda9e781819c8a1 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:13 +0200 Subject: [PATCH 13/16] platform/x86: oxpec: Move pwm_enable read to its own function MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently, this driver breaks ABI by using auto as 0 and manual as 1. However, for pwm_enable, 0 is full speed, 1 is manual, and 2 is auto. For the correction to be possible, this means that the pwm_enable endpoint will need access to both pwm enable and value (as for the 0th value, the fan needs to be set to full power). Therefore, begin by moving the current pwm_enable read to its own function, oxp_pwm_enable. Reviewed-by: Derek J. Clark Reviewed-by: Thomas Weißschuh Reviewed-by: Ilpo Järvinen Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-9-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 50 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 97594fe61489..f5f3fbbca555 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -559,6 +559,32 @@ static int oxp_pwm_disable(void) } } +static int oxp_pwm_read(long *val) +{ + switch (board) { + case orange_pi_neo: + return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val); + case aok_zoe_a1: + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_2: + case oxp_fly: + case oxp_mini_amd: + case oxp_mini_amd_a07: + case oxp_mini_amd_pro: + case oxp_x1: + return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); + default: + return -EOPNOTSUPP; + } +} + /* Callbacks for hwmon interface */ static umode_t oxp_ec_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel) @@ -656,29 +682,7 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, } return 0; case hwmon_pwm_enable: - switch (board) { - case orange_pi_neo: - return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val); - case aok_zoe_a1: - case aya_neo_2: - case aya_neo_air: - case aya_neo_air_1s: - case aya_neo_air_plus_mendo: - case aya_neo_air_pro: - case aya_neo_flip: - case aya_neo_geek: - case aya_neo_kun: - case oxp_2: - case oxp_fly: - case oxp_mini_amd: - case oxp_mini_amd_a07: - case oxp_mini_amd_pro: - case oxp_x1: - return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); - default: - break; - } - break; + return oxp_pwm_read(val); default: break; } -- 2.51.0 From b804a9b818905a6eb9783abc18483a2ca53ee281 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:14 +0200 Subject: [PATCH 14/16] platform/x86: oxpec: Move pwm value read/write to separate functions MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently, this driver breaks hwmon ABI by using auto as 0 and manual as 1. However, for pwm_enable, 0 is full speed, 1 is manual, and 2 is auto. For the correction to be possible, this means that the pwm_enable endpoint will need access to both pwm enable and value (as for the 0th value, the fan needs to be set to full power). Therefore, move the pwm value read/write to separate functions. Reviewed-by: Derek J. Clark Reviewed-by: Thomas Weißschuh Reviewed-by: Ilpo Järvinen Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-10-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 163 +++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 75 deletions(-) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index f5f3fbbca555..5da8e1d6073d 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -599,6 +599,92 @@ static umode_t oxp_ec_hwmon_is_visible(const void *drvdata, } } +/* PWM input read/write functions */ +static int oxp_pwm_input_write(long val) +{ + if (val < 0 || val > 255) + return -EINVAL; + + switch (board) { + case orange_pi_neo: + /* scale to range [1-244] */ + val = ((val - 1) * 243 / 254) + 1; + return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val); + case oxp_2: + case oxp_x1: + /* scale to range [0-184] */ + val = (val * 184) / 255; + return write_to_ec(OXP_SENSOR_PWM_REG, val); + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_mini_amd: + case oxp_mini_amd_a07: + /* scale to range [0-100] */ + val = (val * 100) / 255; + return write_to_ec(OXP_SENSOR_PWM_REG, val); + case aok_zoe_a1: + case oxp_fly: + case oxp_mini_amd_pro: + return write_to_ec(OXP_SENSOR_PWM_REG, val); + default: + return -EOPNOTSUPP; + } +} + +static int oxp_pwm_input_read(long *val) +{ + int ret; + + switch (board) { + case orange_pi_neo: + ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + /* scale from range [1-244] */ + *val = ((*val - 1) * 254 / 243) + 1; + break; + case oxp_2: + case oxp_x1: + ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + /* scale from range [0-184] */ + *val = (*val * 255) / 184; + break; + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_mini_amd: + case oxp_mini_amd_a07: + ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + /* scale from range [0-100] */ + *val = (*val * 255) / 100; + break; + case aok_zoe_a1: + case oxp_fly: + case oxp_mini_amd_pro: + default: + ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); + if (ret) + return ret; + break; + } + return 0; +} + static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -639,48 +725,7 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_pwm: switch (attr) { case hwmon_pwm_input: - switch (board) { - case orange_pi_neo: - ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val); - if (ret) - return ret; - /* scale from range [1-244] */ - *val = ((*val - 1) * 254 / 243) + 1; - break; - case oxp_2: - case oxp_x1: - ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); - if (ret) - return ret; - /* scale from range [0-184] */ - *val = (*val * 255) / 184; - break; - case aya_neo_2: - case aya_neo_air: - case aya_neo_air_1s: - case aya_neo_air_plus_mendo: - case aya_neo_air_pro: - case aya_neo_flip: - case aya_neo_geek: - case aya_neo_kun: - case oxp_mini_amd: - case oxp_mini_amd_a07: - ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); - if (ret) - return ret; - /* scale from range [0-100] */ - *val = (*val * 255) / 100; - break; - case aok_zoe_a1: - case oxp_fly: - case oxp_mini_amd_pro: - default: - ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val); - if (ret) - return ret; - break; - } - return 0; + return oxp_pwm_input_read(val); case hwmon_pwm_enable: return oxp_pwm_read(val); default: @@ -706,39 +751,7 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, return oxp_pwm_disable(); return -EINVAL; case hwmon_pwm_input: - if (val < 0 || val > 255) - return -EINVAL; - switch (board) { - case orange_pi_neo: - /* scale to range [1-244] */ - val = ((val - 1) * 243 / 254) + 1; - return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val); - case oxp_2: - case oxp_x1: - /* scale to range [0-184] */ - val = (val * 184) / 255; - return write_to_ec(OXP_SENSOR_PWM_REG, val); - case aya_neo_2: - case aya_neo_air: - case aya_neo_air_1s: - case aya_neo_air_plus_mendo: - case aya_neo_air_pro: - case aya_neo_flip: - case aya_neo_geek: - case aya_neo_kun: - case oxp_mini_amd: - case oxp_mini_amd_a07: - /* scale to range [0-100] */ - val = (val * 100) / 255; - return write_to_ec(OXP_SENSOR_PWM_REG, val); - case aok_zoe_a1: - case oxp_fly: - case oxp_mini_amd_pro: - return write_to_ec(OXP_SENSOR_PWM_REG, val); - default: - break; - } - break; + return oxp_pwm_input_write(val); default: break; } -- 2.51.0 From 665fab3381220cf8925b9f7c4c8d6512e47efe99 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:15 +0200 Subject: [PATCH 15/16] platform/x86: oxpec: Move fan speed read to separate function MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit While not necessary for fixing the ABI hwmon issue, fan speed will be the only remaining value without a function. Therefore, finish the refactor by moving it to a separate function. Reviewed-by: Derek J. Clark Reviewed-by: Thomas Weißschuh Reviewed-by: Ilpo Järvinen Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-11-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 53 ++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 5da8e1d6073d..918a553bd896 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -599,6 +599,34 @@ static umode_t oxp_ec_hwmon_is_visible(const void *drvdata, } } +/* Fan speed read function */ +static int oxp_pwm_fan_speed(long *val) +{ + switch (board) { + case orange_pi_neo: + return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val); + case oxp_2: + case oxp_x1: + return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); + case aok_zoe_a1: + case aya_neo_2: + case aya_neo_air: + case aya_neo_air_1s: + case aya_neo_air_plus_mendo: + case aya_neo_air_pro: + case aya_neo_flip: + case aya_neo_geek: + case aya_neo_kun: + case oxp_fly: + case oxp_mini_amd: + case oxp_mini_amd_a07: + case oxp_mini_amd_pro: + return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); + default: + return -EOPNOTSUPP; + } +} + /* PWM input read/write functions */ static int oxp_pwm_input_write(long val) { @@ -694,30 +722,7 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_fan: switch (attr) { case hwmon_fan_input: - switch (board) { - case orange_pi_neo: - return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val); - case oxp_2: - case oxp_x1: - return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val); - case aok_zoe_a1: - case aya_neo_2: - case aya_neo_air: - case aya_neo_air_1s: - case aya_neo_air_plus_mendo: - case aya_neo_air_pro: - case aya_neo_flip: - case aya_neo_geek: - case aya_neo_kun: - case oxp_fly: - case oxp_mini_amd: - case oxp_mini_amd_a07: - case oxp_mini_amd_pro: - return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); - default: - break; - } - break; + return oxp_pwm_fan_speed(val); default: break; } -- 2.51.0 From 36a65fa84ac30b8df25799dfcedda76dfd7d087b Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Fri, 25 Apr 2025 13:18:16 +0200 Subject: [PATCH 16/16] platform/x86: oxpec: Adhere to sysfs-class-hwmon and enable pwm on 2 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Currently, the driver does not adhere to the sysfs-class-hwmon specification: 0 is used for auto fan control and 1 is used for manual control. However, it is expected that 0 sets the fan to full speed, 1 sets the fan to manual, and then 2 is used for automatic control. Therefore, change the sysfs API to reflect this and enable pwm on 2. As we are breaking the ABI for this driver, rename oxpec to oxp_ec, reflecting the naming convention used by other drivers, to allow for a smooth migration in current userspace programs. Closes: https://lore.kernel.org/linux-hwmon/20241027174836.8588-1-derekjohn.clark@gmail.com/ Reviewed-by: Derek J. Clark Reviewed-by: Thomas Weißschuh Signed-off-by: Antheas Kapenekakis Link: https://lore.kernel.org/r/20250425111821.88746-12-lkml@antheas.dev Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 37 ++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 918a553bd896..45dfe0a7578b 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -732,7 +732,27 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, case hwmon_pwm_input: return oxp_pwm_input_read(val); case hwmon_pwm_enable: - return oxp_pwm_read(val); + ret = oxp_pwm_read(val); + if (ret) + return ret; + + /* Check for auto and return 2 */ + if (!*val) { + *val = 2; + return 0; + } + + /* Return 0 if at full fan speed, 1 otherwise */ + ret = oxp_pwm_fan_speed(val); + if (ret) + return ret; + + if (*val == 255) + *val = 0; + else + *val = 1; + + return 0; default: break; } @@ -746,15 +766,24 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type, static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { + int ret; + switch (type) { case hwmon_pwm: switch (attr) { case hwmon_pwm_enable: if (val == 1) return oxp_pwm_enable(); - else if (val == 0) + else if (val == 2) return oxp_pwm_disable(); - return -EINVAL; + else if (val != 0) + return -EINVAL; + + /* Enable PWM and set to max speed */ + ret = oxp_pwm_enable(); + if (ret) + return ret; + return oxp_pwm_input_write(255); case hwmon_pwm_input: return oxp_pwm_input_write(val); default: @@ -819,7 +848,7 @@ static int oxp_platform_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device *hwdev; - hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL, + hwdev = devm_hwmon_device_register_with_info(dev, "oxp_ec", NULL, &oxp_ec_chip_info, NULL); return PTR_ERR_OR_ZERO(hwdev); -- 2.51.0