diff options
author | Gilbert Wu <Gilbert_Wu@adaptec.com> | 2007-10-22 18:19:11 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-01-11 19:22:30 -0500 |
commit | 1237c98db2aa94b42dbb9fb1df062b7d3733dc83 (patch) | |
tree | 01c6d82790815753c0e66e9c3e571862d0f226bf /drivers/scsi/aic94xx/aic94xx_sds.c | |
parent | 285e9670d91cdeb6b6693729950339cb45410fdc (diff) |
[SCSI] aic94xx: update BIOS image from user space.
1. Create a file "update_bios" in sysfs to allow user to update bios
from user space.
2. The BIOS image file can be downloaded from web site
"http://www.adaptec.com/en-US/downloads/bios_fw/bios_fw_ver?productId=SAS-48300&dn=Adaptec+Serial+Attached+SCSI+48300"
and copy the BIOS image into /lib/firmware folder.
3. The aic994xx will accept "update bios_file" and "verify bios_file"
commands to perform update and verify BIOS image .
For example:
Type "echo "update asc483c01.ufi" > /sys/devices/.../update_bios"
to update BIOS image from /lib/firmware/as483c01.ufi file into
HBA's flash memory.
Type "echo "verify asc483c01.ufi" > /sys/devices/.../update_bios"
to verify BIOS image between /lib/firmware/asc48c01.ufi file
and
HBA's flash memory.
4. Type "cat /sys/devices/.../update_bios" to view the status or
result
of updating BIOS.
Signed-off-by: Gilbert Wu <gilbert_wu@adaptec.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers/scsi/aic94xx/aic94xx_sds.c')
-rw-r--r-- | drivers/scsi/aic94xx/aic94xx_sds.c | 389 |
1 files changed, 389 insertions, 0 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c index 06509bff71f7..2a4c933eb89c 100644 --- a/drivers/scsi/aic94xx/aic94xx_sds.c +++ b/drivers/scsi/aic94xx/aic94xx_sds.c | |||
@@ -30,6 +30,7 @@ | |||
30 | 30 | ||
31 | #include "aic94xx.h" | 31 | #include "aic94xx.h" |
32 | #include "aic94xx_reg.h" | 32 | #include "aic94xx_reg.h" |
33 | #include "aic94xx_sds.h" | ||
33 | 34 | ||
34 | /* ---------- OCM stuff ---------- */ | 35 | /* ---------- OCM stuff ---------- */ |
35 | 36 | ||
@@ -1083,3 +1084,391 @@ out: | |||
1083 | kfree(flash_dir); | 1084 | kfree(flash_dir); |
1084 | return err; | 1085 | return err; |
1085 | } | 1086 | } |
1087 | |||
1088 | /** | ||
1089 | * asd_verify_flash_seg - verify data with flash memory | ||
1090 | * @asd_ha: pointer to the host adapter structure | ||
1091 | * @src: pointer to the source data to be verified | ||
1092 | * @dest_offset: offset from flash memory | ||
1093 | * @bytes_to_verify: total bytes to verify | ||
1094 | */ | ||
1095 | int asd_verify_flash_seg(struct asd_ha_struct *asd_ha, | ||
1096 | void *src, u32 dest_offset, u32 bytes_to_verify) | ||
1097 | { | ||
1098 | u8 *src_buf; | ||
1099 | u8 flash_char; | ||
1100 | int err; | ||
1101 | u32 nv_offset, reg, i; | ||
1102 | |||
1103 | reg = asd_ha->hw_prof.flash.bar; | ||
1104 | src_buf = NULL; | ||
1105 | |||
1106 | err = FLASH_OK; | ||
1107 | nv_offset = dest_offset; | ||
1108 | src_buf = (u8 *)src; | ||
1109 | for (i = 0; i < bytes_to_verify; i++) { | ||
1110 | flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i); | ||
1111 | if (flash_char != src_buf[i]) { | ||
1112 | err = FAIL_VERIFY; | ||
1113 | break; | ||
1114 | } | ||
1115 | } | ||
1116 | return err; | ||
1117 | } | ||
1118 | |||
1119 | /** | ||
1120 | * asd_write_flash_seg - write data into flash memory | ||
1121 | * @asd_ha: pointer to the host adapter structure | ||
1122 | * @src: pointer to the source data to be written | ||
1123 | * @dest_offset: offset from flash memory | ||
1124 | * @bytes_to_write: total bytes to write | ||
1125 | */ | ||
1126 | int asd_write_flash_seg(struct asd_ha_struct *asd_ha, | ||
1127 | void *src, u32 dest_offset, u32 bytes_to_write) | ||
1128 | { | ||
1129 | u8 *src_buf; | ||
1130 | u32 nv_offset, reg, i; | ||
1131 | int err; | ||
1132 | |||
1133 | reg = asd_ha->hw_prof.flash.bar; | ||
1134 | src_buf = NULL; | ||
1135 | |||
1136 | err = asd_check_flash_type(asd_ha); | ||
1137 | if (err) { | ||
1138 | ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err); | ||
1139 | return err; | ||
1140 | } | ||
1141 | |||
1142 | nv_offset = dest_offset; | ||
1143 | err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write); | ||
1144 | if (err) { | ||
1145 | ASD_DPRINTK("Erase failed at offset:0x%x\n", | ||
1146 | nv_offset); | ||
1147 | return err; | ||
1148 | } | ||
1149 | |||
1150 | err = asd_reset_flash(asd_ha); | ||
1151 | if (err) { | ||
1152 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | ||
1153 | return err; | ||
1154 | } | ||
1155 | |||
1156 | src_buf = (u8 *)src; | ||
1157 | for (i = 0; i < bytes_to_write; i++) { | ||
1158 | /* Setup program command sequence */ | ||
1159 | switch (asd_ha->hw_prof.flash.method) { | ||
1160 | case FLASH_METHOD_A: | ||
1161 | { | ||
1162 | asd_write_reg_byte(asd_ha, | ||
1163 | (reg + 0xAAA), 0xAA); | ||
1164 | asd_write_reg_byte(asd_ha, | ||
1165 | (reg + 0x555), 0x55); | ||
1166 | asd_write_reg_byte(asd_ha, | ||
1167 | (reg + 0xAAA), 0xA0); | ||
1168 | asd_write_reg_byte(asd_ha, | ||
1169 | (reg + nv_offset + i), | ||
1170 | (*(src_buf + i))); | ||
1171 | break; | ||
1172 | } | ||
1173 | case FLASH_METHOD_B: | ||
1174 | { | ||
1175 | asd_write_reg_byte(asd_ha, | ||
1176 | (reg + 0x555), 0xAA); | ||
1177 | asd_write_reg_byte(asd_ha, | ||
1178 | (reg + 0x2AA), 0x55); | ||
1179 | asd_write_reg_byte(asd_ha, | ||
1180 | (reg + 0x555), 0xA0); | ||
1181 | asd_write_reg_byte(asd_ha, | ||
1182 | (reg + nv_offset + i), | ||
1183 | (*(src_buf + i))); | ||
1184 | break; | ||
1185 | } | ||
1186 | default: | ||
1187 | break; | ||
1188 | } | ||
1189 | if (asd_chk_write_status(asd_ha, | ||
1190 | (nv_offset + i), 0) != 0) { | ||
1191 | ASD_DPRINTK("aicx: Write failed at offset:0x%x\n", | ||
1192 | reg + nv_offset + i); | ||
1193 | return FAIL_WRITE_FLASH; | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | err = asd_reset_flash(asd_ha); | ||
1198 | if (err) { | ||
1199 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | ||
1200 | return err; | ||
1201 | } | ||
1202 | return 0; | ||
1203 | } | ||
1204 | |||
1205 | int asd_chk_write_status(struct asd_ha_struct *asd_ha, | ||
1206 | u32 sector_addr, u8 erase_flag) | ||
1207 | { | ||
1208 | u32 reg; | ||
1209 | u32 loop_cnt; | ||
1210 | u8 nv_data1, nv_data2; | ||
1211 | u8 toggle_bit1; | ||
1212 | |||
1213 | /* | ||
1214 | * Read from DQ2 requires sector address | ||
1215 | * while it's dont care for DQ6 | ||
1216 | */ | ||
1217 | reg = asd_ha->hw_prof.flash.bar; | ||
1218 | |||
1219 | for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) { | ||
1220 | nv_data1 = asd_read_reg_byte(asd_ha, reg); | ||
1221 | nv_data2 = asd_read_reg_byte(asd_ha, reg); | ||
1222 | |||
1223 | toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6) | ||
1224 | ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6)); | ||
1225 | |||
1226 | if (toggle_bit1 == 0) { | ||
1227 | return 0; | ||
1228 | } else { | ||
1229 | if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) { | ||
1230 | nv_data1 = asd_read_reg_byte(asd_ha, | ||
1231 | reg); | ||
1232 | nv_data2 = asd_read_reg_byte(asd_ha, | ||
1233 | reg); | ||
1234 | toggle_bit1 = | ||
1235 | ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6) | ||
1236 | ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6)); | ||
1237 | |||
1238 | if (toggle_bit1 == 0) | ||
1239 | return 0; | ||
1240 | } | ||
1241 | } | ||
1242 | |||
1243 | /* | ||
1244 | * ERASE is a sector-by-sector operation and requires | ||
1245 | * more time to finish while WRITE is byte-byte-byte | ||
1246 | * operation and takes lesser time to finish. | ||
1247 | * | ||
1248 | * For some strange reason a reduced ERASE delay gives different | ||
1249 | * behaviour across different spirit boards. Hence we set | ||
1250 | * a optimum balance of 50mus for ERASE which works well | ||
1251 | * across all boards. | ||
1252 | */ | ||
1253 | if (erase_flag) { | ||
1254 | udelay(FLASH_STATUS_ERASE_DELAY_COUNT); | ||
1255 | } else { | ||
1256 | udelay(FLASH_STATUS_WRITE_DELAY_COUNT); | ||
1257 | } | ||
1258 | } | ||
1259 | return -1; | ||
1260 | } | ||
1261 | |||
1262 | /** | ||
1263 | * asd_hwi_erase_nv_sector - Erase the flash memory sectors. | ||
1264 | * @asd_ha: pointer to the host adapter structure | ||
1265 | * @flash_addr: pointer to offset from flash memory | ||
1266 | * @size: total bytes to erase. | ||
1267 | */ | ||
1268 | int asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size) | ||
1269 | { | ||
1270 | u32 reg; | ||
1271 | u32 sector_addr; | ||
1272 | |||
1273 | reg = asd_ha->hw_prof.flash.bar; | ||
1274 | |||
1275 | /* sector staring address */ | ||
1276 | sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK; | ||
1277 | |||
1278 | /* | ||
1279 | * Erasing an flash sector needs to be done in six consecutive | ||
1280 | * write cyles. | ||
1281 | */ | ||
1282 | while (sector_addr < flash_addr+size) { | ||
1283 | switch (asd_ha->hw_prof.flash.method) { | ||
1284 | case FLASH_METHOD_A: | ||
1285 | asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA); | ||
1286 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55); | ||
1287 | asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80); | ||
1288 | asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA); | ||
1289 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55); | ||
1290 | asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30); | ||
1291 | break; | ||
1292 | case FLASH_METHOD_B: | ||
1293 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); | ||
1294 | asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); | ||
1295 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80); | ||
1296 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); | ||
1297 | asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); | ||
1298 | asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30); | ||
1299 | break; | ||
1300 | default: | ||
1301 | break; | ||
1302 | } | ||
1303 | |||
1304 | if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0) | ||
1305 | return FAIL_ERASE_FLASH; | ||
1306 | |||
1307 | sector_addr += FLASH_SECTOR_SIZE; | ||
1308 | } | ||
1309 | |||
1310 | return 0; | ||
1311 | } | ||
1312 | |||
1313 | int asd_check_flash_type(struct asd_ha_struct *asd_ha) | ||
1314 | { | ||
1315 | u8 manuf_id; | ||
1316 | u8 dev_id; | ||
1317 | u8 sec_prot; | ||
1318 | u32 inc; | ||
1319 | u32 reg; | ||
1320 | int err; | ||
1321 | |||
1322 | /* get Flash memory base address */ | ||
1323 | reg = asd_ha->hw_prof.flash.bar; | ||
1324 | |||
1325 | /* Determine flash info */ | ||
1326 | err = asd_reset_flash(asd_ha); | ||
1327 | if (err) { | ||
1328 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | ||
1329 | return err; | ||
1330 | } | ||
1331 | |||
1332 | asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN; | ||
1333 | asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN; | ||
1334 | asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN; | ||
1335 | |||
1336 | /* Get flash info. This would most likely be AMD Am29LV family flash. | ||
1337 | * First try the sequence for word mode. It is the same as for | ||
1338 | * 008B (byte mode only), 160B (word mode) and 800D (word mode). | ||
1339 | */ | ||
1340 | inc = asd_ha->hw_prof.flash.wide ? 2 : 1; | ||
1341 | asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA); | ||
1342 | asd_write_reg_byte(asd_ha, reg + 0x555, 0x55); | ||
1343 | asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90); | ||
1344 | manuf_id = asd_read_reg_byte(asd_ha, reg); | ||
1345 | dev_id = asd_read_reg_byte(asd_ha, reg + inc); | ||
1346 | sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc); | ||
1347 | /* Get out of autoselect mode. */ | ||
1348 | err = asd_reset_flash(asd_ha); | ||
1349 | if (err) { | ||
1350 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | ||
1351 | return err; | ||
1352 | } | ||
1353 | ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) " | ||
1354 | "sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot); | ||
1355 | err = asd_reset_flash(asd_ha); | ||
1356 | if (err != 0) | ||
1357 | return err; | ||
1358 | |||
1359 | switch (manuf_id) { | ||
1360 | case FLASH_MANUF_ID_AMD: | ||
1361 | switch (sec_prot) { | ||
1362 | case FLASH_DEV_ID_AM29LV800DT: | ||
1363 | case FLASH_DEV_ID_AM29LV640MT: | ||
1364 | case FLASH_DEV_ID_AM29F800B: | ||
1365 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | ||
1366 | break; | ||
1367 | default: | ||
1368 | break; | ||
1369 | } | ||
1370 | break; | ||
1371 | case FLASH_MANUF_ID_ST: | ||
1372 | switch (sec_prot) { | ||
1373 | case FLASH_DEV_ID_STM29W800DT: | ||
1374 | case FLASH_DEV_ID_STM29LV640: | ||
1375 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | ||
1376 | break; | ||
1377 | default: | ||
1378 | break; | ||
1379 | } | ||
1380 | break; | ||
1381 | case FLASH_MANUF_ID_FUJITSU: | ||
1382 | switch (sec_prot) { | ||
1383 | case FLASH_DEV_ID_MBM29LV800TE: | ||
1384 | case FLASH_DEV_ID_MBM29DL800TA: | ||
1385 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | ||
1386 | break; | ||
1387 | } | ||
1388 | break; | ||
1389 | case FLASH_MANUF_ID_MACRONIX: | ||
1390 | switch (sec_prot) { | ||
1391 | case FLASH_DEV_ID_MX29LV800BT: | ||
1392 | asd_ha->hw_prof.flash.method = FLASH_METHOD_A; | ||
1393 | break; | ||
1394 | } | ||
1395 | break; | ||
1396 | } | ||
1397 | |||
1398 | if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) { | ||
1399 | err = asd_reset_flash(asd_ha); | ||
1400 | if (err) { | ||
1401 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | ||
1402 | return err; | ||
1403 | } | ||
1404 | |||
1405 | /* Issue Unlock sequence for AM29LV008BT */ | ||
1406 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA); | ||
1407 | asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55); | ||
1408 | asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90); | ||
1409 | manuf_id = asd_read_reg_byte(asd_ha, reg); | ||
1410 | dev_id = asd_read_reg_byte(asd_ha, reg + inc); | ||
1411 | sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc); | ||
1412 | |||
1413 | ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot" | ||
1414 | "(0x%x)\n", manuf_id, dev_id, sec_prot); | ||
1415 | |||
1416 | err = asd_reset_flash(asd_ha); | ||
1417 | if (err != 0) { | ||
1418 | ASD_DPRINTK("couldn't reset flash. err=%d\n", err); | ||
1419 | return err; | ||
1420 | } | ||
1421 | |||
1422 | switch (manuf_id) { | ||
1423 | case FLASH_MANUF_ID_AMD: | ||
1424 | switch (dev_id) { | ||
1425 | case FLASH_DEV_ID_AM29LV008BT: | ||
1426 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | ||
1427 | break; | ||
1428 | default: | ||
1429 | break; | ||
1430 | } | ||
1431 | break; | ||
1432 | case FLASH_MANUF_ID_ST: | ||
1433 | switch (dev_id) { | ||
1434 | case FLASH_DEV_ID_STM29008: | ||
1435 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | ||
1436 | break; | ||
1437 | default: | ||
1438 | break; | ||
1439 | } | ||
1440 | break; | ||
1441 | case FLASH_MANUF_ID_FUJITSU: | ||
1442 | switch (dev_id) { | ||
1443 | case FLASH_DEV_ID_MBM29LV008TA: | ||
1444 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | ||
1445 | break; | ||
1446 | } | ||
1447 | break; | ||
1448 | case FLASH_MANUF_ID_INTEL: | ||
1449 | switch (dev_id) { | ||
1450 | case FLASH_DEV_ID_I28LV00TAT: | ||
1451 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | ||
1452 | break; | ||
1453 | } | ||
1454 | break; | ||
1455 | case FLASH_MANUF_ID_MACRONIX: | ||
1456 | switch (dev_id) { | ||
1457 | case FLASH_DEV_ID_I28LV00TAT: | ||
1458 | asd_ha->hw_prof.flash.method = FLASH_METHOD_B; | ||
1459 | break; | ||
1460 | } | ||
1461 | break; | ||
1462 | default: | ||
1463 | return FAIL_FIND_FLASH_ID; | ||
1464 | } | ||
1465 | } | ||
1466 | |||
1467 | if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) | ||
1468 | return FAIL_FIND_FLASH_ID; | ||
1469 | |||
1470 | asd_ha->hw_prof.flash.manuf = manuf_id; | ||
1471 | asd_ha->hw_prof.flash.dev_id = dev_id; | ||
1472 | asd_ha->hw_prof.flash.sec_prot = sec_prot; | ||
1473 | return 0; | ||
1474 | } | ||