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:
Yimin Peng 2018-07-11 11:59:53 +08:00
parent 595eeed398
commit ef6d23ad26
4 changed files with 256 additions and 14 deletions

View file

@ -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

View file

@ -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",

View file

@ -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;
}

View file

@ -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