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