Merge branch 'origin'
[pandora-kernel.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     sme             = 1;
91         int     stat            = 0;
92         int     timeout         = 0;
93         int     is_ready        = 0;
94         int     dynamic_deskew  = 0;
95
96         if (dynamic_deskew)
97                 sme = 0;
98
99
100         /* 1 */
101         timeout=1000;
102         do {
103                 stat = readl(adapter->regs + A_ESPI_RX_RESET);
104                 is_ready = (stat & 0x4);
105                 timeout--;
106                 udelay(5);
107         } while (!is_ready || (timeout==0));
108         writel(0x2, adapter->regs + A_ESPI_RX_RESET);
109         if (timeout==0)
110         {
111                 CH_ERR("ESPI : ERROR : Timeout tricn_init() \n");
112                 t1_fatal_err(adapter);
113         }
114
115         /* 2 */
116         if (sme) {
117                 tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
118                 tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
119                 tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
120         }
121         for (i=1; i<= 8; i++) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
122         for (i=1; i<= 2; i++) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
123         for (i=1; i<= 3; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
124         for (i=4; i<= 4; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
125         for (i=5; i<= 5; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
126         for (i=6; i<= 6; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
127         for (i=7; i<= 7; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0x80);
128         for (i=8; i<= 8; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
129
130         /* 3 */
131         writel(0x3, adapter->regs + A_ESPI_RX_RESET);
132
133         return 0;
134 }
135
136 void t1_espi_intr_enable(struct peespi *espi)
137 {
138         u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
139
140         /*
141          * Cannot enable ESPI interrupts on T1B because HW asserts the
142          * interrupt incorrectly, namely the driver gets ESPI interrupts
143          * but no data is actually dropped (can verify this reading the ESPI
144          * drop registers).  Also, once the ESPI interrupt is asserted it
145          * cannot be cleared (HW bug).
146          */
147         enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
148         writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE);
149         writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
150 }
151
152 void t1_espi_intr_clear(struct peespi *espi)
153 {
154         writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS);
155         writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE);
156 }
157
158 void t1_espi_intr_disable(struct peespi *espi)
159 {
160         u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
161
162         writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE);
163         writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
164 }
165
166 int t1_espi_intr_handler(struct peespi *espi)
167 {
168         u32 cnt;
169         u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
170
171         if (status & F_DIP4ERR)
172                 espi->intr_cnt.DIP4_err++;
173         if (status & F_RXDROP)
174                 espi->intr_cnt.rx_drops++;
175         if (status & F_TXDROP)
176                 espi->intr_cnt.tx_drops++;
177         if (status & F_RXOVERFLOW)
178                 espi->intr_cnt.rx_ovflw++;
179         if (status & F_RAMPARITYERR)
180                 espi->intr_cnt.parity_err++;
181         if (status & F_DIP2PARITYERR) {
182                 espi->intr_cnt.DIP2_parity_err++;
183
184                 /*
185                  * Must read the error count to clear the interrupt
186                  * that it causes.
187                  */
188                 cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
189         }
190
191         /*
192          * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
193          * write the status as is.
194          */
195         if (status && t1_is_T1B(espi->adapter))
196                 status = 1;
197         writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
198         return 0;
199 }
200
201 const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi)
202 {
203     return &espi->intr_cnt;
204 }
205
206 static void espi_setup_for_pm3393(adapter_t *adapter)
207 {
208         u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
209
210         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0);
211         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1);
212         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2);
213         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3);
214         writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
215         writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
216         writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH);
217         writel(0x08000008, adapter->regs + A_ESPI_TRAIN);
218         writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG);
219 }
220
221 /* T2 Init part --  */
222 /* 1. Set T_ESPI_MISCCTRL_ADDR */
223 /* 2. Init ESPI registers. */
224 /* 3. Init TriCN Hard Macro */
225 int t1_espi_init(struct peespi *espi, int mac_type, int nports)
226 {
227         u32 cnt;
228
229         u32 status_enable_extra = 0;
230         adapter_t *adapter = espi->adapter;
231         u32 status, burstval = 0x800100;
232
233         /* Disable ESPI training.  MACs that can handle it enable it below. */
234         writel(0, adapter->regs + A_ESPI_TRAIN);
235
236         if (is_T2(adapter)) {
237                 writel(V_OUT_OF_SYNC_COUNT(4) |
238                        V_DIP2_PARITY_ERR_THRES(3) |
239                        V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL);
240                 if (nports == 4) {
241                         /* T204: maxburst1 = 0x40, maxburst2 = 0x20 */
242                         burstval = 0x200040;
243                 }
244         }
245         writel(burstval, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2);
246
247         switch (mac_type) {
248         case CHBT_MAC_PM3393:
249                 espi_setup_for_pm3393(adapter);
250                 break;
251         default:
252                 return -1;
253         }
254
255         /*
256          * Make sure any pending interrupts from the SPI are
257          * Cleared before enabling the interrupt.
258          */
259         writel(ESPI_INTR_MASK, espi->adapter->regs + A_ESPI_INTR_ENABLE);
260         status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
261         if (status & F_DIP2PARITYERR) {
262                 cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
263         }
264
265         /*
266          * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
267          * write the status as is.
268          */
269         if (status && t1_is_T1B(espi->adapter))
270                 status = 1;
271         writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
272
273         writel(status_enable_extra | F_RXSTATUSENABLE,
274                adapter->regs + A_ESPI_FIFO_STATUS_ENABLE);
275
276         if (is_T2(adapter)) {
277                 tricn_init(adapter);
278                 /*
279                  * Always position the control at the 1st port egress IN
280                  * (sop,eop) counter to reduce PIOs for T/N210 workaround.
281                  */
282                 espi->misc_ctrl = (readl(adapter->regs + A_ESPI_MISC_CONTROL)
283                                    & ~MON_MASK) | (F_MONITORED_DIRECTION
284                                    | F_MONITORED_INTERFACE);
285                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
286                 spin_lock_init(&espi->lock);
287         }
288
289         return 0;
290 }
291
292 void t1_espi_destroy(struct peespi *espi)
293 {
294         kfree(espi);
295 }
296
297 struct peespi *t1_espi_create(adapter_t *adapter)
298 {
299         struct peespi *espi = kzalloc(sizeof(*espi), GFP_KERNEL);
300
301         if (espi)
302                 espi->adapter = adapter;
303         return espi;
304 }
305
306 void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
307 {
308         struct peespi *espi = adapter->espi;
309
310         if (!is_T2(adapter))
311                 return;
312         spin_lock(&espi->lock);
313         espi->misc_ctrl = (val & ~MON_MASK) |
314                           (espi->misc_ctrl & MON_MASK);
315         writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
316         spin_unlock(&espi->lock);
317 }
318
319 u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
320 {
321         u32 sel;
322
323         struct peespi *espi = adapter->espi;
324
325         if (!is_T2(adapter))
326                 return 0;
327         sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
328         if (!wait) {
329                 if (!spin_trylock(&espi->lock))
330                         return 0;
331         }
332         else
333                 spin_lock(&espi->lock);
334         if ((sel != (espi->misc_ctrl & MON_MASK))) {
335                 writel(((espi->misc_ctrl & ~MON_MASK) | sel),
336                        adapter->regs + A_ESPI_MISC_CONTROL);
337                 sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
338                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
339         }
340         else
341                 sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
342         spin_unlock(&espi->lock);
343         return sel;
344 }