/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 QLogic Corporation. All rights reserved. * Use is subject to license terms. */ #pragma ident "Copyright 2010 QLogic Corporation; ql_nx.c" /* * ISP2xxx Solaris Fibre Channel Adapter (FCA) driver source file. * * *********************************************************************** * * ** * * NOTICE ** * * COPYRIGHT (C) 1996-2010 QLOGIC CORPORATION ** * * ALL RIGHTS RESERVED ** * * ** * *********************************************************************** * */ #include #include #include #include #include /* * Local Function Prototypes. */ static void *ql_8021_pci_base_offsetfset(ql_adapter_state_t *, uint64_t); static void ql_crb_addr_transform_setup(ql_adapter_state_t *); static void ql_8021_pci_set_crbwindow_2M(ql_adapter_state_t *, uint64_t *); static void ql_8021_wr_32(ql_adapter_state_t *, uint64_t, uint32_t); static void ql_8021_rd_32(ql_adapter_state_t *, uint64_t, uint32_t *); static int ql_8021_crb_win_lock(ql_adapter_state_t *); static void ql_8021_crb_win_unlock(ql_adapter_state_t *); static int ql_8021_pci_get_crb_addr_2M(ql_adapter_state_t *, uint64_t *); static uint32_t ql_8021_pci_mem_bound_check(ql_adapter_state_t *, uint64_t, uint32_t); static uint64_t ql_8021_pci_set_window(ql_adapter_state_t *, uint64_t); static int ql_8021_pci_is_same_window(ql_adapter_state_t *, uint64_t); static int ql_8021_pci_mem_read_direct(ql_adapter_state_t *, uint64_t, void *, uint32_t); static int ql_8021_pci_mem_write_direct(ql_adapter_state_t *, uint64_t, void *, uint32_t); static int ql_8021_pci_mem_read_2M(ql_adapter_state_t *, uint64_t, void *, uint32_t); static int ql_8021_pci_mem_write_2M(ql_adapter_state_t *, uint64_t, void *, uint32_t); static uint32_t ql_8021_decode_crb_addr(ql_adapter_state_t *, uint32_t); static int ql_8021_rom_lock(ql_adapter_state_t *); static void ql_8021_rom_unlock(ql_adapter_state_t *); static int ql_8021_wait_rom_done(ql_adapter_state_t *); static int ql_8021_wait_flash_done(ql_adapter_state_t *); static int ql_8021_do_rom_fast_read(ql_adapter_state_t *, uint32_t, uint32_t *); static int ql_8021_rom_fast_read(ql_adapter_state_t *, uint32_t, uint32_t *); static int ql_8021_do_rom_write(ql_adapter_state_t *, uint32_t, uint32_t); static int ql_8021_do_rom_erase(ql_adapter_state_t *, uint32_t); static int ql_8021_phantom_init(ql_adapter_state_t *); static int ql_8021_pinit_from_rom(ql_adapter_state_t *); static int ql_8021_load_from_flash(ql_adapter_state_t *); static int ql_8021_load_firmware(ql_adapter_state_t *); static int ql_8021_reset_hw(ql_adapter_state_t *, int); static int ql_8021_init_p3p(ql_adapter_state_t *); static int ql_8021_hw_lock(ql_adapter_state_t *, uint32_t); static void ql_8021_hw_unlock(ql_adapter_state_t *); static void ql_8021_need_reset_handler(ql_adapter_state_t *); /* * Local Data. */ static uint32_t crb_addr_xform[MAX_CRB_XFORM]; static int crb_table_initialized = 0; static int pci_set_window_warning_count = 0; static struct legacy_intr_set legacy_intr[] = NX_LEGACY_INTR_CONFIG; static crb_128M_2M_block_map_t crb_128M_2M_map[64] = { {{{0, 0, 0, 0}}}, /* 0: PCI */ {{{1, 0x0100000, 0x0102000, 0x120000}, /* 1: PCIE */ {1, 0x0110000, 0x0120000, 0x130000}, {1, 0x0120000, 0x0122000, 0x124000}, {1, 0x0130000, 0x0132000, 0x126000}, {1, 0x0140000, 0x0142000, 0x128000}, {1, 0x0150000, 0x0152000, 0x12a000}, {1, 0x0160000, 0x0170000, 0x110000}, {1, 0x0170000, 0x0172000, 0x12e000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {1, 0x01e0000, 0x01e0800, 0x122000}, {0, 0x0000000, 0x0000000, 0x000000}}}, {{{1, 0x0200000, 0x0210000, 0x180000}}}, /* 2: MN */ {{{0, 0, 0, 0}}}, /* 3: */ {{{1, 0x0400000, 0x0401000, 0x169000}}}, /* 4: P2NR1 */ {{{1, 0x0500000, 0x0510000, 0x140000}}}, /* 5: SRE */ {{{1, 0x0600000, 0x0610000, 0x1c0000}}}, /* 6: NIU */ {{{1, 0x0700000, 0x0704000, 0x1b8000}}}, /* 7: QM */ {{{1, 0x0800000, 0x0802000, 0x170000}, /* 8: SQM0 */ {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {1, 0x08f0000, 0x08f2000, 0x172000}}}, {{{1, 0x0900000, 0x0902000, 0x174000}, /* 9: SQM1 */ {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {1, 0x09f0000, 0x09f2000, 0x176000}}}, {{{0, 0x0a00000, 0x0a02000, 0x178000}, /* 10: SQM2 */ {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {1, 0x0af0000, 0x0af2000, 0x17a000}}}, {{{0, 0x0b00000, 0x0b02000, 0x17c000}, /* 11: SQM3 */ {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {1, 0x0bf0000, 0x0bf2000, 0x17e000}}}, {{{1, 0x0c00000, 0x0c04000, 0x1d4000}}}, /* 12: I2Q */ {{{1, 0x0d00000, 0x0d04000, 0x1a4000}}}, /* 13: TMR */ {{{1, 0x0e00000, 0x0e04000, 0x1a0000}}}, /* 14: ROMUSB */ {{{1, 0x0f00000, 0x0f01000, 0x164000}}}, /* 15: PEG4 */ {{{0, 0x1000000, 0x1004000, 0x1a8000}}}, /* 16: XDMA */ {{{1, 0x1100000, 0x1101000, 0x160000}}}, /* 17: PEG0 */ {{{1, 0x1200000, 0x1201000, 0x161000}}}, /* 18: PEG1 */ {{{1, 0x1300000, 0x1301000, 0x162000}}}, /* 19: PEG2 */ {{{1, 0x1400000, 0x1401000, 0x163000}}}, /* 20: PEG3 */ {{{1, 0x1500000, 0x1501000, 0x165000}}}, /* 21: P2ND */ {{{1, 0x1600000, 0x1601000, 0x166000}}}, /* 22: P2NI */ {{{0, 0, 0, 0}}}, /* 23: */ {{{0, 0, 0, 0}}}, /* 24: */ {{{0, 0, 0, 0}}}, /* 25: */ {{{0, 0, 0, 0}}}, /* 26: */ {{{0, 0, 0, 0}}}, /* 27: */ {{{0, 0, 0, 0}}}, /* 28: */ {{{1, 0x1d00000, 0x1d10000, 0x190000}}}, /* 29: MS */ {{{1, 0x1e00000, 0x1e01000, 0x16a000}}}, /* 30: P2NR2 */ {{{1, 0x1f00000, 0x1f10000, 0x150000}}}, /* 31: EPG */ {{{0}}}, /* 32: PCI */ {{{1, 0x2100000, 0x2102000, 0x120000}, /* 33: PCIE */ {1, 0x2110000, 0x2120000, 0x130000}, {1, 0x2120000, 0x2122000, 0x124000}, {1, 0x2130000, 0x2132000, 0x126000}, {1, 0x2140000, 0x2142000, 0x128000}, {1, 0x2150000, 0x2152000, 0x12a000}, {1, 0x2160000, 0x2170000, 0x110000}, {1, 0x2170000, 0x2172000, 0x12e000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}, {0, 0x0000000, 0x0000000, 0x000000}}}, {{{1, 0x2200000, 0x2204000, 0x1b0000}}}, /* 34: CAM */ {{{0}}}, /* 35: */ {{{0}}}, /* 36: */ {{{0}}}, /* 37: */ {{{0}}}, /* 38: */ {{{0}}}, /* 39: */ {{{1, 0x2800000, 0x2804000, 0x1a4000}}}, /* 40: TMR */ {{{1, 0x2900000, 0x2901000, 0x16b000}}}, /* 41: P2NR3 */ {{{1, 0x2a00000, 0x2a00400, 0x1ac400}}}, /* 42: RPMX1 */ {{{1, 0x2b00000, 0x2b00400, 0x1ac800}}}, /* 43: RPMX2 */ {{{1, 0x2c00000, 0x2c00400, 0x1acc00}}}, /* 44: RPMX3 */ {{{1, 0x2d00000, 0x2d00400, 0x1ad000}}}, /* 45: RPMX4 */ {{{1, 0x2e00000, 0x2e00400, 0x1ad400}}}, /* 46: RPMX5 */ {{{1, 0x2f00000, 0x2f00400, 0x1ad800}}}, /* 47: RPMX6 */ {{{1, 0x3000000, 0x3000400, 0x1adc00}}}, /* 48: RPMX7 */ {{{0, 0x3100000, 0x3104000, 0x1a8000}}}, /* 49: XDMA */ {{{1, 0x3200000, 0x3204000, 0x1d4000}}}, /* 50: I2Q */ {{{1, 0x3300000, 0x3304000, 0x1a0000}}}, /* 51: ROMUSB */ {{{0}}}, /* 52: */ {{{1, 0x3500000, 0x3500400, 0x1ac000}}}, /* 53: RPMX0 */ {{{1, 0x3600000, 0x3600400, 0x1ae000}}}, /* 54: RPMX8 */ {{{1, 0x3700000, 0x3700400, 0x1ae400}}}, /* 55: RPMX9 */ {{{1, 0x3800000, 0x3804000, 0x1d0000}}}, /* 56: OCM0 */ {{{1, 0x3900000, 0x3904000, 0x1b4000}}}, /* 57: CRYPTO */ {{{1, 0x3a00000, 0x3a04000, 0x1d8000}}}, /* 58: SMB */ {{{0}}}, /* 59: I2C0 */ {{{0}}}, /* 60: I2C1 */ {{{1, 0x3d00000, 0x3d04000, 0x1dc000}}}, /* 61: LPC */ {{{1, 0x3e00000, 0x3e01000, 0x167000}}}, /* 62: P2NC */ {{{1, 0x3f00000, 0x3f01000, 0x168000}}} /* 63: P2NR0 */ }; /* * top 12 bits of crb internal address (hub, agent) */ static uint32_t crb_hub_agt[64] = { 0, UNM_HW_CRB_HUB_AGT_ADR_PS, UNM_HW_CRB_HUB_AGT_ADR_MN, UNM_HW_CRB_HUB_AGT_ADR_MS, 0, UNM_HW_CRB_HUB_AGT_ADR_SRE, UNM_HW_CRB_HUB_AGT_ADR_NIU, UNM_HW_CRB_HUB_AGT_ADR_QMN, UNM_HW_CRB_HUB_AGT_ADR_SQN0, UNM_HW_CRB_HUB_AGT_ADR_SQN1, UNM_HW_CRB_HUB_AGT_ADR_SQN2, UNM_HW_CRB_HUB_AGT_ADR_SQN3, UNM_HW_CRB_HUB_AGT_ADR_I2Q, UNM_HW_CRB_HUB_AGT_ADR_TIMR, UNM_HW_CRB_HUB_AGT_ADR_ROMUSB, UNM_HW_CRB_HUB_AGT_ADR_PGN4, UNM_HW_CRB_HUB_AGT_ADR_XDMA, UNM_HW_CRB_HUB_AGT_ADR_PGN0, UNM_HW_CRB_HUB_AGT_ADR_PGN1, UNM_HW_CRB_HUB_AGT_ADR_PGN2, UNM_HW_CRB_HUB_AGT_ADR_PGN3, UNM_HW_CRB_HUB_AGT_ADR_PGND, UNM_HW_CRB_HUB_AGT_ADR_PGNI, UNM_HW_CRB_HUB_AGT_ADR_PGS0, UNM_HW_CRB_HUB_AGT_ADR_PGS1, UNM_HW_CRB_HUB_AGT_ADR_PGS2, UNM_HW_CRB_HUB_AGT_ADR_PGS3, 0, UNM_HW_CRB_HUB_AGT_ADR_PGSI, UNM_HW_CRB_HUB_AGT_ADR_SN, 0, UNM_HW_CRB_HUB_AGT_ADR_EG, 0, UNM_HW_CRB_HUB_AGT_ADR_PS, UNM_HW_CRB_HUB_AGT_ADR_CAM, 0, 0, 0, 0, 0, UNM_HW_CRB_HUB_AGT_ADR_TIMR, 0, UNM_HW_CRB_HUB_AGT_ADR_RPMX1, UNM_HW_CRB_HUB_AGT_ADR_RPMX2, UNM_HW_CRB_HUB_AGT_ADR_RPMX3, UNM_HW_CRB_HUB_AGT_ADR_RPMX4, UNM_HW_CRB_HUB_AGT_ADR_RPMX5, UNM_HW_CRB_HUB_AGT_ADR_RPMX6, UNM_HW_CRB_HUB_AGT_ADR_RPMX7, UNM_HW_CRB_HUB_AGT_ADR_XDMA, UNM_HW_CRB_HUB_AGT_ADR_I2Q, UNM_HW_CRB_HUB_AGT_ADR_ROMUSB, 0, UNM_HW_CRB_HUB_AGT_ADR_RPMX0, UNM_HW_CRB_HUB_AGT_ADR_RPMX8, UNM_HW_CRB_HUB_AGT_ADR_RPMX9, UNM_HW_CRB_HUB_AGT_ADR_OCM0, 0, UNM_HW_CRB_HUB_AGT_ADR_SMB, UNM_HW_CRB_HUB_AGT_ADR_I2C0, UNM_HW_CRB_HUB_AGT_ADR_I2C1, 0, UNM_HW_CRB_HUB_AGT_ADR_PGNC, 0, }; static void * ql_8021_pci_base_offsetfset(ql_adapter_state_t *ha, uint64_t off) { if ((off < ha->first_page_group_end) && (off >= ha->first_page_group_start)) { return ((void *)(ha->nx_pcibase + off)); } return (NULL); } /* ARGSUSED */ static void ql_crb_addr_transform_setup(ql_adapter_state_t *ha) { crb_addr_transform(XDMA); crb_addr_transform(TIMR); crb_addr_transform(SRE); crb_addr_transform(SQN3); crb_addr_transform(SQN2); crb_addr_transform(SQN1); crb_addr_transform(SQN0); crb_addr_transform(SQS3); crb_addr_transform(SQS2); crb_addr_transform(SQS1); crb_addr_transform(SQS0); crb_addr_transform(RPMX7); crb_addr_transform(RPMX6); crb_addr_transform(RPMX5); crb_addr_transform(RPMX4); crb_addr_transform(RPMX3); crb_addr_transform(RPMX2); crb_addr_transform(RPMX1); crb_addr_transform(RPMX0); crb_addr_transform(ROMUSB); crb_addr_transform(SN); crb_addr_transform(QMN); crb_addr_transform(QMS); crb_addr_transform(PGNI); crb_addr_transform(PGND); crb_addr_transform(PGN3); crb_addr_transform(PGN2); crb_addr_transform(PGN1); crb_addr_transform(PGN0); crb_addr_transform(PGSI); crb_addr_transform(PGSD); crb_addr_transform(PGS3); crb_addr_transform(PGS2); crb_addr_transform(PGS1); crb_addr_transform(PGS0); crb_addr_transform(PS); crb_addr_transform(PH); crb_addr_transform(NIU); crb_addr_transform(I2Q); crb_addr_transform(EG); crb_addr_transform(MN); crb_addr_transform(MS); crb_addr_transform(CAS2); crb_addr_transform(CAS1); crb_addr_transform(CAS0); crb_addr_transform(CAM); crb_addr_transform(C2C1); crb_addr_transform(C2C0); crb_addr_transform(SMB); crb_addr_transform(OCM0); /* * Used only in P3 just define it for P2 also. */ crb_addr_transform(I2C0); crb_table_initialized = 1; } /* * In: 'off' is offset from CRB space in 128M pci map * Out: 'off' is 2M pci map addr * side effect: lock crb window */ static void ql_8021_pci_set_crbwindow_2M(ql_adapter_state_t *ha, uint64_t *off) { uint32_t win_read; ha->crb_win = (uint32_t)CRB_HI(*off); WRT_REG_DWORD(ha, CRB_WINDOW_2M + ha->nx_pcibase, ha->crb_win); /* * Read back value to make sure write has gone through before trying * to use it. */ win_read = RD_REG_DWORD(ha, CRB_WINDOW_2M + ha->nx_pcibase); if (win_read != ha->crb_win) { EL(ha, "Written crbwin (0x%x) != Read crbwin (0x%x), " "off=0x%llx\n", ha->crb_win, win_read, *off); } *off = (*off & MASK(16)) + CRB_INDIRECT_2M + (uintptr_t)ha->nx_pcibase; } static void ql_8021_wr_32(ql_adapter_state_t *ha, uint64_t off, uint32_t data) { int rv; rv = ql_8021_pci_get_crb_addr_2M(ha, &off); if (rv == -1) { cmn_err(CE_PANIC, "ql_8021_wr_32, ql_8021_pci_get_crb_addr_" "2M=-1\n"); } if (rv == 1) { (void) ql_8021_crb_win_lock(ha); ql_8021_pci_set_crbwindow_2M(ha, &off); } WRT_REG_DWORD(ha, (uintptr_t)off, data); if (rv == 1) { ql_8021_crb_win_unlock(ha); } } static void ql_8021_rd_32(ql_adapter_state_t *ha, uint64_t off, uint32_t *data) { int rv; uint32_t n; rv = ql_8021_pci_get_crb_addr_2M(ha, &off); if (rv == -1) { cmn_err(CE_PANIC, "ql_8021_rd_32, ql_8021_pci_get_crb_addr_" "2M=-1\n"); } if (rv == 1) { (void) ql_8021_crb_win_lock(ha); ql_8021_pci_set_crbwindow_2M(ha, &off); } n = RD_REG_DWORD(ha, (uintptr_t)off); if (data != NULL) { *data = n; } if (rv == 1) { ql_8021_crb_win_unlock(ha); } } static int ql_8021_crb_win_lock(ql_adapter_state_t *ha) { uint32_t done = 0, timeout = 0; while (!done) { /* acquire semaphore3 from PCI HW block */ ql_8021_rd_32(ha, UNM_PCIE_REG(PCIE_SEM7_LOCK), &done); if (done == 1) { break; } if (timeout >= CRB_WIN_LOCK_TIMEOUT) { EL(ha, "timeout\n"); return (-1); } timeout++; /* Yield CPU */ delay(1); } ql_8021_wr_32(ha, UNM_CRB_WIN_LOCK_ID, ha->function_number); return (0); } static void ql_8021_crb_win_unlock(ql_adapter_state_t *ha) { ql_8021_rd_32(ha, UNM_PCIE_REG(PCIE_SEM7_UNLOCK), NULL); } static int ql_8021_pci_get_crb_addr_2M(ql_adapter_state_t *ha, uint64_t *off) { crb_128M_2M_sub_block_map_t *m; if (*off >= UNM_CRB_MAX) { EL(ha, "%llx >= %llx\n", *off, UNM_CRB_MAX); return (-1); } if (*off >= UNM_PCI_CAMQM && (*off < UNM_PCI_CAMQM_2M_END)) { *off = (*off - UNM_PCI_CAMQM) + UNM_PCI_CAMQM_2M_BASE + (uintptr_t)ha->nx_pcibase; return (0); } if (*off < UNM_PCI_CRBSPACE) { EL(ha, "%llx < %llx\n", *off, UNM_PCI_CRBSPACE); return (-1); } *off -= UNM_PCI_CRBSPACE; /* * Try direct map */ m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)]; if (m->valid && ((uint64_t)m->start_128M <= *off) && ((uint64_t)m->end_128M > *off)) { *off = (uint64_t)(*off + m->start_2M - m->start_128M + (uintptr_t)ha->nx_pcibase); return (0); } /* * Not in direct map, use crb window */ return (1); } /* * check memory access boundary. * used by test agent. support ddr access only for now */ /* ARGSUSED */ static uint32_t ql_8021_pci_mem_bound_check(ql_adapter_state_t *ha, uint64_t addr, uint32_t size) { /*LINTED suspicious 0 comparison*/ if (!QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_DDR_NET, UNM_ADDR_DDR_NET_MAX) || /*LINTED suspicious 0 comparison*/ !QL_8021_ADDR_IN_RANGE(addr + size - 1, UNM_ADDR_DDR_NET, UNM_ADDR_DDR_NET_MAX) || ((size != 1) && (size != 2) && (size != 4) && (size != 8))) { return (0); } return (1); } static uint64_t ql_8021_pci_set_window(ql_adapter_state_t *ha, uint64_t addr) { uint32_t window, win_read; /*LINTED suspicious 0 comparison*/ if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_DDR_NET, UNM_ADDR_DDR_NET_MAX)) { /* DDR network side */ window = (uint32_t)MN_WIN(addr); ha->ddr_mn_window = window; ql_8021_wr_32(ha, ha->mn_win_crb | UNM_PCI_CRBSPACE, window); ql_8021_rd_32(ha, ha->mn_win_crb | UNM_PCI_CRBSPACE, &win_read); if ((win_read << 17) != window) { EL(ha, "Warning, Written MNwin (0x%x) != Read MNwin " "(0x%x)\n", window, win_read); } addr = GET_MEM_OFFS_2M(addr) + UNM_PCI_DDR_NET; } else if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_OCM0, UNM_ADDR_OCM0_MAX)) { uint32_t temp1; if ((addr & 0x00ff800) == 0xff800) { /* if bits 19:18&17:11 are on */ EL(ha, "QM access not handled\n"); addr = -1UL; } window = (uint32_t)OCM_WIN(addr); ha->ddr_mn_window = window; ql_8021_wr_32(ha, ha->mn_win_crb | UNM_PCI_CRBSPACE, window); ql_8021_rd_32(ha, ha->mn_win_crb | UNM_PCI_CRBSPACE, &win_read); temp1 = ((window & 0x1FF) << 7) | ((window & 0x0FFFE0000) >> 17); if (win_read != temp1) { EL(ha, "Written OCMwin (0x%x) != Read OCMwin (0x%x)\n", temp1, win_read); } addr = GET_MEM_OFFS_2M(addr) + UNM_PCI_OCM0_2M; } else if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_QDR_NET, NX_P3_ADDR_QDR_NET_MAX)) { /* QDR network side */ window = (uint32_t)MS_WIN(addr); ha->qdr_sn_window = window; ql_8021_wr_32(ha, ha->mn_win_crb | UNM_PCI_CRBSPACE, window); ql_8021_rd_32(ha, ha->mn_win_crb | UNM_PCI_CRBSPACE, &win_read); if (win_read != window) { EL(ha, "Written MSwin (0x%x) != Read MSwin (0x%x)\n", window, win_read); } addr = GET_MEM_OFFS_2M(addr) + UNM_PCI_QDR_NET; } else { /* * peg gdb frequently accesses memory that doesn't exist, * this limits the chit chat so debugging isn't slowed down. */ if ((pci_set_window_warning_count++ < 8) || (pci_set_window_warning_count % 64 == 0)) { EL(ha, "Unknown address range\n"); } addr = -1UL; } return (addr); } /* check if address is in the same windows as the previous access */ static int ql_8021_pci_is_same_window(ql_adapter_state_t *ha, uint64_t addr) { uint32_t window; uint64_t qdr_max; qdr_max = NX_P3_ADDR_QDR_NET_MAX; /*LINTED suspicious 0 comparison*/ if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_DDR_NET, UNM_ADDR_DDR_NET_MAX)) { /* DDR network side */ EL(ha, "DDR network side\n"); return (0); /* MN access can not come here */ } else if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_OCM0, UNM_ADDR_OCM0_MAX)) { return (1); } else if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_OCM1, UNM_ADDR_OCM1_MAX)) { return (1); } else if (QL_8021_ADDR_IN_RANGE(addr, UNM_ADDR_QDR_NET, qdr_max)) { /* QDR network side */ window = (uint32_t)(((addr - UNM_ADDR_QDR_NET) >> 22) & 0x3f); if (ha->qdr_sn_window == window) { return (1); } } return (0); } static int ql_8021_pci_mem_read_direct(ql_adapter_state_t *ha, uint64_t off, void *data, uint32_t size) { void *addr; int ret = 0; uint64_t start; /* * If attempting to access unknown address or straddle hw windows, * do not access. */ if (((start = ql_8021_pci_set_window(ha, off)) == -1UL) || (ql_8021_pci_is_same_window(ha, off + size - 1) == 0)) { EL(ha, "out of bound pci memory access. offset is 0x%llx\n", off); return (-1); } addr = ql_8021_pci_base_offsetfset(ha, start); if (!addr) { addr = (void *)((uint8_t *)ha->nx_pcibase + start); } switch (size) { case 1: *(uint8_t *)data = RD_REG_BYTE(ha, addr); break; case 2: *(uint16_t *)data = RD_REG_WORD(ha, addr); break; case 4: *(uint32_t *)data = RD_REG_DWORD(ha, addr); break; case 8: *(uint64_t *)data = RD_REG_DDWORD(ha, addr); break; default: EL(ha, "invalid size=%x\n", size); ret = -1; break; } return (ret); } static int ql_8021_pci_mem_write_direct(ql_adapter_state_t *ha, uint64_t off, void *data, uint32_t size) { void *addr; int ret = 0; uint64_t start; /* * If attempting to access unknown address or straddle hw windows, * do not access. */ if (((start = ql_8021_pci_set_window(ha, off)) == -1UL) || (ql_8021_pci_is_same_window(ha, off + size -1) == 0)) { EL(ha, "out of bound pci memory access. offset is 0x%llx\n", off); return (-1); } addr = ql_8021_pci_base_offsetfset(ha, start); if (!addr) { addr = (void *)((uint8_t *)ha->nx_pcibase + start); } switch (size) { case 1: WRT_REG_BYTE(ha, addr, *(uint8_t *)data); break; case 2: WRT_REG_WORD(ha, addr, *(uint16_t *)data); break; case 4: WRT_REG_DWORD(ha, addr, *(uint32_t *)data); break; case 8: WRT_REG_DDWORD(ha, addr, *(uint64_t *)data); break; default: EL(ha, "invalid size=%x\n", size); ret = -1; break; } return (ret); } static int ql_8021_pci_mem_read_2M(ql_adapter_state_t *ha, uint64_t off, void *data, uint32_t size) { int j = 0; uint32_t i, temp, sz[2], loop, shift_amount; uint64_t start, end, k; uint64_t off8, off0[2], val, mem_crb, word[2] = {0, 0}; /* * If not MN, go check for MS or invalid. */ if (off >= UNM_ADDR_QDR_NET && off <= NX_P3_ADDR_QDR_NET_MAX) { mem_crb = UNM_CRB_QDR_NET; } else { mem_crb = UNM_CRB_DDR_NET; if (ql_8021_pci_mem_bound_check(ha, off, size) == 0) { return (ql_8021_pci_mem_read_direct(ha, off, data, size)); } } if (NX_IS_REVISION_P3PLUS(ha->rev_id)) { off8 = off & 0xfffffff0; off0[0] = off & 0xf; sz[0] = (uint32_t)(((uint64_t)size < (16 - off0[0])) ? size : (16 - off0[0])); shift_amount = 4; } else { off8 = off & 0xfffffff8; off0[0] = off & 0x7; sz[0] = (uint32_t)(((uint64_t)size < (8 - off0[0])) ? size : (8 - off0[0])); shift_amount = 3; } loop = (uint32_t)(((off0[0] + size - 1) >> shift_amount) + 1); off0[1] = 0; sz[1] = size - sz[0]; /* * don't lock here - write_wx gets the lock if each time * write_lock_irqsave(&adapter->adapter_lock, flags); * netxen_nic_pci_change_crbwindow_128M(adapter, 0); */ for (i = 0; i < loop; i++) { temp = (uint32_t)(off8 + (i << shift_amount)); ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_LO, temp); temp = 0; ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_HI, temp); temp = MIU_TA_CTL_ENABLE; ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); for (j = 0; j < MAX_CTL_CHECK; j++) { ql_8021_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL, &temp); if ((temp & MIU_TA_CTL_BUSY) == 0) { break; } } if (j >= MAX_CTL_CHECK) { EL(ha, "failed to read through agent\n"); break; } start = off0[i] >> 2; end = (off0[i] + sz[i] - 1) >> 2; for (k = start; k <= end; k++) { ql_8021_rd_32(ha, mem_crb + MIU_TEST_AGT_RDDATA(k), &temp); word[i] |= ((uint64_t)temp << (32 * (k & 1))); } } /* * netxen_nic_pci_change_crbwindow_128M(adapter, 1); * write_unlock_irqrestore(&adapter->adapter_lock, flags); */ if (j >= MAX_CTL_CHECK) { return (-1); } if ((off0[0] & 7) == 0) { val = word[0]; } else { val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) | ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8)); } switch (size) { case 1: *(uint8_t *)data = (uint8_t)val; break; case 2: *(uint16_t *)data = (uint16_t)val; break; case 4: *(uint32_t *)data = (uint32_t)val; break; case 8: *(uint64_t *)data = val; break; } return (0); } static int ql_8021_pci_mem_write_2M(ql_adapter_state_t *ha, uint64_t off, void *data, uint32_t size) { int j, ret = 0; uint32_t i, temp, loop, sz[2]; uint32_t scale, shift_amount, p3p, startword; uint64_t off8, off0, mem_crb, tmpw, word[2] = {0, 0}; /* * If not MN, go check for MS or invalid. */ if (off >= UNM_ADDR_QDR_NET && off <= NX_P3_ADDR_QDR_NET_MAX) { mem_crb = UNM_CRB_QDR_NET; } else { mem_crb = UNM_CRB_DDR_NET; if (ql_8021_pci_mem_bound_check(ha, off, size) == 0) { return (ql_8021_pci_mem_write_direct(ha, off, data, size)); } } off0 = off & 0x7; sz[0] = (uint32_t)(((uint64_t)size < (8 - off0)) ? size : (8 - off0)); sz[1] = size - sz[0]; if (NX_IS_REVISION_P3PLUS(ha->rev_id)) { off8 = off & 0xfffffff0; loop = (uint32_t)((((off & 0xf) + size - 1) >> 4) + 1); shift_amount = 4; scale = 2; p3p = 1; startword = (uint32_t)((off & 0xf) / 8); } else { off8 = off & 0xfffffff8; loop = (uint32_t)(((off0 + size - 1) >> 3) + 1); shift_amount = 3; scale = 1; p3p = 0; startword = 0; } if (p3p || (size != 8) || (off0 != 0)) { for (i = 0; i < loop; i++) { if (ql_8021_pci_mem_read_2M(ha, off8 + (i << shift_amount), &word[i * scale], 8)) { EL(ha, "8021_pci_mem_read_2M != 0\n"); return (-1); } } } switch (size) { case 1: tmpw = (uint64_t)(*((uint8_t *)data)); break; case 2: tmpw = (uint64_t)(*((uint16_t *)data)); break; case 4: tmpw = (uint64_t)(*((uint32_t *)data)); break; case 8: default: tmpw = *((uint64_t *)data); break; } if (p3p) { if (sz[0] == 8) { word[startword] = tmpw; } else { word[startword] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); word[startword] |= tmpw << (off0 * 8); } if (sz[1] != 0) { word[startword+1] &= ~(~0ULL << (sz[1] * 8)); word[startword+1] |= tmpw >> (sz[0] * 8); } } else { word[startword] &= ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8)); word[startword] |= tmpw << (off0 * 8); if (loop == 2) { word[1] &= ~(~0ULL << (sz[1] * 8)); word[1] |= tmpw >> (sz[0] * 8); } } /* * don't lock here - write_wx gets the lock if each time * write_lock_irqsave(&adapter->adapter_lock, flags); * netxen_nic_pci_change_crbwindow_128M(adapter, 0); */ for (i = 0; i < loop; i++) { temp = (uint32_t)(off8 + (i << shift_amount)); ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_LO, temp); temp = 0; ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_HI, temp); temp = (uint32_t)(word[i * scale] & 0xffffffff); ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_WRDATA_LO, temp); temp = (uint32_t)((word[i * scale] >> 32) & 0xffffffff); ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_WRDATA_HI, temp); if (p3p) { temp = (uint32_t)(word[i * scale + 1] & 0xffffffff); ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_WRDATA_UPPER_LO, temp); temp = (uint32_t)((word[i * scale + 1] >> 32) & 0xffffffff); ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_WRDATA_UPPER_HI, temp); } temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE; ql_8021_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp); for (j = 0; j < MAX_CTL_CHECK; j++) { ql_8021_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL, &temp); if ((temp & MIU_TA_CTL_BUSY) == 0) break; } if (j >= MAX_CTL_CHECK) { EL(ha, "failed to write through agent\n"); ret = -1; break; } } return (ret); } static uint32_t ql_8021_decode_crb_addr(ql_adapter_state_t *ha, uint32_t addr) { int i; uint32_t base_addr, offset, pci_base; if (!crb_table_initialized) { ql_crb_addr_transform_setup(ha); } pci_base = ADDR_ERROR; base_addr = addr & 0xfff00000; offset = addr & 0x000fffff; for (i = 0; i < MAX_CRB_XFORM; i++) { if (crb_addr_xform[i] == base_addr) { pci_base = i << 20; break; } } if (pci_base == ADDR_ERROR) { return (pci_base); } else { return (pci_base + offset); } } static int ql_8021_hw_lock(ql_adapter_state_t *ha, uint32_t timer) { uint32_t done = 0, timeout = 0; while (!done) { /* acquire semaphore5 from PCI HW block */ ql_8021_rd_32(ha, UNM_PCIE_REG(PCIE_SEM5_LOCK), &done); if (done == 1) { break; } if (timeout >= timer) { EL(ha, "timeout\n"); return (-1); } timeout++; /* * Yield CPU */ delay(1); } return (0); } static void ql_8021_hw_unlock(ql_adapter_state_t *ha) { ql_8021_rd_32(ha, UNM_PCIE_REG(PCIE_SEM5_UNLOCK), NULL); } static int ql_8021_rom_lock(ql_adapter_state_t *ha) { uint32_t done = 0, timeout = 0; while (!done) { /* acquire semaphore2 from PCI HW block */ ql_8021_rd_32(ha, UNM_PCIE_REG(PCIE_SEM2_LOCK), &done); if (done == 1) { break; } if (timeout >= ROM_LOCK_TIMEOUT) { EL(ha, "timeout\n"); return (-1); } timeout++; /* * Yield CPU */ delay(1); } ql_8021_wr_32(ha, UNM_ROM_LOCK_ID, ROM_LOCK_DRIVER); return (0); } static void ql_8021_rom_unlock(ql_adapter_state_t *ha) { ql_8021_rd_32(ha, UNM_PCIE_REG(PCIE_SEM2_UNLOCK), NULL); } static int ql_8021_wait_rom_done(ql_adapter_state_t *ha) { uint32_t timeout = 0, done = 0; while (done == 0) { ql_8021_rd_32(ha, UNM_ROMUSB_GLB_STATUS, &done); done &= 2; timeout++; if (timeout >= ROM_MAX_TIMEOUT) { EL(ha, "Timeout reached waiting for rom done\n"); return (-1); } } return (0); } static int ql_8021_wait_flash_done(ql_adapter_state_t *ha) { clock_t timer; uint32_t status; for (timer = 30 * drv_usectohz(1000000); timer; timer--) { ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 0); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_RDSR_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done2\n"); return (-1); } /* Get status. */ ql_8021_rd_32(ha, UNM_ROMUSB_ROM_RDATA, &status); if (!(status & BIT_0)) { return (0); } delay(1); } EL(ha, "timeout status=%x\n", status); return (-1); } static int ql_8021_do_rom_fast_read(ql_adapter_state_t *ha, uint32_t addr, uint32_t *valp) { ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ADDRESS, addr); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 3); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_FAST_RD_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done\n"); return (-1); } /* reset abyte_cnt and dummy_byte_cnt */ ql_8021_wr_32(ha, UNM_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); drv_usecwait(10); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 0); ql_8021_rd_32(ha, UNM_ROMUSB_ROM_RDATA, valp); return (0); } int ql_8021_rom_fast_read(ql_adapter_state_t *ha, uint32_t addr, uint32_t *valp) { int ret, loops = 0; while ((ql_8021_rom_lock(ha) != 0) && (loops < 50000)) { drv_usecwait(100); loops++; } if (loops >= 50000) { EL(ha, "rom_lock failed\n"); return (-1); } ret = ql_8021_do_rom_fast_read(ha, addr, valp); ql_8021_rom_unlock(ha); return (ret); } static int ql_8021_do_rom_write(ql_adapter_state_t *ha, uint32_t addr, uint32_t data) { ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 0); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_WREN_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done\n"); return (-1); } ql_8021_wr_32(ha, UNM_ROMUSB_ROM_WDATA, data); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ADDRESS, addr); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 3); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_PP_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done1\n"); return (-1); } if (ql_8021_wait_flash_done(ha)) { EL(ha, "Error waiting for flash done\n"); return (-1); } return (0); } static int ql_8021_do_rom_erase(ql_adapter_state_t *ha, uint32_t addr) { ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 0); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_WREN_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done\n"); return (-1); } ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ADDRESS, addr); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 3); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_SE_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done1\n"); return (-1); } if (ql_8021_wait_flash_done(ha)) { EL(ha, "Error waiting for flash done\n"); return (-1); } return (0); } int ql_8021_rom_read(ql_adapter_state_t *ha, uint32_t addr, uint32_t *bp) { int ret; ret = ql_8021_rom_fast_read(ha, addr << 2, bp) == 0 ? QL_SUCCESS : QL_FUNCTION_FAILED; return (ret); } int ql_8021_rom_write(ql_adapter_state_t *ha, uint32_t addr, uint32_t data) { int ret, loops = 0; while ((ql_8021_rom_lock(ha) != 0) && (loops < 50000)) { drv_usecwait(100); loops++; } if (loops >= 50000) { EL(ha, "rom_lock failed\n"); ret = QL_FUNCTION_TIMEOUT; } else { ret = ql_8021_do_rom_write(ha, addr << 2, data) == 0 ? QL_SUCCESS : QL_FUNCTION_FAILED; ql_8021_rom_unlock(ha); } return (ret); } int ql_8021_rom_erase(ql_adapter_state_t *ha, uint32_t addr) { int ret, loops = 0; while ((ql_8021_rom_lock(ha) != 0) && (loops < 50000)) { drv_usecwait(100); loops++; } if (loops >= 50000) { EL(ha, "rom_lock failed\n"); ret = QL_FUNCTION_TIMEOUT; } else { ret = ql_8021_do_rom_erase(ha, addr << 2) == 0 ? QL_SUCCESS : QL_FUNCTION_FAILED; ql_8021_rom_unlock(ha); } return (ret); } int ql_8021_rom_wrsr(ql_adapter_state_t *ha, uint32_t data) { int ret = QL_SUCCESS, loops = 0; while ((ql_8021_rom_lock(ha) != 0) && (loops < 50000)) { drv_usecwait(100); loops++; } if (loops >= 50000) { EL(ha, "rom_lock failed\n"); ret = QL_FUNCTION_TIMEOUT; } else { ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 0); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_WREN_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done\n"); ret = QL_FUNCTION_FAILED; } else { ql_8021_wr_32(ha, UNM_ROMUSB_ROM_WDATA, data); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_ABYTE_CNT, 0); ql_8021_wr_32(ha, UNM_ROMUSB_ROM_INSTR_OPCODE, UNM_ROMUSB_ROM_WRSR_INSTR); if (ql_8021_wait_rom_done(ha)) { EL(ha, "Error waiting for rom done1\n"); ret = QL_FUNCTION_FAILED; } else if (ql_8021_wait_flash_done(ha)) { EL(ha, "Error waiting for flash done\n"); ret = QL_FUNCTION_FAILED; } } ql_8021_rom_unlock(ha); } return (ret); } static int ql_8021_phantom_init(ql_adapter_state_t *ha) { uint32_t val = 0, err = 0; int retries = 60; do { ql_8021_rd_32(ha, CRB_CMDPEG_STATE, &val); switch (val) { case PHAN_INITIALIZE_COMPLETE: case PHAN_INITIALIZE_ACK: EL(ha, "success=%xh\n", val); return (0); case PHAN_INITIALIZE_FAILED: EL(ha, "PHAN_INITIALIZE_FAILED\n"); err = 1; break; default: break; } if (err) { break; } /* 500 msec wait */ delay(50); } while (--retries); if (!err) { ql_8021_wr_32(ha, CRB_CMDPEG_STATE, PHAN_INITIALIZE_FAILED); } EL(ha, "firmware init failed=%x\n", val); return (-1); } static int ql_8021_pinit_from_rom(ql_adapter_state_t *ha) { int init_delay = 0; struct crb_addr_pair *buf; uint32_t offset, off, i, n, addr, val; /* Grab the lock so that no one can read flash when we reset the chip */ (void) ql_8021_rom_lock(ha); ql_8021_wr_32(ha, UNM_ROMUSB_GLB_SW_RESET, 0xffffffff); /* Just in case it was held when we reset the chip */ ql_8021_rom_unlock(ha); if (ql_8021_rom_fast_read(ha, 0, &n) != 0 || n != 0xcafecafe || ql_8021_rom_fast_read(ha, 4, &n) != 0) { EL(ha, "ERROR Reading crb_init area: n: %08x\n", n); return (-1); } offset = n & 0xffff; n = (n >> 16) & 0xffff; if (n >= 1024) { EL(ha, "n=0x%x Error! NetXen card flash not initialized\n", n); return (-1); } buf = kmem_zalloc(n * sizeof (struct crb_addr_pair), KM_SLEEP); if (buf == NULL) { EL(ha, "Unable to zalloc memory\n"); return (-1); } for (i = 0; i < n; i++) { if (ql_8021_rom_fast_read(ha, 8 * i + 4 * offset, &val) != 0 || ql_8021_rom_fast_read(ha, 8 * i + 4 * offset + 4, &addr) != 0) { kmem_free(buf, n * sizeof (struct crb_addr_pair)); EL(ha, "ql_8021_rom_fast_read != 0 to zalloc memory\n"); return (-1); } buf[i].addr = addr; buf[i].data = val; } for (i = 0; i < n; i++) { off = ql_8021_decode_crb_addr(ha, buf[i].addr); if (off == ADDR_ERROR) { EL(ha, "Err: Unknown addr: 0x%lx\n", buf[i].addr); continue; } off += UNM_PCI_CRBSPACE; if (off & 1) { continue; } /* skipping cold reboot MAGIC */ if (off == UNM_RAM_COLD_BOOT) { continue; } if (off == (UNM_CRB_I2C0 + 0x1c)) { continue; } /* do not reset PCI */ if (off == (ROMUSB_GLB + 0xbc)) { continue; } if (off == (ROMUSB_GLB + 0xa8)) { continue; } if (off == (ROMUSB_GLB + 0xc8)) { /* core clock */ continue; } if (off == (ROMUSB_GLB + 0x24)) { /* MN clock */ continue; } if (off == (ROMUSB_GLB + 0x1c)) { /* MS clock */ continue; } if ((off & 0x0ff00000) == UNM_CRB_DDR_NET) { continue; } if (off == (UNM_CRB_PEG_NET_1 + 0x18) && !NX_IS_REVISION_P3PLUS(ha->rev_id)) { buf[i].data = 0x1020; } /* skip the function enable register */ if (off == UNM_PCIE_REG(PCIE_SETUP_FUNCTION)) { continue; } if (off == UNM_PCIE_REG(PCIE_SETUP_FUNCTION2)) { continue; } if ((off & 0x0ff00000) == UNM_CRB_SMB) { continue; } /* After writing this register, HW needs time for CRB */ /* to quiet down (else crb_window returns 0xffffffff) */ init_delay = 1; if (off == UNM_ROMUSB_GLB_SW_RESET) { init_delay = 100; /* Sleep 1000 msecs */ } ql_8021_wr_32(ha, off, buf[i].data); delay(init_delay); } kmem_free(buf, n * sizeof (struct crb_addr_pair)); /* disable_peg_cache_all */ /* p2dn replyCount */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_D + 0xec, 0x1e); /* disable_peg_cache 0 */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_D + 0x4c, 8); /* disable_peg_cache 1 */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_I + 0x4c, 8); /* peg_clr_all */ /* peg_clr 0 */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_0 + 0x8, 0); ql_8021_wr_32(ha, UNM_CRB_PEG_NET_0 + 0xc, 0); /* peg_clr 1 */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_1 + 0x8, 0); ql_8021_wr_32(ha, UNM_CRB_PEG_NET_1 + 0xc, 0); /* peg_clr 2 */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_2 + 0x8, 0); ql_8021_wr_32(ha, UNM_CRB_PEG_NET_2 + 0xc, 0); /* peg_clr 3 */ ql_8021_wr_32(ha, UNM_CRB_PEG_NET_3 + 0x8, 0); ql_8021_wr_32(ha, UNM_CRB_PEG_NET_3 + 0xc, 0); return (0); } static int ql_8021_load_from_flash(ql_adapter_state_t *ha) { int i; uint32_t flashaddr, memaddr; uint32_t high, low, size; uint64_t data; size = ha->bootloader_size / 2; flashaddr = ha->bootloader_addr << 2; memaddr = BOOTLD_START; for (i = 0; i < size; i++) { if ((ql_8021_rom_fast_read(ha, flashaddr, &low)) || (ql_8021_rom_fast_read(ha, flashaddr + 4, &high))) { EL(ha, "ql_8021_rom_fast_read != 0\n"); return (-1); } data = ((uint64_t)high << 32) | low; (void) ql_8021_pci_mem_write_2M(ha, memaddr, &data, 8); flashaddr += 8; memaddr += 8; } size = ha->flash_fw_size / 2; flashaddr = ha->flash_fw_addr << 2; memaddr = IMAGE_START; for (i = 0; i < size; i++) { if ((ql_8021_rom_fast_read(ha, flashaddr, &low)) || (ql_8021_rom_fast_read(ha, flashaddr + 4, &high))) { EL(ha, "ql_8021_rom_fast_read3 != 0\n"); return (-1); } data = ((uint64_t)high << 32) | low; (void) ql_8021_pci_mem_write_2M(ha, memaddr, &data, 8); flashaddr += 8; memaddr += 8; } return (0); } static int ql_8021_load_firmware(ql_adapter_state_t *ha) { uint64_t data; uint32_t i, flashaddr, size; uint8_t *bp, n, *dp; bp = (uint8_t *)(ha->risc_fw[0].code); dp = (uint8_t *)&size; for (n = 0; n < 4; n++) { dp[n] = *bp++; } LITTLE_ENDIAN_32(&size); EL(ha, "signature=%x\n", size); size = (IMAGE_START - BOOTLD_START) / 8; bp = (uint8_t *)(ha->risc_fw[0].code + BOOTLD_START); flashaddr = BOOTLD_START; dp = (uint8_t *)&data; for (i = 0; i < size; i++) { for (n = 0; n < 8; n++) { dp[n] = *bp++; } LITTLE_ENDIAN_64(&data); (void) ql_8021_pci_mem_write_2M(ha, flashaddr, &data, 8); flashaddr += 8; } bp = (uint8_t *)(ha->risc_fw[0].code + FW_SIZE_OFFSET); dp = (uint8_t *)&size; for (n = 0; n < 4; n++) { dp[n] = *bp++; } LITTLE_ENDIAN_32(&size); EL(ha, "IMAGE_START size=%llx\n", size); size = (size + 7) / 8; bp = (uint8_t *)(ha->risc_fw[0].code + IMAGE_START); flashaddr = IMAGE_START; dp = (uint8_t *)&data; for (i = 0; i < size; i++) { for (n = 0; n < 8; n++) { dp[n] = *bp++; } LITTLE_ENDIAN_64(&data); (void) ql_8021_pci_mem_write_2M(ha, flashaddr, &data, 8); flashaddr += 8; } return (0); } static int ql_8021_init_p3p(ql_adapter_state_t *ha) { uint32_t data; /* ??? */ ql_8021_wr_32(ha, UNM_PORT_MODE_ADDR, UNM_PORT_MODE_AUTO_NEG); delay(drv_usectohz(1000000)); /* CAM RAM Cold Boot Register */ ql_8021_rd_32(ha, UNM_RAM_COLD_BOOT, &data); if (data == 0x55555555) { ql_8021_rd_32(ha, UNM_ROMUSB_GLB_SW_RESET, &data); if (data != 0x80000f) { EL(ha, "CRB_UNM_GLB_SW_RST=%x exit\n", data); return (-1); } ql_8021_wr_32(ha, UNM_RAM_COLD_BOOT, 0); } ql_8021_rd_32(ha, UNM_ROMUSB_GLB_PEGTUNE_DONE, &data); data |= 1; ql_8021_wr_32(ha, UNM_ROMUSB_GLB_PEGTUNE_DONE, data); /* * ??? * data = ha->pci_bus_addr | BIT_31; * ql_8021_wr_32(ha, UNM_BUS_DEV_NO, data); */ return (0); } /* ARGSUSED */ void ql_8021_reset_chip(ql_adapter_state_t *ha) { /* * Disable interrupts does not work on a per function bases * leave them enabled */ ql_8021_enable_intrs(ha); ADAPTER_STATE_LOCK(ha); ha->flags |= INTERRUPTS_ENABLED; ADAPTER_STATE_UNLOCK(ha); (void) ql_stop_firmware(ha); } static int ql_8021_reset_hw(ql_adapter_state_t *ha, int type) { int ret; uint32_t rst; /* scrub dma mask expansion register */ ql_8021_wr_32(ha, CRB_DMA_SHIFT, 0x55555555); /* Overwrite stale initialization register values */ ql_8021_wr_32(ha, CRB_CMDPEG_STATE, 0); ql_8021_wr_32(ha, CRB_RCVPEG_STATE, 0); ql_8021_wr_32(ha, UNM_PEG_HALT_STATUS1, 0); ql_8021_wr_32(ha, UNM_PEG_HALT_STATUS2, 0); (void) ql_8021_pinit_from_rom(ha); delay(1); /* Bring QM and CAMRAM out of reset */ ql_8021_rd_32(ha, UNM_ROMUSB_GLB_SW_RESET, &rst); rst &= ~((1 << 28) | (1 << 24)); ql_8021_wr_32(ha, UNM_ROMUSB_GLB_SW_RESET, rst); switch (type) { case 0: ret = ql_8021_init_p3p(ha); break; case 1: ret = ql_8021_load_from_flash(ha); break; case 2: ret = ql_8021_load_firmware(ha); break; } delay(1); ql_8021_wr_32(ha, UNM_CRB_PEG_NET_0 + 0x18, 0x1020); ql_8021_wr_32(ha, UNM_ROMUSB_GLB_SW_RESET, 0x80001e); if (ret) { EL(ha, "type=%d, ret=%d\n", type, ret); } else { ret = ql_8021_phantom_init(ha); } return (ret); } int ql_8021_load_risc(ql_adapter_state_t *ha) { int rv = 0; static int ql_8021_fw_loaded = 0; GLOBAL_HW_LOCK(); if (!ql_8021_fw_loaded) { if (ha->risc_fw[0].code) { EL(ha, "from driver\n"); rv = ql_8021_reset_hw(ha, 2); } else { /* * BIOS method * ql_8021_reset_hw(ha, 0) */ EL(ha, "from flash\n"); rv = ql_8021_reset_hw(ha, 1); } if (rv == 0) { ql_8021_fw_loaded = 1; ql_8021_wr_32(ha, CRB_DMA_SHIFT, 0x55555555); ql_8021_wr_32(ha, UNM_PEG_HALT_STATUS1, 0x0); ql_8021_wr_32(ha, UNM_PEG_HALT_STATUS2, 0x0); GLOBAL_HW_UNLOCK(); ADAPTER_STATE_LOCK(ha); ha->flags &= ~INTERRUPTS_ENABLED; ADAPTER_STATE_UNLOCK(ha); (void) ql_8021_enable_intrs(ha); ADAPTER_STATE_LOCK(ha); ha->flags |= INTERRUPTS_ENABLED; ADAPTER_STATE_UNLOCK(ha); } else { GLOBAL_HW_UNLOCK(); } } else { GLOBAL_HW_UNLOCK(); EL(ha, "Firmware loaded by other function\n"); } if (rv == 0) { ql_8021_rd_32(ha, UNM_FW_VERSION_MAJOR, &ha->fw_major_version); ql_8021_rd_32(ha, UNM_FW_VERSION_MINOR, &ha->fw_minor_version); ql_8021_rd_32(ha, UNM_FW_VERSION_SUB, &ha->fw_subminor_version); EL(ha, "fw v%d.%02d.%02d\n", ha->fw_major_version, ha->fw_minor_version, ha->fw_subminor_version); } else { EL(ha, "status = -1\n"); return (QL_FUNCTION_FAILED); } return (QL_SUCCESS); } void ql_8021_clr_hw_intr(ql_adapter_state_t *ha) { ql_8021_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff); ql_8021_rd_32(ha, ISR_INT_VECTOR, NULL); ql_8021_rd_32(ha, ISR_INT_VECTOR, NULL); } void ql_8021_clr_fw_intr(ql_adapter_state_t *ha) { WRT32_IO_REG(ha, nx_risc_int, 0); ql_8021_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xfbff); } void ql_8021_enable_intrs(ql_adapter_state_t *ha) { GLOBAL_HW_LOCK(); ql_8021_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); GLOBAL_HW_UNLOCK(); (void) ql_toggle_interrupt(ha, 1); } void ql_8021_disable_intrs(ql_adapter_state_t *ha) { (void) ql_toggle_interrupt(ha, 0); GLOBAL_HW_LOCK(); ql_8021_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0x0400); GLOBAL_HW_UNLOCK(); } void ql_8021_update_crb_int_ptr(ql_adapter_state_t *ha) { struct legacy_intr_set *nx_legacy_intr; ha->qdr_sn_window = (uint32_t)-1; ha->ddr_mn_window = (uint32_t)-1; nx_legacy_intr = &legacy_intr[ha->function_number]; ha->nx_legacy_intr.int_vec_bit = nx_legacy_intr->int_vec_bit; ha->nx_legacy_intr.tgt_status_reg = nx_legacy_intr->tgt_status_reg; ha->nx_legacy_intr.tgt_mask_reg = nx_legacy_intr->tgt_mask_reg; ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg; } void ql_8021_set_drv_active(ql_adapter_state_t *ha) { uint32_t val; if (ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT)) { return; } ql_8021_rd_32(ha, CRB_DRV_ACTIVE, &val); if (val == 0xffffffff) { val = (1 << (ha->function_number * 4)); } else { val |= (1 << (ha->function_number * 4)); } ql_8021_wr_32(ha, CRB_DRV_ACTIVE, val); ql_8021_hw_unlock(ha); } void ql_8021_clr_drv_active(ql_adapter_state_t *ha) { uint32_t val; if (ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT)) { return; } ql_8021_rd_32(ha, CRB_DRV_ACTIVE, &val); val &= ~(1 << (ha->function_number * 4)); ql_8021_wr_32(ha, CRB_DRV_ACTIVE, val); ql_8021_hw_unlock(ha); } static void ql_8021_need_reset_handler(ql_adapter_state_t *ha) { uint32_t drv_state, drv_active; clock_t timer; (void) ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT); ql_8021_rd_32(ha, CRB_DRV_STATE, &drv_state); drv_state |= (1 << (ha->function_number * 4)); ql_8021_wr_32(ha, CRB_DRV_STATE, drv_state); ql_8021_rd_32(ha, CRB_DRV_ACTIVE, &drv_active); ql_8021_hw_unlock(ha); for (timer = 30; timer && drv_state != drv_active; timer--) { delay(100); (void) ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT); ql_8021_rd_32(ha, CRB_DRV_STATE, &drv_state); ql_8021_rd_32(ha, CRB_DRV_ACTIVE, &drv_active); ql_8021_hw_unlock(ha); } } uint32_t ql_8021_idc_handler(ql_adapter_state_t *ha) { uint32_t dev_state, drv_state, rval; clock_t timer; ql_mbx_data_t mr; boolean_t stalled = B_FALSE, lock = B_FALSE; /* wait for 30 seconds for device to go ready */ timer = 30; while (timer) { if (lock == B_FALSE) { (void) ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT); lock = B_TRUE; } ql_8021_rd_32(ha, CRB_DEV_STATE, &dev_state); switch (dev_state) { case 0xffffffff: case NX_DEV_COLD: EL(ha, "dev_state=NX_DEV_COLD\n"); rval = NX_DEV_COLD; ql_8021_wr_32(ha, CRB_DEV_STATE, NX_DEV_INITIALIZING); ql_8021_wr_32(ha, CRB_DRV_IDC_VERSION, NX_IDC_VERSION); (void) ql_8021_hw_unlock(ha); if (ql_get_fw_version(ha, &mr, 2) == QL_SUCCESS && (mr.mb[1] | mr.mb[2] | mr.mb[3])) { ql_8021_rd_32(ha, UNM_FW_VERSION_MAJOR, &ha->fw_major_version); ql_8021_rd_32(ha, UNM_FW_VERSION_MINOR, &ha->fw_minor_version); ql_8021_rd_32(ha, UNM_FW_VERSION_SUB, &ha->fw_subminor_version); rval = NX_DEV_READY; } else if (ql_8021_load_risc(ha) == QL_SUCCESS) { rval = NX_DEV_READY; } (void) ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT); ql_8021_wr_32(ha, CRB_DEV_STATE, rval); break; case NX_DEV_READY: rval = NX_DEV_READY; timer = 0; break; case NX_DEV_FAILED: EL(ha, "dev_state=NX_DEV_FAILED\n"); rval = NX_DEV_FAILED; timer = 0; break; case NX_DEV_NEED_RESET: EL(ha, "dev_state=NX_DEV_NEED_RESET\n"); rval = NX_DEV_NEED_RESET; (void) ql_8021_hw_unlock(ha); lock = B_FALSE; if (ql_stall_driver(ha, 0) == QL_SUCCESS) { stalled = B_TRUE; ql_8021_need_reset_handler(ha); } break; case NX_DEV_NEED_QUIESCENT: EL(ha, "dev_state=NX_DEV_NEED_QUIESCENT\n"); (void) ql_8021_hw_unlock(ha); lock = B_FALSE; rval = ql_stall_driver(ha, 0); if (rval == QL_SUCCESS) { stalled = B_TRUE; (void) ql_8021_hw_lock(ha, IDC_LOCK_TIMEOUT); lock = B_TRUE; ql_8021_rd_32(ha, CRB_DRV_STATE, &drv_state); drv_state |= (2 << (ha->function_number * 4)); ql_8021_wr_32(ha, CRB_DRV_STATE, drv_state); } break; case NX_DEV_INITIALIZING: EL(ha, "dev_state=NX_DEV_INITIALIZING\n"); break; case NX_DEV_QUIESCENT: EL(ha, "dev_state=NX_DEV_QUIESCENT\n"); break; default: EL(ha, "dev_state=%x, default\n", dev_state); break; } if (lock == B_TRUE) { (void) ql_8021_hw_unlock(ha); lock = B_FALSE; } if (timer) { delay(100); timer--; } } if (stalled) { ql_restart_driver(ha); } return (rval); }