/* * Copyright (c) 2010 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* * There are two different OTP controllers so far: * 1. new IPX OTP controller: chipc 21, >=23 * 2. older HND OTP controller: chipc 12, 17, 22 * * Define BCMHNDOTP to include support for the HND OTP controller. * Define BCMIPXOTP to include support for the IPX OTP controller. * * NOTE 1: More than one may be defined * NOTE 2: If none are defined, the default is to include them all. */ #if !defined(BCMHNDOTP) && !defined(BCMIPXOTP) #define BCMHNDOTP 1 #define BCMIPXOTP 1 #endif #define OTPTYPE_HND(ccrev) ((ccrev) < 21 || (ccrev) == 22) #define OTPTYPE_IPX(ccrev) ((ccrev) == 21 || (ccrev) >= 23) #define OTPP_TRIES 10000000 /* # of tries for OTPP */ #ifdef BCMIPXOTP #define MAXNUMRDES 9 /* Maximum OTP redundancy entries */ #endif /* OTP common function type */ typedef int (*otp_status_t) (void *oh); typedef int (*otp_size_t) (void *oh); typedef void *(*otp_init_t) (si_t *sih); typedef u16(*otp_read_bit_t) (void *oh, chipcregs_t *cc, uint off); typedef int (*otp_read_region_t) (si_t *sih, int region, u16 *data, uint *wlen); typedef int (*otp_nvread_t) (void *oh, char *data, uint *len); /* OTP function struct */ typedef struct otp_fn_s { otp_size_t size; otp_read_bit_t read_bit; otp_init_t init; otp_read_region_t read_region; otp_nvread_t nvread; otp_status_t status; } otp_fn_t; typedef struct { uint ccrev; /* chipc revision */ otp_fn_t *fn; /* OTP functions */ si_t *sih; /* Saved sb handle */ #ifdef BCMIPXOTP /* IPX OTP section */ u16 wsize; /* Size of otp in words */ u16 rows; /* Geometry */ u16 cols; /* Geometry */ u32 status; /* Flag bits (lock/prog/rv). * (Reflected only when OTP is power cycled) */ u16 hwbase; /* hardware subregion offset */ u16 hwlim; /* hardware subregion boundary */ u16 swbase; /* software subregion offset */ u16 swlim; /* software subregion boundary */ u16 fbase; /* fuse subregion offset */ u16 flim; /* fuse subregion boundary */ int otpgu_base; /* offset to General Use Region */ #endif /* BCMIPXOTP */ #ifdef BCMHNDOTP /* HND OTP section */ uint size; /* Size of otp in bytes */ uint hwprot; /* Hardware protection bits */ uint signvalid; /* Signature valid bits */ int boundary; /* hw/sw boundary */ #endif /* BCMHNDOTP */ } otpinfo_t; static otpinfo_t otpinfo; /* * IPX OTP Code * * Exported functions: * ipxotp_status() * ipxotp_size() * ipxotp_init() * ipxotp_read_bit() * ipxotp_read_region() * ipxotp_nvread() * */ #ifdef BCMIPXOTP #define HWSW_RGN(rgn) (((rgn) == OTP_HW_RGN) ? "h/w" : "s/w") /* OTP layout */ /* CC revs 21, 24 and 27 OTP General Use Region word offset */ #define REVA4_OTPGU_BASE 12 /* CC revs 23, 25, 26, 28 and above OTP General Use Region word offset */ #define REVB8_OTPGU_BASE 20 /* CC rev 36 OTP General Use Region word offset */ #define REV36_OTPGU_BASE 12 /* Subregion word offsets in General Use region */ #define OTPGU_HSB_OFF 0 #define OTPGU_SFB_OFF 1 #define OTPGU_CI_OFF 2 #define OTPGU_P_OFF 3 #define OTPGU_SROM_OFF 4 /* Flag bit offsets in General Use region */ #define OTPGU_HWP_OFF 60 #define OTPGU_SWP_OFF 61 #define OTPGU_CIP_OFF 62 #define OTPGU_FUSEP_OFF 63 #define OTPGU_CIP_MSK 0x4000 #define OTPGU_P_MSK 0xf000 #define OTPGU_P_SHIFT (OTPGU_HWP_OFF % 16) /* OTP Size */ #define OTP_SZ_FU_324 ((roundup(324, 8))/8) /* 324 bits */ #define OTP_SZ_FU_288 (288/8) /* 288 bits */ #define OTP_SZ_FU_216 (216/8) /* 216 bits */ #define OTP_SZ_FU_72 (72/8) /* 72 bits */ #define OTP_SZ_CHECKSUM (16/8) /* 16 bits */ #define OTP4315_SWREG_SZ 178 /* 178 bytes */ #define OTP_SZ_FU_144 (144/8) /* 144 bits */ static int ipxotp_status(void *oh) { otpinfo_t *oi = (otpinfo_t *) oh; return (int)(oi->status); } /* Return size in bytes */ static int ipxotp_size(void *oh) { otpinfo_t *oi = (otpinfo_t *) oh; return (int)oi->wsize * 2; } static u16 ipxotp_otpr(void *oh, chipcregs_t *cc, uint wn) { otpinfo_t *oi; oi = (otpinfo_t *) oh; return R_REG(&cc->sromotp[wn]); } static u16 ipxotp_read_bit(void *oh, chipcregs_t *cc, uint off) { otpinfo_t *oi = (otpinfo_t *) oh; uint k, row, col; u32 otpp, st; row = off / oi->cols; col = off % oi->cols; otpp = OTPP_START_BUSY | ((OTPPOC_READ << OTPP_OC_SHIFT) & OTPP_OC_MASK) | ((row << OTPP_ROW_SHIFT) & OTPP_ROW_MASK) | ((col << OTPP_COL_SHIFT) & OTPP_COL_MASK); W_REG(&cc->otpprog, otpp); for (k = 0; ((st = R_REG(&cc->otpprog)) & OTPP_START_BUSY) && (k < OTPP_TRIES); k++) ; if (k >= OTPP_TRIES) { return 0xffff; } if (st & OTPP_READERR) { return 0xffff; } st = (st & OTPP_VALUE_MASK) >> OTPP_VALUE_SHIFT; return (int)st; } /* Calculate max HW/SW region byte size by subtracting fuse region and checksum size, * osizew is oi->wsize (OTP size - GU size) in words */ static int ipxotp_max_rgnsz(si_t *sih, int osizew) { int ret = 0; switch (sih->chip) { case BCM43224_CHIP_ID: case BCM43225_CHIP_ID: ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM; break; case BCM4313_CHIP_ID: ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM; break; default: break; /* Don't know about this chip */ } return ret; } static void _ipxotp_init(otpinfo_t *oi, chipcregs_t *cc) { uint k; u32 otpp, st; /* record word offset of General Use Region for various chipcommon revs */ if (oi->sih->ccrev == 21 || oi->sih->ccrev == 24 || oi->sih->ccrev == 27) { oi->otpgu_base = REVA4_OTPGU_BASE; } else if (oi->sih->ccrev == 36) { /* OTP size greater than equal to 2KB (128 words), otpgu_base is similar to rev23 */ if (oi->wsize >= 128) oi->otpgu_base = REVB8_OTPGU_BASE; else oi->otpgu_base = REV36_OTPGU_BASE; } else if (oi->sih->ccrev == 23 || oi->sih->ccrev >= 25) { oi->otpgu_base = REVB8_OTPGU_BASE; } /* First issue an init command so the status is up to date */ otpp = OTPP_START_BUSY | ((OTPPOC_INIT << OTPP_OC_SHIFT) & OTPP_OC_MASK); W_REG(&cc->otpprog, otpp); for (k = 0; ((st = R_REG(&cc->otpprog)) & OTPP_START_BUSY) && (k < OTPP_TRIES); k++) ; if (k >= OTPP_TRIES) { return; } /* Read OTP lock bits and subregion programmed indication bits */ oi->status = R_REG(&cc->otpstatus); if ((oi->sih->chip == BCM43224_CHIP_ID) || (oi->sih->chip == BCM43225_CHIP_ID)) { u32 p_bits; p_bits = (ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_P_OFF) & OTPGU_P_MSK) >> OTPGU_P_SHIFT; oi->status |= (p_bits << OTPS_GUP_SHIFT); } /* * h/w region base and fuse region limit are fixed to the top and * the bottom of the general use region. Everything else can be flexible. */ oi->hwbase = oi->otpgu_base + OTPGU_SROM_OFF; oi->hwlim = oi->wsize; if (oi->status & OTPS_GUP_HW) { oi->hwlim = ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_HSB_OFF) / 16; oi->swbase = oi->hwlim; } else oi->swbase = oi->hwbase; /* subtract fuse and checksum from beginning */ oi->swlim = ipxotp_max_rgnsz(oi->sih, oi->wsize) / 2; if (oi->status & OTPS_GUP_SW) { oi->swlim = ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_SFB_OFF) / 16; oi->fbase = oi->swlim; } else oi->fbase = oi->swbase; oi->flim = oi->wsize; } static void *ipxotp_init(si_t *sih) { uint idx; chipcregs_t *cc; otpinfo_t *oi; /* Make sure we're running IPX OTP */ if (!OTPTYPE_IPX(sih->ccrev)) return NULL; /* Make sure OTP is not disabled */ if (ai_is_otp_disabled(sih)) return NULL; /* Make sure OTP is powered up */ if (!ai_is_otp_powered(sih)) return NULL; oi = &otpinfo; /* Check for otp size */ switch ((sih->cccaps & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT) { case 0: /* Nothing there */ return NULL; case 1: /* 32x64 */ oi->rows = 32; oi->cols = 64; oi->wsize = 128; break; case 2: /* 64x64 */ oi->rows = 64; oi->cols = 64; oi->wsize = 256; break; case 5: /* 96x64 */ oi->rows = 96; oi->cols = 64; oi->wsize = 384; break; case 7: /* 16x64 *//* 1024 bits */ oi->rows = 16; oi->cols = 64; oi->wsize = 64; break; default: /* Don't know the geometry */ return NULL; } /* Retrieve OTP region info */ idx = ai_coreidx(sih); cc = ai_setcoreidx(sih, SI_CC_IDX); _ipxotp_init(oi, cc); ai_setcoreidx(sih, idx); return (void *)oi; } static int ipxotp_read_region(void *oh, int region, u16 *data, uint *wlen) { otpinfo_t *oi = (otpinfo_t *) oh; uint idx; chipcregs_t *cc; uint base, i, sz; /* Validate region selection */ switch (region) { case OTP_HW_RGN: sz = (uint) oi->hwlim - oi->hwbase; if (!(oi->status & OTPS_GUP_HW)) { *wlen = sz; return -ENODATA; } if (*wlen < sz) { *wlen = sz; return -EOVERFLOW; } base = oi->hwbase; break; case OTP_SW_RGN: sz = ((uint) oi->swlim - oi->swbase); if (!(oi->status & OTPS_GUP_SW)) { *wlen = sz; return -ENODATA; } if (*wlen < sz) { *wlen = sz; return -EOVERFLOW; } base = oi->swbase; break; case OTP_CI_RGN: sz = OTPGU_CI_SZ; if (!(oi->status & OTPS_GUP_CI)) { *wlen = sz; return -ENODATA; } if (*wlen < sz) { *wlen = sz; return -EOVERFLOW; } base = oi->otpgu_base + OTPGU_CI_OFF; break; case OTP_FUSE_RGN: sz = (uint) oi->flim - oi->fbase; if (!(oi->status & OTPS_GUP_FUSE)) { *wlen = sz; return -ENODATA; } if (*wlen < sz) { *wlen = sz; return -EOVERFLOW; } base = oi->fbase; break; case OTP_ALL_RGN: sz = ((uint) oi->flim - oi->hwbase); if (!(oi->status & (OTPS_GUP_HW | OTPS_GUP_SW))) { *wlen = sz; return -ENODATA; } if (*wlen < sz) { *wlen = sz; return -EOVERFLOW; } base = oi->hwbase; break; default: return -EINVAL; } idx = ai_coreidx(oi->sih); cc = ai_setcoreidx(oi->sih, SI_CC_IDX); /* Read the data */ for (i = 0; i < sz; i++) data[i] = ipxotp_otpr(oh, cc, base + i); ai_setcoreidx(oi->sih, idx); *wlen = sz; return 0; } static int ipxotp_nvread(void *oh, char *data, uint *len) { return -ENOTSUPP; } static otp_fn_t ipxotp_fn = { (otp_size_t) ipxotp_size, (otp_read_bit_t) ipxotp_read_bit, (otp_init_t) ipxotp_init, (otp_read_region_t) ipxotp_read_region, (otp_nvread_t) ipxotp_nvread, (otp_status_t) ipxotp_status }; #endif /* BCMIPXOTP */ /* * HND OTP Code * * Exported functions: * hndotp_status() * hndotp_size() * hndotp_init() * hndotp_read_bit() * hndotp_read_region() * hndotp_nvread() * */ #ifdef BCMHNDOTP /* Fields in otpstatus */ #define OTPS_PROGFAIL 0x80000000 #define OTPS_PROTECT 0x00000007 #define OTPS_HW_PROTECT 0x00000001 #define OTPS_SW_PROTECT 0x00000002 #define OTPS_CID_PROTECT 0x00000004 #define OTPS_RCEV_MSK 0x00003f00 #define OTPS_RCEV_SHIFT 8 /* Fields in the otpcontrol register */ #define OTPC_RECWAIT 0xff000000 #define OTPC_PROGWAIT 0x00ffff00 #define OTPC_PRW_SHIFT 8 #define OTPC_MAXFAIL 0x00000038 #define OTPC_VSEL 0x00000006 #define OTPC_SELVL 0x00000001 /* OTP regions (Word offsets from otp size) */ #define OTP_SWLIM_OFF (-4) #define OTP_CIDBASE_OFF 0 #define OTP_CIDLIM_OFF 4 /* Predefined OTP words (Word offset from otp size) */ #define OTP_BOUNDARY_OFF (-4) #define OTP_HWSIGN_OFF (-3) #define OTP_SWSIGN_OFF (-2) #define OTP_CIDSIGN_OFF (-1) #define OTP_CID_OFF 0 #define OTP_PKG_OFF 1 #define OTP_FID_OFF 2 #define OTP_RSV_OFF 3 #define OTP_LIM_OFF 4 #define OTP_RD_OFF 4 /* Redundancy row starts here */ #define OTP_RC0_OFF 28 /* Redundancy control word 1 */ #define OTP_RC1_OFF 32 /* Redundancy control word 2 */ #define OTP_RC_LIM_OFF 36 /* Redundancy control word end */ #define OTP_HW_REGION OTPS_HW_PROTECT #define OTP_SW_REGION OTPS_SW_PROTECT #define OTP_CID_REGION OTPS_CID_PROTECT #if OTP_HW_REGION != OTP_HW_RGN #error "incompatible OTP_HW_RGN" #endif #if OTP_SW_REGION != OTP_SW_RGN #error "incompatible OTP_SW_RGN" #endif #if OTP_CID_REGION != OTP_CI_RGN #error "incompatible OTP_CI_RGN" #endif /* Redundancy entry definitions */ #define OTP_RCE_ROW_SZ 6 #define OTP_RCE_SIGN_MASK 0x7fff #define OTP_RCE_ROW_MASK 0x3f #define OTP_RCE_BITS 21 #define OTP_RCE_SIGN_SZ 15 #define OTP_RCE_BIT0 1 #define OTP_WPR 4 #define OTP_SIGNATURE 0x578a #define OTP_MAGIC 0x4e56 static int hndotp_status(void *oh) { otpinfo_t *oi = (otpinfo_t *) oh; return (int)(oi->hwprot | oi->signvalid); } static int hndotp_size(void *oh) { otpinfo_t *oi = (otpinfo_t *) oh; return (int)(oi->size); } static u16 hndotp_otpr(void *oh, chipcregs_t *cc, uint wn) { volatile u16 *ptr; ptr = (volatile u16 *)((volatile char *)cc + CC_SROM_OTP); return R_REG(&ptr[wn]); } static u16 hndotp_otproff(void *oh, chipcregs_t *cc, int woff) { otpinfo_t *oi = (otpinfo_t *) oh; volatile u16 *ptr; ptr = (volatile u16 *)((volatile char *)cc + CC_SROM_OTP); return R_REG(&ptr[(oi->size / 2) + woff]); } static u16 hndotp_read_bit(void *oh, chipcregs_t *cc, uint idx) { uint k, row, col; u32 otpp, st; row = idx / 65; col = idx % 65; otpp = OTPP_START_BUSY | OTPP_READ | ((row << OTPP_ROW_SHIFT) & OTPP_ROW_MASK) | (col & OTPP_COL_MASK); W_REG(&cc->otpprog, otpp); st = R_REG(&cc->otpprog); for (k = 0; ((st & OTPP_START_BUSY) == OTPP_START_BUSY) && (k < OTPP_TRIES); k++) st = R_REG(&cc->otpprog); if (k >= OTPP_TRIES) { return 0xffff; } if (st & OTPP_READERR) { return 0xffff; } st = (st & OTPP_VALUE_MASK) >> OTPP_VALUE_SHIFT; return (u16) st; } static void *hndotp_init(si_t *sih) { uint idx; chipcregs_t *cc; otpinfo_t *oi; u32 cap = 0, clkdiv, otpdiv = 0; void *ret = NULL; oi = &otpinfo; idx = ai_coreidx(sih); /* Check for otp */ cc = ai_setcoreidx(sih, SI_CC_IDX); if (cc != NULL) { cap = R_REG(&cc->capabilities); if ((cap & CC_CAP_OTPSIZE) == 0) { /* Nothing there */ goto out; } if (!((oi->ccrev == 12) || (oi->ccrev == 17) || (oi->ccrev == 22))) return NULL; /* Read the OTP byte size. chipcommon rev >= 18 has RCE so the size is * 8 row (64 bytes) smaller */ oi->size = 1 << (((cap & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT) + CC_CAP_OTPSIZE_BASE); if (oi->ccrev >= 18) oi->size -= ((OTP_RC0_OFF - OTP_BOUNDARY_OFF) * 2); oi->hwprot = (int)(R_REG(&cc->otpstatus) & OTPS_PROTECT); oi->boundary = -1; /* Check the region signature */ if (hndotp_otproff(oi, cc, OTP_HWSIGN_OFF) == OTP_SIGNATURE) { oi->signvalid |= OTP_HW_REGION; oi->boundary = hndotp_otproff(oi, cc, OTP_BOUNDARY_OFF); } if (hndotp_otproff(oi, cc, OTP_SWSIGN_OFF) == OTP_SIGNATURE) oi->signvalid |= OTP_SW_REGION; if (hndotp_otproff(oi, cc, OTP_CIDSIGN_OFF) == OTP_SIGNATURE) oi->signvalid |= OTP_CID_REGION; /* Set OTP clkdiv for stability */ if (oi->ccrev == 22) otpdiv = 12; if (otpdiv) { clkdiv = R_REG(&cc->clkdiv); clkdiv = (clkdiv & ~CLKD_OTP) | (otpdiv << CLKD_OTP_SHIFT); W_REG(&cc->clkdiv, clkdiv); } udelay(10); ret = (void *)oi; } out: /* All done */ ai_setcoreidx(sih, idx); return ret; } static int hndotp_read_region(void *oh, int region, u16 *data, uint *wlen) { otpinfo_t *oi = (otpinfo_t *) oh; u32 idx, st; chipcregs_t *cc; int i; if (region != OTP_HW_REGION) { /* * Only support HW region * (no active chips use HND OTP SW region) * */ return -ENOTSUPP; } /* Region empty? */ st = oi->hwprot | oi->signvalid; if ((st & region) == 0) return -ENODATA; *wlen = ((int)*wlen < oi->boundary / 2) ? *wlen : (uint) oi->boundary / 2; idx = ai_coreidx(oi->sih); cc = ai_setcoreidx(oi->sih, SI_CC_IDX); for (i = 0; i < (int)*wlen; i++) data[i] = hndotp_otpr(oh, cc, i); ai_setcoreidx(oi->sih, idx); return 0; } static int hndotp_nvread(void *oh, char *data, uint *len) { int rc = 0; otpinfo_t *oi = (otpinfo_t *) oh; u32 base, bound, lim = 0, st; int i, chunk, gchunks, tsz = 0; u32 idx; chipcregs_t *cc; uint offset; u16 *rawotp = NULL; /* save the orig core */ idx = ai_coreidx(oi->sih); cc = ai_setcoreidx(oi->sih, SI_CC_IDX); st = hndotp_status(oh); if (!(st & (OTP_HW_REGION | OTP_SW_REGION))) { rc = -1; goto out; } /* Read the whole otp so we can easily manipulate it */ lim = hndotp_size(oh); rawotp = kmalloc(lim, GFP_ATOMIC); if (rawotp == NULL) { rc = -2; goto out; } for (i = 0; i < (int)(lim / 2); i++) rawotp[i] = hndotp_otpr(oh, cc, i); if ((st & OTP_HW_REGION) == 0) { /* This could be a programming failure in the first * chunk followed by one or more good chunks */ for (i = 0; i < (int)(lim / 2); i++) if (rawotp[i] == OTP_MAGIC) break; if (i < (int)(lim / 2)) { base = i; bound = (i * 2) + rawotp[i + 1]; } else { rc = -3; goto out; } } else { bound = rawotp[(lim / 2) + OTP_BOUNDARY_OFF]; /* There are two cases: 1) The whole otp is used as nvram * and 2) There is a hardware header followed by nvram. */ if (rawotp[0] == OTP_MAGIC) { base = 0; } else base = bound; } /* Find and copy the data */ chunk = 0; gchunks = 0; i = base / 2; offset = 0; while ((i < (int)(lim / 2)) && (rawotp[i] == OTP_MAGIC)) { int dsz, rsz = rawotp[i + 1]; if (((i * 2) + rsz) >= (int)lim) { /* Bad length, try to find another chunk anyway */ rsz = 6; } if (crc_ccitt(CRC16_INIT_VALUE, (u8 *) &rawotp[i], rsz) == CRC16_GOOD_VALUE) { /* Good crc, copy the vars */ gchunks++; dsz = rsz - 6; tsz += dsz; if (offset + dsz >= *len) { goto out; } memcpy(&data[offset], &rawotp[i + 2], dsz); offset += dsz; /* Remove extra null characters at the end */ while (offset > 1 && data[offset - 1] == 0 && data[offset - 2] == 0) offset--; i += rsz / 2; } else { /* bad length or crc didn't check, try to find the next set */ if (rawotp[i + (rsz / 2)] == OTP_MAGIC) { /* Assume length is good */ i += rsz / 2; } else { while (++i < (int)(lim / 2)) if (rawotp[i] == OTP_MAGIC) break; } } chunk++; } *len = offset; out: kfree(rawotp); ai_setcoreidx(oi->sih, idx); return rc; } static otp_fn_t hndotp_fn = { (otp_size_t) hndotp_size, (otp_read_bit_t) hndotp_read_bit, (otp_init_t) hndotp_init, (otp_read_region_t) hndotp_read_region, (otp_nvread_t) hndotp_nvread, (otp_status_t) hndotp_status }; #endif /* BCMHNDOTP */ /* * Common Code: Compiled for IPX / HND / AUTO * otp_status() * otp_size() * otp_read_bit() * otp_init() * otp_read_region() * otp_nvread() */ int otp_status(void *oh) { otpinfo_t *oi = (otpinfo_t *) oh; return oi->fn->status(oh); } int otp_size(void *oh) { otpinfo_t *oi = (otpinfo_t *) oh; return oi->fn->size(oh); } u16 otp_read_bit(void *oh, uint offset) { otpinfo_t *oi = (otpinfo_t *) oh; uint idx = ai_coreidx(oi->sih); chipcregs_t *cc = ai_setcoreidx(oi->sih, SI_CC_IDX); u16 readBit = (u16) oi->fn->read_bit(oh, cc, offset); ai_setcoreidx(oi->sih, idx); return readBit; } void *otp_init(si_t *sih) { otpinfo_t *oi; void *ret = NULL; oi = &otpinfo; memset(oi, 0, sizeof(otpinfo_t)); oi->ccrev = sih->ccrev; #ifdef BCMIPXOTP if (OTPTYPE_IPX(oi->ccrev)) oi->fn = &ipxotp_fn; #endif #ifdef BCMHNDOTP if (OTPTYPE_HND(oi->ccrev)) oi->fn = &hndotp_fn; #endif if (oi->fn == NULL) { return NULL; } oi->sih = sih; ret = (oi->fn->init) (sih); return ret; } int otp_read_region(si_t *sih, int region, u16 *data, uint *wlen) { bool wasup = false; void *oh; int err = 0; wasup = ai_is_otp_powered(sih); if (!wasup) ai_otp_power(sih, true); if (!ai_is_otp_powered(sih) || ai_is_otp_disabled(sih)) { err = -EPERM; goto out; } oh = otp_init(sih); if (oh == NULL) { err = -EBADE; goto out; } err = (((otpinfo_t *) oh)->fn->read_region) (oh, region, data, wlen); out: if (!wasup) ai_otp_power(sih, false); return err; } int otp_nvread(void *oh, char *data, uint *len) { otpinfo_t *oi = (otpinfo_t *) oh; return oi->fn->nvread(oh, data, len); }