aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/ricoh_mmc.c
diff options
context:
space:
mode:
authorPhilip Langdale <philipl@overt.org>2007-09-15 15:54:08 -0400
committerPierre Ossman <drzeus@drzeus.cx>2007-10-03 12:40:05 -0400
commit5ae70296c85f96a9969891d9de3410ebdf210b71 (patch)
treeaff80c38b678fe3cfba7062901f27186ee0f31bb /drivers/mmc/host/ricoh_mmc.c
parent6f4285d13300f1c8cd675a41ab390cea06173cd1 (diff)
mmc: Disabler for Ricoh MMC controller
Thanks to Matt Domsch and Rezwanul Kabir at Dell, we know how to disable the MMC controller on the multi-function Ricoh R5C832. The MMC controller needs to be disabled or it will steal MMC cards from the SD controller where they would otherwise be supported by the Linux SDHCI driver. Signed-off-by: Philipl Langdale <philipl@overt.org> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc/host/ricoh_mmc.c')
-rw-r--r--drivers/mmc/host/ricoh_mmc.c151
1 files changed, 151 insertions, 0 deletions
diff --git a/drivers/mmc/host/ricoh_mmc.c b/drivers/mmc/host/ricoh_mmc.c
new file mode 100644
index 000000000000..1e8704533bc5
--- /dev/null
+++ b/drivers/mmc/host/ricoh_mmc.c
@@ -0,0 +1,151 @@
1/*
2 * ricoh_mmc.c - Dummy driver to disable the Rioch MMC controller.
3 *
4 * Copyright (C) 2007 Philip Langdale, All Rights Reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 */
11
12/*
13 * This is a conceptually ridiculous driver, but it is required by the way
14 * the Ricoh multi-function R5C832 works. This chip implements firewire
15 * and four different memory card controllers. Two of those controllers are
16 * an SDHCI controller and a proprietary MMC controller. The linux SDHCI
17 * driver supports MMC cards but the chip detects MMC cards in hardware
18 * and directs them to the MMC controller - so the SDHCI driver never sees
19 * them. To get around this, we must disable the useless MMC controller.
20 * At that point, the SDHCI controller will start seeing them. As a bonus,
21 * a detection event occurs immediately, even if the MMC card is already
22 * in the reader.
23 *
24 * The relevant registers live on the firewire function, so this is unavoidably
25 * ugly. Such is life.
26 */
27
28#include <linux/pci.h>
29
30#define DRIVER_NAME "ricoh-mmc"
31
32static const struct pci_device_id pci_ids[] __devinitdata = {
33 {
34 .vendor = PCI_VENDOR_ID_RICOH,
35 .device = PCI_DEVICE_ID_RICOH_R5C843,
36 .subvendor = PCI_ANY_ID,
37 .subdevice = PCI_ANY_ID,
38 },
39 { /* end: all zeroes */ },
40};
41
42MODULE_DEVICE_TABLE(pci, pci_ids);
43
44static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
45 const struct pci_device_id *ent)
46{
47 u8 rev;
48
49 struct pci_dev *fw_dev = NULL;
50
51 BUG_ON(pdev == NULL);
52 BUG_ON(ent == NULL);
53
54 pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
55
56 printk(KERN_INFO DRIVER_NAME
57 ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
58 pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
59 (int)rev);
60
61 while ((fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
62 if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
63 pdev->bus == fw_dev->bus) {
64 u8 write_enable;
65 u8 disable;
66
67 pci_read_config_byte(fw_dev, 0xCB, &disable);
68 if (disable & 0x02) {
69 printk(KERN_INFO DRIVER_NAME
70 ": Controller already disabled. Nothing to do.\n");
71 return -ENODEV;
72 }
73
74 pci_read_config_byte(fw_dev, 0xCA, &write_enable);
75 pci_write_config_byte(fw_dev, 0xCA, 0x57);
76 pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
77 pci_write_config_byte(fw_dev, 0xCA, write_enable);
78
79 pci_set_drvdata(pdev, fw_dev);
80
81 printk(KERN_INFO DRIVER_NAME
82 ": Controller is now disabled.\n");
83
84 break;
85 }
86 }
87
88 if (pci_get_drvdata(pdev) == NULL) {
89 printk(KERN_WARNING DRIVER_NAME
90 ": Main firewire function not found. Cannot disable controller.\n");
91 return -ENODEV;
92 }
93
94 return 0;
95}
96
97static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
98{
99 u8 write_enable;
100 u8 disable;
101 struct pci_dev *fw_dev = NULL;
102
103 fw_dev = pci_get_drvdata(pdev);
104 BUG_ON(fw_dev == NULL);
105
106 pci_read_config_byte(fw_dev, 0xCA, &write_enable);
107 pci_read_config_byte(fw_dev, 0xCB, &disable);
108 pci_write_config_byte(fw_dev, 0xCA, 0x57);
109 pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
110 pci_write_config_byte(fw_dev, 0xCA, write_enable);
111
112 printk(KERN_INFO DRIVER_NAME
113 ": Controller is now re-enabled.\n");
114
115 pci_set_drvdata(pdev, NULL);
116}
117
118static struct pci_driver ricoh_mmc_driver = {
119 .name = DRIVER_NAME,
120 .id_table = pci_ids,
121 .probe = ricoh_mmc_probe,
122 .remove = __devexit_p(ricoh_mmc_remove),
123};
124
125/*****************************************************************************\
126 * *
127 * Driver init/exit *
128 * *
129\*****************************************************************************/
130
131static int __init ricoh_mmc_drv_init(void)
132{
133 printk(KERN_INFO DRIVER_NAME
134 ": Ricoh MMC Controller disabling driver\n");
135 printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
136
137 return pci_register_driver(&ricoh_mmc_driver);
138}
139
140static void __exit ricoh_mmc_drv_exit(void)
141{
142 pci_unregister_driver(&ricoh_mmc_driver);
143}
144
145module_init(ricoh_mmc_drv_init);
146module_exit(ricoh_mmc_drv_exit);
147
148MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
149MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
150MODULE_LICENSE("GPL");
151