diff options
Diffstat (limited to 'drivers/rapidio/switches/tsi57x.c')
-rw-r--r-- | drivers/rapidio/switches/tsi57x.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c new file mode 100644 index 000000000000..d34df722d95f --- /dev/null +++ b/drivers/rapidio/switches/tsi57x.c | |||
@@ -0,0 +1,315 @@ | |||
1 | /* | ||
2 | * RapidIO Tsi57x switch family support | ||
3 | * | ||
4 | * Copyright 2009-2010 Integrated Device Technology, Inc. | ||
5 | * Alexandre Bounine <alexandre.bounine@idt.com> | ||
6 | * - Added EM support | ||
7 | * - Modified switch operations initialization. | ||
8 | * | ||
9 | * Copyright 2005 MontaVista Software, Inc. | ||
10 | * Matt Porter <mporter@kernel.crashing.org> | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | */ | ||
17 | |||
18 | #include <linux/rio.h> | ||
19 | #include <linux/rio_drv.h> | ||
20 | #include <linux/rio_ids.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include "../rio.h" | ||
23 | |||
24 | /* Global (broadcast) route registers */ | ||
25 | #define SPBC_ROUTE_CFG_DESTID 0x10070 | ||
26 | #define SPBC_ROUTE_CFG_PORT 0x10074 | ||
27 | |||
28 | /* Per port route registers */ | ||
29 | #define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n) | ||
30 | #define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n) | ||
31 | |||
32 | #define TSI578_SP_MODE(n) (0x11004 + n*0x100) | ||
33 | #define TSI578_SP_MODE_GLBL 0x10004 | ||
34 | #define TSI578_SP_MODE_PW_DIS 0x08000000 | ||
35 | #define TSI578_SP_MODE_LUT_512 0x01000000 | ||
36 | |||
37 | #define TSI578_SP_CTL_INDEP(n) (0x13004 + n*0x100) | ||
38 | #define TSI578_SP_LUT_PEINF(n) (0x13010 + n*0x100) | ||
39 | #define TSI578_SP_CS_TX(n) (0x13014 + n*0x100) | ||
40 | #define TSI578_SP_INT_STATUS(n) (0x13018 + n*0x100) | ||
41 | |||
42 | #define TSI578_GLBL_ROUTE_BASE 0x10078 | ||
43 | |||
44 | static int | ||
45 | tsi57x_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, | ||
46 | u16 table, u16 route_destid, u8 route_port) | ||
47 | { | ||
48 | if (table == RIO_GLOBAL_TABLE) { | ||
49 | rio_mport_write_config_32(mport, destid, hopcount, | ||
50 | SPBC_ROUTE_CFG_DESTID, route_destid); | ||
51 | rio_mport_write_config_32(mport, destid, hopcount, | ||
52 | SPBC_ROUTE_CFG_PORT, route_port); | ||
53 | } else { | ||
54 | rio_mport_write_config_32(mport, destid, hopcount, | ||
55 | SPP_ROUTE_CFG_DESTID(table), route_destid); | ||
56 | rio_mport_write_config_32(mport, destid, hopcount, | ||
57 | SPP_ROUTE_CFG_PORT(table), route_port); | ||
58 | } | ||
59 | |||
60 | udelay(10); | ||
61 | |||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int | ||
66 | tsi57x_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, | ||
67 | u16 table, u16 route_destid, u8 *route_port) | ||
68 | { | ||
69 | int ret = 0; | ||
70 | u32 result; | ||
71 | |||
72 | if (table == RIO_GLOBAL_TABLE) { | ||
73 | /* Use local RT of the ingress port to avoid possible | ||
74 | race condition */ | ||
75 | rio_mport_read_config_32(mport, destid, hopcount, | ||
76 | RIO_SWP_INFO_CAR, &result); | ||
77 | table = (result & RIO_SWP_INFO_PORT_NUM_MASK); | ||
78 | } | ||
79 | |||
80 | rio_mport_write_config_32(mport, destid, hopcount, | ||
81 | SPP_ROUTE_CFG_DESTID(table), route_destid); | ||
82 | rio_mport_read_config_32(mport, destid, hopcount, | ||
83 | SPP_ROUTE_CFG_PORT(table), &result); | ||
84 | |||
85 | *route_port = (u8)result; | ||
86 | if (*route_port > 15) | ||
87 | ret = -1; | ||
88 | |||
89 | return ret; | ||
90 | } | ||
91 | |||
92 | static int | ||
93 | tsi57x_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, | ||
94 | u16 table) | ||
95 | { | ||
96 | u32 route_idx; | ||
97 | u32 lut_size; | ||
98 | |||
99 | lut_size = (mport->sys_size) ? 0x1ff : 0xff; | ||
100 | |||
101 | if (table == RIO_GLOBAL_TABLE) { | ||
102 | rio_mport_write_config_32(mport, destid, hopcount, | ||
103 | SPBC_ROUTE_CFG_DESTID, 0x80000000); | ||
104 | for (route_idx = 0; route_idx <= lut_size; route_idx++) | ||
105 | rio_mport_write_config_32(mport, destid, hopcount, | ||
106 | SPBC_ROUTE_CFG_PORT, | ||
107 | RIO_INVALID_ROUTE); | ||
108 | } else { | ||
109 | rio_mport_write_config_32(mport, destid, hopcount, | ||
110 | SPP_ROUTE_CFG_DESTID(table), 0x80000000); | ||
111 | for (route_idx = 0; route_idx <= lut_size; route_idx++) | ||
112 | rio_mport_write_config_32(mport, destid, hopcount, | ||
113 | SPP_ROUTE_CFG_PORT(table) , RIO_INVALID_ROUTE); | ||
114 | } | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static int | ||
120 | tsi57x_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount, | ||
121 | u8 sw_domain) | ||
122 | { | ||
123 | u32 regval; | ||
124 | |||
125 | /* | ||
126 | * Switch domain configuration operates only at global level | ||
127 | */ | ||
128 | |||
129 | /* Turn off flat (LUT_512) mode */ | ||
130 | rio_mport_read_config_32(mport, destid, hopcount, | ||
131 | TSI578_SP_MODE_GLBL, ®val); | ||
132 | rio_mport_write_config_32(mport, destid, hopcount, TSI578_SP_MODE_GLBL, | ||
133 | regval & ~TSI578_SP_MODE_LUT_512); | ||
134 | /* Set switch domain base */ | ||
135 | rio_mport_write_config_32(mport, destid, hopcount, | ||
136 | TSI578_GLBL_ROUTE_BASE, | ||
137 | (u32)(sw_domain << 24)); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int | ||
142 | tsi57x_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, | ||
143 | u8 *sw_domain) | ||
144 | { | ||
145 | u32 regval; | ||
146 | |||
147 | /* | ||
148 | * Switch domain configuration operates only at global level | ||
149 | */ | ||
150 | rio_mport_read_config_32(mport, destid, hopcount, | ||
151 | TSI578_GLBL_ROUTE_BASE, ®val); | ||
152 | |||
153 | *sw_domain = (u8)(regval >> 24); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int | ||
159 | tsi57x_em_init(struct rio_dev *rdev) | ||
160 | { | ||
161 | struct rio_mport *mport = rdev->net->hport; | ||
162 | u16 destid = rdev->rswitch->destid; | ||
163 | u8 hopcount = rdev->rswitch->hopcount; | ||
164 | u32 regval; | ||
165 | int portnum; | ||
166 | |||
167 | pr_debug("TSI578 %s [%d:%d]\n", __func__, destid, hopcount); | ||
168 | |||
169 | for (portnum = 0; portnum < 16; portnum++) { | ||
170 | /* Make sure that Port-Writes are enabled (for all ports) */ | ||
171 | rio_mport_read_config_32(mport, destid, hopcount, | ||
172 | TSI578_SP_MODE(portnum), ®val); | ||
173 | rio_mport_write_config_32(mport, destid, hopcount, | ||
174 | TSI578_SP_MODE(portnum), | ||
175 | regval & ~TSI578_SP_MODE_PW_DIS); | ||
176 | |||
177 | /* Clear all pending interrupts */ | ||
178 | rio_mport_read_config_32(mport, destid, hopcount, | ||
179 | rdev->phys_efptr + | ||
180 | RIO_PORT_N_ERR_STS_CSR(portnum), | ||
181 | ®val); | ||
182 | rio_mport_write_config_32(mport, destid, hopcount, | ||
183 | rdev->phys_efptr + | ||
184 | RIO_PORT_N_ERR_STS_CSR(portnum), | ||
185 | regval & 0x07120214); | ||
186 | |||
187 | rio_mport_read_config_32(mport, destid, hopcount, | ||
188 | TSI578_SP_INT_STATUS(portnum), ®val); | ||
189 | rio_mport_write_config_32(mport, destid, hopcount, | ||
190 | TSI578_SP_INT_STATUS(portnum), | ||
191 | regval & 0x000700bd); | ||
192 | |||
193 | /* Enable all interrupts to allow ports to send a port-write */ | ||
194 | rio_mport_read_config_32(mport, destid, hopcount, | ||
195 | TSI578_SP_CTL_INDEP(portnum), ®val); | ||
196 | rio_mport_write_config_32(mport, destid, hopcount, | ||
197 | TSI578_SP_CTL_INDEP(portnum), | ||
198 | regval | 0x000b0000); | ||
199 | |||
200 | /* Skip next (odd) port if the current port is in x4 mode */ | ||
201 | rio_mport_read_config_32(mport, destid, hopcount, | ||
202 | rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum), | ||
203 | ®val); | ||
204 | if ((regval & RIO_PORT_N_CTL_PWIDTH) == RIO_PORT_N_CTL_PWIDTH_4) | ||
205 | portnum++; | ||
206 | } | ||
207 | |||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int | ||
212 | tsi57x_em_handler(struct rio_dev *rdev, u8 portnum) | ||
213 | { | ||
214 | struct rio_mport *mport = rdev->net->hport; | ||
215 | u16 destid = rdev->rswitch->destid; | ||
216 | u8 hopcount = rdev->rswitch->hopcount; | ||
217 | u32 intstat, err_status; | ||
218 | int sendcount, checkcount; | ||
219 | u8 route_port; | ||
220 | u32 regval; | ||
221 | |||
222 | rio_mport_read_config_32(mport, destid, hopcount, | ||
223 | rdev->phys_efptr + RIO_PORT_N_ERR_STS_CSR(portnum), | ||
224 | &err_status); | ||
225 | |||
226 | if ((err_status & RIO_PORT_N_ERR_STS_PORT_OK) && | ||
227 | (err_status & (RIO_PORT_N_ERR_STS_PW_OUT_ES | | ||
228 | RIO_PORT_N_ERR_STS_PW_INP_ES))) { | ||
229 | /* Remove any queued packets by locking/unlocking port */ | ||
230 | rio_mport_read_config_32(mport, destid, hopcount, | ||
231 | rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum), | ||
232 | ®val); | ||
233 | if (!(regval & RIO_PORT_N_CTL_LOCKOUT)) { | ||
234 | rio_mport_write_config_32(mport, destid, hopcount, | ||
235 | rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum), | ||
236 | regval | RIO_PORT_N_CTL_LOCKOUT); | ||
237 | udelay(50); | ||
238 | rio_mport_write_config_32(mport, destid, hopcount, | ||
239 | rdev->phys_efptr + RIO_PORT_N_CTL_CSR(portnum), | ||
240 | regval); | ||
241 | } | ||
242 | |||
243 | /* Read from link maintenance response register to clear | ||
244 | * valid bit | ||
245 | */ | ||
246 | rio_mport_read_config_32(mport, destid, hopcount, | ||
247 | rdev->phys_efptr + RIO_PORT_N_MNT_RSP_CSR(portnum), | ||
248 | ®val); | ||
249 | |||
250 | /* Send a Packet-Not-Accepted/Link-Request-Input-Status control | ||
251 | * symbol to recover from IES/OES | ||
252 | */ | ||
253 | sendcount = 3; | ||
254 | while (sendcount) { | ||
255 | rio_mport_write_config_32(mport, destid, hopcount, | ||
256 | TSI578_SP_CS_TX(portnum), 0x40fc8000); | ||
257 | checkcount = 3; | ||
258 | while (checkcount--) { | ||
259 | udelay(50); | ||
260 | rio_mport_read_config_32( | ||
261 | mport, destid, hopcount, | ||
262 | rdev->phys_efptr + | ||
263 | RIO_PORT_N_MNT_RSP_CSR(portnum), | ||
264 | ®val); | ||
265 | if (regval & RIO_PORT_N_MNT_RSP_RVAL) | ||
266 | goto exit_es; | ||
267 | } | ||
268 | |||
269 | sendcount--; | ||
270 | } | ||
271 | } | ||
272 | |||
273 | exit_es: | ||
274 | /* Clear implementation specific error status bits */ | ||
275 | rio_mport_read_config_32(mport, destid, hopcount, | ||
276 | TSI578_SP_INT_STATUS(portnum), &intstat); | ||
277 | pr_debug("TSI578[%x:%x] SP%d_INT_STATUS=0x%08x\n", | ||
278 | destid, hopcount, portnum, intstat); | ||
279 | |||
280 | if (intstat & 0x10000) { | ||
281 | rio_mport_read_config_32(mport, destid, hopcount, | ||
282 | TSI578_SP_LUT_PEINF(portnum), ®val); | ||
283 | regval = (mport->sys_size) ? (regval >> 16) : (regval >> 24); | ||
284 | route_port = rdev->rswitch->route_table[regval]; | ||
285 | pr_debug("RIO: TSI578[%s] P%d LUT Parity Error (destID=%d)\n", | ||
286 | rio_name(rdev), portnum, regval); | ||
287 | tsi57x_route_add_entry(mport, destid, hopcount, | ||
288 | RIO_GLOBAL_TABLE, regval, route_port); | ||
289 | } | ||
290 | |||
291 | rio_mport_write_config_32(mport, destid, hopcount, | ||
292 | TSI578_SP_INT_STATUS(portnum), | ||
293 | intstat & 0x000700bd); | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static int tsi57x_switch_init(struct rio_dev *rdev, int do_enum) | ||
299 | { | ||
300 | pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); | ||
301 | rdev->rswitch->add_entry = tsi57x_route_add_entry; | ||
302 | rdev->rswitch->get_entry = tsi57x_route_get_entry; | ||
303 | rdev->rswitch->clr_table = tsi57x_route_clr_table; | ||
304 | rdev->rswitch->set_domain = tsi57x_set_domain; | ||
305 | rdev->rswitch->get_domain = tsi57x_get_domain; | ||
306 | rdev->rswitch->em_init = tsi57x_em_init; | ||
307 | rdev->rswitch->em_handle = tsi57x_em_handler; | ||
308 | |||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI572, tsi57x_switch_init); | ||
313 | DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI574, tsi57x_switch_init); | ||
314 | DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI577, tsi57x_switch_init); | ||
315 | DECLARE_RIO_SWITCH_INIT(RIO_VID_TUNDRA, RIO_DID_TSI578, tsi57x_switch_init); | ||