* Re: [Winbond] flash memory reader SCSI device drivers [headers]
2006-06-15 19:02 ` Pierre Ossman
@ 2006-06-16 1:36 ` dezheng shen
2006-06-16 2:32 ` Randy.Dunlap
` (2 more replies)
2006-06-16 1:38 ` [Winbond] flash memory reader SCSI device drivers [C sources] dezheng shen
1 sibling, 3 replies; 12+ messages in thread
From: dezheng shen @ 2006-06-16 1:36 UTC (permalink / raw)
To: Pierre Ossman; +Cc: Jesper Juhl, Linux Kernel Mailing List, PI14 SJIN
Hi Pierre:
> People are quite used to reading diffs, so it's not that much of a bothe
I made a typo last time. we should say, we are NOT submitting patches;
instead, we are submitting original sources for Winbond 518/528/528DA
MS/MSPRO/xD/SM/SD/MMC sources.
>
>
>> Can we pack our sources then send them in one large attachment?
> Uncompressed attachments, or inline inclusions, are usually preferred as
> it allows you to read the stuff in your mail application.
I think we will do the inline inclusion for it's easier for everybody to
read.
>
> I have one reservation with your driver though. The Linux kernel already
> has a generic SD/MMC layer. So if your hardware is a bus interface, then
> it should use that layer. There is even a driver there for your
> W83L518/9 devices. :)
Hmm, we might take this suggestion into consideration later. Right
now, we would like to keep our current architecture for internal
maintenance issues. The first thing we would like to do now is
contributing our 518/528/528DA MS/MSPRO/xD/SM/SD/MMC and smart card
drivers into Linux kernel source tree. There are, of course, many things
can be improved in our codes. But at least we have run stree testing on
many platforms from Intel 32/64 AMD 32/64 and single/dual
configurations. We would like to contribute to kernel first then we can
still update/modify/improve them later. Is that OK?
>
> For the other buses, a generic layer would be preferable, but as you
> would be the only user, that isn't required quite yet. I would suggest
> contacting Andrew Morton as he is the overall 2.6 maintainer.
yes, and his email? thank you.
> PS. I have a question regarding the W83L518 hardware, but haven't been
> able to get in touch with the right people. Perhaps you have some pointers?
anything about Winbond 518/528/528DA drivers, send email to
dzshen@winbond.com which is ME.....
dz
ps. did you get my email from linux-kernel? It seems like my emails to
linux-kernel with my attachments are gone for no reason. Will that be my
email size is over 100k?
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbdebug.h,v 1.5.2.2 2006/06/14 07:17:06 dzshen 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
*/
#ifndef _WB_DEBUG_H_
#define _WB_DEBUG_H_
#include <asm/io.h>
#include <linux/delay.h>
#include <linux/version.h>
//enable/disable macro for debugging output
//#define WB_DEBUG
//#define WB_DEBUG_SCSI
//#define WB_DEBUG_INTR
//#define WB_DEBUG_THREAD
#define TRUE 1
#define FALSE 0
#define WB_VENDOR_ID 0x1050
#define WB_DRV_NAME "wbstorage"
#define WB_DEBUG_HDR WB_DRV_NAME ":" WB_STRINGIFY(__FILE__)
":"WB_STRINGIFY(__LINE__) ":"
#define WB_STRINGIFY_HELPER(n) #n
#define WB_STRINGIFY(n) WB_STRINGIFY_HELPER(n)
#ifdef WB_DEBUG
#define ENTER() printk( WB_DRV_NAME ":" "ENTERING %s\n", __FUNCTION__ );
#define LEAVE() printk( WB_DRV_NAME ":" "LEAVING %s\n",__FUNCTION__ );
#define WB_PRINTK_NEW_LINE() printk("\n")
#define WB_PRINTK(...) printk( __VA_ARGS__ )
#define WB_PRINTK_HDR(...) printk( WB_DEBUG_HDR \
__VA_ARGS__ )
#define WB_PRINTK_ERROR(...) printk( WB_DEBUG_HDR \
"ERROR: " __VA_ARGS__ )
#define WB_PRINTK_SCSI(...) printk( WB_DEBUG_HDR \
"SCSI command: " __VA_ARGS__ )
#define WB_PRINTK_ERROR_SCSI(...) printk( WB_DEBUG_HDR \
"ERROR:SCSI command: " __VA_ARGS__ )
#define WB_PRINTK_INTR(...) printk( WB_DEBUG_HDR \
"INTR: " __VA_ARGS__ )
#define WB_PRINTK_ERROR_INTR(...) printk( WB_DEBUG_HDR \
"ERROR:INTR: " __VA_ARGS__ )
#define WB_PRINTK_THREAD(...) printk( WB_DEBUG_HDR \
"THREAD: " __VA_ARGS__ )
#else
#define ENTER()
#define LEAVE()
#define WB_PRINTK_NEW_LINE()
#define WB_PRINTK(...)
#define WB_PRINTK_HDR(...)
#define WB_PRINTK_ERROR(...) printk( WB_DEBUG_HDR \
"ERROR: " __VA_ARGS__ )
#if defined WB_DEBUG_SCSI
#define WB_PRINTK_SCSI(...) printk( WB_DEBUG_HDR \
"SCSI: " __VA_ARGS__ )
#else
#define WB_PRINTK_SCSI(...)
#endif
#define WB_PRINTK_ERROR_SCSI(...) printk( WB_DEBUG_HDR \
"ERROR:SCSI command: "
__VA_ARGS__ )
#if defined WB_DEBUG_INTR
#define WB_PRINTK_INTR(...) printk( WB_DEBUG_HDR \
"INTR: " __VA_ARGS__ )
#else
#define WB_PRINTK_INTR(...)
#endif
#define WB_PRINTK_ERROR_INTR(...) printk( WB_DEBUG_HDR \
"ERROR:INTR: " __VA_ARGS__ )
#if defined WB_DEBUG_THREAD
#define WB_PRINTK_THREAD(...) printk( WB_DEBUG_HDR \
"THREAD: " __VA_ARGS__ )
#else
#define WB_PRINTK_THREAD(...)
#endif
#endif
#endif /* _WB_DEBUG_H_ */
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbmscard.h,v 1.8.2.3 2006/06/14 07:20:28 dzshen 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
*/
#ifndef _WB_MS_CARD_H_
#define _WB_MS_CARD_H_
#include <asm/byteorder.h>
#include <linux/types.h>
#include "wbvirtualdev.h"
#include "wbmsreader.h"
#include "wbtable.h"
#define MS_PAGE_SIZE (512)
#define MS_MAX_BOOT_BLOCK_NUM (12)
//---------------------card type--------------------------
#define WDEV_UNKNOWN (0x00)
#define WDEV_MS (0x01)
#define WDEV_MSPRO (0x02)
#define WDEV_MS_IO (0x03)
#define WDEV_MSPRO_IO (0x04)
#define WRW_READ (0x01)
#define WRW_WRITE (0x02)
//---------------------MS/MSPro Command------------------
#define TPC_CODE(tpc) (((u8)(((u8)(tpc))<<4)) |(~((u8)(tpc)) & 0x0F))
#define TPC_READ_LONG_DATA TPC_CODE(0x2) //TPC: 0010
#define TPC_READ_SHORT_DATA TPC_CODE(0x3) //TPC: 0011
#define TPC_READ_REG TPC_CODE(0x4) //TPC: 0100
#define TPC_GET_INT TPC_CODE(0x7) //TPC: 0111
#define TPC_WRITE_LONG_DATA TPC_CODE(0xD) //TPC: 1101
#define TPC_WRITE_SHORT_DATA TPC_CODE(0xC) //TPC: 1100
#define TPC_WRITE_REG TPC_CODE(0xB) //TPC: 1011
#define TPC_SET_RW_REG_ADRS TPC_CODE(0x8) //TPC: 1000
#define TPC_SET_CMD TPC_CODE(0xE) //TPC: 1110
#define TPC_EX_SET_CMD TPC_CODE(0x9) //TPC: 1001
//MS Command Parameter
#define MS_CP_BLOCK (0)
#define MS_CP_PAGE (0x20)
#define MS_CP_EXTRA (0x40)
#define MS_CP_OVERWRITE (0x80)
//MS Memory Access Command
#define MS_BLOCK_READ (0xAA)
#define MS_BLOCK_WRITE (0x55)
#define MS_BLOCK_END (0x33)
#define MS_BLOCK_ERASE (0x99)
#define MS_FLASH_STOP (0xCC)
//MS Function Command
#define MS_SLEEP (0x5A)
#define MS_CLEAR_BUF (0xC3)
#define MS_RESET (0x3C)
//MSPro Memory Access Command
#define MSPRO_READ_DATA (0x20)
#define MSPRO_WRITE_DATA (0x21)
#define MSPRO_READ_ATRB (0x24)
#define MSPRO_STOP (0x25)
#define MSPRO_ERASE (0x26)
#define MSPRO_SET_IBD (0x46)
#define MSPRO_GET_IBA (0x47)
//MSPro Function Command
#define MSPRO_FORMAT (0x10)
#define MSPRO_SLEEP (0x11)
//MS register
#define MSREG_INT_CMDNK (0x01)
#define MSREG_INT_BREQ (0x20)
#define MSREG_INT_ERR (0x40)
#define MSREG_INT_CED (0x80)
#define MSREG_STATUS0_WP (0x01)
#define MSREG_STATUS0_SL (0x02)
#define MSREG_STATUS0_BF (0x10)
#define MSREG_STATUS0_BE (0x20)
#define MSREG_STATUS0_FB0 (0x40)
#define MSREG_STATUS0_MB (0x80)
#define MSREG_STATUS1_UCFG (0x01)
#define MSREG_STATUS1_FGER (0x02)
#define MSREG_STATUS1_UCEX (0x04)
#define MSREG_STATUS1_EXER (0x08)
#define MSREG_STATUS1_UCDT (0x10)
#define MSREG_STATUS1_DTER (0x20)
#define MSREG_STATUS1_FBI (0x40)
#define MSREG_STATUS1_MB (0x80)
#define MSREG_SYSTEM_PAM (0x08)
//MS page Extra Data Area
// Overwrite Flag
#define MS_OVERWRITE_BLOCK_STATUS (0x80)
#define OVERWRITE_BLOCK_OK (0x80)
#define OVERWRITE_BLOCK_NG (0x00)
#define MS_OVERWRITE_PAGE_STATUS (0x60)
#define OVERRITE_PAGE_OK (0x60)
#define OVERRITE_PAGE_NG (0x40)
#define OVERRITE_PAGE_UDEF (0x20)
#define OVERRITE_PAGE_ERROR (0x00)
#define OVERWRITE_USED_UPDATING (0x00)
#define OVERWRITE_ERASED_WRITTEN (0x10)
#define MS_OVERWRITE_UPDATA_STATUS (0x10)
#define MS_OVERWRITE_FORMAT_RESERVED (0x0F)
//Management Flag
#define MS_MANAGEMENT_ACCESS (0x20)
#define MS_MANAGEMENT_COPY (0x10)
#define MS_MANAGEMENT_TRANS_TABLE (0x08)
#define MS_MANAGEMENT_SYSTEM (0x04)
#define MSPROREG_INT_CED (0x80)
#define MSPROREG_INT_ERR (0x40)
#define MSPROREG_INT_BREQ (0x20)
#define MSPROREG_INT_CMDNK (0x01)
#define MSPROREG_STATUS_ES (0x80)
#define MSPROREG_STATUS_SL (0x02)
#define MSPROREG_STATUS_WP (0x01)
#define MSPROREG_SYSTEM_SRAC (0x80)
typedef struct tag_wbms_extra_data {
u8 overwrite_flag;
u8 management_flag;
u16 physical_address;
u8 reserved[5];
u8 extra_ecc;
u8 data_area_ecc;
} __attribute__ ((packed)) wbms_extra_data_t;
#define MS_BOOT_BLOCK_ID (0x0001)
//Header
typedef struct tag_wbms_boot_header {
u16 block_id;
u16 format_reserved;
u8 reserved0[184];
u8 data_entry;
u8 reserved1[179];
} __attribute__ ((packed)) wbms_boot_header_t;
//System Entry
typedef struct tag_wbms_system_entry_item {
u32 start_addr;
u32 data_size;
u8 data_type_id;
u8 reserved[3];
} __attribute__ ((packed)) wbms_system_entry_item_t;
typedef struct tag_wbms_system_entry {
wbms_system_entry_item_t disabled_block;
wbms_system_entry_item_t cis_idi;
u8 reserved[24];
} __attribute__ ((packed)) wbms_system_entry_t;
//Boot & Attribute Information
typedef struct tag_wbms_boot_attribute_information {
u8 memorystick_class;
u8 format_unique_value1;
u16 block_size;
u16 number_of_block;
u16 number_of_effective_block;
u16 page_size;
u8 extr_data_size;
u8 format_unique_value2;
u64 assembly_time;
u8 format_unique_value3;
u8 serial_number[3];
u8 assembly_manufacturer_code;
u8 assembly_model_code[3];
u16 memory_mamufacturer_code;
u16 memory_device_code;
u16 implemented_capacity;
u8 format_unique_value4[2];
u8 vcc;
u8 vpp;
u16 controller_number;
u16 controller_function;
u8 reserved1[9];
u8 transfer_supporting;
u16 format_unique_value5;
u8 format_type;
u8 memorystick_application;
u8 device_type;
u8 reserved2[22];
u8 format_uniqure_value6[2];
u8 reserved3[15];
} __attribute__ ((packed)) wbms_boot_attribute_information_t;
typedef struct tag_wbms_boot_block_page0 {
wbms_boot_header_t header;
wbms_system_entry_t entry;
wbms_boot_attribute_information_t attribute;
} __attribute__ ((packed)) wbms_boot_block_page0_t;
typedef struct tag_wbms_idi {
u16 general_config;
u16 logical_cylinders;
u16 reserved;
u16 logical_heads;
u16 track_size;
u16 sector_size;
u16 sectors_per_track;
u16 msw;
u16 lsw;
u16 reserved0;
u8 serial_number[20];
u16 buffer_type;
u16 buffer_size_increments;
u16 long_command_ecc;
u8 firmware_version;
u8 model_name;
u16 reserved1;
u16 pio_mode_number;
u16 dma_mode_number;
u16 field_validity;
u16 current_logical_cylinders;
u16 current_logical_heads;
u16 current_sectors_per_track;
u32 current_sector_capality;
u16 mutiple_sector_setting;
u32 addressable_sectors;
u16 single_word_dma;
u16 multi_word_dma;
u8 reserved2[128];
} __attribute__ ((packed)) wbms_idi;
//----------------Card Register----------------
//MS register
typedef struct tag_wbms_status_register {
u8 reserved1;
u8 interrupt;
u8 status0;
u8 status1;
u8 type;
u8 reserved2;
u8 category;
u8 class;
u8 reserved3[8];
} __attribute__ ((packed)) wbms_status_register_t;
typedef struct tag_wbms_param_register {
u8 system;
u8 block_address[3];
u8 cp;
u8 page_address;
} __attribute__ ((packed)) wbms_param_register_t;
typedef struct tag_wbms_extra_data_register {
u8 overwrite_flag;
u8 management_flag;
u16 logical_address;
} __attribute__ ((packed)) wbms_extra_data_register_t;
typedef struct tag_wbms_register {
wbms_status_register_t status;
wbms_param_register_t params;
wbms_extra_data_register_t extra;
} __attribute__ ((packed)) wbms_register_t;
//MSPro register
typedef struct tag_wbmspro_status_register {
u8 reserved1;
u8 interrupt;
u8 status;
u8 reserved2;
u8 type;
u8 reserved3;
u8 category;
u8 class;
u8 reserved4[8];
} __attribute__ ((packed)) wbmspro_status_register_t;
typedef struct tag_wbmspro_param_register {
u8 system;
u16 data_count;
u32 data_address;
u8 cmd_param;
} __attribute__ ((packed)) wbmspro_param_register_t;
typedef struct tag_wbmspro_register {
wbmspro_status_register_t status;
wbmspro_param_register_t params;
} __attribute__ ((packed)) wbmspro_register_t;
typedef struct tag_wbmspro_attribute {
u16 signature;
u16 version;
u8 device_information_entry_count;
u8 reserved[11];
} __attribute__ ((packed)) wbmspro_attribute_t;
#define MSPRO_DEVINFOID_SYSINFO (0x10)
#define MSPRO_DEVINFOID_MODELNAME (0x15)
#define MSPRO_DEVINFOID_MBR (0x20)
#define MSPRO_DEVINFOID_PBR16 (0x21)
#define MSPRO_DEVINFOID_PBR32 (0x22)
#define MSPRO_DEVINFOID_SPECFILEVALUES1 (0x25)
#define MSPRO_DEVINFOID_SPECFILEVALUES2 (0x26)
#define MSPRO_DEVINFOID_IDENTIFYDEVINFO (0x30)
typedef struct tag_wbmspro_device_info_entry_item {
u32 address;
u32 size;
u8 info_id;
u8 reserved[3];
} __attribute__ ((packed)) wbmspro_device_info_entry_item_t;
typedef struct tag_wbmspro_device_info_entry {
wbmspro_device_info_entry_item_t entry[12];
u8 reserved[256];
} __attribute__ ((packed)) wbmspro_device_info_entry_t;
typedef struct tag_wbmspro_sys_info {
u8 class; // must be ?
u8 reserved; // see below
u16 block_size; // n KB
u16 block_count; // number of physical block
u16 user_block_count; // number of logical block
u16 page_size; // must be 0x200
u8 reserved1[2]; // MS original Extra data size and format reserved
u8 assembly_date[8];
u32 serial_number;
u8 assembly_maker_code;
u8 assembly_model_code[3];
u16 memory_maker_code;
u16 memory_model_code;
u8 reserved2[4]; //reserved[6]
u8 vcc;
u8 vpp;
u16 controller_number;
u16 controller_function;
u16 start_sector;
u16 unit_size;
u8 ms_sub_class;
u8 reserved3[4];
u8 interface_type;
u16 controller_code;
u8 format_type;
u8 reserved4;;
u8 device_type;
u8 reserved5[7];
u8 mspro_id[16];
u8 reserved6[16];
} __attribute__ ((packed)) wbmspro_sys_info_t;
typedef struct tag_wbmspro_mbr {
u8 default_boot_partition;
u8 start_head;
u8 start_cylinder;
u8 partition_type;
u8 end_head;
u8 end_sector;
u8 end_cylinder;
u32 start_sectors;
u32 sectors_per_partition;
} __attribute__ ((packed)) wbmspro_mbr_t;
#define MS_IDI_OFFSET (256)
typedef struct tab_wbmspro_identify_dev_info {
u16 cylinders;
u16 heads;
u16 byte_per_track;
u16 byte_per_sector;
u16 sectors_per_track;
u8 reserved[6];
} __attribute__ ((packed)) wbmspro_identify_dev_info_t;
//Global variable
typedef struct tag_wbms_card_info {
wbms_register_t *ms_register;
wbmspro_register_t *mspro_registe;
unsigned char registers[0x20];
unsigned char dev_type;
unsigned char rw_capality;
unsigned char transfer_mode;
unsigned int block_size;
unsigned int page_pre_block;
unsigned int number_of_physical_block;
unsigned int number_of_boot_block;
unsigned int boot_block_num[2];
unsigned short cylinders;
unsigned short heads;
unsigned int sectors;
unsigned short sectors_per_track;
unsigned int sector_size;
} __attribute__ ((packed)) wbms_card_info_t;
//TPC layer
static inline int wbms_send_tpc(unsigned char cmd, unsigned char *buffer,
int length);
static inline int tpc_read_long_data(void);
static inline int tpc_read_short_data(void);
static inline int tpc_read_reg(void);
static inline int tpc_get_int(void);
static inline int tpc_write_long_data(void);
static inline int tpc_write_short_data(void);
static inline int tpc_write_reg(unsigned char *buffer, int len);
static inline int tpc_set_cmd(unsigned char flash_cmd);
static inline int tpc_ex_set_cmd(unsigned char flash_cmd,
unsigned int block_addr, unsigned short size);
static inline int tpc_set_rw_reg_adrs(unsigned char read_adrs,
unsigned char read_size,
unsigned char write_adrs,
unsigned char write_size);
//MS interface layer
static int wbmspro_read_attribute(unsigned int page_address,
unsigned int num_page, unsigned char *buffer);
static int wbms_read_status_register(void);
static int wbms_write_register(int start_addr, unsigned char *buffer,
int length);
static int inline wbms_read_extra_register(void);
static int wbms_get_int(void);
static int inline wbms_set_rw_param(unsigned char cp,
unsigned int block_address,
unsigned char page_address);
static int wbms_read_page_extra(unsigned int block_address,
unsigned short page_address);
static int inline wbms_write_page_extra(unsigned int physical,
unsigned char page_index,
unsigned char overwrite,
unsigned short logical);
static int wbms_identification_card_type(void);
//MSPro function interface
static int wbmspro_set_wr_param(unsigned int lba, unsigned short count);
#endif /* _WB_MS_CARD_H_ */
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: sjin $
$Id: wbmsreader528.h,v 1.10.2.4 2006/06/14 07:35:46 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
*/
#ifndef _WB_MS_READER_528_H
#define _WB_MS_READER_528_H
#include <linux/version.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/gfp.h>
#include "wbmsreader.h"
#include <linux/mm.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/ctype.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <asm/io.h>
#include <asm/dma.h>
#include "wbdebug.h"
#include "wbmsreader.h"
#include "wbmscard.h"
#define WMAX_POLLING_CMD (1000000)
#define WMAX_POLLING (1000000)
#define W528MS_VENDOR_ID (0x1050)
#define W528MS_DEVICE_ID (0x8482)
#define WPCI_ICR (0x9EC)
#define WPCI_ISR (0x9F0)
#define WIO_DEVICE_ISR (0x410)
#define WIO_DMA_SD (0x4f0)
#define WIO_DMA_MS (0x4f4)
#define WIO_DMA_SX (0x4f8)
#define WIO_DMA_INT_MSK (0x04)
#define WIO_DMA_INT_SRC (0x0C)
#define WIO_DMA_CH0_CSR (0x20)
#define WIO_DMA_CH0_SZ (0x24)
#define WIO_DMA_CH0_SA (0x28)
#define WIO_DMA_CH0_DA (0x30)
#define WIO_DMA_CH1_CSR (0x40)
#define WIO_DMA_CH1_SZ (0x44)
#define WIO_DMA_CH1_SA (0x48)
#define WIO_DMA_CH1_DA (0x50)
#define WIO_DMA_CH2_CSR (0x60)
#define WIO_DMA_CH2_SZ (0x64)
#define WIO_DMA_CH2_SA (0x68)
#define WIO_DMA_CH2_DA (0x70)
#define WIO_BASE (0x600)
#define WIO_CMDR (WIO_BASE + 0x00)
#define WIO_DFR (WIO_BASE + 0x04)
#define WIO_MSCR1 (WIO_BASE + 0x08)
#define WIO_MSCR2 (WIO_BASE + 0x0C)
#define WIO_ICR (WIO_BASE + 0x10)
#define WIO_ISR1 (WIO_BASE + 0x14)
#define WIO_ISR2 (WIO_BASE + 0x18)
#define WIO_MSSR (WIO_BASE + 0x1C)
#define WPCI_ICR_INT_ENABLE (0x01)
#define WPCI_ICR_SOFTWARE_RESET_ALL (0x20)
#define WPCI_ICR_SOFTWARE_RESET (0x40)
#define WPCI_ISR_STATUS (0x01)
#define WDEVICE_ISR_DMA (0x01)
#define WDEVICE_ISR_SD (0x02)
#define WDEVICE_ISR_MS (0x04)
#define WDEVICE_ISR_SMXD (0x08)
#define WDEVICE_ISR_SDDMA_ERROR (0x10)
#define WDEVICE_ISR_MSDMA_ERROR (0x20)
#define WDEVICE_ISR_SMXDDMA_ERROR (0x40)
#define WMSCR2_PF (0x40)
#define WMSCR2_FIFORST (0x20)
#define WMSCR2_SOFTRST (0x10)
#define WMSCR2_PWRCTRL (0x08)
#define WMSCR2_SWITCHNMOS (0x04)
#define WICR_CRC (0x40)
#define WICR_RDYTO (0x20)
#define WICR_BS0INT (0x10)
#define WICR_INSINT (0x01)
#define WISR1_INTP (0x80)
#define WISR1_FIFOUSED (0x78)
#define WISR1_INS (0x01)
#define WISR2_INTP (0x80)
#define WISR2_CRC (0x40)
#define WISR2_RDYTO (0x20)
#define WISR2_BS0 (0x10)
#define WISR2_CED (0x08)
#define WISR2_ERR (0x04)
#define WISR2_BREQ (0x02)
#define WISR2_CMDNK (0x01)
#define WMSSR_CRC (0x10)
#define WMSSR_INS (0x08)
#define WMSSR_DFF (0x04)
#define WMSSR_DFE (0x02)
#define WMSSR_RDY4CMD (0x01)
#define WDMA_INT_SRC_CH0 (0x01)
#define WDMA_INT_SRC_CH1 (0x02)
#define WDMA_INT_SRC_CH2 (0x04)
#define WDMA_CHCSR_EN (0x01)
#define WDMA_CHCSR_DSTSEL (0x02)
#define WDMA_CHCSR_SRCSEL (0x04)
#define WDMA_CHCSR_INCDST (0x08)
#define WDMA_CHCSR_INCSRC (0x10)
#define WDMA_CHCSR_INTERR (0x20000)
#define WDMA_CHCSR_INTDONE (0x40000)
#define WDMA_CHCSR_WRITE ( WDMA_CHCSR_EN | WDMA_CHCSR_DSTSEL \
| WDMA_CHCSR_INCSRC | WDMA_CHCSR_INTERR | WDMA_CHCSR_INTDONE )
#define WDMA_CHCSR_READ ( WDMA_CHCSR_EN | WDMA_CHCSR_SRCSEL \
| WDMA_CHCSR_INCDST | WDMA_CHCSR_INTERR | WDMA_CHCSR_INTDONE )
//system status definition
#define WSYS_STATUS_INTR_CARD (1)
#define WSYS_STATUS_INTR_CRC (1<<1)
#define WSYS_STATUS_INTR_BS0 (1<<2)
#define WSYS_STATUS_INTR_BS0_TOE (1<<3)
#define WSYS_STATUS_INTR_RDY_TOE (1<<4)
#define WSYS_STATUS_INTR_FIFO_THRESHOLD (1<<5)
#define WSYS_STATUS_INTR_TC (1<<7)
#define WSYS_STATUS_INTR_DMA (1<<8)
typedef struct tag_wb528ms_reader {
// base io address 0
unsigned long mem_addr0;
unsigned long mem_flag0;
// base io address 1
unsigned long mem_addr1;
unsigned long mem_flag1;
struct pci_dev *pci_dev;
unsigned char irq;
unsigned char dma;
dma_addr_t bus_addr;
unsigned char card_exist;
unsigned char medium_changed;
unsigned char card_type;
volatile unsigned int sys_status;
//help thread wait queue
wait_queue_head_t space_helper_thread_wait_queue;
wait_queue_head_t *helper_thread_wait_queue;
} wb528ms_reader_t;
static unsigned int inline wb528ms_pci_inl(unsigned offset);
static void inline wb528ms_pci_outl(volatile unsigned int value,
unsigned offset);
static unsigned int inline wb528ms_inl(unsigned offset);
static void inline wb528ms_outl(volatile unsigned int value, unsigned
offset);
static unsigned short inline wb528ms_inw(unsigned offset);
static void inline wb528ms_outw(volatile unsigned short value, unsigned
offset);
static unsigned char inline wb528ms_inb(unsigned offset);
static void inline wb528ms_outb(volatile unsigned char value, unsigned
offset);
static void inline wb528ms_set_interface(unsigned int);
static inline void wb528ms_power_up(void);
static inline void wb528ms_power_down(void);
static inline void wb528ms_fifo_reset(void);
static int inline wb528ms_pio_data(unsigned char *buff, unsigned int length,
unsigned char read_wrt_flag);
static int inline wb528ms_dma_data(unsigned char *buff, unsigned int length,
unsigned char read_wrt_flag);
static void wb528ms_set_fifo_size(unsigned int length);
static int wb528ms_check_cmd_ready(void);
static int wb528ms_init_card(void);
static int wb528ms_card_exist(void);
static int wb528ms_software_reset(void);
static irqreturn_t wb528ms_do_intr_handler(int irq, void *dev_id,
struct pt_regs *regs);
static void inline __init wb528ms_init_data(void);
#endif
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbmsreader.h,v 1.5.2.3 2006/06/14 07:20:28 dzshen 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
*/
#ifndef _WB_MS_READER_H_
#define _WB_MS_READER_H_
#define WREADER_PIO 0
#define WREADER_DMA 1
static void *wbreader_detect(void);
static int wbreader_release(void);
//send TPC Command and wait untill INT, but needn't justify cmd result
//Result:
// If CMD send (not exec) successful and MS working at
Parallel Mode,
// bit3 to bit0 should be |CED|ERR|BREQ|CMDNK|, or it
should be 0.
// If CMD send error, such as TIMEOUT,the result should below
zero.
static int wbreader_issue_cmd(unsigned char cmd, unsigned char *param,
int length);
// Read TPC Command result (from FIFO or orther register)
//static int wbreader_get_cmd_response(unsigned char *buff, int length);
static int wbreader_prepare_transfer(unsigned char *buff, unsigned int
length,
unsigned char read_wrt_flag);
static int wbreader_transfer_data(unsigned char *buff, unsigned int length,
unsigned char read_wrt_flag, int dma_mode);
#define WSTATE_FIFO_SIZE 1
#define WSTATE_PRESENT (1<<1)
#define WSTATE_CHANGED (1<<2)
#define WSTATE_TRANSFER_CAPABILITY (1 << 3)
#define WSTATE_CARD_TYPE (1<<4)
static int wbreader_get_state(int which_state);
static int wbreader_set_state(int which_state, int state);
//whichconfig defined here
#define WCONFIG_INTERFACE 0x01
#define WCONFIG_CLOCK 0x02
#define WCONFIG_PREPARE_INIT_CARD 0x04
#define WIF_SERIAL 0
#define WIF_PARALLEL 1
static int wbreader_set_config(unsigned char which_config, unsigned int
config);
#endif /* _WB_MS_READER_H_ */
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbscsi.h,v 1.5.2.3 2006/06/14 07:17:06 dzshen 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
*/
#ifndef _WB_SCSI_H_
#define _WB_SCSI_H_
#include "wbdebug.h"
#include <linux/blkdev.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/module.h>
#include <scsi/scsi.h>
// for initializing the host
#define WB_MAX_TARGETS 1
#define WB_MAX_LUNS 1
#define WB_MAX_SECTORS 64
#define WB_UNCHECKED_ISA_DMA 0
// don't make this larger than 8, possible values
// are 0, 2, 4, 8
#define WB_SG_TABLE_SIZE 64
#define WB_LOGICAL_BLK_SIZE 512 // in bytes
#define WB_CMD_RESULT( x, y, c ) (((x)<< 16 ) | ((y) << 8 ) | ((c) << 1) )
#define WB_COMPUTE_START_LBA(cmd) (((unsigned int)cmd->cmnd[2] << 24 ) + \
((unsigned int)cmd->cmnd[3] << 16 ) + \
((unsigned int)cmd->cmnd[4] << 8) + \
((unsigned int)cmd->cmnd[5] ));
#define WB_COMPUTE_NUM_BLK(cmd) (((unsigned short)cmd->cmnd[7] << 8 ) + \
((unsigned short)cmd->cmnd[8] ))
typedef struct scsi_cmnd Scsi_Cmnd;
typedef struct scsi_host_template Scsi_Host_Template;
typedef struct scsi_device Scsi_Device;
// this wrapper is only used for SCSI read/write command
typedef struct tag_wb_scsi_cmd_wrapper {
struct tag_wb_scsi_cmd_wrapper *nextCmd;
Scsi_Cmnd *cmd;
} wb_scsi_cmd_wrapper_t;
//Inquiry Data Format refer to INQUIRYDATA in src
typedef struct tag_wb_scsi_inquiry_data {
unsigned char dev_type;
unsigned char dev_type_modifier:7;
unsigned char removable_medium:1;
unsigned char ver;
unsigned char response_data_format;
unsigned char additional_len;
unsigned char reserved[2];
unsigned char soft_reset:1;
unsigned char cmd_queue:1;
unsigned char reserved2:1;
unsigned char linked_cmds:1;
unsigned char sync:1;
unsigned char wide16b:1;
unsigned char wide32b:1;
unsigned char relative_addr:1;
unsigned char vendor_id[8];
unsigned char prod_id[16];
unsigned char prod_rev_level[4];
} wb_scsi_inquiry_data_t;
// Read Capacity Data Format
typedef struct tag_wb_scsi_capacity {
unsigned int lba;
unsigned int len;
} wb_scsi_capacity_t;
// Sense Data Format - Mode Page Header
typedef struct tag_wb_scsi_mode_page_header {
unsigned char data_len;
unsigned char medium_type;
unsigned char dev_spec_param;
unsigned char blk_desc_len;
} __attribute__ ((packed)) wb_scsi_mode_page_header_t;
typedef struct tag_wb_host_data_t {
unsigned char medium_valid;
unsigned char notice_upper_scsi_layer;
#define WB_SYS_STATE_TEST ( 1 )
#define WB_SYS_STATE_RMMOD ( 1 << 1 )
#define WB_SYS_STATE_RMMOD_ACK ( 1 << 2 )
#define WB_SYS_STATE_QUEUE_CMD_OK ( 1 << 3 )
volatile unsigned int sys_state;
wb_scsi_cmd_wrapper_t space_cmd_wrapper;
wb_scsi_cmd_wrapper_t *cmd_wrapper;
//help thread wait queue
struct semaphore space_semaphore;
struct semaphore *semaphore;
// the extra 4096 handles w528 DMA bug
char scratch[WB_SG_TABLE_SIZE * 512 + 4096];
} wb_host_data_t;
static int __init wb_init_host_data(wb_host_data_t * host_data);
static void wb_stop_helper_kernel_thread(wb_host_data_t * host_data);
static void wb_copy_sgtable_to_scratch(Scsi_Cmnd * cmd, char *scratch);
static void wb_copy_scratch_to_sgtable(char *scratch, Scsi_Cmnd * cmd);
static void wb_make_sense_buffer(Scsi_Cmnd *, int, int, int);
static void wb_chk_valid_medium(wb_host_data_t * hostData);
static int wb_unit_ready(Scsi_Cmnd *);
static int wb_helper_kernel_thread(void *param);
static int wb_process_scsi_cmd(Scsi_Cmnd * cmd);
// execute scsi cmd
static void wb_scsi_test_unit_ready(Scsi_Cmnd *);
static void wb_scsi_inquiry(Scsi_Cmnd *);
static void wb_scsi_start_stop(Scsi_Cmnd *);
static void wb_scsi_mode_sense(Scsi_Cmnd *);
static void wb_scsi_read_capacity(Scsi_Cmnd *);
static void wb_scsi_allow_medium_removal(Scsi_Cmnd *);
static void wb_scsi_read_wrt(Scsi_Cmnd *, unsigned char readCmd);
static void wb_scsi_request_sense(Scsi_Cmnd *);
static void wb_scsi_format_unit(Scsi_Cmnd *);
// entry functions
static void *__init wb_detect(Scsi_Host_Template *);
static int __exit wb_release(struct Scsi_Host *);
//static int wb_ioctl(Scsi_Device *dev,int cmd,void *arg);
static const char *wb_info(struct Scsi_Host *);
static int wb_queue_cmd(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *));
static int wb_eh_abort(Scsi_Cmnd *);
static int wb_eh_reset(Scsi_Cmnd *);
//FIXME, remember to copy this function from Chsun0's memory stick codes
//static int wb_bios_param(Scsi_Device *,struct block_device
*,sector_t, int[]);
#define WB_DRIVER { \
info: wb_info, \
queuecommand: wb_queue_cmd, \
eh_abort_handler:wb_eh_abort, \
eh_host_reset_handler:wb_eh_reset, \
can_queue: 1, \
this_id: -1, \
sg_tablesize: WB_SG_TABLE_SIZE, \
cmd_per_lun: 1, \
present: 0, \
unchecked_isa_dma: WB_UNCHECKED_ISA_DMA, \
use_clustering: ENABLE_CLUSTERING, \
module: THIS_MODULE, \
}
#endif /* _WB_SCSI_H_ */
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbtable.h,v 1.4.2.2 2006/06/14 07:17:06 dzshen 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
*/
#ifndef _WB_TABLE_H_
#define _WB_TABLE_H_
// those are special values, don't change them at will
#define WB_TABLE_DEFECT (0xfffe)
#define WB_TABLE_UNUSED (0xffff)
#define WB_MAX_NUM_PHYSICAL (512)
// for segment 0, only (496 - 2 = 494) logical blocks are mapped
// those two blocks are reserved for book blocks
#define WB_MAX_NUM_LOGICAL (496)
#define WB_MAX_NUM_SEG (16)
// block size: 16KB/10h, 8KB/08h, segment = total_physical_blk /
WB_MAX_NUM_PHYSICAL
int wbtable_init(unsigned int blk_size, unsigned int total_physical_blk);
int wbtable_finit(void);
int wbtable_add_link_logical_physical(unsigned int logical,
unsigned int physical);
int wbtable_add_link_physical_logical(unsigned int physical,
unsigned int logical);
// this link must exist; otherwise, an warning message is printed
int wbtable_del_link(unsigned int physical, unsigned int logical);
unsigned int wbtable_get_physical(unsigned int logical);
unsigned int wbtable_get_logical(unsigned int physical);
unsigned int wbtable_get_seg(unsigned int lba);
unsigned int wbtable_get_unused_physical(unsigned int seg);
unsigned int wbtable_add_link(unsigned int logical, unsigned int physical);
int wbtable_dump(void);
int wbtable_sanity_chk(void);
static int wbtable_chk_limit(unsigned int seg, unsigned int physical,
unsigned int logical);
#endif /* _WB_TABLE_H_ */
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbvirtualdev.h,v 1.4.2.2 2006/06/14 07:17:06 dzshen 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
*/
#ifndef _WB_VIRTUAL_DEV_H_
#define _WB_VIRTUAL_DEV_H_
static void *wbvdev_detect(void);
static int wbvdev_release(void);
// the number of total sectors in this virtual device
static int wbvdev_get_capacity(void);
static int wbvdev_read_wrt(unsigned int start_lba, unsigned int length,
unsigned char *buffer, unsigned char read_wrt_flag);
#define WB_VIRTUAL_STATE_PROTECTED (1)
#define WB_VIRTUAL_STATE_PRESENT (1 << 1)
#define WB_VIRTUAL_STATE_CHANGED (1 << 2)
static unsigned char wbvdev_get_state(int which_state);
static void wbvdev_set_state(int which_state, unsigned char state);
static int wbvdev_init_card(void);
#endif /* _WB_VIRTUAL_DEV_H_ */
===========================================================================================
The privileged confidential information contained in this email is intended for use only by the addressees as indicated by the original sender of this email. If you are not the addressee indicated in this email or are not responsible for delivery of the email to such a person, please kindly reply to the sender indicating this fact and delete all copies of it from your computer and network server immediately. Your cooperation is highly appreciated. It is advised that any unauthorized use of confidential information of Winbond is strictly prohibited; and any information in this email irrelevant to the official business of Winbond shall be deemed as neither given nor endorsed by Winbond.
^ permalink raw reply [flat|nested] 12+ messages in thread* Re: [Winbond] flash memory reader SCSI device drivers [C sources]
2006-06-15 19:02 ` Pierre Ossman
2006-06-16 1:36 ` [Winbond] flash memory reader SCSI device drivers [headers] dezheng shen
@ 2006-06-16 1:38 ` dezheng shen
1 sibling, 0 replies; 12+ messages in thread
From: dezheng shen @ 2006-06-16 1:38 UTC (permalink / raw)
To: Pierre Ossman, Linux Kernel Mailing List; +Cc: Jesper Juhl, PI14 SJIN
[headers] is in previous separate email.
thank you,
dz
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbmscard.c,v 1.12.2.5 2006/06/14 07:30:50 dzshen 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"
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbmsreader528.c,v 1.21.2.4 2006/06/14 07:30:50 dzshen 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 "wbmsreader528.h"
static struct pci_device_id wbms528_ids[] = {
{W528MS_VENDOR_ID, W528MS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,},
};
MODULE_DEVICE_TABLE(pci, wbms528_ids);
static wb528ms_reader_t g_reader_info;
#define WB_DUMP_REG
#ifdef WB_DUMP_REG
#define debug_reg() do{dump_reg(); \
printk(" -- file:"__FILE__" line:%d\n", __LINE__);}while(0)
#else
#define debug_reg()
#endif
#ifdef WB_DUMP_REG
static void dump_reg(void)
{
WB_PRINTK("<<528reg: MSCR1:%X MSCR2:%X ICR:%X MSSR:%X PCI_ICR:%X>>",
wb528ms_inb(WIO_MSCR1),
wb528ms_inb(WIO_MSCR2),
wb528ms_inb(WIO_ICR),
wb528ms_inb(WIO_MSSR), wb528ms_pci_inl(WPCI_ICR));
}
#endif
static unsigned char inline wb528ms_inb(unsigned offset)
{
return readb((void *)(g_reader_info.mem_addr1 + offset));
}
static unsigned int inline wb528ms_pci_inl(unsigned offset)
{
return readl((void *)(g_reader_info.mem_addr0 + offset));
}
static void inline wb528ms_pci_outl(volatile unsigned int value,
unsigned offset)
{
writel(value, (void *)(g_reader_info.mem_addr0 + offset));
}
static unsigned int inline wb528ms_inl(unsigned offset)
{
return readl((void *)(g_reader_info.mem_addr1 + offset));
}
static void inline wb528ms_outl(volatile unsigned int value, unsigned
offset)
{
writel(value, (void *)(g_reader_info.mem_addr1 + offset));
}
static unsigned short inline wb528ms_inw(unsigned offset)
{
return readw((void *)(g_reader_info.mem_addr1 + offset));
}
static void inline wb528ms_outw(volatile unsigned short value, unsigned
offset)
{
writew(value, (void *)g_reader_info.mem_addr1 + offset);
}
static void inline wb528ms_outb(volatile unsigned char value, unsigned
offset)
{
writeb(value, (void *)(g_reader_info.mem_addr1 + offset));
}
static inline void wb528ms_power_down(void)
{
wb528ms_outb(wb528ms_inb(WIO_MSCR2) | WMSCR2_PWRCTRL, WIO_MSCR2);
}
static inline void wb528ms_power_up(void)
{
wb528ms_outb(wb528ms_inb(WIO_MSCR2) & (~WMSCR2_PWRCTRL), WIO_MSCR2);
}
static inline void wb528ms_fifo_reset(void)
{
wb528ms_outb(wb528ms_inb(WIO_MSCR2) | WMSCR2_FIFORST, WIO_MSCR2);
wb528ms_outb(wb528ms_inb(WIO_MSCR2) & ~WMSCR2_FIFORST, WIO_MSCR2);
}
static int wb528ms_card_exist(void)
{
if (wb528ms_inb(WIO_MSSR) & WMSSR_INS) {
g_reader_info.card_exist = FALSE;
g_reader_info.medium_changed = TRUE;
wb528ms_power_down();
} else {
g_reader_info.card_exist = TRUE;
wb528ms_power_up();
}
return g_reader_info.card_exist;
}
static void inline wb528ms_set_interface(unsigned int mode)
{
if (mode == WIF_SERIAL)
wb528ms_outb(wb528ms_inb(WIO_MSCR2) & 0xBF, WIO_MSCR2);
else
wb528ms_outb(wb528ms_inb(WIO_MSCR2) | WMSCR2_PF, WIO_MSCR2);
}
static int wb528ms_init_card(void)
{
wb528ms_software_reset();
wb528ms_set_interface(WIF_SERIAL);
wb528ms_inb(WIO_ISR1);
wb528ms_inb(WIO_ISR2);
wb528ms_fifo_reset();
wb528ms_outb(0x71, WIO_ICR);
wb528ms_pci_outl(0x01, WPCI_ICR);
return 0;
}
static int wb528ms_software_reset(void)
{
wb528ms_outb(wb528ms_inb(WIO_MSCR2) | WMSCR2_SOFTRST, WIO_MSCR2);
while (wb528ms_inb(WIO_MSCR2) & WMSCR2_SOFTRST) ;
wb528ms_inb(WIO_ISR1);
wb528ms_inb(WIO_ISR2);
wb528ms_card_exist();
return 0;
}
static void inline __init wb528ms_init_data(void)
{
memset(&g_reader_info, 0, sizeof(wb528ms_reader_t));
g_reader_info.medium_changed = TRUE;
g_reader_info.card_type = WDEV_MS;
g_reader_info.helper_thread_wait_queue =
&(g_reader_info.space_helper_thread_wait_queue);
init_waitqueue_head(g_reader_info.helper_thread_wait_queue);
g_reader_info.dma = 1;
}
static irqreturn_t wb528ms_do_intr_handler(int irq, void *dev_id,
struct pt_regs *regs)
{
unsigned char isr1, isr2;
unsigned char dev_isr;
if (0 == (wb528ms_pci_inl(WPCI_ISR) & WPCI_ISR_STATUS))
goto lab_end;
wb528ms_pci_outl(0x00, WPCI_ICR);
dev_isr = wb528ms_inb(WIO_DEVICE_ISR);
if (0 ==
(dev_isr &
(WDEVICE_ISR_MS | WDEVICE_ISR_DMA | WDEVICE_ISR_MSDMA_ERROR)))
goto lab_enable_interrupt;
if (dev_isr & WDEVICE_ISR_MS) {
isr1 = wb528ms_inb(WIO_ISR1);
isr2 = wb528ms_inb(WIO_ISR2);
if (isr1 & WISR1_INS) {
wb528ms_card_exist();
}
if (isr2 & WISR2_BS0) {
if (g_reader_info.
sys_status & WSYS_STATUS_INTR_BS0) {
g_reader_info.sys_status &=
~WSYS_STATUS_INTR_BS0;
if (0 ==
(g_reader_info.
sys_status & (WSYS_STATUS_INTR_BS0 |
WSYS_STATUS_INTR_DMA)))
wake_up_interruptible
(g_reader_info.
helper_thread_wait_queue);
}
}
if (isr2 & WISR2_RDYTO) {
g_reader_info.sys_status &=
~WSYS_STATUS_INTR_RDY_TOE;
}
if (isr2 & WISR2_CRC) {
g_reader_info.sys_status &= ~WSYS_STATUS_INTR_CRC;
}
}
if (dev_isr & WDEVICE_ISR_DMA) {
if ((wb528ms_inb(WIO_DMA_INT_SRC) &
(2 * g_reader_info.dma))) {
if (wb528ms_inl(0x20 * (g_reader_info.dma + 1)) &
0x800) {
if (g_reader_info.
sys_status & WSYS_STATUS_INTR_DMA) {
g_reader_info.sys_status &=
~WSYS_STATUS_INTR_DMA;
if (0 ==
(g_reader_info.
sys_status & (WSYS_STATUS_INTR_BS0
|
WSYS_STATUS_INTR_DMA)))
wake_up_interruptible
(g_reader_info.
helper_thread_wait_queue);
}
}
}
wb528ms_outb(0, 0x20 * (g_reader_info.dma + 1));
}
if (dev_isr & WDEVICE_ISR_MSDMA_ERROR) {
wb528ms_outb(0, 0x20 * (g_reader_info.dma + 1));
wake_up_interruptible(g_reader_info.
helper_thread_wait_queue);
}
lab_enable_interrupt:
wb528ms_pci_outl(0x01, WPCI_ICR);
lab_end:
return IRQ_HANDLED;
}
static void *wbreader_detect(void)
{
void *ret;
struct pci_dev *pdev;
ret = NULL;
wb528ms_init_data();
pdev = NULL;
pdev = pci_find_device(W528MS_VENDOR_ID, W528MS_DEVICE_ID, pdev);
if (NULL == pdev) {
goto lab_end;
}
if (pci_enable_device(pdev)) {
WB_PRINTK_ERROR("can't enable 528 device\n");
goto lab_end;
}
g_reader_info.pci_dev = pdev;
g_reader_info.irq = pdev->irq;
g_reader_info.mem_addr0 = pci_resource_start(pdev, 0);
g_reader_info.mem_flag0 = pci_resource_flags(pdev, 0);
g_reader_info.mem_addr1 = pci_resource_start(pdev, 1);
g_reader_info.mem_flag1 = pci_resource_flags(pdev, 1);
g_reader_info.mem_addr0 =
(unsigned long)ioremap(g_reader_info.mem_addr0, 0x0fff);
g_reader_info.mem_addr1 =
(unsigned long)ioremap(g_reader_info.mem_addr1, 0x0fff);
if (request_irq
(pdev->irq, wb528ms_do_intr_handler, SA_INTERRUPT | SA_SHIRQ,
"wb528ms", &g_reader_info)) {
ret = 0;
goto lab_end;
}
wb528ms_software_reset();
wb528ms_pci_outl(0x01, WPCI_ICR);
//enable insert interrupt
wb528ms_outb(WICR_INSINT, WIO_ICR);
wb528ms_outb(wb528ms_inb(WIO_MSCR2) & ~WMSCR2_PF, WIO_MSCR2);
wb528ms_card_exist();
ret = &pdev->dev;
lab_end:
return ret;
}
static int wbreader_release(void)
{
wb528ms_power_down();
wb528ms_outb(0, WIO_ICR);
free_irq(g_reader_info.irq, &g_reader_info);
return 0;
}
static int wb528ms_check_cmd_ready(void)
{
int i;
for (i = 0; i < WMAX_POLLING; i++) {
if (wb528ms_inb(WIO_MSSR) & WMSSR_RDY4CMD)
return 0;
}
return -1;
}
//send TPC Command and wait untill INT, but needn't justify cmd result
//Result:
// If CMD send (not exec) successful and MS working at
Parallel Mode,
// bit3 to bit0 should be |CED|ERR|BREQ|CMDNK|, or it
should be 0.
// If CMD send error, such as TIMEOUT,the result should below
zero.
static int wbreader_issue_cmd(unsigned char cmd, unsigned char *buffer,
int length)
{
int ret, i;
ret = -1;
if (wb528ms_check_cmd_ready()) {
WB_PRINTK_ERROR("wait for cmd RDY timeout\n");
debug_reg();
goto lab_end;
}
wb528ms_inb(WIO_ISR2);
wb528ms_fifo_reset();
g_reader_info.sys_status =
WSYS_STATUS_INTR_BS0 | WSYS_STATUS_INTR_CRC;
if (length) {
wb528ms_set_fifo_size(length);
}
for (i = 0; i < length; i++) {
wb528ms_outb(buffer[i], WIO_DFR);
}
wb528ms_outb(cmd, WIO_CMDR);
switch (cmd) {
case TPC_SET_CMD:
case TPC_EX_SET_CMD:
wait_event_interruptible_timeout(*
(g_reader_info.
helper_thread_wait_queue),
!(g_reader_info.
sys_status &
WSYS_STATUS_INTR_BS0), 200);
if (g_reader_info.sys_status & WSYS_STATUS_INTR_BS0) {
WB_PRINTK_ERROR("wait BS0 fail\n");
goto lab_end;
}
break;
default:
break;
}
ret = 0;
lab_end:
return ret;
}
static int wbreader_prepare_transfer(unsigned char *buff, unsigned int
length,
unsigned char read_flag)
{
int ret;
ret = -1;
// map buff memory address to bus address
g_reader_info.bus_addr =
pci_map_single(g_reader_info.pci_dev, buff, length,
PCI_DMA_BIDIRECTIONAL);
ret = 0;
return ret;
}
static void wb528ms_set_fifo_size(unsigned int length)
{
unsigned char mscr2;
mscr2 = wb528ms_inb(WIO_MSCR2) & 0xFC;
mscr2 |= (length >> 8) & 0x3;
wb528ms_outb(length & 0xFF, WIO_MSCR1);
wb528ms_outb(mscr2, WIO_MSCR2);
}
static int inline wb528ms_pio_data(unsigned char *buffer, unsigned int
length,
unsigned char read_flag)
{
int ret, n;
int i;
unsigned char mssr;
ret = -1;
wb528ms_set_fifo_size(length);
if (read_flag) {
for (n = 0; n < length; n++) {
for (i = 0; i < 20; i++) {
mssr = wb528ms_inb(WIO_MSSR);
if (0 == (mssr & WMSSR_DFE))
break;
if (mssr & WMSSR_CRC) {
goto lab_end;
}
}
buffer[n] = wb528ms_inb(WIO_DFR);
}
ret = 0;
} else {
for (n = 0; n < length; n++) {
for (i = 0; i < 20; i++) {
mssr = wb528ms_inb(WIO_MSSR);
if (0 == (mssr & WMSSR_DFF))
break;
if (mssr & WMSSR_CRC) {
goto lab_end;
}
}
wb528ms_outb(buffer[n], WIO_DFR);
}
for (i = 0;
i < WMAX_POLLING && !(wb528ms_inb(WIO_MSSR) & WMSSR_DFE);
i++) ;
if (i < WMAX_POLLING) {
ret = 0;
}
}
lab_end:
return ret;
}
static int inline wb528ms_dma_data(unsigned char *buffer, unsigned int
length,
unsigned char read_flag)
{
int ret;
int i;
ret = -1;
if (read_flag) {
for (i = 0;
i < WMAX_POLLING && !(wb528ms_inb(WIO_MSSR) & WMSSR_DFF);
i++) ;
if (i == WMAX_POLLING) {
ret = -1;
goto lab_out;
}
}
g_reader_info.sys_status |= WSYS_STATUS_INTR_DMA;
if (g_reader_info.card_type == WDEV_MS) {
g_reader_info.sys_status &= ~WSYS_STATUS_INTR_BS0;
}
wb528ms_outb(2 * g_reader_info.dma, WIO_DMA_INT_MSK);
wb528ms_outl(length, 4 + 0x20 * (g_reader_info.dma + 1));
if (read_flag) {
wb528ms_outl(WIO_DFR, 8 + 0x20 * (g_reader_info.dma + 1));
wb528ms_outl(g_reader_info.bus_addr,
0x10 + 0x20 * (g_reader_info.dma + 1));
wb528ms_outl(WDMA_CHCSR_READ,
0x20 * (g_reader_info.dma + 1));
} else {
wb528ms_outl(g_reader_info.bus_addr,
8 + 0x20 * (g_reader_info.dma + 1));
wb528ms_outl(WIO_DFR,
0x10 + 0x20 * (g_reader_info.dma + 1));
wb528ms_outl(WDMA_CHCSR_WRITE,
0x20 * (g_reader_info.dma + 1));
}
wait_event_interruptible_timeout(*
(g_reader_info.
helper_thread_wait_queue),
!(g_reader_info.
sys_status & (WSYS_STATUS_INTR_DMA |
WSYS_STATUS_INTR_BS0)),
200);
if (0 ==
(g_reader_info.
sys_status & (WSYS_STATUS_INTR_DMA | WSYS_STATUS_INTR_BS0))) {
ret = 0;
}
if (read_flag == 0) {
for (i = 0;
i < WMAX_POLLING && !(wb528ms_inb(WIO_MSSR) & WMSSR_DFE);
i++) ;
if (i == WMAX_POLLING)
ret = -1;
}
if (wb528ms_check_cmd_ready()) {
debug_reg();
}
pci_unmap_single(g_reader_info.pci_dev,
g_reader_info.bus_addr, length,
PCI_DMA_BIDIRECTIONAL);
lab_out:
return ret;
}
static int wbreader_transfer_data(unsigned char *buff, unsigned int length,
unsigned char read_flag, int dma_mode)
{
if (dma_mode) {
return wb528ms_dma_data(buff, length, read_flag);
} else {
return wb528ms_pio_data(buff, length, read_flag);
}
}
static int wbreader_get_state(int which_state)
{
int ret;
switch (which_state) {
case WSTATE_PRESENT:
ret = g_reader_info.card_exist;
break;
case WSTATE_CHANGED:
ret = g_reader_info.medium_changed;
break;
case WSTATE_TRANSFER_CAPABILITY:
ret = WIF_PARALLEL;
break;
default:
ret = -1;
WB_PRINTK("Unknow state 0x%x! \n", which_state);
break;
}
return ret;
}
static int wbreader_set_state(int which_state, int state)
{
switch (which_state) {
case WSTATE_FIFO_SIZE:
wb528ms_set_fifo_size(state);
break;
case WSTATE_PRESENT:
g_reader_info.card_exist = state;
break;
case WSTATE_CHANGED:
g_reader_info.medium_changed = state;
break;
case WSTATE_CARD_TYPE:
g_reader_info.card_type = state;
break;
default:
WB_PRINTK("Unknow state 0x%x! \n", which_state);
break;
}
return 0;
}
static int wbreader_set_config(unsigned char which_config, unsigned int
config)
{
int ret;
ENTER();
ret = 0;
switch (which_config) {
case WCONFIG_PREPARE_INIT_CARD:
ret = wb528ms_init_card();
break;
case WCONFIG_INTERFACE:
if (wb528ms_check_cmd_ready()) {
WB_PRINTK("wait for cmd RDY timeout\n");
debug_reg();
break;
}
if (config == WIF_PARALLEL)
wb528ms_outb(wb528ms_inb(WIO_MSCR2) | WMSCR2_PF,
WIO_MSCR2);
else
wb528ms_outb(wb528ms_inb(WIO_MSCR2) & ~WMSCR2_PF,
WIO_MSCR2);
ret = 0;
break;
default:
ret = -1;
break;
}
LEAVE();
return ret;
}
#include "wbmscard.c"
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Id: wbscsi.c,v 1.8.2.5 2006/06/14 07:35:46 sjin Exp $
$Name: $
--- don't modify lines above ---
Description:
Enviornment:
(1) Redhat 9 kernel
(2) original Linux kernel 32-bit 2.6.9-1.667 on Intel
*/
const static char *WKEEP_THIS_LINE =
"$Id: wbscsi.c,v 1.8.2.5 2006/06/14 07:35:46 sjin Exp $";
#include "wbscsi.h"
#include "wbvirtualdev.h"
wb_host_data_t *g_wb_host_data;
struct Scsi_Host *g_wb_host;
static int __init wb_init_host_data(wb_host_data_t * host_data)
{
int ret;
ENTER();
ret = 0;
memset(host_data, 0, sizeof(wb_host_data_t));
host_data->cmd_wrapper = &host_data->space_cmd_wrapper;
host_data->semaphore = &host_data->space_semaphore;
sema_init(host_data->semaphore, 0);
host_data->sys_state = WB_SYS_STATE_TEST;
host_data->medium_valid = FALSE;
host_data->notice_upper_scsi_layer = FALSE;
//FIXME
if (0 == WB_UNCHECKED_ISA_DMA) {
// PCI DMA
} else {
// ISA DMA
}
LEAVE();
return ret;
}
static void *__init wb_detect(Scsi_Host_Template * tmpt)
{
void *pdev;
int num_host;
struct Scsi_Host *host;
wb_host_data_t *host_data;
ENTER();
pdev = NULL;
num_host = 0;
tmpt->proc_name = "Winbond Storage Driver";
// now support only ONE device
// register Winbond controller as a SCSI host
if (0 == (host = scsi_host_alloc(tmpt, sizeof(wb_host_data_t)))) {
WB_PRINTK_ERROR("can't allocate scsi host \n");
goto lab_end;
}
g_wb_host = host;
host->max_id = WB_MAX_TARGETS;
host->max_lun = WB_MAX_LUNS;
host->this_id = tmpt->this_id;
host->max_sectors = WB_MAX_SECTORS;
host_data = (wb_host_data_t *) host->hostdata;
g_wb_host_data = host_data;
if (wb_init_host_data(host_data)) {
WB_PRINTK_ERROR("initialize wb_init_host_data failed!\n");
goto lab_unregister_host;
}
pdev = wbvdev_detect();
if (NULL == pdev) {
goto lab_unregister_host;
}
num_host = 1;
kernel_thread(wb_helper_kernel_thread, host, CLONE_FS | CLONE_FILES);
schedule();
goto lab_end;
lab_unregister_host:
scsi_host_put(host);
lab_end:
LEAVE();
return pdev;
}
static void wb_stop_helper_kernel_thread(wb_host_data_t * host_data)
{
int times;
ENTER();
times = 0;
// protocol to tell helper thread to terminate
host_data->sys_state = WB_SYS_STATE_RMMOD;
up(host_data->semaphore);
schedule();
while ((WB_SYS_STATE_RMMOD_ACK != host_data->sys_state)
&& (times++ != 1000)) {
mdelay(100);
WB_PRINTK_THREAD("waiting for helper thread to terminate\n");
schedule();
}
LEAVE();
}
static int __exit wb_release(struct Scsi_Host *host)
{
wb_host_data_t *host_data;
ENTER();
host_data = (wb_host_data_t *) host->hostdata;
// terminate the helper kernel thread
wb_stop_helper_kernel_thread(host_data);
wbvdev_release();
scsi_remove_host(host);
LEAVE();
return 0;
}
static const char *wb_info(struct Scsi_Host *host)
{
ENTER();
LEAVE();
return WKEEP_THIS_LINE;
}
static int wb_process_scsi_cmd(Scsi_Cmnd * cmd)
{
unsigned long flags;
wb_host_data_t *host_data;
ENTER();
host_data = g_wb_host_data;
wb_chk_valid_medium(host_data);
wb_unit_ready(cmd);
switch (cmd->cmnd[0]) {
case START_STOP:
WB_PRINTK_SCSI("START_STOP\n");
wb_scsi_start_stop(cmd);
break;
case REQUEST_SENSE:
WB_PRINTK_SCSI("REQUEST_SENSE\n");
wb_scsi_request_sense(cmd);
break;
case ALLOW_MEDIUM_REMOVAL:
WB_PRINTK_SCSI("ALLOW_MEDIUM_REMOVAL\n");
wb_scsi_allow_medium_removal(cmd);
break;
case TEST_UNIT_READY:
WB_PRINTK_SCSI("TEST_UNIT_READY\n");
wb_scsi_test_unit_ready(cmd);
break;
case INQUIRY:
WB_PRINTK_SCSI("INQUIRY\n");
wb_scsi_inquiry(cmd);
break;
case READ_6:
WB_PRINTK_ERROR_SCSI("READ_6\n");
break;
case READ_10:
WB_PRINTK_SCSI("READ_10\n");
wb_scsi_read_wrt(cmd, TRUE);
break;
case WRITE_6:
WB_PRINTK_ERROR_SCSI("WRITE_6\n");
break;
case WRITE_10:
WB_PRINTK_SCSI("WRITE_10\n");
wb_scsi_read_wrt(cmd, FALSE);
break;
// MODE_SENSE is 6-byte command
case MODE_SENSE:
WB_PRINTK_SCSI("MODE_SENSE\n");
wb_scsi_mode_sense(cmd);
break;
case READ_CAPACITY:
WB_PRINTK_SCSI("READ_CAPACITY\n");
wb_scsi_read_capacity(cmd);
break;
case FORMAT_UNIT:
WB_PRINTK_SCSI("FORMAT_UNIT\n");
wb_scsi_format_unit(cmd);
break;
// haven't handled all necessary cases yet
default:
WB_PRINTK_ERROR_SCSI("UNKNOWN command(0x%x)\n", cmd->cmnd[0]);
wb_make_sense_buffer(cmd, ILLEGAL_REQUEST, 0x20, 0x00);
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, CHECK_CONDITION);
break;
} // end switch
spin_lock_irqsave(g_wb_host->host_lock, flags);
cmd->scsi_done(cmd);
spin_unlock_irqrestore(g_wb_host->host_lock, flags);
LEAVE();
return 0;
}
static int wb_helper_kernel_thread(void *param)
{
struct task_struct *current_task;
wb_host_data_t *host_data;
struct Scsi_Host *host;
ENTER();
host = (struct Scsi_Host *)param;
host_data = g_wb_host_data;
current_task = current;
lock_kernel();
daemonize("helperthread");
current->flags |= PF_NOFREEZE;
unlock_kernel();
strcpy(current_task->comm, "tcsKernel");
while (1) {
down(host_data->semaphore);
WB_PRINTK_THREAD("helper kernel thread is running again!!\n");
switch (host_data->sys_state) {
case WB_SYS_STATE_TEST:
WB_PRINTK_THREAD("WB_SYS_STATE_TEST\n");
break;
case WB_SYS_STATE_RMMOD:
WB_PRINTK_THREAD("WB_SYS_STATE_RMMOD\n");
host_data->sys_state = WB_SYS_STATE_RMMOD_ACK;
goto lab_end;
break;
case WB_SYS_STATE_QUEUE_CMD_OK:
WB_PRINTK_THREAD("WB_SYS_STATE_QUEUE_CMD\n");
wb_process_scsi_cmd(host_data->cmd_wrapper->cmd);
break;
default:
WB_PRINTK_ERROR("Oops,host_data->sys_state=0x%x\n",
host_data->sys_state);
break;
}
WB_PRINTK_THREAD("helper kernel thread is idle!!\n");
}
lab_end:
LEAVE();
return 0;
}
static int wb_queue_cmd(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
{
wb_host_data_t *host_data;
ENTER();
host_data = g_wb_host_data;
host_data->cmd_wrapper->cmd = cmd;
cmd->scsi_done = done;
cmd->result = WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, GOOD);
host_data->sys_state = WB_SYS_STATE_QUEUE_CMD_OK;
up(host_data->semaphore);
LEAVE();
return 0;
}
static int wb_eh_abort(Scsi_Cmnd * cmnd)
{
ENTER();
LEAVE();
return 0;
}
static int wb_eh_reset(Scsi_Cmnd * cmnd)
{
ENTER();
LEAVE();
return 0;
}
static void wb_make_sense_buffer(Scsi_Cmnd * cmd, int key, int asc, int
ascq)
{
char *sense_buffer;
ENTER();
switch (key) {
case ILLEGAL_REQUEST:
WB_PRINTK("sense key ILLEGAL_REQUEST\n");
break;
case NOT_READY:
WB_PRINTK("sense key NOT_READY\n");
break;
case MEDIUM_ERROR:
WB_PRINTK("sense key MEDIUM_ERROR\n");
break;
case UNIT_ATTENTION:
WB_PRINTK("sense key UNIT_ATTENTION\n");
break;
case ABORTED_COMMAND:
WB_PRINTK("sense key ABORTED_COMMAND\n");
break;
default:
WB_PRINTK_ERROR("Unknown SCSI sense key %X!!!\n", key);
break;
}
sense_buffer = cmd->sense_buffer;
memset(sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
sense_buffer[0] = 0x70; // Error Code
sense_buffer[2] = key; // Sense Key
sense_buffer[7] = 0x0A; // Additional Sense Length
sense_buffer[12] = asc; // ASC = Invalid OpCode
sense_buffer[13] = ascq; // ASCQ
LEAVE();
}
static int wb_get_medium_valid(wb_host_data_t * host_data)
{
ENTER();
LEAVE();
return host_data->medium_valid;
}
static void wb_set_medium_valid(wb_host_data_t * host_data, unsigned
char bool)
{
ENTER();
host_data->medium_valid = bool;
LEAVE();
}
static int wb_get_notice_upper_scsi_layer(wb_host_data_t * host_data)
{
ENTER();
LEAVE();
return host_data->notice_upper_scsi_layer;
}
static void wb_set_notice_upper_scsi_layer(wb_host_data_t * host_data,
unsigned char bool)
{
ENTER();
host_data->notice_upper_scsi_layer = bool;
LEAVE();
}
static void wb_chk_valid_medium(wb_host_data_t * host_data)
{
ENTER();
if (FALSE == wbvdev_get_state(WB_VIRTUAL_STATE_PRESENT)) {
WB_PRINTK("Card does not exist\n");
} else if (TRUE == wbvdev_get_state(WB_VIRTUAL_STATE_CHANGED)) {
wbvdev_set_state(WB_VIRTUAL_STATE_CHANGED, FALSE);
if (0 == wbvdev_init_card()) {
wb_set_medium_valid(host_data, TRUE);
} else {
wb_set_medium_valid(host_data, FALSE);
}
wb_set_notice_upper_scsi_layer(host_data, TRUE);
}
LEAVE();
return;
}
static void wb_scsi_format_unit(Scsi_Cmnd * cmd)
{
}
/*
wb_transfer_internal to transfer buffers
wr_flag = 0 : driver buffer to command buffer (Read)
wr_flag = 1 : command buffer to driver buffer (Write)
*/
static void wb_transfer_internal(Scsi_Cmnd * incmd,
void *data, u32 len, u8 wr_flag)
{
struct scsi_cmnd *cmd = incmd;
void *buf;
u32 transfer_len;
if (cmd->use_sg) {
struct scatterlist *sg;
sg = (struct scatterlist *)cmd->request_buffer;
buf = kmap_atomic(sg->page, KM_IRQ0) + sg->offset;
transfer_len = min(sg->length, len);
} else {
buf = cmd->request_buffer;
transfer_len = min(cmd->request_bufflen, len);
}
if (wr_flag) {
memcpy(data, buf, transfer_len);
} else {
memcpy(buf, data, transfer_len);
}
if (cmd->use_sg) {
struct scatterlist *sg;
sg = (struct scatterlist *)cmd->request_buffer;
kunmap_atomic(buf - sg->offset, KM_IRQ0);
}
}
static void wb_scsi_inquiry(Scsi_Cmnd * cmd)
{
wb_scsi_inquiry_data_t inquiry;
ENTER();
inquiry.dev_type = 0;
inquiry.removable_medium = 1;
inquiry.ver = 2;
inquiry.response_data_format = 0x02;
inquiry.additional_len = 31 * 3;
strncpy(inquiry.vendor_id, "Winbond", 8);
strncpy(inquiry.prod_id, "PCI MassStorage", 16);
strncpy(inquiry.prod_rev_level, "1.0", 4);
wb_transfer_internal(cmd, &inquiry, sizeof(inquiry), 0);
// even if the card doesn't exist, we still return TRUE
cmd->result = WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, GOOD);
memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
LEAVE();
}
static void wb_scsi_test_unit_ready(Scsi_Cmnd * cmd)
{
ENTER();
if (1 == wb_unit_ready(cmd)) {
wb_make_sense_buffer(cmd, NOT_READY, 0x3a, 0x00);
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, CHECK_CONDITION);
}
LEAVE();
}
static void wb_scsi_start_stop(Scsi_Cmnd * cmd)
{
wb_host_data_t *host_data;
ENTER();
host_data = g_wb_host_data;
// start bit = off
if (0 == (cmd->cmnd[4] & 0x01)) {
wbvdev_set_state(WB_VIRTUAL_STATE_PRESENT, FALSE);
wb_set_medium_valid(host_data, FALSE);
wbvdev_set_state(WB_VIRTUAL_STATE_CHANGED, TRUE);
} else if (1 == wb_unit_ready(cmd)) { // start bit = on, turn on card
//wb_make_sense_buffer( cmd, NOT_READY, 0x3a, 0x00 );
//cmd->result = WB_CMD_RESULT( DID_OK, COMMAND_COMPLETE,
CHECK_CONDITION );
}
LEAVE();
}
static void wb_scsi_read_capacity(Scsi_Cmnd * cmd)
{
wb_host_data_t *host_data;
wb_scsi_capacity_t cap;
ENTER();
host_data = g_wb_host_data;
if (wb_unit_ready(cmd)) {
goto lab_end;
}
cap.lba = cpu_to_be32(wbvdev_get_capacity() - 1);
cap.len = cpu_to_be32((unsigned int)WB_LOGICAL_BLK_SIZE);
wb_transfer_internal(cmd, &cap, sizeof(cap), 0);
lab_end:
LEAVE();
return;
}
static void wb_scsi_mode_sense(Scsi_Cmnd * cmd)
{
wb_host_data_t *host_data;
wb_scsi_mode_page_header_t modepage;
ENTER();
host_data = g_wb_host_data;
if (wbvdev_get_state(WB_VIRTUAL_STATE_PRESENT)) {
modepage.data_len = 3;
modepage.blk_desc_len = 0;
if (wbvdev_get_state(WB_VIRTUAL_STATE_PROTECTED)) {
modepage.dev_spec_param = 0x80;
} else {
modepage.dev_spec_param = 0;
}
} else { // card is not in
wb_make_sense_buffer(cmd, NOT_READY, 0x3a, 0x00);
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, CHECK_CONDITION);
WB_PRINTK("Card may not be inserted!\n");
}
wb_transfer_internal(cmd, &modepage, sizeof(wb_scsi_mode_page_header_t),
0);
LEAVE();
}
static void wb_scsi_allow_medium_removal(Scsi_Cmnd * cmd)
{
ENTER();
//prevent removal cmnd is illegal since card can be removable
if ((cmd->cmnd[4] & 0x01)) {
// AdditionalSenseCode: SCSI_ADSENSE_ILLEGAL_COMMAND
wb_make_sense_buffer(cmd, ILLEGAL_REQUEST, 0x20, 0x00);
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, CHECK_CONDITION);
}
LEAVE();
}
static void wb_copy_scratch_to_sgtable(char *scratch, Scsi_Cmnd * cmd)
{
struct scatterlist *sg_table;
int count;
unsigned char *sg_buf;
ENTER();
sg_table = (struct scatterlist *)cmd->request_buffer;
for (count = 0; count < cmd->use_sg; count++) {
sg_buf =
kmap_atomic(sg_table[count].page,
KM_IRQ0) + sg_table[count].offset;
memcpy(sg_buf, scratch, sg_table[count].length);
kunmap_atomic(sg_buf, KM_IRQ0);
scratch += sg_table[count].length;
}
LEAVE();
}
static void wb_copy_sgtable_to_scratch(Scsi_Cmnd * cmd, char *scratch)
{
struct scatterlist *sg_table;
int count;
unsigned char *sg_buf;
ENTER();
sg_table = (struct scatterlist *)cmd->request_buffer;
for (count = 0; count < cmd->use_sg; count++) {
sg_buf =
kmap_atomic(sg_table[count].page,
KM_IRQ0) + sg_table[count].offset;
memcpy(scratch, sg_buf, sg_table[count].length);
kunmap_atomic(sg_buf, KM_IRQ0);
scratch += sg_table[count].length;
}
LEAVE();
}
static void wb_scsi_read_wrt(Scsi_Cmnd * cmd, unsigned char read_cmd)
{
unsigned int start_lba;
unsigned int length;
char *buff;
wb_host_data_t *host_data;
ENTER();
buff = NULL;
host_data = g_wb_host_data;
if (wb_unit_ready(cmd)) {
goto lab_end;
}
// calculate LBA in sectors
start_lba = WB_COMPUTE_START_LBA(cmd);
WB_PRINTK_SCSI
("[start_lba = 0x%x bufferlen = %d bytes use_sg = %x]\n",
start_lba, cmd->bufflen, cmd->use_sg);
if ((start_lba > wbvdev_get_capacity())) {
WB_PRINTK_ERROR_SCSI("start address out of bound\n");
// AdditionalSenseCode: SCSI_ADSENSE_INVALID_CDB
wb_make_sense_buffer(cmd, ILLEGAL_REQUEST, 0x24, 0x00);
goto lab_end;
}
// get buffer length
length = cmd->request_bufflen;
// get buffer pointer
// if use sgtable,copy the data from sg buffer to buffer we allocated
if (0 != cmd->use_sg) {
buff = host_data->scratch;
if (read_cmd == FALSE) {
wb_copy_sgtable_to_scratch(cmd, host_data->scratch);
}
} else {
buff = cmd->request_buffer;
}
// read data from or write data to card
if (wbvdev_read_wrt(start_lba, length, buff, read_cmd)) {
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, CHECK_CONDITION);
//wb_make_sense_buffer( cmd,MEDIUM_ERROR, 0x11, 0x00 );
wb_make_sense_buffer(cmd, MEDIUM_ERROR, 0x17, 0x01);
//wb_make_sense_buffer( cmd,ABORTED_COMMAND, 0x17, 0x01 );
}
// if use sgtabble,copy the data from buffer we allocated to sg buffer
if (0 != cmd->use_sg) {
if (TRUE == read_cmd)
wb_copy_scratch_to_sgtable(host_data->scratch, cmd);
}
lab_end:
LEAVE();
}
static void wb_scsi_request_sense(Scsi_Cmnd * cmd)
{
ENTER();
LEAVE();
}
// check card state and set corresponding flags
static int wb_unit_ready(Scsi_Cmnd * cmd)
{
int ret;
wb_host_data_t *host_data;
ENTER();
host_data = g_wb_host_data;
ret = 1;
if (TRUE == wbvdev_get_state(WB_VIRTUAL_STATE_PRESENT)) {
if (TRUE == wb_get_medium_valid(host_data)) {
if (FALSE == wb_get_notice_upper_scsi_layer(host_data)) {
ret = 0;
} else {
// for each card inserted/removed, only notice upper SCSI layer once
wb_set_notice_upper_scsi_layer(host_data,
FALSE);
// ASC/ASCQ: not ready, media may have changed
//wb_make_sense_buffer( cmd, UNIT_ATTENTION, 0x28, 0x00 );
wb_make_sense_buffer(cmd, UNIT_ATTENTION,
0x28, 0x00);
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE,
CHECK_CONDITION);
}
} else {
// ASC/ASCQ: incompatible media installed
wb_make_sense_buffer(cmd, MEDIUM_ERROR, 0x30, 0x00);
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE,
CHECK_CONDITION);
}
} else {
// ASC/ASCQ: medium not present
wb_make_sense_buffer(cmd, UNIT_ATTENTION, 0x28, 0x00);
// wb_make_sense_buffer( cmd, NOT_READY, 0x3a, 0x00 );
cmd->result =
WB_CMD_RESULT(DID_OK, COMMAND_COMPLETE, CHECK_CONDITION);
}
LEAVE();
return ret;
}
MODULE_LICENSE("GPL");
static Scsi_Host_Template driver_template = WB_DRIVER;
static int __init wb_init(void)
{
struct device *pdev;
int ret = -1;
ENTER();
pdev = wb_detect(&driver_template);
if (!pdev) {
goto lab_end;
}
if (scsi_add_host(g_wb_host, pdev)) {
scsi_host_put(g_wb_host);
goto lab_end;
}
scsi_scan_host(g_wb_host);
ret = 0;
LEAVE();
lab_end:
return ret;
}
static void __exit wb_exit(void)
{
ENTER();
wb_release(g_wb_host);
scsi_host_put(g_wb_host);
LEAVE();
}
module_init(wb_init);
module_exit(wb_exit);
/*
-*- 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:
name <wmwu AT yyy.com>
Shuai Jin <jin.shuai AT gmail.com>
Yuan Mu <ymu AT winbond.com>
Dezheng Shen <dzshen AT gmail.com>
$Author: dzshen $
$Id: wbtable.c,v 1.5.2.3 2006/06/14 07:30:50 dzshen 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 "wbtable.h"
#include "wbdebug.h"
//table operating functions
static unsigned short g_table_logical_physical[WB_MAX_NUM_SEG *
WB_MAX_NUM_LOGICAL];
static unsigned short g_table_physical_logical[WB_MAX_NUM_SEG *
WB_MAX_NUM_PHYSICAL];
static unsigned short g_num_seg;
static unsigned int g_total_physical;
static unsigned int g_total_logical;
static unsigned short g_num_page_per_physical;
int wbtable_init( unsigned int blk_size, unsigned int total_physical )
{
int ret, i;
ENTER();
ret = -1;
switch (blk_size) {
// 16KB physical block size
case 0x10:
g_num_page_per_physical = 32;
break;
// 16KB physical block size
case 0x08:
g_num_page_per_physical = 16;
break;
default:
WB_PRINTK_ERROR("two possible values are 0x10 and 0x08, blk_size =
%x\n", blk_size);
goto lab_out;
break;
}
g_total_physical = total_physical;
g_num_seg = g_total_physical / WB_MAX_NUM_PHYSICAL;
// only 494 logical blocks are allowed in segment 0
g_total_logical = g_num_seg * WB_MAX_NUM_LOGICAL - 2;
for ( i = 0; i < g_total_physical; i++) {
g_table_physical_logical[i] = WB_TABLE_UNUSED;
}
for ( i = 0; i < g_total_logical; i++ ) {
g_table_logical_physical[i] = WB_TABLE_UNUSED;
}
ret = 0;
lab_out:
LEAVE();
return ret;
}
int wbtable_add_link_physical_logical( unsigned int physical, unsigned
int logical )
{
int ret;
ENTER();
if ( (ret = wbtable_chk_limit( 0, physical, logical) ) )
goto lab_out;
g_table_physical_logical[physical] = logical;
lab_out:
LEAVE();
return ret;
}
int wbtable_add_link_logical_physical( unsigned int logical, unsigned
int physical )
{
int ret;
ENTER();
if ( (ret = wbtable_chk_limit( 0, physical, logical ) ) )
goto lab_out;
g_table_logical_physical[logical] = physical;
lab_out:
LEAVE();
return ret;
}
// precondition:
int wbtable_del_link( unsigned int physical, unsigned int logical )
{
int ret;
ENTER();
if ( (ret = wbtable_chk_limit( 0, physical, logical ) ) )
goto lab_out;
if ( (WB_TABLE_DEFECT == g_table_physical_logical[physical]) ||
(WB_TABLE_UNUSED == g_table_physical_logical[physical])) {
WB_PRINTK_ERROR("physical block address mapping doesn't make sense\n");
ret = 1;
goto lab_out;
}
if ( (WB_TABLE_UNUSED == g_table_logical_physical[logical]) ) {
WB_PRINTK_ERROR("logical block address mapping doesn't make sense\n");
ret = 1;
goto lab_out;
}
g_table_physical_logical[physical] = WB_TABLE_UNUSED;
g_table_logical_physical[logical] = WB_TABLE_UNUSED;
lab_out:
LEAVE();
return ret;
}
// never fails
int wbtable_sanity_chk(void)
{
int physical, logical;
ENTER();
for ( physical = 0; physical < g_total_physical; physical++) {
logical = g_table_physical_logical[physical];
if ( (WB_TABLE_UNUSED == logical ) ||
(WB_TABLE_DEFECT == logical ) )
continue;
else {
if ( g_table_logical_physical[logical] != physical )
WB_PRINTK_ERROR("inconsistency in transformation table, "
"physical = %d logical = %d "
, physical, logical );
}
}
LEAVE();
return 0;
}
// never fails
int wbtable_dump(void)
{
int physical, newLine;
ENTER();
newLine = 0;
for ( physical = 0; physical < 0x20; physical++) {
WB_PRINTK("[%X]=", physical );
if ( WB_TABLE_UNUSED == g_table_physical_logical[physical])
WB_PRINTK("%s", "UN,");
else if ( WB_TABLE_DEFECT == g_table_physical_logical[physical])
WB_PRINTK("%s", "DE,");
else
WB_PRINTK("%X,", g_table_physical_logical[physical] );
if ( 0 == (newLine++ % 5) ) {
WB_PRINTK("\n");
schedule();
}
}
LEAVE();
return 0;
}
int wbtable_chk_limit( unsigned int seg, unsigned int physical,
unsigned int logical )
{
int ret;
ENTER();
ret = 0;
if ( WB_MAX_NUM_SEG < seg ) {
ret = 1;
WB_PRINTK_ERROR("segment out of range, seg = %d MAX = %d\n", seg,
WB_MAX_NUM_SEG);
goto lab_out;
}
if ( g_total_physical < physical ) {
WB_PRINTK_ERROR("physical block address out of range, physical = %d
MAX = %d\n",
physical, g_total_physical );
ret = 1;
goto lab_out;
}
if ( ( g_total_logical < logical) &&
(WB_TABLE_UNUSED != logical ) &&
(WB_TABLE_DEFECT != logical ) ) {
WB_PRINTK_ERROR("logical block address out of range, logical = %d \n",
logical);
ret = 1;
goto lab_out;
}
lab_out:
LEAVE();
return ret;
}
unsigned int wbtable_get_logical(unsigned int physical)
{
ENTER();
LEAVE();
return g_table_physical_logical[physical];
}
unsigned int wbtable_get_physical(unsigned int logical)
{
ENTER();
LEAVE();
return g_table_logical_physical[logical];
}
unsigned int wbtable_get_seg(unsigned int lba)
{
unsigned int seg, logical;
ENTER();
return (lba /g_num_page_per_physical +2)/496;
logical = lba / g_num_page_per_physical;
if (logical < 494)
seg = 0;
else {
seg = (logical - 494) / 496 + 1;
}
LEAVE();
return seg;
}
// the returned physical block is not erased yet
unsigned int wbtable_get_unused_physical(unsigned int seg)
{
int index, low, high;
ENTER();
low = seg * WB_MAX_NUM_PHYSICAL;
high = low + 512;
for ( index = low; index < high; index++ ){
if ( WB_TABLE_UNUSED == g_table_physical_logical[index] )
break;
}
if ( high == index )
WB_PRINTK_ERROR("danger!! can't find any unused physical block\n");
LEAVE();
return index;
}
unsigned int wbtable_add_link(unsigned int logical,unsigned int physical)
{
ENTER();
wbtable_add_link_logical_physical(logical, physical);
wbtable_add_link_physical_logical(physical, logical);
LEAVE();
return 0;
}
===========================================================================================
The privileged confidential information contained in this email is intended for use only by the addressees as indicated by the original sender of this email. If you are not the addressee indicated in this email or are not responsible for delivery of the email to such a person, please kindly reply to the sender indicating this fact and delete all copies of it from your computer and network server immediately. Your cooperation is highly appreciated. It is advised that any unauthorized use of confidential information of Winbond is strictly prohibited; and any information in this email irrelevant to the official business of Winbond shall be deemed as neither given nor endorsed by Winbond.
^ permalink raw reply [flat|nested] 12+ messages in thread