Skip to content

Commit a566fd3

Browse files
chadmedjannau
authored andcommitted
hwmon: macsmc: wire up manual fan control support
The SMC provides an interface for manually controlling the speeds of any fans attached to it. Expose this via the standard hwmon interface. Once a fan is in manual control, the SMC makes no attempts to save users from themselves. It is possible to write arbitrary values outside of the SMC's reported safe range. The driver therefore does its own sanity checking. Since we are unsure whether or not leaving the fans in manual mode can cause damage to Apple Silicon devices, this functionality is gated behind a very explicit and scary-sounding unsafe module parameter. Signed-off-by: James Calligeros <jcalligeros99@gmail.com>
1 parent ca16ba0 commit a566fd3

1 file changed

Lines changed: 115 additions & 1 deletion

File tree

drivers/hwmon/macsmc-hwmon.c

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@
3030
#define MAX_LABEL_LENGTH 32
3131
#define NUM_SENSOR_TYPES 5 /* temp, volt, current, power, fan */
3232

33+
static bool melt_my_mac;
34+
module_param_unsafe(melt_my_mac, bool, 0644);
35+
MODULE_PARM_DESC(melt_my_mac, "Override the SMC to set your own fan speeds on supported machines");
36+
3337
struct macsmc_hwmon_sensor {
3438
struct apple_smc_key_info info;
3539
smc_key macsmc_key;
@@ -41,8 +45,10 @@ struct macsmc_hwmon_fan {
4145
struct macsmc_hwmon_sensor min;
4246
struct macsmc_hwmon_sensor max;
4347
struct macsmc_hwmon_sensor set;
48+
struct macsmc_hwmon_sensor mode;
4449
char label[MAX_LABEL_LENGTH];
4550
u32 attrs;
51+
bool manual;
4652
};
4753

4854
struct macsmc_hwmon_sensors {
@@ -152,6 +158,21 @@ static int macsmc_hwmon_read_key(struct apple_smc *smc,
152158
return 0;
153159
}
154160

161+
static int macsmc_hwmon_write_key(struct apple_smc *smc,
162+
struct macsmc_hwmon_sensor *sensor, long val,
163+
int scale)
164+
{
165+
switch (sensor->info.type_code) {
166+
/* 32-bit IEEE 754 float */
167+
case __SMC_KEY('f', 'l', 't', ' '):
168+
return apple_smc_write_f32_scaled(smc, sensor->macsmc_key, val, scale);
169+
case __SMC_KEY('u', 'i', '8', ' '):
170+
return apple_smc_write_u8(smc, sensor->macsmc_key, val);
171+
default:
172+
return -EOPNOTSUPP;
173+
}
174+
}
175+
155176
static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan, long *val)
156177
{
157178
if (!(hwmon->fan.fans[chan].attrs & BIT(attr)))
@@ -175,6 +196,61 @@ static int macsmc_hwmon_read_fan(struct macsmc_hwmon *hwmon, u32 attr, int chan,
175196
}
176197
}
177198

199+
static int macsmc_hwmon_write_fan(struct device *dev, u32 attr, int channel, long val)
200+
{
201+
struct macsmc_hwmon *hwmon = dev_get_drvdata(dev);
202+
int ret = 0;
203+
long min = 0;
204+
long max = 0;
205+
206+
if (!melt_my_mac ||
207+
hwmon->fan.fans[channel].mode.macsmc_key == 0)
208+
return -EOPNOTSUPP;
209+
210+
if ((channel >= hwmon->fan.n_fans) ||
211+
!(hwmon->fan.fans[channel].attrs & BIT(attr)) ||
212+
(attr != hwmon_fan_target))
213+
return -EINVAL;
214+
215+
/*
216+
* The SMC does no sanity checks on requested fan speeds, so we need to.
217+
*/
218+
ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].min, 1, &min);
219+
if (ret)
220+
return ret;
221+
ret = macsmc_hwmon_read_key(hwmon->smc, &hwmon->fan.fans[channel].max, 1, &max);
222+
if (ret)
223+
return ret;
224+
225+
if (val >= min && val <= max) {
226+
if (!hwmon->fan.fans[channel].manual) {
227+
/* Write 1 to mode key for manual control */
228+
ret = macsmc_hwmon_write_key(hwmon->smc, &hwmon->fan.fans[channel].mode, 1, 1);
229+
if (ret < 0)
230+
return ret;
231+
232+
hwmon->fan.fans[channel].manual = true;
233+
dev_info(dev, "Fan %d now under manual control! Set target speed to 0 for automatic control.\n",
234+
channel + 1);
235+
}
236+
return macsmc_hwmon_write_key(hwmon->smc, &hwmon->fan.fans[channel].set, val, 1);
237+
} else if (!val) {
238+
if (hwmon->fan.fans[channel].manual) {
239+
dev_info(dev, "Returning control of fan %d to SMC.\n", channel + 1);
240+
ret = macsmc_hwmon_write_key(hwmon->smc, &hwmon->fan.fans[channel].mode, 0, 1);
241+
if (ret < 0)
242+
return ret;
243+
244+
hwmon->fan.fans[channel].manual = false;
245+
}
246+
} else {
247+
dev_err(dev, "Requested fan speed %ld out of range [%ld, %ld]", val, min, max);
248+
return -EINVAL;
249+
}
250+
251+
return 0;
252+
}
253+
178254
static int macsmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
179255
u32 attr, int channel, long *val)
180256
{
@@ -212,13 +288,38 @@ static int macsmc_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
212288
static int macsmc_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
213289
u32 attr, int channel, long val)
214290
{
215-
return -EOPNOTSUPP;
291+
switch (type) {
292+
case hwmon_fan:
293+
return macsmc_hwmon_write_fan(dev, attr, channel, val);
294+
default:
295+
return -EOPNOTSUPP;
296+
}
297+
}
298+
299+
static umode_t macsmc_hwmon_fan_is_visible(const void *data, u32 attr, int channel)
300+
{
301+
const struct macsmc_hwmon *hwmon = data;
302+
303+
if (channel >= hwmon->fan.n_fans)
304+
return -EINVAL;
305+
306+
if (melt_my_mac && attr == hwmon_fan_target && hwmon->fan.fans[channel].mode.macsmc_key != 0)
307+
return 0644;
308+
309+
return 0444;
216310
}
217311

218312
static umode_t macsmc_hwmon_is_visible(const void *data,
219313
enum hwmon_sensor_types type, u32 attr,
220314
int channel)
221315
{
316+
switch (type) {
317+
case hwmon_fan:
318+
return macsmc_hwmon_fan_is_visible(data, attr, channel);
319+
default:
320+
break;
321+
}
322+
222323
return 0444;
223324
}
224325

@@ -292,6 +393,7 @@ static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc,
292393
const char *min;
293394
const char *max;
294395
const char *set;
396+
const char *mode;
295397
int ret = 0;
296398

297399
ret = of_property_read_string(fan_node, "apple,key-id", &now);
@@ -335,6 +437,18 @@ static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc,
335437
fan->attrs |= HWMON_F_TARGET;
336438
}
337439

440+
ret = of_property_read_string(fan_node, "apple,fan-mode", &mode);
441+
if (ret)
442+
dev_warn(dev, "No fan mode key for %s", fan->label);
443+
else {
444+
ret = macsmc_hwmon_parse_key(dev, smc, &fan->mode, mode);
445+
if (ret)
446+
return ret;
447+
}
448+
449+
/* Initialise fan control mode to automatic */
450+
fan->manual = false;
451+
338452
return 0;
339453
}
340454

0 commit comments

Comments
 (0)