diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2012-08-16 12:39:43 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2012-08-16 12:51:02 -0400 |
commit | c216fdeb2e7371554c56ba457c374cce9c77f91a (patch) | |
tree | 9d88b3170a5470844e61ac90a30fd923d50df14b /drivers/misc | |
parent | 248ffdf7c95726a8dae76e25fdb037899c5b77fa (diff) |
mei: wd: decouple and revamp watchdog state machine
Before ME watchdog was exported through standard watchdog interface
it was closed and started together with the mei device.
The major issue is that closing ME watchdog disabled also MEI device,
to fix this the watchdog state machine has to be independent from MEI
state machine.
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/mei/init.c | 1 | ||||
-rw-r--r-- | drivers/misc/mei/interface.h | 2 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 9 | ||||
-rw-r--r-- | drivers/misc/mei/main.c | 9 | ||||
-rw-r--r-- | drivers/misc/mei/mei_dev.h | 14 | ||||
-rw-r--r-- | drivers/misc/mei/wd.c | 26 |
6 files changed, 34 insertions, 27 deletions
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index cd6a7f1ff916..98f1430e3e14 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c | |||
@@ -330,7 +330,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) | |||
330 | 330 | ||
331 | dev->me_clients_num = 0; | 331 | dev->me_clients_num = 0; |
332 | dev->rd_msg_hdr = 0; | 332 | dev->rd_msg_hdr = 0; |
333 | dev->stop = false; | ||
334 | dev->wd_pending = false; | 333 | dev->wd_pending = false; |
335 | 334 | ||
336 | /* update the state of the registers after reset */ | 335 | /* update the state of the registers after reset */ |
diff --git a/drivers/misc/mei/interface.h b/drivers/misc/mei/interface.h index c1988f564aa2..ec6c785a3961 100644 --- a/drivers/misc/mei/interface.h +++ b/drivers/misc/mei/interface.h | |||
@@ -56,7 +56,7 @@ int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl); | |||
56 | 56 | ||
57 | 57 | ||
58 | int mei_wd_send(struct mei_device *dev); | 58 | int mei_wd_send(struct mei_device *dev); |
59 | int mei_wd_stop(struct mei_device *dev, bool preserve); | 59 | int mei_wd_stop(struct mei_device *dev); |
60 | int mei_wd_host_init(struct mei_device *dev); | 60 | int mei_wd_host_init(struct mei_device *dev); |
61 | /* | 61 | /* |
62 | * mei_watchdog_register - Registering watchdog interface | 62 | * mei_watchdog_register - Registering watchdog interface |
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 0f25cee6ab85..0900a711badd 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c | |||
@@ -1224,10 +1224,9 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, | |||
1224 | } | 1224 | } |
1225 | } | 1225 | } |
1226 | 1226 | ||
1227 | if (dev->stop && !dev->wd_pending) { | 1227 | if (dev->wd_state == MEI_WD_STOPPING) { |
1228 | dev->wd_stopped = true; | 1228 | dev->wd_state = MEI_WD_IDLE; |
1229 | wake_up_interruptible(&dev->wait_stop_wd); | 1229 | wake_up_interruptible(&dev->wait_stop_wd); |
1230 | return 0; | ||
1231 | } | 1230 | } |
1232 | 1231 | ||
1233 | if (dev->extra_write_index) { | 1232 | if (dev->extra_write_index) { |
@@ -1250,14 +1249,12 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list, | |||
1250 | 1249 | ||
1251 | dev->wd_pending = false; | 1250 | dev->wd_pending = false; |
1252 | 1251 | ||
1253 | if (dev->wd_timeout) | 1252 | if (dev->wd_state == MEI_WD_RUNNING) |
1254 | *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE); | 1253 | *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE); |
1255 | else | 1254 | else |
1256 | *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE); | 1255 | *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE); |
1257 | } | 1256 | } |
1258 | } | 1257 | } |
1259 | if (dev->stop) | ||
1260 | return -ENODEV; | ||
1261 | 1258 | ||
1262 | /* complete control write list CB */ | 1259 | /* complete control write list CB */ |
1263 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); | 1260 | dev_dbg(&dev->pdev->dev, "complete control write list cb.\n"); |
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index 5c557dd129d6..9a595338ae15 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c | |||
@@ -1060,7 +1060,9 @@ static void __devexit mei_remove(struct pci_dev *pdev) | |||
1060 | 1060 | ||
1061 | mutex_lock(&dev->device_lock); | 1061 | mutex_lock(&dev->device_lock); |
1062 | 1062 | ||
1063 | mei_wd_stop(dev, false); | 1063 | cancel_delayed_work(&dev->timer_work); |
1064 | |||
1065 | mei_wd_stop(dev); | ||
1064 | 1066 | ||
1065 | mei_device = NULL; | 1067 | mei_device = NULL; |
1066 | 1068 | ||
@@ -1115,8 +1117,11 @@ static int mei_pci_suspend(struct device *device) | |||
1115 | if (!dev) | 1117 | if (!dev) |
1116 | return -ENODEV; | 1118 | return -ENODEV; |
1117 | mutex_lock(&dev->device_lock); | 1119 | mutex_lock(&dev->device_lock); |
1120 | |||
1121 | cancel_delayed_work(&dev->timer_work); | ||
1122 | |||
1118 | /* Stop watchdog if exists */ | 1123 | /* Stop watchdog if exists */ |
1119 | err = mei_wd_stop(dev, true); | 1124 | err = mei_wd_stop(dev); |
1120 | /* Set new mei state */ | 1125 | /* Set new mei state */ |
1121 | if (dev->dev_state == MEI_DEV_ENABLED || | 1126 | if (dev->dev_state == MEI_DEV_ENABLED || |
1122 | dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { | 1127 | dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) { |
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 64a4f17893e5..c8660c0eb1c7 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h | |||
@@ -33,6 +33,8 @@ | |||
33 | #define MEI_WD_MIN_TIMEOUT 120 /* seconds */ | 33 | #define MEI_WD_MIN_TIMEOUT 120 /* seconds */ |
34 | #define MEI_WD_MAX_TIMEOUT 65535 /* seconds */ | 34 | #define MEI_WD_MAX_TIMEOUT 65535 /* seconds */ |
35 | 35 | ||
36 | #define MEI_WD_STOP_TIMEOUT 10 /* msecs */ | ||
37 | |||
36 | #define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) | 38 | #define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0) |
37 | 39 | ||
38 | #define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) | 40 | #define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32)) |
@@ -120,6 +122,12 @@ enum mei_file_transaction_states { | |||
120 | MEI_READ_COMPLETE | 122 | MEI_READ_COMPLETE |
121 | }; | 123 | }; |
122 | 124 | ||
125 | enum mei_wd_states { | ||
126 | MEI_WD_IDLE, | ||
127 | MEI_WD_RUNNING, | ||
128 | MEI_WD_STOPPING, | ||
129 | }; | ||
130 | |||
123 | /* MEI CB */ | 131 | /* MEI CB */ |
124 | enum mei_cb_major_types { | 132 | enum mei_cb_major_types { |
125 | MEI_READ = 0, | 133 | MEI_READ = 0, |
@@ -228,7 +236,6 @@ struct mei_device { | |||
228 | enum mei_dev_state dev_state; | 236 | enum mei_dev_state dev_state; |
229 | enum mei_init_clients_states init_clients_state; | 237 | enum mei_init_clients_states init_clients_state; |
230 | u16 init_clients_timer; | 238 | u16 init_clients_timer; |
231 | bool stop; | ||
232 | bool need_reset; | 239 | bool need_reset; |
233 | 240 | ||
234 | u32 extra_write_index; | 241 | u32 extra_write_index; |
@@ -248,11 +255,10 @@ struct mei_device { | |||
248 | bool mei_host_buffer_is_empty; | 255 | bool mei_host_buffer_is_empty; |
249 | 256 | ||
250 | struct mei_cl wd_cl; | 257 | struct mei_cl wd_cl; |
258 | enum mei_wd_states wd_state; | ||
251 | bool wd_interface_reg; | 259 | bool wd_interface_reg; |
252 | bool wd_pending; | 260 | bool wd_pending; |
253 | bool wd_stopped; | 261 | u16 wd_timeout; |
254 | bool wd_bypass; /* if false, don't refresh watchdog ME client */ | ||
255 | u16 wd_timeout; /* seconds ((wd_data[1] << 8) + wd_data[0]) */ | ||
256 | unsigned char wd_data[MEI_WD_START_MSG_SIZE]; | 262 | unsigned char wd_data[MEI_WD_START_MSG_SIZE]; |
257 | 263 | ||
258 | 264 | ||
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c index 755a58305a7e..0824166a7303 100644 --- a/drivers/misc/mei/wd.c +++ b/drivers/misc/mei/wd.c | |||
@@ -67,6 +67,7 @@ int mei_wd_host_init(struct mei_device *dev) | |||
67 | /* look for WD client and connect to it */ | 67 | /* look for WD client and connect to it */ |
68 | dev->wd_cl.state = MEI_FILE_DISCONNECTED; | 68 | dev->wd_cl.state = MEI_FILE_DISCONNECTED; |
69 | dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; | 69 | dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT; |
70 | dev->wd_state = MEI_WD_IDLE; | ||
70 | 71 | ||
71 | /* find ME WD client */ | 72 | /* find ME WD client */ |
72 | mei_me_cl_update_filext(dev, &dev->wd_cl, | 73 | mei_me_cl_update_filext(dev, &dev->wd_cl, |
@@ -128,18 +129,17 @@ int mei_wd_send(struct mei_device *dev) | |||
128 | * -EIO when message send fails | 129 | * -EIO when message send fails |
129 | * -EINVAL when invalid message is to be sent | 130 | * -EINVAL when invalid message is to be sent |
130 | */ | 131 | */ |
131 | int mei_wd_stop(struct mei_device *dev, bool preserve) | 132 | int mei_wd_stop(struct mei_device *dev) |
132 | { | 133 | { |
133 | int ret; | 134 | int ret; |
134 | u16 wd_timeout = dev->wd_timeout; | ||
135 | 135 | ||
136 | cancel_delayed_work(&dev->timer_work); | 136 | if (dev->wd_cl.state != MEI_FILE_CONNECTED || |
137 | if (dev->wd_cl.state != MEI_FILE_CONNECTED || !dev->wd_timeout) | 137 | dev->wd_state != MEI_WD_RUNNING) |
138 | return 0; | 138 | return 0; |
139 | 139 | ||
140 | dev->wd_timeout = 0; | ||
141 | memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); | 140 | memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE); |
142 | dev->stop = true; | 141 | |
142 | dev->wd_state = MEI_WD_STOPPING; | ||
143 | 143 | ||
144 | ret = mei_flow_ctrl_creds(dev, &dev->wd_cl); | 144 | ret = mei_flow_ctrl_creds(dev, &dev->wd_cl); |
145 | if (ret < 0) | 145 | if (ret < 0) |
@@ -161,13 +161,14 @@ int mei_wd_stop(struct mei_device *dev, bool preserve) | |||
161 | } else { | 161 | } else { |
162 | dev->wd_pending = true; | 162 | dev->wd_pending = true; |
163 | } | 163 | } |
164 | dev->wd_stopped = false; | 164 | |
165 | mutex_unlock(&dev->device_lock); | 165 | mutex_unlock(&dev->device_lock); |
166 | 166 | ||
167 | ret = wait_event_interruptible_timeout(dev->wait_stop_wd, | 167 | ret = wait_event_interruptible_timeout(dev->wait_stop_wd, |
168 | dev->wd_stopped, 10 * HZ); | 168 | dev->wd_state == MEI_WD_IDLE, |
169 | msecs_to_jiffies(MEI_WD_STOP_TIMEOUT)); | ||
169 | mutex_lock(&dev->device_lock); | 170 | mutex_lock(&dev->device_lock); |
170 | if (dev->wd_stopped) { | 171 | if (dev->wd_state == MEI_WD_IDLE) { |
171 | dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret); | 172 | dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret); |
172 | ret = 0; | 173 | ret = 0; |
173 | } else { | 174 | } else { |
@@ -177,9 +178,6 @@ int mei_wd_stop(struct mei_device *dev, bool preserve) | |||
177 | "wd: stop failed to complete ret=%d.\n", ret); | 178 | "wd: stop failed to complete ret=%d.\n", ret); |
178 | } | 179 | } |
179 | 180 | ||
180 | if (preserve) | ||
181 | dev->wd_timeout = wd_timeout; | ||
182 | |||
183 | out: | 181 | out: |
184 | return ret; | 182 | return ret; |
185 | } | 183 | } |
@@ -239,7 +237,7 @@ static int mei_wd_ops_stop(struct watchdog_device *wd_dev) | |||
239 | return -ENODEV; | 237 | return -ENODEV; |
240 | 238 | ||
241 | mutex_lock(&dev->device_lock); | 239 | mutex_lock(&dev->device_lock); |
242 | mei_wd_stop(dev, false); | 240 | mei_wd_stop(dev); |
243 | mutex_unlock(&dev->device_lock); | 241 | mutex_unlock(&dev->device_lock); |
244 | 242 | ||
245 | return 0; | 243 | return 0; |
@@ -269,6 +267,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev) | |||
269 | goto end; | 267 | goto end; |
270 | } | 268 | } |
271 | 269 | ||
270 | dev->wd_state = MEI_WD_RUNNING; | ||
271 | |||
272 | /* Check if we can send the ping to HW*/ | 272 | /* Check if we can send the ping to HW*/ |
273 | if (dev->mei_host_buffer_is_empty && | 273 | if (dev->mei_host_buffer_is_empty && |
274 | mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { | 274 | mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) { |