167 lines
		
	
	
	
		
			4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			167 lines
		
	
	
	
		
			4 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * TI keystone reboot driver | ||
|  |  * | ||
|  |  * Copyright (C) 2014 Texas Instruments Incorporated. http://www.ti.com/
 | ||
|  |  * | ||
|  |  * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> | ||
|  |  * | ||
|  |  * 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/io.h>
 | ||
|  | #include <linux/module.h>
 | ||
|  | #include <linux/reboot.h>
 | ||
|  | #include <linux/regmap.h>
 | ||
|  | #include <asm/system_misc.h>
 | ||
|  | #include <linux/mfd/syscon.h>
 | ||
|  | #include <linux/of_platform.h>
 | ||
|  | 
 | ||
|  | #define RSTYPE_RG			0x0
 | ||
|  | #define RSCTRL_RG			0x4
 | ||
|  | #define RSCFG_RG			0x8
 | ||
|  | #define RSISO_RG			0xc
 | ||
|  | 
 | ||
|  | #define RSCTRL_KEY_MASK			0x0000ffff
 | ||
|  | #define RSCTRL_RESET_MASK		BIT(16)
 | ||
|  | #define RSCTRL_KEY			0x5a69
 | ||
|  | 
 | ||
|  | #define RSMUX_OMODE_MASK		0xe
 | ||
|  | #define RSMUX_OMODE_RESET_ON		0xa
 | ||
|  | #define RSMUX_OMODE_RESET_OFF		0x0
 | ||
|  | #define RSMUX_LOCK_MASK			0x1
 | ||
|  | #define RSMUX_LOCK_SET			0x1
 | ||
|  | 
 | ||
|  | #define RSCFG_RSTYPE_SOFT		0x300f
 | ||
|  | #define RSCFG_RSTYPE_HARD		0x0
 | ||
|  | 
 | ||
|  | #define WDT_MUX_NUMBER			0x4
 | ||
|  | 
 | ||
|  | static int rspll_offset; | ||
|  | static struct regmap *pllctrl_regs; | ||
|  | 
 | ||
|  | /**
 | ||
|  |  * rsctrl_enable_rspll_write - enable access to RSCTRL, RSCFG | ||
|  |  * To be able to access to RSCTRL, RSCFG registers | ||
|  |  * we have to write a key before | ||
|  |  */ | ||
|  | static inline int rsctrl_enable_rspll_write(void) | ||
|  | { | ||
|  | 	return regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG, | ||
|  | 				  RSCTRL_KEY_MASK, RSCTRL_KEY); | ||
|  | } | ||
|  | 
 | ||
|  | static void rsctrl_restart(enum reboot_mode mode, const char *cmd) | ||
|  | { | ||
|  | 	/* enable write access to RSTCTRL */ | ||
|  | 	rsctrl_enable_rspll_write(); | ||
|  | 
 | ||
|  | 	/* reset the SOC */ | ||
|  | 	regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG, | ||
|  | 			   RSCTRL_RESET_MASK, 0); | ||
|  | } | ||
|  | 
 | ||
|  | static struct of_device_id rsctrl_of_match[] = { | ||
|  | 	{.compatible = "ti,keystone-reset", }, | ||
|  | 	{}, | ||
|  | }; | ||
|  | 
 | ||
|  | static int rsctrl_probe(struct platform_device *pdev) | ||
|  | { | ||
|  | 	int i; | ||
|  | 	int ret; | ||
|  | 	u32 val; | ||
|  | 	unsigned int rg; | ||
|  | 	u32 rsmux_offset; | ||
|  | 	struct regmap *devctrl_regs; | ||
|  | 	struct device *dev = &pdev->dev; | ||
|  | 	struct device_node *np = dev->of_node; | ||
|  | 
 | ||
|  | 	if (!np) | ||
|  | 		return -ENODEV; | ||
|  | 
 | ||
|  | 	/* get regmaps */ | ||
|  | 	pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll"); | ||
|  | 	if (IS_ERR(pllctrl_regs)) | ||
|  | 		return PTR_ERR(pllctrl_regs); | ||
|  | 
 | ||
|  | 	devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); | ||
|  | 	if (IS_ERR(devctrl_regs)) | ||
|  | 		return PTR_ERR(devctrl_regs); | ||
|  | 
 | ||
|  | 	ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset); | ||
|  | 	if (ret) { | ||
|  | 		dev_err(dev, "couldn't read the reset pll offset!\n"); | ||
|  | 		return -EINVAL; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset); | ||
|  | 	if (ret) { | ||
|  | 		dev_err(dev, "couldn't read the rsmux offset!\n"); | ||
|  | 		return -EINVAL; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	/* set soft/hard reset */ | ||
|  | 	val = of_property_read_bool(np, "ti,soft-reset"); | ||
|  | 	val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD; | ||
|  | 
 | ||
|  | 	ret = rsctrl_enable_rspll_write(); | ||
|  | 	if (ret) | ||
|  | 		return ret; | ||
|  | 
 | ||
|  | 	ret = regmap_write(pllctrl_regs, rspll_offset + RSCFG_RG, val); | ||
|  | 	if (ret) | ||
|  | 		return ret; | ||
|  | 
 | ||
|  | 	arm_pm_restart = rsctrl_restart; | ||
|  | 
 | ||
|  | 	/* disable a reset isolation for all module clocks */ | ||
|  | 	ret = regmap_write(pllctrl_regs, rspll_offset + RSISO_RG, 0); | ||
|  | 	if (ret) | ||
|  | 		return ret; | ||
|  | 
 | ||
|  | 	/* enable a reset for watchdogs from wdt-list */ | ||
|  | 	for (i = 0; i < WDT_MUX_NUMBER; i++) { | ||
|  | 		ret = of_property_read_u32_index(np, "ti,wdt-list", i, &val); | ||
|  | 		if (ret == -EOVERFLOW && !i) { | ||
|  | 			dev_err(dev, "ti,wdt-list property has to contain at" | ||
|  | 				"least one entry\n"); | ||
|  | 			return -EINVAL; | ||
|  | 		} else if (ret) { | ||
|  | 			break; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if (val >= WDT_MUX_NUMBER) { | ||
|  | 			dev_err(dev, "ti,wdt-list property can contain" | ||
|  | 				"only numbers < 4\n"); | ||
|  | 			return -EINVAL; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		rg = rsmux_offset + val * 4; | ||
|  | 
 | ||
|  | 		ret = regmap_update_bits(devctrl_regs, rg, RSMUX_OMODE_MASK, | ||
|  | 					 RSMUX_OMODE_RESET_ON | | ||
|  | 					 RSMUX_LOCK_SET); | ||
|  | 		if (ret) | ||
|  | 			return ret; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return 0; | ||
|  | } | ||
|  | 
 | ||
|  | static struct platform_driver rsctrl_driver = { | ||
|  | 	.probe = rsctrl_probe, | ||
|  | 	.driver = { | ||
|  | 		.owner = THIS_MODULE, | ||
|  | 		.name = KBUILD_MODNAME, | ||
|  | 		.of_match_table = rsctrl_of_match, | ||
|  | 	}, | ||
|  | }; | ||
|  | module_platform_driver(rsctrl_driver); | ||
|  | 
 | ||
|  | MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>"); | ||
|  | MODULE_DESCRIPTION("Texas Instruments keystone reset driver"); | ||
|  | MODULE_LICENSE("GPL v2"); | ||
|  | MODULE_ALIAS("platform:" KBUILD_MODNAME); |