svn commit: r212276 - user/nwhitehorn/ps3/powerpc/ps3
Nathan Whitehorn
nwhitehorn at FreeBSD.org
Mon Sep 6 22:59:59 UTC 2010
Author: nwhitehorn
Date: Mon Sep 6 22:59:58 2010
New Revision: 212276
URL: http://svn.freebsd.org/changeset/base/212276
Log:
Fill out the PS3 ethernet driver by adding support for checksum offloading,
media reporting and setting, and link state change notification. Next up
will be USB support.
Modified:
user/nwhitehorn/ps3/powerpc/ps3/if_glc.c
user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h
Modified: user/nwhitehorn/ps3/powerpc/ps3/if_glc.c
==============================================================================
--- user/nwhitehorn/ps3/powerpc/ps3/if_glc.c Mon Sep 6 22:47:52 2010 (r212275)
+++ user/nwhitehorn/ps3/powerpc/ps3/if_glc.c Mon Sep 6 22:59:58 2010 (r212276)
@@ -64,6 +64,7 @@ static int glc_attach(device_t);
static void glc_init(void *xsc);
static void glc_start(struct ifnet *ifp);
static int glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
+static void glc_set_multicast(struct glc_softc *sc);
static int glc_add_rxbuf(struct glc_softc *sc, int idx);
static int glc_add_rxbuf_dma(struct glc_softc *sc, int idx);
static int glc_encap(struct glc_softc *sc, struct mbuf **m_head,
@@ -71,6 +72,8 @@ static int glc_encap(struct glc_softc *s
static int glc_intr_filter(void *xsc);
static void glc_intr(void *xsc);
static void glc_tick(void *xsc);
+static void glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr);
+static int glc_media_change(struct ifnet *ifp);
static MALLOC_DEFINE(M_GLC, "gelic", "PS3 GELIC ethernet");
@@ -297,10 +300,23 @@ glc_attach(device_t dev)
if_initname(sc->sc_ifp, device_get_name(dev), device_get_unit(dev));
sc->sc_ifp->if_mtu = ETHERMTU;
sc->sc_ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ sc->sc_ifp->if_hwassist = CSUM_TCP | CSUM_UDP;
+ sc->sc_ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_RXCSUM;
+ sc->sc_ifp->if_capenable = IFCAP_HWCSUM | IFCAP_RXCSUM;
sc->sc_ifp->if_start = glc_start;
sc->sc_ifp->if_ioctl = glc_ioctl;
sc->sc_ifp->if_init = glc_init;
+ ifmedia_init(&sc->sc_media, IFM_IMASK, glc_media_change,
+ glc_media_status);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
+
IFQ_SET_MAXLEN(&sc->sc_ifp->if_snd, GLC_MAX_TX_PACKETS);
sc->sc_ifp->if_snd.ifq_drv_maxlen = GLC_MAX_TX_PACKETS;
IFQ_SET_READY(&sc->sc_ifp->if_snd);
@@ -328,6 +344,8 @@ glc_init_locked(struct glc_softc *sc)
lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
+ glc_set_multicast(sc);
+
for (i = 0; i < GLC_MAX_RX_PACKETS; i++) {
rxs = &sc->sc_rxsoft[i];
rxs->rxs_desc_slot = i;
@@ -365,6 +383,17 @@ glc_init_locked(struct glc_softc *sc)
}
static void
+glc_stop(void *xsc)
+{
+ struct glc_softc *sc = xsc;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+
+ lv1_net_stop_tx_dma(sc->sc_bus, sc->sc_dev, 0);
+ lv1_net_stop_rx_dma(sc->sc_bus, sc->sc_dev, 0);
+}
+
+static void
glc_init(void *xsc)
{
struct glc_softc *sc = xsc;
@@ -458,31 +487,35 @@ static int
glc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
struct glc_softc *sc = ifp->if_softc;
-#if 0
struct ifreq *ifr = (struct ifreq *)data;
-#endif
int err = 0;
switch (cmd) {
case SIOCSIFFLAGS:
mtx_lock(&sc->sc_mtx);
if ((ifp->if_flags & IFF_UP) != 0) {
-#if 0
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 &&
((ifp->if_flags ^ sc->sc_ifpflags) &
(IFF_ALLMULTI | IFF_PROMISC)) != 0)
- bm_setladrf(sc);
+ glc_set_multicast(sc);
else
-#endif
glc_init_locked(sc);
}
-#if 0
else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
- bm_stop(sc);
-#endif
+ glc_stop(sc);
sc->sc_ifpflags = ifp->if_flags;
mtx_unlock(&sc->sc_mtx);
break;
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ mtx_lock(&sc->sc_mtx);
+ glc_set_multicast(sc);
+ mtx_unlock(&sc->sc_mtx);
+ break;
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ err = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd);
+ break;
default:
err = ether_ioctl(ifp, cmd, data);
break;
@@ -491,6 +524,51 @@ glc_ioctl(struct ifnet *ifp, u_long cmd,
return (err);
}
+static void
+glc_set_multicast(struct glc_softc *sc)
+{
+ struct ifnet *ifp = sc->sc_ifp;
+ struct ifmultiaddr *inm;
+ uint64_t addr;
+ int naddrs;
+
+ /* Clear multicast filter */
+ lv1_net_remove_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
+
+ /* Add broadcast */
+ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
+ 0xffffffffffffL, 0);
+
+ if ((ifp->if_flags & IFF_ALLMULTI) != 0) {
+ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev, 0, 1);
+ } else {
+ if_maddr_rlock(ifp);
+ naddrs = 1; /* Include broadcast */
+ TAILQ_FOREACH(inm, &ifp->if_multiaddrs, ifma_link) {
+ if (inm->ifma_addr->sa_family != AF_LINK)
+ continue;
+ addr = 0;
+ memcpy(&((uint8_t *)(&addr))[2],
+ LLADDR((struct sockaddr_dl *)inm->ifma_addr),
+ ETHER_ADDR_LEN);
+
+ lv1_net_add_multicast_address(sc->sc_bus, sc->sc_dev,
+ addr, 0);
+
+ /*
+ * Filter can only hold 32 addresses, so fall back to
+ * the IFF_ALLMULTI case if we have too many.
+ */
+ if (++naddrs >= 32) {
+ lv1_net_add_multicast_address(sc->sc_bus,
+ sc->sc_dev, 0, 1);
+ break;
+ }
+ }
+ if_maddr_runlock(ifp);
+ }
+}
+
static int
glc_add_rxbuf(struct glc_softc *sc, int idx)
{
@@ -624,6 +702,10 @@ glc_encap(struct glc_softc *sc, struct m
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_LAST;
}
+ if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_TCP;
+ if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
+ sc->sc_txdmadesc[idx].cmd_stat |= GELIC_CMDSTAT_CSUM_UDP;
sc->sc_txdmadesc[idx].cmd_stat |= GELIC_DESCR_OWNED;
idx = (idx + 1) % GLC_MAX_TX_PACKETS;
@@ -670,6 +752,16 @@ glc_rxintr(struct glc_softc *sc)
}
m = sc->sc_rxsoft[i].rxs_mbuf;
+ if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_IPCSUM) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED | CSUM_IP_VALID;
+ }
+ if (sc->sc_rxdmadesc[i].data_stat & GELIC_RX_TCPUDPCSUM) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+
if (glc_add_rxbuf(sc, i)) {
ifp->if_ierrors++;
goto requeue;
@@ -776,7 +868,7 @@ static void
glc_intr(void *xsc)
{
struct glc_softc *sc = xsc;
- uint64_t status;
+ uint64_t status, linkstat, junk;
mtx_lock(&sc->sc_mtx);
@@ -793,6 +885,80 @@ glc_intr(void *xsc)
if (status & (GELIC_INT_TXDONE | GELIC_INT_TX_CHAIN_END))
glc_txintr(sc);
+ if (status & GELIC_INT_PHY) {
+ lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
+ GELIC_VLAN_TX_ETHERNET, 0, 0, &linkstat, &junk);
+
+ linkstat = (linkstat & GELIC_LINK_UP) ?
+ LINK_STATE_UP : LINK_STATE_DOWN;
+ if (linkstat != sc->sc_ifp->if_link_state)
+ if_link_state_change(sc->sc_ifp, linkstat);
+ }
+
mtx_unlock(&sc->sc_mtx);
}
+static void
+glc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct glc_softc *sc = ifp->if_softc;
+ uint64_t status, junk;
+
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+
+ lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_GET_LINK_STATUS,
+ GELIC_VLAN_TX_ETHERNET, 0, 0, &status, &junk);
+
+ if (status & GELIC_LINK_UP)
+ ifmr->ifm_status |= IFM_ACTIVE;
+
+ if (status & GELIC_SPEED_10)
+ ifmr->ifm_active |= IFM_10_T;
+ else if (status & GELIC_SPEED_100)
+ ifmr->ifm_active |= IFM_100_TX;
+ else if (status & GELIC_SPEED_1000)
+ ifmr->ifm_active |= IFM_1000_T;
+
+ if (status & GELIC_FULL_DUPLEX)
+ ifmr->ifm_active |= IFM_FDX;
+ else
+ ifmr->ifm_active |= IFM_HDX;
+}
+
+static int
+glc_media_change(struct ifnet *ifp)
+{
+ struct glc_softc *sc = ifp->if_softc;
+ uint64_t mode, junk;
+ int result;
+
+ if (IFM_TYPE(sc->sc_media.ifm_media) != IFM_ETHER)
+ return (EINVAL);
+
+ switch (IFM_SUBTYPE(sc->sc_media.ifm_media)) {
+ case IFM_AUTO:
+ mode = GELIC_AUTO_NEG;
+ break;
+ case IFM_10_T:
+ mode = GELIC_SPEED_10;
+ break;
+ case IFM_100_TX:
+ mode = GELIC_SPEED_100;
+ break;
+ case IFM_1000_T:
+ mode = GELIC_SPEED_1000 | GELIC_FULL_DUPLEX;
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if (IFM_OPTIONS(sc->sc_media.ifm_media) & IFM_FDX)
+ mode |= GELIC_FULL_DUPLEX;
+
+ result = lv1_net_control(sc->sc_bus, sc->sc_dev, GELIC_SET_LINK_MODE,
+ GELIC_VLAN_TX_ETHERNET, mode, 0, &junk, &junk);
+
+ return (result ? EIO : 0);
+}
+
Modified: user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h
==============================================================================
--- user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h Mon Sep 6 22:47:52 2010 (r212275)
+++ user/nwhitehorn/ps3/powerpc/ps3/if_glcreg.h Mon Sep 6 22:59:58 2010 (r212276)
@@ -79,6 +79,8 @@ struct glc_softc {
uint64_t *sc_hwirq_status;
volatile uint64_t sc_interrupt_status;
+ struct ifmedia sc_media;
+
/* Transmission */
bus_dma_tag_t sc_txdma_tag;
@@ -107,6 +109,7 @@ struct glc_softc {
#define GELIC_GET_MAC_ADDRESS 0x0001
#define GELIC_GET_LINK_STATUS 0x0002
+#define GELIC_SET_LINK_MODE 0x0003
#define GELIC_LINK_UP 0x0001
#define GELIC_FULL_DUPLEX 0x0002
#define GELIC_AUTO_NEG 0x0004
@@ -123,10 +126,16 @@ struct glc_softc {
#define GELIC_DESCR_OWNED 0xa0000000
#define GELIC_CMDSTAT_DMA_DONE 0x00000000
#define GELIC_CMDSTAT_CHAIN_END 0x00000002
+#define GELIC_CMDSTAT_CSUM_TCP 0x00020000
+#define GELIC_CMDSTAT_CSUM_UDP 0x00030000
#define GELIC_CMDSTAT_NOIPSEC 0x00080000
#define GELIC_CMDSTAT_LAST 0x00040000
#define GELIC_RXERRORS 0x7def8000
+/* RX Data Status codes */
+#define GELIC_RX_IPCSUM 0x20000000
+#define GELIC_RX_TCPUDPCSUM 0x10000000
+
/* Interrupt options */
#define GELIC_INT_RXDONE 0x0000000000004000UL
#define GELIC_INT_RXFRAME 0x1000000000000000UL
More information about the svn-src-user
mailing list