diff options
Diffstat (limited to 'drivers/power/reset/keystone-reset.c')
| -rw-r--r-- | drivers/power/reset/keystone-reset.c | 166 | 
1 files changed, 166 insertions, 0 deletions
diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c new file mode 100644 index 00000000000..408a18fd91c --- /dev/null +++ b/drivers/power/reset/keystone-reset.c @@ -0,0 +1,166 @@ +/* + * 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);  | 
