watchdog: f71808e_wdt: dynamically allocate watchdog driver data

While the driver will only match against a single device, convention is
to dynamically allocate the driver data.

Suggested-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/1baffdc410d6476cccffe9712cec56350335812a.1628525954.git-series.a.fatoum@pengutronix.de
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>
This commit is contained in:
Ahmad Fatoum 2021-08-09 18:20:37 +02:00 коммит произвёл Wim Van Sebroeck
Родитель 27e0fe00a5
Коммит a7876735f2
1 изменённых файлов: 111 добавлений и 87 удалений

Просмотреть файл

@ -142,7 +142,9 @@ struct fintek_wdt {
char pulse_mode; /* enable pulse output mode? */ char pulse_mode; /* enable pulse output mode? */
}; };
static struct fintek_wdt watchdog; struct fintek_wdt_pdata {
enum chips type;
};
/* Super I/O functions */ /* Super I/O functions */
static inline int superio_inb(int base, int reg) static inline int superio_inb(int base, int reg)
@ -208,13 +210,15 @@ static inline void superio_exit(int base)
static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
{ {
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
if (timeout > 0xff) { if (timeout > 0xff) {
watchdog.timer_val = DIV_ROUND_UP(timeout, 60); wd->timer_val = DIV_ROUND_UP(timeout, 60);
watchdog.minutes_mode = true; wd->minutes_mode = true;
timeout = watchdog.timer_val * 60; timeout = wd->timer_val * 60;
} else { } else {
watchdog.timer_val = timeout; wd->timer_val = timeout;
watchdog.minutes_mode = false; wd->minutes_mode = false;
} }
wdd->timeout = timeout; wdd->timeout = timeout;
@ -222,63 +226,65 @@ static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int time
return 0; return 0;
} }
static int fintek_wdt_set_pulse_width(unsigned int pw) static int fintek_wdt_set_pulse_width(struct fintek_wdt *wd, unsigned int pw)
{ {
unsigned int t1 = 25, t2 = 125, t3 = 5000; unsigned int t1 = 25, t2 = 125, t3 = 5000;
if (watchdog.type == f71868) { if (wd->type == f71868) {
t1 = 30; t1 = 30;
t2 = 150; t2 = 150;
t3 = 6000; t3 = 6000;
} }
if (pw <= 1) { if (pw <= 1) {
watchdog.pulse_val = 0; wd->pulse_val = 0;
} else if (pw <= t1) { } else if (pw <= t1) {
watchdog.pulse_val = 1; wd->pulse_val = 1;
} else if (pw <= t2) { } else if (pw <= t2) {
watchdog.pulse_val = 2; wd->pulse_val = 2;
} else if (pw <= t3) { } else if (pw <= t3) {
watchdog.pulse_val = 3; wd->pulse_val = 3;
} else { } else {
pr_err("pulse width out of range\n"); pr_err("pulse width out of range\n");
return -EINVAL; return -EINVAL;
} }
watchdog.pulse_mode = pw; wd->pulse_mode = pw;
return 0; return 0;
} }
static int fintek_wdt_keepalive(struct watchdog_device *wdd) static int fintek_wdt_keepalive(struct watchdog_device *wdd)
{ {
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
int err; int err;
err = superio_enter(watchdog.sioaddr); err = superio_enter(wd->sioaddr);
if (err) if (err)
return err; return err;
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
if (watchdog.minutes_mode) if (wd->minutes_mode)
/* select minutes for timer units */ /* select minutes for timer units */
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_UNIT); F71808FG_FLAG_WD_UNIT);
else else
/* select seconds for timer units */ /* select seconds for timer units */
superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_UNIT); F71808FG_FLAG_WD_UNIT);
/* Set timer value */ /* Set timer value */
superio_outb(watchdog.sioaddr, F71808FG_REG_WD_TIME, superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME,
watchdog.timer_val); wd->timer_val);
superio_exit(watchdog.sioaddr); superio_exit(wd->sioaddr);
return 0; return 0;
} }
static int fintek_wdt_start(struct watchdog_device *wdd) static int fintek_wdt_start(struct watchdog_device *wdd)
{ {
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
int err; int err;
u8 tmp; u8 tmp;
@ -287,57 +293,57 @@ static int fintek_wdt_start(struct watchdog_device *wdd)
if (err) if (err)
return err; return err;
err = superio_enter(watchdog.sioaddr); err = superio_enter(wd->sioaddr);
if (err) if (err)
return err; return err;
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
/* Watchdog pin configuration */ /* Watchdog pin configuration */
switch (watchdog.type) { switch (wd->type) {
case f71808fg: case f71808fg:
/* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */ /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT2, 3); superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3);
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 3); superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3);
break; break;
case f71862fg: case f71862fg:
if (f71862fg_pin == 63) { if (f71862fg_pin == 63) {
/* SPI must be disabled first to use this pin! */ /* SPI must be disabled first to use this pin! */
superio_clear_bit(watchdog.sioaddr, SIO_REG_ROM_ADDR_SEL, 6); superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6);
superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 4); superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4);
} else if (f71862fg_pin == 56) { } else if (f71862fg_pin == 56) {
superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
} }
break; break;
case f71868: case f71868:
case f71869: case f71869:
/* GPIO14 --> WDTRST# */ /* GPIO14 --> WDTRST# */
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 4); superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4);
break; break;
case f71882fg: case f71882fg:
/* Set pin 56 to WDTRST# */ /* Set pin 56 to WDTRST# */
superio_set_bit(watchdog.sioaddr, SIO_REG_MFUNCT1, 1); superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
break; break;
case f71889fg: case f71889fg:
/* set pin 40 to WDTRST# */ /* set pin 40 to WDTRST# */
superio_outb(watchdog.sioaddr, SIO_REG_MFUNCT3, superio_outb(wd->sioaddr, SIO_REG_MFUNCT3,
superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf); superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf);
break; break;
case f81803: case f81803:
/* Enable TSI Level register bank */ /* Enable TSI Level register bank */
superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3); superio_clear_bit(wd->sioaddr, SIO_REG_CLOCK_SEL, 3);
/* Set pin 27 to WDTRST# */ /* Set pin 27 to WDTRST# */
superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f & superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL)); superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL));
break; break;
case f81865: case f81865:
/* Set pin 70 to WDTRST# */ /* Set pin 70 to WDTRST# */
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5); superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5);
break; break;
case f81866: case f81866:
@ -347,12 +353,12 @@ static int fintek_wdt_start(struct watchdog_device *wdd)
* BIT5: 0 -> WDTRST# * BIT5: 0 -> WDTRST#
* 1 -> GPIO15 * 1 -> GPIO15
*/ */
tmp = superio_inb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL); tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL);
tmp &= ~(BIT(3) | BIT(0)); tmp &= ~(BIT(3) | BIT(0));
tmp |= BIT(2); tmp |= BIT(2);
superio_outb(watchdog.sioaddr, SIO_F81866_REG_PORT_SEL, tmp); superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
superio_clear_bit(watchdog.sioaddr, SIO_F81866_REG_GPIO1, 5); superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5);
break; break;
default: default:
@ -364,63 +370,64 @@ static int fintek_wdt_start(struct watchdog_device *wdd)
goto exit_superio; goto exit_superio;
} }
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
superio_set_bit(watchdog.sioaddr, SIO_REG_ENABLE, 0); superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0);
if (watchdog.type == f81865 || watchdog.type == f81866) if (wd->type == f81865 || wd->type == f81866)
superio_set_bit(watchdog.sioaddr, F81865_REG_WDO_CONF, superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF,
F81865_FLAG_WDOUT_EN); F81865_FLAG_WDOUT_EN);
else else
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDO_CONF, superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF,
F71808FG_FLAG_WDOUT_EN); F71808FG_FLAG_WDOUT_EN);
superio_set_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_EN); F71808FG_FLAG_WD_EN);
if (watchdog.pulse_mode) { if (wd->pulse_mode) {
/* Select "pulse" output mode with given duration */ /* Select "pulse" output mode with given duration */
u8 wdt_conf = superio_inb(watchdog.sioaddr, u8 wdt_conf = superio_inb(wd->sioaddr,
F71808FG_REG_WDT_CONF); F71808FG_REG_WDT_CONF);
/* Set WD_PSWIDTH bits (1:0) */ /* Set WD_PSWIDTH bits (1:0) */
wdt_conf = (wdt_conf & 0xfc) | (watchdog.pulse_val & 0x03); wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_val & 0x03);
/* Set WD_PULSE to "pulse" mode */ /* Set WD_PULSE to "pulse" mode */
wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE); wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
superio_outb(watchdog.sioaddr, F71808FG_REG_WDT_CONF, superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF,
wdt_conf); wdt_conf);
} else { } else {
/* Select "level" output mode */ /* Select "level" output mode */
superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_PULSE); F71808FG_FLAG_WD_PULSE);
} }
exit_superio: exit_superio:
superio_exit(watchdog.sioaddr); superio_exit(wd->sioaddr);
return err; return err;
} }
static int fintek_wdt_stop(struct watchdog_device *wdd) static int fintek_wdt_stop(struct watchdog_device *wdd)
{ {
struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
int err; int err;
err = superio_enter(watchdog.sioaddr); err = superio_enter(wd->sioaddr);
if (err) if (err)
return err; return err;
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
superio_clear_bit(watchdog.sioaddr, F71808FG_REG_WDT_CONF, superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
F71808FG_FLAG_WD_EN); F71808FG_FLAG_WD_EN);
superio_exit(watchdog.sioaddr); superio_exit(wd->sioaddr);
return 0; return 0;
} }
static bool fintek_wdt_is_running(u8 wdt_conf) static bool fintek_wdt_is_running(struct fintek_wdt *wd, u8 wdt_conf)
{ {
return (superio_inb(watchdog.sioaddr, SIO_REG_ENABLE) & BIT(0)) return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0))
&& (wdt_conf & BIT(F71808FG_FLAG_WD_EN)); && (wdt_conf & BIT(F71808FG_FLAG_WD_EN));
} }
@ -435,7 +442,9 @@ static const struct watchdog_ops fintek_wdt_ops = {
static int fintek_wdt_probe(struct platform_device *pdev) static int fintek_wdt_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct fintek_wdt_pdata *pdata;
struct watchdog_device *wdd; struct watchdog_device *wdd;
struct fintek_wdt *wd;
int wdt_conf, err = 0; int wdt_conf, err = 0;
struct resource *res; struct resource *res;
int sioaddr; int sioaddr;
@ -446,20 +455,27 @@ static int fintek_wdt_probe(struct platform_device *pdev)
sioaddr = res->start; sioaddr = res->start;
watchdog.sioaddr = sioaddr; wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL);
watchdog.ident.options = WDIOF_SETTIMEOUT if (!wd)
| WDIOF_MAGICCLOSE return -ENOMEM;
| WDIOF_KEEPALIVEPING
| WDIOF_CARDRESET;
snprintf(watchdog.ident.identity, pdata = dev->platform_data;
sizeof(watchdog.ident.identity), "%s watchdog",
fintek_wdt_names[watchdog.type]); wd->type = pdata->type;
wd->sioaddr = sioaddr;
wd->ident.options = WDIOF_SETTIMEOUT
| WDIOF_MAGICCLOSE
| WDIOF_KEEPALIVEPING
| WDIOF_CARDRESET;
snprintf(wd->ident.identity,
sizeof(wd->ident.identity), "%s watchdog",
fintek_wdt_names[wd->type]);
err = superio_enter(sioaddr); err = superio_enter(sioaddr);
if (err) if (err)
return err; return err;
superio_select(watchdog.sioaddr, SIO_F71808FG_LD_WDT); superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF); wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
@ -470,19 +486,20 @@ static int fintek_wdt_probe(struct platform_device *pdev)
superio_outb(sioaddr, F71808FG_REG_WDT_CONF, superio_outb(sioaddr, F71808FG_REG_WDT_CONF,
wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS)); wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS));
wdd = &watchdog.wdd; wdd = &wd->wdd;
if (fintek_wdt_is_running(wdt_conf)) if (fintek_wdt_is_running(wd, wdt_conf))
set_bit(WDOG_HW_RUNNING, &wdd->status); set_bit(WDOG_HW_RUNNING, &wdd->status);
superio_exit(sioaddr); superio_exit(sioaddr);
wdd->parent = dev; wdd->parent = dev;
wdd->info = &watchdog.ident; wdd->info = &wd->ident;
wdd->ops = &fintek_wdt_ops; wdd->ops = &fintek_wdt_ops;
wdd->min_timeout = 1; wdd->min_timeout = 1;
wdd->max_timeout = WATCHDOG_MAX_TIMEOUT; wdd->max_timeout = WATCHDOG_MAX_TIMEOUT;
watchdog_set_drvdata(wdd, wd);
watchdog_set_nowayout(wdd, nowayout); watchdog_set_nowayout(wdd, nowayout);
watchdog_stop_on_unregister(wdd); watchdog_stop_on_unregister(wdd);
watchdog_stop_on_reboot(wdd); watchdog_stop_on_reboot(wdd);
@ -497,7 +514,7 @@ static int fintek_wdt_probe(struct platform_device *pdev)
* unconditionally. * unconditionally.
*/ */
fintek_wdt_set_timeout(wdd, wdd->timeout); fintek_wdt_set_timeout(wdd, wdd->timeout);
fintek_wdt_set_pulse_width(pulse_width); fintek_wdt_set_pulse_width(wd, pulse_width);
if (start_withtimeout) { if (start_withtimeout) {
err = fintek_wdt_start(wdd); err = fintek_wdt_start(wdd);
@ -516,6 +533,7 @@ static int fintek_wdt_probe(struct platform_device *pdev)
static int __init fintek_wdt_find(int sioaddr) static int __init fintek_wdt_find(int sioaddr)
{ {
enum chips type;
u16 devid; u16 devid;
int err = superio_enter(sioaddr); int err = superio_enter(sioaddr);
if (err) if (err)
@ -531,36 +549,36 @@ static int __init fintek_wdt_find(int sioaddr)
devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
switch (devid) { switch (devid) {
case SIO_F71808_ID: case SIO_F71808_ID:
watchdog.type = f71808fg; type = f71808fg;
break; break;
case SIO_F71862_ID: case SIO_F71862_ID:
watchdog.type = f71862fg; type = f71862fg;
break; break;
case SIO_F71868_ID: case SIO_F71868_ID:
watchdog.type = f71868; type = f71868;
break; break;
case SIO_F71869_ID: case SIO_F71869_ID:
case SIO_F71869A_ID: case SIO_F71869A_ID:
watchdog.type = f71869; type = f71869;
break; break;
case SIO_F71882_ID: case SIO_F71882_ID:
watchdog.type = f71882fg; type = f71882fg;
break; break;
case SIO_F71889_ID: case SIO_F71889_ID:
watchdog.type = f71889fg; type = f71889fg;
break; break;
case SIO_F71858_ID: case SIO_F71858_ID:
/* Confirmed (by datasheet) not to have a watchdog. */ /* Confirmed (by datasheet) not to have a watchdog. */
err = -ENODEV; err = -ENODEV;
goto exit; goto exit;
case SIO_F81803_ID: case SIO_F81803_ID:
watchdog.type = f81803; type = f81803;
break; break;
case SIO_F81865_ID: case SIO_F81865_ID:
watchdog.type = f81865; type = f81865;
break; break;
case SIO_F81866_ID: case SIO_F81866_ID:
watchdog.type = f81866; type = f81866;
break; break;
default: default:
pr_info("Unrecognized Fintek device: %04x\n", pr_info("Unrecognized Fintek device: %04x\n",
@ -570,11 +588,12 @@ static int __init fintek_wdt_find(int sioaddr)
} }
pr_info("Found %s watchdog chip, revision %d\n", pr_info("Found %s watchdog chip, revision %d\n",
fintek_wdt_names[watchdog.type], fintek_wdt_names[type],
(int)superio_inb(sioaddr, SIO_REG_DEVREV)); (int)superio_inb(sioaddr, SIO_REG_DEVREV));
exit: exit:
superio_exit(sioaddr); superio_exit(sioaddr);
return err; return err ? err : type;
} }
static struct platform_driver fintek_wdt_driver = { static struct platform_driver fintek_wdt_driver = {
@ -589,8 +608,9 @@ static struct platform_device *fintek_wdt_pdev;
static int __init fintek_wdt_init(void) static int __init fintek_wdt_init(void)
{ {
static const unsigned short addrs[] = { 0x2e, 0x4e }; static const unsigned short addrs[] = { 0x2e, 0x4e };
struct fintek_wdt_pdata pdata;
struct resource wdt_res = {}; struct resource wdt_res = {};
int err = -ENODEV; int ret;
int i; int i;
if (f71862fg_pin != 63 && f71862fg_pin != 56) { if (f71862fg_pin != 63 && f71862fg_pin != 56) {
@ -599,12 +619,14 @@ static int __init fintek_wdt_init(void)
} }
for (i = 0; i < ARRAY_SIZE(addrs); i++) { for (i = 0; i < ARRAY_SIZE(addrs); i++) {
err = fintek_wdt_find(addrs[i]); ret = fintek_wdt_find(addrs[i]);
if (err == 0) if (ret >= 0)
break; break;
} }
if (i == ARRAY_SIZE(addrs)) if (i == ARRAY_SIZE(addrs))
return err; return ret;
pdata.type = ret;
platform_driver_register(&fintek_wdt_driver); platform_driver_register(&fintek_wdt_driver);
@ -613,7 +635,9 @@ static int __init fintek_wdt_init(void)
wdt_res.start = addrs[i]; wdt_res.start = addrs[i];
wdt_res.end = addrs[i] + 1; wdt_res.end = addrs[i] + 1;
fintek_wdt_pdev = platform_device_register_simple(DRVNAME, -1, &wdt_res, 1); fintek_wdt_pdev = platform_device_register_resndata(NULL, DRVNAME, -1,
&wdt_res, 1,
&pdata, sizeof(pdata));
if (IS_ERR(fintek_wdt_pdev)) { if (IS_ERR(fintek_wdt_pdev)) {
platform_driver_unregister(&fintek_wdt_driver); platform_driver_unregister(&fintek_wdt_driver);
return PTR_ERR(fintek_wdt_pdev); return PTR_ERR(fintek_wdt_pdev);