uio_mf624.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * UIO driver fo Humusoft MF624 DAQ card.
  4. * Copyright (C) 2011 Rostislav Lisovy <[email protected]>,
  5. * Czech Technical University in Prague
  6. */
  7. #include <linux/init.h>
  8. #include <linux/module.h>
  9. #include <linux/device.h>
  10. #include <linux/pci.h>
  11. #include <linux/slab.h>
  12. #include <linux/io.h>
  13. #include <linux/kernel.h>
  14. #include <linux/uio_driver.h>
  15. #define PCI_VENDOR_ID_HUMUSOFT 0x186c
  16. #define PCI_DEVICE_ID_MF624 0x0624
  17. #define PCI_SUBVENDOR_ID_HUMUSOFT 0x186c
  18. #define PCI_SUBDEVICE_DEVICE 0x0624
  19. /* BAR0 Interrupt control/status register */
  20. #define INTCSR 0x4C
  21. #define INTCSR_ADINT_ENABLE (1 << 0)
  22. #define INTCSR_CTR4INT_ENABLE (1 << 3)
  23. #define INTCSR_PCIINT_ENABLE (1 << 6)
  24. #define INTCSR_ADINT_STATUS (1 << 2)
  25. #define INTCSR_CTR4INT_STATUS (1 << 5)
  26. enum mf624_interrupt_source {ADC, CTR4, ALL};
  27. static void mf624_disable_interrupt(enum mf624_interrupt_source source,
  28. struct uio_info *info)
  29. {
  30. void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  31. switch (source) {
  32. case ADC:
  33. iowrite32(ioread32(INTCSR_reg)
  34. & ~(INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE),
  35. INTCSR_reg);
  36. break;
  37. case CTR4:
  38. iowrite32(ioread32(INTCSR_reg)
  39. & ~(INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE),
  40. INTCSR_reg);
  41. break;
  42. case ALL:
  43. default:
  44. iowrite32(ioread32(INTCSR_reg)
  45. & ~(INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
  46. | INTCSR_PCIINT_ENABLE),
  47. INTCSR_reg);
  48. break;
  49. }
  50. }
  51. static void mf624_enable_interrupt(enum mf624_interrupt_source source,
  52. struct uio_info *info)
  53. {
  54. void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  55. switch (source) {
  56. case ADC:
  57. iowrite32(ioread32(INTCSR_reg)
  58. | INTCSR_ADINT_ENABLE | INTCSR_PCIINT_ENABLE,
  59. INTCSR_reg);
  60. break;
  61. case CTR4:
  62. iowrite32(ioread32(INTCSR_reg)
  63. | INTCSR_CTR4INT_ENABLE | INTCSR_PCIINT_ENABLE,
  64. INTCSR_reg);
  65. break;
  66. case ALL:
  67. default:
  68. iowrite32(ioread32(INTCSR_reg)
  69. | INTCSR_ADINT_ENABLE | INTCSR_CTR4INT_ENABLE
  70. | INTCSR_PCIINT_ENABLE,
  71. INTCSR_reg);
  72. break;
  73. }
  74. }
  75. static irqreturn_t mf624_irq_handler(int irq, struct uio_info *info)
  76. {
  77. void __iomem *INTCSR_reg = info->mem[0].internal_addr + INTCSR;
  78. if ((ioread32(INTCSR_reg) & INTCSR_ADINT_ENABLE)
  79. && (ioread32(INTCSR_reg) & INTCSR_ADINT_STATUS)) {
  80. mf624_disable_interrupt(ADC, info);
  81. return IRQ_HANDLED;
  82. }
  83. if ((ioread32(INTCSR_reg) & INTCSR_CTR4INT_ENABLE)
  84. && (ioread32(INTCSR_reg) & INTCSR_CTR4INT_STATUS)) {
  85. mf624_disable_interrupt(CTR4, info);
  86. return IRQ_HANDLED;
  87. }
  88. return IRQ_NONE;
  89. }
  90. static int mf624_irqcontrol(struct uio_info *info, s32 irq_on)
  91. {
  92. if (irq_on == 0)
  93. mf624_disable_interrupt(ALL, info);
  94. else if (irq_on == 1)
  95. mf624_enable_interrupt(ALL, info);
  96. return 0;
  97. }
  98. static int mf624_setup_mem(struct pci_dev *dev, int bar, struct uio_mem *mem, const char *name)
  99. {
  100. resource_size_t start = pci_resource_start(dev, bar);
  101. resource_size_t len = pci_resource_len(dev, bar);
  102. mem->name = name;
  103. mem->addr = start & PAGE_MASK;
  104. mem->offs = start & ~PAGE_MASK;
  105. if (!mem->addr)
  106. return -ENODEV;
  107. mem->size = ((start & ~PAGE_MASK) + len + PAGE_SIZE - 1) & PAGE_MASK;
  108. mem->memtype = UIO_MEM_PHYS;
  109. mem->internal_addr = pci_ioremap_bar(dev, bar);
  110. if (!mem->internal_addr)
  111. return -ENODEV;
  112. return 0;
  113. }
  114. static int mf624_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
  115. {
  116. struct uio_info *info;
  117. info = devm_kzalloc(&dev->dev, sizeof(struct uio_info), GFP_KERNEL);
  118. if (!info)
  119. return -ENOMEM;
  120. if (pci_enable_device(dev))
  121. return -ENODEV;
  122. if (pci_request_regions(dev, "mf624"))
  123. goto out_disable;
  124. info->name = "mf624";
  125. info->version = "0.0.1";
  126. /* Note: Datasheet says device uses BAR0, BAR1, BAR2 -- do not trust it */
  127. /* BAR0 */
  128. if (mf624_setup_mem(dev, 0, &info->mem[0], "PCI chipset, interrupts, status "
  129. "bits, special functions"))
  130. goto out_release;
  131. /* BAR2 */
  132. if (mf624_setup_mem(dev, 2, &info->mem[1], "ADC, DAC, DIO"))
  133. goto out_unmap0;
  134. /* BAR4 */
  135. if (mf624_setup_mem(dev, 4, &info->mem[2], "Counter/timer chip"))
  136. goto out_unmap1;
  137. info->irq = dev->irq;
  138. info->irq_flags = IRQF_SHARED;
  139. info->handler = mf624_irq_handler;
  140. info->irqcontrol = mf624_irqcontrol;
  141. if (uio_register_device(&dev->dev, info))
  142. goto out_unmap2;
  143. pci_set_drvdata(dev, info);
  144. return 0;
  145. out_unmap2:
  146. iounmap(info->mem[2].internal_addr);
  147. out_unmap1:
  148. iounmap(info->mem[1].internal_addr);
  149. out_unmap0:
  150. iounmap(info->mem[0].internal_addr);
  151. out_release:
  152. pci_release_regions(dev);
  153. out_disable:
  154. pci_disable_device(dev);
  155. return -ENODEV;
  156. }
  157. static void mf624_pci_remove(struct pci_dev *dev)
  158. {
  159. struct uio_info *info = pci_get_drvdata(dev);
  160. mf624_disable_interrupt(ALL, info);
  161. uio_unregister_device(info);
  162. pci_release_regions(dev);
  163. pci_disable_device(dev);
  164. iounmap(info->mem[0].internal_addr);
  165. iounmap(info->mem[1].internal_addr);
  166. iounmap(info->mem[2].internal_addr);
  167. }
  168. static const struct pci_device_id mf624_pci_id[] = {
  169. { PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
  170. { 0, }
  171. };
  172. static struct pci_driver mf624_pci_driver = {
  173. .name = "mf624",
  174. .id_table = mf624_pci_id,
  175. .probe = mf624_pci_probe,
  176. .remove = mf624_pci_remove,
  177. };
  178. MODULE_DEVICE_TABLE(pci, mf624_pci_id);
  179. module_pci_driver(mf624_pci_driver);
  180. MODULE_LICENSE("GPL v2");
  181. MODULE_AUTHOR("Rostislav Lisovy <[email protected]>");