diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 9b52eda68220..706a6e4b5179 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -42,6 +42,27 @@ config SPS_SUPPORT_NDP_BAM to do the actual data transfer themselves, instead of the BAM. +config EP_PCIE + bool "PCIe Endpoint mode support" + select GENERIC_ALLOCATOR + help + PCIe controller is in endpoint mode. + It supports the APIs to clients as a service layer, and allows + clients to enable/disable PCIe link, configure the address + mapping for the access to host memory, trigger wake interrupt + on host side to wake up host, and trigger MSI to host side. + +config EP_PCIE_HW + bool "PCIe Endpoint HW driver" + depends on EP_PCIE + help + PCIe endpoint HW specific implementation. + It supports: + 1. link training with Root Complex. + 2. Address mapping. + 3. Sideband signaling. + 4. Power management. + config IPA3 tristate "IPA3 support" depends on NET diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index 09c2048fe5c2..6448dd18fb5f 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_USB_BAM) += usb_bam.o obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o obj-$(CONFIG_SPS) += sps/ obj-$(CONFIG_MSM_11AD) += msm_11ad/ +obj-$(CONFIG_EP_PCIE) += ep_pcie/ diff --git a/drivers/platform/msm/ep_pcie/Makefile b/drivers/platform/msm/ep_pcie/Makefile new file mode 100644 index 000000000000..03de3e7eb7a5 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# +# Makefile for the PCIE EP specific device drivers. +# +obj-$(CONFIG_EP_PCIE) += ep_pcie.o +obj-$(CONFIG_EP_PCIE_HW) += ep_pcie_core.o ep_pcie_phy.o ep_pcie_dbg.o diff --git a/drivers/platform/msm/ep_pcie/ep_pcie.c b/drivers/platform/msm/ep_pcie/ep_pcie.c new file mode 100644 index 000000000000..c814b43ee4fa --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015, 2017-2020, The Linux Foundation. All rights reserved.*/ + +/* + * MSM PCIe endpoint service layer. + */ +#include +#include +#include +#include +#include +#include +#include "ep_pcie_com.h" + +LIST_HEAD(head); + +int ep_pcie_register_drv(struct ep_pcie_hw *handle) +{ + struct ep_pcie_hw *present; + bool new = true; + + if (WARN_ON(!handle)) + return -EINVAL; + + list_for_each_entry(present, &head, node) { + if (present->device_id == handle->device_id) { + new = false; + break; + } + } + + if (new) { + list_add(&handle->node, &head); + pr_debug("ep_pcie:%s: register a new driver for device 0x%x\n", + __func__, handle->device_id); + return 0; + } + pr_debug( + "ep_pcie:%s: driver to register for device 0x%x has already existed\n", + __func__, handle->device_id); + return -EEXIST; +} +EXPORT_SYMBOL(ep_pcie_register_drv); + +int ep_pcie_deregister_drv(struct ep_pcie_hw *handle) +{ + struct ep_pcie_hw *present; + bool found = false; + + if (WARN_ON(!handle)) + return -EINVAL; + + list_for_each_entry(present, &head, node) { + if (present->device_id == handle->device_id) { + found = true; + list_del(&handle->node); + break; + } + } + + if (found) { + pr_debug("ep_pcie:%s: deregistered driver for device 0x%x\n", + __func__, handle->device_id); + return 0; + } + pr_err("ep_pcie:%s: driver for device 0x%x does not exist\n", + __func__, handle->device_id); + return -EEXIST; +} +EXPORT_SYMBOL(ep_pcie_deregister_drv); + +struct ep_pcie_hw *ep_pcie_get_phandle(u32 id) +{ + struct ep_pcie_hw *present; + + list_for_each_entry(present, &head, node) { + if (present->device_id == id) { + pr_debug("ep_pcie:%s: found driver for device 0x%x\n", + __func__, id); + return present; + } + } + + pr_debug("ep_pcie:%s: driver for device 0x%x does not exist\n", + __func__, id); + return NULL; +} +EXPORT_SYMBOL(ep_pcie_get_phandle); + +int ep_pcie_configure_inactivity_timer(struct ep_pcie_hw *phandle, + struct ep_pcie_inactivity *param) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->configure_inactivity_timer(param); +} +EXPORT_SYMBOL(ep_pcie_configure_inactivity_timer); + +int ep_pcie_register_event(struct ep_pcie_hw *phandle, + struct ep_pcie_register_event *reg) +{ + if (phandle) + return phandle->register_event(reg); + + return ep_pcie_core_register_event(reg); +} +EXPORT_SYMBOL(ep_pcie_register_event); + +int ep_pcie_deregister_event(struct ep_pcie_hw *phandle) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->deregister_event(); +} +EXPORT_SYMBOL(ep_pcie_deregister_event); + +enum ep_pcie_link_status ep_pcie_get_linkstatus(struct ep_pcie_hw *phandle) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->get_linkstatus(); +} +EXPORT_SYMBOL(ep_pcie_get_linkstatus); + +int ep_pcie_config_outbound_iatu(struct ep_pcie_hw *phandle, + struct ep_pcie_iatu entries[], + u32 num_entries) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->config_outbound_iatu(entries, num_entries); +} +EXPORT_SYMBOL(ep_pcie_config_outbound_iatu); + +int ep_pcie_get_msi_config(struct ep_pcie_hw *phandle, + struct ep_pcie_msi_config *cfg) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->get_msi_config(cfg); +} +EXPORT_SYMBOL(ep_pcie_get_msi_config); + +int ep_pcie_trigger_msi(struct ep_pcie_hw *phandle, u32 idx) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->trigger_msi(idx); +} +EXPORT_SYMBOL(ep_pcie_trigger_msi); + +int ep_pcie_wakeup_host(struct ep_pcie_hw *phandle, + enum ep_pcie_event event) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->wakeup_host(event); +} +EXPORT_SYMBOL(ep_pcie_wakeup_host); + +int ep_pcie_config_db_routing(struct ep_pcie_hw *phandle, + struct ep_pcie_db_config chdb_cfg, + struct ep_pcie_db_config erdb_cfg) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->config_db_routing(chdb_cfg, erdb_cfg); +} +EXPORT_SYMBOL(ep_pcie_config_db_routing); + +int ep_pcie_enable_endpoint(struct ep_pcie_hw *phandle, + enum ep_pcie_options opt) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->enable_endpoint(opt); +} +EXPORT_SYMBOL(ep_pcie_enable_endpoint); + +int ep_pcie_disable_endpoint(struct ep_pcie_hw *phandle) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->disable_endpoint(); +} +EXPORT_SYMBOL(ep_pcie_disable_endpoint); + +int ep_pcie_mask_irq_event(struct ep_pcie_hw *phandle, + enum ep_pcie_irq_event event, + bool enable) +{ + if (WARN_ON(!phandle)) + return -EINVAL; + + return phandle->mask_irq_event(event, enable); +} +EXPORT_SYMBOL(ep_pcie_mask_irq_event); diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_com.h b/drivers/platform/msm/ep_pcie/ep_pcie_com.h new file mode 100644 index 000000000000..04c877849939 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -0,0 +1,451 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ + +#ifndef __EP_PCIE_COM_H +#define __EP_PCIE_COM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIE20_PARF_SYS_CTRL 0x00 +#define PCIE20_PARF_DB_CTRL 0x10 +#define PCIE20_PARF_PM_CTRL 0x20 +#define PCIE20_PARF_PM_STTS 0x24 +#define PCIE20_PARF_PHY_CTRL 0x40 +#define PCIE20_PARF_PHY_REFCLK 0x4C +#define PCIE20_PARF_CONFIG_BITS 0x50 +#define PCIE20_PARF_TEST_BUS 0xE4 +#define PCIE20_PARF_MHI_BASE_ADDR_LOWER 0x178 +#define PCIE20_PARF_MHI_BASE_ADDR_UPPER 0x17c +#define PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER 0x180 +#define PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER_RESET_MASK 0x8000000 +#define PCIE20_PARF_MSI_GEN 0x188 +#define PCIE20_PARF_DEBUG_INT_EN 0x190 +#define PCIE20_PARF_DEBUG_INT_EN_L1SUB_TIMEOUT_BIT_MASK BIT(0) +#define PCIE20_PARF_MHI_IPA_DBS 0x198 +#define PCIE20_PARF_MHI_IPA_CDB_TARGET_LOWER 0x19C +#define PCIE20_PARF_MHI_IPA_EDB_TARGET_LOWER 0x1A0 +#define PCIE20_PARF_AXI_MSTR_RD_HALT_NO_WRITES 0x1A4 +#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT 0x1A8 +#define PCIE20_PARF_Q2A_FLUSH 0x1AC +#define PCIE20_PARF_LTSSM 0x1B0 +#define PCIE20_PARF_CFG_BITS 0x210 +#define PCIE20_PARF_LTR_MSI_EXIT_L1SS 0x214 +#define PCIE20_PARF_INT_ALL_STATUS 0x224 +#define PCIE20_PARF_INT_ALL_CLEAR 0x228 +#define PCIE20_PARF_INT_ALL_MASK 0x22C + +#define PCIE20_PARF_CLKREQ_OVERRIDE 0x2B0 +#define PCIE20_PARF_CLKREQ_IN_OVERRIDE_STS BIT(5) +#define PCIE20_PARF_CLKREQ_OE_OVERRIDE_STS BIT(4) +#define PCIE20_PARF_CLKREQ_IN_OVERRIDE_VAL BIT(3) +#define PCIE20_PARF_CLKREQ_OE_OVERRIDE_VAL BIT(2) +#define PCIE20_PARF_CLKREQ_IN_OVERRIDE_ENABLE BIT(1) +#define PCIE20_PARF_CLKREQ_OE_OVERRIDE_ENABLE BIT(0) + +#define PCIE20_PARF_SLV_ADDR_MSB_CTRL 0x2C0 +#define PCIE20_PARF_DBI_BASE_ADDR 0x350 +#define PCIE20_PARF_DBI_BASE_ADDR_HI 0x354 +#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x358 +#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE_HI 0x35C + +#define PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS 0x4D0 +#define PCIE20_PARF_L1SS_SLEEP_MHI_FWD_DISABLE BIT(5) +#define PCIE20_PARF_L1SS_SLEEP_MHI_FWD_ENABLE BIT(4) + +#define PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_CONFIG 0x4D4 + +#define PCIE20_PARF_ATU_BASE_ADDR 0x634 +#define PCIE20_PARF_ATU_BASE_ADDR_HI 0x638 +#define PCIE20_PARF_SRIS_MODE 0x644 +#define PCIE20_PARF_BUS_DISCONNECT_CTRL 0x648 +#define PCIE20_PARF_BUS_DISCONNECT_STATUS 0x64c +#define PCIE20_PARF_BDF_TO_SID_CFG 0x2c00 + +#define PCIE20_PARF_DEVICE_TYPE 0x1000 +#define PCIE20_PARF_EDMA_BASE_ADDR 0x64C +#define PCIE20_PARF_EDMA_BASE_ADDR_HI 0x650 + +#define PCIE20_ELBI_VERSION 0x00 +#define PCIE20_ELBI_SYS_CTRL 0x04 +#define PCIE20_ELBI_SYS_STTS 0x08 +#define PCIE20_ELBI_CS2_ENABLE 0xA4 + +#define PCIE20_DEVICE_ID_VENDOR_ID 0x00 +#define PCIE20_MASK_DEVICE_ID GENMASK(31, 16) +#define PCIE20_MASK_VENDOR_ID GENMASK(15, 0) +#define PCIE20_COMMAND_STATUS 0x04 +#define PCIE20_CLASS_CODE_REVISION_ID 0x08 +#define PCIE20_BIST_HDR_TYPE 0x0C +#define PCIE20_BAR0 0x10 +#define PCIE20_SUBSYSTEM 0x2c +#define PCIE20_CAP_ID_NXT_PTR 0x40 +#define PCIE20_CON_STATUS 0x44 +#define PCIE20_MSI_CAP_ID_NEXT_CTRL 0x50 +#define PCIE20_MSI_LOWER 0x54 +#define PCIE20_MSI_UPPER 0x58 +#define PCIE20_MSI_DATA 0x5C +#define PCIE20_MSI_MASK 0x60 +#define PCIE20_DEVICE_CAPABILITIES 0x74 +#define PCIE20_MASK_EP_L1_ACCPT_LATENCY 0xE00 +#define PCIE20_MASK_EP_L0S_ACCPT_LATENCY 0x1C0 +#define PCIE20_LINK_CAPABILITIES 0x7C +#define PCIE20_MASK_CLOCK_POWER_MAN 0x40000 +#define PCIE20_MASK_L1_EXIT_LATENCY 0x38000 +#define PCIE20_MASK_L0S_EXIT_LATENCY 0x7000 +#define PCIE20_CAP_LINKCTRLSTATUS 0x80 +#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98 +#define PCIE20_LINK_CONTROL2_LINK_STATUS2 0xA0 +#define PCIE20_L1SUB_CAPABILITY 0x154 +#define PCIE20_L1SUB_CONTROL1 0x158 +#define PCIE20_BUS_DISCONNECT_STATUS 0x68c +#define PCIE20_ACK_F_ASPM_CTRL_REG 0x70C +#define PCIE20_MASK_ACK_N_FTS 0xff00 +#define PCIE20_MISC_CONTROL_1 0x8BC + +#define PCIE20_PLR_IATU_VIEWPORT 0x900 +#define PCIE20_PLR_IATU_CTRL1 0x904 +#define PCIE20_PLR_IATU_CTRL2 0x908 +#define PCIE20_PLR_IATU_LBAR 0x90C +#define PCIE20_PLR_IATU_UBAR 0x910 +#define PCIE20_PLR_IATU_LAR 0x914 +#define PCIE20_PLR_IATU_LTAR 0x918 +#define PCIE20_PLR_IATU_UTAR 0x91c + +#define PCIE20_IATU_BASE(n) (n * 0x200) + +#define PCIE20_IATU_O_CTRL1(n) (PCIE20_IATU_BASE(n) + 0x00) +#define PCIE20_IATU_O_CTRL2(n) (PCIE20_IATU_BASE(n) + 0x04) +#define PCIE20_IATU_O_LBAR(n) (PCIE20_IATU_BASE(n) + 0x08) +#define PCIE20_IATU_O_UBAR(n) (PCIE20_IATU_BASE(n) + 0x0c) +#define PCIE20_IATU_O_LAR(n) (PCIE20_IATU_BASE(n) + 0x10) +#define PCIE20_IATU_O_LTAR(n) (PCIE20_IATU_BASE(n) + 0x14) +#define PCIE20_IATU_O_UTAR(n) (PCIE20_IATU_BASE(n) + 0x18) + +#define PCIE20_IATU_I_CTRL1(n) (PCIE20_IATU_BASE(n) + 0x100) +#define PCIE20_IATU_I_CTRL2(n) (PCIE20_IATU_BASE(n) + 0x104) +#define PCIE20_IATU_I_LBAR(n) (PCIE20_IATU_BASE(n) + 0x108) +#define PCIE20_IATU_I_UBAR(n) (PCIE20_IATU_BASE(n) + 0x10c) +#define PCIE20_IATU_I_LAR(n) (PCIE20_IATU_BASE(n) + 0x110) +#define PCIE20_IATU_I_LTAR(n) (PCIE20_IATU_BASE(n) + 0x114) +#define PCIE20_IATU_I_UTAR(n) (PCIE20_IATU_BASE(n) + 0x118) + +#define PCIE20_MHICFG 0x110 +#define PCIE20_BHI_EXECENV 0x228 +#define PCIE20_MHIVER 0x108 +#define PCIE20_MHICTRL 0x138 +#define PCIE20_MHISTATUS 0x148 +#define PCIE20_BHI_VERSION_LOWER 0x200 +#define PCIE20_BHI_VERSION_UPPER 0x204 +#define PCIE20_BHI_INTVEC 0x220 + +#define PCIE20_AUX_CLK_FREQ_REG 0xB40 +#define PCIE20_GEN3_RELATED_OFF 0x890 + +#define PERST_TIMEOUT_US_MIN 1000 +#define PERST_TIMEOUT_US_MAX 1000 +#define PERST_CHECK_MAX_COUNT 30000 +#define LINK_UP_TIMEOUT_US_MIN 1000 +#define LINK_UP_TIMEOUT_US_MAX 1000 +#define LINK_UP_CHECK_MAX_COUNT 30000 +#define BME_TIMEOUT_US_MIN 1000 +#define BME_TIMEOUT_US_MAX 1000 +#define BME_CHECK_MAX_COUNT 100000 +#define PHY_STABILIZATION_DELAY_US_MIN 1000 +#define PHY_STABILIZATION_DELAY_US_MAX 1000 +#define REFCLK_STABILIZATION_DELAY_US_MIN 1000 +#define REFCLK_STABILIZATION_DELAY_US_MAX 1000 +#define PHY_READY_TIMEOUT_COUNT 30000 +#define MSI_EXIT_L1SS_WAIT 10 +#define MSI_EXIT_L1SS_WAIT_MAX_COUNT 100 +#define XMLH_LINK_UP 0x400 +#define PARF_XMLH_LINK_UP 0x40000000 + +#define MAX_PROP_SIZE 32 +#define MAX_MSG_LEN 80 +#define MAX_NAME_LEN 80 +#define MAX_IATU_ENTRY_NUM 2 + +#define EP_PCIE_LOG_PAGES 50 +#define EP_PCIE_MAX_VREG 3 +#define EP_PCIE_MAX_CLK 7 +#define EP_PCIE_MAX_PIPE_CLK 1 +#define EP_PCIE_MAX_RESET 2 + +#define EP_PCIE_ERROR -30655 +#define EP_PCIE_LINK_DOWN 0xFFFFFFFF + +#define EP_PCIE_OATU_INDEX_MSI 1 +#define EP_PCIE_OATU_INDEX_CTRL 2 +#define EP_PCIE_OATU_INDEX_DATA 3 + +#define EP_PCIE_OATU_UPPER 0x100 + +#define EP_PCIE_GEN_DBG(x...) do { \ + if (ep_pcie_get_debug_mask()) \ + pr_alert(x); \ + else \ + pr_debug(x); \ + } while (0) + +#define EP_PCIE_DBG(dev, fmt, arg...) do { \ + ipc_log_string((dev)->ipc_log_ful, "%s: " fmt, __func__, arg); \ + if (ep_pcie_get_debug_mask()) \ + pr_alert("%s: " fmt, __func__, arg); \ + } while (0) + +#define EP_PCIE_DBG2(dev, fmt, arg...) do { \ + ipc_log_string((dev)->ipc_log_sel, \ + "DBG1:%s: " fmt, __func__, arg); \ + ipc_log_string((dev)->ipc_log_ful, \ + "DBG2:%s: " fmt, __func__, arg); \ + if (ep_pcie_get_debug_mask()) \ + pr_alert("%s: " fmt, __func__, arg); \ + } while (0) + +#define EP_PCIE_DBG_FS(fmt, arg...) pr_alert("%s: " fmt, __func__, arg) + +#define EP_PCIE_DUMP(dev, fmt, arg...) do { \ + ipc_log_string((dev)->ipc_log_dump, \ + "DUMP:%s: " fmt, __func__, arg); \ + if (ep_pcie_get_debug_mask()) \ + pr_alert("%s: " fmt, __func__, arg); \ + } while (0) + +#define EP_PCIE_INFO(dev, fmt, arg...) do { \ + ipc_log_string((dev)->ipc_log_sel, \ + "INFO:%s: " fmt, __func__, arg); \ + ipc_log_string((dev)->ipc_log_ful, "%s: " fmt, __func__, arg); \ + pr_info("%s: " fmt, __func__, arg); \ + } while (0) + +#define EP_PCIE_ERR(dev, fmt, arg...) do { \ + ipc_log_string((dev)->ipc_log_sel, \ + "ERR:%s: " fmt, __func__, arg); \ + ipc_log_string((dev)->ipc_log_ful, "%s: " fmt, __func__, arg); \ + pr_err("%s: " fmt, __func__, arg); \ + } while (0) + +enum ep_pcie_res { + EP_PCIE_RES_PARF, + EP_PCIE_RES_PHY, + EP_PCIE_RES_MMIO, + EP_PCIE_RES_MSI, + EP_PCIE_RES_DM_CORE, + EP_PCIE_RES_ELBI, + EP_PCIE_RES_IATU, + EP_PCIE_RES_EDMA, + EP_PCIE_RES_TCSR_PERST, + EP_PCIE_MAX_RES, +}; + +enum ep_pcie_irq { + EP_PCIE_INT_PM_TURNOFF, + EP_PCIE_INT_DSTATE_CHANGE, + EP_PCIE_INT_L1SUB_TIMEOUT, + EP_PCIE_INT_LINK_UP, + EP_PCIE_INT_LINK_DOWN, + EP_PCIE_INT_BRIDGE_FLUSH_N, + EP_PCIE_INT_BME, + EP_PCIE_INT_GLOBAL, + EP_PCIE_MAX_IRQ, +}; + +enum ep_pcie_gpio { + EP_PCIE_GPIO_PERST, + EP_PCIE_GPIO_WAKE, + EP_PCIE_GPIO_CLKREQ, + EP_PCIE_GPIO_MDM2AP, + EP_PCIE_MAX_GPIO, +}; + +struct ep_pcie_gpio_info_t { + char *name; + u32 num; + bool out; + u32 on; + u32 init; +}; + +struct ep_pcie_vreg_info_t { + struct regulator *hdl; + char *name; + u32 max_v; + u32 min_v; + u32 opt_mode; + bool required; +}; + +struct ep_pcie_clk_info_t { + struct clk *hdl; + char *name; + u32 freq; + bool required; +}; + +struct ep_pcie_reset_info_t { + struct reset_control *hdl; + char *name; + bool required; +}; + +struct ep_pcie_res_info_t { + char *name; + struct resource *resource; + void __iomem *base; +}; + +struct ep_pcie_irq_info_t { + char *name; + u32 num; +}; + +/* phy info structure */ +struct ep_pcie_phy_info_t { + u32 offset; + u32 val; + u32 delay; + u32 direction; +}; + +/* pcie endpoint device structure */ +struct ep_pcie_dev_t { + struct platform_device *pdev; + struct regulator *gdsc; + struct ep_pcie_vreg_info_t vreg[EP_PCIE_MAX_VREG]; + struct ep_pcie_gpio_info_t gpio[EP_PCIE_MAX_GPIO]; + struct ep_pcie_clk_info_t clk[EP_PCIE_MAX_CLK]; + struct ep_pcie_clk_info_t pipeclk[EP_PCIE_MAX_PIPE_CLK]; + struct ep_pcie_reset_info_t reset[EP_PCIE_MAX_RESET]; + struct ep_pcie_irq_info_t irq[EP_PCIE_MAX_IRQ]; + struct ep_pcie_res_info_t res[EP_PCIE_MAX_RES]; + + u32 mmio_res_size; + void __iomem *parf; + void __iomem *phy; + void __iomem *mmio; + void __iomem *msi; + void __iomem *dm_core; + void __iomem *edma; + void __iomem *elbi; + void __iomem *iatu; + void __iomem *tcsr_perst_en; + + struct msm_bus_scale_pdata *bus_scale_table; + struct icc_path *icc_path; + u16 vendor_id; + u16 device_id; + u32 subsystem_id; + u32 link_speed; + bool active_config; + bool aggregated_irq; + bool mhi_a7_irq; + bool pcie_edma; + bool tcsr_not_supported; + bool m2_autonomous; + bool mhi_soc_reset_en; + u32 dbi_base_reg; + u32 slv_space_reg; + u32 phy_status_reg; + u32 phy_status_bit_mask_bit; + u32 phy_init_len; + u32 mhi_soc_reset_offset; + struct ep_pcie_phy_info_t *phy_init; + bool perst_enum; + + u32 rev; + u32 phy_rev; + void *ipc_log_sel; + void *ipc_log_ful; + void *ipc_log_dump; + struct mutex setup_mtx; + struct mutex ext_mtx; + spinlock_t ext_lock; + unsigned long ext_save_flags; + + spinlock_t isr_lock; + unsigned long isr_save_flags; + ulong linkdown_counter; + ulong linkup_counter; + ulong bme_counter; + ulong pm_to_counter; + ulong d0_counter; + ulong d3_counter; + ulong perst_ast_counter; + ulong perst_deast_counter; + ulong wake_counter; + ulong msi_counter; + ulong global_irq_counter; + + bool dump_conf; + bool config_mmio_init; + bool enumerated; + enum ep_pcie_link_status link_status; + bool power_on; + bool suspending; + bool l23_ready; + bool l1ss_enabled; + struct ep_pcie_msi_config msi_cfg; + bool no_notify; + bool client_ready; + atomic_t ep_pcie_dev_wake; + atomic_t perst_deast; + + struct ep_pcie_register_event *event_reg; + struct work_struct handle_perst_work; + struct work_struct handle_bme_work; + struct work_struct handle_d3cold_work; +}; + +extern struct ep_pcie_dev_t ep_pcie_dev; +extern struct ep_pcie_hw hw_drv; + +static inline void ep_pcie_write_mask(void __iomem *addr, + u32 clear_mask, u32 set_mask) +{ + u32 val; + + val = (readl_relaxed(addr) & ~clear_mask) | set_mask; + writel_relaxed(val, addr); + /* ensure register write goes through before next regiser operation */ + wmb(); +} + +static inline void ep_pcie_write_reg(void __iomem *base, u32 offset, u32 value) +{ + writel_relaxed(value, base + offset); + /* ensure register write goes through before next regiser operation */ + wmb(); +} + +static inline void ep_pcie_write_reg_field(void __iomem *base, u32 offset, + const u32 mask, u32 val) +{ + u32 shift = find_first_bit((void *)&mask, 32); + u32 tmp = readl_relaxed(base + offset); + + tmp &= ~mask; /* clear written bits */ + val = tmp | (val << shift); + writel_relaxed(val, base + offset); + /* ensure register write goes through before next regiser operation */ + wmb(); +} + +extern int ep_pcie_core_register_event(struct ep_pcie_register_event *reg); +extern int ep_pcie_get_debug_mask(void); +extern void ep_pcie_phy_init(struct ep_pcie_dev_t *dev); +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); + +#endif diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_core.c b/drivers/platform/msm/ep_pcie/ep_pcie_core.c new file mode 100644 index 000000000000..faab36af785a --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -0,0 +1,3381 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ + +/* + * MSM PCIe endpoint core driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ep_pcie_com.h" +#include +#include +#include + +#define PCIE_MHI_STATUS(n) ((n) + 0x148) +#define TCSR_PERST_SEPARATION_ENABLE 0x270 +#define PCIE_ISSUE_WAKE 1 +#define PCIE_MHI_FWD_STATUS_MIN 5000 +#define PCIE_MHI_FWD_STATUS_MAX 5100 +#define PCIE_MHI_FWD_COUNT 200 +#define PCIE_L1SUB_AHB_TIMEOUT_MIN 100 +#define PCIE_L1SUB_AHB_TIMEOUT_MAX 120 + +#define ICC_AVG_BW 500 +#define ICC_PEAK_BW 800 + +/* debug mask sys interface */ +static int ep_pcie_debug_mask; +static int ep_pcie_debug_keep_resource; +static u32 ep_pcie_bar0_address; +static bool m2_enabled; +static u32 clkreq_irq; + +struct ep_pcie_dev_t ep_pcie_dev = {0}; + +static struct ep_pcie_vreg_info_t ep_pcie_vreg_info[EP_PCIE_MAX_VREG] = { + {NULL, "vreg-1.8", 1800000, 1800000, 14000, true}, + {NULL, "vreg-0.9", 1000000, 1000000, 40000, true}, + {NULL, "vreg-cx", 0, 0, 0, false} +}; + +static struct ep_pcie_gpio_info_t ep_pcie_gpio_info[EP_PCIE_MAX_GPIO] = { + {"perst-gpio", 0, 0, 0, 1}, + {"wake-gpio", 0, 1, 0, 1}, + {"clkreq-gpio", 0, 0, 0, 1}, + {"mdm2apstatus-gpio", 0, 1, 1, 0}, +}; + +static struct ep_pcie_clk_info_t + ep_pcie_clk_info[EP_PCIE_MAX_CLK] = { + {NULL, "pcie_cfg_ahb_clk", 0, true}, + {NULL, "pcie_mstr_axi_clk", 0, true}, + {NULL, "pcie_slv_axi_clk", 0, true}, + {NULL, "pcie_aux_clk", 1000000, true}, + {NULL, "pcie_ldo", 0, true}, + {NULL, "pcie_sleep_clk", 0, false}, + {NULL, "pcie_slv_q2a_axi_clk", 0, false}, +}; + +static struct ep_pcie_clk_info_t + ep_pcie_pipe_clk_info[EP_PCIE_MAX_PIPE_CLK] = { + {NULL, "pcie_pipe_clk", 62500000, true}, +}; + +static struct ep_pcie_reset_info_t + ep_pcie_reset_info[EP_PCIE_MAX_RESET] = { + {NULL, "pcie_core_reset", false}, + {NULL, "pcie_phy_reset", false}, +}; + +static const struct ep_pcie_res_info_t ep_pcie_res_info[EP_PCIE_MAX_RES] = { + {"parf", NULL, NULL}, + {"phy", NULL, NULL}, + {"mmio", NULL, NULL}, + {"msi", NULL, NULL}, + {"dm_core", NULL, NULL}, + {"elbi", NULL, NULL}, + {"iatu", NULL, NULL}, + {"edma", NULL, NULL}, + {"tcsr_pcie_perst_en", NULL, NULL}, +}; + +static const struct ep_pcie_irq_info_t ep_pcie_irq_info[EP_PCIE_MAX_IRQ] = { + {"int_pm_turnoff", 0}, + {"int_dstate_change", 0}, + {"int_l1sub_timeout", 0}, + {"int_link_up", 0}, + {"int_link_down", 0}, + {"int_bridge_flush_n", 0}, + {"int_bme", 0}, + {"int_global", 0}, +}; + +int ep_pcie_get_debug_mask(void) +{ + return ep_pcie_debug_mask; +} + +static bool ep_pcie_confirm_linkup(struct ep_pcie_dev_t *dev, + bool check_sw_stts) +{ + u32 val; + + if (check_sw_stts && (dev->link_status != EP_PCIE_LINK_ENABLED)) { + EP_PCIE_DBG(dev, "PCIe V%d: The link is not enabled\n", + dev->rev); + return false; + } + + val = readl_relaxed(dev->dm_core); + EP_PCIE_DBG(dev, "PCIe V%d: device ID and vendor ID are 0x%x\n", + dev->rev, val); + if (val == EP_PCIE_LINK_DOWN) { + EP_PCIE_ERR(dev, + "PCIe V%d: The link is not really up; device ID and vendor ID are 0x%x\n", + dev->rev, val); + return false; + } + + return true; +} + +static int ep_pcie_reset_init(struct ep_pcie_dev_t *dev) +{ + int i, rc = 0; + struct ep_pcie_reset_info_t *reset_info; + + for (i = 0; i < EP_PCIE_MAX_RESET; i++) { + reset_info = &dev->reset[i]; + if (!reset_info->hdl) + continue; + + rc = reset_control_assert(reset_info->hdl); + if (rc) { + if (!reset_info->required) { + EP_PCIE_ERR(dev, + "PCIe V%d: Optional reset: %s assert failed\n", + dev->rev, reset_info->name); + continue; + } else { + EP_PCIE_ERR(dev, + "PCIe V%d: failed to assert reset for %s\n", + dev->rev, reset_info->name); + return rc; + } + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: successfully asserted reset for %s\n", + dev->rev, reset_info->name); + } + EP_PCIE_ERR(dev, "After Reset assert %s\n", + reset_info->name); + /* add a 1ms delay to ensure the reset is asserted */ + usleep_range(1000, 1005); + + rc = reset_control_deassert(reset_info->hdl); + if (rc) { + if (!reset_info->required) { + EP_PCIE_ERR(dev, + "PCIe V%d: Optional reset: %s deassert failed\n", + dev->rev, reset_info->name); + continue; + } else { + EP_PCIE_ERR(dev, + "PCIe V%d: failed to deassert reset for %s\n", + dev->rev, reset_info->name); + return rc; + } + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: successfully deasserted reset for %s\n", + dev->rev, reset_info->name); + } + EP_PCIE_ERR(dev, "After Reset de-assert %s\n", + reset_info->name); + } + return 0; +} + +static int ep_pcie_gpio_init(struct ep_pcie_dev_t *dev) +{ + int i, rc = 0; + struct ep_pcie_gpio_info_t *info; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = 0; i < EP_PCIE_MAX_GPIO; i++) { + info = &dev->gpio[i]; + + if (!info->num) { + 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); + if (rc) { + EP_PCIE_ERR(dev, "PCIe V%d: can't get gpio %s; %d\n", + dev->rev, info->name, rc); + break; + } + + if (info->out) + rc = gpio_direction_output(info->num, info->init); + else + rc = gpio_direction_input(info->num); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't set direction for GPIO %s:%d\n", + dev->rev, info->name, rc); + gpio_free(info->num); + break; + } + } + + if (rc) + while (i--) + gpio_free(dev->gpio[i].num); + + return rc; +} + +static void ep_pcie_gpio_deinit(struct ep_pcie_dev_t *dev) +{ + int i; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = 0; i < EP_PCIE_MAX_GPIO; i++) + gpio_free(dev->gpio[i].num); +} + +static int ep_pcie_vreg_init(struct ep_pcie_dev_t *dev) +{ + int i, rc = 0; + struct regulator *vreg; + struct ep_pcie_vreg_info_t *info; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = 0; i < EP_PCIE_MAX_VREG; i++) { + info = &dev->vreg[i]; + vreg = info->hdl; + + if (!vreg) { + EP_PCIE_ERR(dev, + "PCIe V%d: handle of Vreg %s is NULL\n", + dev->rev, info->name); + rc = -EINVAL; + break; + } + + EP_PCIE_DBG(dev, "PCIe V%d: Vreg %s is being enabled\n", + dev->rev, info->name); + if (info->max_v) { + rc = regulator_set_voltage(vreg, + info->min_v, info->max_v); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't set voltage for %s: %d\n", + dev->rev, info->name, rc); + break; + } + } + + if (info->opt_mode) { + rc = regulator_set_load(vreg, info->opt_mode); + if (rc < 0) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't set mode for %s: %d\n", + dev->rev, info->name, rc); + break; + } + } + + rc = regulator_enable(vreg); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't enable regulator %s: %d\n", + dev->rev, info->name, rc); + break; + } + } + + if (rc) + while (i--) { + struct regulator *hdl = dev->vreg[i].hdl; + + if (hdl) + regulator_disable(hdl); + } + + return rc; +} + +static void ep_pcie_vreg_deinit(struct ep_pcie_dev_t *dev) +{ + int i; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = EP_PCIE_MAX_VREG - 1; i >= 0; i--) { + if (dev->vreg[i].hdl) { + EP_PCIE_DBG(dev, "Vreg %s is being disabled\n", + dev->vreg[i].name); + regulator_disable(dev->vreg[i].hdl); + } + } +} + +static int ep_pcie_clk_init(struct ep_pcie_dev_t *dev) +{ + int i, rc = 0; + struct ep_pcie_clk_info_t *info; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + rc = regulator_enable(dev->gdsc); + + if (rc) { + EP_PCIE_ERR(dev, "PCIe V%d: fail to enable GDSC for %s\n", + dev->rev, dev->pdev->name); + return rc; + } + + if (dev->icc_path) { + rc = icc_set_bw(dev->icc_path, ICC_AVG_BW, ICC_PEAK_BW); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: fail to set bus bandwidth:%d\n", + dev->rev, rc); + return rc; + } + EP_PCIE_DBG(dev, + "PCIe V%d: set bus bandwidth\n", + dev->rev); + } + + for (i = 0; i < EP_PCIE_MAX_CLK; i++) { + info = &dev->clk[i]; + + if (!info->hdl) { + EP_PCIE_DBG(dev, + "PCIe V%d: handle of Clock %s is NULL\n", + dev->rev, info->name); + continue; + } + + if (info->freq) { + rc = clk_set_rate(info->hdl, info->freq); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't set rate for clk %s: %d\n", + dev->rev, info->name, rc); + break; + } + EP_PCIE_DBG(dev, + "PCIe V%d: set rate %d for clk %s\n", + dev->rev, info->freq, info->name); + } + + rc = clk_prepare_enable(info->hdl); + + if (rc) + EP_PCIE_ERR(dev, "PCIe V%d: failed to enable clk %s\n", + dev->rev, info->name); + else + EP_PCIE_DBG(dev, "PCIe V%d: enable clk %s\n", + dev->rev, info->name); + } + + if (rc) { + EP_PCIE_DBG(dev, + "PCIe V%d: disable clocks for error handling\n", + dev->rev); + while (i--) { + struct clk *hdl = dev->clk[i].hdl; + + if (hdl) + clk_disable_unprepare(hdl); + } + + regulator_disable(dev->gdsc); + } + + return rc; +} + +static void ep_pcie_clk_deinit(struct ep_pcie_dev_t *dev) +{ + int i; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = EP_PCIE_MAX_CLK - 1; i >= 0; i--) + if (dev->clk[i].hdl) + clk_disable_unprepare(dev->clk[i].hdl); + + if (dev->icc_path) { + icc_put(dev->icc_path); + dev->icc_path = 0; + EP_PCIE_DBG(dev, + "PCIe V%d: relinquish bus bandwidth\n", + dev->rev); + } + + if (!m2_enabled) + regulator_disable(dev->gdsc); +} + +static int ep_pcie_pipe_clk_init(struct ep_pcie_dev_t *dev) +{ + int i, rc = 0; + struct ep_pcie_clk_info_t *info; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = 0; i < EP_PCIE_MAX_PIPE_CLK; i++) { + info = &dev->pipeclk[i]; + + if (!info->hdl) { + EP_PCIE_ERR(dev, + "PCIe V%d: handle of Pipe Clock %s is NULL\n", + dev->rev, info->name); + rc = -EINVAL; + break; + } + + if (info->freq) { + rc = clk_set_rate(info->hdl, info->freq); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't set rate for clk %s: %d\n", + dev->rev, info->name, rc); + break; + } + EP_PCIE_DBG(dev, + "PCIe V%d: set rate for clk %s\n", + dev->rev, info->name); + } + + rc = clk_prepare_enable(info->hdl); + + if (rc) + EP_PCIE_ERR(dev, "PCIe V%d: failed to enable clk %s\n", + dev->rev, info->name); + else + EP_PCIE_DBG(dev, "PCIe V%d: enabled pipe clk %s\n", + dev->rev, info->name); + } + + if (rc) { + EP_PCIE_DBG(dev, + "PCIe V%d: disable pipe clocks for error handling\n", + dev->rev); + while (i--) + if (dev->pipeclk[i].hdl) + clk_disable_unprepare(dev->pipeclk[i].hdl); + } + + return rc; +} + +static void ep_pcie_pipe_clk_deinit(struct ep_pcie_dev_t *dev) +{ + int i; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + for (i = 0; i < EP_PCIE_MAX_PIPE_CLK; i++) + if (dev->pipeclk[i].hdl) + clk_disable_unprepare( + dev->pipeclk[i].hdl); +} + +static void ep_pcie_bar_init(struct ep_pcie_dev_t *dev) +{ + u32 properties = 0x4; + + EP_PCIE_DBG(dev, "PCIe V%d: BAR mask to program is 0x%x\n", + dev->rev, dev->mmio_res_size); + + /* Configure BAR mask via CS2 */ + ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_CS2_ENABLE, 0, BIT(0)); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0, dev->mmio_res_size); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x4, 0); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x8, dev->mmio_res_size); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0xc, 0); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x10, 0); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x14, 0); + ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_CS2_ENABLE, BIT(0), 0); + + /* Configure BAR properties via CS */ + ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0, BIT(0)); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0, properties); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x8, properties); + ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), 0); +} + +static void ep_pcie_config_mmio(struct ep_pcie_dev_t *dev) +{ + u32 mhi_status; + void __iomem *mhi_status_addr; + + EP_PCIE_DBG(dev, + "Initial version of MMIO is:0x%x\n", + readl_relaxed(dev->mmio + PCIE20_MHIVER)); + + if (dev->config_mmio_init) { + EP_PCIE_DBG(dev, + "PCIe V%d: MMIO already initialized, return\n", + dev->rev); + return; + } + + mhi_status_addr = PCIE_MHI_STATUS(dev->mmio); + mhi_status = readl_relaxed(mhi_status_addr); + if (mhi_status & BIT(2)) { + EP_PCIE_DBG(dev, + "MHISYS error is set:%d, proceed to MHI\n", + mhi_status); + return; + } + + ep_pcie_write_reg(dev->mmio, PCIE20_MHICFG, 0x02800880); + ep_pcie_write_reg(dev->mmio, PCIE20_BHI_EXECENV, 0x2); + ep_pcie_write_reg(dev->mmio, PCIE20_MHICTRL, 0x0); + ep_pcie_write_reg(dev->mmio, PCIE20_MHISTATUS, 0x0); + ep_pcie_write_reg(dev->mmio, PCIE20_MHIVER, 0x1000000); + ep_pcie_write_reg(dev->mmio, PCIE20_BHI_VERSION_LOWER, 0x2); + ep_pcie_write_reg(dev->mmio, PCIE20_BHI_VERSION_UPPER, 0x1); + + dev->config_mmio_init = true; +} + +static void ep_pcie_core_init(struct ep_pcie_dev_t *dev, bool configured) +{ + uint32_t val = 0; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + EP_PCIE_DBG(dev, + "PCIe V%d: WRITING TO BDF TO SID\n", + dev->rev); + /* PARF_BDF_TO_SID disable */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_BDF_TO_SID_CFG, + 0, BIT(0)); + + EP_PCIE_DBG(dev, + "PCIe V%d: FINISHED WRITING BDF TO SID\n", + dev->rev); + /* enable debug IRQ */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_DEBUG_INT_EN, + 0, BIT(3) | BIT(2) | BIT(1)); + /* Reconnect AXI master port */ + val = readl_relaxed(dev->parf + PCIE20_PARF_BUS_DISCONNECT_STATUS); + if (val & BIT(0)) { + EP_PCIE_DBG(dev, + "PCIe V%d: AXI Master port was disconnected, reconnecting...\n", + dev->rev); + ep_pcie_write_mask(dev->parf + PCIE20_PARF_BUS_DISCONNECT_CTRL, + 0, BIT(0)); + } + + /* Update offset to AXI address for Host initiated SOC reset */ + if (dev->mhi_soc_reset_en) { + EP_PCIE_DBG(dev, + "PCIe V%d: Updating SOC reset offset with val:0x%x\n", + dev->rev, dev->mhi_soc_reset_offset); + ep_pcie_write_reg(dev->parf, PCIE20_BUS_DISCONNECT_STATUS, + dev->mhi_soc_reset_offset); + val = readl_relaxed(dev->parf + + PCIE20_BUS_DISCONNECT_STATUS); + EP_PCIE_DBG(dev, + "PCIe V%d:SOC reset offset val:0x%x\n", dev->rev, val); + } + + if (!configured) { + /* Configure PCIe to endpoint mode */ + ep_pcie_write_reg(dev->parf, PCIE20_PARF_DEVICE_TYPE, 0x0); + + /* adjust DBI base address */ + if (dev->phy_rev < 6) { + if (dev->dbi_base_reg) + writel_relaxed(0x3FFFE000, + dev->parf + dev->dbi_base_reg); + else + writel_relaxed(0x3FFFE000, + dev->parf + PCIE20_PARF_DBI_BASE_ADDR); + } + + /* Configure PCIe core to support 1GB aperture */ + if (dev->slv_space_reg) + ep_pcie_write_reg(dev->parf, dev->slv_space_reg, + 0x40000000); + else + ep_pcie_write_reg(dev->parf, + PCIE20_PARF_SLV_ADDR_SPACE_SIZE, 0x40000000); + + /* Configure link speed */ + ep_pcie_write_mask(dev->dm_core + + PCIE20_LINK_CONTROL2_LINK_STATUS2, + 0xf, dev->link_speed); + + EP_PCIE_DBG2(dev, "PCIe V%d: Allow L1 after D3_COLD->D0\n", + dev->rev); + ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, BIT(5), 0); + } + + if (dev->active_config) { + struct resource *dbi = dev->res[EP_PCIE_RES_DM_CORE].resource; + u32 dbi_lo = dbi->start; + + ep_pcie_write_reg(dev->parf + PCIE20_PARF_SLV_ADDR_MSB_CTRL, + 0, BIT(0)); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_SLV_ADDR_SPACE_SIZE_HI, + 0x200); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_SLV_ADDR_SPACE_SIZE, + 0x0); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_DBI_BASE_ADDR_HI, + 0x100); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_DBI_BASE_ADDR, + dbi_lo); + + EP_PCIE_DBG(dev, + "PCIe V%d: DBI base:0x%x\n", dev->rev, + readl_relaxed(dev->parf + PCIE20_PARF_DBI_BASE_ADDR)); + + if (dev->phy_rev >= 6) { + struct resource *atu = + dev->res[EP_PCIE_RES_IATU].resource; + u32 atu_lo = atu->start; + + EP_PCIE_DBG(dev, + "PCIe V%d: configure MSB of ATU base for flipping and LSB as 0x%x\n", + dev->rev, atu_lo); + ep_pcie_write_reg(dev->parf, + PCIE20_PARF_ATU_BASE_ADDR_HI, 0x100); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_ATU_BASE_ADDR, + atu_lo); + EP_PCIE_DBG(dev, + "PCIe V%d: LSB of ATU base:0x%x\n", + dev->rev, readl_relaxed(dev->parf + + PCIE20_PARF_ATU_BASE_ADDR)); + if (dev->pcie_edma) { + struct resource *edma = + dev->res[EP_PCIE_RES_EDMA].resource; + u32 edma_lo = edma->start; + + ep_pcie_write_reg(dev->parf, + PCIE20_PARF_EDMA_BASE_ADDR_HI, 0x100); + EP_PCIE_DBG(dev, + "PCIe V%d: EDMA base HI :0x%x\n", + dev->rev, readl_relaxed(dev->parf + + PCIE20_PARF_EDMA_BASE_ADDR_HI)); + + ep_pcie_write_reg(dev->parf, + PCIE20_PARF_EDMA_BASE_ADDR, edma_lo); + EP_PCIE_DBG(dev, + "PCIe V%d: EDMA base:0x%x\n", dev->rev, + readl_relaxed(dev->parf + + PCIE20_PARF_EDMA_BASE_ADDR)); + } + } + } + + /* Read halts write */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_AXI_MSTR_RD_HALT_NO_WRITES, + 0, BIT(0)); + + /* Write after write halt */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT, + 0, BIT(31)); + + /* Q2A flush disable */ + writel_relaxed(0, dev->parf + PCIE20_PARF_Q2A_FLUSH); + + /* Disable the DBI Wakeup */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, BIT(11), 0); + + /* Disable the debouncers */ + ep_pcie_write_reg(dev->parf, PCIE20_PARF_DB_CTRL, 0x73); + + /* Disable core clock CGC */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, 0, BIT(6)); + + /* Set AUX power to be on */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, 0, BIT(4)); + + /* Request to exit from L1SS for MSI and LTR MSG */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_CFG_BITS, 0, BIT(1)); + + EP_PCIE_DBG(dev, + "Initial: CLASS_CODE_REVISION_ID:0x%x; HDR_TYPE:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_CLASS_CODE_REVISION_ID), + readl_relaxed(dev->dm_core + PCIE20_BIST_HDR_TYPE)); + + if (!configured) { + /* Enable CS for RO(CS) register writes */ + ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0, + BIT(0)); + + /* Set Vendor ID and Device ID */ + if (ep_pcie_dev.device_id != 0xFFFF) + ep_pcie_write_reg_field(dev->dm_core, + PCIE20_DEVICE_ID_VENDOR_ID, + PCIE20_MASK_DEVICE_ID, + ep_pcie_dev.device_id); + if (ep_pcie_dev.vendor_id != 0xFFFF) + ep_pcie_write_reg_field(dev->dm_core, + PCIE20_DEVICE_ID_VENDOR_ID, + PCIE20_MASK_VENDOR_ID, + ep_pcie_dev.vendor_id); + /* Set class code and revision ID */ + ep_pcie_write_reg(dev->dm_core, PCIE20_CLASS_CODE_REVISION_ID, + 0xff000000); + + /* Set header type */ + ep_pcie_write_reg(dev->dm_core, PCIE20_BIST_HDR_TYPE, 0x10); + + /* Set Subsystem ID and Subsystem Vendor ID */ + if (ep_pcie_dev.subsystem_id) + ep_pcie_write_reg(dev->dm_core, PCIE20_SUBSYSTEM, + ep_pcie_dev.subsystem_id); + + /* Set the PMC Register - to support PME in D0/D3hot/D3cold */ + ep_pcie_write_mask(dev->dm_core + PCIE20_CAP_ID_NXT_PTR, 0, + BIT(31)|BIT(30)|BIT(27)); + + /* Set the Endpoint L0s Acceptable Latency to 1us (max) */ + ep_pcie_write_reg_field(dev->dm_core, + PCIE20_DEVICE_CAPABILITIES, + PCIE20_MASK_EP_L0S_ACCPT_LATENCY, 0x7); + + /* Set the Endpoint L1 Acceptable Latency to 2 us (max) */ + ep_pcie_write_reg_field(dev->dm_core, + PCIE20_DEVICE_CAPABILITIES, + PCIE20_MASK_EP_L1_ACCPT_LATENCY, 0x7); + + /* Set the L0s Exit Latency to 2us-4us = 0x6 */ + ep_pcie_write_reg_field(dev->dm_core, PCIE20_LINK_CAPABILITIES, + PCIE20_MASK_L1_EXIT_LATENCY, 0x6); + + /* Set the L1 Exit Latency to be 32us-64 us = 0x6 */ + ep_pcie_write_reg_field(dev->dm_core, PCIE20_LINK_CAPABILITIES, + PCIE20_MASK_L0S_EXIT_LATENCY, 0x6); + + /* L1ss is supported */ + ep_pcie_write_mask(dev->dm_core + PCIE20_L1SUB_CAPABILITY, 0, + 0x1f); + + /* Enable Clock Power Management */ + ep_pcie_write_reg_field(dev->dm_core, PCIE20_LINK_CAPABILITIES, + PCIE20_MASK_CLOCK_POWER_MAN, 0x1); + + /* Disable CS for RO(CS) register writes */ + ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), + 0); + + /* Set FTS value to match the PHY setting */ + ep_pcie_write_reg_field(dev->dm_core, + PCIE20_ACK_F_ASPM_CTRL_REG, + PCIE20_MASK_ACK_N_FTS, 0x80); + + EP_PCIE_DBG(dev, + "After program: CLASS_CODE_REVISION_ID:0x%x; HDR_TYPE:0x%x; L1SUB_CAPABILITY:0x%x; PARF_SYS_CTRL:0x%x\n", + readl_relaxed(dev->dm_core + + PCIE20_CLASS_CODE_REVISION_ID), + readl_relaxed(dev->dm_core + PCIE20_BIST_HDR_TYPE), + readl_relaxed(dev->dm_core + PCIE20_L1SUB_CAPABILITY), + readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL)); + + /* Configure BARs */ + ep_pcie_bar_init(dev); + } + + /* Configure IRQ events */ + if (dev->aggregated_irq) { + ep_pcie_write_reg(dev->parf, PCIE20_PARF_INT_ALL_MASK, 0); + ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK, 0, + BIT(EP_PCIE_INT_EVT_LINK_DOWN) | + BIT(EP_PCIE_INT_EVT_BME) | + BIT(EP_PCIE_INT_EVT_PM_TURNOFF) | + BIT(EP_PCIE_INT_EVT_DSTATE_CHANGE) | + BIT(EP_PCIE_INT_EVT_LINK_UP)); + if (!dev->mhi_a7_irq) + ep_pcie_write_mask(dev->parf + + PCIE20_PARF_INT_ALL_MASK, 0, + BIT(EP_PCIE_INT_EVT_MHI_A7)); + if (dev->pcie_edma) + ep_pcie_write_mask(dev->parf + + PCIE20_PARF_INT_ALL_MASK, 0, + BIT(EP_PCIE_INT_EVT_EDMA)); + if (dev->m2_autonomous) + ep_pcie_write_mask(dev->parf + + PCIE20_PARF_INT_ALL_MASK, 0, + BIT(EP_PCIE_INT_EVT_L1SUB_TIMEOUT)); + + EP_PCIE_DBG(dev, "PCIe V%d: PCIE20_PARF_INT_ALL_MASK:0x%x\n", + dev->rev, + readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK)); + } + + if (dev->active_config) { + ep_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x14); + + /* Prevent L1ss wakeup after 100ms */ + ep_pcie_write_mask(dev->dm_core + PCIE20_GEN3_RELATED_OFF, + BIT(0), 0); + + /* Disable SRIS_MODE */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_SRIS_MODE, + BIT(0), 0); + } + + if (!configured) + ep_pcie_config_mmio(dev); +} + +static void ep_pcie_config_inbound_iatu(struct ep_pcie_dev_t *dev) +{ + struct resource *mmio = dev->res[EP_PCIE_RES_MMIO].resource; + u32 lower, limit, bar; + + lower = mmio->start; + limit = mmio->end; + bar = readl_relaxed(dev->dm_core + PCIE20_BAR0); + + EP_PCIE_DBG(dev, + "PCIe V%d: BAR0 is 0x%x; MMIO[0x%x-0x%x]\n", + dev->rev, bar, lower, limit); + + ep_pcie_write_reg(dev->parf, PCIE20_PARF_MHI_BASE_ADDR_LOWER, lower); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_MHI_BASE_ADDR_UPPER, 0x0); + + if (dev->phy_rev >= 6) { + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_I_CTRL1(0), 0x0); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_I_LTAR(0), lower); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_I_UTAR(0), 0x0); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_I_CTRL2(0), + 0xc0000000); + + EP_PCIE_DBG(dev, + "PCIe V%d: Inbound iATU configuration\n", dev->rev); + EP_PCIE_DBG(dev, "PCIE20_IATU_I_CTRL1(0):0x%x\n", + readl_relaxed(dev->iatu + PCIE20_IATU_I_CTRL1(0))); + EP_PCIE_DBG(dev, "PCIE20_IATU_I_LTAR(0):0x%x\n", + readl_relaxed(dev->iatu + PCIE20_IATU_I_LTAR(0))); + EP_PCIE_DBG(dev, "PCIE20_IATU_I_UTAR(0):0x%x\n", + readl_relaxed(dev->iatu + PCIE20_IATU_I_UTAR(0))); + EP_PCIE_DBG(dev, "PCIE20_IATU_I_CTRL2(0):0x%x\n", + readl_relaxed(dev->iatu + PCIE20_IATU_I_CTRL2(0))); + return; + } + + /* program inbound address translation using region 0 */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_VIEWPORT, 0x80000000); + /* set region to mem type */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_CTRL1, 0x0); + /* setup target address registers */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LTAR, lower); + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_UTAR, 0x0); + /* use BAR match mode for BAR0 and enable region 0 */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_CTRL2, 0xc0000000); + + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_VIEWPORT:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL1:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LTAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_UTAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL2:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2)); +} + +static void ep_pcie_config_outbound_iatu_entry(struct ep_pcie_dev_t *dev, + u32 region, u32 lower, u32 upper, + u32 limit, u32 tgt_lower, u32 tgt_upper) +{ + EP_PCIE_DBG(dev, + "PCIe V%d: region:%d; lower:0x%x; limit:0x%x; target_lower:0x%x; target_upper:0x%x\n", + dev->rev, region, lower, limit, tgt_lower, tgt_upper); + + if (dev->phy_rev >= 6) { + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_O_CTRL1(region), + 0x0); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_O_LBAR(region), + lower); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_O_UBAR(region), + upper); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_O_LAR(region), + limit); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_O_LTAR(region), + tgt_lower); + ep_pcie_write_reg(dev->iatu, PCIE20_IATU_O_UTAR(region), + tgt_upper); + /* Set DMA Bypass bit for eDMA */ + if (dev->pcie_edma) + ep_pcie_write_mask(dev->iatu + + PCIE20_IATU_O_CTRL2(region), 0, + BIT(31)|BIT(27)); + else + ep_pcie_write_mask(dev->iatu + + PCIE20_IATU_O_CTRL2(region), 0, + BIT(31)); + + EP_PCIE_DBG(dev, + "PCIe V%d: Outbound iATU configuration\n", dev->rev); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_CTRL1:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_CTRL1(region))); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_LBAR:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_LBAR(region))); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_UBAR:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_UBAR(region))); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_LAR:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_LAR(region))); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_LTAR:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_LTAR(region))); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_UTAR:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_UTAR(region))); + EP_PCIE_DBG(dev, "PCIE20_IATU_O_CTRL2:0x%x\n", + readl_relaxed(dev->iatu + + PCIE20_IATU_O_CTRL2(region))); + + return; + } + + /* program outbound address translation using an input region */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_VIEWPORT, region); + /* set region to mem type */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_CTRL1, 0x0); + /* setup source address registers */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LBAR, lower); + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_UBAR, upper); + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LAR, limit); + /* setup target address registers */ + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_LTAR, tgt_lower); + ep_pcie_write_reg(dev->dm_core, PCIE20_PLR_IATU_UTAR, tgt_upper); + /* use DMA bypass mode and enable the region */ + ep_pcie_write_mask(dev->dm_core + PCIE20_PLR_IATU_CTRL2, 0, + BIT(31) | BIT(27)); + + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_VIEWPORT:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL1:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LBAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LBAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_UBAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UBAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_LTAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_UTAR:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR)); + EP_PCIE_DBG(dev, "PCIE20_PLR_IATU_CTRL2:0x%x\n", + readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2)); +} + +static void ep_pcie_notify_event(struct ep_pcie_dev_t *dev, + enum ep_pcie_event event) +{ + if (dev->event_reg && dev->event_reg->callback && + (dev->event_reg->events & event)) { + struct ep_pcie_notify *notify = &dev->event_reg->notify; + + notify->event = event; + notify->user = dev->event_reg->user; + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: Callback client for event %d\n", + dev->rev, event); + dev->event_reg->callback(notify); + } else { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: Client does not register for event %d\n", + dev->rev, event); + } +} + +static int ep_pcie_get_resources(struct ep_pcie_dev_t *dev, + struct platform_device *pdev) +{ + int i, len, cnt, ret = 0, size = 0; + struct ep_pcie_vreg_info_t *vreg_info; + struct ep_pcie_gpio_info_t *gpio_info; + struct ep_pcie_clk_info_t *clk_info; + struct ep_pcie_reset_info_t *reset_info; + struct resource *res; + struct ep_pcie_res_info_t *res_info; + struct ep_pcie_irq_info_t *irq_info; + char prop_name[MAX_PROP_SIZE]; + const __be32 *prop; + u32 *clkfreq = NULL; + bool map; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + of_get_property(pdev->dev.of_node, "qcom,phy-init", &size); + if (size) { + dev->phy_init = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (dev->phy_init) { + dev->phy_init_len = + size / sizeof(*dev->phy_init); + EP_PCIE_DBG(dev, + "PCIe V%d: phy init length is 0x%x\n", + dev->rev, dev->phy_init_len); + + of_property_read_u32_array(pdev->dev.of_node, + "qcom,phy-init", + (unsigned int *)dev->phy_init, + size / sizeof(dev->phy_init->offset)); + } else { + EP_PCIE_ERR(dev, + "PCIe V%d: Could not allocate memory for phy init sequence\n", + dev->rev); + return -ENOMEM; + } + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: phy init sequence is not present in DT\n", + dev->rev, dev->phy_rev); + } + + cnt = of_property_count_strings((&pdev->dev)->of_node, + "clock-names"); + if (cnt > 0) { + size_t size = cnt * sizeof(*clkfreq); + + clkfreq = kzalloc(size, GFP_KERNEL); + if (!clkfreq) + return -ENOMEM; + ret = of_property_read_u32_array( + (&pdev->dev)->of_node, + "max-clock-frequency-hz", clkfreq, cnt); + if (ret) + EP_PCIE_DBG2(dev, + "PCIe V%d: cannot get max-clock-frequency-hz property from DT:%d\n", + dev->rev, ret); + } + + for (i = 0; i < EP_PCIE_MAX_VREG; i++) { + vreg_info = &dev->vreg[i]; + vreg_info->hdl = + devm_regulator_get(&pdev->dev, vreg_info->name); + + if (PTR_ERR(vreg_info->hdl) == -EPROBE_DEFER) { + EP_PCIE_DBG(dev, "EPROBE_DEFER for VReg:%s\n", + vreg_info->name); + ret = PTR_ERR(vreg_info->hdl); + goto out; + } + + if (IS_ERR(vreg_info->hdl)) { + if (vreg_info->required) { + EP_PCIE_ERR(dev, "Vreg %s doesn't exist\n", + vreg_info->name); + ret = PTR_ERR(vreg_info->hdl); + goto out; + } else { + EP_PCIE_DBG(dev, + "Optional Vreg %s doesn't exist\n", + vreg_info->name); + vreg_info->hdl = NULL; + } + } else { + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-voltage-level", vreg_info->name); + prop = of_get_property((&pdev->dev)->of_node, + prop_name, &len); + if (!prop || (len != (3 * sizeof(__be32)))) { + EP_PCIE_DBG(dev, "%s %s property\n", + prop ? "invalid format" : + "no", prop_name); + } else { + vreg_info->max_v = be32_to_cpup(&prop[0]); + vreg_info->min_v = be32_to_cpup(&prop[1]); + vreg_info->opt_mode = + be32_to_cpup(&prop[2]); + } + } + } + + dev->gdsc = devm_regulator_get(&pdev->dev, "gdsc-vdd"); + + if (IS_ERR(dev->gdsc)) { + EP_PCIE_ERR(dev, "PCIe V%d: Failed to get %s GDSC:%ld\n", + dev->rev, dev->pdev->name, PTR_ERR(dev->gdsc)); + if (PTR_ERR(dev->gdsc) == -EPROBE_DEFER) + EP_PCIE_DBG(dev, "PCIe V%d: EPROBE_DEFER for %s GDSC\n", + dev->rev, dev->pdev->name); + ret = PTR_ERR(dev->gdsc); + goto out; + } + + for (i = 0; i < EP_PCIE_MAX_GPIO; i++) { + gpio_info = &dev->gpio[i]; + ret = of_get_named_gpio((&pdev->dev)->of_node, + gpio_info->name, 0); + if (ret >= 0) { + gpio_info->num = ret; + ret = 0; + EP_PCIE_DBG(dev, "GPIO num for %s is %d\n", + gpio_info->name, gpio_info->num); + } else { + EP_PCIE_DBG(dev, + "GPIO %s is not supported in this configuration\n", + gpio_info->name); + ret = 0; + } + } + + for (i = 0; i < EP_PCIE_MAX_CLK; i++) { + clk_info = &dev->clk[i]; + + clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name); + + if (IS_ERR(clk_info->hdl)) { + if (clk_info->required) { + EP_PCIE_ERR(dev, + "Clock %s isn't available:%ld\n", + clk_info->name, PTR_ERR(clk_info->hdl)); + ret = PTR_ERR(clk_info->hdl); + goto out; + } else { + EP_PCIE_DBG(dev, "Ignoring Clock %s\n", + clk_info->name); + clk_info->hdl = NULL; + } + } else { + if (clkfreq != NULL) { + clk_info->freq = clkfreq[i + + EP_PCIE_MAX_PIPE_CLK]; + EP_PCIE_DBG(dev, "Freq of Clock %s is:%d\n", + clk_info->name, clk_info->freq); + } + } + } + + for (i = 0; i < EP_PCIE_MAX_PIPE_CLK; i++) { + clk_info = &dev->pipeclk[i]; + + clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name); + + if (IS_ERR(clk_info->hdl)) { + if (clk_info->required) { + EP_PCIE_ERR(dev, + "Clock %s isn't available:%ld\n", + clk_info->name, PTR_ERR(clk_info->hdl)); + ret = PTR_ERR(clk_info->hdl); + goto out; + } else { + EP_PCIE_DBG(dev, "Ignoring Clock %s\n", + clk_info->name); + clk_info->hdl = NULL; + } + } else { + if (clkfreq != NULL) { + clk_info->freq = clkfreq[i]; + EP_PCIE_DBG(dev, "Freq of Clock %s is:%d\n", + clk_info->name, clk_info->freq); + } + } + } + + for (i = 0; i < EP_PCIE_MAX_RESET; i++) { + reset_info = &dev->reset[i]; + + reset_info->hdl = devm_reset_control_get(&pdev->dev, + reset_info->name); + + if (IS_ERR(reset_info->hdl)) { + if (reset_info->required) { + EP_PCIE_ERR(dev, + "Reset %s isn't available:%ld\n", + reset_info->name, + PTR_ERR(reset_info->hdl)); + + ret = PTR_ERR(reset_info->hdl); + reset_info->hdl = NULL; + goto out; + } else { + EP_PCIE_DBG(dev, "Ignoring Reset %s\n", + reset_info->name); + reset_info->hdl = NULL; + } + } + } + + dev->icc_path = of_icc_get(&pdev->dev, "icc_path"); + if (!dev->icc_path) { + EP_PCIE_ERR(dev, + "PCIe V%d: Failed to register bus client for %s\n", + dev->rev, dev->pdev->name); + ret = -ENODEV; + goto out; + } + + for (i = 0; i < EP_PCIE_MAX_RES; i++) { + res_info = &dev->res[i]; + map = false; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + res_info->name); + + if (!res) { + EP_PCIE_ERR(dev, + "PCIe V%d: can't get resource for %s\n", + dev->rev, res_info->name); + if (!strcmp(res_info->name, "tcsr_pcie_perst_en")) { + if (!dev->tcsr_not_supported) { + ret = -ENOMEM; + goto out; + } + } + dev->mmio_res_size = res->end = res->start; + } else { + EP_PCIE_DBG(dev, "start addr for %s is %pa\n", + res_info->name, &res->start); + map = true; + } + + if (map) { + res_info->base = devm_ioremap(&pdev->dev, + res->start, resource_size(res)); + if (!res_info->base) { + EP_PCIE_ERR(dev, "PCIe V%d: can't remap %s\n", + dev->rev, res_info->name); + ret = -ENOMEM; + goto out; + } + res_info->resource = res; + } + } + + for (i = 0; i < EP_PCIE_MAX_IRQ; i++) { + irq_info = &dev->irq[i]; + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + irq_info->name); + + if (!res) { + EP_PCIE_DBG2(dev, "PCIe V%d: can't find IRQ # for %s\n", + dev->rev, irq_info->name); + } else { + irq_info->num = res->start; + EP_PCIE_DBG2(dev, "IRQ # for %s is %d\n", + irq_info->name, irq_info->num); + } + } + + dev->parf = dev->res[EP_PCIE_RES_PARF].base; + dev->phy = dev->res[EP_PCIE_RES_PHY].base; + dev->mmio = dev->res[EP_PCIE_RES_MMIO].base; + dev->msi = dev->res[EP_PCIE_RES_MSI].base; + dev->dm_core = dev->res[EP_PCIE_RES_DM_CORE].base; + dev->edma = dev->res[EP_PCIE_RES_EDMA].base; + dev->elbi = dev->res[EP_PCIE_RES_ELBI].base; + dev->iatu = dev->res[EP_PCIE_RES_IATU].base; + dev->tcsr_perst_en = dev->res[EP_PCIE_RES_TCSR_PERST].base; + +out: + kfree(clkfreq); + return ret; +} + +static void ep_pcie_release_resources(struct ep_pcie_dev_t *dev) +{ + dev->parf = NULL; + dev->elbi = NULL; + dev->dm_core = NULL; + dev->edma = NULL; + dev->phy = NULL; + dev->mmio = NULL; + dev->msi = NULL; + dev->iatu = NULL; + + if (dev->icc_path) { + icc_put(dev->icc_path); + dev->icc_path = 0; + } +} + +static void ep_pcie_enumeration_complete(struct ep_pcie_dev_t *dev) +{ + unsigned long irqsave_flags; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + if (dev->enumerated) { + EP_PCIE_DBG(dev, "PCIe V%d: Enumeration already done\n", + dev->rev); + goto done; + } + dev->enumerated = true; + dev->link_status = EP_PCIE_LINK_ENABLED; + + if (dev->gpio[EP_PCIE_GPIO_MDM2AP].num) { + /* assert MDM2AP Status GPIO */ + EP_PCIE_DBG2(dev, "PCIe V%d: assert MDM2AP Status\n", + dev->rev); + EP_PCIE_DBG(dev, + "PCIe V%d: MDM2APStatus GPIO initial:%d\n", + dev->rev, + gpio_get_value( + dev->gpio[EP_PCIE_GPIO_MDM2AP].num)); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_MDM2AP].num, + dev->gpio[EP_PCIE_GPIO_MDM2AP].on); + EP_PCIE_DBG(dev, + "PCIe V%d: MDM2APStatus GPIO after assertion:%d\n", + dev->rev, + gpio_get_value( + dev->gpio[EP_PCIE_GPIO_MDM2AP].num)); + } + + hw_drv.device_id = readl_relaxed(dev->dm_core); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: register driver for device 0x%x\n", + ep_pcie_dev.rev, hw_drv.device_id); + ep_pcie_register_drv(&hw_drv); + if (!dev->no_notify) + ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP); + else + EP_PCIE_DBG(dev, + "PCIe V%d: do not notify client about linkup\n", + dev->rev); + +done: + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); +} + +static bool ep_pcie_core_get_clkreq_status(void) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe get clkreq status\n", dev->rev); + + return ((readl_relaxed(dev->parf + + PCIE20_PARF_CLKREQ_OVERRIDE) & BIT(5)) ? false : true); +} + +static int ep_pcie_core_clkreq_override(bool config) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe clockreq override config:%d\n", + dev->rev, config); + + if (config) { + ep_pcie_write_reg_field(dev->parf, PCIE20_PARF_CLKREQ_OVERRIDE, + PCIE20_PARF_CLKREQ_IN_OVERRIDE_VAL, BIT(3)); + ep_pcie_write_reg_field(dev->parf, PCIE20_PARF_CLKREQ_OVERRIDE, + PCIE20_PARF_CLKREQ_IN_OVERRIDE_ENABLE, BIT(1)); + } else { + ep_pcie_write_reg_field(dev->parf, PCIE20_PARF_CLKREQ_OVERRIDE, + PCIE20_PARF_CLKREQ_IN_OVERRIDE_ENABLE, 0); + ep_pcie_write_reg_field(dev->parf, PCIE20_PARF_CLKREQ_OVERRIDE, + PCIE20_PARF_CLKREQ_IN_OVERRIDE_VAL, 0); + } + + return 0; +} + +static int ep_pcie_core_config_inact_timer(struct ep_pcie_inactivity *param) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe config inact timer\n", dev->rev); + + if (!param->enable) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: timer value being disabled:0x%x\n", + ep_pcie_dev.rev, param->enable); + ep_pcie_write_reg_field(dev->parf, + PCIE20_PARF_DEBUG_INT_EN, + PCIE20_PARF_DEBUG_INT_EN_L1SUB_TIMEOUT_BIT_MASK, 0); + return 0; + } + + if (param->timer_us & BIT(31)) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: timer value is a 31 bit value:0x%x\n", + ep_pcie_dev.rev, param->timer_us); + return -EINVAL; + } + + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: timer value being programmed:0x%x\n", + ep_pcie_dev.rev, param->timer_us); + + ep_pcie_write_reg_field(dev->parf, + PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER, + PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER_RESET_MASK, 0x1); + + usleep_range(PCIE_L1SUB_AHB_TIMEOUT_MIN, PCIE_L1SUB_AHB_TIMEOUT_MAX); + ep_pcie_write_reg(dev->parf, + PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER, + param->timer_us); + usleep_range(PCIE_L1SUB_AHB_TIMEOUT_MIN, PCIE_L1SUB_AHB_TIMEOUT_MAX); + + ep_pcie_write_reg_field(dev->parf, + PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER, + PCIE20_PARF_L1SUB_AHB_CLK_MAX_TIMER_RESET_MASK, 0x0); + + /* Enable L1SUB timeout bit to enable corresponding aggregated irq */ + ep_pcie_write_reg_field(dev->parf, + PCIE20_PARF_DEBUG_INT_EN, + PCIE20_PARF_DEBUG_INT_EN_L1SUB_TIMEOUT_BIT_MASK, + BIT(0)); + + return 0; +} + + +static int ep_pcie_core_config_l1ss_sleep_mode(bool config) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, + "PCIe V%d:PCIe l1ss sleep mode:%d\n", dev->rev, config); + + if (config) + ep_pcie_write_reg_field(dev->parf, + PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_CONFIG, + BIT(0), 1); + else + ep_pcie_write_reg_field(dev->parf, + PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_CONFIG, + BIT(0), 0); + + return 0; +} + +static int ep_pcie_core_l1ss_sleep_config_disable(void) +{ + int rc = 0, mhi_fwd_status = 0, mhi_fwd_status_cnt = 0; + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, + "PCIe V%d:l1ss sleep disable config\n", + dev->rev); + + EP_PCIE_DBG(dev, "PCIe V%d:Ungate CLKREQ#\n", dev->rev); + + /* Undo CLKREQ# override */ + rc = ep_pcie_core_clkreq_override(false); + if (rc < 0) { + EP_PCIE_ERR(dev, "PCIe V%d:CLKREQ# override config failed\n", + dev->rev); + return rc; + } + + EP_PCIE_DBG(dev, "PCIe V%d:Disable L1ss\n", dev->rev); + + /* Disable L1ss sleep mode */ + rc = ep_pcie_core_config_l1ss_sleep_mode(false); + if (rc < 0) { + EP_PCIE_ERR(dev, "PCIe V%d:L1ss sleep deconfig failed\n", + dev->rev); + return rc; + } + + /* Check MHI_FWD status */ + while (!mhi_fwd_status && + mhi_fwd_status_cnt <= PCIE_MHI_FWD_COUNT) { + mhi_fwd_status = (readl_relaxed(dev->parf + + PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS) & + PCIE20_PARF_L1SS_SLEEP_MHI_FWD_DISABLE); + mhi_fwd_status_cnt++; + usleep_range(PCIE_MHI_FWD_STATUS_MIN, + PCIE_MHI_FWD_STATUS_MAX); + } + + if (mhi_fwd_status >= PCIE_MHI_FWD_COUNT) { + EP_PCIE_ERR(dev, "PCIe V%d:MHI FWD status not set\n", dev->rev); + return -EINVAL; + } + + return 0; +} + +int ep_pcie_core_l1ss_sleep_config_enable(void) +{ + int rc, ret, mhi_fwd_status = 1, mhi_fwd_status_cnt = 0; + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, + "PCIe V%d: l1ss sleep enable config\n", dev->rev); + + enable_irq(clkreq_irq); + + /* Enable CLKREQ# override */ + rc = ep_pcie_core_clkreq_override(true); + if (rc < 0) { + EP_PCIE_ERR(dev, "PCIe V%d:CLKREQ# override config failed\n", + dev->rev); + return rc; + } + + if (ep_pcie_core_get_clkreq_status()) { + EP_PCIE_DBG(dev, "PCIe V%d:CLKREQ status is set\n", dev->rev); + rc = -EINVAL; + goto disable_clkreq; + } + + /* Enter L1ss sleep mode */ + rc = ep_pcie_core_config_l1ss_sleep_mode(true); + if (rc < 0) { + EP_PCIE_DBG(dev, "PCIe V%d:L1ss sleep config failed\n", + dev->rev); + goto disable_clkreq; + } + + /* Check MHI_FWD status */ + while (mhi_fwd_status && + mhi_fwd_status_cnt <= PCIE_MHI_FWD_COUNT) { + mhi_fwd_status = (readl_relaxed(dev->parf + + PCIE20_PARF_L1SS_SLEEP_MODE_HANDLER_STATUS) & + PCIE20_PARF_L1SS_SLEEP_MHI_FWD_ENABLE); + mhi_fwd_status_cnt++; + usleep_range(PCIE_MHI_FWD_STATUS_MIN, + PCIE_MHI_FWD_STATUS_MAX); + } + + if (mhi_fwd_status >= PCIE_MHI_FWD_COUNT) { + EP_PCIE_DBG(dev, "PCIe V%d:MHI FWD status not set\n", dev->rev); + rc = -EINVAL; + goto disable_l1ss_sleep_mode; + } + + m2_enabled = true; + + return 0; + +disable_l1ss_sleep_mode: + ret = ep_pcie_core_config_l1ss_sleep_mode(false); + if (ret) + EP_PCIE_DBG(dev, "PCIe V%d:Disable L1ss sleep mode with %d\n", + dev->rev, ret); +disable_clkreq: + ret = ep_pcie_core_clkreq_override(false); + if (ret) + EP_PCIE_DBG(dev, "PCIe V%d:CLKREQ# override config failed%d\n", + dev->rev, ret); + + return rc; +} +EXPORT_SYMBOL(ep_pcie_core_l1ss_sleep_config_enable); + +int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) +{ + int ret = 0; + u32 val = 0; + u32 retries = 0; + u32 bme = 0; + bool ltssm_en = false; + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, "PCIe V%d: options input are 0x%x\n", dev->rev, opt); + + mutex_lock(&dev->setup_mtx); + + if (dev->link_status == EP_PCIE_LINK_ENABLED) { + EP_PCIE_ERR(dev, + "PCIe V%d: link is already enabled\n", + dev->rev); + goto out; + } + + if (dev->link_status == EP_PCIE_LINK_UP) + EP_PCIE_DBG(dev, + "PCIe V%d: link is already up, let's proceed with the voting for the resources\n", + dev->rev); + + if (dev->power_on && (opt & EP_PCIE_OPT_POWER_ON)) { + EP_PCIE_ERR(dev, + "PCIe V%d: request to turn on the power when link is already powered on\n", + dev->rev); + goto out; + } + + if (opt & EP_PCIE_OPT_POWER_ON) { + /* enable power */ + ret = ep_pcie_vreg_init(dev); + if (ret) { + EP_PCIE_ERR(dev, "PCIe V%d: failed to enable Vreg\n", + dev->rev); + goto out; + } + + /* enable clocks */ + ret = ep_pcie_clk_init(dev); + if (ret) { + EP_PCIE_ERR(dev, "PCIe V%d: failed to enable clocks\n", + dev->rev); + goto clk_fail; + } + + dev->power_on = true; + + if (!dev->tcsr_not_supported) { + EP_PCIE_DBG(dev, + "TCSR PERST_EN value before configure:0x%x\n", + readl_relaxed(dev->tcsr_perst_en + 0x258)); + + /* + * Delatch PERST_EN with TCSR to avoid device reset + * during host reboot case. + */ + writel_relaxed(0, dev->tcsr_perst_en + 0x258); + + EP_PCIE_DBG(dev, + "TCSR PERST_EN value after configure:0x%x\n", + readl_relaxed(dev->tcsr_perst_en + 0x258)); + + /* + * Delatch PERST_SEPARATION_ENABLE with TCSR to avoid + * device reset during host reboot and hibernate case. + */ + writel_relaxed(0, dev->tcsr_perst_en + + TCSR_PERST_SEPARATION_ENABLE); + } + + /* check link status during initial bootup */ + if (!dev->enumerated) { + val = readl_relaxed(dev->parf + PCIE20_PARF_PM_STTS); + val = val & PARF_XMLH_LINK_UP; + EP_PCIE_DBG(dev, "PCIe V%d: Link status is 0x%x.\n", + dev->rev, val); + if (val) { + EP_PCIE_INFO(dev, + "PCIe V%d: link initialized by bootloader for LE PCIe endpoint; skip link training in HLOS.\n", + dev->rev); + /* + * Read and save the subsystem id set in PBL + * (needed for restore during D3->D0) + */ + ep_pcie_dev.subsystem_id = + readl_relaxed(dev->dm_core + + PCIE20_SUBSYSTEM); + /* + * Skip mhi mmio config for host reboot case + * with bios-locking enabled. + */ + dev->config_mmio_init = true; + ep_pcie_core_init(dev, true); + dev->link_status = EP_PCIE_LINK_UP; + dev->l23_ready = false; + + /* enable pipe clock for early link init case*/ + ret = ep_pcie_pipe_clk_init(dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: failed to enable pipe clock\n", + dev->rev); + goto pipe_clk_fail; + } + goto checkbme; + } else { + ltssm_en = readl_relaxed(dev->parf + + PCIE20_PARF_LTSSM) & BIT(8); + + if (ltssm_en) { + EP_PCIE_ERR(dev, + "PCIe V%d: link is not up when LTSSM has already enabled by bootloader.\n", + dev->rev); + ret = EP_PCIE_ERROR; + goto link_fail; + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: Proceed with regular link training.\n", + dev->rev); + } + } + } + + ret = ep_pcie_reset_init(dev); + if (ret) + goto link_fail; + } + + if (!(opt & EP_PCIE_OPT_ENUM)) + goto out; + + if (!dev->tcsr_not_supported) { + EP_PCIE_DBG(dev, + "TCSR PERST_EN value before configure:0x%x\n", + readl_relaxed(dev->tcsr_perst_en + 0x258)); + + /* + * Delatch PERST_EN with TCSR to avoid device reset + * during host reboot case. + */ + writel_relaxed(0, dev->tcsr_perst_en + 0x258); + + EP_PCIE_DBG(dev, + "TCSR PERST_EN value after configure:0x%x\n", + readl_relaxed(dev->tcsr_perst_en)); + } + + if (opt & EP_PCIE_OPT_AST_WAKE) { + /* assert PCIe WAKE# */ + EP_PCIE_INFO(dev, "PCIe V%d: assert PCIe WAKE#\n", + dev->rev); + EP_PCIE_DBG(dev, "PCIe V%d: WAKE GPIO initial:%d\n", + dev->rev, + gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); + EP_PCIE_DBG(dev, + "PCIe V%d: WAKE GPIO after deassertion:%d\n", + dev->rev, + gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + dev->gpio[EP_PCIE_GPIO_WAKE].on); + EP_PCIE_DBG(dev, + "PCIe V%d: WAKE GPIO after assertion:%d\n", + dev->rev, + gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); + } + + /* wait for host side to deassert PERST */ + retries = 0; + do { + if (gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num) == 1) + break; + retries++; + usleep_range(PERST_TIMEOUT_US_MIN, PERST_TIMEOUT_US_MAX); + } while (retries < PERST_CHECK_MAX_COUNT); + + EP_PCIE_DBG(dev, "PCIe V%d: number of PERST retries:%d\n", + dev->rev, retries); + + if (retries == PERST_CHECK_MAX_COUNT) { + EP_PCIE_ERR(dev, + "PCIe V%d: PERST is not de-asserted by host\n", + dev->rev); + ret = EP_PCIE_ERROR; + goto link_fail; + } else { + atomic_set(&dev->perst_deast, 1); + if (opt & EP_PCIE_OPT_AST_WAKE) { + /* deassert PCIe WAKE# */ + EP_PCIE_DBG(dev, + "PCIe V%d: deassert PCIe WAKE# after PERST# is deasserted\n", + dev->rev); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); + } + } + + /* init PCIe PHY */ + ep_pcie_phy_init(dev); + + /* enable pipe clock */ + ret = ep_pcie_pipe_clk_init(dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: failed to enable pipe clock\n", + dev->rev); + goto pipe_clk_fail; + } + + EP_PCIE_DBG(dev, "PCIe V%d: waiting for phy ready...\n", dev->rev); + retries = 0; + do { + if (ep_pcie_phy_is_ready(dev)) + break; + retries++; + if (retries % 100 == 0) + EP_PCIE_DBG(dev, + "PCIe V%d: current number of PHY retries:%d\n", + dev->rev, retries); + usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN, + REFCLK_STABILIZATION_DELAY_US_MAX); + } while (retries < PHY_READY_TIMEOUT_COUNT); + + EP_PCIE_DBG(dev, "PCIe V%d: number of PHY retries:%d\n", + dev->rev, retries); + + if (retries == PHY_READY_TIMEOUT_COUNT) { + EP_PCIE_ERR(dev, "PCIe V%d: PCIe PHY failed to come up\n", + dev->rev); + ret = EP_PCIE_ERROR; + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_PHY), false); + goto link_fail; + } else { + EP_PCIE_INFO(dev, "PCIe V%d: PCIe PHY is ready\n", dev->rev); + } + + ep_pcie_core_init(dev, false); + ep_pcie_config_inbound_iatu(dev); + + /* enable link training */ + if (dev->phy_rev >= 3) + ep_pcie_write_mask(dev->parf + PCIE20_PARF_LTSSM, 0, BIT(8)); + else + ep_pcie_write_mask(dev->elbi + PCIE20_ELBI_SYS_CTRL, 0, BIT(0)); + + EP_PCIE_DBG(dev, "PCIe V%d: check if link is up\n", dev->rev); + + /* Wait for up to 100ms for the link to come up */ + retries = 0; + do { + usleep_range(LINK_UP_TIMEOUT_US_MIN, LINK_UP_TIMEOUT_US_MAX); + val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); + retries++; + if (retries % 100 == 0) + EP_PCIE_DBG(dev, "PCIe V%d: LTSSM_STATE:0x%x\n", + dev->rev, (val >> 0xC) & 0x3f); + } while ((!(val & XMLH_LINK_UP) || + !ep_pcie_confirm_linkup(dev, false)) + && (retries < LINK_UP_CHECK_MAX_COUNT)); + + if (retries == LINK_UP_CHECK_MAX_COUNT) { + EP_PCIE_ERR(dev, "PCIe V%d: link initialization failed\n", + dev->rev); + ret = EP_PCIE_ERROR; + goto link_fail; + } else { + dev->link_status = EP_PCIE_LINK_UP; + dev->l23_ready = false; + EP_PCIE_DBG(dev, + "PCIe V%d: link is up after %d checkings (%d ms)\n", + dev->rev, retries, + LINK_UP_TIMEOUT_US_MIN * retries / 1000); + EP_PCIE_INFO(dev, + "PCIe V%d: link initialized for LE PCIe endpoint\n", + dev->rev); + } + +checkbme: + /* + * De-assert WAKE# GPIO following link until L2/3 and WAKE# + * is triggered to send data from device to host at which point + * it will assert WAKE#. + */ + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); + + if (dev->active_config) + ep_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x14); + + if (!(opt & EP_PCIE_OPT_ENUM_ASYNC)) { + /* Wait for up to 1000ms for BME to be set */ + retries = 0; + + bme = readl_relaxed(dev->dm_core + + PCIE20_COMMAND_STATUS) & BIT(2); + while (!bme && (retries < BME_CHECK_MAX_COUNT)) { + retries++; + usleep_range(BME_TIMEOUT_US_MIN, BME_TIMEOUT_US_MAX); + bme = readl_relaxed(dev->dm_core + + PCIE20_COMMAND_STATUS) & BIT(2); + } + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: EP_PCIE_OPT_ENUM_ASYNC is true\n", + dev->rev); + bme = readl_relaxed(dev->dm_core + + PCIE20_COMMAND_STATUS) & BIT(2); + } + + if (bme) { + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe link is up and BME is enabled after %d checkings (%d ms)\n", + dev->rev, retries, + BME_TIMEOUT_US_MIN * retries / 1000); + ep_pcie_enumeration_complete(dev); + /* expose BAR to user space to identify modem */ + ep_pcie_bar0_address = + readl_relaxed(dev->dm_core + PCIE20_BAR0); + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe link is up but BME is disabled; current SW link status:%d\n", + dev->rev, dev->link_status); + dev->link_status = EP_PCIE_LINK_UP; + } + + dev->suspending = false; + goto out; + +link_fail: + dev->power_on = false; + if (!ep_pcie_debug_keep_resource) + ep_pcie_pipe_clk_deinit(dev); +pipe_clk_fail: + if (!ep_pcie_debug_keep_resource) + ep_pcie_clk_deinit(dev); +clk_fail: + if (!ep_pcie_debug_keep_resource) + ep_pcie_vreg_deinit(dev); + else + ret = 0; +out: + mutex_unlock(&dev->setup_mtx); + + return ret; +} + +int ep_pcie_core_disable_endpoint(void) +{ + u32 val = 0; + unsigned long irqsave_flags; + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + mutex_lock(&dev->setup_mtx); + if (atomic_read(&dev->perst_deast)) { + EP_PCIE_DBG(dev, + "PCIe V%d: PERST is de-asserted, exiting disable\n", + dev->rev); + goto out; + } + + if (!dev->power_on) { + EP_PCIE_DBG(dev, + "PCIe V%d: the link is already power down\n", + dev->rev); + goto out; + } + + if (!m2_enabled) { + dev->link_status = EP_PCIE_LINK_DISABLED; + dev->power_on = false; + + EP_PCIE_DBG(dev, "PCIe V%d: shut down the link\n", + dev->rev); + } + + val = readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS); + EP_PCIE_DBG(dev, "PCIe V%d: LTSSM_STATE during disable:0x%x\n", + dev->rev, (val >> 0xC) & 0x3f); + ep_pcie_pipe_clk_deinit(dev); + ep_pcie_clk_deinit(dev); + ep_pcie_vreg_deinit(dev); + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + if (atomic_read(&dev->ep_pcie_dev_wake) && + !atomic_read(&dev->perst_deast)) { + EP_PCIE_DBG(dev, "PCIe V%d: Released wakelock\n", dev->rev); + atomic_set(&dev->ep_pcie_dev_wake, 0); + pm_relax(&dev->pdev->dev); + } else { + EP_PCIE_DBG(dev, "PCIe V%d: Bail, Perst-assert:%d wake:%d\n", + dev->rev, atomic_read(&dev->perst_deast), + atomic_read(&dev->ep_pcie_dev_wake)); + } + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); +out: + mutex_unlock(&dev->setup_mtx); + return 0; +} + +int ep_pcie_core_mask_irq_event(enum ep_pcie_irq_event event, + bool enable) +{ + int rc = 0; + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + unsigned long irqsave_flags; + u32 mask = 0; + + EP_PCIE_DUMP(dev, + "PCIe V%d: Client askes to %s IRQ event 0x%x\n", + dev->rev, + enable ? "enable" : "disable", + event); + + spin_lock_irqsave(&dev->ext_lock, irqsave_flags); + + if (dev->aggregated_irq) { + mask = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK); + EP_PCIE_DUMP(dev, + "PCIe V%d: current PCIE20_PARF_INT_ALL_MASK:0x%x\n", + dev->rev, mask); + if (enable) + ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK, + 0, BIT(event)); + else + ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK, + BIT(event), 0); + EP_PCIE_DUMP(dev, + "PCIe V%d: new PCIE20_PARF_INT_ALL_MASK:0x%x\n", + dev->rev, + readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK)); + } else { + EP_PCIE_ERR(dev, + "PCIe V%d: Client askes to %s IRQ event 0x%x when aggregated IRQ is not supported\n", + dev->rev, + enable ? "enable" : "disable", + event); + rc = EP_PCIE_ERROR; + } + + spin_unlock_irqrestore(&dev->ext_lock, irqsave_flags); + return rc; +} + +static irqreturn_t ep_pcie_handle_bme_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + unsigned long irqsave_flags; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + dev->bme_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld BME IRQ\n", dev->rev, dev->bme_counter); + + if (readl_relaxed(dev->dm_core + PCIE20_COMMAND_STATUS) & BIT(2)) { + /* BME has been enabled */ + if (!dev->enumerated) { + EP_PCIE_DBG(dev, + "PCIe V%d:BME is set. Enumeration is complete\n", + dev->rev); + schedule_work(&dev->handle_bme_work); + } else { + EP_PCIE_DBG(dev, + "PCIe V%d:BME is set again after the enumeration has completed; callback client for link ready\n", + dev->rev); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKUP); + } + } else { + EP_PCIE_DBG(dev, + "PCIe V%d:BME is still disabled\n", dev->rev); + } + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + return IRQ_HANDLED; +} + +static irqreturn_t ep_pcie_handle_linkdown_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + unsigned long irqsave_flags; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + dev->linkdown_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld linkdown IRQ\n", + dev->rev, dev->linkdown_counter); + + if (!dev->enumerated || dev->link_status == EP_PCIE_LINK_DISABLED) { + EP_PCIE_DBG(dev, + "PCIe V%d:Linkdown IRQ happened when the link is disabled\n", + dev->rev); + } else if (dev->suspending) { + EP_PCIE_DBG(dev, + "PCIe V%d:Linkdown IRQ happened when the link is suspending\n", + dev->rev); + } else { + dev->link_status = EP_PCIE_LINK_DISABLED; + EP_PCIE_ERR(dev, "PCIe V%d:PCIe link is down for %ld times\n", + dev->rev, dev->linkdown_counter); + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_PHY) | + BIT(EP_PCIE_RES_PARF), true); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_LINKDOWN); + } + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + + return IRQ_HANDLED; +} + +static irqreturn_t ep_pcie_handle_linkup_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + unsigned long irqsave_flags; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + dev->linkup_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld linkup IRQ\n", + dev->rev, dev->linkup_counter); + + dev->link_status = EP_PCIE_LINK_UP; + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + + return IRQ_HANDLED; +} + +static irqreturn_t ep_pcie_handle_pm_turnoff_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + unsigned long irqsave_flags; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + dev->pm_to_counter++; + EP_PCIE_DBG2(dev, + "PCIe V%d: No. %ld PM_TURNOFF is received\n", + dev->rev, dev->pm_to_counter); + EP_PCIE_DBG2(dev, "PCIe V%d: Put the link into L23\n", dev->rev); + ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(2)); + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + + return IRQ_HANDLED; +} + +static irqreturn_t ep_pcie_handle_dstate_change_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + unsigned long irqsave_flags; + u32 dstate; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + dstate = readl_relaxed(dev->dm_core + + PCIE20_CON_STATUS) & 0x3; + + if (dev->dump_conf) + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_DM_CORE), false); + + if (dstate == 3) { + dev->l23_ready = true; + dev->d3_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld change to D3 state\n", + dev->rev, dev->d3_counter); + ep_pcie_write_mask(dev->parf + PCIE20_PARF_PM_CTRL, 0, BIT(1)); + + if (dev->enumerated) + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT); + else + EP_PCIE_DBG(dev, + "PCIe V%d: do not notify client about this D3 hot event since enumeration by HLOS is not done yet\n", + dev->rev); + } else if (dstate == 0) { + dev->l23_ready = false; + dev->d0_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld change to D0 state\n", + dev->rev, dev->d0_counter); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D0); + } else { + EP_PCIE_ERR(dev, + "PCIe V%d:invalid D state change to 0x%x\n", + dev->rev, dstate); + } + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + + return IRQ_HANDLED; +} + +static int ep_pcie_enumeration(struct ep_pcie_dev_t *dev) +{ + int ret = 0; + + if (!dev) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: the input handler is NULL\n", + ep_pcie_dev.rev); + return EP_PCIE_ERROR; + } + + EP_PCIE_DBG(dev, + "PCIe V%d: start PCIe link enumeration per host side\n", + dev->rev); + + ret = ep_pcie_core_enable_endpoint(EP_PCIE_OPT_ALL); + + if (ret) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: PCIe link enumeration failed\n", + ep_pcie_dev.rev); + } else { + if (dev->link_status == EP_PCIE_LINK_ENABLED) { + EP_PCIE_INFO(&ep_pcie_dev, + "PCIe V%d: PCIe link enumeration is successful with host side\n", + ep_pcie_dev.rev); + } else if (dev->link_status == EP_PCIE_LINK_UP) { + EP_PCIE_INFO(&ep_pcie_dev, + "PCIe V%d: PCIe link training is successful with host side. Waiting for enumeration to complete\n", + ep_pcie_dev.rev); + } else { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: PCIe link is in the unexpected status: %d\n", + ep_pcie_dev.rev, dev->link_status); + } + } + + return ret; +} + +static void handle_perst_func(struct work_struct *work) +{ + struct ep_pcie_dev_t *dev = container_of(work, struct ep_pcie_dev_t, + handle_perst_work); + + EP_PCIE_DBG(dev, + "PCIe V%d: Start enumeration due to PERST deassertion\n", + dev->rev); + + ep_pcie_enumeration(dev); +} + +static void handle_d3cold_func(struct work_struct *work) +{ + struct ep_pcie_dev_t *dev = container_of(work, struct ep_pcie_dev_t, + handle_d3cold_work); + unsigned long irqsave_flags; + + EP_PCIE_DBG(dev, + "PCIe V%d: shutdown PCIe link due to PERST assertion before BME is set\n", + dev->rev); + ep_pcie_core_disable_endpoint(); + dev->no_notify = false; + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + if (atomic_read(&dev->ep_pcie_dev_wake) && + !atomic_read(&dev->perst_deast)) { + atomic_set(&dev->ep_pcie_dev_wake, 0); + pm_relax(&dev->pdev->dev); + EP_PCIE_DBG(dev, "PCIe V%d: Released wakelock\n", dev->rev); + } else { + EP_PCIE_DBG(dev, "PCIe V%d: Bail, Perst-assert:%d wake:%d\n", + dev->rev, atomic_read(&dev->perst_deast), + atomic_read(&dev->ep_pcie_dev_wake)); + } + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); +} + +static void handle_bme_func(struct work_struct *work) +{ + struct ep_pcie_dev_t *dev = container_of(work, + struct ep_pcie_dev_t, handle_bme_work); + + ep_pcie_enumeration_complete(dev); +} + +static irqreturn_t ep_pcie_handle_perst_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + unsigned long irqsave_flags; + u32 perst; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + perst = gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num); + + if (!dev->enumerated) { + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe is not enumerated yet; PERST is %sasserted\n", + dev->rev, perst ? "de" : ""); + if (perst) { + /* start work for link enumeration with the host side */ + schedule_work(&dev->handle_perst_work); + } else { + dev->no_notify = true; + /* shutdown the link if the link is already on */ + schedule_work(&dev->handle_d3cold_work); + } + + goto out; + } + + if (perst) { + atomic_set(&dev->perst_deast, 1); + dev->perst_deast_counter++; + /* + * Hold a wakelock to avoid missing BME and other + * interrupts if apps goes into suspend before BME is set. + */ + if (!atomic_read(&dev->ep_pcie_dev_wake)) { + pm_stay_awake(&dev->pdev->dev); + atomic_set(&dev->ep_pcie_dev_wake, 1); + EP_PCIE_DBG(dev, "PCIe V%d: Acquired wakelock\n", + dev->rev); + } + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld PERST deassertion\n", + dev->rev, dev->perst_deast_counter); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_RST_DEAST); + } else { + atomic_set(&dev->perst_deast, 0); + dev->perst_ast_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld PERST assertion\n", + dev->rev, dev->perst_ast_counter); + + if (dev->client_ready) { + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_COLD); + } else { + dev->no_notify = true; + EP_PCIE_DBG(dev, + "PCIe V%d: Client driver is not ready when this PERST assertion happens; shutdown link now\n", + dev->rev); + schedule_work(&dev->handle_d3cold_work); + } + } + +out: + /* Set trigger type based on the next expected value of perst gpio */ + irq_set_irq_type(gpio_to_irq(dev->gpio[EP_PCIE_GPIO_PERST].num), + (perst ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH)); + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + + return IRQ_HANDLED; +} + +static irqreturn_t ep_pcie_handle_clkreq_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + int ret; + + EP_PCIE_DBG(dev, "PCIe V%d: received clkreq irq\n", dev->rev); + + if (!m2_enabled) + return IRQ_HANDLED; + + disable_irq_nosync(clkreq_irq); + /* enable clocks */ + ep_pcie_clk_init(dev); + ret = ep_pcie_pipe_clk_init(dev); + if (ret) { + EP_PCIE_ERR(dev, "PCIe V%d: failed to enable pipe clocks\n", + dev->rev); + return IRQ_HANDLED; + } + + m2_enabled = false; + dev->power_on = true; + + ret = ep_pcie_core_l1ss_sleep_config_disable(); + if (ret) { + EP_PCIE_ERR(dev, "PCIe V%d: l1ss sleep disable failed:%d\n", + dev->rev, ret); + return IRQ_HANDLED; + } + + ep_pcie_notify_event(dev, EP_PCIE_EVENT_L1SUB_TIMEOUT_EXIT); + + return IRQ_HANDLED; +} + +static irqreturn_t ep_pcie_handle_global_irq(int irq, void *data) +{ + struct ep_pcie_dev_t *dev = data; + int i; + u32 status = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_STATUS); + u32 mask = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK); + + ep_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_CLEAR, 0, status); + + dev->global_irq_counter++; + EP_PCIE_DUMP(dev, + "PCIe V%d: No. %ld Global IRQ %d received; status:0x%x; mask:0x%x\n", + dev->rev, dev->global_irq_counter, irq, status, mask); + status &= mask; + + for (i = 1; i <= EP_PCIE_INT_EVT_MAX; i++) { + if (status & BIT(i)) { + switch (i) { + case EP_PCIE_INT_EVT_LINK_DOWN: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle linkdown event\n", + dev->rev); + ep_pcie_handle_linkdown_irq(irq, data); + break; + case EP_PCIE_INT_EVT_BME: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle BME event\n", + dev->rev); + ep_pcie_handle_bme_irq(irq, data); + break; + case EP_PCIE_INT_EVT_PM_TURNOFF: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle PM Turn-off event\n", + dev->rev); + ep_pcie_handle_pm_turnoff_irq(irq, data); + break; + case EP_PCIE_INT_EVT_MHI_A7: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle MHI A7 event\n", + dev->rev); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_MHI_A7); + break; + case EP_PCIE_INT_EVT_DSTATE_CHANGE: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle D state change event\n", + dev->rev); + ep_pcie_handle_dstate_change_irq(irq, data); + break; + case EP_PCIE_INT_EVT_LINK_UP: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle linkup event\n", + dev->rev); + ep_pcie_handle_linkup_irq(irq, data); + break; + case EP_PCIE_INT_EVT_L1SUB_TIMEOUT: + EP_PCIE_DUMP(dev, + "PCIe V%d: handle L1ss timeout event\n", + dev->rev); + ep_pcie_notify_event(dev, + EP_PCIE_EVENT_L1SUB_TIMEOUT); + break; + default: + EP_PCIE_ERR(dev, + "PCIe V%d: Unexpected event %d\n", + dev->rev, i); + } + } + } + + return IRQ_HANDLED; +} + +int32_t ep_pcie_irq_init(struct ep_pcie_dev_t *dev) +{ + int ret; + struct device *pdev = &dev->pdev->dev; + u32 perst_irq; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + /* Initialize all works to be performed before registering for IRQs*/ + INIT_WORK(&dev->handle_perst_work, handle_perst_func); + INIT_WORK(&dev->handle_bme_work, handle_bme_func); + INIT_WORK(&dev->handle_d3cold_work, handle_d3cold_func); + + if (dev->aggregated_irq) { + ret = devm_request_irq(pdev, + dev->irq[EP_PCIE_INT_GLOBAL].num, + ep_pcie_handle_global_irq, + IRQF_TRIGGER_HIGH, dev->irq[EP_PCIE_INT_GLOBAL].name, + dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request global interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_GLOBAL].num); + return ret; + } + + ret = enable_irq_wake(dev->irq[EP_PCIE_INT_GLOBAL].num); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to enable wake for Global interrupt\n", + dev->rev); + } + + EP_PCIE_DBG(dev, + "PCIe V%d: request global interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_GLOBAL].num); + goto perst_irq; + } + + /* register handler for BME interrupt */ + ret = devm_request_irq(pdev, + dev->irq[EP_PCIE_INT_BME].num, + ep_pcie_handle_bme_irq, + IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_BME].name, + dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request BME interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_BME].num); + return ret; + } + + ret = enable_irq_wake(dev->irq[EP_PCIE_INT_BME].num); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to enable wake for BME interrupt\n", + dev->rev); + return ret; + } + + /* register handler for linkdown interrupt */ + ret = devm_request_irq(pdev, + dev->irq[EP_PCIE_INT_LINK_DOWN].num, + ep_pcie_handle_linkdown_irq, + IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_LINK_DOWN].name, + dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request linkdown interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_LINK_DOWN].num); + return ret; + } + + /* register handler for linkup interrupt */ + ret = devm_request_irq(pdev, + dev->irq[EP_PCIE_INT_LINK_UP].num, ep_pcie_handle_linkup_irq, + IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_LINK_UP].name, + dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request linkup interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_LINK_UP].num); + return ret; + } + + /* register handler for PM_TURNOFF interrupt */ + ret = devm_request_irq(pdev, + dev->irq[EP_PCIE_INT_PM_TURNOFF].num, + ep_pcie_handle_pm_turnoff_irq, + IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_PM_TURNOFF].name, + dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request PM_TURNOFF interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_PM_TURNOFF].num); + return ret; + } + + /* register handler for D state change interrupt */ + ret = devm_request_irq(pdev, + dev->irq[EP_PCIE_INT_DSTATE_CHANGE].num, + ep_pcie_handle_dstate_change_irq, + IRQF_TRIGGER_RISING, dev->irq[EP_PCIE_INT_DSTATE_CHANGE].name, + dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request D state change interrupt %d\n", + dev->rev, dev->irq[EP_PCIE_INT_DSTATE_CHANGE].num); + return ret; + } + +perst_irq: + /* + * Check initial state of perst gpio to set the trigger type + * based on the next expected level of the gpio + */ + if (gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num) == 1) + atomic_set(&dev->perst_deast, 1); + + /* register handler for PERST interrupt */ + perst_irq = gpio_to_irq(dev->gpio[EP_PCIE_GPIO_PERST].num); + ret = devm_request_irq(pdev, perst_irq, + ep_pcie_handle_perst_irq, + ((atomic_read(&dev->perst_deast) ? + IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH) + | IRQF_EARLY_RESUME), + "ep_pcie_perst", dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request PERST interrupt %d\n", + dev->rev, perst_irq); + return ret; + } + + ret = enable_irq_wake(perst_irq); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to enable PERST interrupt %d\n", + dev->rev, perst_irq); + return ret; + } + + if (dev->m2_autonomous) { + /* register handler for clkreq interrupt */ + EP_PCIE_ERR(dev, + "PCIe V%d: Register for CLKREQ interrupt %d\n", + dev->rev, clkreq_irq); + clkreq_irq = gpio_to_irq(dev->gpio[EP_PCIE_GPIO_CLKREQ].num); + irq_set_status_flags(clkreq_irq, IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(pdev, clkreq_irq, NULL, + ep_pcie_handle_clkreq_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "ep_pcie_clkreq", dev); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: Unable to request CLKREQ interrupt %d\n", + dev->rev, clkreq_irq); + return ret; + } + } + + return 0; +} + +void ep_pcie_irq_deinit(struct ep_pcie_dev_t *dev) +{ + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + disable_irq(gpio_to_irq(dev->gpio[EP_PCIE_GPIO_PERST].num)); +} + +int ep_pcie_core_register_event(struct ep_pcie_register_event *reg) +{ + if (!reg) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: Event registration is NULL\n", + ep_pcie_dev.rev); + return -ENODEV; + } + + if (!reg->user) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: User of event registration is NULL\n", + ep_pcie_dev.rev); + return -ENODEV; + } + + ep_pcie_dev.event_reg = reg; + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: Event 0x%x is registered\n", + ep_pcie_dev.rev, reg->events); + + ep_pcie_dev.client_ready = true; + + return 0; +} + +int ep_pcie_core_deregister_event(void) +{ + if (ep_pcie_dev.event_reg) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: current registered events:0x%x; events are deregistered\n", + ep_pcie_dev.rev, ep_pcie_dev.event_reg->events); + ep_pcie_dev.event_reg = NULL; + } else { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: Event registration is NULL\n", + ep_pcie_dev.rev); + } + + return 0; +} + +enum ep_pcie_link_status ep_pcie_core_get_linkstatus(void) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + u32 bme; + + if (!dev->power_on || (dev->link_status == EP_PCIE_LINK_DISABLED)) { + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe endpoint is not powered on\n", + dev->rev); + return EP_PCIE_LINK_DISABLED; + } + + bme = readl_relaxed(dev->dm_core + + PCIE20_COMMAND_STATUS) & BIT(2); + if (bme) { + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe link is up and BME is enabled; current SW link status:%d\n", + dev->rev, dev->link_status); + dev->link_status = EP_PCIE_LINK_ENABLED; + if (dev->no_notify) { + EP_PCIE_DBG(dev, + "PCIe V%d: BME is set now, but do not tell client about BME enable\n", + dev->rev); + return EP_PCIE_LINK_UP; + } + } else { + EP_PCIE_DBG(dev, + "PCIe V%d: PCIe link is up but BME is disabled; current SW link status:%d\n", + dev->rev, dev->link_status); + dev->link_status = EP_PCIE_LINK_UP; + } + return dev->link_status; +} + +int ep_pcie_core_config_outbound_iatu(struct ep_pcie_iatu entries[], + u32 num_entries) +{ + u32 data_start = 0; + u32 data_end = 0; + u32 data_tgt_lower = 0; + u32 data_tgt_upper = 0; + u32 ctrl_start = 0; + u32 ctrl_end = 0; + u32 ctrl_tgt_lower = 0; + u32 ctrl_tgt_upper = 0; + u32 upper = 0; + bool once = true; + + if (ep_pcie_dev.active_config) { + upper = EP_PCIE_OATU_UPPER; + if (once) { + once = false; + EP_PCIE_DBG2(&ep_pcie_dev, + "PCIe V%d: No outbound iATU config is needed since active config is enabled\n", + ep_pcie_dev.rev); + } + } + + if ((num_entries > MAX_IATU_ENTRY_NUM) || !num_entries) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: Wrong iATU entry number %d\n", + ep_pcie_dev.rev, num_entries); + return EP_PCIE_ERROR; + } + + data_start = entries[0].start; + data_end = entries[0].end; + data_tgt_lower = entries[0].tgt_lower; + data_tgt_upper = entries[0].tgt_upper; + + if (num_entries > 1) { + ctrl_start = entries[1].start; + ctrl_end = entries[1].end; + ctrl_tgt_lower = entries[1].tgt_lower; + ctrl_tgt_upper = entries[1].tgt_upper; + } + + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: data_start:0x%x; data_end:0x%x; data_tgt_lower:0x%x; data_tgt_upper:0x%x; ctrl_start:0x%x; ctrl_end:0x%x; ctrl_tgt_lower:0x%x; ctrl_tgt_upper:0x%x\n", + ep_pcie_dev.rev, data_start, data_end, data_tgt_lower, + data_tgt_upper, ctrl_start, ctrl_end, ctrl_tgt_lower, + ctrl_tgt_upper); + + + if ((ctrl_end < data_start) || (data_end < ctrl_start)) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: iATU configuration case No. 1: detached\n", + ep_pcie_dev.rev); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_DATA, + data_start, upper, data_end, + data_tgt_lower, data_tgt_upper); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_CTRL, + ctrl_start, upper, ctrl_end, + ctrl_tgt_lower, ctrl_tgt_upper); + } else if ((data_start <= ctrl_start) && (ctrl_end <= data_end)) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: iATU configuration case No. 2: included\n", + ep_pcie_dev.rev); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_DATA, + data_start, upper, data_end, + data_tgt_lower, data_tgt_upper); + } else { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: iATU configuration case No. 3: overlap\n", + ep_pcie_dev.rev); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_CTRL, + ctrl_start, upper, ctrl_end, + ctrl_tgt_lower, ctrl_tgt_upper); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_DATA, + data_start, upper, data_end, + data_tgt_lower, data_tgt_upper); + } + + return 0; +} + +int ep_pcie_core_get_msi_config(struct ep_pcie_msi_config *cfg) +{ + u32 cap, lower, upper, data, ctrl_reg; + static u32 changes; + + if (ep_pcie_dev.link_status == EP_PCIE_LINK_DISABLED) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: PCIe link is currently disabled\n", + ep_pcie_dev.rev); + return EP_PCIE_ERROR; + } + + cap = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_CAP_ID_NEXT_CTRL); + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: MSI CAP:0x%x\n", + ep_pcie_dev.rev, cap); + + if (!(cap & BIT(16))) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: MSI is not enabled yet\n", + ep_pcie_dev.rev); + return EP_PCIE_ERROR; + } + + lower = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_LOWER); + upper = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_UPPER); + data = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_DATA); + ctrl_reg = readl_relaxed(ep_pcie_dev.dm_core + + PCIE20_MSI_CAP_ID_NEXT_CTRL); + + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: MSI info: lower:0x%x; upper:0x%x; data:0x%x\n", + ep_pcie_dev.rev, lower, upper, data); + + if (ctrl_reg & BIT(16)) { + struct resource *msi = + ep_pcie_dev.res[EP_PCIE_RES_MSI].resource; + if (ep_pcie_dev.active_config) + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_MSI, + msi->start, EP_PCIE_OATU_UPPER, + msi->end, lower, upper); + else + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_MSI, + msi->start, 0, msi->end, + lower, upper); + + if (ep_pcie_dev.active_config || ep_pcie_dev.pcie_edma) { + cfg->lower = lower; + cfg->upper = upper; + } else { + cfg->lower = msi->start + (lower & 0xfff); + cfg->upper = 0; + } + cfg->data = data; + cfg->msg_num = (cap >> 20) & 0x7; + if ((lower != ep_pcie_dev.msi_cfg.lower) + || (upper != ep_pcie_dev.msi_cfg.upper) + || (data != ep_pcie_dev.msi_cfg.data) + || (cfg->msg_num != ep_pcie_dev.msi_cfg.msg_num)) { + changes++; + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: MSI config has been changed by host side for %d time(s)\n", + ep_pcie_dev.rev, changes); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: old MSI cfg: lower:0x%x; upper:0x%x; data:0x%x; msg_num:0x%x\n", + ep_pcie_dev.rev, ep_pcie_dev.msi_cfg.lower, + ep_pcie_dev.msi_cfg.upper, + ep_pcie_dev.msi_cfg.data, + ep_pcie_dev.msi_cfg.msg_num); + ep_pcie_dev.msi_cfg.lower = lower; + ep_pcie_dev.msi_cfg.upper = upper; + ep_pcie_dev.msi_cfg.data = data; + ep_pcie_dev.msi_cfg.msg_num = cfg->msg_num; + } + return 0; + } + + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: Wrong MSI info found when MSI is enabled: lower:0x%x; data:0x%x\n", + ep_pcie_dev.rev, lower, data); + return EP_PCIE_ERROR; +} + +int ep_pcie_core_trigger_msi(u32 idx) +{ + u32 addr, data, ctrl_reg; + int max_poll = MSI_EXIT_L1SS_WAIT_MAX_COUNT; + + if (ep_pcie_dev.link_status == EP_PCIE_LINK_DISABLED) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: PCIe link is currently disabled\n", + ep_pcie_dev.rev); + return EP_PCIE_ERROR; + } + + addr = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_LOWER); + data = readl_relaxed(ep_pcie_dev.dm_core + PCIE20_MSI_DATA); + ctrl_reg = readl_relaxed(ep_pcie_dev.dm_core + + PCIE20_MSI_CAP_ID_NEXT_CTRL); + + if (ctrl_reg & BIT(16)) { + ep_pcie_dev.msi_counter++; + EP_PCIE_DUMP(&ep_pcie_dev, + "PCIe V%d: No. %ld MSI fired for IRQ %d; index from client:%d; active-config is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.msi_counter, + data + idx, idx, + ep_pcie_dev.active_config ? "" : "not"); + + if (ep_pcie_dev.active_config) { + u32 status; + + if (ep_pcie_dev.msi_counter % 2) { + EP_PCIE_DBG2(&ep_pcie_dev, + "PCIe V%d: try to trigger MSI by PARF_MSI_GEN\n", + ep_pcie_dev.rev); + ep_pcie_write_reg(ep_pcie_dev.parf, + PCIE20_PARF_MSI_GEN, idx); + status = readl_relaxed(ep_pcie_dev.parf + + PCIE20_PARF_LTR_MSI_EXIT_L1SS); + while ((status & BIT(1)) && (max_poll-- > 0)) { + udelay(MSI_EXIT_L1SS_WAIT); + status = readl_relaxed(ep_pcie_dev.parf + + + PCIE20_PARF_LTR_MSI_EXIT_L1SS); + } + if (max_poll == 0) + EP_PCIE_DBG2(&ep_pcie_dev, + "PCIe V%d: MSI_EXIT_L1SS is not cleared yet\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG2(&ep_pcie_dev, + "PCIe V%d: MSI_EXIT_L1SS has been cleared\n", + ep_pcie_dev.rev); + } else { + EP_PCIE_DBG2(&ep_pcie_dev, + "PCIe V%d: try to trigger MSI by direct address write as well\n", + ep_pcie_dev.rev); + ep_pcie_write_reg(ep_pcie_dev.msi, addr & 0xfff, + data + idx); + } + } else { + ep_pcie_write_reg(ep_pcie_dev.msi, addr & 0xfff, data + + idx); + } + return 0; + } + + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: MSI is not enabled yet. MSI addr:0x%x; data:0x%x; index from client:%d\n", + ep_pcie_dev.rev, addr, data, idx); + return EP_PCIE_ERROR; +} + +static void ep_pcie_core_issue_inband_pme(void) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + unsigned long irqsave_flags; + u32 pm_ctrl = 0; + + spin_lock_irqsave(&dev->isr_lock, irqsave_flags); + + EP_PCIE_DBG(dev, + "PCIe V%d: request to assert inband wake\n", + dev->rev); + + pm_ctrl = readl_relaxed(dev->parf + PCIE20_PARF_PM_CTRL); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_PM_CTRL, + (pm_ctrl | BIT(4))); + ep_pcie_write_reg(dev->parf, PCIE20_PARF_PM_CTRL, pm_ctrl); + + EP_PCIE_DBG(dev, + "PCIe V%d: completed assert for inband wake\n", + dev->rev); + + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); +} + +static int ep_pcie_core_wakeup_host(enum ep_pcie_event event) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + if (event == EP_PCIE_EVENT_PM_D3_HOT) + ep_pcie_core_issue_inband_pme(); + + if (atomic_read(&dev->perst_deast) && !dev->l23_ready) { + EP_PCIE_ERR(dev, + "PCIe V%d: request to assert WAKE# when PERST is de-asserted and D3hot is not received\n", + dev->rev); + return EP_PCIE_ERROR; + } + + dev->wake_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld to assert PCIe WAKE#; perst is %s de-asserted; D3hot is %s received\n", + dev->rev, dev->wake_counter, + atomic_read(&dev->perst_deast) ? "" : "not", + dev->l23_ready ? "" : "not"); + /* + * Assert WAKE# GPIO until link is back to L0. + */ + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + dev->gpio[EP_PCIE_GPIO_WAKE].on); + return 0; +} + +int ep_pcie_core_config_db_routing(struct ep_pcie_db_config chdb_cfg, + struct ep_pcie_db_config erdb_cfg) +{ + u32 dbs = (erdb_cfg.end << 24) | (erdb_cfg.base << 16) | + (chdb_cfg.end << 8) | chdb_cfg.base; + + ep_pcie_write_reg(ep_pcie_dev.parf, PCIE20_PARF_MHI_IPA_DBS, dbs); + ep_pcie_write_reg(ep_pcie_dev.parf, + PCIE20_PARF_MHI_IPA_CDB_TARGET_LOWER, + chdb_cfg.tgt_addr); + ep_pcie_write_reg(ep_pcie_dev.parf, + PCIE20_PARF_MHI_IPA_EDB_TARGET_LOWER, + erdb_cfg.tgt_addr); + + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: DB routing info: chdb_cfg.base:0x%x; chdb_cfg.end:0x%x; erdb_cfg.base:0x%x; erdb_cfg.end:0x%x; chdb_cfg.tgt_addr:0x%x; erdb_cfg.tgt_addr:0x%x\n", + ep_pcie_dev.rev, chdb_cfg.base, chdb_cfg.end, erdb_cfg.base, + erdb_cfg.end, chdb_cfg.tgt_addr, erdb_cfg.tgt_addr); + + return 0; +} + +struct ep_pcie_hw hw_drv = { + .register_event = ep_pcie_core_register_event, + .deregister_event = ep_pcie_core_deregister_event, + .get_linkstatus = ep_pcie_core_get_linkstatus, + .config_outbound_iatu = ep_pcie_core_config_outbound_iatu, + .get_msi_config = ep_pcie_core_get_msi_config, + .trigger_msi = ep_pcie_core_trigger_msi, + .wakeup_host = ep_pcie_core_wakeup_host, + .config_db_routing = ep_pcie_core_config_db_routing, + .enable_endpoint = ep_pcie_core_enable_endpoint, + .disable_endpoint = ep_pcie_core_disable_endpoint, + .mask_irq_event = ep_pcie_core_mask_irq_event, + .configure_inactivity_timer = ep_pcie_core_config_inact_timer, +}; + +static int ep_pcie_probe(struct platform_device *pdev) +{ + int ret; + + pr_debug("%s\n", __func__); + + ep_pcie_dev.link_speed = 1; + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,pcie-link-speed", + &ep_pcie_dev.link_speed); + if (ret) + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: pcie-link-speed does not exist\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-link-speed:%d\n", + ep_pcie_dev.rev, ep_pcie_dev.link_speed); + + ep_pcie_dev.vendor_id = 0xFFFF; + ret = of_property_read_u16((&pdev->dev)->of_node, + "qcom,pcie-vendor-id", + &ep_pcie_dev.vendor_id); + if (ret) + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: pcie-vendor-id does not exist.\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-vendor-id:%d.\n", + ep_pcie_dev.rev, ep_pcie_dev.vendor_id); + + ep_pcie_dev.device_id = 0xFFFF; + ret = of_property_read_u16((&pdev->dev)->of_node, + "qcom,pcie-device-id", + &ep_pcie_dev.device_id); + if (ret) + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: pcie-device-id does not exist.\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-device-id:%d.\n", + ep_pcie_dev.rev, ep_pcie_dev.device_id); + + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,dbi-base-reg", + &ep_pcie_dev.dbi_base_reg); + if (ret) + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: dbi-base-reg does not exist\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: dbi-base-reg:0x%x\n", + ep_pcie_dev.rev, ep_pcie_dev.dbi_base_reg); + + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,slv-space-reg", + &ep_pcie_dev.slv_space_reg); + if (ret) + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: slv-space-reg does not exist\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: slv-space-reg:0x%x\n", + ep_pcie_dev.rev, ep_pcie_dev.slv_space_reg); + + 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_status_bit_mask_bit = BIT(6); + + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,phy-status-reg2", + &ep_pcie_dev.phy_status_reg); + if (ret) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: phy-status-reg2 does not exist\n", + ep_pcie_dev.rev); + } else { + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: phy-status-reg2:0x%x\n", + ep_pcie_dev.rev, ep_pcie_dev.phy_status_reg); + ep_pcie_dev.phy_status_bit_mask_bit = BIT(7); + } + + ep_pcie_dev.phy_rev = 1; + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,pcie-phy-ver", + &ep_pcie_dev.phy_rev); + if (ret) + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: pcie-phy-ver does not exist\n", + ep_pcie_dev.rev); + else + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: pcie-phy-ver:%d\n", + ep_pcie_dev.rev, ep_pcie_dev.phy_rev); + + ep_pcie_dev.pcie_edma = of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-edma"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: pcie edma is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.pcie_edma ? "" : "not"); + + ep_pcie_dev.active_config = of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-active-config"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: active config is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.active_config ? "" : "not"); + + ep_pcie_dev.aggregated_irq = + of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-aggregated-irq"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: aggregated IRQ is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.aggregated_irq ? "" : "not"); + + ep_pcie_dev.mhi_a7_irq = + of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-mhi-a7-irq"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: Mhi a7 IRQ is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.mhi_a7_irq ? "" : "not"); + + ep_pcie_dev.perst_enum = of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-perst-enum"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: enum by PERST is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.perst_enum ? "" : "not"); + + ep_pcie_dev.tcsr_not_supported = of_property_read_bool + ((&pdev->dev)->of_node, + "qcom,tcsr-not-supported"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: tcsr pcie perst is %s supported\n", + ep_pcie_dev.rev, ep_pcie_dev.tcsr_not_supported ? "not" : ""); + + ep_pcie_dev.rev = 1711211; + ep_pcie_dev.pdev = pdev; + ep_pcie_dev.m2_autonomous = + of_property_read_bool((&pdev->dev)->of_node, + "qcom,pcie-m2-autonomous"); + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: MHI M2 autonomous is %s enabled\n", + ep_pcie_dev.rev, ep_pcie_dev.m2_autonomous ? "" : "not"); + + ret = of_property_read_u32((&pdev->dev)->of_node, + "qcom,mhi-soc-reset-offset", + &ep_pcie_dev.mhi_soc_reset_offset); + if (ret) { + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: qcom,mhi-soc-reset does not exist\n", + ep_pcie_dev.rev); + } else { + EP_PCIE_DBG(&ep_pcie_dev, "PCIe V%d: soc-reset-offset:0x%x\n", + ep_pcie_dev.rev, ep_pcie_dev.mhi_soc_reset_offset); + ep_pcie_dev.mhi_soc_reset_en = true; + } + + memcpy(ep_pcie_dev.vreg, ep_pcie_vreg_info, + sizeof(ep_pcie_vreg_info)); + memcpy(ep_pcie_dev.gpio, ep_pcie_gpio_info, + sizeof(ep_pcie_gpio_info)); + memcpy(ep_pcie_dev.clk, ep_pcie_clk_info, + sizeof(ep_pcie_clk_info)); + memcpy(ep_pcie_dev.pipeclk, ep_pcie_pipe_clk_info, + sizeof(ep_pcie_pipe_clk_info)); + memcpy(ep_pcie_dev.reset, ep_pcie_reset_info, + sizeof(ep_pcie_reset_info)); + memcpy(ep_pcie_dev.res, ep_pcie_res_info, + sizeof(ep_pcie_res_info)); + memcpy(ep_pcie_dev.irq, ep_pcie_irq_info, + sizeof(ep_pcie_irq_info)); + + ret = ep_pcie_get_resources(&ep_pcie_dev, + ep_pcie_dev.pdev); + if (ret) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: failed to get resources\n", + ep_pcie_dev.rev); + goto res_failure; + } + + ret = ep_pcie_gpio_init(&ep_pcie_dev); + if (ret) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: failed to init GPIO\n", + ep_pcie_dev.rev); + ep_pcie_release_resources(&ep_pcie_dev); + goto gpio_failure; + } + + ret = ep_pcie_irq_init(&ep_pcie_dev); + if (ret) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: failed to init IRQ\n", + ep_pcie_dev.rev); + ep_pcie_release_resources(&ep_pcie_dev); + ep_pcie_gpio_deinit(&ep_pcie_dev); + goto irq_failure; + } + + /* + * Wakelock is needed to avoid missing BME and other + * interrupts if apps goes into suspend before host + * sets them. + */ + device_init_wakeup(&ep_pcie_dev.pdev->dev, true); + atomic_set(&ep_pcie_dev.ep_pcie_dev_wake, 0); + + if (ep_pcie_dev.perst_enum && + !gpio_get_value(ep_pcie_dev.gpio[EP_PCIE_GPIO_PERST].num)) { + EP_PCIE_DBG2(&ep_pcie_dev, + "PCIe V%d: %s probe is done; link will be trained when PERST is deasserted\n", + ep_pcie_dev.rev, dev_name(&(pdev->dev))); + return 0; + } + + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: %s got resources successfully; start turning on the link\n", + ep_pcie_dev.rev, dev_name(&(pdev->dev))); + + ret = ep_pcie_enumeration(&ep_pcie_dev); + if (ret && !ep_pcie_debug_keep_resource) + goto irq_deinit; + + qcom_edma_init(&pdev->dev); + + return 0; + +irq_deinit: + ep_pcie_irq_deinit(&ep_pcie_dev); +irq_failure: + ep_pcie_gpio_deinit(&ep_pcie_dev); +gpio_failure: + ep_pcie_release_resources(&ep_pcie_dev); +res_failure: + EP_PCIE_ERR(&ep_pcie_dev, "PCIe V%d: Driver probe failed:%d\n", + ep_pcie_dev.rev, ret); + + return ret; +} + +static int __exit ep_pcie_remove(struct platform_device *pdev) +{ + pr_debug("%s\n", __func__); + + ep_pcie_irq_deinit(&ep_pcie_dev); + ep_pcie_vreg_deinit(&ep_pcie_dev); + ep_pcie_pipe_clk_deinit(&ep_pcie_dev); + ep_pcie_clk_deinit(&ep_pcie_dev); + ep_pcie_gpio_deinit(&ep_pcie_dev); + ep_pcie_release_resources(&ep_pcie_dev); + ep_pcie_deregister_drv(&hw_drv); + + return 0; +} + +static const struct of_device_id ep_pcie_match[] = { + { .compatible = "qcom,pcie-ep", + }, + {} +}; + +static struct platform_driver ep_pcie_driver = { + .probe = ep_pcie_probe, + .remove = ep_pcie_remove, + .driver = { + .name = "pcie-ep", + .of_match_table = ep_pcie_match, + }, +}; + +static int __init ep_pcie_init(void) +{ + int ret; + char logname[MAX_NAME_LEN]; + + pr_debug("%s\n", __func__); + + snprintf(logname, MAX_NAME_LEN, "ep-pcie-long"); + ep_pcie_dev.ipc_log_sel = + ipc_log_context_create(EP_PCIE_LOG_PAGES, logname, 0); + if (ep_pcie_dev.ipc_log_sel == NULL) + pr_err("%s: unable to create IPC selected log for %s\n", + __func__, logname); + else + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: IPC selected logging is enable for %s\n", + ep_pcie_dev.rev, logname); + + snprintf(logname, MAX_NAME_LEN, "ep-pcie-short"); + ep_pcie_dev.ipc_log_ful = + ipc_log_context_create(EP_PCIE_LOG_PAGES * 2, logname, 0); + if (ep_pcie_dev.ipc_log_ful == NULL) + pr_err("%s: unable to create IPC detailed log for %s\n", + __func__, logname); + else + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: IPC detailed logging is enable for %s\n", + ep_pcie_dev.rev, logname); + + snprintf(logname, MAX_NAME_LEN, "ep-pcie-dump"); + ep_pcie_dev.ipc_log_dump = + ipc_log_context_create(EP_PCIE_LOG_PAGES, logname, 0); + if (ep_pcie_dev.ipc_log_dump == NULL) + pr_err("%s: unable to create IPC dump log for %s\n", + __func__, logname); + else + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: IPC dump logging is enable for %s\n", + ep_pcie_dev.rev, logname); + + mutex_init(&ep_pcie_dev.setup_mtx); + mutex_init(&ep_pcie_dev.ext_mtx); + spin_lock_init(&ep_pcie_dev.ext_lock); + spin_lock_init(&ep_pcie_dev.isr_lock); + + ep_pcie_debugfs_init(&ep_pcie_dev); + + ret = platform_driver_register(&ep_pcie_driver); + + if (ret) + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: failed register platform driver:%d\n", + ep_pcie_dev.rev, ret); + else + EP_PCIE_DBG(&ep_pcie_dev, + "PCIe V%d: platform driver is registered\n", + ep_pcie_dev.rev); + + return ret; +} + +static void __exit ep_pcie_exit(void) +{ + pr_debug("%s\n", __func__); + + ipc_log_context_destroy(ep_pcie_dev.ipc_log_sel); + ipc_log_context_destroy(ep_pcie_dev.ipc_log_ful); + ipc_log_context_destroy(ep_pcie_dev.ipc_log_dump); + + ep_pcie_debugfs_exit(); + + platform_driver_unregister(&ep_pcie_driver); +} + +subsys_initcall(ep_pcie_init); +module_exit(ep_pcie_exit); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MSM PCIe Endpoint Driver"); diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c b/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c new file mode 100644 index 000000000000..4940fe940daa --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ + +/* + * Debugging enhancement in MSM PCIe endpoint driver. + */ + +#include +#include +#include +#include +#include +#include "ep_pcie_com.h" +#include "ep_pcie_phy.h" + +static struct dentry *dent_ep_pcie; +static struct dentry *dfile_case; +static struct ep_pcie_dev_t *dev; + +static void ep_ep_pcie_phy_dump_pcs_debug_bus(struct ep_pcie_dev_t *dev, + u32 cntrl4, u32 cntrl5, + u32 cntrl6, u32 cntrl7) +{ + ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL4, cntrl4); + ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL5, cntrl5); + ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL6, cntrl6); + ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL7, cntrl7); + + if (!cntrl4 && !cntrl5 && !cntrl6 && !cntrl7) { + EP_PCIE_DUMP(dev, + "PCIe V%d: zero out test control registers\n\n", + dev->rev); + return; + } + + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_TEST_CONTROL4: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL4)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_TEST_CONTROL5: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL5)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_TEST_CONTROL6: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL6)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_TEST_CONTROL7: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL7)); + + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_DEBUG_BUS_0_STATUS: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_0_STATUS)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_DEBUG_BUS_1_STATUS: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_1_STATUS)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_DEBUG_BUS_2_STATUS: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_2_STATUS)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_DEBUG_BUS_3_STATUS: 0x%x\n\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_3_STATUS)); +} + +static void ep_ep_pcie_phy_dump_pcs_misc_debug_bus(struct ep_pcie_dev_t *dev, + u32 b0, u32 b1, u32 b2, u32 b3) +{ + ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX, b0); + ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX, b1); + ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX, b2); + ep_pcie_write_reg(dev->phy, PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX, b3); + + if (!b0 && !b1 && !b2 && !b3) { + EP_PCIE_DUMP(dev, + "PCIe V%d: zero out misc debug bus byte index registers\n\n", + dev->rev); + return; + } + + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX)); + + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_0_STATUS: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_0_STATUS)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_1_STATUS: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_1_STATUS)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_2_STATUS: 0x%x\n", dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_2_STATUS)); + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_MISC_DEBUG_BUS_3_STATUS: 0x%x\n\n", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_MISC_DEBUG_BUS_3_STATUS)); +} + +static void ep_pcie_phy_dump(struct ep_pcie_dev_t *dev) +{ + int i; + u32 write_val; + + EP_PCIE_DUMP(dev, "PCIe V%d: Beginning of PHY debug dump\n\n", + dev->rev); + + EP_PCIE_DUMP(dev, "PCIe V%d: PCS Debug Signals\n\n", dev->rev); + + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x01, 0x02, 0x03, 0x0A); + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x0E, 0x0F, 0x12, 0x13); + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x18, 0x19, 0x1A, 0x1B); + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x1C, 0x1D, 0x1E, 0x1F); + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x20, 0x21, 0x22, 0x23); + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0, 0, 0, 0); + + EP_PCIE_DUMP(dev, "PCIe V%d: PCS Misc Debug Signals\n\n", dev->rev); + + ep_ep_pcie_phy_dump_pcs_misc_debug_bus(dev, 0x1, 0x2, 0x3, 0x4); + ep_ep_pcie_phy_dump_pcs_misc_debug_bus(dev, 0x5, 0x6, 0x7, 0x8); + ep_ep_pcie_phy_dump_pcs_misc_debug_bus(dev, 0, 0, 0, 0); + + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES COM Debug Signals\n\n", dev->rev); + + for (i = 0; i < 2; i++) { + write_val = 0x2 + i; + + ep_pcie_write_reg(dev->phy, QSERDES_COM_DEBUG_BUS_SEL, + write_val); + + EP_PCIE_DUMP(dev, + "PCIe V%d: to QSERDES_COM_DEBUG_BUS_SEL: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS_SEL)); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_COM_DEBUG_BUS0: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS0)); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_COM_DEBUG_BUS1: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS1)); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_COM_DEBUG_BUS2: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS2)); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_COM_DEBUG_BUS3: 0x%x\n\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS3)); + } + + ep_pcie_write_reg(dev->phy, QSERDES_COM_DEBUG_BUS_SEL, 0); + + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES LANE Debug Signals\n\n", + dev->rev); + + for (i = 0; i < 3; i++) { + write_val = 0x1 + i; + ep_pcie_write_reg(dev->phy, + QSERDES_TX_DEBUG_BUS_SEL, write_val); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_TX_DEBUG_BUS_SEL: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_TX_DEBUG_BUS_SEL)); + + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0x30, 0x31, 0x32, 0x33); + } + + ep_ep_pcie_phy_dump_pcs_debug_bus(dev, 0, 0, 0, 0); + + EP_PCIE_DUMP(dev, "PCIe V%d: End of PHY debug dump\n\n", dev->rev); + +} + +void ep_pcie_reg_dump(struct ep_pcie_dev_t *dev, u32 sel, bool linkdown) +{ + int r, i; + u32 original; + u32 size; + + EP_PCIE_DBG(dev, + "PCIe V%d: Dump PCIe reg for 0x%x %s linkdown\n", + dev->rev, sel, linkdown ? "with" : "without"); + + if (!dev->power_on) { + EP_PCIE_ERR(dev, + "PCIe V%d: the power is already down; can't dump registers\n", + dev->rev); + return; + } + + if (linkdown) { + EP_PCIE_DUMP(dev, + "PCIe V%d: dump PARF registers for linkdown case\n", + dev->rev); + + original = readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL); + for (i = 1; i <= 0x1A; i++) { + ep_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL, + 0xFF0000, i << 16); + EP_PCIE_DUMP(dev, + "PCIe V%d: PARF_SYS_CTRL:0x%x PARF_TEST_BUS:0x%x\n", + dev->rev, + readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL), + readl_relaxed(dev->parf + + PCIE20_PARF_TEST_BUS)); + } + ep_pcie_write_reg(dev->parf, PCIE20_PARF_SYS_CTRL, original); + } + + for (r = 0; r < EP_PCIE_MAX_RES; r++) { + if (!(sel & BIT(r))) + continue; + + if ((r == EP_PCIE_RES_PHY) && (dev->phy_rev > 3)) + ep_pcie_phy_dump(dev); + + size = resource_size(dev->res[r].resource); + EP_PCIE_DUMP(dev, + "\nPCIe V%d: dump registers of %s\n\n", + dev->rev, dev->res[r].name); + + for (i = 0; i < size; i += 32) { + EP_PCIE_DUMP(dev, + "0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n", + i, readl_relaxed(dev->res[r].base + i), + readl_relaxed(dev->res[r].base + (i + 4)), + readl_relaxed(dev->res[r].base + (i + 8)), + readl_relaxed(dev->res[r].base + (i + 12)), + readl_relaxed(dev->res[r].base + (i + 16)), + readl_relaxed(dev->res[r].base + (i + 20)), + readl_relaxed(dev->res[r].base + (i + 24)), + readl_relaxed(dev->res[r].base + (i + 28))); + } + } +} + +static void ep_pcie_show_status(struct ep_pcie_dev_t *dev) +{ + EP_PCIE_DBG_FS("PCIe: is %s enumerated\n", + dev->enumerated ? "" : "not"); + EP_PCIE_DBG_FS("PCIe: link is %s\n", + (dev->link_status == EP_PCIE_LINK_ENABLED) + ? "enabled" : "disabled"); + EP_PCIE_DBG_FS("the link is %s suspending\n", + dev->suspending ? "" : "not"); + EP_PCIE_DBG_FS("the power is %s on\n", + dev->power_on ? "" : "not"); + EP_PCIE_DBG_FS("linkdown_counter: %lu\n", + dev->linkdown_counter); + EP_PCIE_DBG_FS("linkup_counter: %lu\n", + dev->linkup_counter); + EP_PCIE_DBG_FS("wake_counter: %lu\n", + dev->wake_counter); + EP_PCIE_DBG_FS("d0_counter: %lu\n", + dev->d0_counter); + EP_PCIE_DBG_FS("d3_counter: %lu\n", + dev->d3_counter); + EP_PCIE_DBG_FS("perst_ast_counter: %lu\n", + dev->perst_ast_counter); + EP_PCIE_DBG_FS("perst_deast_counter: %lu\n", + dev->perst_deast_counter); +} + +static ssize_t ep_pcie_cmd_debug(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned long ret; + char str[MAX_MSG_LEN]; + unsigned int testcase = 0; + struct ep_pcie_msi_config msi_cfg; + int i; + struct ep_pcie_hw *phandle = NULL; + struct ep_pcie_iatu entries[2] = { + {0x80000000, 0xbe7fffff, 0, 0}, + {0xb1440000, 0xb144ae1e, 0x31440000, 0} + }; + struct ep_pcie_db_config chdb_cfg = {0x64, 0x6b, 0xfd4fa000}; + struct ep_pcie_db_config erdb_cfg = {0x64, 0x6b, 0xfd4fa080}; + + phandle = ep_pcie_get_phandle(hw_drv.device_id); + + memset(str, 0, sizeof(str)); + ret = copy_from_user(str, buf, sizeof(str)); + if (ret) + return -EFAULT; + + for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i) + testcase = (testcase * 10) + (str[i] - '0'); + + EP_PCIE_DBG_FS("PCIe: TEST: %d\n", testcase); + + + switch (testcase) { + case 0: /* output status */ + ep_pcie_show_status(dev); + break; + case 1: /* output PHY and PARF registers */ + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_PHY) | + BIT(EP_PCIE_RES_PARF), true); + break; + case 2: /* output core registers */ + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_DM_CORE), false); + break; + case 3: /* output MMIO registers */ + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_MMIO), false); + break; + case 4: /* output ELBI registers */ + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_ELBI), false); + break; + case 5: /* output MSI registers */ + ep_pcie_reg_dump(dev, BIT(EP_PCIE_RES_MSI), false); + break; + case 6: /* turn on link */ + ep_pcie_enable_endpoint(phandle, EP_PCIE_OPT_ALL); + break; + case 7: /* enumeration */ + ep_pcie_enable_endpoint(phandle, EP_PCIE_OPT_ENUM); + break; + case 8: /* turn off link */ + ep_pcie_disable_endpoint(phandle); + break; + case 9: /* check MSI */ + ep_pcie_get_msi_config(phandle, &msi_cfg); + break; + case 10: /* trigger MSI */ + ep_pcie_trigger_msi(phandle, 0); + break; + case 11: /* indicate the status of PCIe link */ + EP_PCIE_DBG_FS("\nPCIe: link status is %d\n\n", + ep_pcie_get_linkstatus(phandle)); + break; + case 12: /* configure outbound iATU */ + ep_pcie_config_outbound_iatu(phandle, entries, 2); + break; + case 13: /* wake up the host */ + ep_pcie_wakeup_host(phandle, EP_PCIE_EVENT_PM_D3_HOT); + break; + case 14: /* Configure routing of doorbells */ + ep_pcie_config_db_routing(phandle, chdb_cfg, erdb_cfg); + break; + case 21: /* write D3 */ + EP_PCIE_DBG_FS("\nPCIe Testcase %d: write D3 to EP\n\n", + testcase); + EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x before change\n\n", + readl_relaxed(dev->dm_core + 0x44)); + ep_pcie_write_mask(dev->dm_core + 0x44, 0, 0x3); + EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x now\n\n", + readl_relaxed(dev->dm_core + 0x44)); + break; + case 22: /* write D0 */ + EP_PCIE_DBG_FS("\nPCIe Testcase %d: write D0 to EP\n\n", + testcase); + EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x before change\n\n", + readl_relaxed(dev->dm_core + 0x44)); + ep_pcie_write_mask(dev->dm_core + 0x44, 0x3, 0); + EP_PCIE_DBG_FS("\nPCIe: 0x44 of EP is 0x%x now\n\n", + readl_relaxed(dev->dm_core + 0x44)); + break; + case 23: /* assert wake */ + EP_PCIE_DBG_FS("\nPCIe Testcase %d: assert wake\n\n", + testcase); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + dev->gpio[EP_PCIE_GPIO_WAKE].on); + break; + case 24: /* deassert wake */ + EP_PCIE_DBG_FS("\nPCIe Testcase %d: deassert wake\n\n", + testcase); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); + break; + case 25: /* output PERST# status */ + EP_PCIE_DBG_FS("\nPCIe: PERST# is %d\n\n", + gpio_get_value(dev->gpio[EP_PCIE_GPIO_PERST].num)); + break; + case 26: /* output WAKE# status */ + EP_PCIE_DBG_FS("\nPCIe: WAKE# is %d\n\n", + gpio_get_value(dev->gpio[EP_PCIE_GPIO_WAKE].num)); + break; + case 31: /* output core registers when D3 hot is set by host*/ + dev->dump_conf = true; + break; + case 32: /* do not output core registers when D3 hot is set by host*/ + dev->dump_conf = false; + break; + case 33: /* output edma registers */ + edma_dump(); + break; + default: + EP_PCIE_DBG_FS("PCIe: Invalid testcase: %d\n", testcase); + break; + } + + if (ret == 0) + return count; + else + return -EFAULT; +} + +const struct file_operations ep_pcie_cmd_debug_ops = { + .write = ep_pcie_cmd_debug, +}; + +void ep_pcie_debugfs_init(struct ep_pcie_dev_t *ep_dev) +{ + dev = ep_dev; + dent_ep_pcie = debugfs_create_dir("pcie-ep", 0); + if (IS_ERR(dent_ep_pcie)) { + EP_PCIE_ERR(dev, + "PCIe V%d: fail to create the folder for debug_fs\n", + dev->rev); + return; + } + + dfile_case = debugfs_create_file("case", 0664, + dent_ep_pcie, 0, + &ep_pcie_cmd_debug_ops); + if (!dfile_case || IS_ERR(dfile_case)) { + EP_PCIE_ERR(dev, + "PCIe V%d: fail to create the file for case\n", + dev->rev); + goto case_error; + } + + EP_PCIE_DBG2(dev, + "PCIe V%d: debugfs is enabled\n", + dev->rev); + + return; + +case_error: + debugfs_remove(dent_ep_pcie); +} + +void ep_pcie_debugfs_exit(void) +{ + debugfs_remove(dfile_case); + debugfs_remove(dent_ep_pcie); +} diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.c b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c new file mode 100644 index 000000000000..7cb3c8832288 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015-2018, 2019-2020, The Linux Foundation. All rights reserved.*/ + +/* + * MSM PCIe PHY endpoint mode + */ + +#include "ep_pcie_com.h" +#include "ep_pcie_phy.h" + +void ep_pcie_phy_init(struct ep_pcie_dev_t *dev) +{ + switch (dev->phy_rev) { + case 3: + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 20nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + break; + case 4: + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 14nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + break; + case 5: + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 10nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + break; + case 6: + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 7nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + break; + default: + EP_PCIE_ERR(dev, + "PCIe V%d: Unexpected phy version %d is caught\n", + dev->rev, dev->phy_rev); + } + + if (dev->phy_init_len && dev->phy_init) { + int i; + struct ep_pcie_phy_info_t *phy_init; + + 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_init_len; + phy_init = dev->phy_init; + while (i--) { + ep_pcie_write_reg(dev->phy, + phy_init->offset, + phy_init->val); + if (phy_init->delay) + usleep_range(phy_init->delay, + phy_init->delay + 1); + phy_init++; + } + return; + } + + ep_pcie_write_reg(dev->phy, PCIE_PHY_SW_RESET, 0x01); + ep_pcie_write_reg(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x01); + + /* Common block settings */ + ep_pcie_write_reg(dev->phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18); + ep_pcie_write_reg(dev->phy, QSERDES_COM_CLK_ENABLE1, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_BG_TRIM, 0x0F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP_EN, 0x01); + ep_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_MAP, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER1, 0xFF); + ep_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER2, 0x1F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_CMN_CONFIG, 0x06); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_IVCO, 0x0F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_HSCLK_SEL, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01); + ep_pcie_write_reg(dev->phy, QSERDES_COM_CORE_CLK_EN, 0x20); + ep_pcie_write_reg(dev->phy, QSERDES_COM_CORECLK_DIV, 0x0A); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RESETSM_CNTRL, 0x20); + ep_pcie_write_reg(dev->phy, QSERDES_COM_BG_TIMER, 0x01); + + /* PLL Config Settings */ + ep_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_EN_SEL, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_DEC_START_MODE0, 0x19); + ep_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP3_MODE0, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x02); + ep_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x7F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_CLK_SELECT, 0x30); + ep_pcie_write_reg(dev->phy, QSERDES_COM_SYS_CLK_CTRL, 0x06); + ep_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1E); + ep_pcie_write_reg(dev->phy, QSERDES_COM_CP_CTRL_MODE0, 0x3F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_RCTRL_MODE0, 0x1A); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CCTRL_MODE0, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x03); + ep_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0xFF); + + /* TX settings */ + ep_pcie_write_reg(dev->phy, QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, + 0x45); + ep_pcie_write_reg(dev->phy, QSERDES_TX_LANE_MODE, 0x06); + ep_pcie_write_reg(dev->phy, QSERDES_TX_RES_CODE_LANE_OFFSET, 0x02); + ep_pcie_write_reg(dev->phy, QSERDES_TX_RCV_DETECT_LVL_2, 0x12); + + /* RX settings */ + ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_ENABLES, 0x1C); + ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xDB); + ep_pcie_write_reg(dev->phy, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, + 0x4B); + ep_pcie_write_reg(dev->phy, QSERDES_RX_UCDR_SO_GAIN, 0x04); + ep_pcie_write_reg(dev->phy, QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04); + + /* EP_REF_CLK settings */ + ep_pcie_write_reg(dev->phy, QSERDES_COM_CLK_EP_DIV, 0x19); + ep_pcie_write_reg(dev->phy, PCIE_PHY_ENDPOINT_REFCLK_DRIVE, 0x00); + + /* PCIE L1SS settings */ + ep_pcie_write_reg(dev->phy, PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x40); + ep_pcie_write_reg(dev->phy, PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, + 0x00); + ep_pcie_write_reg(dev->phy, PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, + 0x40); + ep_pcie_write_reg(dev->phy, PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, + 0x00); + ep_pcie_write_reg(dev->phy, PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK, 0x40); + ep_pcie_write_reg(dev->phy, PCIE_PHY_PLL_LOCK_CHK_DLY_TIME, 0x73); + + /* PCS settings */ + ep_pcie_write_reg(dev->phy, PCIE_PHY_SIGDET_CNTRL, 0x07); + ep_pcie_write_reg(dev->phy, PCIE_PHY_RX_SIGDET_LVL, 0x99); + ep_pcie_write_reg(dev->phy, PCIE_PHY_TXDEEMPH_M6DB_V0, 0x15); + ep_pcie_write_reg(dev->phy, PCIE_PHY_TXDEEMPH_M3P5DB_V0, 0x0E); + + ep_pcie_write_reg(dev->phy, PCIE_PHY_SW_RESET, 0x00); + ep_pcie_write_reg(dev->phy, PCIE_PHY_START_CONTROL, 0x03); +} + +bool ep_pcie_phy_is_ready(struct ep_pcie_dev_t *dev) +{ + u32 offset; + + if (dev->phy_status_reg) + offset = dev->phy_status_reg; + else + offset = PCIE_PHY_PCS_STATUS; + + if (readl_relaxed(dev->phy + offset) & dev->phy_status_bit_mask_bit) + return false; + else + return true; +} diff --git a/drivers/platform/msm/ep_pcie/ep_pcie_phy.h b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h new file mode 100644 index 000000000000..b9bc3a297427 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h @@ -0,0 +1,454 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2015, 2017, 2020 The Linux Foundation. All rights reserved.*/ + +#ifndef __EP_PCIE_PHY_H +#define __EP_PCIE_PHY_H + +#define QSERDES_COM_ATB_SEL1 0x000 +#define QSERDES_COM_ATB_SEL2 0x004 +#define QSERDES_COM_FREQ_UPDATE 0x008 +#define QSERDES_COM_BG_TIMER 0x00C +#define QSERDES_COM_SSC_EN_CENTER 0x010 +#define QSERDES_COM_SSC_ADJ_PER1 0x014 +#define QSERDES_COM_SSC_ADJ_PER2 0x018 +#define QSERDES_COM_SSC_PER1 0x01C +#define QSERDES_COM_SSC_PER2 0x020 +#define QSERDES_COM_SSC_STEP_SIZE1 0x024 +#define QSERDES_COM_SSC_STEP_SIZE2 0x028 +#define QSERDES_COM_POST_DIV 0x02C +#define QSERDES_COM_POST_DIV_MUX 0x030 +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x034 +#define QSERDES_COM_CLK_ENABLE1 0x038 +#define QSERDES_COM_SYS_CLK_CTRL 0x03C +#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x040 +#define QSERDES_COM_PLL_EN 0x044 +#define QSERDES_COM_PLL_IVCO 0x048 +#define QSERDES_COM_LOCK_CMP1_MODE0 0x04C +#define QSERDES_COM_LOCK_CMP2_MODE0 0x050 +#define QSERDES_COM_LOCK_CMP3_MODE0 0x054 +#define QSERDES_COM_LOCK_CMP1_MODE1 0x058 +#define QSERDES_COM_LOCK_CMP2_MODE1 0x05C +#define QSERDES_COM_LOCK_CMP3_MODE1 0x060 +#define QSERDES_COM_CMN_RSVD0 0x064 +#define QSERDES_COM_EP_CLOCK_DETECT_CTRL 0x068 +#define QSERDES_COM_SYSCLK_DET_COMP_STATUS 0x06C +#define QSERDES_COM_BG_TRIM 0x070 +#define QSERDES_COM_CLK_EP_DIV 0x074 +#define QSERDES_COM_CP_CTRL_MODE0 0x078 +#define QSERDES_COM_CP_CTRL_MODE1 0x07C +#define QSERDES_COM_CMN_RSVD1 0x080 +#define QSERDES_COM_PLL_RCTRL_MODE0 0x084 +#define QSERDES_COM_PLL_RCTRL_MODE1 0x088 +#define QSERDES_COM_CMN_RSVD2 0x08C +#define QSERDES_COM_PLL_CCTRL_MODE0 0x090 +#define QSERDES_COM_PLL_CCTRL_MODE1 0x094 +#define QSERDES_COM_CMN_RSVD3 0x098 +#define QSERDES_COM_PLL_CNTRL 0x09C +#define QSERDES_COM_PHASE_SEL_CTRL 0x0A0 +#define QSERDES_COM_PHASE_SEL_DC 0x0A4 +#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x0A8 +#define QSERDES_COM_SYSCLK_EN_SEL 0x0AC +#define QSERDES_COM_CML_SYSCLK_SEL 0x0B0 +#define QSERDES_COM_RESETSM_CNTRL 0x0B4 +#define QSERDES_COM_RESETSM_CNTRL2 0x0B8 +#define QSERDES_COM_RESTRIM_CTRL 0x0BC +#define QSERDES_COM_RESTRIM_CTRL2 0x0C0 +#define QSERDES_COM_RESCODE_DIV_NUM 0x0C4 +#define QSERDES_COM_LOCK_CMP_EN 0x0C8 +#define QSERDES_COM_LOCK_CMP_CFG 0x0CC +#define QSERDES_COM_DEC_START_MODE0 0x0D0 +#define QSERDES_COM_DEC_START_MODE1 0x0D4 +#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x0D8 +#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x0DC +#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x0E0 +#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x0E4 +#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x0E8 +#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x0EC +#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x0F0 +#define QSERDES_COM_VCO_TUNE_MINVAL1 0x0F4 +#define QSERDES_COM_VCO_TUNE_MINVAL2 0x0F8 +#define QSERDES_COM_CMN_RSVD4 0x0FC +#define QSERDES_COM_INTEGLOOP_INITVAL 0x100 +#define QSERDES_COM_INTEGLOOP_EN 0x104 +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x108 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x10C +#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x110 +#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x114 +#define QSERDES_COM_VCO_TUNE_MAXVAL1 0x118 +#define QSERDES_COM_VCO_TUNE_MAXVAL2 0x11C +#define QSERDES_COM_RES_TRIM_CONTROL2 0x120 +#define QSERDES_COM_VCO_TUNE_CTRL 0x124 +#define QSERDES_COM_VCO_TUNE_MAP 0x128 +#define QSERDES_COM_VCO_TUNE1_MODE0 0x12C +#define QSERDES_COM_VCO_TUNE2_MODE0 0x130 +#define QSERDES_COM_VCO_TUNE1_MODE1 0x134 +#define QSERDES_COM_VCO_TUNE2_MODE1 0x138 +#define QSERDES_COM_VCO_TUNE_INITVAL1 0x13C +#define QSERDES_COM_VCO_TUNE_INITVAL2 0x140 +#define QSERDES_COM_VCO_TUNE_TIMER1 0x144 +#define QSERDES_COM_VCO_TUNE_TIMER2 0x148 +#define QSERDES_COM_SAR 0x14C +#define QSERDES_COM_SAR_CLK 0x150 +#define QSERDES_COM_SAR_CODE_OUT_STATUS 0x154 +#define QSERDES_COM_SAR_CODE_READY_STATUS 0x158 +#define QSERDES_COM_CMN_STATUS 0x15C +#define QSERDES_COM_RESET_SM_STATUS 0x160 +#define QSERDES_COM_RESTRIM_CODE_STATUS 0x164 +#define QSERDES_COM_PLLCAL_CODE1_STATUS 0x168 +#define QSERDES_COM_PLLCAL_CODE2_STATUS 0x16C +#define QSERDES_COM_BG_CTRL 0x170 +#define QSERDES_COM_CLK_SELECT 0x174 +#define QSERDES_COM_HSCLK_SEL 0x178 +#define QSERDES_COM_PLL_ANALOG 0x180 +#define QSERDES_COM_CORECLK_DIV 0x184 +#define QSERDES_COM_SW_RESET 0x188 +#define QSERDES_COM_CORE_CLK_EN 0x18C +#define QSERDES_COM_C_READY_STATUS 0x190 +#define QSERDES_COM_CMN_CONFIG 0x194 +#define QSERDES_COM_CMN_RATE_OVERRIDE 0x198 +#define QSERDES_COM_SVS_MODE_CLK_SEL 0x19C +#define QSERDES_COM_DEBUG_BUS0 0x1A0 +#define QSERDES_COM_DEBUG_BUS1 0x1A4 +#define QSERDES_COM_DEBUG_BUS2 0x1A8 +#define QSERDES_COM_DEBUG_BUS3 0x1AC +#define QSERDES_COM_DEBUG_BUS_SEL 0x1B0 +#define QSERDES_COM_CMN_MISC1 0x1B4 +#define QSERDES_COM_CMN_MISC2 0x1B8 +#define QSERDES_COM_CORECLK_DIV_MODE1 0x1BC +#define QSERDES_COM_CMN_RSVD5 0x1C0 +#define QSERDES_TX_BIST_MODE_LANENO 0x200 +#define QSERDES_TX_BIST_INVERT 0x204 +#define QSERDES_TX_CLKBUF_ENABLE 0x208 +#define QSERDES_TX_CMN_CONTROL_ONE 0x20C +#define QSERDES_TX_CMN_CONTROL_TWO 0x210 +#define QSERDES_TX_CMN_CONTROL_THREE 0x214 +#define QSERDES_TX_TX_EMP_POST1_LVL 0x218 +#define QSERDES_TX_TX_POST2_EMPH 0x21C +#define QSERDES_TX_TX_BOOST_LVL_UP_DN 0x220 +#define QSERDES_TX_HP_PD_ENABLES 0x224 +#define QSERDES_TX_TX_IDLE_LVL_LARGE_AMP 0x228 +#define QSERDES_TX_TX_DRV_LVL 0x22C +#define QSERDES_TX_TX_DRV_LVL_OFFSET 0x230 +#define QSERDES_TX_RESET_TSYNC_EN 0x234 +#define QSERDES_TX_PRE_STALL_LDO_BOOST_EN 0x238 +#define QSERDES_TX_TX_BAND 0x23C +#define QSERDES_TX_SLEW_CNTL 0x240 +#define QSERDES_TX_INTERFACE_SELECT 0x244 +#define QSERDES_TX_LPB_EN 0x248 +#define QSERDES_TX_RES_CODE_LANE_TX 0x24C +#define QSERDES_TX_RES_CODE_LANE_RX 0x250 +#define QSERDES_TX_RES_CODE_LANE_OFFSET 0x254 +#define QSERDES_TX_PERL_LENGTH1 0x258 +#define QSERDES_TX_PERL_LENGTH2 0x25C +#define QSERDES_TX_SERDES_BYP_EN_OUT 0x260 +#define QSERDES_TX_DEBUG_BUS_SEL 0x264 +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x268 +#define QSERDES_TX_TX_POL_INV 0x26C +#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x270 +#define QSERDES_TX_BIST_PATTERN1 0x274 +#define QSERDES_TX_BIST_PATTERN2 0x278 +#define QSERDES_TX_BIST_PATTERN3 0x27C +#define QSERDES_TX_BIST_PATTERN4 0x280 +#define QSERDES_TX_BIST_PATTERN5 0x284 +#define QSERDES_TX_BIST_PATTERN6 0x288 +#define QSERDES_TX_BIST_PATTERN7 0x28C +#define QSERDES_TX_BIST_PATTERN8 0x290 +#define QSERDES_TX_LANE_MODE 0x294 +#define QSERDES_TX_IDAC_CAL_LANE_MODE 0x298 +#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x29C +#define QSERDES_TX_ATB_SEL1 0x2A0 +#define QSERDES_TX_ATB_SEL2 0x2A4 +#define QSERDES_TX_RCV_DETECT_LVL 0x2A8 +#define QSERDES_TX_RCV_DETECT_LVL_2 0x2AC +#define QSERDES_TX_PRBS_SEED1 0x2B0 +#define QSERDES_TX_PRBS_SEED2 0x2B4 +#define QSERDES_TX_PRBS_SEED3 0x2B8 +#define QSERDES_TX_PRBS_SEED4 0x2BC +#define QSERDES_TX_RESET_GEN 0x2C0 +#define QSERDES_TX_RESET_GEN_MUXES 0x2C4 +#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x2C8 +#define QSERDES_TX_TX_INTERFACE_MODE 0x2CC +#define QSERDES_TX_PWM_CTRL 0x2D0 +#define QSERDES_TX_PWM_ENCODED_OR_DATA 0x2D4 +#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND2 0x2D8 +#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND2 0x2DC +#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND2 0x2E0 +#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND2 0x2E4 +#define QSERDES_TX_PWM_GEAR_1_DIVIDER_BAND0_1 0x2E8 +#define QSERDES_TX_PWM_GEAR_2_DIVIDER_BAND0_1 0x2EC +#define QSERDES_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x2F0 +#define QSERDES_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x2F4 +#define QSERDES_TX_VMODE_CTRL1 0x2F8 +#define QSERDES_TX_VMODE_CTRL2 0x2FC +#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL 0x300 +#define QSERDES_TX_BIST_STATUS 0x304 +#define QSERDES_TX_BIST_ERROR_COUNT1 0x308 +#define QSERDES_TX_BIST_ERROR_COUNT2 0x30C +#define QSERDES_TX_TX_ALOG_INTF_OBSV 0x310 +#define QSERDES_RX_UCDR_FO_GAIN_HALF 0x400 +#define QSERDES_RX_UCDR_FO_GAIN_QUARTER 0x404 +#define QSERDES_RX_UCDR_FO_GAIN_EIGHTH 0x408 +#define QSERDES_RX_UCDR_FO_GAIN 0x40C +#define QSERDES_RX_UCDR_SO_GAIN_HALF 0x410 +#define QSERDES_RX_UCDR_SO_GAIN_QUARTER 0x414 +#define QSERDES_RX_UCDR_SO_GAIN_EIGHTH 0x418 +#define QSERDES_RX_UCDR_SO_GAIN 0x41C +#define QSERDES_RX_UCDR_SVS_FO_GAIN_HALF 0x420 +#define QSERDES_RX_UCDR_SVS_FO_GAIN_QUARTER 0x424 +#define QSERDES_RX_UCDR_SVS_FO_GAIN_EIGHTH 0x428 +#define QSERDES_RX_UCDR_SVS_FO_GAIN 0x42C +#define QSERDES_RX_UCDR_SVS_SO_GAIN_HALF 0x430 +#define QSERDES_RX_UCDR_SVS_SO_GAIN_QUARTER 0x434 +#define QSERDES_RX_UCDR_SVS_SO_GAIN_EIGHTH 0x438 +#define QSERDES_RX_UCDR_SVS_SO_GAIN 0x43C +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN 0x440 +#define QSERDES_RX_UCDR_FD_GAIN 0x444 +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x448 +#define QSERDES_RX_UCDR_FO_TO_SO_DELAY 0x44C +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW 0x450 +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH 0x454 +#define QSERDES_RX_UCDR_MODULATE 0x458 +#define QSERDES_RX_UCDR_PI_CONTROLS 0x45C +#define QSERDES_RX_RBIST_CONTROL 0x460 +#define QSERDES_RX_AUX_CONTROL 0x464 +#define QSERDES_RX_AUX_DATA_TCOARSE 0x468 +#define QSERDES_RX_AUX_DATA_TFINE_LSB 0x46C +#define QSERDES_RX_AUX_DATA_TFINE_MSB 0x470 +#define QSERDES_RX_RCLK_AUXDATA_SEL 0x474 +#define QSERDES_RX_AC_JTAG_ENABLE 0x478 +#define QSERDES_RX_AC_JTAG_INITP 0x47C +#define QSERDES_RX_AC_JTAG_INITN 0x480 +#define QSERDES_RX_AC_JTAG_LVL 0x484 +#define QSERDES_RX_AC_JTAG_MODE 0x488 +#define QSERDES_RX_AC_JTAG_RESET 0x48C +#define QSERDES_RX_RX_TERM_BW 0x490 +#define QSERDES_RX_RX_RCVR_IQ_EN 0x494 +#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS 0x498 +#define QSERDES_RX_RX_IDAC_IBAR_DC_OFFSETS 0x49C +#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS 0x4A0 +#define QSERDES_RX_RX_IDAC_QBAR_DC_OFFSETS 0x4A4 +#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS 0x4A8 +#define QSERDES_RX_RX_IDAC_ABAR_DC_OFFSETS 0x4AC +#define QSERDES_RX_RX_IDAC_EN 0x4B0 +#define QSERDES_RX_RX_IDAC_ENABLES 0x4B4 +#define QSERDES_RX_RX_IDAC_SIGN 0x4B8 +#define QSERDES_RX_RX_HIGHZ_HIGHRATE 0x4BC +#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x4C0 +#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x4C4 +#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x4C8 +#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x4CC +#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x4D0 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x4D4 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x4D8 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x4DC +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x4E0 +#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION 0x4E4 +#define QSERDES_RX_RX_IDAC_TSETTLE_LOW 0x4E8 +#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH 0x4EC +#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW 0x4F0 +#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH 0x4F4 +#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW 0x4F8 +#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH 0x4FC +#define QSERDES_RX_RX_EQ_OFFSET_LSB 0x500 +#define QSERDES_RX_RX_EQ_OFFSET_MSB 0x504 +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x508 +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x50C +#define QSERDES_RX_SIGDET_ENABLES 0x510 +#define QSERDES_RX_SIGDET_CNTRL 0x514 +#define QSERDES_RX_SIGDET_LVL 0x518 +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x51C +#define QSERDES_RX_RX_BAND 0x520 +#define QSERDES_RX_CDR_FREEZE_UP_DN 0x524 +#define QSERDES_RX_CDR_RESET_OVERRIDE 0x528 +#define QSERDES_RX_RX_INTERFACE_MODE 0x52C +#define QSERDES_RX_JITTER_GEN_MODE 0x530 +#define QSERDES_RX_BUJ_AMP 0x534 +#define QSERDES_RX_SJ_AMP1 0x538 +#define QSERDES_RX_SJ_AMP2 0x53C +#define QSERDES_RX_SJ_PER1 0x540 +#define QSERDES_RX_SJ_PER2 0x544 +#define QSERDES_RX_BUJ_STEP_FREQ1 0x548 +#define QSERDES_RX_BUJ_STEP_FREQ2 0x54C +#define QSERDES_RX_PPM_OFFSET1 0x550 +#define QSERDES_RX_PPM_OFFSET2 0x554 +#define QSERDES_RX_SIGN_PPM_PERIOD1 0x558 +#define QSERDES_RX_SIGN_PPM_PERIOD2 0x55C +#define QSERDES_RX_SSC_CTRL 0x560 +#define QSERDES_RX_SSC_COUNT1 0x564 +#define QSERDES_RX_SSC_COUNT2 0x568 +#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL 0x56C +#define QSERDES_RX_RX_PWM_ENABLE_AND_DATA 0x570 +#define QSERDES_RX_RX_PWM_GEAR1_TIMEOUT_COUNT 0x574 +#define QSERDES_RX_RX_PWM_GEAR2_TIMEOUT_COUNT 0x578 +#define QSERDES_RX_RX_PWM_GEAR3_TIMEOUT_COUNT 0x57C +#define QSERDES_RX_RX_PWM_GEAR4_TIMEOUT_COUNT 0x580 +#define QSERDES_RX_PI_CTRL1 0x584 +#define QSERDES_RX_PI_CTRL2 0x588 +#define QSERDES_RX_PI_QUAD 0x58C +#define QSERDES_RX_IDATA1 0x590 +#define QSERDES_RX_IDATA2 0x594 +#define QSERDES_RX_AUX_DATA1 0x598 +#define QSERDES_RX_AUX_DATA2 0x59C +#define QSERDES_RX_AC_JTAG_OUTP 0x5A0 +#define QSERDES_RX_AC_JTAG_OUTN 0x5A4 +#define QSERDES_RX_RX_SIGDET 0x5A8 +#define QSERDES_RX_RX_VDCOFF 0x5AC +#define QSERDES_RX_IDAC_CAL_ON 0x5B0 +#define QSERDES_RX_IDAC_STATUS_I 0x5B4 +#define QSERDES_RX_IDAC_STATUS_IBAR 0x5B8 +#define QSERDES_RX_IDAC_STATUS_Q 0x5BC +#define QSERDES_RX_IDAC_STATUS_QBAR 0x5C0 +#define QSERDES_RX_IDAC_STATUS_A 0x5C4 +#define QSERDES_RX_IDAC_STATUS_ABAR 0x5C8 +#define QSERDES_RX_CALST_STATUS_I 0x5CC +#define QSERDES_RX_CALST_STATUS_Q 0x5D0 +#define QSERDES_RX_CALST_STATUS_A 0x5D4 +#define QSERDES_RX_RX_ALOG_INTF_OBSV 0x5D8 +#define QSERDES_RX_READ_EQCODE 0x5DC +#define QSERDES_RX_READ_OFFSETCODE 0x5E0 +#define QSERDES_RX_IA_ERROR_COUNTER_LOW 0x5E4 +#define QSERDES_RX_IA_ERROR_COUNTER_HIGH 0x5E8 +#define PCIE_PHY_MISC_DEBUG_BUS_BYTE0_INDEX 0x600 +#define PCIE_PHY_MISC_DEBUG_BUS_BYTE1_INDEX 0x604 +#define PCIE_PHY_MISC_DEBUG_BUS_BYTE2_INDEX 0x608 +#define PCIE_PHY_MISC_DEBUG_BUS_BYTE3_INDEX 0x60C +#define PCIE_PHY_MISC_PLACEHOLDER_STATUS 0x610 +#define PCIE_PHY_MISC_DEBUG_BUS_0_STATUS 0x614 +#define PCIE_PHY_MISC_DEBUG_BUS_1_STATUS 0x618 +#define PCIE_PHY_MISC_DEBUG_BUS_2_STATUS 0x61C +#define PCIE_PHY_MISC_DEBUG_BUS_3_STATUS 0x620 +#define PCIE_PHY_MISC_OSC_DTCT_STATUS 0x624 +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG1 0x628 +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG2 0x62C +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG3 0x630 +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG4 0x634 +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG5 0x638 +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG6 0x63C +#define PCIE_PHY_MISC_OSC_DTCT_CONFIG7 0x640 +#define PCIE_PHY_SW_RESET 0x800 +#define PCIE_PHY_POWER_DOWN_CONTROL 0x804 +#define PCIE_PHY_START_CONTROL 0x808 +#define PCIE_PHY_TXMGN_V0 0x80C +#define PCIE_PHY_TXMGN_V1 0x810 +#define PCIE_PHY_TXMGN_V2 0x814 +#define PCIE_PHY_TXMGN_V3 0x818 +#define PCIE_PHY_TXMGN_V4 0x81C +#define PCIE_PHY_TXMGN_LS 0x820 +#define PCIE_PHY_TXDEEMPH_M6DB_V0 0x824 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V0 0x828 +#define PCIE_PHY_TXDEEMPH_M6DB_V1 0x82C +#define PCIE_PHY_TXDEEMPH_M3P5DB_V1 0x830 +#define PCIE_PHY_TXDEEMPH_M6DB_V2 0x834 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V2 0x838 +#define PCIE_PHY_TXDEEMPH_M6DB_V3 0x83C +#define PCIE_PHY_TXDEEMPH_M3P5DB_V3 0x840 +#define PCIE_PHY_TXDEEMPH_M6DB_V4 0x844 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V4 0x848 +#define PCIE_PHY_TXDEEMPH_M6DB_LS 0x84C +#define PCIE_PHY_TXDEEMPH_M3P5DB_LS 0x850 +#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE 0x854 +#define PCIE_PHY_RX_IDLE_DTCT_CNTRL 0x858 +#define PCIE_PHY_RATE_SLEW_CNTRL 0x85C +#define PCIE_PHY_POWER_STATE_CONFIG1 0x860 +#define PCIE_PHY_POWER_STATE_CONFIG2 0x864 +#define PCIE_PHY_POWER_STATE_CONFIG3 0x868 +#define PCIE_PHY_POWER_STATE_CONFIG4 0x86C +#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_L 0x870 +#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_H 0x874 +#define PCIE_PHY_RCVR_DTCT_DLY_U3_L 0x878 +#define PCIE_PHY_RCVR_DTCT_DLY_U3_H 0x87C +#define PCIE_PHY_LOCK_DETECT_CONFIG1 0x880 +#define PCIE_PHY_LOCK_DETECT_CONFIG2 0x884 +#define PCIE_PHY_LOCK_DETECT_CONFIG3 0x888 +#define PCIE_PHY_TSYNC_RSYNC_TIME 0x88C +#define PCIE_PHY_SIGDET_LOW_2_IDLE_TIME 0x890 +#define PCIE_PHY_BEACON_2_IDLE_TIME_L 0x894 +#define PCIE_PHY_BEACON_2_IDLE_TIME_H 0x898 +#define PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK 0x89C +#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK 0x8A0 +#define PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK 0x8A4 +#define PCIE_PHY_PLL_LOCK_CHK_DLY_TIME 0x8A8 +#define PCIE_PHY_LFPS_DET_HIGH_COUNT_VAL 0x8AC +#define PCIE_PHY_LFPS_TX_ECSTART_EQTLOCK 0x8B0 +#define PCIE_PHY_LFPS_TX_END_CNT_P2U3_START 0x8B4 +#define PCIE_PHY_RXEQTRAINING_WAIT_TIME 0x8B8 +#define PCIE_PHY_RXEQTRAINING_RUN_TIME 0x8BC +#define PCIE_PHY_TXONESZEROS_RUN_LENGTH 0x8C0 +#define PCIE_PHY_FLL_CNTRL1 0x8C4 +#define PCIE_PHY_FLL_CNTRL2 0x8C8 +#define PCIE_PHY_FLL_CNT_VAL_L 0x8CC +#define PCIE_PHY_FLL_CNT_VAL_H_TOL 0x8D0 +#define PCIE_PHY_FLL_MAN_CODE 0x8D4 +#define PCIE_PHY_AUTONOMOUS_MODE_CTRL 0x8D8 +#define PCIE_PHY_LFPS_RXTERM_IRQ_CLEAR 0x8DC +#define PCIE_PHY_ARCVR_DTCT_EN_PERIOD 0x8E0 +#define PCIE_PHY_ARCVR_DTCT_CM_DLY 0x8E4 +#define PCIE_PHY_ALFPS_DEGLITCH_VAL 0x8E8 +#define PCIE_PHY_INSIG_SW_CTRL1 0x8EC +#define PCIE_PHY_INSIG_SW_CTRL2 0x8F0 +#define PCIE_PHY_INSIG_SW_CTRL3 0x8F4 +#define PCIE_PHY_INSIG_MX_CTRL1 0x8F8 +#define PCIE_PHY_INSIG_MX_CTRL2 0x8FC +#define PCIE_PHY_INSIG_MX_CTRL3 0x900 +#define PCIE_PHY_OUTSIG_SW_CTRL1 0x904 +#define PCIE_PHY_OUTSIG_MX_CTRL1 0x908 +#define PCIE_PHY_CLK_DEBUG_BYPASS_CTRL 0x90C +#define PCIE_PHY_TEST_CONTROL 0x910 +#define PCIE_PHY_TEST_CONTROL2 0x914 +#define PCIE_PHY_TEST_CONTROL3 0x918 +#define PCIE_PHY_TEST_CONTROL4 0x91C +#define PCIE_PHY_TEST_CONTROL5 0x920 +#define PCIE_PHY_TEST_CONTROL6 0x924 +#define PCIE_PHY_TEST_CONTROL7 0x928 +#define PCIE_PHY_COM_RESET_CONTROL 0x92C +#define PCIE_PHY_BIST_CTRL 0x930 +#define PCIE_PHY_PRBS_POLY0 0x934 +#define PCIE_PHY_PRBS_POLY1 0x938 +#define PCIE_PHY_PRBS_SEED0 0x93C +#define PCIE_PHY_PRBS_SEED1 0x940 +#define PCIE_PHY_FIXED_PAT_CTRL 0x944 +#define PCIE_PHY_FIXED_PAT0 0x948 +#define PCIE_PHY_FIXED_PAT1 0x94C +#define PCIE_PHY_FIXED_PAT2 0x950 +#define PCIE_PHY_FIXED_PAT3 0x954 +#define PCIE_PHY_COM_CLK_SWITCH_CTRL 0x958 +#define PCIE_PHY_ELECIDLE_DLY_SEL 0x95C +#define PCIE_PHY_SPARE1 0x960 +#define PCIE_PHY_BIST_CHK_ERR_CNT_L_STATUS 0x964 +#define PCIE_PHY_BIST_CHK_ERR_CNT_H_STATUS 0x968 +#define PCIE_PHY_BIST_CHK_STATUS 0x96C +#define PCIE_PHY_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x970 +#define PCIE_PHY_PCS_STATUS 0x974 +#define PCIE_PHY_PCS_STATUS2 0x978 +#define PCIE_PHY_PCS_STATUS3 0x97C +#define PCIE_PHY_COM_RESET_STATUS 0x980 +#define PCIE_PHY_OSC_DTCT_STATUS 0x984 +#define PCIE_PHY_REVISION_ID0 0x988 +#define PCIE_PHY_REVISION_ID1 0x98C +#define PCIE_PHY_REVISION_ID2 0x990 +#define PCIE_PHY_REVISION_ID3 0x994 +#define PCIE_PHY_DEBUG_BUS_0_STATUS 0x998 +#define PCIE_PHY_DEBUG_BUS_1_STATUS 0x99C +#define PCIE_PHY_DEBUG_BUS_2_STATUS 0x9A0 +#define PCIE_PHY_DEBUG_BUS_3_STATUS 0x9A4 +#define PCIE_PHY_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x9A8 +#define PCIE_PHY_OSC_DTCT_ACTIONS 0x9AC +#define PCIE_PHY_SIGDET_CNTRL 0x9B0 +#define PCIE_PHY_IDAC_CAL_CNTRL 0x9B4 +#define PCIE_PHY_CMN_ACK_OUT_SEL 0x9B8 +#define PCIE_PHY_PLL_LOCK_CHK_DLY_TIME_SYSCLK 0x9BC +#define PCIE_PHY_AUTONOMOUS_MODE_STATUS 0x9C0 +#define PCIE_PHY_ENDPOINT_REFCLK_CNTRL 0x9C4 +#define PCIE_PHY_EPCLK_PRE_PLL_LOCK_DLY_SYSCLK 0x9C8 +#define PCIE_PHY_EPCLK_PRE_PLL_LOCK_DLY_AUXCLK 0x9CC +#define PCIE_PHY_EPCLK_DLY_COUNT_VAL_L 0x9D0 +#define PCIE_PHY_EPCLK_DLY_COUNT_VAL_H 0x9D4 +#define PCIE_PHY_RX_SIGDET_LVL 0x9D8 +#define PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x9DC +#define PCIE_PHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x9E0 +#define PCIE_PHY_AUTONOMOUS_MODE_CTRL2 0x9E4 +#define PCIE_PHY_RXTERMINATION_DLY_SEL 0x9E8 +#define PCIE_PHY_LFPS_PER_TIMER_VAL 0x9EC +#define PCIE_PHY_SIGDET_STARTUP_TIMER_VAL 0x9F0 +#define PCIE_PHY_LOCK_DETECT_CONFIG4 0x9F4 +#endif diff --git a/include/linux/msm_ep_pcie.h b/include/linux/msm_ep_pcie.h new file mode 100644 index 000000000000..d98ed7b13984 --- /dev/null +++ b/include/linux/msm_ep_pcie.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2015, 2017, 2019-2020, 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 + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MSM_EP_PCIE_H +#define __MSM_EP_PCIE_H + +#include + +enum ep_pcie_link_status { + EP_PCIE_LINK_DISABLED, + EP_PCIE_LINK_UP, + EP_PCIE_LINK_ENABLED, +}; + +enum ep_pcie_event { + EP_PCIE_EVENT_INVALID = 0, + EP_PCIE_EVENT_PM_D0 = 0x1, + EP_PCIE_EVENT_PM_D3_HOT = 0x2, + EP_PCIE_EVENT_PM_D3_COLD = 0x4, + EP_PCIE_EVENT_PM_RST_DEAST = 0x8, + EP_PCIE_EVENT_LINKDOWN = 0x10, + EP_PCIE_EVENT_LINKUP = 0x20, + EP_PCIE_EVENT_MHI_A7 = 0x40, + EP_PCIE_EVENT_MMIO_WRITE = 0x80, + EP_PCIE_EVENT_L1SUB_TIMEOUT = 0x100, + EP_PCIE_EVENT_L1SUB_TIMEOUT_EXIT = 0x200, +}; + +enum ep_pcie_irq_event { + EP_PCIE_INT_EVT_LINK_DOWN = 1, + EP_PCIE_INT_EVT_BME, + EP_PCIE_INT_EVT_PM_TURNOFF, + EP_PCIE_INT_EVT_DEBUG, + EP_PCIE_INT_EVT_LTR, + EP_PCIE_INT_EVT_MHI_Q6, + EP_PCIE_INT_EVT_MHI_A7, + EP_PCIE_INT_EVT_DSTATE_CHANGE, + EP_PCIE_INT_EVT_L1SUB_TIMEOUT, + EP_PCIE_INT_EVT_MMIO_WRITE, + EP_PCIE_INT_EVT_CFG_WRITE, + EP_PCIE_INT_EVT_BRIDGE_FLUSH_N, + EP_PCIE_INT_EVT_LINK_UP, + EP_PCIE_INT_EVT_EDMA = 22, + EP_PCIE_INT_EVT_MAX = 13, +}; + +enum ep_pcie_trigger { + EP_PCIE_TRIGGER_CALLBACK, + EP_PCIE_TRIGGER_COMPLETION, +}; + +enum ep_pcie_options { + EP_PCIE_OPT_NULL = 0, + EP_PCIE_OPT_AST_WAKE = 0x1, + EP_PCIE_OPT_POWER_ON = 0x2, + EP_PCIE_OPT_ENUM = 0x4, + EP_PCIE_OPT_ENUM_ASYNC = 0x8, + EP_PCIE_OPT_ALL = 0xFFFFFFFF, +}; + +struct ep_pcie_notify { + enum ep_pcie_event event; + void *user; + void *data; + u32 options; +}; + +struct ep_pcie_register_event { + u32 events; + void *user; + enum ep_pcie_trigger mode; + void (*callback)(struct ep_pcie_notify *notify); + struct ep_pcie_notify notify; + struct completion *completion; + u32 options; +}; + +struct ep_pcie_iatu { + u32 start; + u32 end; + u32 tgt_lower; + u32 tgt_upper; +}; + +struct ep_pcie_msi_config { + u32 lower; + u32 upper; + u32 data; + u32 msg_num; +}; + +struct ep_pcie_db_config { + u8 base; + u8 end; + u32 tgt_addr; +}; + +struct ep_pcie_inactivity { + bool enable; + uint32_t timer_us; +}; + +struct ep_pcie_hw { + struct list_head node; + u32 device_id; + void **private_data; + int (*register_event)(struct ep_pcie_register_event *reg); + int (*deregister_event)(void); + enum ep_pcie_link_status (*get_linkstatus)(void); + int (*config_outbound_iatu)(struct ep_pcie_iatu entries[], + u32 num_entries); + int (*get_msi_config)(struct ep_pcie_msi_config *cfg); + int (*trigger_msi)(u32 idx); + int (*wakeup_host)(enum ep_pcie_event event); + int (*enable_endpoint)(enum ep_pcie_options opt); + int (*disable_endpoint)(void); + int (*config_db_routing)(struct ep_pcie_db_config chdb_cfg, + struct ep_pcie_db_config erdb_cfg); + int (*mask_irq_event)(enum ep_pcie_irq_event event, + bool enable); + int (*configure_inactivity_timer)(struct ep_pcie_inactivity *param); +}; + +/* + * ep_pcie_register_drv - register HW driver. + * @phandle: PCIe endpoint HW driver handle + * + * This function registers PCIe HW driver to PCIe endpoint service + * layer. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_register_drv(struct ep_pcie_hw *phandle); + +/* + * ep_pcie_deregister_drv - deregister HW driver. + * @phandle: PCIe endpoint HW driver handle + * + * This function deregisters PCIe HW driver to PCIe endpoint service + * layer. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_deregister_drv(struct ep_pcie_hw *phandle); + +/* + * ep_pcie_get_phandle - get PCIe endpoint HW driver handle. + * @id: PCIe endpoint device ID + * + * This function deregisters PCIe HW driver from PCIe endpoint service + * layer. + * + * Return: PCIe endpoint HW driver handle + */ +struct ep_pcie_hw *ep_pcie_get_phandle(u32 id); + +/* + * ep_pcie_register_event - register event with PCIe driver. + * @phandle: PCIe endpoint HW driver handle + * @reg: event structure + * + * This function gives PCIe client driver an option to register + * event with PCIe driver. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_register_event(struct ep_pcie_hw *phandle, + struct ep_pcie_register_event *reg); + +/* + * ep_pcie_deregister_event - deregister event with PCIe driver. + * @phandle: PCIe endpoint HW driver handle + * + * This function gives PCIe client driver an option to deregister + * existing event with PCIe driver. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_deregister_event(struct ep_pcie_hw *phandle); + +/* + * ep_pcie_get_linkstatus - indicate the status of PCIe link. + * @phandle: PCIe endpoint HW driver handle + * + * This function tells PCIe client about the status of PCIe link. + * + * Return: status of PCIe link + */ +enum ep_pcie_link_status ep_pcie_get_linkstatus(struct ep_pcie_hw *phandle); + +/* + * ep_pcie_config_outbound_iatu - configure outbound iATU. + * @entries: iatu entries + * @num_entries: number of iatu entries + * + * This function configures the outbound iATU for PCIe + * client's access to the regions in the host memory which + * are specified by the SW on host side. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_config_outbound_iatu(struct ep_pcie_hw *phandle, + struct ep_pcie_iatu entries[], + u32 num_entries); + +/* + * ep_pcie_get_msi_config - get MSI config info. + * @phandle: PCIe endpoint HW driver handle + * @cfg: pointer to MSI config + * + * This function returns MSI config info. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_get_msi_config(struct ep_pcie_hw *phandle, + struct ep_pcie_msi_config *cfg); + +/* + * ep_pcie_trigger_msi - trigger an MSI. + * @phandle: PCIe endpoint HW driver handle + * @idx: MSI index number + * + * This function allows PCIe client to trigger an MSI + * on host side. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_trigger_msi(struct ep_pcie_hw *phandle, u32 idx); + +/* + * ep_pcie_wakeup_host - wake up the host. + * @phandle: PCIe endpoint HW driver handle + * @event: PCIe event of ep_pcie_event type + * + * This function asserts WAKE GPIO to wake up the host. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_wakeup_host(struct ep_pcie_hw *phandle, enum ep_pcie_event event); + +/* + * ep_pcie_enable_endpoint - enable PCIe endpoint. + * @phandle: PCIe endpoint HW driver handle + * @opt: endpoint enable options + * + * This function is to enable the PCIe endpoint device. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_enable_endpoint(struct ep_pcie_hw *phandle, + enum ep_pcie_options opt); + +/* + * ep_pcie_disable_endpoint - disable PCIe endpoint. + * @phandle: PCIe endpoint HW driver handle + * + * This function is to disable the PCIe endpoint device. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_disable_endpoint(struct ep_pcie_hw *phandle); + +/* + * ep_pcie_config_db_routing - Configure routing of doorbells to another block. + * @phandle: PCIe endpoint HW driver handle + * @chdb_cfg: channel doorbell config + * @erdb_cfg: event ring doorbell config + * + * This function allows PCIe core to route the doorbells intended + * for another entity via a target address. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_config_db_routing(struct ep_pcie_hw *phandle, + struct ep_pcie_db_config chdb_cfg, + struct ep_pcie_db_config erdb_cfg); + +/* + * ep_pcie_mask_irq_event - enable and disable IRQ event. + * @phandle: PCIe endpoint HW driver handle + * @event: IRQ event + * @enable: true to enable that IRQ event and false to disable + * + * This function is to enable and disable IRQ event. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_mask_irq_event(struct ep_pcie_hw *phandle, + enum ep_pcie_irq_event event, + bool enable); + +/* + * ep_pcie_configure_inactivity_timer - Configure timer to trigger + * upon link inactivity. + * @phandle: PCIe endpoint HW driver handle + * @param: structure member to program the timer and enable it. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_configure_inactivity_timer(struct ep_pcie_hw *phandle, + struct ep_pcie_inactivity *param); + +/* + * ep_pcie_core_l1ss_sleep_config_enable - Enable L1ss sleep configuration + * to gate the CLKREQ# and disable PCIe resources. + * + * Return: 0 on success, negative value on error + */ +int ep_pcie_core_l1ss_sleep_config_enable(void); + +#if IS_ENABLED(CONFIG_QCOM_PCI_EDMA) +int qcom_edma_init(struct device *dev); +void edma_dump(void); +#else +static inline int qcom_edma_init(struct device *dev) +{ + return 0; +} +static inline void edma_dump(void) {} +#endif + +#endif