1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * IDT RXS Gen.3 Serial RapidIO switch family support |
4 | * |
5 | * Copyright 2016 Integrated Device Technology, Inc. |
6 | */ |
7 | |
8 | #include <linux/stat.h> |
9 | #include <linux/module.h> |
10 | #include <linux/rio.h> |
11 | #include <linux/rio_drv.h> |
12 | #include <linux/rio_ids.h> |
13 | #include <linux/delay.h> |
14 | |
15 | #include <asm/page.h> |
16 | #include "../rio.h" |
17 | |
18 | #define RIO_EM_PW_STAT 0x40020 |
19 | #define RIO_PW_CTL 0x40204 |
20 | #define RIO_PW_CTL_PW_TMR 0xffffff00 |
21 | #define RIO_PW_ROUTE 0x40208 |
22 | |
23 | #define RIO_EM_DEV_INT_EN 0x40030 |
24 | |
25 | #define RIO_PLM_SPx_IMP_SPEC_CTL(x) (0x10100 + (x)*0x100) |
26 | #define RIO_PLM_SPx_IMP_SPEC_CTL_SOFT_RST 0x02000000 |
27 | |
28 | #define RIO_PLM_SPx_PW_EN(x) (0x10118 + (x)*0x100) |
29 | #define RIO_PLM_SPx_PW_EN_OK2U 0x40000000 |
30 | #define RIO_PLM_SPx_PW_EN_LINIT 0x10000000 |
31 | |
32 | #define RIO_BC_L2_Gn_ENTRYx_CSR(n, x) (0x31000 + (n)*0x400 + (x)*0x4) |
33 | #define RIO_SPx_L2_Gn_ENTRYy_CSR(x, n, y) \ |
34 | (0x51000 + (x)*0x2000 + (n)*0x400 + (y)*0x4) |
35 | |
36 | static int |
37 | idtg3_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, |
38 | u16 table, u16 route_destid, u8 route_port) |
39 | { |
40 | u32 rval; |
41 | u32 entry = route_port; |
42 | int err = 0; |
43 | |
44 | pr_debug("RIO: %s t=0x%x did_%x to p_%x\n" , |
45 | __func__, table, route_destid, entry); |
46 | |
47 | if (route_destid > 0xFF) |
48 | return -EINVAL; |
49 | |
50 | if (route_port == RIO_INVALID_ROUTE) |
51 | entry = RIO_RT_ENTRY_DROP_PKT; |
52 | |
53 | if (table == RIO_GLOBAL_TABLE) { |
54 | /* Use broadcast register to update all per-port tables */ |
55 | err = rio_mport_write_config_32(port: mport, destid, hopcount, |
56 | RIO_BC_L2_Gn_ENTRYx_CSR(0, route_destid), |
57 | data: entry); |
58 | return err; |
59 | } |
60 | |
61 | /* |
62 | * Verify that specified port/table number is valid |
63 | */ |
64 | err = rio_mport_read_config_32(port: mport, destid, hopcount, |
65 | RIO_SWP_INFO_CAR, data: &rval); |
66 | if (err) |
67 | return err; |
68 | |
69 | if (table >= RIO_GET_TOTAL_PORTS(rval)) |
70 | return -EINVAL; |
71 | |
72 | err = rio_mport_write_config_32(port: mport, destid, hopcount, |
73 | RIO_SPx_L2_Gn_ENTRYy_CSR(table, 0, route_destid), |
74 | data: entry); |
75 | return err; |
76 | } |
77 | |
78 | static int |
79 | idtg3_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, |
80 | u16 table, u16 route_destid, u8 *route_port) |
81 | { |
82 | u32 rval; |
83 | int err; |
84 | |
85 | if (route_destid > 0xFF) |
86 | return -EINVAL; |
87 | |
88 | err = rio_mport_read_config_32(port: mport, destid, hopcount, |
89 | RIO_SWP_INFO_CAR, data: &rval); |
90 | if (err) |
91 | return err; |
92 | |
93 | /* |
94 | * This switch device does not have the dedicated global routing table. |
95 | * It is substituted by reading routing table of the ingress port of |
96 | * maintenance read requests. |
97 | */ |
98 | if (table == RIO_GLOBAL_TABLE) |
99 | table = RIO_GET_PORT_NUM(rval); |
100 | else if (table >= RIO_GET_TOTAL_PORTS(rval)) |
101 | return -EINVAL; |
102 | |
103 | err = rio_mport_read_config_32(port: mport, destid, hopcount, |
104 | RIO_SPx_L2_Gn_ENTRYy_CSR(table, 0, route_destid), |
105 | data: &rval); |
106 | if (err) |
107 | return err; |
108 | |
109 | if (rval == RIO_RT_ENTRY_DROP_PKT) |
110 | *route_port = RIO_INVALID_ROUTE; |
111 | else |
112 | *route_port = (u8)rval; |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static int |
118 | idtg3_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, |
119 | u16 table) |
120 | { |
121 | u32 i; |
122 | u32 rval; |
123 | int err; |
124 | |
125 | if (table == RIO_GLOBAL_TABLE) { |
126 | for (i = 0; i <= 0xff; i++) { |
127 | err = rio_mport_write_config_32(port: mport, destid, hopcount, |
128 | RIO_BC_L2_Gn_ENTRYx_CSR(0, i), |
129 | RIO_RT_ENTRY_DROP_PKT); |
130 | if (err) |
131 | break; |
132 | } |
133 | |
134 | return err; |
135 | } |
136 | |
137 | err = rio_mport_read_config_32(port: mport, destid, hopcount, |
138 | RIO_SWP_INFO_CAR, data: &rval); |
139 | if (err) |
140 | return err; |
141 | |
142 | if (table >= RIO_GET_TOTAL_PORTS(rval)) |
143 | return -EINVAL; |
144 | |
145 | for (i = 0; i <= 0xff; i++) { |
146 | err = rio_mport_write_config_32(port: mport, destid, hopcount, |
147 | RIO_SPx_L2_Gn_ENTRYy_CSR(table, 0, i), |
148 | RIO_RT_ENTRY_DROP_PKT); |
149 | if (err) |
150 | break; |
151 | } |
152 | |
153 | return err; |
154 | } |
155 | |
156 | /* |
157 | * This routine performs device-specific initialization only. |
158 | * All standard EM configuration should be performed at upper level. |
159 | */ |
160 | static int |
161 | idtg3_em_init(struct rio_dev *rdev) |
162 | { |
163 | int i, tmp; |
164 | u32 rval; |
165 | |
166 | pr_debug("RIO: %s [%d:%d]\n" , __func__, rdev->destid, rdev->hopcount); |
167 | |
168 | /* Disable assertion of interrupt signal */ |
169 | rio_write_config_32(rdev, RIO_EM_DEV_INT_EN, data: 0); |
170 | |
171 | /* Disable port-write event notifications during initialization */ |
172 | rio_write_config_32(rdev, offset: rdev->em_efptr + RIO_EM_PW_TX_CTRL, |
173 | RIO_EM_PW_TX_CTRL_PW_DIS); |
174 | |
175 | /* Configure Port-Write notifications for hot-swap events */ |
176 | tmp = RIO_GET_TOTAL_PORTS(rdev->swpinfo); |
177 | for (i = 0; i < tmp; i++) { |
178 | |
179 | rio_read_config_32(rdev, |
180 | RIO_DEV_PORT_N_ERR_STS_CSR(rdev, i), |
181 | data: &rval); |
182 | if (rval & RIO_PORT_N_ERR_STS_PORT_UA) |
183 | continue; |
184 | |
185 | /* Clear events signaled before enabling notification */ |
186 | rio_write_config_32(rdev, |
187 | offset: rdev->em_efptr + RIO_EM_PN_ERR_DETECT(i), data: 0); |
188 | |
189 | /* Enable event notifications */ |
190 | rio_write_config_32(rdev, |
191 | offset: rdev->em_efptr + RIO_EM_PN_ERRRATE_EN(i), |
192 | RIO_EM_PN_ERRRATE_EN_OK2U | RIO_EM_PN_ERRRATE_EN_U2OK); |
193 | /* Enable port-write generation on events */ |
194 | rio_write_config_32(rdev, RIO_PLM_SPx_PW_EN(i), |
195 | RIO_PLM_SPx_PW_EN_OK2U | RIO_PLM_SPx_PW_EN_LINIT); |
196 | |
197 | } |
198 | |
199 | /* Set Port-Write destination port */ |
200 | tmp = RIO_GET_PORT_NUM(rdev->swpinfo); |
201 | rio_write_config_32(rdev, RIO_PW_ROUTE, data: 1 << tmp); |
202 | |
203 | |
204 | /* Enable sending port-write event notifications */ |
205 | rio_write_config_32(rdev, offset: rdev->em_efptr + RIO_EM_PW_TX_CTRL, data: 0); |
206 | |
207 | /* set TVAL = ~50us */ |
208 | rio_write_config_32(rdev, |
209 | offset: rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, data: 0x8e << 8); |
210 | return 0; |
211 | } |
212 | |
213 | |
214 | /* |
215 | * idtg3_em_handler - device-specific error handler |
216 | * |
217 | * If the link is down (PORT_UNINIT) does nothing - this is considered |
218 | * as link partner removal from the port. |
219 | * |
220 | * If the link is up (PORT_OK) - situation is handled as *new* device insertion. |
221 | * In this case ERR_STOP bits are cleared by issuing soft reset command to the |
222 | * reporting port. Inbound and outbound ackIDs are cleared by the reset as well. |
223 | * This way the port is synchronized with freshly inserted device (assuming it |
224 | * was reset/powered-up on insertion). |
225 | * |
226 | * TODO: This is not sufficient in a situation when a link between two devices |
227 | * was down and up again (e.g. cable disconnect). For that situation full ackID |
228 | * realignment process has to be implemented. |
229 | */ |
230 | static int |
231 | idtg3_em_handler(struct rio_dev *rdev, u8 pnum) |
232 | { |
233 | u32 err_status; |
234 | u32 rval; |
235 | |
236 | rio_read_config_32(rdev, |
237 | RIO_DEV_PORT_N_ERR_STS_CSR(rdev, pnum), |
238 | data: &err_status); |
239 | |
240 | /* Do nothing for device/link removal */ |
241 | if (err_status & RIO_PORT_N_ERR_STS_PORT_UNINIT) |
242 | return 0; |
243 | |
244 | /* When link is OK we have a device insertion. |
245 | * Request port soft reset to clear errors if they present. |
246 | * Inbound and outbound ackIDs will be 0 after reset. |
247 | */ |
248 | if (err_status & (RIO_PORT_N_ERR_STS_OUT_ES | |
249 | RIO_PORT_N_ERR_STS_INP_ES)) { |
250 | rio_read_config_32(rdev, RIO_PLM_SPx_IMP_SPEC_CTL(pnum), data: &rval); |
251 | rio_write_config_32(rdev, RIO_PLM_SPx_IMP_SPEC_CTL(pnum), |
252 | data: rval | RIO_PLM_SPx_IMP_SPEC_CTL_SOFT_RST); |
253 | udelay(10); |
254 | rio_write_config_32(rdev, RIO_PLM_SPx_IMP_SPEC_CTL(pnum), data: rval); |
255 | msleep(msecs: 500); |
256 | } |
257 | |
258 | return 0; |
259 | } |
260 | |
261 | static struct rio_switch_ops idtg3_switch_ops = { |
262 | .owner = THIS_MODULE, |
263 | .add_entry = idtg3_route_add_entry, |
264 | .get_entry = idtg3_route_get_entry, |
265 | .clr_table = idtg3_route_clr_table, |
266 | .em_init = idtg3_em_init, |
267 | .em_handle = idtg3_em_handler, |
268 | }; |
269 | |
270 | static int idtg3_probe(struct rio_dev *rdev, const struct rio_device_id *id) |
271 | { |
272 | pr_debug("RIO: %s for %s\n" , __func__, rio_name(rdev)); |
273 | |
274 | spin_lock(lock: &rdev->rswitch->lock); |
275 | |
276 | if (rdev->rswitch->ops) { |
277 | spin_unlock(lock: &rdev->rswitch->lock); |
278 | return -EINVAL; |
279 | } |
280 | |
281 | rdev->rswitch->ops = &idtg3_switch_ops; |
282 | |
283 | if (rdev->do_enum) { |
284 | /* Disable hierarchical routing support: Existing fabric |
285 | * enumeration/discovery process (see rio-scan.c) uses 8-bit |
286 | * flat destination ID routing only. |
287 | */ |
288 | rio_write_config_32(rdev, offset: 0x5000 + RIO_BC_RT_CTL_CSR, data: 0); |
289 | } |
290 | |
291 | spin_unlock(lock: &rdev->rswitch->lock); |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static void idtg3_remove(struct rio_dev *rdev) |
297 | { |
298 | pr_debug("RIO: %s for %s\n" , __func__, rio_name(rdev)); |
299 | spin_lock(lock: &rdev->rswitch->lock); |
300 | if (rdev->rswitch->ops == &idtg3_switch_ops) |
301 | rdev->rswitch->ops = NULL; |
302 | spin_unlock(lock: &rdev->rswitch->lock); |
303 | } |
304 | |
305 | /* |
306 | * Gen3 switches repeat sending PW messages until a corresponding event flag |
307 | * is cleared. Use shutdown notification to disable generation of port-write |
308 | * messages if their destination node is shut down. |
309 | */ |
310 | static void idtg3_shutdown(struct rio_dev *rdev) |
311 | { |
312 | int i; |
313 | u32 rval; |
314 | u16 destid; |
315 | |
316 | /* Currently the enumerator node acts also as PW handler */ |
317 | if (!rdev->do_enum) |
318 | return; |
319 | |
320 | pr_debug("RIO: %s(%s)\n" , __func__, rio_name(rdev)); |
321 | |
322 | rio_read_config_32(rdev, RIO_PW_ROUTE, data: &rval); |
323 | i = RIO_GET_PORT_NUM(rdev->swpinfo); |
324 | |
325 | /* Check port-write destination port */ |
326 | if (!((1 << i) & rval)) |
327 | return; |
328 | |
329 | /* Disable sending port-write event notifications if PW destID |
330 | * matches to one of the enumerator node |
331 | */ |
332 | rio_read_config_32(rdev, offset: rdev->em_efptr + RIO_EM_PW_TGT_DEVID, data: &rval); |
333 | |
334 | if (rval & RIO_EM_PW_TGT_DEVID_DEV16) |
335 | destid = rval >> 16; |
336 | else |
337 | destid = ((rval & RIO_EM_PW_TGT_DEVID_D8) >> 16); |
338 | |
339 | if (rdev->net->hport->host_deviceid == destid) { |
340 | rio_write_config_32(rdev, |
341 | offset: rdev->em_efptr + RIO_EM_PW_TX_CTRL, data: 0); |
342 | pr_debug("RIO: %s(%s) PW transmission disabled\n" , |
343 | __func__, rio_name(rdev)); |
344 | } |
345 | } |
346 | |
347 | static const struct rio_device_id idtg3_id_table[] = { |
348 | {RIO_DEVICE(RIO_DID_IDTRXS1632, RIO_VID_IDT)}, |
349 | {RIO_DEVICE(RIO_DID_IDTRXS2448, RIO_VID_IDT)}, |
350 | { 0, } /* terminate list */ |
351 | }; |
352 | |
353 | static struct rio_driver idtg3_driver = { |
354 | .name = "idt_gen3" , |
355 | .id_table = idtg3_id_table, |
356 | .probe = idtg3_probe, |
357 | .remove = idtg3_remove, |
358 | .shutdown = idtg3_shutdown, |
359 | }; |
360 | |
361 | static int __init idtg3_init(void) |
362 | { |
363 | return rio_register_driver(&idtg3_driver); |
364 | } |
365 | |
366 | static void __exit idtg3_exit(void) |
367 | { |
368 | pr_debug("RIO: %s\n" , __func__); |
369 | rio_unregister_driver(&idtg3_driver); |
370 | pr_debug("RIO: %s done\n" , __func__); |
371 | } |
372 | |
373 | device_initcall(idtg3_init); |
374 | module_exit(idtg3_exit); |
375 | |
376 | MODULE_DESCRIPTION("IDT RXS Gen.3 Serial RapidIO switch family driver" ); |
377 | MODULE_AUTHOR("Integrated Device Technology, Inc." ); |
378 | MODULE_LICENSE("GPL" ); |
379 | |