ricoh_mmc.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  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 R5C832 works. This chip implements firewire
  14. * and four different memory card controllers. Two of those controllers are
  15. * an SDHCI controller and a proprietary MMC controller. The linux SDHCI
  16. * driver supports MMC cards but the chip detects MMC cards in hardware
  17. * and directs them to the MMC controller - so the SDHCI driver never sees
  18. * them. To get around this, we must disable the useless MMC controller.
  19. * At that point, the SDHCI controller will start seeing them. As a bonus,
  20. * a detection event occurs immediately, even if the MMC card is already
  21. * in the reader.
  22. *
  23. * The relevant registers live on the firewire function, so this is unavoidably
  24. * ugly. Such is life.
  25. */
  26. #include <linux/pci.h>
  27. #define DRIVER_NAME "ricoh-mmc"
  28. static const struct pci_device_id pci_ids[] __devinitdata = {
  29. {
  30. .vendor = PCI_VENDOR_ID_RICOH,
  31. .device = PCI_DEVICE_ID_RICOH_R5C843,
  32. .subvendor = PCI_ANY_ID,
  33. .subdevice = PCI_ANY_ID,
  34. },
  35. { /* end: all zeroes */ },
  36. };
  37. MODULE_DEVICE_TABLE(pci, pci_ids);
  38. static int ricoh_mmc_disable(struct pci_dev *fw_dev)
  39. {
  40. u8 write_enable;
  41. u8 write_target;
  42. u8 disable;
  43. if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
  44. /* via RL5C476 */
  45. pci_read_config_byte(fw_dev, 0xB7, &disable);
  46. if (disable & 0x02) {
  47. printk(KERN_INFO DRIVER_NAME
  48. ": Controller already disabled. " \
  49. "Nothing to do.\n");
  50. return -ENODEV;
  51. }
  52. pci_read_config_byte(fw_dev, 0x8E, &write_enable);
  53. pci_write_config_byte(fw_dev, 0x8E, 0xAA);
  54. pci_read_config_byte(fw_dev, 0x8D, &write_target);
  55. pci_write_config_byte(fw_dev, 0x8D, 0xB7);
  56. pci_write_config_byte(fw_dev, 0xB7, disable | 0x02);
  57. pci_write_config_byte(fw_dev, 0x8E, write_enable);
  58. pci_write_config_byte(fw_dev, 0x8D, write_target);
  59. } else {
  60. /* via R5C832 */
  61. pci_read_config_byte(fw_dev, 0xCB, &disable);
  62. if (disable & 0x02) {
  63. printk(KERN_INFO DRIVER_NAME
  64. ": Controller already disabled. " \
  65. "Nothing to do.\n");
  66. return -ENODEV;
  67. }
  68. pci_read_config_byte(fw_dev, 0xCA, &write_enable);
  69. pci_write_config_byte(fw_dev, 0xCA, 0x57);
  70. pci_write_config_byte(fw_dev, 0xCB, disable | 0x02);
  71. pci_write_config_byte(fw_dev, 0xCA, write_enable);
  72. }
  73. printk(KERN_INFO DRIVER_NAME
  74. ": Controller is now disabled.\n");
  75. return 0;
  76. }
  77. static int ricoh_mmc_enable(struct pci_dev *fw_dev)
  78. {
  79. u8 write_enable;
  80. u8 write_target;
  81. u8 disable;
  82. if (fw_dev->device == PCI_DEVICE_ID_RICOH_RL5C476) {
  83. /* via RL5C476 */
  84. pci_read_config_byte(fw_dev, 0x8E, &write_enable);
  85. pci_write_config_byte(fw_dev, 0x8E, 0xAA);
  86. pci_read_config_byte(fw_dev, 0x8D, &write_target);
  87. pci_write_config_byte(fw_dev, 0x8D, 0xB7);
  88. pci_read_config_byte(fw_dev, 0xB7, &disable);
  89. pci_write_config_byte(fw_dev, 0xB7, disable & ~0x02);
  90. pci_write_config_byte(fw_dev, 0x8E, write_enable);
  91. pci_write_config_byte(fw_dev, 0x8D, write_target);
  92. } else {
  93. /* via R5C832 */
  94. pci_read_config_byte(fw_dev, 0xCA, &write_enable);
  95. pci_read_config_byte(fw_dev, 0xCB, &disable);
  96. pci_write_config_byte(fw_dev, 0xCA, 0x57);
  97. pci_write_config_byte(fw_dev, 0xCB, disable & ~0x02);
  98. pci_write_config_byte(fw_dev, 0xCA, write_enable);
  99. }
  100. printk(KERN_INFO DRIVER_NAME
  101. ": Controller is now re-enabled.\n");
  102. return 0;
  103. }
  104. static int __devinit ricoh_mmc_probe(struct pci_dev *pdev,
  105. const struct pci_device_id *ent)
  106. {
  107. u8 rev;
  108. u8 ctrlfound = 0;
  109. struct pci_dev *fw_dev = NULL;
  110. BUG_ON(pdev == NULL);
  111. BUG_ON(ent == NULL);
  112. pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev);
  113. printk(KERN_INFO DRIVER_NAME
  114. ": Ricoh MMC controller found at %s [%04x:%04x] (rev %x)\n",
  115. pci_name(pdev), (int)pdev->vendor, (int)pdev->device,
  116. (int)rev);
  117. while ((fw_dev =
  118. pci_get_device(PCI_VENDOR_ID_RICOH,
  119. PCI_DEVICE_ID_RICOH_RL5C476, fw_dev))) {
  120. if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
  121. pdev->bus == fw_dev->bus) {
  122. if (ricoh_mmc_disable(fw_dev) != 0)
  123. return -ENODEV;
  124. pci_set_drvdata(pdev, fw_dev);
  125. ++ctrlfound;
  126. break;
  127. }
  128. }
  129. fw_dev = NULL;
  130. while (!ctrlfound &&
  131. (fw_dev = pci_get_device(PCI_VENDOR_ID_RICOH,
  132. PCI_DEVICE_ID_RICOH_R5C832, fw_dev))) {
  133. if (PCI_SLOT(pdev->devfn) == PCI_SLOT(fw_dev->devfn) &&
  134. pdev->bus == fw_dev->bus) {
  135. if (ricoh_mmc_disable(fw_dev) != 0)
  136. return -ENODEV;
  137. pci_set_drvdata(pdev, fw_dev);
  138. ++ctrlfound;
  139. }
  140. }
  141. if (!ctrlfound) {
  142. printk(KERN_WARNING DRIVER_NAME
  143. ": Main firewire function not found. Cannot disable controller.\n");
  144. return -ENODEV;
  145. }
  146. return 0;
  147. }
  148. static void __devexit ricoh_mmc_remove(struct pci_dev *pdev)
  149. {
  150. struct pci_dev *fw_dev = NULL;
  151. fw_dev = pci_get_drvdata(pdev);
  152. BUG_ON(fw_dev == NULL);
  153. ricoh_mmc_enable(fw_dev);
  154. pci_set_drvdata(pdev, NULL);
  155. }
  156. static int ricoh_mmc_suspend(struct pci_dev *pdev, pm_message_t state)
  157. {
  158. struct pci_dev *fw_dev = NULL;
  159. fw_dev = pci_get_drvdata(pdev);
  160. BUG_ON(fw_dev == NULL);
  161. printk(KERN_INFO DRIVER_NAME ": Suspending.\n");
  162. ricoh_mmc_enable(fw_dev);
  163. return 0;
  164. }
  165. static int ricoh_mmc_resume(struct pci_dev *pdev)
  166. {
  167. struct pci_dev *fw_dev = NULL;
  168. fw_dev = pci_get_drvdata(pdev);
  169. BUG_ON(fw_dev == NULL);
  170. printk(KERN_INFO DRIVER_NAME ": Resuming.\n");
  171. ricoh_mmc_disable(fw_dev);
  172. return 0;
  173. }
  174. static struct pci_driver ricoh_mmc_driver = {
  175. .name = DRIVER_NAME,
  176. .id_table = pci_ids,
  177. .probe = ricoh_mmc_probe,
  178. .remove = __devexit_p(ricoh_mmc_remove),
  179. .suspend = ricoh_mmc_suspend,
  180. .resume = ricoh_mmc_resume,
  181. };
  182. /*****************************************************************************\
  183. * *
  184. * Driver init/exit *
  185. * *
  186. \*****************************************************************************/
  187. static int __init ricoh_mmc_drv_init(void)
  188. {
  189. printk(KERN_INFO DRIVER_NAME
  190. ": Ricoh MMC Controller disabling driver\n");
  191. printk(KERN_INFO DRIVER_NAME ": Copyright(c) Philip Langdale\n");
  192. return pci_register_driver(&ricoh_mmc_driver);
  193. }
  194. static void __exit ricoh_mmc_drv_exit(void)
  195. {
  196. pci_unregister_driver(&ricoh_mmc_driver);
  197. }
  198. module_init(ricoh_mmc_drv_init);
  199. module_exit(ricoh_mmc_drv_exit);
  200. MODULE_AUTHOR("Philip Langdale <philipl@alumni.utexas.net>");
  201. MODULE_DESCRIPTION("Ricoh MMC Controller disabling driver");
  202. MODULE_LICENSE("GPL");