Broadcom SDK and wireless driver: another attempt to update to ver. 5.10.147.0
[tomato.git] / release / src-rt / shared / nicpci.c
blob5d50b7a9f93e80141b84ed8164e49119f50538b2
1 /*
2 * Code to operate on PCI/E core, in NIC mode
3 * Implements pci_api.h
4 * Copyright (C) 2009, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
12 * $Id: nicpci.c,v 1.15.2.12 2009/04/23 05:17:12 Exp $
15 #include <typedefs.h>
16 #include <bcmdefs.h>
17 #include <osl.h>
18 #include <bcmutils.h>
19 #include <siutils.h>
20 #include <hndsoc.h>
21 #include <bcmdevs.h>
22 #include <sbchipc.h>
23 #include <pci_core.h>
24 #include <pcie_core.h>
25 #include <nicpci.h>
26 #include <pcicfg.h>
28 typedef struct {
29 union {
30 sbpcieregs_t *pcieregs;
31 sbpciregs_t *pciregs;
32 } regs; /* Memory mapped register to the core */
34 si_t *sih; /* System interconnect handle */
35 osl_t *osh; /* OSL handle */
36 uint8 pciecap_lcreg_offset; /* PCIE capability LCreg offset in the config space */
37 bool pcie_pr42767;
38 uint8 pcie_polarity;
39 uint8 pcie_war_aspm_ovr; /* Override ASPM/Clkreq settings */
41 uint8 pmecap_offset; /* PM Capability offset in the config space */
42 bool pmecap; /* Capable of generating PME */
43 } pcicore_info_t;
45 /* debug/trace */
46 #define PCI_ERROR(args)
48 /* routines to access mdio slave device registers */
49 static bool pcie_mdiosetblock(pcicore_info_t *pi, uint blk);
50 static int pcie_mdioop(pcicore_info_t *pi, uint physmedia, uint regaddr, bool write, uint *val);
51 static int pcie_mdiowrite(pcicore_info_t *pi, uint physmedia, uint readdr, uint val);
52 static int pcie_mdioread(pcicore_info_t *pi, uint physmedia, uint readdr, uint *ret_val);
54 static void pcie_extendL1timer(pcicore_info_t *pi, bool extend);
55 static void pcie_clkreq_upd(pcicore_info_t *pi, uint state);
57 static void pcie_war_aspm_clkreq(pcicore_info_t *pi);
58 static void pcie_war_serdes(pcicore_info_t *pi);
59 static void pcie_war_noplldown(pcicore_info_t *pi);
60 static void pcie_war_polarity(pcicore_info_t *pi);
61 static void pcie_war_pci_setup(pcicore_info_t *pi);
63 static bool pcicore_pmecap(pcicore_info_t *pi);
64 static void pcicore_fixlatencytimer(pcicore_info_t* pch, uint8 timer_val);
66 #define PCIE(sih) ((BUSTYPE((sih)->bustype) == PCI_BUS) && ((sih)->buscoretype == PCIE_CORE_ID))
67 #define PCIE_ASPM(sih) ((PCIE(sih)) && (((sih)->buscorerev >= 3) && ((sih)->buscorerev <= 5)))
69 #define DWORD_ALIGN(x) (x & ~(0x03))
70 #define BYTE_POS(x) (x & 0x3)
71 #define WORD_POS(x) (x & 0x1)
73 #define BYTE_SHIFT(x) (8 * BYTE_POS(x))
74 #define WORD_SHIFT(x) (16 * WORD_POS(x))
76 #define BYTE_VAL(a, x) ((a >> BYTE_SHIFT(x)) & 0xFF)
77 #define WORD_VAL(a, x) ((a >> WORD_SHIFT(x)) & 0xFFFF)
79 #define read_pci_cfg_byte(a) \
80 (BYTE_VAL(OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4), a) & 0xff)
82 #define read_pci_cfg_word(a) \
83 (WORD_VAL(OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4), a) & 0xffff)
85 #define write_pci_cfg_byte(a, val) do { \
86 uint32 tmpval = (OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4) & ~0xFF << BYTE_POS(a)) | \
87 val << BYTE_POS(a); \
88 OSL_PCI_WRITE_CONFIG(osh, DWORD_ALIGN(a), 4, tmpval); \
89 } while (0)
91 #define write_pci_cfg_word(a, val) do { \
92 uint32 tmpval = (OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4) & ~0xFFFF << WORD_POS(a)) | \
93 val << WORD_POS(a); \
94 OSL_PCI_WRITE_CONFIG(osh, DWORD_ALIGN(a), 4, tmpval); \
95 } while (0)
97 /* delay needed between the mdio control/ mdiodata register data access */
98 #define PR28829_DELAY() OSL_DELAY(10)
100 /* Initialize the PCI core. It's caller's responsibility to make sure that this is done
101 * only once
103 void *
104 pcicore_init(si_t *sih, osl_t *osh, void *regs)
106 pcicore_info_t *pi;
108 ASSERT(sih->bustype == PCI_BUS);
110 /* alloc pcicore_info_t */
111 if ((pi = MALLOC(osh, sizeof(pcicore_info_t))) == NULL) {
112 PCI_ERROR(("pci_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh)));
113 return (NULL);
116 bzero(pi, sizeof(pcicore_info_t));
118 pi->sih = sih;
119 pi->osh = osh;
121 if (sih->buscoretype == PCIE_CORE_ID) {
122 uint8 cap_ptr;
123 pi->regs.pcieregs = (sbpcieregs_t*)regs;
124 cap_ptr = pcicore_find_pci_capability(pi->osh, PCI_CAP_PCIECAP_ID, NULL, NULL);
125 ASSERT(cap_ptr);
126 pi->pciecap_lcreg_offset = cap_ptr + PCIE_CAP_LINKCTRL_OFFSET;
127 } else
128 pi->regs.pciregs = (sbpciregs_t*)regs;
130 return pi;
133 void
134 pcicore_deinit(void *pch)
136 pcicore_info_t *pi = (pcicore_info_t *)pch;
138 if (pi == NULL)
139 return;
140 MFREE(pi->osh, pi, sizeof(pcicore_info_t));
143 /* return cap_offset if requested capability exists in the PCI config space */
144 /* Note that it's caller's responsibility to make sure it's a pci bus */
145 uint8
146 pcicore_find_pci_capability(osl_t *osh, uint8 req_cap_id, uchar *buf, uint32 *buflen)
148 uint8 cap_id;
149 uint8 cap_ptr = 0;
150 uint32 bufsize;
151 uint8 byte_val;
153 /* check for Header type 0 */
154 byte_val = read_pci_cfg_byte(PCI_CFG_HDR);
155 if ((byte_val & 0x7f) != PCI_HEADER_NORMAL)
156 goto end;
158 /* check if the capability pointer field exists */
159 byte_val = read_pci_cfg_byte(PCI_CFG_STAT);
160 if (!(byte_val & PCI_CAPPTR_PRESENT))
161 goto end;
163 cap_ptr = read_pci_cfg_byte(PCI_CFG_CAPPTR);
164 /* check if the capability pointer is 0x00 */
165 if (cap_ptr == 0x00)
166 goto end;
168 /* loop thr'u the capability list and see if the pcie capabilty exists */
170 cap_id = read_pci_cfg_byte(cap_ptr);
172 while (cap_id != req_cap_id) {
173 cap_ptr = read_pci_cfg_byte((cap_ptr+1));
174 if (cap_ptr == 0x00) break;
175 cap_id = read_pci_cfg_byte(cap_ptr);
177 if (cap_id != req_cap_id) {
178 goto end;
180 /* found the caller requested capability */
181 if ((buf != NULL) && (buflen != NULL)) {
182 uint8 cap_data;
184 bufsize = *buflen;
185 if (!bufsize) goto end;
186 *buflen = 0;
187 /* copy the cpability data excluding cap ID and next ptr */
188 cap_data = cap_ptr + 2;
189 if ((bufsize + cap_data) > SZPCR)
190 bufsize = SZPCR - cap_data;
191 *buflen = bufsize;
192 while (bufsize--) {
193 *buf = read_pci_cfg_byte(cap_data);
194 cap_data++;
195 buf++;
198 end:
199 return cap_ptr;
202 /* ***** Register Access API */
203 uint
204 pcie_readreg(osl_t *osh, sbpcieregs_t *pcieregs, uint addrtype, uint offset)
206 uint retval = 0xFFFFFFFF;
208 ASSERT(pcieregs != NULL);
210 switch (addrtype) {
211 case PCIE_CONFIGREGS:
212 W_REG(osh, (&pcieregs->configaddr), offset);
213 (void)R_REG(osh, (&pcieregs->configaddr));
214 retval = R_REG(osh, &(pcieregs->configdata));
215 break;
216 case PCIE_PCIEREGS:
217 W_REG(osh, &(pcieregs->pcieindaddr), offset);
218 (void)R_REG(osh, (&pcieregs->pcieindaddr));
219 retval = R_REG(osh, &(pcieregs->pcieinddata));
220 break;
221 default:
222 ASSERT(0);
223 break;
226 return retval;
229 uint
230 pcie_writereg(osl_t *osh, sbpcieregs_t *pcieregs, uint addrtype, uint offset, uint val)
232 ASSERT(pcieregs != NULL);
234 switch (addrtype) {
235 case PCIE_CONFIGREGS:
236 W_REG(osh, (&pcieregs->configaddr), offset);
237 W_REG(osh, (&pcieregs->configdata), val);
238 break;
239 case PCIE_PCIEREGS:
240 W_REG(osh, (&pcieregs->pcieindaddr), offset);
241 W_REG(osh, (&pcieregs->pcieinddata), val);
242 break;
243 default:
244 ASSERT(0);
245 break;
247 return 0;
250 static bool
251 pcie_mdiosetblock(pcicore_info_t *pi, uint blk)
253 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
254 uint mdiodata, i = 0;
255 uint pcie_serdes_spinwait = 200;
257 mdiodata = MDIODATA_START | MDIODATA_WRITE | (MDIODATA_DEV_ADDR << MDIODATA_DEVADDR_SHF) | \
258 (MDIODATA_BLK_ADDR << MDIODATA_REGADDR_SHF) | MDIODATA_TA | (blk << 4);
259 W_REG(pi->osh, &pcieregs->mdiodata, mdiodata);
261 PR28829_DELAY();
262 /* retry till the transaction is complete */
263 while (i < pcie_serdes_spinwait) {
264 if (R_REG(pi->osh, &(pcieregs->mdiocontrol)) & MDIOCTL_ACCESS_DONE) {
265 break;
267 OSL_DELAY(1000);
268 i++;
271 if (i >= pcie_serdes_spinwait) {
272 PCI_ERROR(("pcie_mdiosetblock: timed out\n"));
273 return FALSE;
276 return TRUE;
279 static int
280 pcie_mdioop(pcicore_info_t *pi, uint physmedia, uint regaddr, bool write, uint *val)
282 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
283 uint mdiodata;
284 uint i = 0;
285 uint pcie_serdes_spinwait = 10;
287 /* enable mdio access to SERDES */
288 W_REG(pi->osh, (&pcieregs->mdiocontrol), MDIOCTL_PREAM_EN | MDIOCTL_DIVISOR_VAL);
290 if (pi->sih->buscorerev >= 10) {
291 /* new serdes is slower in rw, using two layers of reg address mapping */
292 if (!pcie_mdiosetblock(pi, physmedia))
293 return 1;
294 mdiodata = (MDIODATA_DEV_ADDR << MDIODATA_DEVADDR_SHF) | \
295 (regaddr << MDIODATA_REGADDR_SHF);
296 pcie_serdes_spinwait *= 20;
297 } else {
298 mdiodata = (physmedia << MDIODATA_DEVADDR_SHF_OLD) | \
299 (regaddr << MDIODATA_REGADDR_SHF_OLD);
302 if (!write)
303 mdiodata |= (MDIODATA_START | MDIODATA_READ | MDIODATA_TA);
304 else
305 mdiodata |= (MDIODATA_START | MDIODATA_WRITE | MDIODATA_TA | *val);
307 W_REG(pi->osh, &pcieregs->mdiodata, mdiodata);
309 PR28829_DELAY();
311 /* retry till the transaction is complete */
312 while (i < pcie_serdes_spinwait) {
313 if (R_REG(pi->osh, &(pcieregs->mdiocontrol)) & MDIOCTL_ACCESS_DONE) {
314 if (!write) {
315 PR28829_DELAY();
316 *val = (R_REG(pi->osh, &(pcieregs->mdiodata)) & MDIODATA_MASK);
318 /* Disable mdio access to SERDES */
319 W_REG(pi->osh, (&pcieregs->mdiocontrol), 0);
320 return 0;
322 OSL_DELAY(1000);
323 i++;
326 PCI_ERROR(("pcie_mdioop: timed out op: %d\n", write));
327 /* Disable mdio access to SERDES */
328 W_REG(pi->osh, (&pcieregs->mdiocontrol), 0);
329 return 1;
332 /* use the mdio interface to read from mdio slaves */
333 static int
334 pcie_mdioread(pcicore_info_t *pi, uint physmedia, uint regaddr, uint *regval)
336 return pcie_mdioop(pi, physmedia, regaddr, FALSE, regval);
339 /* use the mdio interface to write to mdio slaves */
340 static int
341 pcie_mdiowrite(pcicore_info_t *pi, uint physmedia, uint regaddr, uint val)
343 return pcie_mdioop(pi, physmedia, regaddr, TRUE, &val);
346 /* ***** Support functions ***** */
347 uint8
348 pcie_clkreq(void *pch, uint32 mask, uint32 val)
350 pcicore_info_t *pi = (pcicore_info_t *)pch;
351 uint32 reg_val;
352 uint8 offset;
354 offset = pi->pciecap_lcreg_offset;
355 if (!offset)
356 return 0;
358 reg_val = OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32));
359 /* set operation */
360 if (mask) {
361 if (val)
362 reg_val |= PCIE_CLKREQ_ENAB;
363 else
364 reg_val &= ~PCIE_CLKREQ_ENAB;
365 OSL_PCI_WRITE_CONFIG(pi->osh, offset, sizeof(uint32), reg_val);
366 reg_val = OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32));
368 if (reg_val & PCIE_CLKREQ_ENAB)
369 return 1;
370 else
371 return 0;
374 static void
375 pcie_extendL1timer(pcicore_info_t *pi, bool extend)
377 uint32 w;
378 si_t *sih = pi->sih;
379 osl_t *osh = pi->osh;
380 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
382 if (!PCIE(sih) ||
383 !(sih->buscorerev == 7 || sih->buscorerev == 8))
384 return;
386 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
387 if (extend)
388 w |= PCIE_ASPMTIMER_EXTEND;
389 else
390 w &= ~PCIE_ASPMTIMER_EXTEND;
391 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w);
392 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
395 /* centralized clkreq control policy */
396 static void
397 pcie_clkreq_upd(pcicore_info_t *pi, uint state)
399 si_t *sih = pi->sih;
400 ASSERT(PCIE(sih));
402 switch (state) {
403 case SI_DOATTACH:
404 if (PCIE_ASPM(sih))
405 pcie_clkreq((void *)pi, 1, 0);
406 break;
407 case SI_PCIDOWN:
408 if (sih->buscorerev == 6) { /* turn on serdes PLL down */
409 si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_addr),
410 ~0, 0);
411 si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_data),
412 ~0x40, 0);
413 } else if (pi->pcie_pr42767) {
414 pcie_clkreq((void *)pi, 1, 1);
416 break;
417 case SI_PCIUP:
418 if (sih->buscorerev == 6) { /* turn off serdes PLL down */
419 si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_addr),
420 ~0, 0);
421 si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol_data),
422 ~0x40, 0x40);
423 } else if (PCIE_ASPM(sih)) { /* disable clkreq */
424 pcie_clkreq((void *)pi, 1, 0);
426 break;
427 default:
428 ASSERT(0);
429 break;
433 /* ***** PCI core WARs ***** */
434 /* Done only once at attach time */
435 static void
436 pcie_war_polarity(pcicore_info_t *pi)
438 uint32 w;
440 if (pi->pcie_polarity != 0)
441 return;
443 w = pcie_readreg(pi->osh, pi->regs.pcieregs, PCIE_PCIEREGS, PCIE_PLP_STATUSREG);
445 /* Detect the current polarity at attach and force that polarity and
446 * disable changing the polarity
448 if ((w & PCIE_PLP_POLARITYINV_STAT) == 0)
449 pi->pcie_polarity = (SERDES_RX_CTRL_FORCE);
450 else
451 pi->pcie_polarity = (SERDES_RX_CTRL_FORCE | SERDES_RX_CTRL_POLARITY);
454 /* enable ASPM and CLKREQ if srom doesn't have it */
455 /* Needs to happen when update to shadow SROM is needed
456 * : Coming out of 'standby'/'hibernate'
457 * : If pcie_war_aspm_ovr state changed
459 static void
460 pcie_war_aspm_clkreq(pcicore_info_t *pi)
462 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
463 si_t *sih = pi->sih;
464 uint16 val16, *reg16;
465 uint32 w;
467 if (!PCIE_ASPM(sih))
468 return;
470 /* bypass this on QT or VSIM */
471 if (!ISSIM_ENAB(sih)) {
473 reg16 = &pcieregs->sprom[SRSH_ASPM_OFFSET];
474 val16 = R_REG(pi->osh, reg16);
476 val16 &= ~SRSH_ASPM_ENB;
477 if (pi->pcie_war_aspm_ovr == PCIE_ASPM_ENAB)
478 val16 |= SRSH_ASPM_ENB;
479 else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L1_ENAB)
480 val16 |= SRSH_ASPM_L1_ENB;
481 else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L0s_ENAB)
482 val16 |= SRSH_ASPM_L0s_ENB;
484 W_REG(pi->osh, reg16, val16);
486 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32));
487 w &= ~PCIE_ASPM_ENAB;
488 w |= pi->pcie_war_aspm_ovr;
489 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32), w);
492 reg16 = &pcieregs->sprom[SRSH_CLKREQ_OFFSET_REV5];
493 val16 = R_REG(pi->osh, reg16);
495 if (pi->pcie_war_aspm_ovr != PCIE_ASPM_DISAB) {
496 val16 |= SRSH_CLKREQ_ENB;
497 pi->pcie_pr42767 = TRUE;
498 } else
499 val16 &= ~SRSH_CLKREQ_ENB;
501 W_REG(pi->osh, reg16, val16);
504 /* Apply the polarity determined at the start */
505 /* Needs to happen when coming out of 'standby'/'hibernate' */
506 static void
507 pcie_war_serdes(pcicore_info_t *pi)
509 uint32 w = 0;
511 if (pi->pcie_polarity != 0)
512 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CTRL, pi->pcie_polarity);
514 pcie_mdioread(pi, MDIODATA_DEV_PLL, SERDES_PLL_CTRL, &w);
515 if (w & PLL_CTRL_FREQDET_EN) {
516 w &= ~PLL_CTRL_FREQDET_EN;
517 pcie_mdiowrite(pi, MDIODATA_DEV_PLL, SERDES_PLL_CTRL, w);
521 /* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
522 /* Needs to happen when coming out of 'standby'/'hibernate' */
523 static void
524 BCMINITFN(pcie_misc_config_fixup)(pcicore_info_t *pi)
526 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
527 uint16 val16, *reg16;
529 reg16 = &pcieregs->sprom[SRSH_PCIE_MISC_CONFIG];
530 val16 = R_REG(pi->osh, reg16);
532 if ((val16 & SRSH_L23READY_EXIT_NOPERST) == 0) {
533 val16 |= SRSH_L23READY_EXIT_NOPERST;
534 W_REG(pi->osh, reg16, val16);
538 /* quick hack for testing */
539 /* Needs to happen when coming out of 'standby'/'hibernate' */
540 static void
541 pcie_war_noplldown(pcicore_info_t *pi)
543 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
544 uint16 *reg16;
546 ASSERT(pi->sih->buscorerev == 7);
548 /* turn off serdes PLL down */
549 si_corereg(pi->sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol),
550 CHIPCTRL_4321_PLL_DOWN, CHIPCTRL_4321_PLL_DOWN);
552 /* clear srom shadow backdoor */
553 reg16 = &pcieregs->sprom[SRSH_BD_OFFSET];
554 W_REG(pi->osh, reg16, 0);
557 /* Needs to happen when coming out of 'standby'/'hibernate' */
558 static void
559 pcie_war_pci_setup(pcicore_info_t *pi)
561 si_t *sih = pi->sih;
562 osl_t *osh = pi->osh;
563 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
564 uint32 w;
566 if ((sih->buscorerev == 0) || (sih->buscorerev == 1)) {
567 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_TLP_WORKAROUNDSREG);
568 w |= 0x8;
569 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_TLP_WORKAROUNDSREG, w);
572 if (sih->buscorerev == 1) {
573 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG);
574 w |= (0x40);
575 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG, w);
578 if (sih->buscorerev == 0) {
579 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_TIMER1, 0x8128);
580 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDR, 0x0100);
581 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDRBW, 0x1466);
582 } else if (PCIE_ASPM(sih)) {
583 /* Change the L1 threshold for better performance */
584 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
585 w &= ~(PCIE_L1THRESHOLDTIME_MASK);
586 w |= (PCIE_L1THRESHOLD_WARVAL << PCIE_L1THRESHOLDTIME_SHIFT);
587 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w);
589 pcie_war_serdes(pi);
591 pcie_war_aspm_clkreq(pi);
592 } else if (pi->sih->buscorerev == 7)
593 pcie_war_noplldown(pi);
595 /* Note that the fix is actually in the SROM, that's why this is open-ended */
596 if (pi->sih->buscorerev >= 6)
597 pcie_misc_config_fixup(pi);
600 void
601 pcie_war_ovr_aspm_update(void *pch, uint8 aspm)
603 pcicore_info_t *pi = (pcicore_info_t *)pch;
605 if (!PCIE_ASPM(pi->sih))
606 return;
608 /* Validate */
609 if (aspm > PCIE_ASPM_ENAB)
610 return;
612 pi->pcie_war_aspm_ovr = aspm;
614 /* Update the current state */
615 pcie_war_aspm_clkreq(pi);
618 /* ***** Functions called during driver state changes ***** */
619 void
620 BCMATTACHFN(pcicore_attach)(void *pch, char *pvars, int state)
622 pcicore_info_t *pi = (pcicore_info_t *)pch;
623 si_t *sih = pi->sih;
625 /* Determine if this board needs override */
626 if (PCIE_ASPM(sih)) {
627 if (((sih->boardvendor == VENDOR_APPLE) &&
628 ((uint8)getintvar(pvars, "sromrev") == 4) &&
629 ((uint8)getintvar(pvars, "boardrev") <= 0x71)) ||
630 ((uint32)getintvar(pvars, "boardflags2") & BFL2_PCIEWAR_OVR)) {
631 pi->pcie_war_aspm_ovr = PCIE_ASPM_DISAB;
632 } else {
633 pi->pcie_war_aspm_ovr = PCIE_ASPM_ENAB;
637 /* These need to happen in this order only */
638 pcie_war_polarity(pi);
640 pcie_war_serdes(pi);
642 pcie_war_aspm_clkreq(pi);
644 pcie_clkreq_upd(pi, state);
646 /* Default setting for increasing the TX drive strength */
647 if ((sih->boardvendor == VENDOR_APPLE) &&
648 (sih->boardtype == 0x8d))
649 pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x7f);
653 void
654 pcicore_hwup(void *pch)
656 pcicore_info_t *pi = (pcicore_info_t *)pch;
658 if (!pi || !PCIE(pi->sih))
659 return;
661 if (pi->sih->boardtype == CB2_4321_BOARD || pi->sih->boardtype == CB2_4321_AG_BOARD)
662 pcicore_fixlatencytimer(pch, 0x20);
664 pcie_war_pci_setup(pi);
666 /* Default setting for increasing the TX drive strength */
667 if ((pi->sih->boardvendor == VENDOR_APPLE) &&
668 (pi->sih->boardtype == 0x8d))
669 pcicore_pcieserdesreg(pch, MDIO_DEV_TXCTRL0, 0x18, 0xff, 0x7f);
672 void
673 pcicore_up(void *pch, int state)
675 pcicore_info_t *pi = (pcicore_info_t *)pch;
677 if (!pi || !PCIE(pi->sih))
678 return;
680 /* Restore L1 timer for better performance */
681 pcie_extendL1timer(pi, TRUE);
683 pcie_clkreq_upd(pi, state);
686 /* When the device is going to enter D3 state (or the system is going to enter S3/S4 states */
687 void
688 pcicore_sleep(void *pch)
690 pcicore_info_t *pi = (pcicore_info_t *)pch;
691 uint32 w;
693 if (!pi || !PCIE_ASPM(pi->sih))
694 return;
696 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32));
697 w &= ~PCIE_CAP_LCREG_ASPML1;
698 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32), w);
700 pi->pcie_pr42767 = FALSE;
703 void
704 pcicore_down(void *pch, int state)
706 pcicore_info_t *pi = (pcicore_info_t *)pch;
708 if (!pi || !PCIE(pi->sih))
709 return;
711 pcie_clkreq_upd(pi, state);
713 /* Reduce L1 timer for better power savings */
714 pcie_extendL1timer(pi, FALSE);
717 /* ***** Wake-on-wireless-LAN (WOWL) support functions ***** */
718 /* Just uses PCI config accesses to find out, when needed before sb_attach is done */
719 bool
720 pcicore_pmecap_fast(osl_t *osh)
722 uint8 cap_ptr;
723 uint32 pmecap;
725 cap_ptr = pcicore_find_pci_capability(osh, PCI_CAP_POWERMGMTCAP_ID, NULL, NULL);
727 if (!cap_ptr)
728 return FALSE;
730 pmecap = OSL_PCI_READ_CONFIG(osh, cap_ptr, sizeof(uint32));
732 return ((pmecap & PME_CAP_PM_STATES) != 0);
735 /* return TRUE if PM capability exists in the pci config space
736 * Uses and caches the information using core handle
738 static bool
739 pcicore_pmecap(pcicore_info_t *pi)
741 uint8 cap_ptr;
742 uint32 pmecap;
744 if (!pi->pmecap_offset) {
745 cap_ptr = pcicore_find_pci_capability(pi->osh, PCI_CAP_POWERMGMTCAP_ID, NULL, NULL);
746 if (!cap_ptr)
747 return FALSE;
749 pi->pmecap_offset = cap_ptr;
751 pmecap = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset, sizeof(uint32));
753 /* At least one state can generate PME */
754 pi->pmecap = (pmecap & PME_CAP_PM_STATES) != 0;
757 return (pi->pmecap);
760 /* Enable PME generation */
761 void
762 pcicore_pmeen(void *pch)
764 pcicore_info_t *pi = (pcicore_info_t *)pch;
765 uint32 w;
767 /* if not pmecapable return */
768 if (!pcicore_pmecap(pi))
769 return;
771 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET, sizeof(uint32));
772 w |= (PME_CSR_PME_EN);
773 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET, sizeof(uint32), w);
777 * Return TRUE if PME status set
779 bool
780 pcicore_pmestat(void *pch)
782 pcicore_info_t *pi = (pcicore_info_t *)pch;
783 uint32 w;
785 if (!pcicore_pmecap(pi))
786 return FALSE;
788 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET, sizeof(uint32));
790 return (w & PME_CSR_PME_STAT) == PME_CSR_PME_STAT;
793 /* Disable PME generation, clear the PME status bit if set
795 void
796 pcicore_pmeclr(void *pch)
798 pcicore_info_t *pi = (pcicore_info_t *)pch;
799 uint32 w;
801 if (!pcicore_pmecap(pi))
802 return;
804 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET, sizeof(uint32));
806 PCI_ERROR(("pcicore_pci_pmeclr PMECSR : 0x%x\n", w));
808 /* PMESTAT is cleared by writing 1 to it */
809 w &= ~(PME_CSR_PME_EN);
811 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET, sizeof(uint32), w);
814 static void
815 pcicore_fixlatencytimer(pcicore_info_t* pch, uint8 timer_val)
817 pcicore_info_t *pi = (pcicore_info_t *)pch;
818 osl_t *osh = pi->osh;
819 uint8 lattim = read_pci_cfg_byte(PCI_CFG_LATTIM);
821 if (!lattim) {
822 PCI_ERROR(("%s: Modifying PCI_CFG_LATTIM from 0x%x to 0x%x\n",
823 __FUNCTION__, lattim, timer_val));
824 write_pci_cfg_byte(PCI_CFG_LATTIM, timer_val);
828 uint32
829 pcie_lcreg(void *pch, uint32 mask, uint32 val)
831 pcicore_info_t *pi = (pcicore_info_t *)pch;
832 uint8 offset;
834 offset = pi->pciecap_lcreg_offset;
835 if (!offset)
836 return 0;
838 /* set operation */
839 if (mask)
840 OSL_PCI_WRITE_CONFIG(pi->osh, offset, sizeof(uint32), val);
842 return OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32));
847 uint32
848 pcicore_pcieserdesreg(void *pch, uint32 mdioslave, uint32 offset, uint32 mask, uint32 val)
850 uint32 reg_val = 0;
851 pcicore_info_t *pi = (pcicore_info_t *)pch;
853 if (mask) {
854 PCI_ERROR(("PCIEMDIOREG: 0x%x writeval 0x%x\n", offset, val));
855 pcie_mdiowrite(pi, mdioslave, offset, val);
858 if (pcie_mdioread(pi, mdioslave, offset, &reg_val))
859 reg_val = 0xFFFFFFFF;
860 PCI_ERROR(("PCIEMDIOREG: dev 0x%x offset 0x%x read 0x%x\n", mdioslave, offset, reg_val));
862 return reg_val;