brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 4895a69 Raw
399 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Watchdog driver for the K3 RTI module4 *5 * (c) Copyright 2019-2020 Texas Instruments Inc.6 * All rights reserved.7 */8 9#include <linux/clk.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/kernel.h>14#include <linux/mod_devicetable.h>15#include <linux/module.h>16#include <linux/moduleparam.h>17#include <linux/of.h>18#include <linux/of_address.h>19#include <linux/platform_device.h>20#include <linux/pm_runtime.h>21#include <linux/types.h>22#include <linux/watchdog.h>23 24#define DEFAULT_HEARTBEAT 6025 26/* Max heartbeat is calculated at 32kHz source clock */27#define MAX_HEARTBEAT	100028 29/* Timer register set definition */30#define RTIDWDCTRL	0x9031#define RTIDWDPRLD	0x9432#define RTIWDSTATUS	0x9833#define RTIWDKEY	0x9c34#define RTIDWDCNTR	0xa035#define RTIWWDRXCTRL	0xa436#define RTIWWDSIZECTRL	0xa837 38#define RTIWWDRX_NMI	0xa39 40#define RTIWWDSIZE_50P		0x5041#define RTIWWDSIZE_25P		0x50042#define RTIWWDSIZE_12P5		0x500043#define RTIWWDSIZE_6P25		0x5000044#define RTIWWDSIZE_3P125	0x50000045 46#define WDENABLE_KEY	0xa98559da47 48#define WDKEY_SEQ0		0xe51a49#define WDKEY_SEQ1		0xa35c50 51#define WDT_PRELOAD_SHIFT	1352 53#define WDT_PRELOAD_MAX		0xfff54 55#define DWDST			BIT(1)56 57#define PON_REASON_SOF_NUM	0xBBBBCCCC58#define PON_REASON_MAGIC_NUM	0xDDDDDDDD59#define PON_REASON_EOF_NUM	0xCCCCBBBB60#define RESERVED_MEM_MIN_SIZE	1261 62#define MAX_HW_ERROR		25063 64static int heartbeat = DEFAULT_HEARTBEAT;65 66/*67 * struct to hold data for each WDT device68 * @base - base io address of WD device69 * @freq - source clock frequency of WDT70 * @wdd  - hold watchdog device as is in WDT core71 */72struct rti_wdt_device {73	void __iomem		*base;74	unsigned long		freq;75	struct watchdog_device	wdd;76};77 78static int rti_wdt_start(struct watchdog_device *wdd)79{80	u32 timer_margin;81	struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);82	int ret;83 84	ret = pm_runtime_resume_and_get(wdd->parent);85	if (ret)86		return ret;87 88	/* set timeout period */89	timer_margin = (u64)wdd->timeout * wdt->freq;90	timer_margin >>= WDT_PRELOAD_SHIFT;91	if (timer_margin > WDT_PRELOAD_MAX)92		timer_margin = WDT_PRELOAD_MAX;93	writel_relaxed(timer_margin, wdt->base + RTIDWDPRLD);94 95	/*96	 * RTI only supports a windowed mode, where the watchdog can only97	 * be petted during the open window; not too early or not too late.98	 * The HW configuration options only allow for the open window size99	 * to be 50% or less than that; we obviouly want to configure the open100	 * window as large as possible so we select the 50% option.101	 */102	wdd->min_hw_heartbeat_ms = 520 * wdd->timeout + MAX_HW_ERROR;103 104	/* Generate NMI when wdt expires */105	writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL);106 107	/* Open window size 50%; this is the largest window size available */108	writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL);109 110	readl_relaxed(wdt->base + RTIWWDSIZECTRL);111 112	/* enable watchdog */113	writel_relaxed(WDENABLE_KEY, wdt->base + RTIDWDCTRL);114	return 0;115}116 117static int rti_wdt_ping(struct watchdog_device *wdd)118{119	struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);120 121	/* put watchdog in service state */122	writel_relaxed(WDKEY_SEQ0, wdt->base + RTIWDKEY);123	/* put watchdog in active state */124	writel_relaxed(WDKEY_SEQ1, wdt->base + RTIWDKEY);125 126	return 0;127}128 129static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd, u32 wsize)130{131	/*132	 * RTI only supports a windowed mode, where the watchdog can only133	 * be petted during the open window; not too early or not too late.134	 * The HW configuration options only allow for the open window size135	 * to be 50% or less than that.136	 * To avoid any glitches, we accommodate 2% + max hardware error137	 * safety margin.138	 */139	switch (wsize) {140	case RTIWWDSIZE_50P:141		/* 50% open window => 52% min heartbeat */142		wdd->min_hw_heartbeat_ms = 520 * heartbeat + MAX_HW_ERROR;143		break;144 145	case RTIWWDSIZE_25P:146		/* 25% open window => 77% min heartbeat */147		wdd->min_hw_heartbeat_ms = 770 * heartbeat + MAX_HW_ERROR;148		break;149 150	case RTIWWDSIZE_12P5:151		/* 12.5% open window => 89.5% min heartbeat */152		wdd->min_hw_heartbeat_ms = 895 * heartbeat + MAX_HW_ERROR;153		break;154 155	case RTIWWDSIZE_6P25:156		/* 6.5% open window => 95.5% min heartbeat */157		wdd->min_hw_heartbeat_ms = 955 * heartbeat + MAX_HW_ERROR;158		break;159 160	case RTIWWDSIZE_3P125:161		/* 3.125% open window => 98.9% min heartbeat */162		wdd->min_hw_heartbeat_ms = 989 * heartbeat + MAX_HW_ERROR;163		break;164 165	default:166		return -EINVAL;167	}168 169	return 0;170}171 172static unsigned int rti_wdt_get_timeleft_ms(struct watchdog_device *wdd)173{174	u64 timer_counter;175	u32 val;176	struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);177 178	/* if timeout has occurred then return 0 */179	val = readl_relaxed(wdt->base + RTIWDSTATUS);180	if (val & DWDST)181		return 0;182 183	timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR);184 185	timer_counter *= 1000;186 187	do_div(timer_counter, wdt->freq);188 189	return timer_counter;190}191 192static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)193{194	return rti_wdt_get_timeleft_ms(wdd) / 1000;195}196 197static const struct watchdog_info rti_wdt_info = {198	.options = WDIOF_KEEPALIVEPING,199	.identity = "K3 RTI Watchdog",200};201 202static const struct watchdog_ops rti_wdt_ops = {203	.owner		= THIS_MODULE,204	.start		= rti_wdt_start,205	.ping		= rti_wdt_ping,206	.get_timeleft	= rti_wdt_get_timeleft,207};208 209static int rti_wdt_probe(struct platform_device *pdev)210{211	int ret = 0;212	struct device *dev = &pdev->dev;213	struct watchdog_device *wdd;214	struct rti_wdt_device *wdt;215	struct clk *clk;216	u32 last_ping = 0;217	struct device_node *node;218	u32 reserved_mem_size;219	struct resource res;220	u32 *vaddr;221	u64 paddr;222 223	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);224	if (!wdt)225		return -ENOMEM;226 227	clk = clk_get(dev, NULL);228	if (IS_ERR(clk))229		return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");230 231	wdt->freq = clk_get_rate(clk);232 233	clk_put(clk);234 235	if (!wdt->freq) {236		dev_err(dev, "Failed to get fck rate.\n");237		return -EINVAL;238	}239 240	pm_runtime_enable(dev);241	ret = pm_runtime_resume_and_get(dev);242	if (ret < 0) {243		pm_runtime_disable(&pdev->dev);244		return dev_err_probe(dev, ret, "runtime pm failed\n");245	}246 247	platform_set_drvdata(pdev, wdt);248 249	wdd = &wdt->wdd;250	wdd->info = &rti_wdt_info;251	wdd->ops = &rti_wdt_ops;252	wdd->min_timeout = 1;253	wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /254		wdt->freq * 1000;255	wdd->parent = dev;256 257	watchdog_set_drvdata(wdd, wdt);258	watchdog_set_nowayout(wdd, 1);259	watchdog_set_restart_priority(wdd, 128);260 261	wdt->base = devm_platform_ioremap_resource(pdev, 0);262	if (IS_ERR(wdt->base)) {263		ret = PTR_ERR(wdt->base);264		goto err_iomap;265	}266 267	if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) {268		int preset_heartbeat;269		u32 time_left_ms;270		u64 heartbeat_ms;271		u32 wsize;272 273		set_bit(WDOG_HW_RUNNING, &wdd->status);274		time_left_ms = rti_wdt_get_timeleft_ms(wdd);275		heartbeat_ms = readl(wdt->base + RTIDWDPRLD);276		heartbeat_ms <<= WDT_PRELOAD_SHIFT;277		heartbeat_ms *= 1000;278		do_div(heartbeat_ms, wdt->freq);279		preset_heartbeat = heartbeat_ms + 500;280		preset_heartbeat /= 1000;281		if (preset_heartbeat != heartbeat)282			dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n");283 284		heartbeat = preset_heartbeat;285 286		wsize = readl(wdt->base + RTIWWDSIZECTRL);287		ret = rti_wdt_setup_hw_hb(wdd, wsize);288		if (ret) {289			dev_err(dev, "bad window size.\n");290			goto err_iomap;291		}292 293		last_ping = heartbeat_ms - time_left_ms;294		if (time_left_ms > heartbeat_ms) {295			dev_warn(dev, "time_left > heartbeat? Assuming last ping just before now.\n");296			last_ping = 0;297		}298	}299 300	node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);301	if (node) {302		ret = of_address_to_resource(node, 0, &res);303		if (ret) {304			dev_err(dev, "No memory address assigned to the region.\n");305			goto err_iomap;306		}307 308		/*309		 * If reserved memory is defined for watchdog reset cause.310		 * Readout the Power-on(PON) reason and pass to bootstatus.311		 */312		paddr = res.start;313		reserved_mem_size = resource_size(&res);314		if (reserved_mem_size < RESERVED_MEM_MIN_SIZE) {315			dev_err(dev, "The size of reserved memory is too small.\n");316			ret = -EINVAL;317			goto err_iomap;318		}319 320		vaddr = memremap(paddr, reserved_mem_size, MEMREMAP_WB);321		if (!vaddr) {322			dev_err(dev, "Failed to map memory-region.\n");323			ret = -ENOMEM;324			goto err_iomap;325		}326 327		if (vaddr[0] == PON_REASON_SOF_NUM &&328		    vaddr[1] == PON_REASON_MAGIC_NUM &&329		    vaddr[2] == PON_REASON_EOF_NUM) {330			wdd->bootstatus |= WDIOF_CARDRESET;331		}332		memset(vaddr, 0, reserved_mem_size);333		memunmap(vaddr);334	}335 336	watchdog_init_timeout(wdd, heartbeat, dev);337 338	ret = watchdog_register_device(wdd);339	if (ret) {340		dev_err(dev, "cannot register watchdog device\n");341		goto err_iomap;342	}343 344	if (last_ping)345		watchdog_set_last_hw_keepalive(wdd, last_ping);346 347	if (!watchdog_hw_running(wdd))348		pm_runtime_put_sync(&pdev->dev);349 350	return 0;351 352err_iomap:353	pm_runtime_put_sync(&pdev->dev);354	pm_runtime_disable(&pdev->dev);355 356	return ret;357}358 359static void rti_wdt_remove(struct platform_device *pdev)360{361	struct rti_wdt_device *wdt = platform_get_drvdata(pdev);362 363	watchdog_unregister_device(&wdt->wdd);364 365	if (!pm_runtime_suspended(&pdev->dev))366		pm_runtime_put(&pdev->dev);367 368	pm_runtime_disable(&pdev->dev);369}370 371static const struct of_device_id rti_wdt_of_match[] = {372	{ .compatible = "ti,j7-rti-wdt", },373	{},374};375MODULE_DEVICE_TABLE(of, rti_wdt_of_match);376 377static struct platform_driver rti_wdt_driver = {378	.driver = {379		.name = "rti-wdt",380		.of_match_table = rti_wdt_of_match,381	},382	.probe = rti_wdt_probe,383	.remove_new = rti_wdt_remove,384};385 386module_platform_driver(rti_wdt_driver);387 388MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>");389MODULE_DESCRIPTION("K3 RTI Watchdog Driver");390 391module_param(heartbeat, int, 0);392MODULE_PARM_DESC(heartbeat,393		 "Watchdog heartbeat period in seconds from 1 to "394		 __MODULE_STRING(MAX_HEARTBEAT) ", default "395		 __MODULE_STRING(DEFAULT_HEARTBEAT));396 397MODULE_LICENSE("GPL");398MODULE_ALIAS("platform:rti-wdt");399