[PATCH 2/2] hwmon/applesmc: add fan support for newer macs

Subu Dwevedi posted 2 patches 9 months, 1 week ago
[PATCH 2/2] hwmon/applesmc: add fan support for newer macs
Posted by Subu Dwevedi 9 months, 1 week ago
Newer Mac models have transitioned
the fan speed values from short to float.

Additionally, the fan manual
control mechanism (fan_manual) have changed from u16 to u8

Signed-off-by: Subu Dwevedi <messigoatcr7nop@gmail.com>
---
 drivers/hwmon/applesmc.c | 124 +++++++++++++++++++++++++++++++--------
 1 file changed, 101 insertions(+), 23 deletions(-)

diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 1be4a4026a6e..9157f5978ee7 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -71,10 +71,12 @@
 #define MOTION_SENSOR_KEY	"MOCN" /* r/w ui16 */
 
 #define FANS_COUNT		"FNum" /* r-o ui8 */
+#define FANS_MANUAL_FMT	"F%dMd" /* r-w ui8*/
 #define FANS_MANUAL		"FS! " /* r-w ui16 */
 #define FAN_ID_FMT		"F%dID" /* r-o char[16] */
 
 #define TEMP_SENSOR_TYPE	"sp78"
+#define FLOAT_TYPE		"flt "
 
 /* List of keys used to read/write fan speeds */
 static const char *const fan_speed_fmt[] = {
@@ -145,6 +147,8 @@ static s16 rest_y;
 static u8 backlight_state[2];
 static u8 *__iomem mmio_base;
 static bool is_mmio;
+static bool is_fan_manual_fmt;
+static bool is_fan_speed_float;
 static u32 mmio_base_addr, mmio_base_size;
 static struct device *hwmon_dev;
 static struct input_dev *applesmc_idev;
@@ -652,6 +656,50 @@ static int applesmc_read_s16(const char *key, s16 *value)
 	return 0;
 }
 
+/*
+ * applesmc_float_to_u32 - Retrieve the integral part of a float.
+ * This is needed because Apple made fans use float values in the T2.
+ * The fractional point is not significantly useful though, and the integral
+ */
+static inline u32 applesmc_float_to_u32(u32 from)
+{
+	u8 sign = from >> 31;
+	s32 exp = ((from >> 23) & 0xFF) - 0x7F;
+	u32 fr = from & GENMASK(22, 0);
+	u32 round_up = 0;
+
+	if (sign || exp < 0)
+		return 0;
+
+	u32 int_part = BIT(exp);
+	u32 frac_part = fr >> (23 - exp);
+
+	if (fr & BIT(22 - exp))
+		round_up = 1;
+
+	return int_part + frac_part + round_up;
+}
+
+/*
+ * applesmc_u32_to_float - Convert an u32 into a float.
+ * See applesmc_float_to_u32 for a rationale.
+ */
+static inline u32 applesmc_u32_to_float(u32 from)
+{
+	if (!from)
+		return 0;
+
+	u32 bc = fls(from) - 1;
+	u32 exp = 0x7F + bc;
+	u32 frac_part = (from << (23 - bc)) & GENMASK(22, 0);
+	u32 round_up = 0;
+
+	if (from & BIT(bc - 1))
+		round_up = 1;
+
+	return (exp << 23) | (frac_part + round_up);
+}
+
 /*
  * applesmc_device_init - initialize the accelerometer.  Can sleep.
  */
@@ -763,6 +811,8 @@ static int applesmc_init_mmio_try(void)
 static int applesmc_init_smcreg_try(void)
 {
 	struct applesmc_registers *s = &smcreg;
+	const struct applesmc_entry *e;
+	char newkey[5];
 	bool left_light_sensor = false, right_light_sensor = false;
 	unsigned int count;
 	u8 tmp[1];
@@ -788,6 +838,15 @@ static int applesmc_init_smcreg_try(void)
 	if (!s->cache)
 		return -ENOMEM;
 
+	scnprintf(newkey, sizeof(newkey), fan_speed_fmt[1], 1); //example value
+
+	e = applesmc_get_entry_by_key(newkey);
+	if (IS_ERR(e))
+		return PTR_ERR(e);
+
+	if (!strcmp(e->type, FLOAT_TYPE))
+		is_fan_speed_float = true;
+
 	ret = applesmc_read_key(FANS_COUNT, tmp, 1);
 	if (ret)
 		return ret;
@@ -820,6 +879,10 @@ static int applesmc_init_smcreg_try(void)
 	if (ret)
 		return ret;
 
+	ret = applesmc_has_key(FANS_MANUAL_FMT, &is_fan_manual_fmt);
+	if (ret)
+		return ret;
+
 	s->num_light_sensors = left_light_sensor + right_light_sensor;
 	s->init_complete = true;
 
@@ -1044,11 +1107,16 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
 	scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
 		  to_index(attr));
 
-	ret = applesmc_read_key(newkey, buffer, 2);
+	if (is_fan_speed_float) {
+		ret = applesmc_read_key(newkey, (u8 *) &speed, 4);
+		speed = applesmc_float_to_u32(speed);
+	} else {
+		ret = applesmc_read_key(newkey, buffer, 2);
+		speed = ((buffer[0] << 8 | buffer[1]) >> 2);
+	}
 	if (ret)
 		return ret;
 
