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 | |
6a47c2d4 |
39 | #include "../pico_int.h" |
40 | #include "../cd/genplus_macros.h" |
41 | #include "eeprom_spi.h" |
8c2137f1 |
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 | |
6a47c2d4 |
77 | void *eeprom_spi_init(int *size) |
8c2137f1 |
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 | |
6a47c2d4 |
84 | if (size) |
85 | *size = sizeof(T_EEPROM_SPI); |
86 | return &spi_eeprom; |
8c2137f1 |
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 */ |
88fd63ad |
213 | spi_eeprom.buffer = Pico.sv.data[spi_eeprom.addr]; |
8c2137f1 |
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 | { |
88fd63ad |
269 | Pico.sv.data[spi_eeprom.addr] = spi_eeprom.buffer; |
8c2137f1 |
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 | { |
88fd63ad |
279 | Pico.sv.data[spi_eeprom.addr] = spi_eeprom.buffer; |
8c2137f1 |
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 */ |
88fd63ad |
293 | Pico.sv.data[spi_eeprom.addr] = spi_eeprom.buffer; |
8c2137f1 |
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; |
88fd63ad |
335 | spi_eeprom.buffer = Pico.sv.data[spi_eeprom.addr]; |
8c2137f1 |
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 | |