/* * Switch to 'coreidx', issue a single arbitrary 32bit * register mask&set operation, * switch back to the original core, and return the new value. * * When using the silicon backplane, no fidleing with interrupts * or core switches are needed. * * Also, when using pci/pcie, we can optimize away the core switching * for pci registers * and (on newer pci cores) chipcommon registers. */ uint sb_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val) { uint origidx = 0; u32 *r = NULL; uint w; uint intr_val = 0; bool fast = false; si_info_t *sii; sii = SI_INFO(sih); ASSERT(GOODIDX(coreidx)); ASSERT(regoff < SI_CORE_SIZE); ASSERT((val & ~mask) == 0); if (coreidx >= SI_MAXCORES) return 0; if (!fast) { INTR_OFF(sii, intr_val); /* save current core index */ origidx = si_coreidx(&sii->pub); /* switch core */ r = (u32 *) ((unsigned char *) sb_setcoreidx(&sii->pub, coreidx) + regoff); } ASSERT(r != NULL); /* mask and set */ if (mask || val) { if (regoff >= SBCONFIGOFF) { w = (R_SBREG(sii, r) & ~mask) | val; W_SBREG(sii, r, w); } else { w = (R_REG(sii->osh, r) & ~mask) | val; W_REG(sii->osh, r, w); } } /* readback */ if (regoff >= SBCONFIGOFF) w = R_SBREG(sii, r); else w = R_REG(sii->osh, r); if (!fast) { /* restore core index */ if (origidx != coreidx) sb_setcoreidx(&sii->pub, origidx); INTR_RESTORE(sii, intr_val); } return w; }
static void __init pcibios_fixup_resources(struct pci_dev *dev) { ulong flags; uint coreidx; if (dev->bus->number == 0) { /* * Chipcommon, RAM controller and PCI bridge must not be reset! */ if (dev->device == SB_MIPS || dev->device == SB_MIPS33 || dev->device == SB_EXTIF || dev->device == SB_MEMC || dev->device == SB_PCI || dev->device == SB_CC) return; spin_lock_irqsave(&sbh_lock, flags); coreidx = sb_coreidx(sbh); if (!sb_setcoreidx(sbh, PCI_SLOT(dev->devfn))) return; /* * The USB core requires a special bit to be set during core * reset to enable host (OHCI) mode. Resetting the SB core here * is a hack for compatibility with vanilla usb-ohci so that it * does not have to know about SB. A driver that wants to use * the USB core in device mode should know about SB and should * reset the bit back to 0. */ if (sb_coreid(sbh) == SB_USB) { sb_core_disable(sbh, sb_coreflags(sbh, 0, 0)); sb_core_reset(sbh, 1 << 29); } else sb_core_reset(sbh, 0); sb_setcoreidx(sbh, coreidx); spin_unlock_irqrestore(&sbh_lock, flags); return; } if (dev->bus->number != 1 || PCI_SLOT(dev->devfn) != 0) return; printk("PCI: Fixing up bridge\n"); /* Enable PCI bridge bus mastering */ pci_set_master(dev); /* Enable PCI bridge BAR1 prefetch and burst */ pci_write_config_dword(dev, PCI_BAR1_CONTROL, 0x3); }
/* * Interface to register chipc secondary isr */ bool BCMINITFN(sb_cc_register_isr) (sb_t * sbh, cc_isr_fn isr, uint32 ccintmask, void *cbdata) { bool done = FALSE; chipcregs_t *regs; uint origidx; uint i; /* Save the current core index */ origidx = sb_coreidx(sbh); regs = sb_setcore(sbh, SB_CC, 0); ASSERT(regs); for (i = 0; i < MAX_CC_INT_SOURCE; i++) { if (cc_isr_desc[i].isr == NULL) { cc_isr_desc[i].isr = isr; cc_isr_desc[i].cbdata = cbdata; cc_isr_desc[i].intmask = ccintmask; done = TRUE; break; } } if (done) { cc_intmask = R_REG(sb_osh(sbh), ®s->intmask); cc_intmask |= ccintmask; W_REG(sb_osh(sbh), ®s->intmask, cc_intmask); } /* restore original coreidx */ sb_setcoreidx(sbh, origidx); return done; }
static uint32 config_cmd(sb_t *sbh, uint bus, uint dev, uint func, uint off) { uint coreidx; sbpciregs_t *regs; uint32 addr = 0; /* CardBusMode supports only one device */ if (cardbus && dev > 1) return 0; coreidx = sb_coreidx(sbh); regs = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0); /* Type 0 transaction */ if (bus == 1) { /* Skip unwired slots */ if (dev < PCI_SLOT_MAX) { /* Slide the PCI window to the appropriate slot */ W_REG(®s->sbtopci1, SBTOPCI_CFG0 | ((1 << (dev + 16)) & SBTOPCI1_MASK)); addr = SB_PCI_CFG | ((1 << (dev + 16)) & ~SBTOPCI1_MASK) | (func << 8) | (off & ~3); } } /* Type 1 transaction */ else { W_REG(®s->sbtopci1, SBTOPCI_CFG1); addr = SB_PCI_CFG | (bus << 16) | (dev << 11) | (func << 8) | (off & ~3); } sb_setcoreidx(sbh, coreidx); return addr; }
static int sb_write_config(sb_t *sbh, uint bus, uint dev, uint func, uint off, void *buf, int len) { uint coreidx, n; void *regs; sbconfig_t *sb; pci_config_regs *cfg; if (dev >= SB_MAXCORES || (off + len) > sizeof(pci_config_regs)) return -1; cfg = &sb_config_regs[dev]; ASSERT(ISALIGNED(off, len)); ASSERT(ISALIGNED((uintptr)buf, len)); /* Emulate BAR sizing */ if (off >= OFFSETOF(pci_config_regs, base[0]) && off <= OFFSETOF(pci_config_regs, base[3]) && len == 4 && *((uint32 *) buf) == ~0) { coreidx = sb_coreidx(sbh); if ((regs = sb_setcoreidx(sbh, dev))) { sb = (sbconfig_t *)((ulong) regs + SBCONFIGOFF); /* Highest numbered address match register */ n = (R_REG(&sb->sbidlow) & SBIDL_AR_MASK) >> SBIDL_AR_SHIFT; if (off == OFFSETOF(pci_config_regs, base[0])) cfg->base[0] = ~(sb_size(R_REG(&sb->sbadmatch0)) - 1); else if (off == OFFSETOF(pci_config_regs, base[1]) && n >= 1) cfg->base[1] = ~(sb_size(R_REG(&sb->sbadmatch1)) - 1); else if (off == OFFSETOF(pci_config_regs, base[2]) && n >= 2) cfg->base[2] = ~(sb_size(R_REG(&sb->sbadmatch2)) - 1); else if (off == OFFSETOF(pci_config_regs, base[3]) && n >= 3) cfg->base[3] = ~(sb_size(R_REG(&sb->sbadmatch3)) - 1); }
void * si_setcoreidx(si_t *sih, uint coreidx) { if (CHIPTYPE(sih->socitype) == SOCI_SB) return sb_setcoreidx(sih, coreidx); else if (CHIPTYPE(sih->socitype) == SOCI_AI) return ai_setcoreidx(sih, coreidx); else { ASSERT(0); return NULL; } }
/* get gpio registers base addr */ static uint32* get_gpio_base_addr(void) { sb_info_t *si; uint origidx; uint32 *addr = NULL; uint intr_val = 0; si = SB_INFO(sbh); INTR_OFF(si, intr_val); /* save current core index */ origidx = sb_coreidx(sbh); addr = (uint32*) sb_setcoreidx(sbh, si->gpioidx); /* restore core index */ if (origidx != si->gpioidx) sb_setcoreidx(sbh, origidx); INTR_RESTORE(si, intr_val); return addr; }
static uint32 config_cmd(sb_t *sbh, uint bus, uint dev, uint func, uint off) { uint coreidx; sbpciregs_t *regs; uint32 addr = 0; osl_t *osh; /* CardBusMode supports only one device */ if (cardbus && dev > 1) return 0; osh = sb_osh(sbh); coreidx = sb_coreidx(sbh); regs = (sbpciregs_t *) sb_setcore(sbh, SB_PCI, 0); /* Type 0 transaction */ if (bus == 1) { /* Skip unwired slots */ if (dev < PCI_SLOT_MAX) { uint32 win; /* Slide the PCI window to the appropriate slot */ win = (SBTOPCI_CFG0 | ((1 << (dev + PCI_SLOTAD_MAP)) & SBTOPCI1_MASK)); W_REG(osh, ®s->sbtopci1, win); addr = SB_PCI_CFG | ((1 << (dev + PCI_SLOTAD_MAP)) & ~SBTOPCI1_MASK) | (func << PCICFG_FUN_SHIFT) | (off & ~3); } } else { /* Type 1 transaction */ W_REG(osh, ®s->sbtopci1, SBTOPCI_CFG1); addr = SB_PCI_CFG | (bus << PCICFG_BUS_SHIFT) | (dev << PCICFG_SLOT_SHIFT) | (func << PCICFG_FUN_SHIFT) | (off & ~3); } sb_setcoreidx(sbh, coreidx); return addr; }
/* * This function changes logical "focus" to the indicated core; * must be called with interrupts off. * Moreover, callers should keep interrupts off during switching out of and back to d11 core */ void * si_setcore(si_t *sih, uint coreid, uint coreunit) { uint idx; idx = si_findcoreidx(sih, coreid, coreunit); if (!GOODIDX(idx)) return (NULL); if (CHIPTYPE(sih->socitype) == SOCI_SB) return sb_setcoreidx(sih, idx); else if (CHIPTYPE(sih->socitype) == SOCI_AI) return ai_setcoreidx(sih, idx); else { ASSERT(0); return NULL; } }
static bool nvram_reset(void *sbh) { chipcregs_t *cc; char *value; uint32 watchdog = 0, gpio; uint idx, msec; idx = sb_coreidx(sbh); /* Check if we were soft reset */ if ((cc = sb_setcore(sbh, SB_CC, 0))) { watchdog = R_REG(&cc->intstatus) & 0x80000000; sb_setcoreidx(sbh, idx); } if (watchdog) return FALSE; value = nvram_get("reset_gpio"); if (!value) return FALSE; gpio = (uint32) bcm_atoi(value); if (gpio > 7) return FALSE; /* Setup GPIO input */ sb_gpioouten(sbh, (1 << gpio), 0); /* GPIO reset is asserted low */ for (msec = 0; msec < 5000; msec++) { if (sb_gpioin(sbh) & (1 << gpio)) return FALSE; OSL_DELAY(1000); } return TRUE; }
int BCMINITFN(nvram_init)(void *sbh) { uint idx; bool isemb; int ret; idx = sb_coreidx(sbh); if (sb_setcore(sbh, SB_CC, 0) != NULL) { flash_base = SB_FLASH2; sb_setcoreidx(sbh, idx); } else flash_base = SB_FLASH1; /* Temporarily initialize with embedded NVRAM */ nvram_header = BCMINIT(find_nvram)(TRUE, &isemb); ret = BCMINIT(_nvram_init)(); if (ret == 0) { /* Restore defaults from embedded NVRAM if button held down */ if (BCMINIT(nvram_reset)(sbh)) { return 1; } BCMINIT(_nvram_exit)(); } /* Find NVRAM */ nvram_header = BCMINIT(find_nvram)(FALSE, &isemb); ret = BCMINIT(_nvram_init)(); if (ret == 0) { /* Restore defaults if embedded NVRAM used */ if (nvram_header && isemb) { ret = 1; } } return ret; }
/* * Setup the gige core. * Resetting the core will lose all settings. */ void sb_gige_init(sb_t *sbh, uint32 unit, bool *rgmii) { volatile pci_config_regs *pci; sbgige_pcishim_t *ocp; sbconfig_t *sb; osl_t *osh; uint32 statelow; uint32 statehigh; uint32 base; uint32 idx; void *regs; /* Sanity checks */ ASSERT(sbh); ASSERT(rgmii); idx = sb_coreidx(sbh); /* point to the gige core registers */ regs = sb_setcore(sbh, SB_GIGETH, unit); ASSERT(regs); osh = sb_osh(sbh); pci = &((sbgige_t *)regs)->pcicfg; ocp = &((sbgige_t *)regs)->pcishim; sb = &((sbgige_t *)regs)->sbconfig; /* Enable the core clock and memory access */ if (!sb_iscoreup(sbh)) sb_core_reset(sbh, 0, 0); /* * Setup the 64K memory-mapped region base address through BAR0. * Leave the other BAR values alone. */ base = sb_base(R_REG(osh, &sb->sbadmatch1)); W_REG(osh, &pci->base[0], base); W_REG(osh, &pci->base[1], 0); /* * Enable the PCI memory access anyway. Any PCI config commands * issued before the core is enabled will go to the emulation * only and will not go to the real PCI config registers. */ OR_REG(osh, &pci->command, 2); /* * Enable the posted write flush scheme as follows: * * - Enable flush on any core register read * - Enable timeout on the flush * - Disable the interrupt mask when flushing * * This differs from the default setting only in that interrupts are * not masked. Since posted writes are not flushed on interrupt, the * driver must explicitly request a flush in its interrupt handling * by reading a core register. */ W_REG(osh, &ocp->FlushStatusControl, 0x68); /* * Determine whether the GbE is in GMII or RGMII mode. This is * indicated in bit 16 of the SBTMStateHigh register, which is * part of the core-specific flags field. * * For GMII, bypass the Rx/Tx DLLs, i.e. add no delay to RXC/GTXC * within the core. For RGMII, do not bypass the DLLs, resulting * in added delay for RXC/GTXC. The SBTMStateLow register contains * the controls for doing this in the core-specific flags field: * * bit 24 - Enable DLL controls * bit 20 - Bypass Rx DLL * bit 19 - Bypass Tx DLL */ statelow = R_REG(osh, &sb->sbtmstatelow); /* DLL controls */ statehigh = R_REG(osh, &sb->sbtmstatehigh); /* GMII/RGMII mode */ if ((statehigh & (1 << 16)) != 0) /* RGMII */ { statelow &= ~(1 << 20); /* no Rx bypass (delay) */ statelow &= ~(1 << 19); /* no Tx bypass (delay) */ *rgmii = TRUE; } else /* GMII */ { statelow |= (1 << 20); /* Rx bypass (no delay) */ statelow |= (1 << 19); /* Tx bypass (no delay) */ *rgmii = FALSE; } statelow |= (1 << 24); /* enable DLL controls */ W_REG(osh, &sb->sbtmstatelow, statelow); sb_setcoreidx(sbh, idx); }
int pcibios_enable_device(struct pci_dev *dev, int mask) { ulong flags; uint coreidx; void *regs; /* External PCI device enable */ if (dev->bus->number != 0) return pcibios_enable_resources(dev); /* These cores come out of reset enabled */ if (dev->device == SB_MIPS || dev->device == SB_MIPS33 || dev->device == SB_EXTIF || dev->device == SB_CC) return 0; spin_lock_irqsave(&sbh_lock, flags); coreidx = sb_coreidx(sbh); regs = sb_setcoreidx(sbh, PCI_SLOT(dev->devfn)); if (!regs) return PCIBIOS_DEVICE_NOT_FOUND; /* * The USB core requires a special bit to be set during core * reset to enable host (OHCI) mode. Resetting the SB core in * pcibios_enable_device() is a hack for compatibility with * vanilla usb-ohci so that it does not have to know about * SB. A driver that wants to use the USB core in device mode * should know about SB and should reset the bit back to 0 * after calling pcibios_enable_device(). */ if (sb_coreid(sbh) == SB_USB) { sb_core_disable(sbh, sb_coreflags(sbh, 0, 0)); sb_core_reset(sbh, 1 << 29, 0); } /* * USB 2.0 special considerations: * * 1. Since the core supports both OHCI and EHCI functions, it must * only be reset once. * * 2. In addition to the standard SB reset sequence, the Host Control * Register must be programmed to bring the USB core and various * phy components out of reset. */ else if (sb_coreid(sbh) == SB_USB20H) { if (!sb_iscoreup(sbh)) { sb_core_reset(sbh, 0, 0); writel(0x7FF, (ulong)regs + 0x200); udelay(1); } } else sb_core_reset(sbh, 0, 0); sb_setcoreidx(sbh, coreidx); spin_unlock_irqrestore(&sbh_lock, flags); return 0; }