octe.c revision 210311
1/*-
2 * Copyright (c) 2010 Juli Mallett <jmallett@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/mips/cavium/octe/octe.c 210311 2010-07-20 19:25:11Z jmallett $
27 */
28
29/*
30 * Cavium Octeon Ethernet devices.
31 *
32 * XXX This file should be moved to if_octe.c
33 * XXX The driver may have sufficient locking but we need locking to protect
34 *     the interfaces presented here, right?
35 */
36
37#include "opt_inet.h"
38
39#include <sys/param.h>
40#include <sys/systm.h>
41#include <sys/bus.h>
42#include <sys/endian.h>
43#include <sys/kernel.h>
44#include <sys/mbuf.h>
45#include <sys/lock.h>
46#include <sys/module.h>
47#include <sys/mutex.h>
48#include <sys/rman.h>
49#include <sys/socket.h>
50#include <sys/sockio.h>
51#include <sys/sysctl.h>
52
53#include <net/bpf.h>
54#include <net/ethernet.h>
55#include <net/if.h>
56#include <net/if_dl.h>
57#include <net/if_media.h>
58#include <net/if_types.h>
59#include <net/if_var.h>
60#include <net/if_vlan_var.h>
61
62#ifdef INET
63#include <netinet/in.h>
64#include <netinet/if_ether.h>
65#endif
66
67#include <dev/mii/mii.h>
68#include <dev/mii/miivar.h>
69
70#include "wrapper-cvmx-includes.h"
71#include "cavium-ethernet.h"
72
73#include "ethernet-common.h"
74#include "ethernet-defines.h"
75#include "ethernet-mdio.h"
76#include "ethernet-tx.h"
77
78#include "miibus_if.h"
79
80#define	OCTE_TX_LOCK(priv)	mtx_lock(&(priv)->tx_mtx)
81#define	OCTE_TX_UNLOCK(priv)	mtx_unlock(&(priv)->tx_mtx)
82
83static int		octe_probe(device_t);
84static int		octe_attach(device_t);
85static int		octe_detach(device_t);
86static int		octe_shutdown(device_t);
87
88static int		octe_miibus_readreg(device_t, int, int);
89static int		octe_miibus_writereg(device_t, int, int, int);
90
91static void		octe_init(void *);
92static void		octe_stop(void *);
93static void		octe_start(struct ifnet *);
94
95static int		octe_mii_medchange(struct ifnet *);
96static void		octe_mii_medstat(struct ifnet *, struct ifmediareq *);
97
98static int		octe_medchange(struct ifnet *);
99static void		octe_medstat(struct ifnet *, struct ifmediareq *);
100
101static int		octe_ioctl(struct ifnet *, u_long, caddr_t);
102
103static device_method_t octe_methods[] = {
104	/* Device interface */
105	DEVMETHOD(device_probe,		octe_probe),
106	DEVMETHOD(device_attach,	octe_attach),
107	DEVMETHOD(device_detach,	octe_detach),
108	DEVMETHOD(device_shutdown,	octe_shutdown),
109
110	/* MII interface */
111	DEVMETHOD(miibus_readreg,	octe_miibus_readreg),
112	DEVMETHOD(miibus_writereg,	octe_miibus_writereg),
113
114	{ 0, 0 }
115};
116
117static driver_t octe_driver = {
118	"octe",
119	octe_methods,
120	sizeof (cvm_oct_private_t),
121};
122
123static devclass_t octe_devclass;
124
125DRIVER_MODULE(octe, octebus, octe_driver, octe_devclass, 0, 0);
126DRIVER_MODULE(miibus, octe, miibus_driver, miibus_devclass, 0, 0);
127
128static driver_t pow_driver = {
129	"pow",
130	octe_methods,
131	sizeof (cvm_oct_private_t),
132};
133
134static devclass_t pow_devclass;
135
136DRIVER_MODULE(pow, octebus, pow_driver, pow_devclass, 0, 0);
137
138static int
139octe_probe(device_t dev)
140{
141	return (0);
142}
143
144static int
145octe_attach(device_t dev)
146{
147	struct ifnet *ifp;
148	cvm_oct_private_t *priv;
149	unsigned qos;
150	int error;
151
152	priv = device_get_softc(dev);
153	ifp = priv->ifp;
154
155	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
156
157	if (priv->phy_id != -1) {
158		error = mii_phy_probe(dev, &priv->miibus, octe_mii_medchange,
159				      octe_mii_medstat);
160		if (error != 0) {
161			device_printf(dev, "missing phy %u\n", priv->phy_id);
162		}
163	}
164
165	if (priv->miibus == NULL) {
166		ifmedia_init(&priv->media, 0, octe_medchange, octe_medstat);
167
168		ifmedia_add(&priv->media, IFM_ETHER | IFM_AUTO, 0, NULL);
169		ifmedia_set(&priv->media, IFM_ETHER | IFM_AUTO);
170	}
171
172	/*
173	 * XXX
174	 * We don't support programming the multicast filter right now, although it
175	 * ought to be easy enough.  (Presumably it's just a matter of putting
176	 * multicast addresses in the CAM?)
177	 */
178	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI;
179	ifp->if_init = octe_init;
180	ifp->if_ioctl = octe_ioctl;
181	ifp->if_start = octe_start;
182
183	priv->if_flags = ifp->if_flags;
184
185	mtx_init(&priv->tx_mtx, ifp->if_xname, "octe tx send queue", MTX_DEF);
186
187	for (qos = 0; qos < 16; qos++) {
188		mtx_init(&priv->tx_free_queue[qos].ifq_mtx, ifp->if_xname, "octe tx free queue", MTX_DEF);
189		IFQ_SET_MAXLEN(&priv->tx_free_queue[qos], MAX_OUT_QUEUE_DEPTH);
190	}
191
192	ether_ifattach(ifp, priv->mac);
193
194	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
195	ifp->if_capabilities = IFCAP_VLAN_MTU | IFCAP_HWCSUM;
196	ifp->if_capenable = ifp->if_capabilities;
197	ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
198
199	OCTE_TX_LOCK(priv);
200	IFQ_SET_MAXLEN(&ifp->if_snd, MAX_OUT_QUEUE_DEPTH);
201	ifp->if_snd.ifq_drv_maxlen = MAX_OUT_QUEUE_DEPTH;
202	IFQ_SET_READY(&ifp->if_snd);
203	OCTE_TX_UNLOCK(priv);
204
205	return (0);
206}
207
208static int
209octe_detach(device_t dev)
210{
211	return (0);
212}
213
214static int
215octe_shutdown(device_t dev)
216{
217	return (octe_detach(dev));
218}
219
220static int
221octe_miibus_readreg(device_t dev, int phy, int reg)
222{
223	cvm_oct_private_t *priv;
224
225	priv = device_get_softc(dev);
226
227	if (phy != priv->phy_id)
228		return (0);
229
230	return (cvm_oct_mdio_read(priv->ifp, phy, reg));
231}
232
233static int
234octe_miibus_writereg(device_t dev, int phy, int reg, int val)
235{
236	cvm_oct_private_t *priv;
237
238	priv = device_get_softc(dev);
239
240	KASSERT(phy == priv->phy_id,
241	    ("write to phy %u but our phy is %u", phy, priv->phy_id));
242
243	cvm_oct_mdio_write(priv->ifp, phy, reg, val);
244
245	return (0);
246}
247
248static void
249octe_init(void *arg)
250{
251	struct ifnet *ifp;
252	cvm_oct_private_t *priv;
253
254	priv = arg;
255	ifp = priv->ifp;
256
257	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
258		octe_stop(priv);
259
260	if (priv->open != NULL)
261		priv->open(ifp);
262
263	if (((ifp->if_flags ^ priv->if_flags) & (IFF_ALLMULTI | IFF_MULTICAST | IFF_PROMISC)) != 0)
264		cvm_oct_common_set_multicast_list(ifp);
265
266	cvm_oct_common_set_mac_address(ifp, IF_LLADDR(ifp));
267
268	if (priv->poll != NULL)
269		priv->poll(ifp);
270	if (priv->miibus != NULL)
271		mii_mediachg(device_get_softc(priv->miibus));
272
273	ifp->if_drv_flags |= IFF_DRV_RUNNING;
274	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
275}
276
277static void
278octe_stop(void *arg)
279{
280	struct ifnet *ifp;
281	cvm_oct_private_t *priv;
282
283	priv = arg;
284	ifp = priv->ifp;
285
286	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
287		return;
288
289	if (priv->stop != NULL)
290		priv->stop(ifp);
291
292	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
293}
294
295static void
296octe_start(struct ifnet *ifp)
297{
298	cvm_oct_private_t *priv;
299	struct mbuf *m;
300	int error;
301
302	priv = ifp->if_softc;
303
304	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != IFF_DRV_RUNNING)
305		return;
306
307	OCTE_TX_LOCK(priv);
308	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
309		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
310
311		OCTE_TX_UNLOCK(priv);
312
313		/*
314		 * XXX
315		 *
316		 * We may not be able to pass the mbuf up to BPF for one of
317		 * two very good reasons:
318		 * (1) immediately after our inserting it another CPU may be
319		 *     kind enough to free it for us.
320		 * (2) m_collapse gets called on m and we don't get back the
321		 *     modified pointer.
322		 *
323		 * We have some options other than an m_dup route:
324		 * (1) use a mutex or spinlock to prevent another CPU from
325		 *     freeing it.  We could lock the tx_free_list's lock,
326		 *     that would make sense.
327		 * (2) get back the new mbuf pointer.
328		 * (3) do the collapse here.
329		 */
330
331		if (priv->queue != -1) {
332			error = cvm_oct_xmit(m, ifp);
333		} else {
334			error = cvm_oct_xmit_pow(m, ifp);
335		}
336
337		if (error != 0) {
338			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
339			return;
340		}
341
342		OCTE_TX_LOCK(priv);
343	}
344	OCTE_TX_UNLOCK(priv);
345}
346
347static int
348octe_mii_medchange(struct ifnet *ifp)
349{
350	cvm_oct_private_t *priv;
351	struct mii_data *mii;
352
353	priv = ifp->if_softc;
354	mii = device_get_softc(priv->miibus);
355
356	if (mii->mii_instance) {
357		struct mii_softc *miisc;
358
359		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
360			mii_phy_reset(miisc);
361	}
362	mii_mediachg(mii);
363
364	return (0);
365}
366
367static void
368octe_mii_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
369{
370	cvm_oct_private_t *priv;
371	struct mii_data *mii;
372
373	priv = ifp->if_softc;
374	mii = device_get_softc(priv->miibus);
375
376	mii_pollstat(mii);
377	ifm->ifm_active = mii->mii_media_active;
378	ifm->ifm_status = mii->mii_media_status;
379}
380
381static int
382octe_medchange(struct ifnet *ifp)
383{
384	return (ENOTSUP);
385}
386
387static void
388octe_medstat(struct ifnet *ifp, struct ifmediareq *ifm)
389{
390	cvm_oct_private_t *priv;
391	cvmx_helper_link_info_t link_info;
392
393	priv = ifp->if_softc;
394
395	ifm->ifm_status = IFM_AVALID;
396	ifm->ifm_active = IFT_ETHER;
397
398	if (priv->poll == NULL)
399		return;
400	priv->poll(ifp);
401
402	link_info.u64 = priv->link_info;
403
404	if (!link_info.s.link_up)
405		return;
406
407	ifm->ifm_status |= IFM_ACTIVE;
408
409	switch (link_info.s.speed) {
410	case 10:
411		ifm->ifm_active |= IFM_10_T;
412		break;
413	case 100:
414		ifm->ifm_active |= IFM_100_TX;
415		break;
416	case 1000:
417		ifm->ifm_active |= IFM_1000_T;
418		break;
419	case 10000:
420		ifm->ifm_active |= IFM_10G_T;
421		break;
422	}
423
424	if (link_info.s.full_duplex)
425		ifm->ifm_active |= IFM_FDX;
426	else
427		ifm->ifm_active |= IFM_HDX;
428}
429
430static int
431octe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
432{
433	cvm_oct_private_t *priv;
434	struct mii_data *mii;
435	struct ifreq *ifr;
436#ifdef INET
437	struct ifaddr *ifa;
438#endif
439	int error;
440
441	priv = ifp->if_softc;
442	ifr = (struct ifreq *)data;
443#ifdef INET
444	ifa = (struct ifaddr *)data;
445#endif
446
447	switch (cmd) {
448	case SIOCSIFADDR:
449#ifdef INET
450		/*
451		 * Avoid reinitialization unless it's necessary.
452		 */
453		if (ifa->ifa_addr->sa_family == AF_INET) {
454			ifp->if_flags |= IFF_UP;
455			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
456				octe_init(priv);
457			arp_ifinit(ifp, ifa);
458
459			return (0);
460		}
461#endif
462		error = ether_ioctl(ifp, cmd, data);
463		if (error != 0)
464			return (error);
465		return (0);
466
467	case SIOCSIFFLAGS:
468		if (ifp->if_flags == priv->if_flags)
469			return (0);
470		if ((ifp->if_flags & IFF_UP) != 0) {
471			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
472				octe_init(priv);
473		} else {
474			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
475				octe_stop(priv);
476		}
477		priv->if_flags = ifp->if_flags;
478		return (0);
479
480	case SIOCSIFCAP:
481		/*
482		 * Just change the capabilities in software, currently none
483		 * require reprogramming hardware, they just toggle whether we
484		 * make use of already-present facilities in software.
485		 */
486		ifp->if_capenable = ifr->ifr_reqcap;
487		return (0);
488
489	case SIOCSIFMTU:
490		error = cvm_oct_common_change_mtu(ifp, ifr->ifr_mtu);
491		if (error != 0)
492			return (EINVAL);
493		return (0);
494
495	case SIOCSIFMEDIA:
496	case SIOCGIFMEDIA:
497		if (priv->miibus != NULL) {
498			mii = device_get_softc(priv->miibus);
499			error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
500			if (error != 0)
501				return (error);
502			return (0);
503		}
504		error = ifmedia_ioctl(ifp, ifr, &priv->media, cmd);
505		if (error != 0)
506			return (error);
507		return (0);
508
509	default:
510		error = ether_ioctl(ifp, cmd, data);
511		if (error != 0)
512			return (error);
513		return (0);
514	}
515}
516