symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/syborg_nand.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * Syborg NAND flash controller
       
     3  *
       
     4  * Copyright (c) 2009 CodeSourcery
       
     5  *
       
     6  * Permission is hereby granted, free of charge, to any person obtaining a copy
       
     7  * of this software and associated documentation files (the "Software"), to deal
       
     8  * in the Software without restriction, including without limitation the rights
       
     9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    10  * copies of the Software, and to permit persons to whom the Software is
       
    11  * furnished to do so, subject to the following conditions:
       
    12  *
       
    13  * The above copyright notice and this permission notice shall be included in
       
    14  * all copies or substantial portions of the Software.
       
    15  *
       
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    22  * THE SOFTWARE.
       
    23  */
       
    24 
       
    25 #include "hw.h"
       
    26 #include "syborg.h"
       
    27 #include "devtree.h"
       
    28 #include "block.h"
       
    29 #include "flash.h"
       
    30 
       
    31 //#define DEBUG_SYBORG_NAND
       
    32 
       
    33 #ifdef DEBUG_SYBORG_NAND
       
    34 #define DPRINTF(fmt, args...) \
       
    35 do { printf("syborg_nand: " fmt , ##args); } while (0)
       
    36 #define BADF(fmt, args...) \
       
    37 do { fprintf(stderr, "syborg_nand: error: " fmt , ##args); exit(1);} while (0)
       
    38 #else
       
    39 #define DPRINTF(fmt, args...) do {} while(0)
       
    40 #define BADF(fmt, args...) \
       
    41 do { fprintf(stderr, "syborg_nand: error: " fmt , ##args);} while (0)
       
    42 #endif
       
    43 
       
    44 enum {
       
    45     SNAND_ID            = 0,
       
    46     SNAND_DATA          = 1,
       
    47     SNAND_CTL           = 2,
       
    48     SNAND_ECC_COUNT     = 3,
       
    49     SNAND_ECC_CP        = 4,
       
    50     SNAND_ECC_LP        = 5
       
    51 };
       
    52 
       
    53 #define SNAND_CTL_CLE   0x01
       
    54 #define SNAND_CTL_ALE   0x02
       
    55 #define SNAND_CTL_CE    0x04
       
    56 #define SNAND_CTL_WP    0x08
       
    57 #define SNAND_CTL_RB    0x10
       
    58 #define SNAND_CTL_IN_MASK \
       
    59   (SNAND_CTL_CLE | SNAND_CTL_ALE | SNAND_CTL_CE | SNAND_CTL_WP)
       
    60 
       
    61 typedef struct {
       
    62     QEMUDevice *qdev;
       
    63     struct nand_flash_s *nand;
       
    64     struct ecc_state_s ecc;
       
    65     uint32_t ctl;
       
    66 } syborg_nand_state;
       
    67 
       
    68 static uint32_t syborg_nand_ecc_lp(syborg_nand_state *s)
       
    69 {
       
    70     uint32_t v0;
       
    71     uint32_t v1;
       
    72 
       
    73     v0 = s->ecc.lp[0];
       
    74     v1 = s->ecc.lp[1];
       
    75 
       
    76 #define ODD(n) ((v0 << n) & (1 << (2 * n)))
       
    77 #define EVEN(n) ((v1 << (n + 1)) & (1 << (2 * n + 1)))
       
    78     /* Interleave parity bits.  */
       
    79     return ODD( 0) | ODD( 1) | ODD( 2) | ODD( 3)
       
    80          | ODD( 4) | ODD( 5) | ODD( 6) | ODD( 7)
       
    81          | ODD( 8) | ODD( 9) | ODD(10) | ODD(11)
       
    82          | ODD(12) | ODD(13) | ODD(14) | ODD(15)
       
    83          | EVEN( 0) | EVEN( 1) | EVEN( 2) | EVEN( 3)
       
    84          | EVEN( 4) | EVEN( 5) | EVEN( 6) | EVEN( 7)
       
    85          | EVEN( 8) | EVEN( 9) | EVEN(10) | EVEN(11)
       
    86          | EVEN(12) | EVEN(13) | EVEN(14) | EVEN(15);
       
    87 #undef ODD
       
    88 #undef EVEN
       
    89 }
       
    90 
       
    91 static uint32_t syborg_nand_read(void *opaque, target_phys_addr_t offset)
       
    92 {
       
    93     syborg_nand_state *s = (syborg_nand_state *)opaque;
       
    94     
       
    95     offset &= 0xfff;
       
    96     DPRINTF("read 0x%x\n", (int)offset);
       
    97     switch(offset >> 2) {
       
    98     case SNAND_ID:
       
    99         return SYBORG_ID_NAND;
       
   100     case SNAND_DATA:
       
   101         return ecc_digest(&s->ecc, nand_getio(s->nand));
       
   102     case SNAND_CTL:
       
   103         {
       
   104             int rb;
       
   105             nand_getpins(s->nand, &rb);
       
   106             return s->ctl | (rb ? SNAND_CTL_RB : 0);
       
   107         }
       
   108     case SNAND_ECC_COUNT:
       
   109         return s->ecc.count;
       
   110     case SNAND_ECC_CP:
       
   111         return s->ecc.cp;
       
   112     case SNAND_ECC_LP:
       
   113         return syborg_nand_ecc_lp(s);
       
   114     default:
       
   115         BADF("Bad read offset 0x%x\n", (int)offset);
       
   116         return 0;  
       
   117     }
       
   118 }
       
   119 
       
   120 static void syborg_nand_write(void *opaque, target_phys_addr_t offset,
       
   121                               uint32_t value)
       
   122 {
       
   123     syborg_nand_state *s = (syborg_nand_state *)opaque;
       
   124     
       
   125     offset &= 0xfff;
       
   126     DPRINTF("Write 0x%x=0x%x\n", (int)offset, value);
       
   127     switch (offset >> 2) {
       
   128     case SNAND_DATA:
       
   129         nand_setio(s->nand, ecc_digest(&s->ecc, value & 0xff));
       
   130         break;
       
   131     case SNAND_CTL:
       
   132         s->ctl = value & SNAND_CTL_IN_MASK;
       
   133         nand_setpins(s->nand,
       
   134                      (value & SNAND_CTL_CLE) != 0,
       
   135                      (value & SNAND_CTL_ALE) != 0,
       
   136                      (value & SNAND_CTL_CE) != 0,
       
   137                      (value & SNAND_CTL_WP) != 0,
       
   138                      0);
       
   139         break;
       
   140     case SNAND_ECC_COUNT:
       
   141         if (value != 0) {
       
   142             BADF("Bad write to ECC count\n");
       
   143         }
       
   144         ecc_reset(&s->ecc);
       
   145         break;
       
   146     default:
       
   147         BADF("Bad write offset 0x%x\n", (int)offset);
       
   148         break;
       
   149     }
       
   150 }
       
   151 
       
   152 static CPUReadMemoryFunc *syborg_nand_readfn[] = {
       
   153      syborg_nand_read,
       
   154      syborg_nand_read,
       
   155      syborg_nand_read
       
   156 };
       
   157 
       
   158 static CPUWriteMemoryFunc *syborg_nand_writefn[] = {
       
   159      syborg_nand_write,
       
   160      syborg_nand_write,
       
   161      syborg_nand_write
       
   162 };
       
   163 
       
   164 static void syborg_nand_save(QEMUFile *f, void *opaque)
       
   165 {
       
   166     syborg_nand_state *s = opaque;
       
   167 
       
   168     qemu_put_be32(f, s->ctl);
       
   169     ecc_put(f, &s->ecc);
       
   170 }
       
   171 
       
   172 static int syborg_nand_load(QEMUFile *f, void *opaque, int version_id)
       
   173 {
       
   174     syborg_nand_state *s = opaque;
       
   175 
       
   176     if (version_id != 1)
       
   177         return -EINVAL;
       
   178 
       
   179     s->ctl = qemu_get_be32(f);
       
   180     ecc_get(f, &s->ecc);
       
   181 
       
   182     return 0;
       
   183 }
       
   184 
       
   185 static const struct
       
   186 {
       
   187     int size;
       
   188     int id;
       
   189 } nand_chips[] =
       
   190 {
       
   191     {1, 0x6e},
       
   192     {2, 0x64},
       
   193     {4, 0x6b},
       
   194     {8, 0xd6},
       
   195     {16, 0x33},
       
   196     {32, 0x35},
       
   197     {64, 0x36},
       
   198     {128, 0x78},
       
   199     {256, 0x71},
       
   200     {512, 0xa2},
       
   201     {1024, 0xa1},
       
   202     {2048, 0xaa},
       
   203     {4096, 0xac},
       
   204     {8192, 0xa3},
       
   205     {16384, 0xa5},
       
   206     {0, 0}
       
   207 };
       
   208 
       
   209 static void syborg_nand_create(QEMUDevice *dev)
       
   210 {
       
   211     int n;
       
   212     int size;
       
   213     syborg_nand_state *s;
       
   214     s = (syborg_nand_state *)qemu_mallocz(sizeof(syborg_nand_state));
       
   215     s->qdev = dev;
       
   216     qdev_set_opaque(dev, s);
       
   217 
       
   218     size = qdev_get_property_int(dev, "size");
       
   219     n = 0;
       
   220     while (nand_chips[n].size && nand_chips[n].size != size) {
       
   221         n++;
       
   222     }
       
   223     if (nand_chips[n].size == 0) {
       
   224         BADF("Unsuppored chip size: %d\n", size);
       
   225         exit(1);
       
   226     }
       
   227     s->nand = nand_init(NAND_MFR_SAMSUNG, nand_chips[n].id);
       
   228     ecc_reset(&s->ecc);
       
   229 }
       
   230 
       
   231 void syborg_nand_register(void)
       
   232 {
       
   233     QEMUDeviceClass *dc;
       
   234     dc = qdev_new("syborg,nand", syborg_nand_create, 0);
       
   235     qdev_add_registers(dc, syborg_nand_readfn, syborg_nand_writefn, 0x1000);
       
   236     qdev_add_property_int(dc, "size", 0);
       
   237     qdev_add_savevm(dc, 1, syborg_nand_save, syborg_nand_load);
       
   238 }