int err, res_valid = 0;
struct device *dev = &pcie->pdev->dev;
struct device_node *np = dev->of_node;
---------- struct resource_entry *win;
++++++++++ struct resource_entry *win, *tmp;
resource_size_t iobase;
INIT_LIST_HEAD(&pcie->resources);
if (err)
goto out_release_res;
---------- resource_list_for_each_entry(win, &pcie->resources) {
++++++++++ resource_list_for_each_entry_safe(win, tmp, &pcie->resources) {
struct resource *res = win->res;
switch (resource_type(res)) {
lower_32_bits(res->start),
OB_PCIE_IO);
err = pci_remap_iospace(res, iobase);
---------- if (err)
++++++++++ if (err) {
dev_warn(dev, "error %d: failed to map resource %pR\n",
err, res);
++++++++++ resource_list_destroy_entry(win);
++++++++++ }
break;
case IORESOURCE_MEM:
advk_pcie_set_ob_win(pcie, 0,
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
pcie->base = devm_ioremap_resource(&pdev->dev, res);
- --------- if (IS_ERR(pcie->base)) {
- --------- dev_err(&pdev->dev, "Failed to map registers\n");
+ +++++++++ if (IS_ERR(pcie->base))
return PTR_ERR(pcie->base);
- --------- }
irq = platform_get_irq(pdev, 0);
ret = devm_request_irq(&pdev->dev, irq, advk_pcie_irq_handler,
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
----------#include <linux/module.h>
++++++++++#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
exynos_pcie_msi_init(pp);
}
---- ------static inline void exynos_pcie_readl_rc(struct pcie_port *pp,
---- ------ void __iomem *dbi_base, u32 *val)
++++ ++++++static inline u32 exynos_pcie_readl_rc(struct pcie_port *pp,
++++ ++++++ void __iomem *dbi_base)
{
++++ ++++++ u32 val;
++++ ++++++
exynos_pcie_sideband_dbi_r_mode(pp, true);
---- ------ *val = readl(dbi_base);
++++ ++++++ val = readl(dbi_base);
exynos_pcie_sideband_dbi_r_mode(pp, false);
++++ ++++++ return val;
}
static inline void exynos_pcie_writel_rc(struct pcie_port *pp,
{ .compatible = "samsung,exynos5440-pcie", },
{},
};
----------MODULE_DEVICE_TABLE(of, exynos_pcie_of_match);
static struct platform_driver exynos_pcie_driver = {
.remove = __exit_p(exynos_pcie_remove),
return platform_driver_probe(&exynos_pcie_driver, exynos_pcie_probe);
}
subsys_initcall(exynos_pcie_init);
----------
----------MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
----------MODULE_DESCRIPTION("Samsung PCIe host controller driver");
----------MODULE_LICENSE("GPL v2");
};
/* used to differentiate between Tegra SoC generations */
--------- -struct tegra_pcie_soc_data {
+++++++++ +struct tegra_pcie_soc {
unsigned int num_ports;
unsigned int msi_base_shift;
u32 pads_pll_ctl;
struct regulator_bulk_data *supplies;
unsigned int num_supplies;
--------- - const struct tegra_pcie_soc_data *soc_data;
+++++++++ + const struct tegra_pcie_soc *soc;
struct dentry *debugfs;
};
static void tegra_pcie_port_enable(struct tegra_pcie_port *port)
{
--------- - const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+++++++++ + const struct tegra_pcie_soc *soc = port->pcie->soc;
unsigned long value;
/* enable reference clock */
static void tegra_pcie_port_disable(struct tegra_pcie_port *port)
{
--------- - const struct tegra_pcie_soc_data *soc = port->pcie->soc_data;
unsigned long ctrl = tegra_pcie_port_get_pex_ctrl(port);
+++++++++ + const struct tegra_pcie_soc *soc = port->pcie->soc;
unsigned long value;
/* assert port reset */
if (err < 0)
return err;
---------- pci_add_resource_offset(&sys->resources, &pcie->pio, sys->io_offset);
++++++++++ err = pci_remap_iospace(&pcie->pio, pcie->io.start);
++++++++++ if (!err)
++++++++++ pci_add_resource_offset(&sys->resources, &pcie->pio,
++++++++++ sys->io_offset);
++++++++++
pci_add_resource_offset(&sys->resources, &pcie->mem, sys->mem_offset);
pci_add_resource_offset(&sys->resources, &pcie->prefetch,
sys->mem_offset);
if (err < 0)
return err;
---------- pci_remap_iospace(&pcie->pio, pcie->io.start);
return 1;
}
static int tegra_pcie_pll_wait(struct tegra_pcie *pcie, unsigned long timeout)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
u32 value;
timeout = jiffies + msecs_to_jiffies(timeout);
static int tegra_pcie_phy_enable(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
u32 value;
int err;
static int tegra_pcie_phy_disable(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
u32 value;
/* disable TX/RX data */
static int tegra_pcie_phy_power_on(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
struct tegra_pcie_port *port;
int err;
static int tegra_pcie_enable_controller(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
struct tegra_pcie_port *port;
unsigned long value;
int err;
static int tegra_pcie_power_on(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
int err;
reset_control_assert(pcie->pcie_xrst);
static int tegra_pcie_clocks_get(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
pcie->pex_clk = devm_clk_get(pcie->dev, "pex");
if (IS_ERR(pcie->pex_clk))
static int tegra_pcie_phys_get(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
struct device_node *np = pcie->dev->of_node;
struct tegra_pcie_port *port;
int err;
static int tegra_pcie_enable_msi(struct tegra_pcie *pcie)
{
struct platform_device *pdev = to_platform_device(pcie->dev);
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
struct tegra_msi *msi = &pcie->msi;
unsigned long base;
int err;
static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
{
--------- - const struct tegra_pcie_soc_data *soc = pcie->soc_data;
struct device_node *np = pcie->dev->of_node, *port;
+++++++++ + const struct tegra_pcie_soc *soc = pcie->soc;
struct of_pci_range_parser parser;
struct of_pci_range range;
u32 lanes = 0, mask = 0;
return 0;
}
--------- -static const struct tegra_pcie_soc_data tegra20_pcie_data = {
+++++++++ +static const struct tegra_pcie_soc tegra20_pcie = {
.num_ports = 2,
.msi_base_shift = 0,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA20,
.has_gen2 = false,
};
--------- -static const struct tegra_pcie_soc_data tegra30_pcie_data = {
+++++++++ +static const struct tegra_pcie_soc tegra30_pcie = {
.num_ports = 3,
.msi_base_shift = 8,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
.has_gen2 = false,
};
--------- -static const struct tegra_pcie_soc_data tegra124_pcie_data = {
+++++++++ +static const struct tegra_pcie_soc tegra124_pcie = {
.num_ports = 2,
.msi_base_shift = 8,
.pads_pll_ctl = PADS_PLL_CTL_TEGRA30,
};
static const struct of_device_id tegra_pcie_of_match[] = {
--------- - { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie_data },
--------- - { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie_data },
--------- - { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie_data },
+++++++++ + { .compatible = "nvidia,tegra124-pcie", .data = &tegra124_pcie },
+++++++++ + { .compatible = "nvidia,tegra30-pcie", .data = &tegra30_pcie },
+++++++++ + { .compatible = "nvidia,tegra20-pcie", .data = &tegra20_pcie },
{ },
};
static int tegra_pcie_probe(struct platform_device *pdev)
{
--------- - const struct of_device_id *match;
struct tegra_pcie *pcie;
int err;
--------- - match = of_match_device(tegra_pcie_of_match, &pdev->dev);
--------- - if (!match)
--------- - return -ENODEV;
--------- -
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
if (!pcie)
return -ENOMEM;
+++++++++ + pcie->soc = of_device_get_match_data(&pdev->dev);
INIT_LIST_HEAD(&pcie->buses);
INIT_LIST_HEAD(&pcie->ports);
--------- - pcie->soc_data = match->data;
pcie->dev = &pdev->dev;
err = tegra_pcie_parse_dt(pcie);
/*
++++++++++ * Altera PCIe MSI support
++++++++++ *
++++++++++ * Author: Ley Foon Tan <lftan@altera.com>
++++++++++ *
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
*
* This program is free software; you can redistribute it and/or modify it
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
----------#include <linux/module.h>
++++++++++#include <linux/init.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
msi->pdev = pdev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr");
-- -------- if (!res) {
-- -------- dev_err(&pdev->dev, "no csr memory resource defined\n");
-- -------- return -ENODEV;
-- -------- }
-- --------
msi->csr_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(msi->csr_base)) {
dev_err(&pdev->dev, "failed to map csr memory\n");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"vector_slave");
-- -------- if (!res) {
-- -------- dev_err(&pdev->dev, "no vector_slave memory resource defined\n");
-- -------- return -ENODEV;
-- -------- }
-- --------
msi->vector_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(msi->vector_base)) {
dev_err(&pdev->dev, "failed to map vector_slave memory\n");
return platform_driver_register(&altera_msi_driver);
}
subsys_initcall(altera_msi_init);
----------
----------MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
----------MODULE_DESCRIPTION("Altera PCIe MSI support");
----------MODULE_LICENSE("GPL v2");
/*
* Copyright Altera Corporation (C) 2013-2015. All rights reserved
*
++++++++++ * Author: Ley Foon Tan <lftan@altera.com>
++++++++++ * Description: Altera PCIe host controller driver
++++++++++ *
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
----------#include <linux/module.h>
++++++++++#include <linux/init.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_pci.h>
#define RP_LTSSM_MASK 0x1f
#define LTSSM_L0 0xf
++ ++++++++#define PCIE_CAP_OFFSET 0x80
/* TLP configuration type 0 and 1 */
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
#define TLP_LOOP 500
#define RP_DEVFN 0
-- --------#define LINK_UP_TIMEOUT 5000
++ ++++++++#define LINK_UP_TIMEOUT HZ
++ ++++++++#define LINK_RETRAIN_TIMEOUT HZ
#define INTX_NUM 4
return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0);
}
-- --------static void altera_pcie_retrain(struct pci_dev *dev)
-- --------{
-- -------- u16 linkcap, linkstat;
-- -------- struct altera_pcie *pcie = dev->bus->sysdata;
-- -------- int timeout = 0;
-- --------
-- -------- if (!altera_pcie_link_is_up(pcie))
-- -------- return;
-- --------
-- -------- /*
-- -------- * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
-- -------- * current speed is 2.5 GB/s.
-- -------- */
-- -------- pcie_capability_read_word(dev, PCI_EXP_LNKCAP, &linkcap);
-- --------
-- -------- if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
-- -------- return;
-- --------
-- -------- pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &linkstat);
-- -------- if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
-- -------- pcie_capability_set_word(dev, PCI_EXP_LNKCTL,
-- -------- PCI_EXP_LNKCTL_RL);
-- -------- while (!altera_pcie_link_is_up(pcie)) {
-- -------- timeout++;
-- -------- if (timeout > LINK_UP_TIMEOUT)
-- -------- break;
-- -------- udelay(5);
-- -------- }
-- -------- }
-- --------}
-- --------DECLARE_PCI_FIXUP_EARLY(0x1172, PCI_ANY_ID, altera_pcie_retrain);
-- --------
/*
* Altera PCIe port uses BAR0 of RC's configuration space as the translation
* from PCI bus to native BUS. Entire DDR region is mapped into PCIe space
if (bus->number == pcie->root_bus_nr && dev > 0)
return false;
---------- /*
---------- * Do not read more than one device on the bus directly attached
---------- * to root port, root port can only attach to one downstream port.
---------- */
---------- if (bus->primary == pcie->root_bus_nr && dev > 0)
---------- return false;
----------
return true;
}
return PCIBIOS_SUCCESSFUL;
}
-- --------static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
-- -------- int where, int size, u32 *value)
++ ++++++++static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno,
++ ++++++++ unsigned int devfn, int where, int size,
++ ++++++++ u32 *value)
{
-- -------- struct altera_pcie *pcie = bus->sysdata;
int ret;
u32 data;
u8 byte_en;
-- -------- if (altera_pcie_hide_rc_bar(bus, devfn, where))
-- -------- return PCIBIOS_BAD_REGISTER_NUMBER;
-- --------
-- -------- if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
-- -------- *value = 0xffffffff;
-- -------- return PCIBIOS_DEVICE_NOT_FOUND;
-- -------- }
-- --------
switch (size) {
case 1:
byte_en = 1 << (where & 3);
break;
}
-- -------- ret = tlp_cfg_dword_read(pcie, bus->number, devfn,
++ ++++++++ ret = tlp_cfg_dword_read(pcie, busno, devfn,
(where & ~DWORD_MASK), byte_en, &data);
if (ret != PCIBIOS_SUCCESSFUL)
return ret;
return PCIBIOS_SUCCESSFUL;
}
-- --------static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
-- -------- int where, int size, u32 value)
++ ++++++++static int _altera_pcie_cfg_write(struct altera_pcie *pcie, u8 busno,
++ ++++++++ unsigned int devfn, int where, int size,
++ ++++++++ u32 value)
{
-- -------- struct altera_pcie *pcie = bus->sysdata;
u32 data32;
u32 shift = 8 * (where & 3);
u8 byte_en;
-- -------- if (altera_pcie_hide_rc_bar(bus, devfn, where))
-- -------- return PCIBIOS_BAD_REGISTER_NUMBER;
-- --------
-- -------- if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
-- -------- return PCIBIOS_DEVICE_NOT_FOUND;
-- --------
switch (size) {
case 1:
data32 = (value & 0xff) << shift;
break;
}
-- -------- return tlp_cfg_dword_write(pcie, bus->number, devfn,
-- -------- (where & ~DWORD_MASK), byte_en, data32);
++ ++++++++ return tlp_cfg_dword_write(pcie, busno, devfn, (where & ~DWORD_MASK),
++ ++++++++ byte_en, data32);
++ ++++++++}
++ ++++++++
++ ++++++++static int altera_pcie_cfg_read(struct pci_bus *bus, unsigned int devfn,
++ ++++++++ int where, int size, u32 *value)
++ ++++++++{
++ ++++++++ struct altera_pcie *pcie = bus->sysdata;
++ ++++++++
++ ++++++++ if (altera_pcie_hide_rc_bar(bus, devfn, where))
++ ++++++++ return PCIBIOS_BAD_REGISTER_NUMBER;
++ ++++++++
++ ++++++++ if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn))) {
++ ++++++++ *value = 0xffffffff;
++ ++++++++ return PCIBIOS_DEVICE_NOT_FOUND;
++ ++++++++ }
++ ++++++++
++ ++++++++ return _altera_pcie_cfg_read(pcie, bus->number, devfn, where, size,
++ ++++++++ value);
++ ++++++++}
++ ++++++++
++ ++++++++static int altera_pcie_cfg_write(struct pci_bus *bus, unsigned int devfn,
++ ++++++++ int where, int size, u32 value)
++ ++++++++{
++ ++++++++ struct altera_pcie *pcie = bus->sysdata;
++ ++++++++
++ ++++++++ if (altera_pcie_hide_rc_bar(bus, devfn, where))
++ ++++++++ return PCIBIOS_BAD_REGISTER_NUMBER;
++ ++++++++
++ ++++++++ if (!altera_pcie_valid_config(pcie, bus, PCI_SLOT(devfn)))
++ ++++++++ return PCIBIOS_DEVICE_NOT_FOUND;
++ ++++++++
++ ++++++++ return _altera_pcie_cfg_write(pcie, bus->number, devfn, where, size,
++ ++++++++ value);
}
static struct pci_ops altera_pcie_ops = {
.write = altera_pcie_cfg_write,
};
++ ++++++++static int altera_read_cap_word(struct altera_pcie *pcie, u8 busno,
++ ++++++++ unsigned int devfn, int offset, u16 *value)
++ ++++++++{
++ ++++++++ u32 data;
++ ++++++++ int ret;
++ ++++++++
++ ++++++++ ret = _altera_pcie_cfg_read(pcie, busno, devfn,
++ ++++++++ PCIE_CAP_OFFSET + offset, sizeof(*value),
++ ++++++++ &data);
++ ++++++++ *value = data;
++ ++++++++ return ret;
++ ++++++++}
++ ++++++++
++ ++++++++static int altera_write_cap_word(struct altera_pcie *pcie, u8 busno,
++ ++++++++ unsigned int devfn, int offset, u16 value)
++ ++++++++{
++ ++++++++ return _altera_pcie_cfg_write(pcie, busno, devfn,
++ ++++++++ PCIE_CAP_OFFSET + offset, sizeof(value),
++ ++++++++ value);
++ ++++++++}
++ ++++++++
++ ++++++++static void altera_wait_link_retrain(struct altera_pcie *pcie)
++ ++++++++{
++ ++++++++ u16 reg16;
++ ++++++++ unsigned long start_jiffies;
++ ++++++++
++ ++++++++ /* Wait for link training end. */
++ ++++++++ start_jiffies = jiffies;
++ ++++++++ for (;;) {
++ ++++++++ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
++ ++++++++ PCI_EXP_LNKSTA, ®16);
++ ++++++++ if (!(reg16 & PCI_EXP_LNKSTA_LT))
++ ++++++++ break;
++ ++++++++
++ ++++++++ if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) {
++ ++++++++ dev_err(&pcie->pdev->dev, "link retrain timeout\n");
++ ++++++++ break;
++ ++++++++ }
++ ++++++++ udelay(100);
++ ++++++++ }
++ ++++++++
++ ++++++++ /* Wait for link is up */
++ ++++++++ start_jiffies = jiffies;
++ ++++++++ for (;;) {
++ ++++++++ if (altera_pcie_link_is_up(pcie))
++ ++++++++ break;
++ ++++++++
++ ++++++++ if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) {
++ ++++++++ dev_err(&pcie->pdev->dev, "link up timeout\n");
++ ++++++++ break;
++ ++++++++ }
++ ++++++++ udelay(100);
++ ++++++++ }
++ ++++++++}
++ ++++++++
++ ++++++++static void altera_pcie_retrain(struct altera_pcie *pcie)
++ ++++++++{
++ ++++++++ u16 linkcap, linkstat, linkctl;
++ ++++++++
++ ++++++++ if (!altera_pcie_link_is_up(pcie))
++ ++++++++ return;
++ ++++++++
++ ++++++++ /*
++ ++++++++ * Set the retrain bit if the PCIe rootport support > 2.5GB/s, but
++ ++++++++ * current speed is 2.5 GB/s.
++ ++++++++ */
++ ++++++++ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKCAP,
++ ++++++++ &linkcap);
++ ++++++++ if ((linkcap & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB)
++ ++++++++ return;
++ ++++++++
++ ++++++++ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN, PCI_EXP_LNKSTA,
++ ++++++++ &linkstat);
++ ++++++++ if ((linkstat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) {
++ ++++++++ altera_read_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
++ ++++++++ PCI_EXP_LNKCTL, &linkctl);
++ ++++++++ linkctl |= PCI_EXP_LNKCTL_RL;
++ ++++++++ altera_write_cap_word(pcie, pcie->root_bus_nr, RP_DEVFN,
++ ++++++++ PCI_EXP_LNKCTL, linkctl);
++ ++++++++
++ ++++++++ altera_wait_link_retrain(pcie);
++ ++++++++ }
++ ++++++++}
++ ++++++++
static int altera_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{
return 0;
}
++ ++++++++static void altera_pcie_host_init(struct altera_pcie *pcie)
++ ++++++++{
++ ++++++++ altera_pcie_retrain(pcie);
++ ++++++++}
++ ++++++++
static int altera_pcie_probe(struct platform_device *pdev)
{
struct altera_pcie *pcie;
cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
/* enable all interrupts */
cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE);
++ ++++++++ altera_pcie_host_init(pcie);
bus = pci_scan_root_bus(&pdev->dev, pcie->root_bus_nr, &altera_pcie_ops,
pcie, &pcie->resources);
{ .compatible = "altr,pcie-root-port-1.0", },
{},
};
----------MODULE_DEVICE_TABLE(of, altera_pcie_of_match);
static struct platform_driver altera_pcie_driver = {
.probe = altera_pcie_probe,
{
return platform_driver_register(&altera_pcie_driver);
}
----------module_init(altera_pcie_init);
----------
----------MODULE_AUTHOR("Ley Foon Tan <lftan@altera.com>");
----------MODULE_DESCRIPTION("Altera PCIe host controller driver");
----------MODULE_LICENSE("GPL v2");
++++++++++device_initcall(altera_pcie_init);
#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/of_pci.h>
#include "pcie-designware.h"
---- ------/* Synopsis specific PCIE configuration registers */
++++ ++++++/* Parameters for the waiting for link up routine */
++++ ++++++#define LINK_WAIT_MAX_RETRIES 10
++++ ++++++#define LINK_WAIT_USLEEP_MIN 90000
++++ ++++++#define LINK_WAIT_USLEEP_MAX 100000
++++ ++++++
++++ ++++++/* Parameters for the waiting for iATU enabled routine */
++++ ++++++#define LINK_WAIT_MAX_IATU_RETRIES 5
++++ ++++++#define LINK_WAIT_IATU_MIN 9000
++++ ++++++#define LINK_WAIT_IATU_MAX 10000
++++ ++++++
++++ ++++++/* Synopsys-specific PCIe configuration registers */
#define PCIE_PORT_LINK_CONTROL 0x710
#define PORT_LINK_MODE_MASK (0x3f << 16)
#define PORT_LINK_MODE_1_LANES (0x1 << 16)
#define PCIE_ATU_VIEWPORT 0x900
#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
++++ ++++++#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
#define PCIE_ATU_CR1 0x904
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
#define PCIE_ATU_UPPER_TARGET 0x91C
++++ ++++++/*
++++ ++++++ * iATU Unroll-specific register definitions
++++ ++++++ * From 4.80 core version the address translation will be made by unroll
++++ ++++++ */
++++ ++++++#define PCIE_ATU_UNR_REGION_CTRL1 0x00
++++ ++++++#define PCIE_ATU_UNR_REGION_CTRL2 0x04
++++ ++++++#define PCIE_ATU_UNR_LOWER_BASE 0x08
++++ ++++++#define PCIE_ATU_UNR_UPPER_BASE 0x0C
++++ ++++++#define PCIE_ATU_UNR_LIMIT 0x10
++++ ++++++#define PCIE_ATU_UNR_LOWER_TARGET 0x14
++++ ++++++#define PCIE_ATU_UNR_UPPER_TARGET 0x18
++++ ++++++
++++ ++++++/* Register address builder */
++++ ++++++#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((0x3 << 20) | (region << 9))
++++ ++++++
/* PCIe Port Logic registers */
#define PLR_OFFSET 0x700
#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
---- ------#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
++++ ++++++#define PCIE_PHY_DEBUG_R1_LINK_UP (0x1 << 4)
++++ ++++++#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (0x1 << 29)
static struct pci_ops dw_pcie_ops;
return PCIBIOS_SUCCESSFUL;
}
---- ------static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val)
++++ ++++++static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
{
if (pp->ops->readl_rc)
---- ------ pp->ops->readl_rc(pp, pp->dbi_base + reg, val);
---- ------ else
---- ------ *val = readl(pp->dbi_base + reg);
++++ ++++++ return pp->ops->readl_rc(pp, pp->dbi_base + reg);
++++ ++++++
++++ ++++++ return readl(pp->dbi_base + reg);
}
static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
writel(val, pp->dbi_base + reg);
}
++++ ++++++static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
++++ ++++++{
++++ ++++++ u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
++++ ++++++
++++ ++++++ if (pp->ops->readl_rc)
++++ ++++++ return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg);
++++ ++++++
++++ ++++++ return readl(pp->dbi_base + offset + reg);
++++ ++++++}
++++ ++++++
++++ ++++++static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
++++ ++++++ u32 val, u32 reg)
++++ ++++++{
++++ ++++++ u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
++++ ++++++
++++ ++++++ if (pp->ops->writel_rc)
++++ ++++++ pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg);
++++ ++++++ else
++++ ++++++ writel(val, pp->dbi_base + offset + reg);
++++ ++++++}
++++ ++++++
static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
int type, u64 cpu_addr, u64 pci_addr, u32 size)
{
---- ------ u32 val;
---- ------
---- ------ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
---- ------ PCIE_ATU_VIEWPORT);
---- ------ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
---- ------ dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE);
---- ------ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
---- ------ PCIE_ATU_LIMIT);
---- ------ dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET);
---- ------ dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
---- ------ dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
---- ------ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
++++ ++++++ u32 retries, val;
++++ ++++++
++++ ++++++ if (pp->iatu_unroll_enabled) {
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ type, PCIE_ATU_UNR_REGION_CTRL1);
++++ ++++++ dw_pcie_writel_unroll(pp, index,
++++ ++++++ PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
++++ ++++++ } else {
++++ ++++++ dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
++++ ++++++ PCIE_ATU_VIEWPORT);
++++ ++++++ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr),
++++ ++++++ PCIE_ATU_LOWER_BASE);
++++ ++++++ dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr),
++++ ++++++ PCIE_ATU_UPPER_BASE);
++++ ++++++ dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
++++ ++++++ PCIE_ATU_LIMIT);
++++ ++++++ dw_pcie_writel_rc(pp, lower_32_bits(pci_addr),
++++ ++++++ PCIE_ATU_LOWER_TARGET);
++++ ++++++ dw_pcie_writel_rc(pp, upper_32_bits(pci_addr),
++++ ++++++ PCIE_ATU_UPPER_TARGET);
++++ ++++++ dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
++++ ++++++ dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
++++ ++++++ }
/*
* Make sure ATU enable takes effect before any subsequent config
* and I/O accesses.
*/
---- ------ dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
++++ ++++++ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
++++ ++++++ if (pp->iatu_unroll_enabled)
++++ ++++++ val = dw_pcie_readl_unroll(pp, index,
++++ ++++++ PCIE_ATU_UNR_REGION_CTRL2);
++++ ++++++ else
++++ ++++++ val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2);
++++ ++++++
++++ ++++++ if (val == PCIE_ATU_ENABLE)
++++ ++++++ return;
++++ ++++++
++++ ++++++ usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
++++ ++++++ }
++++ ++++++ dev_err(pp->dev, "iATU is not being enabled\n");
}
static struct irq_chip dw_msi_irq_chip = {
return pp->ops->link_up(pp);
val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
---- ------ return val & PCIE_PHY_DEBUG_R1_LINK_UP;
++++ ++++++ return ((val & PCIE_PHY_DEBUG_R1_LINK_UP) &&
++++ ++++++ (!(val & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING)));
}
static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
.map = dw_pcie_msi_map,
};
++++ ++++++static u8 dw_pcie_iatu_unroll_enabled(struct pcie_port *pp)
++++ ++++++{
++++ ++++++ u32 val;
++++ ++++++
++++ ++++++ val = dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT);
++++ ++++++ if (val == 0xffffffff)
++++ ++++++ return 1;
++++ ++++++
++++ ++++++ return 0;
++++ ++++++}
++++ ++++++
int dw_pcie_host_init(struct pcie_port *pp)
{
struct device_node *np = pp->dev->of_node;
struct resource *cfg_res;
int i, ret;
LIST_HEAD(res);
---------- struct resource_entry *win;
++++++++++ struct resource_entry *win, *tmp;
cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
if (cfg_res) {
goto error;
/* Get the I/O and memory ranges from DT */
---------- resource_list_for_each_entry(win, &res) {
++++++++++ resource_list_for_each_entry_safe(win, tmp, &res) {
switch (resource_type(win->res)) {
case IORESOURCE_IO:
---------- pp->io = win->res;
---------- pp->io->name = "I/O";
---------- pp->io_size = resource_size(pp->io);
---------- pp->io_bus_addr = pp->io->start - win->offset;
---------- ret = pci_remap_iospace(pp->io, pp->io_base);
---------- if (ret)
++++++++++ ret = pci_remap_iospace(win->res, pp->io_base);
++++++++++ if (ret) {
dev_warn(pp->dev, "error %d: failed to map resource %pR\n",
---------- ret, pp->io);
++++++++++ ret, win->res);
++++++++++ resource_list_destroy_entry(win);
++++++++++ } else {
++++++++++ pp->io = win->res;
++++++++++ pp->io->name = "I/O";
++++++++++ pp->io_size = resource_size(pp->io);
++++++++++ pp->io_bus_addr = pp->io->start - win->offset;
++++++++++ }
break;
case IORESOURCE_MEM:
pp->mem = win->res;
if (ret)
pp->lanes = 0;
++++ ++++++ ret = of_property_read_u32(np, "num-viewport", &pp->num_viewport);
++++ ++++++ if (ret)
++++ ++++++ pp->num_viewport = 2;
++++ ++++++
if (IS_ENABLED(CONFIG_PCI_MSI)) {
if (!pp->ops->msi_host_init) {
pp->irq_domain = irq_domain_add_linear(pp->dev->of_node,
}
}
++++ ++++++ pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);
++++ ++++++
if (pp->ops->host_init)
pp->ops->host_init(pp);
va_cfg_base = pp->va_cfg1_base;
}
---- ------ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
++++ ++++++ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
type, cpu_addr,
busdev, cfg_size);
ret = dw_pcie_cfg_read(va_cfg_base + where, size, val);
---- ------ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
---- ------ PCIE_ATU_TYPE_IO, pp->io_base,
---- ------ pp->io_bus_addr, pp->io_size);
++++ ++++++ if (pp->num_viewport <= 2)
++++ ++++++ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
++++ ++++++ PCIE_ATU_TYPE_IO, pp->io_base,
++++ ++++++ pp->io_bus_addr, pp->io_size);
return ret;
}
va_cfg_base = pp->va_cfg1_base;
}
---- ------ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
++++ ++++++ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
type, cpu_addr,
busdev, cfg_size);
ret = dw_pcie_cfg_write(va_cfg_base + where, size, val);
---- ------ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
---- ------ PCIE_ATU_TYPE_IO, pp->io_base,
---- ------ pp->io_bus_addr, pp->io_size);
++++ ++++++ if (pp->num_viewport <= 2)
++++ ++++++ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
++++ ++++++ PCIE_ATU_TYPE_IO, pp->io_base,
++++ ++++++ pp->io_bus_addr, pp->io_size);
return ret;
}
if (bus->number == pp->root_bus_nr && dev > 0)
return 0;
---------- /*
---------- * do not read more than one device on the bus directly attached
---------- * to RC's (Virtual Bridge's) DS side.
---------- */
---------- if (bus->primary == pp->root_bus_nr && dev > 0)
---------- return 0;
----------
return 1;
}
u32 val;
/* set the number of lanes */
---- ------ dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
++++ ++++++ val = dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL);
val &= ~PORT_LINK_MODE_MASK;
switch (pp->lanes) {
case 1:
dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
/* set link width speed control register */
---- ------ dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
++++ ++++++ val = dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL);
val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
switch (pp->lanes) {
case 1:
dw_pcie_writel_rc(pp, 0x00000000, PCI_BASE_ADDRESS_1);
/* setup interrupt pins */
---- ------ dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val);
++++ ++++++ val = dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE);
val &= 0xffff00ff;
val |= 0x00000100;
dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
/* setup bus numbers */
---- ------ dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
++++ ++++++ val = dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS);
val &= 0xff000000;
val |= 0x00010100;
dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
/* setup command register */
---- ------ dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
++++ ++++++ val = dw_pcie_readl_rc(pp, PCI_COMMAND);
val &= 0xffff0000;
val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
* uses its own address translation component rather than ATU, so
* we should not program the ATU here.
*/
---- ------ if (!pp->ops->rd_other_conf)
---- ------ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX1,
++++ ++++++ if (!pp->ops->rd_other_conf) {
++++ ++++++ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX0,
PCIE_ATU_TYPE_MEM, pp->mem_base,
pp->mem_bus_addr, pp->mem_size);
++++ ++++++ if (pp->num_viewport > 2)
++++ ++++++ dw_pcie_prog_outbound_atu(pp, PCIE_ATU_REGION_INDEX2,
++++ ++++++ PCIE_ATU_TYPE_IO, pp->io_base,
++++ ++++++ pp->io_bus_addr, pp->io_size);
++++ ++++++ }
dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
val |= PORT_LOGIC_SPEED_CHANGE;
dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
}
----------
----------MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
----------MODULE_DESCRIPTION("Designware PCIe host controller driver");
----------MODULE_LICENSE("GPL v2");
#define IDSETR1 0x011004
#define TLCTLR 0x011048
#define MACSR 0x011054
+++++++ +++#define SPCHGFIN (1 << 4)
+++++++ +++#define SPCHGFAIL (1 << 6)
+++++++ +++#define SPCHGSUC (1 << 7)
+++++++ +++#define LINK_SPEED (0xf << 16)
+++++++ +++#define LINK_SPEED_2_5GTS (1 << 16)
+++++++ +++#define LINK_SPEED_5_0GTS (2 << 16)
#define MACCTLR 0x011058
+++++++ +++#define SPEED_CHANGE (1 << 24)
#define SCRAMBLE_DISABLE (1 << 27)
+++++++ +++#define MACS2R 0x011078
+++++++ +++#define MACCGSPSETR 0x011084
+++++++ +++#define SPCNGRSN (1 << 31)
/* R-Car H1 PHY */
#define H1_PCIEPHYADRR 0x04000c
return 1;
}
+++++++ +++static void rcar_pcie_force_speedup(struct rcar_pcie *pcie)
+++++++ +++{
+++++++ +++ unsigned int timeout = 1000;
+++++++ +++ u32 macsr;
+++++++ +++
+++++++ +++ if ((rcar_pci_read_reg(pcie, MACS2R) & LINK_SPEED) != LINK_SPEED_5_0GTS)
+++++++ +++ return;
+++++++ +++
+++++++ +++ if (rcar_pci_read_reg(pcie, MACCTLR) & SPEED_CHANGE) {
+++++++ +++ dev_err(pcie->dev, "Speed change already in progress\n");
+++++++ +++ return;
+++++++ +++ }
+++++++ +++
+++++++ +++ macsr = rcar_pci_read_reg(pcie, MACSR);
+++++++ +++ if ((macsr & LINK_SPEED) == LINK_SPEED_5_0GTS)
+++++++ +++ goto done;
+++++++ +++
+++++++ +++ /* Set target link speed to 5.0 GT/s */
+++++++ +++ rcar_rmw32(pcie, EXPCAP(12), PCI_EXP_LNKSTA_CLS,
+++++++ +++ PCI_EXP_LNKSTA_CLS_5_0GB);
+++++++ +++
+++++++ +++ /* Set speed change reason as intentional factor */
+++++++ +++ rcar_rmw32(pcie, MACCGSPSETR, SPCNGRSN, 0);
+++++++ +++
+++++++ +++ /* Clear SPCHGFIN, SPCHGSUC, and SPCHGFAIL */
+++++++ +++ if (macsr & (SPCHGFIN | SPCHGSUC | SPCHGFAIL))
+++++++ +++ rcar_pci_write_reg(pcie, macsr, MACSR);
+++++++ +++
+++++++ +++ /* Start link speed change */
+++++++ +++ rcar_rmw32(pcie, MACCTLR, SPEED_CHANGE, SPEED_CHANGE);
+++++++ +++
+++++++ +++ while (timeout--) {
+++++++ +++ macsr = rcar_pci_read_reg(pcie, MACSR);
+++++++ +++ if (macsr & SPCHGFIN) {
+++++++ +++ /* Clear the interrupt bits */
+++++++ +++ rcar_pci_write_reg(pcie, macsr, MACSR);
+++++++ +++
+++++++ +++ if (macsr & SPCHGFAIL)
+++++++ +++ dev_err(pcie->dev, "Speed change failed\n");
+++++++ +++
+++++++ +++ goto done;
+++++++ +++ }
+++++++ +++
+++++++ +++ msleep(1);
+++++++ +++ };
+++++++ +++
+++++++ +++ dev_err(pcie->dev, "Speed change timed out\n");
+++++++ +++
+++++++ +++done:
+++++++ +++ dev_info(pcie->dev, "Current link speed is %s GT/s\n",
+++++++ +++ (macsr & LINK_SPEED) == LINK_SPEED_5_0GTS ? "5" : "2.5");
+++++++ +++}
+++++++ +++
static int rcar_pcie_enable(struct rcar_pcie *pcie)
{
struct pci_bus *bus, *child;
LIST_HEAD(res);
+++++++ +++ /* Try setting 5 GT/s link speed */
+++++++ +++ rcar_pcie_force_speedup(pcie);
+++++++ +++
rcar_pcie_setup(&res, pcie);
pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
return msi;
}
+++++++ +++static int rcar_msi_alloc_region(struct rcar_msi *chip, int no_irqs)
+++++++ +++{
+++++++ +++ int msi;
+++++++ +++
+++++++ +++ mutex_lock(&chip->lock);
+++++++ +++ msi = bitmap_find_free_region(chip->used, INT_PCI_MSI_NR,
+++++++ +++ order_base_2(no_irqs));
+++++++ +++ mutex_unlock(&chip->lock);
+++++++ +++
+++++++ +++ return msi;
+++++++ +++}
+++++++ +++
static void rcar_msi_free(struct rcar_msi *chip, unsigned long irq)
{
mutex_lock(&chip->lock);
if (hwirq < 0)
return hwirq;
------- --- irq = irq_create_mapping(msi->domain, hwirq);
+++++++ +++ irq = irq_find_mapping(msi->domain, hwirq);
if (!irq) {
rcar_msi_free(msi, hwirq);
return -EINVAL;
return 0;
}
+++++++ +++static int rcar_msi_setup_irqs(struct msi_controller *chip,
+++++++ +++ struct pci_dev *pdev, int nvec, int type)
+++++++ +++{
+++++++ +++ struct rcar_pcie *pcie = container_of(chip, struct rcar_pcie, msi.chip);
+++++++ +++ struct rcar_msi *msi = to_rcar_msi(chip);
+++++++ +++ struct msi_desc *desc;
+++++++ +++ struct msi_msg msg;
+++++++ +++ unsigned int irq;
+++++++ +++ int hwirq;
+++++++ +++ int i;
+++++++ +++
+++++++ +++ /* MSI-X interrupts are not supported */
+++++++ +++ if (type == PCI_CAP_ID_MSIX)
+++++++ +++ return -EINVAL;
+++++++ +++
+++++++ +++ WARN_ON(!list_is_singular(&pdev->dev.msi_list));
+++++++ +++ desc = list_entry(pdev->dev.msi_list.next, struct msi_desc, list);
+++++++ +++
+++++++ +++ hwirq = rcar_msi_alloc_region(msi, nvec);
+++++++ +++ if (hwirq < 0)
+++++++ +++ return -ENOSPC;
+++++++ +++
+++++++ +++ irq = irq_find_mapping(msi->domain, hwirq);
+++++++ +++ if (!irq)
+++++++ +++ return -ENOSPC;
+++++++ +++
+++++++ +++ for (i = 0; i < nvec; i++) {
+++++++ +++ /*
+++++++ +++ * irq_create_mapping() called from rcar_pcie_probe() pre-
+++++++ +++ * allocates descs, so there is no need to allocate descs here.
+++++++ +++ * We can therefore assume that if irq_find_mapping() above
+++++++ +++ * returns non-zero, then the descs are also successfully
+++++++ +++ * allocated.
+++++++ +++ */
+++++++ +++ if (irq_set_msi_desc_off(irq, i, desc)) {
+++++++ +++ /* TODO: clear */
+++++++ +++ return -EINVAL;
+++++++ +++ }
+++++++ +++ }
+++++++ +++
+++++++ +++ desc->nvec_used = nvec;
+++++++ +++ desc->msi_attrib.multiple = order_base_2(nvec);
+++++++ +++
+++++++ +++ msg.address_lo = rcar_pci_read_reg(pcie, PCIEMSIALR) & ~MSIFE;
+++++++ +++ msg.address_hi = rcar_pci_read_reg(pcie, PCIEMSIAUR);
+++++++ +++ msg.data = hwirq;
+++++++ +++
+++++++ +++ pci_write_msi_msg(irq, &msg);
+++++++ +++
+++++++ +++ return 0;
+++++++ +++}
+++++++ +++
static void rcar_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
{
struct rcar_msi *msi = to_rcar_msi(chip);
struct platform_device *pdev = to_platform_device(pcie->dev);
struct rcar_msi *msi = &pcie->msi;
unsigned long base;
------- --- int err;
+++++++ +++ int err, i;
mutex_init(&msi->lock);
msi->chip.dev = pcie->dev;
msi->chip.setup_irq = rcar_msi_setup_irq;
+++++++ +++ msi->chip.setup_irqs = rcar_msi_setup_irqs;
msi->chip.teardown_irq = rcar_msi_teardown_irq;
msi->domain = irq_domain_add_linear(pcie->dev->of_node, INT_PCI_MSI_NR,
return -ENOMEM;
}
+++++++ +++ for (i = 0; i < INT_PCI_MSI_NR; i++)
+++++++ +++ irq_create_mapping(msi->domain, i);
+++++++ +++
/* Two irqs are for MSI, but they are also used for non-MSI irqs */
err = devm_request_irq(&pdev->dev, msi->irq1, rcar_pcie_msi_irq,
IRQF_SHARED | IRQF_NO_THREAD,
if (err)
return err;
+++++++ +++ pcie->base = devm_ioremap_resource(&pdev->dev, &res);
+++++++ +++ if (IS_ERR(pcie->base))
+++++++ +++ return PTR_ERR(pcie->base);
+++++++ +++
pcie->clk = devm_clk_get(&pdev->dev, "pcie");
if (IS_ERR(pcie->clk)) {
dev_err(pcie->dev, "cannot get platform clock\n");
}
err = clk_prepare_enable(pcie->clk);
if (err)
------- --- goto fail_clk;
+++++++ +++ return err;
pcie->bus_clk = devm_clk_get(&pdev->dev, "pcie_bus");
if (IS_ERR(pcie->bus_clk)) {
}
err = clk_prepare_enable(pcie->bus_clk);
if (err)
------- --- goto err_map_reg;
+++++++ +++ goto fail_clk;
i = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!i) {
}
pcie->msi.irq2 = i;
------- --- pcie->base = devm_ioremap_resource(&pdev->dev, &res);
------- --- if (IS_ERR(pcie->base)) {
------- --- err = PTR_ERR(pcie->base);
------- --- goto err_map_reg;
------- --- }
------- ---
return 0;
err_map_reg:
* Set up 64-bit inbound regions as the range parser doesn't
* distinguish between 32 and 64-bit types.
*/
------- --- rcar_pci_write_reg(pcie, lower_32_bits(pci_addr), PCIEPRAR(idx));
+++++++ +++ rcar_pci_write_reg(pcie, lower_32_bits(pci_addr),
+++++++ +++ PCIEPRAR(idx));
rcar_pci_write_reg(pcie, lower_32_bits(cpu_addr), PCIELAR(idx));
------- --- rcar_pci_write_reg(pcie, lower_32_bits(mask) | flags, PCIELAMR(idx));
+++++++ +++ rcar_pci_write_reg(pcie, lower_32_bits(mask) | flags,
+++++++ +++ PCIELAMR(idx));
------- --- rcar_pci_write_reg(pcie, upper_32_bits(pci_addr), PCIEPRAR(idx+1));
------- --- rcar_pci_write_reg(pcie, upper_32_bits(cpu_addr), PCIELAR(idx+1));
+++++++ +++ rcar_pci_write_reg(pcie, upper_32_bits(pci_addr),
+++++++ +++ PCIEPRAR(idx + 1));
+++++++ +++ rcar_pci_write_reg(pcie, upper_32_bits(cpu_addr),
+++++++ +++ PCIELAR(idx + 1));
rcar_pci_write_reg(pcie, 0, PCIELAMR(idx + 1));
pci_addr += size;
/* Get the dma-ranges from DT */
for_each_of_pci_range(&parser, &range) {
u64 end = range.cpu_addr + range.size - 1;
+++++++ +++
dev_dbg(pcie->dev, "0x%08x 0x%016llx..0x%016llx -> 0x%016llx\n",
range.flags, range.cpu_addr, end, range.pci_addr);
static const struct of_device_id rcar_pcie_of_match[] = {
{ .compatible = "renesas,pcie-r8a7779", .data = rcar_pcie_hw_init_h1 },
------- --- { .compatible = "renesas,pcie-rcar-gen2", .data = rcar_pcie_hw_init_gen2 },
------- --- { .compatible = "renesas,pcie-r8a7790", .data = rcar_pcie_hw_init_gen2 },
------- --- { .compatible = "renesas,pcie-r8a7791", .data = rcar_pcie_hw_init_gen2 },
+++++++ +++ { .compatible = "renesas,pcie-rcar-gen2",
+++++++ +++ .data = rcar_pcie_hw_init_gen2 },
+++++++ +++ { .compatible = "renesas,pcie-r8a7790",
+++++++ +++ .data = rcar_pcie_hw_init_gen2 },
+++++++ +++ { .compatible = "renesas,pcie-r8a7791",
+++++++ +++ .data = rcar_pcie_hw_init_gen2 },
{ .compatible = "renesas,pcie-r8a7795", .data = rcar_pcie_hw_init },
{},
};
struct device *dev = pci->dev;
struct device_node *np = dev->of_node;
resource_size_t iobase;
---------- struct resource_entry *win;
++++++++++ struct resource_entry *win, *tmp;
------- --- err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, &iobase);
+++++++ +++ err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources,
+++++++ +++ &iobase);
if (err)
return err;
if (err)
goto out_release_res;
---------- resource_list_for_each_entry(win, &pci->resources) {
++++++++++ resource_list_for_each_entry_safe(win, tmp, &pci->resources) {
struct resource *res = win->res;
if (resource_type(res) == IORESOURCE_IO) {
err = pci_remap_iospace(res, iobase);
---------- if (err)
++++++++++ if (err) {
dev_warn(dev, "error %d: failed to map resource %pR\n",
err, res);
++++++++++
++++++++++ resource_list_destroy_entry(win);
++++++++++ }
}
}
return err;
}
------- --- err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
------- --- if (err)
+++++++ +++ err = rcar_pcie_parse_map_dma_ranges(pcie, pdev->dev.of_node);
+++++++ +++ if (err)
return err;
of_id = of_match_device(rcar_pcie_of_match, pcie->dev);
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
----------#include <linux/module.h>
++++++++++#include <linux/init.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
#define MSGF_MISC_SR_MASTER_ERR BIT(5)
#define MSGF_MISC_SR_I_ADDR_ERR BIT(6)
#define MSGF_MISC_SR_E_ADDR_ERR BIT(7)
---------- #define MSGF_MISC_SR_UR_DETECT BIT(20)
----------
---------- #define MSGF_MISC_SR_PCIE_CORE GENMASK(18, 16)
---------- #define MSGF_MISC_SR_PCIE_CORE_ERR GENMASK(31, 22)
++++++++++ #define MSGF_MISC_SR_FATAL_AER BIT(16)
++++++++++ #define MSGF_MISC_SR_NON_FATAL_AER BIT(17)
++++++++++ #define MSGF_MISC_SR_CORR_AER BIT(18)
++++++++++ #define MSGF_MISC_SR_UR_DETECT BIT(20)
++++++++++ #define MSGF_MISC_SR_NON_FATAL_DEV BIT(22)
++++++++++ #define MSGF_MISC_SR_FATAL_DEV BIT(23)
++++++++++ #define MSGF_MISC_SR_LINK_DOWN BIT(24)
++++++++++ #define MSGF_MSIC_SR_LINK_AUTO_BWIDTH BIT(25)
++++++++++ #define MSGF_MSIC_SR_LINK_BWIDTH BIT(26)
#define MSGF_MISC_SR_MASKALL (MSGF_MISC_SR_RXMSG_AVAIL | \
MSGF_MISC_SR_RXMSG_OVER | \
MSGF_MISC_SR_MASTER_ERR | \
MSGF_MISC_SR_I_ADDR_ERR | \
MSGF_MISC_SR_E_ADDR_ERR | \
++++++++++ MSGF_MISC_SR_FATAL_AER | \
++++++++++ MSGF_MISC_SR_NON_FATAL_AER | \
++++++++++ MSGF_MISC_SR_CORR_AER | \
MSGF_MISC_SR_UR_DETECT | \
---------- MSGF_MISC_SR_PCIE_CORE | \
---------- MSGF_MISC_SR_PCIE_CORE_ERR)
++++++++++ MSGF_MISC_SR_NON_FATAL_DEV | \
++++++++++ MSGF_MISC_SR_FATAL_DEV | \
++++++++++ MSGF_MISC_SR_LINK_DOWN | \
++++++++++ MSGF_MSIC_SR_LINK_AUTO_BWIDTH | \
++++++++++ MSGF_MSIC_SR_LINK_BWIDTH)
/* Legacy interrupt status mask bits */
#define MSGF_LEG_SR_INTA BIT(0)
MSGF_LEG_SR_INTC | MSGF_LEG_SR_INTD)
/* MSI interrupt status mask bits */
---------- #define MSGF_MSI_SR_LO_MASK BIT(0)
---------- #define MSGF_MSI_SR_HI_MASK BIT(0)
++++++++++ #define MSGF_MSI_SR_LO_MASK GENMASK(31, 0)
++++++++++ #define MSGF_MSI_SR_HI_MASK GENMASK(31, 0)
#define MSII_PRESENT BIT(0)
#define MSII_ENABLE BIT(0)
dev_err(pcie->dev,
"In Misc Egress address translation error\n");
---------- if (misc_stat & MSGF_MISC_SR_PCIE_CORE_ERR)
---------- dev_err(pcie->dev, "PCIe Core error\n");
++++++++++ if (misc_stat & MSGF_MISC_SR_FATAL_AER)
++++++++++ dev_err(pcie->dev, "Fatal Error in AER Capability\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MISC_SR_NON_FATAL_AER)
++++++++++ dev_err(pcie->dev, "Non-Fatal Error in AER Capability\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MISC_SR_CORR_AER)
++++++++++ dev_err(pcie->dev, "Correctable Error in AER Capability\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MISC_SR_UR_DETECT)
++++++++++ dev_err(pcie->dev, "Unsupported request Detected\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MISC_SR_NON_FATAL_DEV)
++++++++++ dev_err(pcie->dev, "Non-Fatal Error Detected\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MISC_SR_FATAL_DEV)
++++++++++ dev_err(pcie->dev, "Fatal Error Detected\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MSIC_SR_LINK_AUTO_BWIDTH)
++++++++++ dev_info(pcie->dev, "Link Autonomous Bandwidth Management Status bit set\n");
++++++++++
++++++++++ if (misc_stat & MSGF_MSIC_SR_LINK_BWIDTH)
++++++++++ dev_info(pcie->dev, "Link Bandwidth Management Status bit set\n");
/* Clear misc interrupt status */
nwl_bridge_writel(pcie, misc_stat, MSGF_MISC_STATUS);
.free = nwl_irq_domain_free,
};
----------static void nwl_msi_free_irq_domain(struct nwl_pcie *pcie)
----------{
---------- struct nwl_msi *msi = &pcie->msi;
----------
---------- if (msi->irq_msi0)
---------- irq_set_chained_handler_and_data(msi->irq_msi0, NULL, NULL);
---------- if (msi->irq_msi1)
---------- irq_set_chained_handler_and_data(msi->irq_msi1, NULL, NULL);
----------
---------- if (msi->msi_domain)
---------- irq_domain_remove(msi->msi_domain);
---------- if (msi->dev_domain)
---------- irq_domain_remove(msi->dev_domain);
----------
---------- kfree(msi->bitmap);
---------- msi->bitmap = NULL;
----------}
----------
----------static void nwl_pcie_free_irq_domain(struct nwl_pcie *pcie)
----------{
---------- int i;
---------- u32 irq;
----------
---------- for (i = 0; i < INTX_NUM; i++) {
---------- irq = irq_find_mapping(pcie->legacy_irq_domain, i + 1);
---------- if (irq > 0)
---------- irq_dispose_mapping(irq);
---------- }
---------- if (pcie->legacy_irq_domain)
---------- irq_domain_remove(pcie->legacy_irq_domain);
----------
---------- nwl_msi_free_irq_domain(pcie);
----------}
----------
static int nwl_pcie_init_msi_irq_domain(struct nwl_pcie *pcie)
{
#ifdef CONFIG_PCI_MSI
return err;
}
----------static int nwl_pcie_remove(struct platform_device *pdev)
----------{
---------- struct nwl_pcie *pcie = platform_get_drvdata(pdev);
----------
---------- nwl_pcie_free_irq_domain(pcie);
---------- platform_set_drvdata(pdev, NULL);
---------- return 0;
----------}
----------
static struct platform_driver nwl_pcie_driver = {
.driver = {
.name = "nwl-pcie",
++++++++++ .suppress_bind_attrs = true,
.of_match_table = nwl_pcie_of_match,
},
.probe = nwl_pcie_probe,
---------- .remove = nwl_pcie_remove,
};
----------module_platform_driver(nwl_pcie_driver);
----------
----------MODULE_AUTHOR("Xilinx, Inc");
----------MODULE_DESCRIPTION("NWL PCIe driver");
----------MODULE_LICENSE("GPL");
++++++++++builtin_platform_driver(nwl_pcie_driver);
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h>
----------#include <linux/module.h>
++++++++++#include <linux/init.h>
#include <linux/msi.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
* @msi_pages: MSI pages
* @root_busno: Root Bus number
* @dev: Device pointer
---------- * @irq_domain: IRQ domain pointer
++++++++++ * @msi_domain: MSI IRQ domain pointer
++++++++++ * @leg_domain: Legacy IRQ domain pointer
* @resources: Bus Resources
*/
struct xilinx_pcie_port {
unsigned long msi_pages;
u8 root_busno;
struct device *dev;
---------- struct irq_domain *irq_domain;
++++++++++ struct irq_domain *msi_domain;
++++++++++ struct irq_domain *leg_domain;
struct list_head resources;
};
if (bus->number == port->root_busno && devfn > 0)
return false;
---------- /*
---------- * Do not read more than one device on the bus directly attached
---------- * to RC.
---------- */
---------- if (bus->primary == port->root_busno && devfn > 0)
---------- return false;
----------
return true;
}
{
struct msi_desc *msi;
struct xilinx_pcie_port *port;
++++++++++ struct irq_data *d = irq_get_irq_data(irq);
++++++++++ irq_hw_number_t hwirq = irqd_to_hwirq(d);
---------- if (!test_bit(irq, msi_irq_in_use)) {
++++++++++ if (!test_bit(hwirq, msi_irq_in_use)) {
msi = irq_get_msi_desc(irq);
port = msi_desc_to_pci_sysdata(msi);
dev_err(port->dev, "Trying to free unused MSI#%d\n", irq);
} else {
---------- clear_bit(irq, msi_irq_in_use);
++++++++++ clear_bit(hwirq, msi_irq_in_use);
}
}
unsigned int irq)
{
xilinx_pcie_destroy_msi(irq);
++++++++++ irq_dispose_mapping(irq);
}
/**
if (hwirq < 0)
return hwirq;
---------- irq = irq_create_mapping(port->irq_domain, hwirq);
++++++++++ irq = irq_create_mapping(port->msi_domain, hwirq);
if (!irq)
return -EINVAL;
/* Check whether interrupt valid */
if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
dev_warn(port->dev, "RP Intr FIFO1 read error\n");
---------- return IRQ_HANDLED;
++++++++++ goto error;
}
if (!(val & XILINX_PCIE_RPIFR1_MSI_INTR)) {
/* Handle INTx Interrupt */
val = ((val & XILINX_PCIE_RPIFR1_INTR_MASK) >>
XILINX_PCIE_RPIFR1_INTR_SHIFT) + 1;
---------- generic_handle_irq(irq_find_mapping(port->irq_domain,
++++++++++ generic_handle_irq(irq_find_mapping(port->leg_domain,
val));
}
}
if (!(val & XILINX_PCIE_RPIFR1_INTR_VALID)) {
dev_warn(port->dev, "RP Intr FIFO1 read error\n");
---------- return IRQ_HANDLED;
++++++++++ goto error;
}
if (val & XILINX_PCIE_RPIFR1_MSI_INTR) {
if (status & XILINX_PCIE_INTR_MST_ERRP)
dev_warn(port->dev, "Master error poison\n");
++++++++++ error:
/* Clear the Interrupt Decode register */
pcie_write(port, status, XILINX_PCIE_REG_IDR);
return IRQ_HANDLED;
}
----------/**
---------- * xilinx_pcie_free_irq_domain - Free IRQ domain
---------- * @port: PCIe port information
---------- */
----------static void xilinx_pcie_free_irq_domain(struct xilinx_pcie_port *port)
----------{
---------- int i;
---------- u32 irq, num_irqs;
----------
---------- /* Free IRQ Domain */
---------- if (IS_ENABLED(CONFIG_PCI_MSI)) {
----------
---------- free_pages(port->msi_pages, 0);
----------
---------- num_irqs = XILINX_NUM_MSI_IRQS;
---------- } else {
---------- /* INTx */
---------- num_irqs = 4;
---------- }
----------
---------- for (i = 0; i < num_irqs; i++) {
--------- irq = irq_find_mapping(port->irq_domain, i);
- irq = irq_find_mapping(port->leg_domain, i);
---------- if (irq > 0)
---------- irq_dispose_mapping(irq);
---------- }
---------
--------- irq_domain_remove(port->irq_domain);
- if (port->leg_domain)
- irq_domain_remove(port->leg_domain);
- if (port->msi_domain)
- irq_domain_remove(port->msi_domain);
----------}
----------
/**
* xilinx_pcie_init_irq_domain - Initialize IRQ domain
* @port: PCIe port information
return -ENODEV;
}
---------- port->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
++++++++++ port->leg_domain = irq_domain_add_linear(pcie_intc_node, 4,
&intx_domain_ops,
port);
---------- if (!port->irq_domain) {
++++++++++ if (!port->leg_domain) {
dev_err(dev, "Failed to get a INTx IRQ domain\n");
return -ENODEV;
}
/* Setup MSI */
if (IS_ENABLED(CONFIG_PCI_MSI)) {
---------- port->irq_domain = irq_domain_add_linear(node,
++++++++++ port->msi_domain = irq_domain_add_linear(node,
XILINX_NUM_MSI_IRQS,
&msi_domain_ops,
&xilinx_pcie_msi_chip);
---------- if (!port->irq_domain) {
++++++++++ if (!port->msi_domain) {
dev_err(dev, "Failed to get a MSI IRQ domain\n");
return -ENODEV;
}
return err;
}
----------/**
---------- * xilinx_pcie_remove - Remove function
---------- * @pdev: Platform device pointer
---------- *
---------- * Return: '0' always
---------- */
----------static int xilinx_pcie_remove(struct platform_device *pdev)
----------{
---------- struct xilinx_pcie_port *port = platform_get_drvdata(pdev);
----------
---------- xilinx_pcie_free_irq_domain(port);
----------
---------- return 0;
----------}
----------
static struct of_device_id xilinx_pcie_of_match[] = {
{ .compatible = "xlnx,axi-pcie-host-1.00.a", },
{}
.suppress_bind_attrs = true,
},
.probe = xilinx_pcie_probe,
---------- .remove = xilinx_pcie_remove,
};
----------module_platform_driver(xilinx_pcie_driver);
----------
----------MODULE_AUTHOR("Xilinx Inc");
----------MODULE_DESCRIPTION("Xilinx AXI PCIe driver");
----------MODULE_LICENSE("GPL v2");
++++++++++builtin_platform_driver(xilinx_pcie_driver);