whci.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. * WHCI UWB Multi-interface Controller enumerator.
  3. *
  4. * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
  5. *
  6. * This file is released under the GNU GPL v2.
  7. */
  8. #include <linux/delay.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/pci.h>
  12. #include <linux/dma-mapping.h>
  13. #include <linux/slab.h>
  14. #include <linux/uwb/whci.h>
  15. #include <linux/uwb/umc.h>
  16. struct whci_card {
  17. struct pci_dev *pci;
  18. void __iomem *uwbbase;
  19. u8 n_caps;
  20. struct umc_dev *devs[0];
  21. };
  22. /* Fix faulty HW :( */
  23. static
  24. u64 whci_capdata_quirks(struct whci_card *card, u64 capdata)
  25. {
  26. u64 capdata_orig = capdata;
  27. struct pci_dev *pci_dev = card->pci;
  28. if (pci_dev->vendor == PCI_VENDOR_ID_INTEL
  29. && (pci_dev->device == 0x0c3b || pci_dev->device == 0004)
  30. && pci_dev->class == 0x0d1010) {
  31. switch (UWBCAPDATA_TO_CAP_ID(capdata)) {
  32. /* WLP capability has 0x100 bytes of aperture */
  33. case 0x80:
  34. capdata |= 0x40 << 8; break;
  35. /* WUSB capability has 0x80 bytes of aperture
  36. * and ID is 1 */
  37. case 0x02:
  38. capdata &= ~0xffff;
  39. capdata |= 0x2001;
  40. break;
  41. }
  42. }
  43. if (capdata_orig != capdata)
  44. dev_warn(&pci_dev->dev,
  45. "PCI v%04x d%04x c%06x#%02x: "
  46. "corrected capdata from %016Lx to %016Lx\n",
  47. pci_dev->vendor, pci_dev->device, pci_dev->class,
  48. (unsigned)UWBCAPDATA_TO_CAP_ID(capdata),
  49. (unsigned long long)capdata_orig,
  50. (unsigned long long)capdata);
  51. return capdata;
  52. }
  53. /**
  54. * whci_wait_for - wait for a WHCI register to be set
  55. *
  56. * Polls (for at most @max_ms ms) until '*@reg & @mask == @result'.
  57. */
  58. int whci_wait_for(struct device *dev, u32 __iomem *reg, u32 mask, u32 result,
  59. unsigned long max_ms, const char *tag)
  60. {
  61. unsigned t = 0;
  62. u32 val;
  63. for (;;) {
  64. val = le_readl(reg);
  65. if ((val & mask) == result)
  66. break;
  67. if (t >= max_ms) {
  68. dev_err(dev, "%s timed out\n", tag);
  69. return -ETIMEDOUT;
  70. }
  71. msleep(10);
  72. t += 10;
  73. }
  74. return 0;
  75. }
  76. EXPORT_SYMBOL_GPL(whci_wait_for);
  77. /*
  78. * NOTE: the capinfo and capdata registers are slightly different
  79. * (size and cap-id fields). So for cap #0, we need to fill
  80. * in. Size comes from the size of the register block
  81. * (statically calculated); cap_id comes from nowhere, we use
  82. * zero, that is reserved, for the radio controller, because
  83. * none was defined at the spec level.
  84. */
  85. static int whci_add_cap(struct whci_card *card, int n)
  86. {
  87. struct umc_dev *umc;
  88. u64 capdata;
  89. int bar, err;
  90. umc = umc_device_create(&card->pci->dev, n);
  91. if (umc == NULL)
  92. return -ENOMEM;
  93. capdata = le_readq(card->uwbbase + UWBCAPDATA(n));
  94. bar = UWBCAPDATA_TO_BAR(capdata) << 1;
  95. capdata = whci_capdata_quirks(card, capdata);
  96. /* Capability 0 is the radio controller. It's size is 32
  97. * bytes (WHCI0.95[2.3, T2-9]). */
  98. umc->version = UWBCAPDATA_TO_VERSION(capdata);
  99. umc->cap_id = n == 0 ? 0 : UWBCAPDATA_TO_CAP_ID(capdata);
  100. umc->bar = bar;
  101. umc->resource.start = pci_resource_start(card->pci, bar)
  102. + UWBCAPDATA_TO_OFFSET(capdata);
  103. umc->resource.end = umc->resource.start
  104. + (n == 0 ? 0x20 : UWBCAPDATA_TO_SIZE(capdata)) - 1;
  105. umc->resource.name = dev_name(&umc->dev);
  106. umc->resource.flags = card->pci->resource[bar].flags;
  107. umc->resource.parent = &card->pci->resource[bar];
  108. umc->irq = card->pci->irq;
  109. err = umc_device_register(umc);
  110. if (err < 0)
  111. goto error;
  112. card->devs[n] = umc;
  113. return 0;
  114. error:
  115. kfree(umc);
  116. return err;
  117. }
  118. static void whci_del_cap(struct whci_card *card, int n)
  119. {
  120. struct umc_dev *umc = card->devs[n];
  121. umc_device_unregister(umc);
  122. }
  123. static int whci_n_caps(struct pci_dev *pci)
  124. {
  125. void __iomem *uwbbase;
  126. u64 capinfo;
  127. uwbbase = pci_iomap(pci, 0, 8);
  128. if (!uwbbase)
  129. return -ENOMEM;
  130. capinfo = le_readq(uwbbase + UWBCAPINFO);
  131. pci_iounmap(pci, uwbbase);
  132. return UWBCAPINFO_TO_N_CAPS(capinfo);
  133. }
  134. static int whci_probe(struct pci_dev *pci, const struct pci_device_id *id)
  135. {
  136. struct whci_card *card;
  137. int err, n_caps, n;
  138. err = pci_enable_device(pci);
  139. if (err < 0)
  140. goto error;
  141. pci_enable_msi(pci);
  142. pci_set_master(pci);
  143. err = -ENXIO;
  144. if (!pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
  145. pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
  146. else if (!pci_set_dma_mask(pci, DMA_BIT_MASK(32)))
  147. pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
  148. else
  149. goto error_dma;
  150. err = n_caps = whci_n_caps(pci);
  151. if (n_caps < 0)
  152. goto error_ncaps;
  153. err = -ENOMEM;
  154. card = kzalloc(sizeof(struct whci_card)
  155. + sizeof(struct umc_dev *) * (n_caps + 1),
  156. GFP_KERNEL);
  157. if (card == NULL)
  158. goto error_kzalloc;
  159. card->pci = pci;
  160. card->n_caps = n_caps;
  161. err = -EBUSY;
  162. if (!request_mem_region(pci_resource_start(pci, 0),
  163. UWBCAPDATA_SIZE(card->n_caps),
  164. "whci (capability data)"))
  165. goto error_request_memregion;
  166. err = -ENOMEM;
  167. card->uwbbase = pci_iomap(pci, 0, UWBCAPDATA_SIZE(card->n_caps));
  168. if (!card->uwbbase)
  169. goto error_iomap;
  170. /* Add each capability. */
  171. for (n = 0; n <= card->n_caps; n++) {
  172. err = whci_add_cap(card, n);
  173. if (err < 0 && n == 0) {
  174. dev_err(&pci->dev, "cannot bind UWB radio controller:"
  175. " %d\n", err);
  176. goto error_bind;
  177. }
  178. if (err < 0)
  179. dev_warn(&pci->dev, "warning: cannot bind capability "
  180. "#%u: %d\n", n, err);
  181. }
  182. pci_set_drvdata(pci, card);
  183. return 0;
  184. error_bind:
  185. pci_iounmap(pci, card->uwbbase);
  186. error_iomap:
  187. release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps));
  188. error_request_memregion:
  189. kfree(card);
  190. error_kzalloc:
  191. error_ncaps:
  192. error_dma:
  193. pci_disable_msi(pci);
  194. pci_disable_device(pci);
  195. error:
  196. return err;
  197. }
  198. static void whci_remove(struct pci_dev *pci)
  199. {
  200. struct whci_card *card = pci_get_drvdata(pci);
  201. int n;
  202. pci_set_drvdata(pci, NULL);
  203. /* Unregister each capability in reverse (so the master device
  204. * is unregistered last). */
  205. for (n = card->n_caps; n >= 0 ; n--)
  206. whci_del_cap(card, n);
  207. pci_iounmap(pci, card->uwbbase);
  208. release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps));
  209. kfree(card);
  210. pci_disable_msi(pci);
  211. pci_disable_device(pci);
  212. }
  213. static struct pci_device_id whci_id_table[] = {
  214. { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
  215. { 0 },
  216. };
  217. MODULE_DEVICE_TABLE(pci, whci_id_table);
  218. static struct pci_driver whci_driver = {
  219. .name = "whci",
  220. .id_table = whci_id_table,
  221. .probe = whci_probe,
  222. .remove = whci_remove,
  223. };
  224. module_pci_driver(whci_driver);
  225. MODULE_DESCRIPTION("WHCI UWB Multi-interface Controller enumerator");
  226. MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
  227. MODULE_LICENSE("GPL");