irq-uniphier-aidet.c 6.6 KB

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