diff options
author | Yimin Peng <yiminp@codeaurora.org> | 2018-07-11 11:59:53 +0800 |
---|---|---|
committer | Yimin Peng <yiminp@codeaurora.org> | 2018-10-24 17:11:18 +0800 |
commit | ef6d23ad264c84f1af76014c040102f6f660e26d (patch) | |
tree | d4b999b91102d8dcdbbabaa3dbbe3402913b0580 /drivers/platform | |
parent | 595eeed39838e4f975fc1329e500694d8e4b83ea (diff) |
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>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/msm/ep_pcie/ep_pcie_com.h | 25 | ||||
-rw-r--r-- | drivers/platform/msm/ep_pcie/ep_pcie_core.c | 168 | ||||
-rw-r--r-- | drivers/platform/msm/ep_pcie/ep_pcie_phy.c | 45 | ||||
-rw-r--r-- | drivers/platform/msm/ep_pcie/ep_pcie_phy.h | 32 |
4 files changed, 256 insertions, 14 deletions
diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h index 4b89c8636141..0c3fb1676eea 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_com.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -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 diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c index f3cc96c271a7..f6d39031c5d5 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_core.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -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", diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c index 83e74f2c7ba7..3dfabd3ed81f 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c @@ -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; } diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.h b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h index 199e0760956a..884f9fdf7dba 100644 --- a/drivers/platform/msm/ep_pcie/ep_pcie_phy.h +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h @@ -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 |