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

Subu Dwevedi posted 2 patches 11 months ago
[PATCH 2/2] hwmon/applesmc: add fan support for newer macs
Posted by Subu Dwevedi 11 months 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 11 months 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