blob: 6fa04ed9350198908b9aee1de3a611836ee231a1 [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
Randy Dunlap01d11442010-12-01 11:16:35 -080017#include <linux/delay.h>
Brett Rudley33279892010-10-01 18:03:27 -070018#include <linux/kernel.h>
19#include <linux/string.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070020#include <bcmdefs.h>
21#include <osl.h>
Brett Rudleyc6ac24e2010-10-26 11:55:23 -070022#include <linux/module.h>
23#include <linux/pci.h>
Henry Ptasinskia9533e72010-09-08 21:04:42 -070024#include <bcmdevs.h>
25#include <bcmutils.h>
26#include <siutils.h>
27#include <bcmendian.h>
28#include <hndsoc.h>
29#include <sbchipc.h>
30#include <bcmotp.h>
31#include "siutils_priv.h"
32
33/*
34 * There are two different OTP controllers so far:
35 * 1. new IPX OTP controller: chipc 21, >=23
36 * 2. older HND OTP controller: chipc 12, 17, 22
37 *
38 * Define BCMHNDOTP to include support for the HND OTP controller.
39 * Define BCMIPXOTP to include support for the IPX OTP controller.
40 *
41 * NOTE 1: More than one may be defined
42 * NOTE 2: If none are defined, the default is to include them all.
43 */
44
45#if !defined(BCMHNDOTP) && !defined(BCMIPXOTP)
46#define BCMHNDOTP 1
47#define BCMIPXOTP 1
48#endif
49
50#define OTPTYPE_HND(ccrev) ((ccrev) < 21 || (ccrev) == 22)
51#define OTPTYPE_IPX(ccrev) ((ccrev) == 21 || (ccrev) >= 23)
52
53#define OTPP_TRIES 10000000 /* # of tries for OTPP */
54
55#ifdef BCMIPXOTP
56#define MAXNUMRDES 9 /* Maximum OTP redundancy entries */
57#endif
58
59/* OTP common function type */
60typedef int (*otp_status_t) (void *oh);
61typedef int (*otp_size_t) (void *oh);
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040062typedef void *(*otp_init_t) (si_t *sih);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070063typedef u16(*otp_read_bit_t) (void *oh, chipcregs_t *cc, uint off);
64typedef int (*otp_read_region_t) (si_t *sih, int region, u16 *data,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -040065 uint *wlen);
66typedef int (*otp_nvread_t) (void *oh, char *data, uint *len);
Henry Ptasinskia9533e72010-09-08 21:04:42 -070067
68/* OTP function struct */
69typedef struct otp_fn_s {
70 otp_size_t size;
71 otp_read_bit_t read_bit;
72 otp_init_t init;
73 otp_read_region_t read_region;
74 otp_nvread_t nvread;
75 otp_status_t status;
76} otp_fn_t;
77
78typedef struct {
79 uint ccrev; /* chipc revision */
80 otp_fn_t *fn; /* OTP functions */
81 si_t *sih; /* Saved sb handle */
Brett Rudleye69284f2010-11-16 15:45:48 -080082 struct osl_info *osh;
Henry Ptasinskia9533e72010-09-08 21:04:42 -070083
84#ifdef BCMIPXOTP
85 /* IPX OTP section */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070086 u16 wsize; /* Size of otp in words */
87 u16 rows; /* Geometry */
88 u16 cols; /* Geometry */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -070089 u32 status; /* Flag bits (lock/prog/rv).
Henry Ptasinskia9533e72010-09-08 21:04:42 -070090 * (Reflected only when OTP is power cycled)
91 */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -070092 u16 hwbase; /* hardware subregion offset */
93 u16 hwlim; /* hardware subregion boundary */
94 u16 swbase; /* software subregion offset */
95 u16 swlim; /* software subregion boundary */
96 u16 fbase; /* fuse subregion offset */
97 u16 flim; /* fuse subregion boundary */
Henry Ptasinskia9533e72010-09-08 21:04:42 -070098 int otpgu_base; /* offset to General Use Region */
99#endif /* BCMIPXOTP */
100
101#ifdef BCMHNDOTP
102 /* HND OTP section */
103 uint size; /* Size of otp in bytes */
104 uint hwprot; /* Hardware protection bits */
105 uint signvalid; /* Signature valid bits */
106 int boundary; /* hw/sw boundary */
107#endif /* BCMHNDOTP */
108} otpinfo_t;
109
110static otpinfo_t otpinfo;
111
112/*
113 * IPX OTP Code
114 *
115 * Exported functions:
116 * ipxotp_status()
117 * ipxotp_size()
118 * ipxotp_init()
119 * ipxotp_read_bit()
120 * ipxotp_read_region()
121 * ipxotp_nvread()
122 *
123 */
124
125#ifdef BCMIPXOTP
126
127#define HWSW_RGN(rgn) (((rgn) == OTP_HW_RGN) ? "h/w" : "s/w")
128
129/* OTP layout */
130/* CC revs 21, 24 and 27 OTP General Use Region word offset */
131#define REVA4_OTPGU_BASE 12
132
133/* CC revs 23, 25, 26, 28 and above OTP General Use Region word offset */
134#define REVB8_OTPGU_BASE 20
135
136/* CC rev 36 OTP General Use Region word offset */
137#define REV36_OTPGU_BASE 12
138
139/* Subregion word offsets in General Use region */
140#define OTPGU_HSB_OFF 0
141#define OTPGU_SFB_OFF 1
142#define OTPGU_CI_OFF 2
143#define OTPGU_P_OFF 3
144#define OTPGU_SROM_OFF 4
145
146/* Flag bit offsets in General Use region */
147#define OTPGU_HWP_OFF 60
148#define OTPGU_SWP_OFF 61
149#define OTPGU_CIP_OFF 62
150#define OTPGU_FUSEP_OFF 63
151#define OTPGU_CIP_MSK 0x4000
152#define OTPGU_P_MSK 0xf000
153#define OTPGU_P_SHIFT (OTPGU_HWP_OFF % 16)
154
155/* OTP Size */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -0700156#define OTP_SZ_FU_324 ((roundup(324, 8))/8) /* 324 bits */
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700157#define OTP_SZ_FU_288 (288/8) /* 288 bits */
158#define OTP_SZ_FU_216 (216/8) /* 216 bits */
159#define OTP_SZ_FU_72 (72/8) /* 72 bits */
160#define OTP_SZ_CHECKSUM (16/8) /* 16 bits */
161#define OTP4315_SWREG_SZ 178 /* 178 bytes */
162#define OTP_SZ_FU_144 (144/8) /* 144 bits */
163
164static int ipxotp_status(void *oh)
165{
166 otpinfo_t *oi = (otpinfo_t *) oh;
167 return (int)(oi->status);
168}
169
170/* Return size in bytes */
171static int ipxotp_size(void *oh)
172{
173 otpinfo_t *oi = (otpinfo_t *) oh;
174 return (int)oi->wsize * 2;
175}
176
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700177static u16 ipxotp_otpr(void *oh, chipcregs_t *cc, uint wn)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700178{
179 otpinfo_t *oi;
180
181 oi = (otpinfo_t *) oh;
182
183 ASSERT(wn < oi->wsize);
184 ASSERT(cc != NULL);
185
186 return R_REG(oi->osh, &cc->sromotp[wn]);
187}
188
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700189static u16 ipxotp_read_bit(void *oh, chipcregs_t *cc, uint off)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700190{
191 otpinfo_t *oi = (otpinfo_t *) oh;
192 uint k, row, col;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700193 u32 otpp, st;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700194
195 row = off / oi->cols;
196 col = off % oi->cols;
197
198 otpp = OTPP_START_BUSY |
199 ((OTPPOC_READ << OTPP_OC_SHIFT) & OTPP_OC_MASK) |
200 ((row << OTPP_ROW_SHIFT) & OTPP_ROW_MASK) |
201 ((col << OTPP_COL_SHIFT) & OTPP_COL_MASK);
202 W_REG(oi->osh, &cc->otpprog, otpp);
203
204 for (k = 0;
205 ((st = R_REG(oi->osh, &cc->otpprog)) & OTPP_START_BUSY)
Jason Cooper62145822010-09-14 09:45:34 -0400206 && (k < OTPP_TRIES); k++)
207 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700208 if (k >= OTPP_TRIES) {
209 return 0xffff;
210 }
211 if (st & OTPP_READERR) {
212 return 0xffff;
213 }
214 st = (st & OTPP_VALUE_MASK) >> OTPP_VALUE_SHIFT;
215
216 return (int)st;
217}
218
219/* Calculate max HW/SW region byte size by substracting fuse region and checksum size,
220 * osizew is oi->wsize (OTP size - GU size) in words
221 */
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400222static int ipxotp_max_rgnsz(si_t *sih, int osizew)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700223{
224 int ret = 0;
225
Arend van Sprieldfa26432010-12-02 15:44:51 +0100226 switch (sih->chip) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700227 case BCM43224_CHIP_ID:
228 case BCM43225_CHIP_ID:
229 ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM;
230 break;
231 case BCM4313_CHIP_ID:
232 ret = osizew * 2 - OTP_SZ_FU_72 - OTP_SZ_CHECKSUM;
233 break;
234 default:
235 ASSERT(0); /* Don't konw about this chip */
236 }
237
238 return ret;
239}
240
Jason Coopera9d0fff2010-10-11 10:03:00 -0400241static void _ipxotp_init(otpinfo_t *oi, chipcregs_t *cc)
Jason Coopera2627bc2010-09-14 09:45:31 -0400242{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700243 uint k;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700244 u32 otpp, st;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700245
246 /* record word offset of General Use Region for various chipcommon revs */
247 if (oi->sih->ccrev == 21 || oi->sih->ccrev == 24
248 || oi->sih->ccrev == 27) {
249 oi->otpgu_base = REVA4_OTPGU_BASE;
250 } else if (oi->sih->ccrev == 36) {
251 /* OTP size greater than equal to 2KB (128 words), otpgu_base is similar to rev23 */
252 if (oi->wsize >= 128)
253 oi->otpgu_base = REVB8_OTPGU_BASE;
254 else
255 oi->otpgu_base = REV36_OTPGU_BASE;
256 } else if (oi->sih->ccrev == 23 || oi->sih->ccrev >= 25) {
257 oi->otpgu_base = REVB8_OTPGU_BASE;
258 }
259
260 /* First issue an init command so the status is up to date */
261 otpp =
262 OTPP_START_BUSY | ((OTPPOC_INIT << OTPP_OC_SHIFT) & OTPP_OC_MASK);
263
264 W_REG(oi->osh, &cc->otpprog, otpp);
265 for (k = 0;
266 ((st = R_REG(oi->osh, &cc->otpprog)) & OTPP_START_BUSY)
Jason Cooper62145822010-09-14 09:45:34 -0400267 && (k < OTPP_TRIES); k++)
268 ;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700269 if (k >= OTPP_TRIES) {
270 return;
271 }
272
273 /* Read OTP lock bits and subregion programmed indication bits */
274 oi->status = R_REG(oi->osh, &cc->otpstatus);
275
Arend van Sprieldfa26432010-12-02 15:44:51 +0100276 if ((oi->sih->chip == BCM43224_CHIP_ID)
277 || (oi->sih->chip == BCM43225_CHIP_ID)) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700278 u32 p_bits;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700279 p_bits =
280 (ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_P_OFF) &
281 OTPGU_P_MSK)
282 >> OTPGU_P_SHIFT;
283 oi->status |= (p_bits << OTPS_GUP_SHIFT);
284 }
285
286 /*
287 * h/w region base and fuse region limit are fixed to the top and
288 * the bottom of the general use region. Everything else can be flexible.
289 */
290 oi->hwbase = oi->otpgu_base + OTPGU_SROM_OFF;
291 oi->hwlim = oi->wsize;
292 if (oi->status & OTPS_GUP_HW) {
293 oi->hwlim =
294 ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_HSB_OFF) / 16;
295 oi->swbase = oi->hwlim;
296 } else
297 oi->swbase = oi->hwbase;
298
299 /* subtract fuse and checksum from beginning */
300 oi->swlim = ipxotp_max_rgnsz(oi->sih, oi->wsize) / 2;
301
302 if (oi->status & OTPS_GUP_SW) {
303 oi->swlim =
304 ipxotp_otpr(oi, cc, oi->otpgu_base + OTPGU_SFB_OFF) / 16;
305 oi->fbase = oi->swlim;
306 } else
307 oi->fbase = oi->swbase;
308
309 oi->flim = oi->wsize;
310}
311
Jason Coopera9d0fff2010-10-11 10:03:00 -0400312static void *ipxotp_init(si_t *sih)
Jason Coopera2627bc2010-09-14 09:45:31 -0400313{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700314 uint idx;
315 chipcregs_t *cc;
316 otpinfo_t *oi;
317
318 /* Make sure we're running IPX OTP */
319 ASSERT(OTPTYPE_IPX(sih->ccrev));
320 if (!OTPTYPE_IPX(sih->ccrev))
321 return NULL;
322
323 /* Make sure OTP is not disabled */
324 if (si_is_otp_disabled(sih)) {
325 return NULL;
326 }
327
328 /* Make sure OTP is powered up */
329 if (!si_is_otp_powered(sih)) {
330 return NULL;
331 }
332
333 oi = &otpinfo;
334
335 /* Check for otp size */
336 switch ((sih->cccaps & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT) {
337 case 0:
338 /* Nothing there */
339 return NULL;
340 case 1: /* 32x64 */
341 oi->rows = 32;
342 oi->cols = 64;
343 oi->wsize = 128;
344 break;
345 case 2: /* 64x64 */
346 oi->rows = 64;
347 oi->cols = 64;
348 oi->wsize = 256;
349 break;
350 case 5: /* 96x64 */
351 oi->rows = 96;
352 oi->cols = 64;
353 oi->wsize = 384;
354 break;
355 case 7: /* 16x64 *//* 1024 bits */
356 oi->rows = 16;
357 oi->cols = 64;
358 oi->wsize = 64;
359 break;
360 default:
361 /* Don't know the geometry */
362 return NULL;
363 }
364
365 /* Retrieve OTP region info */
366 idx = si_coreidx(sih);
367 cc = si_setcoreidx(sih, SI_CC_IDX);
368 ASSERT(cc != NULL);
369
370 _ipxotp_init(oi, cc);
371
372 si_setcoreidx(sih, idx);
373
374 return (void *)oi;
375}
376
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700377static int ipxotp_read_region(void *oh, int region, u16 *data, uint *wlen)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700378{
379 otpinfo_t *oi = (otpinfo_t *) oh;
380 uint idx;
381 chipcregs_t *cc;
382 uint base, i, sz;
383
384 /* Validate region selection */
385 switch (region) {
386 case OTP_HW_RGN:
387 sz = (uint) oi->hwlim - oi->hwbase;
388 if (!(oi->status & OTPS_GUP_HW)) {
389 *wlen = sz;
390 return BCME_NOTFOUND;
391 }
392 if (*wlen < sz) {
393 *wlen = sz;
394 return BCME_BUFTOOSHORT;
395 }
396 base = oi->hwbase;
397 break;
398 case OTP_SW_RGN:
399 sz = ((uint) oi->swlim - oi->swbase);
400 if (!(oi->status & OTPS_GUP_SW)) {
401 *wlen = sz;
402 return BCME_NOTFOUND;
403 }
404 if (*wlen < sz) {
405 *wlen = sz;
406 return BCME_BUFTOOSHORT;
407 }
408 base = oi->swbase;
409 break;
410 case OTP_CI_RGN:
411 sz = OTPGU_CI_SZ;
412 if (!(oi->status & OTPS_GUP_CI)) {
413 *wlen = sz;
414 return BCME_NOTFOUND;
415 }
416 if (*wlen < sz) {
417 *wlen = sz;
418 return BCME_BUFTOOSHORT;
419 }
420 base = oi->otpgu_base + OTPGU_CI_OFF;
421 break;
422 case OTP_FUSE_RGN:
423 sz = (uint) oi->flim - oi->fbase;
424 if (!(oi->status & OTPS_GUP_FUSE)) {
425 *wlen = sz;
426 return BCME_NOTFOUND;
427 }
428 if (*wlen < sz) {
429 *wlen = sz;
430 return BCME_BUFTOOSHORT;
431 }
432 base = oi->fbase;
433 break;
434 case OTP_ALL_RGN:
435 sz = ((uint) oi->flim - oi->hwbase);
436 if (!(oi->status & (OTPS_GUP_HW | OTPS_GUP_SW))) {
437 *wlen = sz;
438 return BCME_NOTFOUND;
439 }
440 if (*wlen < sz) {
441 *wlen = sz;
442 return BCME_BUFTOOSHORT;
443 }
444 base = oi->hwbase;
445 break;
446 default:
447 return BCME_BADARG;
448 }
449
450 idx = si_coreidx(oi->sih);
451 cc = si_setcoreidx(oi->sih, SI_CC_IDX);
452 ASSERT(cc != NULL);
453
454 /* Read the data */
455 for (i = 0; i < sz; i++)
456 data[i] = ipxotp_otpr(oh, cc, base + i);
457
458 si_setcoreidx(oi->sih, idx);
459 *wlen = sz;
460 return 0;
461}
462
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400463static int ipxotp_nvread(void *oh, char *data, uint *len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700464{
465 return BCME_UNSUPPORTED;
466}
467
468static otp_fn_t ipxotp_fn = {
469 (otp_size_t) ipxotp_size,
470 (otp_read_bit_t) ipxotp_read_bit,
471
472 (otp_init_t) ipxotp_init,
473 (otp_read_region_t) ipxotp_read_region,
474 (otp_nvread_t) ipxotp_nvread,
475
476 (otp_status_t) ipxotp_status
477};
478
479#endif /* BCMIPXOTP */
480
481/*
482 * HND OTP Code
483 *
484 * Exported functions:
485 * hndotp_status()
486 * hndotp_size()
487 * hndotp_init()
488 * hndotp_read_bit()
489 * hndotp_read_region()
490 * hndotp_nvread()
491 *
492 */
493
494#ifdef BCMHNDOTP
495
496/* Fields in otpstatus */
497#define OTPS_PROGFAIL 0x80000000
498#define OTPS_PROTECT 0x00000007
499#define OTPS_HW_PROTECT 0x00000001
500#define OTPS_SW_PROTECT 0x00000002
501#define OTPS_CID_PROTECT 0x00000004
502#define OTPS_RCEV_MSK 0x00003f00
503#define OTPS_RCEV_SHIFT 8
504
505/* Fields in the otpcontrol register */
506#define OTPC_RECWAIT 0xff000000
507#define OTPC_PROGWAIT 0x00ffff00
508#define OTPC_PRW_SHIFT 8
509#define OTPC_MAXFAIL 0x00000038
510#define OTPC_VSEL 0x00000006
511#define OTPC_SELVL 0x00000001
512
513/* OTP regions (Word offsets from otp size) */
514#define OTP_SWLIM_OFF (-4)
515#define OTP_CIDBASE_OFF 0
516#define OTP_CIDLIM_OFF 4
517
518/* Predefined OTP words (Word offset from otp size) */
519#define OTP_BOUNDARY_OFF (-4)
520#define OTP_HWSIGN_OFF (-3)
521#define OTP_SWSIGN_OFF (-2)
522#define OTP_CIDSIGN_OFF (-1)
523#define OTP_CID_OFF 0
524#define OTP_PKG_OFF 1
525#define OTP_FID_OFF 2
526#define OTP_RSV_OFF 3
527#define OTP_LIM_OFF 4
528#define OTP_RD_OFF 4 /* Redundancy row starts here */
529#define OTP_RC0_OFF 28 /* Redundancy control word 1 */
530#define OTP_RC1_OFF 32 /* Redundancy control word 2 */
531#define OTP_RC_LIM_OFF 36 /* Redundancy control word end */
532
533#define OTP_HW_REGION OTPS_HW_PROTECT
534#define OTP_SW_REGION OTPS_SW_PROTECT
535#define OTP_CID_REGION OTPS_CID_PROTECT
536
537#if OTP_HW_REGION != OTP_HW_RGN
538#error "incompatible OTP_HW_RGN"
539#endif
540#if OTP_SW_REGION != OTP_SW_RGN
541#error "incompatible OTP_SW_RGN"
542#endif
543#if OTP_CID_REGION != OTP_CI_RGN
544#error "incompatible OTP_CI_RGN"
545#endif
546
547/* Redundancy entry definitions */
548#define OTP_RCE_ROW_SZ 6
549#define OTP_RCE_SIGN_MASK 0x7fff
550#define OTP_RCE_ROW_MASK 0x3f
551#define OTP_RCE_BITS 21
552#define OTP_RCE_SIGN_SZ 15
553#define OTP_RCE_BIT0 1
554
555#define OTP_WPR 4
556#define OTP_SIGNATURE 0x578a
557#define OTP_MAGIC 0x4e56
558
559static int hndotp_status(void *oh)
560{
561 otpinfo_t *oi = (otpinfo_t *) oh;
Jason Cooper90ea2292010-09-14 09:45:32 -0400562 return (int)(oi->hwprot | oi->signvalid);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700563}
564
565static int hndotp_size(void *oh)
566{
567 otpinfo_t *oi = (otpinfo_t *) oh;
Jason Cooper90ea2292010-09-14 09:45:32 -0400568 return (int)(oi->size);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700569}
570
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700571static u16 hndotp_otpr(void *oh, chipcregs_t *cc, uint wn)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700572{
573 otpinfo_t *oi = (otpinfo_t *) oh;
Brett Rudleye69284f2010-11-16 15:45:48 -0800574 struct osl_info *osh;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700575 volatile u16 *ptr;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700576
577 ASSERT(wn < ((oi->size / 2) + OTP_RC_LIM_OFF));
578 ASSERT(cc != NULL);
579
580 osh = si_osh(oi->sih);
581
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700582 ptr = (volatile u16 *)((volatile char *)cc + CC_SROM_OTP);
Jason Cooper90ea2292010-09-14 09:45:32 -0400583 return R_REG(osh, &ptr[wn]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700584}
585
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700586static u16 hndotp_otproff(void *oh, chipcregs_t *cc, int woff)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700587{
588 otpinfo_t *oi = (otpinfo_t *) oh;
Brett Rudleye69284f2010-11-16 15:45:48 -0800589 struct osl_info *osh;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700590 volatile u16 *ptr;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700591
592 ASSERT(woff >= (-((int)oi->size / 2)));
593 ASSERT(woff < OTP_LIM_OFF);
594 ASSERT(cc != NULL);
595
596 osh = si_osh(oi->sih);
597
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700598 ptr = (volatile u16 *)((volatile char *)cc + CC_SROM_OTP);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700599
Jason Cooper90ea2292010-09-14 09:45:32 -0400600 return R_REG(osh, &ptr[(oi->size / 2) + woff]);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700601}
602
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700603static u16 hndotp_read_bit(void *oh, chipcregs_t *cc, uint idx)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700604{
605 otpinfo_t *oi = (otpinfo_t *) oh;
606 uint k, row, col;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700607 u32 otpp, st;
Brett Rudleye69284f2010-11-16 15:45:48 -0800608 struct osl_info *osh;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700609
610 osh = si_osh(oi->sih);
611 row = idx / 65;
612 col = idx % 65;
613
614 otpp = OTPP_START_BUSY | OTPP_READ |
615 ((row << OTPP_ROW_SHIFT) & OTPP_ROW_MASK) | (col & OTPP_COL_MASK);
616
617 W_REG(osh, &cc->otpprog, otpp);
618 st = R_REG(osh, &cc->otpprog);
619 for (k = 0;
620 ((st & OTPP_START_BUSY) == OTPP_START_BUSY) && (k < OTPP_TRIES);
621 k++)
622 st = R_REG(osh, &cc->otpprog);
623
624 if (k >= OTPP_TRIES) {
625 return 0xffff;
626 }
627 if (st & OTPP_READERR) {
628 return 0xffff;
629 }
630 st = (st & OTPP_VALUE_MASK) >> OTPP_VALUE_SHIFT;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700631 return (u16) st;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700632}
633
Jason Coopera9d0fff2010-10-11 10:03:00 -0400634static void *hndotp_init(si_t *sih)
Jason Coopera2627bc2010-09-14 09:45:31 -0400635{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700636 uint idx;
637 chipcregs_t *cc;
638 otpinfo_t *oi;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700639 u32 cap = 0, clkdiv, otpdiv = 0;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700640 void *ret = NULL;
Brett Rudleye69284f2010-11-16 15:45:48 -0800641 struct osl_info *osh;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700642
643 oi = &otpinfo;
644
645 idx = si_coreidx(sih);
646 osh = si_osh(oi->sih);
647
648 /* Check for otp */
Jason Cooperca8c1e52010-09-14 09:45:33 -0400649 cc = si_setcoreidx(sih, SI_CC_IDX);
650 if (cc != NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700651 cap = R_REG(osh, &cc->capabilities);
652 if ((cap & CC_CAP_OTPSIZE) == 0) {
653 /* Nothing there */
654 goto out;
655 }
656
657 /* As of right now, support only 4320a2, 4311a1 and 4312 */
658 ASSERT((oi->ccrev == 12) || (oi->ccrev == 17)
659 || (oi->ccrev == 22));
660 if (!
661 ((oi->ccrev == 12) || (oi->ccrev == 17)
662 || (oi->ccrev == 22)))
663 return NULL;
664
665 /* Read the OTP byte size. chipcommon rev >= 18 has RCE so the size is
666 * 8 row (64 bytes) smaller
667 */
668 oi->size =
669 1 << (((cap & CC_CAP_OTPSIZE) >> CC_CAP_OTPSIZE_SHIFT)
670 + CC_CAP_OTPSIZE_BASE);
671 if (oi->ccrev >= 18)
672 oi->size -= ((OTP_RC0_OFF - OTP_BOUNDARY_OFF) * 2);
673
674 oi->hwprot = (int)(R_REG(osh, &cc->otpstatus) & OTPS_PROTECT);
675 oi->boundary = -1;
676
677 /* Check the region signature */
678 if (hndotp_otproff(oi, cc, OTP_HWSIGN_OFF) == OTP_SIGNATURE) {
679 oi->signvalid |= OTP_HW_REGION;
680 oi->boundary = hndotp_otproff(oi, cc, OTP_BOUNDARY_OFF);
681 }
682
683 if (hndotp_otproff(oi, cc, OTP_SWSIGN_OFF) == OTP_SIGNATURE)
684 oi->signvalid |= OTP_SW_REGION;
685
686 if (hndotp_otproff(oi, cc, OTP_CIDSIGN_OFF) == OTP_SIGNATURE)
687 oi->signvalid |= OTP_CID_REGION;
688
689 /* Set OTP clkdiv for stability */
690 if (oi->ccrev == 22)
691 otpdiv = 12;
692
693 if (otpdiv) {
694 clkdiv = R_REG(osh, &cc->clkdiv);
695 clkdiv =
696 (clkdiv & ~CLKD_OTP) | (otpdiv << CLKD_OTP_SHIFT);
697 W_REG(osh, &cc->clkdiv, clkdiv);
698 }
mike.rapoport@gmail.com73831412010-10-13 00:09:07 +0200699 udelay(10);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700700
701 ret = (void *)oi;
702 }
703
704 out: /* All done */
705 si_setcoreidx(sih, idx);
706
707 return ret;
708}
709
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700710static int hndotp_read_region(void *oh, int region, u16 *data, uint *wlen)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700711{
712 otpinfo_t *oi = (otpinfo_t *) oh;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700713 u32 idx, st;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700714 chipcregs_t *cc;
715 int i;
716
717 /* Only support HW region (no active chips use HND OTP SW region) */
718 ASSERT(region == OTP_HW_REGION);
719
720 /* Region empty? */
721 st = oi->hwprot | oi->signvalid;
722 if ((st & region) == 0)
723 return BCME_NOTFOUND;
724
725 *wlen =
726 ((int)*wlen < oi->boundary / 2) ? *wlen : (uint) oi->boundary / 2;
727
728 idx = si_coreidx(oi->sih);
729 cc = si_setcoreidx(oi->sih, SI_CC_IDX);
730 ASSERT(cc != NULL);
731
732 for (i = 0; i < (int)*wlen; i++)
733 data[i] = hndotp_otpr(oh, cc, i);
734
735 si_setcoreidx(oi->sih, idx);
736
737 return 0;
738}
739
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400740static int hndotp_nvread(void *oh, char *data, uint *len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700741{
742 int rc = 0;
743 otpinfo_t *oi = (otpinfo_t *) oh;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700744 u32 base, bound, lim = 0, st;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700745 int i, chunk, gchunks, tsz = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700746 u32 idx;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700747 chipcregs_t *cc;
748 uint offset;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700749 u16 *rawotp = NULL;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700750
751 /* save the orig core */
752 idx = si_coreidx(oi->sih);
753 cc = si_setcoreidx(oi->sih, SI_CC_IDX);
754 ASSERT(cc != NULL);
755
756 st = hndotp_status(oh);
757 if (!(st & (OTP_HW_REGION | OTP_SW_REGION))) {
758 rc = -1;
759 goto out;
760 }
761
762 /* Read the whole otp so we can easily manipulate it */
763 lim = hndotp_size(oh);
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +0200764 rawotp = kmalloc(lim, GFP_ATOMIC);
Jason Cooperca8c1e52010-09-14 09:45:33 -0400765 if (rawotp == NULL) {
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700766 rc = -2;
767 goto out;
768 }
769 for (i = 0; i < (int)(lim / 2); i++)
770 rawotp[i] = hndotp_otpr(oh, cc, i);
771
772 if ((st & OTP_HW_REGION) == 0) {
773 /* This could be a programming failure in the first
774 * chunk followed by one or more good chunks
775 */
776 for (i = 0; i < (int)(lim / 2); i++)
777 if (rawotp[i] == OTP_MAGIC)
778 break;
779
780 if (i < (int)(lim / 2)) {
781 base = i;
782 bound = (i * 2) + rawotp[i + 1];
783 } else {
784 rc = -3;
785 goto out;
786 }
787 } else {
788 bound = rawotp[(lim / 2) + OTP_BOUNDARY_OFF];
789
790 /* There are two cases: 1) The whole otp is used as nvram
791 * and 2) There is a hardware header followed by nvram.
792 */
793 if (rawotp[0] == OTP_MAGIC) {
794 base = 0;
795 } else
796 base = bound;
797 }
798
799 /* Find and copy the data */
800
801 chunk = 0;
802 gchunks = 0;
803 i = base / 2;
804 offset = 0;
805 while ((i < (int)(lim / 2)) && (rawotp[i] == OTP_MAGIC)) {
806 int dsz, rsz = rawotp[i + 1];
807
808 if (((i * 2) + rsz) >= (int)lim) {
809 /* Bad length, try to find another chunk anyway */
810 rsz = 6;
811 }
Greg Kroah-Hartman36ef9a12010-10-05 10:02:49 -0700812 if (hndcrc16((u8 *) &rawotp[i], rsz,
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700813 CRC16_INIT_VALUE) == CRC16_GOOD_VALUE) {
814 /* Good crc, copy the vars */
815 gchunks++;
816 dsz = rsz - 6;
817 tsz += dsz;
818 if (offset + dsz >= *len) {
819 goto out;
820 }
Stanislav Fomichev02160692011-02-15 01:05:10 +0300821 memcpy(&data[offset], &rawotp[i + 2], dsz);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700822 offset += dsz;
823 /* Remove extra null characters at the end */
824 while (offset > 1 &&
825 data[offset - 1] == 0 && data[offset - 2] == 0)
826 offset--;
827 i += rsz / 2;
828 } else {
829 /* bad length or crc didn't check, try to find the next set */
830 if (rawotp[i + (rsz / 2)] == OTP_MAGIC) {
831 /* Assume length is good */
832 i += rsz / 2;
833 } else {
834 while (++i < (int)(lim / 2))
835 if (rawotp[i] == OTP_MAGIC)
836 break;
837 }
838 }
839 chunk++;
840 }
841
842 *len = offset;
843
844 out:
845 if (rawotp)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +0200846 kfree(rawotp);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700847 si_setcoreidx(oi->sih, idx);
848
849 return rc;
850}
851
852static otp_fn_t hndotp_fn = {
853 (otp_size_t) hndotp_size,
854 (otp_read_bit_t) hndotp_read_bit,
855
856 (otp_init_t) hndotp_init,
857 (otp_read_region_t) hndotp_read_region,
858 (otp_nvread_t) hndotp_nvread,
859
860 (otp_status_t) hndotp_status
861};
862
863#endif /* BCMHNDOTP */
864
865/*
866 * Common Code: Compiled for IPX / HND / AUTO
867 * otp_status()
868 * otp_size()
869 * otp_read_bit()
870 * otp_init()
871 * otp_read_region()
872 * otp_nvread()
873 */
874
875int otp_status(void *oh)
876{
877 otpinfo_t *oi = (otpinfo_t *) oh;
878
879 return oi->fn->status(oh);
880}
881
882int otp_size(void *oh)
883{
884 otpinfo_t *oi = (otpinfo_t *) oh;
885
886 return oi->fn->size(oh);
887}
888
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700889u16 otp_read_bit(void *oh, uint offset)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700890{
891 otpinfo_t *oi = (otpinfo_t *) oh;
892 uint idx = si_coreidx(oi->sih);
893 chipcregs_t *cc = si_setcoreidx(oi->sih, SI_CC_IDX);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700894 u16 readBit = (u16) oi->fn->read_bit(oh, cc, offset);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700895 si_setcoreidx(oi->sih, idx);
896 return readBit;
897}
898
Jason Coopera9d0fff2010-10-11 10:03:00 -0400899void *otp_init(si_t *sih)
Jason Coopera2627bc2010-09-14 09:45:31 -0400900{
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700901 otpinfo_t *oi;
902 void *ret = NULL;
903
904 oi = &otpinfo;
Brett Rudley9249ede2010-11-30 20:09:49 -0800905 memset(oi, 0, sizeof(otpinfo_t));
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700906
907 oi->ccrev = sih->ccrev;
908
909#ifdef BCMIPXOTP
910 if (OTPTYPE_IPX(oi->ccrev))
911 oi->fn = &ipxotp_fn;
912#endif
913
914#ifdef BCMHNDOTP
915 if (OTPTYPE_HND(oi->ccrev))
916 oi->fn = &hndotp_fn;
917#endif
918
919 if (oi->fn == NULL) {
920 return NULL;
921 }
922
923 oi->sih = sih;
924 oi->osh = si_osh(oi->sih);
925
926 ret = (oi->fn->init) (sih);
927
928 return ret;
929}
930
931int
Jason Coopera9d0fff2010-10-11 10:03:00 -0400932otp_read_region(si_t *sih, int region, u16 *data,
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400933 uint *wlen) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700934 bool wasup = false;
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700935 void *oh;
936 int err = 0;
937
Jason Cooperca8c1e52010-09-14 09:45:33 -0400938 wasup = si_is_otp_powered(sih);
939 if (!wasup)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700940 si_otp_power(sih, true);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700941
942 if (!si_is_otp_powered(sih) || si_is_otp_disabled(sih)) {
943 err = BCME_NOTREADY;
944 goto out;
945 }
946
947 oh = otp_init(sih);
948 if (oh == NULL) {
949 err = BCME_ERROR;
950 goto out;
951 }
952
953 err = (((otpinfo_t *) oh)->fn->read_region) (oh, region, data, wlen);
954
955 out:
956 if (!wasup)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700957 si_otp_power(sih, false);
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700958
959 return err;
960}
961
Jason Cooper7cc4a4c2010-09-14 09:45:30 -0400962int otp_nvread(void *oh, char *data, uint *len)
Henry Ptasinskia9533e72010-09-08 21:04:42 -0700963{
964 otpinfo_t *oi = (otpinfo_t *) oh;
965
966 return oi->fn->nvread(oh, data, len);
967}