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 if (pcie->ops->link_up) 129 return pcie->ops->link_up(pcie); 130 131 return (mobiveil_csr_readl(pcie, LTSSM_STATUS) & 132 LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; 133 } 134 135 void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, 136 u64 cpu_addr, u64 pci_addr, u32 type, u64 size) 137 { 138 u32 value; 139 u64 size64 = ~(size - 1); 140 141 if (win_num >= pcie->ppio_wins) { 142 dev_err(&pcie->pdev->dev, 143 "ERROR: max inbound windows reached !\n"); 144 return; 145 } 146 147 value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); 148 value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); 149 value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | 150 (lower_32_bits(size64) & WIN_SIZE_MASK); 151 mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); 152 153 mobiveil_csr_writel(pcie, upper_32_bits(size64), 154 PAB_EXT_PEX_AMAP_SIZEN(win_num)); 155 156 mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr), 157 PAB_PEX_AMAP_AXI_WIN(win_num)); 158 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), 159 PAB_EXT_PEX_AMAP_AXI_WIN(win_num)); 160 161 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), 162 PAB_PEX_AMAP_PEX_WIN_L(win_num)); 163 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), 164 PAB_PEX_AMAP_PEX_WIN_H(win_num)); 165 166 pcie->ib_wins_configured++; 167 } 168 169 /* 170 * routine to program the outbound windows 171 */ 172 void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, 173 u64 cpu_addr, u64 pci_addr, u32 type, u64 size) 174 { 175 u32 value; 176 u64 size64 = ~(size - 1); 177 178 if (win_num >= pcie->apio_wins) { 179 dev_err(&pcie->pdev->dev, 180 "ERROR: max outbound windows reached !\n"); 181 return; 182 } 183 184 /* 185 * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit 186 * to 4 KB in PAB_AXI_AMAP_CTRL register 187 */ 188 value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); 189 value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK); 190 value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | 191 (lower_32_bits(size64) & WIN_SIZE_MASK); 192 mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); 193 194 mobiveil_csr_writel(pcie, upper_32_bits(size64), 195 PAB_EXT_AXI_AMAP_SIZE(win_num)); 196 197 /* 198 * program AXI window base with appropriate value in 199 * PAB_AXI_AMAP_AXI_WIN0 register 200 */ 201 mobiveil_csr_writel(pcie, 202 lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), 203 PAB_AXI_AMAP_AXI_WIN(win_num)); 204 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), 205 PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); 206 207 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), 208 PAB_AXI_AMAP_PEX_WIN_L(win_num)); 209 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), 210 PAB_AXI_AMAP_PEX_WIN_H(win_num)); 211 212 pcie->ob_wins_configured++; 213 } 214 215 int mobiveil_bringup_link(struct mobiveil_pcie *pcie) 216 { 217 int retries; 218 219 /* check if the link is up or not */ 220 for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { 221 if (mobiveil_pcie_link_up(pcie)) 222 return 0; 223 224 usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); 225 } 226 227 dev_err(&pcie->pdev->dev, "link never came up\n"); 228 229 return -ETIMEDOUT; 230 } 231