141766119SJoseph Chen /*
241766119SJoseph Chen * (C) Copyright 2019 Rockchip Electronics Co., Ltd
341766119SJoseph Chen *
441766119SJoseph Chen * SPDX-License-Identifier: GPL-2.0+
541766119SJoseph Chen */
641766119SJoseph Chen
741766119SJoseph Chen #include <common.h>
841766119SJoseph Chen #include <dm.h>
941766119SJoseph Chen #include <fdtdec.h>
1041766119SJoseph Chen #include <malloc.h>
1141766119SJoseph Chen #include <asm/io.h>
1241766119SJoseph Chen #include <asm/u-boot-arm.h>
1341766119SJoseph Chen #include <irq-generic.h>
1441766119SJoseph Chen #include "irq-internal.h"
1541766119SJoseph Chen
1641766119SJoseph Chen DECLARE_GLOBAL_DATA_PTR;
1741766119SJoseph Chen
1841766119SJoseph Chen static LIST_HEAD(virq_desc_head);
1941766119SJoseph Chen static u32 virq_id = PLATFORM_MAX_IRQ;
2041766119SJoseph Chen
virq_id_alloc(void)2141766119SJoseph Chen static u32 virq_id_alloc(void)
2241766119SJoseph Chen {
2341766119SJoseph Chen return ++virq_id;
2441766119SJoseph Chen }
2541766119SJoseph Chen
2641766119SJoseph Chen struct virq_data {
2741766119SJoseph Chen int irq;
28ae63f119SJoseph Chen u32 flag;
29ae63f119SJoseph Chen u32 count;
30ae63f119SJoseph Chen
3141766119SJoseph Chen void *data;
3241766119SJoseph Chen interrupt_handler_t *handle_irq;
3341766119SJoseph Chen };
3441766119SJoseph Chen
3541766119SJoseph Chen /* The structure to maintail the irqchip and child virqs */
3641766119SJoseph Chen struct virq_desc {
3741766119SJoseph Chen struct virq_chip *chip; /* irq chip */
3841766119SJoseph Chen struct virq_data *virqs; /* child irq data list */
3941766119SJoseph Chen struct udevice *parent; /* parent device */
4041766119SJoseph Chen int pirq; /* parent irq */
4125c13168SJoseph Chen int use_count; /* enable count */
4241766119SJoseph Chen int irq_base; /* child irq base */
4341766119SJoseph Chen int irq_end; /* child irq end */
4441766119SJoseph Chen uint reg_stride;
4541766119SJoseph Chen uint unalign_reg_idx;
4641766119SJoseph Chen uint unalign_reg_stride;
4741766119SJoseph Chen uint *status_buf;
4841766119SJoseph Chen
4941766119SJoseph Chen struct list_head node;
5041766119SJoseph Chen };
5141766119SJoseph Chen
find_virq_desc(int irq)5241766119SJoseph Chen static struct virq_desc *find_virq_desc(int irq)
5341766119SJoseph Chen {
5441766119SJoseph Chen struct virq_desc *desc;
5541766119SJoseph Chen struct list_head *node;
5641766119SJoseph Chen
5741766119SJoseph Chen list_for_each(node, &virq_desc_head) {
5841766119SJoseph Chen desc = list_entry(node, struct virq_desc, node);
5941766119SJoseph Chen if (irq >= desc->irq_base && irq <= desc->irq_end)
6041766119SJoseph Chen return desc;
6141766119SJoseph Chen }
6241766119SJoseph Chen
6341766119SJoseph Chen return NULL;
6441766119SJoseph Chen }
6541766119SJoseph Chen
find_virq_desc_by_pirq(int parent_irq)6641766119SJoseph Chen static struct virq_desc *find_virq_desc_by_pirq(int parent_irq)
6741766119SJoseph Chen {
6841766119SJoseph Chen struct virq_desc *desc;
6941766119SJoseph Chen struct list_head *node;
7041766119SJoseph Chen
7141766119SJoseph Chen list_for_each(node, &virq_desc_head) {
7241766119SJoseph Chen desc = list_entry(node, struct virq_desc, node);
7341766119SJoseph Chen if (parent_irq == desc->pirq)
7441766119SJoseph Chen return desc;
7541766119SJoseph Chen }
7641766119SJoseph Chen
7741766119SJoseph Chen return NULL;
7841766119SJoseph Chen }
7941766119SJoseph Chen
virq_to_irq(struct virq_chip * chip,int virq)8041766119SJoseph Chen int virq_to_irq(struct virq_chip *chip, int virq)
8141766119SJoseph Chen {
8241766119SJoseph Chen struct virq_desc *desc;
8341766119SJoseph Chen struct list_head *node;
8441766119SJoseph Chen int irq;
8541766119SJoseph Chen
8641766119SJoseph Chen if (!chip)
8741766119SJoseph Chen return -EINVAL;
8841766119SJoseph Chen
8941766119SJoseph Chen list_for_each(node, &virq_desc_head) {
9041766119SJoseph Chen desc = list_entry(node, struct virq_desc, node);
9141766119SJoseph Chen if (desc->chip == chip) {
9241766119SJoseph Chen irq = desc->irq_base + virq;
9341766119SJoseph Chen if (irq >= desc->irq_base && irq <= desc->irq_end)
9441766119SJoseph Chen return irq;
9541766119SJoseph Chen }
9641766119SJoseph Chen }
9741766119SJoseph Chen
9841766119SJoseph Chen return -ENONET;
9941766119SJoseph Chen }
10041766119SJoseph Chen
bad_virq(int irq)10141766119SJoseph Chen int bad_virq(int irq)
10241766119SJoseph Chen {
10341766119SJoseph Chen return !find_virq_desc(irq);
10441766119SJoseph Chen }
10541766119SJoseph Chen
virqs_show(int pirq)10692f4f090SJoseph Chen void virqs_show(int pirq)
10792f4f090SJoseph Chen {
10892f4f090SJoseph Chen struct virq_data *vdata;
10992f4f090SJoseph Chen struct virq_desc *desc;
11092f4f090SJoseph Chen struct udevice *dev;
11192f4f090SJoseph Chen int num;
11292f4f090SJoseph Chen int i;
11392f4f090SJoseph Chen
11492f4f090SJoseph Chen desc = find_virq_desc_by_pirq(pirq);
11592f4f090SJoseph Chen if (!desc)
11692f4f090SJoseph Chen return;
11792f4f090SJoseph Chen
11892f4f090SJoseph Chen vdata = desc->virqs;
11992f4f090SJoseph Chen num = desc->irq_end - desc->irq_base;
12092f4f090SJoseph Chen
12192f4f090SJoseph Chen for (i = 0; i < num; i++) {
12292f4f090SJoseph Chen if (!vdata[i].handle_irq)
12392f4f090SJoseph Chen continue;
12492f4f090SJoseph Chen
12592f4f090SJoseph Chen dev = (struct udevice *)vdata[i].data;
12692f4f090SJoseph Chen printf(" %3d %d 0x%08lx %-12s |-- %-12s %d\n",
12792f4f090SJoseph Chen vdata[i].irq,
12892f4f090SJoseph Chen vdata[i].flag & IRQ_FLG_ENABLE ? 1 : 0,
12992f4f090SJoseph Chen (ulong)vdata[i].handle_irq, dev->driver->name, dev->name,
13092f4f090SJoseph Chen vdata[i].count);
13192f4f090SJoseph Chen }
13292f4f090SJoseph Chen }
13392f4f090SJoseph Chen
virq_install_handler(int irq,interrupt_handler_t * handler,void * data)13441766119SJoseph Chen int virq_install_handler(int irq, interrupt_handler_t *handler, void *data)
13541766119SJoseph Chen {
13641766119SJoseph Chen struct virq_desc *desc;
13741766119SJoseph Chen int virq;
13841766119SJoseph Chen
13941766119SJoseph Chen if (!handler)
14041766119SJoseph Chen return -EINVAL;
14141766119SJoseph Chen
14241766119SJoseph Chen desc = find_virq_desc(irq);
14341766119SJoseph Chen if (!desc)
14441766119SJoseph Chen return -ENOENT;
14541766119SJoseph Chen
14641766119SJoseph Chen virq = irq - desc->irq_base;
14741766119SJoseph Chen if (desc->virqs[virq].handle_irq)
14841766119SJoseph Chen return -EBUSY;
14941766119SJoseph Chen
15041766119SJoseph Chen desc->virqs[virq].handle_irq = handler;
15141766119SJoseph Chen desc->virqs[virq].data = data;
15241766119SJoseph Chen
15341766119SJoseph Chen return 0;
15441766119SJoseph Chen }
15541766119SJoseph Chen
virq_free_handler(int irq)15641766119SJoseph Chen void virq_free_handler(int irq)
15741766119SJoseph Chen {
15841766119SJoseph Chen struct virq_desc *desc;
15941766119SJoseph Chen int virq;
16041766119SJoseph Chen
16141766119SJoseph Chen desc = find_virq_desc(irq);
16241766119SJoseph Chen if (!desc)
16341766119SJoseph Chen return;
16441766119SJoseph Chen
16541766119SJoseph Chen virq = irq - desc->irq_base;
16641766119SJoseph Chen desc->virqs[virq].handle_irq = NULL;
16741766119SJoseph Chen desc->virqs[virq].data = NULL;
16841766119SJoseph Chen }
16941766119SJoseph Chen
reg_base_get(struct virq_desc * desc,uint reg_base,int idx)17041766119SJoseph Chen static uint reg_base_get(struct virq_desc *desc, uint reg_base, int idx)
17141766119SJoseph Chen {
17241766119SJoseph Chen int reg_addr;
17341766119SJoseph Chen
17441766119SJoseph Chen if (idx <= desc->unalign_reg_idx) {
17541766119SJoseph Chen reg_addr = reg_base + (idx * desc->unalign_reg_stride);
17641766119SJoseph Chen } else {
17741766119SJoseph Chen reg_addr = reg_base +
17841766119SJoseph Chen (desc->unalign_reg_idx * desc->unalign_reg_stride);
17941766119SJoseph Chen reg_addr += (idx - desc->unalign_reg_idx) * desc->reg_stride;
18041766119SJoseph Chen }
18141766119SJoseph Chen
18241766119SJoseph Chen return reg_addr;
18341766119SJoseph Chen }
18441766119SJoseph Chen
virq_chip_generic_handler(int pirq,void * pdata)18541766119SJoseph Chen void virq_chip_generic_handler(int pirq, void *pdata)
18641766119SJoseph Chen {
18741766119SJoseph Chen struct virq_chip *chip;
18841766119SJoseph Chen struct virq_desc *desc;
18941766119SJoseph Chen struct virq_data *vdata;
19041766119SJoseph Chen struct udevice *parent;
19141766119SJoseph Chen uint status_reg;
19241766119SJoseph Chen void *data;
19341766119SJoseph Chen int irq;
19441766119SJoseph Chen int ret;
19541766119SJoseph Chen int i;
19641766119SJoseph Chen
19741766119SJoseph Chen desc = find_virq_desc_by_pirq(pirq);
19841766119SJoseph Chen if (!desc)
19941766119SJoseph Chen return;
20041766119SJoseph Chen
20141766119SJoseph Chen chip = desc->chip;
20241766119SJoseph Chen vdata = desc->virqs;
20341766119SJoseph Chen parent = (struct udevice *)pdata;
20441766119SJoseph Chen
20541766119SJoseph Chen if (!chip || !vdata || !parent)
20641766119SJoseph Chen return;
20741766119SJoseph Chen
20841766119SJoseph Chen /* Read all status register */
20941766119SJoseph Chen for (i = 0; i < chip->num_regs; i++) {
21041766119SJoseph Chen status_reg = reg_base_get(desc, chip->status_base, i);
211*1b461e2dSJoseph Chen desc->status_buf[i] = chip->read(parent, status_reg);
21241766119SJoseph Chen if (desc->status_buf[i] < 0) {
21341766119SJoseph Chen printf("%s: Read status register 0x%x failed, ret=%d\n",
21441766119SJoseph Chen __func__, status_reg, desc->status_buf[i]);
21541766119SJoseph Chen }
21641766119SJoseph Chen }
21741766119SJoseph Chen
21841766119SJoseph Chen /* Handle all virq handler */
21941766119SJoseph Chen for (i = 0; i < chip->num_irqs; i++) {
22041766119SJoseph Chen if (desc->status_buf[chip->irqs[i].reg_offset] &
22141766119SJoseph Chen chip->irqs[i].mask) {
22241766119SJoseph Chen irq = vdata[i].irq;
22341766119SJoseph Chen data = vdata[i].data;
22441766119SJoseph Chen
225ae63f119SJoseph Chen if (vdata[i].handle_irq) {
226ae63f119SJoseph Chen vdata[i].count++;
22741766119SJoseph Chen vdata[i].handle_irq(irq, data);
22841766119SJoseph Chen }
22941766119SJoseph Chen }
230ae63f119SJoseph Chen }
23141766119SJoseph Chen
23241766119SJoseph Chen /* Clear all status register */
23341766119SJoseph Chen for (i = 0; i < chip->num_regs; i++) {
23441766119SJoseph Chen status_reg = reg_base_get(desc, chip->status_base, i);
235*1b461e2dSJoseph Chen ret = chip->write(parent, status_reg, ~0U);
23641766119SJoseph Chen if (ret)
23741766119SJoseph Chen printf("%s: Clear status register 0x%x failed, ret=%d\n",
23841766119SJoseph Chen __func__, status_reg, ret);
23941766119SJoseph Chen }
24041766119SJoseph Chen }
24141766119SJoseph Chen
virq_add_chip(struct udevice * dev,struct virq_chip * chip,int irq)24225c13168SJoseph Chen int virq_add_chip(struct udevice *dev, struct virq_chip *chip, int irq)
24341766119SJoseph Chen {
24441766119SJoseph Chen struct virq_data *vdata;
24541766119SJoseph Chen struct virq_desc *desc;
24641766119SJoseph Chen uint *status_buf;
24725c13168SJoseph Chen uint status_reg;
24841766119SJoseph Chen uint mask_reg;
24941766119SJoseph Chen int ret;
25041766119SJoseph Chen int i;
25141766119SJoseph Chen
25241766119SJoseph Chen if (irq < 0)
25341766119SJoseph Chen return -EINVAL;
25441766119SJoseph Chen
25541766119SJoseph Chen desc = (struct virq_desc *)malloc(sizeof(*desc));
25641766119SJoseph Chen if (!desc)
25741766119SJoseph Chen return -ENOMEM;
25841766119SJoseph Chen
25941766119SJoseph Chen vdata = (struct virq_data *)calloc(sizeof(*vdata), chip->num_irqs);
26041766119SJoseph Chen if (!vdata) {
26141766119SJoseph Chen ret = -ENOMEM;
26241766119SJoseph Chen goto free1;
26341766119SJoseph Chen }
26441766119SJoseph Chen
26541766119SJoseph Chen status_buf = (uint *)calloc(sizeof(*status_buf), chip->num_irqs);
26641766119SJoseph Chen if (!status_buf) {
26741766119SJoseph Chen ret = -ENOMEM;
26841766119SJoseph Chen goto free2;
26941766119SJoseph Chen }
27041766119SJoseph Chen
27141766119SJoseph Chen for (i = 0; i < chip->num_irqs; i++)
27241766119SJoseph Chen vdata[i].irq = virq_id_alloc();
27341766119SJoseph Chen
27441766119SJoseph Chen desc->parent = dev;
27541766119SJoseph Chen desc->pirq = irq;
27641766119SJoseph Chen desc->chip = chip;
27741766119SJoseph Chen desc->virqs = vdata;
27825c13168SJoseph Chen desc->use_count = 0;
27941766119SJoseph Chen desc->irq_base = vdata[0].irq;
28041766119SJoseph Chen desc->irq_end = vdata[chip->num_irqs - 1].irq;
28141766119SJoseph Chen desc->status_buf = status_buf;
28241766119SJoseph Chen desc->reg_stride = chip->irq_reg_stride ? : 1;
28341766119SJoseph Chen desc->unalign_reg_stride = chip->irq_unalign_reg_stride ? : 1;
28441766119SJoseph Chen desc->unalign_reg_idx = chip->irq_unalign_reg_stride ?
28541766119SJoseph Chen chip->irq_unalign_reg_idx : 0;
28641766119SJoseph Chen list_add_tail(&desc->node, &virq_desc_head);
28741766119SJoseph Chen
28841766119SJoseph Chen /* Mask all register */
28941766119SJoseph Chen for (i = 0; i < chip->num_regs; i++) {
29041766119SJoseph Chen mask_reg = reg_base_get(desc, chip->mask_base, i);
291*1b461e2dSJoseph Chen ret = chip->write(dev, mask_reg, ~0U);
29241766119SJoseph Chen if (ret)
29341766119SJoseph Chen printf("%s: Set mask register 0x%x failed, ret=%d\n",
29441766119SJoseph Chen __func__, mask_reg, ret);
29541766119SJoseph Chen }
29641766119SJoseph Chen
29725c13168SJoseph Chen /* Clear all status */
29825c13168SJoseph Chen for (i = 0; i < chip->num_regs; i++) {
29925c13168SJoseph Chen status_reg = reg_base_get(desc, chip->status_base, i);
300*1b461e2dSJoseph Chen ret = chip->write(dev, status_reg, ~0U);
30125c13168SJoseph Chen if (ret)
30225c13168SJoseph Chen printf("%s: Clear status register 0x%x failed, ret=%d\n",
30325c13168SJoseph Chen __func__, status_reg, ret);
30425c13168SJoseph Chen }
30525c13168SJoseph Chen
30641766119SJoseph Chen /* Add parent irq into interrupt framework with generic virq handler */
30741766119SJoseph Chen irq_install_handler(irq, virq_chip_generic_handler, dev);
30841766119SJoseph Chen
30925c13168SJoseph Chen return irq_handler_disable(irq);
31041766119SJoseph Chen
31141766119SJoseph Chen free1:
31241766119SJoseph Chen free(desc);
31341766119SJoseph Chen free2:
31441766119SJoseph Chen free(status_buf);
31541766119SJoseph Chen
31641766119SJoseph Chen return ret;
31741766119SJoseph Chen }
31841766119SJoseph Chen
virq_init(void)31941766119SJoseph Chen static int virq_init(void)
32041766119SJoseph Chen {
32141766119SJoseph Chen INIT_LIST_HEAD(&virq_desc_head);
32241766119SJoseph Chen return 0;
32341766119SJoseph Chen }
32441766119SJoseph Chen
__virq_enable(int irq,int enable)32541766119SJoseph Chen static int __virq_enable(int irq, int enable)
32641766119SJoseph Chen {
32741766119SJoseph Chen struct virq_chip *chip;
32841766119SJoseph Chen struct virq_desc *desc;
32941766119SJoseph Chen uint mask_reg, mask_val;
33041766119SJoseph Chen uint reg_val;
33141766119SJoseph Chen int virq;
33241766119SJoseph Chen int ret;
33341766119SJoseph Chen
33441766119SJoseph Chen desc = find_virq_desc(irq);
33541766119SJoseph Chen if (!desc) {
33641766119SJoseph Chen printf("%s: %s Invalid irq %d\n",
33741766119SJoseph Chen __func__, enable ? "Enable" : "Disable", irq);
33841766119SJoseph Chen return -ENOENT;
33941766119SJoseph Chen }
34041766119SJoseph Chen
34141766119SJoseph Chen chip = desc->chip;
34241766119SJoseph Chen if (!chip)
34341766119SJoseph Chen return -ENOENT;
34441766119SJoseph Chen
34541766119SJoseph Chen virq = irq - desc->irq_base;
34641766119SJoseph Chen mask_val = chip->irqs[virq].mask;
34741766119SJoseph Chen mask_reg = reg_base_get(desc, chip->mask_base,
34841766119SJoseph Chen chip->irqs[virq].reg_offset);
349*1b461e2dSJoseph Chen reg_val = chip->read(desc->parent, mask_reg);
35041766119SJoseph Chen if (enable)
35141766119SJoseph Chen reg_val &= ~mask_val;
35241766119SJoseph Chen else
35341766119SJoseph Chen reg_val |= mask_val;
35441766119SJoseph Chen
355*1b461e2dSJoseph Chen ret = chip->write(desc->parent, mask_reg, reg_val);
35641766119SJoseph Chen if (ret) {
35741766119SJoseph Chen printf("%s: Clear status register 0x%x failed, ret=%d\n",
35841766119SJoseph Chen __func__, mask_reg, ret);
35941766119SJoseph Chen return ret;
36041766119SJoseph Chen }
36141766119SJoseph Chen
362ae63f119SJoseph Chen if (enable)
363ae63f119SJoseph Chen desc->virqs[virq].flag |= IRQ_FLG_ENABLE;
364ae63f119SJoseph Chen else
365ae63f119SJoseph Chen desc->virqs[virq].flag &= ~IRQ_FLG_ENABLE;
366ae63f119SJoseph Chen
36741766119SJoseph Chen return 0;
36841766119SJoseph Chen }
36941766119SJoseph Chen
virq_enable(int irq)37041766119SJoseph Chen static int virq_enable(int irq)
37141766119SJoseph Chen {
37225c13168SJoseph Chen struct virq_desc *desc = find_virq_desc(irq);
37325c13168SJoseph Chen int ret;
37425c13168SJoseph Chen
37541766119SJoseph Chen if (bad_virq(irq))
37641766119SJoseph Chen return -EINVAL;
37741766119SJoseph Chen
37825c13168SJoseph Chen ret = __virq_enable(irq, 1);
37925c13168SJoseph Chen if (!ret) {
38025c13168SJoseph Chen if (desc->use_count == 0)
38125c13168SJoseph Chen irq_handler_enable(desc->pirq);
38225c13168SJoseph Chen desc->use_count++;
38325c13168SJoseph Chen }
38425c13168SJoseph Chen
38525c13168SJoseph Chen return ret;
38641766119SJoseph Chen }
38741766119SJoseph Chen
virq_disable(int irq)38841766119SJoseph Chen static int virq_disable(int irq)
38941766119SJoseph Chen {
39025c13168SJoseph Chen struct virq_desc *desc = find_virq_desc(irq);
39125c13168SJoseph Chen int ret;
39225c13168SJoseph Chen
39341766119SJoseph Chen if (bad_virq(irq))
39441766119SJoseph Chen return -EINVAL;
39541766119SJoseph Chen
39625c13168SJoseph Chen ret = __virq_enable(irq, 0);
39725c13168SJoseph Chen if (!ret) {
39825c13168SJoseph Chen if (desc->use_count <= 0)
39925c13168SJoseph Chen return ret;
40025c13168SJoseph Chen
40125c13168SJoseph Chen if (desc->use_count == 1)
40225c13168SJoseph Chen irq_handler_disable(desc->pirq);
40325c13168SJoseph Chen desc->use_count--;
40425c13168SJoseph Chen }
40525c13168SJoseph Chen
40625c13168SJoseph Chen return ret;
40741766119SJoseph Chen }
40841766119SJoseph Chen
40941766119SJoseph Chen struct irq_chip virq_generic_chip = {
41041766119SJoseph Chen .name = "virq-irq-chip",
41141766119SJoseph Chen .irq_init = virq_init,
41241766119SJoseph Chen .irq_enable = virq_enable,
41341766119SJoseph Chen .irq_disable = virq_disable,
41441766119SJoseph Chen };
41541766119SJoseph Chen
arch_virq_get_irqchip(void)41641766119SJoseph Chen struct irq_chip *arch_virq_get_irqchip(void)
41741766119SJoseph Chen {
41841766119SJoseph Chen return &virq_generic_chip;
41941766119SJoseph Chen }
420