// // 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 #include #include #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){ rtapi_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){ rtapi_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){ rtapi_u32 buff=0x8FF; HM2WRITE(remote->command_reg_addr, buff); return waitfor(); } int setup_start(void){ rtapi_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(rtapi_u32 type){ rtapi_u32 buff = LBPNONVOL_flag + LBPWRITE; rtapi_print("buff = %x\n", buff); HM2WRITE(remote->reg_cs_addr, buff); HM2WRITE(remote->rw_addr[0], type); return doit(); } int set_nvram_param(rtapi_u32 addr, rtapi_u32 value){ rtapi_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->rw_addr[0], 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 rtapi_device *dev) { // nothing to do here } int getlocal(int addr, int bytes){ rtapi_u32 val = 0; rtapi_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){ rtapi_u32 b = 0; rtapi_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(rtapi_u32 cmd, rtapi_u32 data){ rtapi_u32 buff = LBPWRITE + cmd; HM2WRITE(remote->reg_cs_addr, buff); HM2WRITE(remote->rw_addr[0], data); doit(); buff = 0; HM2WRITE(remote->reg_cs_addr, buff); } int sslbp_read_cookie(void){ rtapi_u32 buff = READ_COOKIE_CMD; rtapi_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->rw_addr[0], res); buff = 0; HM2WRITE(remote->reg_cs_addr, buff); return res; } rtapi_u8 sslbp_read_byte(rtapi_u32 addr){ rtapi_u32 buff = READ_REM_BYTE_CMD + addr; rtapi_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->rw_addr[0], res); buff = 0; HM2WRITE(remote->reg_cs_addr, buff); return (rtapi_u8)res; } rtapi_u16 sslbp_read_word(rtapi_u32 addr){ rtapi_u32 buff = READ_REM_WORD_CMD + addr; rtapi_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->rw_addr[0], res); buff = 0; HM2WRITE(remote->reg_cs_addr, buff); return (rtapi_u16)res; } rtapi_u32 sslbp_read_long(rtapi_u32 addr){ rtapi_u32 buff = READ_REM_LONG_CMD + addr; rtapi_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->rw_addr[0], buff); buff = 0; HM2WRITE(remote->reg_cs_addr, buff); return res; } rtapi_u64 sslbp_read_double(rtapi_u32 addr){ rtapi_u64 res; rtapi_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->rw_addr[1], buff); res = buff; res <<= 32; HM2READ(remote->rw_addr[0], buff); res += buff; buff = 0; HM2WRITE(remote->reg_cs_addr, buff); return res; } int sslbp_write_byte(rtapi_u32 addr, rtapi_u32 data){ rtapi_u32 buff = WRITE_REM_BYTE_CMD + addr; HM2WRITE(remote->reg_cs_addr, buff); HM2WRITE(remote->rw_addr[0], 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(rtapi_u32 addr, rtapi_u32 data){ rtapi_u32 buff = WRITE_REM_WORD_CMD + addr; HM2WRITE(remote->reg_cs_addr, buff); HM2WRITE(remote->rw_addr[0], 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(rtapi_u32 addr, rtapi_u32 data){ rtapi_u32 buff = WRITE_REM_LONG_CMD + addr; HM2WRITE(remote->reg_cs_addr, buff); HM2WRITE(remote->rw_addr[0], 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(rtapi_u32 addr, rtapi_u32 data0, rtapi_u32 data1){ rtapi_u32 buff = WRITE_REM_DOUBLE_CMD + addr; HM2WRITE(remote->reg_cs_addr, buff); HM2WRITE(remote->rw_addr[0], data0); HM2WRITE(remote->rw_addr[1], 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 rtapi_firmware *fw; struct rtapi_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)); rtapi_dev_set_name(&dev, "%s", hm2->llio->name); dev.release = setsserial_release; r = rtapi_device_register(&dev); if (r != 0) { HM2_ERR("error with device_register\n"); return -1; } r = rtapi_request_firmware(&fw, fname, &dev); rtapi_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){ rtapi_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++; } } rtapi_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 = rtapi_argv_split(RTAPI_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){ rtapi_u32 value; rtapi_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 command or wrong number of parameters to " "setsserial command"); return -1; } return 0; } void rtapi_app_exit(void) { hal_exit(comp_id); }