Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-drm-fsl-dcu.git] / drivers / net / ethernet / chelsio / cxgb / 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, see <http://www.gnu.org/licenses/>.            *
16  *                                                                           *
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED    *
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF      *
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.                     *
20  *                                                                           *
21  * http://www.chelsio.com                                                    *
22  *                                                                           *
23  * Copyright (c) 2003 - 2005 Chelsio Communications, Inc.                    *
24  * All rights reserved.                                                      *
25  *                                                                           *
26  * Maintainers: maintainers@chelsio.com                                      *
27  *                                                                           *
28  * Authors: Dimitrios Michailidis   <dm@chelsio.com>                         *
29  *          Tina Yang               <tainay@chelsio.com>                     *
30  *          Felix Marti             <felix@chelsio.com>                      *
31  *          Scott Bardone           <sbardone@chelsio.com>                   *
32  *          Kurt Ottaway            <kottaway@chelsio.com>                   *
33  *          Frank DiMambro          <frank@chelsio.com>                      *
34  *                                                                           *
35  * History:                                                                  *
36  *                                                                           *
37  ****************************************************************************/
38
39 #include "common.h"
40 #include "regs.h"
41 #include "espi.h"
42
43 struct peespi {
44         adapter_t *adapter;
45         struct espi_intr_counts intr_cnt;
46         u32 misc_ctrl;
47         spinlock_t lock;
48 };
49
50 #define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \
51                         F_RAMPARITYERR | F_DIP2PARITYERR)
52 #define MON_MASK  (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \
53                    | F_MONITORED_INTERFACE)
54
55 #define TRICN_CNFG 14
56 #define TRICN_CMD_READ  0x11
57 #define TRICN_CMD_WRITE 0x21
58 #define TRICN_CMD_ATTEMPTS 10
59
60 static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr,
61                        int ch_addr, int reg_offset, u32 wr_data)
62 {
63         int busy, attempts = TRICN_CMD_ATTEMPTS;
64
65         writel(V_WRITE_DATA(wr_data) |
66                V_REGISTER_OFFSET(reg_offset) |
67                V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
68                V_BUNDLE_ADDR(bundle_addr) |
69                V_SPI4_COMMAND(TRICN_CMD_WRITE),
70                adapter->regs + A_ESPI_CMD_ADDR);
71         writel(0, adapter->regs + A_ESPI_GOSTAT);
72
73         do {
74                 busy = readl(adapter->regs + A_ESPI_GOSTAT) & F_ESPI_CMD_BUSY;
75         } while (busy && --attempts);
76
77         if (busy)
78                 pr_err("%s: TRICN write timed out\n", adapter->name);
79
80         return busy;
81 }
82
83 static int tricn_init(adapter_t *adapter)
84 {
85         int i, sme = 1;
86
87         if (!(readl(adapter->regs + A_ESPI_RX_RESET)  & F_RX_CLK_STATUS)) {
88                 pr_err("%s: ESPI clock not ready\n", adapter->name);
89                 return -1;
90         }
91
92         writel(F_ESPI_RX_CORE_RST, adapter->regs + A_ESPI_RX_RESET);
93
94         if (sme) {
95                 tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
96                 tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
97                 tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
98         }
99         for (i = 1; i <= 8; i++)
100                 tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
101         for (i = 1; i <= 2; i++)
102                 tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
103         for (i = 1; i <= 3; i++)
104                 tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
105         tricn_write(adapter, 0, 2, 4, TRICN_CNFG, 0xf1);
106         tricn_write(adapter, 0, 2, 5, TRICN_CNFG, 0xe1);
107         tricn_write(adapter, 0, 2, 6, TRICN_CNFG, 0xf1);
108         tricn_write(adapter, 0, 2, 7, TRICN_CNFG, 0x80);
109         tricn_write(adapter, 0, 2, 8, TRICN_CNFG, 0xf1);
110
111         writel(F_ESPI_RX_CORE_RST | F_ESPI_RX_LNK_RST,
112                adapter->regs + A_ESPI_RX_RESET);
113
114         return 0;
115 }
116
117 void t1_espi_intr_enable(struct peespi *espi)
118 {
119         u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
120
121         /*
122          * Cannot enable ESPI interrupts on T1B because HW asserts the
123          * interrupt incorrectly, namely the driver gets ESPI interrupts
124          * but no data is actually dropped (can verify this reading the ESPI
125          * drop registers).  Also, once the ESPI interrupt is asserted it
126          * cannot be cleared (HW bug).
127          */
128         enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
129         writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE);
130         writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
131 }
132
133 void t1_espi_intr_clear(struct peespi *espi)
134 {
135         readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
136         writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS);
137         writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE);
138 }
139
140 void t1_espi_intr_disable(struct peespi *espi)
141 {
142         u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE);
143
144         writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE);
145         writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE);
146 }
147
148 int t1_espi_intr_handler(struct peespi *espi)
149 {
150         u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS);
151
152         if (status & F_DIP4ERR)
153                 espi->intr_cnt.DIP4_err++;
154         if (status & F_RXDROP)
155                 espi->intr_cnt.rx_drops++;
156         if (status & F_TXDROP)
157                 espi->intr_cnt.tx_drops++;
158         if (status & F_RXOVERFLOW)
159                 espi->intr_cnt.rx_ovflw++;
160         if (status & F_RAMPARITYERR)
161                 espi->intr_cnt.parity_err++;
162         if (status & F_DIP2PARITYERR) {
163                 espi->intr_cnt.DIP2_parity_err++;
164
165                 /*
166                  * Must read the error count to clear the interrupt
167                  * that it causes.
168                  */
169                 readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT);
170         }
171
172         /*
173          * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
174          * write the status as is.
175          */
176         if (status && t1_is_T1B(espi->adapter))
177                 status = 1;
178         writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS);
179         return 0;
180 }
181
182 const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi)
183 {
184         return &espi->intr_cnt;
185 }
186
187 static void espi_setup_for_pm3393(adapter_t *adapter)
188 {
189         u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
190
191         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0);
192         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1);
193         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2);
194         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3);
195         writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
196         writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
197         writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH);
198         writel(0x08000008, adapter->regs + A_ESPI_TRAIN);
199         writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG);
200 }
201
202 static void espi_setup_for_vsc7321(adapter_t *adapter)
203 {
204         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0);
205         writel(0x1f401f4, adapter->regs + A_ESPI_SCH_TOKEN1);
206         writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2);
207         writel(0xa00, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
208         writel(0x1ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
209         writel(1, adapter->regs + A_ESPI_CALENDAR_LENGTH);
210         writel(V_RX_NPORTS(4) | V_TX_NPORTS(4), adapter->regs + A_PORT_CONFIG);
211
212         writel(0x08000008, adapter->regs + A_ESPI_TRAIN);
213 }
214
215 /*
216  * Note that T1B requires at least 2 ports for IXF1010 due to a HW bug.
217  */
218 static void espi_setup_for_ixf1010(adapter_t *adapter, int nports)
219 {
220         writel(1, adapter->regs + A_ESPI_CALENDAR_LENGTH);
221         if (nports == 4) {
222                 if (is_T2(adapter)) {
223                         writel(0xf00, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
224                         writel(0x3c0, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
225                 } else {
226                         writel(0x7ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
227                         writel(0x1ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
228                 }
229         } else {
230                 writel(0x1fff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK);
231                 writel(0x7ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK);
232         }
233         writel(V_RX_NPORTS(nports) | V_TX_NPORTS(nports), adapter->regs + A_PORT_CONFIG);
234
235 }
236
237 int t1_espi_init(struct peespi *espi, int mac_type, int nports)
238 {
239         u32 status_enable_extra = 0;
240         adapter_t *adapter = espi->adapter;
241
242         /* Disable ESPI training.  MACs that can handle it enable it below. */
243         writel(0, adapter->regs + A_ESPI_TRAIN);
244
245         if (is_T2(adapter)) {
246                 writel(V_OUT_OF_SYNC_COUNT(4) |
247                        V_DIP2_PARITY_ERR_THRES(3) |
248                        V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL);
249                 writel(nports == 4 ? 0x200040 : 0x1000080,
250                        adapter->regs + A_ESPI_MAXBURST1_MAXBURST2);
251         } else
252                 writel(0x800100, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2);
253
254         if (mac_type == CHBT_MAC_PM3393)
255                 espi_setup_for_pm3393(adapter);
256         else if (mac_type == CHBT_MAC_VSC7321)
257                 espi_setup_for_vsc7321(adapter);
258         else if (mac_type == CHBT_MAC_IXF1010) {
259                 status_enable_extra = F_INTEL1010MODE;
260                 espi_setup_for_ixf1010(adapter, nports);
261         } else
262                 return -1;
263
264         writel(status_enable_extra | F_RXSTATUSENABLE,
265                adapter->regs + A_ESPI_FIFO_STATUS_ENABLE);
266
267         if (is_T2(adapter)) {
268                 tricn_init(adapter);
269                 /*
270                  * Always position the control at the 1st port egress IN
271                  * (sop,eop) counter to reduce PIOs for T/N210 workaround.
272                  */
273                 espi->misc_ctrl = readl(adapter->regs + A_ESPI_MISC_CONTROL);
274                 espi->misc_ctrl &= ~MON_MASK;
275                 espi->misc_ctrl |= F_MONITORED_DIRECTION;
276                 if (adapter->params.nports == 1)
277                         espi->misc_ctrl |= F_MONITORED_INTERFACE;
278                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
279                 spin_lock_init(&espi->lock);
280         }
281
282         return 0;
283 }
284
285 void t1_espi_destroy(struct peespi *espi)
286 {
287         kfree(espi);
288 }
289
290 struct peespi *t1_espi_create(adapter_t *adapter)
291 {
292         struct peespi *espi = kzalloc(sizeof(*espi), GFP_KERNEL);
293
294         if (espi)
295                 espi->adapter = adapter;
296         return espi;
297 }
298
299 #if 0
300 void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
301 {
302         struct peespi *espi = adapter->espi;
303
304         if (!is_T2(adapter))
305                 return;
306         spin_lock(&espi->lock);
307         espi->misc_ctrl = (val & ~MON_MASK) |
308                           (espi->misc_ctrl & MON_MASK);
309         writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
310         spin_unlock(&espi->lock);
311 }
312 #endif  /*  0  */
313
314 u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
315 {
316         struct peespi *espi = adapter->espi;
317         u32 sel;
318
319         if (!is_T2(adapter))
320                 return 0;
321
322         sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
323         if (!wait) {
324                 if (!spin_trylock(&espi->lock))
325                         return 0;
326         } else
327                 spin_lock(&espi->lock);
328
329         if ((sel != (espi->misc_ctrl & MON_MASK))) {
330                 writel(((espi->misc_ctrl & ~MON_MASK) | sel),
331                        adapter->regs + A_ESPI_MISC_CONTROL);
332                 sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
333                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
334         } else
335                 sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
336         spin_unlock(&espi->lock);
337         return sel;
338 }
339
340 /*
341  * This function is for T204 only.
342  * compare with t1_espi_get_mon(), it reads espiInTxSop[0 ~ 3] in
343  * one shot, since there is no per port counter on the out side.
344  */
345 int t1_espi_get_mon_t204(adapter_t *adapter, u32 *valp, u8 wait)
346 {
347         struct peespi *espi = adapter->espi;
348         u8 i, nport = (u8)adapter->params.nports;
349
350         if (!wait) {
351                 if (!spin_trylock(&espi->lock))
352                         return -1;
353         } else
354                 spin_lock(&espi->lock);
355
356         if ((espi->misc_ctrl & MON_MASK) != F_MONITORED_DIRECTION) {
357                 espi->misc_ctrl = (espi->misc_ctrl & ~MON_MASK) |
358                                         F_MONITORED_DIRECTION;
359                 writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
360         }
361         for (i = 0 ; i < nport; i++, valp++) {
362                 if (i) {
363                         writel(espi->misc_ctrl | V_MONITORED_PORT_NUM(i),
364                                adapter->regs + A_ESPI_MISC_CONTROL);
365                 }
366                 *valp = readl(adapter->regs + A_ESPI_SCH_TOKEN3);
367         }
368
369         writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL);
370         spin_unlock(&espi->lock);
371         return 0;
372 }