| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * TI keystone reboot driver | |
| * | |
| * Copyright (C) 2014 Texas Instruments Incorporated. http://www.ti.com/ | |
| * | |
| * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> | |
| */ | |
| #include <linux/io.h> | |
| #include <linux/module.h> | |
| #include <linux/notifier.h> | |
| #include <linux/reboot.h> | |
| #include <linux/regmap.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 int rsctrl_restart_handler(struct notifier_block *this, | |
| unsigned long mode, void *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); | |
| return NOTIFY_DONE; | |
| } | |
| static struct notifier_block rsctrl_restart_nb = { | |
| .notifier_call = rsctrl_restart_handler, | |
| .priority = 128, | |
| }; | |
| static const 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; | |
| /* 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; | |
| } | |
| ret = register_restart_handler(&rsctrl_restart_nb); | |
| if (ret) | |
| dev_err(dev, "cannot register restart handler (err=%d)\n", ret); | |
| return ret; | |
| } | |
| static struct platform_driver rsctrl_driver = { | |
| .probe = rsctrl_probe, | |
| .driver = { | |
| .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); |