bfin_cf_pcmcia.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * file: drivers/pcmcia/bfin_cf.c
  3. *
  4. * based on: drivers/pcmcia/omap_cf.c
  5. * omap_cf.c -- OMAP 16xx CompactFlash controller driver
  6. *
  7. * Copyright (c) 2005 David Brownell
  8. * Copyright (c) 2006-2008 Michael Hennerich Analog Devices Inc.
  9. *
  10. * bugs: enter bugs at http://blackfin.uclinux.org/
  11. *
  12. * this program is free software; you can redistribute it and/or modify
  13. * it under the terms of the gnu general public license as published by
  14. * the free software foundation; either version 2, or (at your option)
  15. * any later version.
  16. *
  17. * this program is distributed in the hope that it will be useful,
  18. * but without any warranty; without even the implied warranty of
  19. * merchantability or fitness for a particular purpose. see the
  20. * gnu general public license for more details.
  21. *
  22. * you should have received a copy of the gnu general public license
  23. * along with this program; see the file copying.
  24. * if not, write to the free software foundation,
  25. * 59 temple place - suite 330, boston, ma 02111-1307, usa.
  26. */
  27. #include <linux/module.h>
  28. #include <linux/kernel.h>
  29. #include <linux/sched.h>
  30. #include <linux/platform_device.h>
  31. #include <linux/errno.h>
  32. #include <linux/init.h>
  33. #include <linux/delay.h>
  34. #include <linux/interrupt.h>
  35. #include <linux/irq.h>
  36. #include <linux/io.h>
  37. #include <pcmcia/ss.h>
  38. #include <pcmcia/cisreg.h>
  39. #include <asm/gpio.h>
  40. #define SZ_1K 0x00000400
  41. #define SZ_8K 0x00002000
  42. #define SZ_2K (2 * SZ_1K)
  43. #define POLL_INTERVAL (2 * HZ)
  44. #define CF_ATASEL_ENA 0x20311802 /* Inverts RESET */
  45. #define CF_ATASEL_DIS 0x20311800
  46. #define bfin_cf_present(pfx) (gpio_get_value(pfx))
  47. /*--------------------------------------------------------------------------*/
  48. static const char driver_name[] = "bfin_cf_pcmcia";
  49. struct bfin_cf_socket {
  50. struct pcmcia_socket socket;
  51. struct timer_list timer;
  52. unsigned present:1;
  53. unsigned active:1;
  54. struct platform_device *pdev;
  55. unsigned long phys_cf_io;
  56. unsigned long phys_cf_attr;
  57. u_int irq;
  58. u_short cd_pfx;
  59. };
  60. /*--------------------------------------------------------------------------*/
  61. static int bfin_cf_reset(void)
  62. {
  63. outw(0, CF_ATASEL_ENA);
  64. mdelay(200);
  65. outw(0, CF_ATASEL_DIS);
  66. return 0;
  67. }
  68. static int bfin_cf_ss_init(struct pcmcia_socket *s)
  69. {
  70. return 0;
  71. }
  72. /* the timer is primarily to kick this socket's pccardd */
  73. static void bfin_cf_timer(unsigned long _cf)
  74. {
  75. struct bfin_cf_socket *cf = (void *)_cf;
  76. unsigned short present = bfin_cf_present(cf->cd_pfx);
  77. if (present != cf->present) {
  78. cf->present = present;
  79. dev_dbg(&cf->pdev->dev, ": card %s\n",
  80. present ? "present" : "gone");
  81. pcmcia_parse_events(&cf->socket, SS_DETECT);
  82. }
  83. if (cf->active)
  84. mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
  85. }
  86. static int bfin_cf_get_status(struct pcmcia_socket *s, u_int *sp)
  87. {
  88. struct bfin_cf_socket *cf;
  89. if (!sp)
  90. return -EINVAL;
  91. cf = container_of(s, struct bfin_cf_socket, socket);
  92. if (bfin_cf_present(cf->cd_pfx)) {
  93. *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
  94. s->irq.AssignedIRQ = 0;
  95. s->pci_irq = cf->irq;
  96. } else
  97. *sp = 0;
  98. return 0;
  99. }
  100. static int
  101. bfin_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
  102. {
  103. struct bfin_cf_socket *cf;
  104. cf = container_of(sock, struct bfin_cf_socket, socket);
  105. switch (s->Vcc) {
  106. case 0:
  107. case 33:
  108. break;
  109. case 50:
  110. break;
  111. default:
  112. return -EINVAL;
  113. }
  114. if (s->flags & SS_RESET) {
  115. disable_irq(cf->irq);
  116. bfin_cf_reset();
  117. enable_irq(cf->irq);
  118. }
  119. dev_dbg(&cf->pdev->dev, ": Vcc %d, io_irq %d, flags %04x csc %04x\n",
  120. s->Vcc, s->io_irq, s->flags, s->csc_mask);
  121. return 0;
  122. }
  123. static int bfin_cf_ss_suspend(struct pcmcia_socket *s)
  124. {
  125. return bfin_cf_set_socket(s, &dead_socket);
  126. }
  127. /* regions are 2K each: mem, attrib, io (and reserved-for-ide) */
  128. static int bfin_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
  129. {
  130. struct bfin_cf_socket *cf;
  131. cf = container_of(s, struct bfin_cf_socket, socket);
  132. io->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
  133. io->start = cf->phys_cf_io;
  134. io->stop = io->start + SZ_2K - 1;
  135. return 0;
  136. }
  137. static int
  138. bfin_cf_set_mem_map(struct pcmcia_socket *s, struct pccard_mem_map *map)
  139. {
  140. struct bfin_cf_socket *cf;
  141. if (map->card_start)
  142. return -EINVAL;
  143. cf = container_of(s, struct bfin_cf_socket, socket);
  144. map->static_start = cf->phys_cf_io;
  145. map->flags &= MAP_ACTIVE | MAP_ATTRIB | MAP_16BIT;
  146. if (map->flags & MAP_ATTRIB)
  147. map->static_start = cf->phys_cf_attr;
  148. return 0;
  149. }
  150. static struct pccard_operations bfin_cf_ops = {
  151. .init = bfin_cf_ss_init,
  152. .suspend = bfin_cf_ss_suspend,
  153. .get_status = bfin_cf_get_status,
  154. .set_socket = bfin_cf_set_socket,
  155. .set_io_map = bfin_cf_set_io_map,
  156. .set_mem_map = bfin_cf_set_mem_map,
  157. };
  158. /*--------------------------------------------------------------------------*/
  159. static int __devinit bfin_cf_probe(struct platform_device *pdev)
  160. {
  161. struct bfin_cf_socket *cf;
  162. struct resource *io_mem, *attr_mem;
  163. int irq;
  164. unsigned short cd_pfx;
  165. int status = 0;
  166. dev_info(&pdev->dev, "Blackfin CompactFlash/PCMCIA Socket Driver\n");
  167. irq = platform_get_irq(pdev, 0);
  168. if (!irq)
  169. return -EINVAL;
  170. cd_pfx = platform_get_irq(pdev, 1); /*Card Detect GPIO PIN */
  171. if (gpio_request(cd_pfx, "pcmcia: CD")) {
  172. dev_err(&pdev->dev,
  173. "Failed ro request Card Detect GPIO_%d\n",
  174. cd_pfx);
  175. return -EBUSY;
  176. }
  177. gpio_direction_input(cd_pfx);
  178. cf = kzalloc(sizeof *cf, GFP_KERNEL);
  179. if (!cf) {
  180. gpio_free(cd_pfx);
  181. return -ENOMEM;
  182. }
  183. cf->cd_pfx = cd_pfx;
  184. setup_timer(&cf->timer, bfin_cf_timer, (unsigned long)cf);
  185. cf->pdev = pdev;
  186. platform_set_drvdata(pdev, cf);
  187. cf->irq = irq;
  188. cf->socket.pci_irq = irq;
  189. set_irq_type(irq, IRQF_TRIGGER_LOW);
  190. io_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  191. attr_mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  192. if (!io_mem || !attr_mem)
  193. goto fail0;
  194. cf->phys_cf_io = io_mem->start;
  195. cf->phys_cf_attr = attr_mem->start;
  196. /* pcmcia layer only remaps "real" memory */
  197. cf->socket.io_offset = (unsigned long)
  198. ioremap(cf->phys_cf_io, SZ_2K);
  199. if (!cf->socket.io_offset)
  200. goto fail0;
  201. dev_err(&pdev->dev, ": on irq %d\n", irq);
  202. dev_dbg(&pdev->dev, ": %s\n",
  203. bfin_cf_present(cf->cd_pfx) ? "present" : "(not present)");
  204. cf->socket.owner = THIS_MODULE;
  205. cf->socket.dev.parent = &pdev->dev;
  206. cf->socket.ops = &bfin_cf_ops;
  207. cf->socket.resource_ops = &pccard_static_ops;
  208. cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
  209. | SS_CAP_MEM_ALIGN;
  210. cf->socket.map_size = SZ_2K;
  211. status = pcmcia_register_socket(&cf->socket);
  212. if (status < 0)
  213. goto fail2;
  214. cf->active = 1;
  215. mod_timer(&cf->timer, jiffies + POLL_INTERVAL);
  216. return 0;
  217. fail2:
  218. iounmap((void __iomem *)cf->socket.io_offset);
  219. release_mem_region(cf->phys_cf_io, SZ_8K);
  220. fail0:
  221. gpio_free(cf->cd_pfx);
  222. kfree(cf);
  223. platform_set_drvdata(pdev, NULL);
  224. return status;
  225. }
  226. static int __devexit bfin_cf_remove(struct platform_device *pdev)
  227. {
  228. struct bfin_cf_socket *cf = platform_get_drvdata(pdev);
  229. gpio_free(cf->cd_pfx);
  230. cf->active = 0;
  231. pcmcia_unregister_socket(&cf->socket);
  232. del_timer_sync(&cf->timer);
  233. iounmap((void __iomem *)cf->socket.io_offset);
  234. release_mem_region(cf->phys_cf_io, SZ_8K);
  235. platform_set_drvdata(pdev, NULL);
  236. kfree(cf);
  237. return 0;
  238. }
  239. static int bfin_cf_suspend(struct platform_device *pdev, pm_message_t mesg)
  240. {
  241. return pcmcia_socket_dev_suspend(&pdev->dev, mesg);
  242. }
  243. static int bfin_cf_resume(struct platform_device *pdev)
  244. {
  245. return pcmcia_socket_dev_resume(&pdev->dev);
  246. }
  247. static struct platform_driver bfin_cf_driver = {
  248. .driver = {
  249. .name = (char *)driver_name,
  250. .owner = THIS_MODULE,
  251. },
  252. .probe = bfin_cf_probe,
  253. .remove = __devexit_p(bfin_cf_remove),
  254. .suspend = bfin_cf_suspend,
  255. .resume = bfin_cf_resume,
  256. };
  257. static int __init bfin_cf_init(void)
  258. {
  259. return platform_driver_register(&bfin_cf_driver);
  260. }
  261. static void __exit bfin_cf_exit(void)
  262. {
  263. platform_driver_unregister(&bfin_cf_driver);
  264. }
  265. module_init(bfin_cf_init);
  266. module_exit(bfin_cf_exit);
  267. MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>")
  268. MODULE_DESCRIPTION("BFIN CF/PCMCIA Driver");
  269. MODULE_LICENSE("GPL");