symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/eeprom93xx.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU EEPROM 93xx emulation
       
     3  *
       
     4  * Copyright (c) 2006-2007 Stefan Weil
       
     5  *
       
     6  * This program is free software; you can redistribute it and/or modify
       
     7  * it under the terms of the GNU General Public License as published by
       
     8  * the Free Software Foundation; either version 2 of the License, or
       
     9  * (at your option) any later version.
       
    10  *
       
    11  * This program is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    14  * GNU General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License
       
    17  * along with this program; if not, write to the Free Software
       
    18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
       
    19  */
       
    20 
       
    21 /* Emulation for serial EEPROMs:
       
    22  * NMC93C06 256-Bit (16 x 16)
       
    23  * NMC93C46 1024-Bit (64 x 16)
       
    24  * NMC93C56 2028 Bit (128 x 16)
       
    25  * NMC93C66 4096 Bit (256 x 16)
       
    26  * Compatible devices include FM93C46 and others.
       
    27  *
       
    28  * Other drivers use these interface functions:
       
    29  * eeprom93xx_new   - add a new EEPROM (with 16, 64 or 256 words)
       
    30  * eeprom93xx_free  - destroy EEPROM
       
    31  * eeprom93xx_read  - read data from the EEPROM
       
    32  * eeprom93xx_write - write data to the EEPROM
       
    33  * eeprom93xx_data  - get EEPROM data array for external manipulation
       
    34  *
       
    35  * Todo list:
       
    36  * - No emulation of EEPROM timings.
       
    37  */
       
    38 
       
    39 #include <assert.h>
       
    40 #include "hw.h"
       
    41 #include "eeprom93xx.h"
       
    42 
       
    43 /* Debug EEPROM emulation. */
       
    44 //~ #define DEBUG_EEPROM
       
    45 
       
    46 #ifdef DEBUG_EEPROM
       
    47 #define logout(fmt, args...) fprintf(stderr, "EEPROM\t%-24s" fmt, __func__, ##args)
       
    48 #else
       
    49 #define logout(fmt, args...) ((void)0)
       
    50 #endif
       
    51 
       
    52 static int eeprom_instance = 0;
       
    53 static const int eeprom_version = 20061112;
       
    54 
       
    55 #if 0
       
    56 typedef enum {
       
    57   eeprom_read  = 0x80,   /* read register xx */
       
    58   eeprom_write = 0x40,   /* write register xx */
       
    59   eeprom_erase = 0xc0,   /* erase register xx */
       
    60   eeprom_ewen  = 0x30,   /* erase / write enable */
       
    61   eeprom_ewds  = 0x00,   /* erase / write disable */
       
    62   eeprom_eral  = 0x20,   /* erase all registers */
       
    63   eeprom_wral  = 0x10,   /* write all registers */
       
    64   eeprom_amask = 0x0f,
       
    65   eeprom_imask = 0xf0
       
    66 } eeprom_instruction_t;
       
    67 #endif
       
    68 
       
    69 #ifdef DEBUG_EEPROM
       
    70 static const char *opstring[] = {
       
    71   "extended", "write", "read", "erase"
       
    72 };
       
    73 #endif
       
    74 
       
    75 struct _eeprom_t {
       
    76     uint8_t  tick;
       
    77     uint8_t  address;
       
    78     uint8_t  command;
       
    79     uint8_t  writeable;
       
    80 
       
    81     uint8_t eecs;
       
    82     uint8_t eesk;
       
    83     uint8_t eedo;
       
    84 
       
    85     uint8_t  addrbits;
       
    86     uint8_t  size;
       
    87     uint16_t data;
       
    88     uint16_t contents[0];
       
    89 };
       
    90 
       
    91 /* Code for saving and restoring of EEPROM state. */
       
    92 
       
    93 static void eeprom_save(QEMUFile *f, void *opaque)
       
    94 {
       
    95     /* Save EEPROM data. */
       
    96     unsigned address;
       
    97     eeprom_t *eeprom = (eeprom_t *)opaque;
       
    98     qemu_put_buffer(f, (uint8_t *)eeprom, sizeof(*eeprom) - 2);
       
    99     qemu_put_be16(f, eeprom->data);
       
   100     for (address = 0; address < eeprom->size; address++) {
       
   101         qemu_put_be16(f, eeprom->contents[address]);
       
   102     }
       
   103 }
       
   104 
       
   105 static int eeprom_load(QEMUFile *f, void *opaque, int version_id)
       
   106 {
       
   107     /* Load EEPROM data from saved data if version and EEPROM size
       
   108        of data and current EEPROM are identical. */
       
   109     eeprom_t *eeprom = (eeprom_t *)opaque;
       
   110     int result = -EINVAL;
       
   111     if (version_id == eeprom_version) {
       
   112         unsigned address;
       
   113         uint8_t size = eeprom->size;
       
   114         qemu_get_buffer(f, (uint8_t *)eeprom, sizeof(*eeprom) - 2);
       
   115         if (eeprom->size == size) {
       
   116             eeprom->data = qemu_get_be16(f);
       
   117             for (address = 0; address < eeprom->size; address++) {
       
   118                 eeprom->contents[address] = qemu_get_be16(f);
       
   119             }
       
   120             result = 0;
       
   121         }
       
   122     }
       
   123     return result;
       
   124 }
       
   125 
       
   126 void eeprom93xx_write(eeprom_t *eeprom, int eecs, int eesk, int eedi)
       
   127 {
       
   128     uint8_t tick = eeprom->tick;
       
   129     uint8_t eedo = eeprom->eedo;
       
   130     uint16_t address = eeprom->address;
       
   131     uint8_t command = eeprom->command;
       
   132 
       
   133     logout("CS=%u SK=%u DI=%u DO=%u, tick = %u\n",
       
   134            eecs, eesk, eedi, eedo, tick);
       
   135 
       
   136     if (! eeprom->eecs && eecs) {
       
   137         /* Start chip select cycle. */
       
   138         logout("Cycle start, waiting for 1st start bit (0)\n");
       
   139         tick = 0;
       
   140         command = 0x0;
       
   141         address = 0x0;
       
   142     } else if (eeprom->eecs && ! eecs) {
       
   143         /* End chip select cycle. This triggers write / erase. */
       
   144         if (eeprom->writeable) {
       
   145             uint8_t subcommand = address >> (eeprom->addrbits - 2);
       
   146             if (command == 0 && subcommand == 2) {
       
   147                 /* Erase all. */
       
   148                 for (address = 0; address < eeprom->size; address++) {
       
   149                     eeprom->contents[address] = 0xffff;
       
   150                 }
       
   151             } else if (command == 3) {
       
   152                 /* Erase word. */
       
   153                 eeprom->contents[address] = 0xffff;
       
   154             } else if (tick >= 2 + 2 + eeprom->addrbits + 16) {
       
   155                 if (command == 1) {
       
   156                     /* Write word. */
       
   157                     eeprom->contents[address] &= eeprom->data;
       
   158                 } else if (command == 0 && subcommand == 1) {
       
   159                     /* Write all. */
       
   160                     for (address = 0; address < eeprom->size; address++) {
       
   161                         eeprom->contents[address] &= eeprom->data;
       
   162                     }
       
   163                 }
       
   164             }
       
   165         }
       
   166         /* Output DO is tristate, read results in 1. */
       
   167         eedo = 1;
       
   168     } else if (eecs && ! eeprom->eesk && eesk) {
       
   169         /* Raising edge of clock shifts data in. */
       
   170         if (tick == 0) {
       
   171             /* Wait for 1st start bit. */
       
   172             if (eedi == 0) {
       
   173                 logout("Got correct 1st start bit, waiting for 2nd start bit (1)\n");
       
   174                 tick++;
       
   175             } else {
       
   176                 logout("wrong 1st start bit (is 1, should be 0)\n");
       
   177                 tick = 2;
       
   178                 //~ assert(!"wrong start bit");
       
   179             }
       
   180         } else if (tick == 1) {
       
   181             /* Wait for 2nd start bit. */
       
   182             if (eedi != 0) {
       
   183                 logout("Got correct 2nd start bit, getting command + address\n");
       
   184                 tick++;
       
   185             } else {
       
   186                 logout("1st start bit is longer than needed\n");
       
   187             }
       
   188         } else if (tick < 2 + 2) {
       
   189             /* Got 2 start bits, transfer 2 opcode bits. */
       
   190             tick++;
       
   191             command <<= 1;
       
   192             if (eedi) {
       
   193                 command += 1;
       
   194             }
       
   195         } else if (tick < 2 + 2 + eeprom->addrbits) {
       
   196             /* Got 2 start bits and 2 opcode bits, transfer all address bits. */
       
   197             tick++;
       
   198             address = ((address << 1) | eedi);
       
   199             if (tick == 2 + 2 + eeprom->addrbits) {
       
   200                 logout("%s command, address = 0x%02x (value 0x%04x)\n",
       
   201                        opstring[command], address, eeprom->contents[address]);
       
   202                 if (command == 2) {
       
   203                     eedo = 0;
       
   204                 }
       
   205                 address = address % eeprom->size;
       
   206                 if (command == 0) {
       
   207                     /* Command code in upper 2 bits of address. */
       
   208                     switch (address >> (eeprom->addrbits - 2)) {
       
   209                         case 0:
       
   210                             logout("write disable command\n");
       
   211                             eeprom->writeable = 0;
       
   212                             break;
       
   213                         case 1:
       
   214                             logout("write all command\n");
       
   215                             break;
       
   216                         case 2:
       
   217                             logout("erase all command\n");
       
   218                             break;
       
   219                         case 3:
       
   220                             logout("write enable command\n");
       
   221                             eeprom->writeable = 1;
       
   222                             break;
       
   223                     }
       
   224                 } else {
       
   225                     /* Read, write or erase word. */
       
   226                     eeprom->data = eeprom->contents[address];
       
   227                 }
       
   228             }
       
   229         } else if (tick < 2 + 2 + eeprom->addrbits + 16) {
       
   230             /* Transfer 16 data bits. */
       
   231             tick++;
       
   232             if (command == 2) {
       
   233                 /* Read word. */
       
   234                 eedo = ((eeprom->data & 0x8000) != 0);
       
   235             }
       
   236             eeprom->data <<= 1;
       
   237             eeprom->data += eedi;
       
   238         } else {
       
   239             logout("additional unneeded tick, not processed\n");
       
   240         }
       
   241     }
       
   242     /* Save status of EEPROM. */
       
   243     eeprom->tick = tick;
       
   244     eeprom->eecs = eecs;
       
   245     eeprom->eesk = eesk;
       
   246     eeprom->eedo = eedo;
       
   247     eeprom->address = address;
       
   248     eeprom->command = command;
       
   249 }
       
   250 
       
   251 uint16_t eeprom93xx_read(eeprom_t *eeprom)
       
   252 {
       
   253     /* Return status of pin DO (0 or 1). */
       
   254     logout("CS=%u DO=%u\n", eeprom->eecs, eeprom->eedo);
       
   255     return (eeprom->eedo);
       
   256 }
       
   257 
       
   258 #if 0
       
   259 void eeprom93xx_reset(eeprom_t *eeprom)
       
   260 {
       
   261     /* prepare eeprom */
       
   262     logout("eeprom = 0x%p\n", eeprom);
       
   263     eeprom->tick = 0;
       
   264     eeprom->command = 0;
       
   265 }
       
   266 #endif
       
   267 
       
   268 eeprom_t *eeprom93xx_new(uint16_t nwords)
       
   269 {
       
   270     /* Add a new EEPROM (with 16, 64 or 256 words). */
       
   271     eeprom_t *eeprom;
       
   272     uint8_t addrbits;
       
   273 
       
   274     switch (nwords) {
       
   275         case 16:
       
   276         case 64:
       
   277             addrbits = 6;
       
   278             break;
       
   279         case 128:
       
   280         case 256:
       
   281             addrbits = 8;
       
   282             break;
       
   283         default:
       
   284             assert(!"Unsupported EEPROM size, fallback to 64 words!");
       
   285             nwords = 64;
       
   286             addrbits = 6;
       
   287     }
       
   288 
       
   289     eeprom = (eeprom_t *)qemu_mallocz(sizeof(*eeprom) + nwords * 2);
       
   290     eeprom->size = nwords;
       
   291     eeprom->addrbits = addrbits;
       
   292     /* Output DO is tristate, read results in 1. */
       
   293     eeprom->eedo = 1;
       
   294     logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords);
       
   295     register_savevm("eeprom", eeprom_instance, eeprom_version,
       
   296                     eeprom_save, eeprom_load, eeprom);
       
   297     return eeprom;
       
   298 }
       
   299 
       
   300 void eeprom93xx_free(eeprom_t *eeprom)
       
   301 {
       
   302     /* Destroy EEPROM. */
       
   303     logout("eeprom = 0x%p\n", eeprom);
       
   304     qemu_free(eeprom);
       
   305 }
       
   306 
       
   307 uint16_t *eeprom93xx_data(eeprom_t *eeprom)
       
   308 {
       
   309     /* Get EEPROM data array. */
       
   310     return &eeprom->contents[0];
       
   311 }
       
   312 
       
   313 /* eof */