integrate SPI EEPROM
[picodrive.git] / pico / carthw / eeprom_spi.c
1 /****************************************************************************
2  *  Genesis Plus
3  *  SPI Serial EEPROM (25xxx/95xxx) support
4  *
5  *  Copyright (C) 2012  Eke-Eke (Genesis Plus GX)
6  *
7  *  Redistribution and use of this code or any derivative works are permitted
8  *  provided that the following conditions are met:
9  *
10  *   - Redistributions may not be sold, nor may they be used in a commercial
11  *     product or activity.
12  *
13  *   - Redistributions that are modified from the original source must include the
14  *     complete source code, including the source code for all components used by a
15  *     binary built from the modified sources. However, as a special exception, the
16  *     source code distributed need not include anything that is normally distributed
17  *     (in either source or binary form) with the major components (compiler, kernel,
18  *     and so on) of the operating system on which the executable runs, unless that
19  *     component itself accompanies the executable.
20  *
21  *   - Redistributions must reproduce the above copyright notice, this list of
22  *     conditions and the following disclaimer in the documentation and/or other
23  *     materials provided with the distribution.
24  *
25  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
29  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  *  POSSIBILITY OF SUCH DAMAGE.
36  *
37  ****************************************************************************************/
38
39 #include "../pico_int.h"
40 #include "../cd/genplus_macros.h"
41 #include "eeprom_spi.h"
42
43 /* max supported size 64KB (25x512/95x512) */
44 #define SIZE_MASK 0xffff
45 #define PAGE_MASK 0x7f
46
47 /* hard-coded board implementation (!WP pin not used) */
48 #define BIT_DATA (0)
49 #define BIT_CLK  (1)
50 #define BIT_HOLD (2)
51 #define BIT_CS   (3)
52
53 typedef enum
54 {
55   STANDBY,
56   GET_OPCODE,
57   GET_ADDRESS,
58   WRITE_BYTE,
59   READ_BYTE
60 } T_STATE_SPI;
61
62 typedef struct
63 {
64   uint8 cs;           /* !CS line state */
65   uint8 clk;          /* SCLK line state */
66   uint8 out;          /* SO line state */
67   uint8 status;       /* status register */
68   uint8 opcode;       /* 8-bit opcode */
69   uint8 buffer;       /* 8-bit data buffer */
70   uint16 addr;        /* 16-bit address */
71   uint32 cycles;      /* current operation cycle */
72   T_STATE_SPI state;  /* current operation state */
73 } T_EEPROM_SPI;
74
75 static T_EEPROM_SPI spi_eeprom;
76
77 void *eeprom_spi_init(int *size)
78 {
79   /* reset eeprom state */
80   memset(&spi_eeprom, 0, sizeof(T_EEPROM_SPI));
81   spi_eeprom.out = 1;
82   spi_eeprom.state = GET_OPCODE;
83
84   if (size)
85     *size = sizeof(T_EEPROM_SPI);
86   return &spi_eeprom;
87 }
88
89 void eeprom_spi_write(unsigned char data)
90 {
91   /* Make sure !HOLD is high */
92   if (data & (1 << BIT_HOLD))
93   {
94     /* Check !CS state */
95     if (data & (1 << BIT_CS))
96     {
97       /* !CS high -> end of current operation */
98       spi_eeprom.cycles = 0;
99       spi_eeprom.out = 1;
100       spi_eeprom.opcode = 0;
101       spi_eeprom.state = GET_OPCODE;
102     }
103     else
104     {
105       /* !CS low -> process current operation */
106       switch (spi_eeprom.state)
107       {
108         case GET_OPCODE:
109         {
110           /* latch data on CLK positive edge */
111           if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
112           {
113             /* 8-bit opcode buffer */
114             spi_eeprom.opcode |= ((data >> BIT_DATA) & 1);
115             spi_eeprom.cycles++;
116
117             /* last bit ? */
118             if (spi_eeprom.cycles == 8)
119             {
120               /* reset cycles count */
121               spi_eeprom.cycles = 0;
122
123               /* Decode instruction */
124               switch (spi_eeprom.opcode)
125               {
126                 case 0x01:
127                 {
128                   /* WRITE STATUS */
129                   spi_eeprom.buffer = 0;
130                   spi_eeprom.state = WRITE_BYTE;
131                   break;
132                 }
133
134                 case 0x02:
135                 {
136                   /* WRITE BYTE */
137                   spi_eeprom.addr = 0;
138                   spi_eeprom.state = GET_ADDRESS;
139                   break;
140                 }
141
142                 case 0x03:
143                 {
144                   /* READ BYTE */
145                   spi_eeprom.addr = 0;
146                   spi_eeprom.state = GET_ADDRESS;
147                   break;
148                 }
149
150                 case 0x04:
151                 {
152                   /* WRITE DISABLE */
153                   spi_eeprom.status &= ~0x02;
154                   spi_eeprom.state = STANDBY;
155                   break;
156                 }
157
158                 case 0x05:
159                 {
160                   /* READ STATUS */
161                   spi_eeprom.buffer = spi_eeprom.status;
162                   spi_eeprom.state = READ_BYTE;
163                   break;
164                 }
165
166                 case 0x06:
167                 {
168                   /* WRITE ENABLE */
169                   spi_eeprom.status |= 0x02;
170                   spi_eeprom.state = STANDBY;
171                   break;
172                 }
173
174                 default:
175                 {
176                   /* specific instructions (not supported) */
177                   spi_eeprom.state = STANDBY;
178                   break;
179                 }
180               }
181             }
182             else
183             {
184               /* shift opcode value */
185               spi_eeprom.opcode = spi_eeprom.opcode << 1;
186             }
187           }
188           break;
189         }
190
191         case GET_ADDRESS:
192         {
193           /* latch data on CLK positive edge */
194           if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
195           {
196             /* 16-bit address */
197             spi_eeprom.addr |= ((data >> BIT_DATA) & 1);
198             spi_eeprom.cycles++;
199
200             /* last bit ? */
201             if (spi_eeprom.cycles == 16)
202             {
203               /* reset cycles count */
204               spi_eeprom.cycles = 0;
205
206               /* mask unused address bits */
207               spi_eeprom.addr &= SIZE_MASK;
208
209               /* operation type */
210               if (spi_eeprom.opcode & 0x01)
211               {
212                 /* READ operation */
213                 spi_eeprom.buffer = SRam.data[spi_eeprom.addr];
214                 spi_eeprom.state = READ_BYTE;
215               }
216               else
217               {
218                 /* WRITE operation */
219                 spi_eeprom.buffer = 0;
220                 spi_eeprom.state = WRITE_BYTE;
221               }
222             }
223             else
224             {
225               /* shift address value */
226               spi_eeprom.addr = spi_eeprom.addr << 1;
227             }
228           }
229           break;
230         }
231
232         case WRITE_BYTE:
233         {
234           /* latch data on CLK positive edge */
235           if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
236           {
237             /* 8-bit data buffer */
238             spi_eeprom.buffer |= ((data >> BIT_DATA) & 1);
239             spi_eeprom.cycles++;
240
241             /* last bit ? */
242             if (spi_eeprom.cycles == 8)
243             {
244               /* reset cycles count */
245               spi_eeprom.cycles = 0;
246
247               /* write data to destination */
248               if (spi_eeprom.opcode & 0x01)
249               {
250                 /* update status register */
251                 spi_eeprom.status = (spi_eeprom.status & 0x02) | (spi_eeprom.buffer & 0x0c);
252
253                 /* wait for operation end */
254                 spi_eeprom.state = STANDBY;
255               }
256               else
257               {
258                 /* Memory Array (write-protected) */
259                 if (spi_eeprom.status & 2)
260                 {
261                   /* check array protection bits (BP0, BP1) */
262                   switch ((spi_eeprom.status >> 2) & 0x03)
263                   {
264                     case 0x01:
265                     {
266                       /* $C000-$FFFF (sector #3) is protected */
267                       if (spi_eeprom.addr < 0xC000)
268                       {
269                         SRam.data[spi_eeprom.addr] = spi_eeprom.buffer;
270                       }
271                       break;
272                     }
273
274                     case 0x02:
275                     {
276                       /* $8000-$FFFF (sectors #2 and #3) is protected */
277                       if (spi_eeprom.addr < 0x8000)
278                       {
279                         SRam.data[spi_eeprom.addr] = spi_eeprom.buffer;
280                       }
281                       break;
282                     }
283
284                     case 0x03:
285                     {
286                       /* $0000-$FFFF (all sectors) is protected */
287                       break;
288                     }
289
290                     default:
291                     {
292                       /* no sectors protected */
293                       SRam.data[spi_eeprom.addr] = spi_eeprom.buffer;
294                       break;
295                     }
296                   }
297                 }
298
299                 /* reset data buffer */
300                 spi_eeprom.buffer = 0;
301
302                 /* increase array address (sequential writes are limited within the same page) */
303                 spi_eeprom.addr = (spi_eeprom.addr & ~PAGE_MASK) | ((spi_eeprom.addr + 1) & PAGE_MASK);
304               }
305             }
306             else
307             {
308               /* shift data buffer value */
309               spi_eeprom.buffer = spi_eeprom.buffer << 1;
310             }
311           }
312           break;
313         }
314
315         case READ_BYTE:
316         {
317           /* output data on CLK positive edge */
318           if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk)
319           {
320             /* read out bits */
321             spi_eeprom.out = (spi_eeprom.buffer >> (7 - spi_eeprom.cycles)) & 1;
322             spi_eeprom.cycles++;
323
324             /* last bit ? */
325             if (spi_eeprom.cycles == 8)
326             {
327               /* reset cycles count */
328               spi_eeprom.cycles = 0;
329
330               /* read from memory array ? */
331               if (spi_eeprom.opcode == 0x03)
332               {
333                 /* read next array byte */
334                 spi_eeprom.addr = (spi_eeprom.addr + 1) & SIZE_MASK;
335                 spi_eeprom.buffer = SRam.data[spi_eeprom.addr];
336               }
337             }
338           }
339           break;
340         }
341
342         default:
343         {
344           /* wait for !CS low->high transition */
345           break;
346         }
347       }
348     }
349   }
350
351   /* update input lines */
352   spi_eeprom.cs  = (data >> BIT_CS) & 1;
353   spi_eeprom.clk = (data >> BIT_CLK) & 1;
354 }
355
356 unsigned int eeprom_spi_read(unsigned int address)
357 {
358   return (spi_eeprom.out << BIT_DATA);
359 }
360