linux/drivers/irqchip/irq-ls-scfg-msi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Freescale SCFG MSI(-X) support
   4 *
   5 * Copyright (C) 2016 Freescale Semiconductor.
   6 *
   7 * Author: Minghuan Lian <Minghuan.Lian@nxp.com>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/msi.h>
  13#include <linux/interrupt.h>
  14#include <linux/irq.h>
  15#include <linux/irqchip/chained_irq.h>
  16#include <linux/irqdomain.h>
  17#include <linux/of_irq.h>
  18#include <linux/of_pci.h>
  19#include <linux/of_platform.h>
  20#include <linux/spinlock.h>
  21#include <linux/dma-iommu.h>
  22
  23#define MSI_IRQS_PER_MSIR       32
  24#define MSI_MSIR_OFFSET         4
  25
  26#define MSI_LS1043V1_1_IRQS_PER_MSIR    8
  27#define MSI_LS1043V1_1_MSIR_OFFSET      0x10
  28
  29struct ls_scfg_msi_cfg {
  30        u32 ibs_shift; /* Shift of interrupt bit select */
  31        u32 msir_irqs; /* The irq number per MSIR */
  32        u32 msir_base; /* The base address of MSIR */
  33};
  34
  35struct ls_scfg_msir {
  36        struct ls_scfg_msi *msi_data;
  37        unsigned int index;
  38        unsigned int gic_irq;
  39        unsigned int bit_start;
  40        unsigned int bit_end;
  41        unsigned int srs; /* Shared interrupt register select */
  42        void __iomem *reg;
  43};
  44
  45struct ls_scfg_msi {
  46        spinlock_t              lock;
  47        struct platform_device  *pdev;
  48        struct irq_domain       *parent;
  49        struct irq_domain       *msi_domain;
  50        void __iomem            *regs;
  51        phys_addr_t             msiir_addr;
  52        struct ls_scfg_msi_cfg  *cfg;
  53        u32                     msir_num;
  54        struct ls_scfg_msir     *msir;
  55        u32                     irqs_num;
  56        unsigned long           *used;
  57};
  58
  59static struct irq_chip ls_scfg_msi_irq_chip = {
  60        .name = "MSI",
  61        .irq_mask       = pci_msi_mask_irq,
  62        .irq_unmask     = pci_msi_unmask_irq,
  63};
  64
  65static struct msi_domain_info ls_scfg_msi_domain_info = {
  66        .flags  = (MSI_FLAG_USE_DEF_DOM_OPS |
  67                   MSI_FLAG_USE_DEF_CHIP_OPS |
  68                   MSI_FLAG_PCI_MSIX),
  69        .chip   = &ls_scfg_msi_irq_chip,
  70};
  71
  72static int msi_affinity_flag = 1;
  73
  74static int __init early_parse_ls_scfg_msi(char *p)
  75{
  76        if (p && strncmp(p, "no-affinity", 11) == 0)
  77                msi_affinity_flag = 0;
  78        else
  79                msi_affinity_flag = 1;
  80
  81        return 0;
  82}
  83early_param("lsmsi", early_parse_ls_scfg_msi);
  84
  85static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
  86{
  87        struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
  88
  89        msg->address_hi = upper_32_bits(msi_data->msiir_addr);
  90        msg->address_lo = lower_32_bits(msi_data->msiir_addr);
  91        msg->data = data->hwirq;
  92
  93        if (msi_affinity_flag) {
  94                const struct cpumask *mask;
  95
  96                mask = irq_data_get_effective_affinity_mask(data);
  97                msg->data |= cpumask_first(mask);
  98        }
  99
 100        iommu_dma_compose_msi_msg(irq_data_get_msi_desc(data), msg);
 101}
 102
 103static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
 104                                    const struct cpumask *mask, bool force)
 105{
 106        struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(irq_data);
 107        u32 cpu;
 108
 109        if (!msi_affinity_flag)
 110                return -EINVAL;
 111
 112        if (!force)
 113                cpu = cpumask_any_and(mask, cpu_online_mask);
 114        else
 115                cpu = cpumask_first(mask);
 116
 117        if (cpu >= msi_data->msir_num)
 118                return -EINVAL;
 119
 120        if (msi_data->msir[cpu].gic_irq <= 0) {
 121                pr_warn("cannot bind the irq to cpu%d\n", cpu);
 122                return -EINVAL;
 123        }
 124
 125        irq_data_update_effective_affinity(irq_data, cpumask_of(cpu));
 126
 127        return IRQ_SET_MASK_OK;
 128}
 129
 130static struct irq_chip ls_scfg_msi_parent_chip = {
 131        .name                   = "SCFG",
 132        .irq_compose_msi_msg    = ls_scfg_msi_compose_msg,
 133        .irq_set_affinity       = ls_scfg_msi_set_affinity,
 134};
 135
 136static int ls_scfg_msi_domain_irq_alloc(struct irq_domain *domain,
 137                                        unsigned int virq,
 138                                        unsigned int nr_irqs,
 139                                        void *args)
 140{
 141        msi_alloc_info_t *info = args;
 142        struct ls_scfg_msi *msi_data = domain->host_data;
 143        int pos, err = 0;
 144
 145        WARN_ON(nr_irqs != 1);
 146
 147        spin_lock(&msi_data->lock);
 148        pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num);
 149        if (pos < msi_data->irqs_num)
 150                __set_bit(pos, msi_data->used);
 151        else
 152                err = -ENOSPC;
 153        spin_unlock(&msi_data->lock);
 154
 155        if (err)
 156                return err;
 157
 158        err = iommu_dma_prepare_msi(info->desc, msi_data->msiir_addr);
 159        if (err)
 160                return err;
 161
 162        irq_domain_set_info(domain, virq, pos,
 163                            &ls_scfg_msi_parent_chip, msi_data,
 164                            handle_simple_irq, NULL, NULL);
 165
 166        return 0;
 167}
 168
 169static void ls_scfg_msi_domain_irq_free(struct irq_domain *domain,
 170                                   unsigned int virq, unsigned int nr_irqs)
 171{
 172        struct irq_data *d = irq_domain_get_irq_data(domain, virq);
 173        struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(d);
 174        int pos;
 175
 176        pos = d->hwirq;
 177        if (pos < 0 || pos >= msi_data->irqs_num) {
 178                pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
 179                return;
 180        }
 181
 182        spin_lock(&msi_data->lock);
 183        __clear_bit(pos, msi_data->used);
 184        spin_unlock(&msi_data->lock);
 185}
 186
 187static const struct irq_domain_ops ls_scfg_msi_domain_ops = {
 188        .alloc  = ls_scfg_msi_domain_irq_alloc,
 189        .free   = ls_scfg_msi_domain_irq_free,
 190};
 191
 192static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
 193{
 194        struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
 195        struct ls_scfg_msi *msi_data = msir->msi_data;
 196        unsigned long val;
 197        int pos, size, hwirq;
 198
 199        chained_irq_enter(irq_desc_get_chip(desc), desc);
 200
 201        val = ioread32be(msir->reg);
 202
 203        pos = msir->bit_start;
 204        size = msir->bit_end + 1;
 205
 206        for_each_set_bit_from(pos, &val, size) {
 207                hwirq = ((msir->bit_end - pos) << msi_data->cfg->ibs_shift) |
 208                        msir->srs;
 209                generic_handle_domain_irq(msi_data->parent, hwirq);
 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_bitmap_zalloc(&pdev->dev, msi_data->irqs_num, GFP_KERNEL);
 366        if (!msi_data->used)
 367                return -ENOMEM;
 368        /*
 369         * Reserve all the hwirqs
 370         * The available hwirqs will be released in ls1_msi_setup_hwirq()
 371         */
 372        bitmap_set(msi_data->used, 0, msi_data->irqs_num);
 373
 374        msi_data->msir_num = of_irq_count(pdev->dev.of_node);
 375
 376        if (msi_affinity_flag) {
 377                u32 cpu_num;
 378
 379                cpu_num = num_possible_cpus();
 380                if (msi_data->msir_num >= cpu_num)
 381                        msi_data->msir_num = cpu_num;
 382                else
 383                        msi_affinity_flag = 0;
 384        }
 385
 386        msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
 387                                      sizeof(*msi_data->msir),
 388                                      GFP_KERNEL);
 389        if (!msi_data->msir)
 390                return -ENOMEM;
 391
 392        for (i = 0; i < msi_data->msir_num; i++)
 393                ls_scfg_msi_setup_hwirq(msi_data, i);
 394
 395        ret = ls_scfg_msi_domains_init(msi_data);
 396        if (ret)
 397                return ret;
 398
 399        platform_set_drvdata(pdev, msi_data);
 400
 401        return 0;
 402}
 403
 404static int ls_scfg_msi_remove(struct platform_device *pdev)
 405{
 406        struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
 407        int i;
 408
 409        for (i = 0; i < msi_data->msir_num; i++)
 410                ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
 411
 412        irq_domain_remove(msi_data->msi_domain);
 413        irq_domain_remove(msi_data->parent);
 414
 415        platform_set_drvdata(pdev, NULL);
 416
 417        return 0;
 418}
 419
 420static struct platform_driver ls_scfg_msi_driver = {
 421        .driver = {
 422                .name = "ls-scfg-msi",
 423                .of_match_table = ls_scfg_msi_id,
 424        },
 425        .probe = ls_scfg_msi_probe,
 426        .remove = ls_scfg_msi_remove,
 427};
 428
 429module_platform_driver(ls_scfg_msi_driver);
 430
 431MODULE_AUTHOR("Minghuan Lian <Minghuan.Lian@nxp.com>");
 432MODULE_DESCRIPTION("Freescale Layerscape SCFG MSI controller driver");
 433MODULE_LICENSE("GPL v2");
 434