forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'pci/host-layerscape' into next
* pci/host-layerscape: PCI: layerscape: Add ls_pcie_msi_host_init() PCI: layerscape: Add support for LS1043a and LS2080a PCI: layerscape: Remove unused fields from struct ls_pcie PCI: layerscape: Update ls_add_pcie_port() PCI: layerscape: Factor out SCFG related function PCI: layerscape: Ignore PCIe controllers in Endpoint mode PCI: layerscape: Remove ls_pcie_establish_link()
- Loading branch information
Showing
3 changed files
with
161 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,15 +3,14 @@ | |
* | ||
* Copyright (C) 2014 Freescale Semiconductor. | ||
* | ||
* Author: Minghuan Lian <[email protected]> | ||
* Author: Minghuan Lian <[email protected]> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <linux/kernel.h> | ||
#include <linux/delay.h> | ||
#include <linux/interrupt.h> | ||
#include <linux/module.h> | ||
#include <linux/of_pci.h> | ||
|
@@ -32,27 +31,60 @@ | |
#define LTSSM_STATE_MASK 0x3f | ||
#define LTSSM_PCIE_L0 0x11 /* L0 state */ | ||
|
||
/* Symbol Timer Register and Filter Mask Register 1 */ | ||
#define PCIE_STRFMR1 0x71c | ||
/* PEX Internal Configuration Registers */ | ||
#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */ | ||
#define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */ | ||
|
||
/* PEX LUT registers */ | ||
#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */ | ||
|
||
struct ls_pcie_drvdata { | ||
u32 lut_offset; | ||
u32 ltssm_shift; | ||
struct pcie_host_ops *ops; | ||
}; | ||
|
||
struct ls_pcie { | ||
struct list_head node; | ||
struct device *dev; | ||
struct pci_bus *bus; | ||
void __iomem *dbi; | ||
void __iomem *lut; | ||
struct regmap *scfg; | ||
struct pcie_port pp; | ||
const struct ls_pcie_drvdata *drvdata; | ||
int index; | ||
int msi_irq; | ||
}; | ||
|
||
#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp) | ||
|
||
static int ls_pcie_link_up(struct pcie_port *pp) | ||
static bool ls_pcie_is_bridge(struct ls_pcie *pcie) | ||
{ | ||
u32 header_type; | ||
|
||
header_type = ioread8(pcie->dbi + PCI_HEADER_TYPE); | ||
header_type &= 0x7f; | ||
|
||
return header_type == PCI_HEADER_TYPE_BRIDGE; | ||
} | ||
|
||
/* Clear multi-function bit */ | ||
static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) | ||
{ | ||
iowrite8(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); | ||
} | ||
|
||
/* Fix class value */ | ||
static void ls_pcie_fix_class(struct ls_pcie *pcie) | ||
{ | ||
iowrite16(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); | ||
} | ||
|
||
static int ls1021_pcie_link_up(struct pcie_port *pp) | ||
{ | ||
u32 state; | ||
struct ls_pcie *pcie = to_ls_pcie(pp); | ||
|
||
if (!pcie->scfg) | ||
return 0; | ||
|
||
regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state); | ||
state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK; | ||
|
||
|
@@ -62,27 +94,27 @@ static int ls_pcie_link_up(struct pcie_port *pp) | |
return 1; | ||
} | ||
|
||
static int ls_pcie_establish_link(struct pcie_port *pp) | ||
static void ls1021_pcie_host_init(struct pcie_port *pp) | ||
{ | ||
unsigned int retries; | ||
struct ls_pcie *pcie = to_ls_pcie(pp); | ||
u32 val, index[2]; | ||
|
||
for (retries = 0; retries < 200; retries++) { | ||
if (dw_pcie_link_up(pp)) | ||
return 0; | ||
usleep_range(100, 1000); | ||
pcie->scfg = syscon_regmap_lookup_by_phandle(pp->dev->of_node, | ||
"fsl,pcie-scfg"); | ||
if (IS_ERR(pcie->scfg)) { | ||
dev_err(pp->dev, "No syscfg phandle specified\n"); | ||
pcie->scfg = NULL; | ||
return; | ||
} | ||
|
||
dev_err(pp->dev, "phy link never came up\n"); | ||
return -EINVAL; | ||
} | ||
|
||
static void ls_pcie_host_init(struct pcie_port *pp) | ||
{ | ||
struct ls_pcie *pcie = to_ls_pcie(pp); | ||
u32 val; | ||
if (of_property_read_u32_array(pp->dev->of_node, | ||
"fsl,pcie-scfg", index, 2)) { | ||
pcie->scfg = NULL; | ||
return; | ||
} | ||
pcie->index = index[1]; | ||
|
||
dw_pcie_setup_rc(pp); | ||
ls_pcie_establish_link(pp); | ||
|
||
/* | ||
* LS1021A Workaround for internal TKT228622 | ||
|
@@ -93,21 +125,97 @@ static void ls_pcie_host_init(struct pcie_port *pp) | |
iowrite32(val, pcie->dbi + PCIE_STRFMR1); | ||
} | ||
|
||
static int ls_pcie_link_up(struct pcie_port *pp) | ||
{ | ||
struct ls_pcie *pcie = to_ls_pcie(pp); | ||
u32 state; | ||
|
||
state = (ioread32(pcie->lut + PCIE_LUT_DBG) >> | ||
pcie->drvdata->ltssm_shift) & | ||
LTSSM_STATE_MASK; | ||
|
||
if (state < LTSSM_PCIE_L0) | ||
return 0; | ||
|
||
return 1; | ||
} | ||
|
||
static void ls_pcie_host_init(struct pcie_port *pp) | ||
{ | ||
struct ls_pcie *pcie = to_ls_pcie(pp); | ||
|
||
iowrite32(1, pcie->dbi + PCIE_DBI_RO_WR_EN); | ||
ls_pcie_fix_class(pcie); | ||
ls_pcie_clear_multifunction(pcie); | ||
iowrite32(0, pcie->dbi + PCIE_DBI_RO_WR_EN); | ||
} | ||
|
||
static int ls_pcie_msi_host_init(struct pcie_port *pp, | ||
struct msi_controller *chip) | ||
{ | ||
struct device_node *msi_node; | ||
struct device_node *np = pp->dev->of_node; | ||
|
||
/* | ||
* The MSI domain is set by the generic of_msi_configure(). This | ||
* .msi_host_init() function keeps us from doing the default MSI | ||
* domain setup in dw_pcie_host_init() and also enforces the | ||
* requirement that "msi-parent" exists. | ||
*/ | ||
msi_node = of_parse_phandle(np, "msi-parent", 0); | ||
if (!msi_node) { | ||
dev_err(pp->dev, "failed to find msi-parent\n"); | ||
return -EINVAL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct pcie_host_ops ls1021_pcie_host_ops = { | ||
.link_up = ls1021_pcie_link_up, | ||
.host_init = ls1021_pcie_host_init, | ||
.msi_host_init = ls_pcie_msi_host_init, | ||
}; | ||
|
||
static struct pcie_host_ops ls_pcie_host_ops = { | ||
.link_up = ls_pcie_link_up, | ||
.host_init = ls_pcie_host_init, | ||
.msi_host_init = ls_pcie_msi_host_init, | ||
}; | ||
|
||
static struct ls_pcie_drvdata ls1021_drvdata = { | ||
.ops = &ls1021_pcie_host_ops, | ||
}; | ||
|
||
static struct ls_pcie_drvdata ls1043_drvdata = { | ||
.lut_offset = 0x10000, | ||
.ltssm_shift = 24, | ||
.ops = &ls_pcie_host_ops, | ||
}; | ||
|
||
static int ls_add_pcie_port(struct ls_pcie *pcie) | ||
static struct ls_pcie_drvdata ls2080_drvdata = { | ||
.lut_offset = 0x80000, | ||
.ltssm_shift = 0, | ||
.ops = &ls_pcie_host_ops, | ||
}; | ||
|
||
static const struct of_device_id ls_pcie_of_match[] = { | ||
{ .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata }, | ||
{ .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata }, | ||
{ .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(of, ls_pcie_of_match); | ||
|
||
static int __init ls_add_pcie_port(struct pcie_port *pp, | ||
struct platform_device *pdev) | ||
{ | ||
struct pcie_port *pp; | ||
int ret; | ||
struct ls_pcie *pcie = to_ls_pcie(pp); | ||
|
||
pp = &pcie->pp; | ||
pp->dev = pcie->dev; | ||
pp->dev = &pdev->dev; | ||
pp->dbi_base = pcie->dbi; | ||
pp->root_bus_nr = -1; | ||
pp->ops = &ls_pcie_host_ops; | ||
pp->ops = pcie->drvdata->ops; | ||
|
||
ret = dw_pcie_host_init(pp); | ||
if (ret) { | ||
|
@@ -120,38 +228,33 @@ static int ls_add_pcie_port(struct ls_pcie *pcie) | |
|
||
static int __init ls_pcie_probe(struct platform_device *pdev) | ||
{ | ||
const struct of_device_id *match; | ||
struct ls_pcie *pcie; | ||
struct resource *dbi_base; | ||
u32 index[2]; | ||
int ret; | ||
|
||
match = of_match_device(ls_pcie_of_match, &pdev->dev); | ||
if (!match) | ||
return -ENODEV; | ||
|
||
pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); | ||
if (!pcie) | ||
return -ENOMEM; | ||
|
||
pcie->dev = &pdev->dev; | ||
|
||
dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); | ||
pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base); | ||
if (IS_ERR(pcie->dbi)) { | ||
dev_err(&pdev->dev, "missing *regs* space\n"); | ||
return PTR_ERR(pcie->dbi); | ||
} | ||
|
||
pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, | ||
"fsl,pcie-scfg"); | ||
if (IS_ERR(pcie->scfg)) { | ||
dev_err(&pdev->dev, "No syscfg phandle specified\n"); | ||
return PTR_ERR(pcie->scfg); | ||
} | ||
pcie->drvdata = match->data; | ||
pcie->lut = pcie->dbi + pcie->drvdata->lut_offset; | ||
|
||
ret = of_property_read_u32_array(pdev->dev.of_node, | ||
"fsl,pcie-scfg", index, 2); | ||
if (ret) | ||
return ret; | ||
pcie->index = index[1]; | ||
if (!ls_pcie_is_bridge(pcie)) | ||
return -ENODEV; | ||
|
||
ret = ls_add_pcie_port(pcie); | ||
ret = ls_add_pcie_port(&pcie->pp, pdev); | ||
if (ret < 0) | ||
return ret; | ||
|
||
|
@@ -160,12 +263,6 @@ static int __init ls_pcie_probe(struct platform_device *pdev) | |
return 0; | ||
} | ||
|
||
static const struct of_device_id ls_pcie_of_match[] = { | ||
{ .compatible = "fsl,ls1021a-pcie" }, | ||
{ }, | ||
}; | ||
MODULE_DEVICE_TABLE(of, ls_pcie_of_match); | ||
|
||
static struct platform_driver ls_pcie_driver = { | ||
.driver = { | ||
.name = "layerscape-pcie", | ||
|