8c2137f1 |
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 "shared.h" |
40 | |
41 | /* max supported size 64KB (25x512/95x512) */ |
42 | #define SIZE_MASK 0xffff |
43 | #define PAGE_MASK 0x7f |
44 | |
45 | /* 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) |
50 | |
51 | typedef enum |
52 | { |
53 | STANDBY, |
54 | GET_OPCODE, |
55 | GET_ADDRESS, |
56 | WRITE_BYTE, |
57 | READ_BYTE |
58 | } T_STATE_SPI; |
59 | |
60 | typedef struct |
61 | { |
62 | uint8 cs; /* !CS line state */ |
63 | uint8 clk; /* SCLK line state */ |
64 | uint8 out; /* SO line state */ |
65 | uint8 status; /* status register */ |
66 | uint8 opcode; /* 8-bit opcode */ |
67 | uint8 buffer; /* 8-bit data buffer */ |
68 | uint16 addr; /* 16-bit address */ |
69 | uint32 cycles; /* current operation cycle */ |
70 | T_STATE_SPI state; /* current operation state */ |
71 | } T_EEPROM_SPI; |
72 | |
73 | static T_EEPROM_SPI spi_eeprom; |
74 | |
75 | void eeprom_spi_init() |
76 | { |
77 | /* reset eeprom state */ |
78 | memset(&spi_eeprom, 0, sizeof(T_EEPROM_SPI)); |
79 | spi_eeprom.out = 1; |
80 | spi_eeprom.state = GET_OPCODE; |
81 | |
82 | /* enable backup RAM */ |
83 | sram.custom = 2; |
84 | sram.on = 1; |
85 | } |
86 | |
87 | void eeprom_spi_write(unsigned char data) |
88 | { |
89 | /* Make sure !HOLD is high */ |
90 | if (data & (1 << BIT_HOLD)) |
91 | { |
92 | /* Check !CS state */ |
93 | if (data & (1 << BIT_CS)) |
94 | { |
95 | /* !CS high -> end of current operation */ |
96 | spi_eeprom.cycles = 0; |
97 | spi_eeprom.out = 1; |
98 | spi_eeprom.opcode = 0; |
99 | spi_eeprom.state = GET_OPCODE; |
100 | } |
101 | else |
102 | { |
103 | /* !CS low -> process current operation */ |
104 | switch (spi_eeprom.state) |
105 | { |
106 | case GET_OPCODE: |
107 | { |
108 | /* latch data on CLK positive edge */ |
109 | if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk) |
110 | { |
111 | /* 8-bit opcode buffer */ |
112 | spi_eeprom.opcode |= ((data >> BIT_DATA) & 1); |
113 | spi_eeprom.cycles++; |
114 | |
115 | /* last bit ? */ |
116 | if (spi_eeprom.cycles == 8) |
117 | { |
118 | /* reset cycles count */ |
119 | spi_eeprom.cycles = 0; |
120 | |
121 | /* Decode instruction */ |
122 | switch (spi_eeprom.opcode) |
123 | { |
124 | case 0x01: |
125 | { |
126 | /* WRITE STATUS */ |
127 | spi_eeprom.buffer = 0; |
128 | spi_eeprom.state = WRITE_BYTE; |
129 | break; |
130 | } |
131 | |
132 | case 0x02: |
133 | { |
134 | /* WRITE BYTE */ |
135 | spi_eeprom.addr = 0; |
136 | spi_eeprom.state = GET_ADDRESS; |
137 | break; |
138 | } |
139 | |
140 | case 0x03: |
141 | { |
142 | /* READ BYTE */ |
143 | spi_eeprom.addr = 0; |
144 | spi_eeprom.state = GET_ADDRESS; |
145 | break; |
146 | } |
147 | |
148 | case 0x04: |
149 | { |
150 | /* WRITE DISABLE */ |
151 | spi_eeprom.status &= ~0x02; |
152 | spi_eeprom.state = STANDBY; |
153 | break; |
154 | } |
155 | |
156 | case 0x05: |
157 | { |
158 | /* READ STATUS */ |
159 | spi_eeprom.buffer = spi_eeprom.status; |
160 | spi_eeprom.state = READ_BYTE; |
161 | break; |
162 | } |
163 | |
164 | case 0x06: |
165 | { |
166 | /* WRITE ENABLE */ |
167 | spi_eeprom.status |= 0x02; |
168 | spi_eeprom.state = STANDBY; |
169 | break; |
170 | } |
171 | |
172 | default: |
173 | { |
174 | /* specific instructions (not supported) */ |
175 | spi_eeprom.state = STANDBY; |
176 | break; |
177 | } |
178 | } |
179 | } |
180 | else |
181 | { |
182 | /* shift opcode value */ |
183 | spi_eeprom.opcode = spi_eeprom.opcode << 1; |
184 | } |
185 | } |
186 | break; |
187 | } |
188 | |
189 | case GET_ADDRESS: |
190 | { |
191 | /* latch data on CLK positive edge */ |
192 | if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk) |
193 | { |
194 | /* 16-bit address */ |
195 | spi_eeprom.addr |= ((data >> BIT_DATA) & 1); |
196 | spi_eeprom.cycles++; |
197 | |
198 | /* last bit ? */ |
199 | if (spi_eeprom.cycles == 16) |
200 | { |
201 | /* reset cycles count */ |
202 | spi_eeprom.cycles = 0; |
203 | |
204 | /* mask unused address bits */ |
205 | spi_eeprom.addr &= SIZE_MASK; |
206 | |
207 | /* operation type */ |
208 | if (spi_eeprom.opcode & 0x01) |
209 | { |
210 | /* READ operation */ |
211 | spi_eeprom.buffer = sram.sram[spi_eeprom.addr]; |
212 | spi_eeprom.state = READ_BYTE; |
213 | } |
214 | else |
215 | { |
216 | /* WRITE operation */ |
217 | spi_eeprom.buffer = 0; |
218 | spi_eeprom.state = WRITE_BYTE; |
219 | } |
220 | } |
221 | else |
222 | { |
223 | /* shift address value */ |
224 | spi_eeprom.addr = spi_eeprom.addr << 1; |
225 | } |
226 | } |
227 | break; |
228 | } |
229 | |
230 | case WRITE_BYTE: |
231 | { |
232 | /* latch data on CLK positive edge */ |
233 | if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk) |
234 | { |
235 | /* 8-bit data buffer */ |
236 | spi_eeprom.buffer |= ((data >> BIT_DATA) & 1); |
237 | spi_eeprom.cycles++; |
238 | |
239 | /* last bit ? */ |
240 | if (spi_eeprom.cycles == 8) |
241 | { |
242 | /* reset cycles count */ |
243 | spi_eeprom.cycles = 0; |
244 | |
245 | /* write data to destination */ |
246 | if (spi_eeprom.opcode & 0x01) |
247 | { |
248 | /* update status register */ |
249 | spi_eeprom.status = (spi_eeprom.status & 0x02) | (spi_eeprom.buffer & 0x0c); |
250 | |
251 | /* wait for operation end */ |
252 | spi_eeprom.state = STANDBY; |
253 | } |
254 | else |
255 | { |
256 | /* Memory Array (write-protected) */ |
257 | if (spi_eeprom.status & 2) |
258 | { |
259 | /* check array protection bits (BP0, BP1) */ |
260 | switch ((spi_eeprom.status >> 2) & 0x03) |
261 | { |
262 | case 0x01: |
263 | { |
264 | /* $C000-$FFFF (sector #3) is protected */ |
265 | if (spi_eeprom.addr < 0xC000) |
266 | { |
267 | sram.sram[spi_eeprom.addr] = spi_eeprom.buffer; |
268 | } |
269 | break; |
270 | } |
271 | |
272 | case 0x02: |
273 | { |
274 | /* $8000-$FFFF (sectors #2 and #3) is protected */ |
275 | if (spi_eeprom.addr < 0x8000) |
276 | { |
277 | sram.sram[spi_eeprom.addr] = spi_eeprom.buffer; |
278 | } |
279 | break; |
280 | } |
281 | |
282 | case 0x03: |
283 | { |
284 | /* $0000-$FFFF (all sectors) is protected */ |
285 | break; |
286 | } |
287 | |
288 | default: |
289 | { |
290 | /* no sectors protected */ |
291 | sram.sram[spi_eeprom.addr] = spi_eeprom.buffer; |
292 | break; |
293 | } |
294 | } |
295 | } |
296 | |
297 | /* reset data buffer */ |
298 | spi_eeprom.buffer = 0; |
299 | |
300 | /* increase array address (sequential writes are limited within the same page) */ |
301 | spi_eeprom.addr = (spi_eeprom.addr & ~PAGE_MASK) | ((spi_eeprom.addr + 1) & PAGE_MASK); |
302 | } |
303 | } |
304 | else |
305 | { |
306 | /* shift data buffer value */ |
307 | spi_eeprom.buffer = spi_eeprom.buffer << 1; |
308 | } |
309 | } |
310 | break; |
311 | } |
312 | |
313 | case READ_BYTE: |
314 | { |
315 | /* output data on CLK positive edge */ |
316 | if ((data & (1 << BIT_CLK)) && !spi_eeprom.clk) |
317 | { |
318 | /* read out bits */ |
319 | spi_eeprom.out = (spi_eeprom.buffer >> (7 - spi_eeprom.cycles)) & 1; |
320 | spi_eeprom.cycles++; |
321 | |
322 | /* last bit ? */ |
323 | if (spi_eeprom.cycles == 8) |
324 | { |
325 | /* reset cycles count */ |
326 | spi_eeprom.cycles = 0; |
327 | |
328 | /* read from memory array ? */ |
329 | if (spi_eeprom.opcode == 0x03) |
330 | { |
331 | /* read next array byte */ |
332 | spi_eeprom.addr = (spi_eeprom.addr + 1) & SIZE_MASK; |
333 | spi_eeprom.buffer = sram.sram[spi_eeprom.addr]; |
334 | } |
335 | } |
336 | } |
337 | break; |
338 | } |
339 | |
340 | default: |
341 | { |
342 | /* wait for !CS low->high transition */ |
343 | break; |
344 | } |
345 | } |
346 | } |
347 | } |
348 | |
349 | /* update input lines */ |
350 | spi_eeprom.cs = (data >> BIT_CS) & 1; |
351 | spi_eeprom.clk = (data >> BIT_CLK) & 1; |
352 | } |
353 | |
354 | unsigned int eeprom_spi_read(unsigned int address) |
355 | { |
356 | return (spi_eeprom.out << BIT_DATA); |
357 | } |
358 | |