uboot/arch/nios2/cpu/epcs.c
<<
>>
Prefs
   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
  59static nios_spi_t *epcs = (nios_spi_t *)CONFIG_SYS_NIOS_EPCSBASE;
  60
  61/***********************************************************************
  62 * Device access
  63 ***********************************************************************/
  64static 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
  85static 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
  97static 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
 108static unsigned char bitrev[] = {
 109        0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e,
 110        0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f
 111};
 112
 113static 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
 122static void epcs_rcv (unsigned char *dst, int len)
 123{
 124        while (len--) {
 125                epcs_tx (0);
 126                *dst++ = epcs_rx ();
 127        }
 128}
 129
 130static 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
 138static void epcs_snd (unsigned char *src, int len)
 139{
 140        while (len--) {
 141                epcs_tx (*src++);
 142                epcs_rx ();
 143        }
 144}
 145
 146static void epcs_rsnd (unsigned char *src, int len)
 147{
 148        while (len--) {
 149                epcs_tx (epcs_bitrev (*src++));
 150                epcs_rx ();
 151        }
 152}
 153
 154static 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
 162static 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
 175static 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
 191static 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
 199int 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
 214epcs_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 ***********************************************************************/
 252int 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
 302int 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
 343int 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
 364int 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
 405int 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
 429static 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 ***********************************************************************/
 458static
 459void 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
 505static
 506void 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
 535static
 536void 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
 571static
 572void 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
 603static
 604void 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
 642static
 643void 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/*-----------------------------------------------------------------------*/
 677int 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
 715U_BOOT_CMD( epcs, 5, 0, do_epcs, SHORT_HELP, LONG_HELP );
 716
 717#endif /* CONFIG_NIOS_EPCS */
 718