2013-08-23 18:35:45 +04:00
/*
* linux / drivers / scsi / esas2r / esas2r_flash . c
* For use with ATTO ExpressSAS R6xx SAS / SATA RAID controllers
*
* Copyright ( c ) 2001 - 2013 ATTO Technology , Inc .
* ( mailto : linuxdrivers @ attotech . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* NO WARRANTY
* THE PROGRAM IS PROVIDED ON AN " AS IS " BASIS , WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND , EITHER EXPRESS OR IMPLIED INCLUDING , WITHOUT
* LIMITATION , ANY WARRANTIES OR CONDITIONS OF TITLE , NON - INFRINGEMENT ,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE . Each Recipient is
* solely responsible for determining the appropriateness of using and
* distributing the Program and assumes all risks associated with its
* exercise of rights under this Agreement , including but not limited to
* the risks and costs of program errors , damage to or loss of data ,
* programs or equipment , and unavailability or interruption of operations .
*
* DISCLAIMER OF LIABILITY
* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING WITHOUT LIMITATION LOST PROFITS ) , HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR
* TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE
* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
* HEREUNDER , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 ,
* USA .
*/
# include "esas2r.h"
/* local macro defs */
# define esas2r_nvramcalc_cksum(n) \
( esas2r_calc_byte_cksum ( ( u8 * ) ( n ) , sizeof ( struct esas2r_sas_nvram ) , \
SASNVR_CKSUM_SEED ) )
# define esas2r_nvramcalc_xor_cksum(n) \
( esas2r_calc_byte_xor_cksum ( ( u8 * ) ( n ) , \
sizeof ( struct esas2r_sas_nvram ) , 0 ) )
# define ESAS2R_FS_DRVR_VER 2
static struct esas2r_sas_nvram default_sas_nvram = {
{ ' E ' , ' S ' , ' A ' , ' S ' } , /* signature */
SASNVR_VERSION , /* version */
0 , /* checksum */
31 , /* max_lun_for_target */
SASNVR_PCILAT_MAX , /* pci_latency */
SASNVR1_BOOT_DRVR , /* options1 */
SASNVR2_HEARTBEAT | SASNVR2_SINGLE_BUS /* options2 */
| SASNVR2_SW_MUX_CTRL ,
SASNVR_COAL_DIS , /* int_coalescing */
SASNVR_CMDTHR_NONE , /* cmd_throttle */
3 , /* dev_wait_time */
1 , /* dev_wait_count */
0 , /* spin_up_delay */
0 , /* ssp_align_rate */
{ 0x50 , 0x01 , 0x08 , 0x60 , /* sas_addr */
0x00 , 0x00 , 0x00 , 0x00 } ,
{ SASNVR_SPEED_AUTO } , /* phy_speed */
{ SASNVR_MUX_DISABLED } , /* SAS multiplexing */
{ 0 } , /* phy_flags */
SASNVR_SORT_SAS_ADDR , /* sort_type */
3 , /* dpm_reqcmd_lmt */
3 , /* dpm_stndby_time */
0 , /* dpm_active_time */
{ 0 } , /* phy_target_id */
SASNVR_VSMH_DISABLED , /* virt_ses_mode */
SASNVR_RWM_DEFAULT , /* read_write_mode */
0 , /* link down timeout */
{ 0 } /* reserved */
} ;
static u8 cmd_to_fls_func [ ] = {
0xFF ,
VDA_FLASH_READ ,
VDA_FLASH_BEGINW ,
VDA_FLASH_WRITE ,
VDA_FLASH_COMMIT ,
VDA_FLASH_CANCEL
} ;
static u8 esas2r_calc_byte_xor_cksum ( u8 * addr , u32 len , u8 seed )
{
u32 cksum = seed ;
u8 * p = ( u8 * ) & cksum ;
while ( len ) {
if ( ( ( uintptr_t ) addr & 3 ) = = 0 )
break ;
cksum = cksum ^ * addr ;
addr + + ;
len - - ;
}
while ( len > = sizeof ( u32 ) ) {
cksum = cksum ^ * ( u32 * ) addr ;
addr + = 4 ;
len - = 4 ;
}
while ( len - - ) {
cksum = cksum ^ * addr ;
addr + + ;
}
return p [ 0 ] ^ p [ 1 ] ^ p [ 2 ] ^ p [ 3 ] ;
}
static u8 esas2r_calc_byte_cksum ( void * addr , u32 len , u8 seed )
{
u8 * p = ( u8 * ) addr ;
u8 cksum = seed ;
while ( len - - )
cksum = cksum + p [ len ] ;
return cksum ;
}
/* Interrupt callback to process FM API write requests. */
static void esas2r_fmapi_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
struct atto_vda_flash_req * vrq = & rq - > vrq - > flash ;
struct esas2r_flash_context * fc =
( struct esas2r_flash_context * ) rq - > interrupt_cx ;
if ( rq - > req_stat = = RS_SUCCESS ) {
/* Last request was successful. See what to do now. */
switch ( vrq - > sub_func ) {
case VDA_FLASH_BEGINW :
if ( fc - > sgc . cur_offset = = NULL )
goto commit ;
vrq - > sub_func = VDA_FLASH_WRITE ;
rq - > req_stat = RS_PENDING ;
break ;
case VDA_FLASH_WRITE :
commit :
vrq - > sub_func = VDA_FLASH_COMMIT ;
rq - > req_stat = RS_PENDING ;
rq - > interrupt_cb = fc - > interrupt_cb ;
break ;
default :
break ;
}
}
if ( rq - > req_stat ! = RS_PENDING )
/*
* All done . call the real callback to complete the FM API
* request . We should only get here if a BEGINW or WRITE
* operation failed .
*/
( * fc - > interrupt_cb ) ( a , rq ) ;
}
/*
* Build a flash request based on the flash context . The request status
* is filled in on an error .
*/
static void build_flash_msg ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
struct esas2r_flash_context * fc =
( struct esas2r_flash_context * ) rq - > interrupt_cx ;
struct esas2r_sg_context * sgc = & fc - > sgc ;
u8 cksum = 0 ;
/* calculate the checksum */
if ( fc - > func = = VDA_FLASH_BEGINW ) {
if ( sgc - > cur_offset )
cksum = esas2r_calc_byte_xor_cksum ( sgc - > cur_offset ,
sgc - > length ,
0 ) ;
rq - > interrupt_cb = esas2r_fmapi_callback ;
} else {
rq - > interrupt_cb = fc - > interrupt_cb ;
}
esas2r_build_flash_req ( a ,
rq ,
fc - > func ,
cksum ,
fc - > flsh_addr ,
sgc - > length ) ;
esas2r_rq_free_sg_lists ( rq , a ) ;
/*
* remember the length we asked for . we have to keep track of
* the current amount done so we know how much to compare when
* doing the verification phase .
*/
fc - > curr_len = fc - > sgc . length ;
if ( sgc - > cur_offset ) {
/* setup the S/G context to build the S/G table */
esas2r_sgc_init ( sgc , a , rq , & rq - > vrq - > flash . data . sge [ 0 ] ) ;
if ( ! esas2r_build_sg_list ( a , rq , sgc ) ) {
rq - > req_stat = RS_BUSY ;
return ;
}
} else {
fc - > sgc . length = 0 ;
}
/* update the flsh_addr to the next one to write to */
fc - > flsh_addr + = fc - > curr_len ;
}
/* determine the method to process the flash request */
static bool load_image ( struct esas2r_adapter * a , struct esas2r_request * rq )
{
/*
* assume we have more to do . if we return with the status set to
* RS_PENDING , FM API tasks will continue .
*/
rq - > req_stat = RS_PENDING ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
/* not suppported for now */ ;
else
build_flash_msg ( a , rq ) ;
return rq - > req_stat = = RS_PENDING ;
}
/* boot image fixer uppers called before downloading the image. */
static void fix_bios ( struct esas2r_adapter * a , struct esas2r_flash_img * fi )
{
struct esas2r_component_header * ch = & fi - > cmp_hdr [ CH_IT_BIOS ] ;
struct esas2r_pc_image * pi ;
struct esas2r_boot_header * bh ;
pi = ( struct esas2r_pc_image * ) ( ( u8 * ) fi + ch - > image_offset ) ;
bh =
( struct esas2r_boot_header * ) ( ( u8 * ) pi +
le16_to_cpu ( pi - > header_offset ) ) ;
bh - > device_id = cpu_to_le16 ( a - > pcid - > device ) ;
/* Recalculate the checksum in the PNP header if there */
if ( pi - > pnp_offset ) {
u8 * pnp_header_bytes =
( ( u8 * ) pi + le16_to_cpu ( pi - > pnp_offset ) ) ;
/* Identifier - dword that starts at byte 10 */
* ( ( u32 * ) & pnp_header_bytes [ 10 ] ) =
cpu_to_le32 ( MAKEDWORD ( a - > pcid - > subsystem_vendor ,
a - > pcid - > subsystem_device ) ) ;
/* Checksum - byte 9 */
pnp_header_bytes [ 9 ] - = esas2r_calc_byte_cksum ( pnp_header_bytes ,
32 , 0 ) ;
}
/* Recalculate the checksum needed by the PC */
pi - > checksum = pi - > checksum -
esas2r_calc_byte_cksum ( ( u8 * ) pi , ch - > length , 0 ) ;
}
static void fix_efi ( struct esas2r_adapter * a , struct esas2r_flash_img * fi )
{
struct esas2r_component_header * ch = & fi - > cmp_hdr [ CH_IT_EFI ] ;
u32 len = ch - > length ;
u32 offset = ch - > image_offset ;
struct esas2r_efi_image * ei ;
struct esas2r_boot_header * bh ;
while ( len ) {
u32 thislen ;
ei = ( struct esas2r_efi_image * ) ( ( u8 * ) fi + offset ) ;
bh = ( struct esas2r_boot_header * ) ( ( u8 * ) ei +
le16_to_cpu (
ei - > header_offset ) ) ;
bh - > device_id = cpu_to_le16 ( a - > pcid - > device ) ;
thislen = ( u32 ) le16_to_cpu ( bh - > image_length ) * 512 ;
if ( thislen > len )
break ;
len - = thislen ;
offset + = thislen ;
}
}
/* Complete a FM API request with the specified status. */
static bool complete_fmapi_req ( struct esas2r_adapter * a ,
struct esas2r_request * rq , u8 fi_stat )
{
struct esas2r_flash_context * fc =
( struct esas2r_flash_context * ) rq - > interrupt_cx ;
struct esas2r_flash_img * fi = fc - > fi ;
fi - > status = fi_stat ;
fi - > driver_error = rq - > req_stat ;
rq - > interrupt_cb = NULL ;
rq - > req_stat = RS_SUCCESS ;
if ( fi_stat ! = FI_STAT_IMG_VER )
memset ( fc - > scratch , 0 , FM_BUF_SZ ) ;
esas2r_enable_heartbeat ( a ) ;
2013-10-01 22:26:01 +04:00
clear_bit ( AF_FLASH_LOCK , & a - > flags ) ;
2013-08-23 18:35:45 +04:00
return false ;
}
/* Process each phase of the flash download process. */
static void fw_download_proc ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
struct esas2r_flash_context * fc =
( struct esas2r_flash_context * ) rq - > interrupt_cx ;
struct esas2r_flash_img * fi = fc - > fi ;
struct esas2r_component_header * ch ;
u32 len ;
u8 * p , * q ;
/* If the previous operation failed, just return. */
if ( rq - > req_stat ! = RS_SUCCESS )
goto error ;
/*
* If an upload just completed and the compare length is non - zero ,
* then we just read back part of the image we just wrote . verify the
* section and continue reading until the entire image is verified .
*/
if ( fc - > func = = VDA_FLASH_READ
& & fc - > cmp_len ) {
ch = & fi - > cmp_hdr [ fc - > comp_typ ] ;
p = fc - > scratch ;
q = ( u8 * ) fi /* start of the whole gob */
+ ch - > image_offset /* start of the current image */
+ ch - > length /* end of the current image */
- fc - > cmp_len ; /* where we are now */
/*
* NOTE - curr_len is the exact count of bytes for the read
* even when the end is read and its not a full buffer
*/
for ( len = fc - > curr_len ; len ; len - - )
if ( * p + + ! = * q + + )
goto error ;
fc - > cmp_len - = fc - > curr_len ; /* # left to compare */
/* Update fc and determine the length for the next upload */
if ( fc - > cmp_len > FM_BUF_SZ )
fc - > sgc . length = FM_BUF_SZ ;
else
fc - > sgc . length = fc - > cmp_len ;
fc - > sgc . cur_offset = fc - > sgc_offset +
( ( u8 * ) fc - > scratch - ( u8 * ) fi ) ;
}
/*
* This code uses a ' while ' statement since the next component may
* have a length = zero . This can happen since some components are
* not required . At the end of this ' while ' we set up the length
* for the next request and therefore sgc . length can be = 0.
*/
while ( fc - > sgc . length = = 0 ) {
ch = & fi - > cmp_hdr [ fc - > comp_typ ] ;
switch ( fc - > task ) {
case FMTSK_ERASE_BOOT :
/* the BIOS image is written next */
ch = & fi - > cmp_hdr [ CH_IT_BIOS ] ;
if ( ch - > length = = 0 )
goto no_bios ;
fc - > task = FMTSK_WRTBIOS ;
fc - > func = VDA_FLASH_BEGINW ;
fc - > comp_typ = CH_IT_BIOS ;
fc - > flsh_addr = FLS_OFFSET_BOOT ;
fc - > sgc . length = ch - > length ;
fc - > sgc . cur_offset = fc - > sgc_offset +
ch - > image_offset ;
break ;
case FMTSK_WRTBIOS :
/*
* The BIOS image has been written - read it and
* verify it
*/
fc - > task = FMTSK_READBIOS ;
fc - > func = VDA_FLASH_READ ;
fc - > flsh_addr = FLS_OFFSET_BOOT ;
fc - > cmp_len = ch - > length ;
fc - > sgc . length = FM_BUF_SZ ;
fc - > sgc . cur_offset = fc - > sgc_offset
+ ( ( u8 * ) fc - > scratch -
( u8 * ) fi ) ;
break ;
case FMTSK_READBIOS :
no_bios :
/*
* Mark the component header status for the image
* completed
*/
ch - > status = CH_STAT_SUCCESS ;
/* The MAC image is written next */
ch = & fi - > cmp_hdr [ CH_IT_MAC ] ;
if ( ch - > length = = 0 )
goto no_mac ;
fc - > task = FMTSK_WRTMAC ;
fc - > func = VDA_FLASH_BEGINW ;
fc - > comp_typ = CH_IT_MAC ;
fc - > flsh_addr = FLS_OFFSET_BOOT
+ fi - > cmp_hdr [ CH_IT_BIOS ] . length ;
fc - > sgc . length = ch - > length ;
fc - > sgc . cur_offset = fc - > sgc_offset +
ch - > image_offset ;
break ;
case FMTSK_WRTMAC :
/* The MAC image has been written - read and verify */
fc - > task = FMTSK_READMAC ;
fc - > func = VDA_FLASH_READ ;
fc - > flsh_addr - = ch - > length ;
fc - > cmp_len = ch - > length ;
fc - > sgc . length = FM_BUF_SZ ;
fc - > sgc . cur_offset = fc - > sgc_offset
+ ( ( u8 * ) fc - > scratch -
( u8 * ) fi ) ;
break ;
case FMTSK_READMAC :
no_mac :
/*
* Mark the component header status for the image
* completed
*/
ch - > status = CH_STAT_SUCCESS ;
/* The EFI image is written next */
ch = & fi - > cmp_hdr [ CH_IT_EFI ] ;
if ( ch - > length = = 0 )
goto no_efi ;
fc - > task = FMTSK_WRTEFI ;
fc - > func = VDA_FLASH_BEGINW ;
fc - > comp_typ = CH_IT_EFI ;
fc - > flsh_addr = FLS_OFFSET_BOOT
+ fi - > cmp_hdr [ CH_IT_BIOS ] . length
+ fi - > cmp_hdr [ CH_IT_MAC ] . length ;
fc - > sgc . length = ch - > length ;
fc - > sgc . cur_offset = fc - > sgc_offset +
ch - > image_offset ;
break ;
case FMTSK_WRTEFI :
/* The EFI image has been written - read and verify */
fc - > task = FMTSK_READEFI ;
fc - > func = VDA_FLASH_READ ;
fc - > flsh_addr - = ch - > length ;
fc - > cmp_len = ch - > length ;
fc - > sgc . length = FM_BUF_SZ ;
fc - > sgc . cur_offset = fc - > sgc_offset
+ ( ( u8 * ) fc - > scratch -
( u8 * ) fi ) ;
break ;
case FMTSK_READEFI :
no_efi :
/*
* Mark the component header status for the image
* completed
*/
ch - > status = CH_STAT_SUCCESS ;
/* The CFG image is written next */
ch = & fi - > cmp_hdr [ CH_IT_CFG ] ;
if ( ch - > length = = 0 )
goto no_cfg ;
fc - > task = FMTSK_WRTCFG ;
fc - > func = VDA_FLASH_BEGINW ;
fc - > comp_typ = CH_IT_CFG ;
fc - > flsh_addr = FLS_OFFSET_CPYR - ch - > length ;
fc - > sgc . length = ch - > length ;
fc - > sgc . cur_offset = fc - > sgc_offset +
ch - > image_offset ;
break ;
case FMTSK_WRTCFG :
/* The CFG image has been written - read and verify */
fc - > task = FMTSK_READCFG ;
fc - > func = VDA_FLASH_READ ;
fc - > flsh_addr = FLS_OFFSET_CPYR - ch - > length ;
fc - > cmp_len = ch - > length ;
fc - > sgc . length = FM_BUF_SZ ;
fc - > sgc . cur_offset = fc - > sgc_offset
+ ( ( u8 * ) fc - > scratch -
( u8 * ) fi ) ;
break ;
case FMTSK_READCFG :
no_cfg :
/*
* Mark the component header status for the image
* completed
*/
ch - > status = CH_STAT_SUCCESS ;
/*
* The download is complete . If in degraded mode ,
* attempt a chip reset .
*/
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
esas2r_local_reset_adapter ( a ) ;
a - > flash_ver = fi - > cmp_hdr [ CH_IT_BIOS ] . version ;
esas2r_print_flash_rev ( a ) ;
/* Update the type of boot image on the card */
memcpy ( a - > image_type , fi - > rel_version ,
sizeof ( fi - > rel_version ) ) ;
complete_fmapi_req ( a , rq , FI_STAT_SUCCESS ) ;
return ;
}
/* If verifying, don't try reading more than what's there */
if ( fc - > func = = VDA_FLASH_READ
& & fc - > sgc . length > fc - > cmp_len )
fc - > sgc . length = fc - > cmp_len ;
}
/* Build the request to perform the next action */
if ( ! load_image ( a , rq ) ) {
error :
if ( fc - > comp_typ < fi - > num_comps ) {
ch = & fi - > cmp_hdr [ fc - > comp_typ ] ;
ch - > status = CH_STAT_FAILED ;
}
complete_fmapi_req ( a , rq , FI_STAT_FAILED ) ;
}
}
/* Determine the flash image adaptyp for this adapter */
static u8 get_fi_adap_type ( struct esas2r_adapter * a )
{
u8 type ;
/* use the device ID to get the correct adap_typ for this HBA */
switch ( a - > pcid - > device ) {
case ATTO_DID_INTEL_IOP348 :
type = FI_AT_SUN_LAKE ;
break ;
case ATTO_DID_MV_88RC9580 :
case ATTO_DID_MV_88RC9580TS :
case ATTO_DID_MV_88RC9580TSE :
case ATTO_DID_MV_88RC9580TL :
type = FI_AT_MV_9580 ;
break ;
default :
type = FI_AT_UNKNWN ;
break ;
}
return type ;
}
/* Size of config + copyright + flash_ver images, 0 for failure. */
static u32 chk_cfg ( u8 * cfg , u32 length , u32 * flash_ver )
{
u16 * pw = ( u16 * ) cfg - 1 ;
u32 sz = 0 ;
u32 len = length ;
if ( len = = 0 )
len = FM_BUF_SZ ;
if ( flash_ver )
* flash_ver = 0 ;
while ( true ) {
u16 type ;
u16 size ;
type = le16_to_cpu ( * pw - - ) ;
size = le16_to_cpu ( * pw - - ) ;
if ( type ! = FBT_CPYR
& & type ! = FBT_SETUP
& & type ! = FBT_FLASH_VER )
break ;
if ( type = = FBT_FLASH_VER
& & flash_ver )
* flash_ver = le32_to_cpu ( * ( u32 * ) ( pw - 1 ) ) ;
sz + = size + ( 2 * sizeof ( u16 ) ) ;
pw - = size / sizeof ( u16 ) ;
if ( sz > len - ( 2 * sizeof ( u16 ) ) )
break ;
}
/* See if we are comparing the size to the specified length */
if ( length & & sz ! = length )
return 0 ;
return sz ;
}
/* Verify that the boot image is valid */
static u8 chk_boot ( u8 * boot_img , u32 length )
{
struct esas2r_boot_image * bi = ( struct esas2r_boot_image * ) boot_img ;
u16 hdroffset = le16_to_cpu ( bi - > header_offset ) ;
struct esas2r_boot_header * bh ;
if ( bi - > signature ! = le16_to_cpu ( 0xaa55 )
| | ( long ) hdroffset >
( long ) ( 65536L - sizeof ( struct esas2r_boot_header ) )
| | ( hdroffset & 3 )
| | ( hdroffset < sizeof ( struct esas2r_boot_image ) )
| | ( ( u32 ) hdroffset + sizeof ( struct esas2r_boot_header ) > length ) )
return 0xff ;
bh = ( struct esas2r_boot_header * ) ( ( char * ) bi + hdroffset ) ;
if ( bh - > signature [ 0 ] ! = ' P '
| | bh - > signature [ 1 ] ! = ' C '
| | bh - > signature [ 2 ] ! = ' I '
| | bh - > signature [ 3 ] ! = ' R '
| | le16_to_cpu ( bh - > struct_length ) <
( u16 ) sizeof ( struct esas2r_boot_header )
| | bh - > class_code [ 2 ] ! = 0x01
| | bh - > class_code [ 1 ] ! = 0x04
| | bh - > class_code [ 0 ] ! = 0x00
| | ( bh - > code_type ! = CODE_TYPE_PC
& & bh - > code_type ! = CODE_TYPE_OPEN
& & bh - > code_type ! = CODE_TYPE_EFI ) )
return 0xff ;
return bh - > code_type ;
}
/* The sum of all the WORDS of the image */
static u16 calc_fi_checksum ( struct esas2r_flash_context * fc )
{
struct esas2r_flash_img * fi = fc - > fi ;
u16 cksum ;
u32 len ;
u16 * pw ;
for ( len = ( fi - > length - fc - > fi_hdr_len ) / 2 ,
pw = ( u16 * ) ( ( u8 * ) fi + fc - > fi_hdr_len ) ,
cksum = 0 ;
len ;
len - - , pw + + )
cksum = cksum + le16_to_cpu ( * pw ) ;
return cksum ;
}
/*
* Verify the flash image structure . The following verifications will
* be performed :
* 1 ) verify the fi_version is correct
* 2 ) verify the checksum of the entire image .
* 3 ) validate the adap_typ , action and length fields .
2014-11-27 19:13:01 +03:00
* 4 ) validate each component header . check the img_type and
2013-08-23 18:35:45 +04:00
* length fields
2014-11-27 19:13:01 +03:00
* 5 ) validate each component image . validate signatures and
2013-08-23 18:35:45 +04:00
* local checksums
*/
static bool verify_fi ( struct esas2r_adapter * a ,
struct esas2r_flash_context * fc )
{
struct esas2r_flash_img * fi = fc - > fi ;
u8 type ;
bool imgerr ;
u16 i ;
u32 len ;
struct esas2r_component_header * ch ;
/* Verify the length - length must even since we do a word checksum */
len = fi - > length ;
if ( ( len & 1 )
| | len < fc - > fi_hdr_len ) {
fi - > status = FI_STAT_LENGTH ;
return false ;
}
/* Get adapter type and verify type in flash image */
type = get_fi_adap_type ( a ) ;
if ( ( type = = FI_AT_UNKNWN ) | | ( fi - > adap_typ ! = type ) ) {
fi - > status = FI_STAT_ADAPTYP ;
return false ;
}
/*
* Loop through each component and verify the img_type and length
* fields . Keep a running count of the sizes sooze we can verify total
* size to additive size .
*/
imgerr = false ;
for ( i = 0 , len = 0 , ch = fi - > cmp_hdr ;
i < fi - > num_comps ;
i + + , ch + + ) {
bool cmperr = false ;
/*
* Verify that the component header has the same index as the
* image type . The headers must be ordered correctly
*/
if ( i ! = ch - > img_type ) {
imgerr = true ;
ch - > status = CH_STAT_INVALID ;
continue ;
}
switch ( ch - > img_type ) {
case CH_IT_BIOS :
type = CODE_TYPE_PC ;
break ;
case CH_IT_MAC :
type = CODE_TYPE_OPEN ;
break ;
case CH_IT_EFI :
type = CODE_TYPE_EFI ;
break ;
}
switch ( ch - > img_type ) {
case CH_IT_FW :
case CH_IT_NVR :
break ;
case CH_IT_BIOS :
case CH_IT_MAC :
case CH_IT_EFI :
if ( ch - > length & 0x1ff )
cmperr = true ;
/* Test if component image is present */
if ( ch - > length = = 0 )
break ;
/* Image is present - verify the image */
if ( chk_boot ( ( u8 * ) fi + ch - > image_offset , ch - > length )
! = type )
cmperr = true ;
break ;
case CH_IT_CFG :
/* Test if component image is present */
if ( ch - > length = = 0 ) {
cmperr = true ;
break ;
}
/* Image is present - verify the image */
if ( ! chk_cfg ( ( u8 * ) fi + ch - > image_offset + ch - > length ,
ch - > length , NULL ) )
cmperr = true ;
break ;
default :
fi - > status = FI_STAT_UNKNOWN ;
return false ;
}
if ( cmperr ) {
imgerr = true ;
ch - > status = CH_STAT_INVALID ;
} else {
ch - > status = CH_STAT_PENDING ;
len + = ch - > length ;
}
}
if ( imgerr ) {
fi - > status = FI_STAT_MISSING ;
return false ;
}
/* Compare fi->length to the sum of ch->length fields */
if ( len ! = fi - > length - fc - > fi_hdr_len ) {
fi - > status = FI_STAT_LENGTH ;
return false ;
}
/* Compute the checksum - it should come out zero */
if ( fi - > checksum ! = calc_fi_checksum ( fc ) ) {
fi - > status = FI_STAT_CHKSUM ;
return false ;
}
return true ;
}
/* Fill in the FS IOCTL response data from a completed request. */
static void esas2r_complete_fs_ioctl ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
struct esas2r_ioctl_fs * fs =
( struct esas2r_ioctl_fs * ) rq - > interrupt_cx ;
if ( rq - > vrq - > flash . sub_func = = VDA_FLASH_COMMIT )
esas2r_enable_heartbeat ( a ) ;
fs - > driver_error = rq - > req_stat ;
if ( fs - > driver_error = = RS_SUCCESS )
fs - > status = ATTO_STS_SUCCESS ;
else
fs - > status = ATTO_STS_FAILED ;
}
/* Prepare an FS IOCTL request to be sent to the firmware. */
bool esas2r_process_fs_ioctl ( struct esas2r_adapter * a ,
struct esas2r_ioctl_fs * fs ,
struct esas2r_request * rq ,
struct esas2r_sg_context * sgc )
{
u8 cmdcnt = ( u8 ) ARRAY_SIZE ( cmd_to_fls_func ) ;
struct esas2r_ioctlfs_command * fsc = & fs - > command ;
u8 func = 0 ;
u32 datalen ;
fs - > status = ATTO_STS_FAILED ;
fs - > driver_error = RS_PENDING ;
if ( fs - > version > ESAS2R_FS_VER ) {
fs - > status = ATTO_STS_INV_VERSION ;
return false ;
}
2013-08-29 23:55:40 +04:00
if ( fsc - > command > = cmdcnt ) {
fs - > status = ATTO_STS_INV_FUNC ;
return false ;
}
2013-08-23 18:35:45 +04:00
func = cmd_to_fls_func [ fsc - > command ] ;
2013-08-29 23:55:40 +04:00
if ( func = = 0xFF ) {
2013-08-23 18:35:45 +04:00
fs - > status = ATTO_STS_INV_FUNC ;
return false ;
}
if ( fsc - > command ! = ESAS2R_FS_CMD_CANCEL ) {
if ( ( a - > pcid - > device ! = ATTO_DID_MV_88RC9580
| | fs - > adap_type ! = ESAS2R_FS_AT_ESASRAID2 )
& & ( a - > pcid - > device ! = ATTO_DID_MV_88RC9580TS
| | fs - > adap_type ! = ESAS2R_FS_AT_TSSASRAID2 )
& & ( a - > pcid - > device ! = ATTO_DID_MV_88RC9580TSE
| | fs - > adap_type ! = ESAS2R_FS_AT_TSSASRAID2E )
& & ( a - > pcid - > device ! = ATTO_DID_MV_88RC9580TL
| | fs - > adap_type ! = ESAS2R_FS_AT_TLSASHBA ) ) {
fs - > status = ATTO_STS_INV_ADAPTER ;
return false ;
}
if ( fs - > driver_ver > ESAS2R_FS_DRVR_VER ) {
fs - > status = ATTO_STS_INV_DRVR_VER ;
return false ;
}
}
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) ) {
2013-08-23 18:35:45 +04:00
fs - > status = ATTO_STS_DEGRADED ;
return false ;
}
rq - > interrupt_cb = esas2r_complete_fs_ioctl ;
rq - > interrupt_cx = fs ;
datalen = le32_to_cpu ( fsc - > length ) ;
esas2r_build_flash_req ( a ,
rq ,
func ,
fsc - > checksum ,
le32_to_cpu ( fsc - > flash_addr ) ,
datalen ) ;
if ( func = = VDA_FLASH_WRITE
| | func = = VDA_FLASH_READ ) {
if ( datalen = = 0 ) {
fs - > status = ATTO_STS_INV_FUNC ;
return false ;
}
esas2r_sgc_init ( sgc , a , rq , rq - > vrq - > flash . data . sge ) ;
sgc - > length = datalen ;
if ( ! esas2r_build_sg_list ( a , rq , sgc ) ) {
fs - > status = ATTO_STS_OUT_OF_RSRC ;
return false ;
}
}
if ( func = = VDA_FLASH_COMMIT )
esas2r_disable_heartbeat ( a ) ;
esas2r_start_request ( a , rq ) ;
return true ;
}
static bool esas2r_flash_access ( struct esas2r_adapter * a , u32 function )
{
u32 starttime ;
u32 timeout ;
u32 intstat ;
u32 doorbell ;
/* Disable chip interrupts awhile */
if ( function = = DRBL_FLASH_REQ )
esas2r_disable_chip_interrupts ( a ) ;
/* Issue the request to the firmware */
esas2r_write_register_dword ( a , MU_DOORBELL_IN , function ) ;
/* Now wait for the firmware to process it */
starttime = jiffies_to_msecs ( jiffies ) ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_CHPRST_PENDING , & a - > flags ) | |
test_bit ( AF_DISC_PENDING , & a - > flags ) )
timeout = 40000 ;
else
timeout = 5000 ;
2013-08-23 18:35:45 +04:00
while ( true ) {
intstat = esas2r_read_register_dword ( a , MU_INT_STATUS_OUT ) ;
if ( intstat & MU_INTSTAT_DRBL ) {
/* Got a doorbell interrupt. Check for the function */
doorbell =
esas2r_read_register_dword ( a , MU_DOORBELL_OUT ) ;
esas2r_write_register_dword ( a , MU_DOORBELL_OUT ,
doorbell ) ;
if ( doorbell & function )
break ;
}
schedule_timeout_interruptible ( msecs_to_jiffies ( 100 ) ) ;
if ( ( jiffies_to_msecs ( jiffies ) - starttime ) > timeout ) {
/*
* Iimeout . If we were requesting flash access ,
* indicate we are done so the firmware knows we gave
* up . If this was a REQ , we also need to re - enable
* chip interrupts .
*/
if ( function = = DRBL_FLASH_REQ ) {
esas2r_hdebug ( " flash access timeout " ) ;
esas2r_write_register_dword ( a , MU_DOORBELL_IN ,
DRBL_FLASH_DONE ) ;
esas2r_enable_chip_interrupts ( a ) ;
} else {
esas2r_hdebug ( " flash release timeout " ) ;
}
return false ;
}
}
/* if we're done, re-enable chip interrupts */
if ( function = = DRBL_FLASH_DONE )
esas2r_enable_chip_interrupts ( a ) ;
return true ;
}
# define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)
bool esas2r_read_flash_block ( struct esas2r_adapter * a ,
void * to ,
u32 from ,
u32 size )
{
u8 * end = ( u8 * ) to ;
/* Try to acquire access to the flash */
if ( ! esas2r_flash_access ( a , DRBL_FLASH_REQ ) )
return false ;
while ( size ) {
u32 len ;
u32 offset ;
u32 iatvr ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF2_SERIAL_FLASH , & a - > flags2 ) )
2013-08-23 18:35:45 +04:00
iatvr = MW_DATA_ADDR_SER_FLASH + ( from & - WINDOW_SIZE ) ;
else
iatvr = MW_DATA_ADDR_PAR_FLASH + ( from & - WINDOW_SIZE ) ;
esas2r_map_data_window ( a , iatvr ) ;
offset = from & ( WINDOW_SIZE - 1 ) ;
len = size ;
if ( len > WINDOW_SIZE - offset )
len = WINDOW_SIZE - offset ;
from + = len ;
size - = len ;
while ( len - - ) {
* end + + = esas2r_read_data_byte ( a , offset ) ;
offset + + ;
}
}
/* Release flash access */
esas2r_flash_access ( a , DRBL_FLASH_DONE ) ;
return true ;
}
bool esas2r_read_flash_rev ( struct esas2r_adapter * a )
{
u8 bytes [ 256 ] ;
u16 * pw ;
u16 * pwstart ;
u16 type ;
u16 size ;
u32 sz ;
sz = sizeof ( bytes ) ;
pw = ( u16 * ) ( bytes + sz ) ;
pwstart = ( u16 * ) bytes + 2 ;
if ( ! esas2r_read_flash_block ( a , bytes , FLS_OFFSET_CPYR - sz , sz ) )
goto invalid_rev ;
while ( pw > = pwstart ) {
pw - - ;
type = le16_to_cpu ( * pw ) ;
pw - - ;
size = le16_to_cpu ( * pw ) ;
pw - = size / 2 ;
if ( type = = FBT_CPYR
| | type = = FBT_SETUP
| | pw < pwstart )
continue ;
if ( type = = FBT_FLASH_VER )
a - > flash_ver = le32_to_cpu ( * ( u32 * ) pw ) ;
break ;
}
invalid_rev :
return esas2r_print_flash_rev ( a ) ;
}
bool esas2r_print_flash_rev ( struct esas2r_adapter * a )
{
u16 year = LOWORD ( a - > flash_ver ) ;
u8 day = LOBYTE ( HIWORD ( a - > flash_ver ) ) ;
u8 month = HIBYTE ( HIWORD ( a - > flash_ver ) ) ;
if ( day = = 0
| | month = = 0
| | day > 31
| | month > 12
| | year < 2006
| | year > 9999 ) {
strcpy ( a - > flash_rev , " not found " ) ;
a - > flash_ver = 0 ;
return false ;
}
sprintf ( a - > flash_rev , " %02d/%02d/%04d " , month , day , year ) ;
esas2r_hdebug ( " flash version: %s " , a - > flash_rev ) ;
return true ;
}
/*
* Find the type of boot image type that is currently in the flash .
* The chip only has a 64 KB PCI - e expansion ROM
* size so only one image can be flashed at a time .
*/
bool esas2r_read_image_type ( struct esas2r_adapter * a )
{
u8 bytes [ 256 ] ;
struct esas2r_boot_image * bi ;
struct esas2r_boot_header * bh ;
u32 sz ;
u32 len ;
u32 offset ;
/* Start at the base of the boot images and look for a valid image */
sz = sizeof ( bytes ) ;
len = FLS_LENGTH_BOOT ;
offset = 0 ;
while ( true ) {
if ( ! esas2r_read_flash_block ( a , bytes , FLS_OFFSET_BOOT +
offset ,
sz ) )
goto invalid_rev ;
bi = ( struct esas2r_boot_image * ) bytes ;
bh = ( struct esas2r_boot_header * ) ( ( u8 * ) bi +
le16_to_cpu (
bi - > header_offset ) ) ;
if ( bi - > signature ! = cpu_to_le16 ( 0xAA55 ) )
goto invalid_rev ;
if ( bh - > code_type = = CODE_TYPE_PC ) {
strcpy ( a - > image_type , " BIOS " ) ;
return true ;
} else if ( bh - > code_type = = CODE_TYPE_EFI ) {
struct esas2r_efi_image * ei ;
/*
* So we have an EFI image . There are several types
* so see which architecture we have .
*/
ei = ( struct esas2r_efi_image * ) bytes ;
switch ( le16_to_cpu ( ei - > machine_type ) ) {
case EFI_MACHINE_IA32 :
strcpy ( a - > image_type , " EFI 32-bit " ) ;
return true ;
case EFI_MACHINE_IA64 :
strcpy ( a - > image_type , " EFI itanium " ) ;
return true ;
case EFI_MACHINE_X64 :
strcpy ( a - > image_type , " EFI 64-bit " ) ;
return true ;
case EFI_MACHINE_EBC :
strcpy ( a - > image_type , " EFI EBC " ) ;
return true ;
default :
goto invalid_rev ;
}
} else {
u32 thislen ;
/* jump to the next image */
thislen = ( u32 ) le16_to_cpu ( bh - > image_length ) * 512 ;
if ( thislen = = 0
| | thislen + offset > len
| | bh - > indicator = = INDICATOR_LAST )
break ;
offset + = thislen ;
}
}
invalid_rev :
strcpy ( a - > image_type , " no boot images " ) ;
return false ;
}
/*
* Read and validate current NVRAM parameters by accessing
* physical NVRAM directly . if currently stored parameters are
* invalid , use the defaults .
*/
bool esas2r_nvram_read_direct ( struct esas2r_adapter * a )
{
bool result ;
if ( down_interruptible ( & a - > nvram_semaphore ) )
return false ;
if ( ! esas2r_read_flash_block ( a , a - > nvram , FLS_OFFSET_NVR ,
sizeof ( struct esas2r_sas_nvram ) ) ) {
esas2r_hdebug ( " NVRAM read failed, using defaults " ) ;
return false ;
}
result = esas2r_nvram_validate ( a ) ;
up ( & a - > nvram_semaphore ) ;
return result ;
}
/* Interrupt callback to process NVRAM completions. */
static void esas2r_nvram_callback ( struct esas2r_adapter * a ,
struct esas2r_request * rq )
{
struct atto_vda_flash_req * vrq = & rq - > vrq - > flash ;
if ( rq - > req_stat = = RS_SUCCESS ) {
/* last request was successful. see what to do now. */
switch ( vrq - > sub_func ) {
case VDA_FLASH_BEGINW :
vrq - > sub_func = VDA_FLASH_WRITE ;
rq - > req_stat = RS_PENDING ;
break ;
case VDA_FLASH_WRITE :
vrq - > sub_func = VDA_FLASH_COMMIT ;
rq - > req_stat = RS_PENDING ;
break ;
case VDA_FLASH_READ :
esas2r_nvram_validate ( a ) ;
break ;
case VDA_FLASH_COMMIT :
default :
break ;
}
}
if ( rq - > req_stat ! = RS_PENDING ) {
/* update the NVRAM state */
if ( rq - > req_stat = = RS_SUCCESS )
2013-10-01 22:26:01 +04:00
set_bit ( AF_NVR_VALID , & a - > flags ) ;
2013-08-23 18:35:45 +04:00
else
2013-10-01 22:26:01 +04:00
clear_bit ( AF_NVR_VALID , & a - > flags ) ;
2013-08-23 18:35:45 +04:00
esas2r_enable_heartbeat ( a ) ;
up ( & a - > nvram_semaphore ) ;
}
}
/*
* Write the contents of nvram to the adapter ' s physical NVRAM .
* The cached copy of the NVRAM is also updated .
*/
bool esas2r_nvram_write ( struct esas2r_adapter * a , struct esas2r_request * rq ,
struct esas2r_sas_nvram * nvram )
{
struct esas2r_sas_nvram * n = nvram ;
u8 sas_address_bytes [ 8 ] ;
u32 * sas_address_dwords = ( u32 * ) & sas_address_bytes [ 0 ] ;
struct atto_vda_flash_req * vrq = & rq - > vrq - > flash ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
return false ;
if ( down_interruptible ( & a - > nvram_semaphore ) )
return false ;
if ( n = = NULL )
n = a - > nvram ;
/* check the validity of the settings */
if ( n - > version > SASNVR_VERSION ) {
up ( & a - > nvram_semaphore ) ;
return false ;
}
memcpy ( & sas_address_bytes [ 0 ] , n - > sas_addr , 8 ) ;
if ( sas_address_bytes [ 0 ] ! = 0x50
| | sas_address_bytes [ 1 ] ! = 0x01
| | sas_address_bytes [ 2 ] ! = 0x08
| | ( sas_address_bytes [ 3 ] & 0xF0 ) ! = 0x60
| | ( ( sas_address_bytes [ 3 ] & 0x0F ) | sas_address_dwords [ 1 ] ) = = 0 ) {
up ( & a - > nvram_semaphore ) ;
return false ;
}
if ( n - > spin_up_delay > SASNVR_SPINUP_MAX )
n - > spin_up_delay = SASNVR_SPINUP_MAX ;
n - > version = SASNVR_VERSION ;
n - > checksum = n - > checksum - esas2r_nvramcalc_cksum ( n ) ;
memcpy ( a - > nvram , n , sizeof ( struct esas2r_sas_nvram ) ) ;
/* write the NVRAM */
n = a - > nvram ;
esas2r_disable_heartbeat ( a ) ;
esas2r_build_flash_req ( a ,
rq ,
VDA_FLASH_BEGINW ,
esas2r_nvramcalc_xor_cksum ( n ) ,
FLS_OFFSET_NVR ,
sizeof ( struct esas2r_sas_nvram ) ) ;
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_LEGACY_SGE_MODE , & a - > flags ) ) {
2013-08-23 18:35:45 +04:00
vrq - > data . sge [ 0 ] . length =
cpu_to_le32 ( SGE_LAST |
sizeof ( struct esas2r_sas_nvram ) ) ;
vrq - > data . sge [ 0 ] . address = cpu_to_le64 (
a - > uncached_phys + ( u64 ) ( ( u8 * ) n - a - > uncached ) ) ;
} else {
vrq - > data . prde [ 0 ] . ctl_len =
cpu_to_le32 ( sizeof ( struct esas2r_sas_nvram ) ) ;
vrq - > data . prde [ 0 ] . address = cpu_to_le64 (
a - > uncached_phys
+ ( u64 ) ( ( u8 * ) n - a - > uncached ) ) ;
}
rq - > interrupt_cb = esas2r_nvram_callback ;
esas2r_start_request ( a , rq ) ;
return true ;
}
/* Validate the cached NVRAM. if the NVRAM is invalid, load the defaults. */
bool esas2r_nvram_validate ( struct esas2r_adapter * a )
{
struct esas2r_sas_nvram * n = a - > nvram ;
bool rslt = false ;
if ( n - > signature [ 0 ] ! = ' E '
| | n - > signature [ 1 ] ! = ' S '
| | n - > signature [ 2 ] ! = ' A '
| | n - > signature [ 3 ] ! = ' S ' ) {
esas2r_hdebug ( " invalid NVRAM signature " ) ;
} else if ( esas2r_nvramcalc_cksum ( n ) ) {
esas2r_hdebug ( " invalid NVRAM checksum " ) ;
} else if ( n - > version > SASNVR_VERSION ) {
esas2r_hdebug ( " invalid NVRAM version " ) ;
} else {
2013-10-01 22:26:01 +04:00
set_bit ( AF_NVR_VALID , & a - > flags ) ;
2013-08-23 18:35:45 +04:00
rslt = true ;
}
if ( rslt = = false ) {
esas2r_hdebug ( " using defaults " ) ;
esas2r_nvram_set_defaults ( a ) ;
}
return rslt ;
}
/*
* Set the cached NVRAM to defaults . note that this function sets the default
* NVRAM when it has been determined that the physical NVRAM is invalid .
* In this case , the SAS address is fabricated .
*/
void esas2r_nvram_set_defaults ( struct esas2r_adapter * a )
{
struct esas2r_sas_nvram * n = a - > nvram ;
u32 time = jiffies_to_msecs ( jiffies ) ;
2013-10-01 22:26:01 +04:00
clear_bit ( AF_NVR_VALID , & a - > flags ) ;
2013-08-29 23:55:44 +04:00
* n = default_sas_nvram ;
2013-08-23 18:35:45 +04:00
n - > sas_addr [ 3 ] | = 0x0F ;
n - > sas_addr [ 4 ] = HIBYTE ( LOWORD ( time ) ) ;
n - > sas_addr [ 5 ] = LOBYTE ( LOWORD ( time ) ) ;
n - > sas_addr [ 6 ] = a - > pcid - > bus - > number ;
n - > sas_addr [ 7 ] = a - > pcid - > devfn ;
}
void esas2r_nvram_get_defaults ( struct esas2r_adapter * a ,
struct esas2r_sas_nvram * nvram )
{
u8 sas_addr [ 8 ] ;
/*
* in case we are copying the defaults into the adapter , copy the SAS
* address out first .
*/
memcpy ( & sas_addr [ 0 ] , a - > nvram - > sas_addr , 8 ) ;
2013-08-29 23:55:44 +04:00
* nvram = default_sas_nvram ;
2013-08-23 18:35:45 +04:00
memcpy ( & nvram - > sas_addr [ 0 ] , & sas_addr [ 0 ] , 8 ) ;
}
bool esas2r_fm_api ( struct esas2r_adapter * a , struct esas2r_flash_img * fi ,
struct esas2r_request * rq , struct esas2r_sg_context * sgc )
{
struct esas2r_flash_context * fc = & a - > flash_context ;
u8 j ;
struct esas2r_component_header * ch ;
2013-10-01 22:26:01 +04:00
if ( test_and_set_bit ( AF_FLASH_LOCK , & a - > flags ) ) {
2013-08-23 18:35:45 +04:00
/* flag was already set */
fi - > status = FI_STAT_BUSY ;
return false ;
}
memcpy ( & fc - > sgc , sgc , sizeof ( struct esas2r_sg_context ) ) ;
sgc = & fc - > sgc ;
fc - > fi = fi ;
fc - > sgc_offset = sgc - > cur_offset ;
rq - > req_stat = RS_SUCCESS ;
rq - > interrupt_cx = fc ;
switch ( fi - > fi_version ) {
case FI_VERSION_1 :
fc - > scratch = ( ( struct esas2r_flash_img * ) fi ) - > scratch_buf ;
fc - > num_comps = FI_NUM_COMPS_V1 ;
fc - > fi_hdr_len = sizeof ( struct esas2r_flash_img ) ;
break ;
default :
return complete_fmapi_req ( a , rq , FI_STAT_IMG_VER ) ;
}
2013-10-01 22:26:01 +04:00
if ( test_bit ( AF_DEGRADED_MODE , & a - > flags ) )
2013-08-23 18:35:45 +04:00
return complete_fmapi_req ( a , rq , FI_STAT_DEGRADED ) ;
switch ( fi - > action ) {
case FI_ACT_DOWN : /* Download the components */
/* Verify the format of the flash image */
if ( ! verify_fi ( a , fc ) )
return complete_fmapi_req ( a , rq , fi - > status ) ;
/* Adjust the BIOS fields that are dependent on the HBA */
ch = & fi - > cmp_hdr [ CH_IT_BIOS ] ;
if ( ch - > length )
fix_bios ( a , fi ) ;
/* Adjust the EFI fields that are dependent on the HBA */
ch = & fi - > cmp_hdr [ CH_IT_EFI ] ;
if ( ch - > length )
fix_efi ( a , fi ) ;
/*
* Since the image was just modified , compute the checksum on
* the modified image . First update the CRC for the composite
* expansion ROM image .
*/
fi - > checksum = calc_fi_checksum ( fc ) ;
/* Disable the heartbeat */
esas2r_disable_heartbeat ( a ) ;
/* Now start up the download sequence */
fc - > task = FMTSK_ERASE_BOOT ;
fc - > func = VDA_FLASH_BEGINW ;
fc - > comp_typ = CH_IT_CFG ;
fc - > flsh_addr = FLS_OFFSET_BOOT ;
fc - > sgc . length = FLS_LENGTH_BOOT ;
fc - > sgc . cur_offset = NULL ;
/* Setup the callback address */
fc - > interrupt_cb = fw_download_proc ;
break ;
case FI_ACT_UPSZ : /* Get upload sizes */
fi - > adap_typ = get_fi_adap_type ( a ) ;
fi - > flags = 0 ;
fi - > num_comps = fc - > num_comps ;
fi - > length = fc - > fi_hdr_len ;
/* Report the type of boot image in the rel_version string */
memcpy ( fi - > rel_version , a - > image_type ,
sizeof ( fi - > rel_version ) ) ;
/* Build the component headers */
for ( j = 0 , ch = fi - > cmp_hdr ;
j < fi - > num_comps ;
j + + , ch + + ) {
ch - > img_type = j ;
ch - > status = CH_STAT_PENDING ;
ch - > length = 0 ;
ch - > version = 0xffffffff ;
ch - > image_offset = 0 ;
ch - > pad [ 0 ] = 0 ;
ch - > pad [ 1 ] = 0 ;
}
if ( a - > flash_ver ! = 0 ) {
fi - > cmp_hdr [ CH_IT_BIOS ] . version =
fi - > cmp_hdr [ CH_IT_MAC ] . version =
fi - > cmp_hdr [ CH_IT_EFI ] . version =
fi - > cmp_hdr [ CH_IT_CFG ] . version
= a - > flash_ver ;
fi - > cmp_hdr [ CH_IT_BIOS ] . status =
fi - > cmp_hdr [ CH_IT_MAC ] . status =
fi - > cmp_hdr [ CH_IT_EFI ] . status =
fi - > cmp_hdr [ CH_IT_CFG ] . status =
CH_STAT_SUCCESS ;
return complete_fmapi_req ( a , rq , FI_STAT_SUCCESS ) ;
}
/* fall through */
case FI_ACT_UP : /* Upload the components */
default :
return complete_fmapi_req ( a , rq , FI_STAT_INVALID ) ;
}
/*
* If we make it here , fc has been setup to do the first task . Call
* load_image to format the request , start it , and get out . The
* interrupt code will call the callback when the first message is
* complete .
*/
if ( ! load_image ( a , rq ) )
return complete_fmapi_req ( a , rq , FI_STAT_FAILED ) ;
esas2r_start_request ( a , rq ) ;
return true ;
}