diff options
Diffstat (limited to 'drivers/mmc/host/ricoh_mmc.c')
-rw-r--r-- | drivers/mmc/host/ricoh_mmc.c | 262 |
1 files changed, 0 insertions, 262 deletions
diff --git a/drivers/mmc/host/ricoh_mmc.c b/drivers/mmc/host/ricoh_mmc.c deleted file mode 100644 index f62790513322..000000000000 --- a/drivers/mmc/host/ricoh_mmc.c +++ /dev/null | |||
@@ -1,262 +0,0 @@ | |||
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 chips (R5CXXX) work. These chips implement | ||
15 | * the four main memory card controllers (SD, MMC, MS, xD) and one or both | ||
16 | * of cardbus or firewire. It happens that they implement SD and MMC | ||
17 | * support as separate controllers (and PCI functions). The linux SDHCI | ||
18 | * driver supports MMC cards but the chip detects MMC cards in hardware | ||
19 | * and directs them to the MMC controller - so the SDHCI driver never sees | ||
20 | * them. To get around this, we must disable the useless MMC controller. | ||
21 | * At that point, the SDHCI controller will start seeing them. As a bonus, | ||
22 | * a detection event occurs immediately, even if the MMC card is already | ||
23 | * in the reader. | ||
24 | * | ||
25 | * It seems to be the case that the relevant PCI registers to deactivate the | ||
26 | * MMC controller live on PCI function 0, which might be the cardbus controller | ||
27 | * or the firewire controller, depending on the particular chip in question. As | ||
28 | * such, it makes what this driver has to do unavoidably ugly. Such is life. | ||
29 | */ | ||
30 | |||
31 | #include <linux/pci.h> | ||
32 | |||
33 | #define DRIVER_NAME "ricoh-mmc" | ||
34 | |||
35 | static const struct pci_device_id pci_ids[] __devinitdata = { | ||
36 | { | ||
37 | .vendor = PCI_VENDOR_ID_RICOH, | ||
38 | .device = PCI_DEVICE_ID_RICOH_R5C843, | ||
39 | .subvendor = PCI_ANY_ID, | ||
40 | .subdevice = PCI_ANY_ID, | ||
41 | }, | ||
42 | { /* end: all zeroes */ }, | ||
43 | }; | ||
44 | |||
45 | MODULE_DEVICE_TABLE(pci, pci_ids); | ||
46 | |||
47 | static int ricoh_mmc_disable(struct pci_dev *fw_dev) | ||
48 | { | ||
49 | u8 write_enable; | ||
50 | u8 write_target; | ||
51 | u8 disable; | ||
52 | |||
53 | if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) { | ||
54 | /* via RL5C476 */ | ||
55 | |||
56 | pci_read_config_byte(fw_dev, 0xB7, &disable); | ||
57 | if (disable & 0x02) { | ||
58 | printk(KERN_INFO DRIVER_NAME | ||
59 | ": Controller already disabled. " \ | ||
60 | "Nothing to do.\n"); | ||
61 | return -ENODEV; | ||
62 | } | ||
63 | |||
64 | pci_read_config_byte(fw_dev, 0x8E, &write_enable); | ||
65 | pci_write_config_byte(fw_dev, 0x8E, 0xAA); | ||
66 | pci_read_config_byte(fw_dev, 0x8D, &write_target); | ||
67 | pci_write_config_byte(fw_dev, 0x8D, 0xB7); | ||
68 | pci_write_config_byte(fw_dev, 0xB7, disable | 0x02); | ||
69 | pci_write_config_byte(fw_dev, 0x8E, write_enable); | ||
70 | pci_write_config_byte(fw_dev, 0x8D, write_target); | ||
71 | } else { | ||
72 | /* via R5C832 */ | ||
73 | |||
74 | pci_read_config_byte(fw_dev, 0xCB, &disable); | ||
75 | if (disable & 0x02) { | ||
76 | printk(KERN_INFO DRIVER_NAME | ||
77 | ": Controller already disabled. " \ | ||
78 | "Nothing to do.\n"); | ||
79 | return -ENODEV; | ||
80 | } | ||
81 | |||
82 | pci_read_config_byte(fw_dev, 0xCA, &write_enable); | ||
83 | pci_write_config_byte(fw_dev, 0xCA, 0x57); | ||
84 | pci_write_config_byte(fw_dev, 0xCB, disable | 0x02); | ||
85 | pci_write_config_byte(fw_dev, 0xCA, write_enable); | ||
86 | } | ||
87 | |||
88 | printk(KERN_INFO DRIVER_NAME | ||
89 | ": Controller is now disabled.\n"); | ||
90 | |||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | static int ricoh_mmc_enable(struct pci_dev *fw_dev) | ||
95 | { | ||
96 | u8 write_enable; | ||
97 | u8 write_target; | ||
98 | u8 disable; | ||
99 | |||
100 | if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) { | ||
101 | /* via RL5C476 */ | ||
102 | |||
103 | pci_read_config_byte(fw_dev, 0x8E, &write_enable); | ||
104 | pci_write_config_byte(fw_dev, 0x8E, 0xAA); | ||
105 | pci_read_config_byte(fw_dev, 0x8D, &write_target); | ||
106 | pci_write_config_byte(fw_dev, 0x8D, 0xB7); | ||
107 | pci_read_config_byte(fw_dev, 0xB7, &disable); | ||
108 | pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02); | ||
109 | pci_write_config_byte(fw_dev, 0x8E, write_enable); | ||
110 | pci_write_config_byte(fw_dev, 0x8D, write_target); | ||
111 | } else { | ||
112 | /* via R5C832 */ | ||
113 | |||
114 | pci_read_config_byte(fw_dev, 0xCA, &write_enable); | ||
115 | pci_read_config_byte(fw_dev, 0xCB, &disable); | ||
116 | pci_write_config_byte(fw_dev, 0xCA, 0x57); | ||
117 | pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02); | ||
118 | pci_write_config_byte(fw_dev, 0xCA, write_enable); | ||
119 | } | ||
120 | |||
121 | printk(KERN_INFO DRIVER_NAME | ||
122 | ": Controller is now re-enabled.\n"); | ||
123 | |||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int __devinit ricoh_mmc_probe(struct pci_dev *pdev, | ||
128 | const struct pci_device_id *ent) | ||
129 | { | ||
130 | u8 rev; | ||
131 | u8 ctrlfound = 0; | ||
132 | |||
133 | struct pci_dev *fw_dev = NULL; | ||
134 | |||
135 | BUG_ON(pdev == NULL); | ||
136 | BUG_ON(ent == NULL); | ||
137 | |||
138 | pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); | ||
139 | |||
140 | printk(KERN_INFO DRIVER_NAME | ||
141 | ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n", | ||
142 | pci_name(pdev), (int)pdev->vendor, (int)pdev->device, | ||
143 | (int)rev); | ||
144 | |||
145 | while ((fw_dev = | ||
146 | pci_get_device(PCI_VENDOR_ID_RICOH, | ||
147 | PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) { | ||
148 | if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) && | ||
149 | PCI_FUNC(fw_dev->devfn) == 0 && | ||
150 | pdev->bus == fw_dev->bus) { | ||
151 | if (ricoh_mmc_disable(fw_dev) != 0) | ||
152 | return -ENODEV; | ||
153 | |||
154 | pci_set_drvdata(pdev, fw_dev); | ||
155 | |||
156 | ++ctrlfound; | ||
157 | break; | ||
158 | } | ||
159 | } | ||
160 | |||
161 | fw_dev = NULL; | ||
162 | |||
163 | while (!ctrlfound && | ||
164 | (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH, | ||
165 | PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) { | ||
166 | if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) && | ||
167 | PCI_FUNC(fw_dev->devfn) == 0 && | ||
168 | pdev->bus == fw_dev->bus) { | ||
169 | if (ricoh_mmc_disable(fw_dev) != 0) | ||
170 | return -ENODEV; | ||
171 | |||
172 | pci_set_drvdata(pdev, fw_dev); | ||
173 | |||
174 | ++ctrlfound; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | if (!ctrlfound) { | ||
179 | printk(KERN_WARNING DRIVER_NAME | ||
180 | ": Main Ricoh function not found. Cannot disable controller.\n"); | ||
181 | return -ENODEV; | ||
182 | } | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static void __devexit ricoh_mmc_remove(struct pci_dev *pdev) | ||
188 | { | ||
189 | struct pci_dev *fw_dev = NULL; | ||
190 | |||
191 | fw_dev = pci_get_drvdata(pdev); | ||
192 | BUG_ON(fw_dev == NULL); | ||
193 | |||
194 | ricoh_mmc_enable(fw_dev); | ||
195 | |||
196 | pci_set_drvdata(pdev, NULL); | ||
197 | } | ||
198 | |||
199 | static int ricoh_mmc_suspend_late(struct pci_dev *pdev, pm_message_t state) | ||
200 | { | ||
201 | struct pci_dev *fw_dev = NULL; | ||
202 | |||
203 | fw_dev = pci_get_drvdata(pdev); | ||
204 | BUG_ON(fw_dev == NULL); | ||
205 | |||
206 | printk(KERN_INFO DRIVER_NAME ": Suspending.\n"); | ||
207 | |||
208 | ricoh_mmc_enable(fw_dev); | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | static int ricoh_mmc_resume_early(struct pci_dev *pdev) | ||
214 | { | ||
215 | struct pci_dev *fw_dev = NULL; | ||
216 | |||
217 | fw_dev = pci_get_drvdata(pdev); | ||
218 | BUG_ON(fw_dev == NULL); | ||
219 | |||
220 | printk(KERN_INFO DRIVER_NAME ": Resuming.\n"); | ||
221 | |||
222 | ricoh_mmc_disable(fw_dev); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static struct pci_driver ricoh_mmc_driver = { | ||
228 | .name = DRIVER_NAME, | ||
229 | .id_table = pci_ids, | ||
230 | .probe = ricoh_mmc_probe, | ||
231 | .remove = __devexit_p(ricoh_mmc_remove), | ||
232 | .suspend_late = ricoh_mmc_suspend_late, | ||
233 | .resume_early = ricoh_mmc_resume_early, | ||
234 | }; | ||
235 | |||
236 | /*****************************************************************************\ | ||
237 | * * | ||
238 | * Driver init/exit * | ||
239 | * * | ||
240 | \*****************************************************************************/ | ||
241 | |||
242 | static int __init ricoh_mmc_drv_init(void) | ||
243 | { | ||
244 | printk(KERN_INFO DRIVER_NAME | ||
245 | ": Ricoh MMC Controller disabling driver\n"); | ||
246 | printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n"); | ||
247 | |||
248 | return pci_register_driver(&ricoh_mmc_driver); | ||
249 | } | ||
250 | |||
251 | static void __exit ricoh_mmc_drv_exit(void) | ||
252 | { | ||
253 | pci_unregister_driver(&ricoh_mmc_driver); | ||
254 | } | ||
255 | |||
256 | module_init(ricoh_mmc_drv_init); | ||
257 | module_exit(ricoh_mmc_drv_exit); | ||
258 | |||
259 | MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>"); | ||
260 | MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver"); | ||
261 | MODULE_LICENSE("GPL"); | ||
262 | |||