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