| From bdbc5d0c60f3e9de3eeccf1c1a18bdc11dca62cc Mon Sep 17 00:00:00 2001 |
| From: Terry Barnaby <terry@beam.ltd.uk> |
| Date: Mon, 8 Apr 2013 12:05:47 -0400 |
| Subject: mmc: atmel-mci: pio hang on block errors |
| |
| From: Terry Barnaby <terry@beam.ltd.uk> |
| |
| commit bdbc5d0c60f3e9de3eeccf1c1a18bdc11dca62cc upstream. |
| |
| The driver is doing, by default, multi-block reads. When a block error |
| occurs, card/block.c instigates a single block read: "mmcblk0: retrying |
| using single block read". It leaves the sg chain intact and just changes |
| the length attribute for the first sg entry and the overall sg_len |
| parameter. When atmci_read_data_pio is called to read the single block |
| of data it ignores the sg_len and expects to read more than 512 bytes as |
| it sees there are multiple items in the sg list. No more data comes as |
| the controller has only been commanded to get one block. |
| |
| Signed-off-by: Terry Barnaby <terry@beam.ltd.uk> |
| Acked-by: Ludovic Desroches <ludovic.desroches@atmel.com> |
| Signed-off-by: Chris Ball <cjb@laptop.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/mmc/host/atmel-mci.c | 14 ++++++++++---- |
| 1 file changed, 10 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/mmc/host/atmel-mci.c |
| +++ b/drivers/mmc/host/atmel-mci.c |
| @@ -165,6 +165,7 @@ struct atmel_mci { |
| void __iomem *regs; |
| |
| struct scatterlist *sg; |
| + unsigned int sg_len; |
| unsigned int pio_offset; |
| |
| struct atmel_mci_slot *cur_slot; |
| @@ -754,6 +755,7 @@ static u32 atmci_prepare_data(struct atm |
| data->error = -EINPROGRESS; |
| |
| host->sg = data->sg; |
| + host->sg_len = data->sg_len; |
| host->data = data; |
| host->data_chan = NULL; |
| |
| @@ -1592,7 +1594,8 @@ static void atmci_read_data_pio(struct a |
| if (offset == sg->length) { |
| flush_dcache_page(sg_page(sg)); |
| host->sg = sg = sg_next(sg); |
| - if (!sg) |
| + host->sg_len--; |
| + if (!sg || !host->sg_len) |
| goto done; |
| |
| offset = 0; |
| @@ -1605,7 +1608,8 @@ static void atmci_read_data_pio(struct a |
| |
| flush_dcache_page(sg_page(sg)); |
| host->sg = sg = sg_next(sg); |
| - if (!sg) |
| + host->sg_len--; |
| + if (!sg || !host->sg_len) |
| goto done; |
| |
| offset = 4 - remaining; |
| @@ -1659,7 +1663,8 @@ static void atmci_write_data_pio(struct |
| nbytes += 4; |
| if (offset == sg->length) { |
| host->sg = sg = sg_next(sg); |
| - if (!sg) |
| + host->sg_len--; |
| + if (!sg || !host->sg_len) |
| goto done; |
| |
| offset = 0; |
| @@ -1673,7 +1678,8 @@ static void atmci_write_data_pio(struct |
| nbytes += remaining; |
| |
| host->sg = sg = sg_next(sg); |
| - if (!sg) { |
| + host->sg_len--; |
| + if (!sg || !host->sg_len) { |
| atmci_writel(host, ATMCI_TDR, value); |
| goto done; |
| } |