irq-uniphier-aidet.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Driver for UniPhier AIDET (ARM Interrupt Detector)
  4. *
  5. * Copyright (C) 2017 Socionext Inc.
  6. * Author: Masahiro Yamada <yamada.masahiro@socionext.com>
  7. */
  8. #include <linux/bitops.h>
  9. #include <linux/init.h>
  10. #include <linux/irq.h>
  11. #include <linux/irqdomain.h>
  12. #include <linux/kernel.h>
  13. #include <linux/of.h>
  14. #include <linux/of_device.h>
  15. #include <linux/of_irq.h>
  16. #include <linux/platform_device.h>
  17. #include <linux/spinlock.h>
  18. #define UNIPHIER_AIDET_NR_IRQS 256
  19. #define UNIPHIER_AIDET_DETCONF 0x04 /* inverter register base */
  20. struct uniphier_aidet_priv {
  21. struct irq_domain *domain;
  22. void __iomem *reg_base;
  23. spinlock_t lock;
  24. u32 saved_vals[UNIPHIER_AIDET_NR_IRQS / 32];
  25. };
  26. static void uniphier_aidet_reg_update(struct uniphier_aidet_priv *priv,
  27. unsigned int reg, u32 mask, u32 val)
  28. {
  29. unsigned long flags;
  30. u32 tmp;
  31. spin_lock_irqsave(&priv->lock, flags);
  32. tmp = readl_relaxed(priv->reg_base + reg);
  33. tmp &= ~mask;
  34. tmp |= mask & val;
  35. writel_relaxed(tmp, priv->reg_base + reg);
  36. spin_unlock_irqrestore(&priv->lock, flags);
  37. }
  38. static void uniphier_aidet_detconf_update(struct uniphier_aidet_priv *priv,
  39. unsigned long index, unsigned int val)
  40. {
  41. unsigned int reg;
  42. u32 mask;
  43. reg = UNIPHIER_AIDET_DETCONF + index / 32 * 4;
  44. mask = BIT(index % 32);
  45. uniphier_aidet_reg_update(priv, reg, mask, val ? mask : 0);
  46. }
  47. static int uniphier_aidet_irq_set_type(struct irq_data *data, unsigned int type)
  48. {
  49. struct uniphier_aidet_priv *priv = data->chip_data;
  50. unsigned int val;
  51. /* enable inverter for active low triggers */
  52. switch (type) {
  53. case IRQ_TYPE_EDGE_RISING:
  54. case IRQ_TYPE_LEVEL_HIGH:
  55. val = 0;
  56. break;
  57. case IRQ_TYPE_EDGE_FALLING:
  58. val = 1;
  59. type = IRQ_TYPE_EDGE_RISING;
  60. break;
  61. case IRQ_TYPE_LEVEL_LOW:
  62. val = 1;
  63. type = IRQ_TYPE_LEVEL_HIGH;
  64. break;
  65. default:
  66. return -EINVAL;
  67. }
  68. uniphier_aidet_detconf_update(priv, data->hwirq, val);
  69. return irq_chip_set_type_parent(data, type);
  70. }
  71. static struct irq_chip uniphier_aidet_irq_chip = {
  72. .name = "AIDET",
  73. .irq_mask = irq_chip_mask_parent,
  74. .irq_unmask = irq_chip_unmask_parent,
  75. .irq_eoi = irq_chip_eoi_parent,
  76. .irq_set_affinity = irq_chip_set_affinity_parent,
  77. .irq_set_type = uniphier_aidet_irq_set_type,
  78. };
  79. static int uniphier_aidet_domain_translate(struct irq_domain *domain,
  80. struct irq_fwspec *fwspec,
  81. unsigned long *out_hwirq,
  82. unsigned int *out_type)
  83. {
  84. if (WARN_ON(fwspec->param_count < 2))
  85. return -EINVAL;
  86. *out_hwirq = fwspec->param[0];
  87. *out_type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK;
  88. return 0;
  89. }
  90. static int uniphier_aidet_domain_alloc(struct irq_domain *domain,
  91. unsigned int virq, unsigned int nr_irqs,
  92. void *arg)
  93. {
  94. struct irq_fwspec parent_fwspec;
  95. irq_hw_number_t hwirq;
  96. unsigned int type;
  97. int ret;
  98. if (nr_irqs != 1)
  99. return -EINVAL;
  100. ret = uniphier_aidet_domain_translate(domain, arg, &hwirq, &type);
  101. if (ret)
  102. return ret;
  103. switch (type) {
  104. case IRQ_TYPE_EDGE_RISING:
  105. case IRQ_TYPE_LEVEL_HIGH:
  106. break;
  107. case IRQ_TYPE_EDGE_FALLING:
  108. type = IRQ_TYPE_EDGE_RISING;
  109. break;
  110. case IRQ_TYPE_LEVEL_LOW:
  111. type = IRQ_TYPE_LEVEL_HIGH;
  112. break;
  113. default:
  114. return -EINVAL;
  115. }
  116. if (hwirq >= UNIPHIER_AIDET_NR_IRQS)
  117. return -ENXIO;
  118. ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
  119. &uniphier_aidet_irq_chip,
  120. domain->host_data);
  121. if (ret)
  122. return ret;
  123. /* parent is GIC */
  124. parent_fwspec.fwnode = domain->parent->fwnode;
  125. parent_fwspec.param_count = 3;
  126. parent_fwspec.param[0] = 0; /* SPI */
  127. parent_fwspec.param[1] = hwirq;
  128. parent_fwspec.param[2] = type;
  129. return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
  130. }
  131. static const struct irq_domain_ops uniphier_aidet_domain_ops = {
  132. .alloc = uniphier_aidet_domain_alloc,
  133. .free = irq_domain_free_irqs_common,
  134. .translate = uniphier_aidet_domain_translate,
  135. };
  136. static int uniphier_aidet_probe(struct platform_device *pdev)
  137. {
  138. struct device *dev = &pdev->dev;
  139. struct device_node *parent_np;
  140. struct irq_domain *parent_domain;
  141. struct uniphier_aidet_priv *priv;
  142. parent_np = of_irq_find_parent(dev->of_node);
  143. if (!parent_np)
  144. return -ENXIO;
  145. parent_domain = irq_find_host(parent_np);
  146. of_node_put(parent_np);
  147. if (!parent_domain)
  148. return -EPROBE_DEFER;
  149. priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
  150. if (!priv)
  151. return -ENOMEM;
  152. priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
  153. if (IS_ERR(priv->reg_base))
  154. return PTR_ERR(priv->reg_base);
  155. spin_lock_init(&priv->lock);
  156. priv->domain = irq_domain_create_hierarchy(
  157. parent_domain, 0,
  158. UNIPHIER_AIDET_NR_IRQS,
  159. of_node_to_fwnode(dev->of_node),
  160. &uniphier_aidet_domain_ops, priv);
  161. if (!priv->domain)
  162. return -ENOMEM;
  163. platform_set_drvdata(pdev, priv);
  164. return 0;
  165. }
  166. static int __maybe_unused uniphier_aidet_suspend(struct device *dev)
  167. {
  168. struct uniphier_aidet_priv *priv = dev_get_drvdata(dev);
  169. int i;
  170. for (i = 0; i < ARRAY_SIZE(priv->saved_vals); i++)
  171. priv->saved_vals[i] = readl_relaxed(
  172. priv->reg_base + UNIPHIER_AIDET_DETCONF + i * 4);
  173. return 0;
  174. }
  175. static int __maybe_unused uniphier_aidet_resume(struct device *dev)
  176. {
  177. struct uniphier_aidet_priv *priv = dev_get_drvdata(dev);
  178. int i;
  179. for (i = 0; i < ARRAY_SIZE(priv->saved_vals); i++)
  180. writel_relaxed(priv->saved_vals[i],
  181. priv->reg_base + UNIPHIER_AIDET_DETCONF + i * 4);
  182. return 0;
  183. }
  184. static const struct dev_pm_ops uniphier_aidet_pm_ops = {
  185. SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(uniphier_aidet_suspend,
  186. uniphier_aidet_resume)
  187. };
  188. static const struct of_device_id uniphier_aidet_match[] = {
  189. { .compatible = "socionext,uniphier-ld4-aidet" },
  190. { .compatible = "socionext,uniphier-pro4-aidet" },
  191. { .compatible = "socionext,uniphier-sld8-aidet" },
  192. { .compatible = "socionext,uniphier-pro5-aidet" },
  193. { .compatible = "socionext,uniphier-pxs2-aidet" },
  194. { .compatible = "socionext,uniphier-ld11-aidet" },
  195. { .compatible = "socionext,uniphier-ld20-aidet" },
  196. { .compatible = "socionext,uniphier-pxs3-aidet" },
  197. { /* sentinel */ }
  198. };
  199. static struct platform_driver uniphier_aidet_driver = {
  200. .probe = uniphier_aidet_probe,
  201. .driver = {
  202. .name = "uniphier-aidet",
  203. .of_match_table = uniphier_aidet_match,
  204. .pm = &uniphier_aidet_pm_ops,
  205. },
  206. };
  207. builtin_platform_driver(uniphier_aidet_driver);