/arch/powerpc/platforms/512x/mpc5121_ads_cpld.c

http://github.com/mirrors/linux · C · 200 lines · 139 code · 37 blank · 24 comment · 10 complexity · ced87a2f88d045e4ab84e9d339572727 MD5 · raw file

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