103bdc388SHou Zhiqiang // SPDX-License-Identifier: GPL-2.0
203bdc388SHou Zhiqiang /*
303bdc388SHou Zhiqiang * PCIe host controller driver for Mobiveil PCIe Host controller
403bdc388SHou Zhiqiang *
503bdc388SHou Zhiqiang * Copyright (c) 2018 Mobiveil Inc.
603bdc388SHou Zhiqiang * Copyright 2019-2020 NXP
703bdc388SHou Zhiqiang *
803bdc388SHou Zhiqiang * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in>
903bdc388SHou Zhiqiang * Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
1003bdc388SHou Zhiqiang */
1103bdc388SHou Zhiqiang
1203bdc388SHou Zhiqiang #include <linux/init.h>
1303bdc388SHou Zhiqiang #include <linux/interrupt.h>
1403bdc388SHou Zhiqiang #include <linux/irq.h>
1503bdc388SHou Zhiqiang #include <linux/irqchip/chained_irq.h>
1603bdc388SHou Zhiqiang #include <linux/irqdomain.h>
1703bdc388SHou Zhiqiang #include <linux/kernel.h>
1803bdc388SHou Zhiqiang #include <linux/module.h>
1903bdc388SHou Zhiqiang #include <linux/msi.h>
2003bdc388SHou Zhiqiang #include <linux/of_pci.h>
2103bdc388SHou Zhiqiang #include <linux/pci.h>
2203bdc388SHou Zhiqiang #include <linux/platform_device.h>
2303bdc388SHou Zhiqiang #include <linux/slab.h>
2403bdc388SHou Zhiqiang
2503bdc388SHou Zhiqiang #include "pcie-mobiveil.h"
2603bdc388SHou Zhiqiang
mobiveil_pcie_valid_device(struct pci_bus * bus,unsigned int devfn)2703bdc388SHou Zhiqiang static bool mobiveil_pcie_valid_device(struct pci_bus *bus, unsigned int devfn)
2803bdc388SHou Zhiqiang {
2903bdc388SHou Zhiqiang /* Only one device down on each root port */
30388637d9SRob Herring if (pci_is_root_bus(bus) && (devfn > 0))
3103bdc388SHou Zhiqiang return false;
3203bdc388SHou Zhiqiang
3303bdc388SHou Zhiqiang /*
3403bdc388SHou Zhiqiang * Do not read more than one device on the bus directly
3503bdc388SHou Zhiqiang * attached to RC
3603bdc388SHou Zhiqiang */
37388637d9SRob Herring if ((bus->primary == to_pci_host_bridge(bus->bridge)->busnr) && (PCI_SLOT(devfn) > 0))
3803bdc388SHou Zhiqiang return false;
3903bdc388SHou Zhiqiang
4003bdc388SHou Zhiqiang return true;
4103bdc388SHou Zhiqiang }
4203bdc388SHou Zhiqiang
4303bdc388SHou Zhiqiang /*
4403bdc388SHou Zhiqiang * mobiveil_pcie_map_bus - routine to get the configuration base of either
4503bdc388SHou Zhiqiang * root port or endpoint
4603bdc388SHou Zhiqiang */
mobiveil_pcie_map_bus(struct pci_bus * bus,unsigned int devfn,int where)4703bdc388SHou Zhiqiang static void __iomem *mobiveil_pcie_map_bus(struct pci_bus *bus,
4803bdc388SHou Zhiqiang unsigned int devfn, int where)
4903bdc388SHou Zhiqiang {
5003bdc388SHou Zhiqiang struct mobiveil_pcie *pcie = bus->sysdata;
5103bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
5203bdc388SHou Zhiqiang u32 value;
5303bdc388SHou Zhiqiang
5403bdc388SHou Zhiqiang if (!mobiveil_pcie_valid_device(bus, devfn))
5503bdc388SHou Zhiqiang return NULL;
5603bdc388SHou Zhiqiang
5703bdc388SHou Zhiqiang /* RC config access */
58388637d9SRob Herring if (pci_is_root_bus(bus))
5903bdc388SHou Zhiqiang return pcie->csr_axi_slave_base + where;
6003bdc388SHou Zhiqiang
6103bdc388SHou Zhiqiang /*
6203bdc388SHou Zhiqiang * EP config access (in Config/APIO space)
6303bdc388SHou Zhiqiang * Program PEX Address base (31..16 bits) with appropriate value
6403bdc388SHou Zhiqiang * (BDF) in PAB_AXI_AMAP_PEX_WIN_L0 Register.
6503bdc388SHou Zhiqiang * Relies on pci_lock serialization
6603bdc388SHou Zhiqiang */
6703bdc388SHou Zhiqiang value = bus->number << PAB_BUS_SHIFT |
6803bdc388SHou Zhiqiang PCI_SLOT(devfn) << PAB_DEVICE_SHIFT |
6903bdc388SHou Zhiqiang PCI_FUNC(devfn) << PAB_FUNCTION_SHIFT;
7003bdc388SHou Zhiqiang
7103bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_PEX_WIN_L(WIN_NUM_0));
7203bdc388SHou Zhiqiang
7303bdc388SHou Zhiqiang return rp->config_axi_slave_base + where;
7403bdc388SHou Zhiqiang }
7503bdc388SHou Zhiqiang
7603bdc388SHou Zhiqiang static struct pci_ops mobiveil_pcie_ops = {
7703bdc388SHou Zhiqiang .map_bus = mobiveil_pcie_map_bus,
7803bdc388SHou Zhiqiang .read = pci_generic_config_read,
7903bdc388SHou Zhiqiang .write = pci_generic_config_write,
8003bdc388SHou Zhiqiang };
8103bdc388SHou Zhiqiang
mobiveil_pcie_isr(struct irq_desc * desc)8203bdc388SHou Zhiqiang static void mobiveil_pcie_isr(struct irq_desc *desc)
8303bdc388SHou Zhiqiang {
8403bdc388SHou Zhiqiang struct irq_chip *chip = irq_desc_get_chip(desc);
8503bdc388SHou Zhiqiang struct mobiveil_pcie *pcie = irq_desc_get_handler_data(desc);
8603bdc388SHou Zhiqiang struct device *dev = &pcie->pdev->dev;
8703bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
8803bdc388SHou Zhiqiang struct mobiveil_msi *msi = &rp->msi;
8903bdc388SHou Zhiqiang u32 msi_data, msi_addr_lo, msi_addr_hi;
9003bdc388SHou Zhiqiang u32 intr_status, msi_status;
9103bdc388SHou Zhiqiang unsigned long shifted_status;
92d21faba1SMarc Zyngier u32 bit, val, mask;
9303bdc388SHou Zhiqiang
9403bdc388SHou Zhiqiang /*
9503bdc388SHou Zhiqiang * The core provides a single interrupt for both INTx/MSI messages.
9603bdc388SHou Zhiqiang * So we'll read both INTx and MSI status
9703bdc388SHou Zhiqiang */
9803bdc388SHou Zhiqiang
9903bdc388SHou Zhiqiang chained_irq_enter(chip, desc);
10003bdc388SHou Zhiqiang
10103bdc388SHou Zhiqiang /* read INTx status */
10203bdc388SHou Zhiqiang val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_STAT);
10303bdc388SHou Zhiqiang mask = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
10403bdc388SHou Zhiqiang intr_status = val & mask;
10503bdc388SHou Zhiqiang
10603bdc388SHou Zhiqiang /* Handle INTx */
10703bdc388SHou Zhiqiang if (intr_status & PAB_INTP_INTX_MASK) {
10803bdc388SHou Zhiqiang shifted_status = mobiveil_csr_readl(pcie,
10903bdc388SHou Zhiqiang PAB_INTP_AMBA_MISC_STAT);
11003bdc388SHou Zhiqiang shifted_status &= PAB_INTP_INTX_MASK;
11103bdc388SHou Zhiqiang shifted_status >>= PAB_INTX_START;
11203bdc388SHou Zhiqiang do {
11303bdc388SHou Zhiqiang for_each_set_bit(bit, &shifted_status, PCI_NUM_INTX) {
114d21faba1SMarc Zyngier int ret;
115d21faba1SMarc Zyngier ret = generic_handle_domain_irq(rp->intx_domain,
11603bdc388SHou Zhiqiang bit + 1);
117d21faba1SMarc Zyngier if (ret)
11803bdc388SHou Zhiqiang dev_err_ratelimited(dev, "unexpected IRQ, INT%d\n",
11903bdc388SHou Zhiqiang bit);
12003bdc388SHou Zhiqiang
12103bdc388SHou Zhiqiang /* clear interrupt handled */
12203bdc388SHou Zhiqiang mobiveil_csr_writel(pcie,
12303bdc388SHou Zhiqiang 1 << (PAB_INTX_START + bit),
12403bdc388SHou Zhiqiang PAB_INTP_AMBA_MISC_STAT);
12503bdc388SHou Zhiqiang }
12603bdc388SHou Zhiqiang
12703bdc388SHou Zhiqiang shifted_status = mobiveil_csr_readl(pcie,
12803bdc388SHou Zhiqiang PAB_INTP_AMBA_MISC_STAT);
12903bdc388SHou Zhiqiang shifted_status &= PAB_INTP_INTX_MASK;
13003bdc388SHou Zhiqiang shifted_status >>= PAB_INTX_START;
13103bdc388SHou Zhiqiang } while (shifted_status != 0);
13203bdc388SHou Zhiqiang }
13303bdc388SHou Zhiqiang
13403bdc388SHou Zhiqiang /* read extra MSI status register */
13503bdc388SHou Zhiqiang msi_status = readl_relaxed(pcie->apb_csr_base + MSI_STATUS_OFFSET);
13603bdc388SHou Zhiqiang
13703bdc388SHou Zhiqiang /* handle MSI interrupts */
13803bdc388SHou Zhiqiang while (msi_status & 1) {
13903bdc388SHou Zhiqiang msi_data = readl_relaxed(pcie->apb_csr_base + MSI_DATA_OFFSET);
14003bdc388SHou Zhiqiang
14103bdc388SHou Zhiqiang /*
14203bdc388SHou Zhiqiang * MSI_STATUS_OFFSET register gets updated to zero
14303bdc388SHou Zhiqiang * once we pop not only the MSI data but also address
14403bdc388SHou Zhiqiang * from MSI hardware FIFO. So keeping these following
14503bdc388SHou Zhiqiang * two dummy reads.
14603bdc388SHou Zhiqiang */
14703bdc388SHou Zhiqiang msi_addr_lo = readl_relaxed(pcie->apb_csr_base +
14803bdc388SHou Zhiqiang MSI_ADDR_L_OFFSET);
14903bdc388SHou Zhiqiang msi_addr_hi = readl_relaxed(pcie->apb_csr_base +
15003bdc388SHou Zhiqiang MSI_ADDR_H_OFFSET);
15103bdc388SHou Zhiqiang dev_dbg(dev, "MSI registers, data: %08x, addr: %08x:%08x\n",
15203bdc388SHou Zhiqiang msi_data, msi_addr_hi, msi_addr_lo);
15303bdc388SHou Zhiqiang
154d21faba1SMarc Zyngier generic_handle_domain_irq(msi->dev_domain, msi_data);
15503bdc388SHou Zhiqiang
15603bdc388SHou Zhiqiang msi_status = readl_relaxed(pcie->apb_csr_base +
15703bdc388SHou Zhiqiang MSI_STATUS_OFFSET);
15803bdc388SHou Zhiqiang }
15903bdc388SHou Zhiqiang
16003bdc388SHou Zhiqiang /* Clear the interrupt status */
16103bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, intr_status, PAB_INTP_AMBA_MISC_STAT);
16203bdc388SHou Zhiqiang chained_irq_exit(chip, desc);
16303bdc388SHou Zhiqiang }
16403bdc388SHou Zhiqiang
mobiveil_pcie_parse_dt(struct mobiveil_pcie * pcie)16503bdc388SHou Zhiqiang static int mobiveil_pcie_parse_dt(struct mobiveil_pcie *pcie)
16603bdc388SHou Zhiqiang {
16703bdc388SHou Zhiqiang struct device *dev = &pcie->pdev->dev;
16803bdc388SHou Zhiqiang struct platform_device *pdev = pcie->pdev;
16903bdc388SHou Zhiqiang struct device_node *node = dev->of_node;
17003bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
17103bdc388SHou Zhiqiang struct resource *res;
17203bdc388SHou Zhiqiang
17303bdc388SHou Zhiqiang /* map config resource */
17403bdc388SHou Zhiqiang res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
17503bdc388SHou Zhiqiang "config_axi_slave");
17603bdc388SHou Zhiqiang rp->config_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
17703bdc388SHou Zhiqiang if (IS_ERR(rp->config_axi_slave_base))
17803bdc388SHou Zhiqiang return PTR_ERR(rp->config_axi_slave_base);
17903bdc388SHou Zhiqiang rp->ob_io_res = res;
18003bdc388SHou Zhiqiang
18103bdc388SHou Zhiqiang /* map csr resource */
18203bdc388SHou Zhiqiang res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
18303bdc388SHou Zhiqiang "csr_axi_slave");
18403bdc388SHou Zhiqiang pcie->csr_axi_slave_base = devm_pci_remap_cfg_resource(dev, res);
18503bdc388SHou Zhiqiang if (IS_ERR(pcie->csr_axi_slave_base))
18603bdc388SHou Zhiqiang return PTR_ERR(pcie->csr_axi_slave_base);
18703bdc388SHou Zhiqiang pcie->pcie_reg_base = res->start;
18803bdc388SHou Zhiqiang
18903bdc388SHou Zhiqiang /* read the number of windows requested */
19003bdc388SHou Zhiqiang if (of_property_read_u32(node, "apio-wins", &pcie->apio_wins))
19103bdc388SHou Zhiqiang pcie->apio_wins = MAX_PIO_WINDOWS;
19203bdc388SHou Zhiqiang
19303bdc388SHou Zhiqiang if (of_property_read_u32(node, "ppio-wins", &pcie->ppio_wins))
19403bdc388SHou Zhiqiang pcie->ppio_wins = MAX_PIO_WINDOWS;
19503bdc388SHou Zhiqiang
19603bdc388SHou Zhiqiang return 0;
19703bdc388SHou Zhiqiang }
19803bdc388SHou Zhiqiang
mobiveil_pcie_enable_msi(struct mobiveil_pcie * pcie)19903bdc388SHou Zhiqiang static void mobiveil_pcie_enable_msi(struct mobiveil_pcie *pcie)
20003bdc388SHou Zhiqiang {
20103bdc388SHou Zhiqiang phys_addr_t msg_addr = pcie->pcie_reg_base;
20203bdc388SHou Zhiqiang struct mobiveil_msi *msi = &pcie->rp.msi;
20303bdc388SHou Zhiqiang
20403bdc388SHou Zhiqiang msi->num_of_vectors = PCI_NUM_MSI;
20503bdc388SHou Zhiqiang msi->msi_pages_phys = (phys_addr_t)msg_addr;
20603bdc388SHou Zhiqiang
20703bdc388SHou Zhiqiang writel_relaxed(lower_32_bits(msg_addr),
20803bdc388SHou Zhiqiang pcie->apb_csr_base + MSI_BASE_LO_OFFSET);
20903bdc388SHou Zhiqiang writel_relaxed(upper_32_bits(msg_addr),
21003bdc388SHou Zhiqiang pcie->apb_csr_base + MSI_BASE_HI_OFFSET);
21103bdc388SHou Zhiqiang writel_relaxed(4096, pcie->apb_csr_base + MSI_SIZE_OFFSET);
21203bdc388SHou Zhiqiang writel_relaxed(1, pcie->apb_csr_base + MSI_ENABLE_OFFSET);
21303bdc388SHou Zhiqiang }
21403bdc388SHou Zhiqiang
mobiveil_host_init(struct mobiveil_pcie * pcie,bool reinit)21552cae4c7SHou Zhiqiang int mobiveil_host_init(struct mobiveil_pcie *pcie, bool reinit)
21603bdc388SHou Zhiqiang {
21703bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
21803bdc388SHou Zhiqiang struct pci_host_bridge *bridge = rp->bridge;
21903bdc388SHou Zhiqiang u32 value, pab_ctrl, type;
22003bdc388SHou Zhiqiang struct resource_entry *win;
22103bdc388SHou Zhiqiang
22252cae4c7SHou Zhiqiang pcie->ib_wins_configured = 0;
22352cae4c7SHou Zhiqiang pcie->ob_wins_configured = 0;
22452cae4c7SHou Zhiqiang
22552cae4c7SHou Zhiqiang if (!reinit) {
22603bdc388SHou Zhiqiang /* setup bus numbers */
22703bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PCI_PRIMARY_BUS);
22803bdc388SHou Zhiqiang value &= 0xff000000;
22903bdc388SHou Zhiqiang value |= 0x00ff0100;
23003bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PCI_PRIMARY_BUS);
23152cae4c7SHou Zhiqiang }
23203bdc388SHou Zhiqiang
23303bdc388SHou Zhiqiang /*
23403bdc388SHou Zhiqiang * program Bus Master Enable Bit in Command Register in PAB Config
23503bdc388SHou Zhiqiang * Space
23603bdc388SHou Zhiqiang */
23703bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PCI_COMMAND);
23803bdc388SHou Zhiqiang value |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
23903bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PCI_COMMAND);
24003bdc388SHou Zhiqiang
24103bdc388SHou Zhiqiang /*
24203bdc388SHou Zhiqiang * program PIO Enable Bit to 1 (and PEX PIO Enable to 1) in PAB_CTRL
24303bdc388SHou Zhiqiang * register
24403bdc388SHou Zhiqiang */
24503bdc388SHou Zhiqiang pab_ctrl = mobiveil_csr_readl(pcie, PAB_CTRL);
24603bdc388SHou Zhiqiang pab_ctrl |= (1 << AMBA_PIO_ENABLE_SHIFT) | (1 << PEX_PIO_ENABLE_SHIFT);
24703bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, pab_ctrl, PAB_CTRL);
24803bdc388SHou Zhiqiang
24903bdc388SHou Zhiqiang /*
25003bdc388SHou Zhiqiang * program PIO Enable Bit to 1 and Config Window Enable Bit to 1 in
25103bdc388SHou Zhiqiang * PAB_AXI_PIO_CTRL Register
25203bdc388SHou Zhiqiang */
25303bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PAB_AXI_PIO_CTRL);
25403bdc388SHou Zhiqiang value |= APIO_EN_MASK;
25503bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PAB_AXI_PIO_CTRL);
25603bdc388SHou Zhiqiang
25703bdc388SHou Zhiqiang /* Enable PCIe PIO master */
25803bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PAB_PEX_PIO_CTRL);
25903bdc388SHou Zhiqiang value |= 1 << PIO_ENABLE_SHIFT;
26003bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PAB_PEX_PIO_CTRL);
26103bdc388SHou Zhiqiang
26203bdc388SHou Zhiqiang /*
26303bdc388SHou Zhiqiang * we'll program one outbound window for config reads and
26403bdc388SHou Zhiqiang * another default inbound window for all the upstream traffic
26503bdc388SHou Zhiqiang * rest of the outbound windows will be configured according to
26603bdc388SHou Zhiqiang * the "ranges" field defined in device tree
26703bdc388SHou Zhiqiang */
26803bdc388SHou Zhiqiang
26903bdc388SHou Zhiqiang /* config outbound translation window */
27003bdc388SHou Zhiqiang program_ob_windows(pcie, WIN_NUM_0, rp->ob_io_res->start, 0,
27103bdc388SHou Zhiqiang CFG_WINDOW_TYPE, resource_size(rp->ob_io_res));
27203bdc388SHou Zhiqiang
27303bdc388SHou Zhiqiang /* memory inbound translation window */
27403bdc388SHou Zhiqiang program_ib_windows(pcie, WIN_NUM_0, 0, 0, MEM_WINDOW_TYPE, IB_WIN_SIZE);
27503bdc388SHou Zhiqiang
27603bdc388SHou Zhiqiang /* Get the I/O and memory ranges from DT */
27703bdc388SHou Zhiqiang resource_list_for_each_entry(win, &bridge->windows) {
27803bdc388SHou Zhiqiang if (resource_type(win->res) == IORESOURCE_MEM)
27903bdc388SHou Zhiqiang type = MEM_WINDOW_TYPE;
28003bdc388SHou Zhiqiang else if (resource_type(win->res) == IORESOURCE_IO)
28103bdc388SHou Zhiqiang type = IO_WINDOW_TYPE;
28203bdc388SHou Zhiqiang else
28303bdc388SHou Zhiqiang continue;
28403bdc388SHou Zhiqiang
28503bdc388SHou Zhiqiang /* configure outbound translation window */
28603bdc388SHou Zhiqiang program_ob_windows(pcie, pcie->ob_wins_configured,
28703bdc388SHou Zhiqiang win->res->start,
28803bdc388SHou Zhiqiang win->res->start - win->offset,
28903bdc388SHou Zhiqiang type, resource_size(win->res));
29003bdc388SHou Zhiqiang }
29103bdc388SHou Zhiqiang
29203bdc388SHou Zhiqiang /* fixup for PCIe class register */
29303bdc388SHou Zhiqiang value = mobiveil_csr_readl(pcie, PAB_INTP_AXI_PIO_CLASS);
29403bdc388SHou Zhiqiang value &= 0xff;
295*904b10fbSPali Rohár value |= PCI_CLASS_BRIDGE_PCI_NORMAL << 8;
29603bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, value, PAB_INTP_AXI_PIO_CLASS);
29703bdc388SHou Zhiqiang
29803bdc388SHou Zhiqiang return 0;
29903bdc388SHou Zhiqiang }
30003bdc388SHou Zhiqiang
mobiveil_mask_intx_irq(struct irq_data * data)30103bdc388SHou Zhiqiang static void mobiveil_mask_intx_irq(struct irq_data *data)
30203bdc388SHou Zhiqiang {
303b8fecfdfSThomas Gleixner struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data);
30403bdc388SHou Zhiqiang struct mobiveil_root_port *rp;
30503bdc388SHou Zhiqiang unsigned long flags;
30603bdc388SHou Zhiqiang u32 mask, shifted_val;
30703bdc388SHou Zhiqiang
30803bdc388SHou Zhiqiang rp = &pcie->rp;
30903bdc388SHou Zhiqiang mask = 1 << ((data->hwirq + PAB_INTX_START) - 1);
31003bdc388SHou Zhiqiang raw_spin_lock_irqsave(&rp->intx_mask_lock, flags);
31103bdc388SHou Zhiqiang shifted_val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
31203bdc388SHou Zhiqiang shifted_val &= ~mask;
31303bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB);
31403bdc388SHou Zhiqiang raw_spin_unlock_irqrestore(&rp->intx_mask_lock, flags);
31503bdc388SHou Zhiqiang }
31603bdc388SHou Zhiqiang
mobiveil_unmask_intx_irq(struct irq_data * data)31703bdc388SHou Zhiqiang static void mobiveil_unmask_intx_irq(struct irq_data *data)
31803bdc388SHou Zhiqiang {
319b8fecfdfSThomas Gleixner struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data);
32003bdc388SHou Zhiqiang struct mobiveil_root_port *rp;
32103bdc388SHou Zhiqiang unsigned long flags;
32203bdc388SHou Zhiqiang u32 shifted_val, mask;
32303bdc388SHou Zhiqiang
32403bdc388SHou Zhiqiang rp = &pcie->rp;
32503bdc388SHou Zhiqiang mask = 1 << ((data->hwirq + PAB_INTX_START) - 1);
32603bdc388SHou Zhiqiang raw_spin_lock_irqsave(&rp->intx_mask_lock, flags);
32703bdc388SHou Zhiqiang shifted_val = mobiveil_csr_readl(pcie, PAB_INTP_AMBA_MISC_ENB);
32803bdc388SHou Zhiqiang shifted_val |= mask;
32903bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, shifted_val, PAB_INTP_AMBA_MISC_ENB);
33003bdc388SHou Zhiqiang raw_spin_unlock_irqrestore(&rp->intx_mask_lock, flags);
33103bdc388SHou Zhiqiang }
33203bdc388SHou Zhiqiang
33303bdc388SHou Zhiqiang static struct irq_chip intx_irq_chip = {
33403bdc388SHou Zhiqiang .name = "mobiveil_pcie:intx",
33503bdc388SHou Zhiqiang .irq_enable = mobiveil_unmask_intx_irq,
33603bdc388SHou Zhiqiang .irq_disable = mobiveil_mask_intx_irq,
33703bdc388SHou Zhiqiang .irq_mask = mobiveil_mask_intx_irq,
33803bdc388SHou Zhiqiang .irq_unmask = mobiveil_unmask_intx_irq,
33903bdc388SHou Zhiqiang };
34003bdc388SHou Zhiqiang
34103bdc388SHou Zhiqiang /* routine to setup the INTx related data */
mobiveil_pcie_intx_map(struct irq_domain * domain,unsigned int irq,irq_hw_number_t hwirq)34203bdc388SHou Zhiqiang static int mobiveil_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
34303bdc388SHou Zhiqiang irq_hw_number_t hwirq)
34403bdc388SHou Zhiqiang {
34503bdc388SHou Zhiqiang irq_set_chip_and_handler(irq, &intx_irq_chip, handle_level_irq);
34603bdc388SHou Zhiqiang irq_set_chip_data(irq, domain->host_data);
34703bdc388SHou Zhiqiang
34803bdc388SHou Zhiqiang return 0;
34903bdc388SHou Zhiqiang }
35003bdc388SHou Zhiqiang
35103bdc388SHou Zhiqiang /* INTx domain operations structure */
35203bdc388SHou Zhiqiang static const struct irq_domain_ops intx_domain_ops = {
35303bdc388SHou Zhiqiang .map = mobiveil_pcie_intx_map,
35403bdc388SHou Zhiqiang };
35503bdc388SHou Zhiqiang
35603bdc388SHou Zhiqiang static struct irq_chip mobiveil_msi_irq_chip = {
35703bdc388SHou Zhiqiang .name = "Mobiveil PCIe MSI",
35803bdc388SHou Zhiqiang .irq_mask = pci_msi_mask_irq,
35903bdc388SHou Zhiqiang .irq_unmask = pci_msi_unmask_irq,
36003bdc388SHou Zhiqiang };
36103bdc388SHou Zhiqiang
36203bdc388SHou Zhiqiang static struct msi_domain_info mobiveil_msi_domain_info = {
36303bdc388SHou Zhiqiang .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
36403bdc388SHou Zhiqiang MSI_FLAG_PCI_MSIX),
36503bdc388SHou Zhiqiang .chip = &mobiveil_msi_irq_chip,
36603bdc388SHou Zhiqiang };
36703bdc388SHou Zhiqiang
mobiveil_compose_msi_msg(struct irq_data * data,struct msi_msg * msg)36803bdc388SHou Zhiqiang static void mobiveil_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
36903bdc388SHou Zhiqiang {
37003bdc388SHou Zhiqiang struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(data);
37103bdc388SHou Zhiqiang phys_addr_t addr = pcie->pcie_reg_base + (data->hwirq * sizeof(int));
37203bdc388SHou Zhiqiang
37303bdc388SHou Zhiqiang msg->address_lo = lower_32_bits(addr);
37403bdc388SHou Zhiqiang msg->address_hi = upper_32_bits(addr);
37503bdc388SHou Zhiqiang msg->data = data->hwirq;
37603bdc388SHou Zhiqiang
37703bdc388SHou Zhiqiang dev_dbg(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n",
37803bdc388SHou Zhiqiang (int)data->hwirq, msg->address_hi, msg->address_lo);
37903bdc388SHou Zhiqiang }
38003bdc388SHou Zhiqiang
mobiveil_msi_set_affinity(struct irq_data * irq_data,const struct cpumask * mask,bool force)38103bdc388SHou Zhiqiang static int mobiveil_msi_set_affinity(struct irq_data *irq_data,
38203bdc388SHou Zhiqiang const struct cpumask *mask, bool force)
38303bdc388SHou Zhiqiang {
38403bdc388SHou Zhiqiang return -EINVAL;
38503bdc388SHou Zhiqiang }
38603bdc388SHou Zhiqiang
38703bdc388SHou Zhiqiang static struct irq_chip mobiveil_msi_bottom_irq_chip = {
38803bdc388SHou Zhiqiang .name = "Mobiveil MSI",
38903bdc388SHou Zhiqiang .irq_compose_msi_msg = mobiveil_compose_msi_msg,
39003bdc388SHou Zhiqiang .irq_set_affinity = mobiveil_msi_set_affinity,
39103bdc388SHou Zhiqiang };
39203bdc388SHou Zhiqiang
mobiveil_irq_msi_domain_alloc(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs,void * args)39303bdc388SHou Zhiqiang static int mobiveil_irq_msi_domain_alloc(struct irq_domain *domain,
39403bdc388SHou Zhiqiang unsigned int virq,
39503bdc388SHou Zhiqiang unsigned int nr_irqs, void *args)
39603bdc388SHou Zhiqiang {
39703bdc388SHou Zhiqiang struct mobiveil_pcie *pcie = domain->host_data;
39803bdc388SHou Zhiqiang struct mobiveil_msi *msi = &pcie->rp.msi;
39903bdc388SHou Zhiqiang unsigned long bit;
40003bdc388SHou Zhiqiang
40103bdc388SHou Zhiqiang WARN_ON(nr_irqs != 1);
40203bdc388SHou Zhiqiang mutex_lock(&msi->lock);
40303bdc388SHou Zhiqiang
40403bdc388SHou Zhiqiang bit = find_first_zero_bit(msi->msi_irq_in_use, msi->num_of_vectors);
40503bdc388SHou Zhiqiang if (bit >= msi->num_of_vectors) {
40603bdc388SHou Zhiqiang mutex_unlock(&msi->lock);
40703bdc388SHou Zhiqiang return -ENOSPC;
40803bdc388SHou Zhiqiang }
40903bdc388SHou Zhiqiang
41003bdc388SHou Zhiqiang set_bit(bit, msi->msi_irq_in_use);
41103bdc388SHou Zhiqiang
41203bdc388SHou Zhiqiang mutex_unlock(&msi->lock);
41303bdc388SHou Zhiqiang
41403bdc388SHou Zhiqiang irq_domain_set_info(domain, virq, bit, &mobiveil_msi_bottom_irq_chip,
41503bdc388SHou Zhiqiang domain->host_data, handle_level_irq, NULL, NULL);
41603bdc388SHou Zhiqiang return 0;
41703bdc388SHou Zhiqiang }
41803bdc388SHou Zhiqiang
mobiveil_irq_msi_domain_free(struct irq_domain * domain,unsigned int virq,unsigned int nr_irqs)41903bdc388SHou Zhiqiang static void mobiveil_irq_msi_domain_free(struct irq_domain *domain,
42003bdc388SHou Zhiqiang unsigned int virq,
42103bdc388SHou Zhiqiang unsigned int nr_irqs)
42203bdc388SHou Zhiqiang {
42303bdc388SHou Zhiqiang struct irq_data *d = irq_domain_get_irq_data(domain, virq);
42403bdc388SHou Zhiqiang struct mobiveil_pcie *pcie = irq_data_get_irq_chip_data(d);
42503bdc388SHou Zhiqiang struct mobiveil_msi *msi = &pcie->rp.msi;
42603bdc388SHou Zhiqiang
42703bdc388SHou Zhiqiang mutex_lock(&msi->lock);
42803bdc388SHou Zhiqiang
42903bdc388SHou Zhiqiang if (!test_bit(d->hwirq, msi->msi_irq_in_use))
43003bdc388SHou Zhiqiang dev_err(&pcie->pdev->dev, "trying to free unused MSI#%lu\n",
43103bdc388SHou Zhiqiang d->hwirq);
43203bdc388SHou Zhiqiang else
43303bdc388SHou Zhiqiang __clear_bit(d->hwirq, msi->msi_irq_in_use);
43403bdc388SHou Zhiqiang
43503bdc388SHou Zhiqiang mutex_unlock(&msi->lock);
43603bdc388SHou Zhiqiang }
43703bdc388SHou Zhiqiang static const struct irq_domain_ops msi_domain_ops = {
43803bdc388SHou Zhiqiang .alloc = mobiveil_irq_msi_domain_alloc,
43903bdc388SHou Zhiqiang .free = mobiveil_irq_msi_domain_free,
44003bdc388SHou Zhiqiang };
44103bdc388SHou Zhiqiang
mobiveil_allocate_msi_domains(struct mobiveil_pcie * pcie)44203bdc388SHou Zhiqiang static int mobiveil_allocate_msi_domains(struct mobiveil_pcie *pcie)
44303bdc388SHou Zhiqiang {
44403bdc388SHou Zhiqiang struct device *dev = &pcie->pdev->dev;
44503bdc388SHou Zhiqiang struct fwnode_handle *fwnode = of_node_to_fwnode(dev->of_node);
44603bdc388SHou Zhiqiang struct mobiveil_msi *msi = &pcie->rp.msi;
44703bdc388SHou Zhiqiang
44803bdc388SHou Zhiqiang mutex_init(&msi->lock);
44903bdc388SHou Zhiqiang msi->dev_domain = irq_domain_add_linear(NULL, msi->num_of_vectors,
45003bdc388SHou Zhiqiang &msi_domain_ops, pcie);
45103bdc388SHou Zhiqiang if (!msi->dev_domain) {
45203bdc388SHou Zhiqiang dev_err(dev, "failed to create IRQ domain\n");
45303bdc388SHou Zhiqiang return -ENOMEM;
45403bdc388SHou Zhiqiang }
45503bdc388SHou Zhiqiang
45603bdc388SHou Zhiqiang msi->msi_domain = pci_msi_create_irq_domain(fwnode,
45703bdc388SHou Zhiqiang &mobiveil_msi_domain_info,
45803bdc388SHou Zhiqiang msi->dev_domain);
45903bdc388SHou Zhiqiang if (!msi->msi_domain) {
46003bdc388SHou Zhiqiang dev_err(dev, "failed to create MSI domain\n");
46103bdc388SHou Zhiqiang irq_domain_remove(msi->dev_domain);
46203bdc388SHou Zhiqiang return -ENOMEM;
46303bdc388SHou Zhiqiang }
46403bdc388SHou Zhiqiang
46503bdc388SHou Zhiqiang return 0;
46603bdc388SHou Zhiqiang }
46703bdc388SHou Zhiqiang
mobiveil_pcie_init_irq_domain(struct mobiveil_pcie * pcie)46803bdc388SHou Zhiqiang static int mobiveil_pcie_init_irq_domain(struct mobiveil_pcie *pcie)
46903bdc388SHou Zhiqiang {
47003bdc388SHou Zhiqiang struct device *dev = &pcie->pdev->dev;
47103bdc388SHou Zhiqiang struct device_node *node = dev->of_node;
47203bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
47303bdc388SHou Zhiqiang
47403bdc388SHou Zhiqiang /* setup INTx */
47503bdc388SHou Zhiqiang rp->intx_domain = irq_domain_add_linear(node, PCI_NUM_INTX,
47603bdc388SHou Zhiqiang &intx_domain_ops, pcie);
47703bdc388SHou Zhiqiang
47803bdc388SHou Zhiqiang if (!rp->intx_domain) {
47903bdc388SHou Zhiqiang dev_err(dev, "Failed to get a INTx IRQ domain\n");
48003bdc388SHou Zhiqiang return -ENOMEM;
48103bdc388SHou Zhiqiang }
48203bdc388SHou Zhiqiang
48303bdc388SHou Zhiqiang raw_spin_lock_init(&rp->intx_mask_lock);
48403bdc388SHou Zhiqiang
48503bdc388SHou Zhiqiang /* setup MSI */
486df7fc055SLiu Shixin return mobiveil_allocate_msi_domains(pcie);
48703bdc388SHou Zhiqiang }
48803bdc388SHou Zhiqiang
mobiveil_pcie_integrated_interrupt_init(struct mobiveil_pcie * pcie)489ed620e96SHou Zhiqiang static int mobiveil_pcie_integrated_interrupt_init(struct mobiveil_pcie *pcie)
49003bdc388SHou Zhiqiang {
49103bdc388SHou Zhiqiang struct platform_device *pdev = pcie->pdev;
49203bdc388SHou Zhiqiang struct device *dev = &pdev->dev;
49303bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
49403bdc388SHou Zhiqiang struct resource *res;
49503bdc388SHou Zhiqiang int ret;
49603bdc388SHou Zhiqiang
49703bdc388SHou Zhiqiang /* map MSI config resource */
49803bdc388SHou Zhiqiang res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apb_csr");
49903bdc388SHou Zhiqiang pcie->apb_csr_base = devm_pci_remap_cfg_resource(dev, res);
50003bdc388SHou Zhiqiang if (IS_ERR(pcie->apb_csr_base))
50103bdc388SHou Zhiqiang return PTR_ERR(pcie->apb_csr_base);
50203bdc388SHou Zhiqiang
50303bdc388SHou Zhiqiang /* setup MSI hardware registers */
50403bdc388SHou Zhiqiang mobiveil_pcie_enable_msi(pcie);
50503bdc388SHou Zhiqiang
50603bdc388SHou Zhiqiang rp->irq = platform_get_irq(pdev, 0);
507caecb05cSKrzysztof Wilczyński if (rp->irq < 0)
5080584bff0SAman Sharma return rp->irq;
50903bdc388SHou Zhiqiang
51003bdc388SHou Zhiqiang /* initialize the IRQ domains */
51103bdc388SHou Zhiqiang ret = mobiveil_pcie_init_irq_domain(pcie);
51203bdc388SHou Zhiqiang if (ret) {
51303bdc388SHou Zhiqiang dev_err(dev, "Failed creating IRQ Domain\n");
51403bdc388SHou Zhiqiang return ret;
51503bdc388SHou Zhiqiang }
51603bdc388SHou Zhiqiang
51703bdc388SHou Zhiqiang irq_set_chained_handler_and_data(rp->irq, mobiveil_pcie_isr, pcie);
51803bdc388SHou Zhiqiang
51903bdc388SHou Zhiqiang /* Enable interrupts */
52003bdc388SHou Zhiqiang mobiveil_csr_writel(pcie, (PAB_INTP_INTX_MASK | PAB_INTP_MSI_MASK),
52103bdc388SHou Zhiqiang PAB_INTP_AMBA_MISC_ENB);
52203bdc388SHou Zhiqiang
52303bdc388SHou Zhiqiang
52403bdc388SHou Zhiqiang return 0;
52503bdc388SHou Zhiqiang }
52603bdc388SHou Zhiqiang
mobiveil_pcie_interrupt_init(struct mobiveil_pcie * pcie)527ed620e96SHou Zhiqiang static int mobiveil_pcie_interrupt_init(struct mobiveil_pcie *pcie)
528ed620e96SHou Zhiqiang {
529ed620e96SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
530ed620e96SHou Zhiqiang
531ed620e96SHou Zhiqiang if (rp->ops->interrupt_init)
532ed620e96SHou Zhiqiang return rp->ops->interrupt_init(pcie);
533ed620e96SHou Zhiqiang
534ed620e96SHou Zhiqiang return mobiveil_pcie_integrated_interrupt_init(pcie);
535ed620e96SHou Zhiqiang }
536ed620e96SHou Zhiqiang
mobiveil_pcie_is_bridge(struct mobiveil_pcie * pcie)53711d22cc3SHou Zhiqiang static bool mobiveil_pcie_is_bridge(struct mobiveil_pcie *pcie)
53811d22cc3SHou Zhiqiang {
53911d22cc3SHou Zhiqiang u32 header_type;
54011d22cc3SHou Zhiqiang
54111d22cc3SHou Zhiqiang header_type = mobiveil_csr_readb(pcie, PCI_HEADER_TYPE);
54211d22cc3SHou Zhiqiang header_type &= 0x7f;
54311d22cc3SHou Zhiqiang
54411d22cc3SHou Zhiqiang return header_type == PCI_HEADER_TYPE_BRIDGE;
54511d22cc3SHou Zhiqiang }
54611d22cc3SHou Zhiqiang
mobiveil_pcie_host_probe(struct mobiveil_pcie * pcie)54703bdc388SHou Zhiqiang int mobiveil_pcie_host_probe(struct mobiveil_pcie *pcie)
54803bdc388SHou Zhiqiang {
54903bdc388SHou Zhiqiang struct mobiveil_root_port *rp = &pcie->rp;
55003bdc388SHou Zhiqiang struct pci_host_bridge *bridge = rp->bridge;
55103bdc388SHou Zhiqiang struct device *dev = &pcie->pdev->dev;
55203bdc388SHou Zhiqiang int ret;
55303bdc388SHou Zhiqiang
55403bdc388SHou Zhiqiang ret = mobiveil_pcie_parse_dt(pcie);
55503bdc388SHou Zhiqiang if (ret) {
55603bdc388SHou Zhiqiang dev_err(dev, "Parsing DT failed, ret: %x\n", ret);
55703bdc388SHou Zhiqiang return ret;
55803bdc388SHou Zhiqiang }
55903bdc388SHou Zhiqiang
56011d22cc3SHou Zhiqiang if (!mobiveil_pcie_is_bridge(pcie))
56111d22cc3SHou Zhiqiang return -ENODEV;
56211d22cc3SHou Zhiqiang
56303bdc388SHou Zhiqiang /*
56403bdc388SHou Zhiqiang * configure all inbound and outbound windows and prepare the RC for
56503bdc388SHou Zhiqiang * config access
56603bdc388SHou Zhiqiang */
56752cae4c7SHou Zhiqiang ret = mobiveil_host_init(pcie, false);
56803bdc388SHou Zhiqiang if (ret) {
56903bdc388SHou Zhiqiang dev_err(dev, "Failed to initialize host\n");
57003bdc388SHou Zhiqiang return ret;
57103bdc388SHou Zhiqiang }
57203bdc388SHou Zhiqiang
57303bdc388SHou Zhiqiang ret = mobiveil_pcie_interrupt_init(pcie);
57403bdc388SHou Zhiqiang if (ret) {
57503bdc388SHou Zhiqiang dev_err(dev, "Interrupt init failed\n");
57603bdc388SHou Zhiqiang return ret;
57703bdc388SHou Zhiqiang }
57803bdc388SHou Zhiqiang
57903bdc388SHou Zhiqiang /* Initialize bridge */
58003bdc388SHou Zhiqiang bridge->sysdata = pcie;
58103bdc388SHou Zhiqiang bridge->ops = &mobiveil_pcie_ops;
58203bdc388SHou Zhiqiang
58303bdc388SHou Zhiqiang ret = mobiveil_bringup_link(pcie);
58403bdc388SHou Zhiqiang if (ret) {
58503bdc388SHou Zhiqiang dev_info(dev, "link bring-up failed\n");
58603bdc388SHou Zhiqiang return ret;
58703bdc388SHou Zhiqiang }
58803bdc388SHou Zhiqiang
5891f9b7512SRob Herring return pci_host_probe(bridge);
59003bdc388SHou Zhiqiang }
591