1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /****************************************************************************** |
3 | * |
4 | * (C)Copyright 1998,1999 SysKonnect, |
5 | * a business unit of Schneider & Koch & Co. Datensysteme GmbH. |
6 | * |
7 | * See the file "skfddi.c" for further information. |
8 | * |
9 | * The information in this file is provided "AS IS" without warranty. |
10 | * |
11 | ******************************************************************************/ |
12 | |
13 | /* |
14 | * FBI board dependent Driver for SMT and LLC |
15 | */ |
16 | |
17 | #include "h/types.h" |
18 | #include "h/fddi.h" |
19 | #include "h/smc.h" |
20 | #include "h/supern_2.h" |
21 | #include "h/skfbiinc.h" |
22 | #include <linux/bitrev.h> |
23 | #include <linux/pci.h> |
24 | |
25 | /* |
26 | * PCM active state |
27 | */ |
28 | #define PC8_ACTIVE 8 |
29 | |
30 | #define LED_Y_ON 0x11 /* Used for ring up/down indication */ |
31 | #define LED_Y_OFF 0x10 |
32 | |
33 | |
34 | #define MS2BCLK(x) ((x)*12500L) |
35 | |
36 | /* |
37 | * valid configuration values are: |
38 | */ |
39 | |
40 | /* |
41 | * xPOS_ID:xxxx |
42 | * | \ / |
43 | * | \/ |
44 | * | --------------------- the patched POS_ID of the Adapter |
45 | * | xxxx = (Vendor ID low byte, |
46 | * | Vendor ID high byte, |
47 | * | Device ID low byte, |
48 | * | Device ID high byte) |
49 | * +------------------------------ the patched oem_id must be |
50 | * 'S' for SK or 'I' for IBM |
51 | * this is a short id for the driver. |
52 | */ |
53 | #ifndef MULT_OEM |
54 | #ifndef OEM_CONCEPT |
55 | const u_char oem_id[] = "xPOS_ID:xxxx" ; |
56 | #else /* OEM_CONCEPT */ |
57 | const u_char oem_id[] = OEM_ID ; |
58 | #endif /* OEM_CONCEPT */ |
59 | #define ID_BYTE0 8 |
60 | #define OEMID(smc,i) oem_id[ID_BYTE0 + i] |
61 | #else /* MULT_OEM */ |
62 | const struct s_oem_ids oem_ids[] = { |
63 | #include "oemids.h" |
64 | {0} |
65 | }; |
66 | #define OEMID(smc,i) smc->hw.oem_id->oi_id[i] |
67 | #endif /* MULT_OEM */ |
68 | |
69 | /* Prototypes of external functions */ |
70 | #ifdef AIX |
71 | extern int AIX_vpdReadByte() ; |
72 | #endif |
73 | |
74 | |
75 | /* Prototype of a local function. */ |
76 | static void smt_stop_watchdog(struct s_smc *smc); |
77 | |
78 | /* |
79 | * FDDI card reset |
80 | */ |
81 | static void card_start(struct s_smc *smc) |
82 | { |
83 | int i ; |
84 | #ifdef PCI |
85 | u_char rev_id ; |
86 | u_short word; |
87 | #endif |
88 | |
89 | smt_stop_watchdog(smc) ; |
90 | |
91 | #ifdef PCI |
92 | /* |
93 | * make sure no transfer activity is pending |
94 | */ |
95 | outpw(FM_A(FM_MDREG1),FM_MINIT) ; |
96 | outp(ADDR(B0_CTRL), CTRL_HPI_SET) ; |
97 | hwt_wait_time(smc,start: hwt_quick_read(smc),MS2BCLK(10)) ; |
98 | /* |
99 | * now reset everything |
100 | */ |
101 | outp(ADDR(B0_CTRL),CTRL_RST_SET) ; /* reset for all chips */ |
102 | i = (int) inp(ADDR(B0_CTRL)) ; /* do dummy read */ |
103 | SK_UNUSED(i) ; /* Make LINT happy. */ |
104 | outp(ADDR(B0_CTRL), CTRL_RST_CLR) ; |
105 | |
106 | /* |
107 | * Reset all bits in the PCI STATUS register |
108 | */ |
109 | outp(ADDR(B0_TST_CTRL), TST_CFG_WRITE_ON) ; /* enable for writes */ |
110 | word = inpw(PCI_C(PCI_STATUS)) ; |
111 | outpw(PCI_C(PCI_STATUS), word | PCI_STATUS_ERROR_BITS); |
112 | outp(ADDR(B0_TST_CTRL), TST_CFG_WRITE_OFF) ; /* disable writes */ |
113 | |
114 | /* |
115 | * Release the reset of all the State machines |
116 | * Release Master_Reset |
117 | * Release HPI_SM_Reset |
118 | */ |
119 | outp(ADDR(B0_CTRL), CTRL_MRST_CLR|CTRL_HPI_CLR) ; |
120 | |
121 | /* |
122 | * determine the adapter type |
123 | * Note: Do it here, because some drivers may call card_start() once |
124 | * at very first before any other initialization functions is |
125 | * executed. |
126 | */ |
127 | rev_id = inp(PCI_C(PCI_REVISION_ID)) ; |
128 | if ((rev_id & 0xf0) == SK_ML_ID_1 || (rev_id & 0xf0) == SK_ML_ID_2) { |
129 | smc->hw.hw_is_64bit = TRUE ; |
130 | } else { |
131 | smc->hw.hw_is_64bit = FALSE ; |
132 | } |
133 | |
134 | /* |
135 | * Watermark initialization |
136 | */ |
137 | if (!smc->hw.hw_is_64bit) { |
138 | outpd(ADDR(B4_R1_F), RX_WATERMARK) ; |
139 | outpd(ADDR(B5_XA_F), TX_WATERMARK) ; |
140 | outpd(ADDR(B5_XS_F), TX_WATERMARK) ; |
141 | } |
142 | |
143 | outp(ADDR(B0_CTRL),CTRL_RST_CLR) ; /* clear the reset chips */ |
144 | outp(ADDR(B0_LED),LED_GA_OFF|LED_MY_ON|LED_GB_OFF) ; /* ye LED on */ |
145 | |
146 | /* init the timer value for the watch dog 2,5 minutes */ |
147 | outpd(ADDR(B2_WDOG_INI),0x6FC23AC0) ; |
148 | |
149 | /* initialize the ISR mask */ |
150 | smc->hw.is_imask = ISR_MASK ; |
151 | smc->hw.hw_state = STOPPED ; |
152 | #endif |
153 | GET_PAGE(0) ; /* necessary for BOOT */ |
154 | } |
155 | |
156 | void card_stop(struct s_smc *smc) |
157 | { |
158 | smt_stop_watchdog(smc) ; |
159 | smc->hw.mac_ring_is_up = 0 ; /* ring down */ |
160 | |
161 | #ifdef PCI |
162 | /* |
163 | * make sure no transfer activity is pending |
164 | */ |
165 | outpw(FM_A(FM_MDREG1),FM_MINIT) ; |
166 | outp(ADDR(B0_CTRL), CTRL_HPI_SET) ; |
167 | hwt_wait_time(smc,start: hwt_quick_read(smc),MS2BCLK(10)) ; |
168 | /* |
169 | * now reset everything |
170 | */ |
171 | outp(ADDR(B0_CTRL),CTRL_RST_SET) ; /* reset for all chips */ |
172 | outp(ADDR(B0_CTRL),CTRL_RST_CLR) ; /* reset for all chips */ |
173 | outp(ADDR(B0_LED),LED_GA_OFF|LED_MY_OFF|LED_GB_OFF) ; /* all LEDs off */ |
174 | smc->hw.hw_state = STOPPED ; |
175 | #endif |
176 | } |
177 | /*--------------------------- ISR handling ----------------------------------*/ |
178 | |
179 | void mac1_irq(struct s_smc *smc, u_short stu, u_short stl) |
180 | { |
181 | int restart_tx = 0 ; |
182 | again: |
183 | |
184 | /* |
185 | * parity error: note encoding error is not possible in tag mode |
186 | */ |
187 | if (stl & (FM_SPCEPDS | /* parity err. syn.q.*/ |
188 | FM_SPCEPDA0 | /* parity err. a.q.0 */ |
189 | FM_SPCEPDA1)) { /* parity err. a.q.1 */ |
190 | SMT_PANIC(smc,SMT_E0134, SMT_E0134_MSG) ; |
191 | } |
192 | /* |
193 | * buffer underrun: can only occur if a tx threshold is specified |
194 | */ |
195 | if (stl & (FM_STBURS | /* tx buffer underrun syn.q.*/ |
196 | FM_STBURA0 | /* tx buffer underrun a.q.0 */ |
197 | FM_STBURA1)) { /* tx buffer underrun a.q.2 */ |
198 | SMT_PANIC(smc,SMT_E0133, SMT_E0133_MSG) ; |
199 | } |
200 | |
201 | if ( (stu & (FM_SXMTABT | /* transmit abort */ |
202 | FM_STXABRS | /* syn. tx abort */ |
203 | FM_STXABRA0)) || /* asyn. tx abort */ |
204 | (stl & (FM_SQLCKS | /* lock for syn. q. */ |
205 | FM_SQLCKA0)) ) { /* lock for asyn. q. */ |
206 | formac_tx_restart(smc) ; /* init tx */ |
207 | restart_tx = 1 ; |
208 | stu = inpw(FM_A(FM_ST1U)) ; |
209 | stl = inpw(FM_A(FM_ST1L)) ; |
210 | stu &= ~ (FM_STECFRMA0 | FM_STEFRMA0 | FM_STEFRMS) ; |
211 | if (stu || stl) |
212 | goto again ; |
213 | } |
214 | |
215 | if (stu & (FM_STEFRMA0 | /* end of asyn tx */ |
216 | FM_STEFRMS)) { /* end of sync tx */ |
217 | restart_tx = 1 ; |
218 | } |
219 | |
220 | if (restart_tx) |
221 | llc_restart_tx(smc) ; |
222 | } |
223 | |
224 | /* |
225 | * interrupt source= plc1 |
226 | * this function is called in nwfbisr.asm |
227 | */ |
228 | void plc1_irq(struct s_smc *smc) |
229 | { |
230 | u_short st = inpw(PLC(PB,PL_INTR_EVENT)) ; |
231 | |
232 | plc_irq(smc,PB,cmd: st) ; |
233 | } |
234 | |
235 | /* |
236 | * interrupt source= plc2 |
237 | * this function is called in nwfbisr.asm |
238 | */ |
239 | void plc2_irq(struct s_smc *smc) |
240 | { |
241 | u_short st = inpw(PLC(PA,PL_INTR_EVENT)) ; |
242 | |
243 | plc_irq(smc,PA,cmd: st) ; |
244 | } |
245 | |
246 | |
247 | /* |
248 | * interrupt source= timer |
249 | */ |
250 | void timer_irq(struct s_smc *smc) |
251 | { |
252 | hwt_restart(smc); |
253 | smc->hw.t_stop = smc->hw.t_start; |
254 | smt_timer_done(smc) ; |
255 | } |
256 | |
257 | /* |
258 | * return S-port (PA or PB) |
259 | */ |
260 | int pcm_get_s_port(struct s_smc *smc) |
261 | { |
262 | SK_UNUSED(smc) ; |
263 | return PS; |
264 | } |
265 | |
266 | /* |
267 | * Station Label = "FDDI-XYZ" where |
268 | * |
269 | * X = connector type |
270 | * Y = PMD type |
271 | * Z = port type |
272 | */ |
273 | #define STATION_LABEL_CONNECTOR_OFFSET 5 |
274 | #define STATION_LABEL_PMD_OFFSET 6 |
275 | #define STATION_LABEL_PORT_OFFSET 7 |
276 | |
277 | void read_address(struct s_smc *smc, u_char *mac_addr) |
278 | { |
279 | char ConnectorType ; |
280 | char PmdType ; |
281 | int i ; |
282 | |
283 | #ifdef PCI |
284 | for (i = 0; i < 6; i++) { /* read mac address from board */ |
285 | smc->hw.fddi_phys_addr.a[i] = |
286 | bitrev8(inp(ADDR(B2_MAC_0+i))); |
287 | } |
288 | #endif |
289 | |
290 | ConnectorType = inp(ADDR(B2_CONN_TYP)) ; |
291 | PmdType = inp(ADDR(B2_PMD_TYP)) ; |
292 | |
293 | smc->y[PA].pmd_type[PMD_SK_CONN] = |
294 | smc->y[PB].pmd_type[PMD_SK_CONN] = ConnectorType ; |
295 | smc->y[PA].pmd_type[PMD_SK_PMD ] = |
296 | smc->y[PB].pmd_type[PMD_SK_PMD ] = PmdType ; |
297 | |
298 | if (mac_addr) { |
299 | for (i = 0; i < 6 ;i++) { |
300 | smc->hw.fddi_canon_addr.a[i] = mac_addr[i] ; |
301 | smc->hw.fddi_home_addr.a[i] = bitrev8(mac_addr[i]); |
302 | } |
303 | return ; |
304 | } |
305 | smc->hw.fddi_home_addr = smc->hw.fddi_phys_addr ; |
306 | |
307 | for (i = 0; i < 6 ;i++) { |
308 | smc->hw.fddi_canon_addr.a[i] = |
309 | bitrev8(smc->hw.fddi_phys_addr.a[i]); |
310 | } |
311 | } |
312 | |
313 | /* |
314 | * FDDI card soft reset |
315 | */ |
316 | void init_board(struct s_smc *smc, u_char *mac_addr) |
317 | { |
318 | card_start(smc) ; |
319 | read_address(smc,mac_addr) ; |
320 | |
321 | if (!(inp(ADDR(B0_DAS)) & DAS_AVAIL)) |
322 | smc->s.sas = SMT_SAS ; /* Single att. station */ |
323 | else |
324 | smc->s.sas = SMT_DAS ; /* Dual att. station */ |
325 | |
326 | if (!(inp(ADDR(B0_DAS)) & DAS_BYP_ST)) |
327 | smc->mib.fddiSMTBypassPresent = 0 ; |
328 | /* without opt. bypass */ |
329 | else |
330 | smc->mib.fddiSMTBypassPresent = 1 ; |
331 | /* with opt. bypass */ |
332 | } |
333 | |
334 | /* |
335 | * insert or deinsert optical bypass (called by ECM) |
336 | */ |
337 | void sm_pm_bypass_req(struct s_smc *smc, int mode) |
338 | { |
339 | DB_ECMN(1, "ECM : sm_pm_bypass_req(%s)" , |
340 | mode == BP_INSERT ? "BP_INSERT" : "BP_DEINSERT" ); |
341 | |
342 | if (smc->s.sas != SMT_DAS) |
343 | return ; |
344 | |
345 | #ifdef PCI |
346 | switch(mode) { |
347 | case BP_INSERT : |
348 | outp(ADDR(B0_DAS),DAS_BYP_INS) ; /* insert station */ |
349 | break ; |
350 | case BP_DEINSERT : |
351 | outp(ADDR(B0_DAS),DAS_BYP_RMV) ; /* bypass station */ |
352 | break ; |
353 | } |
354 | #endif |
355 | } |
356 | |
357 | /* |
358 | * check if bypass connected |
359 | */ |
360 | int sm_pm_bypass_present(struct s_smc *smc) |
361 | { |
362 | return (inp(ADDR(B0_DAS)) & DAS_BYP_ST) ? TRUE : FALSE; |
363 | } |
364 | |
365 | void plc_clear_irq(struct s_smc *smc, int p) |
366 | { |
367 | SK_UNUSED(p) ; |
368 | |
369 | SK_UNUSED(smc) ; |
370 | } |
371 | |
372 | |
373 | /* |
374 | * led_indication called by rmt_indication() and |
375 | * pcm_state_change() |
376 | * |
377 | * Input: |
378 | * smc: SMT context |
379 | * led_event: |
380 | * 0 Only switch green LEDs according to their respective PCM state |
381 | * LED_Y_OFF just switch yellow LED off |
382 | * LED_Y_ON just switch yello LED on |
383 | */ |
384 | static void led_indication(struct s_smc *smc, int led_event) |
385 | { |
386 | /* use smc->hw.mac_ring_is_up == TRUE |
387 | * as indication for Ring Operational |
388 | */ |
389 | u_short led_state ; |
390 | struct s_phy *phy ; |
391 | struct fddi_mib_p *mib_a ; |
392 | struct fddi_mib_p *mib_b ; |
393 | |
394 | phy = &smc->y[PA] ; |
395 | mib_a = phy->mib ; |
396 | phy = &smc->y[PB] ; |
397 | mib_b = phy->mib ; |
398 | |
399 | #ifdef PCI |
400 | led_state = 0 ; |
401 | |
402 | /* Ring up = yellow led OFF*/ |
403 | if (led_event == LED_Y_ON) { |
404 | led_state |= LED_MY_ON ; |
405 | } |
406 | else if (led_event == LED_Y_OFF) { |
407 | led_state |= LED_MY_OFF ; |
408 | } |
409 | else { /* PCM state changed */ |
410 | /* Link at Port A/S = green led A ON */ |
411 | if (mib_a->fddiPORTPCMState == PC8_ACTIVE) { |
412 | led_state |= LED_GA_ON ; |
413 | } |
414 | else { |
415 | led_state |= LED_GA_OFF ; |
416 | } |
417 | |
418 | /* Link at Port B = green led B ON */ |
419 | if (mib_b->fddiPORTPCMState == PC8_ACTIVE) { |
420 | led_state |= LED_GB_ON ; |
421 | } |
422 | else { |
423 | led_state |= LED_GB_OFF ; |
424 | } |
425 | } |
426 | |
427 | outp(ADDR(B0_LED), led_state) ; |
428 | #endif /* PCI */ |
429 | |
430 | } |
431 | |
432 | |
433 | void pcm_state_change(struct s_smc *smc, int plc, int p_state) |
434 | { |
435 | /* |
436 | * the current implementation of pcm_state_change() in the driver |
437 | * parts must be renamed to drv_pcm_state_change() which will be called |
438 | * now after led_indication. |
439 | */ |
440 | DRV_PCM_STATE_CHANGE(smc,plc,p_state) ; |
441 | |
442 | led_indication(smc,led_event: 0) ; |
443 | } |
444 | |
445 | |
446 | void rmt_indication(struct s_smc *smc, int i) |
447 | { |
448 | /* Call a driver special function if defined */ |
449 | DRV_RMT_INDICATION(smc,i) ; |
450 | |
451 | led_indication(smc, led_event: i ? LED_Y_OFF : LED_Y_ON) ; |
452 | } |
453 | |
454 | |
455 | /* |
456 | * llc_recover_tx called by init_tx (fplus.c) |
457 | */ |
458 | void llc_recover_tx(struct s_smc *smc) |
459 | { |
460 | #ifdef LOAD_GEN |
461 | extern int load_gen_flag ; |
462 | |
463 | load_gen_flag = 0 ; |
464 | #endif |
465 | #ifndef SYNC |
466 | smc->hw.n_a_send= 0 ; |
467 | #else |
468 | SK_UNUSED(smc) ; |
469 | #endif |
470 | } |
471 | |
472 | #ifdef MULT_OEM |
473 | static int is_equal_num(char comp1[], char comp2[], int num) |
474 | { |
475 | int i ; |
476 | |
477 | for (i = 0 ; i < num ; i++) { |
478 | if (comp1[i] != comp2[i]) |
479 | return 0; |
480 | } |
481 | return 1; |
482 | } /* is_equal_num */ |
483 | |
484 | |
485 | /* |
486 | * set the OEM ID defaults, and test the contents of the OEM data base |
487 | * The default OEM is the first ACTIVE entry in the OEM data base |
488 | * |
489 | * returns: 0 success |
490 | * 1 error in data base |
491 | * 2 data base empty |
492 | * 3 no active entry |
493 | */ |
494 | int set_oi_id_def(struct s_smc *smc) |
495 | { |
496 | int sel_id ; |
497 | int i ; |
498 | int act_entries ; |
499 | |
500 | i = 0 ; |
501 | sel_id = -1 ; |
502 | act_entries = FALSE ; |
503 | smc->hw.oem_id = 0 ; |
504 | smc->hw.oem_min_status = OI_STAT_ACTIVE ; |
505 | |
506 | /* check OEM data base */ |
507 | while (oem_ids[i].oi_status) { |
508 | switch (oem_ids[i].oi_status) { |
509 | case OI_STAT_ACTIVE: |
510 | act_entries = TRUE ; /* we have active IDs */ |
511 | if (sel_id == -1) |
512 | sel_id = i ; /* save the first active ID */ |
513 | case OI_STAT_VALID: |
514 | case OI_STAT_PRESENT: |
515 | i++ ; |
516 | break ; /* entry ok */ |
517 | default: |
518 | return 1; /* invalid oi_status */ |
519 | } |
520 | } |
521 | |
522 | if (i == 0) |
523 | return 2; |
524 | if (!act_entries) |
525 | return 3; |
526 | |
527 | /* ok, we have a valid OEM data base with an active entry */ |
528 | smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[sel_id] ; |
529 | return 0; |
530 | } |
531 | #endif /* MULT_OEM */ |
532 | |
533 | void driver_get_bia(struct s_smc *smc, struct fddi_addr *bia_addr) |
534 | { |
535 | int i ; |
536 | |
537 | for (i = 0 ; i < 6 ; i++) |
538 | bia_addr->a[i] = bitrev8(smc->hw.fddi_phys_addr.a[i]); |
539 | } |
540 | |
541 | void smt_start_watchdog(struct s_smc *smc) |
542 | { |
543 | SK_UNUSED(smc) ; /* Make LINT happy. */ |
544 | |
545 | #ifndef DEBUG |
546 | |
547 | #ifdef PCI |
548 | if (smc->hw.wdog_used) { |
549 | outpw(ADDR(B2_WDOG_CRTL),TIM_START) ; /* Start timer. */ |
550 | } |
551 | #endif |
552 | |
553 | #endif /* DEBUG */ |
554 | } |
555 | |
556 | static void smt_stop_watchdog(struct s_smc *smc) |
557 | { |
558 | SK_UNUSED(smc) ; /* Make LINT happy. */ |
559 | #ifndef DEBUG |
560 | |
561 | #ifdef PCI |
562 | if (smc->hw.wdog_used) { |
563 | outpw(ADDR(B2_WDOG_CRTL),TIM_STOP) ; /* Stop timer. */ |
564 | } |
565 | #endif |
566 | |
567 | #endif /* DEBUG */ |
568 | } |
569 | |
570 | #ifdef PCI |
571 | |
572 | void mac_do_pci_fix(struct s_smc *smc) |
573 | { |
574 | SK_UNUSED(smc) ; |
575 | } |
576 | #endif /* PCI */ |
577 | |
578 | |