Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/ic Add work-in-progress driver for the Designware GM...
details: https://anonhg.NetBSD.org/src/rev/2cba0abf613b
branches: trunk
changeset: 802276:2cba0abf613b
user: martin <martin%NetBSD.org@localhost>
date: Mon Sep 08 14:24:32 2014 +0000
description:
Add work-in-progress driver for the Designware GMAC core, found on some
allwinner chips.
diffstat:
sys/dev/ic/dwc_gmac.c | 811 ++++++++++++++++++++++++++++++++++++++++++++++
sys/dev/ic/dwc_gmac_reg.h | 136 +++++++
sys/dev/ic/dwc_gmac_var.h | 90 +++++
3 files changed, 1037 insertions(+), 0 deletions(-)
diffs (truncated from 1049 to 300 lines):
diff -r 466818c81c9e -r 2cba0abf613b sys/dev/ic/dwc_gmac.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/dev/ic/dwc_gmac.c Mon Sep 08 14:24:32 2014 +0000
@@ -0,0 +1,811 @@
+/*-
+ * Copyright (c) 2013, 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Matt Thomas of 3am Software Foundry and Martin Husemann.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This driver supports the Synopsis Designware GMAC core, as found
+ * on Allwinner A20 cores and others.
+ *
+ * Real documentation seems to not be available, the marketing product
+ * documents could be found here:
+ *
+ * http://www.synopsys.com/dw/ipdir.php?ds=dwc_ether_mac10_100_1000_unive
+ */
+
+#include <sys/cdefs.h>
+
+__KERNEL_RCSID(1, "$NetBSD: dwc_gmac.c,v 1.1 2014/09/08 14:24:32 martin Exp $");
+
+#include "opt_inet.h"
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+
+#include <net/if.h>
+#include <net/if_ether.h>
+#include <net/if_media.h>
+#include <net/bpf.h>
+#ifdef INET
+#include <netinet/if_inarp.h>
+#endif
+
+#include <dev/mii/miivar.h>
+
+#include <dev/ic/dwc_gmac_reg.h>
+#include <dev/ic/dwc_gmac_var.h>
+
+static int dwc_gmac_miibus_read_reg(device_t, int, int);
+static void dwc_gmac_miibus_write_reg(device_t, int, int, int);
+static void dwc_gmac_miibus_statchg(struct ifnet *);
+
+static int dwc_gmac_reset(struct dwc_gmac_softc *sc);
+static void dwc_gmac_write_hwaddr(struct dwc_gmac_softc *sc,
+ uint8_t enaddr[ETHER_ADDR_LEN]);
+static int dwc_gmac_alloc_dma_rings(struct dwc_gmac_softc *sc);
+static void dwc_gmac_free_dma_rings(struct dwc_gmac_softc *sc);
+static int dwc_gmac_alloc_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *);
+static void dwc_gmac_reset_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *);
+static void dwc_gmac_free_rx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_rx_ring *);
+static int dwc_gmac_alloc_tx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_tx_ring *);
+static void dwc_gmac_reset_tx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_tx_ring *);
+static void dwc_gmac_free_tx_ring(struct dwc_gmac_softc *sc, struct dwc_gmac_tx_ring *);
+static void dwc_gmac_txdesc_sync(struct dwc_gmac_softc *sc, int start, int end, int ops);
+static int dwc_gmac_init(struct ifnet *ifp);
+static void dwc_gmac_stop(struct ifnet *ifp, int disable);
+static void dwc_gmac_start(struct ifnet *ifp);
+static int dwc_gmac_queue(struct dwc_gmac_softc *sc, struct mbuf *m0);
+static int dwc_gmac_ioctl(struct ifnet *, u_long, void *);
+
+
+#define TX_DESC_OFFSET(N) ((AWGE_RX_RING_COUNT+(N)) \
+ *sizeof(struct dwc_gmac_dev_dmadesc))
+
+#define RX_DESC_OFFSET(N) ((N)*sizeof(struct dwc_gmac_dev_dmadesc))
+
+void
+dwc_gmac_attach(struct dwc_gmac_softc *sc, uint8_t *ep)
+{
+ uint8_t enaddr[ETHER_ADDR_LEN];
+ uint32_t maclo, machi;
+ struct mii_data * const mii = &sc->sc_mii;
+ struct ifnet * const ifp = &sc->sc_ec.ec_if;
+
+ mutex_init(&sc->sc_mdio_lock, MUTEX_DEFAULT, IPL_NET);
+
+ /*
+ * If the frontend did not pass in a pre-configured ethernet mac
+ * address, try to read on from the current filter setup,
+ * before resetting the chip.
+ */
+ if (ep == NULL) {
+ maclo = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_ADDR0LO);
+ machi = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_ADDR0HI);
+ enaddr[0] = maclo & 0x0ff;
+ enaddr[1] = (maclo >> 8) & 0x0ff;
+ enaddr[2] = (maclo >> 16) & 0x0ff;
+ enaddr[3] = (maclo >> 24) & 0x0ff;
+ enaddr[4] = machi & 0x0ff;
+ enaddr[5] = (machi >> 8) & 0x0ff;
+ ep = enaddr;
+ }
+
+ /*
+ * Init chip and do intial setup
+ */
+ if (dwc_gmac_reset(sc) != 0)
+ return; /* not much to cleanup, haven't attached yet */
+ dwc_gmac_write_hwaddr(sc, ep);
+ aprint_normal_dev(sc->sc_dev, "Ethernet address: %s\n",
+ ether_sprintf(enaddr));
+
+ /*
+ * Allocate Tx and Rx rings
+ */
+ if (dwc_gmac_alloc_dma_rings(sc) != 0) {
+ aprint_error_dev(sc->sc_dev, "could not allocate DMA rings\n");
+ goto fail;
+ }
+
+ if (dwc_gmac_alloc_tx_ring(sc, &sc->sc_txq) != 0) {
+ aprint_error_dev(sc->sc_dev, "could not allocate Tx ring\n");
+ goto fail;
+ }
+
+ mutex_init(&sc->sc_rxq.r_mtx, MUTEX_DEFAULT, IPL_NET);
+ if (dwc_gmac_alloc_rx_ring(sc, &sc->sc_rxq) != 0) {
+ aprint_error_dev(sc->sc_dev, "could not allocate Rx ring\n");
+ goto fail;
+ }
+
+ /*
+ * Prepare interface data
+ */
+ ifp->if_softc = sc;
+ strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_ioctl = dwc_gmac_ioctl;
+ ifp->if_start = dwc_gmac_start;
+ ifp->if_init = dwc_gmac_init;
+ ifp->if_stop = dwc_gmac_stop;
+ IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+ IFQ_SET_READY(&ifp->if_snd);
+
+ /*
+ * Attach MII subdevices
+ */
+ ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus);
+ mii->mii_ifp = ifp;
+ mii->mii_readreg = dwc_gmac_miibus_read_reg;
+ mii->mii_writereg = dwc_gmac_miibus_write_reg;
+ mii->mii_statchg = dwc_gmac_miibus_statchg;
+ mii_attach(sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+
+ if (LIST_EMPTY(&mii->mii_phys)) {
+ aprint_error_dev(sc->sc_dev, "no PHY found!\n");
+ ifmedia_add(&mii->mii_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
+ ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_MANUAL);
+ } else {
+ ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_AUTO);
+ }
+
+ /*
+ * Ready, attach interface
+ */
+ if_attach(ifp);
+ ether_ifattach(ifp, enaddr);
+
+ /*
+ * Enable interrupts
+ */
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_INTR, AWIN_DEF_MAC_INTRMASK);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_INTENABLE, GMAC_DEF_DMA_INT_MASK);
+
+ return;
+
+fail:
+ dwc_gmac_free_rx_ring(sc, &sc->sc_rxq);
+ dwc_gmac_free_tx_ring(sc, &sc->sc_txq);
+}
+
+
+
+static int
+dwc_gmac_reset(struct dwc_gmac_softc *sc)
+{
+ size_t cnt;
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE,
+ bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE) | GMAC_BUSMODE_RESET);
+ for (cnt = 0; cnt < 3000; cnt++) {
+ if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_DMA_BUSMODE)
+ & GMAC_BUSMODE_RESET) == 0)
+ return 0;
+ delay(10);
+ }
+
+ aprint_error_dev(sc->sc_dev, "reset timed out\n");
+ return EIO;
+}
+
+static void
+dwc_gmac_write_hwaddr(struct dwc_gmac_softc *sc,
+ uint8_t enaddr[ETHER_ADDR_LEN])
+{
+ uint32_t lo, hi;
+
+ lo = enaddr[0] | (enaddr[1] << 8) | (enaddr[2] << 16)
+ | (enaddr[3] << 24);
+ hi = enaddr[4] | (enaddr[5] << 8);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_ADDR0LO, lo);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_ADDR0HI, hi);
+}
+
+static int
+dwc_gmac_miibus_read_reg(device_t self, int phy, int reg)
+{
+ struct dwc_gmac_softc * const sc = device_private(self);
+ uint16_t miiaddr;
+ size_t cnt;
+ int rv = 0;
+
+ miiaddr = ((phy << GMAC_MII_PHY_SHIFT) & GMAC_MII_PHY_MASK)
+ | ((reg << GMAC_MII_REG_SHIFT) & GMAC_MII_REG_MASK);
+
+ mutex_enter(&sc->sc_mdio_lock);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR, miiaddr
+ | GMAC_MII_CLK_150_250M | GMAC_MII_BUSY);
+
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if (!(bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR)
+ & GMAC_MII_BUSY)) {
+ rv = bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIDATA);
+ break;
+ }
+ delay(10);
+ }
+
+ mutex_exit(&sc->sc_mdio_lock);
+
+ return rv;
+}
+
+static void
+dwc_gmac_miibus_write_reg(device_t self, int phy, int reg, int val)
+{
+ struct dwc_gmac_softc * const sc = device_private(self);
+ uint16_t miiaddr;
+ size_t cnt;
+
+ miiaddr = ((phy << GMAC_MII_PHY_SHIFT) & GMAC_MII_PHY_MASK)
+ | ((reg << GMAC_MII_REG_SHIFT) & GMAC_MII_REG_MASK)
+ | GMAC_MII_BUSY | GMAC_MII_WRITE;
+
+ mutex_enter(&sc->sc_mdio_lock);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIDATA, val);
+ bus_space_write_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR, miiaddr);
+
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if (!(bus_space_read_4(sc->sc_bst, sc->sc_bsh, AWIN_GMAC_MAC_MIIADDR)
+ & GMAC_MII_BUSY))
+ break;
+ delay(10);
+ }
+
+ mutex_exit(&sc->sc_mdio_lock);
+}
+
+static int
+dwc_gmac_alloc_rx_ring(struct dwc_gmac_softc *sc,
+ struct dwc_gmac_rx_ring *ring)
+{
+ struct dwc_gmac_rx_data *data;
+ bus_addr_t physaddr;
+ const size_t descsize =
+ AWGE_RX_RING_COUNT * sizeof(*ring->r_desc);
+ int error, i, next;
+
+ ring->r_cur = ring->r_next = 0;
+ memset(ring->r_desc, 0, descsize);
Home |
Main Index |
Thread Index |
Old Index