1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014 Linaro Ltd. 4 * 5 * Author: Linus Walleij <linus.walleij@linaro.org> 6 */ 7 #include <linux/init.h> 8 #include <linux/mfd/syscon.h> 9 #include <linux/reboot.h> 10 #include <linux/regmap.h> 11 #include <linux/of.h> 12 13 #define INTEGRATOR_HDR_CTRL_OFFSET 0x0C 14 #define INTEGRATOR_HDR_LOCK_OFFSET 0x14 15 #define INTEGRATOR_CM_CTRL_RESET (1 << 3) 16 17 #define VERSATILE_SYS_LOCK_OFFSET 0x20 18 #define VERSATILE_SYS_RESETCTL_OFFSET 0x40 19 20 /* Magic unlocking token used on all Versatile boards */ 21 #define VERSATILE_LOCK_VAL 0xA05F 22 23 /* 24 * We detect the different syscon types from the compatible strings. 25 */ 26 enum versatile_reboot { 27 INTEGRATOR_REBOOT_CM, 28 VERSATILE_REBOOT_CM, 29 REALVIEW_REBOOT_EB, 30 REALVIEW_REBOOT_PB1176, 31 REALVIEW_REBOOT_PB11MP, 32 REALVIEW_REBOOT_PBA8, 33 REALVIEW_REBOOT_PBX, 34 }; 35 36 /* Pointer to the system controller */ 37 static struct regmap *syscon_regmap; 38 static enum versatile_reboot versatile_reboot_type; 39 40 static const struct of_device_id versatile_reboot_of_match[] = { 41 { 42 .compatible = "arm,core-module-integrator", 43 .data = (void *)INTEGRATOR_REBOOT_CM 44 }, 45 { 46 .compatible = "arm,core-module-versatile", 47 .data = (void *)VERSATILE_REBOOT_CM, 48 }, 49 { 50 .compatible = "arm,realview-eb-syscon", 51 .data = (void *)REALVIEW_REBOOT_EB, 52 }, 53 { 54 .compatible = "arm,realview-pb1176-syscon", 55 .data = (void *)REALVIEW_REBOOT_PB1176, 56 }, 57 { 58 .compatible = "arm,realview-pb11mp-syscon", 59 .data = (void *)REALVIEW_REBOOT_PB11MP, 60 }, 61 { 62 .compatible = "arm,realview-pba8-syscon", 63 .data = (void *)REALVIEW_REBOOT_PBA8, 64 }, 65 { 66 .compatible = "arm,realview-pbx-syscon", 67 .data = (void *)REALVIEW_REBOOT_PBX, 68 }, 69 {}, 70 }; 71 72 static int versatile_reboot(struct notifier_block *this, unsigned long mode, 73 void *cmd) 74 { 75 /* Unlock the reset register */ 76 /* Then hit reset on the different machines */ 77 switch (versatile_reboot_type) { 78 case INTEGRATOR_REBOOT_CM: 79 regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET, 80 VERSATILE_LOCK_VAL); 81 regmap_update_bits(syscon_regmap, 82 INTEGRATOR_HDR_CTRL_OFFSET, 83 INTEGRATOR_CM_CTRL_RESET, 84 INTEGRATOR_CM_CTRL_RESET); 85 break; 86 case VERSATILE_REBOOT_CM: 87 regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, 88 VERSATILE_LOCK_VAL); 89 regmap_update_bits(syscon_regmap, 90 VERSATILE_SYS_RESETCTL_OFFSET, 91 0x0107, 92 0x0105); 93 regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, 94 0); 95 break; 96 case REALVIEW_REBOOT_EB: 97 regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, 98 VERSATILE_LOCK_VAL); 99 regmap_write(syscon_regmap, 100 VERSATILE_SYS_RESETCTL_OFFSET, 0x0008); 101 break; 102 case REALVIEW_REBOOT_PB1176: 103 regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, 104 VERSATILE_LOCK_VAL); 105 regmap_write(syscon_regmap, 106 VERSATILE_SYS_RESETCTL_OFFSET, 0x0100); 107 break; 108 case REALVIEW_REBOOT_PB11MP: 109 case REALVIEW_REBOOT_PBA8: 110 regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, 111 VERSATILE_LOCK_VAL); 112 regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 113 0x0000); 114 regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 115 0x0004); 116 break; 117 case REALVIEW_REBOOT_PBX: 118 regmap_write(syscon_regmap, VERSATILE_SYS_LOCK_OFFSET, 119 VERSATILE_LOCK_VAL); 120 regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 121 0x00f0); 122 regmap_write(syscon_regmap, VERSATILE_SYS_RESETCTL_OFFSET, 123 0x00f4); 124 break; 125 } 126 dsb(); 127 128 return NOTIFY_DONE; 129 } 130 131 static struct notifier_block versatile_reboot_nb = { 132 .notifier_call = versatile_reboot, 133 .priority = 192, 134 }; 135 136 static int __init versatile_reboot_probe(void) 137 { 138 const struct of_device_id *reboot_id; 139 struct device_node *np; 140 int err; 141 142 np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, 143 &reboot_id); 144 if (!np) 145 return -ENODEV; 146 versatile_reboot_type = (enum versatile_reboot)reboot_id->data; 147 148 syscon_regmap = syscon_node_to_regmap(np); 149 of_node_put(np); 150 if (IS_ERR(syscon_regmap)) 151 return PTR_ERR(syscon_regmap); 152 153 err = register_restart_handler(&versatile_reboot_nb); 154 if (err) 155 return err; 156 157 pr_info("versatile reboot driver registered\n"); 158 return 0; 159 } 160 device_initcall(versatile_reboot_probe); 161