1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
From 205e27a7f94a5531764cc517ce43623361ca466c Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.org>
Date: Tue, 19 Jan 2016 17:16:38 +0000
Subject: [PATCH 136/170] bcm2835-sdhost: Add workaround for odd behaviour on
some cards
For reasons not understood, the sdhost driver fails when reading
sectors very near the end of some SD cards. The problem could
be related to the similar issue that reading the final sector
of any card as part of a multiple read never completes, and the
workaround is an extension of the mechanism introduced to solve
that problem which ensures those sectors are always read singly.
---
drivers/mmc/host/bcm2835-sdhost.c | 61 +++++++++++++++++++++++++++++++++------
1 file changed, 52 insertions(+), 9 deletions(-)
--- a/drivers/mmc/host/bcm2835-sdhost.c
+++ b/drivers/mmc/host/bcm2835-sdhost.c
@@ -173,6 +173,9 @@ struct bcm2835_host {
u32 overclock_50; /* frequency to use when 50MHz is requested (in MHz) */
u32 overclock; /* Current frequency if overclocked, else zero */
u32 pio_limit; /* Maximum block count for PIO (0 = always DMA) */
+
+ u32 sectors; /* Cached card size in sectors */
+ u32 single_read_sectors[8];
};
@@ -277,6 +280,9 @@ static void bcm2835_sdhost_reset_interna
{
u32 temp;
+ if (host->debug)
+ pr_info("%s: reset\n", mmc_hostname(host->mmc));
+
bcm2835_sdhost_set_power(host, false);
bcm2835_sdhost_write(host, 0, SDCMD);
@@ -299,6 +305,8 @@ static void bcm2835_sdhost_reset_interna
bcm2835_sdhost_set_power(host, true);
mdelay(10);
host->clock = 0;
+ host->sectors = 0;
+ host->single_read_sectors[0] = ~0;
bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
mmiowb();
@@ -309,8 +317,6 @@ static void bcm2835_sdhost_reset(struct
{
struct bcm2835_host *host = mmc_priv(mmc);
unsigned long flags;
- if (host->debug)
- pr_info("%s: reset\n", mmc_hostname(mmc));
spin_lock_irqsave(&host->lock, flags);
bcm2835_sdhost_reset_internal(host);
@@ -676,6 +682,32 @@ static void bcm2835_sdhost_prepare_data(
host->flush_fifo = 0;
host->data->bytes_xfered = 0;
+ if (!host->sectors && host->mmc->card)
+ {
+ struct mmc_card *card = host->mmc->card;
+ if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) {
+ /*
+ * The EXT_CSD sector count is in number of 512 byte
+ * sectors.
+ */
+ host->sectors = card->ext_csd.sectors;
+ pr_err("%s: using ext_csd!\n", mmc_hostname(host->mmc));
+ } else {
+ /*
+ * The CSD capacity field is in units of read_blkbits.
+ * set_capacity takes units of 512 bytes.
+ */
+ host->sectors = card->csd.capacity <<
+ (card->csd.read_blkbits - 9);
+ }
+ host->single_read_sectors[0] = host->sectors - 65;
+ host->single_read_sectors[1] = host->sectors - 64;
+ host->single_read_sectors[2] = host->sectors - 33;
+ host->single_read_sectors[3] = host->sectors - 32;
+ host->single_read_sectors[4] = host->sectors - 1;
+ host->single_read_sectors[5] = ~0; /* Safety net */
+ }
+
host->use_dma = host->have_dma && (data->blocks > host->pio_limit);
if (!host->use_dma) {
int flags;
@@ -1246,6 +1278,10 @@ static u32 bcm2835_sdhost_block_irq(stru
bcm2835_sdhost_finish_data(host);
} else {
+ /* Reset the timer */
+ mod_timer(&host->pio_timer,
+ jiffies + host->pio_timeout);
+
bcm2835_sdhost_transfer_pio(host);
/* Reset the timer */
@@ -1450,8 +1486,8 @@ void bcm2835_sdhost_set_clock(struct bcm
host->cdiv = div;
bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
- /* Set the timeout to 500ms */
- bcm2835_sdhost_write(host, host->mmc->actual_clock/2, SDTOUT);
+ /* Set the timeout to 250ms */
+ bcm2835_sdhost_write(host, host->mmc->actual_clock/4, SDTOUT);
if (host->debug)
pr_info("%s: clock=%d -> max_clk=%d, cdiv=%x (actual clock %d)\n",
@@ -1566,13 +1602,20 @@ static int bcm2835_sdhost_multi_io_quirk
reading the final sector of the card as part of a multiple read
problematic. Detect that case and shorten the read accordingly.
*/
- /* csd.capacity is in weird units - convert to sectors */
- u32 card_sectors = (card->csd.capacity << (card->csd.read_blkbits - 9));
+ struct bcm2835_host *host;
+
+ host = mmc_priv(card->host);
- if ((direction == MMC_DATA_READ) &&
- ((blk_pos + blk_size) == card_sectors))
- blk_size--;
+ if (direction == MMC_DATA_READ)
+ {
+ int i;
+ int sector;
+ for (i = 0; blk_pos > (sector = host->single_read_sectors[i]); i++)
+ continue;
+ if ((blk_pos + blk_size) > sector)
+ blk_size = (blk_pos == sector) ? 1 : (sector - blk_pos);
+ }
return blk_size;
}
|