Tuesday, March 15, 2011

MMC block quirks.

So I'm secretly working on some block MMC quirks for some eMMC devices. It's not that big of a secret, but I'll keep the specifics to myself until I have collected enough data to post to linux-mmc. I already posted there the conclusions I reached based on other people's partial data, but that tends to be insufficient when it comes to convincing others of taking weird-looking code...

Anyway, the point is that occasionally you might wish the generic MMC code to act differently for various devices, whether as a workaround for not complying to the spec, or as a workaround for some smart proprietary intellectual property running inside these devices. The linux-next tree contains a quirks layer by Pierre Tardy, with a slight slant towards SDIO devices, as it was added to resolve some SDIO issue. Here is a patch against that tree that extends the quirk support to MMC/SD devices, allowing matching on CID fields. If any luck has it, it will be merged eventually. It would probably help if I hurried on the MMC block quirks actually using this ;-)... Data collection and analysis is a pain.




From: Andrei Warkentin <andreiw@motorola.com>
Subject: [PATCH 1/2] MMC: Extends card quicks with MMC/SD quirks matching the CID.
Date: Sun, 13 Mar 2011 08:48:37 -0500

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>
---
 drivers/mmc/core/core.h   |    2 -
 drivers/mmc/core/quirks.c |   82 ++++++++++++++++++----------------------
 drivers/mmc/core/sdio.c   |    2 +-
 include/linux/mmc/card.h  |   92 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 129 insertions(+), 49 deletions(-)

diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index a2a956b..406a50f 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -59,8 +59,6 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr);
 int mmc_attach_sd(struct mmc_host *host, u32 ocr);
 int mmc_attach_sdio(struct mmc_host *host, u32 ocr);
 
-void mmc_fixup_device(struct mmc_card *card);
-
 /* Module parameters */
 extern int use_spi_crc;
 extern int mmc_assume_removable;
diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c
index 4fb16ac..8cc359d 100644
--- a/drivers/mmc/core/quirks.c
+++ b/drivers/mmc/core/quirks.c
@@ -11,71 +11,63 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/mmc/card.h>
-#include <linux/mod_devicetable.h>
 
-/*
- *  The world is not perfect and supplies us with broken mmc/sdio devices.
- *  For at least a part of these bugs we need a work-around
- */
-
-struct mmc_fixup {
- u16 vendor, device; /* You can use SDIO_ANY_ID here of course */
- void (*vendor_fixup)(struct mmc_card *card, int data);
- int data;
-};
+#ifndef SDIO_VENDOR_ID_TI
+#define SDIO_VENDOR_ID_TI  0x0097
+#endif
 
-/*
- * This hook just adds a quirk unconditionnally
- */
-static void __maybe_unused add_quirk(struct mmc_card *card, int data)
-{
- card->quirks |= data;
-}
+#ifndef SDIO_DEVICE_ID_TI_WL1271
+#define SDIO_DEVICE_ID_TI_WL1271 0x4076
+#endif
 
-/*
- * This hook just removes a quirk unconditionnally
- */
-static void __maybe_unused remove_quirk(struct mmc_card *card, int data)
-{
- card->quirks &= ~data;
-}
 
 /*
  * This hook just adds a quirk for all sdio devices
- */
+*/
+
 static void add_quirk_for_sdio_devices(struct mmc_card *card, int data)
 {
  if (mmc_card_sdio(card))
   card->quirks |= data;
 }
 
-#ifndef SDIO_VENDOR_ID_TI
-#define SDIO_VENDOR_ID_TI  0x0097
-#endif
-
-#ifndef SDIO_DEVICE_ID_TI_WL1271
-#define SDIO_DEVICE_ID_TI_WL1271 0x4076
-#endif
-
 static const struct mmc_fixup mmc_fixup_methods[] = {
+
  /* by default sdio devices are considered CLK_GATING broken */
  /* good cards will be whitelisted as they are tested */
- { SDIO_ANY_ID, SDIO_ANY_ID,
-  add_quirk_for_sdio_devices, MMC_QUIRK_BROKEN_CLK_GATING },
- { SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
-  remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING },
- { 0 }
+ SDIO_FIXUP(SDIO_ANY_ID, SDIO_ANY_ID,
+     add_quirk_for_sdio_devices,
+     MMC_QUIRK_BROKEN_CLK_GATING),
+
+ SDIO_FIXUP(SDIO_VENDOR_ID_TI, SDIO_DEVICE_ID_TI_WL1271,
+     remove_quirk, MMC_QUIRK_BROKEN_CLK_GATING),
+
+ END_FIXUP
 };
 
