diff --git a/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt b/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt new file mode 100644 index 000000000000..1ae00121771c --- /dev/null +++ b/Documentation/devicetree/bindings/pci/msm_ep_pcie.txt @@ -0,0 +1,106 @@ +MSM PCI express endpoint + +Required properties: + - compatible: should be "qcom,pcie-ep". + - reg: should contain PCIe register maps. + - reg-names: indicates various resources passed to driver by name. + Should be "msi", "dm_core", "elbi", "parf", "phy", "mmio". + These correspond to different modules within the PCIe domain. + - #address-cells: Should provide a value of 0. + - interrupt-parent: Should be the PCIe device node itself here. + - interrupts: Should be in the format <0 1 2> and it is an index to the + interrupt-map that contains PCIe related interrupts. + - #interrupt-cells: Should provide a value of 1. + - #interrupt-map-mask: should provide a value of 0xffffffff. + - interrupt-map: Must create mapping for the number of interrupts + that are defined in above interrupts property. + For PCIe device node, it should define 6 mappings for + the corresponding PCIe interrupts supporting the + specification. + - interrupt-names: indicates interrupts passed to driver by name. + Should be "int_pm_turnoff", "int_dstate_change", + "int_l1sub_timeout", "int_link_up", + "int_link_down", "int_bridge_flush_n". + - perst-gpio: PERST GPIO specified by PCIe spec. + - wake-gpio: WAKE GPIO specified by PCIe spec. + - clkreq-gpio: CLKREQ GPIO specified by PCIe spec. + - -supply: phandle to the regulator device tree node. + Refer to the schematics for the corresponding voltage regulators. + vreg-1.8-supply: phandle to the analog supply for the PCIe controller. + vreg-0.9-supply: phandle to the analog supply for the PCIe controller. + +Optional Properties: + - qcom,-voltage-level: specifies voltage levels for supply. + Should be specified in pairs (max, min, optimal), units uV. + - clock-names: list of names of clock inputs. + Should be "pcie_0_pipe_clk", + "pcie_0_aux_clk", "pcie_0_cfg_ahb_clk", + "pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk", + "pcie_0_ldo"; + - max-clock-frequency-hz: list of the maximum operating frequencies stored + in the same order of clock names; + - qcom,pcie-phy-ver: version of PCIe PHY. + - qcom,pcie-link-speed: generation of PCIe link speed. The value could be + 1, 2 or 3. + - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for + below optional properties: + - qcom,msm-bus,name + - qcom,msm-bus,num-cases + - qcom,msm-bus,num-paths + - qcom,msm-bus,vectors-KBps + +Example: + + pcie_ep: qcom,pcie@bfffd000 { + compatible = "qcom,pcie-ep"; + + reg = <0xbfffd000 0x1000>, + <0xbfffe000 0x1000>, + <0xbffff000 0x1000>, + <0xfc520000 0x2000>, + <0xfc526000 0x1000>, + <0xfc527000 0x1000>; + reg-names = "msi", "dm_core", "elbi", "parf", "phy", "mmio"; + + #address-cells = <0>; + interrupt-parent = <&pcie_ep>; + interrupts = <0 1 2 3 4 5>; + #interrupt-cells = <1>; + interrupt-map-mask = <0xffffffff>; + interrupt-map = <0 &intc 0 44 0 + 1 &intc 0 46 0 + 2 &intc 0 47 0 + 3 &intc 0 50 0 + 4 &intc 0 51 0 + 5 &intc 0 52 0>; + interrupt-names = "int_pm_turnoff", "int_dstate_change", + "int_l1sub_timeout", "int_link_up", + "int_link_down", "int_bridge_flush_n"; + + perst-gpio = <&msmgpio 65 0>; + wake-gpio = <&msmgpio 61 0>; + clkreq-gpio = <&msmgpio 64 0>; + + gdsc-vdd-supply = <&gdsc_pcie_0>; + vreg-1.8-supply = <&pmd9635_l8>; + vreg-0.9-supply = <&pmd9635_l4>; + + qcom,vreg-1.8-voltage-level = <1800000 1800000 1000>; + qcom,vreg-0.9-voltage-level = <950000 950000 24000>; + + clock-names = "pcie_0_pipe_clk", + "pcie_0_aux_clk", "pcie_0_cfg_ahb_clk", + "pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk", + "pcie_0_ldo"; + max-clock-frequency-hz = <62500000>, <1000000>, + <0>, <0>, <0>, <0>; + + qcom,msm-bus,name = "pcie-ep"; + qcom,msm-bus,num-cases = <2>; + qcom,msm-bus,num-paths = <1>; + qcom,msm-bus,vectors-KBps = + <45 512 0 0>, + <45 512 500 800>; + + qcom,pcie-link-speed = <1>; + }; \ No newline at end of file diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index fb795960580c..0daa88715111 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -48,6 +48,27 @@ config SPS_SUPPORT_NDP_BAM help No-Data-Path BAM is used to improve BAM performance. +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 GPIO_USB_DETECT tristate "GPIO-based USB VBUS Detection" depends on POWER_SUPPLY diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index 879b425c43f6..ef84024ab474 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -4,4 +4,5 @@ obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o obj-$(CONFIG_SPS) += sps/ +obj-$(CONFIG_EP_PCIE) += ep_pcie/ obj-$(CONFIG_GPIO_USB_DETECT) += gpio-usbdetect.o diff --git a/drivers/platform/msm/ep_pcie/Makefile b/drivers/platform/msm/ep_pcie/Makefile new file mode 100644 index 000000000000..fcacad7bc375 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/Makefile @@ -0,0 +1,3 @@ +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..59d9771ebdc7 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie.c @@ -0,0 +1,232 @@ +/* Copyright (c) 2015, 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. + */ + +/* + * MSM PCIe endpoint service layer. + */ +#include +#include +#include +#include +#include +#include +#include + +LIST_HEAD(head); + +int ep_pcie_register_drv(struct ep_pcie_hw *handle) +{ + struct ep_pcie_hw *present; + bool new = true; + + if (!handle) { + pr_err("ep_pcie:%s: the input handle is NULL.", + __func__); + 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.", + __func__, handle->device_id); + return 0; + } else { + pr_debug( + "ep_pcie:%s: driver to register for device 0x%x has already existed.", + __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 (!handle) { + pr_err("ep_pcie:%s: the input handle is NULL.", + __func__); + 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.", + __func__, handle->device_id); + return 0; + } else { + pr_err("ep_pcie:%s: driver for device 0x%x does not exist.", + __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.", + __func__, id); + return present; + } + } + + pr_debug("ep_pcie:%s: driver for device 0x%x does not exist.", + __func__, id); + return NULL; +} +EXPORT_SYMBOL(ep_pcie_get_phandle); + +int ep_pcie_register_event(struct ep_pcie_hw *phandle, + struct ep_pcie_register_event *reg) +{ + if (phandle) { + return phandle->register_event(reg); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_register_event); + +int ep_pcie_deregister_event(struct ep_pcie_hw *phandle) +{ + if (phandle) { + return phandle->deregister_event(); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_deregister_event); + +enum ep_pcie_link_status ep_pcie_get_linkstatus(struct ep_pcie_hw *phandle) +{ + if (phandle) { + return phandle->get_linkstatus(); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +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 (phandle) { + return phandle->config_outbound_iatu(entries, num_entries); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +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 (phandle) { + return phandle->get_msi_config(cfg); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_get_msi_config); + +int ep_pcie_trigger_msi(struct ep_pcie_hw *phandle, u32 idx) +{ + if (phandle) { + return phandle->trigger_msi(idx); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_trigger_msi); + +int ep_pcie_wakeup_host(struct ep_pcie_hw *phandle) +{ + if (phandle) { + return phandle->wakeup_host(); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +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 (phandle) { + return phandle->config_db_routing(chdb_cfg, erdb_cfg); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_config_db_routing); + +int ep_pcie_enable_endpoint(struct ep_pcie_hw *phandle, + enum ep_pcie_options opt) +{ + if (phandle) { + return phandle->enable_endpoint(opt); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_enable_endpoint); + +int ep_pcie_disable_endpoint(struct ep_pcie_hw *phandle) +{ + if (phandle) { + return phandle->disable_endpoint(); + } else { + pr_err("ep_pcie:%s: the input driver handle is NULL.", + __func__); + return -EINVAL; + } +} +EXPORT_SYMBOL(ep_pcie_disable_endpoint); 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..2561f1cc959e --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_com.h @@ -0,0 +1,332 @@ +/* Copyright (c) 2015, 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 __EP_PCIE_COM_H +#define __EP_PCIE_COM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIE20_PARF_SYS_CTRL 0x00 +#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_DBI_BASE_ADDR 0x168 +#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x16C +#define PCIE20_PARF_MHI_BASE_ADDR_LOWER 0x178 +#define PCIE20_PARF_MHI_BASE_ADDR_UPPER 0x17c +#define PCIE20_PARF_DEBUG_INT_EN 0x190 +#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_DEVICE_TYPE 0x1000 + +#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_COMMAND_STATUS 0x04 +#define PCIE20_CLASS_CODE_REVISION_ID 0x08 +#define PCIE20_BIST_HDR_TYPE 0x0C +#define PCIE20_BAR0 0x10 +#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_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_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 PERST_TIMEOUT_US_MIN 5000 +#define PERST_TIMEOUT_US_MAX 5100 +#define PERST_CHECK_MAX_COUNT 2000 +#define LINK_UP_TIMEOUT_US_MIN 5000 +#define LINK_UP_TIMEOUT_US_MAX 5100 +#define LINK_UP_CHECK_MAX_COUNT 2000 +#define BME_TIMEOUT_US_MIN 5000 +#define BME_TIMEOUT_US_MAX 5100 +#define BME_CHECK_MAX_COUNT 6000 +#define PHY_STABILIZATION_DELAY_US_MIN 995 +#define PHY_STABILIZATION_DELAY_US_MAX 1005 +#define REFCLK_STABILIZATION_DELAY_US_MIN 995 +#define REFCLK_STABILIZATION_DELAY_US_MAX 1005 +#define PHY_READY_TIMEOUT_COUNT 10000 +#define XMLH_LINK_UP 0x400 + +#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 2 +#define EP_PCIE_MAX_CLK 5 +#define EP_PCIE_MAX_PIPE_CLK 1 + +#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_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 { \ + if ((dev)->ipc_log_sel) \ + ipc_log_string((dev)->ipc_log_sel, \ + "DBG1:%s: " fmt, __func__, arg); \ + if ((dev)->ipc_log_ful) \ + 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 { \ + if ((dev)->ipc_log_ful) \ + 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 { \ + if ((dev)->ipc_log_dump) \ + 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 { \ + if ((dev)->ipc_log_sel) \ + ipc_log_string((dev)->ipc_log_sel, \ + "INFO:%s: " fmt, __func__, arg); \ + if ((dev)->ipc_log_ful) \ + 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 { \ + if ((dev)->ipc_log_sel) \ + ipc_log_string((dev)->ipc_log_sel, \ + "ERR:%s: " fmt, __func__, arg); \ + if ((dev)->ipc_log_ful) \ + 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_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_MAX_IRQ, +}; + +enum ep_pcie_gpio { + EP_PCIE_GPIO_PERST, + EP_PCIE_GPIO_WAKE, + EP_PCIE_GPIO_CLKREQ, + 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_res_info_t { + char *name; + struct resource *resource; + void __iomem *base; +}; + +struct ep_pcie_irq_info_t { + char *name; + u32 num; +}; + +/* 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_irq_info_t irq[EP_PCIE_MAX_IRQ]; + struct ep_pcie_res_info_t res[EP_PCIE_MAX_RES]; + + void __iomem *parf; + void __iomem *phy; + void __iomem *mmio; + void __iomem *msi; + void __iomem *dm_core; + void __iomem *elbi; + + struct msm_bus_scale_pdata *bus_scale_table; + u32 bus_client; + u32 link_speed; + + 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 pm_to_counter; + ulong d0_counter; + ulong d3_counter; + ulong perst_ast_counter; + ulong perst_deast_counter; + ulong wake_counter; + ulong msi_counter; + + bool dump_conf; + + bool enumerated; + enum ep_pcie_link_status link_status; + bool perst_deast; + bool power_on; + bool suspending; + bool l1ss_enabled; + struct ep_pcie_msi_config msi_cfg; + + struct ep_pcie_register_event *event_reg; +}; + +extern struct ep_pcie_dev_t ep_pcie_dev; + +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 *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 *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_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..72c67c8f8e04 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_core.c @@ -0,0 +1,1924 @@ +/* Copyright (c) 2015, 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. + */ + +/* + * MSM PCIe endpoint core driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ep_pcie_com.h" + +/* debug mask sys interface */ +static int ep_pcie_debug_mask; +static int ep_pcie_debug_keep_resource; +module_param_named(debug_mask, ep_pcie_debug_mask, + int, S_IRUGO | S_IWUSR | S_IWGRP); +module_param_named(debug_keep_resource, ep_pcie_debug_keep_resource, + int, S_IRUGO | S_IWUSR | S_IWGRP); + +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, 1000, true}, + {NULL, "vreg-0.9", 1000000, 1000000, 24000, true} +}; + +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, 1, 0, 0} +}; + +static struct ep_pcie_clk_info_t + ep_pcie_clk_info[EP_PCIE_MAX_CLK] = { + {NULL, "pcie_0_cfg_ahb_clk", 0, true}, + {NULL, "pcie_0_mstr_axi_clk", 0, true}, + {NULL, "pcie_0_slv_axi_clk", 0, true}, + {NULL, "pcie_0_aux_clk", 1000000, true}, + {NULL, "pcie_0_ldo", 0, true} +}; + +static struct ep_pcie_clk_info_t + ep_pcie_pipe_clk_info[EP_PCIE_MAX_PIPE_CLK] = { + {NULL, "pcie_0_pipe_clk", 62500000, true} +}; + +static const struct ep_pcie_res_info_t ep_pcie_res_info[EP_PCIE_MAX_RES] = { + {"parf", 0, 0}, + {"phy", 0, 0}, + {"mmio", 0, 0}, + {"msi", 0, 0}, + {"dm_core", 0, 0}, + {"elbi", 0, 0} +}; + +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 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 vender 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 vender ID are 0x%x.\n", + dev->rev, val); + return false; + } + + return true; +} + +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) { + 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_optimum_mode(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->bus_client) { + rc = msm_bus_scale_client_update_request(dev->bus_client, 1); + if (rc) { + EP_PCIE_ERR(dev, + "PCIe V%d: fail to set bus bandwidth:%d.\n", + dev->rev, rc); + return rc; + } else { + 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_ERR(dev, + "PCIe V%d: handle of 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; + } else { + 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: 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; + int rc; + + 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->bus_client) { + rc = msm_bus_scale_client_update_request(dev->bus_client, 0); + if (rc) + EP_PCIE_ERR(dev, + "PCIe V%d: fail to relinquish bus bandwidth:%d.\n", + dev->rev, rc); + else + EP_PCIE_DBG(dev, + "PCIe V%d: relinquish bus bandwidth.\n", + dev->rev); + } + + 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; + } + + clk_reset(info->hdl, CLK_RESET_DEASSERT); + + 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; + } else { + 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) +{ + struct resource *res = dev->res[EP_PCIE_RES_MMIO].resource; + u32 mask = res->end - res->start; + u32 properties = 0x4; + + EP_PCIE_DBG(dev, "PCIe V%d: BAR mask to program is 0x%x\n", + dev->rev, mask); + + /* 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, mask); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x8, mask); + ep_pcie_write_reg(dev->dm_core, PCIE20_BAR0 + 0x10, mask); + 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_reg(dev->dm_core, PCIE20_BAR0 + 0x10, properties); + ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, BIT(0), 0); +} + +static void ep_pcie_core_init(struct ep_pcie_dev_t *dev) +{ + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + /* enable debug IRQ */ + ep_pcie_write_mask(dev->parf + PCIE20_PARF_DEBUG_INT_EN, + 0, BIT(3) | BIT(1)); + + /* Configure PCIe to endpoint mode */ + ep_pcie_write_reg(dev->parf, PCIE20_PARF_DEVICE_TYPE, 0x0); + + /* adjust DBI base address */ + writel_relaxed(0x3FFFE000, dev->parf + PCIE20_PARF_DBI_BASE_ADDR); + + /* Configure PCIe core to support 1GB aperture */ + ep_pcie_write_reg(dev->parf, PCIE20_PARF_SLV_ADDR_SPACE_SIZE, + 0x40000000); + + /* Configure link speed */ + ep_pcie_write_mask(dev->dm_core + PCIE20_LINK_CONTROL2_LINK_STATUS2, + 0xf, dev->link_speed); + + /* 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 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)); + + 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)); + + /* Enable CS for RO(CS) register writes */ + ep_pcie_write_mask(dev->dm_core + PCIE20_MISC_CONTROL_1, 0, BIT(0)); + + /* 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 the PMC Register - to support PME in D0, D3hot and D3cold */ + ep_pcie_write_mask(dev->dm_core + PCIE20_CAP_ID_NXT_PTR, 0, + BIT(4)|BIT(3)|BIT(0)); + + /* 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 not supported */ + ep_pcie_write_mask(dev->dm_core + PCIE20_L1SUB_CAPABILITY, 0x1f, 0); + + /* 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); +} + +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); + + /* 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 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); + + /* 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, 0x0); + 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 && 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; + 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 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; + + EP_PCIE_DBG(dev, "PCIe V%d\n", dev->rev); + + cnt = of_property_count_strings((&pdev->dev)->of_node, + "clock-names"); + if (cnt > 0) { + clkfreq = kzalloc(cnt * sizeof(*clkfreq), + GFP_KERNEL); + if (!clkfreq) { + EP_PCIE_ERR(dev, "PCIe V%d: memory alloc failed\n", + dev->rev); + return -ENOMEM; + } + ret = of_property_read_u32_array( + (&pdev->dev)->of_node, + "max-clock-frequency-hz", clkfreq, cnt); + if (ret) { + EP_PCIE_ERR(dev, + "PCIe V%d: invalid max-clock-frequency-hz property:%d\n", + dev->rev, ret); + goto out; + } + } + + 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_DBG(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 { + goto out; + } + } + + 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_DBG(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_DBG(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); + } + } + } + + dev->bus_scale_table = msm_bus_cl_get_pdata(pdev); + if (!dev->bus_scale_table) { + EP_PCIE_DBG(dev, "PCIe V%d: No bus scale table for %s\n", + dev->rev, dev->pdev->name); + dev->bus_client = 0; + } else { + dev->bus_client = + msm_bus_scale_register_client(dev->bus_scale_table); + if (!dev->bus_client) { + EP_PCIE_ERR(dev, + "PCIe V%d: Failed to register bus client for %s\n", + dev->rev, dev->pdev->name); + msm_bus_cl_clear_pdata(dev->bus_scale_table); + ret = -ENODEV; + goto out; + } + } + + for (i = 0; i < EP_PCIE_MAX_RES; i++) { + res_info = &dev->res[i]; + + 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); + ret = -ENOMEM; + goto out; + } else { + EP_PCIE_DBG(dev, "start addr for %s is %pa.\n", + res_info->name, &res->start); + } + + 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) { + int j; + for (j = 0; j < EP_PCIE_MAX_RES; j++) { + iounmap(dev->res[j].base); + dev->res[j].base = NULL; + } + EP_PCIE_ERR(dev, "PCIe V%d: can't find IRQ # for %s\n", + dev->rev, irq_info->name); + ret = -ENODEV; + goto out; + } else { + irq_info->num = res->start; + EP_PCIE_DBG(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->elbi = dev->res[EP_PCIE_RES_ELBI].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->phy = NULL; + dev->mmio = NULL; + dev->msi = NULL; +} + +int ep_pcie_core_enable_endpoint(enum ep_pcie_options opt) +{ + int ret = 0; + u32 val; + u32 retries; + u32 bme; + 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_ERR(dev, + "PCIe V%d: link is up but not enabled; need to disable link first.\n", + dev->rev); + ret = EP_PCIE_ERROR; + goto out; + } + + 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); + ret = EP_PCIE_ERROR; + 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; + } + + /* 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; + } + } + + if (!(opt & EP_PCIE_OPT_ENUM)) + goto out; + + 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 pipe_clk_fail; + } else { + dev->perst_deast = true; + 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); + + 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 % 10 == 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); + ep_pcie_config_inbound_iatu(dev); + + /* enable link training */ + 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 % 5 == 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; + 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); + } + + /* 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); + } + + if (bme) { + dev->link_status = EP_PCIE_LINK_ENABLED; + 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); + } else { + EP_PCIE_ERR(dev, + "PCIe V%d: PCIe link is up but BME is still disabled after max waiting time.\n", + dev->rev); + if (!ep_pcie_debug_keep_resource) { + ret = EP_PCIE_ERROR; + dev->link_status = EP_PCIE_LINK_DISABLED; + goto link_fail; + } + } + + dev->power_on = true; + dev->suspending = false; + goto out; + +link_fail: + 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) +{ + int rc = 0; + 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 (!dev->power_on) { + EP_PCIE_DBG(dev, + "PCIe V%d: the link is already power down.\n", + dev->rev); + goto out; + } + + 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); + + ep_pcie_pipe_clk_deinit(dev); + ep_pcie_clk_deinit(dev); + ep_pcie_vreg_deinit(dev); +out: + mutex_unlock(&dev->setup_mtx); + return rc; +} + +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->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)); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_HOT); + } else if (dstate == 0) { + 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 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" : ""); + goto out; + } + + if (perst) { + dev->perst_deast = true; + dev->perst_deast_counter++; + 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 { + dev->perst_deast = false; + dev->perst_ast_counter++; + EP_PCIE_DBG(dev, + "PCIe V%d: No. %ld PERST assertion.\n", + dev->rev, dev->perst_ast_counter); + ep_pcie_notify_event(dev, EP_PCIE_EVENT_PM_D3_COLD); + } + +out: + spin_unlock_irqrestore(&dev->isr_lock, irqsave_flags); + + 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); + + /* 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; + } + + /* 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, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "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; + } + + 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); + + 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; + + 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; + } else { + u32 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; + } 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; + + 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, data_end, + data_tgt_lower, data_tgt_upper); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_CTRL, + ctrl_start, 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, 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, ctrl_end, + ctrl_tgt_lower, ctrl_tgt_upper); + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_DATA, + data_start, 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; + 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); + + 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 (lower && data) { + struct resource *msi = + ep_pcie_dev.res[EP_PCIE_RES_MSI].resource; + ep_pcie_config_outbound_iatu_entry(&ep_pcie_dev, + EP_PCIE_OATU_INDEX_MSI, + msi->start, msi->end, + lower, upper); + 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; + } else { + 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; + + 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); + + if (addr && data) { + EP_PCIE_DUMP(&ep_pcie_dev, + "PCIe V%d: No. %ld MSI fired for IRQ %d; index from client:%d\n", + ep_pcie_dev.rev, ep_pcie_dev.msi_counter++, + data + idx, idx); + ep_pcie_write_reg(ep_pcie_dev.msi, addr & 0xfff, data + idx); + return 0; + } else { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: Wrong MSI info found. MSI addr:0x%x; data:0x%x; index from client:%d.\n", + ep_pcie_dev.rev, addr, data, idx); + return EP_PCIE_ERROR; + } +} + +int ep_pcie_core_wakeup_host(void) +{ + struct ep_pcie_dev_t *dev = &ep_pcie_dev; + + if (dev->perst_deast) { + EP_PCIE_ERR(dev, + "PCIe V%d: request to assert WAKE# when PERST is de-asserted.\n", + dev->rev); + return EP_PCIE_ERROR; + } else { + EP_PCIE_DBG(dev, "PCIe V%d: No. %ld to assert PCIe WAKE#.\n", + dev->rev, ++dev->wake_counter); + gpio_set_value(dev->gpio[EP_PCIE_GPIO_WAKE].num, + 1 - dev->gpio[EP_PCIE_GPIO_WAKE].on); + 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; +} + +static 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, +}; + +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.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.rev = 1503191; + ep_pcie_dev.pdev = pdev; + 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.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; + } + + 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_core_enable_endpoint(EP_PCIE_OPT_ALL); + + if (ret) { + EP_PCIE_ERR(&ep_pcie_dev, + "PCIe V%d: PCIe link failed to be enabled during probe.\n", + ep_pcie_dev.rev); + goto ep_enable_failure; + } else { + EP_PCIE_INFO(&ep_pcie_dev, + "PCIe V%d: PCIe link is enabled during probe.\n", + ep_pcie_dev.rev); + + ep_pcie_dev.enumerated = true; + + hw_drv.device_id = readl_relaxed(ep_pcie_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); + + ep_pcie_notify_event(&ep_pcie_dev, EP_PCIE_EVENT_LINKUP); + + return 0; + } + +ep_enable_failure: + 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 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", + .owner = THIS_MODULE, + .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); + 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); + 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); + 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); +} + +module_init(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..900b33b3554d --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_dbg.c @@ -0,0 +1,466 @@ +/* Copyright (c) 2015, 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. + */ + +/* + * 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_pcie_phy_dump(struct ep_pcie_dev_t *dev) +{ + int i; + int control_offset[6] = {0x60, 0x70, 0x80, 0xA0, 0xB0, 0xB0}; + + EP_PCIE_DUMP(dev, "PCIe V%d: PHY testbus\n", dev->rev); + + for (i = 0; i < 6; i++) { + switch (i) { + case 3: + ep_pcie_write_reg(dev->phy, + QSERDES_COM_ATB_SEL2, + 0x10); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_COM_ATB_SEL2: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + QSERDES_COM_ATB_SEL2)); + break; + case 4: + ep_pcie_write_reg(dev->phy, + QSERDES_TX_SERDES_BYP_EN_OUT, + 0x10); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_TX_SERDES_BYP_EN_OUT: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + + QSERDES_TX_SERDES_BYP_EN_OUT)); + break; + case 5: + ep_pcie_write_reg(dev->phy, + QSERDES_TX_SERDES_BYP_EN_OUT, + 0x30); + EP_PCIE_DUMP(dev, + "PCIe V%d: QSERDES_TX_SERDES_BYP_EN_OUT: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + + QSERDES_TX_SERDES_BYP_EN_OUT)); + break; + default: + break; + } + + ep_pcie_write_reg(dev->phy, PCIE_PHY_TEST_CONTROL, + control_offset[i]); + + EP_PCIE_DUMP(dev, + "PCIe V%d: PCIE_PHY_TEST_CONTROL: 0x%x\n", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_TEST_CONTROL)); + 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", + dev->rev, + readl_relaxed(dev->phy + PCIE_PHY_DEBUG_BUS_3_STATUS)); + } + + EP_PCIE_DUMP(dev, "PCIe V%d: PHY register dump\n", dev->rev); + + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_COM_PLL_VCO_HIGH: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_COM_PLL_VCO_HIGH)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_COM_RESET_SM: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_COM_RESET_SM)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_COM_MUXVAL: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_COM_MUXVAL)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_PI_CTRL1: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_PI_CTRL1)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_PI_CTRL2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_PI_CTRL2)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_PI_QUAD: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_PI_QUAD)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_IDATA1: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_IDATA1)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_IDATA2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_IDATA2)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_AUX_DATA1: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_AUX_DATA1)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_AUX_DATA2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_AUX_DATA2)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_AC_JTAG_OUTP: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_AC_JTAG_OUTP)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_AC_JTAG_OUTN: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_AC_JTAG_OUTN)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_RX_SIGDET: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_RX_SIGDET)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_RX_VDCOFF: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_RX_VDCOFF)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_IDAC_CAL_ON: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_IDAC_CAL_ON)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_IDAC_STATUS_I: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_IDAC_STATUS_I)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_IDAC_STATUS_Q: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_IDAC_STATUS_Q)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_IDAC_STATUS_A: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_IDAC_STATUS_A)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_CALST_STATUS_I: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_CALST_STATUS_I)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_CALST_STATUS_Q: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_CALST_STATUS_Q)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_CALST_STATUS_A: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_CALST_STATUS_A)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS0: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS0)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS1: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS1)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS2)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS3: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS3)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS4: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS4)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS5: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS5)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS6: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS6)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS7: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS7)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS8: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS8)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_EOM_STATUS9: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_EOM_STATUS9)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_RX_ALOG_INTF_OBSV: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_RX_ALOG_INTF_OBSV)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_READ_EQCODE: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_READ_EQCODE)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_RX_READ_OFFSETCODE: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_RX_READ_OFFSETCODE)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_TX_BIST_STATUS: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_TX_BIST_STATUS)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_TX_BIST_ERROR_COUNT1: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_TX_BIST_ERROR_COUNT1)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_TX_BIST_ERROR_COUNT2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_TX_BIST_ERROR_COUNT2)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_TX_TX_ALOG_INTF_OBSV: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_TX_TX_ALOG_INTF_OBSV)); + EP_PCIE_DUMP(dev, "PCIe V%d: QSERDES_TX_PWM_DEC_STATUS: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + QSERDES_TX_PWM_DEC_STATUS)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_BIST_CHK_ERR_CNT_L: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_BIST_CHK_ERR_CNT_L)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_BIST_CHK_ERR_CNT_H: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_BIST_CHK_ERR_CNT_H)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_BIST_CHK_STATUS: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_BIST_CHK_STATUS)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_LFPS_RXTERM_IRQ_SOURCE: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_LFPS_RXTERM_IRQ_SOURCE)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_PCS_STATUS: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_PCS_STATUS)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_PCS_STATUS2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_PCS_STATUS2)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_REVISION_ID0: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_REVISION_ID0)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_REVISION_ID1: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_REVISION_ID1)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_REVISION_ID2: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_REVISION_ID2)); + EP_PCIE_DUMP(dev, "PCIe V%d: PCIE_PHY_REVISION_ID3: 0x%x\n", + dev->rev, readl_relaxed(dev->phy + PCIE_PHY_REVISION_ID3)); +} + +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) + 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("bus_client: %d\n", + dev->bus_client); + 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; + u32 device_id = 0; + 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}; + + if (dev->power_on) { + device_id = readl_relaxed(dev->dm_core); + phandle = ep_pcie_get_phandle(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); + 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; + 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..e0ea00165c89 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.c @@ -0,0 +1,81 @@ +/* Copyright (c) 2015, 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. + */ + +/* + * 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) +{ + EP_PCIE_DBG(dev, + "PCIe V%d: PHY V%d: Initializing 20nm QMP phy - 100MHz\n", + dev->rev, dev->phy_rev); + + ep_pcie_write_reg(dev->phy, PCIE_PHY_POWER_DOWN_CONTROL, 0x01); + ep_pcie_write_reg(dev->phy, QSERDES_COM_SYS_CLK_CTRL, 0x1E); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CP_SETI, 0x11); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_IP_SETP, 0x3F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CP_SETP, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_IP_SETI, 0x3F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_IP_TRIM, 0x0F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RESETSM_CNTRL, 0x90); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RES_CODE_CAL_CSR, 0x77); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RES_TRIM_CONTROL, 0x15); + ep_pcie_write_reg(dev->phy, QSERDES_TX_RCV_DETECT_LVL, 0x03); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQ_GAIN2_MSB, 0x00); + ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_ENABLES, 0x40); + ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_CNTRL, 0x70); + ep_pcie_write_reg(dev->phy, PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK, 0xC8); + ep_pcie_write_reg(dev->phy, PCIE_PHY_POWER_STATE_CONFIG1, 0xA3); + ep_pcie_write_reg(dev->phy, PCIE_PHY_POWER_STATE_CONFIG2, 0x1B); + + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_VCOTAIL_EN, 0xE1); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RESETSM_CNTRL2, 0x07); + ep_pcie_write_reg(dev->phy, QSERDES_COM_IE_TRIM, 0x3F); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CNTRL, 0x46); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLLLOCK_CMP2, 0x05); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLLLOCK_CMP_EN, 0x03); + ep_pcie_write_reg(dev->phy, QSERDES_COM_DEC_START1, 0x99); + ep_pcie_write_reg(dev->phy, QSERDES_RX_CDR_CONTROL1, 0xF5); + ep_pcie_write_reg(dev->phy, QSERDES_RX_CDR_CONTROL_HALF, 0x2C); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RES_CODE_START_SEG1, 0x24); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQ_GAIN1_MSB, 0x07); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x1E); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, + 0x67); + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80); + ep_pcie_write_reg(dev->phy, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x0C); + ep_pcie_write_reg(dev->phy, PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK, 0x80); + ep_pcie_write_reg(dev->phy, PCIE_PHY_RX_IDLE_DTCT_CNTRL, 0x4D); + + if (dev->phy_rev == 1) { + ep_pcie_write_reg(dev->phy, QSERDES_RX_RX_RCVR_IQ_EN, 0x31); + ep_pcie_write_reg(dev->phy, QSERDES_COM_RESETSM_CNTRL2, 0x5); + ep_pcie_write_reg(dev->phy, QSERDES_COM_PLL_VCOTAIL_EN, 0x1); + } + + ep_pcie_write_reg(dev->phy, PCIE_PHY_SW_RESET, 0x00); + ep_pcie_write_reg(dev->phy, PCIE_PHY_START, 0x03); +} + +bool ep_pcie_phy_is_ready(struct ep_pcie_dev_t *dev) +{ + if (readl_relaxed(dev->phy + PCIE_PHY_PCS_STATUS) & BIT(6)) + return false; + 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..e0823e408f88 --- /dev/null +++ b/drivers/platform/msm/ep_pcie/ep_pcie_phy.h @@ -0,0 +1,351 @@ +/* Copyright (c) 2015, 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 __EP_PCIE_PHY_H +#define __EP_PCIE_PHY_H + +#define QSERDES_COM_SYS_CLK_CTRL 0x0 +#define QSERDES_COM_PLL_VCOTAIL_EN 0x4 +#define QSERDES_COM_CMN_MODE 0x8 +#define QSERDES_COM_IE_TRIM 0xC +#define QSERDES_COM_IP_TRIM 0x10 +#define QSERDES_COM_PLL_CNTRL 0x14 +#define QSERDES_COM_PLL_PHSEL_CONTROL 0x18 +#define QSERDES_COM_IPTAT_TRIM_VCCA_TX_SEL 0x1C +#define QSERDES_COM_PLL_PHSEL_DC 0x20 +#define QSERDES_COM_PLL_IP_SETI 0x24 +#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL 0x28 +#define QSERDES_COM_PLL_BKG_KVCO_CAL_EN 0x2C +#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x30 +#define QSERDES_COM_PLL_CP_SETI 0x34 +#define QSERDES_COM_PLL_IP_SETP 0x38 +#define QSERDES_COM_PLL_CP_SETP 0x3C +#define QSERDES_COM_ATB_SEL1 0x40 +#define QSERDES_COM_ATB_SEL2 0x44 +#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND 0x48 +#define QSERDES_COM_RESETSM_CNTRL 0x4C +#define QSERDES_COM_RESETSM_CNTRL2 0x50 +#define QSERDES_COM_RESETSM_CNTRL3 0x54 +#define QSERDES_COM_DIV_REF1 0x58 +#define QSERDES_COM_DIV_REF2 0x5C +#define QSERDES_COM_KVCO_COUNT1 0x60 +#define QSERDES_COM_KVCO_COUNT2 0x64 +#define QSERDES_COM_KVCO_CAL_CNTRL 0x68 +#define QSERDES_COM_KVCO_CODE 0x6C +#define QSERDES_COM_VREF_CFG1 0x70 +#define QSERDES_COM_VREF_CFG2 0x74 +#define QSERDES_COM_VREF_CFG3 0x78 +#define QSERDES_COM_VREF_CFG4 0x7C +#define QSERDES_COM_VREF_CFG5 0x80 +#define QSERDES_COM_VREF_CFG6 0x84 +#define QSERDES_COM_PLLLOCK_CMP1 0x88 +#define QSERDES_COM_PLLLOCK_CMP2 0x8C +#define QSERDES_COM_PLLLOCK_CMP3 0x90 +#define QSERDES_COM_PLLLOCK_CMP_EN 0x94 +#define QSERDES_COM_BGTC 0x98 +#define QSERDES_COM_PLL_TEST_UPDN 0x9C +#define QSERDES_COM_PLL_VCO_TUNE 0xA0 +#define QSERDES_COM_DEC_START1 0xA4 +#define QSERDES_COM_PLL_AMP_OS 0xA8 +#define QSERDES_COM_SSC_EN_CENTER 0xAC +#define QSERDES_COM_SSC_ADJ_PER1 0xB0 +#define QSERDES_COM_SSC_ADJ_PER2 0xB4 +#define QSERDES_COM_SSC_PER1 0xB8 +#define QSERDES_COM_SSC_PER2 0xBC +#define QSERDES_COM_SSC_STEP_SIZE1 0xC0 +#define QSERDES_COM_SSC_STEP_SIZE2 0xC4 +#define QSERDES_COM_RES_CODE_UP 0xC8 +#define QSERDES_COM_RES_CODE_DN 0xCC +#define QSERDES_COM_RES_CODE_UP_OFFSET 0xD0 +#define QSERDES_COM_RES_CODE_DN_OFFSET 0xD4 +#define QSERDES_COM_RES_CODE_START_SEG1 0xD8 +#define QSERDES_COM_RES_CODE_START_SEG2 0xDC +#define QSERDES_COM_RES_CODE_CAL_CSR 0xE0 +#define QSERDES_COM_RES_CODE 0xE4 +#define QSERDES_COM_RES_TRIM_CONTROL 0xE8 +#define QSERDES_COM_RES_TRIM_CONTROL2 0xEC +#define QSERDES_COM_RES_TRIM_EN_VCOCALDONE 0xF0 +#define QSERDES_COM_FAUX_EN 0xF4 +#define QSERDES_COM_DIV_FRAC_START1 0xF8 +#define QSERDES_COM_DIV_FRAC_START2 0xFC +#define QSERDES_COM_DIV_FRAC_START3 0x100 +#define QSERDES_COM_DEC_START2 0x104 +#define QSERDES_COM_PLL_RXTXEPCLK_EN 0x108 +#define QSERDES_COM_PLL_CRCTRL 0x10C +#define QSERDES_COM_PLL_CLKEPDIV 0x110 +#define QSERDES_COM_PLL_FREQUPDATE 0x114 +#define QSERDES_COM_PLL_BKGCAL_TRIM_UP 0x118 +#define QSERDES_COM_PLL_BKGCAL_TRIM_DN 0x11C +#define QSERDES_COM_PLL_BKGCAL_TRIM_MUX 0x120 +#define QSERDES_COM_PLL_BKGCAL_VREF_CFG 0x124 +#define QSERDES_COM_PLL_BKGCAL_DIV_REF1 0x128 +#define QSERDES_COM_PLL_BKGCAL_DIV_REF2 0x12C +#define QSERDES_COM_MUXADDR 0x130 +#define QSERDES_COM_LOW_POWER_RO_CONTROL 0x134 +#define QSERDES_COM_POST_DIVIDER_CONTROL 0x138 +#define QSERDES_COM_HR_OCLK2_DIVIDER 0x13C +#define QSERDES_COM_HR_OCLK3_DIVIDER 0x140 +#define QSERDES_COM_PLL_VCO_HIGH 0x144 +#define QSERDES_COM_RESET_SM 0x148 +#define QSERDES_COM_MUXVAL 0x14C +#define QSERDES_TX_BIST_MODE_LANENO 0x200 +#define QSERDES_TX_CLKBUF_ENABLE 0x204 +#define QSERDES_TX_TX_EMP_POST1_LVL 0x208 +#define QSERDES_TX_TX_DRV_LVL 0x20C +#define QSERDES_TX_RESET_TSYNC_EN 0x210 +#define QSERDES_TX_LPB_EN 0x214 +#define QSERDES_TX_RES_CODE_UP 0x218 +#define QSERDES_TX_RES_CODE_DN 0x21C +#define QSERDES_TX_PERL_LENGTH1 0x220 +#define QSERDES_TX_PERL_LENGTH2 0x224 +#define QSERDES_TX_SERDES_BYP_EN_OUT 0x228 +#define QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN 0x22C +#define QSERDES_TX_PARRATE_REC_DETECT_IDLE_EN 0x230 +#define QSERDES_TX_BIST_PATTERN1 0x234 +#define QSERDES_TX_BIST_PATTERN2 0x238 +#define QSERDES_TX_BIST_PATTERN3 0x23C +#define QSERDES_TX_BIST_PATTERN4 0x240 +#define QSERDES_TX_BIST_PATTERN5 0x244 +#define QSERDES_TX_BIST_PATTERN6 0x248 +#define QSERDES_TX_BIST_PATTERN7 0x24C +#define QSERDES_TX_BIST_PATTERN8 0x250 +#define QSERDES_TX_LANE_MODE 0x254 +#define QSERDES_TX_IDAC_CAL_LANE_MODE 0x258 +#define QSERDES_TX_IDAC_CAL_LANE_MODE_CONFIGURATION 0x25C +#define QSERDES_TX_ATB_SEL1 0x260 +#define QSERDES_TX_ATB_SEL2 0x264 +#define QSERDES_TX_RCV_DETECT_LVL 0x268 +#define QSERDES_TX_PRBS_SEED1 0x26C +#define QSERDES_TX_PRBS_SEED2 0x270 +#define QSERDES_TX_PRBS_SEED3 0x274 +#define QSERDES_TX_PRBS_SEED4 0x278 +#define QSERDES_TX_RESET_GEN 0x27C +#define QSERDES_TX_TRAN_DRVR_EMP_EN 0x280 +#define QSERDES_TX_TX_INTERFACE_MODE 0x284 +#define QSERDES_TX_PWM_CTRL 0x288 +#define QSERDES_TX_PWM_DATA 0x28C +#define QSERDES_TX_PWM_ENC_DIV_CTRL 0x290 +#define QSERDES_TX_VMODE_CTRL1 0x294 +#define QSERDES_TX_VMODE_CTRL2 0x298 +#define QSERDES_TX_VMODE_CTRL3 0x29C +#define QSERDES_TX_VMODE_CTRL4 0x2A0 +#define QSERDES_TX_VMODE_CTRL5 0x2A4 +#define QSERDES_TX_VMODE_CTRL6 0x2A8 +#define QSERDES_TX_VMODE_CTRL7 0x2AC +#define QSERDES_TX_TX_ALOG_INTF_OBSV_CNTL 0x2B0 +#define QSERDES_TX_BIST_STATUS 0x2B4 +#define QSERDES_TX_BIST_ERROR_COUNT1 0x2B8 +#define QSERDES_TX_BIST_ERROR_COUNT2 0x2BC +#define QSERDES_TX_TX_ALOG_INTF_OBSV 0x2C0 +#define QSERDES_TX_PWM_DEC_STATUS 0x2C4 +#define QSERDES_RX_CDR_CONTROL1 0x400 +#define QSERDES_RX_CDR_CONTROL2 0x404 +#define QSERDES_RX_CDR_CONTROL_HALF 0x408 +#define QSERDES_RX_CDR_CONTROL_QUARTER 0x40C +#define QSERDES_RX_CDR_CONTROL_EIGHTH 0x410 +#define QSERDES_RX_UCDR_FO_GAIN 0x414 +#define QSERDES_RX_UCDR_SO_GAIN 0x418 +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE 0x41C +#define QSERDES_RX_UCDR_FO_TO_SO_DELAY 0x420 +#define QSERDES_RX_AUX_CONTROL 0x424 +#define QSERDES_RX_AUX_DATA_TCOARSE 0x428 +#define QSERDES_RX_AUX_DATA_TFINE_LSB 0x42C +#define QSERDES_RX_AUX_DATA_TFINE_MSB 0x430 +#define QSERDES_RX_RCLK_AUXDATA_SEL 0x434 +#define QSERDES_RX_AC_JTAG_ENABLE 0x438 +#define QSERDES_RX_AC_JTAG_INITP 0x43C +#define QSERDES_RX_AC_JTAG_INITN 0x440 +#define QSERDES_RX_AC_JTAG_LVL 0x444 +#define QSERDES_RX_AC_JTAG_MODE 0x448 +#define QSERDES_RX_AC_JTAG_RESET 0x44C +#define QSERDES_RX_RX_RCVR_IQ_EN 0x450 +#define QSERDES_RX_RX_IDAC_I_DC_OFFSETS 0x454 +#define QSERDES_RX_RX_IDAC_Q_DC_OFFSETS 0x458 +#define QSERDES_RX_RX_IDAC_A_DC_OFFSETS 0x45C +#define QSERDES_RX_RX_IDAC_EN 0x460 +#define QSERDES_RX_RX_IDAC_CTRL0 0x464 +#define QSERDES_RX_RX_IDAC_CTRL1 0x468 +#define QSERDES_RX_RX_EOM_EN 0x46C +#define QSERDES_RX_RX_EOM_CTRL0 0x470 +#define QSERDES_RX_RX_EOM_CTRL1 0x474 +#define QSERDES_RX_RX_EOM_CTRL2 0x478 +#define QSERDES_RX_RX_EOM_CTRL3 0x47C +#define QSERDES_RX_RX_EOM_CTRL4 0x480 +#define QSERDES_RX_RX_EOM_CTRL5 0x484 +#define QSERDES_RX_RX_EOM_CTRL6 0x488 +#define QSERDES_RX_RX_EOM_CTRL7 0x48C +#define QSERDES_RX_RX_EOM_CTRL8 0x490 +#define QSERDES_RX_RX_EOM_CTRL9 0x494 +#define QSERDES_RX_RX_EOM_CTRL10 0x498 +#define QSERDES_RX_RX_EOM_CTRL11 0x49C +#define QSERDES_RX_RX_HIGHZ_HIGHRATE 0x4A0 +#define QSERDES_RX_RX_TERM_AC_BYPASS_DC_COUPLE_OFFSET 0x4A4 +#define QSERDES_RX_RX_EQ_GAIN1_LSB 0x4A8 +#define QSERDES_RX_RX_EQ_GAIN1_MSB 0x4AC +#define QSERDES_RX_RX_EQ_GAIN2_LSB 0x4B0 +#define QSERDES_RX_RX_EQ_GAIN2_MSB 0x4B4 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 0x4B8 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 0x4BC +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 0x4C0 +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 0x4C4 +#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION 0x4C8 +#define QSERDES_RX_RX_IDAC_CAL_CONFIGURATION_2 0x4CC +#define QSERDES_RX_RX_IDAC_TSETTLE_LOW 0x4D0 +#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH 0x4D4 +#define QSERDES_RX_RX_IDAC_ENDSAMP_LOW 0x4D8 +#define QSERDES_RX_RX_IDAC_ENDSAMP_HIGH 0x4DC +#define QSERDES_RX_RX_IDAC_MIDPOINT_LOW 0x4E0 +#define QSERDES_RX_RX_IDAC_MIDPOINT_HIGH 0x4E4 +#define QSERDES_RX_RX_EQ_OFFSET_LSB 0x4E8 +#define QSERDES_RX_RX_EQ_OFFSET_MSB 0x4EC +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x4F0 +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 0x4F4 +#define QSERDES_RX_SIGDET_ENABLES 0x4F8 +#define QSERDES_RX_SIGDET_ENABLES_2 0x4FC +#define QSERDES_RX_SIGDET_CNTRL 0x500 +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL 0x504 +#define QSERDES_RX_SIGDET_TIMER_LIMIT 0x508 +#define QSERDES_RX_RX_BAND 0x50C +#define QSERDES_RX_CDR_FREEZE_UP_DN 0x510 +#define QSERDES_RX_RX_INTERFACE_MODE 0x514 +#define QSERDES_RX_JITTER_GEN_MODE 0x518 +#define QSERDES_RX_BUJ_AMP 0x51C +#define QSERDES_RX_SJ_AMP1 0x520 +#define QSERDES_RX_SJ_AMP2 0x524 +#define QSERDES_RX_SJ_PER1 0x528 +#define QSERDES_RX_SJ_PER2 0x52C +#define QSERDES_RX_BUJ_STEP_FREQ1 0x530 +#define QSERDES_RX_BUJ_STEP_FREQ2 0x534 +#define QSERDES_RX_PPM_OFFSET1 0x538 +#define QSERDES_RX_PPM_OFFSET2 0x53C +#define QSERDES_RX_SIGN_PPM_PERIOD1 0x540 +#define QSERDES_RX_SIGN_PPM_PERIOD2 0x544 +#define QSERDES_RX_SSC_CTRL 0x548 +#define QSERDES_RX_SSC_COUNT1 0x54C +#define QSERDES_RX_SSC_COUNT2 0x550 +#define QSERDES_RX_RX_ALOG_INTF_OBSV_CNTL 0x554 +#define QSERDES_RX_PI_CTRL1 0x558 +#define QSERDES_RX_PI_CTRL2 0x55C +#define QSERDES_RX_PI_QUAD 0x560 +#define QSERDES_RX_IDATA1 0x564 +#define QSERDES_RX_IDATA2 0x568 +#define QSERDES_RX_AUX_DATA1 0x56C +#define QSERDES_RX_AUX_DATA2 0x570 +#define QSERDES_RX_AC_JTAG_OUTP 0x574 +#define QSERDES_RX_AC_JTAG_OUTN 0x578 +#define QSERDES_RX_RX_SIGDET 0x57C +#define QSERDES_RX_RX_VDCOFF 0x580 +#define QSERDES_RX_IDAC_CAL_ON 0x584 +#define QSERDES_RX_IDAC_STATUS_I 0x588 +#define QSERDES_RX_IDAC_STATUS_Q 0x58C +#define QSERDES_RX_IDAC_STATUS_A 0x590 +#define QSERDES_RX_CALST_STATUS_I 0x594 +#define QSERDES_RX_CALST_STATUS_Q 0x598 +#define QSERDES_RX_CALST_STATUS_A 0x59C +#define QSERDES_RX_EOM_STATUS0 0x5A0 +#define QSERDES_RX_EOM_STATUS1 0x5A4 +#define QSERDES_RX_EOM_STATUS2 0x5A8 +#define QSERDES_RX_EOM_STATUS3 0x5AC +#define QSERDES_RX_EOM_STATUS4 0x5B0 +#define QSERDES_RX_EOM_STATUS5 0x5B4 +#define QSERDES_RX_EOM_STATUS6 0x5B8 +#define QSERDES_RX_EOM_STATUS7 0x5BC +#define QSERDES_RX_EOM_STATUS8 0x5C0 +#define QSERDES_RX_EOM_STATUS9 0x5C4 +#define QSERDES_RX_RX_ALOG_INTF_OBSV 0x5C8 +#define QSERDES_RX_READ_EQCODE 0x5CC +#define QSERDES_RX_READ_OFFSETCODE 0x5D0 +#define PCIE_PHY_SW_RESET 0x600 +#define PCIE_PHY_POWER_DOWN_CONTROL 0x604 +#define PCIE_PHY_START 0x608 +#define PCIE_PHY_TXMGN_V1_V0 0x60C +#define PCIE_PHY_TXMGN_V3_V2 0x610 +#define PCIE_PHY_TXMGN_LS_V4 0x614 +#define PCIE_PHY_TXDEEMPH_M6DB_V0 0x618 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V0 0x61C +#define PCIE_PHY_TXDEEMPH_M6DB_V1 0x620 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V1 0x624 +#define PCIE_PHY_TXDEEMPH_M6DB_V2 0x628 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V2 0x62C +#define PCIE_PHY_TXDEEMPH_M6DB_V3 0x630 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V3 0x634 +#define PCIE_PHY_TXDEEMPH_M6DB_V4 0x638 +#define PCIE_PHY_TXDEEMPH_M3P5DB_V4 0x63C +#define PCIE_PHY_TXDEEMPH_M6DB_LS 0x640 +#define PCIE_PHY_TXDEEMPH_M3P5DB_LS 0x644 +#define PCIE_PHY_ENDPOINT_REFCLK_DRIVE 0x648 +#define PCIE_PHY_RX_IDLE_DTCT_CNTRL 0x64C +#define PCIE_PHY_POWER_STATE_CONFIG1 0x650 +#define PCIE_PHY_POWER_STATE_CONFIG2 0x654 +#define PCIE_PHY_POWER_STATE_CONFIG3 0x658 +#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_L 0x65C +#define PCIE_PHY_RCVR_DTCT_DLY_P1U2_H 0x660 +#define PCIE_PHY_RCVR_DTCT_DLY_U3_L 0x664 +#define PCIE_PHY_RCVR_DTCT_DLY_U3_H 0x668 +#define PCIE_PHY_LOCK_DETECT_CONFIG1 0x66C +#define PCIE_PHY_LOCK_DETECT_CONFIG2 0x670 +#define PCIE_PHY_LOCK_DETECT_CONFIG3 0x674 +#define PCIE_PHY_TSYNC_RSYNC_TIME 0x678 +#define PCIE_PHY_SIGDET_LOW_2_IDLE_TIME 0x67C +#define PCIE_PHY_BEACON_2_IDLE_TIME_L 0x680 +#define PCIE_PHY_BEACON_2_IDLE_TIME_H 0x684 +#define PCIE_PHY_PWRUP_RESET_DLY_TIME_SYSCLK 0x688 +#define PCIE_PHY_PWRUP_RESET_DLY_TIME_AUXCLK 0x68C +#define PCIE_PHY_LFPS_DET_HIGH_COUNT_VAL 0x690 +#define PCIE_PHY_LFPS_TX_ECSTART_EQTLOCK 0x694 +#define PCIE_PHY_LFPS_TX_END_CNT_P2U3_START 0x698 +#define PCIE_PHY_RXEQTRAINING_WAIT_TIME 0x69C +#define PCIE_PHY_RXEQTRAINING_RUN_TIME 0x6A0 +#define PCIE_PHY_TXONESZEROS_RUN_LENGTH 0x6A4 +#define PCIE_PHY_FLL_CNTRL1 0x6A8 +#define PCIE_PHY_FLL_CNTRL2 0x6AC +#define PCIE_PHY_FLL_CNT_VAL_L 0x6B0 +#define PCIE_PHY_FLL_CNT_VAL_H_TOL 0x6B4 +#define PCIE_PHY_FLL_MAN_CODE 0x6B8 +#define PCIE_PHY_AUTONOMOUS_MODE_CTRL 0x6BC +#define PCIE_PHY_LFPS_RXTERM_IRQ_CLEAR 0x6C0 +#define PCIE_PHY_ARCVR_DTCT_EN_PERIOD 0x6C4 +#define PCIE_PHY_ARCVR_DTCT_CM_DLY 0x6C8 +#define PCIE_PHY_ALFPS_DEGLITCH_VAL 0x6CC +#define PCIE_PHY_INSIG_SW_CTRL1 0x6D0 +#define PCIE_PHY_INSIG_SW_CTRL2 0x6D4 +#define PCIE_PHY_INSIG_SW_CTRL3 0x6D8 +#define PCIE_PHY_INSIG_MX_CTRL1 0x6DC +#define PCIE_PHY_INSIG_MX_CTRL2 0x6E0 +#define PCIE_PHY_INSIG_MX_CTRL3 0x6E4 +#define PCIE_PHY_TEST_CONTROL 0x6E8 +#define PCIE_PHY_BIST_CTRL 0x6EC +#define PCIE_PHY_PRBS_POLY0 0x6F0 +#define PCIE_PHY_PRBS_POLY1 0x6F4 +#define PCIE_PHY_PRBS_SEED0 0x6F8 +#define PCIE_PHY_PRBS_SEED1 0x6FC +#define PCIE_PHY_FIXED_PAT_CTRL 0x700 +#define PCIE_PHY_FIXED_PAT0 0x704 +#define PCIE_PHY_FIXED_PAT1 0x708 +#define PCIE_PHY_FIXED_PAT2 0x70C +#define PCIE_PHY_FIXED_PAT3 0x710 +#define PCIE_PHY_SPARE1 0x714 +#define PCIE_PHY_BIST_CHK_ERR_CNT_L 0x718 +#define PCIE_PHY_BIST_CHK_ERR_CNT_H 0x71C +#define PCIE_PHY_BIST_CHK_STATUS 0x720 +#define PCIE_PHY_LFPS_RXTERM_IRQ_SOURCE 0x724 +#define PCIE_PHY_PCS_STATUS 0x728 +#define PCIE_PHY_PCS_STATUS2 0x72C +#define PCIE_PHY_REVISION_ID0 0x730 +#define PCIE_PHY_REVISION_ID1 0x734 +#define PCIE_PHY_REVISION_ID2 0x738 +#define PCIE_PHY_REVISION_ID3 0x73C +#define PCIE_PHY_DEBUG_BUS_0_STATUS 0x740 +#define PCIE_PHY_DEBUG_BUS_1_STATUS 0x744 +#define PCIE_PHY_DEBUG_BUS_2_STATUS 0x748 +#define PCIE_PHY_DEBUG_BUS_3_STATUS 0x74C +#endif diff --git a/include/linux/msm_ep_pcie.h b/include/linux/msm_ep_pcie.h new file mode 100644 index 000000000000..6d02bdb628bc --- /dev/null +++ b/include/linux/msm_ep_pcie.h @@ -0,0 +1,254 @@ +/* Copyright (c) 2015, 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, +}; + +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_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_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)(void); + 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); +}; + +/* + * 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 + * + * 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); + +/* + * 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); +#endif