···11+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)22+%YAML 1.233+---44+$id: http://devicetree.org/schemas/gpio/apple,smc-gpio.yaml#55+$schema: http://devicetree.org/meta-schemas/core.yaml#66+77+title: Apple Mac System Management Controller GPIO88+99+maintainers:1010+ - Sven Peter <sven@kernel.org>1111+1212+description:1313+ Apple Mac System Management Controller GPIO block.1414+1515+properties:1616+ compatible:1717+ const: apple,smc-gpio1818+1919+ gpio-controller: true2020+2121+ '#gpio-cells':2222+ const: 22323+2424+required:2525+ - compatible2626+ - gpio-controller2727+ - '#gpio-cells'2828+2929+additionalProperties: false
···11+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)22+%YAML 1.233+---44+$id: http://devicetree.org/schemas/power/reset/apple,smc-reboot.yaml#55+$schema: http://devicetree.org/meta-schemas/core.yaml#66+77+title: Apple SMC Reboot Controller88+99+description:1010+ The Apple System Management Controller (SMC) provides reboot functionality1111+ on Apple Silicon SoCs. It uses NVMEM cells to store and track various1212+ system state information related to boot, shutdown, and panic events.1313+1414+maintainers:1515+ - Sven Peter <sven@kernel.org>1616+1717+properties:1818+ compatible:1919+ const: apple,smc-reboot2020+2121+ nvmem-cells:2222+ items:2323+ - description: Flag indicating shutdown (as opposed to reboot)2424+ - description: Stage at which the boot process stopped (0x30 for normal boot)2525+ - description: Counter for boot errors2626+ - description: Counter for system panics2727+2828+ nvmem-cell-names:2929+ items:3030+ - const: shutdown_flag3131+ - const: boot_stage3232+ - const: boot_error_count3333+ - const: panic_count3434+3535+required:3636+ - compatible3737+ - nvmem-cells3838+ - nvmem-cell-names3939+4040+additionalProperties: false
···14651465 This driver can also be built as a module. If so, the module will be14661466 called gpio-lp87565.1467146714681468+config GPIO_MACSMC14691469+ tristate "Apple Mac SMC GPIO"14701470+ depends on MFD_MACSMC14711471+ help14721472+ Support for GPIOs controlled by the SMC microcontroller on Apple Mac14731473+ systems.14741474+14751475+ This driver can also be built as a module. If so, the module will be14761476+ called gpio-macsmc.14771477+14681478config GPIO_MADERA14691479 tristate "Cirrus Logic Madera class codecs"14701480 depends on PINCTRL_MADERA
···11+// SPDX-License-Identifier: GPL-2.0-only OR MIT22+/*33+ * Apple SMC GPIO driver44+ * Copyright The Asahi Linux Contributors55+ *66+ * This driver implements basic SMC PMU GPIO support that can read inputs77+ * and write outputs. Mode changes and IRQ config are not yet implemented.88+ */99+1010+#include <linux/bitmap.h>1111+#include <linux/device.h>1212+#include <linux/gpio/driver.h>1313+#include <linux/mfd/core.h>1414+#include <linux/mfd/macsmc.h>1515+1616+#define MAX_GPIO 641717+1818+/*1919+ * Commands 0-6 are, presumably, the intended API.2020+ * Command 0xff lets you get/set the pin configuration in detail directly,2121+ * but the bit meanings seem not to be stable between devices/PMU hardware2222+ * versions.2323+ *2424+ * We're going to try to make do with the low commands for now.2525+ * We don't implement pin mode changes at this time.2626+ */2727+2828+#define CMD_ACTION (0 << 24)2929+#define CMD_OUTPUT (1 << 24)3030+#define CMD_INPUT (2 << 24)3131+#define CMD_PINMODE (3 << 24)3232+#define CMD_IRQ_ENABLE (4 << 24)3333+#define CMD_IRQ_ACK (5 << 24)3434+#define CMD_IRQ_MODE (6 << 24)3535+#define CMD_CONFIG (0xff << 24)3636+3737+#define MODE_INPUT 03838+#define MODE_OUTPUT 13939+#define MODE_VALUE_0 04040+#define MODE_VALUE_1 24141+4242+#define IRQ_MODE_HIGH 04343+#define IRQ_MODE_LOW 14444+#define IRQ_MODE_RISING 24545+#define IRQ_MODE_FALLING 34646+#define IRQ_MODE_BOTH 44747+4848+#define CONFIG_MASK GENMASK(23, 16)4949+#define CONFIG_VAL GENMASK(7, 0)5050+5151+#define CONFIG_OUTMODE GENMASK(7, 6)5252+#define CONFIG_IRQMODE GENMASK(5, 3)5353+#define CONFIG_PULLDOWN BIT(2)5454+#define CONFIG_PULLUP BIT(1)5555+#define CONFIG_OUTVAL BIT(0)5656+5757+/*5858+ * Output modes seem to differ depending on the PMU in use... ?5959+ * j274 / M1 (Sera PMU):6060+ * 0 = input6161+ * 1 = output6262+ * 2 = open drain6363+ * 3 = disable6464+ * j314 / M1Pro (Maverick PMU):6565+ * 0 = input6666+ * 1 = open drain6767+ * 2 = output6868+ * 3 = ?6969+ */7070+7171+struct macsmc_gpio {7272+ struct device *dev;7373+ struct apple_smc *smc;7474+ struct gpio_chip gc;7575+7676+ int first_index;7777+};7878+7979+static int macsmc_gpio_nr(smc_key key)8080+{8181+ int low = hex_to_bin(key & 0xff);8282+ int high = hex_to_bin((key >> 8) & 0xff);8383+8484+ if (low < 0 || high < 0)8585+ return -1;8686+8787+ return low | (high << 4);8888+}8989+9090+static int macsmc_gpio_key(unsigned int offset)9191+{9292+ return _SMC_KEY("gP\0\0") | hex_asc_hi(offset) << 8 | hex_asc_lo(offset);9393+}9494+9595+static int macsmc_gpio_find_first_gpio_index(struct macsmc_gpio *smcgp)9696+{9797+ struct apple_smc *smc = smcgp->smc;9898+ smc_key key = macsmc_gpio_key(0);9999+ smc_key first_key, last_key;100100+ int start, count, ret;101101+102102+ /* Return early if the key is out of bounds */103103+ ret = apple_smc_get_key_by_index(smc, 0, &first_key);104104+ if (ret)105105+ return ret;106106+ if (key <= first_key)107107+ return -ENODEV;108108+109109+ ret = apple_smc_get_key_by_index(smc, smc->key_count - 1, &last_key);110110+ if (ret)111111+ return ret;112112+ if (key > last_key)113113+ return -ENODEV;114114+115115+ /* Binary search to find index of first SMC key bigger or equal to key */116116+ start = 0;117117+ count = smc->key_count;118118+ while (count > 1) {119119+ smc_key pkey;120120+ int pivot = start + ((count - 1) >> 1);121121+122122+ ret = apple_smc_get_key_by_index(smc, pivot, &pkey);123123+ if (ret < 0)124124+ return ret;125125+126126+ if (pkey == key)127127+ return pivot;128128+129129+ pivot++;130130+131131+ if (pkey < key) {132132+ count -= pivot - start;133133+ start = pivot;134134+ } else {135135+ count = pivot - start;136136+ }137137+ }138138+139139+ return start;140140+}141141+142142+static int macsmc_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)143143+{144144+ struct macsmc_gpio *smcgp = gpiochip_get_data(gc);145145+ smc_key key = macsmc_gpio_key(offset);146146+ u32 val;147147+ int ret;148148+149149+ /* First try reading the explicit pin mode register */150150+ ret = apple_smc_rw_u32(smcgp->smc, key, CMD_PINMODE, &val);151151+ if (!ret)152152+ return (val & MODE_OUTPUT) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;153153+154154+ /*155155+ * Less common IRQ configs cause CMD_PINMODE to fail, and so does open drain mode.156156+ * Fall back to reading IRQ mode, which will only succeed for inputs.157157+ */158158+ ret = apple_smc_rw_u32(smcgp->smc, key, CMD_IRQ_MODE, &val);159159+ return ret ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN;160160+}161161+162162+static int macsmc_gpio_get(struct gpio_chip *gc, unsigned int offset)163163+{164164+ struct macsmc_gpio *smcgp = gpiochip_get_data(gc);165165+ smc_key key = macsmc_gpio_key(offset);166166+ u32 cmd, val;167167+ int ret;168168+169169+ ret = macsmc_gpio_get_direction(gc, offset);170170+ if (ret < 0)171171+ return ret;172172+173173+ if (ret == GPIO_LINE_DIRECTION_OUT)174174+ cmd = CMD_OUTPUT;175175+ else176176+ cmd = CMD_INPUT;177177+178178+ ret = apple_smc_rw_u32(smcgp->smc, key, cmd, &val);179179+ if (ret < 0)180180+ return ret;181181+182182+ return val ? 1 : 0;183183+}184184+185185+static int macsmc_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)186186+{187187+ struct macsmc_gpio *smcgp = gpiochip_get_data(gc);188188+ smc_key key = macsmc_gpio_key(offset);189189+ int ret;190190+191191+ value |= CMD_OUTPUT;192192+ ret = apple_smc_write_u32(smcgp->smc, key, CMD_OUTPUT | value);193193+ if (ret < 0)194194+ dev_err(smcgp->dev, "GPIO set failed %p4ch = 0x%x\n",195195+ &key, value);196196+197197+ return ret;198198+}199199+200200+static int macsmc_gpio_init_valid_mask(struct gpio_chip *gc,201201+ unsigned long *valid_mask, unsigned int ngpios)202202+{203203+ struct macsmc_gpio *smcgp = gpiochip_get_data(gc);204204+ int count;205205+ int i;206206+207207+ count = min(smcgp->smc->key_count, MAX_GPIO);208208+209209+ bitmap_zero(valid_mask, ngpios);210210+211211+ for (i = 0; i < count; i++) {212212+ int ret, gpio_nr;213213+ smc_key key;214214+215215+ ret = apple_smc_get_key_by_index(smcgp->smc, smcgp->first_index + i, &key);216216+ if (ret < 0)217217+ return ret;218218+219219+ if (key > SMC_KEY(gPff))220220+ break;221221+222222+ gpio_nr = macsmc_gpio_nr(key);223223+ if (gpio_nr < 0 || gpio_nr > MAX_GPIO) {224224+ dev_err(smcgp->dev, "Bad GPIO key %p4ch\n", &key);225225+ continue;226226+ }227227+228228+ set_bit(gpio_nr, valid_mask);229229+ }230230+231231+ return 0;232232+}233233+234234+static int macsmc_gpio_probe(struct platform_device *pdev)235235+{236236+ struct macsmc_gpio *smcgp;237237+ struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);238238+ smc_key key;239239+ int ret;240240+241241+ smcgp = devm_kzalloc(&pdev->dev, sizeof(*smcgp), GFP_KERNEL);242242+ if (!smcgp)243243+ return -ENOMEM;244244+245245+ smcgp->dev = &pdev->dev;246246+ smcgp->smc = smc;247247+248248+ smcgp->first_index = macsmc_gpio_find_first_gpio_index(smcgp);249249+ if (smcgp->first_index < 0)250250+ return smcgp->first_index;251251+252252+ ret = apple_smc_get_key_by_index(smc, smcgp->first_index, &key);253253+ if (ret < 0)254254+ return ret;255255+256256+ if (key > macsmc_gpio_key(MAX_GPIO - 1))257257+ return -ENODEV;258258+259259+ dev_info(smcgp->dev, "First GPIO key: %p4ch\n", &key);260260+261261+ smcgp->gc.label = "macsmc-pmu-gpio";262262+ smcgp->gc.owner = THIS_MODULE;263263+ smcgp->gc.get = macsmc_gpio_get;264264+ smcgp->gc.set_rv = macsmc_gpio_set;265265+ smcgp->gc.get_direction = macsmc_gpio_get_direction;266266+ smcgp->gc.init_valid_mask = macsmc_gpio_init_valid_mask;267267+ smcgp->gc.can_sleep = true;268268+ smcgp->gc.ngpio = MAX_GPIO;269269+ smcgp->gc.base = -1;270270+ smcgp->gc.parent = &pdev->dev;271271+272272+ return devm_gpiochip_add_data(&pdev->dev, &smcgp->gc, smcgp);273273+}274274+275275+static const struct of_device_id macsmc_gpio_of_table[] = {276276+ { .compatible = "apple,smc-gpio", },277277+ {}278278+};279279+MODULE_DEVICE_TABLE(of, macsmc_gpio_of_table);280280+281281+static struct platform_driver macsmc_gpio_driver = {282282+ .driver = {283283+ .name = "macsmc-gpio",284284+ .of_match_table = macsmc_gpio_of_table,285285+ },286286+ .probe = macsmc_gpio_probe,287287+};288288+module_platform_driver(macsmc_gpio_driver);289289+290290+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");291291+MODULE_LICENSE("Dual MIT/GPL");292292+MODULE_DESCRIPTION("Apple SMC GPIO driver");
+18
drivers/mfd/Kconfig
···285285 Select this to support the Cirrus Logic CS42L43 PC CODEC with286286 headphone and class D speaker drivers over SoundWire.287287288288+config MFD_MACSMC289289+ tristate "Apple Silicon System Management Controller (SMC)"290290+ depends on ARCH_APPLE || COMPILE_TEST291291+ depends on OF292292+ depends on APPLE_RTKIT293293+ select MFD_CORE294294+ help295295+ The System Management Controller (SMC) on Apple Silicon machines is a296296+ piece of hardware that exposes various functionalities such as297297+ temperature sensors, voltage/power meters, shutdown/reboot handling,298298+ GPIOs and more.299299+300300+ Communication happens via a shared mailbox using the RTKit protocol301301+ which is also used for other co-processors. The SMC protocol then302302+ allows reading and writing many different keys which implement the303303+ various features. The MFD core device handles this protocol and304304+ exposes it to the sub-devices.305305+288306config MFD_MADERA289307 tristate "Cirrus Logic Madera codecs"290308 select MFD_CORE
···128128129129 Say Y here if you have a Buffalo LinkStation LS421D/E.130130131131+config POWER_RESET_MACSMC132132+ tristate "Apple SMC reset/power-off driver"133133+ depends on MFD_MACSMC134134+ help135135+ This driver supports reset and power-off on Apple Mac machines136136+ that implement this functionality via the SMC.137137+138138+ Say Y here if you have an Apple Silicon Mac.139139+131140config POWER_RESET_MSM132141 bool "Qualcomm MSM power-off driver"133142 depends on ARCH_QCOM
···11+// SPDX-License-Identifier: GPL-2.0-only OR MIT22+/*33+ * Apple SMC Reboot/Poweroff Handler44+ * Copyright The Asahi Linux Contributors55+ */66+77+#include <linux/delay.h>88+#include <linux/mfd/core.h>99+#include <linux/mfd/macsmc.h>1010+#include <linux/mod_devicetable.h>1111+#include <linux/module.h>1212+#include <linux/nvmem-consumer.h>1313+#include <linux/platform_device.h>1414+#include <linux/reboot.h>1515+#include <linux/slab.h>1616+1717+struct macsmc_reboot_nvmem {1818+ struct nvmem_cell *shutdown_flag;1919+ struct nvmem_cell *boot_stage;2020+ struct nvmem_cell *boot_error_count;2121+ struct nvmem_cell *panic_count;2222+};2323+2424+static const char * const nvmem_names[] = {2525+ "shutdown_flag",2626+ "boot_stage",2727+ "boot_error_count",2828+ "panic_count",2929+};3030+3131+enum boot_stage {3232+ BOOT_STAGE_SHUTDOWN = 0x00, /* Clean shutdown */3333+ BOOT_STAGE_IBOOT_DONE = 0x2f, /* Last stage of bootloader */3434+ BOOT_STAGE_KERNEL_STARTED = 0x30, /* Normal OS booting */3535+};3636+3737+struct macsmc_reboot {3838+ struct device *dev;3939+ struct apple_smc *smc;4040+ struct notifier_block reboot_notify;4141+4242+ union {4343+ struct macsmc_reboot_nvmem nvm;4444+ struct nvmem_cell *nvm_cells[ARRAY_SIZE(nvmem_names)];4545+ };4646+};4747+4848+/* Helpers to read/write a u8 given a struct nvmem_cell */4949+static int nvmem_cell_get_u8(struct nvmem_cell *cell)5050+{5151+ size_t len;5252+ void *bfr;5353+ u8 val;5454+5555+ bfr = nvmem_cell_read(cell, &len);5656+ if (IS_ERR(bfr))5757+ return PTR_ERR(bfr);5858+5959+ if (len < 1) {6060+ kfree(bfr);6161+ return -EINVAL;6262+ }6363+6464+ val = *(u8 *)bfr;6565+ kfree(bfr);6666+ return val;6767+}6868+6969+static int nvmem_cell_set_u8(struct nvmem_cell *cell, u8 val)7070+{7171+ return nvmem_cell_write(cell, &val, sizeof(val));7272+}7373+7474+/*7575+ * SMC 'MBSE' key actions:7676+ *7777+ * 'offw' - shutdown warning7878+ * 'slpw' - sleep warning7979+ * 'rest' - restart warning8080+ * 'off1' - shutdown (needs PMU bit set to stay on)8181+ * 'susp' - suspend8282+ * 'phra' - restart ("PE Halt Restart Action"?)8383+ * 'panb' - panic beginning8484+ * 'pane' - panic end8585+ */8686+8787+static int macsmc_prepare_atomic(struct sys_off_data *data)8888+{8989+ struct macsmc_reboot *reboot = data->cb_data;9090+9191+ dev_info(reboot->dev, "Preparing SMC for atomic mode\n");9292+9393+ apple_smc_enter_atomic(reboot->smc);9494+ return NOTIFY_OK;9595+}9696+9797+static int macsmc_power_off(struct sys_off_data *data)9898+{9999+ struct macsmc_reboot *reboot = data->cb_data;100100+101101+ dev_info(reboot->dev, "Issuing power off (off1)\n");102102+103103+ if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(off1)) < 0) {104104+ dev_err(reboot->dev, "Failed to issue MBSE = off1 (power_off)\n");105105+ } else {106106+ mdelay(100);107107+ WARN_ONCE(1, "Unable to power off system\n");108108+ }109109+110110+ return NOTIFY_OK;111111+}112112+113113+static int macsmc_restart(struct sys_off_data *data)114114+{115115+ struct macsmc_reboot *reboot = data->cb_data;116116+117117+ dev_info(reboot->dev, "Issuing restart (phra)\n");118118+119119+ if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(phra)) < 0) {120120+ dev_err(reboot->dev, "Failed to issue MBSE = phra (restart)\n");121121+ } else {122122+ mdelay(100);123123+ WARN_ONCE(1, "Unable to restart system\n");124124+ }125125+126126+ return NOTIFY_OK;127127+}128128+129129+static int macsmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data)130130+{131131+ struct macsmc_reboot *reboot = container_of(this, struct macsmc_reboot, reboot_notify);132132+ u8 shutdown_flag;133133+ u32 val;134134+135135+ switch (action) {136136+ case SYS_RESTART:137137+ val = SMC_KEY(rest);138138+ shutdown_flag = 0;139139+ break;140140+ case SYS_POWER_OFF:141141+ val = SMC_KEY(offw);142142+ shutdown_flag = 1;143143+ break;144144+ default:145145+ return NOTIFY_DONE;146146+ }147147+148148+ dev_info(reboot->dev, "Preparing for reboot (%p4ch)\n", &val);149149+150150+ /* On the Mac Mini, this will turn off the LED for power off */151151+ if (apple_smc_write_u32(reboot->smc, SMC_KEY(MBSE), val) < 0)152152+ dev_err(reboot->dev, "Failed to issue MBSE = %p4ch (reboot_prepare)\n", &val);153153+154154+ /* Set the boot_stage to 0, which means we're doing a clean shutdown/reboot. */155155+ if (reboot->nvm.boot_stage &&156156+ nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_SHUTDOWN) < 0)157157+ dev_err(reboot->dev, "Failed to write boot_stage\n");158158+159159+ /*160160+ * Set the PMU flag to actually reboot into the off state.161161+ * Without this, the device will just reboot. We make it optional in case it is no longer162162+ * necessary on newer hardware.163163+ */164164+ if (reboot->nvm.shutdown_flag &&165165+ nvmem_cell_set_u8(reboot->nvm.shutdown_flag, shutdown_flag) < 0)166166+ dev_err(reboot->dev, "Failed to write shutdown_flag\n");167167+168168+ return NOTIFY_OK;169169+}170170+171171+static void macsmc_power_init_error_counts(struct macsmc_reboot *reboot)172172+{173173+ int boot_error_count, panic_count;174174+175175+ if (!reboot->nvm.boot_error_count || !reboot->nvm.panic_count)176176+ return;177177+178178+ boot_error_count = nvmem_cell_get_u8(reboot->nvm.boot_error_count);179179+ if (boot_error_count < 0) {180180+ dev_err(reboot->dev, "Failed to read boot_error_count (%d)\n", boot_error_count);181181+ return;182182+ }183183+184184+ panic_count = nvmem_cell_get_u8(reboot->nvm.panic_count);185185+ if (panic_count < 0) {186186+ dev_err(reboot->dev, "Failed to read panic_count (%d)\n", panic_count);187187+ return;188188+ }189189+190190+ if (!boot_error_count && !panic_count)191191+ return;192192+193193+ dev_warn(reboot->dev, "PMU logged %d boot error(s) and %d panic(s)\n",194194+ boot_error_count, panic_count);195195+196196+ if (nvmem_cell_set_u8(reboot->nvm.panic_count, 0) < 0)197197+ dev_err(reboot->dev, "Failed to reset panic_count\n");198198+ if (nvmem_cell_set_u8(reboot->nvm.boot_error_count, 0) < 0)199199+ dev_err(reboot->dev, "Failed to reset boot_error_count\n");200200+}201201+202202+static int macsmc_reboot_probe(struct platform_device *pdev)203203+{204204+ struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);205205+ struct macsmc_reboot *reboot;206206+ int ret, i;207207+208208+ reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL);209209+ if (!reboot)210210+ return -ENOMEM;211211+212212+ reboot->dev = &pdev->dev;213213+ reboot->smc = smc;214214+215215+ platform_set_drvdata(pdev, reboot);216216+217217+ for (i = 0; i < ARRAY_SIZE(nvmem_names); i++) {218218+ struct nvmem_cell *cell;219219+220220+ cell = devm_nvmem_cell_get(&pdev->dev,221221+ nvmem_names[i]);222222+ if (IS_ERR(cell)) {223223+ if (PTR_ERR(cell) == -EPROBE_DEFER)224224+ return -EPROBE_DEFER;225225+ dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n",226226+ nvmem_names[i], PTR_ERR(cell));227227+ /* Non fatal, we'll deal with it */228228+ cell = NULL;229229+ }230230+ reboot->nvm_cells[i] = cell;231231+ }232232+233233+ /* Set the boot_stage to indicate we're running the OS kernel */234234+ if (reboot->nvm.boot_stage &&235235+ nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_KERNEL_STARTED) < 0)236236+ dev_err(reboot->dev, "Failed to write boot_stage\n");237237+238238+ /* Display and clear the error counts */239239+ macsmc_power_init_error_counts(reboot);240240+241241+ reboot->reboot_notify.notifier_call = macsmc_reboot_notify;242242+243243+ ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF_PREPARE,244244+ SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot);245245+ if (ret)246246+ return dev_err_probe(&pdev->dev, ret,247247+ "Failed to register power-off prepare handler\n");248248+ ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH,249249+ macsmc_power_off, reboot);250250+ if (ret)251251+ return dev_err_probe(&pdev->dev, ret,252252+ "Failed to register power-off handler\n");253253+254254+ ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART_PREPARE,255255+ SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot);256256+ if (ret)257257+ return dev_err_probe(&pdev->dev, ret,258258+ "Failed to register restart prepare handler\n");259259+ ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH,260260+ macsmc_restart, reboot);261261+ if (ret)262262+ return dev_err_probe(&pdev->dev, ret, "Failed to register restart handler\n");263263+264264+ ret = devm_register_reboot_notifier(&pdev->dev, &reboot->reboot_notify);265265+ if (ret)266266+ return dev_err_probe(&pdev->dev, ret, "Failed to register reboot notifier\n");267267+268268+ dev_info(&pdev->dev, "Handling reboot and poweroff requests via SMC\n");269269+270270+ return 0;271271+}272272+273273+static const struct of_device_id macsmc_reboot_of_table[] = {274274+ { .compatible = "apple,smc-reboot", },275275+ {}276276+};277277+MODULE_DEVICE_TABLE(of, macsmc_reboot_of_table);278278+279279+static struct platform_driver macsmc_reboot_driver = {280280+ .driver = {281281+ .name = "macsmc-reboot",282282+ .of_match_table = macsmc_reboot_of_table,283283+ },284284+ .probe = macsmc_reboot_probe,285285+};286286+module_platform_driver(macsmc_reboot_driver);287287+288288+MODULE_LICENSE("Dual MIT/GPL");289289+MODULE_DESCRIPTION("Apple SMC reboot/poweroff driver");290290+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+1-2
drivers/soc/apple/rtkit.c
···279279 dev_dbg(rtk->dev, "RTKit: buffer request for 0x%zx bytes at %pad\n",280280 buffer->size, &buffer->iova);281281282282- if (buffer->iova &&283283- (!rtk->ops->shmem_setup || !rtk->ops->shmem_destroy)) {282282+ if (buffer->iova && !rtk->ops->shmem_setup) {284283 err = -EINVAL;285284 goto error;286285 }
+279
include/linux/mfd/macsmc.h
···11+/* SPDX-License-Identifier: GPL-2.0-only OR MIT */22+/*33+ * Apple SMC (System Management Controller) core definitions44+ *55+ * Copyright (C) The Asahi Linux Contributors66+ */77+88+#ifndef _LINUX_MFD_MACSMC_H99+#define _LINUX_MFD_MACSMC_H1010+1111+#include <linux/soc/apple/rtkit.h>1212+1313+/**1414+ * typedef smc_key - Alias for u32 to be used for SMC keys1515+ *1616+ * SMC keys are 32bit integers containing packed ASCII characters in natural1717+ * integer order, i.e. 0xAABBCCDD, which represent the FourCC ABCD.1818+ * The SMC driver is designed with this assumption and ensures the right1919+ * endianness is used when these are stored to memory and sent to or received2020+ * from the actual SMC firmware (which can be done in either shared memory or2121+ * as 64bit mailbox message on Apple Silicon).2222+ * Internally, SMC stores these keys in a table sorted lexicographically and2323+ * allows resolving an index into this table to the corresponding SMC key.2424+ * Thus, storing keys as u32 is very convenient as it allows to e.g. use2525+ * normal comparison operators which directly map to the natural order used2626+ * by SMC firmware.2727+ *2828+ * This simple type alias is introduced to allow easy recognition of SMC key2929+ * variables and arguments.3030+ */3131+typedef u32 smc_key;3232+3333+/**3434+ * SMC_KEY - Convert FourCC SMC keys in source code to smc_key3535+ *3636+ * This macro can be used to easily define FourCC SMC keys in source code3737+ * and convert these to u32 / smc_key, e.g. SMC_KEY(NTAP) will expand to3838+ * 0x4e544150.3939+ *4040+ * @s: FourCC SMC key to be converted4141+ */4242+#define SMC_KEY(s) (smc_key)(_SMC_KEY(#s))4343+#define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3])4444+4545+#define APPLE_SMC_READABLE BIT(7)4646+#define APPLE_SMC_WRITABLE BIT(6)4747+#define APPLE_SMC_FUNCTION BIT(4)4848+4949+/**5050+ * struct apple_smc_key_info - Information for a SMC key as returned by SMC5151+ * @type_code: FourCC code indicating the type for this key.5252+ * Known types:5353+ * ch8*: ASCII string5454+ * flag: Boolean, 1 or 05555+ * flt: 32-bit single-precision IEEE 754 float5656+ * hex: Binary data5757+ * ioft: 64bit Unsigned fixed-point intger (48.16)5858+ * {si,ui}{8,16,32,64}: Signed/Unsigned 8-/16-/32-/64-bit integer5959+ * @size: Size of the buffer associated with this key6060+ * @flags: Bitfield encoding flags (APPLE_SMC_{READABLE,WRITABLE,FUNCTION})6161+ */6262+struct apple_smc_key_info {6363+ u32 type_code;6464+ u8 size;6565+ u8 flags;6666+};6767+6868+/**6969+ * enum apple_smc_boot_stage - SMC boot stage7070+ * @APPLE_SMC_BOOTING: SMC is booting7171+ * @APPLE_SMC_INITIALIZED: SMC is initialized and ready to use7272+ * @APPLE_SMC_ERROR_NO_SHMEM: Shared memory could not be initialized during boot7373+ * @APPLE_SMC_ERROR_CRASHED: SMC has crashed7474+ */7575+enum apple_smc_boot_stage {7676+ APPLE_SMC_BOOTING,7777+ APPLE_SMC_INITIALIZED,7878+ APPLE_SMC_ERROR_NO_SHMEM,7979+ APPLE_SMC_ERROR_CRASHED8080+};8181+8282+/**8383+ * struct apple_smc8484+ * @dev: Underlying device struct for the physical backend device8585+ * @key_count: Number of available SMC keys8686+ * @first_key: First valid SMC key8787+ * @last_key: Last valid SMC key8888+ * @event_handlers: Notifier call chain for events received from SMC8989+ * @rtk: Pointer to Apple RTKit instance9090+ * @init_done: Completion for initialization9191+ * @boot_stage: Current boot stage of SMC9292+ * @sram: Pointer to SRAM resource9393+ * @sram_base: SRAM base address9494+ * @shmem: RTKit shared memory structure for SRAM9595+ * @msg_id: Current message id for commands, will be incremented for each command9696+ * @atomic_mode: Flag set when atomic mode is entered9797+ * @atomic_pending: Flag indicating pending atomic command9898+ * @cmd_done: Completion for command execution in non-atomic mode9999+ * @cmd_ret: Return value from SMC for last command100100+ * @mutex: Mutex for non-atomic mode101101+ * @lock: Spinlock for atomic mode102102+ */103103+struct apple_smc {104104+ struct device *dev;105105+106106+ u32 key_count;107107+ smc_key first_key;108108+ smc_key last_key;109109+110110+ struct blocking_notifier_head event_handlers;111111+112112+ struct apple_rtkit *rtk;113113+114114+ struct completion init_done;115115+ enum apple_smc_boot_stage boot_stage;116116+117117+ struct resource *sram;118118+ void __iomem *sram_base;119119+ struct apple_rtkit_shmem shmem;120120+121121+ unsigned int msg_id;122122+123123+ bool atomic_mode;124124+ bool atomic_pending;125125+ struct completion cmd_done;126126+ u64 cmd_ret;127127+128128+ struct mutex mutex;129129+ spinlock_t lock;130130+};131131+132132+/**133133+ * apple_smc_read - Read size bytes from given SMC key into buf134134+ * @smc: Pointer to apple_smc struct135135+ * @key: smc_key to be read136136+ * @buf: Buffer into which size bytes of data will be read from SMC137137+ * @size: Number of bytes to be read into buf138138+ *139139+ * Return: Zero on success, negative errno on error140140+ */141141+int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size);142142+143143+/**144144+ * apple_smc_write - Write size bytes into given SMC key from buf145145+ * @smc: Pointer to apple_smc struct146146+ * @key: smc_key data will be written to147147+ * @buf: Buffer from which size bytes of data will be written to SMC148148+ * @size: Number of bytes to be written149149+ *150150+ * Return: Zero on success, negative errno on error151151+ */152152+int apple_smc_write(struct apple_smc *smc, smc_key key, void *buf, size_t size);153153+154154+/**155155+ * apple_smc_enter_atomic - Enter atomic mode to be able to use apple_smc_write_atomic156156+ * @smc: Pointer to apple_smc struct157157+ *158158+ * This function switches the SMC backend to atomic mode which allows the159159+ * use of apple_smc_write_atomic while disabling *all* other functions.160160+ * This is only used for shutdown/reboot which requires writing to a SMC161161+ * key from atomic context.162162+ *163163+ * Return: Zero on success, negative errno on error164164+ */165165+int apple_smc_enter_atomic(struct apple_smc *smc);166166+167167+/**168168+ * apple_smc_write_atomic - Write size bytes into given SMC key from buf without sleeping169169+ * @smc: Pointer to apple_smc struct170170+ * @key: smc_key data will be written to171171+ * @buf: Buffer from which size bytes of data will be written to SMC172172+ * @size: Number of bytes to be written173173+ *174174+ * Note that this function will fail if apple_smc_enter_atomic hasn't been175175+ * called before.176176+ *177177+ * Return: Zero on success, negative errno on error178178+ */179179+int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, void *buf, size_t size);180180+181181+/**182182+ * apple_smc_rw - Write and then read using the given SMC key183183+ * @smc: Pointer to apple_smc struct184184+ * @key: smc_key data will be written to185185+ * @wbuf: Buffer from which size bytes of data will be written to SMC186186+ * @wsize: Number of bytes to be written187187+ * @rbuf: Buffer to which size bytes of data will be read from SMC188188+ * @rsize: Number of bytes to be read189189+ *190190+ * Return: Zero on success, negative errno on error191191+ */192192+int apple_smc_rw(struct apple_smc *smc, smc_key key, void *wbuf, size_t wsize,193193+ void *rbuf, size_t rsize);194194+195195+/**196196+ * apple_smc_get_key_by_index - Given an index return the corresponding SMC key197197+ * @smc: Pointer to apple_smc struct198198+ * @index: Index to be resolved199199+ * @key: Buffer for SMC key to be returned200200+ *201201+ * Return: Zero on success, negative errno on error202202+ */203203+int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key);204204+205205+/**206206+ * apple_smc_get_key_info - Get key information from SMC207207+ * @smc: Pointer to apple_smc struct208208+ * @key: Key to acquire information for209209+ * @info: Pointer to struct apple_smc_key_info which will be filled210210+ *211211+ * Return: Zero on success, negative errno on error212212+ */213213+int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info);214214+215215+/**216216+ * apple_smc_key_exists - Check if the given SMC key exists217217+ * @smc: Pointer to apple_smc struct218218+ * @key: smc_key to be checked219219+ *220220+ * Return: True if the key exists, false otherwise221221+ */222222+static inline bool apple_smc_key_exists(struct apple_smc *smc, smc_key key)223223+{224224+ return apple_smc_get_key_info(smc, key, NULL) >= 0;225225+}226226+227227+#define APPLE_SMC_TYPE_OPS(type) \228228+ static inline int apple_smc_read_##type(struct apple_smc *smc, smc_key key, type *p) \229229+ { \230230+ int ret = apple_smc_read(smc, key, p, sizeof(*p)); \231231+ return (ret < 0) ? ret : ((ret != sizeof(*p)) ? -EINVAL : 0); \232232+ } \233233+ static inline int apple_smc_write_##type(struct apple_smc *smc, smc_key key, type p) \234234+ { \235235+ return apple_smc_write(smc, key, &p, sizeof(p)); \236236+ } \237237+ static inline int apple_smc_write_##type##_atomic(struct apple_smc *smc, smc_key key, type p) \238238+ { \239239+ return apple_smc_write_atomic(smc, key, &p, sizeof(p)); \240240+ } \241241+ static inline int apple_smc_rw_##type(struct apple_smc *smc, smc_key key, \242242+ type w, type *r) \243243+ { \244244+ int ret = apple_smc_rw(smc, key, &w, sizeof(w), r, sizeof(*r)); \245245+ return (ret < 0) ? ret : ((ret != sizeof(*r)) ? -EINVAL : 0); \246246+ }247247+248248+APPLE_SMC_TYPE_OPS(u64)249249+APPLE_SMC_TYPE_OPS(u32)250250+APPLE_SMC_TYPE_OPS(u16)251251+APPLE_SMC_TYPE_OPS(u8)252252+APPLE_SMC_TYPE_OPS(s64)253253+APPLE_SMC_TYPE_OPS(s32)254254+APPLE_SMC_TYPE_OPS(s16)255255+APPLE_SMC_TYPE_OPS(s8)256256+257257+static inline int apple_smc_read_flag(struct apple_smc *smc, smc_key key, bool *flag)258258+{259259+ u8 val;260260+ int ret = apple_smc_read_u8(smc, key, &val);261261+262262+ if (ret < 0)263263+ return ret;264264+265265+ *flag = val ? true : false;266266+ return ret;267267+}268268+269269+static inline int apple_smc_write_flag(struct apple_smc *smc, smc_key key, bool state)270270+{271271+ return apple_smc_write_u8(smc, key, state ? 1 : 0);272272+}273273+274274+static inline int apple_smc_write_flag_atomic(struct apple_smc *smc, smc_key key, bool state)275275+{276276+ return apple_smc_write_u8_atomic(smc, key, state ? 1 : 0);277277+}278278+279279+#endif
+1
include/linux/mfd/tps6594.h
···1919 TPS6593,2020 LP8764,2121 TPS65224,2222+ TPS652G1,2223};23242425/* Macro to get page index from register address */