Messages in this thread | | | Subject | Re: [PATCH v4 2/3] dwc: PCI: intel: PCIe RC controller driver | From | Dilip Kota <> | Date | Tue, 22 Oct 2019 18:18:57 +0800 |
| |
Hi Gustavo Pimentel,
On 10/21/2019 6:44 PM, Dilip Kota wrote: > Hi Gustavo Pimentel, > > On 10/21/2019 4:29 PM, Gustavo Pimentel wrote: >> Hi >> >> On Mon, Oct 21, 2019 at 7:39:19, Dilip Kota >> <eswara.kota@linux.intel.com> >> wrote: >> >>> Add support to PCIe RC controller on Intel Gateway SoCs. >>> PCIe controller is based of Synopsys DesignWare pci core. >>> >>> Intel PCIe driver requires Upconfig support, fast training >>> sequence configuration and link speed change. So adding the >>> respective helper functions in the pcie DesignWare framework. >>> It also programs hardware autonomous speed during speed >>> configuration so defining it in pci_regs.h. >> Please do the replacement in all of your patches >> >> s/pcie/PCIe >> s/pci/PCI >> >> Also I think the correct term is Upconfigure and not Upconfig > Yes, i will update it. >> >>> Changes on v4: >>> Rename the driver naming and description to >>> "PCIe RC controller on Intel Gateway SoCs". >>> Use PCIe core register macros defined in pci_regs.h >>> and remove respective local definitions. >>> Remove pcie core interrupt handling. >>> Move pcie link control speed change, upconfig and FTS. >>> configuration functions to DesignWare framework. >>> Use of_pci_get_max_link_speed(). >>> Mark dependency on X86 and COMPILE_TEST in Kconfig. >>> Remove lanes and add n_fts variables in intel_pcie_port structure. >>> Rename rst_interval variable to rst_intrvl in intel_pcie_port >>> structure. >>> Remove intel_pcie_mem_iatu() as it is already perfomed in >>> dw_setup_rc() >>> Move sysfs attributes specific code to separate patch. >>> Remove redundant error handling. >>> Reduce LoCs by doing variable initializations while declaration >>> itself. >>> Add extra line after closing parenthesis. >>> Move intel_pcie_ep_rst_init() out of get_resources() >>> >>> changes on v3: >>> Rename PCIe app logic registers with PCIE_APP prefix. >>> PCIE_IOP_CTRL configuration is not required. Remove respective >>> code. >>> Remove wrapper functions for clk enable/disable APIs. >>> Use platform_get_resource_byname() instead of >>> devm_platform_ioremap_resource() to be similar with DWC >>> framework. >>> Rename phy name to "pciephy". >>> Modify debug message in msi_init() callback to be more specific. >>> Remove map_irq() callback. >>> Enable the INTx interrupts at the time of PCIe initialization. >>> Reduce memory fragmentation by using variable "struct dw_pcie pci" >>> instead of allocating memory. >>> Reduce the delay to 100us during enpoint initialization >>> intel_pcie_ep_rst_init(). >>> Call dw_pcie_host_deinit() during remove() instead of directly >>> calling PCIe core APIs. >>> Rename "intel,rst-interval" to "reset-assert-ms". >>> Remove unused APP logic Interrupt bit macro definitions. >>> Use dwc framework's dw_pcie_setup_rc() for PCIe host specific >>> configuration instead of redefining the same functionality in >>> the driver. >>> Move the whole DT parsing specific code to >>> intel_pcie_get_resources() >>> >>> Signed-off-by: Dilip Kota <eswara.kota@linux.intel.com> >>> --- >>> drivers/pci/controller/dwc/Kconfig | 10 + >>> drivers/pci/controller/dwc/Makefile | 1 + >>> drivers/pci/controller/dwc/pcie-designware.c | 34 ++ >>> drivers/pci/controller/dwc/pcie-designware.h | 12 + >>> drivers/pci/controller/dwc/pcie-intel-gw.c | 590 >>> +++++++++++++++++++++++++++ >>> include/uapi/linux/pci_regs.h | 1 + >>> 6 files changed, 648 insertions(+) >>> create mode 100644 drivers/pci/controller/dwc/pcie-intel-gw.c >>> >>> diff --git a/drivers/pci/controller/dwc/Kconfig >>> b/drivers/pci/controller/dwc/Kconfig >>> index 0ba988b5b5bc..b33ed1cc873d 100644 >>> --- a/drivers/pci/controller/dwc/Kconfig >>> +++ b/drivers/pci/controller/dwc/Kconfig >>> @@ -82,6 +82,16 @@ config PCIE_DW_PLAT_EP >>> order to enable device-specific features PCI_DW_PLAT_EP must be >>> selected. >>> +config PCIE_INTEL_GW >>> + bool "Intel Gateway PCIe host controller support" >>> + depends on OF && (X86 || COMPILE_TEST) >>> + select PCIE_DW_HOST >>> + help >>> + Say 'Y' here to enable support for PCIe Host controller >>> driver. >>> + The PCIe controller on Intel Gateway SoCs is based on the >>> Synopsys >>> + DesignWare pcie core and therefore uses the DesignWare core >>> + functions for the driver implementation. >>> + >>> config PCI_EXYNOS >>> bool "Samsung Exynos PCIe controller" >>> depends on SOC_EXYNOS5440 || COMPILE_TEST >>> diff --git a/drivers/pci/controller/dwc/Makefile >>> b/drivers/pci/controller/dwc/Makefile >>> index b30336181d46..99db83cd2f35 100644 >>> --- a/drivers/pci/controller/dwc/Makefile >>> +++ b/drivers/pci/controller/dwc/Makefile >>> @@ -3,6 +3,7 @@ obj-$(CONFIG_PCIE_DW) += pcie-designware.o >>> obj-$(CONFIG_PCIE_DW_HOST) += pcie-designware-host.o >>> obj-$(CONFIG_PCIE_DW_EP) += pcie-designware-ep.o >>> obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o >>> +obj-$(CONFIG_PCIE_INTEL_GW) += pcie-intel-gw.o >>> obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o >>> obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o >>> obj-$(CONFIG_PCI_IMX6) += pci-imx6.o >>> diff --git a/drivers/pci/controller/dwc/pcie-designware.c >>> b/drivers/pci/controller/dwc/pcie-designware.c >>> index 820488dfeaed..4c391bfd681a 100644 >>> --- a/drivers/pci/controller/dwc/pcie-designware.c >>> +++ b/drivers/pci/controller/dwc/pcie-designware.c >>> @@ -474,6 +474,40 @@ int dw_pcie_link_up(struct dw_pcie *pci) >>> (!(val & PCIE_PORT_DEBUG1_LINK_IN_TRAINING))); >>> } >>> + >>> +void dw_pcie_upconfig_setup(struct dw_pcie *pci) >>> +{ >>> + u32 val; >>> + >>> + val = dw_pcie_readl_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL); >>> + dw_pcie_writel_dbi(pci, PCIE_PORT_MULTI_LANE_CTRL, >>> + val | PORT_MLTI_UPCFG_SUPPORT); >>> +} >>> + >>> +void dw_pcie_link_speed_change(struct dw_pcie *pci, bool enable) >>> +{ >>> + u32 val; >>> + >>> + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); >>> + >>> + if (enable) >>> + val |= PORT_LOGIC_SPEED_CHANGE; >>> + else >>> + val &= ~PORT_LOGIC_SPEED_CHANGE; >>> + >>> + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); >>> +} >>> + >>> +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts) >>> +{ >>> + u32 val; >>> + >>> + val = dw_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL); >>> + val &= ~PORT_LOGIC_N_FTS; >>> + val |= n_fts; >>> + dw_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val); >>> +} >>> + >>> static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci) >>> { >>> u32 val; >>> diff --git a/drivers/pci/controller/dwc/pcie-designware.h >>> b/drivers/pci/controller/dwc/pcie-designware.h >>> index 5a18e94e52c8..3beac10e4a4c 100644 >>> --- a/drivers/pci/controller/dwc/pcie-designware.h >>> +++ b/drivers/pci/controller/dwc/pcie-designware.h >>> @@ -30,7 +30,12 @@ >>> #define LINK_WAIT_IATU 9 >>> /* Synopsys-specific PCIe configuration registers */ >>> +#define PCIE_PORT_AFR 0x70C >>> +#define PORT_AFR_N_FTS_MASK GENMASK(15, 8) >>> +#define PORT_AFR_CC_N_FTS_MASK GENMASK(23, 16) >>> + >>> #define PCIE_PORT_LINK_CONTROL 0x710 >>> +#define PORT_LINK_DLL_LINK_EN BIT(5) >>> #define PORT_LINK_MODE_MASK GENMASK(21, 16) >>> #define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n) >>> #define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) >>> @@ -46,6 +51,7 @@ >>> #define PCIE_PORT_DEBUG1_LINK_IN_TRAINING BIT(29) >>> #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C >>> +#define PORT_LOGIC_N_FTS GENMASK(7, 0) >>> #define PORT_LOGIC_SPEED_CHANGE BIT(17) >>> #define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8) >>> #define PORT_LOGIC_LINK_WIDTH(n) >>> FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) >>> @@ -60,6 +66,9 @@ >>> #define PCIE_MSI_INTR0_MASK 0x82C >>> #define PCIE_MSI_INTR0_STATUS 0x830 >>> +#define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 >>> +#define PORT_MLTI_UPCFG_SUPPORT BIT(7) >>> + >>> #define PCIE_ATU_VIEWPORT 0x900 >>> #define PCIE_ATU_REGION_INBOUND BIT(31) >>> #define PCIE_ATU_REGION_OUTBOUND 0 >>> @@ -273,6 +282,9 @@ void dw_pcie_write_dbi2(struct dw_pcie *pci, u32 >>> reg, size_t size, u32 val); >>> u32 dw_pcie_read_atu(struct dw_pcie *pci, u32 reg, size_t size); >>> void dw_pcie_write_atu(struct dw_pcie *pci, u32 reg, size_t size, >>> u32 val); >>> int dw_pcie_link_up(struct dw_pcie *pci); >>> +void dw_pcie_upconfig_setup(struct dw_pcie *pci); >>> +void dw_pcie_link_speed_change(struct dw_pcie *pci, bool enable); >>> +void dw_pcie_link_set_n_fts(struct dw_pcie *pci, u32 n_fts); >>> int dw_pcie_wait_for_link(struct dw_pcie *pci); >>> void dw_pcie_prog_outbound_atu(struct dw_pcie *pci, int index, >>> int type, u64 cpu_addr, u64 pci_addr, >>> diff --git a/drivers/pci/controller/dwc/pcie-intel-gw.c >>> b/drivers/pci/controller/dwc/pcie-intel-gw.c >>> new file mode 100644 >>> index 000000000000..9142c70db808 >>> --- /dev/null >>> +++ b/drivers/pci/controller/dwc/pcie-intel-gw.c >>> @@ -0,0 +1,590 @@ >>> +// SPDX-License-Identifier: GPL-2.0 >>> +/* >>> + * PCIe host controller driver for Intel Gateway SoCs >>> + * >>> + * Copyright (c) 2019 Intel Corporation. >>> + */ >>> + >>> +#include <linux/bitfield.h> >>> +#include <linux/clk.h> >>> +#include <linux/gpio/consumer.h> >>> +#include <linux/interrupt.h> >>> +#include <linux/iopoll.h> >>> +#include <linux/of_irq.h> >>> +#include <linux/of_pci.h> >>> +#include <linux/of_platform.h> >>> +#include <linux/pci_regs.h> >>> +#include <linux/phy/phy.h> >>> +#include <linux/platform_device.h> >>> +#include <linux/reset.h> >>> + >>> +#include "../../pci.h" >>> +#include "pcie-designware.h" >>> + >>> +#define PCIE_CAP_OFST 0x70 >>> + >>> +#define PORT_AFR_N_FTS_GEN12_DFT (SZ_128 - 1) >>> +#define PORT_AFR_N_FTS_GEN3 180 >>> +#define PORT_AFR_N_FTS_GEN4 196 >>> + >>> +/* PCIe Application logic Registers */ >>> +#define PCIE_APP_CCR 0x10 >>> +#define PCIE_APP_CCR_LTSSM_ENABLE BIT(0) >>> + >>> +#define PCIE_APP_MSG_CR 0x30 >>> +#define PCIE_APP_MSG_XMT_PM_TURNOFF BIT(0) >>> + >>> +#define PCIE_APP_PMC 0x44 >>> +#define PCIE_APP_PMC_IN_L2 BIT(20) >>> + >>> +#define PCIE_APP_IRNEN 0xF4 >>> +#define PCIE_APP_IRNCR 0xF8 >>> +#define PCIE_APP_IRN_AER_REPORT BIT(0) >>> +#define PCIE_APP_IRN_PME BIT(2) >>> +#define PCIE_APP_IRN_RX_VDM_MSG BIT(4) >>> +#define PCIE_APP_IRN_PM_TO_ACK BIT(9) >>> +#define PCIE_APP_IRN_LINK_AUTO_BW_STAT BIT(11) >>> +#define PCIE_APP_IRN_BW_MGT BIT(12) >>> +#define PCIE_APP_IRN_MSG_LTR BIT(18) >>> +#define PCIE_APP_IRN_SYS_ERR_RC BIT(29) >>> +#define PCIE_APP_INTX_OFST 12 >>> + >>> +#define PCIE_APP_IRN_INT \ >>> + (PCIE_APP_IRN_AER_REPORT | PCIE_APP_IRN_PME | \ >>> + PCIE_APP_IRN_RX_VDM_MSG | PCIE_APP_IRN_SYS_ERR_RC | \ >>> + PCIE_APP_IRN_PM_TO_ACK | PCIE_APP_IRN_MSG_LTR | \ >>> + PCIE_APP_IRN_BW_MGT | PCIE_APP_IRN_LINK_AUTO_BW_STAT | \ >>> + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTA) | \ >>> + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTB) | \ >>> + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTC) | \ >>> + (PCIE_APP_INTX_OFST + PCI_INTERRUPT_INTD)) >>> + >>> +#define BUS_IATU_OFFS SZ_256M >>> +#define RST_INTRVL_DFT_MS 100 >>> + >>> +enum { >>> + PCIE_LINK_SPEED_AUTO = 0, >>> + PCIE_LINK_SPEED_GEN1, >>> + PCIE_LINK_SPEED_GEN2, >>> + PCIE_LINK_SPEED_GEN3, >>> + PCIE_LINK_SPEED_GEN4, >>> +}; >>> + >>> +struct intel_pcie_soc { >>> + unsigned int pcie_ver; >>> + unsigned int pcie_atu_offset; >>> + u32 num_viewport; >>> +}; >>> + >>> +struct intel_pcie_port { >>> + struct dw_pcie pci; >>> + unsigned int id; /* Physical RC Index */ >>> + void __iomem *app_base; >>> + struct gpio_desc *reset_gpio; >>> + u32 rst_intrvl; >>> + u32 max_speed; >>> + u32 link_gen; >>> + u32 max_width; >>> + u32 n_fts; >>> + struct clk *core_clk; >>> + struct reset_control *core_rst; >>> + struct phy *phy; >>> +}; >>> + >>> +static void pcie_update_bits(void __iomem *base, u32 mask, u32 val, >>> u32 ofs) >>> +{ >>> + u32 orig, tmp; >>> + >>> + orig = readl(base + ofs); >>> + >>> + tmp = (orig & ~mask) | (val & mask); >>> + >>> + if (tmp != orig) >>> + writel(tmp, base + ofs); >>> +} >> I'd suggest to the a take on FIELD_PREP() and FIELD_GET() and use more >> intuitive names such as new and old, instead of orig and tmp. > Sure, i will update it. I tried using FIELD_PREP and FIELD_GET but it is failing because FIELD_PREP and FIELD_GET are expecting mask should be constant macro. u32 or u32 const are not accepted.
Regards, Dilip
>> >>> +static inline u32 pcie_app_rd(struct intel_pcie_port *lpp, u32 ofs) >>> +{ >>> + return readl(lpp->app_base + ofs); >>> +} >>> + >>> +static inline void pcie_app_wr(struct intel_pcie_port *lpp, u32 >>> val, u32 ofs) >>> +{ >>> + writel(val, lpp->app_base + ofs); >>> +} >>> + >>> +static void pcie_app_wr_mask(struct intel_pcie_port *lpp, >>> + u32 mask, u32 val, u32 ofs) >>> +{ >>> + pcie_update_bits(lpp->app_base, mask, val, ofs); >>> +} >>> + >>> +static inline u32 pcie_rc_cfg_rd(struct intel_pcie_port *lpp, u32 ofs) >>> +{ >>> + return dw_pcie_readl_dbi(&lpp->pci, ofs); >>> +} >>> + >>> +static inline void pcie_rc_cfg_wr(struct intel_pcie_port *lpp, u32 >>> val, u32 ofs) >>> +{ >>> + dw_pcie_writel_dbi(&lpp->pci, ofs, val); >>> +} >>> + >>> +static void pcie_rc_cfg_wr_mask(struct intel_pcie_port *lpp, >>> + u32 mask, u32 val, u32 ofs) >>> +{ >>> + pcie_update_bits(lpp->pci.dbi_base, mask, val, ofs); >>> +} >>> + >>> +static void intel_pcie_ltssm_enable(struct intel_pcie_port *lpp) >>> +{ >>> + pcie_app_wr_mask(lpp, PCIE_APP_CCR_LTSSM_ENABLE, >>> + PCIE_APP_CCR_LTSSM_ENABLE, PCIE_APP_CCR); >>> +} >>> + >>> +static void intel_pcie_ltssm_disable(struct intel_pcie_port *lpp) >>> +{ >>> + pcie_app_wr_mask(lpp, PCIE_APP_CCR_LTSSM_ENABLE, 0, PCIE_APP_CCR); >>> +} >>> + >>> +static void intel_pcie_link_setup(struct intel_pcie_port *lpp) >>> +{ >>> + u32 val; >>> + >>> + val = pcie_rc_cfg_rd(lpp, PCIE_CAP_OFST + PCI_EXP_LNKCAP); >>> + lpp->max_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); >>> + lpp->max_width = FIELD_GET(PCI_EXP_LNKCAP_MLW, val); >>> + >>> + val = pcie_rc_cfg_rd(lpp, PCIE_CAP_OFST + PCI_EXP_LNKCTL); >>> + >>> + val &= ~(PCI_EXP_LNKCTL_LD | PCI_EXP_LNKCTL_ASPMC); >>> + val |= (PCI_EXP_LNKSTA_SLC << 16) | PCI_EXP_LNKCTL_CCC | >>> + PCI_EXP_LNKCTL_RCB; >>> + pcie_rc_cfg_wr(lpp, val, PCIE_CAP_OFST + PCI_EXP_LNKCTL); >>> +} >>> + >>> +static void intel_pcie_max_speed_setup(struct intel_pcie_port *lpp) >>> +{ >>> + u32 reg, val; >>> + >>> + reg = pcie_rc_cfg_rd(lpp, PCIE_CAP_OFST + PCI_EXP_LNKCTL2); >>> + switch (lpp->link_gen) { >>> + case PCIE_LINK_SPEED_GEN1: >>> + reg &= ~PCI_EXP_LNKCTL2_TLS; >>> + reg |= PCI_EXP_LNKCTL2_HASD| >>> + PCI_EXP_LNKCTL2_TLS_2_5GT; >>> + break; >>> + case PCIE_LINK_SPEED_GEN2: >>> + reg &= ~PCI_EXP_LNKCTL2_TLS; >>> + reg |= PCI_EXP_LNKCTL2_HASD| >>> + PCI_EXP_LNKCTL2_TLS_5_0GT; >>> + break; >>> + case PCIE_LINK_SPEED_GEN3: >>> + reg &= ~PCI_EXP_LNKCTL2_TLS; >>> + reg |= PCI_EXP_LNKCTL2_HASD| >>> + PCI_EXP_LNKCTL2_TLS_8_0GT; >>> + break; >>> + case PCIE_LINK_SPEED_GEN4: >>> + reg &= ~PCI_EXP_LNKCTL2_TLS; >>> + reg |= PCI_EXP_LNKCTL2_HASD| >>> + PCI_EXP_LNKCTL2_TLS_16_0GT; >>> + break; >>> + default: >>> + /* Use hardware capability */ >>> + val = pcie_rc_cfg_rd(lpp, PCIE_CAP_OFST + PCI_EXP_LNKCAP); >>> + val = FIELD_GET(PCI_EXP_LNKCAP_SLS, val); >>> + reg &= ~PCI_EXP_LNKCTL2_HASD; >>> + reg |= val; >>> + break; >>> + } >>> + >>> + pcie_rc_cfg_wr(lpp, reg, PCIE_CAP_OFST + PCI_EXP_LNKCTL2); >>> + dw_pcie_link_set_n_fts(&lpp->pci, lpp->n_fts); >>> +} >>> + >>> + >> Reduce the number of empty lines here. > Ok, will remove it. >> >>> + >>> +static void intel_pcie_port_logic_setup(struct intel_pcie_port *lpp) >>> +{ >>> + u32 val, mask; >>> + >>> + switch (lpp->max_speed) { >>> + case PCIE_LINK_SPEED_GEN3: >>> + lpp->n_fts = PORT_AFR_N_FTS_GEN3; >>> + break; >>> + case PCIE_LINK_SPEED_GEN4: >>> + lpp->n_fts = PORT_AFR_N_FTS_GEN4; >>> + break; >>> + default: >>> + lpp->n_fts = PORT_AFR_N_FTS_GEN12_DFT; >>> + break; >>> + } >>> + >>> + mask = PORT_AFR_N_FTS_MASK | PORT_AFR_CC_N_FTS_MASK; >>> + val = FIELD_PREP(PORT_AFR_N_FTS_MASK, lpp->n_fts) | >>> + FIELD_PREP(PORT_AFR_CC_N_FTS_MASK, lpp->n_fts); >>> + pcie_rc_cfg_wr_mask(lpp, mask, val, PCIE_PORT_AFR); >>> + >>> + /* Port Link Control Register */ >>> + pcie_rc_cfg_wr_mask(lpp, PORT_LINK_DLL_LINK_EN, >>> + PORT_LINK_DLL_LINK_EN, PCIE_PORT_LINK_CONTROL); >>> +} >>> + >>> +static void intel_pcie_rc_setup(struct intel_pcie_port *lpp) >>> +{ >>> + intel_pcie_ltssm_disable(lpp); >>> + intel_pcie_link_setup(lpp); >>> + dw_pcie_setup_rc(&lpp->pci.pp); >>> + dw_pcie_upconfig_setup(&lpp->pci); >>> + intel_pcie_port_logic_setup(lpp); >>> + intel_pcie_max_speed_setup(lpp); >>> +} >>> + >>> +static int intel_pcie_ep_rst_init(struct intel_pcie_port *lpp) >>> +{ >>> + struct device *dev = lpp->pci.dev; >>> + int ret; >>> + >>> + lpp->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); >>> + if (IS_ERR(lpp->reset_gpio)) { >>> + ret = PTR_ERR(lpp->reset_gpio); >>> + if (ret != -EPROBE_DEFER) >>> + dev_err(dev, "failed to request PCIe GPIO: %d\n", ret); >>> + return ret; >>> + } >>> + >>> + /* Make initial reset last for 100us */ >>> + usleep_range(100, 200); >>> + >>> + return 0; >>> +} >>> + >>> +static void intel_pcie_core_rst_assert(struct intel_pcie_port *lpp) >>> +{ >>> + reset_control_assert(lpp->core_rst); >>> +} >>> + >>> +static void intel_pcie_core_rst_deassert(struct intel_pcie_port *lpp) >>> +{ >>> + /* >>> + * One micro-second delay to make sure the reset pulse >>> + * wide enough so that core reset is clean. >>> + */ >>> + udelay(1); >>> + reset_control_deassert(lpp->core_rst); >>> + >>> + /* >>> + * Some SoC core reset also reset PHY, more delay needed >>> + * to make sure the reset process is done. >>> + */ >>> + usleep_range(1000, 2000); >>> +} >>> + >>> +static void intel_pcie_device_rst_assert(struct intel_pcie_port *lpp) >>> +{ >>> + gpiod_set_value_cansleep(lpp->reset_gpio, 1); >>> +} >>> + >>> +static void intel_pcie_device_rst_deassert(struct intel_pcie_port >>> *lpp) >>> +{ >>> + msleep(lpp->rst_intrvl); >>> + gpiod_set_value_cansleep(lpp->reset_gpio, 0); >>> +} >>> + >>> +static int intel_pcie_app_logic_setup(struct intel_pcie_port *lpp) >>> +{ >>> + intel_pcie_device_rst_deassert(lpp); >>> + intel_pcie_ltssm_enable(lpp); >>> + >>> + return dw_pcie_wait_for_link(&lpp->pci); >>> +} >>> + >>> +static void intel_pcie_core_irq_disable(struct intel_pcie_port *lpp) >>> +{ >>> + pcie_app_wr(lpp, 0, PCIE_APP_IRNEN); >>> + pcie_app_wr(lpp, PCIE_APP_IRN_INT, PCIE_APP_IRNCR); >>> +} >>> + >>> +static int intel_pcie_get_resources(struct platform_device *pdev) >>> +{ >>> + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); >>> + struct dw_pcie *pci = &lpp->pci; >>> + struct device *dev = pci->dev; >>> + struct resource *res; >>> + int ret; >>> + >>> + ret = device_property_read_u32(dev, "linux,pci-domain", &lpp->id); >>> + if (ret) { >>> + dev_err(dev, "failed to get domain id, errno %d\n", ret); >>> + return ret; >>> + } >>> + >>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); >>> + >>> + pci->dbi_base = devm_ioremap_resource(dev, res); >>> + if (IS_ERR(pci->dbi_base)) >>> + return PTR_ERR(pci->dbi_base); >>> + >>> + lpp->core_clk = devm_clk_get(dev, NULL); >>> + if (IS_ERR(lpp->core_clk)) { >>> + ret = PTR_ERR(lpp->core_clk); >>> + if (ret != -EPROBE_DEFER) >>> + dev_err(dev, "failed to get clks: %d\n", ret); >>> + return ret; >>> + } >>> + >>> + lpp->core_rst = devm_reset_control_get(dev, NULL); >>> + if (IS_ERR(lpp->core_rst)) { >>> + ret = PTR_ERR(lpp->core_rst); >>> + if (ret != -EPROBE_DEFER) >>> + dev_err(dev, "failed to get resets: %d\n", ret); >>> + return ret; >>> + } >>> + >>> + ret = device_property_match_string(dev, "device_type", "pci"); >>> + if (ret) { >>> + dev_err(dev, "failed to find pci device type: %d\n", ret); >>> + return ret; >>> + } >>> + >>> + if (device_property_read_u32(dev, "reset-assert-ms", >>> &lpp->rst_intrvl)) >>> + lpp->rst_intrvl = RST_INTRVL_DFT_MS; >>> + >>> + ret = of_pci_get_max_link_speed(dev->of_node); >>> + lpp->link_gen = ret < 0 ? 0 : ret; >>> + >>> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "app"); >>> + >>> + lpp->app_base = devm_ioremap_resource(dev, res); >>> + if (IS_ERR(lpp->app_base)) >>> + return PTR_ERR(lpp->app_base); >>> + >>> + lpp->phy = devm_phy_get(dev, "pcie"); >>> + if (IS_ERR(lpp->phy)) { >>> + ret = PTR_ERR(lpp->phy); >>> + if (ret != -EPROBE_DEFER) >>> + dev_err(dev, "couldn't get pcie-phy: %d\n", ret); >>> + return ret; >>> + } >>> + >>> + return 0; >>> +} >>> + >>> +static void intel_pcie_deinit_phy(struct intel_pcie_port *lpp) >>> +{ >>> + phy_exit(lpp->phy); >>> +} >>> + >>> +static int intel_pcie_wait_l2(struct intel_pcie_port *lpp) >>> +{ >>> + u32 value; >>> + int ret; >>> + >>> + if (lpp->max_speed < PCIE_LINK_SPEED_GEN3) >>> + return 0; >>> + >>> + /* Send PME_TURN_OFF message */ >>> + pcie_app_wr_mask(lpp, PCIE_APP_MSG_XMT_PM_TURNOFF, >>> + PCIE_APP_MSG_XMT_PM_TURNOFF, PCIE_APP_MSG_CR); >>> + >>> + /* Read PMC status and wait for falling into L2 link state */ >>> + ret = readl_poll_timeout(lpp->app_base + PCIE_APP_PMC, value, >>> + (value & PCIE_APP_PMC_IN_L2), 20, >>> + jiffies_to_usecs(5 * HZ)); >>> + if (ret) >>> + dev_err(lpp->pci.dev, "PCIe link enter L2 timeout!\n"); >>> + >>> + return ret; >>> +} >>> + >>> +static void intel_pcie_turn_off(struct intel_pcie_port *lpp) >>> +{ >>> + if (dw_pcie_link_up(&lpp->pci)) >>> + intel_pcie_wait_l2(lpp); >>> + >>> + /* Put endpoint device in reset state */ >>> + intel_pcie_device_rst_assert(lpp); >>> + pcie_rc_cfg_wr_mask(lpp, PCI_COMMAND_MEMORY, 0, PCI_COMMAND); >>> +} >>> + >>> +static int intel_pcie_host_setup(struct intel_pcie_port *lpp) >>> +{ >>> + int ret; >>> + >>> + intel_pcie_core_rst_assert(lpp); >>> + intel_pcie_device_rst_assert(lpp); >>> + >>> + ret = phy_init(lpp->phy); >>> + if (ret) >>> + return ret; >>> + >>> + intel_pcie_core_rst_deassert(lpp); >>> + >>> + ret = clk_prepare_enable(lpp->core_clk); >>> + if (ret) { >>> + dev_err(lpp->pci.dev, "Core clock enable failed: %d\n", ret); >>> + goto clk_err; >>> + } >>> + >>> + intel_pcie_rc_setup(lpp); >>> + ret = intel_pcie_app_logic_setup(lpp); >>> + if (ret) >>> + goto app_init_err; >>> + >>> + /* Enable integrated interrupts */ >>> + pcie_app_wr_mask(lpp, PCIE_APP_IRN_INT, PCIE_APP_IRN_INT, >>> + PCIE_APP_IRNEN); >>> + return 0; >>> + >>> +app_init_err: >>> + clk_disable_unprepare(lpp->core_clk); >>> +clk_err: >>> + intel_pcie_core_rst_assert(lpp); >>> + intel_pcie_deinit_phy(lpp); >>> + >>> + return ret; >>> +} >>> + >>> +static void __intel_pcie_remove(struct intel_pcie_port *lpp) >>> +{ >>> + intel_pcie_core_irq_disable(lpp); >>> + intel_pcie_turn_off(lpp); >>> + clk_disable_unprepare(lpp->core_clk); >>> + intel_pcie_core_rst_assert(lpp); >>> + intel_pcie_deinit_phy(lpp); >>> +} >>> + >>> +static int intel_pcie_remove(struct platform_device *pdev) >>> +{ >>> + struct intel_pcie_port *lpp = platform_get_drvdata(pdev); >>> + struct pcie_port *pp = &lpp->pci.pp; >>> + >>> + dw_pcie_host_deinit(pp); >>> + __intel_pcie_remove(lpp); >>> + >>> + return 0; >>> +} >>> + >>> +static int __maybe_unused intel_pcie_suspend_noirq(struct device *dev) >>> +{ >>> + struct intel_pcie_port *lpp = dev_get_drvdata(dev); >>> + int ret; >>> + >>> + intel_pcie_core_irq_disable(lpp); >>> + ret = intel_pcie_wait_l2(lpp); >>> + if (ret) >>> + return ret; >>> + >>> + intel_pcie_deinit_phy(lpp); >>> + clk_disable_unprepare(lpp->core_clk); >>> + return ret; >>> +} >>> + >>> +static int __maybe_unused intel_pcie_resume_noirq(struct device *dev) >>> +{ >>> + struct intel_pcie_port *lpp = dev_get_drvdata(dev); >>> + >>> + return intel_pcie_host_setup(lpp); >>> +} >>> + >>> +static int intel_pcie_rc_init(struct pcie_port *pp) >>> +{ >>> + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); >>> + struct intel_pcie_port *lpp = dev_get_drvdata(pci->dev); >>> + >>> + return intel_pcie_host_setup(lpp); >>> +} >>> + >>> +int intel_pcie_msi_init(struct pcie_port *pp) >>> +{ >>> + /* PCIe MSI/MSIx is handled by MSI in x86 processor */ >>> + return 0; >>> +} >>> + >>> +u64 intel_pcie_cpu_addr(struct dw_pcie *pcie, u64 cpu_addr) >>> +{ >>> + return cpu_addr + BUS_IATU_OFFS; >>> +} >>> + >>> +static const struct dw_pcie_ops intel_pcie_ops = { >>> + .cpu_addr_fixup = intel_pcie_cpu_addr, >>> +}; >>> + >>> +static const struct dw_pcie_host_ops intel_pcie_dw_ops = { >>> + .host_init = intel_pcie_rc_init, >>> + .msi_host_init = intel_pcie_msi_init, >>> +}; >>> + >>> +static const struct intel_pcie_soc pcie_data = { >>> + .pcie_ver = 0x520A, >>> + .pcie_atu_offset = 0xC0000, >>> + .num_viewport = 3, >>> +}; >>> + >>> +static int intel_pcie_probe(struct platform_device *pdev) >>> +{ >>> + const struct intel_pcie_soc *data; >>> + struct device *dev = &pdev->dev; >>> + struct intel_pcie_port *lpp; >>> + struct pcie_port *pp; >>> + struct dw_pcie *pci; >>> + int ret; >>> + >>> + lpp = devm_kzalloc(dev, sizeof(*lpp), GFP_KERNEL); >>> + if (!lpp) >>> + return -ENOMEM; >>> + >>> + platform_set_drvdata(pdev, lpp); >>> + pci = &lpp->pci; >>> + pci->dev = dev; >>> + pp = &pci->pp; >>> + >>> + ret = intel_pcie_get_resources(pdev); >>> + if (ret) >>> + return ret; >>> + >>> + ret = intel_pcie_ep_rst_init(lpp); >>> + if (ret) >>> + return ret; >>> + >>> + data = device_get_match_data(dev); >>> + pci->ops = &intel_pcie_ops; >>> + pci->version = data->pcie_ver; >>> + pci->atu_base = pci->dbi_base + data->pcie_atu_offset; >>> + pp->ops = &intel_pcie_dw_ops; >>> + >>> + ret = dw_pcie_host_init(pp); >>> + if (ret) { >>> + dev_err(dev, "cannot initialize host\n"); >>> + return ret; >>> + } >>> + >>> + /* Intel PCIe doesn't configure IO region, so configure >>> + * viewport to not to access IO region during register >>> + * read write operations. >>> + */ >>> + pci->num_viewport = data->num_viewport; >>> + dev_info(dev, "Intel PCIe Root Complex Port %d init done\n", >>> lpp->id); >>> + >>> + return ret; >>> +} >>> + >>> +static const struct dev_pm_ops intel_pcie_pm_ops = { >>> + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(intel_pcie_suspend_noirq, >>> + intel_pcie_resume_noirq) >>> +}; >>> + >>> +static const struct of_device_id of_intel_pcie_match[] = { >>> + { .compatible = "intel,lgm-pcie", .data = &pcie_data }, >>> + {} >>> +}; >>> + >>> +static struct platform_driver intel_pcie_driver = { >>> + .probe = intel_pcie_probe, >>> + .remove = intel_pcie_remove, >>> + .driver = { >>> + .name = "intel-gw-pcie", >>> + .of_match_table = of_intel_pcie_match, >>> + .pm = &intel_pcie_pm_ops, >>> + }, >>> +}; >>> +builtin_platform_driver(intel_pcie_driver); >>> diff --git a/include/uapi/linux/pci_regs.h >>> b/include/uapi/linux/pci_regs.h >>> index 29d6e93fd15e..f6e7e402f879 100644 >>> --- a/include/uapi/linux/pci_regs.h >>> +++ b/include/uapi/linux/pci_regs.h >>> @@ -673,6 +673,7 @@ >>> #define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed >>> 8GT/s */ >>> #define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed >>> 16GT/s */ >>> #define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed >>> 32GT/s */ >>> +#define PCI_EXP_LNKCTL2_HASD 0x0200 /* Hw Autonomous Speed >>> Disable */ >> s/Hw/HW > > Sure, i will correct it. > > Thanks for the valuable inputs. > > Regards, > Dilip > >> >>> #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ >>> #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints >>> with link end here */ >>> #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ >>> -- >>> 2.11.0 >>
| |