summaryrefslogtreecommitdiff
path: root/src/hal/drivers/mesa-hostmot2/setsserial.c
blob: a53d5653ba563cc0aba0cc5a8667d45b8153a817 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
//
//    Copyright (C) 2011 Andy Pugh
//
//    This program is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
//
//
//    The code in this file is based on UFLBP.PAS by Peter C. Wallace.  

#include <linux/firmware.h>
#include "rtapi.h"
#include "rtapi_app.h"
#include "hal.h"
#include "hostmot2.h"
#include "sserial.h"

static int comp_id;

/* module information */
MODULE_AUTHOR("Andy Pugh");
MODULE_DESCRIPTION("A simple util for nvram setting on smart-serial cards");
MODULE_LICENSE("GPL");

static char *cmd;
RTAPI_MP_STRING(cmd, "smart-serial setting commands");

char **cmd_list;

hostmot2_t *hm2;
hm2_sserial_remote_t *remote;

int waitfor(void){
    u32 buff;
    long long int starttime = rtapi_get_time();
    do {
        rtapi_delay(50000);
        HM2READ(remote->command_reg_addr, buff);
        if (rtapi_get_time() - starttime > 1000000000){
            rtapi_print_msg(RTAPI_MSG_ERR, "Timeout waiting for CMD to clear\n");
            return -1;
        }
    } while (buff);
    
    return 0;
}

int doit(void){
    u32 buff = 0x1000 | (1 << remote->index);
    HM2WRITE(remote->command_reg_addr, buff);
    if (waitfor() < 0) return -1;
    HM2READ(remote->data_reg_addr, buff);
    if (buff & (1 << remote->index)){
        rtapi_print_msg(RTAPI_MSG_ERR, "Error flag set after CMD Clear %08x\n",
                        buff);
        return -1;
    }
    return 0;
}

int stop_all(void){
    u32 buff=0x8FF;
    HM2WRITE(remote->command_reg_addr, buff);
    return waitfor();
}

int setup_start(void){
    u32 buff=0xF00 | 1 << remote->index;
    HM2WRITE(remote->command_reg_addr, buff);
    if (waitfor() < 0) return -1;
    HM2READ(remote->data_reg_addr, buff); 
    rtapi_print("setup start: data_reg readback = %x\n", buff);
    if (buff & (1 << remote->index)){
        rtapi_print("Remote failed to start\n");
        return -1;
    }
    return 0;
}

int nv_access(u32 type){
    u32 buff = LBPNONVOL_flag + LBPWRITE;
    rtapi_print("buff = %x\n", buff);
    HM2WRITE(remote->reg_cs_addr, buff);
    HM2WRITE(remote->reg_0_addr, type);
    return doit();
}

int set_nvram_param(u32 addr, u32 value){
    u32 buff;
    
    if (stop_all() < 0) goto fail0;
    if (setup_start() < 0) goto fail0;
    if (nv_access(LBPNONVOLEEPROM) < 0) goto fail0;
    
    // value to set
    HM2WRITE(remote->reg_0_addr, value);
    buff = WRITE_REM_WORD_CMD | addr; 
    HM2WRITE(remote->reg_cs_addr, buff);
    if (doit() < 0) goto fail0;
    
    if (nv_access(LBPNONVOLCLEAR) < 0) goto fail0;
    
    return 0;
fail0: // It's all gone wrong
    buff=0x800; //Stop
    HM2WRITE(remote->command_reg_addr, buff);
    rtapi_print_msg(RTAPI_MSG_ERR,
                    "Problem with Smart Serial parameter setting, see dmesg\n");
    return -1;
}

static void setsserial_release(struct device *dev) {
    // nothing to do here
}

int getlocal(int addr, int bytes){
    u32 val = 0;
    u32 buff;
    for (;bytes--;){
        buff = READ_LOCAL_CMD | (addr + bytes);
        HM2WRITE(remote->command_reg_addr, buff);
        waitfor();
        HM2READ(remote->data_reg_addr, buff);
        val = (val << 8) | buff;
    }    return val;
}

int setlocal(int addr, int val, int bytes){
    u32 b = 0;
    u32 buff;
    int i;
    for (i = 0; i < bytes; i++){
        b = val & 0xFF;
        val >>= 8;
        HM2WRITE(remote->data_reg_addr, b);
        buff = WRITE_LOCAL_CMD | (addr + i);
        HM2WRITE(remote->command_reg_addr, buff);
        if (waitfor() < 0) return -1;
    }   
    return 0;
}

void sslbp_write_lbp(u32 cmd, u32 data){
    u32 buff = LBPWRITE + cmd;
    HM2WRITE(remote->reg_cs_addr, buff);
    HM2WRITE(remote->reg_0_addr, data);
    doit();
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
}

int sslbp_read_cookie(void){
    u32 buff = READ_COOKIE_CMD;
    u32 res;
    HM2WRITE(remote->reg_cs_addr, buff);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_read_cookie, trying to abort\n");
        return -1;
    }
    HM2READ(remote->reg_0_addr, res);
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return res;
}

