|
8 | 8 | * Copyright (c) 2019 Advantech
|
9 | 9 | * Author: Amy.Shih <[email protected]>
|
10 | 10 | *
|
| 11 | + * Copyright (c) 2020 Advantech |
| 12 | + * Author: Yuechao Zhao <[email protected]> |
| 13 | + * |
11 | 14 | * Supports the following chips:
|
12 | 15 | *
|
13 | 16 | * Chip #vin #fan #pwm #temp #dts chip ID
|
|
20 | 23 | #include <linux/i2c.h>
|
21 | 24 | #include <linux/mutex.h>
|
22 | 25 | #include <linux/hwmon.h>
|
| 26 | +#include <linux/watchdog.h> |
23 | 27 |
|
24 | 28 | #define VENDOR_ID_REG 0x7A /* Any bank */
|
25 | 29 | #define NUVOTON_ID 0x50
|
|
88 | 92 | #define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */
|
89 | 93 | #define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */
|
90 | 94 |
|
| 95 | +#define WDT_LOCK_REG 0xE0 /* W/O Lock Watchdog Register */ |
| 96 | +#define WDT_EN_REG 0xE1 /* R/O Watchdog Enable Register */ |
| 97 | +#define WDT_STS_REG 0xE2 /* R/O Watchdog Status Register */ |
| 98 | +#define WDT_TIMER_REG 0xE3 /* R/W Watchdog Timer Register */ |
| 99 | +#define WDT_SOFT_EN 0x55 /* Enable soft watchdog timer */ |
| 100 | +#define WDT_SOFT_DIS 0xAA /* Disable soft watchdog timer */ |
| 101 | + |
91 | 102 | #define VOLT_MONITOR_MODE 0x0
|
92 | 103 | #define THERMAL_DIODE_MODE 0x1
|
93 | 104 | #define THERMISTOR_MODE 0x3
|
94 | 105 |
|
95 | 106 | #define ENABLE_TSI BIT(1)
|
96 | 107 |
|
| 108 | +#define WATCHDOG_TIMEOUT 1 /* 1 minute default timeout */ |
| 109 | + |
| 110 | +/*The timeout range is 1-255 minutes*/ |
| 111 | +#define MIN_TIMEOUT (1 * 60) |
| 112 | +#define MAX_TIMEOUT (255 * 60) |
| 113 | + |
| 114 | +static int timeout = WATCHDOG_TIMEOUT; |
| 115 | +module_param(timeout, int, 0); |
| 116 | +MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes. 1 <= timeout <= 255, default=" |
| 117 | + __MODULE_STRING(WATCHODOG_TIMEOUT) "."); |
| 118 | + |
| 119 | +static bool nowayout = WATCHDOG_NOWAYOUT; |
| 120 | +module_param(nowayout, bool, 0); |
| 121 | +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started once started (default=" |
| 122 | + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
| 123 | + |
97 | 124 | static const unsigned short normal_i2c[] = {
|
98 | 125 | 0x2d, 0x2e, I2C_CLIENT_END
|
99 | 126 | };
|
100 | 127 |
|
101 | 128 | struct nct7904_data {
|
102 | 129 | struct i2c_client *client;
|
| 130 | + struct watchdog_device wdt; |
103 | 131 | struct mutex bank_lock;
|
104 | 132 | int bank_sel;
|
105 | 133 | u32 fanin_mask;
|
@@ -892,6 +920,95 @@ static const struct hwmon_chip_info nct7904_chip_info = {
|
892 | 920 | .info = nct7904_info,
|
893 | 921 | };
|
894 | 922 |
|
| 923 | +/* |
| 924 | + * Watchdog Function |
| 925 | + */ |
| 926 | +static int nct7904_wdt_start(struct watchdog_device *wdt) |
| 927 | +{ |
| 928 | + struct nct7904_data *data = watchdog_get_drvdata(wdt); |
| 929 | + |
| 930 | + /* Enable soft watchdog timer */ |
| 931 | + return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN); |
| 932 | +} |
| 933 | + |
| 934 | +static int nct7904_wdt_stop(struct watchdog_device *wdt) |
| 935 | +{ |
| 936 | + struct nct7904_data *data = watchdog_get_drvdata(wdt); |
| 937 | + |
| 938 | + return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS); |
| 939 | +} |
| 940 | + |
| 941 | +static int nct7904_wdt_set_timeout(struct watchdog_device *wdt, |
| 942 | + unsigned int timeout) |
| 943 | +{ |
| 944 | + struct nct7904_data *data = watchdog_get_drvdata(wdt); |
| 945 | + /* |
| 946 | + * The NCT7904 is very special in watchdog function. |
| 947 | + * Its minimum unit is minutes. And wdt->timeout needs |
| 948 | + * to match the actual timeout selected. So, this needs |
| 949 | + * to be: wdt->timeout = timeout / 60 * 60. |
| 950 | + * For example, if the user configures a timeout of |
| 951 | + * 119 seconds, the actual timeout will be 60 seconds. |
| 952 | + * So, wdt->timeout must then be set to 60 seconds. |
| 953 | + */ |
| 954 | + wdt->timeout = timeout / 60 * 60; |
| 955 | + |
| 956 | + return nct7904_write_reg(data, BANK_0, WDT_TIMER_REG, |
| 957 | + wdt->timeout / 60); |
| 958 | +} |
| 959 | + |
| 960 | +static int nct7904_wdt_ping(struct watchdog_device *wdt) |
| 961 | +{ |
| 962 | + /* |
| 963 | + * Note: |
| 964 | + * NCT7904 does not support refreshing WDT_TIMER_REG register when |
| 965 | + * the watchdog is active. Please disable watchdog before feeding |
| 966 | + * the watchdog and enable it again. |
| 967 | + */ |
| 968 | + struct nct7904_data *data = watchdog_get_drvdata(wdt); |
| 969 | + int ret; |
| 970 | + |
| 971 | + /* Disable soft watchdog timer */ |
| 972 | + ret = nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS); |
| 973 | + if (ret < 0) |
| 974 | + return ret; |
| 975 | + |
| 976 | + /* feed watchdog */ |
| 977 | + ret = nct7904_write_reg(data, BANK_0, WDT_TIMER_REG, wdt->timeout / 60); |
| 978 | + if (ret < 0) |
| 979 | + return ret; |
| 980 | + |
| 981 | + /* Enable soft watchdog timer */ |
| 982 | + return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN); |
| 983 | +} |
| 984 | + |
| 985 | +static unsigned int nct7904_wdt_get_timeleft(struct watchdog_device *wdt) |
| 986 | +{ |
| 987 | + struct nct7904_data *data = watchdog_get_drvdata(wdt); |
| 988 | + int ret; |
| 989 | + |
| 990 | + ret = nct7904_read_reg(data, BANK_0, WDT_TIMER_REG); |
| 991 | + if (ret < 0) |
| 992 | + return 0; |
| 993 | + |
| 994 | + return ret * 60; |
| 995 | +} |
| 996 | + |
| 997 | +static const struct watchdog_info nct7904_wdt_info = { |
| 998 | + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | |
| 999 | + WDIOF_MAGICCLOSE, |
| 1000 | + .identity = "nct7904 watchdog", |
| 1001 | +}; |
| 1002 | + |
| 1003 | +static const struct watchdog_ops nct7904_wdt_ops = { |
| 1004 | + .owner = THIS_MODULE, |
| 1005 | + .start = nct7904_wdt_start, |
| 1006 | + .stop = nct7904_wdt_stop, |
| 1007 | + .ping = nct7904_wdt_ping, |
| 1008 | + .set_timeout = nct7904_wdt_set_timeout, |
| 1009 | + .get_timeleft = nct7904_wdt_get_timeleft, |
| 1010 | +}; |
| 1011 | + |
895 | 1012 | static int nct7904_probe(struct i2c_client *client,
|
896 | 1013 | const struct i2c_device_id *id)
|
897 | 1014 | {
|
@@ -1022,7 +1139,26 @@ static int nct7904_probe(struct i2c_client *client,
|
1022 | 1139 | hwmon_dev =
|
1023 | 1140 | devm_hwmon_device_register_with_info(dev, client->name, data,
|
1024 | 1141 | &nct7904_chip_info, NULL);
|
1025 |
| - return PTR_ERR_OR_ZERO(hwmon_dev); |
| 1142 | + ret = PTR_ERR_OR_ZERO(hwmon_dev); |
| 1143 | + if (ret) |
| 1144 | + return ret; |
| 1145 | + |
| 1146 | + /* Watchdog initialization */ |
| 1147 | + data->wdt.ops = &nct7904_wdt_ops; |
| 1148 | + data->wdt.info = &nct7904_wdt_info; |
| 1149 | + |
| 1150 | + data->wdt.timeout = timeout * 60; /* in seconds */ |
| 1151 | + data->wdt.min_timeout = MIN_TIMEOUT; |
| 1152 | + data->wdt.max_timeout = MAX_TIMEOUT; |
| 1153 | + data->wdt.parent = &client->dev; |
| 1154 | + |
| 1155 | + watchdog_init_timeout(&data->wdt, timeout * 60, &client->dev); |
| 1156 | + watchdog_set_nowayout(&data->wdt, nowayout); |
| 1157 | + watchdog_set_drvdata(&data->wdt, data); |
| 1158 | + |
| 1159 | + watchdog_stop_on_unregister(&data->wdt); |
| 1160 | + |
| 1161 | + return devm_watchdog_register_device(dev, &data->wdt); |
1026 | 1162 | }
|
1027 | 1163 |
|
1028 | 1164 | static const struct i2c_device_id nct7904_id[] = {
|
|
0 commit comments