126780d9eSBradley Grove
226780d9eSBradley Grove /*
326780d9eSBradley Grove * linux/drivers/scsi/esas2r/esas2r_flash.c
426780d9eSBradley Grove * For use with ATTO ExpressSAS R6xx SAS/SATA RAID controllers
526780d9eSBradley Grove *
626780d9eSBradley Grove * Copyright (c) 2001-2013 ATTO Technology, Inc.
726780d9eSBradley Grove * (mailto:linuxdrivers@attotech.com)
826780d9eSBradley Grove *
926780d9eSBradley Grove * This program is free software; you can redistribute it and/or
1026780d9eSBradley Grove * modify it under the terms of the GNU General Public License
1126780d9eSBradley Grove * as published by the Free Software Foundation; either version 2
1226780d9eSBradley Grove * of the License, or (at your option) any later version.
1326780d9eSBradley Grove *
1426780d9eSBradley Grove * This program is distributed in the hope that it will be useful,
1526780d9eSBradley Grove * but WITHOUT ANY WARRANTY; without even the implied warranty of
1626780d9eSBradley Grove * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1726780d9eSBradley Grove * GNU General Public License for more details.
1826780d9eSBradley Grove *
1926780d9eSBradley Grove * NO WARRANTY
2026780d9eSBradley Grove * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
2126780d9eSBradley Grove * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
2226780d9eSBradley Grove * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
2326780d9eSBradley Grove * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
2426780d9eSBradley Grove * solely responsible for determining the appropriateness of using and
2526780d9eSBradley Grove * distributing the Program and assumes all risks associated with its
2626780d9eSBradley Grove * exercise of rights under this Agreement, including but not limited to
2726780d9eSBradley Grove * the risks and costs of program errors, damage to or loss of data,
2826780d9eSBradley Grove * programs or equipment, and unavailability or interruption of operations.
2926780d9eSBradley Grove *
3026780d9eSBradley Grove * DISCLAIMER OF LIABILITY
3126780d9eSBradley Grove * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
3226780d9eSBradley Grove * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3326780d9eSBradley Grove * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
3426780d9eSBradley Grove * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
3526780d9eSBradley Grove * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
3626780d9eSBradley Grove * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
3726780d9eSBradley Grove * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
3826780d9eSBradley Grove *
3926780d9eSBradley Grove * You should have received a copy of the GNU General Public License
4026780d9eSBradley Grove * along with this program; if not, write to the Free Software
4126780d9eSBradley Grove * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
4226780d9eSBradley Grove * USA.
4326780d9eSBradley Grove */
4426780d9eSBradley Grove
4526780d9eSBradley Grove #include "esas2r.h"
4626780d9eSBradley Grove
4726780d9eSBradley Grove /* local macro defs */
4826780d9eSBradley Grove #define esas2r_nvramcalc_cksum(n) \
4926780d9eSBradley Grove (esas2r_calc_byte_cksum((u8 *)(n), sizeof(struct esas2r_sas_nvram), \
5026780d9eSBradley Grove SASNVR_CKSUM_SEED))
5126780d9eSBradley Grove #define esas2r_nvramcalc_xor_cksum(n) \
5226780d9eSBradley Grove (esas2r_calc_byte_xor_cksum((u8 *)(n), \
5326780d9eSBradley Grove sizeof(struct esas2r_sas_nvram), 0))
5426780d9eSBradley Grove
5526780d9eSBradley Grove #define ESAS2R_FS_DRVR_VER 2
5626780d9eSBradley Grove
5726780d9eSBradley Grove static struct esas2r_sas_nvram default_sas_nvram = {
5826780d9eSBradley Grove { 'E', 'S', 'A', 'S' }, /* signature */
5926780d9eSBradley Grove SASNVR_VERSION, /* version */
6026780d9eSBradley Grove 0, /* checksum */
6126780d9eSBradley Grove 31, /* max_lun_for_target */
6226780d9eSBradley Grove SASNVR_PCILAT_MAX, /* pci_latency */
6326780d9eSBradley Grove SASNVR1_BOOT_DRVR, /* options1 */
6426780d9eSBradley Grove SASNVR2_HEARTBEAT | SASNVR2_SINGLE_BUS /* options2 */
6526780d9eSBradley Grove | SASNVR2_SW_MUX_CTRL,
6626780d9eSBradley Grove SASNVR_COAL_DIS, /* int_coalescing */
6726780d9eSBradley Grove SASNVR_CMDTHR_NONE, /* cmd_throttle */
6826780d9eSBradley Grove 3, /* dev_wait_time */
6926780d9eSBradley Grove 1, /* dev_wait_count */
7026780d9eSBradley Grove 0, /* spin_up_delay */
7126780d9eSBradley Grove 0, /* ssp_align_rate */
7226780d9eSBradley Grove { 0x50, 0x01, 0x08, 0x60, /* sas_addr */
7326780d9eSBradley Grove 0x00, 0x00, 0x00, 0x00 },
7426780d9eSBradley Grove { SASNVR_SPEED_AUTO }, /* phy_speed */
7526780d9eSBradley Grove { SASNVR_MUX_DISABLED }, /* SAS multiplexing */
7626780d9eSBradley Grove { 0 }, /* phy_flags */
7726780d9eSBradley Grove SASNVR_SORT_SAS_ADDR, /* sort_type */
7826780d9eSBradley Grove 3, /* dpm_reqcmd_lmt */
7926780d9eSBradley Grove 3, /* dpm_stndby_time */
8026780d9eSBradley Grove 0, /* dpm_active_time */
8126780d9eSBradley Grove { 0 }, /* phy_target_id */
8226780d9eSBradley Grove SASNVR_VSMH_DISABLED, /* virt_ses_mode */
8326780d9eSBradley Grove SASNVR_RWM_DEFAULT, /* read_write_mode */
8426780d9eSBradley Grove 0, /* link down timeout */
8526780d9eSBradley Grove { 0 } /* reserved */
8626780d9eSBradley Grove };
8726780d9eSBradley Grove
8826780d9eSBradley Grove static u8 cmd_to_fls_func[] = {
8926780d9eSBradley Grove 0xFF,
9026780d9eSBradley Grove VDA_FLASH_READ,
9126780d9eSBradley Grove VDA_FLASH_BEGINW,
9226780d9eSBradley Grove VDA_FLASH_WRITE,
9326780d9eSBradley Grove VDA_FLASH_COMMIT,
9426780d9eSBradley Grove VDA_FLASH_CANCEL
9526780d9eSBradley Grove };
9626780d9eSBradley Grove
esas2r_calc_byte_xor_cksum(u8 * addr,u32 len,u8 seed)9726780d9eSBradley Grove static u8 esas2r_calc_byte_xor_cksum(u8 *addr, u32 len, u8 seed)
9826780d9eSBradley Grove {
9926780d9eSBradley Grove u32 cksum = seed;
10026780d9eSBradley Grove u8 *p = (u8 *)&cksum;
10126780d9eSBradley Grove
10226780d9eSBradley Grove while (len) {
10326780d9eSBradley Grove if (((uintptr_t)addr & 3) == 0)
10426780d9eSBradley Grove break;
10526780d9eSBradley Grove
10626780d9eSBradley Grove cksum = cksum ^ *addr;
10726780d9eSBradley Grove addr++;
10826780d9eSBradley Grove len--;
10926780d9eSBradley Grove }
11026780d9eSBradley Grove while (len >= sizeof(u32)) {
11126780d9eSBradley Grove cksum = cksum ^ *(u32 *)addr;
11226780d9eSBradley Grove addr += 4;
11326780d9eSBradley Grove len -= 4;
11426780d9eSBradley Grove }
11526780d9eSBradley Grove while (len--) {
11626780d9eSBradley Grove cksum = cksum ^ *addr;
11726780d9eSBradley Grove addr++;
11826780d9eSBradley Grove }
11926780d9eSBradley Grove return p[0] ^ p[1] ^ p[2] ^ p[3];
12026780d9eSBradley Grove }
12126780d9eSBradley Grove
esas2r_calc_byte_cksum(void * addr,u32 len,u8 seed)12226780d9eSBradley Grove static u8 esas2r_calc_byte_cksum(void *addr, u32 len, u8 seed)
12326780d9eSBradley Grove {
12426780d9eSBradley Grove u8 *p = (u8 *)addr;
12526780d9eSBradley Grove u8 cksum = seed;
12626780d9eSBradley Grove
12726780d9eSBradley Grove while (len--)
12826780d9eSBradley Grove cksum = cksum + p[len];
12926780d9eSBradley Grove return cksum;
13026780d9eSBradley Grove }
13126780d9eSBradley Grove
13226780d9eSBradley Grove /* Interrupt callback to process FM API write requests. */
esas2r_fmapi_callback(struct esas2r_adapter * a,struct esas2r_request * rq)13326780d9eSBradley Grove static void esas2r_fmapi_callback(struct esas2r_adapter *a,
13426780d9eSBradley Grove struct esas2r_request *rq)
13526780d9eSBradley Grove {
13626780d9eSBradley Grove struct atto_vda_flash_req *vrq = &rq->vrq->flash;
13726780d9eSBradley Grove struct esas2r_flash_context *fc =
13826780d9eSBradley Grove (struct esas2r_flash_context *)rq->interrupt_cx;
13926780d9eSBradley Grove
14026780d9eSBradley Grove if (rq->req_stat == RS_SUCCESS) {
14126780d9eSBradley Grove /* Last request was successful. See what to do now. */
14226780d9eSBradley Grove switch (vrq->sub_func) {
14326780d9eSBradley Grove case VDA_FLASH_BEGINW:
14426780d9eSBradley Grove if (fc->sgc.cur_offset == NULL)
14526780d9eSBradley Grove goto commit;
14626780d9eSBradley Grove
14726780d9eSBradley Grove vrq->sub_func = VDA_FLASH_WRITE;
14826780d9eSBradley Grove rq->req_stat = RS_PENDING;
14926780d9eSBradley Grove break;
15026780d9eSBradley Grove
15126780d9eSBradley Grove case VDA_FLASH_WRITE:
15226780d9eSBradley Grove commit:
15326780d9eSBradley Grove vrq->sub_func = VDA_FLASH_COMMIT;
15426780d9eSBradley Grove rq->req_stat = RS_PENDING;
15526780d9eSBradley Grove rq->interrupt_cb = fc->interrupt_cb;
15626780d9eSBradley Grove break;
15726780d9eSBradley Grove
15826780d9eSBradley Grove default:
15926780d9eSBradley Grove break;
16026780d9eSBradley Grove }
16126780d9eSBradley Grove }
16226780d9eSBradley Grove
16326780d9eSBradley Grove if (rq->req_stat != RS_PENDING)
16426780d9eSBradley Grove /*
16526780d9eSBradley Grove * All done. call the real callback to complete the FM API
16626780d9eSBradley Grove * request. We should only get here if a BEGINW or WRITE
16726780d9eSBradley Grove * operation failed.
16826780d9eSBradley Grove */
16926780d9eSBradley Grove (*fc->interrupt_cb)(a, rq);
17026780d9eSBradley Grove }
17126780d9eSBradley Grove
17226780d9eSBradley Grove /*
17326780d9eSBradley Grove * Build a flash request based on the flash context. The request status
17426780d9eSBradley Grove * is filled in on an error.
17526780d9eSBradley Grove */
build_flash_msg(struct esas2r_adapter * a,struct esas2r_request * rq)17626780d9eSBradley Grove static void build_flash_msg(struct esas2r_adapter *a,
17726780d9eSBradley Grove struct esas2r_request *rq)
17826780d9eSBradley Grove {
17926780d9eSBradley Grove struct esas2r_flash_context *fc =
18026780d9eSBradley Grove (struct esas2r_flash_context *)rq->interrupt_cx;
18126780d9eSBradley Grove struct esas2r_sg_context *sgc = &fc->sgc;
18226780d9eSBradley Grove u8 cksum = 0;
18326780d9eSBradley Grove
18426780d9eSBradley Grove /* calculate the checksum */
18526780d9eSBradley Grove if (fc->func == VDA_FLASH_BEGINW) {
18626780d9eSBradley Grove if (sgc->cur_offset)
18726780d9eSBradley Grove cksum = esas2r_calc_byte_xor_cksum(sgc->cur_offset,
18826780d9eSBradley Grove sgc->length,
18926780d9eSBradley Grove 0);
19026780d9eSBradley Grove rq->interrupt_cb = esas2r_fmapi_callback;
19126780d9eSBradley Grove } else {
19226780d9eSBradley Grove rq->interrupt_cb = fc->interrupt_cb;
19326780d9eSBradley Grove }
19426780d9eSBradley Grove esas2r_build_flash_req(a,
19526780d9eSBradley Grove rq,
19626780d9eSBradley Grove fc->func,
19726780d9eSBradley Grove cksum,
19826780d9eSBradley Grove fc->flsh_addr,
19926780d9eSBradley Grove sgc->length);
20026780d9eSBradley Grove
20126780d9eSBradley Grove esas2r_rq_free_sg_lists(rq, a);
20226780d9eSBradley Grove
20326780d9eSBradley Grove /*
20426780d9eSBradley Grove * remember the length we asked for. we have to keep track of
20526780d9eSBradley Grove * the current amount done so we know how much to compare when
20626780d9eSBradley Grove * doing the verification phase.
20726780d9eSBradley Grove */
20826780d9eSBradley Grove fc->curr_len = fc->sgc.length;
20926780d9eSBradley Grove
21026780d9eSBradley Grove if (sgc->cur_offset) {
21126780d9eSBradley Grove /* setup the S/G context to build the S/G table */
21226780d9eSBradley Grove esas2r_sgc_init(sgc, a, rq, &rq->vrq->flash.data.sge[0]);
21326780d9eSBradley Grove
21426780d9eSBradley Grove if (!esas2r_build_sg_list(a, rq, sgc)) {
21526780d9eSBradley Grove rq->req_stat = RS_BUSY;
21626780d9eSBradley Grove return;
21726780d9eSBradley Grove }
21826780d9eSBradley Grove } else {
21926780d9eSBradley Grove fc->sgc.length = 0;
22026780d9eSBradley Grove }
22126780d9eSBradley Grove
22226780d9eSBradley Grove /* update the flsh_addr to the next one to write to */
22326780d9eSBradley Grove fc->flsh_addr += fc->curr_len;
22426780d9eSBradley Grove }
22526780d9eSBradley Grove
22626780d9eSBradley Grove /* determine the method to process the flash request */
load_image(struct esas2r_adapter * a,struct esas2r_request * rq)22726780d9eSBradley Grove static bool load_image(struct esas2r_adapter *a, struct esas2r_request *rq)
22826780d9eSBradley Grove {
22926780d9eSBradley Grove /*
23026780d9eSBradley Grove * assume we have more to do. if we return with the status set to
23126780d9eSBradley Grove * RS_PENDING, FM API tasks will continue.
23226780d9eSBradley Grove */
23326780d9eSBradley Grove rq->req_stat = RS_PENDING;
2349588d24eSBradley Grove if (test_bit(AF_DEGRADED_MODE, &a->flags))
235*0676f275SJulia Lawall /* not supported for now */;
23626780d9eSBradley Grove else
23726780d9eSBradley Grove build_flash_msg(a, rq);
23826780d9eSBradley Grove
23926780d9eSBradley Grove return rq->req_stat == RS_PENDING;
24026780d9eSBradley Grove }
24126780d9eSBradley Grove
24226780d9eSBradley Grove /* boot image fixer uppers called before downloading the image. */
fix_bios(struct esas2r_adapter * a,struct esas2r_flash_img * fi)24326780d9eSBradley Grove static void fix_bios(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
24426780d9eSBradley Grove {
24526780d9eSBradley Grove struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_BIOS];
24626780d9eSBradley Grove struct esas2r_pc_image *pi;
24726780d9eSBradley Grove struct esas2r_boot_header *bh;
24826780d9eSBradley Grove
24926780d9eSBradley Grove pi = (struct esas2r_pc_image *)((u8 *)fi + ch->image_offset);
25026780d9eSBradley Grove bh =
25126780d9eSBradley Grove (struct esas2r_boot_header *)((u8 *)pi +
25226780d9eSBradley Grove le16_to_cpu(pi->header_offset));
25326780d9eSBradley Grove bh->device_id = cpu_to_le16(a->pcid->device);
25426780d9eSBradley Grove
25526780d9eSBradley Grove /* Recalculate the checksum in the PNP header if there */
25626780d9eSBradley Grove if (pi->pnp_offset) {
25726780d9eSBradley Grove u8 *pnp_header_bytes =
25826780d9eSBradley Grove ((u8 *)pi + le16_to_cpu(pi->pnp_offset));
25926780d9eSBradley Grove
26026780d9eSBradley Grove /* Identifier - dword that starts at byte 10 */
26126780d9eSBradley Grove *((u32 *)&pnp_header_bytes[10]) =
26226780d9eSBradley Grove cpu_to_le32(MAKEDWORD(a->pcid->subsystem_vendor,
26326780d9eSBradley Grove a->pcid->subsystem_device));
26426780d9eSBradley Grove
26526780d9eSBradley Grove /* Checksum - byte 9 */
26626780d9eSBradley Grove pnp_header_bytes[9] -= esas2r_calc_byte_cksum(pnp_header_bytes,
26726780d9eSBradley Grove 32, 0);
26826780d9eSBradley Grove }
26926780d9eSBradley Grove
27026780d9eSBradley Grove /* Recalculate the checksum needed by the PC */
27126780d9eSBradley Grove pi->checksum = pi->checksum -
27226780d9eSBradley Grove esas2r_calc_byte_cksum((u8 *)pi, ch->length, 0);
27326780d9eSBradley Grove }
27426780d9eSBradley Grove
fix_efi(struct esas2r_adapter * a,struct esas2r_flash_img * fi)27526780d9eSBradley Grove static void fix_efi(struct esas2r_adapter *a, struct esas2r_flash_img *fi)
27626780d9eSBradley Grove {
27726780d9eSBradley Grove struct esas2r_component_header *ch = &fi->cmp_hdr[CH_IT_EFI];
27826780d9eSBradley Grove u32 len = ch->length;
27926780d9eSBradley Grove u32 offset = ch->image_offset;
28026780d9eSBradley Grove struct esas2r_efi_image *ei;
28126780d9eSBradley Grove struct esas2r_boot_header *bh;
28226780d9eSBradley Grove
28326780d9eSBradley Grove while (len) {
28426780d9eSBradley Grove u32 thislen;
28526780d9eSBradley Grove
28626780d9eSBradley Grove ei = (struct esas2r_efi_image *)((u8 *)fi + offset);
28726780d9eSBradley Grove bh = (struct esas2r_boot_header *)((u8 *)ei +
28826780d9eSBradley Grove le16_to_cpu(
28926780d9eSBradley Grove ei->header_offset));
29026780d9eSBradley Grove bh->device_id = cpu_to_le16(a->pcid->device);
29126780d9eSBradley Grove thislen = (u32)le16_to_cpu(bh->image_length) * 512;
29226780d9eSBradley Grove
29326780d9eSBradley Grove if (thislen > len)
29426780d9eSBradley Grove break;
29526780d9eSBradley Grove
29626780d9eSBradley Grove len -= thislen;
29726780d9eSBradley Grove offset += thislen;
29826780d9eSBradley Grove }
29926780d9eSBradley Grove }
30026780d9eSBradley Grove
30126780d9eSBradley Grove /* Complete a FM API request with the specified status. */
complete_fmapi_req(struct esas2r_adapter * a,struct esas2r_request * rq,u8 fi_stat)30226780d9eSBradley Grove static bool complete_fmapi_req(struct esas2r_adapter *a,
30326780d9eSBradley Grove struct esas2r_request *rq, u8 fi_stat)
30426780d9eSBradley Grove {
30526780d9eSBradley Grove struct esas2r_flash_context *fc =
30626780d9eSBradley Grove (struct esas2r_flash_context *)rq->interrupt_cx;
30726780d9eSBradley Grove struct esas2r_flash_img *fi = fc->fi;
30826780d9eSBradley Grove
30926780d9eSBradley Grove fi->status = fi_stat;
31026780d9eSBradley Grove fi->driver_error = rq->req_stat;
31126780d9eSBradley Grove rq->interrupt_cb = NULL;
31226780d9eSBradley Grove rq->req_stat = RS_SUCCESS;
31326780d9eSBradley Grove
31426780d9eSBradley Grove if (fi_stat != FI_STAT_IMG_VER)
31526780d9eSBradley Grove memset(fc->scratch, 0, FM_BUF_SZ);
31626780d9eSBradley Grove
31726780d9eSBradley Grove esas2r_enable_heartbeat(a);
3189588d24eSBradley Grove clear_bit(AF_FLASH_LOCK, &a->flags);
31926780d9eSBradley Grove return false;
32026780d9eSBradley Grove }
32126780d9eSBradley Grove
32226780d9eSBradley Grove /* Process each phase of the flash download process. */
fw_download_proc(struct esas2r_adapter * a,struct esas2r_request * rq)32326780d9eSBradley Grove static void fw_download_proc(struct esas2r_adapter *a,
32426780d9eSBradley Grove struct esas2r_request *rq)
32526780d9eSBradley Grove {
32626780d9eSBradley Grove struct esas2r_flash_context *fc =
32726780d9eSBradley Grove (struct esas2r_flash_context *)rq->interrupt_cx;
32826780d9eSBradley Grove struct esas2r_flash_img *fi = fc->fi;
32926780d9eSBradley Grove struct esas2r_component_header *ch;
33026780d9eSBradley Grove u32 len;
33126780d9eSBradley Grove u8 *p, *q;
33226780d9eSBradley Grove
33326780d9eSBradley Grove /* If the previous operation failed, just return. */
33426780d9eSBradley Grove if (rq->req_stat != RS_SUCCESS)
33526780d9eSBradley Grove goto error;
33626780d9eSBradley Grove
33726780d9eSBradley Grove /*
33826780d9eSBradley Grove * If an upload just completed and the compare length is non-zero,
33926780d9eSBradley Grove * then we just read back part of the image we just wrote. verify the
34026780d9eSBradley Grove * section and continue reading until the entire image is verified.
34126780d9eSBradley Grove */
34226780d9eSBradley Grove if (fc->func == VDA_FLASH_READ
34326780d9eSBradley Grove && fc->cmp_len) {
34426780d9eSBradley Grove ch = &fi->cmp_hdr[fc->comp_typ];
34526780d9eSBradley Grove
34626780d9eSBradley Grove p = fc->scratch;
34726780d9eSBradley Grove q = (u8 *)fi /* start of the whole gob */
34826780d9eSBradley Grove + ch->image_offset /* start of the current image */
34926780d9eSBradley Grove + ch->length /* end of the current image */
35026780d9eSBradley Grove - fc->cmp_len; /* where we are now */
35126780d9eSBradley Grove
35226780d9eSBradley Grove /*
35326780d9eSBradley Grove * NOTE - curr_len is the exact count of bytes for the read
35426780d9eSBradley Grove * even when the end is read and its not a full buffer
35526780d9eSBradley Grove */
35626780d9eSBradley Grove for (len = fc->curr_len; len; len--)
35726780d9eSBradley Grove if (*p++ != *q++)
35826780d9eSBradley Grove goto error;
35926780d9eSBradley Grove
36026780d9eSBradley Grove fc->cmp_len -= fc->curr_len; /* # left to compare */
36126780d9eSBradley Grove
36226780d9eSBradley Grove /* Update fc and determine the length for the next upload */
36326780d9eSBradley Grove if (fc->cmp_len > FM_BUF_SZ)
36426780d9eSBradley Grove fc->sgc.length = FM_BUF_SZ;
36526780d9eSBradley Grove else
36626780d9eSBradley Grove fc->sgc.length = fc->cmp_len;
36726780d9eSBradley Grove
36826780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset +
36926780d9eSBradley Grove ((u8 *)fc->scratch - (u8 *)fi);
37026780d9eSBradley Grove }
37126780d9eSBradley Grove
37226780d9eSBradley Grove /*
37326780d9eSBradley Grove * This code uses a 'while' statement since the next component may
37426780d9eSBradley Grove * have a length = zero. This can happen since some components are
37526780d9eSBradley Grove * not required. At the end of this 'while' we set up the length
37626780d9eSBradley Grove * for the next request and therefore sgc.length can be = 0.
37726780d9eSBradley Grove */
37826780d9eSBradley Grove while (fc->sgc.length == 0) {
37926780d9eSBradley Grove ch = &fi->cmp_hdr[fc->comp_typ];
38026780d9eSBradley Grove
38126780d9eSBradley Grove switch (fc->task) {
38226780d9eSBradley Grove case FMTSK_ERASE_BOOT:
38326780d9eSBradley Grove /* the BIOS image is written next */
38426780d9eSBradley Grove ch = &fi->cmp_hdr[CH_IT_BIOS];
38526780d9eSBradley Grove if (ch->length == 0)
38626780d9eSBradley Grove goto no_bios;
38726780d9eSBradley Grove
38826780d9eSBradley Grove fc->task = FMTSK_WRTBIOS;
38926780d9eSBradley Grove fc->func = VDA_FLASH_BEGINW;
39026780d9eSBradley Grove fc->comp_typ = CH_IT_BIOS;
39126780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_BOOT;
39226780d9eSBradley Grove fc->sgc.length = ch->length;
39326780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset +
39426780d9eSBradley Grove ch->image_offset;
39526780d9eSBradley Grove break;
39626780d9eSBradley Grove
39726780d9eSBradley Grove case FMTSK_WRTBIOS:
39826780d9eSBradley Grove /*
39926780d9eSBradley Grove * The BIOS image has been written - read it and
40026780d9eSBradley Grove * verify it
40126780d9eSBradley Grove */
40226780d9eSBradley Grove fc->task = FMTSK_READBIOS;
40326780d9eSBradley Grove fc->func = VDA_FLASH_READ;
40426780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_BOOT;
40526780d9eSBradley Grove fc->cmp_len = ch->length;
40626780d9eSBradley Grove fc->sgc.length = FM_BUF_SZ;
40726780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset
40826780d9eSBradley Grove + ((u8 *)fc->scratch -
40926780d9eSBradley Grove (u8 *)fi);
41026780d9eSBradley Grove break;
41126780d9eSBradley Grove
41226780d9eSBradley Grove case FMTSK_READBIOS:
41326780d9eSBradley Grove no_bios:
41426780d9eSBradley Grove /*
41526780d9eSBradley Grove * Mark the component header status for the image
41626780d9eSBradley Grove * completed
41726780d9eSBradley Grove */
41826780d9eSBradley Grove ch->status = CH_STAT_SUCCESS;
41926780d9eSBradley Grove
42026780d9eSBradley Grove /* The MAC image is written next */
42126780d9eSBradley Grove ch = &fi->cmp_hdr[CH_IT_MAC];
42226780d9eSBradley Grove if (ch->length == 0)
42326780d9eSBradley Grove goto no_mac;
42426780d9eSBradley Grove
42526780d9eSBradley Grove fc->task = FMTSK_WRTMAC;
42626780d9eSBradley Grove fc->func = VDA_FLASH_BEGINW;
42726780d9eSBradley Grove fc->comp_typ = CH_IT_MAC;
42826780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_BOOT
42926780d9eSBradley Grove + fi->cmp_hdr[CH_IT_BIOS].length;
43026780d9eSBradley Grove fc->sgc.length = ch->length;
43126780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset +
43226780d9eSBradley Grove ch->image_offset;
43326780d9eSBradley Grove break;
43426780d9eSBradley Grove
43526780d9eSBradley Grove case FMTSK_WRTMAC:
43626780d9eSBradley Grove /* The MAC image has been written - read and verify */
43726780d9eSBradley Grove fc->task = FMTSK_READMAC;
43826780d9eSBradley Grove fc->func = VDA_FLASH_READ;
43926780d9eSBradley Grove fc->flsh_addr -= ch->length;
44026780d9eSBradley Grove fc->cmp_len = ch->length;
44126780d9eSBradley Grove fc->sgc.length = FM_BUF_SZ;
44226780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset
44326780d9eSBradley Grove + ((u8 *)fc->scratch -
44426780d9eSBradley Grove (u8 *)fi);
44526780d9eSBradley Grove break;
44626780d9eSBradley Grove
44726780d9eSBradley Grove case FMTSK_READMAC:
44826780d9eSBradley Grove no_mac:
44926780d9eSBradley Grove /*
45026780d9eSBradley Grove * Mark the component header status for the image
45126780d9eSBradley Grove * completed
45226780d9eSBradley Grove */
45326780d9eSBradley Grove ch->status = CH_STAT_SUCCESS;
45426780d9eSBradley Grove
45526780d9eSBradley Grove /* The EFI image is written next */
45626780d9eSBradley Grove ch = &fi->cmp_hdr[CH_IT_EFI];
45726780d9eSBradley Grove if (ch->length == 0)
45826780d9eSBradley Grove goto no_efi;
45926780d9eSBradley Grove
46026780d9eSBradley Grove fc->task = FMTSK_WRTEFI;
46126780d9eSBradley Grove fc->func = VDA_FLASH_BEGINW;
46226780d9eSBradley Grove fc->comp_typ = CH_IT_EFI;
46326780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_BOOT
46426780d9eSBradley Grove + fi->cmp_hdr[CH_IT_BIOS].length
46526780d9eSBradley Grove + fi->cmp_hdr[CH_IT_MAC].length;
46626780d9eSBradley Grove fc->sgc.length = ch->length;
46726780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset +
46826780d9eSBradley Grove ch->image_offset;
46926780d9eSBradley Grove break;
47026780d9eSBradley Grove
47126780d9eSBradley Grove case FMTSK_WRTEFI:
47226780d9eSBradley Grove /* The EFI image has been written - read and verify */
47326780d9eSBradley Grove fc->task = FMTSK_READEFI;
47426780d9eSBradley Grove fc->func = VDA_FLASH_READ;
47526780d9eSBradley Grove fc->flsh_addr -= ch->length;
47626780d9eSBradley Grove fc->cmp_len = ch->length;
47726780d9eSBradley Grove fc->sgc.length = FM_BUF_SZ;
47826780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset
47926780d9eSBradley Grove + ((u8 *)fc->scratch -
48026780d9eSBradley Grove (u8 *)fi);
48126780d9eSBradley Grove break;
48226780d9eSBradley Grove
48326780d9eSBradley Grove case FMTSK_READEFI:
48426780d9eSBradley Grove no_efi:
48526780d9eSBradley Grove /*
48626780d9eSBradley Grove * Mark the component header status for the image
48726780d9eSBradley Grove * completed
48826780d9eSBradley Grove */
48926780d9eSBradley Grove ch->status = CH_STAT_SUCCESS;
49026780d9eSBradley Grove
49126780d9eSBradley Grove /* The CFG image is written next */
49226780d9eSBradley Grove ch = &fi->cmp_hdr[CH_IT_CFG];
49326780d9eSBradley Grove
49426780d9eSBradley Grove if (ch->length == 0)
49526780d9eSBradley Grove goto no_cfg;
49626780d9eSBradley Grove fc->task = FMTSK_WRTCFG;
49726780d9eSBradley Grove fc->func = VDA_FLASH_BEGINW;
49826780d9eSBradley Grove fc->comp_typ = CH_IT_CFG;
49926780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
50026780d9eSBradley Grove fc->sgc.length = ch->length;
50126780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset +
50226780d9eSBradley Grove ch->image_offset;
50326780d9eSBradley Grove break;
50426780d9eSBradley Grove
50526780d9eSBradley Grove case FMTSK_WRTCFG:
50626780d9eSBradley Grove /* The CFG image has been written - read and verify */
50726780d9eSBradley Grove fc->task = FMTSK_READCFG;
50826780d9eSBradley Grove fc->func = VDA_FLASH_READ;
50926780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_CPYR - ch->length;
51026780d9eSBradley Grove fc->cmp_len = ch->length;
51126780d9eSBradley Grove fc->sgc.length = FM_BUF_SZ;
51226780d9eSBradley Grove fc->sgc.cur_offset = fc->sgc_offset
51326780d9eSBradley Grove + ((u8 *)fc->scratch -
51426780d9eSBradley Grove (u8 *)fi);
51526780d9eSBradley Grove break;
51626780d9eSBradley Grove
51726780d9eSBradley Grove case FMTSK_READCFG:
51826780d9eSBradley Grove no_cfg:
51926780d9eSBradley Grove /*
52026780d9eSBradley Grove * Mark the component header status for the image
52126780d9eSBradley Grove * completed
52226780d9eSBradley Grove */
52326780d9eSBradley Grove ch->status = CH_STAT_SUCCESS;
52426780d9eSBradley Grove
52526780d9eSBradley Grove /*
52626780d9eSBradley Grove * The download is complete. If in degraded mode,
52726780d9eSBradley Grove * attempt a chip reset.
52826780d9eSBradley Grove */
5299588d24eSBradley Grove if (test_bit(AF_DEGRADED_MODE, &a->flags))
53026780d9eSBradley Grove esas2r_local_reset_adapter(a);
53126780d9eSBradley Grove
53226780d9eSBradley Grove a->flash_ver = fi->cmp_hdr[CH_IT_BIOS].version;
53326780d9eSBradley Grove esas2r_print_flash_rev(a);
53426780d9eSBradley Grove
53526780d9eSBradley Grove /* Update the type of boot image on the card */
53626780d9eSBradley Grove memcpy(a->image_type, fi->rel_version,
53726780d9eSBradley Grove sizeof(fi->rel_version));
53826780d9eSBradley Grove complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
53926780d9eSBradley Grove return;
54026780d9eSBradley Grove }
54126780d9eSBradley Grove
54226780d9eSBradley Grove /* If verifying, don't try reading more than what's there */
54326780d9eSBradley Grove if (fc->func == VDA_FLASH_READ
54426780d9eSBradley Grove && fc->sgc.length > fc->cmp_len)
54526780d9eSBradley Grove fc->sgc.length = fc->cmp_len;
54626780d9eSBradley Grove }
54726780d9eSBradley Grove
54826780d9eSBradley Grove /* Build the request to perform the next action */
54926780d9eSBradley Grove if (!load_image(a, rq)) {
55026780d9eSBradley Grove error:
55126780d9eSBradley Grove if (fc->comp_typ < fi->num_comps) {
55226780d9eSBradley Grove ch = &fi->cmp_hdr[fc->comp_typ];
55326780d9eSBradley Grove ch->status = CH_STAT_FAILED;
55426780d9eSBradley Grove }
55526780d9eSBradley Grove
55626780d9eSBradley Grove complete_fmapi_req(a, rq, FI_STAT_FAILED);
55726780d9eSBradley Grove }
55826780d9eSBradley Grove }
55926780d9eSBradley Grove
56026780d9eSBradley Grove /* Determine the flash image adaptyp for this adapter */
get_fi_adap_type(struct esas2r_adapter * a)56126780d9eSBradley Grove static u8 get_fi_adap_type(struct esas2r_adapter *a)
56226780d9eSBradley Grove {
56326780d9eSBradley Grove u8 type;
56426780d9eSBradley Grove
56526780d9eSBradley Grove /* use the device ID to get the correct adap_typ for this HBA */
56626780d9eSBradley Grove switch (a->pcid->device) {
56726780d9eSBradley Grove case ATTO_DID_INTEL_IOP348:
56826780d9eSBradley Grove type = FI_AT_SUN_LAKE;
56926780d9eSBradley Grove break;
57026780d9eSBradley Grove
57126780d9eSBradley Grove case ATTO_DID_MV_88RC9580:
57226780d9eSBradley Grove case ATTO_DID_MV_88RC9580TS:
57326780d9eSBradley Grove case ATTO_DID_MV_88RC9580TSE:
57426780d9eSBradley Grove case ATTO_DID_MV_88RC9580TL:
57526780d9eSBradley Grove type = FI_AT_MV_9580;
57626780d9eSBradley Grove break;
57726780d9eSBradley Grove
57826780d9eSBradley Grove default:
57926780d9eSBradley Grove type = FI_AT_UNKNWN;
58026780d9eSBradley Grove break;
58126780d9eSBradley Grove }
58226780d9eSBradley Grove
58326780d9eSBradley Grove return type;
58426780d9eSBradley Grove }
58526780d9eSBradley Grove
58626780d9eSBradley Grove /* Size of config + copyright + flash_ver images, 0 for failure. */
chk_cfg(u8 * cfg,u32 length,u32 * flash_ver)58726780d9eSBradley Grove static u32 chk_cfg(u8 *cfg, u32 length, u32 *flash_ver)
58826780d9eSBradley Grove {
58926780d9eSBradley Grove u16 *pw = (u16 *)cfg - 1;
59026780d9eSBradley Grove u32 sz = 0;
59126780d9eSBradley Grove u32 len = length;
59226780d9eSBradley Grove
59326780d9eSBradley Grove if (len == 0)
59426780d9eSBradley Grove len = FM_BUF_SZ;
59526780d9eSBradley Grove
59626780d9eSBradley Grove if (flash_ver)
59726780d9eSBradley Grove *flash_ver = 0;
59826780d9eSBradley Grove
59926780d9eSBradley Grove while (true) {
60026780d9eSBradley Grove u16 type;
60126780d9eSBradley Grove u16 size;
60226780d9eSBradley Grove
60326780d9eSBradley Grove type = le16_to_cpu(*pw--);
60426780d9eSBradley Grove size = le16_to_cpu(*pw--);
60526780d9eSBradley Grove
60626780d9eSBradley Grove if (type != FBT_CPYR
60726780d9eSBradley Grove && type != FBT_SETUP
60826780d9eSBradley Grove && type != FBT_FLASH_VER)
60926780d9eSBradley Grove break;
61026780d9eSBradley Grove
61126780d9eSBradley Grove if (type == FBT_FLASH_VER
61226780d9eSBradley Grove && flash_ver)
61326780d9eSBradley Grove *flash_ver = le32_to_cpu(*(u32 *)(pw - 1));
61426780d9eSBradley Grove
61526780d9eSBradley Grove sz += size + (2 * sizeof(u16));
61626780d9eSBradley Grove pw -= size / sizeof(u16);
61726780d9eSBradley Grove
61826780d9eSBradley Grove if (sz > len - (2 * sizeof(u16)))
61926780d9eSBradley Grove break;
62026780d9eSBradley Grove }
62126780d9eSBradley Grove
62226780d9eSBradley Grove /* See if we are comparing the size to the specified length */
62326780d9eSBradley Grove if (length && sz != length)
62426780d9eSBradley Grove return 0;
62526780d9eSBradley Grove
62626780d9eSBradley Grove return sz;
62726780d9eSBradley Grove }
62826780d9eSBradley Grove
62926780d9eSBradley Grove /* Verify that the boot image is valid */
chk_boot(u8 * boot_img,u32 length)63026780d9eSBradley Grove static u8 chk_boot(u8 *boot_img, u32 length)
63126780d9eSBradley Grove {
63226780d9eSBradley Grove struct esas2r_boot_image *bi = (struct esas2r_boot_image *)boot_img;
63326780d9eSBradley Grove u16 hdroffset = le16_to_cpu(bi->header_offset);
63426780d9eSBradley Grove struct esas2r_boot_header *bh;
63526780d9eSBradley Grove
63626780d9eSBradley Grove if (bi->signature != le16_to_cpu(0xaa55)
63726780d9eSBradley Grove || (long)hdroffset >
63826780d9eSBradley Grove (long)(65536L - sizeof(struct esas2r_boot_header))
63926780d9eSBradley Grove || (hdroffset & 3)
64026780d9eSBradley Grove || (hdroffset < sizeof(struct esas2r_boot_image))
64126780d9eSBradley Grove || ((u32)hdroffset + sizeof(struct esas2r_boot_header) > length))
64226780d9eSBradley Grove return 0xff;
64326780d9eSBradley Grove
64426780d9eSBradley Grove bh = (struct esas2r_boot_header *)((char *)bi + hdroffset);
64526780d9eSBradley Grove
64626780d9eSBradley Grove if (bh->signature[0] != 'P'
64726780d9eSBradley Grove || bh->signature[1] != 'C'
64826780d9eSBradley Grove || bh->signature[2] != 'I'
64926780d9eSBradley Grove || bh->signature[3] != 'R'
65026780d9eSBradley Grove || le16_to_cpu(bh->struct_length) <
65126780d9eSBradley Grove (u16)sizeof(struct esas2r_boot_header)
65226780d9eSBradley Grove || bh->class_code[2] != 0x01
65326780d9eSBradley Grove || bh->class_code[1] != 0x04
65426780d9eSBradley Grove || bh->class_code[0] != 0x00
65526780d9eSBradley Grove || (bh->code_type != CODE_TYPE_PC
65626780d9eSBradley Grove && bh->code_type != CODE_TYPE_OPEN
65726780d9eSBradley Grove && bh->code_type != CODE_TYPE_EFI))
65826780d9eSBradley Grove return 0xff;
65926780d9eSBradley Grove
66026780d9eSBradley Grove return bh->code_type;
66126780d9eSBradley Grove }
66226780d9eSBradley Grove
66326780d9eSBradley Grove /* The sum of all the WORDS of the image */
calc_fi_checksum(struct esas2r_flash_context * fc)66426780d9eSBradley Grove static u16 calc_fi_checksum(struct esas2r_flash_context *fc)
66526780d9eSBradley Grove {
66626780d9eSBradley Grove struct esas2r_flash_img *fi = fc->fi;
66726780d9eSBradley Grove u16 cksum;
66826780d9eSBradley Grove u32 len;
66926780d9eSBradley Grove u16 *pw;
67026780d9eSBradley Grove
67126780d9eSBradley Grove for (len = (fi->length - fc->fi_hdr_len) / 2,
67226780d9eSBradley Grove pw = (u16 *)((u8 *)fi + fc->fi_hdr_len),
67326780d9eSBradley Grove cksum = 0;
67426780d9eSBradley Grove len;
67526780d9eSBradley Grove len--, pw++)
67626780d9eSBradley Grove cksum = cksum + le16_to_cpu(*pw);
67726780d9eSBradley Grove
67826780d9eSBradley Grove return cksum;
67926780d9eSBradley Grove }
68026780d9eSBradley Grove
68126780d9eSBradley Grove /*
68226780d9eSBradley Grove * Verify the flash image structure. The following verifications will
68326780d9eSBradley Grove * be performed:
68426780d9eSBradley Grove * 1) verify the fi_version is correct
68526780d9eSBradley Grove * 2) verify the checksum of the entire image.
68626780d9eSBradley Grove * 3) validate the adap_typ, action and length fields.
68751879016SBoris Bodemann * 4) validate each component header. check the img_type and
68826780d9eSBradley Grove * length fields
68951879016SBoris Bodemann * 5) validate each component image. validate signatures and
69026780d9eSBradley Grove * local checksums
69126780d9eSBradley Grove */
verify_fi(struct esas2r_adapter * a,struct esas2r_flash_context * fc)69226780d9eSBradley Grove static bool verify_fi(struct esas2r_adapter *a,
69326780d9eSBradley Grove struct esas2r_flash_context *fc)
69426780d9eSBradley Grove {
69526780d9eSBradley Grove struct esas2r_flash_img *fi = fc->fi;
69626780d9eSBradley Grove u8 type;
69726780d9eSBradley Grove bool imgerr;
69826780d9eSBradley Grove u16 i;
69926780d9eSBradley Grove u32 len;
70026780d9eSBradley Grove struct esas2r_component_header *ch;
70126780d9eSBradley Grove
70226780d9eSBradley Grove /* Verify the length - length must even since we do a word checksum */
70326780d9eSBradley Grove len = fi->length;
70426780d9eSBradley Grove
70526780d9eSBradley Grove if ((len & 1)
70626780d9eSBradley Grove || len < fc->fi_hdr_len) {
70726780d9eSBradley Grove fi->status = FI_STAT_LENGTH;
70826780d9eSBradley Grove return false;
70926780d9eSBradley Grove }
71026780d9eSBradley Grove
71126780d9eSBradley Grove /* Get adapter type and verify type in flash image */
71226780d9eSBradley Grove type = get_fi_adap_type(a);
71326780d9eSBradley Grove if ((type == FI_AT_UNKNWN) || (fi->adap_typ != type)) {
71426780d9eSBradley Grove fi->status = FI_STAT_ADAPTYP;
71526780d9eSBradley Grove return false;
71626780d9eSBradley Grove }
71726780d9eSBradley Grove
71826780d9eSBradley Grove /*
71926780d9eSBradley Grove * Loop through each component and verify the img_type and length
72026780d9eSBradley Grove * fields. Keep a running count of the sizes sooze we can verify total
72126780d9eSBradley Grove * size to additive size.
72226780d9eSBradley Grove */
72326780d9eSBradley Grove imgerr = false;
72426780d9eSBradley Grove
72526780d9eSBradley Grove for (i = 0, len = 0, ch = fi->cmp_hdr;
72626780d9eSBradley Grove i < fi->num_comps;
72726780d9eSBradley Grove i++, ch++) {
72826780d9eSBradley Grove bool cmperr = false;
72926780d9eSBradley Grove
73026780d9eSBradley Grove /*
73126780d9eSBradley Grove * Verify that the component header has the same index as the
73226780d9eSBradley Grove * image type. The headers must be ordered correctly
73326780d9eSBradley Grove */
73426780d9eSBradley Grove if (i != ch->img_type) {
73526780d9eSBradley Grove imgerr = true;
73626780d9eSBradley Grove ch->status = CH_STAT_INVALID;
73726780d9eSBradley Grove continue;
73826780d9eSBradley Grove }
73926780d9eSBradley Grove
74026780d9eSBradley Grove switch (ch->img_type) {
74126780d9eSBradley Grove case CH_IT_BIOS:
74226780d9eSBradley Grove type = CODE_TYPE_PC;
74326780d9eSBradley Grove break;
74426780d9eSBradley Grove
74526780d9eSBradley Grove case CH_IT_MAC:
74626780d9eSBradley Grove type = CODE_TYPE_OPEN;
74726780d9eSBradley Grove break;
74826780d9eSBradley Grove
74926780d9eSBradley Grove case CH_IT_EFI:
75026780d9eSBradley Grove type = CODE_TYPE_EFI;
75126780d9eSBradley Grove break;
75226780d9eSBradley Grove }
75326780d9eSBradley Grove
75426780d9eSBradley Grove switch (ch->img_type) {
75526780d9eSBradley Grove case CH_IT_FW:
75626780d9eSBradley Grove case CH_IT_NVR:
75726780d9eSBradley Grove break;
75826780d9eSBradley Grove
75926780d9eSBradley Grove case CH_IT_BIOS:
76026780d9eSBradley Grove case CH_IT_MAC:
76126780d9eSBradley Grove case CH_IT_EFI:
76226780d9eSBradley Grove if (ch->length & 0x1ff)
76326780d9eSBradley Grove cmperr = true;
76426780d9eSBradley Grove
76526780d9eSBradley Grove /* Test if component image is present */
76626780d9eSBradley Grove if (ch->length == 0)
76726780d9eSBradley Grove break;
76826780d9eSBradley Grove
76926780d9eSBradley Grove /* Image is present - verify the image */
77026780d9eSBradley Grove if (chk_boot((u8 *)fi + ch->image_offset, ch->length)
77126780d9eSBradley Grove != type)
77226780d9eSBradley Grove cmperr = true;
77326780d9eSBradley Grove
77426780d9eSBradley Grove break;
77526780d9eSBradley Grove
77626780d9eSBradley Grove case CH_IT_CFG:
77726780d9eSBradley Grove
77826780d9eSBradley Grove /* Test if component image is present */
77926780d9eSBradley Grove if (ch->length == 0) {
78026780d9eSBradley Grove cmperr = true;
78126780d9eSBradley Grove break;
78226780d9eSBradley Grove }
78326780d9eSBradley Grove
78426780d9eSBradley Grove /* Image is present - verify the image */
78526780d9eSBradley Grove if (!chk_cfg((u8 *)fi + ch->image_offset + ch->length,
78626780d9eSBradley Grove ch->length, NULL))
78726780d9eSBradley Grove cmperr = true;
78826780d9eSBradley Grove
78926780d9eSBradley Grove break;
79026780d9eSBradley Grove
79126780d9eSBradley Grove default:
79226780d9eSBradley Grove
79326780d9eSBradley Grove fi->status = FI_STAT_UNKNOWN;
79426780d9eSBradley Grove return false;
79526780d9eSBradley Grove }
79626780d9eSBradley Grove
79726780d9eSBradley Grove if (cmperr) {
79826780d9eSBradley Grove imgerr = true;
79926780d9eSBradley Grove ch->status = CH_STAT_INVALID;
80026780d9eSBradley Grove } else {
80126780d9eSBradley Grove ch->status = CH_STAT_PENDING;
80226780d9eSBradley Grove len += ch->length;
80326780d9eSBradley Grove }
80426780d9eSBradley Grove }
80526780d9eSBradley Grove
80626780d9eSBradley Grove if (imgerr) {
80726780d9eSBradley Grove fi->status = FI_STAT_MISSING;
80826780d9eSBradley Grove return false;
80926780d9eSBradley Grove }
81026780d9eSBradley Grove
81126780d9eSBradley Grove /* Compare fi->length to the sum of ch->length fields */
81226780d9eSBradley Grove if (len != fi->length - fc->fi_hdr_len) {
81326780d9eSBradley Grove fi->status = FI_STAT_LENGTH;
81426780d9eSBradley Grove return false;
81526780d9eSBradley Grove }
81626780d9eSBradley Grove
81726780d9eSBradley Grove /* Compute the checksum - it should come out zero */
81826780d9eSBradley Grove if (fi->checksum != calc_fi_checksum(fc)) {
81926780d9eSBradley Grove fi->status = FI_STAT_CHKSUM;
82026780d9eSBradley Grove return false;
82126780d9eSBradley Grove }
82226780d9eSBradley Grove
82326780d9eSBradley Grove return true;
82426780d9eSBradley Grove }
82526780d9eSBradley Grove
82626780d9eSBradley Grove /* Fill in the FS IOCTL response data from a completed request. */
esas2r_complete_fs_ioctl(struct esas2r_adapter * a,struct esas2r_request * rq)82726780d9eSBradley Grove static void esas2r_complete_fs_ioctl(struct esas2r_adapter *a,
82826780d9eSBradley Grove struct esas2r_request *rq)
82926780d9eSBradley Grove {
83026780d9eSBradley Grove struct esas2r_ioctl_fs *fs =
83126780d9eSBradley Grove (struct esas2r_ioctl_fs *)rq->interrupt_cx;
83226780d9eSBradley Grove
83326780d9eSBradley Grove if (rq->vrq->flash.sub_func == VDA_FLASH_COMMIT)
83426780d9eSBradley Grove esas2r_enable_heartbeat(a);
83526780d9eSBradley Grove
83626780d9eSBradley Grove fs->driver_error = rq->req_stat;
83726780d9eSBradley Grove
83826780d9eSBradley Grove if (fs->driver_error == RS_SUCCESS)
83926780d9eSBradley Grove fs->status = ATTO_STS_SUCCESS;
84026780d9eSBradley Grove else
84126780d9eSBradley Grove fs->status = ATTO_STS_FAILED;
84226780d9eSBradley Grove }
84326780d9eSBradley Grove
84426780d9eSBradley Grove /* Prepare an FS IOCTL request to be sent to the firmware. */
esas2r_process_fs_ioctl(struct esas2r_adapter * a,struct esas2r_ioctl_fs * fs,struct esas2r_request * rq,struct esas2r_sg_context * sgc)84526780d9eSBradley Grove bool esas2r_process_fs_ioctl(struct esas2r_adapter *a,
84626780d9eSBradley Grove struct esas2r_ioctl_fs *fs,
84726780d9eSBradley Grove struct esas2r_request *rq,
84826780d9eSBradley Grove struct esas2r_sg_context *sgc)
84926780d9eSBradley Grove {
85026780d9eSBradley Grove u8 cmdcnt = (u8)ARRAY_SIZE(cmd_to_fls_func);
85126780d9eSBradley Grove struct esas2r_ioctlfs_command *fsc = &fs->command;
85226780d9eSBradley Grove u8 func = 0;
85326780d9eSBradley Grove u32 datalen;
85426780d9eSBradley Grove
85526780d9eSBradley Grove fs->status = ATTO_STS_FAILED;
85626780d9eSBradley Grove fs->driver_error = RS_PENDING;
85726780d9eSBradley Grove
85826780d9eSBradley Grove if (fs->version > ESAS2R_FS_VER) {
85926780d9eSBradley Grove fs->status = ATTO_STS_INV_VERSION;
86026780d9eSBradley Grove return false;
86126780d9eSBradley Grove }
86226780d9eSBradley Grove
86364d29bd8SBradley Grove if (fsc->command >= cmdcnt) {
86464d29bd8SBradley Grove fs->status = ATTO_STS_INV_FUNC;
86564d29bd8SBradley Grove return false;
86664d29bd8SBradley Grove }
86764d29bd8SBradley Grove
86826780d9eSBradley Grove func = cmd_to_fls_func[fsc->command];
86964d29bd8SBradley Grove if (func == 0xFF) {
87026780d9eSBradley Grove fs->status = ATTO_STS_INV_FUNC;
87126780d9eSBradley Grove return false;
87226780d9eSBradley Grove }
87326780d9eSBradley Grove
87426780d9eSBradley Grove if (fsc->command != ESAS2R_FS_CMD_CANCEL) {
87526780d9eSBradley Grove if ((a->pcid->device != ATTO_DID_MV_88RC9580
87626780d9eSBradley Grove || fs->adap_type != ESAS2R_FS_AT_ESASRAID2)
87726780d9eSBradley Grove && (a->pcid->device != ATTO_DID_MV_88RC9580TS
87826780d9eSBradley Grove || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2)
87926780d9eSBradley Grove && (a->pcid->device != ATTO_DID_MV_88RC9580TSE
88026780d9eSBradley Grove || fs->adap_type != ESAS2R_FS_AT_TSSASRAID2E)
88126780d9eSBradley Grove && (a->pcid->device != ATTO_DID_MV_88RC9580TL
88226780d9eSBradley Grove || fs->adap_type != ESAS2R_FS_AT_TLSASHBA)) {
88326780d9eSBradley Grove fs->status = ATTO_STS_INV_ADAPTER;
88426780d9eSBradley Grove return false;
88526780d9eSBradley Grove }
88626780d9eSBradley Grove
88726780d9eSBradley Grove if (fs->driver_ver > ESAS2R_FS_DRVR_VER) {
88826780d9eSBradley Grove fs->status = ATTO_STS_INV_DRVR_VER;
88926780d9eSBradley Grove return false;
89026780d9eSBradley Grove }
89126780d9eSBradley Grove }
89226780d9eSBradley Grove
8939588d24eSBradley Grove if (test_bit(AF_DEGRADED_MODE, &a->flags)) {
89426780d9eSBradley Grove fs->status = ATTO_STS_DEGRADED;
89526780d9eSBradley Grove return false;
89626780d9eSBradley Grove }
89726780d9eSBradley Grove
89826780d9eSBradley Grove rq->interrupt_cb = esas2r_complete_fs_ioctl;
89926780d9eSBradley Grove rq->interrupt_cx = fs;
90026780d9eSBradley Grove datalen = le32_to_cpu(fsc->length);
90126780d9eSBradley Grove esas2r_build_flash_req(a,
90226780d9eSBradley Grove rq,
90326780d9eSBradley Grove func,
90426780d9eSBradley Grove fsc->checksum,
90526780d9eSBradley Grove le32_to_cpu(fsc->flash_addr),
90626780d9eSBradley Grove datalen);
90726780d9eSBradley Grove
90826780d9eSBradley Grove if (func == VDA_FLASH_WRITE
90926780d9eSBradley Grove || func == VDA_FLASH_READ) {
91026780d9eSBradley Grove if (datalen == 0) {
91126780d9eSBradley Grove fs->status = ATTO_STS_INV_FUNC;
91226780d9eSBradley Grove return false;
91326780d9eSBradley Grove }
91426780d9eSBradley Grove
91526780d9eSBradley Grove esas2r_sgc_init(sgc, a, rq, rq->vrq->flash.data.sge);
91626780d9eSBradley Grove sgc->length = datalen;
91726780d9eSBradley Grove
91826780d9eSBradley Grove if (!esas2r_build_sg_list(a, rq, sgc)) {
91926780d9eSBradley Grove fs->status = ATTO_STS_OUT_OF_RSRC;
92026780d9eSBradley Grove return false;
92126780d9eSBradley Grove }
92226780d9eSBradley Grove }
92326780d9eSBradley Grove
92426780d9eSBradley Grove if (func == VDA_FLASH_COMMIT)
92526780d9eSBradley Grove esas2r_disable_heartbeat(a);
92626780d9eSBradley Grove
92726780d9eSBradley Grove esas2r_start_request(a, rq);
92826780d9eSBradley Grove
92926780d9eSBradley Grove return true;
93026780d9eSBradley Grove }
93126780d9eSBradley Grove
esas2r_flash_access(struct esas2r_adapter * a,u32 function)93226780d9eSBradley Grove static bool esas2r_flash_access(struct esas2r_adapter *a, u32 function)
93326780d9eSBradley Grove {
93426780d9eSBradley Grove u32 starttime;
93526780d9eSBradley Grove u32 timeout;
93626780d9eSBradley Grove u32 intstat;
93726780d9eSBradley Grove u32 doorbell;
93826780d9eSBradley Grove
93926780d9eSBradley Grove /* Disable chip interrupts awhile */
94026780d9eSBradley Grove if (function == DRBL_FLASH_REQ)
94126780d9eSBradley Grove esas2r_disable_chip_interrupts(a);
94226780d9eSBradley Grove
94326780d9eSBradley Grove /* Issue the request to the firmware */
94426780d9eSBradley Grove esas2r_write_register_dword(a, MU_DOORBELL_IN, function);
94526780d9eSBradley Grove
94626780d9eSBradley Grove /* Now wait for the firmware to process it */
94726780d9eSBradley Grove starttime = jiffies_to_msecs(jiffies);
9489588d24eSBradley Grove
9499588d24eSBradley Grove if (test_bit(AF_CHPRST_PENDING, &a->flags) ||
9509588d24eSBradley Grove test_bit(AF_DISC_PENDING, &a->flags))
9519588d24eSBradley Grove timeout = 40000;
9529588d24eSBradley Grove else
9539588d24eSBradley Grove timeout = 5000;
95426780d9eSBradley Grove
95526780d9eSBradley Grove while (true) {
95626780d9eSBradley Grove intstat = esas2r_read_register_dword(a, MU_INT_STATUS_OUT);
95726780d9eSBradley Grove
95826780d9eSBradley Grove if (intstat & MU_INTSTAT_DRBL) {
95926780d9eSBradley Grove /* Got a doorbell interrupt. Check for the function */
96026780d9eSBradley Grove doorbell =
96126780d9eSBradley Grove esas2r_read_register_dword(a, MU_DOORBELL_OUT);
96226780d9eSBradley Grove esas2r_write_register_dword(a, MU_DOORBELL_OUT,
96326780d9eSBradley Grove doorbell);
96426780d9eSBradley Grove if (doorbell & function)
96526780d9eSBradley Grove break;
96626780d9eSBradley Grove }
96726780d9eSBradley Grove
96826780d9eSBradley Grove schedule_timeout_interruptible(msecs_to_jiffies(100));
96926780d9eSBradley Grove
97026780d9eSBradley Grove if ((jiffies_to_msecs(jiffies) - starttime) > timeout) {
97126780d9eSBradley Grove /*
97226780d9eSBradley Grove * Iimeout. If we were requesting flash access,
97326780d9eSBradley Grove * indicate we are done so the firmware knows we gave
97426780d9eSBradley Grove * up. If this was a REQ, we also need to re-enable
97526780d9eSBradley Grove * chip interrupts.
97626780d9eSBradley Grove */
97726780d9eSBradley Grove if (function == DRBL_FLASH_REQ) {
97826780d9eSBradley Grove esas2r_hdebug("flash access timeout");
97926780d9eSBradley Grove esas2r_write_register_dword(a, MU_DOORBELL_IN,
98026780d9eSBradley Grove DRBL_FLASH_DONE);
98126780d9eSBradley Grove esas2r_enable_chip_interrupts(a);
98226780d9eSBradley Grove } else {
98326780d9eSBradley Grove esas2r_hdebug("flash release timeout");
98426780d9eSBradley Grove }
98526780d9eSBradley Grove
98626780d9eSBradley Grove return false;
98726780d9eSBradley Grove }
98826780d9eSBradley Grove }
98926780d9eSBradley Grove
99026780d9eSBradley Grove /* if we're done, re-enable chip interrupts */
99126780d9eSBradley Grove if (function == DRBL_FLASH_DONE)
99226780d9eSBradley Grove esas2r_enable_chip_interrupts(a);
99326780d9eSBradley Grove
99426780d9eSBradley Grove return true;
99526780d9eSBradley Grove }
99626780d9eSBradley Grove
99726780d9eSBradley Grove #define WINDOW_SIZE ((signed int)MW_DATA_WINDOW_SIZE)
99826780d9eSBradley Grove
esas2r_read_flash_block(struct esas2r_adapter * a,void * to,u32 from,u32 size)99926780d9eSBradley Grove bool esas2r_read_flash_block(struct esas2r_adapter *a,
100026780d9eSBradley Grove void *to,
100126780d9eSBradley Grove u32 from,
100226780d9eSBradley Grove u32 size)
100326780d9eSBradley Grove {
100426780d9eSBradley Grove u8 *end = (u8 *)to;
100526780d9eSBradley Grove
100626780d9eSBradley Grove /* Try to acquire access to the flash */
100726780d9eSBradley Grove if (!esas2r_flash_access(a, DRBL_FLASH_REQ))
100826780d9eSBradley Grove return false;
100926780d9eSBradley Grove
101026780d9eSBradley Grove while (size) {
101126780d9eSBradley Grove u32 len;
101226780d9eSBradley Grove u32 offset;
101326780d9eSBradley Grove u32 iatvr;
101426780d9eSBradley Grove
10159588d24eSBradley Grove if (test_bit(AF2_SERIAL_FLASH, &a->flags2))
101626780d9eSBradley Grove iatvr = MW_DATA_ADDR_SER_FLASH + (from & -WINDOW_SIZE);
101726780d9eSBradley Grove else
101826780d9eSBradley Grove iatvr = MW_DATA_ADDR_PAR_FLASH + (from & -WINDOW_SIZE);
101926780d9eSBradley Grove
102026780d9eSBradley Grove esas2r_map_data_window(a, iatvr);
102126780d9eSBradley Grove offset = from & (WINDOW_SIZE - 1);
102226780d9eSBradley Grove len = size;
102326780d9eSBradley Grove
102426780d9eSBradley Grove if (len > WINDOW_SIZE - offset)
102526780d9eSBradley Grove len = WINDOW_SIZE - offset;
102626780d9eSBradley Grove
102726780d9eSBradley Grove from += len;
102826780d9eSBradley Grove size -= len;
102926780d9eSBradley Grove
103026780d9eSBradley Grove while (len--) {
103126780d9eSBradley Grove *end++ = esas2r_read_data_byte(a, offset);
103226780d9eSBradley Grove offset++;
103326780d9eSBradley Grove }
103426780d9eSBradley Grove }
103526780d9eSBradley Grove
103626780d9eSBradley Grove /* Release flash access */
103726780d9eSBradley Grove esas2r_flash_access(a, DRBL_FLASH_DONE);
103826780d9eSBradley Grove return true;
103926780d9eSBradley Grove }
104026780d9eSBradley Grove
esas2r_read_flash_rev(struct esas2r_adapter * a)104126780d9eSBradley Grove bool esas2r_read_flash_rev(struct esas2r_adapter *a)
104226780d9eSBradley Grove {
104326780d9eSBradley Grove u8 bytes[256];
104426780d9eSBradley Grove u16 *pw;
104526780d9eSBradley Grove u16 *pwstart;
104626780d9eSBradley Grove u16 type;
104726780d9eSBradley Grove u16 size;
104826780d9eSBradley Grove u32 sz;
104926780d9eSBradley Grove
105026780d9eSBradley Grove sz = sizeof(bytes);
105126780d9eSBradley Grove pw = (u16 *)(bytes + sz);
105226780d9eSBradley Grove pwstart = (u16 *)bytes + 2;
105326780d9eSBradley Grove
105426780d9eSBradley Grove if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_CPYR - sz, sz))
105526780d9eSBradley Grove goto invalid_rev;
105626780d9eSBradley Grove
105726780d9eSBradley Grove while (pw >= pwstart) {
105826780d9eSBradley Grove pw--;
105926780d9eSBradley Grove type = le16_to_cpu(*pw);
106026780d9eSBradley Grove pw--;
106126780d9eSBradley Grove size = le16_to_cpu(*pw);
106226780d9eSBradley Grove pw -= size / 2;
106326780d9eSBradley Grove
106426780d9eSBradley Grove if (type == FBT_CPYR
106526780d9eSBradley Grove || type == FBT_SETUP
106626780d9eSBradley Grove || pw < pwstart)
106726780d9eSBradley Grove continue;
106826780d9eSBradley Grove
106926780d9eSBradley Grove if (type == FBT_FLASH_VER)
107026780d9eSBradley Grove a->flash_ver = le32_to_cpu(*(u32 *)pw);
107126780d9eSBradley Grove
107226780d9eSBradley Grove break;
107326780d9eSBradley Grove }
107426780d9eSBradley Grove
107526780d9eSBradley Grove invalid_rev:
107626780d9eSBradley Grove return esas2r_print_flash_rev(a);
107726780d9eSBradley Grove }
107826780d9eSBradley Grove
esas2r_print_flash_rev(struct esas2r_adapter * a)107926780d9eSBradley Grove bool esas2r_print_flash_rev(struct esas2r_adapter *a)
108026780d9eSBradley Grove {
108126780d9eSBradley Grove u16 year = LOWORD(a->flash_ver);
108226780d9eSBradley Grove u8 day = LOBYTE(HIWORD(a->flash_ver));
108326780d9eSBradley Grove u8 month = HIBYTE(HIWORD(a->flash_ver));
108426780d9eSBradley Grove
108526780d9eSBradley Grove if (day == 0
108626780d9eSBradley Grove || month == 0
108726780d9eSBradley Grove || day > 31
108826780d9eSBradley Grove || month > 12
108926780d9eSBradley Grove || year < 2006
109026780d9eSBradley Grove || year > 9999) {
109126780d9eSBradley Grove strcpy(a->flash_rev, "not found");
109226780d9eSBradley Grove a->flash_ver = 0;
109326780d9eSBradley Grove return false;
109426780d9eSBradley Grove }
109526780d9eSBradley Grove
109626780d9eSBradley Grove sprintf(a->flash_rev, "%02d/%02d/%04d", month, day, year);
109726780d9eSBradley Grove esas2r_hdebug("flash version: %s", a->flash_rev);
109826780d9eSBradley Grove return true;
109926780d9eSBradley Grove }
110026780d9eSBradley Grove
110126780d9eSBradley Grove /*
110226780d9eSBradley Grove * Find the type of boot image type that is currently in the flash.
110326780d9eSBradley Grove * The chip only has a 64 KB PCI-e expansion ROM
110426780d9eSBradley Grove * size so only one image can be flashed at a time.
110526780d9eSBradley Grove */
esas2r_read_image_type(struct esas2r_adapter * a)110626780d9eSBradley Grove bool esas2r_read_image_type(struct esas2r_adapter *a)
110726780d9eSBradley Grove {
110826780d9eSBradley Grove u8 bytes[256];
110926780d9eSBradley Grove struct esas2r_boot_image *bi;
111026780d9eSBradley Grove struct esas2r_boot_header *bh;
111126780d9eSBradley Grove u32 sz;
111226780d9eSBradley Grove u32 len;
111326780d9eSBradley Grove u32 offset;
111426780d9eSBradley Grove
111526780d9eSBradley Grove /* Start at the base of the boot images and look for a valid image */
111626780d9eSBradley Grove sz = sizeof(bytes);
111726780d9eSBradley Grove len = FLS_LENGTH_BOOT;
111826780d9eSBradley Grove offset = 0;
111926780d9eSBradley Grove
112026780d9eSBradley Grove while (true) {
112126780d9eSBradley Grove if (!esas2r_read_flash_block(a, bytes, FLS_OFFSET_BOOT +
112226780d9eSBradley Grove offset,
112326780d9eSBradley Grove sz))
112426780d9eSBradley Grove goto invalid_rev;
112526780d9eSBradley Grove
112626780d9eSBradley Grove bi = (struct esas2r_boot_image *)bytes;
112726780d9eSBradley Grove bh = (struct esas2r_boot_header *)((u8 *)bi +
112826780d9eSBradley Grove le16_to_cpu(
112926780d9eSBradley Grove bi->header_offset));
113026780d9eSBradley Grove if (bi->signature != cpu_to_le16(0xAA55))
113126780d9eSBradley Grove goto invalid_rev;
113226780d9eSBradley Grove
113326780d9eSBradley Grove if (bh->code_type == CODE_TYPE_PC) {
113426780d9eSBradley Grove strcpy(a->image_type, "BIOS");
113526780d9eSBradley Grove
113626780d9eSBradley Grove return true;
113726780d9eSBradley Grove } else if (bh->code_type == CODE_TYPE_EFI) {
113826780d9eSBradley Grove struct esas2r_efi_image *ei;
113926780d9eSBradley Grove
114026780d9eSBradley Grove /*
114126780d9eSBradley Grove * So we have an EFI image. There are several types
114226780d9eSBradley Grove * so see which architecture we have.
114326780d9eSBradley Grove */
114426780d9eSBradley Grove ei = (struct esas2r_efi_image *)bytes;
114526780d9eSBradley Grove
114626780d9eSBradley Grove switch (le16_to_cpu(ei->machine_type)) {
114726780d9eSBradley Grove case EFI_MACHINE_IA32:
114826780d9eSBradley Grove strcpy(a->image_type, "EFI 32-bit");
114926780d9eSBradley Grove return true;
115026780d9eSBradley Grove
115126780d9eSBradley Grove case EFI_MACHINE_IA64:
115226780d9eSBradley Grove strcpy(a->image_type, "EFI itanium");
115326780d9eSBradley Grove return true;
115426780d9eSBradley Grove
115526780d9eSBradley Grove case EFI_MACHINE_X64:
115626780d9eSBradley Grove strcpy(a->image_type, "EFI 64-bit");
115726780d9eSBradley Grove return true;
115826780d9eSBradley Grove
115926780d9eSBradley Grove case EFI_MACHINE_EBC:
116026780d9eSBradley Grove strcpy(a->image_type, "EFI EBC");
116126780d9eSBradley Grove return true;
116226780d9eSBradley Grove
116326780d9eSBradley Grove default:
116426780d9eSBradley Grove goto invalid_rev;
116526780d9eSBradley Grove }
116626780d9eSBradley Grove } else {
116726780d9eSBradley Grove u32 thislen;
116826780d9eSBradley Grove
116926780d9eSBradley Grove /* jump to the next image */
117026780d9eSBradley Grove thislen = (u32)le16_to_cpu(bh->image_length) * 512;
117126780d9eSBradley Grove if (thislen == 0
117226780d9eSBradley Grove || thislen + offset > len
117326780d9eSBradley Grove || bh->indicator == INDICATOR_LAST)
117426780d9eSBradley Grove break;
117526780d9eSBradley Grove
117626780d9eSBradley Grove offset += thislen;
117726780d9eSBradley Grove }
117826780d9eSBradley Grove }
117926780d9eSBradley Grove
118026780d9eSBradley Grove invalid_rev:
118126780d9eSBradley Grove strcpy(a->image_type, "no boot images");
118226780d9eSBradley Grove return false;
118326780d9eSBradley Grove }
118426780d9eSBradley Grove
118526780d9eSBradley Grove /*
118626780d9eSBradley Grove * Read and validate current NVRAM parameters by accessing
118726780d9eSBradley Grove * physical NVRAM directly. if currently stored parameters are
118826780d9eSBradley Grove * invalid, use the defaults.
118926780d9eSBradley Grove */
esas2r_nvram_read_direct(struct esas2r_adapter * a)119026780d9eSBradley Grove bool esas2r_nvram_read_direct(struct esas2r_adapter *a)
119126780d9eSBradley Grove {
119226780d9eSBradley Grove bool result;
119326780d9eSBradley Grove
119426780d9eSBradley Grove if (down_interruptible(&a->nvram_semaphore))
119526780d9eSBradley Grove return false;
119626780d9eSBradley Grove
119726780d9eSBradley Grove if (!esas2r_read_flash_block(a, a->nvram, FLS_OFFSET_NVR,
119826780d9eSBradley Grove sizeof(struct esas2r_sas_nvram))) {
119926780d9eSBradley Grove esas2r_hdebug("NVRAM read failed, using defaults");
1200906ca635SDan Carpenter up(&a->nvram_semaphore);
120126780d9eSBradley Grove return false;
120226780d9eSBradley Grove }
120326780d9eSBradley Grove
120426780d9eSBradley Grove result = esas2r_nvram_validate(a);
120526780d9eSBradley Grove
120626780d9eSBradley Grove up(&a->nvram_semaphore);
120726780d9eSBradley Grove
120826780d9eSBradley Grove return result;
120926780d9eSBradley Grove }
121026780d9eSBradley Grove
121126780d9eSBradley Grove /* Interrupt callback to process NVRAM completions. */
esas2r_nvram_callback(struct esas2r_adapter * a,struct esas2r_request * rq)121226780d9eSBradley Grove static void esas2r_nvram_callback(struct esas2r_adapter *a,
121326780d9eSBradley Grove struct esas2r_request *rq)
121426780d9eSBradley Grove {
121526780d9eSBradley Grove struct atto_vda_flash_req *vrq = &rq->vrq->flash;
121626780d9eSBradley Grove
121726780d9eSBradley Grove if (rq->req_stat == RS_SUCCESS) {
121826780d9eSBradley Grove /* last request was successful. see what to do now. */
121926780d9eSBradley Grove
122026780d9eSBradley Grove switch (vrq->sub_func) {
122126780d9eSBradley Grove case VDA_FLASH_BEGINW:
122226780d9eSBradley Grove vrq->sub_func = VDA_FLASH_WRITE;
122326780d9eSBradley Grove rq->req_stat = RS_PENDING;
122426780d9eSBradley Grove break;
122526780d9eSBradley Grove
122626780d9eSBradley Grove case VDA_FLASH_WRITE:
122726780d9eSBradley Grove vrq->sub_func = VDA_FLASH_COMMIT;
122826780d9eSBradley Grove rq->req_stat = RS_PENDING;
122926780d9eSBradley Grove break;
123026780d9eSBradley Grove
123126780d9eSBradley Grove case VDA_FLASH_READ:
123226780d9eSBradley Grove esas2r_nvram_validate(a);
123326780d9eSBradley Grove break;
123426780d9eSBradley Grove
123526780d9eSBradley Grove case VDA_FLASH_COMMIT:
123626780d9eSBradley Grove default:
123726780d9eSBradley Grove break;
123826780d9eSBradley Grove }
123926780d9eSBradley Grove }
124026780d9eSBradley Grove
124126780d9eSBradley Grove if (rq->req_stat != RS_PENDING) {
124226780d9eSBradley Grove /* update the NVRAM state */
124326780d9eSBradley Grove if (rq->req_stat == RS_SUCCESS)
12449588d24eSBradley Grove set_bit(AF_NVR_VALID, &a->flags);
124526780d9eSBradley Grove else
12469588d24eSBradley Grove clear_bit(AF_NVR_VALID, &a->flags);
124726780d9eSBradley Grove
124826780d9eSBradley Grove esas2r_enable_heartbeat(a);
124926780d9eSBradley Grove
125026780d9eSBradley Grove up(&a->nvram_semaphore);
125126780d9eSBradley Grove }
125226780d9eSBradley Grove }
125326780d9eSBradley Grove
125426780d9eSBradley Grove /*
125526780d9eSBradley Grove * Write the contents of nvram to the adapter's physical NVRAM.
125626780d9eSBradley Grove * The cached copy of the NVRAM is also updated.
125726780d9eSBradley Grove */
esas2r_nvram_write(struct esas2r_adapter * a,struct esas2r_request * rq,struct esas2r_sas_nvram * nvram)125826780d9eSBradley Grove bool esas2r_nvram_write(struct esas2r_adapter *a, struct esas2r_request *rq,
125926780d9eSBradley Grove struct esas2r_sas_nvram *nvram)
126026780d9eSBradley Grove {
126126780d9eSBradley Grove struct esas2r_sas_nvram *n = nvram;
126226780d9eSBradley Grove u8 sas_address_bytes[8];
126326780d9eSBradley Grove u32 *sas_address_dwords = (u32 *)&sas_address_bytes[0];
126426780d9eSBradley Grove struct atto_vda_flash_req *vrq = &rq->vrq->flash;
126526780d9eSBradley Grove
12669588d24eSBradley Grove if (test_bit(AF_DEGRADED_MODE, &a->flags))
126726780d9eSBradley Grove return false;
126826780d9eSBradley Grove
126926780d9eSBradley Grove if (down_interruptible(&a->nvram_semaphore))
127026780d9eSBradley Grove return false;
127126780d9eSBradley Grove
127226780d9eSBradley Grove if (n == NULL)
127326780d9eSBradley Grove n = a->nvram;
127426780d9eSBradley Grove
127526780d9eSBradley Grove /* check the validity of the settings */
127626780d9eSBradley Grove if (n->version > SASNVR_VERSION) {
127726780d9eSBradley Grove up(&a->nvram_semaphore);
127826780d9eSBradley Grove return false;
127926780d9eSBradley Grove }
128026780d9eSBradley Grove
128126780d9eSBradley Grove memcpy(&sas_address_bytes[0], n->sas_addr, 8);
128226780d9eSBradley Grove
128326780d9eSBradley Grove if (sas_address_bytes[0] != 0x50
128426780d9eSBradley Grove || sas_address_bytes[1] != 0x01
128526780d9eSBradley Grove || sas_address_bytes[2] != 0x08
128626780d9eSBradley Grove || (sas_address_bytes[3] & 0xF0) != 0x60
128726780d9eSBradley Grove || ((sas_address_bytes[3] & 0x0F) | sas_address_dwords[1]) == 0) {
128826780d9eSBradley Grove up(&a->nvram_semaphore);
128926780d9eSBradley Grove return false;
129026780d9eSBradley Grove }
129126780d9eSBradley Grove
129226780d9eSBradley Grove if (n->spin_up_delay > SASNVR_SPINUP_MAX)
129326780d9eSBradley Grove n->spin_up_delay = SASNVR_SPINUP_MAX;
129426780d9eSBradley Grove
129526780d9eSBradley Grove n->version = SASNVR_VERSION;
129626780d9eSBradley Grove n->checksum = n->checksum - esas2r_nvramcalc_cksum(n);
129726780d9eSBradley Grove memcpy(a->nvram, n, sizeof(struct esas2r_sas_nvram));
129826780d9eSBradley Grove
129926780d9eSBradley Grove /* write the NVRAM */
130026780d9eSBradley Grove n = a->nvram;
130126780d9eSBradley Grove esas2r_disable_heartbeat(a);
130226780d9eSBradley Grove
130326780d9eSBradley Grove esas2r_build_flash_req(a,
130426780d9eSBradley Grove rq,
130526780d9eSBradley Grove VDA_FLASH_BEGINW,
130626780d9eSBradley Grove esas2r_nvramcalc_xor_cksum(n),
130726780d9eSBradley Grove FLS_OFFSET_NVR,
130826780d9eSBradley Grove sizeof(struct esas2r_sas_nvram));
130926780d9eSBradley Grove
13109588d24eSBradley Grove if (test_bit(AF_LEGACY_SGE_MODE, &a->flags)) {
131126780d9eSBradley Grove
131226780d9eSBradley Grove vrq->data.sge[0].length =
131326780d9eSBradley Grove cpu_to_le32(SGE_LAST |
131426780d9eSBradley Grove sizeof(struct esas2r_sas_nvram));
131526780d9eSBradley Grove vrq->data.sge[0].address = cpu_to_le64(
131626780d9eSBradley Grove a->uncached_phys + (u64)((u8 *)n - a->uncached));
131726780d9eSBradley Grove } else {
131826780d9eSBradley Grove vrq->data.prde[0].ctl_len =
131926780d9eSBradley Grove cpu_to_le32(sizeof(struct esas2r_sas_nvram));
132026780d9eSBradley Grove vrq->data.prde[0].address = cpu_to_le64(
132126780d9eSBradley Grove a->uncached_phys
132226780d9eSBradley Grove + (u64)((u8 *)n - a->uncached));
132326780d9eSBradley Grove }
132426780d9eSBradley Grove rq->interrupt_cb = esas2r_nvram_callback;
132526780d9eSBradley Grove esas2r_start_request(a, rq);
132626780d9eSBradley Grove return true;
132726780d9eSBradley Grove }
132826780d9eSBradley Grove
132926780d9eSBradley Grove /* Validate the cached NVRAM. if the NVRAM is invalid, load the defaults. */
esas2r_nvram_validate(struct esas2r_adapter * a)133026780d9eSBradley Grove bool esas2r_nvram_validate(struct esas2r_adapter *a)
133126780d9eSBradley Grove {
133226780d9eSBradley Grove struct esas2r_sas_nvram *n = a->nvram;
133326780d9eSBradley Grove bool rslt = false;
133426780d9eSBradley Grove
133526780d9eSBradley Grove if (n->signature[0] != 'E'
133626780d9eSBradley Grove || n->signature[1] != 'S'
133726780d9eSBradley Grove || n->signature[2] != 'A'
133826780d9eSBradley Grove || n->signature[3] != 'S') {
133926780d9eSBradley Grove esas2r_hdebug("invalid NVRAM signature");
134026780d9eSBradley Grove } else if (esas2r_nvramcalc_cksum(n)) {
134126780d9eSBradley Grove esas2r_hdebug("invalid NVRAM checksum");
134226780d9eSBradley Grove } else if (n->version > SASNVR_VERSION) {
134326780d9eSBradley Grove esas2r_hdebug("invalid NVRAM version");
134426780d9eSBradley Grove } else {
13459588d24eSBradley Grove set_bit(AF_NVR_VALID, &a->flags);
134626780d9eSBradley Grove rslt = true;
134726780d9eSBradley Grove }
134826780d9eSBradley Grove
134926780d9eSBradley Grove if (rslt == false) {
135026780d9eSBradley Grove esas2r_hdebug("using defaults");
135126780d9eSBradley Grove esas2r_nvram_set_defaults(a);
135226780d9eSBradley Grove }
135326780d9eSBradley Grove
135426780d9eSBradley Grove return rslt;
135526780d9eSBradley Grove }
135626780d9eSBradley Grove
135726780d9eSBradley Grove /*
135826780d9eSBradley Grove * Set the cached NVRAM to defaults. note that this function sets the default
135926780d9eSBradley Grove * NVRAM when it has been determined that the physical NVRAM is invalid.
136026780d9eSBradley Grove * In this case, the SAS address is fabricated.
136126780d9eSBradley Grove */
esas2r_nvram_set_defaults(struct esas2r_adapter * a)136226780d9eSBradley Grove void esas2r_nvram_set_defaults(struct esas2r_adapter *a)
136326780d9eSBradley Grove {
136426780d9eSBradley Grove struct esas2r_sas_nvram *n = a->nvram;
136526780d9eSBradley Grove u32 time = jiffies_to_msecs(jiffies);
136626780d9eSBradley Grove
13679588d24eSBradley Grove clear_bit(AF_NVR_VALID, &a->flags);
1368ef11851bSBradley Grove *n = default_sas_nvram;
136926780d9eSBradley Grove n->sas_addr[3] |= 0x0F;
137026780d9eSBradley Grove n->sas_addr[4] = HIBYTE(LOWORD(time));
137126780d9eSBradley Grove n->sas_addr[5] = LOBYTE(LOWORD(time));
137226780d9eSBradley Grove n->sas_addr[6] = a->pcid->bus->number;
137326780d9eSBradley Grove n->sas_addr[7] = a->pcid->devfn;
137426780d9eSBradley Grove }
137526780d9eSBradley Grove
esas2r_nvram_get_defaults(struct esas2r_adapter * a,struct esas2r_sas_nvram * nvram)137626780d9eSBradley Grove void esas2r_nvram_get_defaults(struct esas2r_adapter *a,
137726780d9eSBradley Grove struct esas2r_sas_nvram *nvram)
137826780d9eSBradley Grove {
137926780d9eSBradley Grove u8 sas_addr[8];
138026780d9eSBradley Grove
138126780d9eSBradley Grove /*
138226780d9eSBradley Grove * in case we are copying the defaults into the adapter, copy the SAS
138326780d9eSBradley Grove * address out first.
138426780d9eSBradley Grove */
138526780d9eSBradley Grove memcpy(&sas_addr[0], a->nvram->sas_addr, 8);
1386ef11851bSBradley Grove *nvram = default_sas_nvram;
138726780d9eSBradley Grove memcpy(&nvram->sas_addr[0], &sas_addr[0], 8);
138826780d9eSBradley Grove }
138926780d9eSBradley Grove
esas2r_fm_api(struct esas2r_adapter * a,struct esas2r_flash_img * fi,struct esas2r_request * rq,struct esas2r_sg_context * sgc)139026780d9eSBradley Grove bool esas2r_fm_api(struct esas2r_adapter *a, struct esas2r_flash_img *fi,
139126780d9eSBradley Grove struct esas2r_request *rq, struct esas2r_sg_context *sgc)
139226780d9eSBradley Grove {
139326780d9eSBradley Grove struct esas2r_flash_context *fc = &a->flash_context;
139426780d9eSBradley Grove u8 j;
139526780d9eSBradley Grove struct esas2r_component_header *ch;
139626780d9eSBradley Grove
13979588d24eSBradley Grove if (test_and_set_bit(AF_FLASH_LOCK, &a->flags)) {
139826780d9eSBradley Grove /* flag was already set */
139926780d9eSBradley Grove fi->status = FI_STAT_BUSY;
140026780d9eSBradley Grove return false;
140126780d9eSBradley Grove }
140226780d9eSBradley Grove
140326780d9eSBradley Grove memcpy(&fc->sgc, sgc, sizeof(struct esas2r_sg_context));
140426780d9eSBradley Grove sgc = &fc->sgc;
140526780d9eSBradley Grove fc->fi = fi;
140626780d9eSBradley Grove fc->sgc_offset = sgc->cur_offset;
140726780d9eSBradley Grove rq->req_stat = RS_SUCCESS;
140826780d9eSBradley Grove rq->interrupt_cx = fc;
140926780d9eSBradley Grove
141026780d9eSBradley Grove switch (fi->fi_version) {
141126780d9eSBradley Grove case FI_VERSION_1:
141226780d9eSBradley Grove fc->scratch = ((struct esas2r_flash_img *)fi)->scratch_buf;
141326780d9eSBradley Grove fc->num_comps = FI_NUM_COMPS_V1;
141426780d9eSBradley Grove fc->fi_hdr_len = sizeof(struct esas2r_flash_img);
141526780d9eSBradley Grove break;
141626780d9eSBradley Grove
141726780d9eSBradley Grove default:
141826780d9eSBradley Grove return complete_fmapi_req(a, rq, FI_STAT_IMG_VER);
141926780d9eSBradley Grove }
142026780d9eSBradley Grove
14219588d24eSBradley Grove if (test_bit(AF_DEGRADED_MODE, &a->flags))
142226780d9eSBradley Grove return complete_fmapi_req(a, rq, FI_STAT_DEGRADED);
142326780d9eSBradley Grove
142426780d9eSBradley Grove switch (fi->action) {
142526780d9eSBradley Grove case FI_ACT_DOWN: /* Download the components */
142626780d9eSBradley Grove /* Verify the format of the flash image */
142726780d9eSBradley Grove if (!verify_fi(a, fc))
142826780d9eSBradley Grove return complete_fmapi_req(a, rq, fi->status);
142926780d9eSBradley Grove
143026780d9eSBradley Grove /* Adjust the BIOS fields that are dependent on the HBA */
143126780d9eSBradley Grove ch = &fi->cmp_hdr[CH_IT_BIOS];
143226780d9eSBradley Grove
143326780d9eSBradley Grove if (ch->length)
143426780d9eSBradley Grove fix_bios(a, fi);
143526780d9eSBradley Grove
143626780d9eSBradley Grove /* Adjust the EFI fields that are dependent on the HBA */
143726780d9eSBradley Grove ch = &fi->cmp_hdr[CH_IT_EFI];
143826780d9eSBradley Grove
143926780d9eSBradley Grove if (ch->length)
144026780d9eSBradley Grove fix_efi(a, fi);
144126780d9eSBradley Grove
144226780d9eSBradley Grove /*
144326780d9eSBradley Grove * Since the image was just modified, compute the checksum on
144426780d9eSBradley Grove * the modified image. First update the CRC for the composite
144526780d9eSBradley Grove * expansion ROM image.
144626780d9eSBradley Grove */
144726780d9eSBradley Grove fi->checksum = calc_fi_checksum(fc);
144826780d9eSBradley Grove
144926780d9eSBradley Grove /* Disable the heartbeat */
145026780d9eSBradley Grove esas2r_disable_heartbeat(a);
145126780d9eSBradley Grove
145226780d9eSBradley Grove /* Now start up the download sequence */
145326780d9eSBradley Grove fc->task = FMTSK_ERASE_BOOT;
145426780d9eSBradley Grove fc->func = VDA_FLASH_BEGINW;
145526780d9eSBradley Grove fc->comp_typ = CH_IT_CFG;
145626780d9eSBradley Grove fc->flsh_addr = FLS_OFFSET_BOOT;
145726780d9eSBradley Grove fc->sgc.length = FLS_LENGTH_BOOT;
145826780d9eSBradley Grove fc->sgc.cur_offset = NULL;
145926780d9eSBradley Grove
146026780d9eSBradley Grove /* Setup the callback address */
146126780d9eSBradley Grove fc->interrupt_cb = fw_download_proc;
146226780d9eSBradley Grove break;
146326780d9eSBradley Grove
146426780d9eSBradley Grove case FI_ACT_UPSZ: /* Get upload sizes */
146526780d9eSBradley Grove fi->adap_typ = get_fi_adap_type(a);
146626780d9eSBradley Grove fi->flags = 0;
146726780d9eSBradley Grove fi->num_comps = fc->num_comps;
146826780d9eSBradley Grove fi->length = fc->fi_hdr_len;
146926780d9eSBradley Grove
147026780d9eSBradley Grove /* Report the type of boot image in the rel_version string */
147126780d9eSBradley Grove memcpy(fi->rel_version, a->image_type,
147226780d9eSBradley Grove sizeof(fi->rel_version));
147326780d9eSBradley Grove
147426780d9eSBradley Grove /* Build the component headers */
147526780d9eSBradley Grove for (j = 0, ch = fi->cmp_hdr;
147626780d9eSBradley Grove j < fi->num_comps;
147726780d9eSBradley Grove j++, ch++) {
147826780d9eSBradley Grove ch->img_type = j;
147926780d9eSBradley Grove ch->status = CH_STAT_PENDING;
148026780d9eSBradley Grove ch->length = 0;
148126780d9eSBradley Grove ch->version = 0xffffffff;
148226780d9eSBradley Grove ch->image_offset = 0;
148326780d9eSBradley Grove ch->pad[0] = 0;
148426780d9eSBradley Grove ch->pad[1] = 0;
148526780d9eSBradley Grove }
148626780d9eSBradley Grove
148726780d9eSBradley Grove if (a->flash_ver != 0) {
148826780d9eSBradley Grove fi->cmp_hdr[CH_IT_BIOS].version =
148926780d9eSBradley Grove fi->cmp_hdr[CH_IT_MAC].version =
149026780d9eSBradley Grove fi->cmp_hdr[CH_IT_EFI].version =
149126780d9eSBradley Grove fi->cmp_hdr[CH_IT_CFG].version
149226780d9eSBradley Grove = a->flash_ver;
149326780d9eSBradley Grove
149426780d9eSBradley Grove fi->cmp_hdr[CH_IT_BIOS].status =
149526780d9eSBradley Grove fi->cmp_hdr[CH_IT_MAC].status =
149626780d9eSBradley Grove fi->cmp_hdr[CH_IT_EFI].status =
149726780d9eSBradley Grove fi->cmp_hdr[CH_IT_CFG].status =
149826780d9eSBradley Grove CH_STAT_SUCCESS;
149926780d9eSBradley Grove
150026780d9eSBradley Grove return complete_fmapi_req(a, rq, FI_STAT_SUCCESS);
150126780d9eSBradley Grove }
150226780d9eSBradley Grove
1503df561f66SGustavo A. R. Silva fallthrough;
150426780d9eSBradley Grove
150526780d9eSBradley Grove case FI_ACT_UP: /* Upload the components */
150626780d9eSBradley Grove default:
150726780d9eSBradley Grove return complete_fmapi_req(a, rq, FI_STAT_INVALID);
150826780d9eSBradley Grove }
150926780d9eSBradley Grove
151026780d9eSBradley Grove /*
151126780d9eSBradley Grove * If we make it here, fc has been setup to do the first task. Call
151226780d9eSBradley Grove * load_image to format the request, start it, and get out. The
151326780d9eSBradley Grove * interrupt code will call the callback when the first message is
151426780d9eSBradley Grove * complete.
151526780d9eSBradley Grove */
151626780d9eSBradley Grove if (!load_image(a, rq))
151726780d9eSBradley Grove return complete_fmapi_req(a, rq, FI_STAT_FAILED);
151826780d9eSBradley Grove
151926780d9eSBradley Grove esas2r_start_request(a, rq);
152026780d9eSBradley Grove
152126780d9eSBradley Grove return true;
152226780d9eSBradley Grove }
1523