u8 sslbp_read_byte(u32 addr){
    u32 buff = READ_REM_BYTE_CMD + addr;
    u32 res;
    HM2WRITE(remote->reg_cs_addr, buff);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_read_byte, trying to abort\n");
        return -1;
    }
    HM2READ(remote->reg_0_addr, res);
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return (u8)res;
}

u16 sslbp_read_word(u32 addr){
    u32 buff = READ_REM_WORD_CMD + addr;
    u32 res;
    HM2WRITE(remote->reg_cs_addr, buff);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_read_word, trying to abort\n");
        return -1;
    }
    HM2READ(remote->reg_0_addr, res);
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return (u16)res;
}

u32 sslbp_read_long(u32 addr){
    u32 buff = READ_REM_LONG_CMD + addr;
    u32 res=0;
    HM2WRITE(remote->reg_cs_addr, buff);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_read_long, trying to abort\n");
        return -1;
    }
    HM2READ(remote->reg_0_addr, buff);
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return res;
}

u64 sslbp_read_double(u32 addr){
    u64 res;
    u32 buff = READ_REM_DOUBLE_CMD + addr;
    HM2WRITE(remote->reg_cs_addr, buff);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_read_double, trying to abort\n");
        return -1;
    }
    HM2READ(remote->reg_1_addr, buff);
    res = buff;
    res <<= 32;
    HM2READ(remote->reg_0_addr, buff);
    res += buff;
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return res;
}

int sslbp_write_byte(u32 addr, u32 data){
    u32 buff = WRITE_REM_BYTE_CMD + addr;
    HM2WRITE(remote->reg_cs_addr, buff);
    HM2WRITE(remote->reg_0_addr, data);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_write_byte, trying to abort\n");
        return -1;
    }
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return 0;
}

int sslbp_write_word(u32 addr, u32 data){
    u32 buff = WRITE_REM_WORD_CMD + addr;
    HM2WRITE(remote->reg_cs_addr, buff);
    HM2WRITE(remote->reg_0_addr, data);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_write_word, trying to abort\n");
        return -1;
    }
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return 0;
}

int sslbp_write_long(u32 addr, u32 data){
    u32 buff = WRITE_REM_LONG_CMD + addr;
    HM2WRITE(remote->reg_cs_addr, buff);
    HM2WRITE(remote->reg_0_addr, data);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_write_long, trying to abort\n");
        return -1;
    }
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return 0;
}

int sslbp_write_double(u32 addr, u32 data0, u32 data1){
    u32 buff = WRITE_REM_DOUBLE_CMD + addr;
    HM2WRITE(remote->reg_cs_addr, buff);
    HM2WRITE(remote->reg_0_addr, data0);
    HM2WRITE(remote->reg_1_addr, data1);
    if (doit() < 0){
        HM2_ERR("Error in sslbp_write_double, trying to abort\n");
        return -1;
    }
    buff = 0;
    HM2WRITE(remote->reg_cs_addr, buff);
    return 0;
}

    
void flash_start(void){
    sslbp_write_lbp(LBPNONVOL_flag, LBPNONVOLFLASH);
}

void flash_stop(void){
    sslbp_write_lbp(LBPNONVOL_flag, 0);
}
    
int sslbp_flash(char *fname){
    const struct firmware *fw;
    struct device dev;
    int r;
    int write_sz, erase_sz;
    
    if (strstr("8i20", remote->name)){
        if (hm2->sserial.version < 37){
            rtapi_print("SSLBP Version must be at least v37 to flash the 8i20"
                        "This firmware has v%i. Sorry about that\n"
                        ,hm2->sserial.version);
            return -1;
        }
    }
    else if (hm2->sserial.version < 34){
        rtapi_print("SSLBP Version must be at least v34. This firmware has v%i"
                    "\n",hm2->sserial.version);
        return -1;
    }
    
    if (hm2->sserial.baudrate != 115200){
        rtapi_print("To flash firmware the baud rate of the board must be set "
                    "to 115200 by jumper, and in Hostmot2 using the "
                    "sserial_baudrate modparam\n");
        return -1;
    }
     
    //Copied direct from hostmot2.c. A bit of a faff, but seems to be necessary. 
    memset(&dev, '\0', sizeof(dev));
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)
    strncpy(dev.bus_id, hm2->llio->name, BUS_ID_SIZE);
    dev.bus_id[BUS_ID_SIZE - 1] = '\0';
#else
    dev_set_name(&dev, hm2->llio->name);
