179302e0f4eac9f95868a448918dc5765e551498
[fw/openocd] / src / flash / nor / nuc1x.c
1 /***************************************************************************
2  *   Copyright (C) 2011 by James K. Larson                                 *
3  *   jlarson@pacifier.com                                                  *
4  *                                                                         *
5  *   Copyright (C) 2013 Nemui Trinomius                                    *
6  *   nemuisan_kawausogasuki@live.jp                                        *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
22  ***************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include "imp.h"
29
30 /* nuc1x register locations */
31 #define NUC1X_SYS_BASE        0x50000000
32 #define NUC1X_SYS_WRPROT      0x50000100
33 #define NUC1X_SYS_IPRSTC1     0x50000008
34
35 #define NUC1X_SYSCLK_BASE     0x50000200
36 #define NUC1X_SYSCLK_PWRCON   0x50000200
37 #define NUC1X_SYSCLK_CLKSEL0  0x50000210
38 #define NUC1X_SYSCLK_CLKDIV   0x50000218
39 #define NUC1X_SYSCLK_AHBCLK   0x50000204
40
41 #define NUC1X_FLASH_BASE      0x5000C000
42 #define NUC1X_FLASH_ISPCON    0x5000C000
43 #define NUC1X_FLASH_ISPCMD    0x5000C00C
44 #define NUC1X_FLASH_ISPADR    0x5000C004
45 #define NUC1X_FLASH_ISPDAT    0x5000C008
46 #define NUC1X_FLASH_ISPTRG    0x5000C010
47
48 /* Command register bits */
49 #define PWRCON_OSC22M         (1 << 2)
50 #define PWRCON_XTL12M         (1 << 0)
51
52 #define IPRSTC1_CPU_RST       (1<<1)
53 #define IPRSTC1_CHIP_RST      (1<<0)
54
55 #define AHBCLK_ISP_EN         (1 << 2)
56
57 #define ISPCON_ISPEN          (1 << 0)
58 #define ISPCON_BS_AP          (0 << 1)
59 #define ISPCON_BS_LP          (1 << 1)
60 #define ISPCON_CFGUEN         (1 << 4)
61 #define ISPCON_LDUEN          (1 << 5)
62 #define ISPCON_ISPFF          (1 << 6)
63
64 /* isp commands */
65 #define ISPCMD_FCTRL          (0x2)
66 #define ISPCMD_FCEN           (1 << 4)
67 #define ISPCMD_FOEN           (1 << 5)
68 #define ISPCMD_ERASE          (0x2 | ISPCMD_FOEN)
69 #define ISPCMD_WRITE          (0x1 | ISPCMD_FOEN)
70 #define ISPTRG_ISPGO          (1 << 0)
71
72 /* access unlock keys */
73 #define KEY1 0x59
74 #define KEY2 0x16
75 #define KEY3 0x88
76 #define LOCK 0x00
77
78 /* part structs */
79 static const struct {
80         const char *partname;
81         uint32_t partno;
82         uint16_t num_page;
83 }
84 NuMicroParts[] = {
85         /*PART NO*/   /*PART ID*/   /*NUM PAGE*/
86         {"NUC100LC1", 0x00010008,   64},
87         {"NUC100LD1", 0x00010005,   128},
88         {"NUC100LD2", 0x00010004,   128},
89         {"NUC100RC1", 0x00010017,   64},
90         {"NUC100RD1", 0x00010014,   128},
91         {"NUC100RD2", 0x00010013,   128},
92
93         {"NUC100LD3", 0x00010003,   128},
94         {"NUC100LE3", 0x00010000,   256},
95         {"NUC100RD3", 0x00010012,   128},
96         {"NUC100RE3", 0x00010009,   256},
97         {"NUC100VD2", 0x00010022,   128},
98         {"NUC100VD3", 0x00010021,   128},
99         {"NUC100VE3", 0x00010018,   256},
100
101         {"NUC120LC1", 0x00012008,   64},
102         {"NUC120LD1", 0x00012005,   128},
103         {"NUC120LD2", 0x00012004,   128},
104         {"NUC120RC1", 0x00012017,   64},
105         {"NUC120RD1", 0x00012014,   128},
106         {"NUC120RD2", 0x00012013,   128},
107
108         {"NUC120LD3", 0x00012003,   128},
109         {"NUC120LE3", 0x00012000,   256},
110         {"NUC120RD3", 0x00012012,   128},
111         {"NUC120RE3", 0x00012009,   256},
112         {"NUC120VD2", 0x00012022,   128},
113         {"NUC120VD3", 0x00012021,   128},
114         {"NUC120VE3", 0x00012018,   256},
115
116         {"NUC122ZD2", 0x00012231,   128},
117         {"NUC122ZC1", 0x00012235,   64},
118         {"NUC122LD2", 0x00012204,   128},
119         {"NUC122LC1", 0x00012208,   64},
120         {"NUC122RD2", 0x00012213,   128},
121         {"NUC122RC1", 0x00012217,   64},
122
123         {"NUC123ZD4", 0x00012255,   136},
124         {"NUC123ZC2", 0x00012245,   68},
125         {"NUC123LD4", 0x00012235,   136},
126         {"NUC123LC2", 0x00012225,   68},
127         {"NUC123SD4", 0x00012215,   136},
128         {"NUC123SC2", 0x00012205,   68},
129
130         {"NUC130LC1", 0x00013008,   64},
131         {"NUC130LD2", 0x00013004,   128},
132         {"NUC130LE3", 0x00013000,   256},
133         {"NUC130RC1", 0x00013017,   64},
134         {"NUC130RD2", 0x00013013,   128},
135         {"NUC130RE3", 0x00013009,   256},
136         {"NUC130VE3", 0x00013018,   256},
137
138         {"M052L",     0x00005200,   16},
139         {"M052Z",     0x00005203,   16},
140         {"M054L",     0x00005400,   32},
141         {"M054Z",     0x00005403,   32},
142         {"M058L",     0x00005800,   64},
143         {"M058Z",     0x00005803,   64},
144         {"M0516L",    0x00005A00,   128},
145         {"M0516Z",    0x00005A03,   128},
146
147         {"MINI51L",   0x00205100,   8},
148         {"MINI51Z",   0x00205103,   8},
149         {"MINI52L",   0x00205200,   16},
150         {"MINI52Z",   0x00205203,   16},
151         {"MINI54L",   0x00205400,   32},
152         {"MINI54Z",   0x00205403,   32},
153
154         {"UNKNOWN",   0x00000000,   256},
155 };
156
157 static int nuc1x_unlock(struct flash_bank *bank)
158 {
159         uint32_t is_protected;
160         struct target *target = bank->target;
161
162         /* Check to see if Nuc is unlocked or not */
163         int retval = target_read_u32(target, NUC1X_SYS_WRPROT, &is_protected);
164         if (retval != ERROR_OK)
165                 return retval;
166
167         LOG_DEBUG("protected = 0x%08" PRIx32 "", is_protected);
168         if (is_protected == 0) {        /* means protected - so unlock it */
169                 /* unlock flash registers */
170                 retval = target_write_u32(target, NUC1X_SYS_WRPROT, KEY1);
171                 if (retval != ERROR_OK)
172                         return retval;
173                 retval = target_write_u32(target, NUC1X_SYS_WRPROT, KEY2);
174                 if (retval != ERROR_OK)
175                         return retval;
176                 retval = target_write_u32(target, NUC1X_SYS_WRPROT, KEY3);
177                 if (retval != ERROR_OK)
178                         return retval;
179         }
180         /* Check that unlock worked */
181         retval = target_read_u32(target, NUC1X_SYS_WRPROT, &is_protected);
182         if (retval != ERROR_OK)
183                 return retval;
184
185         if (is_protected == 1) {        /* means unprotected */
186                 LOG_DEBUG("protection removed");
187         } else {
188                 LOG_DEBUG("still protected!!");
189         }
190
191         return ERROR_OK;
192 }
193
194 static int nuc1x_reset(struct flash_bank *bank)
195 {
196         struct target *target = bank->target;
197
198         nuc1x_unlock(bank);
199
200         int retval = target_write_u32(target, NUC1X_SYS_IPRSTC1, IPRSTC1_CPU_RST);
201         if (retval != ERROR_OK)
202                 return retval;
203
204         return ERROR_OK;
205 }
206
207 static int nuc1x_reset2lprom(struct flash_bank *bank)
208 {
209         struct target *target = bank->target;
210
211         nuc1x_unlock(bank);
212         int retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_BS_LP);
213         if (retval != ERROR_OK)
214                 return retval;
215
216         nuc1x_reset(bank);
217
218         return ERROR_OK;
219 }
220
221 static int nuc1x_init_iap(struct flash_bank *bank)
222 {
223         struct target *target = bank->target;
224
225         if (target->state != TARGET_HALTED) {
226                 LOG_ERROR("Target not halted");
227                 return ERROR_TARGET_NOT_HALTED;
228         }
229
230         int retval = nuc1x_unlock(bank);
231         if (retval != ERROR_OK)
232                 return retval;
233
234         /* enable isp clock and ispen bit */
235         retval = target_write_u32(target, NUC1X_SYSCLK_AHBCLK, AHBCLK_ISP_EN);
236         if (retval != ERROR_OK)
237                 return retval;
238
239         retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_ISPFF | ISPCON_LDUEN | ISPCON_CFGUEN | ISPCON_ISPEN);
240         if (retval != ERROR_OK)
241                 return retval;
242
243         return ERROR_OK;
244 }
245
246 /* Private bank information for nuc1x. */
247 struct  nuc1x_flash_bank {
248         struct working_area *write_algorithm;
249         int probed;
250 };
251
252 /* This is the function called in the config file. */
253 FLASH_BANK_COMMAND_HANDLER(nuc1x_flash_bank_command)
254 {
255         struct nuc1x_flash_bank *bank_info;
256
257         if (CMD_ARGC < 6)
258                 return ERROR_COMMAND_SYNTAX_ERROR;
259
260         LOG_INFO("add flash_bank nuc1x %s", bank->name);
261
262         bank_info = malloc(sizeof(struct nuc1x_flash_bank));
263
264         memset(bank_info, 0, sizeof(struct nuc1x_flash_bank));
265
266         bank->driver_priv = bank_info;
267
268         return ERROR_OK;
269
270 }
271
272 /* Protection checking - examines the lock bit. */
273 static int nuc1x_protect_check(struct flash_bank *bank)
274 {
275         uint32_t is_protected, set;
276         struct target *target = bank->target;
277         int i;
278
279         if (target->state != TARGET_HALTED) {
280                 LOG_ERROR("Target not halted");
281                 return ERROR_TARGET_NOT_HALTED;
282         }
283
284         /* Check to see if Nuc is unlocked or not */
285         int retval = target_read_u32(target, NUC1X_SYS_WRPROT, &is_protected);
286         if (retval != ERROR_OK)
287                 return retval;
288
289         LOG_INFO("is_protected = 0x%08" PRIx32 "", is_protected);
290         if (is_protected == 0) {        /* means protected */
291                 set = 1;
292         } else {
293                 set = 0;
294         }
295         for (i = 0; i < bank->num_sectors; i++)
296                 bank->sectors[i].is_protected = set;
297
298         return ERROR_OK;
299 }
300
301 static int nuc1x_erase(struct flash_bank *bank, int first, int last)
302 {
303         struct target *target = bank->target;
304         uint32_t timeout, status;
305         int i;
306
307         if (bank->target->state != TARGET_HALTED) {
308                 LOG_ERROR("Target not halted");
309                 return ERROR_TARGET_NOT_HALTED;
310         }
311
312         LOG_INFO("Nuvoton NUC: Sector Erase ... (%d to %d)", first, last);
313
314         int retval = nuc1x_reset2lprom(bank);
315         if (retval != ERROR_OK)
316                 return retval;
317
318         retval = nuc1x_init_iap(bank);
319         if (retval != ERROR_OK)
320                 return retval;
321
322         retval = nuc1x_unlock(bank);
323         if (retval != ERROR_OK)
324                 return retval;
325
326         retval = target_write_u32(target, NUC1X_FLASH_ISPCMD, ISPCMD_ERASE);
327         if (retval != ERROR_OK)
328                 return retval;
329
330         for (i = first; i <= last; i++) {
331                 LOG_DEBUG("erasing sector %d at addresss 0x%" PRIx32 "", i, bank->base + bank->sectors[i].offset);
332                 retval = target_write_u32(target, NUC1X_FLASH_ISPADR, bank->base + bank->sectors[i].offset);
333                 if (retval != ERROR_OK)
334                         return retval;
335                 retval = target_write_u32(target, NUC1X_FLASH_ISPTRG, ISPTRG_ISPGO); /* This is the only bit available */
336                 if (retval != ERROR_OK)
337                         return retval;
338
339                 /* wait for busy to clear - check the GO flag */
340                 timeout = 100;
341                 for (;;) {
342                         retval = target_read_u32(target, NUC1X_FLASH_ISPTRG, &status);
343                         if (retval != ERROR_OK)
344                                 return retval;
345                                 LOG_DEBUG("status: 0x%" PRIx32 "", status);
346                         if (status == 0)
347                                 break;
348                         if (timeout-- <= 0) {
349                                 LOG_DEBUG("timed out waiting for flash");
350                                 return ERROR_FAIL;
351                         }
352                         busy_sleep(1);  /* can use busy sleep for short times. */
353                 }
354
355                 /* check for failure */
356                 retval = target_read_u32(target, NUC1X_FLASH_ISPCON, &status);
357                 if (retval != ERROR_OK)
358                         return retval;
359                 if ((status & ISPCON_ISPFF) != 0) {
360                         LOG_DEBUG("failure: 0x%" PRIx32 "", status);
361                         /* if bit is set, then must write to it to clear it. */
362                         retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_ISPFF);
363                         if (retval != ERROR_OK)
364                                 return retval;
365                 } else {
366                         bank->sectors[i].is_erased = 1;
367                 }
368         }
369
370         retval = nuc1x_reset(bank);
371         if (retval != ERROR_OK)
372                 return retval;
373
374         /* done, */
375         LOG_DEBUG("Erase done.");
376
377         return ERROR_OK;
378 }
379
380 /* The write routine stub. */
381 static int nuc1x_write(struct flash_bank *bank, const uint8_t *buffer,
382                 uint32_t offset, uint32_t count)
383 {
384         struct target *target = bank->target;
385         uint32_t i, timeout, status;
386
387         if (bank->target->state != TARGET_HALTED) {
388                 LOG_ERROR("Target not halted");
389                 return ERROR_TARGET_NOT_HALTED;
390         }
391
392         LOG_INFO("Novoton NUC: FLASH Write ...");
393
394         int retval = nuc1x_reset2lprom(bank);
395         if (retval != ERROR_OK)
396                 return retval;
397
398         retval = nuc1x_init_iap(bank);
399         if (retval != ERROR_OK)
400                 return retval;
401
402         retval = nuc1x_unlock(bank);
403         if (retval != ERROR_OK)
404                 return retval;
405
406         retval = target_write_u32(target, NUC1X_FLASH_ISPCMD, ISPCMD_WRITE);
407         if (retval != ERROR_OK)
408                 return retval;
409
410         /* program command */
411         for (i = 0; i < count; i += 4) {
412
413                 LOG_DEBUG("write longword @ %08" PRIX32, (uint32_t)(offset + i));
414
415                 uint8_t padding[4] = {0xff, 0xff, 0xff, 0xff};
416                 memcpy(padding, buffer + i, MIN(4, count-i));
417
418                 retval = target_write_u32(target, NUC1X_FLASH_ISPADR, bank->base + offset + i);
419                 if (retval != ERROR_OK)
420                         return retval;
421                 retval = target_write_memory(target, NUC1X_FLASH_ISPDAT, 4, 1, padding);
422                 if (retval != ERROR_OK)
423                         return retval;
424                 retval = target_write_u32(target, NUC1X_FLASH_ISPTRG, ISPTRG_ISPGO);
425                 if (retval != ERROR_OK)
426                         return retval;
427
428                 /* wait for busy to clear - check the GO flag */
429                 timeout = 100;
430                 for (;;) {
431                         retval = target_read_u32(target, NUC1X_FLASH_ISPTRG, &status);
432                         if (retval != ERROR_OK)
433                                 return retval;
434                                 LOG_DEBUG("status: 0x%" PRIx32 "", status);
435                         if (status == 0)
436                                 break;
437                         if (timeout-- <= 0) {
438                                 LOG_DEBUG("timed out waiting for flash");
439                                 return ERROR_FAIL;
440                         }
441                         busy_sleep(1);  /* can use busy sleep for short times. */
442                 }
443
444                 /* check for failure */
445                 retval = target_read_u32(target, NUC1X_FLASH_ISPCON, &status);
446                 if (retval != ERROR_OK)
447                         return retval;
448                 if ((status & ISPCON_ISPFF) != 0) {
449                         LOG_DEBUG("failure: 0x%" PRIx32 "", status);
450                         /* if bit is set, then must write to it to clear it. */
451                         retval = target_write_u32(target, NUC1X_FLASH_ISPCON, ISPCON_ISPFF);
452                         if (retval != ERROR_OK)
453                                 return retval;
454                 } else {
455                         LOG_DEBUG("writed OK");
456                 }
457         }
458
459         retval = nuc1x_reset(bank);
460         if (retval != ERROR_OK)
461                 return retval;
462
463         /* done, */
464         LOG_DEBUG("Write done.");
465
466         return ERROR_OK;
467 }
468
469 /* The probe routine for the nuc. Only recognizes the nuc120 right now. */
470 static int nuc1x_probe(struct flash_bank *bank)
471 {
472         struct target *target = bank->target;
473         struct nuc1x_flash_bank *nuc1x_info = bank->driver_priv;
474         int i;
475         uint16_t num_pages;
476         uint32_t device_id;
477         int page_size;
478         uint32_t base_address = 0x00000000;
479
480         nuc1x_info->probed = 0;
481
482         /* read nuc1x device id register */
483         int retval = target_read_u32(target, 0x50000000, &device_id);
484         if (retval != ERROR_OK)
485                 return retval;
486
487         page_size = 512;        /* all nuc parts has 512 byte per sector */
488
489         /* search part numbers */
490         for (i = 0; NuMicroParts[i].partno; i++) {
491                 if (NuMicroParts[i].partno == (device_id & 0x0FFFFFFF)) {
492                         num_pages = NuMicroParts[i].num_page;
493                         break;
494                 }
495         }
496         if (!(NuMicroParts[i].partno == 0x00000000)) {
497                 LOG_INFO("DeviceID : 0x%08" PRIx32 "", device_id);
498                 LOG_INFO("Detect %s%cN!", NuMicroParts[i].partname, (char)('A'+(device_id>>28)));
499         } else {
500                 LOG_INFO("No NUC Device Detected...");
501                 return ERROR_FAIL;
502         }
503
504         if (bank->sectors) {
505                 free(bank->sectors);
506                 bank->sectors = NULL;
507         }
508
509         bank->base = base_address;
510         bank->size = (num_pages * page_size);
511         bank->num_sectors = num_pages;
512         bank->sectors = malloc(sizeof(struct flash_sector) * num_pages);
513
514         for (i = 0; i < num_pages; i++) {
515                 bank->sectors[i].offset = i * page_size;
516                 bank->sectors[i].size = page_size;
517                 bank->sectors[i].is_erased = -1;
518                 bank->sectors[i].is_protected = 1;
519         }
520
521         nuc1x_info->probed = 1;
522
523         LOG_DEBUG("Novoton NUC: Probed ...");
524
525         return ERROR_OK;
526 }
527
528 /* Standard approach to autoprobing. */
529 static int nuc1x_auto_probe(struct flash_bank *bank)
530 {
531         struct nuc1x_flash_bank *nuc1x_info = bank->driver_priv;
532         if (nuc1x_info->probed)
533                 return ERROR_OK;
534         return nuc1x_probe(bank);
535 }
536
537 /* Info doesn't really add much, but works correctly. */
538 static int get_nuc1x_info(struct flash_bank *bank, char *buf, int buf_size)
539 {
540         struct target *target = bank->target;
541         uint32_t i, device_id;
542
543         /* read nuc1x device id register */
544         int retval = target_read_u32(target, 0x50000000, &device_id);
545         if (retval != ERROR_OK)
546                 return retval;
547
548         /* search part numbers */
549         for (i = 0; NuMicroParts[i].partno; i++) {
550                 if (NuMicroParts[i].partno == (device_id & 0x0FFFFFFF))
551                         break;
552         }
553         if (!(NuMicroParts[i].partno == 0x00000000)) {
554                 LOG_INFO("DeviceID : 0x%08" PRIx32 "", device_id);
555                 LOG_INFO("Detect %s%cN!", NuMicroParts[i].partname, (char)('A'+(device_id>>28)));
556         } else {
557                 LOG_INFO("No NUC Device Detected...");
558                 return ERROR_FAIL;
559         }
560
561         return ERROR_OK;
562 }
563
564 /* The nuc120 doesn't support mass erase, so this will probably be removed soon.
565  * The structure is left for now until I am sure I don't want to add any custom
566  * commands. */
567 static int nuc1x_mass_erase(struct flash_bank *bank)
568 {
569         struct target *target = bank->target;
570         int retval = ERROR_OK;
571
572         if (target->state != TARGET_HALTED) {
573                 LOG_ERROR("Target not halted");
574                 return ERROR_TARGET_NOT_HALTED;
575         }
576
577         LOG_INFO("Novoton NUC: Chip Erase ... (may take several seconds)");
578
579         return retval;
580 }
581
582 COMMAND_HANDLER(nuc1x_handle_mass_erase_command)
583 {
584         int i; /* for erasing sectors */
585         if (CMD_ARGC < 1) {
586                 command_print(CMD_CTX, "nuc1x mass_erase <bank>");
587                 return ERROR_OK;
588         }
589
590         struct flash_bank *bank;
591         int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank);
592         if (ERROR_OK != retval)
593                 return retval;
594
595         retval = nuc1x_mass_erase(bank);
596         if (retval == ERROR_OK) {
597                 /* set all sectors as erased */
598                 for (i = 0; i < bank->num_sectors; i++)
599                         bank->sectors[i].is_erased = 1;
600
601                 command_print(CMD_CTX, "nuc1x mass erase complete");
602         } else
603                 command_print(CMD_CTX, "nuc1x mass erase failed");
604
605         return retval;
606 }
607
608 static const struct command_registration nuc1x_exec_command_handlers[] = {
609         {
610                 .name = "mass_erase",
611                 .handler = nuc1x_handle_mass_erase_command,
612                 .mode = COMMAND_EXEC,
613                 .usage = "bank_id",
614                 .help = "Erase entire Flash device.",
615         },
616         COMMAND_REGISTRATION_DONE
617 };
618
619 static const struct command_registration nuc1x_command_handlers[] = {
620         {
621                 .name = "nuc1x",
622                 .mode = COMMAND_ANY,
623                 .help = "nuc1x Flash command group",
624                 .chain = nuc1x_exec_command_handlers,
625         },
626         COMMAND_REGISTRATION_DONE
627 };
628 struct flash_driver nuc1x_flash = {
629         .name = "nuc1x",
630         .commands = nuc1x_command_handlers,
631         .flash_bank_command = nuc1x_flash_bank_command,
632         .erase = nuc1x_erase,
633         .write = nuc1x_write,
634         .read = default_flash_read,
635         .probe = nuc1x_probe,
636         .auto_probe = nuc1x_auto_probe,
637         .erase_check = default_flash_blank_check,
638         .protect_check = nuc1x_protect_check,
639         .info = get_nuc1x_info,
640 };