/****************************************************************************1* Genesis Plus2* SPI Serial EEPROM (25xxx/95xxx) support3*4* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)5*6* Redistribution and use of this code or any derivative works are permitted7* provided that the following conditions are met:8*9* - Redistributions may not be sold, nor may they be used in a commercial10* product or activity.11*12* - Redistributions that are modified from the original source must include the13* complete source code, including the source code for all components used by a14* binary built from the modified sources. However, as a special exception, the15* source code distributed need not include anything that is normally distributed16* (in either source or binary form) with the major components (compiler, kernel,17* and so on) of the operating system on which the executable runs, unless that18* component itself accompanies the executable.19*20* - Redistributions must reproduce the above copyright notice, this list of21* conditions and the following disclaimer in the documentation and/or other22* materials provided with the distribution.23*24* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"25* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE26* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE27* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE28* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR29* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF30* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN32* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)33* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE34* POSSIBILITY OF SUCH DAMAGE.35*36****************************************************************************************/3738#include "shared.h"39#include "eeprom_spi.h"4041/* max supported size 64KB (25x512/95x512) */42#define SIZE_MASK 0xffff43#define PAGE_MASK 0x7f4445/* hard-coded board implementation (!WP pin not used) */46#define BIT_DATA (0)47#define BIT_CLK (1)48#define BIT_HOLD (2)49#define BIT_CS (3)5051T_EEPROM_SPI spi_eeprom;5253void eeprom_spi_init()54{55/* reset eeprom state */56memset(&spi_eeprom, 0, sizeof(T_EEPROM_SPI));57spi_eeprom.out = 1;58spi_eeprom.state = GET_OPCODE;5960/* enable backup RAM */61sram.custom = 2;62sram.on = 1;63}6465void eeprom_spi_write(unsigned char data)66{67/* Make sure !HOLD is high */68if (data & (1 << BIT_HOLD))69{70/* Check !CS state */71if (data & (1 << BIT_CS))72{73/* !CS high -> end of current operation */74spi_eeprom.cycles = 0;75spi_eeprom.out = 1;76spi_eeprom.opcode = 0;77spi_eeprom.state = GET_OPCODE;78}79else80{81/* !CS low -> process current operation */82switch (spi_eeprom.state)83{84case GET_OPCODE:85{86/* latch data on CLK positive edge */87if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)88{89/* 8-bit opcode buffer */90spi_eeprom.opcode |= ((data >> BIT_DATA) & 1);91spi_eeprom.cycles++;9293/* last bit ? */94if (spi_eeprom.cycles == 8)95{96/* reset cycles count */97spi_eeprom.cycles = 0;9899/* Decode instruction */100switch (spi_eeprom.opcode)101{102case 0x01:103{104/* WRITE STATUS */105spi_eeprom.buffer = 0;106spi_eeprom.state = WRITE_BYTE;107break;108}109110case 0x02:111{112/* WRITE BYTE */113spi_eeprom.addr = 0;114spi_eeprom.state = GET_ADDRESS;115break;116}117118case 0x03:119{120/* READ BYTE */121spi_eeprom.addr = 0;122spi_eeprom.state = GET_ADDRESS;123break;124}125126case 0x04:127{128/* WRITE DISABLE */129spi_eeprom.status &= ~0x02;130spi_eeprom.state = STANDBY;131break;132}133134case 0x05:135{136/* READ STATUS */137spi_eeprom.buffer = spi_eeprom.status;138spi_eeprom.state = READ_BYTE;139break;140}141142case 0x06:143{144/* WRITE ENABLE */145spi_eeprom.status |= 0x02;146spi_eeprom.state = STANDBY;147break;148}149150default:151{152/* specific instructions (not supported) */153spi_eeprom.state = STANDBY;154break;155}156}157}158else159{160/* shift opcode value */161spi_eeprom.opcode = spi_eeprom.opcode << 1;162}163}164break;165}166167case GET_ADDRESS:168{169/* latch data on CLK positive edge */170if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)171{172/* 16-bit address */173spi_eeprom.addr |= ((data >> BIT_DATA) & 1);174spi_eeprom.cycles++;175176/* last bit ? */177if (spi_eeprom.cycles == 16)178{179/* reset cycles count */180spi_eeprom.cycles = 0;181182/* mask unused address bits */183spi_eeprom.addr &= SIZE_MASK;184185/* operation type */186if (spi_eeprom.opcode & 0x01)187{188/* READ operation */189spi_eeprom.buffer = sram.sram[spi_eeprom.addr];190spi_eeprom.state = READ_BYTE;191}192else193{194/* WRITE operation */195spi_eeprom.buffer = 0;196spi_eeprom.state = WRITE_BYTE;197}198}199else200{201/* shift address value */202spi_eeprom.addr = spi_eeprom.addr << 1;203}204}205break;206}207208case WRITE_BYTE:209{210/* latch data on CLK positive edge */211if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)212{213/* 8-bit data buffer */214spi_eeprom.buffer |= ((data >> BIT_DATA) & 1);215spi_eeprom.cycles++;216217/* last bit ? */218if (spi_eeprom.cycles == 8)219{220/* reset cycles count */221spi_eeprom.cycles = 0;222223/* write data to destination */224if (spi_eeprom.opcode & 0x01)225{226/* update status register */227spi_eeprom.status = (spi_eeprom.status & 0x02) | (spi_eeprom.buffer & 0x0c);228229/* wait for operation end */230spi_eeprom.state = STANDBY;231}232else233{234/* Memory Array (write-protected) */235if (spi_eeprom.status & 2)236{237/* check array protection bits (BP0, BP1) */238switch ((spi_eeprom.status >> 2) & 0x03)239{240case 0x01:241{242/* $C000-$FFFF (sector #3) is protected */243if (spi_eeprom.addr < 0xC000)244{245sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;246}247break;248}249250case 0x02:251{252/* $8000-$FFFF (sectors #2 and #3) is protected */253if (spi_eeprom.addr < 0x8000)254{255sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;256}257break;258}259260case 0x03:261{262/* $0000-$FFFF (all sectors) is protected */263break;264}265266default:267{268/* no sectors protected */269sram.sram[spi_eeprom.addr] = spi_eeprom.buffer;270break;271}272}273}274275/* reset data buffer */276spi_eeprom.buffer = 0;277278/* increase array address (sequential writes are limited within the same page) */279spi_eeprom.addr = (spi_eeprom.addr & ~PAGE_MASK) | ((spi_eeprom.addr + 1) & PAGE_MASK);280}281}282else283{284/* shift data buffer value */285spi_eeprom.buffer = spi_eeprom.buffer << 1;286}287}288break;289}290291case READ_BYTE:292{293/* output data on CLK positive edge */294if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)295{296/* read out bits */297spi_eeprom.out = (spi_eeprom.buffer >> (7 - spi_eeprom.cycles)) & 1;298spi_eeprom.cycles++;299300/* last bit ? */301if (spi_eeprom.cycles == 8)302{303/* reset cycles count */304spi_eeprom.cycles = 0;305306/* read from memory array ? */307if (spi_eeprom.opcode == 0x03)308{309/* read next array byte */310spi_eeprom.addr = (spi_eeprom.addr + 1) & SIZE_MASK;311spi_eeprom.buffer = sram.sram[spi_eeprom.addr];312}313}314}315break;316}317318default:319{320/* wait for !CS low->high transition */321break;322}323}324}325}326327/* update input lines */328spi_eeprom.cs = (data >> BIT_CS) & 1;329spi_eeprom.clk = (data >> BIT_CLK) & 1;330}331332unsigned int eeprom_spi_read(unsigned int address)333{334return (spi_eeprom.out << BIT_DATA);335}336337338339