msm: ep_pcie: add PCIe endpoint driver for 8996AU
The MSM PCIE core is enabled in endpoint mode and handles the link from PCIE root complex on host side. Change-Id: I5a23ea1a41fede2d57850ff032bf2b1a92d02463 Signed-off-by: Yimin Peng <yiminp@codeaurora.org>
This commit is contained in:
parent
595eeed398
commit
ef6d23ad26
4 changed files with 256 additions and 14 deletions
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
|
@ -101,6 +101,14 @@
|
|||
|
||||
#define PCIE20_AUX_CLK_FREQ_REG 0xB40
|
||||
|
||||
#define PCIE20_MHISTATUS 0x148
|
||||
#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL 0x174
|
||||
#define PCIE20_PARF_CFG_BITS 0x210
|
||||
|
||||
#define PCIE20_BRIDGE_CTRL_INT_PIN_INT_LINE_REG 0x3C
|
||||
#define PCIE20_DEVICE_ID_VENDOR_ID_REG 0x0
|
||||
#define PCIE20_L1_SUBSTATES_REG 0xB44
|
||||
|
||||
#define PERST_TIMEOUT_US_MIN 1000
|
||||
#define PERST_TIMEOUT_US_MAX 1000
|
||||
#define PERST_CHECK_MAX_COUNT 30000
|
||||
|
@ -126,7 +134,7 @@
|
|||
|
||||
#define EP_PCIE_LOG_PAGES 50
|
||||
#define EP_PCIE_MAX_VREG 2
|
||||
#define EP_PCIE_MAX_CLK 6
|
||||
#define EP_PCIE_MAX_CLK 8
|
||||
#define EP_PCIE_MAX_PIPE_CLK 1
|
||||
|
||||
#define EP_PCIE_ERROR -30655
|
||||
|
@ -255,6 +263,12 @@ struct ep_pcie_irq_info_t {
|
|||
u32 num;
|
||||
};
|
||||
|
||||
struct ep_pcie_phy_info_t {
|
||||
u32 offset;
|
||||
u32 val;
|
||||
u32 delay;
|
||||
};
|
||||
|
||||
/* pcie endpoint device structure */
|
||||
struct ep_pcie_dev_t {
|
||||
struct platform_device *pdev;
|
||||
|
@ -278,6 +292,9 @@ struct ep_pcie_dev_t {
|
|||
u32 link_speed;
|
||||
bool active_config;
|
||||
bool aggregated_irq;
|
||||
u32 phy_status_reg;
|
||||
u32 phy_len;
|
||||
struct ep_pcie_phy_info_t *phy_sequence;
|
||||
|
||||
u32 rev;
|
||||
u32 phy_rev;
|
||||
|
@ -356,5 +373,7 @@ extern bool ep_pcie_phy_is_ready(struct ep_pcie_dev_t *dev);
|
|||
extern void ep_pcie_reg_dump(struct ep_pcie_dev_t *dev, u32 sel, bool linkdown);
|
||||
extern void ep_pcie_debugfs_init(struct ep_pcie_dev_t *ep_dev);
|
||||
extern void ep_pcie_debugfs_exit(void);
|
||||
|
||||
#ifdef CONFIG_ARCH_MSM8996
|
||||
extern void ep_pcie_phy_bringup_port(struct ep_pcie_dev_t *dev);
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
|
@ -64,7 +64,9 @@ static struct ep_pcie_clk_info_t
|
|||
{NULL, "pcie_0_slv_axi_clk", 0, true},
|
||||
{NULL, "pcie_0_aux_clk", 1000000, true},
|
||||
{NULL, "pcie_0_ldo", 0, true},
|
||||
{NULL, "pcie_0_phy_reset", 0, false}
|
||||
{NULL, "pcie_0_phy_reset", 0, false},
|
||||
{NULL, "pcie_0_phy_cfg_ahb_clk", 0, false},
|
||||
{NULL, "pcie_0_phy_aux_clk", 0, false}
|
||||
};
|
||||
|
||||
static struct ep_pcie_clk_info_t
|
||||
|
@ -131,11 +133,18 @@ static int ep_pcie_gpio_init(struct ep_pcie_dev_t *dev)
|
|||
info = &dev->gpio[i];
|
||||
|
||||
if (!info->num) {
|
||||
EP_PCIE_ERR(dev,
|
||||
"PCIe V%d: the number of gpio %s is invalid\n",
|
||||
dev->rev, info->name);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
if (i == EP_PCIE_GPIO_MDM2AP) {
|
||||
EP_PCIE_DBG(dev,
|
||||
"PCIe V%d: gpio %s does not exist.\n",
|
||||
dev->rev, info->name);
|
||||
continue;
|
||||
} else {
|
||||
EP_PCIE_ERR(dev,
|
||||
"PCIe V%d: the number of gpio %s is invalid\n",
|
||||
dev->rev, info->name);
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rc = gpio_request(info->num, info->name);
|
||||
|
@ -451,6 +460,109 @@ static void ep_pcie_bar_init(struct ep_pcie_dev_t *dev)
|
|||
ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_MSM8996
|
||||
static void ep_pcie_core_init(struct ep_pcie_dev_t *dev)
|
||||
{
|
||||
u32 regval;
|
||||
|
||||
EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
|
||||
|
||||
/* Configure PCIe to endpoint mode */
|
||||
ep_pcie_write_reg(dev->parf, PCIE20_PARF_DEVICE_TYPE, 0x0);
|
||||
|
||||
/* adjust DBI base address */
|
||||
writel_relaxed(0x0C000000, dev->parf + PCIE20_PARF_DBI_BASE_ADDR);
|
||||
|
||||
/* Configure PCIe core to support 1GB aperture */
|
||||
ep_pcie_write_reg(dev->parf, PCIE20_PARF_SLV_ADDR_SPACE_SIZE,
|
||||
0x40000000);
|
||||
|
||||
/* Disable the debouncers */
|
||||
ep_pcie_write_reg(dev->parf, PCIE20_PARF_DB_CTRL, 0x73);
|
||||
|
||||
/* Enable Auxiliary Power Detect */
|
||||
ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, 0x10, BIT(4));
|
||||
|
||||
/* Enable the bit to exit l1ss when sending LTR and MSI */
|
||||
ep_pcie_write_mask(dev->parf + PCIE20_PARF_CFG_BITS, 0x2, BIT(1));
|
||||
|
||||
/* Enable CS for RO(CS) register writes */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0, BIT(0));
|
||||
|
||||
/* Set the INT_LINE Register field to 0 */
|
||||
ep_pcie_write_mask(dev->dm_core +
|
||||
PCIE20_BRIDGE_CTRL_INT_PIN_INT_LINE_REG, 0xff, 0);
|
||||
|
||||
/* Set the PMC Register - to support PME in D0, D3hot and D3cold */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_CAP_ID_NXT_PTR,
|
||||
0xF8000000, BIT(31) | BIT(30) | BIT(27));
|
||||
|
||||
/* Set the frequency for the AUX clock to 19.2MHz */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_AUX_CLK_FREQ_REG,
|
||||
0x3FF, BIT(4) | BIT(2));
|
||||
|
||||
/* Set the Endpoint L0s Acceptable Latency to 1us (max) */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_DEVICE_CAPABILITIES,
|
||||
0x1C0, BIT(8) | BIT(7) | BIT(6));
|
||||
|
||||
/* Set the Endpoint L1 Acceptable Latency to 2 us (max) */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_DEVICE_CAPABILITIES,
|
||||
0xE00, BIT(11) | BIT(10) | BIT(9));
|
||||
|
||||
/* Set the L0s Exit Latency to 2us-4us = 0x6 */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CAPABILITIES,
|
||||
0x38000, BIT(17) | BIT(16));
|
||||
|
||||
/* Set the L1 Exit Latency to be 32us-64 us = 0x6 */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CAPABILITIES,
|
||||
0x7000, BIT(14) | BIT(13));
|
||||
|
||||
/* Enable Clock Power Management */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CAPABILITIES,
|
||||
0x40000, BIT(18));
|
||||
|
||||
/* Disable CS for RO(CS) register writes */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), 0);
|
||||
|
||||
/* Enable writes for RO(CS2) */
|
||||
ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_CS2_ENABLE, 0, BIT(0));
|
||||
|
||||
/* Set the Common Clock L0s Exit Latency to 2us-4us = 0x6 */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CAPABILITIES,
|
||||
0x38000, BIT(17) | BIT(16));
|
||||
|
||||
/* Set the Common Clock L1 Exit Latency to be 32us-64 us = 0x6 */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CAPABILITIES,
|
||||
0x7000, BIT(14) | BIT(13));
|
||||
|
||||
/* Disable writes for RO(CS2) */
|
||||
ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_CS2_ENABLE, BIT(0), 0);
|
||||
|
||||
/* T_Power_Off */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_L1_SUBSTATES_REG,
|
||||
BIT(1) | BIT(0), 0);
|
||||
|
||||
/* Set Device ID and Vendor ID */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0, BIT(0));
|
||||
ep_pcie_write_reg(dev->dm_core, PCIE20_DEVICE_ID_VENDOR_ID_REG,
|
||||
0x030217cb);
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), 0);
|
||||
|
||||
/* Configure link speed */
|
||||
ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CONTROL2_LINK_STATUS2,
|
||||
0xf, dev->link_speed);
|
||||
|
||||
/* Configure BARs */
|
||||
ep_pcie_bar_init(dev);
|
||||
|
||||
/* Enable MHI clocks */
|
||||
ep_pcie_write_reg(dev->parf, PCIE20_PARF_MHI_CLOCK_RESET_CTRL,
|
||||
BIT(1) | BIT(0));
|
||||
|
||||
ep_pcie_write_reg(dev->mmio, PCIE20_MHISTATUS, 0x0);
|
||||
ep_pcie_write_reg(dev->mmio, PCIE20_BHI_EXECENV, 0x2);
|
||||
}
|
||||
#else
|
||||
static void ep_pcie_core_init(struct ep_pcie_dev_t *dev)
|
||||
{
|
||||
EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
|
||||
|
@ -578,6 +690,7 @@ static void ep_pcie_core_init(struct ep_pcie_dev_t *dev)
|
|||
|
||||
ep_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x14);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ep_pcie_config_inbound_iatu(struct ep_pcie_dev_t *dev)
|
||||
{
|
||||
|
@ -694,6 +807,32 @@ static int ep_pcie_get_resources(struct ep_pcie_dev_t *dev,
|
|||
|
||||
EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev);
|
||||
|
||||
of_get_property(pdev->dev.of_node, "qcom,phy-sequence", &cnt);
|
||||
if (cnt) {
|
||||
dev->phy_sequence = (struct ep_pcie_phy_info_t *)
|
||||
devm_kzalloc(&pdev->dev, cnt, GFP_KERNEL);
|
||||
|
||||
if (dev->phy_sequence) {
|
||||
dev->phy_len =
|
||||
cnt / sizeof(*dev->phy_sequence);
|
||||
|
||||
of_property_read_u32_array(pdev->dev.of_node,
|
||||
"qcom,phy-sequence",
|
||||
(unsigned int *)dev->phy_sequence,
|
||||
cnt / sizeof(dev->phy_sequence->offset));
|
||||
} else {
|
||||
EP_PCIE_ERR(dev,
|
||||
"PCIe V%d: Failed to alloc mem for phy seq.\n",
|
||||
dev->rev);
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
EP_PCIE_DBG(dev,
|
||||
"PCIe V%d: phy sequence is not present in DT.\n",
|
||||
dev->rev);
|
||||
}
|
||||
|
||||
cnt = of_property_count_strings((&pdev->dev)->of_node,
|
||||
"clock-names");
|
||||
if (cnt > 0) {
|
||||
|
@ -782,6 +921,7 @@ static int ep_pcie_get_resources(struct ep_pcie_dev_t *dev,
|
|||
EP_PCIE_DBG(dev,
|
||||
"GPIO %s is not supported in this configuration.\n",
|
||||
gpio_info->name);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1070,6 +1210,9 @@ int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt)
|
|||
EP_PCIE_INFO(dev, "PCIe V%d: PCIe PHY is ready!\n", dev->rev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_MSM8996
|
||||
ep_pcie_phy_bringup_port(dev);
|
||||
#endif
|
||||
ep_pcie_core_init(dev);
|
||||
ep_pcie_config_inbound_iatu(dev);
|
||||
|
||||
|
@ -1977,6 +2120,17 @@ static int ep_pcie_probe(struct platform_device *pdev)
|
|||
EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-link-speed:%d.\n",
|
||||
ep_pcie_dev.rev, ep_pcie_dev.link_speed);
|
||||
|
||||
ret = of_property_read_u32((&pdev->dev)->of_node,
|
||||
"qcom,phy-status-reg",
|
||||
&ep_pcie_dev.phy_status_reg);
|
||||
if (ret)
|
||||
EP_PCIE_DBG(&ep_pcie_dev,
|
||||
"PCIe V%d: phy-status-reg does not exist.\n",
|
||||
ep_pcie_dev.rev);
|
||||
else
|
||||
EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: phy-status-reg:0x%x.\n",
|
||||
ep_pcie_dev.rev, ep_pcie_dev.phy_status_reg);
|
||||
|
||||
ep_pcie_dev.phy_rev = 1;
|
||||
ret = of_property_read_u32((&pdev->dev)->of_node,
|
||||
"qcom,pcie-phy-ver",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
|
@ -13,6 +13,7 @@
|
|||
/*
|
||||
* MSM PCIe PHY endpoint mode
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "ep_pcie_com.h"
|
||||
#include "ep_pcie_phy.h"
|
||||
|
@ -23,6 +24,28 @@ void ep_pcie_phy_init(struct ep_pcie_dev_t *dev)
|
|||
"PCIe V%d: PHY V%d: Initializing 14nm QMP phy - 100MHz\n",
|
||||
dev->rev, dev->phy_rev);
|
||||
|
||||
if (dev->phy_sequence) {
|
||||
int i;
|
||||
struct ep_pcie_phy_info_t *phy_seq;
|
||||
|
||||
EP_PCIE_DBG(dev,
|
||||
"PCIe V%d: PHY V%d: process the sequence specified by DT.!\n",
|
||||
dev->rev, dev->phy_rev);
|
||||
|
||||
i = dev->phy_len;
|
||||
phy_seq = dev->phy_sequence;
|
||||
while (i--) {
|
||||
ep_pcie_write_reg(dev->phy,
|
||||
phy_seq->offset,
|
||||
phy_seq->val);
|
||||
if (phy_seq->delay)
|
||||
usleep_range(phy_seq->delay,
|
||||
phy_seq->delay + 1);
|
||||
phy_seq++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ep_pcie_write_reg(dev->phy, PCIE_PHY_SW_RESET, 0x01);
|
||||
ep_pcie_write_reg(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x01);
|
||||
|
||||
|
@ -104,10 +127,26 @@ void ep_pcie_phy_init(struct ep_pcie_dev_t *dev)
|
|||
ep_pcie_write_reg(dev->phy, PCIE_PHY_START_CONTROL, 0x03);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_MSM8996
|
||||
void ep_pcie_phy_bringup_port(struct ep_pcie_dev_t *dev)
|
||||
{
|
||||
ep_pcie_write_reg(dev->phy, PCIE_PORT_POWER_DOWN_CONTROL, 0x03);
|
||||
ep_pcie_write_reg(dev->phy, PCIE_PORT_SW_RESET, 0x0);
|
||||
ep_pcie_write_reg(dev->phy, PCIE_PORT_START_CONTROL, 0x0a);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool ep_pcie_phy_is_ready(struct ep_pcie_dev_t *dev)
|
||||
{
|
||||
if (readl_relaxed(dev->phy + PCIE_PHY_PCS_STATUS) & BIT(6))
|
||||
return false;
|
||||
u32 offset;
|
||||
|
||||
if (dev->phy_status_reg)
|
||||
offset = dev->phy_status_reg;
|
||||
else
|
||||
offset = PCIE_PHY_PCS_STATUS;
|
||||
|
||||
if (readl_relaxed(dev->phy + offset) & BIT(0))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
|
@ -460,4 +460,34 @@
|
|||
#define PCIE_PHY_LFPS_PER_TIMER_VAL 0x9EC
|
||||
#define PCIE_PHY_SIGDET_STARTUP_TIMER_VAL 0x9F0
|
||||
#define PCIE_PHY_LOCK_DETECT_CONFIG4 0x9F4
|
||||
#ifdef CONFIG_ARCH_MSM8996
|
||||
#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x1054
|
||||
#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x1068
|
||||
#define QSERDES_TX_LANE_MODE 0x1094
|
||||
#define QSERDES_TX_RCV_DETECT_LVL_2 0x10AC
|
||||
#define QSERDES_RX_UCDR_FO_GAIN_HALF 0x1200
|
||||
#define QSERDES_RX_UCDR_FO_GAIN 0x120C
|
||||
#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x1210
|
||||
#define QSERDES_RX_UCDR_SO_GAIN 0x121C
|
||||
#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x1248
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x12D4
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x12D8
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x12DC
|
||||
#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x12E0
|
||||
#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x130C
|
||||
#define QSERDES_RX_SIGDET_ENABLES 0x1310
|
||||
#define QSERDES_RX_SIGDET_CNTRL 0x1314
|
||||
#define QSERDES_RX_SIGDET_LVL 0x1318
|
||||
#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x131C
|
||||
#define PCIE_PHY_SW_RESET 0x400
|
||||
#define PCIE_PHY_POWER_DOWN_CONTROL 0x404
|
||||
#define PCIE_PHY_START_CONTROL 0x408
|
||||
#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE 0x1454
|
||||
#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK 0x14A0
|
||||
#define PCIE_PORT_ENDPOINT_REFCLK_DRIVE 0x1454
|
||||
#define PCIE_PORT_POWER_DOWN_CONTROL 0x1404
|
||||
#define PCIE_PORT_SW_RESET 0x1400
|
||||
#define PCIE_PORT_START_CONTROL 0x1408
|
||||
#define PCIE_COM_PCS_READY_STATUS 0x448
|
||||
#endif
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue