• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ricoh_mmc_disable(struct pci_dev * fw_dev)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 
ricoh_mmc_enable(struct pci_dev * fw_dev)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 
ricoh_mmc_probe(struct pci_dev * pdev,const struct pci_device_id * ent)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 
ricoh_mmc_remove(struct pci_dev * pdev)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 
ricoh_mmc_suspend_late(struct pci_dev * pdev,pm_message_t state)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 
ricoh_mmc_resume_early(struct pci_dev * pdev)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 
ricoh_mmc_drv_init(void)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 
ricoh_mmc_drv_exit(void)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 
263