aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Warkentin <andreiw@motorola.com>2011-04-11 18:02:15 -0400
committerChris Ball <cjb@laptop.org>2011-05-24 21:00:54 -0400
commit32780cd1350e651e68bdf33b7f5b009d21d5b794 (patch)
treee8e86350a3eca20cca098b488e596d2b76ecaea8
parentf317dfeb86c83d03304a74ce5426a69422b79547 (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.h2
-rw-r--r--drivers/mmc/core/quirks.c93
-rw-r--r--drivers/mmc/core/sdio.c2
-rw-r--r--include/linux/mmc/card.h91
4 files changed, 133 insertions, 55 deletions
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index 20b1c0831ea..ca1fdde29df 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);
61int mmc_attach_sd(struct mmc_host *host); 61int mmc_attach_sd(struct mmc_host *host);
62int mmc_attach_sdio(struct mmc_host *host); 62int mmc_attach_sdio(struct mmc_host *host);
63 63
64void mmc_fixup_device(struct mmc_card *card);
65
66/* Module parameters */ 64/* Module parameters */
67extern int use_spi_crc; 65extern int use_spi_crc;
68 66
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index a4c42edc6cb..3a596217029 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
21struct 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 */
30static 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
38static 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
60static const struct mmc_fixup mmc_fixup_methods[] = { 33static 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
74void mmc_fixup_device(struct mmc_card *card) 52void 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 a5840c0de2e..1e609596150 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 2a7e54970c9..c6513175f7f 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
15struct mmc_cid { 16struct 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
160void 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
166struct 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 */
235static 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 */
243static 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 {
218extern int mmc_register_driver(struct mmc_driver *); 304extern int mmc_register_driver(struct mmc_driver *);
219extern void mmc_unregister_driver(struct mmc_driver *); 305extern void mmc_unregister_driver(struct mmc_driver *);
220 306
307extern void mmc_fixup_device(struct mmc_card *card,
308 const struct mmc_fixup *table);
309
221#endif 310#endif