diff --git a/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml b/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml index 4e0b4268f3ffa..b8e962334935f 100644 --- a/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml +++ b/Documentation/devicetree/bindings/hwmon/apple,smc-hwmon.yaml @@ -40,6 +40,28 @@ properties: compatible: const: apple,smc-hwmon + apple,soc-die-keys: + $ref: /schemas/types.yaml#/definitions/string-array + minItems: 1 + maxItems: 32 + items: + pattern: "^[A-Za-z0-9]{4}$" + description: + SMC FourCC keys for SoC die temperature sensors. Each key must report a + readable four-byte floating-point temperature in degrees Celsius. The + hottest reading supplies the macsmc_soc_die thermal zone. These channels + follow the temperature-* children in the listed order and are labelled + "SoC die ". Keys absent from the running firmware or with an + unsupported format are skipped. These channels do not change the indices + used by thermal-sensors references to temperature-* children. + + apple,thermal-pressure-key: + $ref: /schemas/types.yaml#/definitions/string + pattern: "^[A-Za-z0-9]{4}$" + description: + SMC FourCC key reporting the firmware thermal pressure level as a + readable signed 32-bit integer. This is exposed for monitoring only. + apple,discover-temperatures: type: boolean description: @@ -95,6 +117,16 @@ patternProperties: pattern: "^[A-Za-z0-9]{4}$" description: Writeable key to enable/disable manual fan control + apple,fan-manual-control: + type: boolean + description: + The SMC firmware of this machine accepts a manual speed for this + fan from the host and returns it to firmware control when asked, + so the fan's target speed can be set from the OS. Requires + apple,fan-target and apple,fan-mode. Without it the fan is only + monitored, and driven by the thermal framework where the SMC + node is a cooling device. + "^power-[A-Za-z0-9]{4}$": $ref: "#/$defs/sensor" unevaluatedProperties: false diff --git a/Documentation/hwmon/macsmc-hwmon.rst b/Documentation/hwmon/macsmc-hwmon.rst index 6903f76df62bf..2204745326cba 100644 --- a/Documentation/hwmon/macsmc-hwmon.rst +++ b/Documentation/hwmon/macsmc-hwmon.rst @@ -22,10 +22,28 @@ the desktop Macs), sensors present on any given machine are described via Devicetree. The driver picks these up and registers them with hwmon when probed. -Manual fan speed is supported via the fan_control module parameter. This -is disabled by default and marked as unsafe, as it cannot be proven that -the system will fail safe if overheating due to manual fan control being -used. +Manual fan speed is available on the fans whose Devicetree node has +apple,fan-manual-control, which a machine's Devicetree sets once its SMC +firmware is known to accept a manual speed and to take the fan back. On +those fans fanX_target is writable; writing 0 hands the fan back to the +firmware. The fan_control module parameter, disabled by default and marked +as unsafe, allows manual speeds on every fan with a mode key whatever the +Devicetree says. It cannot be proven that the system will fail safe if it +overheats under a manual fan speed. + +SoC die temperatures are described by the ``apple,soc-die-keys`` string list +on the hwmon device node. The driver appends these temperature channels after +the ``temperature-*`` children, in list order, with labels ``SoC die ``. +Only readable four-byte floating-point keys are used. When thermal support is +enabled, their hottest reading supplies the ``macsmc_soc_die`` thermal zone. +A failed or implausible reading fails the zone read rather than reporting a +partial result. Keys absent from the firmware are skipped. + +The optional ``apple,thermal-pressure-key`` on the same node describes a +readable signed 32-bit firmware thermal pressure level. Neither feature needs +a module parameter. Kernels using these properties need an updated device +tree; the former downstream per-board fallback and its ``soc_die_keys`` and +``board_keys`` parameters have been removed. sysfs interface --------------- @@ -69,3 +87,7 @@ tempX_input tempX_label Temperature sensor label +thermal_pressure_level + Firmware thermal pressure level, exposed read-only when the device tree + supplies a valid key. The integer is firmware-defined and is not a + temperature or a percentage. diff --git a/arch/arm64/boot/dts/apple/t6030-j516s.dts b/arch/arm64/boot/dts/apple/t6030-j516s.dts index edc00c0c1c4df..39ee54f235c0d 100644 --- a/arch/arm64/boot/dts/apple/t6030-j516s.dts +++ b/arch/arm64/boot/dts/apple/t6030-j516s.dts @@ -97,3 +97,11 @@ &isp { apple,platform-id = <16>; }; + +&smc_hwmon { + /* M3-family die keys, not yet confirmed on J516s; absent keys are skipped. */ + apple,soc-die-keys = "Tf04", "Tf09", "Tf0A", "Tf0B", "Tf0D", + "Tf0E", "Tf14", "Tf18", "Tf19", "Tf1A", + "Tf24", "Tf25", "Tf2A", "Tf2B"; + apple,thermal-pressure-key = "mTPL"; +}; diff --git a/arch/arm64/boot/dts/apple/t6031-j514c.dts b/arch/arm64/boot/dts/apple/t6031-j514c.dts index f4f282e9d3207..42aeffa229c75 100644 --- a/arch/arm64/boot/dts/apple/t6031-j514c.dts +++ b/arch/arm64/boot/dts/apple/t6031-j514c.dts @@ -53,3 +53,12 @@ compatible = "apple,j514-macaudio", "apple,j314-macaudio", "apple,macaudio"; model = "MacBook Pro J514"; }; + +&smc_hwmon { + /* Assumed to match J516c; unavailable firmware keys are skipped. */ + apple,soc-die-keys = "Tf04", "Tf06", "Tf09", "Tf0A", "Tf0B", + "Tf14", "Tf16", "Tf19", "Tf1A", "Tf1B", + "Tf24", "Tf26", "Tf29", "Tf2A", "Tf2B", + "Tf34", "Tf36", "Tf39", "Tf3A", "Tf3B", + "Tf44", "Tf46", "Tf49", "Tf4A", "Tf4B"; +}; diff --git a/arch/arm64/boot/dts/apple/t6031-j516c.dts b/arch/arm64/boot/dts/apple/t6031-j516c.dts index 562491755117f..9b3afec0903e1 100644 --- a/arch/arm64/boot/dts/apple/t6031-j516c.dts +++ b/arch/arm64/boot/dts/apple/t6031-j516c.dts @@ -53,3 +53,12 @@ compatible = "apple,j516-macaudio", "apple,j316-macaudio", "apple,macaudio"; model = "MacBook Pro J516"; }; + +&smc_hwmon { + /* M3 Max die keys; unavailable firmware keys are skipped. */ + apple,soc-die-keys = "Tf04", "Tf06", "Tf09", "Tf0A", "Tf0B", + "Tf14", "Tf16", "Tf19", "Tf1A", "Tf1B", + "Tf24", "Tf26", "Tf29", "Tf2A", "Tf2B", + "Tf34", "Tf36", "Tf39", "Tf3A", "Tf3B", + "Tf44", "Tf46", "Tf49", "Tf4A", "Tf4B"; +}; diff --git a/arch/arm64/boot/dts/apple/t6040-j616s.dts b/arch/arm64/boot/dts/apple/t6040-j616s.dts index 83b29a6c3f866..69f0070af7659 100644 --- a/arch/arm64/boot/dts/apple/t6040-j616s.dts +++ b/arch/arm64/boot/dts/apple/t6040-j616s.dts @@ -3868,6 +3868,19 @@ */ &smc_hwmon { #cooling-cells = <3>; + + /* + * The SMC accepts a manual speed for both fans once its fan test + * key unlocks them, and returns them to system mode when the + * request is dropped. + */ + fan-F0Ac { + apple,fan-manual-control; + }; + + fan-F1Ac { + apple,fan-manual-control; + }; }; &pcie_dart { diff --git a/drivers/hwmon/macsmc-hwmon.c b/drivers/hwmon/macsmc-hwmon.c index c0ca04e88d3d2..af67979f51213 100644 --- a/drivers/hwmon/macsmc-hwmon.c +++ b/drivers/hwmon/macsmc-hwmon.c @@ -49,68 +49,15 @@ #define FLT_MANT_MASK GENMASK(22, 0) #define FLT_SIGN_MASK BIT(31) +/* + * Fans whose device tree node has apple,fan-manual-control take a target speed + * from userspace. This parameter, kept for compatibility, allows it for every + * fan with a mode key, whatever the device tree says. + */ static bool fan_control; module_param_unsafe(fan_control, bool, 0644); MODULE_PARM_DESC(fan_control, - "Override the SMC to set your own fan speeds on supported machines"); - -/* - * SoC die temperature keys: temperature channels labelled "SoC die " - * whose hottest reading is the SoC die thermal zone (see - * macsmc_hwmon_die_get_temp()). They come from this parameter or, without it, - * from the machine's entry in macsmc_hwmon_boards[]. Keys the SMC does not - * have are skipped. - */ -static char *soc_die_keys; -module_param(soc_die_keys, charp, 0444); -MODULE_PARM_DESC(soc_die_keys, - "Comma-separated SMC temperature keys whose hottest reading is the SoC die thermal zone, \"none\" for no zone (default: the machine's list)"); - -/* - * Keys a machine's device tree does not list yet. The device tree comes from - * the boot loader, so these let a kernel use keys before its device tree - * catches up. board_keys=0 turns them off: the device tree alone then decides - * (soc_die_keys still applies). - */ -static bool board_keys = true; -module_param(board_keys, bool, 0444); -MODULE_PARM_DESC(board_keys, - "Use the built-in per-machine SoC die and thermal pressure keys the device tree does not list (default: on)"); - -static const struct macsmc_hwmon_board { - const char *compatible; - const char *die_keys; - const char *pressure_key; -} macsmc_hwmon_boards[] = { - /* - * MacBook Pro (16-inch, M3 Pro): key names used for the CPU - * performance-core and GPU die sensors of M3-family machines. They - * are not confirmed on this machine: the debugfs key list shows which - * temperature keys it has. - */ - { - .compatible = "apple,j516s", - .die_keys = "Tf04,Tf09,Tf0A,Tf0B,Tf0D,Tf0E,Tf14,Tf18,Tf19,Tf1A,Tf24,Tf25,Tf2A,Tf2B", - .pressure_key = "mTPL", - }, - /* - * MacBook Pro (14-inch and 16-inch, M3 Max): the same key family in - * five groups. On a J516C every one of these keys read a die - * temperature (47-62 C at idle); the keys ending in D and E repeated - * the one ending in 4 and are left out. The J514C list is assumed to - * be the same; keys the SMC does not have are skipped. - */ - { - .compatible = "apple,j516c", - .die_keys = "Tf04,Tf06,Tf09,Tf0A,Tf0B,Tf14,Tf16,Tf19,Tf1A,Tf1B,Tf24,Tf26,Tf29,Tf2A,Tf2B," - "Tf34,Tf36,Tf39,Tf3A,Tf3B,Tf44,Tf46,Tf49,Tf4A,Tf4B", - }, - { - .compatible = "apple,j514c", - .die_keys = "Tf04,Tf06,Tf09,Tf0A,Tf0B,Tf14,Tf16,Tf19,Tf1A,Tf1B,Tf24,Tf26,Tf29,Tf2A,Tf2B," - "Tf34,Tf36,Tf39,Tf3A,Tf3B,Tf44,Tf46,Tf49,Tf4A,Tf4B", - }, -}; + "Allow manual fan speeds on every fan with a mode key, also where the device tree does not (default: the device tree decides)"); static bool macsmc_hwmon_is_m3(void) { @@ -121,52 +68,6 @@ static bool macsmc_hwmon_is_m3(void) of_machine_is_compatible("apple,t8122"); } -static const struct macsmc_hwmon_board *macsmc_hwmon_board(void) -{ - unsigned int i; - - if (!board_keys) - return NULL; - for (i = 0; i < ARRAY_SIZE(macsmc_hwmon_boards); i++) - if (of_machine_is_compatible(macsmc_hwmon_boards[i].compatible)) - return &macsmc_hwmon_boards[i]; - return NULL; -} - -/* The list of SoC die temperature keys, or NULL for none. */ -static const char *macsmc_hwmon_die_key_list(void) -{ - const struct macsmc_hwmon_board *board; - - if (!macsmc_hwmon_is_m3()) - return NULL; - if (soc_die_keys) - return strcmp(soc_die_keys, "none") ? soc_die_keys : NULL; - board = macsmc_hwmon_board(); - return board ? board->die_keys : NULL; -} - -/* - * The next key of a comma-separated list: returns 1 and the key, 0 at the end - * of the list, or -EINVAL for an entry that is not four characters long. - */ -static int macsmc_hwmon_next_key(const char **list, char key[5]) -{ - const char *p = *list, *end; - - while (*p == ',' || *p == ' ') - p++; - if (!*p) - return 0; - end = strchrnul(p, ','); - *list = end; - if (end - p != 4) - return -EINVAL; - memcpy(key, p, 4); - key[4] = '\0'; - return 1; -} - struct macsmc_hwmon_sensor { struct apple_smc_key_info info; smc_key macsmc_key; @@ -185,6 +86,8 @@ struct macsmc_hwmon_fan { char label[MAX_LABEL_LENGTH]; u32 attrs; bool manual; + /* The device tree allows a manual speed from userspace. */ + bool manual_control; /* Index of the fan node among the fan-* nodes: its cooling device id. */ u32 index; struct macsmc_hwmon *hwmon; @@ -689,12 +592,18 @@ static int macsmc_hwmon_fan_apply(struct macsmc_hwmon *hwmon, return 0; } +/* Whether userspace may set this fan's speed. */ +static bool macsmc_hwmon_fan_user_control(const struct macsmc_hwmon_fan *fan) +{ + return fan->mode.macsmc_key && (fan->manual_control || fan_control); +} + static int macsmc_hwmon_set_fan(struct macsmc_hwmon *hwmon, int channel, long val) { struct macsmc_hwmon_fan *fan = &hwmon->fan.fans[channel]; int ret; - if (!fan_control || fan->mode.macsmc_key == 0) + if (!macsmc_hwmon_fan_user_control(fan)) return -EOPNOTSUPP; if (val < 0) return -EINVAL; @@ -899,7 +808,7 @@ static umode_t macsmc_hwmon_fan_is_visible(const struct macsmc_hwmon_fan *fan, u32 attr) { if (fan->attrs & BIT(attr)) { - if (attr == hwmon_fan_target && fan_control && fan->mode.macsmc_key) + if (attr == hwmon_fan_target && macsmc_hwmon_fan_user_control(fan)) return 0644; return 0444; @@ -1055,15 +964,10 @@ static int macsmc_hwmon_create_pressure(struct macsmc_hwmon *hwmon, ret = of_property_read_string(hwmon_node, "apple,thermal-pressure-key", &key); - if (ret == -EINVAL) { - const struct macsmc_hwmon_board *board = macsmc_hwmon_board(); - - if (!board || !board->pressure_key) - return 0; - key = board->pressure_key; - } else if (ret) { + if (ret == -EINVAL) + return 0; + if (ret) return ret; - } ret = macsmc_hwmon_parse_key(hwmon->dev, hwmon->smc, &pressure, key); if (ret) @@ -1237,6 +1141,14 @@ static int macsmc_hwmon_create_fan(struct device *dev, struct apple_smc *smc, return ret; } + if (of_property_read_bool(fan_node, "apple,fan-manual-control")) { + if (fan->mode.macsmc_key && (fan->attrs & HWMON_F_TARGET)) + fan->manual_control = true; + else + dev_warn(dev, "%s: apple,fan-manual-control needs apple,fan-target and apple,fan-mode\n", + fan->label); + } + /* Initialise fan control mode to automatic */ fan->manual = false; @@ -1249,10 +1161,15 @@ static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon, struct device_node *key_node __maybe_unused; struct macsmc_hwmon_sensor *sensor; u32 n_current = 0, n_fan = 0, n_power = 0, n_temperature = 0, n_voltage = 0; - const char *die_keys = macsmc_hwmon_die_key_list(), *list; - char key[5]; + const char *key; bool indexed; - int ret; + int n_die_keys, i, ret; + + n_die_keys = of_property_count_strings(hwmon_node, "apple,soc-die-keys"); + if (n_die_keys == -EINVAL) + n_die_keys = 0; + else if (n_die_keys < 0) + return n_die_keys; for_each_child_of_node_with_prefix(hwmon_node, key_node, "current-") { n_current++; @@ -1324,8 +1241,7 @@ static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon, for_each_child_of_node_with_prefix(hwmon_node, key_node, "temperature-") { n_temperature++; } - for (list = die_keys; list && (ret = macsmc_hwmon_next_key(&list, key));) - n_temperature++; + n_temperature += n_die_keys; if (n_temperature) { hwmon->temp.sensors = devm_kcalloc(hwmon->dev, n_temperature, @@ -1351,10 +1267,13 @@ static int macsmc_hwmon_populate_sensors(struct macsmc_hwmon *hwmon, macsmc_hwmon_add_temp_channel(&hwmon->temp, ret); } - for (list = die_keys; list && (ret = macsmc_hwmon_next_key(&list, key));) { - if (ret < 0) { - dev_warn(hwmon->dev, "ignoring an entry of the SoC die key list \"%s\"\n", - die_keys); + for (i = 0; i < n_die_keys; i++) { + ret = of_property_read_string_index(hwmon_node, "apple,soc-die-keys", + i, &key); + if (ret) + return ret; + if (strlen(key) != 4) { + dev_warn(hwmon->dev, "Ignoring invalid SoC die key %s\n", key); continue; } hwmon->die_listed++; @@ -1692,11 +1611,9 @@ static int macsmc_hwmon_probe(struct platform_device *pdev) return ret; } - if (macsmc_hwmon_is_m3()) { - ret = macsmc_hwmon_create_pressure(hwmon, hwmon->dev->of_node); - if (ret) - return ret; - } + ret = macsmc_hwmon_create_pressure(hwmon, hwmon->dev->of_node); + if (ret) + return ret; ret = macsmc_hwmon_discover_temperatures(hwmon); if (ret) @@ -1736,17 +1653,17 @@ static int macsmc_hwmon_probe(struct platform_device *pdev) hwmon->hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "macsmc_hwmon", hwmon, &hwmon->chip_info, - macsmc_hwmon_is_m3() ? macsmc_hwmon_extra_groups : NULL); + macsmc_hwmon_extra_groups); if (IS_ERR(hwmon->hwmon_dev)) return dev_err_probe(hwmon->dev, PTR_ERR(hwmon->hwmon_dev), "Probing SMC hwmon device failed\n"); - if (macsmc_hwmon_is_m3()) { + if (macsmc_hwmon_is_m3()) macsmc_hwmon_debugfs_init(hwmon); - ret = macsmc_hwmon_die_register(hwmon); - if (ret) - return ret; - } + + ret = macsmc_hwmon_die_register(hwmon); + if (ret) + return ret; /* * Released before the restore action above, which stops the cooling