]>
Commit | Line | Data |
---|---|---|
d0b01a24 MF |
1 | /* |
2 | * cmd_otp.c - interface to Blackfin on-chip One-Time-Programmable memory | |
3 | * | |
4 | * Copyright (c) 2007-2008 Analog Devices Inc. | |
5 | * | |
6 | * Licensed under the GPL-2 or later. | |
7 | */ | |
8 | ||
9 | /* There are 512 128-bit "pages" (0x000 to 0x1FF). | |
10 | * The pages are accessable as 64-bit "halfpages" (an upper and lower half). | |
11 | * The pages are not part of the memory map. There is an OTP controller which | |
12 | * handles scanning in/out of bits. While access is done through OTP MMRs, | |
13 | * the bootrom provides C-callable helper functions to handle the interaction. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | #include <common.h> | |
18 | #include <command.h> | |
19 | ||
20 | #ifdef CONFIG_CMD_OTP | |
21 | ||
22 | #include <asm/blackfin.h> | |
23 | #include <asm/mach-common/bits/otp.h> | |
24 | ||
25 | static const char *otp_strerror(uint32_t err) | |
26 | { | |
27 | switch (err) { | |
28 | case 0: return "no error"; | |
29 | case OTP_WRITE_ERROR: return "OTP fuse write error"; | |
30 | case OTP_READ_ERROR: return "OTP fuse read error"; | |
31 | case OTP_ACC_VIO_ERROR: return "invalid OTP address"; | |
32 | case OTP_DATA_MULT_ERROR: return "multiple bad bits detected"; | |
33 | case OTP_ECC_MULT_ERROR: return "error in ECC bits"; | |
34 | case OTP_PREV_WR_ERROR: return "space already written"; | |
35 | case OTP_DATA_SB_WARN: return "single bad bit in half page"; | |
36 | case OTP_ECC_SB_WARN: return "single bad bit in ECC"; | |
37 | default: return "unknown error"; | |
38 | } | |
39 | } | |
40 | ||
41 | #define lowup(x) ((x) % 2 ? "upper" : "lower") | |
42 | ||
43 | int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) | |
44 | { | |
45 | bool force = false; | |
46 | if (!strcmp(argv[1], "--force")) { | |
47 | force = true; | |
48 | argv[1] = argv[0]; | |
49 | argv++; | |
50 | --argc; | |
51 | } | |
52 | ||
53 | uint32_t (*otp_func)(uint32_t page, uint32_t flags, uint64_t *page_content); | |
54 | if (!strcmp(argv[1], "read")) | |
55 | otp_func = otp_read; | |
56 | else if (!strcmp(argv[1], "write")) | |
57 | otp_func = otp_write; | |
58 | else { | |
59 | usage: | |
60 | printf("Usage:\n%s\n", cmdtp->usage); | |
61 | return 1; | |
62 | } | |
63 | ||
64 | uint64_t *addr = (uint64_t *)simple_strtoul(argv[2], NULL, 16); | |
65 | uint32_t page = simple_strtoul(argv[3], NULL, 16); | |
66 | uint32_t flags, ret; | |
67 | size_t i, count; | |
68 | ulong half; | |
69 | ||
70 | if (argc > 4) | |
71 | count = simple_strtoul(argv[4], NULL, 16); | |
72 | else | |
73 | count = 2; | |
74 | ||
75 | if (argc > 5) { | |
76 | half = simple_strtoul(argv[5], NULL, 16); | |
77 | if (half != 0 && half != 1) { | |
78 | puts("Error: 'half' can only be '0' or '1'\n"); | |
79 | goto usage; | |
80 | } | |
81 | } else | |
82 | half = 0; | |
83 | ||
84 | /* do to the nature of OTP, make sure users are sure */ | |
85 | if (!force && otp_func == otp_write) { | |
86 | printf( | |
87 | "Writing one time programmable memory\n" | |
88 | "Make sure your operating voltages and temperature are within spec\n" | |
89 | " source address: 0x%p\n" | |
90 | " OTP destination: %s page 0x%03X - %s page 0x%03X\n" | |
91 | " number to write: %ld halfpages\n" | |
92 | " type \"YES\" (no quotes) to confirm: ", | |
93 | addr, | |
94 | lowup(half), page, | |
95 | lowup(half + count - 1), page + (half + count - 1) / 2, | |
96 | half + count | |
97 | ); | |
98 | ||
99 | i = 0; | |
100 | while (1) { | |
101 | if (tstc()) { | |
102 | const char exp_ans[] = "YES\r"; | |
103 | char c; | |
104 | putc(c = getc()); | |
105 | if (exp_ans[i++] != c) { | |
106 | printf(" Aborting\n"); | |
107 | return 1; | |
108 | } else if (!exp_ans[i]) { | |
109 | puts("\n"); | |
110 | break; | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
115 | /* Only supported in newer silicon ... enable writing */ | |
116 | #if (0) | |
117 | otp_command(OTP_INIT, ...); | |
118 | #else | |
119 | *pOTP_TIMING = 0x32149485; | |
120 | #endif | |
121 | } | |
122 | ||
123 | printf("OTP memory %s: addr 0x%08lx page 0x%03X count %ld ... ", | |
124 | argv[1], addr, page, count); | |
125 | ||
126 | ret = 0; | |
127 | for (i = half; i < count + half; ++i) { | |
128 | flags = (i % 2) ? OTP_UPPER_HALF : OTP_LOWER_HALF; | |
129 | ret = otp_func(page, flags, addr); | |
130 | if (ret & 0x1) | |
131 | break; | |
132 | else if (ret) | |
133 | puts("W"); | |
134 | else | |
135 | puts("."); | |
136 | ++addr; | |
137 | if (i % 2) | |
138 | ++page; | |
139 | } | |
140 | if (ret & 0x1) | |
141 | printf("\nERROR at page 0x%03X (%s-halfpage): 0x%03X: %s\n", | |
142 | page, lowup(i), ret, otp_strerror(ret)); | |
143 | else | |
144 | puts(" done\n"); | |
145 | ||
146 | if (otp_func == otp_write) | |
147 | /* Only supported in newer silicon ... disable writing */ | |
148 | #if (0) | |
149 | otp_command(OTP_INIT, ...); | |
150 | #else | |
151 | *pOTP_TIMING = 0x1485; | |
152 | #endif | |
153 | ||
154 | return ret; | |
155 | } | |
156 | ||
157 | U_BOOT_CMD(otp, 6, 0, do_otp, | |
158 | "otp - One-Time-Programmable sub-system\n", | |
159 | "read <addr> <page> [count] [half]\n" | |
160 | "otp write [--force] <addr> <page> [count] [half]\n" | |
161 | " - read/write 'count' half-pages starting at page 'page' (offset 'half')\n"); | |
162 | ||
163 | #endif |