134e36c15SJason Jin /* 26820feadSScott Wood * Copyright (C) 2007-2011 Freescale Semiconductor, Inc. 334e36c15SJason Jin * 434e36c15SJason Jin * Author: Tony Li <tony.li@freescale.com> 534e36c15SJason Jin * Jason Jin <Jason.jin@freescale.com> 634e36c15SJason Jin * 734e36c15SJason Jin * The hwirq alloc and free code reuse from sysdev/mpic_msi.c 834e36c15SJason Jin * 934e36c15SJason Jin * This program is free software; you can redistribute it and/or 1034e36c15SJason Jin * modify it under the terms of the GNU General Public License 1134e36c15SJason Jin * as published by the Free Software Foundation; version 2 of the 1234e36c15SJason Jin * License. 1334e36c15SJason Jin * 1434e36c15SJason Jin */ 1534e36c15SJason Jin #include <linux/irq.h> 1634e36c15SJason Jin #include <linux/bootmem.h> 1734e36c15SJason Jin #include <linux/msi.h> 1834e36c15SJason Jin #include <linux/pci.h> 195a0e3ad6STejun Heo #include <linux/slab.h> 2034e36c15SJason Jin #include <linux/of_platform.h> 2134e36c15SJason Jin #include <sysdev/fsl_soc.h> 2234e36c15SJason Jin #include <asm/prom.h> 2334e36c15SJason Jin #include <asm/hw_irq.h> 2434e36c15SJason Jin #include <asm/ppc-pci.h> 2502adac60SLi Yang #include <asm/mpic.h> 2634e36c15SJason Jin #include "fsl_msi.h" 27b8f44ec2SKumar Gala #include "fsl_pci.h" 2834e36c15SJason Jin 29694a7a36SLi Yang LIST_HEAD(msi_head); 30694a7a36SLi Yang 3134e36c15SJason Jin struct fsl_msi_feature { 3234e36c15SJason Jin u32 fsl_pic_ip; 3334e36c15SJason Jin u32 msiir_offset; 3434e36c15SJason Jin }; 3534e36c15SJason Jin 3602adac60SLi Yang struct fsl_msi_cascade_data { 3702adac60SLi Yang struct fsl_msi *msi_data; 3802adac60SLi Yang int index; 3902adac60SLi Yang }; 4034e36c15SJason Jin 4134e36c15SJason Jin static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg) 4234e36c15SJason Jin { 4334e36c15SJason Jin return in_be32(base + (reg >> 2)); 4434e36c15SJason Jin } 4534e36c15SJason Jin 4634e36c15SJason Jin /* 4734e36c15SJason Jin * We do not need this actually. The MSIR register has been read once 4834e36c15SJason Jin * in the cascade interrupt. So, this MSI interrupt has been acked 4934e36c15SJason Jin */ 5037e16615SLennert Buytenhek static void fsl_msi_end_irq(struct irq_data *d) 5134e36c15SJason Jin { 5234e36c15SJason Jin } 5334e36c15SJason Jin 5434e36c15SJason Jin static struct irq_chip fsl_msi_chip = { 551c9db525SThomas Gleixner .irq_mask = mask_msi_irq, 561c9db525SThomas Gleixner .irq_unmask = unmask_msi_irq, 5737e16615SLennert Buytenhek .irq_ack = fsl_msi_end_irq, 58b27df672SThomas Gleixner .name = "FSL-MSI", 5934e36c15SJason Jin }; 6034e36c15SJason Jin 6134e36c15SJason Jin static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, 6234e36c15SJason Jin irq_hw_number_t hw) 6334e36c15SJason Jin { 6480818813SLan Chunhe-B25806 struct fsl_msi *msi_data = h->host_data; 6534e36c15SJason Jin struct irq_chip *chip = &fsl_msi_chip; 6634e36c15SJason Jin 6798488db9SThomas Gleixner irq_set_status_flags(virq, IRQ_TYPE_EDGE_FALLING); 6834e36c15SJason Jin 69ec775d0eSThomas Gleixner irq_set_chip_data(virq, msi_data); 70ec775d0eSThomas Gleixner irq_set_chip_and_handler(virq, chip, handle_edge_irq); 7134e36c15SJason Jin 7234e36c15SJason Jin return 0; 7334e36c15SJason Jin } 7434e36c15SJason Jin 7534e36c15SJason Jin static struct irq_host_ops fsl_msi_host_ops = { 7634e36c15SJason Jin .map = fsl_msi_host_map, 7734e36c15SJason Jin }; 7834e36c15SJason Jin 7934e36c15SJason Jin static int fsl_msi_init_allocator(struct fsl_msi *msi_data) 8034e36c15SJason Jin { 81692d1037SAnton Vorontsov int rc; 8234e36c15SJason Jin 837e7ab367SMichael Ellerman rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS, 847e7ab367SMichael Ellerman msi_data->irqhost->of_node); 8534e36c15SJason Jin if (rc) 8634e36c15SJason Jin return rc; 8734e36c15SJason Jin 887e7ab367SMichael Ellerman rc = msi_bitmap_reserve_dt_hwirqs(&msi_data->bitmap); 897e7ab367SMichael Ellerman if (rc < 0) { 907e7ab367SMichael Ellerman msi_bitmap_free(&msi_data->bitmap); 917e7ab367SMichael Ellerman return rc; 927e7ab367SMichael Ellerman } 937e7ab367SMichael Ellerman 947e7ab367SMichael Ellerman return 0; 9534e36c15SJason Jin } 9634e36c15SJason Jin 9734e36c15SJason Jin static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type) 9834e36c15SJason Jin { 9934e36c15SJason Jin if (type == PCI_CAP_ID_MSIX) 10034e36c15SJason Jin pr_debug("fslmsi: MSI-X untested, trying anyway.\n"); 10134e36c15SJason Jin 10234e36c15SJason Jin return 0; 10334e36c15SJason Jin } 10434e36c15SJason Jin 10534e36c15SJason Jin static void fsl_teardown_msi_irqs(struct pci_dev *pdev) 10634e36c15SJason Jin { 10734e36c15SJason Jin struct msi_desc *entry; 10880818813SLan Chunhe-B25806 struct fsl_msi *msi_data; 10934e36c15SJason Jin 11034e36c15SJason Jin list_for_each_entry(entry, &pdev->msi_list, list) { 11134e36c15SJason Jin if (entry->irq == NO_IRQ) 11234e36c15SJason Jin continue; 113d1921bcdSMilton Miller msi_data = irq_get_chip_data(entry->irq); 114ec775d0eSThomas Gleixner irq_set_msi_desc(entry->irq, NULL); 1157e7ab367SMichael Ellerman msi_bitmap_free_hwirqs(&msi_data->bitmap, 1167e7ab367SMichael Ellerman virq_to_hw(entry->irq), 1); 11734e36c15SJason Jin irq_dispose_mapping(entry->irq); 11834e36c15SJason Jin } 11934e36c15SJason Jin 12034e36c15SJason Jin return; 12134e36c15SJason Jin } 12234e36c15SJason Jin 12334e36c15SJason Jin static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, 12480818813SLan Chunhe-B25806 struct msi_msg *msg, 12580818813SLan Chunhe-B25806 struct fsl_msi *fsl_msi_data) 12634e36c15SJason Jin { 12780818813SLan Chunhe-B25806 struct fsl_msi *msi_data = fsl_msi_data; 1283da34aaeSKumar Gala struct pci_controller *hose = pci_bus_to_host(pdev->bus); 129b8f44ec2SKumar Gala u64 base = fsl_pci_immrbar_base(hose); 13034e36c15SJason Jin 131b8f44ec2SKumar Gala msg->address_lo = msi_data->msi_addr_lo + lower_32_bits(base); 132b8f44ec2SKumar Gala msg->address_hi = msi_data->msi_addr_hi + upper_32_bits(base); 1333da34aaeSKumar Gala 13434e36c15SJason Jin msg->data = hwirq; 13534e36c15SJason Jin 13634e36c15SJason Jin pr_debug("%s: allocated srs: %d, ibs: %d\n", 13734e36c15SJason Jin __func__, hwirq / IRQS_PER_MSI_REG, hwirq % IRQS_PER_MSI_REG); 13834e36c15SJason Jin } 13934e36c15SJason Jin 14034e36c15SJason Jin static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) 14134e36c15SJason Jin { 142694a7a36SLi Yang int rc, hwirq = -ENOMEM; 14334e36c15SJason Jin unsigned int virq; 14434e36c15SJason Jin struct msi_desc *entry; 14534e36c15SJason Jin struct msi_msg msg; 14680818813SLan Chunhe-B25806 struct fsl_msi *msi_data; 14734e36c15SJason Jin 14834e36c15SJason Jin list_for_each_entry(entry, &pdev->msi_list, list) { 149694a7a36SLi Yang list_for_each_entry(msi_data, &msi_head, list) { 1507e7ab367SMichael Ellerman hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); 151694a7a36SLi Yang if (hwirq >= 0) 152694a7a36SLi Yang break; 153694a7a36SLi Yang } 154694a7a36SLi Yang 15534e36c15SJason Jin if (hwirq < 0) { 15634e36c15SJason Jin rc = hwirq; 15734e36c15SJason Jin pr_debug("%s: fail allocating msi interrupt\n", 15834e36c15SJason Jin __func__); 15934e36c15SJason Jin goto out_free; 16034e36c15SJason Jin } 16134e36c15SJason Jin 16234e36c15SJason Jin virq = irq_create_mapping(msi_data->irqhost, hwirq); 16334e36c15SJason Jin 16434e36c15SJason Jin if (virq == NO_IRQ) { 1657e7ab367SMichael Ellerman pr_debug("%s: fail mapping hwirq 0x%x\n", 16634e36c15SJason Jin __func__, hwirq); 1677e7ab367SMichael Ellerman msi_bitmap_free_hwirqs(&msi_data->bitmap, hwirq, 1); 16834e36c15SJason Jin rc = -ENOSPC; 16934e36c15SJason Jin goto out_free; 17034e36c15SJason Jin } 171d1921bcdSMilton Miller /* chip_data is msi_data via host->hostdata in host->map() */ 172ec775d0eSThomas Gleixner irq_set_msi_desc(virq, entry); 17334e36c15SJason Jin 17480818813SLan Chunhe-B25806 fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); 17534e36c15SJason Jin write_msi_msg(virq, &msg); 17634e36c15SJason Jin } 17734e36c15SJason Jin return 0; 17834e36c15SJason Jin 17934e36c15SJason Jin out_free: 180694a7a36SLi Yang /* free by the caller of this function */ 18134e36c15SJason Jin return rc; 18234e36c15SJason Jin } 18334e36c15SJason Jin 184692d1037SAnton Vorontsov static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) 18534e36c15SJason Jin { 186ddaedd1cSThomas Gleixner struct irq_chip *chip = irq_desc_get_chip(desc); 187ddaedd1cSThomas Gleixner struct irq_data *idata = irq_desc_get_irq_data(desc); 18834e36c15SJason Jin unsigned int cascade_irq; 18902adac60SLi Yang struct fsl_msi *msi_data; 19034e36c15SJason Jin int msir_index = -1; 19134e36c15SJason Jin u32 msir_value = 0; 19234e36c15SJason Jin u32 intr_index; 19334e36c15SJason Jin u32 have_shift = 0; 19402adac60SLi Yang struct fsl_msi_cascade_data *cascade_data; 19502adac60SLi Yang 196d1921bcdSMilton Miller cascade_data = irq_get_handler_data(irq); 19702adac60SLi Yang msi_data = cascade_data->msi_data; 19834e36c15SJason Jin 199239007b8SThomas Gleixner raw_spin_lock(&desc->lock); 20034e36c15SJason Jin if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) { 20137e16615SLennert Buytenhek if (chip->irq_mask_ack) 202ddaedd1cSThomas Gleixner chip->irq_mask_ack(idata); 20334e36c15SJason Jin else { 204ddaedd1cSThomas Gleixner chip->irq_mask(idata); 205ddaedd1cSThomas Gleixner chip->irq_ack(idata); 20634e36c15SJason Jin } 20734e36c15SJason Jin } 20834e36c15SJason Jin 209ddaedd1cSThomas Gleixner if (unlikely(irqd_irq_inprogress(idata))) 21034e36c15SJason Jin goto unlock; 21134e36c15SJason Jin 21202adac60SLi Yang msir_index = cascade_data->index; 21334e36c15SJason Jin 21434e36c15SJason Jin if (msir_index >= NR_MSI_REG) 21534e36c15SJason Jin cascade_irq = NO_IRQ; 21634e36c15SJason Jin 217ddaedd1cSThomas Gleixner irqd_set_chained_irq_inprogress(idata); 21880818813SLan Chunhe-B25806 switch (msi_data->feature & FSL_PIC_IP_MASK) { 21934e36c15SJason Jin case FSL_PIC_IP_MPIC: 22034e36c15SJason Jin msir_value = fsl_msi_read(msi_data->msi_regs, 22134e36c15SJason Jin msir_index * 0x10); 22234e36c15SJason Jin break; 22334e36c15SJason Jin case FSL_PIC_IP_IPIC: 22434e36c15SJason Jin msir_value = fsl_msi_read(msi_data->msi_regs, msir_index * 0x4); 22534e36c15SJason Jin break; 22634e36c15SJason Jin } 22734e36c15SJason Jin 22834e36c15SJason Jin while (msir_value) { 22934e36c15SJason Jin intr_index = ffs(msir_value) - 1; 23034e36c15SJason Jin 23134e36c15SJason Jin cascade_irq = irq_linear_revmap(msi_data->irqhost, 232692d1037SAnton Vorontsov msir_index * IRQS_PER_MSI_REG + 233692d1037SAnton Vorontsov intr_index + have_shift); 23434e36c15SJason Jin if (cascade_irq != NO_IRQ) 23534e36c15SJason Jin generic_handle_irq(cascade_irq); 236692d1037SAnton Vorontsov have_shift += intr_index + 1; 237692d1037SAnton Vorontsov msir_value = msir_value >> (intr_index + 1); 23834e36c15SJason Jin } 239ddaedd1cSThomas Gleixner irqd_clr_chained_irq_inprogress(idata); 24034e36c15SJason Jin 24134e36c15SJason Jin switch (msi_data->feature & FSL_PIC_IP_MASK) { 24234e36c15SJason Jin case FSL_PIC_IP_MPIC: 243ddaedd1cSThomas Gleixner chip->irq_eoi(idata); 24434e36c15SJason Jin break; 24534e36c15SJason Jin case FSL_PIC_IP_IPIC: 246ddaedd1cSThomas Gleixner if (!irqd_irq_disabled(idata) && chip->irq_unmask) 247ddaedd1cSThomas Gleixner chip->irq_unmask(idata); 24834e36c15SJason Jin break; 24934e36c15SJason Jin } 25034e36c15SJason Jin unlock: 251239007b8SThomas Gleixner raw_spin_unlock(&desc->lock); 25234e36c15SJason Jin } 25334e36c15SJason Jin 254a454dc50SGrant Likely static int fsl_of_msi_remove(struct platform_device *ofdev) 25548059993SLi Yang { 2566c4c82e2SMilton Miller struct fsl_msi *msi = platform_get_drvdata(ofdev); 25748059993SLi Yang int virq, i; 25848059993SLi Yang struct fsl_msi_cascade_data *cascade_data; 25948059993SLi Yang 26048059993SLi Yang if (msi->list.prev != NULL) 26148059993SLi Yang list_del(&msi->list); 26248059993SLi Yang for (i = 0; i < NR_MSI_REG; i++) { 26348059993SLi Yang virq = msi->msi_virqs[i]; 26448059993SLi Yang if (virq != NO_IRQ) { 265ec775d0eSThomas Gleixner cascade_data = irq_get_handler_data(virq); 26648059993SLi Yang kfree(cascade_data); 26748059993SLi Yang irq_dispose_mapping(virq); 26848059993SLi Yang } 26948059993SLi Yang } 27048059993SLi Yang if (msi->bitmap.bitmap) 27148059993SLi Yang msi_bitmap_free(&msi->bitmap); 27248059993SLi Yang iounmap(msi->msi_regs); 27348059993SLi Yang kfree(msi); 27448059993SLi Yang 27548059993SLi Yang return 0; 27648059993SLi Yang } 27748059993SLi Yang 2786820feadSScott Wood static int __devinit fsl_msi_setup_hwirq(struct fsl_msi *msi, 2796820feadSScott Wood struct platform_device *dev, 2806820feadSScott Wood int offset, int irq_index) 2816820feadSScott Wood { 2826820feadSScott Wood struct fsl_msi_cascade_data *cascade_data = NULL; 2836820feadSScott Wood int virt_msir; 2846820feadSScott Wood 2856820feadSScott Wood virt_msir = irq_of_parse_and_map(dev->dev.of_node, irq_index); 2866820feadSScott Wood if (virt_msir == NO_IRQ) { 2876820feadSScott Wood dev_err(&dev->dev, "%s: Cannot translate IRQ index %d\n", 2886820feadSScott Wood __func__, irq_index); 2896820feadSScott Wood return 0; 2906820feadSScott Wood } 2916820feadSScott Wood 2926820feadSScott Wood cascade_data = kzalloc(sizeof(struct fsl_msi_cascade_data), GFP_KERNEL); 2936820feadSScott Wood if (!cascade_data) { 2946820feadSScott Wood dev_err(&dev->dev, "No memory for MSI cascade data\n"); 2956820feadSScott Wood return -ENOMEM; 2966820feadSScott Wood } 2976820feadSScott Wood 2986820feadSScott Wood msi->msi_virqs[irq_index] = virt_msir; 2996820feadSScott Wood cascade_data->index = offset + irq_index; 3006820feadSScott Wood cascade_data->msi_data = msi; 301ec775d0eSThomas Gleixner irq_set_handler_data(virt_msir, cascade_data); 302ec775d0eSThomas Gleixner irq_set_chained_handler(virt_msir, fsl_msi_cascade); 3036820feadSScott Wood 3046820feadSScott Wood return 0; 3056820feadSScott Wood } 3066820feadSScott Wood 307b1608d69SGrant Likely static const struct of_device_id fsl_of_msi_ids[]; 30800006124SGrant Likely static int __devinit fsl_of_msi_probe(struct platform_device *dev) 30934e36c15SJason Jin { 310b1608d69SGrant Likely const struct of_device_id *match; 31134e36c15SJason Jin struct fsl_msi *msi; 31234e36c15SJason Jin struct resource res; 3136820feadSScott Wood int err, i, j, irq_index, count; 31434e36c15SJason Jin int rc; 31534e36c15SJason Jin const u32 *p; 31600006124SGrant Likely struct fsl_msi_feature *features; 317061ca4adSLi Yang int len; 318061ca4adSLi Yang u32 offset; 3196820feadSScott Wood static const u32 all_avail[] = { 0, NR_MSI_IRQS }; 32034e36c15SJason Jin 321b1608d69SGrant Likely match = of_match_device(fsl_of_msi_ids, &dev->dev); 322b1608d69SGrant Likely if (!match) 32300006124SGrant Likely return -EINVAL; 324b1608d69SGrant Likely features = match->data; 32500006124SGrant Likely 32634e36c15SJason Jin printk(KERN_DEBUG "Setting up Freescale MSI support\n"); 32734e36c15SJason Jin 32834e36c15SJason Jin msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL); 32934e36c15SJason Jin if (!msi) { 33034e36c15SJason Jin dev_err(&dev->dev, "No memory for MSI structure\n"); 33148059993SLi Yang return -ENOMEM; 33234e36c15SJason Jin } 3336c4c82e2SMilton Miller platform_set_drvdata(dev, msi); 33434e36c15SJason Jin 33561c7a080SGrant Likely msi->irqhost = irq_alloc_host(dev->dev.of_node, IRQ_HOST_MAP_LINEAR, 33634e36c15SJason Jin NR_MSI_IRQS, &fsl_msi_host_ops, 0); 337611cd90cSMichael Ellerman 33834e36c15SJason Jin if (msi->irqhost == NULL) { 33934e36c15SJason Jin dev_err(&dev->dev, "No memory for MSI irqhost\n"); 34034e36c15SJason Jin err = -ENOMEM; 34134e36c15SJason Jin goto error_out; 34234e36c15SJason Jin } 34334e36c15SJason Jin 34434e36c15SJason Jin /* Get the MSI reg base */ 34561c7a080SGrant Likely err = of_address_to_resource(dev->dev.of_node, 0, &res); 34634e36c15SJason Jin if (err) { 34734e36c15SJason Jin dev_err(&dev->dev, "%s resource error!\n", 34861c7a080SGrant Likely dev->dev.of_node->full_name); 34934e36c15SJason Jin goto error_out; 35034e36c15SJason Jin } 35134e36c15SJason Jin 352*28f65c11SJoe Perches msi->msi_regs = ioremap(res.start, resource_size(&res)); 35334e36c15SJason Jin if (!msi->msi_regs) { 35434e36c15SJason Jin dev_err(&dev->dev, "ioremap problem failed\n"); 35534e36c15SJason Jin goto error_out; 35634e36c15SJason Jin } 35734e36c15SJason Jin 358692d1037SAnton Vorontsov msi->feature = features->fsl_pic_ip; 35934e36c15SJason Jin 36034e36c15SJason Jin msi->irqhost->host_data = msi; 36134e36c15SJason Jin 36234e36c15SJason Jin msi->msi_addr_hi = 0x0; 3633da34aaeSKumar Gala msi->msi_addr_lo = features->msiir_offset + (res.start & 0xfffff); 36434e36c15SJason Jin 36534e36c15SJason Jin rc = fsl_msi_init_allocator(msi); 36634e36c15SJason Jin if (rc) { 36734e36c15SJason Jin dev_err(&dev->dev, "Error allocating MSI bitmap\n"); 36834e36c15SJason Jin goto error_out; 36934e36c15SJason Jin } 37034e36c15SJason Jin 3716820feadSScott Wood p = of_get_property(dev->dev.of_node, "msi-available-ranges", &len); 3726820feadSScott Wood if (p && len % (2 * sizeof(u32)) != 0) { 3736820feadSScott Wood dev_err(&dev->dev, "%s: Malformed msi-available-ranges property\n", 3746820feadSScott Wood __func__); 37534e36c15SJason Jin err = -EINVAL; 37634e36c15SJason Jin goto error_out; 37734e36c15SJason Jin } 37834e36c15SJason Jin 3796820feadSScott Wood if (!p) 3806820feadSScott Wood p = all_avail; 3816820feadSScott Wood 3826820feadSScott Wood for (irq_index = 0, i = 0; i < len / (2 * sizeof(u32)); i++) { 3836820feadSScott Wood if (p[i * 2] % IRQS_PER_MSI_REG || 3846820feadSScott Wood p[i * 2 + 1] % IRQS_PER_MSI_REG) { 3856820feadSScott Wood printk(KERN_WARNING "%s: %s: msi available range of %u at %u is not IRQ-aligned\n", 3866820feadSScott Wood __func__, dev->dev.of_node->full_name, 3876820feadSScott Wood p[i * 2 + 1], p[i * 2]); 3886820feadSScott Wood err = -EINVAL; 38902adac60SLi Yang goto error_out; 39002adac60SLi Yang } 3916820feadSScott Wood 3926820feadSScott Wood offset = p[i * 2] / IRQS_PER_MSI_REG; 3936820feadSScott Wood count = p[i * 2 + 1] / IRQS_PER_MSI_REG; 3946820feadSScott Wood 3956820feadSScott Wood for (j = 0; j < count; j++, irq_index++) { 3966820feadSScott Wood err = fsl_msi_setup_hwirq(msi, dev, offset, irq_index); 3976820feadSScott Wood if (err) 3986820feadSScott Wood goto error_out; 39934e36c15SJason Jin } 40034e36c15SJason Jin } 40134e36c15SJason Jin 402694a7a36SLi Yang list_add_tail(&msi->list, &msi_head); 40334e36c15SJason Jin 40480818813SLan Chunhe-B25806 /* The multiple setting ppc_md.setup_msi_irqs will not harm things */ 40580818813SLan Chunhe-B25806 if (!ppc_md.setup_msi_irqs) { 40634e36c15SJason Jin ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; 40734e36c15SJason Jin ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs; 40834e36c15SJason Jin ppc_md.msi_check_device = fsl_msi_check_device; 40980818813SLan Chunhe-B25806 } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) { 41080818813SLan Chunhe-B25806 dev_err(&dev->dev, "Different MSI driver already installed!\n"); 41180818813SLan Chunhe-B25806 err = -ENODEV; 41280818813SLan Chunhe-B25806 goto error_out; 41380818813SLan Chunhe-B25806 } 41434e36c15SJason Jin return 0; 41534e36c15SJason Jin error_out: 41648059993SLi Yang fsl_of_msi_remove(dev); 41734e36c15SJason Jin return err; 41834e36c15SJason Jin } 41934e36c15SJason Jin 42034e36c15SJason Jin static const struct fsl_msi_feature mpic_msi_feature = { 42134e36c15SJason Jin .fsl_pic_ip = FSL_PIC_IP_MPIC, 42234e36c15SJason Jin .msiir_offset = 0x140, 42334e36c15SJason Jin }; 42434e36c15SJason Jin 42534e36c15SJason Jin static const struct fsl_msi_feature ipic_msi_feature = { 42634e36c15SJason Jin .fsl_pic_ip = FSL_PIC_IP_IPIC, 42734e36c15SJason Jin .msiir_offset = 0x38, 42834e36c15SJason Jin }; 42934e36c15SJason Jin 43034e36c15SJason Jin static const struct of_device_id fsl_of_msi_ids[] = { 43134e36c15SJason Jin { 43234e36c15SJason Jin .compatible = "fsl,mpic-msi", 43334e36c15SJason Jin .data = (void *)&mpic_msi_feature, 43434e36c15SJason Jin }, 43534e36c15SJason Jin { 43634e36c15SJason Jin .compatible = "fsl,ipic-msi", 43734e36c15SJason Jin .data = (void *)&ipic_msi_feature, 43834e36c15SJason Jin }, 43934e36c15SJason Jin {} 44034e36c15SJason Jin }; 44134e36c15SJason Jin 44200006124SGrant Likely static struct platform_driver fsl_of_msi_driver = { 4434018294bSGrant Likely .driver = { 44434e36c15SJason Jin .name = "fsl-msi", 4454018294bSGrant Likely .owner = THIS_MODULE, 4464018294bSGrant Likely .of_match_table = fsl_of_msi_ids, 4474018294bSGrant Likely }, 44834e36c15SJason Jin .probe = fsl_of_msi_probe, 44948059993SLi Yang .remove = fsl_of_msi_remove, 45034e36c15SJason Jin }; 45134e36c15SJason Jin 45234e36c15SJason Jin static __init int fsl_of_msi_init(void) 45334e36c15SJason Jin { 45400006124SGrant Likely return platform_driver_register(&fsl_of_msi_driver); 45534e36c15SJason Jin } 45634e36c15SJason Jin 45734e36c15SJason Jin subsys_initcall(fsl_of_msi_init); 458