1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PCIe host controller driver for Mobiveil PCIe Host controller 4 * 5 * Copyright (c) 2018 Mobiveil Inc. 6 * Copyright 2019 NXP 7 * 8 * Author: Subrahmanya Lingappa <l.subrahmanya@mobiveil.co.in> 9 * Hou Zhiqiang <Zhiqiang.Hou@nxp.com> 10 */ 11 12 #include <linux/delay.h> 13 #include <linux/init.h> 14 #include <linux/kernel.h> 15 #include <linux/pci.h> 16 #include <linux/platform_device.h> 17 18 #include "pcie-mobiveil.h" 19 20 /* 21 * mobiveil_pcie_sel_page - routine to access paged register 22 * 23 * Registers whose address greater than PAGED_ADDR_BNDRY (0xc00) are paged, 24 * for this scheme to work extracted higher 6 bits of the offset will be 25 * written to pg_sel field of PAB_CTRL register and rest of the lower 10 26 * bits enabled with PAGED_ADDR_BNDRY are used as offset of the register. 27 */ 28 static void mobiveil_pcie_sel_page(struct mobiveil_pcie *pcie, u8 pg_idx) 29 { 30 u32 val; 31 32 val = readl(pcie->csr_axi_slave_base + PAB_CTRL); 33 val &= ~(PAGE_SEL_MASK << PAGE_SEL_SHIFT); 34 val |= (pg_idx & PAGE_SEL_MASK) << PAGE_SEL_SHIFT; 35 36 writel(val, pcie->csr_axi_slave_base + PAB_CTRL); 37 } 38 39 static void *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie, u32 off) 40 { 41 if (off < PAGED_ADDR_BNDRY) { 42 /* For directly accessed registers, clear the pg_sel field */ 43 mobiveil_pcie_sel_page(pcie, 0); 44 return pcie->csr_axi_slave_base + off; 45 } 46 47 mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off)); 48 return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off); 49 } 50 51 static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val) 52 { 53 if ((uintptr_t)addr & (size - 1)) { 54 *val = 0; 55 return PCIBIOS_BAD_REGISTER_NUMBER; 56 } 57 58 switch (size) { 59 case 4: 60 *val = readl(addr); 61 break; 62 case 2: 63 *val = readw(addr); 64 break; 65 case 1: 66 *val = readb(addr); 67 break; 68 default: 69 *val = 0; 70 return PCIBIOS_BAD_REGISTER_NUMBER; 71 } 72 73 return PCIBIOS_SUCCESSFUL; 74 } 75 76 static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val) 77 { 78 if ((uintptr_t)addr & (size - 1)) 79 return PCIBIOS_BAD_REGISTER_NUMBER; 80 81 switch (size) { 82 case 4: 83 writel(val, addr); 84 break; 85 case 2: 86 writew(val, addr); 87 break; 88 case 1: 89 writeb(val, addr); 90 break; 91 default: 92 return PCIBIOS_BAD_REGISTER_NUMBER; 93 } 94 95 return PCIBIOS_SUCCESSFUL; 96 } 97 98 u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) 99 { 100 void *addr; 101 u32 val; 102 int ret; 103 104 addr = mobiveil_pcie_comp_addr(pcie, off); 105 106 ret = mobiveil_pcie_read(addr, size, &val); 107 if (ret) 108 dev_err(&pcie->pdev->dev, "read CSR address failed\n"); 109 110 return val; 111 } 112 113 void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, 114 size_t size) 115 { 116 void *addr; 117 int ret; 118 119 addr = mobiveil_pcie_comp_addr(pcie, off); 120 121 ret = mobiveil_pcie_write(addr, size, val); 122 if (ret) 123 dev_err(&pcie->pdev->dev, "write CSR address failed\n"); 124 } 125 126 bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) 127 { 128 return (mobiveil_csr_readl(pcie, LTSSM_STATUS) & 129 LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; 130 } 131 132 void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, 133 u64 cpu_addr, u64 pci_addr, u32 type, u64 size) 134 { 135 u32 value; 136 u64 size64 = ~(size - 1); 137 138 if (win_num >= pcie->ppio_wins) { 139 dev_err(&pcie->pdev->dev, 140 "ERROR: max inbound windows reached !\n"); 141 return; 142 } 143 144 value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); 145 value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); 146 value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | 147 (lower_32_bits(size64) & WIN_SIZE_MASK); 148 mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); 149 150 mobiveil_csr_writel(pcie, upper_32_bits(size64), 151 PAB_EXT_PEX_AMAP_SIZEN(win_num)); 152 153 mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr), 154 PAB_PEX_AMAP_AXI_WIN(win_num)); 155 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), 156 PAB_EXT_PEX_AMAP_AXI_WIN(win_num)); 157 158 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), 159 PAB_PEX_AMAP_PEX_WIN_L(win_num)); 160 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), 161 PAB_PEX_AMAP_PEX_WIN_H(win_num)); 162 163 pcie->ib_wins_configured++; 164 } 165 166 /* 167 * routine to program the outbound windows 168 */ 169 void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, 170 u64 cpu_addr, u64 pci_addr, u32 type, u64 size) 171 { 172 u32 value; 173 u64 size64 = ~(size - 1); 174 175 if (win_num >= pcie->apio_wins) { 176 dev_err(&pcie->pdev->dev, 177 "ERROR: max outbound windows reached !\n"); 178 return; 179 } 180 181 /* 182 * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit 183 * to 4 KB in PAB_AXI_AMAP_CTRL register 184 */ 185 value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); 186 value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK); 187 value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | 188 (lower_32_bits(size64) & WIN_SIZE_MASK); 189 mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); 190 191 mobiveil_csr_writel(pcie, upper_32_bits(size64), 192 PAB_EXT_AXI_AMAP_SIZE(win_num)); 193 194 /* 195 * program AXI window base with appropriate value in 196 * PAB_AXI_AMAP_AXI_WIN0 register 197 */ 198 mobiveil_csr_writel(pcie, 199 lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), 200 PAB_AXI_AMAP_AXI_WIN(win_num)); 201 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), 202 PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); 203 204 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), 205 PAB_AXI_AMAP_PEX_WIN_L(win_num)); 206 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), 207 PAB_AXI_AMAP_PEX_WIN_H(win_num)); 208 209 pcie->ob_wins_configured++; 210 } 211 212 int mobiveil_bringup_link(struct mobiveil_pcie *pcie) 213 { 214 int retries; 215 216 /* check if the link is up or not */ 217 for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { 218 if (mobiveil_pcie_link_up(pcie)) 219 return 0; 220 221 usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); 222 } 223 224 dev_err(&pcie->pdev->dev, "link never came up\n"); 225 226 return -ETIMEDOUT; 227 } 228