diff options
Diffstat (limited to 'drivers/mmc/host/ricoh_mmc.c')
-rw-r--r-- | drivers/mmc/host/ricoh_mmc.c | 151 |
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 | |||
32 | static 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 | |||
42 | MODULE_DEVICE_TABLE(pci, pci_ids); | ||
43 | |||
44 | static 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 | |||
97 | static 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 | |||
118 | static 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 | |||
131 | static 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 | |||
140 | static void __exit ricoh_mmc_drv_exit(void) | ||
141 | { | ||
142 | pci_unregister_driver(&ricoh_mmc_driver); | ||
143 | } | ||
144 | |||
145 | module_init(ricoh_mmc_drv_init); | ||
146 | module_exit(ricoh_mmc_drv_exit); | ||
147 | |||
148 | MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>"); | ||
149 | MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver"); | ||
150 | MODULE_LICENSE("GPL"); | ||
151 | |||