1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * T-HEAD TH1520 AON Firmware Reboot Driver 4 * 5 * Copyright (c) 2025 Icenowy Zheng <uwu@icenowy.me> 6 */ 7 8 #include <linux/auxiliary_bus.h> 9 #include <linux/firmware/thead/thead,th1520-aon.h> 10 #include <linux/module.h> 11 #include <linux/notifier.h> 12 #include <linux/of.h> 13 #include <linux/reboot.h> 14 #include <linux/slab.h> 15 16 #define TH1520_AON_REBOOT_PRIORITY 200 17 18 struct th1520_aon_msg_empty_body { 19 struct th1520_aon_rpc_msg_hdr hdr; 20 u16 reserved[12]; 21 } __packed __aligned(1); 22 23 static int th1520_aon_pwroff_handler(struct sys_off_data *data) 24 { 25 struct th1520_aon_chan *aon_chan = data->cb_data; 26 struct th1520_aon_msg_empty_body msg = {}; 27 28 msg.hdr.svc = TH1520_AON_RPC_SVC_WDG; 29 msg.hdr.func = TH1520_AON_WDG_FUNC_POWER_OFF; 30 msg.hdr.size = TH1520_AON_RPC_MSG_NUM; 31 32 th1520_aon_call_rpc(aon_chan, &msg); 33 34 return NOTIFY_DONE; 35 } 36 37 static int th1520_aon_restart_handler(struct sys_off_data *data) 38 { 39 struct th1520_aon_chan *aon_chan = data->cb_data; 40 struct th1520_aon_msg_empty_body msg = {}; 41 42 msg.hdr.svc = TH1520_AON_RPC_SVC_WDG; 43 msg.hdr.func = TH1520_AON_WDG_FUNC_RESTART; 44 msg.hdr.size = TH1520_AON_RPC_MSG_NUM; 45 46 th1520_aon_call_rpc(aon_chan, &msg); 47 48 return NOTIFY_DONE; 49 } 50 51 static int th1520_aon_reboot_probe(struct auxiliary_device *adev, 52 const struct auxiliary_device_id *id) 53 { 54 struct device *dev = &adev->dev; 55 int ret; 56 57 /* Expect struct th1520_aon_chan to be passed via platform_data */ 58 ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, 59 TH1520_AON_REBOOT_PRIORITY, 60 th1520_aon_pwroff_handler, 61 adev->dev.platform_data); 62 63 if (ret) { 64 dev_err(dev, "Failed to register power off handler\n"); 65 return ret; 66 } 67 68 ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 69 TH1520_AON_REBOOT_PRIORITY, 70 th1520_aon_restart_handler, 71 adev->dev.platform_data); 72 73 if (ret) { 74 dev_err(dev, "Failed to register restart handler\n"); 75 return ret; 76 } 77 78 return 0; 79 } 80 81 static const struct auxiliary_device_id th1520_aon_reboot_id_table[] = { 82 { .name = "th1520_pm_domains.reboot" }, 83 {}, 84 }; 85 MODULE_DEVICE_TABLE(auxiliary, th1520_aon_reboot_id_table); 86 87 static struct auxiliary_driver th1520_aon_reboot_driver = { 88 .driver = { 89 .name = "th1520-aon-reboot", 90 }, 91 .probe = th1520_aon_reboot_probe, 92 .id_table = th1520_aon_reboot_id_table, 93 }; 94 module_auxiliary_driver(th1520_aon_reboot_driver); 95 96 MODULE_AUTHOR("Icenowy Zheng <uwu@icenowy.me>"); 97 MODULE_DESCRIPTION("T-HEAD TH1520 AON-firmware-based reboot driver"); 98 MODULE_LICENSE("GPL"); 99