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