0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/clk.h>
0010 #include <linux/delay.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/irqchip.h>
0013 #include <linux/of_address.h>
0014 #include <linux/of_irq.h>
0015 #include <linux/syscore_ops.h>
0016
0017 #include <dt-bindings/interrupt-controller/arm-gic.h>
0018
0019 #define MCHP_EIC_GFCS (0x0)
0020 #define MCHP_EIC_SCFG(x) (0x4 + (x) * 0x4)
0021 #define MCHP_EIC_SCFG_EN BIT(16)
0022 #define MCHP_EIC_SCFG_LVL BIT(9)
0023 #define MCHP_EIC_SCFG_POL BIT(8)
0024
0025 #define MCHP_EIC_NIRQ (2)
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036 struct mchp_eic {
0037 void __iomem *base;
0038 struct clk *clk;
0039 struct irq_domain *domain;
0040 u32 irqs[MCHP_EIC_NIRQ];
0041 u32 scfg[MCHP_EIC_NIRQ];
0042 u32 wakeup_source;
0043 };
0044
0045 static struct mchp_eic *eic;
0046
0047 static void mchp_eic_irq_mask(struct irq_data *d)
0048 {
0049 unsigned int tmp;
0050
0051 tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
0052 tmp &= ~MCHP_EIC_SCFG_EN;
0053 writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
0054
0055 irq_chip_mask_parent(d);
0056 }
0057
0058 static void mchp_eic_irq_unmask(struct irq_data *d)
0059 {
0060 unsigned int tmp;
0061
0062 tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
0063 tmp |= MCHP_EIC_SCFG_EN;
0064 writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
0065
0066 irq_chip_unmask_parent(d);
0067 }
0068
0069 static int mchp_eic_irq_set_type(struct irq_data *d, unsigned int type)
0070 {
0071 unsigned int parent_irq_type;
0072 unsigned int tmp;
0073
0074 tmp = readl_relaxed(eic->base + MCHP_EIC_SCFG(d->hwirq));
0075 tmp &= ~(MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL);
0076 switch (type) {
0077 case IRQ_TYPE_LEVEL_HIGH:
0078 tmp |= MCHP_EIC_SCFG_POL | MCHP_EIC_SCFG_LVL;
0079 parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
0080 break;
0081 case IRQ_TYPE_LEVEL_LOW:
0082 tmp |= MCHP_EIC_SCFG_LVL;
0083 parent_irq_type = IRQ_TYPE_LEVEL_HIGH;
0084 break;
0085 case IRQ_TYPE_EDGE_RISING:
0086 parent_irq_type = IRQ_TYPE_EDGE_RISING;
0087 break;
0088 case IRQ_TYPE_EDGE_FALLING:
0089 tmp |= MCHP_EIC_SCFG_POL;
0090 parent_irq_type = IRQ_TYPE_EDGE_RISING;
0091 break;
0092 default:
0093 return -EINVAL;
0094 }
0095
0096 writel_relaxed(tmp, eic->base + MCHP_EIC_SCFG(d->hwirq));
0097
0098 return irq_chip_set_type_parent(d, parent_irq_type);
0099 }
0100
0101 static int mchp_eic_irq_set_wake(struct irq_data *d, unsigned int on)
0102 {
0103 irq_set_irq_wake(eic->irqs[d->hwirq], on);
0104 if (on)
0105 eic->wakeup_source |= BIT(d->hwirq);
0106 else
0107 eic->wakeup_source &= ~BIT(d->hwirq);
0108
0109 return 0;
0110 }
0111
0112 static int mchp_eic_irq_suspend(void)
0113 {
0114 unsigned int hwirq;
0115
0116 for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
0117 eic->scfg[hwirq] = readl_relaxed(eic->base +
0118 MCHP_EIC_SCFG(hwirq));
0119
0120 if (!eic->wakeup_source)
0121 clk_disable_unprepare(eic->clk);
0122
0123 return 0;
0124 }
0125
0126 static void mchp_eic_irq_resume(void)
0127 {
0128 unsigned int hwirq;
0129
0130 if (!eic->wakeup_source)
0131 clk_prepare_enable(eic->clk);
0132
0133 for (hwirq = 0; hwirq < MCHP_EIC_NIRQ; hwirq++)
0134 writel_relaxed(eic->scfg[hwirq], eic->base +
0135 MCHP_EIC_SCFG(hwirq));
0136 }
0137
0138 static struct syscore_ops mchp_eic_syscore_ops = {
0139 .suspend = mchp_eic_irq_suspend,
0140 .resume = mchp_eic_irq_resume,
0141 };
0142
0143 static struct irq_chip mchp_eic_chip = {
0144 .name = "eic",
0145 .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED,
0146 .irq_mask = mchp_eic_irq_mask,
0147 .irq_unmask = mchp_eic_irq_unmask,
0148 .irq_set_type = mchp_eic_irq_set_type,
0149 .irq_ack = irq_chip_ack_parent,
0150 .irq_eoi = irq_chip_eoi_parent,
0151 .irq_retrigger = irq_chip_retrigger_hierarchy,
0152 .irq_set_wake = mchp_eic_irq_set_wake,
0153 };
0154
0155 static int mchp_eic_domain_alloc(struct irq_domain *domain, unsigned int virq,
0156 unsigned int nr_irqs, void *data)
0157 {
0158 struct irq_fwspec *fwspec = data;
0159 struct irq_fwspec parent_fwspec;
0160 irq_hw_number_t hwirq;
0161 unsigned int type;
0162 int ret;
0163
0164 if (WARN_ON(nr_irqs != 1))
0165 return -EINVAL;
0166
0167 ret = irq_domain_translate_twocell(domain, fwspec, &hwirq, &type);
0168 if (ret || hwirq >= MCHP_EIC_NIRQ)
0169 return ret;
0170
0171 switch (type) {
0172 case IRQ_TYPE_EDGE_RISING:
0173 case IRQ_TYPE_LEVEL_HIGH:
0174 break;
0175 case IRQ_TYPE_EDGE_FALLING:
0176 type = IRQ_TYPE_EDGE_RISING;
0177 break;
0178 case IRQ_TYPE_LEVEL_LOW:
0179 type = IRQ_TYPE_LEVEL_HIGH;
0180 break;
0181 default:
0182 return -EINVAL;
0183 }
0184
0185 irq_domain_set_hwirq_and_chip(domain, virq, hwirq, &mchp_eic_chip, eic);
0186
0187 parent_fwspec.fwnode = domain->parent->fwnode;
0188 parent_fwspec.param_count = 3;
0189 parent_fwspec.param[0] = GIC_SPI;
0190 parent_fwspec.param[1] = eic->irqs[hwirq];
0191 parent_fwspec.param[2] = type;
0192
0193 return irq_domain_alloc_irqs_parent(domain, virq, 1, &parent_fwspec);
0194 }
0195
0196 static const struct irq_domain_ops mchp_eic_domain_ops = {
0197 .translate = irq_domain_translate_twocell,
0198 .alloc = mchp_eic_domain_alloc,
0199 .free = irq_domain_free_irqs_common,
0200 };
0201
0202 static int mchp_eic_init(struct device_node *node, struct device_node *parent)
0203 {
0204 struct irq_domain *parent_domain = NULL;
0205 int ret, i;
0206
0207 eic = kzalloc(sizeof(*eic), GFP_KERNEL);
0208 if (!eic)
0209 return -ENOMEM;
0210
0211 eic->base = of_iomap(node, 0);
0212 if (!eic->base) {
0213 ret = -ENOMEM;
0214 goto free;
0215 }
0216
0217 parent_domain = irq_find_host(parent);
0218 if (!parent_domain) {
0219 ret = -ENODEV;
0220 goto unmap;
0221 }
0222
0223 eic->clk = of_clk_get_by_name(node, "pclk");
0224 if (IS_ERR(eic->clk)) {
0225 ret = PTR_ERR(eic->clk);
0226 goto unmap;
0227 }
0228
0229 ret = clk_prepare_enable(eic->clk);
0230 if (ret)
0231 goto unmap;
0232
0233 for (i = 0; i < MCHP_EIC_NIRQ; i++) {
0234 struct of_phandle_args irq;
0235
0236
0237 writel_relaxed(0UL, eic->base + MCHP_EIC_SCFG(i));
0238
0239 ret = of_irq_parse_one(node, i, &irq);
0240 if (ret)
0241 goto clk_unprepare;
0242
0243 if (WARN_ON(irq.args_count != 3)) {
0244 ret = -EINVAL;
0245 goto clk_unprepare;
0246 }
0247
0248 eic->irqs[i] = irq.args[1];
0249 }
0250
0251 eic->domain = irq_domain_add_hierarchy(parent_domain, 0, MCHP_EIC_NIRQ,
0252 node, &mchp_eic_domain_ops, eic);
0253 if (!eic->domain) {
0254 pr_err("%pOF: Failed to add domain\n", node);
0255 ret = -ENODEV;
0256 goto clk_unprepare;
0257 }
0258
0259 register_syscore_ops(&mchp_eic_syscore_ops);
0260
0261 pr_info("%pOF: EIC registered, nr_irqs %u\n", node, MCHP_EIC_NIRQ);
0262
0263 return 0;
0264
0265 clk_unprepare:
0266 clk_disable_unprepare(eic->clk);
0267 unmap:
0268 iounmap(eic->base);
0269 free:
0270 kfree(eic);
0271 return ret;
0272 }
0273
0274 IRQCHIP_PLATFORM_DRIVER_BEGIN(mchp_eic)
0275 IRQCHIP_MATCH("microchip,sama7g5-eic", mchp_eic_init)
0276 IRQCHIP_PLATFORM_DRIVER_END(mchp_eic)
0277
0278 MODULE_DESCRIPTION("Microchip External Interrupt Controller");
0279 MODULE_LICENSE("GPL v2");
0280 MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea@microchip.com>");