brintos

brintos / linux-shallow public Read only

0
0
Text · 9.0 KiB · 4e689b6 Raw
382 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Spreadtrum watchdog driver4 * Copyright (C) 2017 Spreadtrum - http://www.spreadtrum.com5 */6 7#include <linux/bitops.h>8#include <linux/clk.h>9#include <linux/delay.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/interrupt.h>13#include <linux/io.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/of_address.h>18#include <linux/platform_device.h>19#include <linux/watchdog.h>20 21#define SPRD_WDT_LOAD_LOW		0x022#define SPRD_WDT_LOAD_HIGH		0x423#define SPRD_WDT_CTRL			0x824#define SPRD_WDT_INT_CLR		0xc25#define SPRD_WDT_INT_RAW		0x1026#define SPRD_WDT_INT_MSK		0x1427#define SPRD_WDT_CNT_LOW		0x1828#define SPRD_WDT_CNT_HIGH		0x1c29#define SPRD_WDT_LOCK			0x2030#define SPRD_WDT_IRQ_LOAD_LOW		0x2c31#define SPRD_WDT_IRQ_LOAD_HIGH		0x3032 33/* WDT_CTRL */34#define SPRD_WDT_INT_EN_BIT		BIT(0)35#define SPRD_WDT_CNT_EN_BIT		BIT(1)36#define SPRD_WDT_NEW_VER_EN		BIT(2)37#define SPRD_WDT_RST_EN_BIT		BIT(3)38 39/* WDT_INT_CLR */40#define SPRD_WDT_INT_CLEAR_BIT		BIT(0)41#define SPRD_WDT_RST_CLEAR_BIT		BIT(3)42 43/* WDT_INT_RAW */44#define SPRD_WDT_INT_RAW_BIT		BIT(0)45#define SPRD_WDT_RST_RAW_BIT		BIT(3)46#define SPRD_WDT_LD_BUSY_BIT		BIT(4)47 48/* 1s equal to 32768 counter steps */49#define SPRD_WDT_CNT_STEP		3276850 51#define SPRD_WDT_UNLOCK_KEY		0xe55152#define SPRD_WDT_MIN_TIMEOUT		353#define SPRD_WDT_MAX_TIMEOUT		6054 55#define SPRD_WDT_CNT_HIGH_SHIFT		1656#define SPRD_WDT_LOW_VALUE_MASK		GENMASK(15, 0)57#define SPRD_WDT_LOAD_TIMEOUT		1158 59struct sprd_wdt {60	void __iomem *base;61	struct watchdog_device wdd;62	struct clk *enable;63	struct clk *rtc_enable;64	int irq;65};66 67static inline struct sprd_wdt *to_sprd_wdt(struct watchdog_device *wdd)68{69	return container_of(wdd, struct sprd_wdt, wdd);70}71 72static inline void sprd_wdt_lock(void __iomem *addr)73{74	writel_relaxed(0x0, addr + SPRD_WDT_LOCK);75}76 77static inline void sprd_wdt_unlock(void __iomem *addr)78{79	writel_relaxed(SPRD_WDT_UNLOCK_KEY, addr + SPRD_WDT_LOCK);80}81 82static irqreturn_t sprd_wdt_isr(int irq, void *dev_id)83{84	struct sprd_wdt *wdt = (struct sprd_wdt *)dev_id;85 86	sprd_wdt_unlock(wdt->base);87	writel_relaxed(SPRD_WDT_INT_CLEAR_BIT, wdt->base + SPRD_WDT_INT_CLR);88	sprd_wdt_lock(wdt->base);89	watchdog_notify_pretimeout(&wdt->wdd);90	return IRQ_HANDLED;91}92 93static u32 sprd_wdt_get_cnt_value(struct sprd_wdt *wdt)94{95	u32 val;96 97	val = readl_relaxed(wdt->base + SPRD_WDT_CNT_HIGH) <<98		SPRD_WDT_CNT_HIGH_SHIFT;99	val |= readl_relaxed(wdt->base + SPRD_WDT_CNT_LOW) &100		SPRD_WDT_LOW_VALUE_MASK;101 102	return val;103}104 105static int sprd_wdt_load_value(struct sprd_wdt *wdt, u32 timeout,106			       u32 pretimeout)107{108	u32 val, delay_cnt = 0;109	u32 tmr_step = timeout * SPRD_WDT_CNT_STEP;110	u32 prtmr_step = pretimeout * SPRD_WDT_CNT_STEP;111 112	/*113	 * Checking busy bit to make sure the previous loading operation is114	 * done. According to the specification, the busy bit would be set115	 * after a new loading operation and last 2 or 3 RTC clock116	 * cycles (about 60us~92us).117	 */118	do {119		val = readl_relaxed(wdt->base + SPRD_WDT_INT_RAW);120		if (!(val & SPRD_WDT_LD_BUSY_BIT))121			break;122 123		usleep_range(10, 100);124	} while (delay_cnt++ < SPRD_WDT_LOAD_TIMEOUT);125 126	if (delay_cnt >= SPRD_WDT_LOAD_TIMEOUT)127		return -EBUSY;128 129	sprd_wdt_unlock(wdt->base);130	writel_relaxed((tmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) &131		      SPRD_WDT_LOW_VALUE_MASK, wdt->base + SPRD_WDT_LOAD_HIGH);132	writel_relaxed((tmr_step & SPRD_WDT_LOW_VALUE_MASK),133		       wdt->base + SPRD_WDT_LOAD_LOW);134	writel_relaxed((prtmr_step >> SPRD_WDT_CNT_HIGH_SHIFT) &135			SPRD_WDT_LOW_VALUE_MASK,136		       wdt->base + SPRD_WDT_IRQ_LOAD_HIGH);137	writel_relaxed(prtmr_step & SPRD_WDT_LOW_VALUE_MASK,138		       wdt->base + SPRD_WDT_IRQ_LOAD_LOW);139	sprd_wdt_lock(wdt->base);140 141	return 0;142}143 144static int sprd_wdt_enable(struct sprd_wdt *wdt)145{146	u32 val;147	int ret;148 149	ret = clk_prepare_enable(wdt->enable);150	if (ret)151		return ret;152	ret = clk_prepare_enable(wdt->rtc_enable);153	if (ret) {154		clk_disable_unprepare(wdt->enable);155		return ret;156	}157 158	sprd_wdt_unlock(wdt->base);159	val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);160	val |= SPRD_WDT_NEW_VER_EN;161	writel_relaxed(val, wdt->base + SPRD_WDT_CTRL);162	sprd_wdt_lock(wdt->base);163	return 0;164}165 166static void sprd_wdt_disable(void *_data)167{168	struct sprd_wdt *wdt = _data;169 170	sprd_wdt_unlock(wdt->base);171	writel_relaxed(0x0, wdt->base + SPRD_WDT_CTRL);172	sprd_wdt_lock(wdt->base);173 174	clk_disable_unprepare(wdt->rtc_enable);175	clk_disable_unprepare(wdt->enable);176}177 178static int sprd_wdt_start(struct watchdog_device *wdd)179{180	struct sprd_wdt *wdt = to_sprd_wdt(wdd);181	u32 val;182	int ret;183 184	ret = sprd_wdt_load_value(wdt, wdd->timeout, wdd->pretimeout);185	if (ret)186		return ret;187 188	sprd_wdt_unlock(wdt->base);189	val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);190	val |= SPRD_WDT_CNT_EN_BIT | SPRD_WDT_INT_EN_BIT | SPRD_WDT_RST_EN_BIT;191	writel_relaxed(val, wdt->base + SPRD_WDT_CTRL);192	sprd_wdt_lock(wdt->base);193	set_bit(WDOG_HW_RUNNING, &wdd->status);194 195	return 0;196}197 198static int sprd_wdt_stop(struct watchdog_device *wdd)199{200	struct sprd_wdt *wdt = to_sprd_wdt(wdd);201	u32 val;202 203	sprd_wdt_unlock(wdt->base);204	val = readl_relaxed(wdt->base + SPRD_WDT_CTRL);205	val &= ~(SPRD_WDT_CNT_EN_BIT | SPRD_WDT_RST_EN_BIT |206		SPRD_WDT_INT_EN_BIT);207	writel_relaxed(val, wdt->base + SPRD_WDT_CTRL);208	sprd_wdt_lock(wdt->base);209	return 0;210}211 212static int sprd_wdt_set_timeout(struct watchdog_device *wdd,213				u32 timeout)214{215	struct sprd_wdt *wdt = to_sprd_wdt(wdd);216 217	if (timeout == wdd->timeout)218		return 0;219 220	wdd->timeout = timeout;221 222	return sprd_wdt_load_value(wdt, timeout, wdd->pretimeout);223}224 225static int sprd_wdt_set_pretimeout(struct watchdog_device *wdd,226				   u32 new_pretimeout)227{228	struct sprd_wdt *wdt = to_sprd_wdt(wdd);229 230	if (new_pretimeout < wdd->min_timeout)231		return -EINVAL;232 233	wdd->pretimeout = new_pretimeout;234 235	return sprd_wdt_load_value(wdt, wdd->timeout, new_pretimeout);236}237 238static u32 sprd_wdt_get_timeleft(struct watchdog_device *wdd)239{240	struct sprd_wdt *wdt = to_sprd_wdt(wdd);241	u32 val;242 243	val = sprd_wdt_get_cnt_value(wdt);244	return val / SPRD_WDT_CNT_STEP;245}246 247static const struct watchdog_ops sprd_wdt_ops = {248	.owner = THIS_MODULE,249	.start = sprd_wdt_start,250	.stop = sprd_wdt_stop,251	.set_timeout = sprd_wdt_set_timeout,252	.set_pretimeout = sprd_wdt_set_pretimeout,253	.get_timeleft = sprd_wdt_get_timeleft,254};255 256static const struct watchdog_info sprd_wdt_info = {257	.options = WDIOF_SETTIMEOUT |258		   WDIOF_PRETIMEOUT |259		   WDIOF_MAGICCLOSE |260		   WDIOF_KEEPALIVEPING,261	.identity = "Spreadtrum Watchdog Timer",262};263 264static int sprd_wdt_probe(struct platform_device *pdev)265{266	struct device *dev = &pdev->dev;267	struct sprd_wdt *wdt;268	int ret;269 270	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);271	if (!wdt)272		return -ENOMEM;273 274	wdt->base = devm_platform_ioremap_resource(pdev, 0);275	if (IS_ERR(wdt->base))276		return PTR_ERR(wdt->base);277 278	wdt->enable = devm_clk_get(dev, "enable");279	if (IS_ERR(wdt->enable)) {280		dev_err(dev, "can't get the enable clock\n");281		return PTR_ERR(wdt->enable);282	}283 284	wdt->rtc_enable = devm_clk_get(dev, "rtc_enable");285	if (IS_ERR(wdt->rtc_enable)) {286		dev_err(dev, "can't get the rtc enable clock\n");287		return PTR_ERR(wdt->rtc_enable);288	}289 290	wdt->irq = platform_get_irq(pdev, 0);291	if (wdt->irq < 0)292		return wdt->irq;293 294	ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND,295			       "sprd-wdt", (void *)wdt);296	if (ret) {297		dev_err(dev, "failed to register irq\n");298		return ret;299	}300 301	wdt->wdd.info = &sprd_wdt_info;302	wdt->wdd.ops = &sprd_wdt_ops;303	wdt->wdd.parent = dev;304	wdt->wdd.min_timeout = SPRD_WDT_MIN_TIMEOUT;305	wdt->wdd.max_timeout = SPRD_WDT_MAX_TIMEOUT;306	wdt->wdd.timeout = SPRD_WDT_MAX_TIMEOUT;307 308	ret = sprd_wdt_enable(wdt);309	if (ret) {310		dev_err(dev, "failed to enable wdt\n");311		return ret;312	}313	ret = devm_add_action_or_reset(dev, sprd_wdt_disable, wdt);314	if (ret) {315		dev_err(dev, "Failed to add wdt disable action\n");316		return ret;317	}318 319	watchdog_set_nowayout(&wdt->wdd, WATCHDOG_NOWAYOUT);320	watchdog_init_timeout(&wdt->wdd, 0, dev);321 322	ret = devm_watchdog_register_device(dev, &wdt->wdd);323	if (ret) {324		sprd_wdt_disable(wdt);325		return ret;326	}327	platform_set_drvdata(pdev, wdt);328 329	return 0;330}331 332static int __maybe_unused sprd_wdt_pm_suspend(struct device *dev)333{334	struct sprd_wdt *wdt = dev_get_drvdata(dev);335 336	if (watchdog_active(&wdt->wdd))337		sprd_wdt_stop(&wdt->wdd);338	sprd_wdt_disable(wdt);339 340	return 0;341}342 343static int __maybe_unused sprd_wdt_pm_resume(struct device *dev)344{345	struct sprd_wdt *wdt = dev_get_drvdata(dev);346	int ret;347 348	ret = sprd_wdt_enable(wdt);349	if (ret)350		return ret;351 352	if (watchdog_active(&wdt->wdd))353		ret = sprd_wdt_start(&wdt->wdd);354 355	return ret;356}357 358static const struct dev_pm_ops sprd_wdt_pm_ops = {359	SET_SYSTEM_SLEEP_PM_OPS(sprd_wdt_pm_suspend,360				sprd_wdt_pm_resume)361};362 363static const struct of_device_id sprd_wdt_match_table[] = {364	{ .compatible = "sprd,sp9860-wdt", },365	{},366};367MODULE_DEVICE_TABLE(of, sprd_wdt_match_table);368 369static struct platform_driver sprd_watchdog_driver = {370	.probe	= sprd_wdt_probe,371	.driver	= {372		.name = "sprd-wdt",373		.of_match_table = sprd_wdt_match_table,374		.pm = &sprd_wdt_pm_ops,375	},376};377module_platform_driver(sprd_watchdog_driver);378 379MODULE_AUTHOR("Eric Long <eric.long@spreadtrum.com>");380MODULE_DESCRIPTION("Spreadtrum Watchdog Timer Controller Driver");381MODULE_LICENSE("GPL v2");382