1 /*-
2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3 *
4 * This software was developed by SRI International and the University of
5 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
6 * ("CTSRD"), as part of the DARPA CRASH research programme.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * Ethernet media access controller (EMAC)
32 * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22)
33 *
34 * EMAC is an instance of the Synopsys DesignWare 3504-0
35 * Universal 10/100/1000 Ethernet MAC (DWC_gmac).
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/lock.h>
43 #include <sys/malloc.h>
44 #include <sys/mbuf.h>
45 #include <sys/module.h>
46 #include <sys/mutex.h>
47 #include <sys/rman.h>
48 #include <sys/socket.h>
49
50 #include <net/if.h>
51 #include <net/ethernet.h>
52 #include <net/if_dl.h>
53 #include <net/if_media.h>
54
55 #include <machine/bus.h>
56
57 #include <dev/clk/clk.h>
58 #include <dev/hwreset/hwreset.h>
59
60 #include <dev/mii/mii.h>
61 #include <dev/mii/miivar.h>
62 #include <dev/ofw/ofw_bus.h>
63 #include <dev/ofw/ofw_bus_subr.h>
64 #include <dev/mii/mii_fdt.h>
65
66 #include <dev/dwc/if_dwcvar.h>
67 #include <dev/dwc/dwc1000_reg.h>
68 #include <dev/dwc/dwc1000_core.h>
69 #include <dev/dwc/dwc1000_dma.h>
70
71 #include "if_dwc_if.h"
72
73 struct dwc_hash_maddr_ctx {
74 struct dwc_softc *sc;
75 uint32_t hash[8];
76 };
77
78 #define STATS_HARVEST_INTERVAL 2
79
80 /* Pause time field in the transmitted control frame */
81 static int dwc_pause_time = 0xffff;
82 TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time);
83
84 /*
85 * MIIBUS functions
86 */
87
88 int
dwc1000_miibus_read_reg(device_t dev,int phy,int reg)89 dwc1000_miibus_read_reg(device_t dev, int phy, int reg)
90 {
91 struct dwc_softc *sc;
92 uint16_t mii;
93 size_t cnt;
94 int rv = 0;
95
96 sc = device_get_softc(dev);
97
98 mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT)
99 | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT)
100 | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT)
101 | GMII_ADDRESS_GB; /* Busy flag */
102
103 WRITE4(sc, GMII_ADDRESS, mii);
104
105 for (cnt = 0; cnt < 1000; cnt++) {
106 if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) {
107 rv = READ4(sc, GMII_DATA);
108 break;
109 }
110 DELAY(10);
111 }
112
113 return rv;
114 }
115
116 int
dwc1000_miibus_write_reg(device_t dev,int phy,int reg,int val)117 dwc1000_miibus_write_reg(device_t dev, int phy, int reg, int val)
118 {
119 struct dwc_softc *sc;
120 uint16_t mii;
121 size_t cnt;
122
123 sc = device_get_softc(dev);
124
125 mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT)
126 | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT)
127 | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT)
128 | GMII_ADDRESS_GB | GMII_ADDRESS_GW;
129
130 WRITE4(sc, GMII_DATA, val);
131 WRITE4(sc, GMII_ADDRESS, mii);
132
133 for (cnt = 0; cnt < 1000; cnt++) {
134 if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) {
135 break;
136 }
137 DELAY(10);
138 }
139
140 return (0);
141 }
142
143 void
dwc1000_miibus_statchg(device_t dev)144 dwc1000_miibus_statchg(device_t dev)
145 {
146 struct dwc_softc *sc;
147 struct mii_data *mii;
148 uint32_t reg;
149
150 /*
151 * Called by the MII bus driver when the PHY establishes
152 * link to set the MAC interface registers.
153 */
154
155 sc = device_get_softc(dev);
156
157 DWC_ASSERT_LOCKED(sc);
158
159 mii = sc->mii_softc;
160
161 if (mii->mii_media_status & IFM_ACTIVE)
162 sc->link_is_up = true;
163 else
164 sc->link_is_up = false;
165
166 reg = READ4(sc, MAC_CONFIGURATION);
167 switch (IFM_SUBTYPE(mii->mii_media_active)) {
168 case IFM_1000_T:
169 case IFM_1000_SX:
170 reg &= ~(CONF_FES | CONF_PS);
171 break;
172 case IFM_100_TX:
173 reg |= (CONF_FES | CONF_PS);
174 break;
175 case IFM_10_T:
176 reg &= ~(CONF_FES);
177 reg |= (CONF_PS);
178 break;
179 case IFM_NONE:
180 sc->link_is_up = false;
181 return;
182 default:
183 sc->link_is_up = false;
184 device_printf(dev, "Unsupported media %u\n",
185 IFM_SUBTYPE(mii->mii_media_active));
186 return;
187 }
188 if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
189 reg |= (CONF_DM);
190 else
191 reg &= ~(CONF_DM);
192 WRITE4(sc, MAC_CONFIGURATION, reg);
193
194 reg = FLOW_CONTROL_UP;
195 if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
196 reg |= FLOW_CONTROL_TX;
197 if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
198 reg |= FLOW_CONTROL_RX;
199 if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
200 reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT;
201 WRITE4(sc, FLOW_CONTROL, reg);
202
203 IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
204
205 }
206
207 void
dwc1000_core_setup(struct dwc_softc * sc)208 dwc1000_core_setup(struct dwc_softc *sc)
209 {
210 uint32_t reg;
211
212 DWC_ASSERT_LOCKED(sc);
213
214 /* Enable core */
215 reg = READ4(sc, MAC_CONFIGURATION);
216 reg |= (CONF_JD | CONF_ACS | CONF_BE);
217 WRITE4(sc, MAC_CONFIGURATION, reg);
218 }
219
220 void
dwc1000_enable_mac(struct dwc_softc * sc,bool enable)221 dwc1000_enable_mac(struct dwc_softc *sc, bool enable)
222 {
223 uint32_t reg;
224
225 DWC_ASSERT_LOCKED(sc);
226 reg = READ4(sc, MAC_CONFIGURATION);
227 if (enable)
228 reg |= CONF_TE | CONF_RE;
229 else
230 reg &= ~(CONF_TE | CONF_RE);
231 WRITE4(sc, MAC_CONFIGURATION, reg);
232 }
233
234 void
dwc1000_enable_csum_offload(struct dwc_softc * sc)235 dwc1000_enable_csum_offload(struct dwc_softc *sc)
236 {
237 uint32_t reg;
238
239 DWC_ASSERT_LOCKED(sc);
240 reg = READ4(sc, MAC_CONFIGURATION);
241 if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0)
242 reg |= CONF_IPC;
243 else
244 reg &= ~CONF_IPC;
245 WRITE4(sc, MAC_CONFIGURATION, reg);
246 }
247
248 static const uint8_t nibbletab[] = {
249 /* 0x0 0000 -> 0000 */ 0x0,
250 /* 0x1 0001 -> 1000 */ 0x8,
251 /* 0x2 0010 -> 0100 */ 0x4,
252 /* 0x3 0011 -> 1100 */ 0xc,
253 /* 0x4 0100 -> 0010 */ 0x2,
254 /* 0x5 0101 -> 1010 */ 0xa,
255 /* 0x6 0110 -> 0110 */ 0x6,
256 /* 0x7 0111 -> 1110 */ 0xe,
257 /* 0x8 1000 -> 0001 */ 0x1,
258 /* 0x9 1001 -> 1001 */ 0x9,
259 /* 0xa 1010 -> 0101 */ 0x5,
260 /* 0xb 1011 -> 1101 */ 0xd,
261 /* 0xc 1100 -> 0011 */ 0x3,
262 /* 0xd 1101 -> 1011 */ 0xb,
263 /* 0xe 1110 -> 0111 */ 0x7,
264 /* 0xf 1111 -> 1111 */ 0xf, };
265
266 static uint8_t
bitreverse(uint8_t x)267 bitreverse(uint8_t x)
268 {
269
270 return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4];
271 }
272
273 static u_int
dwc_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)274 dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
275 {
276 struct dwc_hash_maddr_ctx *ctx = arg;
277 uint32_t crc, hashbit, hashreg;
278 uint8_t val;
279
280 crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN);
281 /* Take lower 8 bits and reverse it */
282 val = bitreverse(~crc & 0xff);
283 /*
284 * TODO: There is probably a HW_FEATURES bit which isn't
285 * related to the extended descriptors that describe this
286 */
287 if (!ctx->sc->dma_ext_desc)
288 val >>= 2; /* Only need lower 6 bits */
289 hashreg = (val >> 5);
290 hashbit = (val & 31);
291 ctx->hash[hashreg] |= (1 << hashbit);
292
293 return (1);
294 }
295
296 void
dwc1000_setup_rxfilter(struct dwc_softc * sc)297 dwc1000_setup_rxfilter(struct dwc_softc *sc)
298 {
299 struct dwc_hash_maddr_ctx ctx;
300 if_t ifp;
301 uint8_t *eaddr;
302 uint32_t ffval, hi, lo;
303 int nhash, i;
304
305 DWC_ASSERT_LOCKED(sc);
306
307 ifp = sc->ifp;
308 /*
309 * TODO: There is probably a HW_FEATURES bit which isn't
310 * related to the extended descriptors that describe this
311 */
312 nhash = sc->dma_ext_desc == false ? 2 : 8;
313
314 /*
315 * Set the multicast (group) filter hash.
316 */
317 if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
318 ffval = (FRAME_FILTER_PM);
319 for (i = 0; i < nhash; i++)
320 ctx.hash[i] = ~0;
321 } else {
322 ffval = (FRAME_FILTER_HMC);
323 for (i = 0; i < nhash; i++)
324 ctx.hash[i] = 0;
325 ctx.sc = sc;
326 if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx);
327 }
328
329 /*
330 * Set the individual address filter hash.
331 */
332 if ((if_getflags(ifp) & IFF_PROMISC) != 0)
333 ffval |= (FRAME_FILTER_PR);
334
335 /*
336 * Set the primary address.
337 */
338 eaddr = if_getlladdr(ifp);
339 lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
340 (eaddr[3] << 24);
341 hi = eaddr[4] | (eaddr[5] << 8);
342 WRITE4(sc, MAC_ADDRESS_LOW(0), lo);
343 WRITE4(sc, MAC_ADDRESS_HIGH(0), hi);
344 WRITE4(sc, MAC_FRAME_FILTER, ffval);
345 if (!sc->dma_ext_desc) {
346 WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]);
347 WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]);
348 } else {
349 for (i = 0; i < nhash; i++)
350 WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]);
351 }
352 }
353
354 void
dwc1000_get_hwaddr(struct dwc_softc * sc,uint8_t * hwaddr)355 dwc1000_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
356 {
357 uint32_t hi, lo, rnd;
358
359 /*
360 * Try to recover a MAC address from the running hardware. If there's
361 * something non-zero there, assume the bootloader did the right thing
362 * and just use it.
363 *
364 * Otherwise, set the address to a convenient locally assigned address,
365 * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally
366 * assigned bit set, and the broadcast/multicast bit clear.
367 */
368 lo = READ4(sc, MAC_ADDRESS_LOW(0));
369 hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff;
370 if ((lo != 0xffffffff) || (hi != 0xffff)) {
371 hwaddr[0] = (lo >> 0) & 0xff;
372 hwaddr[1] = (lo >> 8) & 0xff;
373 hwaddr[2] = (lo >> 16) & 0xff;
374 hwaddr[3] = (lo >> 24) & 0xff;
375 hwaddr[4] = (hi >> 0) & 0xff;
376 hwaddr[5] = (hi >> 8) & 0xff;
377 } else {
378 rnd = arc4random() & 0x00ffffff;
379 hwaddr[0] = 'b';
380 hwaddr[1] = 's';
381 hwaddr[2] = 'd';
382 hwaddr[3] = rnd >> 16;
383 hwaddr[4] = rnd >> 8;
384 hwaddr[5] = rnd >> 0;
385 }
386 }
387
388 /*
389 * Stats
390 */
391
392 static void
dwc1000_clear_stats(struct dwc_softc * sc)393 dwc1000_clear_stats(struct dwc_softc *sc)
394 {
395 uint32_t reg;
396
397 reg = READ4(sc, MMC_CONTROL);
398 reg |= (MMC_CONTROL_CNTRST);
399 WRITE4(sc, MMC_CONTROL, reg);
400 }
401
402 void
dwc1000_harvest_stats(struct dwc_softc * sc)403 dwc1000_harvest_stats(struct dwc_softc *sc)
404 {
405 if_t ifp;
406
407 /* We don't need to harvest too often. */
408 if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL)
409 return;
410
411 sc->stats_harvest_count = 0;
412 ifp = sc->ifp;
413
414 if_inc_counter(ifp, IFCOUNTER_IERRORS,
415 READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) +
416 READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) +
417 READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) +
418 READ4(sc, RXLENGTHERROR));
419
420 if_inc_counter(ifp, IFCOUNTER_OERRORS,
421 READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) +
422 READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR));
423
424 if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
425 READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL));
426
427 dwc1000_clear_stats(sc);
428 }
429
430 void
dwc1000_intr(struct dwc_softc * sc)431 dwc1000_intr(struct dwc_softc *sc)
432 {
433 uint32_t reg;
434
435 DWC_ASSERT_LOCKED(sc);
436
437 reg = READ4(sc, INTERRUPT_STATUS);
438 if (reg)
439 READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS);
440 }
441
442 void
dwc1000_intr_disable(struct dwc_softc * sc)443 dwc1000_intr_disable(struct dwc_softc *sc)
444 {
445
446 WRITE4(sc, INTERRUPT_ENABLE, 0);
447 }
448