On 03/11/15 15:23, Bharat Kumar Gogada wrote: > Adding PCIe Root Port driver for Xilinx PCIe NWL bridge IP. > > Signed-off-by: Bharat Kumar Gogada <bharatku@xxxxxxxxxx> > Signed-off-by: Ravi Kiran Gummaluri <rgummal@xxxxxxxxxx> > --- > Removed msi_controller and added irq_domian for MSI domain hierarchy. > Modified code for filling MSI address in struct msg. > Added check for hwirq before passing it to irq_domain_set_info. > --- > .../devicetree/bindings/pci/xilinx-nwl-pcie.txt | 68 ++ > drivers/pci/host/Kconfig | 10 + > drivers/pci/host/Makefile | 1 + > drivers/pci/host/pcie-xilinx-nwl.c | 1053 ++++++++++++++++++++ > 4 files changed, 1132 insertions(+) > create mode 100644 Documentation/devicetree/bindings/pci/xilinx-nwl-pcie.txt > create mode 100644 drivers/pci/host/pcie-xilinx-nwl.c [...] > +#ifdef CONFIG_PCI_MSI > +static struct irq_chip nwl_msi_irq_chip = { > + .name = "nwl_pcie:msi", > + .irq_enable = unmask_msi_irq, > + .irq_disable = mask_msi_irq, > + .irq_mask = mask_msi_irq, > + .irq_unmask = unmask_msi_irq, > + > +}; > + > +static struct msi_domain_info nwl_msi_domain_info = { > + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | > + MSI_FLAG_MULTI_PCI_MSI), If you're supporting multi-MSI, how do you ensure that all hwirqs are contiguous as required by the spec? Clearly, your allocator doesn't provide this guarantee. Also, you still lack support for MSI-X (which would come for free...). > + .chip = &nwl_msi_irq_chip, > +}; > +#endif > + > +static void nwl_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) > +{ > + struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); > + struct nwl_msi *msi = &pcie->msi; > + phys_addr_t msi_addr = virt_to_phys((void *)msi->pages); > + > + msg->address_lo = lower_32_bits(msi_addr); > + msg->address_hi = upper_32_bits(msi_addr); > + msg->data = data->hwirq; > +} > + > +static int nwl_msi_set_affinity(struct irq_data *irq_data, > + const struct cpumask *mask, bool force) > +{ > + return -EINVAL; > +} > + > +static struct irq_chip nwl_irq_chip = { > + .name = "Xilinx MSI", > + .irq_compose_msi_msg = nwl_compose_msi_msg, > + .irq_set_affinity = nwl_msi_set_affinity, > +}; > + > +static int nwl_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, > + unsigned int nr_irqs, void *args) > +{ > + struct nwl_pcie *pcie = domain->host_data; > + struct nwl_msi *msi = &pcie->msi; > + unsigned long bit; > + > + mutex_lock(&msi->lock); > + bit = find_first_zero_bit(msi->used, INT_PCI_MSI_NR); > + if (bit < INT_PCI_MSI_NR) > + set_bit(bit, msi->used); > + else > + bit = -ENOSPC; > + > + mutex_unlock(&msi->lock); > + > + if (bit < 0) > + return bit; > + > + irq_domain_set_info(domain, virq, bit, &nwl_irq_chip, > + domain->host_data, handle_simple_irq, > + NULL, NULL); > + return 0; > +} > + > +static void nwl_irq_domain_free(struct irq_domain *domain, unsigned int virq, > + unsigned int nr_irqs) > +{ > + struct irq_data *data = irq_domain_get_irq_data(domain, virq); > + struct nwl_pcie *pcie = irq_data_get_irq_chip_data(data); > + struct nwl_msi *msi = &pcie->msi; > + > + mutex_lock(&msi->lock); > + if (!test_bit(data->hwirq, msi->used)) > + dev_err(pcie->dev, "freeing unused MSI %lu\n", data->hwirq); > + else > + clear_bit(data->hwirq, msi->used); > + > + mutex_unlock(&msi->lock); > +} > + > +static const struct irq_domain_ops dev_msi_domain_ops = { > + .alloc = nwl_irq_domain_alloc, > + .free = nwl_irq_domain_free, > +}; > + > +static void nwl_pcie_free_irq_domain(struct nwl_pcie *pcie) > +{ > + int i; > + u32 irq; > + > +#ifdef CONFIG_PCI_MSI > + struct nwl_msi *msi = &pcie->msi; > +#endif > + > + for (i = 0; i < 4; i++) { > + irq = irq_find_mapping(pcie->legacy_irq_domain, i + 1); > + if (irq > 0) > + irq_dispose_mapping(irq); > + } > + > + irq_domain_remove(pcie->legacy_irq_domain); > + > +#ifdef CONFIG_PCI_MSI > + irq_set_chained_handler_and_data(msi->irq_msi0, NULL, NULL); > + irq_set_chained_handler_and_data(msi->irq_msi1, NULL, NULL); > + > + irq_domain_remove(msi->msi_domain); > + irq_domain_remove(msi->dev_domain); > +#endif > + > +} Remove these #ifdefs. You can test the validity of these fields before calling the various functions. You can also move this to a separate function. > + > +static int nwl_pcie_init_irq_domain(struct nwl_pcie *pcie) > +{ > + struct device_node *node = pcie->dev->of_node; > + struct device_node *legacy_intc_node; > + > +#ifdef CONFIG_PCI_MSI > + struct nwl_msi *msi = &pcie->msi; > +#endif > + > + legacy_intc_node = of_get_next_child(node, NULL); > + if (!legacy_intc_node) { > + dev_err(pcie->dev, "No legacy intc node found\n"); > + return PTR_ERR(legacy_intc_node); > + } > + > + pcie->legacy_irq_domain = irq_domain_add_linear(legacy_intc_node, 4, > + &legacy_domain_ops, > + pcie); > + > + if (!pcie->legacy_irq_domain) { > + dev_err(pcie->dev, "failed to create IRQ domain\n"); > + return -ENOMEM; > + } > + > +#ifdef CONFIG_PCI_MSI > + msi->dev_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, > + &dev_msi_domain_ops, pcie); > + if (!msi->dev_domain) { > + dev_err(pcie->dev, "failed to create dev IRQ domain\n"); > + return -ENOMEM; > + } > + msi->msi_domain = pci_msi_create_irq_domain(node, > + &nwl_msi_domain_info, > + msi->dev_domain); > + if (!msi->msi_domain) { > + dev_err(pcie->dev, "failed to create msi IRQ domain\n"); > + irq_domain_remove(msi->dev_domain); > + return -ENOMEM; > + } > +#endif Similar treatment here: move anything that's MSI-dependent to another function, and provide a dummy implementation for the !PCI_MSI case. > + return 0; > +} > + > +static int nwl_pcie_enable_msi(struct nwl_pcie *pcie, struct pci_bus *bus) > +{ > + struct platform_device *pdev = to_platform_device(pcie->dev); > + struct nwl_msi *msi = &pcie->msi; > + unsigned long base; > + int ret; > + > + mutex_init(&msi->lock); > + > + /* Check for msii_present bit */ > + ret = nwl_bridge_readl(pcie, I_MSII_CAPABILITIES) & MSII_PRESENT; > + if (!ret) { > + dev_err(pcie->dev, "MSI not present\n"); > + ret = -EIO; > + goto err; > + } > + > + /* Enable MSII */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) | > + MSII_ENABLE, I_MSII_CONTROL); > + > + /* Enable MSII status */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, I_MSII_CONTROL) | > + MSII_STATUS_ENABLE, I_MSII_CONTROL); > + > + /* setup AFI/FPCI range */ > + msi->pages = __get_free_pages(GFP_KERNEL, 0); > + base = virt_to_phys((void *)msi->pages); > + nwl_bridge_writel(pcie, lower_32_bits(base), I_MSII_BASE_LO); > + nwl_bridge_writel(pcie, upper_32_bits(base), I_MSII_BASE_HI); I just read this, and I'm puzzled. Actually, puzzled is an understatement. Why on Earth do you need to give RAM to your MSI HW? This should be a device, not RAM. By the look of it, this could be absolutely anything. Are you sure you have to supply RAM here? > + > + /* Disable high range msi interrupts */ > + nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI); > + > + /* Clear pending high range msi interrupts */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_HI) & > + MSGF_MSI_SR_HI_MASK, MSGF_MSI_STATUS_HI); > + /* Get msi_1 IRQ number */ > + msi->irq_msi1 = platform_get_irq_byname(pdev, "msi1"); > + if (msi->irq_msi1 < 0) { > + dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi1); > + goto err; > + } > + /* Register msi handler */ > + irq_set_chained_handler_and_data(msi->irq_msi1, > + nwl_pcie_msi_handler_high, pcie); > + > + /* Enable all high range msi interrupts */ > + nwl_bridge_writel(pcie, MSGF_MSI_SR_HI_MASK, MSGF_MSI_MASK_HI); > + > + /* Disable low range msi interrupts */ > + nwl_bridge_writel(pcie, (u32)~MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO); > + > + /* Clear pending low range msi interrupts */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MSI_STATUS_LO) & > + MSGF_MSI_SR_LO_MASK, MSGF_MSI_STATUS_LO); > + /* Get msi_0 IRQ number */ > + msi->irq_msi0 = platform_get_irq_byname(pdev, "msi0"); > + if (msi->irq_msi0 < 0) { > + dev_err(&pdev->dev, "failed to get IRQ#%d\n", msi->irq_msi0); > + goto err; > + } > + > + /* Register msi handler */ > + irq_set_chained_handler_and_data(msi->irq_msi0, > + nwl_pcie_msi_handler_low, pcie); > + > + /* Enable all low range msi interrupts */ > + nwl_bridge_writel(pcie, MSGF_MSI_SR_LO_MASK, MSGF_MSI_MASK_LO); > + > + return 0; > +err: > + return ret; > +} > + > +static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) > +{ > + struct platform_device *pdev = to_platform_device(pcie->dev); > + u32 breg_val, ecam_val, first_busno = 0; > + int err; > + int check_link_up = 0; > + > + /* Check for BREG present bit */ > + breg_val = nwl_bridge_readl(pcie, E_BREG_CAPABILITIES) & BREG_PRESENT; > + if (!breg_val) { > + dev_err(pcie->dev, "BREG is not present\n"); > + return breg_val; > + } > + /* Write bridge_off to breg base */ > + nwl_bridge_writel(pcie, (u32)(pcie->phys_breg_base), > + E_BREG_BASE_LO); > + > + /* Enable BREG */ > + nwl_bridge_writel(pcie, ~BREG_ENABLE_FORCE & BREG_ENABLE, > + E_BREG_CONTROL); > + > + /* Disable DMA channel registers */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_PCIE_RX0) | > + CFG_DMA_REG_BAR, BRCFG_PCIE_RX0); > + > + /* Enable the bridge config interrupt */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, BRCFG_INTERRUPT) | > + BRCFG_INTERRUPT_MASK, BRCFG_INTERRUPT); > + /* Enable Ingress subtractive decode translation */ > + nwl_bridge_writel(pcie, SET_ISUB_CONTROL, I_ISUB_CONTROL); > + > + /* Enable msg filtering details */ > + nwl_bridge_writel(pcie, CFG_ENABLE_MSG_FILTER_MASK, > + BRCFG_PCIE_RX_MSG_FILTER); > + do { > + err = nwl_pcie_link_up(pcie, PHY_RDY_LINKUP); > + if (err != 1) { > + check_link_up++; > + if (check_link_up > LINKUP_ITER_CHECK) > + return -ENODEV; > + mdelay(1000); > + } > + } while (!err); > + > + /* Check for ECAM present bit */ > + ecam_val = nwl_bridge_readl(pcie, E_ECAM_CAPABILITIES) & E_ECAM_PRESENT; > + if (!ecam_val) { > + dev_err(pcie->dev, "ECAM is not present\n"); > + return ecam_val; > + } > + > + /* Enable ECAM */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | > + E_ECAM_CR_ENABLE, E_ECAM_CONTROL); > + /* Write ecam_value on ecam_control */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | > + (pcie->ecam_value << E_ECAM_SIZE_SHIFT), > + E_ECAM_CONTROL); > + /* Write phy_reg_base to ecam base */ > + nwl_bridge_writel(pcie, (u32)pcie->phys_ecam_base, E_ECAM_BASE_LO); > + > + /* Get bus range */ > + ecam_val = nwl_bridge_readl(pcie, E_ECAM_CONTROL); > + pcie->last_busno = (ecam_val & E_ECAM_SIZE_LOC) >> E_ECAM_SIZE_SHIFT; > + /* Write primary, secondary and subordinate bus numbers */ > + ecam_val = first_busno; > + ecam_val |= (first_busno + 1) << 8; > + ecam_val |= (pcie->last_busno << E_ECAM_SIZE_SHIFT); > + writel(ecam_val, (pcie->ecam_base + PCI_PRIMARY_BUS)); > + > + /* Check if PCIe link is up? */ > + pcie->link_up = nwl_pcie_link_up(pcie, PCIE_USER_LINKUP); > + if (!pcie->link_up) > + dev_info(pcie->dev, "Link is DOWN\n"); > + else > + dev_info(pcie->dev, "Link is UP\n"); > + > + /* Disable all misc interrupts */ > + nwl_bridge_writel(pcie, (u32)~MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); > + > + /* Clear pending misc interrupts */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_MISC_STATUS) & > + MSGF_MISC_SR_MASKALL, MSGF_MISC_STATUS); > + > + /* Get misc IRQ number */ > + pcie->irq_misc = platform_get_irq_byname(pdev, "misc"); > + if (pcie->irq_misc < 0) { > + dev_err(&pdev->dev, "failed to get misc IRQ#%d\n", That's going to be a pretty funky interrupt number. > + pcie->irq_misc); > + return pcie->irq_misc; Don't you need to turn the thing down when you abort the probing? > + } > + /* Register misc handler */ > + err = devm_request_irq(pcie->dev, pcie->irq_misc, > + nwl_pcie_misc_handler, IRQF_SHARED, > + "nwl_pcie:misc", pcie); > + if (err) { > + dev_err(pcie->dev, "fail to register misc IRQ#%d\n", > + pcie->irq_misc); > + return err; > + } > + /* Enable all misc interrupts */ > + nwl_bridge_writel(pcie, MSGF_MISC_SR_MASKALL, MSGF_MISC_MASK); > + > + /* Disable all legacy interrupts */ > + nwl_bridge_writel(pcie, (u32)~MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); > + > + /* Clear pending legacy interrupts */ > + nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, MSGF_LEG_STATUS) & > + MSGF_LEG_SR_MASKALL, MSGF_LEG_STATUS); > + /* Get intx IRQ number */ > + pcie->irq_intx = platform_get_irq_byname(pdev, "intx"); > + if (pcie->irq_intx < 0) { > + dev_err(&pdev->dev, "failed to get intx IRQ#%d\n", Same here. > + pcie->irq_intx); > + return pcie->irq_intx; > + } > + > + /* Enable all legacy interrupts */ > + nwl_bridge_writel(pcie, MSGF_LEG_SR_MASKALL, MSGF_LEG_MASK); > + > + return 0; > +} Thanks, M. -- Jazz is not dead. It just smells funny... -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html