watchdog: loongson1: Add Loongson1 SoC watchdog driver
Subsystems (3)
PATCH 1/2 watchdog: loongson1: Add Loongson1 SoC watchdog driver
Add watchdog timer specific driver for Loongson1 SoC.
---
V1.1:
Add a little debugging information.
--- drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/loongson1_wdt.c | 160 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/watchdog/loongson1_wdt.c
drivers/watchdog/Kconfig +7 -0
@@ -1553,6 +1553,13 @@ config PIC32_DMT To compile this driver as a loadable module, choose M here. The module will be called pic32-dmt. +config LOONGSON1_WDT + tristate "Loongson1 SoC hardware watchdog" + depends on MACH_LOONGSON32 + select WATCHDOG_CORE + help + Hardware driver for the Loongson1 SoC Watchdog Timer. + # PARISC Architecture # POWERPC Architecture
drivers/watchdog/Makefile +1 -0
@@ -162,6 +162,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o +obj-$(CONFIG_LOONGSON1_WDT) += loongson1_wdt.o # PARISC Architecture
drivers/watchdog/loongson1_wdt.c new file +160 -0
160 lines not on this page · raw message
Signature · 1 line
1.9.1
Guenter Roeck
maintainerQuoted 60 lines · Yang Ling
Add watchdog timer specific driver for Loongson1 SoC.
Signed-off-by Yang Ling <[email protected]>---
V1.1:
Add a little debugging information.--- drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/loongson1_wdt.c | 160 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/watchdog/loongson1_wdt.cdrivers/watchdog/Kconfig +7
-0@@ -1553,6 +1553,13 @@ config PIC32_DMT To compile this driver as a loadable module, choose M here. The module will be called pic32-dmt. +config LOONGSON1_WDT + tristate "Loongson1 SoC hardware watchdog" + depends on MACH_LOONGSON32 + select WATCHDOG_CORE + help + Hardware driver for the Loongson1 SoC Watchdog Timer. + # PARISC Architecture # POWERPC Architecturedrivers/watchdog/Makefile +1
-0@@ -162,6 +162,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o +obj-$(CONFIG_LOONGSON1_WDT) += loongson1_wdt.o # PARISC Architecturedrivers/watchdog/loongson1_wdt.c new file +10
-0@@ -0,0 +1,160 @@ +/* +* Copyright (c) 2016 Yang Ling <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include <linux/module.h>
drivers/watchdog/loongson1_wdt.c line 20
Quoted 10 lines
drivers/watchdog/loongson1_wdt.c new file +10
-0+#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <loongson1.h> + +#define MIN_HEARTBEAT 1 +#define MAX_HEARTBEAT 30 +#define DEFAULT_HEARTBEAT 10 +
The limits are pretty low. On many systems such low limits may result in boot failures if the watchdog is already running at boot.
It might make more sense to determine the maximum timeout dynamically based on the clock rate, and possibly use max_hw_heartbeat_ms instead of max_timeout to let the infrastructure handle small maximum timeouts.
Also, if it is possible that the watchdog is already running at boot, it might be desirable to detect this situation and inform the infrastructure about it (see WDOG_HW_RUNNING flag).
Quoted 8 lines
drivers/watchdog/loongson1_wdt.c new file +8
-0+static bool nowayout =WATCHDOG_NOWAYOUT ; +module_param (nowayout, bool,0 ); + +static unsigned int heartbeat =DEFAULT_HEARTBEAT ; +module_param (heartbeat, uint,0 ); + +struct ls1x_wdt_drvdata { +struct watchdog_device wdt;
drivers/watchdog/loongson1_wdt.c line 38
Quoted 10 lines
drivers/watchdog/loongson1_wdt.c new file +10
-0+void __iomem *base; +unsigned int count; +struct clk *clk; +}; + +static int ls1x_wdt_ping (struct watchdog_device *wdt_dev) +{ +struct ls1x_wdt_drvdata *drvdata =watchdog_get_drvdata (wdt_dev); + +writel (0x1 , drvdata->base +WDT_EN );
Does this enable the timeout ? If so, does it always have to be called ? And is it a good idea to call it prior to setting the counter ?
drivers/watchdog/loongson1_wdt.c line 39
Quoted 1 line
drivers/watchdog/loongson1_wdt.c new file +1
-0+writel (drvdata->count, drvdata->base +WDT_TIMER );
Is this needed for each ping ?
drivers/watchdog/loongson1_wdt.c line 41
Quoted 2 lines
drivers/watchdog/loongson1_wdt.c new file +2
-0+writel (0x1 , drvdata->base +WDT_SET ); +
Or is this the actual ping ?
drivers/watchdog/loongson1_wdt.c line 52
Quoted 11 lines
drivers/watchdog/loongson1_wdt.c new file +11
-0+return 0 ; +} + +static int ls1x_wdt_set_timeout (struct watchdog_device *wdt_dev, +unsigned int new_timeout) +{ +struct ls1x_wdt_drvdata *drvdata =watchdog_get_drvdata (wdt_dev); +int counts_per_second; + +if (watchdog_init_timeout (wdt_dev, new_timeout,NULL )) +return -EINVAL ;
This is supposed to be called from the init function. The set_timeout is supposed to set the timeout, not to call back the infrastructure.
drivers/watchdog/loongson1_wdt.c line 54
Quoted 2 lines
drivers/watchdog/loongson1_wdt.c new file +2
-0+ + counts_per_second =clk_get_rate (drvdata->clk);
clk_get_rate() can, at least in theory, return 0.
In general it is better to call it once in the probe function, validate it, and store the value in the local data structure.
drivers/watchdog/loongson1_wdt.c line 57
Quoted 3 lines
drivers/watchdog/loongson1_wdt.c new file +3
-0+ drvdata->count = counts_per_second * wdt_dev->timeout; + +ls1x_wdt_ping (wdt_dev);
No. This function can be called with the watchdog stopped. The infrastructure will call the ping function if needed.
drivers/watchdog/loongson1_wdt.c line 64
Quoted 7 lines
drivers/watchdog/loongson1_wdt.c new file +7
-0+ +return 0 ; +} + +static int ls1x_wdt_start (struct watchdog_device *wdt_dev) +{ +ls1x_wdt_set_timeout (wdt_dev, wdt_dev->timeout);
This function is supposed to start the watchdog, not to set the timeout.
Quoted 97 lines
drivers/watchdog/loongson1_wdt.c new file +96
-0+ +return 0 ; +} + +static int ls1x_wdt_stop (struct watchdog_device *wdt_dev) +{ +struct ls1x_wdt_drvdata *drvdata =watchdog_get_drvdata (wdt_dev); + +writel (0x0 , drvdata->base +WDT_EN ); + +return 0 ; +} + +static const struct watchdog_info ls1x_wdt_info = { + .options =WDIOF_SETTIMEOUT |WDIOF_KEEPALIVEPING |WDIOF_MAGICCLOSE , + .identity ="Loongson1 Watchdog" , +}; + +static const struct watchdog_ops ls1x_wdt_ops = { + .owner =THIS_MODULE , + .start = ls1x_wdt_start, + .stop = ls1x_wdt_stop, + .ping = ls1x_wdt_ping, + .set_timeout = ls1x_wdt_set_timeout, +}; + +static int ls1x_wdt_probe (struct platform_device *pdev) +{ +struct ls1x_wdt_drvdata *drvdata; +struct watchdog_device *ls1x_wdt; +struct resource *res; +int ret; + + drvdata =devm_kzalloc (&pdev->dev,sizeof (*drvdata),GFP_KERNEL ); +if (!drvdata) +return -ENOMEM ; + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + drvdata->base =devm_ioremap_resource (&pdev->dev, res); +if (IS_ERR (drvdata->base)) +return PTR_ERR (drvdata->base); + + drvdata->clk =devm_clk_get (&pdev->dev, pdev->name); +if (IS_ERR (drvdata->clk)) { +dev_err (&pdev->dev,"failed to get %s clock\n" , pdev->name); +return PTR_ERR (drvdata->clk); + } +clk_prepare_enable (drvdata->clk); + + ls1x_wdt = &drvdata->wdt; + ls1x_wdt->info = &ls1x_wdt_info; + ls1x_wdt->ops = &ls1x_wdt_ops; + ls1x_wdt->timeout = heartbeat; + ls1x_wdt->min_timeout =MIN_HEARTBEAT ; + ls1x_wdt->max_timeout =MAX_HEARTBEAT ; + ls1x_wdt->parent = &pdev->dev; +watchdog_set_nowayout (ls1x_wdt, nowayout); +watchdog_set_drvdata (ls1x_wdt, drvdata); + + ret =watchdog_register_device (&drvdata->wdt); +if (ret <0 ) { +dev_err (&pdev->dev,"failed to register watchdog device\n" ); +return ret; + } + +platform_set_drvdata (pdev, drvdata); + +dev_info (&pdev->dev,"Loongson1 Watchdog driver registered\n" ); + +return 0 ; +} + +static int ls1x_wdt_remove (struct platform_device *pdev) +{ +struct ls1x_wdt_drvdata *drvdata =platform_get_drvdata (pdev); + +ls1x_wdt_stop (&drvdata->wdt); +watchdog_unregister_device (&drvdata->wdt); +clk_disable_unprepare (drvdata->clk); + +return 0 ; +} + +static struct platform_driver ls1x_wdt_driver = { + .probe = ls1x_wdt_probe, + .remove = ls1x_wdt_remove, + .driver = { + .name ="ls1x-wdt" , + }, +}; + +module_platform_driver (ls1x_wdt_driver); + +MODULE_AUTHOR ("Yang Ling <[email protected]>" ); +MODULE_DESCRIPTION ("Loongson1 Watchdog Driver" ); +MODULE_LICENSE ("GPL" );
Quoted 241 lines · Guenter Roeck
Quoted 60 lines · Yang Ling
Add watchdog timer specific driver for Loongson1 SoC.
Signed-off-by Yang Ling <[email protected]>---
V1.1:
Add a little debugging information.
---drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/loongson1_wdt.c | 160 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+)create mode 100644
drivers/watchdog/loongson1_wdt.cdrivers/watchdog/Kconfig +7
-0@@ -1553,6 +1553,13 @@ config PIC32_DMT To compile this driver as a loadable module, choose M here. The module will be called pic32-dmt. +config LOONGSON1_WDT + tristate "Loongson1 SoC hardware watchdog" + depends on MACH_LOONGSON32 + select WATCHDOG_CORE + help + Hardware driver for the Loongson1 SoC Watchdog Timer. + # PARISC Architecture # POWERPC Architecturedrivers/watchdog/Makefile +0
-0@@ -162,6 +162,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.oobj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_LOONGSON1_WDT) += loongson1_wdt.o# PARISC Architecture
drivers/watchdog/loongson1_wdt.c new file +10
-0@@ -0,0 +1,160 @@ +/* +* Copyright (c) 2016 Yang Ling <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +*/ + +#include <linux/module.h> drivers/watchdog/loongson1_wdt.c line 20
Quoted 10 lines
drivers/watchdog/loongson1_wdt.c new file +10
-0+#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <loongson1.h> + +#define MIN_HEARTBEAT 1 +#define MAX_HEARTBEAT 30 +#define DEFAULT_HEARTBEAT 10 +The limits are pretty low. On many systems such low limits may result in boot failures if the watchdog is already running at boot.
It might make more sense to determine the maximum timeout dynamically based on the clock rate, and possibly use
max_hw_heartbeat_msinstead ofmax_timeoutto let the infrastructure handle small maximum timeouts.Also, if it is possible that the watchdog is already running at boot, it might be desirable to detect this situation and inform the infrastructure about it (see
WDOG_HW_RUNNINGflag).Quoted 8 lines
drivers/watchdog/loongson1_wdt.c new file +8
-0+static bool nowayout =WATCHDOG_NOWAYOUT ; +module_param (nowayout, bool,0 ); + +static unsigned int heartbeat =DEFAULT_HEARTBEAT ; +module_param (heartbeat, uint,0 ); + +struct ls1x_wdt_drvdata { +struct watchdog_device wdt;drivers/watchdog/loongson1_wdt.c line 38
Quoted 10 lines
drivers/watchdog/loongson1_wdt.c new file +10
-0+void __iomem *base; +unsigned int count; +struct clk *clk; +}; + +static int ls1x_wdt_ping (struct watchdog_device *wdt_dev) +{ +struct ls1x_wdt_drvdata *drvdata =watchdog_get_drvdata (wdt_dev); + +writel (0x1 , drvdata->base +WDT_EN );Does this enable the timeout ? If so, does it always have to be called ? And is it a good idea to call it prior to setting the counter ?
drivers/watchdog/loongson1_wdt.c line 39
Quoted 1 line
drivers/watchdog/loongson1_wdt.c new file +1
-0+writel (drvdata->count, drvdata->base +WDT_TIMER );Is this needed for each ping ?
drivers/watchdog/loongson1_wdt.c line 41
Quoted 2 lines
drivers/watchdog/loongson1_wdt.c new file +2
-0+writel (0x1 , drvdata->base +WDT_SET ); +Or is this the actual ping ?
drivers/watchdog/loongson1_wdt.c line 52
Quoted 11 lines
drivers/watchdog/loongson1_wdt.c new file +11
-0+return 0 ; +} + +static int ls1x_wdt_set_timeout (struct watchdog_device *wdt_dev, +unsigned int new_timeout) +{ +struct ls1x_wdt_drvdata *drvdata =watchdog_get_drvdata (wdt_dev); +int counts_per_second; + +if (watchdog_init_timeout (wdt_dev, new_timeout,NULL )) +return -EINVAL ;This is supposed to be called from the init function. The
set_timeoutis supposed to set the timeout, not to call back the infrastructure.drivers/watchdog/loongson1_wdt.c line 54
Quoted 2 lines
drivers/watchdog/loongson1_wdt.c new file +2
-0+ + counts_per_second =clk_get_rate (drvdata->clk);
clk_get_rate()can, at least in theory, return 0.
In general it is better to call it once in the probe function, validate it, and store the value in the local data structure.drivers/watchdog/loongson1_wdt.c line 57
Quoted 3 lines
drivers/watchdog/loongson1_wdt.c new file +3
-0+ drvdata->count = counts_per_second * wdt_dev->timeout; + +ls1x_wdt_ping (wdt_dev);No. This function can be called with the watchdog stopped. The infrastructure will call the ping function if needed.
drivers/watchdog/loongson1_wdt.c line 64
Quoted 7 lines
drivers/watchdog/loongson1_wdt.c new file +7
-0+ +return 0 ; +} + +static int ls1x_wdt_start (struct watchdog_device *wdt_dev) +{ +ls1x_wdt_set_timeout (wdt_dev, wdt_dev->timeout);This function is supposed to start the watchdog, not to set the timeout.
Quoted 89 lines
drivers/watchdog/loongson1_wdt.c new file +89
-0+ +return 0 ; +} + +static int ls1x_wdt_stop (struct watchdog_device *wdt_dev) +{ +struct ls1x_wdt_drvdata *drvdata =watchdog_get_drvdata (wdt_dev); + +writel (0x0 , drvdata->base +WDT_EN ); + +return 0 ; +} + +static const struct watchdog_info ls1x_wdt_info = { + .options =WDIOF_SETTIMEOUT |WDIOF_KEEPALIVEPING |WDIOF_MAGICCLOSE , + .identity ="Loongson1 Watchdog" , +}; + +static const struct watchdog_ops ls1x_wdt_ops = { + .owner =THIS_MODULE , + .start = ls1x_wdt_start, + .stop = ls1x_wdt_stop, + .ping = ls1x_wdt_ping, + .set_timeout = ls1x_wdt_set_timeout, +}; + +static int ls1x_wdt_probe (struct platform_device *pdev) +{ +struct ls1x_wdt_drvdata *drvdata; +struct watchdog_device *ls1x_wdt; +struct resource *res; +int ret; + + drvdata =devm_kzalloc (&pdev->dev,sizeof (*drvdata),GFP_KERNEL ); +if (!drvdata) +return -ENOMEM ; + + res =platform_get_resource (pdev,IORESOURCE_MEM ,0 ); + drvdata->base =devm_ioremap_resource (&pdev->dev, res); +if (IS_ERR (drvdata->base)) +return PTR_ERR (drvdata->base); + + drvdata->clk =devm_clk_get (&pdev->dev, pdev->name); +if (IS_ERR (drvdata->clk)) { +dev_err (&pdev->dev,"failed to get %s clock\n" , pdev->name); +return PTR_ERR (drvdata->clk); + } +clk_prepare_enable (drvdata->clk); + + ls1x_wdt = &drvdata->wdt; + ls1x_wdt->info = &ls1x_wdt_info; + ls1x_wdt->ops = &ls1x_wdt_ops; + ls1x_wdt->timeout = heartbeat; + ls1x_wdt->min_timeout =MIN_HEARTBEAT ; + ls1x_wdt->max_timeout =MAX_HEARTBEAT ; + ls1x_wdt->parent = &pdev->dev; +watchdog_set_nowayout (ls1x_wdt, nowayout); +watchdog_set_drvdata (ls1x_wdt, drvdata); + + ret =watchdog_register_device (&drvdata->wdt); +if (ret <0 ) { +dev_err (&pdev->dev,"failed to register watchdog device\n" ); +return ret; + } + +platform_set_drvdata (pdev, drvdata); + +dev_info (&pdev->dev,"Loongson1 Watchdog driver registered\n" ); + +return 0 ; +} + +static int ls1x_wdt_remove (struct platform_device *pdev) +{ +struct ls1x_wdt_drvdata *drvdata =platform_get_drvdata (pdev); + +ls1x_wdt_stop (&drvdata->wdt); +watchdog_unregister_device (&drvdata->wdt); +clk_disable_unprepare (drvdata->clk); + +return 0 ; +} + +static struct platform_driver ls1x_wdt_driver = { + .probe = ls1x_wdt_probe, + .remove = ls1x_wdt_remove, + .driver = { + .name ="ls1x-wdt" , + },
Quoted 9 lines
drivers/watchdog/loongson1_wdt.c line 160
Quoted 8 lines
drivers/watchdog/loongson1_wdt.c new file +7
-0+}; + +module_platform_driver (ls1x_wdt_driver); + +MODULE_AUTHOR ("Yang Ling <[email protected]>" ); +MODULE_DESCRIPTION ("Loongson1 Watchdog Driver" ); +MODULE_LICENSE ("GPL" );
I will improve these places in the next version.
Thanks for your friendly reminder.
Guenter Roeck
maintainerQuoted 59 lines · Yang Ling
Quoted 58 lines · Guenter Roeck
Quoted 57 lines · Yang Ling
Add watchdog timer specific driver for Loongson1 SoC.
Signed-off-by Yang Ling <[email protected]>---
V1.1:
Add a little debugging information.
---drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/loongson1_wdt.c | 160 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+)create mode 100644
drivers/watchdog/loongson1_wdt.cdrivers/watchdog/Kconfig +7
-0@@ -1553,6 +1553,13 @@ config PIC32_DMT To compile this driver as a loadable module, choose M here. The module will be called pic32-dmt. +config LOONGSON1_WDT + tristate "Loongson1 SoC hardware watchdog" + depends on MACH_LOONGSON32 + select WATCHDOG_CORE + help + Hardware driver for the Loongson1 SoC Watchdog Timer. + # PARISC Architecture # POWERPC Architecturedrivers/watchdog/Makefile +0
-0@@ -162,6 +162,7 @@ obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.oobj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o
obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o
obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o
+obj-$(CONFIG_LOONGSON1_WDT) += loongson1_wdt.o# PARISC Architecture
drivers/watchdog/loongson1_wdt.c new file +7
-0@@ -0,0 +1,160 @@ +/* +* Copyright (c) 2016 Yang Ling <[email protected]> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version.
Quoted 8 lines
Quoted 8 lines
drivers/watchdog/loongson1_wdt.c line 15
Quoted 8 lines
drivers/watchdog/loongson1_wdt.c new file +8
-0+*/ + +#include <linux/module.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <loongson1.h>
While you are at it, please order include files alphabetically.
Thanks,
Guenter