ee4e858915d3712773e62cfe97d1e30128908674
[fw/altos] / 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 static uint8_t  flash_erase_page[] = {
76         3,      MOV_direct_data, FADDRH, 0,
77 #define ERASE_PAGE_HIGH 3
78         
79         3,      MOV_direct_data, FADDRL, 0,
80 #define ERASE_PAGE_LOW  7
81
82         3,      MOV_direct_data, FWT, 0x2A,
83         3,      MOV_direct_data, FCTL, FCTL_ERASE,
84         0
85 };
86
87 static uint8_t  flash_read_control[] = {
88         2,      MOV_A_direct,   FCTL,
89         0
90 };
91
92 static uint8_t  flash_control_clear[] = {
93         3,      MOV_direct_data,        FCTL, 0,
94         2,      MOV_A_direct,           FCTL,
95         0
96 };
97
98 static uint8_t
99 ccdbg_flash_erase_page(struct ccdbg *dbg, uint16_t addr)
100 {
101         uint16_t        page_addr = addr >> 1;
102         uint8_t         status;
103         uint8_t         old[0x10], new[0x10];
104         int             i;
105         
106         ccdbg_read_memory(dbg, addr, old, 0x10);
107         flash_erase_page[ERASE_PAGE_HIGH] = page_addr >> 8;
108         flash_erase_page[ERASE_PAGE_LOW] = page_addr & 0xff;
109         status = ccdbg_execute(dbg, flash_erase_page);
110         printf("erase status 0x%02x\n", status);
111         do {
112                 status = ccdbg_execute(dbg, flash_read_control);
113                 printf("fctl 0x%02x\n", status);
114         } while (status & FCTL_BUSY);
115         ccdbg_read_memory(dbg, addr, new, 0x10);
116         for (i = 0; i < 0x10; i++)
117                 printf("0x%02x -> 0x%02x\n", old[i], new[i]);
118         status = ccdbg_execute(dbg, flash_control_clear);
119         printf("clear fctl 0x%02x\n", status);
120         return 0;
121 }
122
123 static uint8_t flash_write[] = {
124         MOV_direct_data, P1DIR, 0x02,
125         MOV_direct_data, P1,    0xFD,
126         
127         MOV_A_direct, FCTL,
128         JB,     ACC(FCTL_BUSY_BIT), 0xf1,
129
130         MOV_direct_data, FCTL, 0x20,
131
132         MOV_direct_data, FADDRH, 0,
133 #define WRITE_PAGE_HIGH 16
134         
135         MOV_direct_data, FADDRL, 0,
136 #define WRITE_PAGE_LOW  19
137         
138         MOV_direct_data, FCTL, FCTL_WRITE,
139         MOV_direct_data, FWDATA, 0,
140 #define WRITE_BYTE_0    25
141         MOV_direct_data, FWDATA, 0,
142 #define WRITE_BYTE_1    28
143         MOV_A_direct, FCTL,
144         JB,     ACC(FCTL_SWBSY_BIT), 0xf1,
145
146         MOV_direct_data, P1,    0xFF,
147         TRAP,
148 };
149
150 static uint8_t
151 ccdbg_clock_init(struct ccdbg *dbg)
152 {
153         static uint8_t set_clkcon_fast[] = {
154                 3,      MOV_direct_data,        CLKCON, 0x00,
155                 0
156         };
157
158         static uint8_t get_sleep[] = {
159                 2,      MOV_A_direct, SLEEP,
160                 0
161         };
162
163         uint8_t status;
164
165         ccdbg_execute(dbg, set_clkcon_fast);
166         do {
167                 status = ccdbg_execute(dbg, get_sleep);
168         } while (!(status & 0x40));
169         return 0;
170 }
171
172 static uint8_t
173 ccdbg_flash_write_word(struct ccdbg *dbg, uint16_t addr, uint8_t data[2])
174 {
175         uint16_t page_addr = addr >> 1;
176         uint8_t check[2];
177         uint8_t status;
178         int i;
179
180         flash_write[WRITE_PAGE_HIGH] = page_addr >> 8;
181         flash_write[WRITE_PAGE_LOW] = page_addr & 0xff;
182         flash_write[WRITE_BYTE_0] = data[0];
183         flash_write[WRITE_BYTE_1] = data[1];
184         printf("upload flash write\n");
185         ccdbg_write_memory(dbg, 0xf000, flash_write, sizeof(flash_write));
186         ccdbg_set_pc(dbg, 0xf000);
187         ccdbg_resume(dbg);
188         for (;;) {
189                 status = ccdbg_read_status(dbg);
190                 printf("waiting for write 0x%02x\n", status);
191                 if ((status & CC_STATUS_CPU_HALTED) != 0)
192                         break;
193                 sleep (1);
194         }
195         status = ccdbg_execute(dbg, flash_control_clear);
196         printf("clear fctl 0x%02x\n", status);
197         ccdbg_read_memory(dbg, addr, check, 2);
198         for (i = 0; i < 2; i++)
199                 printf("0x%02x : 0x%02x\n", data[i], check[i]);
200         return 0;
201 }
202
203 #define TIMERS_OFF              0x08
204 #define DMA_PAUSE               0x04
205 #define TIMER_SUSPEND           0x02
206 #define SEL_FLASH_INFO_PAGE     0x01
207
208 static uint8_t
209 ccdbg_flash_lock(struct ccdbg *dbg, uint8_t lock)
210 {
211         uint8_t config;
212         uint8_t bytes[2];
213         uint8_t old[1], new[1];
214
215         config = ccdbg_rd_config(dbg);
216         ccdbg_wr_config(dbg, config|SEL_FLASH_INFO_PAGE);
217         bytes[0] = lock;
218         bytes[1] = 0;
219         ccdbg_flash_erase_page(dbg, 0);
220         ccdbg_read_memory(dbg, 0, old, 1);
221         ccdbg_flash_write_word(dbg, 0, bytes);
222         ccdbg_read_memory(dbg, 0, new, 1);
223         printf ("flash lock 0x%02x -> 0x%02x\n", old[0], new[0]);
224         ccdbg_wr_config(dbg, config & ~SEL_FLASH_INFO_PAGE);
225         return 0;
226 }
227
228 uint8_t
229 ccdbg_flash_hex_image(struct ccdbg *dbg, struct hex_image *image)
230 {
231         uint16_t offset;
232         struct hex_image *test_image;
233         uint16_t flash_prog;
234         uint16_t flash_len;
235         uint8_t fwt;
236         uint16_t flash_word_addr;
237         uint16_t flash_words;
238         uint16_t ram_addr;
239         uint16_t pc;
240         uint8_t status;
241
242         ccdbg_clock_init(dbg);
243         if (image->address + image->length > 0x8000) {
244                 fprintf(stderr, "cannot flash image from 0x%04x to 0x%04x\n",
245                         image->address, image->address + image->length);
246                 return 1;
247         }
248         flash_word_addr = image->address >> 1;
249         if (flash_word_addr & 0x1ff) {
250                 fprintf(stderr, "flash image must start on page boundary\n");
251                 return 1;
252         }
253         ram_addr = 0xf000;
254         offset = ram_addr - image->address;
255
256 #if 0
257         printf("Downloading flash to check\n");
258         test_image = ccdbg_read_hex_image(dbg, image->address, image->length);
259         if (!ccdbg_hex_image_equal(image, test_image)) {
260                 int i;
261                 fprintf(stderr, "Image not loaded\n");
262                 for (i = 0;i < 0x10; i++)
263                         printf ("0x%02x : 0x%02x\n", image->data[i], test_image->data[i]);
264                 return 1;
265         }
266         return 0;
267 #endif
268         
269         printf("Upload %d bytes at 0x%04x\n", image->length, ram_addr);
270         ccdbg_write_hex_image(dbg, image, offset);
271         printf("Verify %d bytes\n", image->length);
272         test_image = ccdbg_read_hex_image(dbg, ram_addr, image->length);
273         if (!ccdbg_hex_image_equal(image, test_image)) {
274                 ccdbg_hex_image_free(test_image);
275                 fprintf(stderr, "image verify failed\n");
276                 return 1;
277         }
278         ccdbg_hex_image_free(test_image);
279         flash_len = image->length + (image->length & 1);
280         flash_words = flash_len >> 1;
281         flash_prog = ram_addr + flash_len;
282
283         fwt = 0x20;
284         flash_page[FLASH_ADDR_HIGH] = flash_word_addr >> 8;
285         flash_page[FLASH_ADDR_LOW] = flash_word_addr & 0xff;
286
287         flash_page[RAM_ADDR_HIGH] = ram_addr >> 8;
288         flash_page[RAM_ADDR_LOW] = ram_addr & 0xff;
289
290         flash_page[FLASH_WORDS_HIGH] = flash_words >> 8;
291         flash_page[FLASH_WORDS_LOW] = flash_words & 0xff;
292
293         flash_page[FLASH_TIMING] = fwt;
294         
295         printf("Upload %d flash program bytes to 0x%04x\n",
296                sizeof (flash_prog), flash_prog);
297         ccdbg_write_memory(dbg, flash_prog, flash_page, sizeof(flash_page));
298         ccdbg_set_pc(dbg, flash_prog);
299         pc = ccdbg_get_pc(dbg);
300         printf("Starting flash program at 0x%04x\n", pc);
301         status = ccdbg_resume(dbg);
302         printf("resume status is 0x%02x\n", status);
303         do {
304                 status = ccdbg_read_status(dbg);
305                 printf("chip status is 0x%02x\n", status);
306                 sleep(1);
307         } while ((status & CC_STATUS_CPU_HALTED) == 0);
308         return 0;
309 }