18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * TI keystone reboot driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014 Texas Instruments Incorporated. https://www.ti.com/
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/notifier.h>
138c2ecf20Sopenharmony_ci#include <linux/reboot.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
168c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define RSTYPE_RG			0x0
198c2ecf20Sopenharmony_ci#define RSCTRL_RG			0x4
208c2ecf20Sopenharmony_ci#define RSCFG_RG			0x8
218c2ecf20Sopenharmony_ci#define RSISO_RG			0xc
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define RSCTRL_KEY_MASK			0x0000ffff
248c2ecf20Sopenharmony_ci#define RSCTRL_RESET_MASK		BIT(16)
258c2ecf20Sopenharmony_ci#define RSCTRL_KEY			0x5a69
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define RSMUX_OMODE_MASK		0xe
288c2ecf20Sopenharmony_ci#define RSMUX_OMODE_RESET_ON		0xa
298c2ecf20Sopenharmony_ci#define RSMUX_OMODE_RESET_OFF		0x0
308c2ecf20Sopenharmony_ci#define RSMUX_LOCK_MASK			0x1
318c2ecf20Sopenharmony_ci#define RSMUX_LOCK_SET			0x1
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define RSCFG_RSTYPE_SOFT		0x300f
348c2ecf20Sopenharmony_ci#define RSCFG_RSTYPE_HARD		0x0
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define WDT_MUX_NUMBER			0x4
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int rspll_offset;
398c2ecf20Sopenharmony_cistatic struct regmap *pllctrl_regs;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/**
428c2ecf20Sopenharmony_ci * rsctrl_enable_rspll_write - enable access to RSCTRL, RSCFG
438c2ecf20Sopenharmony_ci * To be able to access to RSCTRL, RSCFG registers
448c2ecf20Sopenharmony_ci * we have to write a key before
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic inline int rsctrl_enable_rspll_write(void)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG,
498c2ecf20Sopenharmony_ci				  RSCTRL_KEY_MASK, RSCTRL_KEY);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int rsctrl_restart_handler(struct notifier_block *this,
538c2ecf20Sopenharmony_ci				  unsigned long mode, void *cmd)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	/* enable write access to RSTCTRL */
568c2ecf20Sopenharmony_ci	rsctrl_enable_rspll_write();
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	/* reset the SOC */
598c2ecf20Sopenharmony_ci	regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG,
608c2ecf20Sopenharmony_ci			   RSCTRL_RESET_MASK, 0);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return NOTIFY_DONE;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct notifier_block rsctrl_restart_nb = {
668c2ecf20Sopenharmony_ci	.notifier_call = rsctrl_restart_handler,
678c2ecf20Sopenharmony_ci	.priority = 128,
688c2ecf20Sopenharmony_ci};
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic const struct of_device_id rsctrl_of_match[] = {
718c2ecf20Sopenharmony_ci	{.compatible = "ti,keystone-reset", },
728c2ecf20Sopenharmony_ci	{},
738c2ecf20Sopenharmony_ci};
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int rsctrl_probe(struct platform_device *pdev)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	int i;
788c2ecf20Sopenharmony_ci	int ret;
798c2ecf20Sopenharmony_ci	u32 val;
808c2ecf20Sopenharmony_ci	unsigned int rg;
818c2ecf20Sopenharmony_ci	u32 rsmux_offset;
828c2ecf20Sopenharmony_ci	struct regmap *devctrl_regs;
838c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
848c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (!np)
878c2ecf20Sopenharmony_ci		return -ENODEV;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* get regmaps */
908c2ecf20Sopenharmony_ci	pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll");
918c2ecf20Sopenharmony_ci	if (IS_ERR(pllctrl_regs))
928c2ecf20Sopenharmony_ci		return PTR_ERR(pllctrl_regs);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
958c2ecf20Sopenharmony_ci	if (IS_ERR(devctrl_regs))
968c2ecf20Sopenharmony_ci		return PTR_ERR(devctrl_regs);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset);
998c2ecf20Sopenharmony_ci	if (ret) {
1008c2ecf20Sopenharmony_ci		dev_err(dev, "couldn't read the reset pll offset!\n");
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset);
1058c2ecf20Sopenharmony_ci	if (ret) {
1068c2ecf20Sopenharmony_ci		dev_err(dev, "couldn't read the rsmux offset!\n");
1078c2ecf20Sopenharmony_ci		return -EINVAL;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/* set soft/hard reset */
1118c2ecf20Sopenharmony_ci	val = of_property_read_bool(np, "ti,soft-reset");
1128c2ecf20Sopenharmony_ci	val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	ret = rsctrl_enable_rspll_write();
1158c2ecf20Sopenharmony_ci	if (ret)
1168c2ecf20Sopenharmony_ci		return ret;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	ret = regmap_write(pllctrl_regs, rspll_offset + RSCFG_RG, val);
1198c2ecf20Sopenharmony_ci	if (ret)
1208c2ecf20Sopenharmony_ci		return ret;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* disable a reset isolation for all module clocks */
1238c2ecf20Sopenharmony_ci	ret = regmap_write(pllctrl_regs, rspll_offset + RSISO_RG, 0);
1248c2ecf20Sopenharmony_ci	if (ret)
1258c2ecf20Sopenharmony_ci		return ret;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* enable a reset for watchdogs from wdt-list */
1288c2ecf20Sopenharmony_ci	for (i = 0; i < WDT_MUX_NUMBER; i++) {
1298c2ecf20Sopenharmony_ci		ret = of_property_read_u32_index(np, "ti,wdt-list", i, &val);
1308c2ecf20Sopenharmony_ci		if (ret == -EOVERFLOW && !i) {
1318c2ecf20Sopenharmony_ci			dev_err(dev, "ti,wdt-list property has to contain at"
1328c2ecf20Sopenharmony_ci				"least one entry\n");
1338c2ecf20Sopenharmony_ci			return -EINVAL;
1348c2ecf20Sopenharmony_ci		} else if (ret) {
1358c2ecf20Sopenharmony_ci			break;
1368c2ecf20Sopenharmony_ci		}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		if (val >= WDT_MUX_NUMBER) {
1398c2ecf20Sopenharmony_ci			dev_err(dev, "ti,wdt-list property can contain "
1408c2ecf20Sopenharmony_ci				"only numbers < 4\n");
1418c2ecf20Sopenharmony_ci			return -EINVAL;
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci		rg = rsmux_offset + val * 4;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		ret = regmap_update_bits(devctrl_regs, rg, RSMUX_OMODE_MASK,
1478c2ecf20Sopenharmony_ci					 RSMUX_OMODE_RESET_ON |
1488c2ecf20Sopenharmony_ci					 RSMUX_LOCK_SET);
1498c2ecf20Sopenharmony_ci		if (ret)
1508c2ecf20Sopenharmony_ci			return ret;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	ret = register_restart_handler(&rsctrl_restart_nb);
1548c2ecf20Sopenharmony_ci	if (ret)
1558c2ecf20Sopenharmony_ci		dev_err(dev, "cannot register restart handler (err=%d)\n", ret);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return ret;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic struct platform_driver rsctrl_driver = {
1618c2ecf20Sopenharmony_ci	.probe = rsctrl_probe,
1628c2ecf20Sopenharmony_ci	.driver = {
1638c2ecf20Sopenharmony_ci		.name = KBUILD_MODNAME,
1648c2ecf20Sopenharmony_ci		.of_match_table = rsctrl_of_match,
1658c2ecf20Sopenharmony_ci	},
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_cimodule_platform_driver(rsctrl_driver);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
1708c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Texas Instruments keystone reset driver");
1718c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1728c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" KBUILD_MODNAME);
173