xref: /linux/drivers/pci/controller/mobiveil/pcie-mobiveil.c (revision fc99b3311af7125c46b56e753dc1a65c27b0d7e2)
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