mpc5121_ads_cpld.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /*
  2. * Copyright (C) 2008 Freescale Semiconductor, Inc. All rights reserved.
  3. *
  4. * Author: John Rigby, <jrigby@freescale.com>
  5. *
  6. * Description:
  7. * MPC5121ADS CPLD irq handling
  8. *
  9. * This is free software; you can redistribute it and/or modify it
  10. * under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. */
  14. #undef DEBUG
  15. #include <linux/kernel.h>
  16. #include <linux/interrupt.h>
  17. #include <linux/irq.h>
  18. #include <linux/io.h>
  19. #include <asm/prom.h>
  20. static struct device_node *cpld_pic_node;
  21. static struct irq_domain *cpld_pic_host;
  22. /*
  23. * Bits to ignore in the misc_status register
  24. * 0x10 touch screen pendown is hard routed to irq1
  25. * 0x02 pci status is read from pci status register
  26. */
  27. #define MISC_IGNORE 0x12
  28. /*
  29. * Nothing to ignore in pci status register
  30. */
  31. #define PCI_IGNORE 0x00
  32. struct cpld_pic {
  33. u8 pci_mask;
  34. u8 pci_status;
  35. u8 route;
  36. u8 misc_mask;
  37. u8 misc_status;
  38. u8 misc_control;
  39. };
  40. static struct cpld_pic __iomem *cpld_regs;
  41. static void __iomem *
  42. irq_to_pic_mask(unsigned int irq)
  43. {
  44. return irq <= 7 ? &cpld_regs->pci_mask : &cpld_regs->misc_mask;
  45. }
  46. static unsigned int
  47. irq_to_pic_bit(unsigned int irq)
  48. {
  49. return 1 << (irq & 0x7);
  50. }
  51. static void
  52. cpld_mask_irq(struct irq_data *d)
  53. {
  54. unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
  55. void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
  56. out_8(pic_mask,
  57. in_8(pic_mask) | irq_to_pic_bit(cpld_irq));
  58. }
  59. static void
  60. cpld_unmask_irq(struct irq_data *d)
  61. {
  62. unsigned int cpld_irq = (unsigned int)irqd_to_hwirq(d);
  63. void __iomem *pic_mask = irq_to_pic_mask(cpld_irq);
  64. out_8(pic_mask,
  65. in_8(pic_mask) & ~irq_to_pic_bit(cpld_irq));
  66. }
  67. static struct irq_chip cpld_pic = {
  68. .name = "CPLD PIC",
  69. .irq_mask = cpld_mask_irq,
  70. .irq_ack = cpld_mask_irq,
  71. .irq_unmask = cpld_unmask_irq,
  72. };
  73. static int
  74. cpld_pic_get_irq(int offset, u8 ignore, u8 __iomem *statusp,
  75. u8 __iomem *maskp)
  76. {
  77. int cpld_irq;
  78. u8 status = in_8(statusp);
  79. u8 mask = in_8(maskp);
  80. /* ignore don't cares and masked irqs */
  81. status |= (ignore | mask);
  82. if (status == 0xff)
  83. return NO_IRQ;
  84. cpld_irq = ffz(status) + offset;
  85. return irq_linear_revmap(cpld_pic_host, cpld_irq);
  86. }
  87. static void cpld_pic_cascade(struct irq_desc *desc)
  88. {
  89. unsigned int irq;
  90. irq = cpld_pic_get_irq(0, PCI_IGNORE, &cpld_regs->pci_status,
  91. &cpld_regs->pci_mask);
  92. if (irq != NO_IRQ) {
  93. generic_handle_irq(irq);
  94. return;
  95. }
  96. irq = cpld_pic_get_irq(8, MISC_IGNORE, &cpld_regs->misc_status,
  97. &cpld_regs->misc_mask);
  98. if (irq != NO_IRQ) {
  99. generic_handle_irq(irq);
  100. return;
  101. }
  102. }
  103. static int
  104. cpld_pic_host_match(struct irq_domain *h, struct device_node *node,
  105. enum irq_domain_bus_token bus_token)
  106. {
  107. return cpld_pic_node == node;
  108. }
  109. static int
  110. cpld_pic_host_map(struct irq_domain *h, unsigned int virq,
  111. irq_hw_number_t hw)
  112. {
  113. irq_set_status_flags(virq, IRQ_LEVEL);
  114. irq_set_chip_and_handler(virq, &cpld_pic, handle_level_irq);
  115. return 0;
  116. }
  117. static const struct irq_domain_ops cpld_pic_host_ops = {
  118. .match = cpld_pic_host_match,
  119. .map = cpld_pic_host_map,
  120. };
  121. void __init
  122. mpc5121_ads_cpld_map(void)
  123. {
  124. struct device_node *np = NULL;
  125. np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
  126. if (!np) {
  127. printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
  128. return;
  129. }
  130. cpld_regs = of_iomap(np, 0);
  131. of_node_put(np);
  132. }
  133. void __init
  134. mpc5121_ads_cpld_pic_init(void)
  135. {
  136. unsigned int cascade_irq;
  137. struct device_node *np = NULL;
  138. pr_debug("cpld_ic_init\n");
  139. np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld-pic");
  140. if (!np) {
  141. printk(KERN_ERR "CPLD PIC init: can not find cpld-pic node\n");
  142. return;
  143. }
  144. if (!cpld_regs)
  145. goto end;
  146. cascade_irq = irq_of_parse_and_map(np, 0);
  147. if (cascade_irq == NO_IRQ)
  148. goto end;
  149. /*
  150. * statically route touch screen pendown through 1
  151. * and ignore it here
  152. * route all others through our cascade irq
  153. */
  154. out_8(&cpld_regs->route, 0xfd);
  155. out_8(&cpld_regs->pci_mask, 0xff);
  156. /* unmask pci ints in misc mask */
  157. out_8(&cpld_regs->misc_mask, ~(MISC_IGNORE));
  158. cpld_pic_node = of_node_get(np);
  159. cpld_pic_host = irq_domain_add_linear(np, 16, &cpld_pic_host_ops, NULL);
  160. if (!cpld_pic_host) {
  161. printk(KERN_ERR "CPLD PIC: failed to allocate irq host!\n");
  162. goto end;
  163. }
  164. irq_set_chained_handler(cascade_irq, cpld_pic_cascade);
  165. end:
  166. of_node_put(np);
  167. }