]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/nios2/cpu/epcs.c
karo: tx51: justify and adjust the delay required before releasing the ETN PHY strap...
[karo-tx-uboot.git] / arch / nios2 / cpu / epcs.c
1 /*
2  * (C) Copyright 2004, Psyent Corporation <www.psyent.com>
3  * Scott McNutt <smcnutt@psyent.com>
4  *
5  * SPDX-License-Identifier:     GPL-2.0+
6  */
7
8 #include <common.h>
9
10 #if defined(CONFIG_SYS_NIOS_EPCSBASE)
11 #include <command.h>
12 #include <asm/io.h>
13 #include <nios2-io.h>
14 #include <nios2-epcs.h>
15
16
17 /*-----------------------------------------------------------------------*/
18 #define SHORT_HELP\
19         "epcs    - read/write Cyclone EPCS configuration device.\n"
20
21 #define LONG_HELP\
22         "\n"\
23         "epcs erase start [end]\n"\
24         "    - erase sector start or sectors start through end.\n"\
25         "epcs info\n"\
26         "    - display EPCS device information.\n"\
27         "epcs protect on | off\n"\
28         "    - turn device protection on or off.\n"\
29         "epcs read addr offset count\n"\
30         "    - read count bytes from offset to addr.\n"\
31         "epcs write addr offset count\n"\
32         "    - write count bytes to offset from addr.\n"\
33         "epcs verify addr offset count\n"\
34         "    - verify count bytes at offset from addr."
35
36
37 /*-----------------------------------------------------------------------*/
38 /* Operation codes for serial configuration devices
39  */
40 #define EPCS_WRITE_ENA          0x06    /* Write enable */
41 #define EPCS_WRITE_DIS          0x04    /* Write disable */
42 #define EPCS_READ_STAT          0x05    /* Read status */
43 #define EPCS_READ_BYTES         0x03    /* Read bytes */
44 #define EPCS_READ_ID            0xab    /* Read silicon id */
45 #define EPCS_WRITE_STAT         0x01    /* Write status */
46 #define EPCS_WRITE_BYTES        0x02    /* Write bytes */
47 #define EPCS_ERASE_BULK         0xc7    /* Erase entire device */
48 #define EPCS_ERASE_SECT         0xd8    /* Erase sector */
49
50 /* Device status register bits
51  */
52 #define EPCS_STATUS_WIP         (1<<0)  /* Write in progress */
53 #define EPCS_STATUS_WEL         (1<<1)  /* Write enable latch */
54
55 /* Misc
56  */
57 #define EPCS_TIMEOUT            100     /* 100 msec timeout */
58
59 static nios_spi_t *epcs = (nios_spi_t *)CONFIG_SYS_NIOS_EPCSBASE;
60
61 /***********************************************************************
62  * Device access
63  ***********************************************************************/
64 static int epcs_cs (int assert)
65 {
66         ulong start;
67         unsigned tmp;
68
69
70         if (assert) {
71                 tmp = readl (&epcs->control);
72                 writel (tmp | NIOS_SPI_SSO, &epcs->control);
73         } else {
74                 /* Let all bits shift out */
75                 start = get_timer (0);
76                 while ((readl (&epcs->status) & NIOS_SPI_TMT) == 0)
77                         if (get_timer (start) > EPCS_TIMEOUT)
78                                 return (-1);
79                 tmp = readl (&epcs->control);
80                 writel (tmp & ~NIOS_SPI_SSO, &epcs->control);
81         }
82         return (0);
83 }
84
85 static int epcs_tx (unsigned char c)
86 {
87         ulong start;
88
89         start = get_timer (0);
90         while ((readl (&epcs->status) & NIOS_SPI_TRDY) == 0)
91                 if (get_timer (start) > EPCS_TIMEOUT)
92                         return (-1);
93         writel (c, &epcs->txdata);
94         return (0);
95 }
96
97 static int epcs_rx (void)
98 {
99         ulong start;
100
101         start = get_timer (0);
102         while ((readl (&epcs->status) & NIOS_SPI_RRDY) == 0)
103                 if (get_timer (start) > EPCS_TIMEOUT)
104                         return (-1);
105         return (readl (&epcs->rxdata));
106 }
107
108 static unsigned char bitrev[] = {
109         0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,
110         0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f
111 };
112
113 static unsigned char epcs_bitrev (unsigned char c)
114 {
115         unsigned char val;
116
117         val  = bitrev[c>>4];
118         val |= bitrev[c & 0x0f]<<4;
119         return (val);
120 }
121
122 static void epcs_rcv (unsigned char *dst, int len)
123 {
124         while (len--) {
125                 epcs_tx (0);
126                 *dst++ = epcs_rx ();
127         }
128 }
129
130 static void epcs_rrcv (unsigned char *dst, int len)
131 {
132         while (len--) {
133                 epcs_tx (0);
134                 *dst++ = epcs_bitrev (epcs_rx ());
135         }
136 }
137
138 static void epcs_snd (unsigned char *src, int len)
139 {
140         while (len--) {
141                 epcs_tx (*src++);
142                 epcs_rx ();
143         }
144 }
145
146 static void epcs_rsnd (unsigned char *src, int len)
147 {
148         while (len--) {
149                 epcs_tx (epcs_bitrev (*src++));
150                 epcs_rx ();
151         }
152 }
153
154 static void epcs_wr_enable (void)
155 {
156         epcs_cs (1);
157         epcs_tx (EPCS_WRITE_ENA);
158         epcs_rx ();
159         epcs_cs (0);
160 }
161
162 static unsigned char epcs_status_rd (void)
163 {
164         unsigned char status;
165
166         epcs_cs (1);
167         epcs_tx (EPCS_READ_STAT);
168         epcs_rx ();
169         epcs_tx (0);
170         status = epcs_rx ();
171         epcs_cs (0);
172         return (status);
173 }
174
175 static void epcs_status_wr (unsigned char status)
176 {
177         epcs_wr_enable ();
178         epcs_cs (1);
179         epcs_tx (EPCS_WRITE_STAT);
180         epcs_rx ();
181         epcs_tx (status);
182         epcs_rx ();
183         epcs_cs (0);
184         return;
185 }
186
187 /***********************************************************************
188  * Device information
189  ***********************************************************************/
190
191 static struct epcs_devinfo_t devinfo[] = {
192         { "EPCS1 ", 0x10, 17, 4, 15, 8, 0x0c },
193         { "EPCS4 ", 0x12, 19, 8, 16, 8, 0x1c },
194         { "EPCS16", 0x14, 21, 32, 16, 8, 0x1c },
195         { "EPCS64", 0x16, 23,128, 16, 8, 0x1c },
196         { 0, 0, 0, 0, 0, 0 }
197 };
198
199 int epcs_reset (void)
200 {
201         /* When booting from an epcs controller, the epcs bootrom
202          * code may leave the slave select in an asserted state.
203          * This causes two problems: (1) The initial epcs access
204          * will fail -- not a big deal, and (2) a software reset
205          * will cause the bootrom code to hang since it does not
206          * ensure the select is negated prior to first access -- a
207          * big deal. Here we just negate chip select and everything
208          * gets better :-)
209          */
210         epcs_cs (0); /* Negate chip select */
211         return (0);
212 }
213
214 epcs_devinfo_t *epcs_dev_find (void)
215 {
216         unsigned char buf[4];
217         unsigned char id;
218         int i;
219         struct epcs_devinfo_t *dev = NULL;
220
221         /* Read silicon id requires 3 "dummy bytes" before it's put
222          * on the wire.
223          */
224         buf[0] = EPCS_READ_ID;
225         buf[1] = 0;
226         buf[2] = 0;
227         buf[3] = 0;
228
229         epcs_cs (1);
230         epcs_snd (buf,4);
231         epcs_rcv (buf,1);
232         if (epcs_cs (0) == -1)
233                 return (NULL);
234         id = buf[0];
235
236         /* Find the info struct */
237         i = 0;
238         while (devinfo[i].name) {
239                 if (id == devinfo[i].id) {
240                         dev = &devinfo[i];
241                         break;
242                 }
243                 i++;
244         }
245
246         return (dev);
247 }
248
249 /***********************************************************************
250  * Misc Utilities
251  ***********************************************************************/
252 int epcs_cfgsz (void)
253 {
254         int sz = 0;
255         unsigned char buf[128];
256         unsigned char *p;
257         struct epcs_devinfo_t *dev = epcs_dev_find ();
258
259         if (!dev)
260                 return (-1);
261
262         /* Read in the first 128 bytes of the device */
263         buf[0] = EPCS_READ_BYTES;
264         buf[1] = 0;
265         buf[2] = 0;
266         buf[3] = 0;
267
268         epcs_cs (1);
269         epcs_snd (buf,4);
270         epcs_rrcv (buf, sizeof(buf));
271         epcs_cs (0);
272
273         /* Search for the starting 0x6a which is followed by the
274          * 4-byte 'register' and 4-byte bit-count.
275          */
276         p = buf;
277         while (p < buf + sizeof(buf)-8) {
278                 if ( *p == 0x6a ) {
279                         /* Point to bit count and extract */
280                         p += 5;
281                         sz = *p++;
282                         sz |= *p++ << 8;
283                         sz |= *p++ << 16;
284                         sz |= *p++ << 24;
285                         /* Convert to byte count */
286                         sz += 7;
287                         sz >>= 3;
288                 } else if (*p == 0xff) {
289                         /* 0xff is ok ... just skip */
290                         p++;
291                         continue;
292                 } else {
293                         /* Not 0xff or 0x6a ... something's not
294                          * right ... report 'unknown' (sz=0).
295                          */
296                         break;
297                 }
298         }
299         return (sz);
300 }
301
302 int epcs_erase (unsigned start, unsigned end)
303 {
304         unsigned off, sectsz;
305         unsigned char buf[4];
306         struct epcs_devinfo_t *dev = epcs_dev_find ();
307
308         if (!dev || (start>end))
309                 return (-1);
310
311         /* Erase the requested sectors. An address is required
312          * that lies within the requested sector -- we'll just
313          * use the first address in the sector.
314          */
315         printf ("epcs erasing sector %d ", start);
316         if (start != end)
317                 printf ("to %d ", end);
318         sectsz = (1 << dev->sz_sect);
319         while (start <= end) {
320                 off = start * sectsz;
321                 start++;
322
323                 buf[0] = EPCS_ERASE_SECT;
324                 buf[1] = off >> 16;
325                 buf[2] = off >> 8;
326                 buf[3] = off;
327
328                 epcs_wr_enable ();
329                 epcs_cs (1);
330                 epcs_snd (buf,4);
331                 epcs_cs (0);
332
333                 printf ("."); /* Some user feedback */
334
335                 /* Wait for erase to complete */
336                 while (epcs_status_rd() & EPCS_STATUS_WIP)
337                         ;
338         }
339         printf (" done.\n");
340         return (0);
341 }
342
343 int epcs_read (ulong addr, ulong off, ulong cnt)
344 {
345         unsigned char buf[4];
346         struct epcs_devinfo_t *dev = epcs_dev_find ();
347
348         if (!dev)
349                 return (-1);
350
351         buf[0] = EPCS_READ_BYTES;
352         buf[1] = off >> 16;
353         buf[2] = off >> 8;
354         buf[3] = off;
355
356         epcs_cs (1);
357         epcs_snd (buf,4);
358         epcs_rrcv ((unsigned char *)addr, cnt);
359         epcs_cs (0);
360
361         return (0);
362 }
363
364 int epcs_write (ulong addr, ulong off, ulong cnt)
365 {
366         ulong wrcnt;
367         unsigned pgsz;
368         unsigned char buf[4];
369         struct epcs_devinfo_t *dev = epcs_dev_find ();
370
371         if (!dev)
372                 return (-1);
373
374         pgsz = (1<<dev->sz_page);
375         while (cnt) {
376                 if (off % pgsz)
377                         wrcnt = pgsz - (off % pgsz);
378                 else
379                         wrcnt = pgsz;
380                 wrcnt = (wrcnt > cnt) ? cnt : wrcnt;
381
382                 buf[0] = EPCS_WRITE_BYTES;
383                 buf[1] = off >> 16;
384                 buf[2] = off >> 8;
385                 buf[3] = off;
386
387                 epcs_wr_enable ();
388                 epcs_cs (1);
389                 epcs_snd (buf,4);
390                 epcs_rsnd ((unsigned char *)addr, wrcnt);
391                 epcs_cs (0);
392
393                 /* Wait for write to complete */
394                 while (epcs_status_rd() & EPCS_STATUS_WIP)
395                         ;
396
397                 cnt -= wrcnt;
398                 off += wrcnt;
399                 addr += wrcnt;
400         }
401
402         return (0);
403 }
404
405 int epcs_verify (ulong addr, ulong off, ulong cnt, ulong *err)
406 {
407         ulong rdcnt;
408         unsigned char buf[256];
409         unsigned char *start,*end;
410         int i;
411
412         start = end = (unsigned char *)addr;
413         while (cnt) {
414                 rdcnt = (cnt>sizeof(buf)) ? sizeof(buf) : cnt;
415                 epcs_read ((ulong)buf, off, rdcnt);
416                 for (i=0; i<rdcnt; i++) {
417                         if (*end != buf[i]) {
418                                 *err = end - start;
419                                 return(-1);
420                         }
421                         end++;
422                 }
423                 cnt -= rdcnt;
424                 off += rdcnt;
425         }
426         return (0);
427 }
428
429 static int epcs_sect_erased (int sect, unsigned *offset,
430                 struct epcs_devinfo_t *dev)
431 {
432         unsigned char buf[128];
433         unsigned off, end;
434         unsigned sectsz;
435         int i;
436
437         sectsz = (1 << dev->sz_sect);
438         off = sectsz * sect;
439         end = off + sectsz;
440
441         while (off < end) {
442                 epcs_read ((ulong)buf, off, sizeof(buf));
443                 for (i=0; i < sizeof(buf); i++) {
444                         if (buf[i] != 0xff) {
445                                 *offset = off + i;
446                                 return (0);
447                         }
448                 }
449                 off += sizeof(buf);
450         }
451         return (1);
452 }
453
454
455 /***********************************************************************
456  * Commands
457  ***********************************************************************/
458 static
459 void do_epcs_info (struct epcs_devinfo_t *dev, int argc, char * const argv[])
460 {
461         int i;
462         unsigned char stat;
463         unsigned tmp;
464         int erased;
465
466         /* Basic device info */
467         printf ("%s: %d kbytes (%d sectors x %d kbytes,"
468                 " %d bytes/page)\n",
469                 dev->name, 1 << (dev->size-10),
470                 dev->num_sects, 1 << (dev->sz_sect-10),
471                 1 << dev->sz_page );
472
473         /* Status -- for now protection is all-or-nothing */
474         stat = epcs_status_rd();
475         printf ("status: 0x%02x (WIP:%d, WEL:%d, PROT:%s)\n",
476                 stat,
477                 (stat & EPCS_STATUS_WIP) ? 1 : 0,
478                 (stat & EPCS_STATUS_WEL) ? 1 : 0,
479                 (stat & dev->prot_mask) ? "on" : "off" );
480
481         /* Configuration  */
482         tmp = epcs_cfgsz ();
483         if (tmp) {
484                 printf ("config: 0x%06x (%d) bytes\n", tmp, tmp );
485         } else {
486                 printf ("config: unknown\n" );
487         }
488
489         /* Sector info */
490         for (i=0; (i < dev->num_sects) && (argc > 1); i++) {
491                 erased = epcs_sect_erased (i, &tmp, dev);
492                 if ((i & 0x03) == 0) printf ("\n");
493                 printf ("%4d: %07x ",
494                         i, i*(1<<dev->sz_sect) );
495                 if (erased)
496                         printf ("E ");
497                 else
498                         printf ("  ");
499         }
500         printf ("\n");
501
502         return;
503 }
504
505 static
506 void do_epcs_erase (struct epcs_devinfo_t *dev, int argc, char * const argv[])
507 {
508         unsigned start,end;
509
510         if ((argc < 3) || (argc > 4)) {
511                 printf ("USAGE: epcs erase sect [end]\n");
512                 return;
513         }
514         if ((epcs_status_rd() & dev->prot_mask) != 0) {
515                 printf ( "epcs: device protected.\n");
516                 return;
517         }
518
519         start = simple_strtoul (argv[2], NULL, 10);
520         if (argc > 3)
521                 end = simple_strtoul (argv[3], NULL, 10);
522         else
523                 end = start;
524         if ((start >= dev->num_sects) || (start > end)) {
525                 printf ("epcs: invalid sector range: [%d:%d]\n",
526                         start, end );
527                 return;
528         }
529
530         epcs_erase (start, end);
531
532         return;
533 }
534
535 static
536 void do_epcs_protect (struct epcs_devinfo_t *dev, int argc, char * const argv[])
537 {
538         unsigned char stat;
539
540         /* For now protection is all-or-nothing to keep things
541          * simple. The protection bits don't map in a linear
542          * fashion ... and we would rather protect the bottom
543          * of the device since it contains the config data and
544          * leave the top unprotected for app use. But unfortunately
545          * protection works from top-to-bottom so it does
546          * really help very much from a software app point-of-view.
547          */
548         if (argc < 3) {
549                 printf ("USAGE: epcs protect on | off\n");
550                 return;
551         }
552         if (!dev)
553                 return;
554
555         /* Protection on/off is just a matter of setting/clearing
556          * all protection bits in the status register.
557          */
558         stat = epcs_status_rd ();
559         if (strcmp ("on", argv[2]) == 0) {
560                 stat |= dev->prot_mask;
561         } else if (strcmp ("off", argv[2]) == 0 ) {
562                 stat &= ~dev->prot_mask;
563         } else {
564                 printf ("epcs: unknown protection: %s\n", argv[2]);
565                 return;
566         }
567         epcs_status_wr (stat);
568         return;
569 }
570
571 static
572 void do_epcs_read (struct epcs_devinfo_t *dev, int argc, char * const argv[])
573 {
574         ulong addr,off,cnt;
575         ulong sz;
576
577         if (argc < 5) {
578                 printf ("USAGE: epcs read addr offset count\n");
579                 return;
580         }
581
582         sz = 1 << dev->size;
583         addr = simple_strtoul (argv[2], NULL, 16);
584         off  = simple_strtoul (argv[3], NULL, 16);
585         cnt  = simple_strtoul (argv[4], NULL, 16);
586         if (off > sz) {
587                 printf ("offset is greater than device size"
588                         "... aborting.\n");
589                 return;
590         }
591         if ((off + cnt) > sz) {
592                 printf ("request exceeds device size"
593                         "... truncating.\n");
594                 cnt = sz - off;
595         }
596         printf ("epcs: read %08lx <- %06lx (0x%lx bytes)\n",
597                         addr, off, cnt);
598         epcs_read (addr, off, cnt);
599
600         return;
601 }
602
603 static
604 void do_epcs_write (struct epcs_devinfo_t *dev, int argc, char * const argv[])
605 {
606         ulong addr,off,cnt;
607         ulong sz;
608         ulong err;
609
610         if (argc < 5) {
611                 printf ("USAGE: epcs write addr offset count\n");
612                 return;
613         }
614         if ((epcs_status_rd() & dev->prot_mask) != 0) {
615                 printf ( "epcs: device protected.\n");
616                 return;
617         }
618
619         sz = 1 << dev->size;
620         addr = simple_strtoul (argv[2], NULL, 16);
621         off  = simple_strtoul (argv[3], NULL, 16);
622         cnt  = simple_strtoul (argv[4], NULL, 16);
623         if (off > sz) {
624                 printf ("offset is greater than device size"
625                         "... aborting.\n");
626                 return;
627         }
628         if ((off + cnt) > sz) {
629                 printf ("request exceeds device size"
630                         "... truncating.\n");
631                 cnt = sz - off;
632         }
633         printf ("epcs: write %08lx -> %06lx (0x%lx bytes)\n",
634                         addr, off, cnt);
635         epcs_write (addr, off, cnt);
636         if (epcs_verify (addr, off, cnt, &err) != 0)
637                 printf ("epcs: write error at offset %06lx\n", err);
638
639         return;
640 }
641
642 static
643 void do_epcs_verify (struct epcs_devinfo_t *dev, int argc, char * const argv[])
644 {
645         ulong addr,off,cnt;
646         ulong sz;
647         ulong err;
648
649         if (argc < 5) {
650                 printf ("USAGE: epcs verify addr offset count\n");
651                 return;
652         }
653
654         sz = 1 << dev->size;
655         addr = simple_strtoul (argv[2], NULL, 16);
656         off  = simple_strtoul (argv[3], NULL, 16);
657         cnt  = simple_strtoul (argv[4], NULL, 16);
658         if (off > sz) {
659                 printf ("offset is greater than device size"
660                         "... aborting.\n");
661                 return;
662         }
663         if ((off + cnt) > sz) {
664                 printf ("request exceeds device size"
665                         "... truncating.\n");
666                 cnt = sz - off;
667         }
668         printf ("epcs: verify %08lx -> %06lx (0x%lx bytes)\n",
669                         addr, off, cnt);
670         if (epcs_verify (addr, off, cnt, &err) != 0)
671                 printf ("epcs: verify error at offset %06lx\n", err);
672
673         return;
674 }
675
676 /*-----------------------------------------------------------------------*/
677 int do_epcs (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
678 {
679         int len;
680         struct epcs_devinfo_t *dev = epcs_dev_find ();
681
682         if (!dev) {
683                 printf ("epcs: device not found.\n");
684                 return (-1);
685         }
686
687         if (argc < 2) {
688                 do_epcs_info (dev, argc, argv);
689                 return (0);
690         }
691
692         len = strlen (argv[1]);
693         if (strncmp ("info", argv[1], len) == 0) {
694                 do_epcs_info (dev, argc, argv);
695         } else if (strncmp ("erase", argv[1], len) == 0) {
696                 do_epcs_erase (dev, argc, argv);
697         } else if (strncmp ("protect", argv[1], len) == 0) {
698                 do_epcs_protect (dev, argc, argv);
699         } else if (strncmp ("read", argv[1], len) == 0) {
700                 do_epcs_read (dev, argc, argv);
701         } else if (strncmp ("write", argv[1], len) == 0) {
702                 do_epcs_write (dev, argc, argv);
703         } else if (strncmp ("verify", argv[1], len) == 0) {
704                 do_epcs_verify (dev, argc, argv);
705         } else {
706                 printf ("epcs: unknown operation: %s\n", argv[1]);
707         }
708
709         return (0);
710 }
711
712 /*-----------------------------------------------------------------------*/
713
714
715 U_BOOT_CMD( epcs, 5, 0, do_epcs, SHORT_HELP, LONG_HELP );
716
717 #endif /* CONFIG_NIOS_EPCS */