| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (C) 2018 ASR Microelectronics(Shanghai) Co., Ltd. | |
| * | |
| * Copyright (C) 2018 Yu Zhang <yuzhang@asrmicro.com> | |
| * | |
| * Based on upstream Linux kernel driver: | |
| * pcie-nz3.c: Yu Zhang <yuzhang@asrmicro.com> | |
| * pcie-designware.c: Jingoo Han <jg1.han@samsung.com> | |
| */ | |
| #include <common.h> | |
| #include <pci.h> | |
| #include <asm/io.h> | |
| #include <asm-generic/gpio.h> | |
| DECLARE_GLOBAL_DATA_PTR; | |
| #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) | |
| #define lower_32_bits(n) ((u32)(n)) | |
| #define PCIE_DW_NZ3_DBI_BASE 0xd4209000 | |
| #define PCIE_DW_NZ3_LANES 1 | |
| #define NZ3_PCIE_PREF_BASE 0xE8000000 | |
| #define NZ3_PCIE_PREF_SIZE 0x00100000 | |
| #define NZ3_PCIE_IO_BASE 0xE8100000 | |
| #define NZ3_PCIE_IO_SIZE 0x00100000 | |
| #define NZ3_PCIE_MEM_BASE 0xE0000000 | |
| #define NZ3_PCIE_MEM_SIZE 0x01000000 | |
| #define NZ3_PCIE_SYS_MEM_BASE 0x00000000 | |
| #define NZ3_PCIE_SYS_MEM_SIZE 0x04000000 | |
| #define NZ3_PCIE_CFG_BASE 0xE8200000 | |
| #define NZ3_PCIE_CFG_SIZE 0x00001000 | |
| /* PCIe DBI registers */ | |
| #define PCIE_BASE_ADDRESS_0 0x10 | |
| #define PCIE_BASE_ADDRESS_1 0x14 | |
| #define PCIE_LINK_CTRL_STATUS 0x80 | |
| #define PCIE_CAP_LINK_AUTO_BW_INT_EN (1 << 11) | |
| #define PCIE_CAP_LINK_BW_MAN_INT_EN (1 << 10) | |
| #define PCIE_DW_ADV_ERR_CAP_CTRL 0x118 | |
| #define ECRC_CHECK_EN (1 << 8) | |
| #define ECRC_GEN_EN (1 << 6) | |
| /* Synopsis specific PCIE configuration registers */ | |
| #define PCIE_PORT_LINK_CONTROL 0x710 | |
| #define PORT_LINK_MODE_MASK (0x3f << 16) | |
| #define PORT_LINK_MODE_1_LANES (0x1 << 16) | |
| #define PORT_LINK_MODE_2_LANES (0x3 << 16) | |
| #define PORT_LINK_MODE_4_LANES (0x7 << 16) | |
| #define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C | |
| #define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) | |
| #define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8) | |
| #define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8) | |
| #define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8) | |
| #define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8) | |
| /* iATU registers */ | |
| #define PCIE_ATU_VIEWPORT 0x900 | |
| #define PCIE_ATU_REGION_INBOUND (0x1 << 31) | |
| #define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) | |
| #define PCIE_ATU_REGION_INDEX1 (0x1 << 0) | |
| #define PCIE_ATU_REGION_INDEX0 (0x0 << 0) | |
| #define PCIE_ATU_CR1 0x904 | |
| #define PCIE_ATU_TYPE_MEM (0x0 << 0) | |
| #define PCIE_ATU_TYPE_IO (0x2 << 0) | |
| #define PCIE_ATU_TYPE_CFG0 (0x4 << 0) | |
| #define PCIE_ATU_TYPE_CFG1 (0x5 << 0) | |
| #define PCIE_ATU_CR2 0x908 | |
| #define PCIE_ATU_ENABLE (0x1 << 31) | |
| #define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) | |
| #define PCIE_ATU_LOWER_BASE 0x90C | |
| #define PCIE_ATU_UPPER_BASE 0x910 | |
| #define PCIE_ATU_LIMIT 0x914 | |
| #define PCIE_ATU_LOWER_TARGET 0x918 | |
| #define PCIE_ATU_UPPER_TARGET 0x91C | |
| /* PCIe APP registers */ | |
| #define PCIE_INT_STATUS 0x0 | |
| #define RX_MSI_INT (1 << 8) | |
| #define DMA_LOCAL_INT (1 << 2) | |
| #define DLL_LINK_CHA_INT (1 << 1) | |
| #define PCIE_INT_ENABLE 0x8 | |
| #define RX_MSI_INT_EN (1 << 8) | |
| #define RX_ASSERT_INTA_EN (1 << 9) | |
| #define DMA_LOCAL_INT_EN (1 << 2) | |
| #define DLL_LINK_CHA_EN (1 << 1) | |
| #define PCIE_MISC_CTRL 0x40 | |
| #define APP_LINK_EN (1 << 0) | |
| #define BAR_MASK_WRITE_EN (1 << 5) | |
| #define PCIE_MISC_RST_CTRL 0x0FC | |
| #define APP_RLS_RST (0x7F << 8) | |
| #define PCIE_SYSP_DEV_STATUS 0x104 | |
| #define DLL_LINK_UP (1 << 1) | |
| #define PHY_LINK_UP (1 << 0) | |
| /* PCIe PHY registers */ | |
| #define PCIE_POWER_REG0 0x4 | |
| #define PHY_MODE_MASK (~(7 << 5)) | |
| #define PHY_MODE_PCIE (3 << 5) | |
| #define REFCLK_MASK (~(0x1F << 0)) | |
| #define REFCLK_26M (0xD << 0) | |
| #define PCIE_INTERFACE_REG1 0x94 | |
| #define PHY_MAX_GEN1 (~(3 << 10)) | |
| #define PCIE_MISC_REG0 0x13C | |
| #define REFCLK_SEL_GRP1 (~(1 << 10)) | |
| #define PIN_TXDCLK_2X (~(1 << 6)) | |
| #define REFCLK_EN (1 << 4) | |
| #define PCIE_LANE_STATUS1 0x60C | |
| #define PM_TXDCLK_PCLK_EN (1 << 0) | |
| #define PCIE_GLOB_CLK_CTRL 0x704 | |
| #define SOFT_RESET_DEASSERT (~(1 << 0)) | |
| #define MODE_CORE_CLK_250M (~(1 << 9)) | |
| #define MODE_FIXED_PCLK (1 << 2) | |
| #define PCIE_GLOB_CLK_SRC_LO 0x70C | |
| #define PLL_READY_DLY_MASK (~(7 << 5)) | |
| #define PLL_READY_DLY (2 << 5) | |
| #define PCIE_GLOB_PM_CFG0 0x740 | |
| #define TXDETRX_DLY_MASK (~(0xFF << 0)) | |
| #define TXDETRX_DLY4REFCLK_26M (8 << 0) | |
| /* APMU registers related to PCIE PHY */ | |
| #define APMU_USB_CLK_RES_CTRL 0xd428285c | |
| #define APMU_REFCLK_BUFF_CTRL 0xd4282a00 | |
| #define PCIE_AXI_RST (0x1 << 25) | |
| #define PCIE_AXI_CLK_ENB (0x1 << 24) | |
| #define PCIE_CORE_RC_MODE (0x4 << 17) | |
| #define PCIE_CORE_MODE_MASK (0xf << 17) | |
| #define PCIE_PHY_RC_MODE (0x1 << 10) | |
| #define PCIE_PHY_EP_MODE (0x1 << 9) | |
| #define PCIE_PHY_DISABLE (0x1 << 8) | |
| #define PCIE_REFCLK_PU (0x1 << 0) | |
| #define PCIE_REFCLK_SEL_EXT (0x1 << 1) | |
| #define PCIE_REFCLK_RX_EN (0x1 << 10) | |
| #define PCIE_REFCLK_TX_EN (0x1 << 11) | |
| #define PCIE_RESET_ASSERT (~(3 << 24)) | |
| #define PCIE_RESET_DEASSERT (3 << 24) | |
| /** | |
| * struct nz3_pcie - Nezha3 DW PCIe controller state | |
| * | |
| * @dbi_base: The base address of DW PCIe core register space | |
| * @phy_base: The base address of PHY register space | |
| * @app_base: The base address of SoC APP register space | |
| * @cfg_base: The base address of the configuration space | |
| * @cfg_size: The size of the configuration space which is needed | |
| * as it gets written into the PCIE_ATU_LIMIT register | |
| */ | |
| struct nz3_pcie { | |
| struct pci_controller hose; | |
| void *dbi_base; | |
| void *phy_base; | |
| void *app_base; | |
| void *cfg_base; | |
| unsigned long cfg_size; | |
| int lanes; | |
| }; | |
| static inline struct nz3_pcie * | |
| hose_to_nz3_pcie(struct pci_controller *hose) | |
| { | |
| return container_of(hose, struct nz3_pcie, hose); | |
| } | |
| static inline void nz3_pcie_elbi_writel(struct nz3_pcie *pcie, u32 val, u32 reg) | |
| { | |
| writel(val, pcie->dbi_base + reg); | |
| } | |
| static inline u32 nz3_pcie_elbi_readl(struct nz3_pcie *pcie, u32 reg) | |
| { | |
| return readl(pcie->dbi_base + reg); | |
| } | |
| static inline void nz3_pcie_phy_writel(struct nz3_pcie *pcie, u32 val, u32 reg) | |
| { | |
| writel(val, pcie->phy_base + reg); | |
| } | |
| static inline u32 nz3_pcie_phy_readl(struct nz3_pcie *pcie, u32 reg) | |
| { | |
| return readl(pcie->phy_base + reg); | |
| } | |
| static inline void nz3_pcie_app_writel(struct nz3_pcie *pcie, u32 val, u32 reg) | |
| { | |
| writel(val, pcie->app_base + reg); | |
| } | |
| static inline u32 nz3_pcie_app_readl(struct nz3_pcie *pcie, u32 reg) | |
| { | |
| return readl(pcie->app_base + reg); | |
| } | |
| /** | |
| * pcie_dw_prog_outbound_atu() - Configure ATU for outbound accesses | |
| * | |
| * @pcie: Pointer to the PCI controller state | |
| * @index: ATU region index | |
| * @type: ATU accsess type | |
| * @cpu_addr: the physical address for the translation entry | |
| * @pci_addr: the pcie bus address for the translation entry | |
| * @size: the size of the translation entry | |
| */ | |
| static void pcie_dw_prog_outbound_atu(struct nz3_pcie *pcie, int index, | |
| int type, u64 cpu_addr, u64 pci_addr, | |
| u32 size) | |
| { | |
| writel(PCIE_ATU_REGION_OUTBOUND | index, | |
| pcie->dbi_base + PCIE_ATU_VIEWPORT); | |
| writel(lower_32_bits(cpu_addr), pcie->dbi_base + PCIE_ATU_LOWER_BASE); | |
| writel(upper_32_bits(cpu_addr), pcie->dbi_base + PCIE_ATU_UPPER_BASE); | |
| writel(lower_32_bits(cpu_addr + size - 1), | |
| pcie->dbi_base + PCIE_ATU_LIMIT); | |
| writel(lower_32_bits(pci_addr), | |
| pcie->dbi_base + PCIE_ATU_LOWER_TARGET); | |
| writel(upper_32_bits(pci_addr), | |
| pcie->dbi_base + PCIE_ATU_UPPER_TARGET); | |
| writel(type, pcie->dbi_base + PCIE_ATU_CR1); | |
| writel(PCIE_ATU_ENABLE, pcie->dbi_base + PCIE_ATU_CR2); | |
| } | |
| /** | |
| * set_cfg_address() - Configure the PCIe controller config space access | |
| * | |
| * @pcie: Pointer to the PCI controller state | |
| * @d: PCI device to access | |
| * @where: Offset in the configuration space | |
| * | |
| * Configures the PCIe controller to access the configuration space of | |
| * a specific PCIe device and returns the address to use for this | |
| * access. | |
| * | |
| * Return: Address that can be used to access the configation space | |
| * of the requested device / offset | |
| */ | |
| static uintptr_t set_cfg_address(struct nz3_pcie *pcie, | |
| pci_dev_t d, uint where) | |
| { | |
| uintptr_t va_address; | |
| u32 atu_type; | |
| /* | |
| * Region #0 is used for Outbound CFG space access. | |
| * Direction = Outbound | |
| * Region Index = 0 | |
| */ | |
| if (PCI_BUS(d) < 2) | |
| /* For local bus, change TLP Type field to 4. */ | |
| atu_type = PCIE_ATU_TYPE_CFG0; | |
| else | |
| /* Otherwise, change TLP Type field to 5. */ | |
| atu_type = PCIE_ATU_TYPE_CFG1; | |
| if (PCI_BUS(d) == 0) { | |
| /* Accessing root port configuration space. */ | |
| va_address = (uintptr_t)pcie->dbi_base; | |
| } else { | |
| d &= 0xffff; | |
| pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, | |
| atu_type, (u64)pcie->cfg_base, | |
| d << 8, pcie->cfg_size); | |
| va_address = (uintptr_t)pcie->cfg_base; | |
| } | |
| va_address += where & ~0x3; | |
| return va_address; | |
| } | |
| /** | |
| * pcie_dw_addr_valid() - Check for valid bus address | |
| * | |
| * @d: The PCI device to access | |
| * | |
| * Return 1 (true) if the PCI device can be accessed by this controller. | |
| * | |
| * Return: 1 on valid, 0 on invalid | |
| */ | |
| static int pcie_dw_addr_valid(pci_dev_t d) | |
| { | |
| if ((PCI_BUS(d) == 0) && (PCI_DEV(d) > 0)) | |
| return 0; | |
| if ((PCI_BUS(d) == 1) && (PCI_DEV(d) > 0)) | |
| return 0; | |
| return 1; | |
| } | |
| /** | |
| * nz3_pcie_read_config() - Read from configuration space | |
| * | |
| * @hose: Pointer to the PCI host | |
| * @bdf: Identifies the PCIe device to access | |
| * @where: The offset into the device's configuration space | |
| * @val: A pointer at which to store the read value | |
| * | |
| * Return: 0 on success | |
| */ | |
| static int nz3_pcie_read_config(struct pci_controller *hose, pci_dev_t bdf, | |
| int where, uint32_t *val) | |
| { | |
| struct nz3_pcie *pcie = hose_to_nz3_pcie(hose); | |
| uint32_t va_address; | |
| debug("PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ", | |
| PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); | |
| if (!pcie_dw_addr_valid(bdf)) { | |
| debug("- out of range\n"); | |
| *val = 0xffffffff; | |
| return 0; | |
| } | |
| va_address = set_cfg_address(pcie, bdf, where); | |
| *val = readl(va_address); | |
| debug("(addr,val)=(0x%04x, 0x%08lx)\n", where, val); | |
| pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, | |
| PCIE_ATU_TYPE_IO, NZ3_PCIE_IO_BASE, | |
| NZ3_PCIE_IO_BASE, NZ3_PCIE_IO_SIZE); | |
| return 0; | |
| } | |
| /** | |
| * nz3_pcie_write_config() - Write to configuration space | |
| * | |
| * @hose: Pointer to the PCI host | |
| * @bdf: Identifies the PCIe device to access | |
| * @where: The offset into the device's configuration space | |
| * @val: The value to write | |
| * | |
| * Return: 0 on success | |
| */ | |
| static int nz3_pcie_write_config(struct pci_controller *hose, pci_dev_t bdf, | |
| int where, uint32_t val) | |
| { | |
| struct nz3_pcie *pcie = hose_to_nz3_pcie(hose); | |
| uint32_t va_address; | |
| debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ", | |
| PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); | |
| debug("(addr,val)=(0x%04x, 0x%08lx)\n", where, val); | |
| if (!pcie_dw_addr_valid(bdf)) { | |
| debug("- out of range\n"); | |
| return 0; | |
| } | |
| va_address = set_cfg_address(pcie, bdf, where); | |
| writel(val, va_address); | |
| pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, | |
| PCIE_ATU_TYPE_IO, NZ3_PCIE_IO_BASE, | |
| NZ3_PCIE_IO_BASE, NZ3_PCIE_IO_SIZE); | |
| return 0; | |
| } | |
| /** | |
| * nz3_pcie_clk_enable() - Enable PHY clk and config as RC mode | |
| * | |
| */ | |
| static void nz3_pcie_rc_clk_enable(void) | |
| { | |
| uint32_t val; | |
| val = readl(APMU_USB_CLK_RES_CTRL); | |
| val &= ~(PCIE_CORE_MODE_MASK | PCIE_PHY_RC_MODE | | |
| PCIE_PHY_EP_MODE | PCIE_PHY_DISABLE); | |
| val |= (PCIE_CORE_RC_MODE | PCIE_PHY_RC_MODE); | |
| writel(val, APMU_USB_CLK_RES_CTRL); | |
| val = readl(APMU_REFCLK_BUFF_CTRL); | |
| val &= ~(PCIE_REFCLK_RX_EN); | |
| val |= (PCIE_REFCLK_PU | PCIE_REFCLK_TX_EN); | |
| writel(val, APMU_REFCLK_BUFF_CTRL); | |
| /* Release areset and enable aclock */ | |
| val = readl(APMU_USB_CLK_RES_CTRL); | |
| val |= PCIE_AXI_CLK_ENB | PCIE_AXI_RST; | |
| writel(val, APMU_USB_CLK_RES_CTRL); | |
| } | |
| /** | |
| * nz3_pcie_init_phy() - Initialize PCIe PHY | |
| * | |
| * Return: 1 on success, 0 on fail | |
| */ | |
| static int nz3_pcie_init_phy(struct nz3_pcie *pcie) | |
| { | |
| uint32_t val; | |
| int count = 10; | |
| nz3_pcie_rc_clk_enable(); | |
| /* set COMPHY signal PIN_PLL_READY_TX sampling delay */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_GLOB_CLK_SRC_LO); | |
| val &= PLL_READY_DLY_MASK; | |
| val |= PLL_READY_DLY; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_GLOB_CLK_SRC_LO); | |
| /* select the correct REFCLK source */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_MISC_REG0); | |
| val &= REFCLK_SEL_GRP1; | |
| val &= PIN_TXDCLK_2X; | |
| val |= REFCLK_EN; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_MISC_REG0); | |
| /* Set PHY as PCIE mode */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_POWER_REG0); | |
| val &= PHY_MODE_MASK; | |
| val |= PHY_MODE_PCIE; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_POWER_REG0); | |
| /* select 26MHz refclk */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_POWER_REG0); | |
| val &= REFCLK_MASK; | |
| val |= REFCLK_26M; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_POWER_REG0); | |
| /* set txdetrx delay time */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_GLOB_PM_CFG0); | |
| val &= TXDETRX_DLY_MASK; | |
| val |= TXDETRX_DLY4REFCLK_26M; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_GLOB_PM_CFG0); | |
| /* set PIPE 250Mhz 8bit mode */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_GLOB_CLK_CTRL); | |
| val &= MODE_CORE_CLK_250M; | |
| /* 8-bit at 250MHz at 2.5GT/s */ | |
| val |= MODE_FIXED_PCLK; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_GLOB_CLK_CTRL); | |
| /* set the user intended maximum data rate as 2.5G */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_INTERFACE_REG1); | |
| val &= PHY_MAX_GEN1; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_INTERFACE_REG1); | |
| /* Release soft reset */ | |
| val = nz3_pcie_phy_readl(pcie, PCIE_GLOB_CLK_CTRL); | |
| val &= SOFT_RESET_DEASSERT; | |
| nz3_pcie_phy_writel(pcie, val, PCIE_GLOB_CLK_CTRL); | |
| while (count--) { | |
| val = nz3_pcie_phy_readl(pcie, PCIE_LANE_STATUS1); | |
| mdelay(1); | |
| if (val & PM_TXDCLK_PCLK_EN) | |
| break; | |
| } | |
| if (count == 0) { | |
| printf("PCIe PLL isn't ready in time\n"); | |
| return 0; | |
| } | |
| return 1; | |
| } | |
| /** | |
| * nz3_pcie_link_up() - Return the link state | |
| * | |
| * Return: 1 (true) for active line and 0 (false) for no link | |
| */ | |
| static int nz3_pcie_link_up(struct nz3_pcie *pcie) | |
| { | |
| u32 status; | |
| status = nz3_pcie_app_readl(pcie, PCIE_SYSP_DEV_STATUS); | |
| if (status & DLL_LINK_UP) | |
| return 1; | |
| return 0; | |
| } | |
| /** | |
| * nz3_pcie_core_reset() - assert/deassert core reset | |
| * | |
| * @reset: ture: de-assert, false: assert | |
| */ | |
| static void nz3_pcie_core_reset(int reset) | |
| { | |
| u32 val; | |
| val = readl(APMU_USB_CLK_RES_CTRL); | |
| if (reset) | |
| val |= PCIE_RESET_DEASSERT; | |
| else | |
| val &= PCIE_RESET_ASSERT; | |
| writel(val, APMU_USB_CLK_RES_CTRL); | |
| } | |
| /** | |
| * nz3_pcie_dw_configure_rc() - Configure RC related stuff | |
| * | |
| * @nz3_pcie: A pointer to the PCIe controller | |
| * | |
| * Configure the link capabilities and speed in the PCIe root complex. | |
| */ | |
| static void nz3_pcie_dw_configure_rc(struct nz3_pcie *pcie) | |
| { | |
| uint32_t val; | |
| uint32_t membase; | |
| uint32_t memlimit; | |
| /* set the number of lanes */ | |
| val = nz3_pcie_elbi_readl(pcie, PCIE_PORT_LINK_CONTROL); | |
| val &= ~PORT_LINK_MODE_MASK; | |
| switch (pcie->lanes) { | |
| case 1: | |
| val |= PORT_LINK_MODE_1_LANES; | |
| break; | |
| case 2: | |
| val |= PORT_LINK_MODE_2_LANES; | |
| break; | |
| case 4: | |
| val |= PORT_LINK_MODE_4_LANES; | |
| break; | |
| } | |
| nz3_pcie_elbi_writel(pcie, val, PCIE_PORT_LINK_CONTROL); | |
| /* set link width speed control register */ | |
| val = nz3_pcie_elbi_readl(pcie, PCIE_LINK_WIDTH_SPEED_CONTROL); | |
| val &= ~PORT_LOGIC_LINK_WIDTH_MASK; | |
| switch (pcie->lanes) { | |
| case 1: | |
| val |= PORT_LOGIC_LINK_WIDTH_1_LANES; | |
| break; | |
| case 2: | |
| val |= PORT_LOGIC_LINK_WIDTH_2_LANES; | |
| break; | |
| case 4: | |
| val |= PORT_LOGIC_LINK_WIDTH_4_LANES; | |
| break; | |
| } | |
| nz3_pcie_elbi_writel(pcie, val, PCIE_LINK_WIDTH_SPEED_CONTROL); | |
| /* setup RC BARs */ | |
| nz3_pcie_elbi_writel(pcie, 0x00000004, PCI_BASE_ADDRESS_0); | |
| nz3_pcie_elbi_writel(pcie, 0x00000000, PCI_BASE_ADDRESS_1); | |
| /* setup interrupt pins */ | |
| val = nz3_pcie_elbi_readl(pcie, PCI_INTERRUPT_LINE); | |
| val &= 0xffff00ff; | |
| val |= 0x00000100; | |
| nz3_pcie_elbi_writel(pcie, val, PCI_INTERRUPT_LINE); | |
| /* setup bus numbers */ | |
| val = nz3_pcie_elbi_readl(pcie, PCI_PRIMARY_BUS); | |
| val &= 0xff000000; | |
| val |= 0x00010100; | |
| nz3_pcie_elbi_writel(pcie, val, PCI_PRIMARY_BUS); | |
| /* setup memory base, memory limit */ | |
| membase = (NZ3_PCIE_MEM_BASE & 0xfff00000) >> 16; | |
| memlimit = (NZ3_PCIE_MEM_BASE + NZ3_PCIE_MEM_SIZE) & 0xfff00000; | |
| val = memlimit | membase; | |
| nz3_pcie_elbi_writel(pcie, val, PCI_MEMORY_BASE); | |
| /* setup command register */ | |
| val = nz3_pcie_elbi_readl(pcie, PCI_COMMAND); | |
| val &= 0xffff0000; | |
| val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | | |
| PCI_COMMAND_MASTER | PCI_COMMAND_SERR; | |
| nz3_pcie_elbi_writel(pcie, val, PCI_COMMAND); | |
| } | |
| /** | |
| * nz3_pcie_establish_link() - Establish link between RC and EP | |
| * | |
| * @nz3_pcie: A pointer to the PCIe controller | |
| * | |
| * Return: 1 (true) for active line and 0 (false) for no link | |
| */ | |
| static int nz3_pcie_establish_link(struct nz3_pcie *pcie) | |
| { | |
| int ret; | |
| u32 val; | |
| int count = 0; | |
| if (nz3_pcie_link_up(pcie)) { | |
| printf("Link already up\n"); | |
| return 1; | |
| } | |
| /* Initialize COMPHY */ | |
| ret = nz3_pcie_init_phy(pcie); | |
| if (!ret) | |
| return ret; | |
| /* release reset */ | |
| val = nz3_pcie_app_readl(pcie, PCIE_MISC_RST_CTRL); | |
| val |= APP_RLS_RST; | |
| nz3_pcie_app_writel(pcie, val, PCIE_MISC_RST_CTRL); | |
| /* enable ecrc generation and check */ | |
| val = nz3_pcie_elbi_readl(pcie, PCIE_DW_ADV_ERR_CAP_CTRL); | |
| val |= (ECRC_CHECK_EN | ECRC_GEN_EN); | |
| nz3_pcie_elbi_writel(pcie, val, PCIE_DW_ADV_ERR_CAP_CTRL); | |
| /* | |
| * enable Link Bandwidth Management and | |
| * Autonomous Bandwidth interrupt | |
| */ | |
| val = nz3_pcie_elbi_readl(pcie, PCIE_LINK_CTRL_STATUS); | |
| val |= PCIE_CAP_LINK_AUTO_BW_INT_EN; | |
| val |= PCIE_CAP_LINK_BW_MAN_INT_EN; | |
| nz3_pcie_elbi_writel(pcie, val, PCIE_LINK_CTRL_STATUS); | |
| nz3_pcie_dw_configure_rc(pcie); | |
| /* Enable BAR mask register write */ | |
| val = nz3_pcie_app_readl(pcie, PCIE_MISC_CTRL); | |
| val |= BAR_MASK_WRITE_EN; | |
| nz3_pcie_app_writel(pcie, val, PCIE_MISC_CTRL); | |
| /* Disable RC BAR0 and BAR1 */ | |
| nz3_pcie_elbi_writel(pcie, 0, PCIE_BASE_ADDRESS_0); | |
| nz3_pcie_elbi_writel(pcie, 0, PCIE_BASE_ADDRESS_1); | |
| /* Disable BAR mask register write */ | |
| val = nz3_pcie_app_readl(pcie, PCIE_MISC_CTRL); | |
| val &= ~BAR_MASK_WRITE_EN; | |
| nz3_pcie_app_writel(pcie, val, PCIE_MISC_CTRL); | |
| /* allow the LTSSM to establish link */ | |
| val = nz3_pcie_app_readl(pcie, PCIE_MISC_CTRL); | |
| val |= APP_LINK_EN; | |
| nz3_pcie_app_writel(pcie, val, PCIE_MISC_CTRL); | |
| /* check if the link is up or not */ | |
| while (!nz3_pcie_link_up(pcie)) { | |
| mdelay(1); | |
| count++; | |
| if (count == 100) { | |
| printf("PCIe Link Fail\n"); | |
| return 0; | |
| } | |
| } | |
| printf("Link up\n"); | |
| return 1; | |
| } | |
| /** | |
| * nz3_pcie_init() - Probe Nezha3 PCIe bus for active link | |
| * | |
| */ | |
| void nz3_pcie_init(void) | |
| { | |
| /* Static instance of the controller. */ | |
| static struct nz3_pcie pcie; | |
| struct pci_controller *hose = &(pcie.hose); | |
| int ret = 0; | |
| memset(&pcie, 0, sizeof(pcie)); | |
| pcie.dbi_base = PCIE_DW_NZ3_DBI_BASE; | |
| pcie.phy_base = pcie.dbi_base + 0x1000; | |
| pcie.app_base = pcie.dbi_base + 0x2000; | |
| pcie.cfg_base = NZ3_PCIE_CFG_BASE; | |
| pcie.cfg_size = NZ3_PCIE_MEM_SIZE; | |
| pcie.lanes = PCIE_DW_NZ3_LANES; | |
| hose->first_busno = 0x0; | |
| hose->last_busno = 0xff; | |
| /* PCI I/O space */ | |
| pci_set_region(&hose->regions[0], | |
| NZ3_PCIE_IO_BASE, NZ3_PCIE_IO_BASE, | |
| NZ3_PCIE_IO_SIZE, PCI_REGION_IO); | |
| /* PCI memory space */ | |
| pci_set_region(&hose->regions[1], | |
| NZ3_PCIE_MEM_BASE, NZ3_PCIE_MEM_BASE, | |
| NZ3_PCIE_MEM_SIZE, PCI_REGION_MEM); | |
| /* System memory space */ | |
| pci_set_region(&hose->regions[2], | |
| NZ3_PCIE_SYS_MEM_BASE, NZ3_PCIE_SYS_MEM_BASE, | |
| NZ3_PCIE_SYS_MEM_SIZE, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY); | |
| hose->region_count = 3; | |
| pci_set_ops(hose, | |
| pci_hose_read_config_byte_via_dword, | |
| pci_hose_read_config_word_via_dword, | |
| nz3_pcie_read_config, | |
| pci_hose_write_config_byte_via_dword, | |
| pci_hose_write_config_word_via_dword, | |
| nz3_pcie_write_config); | |
| /* Start the controller and establish link. */ | |
| ret = nz3_pcie_establish_link(&pcie); | |
| if (!ret) { | |
| pci_register_hose(hose); | |
| hose->last_busno = pci_hose_scan(hose); | |
| } | |
| } | |
| void nz3_pcie_remove(void) | |
| { | |
| nz3_pcie_core_reset(0); | |
| } | |
| /* Probe function. */ | |
| void pci_init_board(void) | |
| { | |
| nz3_pcie_init(); | |
| } |