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