/* * 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 */ /* * This file is part of the Chelsio T1 Ethernet driver. * * Copyright (C) 2003-2005 Chelsio Communications. All rights reserved. */ #include "common.h" #include "regs.h" #include "mc4.h" struct pemc4 { adapter_t *adapter; unsigned int size; unsigned int nwords; /* MC4 width in terms of 32-bit words */ struct pemc4_intr_counts intr_cnt; }; void t1_mc4_destroy(struct pemc4 *mc4) { t1_os_free((void *)mc4, sizeof(*mc4)); } #define is_MC4A(adapter) (!t1_is_T1B(adapter)) /* Calculate amount of MC4 memory. */ static unsigned int __devinit mc4_calc_size(adapter_t *adapter) { u32 mc4_cfg = t1_read_reg_4(adapter, A_MC4_CFG); unsigned int width = is_MC4A(adapter) ? G_MC4A_WIDTH(mc4_cfg) : !!(mc4_cfg & F_MC4_NARROW); return (256 * 1024 * 1024) >> width; } /* * Write a value to a register and check that the write completed. These * writes normally complete in a cycle or two, so one read should suffice but * just in case we give them a bit of grace period. Note that the very first * read exists to flush the posted write to the device. */ static int wrreg_wait(adapter_t *adapter, unsigned int addr, u32 val) { int attempts = 2; t1_write_reg_4(adapter, addr, val); val = t1_read_reg_4(adapter, addr); /* flush */ while (attempts--) { if (!(t1_read_reg_4(adapter, addr) & F_BUSY)) return 0; if (attempts) DELAY_US(1); } CH_ERR("%s: write to MC4 register 0x%x timed out\n", adapter_name(adapter), addr); return -EIO; } #define MC4_DLL_DONE (F_MASTER_DLL_LOCKED | F_MASTER_DLL_MAX_TAP_COUNT) int t1_mc4_init(struct pemc4 *mc4, unsigned int mc4_clock) { int attempts; u32 val; unsigned int width, ext_mode, slow_mode; adapter_t *adapter = mc4->adapter; /* Power up the FCRAMs. */ val = t1_read_reg_4(adapter, A_MC4_CFG); t1_write_reg_4(adapter, A_MC4_CFG, val | F_POWER_UP); val = t1_read_reg_4(adapter, A_MC4_CFG); /* flush */ if (is_MC4A(adapter)) { slow_mode = val & F_MC4A_SLOW; width = G_MC4A_WIDTH(val); /* If we're not in slow mode, we are using the DLLs */ if (!slow_mode) { /* Clear Reset */ val = t1_read_reg_4(adapter, A_MC4_STROBE); t1_write_reg_4(adapter, A_MC4_STROBE, val & ~F_SLAVE_DLL_RESET); /* Wait for slave DLLs to lock */ DELAY_US(2 * 512 / (mc4_clock / 1000000) + 1); } } else { slow_mode = val & F_MC4_SLOW; width = !!(val & F_MC4_NARROW); /* Initializes the master DLL and slave delay lines. */ if (t1_is_asic(adapter) && !slow_mode) { val = t1_read_reg_4(adapter, A_MC4_STROBE); t1_write_reg_4(adapter, A_MC4_STROBE, val & ~F_MASTER_DLL_RESET); /* Wait for the master DLL to lock. */ attempts = 100; do { DELAY_US(1); val = t1_read_reg_4(adapter, A_MC4_STROBE); } while (!(val & MC4_DLL_DONE) && --attempts); if (!(val & MC4_DLL_DONE)) { CH_ERR("%s: MC4 DLL lock failed\n", adapter_name(adapter)); goto out_fail; } } } mc4->nwords = 4 >> width; /* Set the FCRAM output drive strength and enable DLLs if needed */ ext_mode = t1_is_asic(adapter) && !slow_mode ? 0 : 1; if (wrreg_wait(adapter, A_MC4_EXT_MODE, ext_mode)) goto out_fail; /* Specify the FCRAM operating parameters */ if (wrreg_wait(adapter, A_MC4_MODE, 0x32)) goto out_fail; /* Initiate an immediate refresh and wait for the write to complete. */ val = t1_read_reg_4(adapter, A_MC4_REFRESH); if (wrreg_wait(adapter, A_MC4_REFRESH, val & ~F_REFRESH_ENABLE)) goto out_fail; /* 2nd immediate refresh as before */ if (wrreg_wait(adapter, A_MC4_REFRESH, val & ~F_REFRESH_ENABLE)) goto out_fail; /* Convert to KHz first to avoid 64-bit division. */ mc4_clock /= 1000; /* Hz->KHz */ mc4_clock = mc4_clock * 7812 + mc4_clock / 2; /* ns */ mc4_clock /= 1000000; /* KHz->MHz, ns->us */ /* Enable periodic refresh. */ t1_write_reg_4(adapter, A_MC4_REFRESH, F_REFRESH_ENABLE | V_REFRESH_DIVISOR(mc4_clock)); (void) t1_read_reg_4(adapter, A_MC4_REFRESH); /* flush */ t1_write_reg_4(adapter, A_MC4_ECC_CNTL, F_ECC_GENERATION_ENABLE | F_ECC_CHECK_ENABLE); /* Use the BIST engine to clear all of the MC4 memory. */ t1_write_reg_4(adapter, A_MC4_BIST_ADDR_BEG, 0); t1_write_reg_4(adapter, A_MC4_BIST_ADDR_END, (mc4->size << width) - 1); t1_write_reg_4(adapter, A_MC4_BIST_DATA, 0); t1_write_reg_4(adapter, A_MC4_BIST_OP, V_OP(1) | 0x1f0); (void) t1_read_reg_4(adapter, A_MC4_BIST_OP); /* flush */ attempts = 100; do { DELAY_MS(100); val = t1_read_reg_4(adapter, A_MC4_BIST_OP); } while ((val & F_BUSY) && --attempts); if (val & F_BUSY) { CH_ERR("%s: MC4 BIST timed out\n", adapter_name(adapter)); goto out_fail; } /* Enable normal memory accesses. */ val = t1_read_reg_4(adapter, A_MC4_CFG); t1_write_reg_4(adapter, A_MC4_CFG, val | F_READY); val = t1_read_reg_4(adapter, A_MC4_CFG); /* flush */ return 0; out_fail: return -1; } struct pemc4 * __devinit t1_mc4_create(adapter_t *adapter) { struct pemc4 *mc4 = t1_os_malloc_wait_zero(sizeof(*mc4)); if (mc4) { mc4->adapter = adapter; mc4->size = mc4_calc_size(adapter); } return mc4; } unsigned int t1_mc4_get_size(struct pemc4 *mc4) { return mc4->size; } #define MC4_INT_MASK (F_MC4_CORR_ERR | F_MC4_UNCORR_ERR | F_MC4_ADDR_ERR) #define MC4_INT_FATAL (F_MC4_UNCORR_ERR | F_MC4_ADDR_ERR) void t1_mc4_intr_enable(struct pemc4 *mc4) { u32 pl_intr; if (t1_is_asic(mc4->adapter)) { t1_write_reg_4(mc4->adapter, A_MC4_INT_ENABLE, MC4_INT_MASK); pl_intr = t1_read_reg_4(mc4->adapter, A_PL_ENABLE); t1_write_reg_4(mc4->adapter, A_PL_ENABLE, pl_intr | F_PL_INTR_MC4); } } void t1_mc4_intr_disable(struct pemc4 *mc4) { u32 pl_intr; if (t1_is_asic(mc4->adapter)) { t1_write_reg_4(mc4->adapter, A_MC4_INT_ENABLE, 0); pl_intr = t1_read_reg_4(mc4->adapter, A_PL_ENABLE); t1_write_reg_4(mc4->adapter, A_PL_ENABLE, pl_intr & ~F_PL_INTR_MC4); } } void t1_mc4_intr_clear(struct pemc4 *mc4) { if (t1_is_asic(mc4->adapter)) { t1_write_reg_4(mc4->adapter, A_MC4_INT_CAUSE, 0xffffffff); t1_write_reg_4(mc4->adapter, A_PL_CAUSE, F_PL_INTR_MC4); } } int t1_mc4_intr_handler(struct pemc4 *mc4) { adapter_t *adapter = mc4->adapter; u32 cause = t1_read_reg_4(adapter, A_MC4_INT_CAUSE); if (cause & F_MC4_CORR_ERR) { mc4->intr_cnt.corr_err++; CH_WARN("%s: MC4 correctable error at addr 0x%x, " "data 0x%x 0x%x 0x%x 0x%x 0x%x\n", adapter_name(adapter), G_MC4_CE_ADDR(t1_read_reg_4(adapter, A_MC4_CE_ADDR)), t1_read_reg_4(adapter, A_MC4_CE_DATA0), t1_read_reg_4(adapter, A_MC4_CE_DATA1), t1_read_reg_4(adapter, A_MC4_CE_DATA2), t1_read_reg_4(adapter, A_MC4_CE_DATA3), t1_read_reg_4(adapter, A_MC4_CE_DATA4)); } if (cause & F_MC4_UNCORR_ERR) { mc4->intr_cnt.uncorr_err++; CH_ALERT("%s: MC4 uncorrectable error at addr 0x%x, " "data 0x%x 0x%x 0x%x 0x%x 0x%x\n", adapter_name(adapter), G_MC4_UE_ADDR(t1_read_reg_4(adapter, A_MC4_UE_ADDR)), t1_read_reg_4(adapter, A_MC4_UE_DATA0), t1_read_reg_4(adapter, A_MC4_UE_DATA1), t1_read_reg_4(adapter, A_MC4_UE_DATA2), t1_read_reg_4(adapter, A_MC4_UE_DATA3), t1_read_reg_4(adapter, A_MC4_UE_DATA4)); } if (cause & F_MC4_ADDR_ERR) { mc4->intr_cnt.addr_err++; CH_ALERT("%s: MC4 address error\n", adapter_name(adapter)); } if (cause & MC4_INT_FATAL) t1_fatal_err(adapter); t1_write_reg_4(mc4->adapter, A_MC4_INT_CAUSE, cause); return 0; } const struct pemc4_intr_counts *t1_mc4_get_intr_counts(struct pemc4 *mc4) { return &mc4->intr_cnt; } /* * Read n 256-bit words from MC4 starting at word start, using backdoor * accesses. */ int t1_mc4_bd_read(struct pemc4 *mc4, unsigned int start, unsigned int n, u32 *buf) { adapter_t *adap = mc4->adapter; unsigned int size256 = mc4->size / 32, c = 8 / mc4->nwords, i; if (start >= size256 || start + n > size256) return -EINVAL; for (i = 8, start *= 16 * c, n *= c; n; --n, start += 16) { int attempts = 10; u32 val; t1_write_reg_4(adap, A_MC4_BD_ADDR, start); t1_write_reg_4(adap, A_MC4_BD_OP, 0); val = t1_read_reg_4(adap, A_MC4_BD_OP); while ((val & F_BUSY) && attempts--) val = t1_read_reg_4(adap, A_MC4_BD_OP); if (val & F_BUSY) return -EIO; buf[--i] = t1_read_reg_4(adap, A_MC4_BD_DATA3); if (mc4->nwords >= 2) buf[--i] = t1_read_reg_4(adap, A_MC4_BD_DATA2); if (mc4->nwords == 4) { buf[--i] = t1_read_reg_4(adap, A_MC4_BD_DATA1); buf[--i] = t1_read_reg_4(adap, A_MC4_BD_DATA0); } if (i == 0) { i = 8; buf += 8; } } return 0; }