ricoh_mmc.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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. * This is a conceptually ridiculous driver, but it is required by the way
  13. * the Ricoh multi-function chips (R5CXXX) work. These chips implement
  14. * the four main memory card controllers (SD, MMC, MS, xD) and one or both
  15. * of cardbus or firewire. It happens that they implement SD and MMC
  16. * support as separate controllers (and PCI functions). 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. * It seems to be the case that the relevant PCI registers to deactivate the
  25. * MMC controller live on PCI function 0, which might be the cardbus controller
  26. * or the firewire controller, depending on the particular chip in question. As
  27. * such, it makes what this driver has to do unavoidably ugly. Such is life.
  28. */
  29. #include <linux/pci.h>
  30. #define DRIVER_NAME "ricoh-mmc"
  31. static const struct pci_device_id pci_ids[] __devinitdata = {
  32. {
  33. .vendor = PCI_VENDOR_ID_RICOH,
  34. .device = PCI_DEVICE_ID_RICOH_R5C843,
  35. .subvendor = PCI_ANY_ID,
  36. .subdevice = PCI_ANY_ID,
  37. },
  38. { /* end: all zeroes */ },
  39. };
  40. MODULE_DEVICE_TABLE(pci, pci_ids);
  41. static int ricoh_mmc_disable(struct pci_dev *fw_dev)
  42. {
  43. u8 write_enable;
  44. u8 write_target;
  45. u8 disable;
  46. if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
  47. /* via RL5C476 */
  48. pci_read_config_byte(fw_dev, 0xB7, &disable);
  49. if (disable & 0x02) {
  50. printk(KERN_INFO DRIVER_NAME
  51. ": Controller already disabled. " \
  52. "Nothing to do.\n");
  53. return -ENODEV;
  54. }
  55. pci_read_config_byte(fw_dev, 0x8E, &write_enable);
  56. pci_write_config_byte(fw_dev, 0x8E, 0xAA);
  57. pci_read_config_byte(fw_dev, 0x8D, &write_target);
  58. pci_write_config_byte(fw_dev, 0x8D, 0xB7);
  59. pci_write_config_byte(fw_dev, 0xB7, disable | 0x02);
  60. pci_write_config_byte(fw_dev, 0x8E, write_enable);
  61. pci_write_config_byte(fw_dev, 0x8D, write_target);
  62. } else {
  63. /* via R5C832 */
  64. pci_read_config_byte(fw_dev, 0xCB, &disable);
  65. if (disable & 0x02) {
  66. printk(KERN_INFO DRIVER_NAME
  67. ": Controller already disabled. " \
  68. "Nothing to do.\n");
  69. return -ENODEV;
  70. }
  71. pci_read_config_byte(fw_dev, 0xCA, &write_enable);
  72. pci_write_config_byte(fw_dev, 0xCA, 0x57);
  73. pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
  74. pci_write_config_byte(fw_dev, 0xCA, write_enable);
  75. }
  76. printk(KERN_INFO DRIVER_NAME
  77. ": Controller is now disabled.\n");
  78. return 0;
  79. }
  80. static int ricoh_mmc_enable(struct pci_dev *fw_dev)
  81. {
  82. u8 write_enable;
  83. u8 write_target;
  84. u8 disable;
  85. if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
  86. /* via RL5C476 */
  87. pci_read_config_byte(fw_dev, 0x8E, &write_enable);
  88. pci_write_config_byte(fw_dev, 0x8E, 0xAA);
  89. pci_read_config_byte(fw_dev, 0x8D, &write_target);
  90. pci_write_config_byte(fw_dev, 0x8D, 0xB7);
  91. pci_read_config_byte(fw_dev, 0xB7, &disable);
  92. pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02);
  93. pci_write_config_byte(fw_dev, 0x8E, write_enable);
  94. pci_write_config_byte(fw_dev, 0x8D, write_target);
  95. } else {
  96. /* via R5C832 */
  97. pci_read_config_byte(fw_dev, 0xCA, &write_enable);
  98. pci_read_config_byte(fw_dev, 0xCB, &disable);
  99. pci_write_config_byte(fw_dev, 0xCA, 0x57);
  100. pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
  101. pci_write_config_byte(fw_dev, 0xCA, write_enable);
  102. }
  103. printk(KERN_INFO DRIVER_NAME
  104. ": Controller is now re-enabled.\n");
  105. return 0;
  106. }
  107. static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
  108. const struct pci_device_id *ent)
  109. {
  110. u8 rev;
  111. u8 ctrlfound = 0;
  112. struct pci_dev *fw_dev = NULL;
  113. BUG_ON(pdev == NULL);
  114. BUG_ON(ent == NULL);
  115. pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
  116. printk(KERN_INFO DRIVER_NAME
  117. ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
  118. pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
  119. (int)rev);
  120. while ((fw_dev =
  121. pci_get_device(PCI_VENDOR_ID_RICOH,
  122. PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) {
  123. if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
  124. PCI_FUNC(fw_dev->devfn) == 0 &&
  125. pdev->bus == fw_dev->bus) {
  126. if (ricoh_mmc_disable(fw_dev) != 0)
  127. return -ENODEV;
  128. pci_set_drvdata(pdev, fw_dev);
  129. ++ctrlfound;
  130. break;
  131. }
  132. }
  133. fw_dev = NULL;
  134. while (!ctrlfound &&
  135. (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH,
  136. PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
  137. if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
  138. PCI_FUNC(fw_dev->devfn) == 0 &&
  139. pdev->bus == fw_dev->bus) {
  140. if (ricoh_mmc_disable(fw_dev) != 0)
  141. return -ENODEV;
  142. pci_set_drvdata(pdev, fw_dev);
  143. ++ctrlfound;
  144. }
  145. }
  146. if (!ctrlfound) {
  147. printk(KERN_WARNING DRIVER_NAME
  148. ": Main Ricoh function not found. Cannot disable controller.\n");
  149. return -ENODEV;
  150. }
  151. return 0;
  152. }
  153. static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
  154. {
  155. struct pci_dev *fw_dev = NULL;
  156. fw_dev = pci_get_drvdata(pdev);
  157. BUG_ON(fw_dev == NULL);
  158. ricoh_mmc_enable(fw_dev);
  159. pci_set_drvdata(pdev, NULL);
  160. }
  161. static int ricoh_mmc_suspend(struct pci_dev *pdev, pm_message_t state)
  162. {
  163. struct pci_dev *fw_dev = NULL;
  164. fw_dev = pci_get_drvdata(pdev);
  165. BUG_ON(fw_dev == NULL);
  166. printk(KERN_INFO DRIVER_NAME ": Suspending.\n");
  167. ricoh_mmc_enable(fw_dev);
  168. return 0;
  169. }
  170. static int ricoh_mmc_resume(struct pci_dev *pdev)
  171. {
  172. struct pci_dev *fw_dev = NULL;
  173. fw_dev = pci_get_drvdata(pdev);
  174. BUG_ON(fw_dev == NULL);
  175. printk(KERN_INFO DRIVER_NAME ": Resuming.\n");
  176. ricoh_mmc_disable(fw_dev);
  177. return 0;
  178. }
  179. static struct pci_driver ricoh_mmc_driver = {
  180. .name = DRIVER_NAME,
  181. .id_table = pci_ids,
  182. .probe = ricoh_mmc_probe,
  183. .remove = __devexit_p(ricoh_mmc_remove),
  184. .suspend = ricoh_mmc_suspend,
  185. .resume = ricoh_mmc_resume,
  186. };
  187. /*****************************************************************************\
  188. * *
  189. * Driver init/exit *
  190. * *
  191. \*****************************************************************************/
  192. static int __init ricoh_mmc_drv_init(void)
  193. {
  194. printk(KERN_INFO DRIVER_NAME
  195. ": Ricoh MMC Controller disabling driver\n");
  196. printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
  197. return pci_register_driver(&ricoh_mmc_driver);
  198. }
  199. static void __exit ricoh_mmc_drv_exit(void)
  200. {
  201. pci_unregister_driver(&ricoh_mmc_driver);
  202. }
  203. module_init(ricoh_mmc_drv_init);
  204. module_exit(ricoh_mmc_drv_exit);
  205. MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
  206. MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
  207. MODULE_LICENSE("GPL");