altos: Move common storage code to ao_storage.c. Add M25P80 driver
[fw/altos] / src / ao_ee.c
1 /*
2  * Copyright © 2009 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; version 2 of the License.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License along
14  * with this program; if not, write to the Free Software Foundation, Inc.,
15  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
16  */
17
18 #include "ao.h"
19 #include "25lc1024.h"
20
21 #define EE_BLOCK_SIZE   ((uint16_t) (256))
22 #define EE_DEVICE_SIZE  ((uint32_t) 128 * (uint32_t) 1024)
23
24 /* Total bytes of available storage */
25 __xdata uint32_t        ao_storage_total;
26
27 /* Block size - device is erased in these units. At least 256 bytes */
28 __xdata uint32_t        ao_storage_block;
29
30 /* Byte offset of config block. Will be ao_storage_block bytes long */
31 __xdata uint32_t        ao_storage_config;
32
33 /* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
34 __xdata uint16_t        ao_storage_unit;
35
36 /*
37  * Using SPI on USART 0, with P1_2 as the chip select
38  */
39
40 #define EE_CS           P1_2
41 #define EE_CS_INDEX     2
42
43 static __xdata uint8_t ao_ee_mutex;
44
45 #define ao_ee_delay() do { \
46         _asm nop _endasm; \
47         _asm nop _endasm; \
48         _asm nop _endasm; \
49 } while(0)
50
51 static void ao_ee_cs_low(void)
52 {
53         ao_ee_delay();
54         EE_CS = 0;
55         ao_ee_delay();
56 }
57
58 static void ao_ee_cs_high(void)
59 {
60         ao_ee_delay();
61         EE_CS = 1;
62         ao_ee_delay();
63 }
64
65 struct ao_ee_instruction {
66         uint8_t instruction;
67         uint8_t address[3];
68 } __xdata ao_ee_instruction;
69
70 static void
71 ao_ee_write_enable(void)
72 {
73         ao_ee_cs_low();
74         ao_ee_instruction.instruction = EE_WREN;
75         ao_spi_send(&ao_ee_instruction, 1);
76         ao_ee_cs_high();
77 }
78
79 static uint8_t
80 ao_ee_rdsr(void)
81 {
82         ao_ee_cs_low();
83         ao_ee_instruction.instruction = EE_RDSR;
84         ao_spi_send(&ao_ee_instruction, 1);
85         ao_spi_recv(&ao_ee_instruction, 1);
86         ao_ee_cs_high();
87         return ao_ee_instruction.instruction;
88 }
89
90 static void
91 ao_ee_wrsr(uint8_t status)
92 {
93         ao_ee_cs_low();
94         ao_ee_instruction.instruction = EE_WRSR;
95         ao_ee_instruction.address[0] = status;
96         ao_spi_send(&ao_ee_instruction, 2);
97         ao_ee_cs_high();
98 }
99
100 #define EE_BLOCK_NONE   0xffff
101
102 static __xdata uint8_t ao_ee_data[EE_BLOCK_SIZE];
103 static __pdata uint16_t ao_ee_block = EE_BLOCK_NONE;
104 static __pdata uint8_t  ao_ee_block_dirty;
105
106 /* Write the current block to the EEPROM */
107 static void
108 ao_ee_write_block(void)
109 {
110         uint8_t status;
111
112         status = ao_ee_rdsr();
113         if (status & (EE_STATUS_BP0|EE_STATUS_BP1|EE_STATUS_WPEN)) {
114                 status &= ~(EE_STATUS_BP0|EE_STATUS_BP1|EE_STATUS_WPEN);
115                 ao_ee_wrsr(status);
116         }
117         ao_ee_write_enable();
118         ao_ee_cs_low();
119         ao_ee_instruction.instruction = EE_WRITE;
120         ao_ee_instruction.address[0] = ao_ee_block >> 8;
121         ao_ee_instruction.address[1] = ao_ee_block;
122         ao_ee_instruction.address[2] = 0;
123         ao_spi_send(&ao_ee_instruction, 4);
124         ao_spi_send(ao_ee_data, EE_BLOCK_SIZE);
125         ao_ee_cs_high();
126         for (;;) {
127                 uint8_t status = ao_ee_rdsr();
128                 if ((status & EE_STATUS_WIP) == 0)
129                         break;
130         }
131 }
132
133 /* Read the current block from the EEPROM */
134 static void
135 ao_ee_read_block(void)
136 {
137         ao_ee_cs_low();
138         ao_ee_instruction.instruction = EE_READ;
139         ao_ee_instruction.address[0] = ao_ee_block >> 8;
140         ao_ee_instruction.address[1] = ao_ee_block;
141         ao_ee_instruction.address[2] = 0;
142         ao_spi_send(&ao_ee_instruction, 4);
143         ao_spi_recv(ao_ee_data, EE_BLOCK_SIZE);
144         ao_ee_cs_high();
145 }
146
147 static void
148 ao_ee_flush_internal(void)
149 {
150         if (ao_ee_block_dirty) {
151                 ao_ee_write_block();
152                 ao_ee_block_dirty = 0;
153         }
154 }
155
156 static void
157 ao_ee_fill(uint16_t block)
158 {
159         if (block != ao_ee_block) {
160                 ao_ee_flush_internal();
161                 ao_ee_block = block;
162                 ao_ee_read_block();
163         }
164 }
165
166 uint8_t
167 ao_storage_device_write(uint32_t pos, __xdata void *buf, uint16_t len) __reentrant
168 {
169         uint16_t block = (uint16_t) (pos >> 8);
170
171         /* Transfer the data */
172         ao_mutex_get(&ao_ee_mutex); {
173                 if (len != EE_BLOCK_SIZE)
174                         ao_ee_fill(block);
175                 else {
176                         ao_ee_flush_internal();
177                         ao_ee_block = block;
178                 }
179                 memcpy(ao_ee_data + (uint16_t) (pos & 0xff), buf, len);
180                 ao_ee_block_dirty = 1;
181         } ao_mutex_put(&ao_ee_mutex);
182         return 1;
183 }
184
185 uint8_t
186 ao_storage_device_read(uint32_t pos, __xdata void *buf, uint16_t len) __reentrant
187 {
188         uint16_t block = (uint16_t) (pos >> 8);
189
190         /* Transfer the data */
191         ao_mutex_get(&ao_ee_mutex); {
192                 ao_ee_fill(block);
193                 memcpy(buf, ao_ee_data + (uint16_t) (pos & 0xff), len);
194         } ao_mutex_put(&ao_ee_mutex);
195         return 1;
196 }
197
198 void
199 ao_storage_flush(void) __reentrant
200 {
201         ao_mutex_get(&ao_ee_mutex); {
202                 ao_ee_flush_internal();
203         } ao_mutex_put(&ao_ee_mutex);
204 }
205
206 uint8_t
207 ao_storage_erase(uint32_t pos) __reentrant
208 {
209         ao_mutex_get(&ao_ee_mutex); {
210                 uint16_t block = (uint16_t) (pos >> 8);
211                 ao_ee_fill(block);
212                 memset(ao_ee_data, 0xff, EE_BLOCK_SIZE);
213                 ao_ee_block_dirty = 1;
214         } ao_mutex_put(&ao_ee_mutex);
215         return 1;
216 }
217
218 static void
219 ee_store(void) __reentrant
220 {
221 }
222
223 void
224 ao_storage_setup(void)
225 {
226         if (ao_storage_total == 0) {
227                 ao_storage_total = EE_DEVICE_SIZE;
228                 ao_storage_block = EE_BLOCK_SIZE;
229                 ao_storage_config = EE_DEVICE_SIZE - EE_BLOCK_SIZE;
230                 ao_storage_unit = EE_BLOCK_SIZE;
231         }
232 }
233
234 void
235 ao_storage_device_info(void) __reentrant
236 {
237 }
238
239 /*
240  * To initialize the chip, set up the CS line and
241  * the SPI interface
242  */
243 void
244 ao_storage_device_init(void)
245 {
246         /* set up CS */
247         EE_CS = 1;
248         P1DIR |= (1 << EE_CS_INDEX);
249         P1SEL &= ~(1 << EE_CS_INDEX);
250 }