aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/mei
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2014-01-11 17:36:09 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-13 17:57:21 -0500
commit33ec0826314734fc4f3c9bf37d12e98063339b31 (patch)
tree9fb9169af14ae0930fd49e96b4d4f34fcb619c0f /drivers/misc/mei
parentdb50d2f65b7c2bcdfb931dfa18cdcb4bb75dcfaa (diff)
mei: revamp mei reset state machine
1. MEI_DEV_RESETTING device state spans only hardware reset flow while starting dev state is saved into a local variable for further reference, this let us to reduce big if statements in case we are trying to avoid nested resets 2. During initializations if the reset ended in MEI_DEV_DISABLED device state we bail out with -ENODEV 3. Remove redundant interrupts_enabled parameter as this can be deduced from the starting dev_state 4. mei_reset propagates error code to the caller 5. Add mei_restart function to wrap the pci resume Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/misc/mei')
-rw-r--r--drivers/misc/mei/hbm.c2
-rw-r--r--drivers/misc/mei/hw-me.c10
-rw-r--r--drivers/misc/mei/init.c209
-rw-r--r--drivers/misc/mei/interrupt.c13
-rw-r--r--drivers/misc/mei/mei_dev.h3
-rw-r--r--drivers/misc/mei/pci-me.c10
6 files changed, 143 insertions, 104 deletions
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 8520cdfa2f05..28cd74c073b9 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -763,7 +763,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
763 return -EPROTO; 763 return -EPROTO;
764 } 764 }
765 765
766 dev->dev_state = MEI_DEV_DISABLED; 766 dev->dev_state = MEI_DEV_POWER_DOWN;
767 dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n"); 767 dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
768 /* force the reset */ 768 /* force the reset */
769 return -EPROTO; 769 return -EPROTO;
diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c
index 6c07623704c2..6f656c053b14 100644
--- a/drivers/misc/mei/hw-me.c
+++ b/drivers/misc/mei/hw-me.c
@@ -185,7 +185,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
185 185
186 mei_me_reg_write(hw, H_CSR, hcsr); 186 mei_me_reg_write(hw, H_CSR, hcsr);
187 187
188 if (dev->dev_state == MEI_DEV_POWER_DOWN) 188 if (intr_enable == false)
189 mei_me_hw_reset_release(dev); 189 mei_me_hw_reset_release(dev);
190 190
191 return 0; 191 return 0;
@@ -482,11 +482,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
482 mei_clear_interrupts(dev); 482 mei_clear_interrupts(dev);
483 483
484 /* check if ME wants a reset */ 484 /* check if ME wants a reset */
485 if (!mei_hw_is_ready(dev) && 485 if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
486 dev->dev_state != MEI_DEV_RESETTING &&
487 dev->dev_state != MEI_DEV_INITIALIZING &&
488 dev->dev_state != MEI_DEV_POWER_DOWN &&
489 dev->dev_state != MEI_DEV_POWER_UP) {
490 dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n"); 486 dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
491 schedule_work(&dev->reset_work); 487 schedule_work(&dev->reset_work);
492 goto end; 488 goto end;
@@ -514,7 +510,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
514 break; 510 break;
515 dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots); 511 dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
516 rets = mei_irq_read_handler(dev, &complete_list, &slots); 512 rets = mei_irq_read_handler(dev, &complete_list, &slots);
517 if (rets) { 513 if (rets && dev->dev_state != MEI_DEV_RESETTING) {
518 schedule_work(&dev->reset_work); 514 schedule_work(&dev->reset_work);
519 goto end; 515 goto end;
520 } 516 }
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index c47fa273879e..059133d8caca 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -43,62 +43,13 @@ const char *mei_dev_state_str(int state)
43#undef MEI_DEV_STATE 43#undef MEI_DEV_STATE
44} 44}
45 45
46/**
47 * mei_start - initializes host and fw to start work.
48 *
49 * @dev: the device structure
50 *
51 * returns 0 on success, <0 on failure.
52 */
53int mei_start(struct mei_device *dev)
54{
55 mutex_lock(&dev->device_lock);
56
57 /* acknowledge interrupt and stop interupts */
58 mei_clear_interrupts(dev);
59
60 mei_hw_config(dev);
61
62 dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
63
64 mei_reset(dev, 1);
65
66 if (mei_hbm_start_wait(dev)) {
67 dev_err(&dev->pdev->dev, "HBM haven't started");
68 goto err;
69 }
70
71 if (!mei_host_is_ready(dev)) {
72 dev_err(&dev->pdev->dev, "host is not ready.\n");
73 goto err;
74 }
75
76 if (!mei_hw_is_ready(dev)) {
77 dev_err(&dev->pdev->dev, "ME is not ready.\n");
78 goto err;
79 }
80
81 if (!mei_hbm_version_is_supported(dev)) {
82 dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
83 goto err;
84 }
85
86 dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
87
88 mutex_unlock(&dev->device_lock);
89 return 0;
90err:
91 dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
92 dev->dev_state = MEI_DEV_DISABLED;
93 mutex_unlock(&dev->device_lock);
94 return -ENODEV;
95}
96EXPORT_SYMBOL_GPL(mei_start);
97 46
98/** 47/**
99 * mei_cancel_work. Cancel mei background jobs 48 * mei_cancel_work. Cancel mei background jobs
100 * 49 *
101 * @dev: the device structure 50 * @dev: the device structure
51 *
52 * returns 0 on success or < 0 if the reset hasn't succeeded
102 */ 53 */
103void mei_cancel_work(struct mei_device *dev) 54void mei_cancel_work(struct mei_device *dev)
104{ 55{
@@ -113,21 +64,19 @@ EXPORT_SYMBOL_GPL(mei_cancel_work);
113 * mei_reset - resets host and fw. 64 * mei_reset - resets host and fw.
114 * 65 *
115 * @dev: the device structure 66 * @dev: the device structure
116 * @interrupts_enabled: if interrupt should be enabled after reset.
117 */ 67 */
118void mei_reset(struct mei_device *dev, int interrupts_enabled) 68int mei_reset(struct mei_device *dev)
119{ 69{
120 bool unexpected; 70 enum mei_dev_state state = dev->dev_state;
71 bool interrupts_enabled;
121 int ret; 72 int ret;
122 73
123 unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && 74 if (state != MEI_DEV_INITIALIZING &&
124 dev->dev_state != MEI_DEV_DISABLED && 75 state != MEI_DEV_DISABLED &&
125 dev->dev_state != MEI_DEV_POWER_DOWN && 76 state != MEI_DEV_POWER_DOWN &&
126 dev->dev_state != MEI_DEV_POWER_UP); 77 state != MEI_DEV_POWER_UP)
127
128 if (unexpected)
129 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", 78 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
130 mei_dev_state_str(dev->dev_state)); 79 mei_dev_state_str(state));
131 80
132 /* we're already in reset, cancel the init timer 81 /* we're already in reset, cancel the init timer
133 * if the reset was called due the hbm protocol error 82 * if the reset was called due the hbm protocol error
@@ -136,25 +85,23 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
136 */ 85 */
137 mei_hbm_idle(dev); 86 mei_hbm_idle(dev);
138 87
139 ret = mei_hw_reset(dev, interrupts_enabled); 88 /* enter reset flow */
140 if (ret) { 89 interrupts_enabled = state != MEI_DEV_POWER_DOWN;
141 dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); 90 dev->dev_state = MEI_DEV_RESETTING;
142 interrupts_enabled = false;
143 }
144 91
92 ret = mei_hw_reset(dev, interrupts_enabled);
93 /* fall through and remove the sw state even if hw reset has failed */
145 94
146 if (dev->dev_state != MEI_DEV_INITIALIZING && 95 /* no need to clean up software state in case of power up */
147 dev->dev_state != MEI_DEV_POWER_UP) { 96 if (state != MEI_DEV_INITIALIZING &&
148 if (dev->dev_state != MEI_DEV_DISABLED && 97 state != MEI_DEV_POWER_UP) {
149 dev->dev_state != MEI_DEV_POWER_DOWN)
150 dev->dev_state = MEI_DEV_RESETTING;
151 98
152 /* remove all waiting requests */ 99 /* remove all waiting requests */
153 mei_cl_all_write_clear(dev); 100 mei_cl_all_write_clear(dev);
154 101
155 mei_cl_all_disconnect(dev); 102 mei_cl_all_disconnect(dev);
156 103
157 /* wake up all readings so they can be interrupted */ 104 /* wake up all readers and writers so they can be interrupted */
158 mei_cl_all_wakeup(dev); 105 mei_cl_all_wakeup(dev);
159 106
160 /* remove entry if already in list */ 107 /* remove entry if already in list */
@@ -170,33 +117,126 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
170 dev->rd_msg_hdr = 0; 117 dev->rd_msg_hdr = 0;
171 dev->wd_pending = false; 118 dev->wd_pending = false;
172 119
173 if (!interrupts_enabled) { 120 if (ret) {
174 dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); 121 dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret);
175 dev->dev_state = MEI_DEV_DISABLED; 122 dev->dev_state = MEI_DEV_DISABLED;
176 return; 123 return ret;
124 }
125
126 if (state == MEI_DEV_POWER_DOWN) {
127 dev_dbg(&dev->pdev->dev, "powering down: end of reset\n");
128 dev->dev_state = MEI_DEV_DISABLED;
129 return 0;
177 } 130 }
178 131
179 ret = mei_hw_start(dev); 132 ret = mei_hw_start(dev);
180 if (ret) { 133 if (ret) {
181 dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n"); 134 dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
182 dev->dev_state = MEI_DEV_DISABLED; 135 dev->dev_state = MEI_DEV_DISABLED;
183 return; 136 return ret;
184 } 137 }
185 138
186 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n"); 139 dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
187 /* link is established * start sending messages. */
188 140
189 dev->dev_state = MEI_DEV_INIT_CLIENTS; 141 dev->dev_state = MEI_DEV_INIT_CLIENTS;
190
191 ret = mei_hbm_start_req(dev); 142 ret = mei_hbm_start_req(dev);
192 if (ret) { 143 if (ret) {
193 dev_err(&dev->pdev->dev, "hbm_start failed disabling the device\n"); 144 dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
194 dev->dev_state = MEI_DEV_DISABLED; 145 dev->dev_state = MEI_DEV_DISABLED;
195 return; 146 return ret;
196 } 147 }
148
149 return 0;
197} 150}
198EXPORT_SYMBOL_GPL(mei_reset); 151EXPORT_SYMBOL_GPL(mei_reset);
199 152
153/**
154 * mei_start - initializes host and fw to start work.
155 *
156 * @dev: the device structure
157 *
158 * returns 0 on success, <0 on failure.
159 */
160int mei_start(struct mei_device *dev)
161{
162 mutex_lock(&dev->device_lock);
163
164 /* acknowledge interrupt and stop interrupts */
165 mei_clear_interrupts(dev);
166
167 mei_hw_config(dev);
168
169 dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
170
171 dev->dev_state = MEI_DEV_INITIALIZING;
172 mei_reset(dev);
173
174 if (dev->dev_state == MEI_DEV_DISABLED) {
175 dev_err(&dev->pdev->dev, "reset failed");
176 goto err;
177 }
178
179 if (mei_hbm_start_wait(dev)) {
180 dev_err(&dev->pdev->dev, "HBM haven't started");
181 goto err;
182 }
183
184 if (!mei_host_is_ready(dev)) {
185 dev_err(&dev->pdev->dev, "host is not ready.\n");
186 goto err;
187 }
188
189 if (!mei_hw_is_ready(dev)) {
190 dev_err(&dev->pdev->dev, "ME is not ready.\n");
191 goto err;
192 }
193
194 if (!mei_hbm_version_is_supported(dev)) {
195 dev_dbg(&dev->pdev->dev, "MEI start failed.\n");
196 goto err;
197 }
198
199 dev_dbg(&dev->pdev->dev, "link layer has been established.\n");
200
201 mutex_unlock(&dev->device_lock);
202 return 0;
203err:
204 dev_err(&dev->pdev->dev, "link layer initialization failed.\n");
205 dev->dev_state = MEI_DEV_DISABLED;
206 mutex_unlock(&dev->device_lock);
207 return -ENODEV;
208}
209EXPORT_SYMBOL_GPL(mei_start);
210
211/**
212 * mei_restart - restart device after suspend
213 *
214 * @dev: the device structure
215 *
216 * returns 0 on success or -ENODEV if the restart hasn't succeeded
217 */
218int mei_restart(struct mei_device *dev)
219{
220 int err;
221
222 mutex_lock(&dev->device_lock);
223
224 mei_clear_interrupts(dev);
225
226 dev->dev_state = MEI_DEV_POWER_UP;
227
228 err = mei_reset(dev);
229
230 mutex_unlock(&dev->device_lock);
231
232 if (err || dev->dev_state == MEI_DEV_DISABLED)
233 return -ENODEV;
234
235 return 0;
236}
237EXPORT_SYMBOL_GPL(mei_restart);
238
239
200static void mei_reset_work(struct work_struct *work) 240static void mei_reset_work(struct work_struct *work)
201{ 241{
202 struct mei_device *dev = 242 struct mei_device *dev =
@@ -204,9 +244,12 @@ static void mei_reset_work(struct work_struct *work)
204 244
205 mutex_lock(&dev->device_lock); 245 mutex_lock(&dev->device_lock);
206 246
207 mei_reset(dev, true); 247 mei_reset(dev);
208 248
209 mutex_unlock(&dev->device_lock); 249 mutex_unlock(&dev->device_lock);
250
251 if (dev->dev_state == MEI_DEV_DISABLED)
252 dev_err(&dev->pdev->dev, "reset failed");
210} 253}
211 254
212void mei_stop(struct mei_device *dev) 255void mei_stop(struct mei_device *dev)
@@ -222,7 +265,7 @@ void mei_stop(struct mei_device *dev)
222 mei_wd_stop(dev); 265 mei_wd_stop(dev);
223 266
224 dev->dev_state = MEI_DEV_POWER_DOWN; 267 dev->dev_state = MEI_DEV_POWER_DOWN;
225 mei_reset(dev, 0); 268 mei_reset(dev);
226 269
227 mutex_unlock(&dev->device_lock); 270 mutex_unlock(&dev->device_lock);
228 271
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index 2a7277de7ca1..f0fbb5179f80 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -560,7 +560,7 @@ void mei_timer(struct work_struct *work)
560 if (--dev->init_clients_timer == 0) { 560 if (--dev->init_clients_timer == 0) {
561 dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n", 561 dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n",
562 dev->hbm_state); 562 dev->hbm_state);
563 mei_reset(dev, 1); 563 mei_reset(dev);
564 goto out; 564 goto out;
565 } 565 }
566 } 566 }
@@ -573,8 +573,8 @@ void mei_timer(struct work_struct *work)
573 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) { 573 list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
574 if (cl_pos->timer_count) { 574 if (cl_pos->timer_count) {
575 if (--cl_pos->timer_count == 0) { 575 if (--cl_pos->timer_count == 0) {
576 dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n"); 576 dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n");
577 mei_reset(dev, 1); 577 mei_reset(dev);
578 goto out; 578 goto out;
579 } 579 }
580 } 580 }
@@ -582,8 +582,8 @@ void mei_timer(struct work_struct *work)
582 582
583 if (dev->iamthif_stall_timer) { 583 if (dev->iamthif_stall_timer) {
584 if (--dev->iamthif_stall_timer == 0) { 584 if (--dev->iamthif_stall_timer == 0) {
585 dev_err(&dev->pdev->dev, "reset: amthif hanged.\n"); 585 dev_err(&dev->pdev->dev, "timer: amthif hanged.\n");
586 mei_reset(dev, 1); 586 mei_reset(dev);
587 dev->iamthif_msg_buf_size = 0; 587 dev->iamthif_msg_buf_size = 0;
588 dev->iamthif_msg_buf_index = 0; 588 dev->iamthif_msg_buf_index = 0;
589 dev->iamthif_canceled = false; 589 dev->iamthif_canceled = false;
@@ -636,7 +636,8 @@ void mei_timer(struct work_struct *work)
636 } 636 }
637 } 637 }
638out: 638out:
639 schedule_delayed_work(&dev->timer_work, 2 * HZ); 639 if (dev->dev_state != MEI_DEV_DISABLED)
640 schedule_delayed_work(&dev->timer_work, 2 * HZ);
640 mutex_unlock(&dev->device_lock); 641 mutex_unlock(&dev->device_lock);
641} 642}
642 643
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 2c7692807a4a..a617c8494b70 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -472,8 +472,9 @@ static inline u32 mei_slots2data(int slots)
472 * mei init function prototypes 472 * mei init function prototypes
473 */ 473 */
474void mei_device_init(struct mei_device *dev); 474void mei_device_init(struct mei_device *dev);
475void mei_reset(struct mei_device *dev, int interrupts); 475int mei_reset(struct mei_device *dev);
476int mei_start(struct mei_device *dev); 476int mei_start(struct mei_device *dev);
477int mei_restart(struct mei_device *dev);
477void mei_stop(struct mei_device *dev); 478void mei_stop(struct mei_device *dev);
478void mei_cancel_work(struct mei_device *dev); 479void mei_cancel_work(struct mei_device *dev);
479 480
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 8b1deea2abf6..ddadd08956f4 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -321,16 +321,14 @@ static int mei_me_pci_resume(struct device *device)
321 return err; 321 return err;
322 } 322 }
323 323
324 mutex_lock(&dev->device_lock); 324 err = mei_restart(dev);
325 dev->dev_state = MEI_DEV_POWER_UP; 325 if (err)
326 mei_clear_interrupts(dev); 326 return err;
327 mei_reset(dev, 1);
328 mutex_unlock(&dev->device_lock);
329 327
330 /* Start timer if stopped in suspend */ 328 /* Start timer if stopped in suspend */
331 schedule_delayed_work(&dev->timer_work, HZ); 329 schedule_delayed_work(&dev->timer_work, HZ);
332 330
333 return err; 331 return 0;
334} 332}
335static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume); 333static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
336#define MEI_ME_PM_OPS (&mei_me_pm_ops) 334#define MEI_ME_PM_OPS (&mei_me_pm_ops)