brintos

brintos / linux-shallow public Read only

0
0
Text · 5.1 KiB · 7c380f9 Raw
206 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *	Advantech Embedded Controller Watchdog Driver4 *5 *	This driver supports Advantech products with ITE based Embedded Controller.6 *	It does not support Advantech products with other ECs or without EC.7 *8 *	Copyright (C) 2022 Advantech Europe B.V.9 */10 11#include <linux/delay.h>12#include <linux/io.h>13#include <linux/isa.h>14#include <linux/kernel.h>15#include <linux/module.h>16#include <linux/moduleparam.h>17#include <linux/watchdog.h>18 19#define DRIVER_NAME		"advantech_ec_wdt"20 21/* EC IO region */22#define EC_BASE_ADDR		0x29923#define EC_ADDR_EXTENT		224 25/* EC minimum IO access delay in ms */26#define EC_MIN_DELAY		1027 28/* EC interface definitions */29#define EC_ADDR_CMD		(EC_BASE_ADDR + 1)30#define EC_ADDR_DATA		EC_BASE_ADDR31#define EC_CMD_EC_PROBE		0x3032#define EC_CMD_COMM		0x8933#define EC_CMD_WDT_START	0x2834#define EC_CMD_WDT_STOP		0x2935#define EC_CMD_WDT_RESET	0x2A36#define EC_DAT_EN_DLY_H		0x5837#define EC_DAT_EN_DLY_L		0x5938#define EC_DAT_RST_DLY_H	0x5E39#define EC_DAT_RST_DLY_L	0x5F40#define EC_MAGIC		0x9541 42/* module parameters */43#define MIN_TIME		144#define MAX_TIME		6000 /* 100 minutes */45#define DEFAULT_TIME		6046 47static unsigned int timeout;48static ktime_t ec_timestamp;49 50module_param(timeout, uint, 0);51MODULE_PARM_DESC(timeout,52		 "Default Watchdog timer setting (" __MODULE_STRING(DEFAULT_TIME) "s). The range is from " __MODULE_STRING(MIN_TIME) " to " __MODULE_STRING(MAX_TIME) ".");53 54static void adv_ec_wdt_timing_gate(void)55{56	ktime_t time_cur, time_delta;57 58	/* ensure minimum delay between IO accesses*/59	time_cur = ktime_get();60	time_delta = ktime_to_ms(ktime_sub(time_cur, ec_timestamp));61	if (time_delta < EC_MIN_DELAY) {62		time_delta = EC_MIN_DELAY - time_delta;63		usleep_range(time_delta * 1000, (time_delta + 1) * 1000);64	}65	ec_timestamp = ktime_get();66}67 68static void adv_ec_wdt_outb(unsigned char value, unsigned short port)69{70	adv_ec_wdt_timing_gate();71	outb(value, port);72}73 74static unsigned char adv_ec_wdt_inb(unsigned short port)75{76	adv_ec_wdt_timing_gate();77	return inb(port);78}79 80static int adv_ec_wdt_ping(struct watchdog_device *wdd)81{82	adv_ec_wdt_outb(EC_CMD_WDT_RESET, EC_ADDR_CMD);83	return 0;84}85 86static int adv_ec_wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)87{88	unsigned int val;89 90	/* scale time to EC 100 ms base */91	val = t * 10;92 93	/* reset enable delay, just in case it was set by BIOS etc. */94	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);95	adv_ec_wdt_outb(EC_DAT_EN_DLY_H, EC_ADDR_DATA);96	adv_ec_wdt_outb(0, EC_ADDR_DATA);97 98	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);99	adv_ec_wdt_outb(EC_DAT_EN_DLY_L, EC_ADDR_DATA);100	adv_ec_wdt_outb(0, EC_ADDR_DATA);101 102	/* set reset delay */103	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);104	adv_ec_wdt_outb(EC_DAT_RST_DLY_H, EC_ADDR_DATA);105	adv_ec_wdt_outb(val >> 8, EC_ADDR_DATA);106 107	adv_ec_wdt_outb(EC_CMD_COMM, EC_ADDR_CMD);108	adv_ec_wdt_outb(EC_DAT_RST_DLY_L, EC_ADDR_DATA);109	adv_ec_wdt_outb(val & 0xFF, EC_ADDR_DATA);110 111	wdd->timeout = t;112	return 0;113}114 115static int adv_ec_wdt_start(struct watchdog_device *wdd)116{117	adv_ec_wdt_set_timeout(wdd, wdd->timeout);118	adv_ec_wdt_outb(EC_CMD_WDT_START, EC_ADDR_CMD);119 120	return 0;121}122 123static int adv_ec_wdt_stop(struct watchdog_device *wdd)124{125	adv_ec_wdt_outb(EC_CMD_WDT_STOP, EC_ADDR_CMD);126 127	return 0;128}129 130static const struct watchdog_info adv_ec_wdt_info = {131	.identity =	DRIVER_NAME,132	.options =	WDIOF_SETTIMEOUT |133			WDIOF_MAGICCLOSE |134			WDIOF_KEEPALIVEPING,135};136 137static const struct watchdog_ops adv_ec_wdt_ops = {138	.owner =	THIS_MODULE,139	.start =	adv_ec_wdt_start,140	.stop =		adv_ec_wdt_stop,141	.ping =		adv_ec_wdt_ping,142	.set_timeout =	adv_ec_wdt_set_timeout,143};144 145static struct watchdog_device adv_ec_wdt_dev = {146	.info =		&adv_ec_wdt_info,147	.ops =		&adv_ec_wdt_ops,148	.min_timeout =	MIN_TIME,149	.max_timeout =	MAX_TIME,150	.timeout =	DEFAULT_TIME,151};152 153static int adv_ec_wdt_probe(struct device *dev, unsigned int id)154{155	if (!devm_request_region(dev, EC_BASE_ADDR, EC_ADDR_EXTENT, dev_name(dev))) {156		dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",157			EC_BASE_ADDR, EC_BASE_ADDR + EC_ADDR_EXTENT);158		return -EBUSY;159	}160 161	watchdog_init_timeout(&adv_ec_wdt_dev, timeout, dev);162	watchdog_stop_on_reboot(&adv_ec_wdt_dev);163	watchdog_stop_on_unregister(&adv_ec_wdt_dev);164 165	return devm_watchdog_register_device(dev, &adv_ec_wdt_dev);166}167 168static struct isa_driver adv_ec_wdt_driver = {169	.probe		= adv_ec_wdt_probe,170	.driver		= {171	.name		= DRIVER_NAME,172	},173};174 175static int __init adv_ec_wdt_init(void)176{177	unsigned int val;178 179	/* quick probe for EC */180	if (!request_region(EC_BASE_ADDR, EC_ADDR_EXTENT, DRIVER_NAME))181		return -EBUSY;182 183	adv_ec_wdt_outb(EC_CMD_EC_PROBE, EC_ADDR_CMD);184	val = adv_ec_wdt_inb(EC_ADDR_DATA);185	release_region(EC_BASE_ADDR, EC_ADDR_EXTENT);186 187	if (val != EC_MAGIC)188		return -ENODEV;189 190	return isa_register_driver(&adv_ec_wdt_driver, 1);191}192 193static void __exit adv_ec_wdt_exit(void)194{195	isa_unregister_driver(&adv_ec_wdt_driver);196}197 198module_init(adv_ec_wdt_init);199module_exit(adv_ec_wdt_exit);200 201MODULE_AUTHOR("Thomas Kastner <thomas.kastner@advantech.com>");202MODULE_DESCRIPTION("Advantech Embedded Controller Watchdog Device Driver");203MODULE_LICENSE("GPL");204MODULE_VERSION("20221019");205MODULE_ALIAS("isa:" DRIVER_NAME);206