From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Patrick Yavitz <pyavitz@xxxxx.com>
Date: Tue, 10 Oct 2023 18:54:22 -0400
Subject: hwmon: emc2305: fixups for driver

BPI-CM4 fan control

hwmon: emc2305: fixups for driver
The driver had a number of issues, checkpatch warnings/errors,
and other limitations, so fix these up to make it usable.
hwmon: emc2305: Change OF properties pwm-min & pwm-max to u8
hwmon: emc2305: Add calls to initialize cooling maps
https://github.com/raspberrypi/linux/commits/233096b8a9023f7e02960543c85447d46af81e81/drivers/hwmon/emc2305.c

Tested-on: CM4-IO-BASE-B: https://www.waveshare.com/wiki/CM4-IO-BASE-B
Signed-off-by: Phil Elwell <phil@raspberrypi.com>
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Patrick Yavitz <pyavitz@xxxxx.com>
---
 drivers/hwmon/emc2305.c | 96 +++++++++-
 1 file changed, 88 insertions(+), 8 deletions(-)

diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c
index 29f0e4945f19..2581166cb978 100644
--- a/drivers/hwmon/emc2305.c
+++ b/drivers/hwmon/emc2305.c
@@ -15,12 +15,13 @@
 static const unsigned short
 emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END };
 
+#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
@@ -39,6 +40,7 @@ emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_EN
 #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))
 
@@ -58,6 +60,16 @@ static const struct i2c_device_id emc2305_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, emc2305_ids);
 
+static const struct of_device_id emc2305_dt_ids[] = {
+	{ .compatible = "smsc,emc2305" },
+	{ .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
@@ -103,6 +115,7 @@ struct emc2305_data {
 	u8 pwm_num;
 	bool pwm_separate;
 	u8 pwm_min[EMC2305_PWM_MAX];
+	u8 pwm_max;
 	struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX];
 };
 
@@ -275,7 +288,7 @@ static int emc2305_set_pwm(struct device *dev, long val, int channel)
 	struct i2c_client *client = data->client;
 	int ret;
 
-	if (val < data->pwm_min[channel] || val > EMC2305_FAN_MAX)
+	if (val < data->pwm_min[channel] || val > data->pwm_max)
 		return -EINVAL;
 
 	ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(channel), val);
@@ -286,6 +299,49 @@ static int emc2305_set_pwm(struct device *dev, long val, int channel)
 	return 0;
 }
 
+static int emc2305_get_tz_of(struct device *dev)
+{
+	struct device_node *np = dev->of_node;
+	struct emc2305_data *data = dev_get_drvdata(dev);
+	int ret = 0;
+	u8 val;
+	int i;
+
+	/* OF parameters are optional - overwrite default setting
+	 * if some of them are provided.
+	 */
+
+	ret = of_property_read_u8(np, "emc2305,cooling-levels", &val);
+	if (!ret)
+		data->max_state = val;
+	else if (ret != -EINVAL)
+		return ret;
+
+	ret = of_property_read_u8(np, "emc2305,pwm-max", &val);
+	if (!ret)
+		data->pwm_max = val;
+	else if (ret != -EINVAL)
+		return ret;
+
+	ret = of_property_read_u8(np, "emc2305,pwm-min", &val);
+	if (!ret)
+		for (i = 0; i < EMC2305_PWM_MAX; i++)
+			data->pwm_min[i] = val;
+	else if (ret != -EINVAL)
+		return ret;
+
+	/* Not defined or 0 means one thermal zone over all cooling devices.
+	 * Otherwise - separated thermal zones for each PWM channel.
+	 */
+	ret = of_property_read_u8(np, "emc2305,pwm-channel", &val);
+	if (!ret)
+		data->pwm_separate = (val != 0);
+	else if (ret != -EINVAL)
+		return ret;
+
+	return 0;
+}
+
 static int emc2305_set_single_tz(struct device *dev, int idx)
 {
 	struct emc2305_data *data = dev_get_drvdata(dev);
@@ -295,9 +351,17 @@ static int emc2305_set_single_tz(struct device *dev, int idx)
 	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);
+	else
+		data->cdev_data[cdev_idx].cdev =
+			thermal_cooling_device_register(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]);
@@ -350,9 +414,11 @@ static void emc2305_unset_tz(struct device *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);
+	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 umode_t
@@ -574,11 +640,18 @@ static int emc2305_probe(struct i2c_client *client)
 		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;
 	} else {
 		data->max_state = EMC2305_FAN_MAX_STATE;
 		data->pwm_separate = false;
 		for (i = 0; i < EMC2305_PWM_MAX; i++)
 			data->pwm_min[i] = EMC2305_FAN_MIN;
+		data->pwm_max = EMC2305_FAN_MAX;
+		if (dev->of_node) {
+			ret = emc2305_get_tz_of(dev);
+			if (ret < 0)
+				return ret;
+		}
 	}
 
 	data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "emc2305", data,
@@ -599,6 +672,12 @@ static int emc2305_probe(struct i2c_client *client)
 			return ret;
 	}
 
+	/* Acknowledge any existing faults. Stops the device responding on the
+	 * SMBus alert address.
+	 */
+	i2c_smbus_read_byte_data(client, EMC2305_REG_FAN_STALL_STATUS);
+	i2c_smbus_read_byte_data(client, EMC2305_REG_FAN_STATUS);
+
 	return 0;
 }
 
@@ -614,6 +693,7 @@ static struct i2c_driver emc2305_driver = {
 	.class  = I2C_CLASS_HWMON,
 	.driver = {
 		.name = "emc2305",
+		.of_match_table = emc2305_dt_ids,
 	},
 	.probe = emc2305_probe,
 	.remove	  = emc2305_remove,
-- 
Armbian