#endif
    dev.release = setsserial_release;
    r = device_register(&dev);
    if (r != 0) {
        HM2_ERR("error with device_register\n");
        return -1;
    }
    r = request_firmware(&fw, fname, &dev);
    device_unregister(&dev);
    if (r == -ENOENT) {
        HM2_ERR("firmware %s not found\n",fname);
        return -1;
    }
    if (r != 0) {
        HM2_ERR("request for firmware %s failed, aborting\n", fname);
        return -1;
    }    
    rtapi_print("Firmware size 0x%zx\n", fw->size);
    
    if (setup_start() < 0) goto fail0;
    flash_start();
    write_sz = 1 << sslbp_read_byte(LBPFLASHWRITESIZELOC);
    erase_sz = 1 << sslbp_read_byte(LBPFLASHERASESIZELOC);
    HM2_PRINT("Write Size = %x, Erase Size = %x\n", write_sz, erase_sz);
    flash_stop();
    
    //Programming Loop
    {
        int ReservedBlock = 0;
        int StartBlock = ReservedBlock + 1;
        
        int blocknum = StartBlock;
        int block_start;
        int i, j, t;
        while (blocknum * erase_sz < fw->size){
            block_start = blocknum * erase_sz;
            for (t = 0; t < erase_sz && fw->data[block_start + t] == 0 ; t++){ }
            if (t <  erase_sz){ // found a non-zero byte
                flash_start();
                sslbp_write_long(LBPFLASHOFFSETLOC, block_start);
                sslbp_write_byte(LBPFLASHCOMMITLOC, FLASHERASE_CMD);
                if (sslbp_read_cookie() != LBPCOOKIE){
                    HM2_ERR("Synch failed during block erase: aborting\n");
                    goto fail0;
                }
                flash_stop();
                HM2_PRINT("Erased block %i\n", blocknum);
                flash_start();
                for (i = 0; i < erase_sz ; i += write_sz){
                    sslbp_write_long(LBPFLASHOFFSETLOC, block_start + i);
                    for (j = 0 ; j < write_sz ; j += 8){
                        u32 data0, data1, m;
                        m = block_start + i + j;
                        data0 = (fw->data[m] 
                              + (fw->data[m + 1] << 8)
                              + (fw->data[m + 2] << 16)
                              + (fw->data[m + 3] << 24));
                        data1 = (fw->data[m + 4] 
                              + (fw->data[m + 5] << 8)
                              + (fw->data[m + 6] << 16)
                              + (fw->data[m + 7] << 24));
                        sslbp_write_double(j, data0, data1);
                    }
                    sslbp_write_byte(LBPFLASHCOMMITLOC, FLASHWRITE_CMD);
                    if (sslbp_read_cookie() != LBPCOOKIE){
                        HM2_ERR("Synch failed during block write: aborting\n");
                        goto fail0;
                    }
                }
                flash_stop();
                HM2_PRINT("Wrote block %i\n", blocknum);
            }
            else // Looks like an all-zeros block
            { 
                HM2_PRINT("Skipped Block %i\n", blocknum);
            }
            blocknum++;
        }
    }
    
    release_firmware(fw);
    
    return 0;
    
fail0:
    flash_stop();
    return -1;
}

int rtapi_app_main(void)
{
    int cnt;
    
    comp_id = hal_init("setsserial");
    hal_ready(comp_id);
    
    cmd_list = argv_split(GFP_KERNEL, cmd, &cnt);
    
    remote = hm2_get_sserial(&hm2, cmd_list[1]);
    if (! remote) {   
        rtapi_print_msg(RTAPI_MSG_ERR,
                        "Unable to find sserial remote corresponding to %s\n", 
                        cmd_list[1]);
        return -1;
    }    
    
    if (! strncmp("set", cmd_list[0], 3) && cnt == 3){
        u32 value;
        u32 addr;
        int i;
        rtapi_print("set command %s\n", cmd_list[1]);
        addr = 0;
        for (i = 0; i < remote->num_globals; i++){
            if (strstr(cmd_list[1], remote->globals[i].NameString)){
                addr = remote->globals[i].ParmAddr;
                break;
            }
        }
        if (! addr) {   
            rtapi_print_msg(RTAPI_MSG_ERR,
                            "Unable to find parameter corresponding to %s\n", 
                            cmd_list[1]);
            return -1;
        }
        value = simple_strtol(cmd_list[2], NULL, 0);
        rtapi_print("remote name = %s ParamAddr = %x Value = %i\n",
                    remote->name,
                    addr,
                    value);
        if (set_nvram_param(addr, value) < 0) {
            rtapi_print_msg(RTAPI_MSG_ERR, "Parameter setting failed\n");
            return -1;
        } 
        else
        {   rtapi_print_msg(RTAPI_MSG_ERR, "Parameter setting success\n");
            return 0;
        }
    } 
    else if (! strncmp("flash", cmd_list[0], 5) && cnt == 3){
        rtapi_print("flash command\n");
        if ( ! strstr(cmd_list[2], ".BIN")){
            rtapi_print("Smart-Serial remote firmwares are .BIN format\n "
                        "flashing with the wrong one would be bad. Aborting\n");
            return -EINVAL;
        }
        if (sslbp_flash(cmd_list[2]) < 0){
            rtapi_print_msg(RTAPI_MSG_ERR, "Firmware Flash Failed\n");
            return -1;
        } 
        else
        {   rtapi_print_msg(RTAPI_MSG_ERR, "Firmware Flash Success\n");
            return 0;
        } 
    }
    else {
        rtapi_print_msg(RTAPI_MSG_ERR, 
                        "Unknown commmand or wrong number of parameters to " 
                        "setsserial command");
        return -1;
    }
    
    return 0;
}

void rtapi_app_exit(void)
{
    hal_exit(comp_id);
}