diff options
Diffstat (limited to 'drivers/mmc/core/quirks.c')
-rw-r--r-- | drivers/mmc/core/quirks.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c new file mode 100644 index 000000000000..11118b74eb20 --- /dev/null +++ b/drivers/mmc/core/quirks.c | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * This file contains work-arounds for many known sdio hardware | ||
3 | * bugs. | ||
4 | * | ||
5 | * Copyright (c) 2011 Pierre Tardy <tardyp@gmail.com> | ||
6 | * Inspired from pci fixup code: | ||
7 | * Copyright (c) 1999 Martin Mares <mj@ucw.cz> | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/mmc/card.h> | ||
14 | #include <linux/mod_devicetable.h> | ||
15 | |||
16 | /* | ||
17 | * The world is not perfect and supplies us with broken mmc/sdio devices. | ||
18 | * For at least a part of these bugs we need a work-around | ||
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 | |||
35 | /* | ||
36 | * This hook just removes a quirk unconditionnally | ||
37 | */ | ||
38 | static void __maybe_unused remove_quirk(struct mmc_card *card, int data) | ||
39 | { | ||
40 | card->quirks &= ~data; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | * This hook just adds a quirk for all sdio devices | ||
45 | */ | ||
46 | static void add_quirk_for_sdio_devices(struct mmc_card *card, int data) | ||
47 | { | ||
48 | if (mmc_card_sdio(card)) | ||
49 | card->quirks |= data; | ||
50 | } | ||
51 | |||
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[] = { | ||
61 | /* by default sdio devices are considered CLK_GATING broken */ | ||
62 | /* good cards will be whitelisted as they are tested */ | ||
63 | { SDIO_ANY_ID, SDIO_ANY_ID, | ||
64 | add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING }, | ||
65 | { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271, | ||
66 | remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING }, | ||
67 | { 0 } | ||
68 | }; | ||
69 | |||
70 | void mmc_fixup_device(struct mmc_card *card) | ||
71 | { | ||
72 | const struct mmc_fixup *f; | ||
73 | |||
74 | for (f = mmc_fixup_methods; f->vendor_fixup; f++) { | ||
75 | if ((f->vendor == card->cis.vendor | ||
76 | || f->vendor == (u16) SDIO_ANY_ID) && | ||
77 | (f->device == card->cis.device | ||
78 | || f->device == (u16) SDIO_ANY_ID)) { | ||
79 | dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); | ||
80 | f->vendor_fixup(card, f->data); | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | EXPORT_SYMBOL(mmc_fixup_device); | ||