--- /dev/null
+/*
+ *  ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
+ *
+ *  Copyright (C) 2007 Philip Langdale, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+/*
+ * This is a conceptually ridiculous driver, but it is required by the way
+ * the Ricoh multi-function R5C832 works. This chip implements firewire
+ * and four different memory card controllers. Two of those controllers are
+ * an SDHCI controller and a proprietary MMC controller. The linux SDHCI
+ * driver supports MMC cards but the chip detects MMC cards in hardware
+ * and directs them to the MMC controller - so the SDHCI driver never sees
+ * them. To get around this, we must disable the useless MMC controller.
+ * At that point, the SDHCI controller will start seeing them. As a bonus,
+ * a detection event occurs immediately, even if the MMC card is already
+ * in the reader.
+ *
+ * The relevant registers live on the firewire function, so this is unavoidably
+ * ugly. Such is life.
+ */
+
+#include <linux/pci.h>
+
+#define DRIVER_NAME "ricoh-mmc"
+
+static const struct pci_device_id pci_ids[] __devinitdata = {
+       {
+               .vendor         = PCI_VENDOR_ID_RICOH,
+               .device         = PCI_DEVICE_ID_RICOH_R5C843,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+       },
+       { /* end: all zeroes */ },
+};
+
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
+                                    const struct pci_device_id *ent)
+{
+       u8 rev;
+
+       struct pci_dev *fw_dev = NULL;
+
+       BUG_ON(pdev == NULL);
+       BUG_ON(ent == NULL);
+
+       pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
+
+       printk(KERN_INFO DRIVER_NAME
+               ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
+               pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
+               (int)rev);
+
+       while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
+               if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
+                   pdev->bus == fw_dev->bus) {
+                       u8 write_enable;
+                       u8 disable;
+
+                       pci_read_config_byte(fw_dev, 0xCB, &disable);
+                       if (disable & 0x02) {
+                               printk(KERN_INFO DRIVER_NAME
+                                      ": Controller already disabled. Nothing to do.\n");
+                               return -ENODEV;
+                       }
+
+                       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+                       pci_write_config_byte(fw_dev, 0xCA, 0x57);
+                       pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
+                       pci_write_config_byte(fw_dev, 0xCA, write_enable);
+
+                       pci_set_drvdata(pdev, fw_dev);
+
+                       printk(KERN_INFO DRIVER_NAME
+                              ": Controller is now disabled.\n");
+
+                       break;
+               }
+       }
+
+       if (pci_get_drvdata(pdev) == NULL) {
+               printk(KERN_WARNING DRIVER_NAME
+                      ": Main firewire function not found. Cannot disable controller.\n");
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
+{
+       u8 write_enable;
+       u8 disable;
+       struct pci_dev *fw_dev = NULL;
+
+       fw_dev = pci_get_drvdata(pdev);
+       BUG_ON(fw_dev == NULL);
+
+       pci_read_config_byte(fw_dev, 0xCA, &write_enable);
+       pci_read_config_byte(fw_dev, 0xCB, &disable);
+       pci_write_config_byte(fw_dev, 0xCA, 0x57);
+       pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
+       pci_write_config_byte(fw_dev, 0xCA, write_enable);
+
+       printk(KERN_INFO DRIVER_NAME
+              ": Controller is now re-enabled.\n");
+
+       pci_set_drvdata(pdev, NULL);
+}
+
+static struct pci_driver ricoh_mmc_driver = {
+       .name =         DRIVER_NAME,
+       .id_table =     pci_ids,
+       .probe =        ricoh_mmc_probe,
+       .remove =       __devexit_p(ricoh_mmc_remove),
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Driver init/exit                                                          *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __init ricoh_mmc_drv_init(void)
+{
+       printk(KERN_INFO DRIVER_NAME
+               ": Ricoh MMC Controller disabling driver\n");
+       printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
+
+       return pci_register_driver(&ricoh_mmc_driver);
+}
+
+static void __exit ricoh_mmc_drv_exit(void)
+{
+       pci_unregister_driver(&ricoh_mmc_driver);
+}
+
+module_init(ricoh_mmc_drv_init);
+module_exit(ricoh_mmc_drv_exit);
+
+MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
+MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
+MODULE_LICENSE("GPL");
+