uio_mf624.c 5.9 KB

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