Merge branch '080202_at91rm9200dk' of git://linux-arm.org/u-boot-armdev
[platform/kernel/u-boot.git] / common / cmd_otp.c
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