blob: 0b4f562427e2fb692657eb785d940eae13dfc943 [file] [log] [blame]
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <typedefs.h>
18#include <bcmdefs.h>
19#include <osl.h>
20#include <bcmutils.h>
21#include <siutils.h>
22#include <hndsoc.h>
23#include <bcmdevs.h>
24#include <sbchipc.h>
25#include <pci_core.h>
26#include <pcie_core.h>
27#include <nicpci.h>
28#include <pcicfg.h>
29
30typedef struct {
31 union {
32 sbpcieregs_t *pcieregs;
33 sbpciregs_t *pciregs;
34 } regs; /* Memory mapped register to the core */
35
36 si_t *sih; /* System interconnect handle */
37 osl_t *osh; /* OSL handle */
38 uint8 pciecap_lcreg_offset; /* PCIE capability LCreg offset in the config space */
39 bool pcie_pr42767;
40 uint8 pcie_polarity;
41 uint8 pcie_war_aspm_ovr; /* Override ASPM/Clkreq settings */
42
43 uint8 pmecap_offset; /* PM Capability offset in the config space */
44 bool pmecap; /* Capable of generating PME */
45} pcicore_info_t;
46
47/* debug/trace */
48#define PCI_ERROR(args)
49#define PCIE_PUB(sih) ((BUSTYPE((sih)->bustype) == PCI_BUS) && ((sih)->buscoretype == PCIE_CORE_ID))
50
51/* routines to access mdio slave device registers */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040052static bool pcie_mdiosetblock(pcicore_info_t *pi, uint blk);
53static int pcie_mdioop(pcicore_info_t *pi, uint physmedia, uint regaddr,
54 bool write, uint *val);
55static int pcie_mdiowrite(pcicore_info_t *pi, uint physmedia, uint readdr,
Henry Ptasinskia9533e72010-09-08 21:04:42 -070056 uint val);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040057static int pcie_mdioread(pcicore_info_t *pi, uint physmedia, uint readdr,
58 uint *ret_val);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070059
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040060static void pcie_extendL1timer(pcicore_info_t *pi, bool extend);
61static void pcie_clkreq_upd(pcicore_info_t *pi, uint state);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070062
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040063static void pcie_war_aspm_clkreq(pcicore_info_t *pi);
64static void pcie_war_serdes(pcicore_info_t *pi);
65static void pcie_war_noplldown(pcicore_info_t *pi);
66static void pcie_war_polarity(pcicore_info_t *pi);
67static void pcie_war_pci_setup(pcicore_info_t *pi);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070068
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040069static bool pcicore_pmecap(pcicore_info_t *pi);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070070
71#define PCIE_ASPM(sih) ((PCIE_PUB(sih)) && (((sih)->buscorerev >= 3) && ((sih)->buscorerev <= 5)))
72
73#define DWORD_ALIGN(x) (x & ~(0x03))
74#define BYTE_POS(x) (x & 0x3)
75#define WORD_POS(x) (x & 0x1)
76
77#define BYTE_SHIFT(x) (8 * BYTE_POS(x))
78#define WORD_SHIFT(x) (16 * WORD_POS(x))
79
80#define BYTE_VAL(a, x) ((a >> BYTE_SHIFT(x)) & 0xFF)
81#define WORD_VAL(a, x) ((a >> WORD_SHIFT(x)) & 0xFFFF)
82
83#define read_pci_cfg_byte(a) \
84 (BYTE_VAL(OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4), a) & 0xff)
85
86#define read_pci_cfg_word(a) \
87 (WORD_VAL(OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4), a) & 0xffff)
88
89#define write_pci_cfg_byte(a, val) do { \
90 uint32 tmpval; \
91 tmpval = (OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4) & ~0xFF << BYTE_POS(a)) | \
92 val << BYTE_POS(a); \
93 OSL_PCI_WRITE_CONFIG(osh, DWORD_ALIGN(a), 4, tmpval); \
94 } while (0)
95
96#define write_pci_cfg_word(a, val) do { \
97 uint32 tmpval; \
98 tmpval = (OSL_PCI_READ_CONFIG(osh, DWORD_ALIGN(a), 4) & ~0xFFFF << WORD_POS(a)) | \
99 val << WORD_POS(a); \
100 OSL_PCI_WRITE_CONFIG(osh, DWORD_ALIGN(a), 4, tmpval); \
101 } while (0)
102
103/* delay needed between the mdio control/ mdiodata register data access */
104#define PR28829_DELAY() OSL_DELAY(10)
105
106/* Initialize the PCI core. It's caller's responsibility to make sure that this is done
107 * only once
108 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400109void *pcicore_init(si_t *sih, osl_t *osh, void *regs)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700110{
111 pcicore_info_t *pi;
112
113 ASSERT(sih->bustype == PCI_BUS);
114
115 /* alloc pcicore_info_t */
Jason Cooperca8c1e52010-09-14 09:45:33 -0400116 pi = MALLOC(osh, sizeof(pcicore_info_t));
117 if (pi == NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700118 PCI_ERROR(("pci_attach: malloc failed! malloced %d bytes\n",
119 MALLOCED(osh)));
Jason Cooper90ea2292010-09-14 09:45:32 -0400120 return NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700121 }
122
123 bzero(pi, sizeof(pcicore_info_t));
124
125 pi->sih = sih;
126 pi->osh = osh;
127
128 if (sih->buscoretype == PCIE_CORE_ID) {
129 uint8 cap_ptr;
130 pi->regs.pcieregs = (sbpcieregs_t *) regs;
131 cap_ptr =
132 pcicore_find_pci_capability(pi->osh, PCI_CAP_PCIECAP_ID,
133 NULL, NULL);
134 ASSERT(cap_ptr);
135 pi->pciecap_lcreg_offset = cap_ptr + PCIE_CAP_LINKCTRL_OFFSET;
136 } else
137 pi->regs.pciregs = (sbpciregs_t *) regs;
138
139 return pi;
140}
141
142void pcicore_deinit(void *pch)
143{
144 pcicore_info_t *pi = (pcicore_info_t *) pch;
145
146 if (pi == NULL)
147 return;
148 MFREE(pi->osh, pi, sizeof(pcicore_info_t));
149}
150
151/* return cap_offset if requested capability exists in the PCI config space */
152/* Note that it's caller's responsibility to make sure it's a pci bus */
153uint8
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400154pcicore_find_pci_capability(osl_t *osh, uint8 req_cap_id, uchar *buf,
155 uint32 *buflen)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700156{
157 uint8 cap_id;
158 uint8 cap_ptr = 0;
159 uint32 bufsize;
160 uint8 byte_val;
161
162 /* check for Header type 0 */
163 byte_val = read_pci_cfg_byte(PCI_CFG_HDR);
164 if ((byte_val & 0x7f) != PCI_HEADER_NORMAL)
165 goto end;
166
167 /* check if the capability pointer field exists */
168 byte_val = read_pci_cfg_byte(PCI_CFG_STAT);
169 if (!(byte_val & PCI_CAPPTR_PRESENT))
170 goto end;
171
172 cap_ptr = read_pci_cfg_byte(PCI_CFG_CAPPTR);
173 /* check if the capability pointer is 0x00 */
174 if (cap_ptr == 0x00)
175 goto end;
176
177 /* loop thr'u the capability list and see if the pcie capabilty exists */
178
179 cap_id = read_pci_cfg_byte(cap_ptr);
180
181 while (cap_id != req_cap_id) {
182 cap_ptr = read_pci_cfg_byte((cap_ptr + 1));
183 if (cap_ptr == 0x00)
184 break;
185 cap_id = read_pci_cfg_byte(cap_ptr);
186 }
187 if (cap_id != req_cap_id) {
188 goto end;
189 }
190 /* found the caller requested capability */
191 if ((buf != NULL) && (buflen != NULL)) {
192 uint8 cap_data;
193
194 bufsize = *buflen;
195 if (!bufsize)
196 goto end;
197 *buflen = 0;
198 /* copy the cpability data excluding cap ID and next ptr */
199 cap_data = cap_ptr + 2;
200 if ((bufsize + cap_data) > SZPCR)
201 bufsize = SZPCR - cap_data;
202 *buflen = bufsize;
203 while (bufsize--) {
204 *buf = read_pci_cfg_byte(cap_data);
205 cap_data++;
206 buf++;
207 }
208 }
209 end:
210 return cap_ptr;
211}
212
213/* ***** Register Access API */
214uint
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400215pcie_readreg(osl_t *osh, sbpcieregs_t *pcieregs, uint addrtype, uint offset)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700216{
217 uint retval = 0xFFFFFFFF;
218
219 ASSERT(pcieregs != NULL);
220
221 switch (addrtype) {
222 case PCIE_CONFIGREGS:
223 W_REG(osh, (&pcieregs->configaddr), offset);
224 (void)R_REG(osh, (&pcieregs->configaddr));
225 retval = R_REG(osh, &(pcieregs->configdata));
226 break;
227 case PCIE_PCIEREGS:
228 W_REG(osh, &(pcieregs->pcieindaddr), offset);
229 (void)R_REG(osh, (&pcieregs->pcieindaddr));
230 retval = R_REG(osh, &(pcieregs->pcieinddata));
231 break;
232 default:
233 ASSERT(0);
234 break;
235 }
236
237 return retval;
238}
239
240uint
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400241pcie_writereg(osl_t *osh, sbpcieregs_t *pcieregs, uint addrtype, uint offset,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700242 uint val)
243{
244 ASSERT(pcieregs != NULL);
245
246 switch (addrtype) {
247 case PCIE_CONFIGREGS:
248 W_REG(osh, (&pcieregs->configaddr), offset);
249 W_REG(osh, (&pcieregs->configdata), val);
250 break;
251 case PCIE_PCIEREGS:
252 W_REG(osh, (&pcieregs->pcieindaddr), offset);
253 W_REG(osh, (&pcieregs->pcieinddata), val);
254 break;
255 default:
256 ASSERT(0);
257 break;
258 }
259 return 0;
260}
261
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400262static bool pcie_mdiosetblock(pcicore_info_t *pi, uint blk)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700263{
264 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
265 uint mdiodata, i = 0;
266 uint pcie_serdes_spinwait = 200;
267
268 mdiodata =
269 MDIODATA_START | MDIODATA_WRITE | (MDIODATA_DEV_ADDR <<
270 MDIODATA_DEVADDR_SHF) |
271 (MDIODATA_BLK_ADDR << MDIODATA_REGADDR_SHF) | MDIODATA_TA | (blk <<
272 4);
273 W_REG(pi->osh, &pcieregs->mdiodata, mdiodata);
274
275 PR28829_DELAY();
276 /* retry till the transaction is complete */
277 while (i < pcie_serdes_spinwait) {
278 if (R_REG(pi->osh, &(pcieregs->mdiocontrol)) &
279 MDIOCTL_ACCESS_DONE) {
280 break;
281 }
282 OSL_DELAY(1000);
283 i++;
284 }
285
286 if (i >= pcie_serdes_spinwait) {
287 PCI_ERROR(("pcie_mdiosetblock: timed out\n"));
288 return FALSE;
289 }
290
291 return TRUE;
292}
293
294static int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400295pcie_mdioop(pcicore_info_t *pi, uint physmedia, uint regaddr, bool write,
296 uint *val)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700297{
298 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
299 uint mdiodata;
300 uint i = 0;
301 uint pcie_serdes_spinwait = 10;
302
303 /* enable mdio access to SERDES */
304 W_REG(pi->osh, (&pcieregs->mdiocontrol),
305 MDIOCTL_PREAM_EN | MDIOCTL_DIVISOR_VAL);
306
307 if (pi->sih->buscorerev >= 10) {
308 /* new serdes is slower in rw, using two layers of reg address mapping */
309 if (!pcie_mdiosetblock(pi, physmedia))
310 return 1;
311 mdiodata = (MDIODATA_DEV_ADDR << MDIODATA_DEVADDR_SHF) |
312 (regaddr << MDIODATA_REGADDR_SHF);
313 pcie_serdes_spinwait *= 20;
314 } else {
315 mdiodata = (physmedia << MDIODATA_DEVADDR_SHF_OLD) |
316 (regaddr << MDIODATA_REGADDR_SHF_OLD);
317 }
318
319 if (!write)
320 mdiodata |= (MDIODATA_START | MDIODATA_READ | MDIODATA_TA);
321 else
322 mdiodata |=
323 (MDIODATA_START | MDIODATA_WRITE | MDIODATA_TA | *val);
324
325 W_REG(pi->osh, &pcieregs->mdiodata, mdiodata);
326
327 PR28829_DELAY();
328
329 /* retry till the transaction is complete */
330 while (i < pcie_serdes_spinwait) {
331 if (R_REG(pi->osh, &(pcieregs->mdiocontrol)) &
332 MDIOCTL_ACCESS_DONE) {
333 if (!write) {
334 PR28829_DELAY();
335 *val =
336 (R_REG(pi->osh, &(pcieregs->mdiodata)) &
337 MDIODATA_MASK);
338 }
339 /* Disable mdio access to SERDES */
340 W_REG(pi->osh, (&pcieregs->mdiocontrol), 0);
341 return 0;
342 }
343 OSL_DELAY(1000);
344 i++;
345 }
346
347 PCI_ERROR(("pcie_mdioop: timed out op: %d\n", write));
348 /* Disable mdio access to SERDES */
349 W_REG(pi->osh, (&pcieregs->mdiocontrol), 0);
350 return 1;
351}
352
353/* use the mdio interface to read from mdio slaves */
354static int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400355pcie_mdioread(pcicore_info_t *pi, uint physmedia, uint regaddr, uint *regval)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700356{
357 return pcie_mdioop(pi, physmedia, regaddr, FALSE, regval);
358}
359
360/* use the mdio interface to write to mdio slaves */
361static int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400362pcie_mdiowrite(pcicore_info_t *pi, uint physmedia, uint regaddr, uint val)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700363{
364 return pcie_mdioop(pi, physmedia, regaddr, TRUE, &val);
365}
366
367/* ***** Support functions ***** */
368uint8 pcie_clkreq(void *pch, uint32 mask, uint32 val)
369{
370 pcicore_info_t *pi = (pcicore_info_t *) pch;
371 uint32 reg_val;
372 uint8 offset;
373
374 offset = pi->pciecap_lcreg_offset;
375 if (!offset)
376 return 0;
377
378 reg_val = OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32));
379 /* set operation */
380 if (mask) {
381 if (val)
382 reg_val |= PCIE_CLKREQ_ENAB;
383 else
384 reg_val &= ~PCIE_CLKREQ_ENAB;
385 OSL_PCI_WRITE_CONFIG(pi->osh, offset, sizeof(uint32), reg_val);
386 reg_val = OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32));
387 }
388 if (reg_val & PCIE_CLKREQ_ENAB)
389 return 1;
390 else
391 return 0;
392}
393
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400394static void pcie_extendL1timer(pcicore_info_t *pi, bool extend)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700395{
396 uint32 w;
397 si_t *sih = pi->sih;
398 osl_t *osh = pi->osh;
399 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
400
401 if (!PCIE_PUB(sih) || sih->buscorerev < 7)
402 return;
403
404 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
405 if (extend)
406 w |= PCIE_ASPMTIMER_EXTEND;
407 else
408 w &= ~PCIE_ASPMTIMER_EXTEND;
409 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG, w);
410 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_PMTHRESHREG);
411}
412
413/* centralized clkreq control policy */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400414static void pcie_clkreq_upd(pcicore_info_t *pi, uint state)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700415{
416 si_t *sih = pi->sih;
417 ASSERT(PCIE_PUB(sih));
418
419 switch (state) {
420 case SI_DOATTACH:
421 if (PCIE_ASPM(sih))
422 pcie_clkreq((void *)pi, 1, 0);
423 break;
424 case SI_PCIDOWN:
425 if (sih->buscorerev == 6) { /* turn on serdes PLL down */
426 si_corereg(sih, SI_CC_IDX,
427 OFFSETOF(chipcregs_t, chipcontrol_addr), ~0,
428 0);
429 si_corereg(sih, SI_CC_IDX,
430 OFFSETOF(chipcregs_t, chipcontrol_data),
431 ~0x40, 0);
432 } else if (pi->pcie_pr42767) {
433 pcie_clkreq((void *)pi, 1, 1);
434 }
435 break;
436 case SI_PCIUP:
437 if (sih->buscorerev == 6) { /* turn off serdes PLL down */
438 si_corereg(sih, SI_CC_IDX,
439 OFFSETOF(chipcregs_t, chipcontrol_addr), ~0,
440 0);
441 si_corereg(sih, SI_CC_IDX,
442 OFFSETOF(chipcregs_t, chipcontrol_data),
443 ~0x40, 0x40);
444 } else if (PCIE_ASPM(sih)) { /* disable clkreq */
445 pcie_clkreq((void *)pi, 1, 0);
446 }
447 break;
448 default:
449 ASSERT(0);
450 break;
451 }
452}
453
454/* ***** PCI core WARs ***** */
455/* Done only once at attach time */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400456static void pcie_war_polarity(pcicore_info_t *pi)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700457{
458 uint32 w;
459
460 if (pi->pcie_polarity != 0)
461 return;
462
463 w = pcie_readreg(pi->osh, pi->regs.pcieregs, PCIE_PCIEREGS,
464 PCIE_PLP_STATUSREG);
465
466 /* Detect the current polarity at attach and force that polarity and
467 * disable changing the polarity
468 */
469 if ((w & PCIE_PLP_POLARITYINV_STAT) == 0)
470 pi->pcie_polarity = (SERDES_RX_CTRL_FORCE);
471 else
472 pi->pcie_polarity =
473 (SERDES_RX_CTRL_FORCE | SERDES_RX_CTRL_POLARITY);
474}
475
476/* enable ASPM and CLKREQ if srom doesn't have it */
477/* Needs to happen when update to shadow SROM is needed
478 * : Coming out of 'standby'/'hibernate'
479 * : If pcie_war_aspm_ovr state changed
480 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400481static void pcie_war_aspm_clkreq(pcicore_info_t *pi)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700482{
483 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
484 si_t *sih = pi->sih;
485 uint16 val16, *reg16;
486 uint32 w;
487
488 if (!PCIE_ASPM(sih))
489 return;
490
491 /* bypass this on QT or VSIM */
492 if (!ISSIM_ENAB(sih)) {
493
494 reg16 = &pcieregs->sprom[SRSH_ASPM_OFFSET];
495 val16 = R_REG(pi->osh, reg16);
496
497 val16 &= ~SRSH_ASPM_ENB;
498 if (pi->pcie_war_aspm_ovr == PCIE_ASPM_ENAB)
499 val16 |= SRSH_ASPM_ENB;
500 else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L1_ENAB)
501 val16 |= SRSH_ASPM_L1_ENB;
502 else if (pi->pcie_war_aspm_ovr == PCIE_ASPM_L0s_ENAB)
503 val16 |= SRSH_ASPM_L0s_ENB;
504
505 W_REG(pi->osh, reg16, val16);
506
507 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset,
508 sizeof(uint32));
509 w &= ~PCIE_ASPM_ENAB;
510 w |= pi->pcie_war_aspm_ovr;
511 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset,
512 sizeof(uint32), w);
513 }
514
515 reg16 = &pcieregs->sprom[SRSH_CLKREQ_OFFSET_REV5];
516 val16 = R_REG(pi->osh, reg16);
517
518 if (pi->pcie_war_aspm_ovr != PCIE_ASPM_DISAB) {
519 val16 |= SRSH_CLKREQ_ENB;
520 pi->pcie_pr42767 = TRUE;
521 } else
522 val16 &= ~SRSH_CLKREQ_ENB;
523
524 W_REG(pi->osh, reg16, val16);
525}
526
527/* Apply the polarity determined at the start */
528/* Needs to happen when coming out of 'standby'/'hibernate' */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400529static void pcie_war_serdes(pcicore_info_t *pi)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700530{
531 uint32 w = 0;
532
533 if (pi->pcie_polarity != 0)
534 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CTRL,
535 pi->pcie_polarity);
536
537 pcie_mdioread(pi, MDIODATA_DEV_PLL, SERDES_PLL_CTRL, &w);
538 if (w & PLL_CTRL_FREQDET_EN) {
539 w &= ~PLL_CTRL_FREQDET_EN;
540 pcie_mdiowrite(pi, MDIODATA_DEV_PLL, SERDES_PLL_CTRL, w);
541 }
542}
543
544/* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */
545/* Needs to happen when coming out of 'standby'/'hibernate' */
Jason Coopera2627bc2010-09-14 09:45:31 -0400546static void BCMINITFN(pcie_misc_config_fixup) (pcicore_info_t *pi)
547{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700548 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
549 uint16 val16, *reg16;
550
551 reg16 = &pcieregs->sprom[SRSH_PCIE_MISC_CONFIG];
552 val16 = R_REG(pi->osh, reg16);
553
554 if ((val16 & SRSH_L23READY_EXIT_NOPERST) == 0) {
555 val16 |= SRSH_L23READY_EXIT_NOPERST;
556 W_REG(pi->osh, reg16, val16);
557 }
558}
559
560/* quick hack for testing */
561/* Needs to happen when coming out of 'standby'/'hibernate' */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400562static void pcie_war_noplldown(pcicore_info_t *pi)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700563{
564 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
565 uint16 *reg16;
566
567 ASSERT(pi->sih->buscorerev == 7);
568
569 /* turn off serdes PLL down */
570 si_corereg(pi->sih, SI_CC_IDX, OFFSETOF(chipcregs_t, chipcontrol),
571 CHIPCTRL_4321_PLL_DOWN, CHIPCTRL_4321_PLL_DOWN);
572
573 /* clear srom shadow backdoor */
574 reg16 = &pcieregs->sprom[SRSH_BD_OFFSET];
575 W_REG(pi->osh, reg16, 0);
576}
577
578/* Needs to happen when coming out of 'standby'/'hibernate' */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400579static void pcie_war_pci_setup(pcicore_info_t *pi)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700580{
581 si_t *sih = pi->sih;
582 osl_t *osh = pi->osh;
583 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
584 uint32 w;
585
586 if ((sih->buscorerev == 0) || (sih->buscorerev == 1)) {
587 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS,
588 PCIE_TLP_WORKAROUNDSREG);
589 w |= 0x8;
590 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS,
591 PCIE_TLP_WORKAROUNDSREG, w);
592 }
593
594 if (sih->buscorerev == 1) {
595 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG);
596 w |= (0x40);
597 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS, PCIE_DLLP_LCREG, w);
598 }
599
600 if (sih->buscorerev == 0) {
601 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_TIMER1, 0x8128);
602 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDR, 0x0100);
603 pcie_mdiowrite(pi, MDIODATA_DEV_RX, SERDES_RX_CDRBW, 0x1466);
604 } else if (PCIE_ASPM(sih)) {
605 /* Change the L1 threshold for better performance */
606 w = pcie_readreg(osh, pcieregs, PCIE_PCIEREGS,
607 PCIE_DLLP_PMTHRESHREG);
608 w &= ~(PCIE_L1THRESHOLDTIME_MASK);
609 w |= (PCIE_L1THRESHOLD_WARVAL << PCIE_L1THRESHOLDTIME_SHIFT);
610 pcie_writereg(osh, pcieregs, PCIE_PCIEREGS,
611 PCIE_DLLP_PMTHRESHREG, w);
612
613 pcie_war_serdes(pi);
614
615 pcie_war_aspm_clkreq(pi);
616 } else if (pi->sih->buscorerev == 7)
617 pcie_war_noplldown(pi);
618
619 /* Note that the fix is actually in the SROM, that's why this is open-ended */
620 if (pi->sih->buscorerev >= 6)
621 pcie_misc_config_fixup(pi);
622}
623
624void pcie_war_ovr_aspm_update(void *pch, uint8 aspm)
625{
626 pcicore_info_t *pi = (pcicore_info_t *) pch;
627
628 if (!PCIE_ASPM(pi->sih))
629 return;
630
631 /* Validate */
632 if (aspm > PCIE_ASPM_ENAB)
633 return;
634
635 pi->pcie_war_aspm_ovr = aspm;
636
637 /* Update the current state */
638 pcie_war_aspm_clkreq(pi);
639}
640
641/* ***** Functions called during driver state changes ***** */
Jason Coopera2627bc2010-09-14 09:45:31 -0400642void BCMATTACHFN(pcicore_attach) (void *pch, char *pvars, int state)
643{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700644 pcicore_info_t *pi = (pcicore_info_t *) pch;
645 si_t *sih = pi->sih;
646
647 /* Determine if this board needs override */
648 if (PCIE_ASPM(sih)) {
649 if ((uint32) getintvar(pvars, "boardflags2") & BFL2_PCIEWAR_OVR) {
650 pi->pcie_war_aspm_ovr = PCIE_ASPM_DISAB;
651 } else {
652 pi->pcie_war_aspm_ovr = PCIE_ASPM_ENAB;
653 }
654 }
655
656 /* These need to happen in this order only */
657 pcie_war_polarity(pi);
658
659 pcie_war_serdes(pi);
660
661 pcie_war_aspm_clkreq(pi);
662
663 pcie_clkreq_upd(pi, state);
664
665}
666
667void pcicore_hwup(void *pch)
668{
669 pcicore_info_t *pi = (pcicore_info_t *) pch;
670
671 if (!pi || !PCIE_PUB(pi->sih))
672 return;
673
674 pcie_war_pci_setup(pi);
675}
676
677void pcicore_up(void *pch, int state)
678{
679 pcicore_info_t *pi = (pcicore_info_t *) pch;
680
681 if (!pi || !PCIE_PUB(pi->sih))
682 return;
683
684 /* Restore L1 timer for better performance */
685 pcie_extendL1timer(pi, TRUE);
686
687 pcie_clkreq_upd(pi, state);
688}
689
690/* When the device is going to enter D3 state (or the system is going to enter S3/S4 states */
691void pcicore_sleep(void *pch)
692{
693 pcicore_info_t *pi = (pcicore_info_t *) pch;
694 uint32 w;
695
696 if (!pi || !PCIE_ASPM(pi->sih))
697 return;
698
699 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pciecap_lcreg_offset,
700 sizeof(uint32));
701 w &= ~PCIE_CAP_LCREG_ASPML1;
702 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pciecap_lcreg_offset, sizeof(uint32),
703 w);
704
705 pi->pcie_pr42767 = FALSE;
706}
707
708void pcicore_down(void *pch, int state)
709{
710 pcicore_info_t *pi = (pcicore_info_t *) pch;
711
712 if (!pi || !PCIE_PUB(pi->sih))
713 return;
714
715 pcie_clkreq_upd(pi, state);
716
717 /* Reduce L1 timer for better power savings */
718 pcie_extendL1timer(pi, FALSE);
719}
720
721/* ***** Wake-on-wireless-LAN (WOWL) support functions ***** */
722/* Just uses PCI config accesses to find out, when needed before sb_attach is done */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400723bool pcicore_pmecap_fast(osl_t *osh)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700724{
725 uint8 cap_ptr;
726 uint32 pmecap;
727
728 cap_ptr =
729 pcicore_find_pci_capability(osh, PCI_CAP_POWERMGMTCAP_ID, NULL,
730 NULL);
731
732 if (!cap_ptr)
733 return FALSE;
734
735 pmecap = OSL_PCI_READ_CONFIG(osh, cap_ptr, sizeof(uint32));
736
Jason Cooper90ea2292010-09-14 09:45:32 -0400737 return (pmecap & PME_CAP_PM_STATES) != 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700738}
739
740/* return TRUE if PM capability exists in the pci config space
741 * Uses and caches the information using core handle
742 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400743static bool pcicore_pmecap(pcicore_info_t *pi)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700744{
745 uint8 cap_ptr;
746 uint32 pmecap;
747
748 if (!pi->pmecap_offset) {
749 cap_ptr =
750 pcicore_find_pci_capability(pi->osh,
751 PCI_CAP_POWERMGMTCAP_ID, NULL,
752 NULL);
753 if (!cap_ptr)
754 return FALSE;
755
756 pi->pmecap_offset = cap_ptr;
757
758 pmecap =
759 OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset,
760 sizeof(uint32));
761
762 /* At least one state can generate PME */
763 pi->pmecap = (pmecap & PME_CAP_PM_STATES) != 0;
764 }
765
Jason Cooper90ea2292010-09-14 09:45:32 -0400766 return pi->pmecap;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700767}
768
769/* Enable PME generation */
770void pcicore_pmeen(void *pch)
771{
772 pcicore_info_t *pi = (pcicore_info_t *) pch;
773 uint32 w;
774
775 /* if not pmecapable return */
776 if (!pcicore_pmecap(pi))
777 return;
778
779 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET,
780 sizeof(uint32));
781 w |= (PME_CSR_PME_EN);
782 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET,
783 sizeof(uint32), w);
784}
785
786/*
787 * Return TRUE if PME status set
788 */
789bool pcicore_pmestat(void *pch)
790{
791 pcicore_info_t *pi = (pcicore_info_t *) pch;
792 uint32 w;
793
794 if (!pcicore_pmecap(pi))
795 return FALSE;
796
797 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET,
798 sizeof(uint32));
799
800 return (w & PME_CSR_PME_STAT) == PME_CSR_PME_STAT;
801}
802
803/* Disable PME generation, clear the PME status bit if set
804 */
805void pcicore_pmeclr(void *pch)
806{
807 pcicore_info_t *pi = (pcicore_info_t *) pch;
808 uint32 w;
809
810 if (!pcicore_pmecap(pi))
811 return;
812
813 w = OSL_PCI_READ_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET,
814 sizeof(uint32));
815
816 PCI_ERROR(("pcicore_pci_pmeclr PMECSR : 0x%x\n", w));
817
818 /* PMESTAT is cleared by writing 1 to it */
819 w &= ~(PME_CSR_PME_EN);
820
821 OSL_PCI_WRITE_CONFIG(pi->osh, pi->pmecap_offset + PME_CSR_OFFSET,
822 sizeof(uint32), w);
823}
824
825uint32 pcie_lcreg(void *pch, uint32 mask, uint32 val)
826{
827 pcicore_info_t *pi = (pcicore_info_t *) pch;
828 uint8 offset;
829
830 offset = pi->pciecap_lcreg_offset;
831 if (!offset)
832 return 0;
833
834 /* set operation */
835 if (mask)
836 OSL_PCI_WRITE_CONFIG(pi->osh, offset, sizeof(uint32), val);
837
838 return OSL_PCI_READ_CONFIG(pi->osh, offset, sizeof(uint32));
839}
840
841uint32
842pcicore_pciereg(void *pch, uint32 offset, uint32 mask, uint32 val, uint type)
843{
844 uint32 reg_val = 0;
845 pcicore_info_t *pi = (pcicore_info_t *) pch;
846 sbpcieregs_t *pcieregs = pi->regs.pcieregs;
847 osl_t *osh = pi->osh;
848
849 if (mask) {
850 PCI_ERROR(("PCIEREG: 0x%x writeval 0x%x\n", offset, val));
851 pcie_writereg(osh, pcieregs, type, offset, val);
852 }
853
854 /* Should not read register 0x154 */
855 if (pi->sih->buscorerev <= 5 && offset == PCIE_DLLP_PCIE11
856 && type == PCIE_PCIEREGS)
857 return reg_val;
858
859 reg_val = pcie_readreg(osh, pcieregs, type, offset);
860 PCI_ERROR(("PCIEREG: 0x%x readval is 0x%x\n", offset, reg_val));
861
862 return reg_val;
863}
864
865uint32
866pcicore_pcieserdesreg(void *pch, uint32 mdioslave, uint32 offset, uint32 mask,
867 uint32 val)
868{
869 uint32 reg_val = 0;
870 pcicore_info_t *pi = (pcicore_info_t *) pch;
871
872 if (mask) {
873 PCI_ERROR(("PCIEMDIOREG: 0x%x writeval 0x%x\n", offset, val));
874 pcie_mdiowrite(pi, mdioslave, offset, val);
875 }
876
877 if (pcie_mdioread(pi, mdioslave, offset, &reg_val))
878 reg_val = 0xFFFFFFFF;
879 PCI_ERROR(("PCIEMDIOREG: dev 0x%x offset 0x%x read 0x%x\n", mdioslave,
880 offset, reg_val));
881
882 return reg_val;
883}