xref: /openbmc/linux/arch/powerpc/sysdev/fsl_msi.c (revision 28f65c11f2ffb3957259dece647a24f8ad2e241b)
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