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