f950dd1b65ee8081f270e1162bf23f2db11cddfb
[fw/altos] / lib / ccdbg-flash.c
1 /*
2  * Copyright © 2008 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; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ccdbg.h"
20
21 /* From SWRA124 section 3.1.6 */
22
23 static uint8_t flash_page[] = {
24
25         MOV_direct_data, P1DIR, 0x02,
26         MOV_direct_data, P1,    0xFF,
27
28         MOV_direct_data, FADDRH, 0,
29 #define FLASH_ADDR_HIGH 8
30
31         MOV_direct_data, FADDRL, 0,
32 #define FLASH_ADDR_LOW  11
33
34         MOV_DPTR_data16, 0, 0,
35 #define RAM_ADDR_HIGH   13
36 #define RAM_ADDR_LOW    14
37
38         MOV_Rn_data(7), 0,
39 #define FLASH_WORDS_HIGH        16
40         
41         MOV_Rn_data(6), 0,
42 #define FLASH_WORDS_LOW         18
43         
44         MOV_direct_data, FWT, 0x20,
45 #define FLASH_TIMING            21
46
47         MOV_direct_data, FCTL, FCTL_ERASE,
48 /* eraseWaitLoop: */
49                 MOV_A_direct,           FCTL,
50         JB, ACC(FCTL_BUSY_BIT), 0xfb,
51
52         MOV_direct_data, P1, 0xfd,
53
54         MOV_direct_data, FCTL, FCTL_WRITE,
55 /* writeLoop: */
56                 MOV_Rn_data(5), 2,
57 /* writeWordLoop: */
58                         MOVX_A_atDPTR,
59                         INC_DPTR,
60                         MOV_direct_A, FWDATA,
61                 DJNZ_Rn_rel(5), 0xfa,           /* writeWordLoop */
62 /* writeWaitLoop: */
63                         MOV_A_direct, FCTL,
64                 JB, ACC(FCTL_SWBSY_BIT), 0xfb,          /* writeWaitLoop */
65         DJNZ_Rn_rel(6), 0xf1,                   /* writeLoop */
66         DJNZ_Rn_rel(7), 0xef,                   /* writeLoop */
67
68         MOV_direct_data, P1DIR, 0x00,
69         MOV_direct_data, P1,    0xFF,
70         TRAP,
71 };
72
73 #define FLASH_RAM       0xf000
74
75 #if 0
76 static uint8_t  flash_erase_page[] = {
77         3,      MOV_direct_data, FADDRH, 0,
78 #define ERASE_PAGE_HIGH 3
79         
80         3,      MOV_direct_data, FADDRL, 0,
81 #define ERASE_PAGE_LOW  7
82
83         3,      MOV_direct_data, FWT, 0x2A,
84         3,      MOV_direct_data, FCTL, FCTL_ERASE,
85         0
86 };
87
88 static uint8_t  flash_read_control[] = {
89         2,      MOV_A_direct,   FCTL,
90         0
91 };
92 #endif
93
94 #if 0
95 static uint8_t  flash_control_clear[] = {
96         3,      MOV_direct_data,        FCTL, 0,
97         2,      MOV_A_direct,           FCTL,
98         0
99 };
100 #endif
101
102 #if 0
103 static uint8_t
104 ccdbg_flash_erase_page(struct ccdbg *dbg, uint16_t addr)
105 {
106         uint16_t        page_addr = addr >> 1;
107         uint8_t         status;
108         uint8_t         old[0x10], new[0x10];
109         int             i;
110         
111         ccdbg_read_memory(dbg, addr, old, 0x10);
112         flash_erase_page[ERASE_PAGE_HIGH] = page_addr >> 8;
113         flash_erase_page[ERASE_PAGE_LOW] = page_addr & 0xff;
114         status = ccdbg_execute(dbg, flash_erase_page);
115         ccdbg_debug(CC_DEBUG_FLASH, "erase status 0x%02x\n", status);
116         do {
117                 status = ccdbg_execute(dbg, flash_read_control);
118                 ccdbg_debug(CC_DEBUG_FLASH, "fctl 0x%02x\n", status);
119         } while (status & FCTL_BUSY);
120         ccdbg_read_memory(dbg, addr, new, 0x10);
121         for (i = 0; i < 0x10; i++)
122                 ccdbg_debug(CC_DEBUG_FLASH, "0x%02x -> 0x%02x\n", old[i], new[i]);
123         status = ccdbg_execute(dbg, flash_control_clear);
124         ccdbg_debug(CC_DEBUG_FLASH, "clear fctl 0x%02x\n", status);
125         return 0;
126 }
127 #endif
128
129 #if 0
130 static uint8_t flash_write[] = {
131         MOV_direct_data, P1DIR, 0x02,
132         MOV_direct_data, P1,    0xFD,
133         
134         MOV_A_direct, FCTL,
135         JB,     ACC(FCTL_BUSY_BIT), 0xf1,
136
137         MOV_direct_data, FCTL, 0x20,
138
139         MOV_direct_data, FADDRH, 0,
140 #define WRITE_PAGE_HIGH 16
141         
142         MOV_direct_data, FADDRL, 0,
143 #define WRITE_PAGE_LOW  19
144         
145         MOV_direct_data, FCTL, FCTL_WRITE,
146         MOV_direct_data, FWDATA, 0,
147 #define WRITE_BYTE_0    25
148         MOV_direct_data, FWDATA, 0,
149 #define WRITE_BYTE_1    28
150         MOV_A_direct, FCTL,
151         JB,     ACC(FCTL_SWBSY_BIT), 0xf1,
152
153         MOV_direct_data, P1,    0xFF,
154         TRAP,
155 };
156 #endif
157
158 static uint8_t
159 ccdbg_clock_init(struct ccdbg *dbg)
160 {
161         static uint8_t set_clkcon_fast[] = {
162                 3,      MOV_direct_data,        CLKCON, 0x00,
163                 0
164         };
165
166         static uint8_t get_sleep[] = {
167                 2,      MOV_A_direct, SLEEP,
168                 0
169         };
170
171         uint8_t status;
172
173         ccdbg_execute(dbg, set_clkcon_fast);
174         do {
175                 status = ccdbg_execute(dbg, get_sleep);
176         } while (!(status & 0x40));
177         return 0;
178 }
179
180 #if 0
181 static uint8_t
182 ccdbg_flash_write_word(struct ccdbg *dbg, uint16_t addr, uint8_t data[2])
183 {
184         uint16_t page_addr = addr >> 1;
185         uint8_t check[2];
186         uint8_t status;
187         int i;
188
189         flash_write[WRITE_PAGE_HIGH] = page_addr >> 8;
190         flash_write[WRITE_PAGE_LOW] = page_addr & 0xff;
191         flash_write[WRITE_BYTE_0] = data[0];
192         flash_write[WRITE_BYTE_1] = data[1];
193         ccdbg_debug(CC_DEBUG_FLASH, "upload flash write\n");
194         ccdbg_write_memory(dbg, 0xf000, flash_write, sizeof(flash_write));
195         ccdbg_set_pc(dbg, 0xf000);
196         ccdbg_resume(dbg);
197         for (;;) {
198                 status = ccdbg_read_status(dbg);
199                 ccdbg_debug(CC_DEBUG_FLASH, "waiting for write 0x%02x\n", status);
200                 if ((status & CC_STATUS_CPU_HALTED) != 0)
201                         break;
202                 sleep (1);
203         }
204         status = ccdbg_execute(dbg, flash_control_clear);
205         ccdbg_debug(CC_DEBUG_FLASH, "clear fctl 0x%02x\n", status);
206         ccdbg_read_memory(dbg, addr, check, 2);
207         for (i = 0; i < 2; i++)
208                 ccdbg_debug(CC_DEBUG_FLASH, "0x%02x : 0x%02x\n", data[i], check[i]);
209         return 0;
210 }
211 #endif
212
213 #define TIMERS_OFF              0x08
214 #define DMA_PAUSE               0x04
215 #define TIMER_SUSPEND           0x02
216 #define SEL_FLASH_INFO_PAGE     0x01
217
218 #if 0
219 static uint8_t
220 ccdbg_flash_lock(struct ccdbg *dbg, uint8_t lock)
221 {
222         uint8_t config;
223         uint8_t bytes[2];
224         uint8_t old[1], new[1];
225
226         config = ccdbg_rd_config(dbg);
227         ccdbg_wr_config(dbg, config|SEL_FLASH_INFO_PAGE);
228         bytes[0] = lock;
229         bytes[1] = 0;
230         ccdbg_flash_erase_page(dbg, 0);
231         ccdbg_read_memory(dbg, 0, old, 1);
232         ccdbg_flash_write_word(dbg, 0, bytes);
233         ccdbg_read_memory(dbg, 0, new, 1);
234         ccdbg_debug(CC_DEBUG_FLASH, "flash lock 0x%02x -> 0x%02x\n", old[0], new[0]);
235         ccdbg_wr_config(dbg, config & ~SEL_FLASH_INFO_PAGE);
236         return 0;
237 }
238 #endif
239
240 uint8_t
241 ccdbg_flash_hex_image(struct ccdbg *dbg, struct hex_image *image)
242 {
243         uint16_t offset;
244         uint16_t flash_prog;
245         uint16_t flash_len;
246         uint8_t fwt;
247         uint16_t flash_word_addr;
248         uint16_t flash_words;
249         uint16_t ram_addr;
250         uint16_t pc;
251         uint8_t status;
252         uint16_t remain, this_time, start;
253         uint8_t verify[0x400];
254
255         ccdbg_clock_init(dbg);
256         if (image->address + image->length > 0x8000) {
257                 fprintf(stderr, "cannot flash image from 0x%04x to 0x%04x\n",
258                         image->address, image->address + image->length);
259                 return 1;
260         }
261         if (image->address & 0x3ff) {
262                 fprintf(stderr, "flash image must start on page boundary\n");
263                 return 1;
264         }
265         ram_addr = 0xf000;
266
267         
268         flash_prog = 0xf400;
269
270         fwt = 0x20;
271
272         flash_page[FLASH_TIMING] = fwt;
273         ccdbg_debug(CC_DEBUG_FLASH, "Upload %d flash program bytes to 0x%04x\n",
274                sizeof (flash_page), flash_prog);
275         ccdbg_write_memory(dbg, flash_prog, flash_page, sizeof(flash_page));
276         
277         remain = image->length;
278         start = 0;
279         while (remain) {
280                 this_time = remain;
281                 if (this_time > 0x400)
282                         this_time = 0x400;
283
284                 offset = ram_addr - (image->address + start);
285
286                 ccdbg_debug(CC_DEBUG_FLASH, "Upload %d bytes at 0x%04x\n", this_time, ram_addr);
287                 ccdbg_write_memory(dbg, ram_addr, image->data + start, this_time);
288
289                 ccdbg_debug(CC_DEBUG_FLASH, "Verify %d bytes\n", image->length);
290                 ccdbg_read_memory(dbg, ram_addr, verify, this_time);
291                 if (memcmp (image->data + start, verify, this_time) != 0) {
292                         fprintf(stderr, "image verify failed\n");
293                         return 1;
294                 }
295                 
296                 flash_word_addr = (image->address + start) >> 1;
297                 flash_len = this_time + (this_time & 1);
298                 flash_words = flash_len >> 1;
299
300                 ccdbg_write_uint8(dbg, flash_prog + FLASH_ADDR_HIGH, flash_word_addr >> 8);
301                 ccdbg_write_uint8(dbg, flash_prog + FLASH_ADDR_LOW, flash_word_addr & 0xff);
302
303                 ccdbg_write_uint8(dbg, flash_prog + RAM_ADDR_HIGH, ram_addr >> 8);
304                 ccdbg_write_uint8(dbg, flash_prog + RAM_ADDR_LOW, ram_addr & 0xff);
305
306                 ccdbg_write_uint8(dbg, flash_prog + FLASH_WORDS_HIGH, flash_words >> 8);
307                 ccdbg_write_uint8(dbg, flash_prog + FLASH_WORDS_LOW, flash_words & 0xff);
308
309                 ccdbg_set_pc(dbg, flash_prog);
310                 pc = ccdbg_get_pc(dbg);
311                 ccdbg_debug(CC_DEBUG_FLASH, "Starting flash program at 0x%04x\n", pc);
312                 status = ccdbg_resume(dbg);
313                 ccdbg_debug(CC_DEBUG_FLASH, "resume status is 0x%02x\n", status);
314                 do {
315                         status = ccdbg_read_status(dbg);
316                         ccdbg_debug(CC_DEBUG_FLASH, "chip status is 0x%02x\n", status);
317                         sleep(1);
318                 } while ((status & CC_STATUS_CPU_HALTED) == 0);
319                 
320                 remain -= this_time;
321                 start += this_time;
322         }
323 #if 1
324         ccdbg_debug(CC_DEBUG_FLASH, "Downloading flash to check\n");
325         struct hex_image *test_image;
326         test_image = ccdbg_read_hex_image(dbg, image->address, image->length);
327         if (!ccdbg_hex_image_equal(image, test_image)) {
328                 int i;
329                 fprintf(stderr, "Image not loaded\n");
330                 for (i = 0;i < 0x10; i++)
331                         ccdbg_debug(CC_DEBUG_FLASH, "0x%02x : 0x%02x\n", image->data[i], test_image->data[i]);
332                 return 1;
333         }
334         return 0;
335 #endif
336         return 0;
337 }