drivers/hwmon/applesmc.c | 122 ++++++++++++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 22 deletions(-)
This patch adds support for fan control on T2 Macs in the applesmc driver.
It introduces functions to handle floating-point fan speed values
(which are required by t2 chips).
The fan speed reading and writing are updated to support
both integer and floating-point values.
The fan manual control is also updated to handle T2 Mac-specific keys.
Guenter Roeck asked "Does this limit still apply?"
so yes the limit still applies.
Changes since v2:
--- fixed checkpatch issues
--- used function such as BIT(), FIELD_GET(), BIT_MASK()
in function "applesmc_float_to_u32" and "applesmc_u32_to_float"
for readability.
--- added error handling in function applesmc_show_fan_speed
--- used applesmc_write_entry instead of applesmc_write_key
in function applesmc_store_fan_speed
--- defined "F%dMd" as FAN_MANUAL_FMT
Co-developed-by: Aun-Ali Zaidi <admin@kodeit.net>
Signed-off-by: Aun-Ali Zaidi <admin@kodeit.net>
Signed-off-by: Atharva Tiwari <evepolonium@gmail.com>
---
drivers/hwmon/applesmc.c | 122 ++++++++++++++++++++++++++++++++-------
1 file changed, 100 insertions(+), 22 deletions(-)
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index fc6d6a9053ce..e792d4283861 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -74,6 +74,7 @@
#define FAN_ID_FMT "F%dID" /* r-o char[16] */
#define TEMP_SENSOR_TYPE "sp78"
+#define FAN_MANUAL_FMT "F%dMd"
/* List of keys used to read/write fan speeds */
static const char *const fan_speed_fmt[] = {
@@ -511,6 +512,32 @@ static int applesmc_read_s16(const char *key, s16 *value)
return 0;
}
+static inline u32 applesmc_float_to_u32(u32 d)
+{
+ u8 sign = FIELD_GET(BIT(31), d);
+ s32 exp = FIELD_GET(BIT_MASK(8) << 23, d) - 0x7F;
+ u32 fr = d & ((1u << 23) - 1);
+
+ if (sign || exp < 0)
+ return 0;
+
+ return BIT(exp) + (fr >> (23 - exp));
+}
+
+static inline u32 applesmc_u32_to_float(u32 d)
+{
+ u32 dc = d, bc = 0, exp;
+
+ if (!d)
+ return 0;
+ while (dc >>= 1)
+ ++bc;
+ exp = 0x7F + bc;
+ return FIELD_PREP(BIT_MASK(8) << 23, exp) |
+ (d << (23 - (exp - 0x7F)) & BIT_MASK(23));
+
+}
+
/*
* applesmc_device_init - initialize the accelerometer. Can sleep.
*/
@@ -841,16 +868,33 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
int ret;
unsigned int speed = 0;
char newkey[5];
- u8 buffer[2];
+ u8 buffer[4] = {0};
+ const struct applesmc_entry *entry;
+ bool is_float = false;
scnprintf(newkey, sizeof(newkey), fan_speed_fmt[to_option(attr)],
to_index(attr));
- ret = applesmc_read_key(newkey, buffer, 2);
+ entry = applesmc_get_entry_by_key(newkey);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+
+ if (!strcmp(entry->type, "flt"))
+ is_float = true;
+
+ if (is_float) {
+ ret = applesmc_read_entry(entry, buffer, 4);
+ if (ret)
+ return ret;
+ speed = applesmc_float_to_u32(*(u32 *)buffer);
+ } else {
+ ret = applesmc_read_entry(entry, buffer, 2);
+ if (ret)
+ return ret;
+ 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);
}
@@ -861,7 +905,9 @@ static ssize_t applesmc_store_fan_speed(struct device *dev,
int ret;
unsigned long speed;
char newkey[5];
- u8 buffer[2];
+ u8 buffer[4];
+ const struct applesmc_entry *entry;
+ bool is_float = false;
if (kstrtoul(sysfsbuf, 10, &speed) < 0 || speed >= 0x4000)
return -EINVAL; /* Bigger than a 14-bit value */
@@ -869,9 +915,20 @@ 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);
+ entry = applesmc_get_entry_by_key(newkey);
+ if (IS_ERR(entry))
+ return PTR_ERR(entry);
+ if (!strcmp(entry->type, "flt"))
+ is_float = true;
+
+ if (is_float) {
+ *(u32 *)buffer = applesmc_u32_to_float(speed);
+ ret = applesmc_write_entry(entry, buffer, 4);
+ } else {
+ buffer[0] = (speed >> 6) & 0xff;
+ buffer[1] = (speed << 2) & 0xff;
+ ret = applesmc_write_entry((const struct applesmc_entry *)newkey, buffer, 2);
+ }
if (ret)
return ret;
@@ -885,12 +942,23 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
int ret;
u16 manual = 0;
u8 buffer[2];
+ char newkey[5];
+ bool has_newkey = false;
+
+ scnprintf(newkey, sizeof(newkey), FAN_MANUAL_FMT, to_index(attr));
+
+ ret = applesmc_has_key(newkey, &has_newkey);
+ if (has_newkey) {
+ ret = applesmc_read_key(newkey, buffer, 1);
+ manual = buffer[0] & 0x01;
+ } else {
+ ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+ manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
+ }
- ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
if (ret)
return ret;
- manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
return sysfs_emit(sysfsbuf, "%d\n", manual);
}
@@ -900,28 +968,38 @@ static ssize_t applesmc_store_fan_manual(struct device *dev,
{
int ret;
u8 buffer[2];
+ char newkey[5];
+ bool has_newkey = false;
unsigned long input;
u16 val;
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]);
+ scnprintf(newkey, sizeof(newkey), FAN_MANUAL_FMT, to_index(attr));
- if (input)
- val = val | (0x01 << to_index(attr));
- else
- val = val & ~(0x01 << to_index(attr));
+ ret = applesmc_has_key(newkey, &has_newkey);
+ if (ret)
+ return ret;
+ if (has_newkey) {
+ buffer[0] = (input != 0x00);
+ ret = applesmc_write_key(newkey, buffer, 1);
+ } else {
+ ret = applesmc_read_key(FANS_MANUAL, buffer, 2);
+ val = (buffer[0] << 8 | buffer[1]);
+ if (ret)
+ goto out;
- 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.39.5
Hi Atharva,
kernel test robot noticed the following build errors:
[auto build test ERROR on groeck-staging/hwmon-next]
[also build test ERROR on linus/master v6.13-rc7 next-20250113]
[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/Atharva-Tiwari/hwmon-Add-T2-Mac-fan-control-support-in-applesmc-driver/20250113-164741
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
patch link: https://lore.kernel.org/r/20250113084503.228037-1-evepolonium%40gmail.com
patch subject: [PATCH v3] hwmon: Add T2 Mac fan control support in applesmc driver
config: i386-buildonly-randconfig-004-20250114 (https://download.01.org/0day-ci/archive/20250114/202501140453.bPb2q7KC-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/20250114/202501140453.bPb2q7KC-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/202501140453.bPb2q7KC-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/hwmon/applesmc.c: In function 'applesmc_float_to_u32':
>> drivers/hwmon/applesmc.c:517:19: error: implicit declaration of function 'FIELD_GET' [-Werror=implicit-function-declaration]
517 | u8 sign = FIELD_GET(BIT(31), d);
| ^~~~~~~~~
drivers/hwmon/applesmc.c: In function 'applesmc_u32_to_float':
>> drivers/hwmon/applesmc.c:536:16: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration]
536 | return FIELD_PREP(BIT_MASK(8) << 23, exp) |
| ^~~~~~~~~~
cc1: some warnings being treated as errors
vim +/FIELD_GET +517 drivers/hwmon/applesmc.c
514
515 static inline u32 applesmc_float_to_u32(u32 d)
516 {
> 517 u8 sign = FIELD_GET(BIT(31), d);
518 s32 exp = FIELD_GET(BIT_MASK(8) << 23, d) - 0x7F;
519 u32 fr = d & ((1u << 23) - 1);
520
521 if (sign || exp < 0)
522 return 0;
523
524 return BIT(exp) + (fr >> (23 - exp));
525 }
526
527 static inline u32 applesmc_u32_to_float(u32 d)
528 {
529 u32 dc = d, bc = 0, exp;
530
531 if (!d)
532 return 0;
533 while (dc >>= 1)
534 ++bc;
535 exp = 0x7F + bc;
> 536 return FIELD_PREP(BIT_MASK(8) << 23, exp) |
537 (d << (23 - (exp - 0x7F)) & BIT_MASK(23));
538
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Atharva,
kernel test robot noticed the following build errors:
[auto build test ERROR on groeck-staging/hwmon-next]
[also build test ERROR on linus/master v6.13-rc7 next-20250113]
[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/Atharva-Tiwari/hwmon-Add-T2-Mac-fan-control-support-in-applesmc-driver/20250113-164741
base: https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
patch link: https://lore.kernel.org/r/20250113084503.228037-1-evepolonium%40gmail.com
patch subject: [PATCH v3] hwmon: Add T2 Mac fan control support in applesmc driver
config: i386-buildonly-randconfig-002-20250114 (https://download.01.org/0day-ci/archive/20250114/202501140359.tA17RgoT-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250114/202501140359.tA17RgoT-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/202501140359.tA17RgoT-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/hwmon/applesmc.c:517:12: error: call to undeclared function 'FIELD_GET'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
517 | u8 sign = FIELD_GET(BIT(31), d);
| ^
>> drivers/hwmon/applesmc.c:536:9: error: call to undeclared function 'FIELD_PREP'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
536 | return FIELD_PREP(BIT_MASK(8) << 23, exp) |
| ^
2 errors generated.
vim +/FIELD_GET +517 drivers/hwmon/applesmc.c
514
515 static inline u32 applesmc_float_to_u32(u32 d)
516 {
> 517 u8 sign = FIELD_GET(BIT(31), d);
518 s32 exp = FIELD_GET(BIT_MASK(8) << 23, d) - 0x7F;
519 u32 fr = d & ((1u << 23) - 1);
520
521 if (sign || exp < 0)
522 return 0;
523
524 return BIT(exp) + (fr >> (23 - exp));
525 }
526
527 static inline u32 applesmc_u32_to_float(u32 d)
528 {
529 u32 dc = d, bc = 0, exp;
530
531 if (!d)
532 return 0;
533 while (dc >>= 1)
534 ++bc;
535 exp = 0x7F + bc;
> 536 return FIELD_PREP(BIT_MASK(8) << 23, exp) |
537 (d << (23 - (exp - 0x7F)) & BIT_MASK(23));
538
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.