linux/drivers/irqchip/irq-mvebu-gicp.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Marvell
   3 *
   4 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2. This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#include <linux/io.h>
  12#include <linux/irq.h>
  13#include <linux/irqdomain.h>
  14#include <linux/msi.h>
  15#include <linux/of.h>
  16#include <linux/of_irq.h>
  17#include <linux/of_platform.h>
  18#include <linux/platform_device.h>
  19
  20#include <dt-bindings/interrupt-controller/arm-gic.h>
  21
  22#include "irq-mvebu-gicp.h"
  23
  24#define GICP_SETSPI_NSR_OFFSET  0x0
  25#define GICP_CLRSPI_NSR_OFFSET  0x8
  26
  27struct mvebu_gicp_spi_range {
  28        unsigned int start;
  29        unsigned int count;
  30};
  31
  32struct mvebu_gicp {
  33        struct mvebu_gicp_spi_range *spi_ranges;
  34        unsigned int spi_ranges_cnt;
  35        unsigned int spi_cnt;
  36        unsigned long *spi_bitmap;
  37        spinlock_t spi_lock;
  38        struct resource *res;
  39        struct device *dev;
  40};
  41
  42static int gicp_idx_to_spi(struct mvebu_gicp *gicp, int idx)
  43{
  44        int i;
  45
  46        for (i = 0; i < gicp->spi_ranges_cnt; i++) {
  47                struct mvebu_gicp_spi_range *r = &gicp->spi_ranges[i];
  48
  49                if (idx < r->count)
  50                        return r->start + idx;
  51
  52                idx -= r->count;
  53        }
  54
  55        return -EINVAL;
  56}
  57
  58int mvebu_gicp_get_doorbells(struct device_node *dn, phys_addr_t *setspi,
  59                             phys_addr_t *clrspi)
  60{
  61        struct platform_device *pdev;
  62        struct mvebu_gicp *gicp;
  63
  64        pdev = of_find_device_by_node(dn);
  65        if (!pdev)
  66                return -ENODEV;
  67
  68        gicp = platform_get_drvdata(pdev);
  69        if (!gicp)
  70                return -ENODEV;
  71
  72        *setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET;
  73        *clrspi = gicp->res->start + GICP_CLRSPI_NSR_OFFSET;
  74
  75        return 0;
  76}
  77
  78static void gicp_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
  79{
  80        struct mvebu_gicp *gicp = data->chip_data;
  81        phys_addr_t setspi = gicp->res->start + GICP_SETSPI_NSR_OFFSET;
  82
  83        msg->data = data->hwirq;
  84        msg->address_lo = lower_32_bits(setspi);
  85        msg->address_hi = upper_32_bits(setspi);
  86}
  87
  88static struct irq_chip gicp_irq_chip = {
  89        .name                   = "GICP",
  90        .irq_mask               = irq_chip_mask_parent,
  91        .irq_unmask             = irq_chip_unmask_parent,
  92        .irq_eoi                = irq_chip_eoi_parent,
  93        .irq_set_affinity       = irq_chip_set_affinity_parent,
  94        .irq_set_type           = irq_chip_set_type_parent,
  95        .irq_compose_msi_msg    = gicp_compose_msi_msg,
  96};
  97
  98static int gicp_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
  99                                 unsigned int nr_irqs, void *args)
 100{
 101        struct mvebu_gicp *gicp = domain->host_data;
 102        struct irq_fwspec fwspec;
 103        unsigned int hwirq;
 104        int ret;
 105
 106        spin_lock(&gicp->spi_lock);
 107        hwirq = find_first_zero_bit(gicp->spi_bitmap, gicp->spi_cnt);
 108        if (hwirq == gicp->spi_cnt) {
 109                spin_unlock(&gicp->spi_lock);
 110                return -ENOSPC;
 111        }
 112        __set_bit(hwirq, gicp->spi_bitmap);
 113        spin_unlock(&gicp->spi_lock);
 114
 115        fwspec.fwnode = domain->parent->fwnode;
 116        fwspec.param_count = 3;
 117        fwspec.param[0] = GIC_SPI;
 118        fwspec.param[1] = gicp_idx_to_spi(gicp, hwirq) - 32;
 119        /*
 120         * Assume edge rising for now, it will be properly set when
 121         * ->set_type() is called
 122         */
 123        fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
 124
 125        ret = irq_domain_alloc_irqs_parent(domain, virq, 1, &fwspec);
 126        if (ret) {
 127                dev_err(gicp->dev, "Cannot allocate parent IRQ\n");
 128                goto free_hwirq;
 129        }
 130
 131        ret = irq_domain_set_hwirq_and_chip(domain, virq, hwirq,
 132                                            &gicp_irq_chip, gicp);
 133        if (ret)
 134                goto free_irqs_parent;
 135
 136        return 0;
 137
 138free_irqs_parent:
 139        irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 140free_hwirq:
 141        spin_lock(&gicp->spi_lock);
 142        __clear_bit(hwirq, gicp->spi_bitmap);
 143        spin_unlock(&gicp->spi_lock);
 144        return ret;
 145}
 146
 147static void gicp_irq_domain_free(struct irq_domain *domain,
 148                                 unsigned int virq, unsigned int nr_irqs)
 149{
 150        struct mvebu_gicp *gicp = domain->host_data;
 151        struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 152
 153        if (d->hwirq >= gicp->spi_cnt) {
 154                dev_err(gicp->dev, "Invalid hwirq %lu\n", d->hwirq);
 155                return;
 156        }
 157
 158        irq_domain_free_irqs_parent(domain, virq, nr_irqs);
 159
 160        spin_lock(&gicp->spi_lock);
 161        __clear_bit(d->hwirq, gicp->spi_bitmap);
 162        spin_unlock(&gicp->spi_lock);
 163}
 164
 165static const struct irq_domain_ops gicp_domain_ops = {
 166        .alloc  = gicp_irq_domain_alloc,
 167        .free   = gicp_irq_domain_free,
 168};
 169
 170static struct irq_chip gicp_msi_irq_chip = {
 171        .name           = "GICP",
 172        .irq_set_type   = irq_chip_set_type_parent,
 173};
 174
 175static struct msi_domain_ops gicp_msi_ops = {
 176};
 177
 178static struct msi_domain_info gicp_msi_domain_info = {
 179        .flags  = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS),
 180        .ops    = &gicp_msi_ops,
 181        .chip   = &gicp_msi_irq_chip,
 182};
 183
 184static int mvebu_gicp_probe(struct platform_device *pdev)
 185{
 186        struct mvebu_gicp *gicp;
 187        struct irq_domain *inner_domain, *plat_domain, *parent_domain;
 188        struct device_node *node = pdev->dev.of_node;
 189        struct device_node *irq_parent_dn;
 190        int ret, i;
 191
 192        gicp = devm_kzalloc(&pdev->dev, sizeof(*gicp), GFP_KERNEL);
 193        if (!gicp)
 194                return -ENOMEM;
 195
 196        gicp->dev = &pdev->dev;
 197
 198        gicp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 199        if (!gicp->res)
 200                return -ENODEV;
 201
 202        ret = of_property_count_u32_elems(node, "marvell,spi-ranges");
 203        if (ret < 0)
 204                return ret;
 205
 206        gicp->spi_ranges_cnt = ret / 2;
 207
 208        gicp->spi_ranges =
 209                devm_kzalloc(&pdev->dev,
 210                             gicp->spi_ranges_cnt *
 211                             sizeof(struct mvebu_gicp_spi_range),
 212                             GFP_KERNEL);
 213        if (!gicp->spi_ranges)
 214                return -ENOMEM;
 215
 216        for (i = 0; i < gicp->spi_ranges_cnt; i++) {
 217                of_property_read_u32_index(node, "marvell,spi-ranges",
 218                                           i * 2,
 219                                           &gicp->spi_ranges[i].start);
 220
 221                of_property_read_u32_index(node, "marvell,spi-ranges",
 222                                           i * 2 + 1,
 223                                           &gicp->spi_ranges[i].count);
 224
 225                gicp->spi_cnt += gicp->spi_ranges[i].count;
 226        }
 227
 228        gicp->spi_bitmap = devm_kzalloc(&pdev->dev,
 229                                BITS_TO_LONGS(gicp->spi_cnt) * sizeof(long),
 230                                GFP_KERNEL);
 231        if (!gicp->spi_bitmap)
 232                return -ENOMEM;
 233
 234        irq_parent_dn = of_irq_find_parent(node);
 235        if (!irq_parent_dn) {
 236                dev_err(&pdev->dev, "failed to find parent IRQ node\n");
 237                return -ENODEV;
 238        }
 239
 240        parent_domain = irq_find_host(irq_parent_dn);
 241        if (!parent_domain) {
 242                dev_err(&pdev->dev, "failed to find parent IRQ domain\n");
 243                return -ENODEV;
 244        }
 245
 246        inner_domain = irq_domain_create_hierarchy(parent_domain, 0,
 247                                                   gicp->spi_cnt,
 248                                                   of_node_to_fwnode(node),
 249                                                   &gicp_domain_ops, gicp);
 250        if (!inner_domain)
 251                return -ENOMEM;
 252
 253
 254        plat_domain = platform_msi_create_irq_domain(of_node_to_fwnode(node),
 255                                                     &gicp_msi_domain_info,
 256                                                     inner_domain);
 257        if (!plat_domain) {
 258                irq_domain_remove(inner_domain);
 259                return -ENOMEM;
 260        }
 261
 262        platform_set_drvdata(pdev, gicp);
 263
 264        return 0;
 265}
 266
 267static const struct of_device_id mvebu_gicp_of_match[] = {
 268        { .compatible = "marvell,ap806-gicp", },
 269        {},
 270};
 271
 272static struct platform_driver mvebu_gicp_driver = {
 273        .probe  = mvebu_gicp_probe,
 274        .driver = {
 275                .name = "mvebu-gicp",
 276                .of_match_table = mvebu_gicp_of_match,
 277        },
 278};
 279builtin_platform_driver(mvebu_gicp_driver);
 280