Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/dev/ic - add DM9000 internal PHY manipulating routines.
details: https://anonhg.NetBSD.org/src/rev/a111e0a148aa
branches: trunk
changeset: 773117:a111e0a148aa
user: nisimura <nisimura%NetBSD.org@localhost>
date: Sat Jan 28 08:29:55 2012 +0000
description:
- add DM9000 internal PHY manipulating routines.
- now capable of auto-negotiation, use this as default media selection.
- add multicast filter support.
- code cleanup.
Code update from Paul Fleischer.
diffstat:
sys/dev/ic/dm9000.c | 736 ++++++++++++++++++++++++++++++++++--------------
sys/dev/ic/dm9000reg.h | 6 +-
sys/dev/ic/dm9000var.h | 21 +-
3 files changed, 537 insertions(+), 226 deletions(-)
diffs (truncated from 1025 to 300 lines):
diff -r 5629c4b4bf16 -r a111e0a148aa sys/dev/ic/dm9000.c
--- a/sys/dev/ic/dm9000.c Sat Jan 28 07:19:17 2012 +0000
+++ b/sys/dev/ic/dm9000.c Sat Jan 28 08:29:55 2012 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: dm9000.c,v 1.3 2011/06/30 20:09:39 wiz Exp $ */
+/* $NetBSD: dm9000.c,v 1.4 2012/01/28 08:29:55 nisimura Exp $ */
/*
* Copyright (c) 2009 Paul Fleischer
@@ -89,6 +89,7 @@
#include <sys/cdefs.h>
#include <sys/param.h>
+#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/syslog.h>
@@ -117,7 +118,7 @@
#if 1
#undef DM9000_DEBUG
-#undef DM9000_TX_DEBUG
+#undef DM9000_TX_DEBUG
#undef DM9000_TX_DATA_DEBUG
#undef DM9000_RX_DEBUG
#undef DM9000_RX_DATA_DEBUG
@@ -159,9 +160,13 @@
#define TX_DATA_DPRINTF(s) do {} while (/*CONSTCOND*/0)
#endif
-
+/*** Internal PHY functions ***/
uint16_t dme_phy_read(struct dme_softc *sc, int reg);
-void dme_phy_write(struct dme_softc *sc, int reg, uint16_t value);
+void dme_phy_write(struct dme_softc *sc, int reg, uint16_t value);
+void dme_phy_init(struct dme_softc *sc);
+void dme_phy_reset(struct dme_softc *sc);
+void dme_phy_update_media(struct dme_softc *sc);
+void dme_phy_check_link(void *arg);
/*** Methods registered in struct ifnet ***/
void dme_start_output(struct ifnet *ifp);
@@ -186,6 +191,17 @@
/* Software Initialize/Reset of the DM9000 */
void dme_reset(struct dme_softc *sc);
+/* Configure multicast filter */
+void dme_set_addr_filter(struct dme_softc *sc);
+
+/* Set media */
+int dme_set_media(struct dme_softc *sc, int media);
+
+/* Read/write packet data from/to DM9000 IC in various transfer sizes */
+int dme_pkt_read_2(struct dme_softc *sc, struct ifnet *ifp, struct mbuf **outBuf);
+int dme_pkt_write_2(struct dme_softc *sc, struct mbuf *bufChain);
+/* TODO: Implement 8 and 32 bit read/write functions */
+
uint16_t
dme_phy_read(struct dme_softc *sc, int reg)
{
@@ -199,9 +215,6 @@
/* Wait until access to PHY has completed */
while (dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE);
- /* XXX: The delay is probably not necessary as we just busy-waited */
- delay(200);
-
/* Reset ERPRR-bit */
dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY);
@@ -228,19 +241,172 @@
/* Wait until access to PHY has completed */
while(dme_read(sc, DM9000_EPCR) & DM9000_EPCR_ERRE);
-
- /* XXX: The delay is probably not necessary as we just busy-waited */
- delay(200);
-
/* Reset ERPRR-bit */
dme_write(sc, DM9000_EPCR, DM9000_EPCR_EPOS_PHY);
}
-int
-dme_attach(struct dme_softc *sc, uint8_t *enaddr)
+void
+dme_phy_init(struct dme_softc *sc)
+{
+ u_int ifm_media = sc->sc_media.ifm_media;
+ uint32_t bmcr, anar;
+
+ bmcr = dme_phy_read(sc, DM9000_PHY_BMCR);
+ anar = dme_phy_read(sc, DM9000_PHY_ANAR);
+
+ anar = anar & ~DM9000_PHY_ANAR_10_HDX
+ & ~DM9000_PHY_ANAR_10_FDX
+ & ~DM9000_PHY_ANAR_TX_HDX
+ & ~DM9000_PHY_ANAR_TX_FDX;
+
+ switch (IFM_SUBTYPE(ifm_media)) {
+ case IFM_AUTO:
+ bmcr |= DM9000_PHY_BMCR_AUTO_NEG_EN;
+ anar |= DM9000_PHY_ANAR_10_HDX |
+ DM9000_PHY_ANAR_10_FDX |
+ DM9000_PHY_ANAR_TX_HDX |
+ DM9000_PHY_ANAR_TX_FDX;
+ break;
+ case IFM_10_T:
+ //bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN;
+ bmcr &= ~DM9000_PHY_BMCR_SPEED_SELECT;
+ if (ifm_media & IFM_FDX)
+ anar |= DM9000_PHY_ANAR_10_FDX;
+ else
+ anar |= DM9000_PHY_ANAR_10_HDX;
+ break;
+ case IFM_100_TX:
+ //bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN;
+ bmcr |= DM9000_PHY_BMCR_SPEED_SELECT;
+ if (ifm_media & IFM_FDX)
+ anar |= DM9000_PHY_ANAR_TX_FDX;
+ else
+ anar |= DM9000_PHY_ANAR_TX_HDX;
+
+ break;
+ }
+
+ if(ifm_media & IFM_FDX) {
+ bmcr |= DM9000_PHY_BMCR_DUPLEX_MODE;
+ } else {
+ bmcr &= ~DM9000_PHY_BMCR_DUPLEX_MODE;
+ }
+
+ dme_phy_write(sc, DM9000_PHY_BMCR, bmcr);
+ dme_phy_write(sc, DM9000_PHY_ANAR, anar);
+}
+
+void
+dme_phy_reset(struct dme_softc *sc)
+{
+ uint32_t reg;
+
+ /* PHY Reset */
+ dme_phy_write(sc, DM9000_PHY_BMCR, DM9000_PHY_BMCR_RESET);
+
+ reg = dme_read(sc, DM9000_GPCR);
+ dme_write(sc, DM9000_GPCR, reg & ~DM9000_GPCR_GPIO0_OUT);
+ reg = dme_read(sc, DM9000_GPR);
+ dme_write(sc, DM9000_GPR, reg | DM9000_GPR_PHY_PWROFF);
+
+ dme_phy_init(sc);
+
+ reg = dme_read(sc, DM9000_GPR);
+ dme_write(sc, DM9000_GPR, reg & ~DM9000_GPR_PHY_PWROFF);
+ reg = dme_read(sc, DM9000_GPCR);
+ dme_write(sc, DM9000_GPCR, reg | DM9000_GPCR_GPIO0_OUT);
+
+ dme_phy_update_media(sc);
+}
+
+void
+dme_phy_update_media(struct dme_softc *sc)
{
- struct ifnet *ifp = &sc->sc_ethercom.ec_if;
- uint8_t b[2];
+ u_int ifm_media = sc->sc_media.ifm_media;
+ uint32_t reg;
+
+ if (IFM_SUBTYPE(ifm_media) == IFM_AUTO) {
+ /* If auto-negotiation is used, ensures that it is completed
+ before trying to extract any media information. */
+ reg = dme_phy_read(sc, DM9000_PHY_BMSR);
+ if ((reg & DM9000_PHY_BMSR_AUTO_NEG_AB) == 0) {
+ /* Auto-negotation not possible, therefore there is no
+ reason to try obtain any media information. */
+ return;
+ }
+
+ /* Then loop until the negotiation is completed. */
+ while ((reg & DM9000_PHY_BMSR_AUTO_NEG_COM) == 0) {
+ /* TODO: Bail out after a finite number of attempts
+ in case something goes wrong. */
+ preempt();
+ reg = dme_phy_read(sc, DM9000_PHY_BMSR);
+ }
+ }
+
+
+ sc->sc_media_active = IFM_ETHER;
+ reg = dme_phy_read(sc, DM9000_PHY_BMCR);
+
+ if (reg & DM9000_PHY_BMCR_SPEED_SELECT) {
+ sc->sc_media_active |= IFM_100_TX;
+ } else {
+ sc->sc_media_active |= IFM_10_T;
+ }
+
+ if (reg & DM9000_PHY_BMCR_DUPLEX_MODE) {
+ sc->sc_media_active |= IFM_FDX;
+ }
+}
+
+void
+dme_phy_check_link(void *arg)
+{
+ struct dme_softc *sc = arg;
+ uint32_t reg;
+ int s;
+
+ s = splnet();
+
+ reg = dme_read(sc, DM9000_NSR) & DM9000_NSR_LINKST;
+
+ if( reg )
+ reg = IFM_ETHER | IFM_AVALID | IFM_ACTIVE;
+ else {
+ reg = IFM_ETHER | IFM_AVALID;
+ sc->sc_media_active = IFM_NONE;
+ }
+
+ if ( (sc->sc_media_status != reg) && (reg & IFM_ACTIVE)) {
+ dme_phy_reset(sc);
+ }
+
+ sc->sc_media_status = reg;
+
+ callout_schedule(&sc->sc_link_callout, mstohz(2000));
+ splx(s);
+}
+
+int
+dme_set_media(struct dme_softc *sc, int media)
+{
+ int s;
+
+ s = splnet();
+ sc->sc_media.ifm_media = media;
+ dme_phy_reset(sc);
+
+ splx(s);
+
+ return 0;
+}
+
+int
+dme_attach(struct dme_softc *sc, const uint8_t *enaddr)
+{
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+ uint8_t b[2];
+ uint16_t io_mode;
dme_read_c(sc, DM9000_VID0, b, 2);
#if BYTE_ORDER == BIG_ENDIAN
@@ -260,16 +426,6 @@
sc->sc_product_id);
}
-#if 0
- {
- /* Force 10Mbps to test dme_phy_write */
- uint16_t bmcr;
- bmcr = dme_phy_read(sc, DM9000_PHY_BMCR);
- bmcr &= ~DM9000_PHY_BMCR_AUTO_NEG_EN;
- bmcr &= ~DM9000_PHY_BMCR_SPEED_SELECT; /* select 100Mbps */
- dme_phy_write(sc, DM9000_PHY_BMCR, bmcr);
- }
-#endif
/* Initialize ifnet structure. */
strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
ifp->if_softc = sc;
@@ -278,17 +434,28 @@
ifp->if_ioctl = dme_ioctl;
ifp->if_stop = dme_stop;
ifp->if_watchdog = NULL; /* no watchdog at this stage */
- ifp->if_flags = IFF_SIMPLEX | IFF_NOTRAILERS |
- IFF_BROADCAST; /* No multicast support for now */
+ ifp->if_flags = IFF_SIMPLEX | IFF_NOTRAILERS | IFF_BROADCAST |
+ IFF_MULTICAST;
IFQ_SET_READY(&ifp->if_snd);
/* Initialize ifmedia structures. */
ifmedia_init(&sc->sc_media, 0, dme_mediachange, dme_mediastatus);
- ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T|IFM_100_TX, 0, NULL);
- ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_10_T|IFM_100_TX);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_AUTO, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_10_T, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER|IFM_100_TX, 0, NULL);
+
+ ifmedia_set(&sc->sc_media, IFM_ETHER|IFM_AUTO);
if (enaddr != NULL)
memcpy(sc->sc_enaddr, enaddr, sizeof(sc->sc_enaddr));
+ /* TODO: Support an EEPROM attached to the DM9000 chip */
+
+ callout_init(&sc->sc_link_callout, 0);
+ callout_setfunc(&sc->sc_link_callout, dme_phy_check_link, sc);
+
+ sc->sc_media_status = 0;
/* Configure DM9000 with the MAC address */
Home |
Main Index |
Thread Index |
Old Index