+ si = SB_INFO(sbh);
+ if (si->sb.ccrev < 11)
+ return NULL;
+
+ if ((gi = MALLOC(si->osh, sizeof(gpioh_item_t))) == NULL)
+ return NULL;
+
+ bzero(gi, sizeof(gpioh_item_t));
+ gi->event = event;
+ gi->handler = cb;
+ gi->arg = arg;
+ gi->level = level;
+
+ gi->next = si->gpioh_head;
+ si->gpioh_head = gi;
+
+ return (void *)(gi);
+}
+
+void BCMINITFN(sb_gpio_handler_unregister) (sb_t * sbh, void *gpioh) {
+ sb_info_t *si;
+ gpioh_item_t *p, *n;
+
+ si = SB_INFO(sbh);
+ if (si->sb.ccrev < 11)
+ return;
+
+ ASSERT(si->gpioh_head);
+ if ((void *)si->gpioh_head == gpioh) {
+ si->gpioh_head = si->gpioh_head->next;
+ MFREE(si->osh, gpioh, sizeof(gpioh_item_t));
+ return;
+ } else {
+ p = si->gpioh_head;
+ n = p->next;
+ while (n) {
+ if ((void *)n == gpioh) {
+ p->next = n->next;
+ MFREE(si->osh, gpioh, sizeof(gpioh_item_t));
+ return;
+ }
+ p = n;
+ n = n->next;
+ }
+ }
+
+ ASSERT(0); /* Not found in list */
+}
+
+void sb_gpio_handler_process(sb_t * sbh)
+{
+ sb_info_t *si;
+ gpioh_item_t *h;
+ uint32 status;
+ uint32 level = sb_gpioin(sbh);
+ uint32 edge = sb_gpioevent(sbh, GPIO_REGEVT, 0, 0);
+
+ si = SB_INFO(sbh);
+ for (h = si->gpioh_head; h != NULL; h = h->next) {
+ if (h->handler) {
+ status = (h->level ? level : edge);
+
+ if (status & h->event)
+ h->handler(status, h->arg);
+ }
+ }
+
+ sb_gpioevent(sbh, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */
+}
+
+uint32 sb_gpio_int_enable(sb_t * sbh, bool enable)
+{
+ sb_info_t *si;
+ uint offs;
+
+ si = SB_INFO(sbh);
+ if (si->sb.ccrev < 11)
+ return -1;
+
+ offs = OFFSETOF(chipcregs_t, intmask);
+ return (sb_corereg
+ (sbh, SB_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0)));
+}
+
+#ifdef BCMDBG
+void sb_dump(sb_t * sbh, struct bcmstrbuf *b)
+{
+ sb_info_t *si;
+ uint i;
+
+ si = SB_INFO(sbh);
+
+ bcm_bprintf(b,
+ "si %p chip 0x%x chiprev 0x%x boardtype 0x%x boardvendor 0x%x bus %d\n",
+ si, si->sb.chip, si->sb.chiprev, si->sb.boardtype,
+ si->sb.boardvendor, si->sb.bustype);
+ bcm_bprintf(b, "osh %p curmap %p\n", si->osh, si->curmap);
+ bcm_bprintf(b,
+ "sonicsrev %d ccrev %d buscoretype 0x%x buscorerev %d curidx %d\n",
+ si->sb.sonicsrev, si->sb.ccrev, si->sb.buscoretype,
+ si->sb.buscorerev, si->curidx);
+
+ bcm_bprintf(b, "forceHT %d ASPM overflowPR42780 %d pcie_polarity %d\n",
+ si->sb.pr32414, si->sb.pr42780, si->pcie_polarity);
+
+ bcm_bprintf(b, "cores: ");
+ for (i = 0; i < si->numcores; i++)
+ bcm_bprintf(b, "0x%x ", si->coreid[i]);
+ bcm_bprintf(b, "\n");
+}
+
+/* print interesting sbconfig registers */
+void sb_dumpregs(sb_t * sbh, struct bcmstrbuf *b)
+{
+ sb_info_t *si;
+ sbconfig_t *sb;
+ uint origidx;
+ uint curidx, i, intr_val = 0;
+
+ si = SB_INFO(sbh);
+ origidx = si->curidx;
+
+ INTR_OFF(si, intr_val);
+ curidx = si->curidx;
+
+ for (i = 0; i < si->numcores; i++) {
+ sb = REGS2SB(sb_setcoreidx(sbh, i));
+
+ bcm_bprintf(b, "core 0x%x: \n", si->coreid[i]);
+ bcm_bprintf(b,
+ "sbtmstatelow 0x%x sbtmstatehigh 0x%x sbidhigh 0x%x "
+ "sbimstate 0x%x\n sbimconfiglow 0x%x sbimconfighigh 0x%x\n",
+ R_SBREG(si, &sb->sbtmstatelow), R_SBREG(si,
+ &sb->
+ sbtmstatehigh),
+ R_SBREG(si, &sb->sbidhigh), R_SBREG(si,
+ &sb->sbimstate),
+ R_SBREG(si, &sb->sbimconfiglow), R_SBREG(si,
+ &sb->
+ sbimconfighigh));
+ }
+
+ sb_setcoreidx(sbh, origidx);
+ INTR_RESTORE(si, intr_val);
+}
+
+void sb_view(sb_t * sbh)
+{
+ sb_info_t *si;
+ sbconfig_t *sb;
+
+ si = SB_INFO(sbh);
+ sb = REGS2SB(si->curmap);
+
+ if (si->sb.sonicsrev > SONICS_2_2)
+ SB_ERROR(("sbimerrlog 0x%x sbimerrloga 0x%x\n",
+ sb_corereg(sbh, sb_coreidx(&si->sb), SBIMERRLOG, 0,
+ 0), sb_corereg(sbh, sb_coreidx(&si->sb),
+ SBIMERRLOGA, 0, 0)));
+
+ SB_ERROR(("sbipsflag 0x%x sbtpsflag 0x%x sbtmerrloga 0x%x sbtmerrlog 0x%x\n", R_SBREG(si, &sb->sbipsflag), R_SBREG(si, &sb->sbtpsflag), R_SBREG(si, &sb->sbtmerrloga), R_SBREG(si, &sb->sbtmerrlog)));
+ SB_ERROR(("sbadmatch3 0x%x sbadmatch2 0x%x sbadmatch1 0x%x\n",
+ R_SBREG(si, &sb->sbadmatch3), R_SBREG(si, &sb->sbadmatch2),
+ R_SBREG(si, &sb->sbadmatch1)));
+ SB_ERROR(("sbimstate 0x%x sbintvec 0x%x sbtmstatelow 0x%x sbtmstatehigh 0x%x\n", R_SBREG(si, &sb->sbimstate), R_SBREG(si, &sb->sbintvec), R_SBREG(si, &sb->sbtmstatelow), R_SBREG(si, &sb->sbtmstatehigh)));
+ SB_ERROR(("sbbwa0 0x%x sbimconfiglow 0x%x sbimconfighigh 0x%x sbadmatch0 0x%x\n", R_SBREG(si, &sb->sbbwa0), R_SBREG(si, &sb->sbimconfiglow), R_SBREG(si, &sb->sbimconfighigh), R_SBREG(si, &sb->sbadmatch0)));
+ SB_ERROR(("sbtmconfiglow 0x%x sbtmconfighigh 0x%x sbbconfig 0x%x sbbstate 0x%x\n", R_SBREG(si, &sb->sbtmconfiglow), R_SBREG(si, &sb->sbtmconfighigh), R_SBREG(si, &sb->sbbconfig), R_SBREG(si, &sb->sbbstate)));
+ SB_ERROR(("sbactcnfg 0x%x sbflagst 0x%x sbidlow 0x%x sbidhigh 0x%x\n",
+ R_SBREG(si, &sb->sbactcnfg), R_SBREG(si, &sb->sbflagst),
+ R_SBREG(si, &sb->sbidlow), R_SBREG(si, &sb->sbidhigh)));
+}
+
+void sb_viewall(sb_t * sbh)
+{
+ sb_info_t *si;
+ uint curidx, i;
+ uint intr_val = 0;
+
+ si = SB_INFO(sbh);
+ curidx = si->curidx;
+
+ for (i = 0; i < si->numcores; i++) {
+ INTR_OFF(si, intr_val);
+ sb_setcoreidx(sbh, i);
+ sb_view(sbh);
+ INTR_RESTORE(si, intr_val);
+ }
+
+ sb_setcoreidx(sbh, curidx);
+}
+#endif /* BCMDBG */
+
+/* return the slow clock source - LPO, XTAL, or PCI */
+static uint sb_slowclk_src(sb_info_t * si)
+{
+ chipcregs_t *cc;
+
+ ASSERT(sb_coreid(&si->sb) == SB_CC);
+
+ if (si->sb.ccrev < 6) {
+ if ((BUSTYPE(si->sb.bustype) == PCI_BUS) &&
+ (OSL_PCI_READ_CONFIG(si->osh, PCI_GPIO_OUT, sizeof(uint32))
+ & PCI_CFG_GPIO_SCS))
+ return (SCC_SS_PCI);
+ else
+ return (SCC_SS_XTAL);
+ } else if (si->sb.ccrev < 10) {
+ cc = (chipcregs_t *) sb_setcoreidx(&si->sb, si->curidx);
+ return (R_REG(si->osh, &cc->slow_clk_ctl) & SCC_SS_MASK);
+ } else /* Insta-clock */
+ return (SCC_SS_XTAL);
+}
+
+/* return the ILP (slowclock) min or max frequency */
+static uint sb_slowclk_freq(sb_info_t * si, bool max_freq)
+{
+ chipcregs_t *cc;
+ uint32 slowclk;
+ uint div;
+
+ ASSERT(sb_coreid(&si->sb) == SB_CC);
+
+ cc = (chipcregs_t *) sb_setcoreidx(&si->sb, si->curidx);
+
+ /* shouldn't be here unless we've established the chip has dynamic clk control */
+ ASSERT(R_REG(si->osh, &cc->capabilities) & CC_CAP_PWR_CTL);
+
+ slowclk = sb_slowclk_src(si);
+ if (si->sb.ccrev < 6) {
+ if (slowclk == SCC_SS_PCI)
+ return (max_freq ? (PCIMAXFREQ / 64)
+ : (PCIMINFREQ / 64));
+ else
+ return (max_freq ? (XTALMAXFREQ / 32)
+ : (XTALMINFREQ / 32));
+ } else if (si->sb.ccrev < 10) {
+ div =
+ 4 *
+ (((R_REG(si->osh, &cc->slow_clk_ctl) & SCC_CD_MASK) >>
+ SCC_CD_SHIFT)
+ + 1);
+ if (slowclk == SCC_SS_LPO)
+ return (max_freq ? LPOMAXFREQ : LPOMINFREQ);
+ else if (slowclk == SCC_SS_XTAL)
+ return (max_freq ? (XTALMAXFREQ / div)
+ : (XTALMINFREQ / div));
+ else if (slowclk == SCC_SS_PCI)
+ return (max_freq ? (PCIMAXFREQ / div)
+ : (PCIMINFREQ / div));
+ else
+ ASSERT(0);
+ } else {
+ /* Chipc rev 10 is InstaClock */
+ div = R_REG(si->osh, &cc->system_clk_ctl) >> SYCC_CD_SHIFT;
+ div = 4 * (div + 1);
+ return (max_freq ? XTALMAXFREQ : (XTALMINFREQ / div));
+ }
+ return (0);
+}
+
+static void BCMINITFN(sb_clkctl_setdelay) (sb_info_t * si, void *chipcregs) {
+ chipcregs_t *cc;
+ uint slowmaxfreq, pll_delay, slowclk;