xref: /linux/drivers/pci/controller/mobiveil/pcie-mobiveil.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
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 __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 
52 static 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 
77 static 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 
99 u32 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 
114 void 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 
127 bool 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 
136 void 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  */
173 void 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 
216 int 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 }
232