/* -*- linux-c -*- * Copyright (c) 2004-2006 Winbond Electronics Corp. All rights reserved. * * The contents of this file are subject to the Open * Software License version 1.1 that can be found at * http://www.opensource.org/licenses/osl-1.1.txt and is included herein * by reference. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License version 2 (the "GPL") as distributed * in the kernel source COPYING file, in which case the provisions of * the GPL are applicable instead of the above. If you wish to allow * the use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under * the OSL, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the OSL or the GPL. * Maintained by: Weiming Shuai Jin Yuan Mu Dezheng Shen $Author: sjin $ $Id: wbmscard.c,v 1.12.2.7 2006/06/22 06:06:12 sjin Exp $ --- don't modify lines above --- Description: Enviornments: (1) RedHat 9 on 32-bit Intel UP/32-bit Intel dual-core (2) Fedora Core 4 (2.6.11) on 32-bit Intel/32-bit Intel dual-core/64-bit AMD/64-bit Intel dual-core (3) RedHat Enterprise Workstatin Rel 4 UP2 (2.6.9) on 32-bit Intel 32-bit Intel dual-core/64-bit AMD/64-bit Intel dual-core */ #include "wbmscard.h" #include "wbtable.h" static wbms_card_info_t g_card_info; static inline int wbms_send_tpc(unsigned char cmd, unsigned char *buffer, int length) { unsigned int ret; ret = wbreader_issue_cmd(cmd, buffer, length); if (ret > 0) { g_card_info.registers[1] = ((ret << 4) & 0xE0) | (ret & 0x10); } return ret; } static inline int tpc_read_long_data(void) { wbreader_set_state(WSTATE_FIFO_SIZE, MS_PAGE_SIZE); return wbms_send_tpc(TPC_READ_LONG_DATA, NULL, 0); } static inline int tpc_read_short_data(void) { return wbms_send_tpc(TPC_READ_SHORT_DATA, NULL, 0); } static inline int tpc_read_reg(void) { return wbms_send_tpc(TPC_READ_REG, NULL, 0); } static inline int tpc_get_int(void) { wbreader_set_state(WSTATE_FIFO_SIZE, 1); return wbms_send_tpc(TPC_GET_INT, NULL, 0); } static inline int tpc_write_long_data(void) { wbreader_set_state(WSTATE_FIFO_SIZE, MS_PAGE_SIZE); return wbms_send_tpc(TPC_WRITE_LONG_DATA, NULL, 0); } static inline int tpc_write_short_data(void) { return wbms_send_tpc(TPC_WRITE_SHORT_DATA, NULL, 0); } static inline int tpc_write_reg(unsigned char *buffer, int len) { return wbms_send_tpc(TPC_WRITE_REG, buffer, len); } static inline int tpc_set_cmd(unsigned char flash_cmd) { return wbms_send_tpc(TPC_SET_CMD, &flash_cmd, 1); } static inline int tpc_ex_set_cmd(unsigned char flash_cmd, unsigned int lba, unsigned short size) { unsigned char param[7]; param[0] = flash_cmd; param[1] = 0xFF & (size >> 8); param[2] = 0xFF & size; param[3] = 0xFF & (lba >> 24); param[4] = 0xFF & (lba >> 16); param[5] = 0xFF & (lba >> 8); param[6] = 0xFF & lba; return wbms_send_tpc(TPC_EX_SET_CMD, param, 7); } static inline int tpc_set_rw_reg_adrs(unsigned char read_adrs, unsigned char read_size, unsigned char write_adrs, unsigned char write_size) { unsigned char param[4]; param[0] = read_adrs; param[1] = read_size; param[2] = write_adrs; param[3] = write_size; return wbms_send_tpc(TPC_SET_RW_REG_ADRS, param, 4); } static int wbms_read_register(void) { int ret; ret = -1; if (0 > tpc_set_rw_reg_adrs(0, 0x20, 0x10, 1)) { WB_PRINTK_ERROR("tpc_set_rw_reg_adrs error\n"); goto lab_out; } wbreader_set_state(WSTATE_FIFO_SIZE, 0x20); if (0 > tpc_read_reg()) { WB_PRINTK_ERROR("tpc_read_reg error\n"); goto lab_out; } if (wbreader_transfer_data ((unsigned char *)g_card_info.registers, 0x20, TRUE, WREADER_PIO) != 0) { WB_PRINTK_ERROR("wbreader_transfer_data error\n"); goto lab_out; } ret = 0; lab_out: return ret; } static int wbms_read_status_register(void) { int ret; ret = -1; if (0 > tpc_set_rw_reg_adrs(0, 16, 0x10, 1)) { WB_PRINTK("tpc_set_rw_reg_adrs error\n"); goto lab_out; } wbreader_set_state(WSTATE_FIFO_SIZE, 0x10); if (0 > tpc_read_reg()) { WB_PRINTK("tpc_read_reg error\n"); goto lab_out; } if (wbreader_transfer_data ((unsigned char *)g_card_info.registers, 16, TRUE, WREADER_PIO) != 0) { WB_PRINTK_ERROR("wbreader_transfer_data error\n"); goto lab_out; } ret = 0; lab_out: return ret; } static int inline wbms_read_extra_register(void) { if (0 > tpc_set_rw_reg_adrs(0x16, sizeof(wbms_extra_data_register_t), 0x10, 1)) { goto lab_error; } wbreader_set_state(WSTATE_FIFO_SIZE, sizeof(wbms_extra_data_register_t)); if (0 > tpc_read_reg()) goto lab_error; if (wbreader_transfer_data ((unsigned char *)&(g_card_info.ms_register->extra), sizeof(wbms_extra_data_register_t), TRUE, WREADER_PIO)) { goto lab_error; } return 0; lab_error: return -1; } static int inline wbms_write_extra_register(unsigned char overwrite, unsigned char management, unsigned short logical) { int ret; wbms_extra_data_register_t extra; ret = -1; extra.overwrite_flag = overwrite; extra.management_flag = management; extra.logical_address = cpu_to_be16(logical); if (0 > tpc_set_rw_reg_adrs(0, 0x20, 0x16, 0x04)) { goto lab_out; } if (0 > tpc_write_reg((unsigned char *)&extra, sizeof(wbms_extra_data_register_t))) { goto lab_out; } ret = 0; lab_out: return ret; } static int inline wbms_set_rw_param(unsigned char cp, unsigned int block_address, unsigned char page_address) { int ret; wbms_param_register_t param; ret = -1; param.system = 0x80; param.block_address[0] = (u8) (0xFF & (block_address >> 16)); param.block_address[1] = (u8) (0xFF & (block_address >> 8)); param.block_address[2] = (u8) (block_address); param.cp = cp; param.page_address = page_address; if (0 > tpc_set_rw_reg_adrs(0, 0x20, 0x10, 6)) { WB_PRINTK_ERROR("TPC_SET_RW_REG_ADRS Fail\n"); goto lab_out; } if (0 > tpc_write_reg((unsigned char *)¶m, sizeof(param))) { WB_PRINTK_ERROR("TPC_WRITE_REG Fail\n"); goto lab_out; } ret = 0; lab_out: return ret; } static int inline wbms_write_page_extra(unsigned int physical, unsigned char page_index, unsigned char overwrite, unsigned short logical) { int ret; ret = -1; if (logical < 2) goto lab_out; if (wbms_set_rw_param(MS_CP_EXTRA, physical, page_index)) goto lab_out; if (wbms_write_extra_register(0xF8, 0xFF, logical)) goto lab_out; if (tpc_set_cmd(MS_BLOCK_WRITE)) goto lab_out; if (wbms_get_int()) goto lab_out; if (g_card_info.ms_register->status.interrupt != MSREG_INT_CED) goto lab_out; ret = 0; lab_out: return ret; } static int wbms_get_int(void) { unsigned char int_reg; if (tpc_get_int()) goto lab_error; if (wbreader_transfer_data(&int_reg, 1, TRUE, WREADER_PIO)) goto lab_error; if (g_card_info.dev_type == WDEV_MS) g_card_info.ms_register->status.interrupt = int_reg; else if (g_card_info.dev_type == WDEV_MSPRO) g_card_info.mspro_registe->status.interrupt = int_reg; return 0; lab_error: return -1; } static int wbms_write_register(int start_addr, unsigned char *buffer, int length) { if (0 > tpc_set_rw_reg_adrs(0, 16, start_addr, length)) goto lab_error; if (0 > tpc_write_reg(buffer, length)) goto lab_error; return 0; lab_error: return -1; } static int wbms_read_page2buffer(unsigned int block_address, unsigned short page_address) { int ret; ENTER(); ret = -1; WB_PRINTK("block: %X page : %X\n", block_address, page_address); if (wbms_set_rw_param(MS_CP_PAGE, block_address, page_address)) { WB_PRINTK_ERROR("set rw param fail\n"); goto lab_out; } wbreader_set_state(WSTATE_FIFO_SIZE, 0); if (0 > tpc_set_cmd(MS_BLOCK_READ)) { WB_PRINTK_ERROR("set cmd MS_BLOCK_READ fail\n"); goto lab_out; } if (wbms_read_register()) { WB_PRINTK_ERROR("read register fail\n"); goto lab_out; } //check cmd successful if (g_card_info.ms_register->status.interrupt & MSREG_INT_CMDNK) { WB_PRINTK_ERROR("CMDNK\n"); goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_ERR) { if (g_card_info.ms_register->status. status1 & (MSREG_STATUS1_UCDT | MSREG_STATUS1_UCEX | MSREG_STATUS1_UCFG)) { WB_PRINTK_ERROR("abnormal in block: %X page: %X", block_address, page_address); goto lab_out; } } ret = 0; lab_out: LEAVE(); return ret; } static int wbms_read_buffer2mem(char *buffer) { int ret; ENTER(); ret = -1; if (wbreader_prepare_transfer(buffer, MS_PAGE_SIZE, TRUE)) { WB_PRINTK_ERROR("wbreader_prepare_transfer ERROR\n"); goto lab_out; } if (0 > tpc_read_long_data()) { WB_PRINTK_ERROR("tpc_read_long_data ERROR\n"); goto lab_out; } if (wbreader_transfer_data(buffer, MS_PAGE_SIZE, TRUE, WREADER_DMA)) { WB_PRINTK_ERROR("transfer_data ERROR\n"); goto lab_out; } ret = 0; lab_out: LEAVE(); return ret; } static int wbms_read_page(unsigned int block_address, unsigned short page_address, unsigned char *buffer) { int ret; ret = -1; ENTER(); if (wbms_read_page2buffer(block_address, page_address)) { WB_PRINTK_ERROR("page2buffer fail\n"); goto lab_clear_buffer; } //Check page state //if((g_card_info.ms_register->extra.overwrite_flag & MS_OVERWRITE_PAGE_STATUS) != OVERRITE_PAGE_OK // || 0 == (g_card_info.ms_register->extra.management_flag & MS_MANAGEMENT_ACCESS) // || 0 == (g_card_info.ms_register->extra.management_flag & MS_MANAGEMENT_COPY)) { // goto lab_clear_buffer; // } if (0 != wbms_read_buffer2mem(buffer)) { WB_PRINTK_ERROR("buffer2mem fail\n"); goto lab_clear_buffer; } ret = 0; goto lab_out; lab_clear_buffer: WB_PRINTK("MS_CLEAR_BUF\n"); tpc_set_cmd(MS_CLEAR_BUF); lab_out: LEAVE(); return ret; } static int wbms_write_page(unsigned int block_address, unsigned short page_address, char *buffer) { int ret, i; ret = -1; if (0 > wbms_set_rw_param(MS_CP_PAGE, block_address, page_address)) { WB_PRINTK_ERROR("SET_RW_REG_ADRS fail\n"); goto lab_out; } if (wbreader_prepare_transfer(buffer, MS_PAGE_SIZE, FALSE)) { ret = -1; goto lab_out; } wbreader_set_state(WSTATE_FIFO_SIZE, MS_PAGE_SIZE); if (0 > tpc_write_long_data()) { WB_PRINTK_ERROR("TPC_WRITE_LONG_DATA fail\n"); goto lab_out; } if (wbreader_transfer_data(buffer, MS_PAGE_SIZE, FALSE, WREADER_DMA)) { WB_PRINTK_ERROR("transfer_data fail\n"); goto lab_out; } if (0 > tpc_set_cmd(MS_BLOCK_WRITE)) { WB_PRINTK_ERROR("MS_BLOCK_WRITE fail\n"); ret = -2; goto lab_out; } //Check INT for (i = 0; i < WMAX_POLLING; i++) { if (wbms_get_int()) { WB_PRINTK_ERROR("Get_INT fail\n"); goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_ERR) { ret = -2; goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_CED) { break; } } if (WMAX_POLLING == i) { WB_PRINTK_ERROR("Wait CED timeout\n"); goto lab_out; } ret = 0; lab_out: return ret; } static int wbms_read_page_extra(unsigned int block_address, unsigned short page_address) { int ret; ret = -1; if (0 > wbms_set_rw_param(MS_CP_EXTRA, block_address, page_address)) { WB_PRINTK_ERROR("read extra : SET_RW_PARAM fail\n"); goto lab_out; } if (0 > tpc_set_cmd(MS_BLOCK_READ)) { WB_PRINTK_ERROR("read extra : MS_BLOCK_READ fail\n"); goto lab_out; } if (wbms_read_register()) { WB_PRINTK_ERROR("read extra : read_register fail\n"); goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_CMDNK) { WB_PRINTK_ERROR ("read extra : CMDNK status0 : %X status1 : %X\n", g_card_info.ms_register->status.status0, g_card_info.ms_register->status.status1); goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_ERR && (g_card_info.ms_register->status. status1 & (MSREG_STATUS1_UCDT | MSREG_STATUS1_UCEX | MSREG_STATUS1_UCFG))) { WB_PRINTK("read extra : ERR block : %X page : %X\n", block_address, page_address); goto lab_out; } ret = 0; lab_out: return ret; } static int wbms_erase_block(unsigned int block_address) { int ret, i; ret = -1; wbms_set_rw_param(MS_CP_BLOCK, block_address, 0); if (0 > tpc_set_cmd(MS_BLOCK_ERASE)) { goto lab_out; } for (i = 0; i < WMAX_POLLING; i++) { if (wbms_read_register()) { WB_PRINTK("erase : read register fail\n"); goto lab_out; } if (g_card_info.ms_register->status. interrupt & (MSREG_INT_CED | MSREG_INT_CMDNK)) break; } if (i == WMAX_POLLING) { WB_PRINTK("erase : CMD not end\n"); goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_CMDNK) { goto lab_out; } ret = 0; lab_out: return ret; } static int wbms_copy_page(unsigned int src_block, unsigned int src_page, unsigned int dec_block, unsigned int dec_page) { int ret; ret = -1; if (wbms_set_rw_param(MS_CP_PAGE, src_block, src_page)) { WB_PRINTK("copy set src rw param fail\n"); } if (0 > tpc_set_cmd(MS_BLOCK_READ)) { WB_PRINTK("copy block_read fail\n"); goto lab_out; } if (wbms_read_register()) { WB_PRINTK("copy get_int fail\n"); goto lab_out; } if (g_card_info.ms_register->status.interrupt & MSREG_INT_CMDNK) { WB_PRINTK("block_read CMDNK\n"); goto lab_out; } if (wbms_write_extra_register (g_card_info.ms_register->extra.overwrite_flag, g_card_info.ms_register->extra.management_flag, cpu_to_be16(g_card_info.ms_register->extra.logical_address))) { WB_PRINTK("write extra register fail\n"); goto lab_out; } wbms_set_rw_param(MS_CP_PAGE, dec_block, dec_page); if (0 > tpc_set_cmd(MS_BLOCK_WRITE)) { WB_PRINTK("copy block_write fail\n"); goto lab_out; } if (0 > wbms_read_register()) { WB_PRINTK("GET_INT Fail.\n"); goto lab_out; } if (!(g_card_info.ms_register->status.interrupt == MSREG_INT_CED)) { WB_PRINTK("copy : MS_BLOCK_WRITE Fail\n"); goto lab_out; } ret = 0; lab_out: return ret; } static inline int wbms_get_wp(void) { int ret; ret = 1; if (wbms_read_status_register()) goto lab_out; if (g_card_info.dev_type == WDEV_MS) ret = (g_card_info.ms_register->status. status0 & MSREG_STATUS0_WP); else ret = (g_card_info.mspro_registe->status. status & MSPROREG_STATUS_WP); lab_out: return ret; } static int wbms_read_multi_pages(unsigned int start_lba, unsigned int mun_page, unsigned char *buffer) { int ret; unsigned int lba; unsigned short physical_address; unsigned short page_per_block; unsigned short page_address; ret = -1; page_per_block = g_card_info.block_size * (1024 / MS_PAGE_SIZE); for (lba = start_lba; lba < start_lba + mun_page; lba++) { physical_address = wbtable_get_physical(lba / page_per_block); page_address = lba % page_per_block; if (wbms_read_page (physical_address, page_address, buffer + (lba - start_lba) * MS_PAGE_SIZE)) { goto lab_out; } } ret = 0; lab_out: return ret; } static int wbms_reject_block(unsigned int block_address) { return wbms_write_page_extra(block_address, 0, ~OVERWRITE_BLOCK_OK & 0xF8, 0xFF); } static int wbms_write_multi_pages(unsigned int start_lba, unsigned int num_page, unsigned char *buffer) { int ret, i; int err1, err2; unsigned int lba, end_lba; unsigned short page_per_block; unsigned short physical_address, new_physical_address; unsigned short logical_address; unsigned int lba_block_end; ret = -1; end_lba = start_lba + num_page; page_per_block = g_card_info.block_size * (1024 / MS_PAGE_SIZE); for (lba = start_lba; lba < end_lba;) { logical_address = lba / page_per_block; physical_address = wbtable_get_physical(logical_address); lba_block_end = logical_address * page_per_block + page_per_block - 1; new_physical_address = wbtable_get_unused_physical(wbtable_get_seg(lba)); wbms_erase_block(new_physical_address); switch (physical_address) { case WB_TABLE_DEFECT: case WB_TABLE_UNUSED: if (wbms_write_extra_register (0xF8, 0xFF, logical_address)) WB_PRINTK ("new block write extra register fail\n"); for (; lba < end_lba && lba <= lba_block_end; lba++) { if ((err1 = wbms_write_page(new_physical_address, lba % page_per_block, buffer + (lba - start_lba) * MS_PAGE_SIZE))) { if ((err2 = wbms_write_page (new_physical_address, lba % page_per_block, buffer + (lba - start_lba) * MS_PAGE_SIZE))) { if (err1 == -2 && err2 == -2) { wbtable_add_link_physical_logical (new_physical_address, WB_TABLE_DEFECT); wbms_reject_block (new_physical_address); } WB_PRINTK("write page fail\n"); goto lab_out; } } } wbtable_add_link(logical_address, new_physical_address); break; default: for (i = 0; i < page_per_block; i++) { if (i == lba % page_per_block && lba < end_lba) { if (i == 0) { if (wbms_write_extra_register (0xF8, 0xFF, logical_address)) WB_PRINTK ("new block write extra register fail\n"); } if ((err1 = wbms_write_page (new_physical_address, i, buffer + (lba - start_lba) * MS_PAGE_SIZE))) { if ((err2 = wbms_write_page (new_physical_address, i, buffer + (lba - start_lba) * MS_PAGE_SIZE))) { if (err1 == -2 && err2 == -2) { wbtable_add_link_physical_logical (new_physical_address, WB_TABLE_DEFECT); wbms_reject_block (new_physical_address); } WB_PRINTK ("write page fail\n"); goto lab_out; } } lba++; } else { if (wbms_copy_page (physical_address, i, new_physical_address, i)) { WB_PRINTK("copy page fail\n"); goto lab_out; } } } if (wbms_erase_block(physical_address)) { WB_PRINTK("erase physical block fail\n"); goto lab_out; } wbtable_del_link(physical_address, logical_address); wbtable_add_link(logical_address, new_physical_address); break; } } ret = 0; lab_out: return ret; } static int wbmspro_read_multi_pages(unsigned int start_lba, unsigned int num_page, unsigned char *buffer) { int ret, i, j; ret = -1; #if 0 if (0 > tpc_ex_set_cmd(MSPRO_READ_DATA, start_lba, num_page)) { goto lab_out; } #else wbmspro_set_wr_param(start_lba, num_page); g_card_info.mspro_registe->status.interrupt = 0; if (tpc_set_cmd(MSPRO_READ_DATA)) { WB_PRINTK("set_cmd: MSPRO_READ_DATA fail\n"); goto lab_out; } #endif for (i = 0; i < num_page; i++) { g_card_info.mspro_registe->status.interrupt = 0; for (j = 0; j < WMAX_POLLING && !(g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_BREQ); j++) { if (wbms_get_int()) { WB_PRINTK("get int fail\n"); goto lab_out; } } if (wbreader_prepare_transfer (buffer + i * MS_PAGE_SIZE, num_page * MS_PAGE_SIZE, TRUE)) goto lab_out; if (0 > tpc_read_long_data()) goto lab_out; if (wbreader_transfer_data (buffer + i * MS_PAGE_SIZE, MS_PAGE_SIZE, TRUE, WREADER_DMA)) { goto lab_out; } } g_card_info.mspro_registe->status.interrupt = 0; for (j = 0; j < WMAX_POLLING && !(g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_CED); j++) { if (wbms_get_int()) { WB_PRINTK("get int fail\n"); goto lab_out; } } ret = 0; lab_out: return ret; } static int wbmspro_write_multi_pages(unsigned int start_lba, unsigned int num_page, unsigned char *buffer) { int ret, i, j; ret = -1; #if 0 if (0 > tpc_ex_set_cmd(MSPRO_WRITE_DATA, start_lba, num_page)) { goto lab_out; } #else wbmspro_set_wr_param(start_lba, num_page); if (tpc_set_cmd(MSPRO_WRITE_DATA)) { WB_PRINTK_ERROR("set_cmd: MSPRO_WRITE_DATA fail\n"); goto lab_out; } #endif for (i = 0; i < num_page; i++) { g_card_info.mspro_registe->status.interrupt = 0; for (j = 0; j < WMAX_POLLING && !(g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_BREQ); j++) { if (wbms_get_int()) { WB_PRINTK_ERROR("get int fail\n"); goto lab_out; } } if (g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_ERR) { WB_PRINTK_ERROR("INT_ERR DETECTED\n"); goto lab_out; } if (wbreader_prepare_transfer (buffer + i * MS_PAGE_SIZE, num_page * MS_PAGE_SIZE, FALSE)) goto lab_out; if (0 > tpc_write_long_data()) { WB_PRINTK_ERROR("tpc_write_long_data fail\n"); goto lab_out; } wbreader_transfer_data(buffer + i * MS_PAGE_SIZE, MS_PAGE_SIZE, FALSE, WREADER_DMA); } for (j = 0; j < WMAX_POLLING && !(g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_CED); j++) { if (wbms_get_int()) { WB_PRINTK_ERROR("get int fail\n"); goto lab_out; } } if (g_card_info.mspro_registe->status.interrupt & MSPROREG_INT_ERR) { WB_PRINTK_ERROR("INT_CED & INT_ERR DETECTED\n"); goto lab_out; } ret = 0; lab_out: return ret; } static inline void *wbvdev_detect(void) { void *ret; if (NULL == (ret = wbreader_detect())) goto lab_out; lab_out: return ret; } static inline int wbvdev_release(void) { return wbreader_release(); } // the number of total sectors in this virtual device static int wbvdev_get_capacity(void) { return g_card_info.sectors; } static int wbvdev_read_wrt(unsigned int start_lba, unsigned int length, unsigned char *buffer, unsigned char read_flag) { int ret; ret = -1; switch (g_card_info.dev_type) { case WDEV_MS: if (read_flag) { wbms_read_multi_pages(start_lba, length / MS_PAGE_SIZE, buffer); } else { wbms_write_multi_pages(start_lba, length / MS_PAGE_SIZE, buffer); } break; case WDEV_MSPRO: if (read_flag) { wbmspro_read_multi_pages(start_lba, length / MS_PAGE_SIZE, buffer); } else { wbmspro_write_multi_pages(start_lba, length / MS_PAGE_SIZE, buffer); } break; default: goto lab_out; } ret = 0; lab_out: return ret; } static unsigned char wbvdev_get_state(int which_state) { unsigned char state; state = 0; switch (which_state) { case WB_VIRTUAL_STATE_PROTECTED: state = wbms_get_wp(); break; case WB_VIRTUAL_STATE_PRESENT: state = wbreader_get_state(WSTATE_PRESENT); break; case WB_VIRTUAL_STATE_CHANGED: state = wbreader_get_state(WSTATE_CHANGED); break; default: WB_PRINTK_ERROR("Unknow state 0x%x! \n", which_state); } return state; } static void wbvdev_set_state(int which_state, unsigned char state) { switch (which_state) { case WB_VIRTUAL_STATE_PROTECTED: break; case WB_VIRTUAL_STATE_PRESENT: wbreader_set_state(WSTATE_PRESENT, state); break; case WB_VIRTUAL_STATE_CHANGED: wbreader_set_state(WSTATE_CHANGED, state); break; default: WB_PRINTK_ERROR("Unknow state 0x%x! \n", which_state); } } static int wbms_identification_card_type(void) { ENTER(); g_card_info.dev_type = WDEV_UNKNOWN; if (0 != wbms_read_status_register()) { WB_PRINTK("read status register err\n"); goto lab_out; } switch (g_card_info.ms_register->status.type) { case 0: case 0xFF: g_card_info.dev_type = WDEV_MS; break; case 0x01: g_card_info.dev_type = WDEV_MSPRO; default: goto lab_out; } switch (g_card_info.ms_register->status.category) { case 0x00: case 0xFF: break; default: if (g_card_info.ms_register->status.category > 0 && g_card_info.ms_register->status.category < 0x80) { if (g_card_info.ms_register->status.category == 0x10 && g_card_info.dev_type == WDEV_MSPRO) g_card_info.dev_type = WDEV_MSPRO_IO; else g_card_info.dev_type = WDEV_MS_IO; break; } goto lab_out; } lab_out: LEAVE(); return g_card_info.dev_type; } static int wbms_scan_physical_block(void) { int i, ret; unsigned short logical_addr; unsigned short conflict_physical_addr; ret = -1; for (i = 0; i < g_card_info.number_of_physical_block; i++) { if (!wbreader_get_state(WSTATE_PRESENT)) { goto lab_out; } if (wbtable_get_logical(i) == WB_TABLE_DEFECT) continue; wbms_read_page_extra(i, 0); if (OVERWRITE_BLOCK_NG == (g_card_info.ms_register->extra. overwrite_flag & MS_OVERWRITE_BLOCK_STATUS)) { wbtable_add_link_physical_logical(i, WB_TABLE_DEFECT); continue; } if (i > g_card_info.number_of_physical_block - 512) { if (0 == (g_card_info.ms_register->extra. management_flag & MS_MANAGEMENT_TRANS_TABLE)) { if (!wbms_get_wp()) { wbms_erase_block(i); } } } if (WB_TABLE_UNUSED == (logical_addr = be16_to_cpu(g_card_info.ms_register->extra. logical_address))) continue; if (WB_TABLE_UNUSED != (conflict_physical_addr = wbtable_get_physical(logical_addr))) { if (OVERWRITE_USED_UPDATING == (g_card_info.ms_register->extra. overwrite_flag & MS_OVERWRITE_UPDATA_STATUS)) { wbms_erase_block(i); continue; } else { wbms_read_page_extra(conflict_physical_addr, 0); if (OVERWRITE_USED_UPDATING == (g_card_info.ms_register->extra. overwrite_flag & MS_OVERWRITE_UPDATA_STATUS)) { wbms_erase_block (conflict_physical_addr); wbtable_add_link_physical_logical (conflict_physical_addr, WB_TABLE_UNUSED); } else { wbms_erase_block(i); } continue; } } wbtable_add_link(be16_to_cpu (g_card_info.ms_register->extra. logical_address), i); } if (wbtable_sanity_chk()) { goto lab_out; } ret = 0; lab_out: return ret; } static int wbms_parse_boot_block(char *buffer) { unsigned char *boot_block_page1; wbms_boot_block_page0_t *page0; int i, ret; u16 *defect_block; wbms_idi *idi; ENTER(); ret = -1; boot_block_page1 = (unsigned char *)kmalloc(MS_PAGE_SIZE, GFP_KERNEL | GFP_DMA); page0 = (wbms_boot_block_page0_t *) buffer; g_card_info.number_of_physical_block = be16_to_cpu(page0->attribute.number_of_block); g_card_info.block_size = be16_to_cpu(page0->attribute.block_size); wbtable_init(g_card_info.block_size, g_card_info.number_of_physical_block); for (i = 0; i < g_card_info.number_of_boot_block; i++) { wbtable_add_link_physical_logical(g_card_info. boot_block_num[i], WB_TABLE_DEFECT); } if (0 != wbms_read_page(g_card_info.boot_block_num[0], 1, boot_block_page1)) { goto lab_out; } defect_block = (u16 *) (boot_block_page1 + be32_to_cpu(page0->entry.disabled_block.start_addr)); for (i = 0; i < be32_to_cpu(page0->entry.disabled_block.data_size) / 2; i++) { wbtable_add_link_physical_logical(be16_to_cpu(defect_block[i]), WB_TABLE_DEFECT); } if (MS_PAGE_SIZE <= be32_to_cpu(page0->entry.cis_idi.start_addr)) { if (0 != wbms_read_page(g_card_info.boot_block_num[0], 1 + be32_to_cpu(page0->entry.cis_idi. start_addr) / MS_PAGE_SIZE, boot_block_page1)) goto lab_out; } idi = (wbms_idi *) (boot_block_page1 + be32_to_cpu(page0->entry.cis_idi.start_addr) % MS_PAGE_SIZE + MS_IDI_OFFSET); g_card_info.cylinders = idi->logical_cylinders; g_card_info.heads = idi->logical_heads; g_card_info.sectors_per_track = idi->sectors_per_track; g_card_info.sectors = (((u32) idi->msw) << 16) | idi->lsw; g_card_info.sector_size = idi->sector_size; ret = 0; lab_out: kfree(boot_block_page1); LEAVE(); return ret; } static int wbms_process_boot_block(void) { int ret, i; wbms_boot_header_t *header; unsigned char *boot_page[2]; int value; ENTER(); boot_page[0] = kmalloc(MS_PAGE_SIZE, GFP_KERNEL | GFP_DMA); boot_page[1] = kmalloc(MS_PAGE_SIZE, GFP_KERNEL | GFP_DMA); ret = 0; g_card_info.number_of_boot_block = 0; for (i = 0; i < MS_MAX_BOOT_BLOCK_NUM; i++) { if (!wbreader_get_state(WSTATE_PRESENT)) { ret = -1; goto lab_out; } if ((value = wbms_read_page(i, 0, boot_page[g_card_info. number_of_boot_block]))) { WB_PRINTK("read page error\n"); continue; } header = (wbms_boot_header_t *) boot_page[g_card_info. number_of_boot_block]; if (MS_BOOT_BLOCK_ID != be16_to_cpu(header->block_id)) continue; g_card_info.boot_block_num[g_card_info. number_of_boot_block++] = i; if (g_card_info.number_of_boot_block == 2) break; } if (g_card_info.number_of_boot_block == 0) { WB_PRINTK_ERROR("Can not find boot block.\n"); ret = -1; goto lab_out; } if (0 != (ret = wbms_parse_boot_block(boot_page[0]))) ret = wbms_parse_boot_block(boot_page [g_card_info. number_of_boot_block - 1]); lab_out: kfree(boot_page[0]); kfree(boot_page[1]); LEAVE(); return ret; } static int wbmspro_change_transfer_mode(unsigned int mode) { unsigned char value; int ret; ret = -1; if (WIF_PARALLEL == mode) { value = 0; } else { value = MSPROREG_SYSTEM_SRAC; } if (0 > wbms_write_register(0x10, &value, 1)) goto lab_end; wbreader_set_config(WCONFIG_INTERFACE, mode); ret = 0; lab_end: return ret; } static int wbms_clear_preboot(void) { int ret, i, j; unsigned short boot_block_num; unsigned short new_physical; unsigned short logical_addrass; ret = -1; boot_block_num = g_card_info.boot_block_num[g_card_info.number_of_boot_block - 1]; for (i = 0; i < boot_block_num; i++) { logical_addrass = wbtable_get_logical(i); if ((WB_TABLE_DEFECT == logical_addrass) || (WB_TABLE_UNUSED == logical_addrass)) { continue; } new_physical = wbtable_get_unused_physical(0); if (wbms_erase_block(new_physical)) goto lab_out; for (j = 0; j < g_card_info.block_size * 1024 / MS_PAGE_SIZE; j++) { wbms_copy_page(i, j, new_physical, j); } if (wbms_erase_block(i)) goto lab_out; wbtable_del_link(i, logical_addrass); wbtable_add_link(logical_addrass, new_physical); wbms_write_page_extra(i, 0, 0x78, 0xFFFF); } for (i = 0; i < g_card_info.boot_block_num[g_card_info. number_of_boot_block - 1]; i++) { wbtable_add_link_physical_logical(i, WB_TABLE_DEFECT); } ret = 0; lab_out: return ret; } static int wbms_init_card(void) { int ret; ENTER(); ret = -1; g_card_info.ms_register = (wbms_register_t *) g_card_info.registers; #if 0 if (g_card_info.transfer_mode == WIF_PARALLEL) { wbms_change_transfer_mode(WIF_PARALLEL); } #endif if (wbms_process_boot_block()) { goto lab_out; } if (wbms_scan_physical_block()) { goto lab_out; } if (!wbms_get_wp()) { if (wbms_clear_preboot()) { WB_PRINTK ("Fail to move logical boot befor boot block\n"); } } ret = 0; lab_out: LEAVE(); return ret; } static int wbmspro_set_wr_param(unsigned int lba, unsigned short count) { int ret; unsigned char param[6]; ret = -1; param[0] = 0xFF & (count >> 8); param[1] = 0xFF & count; param[2] = 0xFF & (lba >> 24); param[3] = 0xFF & (lba >> 16); param[4] = 0xFF & (lba >> 8); param[5] = 0xFF & lba; if (0 > tpc_set_rw_reg_adrs(0, 0x08, 0x11, 6)) goto lab_out; if (0 > tpc_write_reg(param, 6)) goto lab_out; ret = 0; lab_out: return ret; } static int wbmspro_process_attribute(void) { int ret, i; char page[512]; unsigned int sys_info_address, dev_info_address; wbmspro_attribute_t *attribute; wbmspro_device_info_entry_t *info_entry; wbmspro_sys_info_t *sys_info; wbmspro_identify_dev_info_t *dev_info; sys_info_address = 0; dev_info_address = 0; ret = -1; if (wbmspro_read_attribute(0, 1, page)) { WB_PRINTK_ERROR("PRO READ ATTRIBUTE FAIL\n"); goto lab_out; } attribute = (wbmspro_attribute_t *) page; info_entry = (wbmspro_device_info_entry_t *) (page + 16); if (0xA5C3 != be16_to_cpu(attribute->signature)) { WB_PRINTK_ERROR ("Unknow MSPro attribute infromation signature : %X\n", be16_to_cpu(attribute->signature)); } for (i = 0; i < attribute->device_information_entry_count; i++) { if (info_entry->entry[i].info_id == MSPRO_DEVINFOID_SYSINFO) { sys_info_address = be32_to_cpu(info_entry->entry[i].address); break; } } for (i = 0; i < attribute->device_information_entry_count; i++) { if (info_entry->entry[i].info_id == MSPRO_DEVINFOID_IDENTIFYDEVINFO) { dev_info_address = be32_to_cpu(info_entry->entry[i].address); break; } } if (sys_info_address / MS_PAGE_SIZE) { wbmspro_read_attribute(sys_info_address / MS_PAGE_SIZE, 1, page); } sys_info = (wbmspro_sys_info_t *) (page + sys_info_address % MS_PAGE_SIZE); g_card_info.block_size = be16_to_cpu(sys_info->block_size); g_card_info.number_of_physical_block = be16_to_cpu(sys_info->block_count); g_card_info.sectors = be16_to_cpu(sys_info->user_block_count) * g_card_info.block_size; if (dev_info_address / MS_PAGE_SIZE != sys_info_address / MS_PAGE_SIZE) { wbmspro_read_attribute(dev_info_address / MS_PAGE_SIZE, 1, page); } dev_info = (wbmspro_identify_dev_info_t *) (page + dev_info_address % MS_PAGE_SIZE); g_card_info.cylinders = be16_to_cpu(dev_info->cylinders); g_card_info.heads = be16_to_cpu(dev_info->heads); g_card_info.sectors_per_track = be16_to_cpu(dev_info->sectors_per_track); g_card_info.sector_size = be16_to_cpu(dev_info->byte_per_sector); ret = 0; lab_out: return ret; } static int wbmspro_read_attribute(unsigned int page_address, unsigned int num_page, unsigned char *buffer) { int i, ret; ret = -1; g_card_info.mspro_registe->status.interrupt = 0; #if 1 wbmspro_set_wr_param(page_address, num_page); g_card_info.mspro_registe->status.interrupt = 0; if (tpc_set_cmd(MSPRO_READ_ATRB)) { WB_PRINTK("set_cmd: read_atrb fail\n"); goto lab_out; } #else if (0 > tpc_ex_set_cmd(MSPRO_READ_ATRB, page_address, num_page)) { WB_PRINTK("ex set cmd fail\n"); //goto lab_out; } #endif for (i = 0; i < WMAX_POLLING && !(g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_BREQ); i++) { if (wbms_get_int()) { WB_PRINTK("get int fail\n"); goto lab_out; } } for (i = 0; i < num_page; i++) { if (wbreader_prepare_transfer (buffer + i * MS_PAGE_SIZE, MS_PAGE_SIZE, TRUE)) { goto lab_out; } if (0 > tpc_read_long_data()) { WB_PRINTK("read long data fail\n"); goto lab_out; } if (wbreader_transfer_data (buffer + i * MS_PAGE_SIZE, MS_PAGE_SIZE, TRUE, WREADER_DMA)) { WB_PRINTK("Pro transfer fail\n"); goto lab_out; } } for (i = 0; i < WMAX_POLLING && !(g_card_info.mspro_registe->status. interrupt & MSPROREG_INT_CED); i++) { if (wbms_get_int()) { WB_PRINTK("get int fail\n"); goto lab_out; } } ret = 0; lab_out: return ret; } static int wbmspro_init_card(void) { int ret, i; ret = -1; g_card_info.mspro_registe = (wbmspro_register_t *) g_card_info.registers; if (g_card_info.transfer_mode == WIF_PARALLEL) { wbmspro_change_transfer_mode(WIF_PARALLEL); } //check cpu power up for (i = 0, g_card_info.mspro_registe->status.interrupt = 0; i < WMAX_POLLING && (g_card_info.mspro_registe->status.interrupt == 0); i++) { if (wbms_get_int()) { WB_PRINTK("get int fail\n"); goto lab_out; } } if (wbms_get_int()) { WB_PRINTK("get int fail\n"); goto lab_out; } if (MSREG_INT_ERR & g_card_info.mspro_registe->status.interrupt) { if (MSREG_INT_CMDNK & g_card_info.mspro_registe->status. interrupt) { g_card_info.rw_capality = WRW_READ; } else { goto lab_out; } } if (wbmspro_process_attribute()) goto lab_out; ret = 0; lab_out: return ret; } static int wbvdev_init_card(void) { int ret; ret = -1; ENTER(); memset(&g_card_info, 0, sizeof(g_card_info)); g_card_info.ms_register = (wbms_register_t *) g_card_info.registers; g_card_info.transfer_mode = wbreader_get_state(WSTATE_TRANSFER_CAPABILITY); if (wbreader_set_config(WCONFIG_PREPARE_INIT_CARD, 0) != 0) { goto lab_out; } mdelay(10); wbms_identification_card_type(); switch (g_card_info.dev_type) { case WDEV_MS: wbreader_set_state(WSTATE_CARD_TYPE, WDEV_MS); if (0 != wbms_init_card()) goto lab_out; break; case WDEV_MSPRO: wbreader_set_state(WSTATE_CARD_TYPE, WDEV_MSPRO); if (0 != wbmspro_init_card()) goto lab_out; break; default: WB_PRINTK_ERROR("Unknow card type\n"); goto lab_out; } ret = 0; lab_out: LEAVE(); return ret; } #include "wbscsi.c" #include "wbtable.c"