^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) * Copyright (C) 2016 Marvell
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * This file is licensed under the terms of the GNU General Public
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * License version 2. This program is licensed "as is" without any
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * warranty of any kind, whether express or implied.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #define pr_fmt(fmt) "GIC-ODMI: " fmt
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/irq.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/irqchip.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/irqdomain.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/msi.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <dt-bindings/interrupt-controller/arm-gic.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define GICP_ODMIN_SET 0x40
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define GICP_ODMI_INT_NUM_SHIFT 12
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define GICP_ODMIN_GM_EP_R0 0x110
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define GICP_ODMIN_GM_EP_R1 0x114
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define GICP_ODMIN_GM_EA_R0 0x108
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) #define GICP_ODMIN_GM_EA_R1 0x118
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) * We don't support the group events, so we simply have 8 interrupts
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) * per frame.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) #define NODMIS_SHIFT 3
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) #define NODMIS_PER_FRAME (1 << NODMIS_SHIFT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) #define NODMIS_MASK (NODMIS_PER_FRAME - 1)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct odmi_data {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) struct resource res;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) void __iomem *base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) unsigned int spi_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) static struct odmi_data *odmis;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) static unsigned long *odmis_bm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static unsigned int odmis_count;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) /* Protects odmis_bm */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) static DEFINE_SPINLOCK(odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) static void odmi_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) struct odmi_data *odmi;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) phys_addr_t addr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) unsigned int odmin;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (WARN_ON(d->hwirq >= odmis_count * NODMIS_PER_FRAME))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) odmi = &odmis[d->hwirq >> NODMIS_SHIFT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) odmin = d->hwirq & NODMIS_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) addr = odmi->res.start + GICP_ODMIN_SET;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) msg->address_hi = upper_32_bits(addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) msg->address_lo = lower_32_bits(addr);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) msg->data = odmin << GICP_ODMI_INT_NUM_SHIFT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) static struct irq_chip odmi_irq_chip = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) .name = "ODMI",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) .irq_mask = irq_chip_mask_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) .irq_unmask = irq_chip_unmask_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) .irq_eoi = irq_chip_eoi_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) .irq_set_affinity = irq_chip_set_affinity_parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) .irq_compose_msi_msg = odmi_compose_msi_msg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) static int odmi_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) unsigned int nr_irqs, void *args)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) struct odmi_data *odmi = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) struct irq_fwspec fwspec;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) struct irq_data *d;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) unsigned int hwirq, odmin;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) spin_lock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) hwirq = find_first_zero_bit(odmis_bm, NODMIS_PER_FRAME * odmis_count);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) if (hwirq >= NODMIS_PER_FRAME * odmis_count) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) spin_unlock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) return -ENOSPC;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) __set_bit(hwirq, odmis_bm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) spin_unlock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) odmi = &odmis[hwirq >> NODMIS_SHIFT];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) odmin = hwirq & NODMIS_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) fwspec.fwnode = domain->parent->fwnode;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) fwspec.param_count = 3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) fwspec.param[0] = GIC_SPI;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) fwspec.param[1] = odmi->spi_base - 32 + odmin;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) pr_err("Cannot allocate parent IRQ\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) spin_lock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) __clear_bit(odmin, odmis_bm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) spin_unlock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) /* Configure the interrupt line to be edge */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) d = irq_domain_get_irq_data(domain->parent, virq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) d->chip->irq_set_type(d, IRQ_TYPE_EDGE_RISING);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) &odmi_irq_chip, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) static void odmi_irq_domain_free(struct irq_domain *domain,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) unsigned int virq, unsigned int nr_irqs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) struct irq_data *d = irq_domain_get_irq_data(domain, virq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (d->hwirq >= odmis_count * NODMIS_PER_FRAME) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) pr_err("Failed to teardown msi. Invalid hwirq %lu\n", d->hwirq);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) irq_domain_free_irqs_parent(domain, virq, nr_irqs);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) /* Actually free the MSI */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) spin_lock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) __clear_bit(d->hwirq, odmis_bm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) spin_unlock(&odmis_bm_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) static const struct irq_domain_ops odmi_domain_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) .alloc = odmi_irq_domain_alloc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) .free = odmi_irq_domain_free,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) static struct irq_chip odmi_msi_irq_chip = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) .name = "ODMI",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) static struct msi_domain_ops odmi_msi_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) static struct msi_domain_info odmi_msi_domain_info = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) .ops = &odmi_msi_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) .chip = &odmi_msi_irq_chip,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) static int __init mvebu_odmi_init(struct device_node *node,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) struct device_node *parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) struct irq_domain *inner_domain, *plat_domain;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) int ret, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) if (of_property_read_u32(node, "marvell,odmi-frames", &odmis_count))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) odmis = kcalloc(odmis_count, sizeof(struct odmi_data), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) if (!odmis)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) odmis_bm = kcalloc(BITS_TO_LONGS(odmis_count * NODMIS_PER_FRAME),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) sizeof(long), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) if (!odmis_bm) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) goto err_alloc;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) for (i = 0; i < odmis_count; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) struct odmi_data *odmi = &odmis[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) ret = of_address_to_resource(node, i, &odmi->res);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) goto err_unmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) odmi->base = of_io_request_and_map(node, i, "odmi");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) if (IS_ERR(odmi->base)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) ret = PTR_ERR(odmi->base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) goto err_unmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) if (of_property_read_u32_index(node, "marvell,spi-base",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) i, &odmi->spi_base)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) ret = -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) goto err_unmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) inner_domain = irq_domain_create_linear(of_node_to_fwnode(node),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) odmis_count * NODMIS_PER_FRAME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) &odmi_domain_ops, NULL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) if (!inner_domain) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) goto err_unmap;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) inner_domain->parent = irq_find_host(parent);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) &odmi_msi_domain_info,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) inner_domain);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) if (!plat_domain) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) ret = -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) goto err_remove_inner;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221) err_remove_inner:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) irq_domain_remove(inner_domain);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) err_unmap:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) for (i = 0; i < odmis_count; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) struct odmi_data *odmi = &odmis[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) if (odmi->base && !IS_ERR(odmi->base))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) iounmap(odmis[i].base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) kfree(odmis_bm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) err_alloc:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) kfree(odmis);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) IRQCHIP_DECLARE(mvebu_odmi, "marvell,odmi-controller", mvebu_odmi_init);