mirror of
https://github.com/torvalds/linux.git
synced 2024-12-24 11:51:27 +00:00
9588d24e36
Previously the code embedded the kernel's test_bit/clear_bit functions in wrappers that accepted u32 parameters. The wrapper cast these parameters to longs before passing them to the kernel's bit functions. This did not work properly on platforms with 64-bit longs. Signed-off-by: Bradley Grove <bgrove@attotech.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
1522 lines
38 KiB
C
1522 lines
38 KiB
C
|
|
/*
|
|
* 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;
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags))
|
|
/* 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);
|
|
clear_bit(AF_FLASH_LOCK, &a->flags);
|
|
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.
|
|
*/
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags))
|
|
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.
|
|
* 4) valdiate each component header. check the img_type and
|
|
* length fields
|
|
* 5) valdiate each component image. validate signatures and
|
|
* 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;
|
|
}
|
|
|
|
if (fsc->command >= cmdcnt) {
|
|
fs->status = ATTO_STS_INV_FUNC;
|
|
return false;
|
|
}
|
|
|
|
func = cmd_to_fls_func[fsc->command];
|
|
if (func == 0xFF) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
|
|
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);
|
|
|
|
if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
|
|
test_bit(AF_DISC_PENDING, &a->flags))
|
|
timeout = 40000;
|
|
else
|
|
timeout = 5000;
|
|
|
|
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;
|
|
|
|
if (test_bit(AF2_SERIAL_FLASH, &a->flags2))
|
|
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)
|
|
set_bit(AF_NVR_VALID, &a->flags);
|
|
else
|
|
clear_bit(AF_NVR_VALID, &a->flags);
|
|
|
|
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;
|
|
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags))
|
|
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));
|
|
|
|
if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
|
|
|
|
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 {
|
|
set_bit(AF_NVR_VALID, &a->flags);
|
|
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);
|
|
|
|
clear_bit(AF_NVR_VALID, &a->flags);
|
|
*n = default_sas_nvram;
|
|
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);
|
|
*nvram = default_sas_nvram;
|
|
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;
|
|
|
|
if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) {
|
|
/* 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);
|
|
}
|
|
|
|
if (test_bit(AF_DEGRADED_MODE, &a->flags))
|
|
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;
|
|
}
|