2009-04-14 22:53:22 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2009, Oleksandr Tymoshenko
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* 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 unmodified, 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 AUTHOR 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 AUTHOR 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AR71XX gigabit ethernet driver
|
|
|
|
*/
|
2009-05-26 03:45:58 +00:00
|
|
|
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
|
|
|
#include "opt_device_polling.h"
|
|
|
|
#endif
|
|
|
|
|
2012-05-01 04:35:53 +00:00
|
|
|
#include "opt_arge.h"
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/endian.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/sockio.h>
|
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/taskqueue.h>
|
2010-07-08 14:34:15 +00:00
|
|
|
#include <sys/sysctl.h>
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_media.h>
|
|
|
|
#include <net/if_types.h>
|
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <machine/cache.h>
|
|
|
|
#include <machine/resource.h>
|
|
|
|
#include <vm/vm_param.h>
|
|
|
|
#include <vm/vm.h>
|
|
|
|
#include <vm/pmap.h>
|
|
|
|
#include <machine/pmap.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/rman.h>
|
|
|
|
|
|
|
|
#include <dev/mii/mii.h>
|
|
|
|
#include <dev/mii/miivar.h>
|
|
|
|
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
#include "opt_arge.h"
|
|
|
|
|
|
|
|
#if defined(ARGE_MDIO)
|
|
|
|
#include <dev/etherswitch/mdio.h>
|
|
|
|
#include <dev/etherswitch/miiproxy.h>
|
|
|
|
#include "mdio_if.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
MODULE_DEPEND(arge, ether, 1, 1, 1);
|
|
|
|
MODULE_DEPEND(arge, miibus, 1, 1, 1);
|
2012-05-01 06:18:30 +00:00
|
|
|
MODULE_VERSION(arge, 1);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
#include "miibus_if.h"
|
|
|
|
|
|
|
|
#include <mips/atheros/ar71xxreg.h>
|
|
|
|
#include <mips/atheros/if_argevar.h>
|
2011-03-13 08:28:21 +00:00
|
|
|
#include <mips/atheros/ar71xx_setup.h>
|
2010-08-19 02:04:35 +00:00
|
|
|
#include <mips/atheros/ar71xx_cpudef.h>
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2011-04-05 05:15:48 +00:00
|
|
|
typedef enum {
|
|
|
|
ARGE_DBG_MII = 0x00000001,
|
2011-04-05 06:33:35 +00:00
|
|
|
ARGE_DBG_INTR = 0x00000002,
|
|
|
|
ARGE_DBG_TX = 0x00000004,
|
|
|
|
ARGE_DBG_RX = 0x00000008,
|
|
|
|
ARGE_DBG_ERR = 0x00000010,
|
|
|
|
ARGE_DBG_RESET = 0x00000020,
|
2012-05-02 07:43:11 +00:00
|
|
|
ARGE_DBG_PLL = 0x00000040,
|
2011-04-05 05:15:48 +00:00
|
|
|
} arge_debug_flags;
|
|
|
|
|
2012-05-02 06:18:12 +00:00
|
|
|
static const char * arge_miicfg_str[] = {
|
|
|
|
"NONE",
|
|
|
|
"GMII",
|
|
|
|
"MII",
|
|
|
|
"RGMII",
|
|
|
|
"RMII"
|
|
|
|
};
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
#ifdef ARGE_DEBUG
|
2011-04-05 05:15:48 +00:00
|
|
|
#define ARGEDEBUG(_sc, _m, ...) \
|
|
|
|
do { \
|
|
|
|
if ((_m) & (_sc)->arge_debug) \
|
|
|
|
device_printf((_sc)->arge_dev, __VA_ARGS__); \
|
|
|
|
} while (0)
|
2009-04-14 22:53:22 +00:00
|
|
|
#else
|
2011-04-05 05:15:48 +00:00
|
|
|
#define ARGEDEBUG(_sc, _m, ...)
|
2009-04-14 22:53:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static int arge_attach(device_t);
|
|
|
|
static int arge_detach(device_t);
|
|
|
|
static void arge_flush_ddr(struct arge_softc *);
|
|
|
|
static int arge_ifmedia_upd(struct ifnet *);
|
|
|
|
static void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
|
|
|
|
static int arge_ioctl(struct ifnet *, u_long, caddr_t);
|
|
|
|
static void arge_init(void *);
|
|
|
|
static void arge_init_locked(struct arge_softc *);
|
|
|
|
static void arge_link_task(void *, int);
|
Fix link status handling on if_arge upon system boot to allow bootp/NFS to
function.
From the submitter:
This patch fixes an issue I encountered using an NFS root with an
ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
to. This may be the same issue that Monthadar Al Jaberi reported against
an RSPRO on 6 March, as the signature is the same:
%%%
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
.
.
.
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
%%%
The primary issue that I found is that the DHCP/BOOTP message that
bootpc_call() is sending never makes it onto the wire, which I believe is
due to the following:
- Last December, a change was made to the ifioctl that bootpc_call() uses
to adjust the netmask around the sosend().
- The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
old one (SIOCSIFNETMASK) did not.
- if_arge maintains its own sense of link state in sc->arge_link_status.
- On a single-phy interface, sc->arge_link_status is initialized to 0 in
arge_init_locked().
- sc->arge_link_status remains 0 until a phy state change notification
causes arge_link_task to run, notice the link is up, and set it to 1.
- The inits caused by the ifioctls in bootpc_call are reinitializing the
interface, but not the phy, so sc->arge_link_status goes to 0 and remains
there.
- arge_start_locked() always sees sc->arge_link_status == 0 and returns
without queuing anything.
The attached patch changes arge_init_locked() such that in the single-phy
case, instead of initializing sc->arge_link_status to 0, it runs
arge_link_task() to set it according to the current phy state. This change
has allowed my setup to mount an NFS root successfully.
Submitted by: Patrick Kelsey <kelsey@ieee.org>
Reviewed by: juli
2012-03-13 06:28:52 +00:00
|
|
|
static void arge_update_link_locked(struct arge_softc *sc);
|
2009-11-12 21:27:58 +00:00
|
|
|
static void arge_set_pll(struct arge_softc *, int, int);
|
2009-04-14 22:53:22 +00:00
|
|
|
static int arge_miibus_readreg(device_t, int, int);
|
|
|
|
static void arge_miibus_statchg(device_t);
|
|
|
|
static int arge_miibus_writereg(device_t, int, int, int);
|
|
|
|
static int arge_probe(device_t);
|
|
|
|
static void arge_reset_dma(struct arge_softc *);
|
|
|
|
static int arge_resume(device_t);
|
|
|
|
static int arge_rx_ring_init(struct arge_softc *);
|
2012-03-13 06:15:20 +00:00
|
|
|
static void arge_rx_ring_free(struct arge_softc *sc);
|
2009-04-14 22:53:22 +00:00
|
|
|
static int arge_tx_ring_init(struct arge_softc *);
|
2009-05-26 17:43:32 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
2009-10-30 01:40:32 +00:00
|
|
|
static int arge_poll(struct ifnet *, enum poll_cmd, int);
|
2009-05-26 17:43:32 +00:00
|
|
|
#endif
|
2009-06-12 12:17:32 +00:00
|
|
|
static int arge_shutdown(device_t);
|
2009-04-14 22:53:22 +00:00
|
|
|
static void arge_start(struct ifnet *);
|
|
|
|
static void arge_start_locked(struct ifnet *);
|
|
|
|
static void arge_stop(struct arge_softc *);
|
|
|
|
static int arge_suspend(device_t);
|
|
|
|
|
2009-10-30 01:40:32 +00:00
|
|
|
static int arge_rx_locked(struct arge_softc *);
|
2009-04-14 22:53:22 +00:00
|
|
|
static void arge_tx_locked(struct arge_softc *);
|
|
|
|
static void arge_intr(void *);
|
|
|
|
static int arge_intr_filter(void *);
|
|
|
|
static void arge_tick(void *);
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
static void arge_hinted_child(device_t bus, const char *dname, int dunit);
|
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
/*
|
|
|
|
* ifmedia callbacks for multiPHY MAC
|
|
|
|
*/
|
|
|
|
void arge_multiphy_mediastatus(struct ifnet *, struct ifmediareq *);
|
|
|
|
int arge_multiphy_mediachange(struct ifnet *);
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
static void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int);
|
|
|
|
static int arge_dma_alloc(struct arge_softc *);
|
|
|
|
static void arge_dma_free(struct arge_softc *);
|
|
|
|
static int arge_newbuf(struct arge_softc *, int);
|
|
|
|
static __inline void arge_fixup_rx(struct mbuf *);
|
|
|
|
|
|
|
|
static device_method_t arge_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_probe, arge_probe),
|
|
|
|
DEVMETHOD(device_attach, arge_attach),
|
|
|
|
DEVMETHOD(device_detach, arge_detach),
|
|
|
|
DEVMETHOD(device_suspend, arge_suspend),
|
|
|
|
DEVMETHOD(device_resume, arge_resume),
|
|
|
|
DEVMETHOD(device_shutdown, arge_shutdown),
|
|
|
|
|
|
|
|
/* MII interface */
|
|
|
|
DEVMETHOD(miibus_readreg, arge_miibus_readreg),
|
|
|
|
DEVMETHOD(miibus_writereg, arge_miibus_writereg),
|
|
|
|
DEVMETHOD(miibus_statchg, arge_miibus_statchg),
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
/* bus interface */
|
|
|
|
DEVMETHOD(bus_add_child, device_add_child_ordered),
|
|
|
|
DEVMETHOD(bus_hinted_child, arge_hinted_child),
|
|
|
|
|
2011-11-22 21:28:20 +00:00
|
|
|
DEVMETHOD_END
|
2009-04-14 22:53:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t arge_driver = {
|
|
|
|
"arge",
|
|
|
|
arge_methods,
|
|
|
|
sizeof(struct arge_softc)
|
|
|
|
};
|
|
|
|
|
|
|
|
static devclass_t arge_devclass;
|
|
|
|
|
|
|
|
DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0);
|
|
|
|
DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0);
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
#if defined(ARGE_MDIO)
|
|
|
|
static int argemdio_probe(device_t);
|
|
|
|
static int argemdio_attach(device_t);
|
|
|
|
static int argemdio_detach(device_t);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Declare an additional, separate driver for accessing the MDIO bus.
|
|
|
|
*/
|
|
|
|
static device_method_t argemdio_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_probe, argemdio_probe),
|
|
|
|
DEVMETHOD(device_attach, argemdio_attach),
|
|
|
|
DEVMETHOD(device_detach, argemdio_detach),
|
|
|
|
|
|
|
|
/* bus interface */
|
|
|
|
DEVMETHOD(bus_add_child, device_add_child_ordered),
|
|
|
|
|
|
|
|
/* MDIO access */
|
|
|
|
DEVMETHOD(mdio_readreg, arge_miibus_readreg),
|
|
|
|
DEVMETHOD(mdio_writereg, arge_miibus_writereg),
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods,
|
|
|
|
sizeof(struct arge_softc));
|
|
|
|
static devclass_t argemdio_devclass;
|
|
|
|
|
|
|
|
DRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0);
|
|
|
|
DRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0);
|
|
|
|
DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0);
|
|
|
|
#endif
|
|
|
|
|
2009-05-16 02:45:38 +00:00
|
|
|
/*
|
2012-03-06 22:16:10 +00:00
|
|
|
* RedBoot passes MAC address to entry point as environment
|
2009-05-16 02:45:38 +00:00
|
|
|
* variable. platfrom_start parses it and stores in this variable
|
|
|
|
*/
|
|
|
|
extern uint32_t ar711_base_mac[ETHER_ADDR_LEN];
|
|
|
|
|
2009-11-08 07:26:02 +00:00
|
|
|
static struct mtx miibus_mtx;
|
|
|
|
|
2010-04-08 18:32:13 +00:00
|
|
|
MTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF);
|
2009-11-08 07:26:02 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
/*
|
2012-03-06 22:16:10 +00:00
|
|
|
* Flushes all
|
2009-04-14 22:53:22 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
arge_flush_ddr(struct arge_softc *sc)
|
|
|
|
{
|
2011-11-28 13:42:59 +00:00
|
|
|
|
|
|
|
ar71xx_device_flush_ddr_ge(sc->arge_mac_unit);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
static int
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_probe(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
device_set_desc(dev, "Atheros AR71xx built-in ethernet interface");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2010-07-08 14:34:15 +00:00
|
|
|
static void
|
|
|
|
arge_attach_sysctl(device_t dev)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = device_get_softc(dev);
|
|
|
|
struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
|
|
|
|
struct sysctl_oid *tree = device_get_sysctl_tree(dev);
|
|
|
|
|
2011-04-05 05:29:10 +00:00
|
|
|
#ifdef ARGE_DEBUG
|
2010-07-08 14:34:15 +00:00
|
|
|
SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"debug", CTLFLAG_RW, &sc->arge_debug, 0,
|
|
|
|
"arge interface debugging flags");
|
2011-04-05 05:29:10 +00:00
|
|
|
#endif
|
2010-07-08 15:20:57 +00:00
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
|
|
|
"tx_pkts_aligned", CTLFLAG_RW, &sc->stats.tx_pkts_aligned, 0,
|
|
|
|
"number of TX aligned packets");
|
|
|
|
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
|
2012-03-06 22:45:54 +00:00
|
|
|
"tx_pkts_unaligned", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned,
|
|
|
|
0, "number of TX unaligned packets");
|
2011-04-05 05:15:48 +00:00
|
|
|
|
2011-04-05 05:29:10 +00:00
|
|
|
#ifdef ARGE_DEBUG
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_prod",
|
|
|
|
CTLFLAG_RW, &sc->arge_cdata.arge_tx_prod, 0, "");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cons",
|
|
|
|
CTLFLAG_RW, &sc->arge_cdata.arge_tx_cons, 0, "");
|
|
|
|
SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cnt",
|
|
|
|
CTLFLAG_RW, &sc->arge_cdata.arge_tx_cnt, 0, "");
|
|
|
|
#endif
|
2010-07-08 14:34:15 +00:00
|
|
|
}
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
static void
|
|
|
|
arge_reset_mac(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
uint32_t reg;
|
|
|
|
|
|
|
|
/* Step 1. Soft-reset MAC */
|
|
|
|
ARGE_SET_BITS(sc, AR71XX_MAC_CFG1, MAC_CFG1_SOFT_RESET);
|
|
|
|
DELAY(20);
|
|
|
|
|
|
|
|
/* Step 2. Punt the MAC core from the central reset register */
|
|
|
|
ar71xx_device_stop(sc->arge_mac_unit == 0 ? RST_RESET_GE0_MAC :
|
|
|
|
RST_RESET_GE1_MAC);
|
|
|
|
DELAY(100);
|
|
|
|
ar71xx_device_start(sc->arge_mac_unit == 0 ? RST_RESET_GE0_MAC :
|
|
|
|
RST_RESET_GE1_MAC);
|
|
|
|
|
|
|
|
/* Step 3. Reconfigure MAC block */
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_CFG1,
|
|
|
|
MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE |
|
|
|
|
MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE);
|
|
|
|
|
|
|
|
reg = ARGE_READ(sc, AR71XX_MAC_CFG2);
|
|
|
|
reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ;
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg);
|
|
|
|
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_reset_miibus(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* Reset MII bus */
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET);
|
|
|
|
DELAY(100);
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28);
|
|
|
|
DELAY(100);
|
|
|
|
}
|
|
|
|
|
2012-05-02 07:43:11 +00:00
|
|
|
static void
|
|
|
|
arge_fetch_pll_config(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
long int val;
|
|
|
|
|
|
|
|
if (resource_long_value(device_get_name(sc->arge_dev),
|
|
|
|
device_get_unit(sc->arge_dev),
|
|
|
|
"pll_10", &val) == 0) {
|
|
|
|
sc->arge_pllcfg.pll_10 = val;
|
|
|
|
device_printf(sc->arge_dev, "%s: pll_10 = 0x%x\n",
|
|
|
|
__func__, (int) val);
|
|
|
|
}
|
|
|
|
if (resource_long_value(device_get_name(sc->arge_dev),
|
|
|
|
device_get_unit(sc->arge_dev),
|
|
|
|
"pll_100", &val) == 0) {
|
|
|
|
sc->arge_pllcfg.pll_100 = val;
|
|
|
|
device_printf(sc->arge_dev, "%s: pll_100 = 0x%x\n",
|
|
|
|
__func__, (int) val);
|
|
|
|
}
|
|
|
|
if (resource_long_value(device_get_name(sc->arge_dev),
|
|
|
|
device_get_unit(sc->arge_dev),
|
|
|
|
"pll_1000", &val) == 0) {
|
|
|
|
sc->arge_pllcfg.pll_1000 = val;
|
|
|
|
device_printf(sc->arge_dev, "%s: pll_1000 = 0x%x\n",
|
|
|
|
__func__, (int) val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
static int
|
|
|
|
arge_attach(device_t dev)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
struct arge_softc *sc;
|
2012-05-01 06:18:30 +00:00
|
|
|
int error = 0, rid;
|
|
|
|
uint32_t rnd;
|
|
|
|
int is_base_mac_empty, i;
|
2009-11-12 21:27:58 +00:00
|
|
|
uint32_t hint;
|
2011-04-02 03:48:15 +00:00
|
|
|
long eeprom_mac_addr = 0;
|
2012-05-02 06:18:12 +00:00
|
|
|
int miicfg = 0;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
sc->arge_dev = dev;
|
|
|
|
sc->arge_mac_unit = device_get_unit(dev);
|
|
|
|
|
2011-04-02 03:48:15 +00:00
|
|
|
/*
|
|
|
|
* Some units (eg the TP-Link WR-1043ND) do not have a convenient
|
|
|
|
* EEPROM location to read the ethernet MAC address from.
|
|
|
|
* OpenWRT simply snaffles it from a fixed location.
|
|
|
|
*
|
|
|
|
* Since multiple units seem to use this feature, include
|
|
|
|
* a method of setting the MAC address based on an flash location
|
|
|
|
* in CPU address space.
|
|
|
|
*/
|
|
|
|
if (sc->arge_mac_unit == 0 &&
|
2012-03-06 22:16:10 +00:00
|
|
|
resource_long_value(device_get_name(dev), device_get_unit(dev),
|
2011-04-02 03:48:15 +00:00
|
|
|
"eeprommac", &eeprom_mac_addr) == 0) {
|
|
|
|
int i;
|
2012-03-06 22:45:54 +00:00
|
|
|
const char *mac =
|
|
|
|
(const char *) MIPS_PHYS_TO_KSEG1(eeprom_mac_addr);
|
2011-04-02 03:48:15 +00:00
|
|
|
device_printf(dev, "Overriding MAC from EEPROM\n");
|
|
|
|
for (i = 0; i < 6; i++) {
|
|
|
|
ar711_base_mac[i] = mac[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)),
|
2009-04-14 22:53:22 +00:00
|
|
|
("if_arge: Only MAC0 and MAC1 supported"));
|
|
|
|
|
2012-05-02 07:43:11 +00:00
|
|
|
/*
|
|
|
|
* Fetch the PLL configuration.
|
|
|
|
*/
|
|
|
|
arge_fetch_pll_config(sc);
|
|
|
|
|
2012-05-02 06:18:12 +00:00
|
|
|
/*
|
|
|
|
* Get the MII configuration, if applicable.
|
|
|
|
*/
|
|
|
|
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
|
|
|
"miimode", &miicfg) == 0) {
|
|
|
|
/* XXX bounds check? */
|
|
|
|
device_printf(dev, "%s: overriding MII mode to '%s'\n",
|
|
|
|
__func__, arge_miicfg_str[miicfg]);
|
|
|
|
sc->arge_miicfg = miicfg;
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
/*
|
|
|
|
* Get which PHY of 5 available we should use for this unit
|
|
|
|
*/
|
2012-05-01 06:18:30 +00:00
|
|
|
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
|
|
|
"phymask", &sc->arge_phymask) != 0) {
|
2009-04-14 22:53:22 +00:00
|
|
|
/*
|
2012-03-06 22:16:10 +00:00
|
|
|
* Use port 4 (WAN) for GE0. For any other port use
|
|
|
|
* its PHY the same as its unit number
|
2009-04-14 22:53:22 +00:00
|
|
|
*/
|
|
|
|
if (sc->arge_mac_unit == 0)
|
2012-05-01 06:18:30 +00:00
|
|
|
sc->arge_phymask = (1 << 4);
|
2009-04-14 22:53:22 +00:00
|
|
|
else
|
2009-11-12 21:27:58 +00:00
|
|
|
/* Use all phys up to 4 */
|
2012-05-01 06:18:30 +00:00
|
|
|
sc->arge_phymask = (1 << 4) - 1;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
/*
|
2012-03-06 22:16:10 +00:00
|
|
|
* Get default media & duplex mode, by default its Base100T
|
2009-11-12 21:27:58 +00:00
|
|
|
* and full duplex
|
|
|
|
*/
|
2012-03-06 22:16:10 +00:00
|
|
|
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
2009-11-12 21:27:58 +00:00
|
|
|
"media", &hint) != 0)
|
|
|
|
hint = 0;
|
|
|
|
|
|
|
|
if (hint == 1000)
|
|
|
|
sc->arge_media_type = IFM_1000_T;
|
|
|
|
else
|
|
|
|
sc->arge_media_type = IFM_100_TX;
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
|
2009-11-12 21:27:58 +00:00
|
|
|
"fduplex", &hint) != 0)
|
|
|
|
hint = 1;
|
|
|
|
|
|
|
|
if (hint)
|
|
|
|
sc->arge_duplex_mode = IFM_FDX;
|
|
|
|
else
|
|
|
|
sc->arge_duplex_mode = 0;
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
|
|
|
|
MTX_DEF);
|
|
|
|
callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0);
|
|
|
|
TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc);
|
|
|
|
|
|
|
|
/* Map control/status registers. */
|
|
|
|
sc->arge_rid = 0;
|
2012-05-01 06:18:30 +00:00
|
|
|
sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
|
|
&sc->arge_rid, RF_ACTIVE | RF_SHAREABLE);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
if (sc->arge_res == NULL) {
|
|
|
|
device_printf(dev, "couldn't map memory\n");
|
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate interrupts */
|
|
|
|
rid = 0;
|
2012-03-06 22:16:10 +00:00
|
|
|
sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
|
2009-04-14 22:53:22 +00:00
|
|
|
RF_SHAREABLE | RF_ACTIVE);
|
|
|
|
|
|
|
|
if (sc->arge_irq == NULL) {
|
|
|
|
device_printf(dev, "couldn't map interrupt\n");
|
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate ifnet structure. */
|
|
|
|
ifp = sc->arge_ifp = if_alloc(IFT_ETHER);
|
|
|
|
|
|
|
|
if (ifp == NULL) {
|
|
|
|
device_printf(dev, "couldn't allocate ifnet structure\n");
|
|
|
|
error = ENOSPC;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ifp->if_softc = sc;
|
|
|
|
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
|
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
ifp->if_ioctl = arge_ioctl;
|
|
|
|
ifp->if_start = arge_start;
|
|
|
|
ifp->if_init = arge_init;
|
2009-11-04 23:33:36 +00:00
|
|
|
sc->arge_if_flags = ifp->if_flags;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
/* XXX: add real size */
|
2010-05-03 07:32:50 +00:00
|
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
|
|
|
|
ifp->if_snd.ifq_maxlen = ifqmaxlen;
|
2009-04-14 22:53:22 +00:00
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
|
|
|
|
|
|
|
ifp->if_capenable = ifp->if_capabilities;
|
2009-05-26 03:45:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
|
|
|
ifp->if_capabilities |= IFCAP_POLLING;
|
|
|
|
#endif
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-05-16 02:45:38 +00:00
|
|
|
is_base_mac_empty = 1;
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
2012-05-01 06:18:30 +00:00
|
|
|
sc->arge_eaddr[i] = ar711_base_mac[i] & 0xff;
|
|
|
|
if (sc->arge_eaddr[i] != 0)
|
2009-05-16 02:45:38 +00:00
|
|
|
is_base_mac_empty = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_base_mac_empty) {
|
|
|
|
/*
|
|
|
|
* No MAC address configured. Generate the random one.
|
|
|
|
*/
|
2009-11-04 23:34:58 +00:00
|
|
|
if (bootverbose)
|
2012-03-06 22:16:10 +00:00
|
|
|
device_printf(dev,
|
2009-05-16 02:45:38 +00:00
|
|
|
"Generating random ethernet address.\n");
|
|
|
|
|
|
|
|
rnd = arc4random();
|
2012-05-01 06:18:30 +00:00
|
|
|
sc->arge_eaddr[0] = 'b';
|
|
|
|
sc->arge_eaddr[1] = 's';
|
|
|
|
sc->arge_eaddr[2] = 'd';
|
|
|
|
sc->arge_eaddr[3] = (rnd >> 24) & 0xff;
|
|
|
|
sc->arge_eaddr[4] = (rnd >> 16) & 0xff;
|
|
|
|
sc->arge_eaddr[5] = (rnd >> 8) & 0xff;
|
2009-05-16 02:45:38 +00:00
|
|
|
}
|
2009-11-06 06:50:45 +00:00
|
|
|
if (sc->arge_mac_unit != 0)
|
2012-05-01 06:18:30 +00:00
|
|
|
sc->arge_eaddr[5] += sc->arge_mac_unit;
|
2009-11-06 06:50:45 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
if (arge_dma_alloc(sc) != 0) {
|
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
/*
|
|
|
|
* Don't do this for the MDIO bus case - it's already done
|
|
|
|
* as part of the MDIO bus attachment.
|
|
|
|
*/
|
|
|
|
#if !defined(ARGE_MDIO)
|
2009-05-21 22:12:42 +00:00
|
|
|
/* Initialize the MAC block */
|
2012-05-01 06:18:30 +00:00
|
|
|
arge_reset_mac(sc);
|
|
|
|
arge_reset_miibus(sc);
|
|
|
|
#endif
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-02 06:18:12 +00:00
|
|
|
/* Configure MII mode, just for convienence */
|
|
|
|
if (sc->arge_miicfg != 0)
|
|
|
|
ar71xx_device_set_mii_if(sc->arge_mac_unit, sc->arge_miicfg);
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
2009-04-14 22:53:22 +00:00
|
|
|
* Set all Ethernet address registers to the same initial values
|
2012-03-06 22:16:10 +00:00
|
|
|
* set all four addresses to 66-88-aa-cc-dd-ee
|
2009-04-14 22:53:22 +00:00
|
|
|
*/
|
2012-05-01 06:18:30 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24)
|
|
|
|
| (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8)
|
|
|
|
| sc->arge_eaddr[5]);
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8)
|
|
|
|
| sc->arge_eaddr[1]);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0,
|
2009-04-14 22:53:22 +00:00
|
|
|
FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT);
|
2011-03-13 08:28:21 +00:00
|
|
|
|
|
|
|
switch (ar71xx_soc) {
|
|
|
|
case AR71XX_SOC_AR7240:
|
|
|
|
case AR71XX_SOC_AR7241:
|
|
|
|
case AR71XX_SOC_AR7242:
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0010ffff);
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x015500aa);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000);
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff);
|
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH,
|
2009-05-26 03:45:58 +00:00
|
|
|
FIFO_RX_FILTMATCH_DEFAULT);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK,
|
2009-05-26 03:45:58 +00:00
|
|
|
FIFO_RX_FILTMASK_DEFAULT);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
#if defined(ARGE_MDIO)
|
|
|
|
sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev);
|
|
|
|
#endif
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
device_printf(sc->arge_dev, "finishing attachment, phymask %04x"
|
|
|
|
", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ?
|
|
|
|
"null" : "set");
|
|
|
|
for (i = 0; i < ARGE_NPHY; i++) {
|
|
|
|
if (((1 << i) & sc->arge_phymask) != 0) {
|
|
|
|
error = mii_attach(sc->arge_miiproxy != NULL ?
|
|
|
|
sc->arge_miiproxy : sc->arge_dev,
|
|
|
|
&sc->arge_miibus, sc->arge_ifp,
|
|
|
|
arge_ifmedia_upd, arge_ifmedia_sts,
|
|
|
|
BMSR_DEFCAPMASK, i, MII_OFFSET_ANY, 0);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev, "unable to attach"
|
|
|
|
" PHY %d: %d\n", i, error);
|
|
|
|
goto fail;
|
|
|
|
}
|
2009-11-12 21:27:58 +00:00
|
|
|
}
|
|
|
|
}
|
2012-05-01 06:18:30 +00:00
|
|
|
if (sc->arge_miibus == NULL) {
|
|
|
|
/* no PHY, so use hard-coded values */
|
|
|
|
ifmedia_init(&sc->arge_ifmedia, 0,
|
2009-11-12 21:27:58 +00:00
|
|
|
arge_multiphy_mediachange,
|
|
|
|
arge_multiphy_mediastatus);
|
|
|
|
ifmedia_add(&sc->arge_ifmedia,
|
2012-03-06 22:16:10 +00:00
|
|
|
IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode,
|
2009-11-12 21:27:58 +00:00
|
|
|
0, NULL);
|
|
|
|
ifmedia_set(&sc->arge_ifmedia,
|
|
|
|
IFM_ETHER | sc->arge_media_type | sc->arge_duplex_mode);
|
|
|
|
arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode);
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
/* Call MI attach routine. */
|
2012-05-01 06:18:30 +00:00
|
|
|
ether_ifattach(sc->arge_ifp, sc->arge_eaddr);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
/* Hook interrupt last to avoid having to lock softc */
|
2012-05-01 06:18:30 +00:00
|
|
|
error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE,
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_intr_filter, arge_intr, sc, &sc->arge_intrhand);
|
|
|
|
|
|
|
|
if (error) {
|
2012-05-01 06:18:30 +00:00
|
|
|
device_printf(sc->arge_dev, "couldn't set up irq\n");
|
|
|
|
ether_ifdetach(sc->arge_ifp);
|
2009-04-14 22:53:22 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2010-07-08 14:34:15 +00:00
|
|
|
/* setup sysctl variables */
|
2012-05-01 06:18:30 +00:00
|
|
|
arge_attach_sysctl(sc->arge_dev);
|
2010-07-08 14:34:15 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
fail:
|
2012-05-01 06:18:30 +00:00
|
|
|
if (error)
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_detach(dev);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_detach(device_t dev)
|
|
|
|
{
|
2009-05-26 03:45:58 +00:00
|
|
|
struct arge_softc *sc = device_get_softc(dev);
|
2009-04-14 22:53:22 +00:00
|
|
|
struct ifnet *ifp = sc->arge_ifp;
|
|
|
|
|
2012-03-06 22:45:54 +00:00
|
|
|
KASSERT(mtx_initialized(&sc->arge_mtx),
|
|
|
|
("arge mutex not initialized"));
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
/* These should only be active if attach succeeded */
|
|
|
|
if (device_is_attached(dev)) {
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
sc->arge_detach = 1;
|
2009-05-26 03:45:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
|
|
|
if (ifp->if_capenable & IFCAP_POLLING)
|
|
|
|
ether_poll_deregister(ifp);
|
|
|
|
#endif
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_stop(sc);
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
taskqueue_drain(taskqueue_swi, &sc->arge_link_task);
|
|
|
|
ether_ifdetach(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->arge_miibus)
|
|
|
|
device_delete_child(dev, sc->arge_miibus);
|
2009-11-12 21:27:58 +00:00
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
if (sc->arge_miiproxy)
|
|
|
|
device_delete_child(dev, sc->arge_miiproxy);
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
bus_generic_detach(dev);
|
|
|
|
|
|
|
|
if (sc->arge_intrhand)
|
|
|
|
bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand);
|
|
|
|
|
|
|
|
if (sc->arge_res)
|
2012-03-06 22:16:10 +00:00
|
|
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid,
|
2009-04-14 22:53:22 +00:00
|
|
|
sc->arge_res);
|
|
|
|
|
|
|
|
if (ifp)
|
|
|
|
if_free(ifp);
|
|
|
|
|
|
|
|
arge_dma_free(sc);
|
|
|
|
|
|
|
|
mtx_destroy(&sc->arge_mtx);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_suspend(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
panic("%s", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_resume(device_t dev)
|
|
|
|
{
|
|
|
|
|
|
|
|
panic("%s", __func__);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-06-12 12:17:32 +00:00
|
|
|
static int
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_shutdown(device_t dev)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
arge_stop(sc);
|
|
|
|
ARGE_UNLOCK(sc);
|
2009-06-12 12:17:32 +00:00
|
|
|
|
|
|
|
return (0);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
static void
|
|
|
|
arge_hinted_child(device_t bus, const char *dname, int dunit)
|
|
|
|
{
|
|
|
|
BUS_ADD_CHILD(bus, 0, dname, dunit);
|
|
|
|
device_printf(bus, "hinted child %s%d\n", dname, dunit);
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
static int
|
|
|
|
arge_miibus_readreg(device_t dev, int phy, int reg)
|
|
|
|
{
|
|
|
|
struct arge_softc * sc = device_get_softc(dev);
|
|
|
|
int i, result;
|
2012-03-06 22:16:10 +00:00
|
|
|
uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT)
|
2009-04-14 22:53:22 +00:00
|
|
|
| (reg & MAC_MII_REG_MASK);
|
|
|
|
|
2009-11-08 07:26:02 +00:00
|
|
|
mtx_lock(&miibus_mtx);
|
2012-05-01 06:18:30 +00:00
|
|
|
ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
|
|
|
|
ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
|
|
|
|
ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
i = ARGE_MII_TIMEOUT;
|
2012-05-01 06:18:30 +00:00
|
|
|
while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) &
|
2009-04-14 22:53:22 +00:00
|
|
|
MAC_MII_INDICATOR_BUSY) && (i--))
|
|
|
|
DELAY(5);
|
|
|
|
|
|
|
|
if (i < 0) {
|
2009-11-08 07:26:02 +00:00
|
|
|
mtx_unlock(&miibus_mtx);
|
2011-04-05 05:15:48 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__);
|
2009-04-14 22:53:22 +00:00
|
|
|
/* XXX: return ERRNO istead? */
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK;
|
|
|
|
ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
|
2009-11-08 07:26:02 +00:00
|
|
|
mtx_unlock(&miibus_mtx);
|
|
|
|
|
2012-03-06 22:45:54 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_MII,
|
|
|
|
"%s: phy=%d, reg=%02x, value[%08x]=%04x\n",
|
|
|
|
__func__, phy, reg, addr, result);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
return (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_miibus_writereg(device_t dev, int phy, int reg, int data)
|
|
|
|
{
|
|
|
|
struct arge_softc * sc = device_get_softc(dev);
|
|
|
|
int i;
|
2012-03-06 22:16:10 +00:00
|
|
|
uint32_t addr =
|
2009-09-03 18:23:23 +00:00
|
|
|
(phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__,
|
|
|
|
phy, reg, data);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-11-08 07:26:02 +00:00
|
|
|
mtx_lock(&miibus_mtx);
|
2012-05-01 06:18:30 +00:00
|
|
|
ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
|
|
|
|
ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
i = ARGE_MII_TIMEOUT;
|
2012-05-01 06:18:30 +00:00
|
|
|
while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) &
|
2009-04-14 22:53:22 +00:00
|
|
|
MAC_MII_INDICATOR_BUSY) && (i--))
|
|
|
|
DELAY(5);
|
|
|
|
|
2009-11-08 07:26:02 +00:00
|
|
|
mtx_unlock(&miibus_mtx);
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
if (i < 0) {
|
2011-04-05 05:15:48 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__);
|
2009-04-14 22:53:22 +00:00
|
|
|
/* XXX: return ERRNO istead? */
|
|
|
|
return (-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_miibus_statchg(device_t dev)
|
|
|
|
{
|
2012-03-06 22:16:10 +00:00
|
|
|
struct arge_softc *sc;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_link_task(void *arg, int pending)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc;
|
Fix link status handling on if_arge upon system boot to allow bootp/NFS to
function.
From the submitter:
This patch fixes an issue I encountered using an NFS root with an
ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
to. This may be the same issue that Monthadar Al Jaberi reported against
an RSPRO on 6 March, as the signature is the same:
%%%
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
.
.
.
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
%%%
The primary issue that I found is that the DHCP/BOOTP message that
bootpc_call() is sending never makes it onto the wire, which I believe is
due to the following:
- Last December, a change was made to the ifioctl that bootpc_call() uses
to adjust the netmask around the sosend().
- The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
old one (SIOCSIFNETMASK) did not.
- if_arge maintains its own sense of link state in sc->arge_link_status.
- On a single-phy interface, sc->arge_link_status is initialized to 0 in
arge_init_locked().
- sc->arge_link_status remains 0 until a phy state change notification
causes arge_link_task to run, notice the link is up, and set it to 1.
- The inits caused by the ifioctls in bootpc_call are reinitializing the
interface, but not the phy, so sc->arge_link_status goes to 0 and remains
there.
- arge_start_locked() always sees sc->arge_link_status == 0 and returns
without queuing anything.
The attached patch changes arge_init_locked() such that in the single-phy
case, instead of initializing sc->arge_link_status to 0, it runs
arge_link_task() to set it according to the current phy state. This change
has allowed my setup to mount an NFS root successfully.
Submitted by: Patrick Kelsey <kelsey@ieee.org>
Reviewed by: juli
2012-03-13 06:28:52 +00:00
|
|
|
sc = (struct arge_softc *)arg;
|
|
|
|
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
arge_update_link_locked(sc);
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_update_link_locked(struct arge_softc *sc)
|
|
|
|
{
|
2009-04-14 22:53:22 +00:00
|
|
|
struct mii_data *mii;
|
|
|
|
struct ifnet *ifp;
|
2009-11-12 21:27:58 +00:00
|
|
|
uint32_t media, duplex;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
mii = device_get_softc(sc->arge_miibus);
|
|
|
|
ifp = sc->arge_ifp;
|
|
|
|
if (mii == NULL || ifp == NULL ||
|
|
|
|
(ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mii->mii_media_status & IFM_ACTIVE) {
|
|
|
|
|
|
|
|
media = IFM_SUBTYPE(mii->mii_media_active);
|
|
|
|
if (media != IFM_NONE) {
|
|
|
|
sc->arge_link_status = 1;
|
2009-11-12 21:27:58 +00:00
|
|
|
duplex = mii->mii_media_active & IFM_GMASK;
|
2012-05-02 04:51:43 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_MII, "%s: media=%d, duplex=%d\n",
|
|
|
|
__func__,
|
|
|
|
media,
|
|
|
|
duplex);
|
2009-11-12 21:27:58 +00:00
|
|
|
arge_set_pll(sc, media, duplex);
|
|
|
|
}
|
Fix link status handling on if_arge upon system boot to allow bootp/NFS to
function.
From the submitter:
This patch fixes an issue I encountered using an NFS root with an
ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
to. This may be the same issue that Monthadar Al Jaberi reported against
an RSPRO on 6 March, as the signature is the same:
%%%
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
.
.
.
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
%%%
The primary issue that I found is that the DHCP/BOOTP message that
bootpc_call() is sending never makes it onto the wire, which I believe is
due to the following:
- Last December, a change was made to the ifioctl that bootpc_call() uses
to adjust the netmask around the sosend().
- The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
old one (SIOCSIFNETMASK) did not.
- if_arge maintains its own sense of link state in sc->arge_link_status.
- On a single-phy interface, sc->arge_link_status is initialized to 0 in
arge_init_locked().
- sc->arge_link_status remains 0 until a phy state change notification
causes arge_link_task to run, notice the link is up, and set it to 1.
- The inits caused by the ifioctls in bootpc_call are reinitializing the
interface, but not the phy, so sc->arge_link_status goes to 0 and remains
there.
- arge_start_locked() always sees sc->arge_link_status == 0 and returns
without queuing anything.
The attached patch changes arge_init_locked() such that in the single-phy
case, instead of initializing sc->arge_link_status to 0, it runs
arge_link_task() to set it according to the current phy state. This change
has allowed my setup to mount an NFS root successfully.
Submitted by: Patrick Kelsey <kelsey@ieee.org>
Reviewed by: juli
2012-03-13 06:28:52 +00:00
|
|
|
} else {
|
2009-11-12 21:27:58 +00:00
|
|
|
sc->arge_link_status = 0;
|
Fix link status handling on if_arge upon system boot to allow bootp/NFS to
function.
From the submitter:
This patch fixes an issue I encountered using an NFS root with an
ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
to. This may be the same issue that Monthadar Al Jaberi reported against
an RSPRO on 6 March, as the signature is the same:
%%%
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
.
.
.
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
%%%
The primary issue that I found is that the DHCP/BOOTP message that
bootpc_call() is sending never makes it onto the wire, which I believe is
due to the following:
- Last December, a change was made to the ifioctl that bootpc_call() uses
to adjust the netmask around the sosend().
- The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
old one (SIOCSIFNETMASK) did not.
- if_arge maintains its own sense of link state in sc->arge_link_status.
- On a single-phy interface, sc->arge_link_status is initialized to 0 in
arge_init_locked().
- sc->arge_link_status remains 0 until a phy state change notification
causes arge_link_task to run, notice the link is up, and set it to 1.
- The inits caused by the ifioctls in bootpc_call are reinitializing the
interface, but not the phy, so sc->arge_link_status goes to 0 and remains
there.
- arge_start_locked() always sees sc->arge_link_status == 0 and returns
without queuing anything.
The attached patch changes arge_init_locked() such that in the single-phy
case, instead of initializing sc->arge_link_status to 0, it runs
arge_link_task() to set it according to the current phy state. This change
has allowed my setup to mount an NFS root successfully.
Submitted by: Patrick Kelsey <kelsey@ieee.org>
Reviewed by: juli
2012-03-13 06:28:52 +00:00
|
|
|
}
|
2009-11-12 21:27:58 +00:00
|
|
|
}
|
2009-05-26 03:45:58 +00:00
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
static void
|
|
|
|
arge_set_pll(struct arge_softc *sc, int media, int duplex)
|
|
|
|
{
|
2010-08-19 16:29:08 +00:00
|
|
|
uint32_t cfg, ifcontrol, rx_filtmask;
|
2012-05-02 04:51:43 +00:00
|
|
|
uint32_t fifo_tx, pll;
|
2010-08-19 16:29:08 +00:00
|
|
|
int if_speed;
|
2009-05-26 03:45:58 +00:00
|
|
|
|
2012-05-02 07:43:11 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_PLL, "set_pll(%04x, %s)\n", media,
|
2012-05-01 06:18:30 +00:00
|
|
|
duplex == IFM_FDX ? "full" : "half");
|
2009-11-12 21:27:58 +00:00
|
|
|
cfg = ARGE_READ(sc, AR71XX_MAC_CFG2);
|
2012-03-06 22:16:10 +00:00
|
|
|
cfg &= ~(MAC_CFG2_IFACE_MODE_1000
|
|
|
|
| MAC_CFG2_IFACE_MODE_10_100
|
2009-11-12 21:27:58 +00:00
|
|
|
| MAC_CFG2_FULL_DUPLEX);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
if (duplex == IFM_FDX)
|
|
|
|
cfg |= MAC_CFG2_FULL_DUPLEX;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL);
|
|
|
|
ifcontrol &= ~MAC_IFCONTROL_SPEED;
|
2012-03-06 22:16:10 +00:00
|
|
|
rx_filtmask =
|
2009-11-12 21:27:58 +00:00
|
|
|
ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK);
|
|
|
|
rx_filtmask &= ~FIFO_RX_MASK_BYTE_MODE;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
switch(media) {
|
|
|
|
case IFM_10_T:
|
|
|
|
cfg |= MAC_CFG2_IFACE_MODE_10_100;
|
2010-08-19 16:29:08 +00:00
|
|
|
if_speed = 10;
|
2009-11-12 21:27:58 +00:00
|
|
|
break;
|
|
|
|
case IFM_100_TX:
|
|
|
|
cfg |= MAC_CFG2_IFACE_MODE_10_100;
|
|
|
|
ifcontrol |= MAC_IFCONTROL_SPEED;
|
2010-08-19 16:29:08 +00:00
|
|
|
if_speed = 100;
|
2009-11-12 21:27:58 +00:00
|
|
|
break;
|
|
|
|
case IFM_1000_T:
|
|
|
|
case IFM_1000_SX:
|
|
|
|
cfg |= MAC_CFG2_IFACE_MODE_1000;
|
|
|
|
rx_filtmask |= FIFO_RX_MASK_BYTE_MODE;
|
2010-08-19 16:29:08 +00:00
|
|
|
if_speed = 1000;
|
2009-11-12 21:27:58 +00:00
|
|
|
break;
|
|
|
|
default:
|
2010-08-19 16:29:08 +00:00
|
|
|
if_speed = 100;
|
2012-03-06 22:16:10 +00:00
|
|
|
device_printf(sc->arge_dev,
|
2009-11-12 21:27:58 +00:00
|
|
|
"Unknown media %d\n", media);
|
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-02 07:43:11 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: if_speed=%d\n", __func__, if_speed);
|
2012-05-02 04:51:43 +00:00
|
|
|
|
2011-03-13 08:28:21 +00:00
|
|
|
switch (ar71xx_soc) {
|
|
|
|
case AR71XX_SOC_AR7240:
|
|
|
|
case AR71XX_SOC_AR7241:
|
|
|
|
case AR71XX_SOC_AR7242:
|
|
|
|
fifo_tx = 0x01f00140;
|
|
|
|
break;
|
|
|
|
case AR71XX_SOC_AR9130:
|
|
|
|
case AR71XX_SOC_AR9132:
|
|
|
|
fifo_tx = 0x00780fff;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fifo_tx = 0x008001ff;
|
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg);
|
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol);
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK,
|
2009-11-12 21:27:58 +00:00
|
|
|
rx_filtmask);
|
2011-03-13 08:28:21 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, fifo_tx);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-05-02 07:43:11 +00:00
|
|
|
/* fetch PLL registers */
|
2012-05-02 04:51:43 +00:00
|
|
|
pll = ar71xx_device_get_eth_pll(sc->arge_mac_unit, if_speed);
|
2012-05-02 07:43:11 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: pll=0x%x\n", __func__, pll);
|
|
|
|
|
|
|
|
/* Override if required by platform data */
|
|
|
|
if (if_speed == 10 && sc->arge_pllcfg.pll_10 != 0)
|
|
|
|
pll = sc->arge_pllcfg.pll_10;
|
|
|
|
else if (if_speed == 100 && sc->arge_pllcfg.pll_100 != 0)
|
|
|
|
pll = sc->arge_pllcfg.pll_100;
|
|
|
|
else if (if_speed == 1000 && sc->arge_pllcfg.pll_1000 != 0)
|
|
|
|
pll = sc->arge_pllcfg.pll_1000;
|
|
|
|
ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: final pll=0x%x\n", __func__, pll);
|
2012-05-02 04:51:43 +00:00
|
|
|
|
|
|
|
/* XXX ensure pll != 0 */
|
|
|
|
ar71xx_device_set_pll_ge(sc->arge_mac_unit, if_speed, pll);
|
|
|
|
|
|
|
|
/* set MII registers */
|
2012-05-04 02:26:15 +00:00
|
|
|
/*
|
|
|
|
* This was introduced to match what the Linux ag71xx ethernet
|
|
|
|
* driver does. For the AR71xx case, it does set the port
|
|
|
|
* MII speed. However, if this is done, non-gigabit speeds
|
|
|
|
* are not at all reliable when speaking via RGMII through
|
|
|
|
* 'bridge' PHY port that's pretending to be a local PHY.
|
|
|
|
*
|
|
|
|
* Until that gets root caused, and until an AR71xx + normal
|
|
|
|
* PHY board is tested, leave this disabled.
|
|
|
|
*/
|
|
|
|
#if 0
|
2012-05-02 04:51:43 +00:00
|
|
|
ar71xx_device_set_mii_speed(sc->arge_mac_unit, if_speed);
|
2012-05-04 02:26:15 +00:00
|
|
|
#endif
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
static void
|
|
|
|
arge_reset_dma(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0);
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0);
|
|
|
|
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0);
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0);
|
|
|
|
|
|
|
|
/* Clear all possible RX interrupts */
|
2009-05-21 22:12:42 +00:00
|
|
|
while(ARGE_READ(sc, AR71XX_DMA_RX_STATUS) & DMA_RX_STATUS_PKT_RECVD)
|
2009-04-14 22:53:22 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
2009-04-14 22:53:22 +00:00
|
|
|
* Clear all possible TX interrupts
|
|
|
|
*/
|
2009-05-21 22:12:42 +00:00
|
|
|
while(ARGE_READ(sc, AR71XX_DMA_TX_STATUS) & DMA_TX_STATUS_PKT_SENT)
|
2009-04-14 22:53:22 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
2009-04-14 22:53:22 +00:00
|
|
|
* Now Rx/Tx errors
|
|
|
|
*/
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS,
|
2009-04-14 22:53:22 +00:00
|
|
|
DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW);
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS,
|
2009-04-14 22:53:22 +00:00
|
|
|
DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN);
|
2012-03-13 06:15:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Force a DDR flush so any pending data is properly
|
|
|
|
* flushed to RAM before underlying buffers are freed.
|
|
|
|
*/
|
|
|
|
arge_flush_ddr(sc);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_init(void *xsc)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = xsc;
|
|
|
|
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
arge_init_locked(sc);
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_init_locked(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp = sc->arge_ifp;
|
|
|
|
struct mii_data *mii;
|
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
arge_stop(sc);
|
|
|
|
|
|
|
|
/* Init circular RX list. */
|
|
|
|
if (arge_rx_ring_init(sc) != 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"initialization failed: no memory for rx buffers\n");
|
|
|
|
arge_stop(sc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init tx descriptors. */
|
|
|
|
arge_tx_ring_init(sc);
|
|
|
|
|
|
|
|
arge_reset_dma(sc);
|
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
if (sc->arge_miibus) {
|
|
|
|
mii = device_get_softc(sc->arge_miibus);
|
|
|
|
mii_mediachg(mii);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/*
|
|
|
|
* Sun always shines over multiPHY interface
|
|
|
|
*/
|
|
|
|
sc->arge_link_status = 1;
|
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
|
|
|
Fix link status handling on if_arge upon system boot to allow bootp/NFS to
function.
From the submitter:
This patch fixes an issue I encountered using an NFS root with an
ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
to. This may be the same issue that Monthadar Al Jaberi reported against
an RSPRO on 6 March, as the signature is the same:
%%%
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
.
.
.
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
%%%
The primary issue that I found is that the DHCP/BOOTP message that
bootpc_call() is sending never makes it onto the wire, which I believe is
due to the following:
- Last December, a change was made to the ifioctl that bootpc_call() uses
to adjust the netmask around the sosend().
- The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
old one (SIOCSIFNETMASK) did not.
- if_arge maintains its own sense of link state in sc->arge_link_status.
- On a single-phy interface, sc->arge_link_status is initialized to 0 in
arge_init_locked().
- sc->arge_link_status remains 0 until a phy state change notification
causes arge_link_task to run, notice the link is up, and set it to 1.
- The inits caused by the ifioctls in bootpc_call are reinitializing the
interface, but not the phy, so sc->arge_link_status goes to 0 and remains
there.
- arge_start_locked() always sees sc->arge_link_status == 0 and returns
without queuing anything.
The attached patch changes arge_init_locked() such that in the single-phy
case, instead of initializing sc->arge_link_status to 0, it runs
arge_link_task() to set it according to the current phy state. This change
has allowed my setup to mount an NFS root successfully.
Submitted by: Patrick Kelsey <kelsey@ieee.org>
Reviewed by: juli
2012-03-13 06:28:52 +00:00
|
|
|
if (sc->arge_miibus) {
|
2009-11-12 21:27:58 +00:00
|
|
|
callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
|
Fix link status handling on if_arge upon system boot to allow bootp/NFS to
function.
From the submitter:
This patch fixes an issue I encountered using an NFS root with an
ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
to. This may be the same issue that Monthadar Al Jaberi reported against
an RSPRO on 6 March, as the signature is the same:
%%%
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
.
.
.
DHCP/BOOTP timeout for server 255.255.255.255
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
DHCP/BOOTP timeout for server 255.255.255.255
arge0: initialization failed: no memory for rx buffers
%%%
The primary issue that I found is that the DHCP/BOOTP message that
bootpc_call() is sending never makes it onto the wire, which I believe is
due to the following:
- Last December, a change was made to the ifioctl that bootpc_call() uses
to adjust the netmask around the sosend().
- The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
old one (SIOCSIFNETMASK) did not.
- if_arge maintains its own sense of link state in sc->arge_link_status.
- On a single-phy interface, sc->arge_link_status is initialized to 0 in
arge_init_locked().
- sc->arge_link_status remains 0 until a phy state change notification
causes arge_link_task to run, notice the link is up, and set it to 1.
- The inits caused by the ifioctls in bootpc_call are reinitializing the
interface, but not the phy, so sc->arge_link_status goes to 0 and remains
there.
- arge_start_locked() always sees sc->arge_link_status == 0 and returns
without queuing anything.
The attached patch changes arge_init_locked() such that in the single-phy
case, instead of initializing sc->arge_link_status to 0, it runs
arge_link_task() to set it according to the current phy state. This change
has allowed my setup to mount an NFS root successfully.
Submitted by: Patrick Kelsey <kelsey@ieee.org>
Reviewed by: juli
2012-03-13 06:28:52 +00:00
|
|
|
arge_update_link_locked(sc);
|
|
|
|
}
|
2009-05-26 03:45:58 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0));
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0));
|
|
|
|
|
|
|
|
/* Start listening */
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN);
|
|
|
|
|
|
|
|
/* Enable interrupts */
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
|
|
|
|
}
|
|
|
|
|
2010-07-08 14:59:32 +00:00
|
|
|
/*
|
|
|
|
* Return whether the mbuf chain is correctly aligned
|
|
|
|
* for the arge TX engine.
|
|
|
|
*
|
|
|
|
* The TX engine requires each fragment to be aligned to a
|
|
|
|
* 4 byte boundary and the size of each fragment except
|
|
|
|
* the last to be a multiple of 4 bytes.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
arge_mbuf_chain_is_tx_aligned(struct mbuf *m0)
|
|
|
|
{
|
|
|
|
struct mbuf *m;
|
|
|
|
|
|
|
|
for (m = m0; m != NULL; m = m->m_next) {
|
|
|
|
if((mtod(m, intptr_t) & 3) != 0)
|
|
|
|
return 0;
|
|
|
|
if ((m->m_next != NULL) && ((m->m_len & 0x03) != 0))
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
/*
|
|
|
|
* Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
|
|
|
|
* pointers to the fragment pointers.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
arge_encap(struct arge_softc *sc, struct mbuf **m_head)
|
|
|
|
{
|
|
|
|
struct arge_txdesc *txd;
|
|
|
|
struct arge_desc *desc, *prev_desc;
|
|
|
|
bus_dma_segment_t txsegs[ARGE_MAXFRAGS];
|
2009-05-21 22:12:42 +00:00
|
|
|
int error, i, nsegs, prod, prev_prod;
|
2009-05-26 03:45:58 +00:00
|
|
|
struct mbuf *m;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
/*
|
|
|
|
* Fix mbuf chain, all fragments should be 4 bytes aligned and
|
|
|
|
* even 4 bytes
|
|
|
|
*/
|
|
|
|
m = *m_head;
|
2010-07-08 14:59:32 +00:00
|
|
|
if (! arge_mbuf_chain_is_tx_aligned(m)) {
|
2010-07-08 15:20:57 +00:00
|
|
|
sc->stats.tx_pkts_unaligned++;
|
2012-12-05 08:04:20 +00:00
|
|
|
m = m_defrag(*m_head, M_NOWAIT);
|
2009-05-26 03:45:58 +00:00
|
|
|
if (m == NULL) {
|
|
|
|
*m_head = NULL;
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
*m_head = m;
|
2010-07-08 15:20:57 +00:00
|
|
|
} else
|
|
|
|
sc->stats.tx_pkts_aligned++;
|
2009-05-26 03:45:58 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
prod = sc->arge_cdata.arge_tx_prod;
|
|
|
|
txd = &sc->arge_cdata.arge_txdesc[prod];
|
2012-03-06 22:16:10 +00:00
|
|
|
error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag,
|
2009-04-14 22:53:22 +00:00
|
|
|
txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT);
|
|
|
|
|
|
|
|
if (error == EFBIG) {
|
|
|
|
panic("EFBIG");
|
|
|
|
} else if (error != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
if (nsegs == 0) {
|
|
|
|
m_freem(*m_head);
|
|
|
|
*m_head = NULL;
|
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check number of available descriptors. */
|
|
|
|
if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) {
|
|
|
|
bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
|
|
|
|
txd->tx_m = *m_head;
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
2009-04-14 22:53:22 +00:00
|
|
|
* Make a list of descriptors for this packet. DMA controller will
|
|
|
|
* walk through it while arge_link is not zero.
|
|
|
|
*/
|
|
|
|
prev_prod = prod;
|
|
|
|
desc = prev_desc = NULL;
|
|
|
|
for (i = 0; i < nsegs; i++) {
|
|
|
|
desc = &sc->arge_rdata.arge_tx_ring[prod];
|
|
|
|
desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len);
|
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
if (txsegs[i].ds_addr & 3)
|
|
|
|
panic("TX packet address unaligned\n");
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
desc->packet_addr = txsegs[i].ds_addr;
|
2012-03-06 22:16:10 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
/* link with previous descriptor */
|
|
|
|
if (prev_desc)
|
|
|
|
prev_desc->packet_ctrl |= ARGE_DESC_MORE;
|
|
|
|
|
|
|
|
sc->arge_cdata.arge_tx_cnt++;
|
|
|
|
prev_desc = desc;
|
|
|
|
ARGE_INC(prod, ARGE_TX_RING_COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update producer index. */
|
|
|
|
sc->arge_cdata.arge_tx_prod = prod;
|
|
|
|
|
|
|
|
/* Sync descriptors. */
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map,
|
|
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
/* Start transmitting */
|
2012-03-06 22:45:54 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_TX, "%s: setting DMA_TX_CONTROL_EN\n",
|
|
|
|
__func__);
|
2009-04-14 22:53:22 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_start(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc;
|
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
arge_start_locked(ifp);
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_start_locked(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc;
|
|
|
|
struct mbuf *m_head;
|
2011-04-05 06:33:35 +00:00
|
|
|
int enq = 0;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
2011-04-05 06:33:35 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_TX, "%s: beginning\n", __func__);
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
|
|
|
|
IFF_DRV_RUNNING || sc->arge_link_status == 0 )
|
|
|
|
return;
|
|
|
|
|
2011-04-05 06:33:35 +00:00
|
|
|
/*
|
|
|
|
* Before we go any further, check whether we're already full.
|
|
|
|
* The below check errors out immediately if the ring is full
|
|
|
|
* and never gets a chance to set this flag. Although it's
|
|
|
|
* likely never needed, this at least avoids an unexpected
|
|
|
|
* situation.
|
|
|
|
*/
|
|
|
|
if (sc->arge_cdata.arge_tx_cnt >= ARGE_TX_RING_COUNT - 2) {
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
2012-03-06 22:45:54 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_ERR,
|
|
|
|
"%s: tx_cnt %d >= max %d; setting IFF_DRV_OACTIVE\n",
|
|
|
|
__func__, sc->arge_cdata.arge_tx_cnt,
|
|
|
|
ARGE_TX_RING_COUNT - 2);
|
2011-04-05 06:33:35 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_flush_ddr(sc);
|
|
|
|
|
|
|
|
for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
|
|
|
|
sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) {
|
|
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
|
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pack the data into the transmit ring.
|
|
|
|
*/
|
|
|
|
if (arge_encap(sc, &m_head)) {
|
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
enq++;
|
|
|
|
/*
|
|
|
|
* If there's a BPF listener, bounce a copy of this frame
|
|
|
|
* to him.
|
|
|
|
*/
|
|
|
|
ETHER_BPF_MTAP(ifp, m_head);
|
|
|
|
}
|
2012-03-06 22:45:54 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_TX, "%s: finished; queued %d packets\n",
|
|
|
|
__func__, enq);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_stop(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
ifp = sc->arge_ifp;
|
|
|
|
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
|
2009-11-12 21:27:58 +00:00
|
|
|
if (sc->arge_miibus)
|
|
|
|
callout_stop(&sc->arge_stat_callout);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
/* mask out interrupts */
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
|
|
|
|
|
|
|
|
arge_reset_dma(sc);
|
2012-03-13 06:15:20 +00:00
|
|
|
|
|
|
|
/* Flush FIFO and free any existing mbufs */
|
|
|
|
arge_flush_ddr(sc);
|
|
|
|
arge_rx_ring_free(sc);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = ifp->if_softc;
|
|
|
|
struct ifreq *ifr = (struct ifreq *) data;
|
|
|
|
struct mii_data *mii;
|
|
|
|
int error;
|
2009-05-26 03:45:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
|
|
|
int mask;
|
|
|
|
#endif
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
switch (command) {
|
|
|
|
case SIOCSIFFLAGS:
|
2009-11-04 23:33:36 +00:00
|
|
|
ARGE_LOCK(sc);
|
|
|
|
if ((ifp->if_flags & IFF_UP) != 0) {
|
|
|
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
|
|
|
|
if (((ifp->if_flags ^ sc->arge_if_flags)
|
2009-11-05 03:54:03 +00:00
|
|
|
& (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
|
|
|
|
/* XXX: handle promisc & multi flags */
|
|
|
|
}
|
2012-03-06 22:16:10 +00:00
|
|
|
|
2009-11-04 23:33:36 +00:00
|
|
|
} else {
|
|
|
|
if (!sc->arge_detach)
|
|
|
|
arge_init_locked(sc);
|
|
|
|
}
|
|
|
|
} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
|
|
|
|
arge_stop(sc);
|
|
|
|
}
|
|
|
|
sc->arge_if_flags = ifp->if_flags;
|
|
|
|
ARGE_UNLOCK(sc);
|
2009-04-14 22:53:22 +00:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
2009-11-04 23:33:36 +00:00
|
|
|
/* XXX: implement SIOCDELMULTI */
|
2009-04-14 22:53:22 +00:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
|
|
case SIOCSIFMEDIA:
|
2009-11-12 21:27:58 +00:00
|
|
|
if (sc->arge_miibus) {
|
|
|
|
mii = device_get_softc(sc->arge_miibus);
|
2012-03-06 22:45:54 +00:00
|
|
|
error = ifmedia_ioctl(ifp, ifr, &mii->mii_media,
|
|
|
|
command);
|
2009-11-12 21:27:58 +00:00
|
|
|
}
|
2012-03-06 22:16:10 +00:00
|
|
|
else
|
2012-03-06 22:45:54 +00:00
|
|
|
error = ifmedia_ioctl(ifp, ifr, &sc->arge_ifmedia,
|
|
|
|
command);
|
2009-04-14 22:53:22 +00:00
|
|
|
break;
|
2009-11-04 23:34:58 +00:00
|
|
|
case SIOCSIFCAP:
|
2009-11-04 23:33:36 +00:00
|
|
|
/* XXX: Check other capabilities */
|
2009-05-26 03:45:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
2009-11-04 23:34:58 +00:00
|
|
|
mask = ifp->if_capenable ^ ifr->ifr_reqcap;
|
|
|
|
if (mask & IFCAP_POLLING) {
|
|
|
|
if (ifr->ifr_reqcap & IFCAP_POLLING) {
|
2009-05-26 03:45:58 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
|
2009-11-04 23:34:58 +00:00
|
|
|
error = ether_poll_register(arge_poll, ifp);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
ifp->if_capenable |= IFCAP_POLLING;
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
} else {
|
2009-05-26 03:45:58 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
|
2009-11-04 23:34:58 +00:00
|
|
|
error = ether_poll_deregister(ifp);
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
ifp->if_capenable &= ~IFCAP_POLLING;
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
}
|
2009-11-04 23:33:36 +00:00
|
|
|
error = 0;
|
2009-11-04 23:34:58 +00:00
|
|
|
break;
|
2009-05-26 03:45:58 +00:00
|
|
|
#endif
|
2009-04-14 22:53:22 +00:00
|
|
|
default:
|
|
|
|
error = ether_ioctl(ifp, command, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set media options.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
arge_ifmedia_upd(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc;
|
|
|
|
struct mii_data *mii;
|
|
|
|
struct mii_softc *miisc;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = ifp->if_softc;
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
mii = device_get_softc(sc->arge_miibus);
|
- Remove attempts to implement setting of BMCR_LOOP/MIIF_NOLOOP
(reporting IFM_LOOP based on BMCR_LOOP is left in place though as
it might provide useful for debugging). For most mii(4) drivers it
was unclear whether the PHYs driven by them actually support
loopback or not. Moreover, typically loopback mode also needs to
be activated on the MAC, which none of the Ethernet drivers using
mii(4) implements. Given that loopback media has no real use (and
obviously hardly had a chance to actually work) besides for driver
development (which just loopback mode should be sufficient for
though, i.e one doesn't necessary need support for loopback media)
support for it is just dropped as both NetBSD and OpenBSD already
did quite some time ago.
- Let mii_phy_add_media() also announce the support of IFM_NONE.
- Restructure the PHY entry points to use a structure of entry points
instead of discrete function pointers, and extend this to include
a "reset" entry point. Make sure any PHY-specific reset routine is
always used, and provide one for lxtphy(4) which disables MII
interrupts (as is done for a few other PHYs we have drivers for).
This includes changing NIC drivers which previously just called the
generic mii_phy_reset() to now actually call the PHY-specific reset
routine, which might be crucial in some cases. While at it, the
redundant checks in these NIC drivers for mii->mii_instance not being
zero before calling the reset routines were removed because as soon
as one PHY driver attaches mii->mii_instance is incremented and we
hardly can end up in their media change callbacks etc if no PHY driver
has attached as mii_attach() would have failed in that case and not
attach a miibus(4) instance.
Consequently, NIC drivers now no longer should call mii_phy_reset()
directly, so it was removed from EXPORT_SYMS.
- Add a mii_phy_dev_attach() as a companion helper to mii_phy_dev_probe().
The purpose of that function is to perform the common steps to attach
a PHY driver instance and to hook it up to the miibus(4) instance and to
optionally also handle the probing, addition and initialization of the
supported media. So all a PHY driver without any special requirements
has to do in its bus attach method is to call mii_phy_dev_attach()
along with PHY-specific MIIF_* flags, a pointer to its PHY functions
and the add_media set to one. All PHY drivers were updated to take
advantage of mii_phy_dev_attach() as appropriate. Along with these
changes the capability mask was added to the mii_softc structure so
PHY drivers taking advantage of mii_phy_dev_attach() but still
handling media on their own do not need to fiddle with the MII attach
arguments anyway.
- Keep track of the PHY offset in the mii_softc structure. This is done
for compatibility with NetBSD/OpenBSD.
- Keep track of the PHY's OUI, model and revision in the mii_softc
structure. Several PHY drivers require this information also after
attaching and previously had to wrap their own softc around mii_softc.
NetBSD/OpenBSD also keep track of the model and revision on their
mii_softc structure. All PHY drivers were updated to take advantage
as appropriate.
- Convert the mebers of the MII data structure to unsigned where
appropriate. This is partly inspired by NetBSD/OpenBSD.
- According to IEEE 802.3-2002 the bits actually have to be reversed
when mapping an OUI to the MII ID registers. All PHY drivers and
miidevs where changed as necessary. Actually this now again allows to
largely share miidevs with NetBSD, which fixed this problem already
9 years ago. Consequently miidevs was synced as far as possible.
- Add MIIF_NOMANPAUSE and mii_phy_flowstatus() calls to drivers that
weren't explicitly converted to support flow control before. It's
unclear whether flow control actually works with these but typically
it should and their net behavior should be more correct with these
changes in place than without if the MAC driver sets MIIF_DOPAUSE.
Obtained from: NetBSD (partially)
Reviewed by: yongari (earlier version), silence on arch@ and net@
2011-05-03 19:51:29 +00:00
|
|
|
LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
|
|
|
|
PHY_RESET(miisc);
|
2009-04-14 22:53:22 +00:00
|
|
|
error = mii_mediachg(mii);
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report current media status.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
arge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = ifp->if_softc;
|
|
|
|
struct mii_data *mii;
|
|
|
|
|
|
|
|
mii = device_get_softc(sc->arge_miibus);
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
mii_pollstat(mii);
|
|
|
|
ifmr->ifm_active = mii->mii_media_active;
|
|
|
|
ifmr->ifm_status = mii->mii_media_status;
|
2011-10-17 19:49:00 +00:00
|
|
|
ARGE_UNLOCK(sc);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct arge_dmamap_arg {
|
|
|
|
bus_addr_t arge_busaddr;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
|
|
|
{
|
|
|
|
struct arge_dmamap_arg *ctx;
|
|
|
|
|
|
|
|
if (error != 0)
|
|
|
|
return;
|
|
|
|
ctx = arg;
|
|
|
|
ctx->arge_busaddr = segs[0].ds_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_dma_alloc(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct arge_dmamap_arg ctx;
|
|
|
|
struct arge_txdesc *txd;
|
|
|
|
struct arge_rxdesc *rxd;
|
|
|
|
int error, i;
|
|
|
|
|
|
|
|
/* Create parent DMA tag. */
|
|
|
|
error = bus_dma_tag_create(
|
|
|
|
bus_get_dma_tag(sc->arge_dev), /* parent */
|
|
|
|
1, 0, /* alignment, boundary */
|
|
|
|
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT, /* maxsize */
|
|
|
|
0, /* nsegments */
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&sc->arge_cdata.arge_parent_tag);
|
|
|
|
if (error != 0) {
|
2012-03-06 22:45:54 +00:00
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to create parent DMA tag\n");
|
2009-04-14 22:53:22 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
/* Create tag for Tx ring. */
|
|
|
|
error = bus_dma_tag_create(
|
|
|
|
sc->arge_cdata.arge_parent_tag, /* parent */
|
|
|
|
ARGE_RING_ALIGN, 0, /* alignment, boundary */
|
|
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
|
|
|
ARGE_TX_DMA_SIZE, /* maxsize */
|
|
|
|
1, /* nsegments */
|
|
|
|
ARGE_TX_DMA_SIZE, /* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&sc->arge_cdata.arge_tx_ring_tag);
|
|
|
|
if (error != 0) {
|
2012-03-06 22:45:54 +00:00
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to create Tx ring DMA tag\n");
|
2009-04-14 22:53:22 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create tag for Rx ring. */
|
|
|
|
error = bus_dma_tag_create(
|
|
|
|
sc->arge_cdata.arge_parent_tag, /* parent */
|
|
|
|
ARGE_RING_ALIGN, 0, /* alignment, boundary */
|
|
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
|
|
|
ARGE_RX_DMA_SIZE, /* maxsize */
|
|
|
|
1, /* nsegments */
|
|
|
|
ARGE_RX_DMA_SIZE, /* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&sc->arge_cdata.arge_rx_ring_tag);
|
|
|
|
if (error != 0) {
|
2012-03-06 22:45:54 +00:00
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to create Rx ring DMA tag\n");
|
2009-04-14 22:53:22 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create tag for Tx buffers. */
|
|
|
|
error = bus_dma_tag_create(
|
|
|
|
sc->arge_cdata.arge_parent_tag, /* parent */
|
|
|
|
sizeof(uint32_t), 0, /* alignment, boundary */
|
|
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
|
|
|
MCLBYTES * ARGE_MAXFRAGS, /* maxsize */
|
|
|
|
ARGE_MAXFRAGS, /* nsegments */
|
|
|
|
MCLBYTES, /* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&sc->arge_cdata.arge_tx_tag);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev, "failed to create Tx DMA tag\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Create tag for Rx buffers. */
|
|
|
|
error = bus_dma_tag_create(
|
|
|
|
sc->arge_cdata.arge_parent_tag, /* parent */
|
|
|
|
ARGE_RX_ALIGN, 0, /* alignment, boundary */
|
|
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
|
|
|
MCLBYTES, /* maxsize */
|
2009-05-26 17:43:32 +00:00
|
|
|
ARGE_MAXFRAGS, /* nsegments */
|
2009-04-14 22:53:22 +00:00
|
|
|
MCLBYTES, /* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&sc->arge_cdata.arge_rx_tag);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev, "failed to create Rx DMA tag\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate DMA'able memory and load the DMA map for Tx ring. */
|
|
|
|
error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
(void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK |
|
2012-03-06 22:45:54 +00:00
|
|
|
BUS_DMA_COHERENT | BUS_DMA_ZERO,
|
|
|
|
&sc->arge_cdata.arge_tx_ring_map);
|
2009-04-14 22:53:22 +00:00
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to allocate DMA'able memory for Tx ring\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.arge_busaddr = 0;
|
|
|
|
error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring,
|
|
|
|
ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
|
|
|
|
if (error != 0 || ctx.arge_busaddr == 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to load DMA'able memory for Tx ring\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr;
|
|
|
|
|
|
|
|
/* Allocate DMA'able memory and load the DMA map for Rx ring. */
|
|
|
|
error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
(void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK |
|
2012-03-06 22:45:54 +00:00
|
|
|
BUS_DMA_COHERENT | BUS_DMA_ZERO,
|
|
|
|
&sc->arge_cdata.arge_rx_ring_map);
|
2009-04-14 22:53:22 +00:00
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to allocate DMA'able memory for Rx ring\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.arge_busaddr = 0;
|
|
|
|
error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring,
|
|
|
|
ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
|
|
|
|
if (error != 0 || ctx.arge_busaddr == 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to load DMA'able memory for Rx ring\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr;
|
|
|
|
|
|
|
|
/* Create DMA maps for Tx buffers. */
|
|
|
|
for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
|
|
|
|
txd = &sc->arge_cdata.arge_txdesc[i];
|
|
|
|
txd->tx_m = NULL;
|
|
|
|
txd->tx_dmamap = NULL;
|
|
|
|
error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0,
|
|
|
|
&txd->tx_dmamap);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to create Tx dmamap\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Create DMA maps for Rx buffers. */
|
|
|
|
if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
|
|
|
|
&sc->arge_cdata.arge_rx_sparemap)) != 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to create spare Rx dmamap\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
|
|
|
|
rxd = &sc->arge_cdata.arge_rxdesc[i];
|
|
|
|
rxd->rx_m = NULL;
|
|
|
|
rxd->rx_dmamap = NULL;
|
|
|
|
error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
|
|
|
|
&rxd->rx_dmamap);
|
|
|
|
if (error != 0) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"failed to create Rx dmamap\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_dma_free(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct arge_txdesc *txd;
|
|
|
|
struct arge_rxdesc *rxd;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Tx ring. */
|
|
|
|
if (sc->arge_cdata.arge_tx_ring_tag) {
|
|
|
|
if (sc->arge_cdata.arge_tx_ring_map)
|
|
|
|
bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map);
|
|
|
|
if (sc->arge_cdata.arge_tx_ring_map &&
|
|
|
|
sc->arge_rdata.arge_tx_ring)
|
|
|
|
bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_rdata.arge_tx_ring,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map);
|
|
|
|
sc->arge_rdata.arge_tx_ring = NULL;
|
|
|
|
sc->arge_cdata.arge_tx_ring_map = NULL;
|
|
|
|
bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag);
|
|
|
|
sc->arge_cdata.arge_tx_ring_tag = NULL;
|
|
|
|
}
|
|
|
|
/* Rx ring. */
|
|
|
|
if (sc->arge_cdata.arge_rx_ring_tag) {
|
|
|
|
if (sc->arge_cdata.arge_rx_ring_map)
|
|
|
|
bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map);
|
|
|
|
if (sc->arge_cdata.arge_rx_ring_map &&
|
|
|
|
sc->arge_rdata.arge_rx_ring)
|
|
|
|
bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_rdata.arge_rx_ring,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map);
|
|
|
|
sc->arge_rdata.arge_rx_ring = NULL;
|
|
|
|
sc->arge_cdata.arge_rx_ring_map = NULL;
|
|
|
|
bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag);
|
|
|
|
sc->arge_cdata.arge_rx_ring_tag = NULL;
|
|
|
|
}
|
|
|
|
/* Tx buffers. */
|
|
|
|
if (sc->arge_cdata.arge_tx_tag) {
|
|
|
|
for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
|
|
|
|
txd = &sc->arge_cdata.arge_txdesc[i];
|
|
|
|
if (txd->tx_dmamap) {
|
|
|
|
bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag,
|
|
|
|
txd->tx_dmamap);
|
|
|
|
txd->tx_dmamap = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag);
|
|
|
|
sc->arge_cdata.arge_tx_tag = NULL;
|
|
|
|
}
|
|
|
|
/* Rx buffers. */
|
|
|
|
if (sc->arge_cdata.arge_rx_tag) {
|
|
|
|
for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
|
|
|
|
rxd = &sc->arge_cdata.arge_rxdesc[i];
|
|
|
|
if (rxd->rx_dmamap) {
|
|
|
|
bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
|
|
|
|
rxd->rx_dmamap);
|
|
|
|
rxd->rx_dmamap = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sc->arge_cdata.arge_rx_sparemap) {
|
|
|
|
bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
|
|
|
|
sc->arge_cdata.arge_rx_sparemap);
|
|
|
|
sc->arge_cdata.arge_rx_sparemap = 0;
|
|
|
|
}
|
|
|
|
bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag);
|
|
|
|
sc->arge_cdata.arge_rx_tag = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->arge_cdata.arge_parent_tag) {
|
|
|
|
bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag);
|
|
|
|
sc->arge_cdata.arge_parent_tag = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the transmit descriptors.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
arge_tx_ring_init(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct arge_ring_data *rd;
|
|
|
|
struct arge_txdesc *txd;
|
|
|
|
bus_addr_t addr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sc->arge_cdata.arge_tx_prod = 0;
|
|
|
|
sc->arge_cdata.arge_tx_cons = 0;
|
|
|
|
sc->arge_cdata.arge_tx_cnt = 0;
|
|
|
|
|
|
|
|
rd = &sc->arge_rdata;
|
|
|
|
bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring));
|
|
|
|
for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
|
|
|
|
if (i == ARGE_TX_RING_COUNT - 1)
|
|
|
|
addr = ARGE_TX_RING_ADDR(sc, 0);
|
|
|
|
else
|
|
|
|
addr = ARGE_TX_RING_ADDR(sc, i + 1);
|
|
|
|
rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY;
|
|
|
|
rd->arge_tx_ring[i].next_desc = addr;
|
|
|
|
txd = &sc->arge_cdata.arge_txdesc[i];
|
|
|
|
txd->tx_m = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map,
|
|
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the RX descriptors and allocate mbufs for them. Note that
|
|
|
|
* we arrange the descriptors in a closed ring, so that the last descriptor
|
|
|
|
* points back to the first.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
arge_rx_ring_init(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct arge_ring_data *rd;
|
|
|
|
struct arge_rxdesc *rxd;
|
|
|
|
bus_addr_t addr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
sc->arge_cdata.arge_rx_cons = 0;
|
|
|
|
|
|
|
|
rd = &sc->arge_rdata;
|
|
|
|
bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring));
|
|
|
|
for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
|
|
|
|
rxd = &sc->arge_cdata.arge_rxdesc[i];
|
2012-03-13 06:15:20 +00:00
|
|
|
if (rxd->rx_m != NULL) {
|
|
|
|
device_printf(sc->arge_dev,
|
|
|
|
"%s: ring[%d] rx_m wasn't free?\n",
|
|
|
|
__func__,
|
|
|
|
i);
|
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
rxd->rx_m = NULL;
|
|
|
|
rxd->desc = &rd->arge_rx_ring[i];
|
|
|
|
if (i == ARGE_RX_RING_COUNT - 1)
|
|
|
|
addr = ARGE_RX_RING_ADDR(sc, 0);
|
|
|
|
else
|
|
|
|
addr = ARGE_RX_RING_ADDR(sc, i + 1);
|
|
|
|
rd->arge_rx_ring[i].next_desc = addr;
|
2009-05-26 03:45:58 +00:00
|
|
|
if (arge_newbuf(sc, i) != 0) {
|
2009-04-14 22:53:22 +00:00
|
|
|
return (ENOBUFS);
|
2009-05-26 03:45:58 +00:00
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map,
|
2009-07-08 02:21:08 +00:00
|
|
|
BUS_DMASYNC_PREWRITE);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2012-03-13 06:15:20 +00:00
|
|
|
/*
|
|
|
|
* Free all the buffers in the RX ring.
|
|
|
|
*
|
|
|
|
* TODO: ensure that DMA is disabled and no pending DMA
|
|
|
|
* is lurking in the FIFO.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
arge_rx_ring_free(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct arge_rxdesc *rxd;
|
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
|
|
|
|
rxd = &sc->arge_cdata.arge_rxdesc[i];
|
|
|
|
/* Unmap the mbuf */
|
|
|
|
if (rxd->rx_m != NULL) {
|
|
|
|
bus_dmamap_unload(sc->arge_cdata.arge_rx_tag,
|
|
|
|
rxd->rx_dmamap);
|
|
|
|
m_free(rxd->rx_m);
|
|
|
|
rxd->rx_m = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
/*
|
|
|
|
* Initialize an RX descriptor and attach an MBUF cluster.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
arge_newbuf(struct arge_softc *sc, int idx)
|
|
|
|
{
|
|
|
|
struct arge_desc *desc;
|
|
|
|
struct arge_rxdesc *rxd;
|
|
|
|
struct mbuf *m;
|
|
|
|
bus_dma_segment_t segs[1];
|
|
|
|
bus_dmamap_t map;
|
|
|
|
int nsegs;
|
|
|
|
|
2012-12-05 08:04:20 +00:00
|
|
|
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
|
2009-04-14 22:53:22 +00:00
|
|
|
if (m == NULL)
|
|
|
|
return (ENOBUFS);
|
|
|
|
m->m_len = m->m_pkthdr.len = MCLBYTES;
|
|
|
|
m_adj(m, sizeof(uint64_t));
|
|
|
|
|
|
|
|
if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag,
|
|
|
|
sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) {
|
|
|
|
m_freem(m);
|
|
|
|
return (ENOBUFS);
|
|
|
|
}
|
|
|
|
KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
|
|
|
|
|
|
|
|
rxd = &sc->arge_cdata.arge_rxdesc[idx];
|
|
|
|
if (rxd->rx_m != NULL) {
|
|
|
|
bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap);
|
|
|
|
}
|
|
|
|
map = rxd->rx_dmamap;
|
|
|
|
rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap;
|
|
|
|
sc->arge_cdata.arge_rx_sparemap = map;
|
|
|
|
rxd->rx_m = m;
|
|
|
|
desc = rxd->desc;
|
2009-05-26 03:45:58 +00:00
|
|
|
if (segs[0].ds_addr & 3)
|
|
|
|
panic("RX packet address unaligned");
|
2009-04-14 22:53:22 +00:00
|
|
|
desc->packet_addr = segs[0].ds_addr;
|
2009-05-26 03:45:58 +00:00
|
|
|
desc->packet_ctrl = ARGE_DESC_EMPTY | ARGE_DMASIZE(segs[0].ds_len);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-07-08 02:21:08 +00:00
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static __inline void
|
|
|
|
arge_fixup_rx(struct mbuf *m)
|
|
|
|
{
|
2009-11-04 23:34:58 +00:00
|
|
|
int i;
|
|
|
|
uint16_t *src, *dst;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
src = mtod(m, uint16_t *);
|
|
|
|
dst = src - 1;
|
|
|
|
|
2009-07-08 02:21:08 +00:00
|
|
|
for (i = 0; i < m->m_len / sizeof(uint16_t); i++) {
|
2009-04-14 22:53:22 +00:00
|
|
|
*dst++ = *src++;
|
2009-07-08 02:21:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m->m_len % sizeof(uint16_t))
|
|
|
|
*(uint8_t *)dst = *(uint8_t *)src;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
m->m_data -= ETHER_ALIGN;
|
|
|
|
}
|
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
2009-10-30 01:40:32 +00:00
|
|
|
static int
|
2009-05-26 03:45:58 +00:00
|
|
|
arge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = ifp->if_softc;
|
2009-10-30 01:40:32 +00:00
|
|
|
int rx_npkts = 0;
|
2009-05-26 03:45:58 +00:00
|
|
|
|
2009-11-04 23:34:58 +00:00
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
|
2009-05-26 03:45:58 +00:00
|
|
|
ARGE_LOCK(sc);
|
|
|
|
arge_tx_locked(sc);
|
2009-10-30 01:40:32 +00:00
|
|
|
rx_npkts = arge_rx_locked(sc);
|
2009-05-26 03:45:58 +00:00
|
|
|
ARGE_UNLOCK(sc);
|
2009-11-04 23:34:58 +00:00
|
|
|
}
|
2009-10-30 01:40:32 +00:00
|
|
|
|
|
|
|
return (rx_npkts);
|
2009-05-26 03:45:58 +00:00
|
|
|
}
|
|
|
|
#endif /* DEVICE_POLLING */
|
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
arge_tx_locked(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct arge_txdesc *txd;
|
|
|
|
struct arge_desc *cur_tx;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
uint32_t ctrl;
|
|
|
|
int cons, prod;
|
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
cons = sc->arge_cdata.arge_tx_cons;
|
|
|
|
prod = sc->arge_cdata.arge_tx_prod;
|
2011-04-05 06:33:35 +00:00
|
|
|
|
2012-03-06 22:45:54 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_TX, "%s: cons=%d, prod=%d\n", __func__, cons,
|
|
|
|
prod);
|
2011-04-05 06:33:35 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
if (cons == prod)
|
|
|
|
return;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map,
|
|
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
|
|
|
ifp = sc->arge_ifp;
|
|
|
|
/*
|
|
|
|
* Go through our tx list and free mbufs for those
|
|
|
|
* frames that have been transmitted.
|
|
|
|
*/
|
|
|
|
for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) {
|
|
|
|
cur_tx = &sc->arge_rdata.arge_tx_ring[cons];
|
|
|
|
ctrl = cur_tx->packet_ctrl;
|
|
|
|
/* Check if descriptor has "finished" flag */
|
|
|
|
if ((ctrl & ARGE_DESC_EMPTY) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
|
|
|
|
|
|
|
|
sc->arge_cdata.arge_tx_cnt--;
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
|
|
|
|
|
|
|
txd = &sc->arge_cdata.arge_txdesc[cons];
|
|
|
|
|
|
|
|
ifp->if_opackets++;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
|
|
|
|
BUS_DMASYNC_POSTWRITE);
|
|
|
|
bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
|
|
|
|
|
|
|
|
/* Free only if it's first descriptor in list */
|
|
|
|
if (txd->tx_m)
|
|
|
|
m_freem(txd->tx_m);
|
|
|
|
txd->tx_m = NULL;
|
|
|
|
|
|
|
|
/* reset descriptor */
|
|
|
|
cur_tx->packet_addr = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
sc->arge_cdata.arge_tx_cons = cons;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 01:40:32 +00:00
|
|
|
static int
|
2009-04-14 22:53:22 +00:00
|
|
|
arge_rx_locked(struct arge_softc *sc)
|
|
|
|
{
|
|
|
|
struct arge_rxdesc *rxd;
|
|
|
|
struct ifnet *ifp = sc->arge_ifp;
|
2009-05-26 03:45:58 +00:00
|
|
|
int cons, prog, packet_len, i;
|
2009-04-14 22:53:22 +00:00
|
|
|
struct arge_desc *cur_rx;
|
|
|
|
struct mbuf *m;
|
2009-10-30 01:40:32 +00:00
|
|
|
int rx_npkts = 0;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
cons = sc->arge_cdata.arge_rx_cons;
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map,
|
|
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
for (prog = 0; prog < ARGE_RX_RING_COUNT;
|
2009-04-14 22:53:22 +00:00
|
|
|
ARGE_INC(cons, ARGE_RX_RING_COUNT)) {
|
|
|
|
cur_rx = &sc->arge_rdata.arge_rx_ring[cons];
|
|
|
|
rxd = &sc->arge_cdata.arge_rxdesc[cons];
|
|
|
|
m = rxd->rx_m;
|
|
|
|
|
|
|
|
if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0)
|
2012-03-06 22:16:10 +00:00
|
|
|
break;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
|
|
|
|
|
|
|
|
prog++;
|
|
|
|
|
|
|
|
packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl);
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
|
2009-07-08 02:21:08 +00:00
|
|
|
BUS_DMASYNC_POSTREAD);
|
2009-04-14 22:53:22 +00:00
|
|
|
m = rxd->rx_m;
|
|
|
|
|
|
|
|
arge_fixup_rx(m);
|
|
|
|
m->m_pkthdr.rcvif = ifp;
|
|
|
|
/* Skip 4 bytes of CRC */
|
|
|
|
m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN;
|
|
|
|
ifp->if_ipackets++;
|
2009-10-30 01:40:32 +00:00
|
|
|
rx_npkts++;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
ARGE_UNLOCK(sc);
|
|
|
|
(*ifp->if_input)(ifp, m);
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
cur_rx->packet_addr = 0;
|
2009-05-26 03:45:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (prog > 0) {
|
|
|
|
|
|
|
|
i = sc->arge_cdata.arge_rx_cons;
|
|
|
|
for (; prog > 0 ; prog--) {
|
|
|
|
if (arge_newbuf(sc, i) != 0) {
|
2012-03-06 22:16:10 +00:00
|
|
|
device_printf(sc->arge_dev,
|
2009-05-26 03:45:58 +00:00
|
|
|
"Failed to allocate buffer\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ARGE_INC(i, ARGE_RX_RING_COUNT);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
|
|
|
|
sc->arge_cdata.arge_rx_ring_map,
|
2009-07-08 02:21:08 +00:00
|
|
|
BUS_DMASYNC_PREWRITE);
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
sc->arge_cdata.arge_rx_cons = cons;
|
|
|
|
}
|
2009-10-30 01:40:32 +00:00
|
|
|
|
|
|
|
return (rx_npkts);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
arge_intr_filter(void *arg)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = arg;
|
|
|
|
uint32_t status, ints;
|
|
|
|
|
|
|
|
status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS);
|
|
|
|
ints = ARGE_READ(sc, AR71XX_DMA_INTR);
|
|
|
|
|
2011-04-05 05:15:48 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_INTR, "int mask(filter) = %b\n", ints,
|
2009-04-14 22:53:22 +00:00
|
|
|
"\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
|
|
|
|
"\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_INTR, "status(filter) = %b\n", status,
|
2009-04-14 22:53:22 +00:00
|
|
|
"\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
|
|
|
|
"\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
|
|
|
|
|
|
|
|
if (status & DMA_INTR_ALL) {
|
2009-04-29 03:21:53 +00:00
|
|
|
sc->arge_intr_status |= status;
|
2009-05-26 03:45:58 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
|
2009-04-14 22:53:22 +00:00
|
|
|
return (FILTER_SCHEDULE_THREAD);
|
2012-03-06 22:16:10 +00:00
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
|
|
|
|
sc->arge_intr_status = 0;
|
|
|
|
return (FILTER_STRAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
arge_intr(void *arg)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = arg;
|
|
|
|
uint32_t status;
|
2011-04-05 06:33:35 +00:00
|
|
|
struct ifnet *ifp = sc->arge_ifp;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS);
|
|
|
|
status |= sc->arge_intr_status;
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_INTR, "int status(intr) = %b\n", status,
|
2009-04-14 22:53:22 +00:00
|
|
|
"\20\10\7RX_OVERFLOW\5RX_PKT_RCVD"
|
|
|
|
"\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
|
|
|
* Is it our interrupt at all?
|
2009-04-14 22:53:22 +00:00
|
|
|
*/
|
|
|
|
if (status == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (status & DMA_INTR_RX_BUS_ERROR) {
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR);
|
|
|
|
device_printf(sc->arge_dev, "RX bus error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status & DMA_INTR_TX_BUS_ERROR) {
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR);
|
|
|
|
device_printf(sc->arge_dev, "TX bus error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ARGE_LOCK(sc);
|
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
if (status & DMA_INTR_RX_PKT_RCVD)
|
|
|
|
arge_rx_locked(sc);
|
|
|
|
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
|
|
|
* RX overrun disables the receiver.
|
|
|
|
* Clear indication and re-enable rx.
|
2009-05-26 03:45:58 +00:00
|
|
|
*/
|
|
|
|
if ( status & DMA_INTR_RX_OVERFLOW) {
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_OVERFLOW);
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN);
|
2011-04-05 06:33:35 +00:00
|
|
|
sc->stats.rx_overflow++;
|
2009-05-26 03:45:58 +00:00
|
|
|
}
|
2009-04-14 22:53:22 +00:00
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
if (status & DMA_INTR_TX_PKT_SENT)
|
|
|
|
arge_tx_locked(sc);
|
2012-03-06 22:16:10 +00:00
|
|
|
/*
|
|
|
|
* Underrun turns off TX. Clear underrun indication.
|
|
|
|
* If there's anything left in the ring, reactivate the tx.
|
2009-05-26 03:45:58 +00:00
|
|
|
*/
|
2009-05-21 22:12:42 +00:00
|
|
|
if (status & DMA_INTR_TX_UNDERRUN) {
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_UNDERRUN);
|
2011-04-05 06:33:35 +00:00
|
|
|
sc->stats.tx_underflow++;
|
2012-03-06 22:45:54 +00:00
|
|
|
ARGEDEBUG(sc, ARGE_DBG_TX, "%s: TX underrun; tx_cnt=%d\n",
|
|
|
|
__func__, sc->arge_cdata.arge_tx_cnt);
|
2011-03-13 08:34:14 +00:00
|
|
|
if (sc->arge_cdata.arge_tx_cnt > 0 ) {
|
2012-03-06 22:16:10 +00:00
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL,
|
2009-05-26 03:45:58 +00:00
|
|
|
DMA_TX_CONTROL_EN);
|
|
|
|
}
|
2009-05-21 22:12:42 +00:00
|
|
|
}
|
|
|
|
|
2011-04-05 06:46:07 +00:00
|
|
|
/*
|
|
|
|
* If we've finished TXing and there's space for more packets
|
|
|
|
* to be queued for TX, do so. Otherwise we may end up in a
|
|
|
|
* situation where the interface send queue was filled
|
|
|
|
* whilst the hardware queue was full, then the hardware
|
|
|
|
* queue was drained by the interface send queue wasn't,
|
|
|
|
* and thus if_start() is never called to kick-start
|
|
|
|
* the send process (and all subsequent packets are simply
|
|
|
|
* discarded.
|
|
|
|
*
|
|
|
|
* XXX TODO: make sure that the hardware deals nicely
|
|
|
|
* with the possibility of the queue being enabled above
|
|
|
|
* after a TX underrun, then having the hardware queue added
|
|
|
|
* to below.
|
|
|
|
*/
|
|
|
|
if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN) &&
|
|
|
|
(ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
|
|
|
|
if (!IFQ_IS_EMPTY(&ifp->if_snd))
|
|
|
|
arge_start_locked(ifp);
|
|
|
|
}
|
|
|
|
|
2009-05-28 00:47:50 +00:00
|
|
|
/*
|
|
|
|
* We handled all bits, clear status
|
|
|
|
*/
|
|
|
|
sc->arge_intr_status = 0;
|
2009-04-14 22:53:22 +00:00
|
|
|
ARGE_UNLOCK(sc);
|
2009-05-26 03:45:58 +00:00
|
|
|
/*
|
2012-03-06 22:16:10 +00:00
|
|
|
* re-enable all interrupts
|
2009-05-26 03:45:58 +00:00
|
|
|
*/
|
|
|
|
ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
|
|
|
|
2009-05-26 03:45:58 +00:00
|
|
|
|
2009-04-14 22:53:22 +00:00
|
|
|
static void
|
|
|
|
arge_tick(void *xsc)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = xsc;
|
|
|
|
struct mii_data *mii;
|
|
|
|
|
|
|
|
ARGE_LOCK_ASSERT(sc);
|
|
|
|
|
2009-11-12 21:27:58 +00:00
|
|
|
if (sc->arge_miibus) {
|
|
|
|
mii = device_get_softc(sc->arge_miibus);
|
|
|
|
mii_tick(mii);
|
|
|
|
callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
arge_multiphy_mediachange(struct ifnet *ifp)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = ifp->if_softc;
|
|
|
|
struct ifmedia *ifm = &sc->arge_ifmedia;
|
|
|
|
struct ifmedia_entry *ife = ifm->ifm_cur;
|
|
|
|
|
|
|
|
if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
|
|
|
|
return (EINVAL);
|
|
|
|
|
|
|
|
if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
|
2012-03-06 22:16:10 +00:00
|
|
|
device_printf(sc->arge_dev,
|
2009-11-12 21:27:58 +00:00
|
|
|
"AUTO is not supported for multiphy MAC");
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore everything
|
|
|
|
*/
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
|
2012-03-06 22:16:10 +00:00
|
|
|
ifmr->ifm_active = IFM_ETHER | sc->arge_media_type |
|
2009-11-12 21:27:58 +00:00
|
|
|
sc->arge_duplex_mode;
|
2009-04-14 22:53:22 +00:00
|
|
|
}
|
2009-11-12 21:27:58 +00:00
|
|
|
|
2012-05-01 06:18:30 +00:00
|
|
|
#if defined(ARGE_MDIO)
|
|
|
|
static int
|
|
|
|
argemdio_probe(device_t dev)
|
|
|
|
{
|
|
|
|
device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
argemdio_attach(device_t dev)
|
|
|
|
{
|
|
|
|
struct arge_softc *sc;
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
sc->arge_dev = dev;
|
|
|
|
sc->arge_mac_unit = device_get_unit(dev);
|
|
|
|
sc->arge_rid = 0;
|
|
|
|
sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
|
|
&sc->arge_rid, RF_ACTIVE | RF_SHAREABLE);
|
|
|
|
if (sc->arge_res == NULL) {
|
|
|
|
device_printf(dev, "couldn't map memory\n");
|
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset MAC - required for AR71xx MDIO to successfully occur */
|
|
|
|
arge_reset_mac(sc);
|
|
|
|
/* Reset MII bus */
|
|
|
|
arge_reset_miibus(sc);
|
|
|
|
|
|
|
|
bus_generic_probe(dev);
|
|
|
|
bus_enumerate_hinted_children(dev);
|
|
|
|
error = bus_generic_attach(dev);
|
|
|
|
fail:
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
argemdio_detach(device_t dev)
|
|
|
|
{
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|