This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / net / chelsio / espi.c
1 /*****************************************************************************
2  *                                                                           *
3  * File: espi.c                                                              *
4  * $Revision: 1.14 $                                                         *
5  * $Date: 2005/05/14 00:59:32 $                                              *
6  * Description:                                                              *
7  *  Ethernet SPI functionality.                                              *
8  *  part of the Chelsio 10Gb Ethernet Driver.                                *
9  *                                                                           *
10  * This program is free software; you can redistribute it and/or modify      *
11  * it under the terms of the GNU General Public License, version 2, as       *
12  * published by the Free Software Foundation.                                *
13  *                                                                           *
14  * You should have received a copy of the GNU General Public License along   *
15  * with this program; if not, write to the Free Software Foundation, Inc.,   *
16  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.                 *
17  *                                                                           *
18  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
19  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
20  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
21  *                                                                           *
22  * http://www.chelsio.com                                                    *
23  *                                                                           *
24  * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
25  * All rights reserved.                                                      *
26  *                                                                           *
27  * Maintainers: maintainers@chelsio.com                                      *
28  *                                                                           *
29  * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
30  *          Tina Yang               <tainay@chelsio.com>                     *
31  *          Felix Marti             <felix@chelsio.com>                      *
32  *          Scott Bardone           <sbardone@chelsio.com>                   *
33  *          Kurt Ottaway            <kottaway@chelsio.com>                   *
34  *          Frank DiMambro          <frank@chelsio.com>                      *
35  *                                                                           *
36  * History:                                                                  *
37  *                                                                           *
38  ****************************************************************************/
39
40 #include "common.h"
41 #include "regs.h"
42 #include "espi.h"
43
44 struct peespi {
45         adapter_t *adapter;
46         struct espi_intr_counts intr_cnt;
47         u32 misc_ctrl;
48         spinlock_t lock;
49 };
50
51 #define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \
52                         F_RAMPARITYERR | F_DIP2PARITYERR)
53 #define MON_MASK  (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \
54                    | F_MONITORED_INTERFACE)
55
56 #define TRICN_CNFG 14
57 #define TRICN_CMD_READ  0x11
58 #define TRICN_CMD_WRITE 0x21
59 #define TRICN_CMD_ATTEMPTS 10
60
61 static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr,
62                        int ch_addr, int reg_offset, u32 wr_data)
63 {
64         int busy, attempts = TRICN_CMD_ATTEMPTS;
65
66         writel(V_WRITE_DATA(wr_data) |
67                V_REGISTER_OFFSET(reg_offset) |
68                V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
69                V_BUNDLE_ADDR(bundle_addr) |
70                V_SPI4_COMMAND(TRICN_CMD_WRITE),
71                adapter->regs + A_ESPI_CMD_ADDR);
72         writel(0, adapter->regs + A_ESPI_GOSTAT);
73
74         do {
75                 busy = readl(adapter->regs + A_ESPI_GOSTAT) & F_ESPI_CMD_BUSY;
76         } while (busy && --attempts);
77
78         if (busy)
79                 CH_ERR("%s: TRICN write timed out\n", adapter->name);
80
81         return busy;
82 }
83
84 /* 1. Deassert rx_reset_core. */
85 /* 2. Program TRICN_CNFG registers. */
86 /* 3. Deassert rx_reset_link */
87 static int tricn_init(adapter_t *adapter)
88 {
89         int     i               = 0;
90         int     stat            = 0;
91         int     timeout         = 0;
92         int     is_ready        = 0;
93
94         /* 1 */
95         timeout=1000;
96         do {
97                 stat = readl(adapter->regs + A_ESPI_RX_RESET);
98                 is_ready = (stat & 0x4);
99                 timeout--;
100                 udelay(5);
101         } while (!is_ready || (timeout==0));
102         writel(0x2, adapter->regs + A_ESPI_RX_RESET);
103         if (timeout==0)
104         {
105                 CH_ERR("ESPI : ERROR : Timeout tricn_init() \n");
106                 t1_fatal_err(adapter);
107         }
108
109         /* 2 */
110         tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
111         tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
112         tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
113         for (i=1; i<= 8; i++) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
114         for (i=1; i<= 2; i++) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
115         for (i=1; i<= 3; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
116         for (i=4; i<= 4; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
117         for (i=5; i<= 5; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
118         for (i=6; i<= 6; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
119         for (i=7; i<= 7; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0x80);
120         for (i=8; i<= 8; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
121
122         /* 3 */
123         writel(0x3, adapter->regs + A_ESPI_RX_RESET);
124
125         return 0;
126 }
127
128 void t1_espi_intr_enable(struct peespi *espi)
129 {
130         u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
131
132         /*
133          * Cannot enable ESPI interrupts on T1B because HW asserts the
134          * interrupt incorrectly, namely the driver gets ESPI interrupts
135          * but no data is actually dropped (can verify this reading the ESPI
136          * drop registers).  Also, once the ESPI interrupt is asserted it
137          * cannot be cleared (HW bug).
138          */
139         enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
140         writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE);
141         writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
142 }
143
144 void t1_espi_intr_clear(struct peespi *espi)
145 {
146         writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS);
147         writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE);
148 }
149
150 void t1_espi_intr_disable(struct peespi *espi)
151 {
152         u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
153
154         writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE);
155         writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
156 }
157
158 int t1_espi_intr_handler(struct peespi *espi)
159 {
160         u32 cnt;
161         u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
162
163         if (status & F_DIP4ERR)
164                 espi->intr_cnt.DIP4_err++;
165         if (status & F_RXDROP)
166                 espi->intr_cnt.rx_drops++;
167         if (status & F_TXDROP)
168                 espi->intr_cnt.tx_drops++;
169         if (status & F_RXOVERFLOW)
170                 espi->intr_cnt.rx_ovflw++;
171         if (status & F_RAMPARITYERR)
172                 espi->intr_cnt.parity_err++;
173         if (status & F_DIP2PARITYERR) {
174                 espi->intr_cnt.DIP2_parity_err++;
175
176                 /*
177                  * Must read the error count to clear the interrupt
178                  * that it causes.
179                  */
180                 cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
181         }
182
183         /*
184          * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
185          * write the status as is.
186          */
187         if (status && t1_is_T1B(espi->adapter))
188                 status = 1;
189         writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
190         return 0;
191 }
192
193 const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi)
194 {
195     return &espi->intr_cnt;
196 }
197
198 static void espi_setup_for_pm3393(adapter_t *adapter)
199 {
200         u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
201
202         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0);
203         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1);
204         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2);
205         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3);
206         writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
207         writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
208         writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH);
209         writel(0x08000008, adapter->regs + A_ESPI_TRAIN);
210         writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG);
211 }
212
213 /* T2 Init part --  */
214 /* 1. Set T_ESPI_MISCCTRL_ADDR */
215 /* 2. Init ESPI registers. */
216 /* 3. Init TriCN Hard Macro */
217 int t1_espi_init(struct peespi *espi, int mac_type, int nports)
218 {
219         u32 cnt;
220
221         u32 status_enable_extra = 0;
222         adapter_t *adapter = espi->adapter;
223         u32 status, burstval = 0x800100;
224
225         /* Disable ESPI training.  MACs that can handle it enable it below. */
226         writel(0, adapter->regs + A_ESPI_TRAIN);
227
228         if (is_T2(adapter)) {
229                 writel(V_OUT_OF_SYNC_COUNT(4) |
230                        V_DIP2_PARITY_ERR_THRES(3) |
231                        V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL);
232                 if (nports == 4) {
233                         /* T204: maxburst1 = 0x40, maxburst2 = 0x20 */
234                         burstval = 0x200040;
235                 }
236         }
237         writel(burstval, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2);
238
239         switch (mac_type) {
240         case CHBT_MAC_PM3393:
241                 espi_setup_for_pm3393(adapter);
242                 break;
243         default:
244                 return -1;
245         }
246
247         /*
248          * Make sure any pending interrupts from the SPI are
249          * Cleared before enabling the interrupt.
250          */
251         writel(ESPI_INTR_MASK, espi->adapter->regs + A_ESPI_INTR_ENABLE);
252         status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
253         if (status & F_DIP2PARITYERR) {
254                 cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
255         }
256
257         /*
258          * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
259          * write the status as is.
260          */
261         if (status && t1_is_T1B(espi->adapter))
262                 status = 1;
263         writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
264
265         writel(status_enable_extra | F_RXSTATUSENABLE,
266                adapter->regs + A_ESPI_FIFO_STATUS_ENABLE);
267
268         if (is_T2(adapter)) {
269                 tricn_init(adapter);
270                 /*
271                  * Always position the control at the 1st port egress IN
272                  * (sop,eop) counter to reduce PIOs for T/N210 workaround.
273                  */
274                 espi->misc_ctrl = (readl(adapter->regs + A_ESPI_MISC_CONTROL)
275                                    & ~MON_MASK) | (F_MONITORED_DIRECTION
276                                    | F_MONITORED_INTERFACE);
277                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
278                 spin_lock_init(&espi->lock);
279         }
280
281         return 0;
282 }
283
284 void t1_espi_destroy(struct peespi *espi)
285 {
286         kfree(espi);
287 }
288
289 struct peespi *t1_espi_create(adapter_t *adapter)
290 {
291         struct peespi *espi = kzalloc(sizeof(*espi), GFP_KERNEL);
292
293         if (espi)
294                 espi->adapter = adapter;
295         return espi;
296 }
297
298 void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
299 {
300         struct peespi *espi = adapter->espi;
301
302         if (!is_T2(adapter))
303                 return;
304         spin_lock(&espi->lock);
305         espi->misc_ctrl = (val & ~MON_MASK) |
306                           (espi->misc_ctrl & MON_MASK);
307         writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
308         spin_unlock(&espi->lock);
309 }
310
311 u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
312 {
313         u32 sel;
314
315         struct peespi *espi = adapter->espi;
316
317         if (!is_T2(adapter))
318                 return 0;
319         sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
320         if (!wait) {
321                 if (!spin_trylock(&espi->lock))
322                         return 0;
323         }
324         else
325                 spin_lock(&espi->lock);
326         if ((sel != (espi->misc_ctrl & MON_MASK))) {
327                 writel(((espi->misc_ctrl & ~MON_MASK) | sel),
328                        adapter->regs + A_ESPI_MISC_CONTROL);
329                 sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
330                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
331         }
332         else
333                 sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
334         spin_unlock(&espi->lock);
335         return sel;
336 }