1e0de91a9SThomas Petazzoni /* 2e0de91a9SThomas Petazzoni * Copyright (C) 2017 Marvell 3e0de91a9SThomas Petazzoni * 4e0de91a9SThomas Petazzoni * Hanna Hawa <hannah@marvell.com> 5e0de91a9SThomas Petazzoni * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 6e0de91a9SThomas Petazzoni * 7e0de91a9SThomas Petazzoni * This file is licensed under the terms of the GNU General Public 8e0de91a9SThomas Petazzoni * License version 2. This program is licensed "as is" without any 9e0de91a9SThomas Petazzoni * warranty of any kind, whether express or implied. 10e0de91a9SThomas Petazzoni */ 11e0de91a9SThomas Petazzoni 12e0de91a9SThomas Petazzoni #include <linux/interrupt.h> 13e0de91a9SThomas Petazzoni #include <linux/irq.h> 14e0de91a9SThomas Petazzoni #include <linux/irqchip.h> 15e0de91a9SThomas Petazzoni #include <linux/irqdomain.h> 164f4c867cSMiquel Raynal #include <linux/jump_label.h> 17e0de91a9SThomas Petazzoni #include <linux/kernel.h> 18e0de91a9SThomas Petazzoni #include <linux/msi.h> 19e0de91a9SThomas Petazzoni #include <linux/of_irq.h> 20e0de91a9SThomas Petazzoni #include <linux/of_platform.h> 21e0de91a9SThomas Petazzoni #include <linux/platform_device.h> 22e0de91a9SThomas Petazzoni 23e0de91a9SThomas Petazzoni #include <dt-bindings/interrupt-controller/mvebu-icu.h> 24e0de91a9SThomas Petazzoni 25e0de91a9SThomas Petazzoni /* ICU registers */ 26e0de91a9SThomas Petazzoni #define ICU_SETSPI_NSR_AL 0x10 27e0de91a9SThomas Petazzoni #define ICU_SETSPI_NSR_AH 0x14 28e0de91a9SThomas Petazzoni #define ICU_CLRSPI_NSR_AL 0x18 29e0de91a9SThomas Petazzoni #define ICU_CLRSPI_NSR_AH 0x1c 30175c98aaSMiquel Raynal #define ICU_SET_SEI_AL 0x50 31175c98aaSMiquel Raynal #define ICU_SET_SEI_AH 0x54 32175c98aaSMiquel Raynal #define ICU_CLR_SEI_AL 0x58 33175c98aaSMiquel Raynal #define ICU_CLR_SEI_AH 0x5C 34e0de91a9SThomas Petazzoni #define ICU_INT_CFG(x) (0x100 + 4 * (x)) 35e0de91a9SThomas Petazzoni #define ICU_INT_ENABLE BIT(24) 36e0de91a9SThomas Petazzoni #define ICU_IS_EDGE BIT(28) 37e0de91a9SThomas Petazzoni #define ICU_GROUP_SHIFT 29 38e0de91a9SThomas Petazzoni 39e0de91a9SThomas Petazzoni /* ICU definitions */ 40e0de91a9SThomas Petazzoni #define ICU_MAX_IRQS 207 41e0de91a9SThomas Petazzoni #define ICU_SATA0_ICU_ID 109 42e0de91a9SThomas Petazzoni #define ICU_SATA1_ICU_ID 107 43e0de91a9SThomas Petazzoni 44175c98aaSMiquel Raynal struct mvebu_icu_subset_data { 45175c98aaSMiquel Raynal unsigned int icu_group; 46175c98aaSMiquel Raynal unsigned int offset_set_ah; 47175c98aaSMiquel Raynal unsigned int offset_set_al; 48175c98aaSMiquel Raynal unsigned int offset_clr_ah; 49175c98aaSMiquel Raynal unsigned int offset_clr_al; 50175c98aaSMiquel Raynal }; 51175c98aaSMiquel Raynal 52e0de91a9SThomas Petazzoni struct mvebu_icu { 53e0de91a9SThomas Petazzoni void __iomem *base; 54e0de91a9SThomas Petazzoni struct device *dev; 55175c98aaSMiquel Raynal }; 56175c98aaSMiquel Raynal 57175c98aaSMiquel Raynal struct mvebu_icu_msi_data { 58175c98aaSMiquel Raynal struct mvebu_icu *icu; 5925eaaabbSMarc Zyngier atomic_t initialized; 60175c98aaSMiquel Raynal const struct mvebu_icu_subset_data *subset_data; 61e0de91a9SThomas Petazzoni }; 62e0de91a9SThomas Petazzoni 63e0de91a9SThomas Petazzoni struct mvebu_icu_irq_data { 64e0de91a9SThomas Petazzoni struct mvebu_icu *icu; 65e0de91a9SThomas Petazzoni unsigned int icu_group; 66e0de91a9SThomas Petazzoni unsigned int type; 67e0de91a9SThomas Petazzoni }; 68e0de91a9SThomas Petazzoni 699fed9ccbSJason Yan static DEFINE_STATIC_KEY_FALSE(legacy_bindings); 704f4c867cSMiquel Raynal 71175c98aaSMiquel Raynal static void mvebu_icu_init(struct mvebu_icu *icu, 72175c98aaSMiquel Raynal struct mvebu_icu_msi_data *msi_data, 73175c98aaSMiquel Raynal struct msi_msg *msg) 7425eaaabbSMarc Zyngier { 75175c98aaSMiquel Raynal const struct mvebu_icu_subset_data *subset = msi_data->subset_data; 76175c98aaSMiquel Raynal 77175c98aaSMiquel Raynal if (atomic_cmpxchg(&msi_data->initialized, false, true)) 7825eaaabbSMarc Zyngier return; 7925eaaabbSMarc Zyngier 80175c98aaSMiquel Raynal /* Set 'SET' ICU SPI message address in AP */ 81175c98aaSMiquel Raynal writel_relaxed(msg[0].address_hi, icu->base + subset->offset_set_ah); 82175c98aaSMiquel Raynal writel_relaxed(msg[0].address_lo, icu->base + subset->offset_set_al); 83175c98aaSMiquel Raynal 84175c98aaSMiquel Raynal if (subset->icu_group != ICU_GRP_NSR) 85175c98aaSMiquel Raynal return; 86175c98aaSMiquel Raynal 87175c98aaSMiquel Raynal /* Set 'CLEAR' ICU SPI message address in AP (level-MSI only) */ 88175c98aaSMiquel Raynal writel_relaxed(msg[1].address_hi, icu->base + subset->offset_clr_ah); 89175c98aaSMiquel Raynal writel_relaxed(msg[1].address_lo, icu->base + subset->offset_clr_al); 9025eaaabbSMarc Zyngier } 9125eaaabbSMarc Zyngier 92e0de91a9SThomas Petazzoni static void mvebu_icu_write_msg(struct msi_desc *desc, struct msi_msg *msg) 93e0de91a9SThomas Petazzoni { 94e0de91a9SThomas Petazzoni struct irq_data *d = irq_get_irq_data(desc->irq); 95175c98aaSMiquel Raynal struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d->domain); 96e0de91a9SThomas Petazzoni struct mvebu_icu_irq_data *icu_irqd = d->chip_data; 97e0de91a9SThomas Petazzoni struct mvebu_icu *icu = icu_irqd->icu; 98e0de91a9SThomas Petazzoni unsigned int icu_int; 99e0de91a9SThomas Petazzoni 100e0de91a9SThomas Petazzoni if (msg->address_lo || msg->address_hi) { 101175c98aaSMiquel Raynal /* One off initialization per domain */ 102175c98aaSMiquel Raynal mvebu_icu_init(icu, msi_data, msg); 103e0de91a9SThomas Petazzoni /* Configure the ICU with irq number & type */ 104e0de91a9SThomas Petazzoni icu_int = msg->data | ICU_INT_ENABLE; 105e0de91a9SThomas Petazzoni if (icu_irqd->type & IRQ_TYPE_EDGE_RISING) 106e0de91a9SThomas Petazzoni icu_int |= ICU_IS_EDGE; 107e0de91a9SThomas Petazzoni icu_int |= icu_irqd->icu_group << ICU_GROUP_SHIFT; 108e0de91a9SThomas Petazzoni } else { 109e0de91a9SThomas Petazzoni /* De-configure the ICU */ 110e0de91a9SThomas Petazzoni icu_int = 0; 111e0de91a9SThomas Petazzoni } 112e0de91a9SThomas Petazzoni 113e0de91a9SThomas Petazzoni writel_relaxed(icu_int, icu->base + ICU_INT_CFG(d->hwirq)); 114e0de91a9SThomas Petazzoni 115e0de91a9SThomas Petazzoni /* 116e0de91a9SThomas Petazzoni * The SATA unit has 2 ports, and a dedicated ICU entry per 117e0de91a9SThomas Petazzoni * port. The ahci sata driver supports only one irq interrupt 118e0de91a9SThomas Petazzoni * per SATA unit. To solve this conflict, we configure the 2 119e0de91a9SThomas Petazzoni * SATA wired interrupts in the south bridge into 1 GIC 120e0de91a9SThomas Petazzoni * interrupt in the north bridge. Even if only a single port 121e0de91a9SThomas Petazzoni * is enabled, if sata node is enabled, both interrupts are 122e0de91a9SThomas Petazzoni * configured (regardless of which port is actually in use). 123e0de91a9SThomas Petazzoni */ 124e0de91a9SThomas Petazzoni if (d->hwirq == ICU_SATA0_ICU_ID || d->hwirq == ICU_SATA1_ICU_ID) { 125e0de91a9SThomas Petazzoni writel_relaxed(icu_int, 126e0de91a9SThomas Petazzoni icu->base + ICU_INT_CFG(ICU_SATA0_ICU_ID)); 127e0de91a9SThomas Petazzoni writel_relaxed(icu_int, 128e0de91a9SThomas Petazzoni icu->base + ICU_INT_CFG(ICU_SATA1_ICU_ID)); 129e0de91a9SThomas Petazzoni } 130e0de91a9SThomas Petazzoni } 131e0de91a9SThomas Petazzoni 132175c98aaSMiquel Raynal static struct irq_chip mvebu_icu_nsr_chip = { 133175c98aaSMiquel Raynal .name = "ICU-NSR", 134175c98aaSMiquel Raynal .irq_mask = irq_chip_mask_parent, 135175c98aaSMiquel Raynal .irq_unmask = irq_chip_unmask_parent, 136175c98aaSMiquel Raynal .irq_eoi = irq_chip_eoi_parent, 137175c98aaSMiquel Raynal .irq_set_type = irq_chip_set_type_parent, 138175c98aaSMiquel Raynal .irq_set_affinity = irq_chip_set_affinity_parent, 139175c98aaSMiquel Raynal }; 140175c98aaSMiquel Raynal 141175c98aaSMiquel Raynal static struct irq_chip mvebu_icu_sei_chip = { 142175c98aaSMiquel Raynal .name = "ICU-SEI", 143175c98aaSMiquel Raynal .irq_ack = irq_chip_ack_parent, 144175c98aaSMiquel Raynal .irq_mask = irq_chip_mask_parent, 145175c98aaSMiquel Raynal .irq_unmask = irq_chip_unmask_parent, 146175c98aaSMiquel Raynal .irq_set_type = irq_chip_set_type_parent, 147175c98aaSMiquel Raynal .irq_set_affinity = irq_chip_set_affinity_parent, 148175c98aaSMiquel Raynal }; 149175c98aaSMiquel Raynal 150e0de91a9SThomas Petazzoni static int 151e0de91a9SThomas Petazzoni mvebu_icu_irq_domain_translate(struct irq_domain *d, struct irq_fwspec *fwspec, 152e0de91a9SThomas Petazzoni unsigned long *hwirq, unsigned int *type) 153e0de91a9SThomas Petazzoni { 154175c98aaSMiquel Raynal struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(d); 1552b4dab69SMiquel Raynal struct mvebu_icu *icu = platform_msi_get_host_data(d); 1564f4c867cSMiquel Raynal unsigned int param_count = static_branch_unlikely(&legacy_bindings) ? 3 : 2; 157e0de91a9SThomas Petazzoni 158e0de91a9SThomas Petazzoni /* Check the count of the parameters in dt */ 1594f4c867cSMiquel Raynal if (WARN_ON(fwspec->param_count != param_count)) { 160e0de91a9SThomas Petazzoni dev_err(icu->dev, "wrong ICU parameter count %d\n", 161e0de91a9SThomas Petazzoni fwspec->param_count); 162e0de91a9SThomas Petazzoni return -EINVAL; 163e0de91a9SThomas Petazzoni } 164e0de91a9SThomas Petazzoni 1654f4c867cSMiquel Raynal if (static_branch_unlikely(&legacy_bindings)) { 1664f4c867cSMiquel Raynal *hwirq = fwspec->param[1]; 1674f4c867cSMiquel Raynal *type = fwspec->param[2] & IRQ_TYPE_SENSE_MASK; 1684f4c867cSMiquel Raynal if (fwspec->param[0] != ICU_GRP_NSR) { 1694f4c867cSMiquel Raynal dev_err(icu->dev, "wrong ICU group type %x\n", 1704f4c867cSMiquel Raynal fwspec->param[0]); 171e0de91a9SThomas Petazzoni return -EINVAL; 172e0de91a9SThomas Petazzoni } 1734f4c867cSMiquel Raynal } else { 1744f4c867cSMiquel Raynal *hwirq = fwspec->param[0]; 1754f4c867cSMiquel Raynal *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; 176175c98aaSMiquel Raynal 177175c98aaSMiquel Raynal /* 178175c98aaSMiquel Raynal * The ICU receives level interrupts. While the NSR are also 179175c98aaSMiquel Raynal * level interrupts, SEI are edge interrupts. Force the type 180175c98aaSMiquel Raynal * here in this case. Please note that this makes the interrupt 181175c98aaSMiquel Raynal * handling unreliable. 182175c98aaSMiquel Raynal */ 183175c98aaSMiquel Raynal if (msi_data->subset_data->icu_group == ICU_GRP_SEI) 184175c98aaSMiquel Raynal *type = IRQ_TYPE_EDGE_RISING; 1854f4c867cSMiquel Raynal } 186e0de91a9SThomas Petazzoni 187e0de91a9SThomas Petazzoni if (*hwirq >= ICU_MAX_IRQS) { 188e0de91a9SThomas Petazzoni dev_err(icu->dev, "invalid interrupt number %ld\n", *hwirq); 189e0de91a9SThomas Petazzoni return -EINVAL; 190e0de91a9SThomas Petazzoni } 191e0de91a9SThomas Petazzoni 192e0de91a9SThomas Petazzoni return 0; 193e0de91a9SThomas Petazzoni } 194e0de91a9SThomas Petazzoni 195e0de91a9SThomas Petazzoni static int 196e0de91a9SThomas Petazzoni mvebu_icu_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, 197e0de91a9SThomas Petazzoni unsigned int nr_irqs, void *args) 198e0de91a9SThomas Petazzoni { 199e0de91a9SThomas Petazzoni int err; 200e0de91a9SThomas Petazzoni unsigned long hwirq; 201e0de91a9SThomas Petazzoni struct irq_fwspec *fwspec = args; 202175c98aaSMiquel Raynal struct mvebu_icu_msi_data *msi_data = platform_msi_get_host_data(domain); 203175c98aaSMiquel Raynal struct mvebu_icu *icu = msi_data->icu; 204e0de91a9SThomas Petazzoni struct mvebu_icu_irq_data *icu_irqd; 205175c98aaSMiquel Raynal struct irq_chip *chip = &mvebu_icu_nsr_chip; 206e0de91a9SThomas Petazzoni 207e0de91a9SThomas Petazzoni icu_irqd = kmalloc(sizeof(*icu_irqd), GFP_KERNEL); 208e0de91a9SThomas Petazzoni if (!icu_irqd) 209e0de91a9SThomas Petazzoni return -ENOMEM; 210e0de91a9SThomas Petazzoni 211e0de91a9SThomas Petazzoni err = mvebu_icu_irq_domain_translate(domain, fwspec, &hwirq, 212e0de91a9SThomas Petazzoni &icu_irqd->type); 213e0de91a9SThomas Petazzoni if (err) { 214e0de91a9SThomas Petazzoni dev_err(icu->dev, "failed to translate ICU parameters\n"); 215e0de91a9SThomas Petazzoni goto free_irqd; 216e0de91a9SThomas Petazzoni } 217e0de91a9SThomas Petazzoni 2184f4c867cSMiquel Raynal if (static_branch_unlikely(&legacy_bindings)) 219e0de91a9SThomas Petazzoni icu_irqd->icu_group = fwspec->param[0]; 2204f4c867cSMiquel Raynal else 221175c98aaSMiquel Raynal icu_irqd->icu_group = msi_data->subset_data->icu_group; 222e0de91a9SThomas Petazzoni icu_irqd->icu = icu; 223e0de91a9SThomas Petazzoni 2249835cec6SThomas Gleixner err = platform_msi_device_domain_alloc(domain, virq, nr_irqs); 225e0de91a9SThomas Petazzoni if (err) { 226e0de91a9SThomas Petazzoni dev_err(icu->dev, "failed to allocate ICU interrupt in parent domain\n"); 227e0de91a9SThomas Petazzoni goto free_irqd; 228e0de91a9SThomas Petazzoni } 229e0de91a9SThomas Petazzoni 230e0de91a9SThomas Petazzoni /* Make sure there is no interrupt left pending by the firmware */ 231e0de91a9SThomas Petazzoni err = irq_set_irqchip_state(virq, IRQCHIP_STATE_PENDING, false); 232e0de91a9SThomas Petazzoni if (err) 233e0de91a9SThomas Petazzoni goto free_msi; 234e0de91a9SThomas Petazzoni 235175c98aaSMiquel Raynal if (icu_irqd->icu_group == ICU_GRP_SEI) 236175c98aaSMiquel Raynal chip = &mvebu_icu_sei_chip; 237175c98aaSMiquel Raynal 238e0de91a9SThomas Petazzoni err = irq_domain_set_hwirq_and_chip(domain, virq, hwirq, 239175c98aaSMiquel Raynal chip, icu_irqd); 240e0de91a9SThomas Petazzoni if (err) { 241e0de91a9SThomas Petazzoni dev_err(icu->dev, "failed to set the data to IRQ domain\n"); 242e0de91a9SThomas Petazzoni goto free_msi; 243e0de91a9SThomas Petazzoni } 244e0de91a9SThomas Petazzoni 245e0de91a9SThomas Petazzoni return 0; 246e0de91a9SThomas Petazzoni 247e0de91a9SThomas Petazzoni free_msi: 2489835cec6SThomas Gleixner platform_msi_device_domain_free(domain, virq, nr_irqs); 249e0de91a9SThomas Petazzoni free_irqd: 250e0de91a9SThomas Petazzoni kfree(icu_irqd); 251e0de91a9SThomas Petazzoni return err; 252e0de91a9SThomas Petazzoni } 253e0de91a9SThomas Petazzoni 254e0de91a9SThomas Petazzoni static void 255e0de91a9SThomas Petazzoni mvebu_icu_irq_domain_free(struct irq_domain *domain, unsigned int virq, 256e0de91a9SThomas Petazzoni unsigned int nr_irqs) 257e0de91a9SThomas Petazzoni { 258e0de91a9SThomas Petazzoni struct irq_data *d = irq_get_irq_data(virq); 259e0de91a9SThomas Petazzoni struct mvebu_icu_irq_data *icu_irqd = d->chip_data; 260e0de91a9SThomas Petazzoni 261e0de91a9SThomas Petazzoni kfree(icu_irqd); 262e0de91a9SThomas Petazzoni 2639835cec6SThomas Gleixner platform_msi_device_domain_free(domain, virq, nr_irqs); 264e0de91a9SThomas Petazzoni } 265e0de91a9SThomas Petazzoni 266e0de91a9SThomas Petazzoni static const struct irq_domain_ops mvebu_icu_domain_ops = { 267e0de91a9SThomas Petazzoni .translate = mvebu_icu_irq_domain_translate, 268e0de91a9SThomas Petazzoni .alloc = mvebu_icu_irq_domain_alloc, 269e0de91a9SThomas Petazzoni .free = mvebu_icu_irq_domain_free, 270e0de91a9SThomas Petazzoni }; 271e0de91a9SThomas Petazzoni 272175c98aaSMiquel Raynal static const struct mvebu_icu_subset_data mvebu_icu_nsr_subset_data = { 273175c98aaSMiquel Raynal .icu_group = ICU_GRP_NSR, 274175c98aaSMiquel Raynal .offset_set_ah = ICU_SETSPI_NSR_AH, 275175c98aaSMiquel Raynal .offset_set_al = ICU_SETSPI_NSR_AL, 276175c98aaSMiquel Raynal .offset_clr_ah = ICU_CLRSPI_NSR_AH, 277175c98aaSMiquel Raynal .offset_clr_al = ICU_CLRSPI_NSR_AL, 278175c98aaSMiquel Raynal }; 279175c98aaSMiquel Raynal 280175c98aaSMiquel Raynal static const struct mvebu_icu_subset_data mvebu_icu_sei_subset_data = { 281175c98aaSMiquel Raynal .icu_group = ICU_GRP_SEI, 282175c98aaSMiquel Raynal .offset_set_ah = ICU_SET_SEI_AH, 283175c98aaSMiquel Raynal .offset_set_al = ICU_SET_SEI_AL, 284175c98aaSMiquel Raynal }; 285175c98aaSMiquel Raynal 2864f4c867cSMiquel Raynal static const struct of_device_id mvebu_icu_subset_of_match[] = { 2874f4c867cSMiquel Raynal { 2884f4c867cSMiquel Raynal .compatible = "marvell,cp110-icu-nsr", 289175c98aaSMiquel Raynal .data = &mvebu_icu_nsr_subset_data, 290175c98aaSMiquel Raynal }, 291175c98aaSMiquel Raynal { 292175c98aaSMiquel Raynal .compatible = "marvell,cp110-icu-sei", 293175c98aaSMiquel Raynal .data = &mvebu_icu_sei_subset_data, 2944f4c867cSMiquel Raynal }, 2954f4c867cSMiquel Raynal {}, 2964f4c867cSMiquel Raynal }; 2974f4c867cSMiquel Raynal 29800885a77SMiquel Raynal static int mvebu_icu_subset_probe(struct platform_device *pdev) 29900885a77SMiquel Raynal { 300175c98aaSMiquel Raynal struct mvebu_icu_msi_data *msi_data; 30100885a77SMiquel Raynal struct device_node *msi_parent_dn; 30200885a77SMiquel Raynal struct device *dev = &pdev->dev; 30300885a77SMiquel Raynal struct irq_domain *irq_domain; 30400885a77SMiquel Raynal 305175c98aaSMiquel Raynal msi_data = devm_kzalloc(dev, sizeof(*msi_data), GFP_KERNEL); 306175c98aaSMiquel Raynal if (!msi_data) 307175c98aaSMiquel Raynal return -ENOMEM; 308175c98aaSMiquel Raynal 309175c98aaSMiquel Raynal if (static_branch_unlikely(&legacy_bindings)) { 310175c98aaSMiquel Raynal msi_data->icu = dev_get_drvdata(dev); 311175c98aaSMiquel Raynal msi_data->subset_data = &mvebu_icu_nsr_subset_data; 312175c98aaSMiquel Raynal } else { 313175c98aaSMiquel Raynal msi_data->icu = dev_get_drvdata(dev->parent); 314175c98aaSMiquel Raynal msi_data->subset_data = of_device_get_match_data(dev); 315175c98aaSMiquel Raynal } 31600885a77SMiquel Raynal 31734fff628SThomas Gleixner dev->msi.domain = of_msi_get_domain(dev, dev->of_node, 31800885a77SMiquel Raynal DOMAIN_BUS_PLATFORM_MSI); 31934fff628SThomas Gleixner if (!dev->msi.domain) 32000885a77SMiquel Raynal return -EPROBE_DEFER; 32100885a77SMiquel Raynal 32234fff628SThomas Gleixner msi_parent_dn = irq_domain_get_of_node(dev->msi.domain); 32300885a77SMiquel Raynal if (!msi_parent_dn) 32400885a77SMiquel Raynal return -ENODEV; 32500885a77SMiquel Raynal 32600885a77SMiquel Raynal irq_domain = platform_msi_create_device_tree_domain(dev, ICU_MAX_IRQS, 32700885a77SMiquel Raynal mvebu_icu_write_msg, 32800885a77SMiquel Raynal &mvebu_icu_domain_ops, 329175c98aaSMiquel Raynal msi_data); 33000885a77SMiquel Raynal if (!irq_domain) { 33100885a77SMiquel Raynal dev_err(dev, "Failed to create ICU MSI domain\n"); 33200885a77SMiquel Raynal return -ENOMEM; 33300885a77SMiquel Raynal } 33400885a77SMiquel Raynal 33500885a77SMiquel Raynal return 0; 33600885a77SMiquel Raynal } 33700885a77SMiquel Raynal 3384f4c867cSMiquel Raynal static struct platform_driver mvebu_icu_subset_driver = { 3394f4c867cSMiquel Raynal .probe = mvebu_icu_subset_probe, 3404f4c867cSMiquel Raynal .driver = { 3414f4c867cSMiquel Raynal .name = "mvebu-icu-subset", 3424f4c867cSMiquel Raynal .of_match_table = mvebu_icu_subset_of_match, 3434f4c867cSMiquel Raynal }, 3444f4c867cSMiquel Raynal }; 3454f4c867cSMiquel Raynal builtin_platform_driver(mvebu_icu_subset_driver); 3464f4c867cSMiquel Raynal 347e0de91a9SThomas Petazzoni static int mvebu_icu_probe(struct platform_device *pdev) 348e0de91a9SThomas Petazzoni { 349e0de91a9SThomas Petazzoni struct mvebu_icu *icu; 35025eaaabbSMarc Zyngier int i; 351e0de91a9SThomas Petazzoni 352e0de91a9SThomas Petazzoni icu = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_icu), 353e0de91a9SThomas Petazzoni GFP_KERNEL); 354e0de91a9SThomas Petazzoni if (!icu) 355e0de91a9SThomas Petazzoni return -ENOMEM; 356e0de91a9SThomas Petazzoni 357e0de91a9SThomas Petazzoni icu->dev = &pdev->dev; 358e0de91a9SThomas Petazzoni 3590c1479a6SCai Huoqing icu->base = devm_platform_ioremap_resource(pdev, 0); 360fbb80d5aSZhen Lei if (IS_ERR(icu->base)) 361e0de91a9SThomas Petazzoni return PTR_ERR(icu->base); 362e0de91a9SThomas Petazzoni 3634f4c867cSMiquel Raynal /* 3644f4c867cSMiquel Raynal * Legacy bindings: ICU is one node with one MSI parent: force manually 3654f4c867cSMiquel Raynal * the probe of the NSR interrupts side. 3664f4c867cSMiquel Raynal * New bindings: ICU node has children, one per interrupt controller 3674f4c867cSMiquel Raynal * having its own MSI parent: call platform_populate(). 3684f4c867cSMiquel Raynal * All ICU instances should use the same bindings. 3694f4c867cSMiquel Raynal */ 3704f4c867cSMiquel Raynal if (!of_get_child_count(pdev->dev.of_node)) 3714f4c867cSMiquel Raynal static_branch_enable(&legacy_bindings); 3724f4c867cSMiquel Raynal 373e0de91a9SThomas Petazzoni /* 374175c98aaSMiquel Raynal * Clean all ICU interrupts of type NSR and SEI, required to 375e0de91a9SThomas Petazzoni * avoid unpredictable SPI assignments done by firmware. 376e0de91a9SThomas Petazzoni */ 377e0de91a9SThomas Petazzoni for (i = 0 ; i < ICU_MAX_IRQS ; i++) { 3789770c667SMiquel Raynal u32 icu_int, icu_grp; 3799770c667SMiquel Raynal 3809770c667SMiquel Raynal icu_int = readl_relaxed(icu->base + ICU_INT_CFG(i)); 3819770c667SMiquel Raynal icu_grp = icu_int >> ICU_GROUP_SHIFT; 3829770c667SMiquel Raynal 3834f4c867cSMiquel Raynal if (icu_grp == ICU_GRP_NSR || 3844f4c867cSMiquel Raynal (icu_grp == ICU_GRP_SEI && 3854f4c867cSMiquel Raynal !static_branch_unlikely(&legacy_bindings))) 386e0de91a9SThomas Petazzoni writel_relaxed(0x0, icu->base + ICU_INT_CFG(i)); 387e0de91a9SThomas Petazzoni } 388e0de91a9SThomas Petazzoni 38900885a77SMiquel Raynal platform_set_drvdata(pdev, icu); 390e0de91a9SThomas Petazzoni 3914f4c867cSMiquel Raynal if (static_branch_unlikely(&legacy_bindings)) 39200885a77SMiquel Raynal return mvebu_icu_subset_probe(pdev); 3934f4c867cSMiquel Raynal else 3944f4c867cSMiquel Raynal return devm_of_platform_populate(&pdev->dev); 395e0de91a9SThomas Petazzoni } 396e0de91a9SThomas Petazzoni 397e0de91a9SThomas Petazzoni static const struct of_device_id mvebu_icu_of_match[] = { 398e0de91a9SThomas Petazzoni { .compatible = "marvell,cp110-icu", }, 399e0de91a9SThomas Petazzoni {}, 400e0de91a9SThomas Petazzoni }; 401e0de91a9SThomas Petazzoni 402e0de91a9SThomas Petazzoni static struct platform_driver mvebu_icu_driver = { 403e0de91a9SThomas Petazzoni .probe = mvebu_icu_probe, 404e0de91a9SThomas Petazzoni .driver = { 405e0de91a9SThomas Petazzoni .name = "mvebu-icu", 406e0de91a9SThomas Petazzoni .of_match_table = mvebu_icu_of_match, 407e0de91a9SThomas Petazzoni }, 408e0de91a9SThomas Petazzoni }; 409e0de91a9SThomas Petazzoni builtin_platform_driver(mvebu_icu_driver); 410