(linenum→info "unix/slp.c:2238")

qemu/0.9.1/hw/eeprom93xx.c

    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 */
Syntax (Markdown)