mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
linux-watchdog 5.4-rc1 tag
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iEYEABECAAYFAl2NtDQACgkQ+iyteGJfRsoHgQCdGLeQMm4IR3jsDdFQk/eTIGfR eNIAoN8AY1UTFvWJTxEOhucdAzEAVnHs =TdeQ -----END PGP SIGNATURE----- Merge tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog Pull watchdog updates from Wim Van Sebroeck: - addition of AST2600, i.MX7ULP and F81803 watchdog support - removal of the w90x900 and ks8695 drivers - ziirave_wdt improvements - small fixes and improvements * tag 'linux-watchdog-5.4-rc1' of git://www.linux-watchdog.org/linux-watchdog: (51 commits) watchdog: f71808e_wdt: Add F81803 support watchdog: qcom: remove unnecessary variable from private storage watchdog: qcom: support pre-timeout when the bark irq is available watchdog: imx_sc: this patch just fixes whitespaces watchdog: apseed: Add access_cs0 option for alt-boot watchdog: aspeed: add support for dual boot watchdog: orion_wdt: use timer1 as a pretimeout watchdog: Add i.MX7ULP watchdog support dt-bindings: watchdog: Add i.MX7ULP bindings dt-bindings: watchdog: sun4i: Add the watchdog clock dt-bindings: watchdog: sun4i: Add the watchdog interrupts dt-bindings: watchdog: Convert Allwinner watchdog to a schema dt-bindings: watchdog: Add YAML schemas for the generic watchdog bindings watchdog: aspeed: Add support for AST2600 dt-bindings: watchdog: Add ast2600 compatible watchdog: ziirave_wdt: Update checked I2C functionality mask watchdog: ziirave_wdt: Drop ziirave_firm_write_block_data() watchdog: ziirave_wdt: Fix DOWNLOAD_START payload watchdog: ziirave_wdt: Drop status polling code watchdog: ziirave_wdt: Fix RESET_PROCESSOR payload ...
This commit is contained in:
commit
7bccb9f10c
@ -72,3 +72,37 @@ Description:
|
||||
It is a read/write file. When read, the currently assigned
|
||||
pretimeout governor is returned. When written, it sets
|
||||
the pretimeout governor.
|
||||
|
||||
What: /sys/class/watchdog/watchdog1/access_cs0
|
||||
Date: August 2019
|
||||
Contact: Ivan Mikhaylov <i.mikhaylov@yadro.com>,
|
||||
Alexander Amelkin <a.amelkin@yadro.com>
|
||||
Description:
|
||||
It is a read/write file. This attribute exists only if the
|
||||
system has booted from the alternate flash chip due to
|
||||
expiration of a watchdog timer of AST2400/AST2500 when
|
||||
alternate boot function was enabled with 'aspeed,alt-boot'
|
||||
devicetree option for that watchdog or with an appropriate
|
||||
h/w strapping (for WDT2 only).
|
||||
|
||||
At alternate flash the 'access_cs0' sysfs node provides:
|
||||
ast2400: a way to get access to the primary SPI flash
|
||||
chip at CS0 after booting from the alternate
|
||||
chip at CS1.
|
||||
ast2500: a way to restore the normal address mapping
|
||||
from (CS0->CS1, CS1->CS0) to (CS0->CS0,
|
||||
CS1->CS1).
|
||||
|
||||
Clearing the boot code selection and timeout counter also
|
||||
resets to the initial state the chip select line mapping. When
|
||||
the SoC is in normal mapping state (i.e. booted from CS0),
|
||||
clearing those bits does nothing for both versions of the SoC.
|
||||
For alternate boot mode (booted from CS1 due to wdt2
|
||||
expiration) the behavior differs as described above.
|
||||
|
||||
This option can be used with wdt2 (watchdog1) only.
|
||||
|
||||
When read, the current status of the boot code selection is
|
||||
shown. When written with any non-zero value, it clears
|
||||
the boot code selection and the timeout counter, which results
|
||||
in chipselect reset for AST2400/AST2500.
|
||||
|
@ -0,0 +1,58 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/allwinner,sun4i-a10-wdt.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Allwinner A10 Watchdog Device Tree Bindings
|
||||
|
||||
allOf:
|
||||
- $ref: "watchdog.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Chen-Yu Tsai <wens@csie.org>
|
||||
- Maxime Ripard <maxime.ripard@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: allwinner,sun4i-a10-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,sun50i-a64-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,sun50i-h6-wdt
|
||||
- const: allwinner,sun6i-a31-wdt
|
||||
- items:
|
||||
- const: allwinner,suniv-f1c100s-wdt
|
||||
- const: allwinner,sun4i-a10-wdt
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
wdt: watchdog@1c20c90 {
|
||||
compatible = "allwinner,sun4i-a10-wdt";
|
||||
reg = <0x01c20c90 0x10>;
|
||||
interrupts = <24>;
|
||||
clocks = <&osc24M>;
|
||||
timeout-sec = <10>;
|
||||
};
|
||||
|
||||
...
|
@ -4,6 +4,7 @@ Required properties:
|
||||
- compatible: must be one of:
|
||||
- "aspeed,ast2400-wdt"
|
||||
- "aspeed,ast2500-wdt"
|
||||
- "aspeed,ast2600-wdt"
|
||||
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region
|
||||
|
@ -0,0 +1,22 @@
|
||||
* Freescale i.MX7ULP Watchdog Timer (WDT) Controller
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be "fsl,imx7ulp-wdt"
|
||||
- reg : Should contain WDT registers location and length
|
||||
- interrupts : Should contain WDT interrupt
|
||||
- clocks: Should contain a phandle pointing to the gated peripheral clock.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec : Contains the watchdog timeout in seconds
|
||||
|
||||
Examples:
|
||||
|
||||
wdog1: watchdog@403d0000 {
|
||||
compatible = "fsl,imx7ulp-wdt";
|
||||
reg = <0x403d0000 0x10000>;
|
||||
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&pcc2 IMX7ULP_CLK_WDG1>;
|
||||
assigned-clocks = <&pcc2 IMX7ULP_CLK_WDG1>;
|
||||
assigned-clocks-parents = <&scg1 IMX7ULP_CLK_FIRC_BUS_CLK>;
|
||||
timeout-sec = <40>;
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
Allwinner SoCs Watchdog timer
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : should be one of
|
||||
"allwinner,sun4i-a10-wdt"
|
||||
"allwinner,sun6i-a31-wdt"
|
||||
"allwinner,sun50i-a64-wdt","allwinner,sun6i-a31-wdt"
|
||||
"allwinner,sun50i-h6-wdt","allwinner,sun6i-a31-wdt"
|
||||
"allwinner,suniv-f1c100s-wdt", "allwinner,sun4i-a10-wdt"
|
||||
- reg : Specifies base physical address and size of the registers.
|
||||
|
||||
Optional properties:
|
||||
- timeout-sec : Contains the watchdog timeout in seconds
|
||||
|
||||
Example:
|
||||
|
||||
wdt: watchdog@1c20c90 {
|
||||
compatible = "allwinner,sun4i-a10-wdt";
|
||||
reg = <0x01c20c90 0x10>;
|
||||
timeout-sec = <10>;
|
||||
};
|
26
Documentation/devicetree/bindings/watchdog/watchdog.yaml
Normal file
26
Documentation/devicetree/bindings/watchdog/watchdog.yaml
Normal file
@ -0,0 +1,26 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/watchdog/watchdog.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Watchdog Generic Bindings
|
||||
|
||||
maintainers:
|
||||
- Guenter Roeck <linux@roeck-us.net>
|
||||
- Wim Van Sebroeck <wim@linux-watchdog.org>
|
||||
|
||||
description: |
|
||||
This document describes generic bindings which can be used to
|
||||
describe watchdog devices in a device tree.
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: "^watchdog(@.*|-[0-9a-f])?$"
|
||||
|
||||
timeout-sec:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Contains the watchdog timeout in seconds.
|
||||
|
||||
...
|
@ -301,15 +301,6 @@ ixp4xx_wdt:
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
ks8695_wdt:
|
||||
wdt_time:
|
||||
Watchdog time in seconds. (default=5)
|
||||
nowayout:
|
||||
Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
machzwd:
|
||||
nowayout:
|
||||
Watchdog cannot be stopped once started
|
||||
@ -375,16 +366,6 @@ nic7018_wdt:
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
nuc900_wdt:
|
||||
heartbeat:
|
||||
Watchdog heartbeats in seconds.
|
||||
(default = 15)
|
||||
nowayout:
|
||||
Watchdog cannot be stopped once started
|
||||
(default=kernel config parameter)
|
||||
|
||||
-------------------------------------------------
|
||||
|
||||
omap_wdt:
|
||||
timer_margin:
|
||||
initial watchdog timeout (in seconds)
|
||||
|
@ -477,13 +477,6 @@ config IXP4XX_WATCHDOG
|
||||
|
||||
Say N if you are unsure.
|
||||
|
||||
config KS8695_WATCHDOG
|
||||
tristate "KS8695 watchdog"
|
||||
depends on ARCH_KS8695
|
||||
help
|
||||
Watchdog timer embedded into KS8695 processor. This will reboot your
|
||||
system when the timeout is reached.
|
||||
|
||||
config HAVE_S3C2410_WATCHDOG
|
||||
bool
|
||||
help
|
||||
@ -662,15 +655,6 @@ config STMP3XXX_RTC_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stmp3xxx_rtc_wdt.
|
||||
|
||||
config NUC900_WATCHDOG
|
||||
tristate "Nuvoton NUC900 watchdog"
|
||||
depends on ARCH_W90X900 || COMPILE_TEST
|
||||
help
|
||||
Say Y here if to include support for the watchdog timer
|
||||
for the Nuvoton NUC900 series SoCs.
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nuc900_wdt.
|
||||
|
||||
config TS4800_WATCHDOG
|
||||
tristate "TS-4800 Watchdog"
|
||||
depends on HAS_IOMEM && OF
|
||||
@ -740,6 +724,19 @@ config IMX_SC_WDT
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx_sc_wdt.
|
||||
|
||||
config IMX7ULP_WDT
|
||||
tristate "IMX7ULP Watchdog"
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
This is the driver for the hardware watchdog on the Freescale
|
||||
IMX7ULP and later processors. If you have one of these
|
||||
processors and wish to have watchdog support enabled,
|
||||
say Y, otherwise say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called imx7ulp_wdt.
|
||||
|
||||
config UX500_WATCHDOG
|
||||
tristate "ST-Ericsson Ux500 watchdog"
|
||||
depends on MFD_DB8500_PRCMU
|
||||
@ -1046,8 +1043,8 @@ config F71808E_WDT
|
||||
depends on X86
|
||||
help
|
||||
This is the driver for the hardware watchdog on the Fintek F71808E,
|
||||
F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
|
||||
Super I/O controllers.
|
||||
F71862FG, F71868, F71869, F71882FG, F71889FG, F81803, F81865, and
|
||||
F81866 Super I/O controllers.
|
||||
|
||||
You can compile this driver directly into the kernel, or use
|
||||
it as a module. The module will be called f71808e_wdt.
|
||||
|
@ -49,7 +49,6 @@ obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
|
||||
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
|
||||
obj-$(CONFIG_FTWDT010_WATCHDOG) += ftwdt010_wdt.o
|
||||
obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
|
||||
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
|
||||
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
|
||||
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
|
||||
obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
|
||||
@ -64,11 +63,11 @@ obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
|
||||
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
|
||||
obj-$(CONFIG_NPCM7XX_WATCHDOG) += npcm_wdt.o
|
||||
obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
|
||||
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
|
||||
obj-$(CONFIG_TS4800_WATCHDOG) += ts4800_wdt.o
|
||||
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
|
||||
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
|
||||
obj-$(CONFIG_IMX_SC_WDT) += imx_sc_wdt.o
|
||||
obj-$(CONFIG_IMX7ULP_WDT) += imx7ulp_wdt.o
|
||||
obj-$(CONFIG_UX500_WATCHDOG) += ux500_wdt.o
|
||||
obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
|
||||
obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
|
||||
|
@ -34,6 +34,7 @@ static const struct aspeed_wdt_config ast2500_config = {
|
||||
static const struct of_device_id aspeed_wdt_of_table[] = {
|
||||
{ .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
|
||||
{ .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
|
||||
{ .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
|
||||
@ -53,6 +54,8 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
|
||||
#define WDT_CTRL_ENABLE BIT(0)
|
||||
#define WDT_TIMEOUT_STATUS 0x10
|
||||
#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
|
||||
#define WDT_CLEAR_TIMEOUT_STATUS 0x14
|
||||
#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
|
||||
|
||||
/*
|
||||
* WDT_RESET_WIDTH controls the characteristics of the external pulse (if
|
||||
@ -165,6 +168,60 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* access_cs0 shows if cs0 is accessible, hence the reverted bit */
|
||||
static ssize_t access_cs0_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct aspeed_wdt *wdt = dev_get_drvdata(dev);
|
||||
u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
|
||||
|
||||
return sprintf(buf, "%u\n",
|
||||
!(status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY));
|
||||
}
|
||||
|
||||
static ssize_t access_cs0_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t size)
|
||||
{
|
||||
struct aspeed_wdt *wdt = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
|
||||
if (kstrtoul(buf, 10, &val))
|
||||
return -EINVAL;
|
||||
|
||||
if (val)
|
||||
writel(WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION,
|
||||
wdt->base + WDT_CLEAR_TIMEOUT_STATUS);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* This attribute exists only if the system has booted from the alternate
|
||||
* flash with 'alt-boot' option.
|
||||
*
|
||||
* At alternate flash the 'access_cs0' sysfs node provides:
|
||||
* ast2400: a way to get access to the primary SPI flash chip at CS0
|
||||
* after booting from the alternate chip at CS1.
|
||||
* ast2500: a way to restore the normal address mapping from
|
||||
* (CS0->CS1, CS1->CS0) to (CS0->CS0, CS1->CS1).
|
||||
*
|
||||
* Clearing the boot code selection and timeout counter also resets to the
|
||||
* initial state the chip select line mapping. When the SoC is in normal
|
||||
* mapping state (i.e. booted from CS0), clearing those bits does nothing for
|
||||
* both versions of the SoC. For alternate boot mode (booted from CS1 due to
|
||||
* wdt2 expiration) the behavior differs as described above.
|
||||
*
|
||||
* This option can be used with wdt2 (watchdog1) only.
|
||||
*/
|
||||
static DEVICE_ATTR_RW(access_cs0);
|
||||
|
||||
static struct attribute *bswitch_attrs[] = {
|
||||
&dev_attr_access_cs0.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(bswitch);
|
||||
|
||||
static const struct watchdog_ops aspeed_wdt_ops = {
|
||||
.start = aspeed_wdt_start,
|
||||
.stop = aspeed_wdt_stop,
|
||||
@ -259,7 +316,8 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
|
||||
}
|
||||
|
||||
if (of_device_is_compatible(np, "aspeed,ast2500-wdt")) {
|
||||
if ((of_device_is_compatible(np, "aspeed,ast2500-wdt")) ||
|
||||
(of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
|
||||
u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
|
||||
|
||||
reg &= config->ext_pulse_width_mask;
|
||||
@ -306,9 +364,16 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
status = readl(wdt->base + WDT_TIMEOUT_STATUS);
|
||||
if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY)
|
||||
if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
|
||||
wdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
|
||||
of_device_is_compatible(np, "aspeed,ast2500-wdt"))
|
||||
wdt->wdd.groups = bswitch_groups;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, wdt);
|
||||
|
||||
return devm_watchdog_register_device(dev, &wdt->wdd);
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ static int ath79_wdt_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath97_wdt_shutdown(struct platform_device *pdev)
|
||||
static void ath79_wdt_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
ath79_wdt_disable();
|
||||
}
|
||||
@ -318,7 +318,7 @@ MODULE_DEVICE_TABLE(of, ath79_wdt_match);
|
||||
static struct platform_driver ath79_wdt_driver = {
|
||||
.probe = ath79_wdt_probe,
|
||||
.remove = ath79_wdt_remove,
|
||||
.shutdown = ath97_wdt_shutdown,
|
||||
.shutdown = ath79_wdt_shutdown,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(ath79_wdt_match),
|
||||
|
@ -473,29 +473,6 @@ static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
int rval = -ENOIOCTLCMD;
|
||||
|
||||
switch (cmd) {
|
||||
/* solaris ioctls are specific to this driver */
|
||||
case WIOCSTART:
|
||||
case WIOCSTOP:
|
||||
case WIOCGSTAT:
|
||||
mutex_lock(&cpwd_mutex);
|
||||
rval = cpwd_ioctl(file, cmd, arg);
|
||||
mutex_unlock(&cpwd_mutex);
|
||||
break;
|
||||
|
||||
/* everything else is handled by the generic compat layer */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
static ssize_t cpwd_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -520,7 +497,7 @@ static ssize_t cpwd_read(struct file *file, char __user *buffer,
|
||||
static const struct file_operations cpwd_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = cpwd_ioctl,
|
||||
.compat_ioctl = cpwd_compat_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = cpwd_open,
|
||||
.write = cpwd_write,
|
||||
.read = cpwd_read,
|
||||
|
@ -26,13 +26,11 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/diag.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define MAX_CMDLEN 240
|
||||
#define DEFAULT_CMD "SYSTEM RESTART"
|
||||
@ -70,7 +68,6 @@ MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is ac
|
||||
module_param_named(nowayout, nowayout_info, bool, 0444);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)");
|
||||
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
||||
MODULE_ALIAS("vmwatchdog");
|
||||
|
||||
static int __diag288(unsigned int func, unsigned int timeout,
|
||||
|
@ -31,8 +31,10 @@
|
||||
#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
|
||||
#define SIO_REG_DEVREV 0x22 /* Device revision */
|
||||
#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
|
||||
#define SIO_REG_CLOCK_SEL 0x26 /* Clock select */
|
||||
#define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
|
||||
#define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */
|
||||
#define SIO_REG_TSI_LEVEL_SEL 0x28 /* TSI Level select */
|
||||
#define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
|
||||
#define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
|
||||
#define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
|
||||
@ -49,6 +51,7 @@
|
||||
#define SIO_F71869A_ID 0x1007 /* Chipset ID */
|
||||
#define SIO_F71882_ID 0x0541 /* Chipset ID */
|
||||
#define SIO_F71889_ID 0x0723 /* Chipset ID */
|
||||
#define SIO_F81803_ID 0x1210 /* Chipset ID */
|
||||
#define SIO_F81865_ID 0x0704 /* Chipset ID */
|
||||
#define SIO_F81866_ID 0x1010 /* Chipset ID */
|
||||
|
||||
@ -108,7 +111,7 @@ MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
|
||||
" given initial timeout. Zero (default) disables this feature.");
|
||||
|
||||
enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
|
||||
f81865, f81866};
|
||||
f81803, f81865, f81866};
|
||||
|
||||
static const char *f71808e_names[] = {
|
||||
"f71808fg",
|
||||
@ -118,6 +121,7 @@ static const char *f71808e_names[] = {
|
||||
"f71869",
|
||||
"f71882fg",
|
||||
"f71889fg",
|
||||
"f81803",
|
||||
"f81865",
|
||||
"f81866",
|
||||
};
|
||||
@ -370,6 +374,14 @@ static int watchdog_start(void)
|
||||
superio_inb(watchdog.sioaddr, SIO_REG_MFUNCT3) & 0xcf);
|
||||
break;
|
||||
|
||||
case f81803:
|
||||
/* Enable TSI Level register bank */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_CLOCK_SEL, 3);
|
||||
/* Set pin 27 to WDTRST# */
|
||||
superio_outb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
|
||||
superio_inb(watchdog.sioaddr, SIO_REG_TSI_LEVEL_SEL));
|
||||
break;
|
||||
|
||||
case f81865:
|
||||
/* Set pin 70 to WDTRST# */
|
||||
superio_clear_bit(watchdog.sioaddr, SIO_REG_MFUNCT3, 5);
|
||||
@ -809,6 +821,9 @@ static int __init f71808e_find(int sioaddr)
|
||||
/* Confirmed (by datasheet) not to have a watchdog. */
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
case SIO_F81803_ID:
|
||||
watchdog.type = f81803;
|
||||
break;
|
||||
case SIO_F81865_ID:
|
||||
watchdog.type = f81865;
|
||||
break;
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
#define IMX2_WDT_WMCR 0x08 /* Misc Register */
|
||||
|
||||
#define IMX2_WDT_MAX_TIME 128
|
||||
#define IMX2_WDT_MAX_TIME 128U
|
||||
#define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
|
||||
|
||||
#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
|
||||
@ -180,7 +180,7 @@ static int imx2_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
{
|
||||
unsigned int actual;
|
||||
|
||||
actual = min(new_timeout, wdog->max_hw_heartbeat_ms * 1000);
|
||||
actual = min(new_timeout, IMX2_WDT_MAX_TIME);
|
||||
__imx2_wdt_set_timeout(wdog, actual);
|
||||
wdog->timeout = new_timeout;
|
||||
return 0;
|
||||
|
243
drivers/watchdog/imx7ulp_wdt.c
Normal file
243
drivers/watchdog/imx7ulp_wdt.c
Normal file
@ -0,0 +1,243 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright 2019 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#define WDOG_CS 0x0
|
||||
#define WDOG_CS_CMD32EN BIT(13)
|
||||
#define WDOG_CS_ULK BIT(11)
|
||||
#define WDOG_CS_RCS BIT(10)
|
||||
#define WDOG_CS_EN BIT(7)
|
||||
#define WDOG_CS_UPDATE BIT(5)
|
||||
|
||||
#define WDOG_CNT 0x4
|
||||
#define WDOG_TOVAL 0x8
|
||||
|
||||
#define REFRESH_SEQ0 0xA602
|
||||
#define REFRESH_SEQ1 0xB480
|
||||
#define REFRESH ((REFRESH_SEQ1 << 16) | REFRESH_SEQ0)
|
||||
|
||||
#define UNLOCK_SEQ0 0xC520
|
||||
#define UNLOCK_SEQ1 0xD928
|
||||
#define UNLOCK ((UNLOCK_SEQ1 << 16) | UNLOCK_SEQ0)
|
||||
|
||||
#define DEFAULT_TIMEOUT 60
|
||||
#define MAX_TIMEOUT 128
|
||||
#define WDOG_CLOCK_RATE 1000
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0000);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
struct imx7ulp_wdt_device {
|
||||
struct notifier_block restart_handler;
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static inline void imx7ulp_wdt_enable(void __iomem *base, bool enable)
|
||||
{
|
||||
u32 val = readl(base + WDOG_CS);
|
||||
|
||||
writel(UNLOCK, base + WDOG_CNT);
|
||||
if (enable)
|
||||
writel(val | WDOG_CS_EN, base + WDOG_CS);
|
||||
else
|
||||
writel(val & ~WDOG_CS_EN, base + WDOG_CS);
|
||||
}
|
||||
|
||||
static inline bool imx7ulp_wdt_is_enabled(void __iomem *base)
|
||||
{
|
||||
u32 val = readl(base + WDOG_CS);
|
||||
|
||||
return val & WDOG_CS_EN;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_ping(struct watchdog_device *wdog)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
|
||||
writel(REFRESH, wdt->base + WDOG_CNT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_start(struct watchdog_device *wdog)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
|
||||
imx7ulp_wdt_enable(wdt->base, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
|
||||
imx7ulp_wdt_enable(wdt->base, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
|
||||
unsigned int timeout)
|
||||
{
|
||||
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
|
||||
u32 val = WDOG_CLOCK_RATE * timeout;
|
||||
|
||||
writel(UNLOCK, wdt->base + WDOG_CNT);
|
||||
writel(val, wdt->base + WDOG_TOVAL);
|
||||
|
||||
wdog->timeout = timeout;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops imx7ulp_wdt_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.start = imx7ulp_wdt_start,
|
||||
.stop = imx7ulp_wdt_stop,
|
||||
.ping = imx7ulp_wdt_ping,
|
||||
.set_timeout = imx7ulp_wdt_set_timeout,
|
||||
};
|
||||
|
||||
static const struct watchdog_info imx7ulp_wdt_info = {
|
||||
.identity = "i.MX7ULP watchdog timer",
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static inline void imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* unlock the wdog for reconfiguration */
|
||||
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
|
||||
writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
|
||||
|
||||
/* set an initial timeout value in TOVAL */
|
||||
writel(timeout, base + WDOG_TOVAL);
|
||||
/* enable 32bit command sequence and reconfigure */
|
||||
val = BIT(13) | BIT(8) | BIT(5);
|
||||
writel(val, base + WDOG_CS);
|
||||
}
|
||||
|
||||
static void imx7ulp_wdt_action(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
}
|
||||
|
||||
static int imx7ulp_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct imx7ulp_wdt_device *imx7ulp_wdt;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct watchdog_device *wdog;
|
||||
int ret;
|
||||
|
||||
imx7ulp_wdt = devm_kzalloc(dev, sizeof(*imx7ulp_wdt), GFP_KERNEL);
|
||||
if (!imx7ulp_wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, imx7ulp_wdt);
|
||||
|
||||
imx7ulp_wdt->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(imx7ulp_wdt->base))
|
||||
return PTR_ERR(imx7ulp_wdt->base);
|
||||
|
||||
imx7ulp_wdt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(imx7ulp_wdt->clk)) {
|
||||
dev_err(dev, "Failed to get watchdog clock\n");
|
||||
return PTR_ERR(imx7ulp_wdt->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(imx7ulp_wdt->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, imx7ulp_wdt_action, imx7ulp_wdt->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wdog = &imx7ulp_wdt->wdd;
|
||||
wdog->info = &imx7ulp_wdt_info;
|
||||
wdog->ops = &imx7ulp_wdt_ops;
|
||||
wdog->min_timeout = 1;
|
||||
wdog->max_timeout = MAX_TIMEOUT;
|
||||
wdog->parent = dev;
|
||||
wdog->timeout = DEFAULT_TIMEOUT;
|
||||
|
||||
watchdog_init_timeout(wdog, 0, dev);
|
||||
watchdog_stop_on_reboot(wdog);
|
||||
watchdog_stop_on_unregister(wdog);
|
||||
watchdog_set_drvdata(wdog, imx7ulp_wdt);
|
||||
imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
|
||||
|
||||
return devm_watchdog_register_device(dev, wdog);
|
||||
}
|
||||
|
||||
static int __maybe_unused imx7ulp_wdt_suspend(struct device *dev)
|
||||
{
|
||||
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
|
||||
|
||||
if (watchdog_active(&imx7ulp_wdt->wdd))
|
||||
imx7ulp_wdt_stop(&imx7ulp_wdt->wdd);
|
||||
|
||||
clk_disable_unprepare(imx7ulp_wdt->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused imx7ulp_wdt_resume(struct device *dev)
|
||||
{
|
||||
struct imx7ulp_wdt_device *imx7ulp_wdt = dev_get_drvdata(dev);
|
||||
u32 timeout = imx7ulp_wdt->wdd.timeout * WDOG_CLOCK_RATE;
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(imx7ulp_wdt->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
|
||||
imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
|
||||
|
||||
if (watchdog_active(&imx7ulp_wdt->wdd))
|
||||
imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx7ulp_wdt_pm_ops, imx7ulp_wdt_suspend,
|
||||
imx7ulp_wdt_resume);
|
||||
|
||||
static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
|
||||
{ .compatible = "fsl,imx7ulp-wdt", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, imx7ulp_wdt_dt_ids);
|
||||
|
||||
static struct platform_driver imx7ulp_wdt_driver = {
|
||||
.probe = imx7ulp_wdt_probe,
|
||||
.driver = {
|
||||
.name = "imx7ulp-wdt",
|
||||
.pm = &imx7ulp_wdt_pm_ops,
|
||||
.of_match_table = imx7ulp_wdt_dt_ids,
|
||||
},
|
||||
};
|
||||
module_platform_driver(imx7ulp_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Anson Huang <Anson.Huang@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale i.MX7ULP watchdog driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -175,12 +175,9 @@ static int imx_sc_wdt_probe(struct platform_device *pdev)
|
||||
watchdog_stop_on_unregister(wdog);
|
||||
|
||||
ret = devm_watchdog_register_device(dev, wdog);
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register watchdog device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WDOG,
|
||||
SC_IRQ_WDOG,
|
||||
true);
|
||||
|
@ -162,7 +162,6 @@ static int jz4740_wdt_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct jz4740_wdt_drvdata *drvdata;
|
||||
struct watchdog_device *jz4740_wdt;
|
||||
int ret;
|
||||
|
||||
drvdata = devm_kzalloc(dev, sizeof(struct jz4740_wdt_drvdata),
|
||||
GFP_KERNEL);
|
||||
|
@ -1,319 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Watchdog driver for Kendin/Micrel KS8695.
|
||||
*
|
||||
* (C) 2007 Andrew Victor
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <mach/hardware.h>
|
||||
|
||||
#define KS8695_TMR_OFFSET (0xF0000 + 0xE400)
|
||||
#define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET)
|
||||
|
||||
/*
|
||||
* Timer registers
|
||||
*/
|
||||
#define KS8695_TMCON (0x00) /* Timer Control Register */
|
||||
#define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */
|
||||
#define TMCON_T0EN (1 << 0) /* Timer 0 Enable */
|
||||
|
||||
/* Timer0 Timeout Counter Register */
|
||||
#define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */
|
||||
|
||||
#define WDT_DEFAULT_TIME 5 /* seconds */
|
||||
#define WDT_MAX_TIME 171 /* seconds */
|
||||
|
||||
static int wdt_time = WDT_DEFAULT_TIME;
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
|
||||
module_param(wdt_time, int, 0);
|
||||
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
|
||||
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
|
||||
|
||||
#ifdef CONFIG_WATCHDOG_NOWAYOUT
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
#endif
|
||||
|
||||
|
||||
static unsigned long ks8695wdt_busy;
|
||||
static DEFINE_SPINLOCK(ks8695_lock);
|
||||
|
||||
/* ......................................................................... */
|
||||
|
||||
/*
|
||||
* Disable the watchdog.
|
||||
*/
|
||||
static inline void ks8695_wdt_stop(void)
|
||||
{
|
||||
unsigned long tmcon;
|
||||
|
||||
spin_lock(&ks8695_lock);
|
||||
/* disable timer0 */
|
||||
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
|
||||
__raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
|
||||
spin_unlock(&ks8695_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable and reset the watchdog.
|
||||
*/
|
||||
static inline void ks8695_wdt_start(void)
|
||||
{
|
||||
unsigned long tmcon;
|
||||
unsigned long tval = wdt_time * KS8695_CLOCK_RATE;
|
||||
|
||||
spin_lock(&ks8695_lock);
|
||||
/* disable timer0 */
|
||||
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
|
||||
__raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
|
||||
|
||||
/* program timer0 */
|
||||
__raw_writel(tval | T0TC_WATCHDOG, KS8695_TMR_VA + KS8695_T0TC);
|
||||
|
||||
/* re-enable timer0 */
|
||||
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
|
||||
__raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
|
||||
spin_unlock(&ks8695_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reload the watchdog timer. (ie, pat the watchdog)
|
||||
*/
|
||||
static inline void ks8695_wdt_reload(void)
|
||||
{
|
||||
unsigned long tmcon;
|
||||
|
||||
spin_lock(&ks8695_lock);
|
||||
/* disable, then re-enable timer0 */
|
||||
tmcon = __raw_readl(KS8695_TMR_VA + KS8695_TMCON);
|
||||
__raw_writel(tmcon & ~TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
|
||||
__raw_writel(tmcon | TMCON_T0EN, KS8695_TMR_VA + KS8695_TMCON);
|
||||
spin_unlock(&ks8695_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the watchdog time interval.
|
||||
*/
|
||||
static int ks8695_wdt_settimeout(int new_time)
|
||||
{
|
||||
/*
|
||||
* All counting occurs at KS8695_CLOCK_RATE / 128 = 0.256 Hz
|
||||
*
|
||||
* Since WDV is a 16-bit counter, the maximum period is
|
||||
* 65536 / 0.256 = 256 seconds.
|
||||
*/
|
||||
if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
|
||||
return -EINVAL;
|
||||
|
||||
/* Set new watchdog time. It will be used when
|
||||
ks8695_wdt_start() is called. */
|
||||
wdt_time = new_time;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ......................................................................... */
|
||||
|
||||
/*
|
||||
* Watchdog device is opened, and watchdog starts running.
|
||||
*/
|
||||
static int ks8695_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(0, &ks8695wdt_busy))
|
||||
return -EBUSY;
|
||||
|
||||
ks8695_wdt_start();
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the watchdog device.
|
||||
* If CONFIG_WATCHDOG_NOWAYOUT is NOT defined then the watchdog is also
|
||||
* disabled.
|
||||
*/
|
||||
static int ks8695_wdt_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* Disable the watchdog when file is closed */
|
||||
if (!nowayout)
|
||||
ks8695_wdt_stop();
|
||||
clear_bit(0, &ks8695wdt_busy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ks8695_wdt_info = {
|
||||
.identity = "ks8695 watchdog",
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle commands from user-space.
|
||||
*/
|
||||
static long ks8695_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int new_value;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, &ks8695_wdt_info,
|
||||
sizeof(ks8695_wdt_info)) ? -EFAULT : 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
if (new_value & WDIOS_DISABLECARD)
|
||||
ks8695_wdt_stop();
|
||||
if (new_value & WDIOS_ENABLECARD)
|
||||
ks8695_wdt_start();
|
||||
return 0;
|
||||
case WDIOC_KEEPALIVE:
|
||||
ks8695_wdt_reload(); /* pat the watchdog */
|
||||
return 0;
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
if (ks8695_wdt_settimeout(new_value))
|
||||
return -EINVAL;
|
||||
/* Enable new time value */
|
||||
ks8695_wdt_start();
|
||||
/* Return current value */
|
||||
return put_user(wdt_time, p);
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(wdt_time, p);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Pat the watchdog whenever device is written to.
|
||||
*/
|
||||
static ssize_t ks8695_wdt_write(struct file *file, const char *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
ks8695_wdt_reload(); /* pat the watchdog */
|
||||
return len;
|
||||
}
|
||||
|
||||
/* ......................................................................... */
|
||||
|
||||
static const struct file_operations ks8695wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = ks8695_wdt_ioctl,
|
||||
.open = ks8695_wdt_open,
|
||||
.release = ks8695_wdt_close,
|
||||
.write = ks8695_wdt_write,
|
||||
};
|
||||
|
||||
static struct miscdevice ks8695wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &ks8695wdt_fops,
|
||||
};
|
||||
|
||||
static int ks8695wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (ks8695wdt_miscdev.parent)
|
||||
return -EBUSY;
|
||||
ks8695wdt_miscdev.parent = &pdev->dev;
|
||||
|
||||
res = misc_register(&ks8695wdt_miscdev);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
pr_info("KS8695 Watchdog Timer enabled (%d seconds%s)\n",
|
||||
wdt_time, nowayout ? ", nowayout" : "");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ks8695wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&ks8695wdt_miscdev);
|
||||
ks8695wdt_miscdev.parent = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ks8695wdt_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
ks8695_wdt_stop();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
static int ks8695wdt_suspend(struct platform_device *pdev, pm_message_t message)
|
||||
{
|
||||
ks8695_wdt_stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ks8695wdt_resume(struct platform_device *pdev)
|
||||
{
|
||||
if (ks8695wdt_busy)
|
||||
ks8695_wdt_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define ks8695wdt_suspend NULL
|
||||
#define ks8695wdt_resume NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver ks8695wdt_driver = {
|
||||
.probe = ks8695wdt_probe,
|
||||
.remove = ks8695wdt_remove,
|
||||
.shutdown = ks8695wdt_shutdown,
|
||||
.suspend = ks8695wdt_suspend,
|
||||
.resume = ks8695wdt_resume,
|
||||
.driver = {
|
||||
.name = "ks8695_wdt",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init ks8695_wdt_init(void)
|
||||
{
|
||||
/* Check that the heartbeat value is within range;
|
||||
if not reset to the default */
|
||||
if (ks8695_wdt_settimeout(wdt_time)) {
|
||||
ks8695_wdt_settimeout(WDT_DEFAULT_TIME);
|
||||
pr_info("ks8695_wdt: wdt_time value must be 1 <= wdt_time <= %i"
|
||||
", using %d\n", wdt_time, WDT_MAX_TIME);
|
||||
}
|
||||
return platform_driver_register(&ks8695wdt_driver);
|
||||
}
|
||||
|
||||
static void __exit ks8695_wdt_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ks8695wdt_driver);
|
||||
}
|
||||
|
||||
module_init(ks8695_wdt_init);
|
||||
module_exit(ks8695_wdt_exit);
|
||||
|
||||
MODULE_AUTHOR("Andrew Victor");
|
||||
MODULE_DESCRIPTION("Watchdog driver for KS8695");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:ks8695_wdt");
|
@ -1,302 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2009 Nuvoton technology corporation.
|
||||
*
|
||||
* Wan ZongShun <mcuos.com@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define REG_WTCR 0x1c
|
||||
#define WTCLK (0x01 << 10)
|
||||
#define WTE (0x01 << 7) /*wdt enable*/
|
||||
#define WTIS (0x03 << 4)
|
||||
#define WTIF (0x01 << 3)
|
||||
#define WTRF (0x01 << 2)
|
||||
#define WTRE (0x01 << 1)
|
||||
#define WTR (0x01 << 0)
|
||||
/*
|
||||
* The watchdog time interval can be calculated via following formula:
|
||||
* WTIS real time interval (formula)
|
||||
* 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
|
||||
* 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
|
||||
* 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
|
||||
* 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
|
||||
*
|
||||
* The external crystal freq is 15Mhz in the nuc900 evaluation board.
|
||||
* So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
|
||||
* 0x03 = +- 16.92 seconds..
|
||||
*/
|
||||
#define WDT_HW_TIMEOUT 0x02
|
||||
#define WDT_TIMEOUT (HZ/2)
|
||||
#define WDT_HEARTBEAT 15
|
||||
|
||||
static int heartbeat = WDT_HEARTBEAT;
|
||||
module_param(heartbeat, int, 0);
|
||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
|
||||
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
|
||||
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
struct nuc900_wdt {
|
||||
struct clk *wdt_clock;
|
||||
struct platform_device *pdev;
|
||||
void __iomem *wdt_base;
|
||||
char expect_close;
|
||||
struct timer_list timer;
|
||||
spinlock_t wdt_lock;
|
||||
unsigned long next_heartbeat;
|
||||
};
|
||||
|
||||
static unsigned long nuc900wdt_busy;
|
||||
static struct nuc900_wdt *nuc900_wdt;
|
||||
|
||||
static inline void nuc900_wdt_keepalive(void)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
spin_lock(&nuc900_wdt->wdt_lock);
|
||||
|
||||
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
|
||||
val |= (WTR | WTIF);
|
||||
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
|
||||
|
||||
spin_unlock(&nuc900_wdt->wdt_lock);
|
||||
}
|
||||
|
||||
static inline void nuc900_wdt_start(void)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
spin_lock(&nuc900_wdt->wdt_lock);
|
||||
|
||||
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
|
||||
val |= (WTRE | WTE | WTR | WTCLK | WTIF);
|
||||
val &= ~WTIS;
|
||||
val |= (WDT_HW_TIMEOUT << 0x04);
|
||||
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
|
||||
|
||||
spin_unlock(&nuc900_wdt->wdt_lock);
|
||||
|
||||
nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
|
||||
mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
|
||||
}
|
||||
|
||||
static inline void nuc900_wdt_stop(void)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
del_timer(&nuc900_wdt->timer);
|
||||
|
||||
spin_lock(&nuc900_wdt->wdt_lock);
|
||||
|
||||
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
|
||||
val &= ~WTE;
|
||||
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
|
||||
|
||||
spin_unlock(&nuc900_wdt->wdt_lock);
|
||||
}
|
||||
|
||||
static inline void nuc900_wdt_ping(void)
|
||||
{
|
||||
nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
|
||||
}
|
||||
|
||||
static int nuc900_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
|
||||
if (test_and_set_bit(0, &nuc900wdt_busy))
|
||||
return -EBUSY;
|
||||
|
||||
nuc900_wdt_start();
|
||||
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static int nuc900_wdt_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (nuc900_wdt->expect_close == 42)
|
||||
nuc900_wdt_stop();
|
||||
else {
|
||||
dev_crit(&nuc900_wdt->pdev->dev,
|
||||
"Unexpected close, not stopping watchdog!\n");
|
||||
nuc900_wdt_ping();
|
||||
}
|
||||
|
||||
nuc900_wdt->expect_close = 0;
|
||||
clear_bit(0, &nuc900wdt_busy);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_info nuc900_wdt_info = {
|
||||
.identity = "nuc900 watchdog",
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
|
||||
static long nuc900_wdt_ioctl(struct file *file,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
int new_value;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, &nuc900_wdt_info,
|
||||
sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
nuc900_wdt_ping();
|
||||
return 0;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_value, p))
|
||||
return -EFAULT;
|
||||
|
||||
heartbeat = new_value;
|
||||
nuc900_wdt_ping();
|
||||
|
||||
return put_user(new_value, p);
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(heartbeat, p);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* Scan for magic character */
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
nuc900_wdt->expect_close = 0;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
char c;
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V') {
|
||||
nuc900_wdt->expect_close = 42;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nuc900_wdt_ping();
|
||||
return len;
|
||||
}
|
||||
|
||||
static void nuc900_wdt_timer_ping(struct timer_list *unused)
|
||||
{
|
||||
if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
|
||||
nuc900_wdt_keepalive();
|
||||
mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
|
||||
} else
|
||||
dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
|
||||
}
|
||||
|
||||
static const struct file_operations nuc900wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.unlocked_ioctl = nuc900_wdt_ioctl,
|
||||
.open = nuc900_wdt_open,
|
||||
.release = nuc900_wdt_close,
|
||||
.write = nuc900_wdt_write,
|
||||
};
|
||||
|
||||
static struct miscdevice nuc900wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &nuc900wdt_fops,
|
||||
};
|
||||
|
||||
static int nuc900wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
nuc900_wdt = devm_kzalloc(&pdev->dev, sizeof(*nuc900_wdt),
|
||||
GFP_KERNEL);
|
||||
if (!nuc900_wdt)
|
||||
return -ENOMEM;
|
||||
|
||||
nuc900_wdt->pdev = pdev;
|
||||
|
||||
spin_lock_init(&nuc900_wdt->wdt_lock);
|
||||
|
||||
nuc900_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(nuc900_wdt->wdt_base))
|
||||
return PTR_ERR(nuc900_wdt->wdt_base);
|
||||
|
||||
nuc900_wdt->wdt_clock = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(nuc900_wdt->wdt_clock)) {
|
||||
dev_err(&pdev->dev, "failed to find watchdog clock source\n");
|
||||
return PTR_ERR(nuc900_wdt->wdt_clock);
|
||||
}
|
||||
|
||||
clk_enable(nuc900_wdt->wdt_clock);
|
||||
|
||||
timer_setup(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
|
||||
|
||||
ret = misc_register(&nuc900wdt_miscdev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
|
||||
WATCHDOG_MINOR, ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable(nuc900_wdt->wdt_clock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nuc900wdt_remove(struct platform_device *pdev)
|
||||
{
|
||||
misc_deregister(&nuc900wdt_miscdev);
|
||||
|
||||
clk_disable(nuc900_wdt->wdt_clock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver nuc900wdt_driver = {
|
||||
.probe = nuc900wdt_probe,
|
||||
.remove = nuc900wdt_remove,
|
||||
.driver = {
|
||||
.name = "nuc900-wdt",
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(nuc900wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
|
||||
MODULE_DESCRIPTION("Watchdog driver for NUC900");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:nuc900-wdt");
|
@ -35,7 +35,15 @@
|
||||
* Watchdog timer block registers.
|
||||
*/
|
||||
#define TIMER_CTRL 0x0000
|
||||
#define TIMER_A370_STATUS 0x04
|
||||
#define TIMER1_FIXED_ENABLE_BIT BIT(12)
|
||||
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
|
||||
#define TIMER1_ENABLE_BIT BIT(2)
|
||||
|
||||
#define TIMER_A370_STATUS 0x0004
|
||||
#define WDT_A370_EXPIRED BIT(31)
|
||||
#define TIMER1_STATUS_BIT BIT(8)
|
||||
|
||||
#define TIMER1_VAL_OFF 0x001c
|
||||
|
||||
#define WDT_MAX_CYCLE_COUNT 0xffffffff
|
||||
|
||||
@ -43,9 +51,6 @@
|
||||
#define WDT_A370_RATIO_SHIFT 5
|
||||
#define WDT_A370_RATIO (1 << WDT_A370_RATIO_SHIFT)
|
||||
|
||||
#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
|
||||
#define WDT_A370_EXPIRED BIT(31)
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static int heartbeat = -1; /* module parameter (seconds) */
|
||||
|
||||
@ -158,6 +163,7 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
|
||||
struct orion_watchdog *dev)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
|
||||
if (IS_ERR(dev->clk))
|
||||
@ -168,10 +174,9 @@ static int armadaxp_wdt_clock_init(struct platform_device *pdev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable the fixed watchdog clock input */
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL,
|
||||
WDT_AXP_FIXED_ENABLE_BIT,
|
||||
WDT_AXP_FIXED_ENABLE_BIT);
|
||||
/* Fix the wdt and timer1 clock freqency to 25MHz */
|
||||
val = WDT_AXP_FIXED_ENABLE_BIT | TIMER1_FIXED_ENABLE_BIT;
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, val, val);
|
||||
|
||||
dev->clk_rate = clk_get_rate(dev->clk);
|
||||
return 0;
|
||||
@ -183,6 +188,10 @@ static int orion_wdt_ping(struct watchdog_device *wdt_dev)
|
||||
/* Reload watchdog duration */
|
||||
writel(dev->clk_rate * wdt_dev->timeout,
|
||||
dev->reg + dev->data->wdt_counter_offset);
|
||||
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
|
||||
writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout),
|
||||
dev->reg + TIMER1_VAL_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -194,13 +203,18 @@ static int armada375_start(struct watchdog_device *wdt_dev)
|
||||
/* Set watchdog duration */
|
||||
writel(dev->clk_rate * wdt_dev->timeout,
|
||||
dev->reg + dev->data->wdt_counter_offset);
|
||||
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
|
||||
writel(dev->clk_rate * (wdt_dev->timeout - wdt_dev->pretimeout),
|
||||
dev->reg + TIMER1_VAL_OFF);
|
||||
|
||||
/* Clear the watchdog expiration bit */
|
||||
atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
|
||||
|
||||
/* Enable watchdog timer */
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
|
||||
dev->data->wdt_enable_bit);
|
||||
reg = dev->data->wdt_enable_bit;
|
||||
if (dev->wdt.info->options & WDIOF_PRETIMEOUT)
|
||||
reg |= TIMER1_ENABLE_BIT;
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, reg, reg);
|
||||
|
||||
/* Enable reset on watchdog */
|
||||
reg = readl(dev->rstout);
|
||||
@ -277,7 +291,7 @@ static int orion_stop(struct watchdog_device *wdt_dev)
|
||||
static int armada375_stop(struct watchdog_device *wdt_dev)
|
||||
{
|
||||
struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
|
||||
u32 reg;
|
||||
u32 reg, mask;
|
||||
|
||||
/* Disable reset on watchdog */
|
||||
atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
|
||||
@ -287,7 +301,10 @@ static int armada375_stop(struct watchdog_device *wdt_dev)
|
||||
writel(reg, dev->rstout);
|
||||
|
||||
/* Disable watchdog timer */
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
|
||||
mask = dev->data->wdt_enable_bit;
|
||||
if (wdt_dev->info->options & WDIOF_PRETIMEOUT)
|
||||
mask |= TIMER1_ENABLE_BIT;
|
||||
atomic_io_modify(dev->reg + TIMER_CTRL, mask, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -349,7 +366,7 @@ static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
|
||||
return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
|
||||
}
|
||||
|
||||
static const struct watchdog_info orion_wdt_info = {
|
||||
static struct watchdog_info orion_wdt_info = {
|
||||
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
|
||||
.identity = "Orion Watchdog",
|
||||
};
|
||||
@ -368,6 +385,16 @@ static irqreturn_t orion_wdt_irq(int irq, void *devid)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t orion_wdt_pre_irq(int irq, void *devid)
|
||||
{
|
||||
struct orion_watchdog *dev = devid;
|
||||
|
||||
atomic_io_modify(dev->reg + TIMER_A370_STATUS,
|
||||
TIMER1_STATUS_BIT, 0);
|
||||
watchdog_notify_pretimeout(&dev->wdt);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* The original devicetree binding for this driver specified only
|
||||
* one memory resource, so in order to keep DT backwards compatibility
|
||||
@ -589,6 +616,19 @@ static int orion_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
/* Optional 2nd interrupt for pretimeout */
|
||||
irq = platform_get_irq(pdev, 1);
|
||||
if (irq > 0) {
|
||||
orion_wdt_info.options |= WDIOF_PRETIMEOUT;
|
||||
ret = devm_request_irq(&pdev->dev, irq, orion_wdt_pre_irq,
|
||||
0, pdev->name, dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request IRQ\n");
|
||||
goto disable_clk;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
watchdog_set_nowayout(&dev->wdt, nowayout);
|
||||
ret = watchdog_register_device(&dev->wdt);
|
||||
if (ret)
|
||||
|
@ -1,8 +1,10 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
@ -19,6 +21,9 @@ enum wdt_reg {
|
||||
WDT_BITE_TIME,
|
||||
};
|
||||
|
||||
#define QCOM_WDT_ENABLE BIT(0)
|
||||
#define QCOM_WDT_ENABLE_IRQ BIT(1)
|
||||
|
||||
static const u32 reg_offset_data_apcs_tmr[] = {
|
||||
[WDT_RST] = 0x38,
|
||||
[WDT_EN] = 0x40,
|
||||
@ -37,7 +42,6 @@ static const u32 reg_offset_data_kpss[] = {
|
||||
|
||||
struct qcom_wdt {
|
||||
struct watchdog_device wdd;
|
||||
struct clk *clk;
|
||||
unsigned long rate;
|
||||
void __iomem *base;
|
||||
const u32 *layout;
|
||||
@ -54,15 +58,35 @@ struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
|
||||
return container_of(wdd, struct qcom_wdt, wdd);
|
||||
}
|
||||
|
||||
static inline int qcom_get_enable(struct watchdog_device *wdd)
|
||||
{
|
||||
int enable = QCOM_WDT_ENABLE;
|
||||
|
||||
if (wdd->pretimeout)
|
||||
enable |= QCOM_WDT_ENABLE_IRQ;
|
||||
|
||||
return enable;
|
||||
}
|
||||
|
||||
static irqreturn_t qcom_wdt_isr(int irq, void *arg)
|
||||
{
|
||||
struct watchdog_device *wdd = arg;
|
||||
|
||||
watchdog_notify_pretimeout(wdd);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int qcom_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
|
||||
unsigned int bark = wdd->timeout - wdd->pretimeout;
|
||||
|
||||
writel(0, wdt_addr(wdt, WDT_EN));
|
||||
writel(1, wdt_addr(wdt, WDT_RST));
|
||||
writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
|
||||
writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
|
||||
writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
|
||||
writel(1, wdt_addr(wdt, WDT_EN));
|
||||
writel(qcom_get_enable(wdd), wdt_addr(wdt, WDT_EN));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -89,6 +113,13 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
return qcom_wdt_start(wdd);
|
||||
}
|
||||
|
||||
static int qcom_wdt_set_pretimeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
wdd->pretimeout = timeout;
|
||||
return qcom_wdt_start(wdd);
|
||||
}
|
||||
|
||||
static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
|
||||
void *data)
|
||||
{
|
||||
@ -105,7 +136,7 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
|
||||
writel(1, wdt_addr(wdt, WDT_RST));
|
||||
writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
|
||||
writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
|
||||
writel(1, wdt_addr(wdt, WDT_EN));
|
||||
writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN));
|
||||
|
||||
/*
|
||||
* Actually make sure the above sequence hits hardware before sleeping.
|
||||
@ -121,6 +152,7 @@ static const struct watchdog_ops qcom_wdt_ops = {
|
||||
.stop = qcom_wdt_stop,
|
||||
.ping = qcom_wdt_ping,
|
||||
.set_timeout = qcom_wdt_set_timeout,
|
||||
.set_pretimeout = qcom_wdt_set_pretimeout,
|
||||
.restart = qcom_wdt_restart,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
@ -133,6 +165,15 @@ static const struct watchdog_info qcom_wdt_info = {
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
static const struct watchdog_info qcom_wdt_pt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_SETTIMEOUT
|
||||
| WDIOF_PRETIMEOUT
|
||||
| WDIOF_CARDRESET,
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
static void qcom_clk_disable_unprepare(void *data)
|
||||
{
|
||||
clk_disable_unprepare(data);
|
||||
@ -146,7 +187,8 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
struct device_node *np = dev->of_node;
|
||||
const u32 *regs;
|
||||
u32 percpu_offset;
|
||||
int ret;
|
||||
int irq, ret;
|
||||
struct clk *clk;
|
||||
|
||||
regs = of_device_get_match_data(dev);
|
||||
if (!regs) {
|
||||
@ -173,19 +215,18 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(wdt->base))
|
||||
return PTR_ERR(wdt->base);
|
||||
|
||||
wdt->clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(wdt->clk)) {
|
||||
clk = devm_clk_get(dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "failed to get input clock\n");
|
||||
return PTR_ERR(wdt->clk);
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(wdt->clk);
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to setup clock\n");
|
||||
return ret;
|
||||
}
|
||||
ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare,
|
||||
wdt->clk);
|
||||
ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -197,14 +238,31 @@ static int qcom_wdt_probe(struct platform_device *pdev)
|
||||
* that it would bite before a second elapses it's usefulness is
|
||||
* limited. Bail if this is the case.
|
||||
*/
|
||||
wdt->rate = clk_get_rate(wdt->clk);
|
||||
wdt->rate = clk_get_rate(clk);
|
||||
if (wdt->rate == 0 ||
|
||||
wdt->rate > 0x10000000U) {
|
||||
dev_err(dev, "invalid clock rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wdt->wdd.info = &qcom_wdt_info;
|
||||
/* check if there is pretimeout support */
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq > 0) {
|
||||
ret = devm_request_irq(dev, irq, qcom_wdt_isr,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"wdt_bark", &wdt->wdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wdt->wdd.info = &qcom_wdt_pt_info;
|
||||
wdt->wdd.pretimeout = 1;
|
||||
} else {
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
wdt->wdd.info = &qcom_wdt_info;
|
||||
}
|
||||
|
||||
wdt->wdd.ops = &qcom_wdt_ops;
|
||||
wdt->wdd.min_timeout = 1;
|
||||
wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
|
||||
|
@ -284,10 +284,8 @@ static int sprd_wdt_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
wdt->irq = platform_get_irq(pdev, 0);
|
||||
if (wdt->irq < 0) {
|
||||
dev_err(dev, "failed to get IRQ resource\n");
|
||||
if (wdt->irq < 0)
|
||||
return wdt->irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, wdt->irq, sprd_wdt_isr, IRQF_NO_SUSPEND,
|
||||
"sprd-wdt", (void *)wdt);
|
||||
|
@ -21,8 +21,11 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define ZIIRAVE_TIMEOUT_MIN 3
|
||||
#define ZIIRAVE_TIMEOUT_MAX 255
|
||||
#define ZIIRAVE_TIMEOUT_DEFAULT 30
|
||||
|
||||
#define ZIIRAVE_PING_VALUE 0x0
|
||||
|
||||
@ -48,16 +51,12 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
|
||||
|
||||
#define ZIIRAVE_FIRM_PKT_TOTAL_SIZE 20
|
||||
#define ZIIRAVE_FIRM_PKT_DATA_SIZE 16
|
||||
#define ZIIRAVE_FIRM_FLASH_MEMORY_START 0x1600
|
||||
#define ZIIRAVE_FIRM_FLASH_MEMORY_END 0x2bbf
|
||||
#define ZIIRAVE_FIRM_FLASH_MEMORY_START (2 * 0x1600)
|
||||
#define ZIIRAVE_FIRM_FLASH_MEMORY_END (2 * 0x2bbf)
|
||||
#define ZIIRAVE_FIRM_PAGE_SIZE 128
|
||||
|
||||
/* Received and ready for next Download packet. */
|
||||
#define ZIIRAVE_FIRM_DOWNLOAD_ACK 1
|
||||
/* Currently writing to flash. Retry Download status in a moment! */
|
||||
#define ZIIRAVE_FIRM_DOWNLOAD_BUSY 2
|
||||
|
||||
/* Wait for ACK timeout in ms */
|
||||
#define ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT 50
|
||||
|
||||
/* Firmware commands */
|
||||
#define ZIIRAVE_CMD_DOWNLOAD_START 0x10
|
||||
@ -68,6 +67,12 @@ static char *ziirave_reasons[] = {"power cycle", "hw watchdog", NULL, NULL,
|
||||
#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER 0x0c
|
||||
#define ZIIRAVE_CMD_DOWNLOAD_PACKET 0x0e
|
||||
|
||||
#define ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC 1
|
||||
#define ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC 1
|
||||
|
||||
#define ZIIRAVE_FW_VERSION_FMT "02.%02u.%02u"
|
||||
#define ZIIRAVE_BL_VERSION_FMT "01.%02u.%02u"
|
||||
|
||||
struct ziirave_wdt_rev {
|
||||
unsigned char major;
|
||||
unsigned char minor;
|
||||
@ -165,67 +170,37 @@ static unsigned int ziirave_wdt_get_timeleft(struct watchdog_device *wdd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ziirave_firm_wait_for_ack(struct watchdog_device *wdd)
|
||||
static int ziirave_firm_read_ack(struct watchdog_device *wdd)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(wdd->parent);
|
||||
int ret;
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(ZIIRAVE_FIRM_WAIT_FOR_ACK_TIMEOUT);
|
||||
do {
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
usleep_range(5000, 10000);
|
||||
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read byte\n");
|
||||
return ret;
|
||||
}
|
||||
} while (ret == ZIIRAVE_FIRM_DOWNLOAD_BUSY);
|
||||
ret = i2c_smbus_read_byte(client);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read status byte\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret == ZIIRAVE_FIRM_DOWNLOAD_ACK ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u16 addr)
|
||||
static int ziirave_firm_set_read_addr(struct watchdog_device *wdd, u32 addr)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(wdd->parent);
|
||||
const u16 addr16 = (u16)addr / 2;
|
||||
u8 address[2];
|
||||
|
||||
address[0] = addr & 0xff;
|
||||
address[1] = (addr >> 8) & 0xff;
|
||||
put_unaligned_le16(addr16, address);
|
||||
|
||||
return i2c_smbus_write_block_data(client,
|
||||
ZIIRAVE_CMD_DOWNLOAD_SET_READ_ADDR,
|
||||
ARRAY_SIZE(address), address);
|
||||
sizeof(address), address);
|
||||
}
|
||||
|
||||
static int ziirave_firm_write_block_data(struct watchdog_device *wdd,
|
||||
u8 command, u8 length, const u8 *data,
|
||||
bool wait_for_ack)
|
||||
static bool ziirave_firm_addr_readonly(u32 addr)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(wdd->parent);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_block_data(client, command, length, data);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to send command 0x%02x: %d\n", command, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (wait_for_ack)
|
||||
ret = ziirave_firm_wait_for_ack(wdd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
|
||||
u8 byte, bool wait_for_ack)
|
||||
{
|
||||
return ziirave_firm_write_block_data(wdd, command, 1, &byte,
|
||||
wait_for_ack);
|
||||
return addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
|
||||
addr > ZIIRAVE_FIRM_FLASH_MEMORY_END;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -240,35 +215,53 @@ static int ziirave_firm_write_byte(struct watchdog_device *wdd, u8 command,
|
||||
* Data0 .. Data15: Array of 16 bytes of data.
|
||||
* Checksum: Checksum byte to verify data integrity.
|
||||
*/
|
||||
static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
|
||||
const struct ihex_binrec *rec)
|
||||
static int __ziirave_firm_write_pkt(struct watchdog_device *wdd,
|
||||
u32 addr, const u8 *data, u8 len)
|
||||
{
|
||||
const u16 addr16 = (u16)addr / 2;
|
||||
struct i2c_client *client = to_i2c_client(wdd->parent);
|
||||
u8 i, checksum = 0, packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE];
|
||||
int ret;
|
||||
u16 addr;
|
||||
|
||||
memset(packet, 0, ARRAY_SIZE(packet));
|
||||
/* Check max data size */
|
||||
if (len > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
|
||||
dev_err(&client->dev, "Firmware packet too long (%d)\n",
|
||||
len);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ignore packets that are targeting program memory outisde of
|
||||
* app partition, since they will be ignored by the
|
||||
* bootloader. At the same time, we need to make sure we'll
|
||||
* allow zero length packet that will be sent as the last step
|
||||
* of firmware update
|
||||
*/
|
||||
if (len && ziirave_firm_addr_readonly(addr))
|
||||
return 0;
|
||||
|
||||
/* Packet length */
|
||||
packet[0] = (u8)be16_to_cpu(rec->len);
|
||||
packet[0] = len;
|
||||
/* Packet address */
|
||||
addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
|
||||
packet[1] = addr & 0xff;
|
||||
packet[2] = (addr & 0xff00) >> 8;
|
||||
put_unaligned_le16(addr16, packet + 1);
|
||||
|
||||
/* Packet data */
|
||||
if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE)
|
||||
return -EMSGSIZE;
|
||||
memcpy(packet + 3, rec->data, be16_to_cpu(rec->len));
|
||||
memcpy(packet + 3, data, len);
|
||||
memset(packet + 3 + len, 0, ZIIRAVE_FIRM_PKT_DATA_SIZE - len);
|
||||
|
||||
/* Packet checksum */
|
||||
for (i = 0; i < ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1; i++)
|
||||
for (i = 0; i < len + 3; i++)
|
||||
checksum += packet[i];
|
||||
packet[ZIIRAVE_FIRM_PKT_TOTAL_SIZE - 1] = checksum;
|
||||
|
||||
ret = ziirave_firm_write_block_data(wdd, ZIIRAVE_CMD_DOWNLOAD_PACKET,
|
||||
ARRAY_SIZE(packet), packet, true);
|
||||
ret = i2c_smbus_write_block_data(client, ZIIRAVE_CMD_DOWNLOAD_PACKET,
|
||||
sizeof(packet), packet);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to send DOWNLOAD_PACKET: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ziirave_firm_read_ack(wdd);
|
||||
if (ret)
|
||||
dev_err(&client->dev,
|
||||
"Failed to write firmware packet at address 0x%04x: %d\n",
|
||||
@ -277,6 +270,30 @@ static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ziirave_firm_write_pkt(struct watchdog_device *wdd,
|
||||
u32 addr, const u8 *data, u8 len)
|
||||
{
|
||||
const u8 max_write_len = ZIIRAVE_FIRM_PAGE_SIZE -
|
||||
(addr - ALIGN_DOWN(addr, ZIIRAVE_FIRM_PAGE_SIZE));
|
||||
int ret;
|
||||
|
||||
if (len > max_write_len) {
|
||||
/*
|
||||
* If data crossed page boundary we need to split this
|
||||
* write in two
|
||||
*/
|
||||
ret = __ziirave_firm_write_pkt(wdd, addr, data, max_write_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
addr += max_write_len;
|
||||
data += max_write_len;
|
||||
len -= max_write_len;
|
||||
}
|
||||
|
||||
return __ziirave_firm_write_pkt(wdd, addr, data, len);
|
||||
}
|
||||
|
||||
static int ziirave_firm_verify(struct watchdog_device *wdd,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
@ -284,16 +301,12 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
|
||||
const struct ihex_binrec *rec;
|
||||
int i, ret;
|
||||
u8 data[ZIIRAVE_FIRM_PKT_DATA_SIZE];
|
||||
u16 addr;
|
||||
|
||||
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
|
||||
/* Zero length marks end of records */
|
||||
if (!be16_to_cpu(rec->len))
|
||||
break;
|
||||
const u16 len = be16_to_cpu(rec->len);
|
||||
const u32 addr = be32_to_cpu(rec->addr);
|
||||
|
||||
addr = (be32_to_cpu(rec->addr) & 0xffff) >> 1;
|
||||
if (addr < ZIIRAVE_FIRM_FLASH_MEMORY_START ||
|
||||
addr > ZIIRAVE_FIRM_FLASH_MEMORY_END)
|
||||
if (ziirave_firm_addr_readonly(addr))
|
||||
continue;
|
||||
|
||||
ret = ziirave_firm_set_read_addr(wdd, addr);
|
||||
@ -304,7 +317,7 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data); i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
ret = i2c_smbus_read_byte_data(client,
|
||||
ZIIRAVE_CMD_DOWNLOAD_READ_BYTE);
|
||||
if (ret < 0) {
|
||||
@ -315,7 +328,7 @@ static int ziirave_firm_verify(struct watchdog_device *wdd,
|
||||
data[i] = ret;
|
||||
}
|
||||
|
||||
if (memcmp(data, rec->data, be16_to_cpu(rec->len))) {
|
||||
if (memcmp(data, rec->data, len)) {
|
||||
dev_err(&client->dev,
|
||||
"Firmware mismatch at address 0x%04x\n", addr);
|
||||
return -EINVAL;
|
||||
@ -329,97 +342,45 @@ static int ziirave_firm_upload(struct watchdog_device *wdd,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(wdd->parent);
|
||||
int ret, words_till_page_break;
|
||||
const struct ihex_binrec *rec;
|
||||
struct ihex_binrec *rec_new;
|
||||
int ret;
|
||||
|
||||
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_JUMP_TO_BOOTLOADER, 1,
|
||||
false);
|
||||
if (ret)
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ZIIRAVE_CMD_JUMP_TO_BOOTLOADER,
|
||||
ZIIRAVE_CMD_JUMP_TO_BOOTLOADER_MAGIC);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to jump to bootloader\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(500);
|
||||
|
||||
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_START, 1, true);
|
||||
if (ret)
|
||||
ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_START);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to start download\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ziirave_firm_read_ack(wdd);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "No ACK for start download\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(500);
|
||||
|
||||
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
|
||||
/* Zero length marks end of records */
|
||||
if (!be16_to_cpu(rec->len))
|
||||
break;
|
||||
|
||||
/* Check max data size */
|
||||
if (be16_to_cpu(rec->len) > ZIIRAVE_FIRM_PKT_DATA_SIZE) {
|
||||
dev_err(&client->dev, "Firmware packet too long (%d)\n",
|
||||
be16_to_cpu(rec->len));
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
/* Calculate words till page break */
|
||||
words_till_page_break = (64 - ((be32_to_cpu(rec->addr) >> 1) &
|
||||
0x3f));
|
||||
if ((be16_to_cpu(rec->len) >> 1) > words_till_page_break) {
|
||||
/*
|
||||
* Data in passes page boundary, so we need to split in
|
||||
* two blocks of data. Create a packet with the first
|
||||
* block of data.
|
||||
*/
|
||||
rec_new = kzalloc(sizeof(struct ihex_binrec) +
|
||||
(words_till_page_break << 1),
|
||||
GFP_KERNEL);
|
||||
if (!rec_new)
|
||||
return -ENOMEM;
|
||||
|
||||
rec_new->len = cpu_to_be16(words_till_page_break << 1);
|
||||
rec_new->addr = rec->addr;
|
||||
memcpy(rec_new->data, rec->data,
|
||||
be16_to_cpu(rec_new->len));
|
||||
|
||||
ret = ziirave_firm_write_pkt(wdd, rec_new);
|
||||
kfree(rec_new);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Create a packet with the second block of data */
|
||||
rec_new = kzalloc(sizeof(struct ihex_binrec) +
|
||||
be16_to_cpu(rec->len) -
|
||||
(words_till_page_break << 1),
|
||||
GFP_KERNEL);
|
||||
if (!rec_new)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Remaining bytes */
|
||||
rec_new->len = rec->len -
|
||||
cpu_to_be16(words_till_page_break << 1);
|
||||
|
||||
rec_new->addr = cpu_to_be32(be32_to_cpu(rec->addr) +
|
||||
(words_till_page_break << 1));
|
||||
|
||||
memcpy(rec_new->data,
|
||||
rec->data + (words_till_page_break << 1),
|
||||
be16_to_cpu(rec_new->len));
|
||||
|
||||
ret = ziirave_firm_write_pkt(wdd, rec_new);
|
||||
kfree(rec_new);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
ret = ziirave_firm_write_pkt(wdd, rec);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = ziirave_firm_write_pkt(wdd, be32_to_cpu(rec->addr),
|
||||
rec->data, be16_to_cpu(rec->len));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* For end of download, the length field will be set to 0 */
|
||||
rec_new = kzalloc(sizeof(struct ihex_binrec) + 1, GFP_KERNEL);
|
||||
if (!rec_new)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ziirave_firm_write_pkt(wdd, rec_new);
|
||||
kfree(rec_new);
|
||||
/*
|
||||
* Finish firmware download process by sending a zero length
|
||||
* payload
|
||||
*/
|
||||
ret = ziirave_firm_write_pkt(wdd, 0, NULL, 0);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to send EMPTY packet: %d\n", ret);
|
||||
return ret;
|
||||
@ -437,15 +398,22 @@ static int ziirave_firm_upload(struct watchdog_device *wdd,
|
||||
}
|
||||
|
||||
/* End download operation */
|
||||
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_DOWNLOAD_END, 1, false);
|
||||
if (ret)
|
||||
ret = i2c_smbus_write_byte(client, ZIIRAVE_CMD_DOWNLOAD_END);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to end firmware download: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset the processor */
|
||||
ret = ziirave_firm_write_byte(wdd, ZIIRAVE_CMD_RESET_PROCESSOR, 1,
|
||||
false);
|
||||
if (ret)
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
ZIIRAVE_CMD_RESET_PROCESSOR,
|
||||
ZIIRAVE_CMD_RESET_PROCESSOR_MAGIC);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to reset the watchdog: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
msleep(500);
|
||||
|
||||
@ -478,7 +446,7 @@ static ssize_t ziirave_wdt_sysfs_show_firm(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(buf, "02.%02u.%02u", w_priv->firmware_rev.major,
|
||||
ret = sprintf(buf, ZIIRAVE_FW_VERSION_FMT, w_priv->firmware_rev.major,
|
||||
w_priv->firmware_rev.minor);
|
||||
|
||||
mutex_unlock(&w_priv->sysfs_mutex);
|
||||
@ -501,7 +469,7 @@ static ssize_t ziirave_wdt_sysfs_show_boot(struct device *dev,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprintf(buf, "01.%02u.%02u", w_priv->bootloader_rev.major,
|
||||
ret = sprintf(buf, ZIIRAVE_BL_VERSION_FMT, w_priv->bootloader_rev.major,
|
||||
w_priv->bootloader_rev.minor);
|
||||
|
||||
mutex_unlock(&w_priv->sysfs_mutex);
|
||||
@ -568,7 +536,8 @@ static ssize_t ziirave_wdt_sysfs_store_firm(struct device *dev,
|
||||
goto unlock_mutex;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "Firmware updated to version 02.%02u.%02u\n",
|
||||
dev_info(&client->dev,
|
||||
"Firmware updated to version " ZIIRAVE_FW_VERSION_FMT "\n",
|
||||
w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
|
||||
|
||||
/* Restore the watchdog timeout */
|
||||
@ -611,7 +580,7 @@ static int ziirave_wdt_init_duration(struct i2c_client *client)
|
||||
&reset_duration);
|
||||
if (ret) {
|
||||
dev_info(&client->dev,
|
||||
"Unable to set reset pulse duration, using default\n");
|
||||
"No reset pulse duration specified, using default\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -633,7 +602,10 @@ static int ziirave_wdt_probe(struct i2c_client *client,
|
||||
struct ziirave_wdt_data *w_priv;
|
||||
int val;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_WRITE_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
w_priv = devm_kzalloc(&client->dev, sizeof(*w_priv), GFP_KERNEL);
|
||||
@ -658,57 +630,80 @@ static int ziirave_wdt_probe(struct i2c_client *client,
|
||||
*/
|
||||
if (w_priv->wdd.timeout == 0) {
|
||||
val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_TIMEOUT);
|
||||
if (val < 0)
|
||||
if (val < 0) {
|
||||
dev_err(&client->dev, "Failed to read timeout\n");
|
||||
return val;
|
||||
}
|
||||
|
||||
if (val < ZIIRAVE_TIMEOUT_MIN)
|
||||
return -ENODEV;
|
||||
if (val > ZIIRAVE_TIMEOUT_MAX ||
|
||||
val < ZIIRAVE_TIMEOUT_MIN)
|
||||
val = ZIIRAVE_TIMEOUT_DEFAULT;
|
||||
|
||||
w_priv->wdd.timeout = val;
|
||||
} else {
|
||||
ret = ziirave_wdt_set_timeout(&w_priv->wdd,
|
||||
w_priv->wdd.timeout);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(&client->dev, "Timeout set to %ds.",
|
||||
w_priv->wdd.timeout);
|
||||
}
|
||||
|
||||
ret = ziirave_wdt_set_timeout(&w_priv->wdd, w_priv->wdd.timeout);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to set timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "Timeout set to %ds\n", w_priv->wdd.timeout);
|
||||
|
||||
watchdog_set_nowayout(&w_priv->wdd, nowayout);
|
||||
|
||||
i2c_set_clientdata(client, w_priv);
|
||||
|
||||
/* If in unconfigured state, set to stopped */
|
||||
val = i2c_smbus_read_byte_data(client, ZIIRAVE_WDT_STATE);
|
||||
if (val < 0)
|
||||
if (val < 0) {
|
||||
dev_err(&client->dev, "Failed to read state\n");
|
||||
return val;
|
||||
}
|
||||
|
||||
if (val == ZIIRAVE_STATE_INITIAL)
|
||||
ziirave_wdt_stop(&w_priv->wdd);
|
||||
|
||||
ret = ziirave_wdt_init_duration(client);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to init duration\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ziirave_wdt_revision(client, &w_priv->firmware_rev,
|
||||
ZIIRAVE_WDT_FIRM_VER_MAJOR);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to read firmware version\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&client->dev,
|
||||
"Firmware version: " ZIIRAVE_FW_VERSION_FMT "\n",
|
||||
w_priv->firmware_rev.major, w_priv->firmware_rev.minor);
|
||||
|
||||
ret = ziirave_wdt_revision(client, &w_priv->bootloader_rev,
|
||||
ZIIRAVE_WDT_BOOT_VER_MAJOR);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "Failed to read bootloader version\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_info(&client->dev,
|
||||
"Bootloader version: " ZIIRAVE_BL_VERSION_FMT "\n",
|
||||
w_priv->bootloader_rev.major, w_priv->bootloader_rev.minor);
|
||||
|
||||
w_priv->reset_reason = i2c_smbus_read_byte_data(client,
|
||||
ZIIRAVE_WDT_RESET_REASON);
|
||||
if (w_priv->reset_reason < 0)
|
||||
if (w_priv->reset_reason < 0) {
|
||||
dev_err(&client->dev, "Failed to read reset reason\n");
|
||||
return w_priv->reset_reason;
|
||||
}
|
||||
|
||||
if (w_priv->reset_reason >= ARRAY_SIZE(ziirave_reasons) ||
|
||||
!ziirave_reasons[w_priv->reset_reason])
|
||||
!ziirave_reasons[w_priv->reset_reason]) {
|
||||
dev_err(&client->dev, "Invalid reset reason\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = watchdog_register_device(&w_priv->wdd);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user