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