xref: /illumos-gate/usr/src/uts/common/io/chxge/com/mc3.c (revision 2d6eb4a5)
1d39a76e7Sxw /*
2d39a76e7Sxw  * CDDL HEADER START
3d39a76e7Sxw  *
4d39a76e7Sxw  * The contents of this file are subject to the terms of the
5d39a76e7Sxw  * Common Development and Distribution License (the "License").
6d39a76e7Sxw  * You may not use this file except in compliance with the License.
7d39a76e7Sxw  *
8d39a76e7Sxw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d39a76e7Sxw  * or http://www.opensolaris.org/os/licensing.
10d39a76e7Sxw  * See the License for the specific language governing permissions
11d39a76e7Sxw  * and limitations under the License.
12d39a76e7Sxw  *
13d39a76e7Sxw  * When distributing Covered Code, include this CDDL HEADER in each
14d39a76e7Sxw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d39a76e7Sxw  * If applicable, add the following below this CDDL HEADER, with the
16d39a76e7Sxw  * fields enclosed by brackets "[]" replaced with your own identifying
17d39a76e7Sxw  * information: Portions Copyright [yyyy] [name of copyright owner]
18d39a76e7Sxw  *
19d39a76e7Sxw  * CDDL HEADER END
20d39a76e7Sxw  */
21d39a76e7Sxw 
22d39a76e7Sxw /*
23d39a76e7Sxw  * Copyright (C) 2003-2005 Chelsio Communications.  All rights reserved.
24d39a76e7Sxw  */
25d39a76e7Sxw 
26d39a76e7Sxw #include "common.h"
27d39a76e7Sxw #include "regs.h"
28d39a76e7Sxw #include "mc3.h"
29d39a76e7Sxw 
30d39a76e7Sxw #ifdef CONFIG_CHELSIO_T1_1G
31d39a76e7Sxw # include "fpga_defs.h"
32d39a76e7Sxw #endif
33d39a76e7Sxw 
34d39a76e7Sxw struct pemc3 {
35d39a76e7Sxw 	adapter_t *adapter;
36d39a76e7Sxw 	unsigned int size;
37d39a76e7Sxw 	struct pemc3_intr_counts intr_cnt;
38d39a76e7Sxw };
39d39a76e7Sxw 
40d39a76e7Sxw #define MC3_INTR_MASK (F_MC3_CORR_ERR | F_MC3_UNCORR_ERR | \
41d39a76e7Sxw 		       V_MC3_PARITY_ERR(M_MC3_PARITY_ERR) | F_MC3_ADDR_ERR)
42d39a76e7Sxw #define MC3_INTR_FATAL (F_MC3_UNCORR_ERR | V_MC3_PARITY_ERR(M_MC3_PARITY_ERR) | F_MC3_ADDR_ERR)
43d39a76e7Sxw 
t1_mc3_intr_enable(struct pemc3 * mc3)44d39a76e7Sxw void t1_mc3_intr_enable(struct pemc3 *mc3)
45d39a76e7Sxw {
46d39a76e7Sxw 	u32 en = t1_read_reg_4(mc3->adapter, A_PL_ENABLE);
47d39a76e7Sxw 
48d39a76e7Sxw 	if (t1_is_asic(mc3->adapter)) {
49d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_MC3_INT_ENABLE, MC3_INTR_MASK);
50d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_PL_ENABLE, en | F_PL_INTR_MC3);
51d39a76e7Sxw #ifdef CONFIG_CHELSIO_T1_1G
52d39a76e7Sxw 	} else {
53d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, FPGA_MC3_REG_INTRENABLE,
54d39a76e7Sxw 			       MC3_INTR_MASK);
55d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_PL_ENABLE,
56d39a76e7Sxw 			       en | FPGA_PCIX_INTERRUPT_MC3);
57d39a76e7Sxw #endif
58d39a76e7Sxw 	}
59d39a76e7Sxw }
60d39a76e7Sxw 
t1_mc3_intr_disable(struct pemc3 * mc3)61d39a76e7Sxw void t1_mc3_intr_disable(struct pemc3 *mc3)
62d39a76e7Sxw {
63d39a76e7Sxw 	u32 pl_intr = t1_read_reg_4(mc3->adapter, A_PL_ENABLE);
64d39a76e7Sxw 
65d39a76e7Sxw 	if (t1_is_asic(mc3->adapter)) {
66d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_MC3_INT_ENABLE, 0);
67d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_PL_ENABLE,
68d39a76e7Sxw 			       pl_intr & ~F_PL_INTR_MC3);
69d39a76e7Sxw #ifdef CONFIG_CHELSIO_T1_1G
70d39a76e7Sxw 	} else {
71d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, FPGA_MC3_REG_INTRENABLE, 0);
72d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_PL_ENABLE,
73d39a76e7Sxw 			       pl_intr & ~FPGA_PCIX_INTERRUPT_MC3);
74d39a76e7Sxw #endif
75d39a76e7Sxw 	}
76d39a76e7Sxw }
77d39a76e7Sxw 
t1_mc3_intr_clear(struct pemc3 * mc3)78d39a76e7Sxw void t1_mc3_intr_clear(struct pemc3 *mc3)
79d39a76e7Sxw {
80d39a76e7Sxw 	if (t1_is_asic(mc3->adapter)) {
81d39a76e7Sxw 		if (t1_is_T1B(mc3->adapter)) {
82d39a76e7Sxw 			/*
83d39a76e7Sxw 			 * Workaround for T1B bug: we must write to enable
84d39a76e7Sxw 			 * register to clear interrupts.
85d39a76e7Sxw 			 */
86d39a76e7Sxw 			u32 old_en;
87d39a76e7Sxw 
88d39a76e7Sxw 			old_en = t1_read_reg_4(mc3->adapter, A_MC3_INT_ENABLE);
89d39a76e7Sxw 			t1_write_reg_4(mc3->adapter, A_MC3_INT_ENABLE,
90d39a76e7Sxw 				       0xffffffff);
91d39a76e7Sxw 			t1_write_reg_4(mc3->adapter, A_MC3_INT_ENABLE, old_en);
92d39a76e7Sxw 		} else
93d39a76e7Sxw 			t1_write_reg_4(mc3->adapter, A_MC3_INT_CAUSE,
94d39a76e7Sxw 				       0xffffffff);
95d39a76e7Sxw 
96d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_PL_CAUSE, F_PL_INTR_MC3);
97d39a76e7Sxw #ifdef CONFIG_CHELSIO_T1_1G
98d39a76e7Sxw 	} else {
99d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, FPGA_MC3_REG_INTRCAUSE,
100d39a76e7Sxw 			       0xffffffff);
101d39a76e7Sxw 		t1_write_reg_4(mc3->adapter, A_PL_CAUSE,
102d39a76e7Sxw 			       FPGA_PCIX_INTERRUPT_MC3);
103d39a76e7Sxw #endif
104d39a76e7Sxw 	}
105d39a76e7Sxw }
106d39a76e7Sxw 
t1_mc3_intr_handler(struct pemc3 * mc3)107d39a76e7Sxw int t1_mc3_intr_handler(struct pemc3 *mc3)
108d39a76e7Sxw {
109d39a76e7Sxw 	adapter_t *adapter = mc3->adapter;
110d39a76e7Sxw 	int cause_reg = A_MC3_INT_CAUSE;
111d39a76e7Sxw 	u32 cause;
112d39a76e7Sxw 
113d39a76e7Sxw #ifdef CONFIG_CHELSIO_T1_1G
114d39a76e7Sxw 	if (!t1_is_asic(adapter))
115d39a76e7Sxw 		cause_reg = FPGA_MC3_REG_INTRCAUSE;
116d39a76e7Sxw #endif
117d39a76e7Sxw 	cause = t1_read_reg_4(adapter, cause_reg);
118d39a76e7Sxw 
119d39a76e7Sxw 	if (cause & F_MC3_CORR_ERR) {
120d39a76e7Sxw 		mc3->intr_cnt.corr_err++;
121d39a76e7Sxw 		CH_WARN("%s: MC3 correctable error at addr 0x%x, "
122d39a76e7Sxw 			"data 0x%x 0x%x 0x%x 0x%x 0x%x\n",
123d39a76e7Sxw 			adapter_name(adapter),
124d39a76e7Sxw 			G_MC3_CE_ADDR(t1_read_reg_4(adapter, A_MC3_CE_ADDR)),
125d39a76e7Sxw 			t1_read_reg_4(adapter, A_MC3_CE_DATA0),
126d39a76e7Sxw 			t1_read_reg_4(adapter, A_MC3_CE_DATA1),
127d39a76e7Sxw 			t1_read_reg_4(adapter, A_MC3_CE_DATA2),
128d39a76e7Sxw 			t1_read_reg_4(adapter, A_MC3_CE_DATA3),
129d39a76e7Sxw 			t1_read_reg_4(adapter, A_MC3_CE_DATA4));
130d39a76e7Sxw 	}
131d39a76e7Sxw 
132d39a76e7Sxw 	if (cause & F_MC3_UNCORR_ERR) {
133d39a76e7Sxw 		mc3->intr_cnt.uncorr_err++;
134d39a76e7Sxw 		CH_ALERT("%s: MC3 uncorrectable error at addr 0x%x, "
135d39a76e7Sxw 			 "data 0x%x 0x%x 0x%x 0x%x 0x%x\n",
136d39a76e7Sxw 			 adapter_name(adapter),
137d39a76e7Sxw 			 G_MC3_UE_ADDR(t1_read_reg_4(adapter, A_MC3_UE_ADDR)),
138d39a76e7Sxw 			 t1_read_reg_4(adapter, A_MC3_UE_DATA0),
139d39a76e7Sxw 			 t1_read_reg_4(adapter, A_MC3_UE_DATA1),
140d39a76e7Sxw 			 t1_read_reg_4(adapter, A_MC3_UE_DATA2),
141d39a76e7Sxw 			 t1_read_reg_4(adapter, A_MC3_UE_DATA3),
142d39a76e7Sxw 			 t1_read_reg_4(adapter, A_MC3_UE_DATA4));
143d39a76e7Sxw 	}
144d39a76e7Sxw 
145d39a76e7Sxw 	if (G_MC3_PARITY_ERR(cause)) {
146d39a76e7Sxw 		mc3->intr_cnt.parity_err++;
147d39a76e7Sxw 		CH_ALERT("%s: MC3 parity error 0x%x\n", adapter_name(adapter),
148d39a76e7Sxw 			 G_MC3_PARITY_ERR(cause));
149d39a76e7Sxw 	}
150d39a76e7Sxw 
151d39a76e7Sxw 	if (cause & F_MC3_ADDR_ERR) {
152d39a76e7Sxw 		mc3->intr_cnt.addr_err++;
153d39a76e7Sxw 		CH_ALERT("%s: MC3 address error\n", adapter_name(adapter));
154d39a76e7Sxw 	}
155d39a76e7Sxw 
156d39a76e7Sxw 	if (cause & MC3_INTR_FATAL)
157d39a76e7Sxw 		t1_fatal_err(adapter);
158d39a76e7Sxw 
159d39a76e7Sxw 	if (t1_is_T1B(adapter)) {
160d39a76e7Sxw 		/*
161d39a76e7Sxw 		 * Workaround for T1B bug: we must write to enable register to
162d39a76e7Sxw 		 * clear interrupts.
163d39a76e7Sxw 		 */
164d39a76e7Sxw 		t1_write_reg_4(adapter, A_MC3_INT_ENABLE, cause);
165d39a76e7Sxw 		/* restore enable */
166d39a76e7Sxw 		t1_write_reg_4(adapter, A_MC3_INT_ENABLE, MC3_INTR_MASK);
167d39a76e7Sxw 	} else
168d39a76e7Sxw 		t1_write_reg_4(adapter, cause_reg, cause);
169d39a76e7Sxw 
170d39a76e7Sxw 	return 0;
171d39a76e7Sxw }
172d39a76e7Sxw 
173d39a76e7Sxw #define is_MC3A(adapter) (!t1_is_T1B(adapter))
174d39a76e7Sxw 
175d39a76e7Sxw /*
176d39a76e7Sxw  * Write a value to a register and check that the write completed.  These
177d39a76e7Sxw  * writes normally complete in a cycle or two, so one read should suffice.
178d39a76e7Sxw  * The very first read exists to flush the posted write to the device.
179d39a76e7Sxw  */
wrreg_wait(adapter_t * adapter,unsigned int addr,u32 val)180d39a76e7Sxw static int wrreg_wait(adapter_t *adapter, unsigned int addr, u32 val)
181d39a76e7Sxw {
182d39a76e7Sxw 	t1_write_reg_4(adapter,	addr, val);
183d39a76e7Sxw 	val = t1_read_reg_4(adapter, addr);                   /* flush */
184d39a76e7Sxw 	if (!(t1_read_reg_4(adapter, addr) & F_BUSY))
185d39a76e7Sxw 		return 0;
186d39a76e7Sxw 	CH_ERR("%s: write to MC3 register 0x%x timed out\n",
187d39a76e7Sxw 	       adapter_name(adapter), addr);
188d39a76e7Sxw 	return -EIO;
189d39a76e7Sxw }
190d39a76e7Sxw 
191d39a76e7Sxw #define MC3_DLL_DONE (F_MASTER_DLL_LOCKED | F_MASTER_DLL_MAX_TAP_COUNT)
192d39a76e7Sxw 
t1_mc3_init(struct pemc3 * mc3,unsigned int mc3_clock)193d39a76e7Sxw int t1_mc3_init(struct pemc3 *mc3, unsigned int mc3_clock)
194d39a76e7Sxw {
195d39a76e7Sxw 	u32 val;
196d39a76e7Sxw 	unsigned int width, fast_asic, attempts;
197d39a76e7Sxw 	adapter_t *adapter = mc3->adapter;
198d39a76e7Sxw 
199d39a76e7Sxw 	/* Check to see if ASIC is running in slow mode. */
200d39a76e7Sxw 	val = t1_read_reg_4(adapter, A_MC3_CFG);
201d39a76e7Sxw 	width = is_MC3A(adapter) ? G_MC3_WIDTH(val) : 0;
202d39a76e7Sxw 	fast_asic = t1_is_asic(adapter) && !(val & F_MC3_SLOW);
203d39a76e7Sxw 
204d39a76e7Sxw 	val &= ~(V_MC3_BANK_CYCLE(M_MC3_BANK_CYCLE) |
205d39a76e7Sxw 		 V_REFRESH_CYCLE(M_REFRESH_CYCLE) |
206d39a76e7Sxw 		 V_PRECHARGE_CYCLE(M_PRECHARGE_CYCLE) |
207d39a76e7Sxw 		 F_ACTIVE_TO_READ_WRITE_DELAY |
208d39a76e7Sxw 		 V_ACTIVE_TO_PRECHARGE_DELAY(M_ACTIVE_TO_PRECHARGE_DELAY) |
209d39a76e7Sxw 		 V_WRITE_RECOVERY_DELAY(M_WRITE_RECOVERY_DELAY));
210d39a76e7Sxw 
211d39a76e7Sxw 	if (mc3_clock <= 100000000)
212d39a76e7Sxw 		val |= V_MC3_BANK_CYCLE(7) | V_REFRESH_CYCLE(4) |
213d39a76e7Sxw 			V_PRECHARGE_CYCLE(2) | V_ACTIVE_TO_PRECHARGE_DELAY(5) |
214d39a76e7Sxw 			V_WRITE_RECOVERY_DELAY(2);
215d39a76e7Sxw 	else if (mc3_clock <= 133000000)
216d39a76e7Sxw 		val |= V_MC3_BANK_CYCLE(9) | V_REFRESH_CYCLE(5) |
217d39a76e7Sxw 			V_PRECHARGE_CYCLE(3) | F_ACTIVE_TO_READ_WRITE_DELAY |
218d39a76e7Sxw 			V_ACTIVE_TO_PRECHARGE_DELAY(6) |
219d39a76e7Sxw 			V_WRITE_RECOVERY_DELAY(2);
220d39a76e7Sxw 	else
221d39a76e7Sxw 		val |= V_MC3_BANK_CYCLE(0xA) | V_REFRESH_CYCLE(6) |
222d39a76e7Sxw 			V_PRECHARGE_CYCLE(3) | F_ACTIVE_TO_READ_WRITE_DELAY |
223d39a76e7Sxw 			V_ACTIVE_TO_PRECHARGE_DELAY(7) |
224d39a76e7Sxw 			V_WRITE_RECOVERY_DELAY(3);
225d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_CFG, val);
226d39a76e7Sxw 
227d39a76e7Sxw 	val = t1_read_reg_4(adapter, A_MC3_CFG);
228d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_CFG, val | F_CLK_ENABLE);
229d39a76e7Sxw 	val = t1_read_reg_4(adapter, A_MC3_CFG);                 /* flush */
230d39a76e7Sxw 
231d39a76e7Sxw 	if (fast_asic) {                                     /* setup DLLs */
232d39a76e7Sxw 		val = t1_read_reg_4(adapter, A_MC3_STROBE);
233d39a76e7Sxw 		if (is_MC3A(adapter)) {
234d39a76e7Sxw 			t1_write_reg_4(adapter, A_MC3_STROBE,
235d39a76e7Sxw 				       val & ~F_SLAVE_DLL_RESET);
236d39a76e7Sxw 
237d39a76e7Sxw 			/* Wait for slave DLLs to lock */
238d39a76e7Sxw 			DELAY_US(2 * 512 / (mc3_clock / 1000000) + 1);
239d39a76e7Sxw 		} else {
240d39a76e7Sxw 			/* Initialize the master DLL and slave delay lines. */
241d39a76e7Sxw 			t1_write_reg_4(adapter, A_MC3_STROBE,
242d39a76e7Sxw 				       val & ~F_MASTER_DLL_RESET);
243d39a76e7Sxw 
244d39a76e7Sxw 			/* Wait for the master DLL to lock. */
245d39a76e7Sxw 			attempts = 100;
246d39a76e7Sxw 			do {
247d39a76e7Sxw 				DELAY_US(1);
248d39a76e7Sxw 				val = t1_read_reg_4(adapter, A_MC3_STROBE);
249d39a76e7Sxw 			} while (!(val & MC3_DLL_DONE) && --attempts);
250d39a76e7Sxw 			if (!(val & MC3_DLL_DONE)) {
251d39a76e7Sxw 				CH_ERR("%s: MC3 DLL lock failed\n",
252d39a76e7Sxw 				       adapter_name(adapter));
253d39a76e7Sxw 				goto out_fail;
254d39a76e7Sxw 			}
255d39a76e7Sxw 		}
256d39a76e7Sxw 	}
257d39a76e7Sxw 
258d39a76e7Sxw 	/* Initiate a precharge and wait for the precharge to complete. */
259d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_PRECHARG, 0))
260d39a76e7Sxw 		goto out_fail;
261d39a76e7Sxw 
262d39a76e7Sxw 	/* Set the SDRAM output drive strength and enable DLLs if needed */
263d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_EXT_MODE, fast_asic ? 0 : 1))
264d39a76e7Sxw 		goto out_fail;
265d39a76e7Sxw 
266d39a76e7Sxw 	/* Specify the SDRAM operating parameters. */
267d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_MODE, fast_asic ? 0x161 : 0x21))
268d39a76e7Sxw 		goto out_fail;
269d39a76e7Sxw 
270d39a76e7Sxw 	/* Initiate a precharge and wait for the precharge to complete. */
271d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_PRECHARG, 0))
272d39a76e7Sxw 		goto out_fail;
273d39a76e7Sxw 
274d39a76e7Sxw 	/* Initiate an immediate refresh and wait for the write to complete. */
275d39a76e7Sxw 	val = t1_read_reg_4(adapter, A_MC3_REFRESH);
276d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_REFRESH, val & ~F_REFRESH_ENABLE))
277d39a76e7Sxw 		goto out_fail;
278d39a76e7Sxw 
279d39a76e7Sxw 	/* 2nd immediate refresh as before */
280d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_REFRESH, val & ~F_REFRESH_ENABLE))
281d39a76e7Sxw 		goto out_fail;
282d39a76e7Sxw 
283d39a76e7Sxw 	/* Specify the SDRAM operating parameters. */
284d39a76e7Sxw 	if (wrreg_wait(adapter, A_MC3_MODE, fast_asic ? 0x61 : 0x21))
285d39a76e7Sxw 		goto out_fail;
286d39a76e7Sxw 
287d39a76e7Sxw 	/* Convert to KHz first to avoid 64-bit division. */
288d39a76e7Sxw 	mc3_clock /=  1000;                            /* Hz->KHz */
289d39a76e7Sxw 	mc3_clock = mc3_clock * 7812 + mc3_clock / 2;  /* ns */
290d39a76e7Sxw 	mc3_clock /= 1000000;                          /* KHz->MHz, ns->us */
291d39a76e7Sxw 
292d39a76e7Sxw 	/* Enable periodic refresh. */
293d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_REFRESH,
294d39a76e7Sxw 		       F_REFRESH_ENABLE | V_REFRESH_DIVISOR(mc3_clock));
295d39a76e7Sxw 	(void) t1_read_reg_4(adapter, A_MC3_REFRESH);    /* flush */
296d39a76e7Sxw 
297d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_ECC_CNTL,
298d39a76e7Sxw 		       F_ECC_GENERATION_ENABLE | F_ECC_CHECK_ENABLE);
299d39a76e7Sxw 
300d39a76e7Sxw 	/* Use the BIST engine to clear MC3 memory and initialize ECC. */
301d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_BIST_ADDR_BEG, 0);
302d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_BIST_ADDR_END, (mc3->size << width) - 1);
303d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_BIST_DATA, 0);
304d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_BIST_OP, V_OP(1) | 0x1f0);
305d39a76e7Sxw 	(void) t1_read_reg_4(adapter, A_MC3_BIST_OP);              /* flush */
306d39a76e7Sxw 
307d39a76e7Sxw 	attempts = 100;
308d39a76e7Sxw 	do {
309d39a76e7Sxw 		DELAY_MS(100);
310d39a76e7Sxw 		val = t1_read_reg_4(adapter, A_MC3_BIST_OP);
311d39a76e7Sxw 	} while ((val & F_BUSY) && --attempts);
312d39a76e7Sxw 	if (val & F_BUSY) {
313d39a76e7Sxw 		CH_ERR("%s: MC3 BIST timed out\n", adapter_name(adapter));
314d39a76e7Sxw 		goto out_fail;
315d39a76e7Sxw 	}
316d39a76e7Sxw 
317d39a76e7Sxw 	/* Enable normal memory accesses. */
318d39a76e7Sxw 	val = t1_read_reg_4(adapter, A_MC3_CFG);
319d39a76e7Sxw 	t1_write_reg_4(adapter, A_MC3_CFG, val | F_READY);
320d39a76e7Sxw 	return 0;
321d39a76e7Sxw 
322d39a76e7Sxw  out_fail:
323d39a76e7Sxw 	return -1;
324d39a76e7Sxw }
325*2d6eb4a5SToomas Soome 
mc3_calc_size(const adapter_t * adapter,u32 cfg)326d39a76e7Sxw static unsigned int __devinit mc3_calc_size(const adapter_t *adapter, u32 cfg)
327d39a76e7Sxw {
328d39a76e7Sxw 	unsigned int banks = !!(cfg & F_BANKS) + 1;
329d39a76e7Sxw 	unsigned int org = !!(cfg & F_ORGANIZATION) + 1;
330d39a76e7Sxw 	unsigned int density = G_DENSITY(cfg);
331d39a76e7Sxw 
332d39a76e7Sxw 	unsigned int capacity_in_MB = is_MC3A(adapter) ?
333d39a76e7Sxw 		((256 << density) * banks) / (org << G_MC3_WIDTH(cfg)) :
334d39a76e7Sxw 		((128 << density) * (16 / org) * banks) / 8;
335d39a76e7Sxw 
336d39a76e7Sxw 	return capacity_in_MB * 1024 * 1024;
337d39a76e7Sxw }
338d39a76e7Sxw 
t1_mc3_create(adapter_t * adapter)339d39a76e7Sxw struct pemc3 * __devinit t1_mc3_create(adapter_t *adapter)
340d39a76e7Sxw {
341d39a76e7Sxw 	struct pemc3 *mc3 = t1_os_malloc_wait_zero(sizeof(*mc3));
342d39a76e7Sxw 
343d39a76e7Sxw 	if (mc3) {
344d39a76e7Sxw 		mc3->adapter = adapter;
345d39a76e7Sxw 		mc3->size = mc3_calc_size(adapter,
346d39a76e7Sxw 					  t1_read_reg_4(adapter, A_MC3_CFG));
347d39a76e7Sxw 	}
348d39a76e7Sxw 	return mc3;
349d39a76e7Sxw }
350d39a76e7Sxw 
t1_mc3_destroy(struct pemc3 * mc3)351d39a76e7Sxw void t1_mc3_destroy(struct pemc3 *mc3)
352d39a76e7Sxw {
353d39a76e7Sxw 	t1_os_free((void *)mc3, sizeof(*mc3));
354d39a76e7Sxw }
355d39a76e7Sxw 
t1_mc3_get_size(struct pemc3 * mc3)356d39a76e7Sxw unsigned int t1_mc3_get_size(struct pemc3 *mc3)
357d39a76e7Sxw {
358d39a76e7Sxw 	return mc3->size;
359d39a76e7Sxw }
360d39a76e7Sxw 
t1_mc3_get_intr_counts(struct pemc3 * mc3)361d39a76e7Sxw const struct pemc3_intr_counts *t1_mc3_get_intr_counts(struct pemc3 *mc3)
362d39a76e7Sxw {
363d39a76e7Sxw 	return &mc3->intr_cnt;
364d39a76e7Sxw }
365