pcie-mobiveil.c (5855B)
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 */ 28static 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 39static void __iomem *mobiveil_pcie_comp_addr(struct mobiveil_pcie *pcie, 40 u32 off) 41{ 42 if (off < PAGED_ADDR_BNDRY) { 43 /* For directly accessed registers, clear the pg_sel field */ 44 mobiveil_pcie_sel_page(pcie, 0); 45 return pcie->csr_axi_slave_base + off; 46 } 47 48 mobiveil_pcie_sel_page(pcie, OFFSET_TO_PAGE_IDX(off)); 49 return pcie->csr_axi_slave_base + OFFSET_TO_PAGE_ADDR(off); 50} 51 52static int mobiveil_pcie_read(void __iomem *addr, int size, u32 *val) 53{ 54 if ((uintptr_t)addr & (size - 1)) { 55 *val = 0; 56 return PCIBIOS_BAD_REGISTER_NUMBER; 57 } 58 59 switch (size) { 60 case 4: 61 *val = readl(addr); 62 break; 63 case 2: 64 *val = readw(addr); 65 break; 66 case 1: 67 *val = readb(addr); 68 break; 69 default: 70 *val = 0; 71 return PCIBIOS_BAD_REGISTER_NUMBER; 72 } 73 74 return PCIBIOS_SUCCESSFUL; 75} 76 77static int mobiveil_pcie_write(void __iomem *addr, int size, u32 val) 78{ 79 if ((uintptr_t)addr & (size - 1)) 80 return PCIBIOS_BAD_REGISTER_NUMBER; 81 82 switch (size) { 83 case 4: 84 writel(val, addr); 85 break; 86 case 2: 87 writew(val, addr); 88 break; 89 case 1: 90 writeb(val, addr); 91 break; 92 default: 93 return PCIBIOS_BAD_REGISTER_NUMBER; 94 } 95 96 return PCIBIOS_SUCCESSFUL; 97} 98 99u32 mobiveil_csr_read(struct mobiveil_pcie *pcie, u32 off, size_t size) 100{ 101 void __iomem *addr; 102 u32 val; 103 int ret; 104 105 addr = mobiveil_pcie_comp_addr(pcie, off); 106 107 ret = mobiveil_pcie_read(addr, size, &val); 108 if (ret) 109 dev_err(&pcie->pdev->dev, "read CSR address failed\n"); 110 111 return val; 112} 113 114void mobiveil_csr_write(struct mobiveil_pcie *pcie, u32 val, u32 off, 115 size_t size) 116{ 117 void __iomem *addr; 118 int ret; 119 120 addr = mobiveil_pcie_comp_addr(pcie, off); 121 122 ret = mobiveil_pcie_write(addr, size, val); 123 if (ret) 124 dev_err(&pcie->pdev->dev, "write CSR address failed\n"); 125} 126 127bool mobiveil_pcie_link_up(struct mobiveil_pcie *pcie) 128{ 129 if (pcie->ops->link_up) 130 return pcie->ops->link_up(pcie); 131 132 return (mobiveil_csr_readl(pcie, LTSSM_STATUS) & 133 LTSSM_STATUS_L0_MASK) == LTSSM_STATUS_L0; 134} 135 136void program_ib_windows(struct mobiveil_pcie *pcie, int win_num, 137 u64 cpu_addr, u64 pci_addr, u32 type, u64 size) 138{ 139 u32 value; 140 u64 size64 = ~(size - 1); 141 142 if (win_num >= pcie->ppio_wins) { 143 dev_err(&pcie->pdev->dev, 144 "ERROR: max inbound windows reached !\n"); 145 return; 146 } 147 148 value = mobiveil_csr_readl(pcie, PAB_PEX_AMAP_CTRL(win_num)); 149 value &= ~(AMAP_CTRL_TYPE_MASK << AMAP_CTRL_TYPE_SHIFT | WIN_SIZE_MASK); 150 value |= type << AMAP_CTRL_TYPE_SHIFT | 1 << AMAP_CTRL_EN_SHIFT | 151 (lower_32_bits(size64) & WIN_SIZE_MASK); 152 mobiveil_csr_writel(pcie, value, PAB_PEX_AMAP_CTRL(win_num)); 153 154 mobiveil_csr_writel(pcie, upper_32_bits(size64), 155 PAB_EXT_PEX_AMAP_SIZEN(win_num)); 156 157 mobiveil_csr_writel(pcie, lower_32_bits(cpu_addr), 158 PAB_PEX_AMAP_AXI_WIN(win_num)); 159 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), 160 PAB_EXT_PEX_AMAP_AXI_WIN(win_num)); 161 162 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), 163 PAB_PEX_AMAP_PEX_WIN_L(win_num)); 164 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), 165 PAB_PEX_AMAP_PEX_WIN_H(win_num)); 166 167 pcie->ib_wins_configured++; 168} 169 170/* 171 * routine to program the outbound windows 172 */ 173void program_ob_windows(struct mobiveil_pcie *pcie, int win_num, 174 u64 cpu_addr, u64 pci_addr, u32 type, u64 size) 175{ 176 u32 value; 177 u64 size64 = ~(size - 1); 178 179 if (win_num >= pcie->apio_wins) { 180 dev_err(&pcie->pdev->dev, 181 "ERROR: max outbound windows reached !\n"); 182 return; 183 } 184 185 /* 186 * program Enable Bit to 1, Type Bit to (00) base 2, AXI Window Size Bit 187 * to 4 KB in PAB_AXI_AMAP_CTRL register 188 */ 189 value = mobiveil_csr_readl(pcie, PAB_AXI_AMAP_CTRL(win_num)); 190 value &= ~(WIN_TYPE_MASK << WIN_TYPE_SHIFT | WIN_SIZE_MASK); 191 value |= 1 << WIN_ENABLE_SHIFT | type << WIN_TYPE_SHIFT | 192 (lower_32_bits(size64) & WIN_SIZE_MASK); 193 mobiveil_csr_writel(pcie, value, PAB_AXI_AMAP_CTRL(win_num)); 194 195 mobiveil_csr_writel(pcie, upper_32_bits(size64), 196 PAB_EXT_AXI_AMAP_SIZE(win_num)); 197 198 /* 199 * program AXI window base with appropriate value in 200 * PAB_AXI_AMAP_AXI_WIN0 register 201 */ 202 mobiveil_csr_writel(pcie, 203 lower_32_bits(cpu_addr) & (~AXI_WINDOW_ALIGN_MASK), 204 PAB_AXI_AMAP_AXI_WIN(win_num)); 205 mobiveil_csr_writel(pcie, upper_32_bits(cpu_addr), 206 PAB_EXT_AXI_AMAP_AXI_WIN(win_num)); 207 208 mobiveil_csr_writel(pcie, lower_32_bits(pci_addr), 209 PAB_AXI_AMAP_PEX_WIN_L(win_num)); 210 mobiveil_csr_writel(pcie, upper_32_bits(pci_addr), 211 PAB_AXI_AMAP_PEX_WIN_H(win_num)); 212 213 pcie->ob_wins_configured++; 214} 215 216int mobiveil_bringup_link(struct mobiveil_pcie *pcie) 217{ 218 int retries; 219 220 /* check if the link is up or not */ 221 for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { 222 if (mobiveil_pcie_link_up(pcie)) 223 return 0; 224 225 usleep_range(LINK_WAIT_MIN, LINK_WAIT_MAX); 226 } 227 228 dev_err(&pcie->pdev->dev, "link never came up\n"); 229 230 return -ETIMEDOUT; 231}