aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2014-01-08 13:19:22 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-08 18:25:41 -0500
commit66ae460b13c31a176b41550259683c841a62af3e (patch)
treee923cdf7f4cc857bed6a5c74a5ca4c674034a37e
parent544f94601409653f07ae6e22d4a39e3a90dceead (diff)
mei: use hbm idle state to prevent spurious resets
When reset is caused by hbm protocol mismatch or timeout we might end up in an endless reset loop and hbm protocol will never sync Cc: <stable@vger.kernel.org> 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>
-rw-r--r--drivers/misc/mei/hbm.c19
-rw-r--r--drivers/misc/mei/hbm.h1
-rw-r--r--drivers/misc/mei/init.c12
-rw-r--r--drivers/misc/mei/interrupt.c25
4 files changed, 43 insertions, 14 deletions
diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c
index 8109b9a98cc3..836f92db7983 100644
--- a/drivers/misc/mei/hbm.c
+++ b/drivers/misc/mei/hbm.c
@@ -126,6 +126,17 @@ static bool is_treat_specially_client(struct mei_cl *cl,
126 return false; 126 return false;
127} 127}
128 128
129/**
130 * mei_hbm_idle - set hbm to idle state
131 *
132 * @dev: the device structure
133 */
134void mei_hbm_idle(struct mei_device *dev)
135{
136 dev->init_clients_timer = 0;
137 dev->hbm_state = MEI_HBM_IDLE;
138}
139
129int mei_hbm_start_wait(struct mei_device *dev) 140int mei_hbm_start_wait(struct mei_device *dev)
130{ 141{
131 int ret; 142 int ret;
@@ -583,6 +594,14 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
583 mei_read_slots(dev, dev->rd_msg_buf, hdr->length); 594 mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
584 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf; 595 mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
585 596
597 /* ignore spurious message and prevent reset nesting
598 * hbm is put to idle during system reset
599 */
600 if (dev->hbm_state == MEI_HBM_IDLE) {
601 dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n");
602 return 0;
603 }
604
586 switch (mei_msg->hbm_cmd) { 605 switch (mei_msg->hbm_cmd) {
587 case HOST_START_RES_CMD: 606 case HOST_START_RES_CMD:
588 dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n"); 607 dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");
diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h
index f2540fff7080..5f92188a5cd7 100644
--- a/drivers/misc/mei/hbm.h
+++ b/drivers/misc/mei/hbm.h
@@ -49,6 +49,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
49 hdr->reserved = 0; 49 hdr->reserved = 0;
50} 50}
51 51
52void mei_hbm_idle(struct mei_device *dev);
52int mei_hbm_start_req(struct mei_device *dev); 53int mei_hbm_start_req(struct mei_device *dev);
53int mei_hbm_start_wait(struct mei_device *dev); 54int mei_hbm_start_wait(struct mei_device *dev);
54int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl); 55int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index 87c077bae5d8..c47fa273879e 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -129,14 +129,19 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
129 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n", 129 dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
130 mei_dev_state_str(dev->dev_state)); 130 mei_dev_state_str(dev->dev_state));
131 131
132 /* we're already in reset, cancel the init timer
133 * if the reset was called due the hbm protocol error
134 * we need to call it before hw start
135 * so the hbm watchdog won't kick in
136 */
137 mei_hbm_idle(dev);
138
132 ret = mei_hw_reset(dev, interrupts_enabled); 139 ret = mei_hw_reset(dev, interrupts_enabled);
133 if (ret) { 140 if (ret) {
134 dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); 141 dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
135 interrupts_enabled = false; 142 interrupts_enabled = false;
136 dev->dev_state = MEI_DEV_DISABLED;
137 } 143 }
138 144
139 dev->hbm_state = MEI_HBM_IDLE;
140 145
141 if (dev->dev_state != MEI_DEV_INITIALIZING && 146 if (dev->dev_state != MEI_DEV_INITIALIZING &&
142 dev->dev_state != MEI_DEV_POWER_UP) { 147 dev->dev_state != MEI_DEV_POWER_UP) {
@@ -160,8 +165,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
160 memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg)); 165 memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
161 } 166 }
162 167
163 /* we're already in reset, cancel the init timer */
164 dev->init_clients_timer = 0;
165 168
166 dev->me_clients_num = 0; 169 dev->me_clients_num = 0;
167 dev->rd_msg_hdr = 0; 170 dev->rd_msg_hdr = 0;
@@ -169,6 +172,7 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
169 172
170 if (!interrupts_enabled) { 173 if (!interrupts_enabled) {
171 dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); 174 dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
175 dev->dev_state = MEI_DEV_DISABLED;
172 return; 176 return;
173 } 177 }
174 178
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index bbb61bec863b..206dbe99bedd 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -537,7 +537,6 @@ EXPORT_SYMBOL_GPL(mei_irq_write_handler);
537 * 537 *
538 * @work: pointer to the work_struct structure 538 * @work: pointer to the work_struct structure
539 * 539 *
540 * NOTE: This function is called by timer interrupt work
541 */ 540 */
542void mei_timer(struct work_struct *work) 541void mei_timer(struct work_struct *work)
543{ 542{
@@ -552,18 +551,24 @@ void mei_timer(struct work_struct *work)
552 551
553 552
554 mutex_lock(&dev->device_lock); 553 mutex_lock(&dev->device_lock);
555 if (dev->dev_state != MEI_DEV_ENABLED) { 554
556 if (dev->dev_state == MEI_DEV_INIT_CLIENTS) { 555 /* Catch interrupt stalls during HBM init handshake */
557 if (dev->init_clients_timer) { 556 if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
558 if (--dev->init_clients_timer == 0) { 557 dev->hbm_state != MEI_HBM_IDLE) {
559 dev_err(&dev->pdev->dev, "reset: init clients timeout hbm_state = %d.\n", 558
560 dev->hbm_state); 559 if (dev->init_clients_timer) {
561 mei_reset(dev, 1); 560 if (--dev->init_clients_timer == 0) {
562 } 561 dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n",
562 dev->hbm_state);
563 mei_reset(dev, 1);
564 goto out;
563 } 565 }
564 } 566 }
565 goto out;
566 } 567 }
568
569 if (dev->dev_state != MEI_DEV_ENABLED)
570 goto out;
571
567 /*** connect/disconnect timeouts ***/ 572 /*** connect/disconnect timeouts ***/
568 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) {
569 if (cl_pos->timer_count) { 574 if (cl_pos->timer_count) {