forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
4.1 KiB
173 lines
4.1 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* TI keystone reboot driver |
|
* |
|
* Copyright (C) 2014 Texas Instruments Incorporated. https://www.ti.com/ |
|
* |
|
* Author: Ivan Khoronzhuk <[email protected]> |
|
*/ |
|
|
|
#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", }, |
|
{}, |
|
}; |
|
MODULE_DEVICE_TABLE(of, rsctrl_of_match); |
|
|
|
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 <[email protected]>"); |
|
MODULE_DESCRIPTION("Texas Instruments keystone reset driver"); |
|
MODULE_LICENSE("GPL v2"); |
|
MODULE_ALIAS("platform:" KBUILD_MODNAME);
|
|
|