diff options
author | Andrei Warkentin <andreiw@motorola.com> | 2011-04-11 18:02:15 -0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-05-24 21:00:54 -0400 |
commit | 32780cd1350e651e68bdf33b7f5b009d21d5b794 (patch) | |
tree | e8e86350a3eca20cca098b488e596d2b76ecaea8 | |
parent | f317dfeb86c83d03304a74ce5426a69422b79547 (diff) |
mmc: quirks: Extends card quirks with MMC/SD quirks matching the CID.
The current mechanism is SDIO-only. This allows us to create
function-specific quirks, without creating messy Kconfig dependencies,
or polluting core/ with function-specific code.
Signed-off-by: Andrei Warkentin <andreiw@motorola.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r-- | drivers/mmc/core/core.h | 2 | ||||
-rw-r--r-- | drivers/mmc/core/quirks.c | 93 | ||||
-rw-r--r-- | drivers/mmc/core/sdio.c | 2 | ||||
-rw-r--r-- | include/linux/mmc/card.h | 91 |
4 files changed, 133 insertions, 55 deletions
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 20b1c0831eac..ca1fdde29df6 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h | |||
@@ -61,8 +61,6 @@ int mmc_attach_mmc(struct mmc_host *host); | |||
61 | int mmc_attach_sd(struct mmc_host *host); | 61 | int mmc_attach_sd(struct mmc_host *host); |
62 | int mmc_attach_sdio(struct mmc_host *host); | 62 | int mmc_attach_sdio(struct mmc_host *host); |
63 | 63 | ||
64 | void mmc_fixup_device(struct mmc_card *card); | ||
65 | |||
66 | /* Module parameters */ | 64 | /* Module parameters */ |
67 | extern int use_spi_crc; | 65 | extern int use_spi_crc; |
68 | 66 | ||
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index a4c42edc6cb3..3a596217029e 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c | |||
@@ -1,7 +1,8 @@ | |||
1 | /* | 1 | /* |
2 | * This file contains work-arounds for many known sdio hardware | 2 | * This file contains work-arounds for many known SD/MMC |
3 | * bugs. | 3 | * and SDIO hardware bugs. |
4 | * | 4 | * |
5 | * Copyright (c) 2011 Andrei Warkentin <andreiw@motorola.com> | ||
5 | * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com> | 6 | * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com> |
6 | * Inspired from pci fixup code: | 7 | * Inspired from pci fixup code: |
7 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> | 8 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> |
@@ -11,34 +12,14 @@ | |||
11 | #include <linux/types.h> | 12 | #include <linux/types.h> |
12 | #include <linux/kernel.h> | 13 | #include <linux/kernel.h> |
13 | #include <linux/mmc/card.h> | 14 | #include <linux/mmc/card.h> |
14 | #include <linux/mod_devicetable.h> | ||
15 | 15 | ||
16 | /* | 16 | #ifndef SDIO_VENDOR_ID_TI |
17 | * The world is not perfect and supplies us with broken mmc/sdio devices. | 17 | #define SDIO_VENDOR_ID_TI 0x0097 |
18 | * For at least a part of these bugs we need a work-around | 18 | #endif |
19 | */ | ||
20 | |||
21 | struct mmc_fixup { | ||
22 | u16 vendor, device; /* You can use SDIO_ANY_ID here of course */ | ||
23 | void (*vendor_fixup)(struct mmc_card *card, int data); | ||
24 | int data; | ||
25 | }; | ||
26 | |||
27 | /* | ||
28 | * This hook just adds a quirk unconditionnally | ||
29 | */ | ||
30 | static void __maybe_unused add_quirk(struct mmc_card *card, int data) | ||
31 | { | ||
32 | card->quirks |= data; | ||
33 | } | ||
34 | 19 | ||
35 | /* | 20 | #ifndef SDIO_DEVICE_ID_TI_WL1271 |
36 | * This hook just removes a quirk unconditionnally | 21 | #define SDIO_DEVICE_ID_TI_WL1271 0x4076 |
37 | */ | 22 | #endif |
38 | static void __maybe_unused remove_quirk(struct mmc_card *card, int data) | ||
39 | { | ||
40 | card->quirks &= ~data; | ||
41 | } | ||
42 | 23 | ||
43 | /* | 24 | /* |
44 | * This hook just adds a quirk for all sdio devices | 25 | * This hook just adds a quirk for all sdio devices |
@@ -49,37 +30,47 @@ static void add_quirk_for_sdio_devices(struct mmc_card *card, int data) | |||
49 | card->quirks |= data; | 30 | card->quirks |= data; |
50 | } | 31 | } |
51 | 32 | ||
52 | #ifndef SDIO_VENDOR_ID_TI | ||
53 | #define SDIO_VENDOR_ID_TI 0x0097 | ||
54 | #endif | ||
55 | |||
56 | #ifndef SDIO_DEVICE_ID_TI_WL1271 | ||
57 | #define SDIO_DEVICE_ID_TI_WL1271 0x4076 | ||
58 | #endif | ||
59 | |||
60 | static const struct mmc_fixup mmc_fixup_methods[] = { | 33 | static const struct mmc_fixup mmc_fixup_methods[] = { |
61 | /* by default sdio devices are considered CLK_GATING broken */ | 34 | /* by default sdio devices are considered CLK_GATING broken */ |
62 | /* good cards will be whitelisted as they are tested */ | 35 | /* good cards will be whitelisted as they are tested */ |
63 | { SDIO_ANY_ID, SDIO_ANY_ID, | 36 | SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID, |
64 | add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING }, | 37 | add_quirk_for_sdio_devices, |
65 | { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, | 38 | MMC_QUIRK_BROKEN_CLK_GATING), |
66 | remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING }, | 39 | |
67 | { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, | 40 | SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, |
68 | add_quirk, MMC_QUIRK_NONSTD_FUNC_IF }, | 41 | remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING), |
69 | { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, | 42 | |
70 | add_quirk, MMC_QUIRK_DISABLE_CD }, | 43 | SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, |
71 | { 0 } | 44 | add_quirk, MMC_QUIRK_NONSTD_FUNC_IF), |
45 | |||
46 | SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, | ||
47 | add_quirk, MMC_QUIRK_DISABLE_CD), | ||
48 | |||
49 | END_FIXUP | ||
72 | }; | 50 | }; |
73 | 51 | ||
74 | void mmc_fixup_device(struct mmc_card *card) | 52 | void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) |
75 | { | 53 | { |
76 | const struct mmc_fixup *f; | 54 | const struct mmc_fixup *f; |
55 | u64 rev = cid_rev_card(card); | ||
56 | |||
57 | /* Non-core specific workarounds. */ | ||
58 | if (!table) | ||
59 | table = mmc_fixup_methods; | ||
77 | 60 | ||
78 | for (f = mmc_fixup_methods; f->vendor_fixup; f++) { | 61 | for (f = table; f->vendor_fixup; f++) { |
79 | if ((f->vendor == card->cis.vendor | 62 | if ((f->manfid == CID_MANFID_ANY || |
80 | || f->vendor == (u16) SDIO_ANY_ID) && | 63 | f->manfid == card->cid.manfid) && |
81 | (f->device == card->cis.device | 64 | (f->oemid == CID_OEMID_ANY || |
82 | || f->device == (u16) SDIO_ANY_ID)) { | 65 | f->oemid == card->cid.oemid) && |
66 | (f->name == CID_NAME_ANY || | ||
67 | !strncmp(f->name, card->cid.prod_name, | ||
68 | sizeof(card->cid.prod_name))) && | ||
69 | (f->cis_vendor == card->cis.vendor || | ||
70 | f->cis_vendor == (u16) SDIO_ANY_ID) && | ||
71 | (f->cis_device == card->cis.device || | ||
72 | f->cis_device == (u16) SDIO_ANY_ID) && | ||
73 | rev >= f->rev_start && rev <= f->rev_end) { | ||
83 | dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); | 74 | dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); |
84 | f->vendor_fixup(card, f->data); | 75 | f->vendor_fixup(card, f->data); |
85 | } | 76 | } |
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index a5840c0de2e4..1e6095961500 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c | |||
@@ -472,7 +472,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, | |||
472 | 472 | ||
473 | card = oldcard; | 473 | card = oldcard; |
474 | } | 474 | } |
475 | mmc_fixup_device(card); | 475 | mmc_fixup_device(card, NULL); |
476 | 476 | ||
477 | if (card->type == MMC_TYPE_SD_COMBO) { | 477 | if (card->type == MMC_TYPE_SD_COMBO) { |
478 | err = mmc_sd_setup_card(host, card, oldcard != NULL); | 478 | err = mmc_sd_setup_card(host, card, oldcard != NULL); |
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 2a7e54970c93..c6513175f7f1 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #define LINUX_MMC_CARD_H | 11 | #define LINUX_MMC_CARD_H |
12 | 12 | ||
13 | #include <linux/mmc/core.h> | 13 | #include <linux/mmc/core.h> |
14 | #include <linux/mod_devicetable.h> | ||
14 | 15 | ||
15 | struct mmc_cid { | 16 | struct mmc_cid { |
16 | unsigned int manfid; | 17 | unsigned int manfid; |
@@ -157,7 +158,92 @@ struct mmc_card { | |||
157 | struct dentry *debugfs_root; | 158 | struct dentry *debugfs_root; |
158 | }; | 159 | }; |
159 | 160 | ||
160 | void mmc_fixup_device(struct mmc_card *dev); | 161 | /* |
162 | * The world is not perfect and supplies us with broken mmc/sdio devices. | ||
163 | * For at least some of these bugs we need a work-around. | ||
164 | */ | ||
165 | |||
166 | struct mmc_fixup { | ||
167 | /* CID-specific fields. */ | ||
168 | const char *name; | ||
169 | |||
170 | /* Valid revision range */ | ||
171 | u64 rev_start, rev_end; | ||
172 | |||
173 | unsigned int manfid; | ||
174 | unsigned short oemid; | ||
175 | |||
176 | /* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */ | ||
177 | u16 cis_vendor, cis_device; | ||
178 | |||
179 | void (*vendor_fixup)(struct mmc_card *card, int data); | ||
180 | int data; | ||
181 | }; | ||
182 | |||
183 | #define CID_MANFID_ANY (-1ul) | ||
184 | #define CID_OEMID_ANY ((unsigned short) -1) | ||
185 | #define CID_NAME_ANY (NULL) | ||
186 | |||
187 | #define END_FIXUP { 0 } | ||
188 | |||
189 | #define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \ | ||
190 | _cis_vendor, _cis_device, \ | ||
191 | _fixup, _data) \ | ||
192 | { \ | ||
193 | .name = (_name), \ | ||
194 | .manfid = (_manfid), \ | ||
195 | .oemid = (_oemid), \ | ||
196 | .rev_start = (_rev_start), \ | ||
197 | .rev_end = (_rev_end), \ | ||
198 | .cis_vendor = (_cis_vendor), \ | ||
199 | .cis_device = (_cis_device), \ | ||
200 | .vendor_fixup = (_fixup), \ | ||
201 | .data = (_data), \ | ||
202 | } | ||
203 | |||
204 | #define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \ | ||
205 | _fixup, _data) \ | ||
206 | _FIXUP_EXT(_name, _manfid, \ | ||
207 | _oemid, _rev_start, _rev_end, \ | ||
208 | SDIO_ANY_ID, SDIO_ANY_ID, \ | ||
209 | _fixup, _data) \ | ||
210 | |||
211 | #define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \ | ||
212 | MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data) | ||
213 | |||
214 | #define SDIO_FIXUP(_vendor, _device, _fixup, _data) \ | ||
215 | _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY, \ | ||
216 | CID_OEMID_ANY, 0, -1ull, \ | ||
217 | _vendor, _device, \ | ||
218 | _fixup, _data) \ | ||
219 | |||
220 | #define cid_rev(hwrev, fwrev, year, month) \ | ||
221 | (((u64) hwrev) << 40 | \ | ||
222 | ((u64) fwrev) << 32 | \ | ||
223 | ((u64) year) << 16 | \ | ||
224 | ((u64) month)) | ||
225 | |||
226 | #define cid_rev_card(card) \ | ||
227 | cid_rev(card->cid.hwrev, \ | ||
228 | card->cid.fwrev, \ | ||
229 | card->cid.year, \ | ||
230 | card->cid.month) | ||
231 | |||
232 | /* | ||
233 | * This hook just adds a quirk unconditionally. | ||
234 | */ | ||
235 | static inline void __maybe_unused add_quirk(struct mmc_card *card, int data) | ||
236 | { | ||
237 | card->quirks |= data; | ||
238 | } | ||
239 | |||
240 | /* | ||
241 | * This hook just removes a quirk unconditionally. | ||
242 | */ | ||
243 | static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) | ||
244 | { | ||
245 | card->quirks &= ~data; | ||
246 | } | ||
161 | 247 | ||
162 | #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) | 248 | #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) |
163 | #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) | 249 | #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) |
@@ -218,4 +304,7 @@ struct mmc_driver { | |||
218 | extern int mmc_register_driver(struct mmc_driver *); | 304 | extern int mmc_register_driver(struct mmc_driver *); |
219 | extern void mmc_unregister_driver(struct mmc_driver *); | 305 | extern void mmc_unregister_driver(struct mmc_driver *); |
220 | 306 | ||
307 | extern void mmc_fixup_device(struct mmc_card *card, | ||
308 | const struct mmc_fixup *table); | ||
309 | |||
221 | #endif | 310 | #endif |