linux/drivers/irqchip/irq-ls-scfg-msi.c
<<
>>
Prefs
   1/*
   2 * Freescale SCFG MSI(-X) support
   3 *
   4 * Copyright (C) 2016 Freescale Semiconductor.
   5 *
   6 * Author: Minghuan Lian <Minghuan.Lian@nxp.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/module.h>
  15#include <linux/msi.h>
  16#include <linux/interrupt.h>
  17#include <linux/irq.h>
  18#include <linux/irqchip/chained_irq.h>
  19#include <linux/irqdomain.h>
  20#include <linux/of_irq.h>
  21#include <linux/of_pci.h>
  22#include <linux/of_platform.h>
  23#include <linux/spinlock.h>
  24#include <linux/dma-iommu.h>
  25
  26#define MSI_IRQS_PER_MSIR       32
  27#define MSI_MSIR_OFFSET         4
  28
  29#define MSI_LS1043V1_1_IRQS_PER_MSIR    8
  30#define MSI_LS1043V1_1_MSIR_OFFSET      0x10
  31
  32struct ls_scfg_msi_cfg {
  33        u32 ibs_shift; /* Shift of interrupt bit select */
  34        u32 msir_irqs; /* The irq number per MSIR */
  35        u32 msir_base; /* The base address of MSIR */
  36};
  37
  38struct ls_scfg_msir {
  39        struct ls_scfg_msi *msi_data;
  40        unsigned int index;
  41        unsigned int gic_irq;
  42        unsigned int bit_start;
  43        unsigned int bit_end;
  44        unsigned int srs; /* Shared interrupt register select */
  45        void __iomem *reg;
  46};
  47
  48struct ls_scfg_msi {
  49        spinlock_t              lock;
  50        struct platform_device  *pdev;
  51        struct irq_domain       *parent;
  52        struct irq_domain       *msi_domain;
  53        void __iomem            *regs;
  54        phys_addr_t             msiir_addr;
  55        struct ls_scfg_msi_cfg  *cfg;
  56        u32                     msir_num;
  57        struct ls_scfg_msir     *msir;
  58        u32                     irqs_num;
  59        unsigned long           *used;
  60};
  61
  62static struct irq_chip ls_scfg_msi_irq_chip = {
  63        .name = "MSI",
  64        .irq_mask       = pci_msi_mask_irq,
  65        .irq_unmask     = pci_msi_unmask_irq,
  66};
  67
  68static struct msi_domain_info ls_scfg_msi_domain_info = {
  69        .flags  = (MSI_FLAG_USE_DEF_DOM_OPS |
  70                   MSI_FLAG_USE_DEF_CHIP_OPS |
  71                   MSI_FLAG_PCI_MSIX),
  72        .chip   = &ls_scfg_msi_irq_chip,
  73};
  74
  75static int msi_affinity_flag = 1;
  76
  77static int __init early_parse_ls_scfg_msi(char *p)
  78{
  79        if (p && strncmp(p, "no-affinity", 11) == 0)
  80                msi_affinity_flag = 0;
  81        else
  82                msi_affinity_flag = 1;
  83
  84        return 0;
  85}
  86early_param("lsmsi", early_parse_ls_scfg_msi);
  87
  88static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
  89{
  90        struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
  91
  92        msg->address_hi = upper_32_bits(msi_data->msiir_addr);
  93        msg->address_lo = lower_32_bits(msi_data->msiir_addr);
  94        msg->data = data->hwirq;
  95
  96        if (msi_affinity_flag) {
  97                const struct cpumask *mask;
  98
  99                mask = irq_data_get_effective_affinity_mask(data);
 100                msg->data |= cpumask_first(mask);
 101        }
 102
 103        iommu_dma_map_msi_msg(data->irq, msg);
 104}
 105
 106static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
 107                                    const struct cpumask *mask, bool force)
 108{
 109        struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(irq_data);
 110        u32 cpu;
 111
 112        if (!msi_affinity_flag)
 113                return -EINVAL;
 114
 115        if (!force)
 116                cpu = cpumask_any_and(mask, cpu_online_mask);
 117        else
 118                cpu = cpumask_first(mask);
 119
 120        if (cpu >= msi_data->msir_num)
 121                return -EINVAL;
 122
 123        if (msi_data->msir[cpu].gic_irq <= 0) {
 124                pr_warn("cannot bind the irq to cpu%d\n", cpu);
 125                return -EINVAL;
 126        }
 127
 128        irq_data_update_effective_affinity(irq_data, cpumask_of(cpu));
 129
 130        return IRQ_SET_MASK_OK;
 131}
 132
 133static struct irq_chip ls_scfg_msi_parent_chip = {
 134        .name                   = "SCFG",
 135        .irq_compose_msi_msg    = ls_scfg_msi_compose_msg,
 136        .irq_set_affinity       = ls_scfg_msi_set_affinity,
 137};
 138
 139static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
 140                                        unsigned int virq,
 141                                        unsigned int nr_irqs,
 142                                        void *args)
 143{
 144        struct ls_scfg_msi *msi_data = domain->host_data;
 145        int pos, err = 0;
 146
 147        WARN_ON(nr_irqs != 1);
 148
 149        spin_lock(&msi_data->lock);
 150        pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num);
 151        if (pos < msi_data->irqs_num)
 152                __set_bit(pos, msi_data->used);
 153        else
 154                err = -ENOSPC;
 155        spin_unlock(&msi_data->lock);
 156
 157        if (err)
 158                return err;
 159
 160        irq_domain_set_info(domain, virq, pos,
 161                            &ls_scfg_msi_parent_chip, msi_data,
 162                            handle_simple_irq, NULL, NULL);
 163
 164        return 0;
 165}
 166
 167static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
 168                                   unsigned int virq, unsigned int nr_irqs)
 169{
 170        struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 171        struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d);
 172        int pos;
 173
 174        pos = d->hwirq;
 175        if (pos < 0 || pos >= msi_data->irqs_num) {
 176                pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
 177                return;
 178        }
 179
 180        spin_lock(&msi_data->lock);
 181        __clear_bit(pos, msi_data->used);
 182        spin_unlock(&msi_data->lock);
 183}
 184
 185static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
 186        .alloc  = ls_scfg_msi_domain_irq_alloc,
 187        .free   = ls_scfg_msi_domain_irq_free,
 188};
 189
 190static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
 191{
 192        struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
 193        struct ls_scfg_msi *msi_data = msir->msi_data;
 194        unsigned long val;
 195        int pos, size, virq, hwirq;
 196
 197        chained_irq_enter(irq_desc_get_chip(desc), desc);
 198
 199        val = ioread32be(msir->reg);
 200
 201        pos = msir->bit_start;
 202        size = msir->bit_end + 1;
 203
 204        for_each_set_bit_from(pos, &val, size) {
 205                hwirq = ((msir->bit_end - pos) << msi_data->cfg->ibs_shift) |
 206                        msir->srs;
 207                virq = irq_find_mapping(msi_data->parent, hwirq);
 208                if (virq)
 209                        generic_handle_irq(virq);
 210        }
 211
 212        chained_irq_exit(irq_desc_get_chip(desc), desc);
 213}
 214
 215static int ls_scfg_msi_domains_init(struct ls_scfg_msi *msi_data)
 216{
 217        /* Initialize MSI domain parent */
 218        msi_data->parent = irq_domain_add_linear(NULL,
 219                                                 msi_data->irqs_num,
 220                                                 &ls_scfg_msi_domain_ops,
 221                                                 msi_data);
 222        if (!msi_data->parent) {
 223                dev_err(&msi_data->pdev->dev, "failed to create IRQ domain\n");
 224                return -ENOMEM;
 225        }
 226
 227        msi_data->msi_domain = pci_msi_create_irq_domain(
 228                                of_node_to_fwnode(msi_data->pdev->dev.of_node),
 229                                &ls_scfg_msi_domain_info,
 230                                msi_data->parent);
 231        if (!msi_data->msi_domain) {
 232                dev_err(&msi_data->pdev->dev, "failed to create MSI domain\n");
 233                irq_domain_remove(msi_data->parent);
 234                return -ENOMEM;
 235        }
 236
 237        return 0;
 238}
 239
 240static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index)
 241{
 242        struct ls_scfg_msir *msir;
 243        int virq, i, hwirq;
 244
 245        virq = platform_get_irq(msi_data->pdev, index);
 246        if (virq <= 0)
 247                return -ENODEV;
 248
 249        msir = &msi_data->msir[index];
 250        msir->index = index;
 251        msir->msi_data = msi_data;
 252        msir->gic_irq = virq;
 253        msir->reg = msi_data->regs + msi_data->cfg->msir_base + 4 * index;
 254
 255        if (msi_data->cfg->msir_irqs == MSI_LS1043V1_1_IRQS_PER_MSIR) {
 256                msir->bit_start = 32 - ((msir->index + 1) *
 257                                  MSI_LS1043V1_1_IRQS_PER_MSIR);
 258                msir->bit_end = msir->bit_start +
 259                                MSI_LS1043V1_1_IRQS_PER_MSIR - 1;
 260        } else {
 261                msir->bit_start = 0;
 262                msir->bit_end = msi_data->cfg->msir_irqs - 1;
 263        }
 264
 265        irq_set_chained_handler_and_data(msir->gic_irq,
 266                                         ls_scfg_msi_irq_handler,
 267                                         msir);
 268
 269        if (msi_affinity_flag) {
 270                /* Associate MSIR interrupt to the cpu */
 271                irq_set_affinity(msir->gic_irq, get_cpu_mask(index));
 272                msir->srs = 0; /* This value is determined by the CPU */
 273        } else
 274                msir->srs = index;
 275
 276        /* Release the hwirqs corresponding to this MSIR */
 277        if (!msi_affinity_flag || msir->index == 0) {
 278                for (i = 0; i < msi_data->cfg->msir_irqs; i++) {
 279                        hwirq = i << msi_data->cfg->ibs_shift | msir->index;
 280                        bitmap_clear(msi_data->used, hwirq, 1);
 281                }
 282        }
 283
 284        return 0;
 285}
 286
 287static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir)
 288{
 289        struct ls_scfg_msi *msi_data = msir->msi_data;
 290        int i, hwirq;
 291
 292        if (msir->gic_irq > 0)
 293                irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL);
 294
 295        for (i = 0; i < msi_data->cfg->msir_irqs; i++) {
 296                hwirq = i << msi_data->cfg->ibs_shift | msir->index;
 297                bitmap_set(msi_data->used, hwirq, 1);
 298        }
 299
 300        return 0;
 301}
 302
 303static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
 304        .ibs_shift = 3,
 305        .msir_irqs = MSI_IRQS_PER_MSIR,
 306        .msir_base = MSI_MSIR_OFFSET,
 307};
 308
 309static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
 310        .ibs_shift = 2,
 311        .msir_irqs = MSI_IRQS_PER_MSIR,
 312        .msir_base = MSI_MSIR_OFFSET,
 313};
 314
 315static struct ls_scfg_msi_cfg ls1043_v1_1_msi_cfg = {
 316        .ibs_shift = 2,
 317        .msir_irqs = MSI_LS1043V1_1_IRQS_PER_MSIR,
 318        .msir_base = MSI_LS1043V1_1_MSIR_OFFSET,
 319};
 320
 321static const struct of_device_id ls_scfg_msi_id[] = {
 322        /* The following two misspelled compatibles are obsolete */
 323        { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg},
 324        { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg},
 325
 326        { .compatible = "fsl,ls1012a-msi", .data = &ls1021_msi_cfg },
 327        { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg },
 328        { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg },
 329        { .compatible = "fsl,ls1043a-v1.1-msi", .data = &ls1043_v1_1_msi_cfg },
 330        { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg },
 331        {},
 332};
 333MODULE_DEVICE_TABLE(of, ls_scfg_msi_id);
 334
 335static int ls_scfg_msi_probe(struct platform_device *pdev)
 336{
 337        const struct of_device_id *match;
 338        struct ls_scfg_msi *msi_data;
 339        struct resource *res;
 340        int i, ret;
 341
 342        match = of_match_device(ls_scfg_msi_id, &pdev->dev);
 343        if (!match)
 344                return -ENODEV;
 345
 346        msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
 347        if (!msi_data)
 348                return -ENOMEM;
 349
 350        msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data;
 351
 352        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 353        msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
 354        if (IS_ERR(msi_data->regs)) {
 355                dev_err(&pdev->dev, "failed to initialize 'regs'\n");
 356                return PTR_ERR(msi_data->regs);
 357        }
 358        msi_data->msiir_addr = res->start;
 359
 360        msi_data->pdev = pdev;
 361        spin_lock_init(&msi_data->lock);
 362
 363        msi_data->irqs_num = MSI_IRQS_PER_MSIR *
 364                             (1 << msi_data->cfg->ibs_shift);
 365        msi_data->used = devm_kcalloc(&pdev->dev,
 366                                    BITS_TO_LONGS(msi_data->irqs_num),
 367                                    sizeof(*msi_data->used),
 368                                    GFP_KERNEL);
 369        if (!msi_data->used)
 370                return -ENOMEM;
 371        /*
 372         * Reserve all the hwirqs
 373         * The available hwirqs will be released in ls1_msi_setup_hwirq()
 374         */
 375        bitmap_set(msi_data->used, 0, msi_data->irqs_num);
 376
 377        msi_data->msir_num = of_irq_count(pdev->dev.of_node);
 378
 379        if (msi_affinity_flag) {
 380                u32 cpu_num;
 381
 382                cpu_num = num_possible_cpus();
 383                if (msi_data->msir_num >= cpu_num)
 384                        msi_data->msir_num = cpu_num;
 385                else
 386                        msi_affinity_flag = 0;
 387        }
 388
 389        msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
 390                                      sizeof(*msi_data->msir),
 391                                      GFP_KERNEL);
 392        if (!msi_data->msir)
 393                return -ENOMEM;
 394
 395        for (i = 0; i < msi_data->msir_num; i++)
 396                ls_scfg_msi_setup_hwirq(msi_data, i);
 397
 398        ret = ls_scfg_msi_domains_init(msi_data);
 399        if (ret)
 400                return ret;
 401
 402        platform_set_drvdata(pdev, msi_data);
 403
 404        return 0;
 405}
 406
 407static int ls_scfg_msi_remove(struct platform_device *pdev)
 408{
 409        struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
 410        int i;
 411
 412        for (i = 0; i < msi_data->msir_num; i++)
 413                ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
 414
 415        irq_domain_remove(msi_data->msi_domain);
 416        irq_domain_remove(msi_data->parent);
 417
 418        platform_set_drvdata(pdev, NULL);
 419
 420        return 0;
 421}
 422
 423static struct platform_driver ls_scfg_msi_driver = {
 424        .driver = {
 425                .name = "ls-scfg-msi",
 426                .of_match_table = ls_scfg_msi_id,
 427        },
 428        .probe = ls_scfg_msi_probe,
 429        .remove = ls_scfg_msi_remove,
 430};
 431
 432module_platform_driver(ls_scfg_msi_driver);
 433
 434MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>");
 435MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver");
 436MODULE_LICENSE("GPL v2");
 437