diff options
Diffstat (limited to 'drivers/net/chelsio/vsc7326.c')
-rw-r--r-- | drivers/net/chelsio/vsc7326.c | 725 |
1 files changed, 725 insertions, 0 deletions
diff --git a/drivers/net/chelsio/vsc7326.c b/drivers/net/chelsio/vsc7326.c new file mode 100644 index 000000000000..85dc3b1dc309 --- /dev/null +++ b/drivers/net/chelsio/vsc7326.c | |||
@@ -0,0 +1,725 @@ | |||
1 | /* $Date: 2006/04/28 19:20:06 $ $RCSfile: vsc7326.c,v $ $Revision: 1.19 $ */ | ||
2 | |||
3 | /* Driver for Vitesse VSC7326 (Schaumburg) MAC */ | ||
4 | |||
5 | #include "gmac.h" | ||
6 | #include "elmer0.h" | ||
7 | #include "vsc7326_reg.h" | ||
8 | |||
9 | /* Update fast changing statistics every 15 seconds */ | ||
10 | #define STATS_TICK_SECS 15 | ||
11 | /* 30 minutes for full statistics update */ | ||
12 | #define MAJOR_UPDATE_TICKS (1800 / STATS_TICK_SECS) | ||
13 | |||
14 | #define MAX_MTU 9600 | ||
15 | |||
16 | /* The egress WM value 0x01a01fff should be used only when the | ||
17 | * interface is down (MAC port disabled). This is a workaround | ||
18 | * for disabling the T2/MAC flow-control. When the interface is | ||
19 | * enabled, the WM value should be set to 0x014a03F0. | ||
20 | */ | ||
21 | #define WM_DISABLE 0x01a01fff | ||
22 | #define WM_ENABLE 0x014a03F0 | ||
23 | |||
24 | struct init_table { | ||
25 | u32 addr; | ||
26 | u32 data; | ||
27 | }; | ||
28 | |||
29 | struct _cmac_instance { | ||
30 | u32 index; | ||
31 | u32 ticks; | ||
32 | }; | ||
33 | |||
34 | #define INITBLOCK_SLEEP 0xffffffff | ||
35 | |||
36 | static void vsc_read(adapter_t *adapter, u32 addr, u32 *val) | ||
37 | { | ||
38 | u32 status, vlo, vhi; | ||
39 | int i; | ||
40 | |||
41 | spin_lock_bh(&adapter->mac_lock); | ||
42 | t1_tpi_read(adapter, (addr << 2) + 4, &vlo); | ||
43 | i = 0; | ||
44 | do { | ||
45 | t1_tpi_read(adapter, (REG_LOCAL_STATUS << 2) + 4, &vlo); | ||
46 | t1_tpi_read(adapter, REG_LOCAL_STATUS << 2, &vhi); | ||
47 | status = (vhi << 16) | vlo; | ||
48 | i++; | ||
49 | } while (((status & 1) == 0) && (i < 50)); | ||
50 | if (i == 50) | ||
51 | CH_ERR("Invalid tpi read from MAC, breaking loop.\n"); | ||
52 | |||
53 | t1_tpi_read(adapter, (REG_LOCAL_DATA << 2) + 4, &vlo); | ||
54 | t1_tpi_read(adapter, REG_LOCAL_DATA << 2, &vhi); | ||
55 | |||
56 | *val = (vhi << 16) | vlo; | ||
57 | |||
58 | /* CH_ERR("rd: block: 0x%x sublock: 0x%x reg: 0x%x data: 0x%x\n", | ||
59 | ((addr&0xe000)>>13), ((addr&0x1e00)>>9), | ||
60 | ((addr&0x01fe)>>1), *val); */ | ||
61 | spin_unlock_bh(&adapter->mac_lock); | ||
62 | } | ||
63 | |||
64 | static void vsc_write(adapter_t *adapter, u32 addr, u32 data) | ||
65 | { | ||
66 | spin_lock_bh(&adapter->mac_lock); | ||
67 | t1_tpi_write(adapter, (addr << 2) + 4, data & 0xFFFF); | ||
68 | t1_tpi_write(adapter, addr << 2, (data >> 16) & 0xFFFF); | ||
69 | /* CH_ERR("wr: block: 0x%x sublock: 0x%x reg: 0x%x data: 0x%x\n", | ||
70 | ((addr&0xe000)>>13), ((addr&0x1e00)>>9), | ||
71 | ((addr&0x01fe)>>1), data); */ | ||
72 | spin_unlock_bh(&adapter->mac_lock); | ||
73 | } | ||
74 | |||
75 | /* Hard reset the MAC. This wipes out *all* configuration. */ | ||
76 | static void vsc7326_full_reset(adapter_t* adapter) | ||
77 | { | ||
78 | u32 val; | ||
79 | u32 result = 0xffff; | ||
80 | |||
81 | t1_tpi_read(adapter, A_ELMER0_GPO, &val); | ||
82 | val &= ~1; | ||
83 | t1_tpi_write(adapter, A_ELMER0_GPO, val); | ||
84 | udelay(2); | ||
85 | val |= 0x1; /* Enable mac MAC itself */ | ||
86 | val |= 0x800; /* Turn off the red LED */ | ||
87 | t1_tpi_write(adapter, A_ELMER0_GPO, val); | ||
88 | mdelay(1); | ||
89 | vsc_write(adapter, REG_SW_RESET, 0x80000001); | ||
90 | do { | ||
91 | mdelay(1); | ||
92 | vsc_read(adapter, REG_SW_RESET, &result); | ||
93 | } while (result != 0x0); | ||
94 | } | ||
95 | |||
96 | static struct init_table vsc7326_reset[] = { | ||
97 | { REG_IFACE_MODE, 0x00000000 }, | ||
98 | { REG_CRC_CFG, 0x00000020 }, | ||
99 | { REG_PLL_CLK_SPEED, 0x00050c00 }, | ||
100 | { REG_PLL_CLK_SPEED, 0x00050c00 }, | ||
101 | { REG_MSCH, 0x00002f14 }, | ||
102 | { REG_SPI4_MISC, 0x00040409 }, | ||
103 | { REG_SPI4_DESKEW, 0x00080000 }, | ||
104 | { REG_SPI4_ING_SETUP2, 0x08080004 }, | ||
105 | { REG_SPI4_ING_SETUP0, 0x04111004 }, | ||
106 | { REG_SPI4_EGR_SETUP0, 0x80001a04 }, | ||
107 | { REG_SPI4_ING_SETUP1, 0x02010000 }, | ||
108 | { REG_AGE_INC(0), 0x00000000 }, | ||
109 | { REG_AGE_INC(1), 0x00000000 }, | ||
110 | { REG_ING_CONTROL, 0x0a200011 }, | ||
111 | { REG_EGR_CONTROL, 0xa0010091 }, | ||
112 | }; | ||
113 | |||
114 | static struct init_table vsc7326_portinit[4][22] = { | ||
115 | { /* Port 0 */ | ||
116 | /* FIFO setup */ | ||
117 | { REG_DBG(0), 0x000004f0 }, | ||
118 | { REG_HDX(0), 0x00073101 }, | ||
119 | { REG_TEST(0,0), 0x00000022 }, | ||
120 | { REG_TEST(1,0), 0x00000022 }, | ||
121 | { REG_TOP_BOTTOM(0,0), 0x003f0000 }, | ||
122 | { REG_TOP_BOTTOM(1,0), 0x00120000 }, | ||
123 | { REG_HIGH_LOW_WM(0,0), 0x07460757 }, | ||
124 | { REG_HIGH_LOW_WM(1,0), WM_DISABLE }, | ||
125 | { REG_CT_THRHLD(0,0), 0x00000000 }, | ||
126 | { REG_CT_THRHLD(1,0), 0x00000000 }, | ||
127 | { REG_BUCKE(0), 0x0002ffff }, | ||
128 | { REG_BUCKI(0), 0x0002ffff }, | ||
129 | { REG_TEST(0,0), 0x00000020 }, | ||
130 | { REG_TEST(1,0), 0x00000020 }, | ||
131 | /* Port config */ | ||
132 | { REG_MAX_LEN(0), 0x00002710 }, | ||
133 | { REG_PORT_FAIL(0), 0x00000002 }, | ||
134 | { REG_NORMALIZER(0), 0x00000a64 }, | ||
135 | { REG_DENORM(0), 0x00000010 }, | ||
136 | { REG_STICK_BIT(0), 0x03baa370 }, | ||
137 | { REG_DEV_SETUP(0), 0x00000083 }, | ||
138 | { REG_DEV_SETUP(0), 0x00000082 }, | ||
139 | { REG_MODE_CFG(0), 0x0200259f }, | ||
140 | }, | ||
141 | { /* Port 1 */ | ||
142 | /* FIFO setup */ | ||
143 | { REG_DBG(1), 0x000004f0 }, | ||
144 | { REG_HDX(1), 0x00073101 }, | ||
145 | { REG_TEST(0,1), 0x00000022 }, | ||
146 | { REG_TEST(1,1), 0x00000022 }, | ||
147 | { REG_TOP_BOTTOM(0,1), 0x007e003f }, | ||
148 | { REG_TOP_BOTTOM(1,1), 0x00240012 }, | ||
149 | { REG_HIGH_LOW_WM(0,1), 0x07460757 }, | ||
150 | { REG_HIGH_LOW_WM(1,1), WM_DISABLE }, | ||
151 | { REG_CT_THRHLD(0,1), 0x00000000 }, | ||
152 | { REG_CT_THRHLD(1,1), 0x00000000 }, | ||
153 | { REG_BUCKE(1), 0x0002ffff }, | ||
154 | { REG_BUCKI(1), 0x0002ffff }, | ||
155 | { REG_TEST(0,1), 0x00000020 }, | ||
156 | { REG_TEST(1,1), 0x00000020 }, | ||
157 | /* Port config */ | ||
158 | { REG_MAX_LEN(1), 0x00002710 }, | ||
159 | { REG_PORT_FAIL(1), 0x00000002 }, | ||
160 | { REG_NORMALIZER(1), 0x00000a64 }, | ||
161 | { REG_DENORM(1), 0x00000010 }, | ||
162 | { REG_STICK_BIT(1), 0x03baa370 }, | ||
163 | { REG_DEV_SETUP(1), 0x00000083 }, | ||
164 | { REG_DEV_SETUP(1), 0x00000082 }, | ||
165 | { REG_MODE_CFG(1), 0x0200259f }, | ||
166 | }, | ||
167 | { /* Port 2 */ | ||
168 | /* FIFO setup */ | ||
169 | { REG_DBG(2), 0x000004f0 }, | ||
170 | { REG_HDX(2), 0x00073101 }, | ||
171 | { REG_TEST(0,2), 0x00000022 }, | ||
172 | { REG_TEST(1,2), 0x00000022 }, | ||
173 | { REG_TOP_BOTTOM(0,2), 0x00bd007e }, | ||
174 | { REG_TOP_BOTTOM(1,2), 0x00360024 }, | ||
175 | { REG_HIGH_LOW_WM(0,2), 0x07460757 }, | ||
176 | { REG_HIGH_LOW_WM(1,2), WM_DISABLE }, | ||
177 | { REG_CT_THRHLD(0,2), 0x00000000 }, | ||
178 | { REG_CT_THRHLD(1,2), 0x00000000 }, | ||
179 | { REG_BUCKE(2), 0x0002ffff }, | ||
180 | { REG_BUCKI(2), 0x0002ffff }, | ||
181 | { REG_TEST(0,2), 0x00000020 }, | ||
182 | { REG_TEST(1,2), 0x00000020 }, | ||
183 | /* Port config */ | ||
184 | { REG_MAX_LEN(2), 0x00002710 }, | ||
185 | { REG_PORT_FAIL(2), 0x00000002 }, | ||
186 | { REG_NORMALIZER(2), 0x00000a64 }, | ||
187 | { REG_DENORM(2), 0x00000010 }, | ||
188 | { REG_STICK_BIT(2), 0x03baa370 }, | ||
189 | { REG_DEV_SETUP(2), 0x00000083 }, | ||
190 | { REG_DEV_SETUP(2), 0x00000082 }, | ||
191 | { REG_MODE_CFG(2), 0x0200259f }, | ||
192 | }, | ||
193 | { /* Port 3 */ | ||
194 | /* FIFO setup */ | ||
195 | { REG_DBG(3), 0x000004f0 }, | ||
196 | { REG_HDX(3), 0x00073101 }, | ||
197 | { REG_TEST(0,3), 0x00000022 }, | ||
198 | { REG_TEST(1,3), 0x00000022 }, | ||
199 | { REG_TOP_BOTTOM(0,3), 0x00fc00bd }, | ||
200 | { REG_TOP_BOTTOM(1,3), 0x00480036 }, | ||
201 | { REG_HIGH_LOW_WM(0,3), 0x07460757 }, | ||
202 | { REG_HIGH_LOW_WM(1,3), WM_DISABLE }, | ||
203 | { REG_CT_THRHLD(0,3), 0x00000000 }, | ||
204 | { REG_CT_THRHLD(1,3), 0x00000000 }, | ||
205 | { REG_BUCKE(3), 0x0002ffff }, | ||
206 | { REG_BUCKI(3), 0x0002ffff }, | ||
207 | { REG_TEST(0,3), 0x00000020 }, | ||
208 | { REG_TEST(1,3), 0x00000020 }, | ||
209 | /* Port config */ | ||
210 | { REG_MAX_LEN(3), 0x00002710 }, | ||
211 | { REG_PORT_FAIL(3), 0x00000002 }, | ||
212 | { REG_NORMALIZER(3), 0x00000a64 }, | ||
213 | { REG_DENORM(3), 0x00000010 }, | ||
214 | { REG_STICK_BIT(3), 0x03baa370 }, | ||
215 | { REG_DEV_SETUP(3), 0x00000083 }, | ||
216 | { REG_DEV_SETUP(3), 0x00000082 }, | ||
217 | { REG_MODE_CFG(3), 0x0200259f }, | ||
218 | }, | ||
219 | }; | ||
220 | |||
221 | static void run_table(adapter_t *adapter, struct init_table *ib, int len) | ||
222 | { | ||
223 | int i; | ||
224 | |||
225 | for (i = 0; i < len; i++) { | ||
226 | if (ib[i].addr == INITBLOCK_SLEEP) { | ||
227 | udelay( ib[i].data ); | ||
228 | CH_ERR("sleep %d us\n",ib[i].data); | ||
229 | } else { | ||
230 | vsc_write( adapter, ib[i].addr, ib[i].data ); | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | |||
235 | static int bist_rd(adapter_t *adapter, int moduleid, int address) | ||
236 | { | ||
237 | int data=0; | ||
238 | u32 result=0; | ||
239 | |||
240 | if( (address != 0x0) && | ||
241 | (address != 0x1) && | ||
242 | (address != 0x2) && | ||
243 | (address != 0xd) && | ||
244 | (address != 0xe)) | ||
245 | CH_ERR("No bist address: 0x%x\n", address); | ||
246 | |||
247 | data = ((0x00 << 24) | ((address & 0xff) << 16) | (0x00 << 8) | | ||
248 | ((moduleid & 0xff) << 0)); | ||
249 | vsc_write(adapter, REG_RAM_BIST_CMD, data); | ||
250 | |||
251 | udelay(10); | ||
252 | |||
253 | vsc_read(adapter, REG_RAM_BIST_RESULT, &result); | ||
254 | if((result & (1<<9)) != 0x0) | ||
255 | CH_ERR("Still in bist read: 0x%x\n", result); | ||
256 | else if((result & (1<<8)) != 0x0) | ||
257 | CH_ERR("bist read error: 0x%x\n", result); | ||
258 | |||
259 | return(result & 0xff); | ||
260 | } | ||
261 | |||
262 | static int bist_wr(adapter_t *adapter, int moduleid, int address, int value) | ||
263 | { | ||
264 | int data=0; | ||
265 | u32 result=0; | ||
266 | |||
267 | if( (address != 0x0) && | ||
268 | (address != 0x1) && | ||
269 | (address != 0x2) && | ||
270 | (address != 0xd) && | ||
271 | (address != 0xe)) | ||
272 | CH_ERR("No bist address: 0x%x\n", address); | ||
273 | |||
274 | if( value>255 ) | ||
275 | CH_ERR("Suspicious write out of range value: 0x%x\n", value); | ||
276 | |||
277 | data = ((0x01 << 24) | ((address & 0xff) << 16) | (value << 8) | | ||
278 | ((moduleid & 0xff) << 0)); | ||
279 | vsc_write(adapter, REG_RAM_BIST_CMD, data); | ||
280 | |||
281 | udelay(5); | ||
282 | |||
283 | vsc_read(adapter, REG_RAM_BIST_CMD, &result); | ||
284 | if((result & (1<<27)) != 0x0) | ||
285 | CH_ERR("Still in bist write: 0x%x\n", result); | ||
286 | else if((result & (1<<26)) != 0x0) | ||
287 | CH_ERR("bist write error: 0x%x\n", result); | ||
288 | |||
289 | return(0); | ||
290 | } | ||
291 | |||
292 | static int run_bist(adapter_t *adapter, int moduleid) | ||
293 | { | ||
294 | /*run bist*/ | ||
295 | (void) bist_wr(adapter,moduleid, 0x00, 0x02); | ||
296 | (void) bist_wr(adapter,moduleid, 0x01, 0x01); | ||
297 | |||
298 | return(0); | ||
299 | } | ||
300 | |||
301 | static int check_bist(adapter_t *adapter, int moduleid) | ||
302 | { | ||
303 | int result=0; | ||
304 | int column=0; | ||
305 | /*check bist*/ | ||
306 | result = bist_rd(adapter,moduleid, 0x02); | ||
307 | column = ((bist_rd(adapter,moduleid, 0x0e)<<8) + | ||
308 | (bist_rd(adapter,moduleid, 0x0d))); | ||
309 | if ((result & 3) != 0x3) | ||
310 | CH_ERR("Result: 0x%x BIST error in ram %d, column: 0x%04x\n", | ||
311 | result, moduleid, column); | ||
312 | return(0); | ||
313 | } | ||
314 | |||
315 | static int enable_mem(adapter_t *adapter, int moduleid) | ||
316 | { | ||
317 | /*enable mem*/ | ||
318 | (void) bist_wr(adapter,moduleid, 0x00, 0x00); | ||
319 | return(0); | ||
320 | } | ||
321 | |||
322 | static int run_bist_all(adapter_t *adapter) | ||
323 | { | ||
324 | int port=0; | ||
325 | u32 val=0; | ||
326 | |||
327 | vsc_write(adapter, REG_MEM_BIST, 0x5); | ||
328 | vsc_read(adapter, REG_MEM_BIST, &val); | ||
329 | |||
330 | for(port=0; port<12; port++){ | ||
331 | vsc_write(adapter, REG_DEV_SETUP(port), 0x0); | ||
332 | } | ||
333 | |||
334 | udelay(300); | ||
335 | vsc_write(adapter, REG_SPI4_MISC, 0x00040409); | ||
336 | udelay(300); | ||
337 | |||
338 | (void) run_bist(adapter,13); | ||
339 | (void) run_bist(adapter,14); | ||
340 | (void) run_bist(adapter,20); | ||
341 | (void) run_bist(adapter,21); | ||
342 | mdelay(200); | ||
343 | (void) check_bist(adapter,13); | ||
344 | (void) check_bist(adapter,14); | ||
345 | (void) check_bist(adapter,20); | ||
346 | (void) check_bist(adapter,21); | ||
347 | udelay(100); | ||
348 | (void) enable_mem(adapter,13); | ||
349 | (void) enable_mem(adapter,14); | ||
350 | (void) enable_mem(adapter,20); | ||
351 | (void) enable_mem(adapter,21); | ||
352 | udelay(300); | ||
353 | vsc_write(adapter, REG_SPI4_MISC, 0x60040400); | ||
354 | udelay(300); | ||
355 | for(port=0; port<12; port++){ | ||
356 | vsc_write(adapter, REG_DEV_SETUP(port), 0x1); | ||
357 | } | ||
358 | udelay(300); | ||
359 | vsc_write(adapter, REG_MEM_BIST, 0x0); | ||
360 | mdelay(10); | ||
361 | return(0); | ||
362 | } | ||
363 | |||
364 | static int mac_intr_handler(struct cmac *mac) | ||
365 | { | ||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int mac_intr_enable(struct cmac *mac) | ||
370 | { | ||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | static int mac_intr_disable(struct cmac *mac) | ||
375 | { | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int mac_intr_clear(struct cmac *mac) | ||
380 | { | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | /* Expect MAC address to be in network byte order. */ | ||
385 | static int mac_set_address(struct cmac* mac, u8 addr[6]) | ||
386 | { | ||
387 | u32 val; | ||
388 | int port = mac->instance->index; | ||
389 | |||
390 | vsc_write(mac->adapter, REG_MAC_LOW_ADDR(port), | ||
391 | (addr[3] << 16) | (addr[4] << 8) | addr[5]); | ||
392 | vsc_write(mac->adapter, REG_MAC_HIGH_ADDR(port), | ||
393 | (addr[0] << 16) | (addr[1] << 8) | addr[2]); | ||
394 | |||
395 | vsc_read(mac->adapter, REG_ING_FFILT_UM_EN, &val); | ||
396 | val &= ~0xf0000000; | ||
397 | vsc_write(mac->adapter, REG_ING_FFILT_UM_EN, val | (port << 28)); | ||
398 | |||
399 | vsc_write(mac->adapter, REG_ING_FFILT_MASK0, | ||
400 | 0xffff0000 | (addr[4] << 8) | addr[5]); | ||
401 | vsc_write(mac->adapter, REG_ING_FFILT_MASK1, | ||
402 | 0xffff0000 | (addr[2] << 8) | addr[3]); | ||
403 | vsc_write(mac->adapter, REG_ING_FFILT_MASK2, | ||
404 | 0xffff0000 | (addr[0] << 8) | addr[1]); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int mac_get_address(struct cmac *mac, u8 addr[6]) | ||
409 | { | ||
410 | u32 addr_lo, addr_hi; | ||
411 | int port = mac->instance->index; | ||
412 | |||
413 | vsc_read(mac->adapter, REG_MAC_LOW_ADDR(port), &addr_lo); | ||
414 | vsc_read(mac->adapter, REG_MAC_HIGH_ADDR(port), &addr_hi); | ||
415 | |||
416 | addr[0] = (u8) (addr_hi >> 16); | ||
417 | addr[1] = (u8) (addr_hi >> 8); | ||
418 | addr[2] = (u8) addr_hi; | ||
419 | addr[3] = (u8) (addr_lo >> 16); | ||
420 | addr[4] = (u8) (addr_lo >> 8); | ||
421 | addr[5] = (u8) addr_lo; | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* This is intended to reset a port, not the whole MAC */ | ||
426 | static int mac_reset(struct cmac *mac) | ||
427 | { | ||
428 | int index = mac->instance->index; | ||
429 | |||
430 | run_table(mac->adapter, vsc7326_portinit[index], | ||
431 | ARRAY_SIZE(vsc7326_portinit[index])); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm) | ||
437 | { | ||
438 | u32 v; | ||
439 | int port = mac->instance->index; | ||
440 | |||
441 | vsc_read(mac->adapter, REG_ING_FFILT_UM_EN, &v); | ||
442 | v |= 1 << 12; | ||
443 | |||
444 | if (t1_rx_mode_promisc(rm)) | ||
445 | v &= ~(1 << (port + 16)); | ||
446 | else | ||
447 | v |= 1 << (port + 16); | ||
448 | |||
449 | vsc_write(mac->adapter, REG_ING_FFILT_UM_EN, v); | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static int mac_set_mtu(struct cmac *mac, int mtu) | ||
454 | { | ||
455 | int port = mac->instance->index; | ||
456 | |||
457 | if (mtu > MAX_MTU) | ||
458 | return -EINVAL; | ||
459 | |||
460 | /* max_len includes header and FCS */ | ||
461 | vsc_write(mac->adapter, REG_MAX_LEN(port), mtu + 14 + 4); | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, | ||
466 | int fc) | ||
467 | { | ||
468 | u32 v; | ||
469 | int enable, port = mac->instance->index; | ||
470 | |||
471 | if (speed >= 0 && speed != SPEED_10 && speed != SPEED_100 && | ||
472 | speed != SPEED_1000) | ||
473 | return -1; | ||
474 | if (duplex > 0 && duplex != DUPLEX_FULL) | ||
475 | return -1; | ||
476 | |||
477 | if (speed >= 0) { | ||
478 | vsc_read(mac->adapter, REG_MODE_CFG(port), &v); | ||
479 | enable = v & 3; /* save tx/rx enables */ | ||
480 | v &= ~0xf; | ||
481 | v |= 4; /* full duplex */ | ||
482 | if (speed == SPEED_1000) | ||
483 | v |= 8; /* GigE */ | ||
484 | enable |= v; | ||
485 | vsc_write(mac->adapter, REG_MODE_CFG(port), v); | ||
486 | |||
487 | if (speed == SPEED_1000) | ||
488 | v = 0x82; | ||
489 | else if (speed == SPEED_100) | ||
490 | v = 0x84; | ||
491 | else /* SPEED_10 */ | ||
492 | v = 0x86; | ||
493 | vsc_write(mac->adapter, REG_DEV_SETUP(port), v | 1); /* reset */ | ||
494 | vsc_write(mac->adapter, REG_DEV_SETUP(port), v); | ||
495 | vsc_read(mac->adapter, REG_DBG(port), &v); | ||
496 | v &= ~0xff00; | ||
497 | if (speed == SPEED_1000) | ||
498 | v |= 0x400; | ||
499 | else if (speed == SPEED_100) | ||
500 | v |= 0x2000; | ||
501 | else /* SPEED_10 */ | ||
502 | v |= 0xff00; | ||
503 | vsc_write(mac->adapter, REG_DBG(port), v); | ||
504 | |||
505 | vsc_write(mac->adapter, REG_TX_IFG(port), | ||
506 | speed == SPEED_1000 ? 5 : 0x11); | ||
507 | if (duplex == DUPLEX_HALF) | ||
508 | enable = 0x0; /* 100 or 10 */ | ||
509 | else if (speed == SPEED_1000) | ||
510 | enable = 0xc; | ||
511 | else /* SPEED_100 or 10 */ | ||
512 | enable = 0x4; | ||
513 | enable |= 0x9 << 10; /* IFG1 */ | ||
514 | enable |= 0x6 << 6; /* IFG2 */ | ||
515 | enable |= 0x1 << 4; /* VLAN */ | ||
516 | enable |= 0x3; /* RX/TX EN */ | ||
517 | vsc_write(mac->adapter, REG_MODE_CFG(port), enable); | ||
518 | |||
519 | } | ||
520 | |||
521 | vsc_read(mac->adapter, REG_PAUSE_CFG(port), &v); | ||
522 | v &= 0xfff0ffff; | ||
523 | v |= 0x20000; /* xon/xoff */ | ||
524 | if (fc & PAUSE_RX) | ||
525 | v |= 0x40000; | ||
526 | if (fc & PAUSE_TX) | ||
527 | v |= 0x80000; | ||
528 | if (fc == (PAUSE_RX | PAUSE_TX)) | ||
529 | v |= 0x10000; | ||
530 | vsc_write(mac->adapter, REG_PAUSE_CFG(port), v); | ||
531 | return 0; | ||
532 | } | ||
533 | |||
534 | static int mac_enable(struct cmac *mac, int which) | ||
535 | { | ||
536 | u32 val; | ||
537 | int port = mac->instance->index; | ||
538 | |||
539 | /* Write the correct WM value when the port is enabled. */ | ||
540 | vsc_write(mac->adapter, REG_HIGH_LOW_WM(1,port), WM_ENABLE); | ||
541 | |||
542 | vsc_read(mac->adapter, REG_MODE_CFG(port), &val); | ||
543 | if (which & MAC_DIRECTION_RX) | ||
544 | val |= 0x2; | ||
545 | if (which & MAC_DIRECTION_TX) | ||
546 | val |= 1; | ||
547 | vsc_write(mac->adapter, REG_MODE_CFG(port), val); | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | static int mac_disable(struct cmac *mac, int which) | ||
552 | { | ||
553 | u32 val; | ||
554 | int i, port = mac->instance->index; | ||
555 | |||
556 | /* Reset the port, this also writes the correct WM value */ | ||
557 | mac_reset(mac); | ||
558 | |||
559 | vsc_read(mac->adapter, REG_MODE_CFG(port), &val); | ||
560 | if (which & MAC_DIRECTION_RX) | ||
561 | val &= ~0x2; | ||
562 | if (which & MAC_DIRECTION_TX) | ||
563 | val &= ~0x1; | ||
564 | vsc_write(mac->adapter, REG_MODE_CFG(port), val); | ||
565 | vsc_read(mac->adapter, REG_MODE_CFG(port), &val); | ||
566 | |||
567 | /* Clear stats */ | ||
568 | for (i = 0; i <= 0x3a; ++i) | ||
569 | vsc_write(mac->adapter, CRA(4, port, i), 0); | ||
570 | |||
571 | /* Clear sofware counters */ | ||
572 | memset(&mac->stats, 0, sizeof(struct cmac_statistics)); | ||
573 | |||
574 | return 0; | ||
575 | } | ||
576 | |||
577 | static void rmon_update(struct cmac *mac, unsigned int addr, u64 *stat) | ||
578 | { | ||
579 | u32 v, lo; | ||
580 | |||
581 | vsc_read(mac->adapter, addr, &v); | ||
582 | lo = *stat; | ||
583 | *stat = *stat - lo + v; | ||
584 | |||
585 | if (v == 0) | ||
586 | return; | ||
587 | |||
588 | if (v < lo) | ||
589 | *stat += (1ULL << 32); | ||
590 | } | ||
591 | |||
592 | static void port_stats_update(struct cmac *mac) | ||
593 | { | ||
594 | int port = mac->instance->index; | ||
595 | |||
596 | /* Rx stats */ | ||
597 | rmon_update(mac, REG_RX_OK_BYTES(port), &mac->stats.RxOctetsOK); | ||
598 | rmon_update(mac, REG_RX_BAD_BYTES(port), &mac->stats.RxOctetsBad); | ||
599 | rmon_update(mac, REG_RX_UNICAST(port), &mac->stats.RxUnicastFramesOK); | ||
600 | rmon_update(mac, REG_RX_MULTICAST(port), | ||
601 | &mac->stats.RxMulticastFramesOK); | ||
602 | rmon_update(mac, REG_RX_BROADCAST(port), | ||
603 | &mac->stats.RxBroadcastFramesOK); | ||
604 | rmon_update(mac, REG_CRC(port), &mac->stats.RxFCSErrors); | ||
605 | rmon_update(mac, REG_RX_ALIGNMENT(port), &mac->stats.RxAlignErrors); | ||
606 | rmon_update(mac, REG_RX_OVERSIZE(port), | ||
607 | &mac->stats.RxFrameTooLongErrors); | ||
608 | rmon_update(mac, REG_RX_PAUSE(port), &mac->stats.RxPauseFrames); | ||
609 | rmon_update(mac, REG_RX_JABBERS(port), &mac->stats.RxJabberErrors); | ||
610 | rmon_update(mac, REG_RX_FRAGMENTS(port), &mac->stats.RxRuntErrors); | ||
611 | rmon_update(mac, REG_RX_UNDERSIZE(port), &mac->stats.RxRuntErrors); | ||
612 | rmon_update(mac, REG_RX_SYMBOL_CARRIER(port), | ||
613 | &mac->stats.RxSymbolErrors); | ||
614 | rmon_update(mac, REG_RX_SIZE_1519_TO_MAX(port), | ||
615 | &mac->stats.RxJumboFramesOK); | ||
616 | |||
617 | /* Tx stats (skip collision stats as we are full-duplex only) */ | ||
618 | rmon_update(mac, REG_TX_OK_BYTES(port), &mac->stats.TxOctetsOK); | ||
619 | rmon_update(mac, REG_TX_UNICAST(port), &mac->stats.TxUnicastFramesOK); | ||
620 | rmon_update(mac, REG_TX_MULTICAST(port), | ||
621 | &mac->stats.TxMulticastFramesOK); | ||
622 | rmon_update(mac, REG_TX_BROADCAST(port), | ||
623 | &mac->stats.TxBroadcastFramesOK); | ||
624 | rmon_update(mac, REG_TX_PAUSE(port), &mac->stats.TxPauseFrames); | ||
625 | rmon_update(mac, REG_TX_UNDERRUN(port), &mac->stats.TxUnderrun); | ||
626 | rmon_update(mac, REG_TX_SIZE_1519_TO_MAX(port), | ||
627 | &mac->stats.TxJumboFramesOK); | ||
628 | } | ||
629 | |||
630 | /* | ||
631 | * This function is called periodically to accumulate the current values of the | ||
632 | * RMON counters into the port statistics. Since the counters are only 32 bits | ||
633 | * some of them can overflow in less than a minute at GigE speeds, so this | ||
634 | * function should be called every 30 seconds or so. | ||
635 | * | ||
636 | * To cut down on reading costs we update only the octet counters at each tick | ||
637 | * and do a full update at major ticks, which can be every 30 minutes or more. | ||
638 | */ | ||
639 | static const struct cmac_statistics *mac_update_statistics(struct cmac *mac, | ||
640 | int flag) | ||
641 | { | ||
642 | if (flag == MAC_STATS_UPDATE_FULL || | ||
643 | mac->instance->ticks >= MAJOR_UPDATE_TICKS) { | ||
644 | port_stats_update(mac); | ||
645 | mac->instance->ticks = 0; | ||
646 | } else { | ||
647 | int port = mac->instance->index; | ||
648 | |||
649 | rmon_update(mac, REG_RX_OK_BYTES(port), | ||
650 | &mac->stats.RxOctetsOK); | ||
651 | rmon_update(mac, REG_RX_BAD_BYTES(port), | ||
652 | &mac->stats.RxOctetsBad); | ||
653 | rmon_update(mac, REG_TX_OK_BYTES(port), | ||
654 | &mac->stats.TxOctetsOK); | ||
655 | mac->instance->ticks++; | ||
656 | } | ||
657 | return &mac->stats; | ||
658 | } | ||
659 | |||
660 | static void mac_destroy(struct cmac *mac) | ||
661 | { | ||
662 | kfree(mac); | ||
663 | } | ||
664 | |||
665 | static struct cmac_ops vsc7326_ops = { | ||
666 | .destroy = mac_destroy, | ||
667 | .reset = mac_reset, | ||
668 | .interrupt_handler = mac_intr_handler, | ||
669 | .interrupt_enable = mac_intr_enable, | ||
670 | .interrupt_disable = mac_intr_disable, | ||
671 | .interrupt_clear = mac_intr_clear, | ||
672 | .enable = mac_enable, | ||
673 | .disable = mac_disable, | ||
674 | .set_mtu = mac_set_mtu, | ||
675 | .set_rx_mode = mac_set_rx_mode, | ||
676 | .set_speed_duplex_fc = mac_set_speed_duplex_fc, | ||
677 | .statistics_update = mac_update_statistics, | ||
678 | .macaddress_get = mac_get_address, | ||
679 | .macaddress_set = mac_set_address, | ||
680 | }; | ||
681 | |||
682 | static struct cmac *vsc7326_mac_create(adapter_t *adapter, int index) | ||
683 | { | ||
684 | struct cmac *mac; | ||
685 | u32 val; | ||
686 | int i; | ||
687 | |||
688 | mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); | ||
689 | if (!mac) return NULL; | ||
690 | |||
691 | mac->ops = &vsc7326_ops; | ||
692 | mac->instance = (cmac_instance *)(mac + 1); | ||
693 | mac->adapter = adapter; | ||
694 | |||
695 | mac->instance->index = index; | ||
696 | mac->instance->ticks = 0; | ||
697 | |||
698 | i = 0; | ||
699 | do { | ||
700 | u32 vhi, vlo; | ||
701 | |||
702 | vhi = vlo = 0; | ||
703 | t1_tpi_read(adapter, (REG_LOCAL_STATUS << 2) + 4, &vlo); | ||
704 | udelay(1); | ||
705 | t1_tpi_read(adapter, REG_LOCAL_STATUS << 2, &vhi); | ||
706 | udelay(5); | ||
707 | val = (vhi << 16) | vlo; | ||
708 | } while ((++i < 10000) && (val == 0xffffffff)); | ||
709 | |||
710 | return mac; | ||
711 | } | ||
712 | |||
713 | static int vsc7326_mac_reset(adapter_t *adapter) | ||
714 | { | ||
715 | vsc7326_full_reset(adapter); | ||
716 | (void) run_bist_all(adapter); | ||
717 | run_table(adapter, vsc7326_reset, ARRAY_SIZE(vsc7326_reset)); | ||
718 | return 0; | ||
719 | } | ||
720 | |||
721 | struct gmac t1_vsc7326_ops = { | ||
722 | .stats_update_period = STATS_TICK_SECS, | ||
723 | .create = vsc7326_mac_create, | ||
724 | .reset = vsc7326_mac_reset, | ||
725 | }; | ||