blob: 1c6c57b469a01a35c85da87db498a544768921f6 [file] [log] [blame]
Henry Ptasinskia9533e72010-09-08 21:04:42 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <typedefs.h>
18#include <bcmdefs.h>
19#include <osl.h>
20#include <bcmdevs.h>
21#include <bcmutils.h>
22#include <siutils.h>
23#include <bcmendian.h>
24#include <hndsoc.h>
25#include <sbchipc.h>
26#include <bcmotp.h>
27#include "siutils_priv.h"
28
29/*
30 * There are two different OTP controllers so far:
31 * 1. new IPX OTP controller: chipc 21, >=23
32 * 2. older HND OTP controller: chipc 12, 17, 22
33 *
34 * Define BCMHNDOTP to include support for the HND OTP controller.
35 * Define BCMIPXOTP to include support for the IPX OTP controller.
36 *
37 * NOTE 1: More than one may be defined
38 * NOTE 2: If none are defined, the default is to include them all.
39 */
40
41#if !defined(BCMHNDOTP) && !defined(BCMIPXOTP)
42#define BCMHNDOTP 1
43#define BCMIPXOTP 1
44#endif
45
46#define OTPTYPE_HND(ccrev) ((ccrev) < 21 || (ccrev) == 22)
47#define OTPTYPE_IPX(ccrev) ((ccrev) == 21 || (ccrev) >= 23)
48
49#define OTPP_TRIES 10000000 /* # of tries for OTPP */
50
51#ifdef BCMIPXOTP
52#define MAXNUMRDES 9 /* Maximum OTP redundancy entries */
53#endif
54
55/* OTP common function type */
56typedef int (*otp_status_t) (void *oh);
57typedef int (*otp_size_t) (void *oh);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040058typedef void *(*otp_init_t) (si_t *sih);
59typedef uint16(*otp_read_bit_t) (void *oh, chipcregs_t *cc, uint off);
60typedef int (*otp_read_region_t) (si_t *sih, int region, uint16 *data,
61 uint *wlen);
62typedef int (*otp_nvread_t) (void *oh, char *data, uint *len);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070063
64/* OTP function struct */
65typedef struct otp_fn_s {
66 otp_size_t size;
67 otp_read_bit_t read_bit;
68 otp_init_t init;
69 otp_read_region_t read_region;
70 otp_nvread_t nvread;
71 otp_status_t status;
72} otp_fn_t;
73
74typedef struct {
75 uint ccrev; /* chipc revision */
76 otp_fn_t *fn; /* OTP functions */
77 si_t *sih; /* Saved sb handle */
78 osl_t *osh;
79
80#ifdef BCMIPXOTP
81 /* IPX OTP section */
82 uint16 wsize; /* Size of otp in words */
83 uint16 rows; /* Geometry */
84 uint16 cols; /* Geometry */
85 uint32 status; /* Flag bits (lock/prog/rv).
86 * (Reflected only when OTP is power cycled)
87 */
88 uint16 hwbase; /* hardware subregion offset */
89 uint16 hwlim; /* hardware subregion boundary */
90 uint16 swbase; /* software subregion offset */
91 uint16 swlim; /* software subregion boundary */
92 uint16 fbase; /* fuse subregion offset */
93 uint16 flim; /* fuse subregion boundary */
94 int otpgu_base; /* offset to General Use Region */
95#endif /* BCMIPXOTP */
96
97#ifdef BCMHNDOTP
98 /* HND OTP section */
99 uint size; /* Size of otp in bytes */
100 uint hwprot; /* Hardware protection bits */
101 uint signvalid; /* Signature valid bits */
102 int boundary; /* hw/sw boundary */
103#endif /* BCMHNDOTP */
104} otpinfo_t;
105
106static otpinfo_t otpinfo;
107
108/*
109 * IPX OTP Code
110 *
111 * Exported functions:
112 * ipxotp_status()
113 * ipxotp_size()
114 * ipxotp_init()
115 * ipxotp_read_bit()
116 * ipxotp_read_region()
117 * ipxotp_nvread()
118 *
119 */
120
121#ifdef BCMIPXOTP
122
123#define HWSW_RGN(rgn) (((rgn) == OTP_HW_RGN) ? "h/w" : "s/w")
124
125/* OTP layout */
126/* CC revs 21, 24 and 27 OTP General Use Region word offset */
127#define REVA4_OTPGU_BASE 12
128
129/* CC revs 23, 25, 26, 28 and above OTP General Use Region word offset */
130#define REVB8_OTPGU_BASE 20
131
132/* CC rev 36 OTP General Use Region word offset */
133#define REV36_OTPGU_BASE 12
134
135/* Subregion word offsets in General Use region */
136#define OTPGU_HSB_OFF 0
137#define OTPGU_SFB_OFF 1
138#define OTPGU_CI_OFF 2
139#define OTPGU_P_OFF 3
140#define OTPGU_SROM_OFF 4
141
142/* Flag bit offsets in General Use region */
143#define OTPGU_HWP_OFF 60
144#define OTPGU_SWP_OFF 61
145#define OTPGU_CIP_OFF 62
146#define OTPGU_FUSEP_OFF 63
147#define OTPGU_CIP_MSK 0x4000
148#define OTPGU_P_MSK 0xf000
149#define OTPGU_P_SHIFT (OTPGU_HWP_OFF % 16)
150
151/* OTP Size */
152#define OTP_SZ_FU_324 ((ROUNDUP(324,8))/8) /* 324 bits */
153#define OTP_SZ_FU_288 (288/8) /* 288 bits */
154#define OTP_SZ_FU_216 (216/8) /* 216 bits */
155#define OTP_SZ_FU_72 (72/8) /* 72 bits */
156#define OTP_SZ_CHECKSUM (16/8) /* 16 bits */
157#define OTP4315_SWREG_SZ 178 /* 178 bytes */
158#define OTP_SZ_FU_144 (144/8) /* 144 bits */
159
160static int ipxotp_status(void *oh)
161{
162 otpinfo_t *oi = (otpinfo_t *) oh;
163 return (int)(oi->status);
164}
165
166/* Return size in bytes */
167static int ipxotp_size(void *oh)
168{
169 otpinfo_t *oi = (otpinfo_t *) oh;
170 return (int)oi->wsize * 2;
171}
172
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400173static uint16 ipxotp_otpr(void *oh, chipcregs_t *cc, uint wn)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700174{
175 otpinfo_t *oi;
176
177 oi = (otpinfo_t *) oh;
178
179 ASSERT(wn < oi->wsize);
180 ASSERT(cc != NULL);
181
182 return R_REG(oi->osh, &cc->sromotp[wn]);
183}
184
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400185static uint16 ipxotp_read_bit(void *oh, chipcregs_t *cc, uint off)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700186{
187 otpinfo_t *oi = (otpinfo_t *) oh;
188 uint k, row, col;
189 uint32 otpp, st;
190
191 row = off / oi->cols;
192 col = off % oi->cols;
193
194 otpp = OTPP_START_BUSY |
195 ((OTPPOC_READ << OTPP_OC_SHIFT) & OTPP_OC_MASK) |
196 ((row << OTPP_ROW_SHIFT) & OTPP_ROW_MASK) |
197 ((col << OTPP_COL_SHIFT) & OTPP_COL_MASK);
198 W_REG(oi->osh, &cc->otpprog, otpp);
199
200 for (k = 0;
201 ((st = R_REG(oi->osh, &cc->otpprog)) & OTPP_START_BUSY)
Jason Cooper62145822010-09-14 09:45:34 -0400202 && (k < OTPP_TRIES); k++)
203 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700204 if (k >= OTPP_TRIES) {
205 return 0xffff;
206 }
207 if (st & OTPP_READERR) {
208 return 0xffff;
209 }
210 st = (st & OTPP_VALUE_MASK) >> OTPP_VALUE_SHIFT;
211
212 return (int)st;
213}
214
215/* Calculate max HW/SW region byte size by substracting fuse region and checksum size,
216 * osizew is oi->wsize (OTP size - GU size) in words
217 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400218static int ipxotp_max_rgnsz(si_t *sih, int osizew)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700219{
220 int ret = 0;
221
222 switch (CHIPID(sih->chip)) {
223 case BCM43224_CHIP_ID:
224 case BCM43225_CHIP_ID:
225 ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM;
226 break;
227 case BCM4313_CHIP_ID:
228 ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM;
229 break;
230 default:
231 ASSERT(0); /* Don't konw about this chip */
232 }
233
234 return ret;
235}
236
Jason Coopera2627bc2010-09-14 09:45:31 -0400237static void BCMNMIATTACHFN(_ipxotp_init) (otpinfo_t *oi, chipcregs_t *cc)
238{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700239 uint k;
240 uint32 otpp, st;
241
242 /* record word offset of General Use Region for various chipcommon revs */
243 if (oi->sih->ccrev == 21 || oi->sih->ccrev == 24
244 || oi->sih->ccrev == 27) {
245 oi->otpgu_base = REVA4_OTPGU_BASE;
246 } else if (oi->sih->ccrev == 36) {
247 /* OTP size greater than equal to 2KB (128 words), otpgu_base is similar to rev23 */
248 if (oi->wsize >= 128)
249 oi->otpgu_base = REVB8_OTPGU_BASE;
250 else
251 oi->otpgu_base = REV36_OTPGU_BASE;
252 } else if (oi->sih->ccrev == 23 || oi->sih->ccrev >= 25) {
253 oi->otpgu_base = REVB8_OTPGU_BASE;
254 }
255
256 /* First issue an init command so the status is up to date */
257 otpp =
258 OTPP_START_BUSY | ((OTPPOC_INIT << OTPP_OC_SHIFT) & OTPP_OC_MASK);
259
260 W_REG(oi->osh, &cc->otpprog, otpp);
261 for (k = 0;
262 ((st = R_REG(oi->osh, &cc->otpprog)) & OTPP_START_BUSY)
Jason Cooper62145822010-09-14 09:45:34 -0400263 && (k < OTPP_TRIES); k++)
264 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700265 if (k >= OTPP_TRIES) {
266 return;
267 }
268
269 /* Read OTP lock bits and subregion programmed indication bits */
270 oi->status = R_REG(oi->osh, &cc->otpstatus);
271
272 if ((CHIPID(oi->sih->chip) == BCM43224_CHIP_ID)
273 || (CHIPID(oi->sih->chip) == BCM43225_CHIP_ID)) {
274 uint32 p_bits;
275 p_bits =
276 (ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_P_OFF) &
277 OTPGU_P_MSK)
278 >> OTPGU_P_SHIFT;
279 oi->status |= (p_bits << OTPS_GUP_SHIFT);
280 }
281
282 /*
283 * h/w region base and fuse region limit are fixed to the top and
284 * the bottom of the general use region. Everything else can be flexible.
285 */
286 oi->hwbase = oi->otpgu_base + OTPGU_SROM_OFF;
287 oi->hwlim = oi->wsize;
288 if (oi->status & OTPS_GUP_HW) {
289 oi->hwlim =
290 ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_HSB_OFF) / 16;
291 oi->swbase = oi->hwlim;
292 } else
293 oi->swbase = oi->hwbase;
294
295 /* subtract fuse and checksum from beginning */
296 oi->swlim = ipxotp_max_rgnsz(oi->sih, oi->wsize) / 2;
297
298 if (oi->status & OTPS_GUP_SW) {
299 oi->swlim =
300 ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_SFB_OFF) / 16;
301 oi->fbase = oi->swlim;
302 } else
303 oi->fbase = oi->swbase;
304
305 oi->flim = oi->wsize;
306}
307
Jason Coopera2627bc2010-09-14 09:45:31 -0400308static void *BCMNMIATTACHFN(ipxotp_init) (si_t *sih)
309{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700310 uint idx;
311 chipcregs_t *cc;
312 otpinfo_t *oi;
313
314 /* Make sure we're running IPX OTP */
315 ASSERT(OTPTYPE_IPX(sih->ccrev));
316 if (!OTPTYPE_IPX(sih->ccrev))
317 return NULL;
318
319 /* Make sure OTP is not disabled */
320 if (si_is_otp_disabled(sih)) {
321 return NULL;
322 }
323
324 /* Make sure OTP is powered up */
325 if (!si_is_otp_powered(sih)) {
326 return NULL;
327 }
328
329 oi = &otpinfo;
330
331 /* Check for otp size */
332 switch ((sih->cccaps & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT) {
333 case 0:
334 /* Nothing there */
335 return NULL;
336 case 1: /* 32x64 */
337 oi->rows = 32;
338 oi->cols = 64;
339 oi->wsize = 128;
340 break;
341 case 2: /* 64x64 */
342 oi->rows = 64;
343 oi->cols = 64;
344 oi->wsize = 256;
345 break;
346 case 5: /* 96x64 */
347 oi->rows = 96;
348 oi->cols = 64;
349 oi->wsize = 384;
350 break;
351 case 7: /* 16x64 *//* 1024 bits */
352 oi->rows = 16;
353 oi->cols = 64;
354 oi->wsize = 64;
355 break;
356 default:
357 /* Don't know the geometry */
358 return NULL;
359 }
360
361 /* Retrieve OTP region info */
362 idx = si_coreidx(sih);
363 cc = si_setcoreidx(sih, SI_CC_IDX);
364 ASSERT(cc != NULL);
365
366 _ipxotp_init(oi, cc);
367
368 si_setcoreidx(sih, idx);
369
370 return (void *)oi;
371}
372
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400373static int ipxotp_read_region(void *oh, int region, uint16 *data, uint *wlen)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700374{
375 otpinfo_t *oi = (otpinfo_t *) oh;
376 uint idx;
377 chipcregs_t *cc;
378 uint base, i, sz;
379
380 /* Validate region selection */
381 switch (region) {
382 case OTP_HW_RGN:
383 sz = (uint) oi->hwlim - oi->hwbase;
384 if (!(oi->status & OTPS_GUP_HW)) {
385 *wlen = sz;
386 return BCME_NOTFOUND;
387 }
388 if (*wlen < sz) {
389 *wlen = sz;
390 return BCME_BUFTOOSHORT;
391 }
392 base = oi->hwbase;
393 break;
394 case OTP_SW_RGN:
395 sz = ((uint) oi->swlim - oi->swbase);
396 if (!(oi->status & OTPS_GUP_SW)) {
397 *wlen = sz;
398 return BCME_NOTFOUND;
399 }
400 if (*wlen < sz) {
401 *wlen = sz;
402 return BCME_BUFTOOSHORT;
403 }
404 base = oi->swbase;
405 break;
406 case OTP_CI_RGN:
407 sz = OTPGU_CI_SZ;
408 if (!(oi->status & OTPS_GUP_CI)) {
409 *wlen = sz;
410 return BCME_NOTFOUND;
411 }
412 if (*wlen < sz) {
413 *wlen = sz;
414 return BCME_BUFTOOSHORT;
415 }
416 base = oi->otpgu_base + OTPGU_CI_OFF;
417 break;
418 case OTP_FUSE_RGN:
419 sz = (uint) oi->flim - oi->fbase;
420 if (!(oi->status & OTPS_GUP_FUSE)) {
421 *wlen = sz;
422 return BCME_NOTFOUND;
423 }
424 if (*wlen < sz) {
425 *wlen = sz;
426 return BCME_BUFTOOSHORT;
427 }
428 base = oi->fbase;
429 break;
430 case OTP_ALL_RGN:
431 sz = ((uint) oi->flim - oi->hwbase);
432 if (!(oi->status & (OTPS_GUP_HW | OTPS_GUP_SW))) {
433 *wlen = sz;
434 return BCME_NOTFOUND;
435 }
436 if (*wlen < sz) {
437 *wlen = sz;
438 return BCME_BUFTOOSHORT;
439 }
440 base = oi->hwbase;
441 break;
442 default:
443 return BCME_BADARG;
444 }
445
446 idx = si_coreidx(oi->sih);
447 cc = si_setcoreidx(oi->sih, SI_CC_IDX);
448 ASSERT(cc != NULL);
449
450 /* Read the data */
451 for (i = 0; i < sz; i++)
452 data[i] = ipxotp_otpr(oh, cc, base + i);
453
454 si_setcoreidx(oi->sih, idx);
455 *wlen = sz;
456 return 0;
457}
458
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400459static int ipxotp_nvread(void *oh, char *data, uint *len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700460{
461 return BCME_UNSUPPORTED;
462}
463
464static otp_fn_t ipxotp_fn = {
465 (otp_size_t) ipxotp_size,
466 (otp_read_bit_t) ipxotp_read_bit,
467
468 (otp_init_t) ipxotp_init,
469 (otp_read_region_t) ipxotp_read_region,
470 (otp_nvread_t) ipxotp_nvread,
471
472 (otp_status_t) ipxotp_status
473};
474
475#endif /* BCMIPXOTP */
476
477/*
478 * HND OTP Code
479 *
480 * Exported functions:
481 * hndotp_status()
482 * hndotp_size()
483 * hndotp_init()
484 * hndotp_read_bit()
485 * hndotp_read_region()
486 * hndotp_nvread()
487 *
488 */
489
490#ifdef BCMHNDOTP
491
492/* Fields in otpstatus */
493#define OTPS_PROGFAIL 0x80000000
494#define OTPS_PROTECT 0x00000007
495#define OTPS_HW_PROTECT 0x00000001
496#define OTPS_SW_PROTECT 0x00000002
497#define OTPS_CID_PROTECT 0x00000004
498#define OTPS_RCEV_MSK 0x00003f00
499#define OTPS_RCEV_SHIFT 8
500
501/* Fields in the otpcontrol register */
502#define OTPC_RECWAIT 0xff000000
503#define OTPC_PROGWAIT 0x00ffff00
504#define OTPC_PRW_SHIFT 8
505#define OTPC_MAXFAIL 0x00000038
506#define OTPC_VSEL 0x00000006
507#define OTPC_SELVL 0x00000001
508
509/* OTP regions (Word offsets from otp size) */
510#define OTP_SWLIM_OFF (-4)
511#define OTP_CIDBASE_OFF 0
512#define OTP_CIDLIM_OFF 4
513
514/* Predefined OTP words (Word offset from otp size) */
515#define OTP_BOUNDARY_OFF (-4)
516#define OTP_HWSIGN_OFF (-3)
517#define OTP_SWSIGN_OFF (-2)
518#define OTP_CIDSIGN_OFF (-1)
519#define OTP_CID_OFF 0
520#define OTP_PKG_OFF 1
521#define OTP_FID_OFF 2
522#define OTP_RSV_OFF 3
523#define OTP_LIM_OFF 4
524#define OTP_RD_OFF 4 /* Redundancy row starts here */
525#define OTP_RC0_OFF 28 /* Redundancy control word 1 */
526#define OTP_RC1_OFF 32 /* Redundancy control word 2 */
527#define OTP_RC_LIM_OFF 36 /* Redundancy control word end */
528
529#define OTP_HW_REGION OTPS_HW_PROTECT
530#define OTP_SW_REGION OTPS_SW_PROTECT
531#define OTP_CID_REGION OTPS_CID_PROTECT
532
533#if OTP_HW_REGION != OTP_HW_RGN
534#error "incompatible OTP_HW_RGN"
535#endif
536#if OTP_SW_REGION != OTP_SW_RGN
537#error "incompatible OTP_SW_RGN"
538#endif
539#if OTP_CID_REGION != OTP_CI_RGN
540#error "incompatible OTP_CI_RGN"
541#endif
542
543/* Redundancy entry definitions */
544#define OTP_RCE_ROW_SZ 6
545#define OTP_RCE_SIGN_MASK 0x7fff
546#define OTP_RCE_ROW_MASK 0x3f
547#define OTP_RCE_BITS 21
548#define OTP_RCE_SIGN_SZ 15
549#define OTP_RCE_BIT0 1
550
551#define OTP_WPR 4
552#define OTP_SIGNATURE 0x578a
553#define OTP_MAGIC 0x4e56
554
555static int hndotp_status(void *oh)
556{
557 otpinfo_t *oi = (otpinfo_t *) oh;
Jason Cooper90ea2292010-09-14 09:45:32 -0400558 return (int)(oi->hwprot | oi->signvalid);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700559}
560
561static int hndotp_size(void *oh)
562{
563 otpinfo_t *oi = (otpinfo_t *) oh;
Jason Cooper90ea2292010-09-14 09:45:32 -0400564 return (int)(oi->size);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700565}
566
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400567static uint16 hndotp_otpr(void *oh, chipcregs_t *cc, uint wn)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700568{
569 otpinfo_t *oi = (otpinfo_t *) oh;
570 osl_t *osh;
571 volatile uint16 *ptr;
572
573 ASSERT(wn < ((oi->size / 2) + OTP_RC_LIM_OFF));
574 ASSERT(cc != NULL);
575
576 osh = si_osh(oi->sih);
577
578 ptr = (volatile uint16 *)((volatile char *)cc + CC_SROM_OTP);
Jason Cooper90ea2292010-09-14 09:45:32 -0400579 return R_REG(osh, &ptr[wn]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700580}
581
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400582static uint16 hndotp_otproff(void *oh, chipcregs_t *cc, int woff)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700583{
584 otpinfo_t *oi = (otpinfo_t *) oh;
585 osl_t *osh;
586 volatile uint16 *ptr;
587
588 ASSERT(woff >= (-((int)oi->size / 2)));
589 ASSERT(woff < OTP_LIM_OFF);
590 ASSERT(cc != NULL);
591
592 osh = si_osh(oi->sih);
593
594 ptr = (volatile uint16 *)((volatile char *)cc + CC_SROM_OTP);
595
Jason Cooper90ea2292010-09-14 09:45:32 -0400596 return R_REG(osh, &ptr[(oi->size / 2) + woff]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700597}
598
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400599static uint16 hndotp_read_bit(void *oh, chipcregs_t *cc, uint idx)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700600{
601 otpinfo_t *oi = (otpinfo_t *) oh;
602 uint k, row, col;
603 uint32 otpp, st;
604 osl_t *osh;
605
606 osh = si_osh(oi->sih);
607 row = idx / 65;
608 col = idx % 65;
609
610 otpp = OTPP_START_BUSY | OTPP_READ |
611 ((row << OTPP_ROW_SHIFT) & OTPP_ROW_MASK) | (col & OTPP_COL_MASK);
612
613 W_REG(osh, &cc->otpprog, otpp);
614 st = R_REG(osh, &cc->otpprog);
615 for (k = 0;
616 ((st & OTPP_START_BUSY) == OTPP_START_BUSY) && (k < OTPP_TRIES);
617 k++)
618 st = R_REG(osh, &cc->otpprog);
619
620 if (k >= OTPP_TRIES) {
621 return 0xffff;
622 }
623 if (st & OTPP_READERR) {
624 return 0xffff;
625 }
626 st = (st & OTPP_VALUE_MASK) >> OTPP_VALUE_SHIFT;
627 return (uint16) st;
628}
629
Jason Coopera2627bc2010-09-14 09:45:31 -0400630static void *BCMNMIATTACHFN(hndotp_init) (si_t *sih)
631{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700632 uint idx;
633 chipcregs_t *cc;
634 otpinfo_t *oi;
635 uint32 cap = 0, clkdiv, otpdiv = 0;
636 void *ret = NULL;
637 osl_t *osh;
638
639 oi = &otpinfo;
640
641 idx = si_coreidx(sih);
642 osh = si_osh(oi->sih);
643
644 /* Check for otp */
Jason Cooperca8c1e52010-09-14 09:45:33 -0400645 cc = si_setcoreidx(sih, SI_CC_IDX);
646 if (cc != NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700647 cap = R_REG(osh, &cc->capabilities);
648 if ((cap & CC_CAP_OTPSIZE) == 0) {
649 /* Nothing there */
650 goto out;
651 }
652
653 /* As of right now, support only 4320a2, 4311a1 and 4312 */
654 ASSERT((oi->ccrev == 12) || (oi->ccrev == 17)
655 || (oi->ccrev == 22));
656 if (!
657 ((oi->ccrev == 12) || (oi->ccrev == 17)
658 || (oi->ccrev == 22)))
659 return NULL;
660
661 /* Read the OTP byte size. chipcommon rev >= 18 has RCE so the size is
662 * 8 row (64 bytes) smaller
663 */
664 oi->size =
665 1 << (((cap & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT)
666 + CC_CAP_OTPSIZE_BASE);
667 if (oi->ccrev >= 18)
668 oi->size -= ((OTP_RC0_OFF - OTP_BOUNDARY_OFF) * 2);
669
670 oi->hwprot = (int)(R_REG(osh, &cc->otpstatus) & OTPS_PROTECT);
671 oi->boundary = -1;
672
673 /* Check the region signature */
674 if (hndotp_otproff(oi, cc, OTP_HWSIGN_OFF) == OTP_SIGNATURE) {
675 oi->signvalid |= OTP_HW_REGION;
676 oi->boundary = hndotp_otproff(oi, cc, OTP_BOUNDARY_OFF);
677 }
678
679 if (hndotp_otproff(oi, cc, OTP_SWSIGN_OFF) == OTP_SIGNATURE)
680 oi->signvalid |= OTP_SW_REGION;
681
682 if (hndotp_otproff(oi, cc, OTP_CIDSIGN_OFF) == OTP_SIGNATURE)
683 oi->signvalid |= OTP_CID_REGION;
684
685 /* Set OTP clkdiv for stability */
686 if (oi->ccrev == 22)
687 otpdiv = 12;
688
689 if (otpdiv) {
690 clkdiv = R_REG(osh, &cc->clkdiv);
691 clkdiv =
692 (clkdiv & ~CLKD_OTP) | (otpdiv << CLKD_OTP_SHIFT);
693 W_REG(osh, &cc->clkdiv, clkdiv);
694 }
695 OSL_DELAY(10);
696
697 ret = (void *)oi;
698 }
699
700 out: /* All done */
701 si_setcoreidx(sih, idx);
702
703 return ret;
704}
705
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400706static int hndotp_read_region(void *oh, int region, uint16 *data, uint *wlen)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700707{
708 otpinfo_t *oi = (otpinfo_t *) oh;
709 uint32 idx, st;
710 chipcregs_t *cc;
711 int i;
712
713 /* Only support HW region (no active chips use HND OTP SW region) */
714 ASSERT(region == OTP_HW_REGION);
715
716 /* Region empty? */
717 st = oi->hwprot | oi->signvalid;
718 if ((st & region) == 0)
719 return BCME_NOTFOUND;
720
721 *wlen =
722 ((int)*wlen < oi->boundary / 2) ? *wlen : (uint) oi->boundary / 2;
723
724 idx = si_coreidx(oi->sih);
725 cc = si_setcoreidx(oi->sih, SI_CC_IDX);
726 ASSERT(cc != NULL);
727
728 for (i = 0; i < (int)*wlen; i++)
729 data[i] = hndotp_otpr(oh, cc, i);
730
731 si_setcoreidx(oi->sih, idx);
732
733 return 0;
734}
735
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400736static int hndotp_nvread(void *oh, char *data, uint *len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700737{
738 int rc = 0;
739 otpinfo_t *oi = (otpinfo_t *) oh;
740 uint32 base, bound, lim = 0, st;
741 int i, chunk, gchunks, tsz = 0;
742 uint32 idx;
743 chipcregs_t *cc;
744 uint offset;
745 uint16 *rawotp = NULL;
746
747 /* save the orig core */
748 idx = si_coreidx(oi->sih);
749 cc = si_setcoreidx(oi->sih, SI_CC_IDX);
750 ASSERT(cc != NULL);
751
752 st = hndotp_status(oh);
753 if (!(st & (OTP_HW_REGION | OTP_SW_REGION))) {
754 rc = -1;
755 goto out;
756 }
757
758 /* Read the whole otp so we can easily manipulate it */
759 lim = hndotp_size(oh);
Jason Cooperca8c1e52010-09-14 09:45:33 -0400760 rawotp = MALLOC(si_osh(oi->sih), lim);
761 if (rawotp == NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700762 rc = -2;
763 goto out;
764 }
765 for (i = 0; i < (int)(lim / 2); i++)
766 rawotp[i] = hndotp_otpr(oh, cc, i);
767
768 if ((st & OTP_HW_REGION) == 0) {
769 /* This could be a programming failure in the first
770 * chunk followed by one or more good chunks
771 */
772 for (i = 0; i < (int)(lim / 2); i++)
773 if (rawotp[i] == OTP_MAGIC)
774 break;
775
776 if (i < (int)(lim / 2)) {
777 base = i;
778 bound = (i * 2) + rawotp[i + 1];
779 } else {
780 rc = -3;
781 goto out;
782 }
783 } else {
784 bound = rawotp[(lim / 2) + OTP_BOUNDARY_OFF];
785
786 /* There are two cases: 1) The whole otp is used as nvram
787 * and 2) There is a hardware header followed by nvram.
788 */
789 if (rawotp[0] == OTP_MAGIC) {
790 base = 0;
791 } else
792 base = bound;
793 }
794
795 /* Find and copy the data */
796
797 chunk = 0;
798 gchunks = 0;
799 i = base / 2;
800 offset = 0;
801 while ((i < (int)(lim / 2)) && (rawotp[i] == OTP_MAGIC)) {
802 int dsz, rsz = rawotp[i + 1];
803
804 if (((i * 2) + rsz) >= (int)lim) {
805 /* Bad length, try to find another chunk anyway */
806 rsz = 6;
807 }
Jason Cooper29c42752010-09-14 09:45:43 -0400808 if (hndcrc16((uint8 *) &rawotp[i], rsz,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700809 CRC16_INIT_VALUE) == CRC16_GOOD_VALUE) {
810 /* Good crc, copy the vars */
811 gchunks++;
812 dsz = rsz - 6;
813 tsz += dsz;
814 if (offset + dsz >= *len) {
815 goto out;
816 }
817 bcopy((char *)&rawotp[i + 2], &data[offset], dsz);
818 offset += dsz;
819 /* Remove extra null characters at the end */
820 while (offset > 1 &&
821 data[offset - 1] == 0 && data[offset - 2] == 0)
822 offset--;
823 i += rsz / 2;
824 } else {
825 /* bad length or crc didn't check, try to find the next set */
826 if (rawotp[i + (rsz / 2)] == OTP_MAGIC) {
827 /* Assume length is good */
828 i += rsz / 2;
829 } else {
830 while (++i < (int)(lim / 2))
831 if (rawotp[i] == OTP_MAGIC)
832 break;
833 }
834 }
835 chunk++;
836 }
837
838 *len = offset;
839
840 out:
841 if (rawotp)
842 MFREE(si_osh(oi->sih), rawotp, lim);
843 si_setcoreidx(oi->sih, idx);
844
845 return rc;
846}
847
848static otp_fn_t hndotp_fn = {
849 (otp_size_t) hndotp_size,
850 (otp_read_bit_t) hndotp_read_bit,
851
852 (otp_init_t) hndotp_init,
853 (otp_read_region_t) hndotp_read_region,
854 (otp_nvread_t) hndotp_nvread,
855
856 (otp_status_t) hndotp_status
857};
858
859#endif /* BCMHNDOTP */
860
861/*
862 * Common Code: Compiled for IPX / HND / AUTO
863 * otp_status()
864 * otp_size()
865 * otp_read_bit()
866 * otp_init()
867 * otp_read_region()
868 * otp_nvread()
869 */
870
871int otp_status(void *oh)
872{
873 otpinfo_t *oi = (otpinfo_t *) oh;
874
875 return oi->fn->status(oh);
876}
877
878int otp_size(void *oh)
879{
880 otpinfo_t *oi = (otpinfo_t *) oh;
881
882 return oi->fn->size(oh);
883}
884
885uint16 otp_read_bit(void *oh, uint offset)
886{
887 otpinfo_t *oi = (otpinfo_t *) oh;
888 uint idx = si_coreidx(oi->sih);
889 chipcregs_t *cc = si_setcoreidx(oi->sih, SI_CC_IDX);
890 uint16 readBit = (uint16) oi->fn->read_bit(oh, cc, offset);
891 si_setcoreidx(oi->sih, idx);
892 return readBit;
893}
894
Jason Coopera2627bc2010-09-14 09:45:31 -0400895void *BCMNMIATTACHFN(otp_init) (si_t *sih)
896{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700897 otpinfo_t *oi;
898 void *ret = NULL;
899
900 oi = &otpinfo;
901 bzero(oi, sizeof(otpinfo_t));
902
903 oi->ccrev = sih->ccrev;
904
905#ifdef BCMIPXOTP
906 if (OTPTYPE_IPX(oi->ccrev))
907 oi->fn = &ipxotp_fn;
908#endif
909
910#ifdef BCMHNDOTP
911 if (OTPTYPE_HND(oi->ccrev))
912 oi->fn = &hndotp_fn;
913#endif
914
915 if (oi->fn == NULL) {
916 return NULL;
917 }
918
919 oi->sih = sih;
920 oi->osh = si_osh(oi->sih);
921
922 ret = (oi->fn->init) (sih);
923
924 return ret;
925}
926
927int
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400928BCMNMIATTACHFN(otp_read_region) (si_t *sih, int region, uint16 *data,
929 uint *wlen) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700930 bool wasup = FALSE;
931 void *oh;
932 int err = 0;
933
Jason Cooperca8c1e52010-09-14 09:45:33 -0400934 wasup = si_is_otp_powered(sih);
935 if (!wasup)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700936 si_otp_power(sih, TRUE);
937
938 if (!si_is_otp_powered(sih) || si_is_otp_disabled(sih)) {
939 err = BCME_NOTREADY;
940 goto out;
941 }
942
943 oh = otp_init(sih);
944 if (oh == NULL) {
945 err = BCME_ERROR;
946 goto out;
947 }
948
949 err = (((otpinfo_t *) oh)->fn->read_region) (oh, region, data, wlen);
950
951 out:
952 if (!wasup)
953 si_otp_power(sih, FALSE);
954
955 return err;
956}
957
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400958int otp_nvread(void *oh, char *data, uint *len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700959{
960 otpinfo_t *oi = (otpinfo_t *) oh;
961
962 return oi->fn->nvread(oh, data, len);
963}