cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

st_lpc_wdt.c (7031B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * ST's LPC Watchdog
      4 *
      5 * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
      6 *
      7 * Author: David Paris <david.paris@st.com> for STMicroelectronics
      8 *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
      9 */
     10
     11#include <linux/clk.h>
     12#include <linux/init.h>
     13#include <linux/io.h>
     14#include <linux/kernel.h>
     15#include <linux/mfd/syscon.h>
     16#include <linux/module.h>
     17#include <linux/of.h>
     18#include <linux/of_platform.h>
     19#include <linux/platform_device.h>
     20#include <linux/regmap.h>
     21#include <linux/watchdog.h>
     22
     23#include <dt-bindings/mfd/st-lpc.h>
     24
     25/* Low Power Alarm */
     26#define LPC_LPA_LSB_OFF			0x410
     27#define LPC_LPA_START_OFF		0x418
     28
     29/* LPC as WDT */
     30#define LPC_WDT_OFF			0x510
     31
     32static struct watchdog_device st_wdog_dev;
     33
     34struct st_wdog_syscfg {
     35	unsigned int reset_type_reg;
     36	unsigned int reset_type_mask;
     37	unsigned int enable_reg;
     38	unsigned int enable_mask;
     39};
     40
     41struct st_wdog {
     42	void __iomem *base;
     43	struct device *dev;
     44	struct regmap *regmap;
     45	struct st_wdog_syscfg *syscfg;
     46	struct clk *clk;
     47	unsigned long clkrate;
     48	bool warm_reset;
     49};
     50
     51static struct st_wdog_syscfg stih407_syscfg = {
     52	.enable_reg		= 0x204,
     53	.enable_mask		= BIT(19),
     54};
     55
     56static const struct of_device_id st_wdog_match[] = {
     57	{
     58		.compatible = "st,stih407-lpc",
     59		.data = &stih407_syscfg,
     60	},
     61	{},
     62};
     63MODULE_DEVICE_TABLE(of, st_wdog_match);
     64
     65static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
     66{
     67	/* Type of watchdog reset - 0: Cold 1: Warm */
     68	if (st_wdog->syscfg->reset_type_reg)
     69		regmap_update_bits(st_wdog->regmap,
     70				   st_wdog->syscfg->reset_type_reg,
     71				   st_wdog->syscfg->reset_type_mask,
     72				   st_wdog->warm_reset);
     73
     74	/* Mask/unmask watchdog reset */
     75	regmap_update_bits(st_wdog->regmap,
     76			   st_wdog->syscfg->enable_reg,
     77			   st_wdog->syscfg->enable_mask,
     78			   enable ? 0 : st_wdog->syscfg->enable_mask);
     79}
     80
     81static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
     82{
     83	unsigned long clkrate = st_wdog->clkrate;
     84
     85	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
     86	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
     87}
     88
     89static int st_wdog_start(struct watchdog_device *wdd)
     90{
     91	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
     92
     93	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
     94
     95	return 0;
     96}
     97
     98static int st_wdog_stop(struct watchdog_device *wdd)
     99{
    100	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
    101
    102	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
    103
    104	return 0;
    105}
    106
    107static int st_wdog_set_timeout(struct watchdog_device *wdd,
    108			       unsigned int timeout)
    109{
    110	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
    111
    112	wdd->timeout = timeout;
    113	st_wdog_load_timer(st_wdog, timeout);
    114
    115	return 0;
    116}
    117
    118static int st_wdog_keepalive(struct watchdog_device *wdd)
    119{
    120	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
    121
    122	st_wdog_load_timer(st_wdog, wdd->timeout);
    123
    124	return 0;
    125}
    126
    127static const struct watchdog_info st_wdog_info = {
    128	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    129	.identity = "ST LPC WDT",
    130};
    131
    132static const struct watchdog_ops st_wdog_ops = {
    133	.owner		= THIS_MODULE,
    134	.start		= st_wdog_start,
    135	.stop		= st_wdog_stop,
    136	.ping		= st_wdog_keepalive,
    137	.set_timeout	= st_wdog_set_timeout,
    138};
    139
    140static struct watchdog_device st_wdog_dev = {
    141	.info		= &st_wdog_info,
    142	.ops		= &st_wdog_ops,
    143};
    144
    145static void st_clk_disable_unprepare(void *data)
    146{
    147	clk_disable_unprepare(data);
    148}
    149
    150static int st_wdog_probe(struct platform_device *pdev)
    151{
    152	struct device *dev = &pdev->dev;
    153	const struct of_device_id *match;
    154	struct device_node *np = dev->of_node;
    155	struct st_wdog *st_wdog;
    156	struct regmap *regmap;
    157	struct clk *clk;
    158	void __iomem *base;
    159	uint32_t mode;
    160	int ret;
    161
    162	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
    163	if (ret) {
    164		dev_err(dev, "An LPC mode must be provided\n");
    165		return -EINVAL;
    166	}
    167
    168	/* LPC can either run as a Clocksource or in RTC or WDT mode */
    169	if (mode != ST_LPC_MODE_WDT)
    170		return -ENODEV;
    171
    172	st_wdog = devm_kzalloc(dev, sizeof(*st_wdog), GFP_KERNEL);
    173	if (!st_wdog)
    174		return -ENOMEM;
    175
    176	match = of_match_device(st_wdog_match, dev);
    177	if (!match) {
    178		dev_err(dev, "Couldn't match device\n");
    179		return -ENODEV;
    180	}
    181	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
    182
    183	base = devm_platform_ioremap_resource(pdev, 0);
    184	if (IS_ERR(base))
    185		return PTR_ERR(base);
    186
    187	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
    188	if (IS_ERR(regmap)) {
    189		dev_err(dev, "No syscfg phandle specified\n");
    190		return PTR_ERR(regmap);
    191	}
    192
    193	clk = devm_clk_get(dev, NULL);
    194	if (IS_ERR(clk)) {
    195		dev_err(dev, "Unable to request clock\n");
    196		return PTR_ERR(clk);
    197	}
    198
    199	st_wdog->dev		= dev;
    200	st_wdog->base		= base;
    201	st_wdog->clk		= clk;
    202	st_wdog->regmap		= regmap;
    203	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
    204	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
    205
    206	if (!st_wdog->clkrate) {
    207		dev_err(dev, "Unable to fetch clock rate\n");
    208		return -EINVAL;
    209	}
    210	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
    211	st_wdog_dev.parent = dev;
    212
    213	ret = clk_prepare_enable(clk);
    214	if (ret) {
    215		dev_err(dev, "Unable to enable clock\n");
    216		return ret;
    217	}
    218	ret = devm_add_action_or_reset(dev, st_clk_disable_unprepare, clk);
    219	if (ret)
    220		return ret;
    221
    222	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
    223	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
    224
    225	/* Init Watchdog timeout with value in DT */
    226	ret = watchdog_init_timeout(&st_wdog_dev, 0, dev);
    227	if (ret)
    228		return ret;
    229
    230	ret = devm_watchdog_register_device(dev, &st_wdog_dev);
    231	if (ret)
    232		return ret;
    233
    234	st_wdog_setup(st_wdog, true);
    235
    236	dev_info(dev, "LPC Watchdog driver registered, reset type is %s",
    237		 st_wdog->warm_reset ? "warm" : "cold");
    238
    239	return ret;
    240}
    241
    242static int st_wdog_remove(struct platform_device *pdev)
    243{
    244	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
    245
    246	st_wdog_setup(st_wdog, false);
    247
    248	return 0;
    249}
    250
    251#ifdef CONFIG_PM_SLEEP
    252static int st_wdog_suspend(struct device *dev)
    253{
    254	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
    255
    256	if (watchdog_active(&st_wdog_dev))
    257		st_wdog_stop(&st_wdog_dev);
    258
    259	st_wdog_setup(st_wdog, false);
    260
    261	clk_disable(st_wdog->clk);
    262
    263	return 0;
    264}
    265
    266static int st_wdog_resume(struct device *dev)
    267{
    268	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
    269	int ret;
    270
    271	ret = clk_enable(st_wdog->clk);
    272	if (ret) {
    273		dev_err(dev, "Unable to re-enable clock\n");
    274		watchdog_unregister_device(&st_wdog_dev);
    275		clk_unprepare(st_wdog->clk);
    276		return ret;
    277	}
    278
    279	st_wdog_setup(st_wdog, true);
    280
    281	if (watchdog_active(&st_wdog_dev)) {
    282		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
    283		st_wdog_start(&st_wdog_dev);
    284	}
    285
    286	return 0;
    287}
    288#endif
    289
    290static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
    291			 st_wdog_suspend,
    292			 st_wdog_resume);
    293
    294static struct platform_driver st_wdog_driver = {
    295	.driver	= {
    296		.name = "st-lpc-wdt",
    297		.pm = &st_wdog_pm_ops,
    298		.of_match_table = st_wdog_match,
    299	},
    300	.probe = st_wdog_probe,
    301	.remove = st_wdog_remove,
    302};
    303module_platform_driver(st_wdog_driver);
    304
    305MODULE_AUTHOR("David Paris <david.paris@st.com>");
    306MODULE_DESCRIPTION("ST LPC Watchdog Driver");
    307MODULE_LICENSE("GPL");