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 REALVIEW_SYS_LOCK_OFFSET 0x20 22 #define REALVIEW_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 REALVIEW_REBOOT_EB, 33 REALVIEW_REBOOT_PB1176, 34 REALVIEW_REBOOT_PB11MP, 35 REALVIEW_REBOOT_PBA8, 36 REALVIEW_REBOOT_PBX, 37 }; 38 39 /* Pointer to the system controller */ 40 static struct regmap *syscon_regmap; 41 static enum versatile_reboot versatile_reboot_type; 42 43 static const struct of_device_id versatile_reboot_of_match[] = { 44 { 45 .compatible = "arm,core-module-integrator", 46 .data = (void *)INTEGRATOR_REBOOT_CM 47 }, 48 { 49 .compatible = "arm,realview-eb-syscon", 50 .data = (void *)REALVIEW_REBOOT_EB, 51 }, 52 { 53 .compatible = "arm,realview-pb1176-syscon", 54 .data = (void *)REALVIEW_REBOOT_PB1176, 55 }, 56 { 57 .compatible = "arm,realview-pb11mp-syscon", 58 .data = (void *)REALVIEW_REBOOT_PB11MP, 59 }, 60 { 61 .compatible = "arm,realview-pba8-syscon", 62 .data = (void *)REALVIEW_REBOOT_PBA8, 63 }, 64 { 65 .compatible = "arm,realview-pbx-syscon", 66 .data = (void *)REALVIEW_REBOOT_PBX, 67 }, 68 {}, 69 }; 70 71 static int versatile_reboot(struct notifier_block *this, unsigned long mode, 72 void *cmd) 73 { 74 /* Unlock the reset register */ 75 /* Then hit reset on the different machines */ 76 switch (versatile_reboot_type) { 77 case INTEGRATOR_REBOOT_CM: 78 regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET, 79 VERSATILE_LOCK_VAL); 80 regmap_update_bits(syscon_regmap, 81 INTEGRATOR_HDR_CTRL_OFFSET, 82 INTEGRATOR_CM_CTRL_RESET, 83 INTEGRATOR_CM_CTRL_RESET); 84 break; 85 case REALVIEW_REBOOT_EB: 86 regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, 87 VERSATILE_LOCK_VAL); 88 regmap_write(syscon_regmap, 89 REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); 90 break; 91 case REALVIEW_REBOOT_PB1176: 92 regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, 93 VERSATILE_LOCK_VAL); 94 regmap_write(syscon_regmap, 95 REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); 96 break; 97 case REALVIEW_REBOOT_PB11MP: 98 case REALVIEW_REBOOT_PBA8: 99 regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, 100 VERSATILE_LOCK_VAL); 101 regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 102 0x0000); 103 regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 104 0x0004); 105 break; 106 case REALVIEW_REBOOT_PBX: 107 regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, 108 VERSATILE_LOCK_VAL); 109 regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 110 0x00f0); 111 regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 112 0x00f4); 113 break; 114 } 115 dsb(); 116 117 return NOTIFY_DONE; 118 } 119 120 static struct notifier_block versatile_reboot_nb = { 121 .notifier_call = versatile_reboot, 122 .priority = 192, 123 }; 124 125 static int __init versatile_reboot_probe(void) 126 { 127 const struct of_device_id *reboot_id; 128 struct device_node *np; 129 int err; 130 131 np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, 132 &reboot_id); 133 if (!np) 134 return -ENODEV; 135 versatile_reboot_type = (enum versatile_reboot)reboot_id->data; 136 137 syscon_regmap = syscon_node_to_regmap(np); 138 if (IS_ERR(syscon_regmap)) 139 return PTR_ERR(syscon_regmap); 140 141 err = register_restart_handler(&versatile_reboot_nb); 142 if (err) 143 return err; 144 145 pr_info("versatile reboot driver registered\n"); 146 return 0; 147 } 148 device_initcall(versatile_reboot_probe); 149