/* Initialize hash table. Should be locked. */ int BCMINITFN(_nvram_init)(void *sih) { struct nvram_header *header; int ret; if (!(header = (struct nvram_header *) MALLOC(si_osh(sih), NVRAM_SPACE))) { printf("nvram_init: out of memory\n"); return -12; /* -ENOMEM */ } #if 0 if ((ret = _nvram_read(header)) == 0 && header->magic == NVRAM_MAGIC) nvram_rehash(header); #else // nvram_rehash - may corrupt correct header _nvram_read(header); if (header->magic != NVRAM_MAGIC) nvram_rehash(header); #endif MFREE(si_osh(sih), header, NVRAM_SPACE); return ret; }
bool BCMATTACHFN(si_cc_register_isr)(si_t *sih, cc_isr_fn isr, uint32 ccintmask, void *cbdata) { bool done = FALSE; chipcregs_t *regs; uint origidx; uint i; /* Save the current core index */ origidx = si_coreidx(sih); regs = si_setcoreidx(sih, SI_CC_IDX); 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(si_osh(sih), ®s->intmask); cc_intmask |= ccintmask; W_REG(si_osh(sih), ®s->intmask, cc_intmask); } /* restore original coreidx */ si_setcoreidx(sih, origidx); return done; }
/* Erase a region. Returns number of bytes scheduled for erasure. * Caller should poll for completion. */ int sflash_erase(si_t *sih, chipcregs_t *cc, uint offset) { struct sflash *sfl; osl_t *osh; ASSERT(sih); osh = si_osh(sih); sfl = &sflash; if (offset >= sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: sflash_cmd(osh, cc, SFLASH_ST_WREN); W_REG(osh, &cc->sflashaddress, offset); sflash_cmd(osh, cc, SFLASH_ST_SE); return sfl->blocksize; case SFLASH_AT: W_REG(osh, &cc->sflashaddress, offset << 1); sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE); return sfl->blocksize; } return 0; }
/* Poll for command completion. Returns zero when complete. */ static int ccsflash_poll(hndsflash_t *sfl, uint offset) { si_t *sih = sfl->sih; chipcregs_t *cc = (chipcregs_t *)sfl->core; osl_t *osh; ASSERT(sih); osh = si_osh(sih); if (offset >= sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: /* Check for ST Write In Progress bit */ ccsflash_cmd(osh, cc, SFLASH_ST_RDSR); return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP; case SFLASH_AT: /* Check for Atmel Ready bit */ ccsflash_cmd(osh, cc, SFLASH_AT_STATUS); return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY); } return 0; }
uint pcie_writereg(si_t *sih, sbpcieregs_t *pcieregs, uint addrtype, uint offset, uint val) { osl_t *osh = si_osh(sih); ASSERT(pcieregs != NULL); BCM_REFERENCE(osh); if ((BUSTYPE(sih->bustype) == SI_BUS) || PCIE_GEN1(sih)) { switch (addrtype) { case PCIE_CONFIGREGS: W_REG(osh, (&pcieregs->configaddr), offset); W_REG(osh, (&pcieregs->configdata), val); break; case PCIE_PCIEREGS: W_REG(osh, (&pcieregs->u.pcie1.pcieindaddr), offset); W_REG(osh, (&pcieregs->u.pcie1.pcieinddata), val); break; default: ASSERT(0); break; } } else if (PCIE_GEN2(sih)) { W_REG(osh, (&pcieregs->configaddr), offset); W_REG(osh, (&pcieregs->configdata), val); } return 0; }
/* Poll for command completion. Returns zero when complete. */ int sflash_poll(si_t *sih, chipcregs_t *cc, uint offset) { osl_t *osh; ASSERT(sih); osh = si_osh(sih); if (offset >= sflash.size) return -22; switch (sflash.type) { case SFLASH_ST: /* Check for ST Write In Progress bit */ sflash_cmd(osh, cc, SFLASH_ST_RDSR); return R_REG(osh, &cc->flashdata) & SFLASH_ST_WIP; case SFLASH_AT: /* Check for Atmel Ready bit */ sflash_cmd(osh, cc, SFLASH_AT_STATUS); return !(R_REG(osh, &cc->flashdata) & SFLASH_AT_READY); } return 0; }
/* * Read host bridge PCI config registers from Silicon Backplane ( >= rev8 ). * * It returns TRUE to indicate that access to the host bridge's pci config * from SI is ok, and values in 'addr' and 'val' are valid. * * It can only read registers at multiple of 4-bytes. Callers must pick up * needed bytes from 'val' based on 'off' value. Value in 'addr' reflects * the register address where value in 'val' is read. */ static bool si_pcihb_read_config(si_t *sih, uint bus, uint dev, uint func, uint off, uint32 **addr, uint32 *val) { sbpciregs_t *pci; osl_t *osh; uint coreidx; bool ret = FALSE; uint8 port; /* sanity check */ ASSERT(PCIE_IS_BUS_HOST_BRIDGE(bus)); ASSERT(dev == pci_hbslot); /* we support only two functions on device 0 */ if (func > 1) return FALSE; /* Convert logical bus to physical port/bus for following processing */ port = PCIE_GET_PORT_BY_BUS(bus); osh = si_osh(sih); /* read pci config when core rev >= 8 */ coreidx = si_coreidx(sih); pci = (sbpciregs_t *)si_setcore(sih, PCI_CORE_ID, port); if (pci) { if (si_corerev(sih) >= PCI_HBSBCFG_REV) { *addr = (uint32 *)&pci->pcicfg[func][off >> 2]; *val = R_REG(osh, *addr); ret = TRUE; } } else {
/* * Read host bridge PCI config registers from Silicon Backplane ( >= rev8 ). * * It returns TRUE to indicate that access to the host bridge's pci config * from SI is ok, and values in 'addr' and 'val' are valid. * * It can only read registers at multiple of 4-bytes. Callers must pick up * needed bytes from 'val' based on 'off' value. Value in 'addr' reflects * the register address where value in 'val' is read. */ static bool si_pcihb_read_config(si_t *sih, uint coreunit, uint bus, uint dev, uint func, uint off, uint32 **addr, uint32 *val) { sbpciregs_t *pci; osl_t *osh; uint coreidx; bool ret = FALSE; /* sanity check */ ASSERT(hndpci_is_hostbridge(bus, dev)); /* we support only two functions on device 0 */ if (func > 1) return FALSE; osh = si_osh(sih); /* read pci config when core rev >= 8 */ coreidx = si_coreidx(sih); pci = (sbpciregs_t *)si_setcore(sih, PCI_CORE_ID, coreunit); if (pci) { if (si_corerev(sih) >= PCI_HBSBCFG_REV) { *addr = (uint32 *)&pci->pcicfg[func][off >> 2]; *val = R_REG(osh, *addr); ret = TRUE; } } else {
/* Erase a region. Returns number of bytes scheduled for erasure. * Caller should poll for completion. */ int sflash_erase(si_t *sih, chipcregs_t *cc, uint offset) { struct sflash *sfl; osl_t *osh; ASSERT(sih); osh = si_osh(sih); sfl = &sflash; if (offset >= sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: sflash_cmd(osh, cc, SFLASH_ST_WREN); W_REG(osh, &cc->flashaddress, offset); /* Newer flashes have "sub-sectors" which can be erased independently * with a new command: ST_SSE. The ST_SE command erases 64KB just as * before. */ sflash_cmd(osh, cc, (sfl->blocksize < (64 * 1024)) ? SFLASH_ST_SSE : SFLASH_ST_SE); return sfl->blocksize; case SFLASH_AT: W_REG(osh, &cc->flashaddress, offset << 1); sflash_cmd(osh, cc, SFLASH_AT_PAGE_ERASE); return sfl->blocksize; } return 0; }
/* Allocate private resource */ adm_info_t * adm_attach(si_t *sih, char *vars) { adm_info_t *adm; int gpio; /* Allocate private data */ if (!(adm = MALLOC(si_osh(sih), sizeof(adm_info_t)))) { ET_ERROR(("adm_attach: out of memory, malloc %d bytes", MALLOCED(si_osh(sih)))); return NULL; } bzero((char *) adm, sizeof(adm_info_t)); adm->sih = sih; adm->vars = vars; /* Init GPIO mapping. Default GPIO: 2, 3, 4 */ gpio = getgpiopin(vars, "adm_eecs", 2); ET_ERROR(("adm_attach: got %d as adm_eecs", gpio)); if (gpio == GPIO_PIN_NOTDEFINED) { ET_ERROR(("adm_attach: adm_eecs gpio fail: GPIO 2 in use")); goto error; } adm->eecs = 1 << gpio; gpio = getgpiopin(vars, "adm_eesk", 3); ET_ERROR(("adm_attach: got %d as adm_eesk", gpio)); if (gpio == GPIO_PIN_NOTDEFINED) { ET_ERROR(("adm_attach: adm_eesk gpio fail: GPIO 3 in use")); goto error; } adm->eesk = 1 << gpio; gpio = getgpiopin(vars, "adm_eedi", 4); ET_ERROR(("adm_attach: got %d as adm_eedi", gpio)); if (gpio == GPIO_PIN_NOTDEFINED) { ET_ERROR(("adm_attach: adm_eedi gpio fail: GPIO 4 in use")); goto error; } adm->eedi = 1 << gpio; return adm; error: adm_detach(adm); return NULL; }
/* Read len bytes starting at offset into buf. Returns number of bytes read. */ int sflash_read(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf) { uint8 *from, *to; int cnt, i; osl_t *osh; ASSERT(sih); if (!len) return 0; if ((offset + len) > sflash.size) return -22; if ((len >= 4) && (offset & 3)) cnt = 4 - (offset & 3); else if ((len >= 4) && ((uintptr)buf & 3)) cnt = 4 - ((uintptr)buf & 3); else cnt = len; osh = si_osh(sih); if (sih->ccrev == 12) from = (uint8 *)OSL_UNCACHED((void *)SI_FLASH2 + offset); else from = (uint8 *)OSL_CACHED((void *)SI_FLASH2 + offset); to = (uint8 *)buf; if (cnt < 4) { for (i = 0; i < cnt; i ++) { /* Cannot use R_REG because in bigendian that will * xor the address and we don't want that here. */ *to = *from; from ++; to ++; } return cnt; } while (cnt >= 4) { *(uint32 *)to = *(uint32 *)from; from += 4; to += 4; cnt -= 4; } return (len - cnt); }
/* Read len bytes starting at offset into buf. Returns number of bytes read. */ int sflash_read(si_t *sih, chipcregs_t *cc, uint offset, uint len, uchar *buf) { uint8 *from, *to; int cnt, i; osl_t *osh; ASSERT(sih); if (!len) return 0; if ((offset + len) > sflash.size) return -22; if ((len >= 4) && (offset & 3)) cnt = 4 - (offset & 3); else if ((len >= 4) && ((uintptr)buf & 3)) cnt = 4 - ((uintptr)buf & 3); else cnt = len; osh = si_osh(sih); from = (uint8 *)OSL_UNCACHED(SI_FLASH2 + offset); to = (uint8 *)buf; if (cnt < 4) { for (i = 0; i < cnt; i ++) { *to = R_REG(osh, from); from ++; to ++; } return cnt; } while (cnt >= 4) { *(uint32 *)to = R_REG(osh, (uint32 *)from); from += 4; to += 4; cnt -= 4; } return (len - cnt); }
/* * Initialize jtag master and return handle for * jtag_rwreg. Returns NULL on failure. */ void * hnd_jtagm_init(si_t *sih, uint clkd, bool exttap) { void *regs; osl_t *osh; osh = si_osh(sih); if ((regs = si_setcoreidx(sih, SI_CC_IDX)) != NULL) { chipcregs_t *cc = (chipcregs_t *) regs; uint32 tmp; /* * Determine jtagm availability from * core revision and capabilities. */ /* * Corerev 10 has jtagm, but the only chip * with it does not have a mips, and * the layout of the jtagcmd register is * different. We'll only accept >= 11. */ if (sih->ccrev < 11) return (NULL); if ((sih->cccaps & CC_CAP_JTAGP) == 0) return (NULL); /* Set clock divider if requested */ if (clkd != 0) { tmp = R_REG(osh, &cc->clkdiv); tmp = (tmp & ~CLKD_JTAG) | ((clkd << CLKD_JTAG_SHIFT) & CLKD_JTAG); W_REG(osh, &cc->clkdiv, tmp); } /* Enable jtagm */ tmp = JCTRL_EN | (exttap ? JCTRL_EXT_EN : 0); W_REG(osh, &cc->jtagctrl, tmp); } return (regs); }
void si_cc_isr(si_t *sih, chipcregs_t *regs) { uint32 ccintstatus; uint32 intstatus; uint32 i; /* prior to rev 21 chipc interrupt means uart and gpio */ if (sih->ccrev >= 21) ccintstatus = R_REG(si_osh(sih), ®s->intstatus) & cc_intmask; else ccintstatus = (CI_UART | CI_GPIO); for (i = 0; i < MAX_CC_INT_SOURCE; i ++) { if ((cc_isr_desc[i].isr != NULL) && (intstatus = (cc_isr_desc[i].intmask & ccintstatus))) { (cc_isr_desc[i].isr)(cc_isr_desc[i].cbdata, intstatus); } } }
void si_cc_isr(si_t *sih, chipcregs_t *regs) { uint32 ccintstatus; uint32 intstatus; uint32 i; cc_isr_info_t *desc; /* prior to rev 21 chipc interrupt means uart and gpio */ if (sih->ccrev >= 21) ccintstatus = R_REG(si_osh(sih), ®s->intstatus) & cc_intmask; else ccintstatus = (CI_UART | CI_GPIO); desc = get_cc_isr_desc(); ASSERT(desc); for (i = 0; i < MAX_CC_INT_SOURCE; i++, desc++) { if ((desc->isr != NULL) && (intstatus = (desc->intmask & ccintstatus))) { (desc->isr)(desc->cbdata, intstatus); } } }
/* * Initializes UART access. The callback function will be called once * per found UART. */ void BCMATTACHFN(si_serial_init)(si_t *sih, si_serial_init_fn add) { osl_t *osh; void *regs; chipcregs_t *cc; uint32 rev, cap, pll, baud_base, div; uint irq; int i, n; osh = si_osh(sih); cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX); ASSERT(cc); /* Determine core revision and capabilities */ rev = sih->ccrev; cap = sih->cccaps; pll = cap & CC_CAP_PLL_MASK; /* Determine IRQ */ irq = si_irq(sih); if (CCPLL_ENAB(sih) && pll == PLL_TYPE1) { /* PLL clock */ baud_base = si_clock_rate(pll, R_REG(osh, &cc->clockcontrol_n), R_REG(osh, &cc->clockcontrol_m2)); div = 1; } else { /* Fixed ALP clock */ if (rev >= 11 && rev != 15) { baud_base = si_alp_clock(sih); div = 1; /* Turn off UART clock before switching clock source */ if (rev >= 21) AND_REG(osh, &cc->corecontrol, ~CC_UARTCLKEN); /* Set the override bit so we don't divide it */ OR_REG(osh, &cc->corecontrol, CC_UARTCLKO); if (rev >= 21) OR_REG(osh, &cc->corecontrol, CC_UARTCLKEN); } else if (rev >= 3) { /* Internal backplane clock */ baud_base = si_clock(sih); div = 2; /* Minimum divisor */ W_REG(osh, &cc->clkdiv, ((R_REG(osh, &cc->clkdiv) & ~CLKD_UART) | div)); } else { /* Fixed internal backplane clock */ baud_base = 88000000; div = 48; } /* Clock source depends on strapping if UartClkOverride is unset */ if ((R_REG(osh, &cc->corecontrol) & CC_UARTCLKO) == 0) { if ((cap & CC_CAP_UCLKSEL) == CC_CAP_UINTCLK) { /* Internal divided backplane clock */ baud_base /= div; } else { /* Assume external clock of 1.8432 MHz */ baud_base = 1843200; } } } /* Add internal UARTs */ n = cap & CC_CAP_UARTS_MASK; for (i = 0; i < n; i++) { regs = (void *)((ulong) &cc->uart0data + (i * 256)); if (add) add(regs, irq, baud_base, 0); } }
int sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer) { struct sflash *sfl; uint off = offset, len = length; const uint8 *buf = buffer; uint8 data; int ret = 0, ntry = 0; bool is4712b0; uint32 page, byte, mask; osl_t *osh; ASSERT(sih); osh = si_osh(sih); if (!len) return 0; sfl = &sflash; if ((off + len) > sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: is4712b0 = (CHIPID(sih->chip) == BCM4712_CHIP_ID) && (CHIPREV(sih->chiprev) == 3); /* Enable writes */ retry: sflash_cmd(osh, cc, SFLASH_ST_WREN); off = offset; len = length; buf = buffer; ntry++; if (is4712b0) { mask = 1 << 14; W_REG(osh, &cc->flashaddress, off); data = GET_BYTE(buf); buf++; W_REG(osh, &cc->flashdata, data); /* Set chip select */ OR_REG(osh, &cc->gpioout, mask); /* Issue a page program with the first byte */ sflash_cmd(osh, cc, SFLASH_ST_PP); ret = 1; off++; len--; while (len > 0) { if ((off & 255) == 0) { /* Page boundary, drop cs and return */ AND_REG(osh, &cc->gpioout, ~mask); OSL_DELAY(1); if (!sflash_poll(sih, cc, off)) { /* Flash rejected command */ if (ntry <= ST_RETRIES) goto retry; else return -11; } return ret; } else { /* Write single byte */ data = GET_BYTE(buf); buf++; sflash_cmd(osh, cc, data); } ret++; off++; len--; } /* All done, drop cs */ AND_REG(osh, &cc->gpioout, ~mask); OSL_DELAY(1); if (!sflash_poll(sih, cc, off)) { /* Flash rejected command */ if (ntry <= ST_RETRIES) goto retry; else return -12; } } else if (sih->ccrev >= 20) { W_REG(osh, &cc->flashaddress, off); data = GET_BYTE(buf); buf++; W_REG(osh, &cc->flashdata, data); /* Issue a page program with CSA bit set */ sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); ret = 1; off++; len--; while (len > 0) { if ((off & 255) == 0) { /* Page boundary, poll droping cs and return */ W_REG(NULL, &cc->flashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, off) == 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, ntry: %d," " off: %d/%d, len: %d/%d, ret:" "%d\n", ntry, off, offset, len, length, ret)); if (ntry <= ST_RETRIES) goto retry; else return -11; } return ret; } else { /* Write single byte */ data = GET_BYTE(buf); buf++; sflash_cmd(osh, cc, SFLASH_ST_CSA | data); } ret++; off++; len--; } /* All done, drop cs & poll */ W_REG(NULL, &cc->flashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, off) == 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, ntry: %d, off: %d/%d," " len: %d/%d, ret: %d\n", ntry, off, offset, len, length, ret)); if (ntry <= ST_RETRIES) goto retry; else return -12; } } else { ret = 1; W_REG(osh, &cc->flashaddress, off); data = GET_BYTE(buf); buf++; W_REG(osh, &cc->flashdata, data); /* Page program */ sflash_cmd(osh, cc, SFLASH_ST_PP); } break; case SFLASH_AT: mask = sfl->blocksize - 1; page = (off & ~mask) << 1; byte = off & mask; /* Read main memory page into buffer 1 */ if (byte || (len < sfl->blocksize)) { W_REG(osh, &cc->flashaddress, page); sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD); /* 250 us for AT45DB321B */ SPINWAIT(sflash_poll(sih, cc, off), 1000); ASSERT(!sflash_poll(sih, cc, off)); } /* Write into buffer 1 */ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { W_REG(osh, &cc->flashaddress, byte++); W_REG(osh, &cc->flashdata, *buf++); sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE); } /* Write buffer 1 into main memory page */ W_REG(osh, &cc->flashaddress, page); sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM); break; } return ret; }
static uint32 config_cmd(si_t *sih, uint bus, uint dev, uint func, uint off) { uint coreidx; sbpciregs_t *pci; uint32 addr = 0, *sbtopci1; osl_t *osh; uint8 port; /* CardBusMode supports only one device */ if (cardbus && dev > 1) return 0; /* Convert logical bus to physical port/bus for following processing */ port = PCIE_GET_PORT_BY_BUS(bus); if (port == 1) { /* 1-based bus number */ bus = bus - PCIE_PORT1_BUS_START + 1; } osh = si_osh(sih); coreidx = si_coreidx(sih); pci = (sbpciregs_t *)si_setcore(sih, PCI_CORE_ID, port); if (pci) { sbtopci1 = &pci->sbtopci1; } else { sbpcieregs_t *pcie; pcie = (sbpcieregs_t *)si_setcore(sih, PCIE_CORE_ID, port); /* Issue config commands only when the data link is up (atleast * one external pcie device is present). */ if (pcie && (dev < 2) && (pcie_readreg(osh, pcie, PCIE_PCIEREGS, PCIE_DLLP_LSREG) & PCIE_DLLP_LSREG_LINKUP)) { sbtopci1 = &pcie->sbtopcie1; } else { si_setcoreidx(sih, coreidx); return 0; } } /* Type 0 transaction */ if (bus == 1) { /* Skip unwired slots */ if (dev < PCI_SLOT_MAX) { /* Slide the PCI window to the appropriate slot */ if (pci) { uint32 win; win = (SBTOPCI_CFG0 | ((1 << (dev + PCI_SLOTAD_MAP)) & SBTOPCI1_MASK)); W_REG(osh, sbtopci1, win); addr = (SI_PCI_CFG(port) | ((1 << (dev + PCI_SLOTAD_MAP)) & ~SBTOPCI1_MASK) | (func << PCICFG_FUN_SHIFT) | (off & ~3)); } else { W_REG(osh, sbtopci1, SBTOPCI_CFG0); addr = (SI_PCI_CFG(port) | (dev << PCIECFG_SLOT_SHIFT) | (func << PCIECFG_FUN_SHIFT) | (off & ~3)); } } } else { /* Type 1 transaction */ addr = SI_PCI_CFG(port); if (pci) { addr |= PCI_CONFIG_ADDR(bus, dev, func, (off & ~3)); /* * Higher bus bits (which lets the address exceed the 64MB space) * should be specified in sbtopci1's UpperAddress. */ W_REG(osh, sbtopci1, SBTOPCI_CFG1 | (addr & SBTOPCI1_MASK)); /* To not exceed the 64MB space (by using UA instead) */ addr &= ~SBTOPCI1_MASK; } else { addr |= PCIE_CONFIG_ADDR((bus - 1), dev, func, (off & ~3)); /* * Higher bus bits (which lets the address exceed the 64MB space) * should be specified in sbtopci1's UpperAddress. */ W_REG(osh, sbtopci1, SBTOPCI_CFG1 | (addr & SBTOPCIE1_MASK)); /* To not exceed the 64MB space (by using UA instead) */ addr &= ~SBTOPCIE1_MASK; } } si_setcoreidx(sih, coreidx); return addr; }
static int dev_nvram_init(void) { int order = 0, ret = 0; struct page *page, *end; osl_t *osh; #if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE) unsigned int i; #endif /* Allocate and reserve memory to mmap() */ while ((PAGE_SIZE << order) < nvram_space) order++; end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(nvram_buf); page <= end; page++) { SetPageReserved(page); } #if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE) /* Find associated MTD device */ for (i = 0; i < MAX_MTD_DEVICES; i++) { nvram_mtd = get_mtd_device(NULL, i); if (!IS_ERR(nvram_mtd)) { if (!strcmp(nvram_mtd->name, "nvram") && nvram_mtd->size >= nvram_space) { break; } put_mtd_device(nvram_mtd); } } if (i >= MAX_MTD_DEVICES) nvram_mtd = NULL; #endif /* Initialize hash table lock */ spin_lock_init(&nvram_lock); /* Initialize commit semaphore */ init_MUTEX(&nvram_sem); /* Register char device */ if ((nvram_major = register_chrdev(0, "nvram", &dev_nvram_fops)) < 0) { ret = nvram_major; goto err; } if (si_osh(sih) == NULL) { osh = osl_attach(NULL, SI_BUS, FALSE); if (osh == NULL) { printk("Error allocating osh\n"); unregister_chrdev(nvram_major, "nvram"); goto err; } si_setosh(sih, osh); } /* Initialize hash table */ _nvram_init(sih); /* Create /dev/nvram handle */ nvram_class = class_create(THIS_MODULE, "nvram"); if (IS_ERR(nvram_class)) { printk("Error creating nvram class\n"); goto err; } /* Add the device nvram0 */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) class_device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram"); #else /* Linux 2.6.36 and above */ device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram"); #endif /* Linux 2.6.36 */ return 0; err: dev_nvram_exit(); return ret; }
#endif static char *findvar(char *vars_arg, char *lim, const char *name); extern void nvram_get_global_vars(char **varlst, uint *varsz); static char *nvram_get_internal(const char *name); static int nvram_getall_internal(char *buf, int count); static void #if defined(WLTEST) || !defined(WLC_HIGH) sortvars(si_t *sih, vars_t *new) #else BCMATTACHFN(sortvars)(si_t *sih, vars_t *new) #endif { osl_t *osh = si_osh(sih); char *s = new->vars; int i; char *temp; char *c, *cend; uint8 *lp; /* * Sort the variables by length. Everything len NUM_VISZES or * greater is dumped into the end of the area * in no particular order. The search algorithm can then * restrict the search to just those variables that are the * proper length. */ /* get a temp array to hold the sorted vars */
/* Initialize serial flash access */ struct sflash * sflash_init(si_t *sih, chipcregs_t *cc) { uint32 id; osl_t *osh; uint32 mem_type, mem_cap; ASSERT(sih); osh = si_osh(sih); bzero(&sflash, sizeof(sflash)); sflash.type = sih->cccaps & CC_CAP_FLASH_MASK; switch (sflash.type) { case SFLASH_ST: /* Probe for ST chips */ sflash_cmd(osh, cc, (SFLASH_ST_RDID | SFLASH_ST_CSA)); sflash_cmd(osh, cc, (SFLASH_ST_RDID_READ | SFLASH_ST_CSA)); id = R_REG(osh, &cc->sflashdata); sflash_cmd(osh, cc, (SFLASH_ST_RDID_READ | SFLASH_ST_CSA)); mem_type = R_REG(osh, &cc->sflashdata); sflash_cmd(osh, cc, (SFLASH_ST_RDID_READ | SFLASH_ST_CSA)); mem_cap = R_REG(osh, &cc->sflashdata); switch (id) { case 0x20: /* Micron */ switch(mem_type) { case 0x20: /* M25P series */ case 0xba: /* N25Q series */ switch (mem_cap) { case 0x12: /* ST M25P20 2 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 4; break; case 0x13: /* ST M25P40 4 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 8; break; case 0x14: /* ST M25P80 8 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 16; break; case 0x15: /* ST M25P16 16 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 32; break; case 0x16: /* ST M25P32 32 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 64; break; case 0x17: /* ST M25P64 64 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 128; break; case 0x18: /* ST M25P 128 Mbit Serial Flash */ sflash.blocksize = 64 * 1024; sflash.numblocks = 256; break; } break; } break; case 0x01: /* Spansion */ if ((mem_type == 0x20) && (mem_cap == 0x18)) { /* ST S25FL128P00M 128 Mbit */ sflash.blocksize = 64 * 1024; sflash.numblocks = 256; } else if ((mem_type == 0x02) && (mem_cap == 0x15)) { /* ST S25FL032P */ sflash.blocksize = 64 * 1024; sflash.numblocks = 64; } break; case 0xbf: /* SST */ if ((mem_type == 0x25) && (mem_cap == 0x8e)) { /* SST 25VF80 */ sflash.blocksize = 64 * 1024; sflash.numblocks = 8; } break; } /* All done, drop cs & read */ W_REG(osh, &cc->sflashcontrol, 0); break; case SFLASH_AT: /* Probe for Atmel chips */ sflash_cmd(osh, cc, SFLASH_AT_STATUS); id = R_REG(osh, &cc->sflashdata) & 0x3c; switch (id) { case 0xc: /* Atmel AT45DB011 1Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 512; break; case 0x14: /* Atmel AT45DB021 2Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 1024; break; case 0x1c: /* Atmel AT45DB041 4Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 2048; break; case 0x24: /* Atmel AT45DB081 8Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 4096; break; case 0x2c: /* Atmel AT45DB161 16Mbit Serial Flash */ sflash.blocksize = 512; sflash.numblocks = 4096; break; case 0x34: /* Atmel AT45DB321 32Mbit Serial Flash */ sflash.blocksize = 512; sflash.numblocks = 8192; break; case 0x3c: /* Atmel AT45DB642 64Mbit Serial Flash */ sflash.blocksize = 1024; sflash.numblocks = 8192; break; } break; } sflash.size = sflash.blocksize * sflash.numblocks; return sflash.size ? &sflash : NULL; }
/* Initialize serial flash access */ hndsflash_t * ccsflash_init(si_t *sih) { chipcregs_t *cc; uint32 id, id2; const char *name = ""; osl_t *osh; ASSERT(sih); /* No sflash for NorthStar */ if (sih->ccrev == 42) return NULL; if ((cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX)) == NULL) return NULL; if (!firsttime && ccsflash.size != 0) return &ccsflash; osh = si_osh(sih); bzero(&ccsflash, sizeof(ccsflash)); ccsflash.sih = sih; ccsflash.core = (void *)cc; ccsflash.read = ccsflash_read; ccsflash.write = ccsflash_write; ccsflash.erase = ccsflash_erase; ccsflash.commit = ccsflash_commit; ccsflash.poll = ccsflash_poll; ccsflash.type = sih->cccaps & CC_CAP_FLASH_MASK; switch (ccsflash.type) { case SFLASH_ST: /* Probe for ST chips */ name = "ST compatible"; ccsflash_cmd(osh, cc, SFLASH_ST_DP); W_REG(osh, &cc->flashaddress, 0); ccsflash_cmd(osh, cc, SFLASH_ST_RES); id = R_REG(osh, &cc->flashdata); ccsflash.blocksize = 64 * 1024; switch (id) { case 0x11: /* ST M25P20 2 Mbit Serial Flash */ ccsflash.numblocks = 4; break; case 0x12: /* ST M25P40 4 Mbit Serial Flash */ ccsflash.numblocks = 8; break; case 0x13: ccsflash_cmd(osh, cc, SFLASH_MXIC_RDID); id = R_REG(osh, &cc->flashdata); if (id == SFLASH_MXIC_MFID) { /* MXIC MX25L8006E 8 Mbit Serial Flash */ ccsflash.blocksize = 4 * 1024; ccsflash.numblocks = 16 * 16; } else { /* ST M25P80 8 Mbit Serial Flash */ ccsflash.numblocks = 16; } break; case 0x14: /* ST M25P16 16 Mbit Serial Flash */ ccsflash.numblocks = 32; break; case 0x15: /* ST M25P32 32 Mbit Serial Flash */ ccsflash.numblocks = 64; break; case 0x16: /* ST M25P64 64 Mbit Serial Flash */ ccsflash.numblocks = 128; break; case 0x17: /* ST M25FL128 128 Mbit Serial Flash */ ccsflash.numblocks = 256; break; case 0xbf: /* All of the following flashes are SST with * 4KB subsectors. Others should be added but * We'll have to revamp the way we identify them * since RES is not eough to disambiguate them. */ name = "SST"; ccsflash.blocksize = 4 * 1024; W_REG(osh, &cc->flashaddress, 1); ccsflash_cmd(osh, cc, SFLASH_ST_RES); id2 = R_REG(osh, &cc->flashdata); switch (id2) { case 1: /* SST25WF512 512 Kbit Serial Flash */ ccsflash.numblocks = 16; break; case 0x48: /* SST25VF512 512 Kbit Serial Flash */ ccsflash.numblocks = 16; break; case 2: /* SST25WF010 1 Mbit Serial Flash */ ccsflash.numblocks = 32; break; case 0x49: /* SST25VF010 1 Mbit Serial Flash */ ccsflash.numblocks = 32; break; case 3: /* SST25WF020 2 Mbit Serial Flash */ ccsflash.numblocks = 64; break; case 0x43: /* SST25VF020 2 Mbit Serial Flash */ ccsflash.numblocks = 64; break; case 4: /* SST25WF040 4 Mbit Serial Flash */ ccsflash.numblocks = 128; break; case 0x44: /* SST25VF040 4 Mbit Serial Flash */ ccsflash.numblocks = 128; break; case 0x8d: /* SST25VF040B 4 Mbit Serial Flash */ ccsflash.numblocks = 128; break; case 5: /* SST25WF080 8 Mbit Serial Flash */ ccsflash.numblocks = 256; break; case 0x8e: /* SST25VF080B 8 Mbit Serial Flash */ ccsflash.numblocks = 256; break; case 0x41: /* SST25VF016 16 Mbit Serial Flash */ ccsflash.numblocks = 512; break; case 0x4a: /* SST25VF032 32 Mbit Serial Flash */ ccsflash.numblocks = 1024; break; case 0x4b: /* SST25VF064 64 Mbit Serial Flash */ ccsflash.numblocks = 2048; break; } break; } break; case SFLASH_AT: /* Probe for Atmel chips */ name = "Atmel"; ccsflash_cmd(osh, cc, SFLASH_AT_STATUS); id = R_REG(osh, &cc->flashdata) & 0x3c; switch (id) { case 0xc: /* Atmel AT45DB011 1Mbit Serial Flash */ ccsflash.blocksize = 256; ccsflash.numblocks = 512; break; case 0x14: /* Atmel AT45DB021 2Mbit Serial Flash */ ccsflash.blocksize = 256; ccsflash.numblocks = 1024; break; case 0x1c: /* Atmel AT45DB041 4Mbit Serial Flash */ ccsflash.blocksize = 256; ccsflash.numblocks = 2048; break; case 0x24: /* Atmel AT45DB081 8Mbit Serial Flash */ ccsflash.blocksize = 256; ccsflash.numblocks = 4096; break; case 0x2c: /* Atmel AT45DB161 16Mbit Serial Flash */ ccsflash.blocksize = 512; ccsflash.numblocks = 4096; break; case 0x34: /* Atmel AT45DB321 32Mbit Serial Flash */ ccsflash.blocksize = 512; ccsflash.numblocks = 8192; break; case 0x3c: /* Atmel AT45DB642 64Mbit Serial Flash */ ccsflash.blocksize = 1024; ccsflash.numblocks = 8192; break; } break; } ccsflash.size = ccsflash.blocksize * ccsflash.numblocks; ccsflash.phybase = SI_FLASH2; if (firsttime) printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n", name, ccsflash.numblocks, ccsflash.blocksize / 1024, ccsflash.size / (1024 * 1024)); firsttime = FALSE; return ccsflash.size ? &ccsflash : NULL; }
int sflash_write(si_t *sih, chipcregs_t *cc, uint offset, uint length, const uchar *buffer) { struct sflash *sfl; uint off = offset, len = length; #if SFLASH_ST_PAGE_MODE_WRITE int tryn = 0; #else /* !SFLASH_ST_PAGE_MODE_WRITE */ uint quot = 0, remain = 0, wlen = 0; uint32 reg_val = 0; #endif /* SFLASH_ST_PAGE_MODE_WRITE */ const uchar *buf = buffer; int ret = 0; uint32 page, byte, mask; osl_t *osh; ASSERT(sih); osh = si_osh(sih); if (!len) return 0; sfl = &sflash; if ((off + len) > sfl->size) return -22; switch (sfl->type) { case SFLASH_ST: #if SFLASH_ST_PAGE_MODE_WRITE /* Enable writes */ retry: sflash_cmd(osh, cc, SFLASH_ST_WREN); off = offset; len = length; buf = buffer; tryn++; if (sih->ccrev >= 20) { W_REG(osh, &cc->sflashaddress, off); W_REG(osh, &cc->sflashdata, *buf++); /* Issue a page program with CSA bit set */ sflash_cmd(osh, cc, SFLASH_ST_CSA | SFLASH_ST_PP); ret = 1; off++; len--; while (len > 0) { if ((off & 255) == 0) { /* Page boundary, poll droping cs and return */ W_REG(osh, &cc->sflashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, offset) != 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, tryn: %d," " off: %d/%d, len: %d/%d, ret:" "%d\n", tryn, off, offset, len, length, ret)); if (tryn <= ST_RETRIES) goto retry; else return -11; } return ret; } else { /* Write single byte */ sflash_cmd(osh, cc, SFLASH_ST_CSA | *buf++); } ret++; off++; len--; } /* All done, drop cs & poll */ W_REG(osh, &cc->sflashcontrol, 0); OSL_DELAY(1); if (sflash_poll(sih, cc, offset) != 0) { /* Flash rejected command */ SFL_MSG(("sflash: pp rejected, tryn: %d, off: %d/%d," " len: %d/%d, ret: %d\n", tryn, off, offset, len, length, ret)); if (tryn <= ST_RETRIES) goto retry; else return -12; } } else { ret = 1; W_REG(osh, &cc->sflashaddress, off); W_REG(osh, &cc->sflashdata, *buf); /* Page program */ sflash_cmd(osh, cc, SFLASH_ST_PP); } #else /* !SFLASH_ST_PAGE_MODE_WRITE */ off = offset; len = length; buf = buffer; if (sih->ccrev >= 20) { ret = 0; while (len > 0) { /* Enable writes */ sflash_cmd(osh, cc, SFLASH_ST_WREN); /* Drop cs before starting a page program */ W_REG(osh, &cc->sflashcontrol, 0); W_REG(osh, &cc->sflashaddress, off); quot = (len / 4); remain = (len % 4); if (quot != 0) { /* len >= 4 bytes */ wlen = 4; reg_val = (*buf << 24); buf++; reg_val |= (*buf << 16); buf++; reg_val |= (*buf << 8); buf++; reg_val |= (*buf); buf++; W_REG(osh, &cc->sflashdata, reg_val); /* Issue a page program with CSA bit set : opcode+3 addres & 4 data bytes */ sflash_cmd(osh, cc, (SFLASH_ST_CSA | SFLASH_ST_PP3A4D)); } else { /* len < 4 bytes */ wlen = 1; W_REG(osh, &cc->sflashdata, *buf++); /* Issue a page program with CSA bit set : opcode+3 addres & 1 data bytes */ sflash_cmd(osh, cc, (SFLASH_ST_CSA | SFLASH_ST_PP)); } ret += wlen; off += wlen; len -= wlen; /* A page program done(1 or 4 data bytes), drop cs & poll */ W_REG(osh, &cc->sflashcontrol, 0); while (sflash_poll(sih, cc, offset) != 0) { /* Poll until command completion */ } /* Page boundary and return for 256 bytes write */ if ((off & 255) == 0) { return ret; } } } else { /* Enable writes */ sflash_cmd(osh, cc, SFLASH_ST_WREN); ret = 1; W_REG(osh, &cc->sflashaddress, off); W_REG(osh, &cc->sflashdata, *buf); /* Page program */ sflash_cmd(osh, cc, SFLASH_ST_PP); } #endif /* SFLASH_ST_PAGE_MODE_WRITE */ break; case SFLASH_AT: mask = sfl->blocksize - 1; page = (off & ~mask) << 1; byte = off & mask; /* Read main memory page into buffer 1 */ if (byte || (len < sfl->blocksize)) { W_REG(osh, &cc->sflashaddress, page); sflash_cmd(osh, cc, SFLASH_AT_BUF1_LOAD); /* 250 us for AT45DB321B */ SPINWAIT(sflash_poll(sih, cc, offset), 1000); ASSERT(!sflash_poll(sih, cc, offset)); } /* Write into buffer 1 */ for (ret = 0; (ret < (int)len) && (byte < sfl->blocksize); ret++) { W_REG(osh, &cc->sflashaddress, byte++); W_REG(osh, &cc->sflashdata, *buf++); sflash_cmd(osh, cc, SFLASH_AT_BUF1_WRITE); } /* Write buffer 1 into main memory page */ W_REG(osh, &cc->sflashaddress, page); sflash_cmd(osh, cc, SFLASH_AT_BUF1_PROGRAM); break; } return ret; }
int flashDrvLibInit(void) { FLASH_TYPES dev; FLASH_VENDORS vendor; int i; uint32 fltype = PFLASH; osl_t *osh; struct sflash *sflash; if (flashDrvInitialized) return (OK); flashBaseAddress = FLASH_BASE_ADDRESS_ALIAS; /* * Check for serial flash. */ sih = si_kattach(SI_OSH); ASSERT(sih); osh = si_osh(sih); cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX); ASSERT(cc); /* Select SFLASH */ fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK; if (fltype == SFLASH_ST || fltype == SFLASH_AT) { sflash = sflash_init(sih, cc); if (sflash == NULL) { printf("flashInit(): Unrecognized Device (SFLASH)\n"); return (ERROR); } flashDrvFuncs = &flashsflash; flashDrvFuncs->flashAutoSelect(&dev, &vendor); flashSectorCount = sflash->numblocks; flashDevSectorSize = sflash->blocksize; if (flashVerbose) { printf("flashInit(): SFLASH Found\n"); } } else { flashDrvFuncs = &flashs29gl256p; flashDrvFuncs->flashAutoSelect(&dev, &vendor); if ((vendor == 0xFF) && (dev == 0xFF)) { flashDrvFuncs = &flash29gl128; flashDrvFuncs->flashAutoSelect(&dev, &vendor); } if ((vendor == 0xFF) && (dev == 0xFF)) { flashDrvFuncs = &flash29l640; flashDrvFuncs->flashAutoSelect(&dev, &vendor); } if ((vendor == 0xFF) && (dev == 0xFF)) { flashDrvFuncs = &flash29l320; flashDrvFuncs->flashAutoSelect(&dev, &vendor); } if ((vendor == 0xFF) && (dev == 0xFF)) { flashDrvFuncs = &flash29l160; flashDrvFuncs->flashAutoSelect(&dev, &vendor); } if ((vendor == 0xFF) && (dev == 0xFF)) { flashDrvFuncs = &flash28f320; flashDrvFuncs->flashAutoSelect(&dev, &vendor); } switch (vendor) { case AMD: case ALLIANCE: case MXIC: switch (dev) { case FLASH_2F040: flashSectorCount = 8; flashDevSectorSize = 0x10000; if (flashVerbose) printf("flashInit(): 2F040 Found\n"); break; case FLASH_2F080: flashSectorCount = 16; flashDevSectorSize = 0x10000; if (flashVerbose) printf("flashInit(): 2F080 Found\n"); break; case FLASH_2L081: flashSectorCount = 16; flashDevSectorSize = 0x10000; if (flashVerbose) printf("flashInit(): 29LV081B Found\n"); break; case FLASH_2L160: case FLASH_2L017: flashSectorCount = 32; flashDevSectorSize = 0x10000; if (flashVerbose) printf("flashInit(): 29LV160D Found\n"); break; case FLASH_2L640: case FLASH_MX2L640: flashSectorCount = 128; flashDevSectorSize = 0x10000; if (flashVerbose) printf("flashInit(): 29LV640M Found\n"); break; case FLASH_29GL128: /* Spansion 29GL128 is physically 128 sector count * To make flash support backward compatible to old device, * only use 64 for 8MB */ flashSectorCount = 64; flashDevSectorSize = 0x20000; if (flashVerbose) printf("flashInit(): 29GL128 Found\n"); break; case FLASH_2L320: flashSectorCount = 64; flashDevSectorSize = 0x10000; if (flashVerbose) printf("flashInit(): 29LV320D Found\n"); break; case FLASH_S29GL128P: flashSectorCount = 128; flashDevSectorSize = 0x20000; if (flashVerbose) printf("flashInit(): FLASH_S29GL128P Found\n"); break; case FLASH_S29GL256P: flashSectorCount = 256; flashDevSectorSize = 0x20000; if (flashVerbose) printf("flashInit(): S29GL256P Found\n"); break; case FLASH_S29GL512P: flashSectorCount = 512; flashDevSectorSize = 0x20000; if (flashVerbose) printf("flashInit(): FLASH_S29GL512P Found\n"); break; case FLASH_S29GL01GP: flashSectorCount = 1024; flashDevSectorSize = 0x20000; if (flashVerbose) printf("flashInit(): FLASH_S29GL01GP Found\n"); break; default: printf("flashInit(): Unrecognized Device (0x%02X)\n", dev); return (ERROR); } break; case INTEL: switch (dev) { case FLASH_2F320: flashSectorCount = 32; flashDevSectorSize = 0x20000; if (flashVerbose) printf("flashInit(): 28F320 Found\n"); break; default: printf("flashInit(): Unrecognized Device (0x%02X)\n", dev); return (ERROR); } break; default: printf("flashInit(): Unrecognized Vendor (0x%02X)\n", vendor); return (ERROR); } } flashSize = flashDevSectorSize * flashSectorCount; for (i = 0; i < TOTAL_LOADED_SECS; i++) { flashLoadedSectors[i].buffer = malloc(FLASH_SECTOR_SIZE); if (flashLoadedSectors[i].buffer == NULL) { printf("flashInit(): malloc() failed\n"); for (; i > 0; i--) { free(flashLoadedSectors[i-1].buffer); } return (ERROR); } flashLoadedSectors[i].sector = -1; flashLoadedSectors[i].dirty = 0; flashLoadedSectors[i].fsSemID = semMCreate (SEM_Q_PRIORITY | SEM_DELETE_SAFE); } flashDrvInitialized ++; return (OK); }
static int dev_nvram_init(void) { int order = 0, ret = 0; struct page *page, *end; unsigned int i; osl_t *osh; /* Allocate and reserve memory to mmap() */ while ((PAGE_SIZE << order) < nvram_space) order++; end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(nvram_buf); page <= end; page++) { SetPageReserved(page); } #if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE) /* Find associated MTD device */ for (i = 0; i < MAX_MTD_DEVICES; i++) { nvram_mtd = get_mtd_device(NULL, i); if (!IS_ERR(nvram_mtd)) { if (!strcmp(nvram_mtd->name, "nvram") && nvram_mtd->size >= nvram_space) { break; } put_mtd_device(nvram_mtd); } } if (i >= MAX_MTD_DEVICES) nvram_mtd = NULL; #endif /* Initialize hash table lock */ spin_lock_init(&nvram_lock); /* Initialize commit semaphore */ init_MUTEX(&nvram_sem); /* Register char device */ if ((nvram_major = register_chrdev(0, "nvram", &dev_nvram_fops)) < 0) { ret = nvram_major; goto err; } if (si_osh(sih) == NULL) { osh = osl_attach(NULL, SI_BUS, FALSE); if (osh == NULL) { printk("Error allocating osh\n"); unregister_chrdev(nvram_major, "nvram"); goto err; } si_setosh(sih, osh); } printk("dev_nvram_init: _nvram_init\n"); /* Initialize hash table */ _nvram_init((void *)sih); /* Create /dev/nvram handle */ nvram_class = class_create(THIS_MODULE, "nvram"); if (IS_ERR(nvram_class)) { printk("Error creating nvram class\n"); goto err; } /* Add the device nvram0 */ class_device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram"); /* reserve commit read buffer */ /* Backup sector blocks to be erased */ if (!(nvram_commit_buf = kmalloc(ROUNDUP(nvram_space, nvram_mtd->erasesize), GFP_KERNEL))) { printk("dev_nvram_init: nvram_commit_buf out of memory\n"); goto err; } /* Set the SDRAM NCDL value into NVRAM if not already done */ if (getintvar(NULL, "sdram_ncdl") == 0) { unsigned int ncdl; char buf[] = "0x00000000"; if ((ncdl = si_memc_get_ncdl(sih))) { sprintf(buf, "0x%08x", ncdl); nvram_set("sdram_ncdl", buf); nvram_commit(); } } return 0; err: dev_nvram_exit(); return ret; }
static int dev_nvram_init(void) { int order = 0, ret = 0; struct page *page, *end; unsigned int i; osl_t *osh; /* Allocate and reserve memory to mmap() */ while ((PAGE_SIZE << order) < NVRAM_SPACE) order++; end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1); for (page = virt_to_page(nvram_buf); page <= end; page++) { SetPageReserved(page); } #if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE) /* Find associated MTD device */ for (i = 0; i < MAX_MTD_DEVICES; i++) { nvram_mtd = get_mtd_device(NULL, i); if (!IS_ERR(nvram_mtd)) { if (!strcmp(nvram_mtd->name, "nvram") && nvram_mtd->size >= NVRAM_SPACE) { break; } put_mtd_device(nvram_mtd); } } if (i >= MAX_MTD_DEVICES) nvram_mtd = NULL; #endif #ifdef RTN66U_NVRAM_64K_SUPPORT int ret32; char *log_buf; u_int32_t offset_t; size_t log_len; DECLARE_WAITQUEUE(wait, current); wait_queue_head_t wait_q; struct erase_info erase; offset_t = 0x18000; ret32 = nvram_mtd->read(nvram_mtd, offset_t, 4, &log_len, &log_buf); if(log_buf==0xffffffff) { /* Erase sector blocks */ init_waitqueue_head(&wait_q); erase.mtd = nvram_mtd; erase.addr = 0; erase.len = nvram_mtd->erasesize; erase.callback = erase_callback; erase.priv = (u_long) &wait_q; set_current_state(TASK_INTERRUPTIBLE); add_wait_queue(&wait_q, &wait); /* Unlock sector blocks */ if (nvram_mtd->unlock) nvram_mtd->unlock(nvram_mtd, 0, nvram_mtd->erasesize); if ((ret = nvram_mtd->erase(nvram_mtd, &erase))) { set_current_state(TASK_RUNNING); remove_wait_queue(&wait_q, &wait); printk("nvram mtd erase error\n"); } /* Wait for erase to finish */ schedule(); remove_wait_queue(&wait_q, &wait); } #endif /* Initialize hash table lock */ spin_lock_init(&nvram_lock); /* Initialize commit semaphore */ init_MUTEX(&nvram_sem); /* Register char device */ if ((nvram_major = register_chrdev(0, "nvram", &dev_nvram_fops)) < 0) { ret = nvram_major; goto err; } if (si_osh(sih) == NULL) { osh = osl_attach(NULL, SI_BUS, FALSE); if (osh == NULL) { printk("Error allocating osh\n"); unregister_chrdev(nvram_major, "nvram"); goto err; } si_setosh(sih, osh); } /* Initialize hash table */ _nvram_init(sih); /* Create /dev/nvram handle */ nvram_class = class_create(THIS_MODULE, "nvram"); if (IS_ERR(nvram_class)) { printk("Error creating nvram class\n"); goto err; } /* Add the device nvram0 */ class_device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram"); /* reserve commit read buffer */ /* Backup sector blocks to be erased */ if (!(nvram_commit_buf = kmalloc(ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize), GFP_KERNEL))) { printk("dev_nvram_init: nvram_commit_buf out of memory\n"); goto err; } /* Set the SDRAM NCDL value into NVRAM if not already done */ if (getintvar(NULL, "sdram_ncdl") == 0) { unsigned int ncdl; char buf[] = "0x00000000"; if ((ncdl = si_memc_get_ncdl(sih))) { sprintf(buf, "0x%08x", ncdl); nvram_set("sdram_ncdl", buf); nvram_commit(); } } return 0; err: dev_nvram_exit(); return ret; }
/* Read the flash ID and set the globals */ int sysFlashInit(char *flash_str) { osl_t *osh; uint32 fltype = PFLASH; uint16 flash_vendid = 0; uint16 flash_devid = 0; int idx; struct sflash *sflash; /* * Check for serial flash. */ sih = si_kattach(SI_OSH); ASSERT(sih); osh = si_osh(sih); cc = (chipcregs_t *)si_setcoreidx(sih, SI_CC_IDX); ASSERT(cc); flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2); /* Select SFLASH ? */ fltype = R_REG(osh, &cc->capabilities) & CC_CAP_FLASH_MASK; if (fltype == SFLASH_ST || fltype == SFLASH_AT) { if (sih->ccrev == 12) flashutl_base = (void *)OSL_UNCACHED((uintptr)SI_FLASH2); else flashutl_base = (void *)OSL_CACHED((uintptr)SI_FLASH2); sflash = sflash_init(sih, cc); flashutl_cmd = &sflash_cmd_t; flashutl_desc = &sflash_desc; flashutl_desc->size = sflash->size; if (flash_str) sprintf(flash_str, "SFLASH %d kB", sflash->size/1024); return (0); } flashutl_wsz = (R_REG(osh, &cc->flash_config) & CC_CFG_DS) ? sizeof(uint16) : sizeof(uint8); ASSERT(flashutl_wsz == sizeof(uint8) || flashutl_wsz == sizeof(uint16)); /* * Parallel flash support * Some flashes have different unlock addresses, try each it turn */ for (idx = 0; fltype == PFLASH && idx < ARRAYSIZE(flash_cmds); idx ++) { flashutl_cmd = &flash_cmds[idx]; if (flashutl_cmd->type == OLD) continue; if (flashutl_cmd->read_id) { cmd(flashutl_cmd->read_id, CMD_ADDR); /* Delay for turn around time */ OSL_DELAY(1); } #ifdef MIPSEB #ifdef BCMHND74K flash_vendid = flash_readword(FLASH_ADDR(0)^6); flash_devid = flash_readword(FLASH_ADDR(2)^6); #else /* !74K, bcm33xx */ flash_vendid = flash_readword(FLASH_ADDR(2)); flash_devid = flash_readword(FLASH_ADDR(0)); #endif /* BCMHND74K */ #else flash_vendid = flash_readword(FLASH_ADDR(0)); flash_devid = flash_readword(FLASH_ADDR(2)); #endif /* MIPSEB */ /* Funky AMD, uses 3 byte device ID so use first byte (4th addr) to * identify it is a 3-byte ID and use the next two bytes (5th & 6th addr) * to form a word for unique identification of format xxyy, where * xx = 5th addr and yy = 6th addr */ if ((flash_vendid == 1) && ((flash_devid == 0x227e && flashutl_wsz == sizeof(uint16)) || (flash_devid == 0x7e && flashutl_wsz == sizeof(uint8)))) { /* Get real devid */ uint16 flash_devid_5th; #ifdef MIPSEB #ifdef BCMHND74K flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)^6) << 8; flash_devid = (flash_readword(FLASH_ADDR(0x1e)^6) & 0xff) | flash_devid_5th; #else /* !74K, bcm33xx */ flash_devid_5th = flash_readword(FLASH_ADDR(0x1e)) << 8; flash_devid = (flash_readword(FLASH_ADDR(0x1c)) & 0xff) | flash_devid_5th; #endif /* BCMHND74K */ #else flash_devid_5th = flash_readword(FLASH_ADDR(0x1c)) << 8; flash_devid = (flash_readword(FLASH_ADDR(0x1e)) & 0xff) | flash_devid_5th; #endif /* MIPSEB */ } flashutl_desc = flashes; while (flashutl_desc->mfgid != 0 && !(flashutl_desc->mfgid == flash_vendid && flashutl_desc->devid == flash_devid)) { flashutl_desc++; } if (flashutl_desc->mfgid != 0) break; } if (flashutl_desc->mfgid == 0) { flashutl_desc = NULL; flashutl_cmd = NULL; } else { flashutl_cmd = flash_cmds; while (flashutl_cmd->type != 0 && flashutl_cmd->type != flashutl_desc->type) flashutl_cmd++; if (flashutl_cmd->type == 0) flashutl_cmd = NULL; } if (flashutl_cmd != NULL) { flash_reset(); } if (flashutl_desc == NULL) { if (flash_str) sprintf(flash_str, "UNKNOWN 0x%x 0x%x", flash_vendid, flash_devid); DPRINT(("Flash type UNKNOWN\n")); return 1; } if (flash_str) strcpy(flash_str, flashutl_desc->desc); DPRINT(("Flash type \"%s\"\n", flashutl_desc->desc)); return 0; }
/* Initialize serial flash access */ struct sflash * sflash_init(si_t *sih, chipcregs_t *cc) { uint32 id, id2; char *name = ""; osl_t *osh; ASSERT(sih); osh = si_osh(sih); bzero(&sflash, sizeof(sflash)); sflash.type = sih->cccaps & CC_CAP_FLASH_MASK; switch (sflash.type) { case SFLASH_ST: /* Probe for ST chips */ name = "ST compatible"; sflash_cmd(osh, cc, SFLASH_ST_DP); W_REG(osh, &cc->flashaddress, 0); sflash_cmd(osh, cc, SFLASH_ST_RES); id = R_REG(osh, &cc->flashdata); sflash.blocksize = 64 * 1024; switch (id) { case 0x11: /* ST M25P20 2 Mbit Serial Flash */ sflash.numblocks = 4; break; case 0x12: /* ST M25P40 4 Mbit Serial Flash */ sflash.numblocks = 8; break; case 0x13: /* ST M25P80 8 Mbit Serial Flash */ sflash.numblocks = 16; break; case 0x14: /* ST M25P16 16 Mbit Serial Flash */ sflash.numblocks = 32; break; case 0x15: /* ST M25P32 32 Mbit Serial Flash */ sflash.numblocks = 64; break; case 0x16: /* ST M25P64 64 Mbit Serial Flash */ sflash.numblocks = 128; break; case 0x17: /* ST M25FL128 128 Mbit Serial Flash */ sflash.numblocks = 256; break; case 0xbf: /* All of the following flashes are SST with * 4KB subsectors. Others should be added but * We'll have to revamp the way we identify them * since RES is not eough to disambiguate them. */ name = "SST"; sflash.blocksize = 4 * 1024; W_REG(osh, &cc->flashaddress, 1); sflash_cmd(osh, cc, SFLASH_ST_RES); id2 = R_REG(osh, &cc->flashdata); switch (id2) { case 1: /* SST25WF512 512 Kbit Serial Flash */ sflash.numblocks = 16; break; case 0x48: /* SST25VF512 512 Kbit Serial Flash */ sflash.numblocks = 16; break; case 2: /* SST25WF010 1 Mbit Serial Flash */ sflash.numblocks = 32; break; case 0x49: /* SST25VF010 1 Mbit Serial Flash */ sflash.numblocks = 32; break; case 3: /* SST25WF020 2 Mbit Serial Flash */ sflash.numblocks = 64; break; case 0x43: /* SST25VF020 2 Mbit Serial Flash */ sflash.numblocks = 64; break; case 4: /* SST25WF040 4 Mbit Serial Flash */ sflash.numblocks = 128; break; case 0x44: /* SST25VF040 4 Mbit Serial Flash */ sflash.numblocks = 128; break; case 0x8d: /* SST25VF040B 4 Mbit Serial Flash */ sflash.numblocks = 128; break; case 5: /* SST25WF080 8 Mbit Serial Flash */ sflash.numblocks = 256; break; case 0x8e: /* SST25VF080B 8 Mbit Serial Flash */ sflash.numblocks = 256; break; case 0x41: /* SST25VF016 16 Mbit Serial Flash */ sflash.numblocks = 512; break; case 0x4a: /* SST25VF032 32 Mbit Serial Flash */ sflash.numblocks = 1024; break; case 0x4b: /* SST25VF064 64 Mbit Serial Flash */ sflash.numblocks = 2048; break; } break; } break; case SFLASH_AT: /* Probe for Atmel chips */ name = "Atmel"; sflash_cmd(osh, cc, SFLASH_AT_STATUS); id = R_REG(osh, &cc->flashdata) & 0x3c; switch (id) { case 0xc: /* Atmel AT45DB011 1Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 512; break; case 0x14: /* Atmel AT45DB021 2Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 1024; break; case 0x1c: /* Atmel AT45DB041 4Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 2048; break; case 0x24: /* Atmel AT45DB081 8Mbit Serial Flash */ sflash.blocksize = 256; sflash.numblocks = 4096; break; case 0x2c: /* Atmel AT45DB161 16Mbit Serial Flash */ sflash.blocksize = 512; sflash.numblocks = 4096; break; case 0x34: /* Atmel AT45DB321 32Mbit Serial Flash */ sflash.blocksize = 512; sflash.numblocks = 8192; break; case 0x3c: /* Atmel AT45DB642 64Mbit Serial Flash */ sflash.blocksize = 1024; sflash.numblocks = 8192; break; } break; } sflash.size = sflash.blocksize * sflash.numblocks; if (firsttime) printf("Found an %s serial flash with %d %dKB blocks; total size %dMB\n", name, sflash.numblocks, sflash.blocksize / 1024, sflash.size / (1024 * 1024)); firsttime = FALSE; return sflash.size ? &sflash : NULL; }
/* * writes the appropriate range of flash, a NULL buf simply erases * the region of flash */ int sflash_commit(si_t *sih, chipcregs_t *cc, uint offset, uint len, const uchar *buf) { struct sflash *sfl; uchar *block = NULL, *cur_ptr, *blk_ptr; uint blocksize = 0, mask, cur_offset, cur_length, cur_retlen, remainder; uint blk_offset, blk_len, copied; int bytes, ret = 0; osl_t *osh; ASSERT(sih); osh = si_osh(sih); /* Check address range */ if (len <= 0) return 0; sfl = &sflash; if ((offset + len) > sfl->size) return -1; blocksize = sfl->blocksize; mask = blocksize - 1; /* Allocate a block of mem */ if (!(block = MALLOC(osh, blocksize))) return -1; while (len) { /* Align offset */ cur_offset = offset & ~mask; cur_length = blocksize; cur_ptr = block; remainder = blocksize - (offset & mask); if (len < remainder) cur_retlen = len; else cur_retlen = remainder; /* buf == NULL means erase only */ if (buf) { /* Copy existing data into holding block if necessary */ if ((offset & mask) || (len < blocksize)) { blk_offset = cur_offset; blk_len = cur_length; blk_ptr = cur_ptr; /* Copy entire block */ while (blk_len) { copied = sflash_read(sih, cc, blk_offset, blk_len, blk_ptr); blk_offset += copied; blk_len -= copied; blk_ptr += copied; } } /* Copy input data into holding block */ memcpy(cur_ptr + (offset & mask), buf, cur_retlen); } /* Erase block */ if ((ret = sflash_erase(sih, cc, (uint) cur_offset)) < 0) goto done; while (sflash_poll(sih, cc, (uint) cur_offset)); /* buf == NULL means erase only */ if (!buf) { offset += cur_retlen; len -= cur_retlen; continue; } /* Write holding block */ while (cur_length > 0) { if ((bytes = sflash_write(sih, cc, (uint) cur_offset, (uint) cur_length, (uchar *) cur_ptr)) < 0) { ret = bytes; goto done; } while (sflash_poll(sih, cc, (uint) cur_offset)); cur_offset += bytes; cur_length -= bytes; cur_ptr += bytes; } offset += cur_retlen; len -= cur_retlen; buf += cur_retlen; } ret = len; done: if (block) MFREE(osh, block, blocksize); return ret; }
/* * Setup the gige core. * Resetting the core will lose all settings. */ void hndgige_init(si_t *sih, 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(sih); ASSERT(rgmii); idx = si_coreidx(sih); /* point to the gige core registers */ regs = si_setcore(sih, GIGETH_CORE_ID, unit); ASSERT(regs); osh = si_osh(sih); pci = &((sbgige_t *)regs)->pcicfg; ocp = &((sbgige_t *)regs)->pcishim; sb = &((sbgige_t *)regs)->sbconfig; /* Enable the core clock and memory access */ if (!si_iscoreup(sih)) si_core_reset(sih, 0, 0); /* * Setup the 64K memory-mapped region base address through BAR0. * Leave the other BAR values alone. */ base = si_addrspace(sih, 1); 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); si_setcoreidx(sih, idx); }