diff options
author | Phil Edworthy <phil.edworthy@renesas.com> | 2014-05-12 06:57:49 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-05-27 22:39:17 -0400 |
commit | 290c1fb35860540223bef1be021944d02232889d (patch) | |
tree | 57b6c974ebdc0da583cca652f4b23d113b7d633c | |
parent | c25da4778803b41e11fd82dd5576c35c09b5f0e0 (diff) |
PCI: rcar: Add MSI support for PCIe
Add MSI support to the R-Car PCIe driver.
Signed-off-by: Phil Edworthy <phil.edworthy@renesas.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
Acked-by: Simon Horman <horms+renesas@verge.net.au>
-rw-r--r-- | drivers/pci/host/pcie-rcar.c | 242 |
1 files changed, 241 insertions, 1 deletions
diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 3c524b9e60e5..8e06124aa80f 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c | |||
@@ -15,8 +15,11 @@ | |||
15 | #include <linux/clk.h> | 15 | #include <linux/clk.h> |
16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | #include <linux/irq.h> | ||
19 | #include <linux/irqdomain.h> | ||
18 | #include <linux/kernel.h> | 20 | #include <linux/kernel.h> |
19 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/msi.h> | ||
20 | #include <linux/of_address.h> | 23 | #include <linux/of_address.h> |
21 | #include <linux/of_irq.h> | 24 | #include <linux/of_irq.h> |
22 | #include <linux/of_pci.h> | 25 | #include <linux/of_pci.h> |
@@ -35,6 +38,7 @@ | |||
35 | #define PCIECDR 0x000020 | 38 | #define PCIECDR 0x000020 |
36 | #define PCIEMSR 0x000028 | 39 | #define PCIEMSR 0x000028 |
37 | #define PCIEINTXR 0x000400 | 40 | #define PCIEINTXR 0x000400 |
41 | #define PCIEMSITXR 0x000840 | ||
38 | 42 | ||
39 | /* Transfer control */ | 43 | /* Transfer control */ |
40 | #define PCIETCTLR 0x02000 | 44 | #define PCIETCTLR 0x02000 |
@@ -43,6 +47,11 @@ | |||
43 | #define DATA_LINK_ACTIVE 1 | 47 | #define DATA_LINK_ACTIVE 1 |
44 | #define PCIEERRFR 0x02020 | 48 | #define PCIEERRFR 0x02020 |
45 | #define UNSUPPORTED_REQUEST (1 << 4) | 49 | #define UNSUPPORTED_REQUEST (1 << 4) |
50 | #define PCIEMSIFR 0x02044 | ||
51 | #define PCIEMSIALR 0x02048 | ||
52 | #define MSIFE 1 | ||
53 | #define PCIEMSIAUR 0x0204c | ||
54 | #define PCIEMSIIER 0x02050 | ||
46 | 55 | ||
47 | /* root port address */ | 56 | /* root port address */ |
48 | #define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) | 57 | #define PCIEPRAR(x) (0x02080 + ((x) * 0x4)) |
@@ -85,6 +94,8 @@ | |||
85 | #define H1_PCIEPHYDOUTR 0x040014 | 94 | #define H1_PCIEPHYDOUTR 0x040014 |
86 | #define H1_PCIEPHYSR 0x040018 | 95 | #define H1_PCIEPHYSR 0x040018 |
87 | 96 | ||
97 | #define INT_PCI_MSI_NR 32 | ||
98 | |||
88 | #define RCONF(x) (PCICONF(0)+(x)) | 99 | #define RCONF(x) (PCICONF(0)+(x)) |
89 | #define RPMCAP(x) (PMCAP(0)+(x)) | 100 | #define RPMCAP(x) (PMCAP(0)+(x)) |
90 | #define REXPCAP(x) (EXPCAP(0)+(x)) | 101 | #define REXPCAP(x) (EXPCAP(0)+(x)) |
@@ -97,6 +108,21 @@ | |||
97 | #define PCI_MAX_RESOURCES 4 | 108 | #define PCI_MAX_RESOURCES 4 |
98 | #define MAX_NR_INBOUND_MAPS 6 | 109 | #define MAX_NR_INBOUND_MAPS 6 |
99 | 110 | ||
111 | struct rcar_msi { | ||
112 | DECLARE_BITMAP(used, INT_PCI_MSI_NR); | ||
113 | struct irq_domain *domain; | ||
114 | struct msi_chip chip; | ||
115 | unsigned long pages; | ||
116 | struct mutex lock; | ||
117 | int irq1; | ||
118 | int irq2; | ||
119 | }; | ||
120 | |||
121 | static inline struct rcar_msi *to_rcar_msi(struct msi_chip *chip) | ||
122 | { | ||
123 | return container_of(chip, struct rcar_msi, chip); | ||
124 | } | ||
125 | |||
100 | /* Structure representing the PCIe interface */ | 126 | /* Structure representing the PCIe interface */ |
101 | struct rcar_pcie { | 127 | struct rcar_pcie { |
102 | struct device *dev; | 128 | struct device *dev; |
@@ -106,6 +132,7 @@ struct rcar_pcie { | |||
106 | int root_bus_nr; | 132 | int root_bus_nr; |
107 | struct clk *clk; | 133 | struct clk *clk; |
108 | struct clk *bus_clk; | 134 | struct clk *bus_clk; |
135 | struct rcar_msi msi; | ||
109 | }; | 136 | }; |
110 | 137 | ||
111 | static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) | 138 | static inline struct rcar_pcie *sys_to_pcie(struct pci_sys_data *sys) |
@@ -356,10 +383,20 @@ static int rcar_pcie_setup(int nr, struct pci_sys_data *sys) | |||
356 | return 1; | 383 | return 1; |
357 | } | 384 | } |
358 | 385 | ||
386 | static void rcar_pcie_add_bus(struct pci_bus *bus) | ||
387 | { | ||
388 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | ||
389 | struct rcar_pcie *pcie = sys_to_pcie(bus->sysdata); | ||
390 | |||
391 | bus->msi = &pcie->msi.chip; | ||
392 | } | ||
393 | } | ||
394 | |||
359 | struct hw_pci rcar_pci = { | 395 | struct hw_pci rcar_pci = { |
360 | .setup = rcar_pcie_setup, | 396 | .setup = rcar_pcie_setup, |
361 | .map_irq = of_irq_parse_and_map_pci, | 397 | .map_irq = of_irq_parse_and_map_pci, |
362 | .ops = &rcar_pcie_ops, | 398 | .ops = &rcar_pcie_ops, |
399 | .add_bus = rcar_pcie_add_bus, | ||
363 | }; | 400 | }; |
364 | 401 | ||
365 | static void rcar_pcie_enable(struct rcar_pcie *pcie) | 402 | static void rcar_pcie_enable(struct rcar_pcie *pcie) |
@@ -477,6 +514,10 @@ static int rcar_pcie_hw_init(struct rcar_pcie *pcie) | |||
477 | /* Enable MAC data scrambling. */ | 514 | /* Enable MAC data scrambling. */ |
478 | rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); | 515 | rcar_rmw32(pcie, MACCTLR, SCRAMBLE_DISABLE, 0); |
479 | 516 | ||
517 | /* Enable MSI */ | ||
518 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
519 | pci_write_reg(pcie, 0x101f0000, PCIEMSITXR); | ||
520 | |||
480 | /* Finish initialization - establish a PCI Express link */ | 521 | /* Finish initialization - establish a PCI Express link */ |
481 | pci_write_reg(pcie, CFINIT, PCIETCTLR); | 522 | pci_write_reg(pcie, CFINIT, PCIETCTLR); |
482 | 523 | ||
@@ -530,11 +571,184 @@ static int rcar_pcie_hw_init_h1(struct rcar_pcie *pcie) | |||
530 | return -ETIMEDOUT; | 571 | return -ETIMEDOUT; |
531 | } | 572 | } |
532 | 573 | ||
574 | static int rcar_msi_alloc(struct rcar_msi *chip) | ||
575 | { | ||
576 | int msi; | ||
577 | |||
578 | mutex_lock(&chip->lock); | ||
579 | |||
580 | msi = find_first_zero_bit(chip->used, INT_PCI_MSI_NR); | ||
581 | if (msi < INT_PCI_MSI_NR) | ||
582 | set_bit(msi, chip->used); | ||
583 | else | ||
584 | msi = -ENOSPC; | ||
585 | |||
586 | mutex_unlock(&chip->lock); | ||
587 | |||
588 | return msi; | ||
589 | } | ||
590 | |||
591 | static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq) | ||
592 | { | ||
593 | mutex_lock(&chip->lock); | ||
594 | clear_bit(irq, chip->used); | ||
595 | mutex_unlock(&chip->lock); | ||
596 | } | ||
597 | |||
598 | static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) | ||
599 | { | ||
600 | struct rcar_pcie *pcie = data; | ||
601 | struct rcar_msi *msi = &pcie->msi; | ||
602 | unsigned long reg; | ||
603 | |||
604 | reg = pci_read_reg(pcie, PCIEMSIFR); | ||
605 | |||
606 | /* MSI & INTx share an interrupt - we only handle MSI here */ | ||
607 | if (!reg) | ||
608 | return IRQ_NONE; | ||
609 | |||
610 | while (reg) { | ||
611 | unsigned int index = find_first_bit(®, 32); | ||
612 | unsigned int irq; | ||
613 | |||
614 | /* clear the interrupt */ | ||
615 | pci_write_reg(pcie, 1 << index, PCIEMSIFR); | ||
616 | |||
617 | irq = irq_find_mapping(msi->domain, index); | ||
618 | if (irq) { | ||
619 | if (test_bit(index, msi->used)) | ||
620 | generic_handle_irq(irq); | ||
621 | else | ||
622 | dev_info(pcie->dev, "unhandled MSI\n"); | ||
623 | } else { | ||
624 | /* Unknown MSI, just clear it */ | ||
625 | dev_dbg(pcie->dev, "unexpected MSI\n"); | ||
626 | } | ||
627 | |||
628 | /* see if there's any more pending in this vector */ | ||
629 | reg = pci_read_reg(pcie, PCIEMSIFR); | ||
630 | } | ||
631 | |||
632 | return IRQ_HANDLED; | ||
633 | } | ||
634 | |||
635 | static int rcar_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, | ||
636 | struct msi_desc *desc) | ||
637 | { | ||
638 | struct rcar_msi *msi = to_rcar_msi(chip); | ||
639 | struct rcar_pcie *pcie = container_of(chip, struct rcar_pcie, msi.chip); | ||
640 | struct msi_msg msg; | ||
641 | unsigned int irq; | ||
642 | int hwirq; | ||
643 | |||
644 | hwirq = rcar_msi_alloc(msi); | ||
645 | if (hwirq < 0) | ||
646 | return hwirq; | ||
647 | |||
648 | irq = irq_create_mapping(msi->domain, hwirq); | ||
649 | if (!irq) { | ||
650 | rcar_msi_free(msi, hwirq); | ||
651 | return -EINVAL; | ||
652 | } | ||
653 | |||
654 | irq_set_msi_desc(irq, desc); | ||
655 | |||
656 | msg.address_lo = pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE; | ||
657 | msg.address_hi = pci_read_reg(pcie, PCIEMSIAUR); | ||
658 | msg.data = hwirq; | ||
659 | |||
660 | write_msi_msg(irq, &msg); | ||
661 | |||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | static void rcar_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) | ||
666 | { | ||
667 | struct rcar_msi *msi = to_rcar_msi(chip); | ||
668 | struct irq_data *d = irq_get_irq_data(irq); | ||
669 | |||
670 | rcar_msi_free(msi, d->hwirq); | ||
671 | } | ||
672 | |||
673 | static struct irq_chip rcar_msi_irq_chip = { | ||
674 | .name = "R-Car PCIe MSI", | ||
675 | .irq_enable = unmask_msi_irq, | ||
676 | .irq_disable = mask_msi_irq, | ||
677 | .irq_mask = mask_msi_irq, | ||
678 | .irq_unmask = unmask_msi_irq, | ||
679 | }; | ||
680 | |||
681 | static int rcar_msi_map(struct irq_domain *domain, unsigned int irq, | ||
682 | irq_hw_number_t hwirq) | ||
683 | { | ||
684 | irq_set_chip_and_handler(irq, &rcar_msi_irq_chip, handle_simple_irq); | ||
685 | irq_set_chip_data(irq, domain->host_data); | ||
686 | set_irq_flags(irq, IRQF_VALID); | ||
687 | |||
688 | return 0; | ||
689 | } | ||
690 | |||
691 | static const struct irq_domain_ops msi_domain_ops = { | ||
692 | .map = rcar_msi_map, | ||
693 | }; | ||
694 | |||
695 | static int rcar_pcie_enable_msi(struct rcar_pcie *pcie) | ||
696 | { | ||
697 | struct platform_device *pdev = to_platform_device(pcie->dev); | ||
698 | struct rcar_msi *msi = &pcie->msi; | ||
699 | unsigned long base; | ||
700 | int err; | ||
701 | |||
702 | mutex_init(&msi->lock); | ||
703 | |||
704 | msi->chip.dev = pcie->dev; | ||
705 | msi->chip.setup_irq = rcar_msi_setup_irq; | ||
706 | msi->chip.teardown_irq = rcar_msi_teardown_irq; | ||
707 | |||
708 | msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR, | ||
709 | &msi_domain_ops, &msi->chip); | ||
710 | if (!msi->domain) { | ||
711 | dev_err(&pdev->dev, "failed to create IRQ domain\n"); | ||
712 | return -ENOMEM; | ||
713 | } | ||
714 | |||
715 | /* Two irqs are for MSI, but they are also used for non-MSI irqs */ | ||
716 | err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq, | ||
717 | IRQF_SHARED, rcar_msi_irq_chip.name, pcie); | ||
718 | if (err < 0) { | ||
719 | dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); | ||
720 | goto err; | ||
721 | } | ||
722 | |||
723 | err = devm_request_irq(&pdev->dev, msi->irq2, rcar_pcie_msi_irq, | ||
724 | IRQF_SHARED, rcar_msi_irq_chip.name, pcie); | ||
725 | if (err < 0) { | ||
726 | dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); | ||
727 | goto err; | ||
728 | } | ||
729 | |||
730 | /* setup MSI data target */ | ||
731 | msi->pages = __get_free_pages(GFP_KERNEL, 0); | ||
732 | base = virt_to_phys((void *)msi->pages); | ||
733 | |||
734 | pci_write_reg(pcie, base | MSIFE, PCIEMSIALR); | ||
735 | pci_write_reg(pcie, 0, PCIEMSIAUR); | ||
736 | |||
737 | /* enable all MSI interrupts */ | ||
738 | pci_write_reg(pcie, 0xffffffff, PCIEMSIIER); | ||
739 | |||
740 | return 0; | ||
741 | |||
742 | err: | ||
743 | irq_domain_remove(msi->domain); | ||
744 | return err; | ||
745 | } | ||
746 | |||
533 | static int rcar_pcie_get_resources(struct platform_device *pdev, | 747 | static int rcar_pcie_get_resources(struct platform_device *pdev, |
534 | struct rcar_pcie *pcie) | 748 | struct rcar_pcie *pcie) |
535 | { | 749 | { |
536 | struct resource res; | 750 | struct resource res; |
537 | int err; | 751 | int err, i; |
538 | 752 | ||
539 | err = of_address_to_resource(pdev->dev.of_node, 0, &res); | 753 | err = of_address_to_resource(pdev->dev.of_node, 0, &res); |
540 | if (err) | 754 | if (err) |
@@ -559,6 +773,22 @@ static int rcar_pcie_get_resources(struct platform_device *pdev, | |||
559 | if (err) | 773 | if (err) |
560 | goto err_map_reg; | 774 | goto err_map_reg; |
561 | 775 | ||
776 | i = irq_of_parse_and_map(pdev->dev.of_node, 0); | ||
777 | if (i < 0) { | ||
778 | dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); | ||
779 | err = -ENOENT; | ||
780 | goto err_map_reg; | ||
781 | } | ||
782 | pcie->msi.irq1 = i; | ||
783 | |||
784 | i = irq_of_parse_and_map(pdev->dev.of_node, 1); | ||
785 | if (i < 0) { | ||
786 | dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); | ||
787 | err = -ENOENT; | ||
788 | goto err_map_reg; | ||
789 | } | ||
790 | pcie->msi.irq2 = i; | ||
791 | |||
562 | pcie->base = devm_ioremap_resource(&pdev->dev, &res); | 792 | pcie->base = devm_ioremap_resource(&pdev->dev, &res); |
563 | if (IS_ERR(pcie->base)) { | 793 | if (IS_ERR(pcie->base)) { |
564 | err = PTR_ERR(pcie->base); | 794 | err = PTR_ERR(pcie->base); |
@@ -732,6 +962,16 @@ static int rcar_pcie_probe(struct platform_device *pdev) | |||
732 | if (err) | 962 | if (err) |
733 | return err; | 963 | return err; |
734 | 964 | ||
965 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | ||
966 | err = rcar_pcie_enable_msi(pcie); | ||
967 | if (err < 0) { | ||
968 | dev_err(&pdev->dev, | ||
969 | "failed to enable MSI support: %d\n", | ||
970 | err); | ||
971 | return err; | ||
972 | } | ||
973 | } | ||
974 | |||
735 | of_id = of_match_device(rcar_pcie_of_match, pcie->dev); | 975 | of_id = of_match_device(rcar_pcie_of_match, pcie->dev); |
736 | if (!of_id || !of_id->data) | 976 | if (!of_id || !of_id->data) |
737 | return -EINVAL; | 977 | return -EINVAL; |