161ce8d8dSMiquel Raynal // SPDX-License-Identifier: GPL-2.0 261ce8d8dSMiquel Raynal 361ce8d8dSMiquel Raynal #define pr_fmt(fmt) "mvebu-sei: " fmt 461ce8d8dSMiquel Raynal 561ce8d8dSMiquel Raynal #include <linux/interrupt.h> 661ce8d8dSMiquel Raynal #include <linux/irq.h> 761ce8d8dSMiquel Raynal #include <linux/irqchip.h> 861ce8d8dSMiquel Raynal #include <linux/irqchip/chained_irq.h> 961ce8d8dSMiquel Raynal #include <linux/irqdomain.h> 1061ce8d8dSMiquel Raynal #include <linux/kernel.h> 1161ce8d8dSMiquel Raynal #include <linux/msi.h> 1261ce8d8dSMiquel Raynal #include <linux/platform_device.h> 1361ce8d8dSMiquel Raynal #include <linux/of_address.h> 1461ce8d8dSMiquel Raynal #include <linux/of_irq.h> 1561ce8d8dSMiquel Raynal #include <linux/of_platform.h> 1661ce8d8dSMiquel Raynal 1761ce8d8dSMiquel Raynal /* Cause register */ 1861ce8d8dSMiquel Raynal #define GICP_SECR(idx) (0x0 + ((idx) * 0x4)) 1961ce8d8dSMiquel Raynal /* Mask register */ 2061ce8d8dSMiquel Raynal #define GICP_SEMR(idx) (0x20 + ((idx) * 0x4)) 2161ce8d8dSMiquel Raynal #define GICP_SET_SEI_OFFSET 0x30 2261ce8d8dSMiquel Raynal 2361ce8d8dSMiquel Raynal #define SEI_IRQ_COUNT_PER_REG 32 2461ce8d8dSMiquel Raynal #define SEI_IRQ_REG_COUNT 2 2561ce8d8dSMiquel Raynal #define SEI_IRQ_COUNT (SEI_IRQ_COUNT_PER_REG * SEI_IRQ_REG_COUNT) 2661ce8d8dSMiquel Raynal #define SEI_IRQ_REG_IDX(irq_id) ((irq_id) / SEI_IRQ_COUNT_PER_REG) 2761ce8d8dSMiquel Raynal #define SEI_IRQ_REG_BIT(irq_id) ((irq_id) % SEI_IRQ_COUNT_PER_REG) 2861ce8d8dSMiquel Raynal 2961ce8d8dSMiquel Raynal struct mvebu_sei_interrupt_range { 3061ce8d8dSMiquel Raynal u32 first; 3161ce8d8dSMiquel Raynal u32 size; 3261ce8d8dSMiquel Raynal }; 3361ce8d8dSMiquel Raynal 3461ce8d8dSMiquel Raynal struct mvebu_sei_caps { 3561ce8d8dSMiquel Raynal struct mvebu_sei_interrupt_range ap_range; 3661ce8d8dSMiquel Raynal struct mvebu_sei_interrupt_range cp_range; 3761ce8d8dSMiquel Raynal }; 3861ce8d8dSMiquel Raynal 3961ce8d8dSMiquel Raynal struct mvebu_sei { 4061ce8d8dSMiquel Raynal struct device *dev; 4161ce8d8dSMiquel Raynal void __iomem *base; 4261ce8d8dSMiquel Raynal struct resource *res; 4361ce8d8dSMiquel Raynal struct irq_domain *sei_domain; 4461ce8d8dSMiquel Raynal struct irq_domain *ap_domain; 4561ce8d8dSMiquel Raynal struct irq_domain *cp_domain; 4661ce8d8dSMiquel Raynal const struct mvebu_sei_caps *caps; 4761ce8d8dSMiquel Raynal 4861ce8d8dSMiquel Raynal /* Lock on MSI allocations/releases */ 4961ce8d8dSMiquel Raynal struct mutex cp_msi_lock; 5061ce8d8dSMiquel Raynal DECLARE_BITMAP(cp_msi_bitmap, SEI_IRQ_COUNT); 5161ce8d8dSMiquel Raynal 5261ce8d8dSMiquel Raynal /* Lock on IRQ masking register */ 5361ce8d8dSMiquel Raynal raw_spinlock_t mask_lock; 5461ce8d8dSMiquel Raynal }; 5561ce8d8dSMiquel Raynal 5661ce8d8dSMiquel Raynal static void mvebu_sei_ack_irq(struct irq_data *d) 5761ce8d8dSMiquel Raynal { 5861ce8d8dSMiquel Raynal struct mvebu_sei *sei = irq_data_get_irq_chip_data(d); 5961ce8d8dSMiquel Raynal u32 reg_idx = SEI_IRQ_REG_IDX(d->hwirq); 6061ce8d8dSMiquel Raynal 6161ce8d8dSMiquel Raynal writel_relaxed(BIT(SEI_IRQ_REG_BIT(d->hwirq)), 6261ce8d8dSMiquel Raynal sei->base + GICP_SECR(reg_idx)); 6361ce8d8dSMiquel Raynal } 6461ce8d8dSMiquel Raynal 6561ce8d8dSMiquel Raynal static void mvebu_sei_mask_irq(struct irq_data *d) 6661ce8d8dSMiquel Raynal { 6761ce8d8dSMiquel Raynal struct mvebu_sei *sei = irq_data_get_irq_chip_data(d); 6861ce8d8dSMiquel Raynal u32 reg, reg_idx = SEI_IRQ_REG_IDX(d->hwirq); 6961ce8d8dSMiquel Raynal unsigned long flags; 7061ce8d8dSMiquel Raynal 7161ce8d8dSMiquel Raynal /* 1 disables the interrupt */ 7261ce8d8dSMiquel Raynal raw_spin_lock_irqsave(&sei->mask_lock, flags); 7361ce8d8dSMiquel Raynal reg = readl_relaxed(sei->base + GICP_SEMR(reg_idx)); 7461ce8d8dSMiquel Raynal reg |= BIT(SEI_IRQ_REG_BIT(d->hwirq)); 7561ce8d8dSMiquel Raynal writel_relaxed(reg, sei->base + GICP_SEMR(reg_idx)); 7661ce8d8dSMiquel Raynal raw_spin_unlock_irqrestore(&sei->mask_lock, flags); 7761ce8d8dSMiquel Raynal } 7861ce8d8dSMiquel Raynal 7961ce8d8dSMiquel Raynal static void mvebu_sei_unmask_irq(struct irq_data *d) 8061ce8d8dSMiquel Raynal { 8161ce8d8dSMiquel Raynal struct mvebu_sei *sei = irq_data_get_irq_chip_data(d); 8261ce8d8dSMiquel Raynal u32 reg, reg_idx = SEI_IRQ_REG_IDX(d->hwirq); 8361ce8d8dSMiquel Raynal unsigned long flags; 8461ce8d8dSMiquel Raynal 8561ce8d8dSMiquel Raynal /* 0 enables the interrupt */ 8661ce8d8dSMiquel Raynal raw_spin_lock_irqsave(&sei->mask_lock, flags); 8761ce8d8dSMiquel Raynal reg = readl_relaxed(sei->base + GICP_SEMR(reg_idx)); 8861ce8d8dSMiquel Raynal reg &= ~BIT(SEI_IRQ_REG_BIT(d->hwirq)); 8961ce8d8dSMiquel Raynal writel_relaxed(reg, sei->base + GICP_SEMR(reg_idx)); 9061ce8d8dSMiquel Raynal raw_spin_unlock_irqrestore(&sei->mask_lock, flags); 9161ce8d8dSMiquel Raynal } 9261ce8d8dSMiquel Raynal 9361ce8d8dSMiquel Raynal static int mvebu_sei_set_affinity(struct irq_data *d, 9461ce8d8dSMiquel Raynal const struct cpumask *mask_val, 9561ce8d8dSMiquel Raynal bool force) 9661ce8d8dSMiquel Raynal { 9761ce8d8dSMiquel Raynal return -EINVAL; 9861ce8d8dSMiquel Raynal } 9961ce8d8dSMiquel Raynal 10061ce8d8dSMiquel Raynal static int mvebu_sei_set_irqchip_state(struct irq_data *d, 10161ce8d8dSMiquel Raynal enum irqchip_irq_state which, 10261ce8d8dSMiquel Raynal bool state) 10361ce8d8dSMiquel Raynal { 10461ce8d8dSMiquel Raynal /* We can only clear the pending state by acking the interrupt */ 10561ce8d8dSMiquel Raynal if (which != IRQCHIP_STATE_PENDING || state) 10661ce8d8dSMiquel Raynal return -EINVAL; 10761ce8d8dSMiquel Raynal 10861ce8d8dSMiquel Raynal mvebu_sei_ack_irq(d); 10961ce8d8dSMiquel Raynal return 0; 11061ce8d8dSMiquel Raynal } 11161ce8d8dSMiquel Raynal 11261ce8d8dSMiquel Raynal static struct irq_chip mvebu_sei_irq_chip = { 11361ce8d8dSMiquel Raynal .name = "SEI", 11461ce8d8dSMiquel Raynal .irq_ack = mvebu_sei_ack_irq, 11561ce8d8dSMiquel Raynal .irq_mask = mvebu_sei_mask_irq, 11661ce8d8dSMiquel Raynal .irq_unmask = mvebu_sei_unmask_irq, 11761ce8d8dSMiquel Raynal .irq_set_affinity = mvebu_sei_set_affinity, 11861ce8d8dSMiquel Raynal .irq_set_irqchip_state = mvebu_sei_set_irqchip_state, 11961ce8d8dSMiquel Raynal }; 12061ce8d8dSMiquel Raynal 12161ce8d8dSMiquel Raynal static int mvebu_sei_ap_set_type(struct irq_data *data, unsigned int type) 12261ce8d8dSMiquel Raynal { 12361ce8d8dSMiquel Raynal if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_LEVEL_HIGH) 12461ce8d8dSMiquel Raynal return -EINVAL; 12561ce8d8dSMiquel Raynal 12661ce8d8dSMiquel Raynal return 0; 12761ce8d8dSMiquel Raynal } 12861ce8d8dSMiquel Raynal 12961ce8d8dSMiquel Raynal static struct irq_chip mvebu_sei_ap_irq_chip = { 13061ce8d8dSMiquel Raynal .name = "AP SEI", 13161ce8d8dSMiquel Raynal .irq_ack = irq_chip_ack_parent, 13261ce8d8dSMiquel Raynal .irq_mask = irq_chip_mask_parent, 13361ce8d8dSMiquel Raynal .irq_unmask = irq_chip_unmask_parent, 13461ce8d8dSMiquel Raynal .irq_set_affinity = irq_chip_set_affinity_parent, 13561ce8d8dSMiquel Raynal .irq_set_type = mvebu_sei_ap_set_type, 13661ce8d8dSMiquel Raynal }; 13761ce8d8dSMiquel Raynal 13861ce8d8dSMiquel Raynal static void mvebu_sei_cp_compose_msi_msg(struct irq_data *data, 13961ce8d8dSMiquel Raynal struct msi_msg *msg) 14061ce8d8dSMiquel Raynal { 14161ce8d8dSMiquel Raynal struct mvebu_sei *sei = data->chip_data; 14261ce8d8dSMiquel Raynal phys_addr_t set = sei->res->start + GICP_SET_SEI_OFFSET; 14361ce8d8dSMiquel Raynal 14461ce8d8dSMiquel Raynal msg->data = data->hwirq + sei->caps->cp_range.first; 14561ce8d8dSMiquel Raynal msg->address_lo = lower_32_bits(set); 14661ce8d8dSMiquel Raynal msg->address_hi = upper_32_bits(set); 14761ce8d8dSMiquel Raynal } 14861ce8d8dSMiquel Raynal 14961ce8d8dSMiquel Raynal static int mvebu_sei_cp_set_type(struct irq_data *data, unsigned int type) 15061ce8d8dSMiquel Raynal { 15161ce8d8dSMiquel Raynal if ((type & IRQ_TYPE_SENSE_MASK) != IRQ_TYPE_EDGE_RISING) 15261ce8d8dSMiquel Raynal return -EINVAL; 15361ce8d8dSMiquel Raynal 15461ce8d8dSMiquel Raynal return 0; 15561ce8d8dSMiquel Raynal } 15661ce8d8dSMiquel Raynal 15761ce8d8dSMiquel Raynal static struct irq_chip mvebu_sei_cp_irq_chip = { 15861ce8d8dSMiquel Raynal .name = "CP SEI", 15961ce8d8dSMiquel Raynal .irq_ack = irq_chip_ack_parent, 16061ce8d8dSMiquel Raynal .irq_mask = irq_chip_mask_parent, 16161ce8d8dSMiquel Raynal .irq_unmask = irq_chip_unmask_parent, 16261ce8d8dSMiquel Raynal .irq_set_affinity = irq_chip_set_affinity_parent, 16361ce8d8dSMiquel Raynal .irq_set_type = mvebu_sei_cp_set_type, 16461ce8d8dSMiquel Raynal .irq_compose_msi_msg = mvebu_sei_cp_compose_msi_msg, 16561ce8d8dSMiquel Raynal }; 16661ce8d8dSMiquel Raynal 16761ce8d8dSMiquel Raynal static int mvebu_sei_domain_alloc(struct irq_domain *domain, unsigned int virq, 16861ce8d8dSMiquel Raynal unsigned int nr_irqs, void *arg) 16961ce8d8dSMiquel Raynal { 17061ce8d8dSMiquel Raynal struct mvebu_sei *sei = domain->host_data; 17161ce8d8dSMiquel Raynal struct irq_fwspec *fwspec = arg; 17261ce8d8dSMiquel Raynal 17361ce8d8dSMiquel Raynal /* Not much to do, just setup the irqdata */ 17461ce8d8dSMiquel Raynal irq_domain_set_hwirq_and_chip(domain, virq, fwspec->param[0], 17561ce8d8dSMiquel Raynal &mvebu_sei_irq_chip, sei); 17661ce8d8dSMiquel Raynal 17761ce8d8dSMiquel Raynal return 0; 17861ce8d8dSMiquel Raynal } 17961ce8d8dSMiquel Raynal 18061ce8d8dSMiquel Raynal static void mvebu_sei_domain_free(struct irq_domain *domain, unsigned int virq, 18161ce8d8dSMiquel Raynal unsigned int nr_irqs) 18261ce8d8dSMiquel Raynal { 18361ce8d8dSMiquel Raynal int i; 18461ce8d8dSMiquel Raynal 18561ce8d8dSMiquel Raynal for (i = 0; i < nr_irqs; i++) { 18661ce8d8dSMiquel Raynal struct irq_data *d = irq_domain_get_irq_data(domain, virq + i); 18761ce8d8dSMiquel Raynal irq_set_handler(virq + i, NULL); 18861ce8d8dSMiquel Raynal irq_domain_reset_irq_data(d); 18961ce8d8dSMiquel Raynal } 19061ce8d8dSMiquel Raynal } 19161ce8d8dSMiquel Raynal 19261ce8d8dSMiquel Raynal static const struct irq_domain_ops mvebu_sei_domain_ops = { 19361ce8d8dSMiquel Raynal .alloc = mvebu_sei_domain_alloc, 19461ce8d8dSMiquel Raynal .free = mvebu_sei_domain_free, 19561ce8d8dSMiquel Raynal }; 19661ce8d8dSMiquel Raynal 19761ce8d8dSMiquel Raynal static int mvebu_sei_ap_translate(struct irq_domain *domain, 19861ce8d8dSMiquel Raynal struct irq_fwspec *fwspec, 19961ce8d8dSMiquel Raynal unsigned long *hwirq, 20061ce8d8dSMiquel Raynal unsigned int *type) 20161ce8d8dSMiquel Raynal { 20261ce8d8dSMiquel Raynal *hwirq = fwspec->param[0]; 20361ce8d8dSMiquel Raynal *type = IRQ_TYPE_LEVEL_HIGH; 20461ce8d8dSMiquel Raynal 20561ce8d8dSMiquel Raynal return 0; 20661ce8d8dSMiquel Raynal } 20761ce8d8dSMiquel Raynal 20861ce8d8dSMiquel Raynal static int mvebu_sei_ap_alloc(struct irq_domain *domain, unsigned int virq, 20961ce8d8dSMiquel Raynal unsigned int nr_irqs, void *arg) 21061ce8d8dSMiquel Raynal { 21161ce8d8dSMiquel Raynal struct mvebu_sei *sei = domain->host_data; 21261ce8d8dSMiquel Raynal struct irq_fwspec fwspec; 21361ce8d8dSMiquel Raynal unsigned long hwirq; 21461ce8d8dSMiquel Raynal unsigned int type; 21561ce8d8dSMiquel Raynal int err; 21661ce8d8dSMiquel Raynal 21761ce8d8dSMiquel Raynal mvebu_sei_ap_translate(domain, arg, &hwirq, &type); 21861ce8d8dSMiquel Raynal 21961ce8d8dSMiquel Raynal fwspec.fwnode = domain->parent->fwnode; 22061ce8d8dSMiquel Raynal fwspec.param_count = 1; 22161ce8d8dSMiquel Raynal fwspec.param[0] = hwirq + sei->caps->ap_range.first; 22261ce8d8dSMiquel Raynal 22361ce8d8dSMiquel Raynal err = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 22461ce8d8dSMiquel Raynal if (err) 22561ce8d8dSMiquel Raynal return err; 22661ce8d8dSMiquel Raynal 22761ce8d8dSMiquel Raynal irq_domain_set_info(domain, virq, hwirq, 22861ce8d8dSMiquel Raynal &mvebu_sei_ap_irq_chip, sei, 22961ce8d8dSMiquel Raynal handle_level_irq, NULL, NULL); 23061ce8d8dSMiquel Raynal irq_set_probe(virq); 23161ce8d8dSMiquel Raynal 23261ce8d8dSMiquel Raynal return 0; 23361ce8d8dSMiquel Raynal } 23461ce8d8dSMiquel Raynal 23561ce8d8dSMiquel Raynal static const struct irq_domain_ops mvebu_sei_ap_domain_ops = { 23661ce8d8dSMiquel Raynal .translate = mvebu_sei_ap_translate, 23761ce8d8dSMiquel Raynal .alloc = mvebu_sei_ap_alloc, 23861ce8d8dSMiquel Raynal .free = irq_domain_free_irqs_parent, 23961ce8d8dSMiquel Raynal }; 24061ce8d8dSMiquel Raynal 24161ce8d8dSMiquel Raynal static void mvebu_sei_cp_release_irq(struct mvebu_sei *sei, unsigned long hwirq) 24261ce8d8dSMiquel Raynal { 24361ce8d8dSMiquel Raynal mutex_lock(&sei->cp_msi_lock); 24461ce8d8dSMiquel Raynal clear_bit(hwirq, sei->cp_msi_bitmap); 24561ce8d8dSMiquel Raynal mutex_unlock(&sei->cp_msi_lock); 24661ce8d8dSMiquel Raynal } 24761ce8d8dSMiquel Raynal 24861ce8d8dSMiquel Raynal static int mvebu_sei_cp_domain_alloc(struct irq_domain *domain, 24961ce8d8dSMiquel Raynal unsigned int virq, unsigned int nr_irqs, 25061ce8d8dSMiquel Raynal void *args) 25161ce8d8dSMiquel Raynal { 25261ce8d8dSMiquel Raynal struct mvebu_sei *sei = domain->host_data; 25361ce8d8dSMiquel Raynal struct irq_fwspec fwspec; 25461ce8d8dSMiquel Raynal unsigned long hwirq; 25561ce8d8dSMiquel Raynal int ret; 25661ce8d8dSMiquel Raynal 25761ce8d8dSMiquel Raynal /* The software only supports single allocations for now */ 25861ce8d8dSMiquel Raynal if (nr_irqs != 1) 25961ce8d8dSMiquel Raynal return -ENOTSUPP; 26061ce8d8dSMiquel Raynal 26161ce8d8dSMiquel Raynal mutex_lock(&sei->cp_msi_lock); 26261ce8d8dSMiquel Raynal hwirq = find_first_zero_bit(sei->cp_msi_bitmap, 26361ce8d8dSMiquel Raynal sei->caps->cp_range.size); 26461ce8d8dSMiquel Raynal if (hwirq < sei->caps->cp_range.size) 26561ce8d8dSMiquel Raynal set_bit(hwirq, sei->cp_msi_bitmap); 26661ce8d8dSMiquel Raynal mutex_unlock(&sei->cp_msi_lock); 26761ce8d8dSMiquel Raynal 26861ce8d8dSMiquel Raynal if (hwirq == sei->caps->cp_range.size) 26961ce8d8dSMiquel Raynal return -ENOSPC; 27061ce8d8dSMiquel Raynal 27161ce8d8dSMiquel Raynal fwspec.fwnode = domain->parent->fwnode; 27261ce8d8dSMiquel Raynal fwspec.param_count = 1; 27361ce8d8dSMiquel Raynal fwspec.param[0] = hwirq + sei->caps->cp_range.first; 27461ce8d8dSMiquel Raynal 27561ce8d8dSMiquel Raynal ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec); 27661ce8d8dSMiquel Raynal if (ret) 27761ce8d8dSMiquel Raynal goto free_irq; 27861ce8d8dSMiquel Raynal 27961ce8d8dSMiquel Raynal irq_domain_set_info(domain, virq, hwirq, 28061ce8d8dSMiquel Raynal &mvebu_sei_cp_irq_chip, sei, 28161ce8d8dSMiquel Raynal handle_edge_irq, NULL, NULL); 28261ce8d8dSMiquel Raynal 28361ce8d8dSMiquel Raynal return 0; 28461ce8d8dSMiquel Raynal 28561ce8d8dSMiquel Raynal free_irq: 28661ce8d8dSMiquel Raynal mvebu_sei_cp_release_irq(sei, hwirq); 28761ce8d8dSMiquel Raynal return ret; 28861ce8d8dSMiquel Raynal } 28961ce8d8dSMiquel Raynal 29061ce8d8dSMiquel Raynal static void mvebu_sei_cp_domain_free(struct irq_domain *domain, 29161ce8d8dSMiquel Raynal unsigned int virq, unsigned int nr_irqs) 29261ce8d8dSMiquel Raynal { 29361ce8d8dSMiquel Raynal struct mvebu_sei *sei = domain->host_data; 29461ce8d8dSMiquel Raynal struct irq_data *d = irq_domain_get_irq_data(domain, virq); 29561ce8d8dSMiquel Raynal 29661ce8d8dSMiquel Raynal if (nr_irqs != 1 || d->hwirq >= sei->caps->cp_range.size) { 29761ce8d8dSMiquel Raynal dev_err(sei->dev, "Invalid hwirq %lu\n", d->hwirq); 29861ce8d8dSMiquel Raynal return; 29961ce8d8dSMiquel Raynal } 30061ce8d8dSMiquel Raynal 30161ce8d8dSMiquel Raynal mvebu_sei_cp_release_irq(sei, d->hwirq); 30261ce8d8dSMiquel Raynal irq_domain_free_irqs_parent(domain, virq, 1); 30361ce8d8dSMiquel Raynal } 30461ce8d8dSMiquel Raynal 30561ce8d8dSMiquel Raynal static const struct irq_domain_ops mvebu_sei_cp_domain_ops = { 30661ce8d8dSMiquel Raynal .alloc = mvebu_sei_cp_domain_alloc, 30761ce8d8dSMiquel Raynal .free = mvebu_sei_cp_domain_free, 30861ce8d8dSMiquel Raynal }; 30961ce8d8dSMiquel Raynal 31061ce8d8dSMiquel Raynal static struct irq_chip mvebu_sei_msi_irq_chip = { 31161ce8d8dSMiquel Raynal .name = "SEI pMSI", 31261ce8d8dSMiquel Raynal .irq_ack = irq_chip_ack_parent, 31361ce8d8dSMiquel Raynal .irq_set_type = irq_chip_set_type_parent, 31461ce8d8dSMiquel Raynal }; 31561ce8d8dSMiquel Raynal 31661ce8d8dSMiquel Raynal static struct msi_domain_ops mvebu_sei_msi_ops = { 31761ce8d8dSMiquel Raynal }; 31861ce8d8dSMiquel Raynal 31961ce8d8dSMiquel Raynal static struct msi_domain_info mvebu_sei_msi_domain_info = { 32061ce8d8dSMiquel Raynal .flags = MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS, 32161ce8d8dSMiquel Raynal .ops = &mvebu_sei_msi_ops, 32261ce8d8dSMiquel Raynal .chip = &mvebu_sei_msi_irq_chip, 32361ce8d8dSMiquel Raynal }; 32461ce8d8dSMiquel Raynal 32561ce8d8dSMiquel Raynal static void mvebu_sei_handle_cascade_irq(struct irq_desc *desc) 32661ce8d8dSMiquel Raynal { 32761ce8d8dSMiquel Raynal struct mvebu_sei *sei = irq_desc_get_handler_data(desc); 32861ce8d8dSMiquel Raynal struct irq_chip *chip = irq_desc_get_chip(desc); 32961ce8d8dSMiquel Raynal u32 idx; 33061ce8d8dSMiquel Raynal 33161ce8d8dSMiquel Raynal chained_irq_enter(chip, desc); 33261ce8d8dSMiquel Raynal 33361ce8d8dSMiquel Raynal for (idx = 0; idx < SEI_IRQ_REG_COUNT; idx++) { 33461ce8d8dSMiquel Raynal unsigned long irqmap; 33561ce8d8dSMiquel Raynal int bit; 33661ce8d8dSMiquel Raynal 33761ce8d8dSMiquel Raynal irqmap = readl_relaxed(sei->base + GICP_SECR(idx)); 33861ce8d8dSMiquel Raynal for_each_set_bit(bit, &irqmap, SEI_IRQ_COUNT_PER_REG) { 33961ce8d8dSMiquel Raynal unsigned long hwirq; 34061ce8d8dSMiquel Raynal unsigned int virq; 34161ce8d8dSMiquel Raynal 34261ce8d8dSMiquel Raynal hwirq = idx * SEI_IRQ_COUNT_PER_REG + bit; 34361ce8d8dSMiquel Raynal virq = irq_find_mapping(sei->sei_domain, hwirq); 34461ce8d8dSMiquel Raynal if (likely(virq)) { 34561ce8d8dSMiquel Raynal generic_handle_irq(virq); 34661ce8d8dSMiquel Raynal continue; 34761ce8d8dSMiquel Raynal } 34861ce8d8dSMiquel Raynal 34961ce8d8dSMiquel Raynal dev_warn(sei->dev, 35061ce8d8dSMiquel Raynal "Spurious IRQ detected (hwirq %lu)\n", hwirq); 35161ce8d8dSMiquel Raynal } 35261ce8d8dSMiquel Raynal } 35361ce8d8dSMiquel Raynal 35461ce8d8dSMiquel Raynal chained_irq_exit(chip, desc); 35561ce8d8dSMiquel Raynal } 35661ce8d8dSMiquel Raynal 35761ce8d8dSMiquel Raynal static void mvebu_sei_reset(struct mvebu_sei *sei) 35861ce8d8dSMiquel Raynal { 35961ce8d8dSMiquel Raynal u32 reg_idx; 36061ce8d8dSMiquel Raynal 36161ce8d8dSMiquel Raynal /* Clear IRQ cause registers, mask all interrupts */ 36261ce8d8dSMiquel Raynal for (reg_idx = 0; reg_idx < SEI_IRQ_REG_COUNT; reg_idx++) { 36361ce8d8dSMiquel Raynal writel_relaxed(0xFFFFFFFF, sei->base + GICP_SECR(reg_idx)); 36461ce8d8dSMiquel Raynal writel_relaxed(0xFFFFFFFF, sei->base + GICP_SEMR(reg_idx)); 36561ce8d8dSMiquel Raynal } 36661ce8d8dSMiquel Raynal } 36761ce8d8dSMiquel Raynal 36861ce8d8dSMiquel Raynal static int mvebu_sei_probe(struct platform_device *pdev) 36961ce8d8dSMiquel Raynal { 37061ce8d8dSMiquel Raynal struct device_node *node = pdev->dev.of_node; 37161ce8d8dSMiquel Raynal struct irq_domain *plat_domain; 37261ce8d8dSMiquel Raynal struct mvebu_sei *sei; 37361ce8d8dSMiquel Raynal u32 parent_irq; 37461ce8d8dSMiquel Raynal int ret; 37561ce8d8dSMiquel Raynal 37661ce8d8dSMiquel Raynal sei = devm_kzalloc(&pdev->dev, sizeof(*sei), GFP_KERNEL); 37761ce8d8dSMiquel Raynal if (!sei) 37861ce8d8dSMiquel Raynal return -ENOMEM; 37961ce8d8dSMiquel Raynal 38061ce8d8dSMiquel Raynal sei->dev = &pdev->dev; 38161ce8d8dSMiquel Raynal 38261ce8d8dSMiquel Raynal mutex_init(&sei->cp_msi_lock); 38361ce8d8dSMiquel Raynal raw_spin_lock_init(&sei->mask_lock); 38461ce8d8dSMiquel Raynal 38561ce8d8dSMiquel Raynal sei->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 38661ce8d8dSMiquel Raynal sei->base = devm_ioremap_resource(sei->dev, sei->res); 3873424243eSDan Carpenter if (IS_ERR(sei->base)) { 38861ce8d8dSMiquel Raynal dev_err(sei->dev, "Failed to remap SEI resource\n"); 3893424243eSDan Carpenter return PTR_ERR(sei->base); 39061ce8d8dSMiquel Raynal } 39161ce8d8dSMiquel Raynal 39261ce8d8dSMiquel Raynal /* Retrieve the SEI capabilities with the interrupt ranges */ 39361ce8d8dSMiquel Raynal sei->caps = of_device_get_match_data(&pdev->dev); 39461ce8d8dSMiquel Raynal if (!sei->caps) { 39561ce8d8dSMiquel Raynal dev_err(sei->dev, 39661ce8d8dSMiquel Raynal "Could not retrieve controller capabilities\n"); 39761ce8d8dSMiquel Raynal return -EINVAL; 39861ce8d8dSMiquel Raynal } 39961ce8d8dSMiquel Raynal 40061ce8d8dSMiquel Raynal /* 40161ce8d8dSMiquel Raynal * Reserve the single (top-level) parent SPI IRQ from which all the 40261ce8d8dSMiquel Raynal * interrupts handled by this driver will be signaled. 40361ce8d8dSMiquel Raynal */ 40461ce8d8dSMiquel Raynal parent_irq = irq_of_parse_and_map(node, 0); 40561ce8d8dSMiquel Raynal if (parent_irq <= 0) { 40661ce8d8dSMiquel Raynal dev_err(sei->dev, "Failed to retrieve top-level SPI IRQ\n"); 40761ce8d8dSMiquel Raynal return -ENODEV; 40861ce8d8dSMiquel Raynal } 40961ce8d8dSMiquel Raynal 41061ce8d8dSMiquel Raynal /* Create the root SEI domain */ 41161ce8d8dSMiquel Raynal sei->sei_domain = irq_domain_create_linear(of_node_to_fwnode(node), 41261ce8d8dSMiquel Raynal (sei->caps->ap_range.size + 41361ce8d8dSMiquel Raynal sei->caps->cp_range.size), 41461ce8d8dSMiquel Raynal &mvebu_sei_domain_ops, 41561ce8d8dSMiquel Raynal sei); 41661ce8d8dSMiquel Raynal if (!sei->sei_domain) { 41761ce8d8dSMiquel Raynal dev_err(sei->dev, "Failed to create SEI IRQ domain\n"); 41861ce8d8dSMiquel Raynal ret = -ENOMEM; 41961ce8d8dSMiquel Raynal goto dispose_irq; 42061ce8d8dSMiquel Raynal } 42161ce8d8dSMiquel Raynal 42261ce8d8dSMiquel Raynal irq_domain_update_bus_token(sei->sei_domain, DOMAIN_BUS_NEXUS); 42361ce8d8dSMiquel Raynal 42461ce8d8dSMiquel Raynal /* Create the 'wired' domain */ 42561ce8d8dSMiquel Raynal sei->ap_domain = irq_domain_create_hierarchy(sei->sei_domain, 0, 42661ce8d8dSMiquel Raynal sei->caps->ap_range.size, 42761ce8d8dSMiquel Raynal of_node_to_fwnode(node), 42861ce8d8dSMiquel Raynal &mvebu_sei_ap_domain_ops, 42961ce8d8dSMiquel Raynal sei); 43061ce8d8dSMiquel Raynal if (!sei->ap_domain) { 43161ce8d8dSMiquel Raynal dev_err(sei->dev, "Failed to create AP IRQ domain\n"); 43261ce8d8dSMiquel Raynal ret = -ENOMEM; 43361ce8d8dSMiquel Raynal goto remove_sei_domain; 43461ce8d8dSMiquel Raynal } 43561ce8d8dSMiquel Raynal 43661ce8d8dSMiquel Raynal irq_domain_update_bus_token(sei->ap_domain, DOMAIN_BUS_WIRED); 43761ce8d8dSMiquel Raynal 43861ce8d8dSMiquel Raynal /* Create the 'MSI' domain */ 43961ce8d8dSMiquel Raynal sei->cp_domain = irq_domain_create_hierarchy(sei->sei_domain, 0, 44061ce8d8dSMiquel Raynal sei->caps->cp_range.size, 44161ce8d8dSMiquel Raynal of_node_to_fwnode(node), 44261ce8d8dSMiquel Raynal &mvebu_sei_cp_domain_ops, 44361ce8d8dSMiquel Raynal sei); 44461ce8d8dSMiquel Raynal if (!sei->cp_domain) { 44561ce8d8dSMiquel Raynal pr_err("Failed to create CPs IRQ domain\n"); 44661ce8d8dSMiquel Raynal ret = -ENOMEM; 44761ce8d8dSMiquel Raynal goto remove_ap_domain; 44861ce8d8dSMiquel Raynal } 44961ce8d8dSMiquel Raynal 45061ce8d8dSMiquel Raynal irq_domain_update_bus_token(sei->cp_domain, DOMAIN_BUS_GENERIC_MSI); 45161ce8d8dSMiquel Raynal 45261ce8d8dSMiquel Raynal plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node), 45361ce8d8dSMiquel Raynal &mvebu_sei_msi_domain_info, 45461ce8d8dSMiquel Raynal sei->cp_domain); 45561ce8d8dSMiquel Raynal if (!plat_domain) { 45661ce8d8dSMiquel Raynal pr_err("Failed to create CPs MSI domain\n"); 45761ce8d8dSMiquel Raynal ret = -ENOMEM; 45861ce8d8dSMiquel Raynal goto remove_cp_domain; 45961ce8d8dSMiquel Raynal } 46061ce8d8dSMiquel Raynal 46161ce8d8dSMiquel Raynal mvebu_sei_reset(sei); 46261ce8d8dSMiquel Raynal 46361ce8d8dSMiquel Raynal irq_set_chained_handler_and_data(parent_irq, 46461ce8d8dSMiquel Raynal mvebu_sei_handle_cascade_irq, 46561ce8d8dSMiquel Raynal sei); 46661ce8d8dSMiquel Raynal 46761ce8d8dSMiquel Raynal return 0; 46861ce8d8dSMiquel Raynal 46961ce8d8dSMiquel Raynal remove_cp_domain: 47061ce8d8dSMiquel Raynal irq_domain_remove(sei->cp_domain); 47161ce8d8dSMiquel Raynal remove_ap_domain: 47261ce8d8dSMiquel Raynal irq_domain_remove(sei->ap_domain); 47361ce8d8dSMiquel Raynal remove_sei_domain: 47461ce8d8dSMiquel Raynal irq_domain_remove(sei->sei_domain); 47561ce8d8dSMiquel Raynal dispose_irq: 47661ce8d8dSMiquel Raynal irq_dispose_mapping(parent_irq); 47761ce8d8dSMiquel Raynal 47861ce8d8dSMiquel Raynal return ret; 47961ce8d8dSMiquel Raynal } 48061ce8d8dSMiquel Raynal 481*f27b744bSYueHaibing static struct mvebu_sei_caps mvebu_sei_ap806_caps = { 48261ce8d8dSMiquel Raynal .ap_range = { 48361ce8d8dSMiquel Raynal .first = 0, 48461ce8d8dSMiquel Raynal .size = 21, 48561ce8d8dSMiquel Raynal }, 48661ce8d8dSMiquel Raynal .cp_range = { 48761ce8d8dSMiquel Raynal .first = 21, 48861ce8d8dSMiquel Raynal .size = 43, 48961ce8d8dSMiquel Raynal }, 49061ce8d8dSMiquel Raynal }; 49161ce8d8dSMiquel Raynal 49261ce8d8dSMiquel Raynal static const struct of_device_id mvebu_sei_of_match[] = { 49361ce8d8dSMiquel Raynal { 49461ce8d8dSMiquel Raynal .compatible = "marvell,ap806-sei", 49561ce8d8dSMiquel Raynal .data = &mvebu_sei_ap806_caps, 49661ce8d8dSMiquel Raynal }, 49761ce8d8dSMiquel Raynal {}, 49861ce8d8dSMiquel Raynal }; 49961ce8d8dSMiquel Raynal 50061ce8d8dSMiquel Raynal static struct platform_driver mvebu_sei_driver = { 50161ce8d8dSMiquel Raynal .probe = mvebu_sei_probe, 50261ce8d8dSMiquel Raynal .driver = { 50361ce8d8dSMiquel Raynal .name = "mvebu-sei", 50461ce8d8dSMiquel Raynal .of_match_table = mvebu_sei_of_match, 50561ce8d8dSMiquel Raynal }, 50661ce8d8dSMiquel Raynal }; 50761ce8d8dSMiquel Raynal builtin_platform_driver(mvebu_sei_driver); 508