DevEEPROM.cpp revision f0679a3d26514d6f85279a4018282ead9ff0f067
0N/A/* $Id$ */
0N/A/** @file
0N/A * DevEEPROM - Microware-compatible 64x16-bit 93C46 EEPROM Emulation.
0N/A */
0N/A
0N/A/*
0N/A * Copyright (C) 2007 Sun Microsystems, Inc.
0N/A *
0N/A * This file is part of VirtualBox Open Source Edition (OSE), as
0N/A * available from http://www.virtualbox.org. This file is free software;
0N/A * you can redistribute it and/or modify it under the terms of the GNU
0N/A * General Public License (GPL) as published by the Free Software
0N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
0N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
0N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
0N/A *
0N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
0N/A * Clara, CA 95054 USA or visit http://www.sun.com if you need
0N/A * additional information or have any questions.
0N/A */
0N/A
0N/A#define LOG_GROUP LOG_GROUP_DEV_E1000 /// @todo Add a EEPROM logging group.
0N/A#include <VBox/log.h>
0N/A#include <VBox/pdmdev.h>
0N/A#include <iprt/string.h>
0N/A#include "DevEEPROM.h"
0N/A
0N/A#define E1kLog(a) Log(a)
0N/A
0N/A/**
0N/A * Initialize EEPROM device.
0N/A *
0N/A * @param pu16Initial Initial EEPROM content (optional). The size of initial
0N/A * content must be sizeof(uint16_t)*EEPROM93C46::SIZE
0N/A * bytes.
0N/A */
0N/Avoid EEPROM93C46::init(const uint16_t *pu16Initial)
0N/A{
0N/A if ( pu16Initial ) {
0N/A memcpy(this->m_au16Data, pu16Initial, sizeof(this->m_au16Data));
0N/A } else {
0N/A memset(this->m_au16Data, 0, sizeof(this->m_au16Data));
0N/A }
0N/A m_fWriteEnabled = false;
0N/A m_u32InternalWires = 0;
0N/A m_eState = STANDBY;
0N/A}
0N/A
0N/A/**
0N/A * Writes one word to specified location if write is enabled.
0N/A *
0N/A * @param u32Addr Address to write at
0N/A * @param u16Value Value to store
0N/A */
0N/Avoid EEPROM93C46::storeWord(uint32_t u32Addr, uint16_t u16Value)
0N/A{
0N/A if (m_fWriteEnabled) {
0N/A E1kLog(("EEPROM: Stored word %04x at %08x\n", u16Value, u32Addr));
0N/A m_au16Data[u32Addr] = u16Value;
0N/A }
0N/A m_u16Mask = DATA_MSB;
0N/A}
0N/A
0N/A/**
0N/A * Reads one word at specified location.
0N/A *
0N/A * @returns True if read was successful.
0N/A *
0N/A * @param u32Addr Address to read from
0N/A * @param pu16Value Placeholder to store the value
0N/A */
0N/Abool EEPROM93C46::readWord(uint32_t u32Addr, uint16_t *pu16Value)
0N/A{
0N/A if (u32Addr < SIZE)
0N/A {
0N/A *pu16Value = m_au16Data[u32Addr];
0N/A return true;
0N/A }
0N/A
0N/A return false;
0N/A}
0N/A
0N/A/**
0N/A * Fetch next word pointer by m_u16Addr.
0N/A *
0N/A * m_u16Addr is advanced and mask is reset to support sequential reads.
0N/A *
0N/A * @returns New state
0N/A */
0N/AEEPROM93C46::State EEPROM93C46::opRead()
0N/A{
0N/A m_u16Word = m_au16Data[m_u16Addr++];
0N/A E1kLog(("EEPROM: Reading word %04x at %08x\n", m_u16Word, m_u16Addr-1));
0N/A m_u16Mask = DATA_MSB;
0N/A return WRITING_DO;
0N/A}
0N/A
0N/A/**
0N/A * Write the value of m_u16Word to the location specified by m_u16Addr.
0N/A *
0N/A * @returns New state
0N/A *
0N/A * @remarks Need to wait for CS lower/raise to show busy/ready indication.
0N/A */
0N/AEEPROM93C46::State EEPROM93C46::opWrite()
0N/A{
0N/A storeWord(m_u16Addr, m_u16Word);
0N/A return WAITING_CS_FALL;
0N/A}
0N/A
0N/A/**
0N/A * Overwrite the entire contents of EEPROM with the value of m_u16Word.
0N/A *
0N/A * @returns New state
0N/A *
0N/A * @remarks Need to wait for CS lower/raise to show busy/ready indication.
0N/A */
0N/AEEPROM93C46::State EEPROM93C46::opWriteAll()
0N/A{
0N/A for (int i = 0; i < SIZE; i++)
0N/A storeWord(i, m_u16Word);
0N/A return WAITING_CS_FALL;
0N/A}
0N/A
0N/A/**
0N/A * Decode opcode and address from 'opAddr' member.
0N/A *
0N/A * Decodes operation and executes it immediately if possible; otherwise, stores
0N/A * the decoded operation and address.
0N/A *
0N/A * @returns New state
0N/A */
0N/AEEPROM93C46::State EEPROM93C46::opDecode()
0N/A{
0N/A switch (m_u16Word>>6) {
0N/A case 3: /* ERASE */
0N/A storeWord(m_u16Word & ADDR_MASK, 0xFFFF);
0N/A return WAITING_CS_FALL;
0N/A case 2: /* READ */
0N/A m_eOp = OP_READ;
0N/A m_u16Addr = m_u16Word & ADDR_MASK;
0N/A return opRead(); /* Load first word */
0N/A case 1: /* WRITE */
0N/A m_eOp = OP_WRITE;
0N/A m_u16Addr = m_u16Word & ADDR_MASK;
0N/A m_u16Word = 0;
0N/A m_u16Mask = DATA_MSB;
0N/A return READING_DI;
0N/A case 0:
0N/A switch (m_u16Word>>4) {
0N/A case 0: /* ERASE/WRITE DISABLE */
0N/A m_fWriteEnabled = false;
0N/A return STANDBY;
0N/A case 1: /* WRITE ALL */
0N/A m_eOp = OP_WRITE_ALL;
0N/A m_u16Word = 0;
0N/A m_u16Mask = DATA_MSB;
0N/A return READING_DI;
0N/A case 2: /* ERASE ALL */
0N/A /* Re-use opWriteAll */
0N/A m_u16Word = 0xFFFF;
0N/A return opWriteAll();
0N/A case 3: /* ERASE/WRITE ENABLE */
0N/A m_fWriteEnabled = true;
0N/A return STANDBY;
0N/A }
0N/A }
0N/A return m_eState;
0N/A}
0N/A
0N/A/**
0N/A * Set bits in EEPROM 4-wire interface.
0N/A *
0N/A * @param u32Wires Values of DI, CS, SK.
0N/A * @remarks The value of DO bit in 'u32Wires' is ignored.
0N/A */
0N/Avoid EEPROM93C46::write(uint32_t u32Wires)
0N/A{
0N/A if (u32Wires & WIRES_CS) {
0N/A if (!(m_u32InternalWires & WIRES_SK) && (u32Wires & WIRES_SK)) {
0N/A /* Positive edge of clock */
0N/A if (m_eState == STANDBY) {
0N/A if (u32Wires & WIRES_DI) {
0N/A m_eState = READING_DI;
0N/A m_eOp = OP_DECODE;
0N/A m_u16Mask = OPADDR_MSB;
0N/A m_u16Word = 0;
0N/A }
0N/A }
0N/A else {
0N/A if (m_eState == READING_DI) {
0N/A if (u32Wires & WIRES_DI) {
0N/A m_u16Word |= m_u16Mask;
0N/A }
0N/A }
0N/A else if (m_eState == WRITING_DO) {
0N/A m_u32InternalWires &= ~WIRES_DO;
0N/A if (m_u16Word & m_u16Mask) {
0N/A m_u32InternalWires |= WIRES_DO;
0N/A }
0N/A }
0N/A else return;
0N/A /* Next bit */
0N/A m_u16Mask >>= 1;
0N/A if (m_u16Mask == 0)
0N/A {
0N/A switch (this->m_eOp)
17N/A {
0N/A case OP_READ:
0N/A m_eState = opRead();
0N/A break;
0N/A case OP_WRITE:
0N/A m_eState = opWrite();
0N/A break;
0N/A case OP_WRITE_ALL:
0N/A m_eState = opWriteAll();
0N/A break;
0N/A case OP_DECODE:
0N/A m_eState = opDecode();
0N/A break;
0N/A default:
0N/A ;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A else if (m_eState == WAITING_CS_RISE) {
0N/A m_u32InternalWires |= WIRES_DO; /* ready */
0N/A m_eState = STANDBY;
0N/A }
0N/A }
0N/A else {
0N/A switch(m_eState) {
0N/A case WAITING_CS_FALL:
0N/A m_eState = WAITING_CS_RISE;
0N/A m_u32InternalWires &= ~WIRES_DO; /* busy */
0N/A break;
0N/A case WAITING_CS_RISE:
0N/A break;
0N/A case READING_DI:
0N/A m_u32InternalWires &= ~WIRES_DO; /* Clear ready/busy status from DO. */
0N/A /* Fall through! */
0N/A default:
0N/A m_eState = STANDBY;
0N/A break;
0N/A }
0N/A }
0N/A m_u32InternalWires &= WIRES_DO;
0N/A m_u32InternalWires |= u32Wires & ~WIRES_DO; /* Do not overwrite DO */
0N/A}
0N/A
0N/A/**
0N/A * Read bits in EEPROM 4-wire interface.
0N/A *
0N/A * @returns Current values of DO, DI, CS, SK.
0N/A *
0N/A * @remarks Only DO is controlled by EEPROM, other bits are returned as they
0N/A * were written by 'write'.
0N/A */
0N/Auint32_t EEPROM93C46::read()
0N/A{
0N/A return m_u32InternalWires;
0N/A}
0N/A
0N/Avoid EEPROM93C46::save(PSSMHANDLE pSSM)
0N/A{
0N/A SSMR3PutU8( pSSM, EEPROM93C46_SAVEDSTATE_VERSION);
0N/A SSMR3PutU8( pSSM, m_eState);
0N/A SSMR3PutU8( pSSM, m_eOp);
0N/A SSMR3PutBool(pSSM, m_fWriteEnabled);
0N/A SSMR3PutU32( pSSM, m_u32InternalWires);
0N/A SSMR3PutU16( pSSM, m_u16Word);
0N/A SSMR3PutU16( pSSM, m_u16Mask);
0N/A SSMR3PutU16( pSSM, m_u16Addr);
0N/A SSMR3PutMem( pSSM, m_au16Data, sizeof(m_au16Data));
0N/A}
0N/A
0N/Aint EEPROM93C46::load(PSSMHANDLE pSSM)
0N/A{
0N/A int rc;
0N/A uint8_t uVersion;
0N/A
0N/A rc = SSMR3GetU8(pSSM, &uVersion);
0N/A AssertRCReturn(rc, rc);
0N/A if (uVersion != EEPROM93C46_SAVEDSTATE_VERSION)
0N/A return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
0N/A
0N/A SSMR3GetU8( pSSM, (uint8_t*)&m_eState);
0N/A SSMR3GetU8( pSSM, (uint8_t*)&m_eOp);
0N/A SSMR3GetBool(pSSM, &m_fWriteEnabled);
0N/A SSMR3GetU32( pSSM, &m_u32InternalWires);
0N/A SSMR3GetU16( pSSM, &m_u16Word);
0N/A SSMR3GetU16( pSSM, &m_u16Mask);
0N/A SSMR3GetU16( pSSM, &m_u16Addr);
0N/A return SSMR3GetMem( pSSM, m_au16Data, sizeof(m_au16Data));
0N/A}
48N/A