msm: pcie: add SMMU support to calculate SID for PCIe EP

SMMU requires PCIe to provide a SID for each of its endpoint
so that the endpoint can successful transaction on the bus.
This change adds the support for PCIe bus driver to calculate
a SID for its endpoint and give it to the SMMU driver.

Change-Id: I52099bbfed0a38c75b0277b0f58f45f6e6559695
Signed-off-by: Tony Truong <truong@codeaurora.org>
This commit is contained in:
Tony Truong 2015-01-05 12:58:58 -08:00 committed by David Keitel
parent 58763943bc
commit c27c02ee65
3 changed files with 163 additions and 1 deletions

View file

@ -81,6 +81,7 @@ Optional Properties:
complex. This should be used in separate nodes from the main root complex. This should be used in separate nodes from the main root
complex nodes, and is the only property needed in that case. complex nodes, and is the only property needed in that case.
- qcom,common-phy: There is a common phy for all the Root Complexes. - qcom,common-phy: There is a common phy for all the Root Complexes.
- qcom,smmu-exist: PCIe uses a SMMU.
- qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become
stable after power on, before de-assert the PERST to the endpoint. stable after power on, before de-assert the PERST to the endpoint.
- qcom,tlp-rd-size: The max TLP read size (Calculation: 128 times 2 to the - qcom,tlp-rd-size: The max TLP read size (Calculation: 128 times 2 to the
@ -220,6 +221,7 @@ Example:
qcom,ext-ref-clk; qcom,ext-ref-clk;
qcom,tlp-rd-size = <0x5>; qcom,tlp-rd-size = <0x5>;
qcom,common-phy; qcom,common-phy;
qcom,smmu-exist;
qcom,ep-latency = <100>; qcom,ep-latency = <100>;
iommus = <&anoc0_smmu>; iommus = <&anoc0_smmu>;

View file

@ -160,6 +160,10 @@
#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174 #define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174
#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x1A8 #define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x1A8
#define PCIE20_PARF_LTSSM 0x1B0 #define PCIE20_PARF_LTSSM 0x1B0
#define PCIE20_PARF_SID_OFFSET 0x234
#define PCIE20_PARF_BDF_TRANSLATE_CFG 0x24C
#define PCIE20_PARF_BDF_TRANSLATE_N 0x250
#define PCIE20_ELBI_VERSION 0x00 #define PCIE20_ELBI_VERSION 0x00
#define PCIE20_ELBI_SYS_CTRL 0x04 #define PCIE20_ELBI_SYS_CTRL 0x04
@ -237,6 +241,7 @@
#define MSM_PCIE_MAX_PIPE_CLK 1 #define MSM_PCIE_MAX_PIPE_CLK 1
#define MAX_RC_NUM 3 #define MAX_RC_NUM 3
#define MAX_DEVICE_NUM 20 #define MAX_DEVICE_NUM 20
#define MAX_SHORT_BDF_NUM 16
#define PCIE_TLP_RD_SIZE 0x5 #define PCIE_TLP_RD_SIZE 0x5
#define PCIE_MSI_NR_IRQS 256 #define PCIE_MSI_NR_IRQS 256
#define MSM_PCIE_MAX_MSI 32 #define MSM_PCIE_MAX_MSI 32
@ -415,6 +420,8 @@ struct msm_pcie_irq_info_t {
struct msm_pcie_device_info { struct msm_pcie_device_info {
u32 bdf; u32 bdf;
struct pci_dev *dev; struct pci_dev *dev;
short short_bdf;
u32 sid;
int domain; int domain;
void __iomem *conf_base; void __iomem *conf_base;
unsigned long phy_address; unsigned long phy_address;
@ -482,11 +489,13 @@ struct msm_pcie_dev_t {
bool common_clk_en; bool common_clk_en;
bool clk_power_manage_en; bool clk_power_manage_en;
bool aux_clk_sync; bool aux_clk_sync;
bool smmu_exist;
uint32_t n_fts; uint32_t n_fts;
bool ext_ref_clk; bool ext_ref_clk;
bool common_phy; bool common_phy;
uint32_t ep_latency; uint32_t ep_latency;
uint32_t current_bdf; uint32_t current_bdf;
short current_short_bdf;
uint32_t tlp_rd_size; uint32_t tlp_rd_size;
bool ep_wakeirq; bool ep_wakeirq;
@ -1440,6 +1449,10 @@ static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
dev->msi_gicm_base); dev->msi_gicm_base);
pr_alert("bus_client: %d\n", pr_alert("bus_client: %d\n",
dev->bus_client); dev->bus_client);
pr_alert("current short bdf: %d\n",
dev->current_short_bdf);
pr_alert("smmu does %s exist\n",
dev->smmu_exist ? "" : "not");
pr_alert("n_fts: %d\n", pr_alert("n_fts: %d\n",
dev->n_fts); dev->n_fts);
pr_alert("common_phy: %d\n", pr_alert("common_phy: %d\n",
@ -2929,6 +2942,8 @@ static void msm_pcie_iatu_config_all_ep(struct msm_pcie_dev_t *dev)
static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev) static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
{ {
int i;
PCIE_DBG(dev, "RC%d\n", dev->rc_idx); PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
/* /*
@ -2970,6 +2985,27 @@ static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
PCIE_DBG(dev, "RC's PCIE20_CAP_DEVCTRLSTATUS:0x%x\n", PCIE_DBG(dev, "RC's PCIE20_CAP_DEVCTRLSTATUS:0x%x\n",
readl_relaxed(dev->dm_core + PCIE20_CAP_DEVCTRLSTATUS)); readl_relaxed(dev->dm_core + PCIE20_CAP_DEVCTRLSTATUS));
/* configure SMMU registers */
if (dev->smmu_exist) {
msm_pcie_write_reg(dev->parf,
PCIE20_PARF_BDF_TRANSLATE_CFG, 0);
msm_pcie_write_reg(dev->parf,
PCIE20_PARF_SID_OFFSET, 0);
if (dev->enumerated) {
for (i = 0; i < MAX_DEVICE_NUM; i++) {
if (dev->pcidev_table[i].dev &&
dev->pcidev_table[i].short_bdf) {
msm_pcie_write_reg(dev->parf,
PCIE20_PARF_BDF_TRANSLATE_N +
dev->pcidev_table[i].short_bdf
* 4,
dev->pcidev_table[i].bdf >> 16);
}
}
}
}
} }
static void msm_pcie_config_link_state(struct msm_pcie_dev_t *dev) static void msm_pcie_config_link_state(struct msm_pcie_dev_t *dev)
@ -3889,6 +3925,105 @@ static int msm_pcie_config_device_table(struct device *dev, void *pdev)
return ret; return ret;
} }
int msm_pcie_configure_sid(struct device *dev, u32 *sid, int *domain)
{
struct pci_dev *pcidev;
struct msm_pcie_dev_t *pcie_dev;
struct pci_bus *bus;
int i;
u32 bdf;
if (!dev) {
pr_err("%s: PCIe: endpoint device passed in is NULL\n",
__func__);
return MSM_PCIE_ERROR;
}
pcidev = to_pci_dev(dev);
if (!pcidev) {
pr_err("%s: PCIe: PCI device of endpoint is NULL\n",
__func__);
return MSM_PCIE_ERROR;
}
bus = pcidev->bus;
if (!bus) {
pr_err("%s: PCIe: Bus of PCI device is NULL\n",
__func__);
return MSM_PCIE_ERROR;
}
while (!pci_is_root_bus(bus))
bus = bus->parent;
pcie_dev = (struct msm_pcie_dev_t *)(bus->sysdata);
if (!pcie_dev) {
pr_err("%s: PCIe: Could not get PCIe structure\n",
__func__);
return MSM_PCIE_ERROR;
}
if (!pcie_dev->smmu_exist) {
PCIE_DBG(pcie_dev,
"PCIe: RC:%d: smmu does not exist\n",
pcie_dev->rc_idx);
return MSM_PCIE_ERROR;
}
PCIE_DBG(pcie_dev, "PCIe: RC%d: device address is: %p\n",
pcie_dev->rc_idx, dev);
PCIE_DBG(pcie_dev, "PCIe: RC%d: PCI device address is: %p\n",
pcie_dev->rc_idx, pcidev);
*domain = pcie_dev->rc_idx;
if (pcie_dev->current_short_bdf < (MAX_SHORT_BDF_NUM - 1)) {
pcie_dev->current_short_bdf++;
} else {
PCIE_ERR(pcie_dev,
"PCIe: RC%d: No more short BDF left\n",
pcie_dev->rc_idx);
return MSM_PCIE_ERROR;
}
bdf = BDF_OFFSET(pcidev->bus->number, pcidev->devfn);
for (i = 0; i < MAX_DEVICE_NUM; i++) {
if (pcie_dev->pcidev_table[i].bdf == bdf) {
*sid = (pcie_dev->rc_idx << 4) |
pcie_dev->current_short_bdf;
msm_pcie_write_reg(pcie_dev->parf,
PCIE20_PARF_BDF_TRANSLATE_N +
pcie_dev->current_short_bdf * 4,
bdf >> 16);
pcie_dev->pcidev_table[i].sid = *sid;
pcie_dev->pcidev_table[i].short_bdf =
pcie_dev->current_short_bdf;
break;
}
}
if (i == MAX_DEVICE_NUM) {
pcie_dev->current_short_bdf--;
PCIE_ERR(pcie_dev,
"PCIe: RC%d could not find BDF:%d\n",
pcie_dev->rc_idx, bdf);
return MSM_PCIE_ERROR;
}
PCIE_DBG(pcie_dev,
"PCIe: RC%d: Device: %02x:%02x.%01x received SID %d\n",
pcie_dev->rc_idx,
bdf >> 24,
bdf >> 19 & 0x1f,
bdf >> 16 & 0x07,
*sid);
return 0;
}
EXPORT_SYMBOL(msm_pcie_configure_sid);
int msm_pcie_enumerate(u32 rc_idx) int msm_pcie_enumerate(u32 rc_idx)
{ {
@ -4817,6 +4952,13 @@ static int msm_pcie_probe(struct platform_device *pdev)
"AUX clock is %s synchronous to Core clock.\n", "AUX clock is %s synchronous to Core clock.\n",
msm_pcie_dev[rc_idx].aux_clk_sync ? "" : "not"); msm_pcie_dev[rc_idx].aux_clk_sync ? "" : "not");
msm_pcie_dev[rc_idx].smmu_exist =
of_property_read_bool((&pdev->dev)->of_node,
"qcom,smmu-exist");
PCIE_DBG(&msm_pcie_dev[rc_idx],
"SMMU does %s exist.\n",
msm_pcie_dev[rc_idx].smmu_exist ? "" : "not");
msm_pcie_dev[rc_idx].ep_wakeirq = msm_pcie_dev[rc_idx].ep_wakeirq =
of_property_read_bool((&pdev->dev)->of_node, of_property_read_bool((&pdev->dev)->of_node,
"qcom,ep-wakeirq"); "qcom,ep-wakeirq");
@ -4933,6 +5075,7 @@ static int msm_pcie_probe(struct platform_device *pdev)
msm_pcie_dev[rc_idx].suspending = false; msm_pcie_dev[rc_idx].suspending = false;
msm_pcie_dev[rc_idx].wake_counter = 0; msm_pcie_dev[rc_idx].wake_counter = 0;
msm_pcie_dev[rc_idx].power_on = false; msm_pcie_dev[rc_idx].power_on = false;
msm_pcie_dev[rc_idx].current_short_bdf = 0;
msm_pcie_dev[rc_idx].use_msi = false; msm_pcie_dev[rc_idx].use_msi = false;
msm_pcie_dev[rc_idx].use_pinctrl = false; msm_pcie_dev[rc_idx].use_pinctrl = false;
msm_pcie_dev[rc_idx].bridge_found = false; msm_pcie_dev[rc_idx].bridge_found = false;
@ -4959,6 +5102,8 @@ static int msm_pcie_probe(struct platform_device *pdev)
for (i = 0; i < MAX_DEVICE_NUM; i++) { for (i = 0; i < MAX_DEVICE_NUM; i++) {
msm_pcie_dev[rc_idx].pcidev_table[i].bdf = 0; msm_pcie_dev[rc_idx].pcidev_table[i].bdf = 0;
msm_pcie_dev[rc_idx].pcidev_table[i].dev = NULL; msm_pcie_dev[rc_idx].pcidev_table[i].dev = NULL;
msm_pcie_dev[rc_idx].pcidev_table[i].short_bdf = 0;
msm_pcie_dev[rc_idx].pcidev_table[i].sid = 0;
msm_pcie_dev[rc_idx].pcidev_table[i].domain = rc_idx; msm_pcie_dev[rc_idx].pcidev_table[i].domain = rc_idx;
msm_pcie_dev[rc_idx].pcidev_table[i].conf_base = 0; msm_pcie_dev[rc_idx].pcidev_table[i].conf_base = 0;
msm_pcie_dev[rc_idx].pcidev_table[i].phy_address = 0; msm_pcie_dev[rc_idx].pcidev_table[i].phy_address = 0;
@ -5158,6 +5303,8 @@ int __init pcie_init(void)
for (i = 0; i < MAX_RC_NUM * MAX_DEVICE_NUM; i++) { for (i = 0; i < MAX_RC_NUM * MAX_DEVICE_NUM; i++) {
msm_pcie_dev_tbl[i].bdf = 0; msm_pcie_dev_tbl[i].bdf = 0;
msm_pcie_dev_tbl[i].dev = NULL; msm_pcie_dev_tbl[i].dev = NULL;
msm_pcie_dev_tbl[i].short_bdf = 0;
msm_pcie_dev_tbl[i].sid = 0;
msm_pcie_dev_tbl[i].domain = -1; msm_pcie_dev_tbl[i].domain = -1;
msm_pcie_dev_tbl[i].conf_base = 0; msm_pcie_dev_tbl[i].conf_base = 0;
msm_pcie_dev_tbl[i].phy_address = 0; msm_pcie_dev_tbl[i].phy_address = 0;

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and * it under the terms of the GNU General Public License version 2 and
@ -155,4 +155,17 @@ int msm_pcie_shadow_control(struct pci_dev *dev, bool enable);
*/ */
int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base, int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
u32 offset, u32 mask, u32 value); u32 offset, u32 mask, u32 value);
/*
* msm_pcie_configure_sid - calculates the SID for a PCIe endpoint.
* @dev: device structure
* @sid: the calculated SID
* @domain: the domain number of the Root Complex
*
* This function calculates the SID for a PCIe endpoint device.
*
* Return: 0 on success, negative value on error
*/
int msm_pcie_configure_sid(struct device *dev, u32 *sid,
int *domain);
#endif #endif