diff --git a/target/linux/bcm27xx/patches-6.12/950-0284-hwmon-emc2305-fixups-for-driver-submitted-to-mailing.patch b/target/linux/bcm27xx/patches-6.12/950-0284-hwmon-emc2305-fixups-for-driver-submitted-to-mailing.patch index ef86c0ee60e..23de440f6b4 100644 --- a/target/linux/bcm27xx/patches-6.12/950-0284-hwmon-emc2305-fixups-for-driver-submitted-to-mailing.patch +++ b/target/linux/bcm27xx/patches-6.12/950-0284-hwmon-emc2305-fixups-for-driver-submitted-to-mailing.patch @@ -1,4 +1,4 @@ -From 38798884cb3ccd05b2a82299db865aeb7e7c059b Mon Sep 17 00:00:00 2001 +From 936b6af1d55981c2f5306ec3c06432e676e1a534 Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Thu, 5 May 2022 15:46:07 +0100 Subject: [PATCH] hwmon: emc2305: fixups for driver submitted to mailing lists @@ -33,59 +33,29 @@ As this is all downstream only, revert to u8 to match 5.15. Signed-off-by: Dave Stevenson --- - drivers/hwmon/emc2305.c | 95 +++++++++++++++++++++++++++++++++++++---- - 1 file changed, 87 insertions(+), 8 deletions(-) + drivers/hwmon/emc2305.c | 106 ++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 103 insertions(+), 3 deletions(-) --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c -@@ -12,12 +12,13 @@ - #include - #include +@@ -15,6 +15,8 @@ + #include + #include +#define EMC2305_REG_FAN_STATUS 0x24 +#define EMC2305_REG_FAN_STALL_STATUS 0x25 #define EMC2305_REG_DRIVE_FAIL_STATUS 0x27 #define EMC2305_REG_VENDOR 0xfe #define EMC2305_FAN_MAX 0xff - #define EMC2305_FAN_MIN 0x00 - #define EMC2305_FAN_MAX_STATE 10 --#define EMC2305_DEVICE 0x34 - #define EMC2305_VENDOR 0x5d - #define EMC2305_REG_PRODUCT_ID 0xfd - #define EMC2305_TACH_REGS_UNUSE_BITS 3 -@@ -36,6 +37,7 @@ - #define EMC2305_RPM_FACTOR 3932160 - - #define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * (n)) -+#define EMC2305_REG_FAN_CFG(n) (0x32 + 0x10 * (n)) - #define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * (n)) - #define EMC2305_REG_FAN_TACH(n) (0x3e + 0x10 * (n)) - -@@ -55,6 +57,15 @@ static const struct i2c_device_id emc230 - }; - MODULE_DEVICE_TABLE(i2c, emc2305_ids); - -+static const struct of_device_id emc2305_dt_ids[] = { -+ { .compatible = "microchip,emc2305" }, -+ { .compatible = "microchip,emc2303" }, -+ { .compatible = "microchip,emc2302" }, -+ { .compatible = "microchip,emc2301" }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, emc2305_dt_ids); -+ - /** - * struct emc2305_cdev_data - device-specific cooling device state - * @cdev: cooling device -@@ -100,6 +111,7 @@ struct emc2305_data { - u8 pwm_num; +@@ -117,6 +119,7 @@ struct emc2305_data { + u8 pwm_polarity_mask; bool pwm_separate; u8 pwm_min[EMC2305_PWM_MAX]; + u8 pwm_max; + u16 pwm_freq[EMC2305_PWM_MAX]; struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; }; - -@@ -272,7 +284,7 @@ static int emc2305_set_pwm(struct device +@@ -288,7 +291,7 @@ static int emc2305_set_pwm(struct device struct i2c_client *client = data->client; int ret; @@ -94,7 +64,7 @@ Signed-off-by: Dave Stevenson return -EINVAL; ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(channel), val); -@@ -283,6 +295,49 @@ static int emc2305_set_pwm(struct device +@@ -299,6 +302,49 @@ static int emc2305_set_pwm(struct device return 0; } @@ -141,22 +111,19 @@ Signed-off-by: Dave Stevenson + return 0; +} + - static int emc2305_set_single_tz(struct device *dev, int idx) + static int emc2305_set_single_tz(struct device *dev, struct device_node *fan_node, int idx) { struct emc2305_data *data = dev_get_drvdata(dev); -@@ -292,9 +347,17 @@ static int emc2305_set_single_tz(struct +@@ -308,10 +354,16 @@ static int emc2305_set_single_tz(struct cdev_idx = (idx) ? idx - 1 : 0; pwm = data->pwm_min[cdev_idx]; - data->cdev_data[cdev_idx].cdev = -- thermal_cooling_device_register(emc2305_fan_name[idx], data, -- &emc2305_cooling_ops); + if (dev->of_node) + data->cdev_data[cdev_idx].cdev = -+ devm_thermal_of_cooling_device_register(dev, dev->of_node, -+ emc2305_fan_name[idx], -+ data, -+ &emc2305_cooling_ops); + devm_thermal_of_cooling_device_register(dev, fan_node, + emc2305_fan_name[idx], data, + &emc2305_cooling_ops); + else + data->cdev_data[cdev_idx].cdev = + thermal_cooling_device_register(emc2305_fan_name[idx], @@ -165,26 +132,60 @@ Signed-off-by: Dave Stevenson if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); -@@ -353,9 +416,11 @@ static void emc2305_unset_tz(struct devi - int i; +@@ -344,6 +396,19 @@ static int emc2305_set_single_tz(struct + return 0; + } - /* Unregister cooling device. */ -- for (i = 0; i < EMC2305_PWM_MAX; i++) -- if (data->cdev_data[i].cdev) -- thermal_cooling_device_unregister(data->cdev_data[i].cdev); ++static void emc2305_unset_tz(struct device *dev) ++{ ++ struct emc2305_data *data = dev_get_drvdata(dev); ++ int i; ++ ++ /* Unregister cooling device. */ + if (!dev->of_node) { + for (i = 0; i < EMC2305_PWM_MAX; i++) + if (data->cdev_data[i].cdev) + thermal_cooling_device_unregister(data->cdev_data[i].cdev); + } ++} ++ + static int emc2305_set_tz(struct device *dev) + { + struct emc2305_data *data = dev_get_drvdata(dev); +@@ -355,9 +420,13 @@ static int emc2305_set_tz(struct device + for (i = 0; i < data->pwm_num; i++) { + ret = emc2305_set_single_tz(dev, dev->of_node, i + 1); + if (ret) +- return ret; ++ goto thermal_cooling_device_register_fail; + } + return 0; ++ ++thermal_cooling_device_register_fail: ++ emc2305_unset_tz(dev); ++ return ret; } static umode_t -@@ -577,11 +642,18 @@ static int emc2305_probe(struct i2c_clie - data->pwm_separate = pdata->pwm_separate; - for (i = 0; i < EMC2305_PWM_MAX; i++) - data->pwm_min[i] = pdata->pwm_min[i]; -+ data->pwm_max = EMC2305_FAN_MAX; +@@ -655,6 +724,7 @@ static int emc2305_probe(struct i2c_clie + data->pwm_min[i] = pdata->pwm_min[i]; + data->pwm_freq[i] = pdata->pwm_freq[i]; + } ++ data->pwm_max = EMC2305_FAN_MAX; + } else { + data->max_state = EMC2305_FAN_MAX_STATE; + data->pwm_separate = false; +@@ -664,12 +734,24 @@ static int emc2305_probe(struct i2c_clie + data->pwm_min[i] = EMC2305_FAN_MIN; + data->pwm_freq[i] = base_freq_table[3]; + } ++ data->pwm_max = EMC2305_FAN_MAX; ++ if (dev->of_node) { ++ ret = emc2305_get_tz_of(dev); ++ if (ret < 0) ++ return ret; ++ } + } } else { data->max_state = EMC2305_FAN_MAX_STATE; data->pwm_separate = false; @@ -199,7 +200,7 @@ Signed-off-by: Dave Stevenson } data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "emc2305", data, -@@ -602,6 +674,12 @@ static int emc2305_probe(struct i2c_clie +@@ -711,21 +793,39 @@ static int emc2305_probe(struct i2c_clie return ret; } @@ -212,11 +213,30 @@ Signed-off-by: Dave Stevenson return 0; } -@@ -616,6 +694,7 @@ static void emc2305_remove(struct i2c_cl + static const struct of_device_id of_emc2305_match_table[] = { + { .compatible = "microchip,emc2305", }, ++ { .compatible = "microchip,emc2303", }, ++ { .compatible = "microchip,emc2302", }, ++ { .compatible = "microchip,emc2301", }, + {}, + }; + MODULE_DEVICE_TABLE(of, of_emc2305_match_table); + ++static void emc2305_remove(struct i2c_client *client) ++{ ++ struct device *dev = &client->dev; ++ ++ if (IS_REACHABLE(CONFIG_THERMAL)) ++ emc2305_unset_tz(dev); ++} ++ static struct i2c_driver emc2305_driver = { .driver = { .name = "emc2305", -+ .of_match_table = emc2305_dt_ids, + .of_match_table = of_emc2305_match_table, }, .probe = emc2305_probe, - .remove = emc2305_remove, ++ .remove = emc2305_remove, + .id_table = emc2305_ids, + }; + diff --git a/target/linux/generic/backport-6.12/830-01-v6.15-hwmon-emc2305-Add-OF-support.patch b/target/linux/generic/backport-6.12/830-01-v6.15-hwmon-emc2305-Add-OF-support.patch new file mode 100644 index 00000000000..44bf97f78e5 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-01-v6.15-hwmon-emc2305-Add-OF-support.patch @@ -0,0 +1,34 @@ +From 882bd6de1a5b4488dc0747d74420af34d419fd99 Mon Sep 17 00:00:00 2001 +From: Florin Leotescu +Date: Fri, 21 Mar 2025 16:33:07 +0200 +Subject: [PATCH] hwmon: emc2305: Add OF support + +Introduce OF support for Microchip emc2305 pwm fan controller. + +Signed-off-by: Florin Leotescu +Reviewed-by: Frank Li +Link: https://lore.kernel.org/r/20250321143308.4008623-3-florin.leotescu@oss.nxp.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -613,9 +613,16 @@ static void emc2305_remove(struct i2c_cl + emc2305_unset_tz(dev); + } + ++static const struct of_device_id of_emc2305_match_table[] = { ++ { .compatible = "microchip,emc2305", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, of_emc2305_match_table); ++ + static struct i2c_driver emc2305_driver = { + .driver = { + .name = "emc2305", ++ .of_match_table = of_emc2305_match_table, + }, + .probe = emc2305_probe, + .remove = emc2305_remove, diff --git a/target/linux/generic/backport-6.12/830-02-v6.15-hwmon-emc2305-Use-devm_thermal_of_cooling_device_reg.patch b/target/linux/generic/backport-6.12/830-02-v6.15-hwmon-emc2305-Use-devm_thermal_of_cooling_device_reg.patch new file mode 100644 index 00000000000..d33eae540b1 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-02-v6.15-hwmon-emc2305-Use-devm_thermal_of_cooling_device_reg.patch @@ -0,0 +1,90 @@ +From 2115cbeec8a3ccc69e3b7ecdf97b4472b0829cfc Mon Sep 17 00:00:00 2001 +From: Florin Leotescu +Date: Fri, 21 Mar 2025 16:33:08 +0200 +Subject: [PATCH] hwmon: emc2305: Use devm_thermal_of_cooling_device_register + +Prepare the emc2305 driver to use configuration from Device Tree nodes. +Switch to devm_thermal_of_cooling_device_register to simplify the +cleanup procedure, allowing the removal of emc2305_unset_tz and +emc2305_remove, which are no longer needed. + +Signed-off-by: Florin Leotescu +Reviewed-by: Frank Li +Link: https://lore.kernel.org/r/20250321143308.4008623-4-florin.leotescu@oss.nxp.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 33 ++++----------------------------- + 1 file changed, 4 insertions(+), 29 deletions(-) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -112,8 +112,6 @@ static char *emc2305_fan_name[] = { + "emc2305_fan5", + }; + +-static void emc2305_unset_tz(struct device *dev); +- + static int emc2305_get_max_channel(const struct emc2305_data *data) + { + return data->pwm_num; +@@ -293,8 +291,9 @@ static int emc2305_set_single_tz(struct + pwm = data->pwm_min[cdev_idx]; + + data->cdev_data[cdev_idx].cdev = +- thermal_cooling_device_register(emc2305_fan_name[idx], data, +- &emc2305_cooling_ops); ++ devm_thermal_of_cooling_device_register(dev, dev->of_node, ++ emc2305_fan_name[idx], data, ++ &emc2305_cooling_ops); + + if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { + dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); +@@ -338,24 +337,9 @@ static int emc2305_set_tz(struct device + for (i = 0; i < data->pwm_num; i++) { + ret = emc2305_set_single_tz(dev, i + 1); + if (ret) +- goto thermal_cooling_device_register_fail; ++ return ret; + } + return 0; +- +-thermal_cooling_device_register_fail: +- emc2305_unset_tz(dev); +- return ret; +-} +- +-static void emc2305_unset_tz(struct device *dev) +-{ +- struct emc2305_data *data = dev_get_drvdata(dev); +- int i; +- +- /* Unregister cooling device. */ +- for (i = 0; i < EMC2305_PWM_MAX; i++) +- if (data->cdev_data[i].cdev) +- thermal_cooling_device_unregister(data->cdev_data[i].cdev); + } + + static umode_t +@@ -605,14 +589,6 @@ static int emc2305_probe(struct i2c_clie + return 0; + } + +-static void emc2305_remove(struct i2c_client *client) +-{ +- struct device *dev = &client->dev; +- +- if (IS_REACHABLE(CONFIG_THERMAL)) +- emc2305_unset_tz(dev); +-} +- + static const struct of_device_id of_emc2305_match_table[] = { + { .compatible = "microchip,emc2305", }, + {}, +@@ -625,7 +601,6 @@ static struct i2c_driver emc2305_driver + .of_match_table = of_emc2305_match_table, + }, + .probe = emc2305_probe, +- .remove = emc2305_remove, + .id_table = emc2305_ids, + }; + diff --git a/target/linux/generic/backport-6.12/830-03-v6.17-hwmon-emc2305-Add-support-for-PWM-frequency-polarity.patch b/target/linux/generic/backport-6.12/830-03-v6.17-hwmon-emc2305-Add-support-for-PWM-frequency-polarity.patch new file mode 100644 index 00000000000..c7582582802 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-03-v6.17-hwmon-emc2305-Add-support-for-PWM-frequency-polarity.patch @@ -0,0 +1,66 @@ +From 7114b74d99a3cd588da4ecb6011858c06f8408a1 Mon Sep 17 00:00:00 2001 +From: Florin Leotescu +Date: Tue, 3 Jun 2025 14:31:22 +0300 +Subject: [PATCH 1/3] hwmon: (emc2305) Add support for PWM frequency, polarity + and output + +Add three new attributes to the driver data structures to support +configuration of PWM frequency, PWM polarity and PWM output config. + +Signed-off-by: Florin Leotescu +Link: https://lore.kernel.org/r/20250603113125.3175103-2-florin.leotescu@oss.nxp.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 6 ++++++ + include/linux/platform_data/emc2305.h | 6 ++++++ + 2 files changed, 12 insertions(+) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -89,8 +89,11 @@ struct emc2305_cdev_data { + * @hwmon_dev: hwmon device + * @max_state: maximum cooling state of the cooling device + * @pwm_num: number of PWM channels ++ * @pwm_output_mask: PWM output mask ++ * @pwm_polarity_mask: PWM polarity mask + * @pwm_separate: separate PWM settings for every channel + * @pwm_min: array of minimum PWM per channel ++ * @pwm_freq: array of PWM frequency per channel + * @cdev_data: array of cooling devices data + */ + struct emc2305_data { +@@ -98,8 +101,11 @@ struct emc2305_data { + struct device *hwmon_dev; + u8 max_state; + u8 pwm_num; ++ u8 pwm_output_mask; ++ u8 pwm_polarity_mask; + bool pwm_separate; + u8 pwm_min[EMC2305_PWM_MAX]; ++ u16 pwm_freq[EMC2305_PWM_MAX]; + struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; + }; + +--- a/include/linux/platform_data/emc2305.h ++++ b/include/linux/platform_data/emc2305.h +@@ -9,14 +9,20 @@ + * struct emc2305_platform_data - EMC2305 driver platform data + * @max_state: maximum cooling state of the cooling device; + * @pwm_num: number of active channels; ++ * @pwm_output_mask: PWM output mask ++ * @pwm_polarity_mask: PWM polarity mask + * @pwm_separate: separate PWM settings for every channel; + * @pwm_min: array of minimum PWM per channel; ++ * @pwm_freq: array of PWM frequency per channel + */ + struct emc2305_platform_data { + u8 max_state; + u8 pwm_num; ++ u8 pwm_output_mask; ++ u8 pwm_polarity_mask; + bool pwm_separate; + u8 pwm_min[EMC2305_PWM_MAX]; ++ u16 pwm_freq[EMC2305_PWM_MAX]; + }; + + #endif diff --git a/target/linux/generic/backport-6.12/830-04-v6.17-hwmon-emc2305-Configure-PWM-channels-based-on-DT-pro.patch b/target/linux/generic/backport-6.12/830-04-v6.17-hwmon-emc2305-Configure-PWM-channels-based-on-DT-pro.patch new file mode 100644 index 00000000000..2b6481a29c6 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-04-v6.17-hwmon-emc2305-Configure-PWM-channels-based-on-DT-pro.patch @@ -0,0 +1,244 @@ +From 2ed4db7a1d07b349b50e890dee3d0f245230d254 Mon Sep 17 00:00:00 2001 +From: Florin Leotescu +Date: Tue, 3 Jun 2025 14:31:23 +0300 +Subject: [PATCH 2/3] hwmon: (emc2305) Configure PWM channels based on DT + properties + +Add support for configuring each PWM channel using Device Tree (DT) +properties by parsing the 'pwms' phandle arguments. + +Signed-off-by: Florin Leotescu +Link: https://lore.kernel.org/r/20250603113125.3175103-3-florin.leotescu@oss.nxp.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 151 ++++++++++++++++++++++++++++++++++------ + 1 file changed, 129 insertions(+), 22 deletions(-) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -11,6 +11,9 @@ + #include + #include + #include ++#include ++#include ++#include + + #define EMC2305_REG_DRIVE_FAIL_STATUS 0x27 + #define EMC2305_REG_VENDOR 0xfe +@@ -23,6 +26,8 @@ + #define EMC2305_TACH_REGS_UNUSE_BITS 3 + #define EMC2305_TACH_CNT_MULTIPLIER 0x02 + #define EMC2305_TACH_RANGE_MIN 480 ++#define EMC2305_DEFAULT_OUTPUT 0x0 ++#define EMC2305_DEFAULT_POLARITY 0x0 + + #define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ + DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) +@@ -39,6 +44,9 @@ + #define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * (n)) + #define EMC2305_REG_FAN_TACH(n) (0x3e + 0x10 * (n)) + ++/* Supported base PWM frequencies */ ++static const unsigned int base_freq_table[] = { 2441, 4882, 19530, 26000 }; ++ + enum emc230x_product_id { + EMC2305 = 0x34, + EMC2303 = 0x35, +@@ -287,7 +295,7 @@ static int emc2305_set_pwm(struct device + return 0; + } + +-static int emc2305_set_single_tz(struct device *dev, int idx) ++static int emc2305_set_single_tz(struct device *dev, struct device_node *fan_node, int idx) + { + struct emc2305_data *data = dev_get_drvdata(dev); + long pwm; +@@ -297,7 +305,7 @@ static int emc2305_set_single_tz(struct + pwm = data->pwm_min[cdev_idx]; + + data->cdev_data[cdev_idx].cdev = +- devm_thermal_of_cooling_device_register(dev, dev->of_node, ++ devm_thermal_of_cooling_device_register(dev, fan_node, + emc2305_fan_name[idx], data, + &emc2305_cooling_ops); + +@@ -338,10 +346,10 @@ static int emc2305_set_tz(struct device + int i, ret; + + if (!data->pwm_separate) +- return emc2305_set_single_tz(dev, 0); ++ return emc2305_set_single_tz(dev, dev->of_node, 0); + + for (i = 0; i < data->pwm_num; i++) { +- ret = emc2305_set_single_tz(dev, i + 1); ++ ret = emc2305_set_single_tz(dev, dev->of_node, i + 1); + if (ret) + return ret; + } +@@ -523,15 +531,85 @@ static int emc2305_identify(struct devic + return 0; + } + ++static int emc2305_of_parse_pwm_child(struct device *dev, ++ struct device_node *child, ++ struct emc2305_data *data) ++{ u32 ch; ++ int ret; ++ struct of_phandle_args args; ++ ++ ret = of_property_read_u32(child, "reg", &ch); ++ if (ret) { ++ dev_err(dev, "missing reg property of %pOFn\n", child); ++ return ret; ++ } ++ ++ ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", 0, &args); ++ ++ if (ret) ++ return ret; ++ ++ if (args.args_count > 0) { ++ data->pwm_freq[ch] = find_closest(args.args[0], base_freq_table, ++ ARRAY_SIZE(base_freq_table)); ++ } else { ++ data->pwm_freq[ch] = base_freq_table[3]; ++ } ++ ++ if (args.args_count > 1) { ++ if (args.args[1] == PWM_POLARITY_NORMAL || args.args[1] == PWM_POLARITY_INVERSED) ++ data->pwm_polarity_mask |= args.args[1] << ch; ++ else ++ dev_err(dev, "Wrong PWM polarity config provided: %d\n", args.args[0]); ++ } else { ++ data->pwm_polarity_mask |= PWM_POLARITY_NORMAL << ch; ++ } ++ ++ if (args.args_count > 2) { ++ if (args.args[2] == EMC2305_PUSH_PULL || args.args[2] <= EMC2305_OPEN_DRAIN) ++ data->pwm_output_mask |= args.args[2] << ch; ++ else ++ dev_err(dev, "Wrong PWM output config provided: %d\n", args.args[1]); ++ } else { ++ data->pwm_output_mask |= EMC2305_OPEN_DRAIN << ch; ++ } ++ ++ return 0; ++} ++ ++static int emc2305_probe_childs_from_dt(struct device *dev) ++{ ++ struct emc2305_data *data = dev_get_drvdata(dev); ++ struct device_node *child; ++ int ret, count = 0; ++ ++ data->pwm_output_mask = 0x0; ++ data->pwm_polarity_mask = 0x0; ++ ++ for_each_child_of_node(dev->of_node, child) { ++ if (of_property_present(child, "reg")) { ++ ret = emc2305_of_parse_pwm_child(dev, child, data); ++ if (ret) { ++ of_node_put(child); ++ continue; ++ } ++ count++; ++ } ++ } ++ return count; ++} ++ + static int emc2305_probe(struct i2c_client *client) + { + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; ++ struct device_node *child; + struct emc2305_data *data; + struct emc2305_platform_data *pdata; + int vendor; + int ret; + int i; ++ int pwm_childs; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; +@@ -551,22 +629,40 @@ static int emc2305_probe(struct i2c_clie + if (ret) + return ret; + ++ pwm_childs = emc2305_probe_childs_from_dt(dev); ++ + pdata = dev_get_platdata(&client->dev); +- if (pdata) { +- if (!pdata->max_state || pdata->max_state > EMC2305_FAN_MAX_STATE) +- return -EINVAL; +- data->max_state = pdata->max_state; +- /* +- * Validate a number of active PWM channels. Note that +- * configured number can be less than the actual maximum +- * supported by the device. +- */ +- if (!pdata->pwm_num || pdata->pwm_num > EMC2305_PWM_MAX) +- return -EINVAL; +- data->pwm_num = pdata->pwm_num; +- data->pwm_separate = pdata->pwm_separate; +- for (i = 0; i < EMC2305_PWM_MAX; i++) +- data->pwm_min[i] = pdata->pwm_min[i]; ++ ++ if (!pwm_childs) { ++ if (pdata) { ++ if (!pdata->max_state || pdata->max_state > EMC2305_FAN_MAX_STATE) ++ return -EINVAL; ++ data->max_state = pdata->max_state; ++ /* ++ * Validate a number of active PWM channels. Note that ++ * configured number can be less than the actual maximum ++ * supported by the device. ++ */ ++ if (!pdata->pwm_num || pdata->pwm_num > EMC2305_PWM_MAX) ++ return -EINVAL; ++ data->pwm_num = pdata->pwm_num; ++ data->pwm_output_mask = pdata->pwm_output_mask; ++ data->pwm_polarity_mask = pdata->pwm_polarity_mask; ++ data->pwm_separate = pdata->pwm_separate; ++ for (i = 0; i < EMC2305_PWM_MAX; i++) { ++ data->pwm_min[i] = pdata->pwm_min[i]; ++ data->pwm_freq[i] = pdata->pwm_freq[i]; ++ } ++ } else { ++ data->max_state = EMC2305_FAN_MAX_STATE; ++ data->pwm_separate = false; ++ data->pwm_output_mask = EMC2305_DEFAULT_OUTPUT; ++ data->pwm_polarity_mask = EMC2305_DEFAULT_POLARITY; ++ for (i = 0; i < EMC2305_PWM_MAX; i++) { ++ data->pwm_min[i] = EMC2305_FAN_MIN; ++ data->pwm_freq[i] = base_freq_table[3]; ++ } ++ } + } else { + data->max_state = EMC2305_FAN_MAX_STATE; + data->pwm_separate = false; +@@ -580,9 +676,20 @@ static int emc2305_probe(struct i2c_clie + return PTR_ERR(data->hwmon_dev); + + if (IS_REACHABLE(CONFIG_THERMAL)) { +- ret = emc2305_set_tz(dev); +- if (ret != 0) +- return ret; ++ /* Parse and check for the available PWM child nodes */ ++ if (pwm_childs > 0) { ++ i = 0; ++ for_each_child_of_node(dev->of_node, child) { ++ ret = emc2305_set_single_tz(dev, child, i); ++ if (ret != 0) ++ return ret; ++ i++; ++ } ++ } else { ++ ret = emc2305_set_tz(dev); ++ if (ret != 0) ++ return ret; ++ } + } + + for (i = 0; i < data->pwm_num; i++) { diff --git a/target/linux/generic/backport-6.12/830-05-v6.17-hwmon-emc2305-Enable-PWM-polarity-and-output-configu.patch b/target/linux/generic/backport-6.12/830-05-v6.17-hwmon-emc2305-Enable-PWM-polarity-and-output-configu.patch new file mode 100644 index 00000000000..96b435f88bd --- /dev/null +++ b/target/linux/generic/backport-6.12/830-05-v6.17-hwmon-emc2305-Enable-PWM-polarity-and-output-configu.patch @@ -0,0 +1,46 @@ +From ef8b1b4eb702cdd56807c0430b511f94b2af8e66 Mon Sep 17 00:00:00 2001 +From: Florin Leotescu +Date: Tue, 3 Jun 2025 14:31:24 +0300 +Subject: [PATCH 3/3] hwmon: (emc2305) Enable PWM polarity and output + configuration + +Enable configuration of PWM polarity and PWM output config +based Device Tree properties. + +Signed-off-by: Florin Leotescu +Link: https://lore.kernel.org/r/20250603113125.3175103-4-florin.leotescu@oss.nxp.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -28,6 +28,10 @@ + #define EMC2305_TACH_RANGE_MIN 480 + #define EMC2305_DEFAULT_OUTPUT 0x0 + #define EMC2305_DEFAULT_POLARITY 0x0 ++#define EMC2305_REG_POLARITY 0x2a ++#define EMC2305_REG_DRIVE_PWM_OUT 0x2b ++#define EMC2305_OPEN_DRAIN 0x0 ++#define EMC2305_PUSH_PULL 0x1 + + #define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ + DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) +@@ -692,6 +696,16 @@ static int emc2305_probe(struct i2c_clie + } + } + ++ ret = i2c_smbus_write_byte_data(client, EMC2305_REG_DRIVE_PWM_OUT, ++ data->pwm_output_mask); ++ if (ret < 0) ++ dev_err(dev, "Failed to configure pwm output, using default\n"); ++ ++ ret = i2c_smbus_write_byte_data(client, EMC2305_REG_POLARITY, ++ data->pwm_polarity_mask); ++ if (ret < 0) ++ dev_err(dev, "Failed to configure pwm polarity, using default\n"); ++ + for (i = 0; i < data->pwm_num; i++) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i), + data->pwm_min[i]); diff --git a/target/linux/generic/backport-6.12/830-06-v6.19-hwmon-emc2305-fix-double-put-in-emc2305_probe_childs.patch b/target/linux/generic/backport-6.12/830-06-v6.19-hwmon-emc2305-fix-double-put-in-emc2305_probe_childs.patch new file mode 100644 index 00000000000..098ba846dcb --- /dev/null +++ b/target/linux/generic/backport-6.12/830-06-v6.19-hwmon-emc2305-fix-double-put-in-emc2305_probe_childs.patch @@ -0,0 +1,32 @@ +From 541dfb49dcb80c2509e030842de77adfb77820f5 Mon Sep 17 00:00:00 2001 +From: Pei Xiao +Date: Fri, 5 Dec 2025 10:02:41 +0800 +Subject: [PATCH 1/2] hwmon: (emc2305) fix double put in + emc2305_probe_childs_from_dt + +./drivers/hwmon/emc2305.c:597:4-15: ERROR: probable double put + +Device node iterators put the previous value of the index variable, so an +explicit put causes a double put. + +Signed-off-by: Pei Xiao +Link: https://lore.kernel.org/r/tencent_CD373F952BE48697C949E39CB5EB77841D06@qq.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -593,10 +593,8 @@ static int emc2305_probe_childs_from_dt( + for_each_child_of_node(dev->of_node, child) { + if (of_property_present(child, "reg")) { + ret = emc2305_of_parse_pwm_child(dev, child, data); +- if (ret) { +- of_node_put(child); ++ if (ret) + continue; +- } + count++; + } + } diff --git a/target/linux/generic/backport-6.12/830-07-v6.19-hwmon-emc2305-fix-device-node-refcount-leak-in-error.patch b/target/linux/generic/backport-6.12/830-07-v6.19-hwmon-emc2305-fix-device-node-refcount-leak-in-error.patch new file mode 100644 index 00000000000..05039136ed4 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-07-v6.19-hwmon-emc2305-fix-device-node-refcount-leak-in-error.patch @@ -0,0 +1,37 @@ +From 4910da6b36b122db50a27fabf6ab7f8611b60bf8 Mon Sep 17 00:00:00 2001 +From: Pei Xiao +Date: Fri, 5 Dec 2025 11:15:13 +0800 +Subject: [PATCH 2/2] hwmon: (emc2305) fix device node refcount leak in error + path + +The for_each_child_of_node() macro automatically manages device node +reference counts during normal iteration. However, when breaking out +of the loop early with return, the current iteration's node is not +automatically released, leading to a reference count leak. + +Fix this by adding of_node_put(child) before returning from the loop +when emc2305_set_single_tz() fails. + +This issue could lead to memory leaks over multiple probe cycles. + +Signed-off-by: Pei Xiao +Link: https://lore.kernel.org/r/tencent_5CDC08544C901D5ECA270573D5AEE3117108@qq.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -683,8 +683,10 @@ static int emc2305_probe(struct i2c_clie + i = 0; + for_each_child_of_node(dev->of_node, child) { + ret = emc2305_set_single_tz(dev, child, i); +- if (ret != 0) ++ if (ret != 0) { ++ of_node_put(child); + return ret; ++ } + i++; + } + } else { diff --git a/target/linux/generic/backport-6.12/830-08-v7.0-hwmon-emc2305-Simplify-with-scoped-for-each-OF-child.patch b/target/linux/generic/backport-6.12/830-08-v7.0-hwmon-emc2305-Simplify-with-scoped-for-each-OF-child.patch new file mode 100644 index 00000000000..cae5a5ce3d4 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-08-v7.0-hwmon-emc2305-Simplify-with-scoped-for-each-OF-child.patch @@ -0,0 +1,40 @@ +From bfd0103be5d85751fc8b7c6c8c7553d71022efe0 Mon Sep 17 00:00:00 2001 +From: Krzysztof Kozlowski +Date: Wed, 24 Dec 2025 12:07:03 +0100 +Subject: [PATCH] hwmon: (emc2305) Simplify with scoped for each OF child loop + +Use scoped for-each loop when iterating over device nodes to make code a +bit simpler. + +Signed-off-by: Krzysztof Kozlowski +Link: https://lore.kernel.org/r/20251224110702.61746-4-krzysztof.kozlowski@oss.qualcomm.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -605,7 +605,6 @@ static int emc2305_probe(struct i2c_clie + { + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; +- struct device_node *child; + struct emc2305_data *data; + struct emc2305_platform_data *pdata; + int vendor; +@@ -681,12 +680,10 @@ static int emc2305_probe(struct i2c_clie + /* Parse and check for the available PWM child nodes */ + if (pwm_childs > 0) { + i = 0; +- for_each_child_of_node(dev->of_node, child) { ++ for_each_child_of_node_scoped(dev->of_node, child) { + ret = emc2305_set_single_tz(dev, child, i); +- if (ret != 0) { +- of_node_put(child); ++ if (ret != 0) + return ret; +- } + i++; + } + } else { diff --git a/target/linux/generic/backport-6.12/830-09-v7.0-hwmon-emc2305-Fix-a-resource-leak-in-emc2305_of_pars.patch b/target/linux/generic/backport-6.12/830-09-v7.0-hwmon-emc2305-Fix-a-resource-leak-in-emc2305_of_pars.patch new file mode 100644 index 00000000000..7b19b8e43d0 --- /dev/null +++ b/target/linux/generic/backport-6.12/830-09-v7.0-hwmon-emc2305-Fix-a-resource-leak-in-emc2305_of_pars.patch @@ -0,0 +1,28 @@ +From 2954ce672b7623478c1cfeb69e6a6e4042a3656e Mon Sep 17 00:00:00 2001 +From: Felix Gu +Date: Thu, 15 Jan 2026 21:51:48 +0800 +Subject: [PATCH] hwmon: (emc2305) Fix a resource leak in + emc2305_of_parse_pwm_child + +When calling of_parse_phandle_with_args(), the caller is responsible +to call of_node_put() to release the reference of device node. +In emc2305_of_parse_pwm_child, it does not release the reference, +causing a resource leak. + +Signed-off-by: Felix Gu +Link: https://lore.kernel.org/r/tencent_738BA80BBF28F3440301EEE6F9E470165105@qq.com +Signed-off-by: Guenter Roeck +--- + drivers/hwmon/emc2305.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/hwmon/emc2305.c ++++ b/drivers/hwmon/emc2305.c +@@ -578,6 +578,7 @@ static int emc2305_of_parse_pwm_child(st + data->pwm_output_mask |= EMC2305_OPEN_DRAIN << ch; + } + ++ of_node_put(args.np); + return 0; + } +