Loading MAINTAINERS +6 −0 Original line number Diff line number Diff line Loading @@ -6346,6 +6346,12 @@ S: Supported F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt F: drivers/pci/host/pci-tegra.c PCI DRIVER FOR SAMSUNG EXYNOS M: Jingoo Han <jg1.han@samsung.com> L: linux-pci@vger.kernel.org S: Maintained F: drivers/pci/host/pci-exynos.c PCMCIA SUBSYSTEM P: Linux PCMCIA Team L: linux-pcmcia@lists.infradead.org Loading drivers/pci/host/pci-exynos.c +112 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ struct exynos_pcie { #define PCIE_IRQ_SPECIAL 0x008 #define PCIE_IRQ_EN_PULSE 0x00c #define PCIE_IRQ_EN_LEVEL 0x010 #define IRQ_MSI_ENABLE (0x1 << 2) #define PCIE_IRQ_EN_SPECIAL 0x014 #define PCIE_PWR_RESET 0x018 #define PCIE_CORE_RESET 0x01c Loading Loading @@ -77,18 +78,28 @@ struct exynos_pcie { #define PCIE_PHY_PLL_BIAS 0x00c #define PCIE_PHY_DCC_FEEDBACK 0x014 #define PCIE_PHY_PLL_DIV_1 0x05c #define PCIE_PHY_COMMON_POWER 0x064 #define PCIE_PHY_COMMON_PD_CMN (0x1 << 3) #define PCIE_PHY_TRSV0_EMP_LVL 0x084 #define PCIE_PHY_TRSV0_DRV_LVL 0x088 #define PCIE_PHY_TRSV0_RXCDR 0x0ac #define PCIE_PHY_TRSV0_POWER 0x0c4 #define PCIE_PHY_TRSV0_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV0_LVCC 0x0dc #define PCIE_PHY_TRSV1_EMP_LVL 0x144 #define PCIE_PHY_TRSV1_RXCDR 0x16c #define PCIE_PHY_TRSV1_POWER 0x184 #define PCIE_PHY_TRSV1_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV1_LVCC 0x19c #define PCIE_PHY_TRSV2_EMP_LVL 0x204 #define PCIE_PHY_TRSV2_RXCDR 0x22c #define PCIE_PHY_TRSV2_POWER 0x244 #define PCIE_PHY_TRSV2_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV2_LVCC 0x25c #define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 #define PCIE_PHY_TRSV3_RXCDR 0x2ec #define PCIE_PHY_TRSV3_POWER 0x304 #define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV3_LVCC 0x31c static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg) Loading Loading @@ -202,6 +213,58 @@ static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp) exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET); } static void exynos_pcie_power_on_phy(struct pcie_port *pp) { u32 val; struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); val &= ~PCIE_PHY_COMMON_PD_CMN; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); val &= ~PCIE_PHY_TRSV0_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); val &= ~PCIE_PHY_TRSV1_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); val &= ~PCIE_PHY_TRSV2_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); val &= ~PCIE_PHY_TRSV3_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); } static void exynos_pcie_power_off_phy(struct pcie_port *pp) { u32 val; struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); val |= PCIE_PHY_COMMON_PD_CMN; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); val |= PCIE_PHY_TRSV0_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); val |= PCIE_PHY_TRSV1_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); val |= PCIE_PHY_TRSV2_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); val |= PCIE_PHY_TRSV3_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); } static void exynos_pcie_init_phy(struct pcie_port *pp) { struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); Loading Loading @@ -270,6 +333,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) /* de-assert phy reset */ exynos_pcie_deassert_phy_reset(pp); /* power on phy */ exynos_pcie_power_on_phy(pp); /* initialize phy */ exynos_pcie_init_phy(pp); Loading Loading @@ -302,6 +368,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) PCIE_PHY_PLL_LOCKED); dev_info(pp->dev, "PLL Locked: 0x%x\n", val); } /* power off phy */ exynos_pcie_power_off_phy(pp); dev_err(pp->dev, "PCIe Link Fail\n"); return -EINVAL; } Loading Loading @@ -342,9 +411,36 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) { struct pcie_port *pp = arg; dw_handle_msi_irq(pp); return IRQ_HANDLED; } static void exynos_pcie_msi_init(struct pcie_port *pp) { u32 val; struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); dw_pcie_msi_init(pp); /* enable MSI interrupt */ val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL); val |= IRQ_MSI_ENABLE; exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL); return; } static void exynos_pcie_enable_interrupts(struct pcie_port *pp) { exynos_pcie_enable_irq_pulse(pp); if (IS_ENABLED(CONFIG_PCI_MSI)) exynos_pcie_msi_init(pp); return; } Loading Loading @@ -430,6 +526,22 @@ static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) return ret; } if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq(pdev, 0); if (!pp->msi_irq) { dev_err(&pdev->dev, "failed to get msi irq\n"); return -ENODEV; } ret = devm_request_irq(&pdev->dev, pp->msi_irq, exynos_pcie_msi_irq_handler, IRQF_SHARED, "exynos-pcie", pp); if (ret) { dev_err(&pdev->dev, "failed to request msi irq\n"); return ret; } } pp->root_bus_nr = -1; pp->ops = &exynos_pcie_host_ops; Loading drivers/pci/host/pcie-designware.c +240 −0 Original line number Diff line number Diff line Loading @@ -11,8 +11,11 @@ * published by the Free Software Foundation. */ #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/msi.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/pci_regs.h> Loading Loading @@ -142,6 +145,204 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return ret; } static struct irq_chip dw_msi_irq_chip = { .name = "PCI-MSI", .irq_enable = unmask_msi_irq, .irq_disable = mask_msi_irq, .irq_mask = mask_msi_irq, .irq_unmask = unmask_msi_irq, }; /* MSI int handler */ void dw_handle_msi_irq(struct pcie_port *pp) { unsigned long val; int i, pos; for (i = 0; i < MAX_MSI_CTRLS; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, (u32 *)&val); if (val) { pos = 0; while ((pos = find_next_bit(&val, 32, pos)) != 32) { generic_handle_irq(pp->msi_irq_start + (i * 32) + pos); pos++; } } dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); } } void dw_pcie_msi_init(struct pcie_port *pp) { pp->msi_data = __get_free_pages(GFP_KERNEL, 0); /* program the msi_data */ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, virt_to_phys((void *)pp->msi_data)); dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); } static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) { int flag = 1; do { pos = find_next_zero_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos); /*if you have reached to the end then get out from here.*/ if (pos == MAX_MSI_IRQS) return -ENOSPC; /* * Check if this position is at correct offset.nvec is always a * power of two. pos0 must be nvec bit alligned. */ if (pos % msgvec) pos += msgvec - (pos % msgvec); else flag = 0; } while (flag); *pos0 = pos; return 0; } static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int res, bit, irq, pos0, pos1, i; u32 val; struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); if (!pp) { BUG(); return -EINVAL; } pos0 = find_first_zero_bit(pp->msi_irq_in_use, MAX_MSI_IRQS); if (pos0 % no_irqs) { if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) goto no_valid_irq; } if (no_irqs > 1) { pos1 = find_next_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos0); /* there must be nvec number of consecutive free bits */ while ((pos1 - pos0) < no_irqs) { if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) goto no_valid_irq; pos1 = find_next_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos0); } } irq = (pp->msi_irq_start + pos0); if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1)) goto no_valid_irq; i = 0; while (i < no_irqs) { set_bit(pos0 + i, pp->msi_irq_in_use); irq_alloc_descs((irq + i), (irq + i), 1, 0); irq_set_msi_desc(irq + i, desc); /*Enable corresponding interrupt in MSI interrupt controller */ res = ((pos0 + i) / 32) * 12; bit = (pos0 + i) % 32; dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); i++; } *pos = pos0; return irq; no_valid_irq: *pos = pos0; return -ENOSPC; } static void clear_irq(unsigned int irq) { int res, bit, val, pos; struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; /* get the port structure */ desc = irq_to_desc(irq); msi = irq_desc_get_msi_desc(desc); pp = sys_to_pcie(msi->dev->bus->sysdata); if (!pp) { BUG(); return; } pos = irq - pp->msi_irq_start; irq_free_desc(irq); clear_bit(pos, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI interrupt controller */ res = (pos / 32) * 12; bit = pos % 32; dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); } static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, struct msi_desc *desc) { int irq, pos, msgvec; u16 msg_ctr; struct msi_msg msg; struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); if (!pp) { BUG(); return -EINVAL; } pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, &msg_ctr); msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; if (msgvec == 0) msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; if (msgvec > 5) msgvec = 0; irq = assign_irq((1 << msgvec), desc, &pos); if (irq < 0) return irq; msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; msg_ctr |= msgvec << 4; pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, msg_ctr); desc->msi_attrib.multiple = msgvec; msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; msg.data = pos; write_msi_msg(irq, &msg); return 0; } static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) { clear_irq(irq); } static struct msi_chip dw_pcie_msi_chip = { .setup_irq = dw_msi_setup_irq, .teardown_irq = dw_msi_teardown_irq, }; int dw_pcie_link_up(struct pcie_port *pp) { if (pp->ops->link_up) Loading @@ -150,6 +351,20 @@ int dw_pcie_link_up(struct pcie_port *pp) return 0; } static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); set_irq_flags(irq, IRQF_VALID); return 0; } static const struct irq_domain_ops msi_domain_ops = { .map = dw_pcie_msi_map, }; int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; Loading @@ -157,6 +372,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range_parser parser; u32 val; struct irq_domain *irq_domain; if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); return -EINVAL; Loading Loading @@ -223,6 +440,18 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } if (IS_ENABLED(CONFIG_PCI_MSI)) { irq_domain = irq_domain_add_linear(pp->dev->of_node, MAX_MSI_IRQS, &msi_domain_ops, &dw_pcie_msi_chip); if (!irq_domain) { dev_err(pp->dev, "irq domain init failed\n"); return -ENXIO; } pp->msi_irq_start = irq_find_mapping(irq_domain, 0); } if (pp->ops->host_init) pp->ops->host_init(pp); Loading Loading @@ -485,10 +714,21 @@ int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return pp->irq; } static void dw_pcie_add_bus(struct pci_bus *bus) { if (IS_ENABLED(CONFIG_PCI_MSI)) { struct pcie_port *pp = sys_to_pcie(bus->sysdata); dw_pcie_msi_chip.dev = pp->dev; bus->msi = &dw_pcie_msi_chip; } } static struct hw_pci dw_pci = { .setup = dw_pcie_setup, .scan = dw_pcie_scan_bus, .map_irq = dw_pcie_map_irq, .add_bus = dw_pcie_add_bus, }; void dw_pcie_setup_rc(struct pcie_port *pp) Loading drivers/pci/host/pcie-designware.h +14 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,14 @@ struct pcie_port_info { phys_addr_t mem_bus_addr; }; /* * Maximum number of MSI IRQs can be 256 per controller. But keep * it 32 as of now. Probably we will never need more than 32. If needed, * then increment it in multiple of 32. */ #define MAX_MSI_IRQS 32 #define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) struct pcie_port { struct device *dev; u8 root_bus_nr; Loading @@ -38,6 +46,10 @@ struct pcie_port { int irq; u32 lanes; struct pcie_host_ops *ops; int msi_irq; int msi_irq_start; unsigned long msi_data; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; struct pcie_host_ops { Loading @@ -57,6 +69,8 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val); int cfg_write(void __iomem *addr, int where, int size, u32 val); int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); void dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); Loading Loading
MAINTAINERS +6 −0 Original line number Diff line number Diff line Loading @@ -6346,6 +6346,12 @@ S: Supported F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt F: drivers/pci/host/pci-tegra.c PCI DRIVER FOR SAMSUNG EXYNOS M: Jingoo Han <jg1.han@samsung.com> L: linux-pci@vger.kernel.org S: Maintained F: drivers/pci/host/pci-exynos.c PCMCIA SUBSYSTEM P: Linux PCMCIA Team L: linux-pcmcia@lists.infradead.org Loading
drivers/pci/host/pci-exynos.c +112 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,7 @@ struct exynos_pcie { #define PCIE_IRQ_SPECIAL 0x008 #define PCIE_IRQ_EN_PULSE 0x00c #define PCIE_IRQ_EN_LEVEL 0x010 #define IRQ_MSI_ENABLE (0x1 << 2) #define PCIE_IRQ_EN_SPECIAL 0x014 #define PCIE_PWR_RESET 0x018 #define PCIE_CORE_RESET 0x01c Loading Loading @@ -77,18 +78,28 @@ struct exynos_pcie { #define PCIE_PHY_PLL_BIAS 0x00c #define PCIE_PHY_DCC_FEEDBACK 0x014 #define PCIE_PHY_PLL_DIV_1 0x05c #define PCIE_PHY_COMMON_POWER 0x064 #define PCIE_PHY_COMMON_PD_CMN (0x1 << 3) #define PCIE_PHY_TRSV0_EMP_LVL 0x084 #define PCIE_PHY_TRSV0_DRV_LVL 0x088 #define PCIE_PHY_TRSV0_RXCDR 0x0ac #define PCIE_PHY_TRSV0_POWER 0x0c4 #define PCIE_PHY_TRSV0_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV0_LVCC 0x0dc #define PCIE_PHY_TRSV1_EMP_LVL 0x144 #define PCIE_PHY_TRSV1_RXCDR 0x16c #define PCIE_PHY_TRSV1_POWER 0x184 #define PCIE_PHY_TRSV1_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV1_LVCC 0x19c #define PCIE_PHY_TRSV2_EMP_LVL 0x204 #define PCIE_PHY_TRSV2_RXCDR 0x22c #define PCIE_PHY_TRSV2_POWER 0x244 #define PCIE_PHY_TRSV2_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV2_LVCC 0x25c #define PCIE_PHY_TRSV3_EMP_LVL 0x2c4 #define PCIE_PHY_TRSV3_RXCDR 0x2ec #define PCIE_PHY_TRSV3_POWER 0x304 #define PCIE_PHY_TRSV3_PD_TSV (0x1 << 7) #define PCIE_PHY_TRSV3_LVCC 0x31c static inline void exynos_elb_writel(struct exynos_pcie *pcie, u32 val, u32 reg) Loading Loading @@ -202,6 +213,58 @@ static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp) exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET); } static void exynos_pcie_power_on_phy(struct pcie_port *pp) { u32 val; struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); val &= ~PCIE_PHY_COMMON_PD_CMN; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); val &= ~PCIE_PHY_TRSV0_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); val &= ~PCIE_PHY_TRSV1_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); val &= ~PCIE_PHY_TRSV2_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); val &= ~PCIE_PHY_TRSV3_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); } static void exynos_pcie_power_off_phy(struct pcie_port *pp) { u32 val; struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_COMMON_POWER); val |= PCIE_PHY_COMMON_PD_CMN; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_COMMON_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV0_POWER); val |= PCIE_PHY_TRSV0_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV0_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV1_POWER); val |= PCIE_PHY_TRSV1_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV1_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV2_POWER); val |= PCIE_PHY_TRSV2_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV2_POWER); val = exynos_phy_readl(exynos_pcie, PCIE_PHY_TRSV3_POWER); val |= PCIE_PHY_TRSV3_PD_TSV; exynos_phy_writel(exynos_pcie, val, PCIE_PHY_TRSV3_POWER); } static void exynos_pcie_init_phy(struct pcie_port *pp) { struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); Loading Loading @@ -270,6 +333,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) /* de-assert phy reset */ exynos_pcie_deassert_phy_reset(pp); /* power on phy */ exynos_pcie_power_on_phy(pp); /* initialize phy */ exynos_pcie_init_phy(pp); Loading Loading @@ -302,6 +368,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp) PCIE_PHY_PLL_LOCKED); dev_info(pp->dev, "PLL Locked: 0x%x\n", val); } /* power off phy */ exynos_pcie_power_off_phy(pp); dev_err(pp->dev, "PCIe Link Fail\n"); return -EINVAL; } Loading Loading @@ -342,9 +411,36 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) return IRQ_HANDLED; } static irqreturn_t exynos_pcie_msi_irq_handler(int irq, void *arg) { struct pcie_port *pp = arg; dw_handle_msi_irq(pp); return IRQ_HANDLED; } static void exynos_pcie_msi_init(struct pcie_port *pp) { u32 val; struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp); dw_pcie_msi_init(pp); /* enable MSI interrupt */ val = exynos_elb_readl(exynos_pcie, PCIE_IRQ_EN_LEVEL); val |= IRQ_MSI_ENABLE; exynos_elb_writel(exynos_pcie, val, PCIE_IRQ_EN_LEVEL); return; } static void exynos_pcie_enable_interrupts(struct pcie_port *pp) { exynos_pcie_enable_irq_pulse(pp); if (IS_ENABLED(CONFIG_PCI_MSI)) exynos_pcie_msi_init(pp); return; } Loading Loading @@ -430,6 +526,22 @@ static int add_pcie_port(struct pcie_port *pp, struct platform_device *pdev) return ret; } if (IS_ENABLED(CONFIG_PCI_MSI)) { pp->msi_irq = platform_get_irq(pdev, 0); if (!pp->msi_irq) { dev_err(&pdev->dev, "failed to get msi irq\n"); return -ENODEV; } ret = devm_request_irq(&pdev->dev, pp->msi_irq, exynos_pcie_msi_irq_handler, IRQF_SHARED, "exynos-pcie", pp); if (ret) { dev_err(&pdev->dev, "failed to request msi irq\n"); return ret; } } pp->root_bus_nr = -1; pp->ops = &exynos_pcie_host_ops; Loading
drivers/pci/host/pcie-designware.c +240 −0 Original line number Diff line number Diff line Loading @@ -11,8 +11,11 @@ * published by the Free Software Foundation. */ #include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/msi.h> #include <linux/of_address.h> #include <linux/pci.h> #include <linux/pci_regs.h> Loading Loading @@ -142,6 +145,204 @@ int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, return ret; } static struct irq_chip dw_msi_irq_chip = { .name = "PCI-MSI", .irq_enable = unmask_msi_irq, .irq_disable = mask_msi_irq, .irq_mask = mask_msi_irq, .irq_unmask = unmask_msi_irq, }; /* MSI int handler */ void dw_handle_msi_irq(struct pcie_port *pp) { unsigned long val; int i, pos; for (i = 0; i < MAX_MSI_CTRLS; i++) { dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, (u32 *)&val); if (val) { pos = 0; while ((pos = find_next_bit(&val, 32, pos)) != 32) { generic_handle_irq(pp->msi_irq_start + (i * 32) + pos); pos++; } } dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4, val); } } void dw_pcie_msi_init(struct pcie_port *pp) { pp->msi_data = __get_free_pages(GFP_KERNEL, 0); /* program the msi_data */ dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4, virt_to_phys((void *)pp->msi_data)); dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4, 0); } static int find_valid_pos0(struct pcie_port *pp, int msgvec, int pos, int *pos0) { int flag = 1; do { pos = find_next_zero_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos); /*if you have reached to the end then get out from here.*/ if (pos == MAX_MSI_IRQS) return -ENOSPC; /* * Check if this position is at correct offset.nvec is always a * power of two. pos0 must be nvec bit alligned. */ if (pos % msgvec) pos += msgvec - (pos % msgvec); else flag = 0; } while (flag); *pos0 = pos; return 0; } static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos) { int res, bit, irq, pos0, pos1, i; u32 val; struct pcie_port *pp = sys_to_pcie(desc->dev->bus->sysdata); if (!pp) { BUG(); return -EINVAL; } pos0 = find_first_zero_bit(pp->msi_irq_in_use, MAX_MSI_IRQS); if (pos0 % no_irqs) { if (find_valid_pos0(pp, no_irqs, pos0, &pos0)) goto no_valid_irq; } if (no_irqs > 1) { pos1 = find_next_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos0); /* there must be nvec number of consecutive free bits */ while ((pos1 - pos0) < no_irqs) { if (find_valid_pos0(pp, no_irqs, pos1, &pos0)) goto no_valid_irq; pos1 = find_next_bit(pp->msi_irq_in_use, MAX_MSI_IRQS, pos0); } } irq = (pp->msi_irq_start + pos0); if ((irq + no_irqs) > (pp->msi_irq_start + MAX_MSI_IRQS-1)) goto no_valid_irq; i = 0; while (i < no_irqs) { set_bit(pos0 + i, pp->msi_irq_in_use); irq_alloc_descs((irq + i), (irq + i), 1, 0); irq_set_msi_desc(irq + i, desc); /*Enable corresponding interrupt in MSI interrupt controller */ res = ((pos0 + i) / 32) * 12; bit = (pos0 + i) % 32; dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val |= 1 << bit; dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); i++; } *pos = pos0; return irq; no_valid_irq: *pos = pos0; return -ENOSPC; } static void clear_irq(unsigned int irq) { int res, bit, val, pos; struct irq_desc *desc; struct msi_desc *msi; struct pcie_port *pp; /* get the port structure */ desc = irq_to_desc(irq); msi = irq_desc_get_msi_desc(desc); pp = sys_to_pcie(msi->dev->bus->sysdata); if (!pp) { BUG(); return; } pos = irq - pp->msi_irq_start; irq_free_desc(irq); clear_bit(pos, pp->msi_irq_in_use); /* Disable corresponding interrupt on MSI interrupt controller */ res = (pos / 32) * 12; bit = pos % 32; dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val); val &= ~(1 << bit); dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val); } static int dw_msi_setup_irq(struct msi_chip *chip, struct pci_dev *pdev, struct msi_desc *desc) { int irq, pos, msgvec; u16 msg_ctr; struct msi_msg msg; struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); if (!pp) { BUG(); return -EINVAL; } pci_read_config_word(pdev, desc->msi_attrib.pos+PCI_MSI_FLAGS, &msg_ctr); msgvec = (msg_ctr&PCI_MSI_FLAGS_QSIZE) >> 4; if (msgvec == 0) msgvec = (msg_ctr & PCI_MSI_FLAGS_QMASK) >> 1; if (msgvec > 5) msgvec = 0; irq = assign_irq((1 << msgvec), desc, &pos); if (irq < 0) return irq; msg_ctr &= ~PCI_MSI_FLAGS_QSIZE; msg_ctr |= msgvec << 4; pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, msg_ctr); desc->msi_attrib.multiple = msgvec; msg.address_lo = virt_to_phys((void *)pp->msi_data); msg.address_hi = 0x0; msg.data = pos; write_msi_msg(irq, &msg); return 0; } static void dw_msi_teardown_irq(struct msi_chip *chip, unsigned int irq) { clear_irq(irq); } static struct msi_chip dw_pcie_msi_chip = { .setup_irq = dw_msi_setup_irq, .teardown_irq = dw_msi_teardown_irq, }; int dw_pcie_link_up(struct pcie_port *pp) { if (pp->ops->link_up) Loading @@ -150,6 +351,20 @@ int dw_pcie_link_up(struct pcie_port *pp) return 0; } static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq, irq_hw_number_t hwirq) { irq_set_chip_and_handler(irq, &dw_msi_irq_chip, handle_simple_irq); irq_set_chip_data(irq, domain->host_data); set_irq_flags(irq, IRQF_VALID); return 0; } static const struct irq_domain_ops msi_domain_ops = { .map = dw_pcie_msi_map, }; int __init dw_pcie_host_init(struct pcie_port *pp) { struct device_node *np = pp->dev->of_node; Loading @@ -157,6 +372,8 @@ int __init dw_pcie_host_init(struct pcie_port *pp) struct of_pci_range_parser parser; u32 val; struct irq_domain *irq_domain; if (of_pci_range_parser_init(&parser, np)) { dev_err(pp->dev, "missing ranges property\n"); return -EINVAL; Loading Loading @@ -223,6 +440,18 @@ int __init dw_pcie_host_init(struct pcie_port *pp) return -EINVAL; } if (IS_ENABLED(CONFIG_PCI_MSI)) { irq_domain = irq_domain_add_linear(pp->dev->of_node, MAX_MSI_IRQS, &msi_domain_ops, &dw_pcie_msi_chip); if (!irq_domain) { dev_err(pp->dev, "irq domain init failed\n"); return -ENXIO; } pp->msi_irq_start = irq_find_mapping(irq_domain, 0); } if (pp->ops->host_init) pp->ops->host_init(pp); Loading Loading @@ -485,10 +714,21 @@ int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return pp->irq; } static void dw_pcie_add_bus(struct pci_bus *bus) { if (IS_ENABLED(CONFIG_PCI_MSI)) { struct pcie_port *pp = sys_to_pcie(bus->sysdata); dw_pcie_msi_chip.dev = pp->dev; bus->msi = &dw_pcie_msi_chip; } } static struct hw_pci dw_pci = { .setup = dw_pcie_setup, .scan = dw_pcie_scan_bus, .map_irq = dw_pcie_map_irq, .add_bus = dw_pcie_add_bus, }; void dw_pcie_setup_rc(struct pcie_port *pp) Loading
drivers/pci/host/pcie-designware.h +14 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,14 @@ struct pcie_port_info { phys_addr_t mem_bus_addr; }; /* * Maximum number of MSI IRQs can be 256 per controller. But keep * it 32 as of now. Probably we will never need more than 32. If needed, * then increment it in multiple of 32. */ #define MAX_MSI_IRQS 32 #define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) struct pcie_port { struct device *dev; u8 root_bus_nr; Loading @@ -38,6 +46,10 @@ struct pcie_port { int irq; u32 lanes; struct pcie_host_ops *ops; int msi_irq; int msi_irq_start; unsigned long msi_data; DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS); }; struct pcie_host_ops { Loading @@ -57,6 +69,8 @@ int cfg_read(void __iomem *addr, int where, int size, u32 *val); int cfg_write(void __iomem *addr, int where, int size, u32 val); int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val); int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val); void dw_handle_msi_irq(struct pcie_port *pp); void dw_pcie_msi_init(struct pcie_port *pp); int dw_pcie_link_up(struct pcie_port *pp); void dw_pcie_setup_rc(struct pcie_port *pp); int dw_pcie_host_init(struct pcie_port *pp); Loading