#include "common.h"
#include "regs.h"
#include "mc4.h"
struct pemc4 {
adapter_t *adapter;
unsigned int size;
unsigned int nwords;
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))
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;
}
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);
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;
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);
if (is_MC4A(adapter)) {
slow_mode = val & F_MC4A_SLOW;
width = G_MC4A_WIDTH(val);
if (!slow_mode) {
val = t1_read_reg_4(adapter, A_MC4_STROBE);
t1_write_reg_4(adapter, A_MC4_STROBE,
val & ~F_SLAVE_DLL_RESET);
DELAY_US(2 * 512 / (mc4_clock / 1000000) + 1);
}
} else {
slow_mode = val & F_MC4_SLOW;
width = !!(val & F_MC4_NARROW);
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);
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;
ext_mode = t1_is_asic(adapter) && !slow_mode ? 0 : 1;
if (wrreg_wait(adapter, A_MC4_EXT_MODE, ext_mode))
goto out_fail;
if (wrreg_wait(adapter, A_MC4_MODE, 0x32))
goto out_fail;
val = t1_read_reg_4(adapter, A_MC4_REFRESH);
if (wrreg_wait(adapter, A_MC4_REFRESH, val & ~F_REFRESH_ENABLE))
goto out_fail;
if (wrreg_wait(adapter, A_MC4_REFRESH, val & ~F_REFRESH_ENABLE))
goto out_fail;
mc4_clock /= 1000;
mc4_clock = mc4_clock * 7812 + mc4_clock / 2;
mc4_clock /= 1000000;
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);
t1_write_reg_4(adapter, A_MC4_ECC_CNTL,
F_ECC_GENERATION_ENABLE | F_ECC_CHECK_ENABLE);
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);
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;
}
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);
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;
}
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;
}