diff options
-rw-r--r-- | drivers/net/sfc/mcdi.c | 1112 | ||||
-rw-r--r-- | drivers/net/sfc/mcdi.h | 130 | ||||
-rw-r--r-- | drivers/net/sfc/mcdi_mac.c | 152 | ||||
-rw-r--r-- | drivers/net/sfc/mcdi_phy.c | 597 | ||||
-rw-r--r-- | drivers/net/sfc/siena.c | 604 |
5 files changed, 2595 insertions, 0 deletions
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c new file mode 100644 index 000000000000..683353b904c7 --- /dev/null +++ b/drivers/net/sfc/mcdi.c | |||
@@ -0,0 +1,1112 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2008-2009 Solarflare Communications Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation, incorporated herein by reference. | ||
8 | */ | ||
9 | |||
10 | #include <linux/delay.h> | ||
11 | #include "net_driver.h" | ||
12 | #include "nic.h" | ||
13 | #include "io.h" | ||
14 | #include "regs.h" | ||
15 | #include "mcdi_pcol.h" | ||
16 | #include "phy.h" | ||
17 | |||
18 | /************************************************************************** | ||
19 | * | ||
20 | * Management-Controller-to-Driver Interface | ||
21 | * | ||
22 | ************************************************************************** | ||
23 | */ | ||
24 | |||
25 | /* Software-defined structure to the shared-memory */ | ||
26 | #define CMD_NOTIFY_PORT0 0 | ||
27 | #define CMD_NOTIFY_PORT1 4 | ||
28 | #define CMD_PDU_PORT0 0x008 | ||
29 | #define CMD_PDU_PORT1 0x108 | ||
30 | #define REBOOT_FLAG_PORT0 0x3f8 | ||
31 | #define REBOOT_FLAG_PORT1 0x3fc | ||
32 | |||
33 | #define MCDI_RPC_TIMEOUT 10 /*seconds */ | ||
34 | |||
35 | #define MCDI_PDU(efx) \ | ||
36 | (efx_port_num(efx) ? CMD_PDU_PORT1 : CMD_PDU_PORT0) | ||
37 | #define MCDI_DOORBELL(efx) \ | ||
38 | (efx_port_num(efx) ? CMD_NOTIFY_PORT1 : CMD_NOTIFY_PORT0) | ||
39 | #define MCDI_REBOOT_FLAG(efx) \ | ||
40 | (efx_port_num(efx) ? REBOOT_FLAG_PORT1 : REBOOT_FLAG_PORT0) | ||
41 | |||
42 | #define SEQ_MASK \ | ||
43 | EFX_MASK32(EFX_WIDTH(MCDI_HEADER_SEQ)) | ||
44 | |||
45 | static inline struct efx_mcdi_iface *efx_mcdi(struct efx_nic *efx) | ||
46 | { | ||
47 | struct siena_nic_data *nic_data; | ||
48 | EFX_BUG_ON_PARANOID(efx_nic_rev(efx) < EFX_REV_SIENA_A0); | ||
49 | nic_data = efx->nic_data; | ||
50 | return &nic_data->mcdi; | ||
51 | } | ||
52 | |||
53 | void efx_mcdi_init(struct efx_nic *efx) | ||
54 | { | ||
55 | struct efx_mcdi_iface *mcdi; | ||
56 | |||
57 | if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) | ||
58 | return; | ||
59 | |||
60 | mcdi = efx_mcdi(efx); | ||
61 | init_waitqueue_head(&mcdi->wq); | ||
62 | spin_lock_init(&mcdi->iface_lock); | ||
63 | atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); | ||
64 | mcdi->mode = MCDI_MODE_POLL; | ||
65 | |||
66 | (void) efx_mcdi_poll_reboot(efx); | ||
67 | } | ||
68 | |||
69 | static void efx_mcdi_copyin(struct efx_nic *efx, unsigned cmd, | ||
70 | const u8 *inbuf, size_t inlen) | ||
71 | { | ||
72 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
73 | unsigned pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); | ||
74 | unsigned doorbell = FR_CZ_MC_TREG_SMEM + MCDI_DOORBELL(efx); | ||
75 | unsigned int i; | ||
76 | efx_dword_t hdr; | ||
77 | u32 xflags, seqno; | ||
78 | |||
79 | BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); | ||
80 | BUG_ON(inlen & 3 || inlen >= 0x100); | ||
81 | |||
82 | seqno = mcdi->seqno & SEQ_MASK; | ||
83 | xflags = 0; | ||
84 | if (mcdi->mode == MCDI_MODE_EVENTS) | ||
85 | xflags |= MCDI_HEADER_XFLAGS_EVREQ; | ||
86 | |||
87 | EFX_POPULATE_DWORD_6(hdr, | ||
88 | MCDI_HEADER_RESPONSE, 0, | ||
89 | MCDI_HEADER_RESYNC, 1, | ||
90 | MCDI_HEADER_CODE, cmd, | ||
91 | MCDI_HEADER_DATALEN, inlen, | ||
92 | MCDI_HEADER_SEQ, seqno, | ||
93 | MCDI_HEADER_XFLAGS, xflags); | ||
94 | |||
95 | efx_writed(efx, &hdr, pdu); | ||
96 | |||
97 | for (i = 0; i < inlen; i += 4) | ||
98 | _efx_writed(efx, *((__le32 *)(inbuf + i)), pdu + 4 + i); | ||
99 | |||
100 | /* Ensure the payload is written out before the header */ | ||
101 | wmb(); | ||
102 | |||
103 | /* ring the doorbell with a distinctive value */ | ||
104 | _efx_writed(efx, (__force __le32) 0x45789abc, doorbell); | ||
105 | } | ||
106 | |||
107 | static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) | ||
108 | { | ||
109 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
110 | unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); | ||
111 | int i; | ||
112 | |||
113 | BUG_ON(atomic_read(&mcdi->state) == MCDI_STATE_QUIESCENT); | ||
114 | BUG_ON(outlen & 3 || outlen >= 0x100); | ||
115 | |||
116 | for (i = 0; i < outlen; i += 4) | ||
117 | *((__le32 *)(outbuf + i)) = _efx_readd(efx, pdu + 4 + i); | ||
118 | } | ||
119 | |||
120 | static int efx_mcdi_poll(struct efx_nic *efx) | ||
121 | { | ||
122 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
123 | unsigned int time, finish; | ||
124 | unsigned int respseq, respcmd, error; | ||
125 | unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); | ||
126 | unsigned int rc, spins; | ||
127 | efx_dword_t reg; | ||
128 | |||
129 | /* Check for a reboot atomically with respect to efx_mcdi_copyout() */ | ||
130 | rc = efx_mcdi_poll_reboot(efx); | ||
131 | if (rc) | ||
132 | goto out; | ||
133 | |||
134 | /* Poll for completion. Poll quickly (once a us) for the 1st jiffy, | ||
135 | * because generally mcdi responses are fast. After that, back off | ||
136 | * and poll once a jiffy (approximately) | ||
137 | */ | ||
138 | spins = TICK_USEC; | ||
139 | finish = get_seconds() + MCDI_RPC_TIMEOUT; | ||
140 | |||
141 | while (1) { | ||
142 | if (spins != 0) { | ||
143 | --spins; | ||
144 | udelay(1); | ||
145 | } else | ||
146 | schedule(); | ||
147 | |||
148 | time = get_seconds(); | ||
149 | |||
150 | rmb(); | ||
151 | efx_readd(efx, ®, pdu); | ||
152 | |||
153 | /* All 1's indicates that shared memory is in reset (and is | ||
154 | * not a valid header). Wait for it to come out reset before | ||
155 | * completing the command */ | ||
156 | if (EFX_DWORD_FIELD(reg, EFX_DWORD_0) != 0xffffffff && | ||
157 | EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE)) | ||
158 | break; | ||
159 | |||
160 | if (time >= finish) | ||
161 | return -ETIMEDOUT; | ||
162 | } | ||
163 | |||
164 | mcdi->resplen = EFX_DWORD_FIELD(reg, MCDI_HEADER_DATALEN); | ||
165 | respseq = EFX_DWORD_FIELD(reg, MCDI_HEADER_SEQ); | ||
166 | respcmd = EFX_DWORD_FIELD(reg, MCDI_HEADER_CODE); | ||
167 | error = EFX_DWORD_FIELD(reg, MCDI_HEADER_ERROR); | ||
168 | |||
169 | if (error && mcdi->resplen == 0) { | ||
170 | EFX_ERR(efx, "MC rebooted\n"); | ||
171 | rc = EIO; | ||
172 | } else if ((respseq ^ mcdi->seqno) & SEQ_MASK) { | ||
173 | EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx seq 0x%x\n", | ||
174 | respseq, mcdi->seqno); | ||
175 | rc = EIO; | ||
176 | } else if (error) { | ||
177 | efx_readd(efx, ®, pdu + 4); | ||
178 | switch (EFX_DWORD_FIELD(reg, EFX_DWORD_0)) { | ||
179 | #define TRANSLATE_ERROR(name) \ | ||
180 | case MC_CMD_ERR_ ## name: \ | ||
181 | rc = name; \ | ||
182 | break | ||
183 | TRANSLATE_ERROR(ENOENT); | ||
184 | TRANSLATE_ERROR(EINTR); | ||
185 | TRANSLATE_ERROR(EACCES); | ||
186 | TRANSLATE_ERROR(EBUSY); | ||
187 | TRANSLATE_ERROR(EINVAL); | ||
188 | TRANSLATE_ERROR(EDEADLK); | ||
189 | TRANSLATE_ERROR(ENOSYS); | ||
190 | TRANSLATE_ERROR(ETIME); | ||
191 | #undef TRANSLATE_ERROR | ||
192 | default: | ||
193 | rc = EIO; | ||
194 | break; | ||
195 | } | ||
196 | } else | ||
197 | rc = 0; | ||
198 | |||
199 | out: | ||
200 | mcdi->resprc = rc; | ||
201 | if (rc) | ||
202 | mcdi->resplen = 0; | ||
203 | |||
204 | /* Return rc=0 like wait_event_timeout() */ | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* Test and clear MC-rebooted flag for this port/function */ | ||
209 | int efx_mcdi_poll_reboot(struct efx_nic *efx) | ||
210 | { | ||
211 | unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_REBOOT_FLAG(efx); | ||
212 | efx_dword_t reg; | ||
213 | uint32_t value; | ||
214 | |||
215 | if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) | ||
216 | return false; | ||
217 | |||
218 | efx_readd(efx, ®, addr); | ||
219 | value = EFX_DWORD_FIELD(reg, EFX_DWORD_0); | ||
220 | |||
221 | if (value == 0) | ||
222 | return 0; | ||
223 | |||
224 | EFX_ZERO_DWORD(reg); | ||
225 | efx_writed(efx, ®, addr); | ||
226 | |||
227 | if (value == MC_STATUS_DWORD_ASSERT) | ||
228 | return -EINTR; | ||
229 | else | ||
230 | return -EIO; | ||
231 | } | ||
232 | |||
233 | static void efx_mcdi_acquire(struct efx_mcdi_iface *mcdi) | ||
234 | { | ||
235 | /* Wait until the interface becomes QUIESCENT and we win the race | ||
236 | * to mark it RUNNING. */ | ||
237 | wait_event(mcdi->wq, | ||
238 | atomic_cmpxchg(&mcdi->state, | ||
239 | MCDI_STATE_QUIESCENT, | ||
240 | MCDI_STATE_RUNNING) | ||
241 | == MCDI_STATE_QUIESCENT); | ||
242 | } | ||
243 | |||
244 | static int efx_mcdi_await_completion(struct efx_nic *efx) | ||
245 | { | ||
246 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
247 | |||
248 | if (wait_event_timeout( | ||
249 | mcdi->wq, | ||
250 | atomic_read(&mcdi->state) == MCDI_STATE_COMPLETED, | ||
251 | msecs_to_jiffies(MCDI_RPC_TIMEOUT * 1000)) == 0) | ||
252 | return -ETIMEDOUT; | ||
253 | |||
254 | /* Check if efx_mcdi_set_mode() switched us back to polled completions. | ||
255 | * In which case, poll for completions directly. If efx_mcdi_ev_cpl() | ||
256 | * completed the request first, then we'll just end up completing the | ||
257 | * request again, which is safe. | ||
258 | * | ||
259 | * We need an smp_rmb() to synchronise with efx_mcdi_mode_poll(), which | ||
260 | * wait_event_timeout() implicitly provides. | ||
261 | */ | ||
262 | if (mcdi->mode == MCDI_MODE_POLL) | ||
263 | return efx_mcdi_poll(efx); | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | static bool efx_mcdi_complete(struct efx_mcdi_iface *mcdi) | ||
269 | { | ||
270 | /* If the interface is RUNNING, then move to COMPLETED and wake any | ||
271 | * waiters. If the interface isn't in RUNNING then we've received a | ||
272 | * duplicate completion after we've already transitioned back to | ||
273 | * QUIESCENT. [A subsequent invocation would increment seqno, so would | ||
274 | * have failed the seqno check]. | ||
275 | */ | ||
276 | if (atomic_cmpxchg(&mcdi->state, | ||
277 | MCDI_STATE_RUNNING, | ||
278 | MCDI_STATE_COMPLETED) == MCDI_STATE_RUNNING) { | ||
279 | wake_up(&mcdi->wq); | ||
280 | return true; | ||
281 | } | ||
282 | |||
283 | return false; | ||
284 | } | ||
285 | |||
286 | static void efx_mcdi_release(struct efx_mcdi_iface *mcdi) | ||
287 | { | ||
288 | atomic_set(&mcdi->state, MCDI_STATE_QUIESCENT); | ||
289 | wake_up(&mcdi->wq); | ||
290 | } | ||
291 | |||
292 | static void efx_mcdi_ev_cpl(struct efx_nic *efx, unsigned int seqno, | ||
293 | unsigned int datalen, unsigned int errno) | ||
294 | { | ||
295 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
296 | bool wake = false; | ||
297 | |||
298 | spin_lock(&mcdi->iface_lock); | ||
299 | |||
300 | if ((seqno ^ mcdi->seqno) & SEQ_MASK) { | ||
301 | if (mcdi->credits) | ||
302 | /* The request has been cancelled */ | ||
303 | --mcdi->credits; | ||
304 | else | ||
305 | EFX_ERR(efx, "MC response mismatch tx seq 0x%x rx " | ||
306 | "seq 0x%x\n", seqno, mcdi->seqno); | ||
307 | } else { | ||
308 | mcdi->resprc = errno; | ||
309 | mcdi->resplen = datalen; | ||
310 | |||
311 | wake = true; | ||
312 | } | ||
313 | |||
314 | spin_unlock(&mcdi->iface_lock); | ||
315 | |||
316 | if (wake) | ||
317 | efx_mcdi_complete(mcdi); | ||
318 | } | ||
319 | |||
320 | /* Issue the given command by writing the data into the shared memory PDU, | ||
321 | * ring the doorbell and wait for completion. Copyout the result. */ | ||
322 | int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, | ||
323 | const u8 *inbuf, size_t inlen, u8 *outbuf, size_t outlen, | ||
324 | size_t *outlen_actual) | ||
325 | { | ||
326 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
327 | int rc; | ||
328 | BUG_ON(efx_nic_rev(efx) < EFX_REV_SIENA_A0); | ||
329 | |||
330 | efx_mcdi_acquire(mcdi); | ||
331 | |||
332 | /* Serialise with efx_mcdi_ev_cpl() and efx_mcdi_ev_death() */ | ||
333 | spin_lock_bh(&mcdi->iface_lock); | ||
334 | ++mcdi->seqno; | ||
335 | spin_unlock_bh(&mcdi->iface_lock); | ||
336 | |||
337 | efx_mcdi_copyin(efx, cmd, inbuf, inlen); | ||
338 | |||
339 | if (mcdi->mode == MCDI_MODE_POLL) | ||
340 | rc = efx_mcdi_poll(efx); | ||
341 | else | ||
342 | rc = efx_mcdi_await_completion(efx); | ||
343 | |||
344 | if (rc != 0) { | ||
345 | /* Close the race with efx_mcdi_ev_cpl() executing just too late | ||
346 | * and completing a request we've just cancelled, by ensuring | ||
347 | * that the seqno check therein fails. | ||
348 | */ | ||
349 | spin_lock_bh(&mcdi->iface_lock); | ||
350 | ++mcdi->seqno; | ||
351 | ++mcdi->credits; | ||
352 | spin_unlock_bh(&mcdi->iface_lock); | ||
353 | |||
354 | EFX_ERR(efx, "MC command 0x%x inlen %d mode %d timed out\n", | ||
355 | cmd, (int)inlen, mcdi->mode); | ||
356 | } else { | ||
357 | size_t resplen; | ||
358 | |||
359 | /* At the very least we need a memory barrier here to ensure | ||
360 | * we pick up changes from efx_mcdi_ev_cpl(). Protect against | ||
361 | * a spurious efx_mcdi_ev_cpl() running concurrently by | ||
362 | * acquiring the iface_lock. */ | ||
363 | spin_lock_bh(&mcdi->iface_lock); | ||
364 | rc = -mcdi->resprc; | ||
365 | resplen = mcdi->resplen; | ||
366 | spin_unlock_bh(&mcdi->iface_lock); | ||
367 | |||
368 | if (rc == 0) { | ||
369 | efx_mcdi_copyout(efx, outbuf, | ||
370 | min(outlen, mcdi->resplen + 3) & ~0x3); | ||
371 | if (outlen_actual != NULL) | ||
372 | *outlen_actual = resplen; | ||
373 | } else if (cmd == MC_CMD_REBOOT && rc == -EIO) | ||
374 | ; /* Don't reset if MC_CMD_REBOOT returns EIO */ | ||
375 | else if (rc == -EIO || rc == -EINTR) { | ||
376 | EFX_ERR(efx, "MC fatal error %d\n", -rc); | ||
377 | efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); | ||
378 | } else | ||
379 | EFX_ERR(efx, "MC command 0x%x inlen %d failed rc=%d\n", | ||
380 | cmd, (int)inlen, -rc); | ||
381 | } | ||
382 | |||
383 | efx_mcdi_release(mcdi); | ||
384 | return rc; | ||
385 | } | ||
386 | |||
387 | void efx_mcdi_mode_poll(struct efx_nic *efx) | ||
388 | { | ||
389 | struct efx_mcdi_iface *mcdi; | ||
390 | |||
391 | if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) | ||
392 | return; | ||
393 | |||
394 | mcdi = efx_mcdi(efx); | ||
395 | if (mcdi->mode == MCDI_MODE_POLL) | ||
396 | return; | ||
397 | |||
398 | /* We can switch from event completion to polled completion, because | ||
399 | * mcdi requests are always completed in shared memory. We do this by | ||
400 | * switching the mode to POLL'd then completing the request. | ||
401 | * efx_mcdi_await_completion() will then call efx_mcdi_poll(). | ||
402 | * | ||
403 | * We need an smp_wmb() to synchronise with efx_mcdi_await_completion(), | ||
404 | * which efx_mcdi_complete() provides for us. | ||
405 | */ | ||
406 | mcdi->mode = MCDI_MODE_POLL; | ||
407 | |||
408 | efx_mcdi_complete(mcdi); | ||
409 | } | ||
410 | |||
411 | void efx_mcdi_mode_event(struct efx_nic *efx) | ||
412 | { | ||
413 | struct efx_mcdi_iface *mcdi; | ||
414 | |||
415 | if (efx_nic_rev(efx) < EFX_REV_SIENA_A0) | ||
416 | return; | ||
417 | |||
418 | mcdi = efx_mcdi(efx); | ||
419 | |||
420 | if (mcdi->mode == MCDI_MODE_EVENTS) | ||
421 | return; | ||
422 | |||
423 | /* We can't switch from polled to event completion in the middle of a | ||
424 | * request, because the completion method is specified in the request. | ||
425 | * So acquire the interface to serialise the requestors. We don't need | ||
426 | * to acquire the iface_lock to change the mode here, but we do need a | ||
427 | * write memory barrier ensure that efx_mcdi_rpc() sees it, which | ||
428 | * efx_mcdi_acquire() provides. | ||
429 | */ | ||
430 | efx_mcdi_acquire(mcdi); | ||
431 | mcdi->mode = MCDI_MODE_EVENTS; | ||
432 | efx_mcdi_release(mcdi); | ||
433 | } | ||
434 | |||
435 | static void efx_mcdi_ev_death(struct efx_nic *efx, int rc) | ||
436 | { | ||
437 | struct efx_mcdi_iface *mcdi = efx_mcdi(efx); | ||
438 | |||
439 | /* If there is an outstanding MCDI request, it has been terminated | ||
440 | * either by a BADASSERT or REBOOT event. If the mcdi interface is | ||
441 | * in polled mode, then do nothing because the MC reboot handler will | ||
442 | * set the header correctly. However, if the mcdi interface is waiting | ||
443 | * for a CMDDONE event it won't receive it [and since all MCDI events | ||
444 | * are sent to the same queue, we can't be racing with | ||
445 | * efx_mcdi_ev_cpl()] | ||
446 | * | ||
447 | * There's a race here with efx_mcdi_rpc(), because we might receive | ||
448 | * a REBOOT event *before* the request has been copied out. In polled | ||
449 | * mode (during startup) this is irrelevent, because efx_mcdi_complete() | ||
450 | * is ignored. In event mode, this condition is just an edge-case of | ||
451 | * receiving a REBOOT event after posting the MCDI request. Did the mc | ||
452 | * reboot before or after the copyout? The best we can do always is | ||
453 | * just return failure. | ||
454 | */ | ||
455 | spin_lock(&mcdi->iface_lock); | ||
456 | if (efx_mcdi_complete(mcdi)) { | ||
457 | if (mcdi->mode == MCDI_MODE_EVENTS) { | ||
458 | mcdi->resprc = rc; | ||
459 | mcdi->resplen = 0; | ||
460 | } | ||
461 | } else | ||
462 | /* Nobody was waiting for an MCDI request, so trigger a reset */ | ||
463 | efx_schedule_reset(efx, RESET_TYPE_MC_FAILURE); | ||
464 | |||
465 | spin_unlock(&mcdi->iface_lock); | ||
466 | } | ||
467 | |||
468 | static unsigned int efx_mcdi_event_link_speed[] = { | ||
469 | [MCDI_EVENT_LINKCHANGE_SPEED_100M] = 100, | ||
470 | [MCDI_EVENT_LINKCHANGE_SPEED_1G] = 1000, | ||
471 | [MCDI_EVENT_LINKCHANGE_SPEED_10G] = 10000, | ||
472 | }; | ||
473 | |||
474 | |||
475 | static void efx_mcdi_process_link_change(struct efx_nic *efx, efx_qword_t *ev) | ||
476 | { | ||
477 | u32 flags, fcntl, speed, lpa; | ||
478 | |||
479 | speed = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_SPEED); | ||
480 | EFX_BUG_ON_PARANOID(speed >= ARRAY_SIZE(efx_mcdi_event_link_speed)); | ||
481 | speed = efx_mcdi_event_link_speed[speed]; | ||
482 | |||
483 | flags = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LINK_FLAGS); | ||
484 | fcntl = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_FCNTL); | ||
485 | lpa = EFX_QWORD_FIELD(*ev, MCDI_EVENT_LINKCHANGE_LP_CAP); | ||
486 | |||
487 | /* efx->link_state is only modified by efx_mcdi_phy_get_link(), | ||
488 | * which is only run after flushing the event queues. Therefore, it | ||
489 | * is safe to modify the link state outside of the mac_lock here. | ||
490 | */ | ||
491 | efx_mcdi_phy_decode_link(efx, &efx->link_state, speed, flags, fcntl); | ||
492 | |||
493 | efx_mcdi_phy_check_fcntl(efx, lpa); | ||
494 | |||
495 | efx_link_status_changed(efx); | ||
496 | } | ||
497 | |||
498 | static const char *sensor_names[] = { | ||
499 | [MC_CMD_SENSOR_CONTROLLER_TEMP] = "Controller temp. sensor", | ||
500 | [MC_CMD_SENSOR_PHY_COMMON_TEMP] = "PHY shared temp. sensor", | ||
501 | [MC_CMD_SENSOR_CONTROLLER_COOLING] = "Controller cooling", | ||
502 | [MC_CMD_SENSOR_PHY0_TEMP] = "PHY 0 temp. sensor", | ||
503 | [MC_CMD_SENSOR_PHY0_COOLING] = "PHY 0 cooling", | ||
504 | [MC_CMD_SENSOR_PHY1_TEMP] = "PHY 1 temp. sensor", | ||
505 | [MC_CMD_SENSOR_PHY1_COOLING] = "PHY 1 cooling", | ||
506 | [MC_CMD_SENSOR_IN_1V0] = "1.0V supply sensor", | ||
507 | [MC_CMD_SENSOR_IN_1V2] = "1.2V supply sensor", | ||
508 | [MC_CMD_SENSOR_IN_1V8] = "1.8V supply sensor", | ||
509 | [MC_CMD_SENSOR_IN_2V5] = "2.5V supply sensor", | ||
510 | [MC_CMD_SENSOR_IN_3V3] = "3.3V supply sensor", | ||
511 | [MC_CMD_SENSOR_IN_12V0] = "12V supply sensor" | ||
512 | }; | ||
513 | |||
514 | static const char *sensor_status_names[] = { | ||
515 | [MC_CMD_SENSOR_STATE_OK] = "OK", | ||
516 | [MC_CMD_SENSOR_STATE_WARNING] = "Warning", | ||
517 | [MC_CMD_SENSOR_STATE_FATAL] = "Fatal", | ||
518 | [MC_CMD_SENSOR_STATE_BROKEN] = "Device failure", | ||
519 | }; | ||
520 | |||
521 | static void efx_mcdi_sensor_event(struct efx_nic *efx, efx_qword_t *ev) | ||
522 | { | ||
523 | unsigned int monitor, state, value; | ||
524 | const char *name, *state_txt; | ||
525 | monitor = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_MONITOR); | ||
526 | state = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_STATE); | ||
527 | value = EFX_QWORD_FIELD(*ev, MCDI_EVENT_SENSOREVT_VALUE); | ||
528 | /* Deal gracefully with the board having more drivers than we | ||
529 | * know about, but do not expect new sensor states. */ | ||
530 | name = (monitor >= ARRAY_SIZE(sensor_names)) | ||
531 | ? "No sensor name available" : | ||
532 | sensor_names[monitor]; | ||
533 | EFX_BUG_ON_PARANOID(state >= ARRAY_SIZE(sensor_status_names)); | ||
534 | state_txt = sensor_status_names[state]; | ||
535 | |||
536 | EFX_ERR(efx, "Sensor %d (%s) reports condition '%s' for raw value %d\n", | ||
537 | monitor, name, state_txt, value); | ||
538 | } | ||
539 | |||
540 | /* Called from falcon_process_eventq for MCDI events */ | ||
541 | void efx_mcdi_process_event(struct efx_channel *channel, | ||
542 | efx_qword_t *event) | ||
543 | { | ||
544 | struct efx_nic *efx = channel->efx; | ||
545 | int code = EFX_QWORD_FIELD(*event, MCDI_EVENT_CODE); | ||
546 | u32 data = EFX_QWORD_FIELD(*event, MCDI_EVENT_DATA); | ||
547 | |||
548 | switch (code) { | ||
549 | case MCDI_EVENT_CODE_BADSSERT: | ||
550 | EFX_ERR(efx, "MC watchdog or assertion failure at 0x%x\n", data); | ||
551 | efx_mcdi_ev_death(efx, EINTR); | ||
552 | break; | ||
553 | |||
554 | case MCDI_EVENT_CODE_PMNOTICE: | ||
555 | EFX_INFO(efx, "MCDI PM event.\n"); | ||
556 | break; | ||
557 | |||
558 | case MCDI_EVENT_CODE_CMDDONE: | ||
559 | efx_mcdi_ev_cpl(efx, | ||
560 | MCDI_EVENT_FIELD(*event, CMDDONE_SEQ), | ||
561 | MCDI_EVENT_FIELD(*event, CMDDONE_DATALEN), | ||
562 | MCDI_EVENT_FIELD(*event, CMDDONE_ERRNO)); | ||
563 | break; | ||
564 | |||
565 | case MCDI_EVENT_CODE_LINKCHANGE: | ||
566 | efx_mcdi_process_link_change(efx, event); | ||
567 | break; | ||
568 | case MCDI_EVENT_CODE_SENSOREVT: | ||
569 | efx_mcdi_sensor_event(efx, event); | ||
570 | break; | ||
571 | case MCDI_EVENT_CODE_SCHEDERR: | ||
572 | EFX_INFO(efx, "MC Scheduler error address=0x%x\n", data); | ||
573 | break; | ||
574 | case MCDI_EVENT_CODE_REBOOT: | ||
575 | EFX_INFO(efx, "MC Reboot\n"); | ||
576 | efx_mcdi_ev_death(efx, EIO); | ||
577 | break; | ||
578 | case MCDI_EVENT_CODE_MAC_STATS_DMA: | ||
579 | /* MAC stats are gather lazily. We can ignore this. */ | ||
580 | break; | ||
581 | |||
582 | default: | ||
583 | EFX_ERR(efx, "Unknown MCDI event 0x%x\n", code); | ||
584 | } | ||
585 | } | ||
586 | |||
587 | /************************************************************************** | ||
588 | * | ||
589 | * Specific request functions | ||
590 | * | ||
591 | ************************************************************************** | ||
592 | */ | ||
593 | |||
594 | int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build) | ||
595 | { | ||
596 | u8 outbuf[ALIGN(MC_CMD_GET_VERSION_V1_OUT_LEN, 4)]; | ||
597 | size_t outlength; | ||
598 | const __le16 *ver_words; | ||
599 | int rc; | ||
600 | |||
601 | BUILD_BUG_ON(MC_CMD_GET_VERSION_IN_LEN != 0); | ||
602 | |||
603 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_VERSION, NULL, 0, | ||
604 | outbuf, sizeof(outbuf), &outlength); | ||
605 | if (rc) | ||
606 | goto fail; | ||
607 | |||
608 | if (outlength == MC_CMD_GET_VERSION_V0_OUT_LEN) { | ||
609 | *version = 0; | ||
610 | *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | if (outlength < MC_CMD_GET_VERSION_V1_OUT_LEN) { | ||
615 | rc = -EMSGSIZE; | ||
616 | goto fail; | ||
617 | } | ||
618 | |||
619 | ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION); | ||
620 | *version = (((u64)le16_to_cpu(ver_words[0]) << 48) | | ||
621 | ((u64)le16_to_cpu(ver_words[1]) << 32) | | ||
622 | ((u64)le16_to_cpu(ver_words[2]) << 16) | | ||
623 | le16_to_cpu(ver_words[3])); | ||
624 | *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); | ||
625 | |||
626 | return 0; | ||
627 | |||
628 | fail: | ||
629 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
630 | return rc; | ||
631 | } | ||
632 | |||
633 | int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, | ||
634 | bool *was_attached) | ||
635 | { | ||
636 | u8 inbuf[MC_CMD_DRV_ATTACH_IN_LEN]; | ||
637 | u8 outbuf[MC_CMD_DRV_ATTACH_OUT_LEN]; | ||
638 | size_t outlen; | ||
639 | int rc; | ||
640 | |||
641 | MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_NEW_STATE, | ||
642 | driver_operating ? 1 : 0); | ||
643 | MCDI_SET_DWORD(inbuf, DRV_ATTACH_IN_UPDATE, 1); | ||
644 | |||
645 | rc = efx_mcdi_rpc(efx, MC_CMD_DRV_ATTACH, inbuf, sizeof(inbuf), | ||
646 | outbuf, sizeof(outbuf), &outlen); | ||
647 | if (rc) | ||
648 | goto fail; | ||
649 | if (outlen < MC_CMD_DRV_ATTACH_OUT_LEN) | ||
650 | goto fail; | ||
651 | |||
652 | if (was_attached != NULL) | ||
653 | *was_attached = MCDI_DWORD(outbuf, DRV_ATTACH_OUT_OLD_STATE); | ||
654 | return 0; | ||
655 | |||
656 | fail: | ||
657 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
658 | return rc; | ||
659 | } | ||
660 | |||
661 | int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, | ||
662 | u16 *fw_subtype_list) | ||
663 | { | ||
664 | uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LEN]; | ||
665 | size_t outlen; | ||
666 | int port_num = efx_port_num(efx); | ||
667 | int offset; | ||
668 | int rc; | ||
669 | |||
670 | BUILD_BUG_ON(MC_CMD_GET_BOARD_CFG_IN_LEN != 0); | ||
671 | |||
672 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_BOARD_CFG, NULL, 0, | ||
673 | outbuf, sizeof(outbuf), &outlen); | ||
674 | if (rc) | ||
675 | goto fail; | ||
676 | |||
677 | if (outlen < MC_CMD_GET_BOARD_CFG_OUT_LEN) { | ||
678 | rc = -EMSGSIZE; | ||
679 | goto fail; | ||
680 | } | ||
681 | |||
682 | offset = (port_num) | ||
683 | ? MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT1_OFST | ||
684 | : MC_CMD_GET_BOARD_CFG_OUT_MAC_ADDR_BASE_PORT0_OFST; | ||
685 | if (mac_address) | ||
686 | memcpy(mac_address, outbuf + offset, ETH_ALEN); | ||
687 | if (fw_subtype_list) | ||
688 | memcpy(fw_subtype_list, | ||
689 | outbuf + MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST, | ||
690 | MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_LEN); | ||
691 | |||
692 | return 0; | ||
693 | |||
694 | fail: | ||
695 | EFX_ERR(efx, "%s: failed rc=%d len=%d\n", __func__, rc, (int)outlen); | ||
696 | |||
697 | return rc; | ||
698 | } | ||
699 | |||
700 | int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, u32 dest_evq) | ||
701 | { | ||
702 | u8 inbuf[MC_CMD_LOG_CTRL_IN_LEN]; | ||
703 | u32 dest = 0; | ||
704 | int rc; | ||
705 | |||
706 | if (uart) | ||
707 | dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_UART; | ||
708 | if (evq) | ||
709 | dest |= MC_CMD_LOG_CTRL_IN_LOG_DEST_EVQ; | ||
710 | |||
711 | MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST, dest); | ||
712 | MCDI_SET_DWORD(inbuf, LOG_CTRL_IN_LOG_DEST_EVQ, dest_evq); | ||
713 | |||
714 | BUILD_BUG_ON(MC_CMD_LOG_CTRL_OUT_LEN != 0); | ||
715 | |||
716 | rc = efx_mcdi_rpc(efx, MC_CMD_LOG_CTRL, inbuf, sizeof(inbuf), | ||
717 | NULL, 0, NULL); | ||
718 | if (rc) | ||
719 | goto fail; | ||
720 | |||
721 | return 0; | ||
722 | |||
723 | fail: | ||
724 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
725 | return rc; | ||
726 | } | ||
727 | |||
728 | int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out) | ||
729 | { | ||
730 | u8 outbuf[MC_CMD_NVRAM_TYPES_OUT_LEN]; | ||
731 | size_t outlen; | ||
732 | int rc; | ||
733 | |||
734 | BUILD_BUG_ON(MC_CMD_NVRAM_TYPES_IN_LEN != 0); | ||
735 | |||
736 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_TYPES, NULL, 0, | ||
737 | outbuf, sizeof(outbuf), &outlen); | ||
738 | if (rc) | ||
739 | goto fail; | ||
740 | if (outlen < MC_CMD_NVRAM_TYPES_OUT_LEN) | ||
741 | goto fail; | ||
742 | |||
743 | *nvram_types_out = MCDI_DWORD(outbuf, NVRAM_TYPES_OUT_TYPES); | ||
744 | return 0; | ||
745 | |||
746 | fail: | ||
747 | EFX_ERR(efx, "%s: failed rc=%d\n", | ||
748 | __func__, rc); | ||
749 | return rc; | ||
750 | } | ||
751 | |||
752 | int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, | ||
753 | size_t *size_out, size_t *erase_size_out, | ||
754 | bool *protected_out) | ||
755 | { | ||
756 | u8 inbuf[MC_CMD_NVRAM_INFO_IN_LEN]; | ||
757 | u8 outbuf[MC_CMD_NVRAM_INFO_OUT_LEN]; | ||
758 | size_t outlen; | ||
759 | int rc; | ||
760 | |||
761 | MCDI_SET_DWORD(inbuf, NVRAM_INFO_IN_TYPE, type); | ||
762 | |||
763 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_INFO, inbuf, sizeof(inbuf), | ||
764 | outbuf, sizeof(outbuf), &outlen); | ||
765 | if (rc) | ||
766 | goto fail; | ||
767 | if (outlen < MC_CMD_NVRAM_INFO_OUT_LEN) | ||
768 | goto fail; | ||
769 | |||
770 | *size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_SIZE); | ||
771 | *erase_size_out = MCDI_DWORD(outbuf, NVRAM_INFO_OUT_ERASESIZE); | ||
772 | *protected_out = !!(MCDI_DWORD(outbuf, NVRAM_INFO_OUT_FLAGS) & | ||
773 | (1 << MC_CMD_NVRAM_PROTECTED_LBN)); | ||
774 | return 0; | ||
775 | |||
776 | fail: | ||
777 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
778 | return rc; | ||
779 | } | ||
780 | |||
781 | int efx_mcdi_nvram_update_start(struct efx_nic *efx, unsigned int type) | ||
782 | { | ||
783 | u8 inbuf[MC_CMD_NVRAM_UPDATE_START_IN_LEN]; | ||
784 | int rc; | ||
785 | |||
786 | MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_START_IN_TYPE, type); | ||
787 | |||
788 | BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_START_OUT_LEN != 0); | ||
789 | |||
790 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_START, inbuf, sizeof(inbuf), | ||
791 | NULL, 0, NULL); | ||
792 | if (rc) | ||
793 | goto fail; | ||
794 | |||
795 | return 0; | ||
796 | |||
797 | fail: | ||
798 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
799 | return rc; | ||
800 | } | ||
801 | |||
802 | int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, | ||
803 | loff_t offset, u8 *buffer, size_t length) | ||
804 | { | ||
805 | u8 inbuf[MC_CMD_NVRAM_READ_IN_LEN]; | ||
806 | u8 outbuf[MC_CMD_NVRAM_READ_OUT_LEN(length)]; | ||
807 | size_t outlen; | ||
808 | int rc; | ||
809 | |||
810 | MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_TYPE, type); | ||
811 | MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_OFFSET, offset); | ||
812 | MCDI_SET_DWORD(inbuf, NVRAM_READ_IN_LENGTH, length); | ||
813 | |||
814 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_READ, inbuf, sizeof(inbuf), | ||
815 | outbuf, sizeof(outbuf), &outlen); | ||
816 | if (rc) | ||
817 | goto fail; | ||
818 | |||
819 | memcpy(buffer, MCDI_PTR(outbuf, NVRAM_READ_OUT_READ_BUFFER), length); | ||
820 | return 0; | ||
821 | |||
822 | fail: | ||
823 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
824 | return rc; | ||
825 | } | ||
826 | |||
827 | int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, | ||
828 | loff_t offset, const u8 *buffer, size_t length) | ||
829 | { | ||
830 | u8 inbuf[MC_CMD_NVRAM_WRITE_IN_LEN(length)]; | ||
831 | int rc; | ||
832 | |||
833 | MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_TYPE, type); | ||
834 | MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_OFFSET, offset); | ||
835 | MCDI_SET_DWORD(inbuf, NVRAM_WRITE_IN_LENGTH, length); | ||
836 | memcpy(MCDI_PTR(inbuf, NVRAM_WRITE_IN_WRITE_BUFFER), buffer, length); | ||
837 | |||
838 | BUILD_BUG_ON(MC_CMD_NVRAM_WRITE_OUT_LEN != 0); | ||
839 | |||
840 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_WRITE, inbuf, sizeof(inbuf), | ||
841 | NULL, 0, NULL); | ||
842 | if (rc) | ||
843 | goto fail; | ||
844 | |||
845 | return 0; | ||
846 | |||
847 | fail: | ||
848 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
849 | return rc; | ||
850 | } | ||
851 | |||
852 | int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, | ||
853 | loff_t offset, size_t length) | ||
854 | { | ||
855 | u8 inbuf[MC_CMD_NVRAM_ERASE_IN_LEN]; | ||
856 | int rc; | ||
857 | |||
858 | MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_TYPE, type); | ||
859 | MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_OFFSET, offset); | ||
860 | MCDI_SET_DWORD(inbuf, NVRAM_ERASE_IN_LENGTH, length); | ||
861 | |||
862 | BUILD_BUG_ON(MC_CMD_NVRAM_ERASE_OUT_LEN != 0); | ||
863 | |||
864 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_ERASE, inbuf, sizeof(inbuf), | ||
865 | NULL, 0, NULL); | ||
866 | if (rc) | ||
867 | goto fail; | ||
868 | |||
869 | return 0; | ||
870 | |||
871 | fail: | ||
872 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
873 | return rc; | ||
874 | } | ||
875 | |||
876 | int efx_mcdi_nvram_update_finish(struct efx_nic *efx, unsigned int type) | ||
877 | { | ||
878 | u8 inbuf[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN]; | ||
879 | int rc; | ||
880 | |||
881 | MCDI_SET_DWORD(inbuf, NVRAM_UPDATE_FINISH_IN_TYPE, type); | ||
882 | |||
883 | BUILD_BUG_ON(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN != 0); | ||
884 | |||
885 | rc = efx_mcdi_rpc(efx, MC_CMD_NVRAM_UPDATE_FINISH, inbuf, sizeof(inbuf), | ||
886 | NULL, 0, NULL); | ||
887 | if (rc) | ||
888 | goto fail; | ||
889 | |||
890 | return 0; | ||
891 | |||
892 | fail: | ||
893 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
894 | return rc; | ||
895 | } | ||
896 | |||
897 | int efx_mcdi_handle_assertion(struct efx_nic *efx) | ||
898 | { | ||
899 | union { | ||
900 | u8 asserts[MC_CMD_GET_ASSERTS_IN_LEN]; | ||
901 | u8 reboot[MC_CMD_REBOOT_IN_LEN]; | ||
902 | } inbuf; | ||
903 | u8 assertion[MC_CMD_GET_ASSERTS_OUT_LEN]; | ||
904 | unsigned int flags, index, ofst; | ||
905 | const char *reason; | ||
906 | size_t outlen; | ||
907 | int retry; | ||
908 | int rc; | ||
909 | |||
910 | /* Check if the MC is in the assertion handler, retrying twice. Once | ||
911 | * because a boot-time assertion might cause this command to fail | ||
912 | * with EINTR. And once again because GET_ASSERTS can race with | ||
913 | * MC_CMD_REBOOT running on the other port. */ | ||
914 | retry = 2; | ||
915 | do { | ||
916 | MCDI_SET_DWORD(inbuf.asserts, GET_ASSERTS_IN_CLEAR, 0); | ||
917 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_ASSERTS, | ||
918 | inbuf.asserts, MC_CMD_GET_ASSERTS_IN_LEN, | ||
919 | assertion, sizeof(assertion), &outlen); | ||
920 | } while ((rc == -EINTR || rc == -EIO) && retry-- > 0); | ||
921 | |||
922 | if (rc) | ||
923 | return rc; | ||
924 | if (outlen < MC_CMD_GET_ASSERTS_OUT_LEN) | ||
925 | return -EINVAL; | ||
926 | |||
927 | flags = MCDI_DWORD(assertion, GET_ASSERTS_OUT_GLOBAL_FLAGS); | ||
928 | if (flags == MC_CMD_GET_ASSERTS_FLAGS_NO_FAILS) | ||
929 | return 0; | ||
930 | |||
931 | /* Reset the hardware atomically such that only one port with succeed. | ||
932 | * This command will succeed if a reboot is no longer required (because | ||
933 | * the other port did it first), but fail with EIO if it succeeds. | ||
934 | */ | ||
935 | BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); | ||
936 | MCDI_SET_DWORD(inbuf.reboot, REBOOT_IN_FLAGS, | ||
937 | MC_CMD_REBOOT_FLAGS_AFTER_ASSERTION); | ||
938 | efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf.reboot, MC_CMD_REBOOT_IN_LEN, | ||
939 | NULL, 0, NULL); | ||
940 | |||
941 | /* Print out the assertion */ | ||
942 | reason = (flags == MC_CMD_GET_ASSERTS_FLAGS_SYS_FAIL) | ||
943 | ? "system-level assertion" | ||
944 | : (flags == MC_CMD_GET_ASSERTS_FLAGS_THR_FAIL) | ||
945 | ? "thread-level assertion" | ||
946 | : (flags == MC_CMD_GET_ASSERTS_FLAGS_WDOG_FIRED) | ||
947 | ? "watchdog reset" | ||
948 | : "unknown assertion"; | ||
949 | EFX_ERR(efx, "MCPU %s at PC = 0x%.8x in thread 0x%.8x\n", reason, | ||
950 | MCDI_DWORD(assertion, GET_ASSERTS_OUT_SAVED_PC_OFFS), | ||
951 | MCDI_DWORD(assertion, GET_ASSERTS_OUT_THREAD_OFFS)); | ||
952 | |||
953 | /* Print out the registers */ | ||
954 | ofst = MC_CMD_GET_ASSERTS_OUT_GP_REGS_OFFS_OFST; | ||
955 | for (index = 1; index < 32; index++) { | ||
956 | EFX_ERR(efx, "R%.2d (?): 0x%.8x\n", index, | ||
957 | MCDI_DWORD2(assertion, ofst)); | ||
958 | ofst += sizeof(efx_dword_t); | ||
959 | } | ||
960 | |||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode) | ||
965 | { | ||
966 | u8 inbuf[MC_CMD_SET_ID_LED_IN_LEN]; | ||
967 | int rc; | ||
968 | |||
969 | BUILD_BUG_ON(EFX_LED_OFF != MC_CMD_LED_OFF); | ||
970 | BUILD_BUG_ON(EFX_LED_ON != MC_CMD_LED_ON); | ||
971 | BUILD_BUG_ON(EFX_LED_DEFAULT != MC_CMD_LED_DEFAULT); | ||
972 | |||
973 | BUILD_BUG_ON(MC_CMD_SET_ID_LED_OUT_LEN != 0); | ||
974 | |||
975 | MCDI_SET_DWORD(inbuf, SET_ID_LED_IN_STATE, mode); | ||
976 | |||
977 | rc = efx_mcdi_rpc(efx, MC_CMD_SET_ID_LED, inbuf, sizeof(inbuf), | ||
978 | NULL, 0, NULL); | ||
979 | if (rc) | ||
980 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
981 | } | ||
982 | |||
983 | int efx_mcdi_reset_port(struct efx_nic *efx) | ||
984 | { | ||
985 | int rc = efx_mcdi_rpc(efx, MC_CMD_PORT_RESET, NULL, 0, NULL, 0, NULL); | ||
986 | if (rc) | ||
987 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
988 | return rc; | ||
989 | } | ||
990 | |||
991 | int efx_mcdi_reset_mc(struct efx_nic *efx) | ||
992 | { | ||
993 | u8 inbuf[MC_CMD_REBOOT_IN_LEN]; | ||
994 | int rc; | ||
995 | |||
996 | BUILD_BUG_ON(MC_CMD_REBOOT_OUT_LEN != 0); | ||
997 | MCDI_SET_DWORD(inbuf, REBOOT_IN_FLAGS, 0); | ||
998 | rc = efx_mcdi_rpc(efx, MC_CMD_REBOOT, inbuf, sizeof(inbuf), | ||
999 | NULL, 0, NULL); | ||
1000 | /* White is black, and up is down */ | ||
1001 | if (rc == -EIO) | ||
1002 | return 0; | ||
1003 | if (rc == 0) | ||
1004 | rc = -EIO; | ||
1005 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
1006 | return rc; | ||
1007 | } | ||
1008 | |||
1009 | int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, | ||
1010 | const u8 *mac, int *id_out) | ||
1011 | { | ||
1012 | u8 inbuf[MC_CMD_WOL_FILTER_SET_IN_LEN]; | ||
1013 | u8 outbuf[MC_CMD_WOL_FILTER_SET_OUT_LEN]; | ||
1014 | size_t outlen; | ||
1015 | int rc; | ||
1016 | |||
1017 | MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_WOL_TYPE, type); | ||
1018 | MCDI_SET_DWORD(inbuf, WOL_FILTER_SET_IN_FILTER_MODE, | ||
1019 | MC_CMD_FILTER_MODE_SIMPLE); | ||
1020 | memcpy(MCDI_PTR(inbuf, WOL_FILTER_SET_IN_MAGIC_MAC), mac, ETH_ALEN); | ||
1021 | |||
1022 | rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_SET, inbuf, sizeof(inbuf), | ||
1023 | outbuf, sizeof(outbuf), &outlen); | ||
1024 | if (rc) | ||
1025 | goto fail; | ||
1026 | |||
1027 | if (outlen < MC_CMD_WOL_FILTER_SET_OUT_LEN) { | ||
1028 | rc = -EMSGSIZE; | ||
1029 | goto fail; | ||
1030 | } | ||
1031 | |||
1032 | *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_SET_OUT_FILTER_ID); | ||
1033 | |||
1034 | return 0; | ||
1035 | |||
1036 | fail: | ||
1037 | *id_out = -1; | ||
1038 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
1039 | return rc; | ||
1040 | |||
1041 | } | ||
1042 | |||
1043 | |||
1044 | int | ||
1045 | efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, const u8 *mac, int *id_out) | ||
1046 | { | ||
1047 | return efx_mcdi_wol_filter_set(efx, MC_CMD_WOL_TYPE_MAGIC, mac, id_out); | ||
1048 | } | ||
1049 | |||
1050 | |||
1051 | int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out) | ||
1052 | { | ||
1053 | u8 outbuf[MC_CMD_WOL_FILTER_GET_OUT_LEN]; | ||
1054 | size_t outlen; | ||
1055 | int rc; | ||
1056 | |||
1057 | rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_GET, NULL, 0, | ||
1058 | outbuf, sizeof(outbuf), &outlen); | ||
1059 | if (rc) | ||
1060 | goto fail; | ||
1061 | |||
1062 | if (outlen < MC_CMD_WOL_FILTER_GET_OUT_LEN) { | ||
1063 | rc = -EMSGSIZE; | ||
1064 | goto fail; | ||
1065 | } | ||
1066 | |||
1067 | *id_out = (int)MCDI_DWORD(outbuf, WOL_FILTER_GET_OUT_FILTER_ID); | ||
1068 | |||
1069 | return 0; | ||
1070 | |||
1071 | fail: | ||
1072 | *id_out = -1; | ||
1073 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
1074 | return rc; | ||
1075 | } | ||
1076 | |||
1077 | |||
1078 | int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id) | ||
1079 | { | ||
1080 | u8 inbuf[MC_CMD_WOL_FILTER_REMOVE_IN_LEN]; | ||
1081 | int rc; | ||
1082 | |||
1083 | MCDI_SET_DWORD(inbuf, WOL_FILTER_REMOVE_IN_FILTER_ID, (u32)id); | ||
1084 | |||
1085 | rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_REMOVE, inbuf, sizeof(inbuf), | ||
1086 | NULL, 0, NULL); | ||
1087 | if (rc) | ||
1088 | goto fail; | ||
1089 | |||
1090 | return 0; | ||
1091 | |||
1092 | fail: | ||
1093 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
1094 | return rc; | ||
1095 | } | ||
1096 | |||
1097 | |||
1098 | int efx_mcdi_wol_filter_reset(struct efx_nic *efx) | ||
1099 | { | ||
1100 | int rc; | ||
1101 | |||
1102 | rc = efx_mcdi_rpc(efx, MC_CMD_WOL_FILTER_RESET, NULL, 0, NULL, 0, NULL); | ||
1103 | if (rc) | ||
1104 | goto fail; | ||
1105 | |||
1106 | return 0; | ||
1107 | |||
1108 | fail: | ||
1109 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
1110 | return rc; | ||
1111 | } | ||
1112 | |||
diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/sfc/mcdi.h new file mode 100644 index 000000000000..de916728c2e3 --- /dev/null +++ b/drivers/net/sfc/mcdi.h | |||
@@ -0,0 +1,130 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2008-2009 Solarflare Communications Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation, incorporated herein by reference. | ||
8 | */ | ||
9 | |||
10 | #ifndef EFX_MCDI_H | ||
11 | #define EFX_MCDI_H | ||
12 | |||
13 | /** | ||
14 | * enum efx_mcdi_state | ||
15 | * @MCDI_STATE_QUIESCENT: No pending MCDI requests. If the caller holds the | ||
16 | * mcdi_lock then they are able to move to MCDI_STATE_RUNNING | ||
17 | * @MCDI_STATE_RUNNING: There is an MCDI request pending. Only the thread that | ||
18 | * moved into this state is allowed to move out of it. | ||
19 | * @MCDI_STATE_COMPLETED: An MCDI request has completed, but the owning thread | ||
20 | * has not yet consumed the result. For all other threads, equivalent to | ||
21 | * MCDI_STATE_RUNNING. | ||
22 | */ | ||
23 | enum efx_mcdi_state { | ||
24 | MCDI_STATE_QUIESCENT, | ||
25 | MCDI_STATE_RUNNING, | ||
26 | MCDI_STATE_COMPLETED, | ||
27 | }; | ||
28 | |||
29 | enum efx_mcdi_mode { | ||
30 | MCDI_MODE_POLL, | ||
31 | MCDI_MODE_EVENTS, | ||
32 | }; | ||
33 | |||
34 | /** | ||
35 | * struct efx_mcdi_iface | ||
36 | * @state: Interface state. Waited for by mcdi_wq. | ||
37 | * @wq: Wait queue for threads waiting for state != STATE_RUNNING | ||
38 | * @iface_lock: Protects @credits, @seqno, @resprc, @resplen | ||
39 | * @mode: Poll for mcdi completion, or wait for an mcdi_event. | ||
40 | * Serialised by @lock | ||
41 | * @seqno: The next sequence number to use for mcdi requests. | ||
42 | * Serialised by @lock | ||
43 | * @credits: Number of spurious MCDI completion events allowed before we | ||
44 | * trigger a fatal error. Protected by @lock | ||
45 | * @resprc: Returned MCDI completion | ||
46 | * @resplen: Returned payload length | ||
47 | */ | ||
48 | struct efx_mcdi_iface { | ||
49 | atomic_t state; | ||
50 | wait_queue_head_t wq; | ||
51 | spinlock_t iface_lock; | ||
52 | enum efx_mcdi_mode mode; | ||
53 | unsigned int credits; | ||
54 | unsigned int seqno; | ||
55 | unsigned int resprc; | ||
56 | size_t resplen; | ||
57 | }; | ||
58 | |||
59 | extern void efx_mcdi_init(struct efx_nic *efx); | ||
60 | |||
61 | extern int efx_mcdi_rpc(struct efx_nic *efx, unsigned cmd, const u8 *inbuf, | ||
62 | size_t inlen, u8 *outbuf, size_t outlen, | ||
63 | size_t *outlen_actual); | ||
64 | |||
65 | extern int efx_mcdi_poll_reboot(struct efx_nic *efx); | ||
66 | extern void efx_mcdi_mode_poll(struct efx_nic *efx); | ||
67 | extern void efx_mcdi_mode_event(struct efx_nic *efx); | ||
68 | |||
69 | extern void efx_mcdi_process_event(struct efx_channel *channel, | ||
70 | efx_qword_t *event); | ||
71 | |||
72 | #define MCDI_PTR2(_buf, _ofst) \ | ||
73 | (((u8 *)_buf) + _ofst) | ||
74 | #define MCDI_SET_DWORD2(_buf, _ofst, _value) \ | ||
75 | EFX_POPULATE_DWORD_1(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ | ||
76 | EFX_DWORD_0, _value) | ||
77 | #define MCDI_DWORD2(_buf, _ofst) \ | ||
78 | EFX_DWORD_FIELD(*((efx_dword_t *)MCDI_PTR2(_buf, _ofst)), \ | ||
79 | EFX_DWORD_0) | ||
80 | #define MCDI_QWORD2(_buf, _ofst) \ | ||
81 | EFX_QWORD_FIELD64(*((efx_qword_t *)MCDI_PTR2(_buf, _ofst)), \ | ||
82 | EFX_QWORD_0) | ||
83 | |||
84 | #define MCDI_PTR(_buf, _ofst) \ | ||
85 | MCDI_PTR2(_buf, MC_CMD_ ## _ofst ## _OFST) | ||
86 | #define MCDI_SET_DWORD(_buf, _ofst, _value) \ | ||
87 | MCDI_SET_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST, _value) | ||
88 | #define MCDI_DWORD(_buf, _ofst) \ | ||
89 | MCDI_DWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) | ||
90 | #define MCDI_QWORD(_buf, _ofst) \ | ||
91 | MCDI_QWORD2(_buf, MC_CMD_ ## _ofst ## _OFST) | ||
92 | |||
93 | #define MCDI_EVENT_FIELD(_ev, _field) \ | ||
94 | EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) | ||
95 | |||
96 | extern int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build); | ||
97 | extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, | ||
98 | bool *was_attached_out); | ||
99 | extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, | ||
100 | u16 *fw_subtype_list); | ||
101 | extern int efx_mcdi_log_ctrl(struct efx_nic *efx, bool evq, bool uart, | ||
102 | u32 dest_evq); | ||
103 | extern int efx_mcdi_nvram_types(struct efx_nic *efx, u32 *nvram_types_out); | ||
104 | extern int efx_mcdi_nvram_info(struct efx_nic *efx, unsigned int type, | ||
105 | size_t *size_out, size_t *erase_size_out, | ||
106 | bool *protected_out); | ||
107 | extern int efx_mcdi_nvram_update_start(struct efx_nic *efx, | ||
108 | unsigned int type); | ||
109 | extern int efx_mcdi_nvram_read(struct efx_nic *efx, unsigned int type, | ||
110 | loff_t offset, u8 *buffer, size_t length); | ||
111 | extern int efx_mcdi_nvram_write(struct efx_nic *efx, unsigned int type, | ||
112 | loff_t offset, const u8 *buffer, | ||
113 | size_t length); | ||
114 | extern int efx_mcdi_nvram_erase(struct efx_nic *efx, unsigned int type, | ||
115 | loff_t offset, size_t length); | ||
116 | extern int efx_mcdi_nvram_update_finish(struct efx_nic *efx, | ||
117 | unsigned int type); | ||
118 | extern int efx_mcdi_handle_assertion(struct efx_nic *efx); | ||
119 | extern void efx_mcdi_set_id_led(struct efx_nic *efx, enum efx_led_mode mode); | ||
120 | extern int efx_mcdi_reset_port(struct efx_nic *efx); | ||
121 | extern int efx_mcdi_reset_mc(struct efx_nic *efx); | ||
122 | extern int efx_mcdi_wol_filter_set(struct efx_nic *efx, u32 type, | ||
123 | const u8 *mac, int *id_out); | ||
124 | extern int efx_mcdi_wol_filter_set_magic(struct efx_nic *efx, | ||
125 | const u8 *mac, int *id_out); | ||
126 | extern int efx_mcdi_wol_filter_get_magic(struct efx_nic *efx, int *id_out); | ||
127 | extern int efx_mcdi_wol_filter_remove(struct efx_nic *efx, int id); | ||
128 | extern int efx_mcdi_wol_filter_reset(struct efx_nic *efx); | ||
129 | |||
130 | #endif /* EFX_MCDI_H */ | ||
diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/sfc/mcdi_mac.c new file mode 100644 index 000000000000..06d24a1e412a --- /dev/null +++ b/drivers/net/sfc/mcdi_mac.c | |||
@@ -0,0 +1,152 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2009 Solarflare Communications Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation, incorporated herein by reference. | ||
8 | */ | ||
9 | |||
10 | #include "net_driver.h" | ||
11 | #include "efx.h" | ||
12 | #include "mac.h" | ||
13 | #include "mcdi.h" | ||
14 | #include "mcdi_pcol.h" | ||
15 | |||
16 | static int efx_mcdi_set_mac(struct efx_nic *efx) | ||
17 | { | ||
18 | u32 reject, fcntl; | ||
19 | u8 cmdbytes[MC_CMD_SET_MAC_IN_LEN]; | ||
20 | |||
21 | memcpy(cmdbytes + MC_CMD_SET_MAC_IN_ADDR_OFST, | ||
22 | efx->net_dev->dev_addr, ETH_ALEN); | ||
23 | |||
24 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_MTU, | ||
25 | EFX_MAX_FRAME_LEN(efx->net_dev->mtu)); | ||
26 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_DRAIN, 0); | ||
27 | |||
28 | /* The MCDI command provides for controlling accept/reject | ||
29 | * of broadcast packets too, but the driver doesn't currently | ||
30 | * expose this. */ | ||
31 | reject = (efx->promiscuous) ? 0 : | ||
32 | (1 << MC_CMD_SET_MAC_IN_REJECT_UNCST_LBN); | ||
33 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_REJECT, reject); | ||
34 | |||
35 | switch (efx->wanted_fc) { | ||
36 | case EFX_FC_RX | EFX_FC_TX: | ||
37 | fcntl = MC_CMD_FCNTL_BIDIR; | ||
38 | break; | ||
39 | case EFX_FC_RX: | ||
40 | fcntl = MC_CMD_FCNTL_RESPOND; | ||
41 | break; | ||
42 | default: | ||
43 | fcntl = MC_CMD_FCNTL_OFF; | ||
44 | break; | ||
45 | } | ||
46 | if (efx->wanted_fc & EFX_FC_AUTO) | ||
47 | fcntl = MC_CMD_FCNTL_AUTO; | ||
48 | |||
49 | MCDI_SET_DWORD(cmdbytes, SET_MAC_IN_FCNTL, fcntl); | ||
50 | |||
51 | return efx_mcdi_rpc(efx, MC_CMD_SET_MAC, cmdbytes, sizeof(cmdbytes), | ||
52 | NULL, 0, NULL); | ||
53 | } | ||
54 | |||
55 | static int efx_mcdi_get_mac_faults(struct efx_nic *efx, u32 *faults) | ||
56 | { | ||
57 | u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; | ||
58 | size_t outlength; | ||
59 | int rc; | ||
60 | |||
61 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); | ||
62 | |||
63 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, | ||
64 | outbuf, sizeof(outbuf), &outlength); | ||
65 | if (rc) | ||
66 | goto fail; | ||
67 | |||
68 | *faults = MCDI_DWORD(outbuf, GET_LINK_OUT_MAC_FAULT); | ||
69 | return 0; | ||
70 | |||
71 | fail: | ||
72 | EFX_ERR(efx, "%s: failed rc=%d\n", | ||
73 | __func__, rc); | ||
74 | return rc; | ||
75 | } | ||
76 | |||
77 | int efx_mcdi_mac_stats(struct efx_nic *efx, dma_addr_t dma_addr, | ||
78 | u32 dma_len, int enable, int clear) | ||
79 | { | ||
80 | u8 inbuf[MC_CMD_MAC_STATS_IN_LEN]; | ||
81 | int rc; | ||
82 | efx_dword_t *cmd_ptr; | ||
83 | int period = 1000; | ||
84 | u32 addr_hi; | ||
85 | u32 addr_lo; | ||
86 | |||
87 | BUILD_BUG_ON(MC_CMD_MAC_STATS_OUT_LEN != 0); | ||
88 | |||
89 | addr_lo = ((u64)dma_addr) >> 0; | ||
90 | addr_hi = ((u64)dma_addr) >> 32; | ||
91 | |||
92 | MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_LO, addr_lo); | ||
93 | MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_ADDR_HI, addr_hi); | ||
94 | cmd_ptr = (efx_dword_t *)MCDI_PTR(inbuf, MAC_STATS_IN_CMD); | ||
95 | if (enable) | ||
96 | EFX_POPULATE_DWORD_6(*cmd_ptr, | ||
97 | MC_CMD_MAC_STATS_CMD_DMA, 1, | ||
98 | MC_CMD_MAC_STATS_CMD_CLEAR, clear, | ||
99 | MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, | ||
100 | MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 1, | ||
101 | MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0, | ||
102 | MC_CMD_MAC_STATS_CMD_PERIOD_MS, period); | ||
103 | else | ||
104 | EFX_POPULATE_DWORD_5(*cmd_ptr, | ||
105 | MC_CMD_MAC_STATS_CMD_DMA, 0, | ||
106 | MC_CMD_MAC_STATS_CMD_CLEAR, clear, | ||
107 | MC_CMD_MAC_STATS_CMD_PERIODIC_CHANGE, 1, | ||
108 | MC_CMD_MAC_STATS_CMD_PERIODIC_ENABLE, 0, | ||
109 | MC_CMD_MAC_STATS_CMD_PERIODIC_CLEAR, 0); | ||
110 | MCDI_SET_DWORD(inbuf, MAC_STATS_IN_DMA_LEN, dma_len); | ||
111 | |||
112 | rc = efx_mcdi_rpc(efx, MC_CMD_MAC_STATS, inbuf, sizeof(inbuf), | ||
113 | NULL, 0, NULL); | ||
114 | if (rc) | ||
115 | goto fail; | ||
116 | |||
117 | return 0; | ||
118 | |||
119 | fail: | ||
120 | EFX_ERR(efx, "%s: %s failed rc=%d\n", | ||
121 | __func__, enable ? "enable" : "disable", rc); | ||
122 | return rc; | ||
123 | } | ||
124 | |||
125 | static int efx_mcdi_mac_reconfigure(struct efx_nic *efx) | ||
126 | { | ||
127 | int rc; | ||
128 | |||
129 | rc = efx_mcdi_set_mac(efx); | ||
130 | if (rc != 0) | ||
131 | return rc; | ||
132 | |||
133 | /* Restore the multicast hash registers. */ | ||
134 | efx->type->push_multicast_hash(efx); | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | |||
140 | static bool efx_mcdi_mac_check_fault(struct efx_nic *efx) | ||
141 | { | ||
142 | u32 faults; | ||
143 | int rc = efx_mcdi_get_mac_faults(efx, &faults); | ||
144 | return (rc != 0) || (faults != 0); | ||
145 | } | ||
146 | |||
147 | |||
148 | struct efx_mac_operations efx_mcdi_mac_operations = { | ||
149 | .reconfigure = efx_mcdi_mac_reconfigure, | ||
150 | .update_stats = efx_port_dummy_op_void, | ||
151 | .check_fault = efx_mcdi_mac_check_fault, | ||
152 | }; | ||
diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c new file mode 100644 index 000000000000..0e1bcc5a0d52 --- /dev/null +++ b/drivers/net/sfc/mcdi_phy.c | |||
@@ -0,0 +1,597 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2009 Solarflare Communications Inc. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify it | ||
6 | * under the terms of the GNU General Public License version 2 as published | ||
7 | * by the Free Software Foundation, incorporated herein by reference. | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * Driver for PHY related operations via MCDI. | ||
12 | */ | ||
13 | |||
14 | #include "efx.h" | ||
15 | #include "phy.h" | ||
16 | #include "mcdi.h" | ||
17 | #include "mcdi_pcol.h" | ||
18 | #include "mdio_10g.h" | ||
19 | |||
20 | struct efx_mcdi_phy_cfg { | ||
21 | u32 flags; | ||
22 | u32 type; | ||
23 | u32 supported_cap; | ||
24 | u32 channel; | ||
25 | u32 port; | ||
26 | u32 stats_mask; | ||
27 | u8 name[20]; | ||
28 | u32 media; | ||
29 | u32 mmd_mask; | ||
30 | u8 revision[20]; | ||
31 | u32 forced_cap; | ||
32 | }; | ||
33 | |||
34 | static int | ||
35 | efx_mcdi_get_phy_cfg(struct efx_nic *efx, struct efx_mcdi_phy_cfg *cfg) | ||
36 | { | ||
37 | u8 outbuf[MC_CMD_GET_PHY_CFG_OUT_LEN]; | ||
38 | size_t outlen; | ||
39 | int rc; | ||
40 | |||
41 | BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_IN_LEN != 0); | ||
42 | BUILD_BUG_ON(MC_CMD_GET_PHY_CFG_OUT_NAME_LEN != sizeof(cfg->name)); | ||
43 | |||
44 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_CFG, NULL, 0, | ||
45 | outbuf, sizeof(outbuf), &outlen); | ||
46 | if (rc) | ||
47 | goto fail; | ||
48 | |||
49 | if (outlen < MC_CMD_GET_PHY_CFG_OUT_LEN) { | ||
50 | rc = -EMSGSIZE; | ||
51 | goto fail; | ||
52 | } | ||
53 | |||
54 | cfg->flags = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_FLAGS); | ||
55 | cfg->type = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_TYPE); | ||
56 | cfg->supported_cap = | ||
57 | MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_SUPPORTED_CAP); | ||
58 | cfg->channel = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_CHANNEL); | ||
59 | cfg->port = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_PRT); | ||
60 | cfg->stats_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_STATS_MASK); | ||
61 | memcpy(cfg->name, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_NAME), | ||
62 | sizeof(cfg->name)); | ||
63 | cfg->media = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MEDIA_TYPE); | ||
64 | cfg->mmd_mask = MCDI_DWORD(outbuf, GET_PHY_CFG_OUT_MMD_MASK); | ||
65 | memcpy(cfg->revision, MCDI_PTR(outbuf, GET_PHY_CFG_OUT_REVISION), | ||
66 | sizeof(cfg->revision)); | ||
67 | |||
68 | return 0; | ||
69 | |||
70 | fail: | ||
71 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
72 | return rc; | ||
73 | } | ||
74 | |||
75 | static int efx_mcdi_set_link(struct efx_nic *efx, u32 capabilities, | ||
76 | u32 flags, u32 loopback_mode, | ||
77 | u32 loopback_speed) | ||
78 | { | ||
79 | u8 inbuf[MC_CMD_SET_LINK_IN_LEN]; | ||
80 | int rc; | ||
81 | |||
82 | BUILD_BUG_ON(MC_CMD_SET_LINK_OUT_LEN != 0); | ||
83 | |||
84 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_CAP, capabilities); | ||
85 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_FLAGS, flags); | ||
86 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_MODE, loopback_mode); | ||
87 | MCDI_SET_DWORD(inbuf, SET_LINK_IN_LOOPBACK_SPEED, loopback_speed); | ||
88 | |||
89 | rc = efx_mcdi_rpc(efx, MC_CMD_SET_LINK, inbuf, sizeof(inbuf), | ||
90 | NULL, 0, NULL); | ||
91 | if (rc) | ||
92 | goto fail; | ||
93 | |||
94 | return 0; | ||
95 | |||
96 | fail: | ||
97 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
98 | return rc; | ||
99 | } | ||
100 | |||
101 | static int efx_mcdi_loopback_modes(struct efx_nic *efx, u64 *loopback_modes) | ||
102 | { | ||
103 | u8 outbuf[MC_CMD_GET_LOOPBACK_MODES_OUT_LEN]; | ||
104 | size_t outlen; | ||
105 | int rc; | ||
106 | |||
107 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LOOPBACK_MODES, NULL, 0, | ||
108 | outbuf, sizeof(outbuf), &outlen); | ||
109 | if (rc) | ||
110 | goto fail; | ||
111 | |||
112 | if (outlen < MC_CMD_GET_LOOPBACK_MODES_OUT_LEN) { | ||
113 | rc = -EMSGSIZE; | ||
114 | goto fail; | ||
115 | } | ||
116 | |||
117 | *loopback_modes = MCDI_QWORD(outbuf, GET_LOOPBACK_MODES_SUGGESTED); | ||
118 | |||
119 | return 0; | ||
120 | |||
121 | fail: | ||
122 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
123 | return rc; | ||
124 | } | ||
125 | |||
126 | int efx_mcdi_mdio_read(struct efx_nic *efx, unsigned int bus, | ||
127 | unsigned int prtad, unsigned int devad, u16 addr, | ||
128 | u16 *value_out, u32 *status_out) | ||
129 | { | ||
130 | u8 inbuf[MC_CMD_MDIO_READ_IN_LEN]; | ||
131 | u8 outbuf[MC_CMD_MDIO_READ_OUT_LEN]; | ||
132 | size_t outlen; | ||
133 | int rc; | ||
134 | |||
135 | MCDI_SET_DWORD(inbuf, MDIO_READ_IN_BUS, bus); | ||
136 | MCDI_SET_DWORD(inbuf, MDIO_READ_IN_PRTAD, prtad); | ||
137 | MCDI_SET_DWORD(inbuf, MDIO_READ_IN_DEVAD, devad); | ||
138 | MCDI_SET_DWORD(inbuf, MDIO_READ_IN_ADDR, addr); | ||
139 | |||
140 | rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_READ, inbuf, sizeof(inbuf), | ||
141 | outbuf, sizeof(outbuf), &outlen); | ||
142 | if (rc) | ||
143 | goto fail; | ||
144 | |||
145 | *value_out = (u16)MCDI_DWORD(outbuf, MDIO_READ_OUT_VALUE); | ||
146 | *status_out = MCDI_DWORD(outbuf, MDIO_READ_OUT_STATUS); | ||
147 | return 0; | ||
148 | |||
149 | fail: | ||
150 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
151 | return rc; | ||
152 | } | ||
153 | |||
154 | int efx_mcdi_mdio_write(struct efx_nic *efx, unsigned int bus, | ||
155 | unsigned int prtad, unsigned int devad, u16 addr, | ||
156 | u16 value, u32 *status_out) | ||
157 | { | ||
158 | u8 inbuf[MC_CMD_MDIO_WRITE_IN_LEN]; | ||
159 | u8 outbuf[MC_CMD_MDIO_WRITE_OUT_LEN]; | ||
160 | size_t outlen; | ||
161 | int rc; | ||
162 | |||
163 | MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_BUS, bus); | ||
164 | MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_PRTAD, prtad); | ||
165 | MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_DEVAD, devad); | ||
166 | MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_ADDR, addr); | ||
167 | MCDI_SET_DWORD(inbuf, MDIO_WRITE_IN_VALUE, value); | ||
168 | |||
169 | rc = efx_mcdi_rpc(efx, MC_CMD_MDIO_WRITE, inbuf, sizeof(inbuf), | ||
170 | outbuf, sizeof(outbuf), &outlen); | ||
171 | if (rc) | ||
172 | goto fail; | ||
173 | |||
174 | *status_out = MCDI_DWORD(outbuf, MDIO_WRITE_OUT_STATUS); | ||
175 | return 0; | ||
176 | |||
177 | fail: | ||
178 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
179 | return rc; | ||
180 | } | ||
181 | |||
182 | static u32 mcdi_to_ethtool_cap(u32 media, u32 cap) | ||
183 | { | ||
184 | u32 result = 0; | ||
185 | |||
186 | switch (media) { | ||
187 | case MC_CMD_MEDIA_KX4: | ||
188 | result |= SUPPORTED_Backplane; | ||
189 | if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) | ||
190 | result |= SUPPORTED_1000baseKX_Full; | ||
191 | if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) | ||
192 | result |= SUPPORTED_10000baseKX4_Full; | ||
193 | break; | ||
194 | |||
195 | case MC_CMD_MEDIA_XFP: | ||
196 | case MC_CMD_MEDIA_SFP_PLUS: | ||
197 | result |= SUPPORTED_FIBRE; | ||
198 | break; | ||
199 | |||
200 | case MC_CMD_MEDIA_BASE_T: | ||
201 | result |= SUPPORTED_TP; | ||
202 | if (cap & (1 << MC_CMD_PHY_CAP_10HDX_LBN)) | ||
203 | result |= SUPPORTED_10baseT_Half; | ||
204 | if (cap & (1 << MC_CMD_PHY_CAP_10FDX_LBN)) | ||
205 | result |= SUPPORTED_10baseT_Full; | ||
206 | if (cap & (1 << MC_CMD_PHY_CAP_100HDX_LBN)) | ||
207 | result |= SUPPORTED_100baseT_Half; | ||
208 | if (cap & (1 << MC_CMD_PHY_CAP_100FDX_LBN)) | ||
209 | result |= SUPPORTED_100baseT_Full; | ||
210 | if (cap & (1 << MC_CMD_PHY_CAP_1000HDX_LBN)) | ||
211 | result |= SUPPORTED_1000baseT_Half; | ||
212 | if (cap & (1 << MC_CMD_PHY_CAP_1000FDX_LBN)) | ||
213 | result |= SUPPORTED_1000baseT_Full; | ||
214 | if (cap & (1 << MC_CMD_PHY_CAP_10000FDX_LBN)) | ||
215 | result |= SUPPORTED_10000baseT_Full; | ||
216 | break; | ||
217 | } | ||
218 | |||
219 | if (cap & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) | ||
220 | result |= SUPPORTED_Pause; | ||
221 | if (cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) | ||
222 | result |= SUPPORTED_Asym_Pause; | ||
223 | if (cap & (1 << MC_CMD_PHY_CAP_AN_LBN)) | ||
224 | result |= SUPPORTED_Autoneg; | ||
225 | |||
226 | return result; | ||
227 | } | ||
228 | |||
229 | static u32 ethtool_to_mcdi_cap(u32 cap) | ||
230 | { | ||
231 | u32 result = 0; | ||
232 | |||
233 | if (cap & SUPPORTED_10baseT_Half) | ||
234 | result |= (1 << MC_CMD_PHY_CAP_10HDX_LBN); | ||
235 | if (cap & SUPPORTED_10baseT_Full) | ||
236 | result |= (1 << MC_CMD_PHY_CAP_10FDX_LBN); | ||
237 | if (cap & SUPPORTED_100baseT_Half) | ||
238 | result |= (1 << MC_CMD_PHY_CAP_100HDX_LBN); | ||
239 | if (cap & SUPPORTED_100baseT_Full) | ||
240 | result |= (1 << MC_CMD_PHY_CAP_100FDX_LBN); | ||
241 | if (cap & SUPPORTED_1000baseT_Half) | ||
242 | result |= (1 << MC_CMD_PHY_CAP_1000HDX_LBN); | ||
243 | if (cap & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseKX_Full)) | ||
244 | result |= (1 << MC_CMD_PHY_CAP_1000FDX_LBN); | ||
245 | if (cap & (SUPPORTED_10000baseT_Full | SUPPORTED_10000baseKX4_Full)) | ||
246 | result |= (1 << MC_CMD_PHY_CAP_10000FDX_LBN); | ||
247 | if (cap & SUPPORTED_Pause) | ||
248 | result |= (1 << MC_CMD_PHY_CAP_PAUSE_LBN); | ||
249 | if (cap & SUPPORTED_Asym_Pause) | ||
250 | result |= (1 << MC_CMD_PHY_CAP_ASYM_LBN); | ||
251 | if (cap & SUPPORTED_Autoneg) | ||
252 | result |= (1 << MC_CMD_PHY_CAP_AN_LBN); | ||
253 | |||
254 | return result; | ||
255 | } | ||
256 | |||
257 | static u32 efx_get_mcdi_phy_flags(struct efx_nic *efx) | ||
258 | { | ||
259 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
260 | enum efx_phy_mode mode, supported; | ||
261 | u32 flags; | ||
262 | |||
263 | /* TODO: Advertise the capabilities supported by this PHY */ | ||
264 | supported = 0; | ||
265 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_TXDIS_LBN)) | ||
266 | supported |= PHY_MODE_TX_DISABLED; | ||
267 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_LOWPOWER_LBN)) | ||
268 | supported |= PHY_MODE_LOW_POWER; | ||
269 | if (phy_cfg->flags & (1 << MC_CMD_GET_PHY_CFG_POWEROFF_LBN)) | ||
270 | supported |= PHY_MODE_OFF; | ||
271 | |||
272 | mode = efx->phy_mode & supported; | ||
273 | |||
274 | flags = 0; | ||
275 | if (mode & PHY_MODE_TX_DISABLED) | ||
276 | flags |= (1 << MC_CMD_SET_LINK_TXDIS_LBN); | ||
277 | if (mode & PHY_MODE_LOW_POWER) | ||
278 | flags |= (1 << MC_CMD_SET_LINK_LOWPOWER_LBN); | ||
279 | if (mode & PHY_MODE_OFF) | ||
280 | flags |= (1 << MC_CMD_SET_LINK_POWEROFF_LBN); | ||
281 | |||
282 | return flags; | ||
283 | } | ||
284 | |||
285 | static u32 mcdi_to_ethtool_media(u32 media) | ||
286 | { | ||
287 | switch (media) { | ||
288 | case MC_CMD_MEDIA_XAUI: | ||
289 | case MC_CMD_MEDIA_CX4: | ||
290 | case MC_CMD_MEDIA_KX4: | ||
291 | return PORT_OTHER; | ||
292 | |||
293 | case MC_CMD_MEDIA_XFP: | ||
294 | case MC_CMD_MEDIA_SFP_PLUS: | ||
295 | return PORT_FIBRE; | ||
296 | |||
297 | case MC_CMD_MEDIA_BASE_T: | ||
298 | return PORT_TP; | ||
299 | |||
300 | default: | ||
301 | return PORT_OTHER; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | static int efx_mcdi_phy_probe(struct efx_nic *efx) | ||
306 | { | ||
307 | struct efx_mcdi_phy_cfg *phy_cfg; | ||
308 | int rc; | ||
309 | |||
310 | /* TODO: Move phy_data initialisation to | ||
311 | * phy_op->probe/remove, rather than init/fini */ | ||
312 | phy_cfg = kzalloc(sizeof(*phy_cfg), GFP_KERNEL); | ||
313 | if (phy_cfg == NULL) { | ||
314 | rc = -ENOMEM; | ||
315 | goto fail_alloc; | ||
316 | } | ||
317 | rc = efx_mcdi_get_phy_cfg(efx, phy_cfg); | ||
318 | if (rc != 0) | ||
319 | goto fail; | ||
320 | |||
321 | efx->phy_type = phy_cfg->type; | ||
322 | |||
323 | efx->mdio_bus = phy_cfg->channel; | ||
324 | efx->mdio.prtad = phy_cfg->port; | ||
325 | efx->mdio.mmds = phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22); | ||
326 | efx->mdio.mode_support = 0; | ||
327 | if (phy_cfg->mmd_mask & (1 << MC_CMD_MMD_CLAUSE22)) | ||
328 | efx->mdio.mode_support |= MDIO_SUPPORTS_C22; | ||
329 | if (phy_cfg->mmd_mask & ~(1 << MC_CMD_MMD_CLAUSE22)) | ||
330 | efx->mdio.mode_support |= MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; | ||
331 | |||
332 | /* Assert that we can map efx -> mcdi loopback modes */ | ||
333 | BUILD_BUG_ON(LOOPBACK_NONE != MC_CMD_LOOPBACK_NONE); | ||
334 | BUILD_BUG_ON(LOOPBACK_DATA != MC_CMD_LOOPBACK_DATA); | ||
335 | BUILD_BUG_ON(LOOPBACK_GMAC != MC_CMD_LOOPBACK_GMAC); | ||
336 | BUILD_BUG_ON(LOOPBACK_XGMII != MC_CMD_LOOPBACK_XGMII); | ||
337 | BUILD_BUG_ON(LOOPBACK_XGXS != MC_CMD_LOOPBACK_XGXS); | ||
338 | BUILD_BUG_ON(LOOPBACK_XAUI != MC_CMD_LOOPBACK_XAUI); | ||
339 | BUILD_BUG_ON(LOOPBACK_GMII != MC_CMD_LOOPBACK_GMII); | ||
340 | BUILD_BUG_ON(LOOPBACK_SGMII != MC_CMD_LOOPBACK_SGMII); | ||
341 | BUILD_BUG_ON(LOOPBACK_XGBR != MC_CMD_LOOPBACK_XGBR); | ||
342 | BUILD_BUG_ON(LOOPBACK_XFI != MC_CMD_LOOPBACK_XFI); | ||
343 | BUILD_BUG_ON(LOOPBACK_XAUI_FAR != MC_CMD_LOOPBACK_XAUI_FAR); | ||
344 | BUILD_BUG_ON(LOOPBACK_GMII_FAR != MC_CMD_LOOPBACK_GMII_FAR); | ||
345 | BUILD_BUG_ON(LOOPBACK_SGMII_FAR != MC_CMD_LOOPBACK_SGMII_FAR); | ||
346 | BUILD_BUG_ON(LOOPBACK_XFI_FAR != MC_CMD_LOOPBACK_XFI_FAR); | ||
347 | BUILD_BUG_ON(LOOPBACK_GPHY != MC_CMD_LOOPBACK_GPHY); | ||
348 | BUILD_BUG_ON(LOOPBACK_PHYXS != MC_CMD_LOOPBACK_PHYXS); | ||
349 | BUILD_BUG_ON(LOOPBACK_PCS != MC_CMD_LOOPBACK_PCS); | ||
350 | BUILD_BUG_ON(LOOPBACK_PMAPMD != MC_CMD_LOOPBACK_PMAPMD); | ||
351 | BUILD_BUG_ON(LOOPBACK_XPORT != MC_CMD_LOOPBACK_XPORT); | ||
352 | BUILD_BUG_ON(LOOPBACK_XGMII_WS != MC_CMD_LOOPBACK_XGMII_WS); | ||
353 | BUILD_BUG_ON(LOOPBACK_XAUI_WS != MC_CMD_LOOPBACK_XAUI_WS); | ||
354 | BUILD_BUG_ON(LOOPBACK_XAUI_WS_FAR != MC_CMD_LOOPBACK_XAUI_WS_FAR); | ||
355 | BUILD_BUG_ON(LOOPBACK_XAUI_WS_NEAR != MC_CMD_LOOPBACK_XAUI_WS_NEAR); | ||
356 | BUILD_BUG_ON(LOOPBACK_GMII_WS != MC_CMD_LOOPBACK_GMII_WS); | ||
357 | BUILD_BUG_ON(LOOPBACK_XFI_WS != MC_CMD_LOOPBACK_XFI_WS); | ||
358 | BUILD_BUG_ON(LOOPBACK_XFI_WS_FAR != MC_CMD_LOOPBACK_XFI_WS_FAR); | ||
359 | BUILD_BUG_ON(LOOPBACK_PHYXS_WS != MC_CMD_LOOPBACK_PHYXS_WS); | ||
360 | |||
361 | rc = efx_mcdi_loopback_modes(efx, &efx->loopback_modes); | ||
362 | if (rc != 0) | ||
363 | goto fail; | ||
364 | /* The MC indicates that LOOPBACK_NONE is a valid loopback mode, | ||
365 | * but by convention we don't */ | ||
366 | efx->loopback_modes &= ~(1 << LOOPBACK_NONE); | ||
367 | |||
368 | kfree(phy_cfg); | ||
369 | |||
370 | return 0; | ||
371 | |||
372 | fail: | ||
373 | kfree(phy_cfg); | ||
374 | fail_alloc: | ||
375 | return rc; | ||
376 | } | ||
377 | |||
378 | static int efx_mcdi_phy_init(struct efx_nic *efx) | ||
379 | { | ||
380 | struct efx_mcdi_phy_cfg *phy_data; | ||
381 | u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; | ||
382 | u32 caps; | ||
383 | int rc; | ||
384 | |||
385 | phy_data = kzalloc(sizeof(*phy_data), GFP_KERNEL); | ||
386 | if (phy_data == NULL) | ||
387 | return -ENOMEM; | ||
388 | |||
389 | rc = efx_mcdi_get_phy_cfg(efx, phy_data); | ||
390 | if (rc != 0) | ||
391 | goto fail; | ||
392 | |||
393 | efx->phy_data = phy_data; | ||
394 | |||
395 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); | ||
396 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, | ||
397 | outbuf, sizeof(outbuf), NULL); | ||
398 | if (rc) | ||
399 | goto fail; | ||
400 | |||
401 | caps = MCDI_DWORD(outbuf, GET_LINK_OUT_CAP); | ||
402 | if (caps & (1 << MC_CMD_PHY_CAP_AN_LBN)) | ||
403 | efx->link_advertising = | ||
404 | mcdi_to_ethtool_cap(phy_data->media, caps); | ||
405 | else | ||
406 | phy_data->forced_cap = caps; | ||
407 | |||
408 | return 0; | ||
409 | |||
410 | fail: | ||
411 | kfree(phy_data); | ||
412 | return rc; | ||
413 | } | ||
414 | |||
415 | int efx_mcdi_phy_reconfigure(struct efx_nic *efx) | ||
416 | { | ||
417 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
418 | u32 caps = (efx->link_advertising ? | ||
419 | ethtool_to_mcdi_cap(efx->link_advertising) : | ||
420 | phy_cfg->forced_cap); | ||
421 | |||
422 | return efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), | ||
423 | efx->loopback_mode, 0); | ||
424 | } | ||
425 | |||
426 | void efx_mcdi_phy_decode_link(struct efx_nic *efx, | ||
427 | struct efx_link_state *link_state, | ||
428 | u32 speed, u32 flags, u32 fcntl) | ||
429 | { | ||
430 | switch (fcntl) { | ||
431 | case MC_CMD_FCNTL_AUTO: | ||
432 | WARN_ON(1); /* This is not a link mode */ | ||
433 | link_state->fc = EFX_FC_AUTO | EFX_FC_TX | EFX_FC_RX; | ||
434 | break; | ||
435 | case MC_CMD_FCNTL_BIDIR: | ||
436 | link_state->fc = EFX_FC_TX | EFX_FC_RX; | ||
437 | break; | ||
438 | case MC_CMD_FCNTL_RESPOND: | ||
439 | link_state->fc = EFX_FC_RX; | ||
440 | break; | ||
441 | default: | ||
442 | WARN_ON(1); | ||
443 | case MC_CMD_FCNTL_OFF: | ||
444 | link_state->fc = 0; | ||
445 | break; | ||
446 | } | ||
447 | |||
448 | link_state->up = !!(flags & (1 << MC_CMD_GET_LINK_LINK_UP_LBN)); | ||
449 | link_state->fd = !!(flags & (1 << MC_CMD_GET_LINK_FULL_DUPLEX_LBN)); | ||
450 | link_state->speed = speed; | ||
451 | } | ||
452 | |||
453 | /* Verify that the forced flow control settings (!EFX_FC_AUTO) are | ||
454 | * supported by the link partner. Warn the user if this isn't the case | ||
455 | */ | ||
456 | void efx_mcdi_phy_check_fcntl(struct efx_nic *efx, u32 lpa) | ||
457 | { | ||
458 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
459 | u32 rmtadv; | ||
460 | |||
461 | /* The link partner capabilities are only relevent if the | ||
462 | * link supports flow control autonegotiation */ | ||
463 | if (~phy_cfg->supported_cap & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) | ||
464 | return; | ||
465 | |||
466 | /* If flow control autoneg is supported and enabled, then fine */ | ||
467 | if (efx->wanted_fc & EFX_FC_AUTO) | ||
468 | return; | ||
469 | |||
470 | rmtadv = 0; | ||
471 | if (lpa & (1 << MC_CMD_PHY_CAP_PAUSE_LBN)) | ||
472 | rmtadv |= ADVERTISED_Pause; | ||
473 | if (lpa & (1 << MC_CMD_PHY_CAP_ASYM_LBN)) | ||
474 | rmtadv |= ADVERTISED_Asym_Pause; | ||
475 | |||
476 | if ((efx->wanted_fc & EFX_FC_TX) && rmtadv == ADVERTISED_Asym_Pause) | ||
477 | EFX_ERR(efx, "warning: link partner doesn't support " | ||
478 | "pause frames"); | ||
479 | } | ||
480 | |||
481 | static bool efx_mcdi_phy_poll(struct efx_nic *efx) | ||
482 | { | ||
483 | struct efx_link_state old_state = efx->link_state; | ||
484 | u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; | ||
485 | int rc; | ||
486 | |||
487 | WARN_ON(!mutex_is_locked(&efx->mac_lock)); | ||
488 | |||
489 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); | ||
490 | |||
491 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, | ||
492 | outbuf, sizeof(outbuf), NULL); | ||
493 | if (rc) { | ||
494 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
495 | efx->link_state.up = false; | ||
496 | } else { | ||
497 | efx_mcdi_phy_decode_link( | ||
498 | efx, &efx->link_state, | ||
499 | MCDI_DWORD(outbuf, GET_LINK_OUT_LINK_SPEED), | ||
500 | MCDI_DWORD(outbuf, GET_LINK_OUT_FLAGS), | ||
501 | MCDI_DWORD(outbuf, GET_LINK_OUT_FCNTL)); | ||
502 | } | ||
503 | |||
504 | return !efx_link_state_equal(&efx->link_state, &old_state); | ||
505 | } | ||
506 | |||
507 | static void efx_mcdi_phy_fini(struct efx_nic *efx) | ||
508 | { | ||
509 | struct efx_mcdi_phy_data *phy_data = efx->phy_data; | ||
510 | |||
511 | efx->phy_data = NULL; | ||
512 | kfree(phy_data); | ||
513 | } | ||
514 | |||
515 | static void efx_mcdi_phy_get_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) | ||
516 | { | ||
517 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
518 | u8 outbuf[MC_CMD_GET_LINK_OUT_LEN]; | ||
519 | int rc; | ||
520 | |||
521 | ecmd->supported = | ||
522 | mcdi_to_ethtool_cap(phy_cfg->media, phy_cfg->supported_cap); | ||
523 | ecmd->advertising = efx->link_advertising; | ||
524 | ecmd->speed = efx->link_state.speed; | ||
525 | ecmd->duplex = efx->link_state.fd; | ||
526 | ecmd->port = mcdi_to_ethtool_media(phy_cfg->media); | ||
527 | ecmd->phy_address = phy_cfg->port; | ||
528 | ecmd->transceiver = XCVR_INTERNAL; | ||
529 | ecmd->autoneg = !!(efx->link_advertising & ADVERTISED_Autoneg); | ||
530 | ecmd->mdio_support = (efx->mdio.mode_support & | ||
531 | (MDIO_SUPPORTS_C45 | MDIO_SUPPORTS_C22)); | ||
532 | |||
533 | BUILD_BUG_ON(MC_CMD_GET_LINK_IN_LEN != 0); | ||
534 | rc = efx_mcdi_rpc(efx, MC_CMD_GET_LINK, NULL, 0, | ||
535 | outbuf, sizeof(outbuf), NULL); | ||
536 | if (rc) { | ||
537 | EFX_ERR(efx, "%s: failed rc=%d\n", __func__, rc); | ||
538 | return; | ||
539 | } | ||
540 | ecmd->lp_advertising = | ||
541 | mcdi_to_ethtool_cap(phy_cfg->media, | ||
542 | MCDI_DWORD(outbuf, GET_LINK_OUT_LP_CAP)); | ||
543 | } | ||
544 | |||
545 | static int efx_mcdi_phy_set_settings(struct efx_nic *efx, struct ethtool_cmd *ecmd) | ||
546 | { | ||
547 | struct efx_mcdi_phy_cfg *phy_cfg = efx->phy_data; | ||
548 | u32 caps; | ||
549 | int rc; | ||
550 | |||
551 | if (ecmd->autoneg) { | ||
552 | caps = (ethtool_to_mcdi_cap(ecmd->advertising) | | ||
553 | 1 << MC_CMD_PHY_CAP_AN_LBN); | ||
554 | } else if (ecmd->duplex) { | ||
555 | switch (ecmd->speed) { | ||
556 | case 10: caps = 1 << MC_CMD_PHY_CAP_10FDX_LBN; break; | ||
557 | case 100: caps = 1 << MC_CMD_PHY_CAP_100FDX_LBN; break; | ||
558 | case 1000: caps = 1 << MC_CMD_PHY_CAP_1000FDX_LBN; break; | ||
559 | case 10000: caps = 1 << MC_CMD_PHY_CAP_10000FDX_LBN; break; | ||
560 | default: return -EINVAL; | ||
561 | } | ||
562 | } else { | ||
563 | switch (ecmd->speed) { | ||
564 | case 10: caps = 1 << MC_CMD_PHY_CAP_10HDX_LBN; break; | ||
565 | case 100: caps = 1 << MC_CMD_PHY_CAP_100HDX_LBN; break; | ||
566 | case 1000: caps = 1 << MC_CMD_PHY_CAP_1000HDX_LBN; break; | ||
567 | default: return -EINVAL; | ||
568 | } | ||
569 | } | ||
570 | |||
571 | rc = efx_mcdi_set_link(efx, caps, efx_get_mcdi_phy_flags(efx), | ||
572 | efx->loopback_mode, 0); | ||
573 | if (rc) | ||
574 | return rc; | ||
575 | |||
576 | if (ecmd->autoneg) { | ||
577 | efx_link_set_advertising( | ||
578 | efx, ecmd->advertising | ADVERTISED_Autoneg); | ||
579 | phy_cfg->forced_cap = 0; | ||
580 | } else { | ||
581 | efx_link_set_advertising(efx, 0); | ||
582 | phy_cfg->forced_cap = caps; | ||
583 | } | ||
584 | return 0; | ||
585 | } | ||
586 | |||
587 | struct efx_phy_operations efx_mcdi_phy_ops = { | ||
588 | .probe = efx_mcdi_phy_probe, | ||
589 | .init = efx_mcdi_phy_init, | ||
590 | .reconfigure = efx_mcdi_phy_reconfigure, | ||
591 | .poll = efx_mcdi_phy_poll, | ||
592 | .fini = efx_mcdi_phy_fini, | ||
593 | .get_settings = efx_mcdi_phy_get_settings, | ||
594 | .set_settings = efx_mcdi_phy_set_settings, | ||
595 | .run_tests = NULL, | ||
596 | .test_name = NULL, | ||
597 | }; | ||
diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c new file mode 100644 index 000000000000..de07a4f031b2 --- /dev/null +++ b/drivers/net/sfc/siena.c | |||
@@ -0,0 +1,604 @@ | |||
1 | /**************************************************************************** | ||
2 | * Driver for Solarflare Solarstorm network controllers and boards | ||
3 | * Copyright 2005-2006 Fen Systems Ltd. | ||
4 | * Copyright 2006-2009 Solarflare Communications Inc. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License version 2 as published | ||
8 | * by the Free Software Foundation, incorporated herein by reference. | ||
9 | */ | ||
10 | |||
11 | #include <linux/bitops.h> | ||
12 | #include <linux/delay.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/module.h> | ||
15 | #include "net_driver.h" | ||
16 | #include "bitfield.h" | ||
17 | #include "efx.h" | ||
18 | #include "nic.h" | ||
19 | #include "mac.h" | ||
20 | #include "spi.h" | ||
21 | #include "regs.h" | ||
22 | #include "io.h" | ||
23 | #include "phy.h" | ||
24 | #include "workarounds.h" | ||
25 | #include "mcdi.h" | ||
26 | #include "mcdi_pcol.h" | ||
27 | |||
28 | /* Hardware control for SFC9000 family including SFL9021 (aka Siena). */ | ||
29 | |||
30 | static void siena_init_wol(struct efx_nic *efx); | ||
31 | |||
32 | |||
33 | static void siena_push_irq_moderation(struct efx_channel *channel) | ||
34 | { | ||
35 | efx_dword_t timer_cmd; | ||
36 | |||
37 | if (channel->irq_moderation) | ||
38 | EFX_POPULATE_DWORD_2(timer_cmd, | ||
39 | FRF_CZ_TC_TIMER_MODE, | ||
40 | FFE_CZ_TIMER_MODE_INT_HLDOFF, | ||
41 | FRF_CZ_TC_TIMER_VAL, | ||
42 | channel->irq_moderation - 1); | ||
43 | else | ||
44 | EFX_POPULATE_DWORD_2(timer_cmd, | ||
45 | FRF_CZ_TC_TIMER_MODE, | ||
46 | FFE_CZ_TIMER_MODE_DIS, | ||
47 | FRF_CZ_TC_TIMER_VAL, 0); | ||
48 | efx_writed_page_locked(channel->efx, &timer_cmd, FR_BZ_TIMER_COMMAND_P0, | ||
49 | channel->channel); | ||
50 | } | ||
51 | |||
52 | static void siena_push_multicast_hash(struct efx_nic *efx) | ||
53 | { | ||
54 | WARN_ON(!mutex_is_locked(&efx->mac_lock)); | ||
55 | |||
56 | efx_mcdi_rpc(efx, MC_CMD_SET_MCAST_HASH, | ||
57 | efx->multicast_hash.byte, sizeof(efx->multicast_hash), | ||
58 | NULL, 0, NULL); | ||
59 | } | ||
60 | |||
61 | static int siena_mdio_write(struct net_device *net_dev, | ||
62 | int prtad, int devad, u16 addr, u16 value) | ||
63 | { | ||
64 | struct efx_nic *efx = netdev_priv(net_dev); | ||
65 | uint32_t status; | ||
66 | int rc; | ||
67 | |||
68 | rc = efx_mcdi_mdio_write(efx, efx->mdio_bus, prtad, devad, | ||
69 | addr, value, &status); | ||
70 | if (rc) | ||
71 | return rc; | ||
72 | if (status != MC_CMD_MDIO_STATUS_GOOD) | ||
73 | return -EIO; | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int siena_mdio_read(struct net_device *net_dev, | ||
79 | int prtad, int devad, u16 addr) | ||
80 | { | ||
81 | struct efx_nic *efx = netdev_priv(net_dev); | ||
82 | uint16_t value; | ||
83 | uint32_t status; | ||
84 | int rc; | ||
85 | |||
86 | rc = efx_mcdi_mdio_read(efx, efx->mdio_bus, prtad, devad, | ||
87 | addr, &value, &status); | ||
88 | if (rc) | ||
89 | return rc; | ||
90 | if (status != MC_CMD_MDIO_STATUS_GOOD) | ||
91 | return -EIO; | ||
92 | |||
93 | return (int)value; | ||
94 | } | ||
95 | |||
96 | /* This call is responsible for hooking in the MAC and PHY operations */ | ||
97 | static int siena_probe_port(struct efx_nic *efx) | ||
98 | { | ||
99 | int rc; | ||
100 | |||
101 | /* Hook in PHY operations table */ | ||
102 | efx->phy_op = &efx_mcdi_phy_ops; | ||
103 | |||
104 | /* Set up MDIO structure for PHY */ | ||
105 | efx->mdio.mode_support = MDIO_SUPPORTS_C45 | MDIO_EMULATE_C22; | ||
106 | efx->mdio.mdio_read = siena_mdio_read; | ||
107 | efx->mdio.mdio_write = siena_mdio_write; | ||
108 | |||
109 | /* Fill out MDIO structure and loopback modes */ | ||
110 | rc = efx->phy_op->probe(efx); | ||
111 | if (rc != 0) | ||
112 | return rc; | ||
113 | |||
114 | /* Initial assumption */ | ||
115 | efx->link_state.speed = 10000; | ||
116 | efx->link_state.fd = true; | ||
117 | efx->wanted_fc = EFX_FC_RX | EFX_FC_TX; | ||
118 | |||
119 | /* Allocate buffer for stats */ | ||
120 | rc = efx_nic_alloc_buffer(efx, &efx->stats_buffer, | ||
121 | MC_CMD_MAC_NSTATS * sizeof(u64)); | ||
122 | if (rc) | ||
123 | return rc; | ||
124 | EFX_LOG(efx, "stats buffer at %llx (virt %p phys %llx)\n", | ||
125 | (u64)efx->stats_buffer.dma_addr, | ||
126 | efx->stats_buffer.addr, | ||
127 | (u64)virt_to_phys(efx->stats_buffer.addr)); | ||
128 | |||
129 | efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 1); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | void siena_remove_port(struct efx_nic *efx) | ||
135 | { | ||
136 | efx_nic_free_buffer(efx, &efx->stats_buffer); | ||
137 | } | ||
138 | |||
139 | static const struct efx_nic_register_test siena_register_tests[] = { | ||
140 | { FR_AZ_ADR_REGION, | ||
141 | EFX_OWORD32(0x0001FFFF, 0x0001FFFF, 0x0001FFFF, 0x0001FFFF) }, | ||
142 | { FR_CZ_USR_EV_CFG, | ||
143 | EFX_OWORD32(0x000103FF, 0x00000000, 0x00000000, 0x00000000) }, | ||
144 | { FR_AZ_RX_CFG, | ||
145 | EFX_OWORD32(0xFFFFFFFE, 0xFFFFFFFF, 0x0003FFFF, 0x00000000) }, | ||
146 | { FR_AZ_TX_CFG, | ||
147 | EFX_OWORD32(0x7FFF0037, 0xFFFF8000, 0xFFFFFFFF, 0x03FFFFFF) }, | ||
148 | { FR_AZ_TX_RESERVED, | ||
149 | EFX_OWORD32(0xFFFEFE80, 0x1FFFFFFF, 0x020000FE, 0x007FFFFF) }, | ||
150 | { FR_AZ_SRM_TX_DC_CFG, | ||
151 | EFX_OWORD32(0x001FFFFF, 0x00000000, 0x00000000, 0x00000000) }, | ||
152 | { FR_AZ_RX_DC_CFG, | ||
153 | EFX_OWORD32(0x00000003, 0x00000000, 0x00000000, 0x00000000) }, | ||
154 | { FR_AZ_RX_DC_PF_WM, | ||
155 | EFX_OWORD32(0x000003FF, 0x00000000, 0x00000000, 0x00000000) }, | ||
156 | { FR_BZ_DP_CTRL, | ||
157 | EFX_OWORD32(0x00000FFF, 0x00000000, 0x00000000, 0x00000000) }, | ||
158 | { FR_BZ_RX_RSS_TKEY, | ||
159 | EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, | ||
160 | { FR_CZ_RX_RSS_IPV6_REG1, | ||
161 | EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, | ||
162 | { FR_CZ_RX_RSS_IPV6_REG2, | ||
163 | EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF) }, | ||
164 | { FR_CZ_RX_RSS_IPV6_REG3, | ||
165 | EFX_OWORD32(0xFFFFFFFF, 0xFFFFFFFF, 0x00000007, 0x00000000) }, | ||
166 | }; | ||
167 | |||
168 | static int siena_test_registers(struct efx_nic *efx) | ||
169 | { | ||
170 | return efx_nic_test_registers(efx, siena_register_tests, | ||
171 | ARRAY_SIZE(siena_register_tests)); | ||
172 | } | ||
173 | |||
174 | /************************************************************************** | ||
175 | * | ||
176 | * Device reset | ||
177 | * | ||
178 | ************************************************************************** | ||
179 | */ | ||
180 | |||
181 | static int siena_reset_hw(struct efx_nic *efx, enum reset_type method) | ||
182 | { | ||
183 | |||
184 | if (method == RESET_TYPE_WORLD) | ||
185 | return efx_mcdi_reset_mc(efx); | ||
186 | else | ||
187 | return efx_mcdi_reset_port(efx); | ||
188 | } | ||
189 | |||
190 | static int siena_probe_nvconfig(struct efx_nic *efx) | ||
191 | { | ||
192 | int rc; | ||
193 | |||
194 | rc = efx_mcdi_get_board_cfg(efx, efx->mac_address, NULL); | ||
195 | if (rc) | ||
196 | return rc; | ||
197 | |||
198 | return 0; | ||
199 | } | ||
200 | |||
201 | static int siena_probe_nic(struct efx_nic *efx) | ||
202 | { | ||
203 | struct siena_nic_data *nic_data; | ||
204 | bool already_attached = 0; | ||
205 | int rc; | ||
206 | |||
207 | /* Allocate storage for hardware specific data */ | ||
208 | nic_data = kzalloc(sizeof(struct siena_nic_data), GFP_KERNEL); | ||
209 | if (!nic_data) | ||
210 | return -ENOMEM; | ||
211 | efx->nic_data = nic_data; | ||
212 | |||
213 | if (efx_nic_fpga_ver(efx) != 0) { | ||
214 | EFX_ERR(efx, "Siena FPGA not supported\n"); | ||
215 | rc = -ENODEV; | ||
216 | goto fail1; | ||
217 | } | ||
218 | |||
219 | efx_mcdi_init(efx); | ||
220 | |||
221 | /* Recover from a failed assertion before probing */ | ||
222 | rc = efx_mcdi_handle_assertion(efx); | ||
223 | if (rc) | ||
224 | goto fail1; | ||
225 | |||
226 | rc = efx_mcdi_fwver(efx, &nic_data->fw_version, &nic_data->fw_build); | ||
227 | if (rc) { | ||
228 | EFX_ERR(efx, "Failed to read MCPU firmware version - " | ||
229 | "rc %d\n", rc); | ||
230 | goto fail1; /* MCPU absent? */ | ||
231 | } | ||
232 | |||
233 | /* Let the BMC know that the driver is now in charge of link and | ||
234 | * filter settings. We must do this before we reset the NIC */ | ||
235 | rc = efx_mcdi_drv_attach(efx, true, &already_attached); | ||
236 | if (rc) { | ||
237 | EFX_ERR(efx, "Unable to register driver with MCPU\n"); | ||
238 | goto fail2; | ||
239 | } | ||
240 | if (already_attached) | ||
241 | /* Not a fatal error */ | ||
242 | EFX_ERR(efx, "Host already registered with MCPU\n"); | ||
243 | |||
244 | /* Now we can reset the NIC */ | ||
245 | rc = siena_reset_hw(efx, RESET_TYPE_ALL); | ||
246 | if (rc) { | ||
247 | EFX_ERR(efx, "failed to reset NIC\n"); | ||
248 | goto fail3; | ||
249 | } | ||
250 | |||
251 | siena_init_wol(efx); | ||
252 | |||
253 | /* Allocate memory for INT_KER */ | ||
254 | rc = efx_nic_alloc_buffer(efx, &efx->irq_status, sizeof(efx_oword_t)); | ||
255 | if (rc) | ||
256 | goto fail4; | ||
257 | BUG_ON(efx->irq_status.dma_addr & 0x0f); | ||
258 | |||
259 | EFX_LOG(efx, "INT_KER at %llx (virt %p phys %llx)\n", | ||
260 | (unsigned long long)efx->irq_status.dma_addr, | ||
261 | efx->irq_status.addr, | ||
262 | (unsigned long long)virt_to_phys(efx->irq_status.addr)); | ||
263 | |||
264 | /* Read in the non-volatile configuration */ | ||
265 | rc = siena_probe_nvconfig(efx); | ||
266 | if (rc == -EINVAL) { | ||
267 | EFX_ERR(efx, "NVRAM is invalid therefore using defaults\n"); | ||
268 | efx->phy_type = PHY_TYPE_NONE; | ||
269 | efx->mdio.prtad = MDIO_PRTAD_NONE; | ||
270 | } else if (rc) { | ||
271 | goto fail5; | ||
272 | } | ||
273 | |||
274 | return 0; | ||
275 | |||
276 | fail5: | ||
277 | efx_nic_free_buffer(efx, &efx->irq_status); | ||
278 | fail4: | ||
279 | fail3: | ||
280 | efx_mcdi_drv_attach(efx, false, NULL); | ||
281 | fail2: | ||
282 | fail1: | ||
283 | kfree(efx->nic_data); | ||
284 | return rc; | ||
285 | } | ||
286 | |||
287 | /* This call performs hardware-specific global initialisation, such as | ||
288 | * defining the descriptor cache sizes and number of RSS channels. | ||
289 | * It does not set up any buffers, descriptor rings or event queues. | ||
290 | */ | ||
291 | static int siena_init_nic(struct efx_nic *efx) | ||
292 | { | ||
293 | efx_oword_t temp; | ||
294 | int rc; | ||
295 | |||
296 | /* Recover from a failed assertion post-reset */ | ||
297 | rc = efx_mcdi_handle_assertion(efx); | ||
298 | if (rc) | ||
299 | return rc; | ||
300 | |||
301 | /* Squash TX of packets of 16 bytes or less */ | ||
302 | efx_reado(efx, &temp, FR_AZ_TX_RESERVED); | ||
303 | EFX_SET_OWORD_FIELD(temp, FRF_BZ_TX_FLUSH_MIN_LEN_EN, 1); | ||
304 | efx_writeo(efx, &temp, FR_AZ_TX_RESERVED); | ||
305 | |||
306 | /* Do not enable TX_NO_EOP_DISC_EN, since it limits packets to 16 | ||
307 | * descriptors (which is bad). | ||
308 | */ | ||
309 | efx_reado(efx, &temp, FR_AZ_TX_CFG); | ||
310 | EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_NO_EOP_DISC_EN, 0); | ||
311 | EFX_SET_OWORD_FIELD(temp, FRF_CZ_TX_FILTER_EN_BIT, 1); | ||
312 | efx_writeo(efx, &temp, FR_AZ_TX_CFG); | ||
313 | |||
314 | efx_reado(efx, &temp, FR_AZ_RX_CFG); | ||
315 | EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_DESC_PUSH_EN, 0); | ||
316 | EFX_SET_OWORD_FIELD(temp, FRF_BZ_RX_INGR_EN, 1); | ||
317 | efx_writeo(efx, &temp, FR_AZ_RX_CFG); | ||
318 | |||
319 | if (efx_nic_rx_xoff_thresh >= 0 || efx_nic_rx_xon_thresh >= 0) | ||
320 | /* No MCDI operation has been defined to set thresholds */ | ||
321 | EFX_ERR(efx, "ignoring RX flow control thresholds\n"); | ||
322 | |||
323 | /* Enable event logging */ | ||
324 | rc = efx_mcdi_log_ctrl(efx, true, false, 0); | ||
325 | if (rc) | ||
326 | return rc; | ||
327 | |||
328 | /* Set destination of both TX and RX Flush events */ | ||
329 | EFX_POPULATE_OWORD_1(temp, FRF_BZ_FLS_EVQ_ID, 0); | ||
330 | efx_writeo(efx, &temp, FR_BZ_DP_CTRL); | ||
331 | |||
332 | EFX_POPULATE_OWORD_1(temp, FRF_CZ_USREV_DIS, 1); | ||
333 | efx_writeo(efx, &temp, FR_CZ_USR_EV_CFG); | ||
334 | |||
335 | efx_nic_init_common(efx); | ||
336 | return 0; | ||
337 | } | ||
338 | |||
339 | static void siena_remove_nic(struct efx_nic *efx) | ||
340 | { | ||
341 | efx_nic_free_buffer(efx, &efx->irq_status); | ||
342 | |||
343 | siena_reset_hw(efx, RESET_TYPE_ALL); | ||
344 | |||
345 | /* Relinquish the device back to the BMC */ | ||
346 | if (efx_nic_has_mc(efx)) | ||
347 | efx_mcdi_drv_attach(efx, false, NULL); | ||
348 | |||
349 | /* Tear down the private nic state */ | ||
350 | kfree(efx->nic_data); | ||
351 | efx->nic_data = NULL; | ||
352 | } | ||
353 | |||
354 | #define STATS_GENERATION_INVALID ((u64)(-1)) | ||
355 | |||
356 | static int siena_try_update_nic_stats(struct efx_nic *efx) | ||
357 | { | ||
358 | u64 *dma_stats; | ||
359 | struct efx_mac_stats *mac_stats; | ||
360 | u64 generation_start; | ||
361 | u64 generation_end; | ||
362 | |||
363 | mac_stats = &efx->mac_stats; | ||
364 | dma_stats = (u64 *)efx->stats_buffer.addr; | ||
365 | |||
366 | generation_end = dma_stats[MC_CMD_MAC_GENERATION_END]; | ||
367 | if (generation_end == STATS_GENERATION_INVALID) | ||
368 | return 0; | ||
369 | rmb(); | ||
370 | |||
371 | #define MAC_STAT(M, D) \ | ||
372 | mac_stats->M = dma_stats[MC_CMD_MAC_ ## D] | ||
373 | |||
374 | MAC_STAT(tx_bytes, TX_BYTES); | ||
375 | MAC_STAT(tx_bad_bytes, TX_BAD_BYTES); | ||
376 | mac_stats->tx_good_bytes = (mac_stats->tx_bytes - | ||
377 | mac_stats->tx_bad_bytes); | ||
378 | MAC_STAT(tx_packets, TX_PKTS); | ||
379 | MAC_STAT(tx_bad, TX_BAD_FCS_PKTS); | ||
380 | MAC_STAT(tx_pause, TX_PAUSE_PKTS); | ||
381 | MAC_STAT(tx_control, TX_CONTROL_PKTS); | ||
382 | MAC_STAT(tx_unicast, TX_UNICAST_PKTS); | ||
383 | MAC_STAT(tx_multicast, TX_MULTICAST_PKTS); | ||
384 | MAC_STAT(tx_broadcast, TX_BROADCAST_PKTS); | ||
385 | MAC_STAT(tx_lt64, TX_LT64_PKTS); | ||
386 | MAC_STAT(tx_64, TX_64_PKTS); | ||
387 | MAC_STAT(tx_65_to_127, TX_65_TO_127_PKTS); | ||
388 | MAC_STAT(tx_128_to_255, TX_128_TO_255_PKTS); | ||
389 | MAC_STAT(tx_256_to_511, TX_256_TO_511_PKTS); | ||
390 | MAC_STAT(tx_512_to_1023, TX_512_TO_1023_PKTS); | ||
391 | MAC_STAT(tx_1024_to_15xx, TX_1024_TO_15XX_PKTS); | ||
392 | MAC_STAT(tx_15xx_to_jumbo, TX_15XX_TO_JUMBO_PKTS); | ||
393 | MAC_STAT(tx_gtjumbo, TX_GTJUMBO_PKTS); | ||
394 | mac_stats->tx_collision = 0; | ||
395 | MAC_STAT(tx_single_collision, TX_SINGLE_COLLISION_PKTS); | ||
396 | MAC_STAT(tx_multiple_collision, TX_MULTIPLE_COLLISION_PKTS); | ||
397 | MAC_STAT(tx_excessive_collision, TX_EXCESSIVE_COLLISION_PKTS); | ||
398 | MAC_STAT(tx_deferred, TX_DEFERRED_PKTS); | ||
399 | MAC_STAT(tx_late_collision, TX_LATE_COLLISION_PKTS); | ||
400 | mac_stats->tx_collision = (mac_stats->tx_single_collision + | ||
401 | mac_stats->tx_multiple_collision + | ||
402 | mac_stats->tx_excessive_collision + | ||
403 | mac_stats->tx_late_collision); | ||
404 | MAC_STAT(tx_excessive_deferred, TX_EXCESSIVE_DEFERRED_PKTS); | ||
405 | MAC_STAT(tx_non_tcpudp, TX_NON_TCPUDP_PKTS); | ||
406 | MAC_STAT(tx_mac_src_error, TX_MAC_SRC_ERR_PKTS); | ||
407 | MAC_STAT(tx_ip_src_error, TX_IP_SRC_ERR_PKTS); | ||
408 | MAC_STAT(rx_bytes, RX_BYTES); | ||
409 | MAC_STAT(rx_bad_bytes, RX_BAD_BYTES); | ||
410 | mac_stats->rx_good_bytes = (mac_stats->rx_bytes - | ||
411 | mac_stats->rx_bad_bytes); | ||
412 | MAC_STAT(rx_packets, RX_PKTS); | ||
413 | MAC_STAT(rx_good, RX_GOOD_PKTS); | ||
414 | mac_stats->rx_bad = mac_stats->rx_packets - mac_stats->rx_good; | ||
415 | MAC_STAT(rx_pause, RX_PAUSE_PKTS); | ||
416 | MAC_STAT(rx_control, RX_CONTROL_PKTS); | ||
417 | MAC_STAT(rx_unicast, RX_UNICAST_PKTS); | ||
418 | MAC_STAT(rx_multicast, RX_MULTICAST_PKTS); | ||
419 | MAC_STAT(rx_broadcast, RX_BROADCAST_PKTS); | ||
420 | MAC_STAT(rx_lt64, RX_UNDERSIZE_PKTS); | ||
421 | MAC_STAT(rx_64, RX_64_PKTS); | ||
422 | MAC_STAT(rx_65_to_127, RX_65_TO_127_PKTS); | ||
423 | MAC_STAT(rx_128_to_255, RX_128_TO_255_PKTS); | ||
424 | MAC_STAT(rx_256_to_511, RX_256_TO_511_PKTS); | ||
425 | MAC_STAT(rx_512_to_1023, RX_512_TO_1023_PKTS); | ||
426 | MAC_STAT(rx_1024_to_15xx, RX_1024_TO_15XX_PKTS); | ||
427 | MAC_STAT(rx_15xx_to_jumbo, RX_15XX_TO_JUMBO_PKTS); | ||
428 | MAC_STAT(rx_gtjumbo, RX_GTJUMBO_PKTS); | ||
429 | mac_stats->rx_bad_lt64 = 0; | ||
430 | mac_stats->rx_bad_64_to_15xx = 0; | ||
431 | mac_stats->rx_bad_15xx_to_jumbo = 0; | ||
432 | MAC_STAT(rx_bad_gtjumbo, RX_JABBER_PKTS); | ||
433 | MAC_STAT(rx_overflow, RX_OVERFLOW_PKTS); | ||
434 | mac_stats->rx_missed = 0; | ||
435 | MAC_STAT(rx_false_carrier, RX_FALSE_CARRIER_PKTS); | ||
436 | MAC_STAT(rx_symbol_error, RX_SYMBOL_ERROR_PKTS); | ||
437 | MAC_STAT(rx_align_error, RX_ALIGN_ERROR_PKTS); | ||
438 | MAC_STAT(rx_length_error, RX_LENGTH_ERROR_PKTS); | ||
439 | MAC_STAT(rx_internal_error, RX_INTERNAL_ERROR_PKTS); | ||
440 | mac_stats->rx_good_lt64 = 0; | ||
441 | |||
442 | efx->n_rx_nodesc_drop_cnt = dma_stats[MC_CMD_MAC_RX_NODESC_DROPS]; | ||
443 | |||
444 | #undef MAC_STAT | ||
445 | |||
446 | rmb(); | ||
447 | generation_start = dma_stats[MC_CMD_MAC_GENERATION_START]; | ||
448 | if (generation_end != generation_start) | ||
449 | return -EAGAIN; | ||
450 | |||
451 | return 0; | ||
452 | } | ||
453 | |||
454 | static void siena_update_nic_stats(struct efx_nic *efx) | ||
455 | { | ||
456 | while (siena_try_update_nic_stats(efx) == -EAGAIN) | ||
457 | cpu_relax(); | ||
458 | } | ||
459 | |||
460 | static void siena_start_nic_stats(struct efx_nic *efx) | ||
461 | { | ||
462 | u64 *dma_stats = (u64 *)efx->stats_buffer.addr; | ||
463 | |||
464 | dma_stats[MC_CMD_MAC_GENERATION_END] = STATS_GENERATION_INVALID; | ||
465 | |||
466 | efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, | ||
467 | MC_CMD_MAC_NSTATS * sizeof(u64), 1, 0); | ||
468 | } | ||
469 | |||
470 | static void siena_stop_nic_stats(struct efx_nic *efx) | ||
471 | { | ||
472 | efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0); | ||
473 | } | ||
474 | |||
475 | void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len) | ||
476 | { | ||
477 | struct siena_nic_data *nic_data = efx->nic_data; | ||
478 | snprintf(buf, len, "%u.%u.%u.%u", | ||
479 | (unsigned int)(nic_data->fw_version >> 48), | ||
480 | (unsigned int)(nic_data->fw_version >> 32 & 0xffff), | ||
481 | (unsigned int)(nic_data->fw_version >> 16 & 0xffff), | ||
482 | (unsigned int)(nic_data->fw_version & 0xffff)); | ||
483 | } | ||
484 | |||
485 | /************************************************************************** | ||
486 | * | ||
487 | * Wake on LAN | ||
488 | * | ||
489 | ************************************************************************** | ||
490 | */ | ||
491 | |||
492 | static void siena_get_wol(struct efx_nic *efx, struct ethtool_wolinfo *wol) | ||
493 | { | ||
494 | struct siena_nic_data *nic_data = efx->nic_data; | ||
495 | |||
496 | wol->supported = WAKE_MAGIC; | ||
497 | if (nic_data->wol_filter_id != -1) | ||
498 | wol->wolopts = WAKE_MAGIC; | ||
499 | else | ||
500 | wol->wolopts = 0; | ||
501 | memset(&wol->sopass, 0, sizeof(wol->sopass)); | ||
502 | } | ||
503 | |||
504 | |||
505 | static int siena_set_wol(struct efx_nic *efx, u32 type) | ||
506 | { | ||
507 | struct siena_nic_data *nic_data = efx->nic_data; | ||
508 | int rc; | ||
509 | |||
510 | if (type & ~WAKE_MAGIC) | ||
511 | return -EINVAL; | ||
512 | |||
513 | if (type & WAKE_MAGIC) { | ||
514 | if (nic_data->wol_filter_id != -1) | ||
515 | efx_mcdi_wol_filter_remove(efx, | ||
516 | nic_data->wol_filter_id); | ||
517 | rc = efx_mcdi_wol_filter_set_magic(efx, efx->mac_address, | ||
518 | &nic_data->wol_filter_id); | ||
519 | if (rc) | ||
520 | goto fail; | ||
521 | |||
522 | pci_wake_from_d3(efx->pci_dev, true); | ||
523 | } else { | ||
524 | rc = efx_mcdi_wol_filter_reset(efx); | ||
525 | nic_data->wol_filter_id = -1; | ||
526 | pci_wake_from_d3(efx->pci_dev, false); | ||
527 | if (rc) | ||
528 | goto fail; | ||
529 | } | ||
530 | |||
531 | return 0; | ||
532 | fail: | ||
533 | EFX_ERR(efx, "%s failed: type=%d rc=%d\n", __func__, type, rc); | ||
534 | return rc; | ||
535 | } | ||
536 | |||
537 | |||
538 | static void siena_init_wol(struct efx_nic *efx) | ||
539 | { | ||
540 | struct siena_nic_data *nic_data = efx->nic_data; | ||
541 | int rc; | ||
542 | |||
543 | rc = efx_mcdi_wol_filter_get_magic(efx, &nic_data->wol_filter_id); | ||
544 | |||
545 | if (rc != 0) { | ||
546 | /* If it failed, attempt to get into a synchronised | ||
547 | * state with MC by resetting any set WoL filters */ | ||
548 | efx_mcdi_wol_filter_reset(efx); | ||
549 | nic_data->wol_filter_id = -1; | ||
550 | } else if (nic_data->wol_filter_id != -1) { | ||
551 | pci_wake_from_d3(efx->pci_dev, true); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | |||
556 | /************************************************************************** | ||
557 | * | ||
558 | * Revision-dependent attributes used by efx.c and nic.c | ||
559 | * | ||
560 | ************************************************************************** | ||
561 | */ | ||
562 | |||
563 | struct efx_nic_type siena_a0_nic_type = { | ||
564 | .probe = siena_probe_nic, | ||
565 | .remove = siena_remove_nic, | ||
566 | .init = siena_init_nic, | ||
567 | .fini = efx_port_dummy_op_void, | ||
568 | .monitor = NULL, | ||
569 | .reset = siena_reset_hw, | ||
570 | .probe_port = siena_probe_port, | ||
571 | .remove_port = siena_remove_port, | ||
572 | .prepare_flush = efx_port_dummy_op_void, | ||
573 | .update_stats = siena_update_nic_stats, | ||
574 | .start_stats = siena_start_nic_stats, | ||
575 | .stop_stats = siena_stop_nic_stats, | ||
576 | .set_id_led = efx_mcdi_set_id_led, | ||
577 | .push_irq_moderation = siena_push_irq_moderation, | ||
578 | .push_multicast_hash = siena_push_multicast_hash, | ||
579 | .reconfigure_port = efx_mcdi_phy_reconfigure, | ||
580 | .get_wol = siena_get_wol, | ||
581 | .set_wol = siena_set_wol, | ||
582 | .resume_wol = siena_init_wol, | ||
583 | .test_registers = siena_test_registers, | ||
584 | .default_mac_ops = &efx_mcdi_mac_operations, | ||
585 | |||
586 | .revision = EFX_REV_SIENA_A0, | ||
587 | .mem_map_size = (FR_CZ_MC_TREG_SMEM + | ||
588 | FR_CZ_MC_TREG_SMEM_STEP * FR_CZ_MC_TREG_SMEM_ROWS), | ||
589 | .txd_ptr_tbl_base = FR_BZ_TX_DESC_PTR_TBL, | ||
590 | .rxd_ptr_tbl_base = FR_BZ_RX_DESC_PTR_TBL, | ||
591 | .buf_tbl_base = FR_BZ_BUF_FULL_TBL, | ||
592 | .evq_ptr_tbl_base = FR_BZ_EVQ_PTR_TBL, | ||
593 | .evq_rptr_tbl_base = FR_BZ_EVQ_RPTR, | ||
594 | .max_dma_mask = DMA_BIT_MASK(FSF_AZ_TX_KER_BUF_ADDR_WIDTH), | ||
595 | .rx_buffer_padding = 0, | ||
596 | .max_interrupt_mode = EFX_INT_MODE_MSIX, | ||
597 | .phys_addr_channels = 32, /* Hardware limit is 64, but the legacy | ||
598 | * interrupt handler only supports 32 | ||
599 | * channels */ | ||
600 | .tx_dc_base = 0x88000, | ||
601 | .rx_dc_base = 0x68000, | ||
602 | .offload_features = NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM, | ||
603 | .reset_world_flags = ETH_RESET_MGMT << ETH_RESET_SHARED_SHIFT, | ||
604 | }; | ||