112 lines
		
	
	
	
		
			2.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			112 lines
		
	
	
	
		
			2.8 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * Copyright (C) 2014 Linaro Ltd. | ||
|  |  * | ||
|  |  * Author: Linus Walleij <linus.walleij@linaro.org> | ||
|  |  * | ||
|  |  * This program is free software; you can redistribute it and/or modify | ||
|  |  * it under the terms of the GNU General Public License version 2, as | ||
|  |  * published by the Free Software Foundation. | ||
|  |  * | ||
|  |  */ | ||
|  | #include <linux/init.h>
 | ||
|  | #include <linux/mfd/syscon.h>
 | ||
|  | #include <linux/reboot.h>
 | ||
|  | #include <linux/regmap.h>
 | ||
|  | #include <linux/of.h>
 | ||
|  | #include <asm/system_misc.h>
 | ||
|  | 
 | ||
|  | #define REALVIEW_SYS_LOCK_OFFSET	0x20
 | ||
|  | #define REALVIEW_SYS_LOCK_VAL		0xA05F
 | ||
|  | #define REALVIEW_SYS_RESETCTL_OFFSET	0x40
 | ||
|  | 
 | ||
|  | /*
 | ||
|  |  * We detect the different syscon types from the compatible strings. | ||
|  |  */ | ||
|  | enum versatile_reboot { | ||
|  | 	REALVIEW_REBOOT_EB, | ||
|  | 	REALVIEW_REBOOT_PB1176, | ||
|  | 	REALVIEW_REBOOT_PB11MP, | ||
|  | 	REALVIEW_REBOOT_PBA8, | ||
|  | 	REALVIEW_REBOOT_PBX, | ||
|  | }; | ||
|  | 
 | ||
|  | /* Pointer to the system controller */ | ||
|  | static struct regmap *syscon_regmap; | ||
|  | static enum versatile_reboot versatile_reboot_type; | ||
|  | 
 | ||
|  | static const struct of_device_id versatile_reboot_of_match[] = { | ||
|  | 	{ | ||
|  | 		.compatible = "arm,realview-eb-syscon", | ||
|  | 		.data = (void *)REALVIEW_REBOOT_EB, | ||
|  | 	}, | ||
|  | 	{ | ||
|  | 		.compatible = "arm,realview-pb1176-syscon", | ||
|  | 		.data = (void *)REALVIEW_REBOOT_PB1176, | ||
|  | 	}, | ||
|  | 	{ | ||
|  | 		.compatible = "arm,realview-pb11mp-syscon", | ||
|  | 		.data = (void *)REALVIEW_REBOOT_PB11MP, | ||
|  | 	}, | ||
|  | 	{ | ||
|  | 		.compatible = "arm,realview-pba8-syscon", | ||
|  | 		.data = (void *)REALVIEW_REBOOT_PBA8, | ||
|  | 	}, | ||
|  | 	{ | ||
|  | 		.compatible = "arm,realview-pbx-syscon", | ||
|  | 		.data = (void *)REALVIEW_REBOOT_PBX, | ||
|  | 	}, | ||
|  | }; | ||
|  | 
 | ||
|  | static void versatile_reboot(enum reboot_mode mode, const char *cmd) | ||
|  | { | ||
|  | 	/* Unlock the reset register */ | ||
|  | 	regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, | ||
|  | 		     REALVIEW_SYS_LOCK_VAL); | ||
|  | 	/* Then hit reset on the different machines */ | ||
|  | 	switch (versatile_reboot_type) { | ||
|  | 	case REALVIEW_REBOOT_EB: | ||
|  | 		regmap_write(syscon_regmap, | ||
|  | 			     REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); | ||
|  | 		break; | ||
|  | 	case REALVIEW_REBOOT_PB1176: | ||
|  | 		regmap_write(syscon_regmap, | ||
|  | 			     REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); | ||
|  | 		break; | ||
|  | 	case REALVIEW_REBOOT_PB11MP: | ||
|  | 	case REALVIEW_REBOOT_PBA8: | ||
|  | 		regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
|  | 			     0x0000); | ||
|  | 		regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
|  | 			     0x0004); | ||
|  | 		break; | ||
|  | 	case REALVIEW_REBOOT_PBX: | ||
|  | 		regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
|  | 			     0x00f0); | ||
|  | 		regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, | ||
|  | 			     0x00f4); | ||
|  | 		break; | ||
|  | 	} | ||
|  | 	dsb(); | ||
|  | } | ||
|  | 
 | ||
|  | static int __init versatile_reboot_probe(void) | ||
|  | { | ||
|  | 	const struct of_device_id *reboot_id; | ||
|  | 	struct device_node *np; | ||
|  | 
 | ||
|  | 	np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, | ||
|  | 						 &reboot_id); | ||
|  | 	if (!np) | ||
|  | 		return -ENODEV; | ||
|  | 	versatile_reboot_type = (enum versatile_reboot)reboot_id->data; | ||
|  | 
 | ||
|  | 	syscon_regmap = syscon_node_to_regmap(np); | ||
|  | 	if (IS_ERR(syscon_regmap)) | ||
|  | 		return PTR_ERR(syscon_regmap); | ||
|  | 
 | ||
|  | 	arm_pm_restart = versatile_reboot; | ||
|  | 	pr_info("versatile reboot driver registered\n"); | ||
|  | 	return 0; | ||
|  | } | ||
|  | device_initcall(versatile_reboot_probe); |