-void mmc_fixup_device(struct mmc_card *card)
+void mmc_fixup_device(struct mmc_card *card,
+ const struct mmc_fixup *table)
 {
  const struct mmc_fixup *f;
+ u64 rev = cid_rev_card(card);
+
+ /* Non-core specific workarounds. */
+ if (!table)
+  table = mmc_fixup_methods;
 
- for (f = mmc_fixup_methods; f->vendor_fixup; f++) {
-  if ((f->vendor == card->cis.vendor
-       || f->vendor == (u16) SDIO_ANY_ID) &&
-      (f->device == card->cis.device
-       || f->device == (u16) SDIO_ANY_ID)) {
+ for (f = table; f->vendor_fixup; f++) {
+  if ((f->manfid == CID_MANFID_ANY
+       || f->manfid == card->cid.manfid) &&
+      (f->oemid == CID_OEMID_ANY
+       || f->oemid == card->cid.oemid) &&
+      (f->name == CID_NAME_ANY
+       || !strcmp(f->name, card->cid.prod_name)) &&
+      (f->cis_vendor == card->cis.vendor
+       || f->cis_vendor == (u16) SDIO_ANY_ID) &&
+      (f->cis_device == card->cis.device
+      || f->cis_device == (u16) SDIO_ANY_ID) &&
+      rev >= f->rev_start &&
+      rev <= f->rev_end) {
    dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup);
    f->vendor_fixup(card, f->data);
   }
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index 50749f5..d3ec4dc 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -480,7 +480,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
   card = oldcard;
   return 0;
  }
- mmc_fixup_device(card);
+ mmc_fixup_device(card, NULL);
 
  if (card->type == MMC_TYPE_SD_COMBO) {
   err = mmc_sd_setup_card(host, card, oldcard != NULL);
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index fe9d7be..00fdeb9 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -11,6 +11,7 @@
 #define LINUX_MMC_CARD_H
 
 #include <linux/mmc/core.h>
+#include <linux/mod_devicetable.h>
 
 struct mmc_cid {
  unsigned int  manfid;
@@ -149,7 +150,93 @@ struct mmc_card {
  struct dentry  *debugfs_root;
 };
 
-void mmc_fixup_device(struct mmc_card *dev);
+/*
+ *  The world is not perfect and supplies us with broken mmc/sdio devices.
+ *  For at least a part of these bugs we need a work-around
+ */
+
+struct mmc_fixup {
+
+ /* CID-specific fields. */
+ const char *name;
+
+ /* Valid revision range */
+ u64 rev_start, rev_end;
+
+ unsigned int manfid;
+ unsigned short oemid;
+
+       /* SDIO-specfic fields. You can use SDIO_ANY_ID here of course */
+ u16 cis_vendor, cis_device;
+
+ void (*vendor_fixup)(struct mmc_card *card, int data);
+ int data;
+};
+
+#define CID_MANFID_ANY (-1ul)
+#define CID_OEMID_ANY ((unsigned short) -1)
+#define CID_NAME_ANY (NULL)
+
+#define END_FIXUP { 0 }
+
+#define _FIXUP_EXT(_name, _manfid, _oemid, _rev_start, _rev_end, \
+     _cis_vendor, _cis_device,    \
+     _fixup, _data)     \
+ {         \
+  .name = (_name),      \
+  .manfid = (_manfid),      \
+  .oemid = (_oemid),      \
+  .rev_start = (_rev_start),     \
+  .rev_end = (_rev_end),      \
+  .cis_vendor = (_cis_vendor),     \
+  .cis_device = (_cis_device),     \
+  .vendor_fixup = (_fixup),     \
+  .data = (_data),      \
+  }
+
+#define MMC_FIXUP_REV(_name, _manfid, _oemid, _rev_start, _rev_end, \
+        _fixup, _data)     \
+ _FIXUP_EXT(_name, _manfid,     \
+     _oemid, _rev_start, _rev_end,   \
+     SDIO_ANY_ID, SDIO_ANY_ID,    \
+     _fixup, _data)     \
+
+#define MMC_FIXUP(_name, _manfid, _oemid, _fixup, _data) \
+ MMC_FIXUP_REV(_name, _manfid, _oemid, 0, -1ull, _fixup, _data)
+
+#define SDIO_FIXUP(_vendor, _device, _fixup, _data)   \
+ _FIXUP_EXT(CID_NAME_ANY, CID_MANFID_ANY,   \
+      CID_OEMID_ANY, 0, -1ull,    \
+     _vendor, _device,     \
+     _fixup, _data)     \
+
+#define cid_rev(hwrev, fwrev, year, month) \
+ (((u64) hwrev) << 40 |                  \
+  ((u64) fwrev) << 32 |                  \
+  ((u64) year) << 16 |                   \
+  ((u64) month))
+
+#define cid_rev_card(card)    \
+ cid_rev(card->cid.hwrev,   \
+      card->cid.fwrev,      \
+      card->cid.year,   \
+      card->cid.month)
+
+/*
+ * This hook just adds a quirk unconditionally
+ */
+static inline void __maybe_unused add_quirk(struct mmc_card *card, int data)
+{
+ card->quirks |= data;
+}
+
+/*
+ * This hook just removes a quirk unconditionnally
+ */
+static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data)
+{
+ card->quirks &= ~data;
+}
 
 #define mmc_card_mmc(c)  ((c)->type == MMC_TYPE_MMC)
 #define mmc_card_sd(c)  ((c)->type == MMC_TYPE_SD)
@@ -196,4 +283,7 @@ struct mmc_driver {
 extern int mmc_register_driver(struct mmc_driver *);
 extern void mmc_unregister_driver(struct mmc_driver *);
 
+extern void mmc_fixup_device(struct mmc_card *card,
+        const struct mmc_fixup *table);
+
 #endif
-- 
1.7.0.4

No comments:

Post a Comment