diff options
author | Tomas Winkler <tomas.winkler@intel.com> | 2014-01-08 13:19:22 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-01-08 18:25:41 -0500 |
commit | 66ae460b13c31a176b41550259683c841a62af3e (patch) | |
tree | e923cdf7f4cc857bed6a5c74a5ca4c674034a37e /drivers/misc | |
parent | 544f94601409653f07ae6e22d4a39e3a90dceead (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>
Diffstat (limited to 'drivers/misc')
-rw-r--r-- | drivers/misc/mei/hbm.c | 19 | ||||
-rw-r--r-- | drivers/misc/mei/hbm.h | 1 | ||||
-rw-r--r-- | drivers/misc/mei/init.c | 12 | ||||
-rw-r--r-- | drivers/misc/mei/interrupt.c | 25 |
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 | */ | ||
134 | void mei_hbm_idle(struct mei_device *dev) | ||
135 | { | ||
136 | dev->init_clients_timer = 0; | ||
137 | dev->hbm_state = MEI_HBM_IDLE; | ||
138 | } | ||
139 | |||
129 | int mei_hbm_start_wait(struct mei_device *dev) | 140 | int 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 | ||
52 | void mei_hbm_idle(struct mei_device *dev); | ||
52 | int mei_hbm_start_req(struct mei_device *dev); | 53 | int mei_hbm_start_req(struct mei_device *dev); |
53 | int mei_hbm_start_wait(struct mei_device *dev); | 54 | int mei_hbm_start_wait(struct mei_device *dev); |
54 | int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl); | 55 | int 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 | */ |
542 | void mei_timer(struct work_struct *work) | 541 | void 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) { |