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

pm8916_wdt.c (6119B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <linux/bitops.h>
      3#include <linux/interrupt.h>
      4#include <linux/kernel.h>
      5#include <linux/module.h>
      6#include <linux/of.h>
      7#include <linux/property.h>
      8#include <linux/platform_device.h>
      9#include <linux/regmap.h>
     10#include <linux/watchdog.h>
     11
     12#define PON_INT_RT_STS			0x10
     13#define PMIC_WD_BARK_STS_BIT		BIT(6)
     14
     15#define PON_PMIC_WD_RESET_S1_TIMER	0x54
     16#define PON_PMIC_WD_RESET_S2_TIMER	0x55
     17
     18#define PON_PMIC_WD_RESET_S2_CTL	0x56
     19#define RESET_TYPE_WARM			0x01
     20#define RESET_TYPE_SHUTDOWN		0x04
     21#define RESET_TYPE_HARD			0x07
     22
     23#define PON_PMIC_WD_RESET_S2_CTL2	0x57
     24#define S2_RESET_EN_BIT			BIT(7)
     25
     26#define PON_PMIC_WD_RESET_PET		0x58
     27#define WATCHDOG_PET_BIT		BIT(0)
     28
     29#define PM8916_WDT_DEFAULT_TIMEOUT	32
     30#define PM8916_WDT_MIN_TIMEOUT		1
     31#define PM8916_WDT_MAX_TIMEOUT		127
     32
     33struct pm8916_wdt {
     34	struct regmap *regmap;
     35	struct watchdog_device wdev;
     36	u32 baseaddr;
     37};
     38
     39static int pm8916_wdt_start(struct watchdog_device *wdev)
     40{
     41	struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
     42
     43	return regmap_update_bits(wdt->regmap,
     44				  wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
     45				  S2_RESET_EN_BIT, S2_RESET_EN_BIT);
     46}
     47
     48static int pm8916_wdt_stop(struct watchdog_device *wdev)
     49{
     50	struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
     51
     52	return regmap_update_bits(wdt->regmap,
     53				  wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL2,
     54				  S2_RESET_EN_BIT, 0);
     55}
     56
     57static int pm8916_wdt_ping(struct watchdog_device *wdev)
     58{
     59	struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
     60
     61	return regmap_update_bits(wdt->regmap,
     62				  wdt->baseaddr + PON_PMIC_WD_RESET_PET,
     63				  WATCHDOG_PET_BIT, WATCHDOG_PET_BIT);
     64}
     65
     66static int pm8916_wdt_configure_timers(struct watchdog_device *wdev)
     67{
     68	struct pm8916_wdt *wdt = watchdog_get_drvdata(wdev);
     69	int err;
     70
     71	err = regmap_write(wdt->regmap,
     72			   wdt->baseaddr + PON_PMIC_WD_RESET_S1_TIMER,
     73			   wdev->timeout - wdev->pretimeout);
     74	if (err)
     75		return err;
     76
     77	return regmap_write(wdt->regmap,
     78			    wdt->baseaddr + PON_PMIC_WD_RESET_S2_TIMER,
     79			    wdev->pretimeout);
     80}
     81
     82static int pm8916_wdt_set_timeout(struct watchdog_device *wdev,
     83				  unsigned int timeout)
     84{
     85	wdev->timeout = timeout;
     86
     87	return pm8916_wdt_configure_timers(wdev);
     88}
     89
     90static int pm8916_wdt_set_pretimeout(struct watchdog_device *wdev,
     91				     unsigned int pretimeout)
     92{
     93	wdev->pretimeout = pretimeout;
     94
     95	return pm8916_wdt_configure_timers(wdev);
     96}
     97
     98static irqreturn_t pm8916_wdt_isr(int irq, void *arg)
     99{
    100	struct pm8916_wdt *wdt = arg;
    101	int err, sts;
    102
    103	err = regmap_read(wdt->regmap, wdt->baseaddr + PON_INT_RT_STS, &sts);
    104	if (err)
    105		return IRQ_HANDLED;
    106
    107	if (sts & PMIC_WD_BARK_STS_BIT)
    108		watchdog_notify_pretimeout(&wdt->wdev);
    109
    110	return IRQ_HANDLED;
    111}
    112
    113static const struct watchdog_info pm8916_wdt_ident = {
    114	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
    115	.identity = "QCOM PM8916 PON WDT",
    116};
    117
    118static const struct watchdog_info pm8916_wdt_pt_ident = {
    119	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE |
    120		   WDIOF_PRETIMEOUT,
    121	.identity = "QCOM PM8916 PON WDT",
    122};
    123
    124static const struct watchdog_ops pm8916_wdt_ops = {
    125	.owner = THIS_MODULE,
    126	.start = pm8916_wdt_start,
    127	.stop = pm8916_wdt_stop,
    128	.ping = pm8916_wdt_ping,
    129	.set_timeout = pm8916_wdt_set_timeout,
    130	.set_pretimeout = pm8916_wdt_set_pretimeout,
    131};
    132
    133static int pm8916_wdt_probe(struct platform_device *pdev)
    134{
    135	struct device *dev = &pdev->dev;
    136	struct pm8916_wdt *wdt;
    137	struct device *parent;
    138	int err, irq;
    139
    140	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
    141	if (!wdt)
    142		return -ENOMEM;
    143
    144	parent = dev->parent;
    145
    146	/*
    147	 * The pm8916-pon-wdt is a child of the pon device, which is a child
    148	 * of the pm8916 mfd device. We want access to the pm8916 registers.
    149	 * Retrieve regmap from pm8916 (parent->parent) and base address
    150	 * from pm8916-pon (pon).
    151	 */
    152	wdt->regmap = dev_get_regmap(parent->parent, NULL);
    153	if (!wdt->regmap) {
    154		dev_err(dev, "failed to locate regmap\n");
    155		return -ENODEV;
    156	}
    157
    158	err = device_property_read_u32(parent, "reg", &wdt->baseaddr);
    159	if (err) {
    160		dev_err(dev, "failed to get pm8916-pon address\n");
    161		return err;
    162	}
    163
    164	irq = platform_get_irq(pdev, 0);
    165	if (irq > 0) {
    166		err = devm_request_irq(dev, irq, pm8916_wdt_isr, 0,
    167				       "pm8916_wdt", wdt);
    168		if (err)
    169			return err;
    170
    171		wdt->wdev.info = &pm8916_wdt_pt_ident;
    172	} else {
    173		if (irq == -EPROBE_DEFER)
    174			return -EPROBE_DEFER;
    175
    176		wdt->wdev.info = &pm8916_wdt_ident;
    177	}
    178
    179	/* Configure watchdog to hard-reset mode */
    180	err = regmap_write(wdt->regmap,
    181			   wdt->baseaddr + PON_PMIC_WD_RESET_S2_CTL,
    182			   RESET_TYPE_HARD);
    183	if (err) {
    184		dev_err(dev, "failed configure watchdog\n");
    185		return err;
    186	}
    187
    188	wdt->wdev.ops = &pm8916_wdt_ops,
    189	wdt->wdev.parent = dev;
    190	wdt->wdev.min_timeout = PM8916_WDT_MIN_TIMEOUT;
    191	wdt->wdev.max_timeout = PM8916_WDT_MAX_TIMEOUT;
    192	wdt->wdev.timeout = PM8916_WDT_DEFAULT_TIMEOUT;
    193	wdt->wdev.pretimeout = 0;
    194	watchdog_set_drvdata(&wdt->wdev, wdt);
    195	platform_set_drvdata(pdev, wdt);
    196
    197	watchdog_init_timeout(&wdt->wdev, 0, dev);
    198	pm8916_wdt_configure_timers(&wdt->wdev);
    199
    200	return devm_watchdog_register_device(dev, &wdt->wdev);
    201}
    202
    203static int __maybe_unused pm8916_wdt_suspend(struct device *dev)
    204{
    205	struct pm8916_wdt *wdt = dev_get_drvdata(dev);
    206
    207	if (watchdog_active(&wdt->wdev))
    208		return pm8916_wdt_stop(&wdt->wdev);
    209
    210	return 0;
    211}
    212
    213static int __maybe_unused pm8916_wdt_resume(struct device *dev)
    214{
    215	struct pm8916_wdt *wdt = dev_get_drvdata(dev);
    216
    217	if (watchdog_active(&wdt->wdev))
    218		return pm8916_wdt_start(&wdt->wdev);
    219
    220	return 0;
    221}
    222
    223static SIMPLE_DEV_PM_OPS(pm8916_wdt_pm_ops, pm8916_wdt_suspend,
    224			 pm8916_wdt_resume);
    225
    226static const struct of_device_id pm8916_wdt_id_table[] = {
    227	{ .compatible = "qcom,pm8916-wdt" },
    228	{ }
    229};
    230MODULE_DEVICE_TABLE(of, pm8916_wdt_id_table);
    231
    232static struct platform_driver pm8916_wdt_driver = {
    233	.probe = pm8916_wdt_probe,
    234	.driver = {
    235		.name = "pm8916-wdt",
    236		.of_match_table = of_match_ptr(pm8916_wdt_id_table),
    237		.pm = &pm8916_wdt_pm_ops,
    238	},
    239};
    240module_platform_driver(pm8916_wdt_driver);
    241
    242MODULE_AUTHOR("Loic Poulain <loic.poulain@linaro.org>");
    243MODULE_DESCRIPTION("Qualcomm pm8916 watchdog driver");
    244MODULE_LICENSE("GPL v2");