-From 38798884cb3ccd05b2a82299db865aeb7e7c059b Mon Sep 17 00:00:00 2001
+From 936b6af1d55981c2f5306ec3c06432e676e1a534 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Thu, 5 May 2022 15:46:07 +0100
Subject: [PATCH] hwmon: emc2305: fixups for driver submitted to mailing lists
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
---
- 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 <linux/platform_data/emc2305.h>
- #include <linux/thermal.h>
+@@ -15,6 +15,8 @@
+ #include <linux/of_device.h>
+ #include <linux/util_macros.h>
+#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;
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;
}
+ 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],
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;
}
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;
}
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,
+ };
+
--- /dev/null
+From 882bd6de1a5b4488dc0747d74420af34d419fd99 Mon Sep 17 00:00:00 2001
+From: Florin Leotescu <florin.leotescu@nxp.com>
+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 <florin.leotescu@nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://lore.kernel.org/r/20250321143308.4008623-3-florin.leotescu@oss.nxp.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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,
--- /dev/null
+From 2115cbeec8a3ccc69e3b7ecdf97b4472b0829cfc Mon Sep 17 00:00:00 2001
+From: Florin Leotescu <florin.leotescu@nxp.com>
+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 <florin.leotescu@nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://lore.kernel.org/r/20250321143308.4008623-4-florin.leotescu@oss.nxp.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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,
+ };
+
--- /dev/null
+From 7114b74d99a3cd588da4ecb6011858c06f8408a1 Mon Sep 17 00:00:00 2001
+From: Florin Leotescu <florin.leotescu@nxp.com>
+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 <florin.leotescu@nxp.com>
+Link: https://lore.kernel.org/r/20250603113125.3175103-2-florin.leotescu@oss.nxp.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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
--- /dev/null
+From 2ed4db7a1d07b349b50e890dee3d0f245230d254 Mon Sep 17 00:00:00 2001
+From: Florin Leotescu <florin.leotescu@nxp.com>
+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 <florin.leotescu@nxp.com>
+Link: https://lore.kernel.org/r/20250603113125.3175103-3-florin.leotescu@oss.nxp.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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 <linux/module.h>
+ #include <linux/platform_data/emc2305.h>
+ #include <linux/thermal.h>
++#include <linux/pwm.h>
++#include <linux/of_device.h>
++#include <linux/util_macros.h>
+
+ #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++) {
--- /dev/null
+From ef8b1b4eb702cdd56807c0430b511f94b2af8e66 Mon Sep 17 00:00:00 2001
+From: Florin Leotescu <florin.leotescu@nxp.com>
+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 <florin.leotescu@nxp.com>
+Link: https://lore.kernel.org/r/20250603113125.3175103-4-florin.leotescu@oss.nxp.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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]);
--- /dev/null
+From 541dfb49dcb80c2509e030842de77adfb77820f5 Mon Sep 17 00:00:00 2001
+From: Pei Xiao <xiaopei01@kylinos.cn>
+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 <xiaopei01@kylinos.cn>
+Link: https://lore.kernel.org/r/tencent_CD373F952BE48697C949E39CB5EB77841D06@qq.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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++;
+ }
+ }
--- /dev/null
+From 4910da6b36b122db50a27fabf6ab7f8611b60bf8 Mon Sep 17 00:00:00 2001
+From: Pei Xiao <xiaopei01@kylinos.cn>
+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 <xiaopei01@kylinos.cn>
+Link: https://lore.kernel.org/r/tencent_5CDC08544C901D5ECA270573D5AEE3117108@qq.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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 {
--- /dev/null
+From bfd0103be5d85751fc8b7c6c8c7553d71022efe0 Mon Sep 17 00:00:00 2001
+From: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
+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 <krzysztof.kozlowski@oss.qualcomm.com>
+Link: https://lore.kernel.org/r/20251224110702.61746-4-krzysztof.kozlowski@oss.qualcomm.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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 {
--- /dev/null
+From 2954ce672b7623478c1cfeb69e6a6e4042a3656e Mon Sep 17 00:00:00 2001
+From: Felix Gu <gu_0233@qq.com>
+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 <gu_0233@qq.com>
+Link: https://lore.kernel.org/r/tencent_738BA80BBF28F3440301EEE6F9E470165105@qq.com
+Signed-off-by: Guenter Roeck <linux@roeck-us.net>
+---
+ 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;
+ }
+