📄 sbutils.c
字号:
/* * Misc utility routines for accessing chip-specific features * of the SiliconBackplane-based Broadcom chips. * * Copyright 2005-2006, Broadcom Corporation * All Rights Reserved. * * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE. * $Id$ */#include <typedefs.h>#include <bcmdefs.h>#include <osl.h>#include <sbutils.h>#include <bcmutils.h>#include <bcmdevs.h>#include <sbconfig.h>#include <sbchipc.h>#include <sbpci.h>#include <sbpcie.h>#include <pcicfg.h>#include <bcmsrom.h>/* debug/trace */#define SB_ERROR(args)typedef uint32 (*sb_intrsoff_t)(void *intr_arg);typedef void (*sb_intrsrestore_t)(void *intr_arg, uint32 arg);typedef bool (*sb_intrsenabled_t)(void *intr_arg);/* misc sb info needed by some of the routines */typedef struct sb_info { struct sb_pub sb; /* back plane public state (must be first field) */ void *osh; /* osl os handle */ void *sdh; /* bcmsdh handle */ void *curmap; /* current regs va */ void *regs[SB_MAXCORES]; /* other regs va */ uint curidx; /* current core index */ uint dev_coreid; /* the core provides driver functions */ bool memseg; /* flag to toggle MEM_SEG register */ uint gpioidx; /* gpio control core index */ uint gpioid; /* gpio control coretype */ uint numcores; /* # discovered cores */ uint coreid[SB_MAXCORES]; /* id of each core */ void *intr_arg; /* interrupt callback function arg */ sb_intrsoff_t intrsoff_fn; /* turns chip interrupts off */ sb_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */ sb_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */} sb_info_t;/* local prototypes */static sb_info_t * sb_doattach(sb_info_t *si, uint devid, osl_t *osh, void *regs, uint bustype, void *sdh, char **vars, uint *varsz);static void sb_scan(sb_info_t *si);static uint sb_corereg(sb_info_t *si, uint coreidx, uint regoff, uint mask, uint val);static uint _sb_coreidx(sb_info_t *si);static uint sb_findcoreidx(sb_info_t *si, uint coreid, uint coreunit);static bool sb_ispcie(sb_info_t *si);static bool sb_find_pci_capability(sb_info_t *si, uint8 req_cap_id, uchar *buf, uint32 *buflen);static int sb_pci_fixcfg(sb_info_t *si);/* routines to access mdio slave device registers */static int sb_pcie_mdiowrite(sb_info_t *si, uint physmedia, uint readdr, uint val);static void sb_war30841(sb_info_t *si);/* delay needed between the mdio control/ mdiodata register data access */#define PR28829_DELAY() OSL_DELAY(10)/* global variable to indicate reservation/release of gpio's */static uint32 sb_gpioreservation = 0;#define SB_INFO(sbh) (sb_info_t*)sbh#define SET_SBREG(si, r, mask, val) \ W_SBREG((si), (r), ((R_SBREG((si), (r)) & ~(mask)) | (val)))#define GOODCOREADDR(x) (((x) >= SB_ENUM_BASE) && ((x) <= SB_ENUM_LIM) && \ ISALIGNED((x), SB_CORE_SIZE))#define GOODREGS(regs) ((regs) && ISALIGNED((uintptr)(regs), SB_CORE_SIZE))#define REGS2SB(va) (sbconfig_t*) ((int8*)(va) + SBCONFIGOFF)#define GOODIDX(idx) (((uint)idx) < SB_MAXCORES)#define BADIDX (SB_MAXCORES+1)#define NOREV -1 /* Invalid rev */#define PCI(si) ((BUSTYPE(si->sb.bustype) == PCI_BUS) && (si->sb.buscoretype == SB_PCI))#define PCIE(si) ((BUSTYPE(si->sb.bustype) == PCI_BUS) && (si->sb.buscoretype == SB_PCIE))/* sonicsrev */#define SONICS_2_2 (SBIDL_RV_2_2 >> SBIDL_RV_SHIFT)#define SONICS_2_3 (SBIDL_RV_2_3 >> SBIDL_RV_SHIFT)#define R_SBREG(si, sbr) sb_read_sbreg((si), (sbr))#define W_SBREG(si, sbr, v) sb_write_sbreg((si), (sbr), (v))#define AND_SBREG(si, sbr, v) W_SBREG((si), (sbr), (R_SBREG((si), (sbr)) & (v)))#define OR_SBREG(si, sbr, v) W_SBREG((si), (sbr), (R_SBREG((si), (sbr)) | (v)))/* * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts before/ * after core switching to avoid invalid register accesss inside ISR. */#define INTR_OFF(si, intr_val) \ if ((si)->intrsoff_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ intr_val = (*(si)->intrsoff_fn)((si)->intr_arg); }#define INTR_RESTORE(si, intr_val) \ if ((si)->intrsrestore_fn && (si)->coreid[(si)->curidx] == (si)->dev_coreid) { \ (*(si)->intrsrestore_fn)((si)->intr_arg, intr_val); }/* dynamic clock control defines */#define LPOMINFREQ 25000 /* low power oscillator min */#define LPOMAXFREQ 43000 /* low power oscillator max */#define XTALMINFREQ 19800000 /* 20 MHz - 1% */#define XTALMAXFREQ 20200000 /* 20 MHz + 1% */#define PCIMINFREQ 25000000 /* 25 MHz */#define PCIMAXFREQ 34000000 /* 33 MHz + fudge */#define ILP_DIV_5MHZ 0 /* ILP = 5 MHz */#define ILP_DIV_1MHZ 4 /* ILP = 1 MHz */#define MIN_DUMPBUFLEN 32 /* debug *//* different register spaces to access thr'u pcie indirect access */#define PCIE_CONFIGREGS 1 /* Access to config space */#define PCIE_PCIEREGS 2 /* Access to pcie registers *//* GPIO Based LED powersave defines */#define DEFAULT_GPIO_ONTIME 10 /* Default: 10% on */#define DEFAULT_GPIO_OFFTIME 90 /* Default: 10% on */#define DEFAULT_GPIOTIMERVAL ((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME)static uint32sb_read_sbreg(sb_info_t *si, volatile uint32 *sbr){ return (R_REG(sbr));}static voidsb_write_sbreg(sb_info_t *si, volatile uint32 *sbr, uint32 v){ W_REG(sbr, v);}/* * Allocate a sb handle. * devid - pci device id (used to determine chip#) * osh - opaque OS handle * regs - virtual address of initial core registers * bustype - pci/pcmcia/sb/sdio/etc * vars - pointer to a pointer area for "environment" variables * varsz - pointer to int to return the size of the vars */sb_t *BCMINITFN(sb_attach)(uint devid, osl_t *osh, void *regs, uint bustype, void *sdh, char **vars, int *varsz){ sb_info_t *si; /* alloc sb_info_t */ if ((si = MALLOC(osh, sizeof (sb_info_t))) == NULL) { SB_ERROR(("sb_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh))); return (NULL); } if (sb_doattach(si, devid, osh, regs, bustype, sdh, vars, (uint*)varsz) == NULL) { MFREE(osh, si, sizeof(sb_info_t)); return (NULL); } return (sb_t *)si;}static sb_info_t *BCMINITFN(sb_doattach)(sb_info_t *si, uint devid, osl_t *osh, void *regs, uint bustype, void *sdh, char **vars, uint *varsz){ uint origidx; chipcregs_t *cc; sbconfig_t *sb; uint32 w; ASSERT(GOODREGS(regs)); bzero((uchar*)si, sizeof(sb_info_t)); si->sb.buscoreidx = si->gpioidx = BADIDX; si->osh = osh; si->curmap = regs; si->sdh = sdh; /* check to see if we are a sb core mimic'ing a pci core */ if (bustype == PCI_BUS) { if (OSL_PCI_READ_CONFIG(osh, PCI_SPROM_CONTROL, sizeof (uint32)) == 0xffffffff) { SB_ERROR(("%s: incoming bus is PCI but it's a lie, switching to SB " "devid:0x%x\n", __FUNCTION__, devid)); bustype = SB_BUS; } } si->sb.bustype = bustype; if (si->sb.bustype != BUSTYPE(si->sb.bustype)) { SB_ERROR(("sb_doattach: bus type %d does not match configured bus type %d\n", si->sb.bustype, BUSTYPE(si->sb.bustype))); return NULL; } /* kludge to enable the clock on the 4306 which lacks a slowclock */ if (BUSTYPE(si->sb.bustype) == PCI_BUS) sb_clkctl_xtal(&si->sb, XTAL|PLL, ON); if (BUSTYPE(si->sb.bustype) == PCI_BUS) { w = OSL_PCI_READ_CONFIG(osh, PCI_BAR0_WIN, sizeof(uint32)); if (!GOODCOREADDR(w)) OSL_PCI_WRITE_CONFIG(si->osh, PCI_BAR0_WIN, sizeof(uint32), SB_ENUM_BASE); } /* initialize current core index value */ si->curidx = _sb_coreidx(si); if (si->curidx == BADIDX) { SB_ERROR(("sb_doattach: bad core index\n")); return NULL; } /* get sonics backplane revision */ sb = REGS2SB(si->curmap); si->sb.sonicsrev = (R_SBREG(si, &sb->sbidlow) & SBIDL_RV_MASK) >> SBIDL_RV_SHIFT; /* keep and reuse the initial register mapping */ origidx = si->curidx; if (BUSTYPE(si->sb.bustype) == SB_BUS) si->regs[origidx] = regs; /* is core-0 a chipcommon core? */ si->numcores = 1; cc = (chipcregs_t*) sb_setcoreidx(&si->sb, 0); if (sb_coreid(&si->sb) != SB_CC) cc = NULL; /* determine chip id and rev */ if (cc) { /* chip common core found! */ si->sb.chip = R_REG(&cc->chipid) & CID_ID_MASK; si->sb.chiprev = (R_REG(&cc->chipid) & CID_REV_MASK) >> CID_REV_SHIFT; si->sb.chippkg = (R_REG(&cc->chipid) & CID_PKG_MASK) >> CID_PKG_SHIFT; } else return NULL; /* get chipcommon rev */ si->sb.ccrev = cc ? (int)sb_corerev(&si->sb) : NOREV; /* determine numcores */ if (cc && ((si->sb.ccrev == 4) || (si->sb.ccrev >= 6))) si->numcores = (R_REG(&cc->chipid) & CID_CC_MASK) >> CID_CC_SHIFT; /* return to original core */ sb_setcoreidx(&si->sb, origidx); /* sanity checks */ ASSERT(si->sb.chip); /* scan for cores */ sb_scan(si); /* fixup necessary chip/core configurations */ if (BUSTYPE(si->sb.bustype) == PCI_BUS) { if (sb_pci_fixcfg(si)) { SB_ERROR(("sb_doattach: sb_pci_fixcfg failed\n")); return NULL; } } /* srom_var_init() depends on sb_scan() info */ if (srom_var_init(si, si->sb.bustype, si->curmap, osh, vars, varsz)) { SB_ERROR(("sb_doattach: srom_var_init failed: bad srom\n")); return (NULL); } if (cc == NULL) { /* * The chip revision number is hardwired into all * of the pci function config rev fields and is * independent from the individual core revision numbers. * For example, the "A0" silicon of each chip is chip rev 0. */ if (BUSTYPE(si->sb.bustype) == PCI_BUS) { w = OSL_PCI_READ_CONFIG(osh, PCI_CFG_REV, sizeof(uint32)); si->sb.chiprev = w & 0xff; } else si->sb.chiprev = 0; } /* gpio control core is required */ if (!GOODIDX(si->gpioidx)) { SB_ERROR(("sb_doattach: gpio control core not found\n")); return NULL; } /* get boardtype and boardrev */ switch (BUSTYPE(si->sb.bustype)) { case PCI_BUS: /* do a pci config read to get subsystem id and subvendor id */ w = OSL_PCI_READ_CONFIG(osh, PCI_CFG_SVID, sizeof(uint32)); si->sb.boardvendor = w & 0xffff; si->sb.boardtype = (w >> 16) & 0xffff; break; } if (si->sb.boardtype == 0) { SB_ERROR(("sb_doattach: unknown board type\n")); ASSERT(si->sb.boardtype); } /* setup the GPIO based LED powersave register */ if (si->sb.ccrev >= 16) { if ((vars == NULL) || ((w = getintvar(*vars, "gpiotimerval")) == 0)) w = DEFAULT_GPIOTIMERVAL; sb_corereg(si, 0, OFFSETOF(chipcregs_t, gpiotimerval), ~0, w); } if ((si->sb.chip == BCM4311_CHIP_ID) && (si->sb.chiprev <= 1)) { /* set proper clk setup delays before forcing HT */ sb_clkctl_init((void *)si); sb_corereg((void*)si, SB_CC_IDX, OFFSETOF(chipcregs_t, system_clk_ctl), SYCC_HR, SYCC_HR); } return (si);}uintsb_coreid(sb_t *sbh){ sb_info_t *si; sbconfig_t *sb; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); return ((R_SBREG(si, &sb->sbidhigh) & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT);}uintsb_coreidx(sb_t *sbh){ sb_info_t *si; si = SB_INFO(sbh); return (si->curidx);}/* return current index of core */static uint_sb_coreidx(sb_info_t *si){ uint32 sbaddr = 0; ASSERT(si); switch (BUSTYPE(si->sb.bustype)) { case PCI_BUS: sbaddr = OSL_PCI_READ_CONFIG(si->osh, PCI_BAR0_WIN, sizeof(uint32)); break; default: ASSERT(0); } if (!GOODCOREADDR(sbaddr)) return BADIDX; return ((sbaddr - SB_ENUM_BASE) / SB_CORE_SIZE);}uintsb_corevendor(sb_t *sbh){ sb_info_t *si; sbconfig_t *sb; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); return ((R_SBREG(si, &sb->sbidhigh) & SBIDH_VC_MASK) >> SBIDH_VC_SHIFT);}uintsb_corerev(sb_t *sbh){ sb_info_t *si; sbconfig_t *sb; uint sbidh; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); sbidh = R_SBREG(si, &sb->sbidhigh); return (SBCOREREV(sbidh));}void *sb_osh(sb_t *sbh){ sb_info_t *si; si = SB_INFO(sbh); return si->osh;}#define SBTML_ALLOW (SBTML_PE | SBTML_FGC | SBTML_FL_MASK)/* set/clear sbtmstatelow core-specific flags */uint32sb_coreflags(sb_t *sbh, uint32 mask, uint32 val){ sb_info_t *si; sbconfig_t *sb; uint32 w; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); ASSERT((val & ~mask) == 0); ASSERT((mask & ~SBTML_ALLOW) == 0); /* mask and set */ if (mask || val) { w = (R_SBREG(si, &sb->sbtmstatelow) & ~mask) | val; W_SBREG(si, &sb->sbtmstatelow, w); } /* return the new value */ return (R_SBREG(si, &sb->sbtmstatelow) & SBTML_ALLOW);}/* set/clear sbtmstatehigh core-specific flags */uint32sb_coreflagshi(sb_t *sbh, uint32 mask, uint32 val){ sb_info_t *si; sbconfig_t *sb; uint32 w; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); ASSERT((val & ~mask) == 0); ASSERT((mask & ~SBTMH_FL_MASK) == 0); /* mask and set */ if (mask || val) { w = (R_SBREG(si, &sb->sbtmstatehigh) & ~mask) | val; W_SBREG(si, &sb->sbtmstatehigh, w); } /* return the new value */ return (R_SBREG(si, &sb->sbtmstatehigh) & SBTMH_FL_MASK);}/* Run bist on current core. Caller needs to take care of core-specific bist hazards */intsb_corebist(sb_t *sbh){ uint32 sblo; sb_info_t *si; sbconfig_t *sb; int result = 0; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); sblo = R_SBREG(si, &sb->sbtmstatelow); W_SBREG(si, &sb->sbtmstatelow, (sblo | SBTML_FGC | SBTML_BE)); SPINWAIT(((R_SBREG(si, &sb->sbtmstatehigh) & SBTMH_BISTD) == 0), 100000); if (R_SBREG(si, &sb->sbtmstatehigh) & SBTMH_BISTF) result = BCME_ERROR; W_SBREG(si, &sb->sbtmstatelow, sblo); return result;}boolsb_iscoreup(sb_t *sbh){ sb_info_t *si; sbconfig_t *sb; si = SB_INFO(sbh); sb = REGS2SB(si->curmap); return ((R_SBREG(si, &sb->sbtmstatelow) & (SBTML_RESET | SBTML_REJ_MASK | SBTML_CLK)) == SBTML_CLK);}/* * 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. */static uintsb_corereg(sb_info_t *si, uint coreidx, uint regoff, uint mask, uint val){ uint origidx = 0; uint32 *r = NULL; uint w; uint intr_val = 0; bool fast = FALSE; ASSERT(GOODIDX(coreidx)); ASSERT(regoff < SB_CORE_SIZE); ASSERT((val & ~mask) == 0); if (!fast) { INTR_OFF(si, intr_val); /* save current core index */ origidx = sb_coreidx(&si->sb); /* switch core */ r = (uint32*) ((uchar*) sb_setcoreidx(&si->sb, coreidx) + regoff); } ASSERT(r); /* mask and set */ if (mask || val) { if (regoff >= SBCONFIGOFF) { w = (R_SBREG(si, r) & ~mask) | val; W_SBREG(si, r, w); } else { w = (R_REG(r) & ~mask) | val; W_REG(r, w); } } /* readback */ if (regoff >= SBCONFIGOFF) w = R_SBREG(si, r); else w = R_REG(r); if (!fast) { /* restore core index */ if (origidx != coreidx) sb_setcoreidx(&si->sb, origidx); INTR_RESTORE(si, intr_val); } return (w);}#define DWORD_ALIGN(x) (x & ~(0x03))#define BYTE_POS(x) (x & 0x3)#define WORD_POS(x) (x & 0x1)#define BYTE_SHIFT(x) (8 * BYTE_POS(x))#define WORD_SHIFT(x) (16 * WORD_POS(x))#define BYTE_VAL(a, x) ((a >> BYTE_SHIFT(x)) & 0xFF)#define WORD_VAL(a, x) ((a >> WORD_SHIFT(x)) & 0xFFFF)#define read_pci_cfg_byte(a) \ (BYTE_VAL(OSL_PCI_READ_CONFIG(si->osh, DWORD_ALIGN(a), 4), a) & 0xff)#define read_pci_cfg_write(a) \ (WORD_VAL(OSL_PCI_READ_CONFIG(si->osh, DWORD_ALIGN(a), 4), a) & 0xffff)/* return TRUE if requested capability exists in the PCI config space */static boolsb_find_pci_capability(sb_info_t *si, uint8 req_cap_id, uchar *buf, uint32 *buflen){ uint8 cap_id; uint8 cap_ptr; uint32 bufsize; uint8 byte_val; if (BUSTYPE(si->sb.bustype) != PCI_BUS) return FALSE; /* check for Header type 0 */ byte_val = read_pci_cfg_byte(PCI_CFG_HDR); if ((byte_val & 0x7f) != PCI_HEADER_NORMAL) return FALSE; /* check if the capability pointer field exists */ byte_val = read_pci_cfg_byte(PCI_CFG_STAT); if (!(byte_val & PCI_CAPPTR_PRESENT)) return FALSE; cap_ptr = read_pci_cfg_byte(PCI_CFG_CAPPTR); /* check if the capability pointer is 0x00 */ if (cap_ptr == 0x00) return FALSE;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -