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