-	speed = ((buffer[0] << 8 | buffer[1]) >> 2);
 	return sysfs_emit(sysfsbuf, "%u\n", speed);
 }
 
@@ -1067,10 +1135,14 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
 	scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
 		  to_index(attr));
 
-	buffer[0] = (speed >> 6) & 0xff;
-	buffer[1] = (speed << 2) & 0xff;
-	ret = applesmc_write_key(newkey, buffer, 2);
-
+	if (is_fan_speed_float) {
+		speed = applesmc_u32_to_float(speed);
+		ret = applesmc_write_key(newkey, (u8 *) &speed, 4);
+	} else {
+		buffer[0] = (speed >> 6) & 0xff;
+		buffer[1] = (speed << 2) & 0xff;
+		ret = applesmc_write_key(newkey, buffer, 2);
+	}
 	if (ret)
 		return ret;
 	else
@@ -1084,11 +1156,13 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
 	u16 manual = 0;
 	u8 buffer[2];
 
-	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
-	if (ret)
-		return ret;
-
-	manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
+	if (is_fan_manual_fmt) {
+		ret = applesmc_read_key(FANS_MANUAL_FMT, buffer, 1);
+		manual = buffer[0];
+	} else {
+		ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+		manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
+	}
 	return sysfs_emit(sysfsbuf, "%d\n", manual);
 }
 
@@ -1104,22 +1178,26 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
 	if (kstrtoul(sysfsbuf, 10, &input) < 0)
 		return -EINVAL;
 
-	ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
-	if (ret)
-		goto out;
-
-	val = (buffer[0] << 8 | buffer[1]);
+	if (is_fan_manual_fmt) {
+		buffer[0] = input & 0x01;
+		ret = applesmc_write_key(FANS_MANUAL_FMT, buffer, 1);
+	} else {
+		ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+		if (ret)
+			goto out;
 
-	if (input)
-		val = val | (0x01 << to_index(attr));
-	else
-		val = val & ~(0x01 << to_index(attr));
+		val = (buffer[0] << 8 | buffer[1]);
 
-	buffer[0] = (val >> 8) & 0xFF;
-	buffer[1] = val & 0xFF;
+		if (input)
+			val = val | (0x01 << to_index(attr));
+		else
+			val = val & ~(0x01 << to_index(attr));
 
-	ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+		buffer[0] = (val >> 8) & 0xFF;
+		buffer[1] = val & 0xFF;
 
+		ret = applesmc_write_key(FANS_MANUAL, buffer, 2);
+	}
 out:
 	if (ret)
 		return ret;
-- 
2.43.0
Re: [PATCH 2/2] hwmon/applesmc: add fan support for newer macs
Posted by kernel test robot 9 months, 1 week ago
Hi Subu,

kernel test robot noticed the following build warnings:

[auto build test WARNING on groeck-staging/hwmon-next]
[also build test WARNING on linus/master v6.14-rc6 next-20250313]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Subu-Dwevedi/hwmon-applesmc-add-MMIO-for-newer-macs/20250312-203248
base:   https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
patch link:    https://lore.kernel.org/r/20250312123055.1735-3-messigoatcr7nop%40gmail.com
patch subject: [PATCH 2/2] hwmon/applesmc: add fan support for newer macs
config: i386-randconfig-002-20250313 (https://download.01.org/0day-ci/archive/20250313/202503132205.Vf8imlWS-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250313/202503132205.Vf8imlWS-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202503132205.Vf8imlWS-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/hwmon/applesmc.c: In function 'applesmc_show_fan_manual':
>> drivers/hwmon/applesmc.c:1155:13: warning: variable 'ret' set but not used [-Wunused-but-set-variable]
    1155 |         int ret;
         |             ^~~


vim +/ret +1155 drivers/hwmon/applesmc.c

6f2fad748ccced5 Nicolas Boichat 2007-05-08  1151  
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1152  static ssize_t applesmc_show_fan_manual(struct device *dev,
3eba2bf7c5fb786 Henrik Rydberg  2010-11-09  1153  			struct device_attribute *attr, char *sysfsbuf)
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1154  {
6f2fad748ccced5 Nicolas Boichat 2007-05-08 @1155  	int ret;
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1156  	u16 manual = 0;
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1157  	u8 buffer[2];
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1158  
beab9ce267efe06 Subu Dwevedi    2025-03-12  1159  	if (is_fan_manual_fmt) {
beab9ce267efe06 Subu Dwevedi    2025-03-12  1160  		ret = applesmc_read_key(FANS_MANUAL_FMT, buffer, 1);
beab9ce267efe06 Subu Dwevedi    2025-03-12  1161  		manual = buffer[0];
beab9ce267efe06 Subu Dwevedi    2025-03-12  1162  	} else {
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1163  		ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
cecf7560f00a841 Tom Rix         2020-08-20  1164  		manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
beab9ce267efe06 Subu Dwevedi    2025-03-12  1165  	}
1f4d4af4d7a1c79 Guenter Roeck   2021-03-21  1166  	return sysfs_emit(sysfsbuf, "%d\n", manual);
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1167  }
6f2fad748ccced5 Nicolas Boichat 2007-05-08  1168  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki