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/87bda593fac9
branches:  trunk
changeset: 332134:87bda593fac9
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 3e6fc76afa96 -r 87bda593fac9 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