Module Name: src
Committed By: nisimura
Date: Tue Nov 26 08:37:05 UTC 2019
Modified Files:
src/sys/dev/pci: files.pci if_kse.c
Log Message:
- use mii(4) layer to control KSZ8841 builtin PHY.
- handle PAUSE flow control properly according to ifconfig(8) mediaopt
selection.
- some style knits; use aprint(9) and modernise callout(9).
To generate a diff of this commit:
cvs rdiff -u -r1.416 -r1.417 src/sys/dev/pci/files.pci
cvs rdiff -u -r1.41 -r1.42 src/sys/dev/pci/if_kse.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/pci/files.pci
diff -u src/sys/dev/pci/files.pci:1.416 src/sys/dev/pci/files.pci:1.417
--- src/sys/dev/pci/files.pci:1.416 Wed Nov 20 09:37:45 2019
+++ src/sys/dev/pci/files.pci Tue Nov 26 08:37:05 2019
@@ -1,4 +1,4 @@
-# $NetBSD: files.pci,v 1.416 2019/11/20 09:37:45 hikaru Exp $
+# $NetBSD: files.pci,v 1.417 2019/11/26 08:37:05 nisimura Exp $
#
# Config file and device description for machine-independent PCI code.
# Included by ports that need it. Requires that the SCSI files be
@@ -965,7 +965,7 @@ attach nfe at pci
file dev/pci/if_nfe.c nfe
# MICREL Etherent
-device kse: ether, ifnet, arp
+device kse: ether, ifnet, arp, mii
attach kse at pci
file dev/pci/if_kse.c kse
Index: src/sys/dev/pci/if_kse.c
diff -u src/sys/dev/pci/if_kse.c:1.41 src/sys/dev/pci/if_kse.c:1.42
--- src/sys/dev/pci/if_kse.c:1.41 Thu Nov 7 22:00:37 2019
+++ src/sys/dev/pci/if_kse.c Tue Nov 26 08:37:05 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: if_kse.c,v 1.41 2019/11/07 22:00:37 nisimura Exp $ */
+/* $NetBSD: if_kse.c,v 1.42 2019/11/26 08:37:05 nisimura Exp $ */
/*-
* Copyright (c) 2006 The NetBSD Foundation, Inc.
@@ -30,11 +30,11 @@
*/
/*
- * Micrel 8841/8842 10/100 ethernet driver
+ * Micrel 8841/8842 10/100 PCI ethernet driver
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1.41 2019/11/07 22:00:37 nisimura Exp $");
+__KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1.42 2019/11/26 08:37:05 nisimura Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -55,6 +55,8 @@ __KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1
#include <net/if_media.h>
#include <net/if_dl.h>
#include <net/if_ether.h>
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
#include <net/bpf.h>
#include <dev/pci/pcivar.h>
@@ -68,9 +70,9 @@ __KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1
#define CSR_WRITE_4(sc, off, val) \
bus_space_write_4(sc->sc_st, sc->sc_sh, off, val)
#define CSR_READ_2(sc, off) \
- bus_space_read_2(sc->sc_st, sc->sc_sh, off)
+ bus_space_read_2((sc)->sc_st, (sc)->sc_sh, (off))
#define CSR_WRITE_2(sc, off, val) \
- bus_space_write_2(sc->sc_st, sc->sc_sh, off, val)
+ bus_space_write_2((sc)->sc_st, (sc)->sc_sh, (off), (val))
#define MDTXC 0x000 /* DMA transmit control */
#define MDRXC 0x004 /* DMA receive control */
@@ -86,8 +88,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1
#define MARM 0x202 /* MAC address middle */
#define MARH 0x204 /* MAC address high */
#define GRR 0x216 /* global reset */
-#define CIDR 0x400 /* chip ID and enable */
-#define CGCR 0x40a /* chip global control */
+#define SIDER 0x400 /* switch ID and function enable */
#define IACR 0x4a0 /* indirect access control */
#define IADR1 0x4a2 /* indirect access data 66:63 */
#define IADR2 0x4a4 /* indirect access data 47:32 */
@@ -98,24 +99,27 @@ __KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1
#define P1SR 0x514 /* port 1 status */
#define P2CR4 0x532 /* port 2 control 4 */
#define P2SR 0x534 /* port 2 status */
-#define PxCR_STARTNEG (1U << 9) /* restart auto negotiation */
-#define PxCR_AUTOEN (1U << 7) /* auto negotiation enable */
-#define PxCR_SPD100 (1U << 6) /* force speed 100 */
-#define PxCR_USEFDX (1U << 5) /* force full duplex */
-#define PxCR_USEFC (1U << 4) /* advertise pause flow control */
-#define PxSR_ACOMP (1U << 6) /* auto negotiation completed */
-#define PxSR_SPD100 (1U << 10) /* speed is 100Mbps */
-#define PxSR_FDX (1U << 9) /* full duplex */
-#define PxSR_LINKUP (1U << 5) /* link is good */
-#define PxSR_RXFLOW (1U << 12) /* receive flow control active */
-#define PxSR_TXFLOW (1U << 11) /* transmit flow control active */
+#define PxCR_STARTNEG (1U<<9) /* restart auto negotiation */
+#define PxCR_AUTOEN (1U<<7) /* auto negotiation enable */
+#define PxCR_SPD100 (1U<<6) /* force speed 100 */
+#define PxCR_USEFDX (1U<<5) /* force full duplex */
+#define PxCR_USEFC (1U<<4) /* advertise pause flow control */
+#define PxSR_ACOMP (1U<<6) /* auto negotiation completed */
+#define PxSR_SPD100 (1U<<10) /* speed is 100Mbps */
+#define PxSR_FDX (1U<<9) /* full duplex */
+#define PxSR_LINKUP (1U<<5) /* link is good */
+#define PxSR_RXFLOW (1U<<12) /* receive flow control active */
+#define PxSR_TXFLOW (1U<<11) /* transmit flow control active */
+#define P1VIDCR 0x504 /* port 1 vtag */
+#define P2VIDCR 0x524 /* port 2 vtag */
+#define P3VIDCR 0x544 /* 8842 host vtag */
#define TXC_BS_MSK 0x3f000000 /* burst size */
#define TXC_BS_SFT (24) /* 1,2,4,8,16,32 or 0 for unlimited */
#define TXC_UCG (1U<<18) /* generate UDP checksum */
#define TXC_TCG (1U<<17) /* generate TCP checksum */
#define TXC_ICG (1U<<16) /* generate IP checksum */
-#define TXC_FCE (1U<<9) /* enable flowcontrol */
+#define TXC_FCE (1U<<9) /* generate PAUSE to moderate Rx lvl */
#define TXC_EP (1U<<2) /* enable automatic padding */
#define TXC_AC (1U<<1) /* add CRC to frame */
#define TXC_TEN (1) /* enable DMA to run */
@@ -126,7 +130,7 @@ __KERNEL_RCSID(0, "$NetBSD: if_kse.c,v 1
#define RXC_UCC (1U<<18) /* run UDP checksum */
#define RXC_TCC (1U<<17) /* run TDP checksum */
#define RXC_ICC (1U<<16) /* run IP checksum */
-#define RXC_FCE (1U<<9) /* enable flowcontrol */
+#define RXC_FCE (1U<<9) /* accept PAUSE to throttle Tx */
#define RXC_RB (1U<<6) /* receive broadcast frame */
#define RXC_RM (1U<<5) /* receive multicast frame */
#define RXC_RU (1U<<4) /* receive unicast frame */
@@ -218,14 +222,17 @@ struct kse_softc {
device_t sc_dev; /* generic device information */
bus_space_tag_t sc_st; /* bus space tag */
bus_space_handle_t sc_sh; /* bus space handle */
+ bus_size_t sc_memsize; /* csr map size */
bus_dma_tag_t sc_dmat; /* bus DMA tag */
+ pci_chipset_tag_t sc_pc; /* PCI chipset tag */
struct ethercom sc_ethercom; /* Ethernet common data */
void *sc_ih; /* interrupt cookie */
- struct ifmedia sc_media; /* ifmedia information */
- int sc_linkstatus; /* last P1SR register value */
+ struct mii_data sc_mii; /* mii 8841 */
+ struct ifmedia sc_media; /* ifmedia 8842 */
+ int sc_flowflags; /* 802.3x PAUSE flow control */
- callout_t sc_callout; /* MII tick callout */
+ callout_t sc_tick_ch; /* MII tick callout */
callout_t sc_stat_ch; /* statistics counter callout */
bus_dmamap_t sc_cddmamap; /* control data DMA map */
@@ -329,10 +336,13 @@ static int kse_intr(void *);
static void rxintr(struct kse_softc *);
static void txreap(struct kse_softc *);
static void lnkchg(struct kse_softc *);
-static int ksephy_change(struct ifnet *);
-static void ksephy_status(struct ifnet *, struct ifmediareq *);
-static void nopifm_status(struct ifnet *, struct ifmediareq *);
+static int kse_ifmedia_upd(struct ifnet *);
+static void kse_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+static void nopifmedia_sts(struct ifnet *, struct ifmediareq *);
static void phy_tick(void *);
+int kse_mii_readreg(device_t, int, int, uint16_t *);
+int kse_mii_writereg(device_t, int, int, uint16_t);
+void kse_mii_statchg(struct ifnet *);
#ifdef KSE_EVENT_COUNTERS
static void stat_tick(void *);
static void zerostats(struct kse_softc *);
@@ -360,90 +370,77 @@ kse_attach(device_t parent, device_t sel
pci_chipset_tag_t pc = pa->pa_pc;
pci_intr_handle_t ih;
const char *intrstr;
- struct ifnet *ifp;
+ struct ifnet *ifp = &sc->sc_ethercom.ec_if;
+ struct mii_data * const mii = &sc->sc_mii;
struct ifmedia *ifm;
uint8_t enaddr[ETHER_ADDR_LEN];
bus_dma_segment_t seg;
int i, error, nseg;
- pcireg_t pmode;
- int pmreg;
char intrbuf[PCI_INTRSTR_LEN];
+ aprint_normal(": Micrel KSZ%04x Ethernet (rev. 0x%02x)\n",
+ PCI_PRODUCT(pa->pa_id), PCI_REVISION(pa->pa_class));
+
if (pci_mapreg_map(pa, 0x10,
PCI_MAPREG_TYPE_MEM | PCI_MAPREG_MEM_TYPE_32BIT,
- 0, &sc->sc_st, &sc->sc_sh, NULL, NULL) != 0) {
- printf(": unable to map device registers\n");
+ 0, &sc->sc_st, &sc->sc_sh, NULL, &sc->sc_memsize) != 0) {
+ aprint_error_dev(self, "unable to map device registers\n");
return;
}
- sc->sc_dev = self;
- sc->sc_dmat = pa->pa_dmat;
-
/* Make sure bus mastering is enabled. */
pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG) |
PCI_COMMAND_MASTER_ENABLE);
- /* Get it out of power save mode, if needed. */
- if (pci_get_capability(pc, pa->pa_tag, PCI_CAP_PWRMGMT, &pmreg, 0)) {
- pmode = pci_conf_read(pc, pa->pa_tag, pmreg + PCI_PMCSR) &
- PCI_PMCSR_STATE_MASK;
- if (pmode == PCI_PMCSR_STATE_D3) {
- /*
- * The card has lost all configuration data in
- * this state, so punt.
- */
- printf("%s: unable to wake from power state D3\n",
- device_xname(sc->sc_dev));
- return;
- }
- if (pmode != PCI_PMCSR_STATE_D0) {
- printf("%s: waking up from power date D%d\n",
- device_xname(sc->sc_dev), pmode);
- pci_conf_write(pc, pa->pa_tag, pmreg + PCI_PMCSR,
- PCI_PMCSR_STATE_D0);
- }
+ /* Power up chip if necessary. */
+ if ((error = pci_activate(pc, pa->pa_tag, self, NULL))
+ && error != EOPNOTSUPP) {
+ aprint_error_dev(self, "cannot activate %d\n", error);
+ return;
}
- sc->sc_chip = PCI_PRODUCT(pa->pa_id);
- printf(": Micrel KSZ%04x Ethernet (rev. 0x%02x)\n",
- sc->sc_chip, PCI_REVISION(pa->pa_class));
-
- /*
- * Read the Ethernet address from the EEPROM.
- */
- i = CSR_READ_2(sc, MARL);
- enaddr[5] = i; enaddr[4] = i >> 8;
- i = CSR_READ_2(sc, MARM);
- enaddr[3] = i; enaddr[2] = i >> 8;
- i = CSR_READ_2(sc, MARH);
- enaddr[1] = i; enaddr[0] = i >> 8;
- printf("%s: Ethernet address %s\n",
- device_xname(sc->sc_dev), ether_sprintf(enaddr));
-
- /*
- * Enable chip function.
- */
- CSR_WRITE_2(sc, CIDR, 1);
-
- /*
- * Map and establish our interrupt.
- */
+ /* Map and establish our interrupt. */
if (pci_intr_map(pa, &ih)) {
- aprint_error_dev(sc->sc_dev, "unable to map interrupt\n");
+ aprint_error_dev(self, "unable to map interrupt\n");
return;
}
intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf));
sc->sc_ih = pci_intr_establish_xname(pc, ih, IPL_NET, kse_intr, sc,
device_xname(self));
if (sc->sc_ih == NULL) {
- aprint_error_dev(sc->sc_dev, "unable to establish interrupt");
+ aprint_error_dev(self, "unable to establish interrupt");
if (intrstr != NULL)
aprint_error(" at %s", intrstr);
aprint_error("\n");
return;
}
- aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr);
+ aprint_normal_dev(self, "interrupting at %s\n", intrstr);
+
+ sc->sc_dev = self;
+ sc->sc_dmat = pa->pa_dmat;
+ sc->sc_pc = pa->pa_pc;
+ sc->sc_chip = PCI_PRODUCT(pa->pa_id);
+
+ /*
+ * Read the Ethernet address from the EEPROM.
+ */
+ i = CSR_READ_2(sc, MARL);
+ enaddr[5] = i;
+ enaddr[4] = i >> 8;
+ i = CSR_READ_2(sc, MARM);
+ enaddr[3] = i;
+ enaddr[2] = i >> 8;
+ i = CSR_READ_2(sc, MARH);
+ enaddr[1] = i;
+ enaddr[0] = i >> 8;
+ aprint_normal_dev(self,
+ "Ethernet address %s\n", ether_sprintf(enaddr));
+
+ /*
+ * Enable chip function.
+ */
+ CSR_WRITE_2(sc, SIDER, 1);
/*
* Allocate the control data structures, and create and load the
@@ -452,7 +449,7 @@ kse_attach(device_t parent, device_t sel
error = bus_dmamem_alloc(sc->sc_dmat,
sizeof(struct kse_control_data), PAGE_SIZE, 0, &seg, 1, &nseg, 0);
if (error != 0) {
- aprint_error_dev(sc->sc_dev,
+ aprint_error_dev(self,
"unable to allocate control data, error = %d\n", error);
goto fail_0;
}
@@ -460,7 +457,7 @@ kse_attach(device_t parent, device_t sel
sizeof(struct kse_control_data), (void **)&sc->sc_control_data,
BUS_DMA_COHERENT);
if (error != 0) {
- aprint_error_dev(sc->sc_dev,
+ aprint_error_dev(self,
"unable to map control data, error = %d\n", error);
goto fail_1;
}
@@ -468,7 +465,7 @@ kse_attach(device_t parent, device_t sel
sizeof(struct kse_control_data), 1,
sizeof(struct kse_control_data), 0, 0, &sc->sc_cddmamap);
if (error != 0) {
- aprint_error_dev(sc->sc_dev,
+ aprint_error_dev(self,
"unable to create control data DMA map, "
"error = %d\n", error);
goto fail_2;
@@ -476,7 +473,7 @@ kse_attach(device_t parent, device_t sel
error = bus_dmamap_load(sc->sc_dmat, sc->sc_cddmamap,
sc->sc_control_data, sizeof(struct kse_control_data), NULL, 0);
if (error != 0) {
- aprint_error_dev(sc->sc_dev,
+ aprint_error_dev(self,
"unable to load control data DMA map, error = %d\n",
error);
goto fail_3;
@@ -485,7 +482,7 @@ kse_attach(device_t parent, device_t sel
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
KSE_NTXSEGS, MCLBYTES, 0, 0,
&sc->sc_txsoft[i].txs_dmamap)) != 0) {
- aprint_error_dev(sc->sc_dev,
+ aprint_error_dev(self,
"unable to create tx DMA map %d, error = %d\n",
i, error);
goto fail_4;
@@ -494,7 +491,7 @@ kse_attach(device_t parent, device_t sel
for (i = 0; i < KSE_NRXDESC; i++) {
if ((error = bus_dmamap_create(sc->sc_dmat, MCLBYTES,
1, MCLBYTES, 0, 0, &sc->sc_rxsoft[i].rxs_dmamap)) != 0) {
- aprint_error_dev(sc->sc_dev,
+ aprint_error_dev(self,
"unable to create rx DMA map %d, error = %d\n",
i, error);
goto fail_5;
@@ -502,40 +499,53 @@ kse_attach(device_t parent, device_t sel
sc->sc_rxsoft[i].rxs_mbuf = NULL;
}
- callout_init(&sc->sc_callout, 0);
+ callout_init(&sc->sc_tick_ch, 0);
callout_init(&sc->sc_stat_ch, 0);
+ callout_setfunc(&sc->sc_tick_ch, phy_tick, sc);
+#ifdef KSE_EVENT_COUNTERS
+ callout_setfunc(&sc->sc_stat_ch, stat_tick, sc);
+#endif
+
+ mii->mii_ifp = ifp;
+ mii->mii_readreg = kse_mii_readreg;
+ mii->mii_writereg = kse_mii_writereg;
+ mii->mii_statchg = kse_mii_statchg;
/* Initialize ifmedia structures. */
- ifm = &sc->sc_media;
- sc->sc_ethercom.ec_ifmedia = ifm;
- sc->sc_linkstatus = 0;
+ sc->sc_flowflags = 0;
if (sc->sc_chip == 0x8841) {
- ifmedia_init(ifm, 0, ksephy_change, ksephy_status);
- ifmedia_add(ifm, IFM_ETHER | IFM_10_T, 0, NULL);
- ifmedia_add(ifm, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
- ifmedia_add(ifm, IFM_ETHER | IFM_100_TX, 0, NULL);
- ifmedia_add(ifm, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
- ifmedia_add(ifm, IFM_ETHER | IFM_AUTO, 0, NULL);
- ifmedia_set(ifm, IFM_ETHER | IFM_AUTO);
+ /* use port 1 builtin PHY as index 1 device */
+ sc->sc_ethercom.ec_mii = mii;
+ ifm = &mii->mii_media;
+ ifmedia_init(ifm, 0, kse_ifmedia_upd, kse_ifmedia_sts);
+ mii_attach(sc->sc_dev, mii, 0xffffffff, 1 /* PHY1 */,
+ MII_OFFSET_ANY, MIIF_DOPAUSE);
+ if (LIST_FIRST(&mii->mii_phys) == NULL) {
+ ifmedia_add(ifm, IFM_ETHER | IFM_NONE, 0, NULL);
+ ifmedia_set(ifm, IFM_ETHER | IFM_NONE);
+ } else
+ ifmedia_set(ifm, IFM_ETHER | IFM_AUTO);
} else {
/*
* pretend 100FDX w/ no alternative media selection.
- * 8842 MAC is tied with a builtin 3 port switch.
- * It can do rate control over either of tx / rx direction
- * respectively, tough, this driver leaves the rate unlimited
- * intending 100Mbps maximum.
- * 2 ports behave in AN mode and this driver provides no mean
- * to see the exact details.
+ * 8842 MAC is tied with a builtin 3 port switch. It can do
+ * 4 degree priotised rate control over either of tx/rx
+ * direction for any of ports, respectively. Tough, this
+ * driver leaves the rate unlimited intending 100Mbps maximum.
+ * 2 external ports behave in AN mode and this driver provides
+ * no mean to manipulate and see their operational details.
*/
- ifmedia_init(ifm, 0, NULL, nopifm_status);
+ sc->sc_ethercom.ec_ifmedia = ifm = &sc->sc_media;
+ ifmedia_init(ifm, 0, NULL, nopifmedia_sts);
ifmedia_add(ifm, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
ifmedia_set(ifm, IFM_ETHER | IFM_100_TX | IFM_FDX);
+
+ aprint_normal_dev(self,
+ "10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto\n");
}
+ ifm->ifm_media = ifm->ifm_cur->ifm_media; /* as if user has requested */
- printf("%s: 10baseT, 10baseT-FDX, 100baseTX, 100baseTX-FDX, auto\n",
- device_xname(sc->sc_dev));
- ifp = &sc->sc_ethercom.ec_if;
strlcpy(ifp->if_xname, device_xname(sc->sc_dev), IFNAMSIZ);
ifp->if_softc = sc;
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
@@ -547,11 +557,11 @@ kse_attach(device_t parent, device_t sel
IFQ_SET_READY(&ifp->if_snd);
/*
- * capable of 802.1Q VLAN-sized frames,
+ * capable of 802.1Q VLAN-sized frames and hw assisted tagging.
* can do IPv4, TCPv4, and UDPv4 checksums in hardware.
*/
- sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
- ifp->if_capabilities |=
+ sc->sc_ethercom.ec_capabilities = ETHERCAP_VLAN_MTU;
+ ifp->if_capabilities =
IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx |
IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx;
@@ -658,6 +668,14 @@ kse_attach(device_t parent, device_t sel
fail_1:
bus_dmamem_free(sc->sc_dmat, &seg, nseg);
fail_0:
+ if (sc->sc_ih != NULL) {
+ pci_intr_disestablish(pc, sc->sc_ih);
+ sc->sc_ih = NULL;
+ }
+ if (sc->sc_memsize) {
+ bus_space_unmap(sc->sc_st, sc->sc_sh, sc->sc_memsize);
+ sc->sc_memsize = 0;
+ }
return;
}
@@ -665,11 +683,30 @@ static int
kse_ioctl(struct ifnet *ifp, u_long cmd, void *data)
{
struct kse_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ struct ifmedia *ifm;
int s, error;
s = splnet();
switch (cmd) {
+ case SIOCSIFMEDIA:
+ /* Flow control requires full-duplex mode. */
+ if (IFM_SUBTYPE(ifr->ifr_media) == IFM_AUTO ||
+ (ifr->ifr_media & IFM_FDX) == 0)
+ ifr->ifr_media &= ~IFM_ETH_FMASK;
+ if (IFM_SUBTYPE(ifr->ifr_media) != IFM_AUTO) {
+ if ((ifr->ifr_media & IFM_ETH_FMASK) == IFM_FLOW) {
+ /* We can do both TXPAUSE and RXPAUSE. */
+ ifr->ifr_media |=
+ IFM_ETH_TXPAUSE | IFM_ETH_RXPAUSE;
+ }
+ sc->sc_flowflags = ifr->ifr_media & IFM_ETH_FMASK;
+ }
+ ifm = (sc->sc_chip == 0x8841)
+ ? &sc->sc_mii.mii_media : &sc->sc_media;
+ error = ifmedia_ioctl(ifp, ifr, ifm, cmd);
+ break;
default:
if ((error = ether_ioctl(ifp, cmd, data)) != ENETRESET)
break;
@@ -690,8 +727,6 @@ kse_ioctl(struct ifnet *ifp, u_long cmd,
break;
}
- kse_start(ifp);
-
splx(s);
return error;
}
@@ -737,9 +772,10 @@ kse_init(struct ifnet *ifp)
for (i = 0; i < KSE_NRXDESC; i++) {
if (sc->sc_rxsoft[i].rxs_mbuf == NULL) {
if ((error = add_rxbuf(sc, i)) != 0) {
- printf("%s: unable to allocate or map rx "
+ aprint_error_dev(sc->sc_dev,
+ "unable to allocate or map rx "
"buffer %d, error = %d\n",
- device_xname(sc->sc_dev), i, error);
+ i, error);
rxdrain(sc);
goto out;
}
@@ -753,8 +789,8 @@ kse_init(struct ifnet *ifp)
CSR_WRITE_4(sc, TDLB, KSE_CDTXADDR(sc, 0));
CSR_WRITE_4(sc, RDLB, KSE_CDRXADDR(sc, 0));
- sc->sc_txc = TXC_TEN | TXC_EP | TXC_AC | TXC_FCE;
- sc->sc_rxc = RXC_REN | RXC_RU | RXC_FCE;
+ sc->sc_txc = TXC_TEN | TXC_EP | TXC_AC;
+ sc->sc_rxc = RXC_REN | RXC_RU;
if (ifp->if_flags & IFF_PROMISC)
sc->sc_rxc |= RXC_RA;
if (ifp->if_flags & IFF_BROADCAST)
@@ -787,12 +823,17 @@ kse_init(struct ifnet *ifp)
sc->sc_txc |= (kse_burstsize << TXC_BS_SFT);
sc->sc_rxc |= (kse_burstsize << RXC_BS_SFT);
+ if (sc->sc_chip == 0x8842) {
+ sc->sc_txc |= TXC_FCE;
+ sc->sc_rxc |= RXC_FCE;
+ }
+
/* build multicast hash filter if necessary */
kse_set_filter(sc);
/* set current media */
if (sc->sc_chip == 0x8841)
- (void)ksephy_change(ifp);
+ (void)kse_ifmedia_upd(ifp);
/* enable transmitter and receiver */
CSR_WRITE_4(sc, MDTXC, sc->sc_txc);
@@ -811,19 +852,19 @@ kse_init(struct ifnet *ifp)
if (sc->sc_chip == 0x8841) {
/* start one second timer */
- callout_reset(&sc->sc_callout, hz, phy_tick, sc);
+ callout_schedule(&sc->sc_tick_ch, hz);
}
#ifdef KSE_EVENT_COUNTERS
- /* start statistics gather 1 minute timer */
+ /* start statistics gather 1 minute timer. should be tunable */
zerostats(sc);
- callout_reset(&sc->sc_stat_ch, hz * 60, stat_tick, sc);
+ callout_schedule(&sc->sc_stat_ch, hz * 60);
#endif
out:
if (error) {
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
ifp->if_timer = 0;
- printf("%s: interface not running\n", device_xname(sc->sc_dev));
+ aprint_error_dev(sc->sc_dev, "interface not running\n");
}
return error;
}
@@ -836,7 +877,7 @@ kse_stop(struct ifnet *ifp, int disable)
int i;
if (sc->sc_chip == 0x8841)
- callout_stop(&sc->sc_callout);
+ callout_stop(&sc->sc_tick_ch);
callout_stop(&sc->sc_stat_ch);
sc->sc_txc &= ~TXC_TEN;
@@ -864,11 +905,13 @@ static void
kse_reset(struct kse_softc *sc)
{
+ /* software reset */
CSR_WRITE_2(sc, GRR, 1);
delay(1000); /* PDF does not mention the delay amount */
CSR_WRITE_2(sc, GRR, 0);
- CSR_WRITE_2(sc, CIDR, 1);
+ /* enable switch function */
+ CSR_WRITE_2(sc, SIDER, 1);
}
static void
@@ -883,17 +926,16 @@ kse_watchdog(struct ifnet *ifp)
txreap(sc);
if (sc->sc_txfree != KSE_NTXDESC) {
- printf("%s: device timeout (txfree %d txsfree %d txnext %d)\n",
- device_xname(sc->sc_dev), sc->sc_txfree, sc->sc_txsfree,
- sc->sc_txnext);
+ aprint_error_dev(sc->sc_dev,
+ "device timeout (txfree %d txsfree %d txnext %d)\n",
+ sc->sc_txfree, sc->sc_txsfree, sc->sc_txnext);
ifp->if_oerrors++;
/* Reset the interface. */
kse_init(ifp);
}
else if (ifp->if_flags & IFF_DEBUG)
- printf("%s: recovered from device timeout\n",
- device_xname(sc->sc_dev));
+ aprint_error_dev(sc->sc_dev, "recovered from device timeout\n");
/* Try to get more packets going. */
kse_start(ifp);
@@ -937,9 +979,9 @@ kse_start(struct ifnet *ifp)
BUS_DMA_WRITE | BUS_DMA_NOWAIT);
if (error) {
if (error == EFBIG) {
- printf("%s: Tx packet consumes too many "
- "DMA segments, dropping...\n",
- device_xname(sc->sc_dev));
+ aprint_error_dev(sc->sc_dev,
+ "Tx packet consumes too many "
+ "DMA segments, dropping...\n");
IFQ_DEQUEUE(&ifp->if_snd, m0);
m_freem(m0);
continue;
@@ -1113,8 +1155,8 @@ add_rxbuf(struct kse_softc *sc, int idx)
error = bus_dmamap_load(sc->sc_dmat, rxs->rxs_dmamap,
m->m_ext.ext_buf, m->m_ext.ext_size, NULL, BUS_DMA_NOWAIT);
if (error) {
- printf("%s: can't load rx DMA map %d, error = %d\n",
- device_xname(sc->sc_dev), idx, error);
+ aprint_error_dev(sc->sc_dev,
+ "can't load rx DMA map %d, error = %d\n", idx, error);
panic("kse_add_rxbuf");
}
@@ -1158,7 +1200,7 @@ kse_intr(void *arg)
if (isr & INT_DMLCS)
lnkchg(sc);
if (isr & INT_DMRBUS)
- printf("%s: Rx descriptor full\n", device_xname(sc->sc_dev));
+ aprint_error_dev(sc->sc_dev, "Rx descriptor full\n");
CSR_WRITE_4(sc, INTST, isr);
return 1;
@@ -1190,8 +1232,8 @@ rxintr(struct kse_softc *sc)
ifp->if_ierrors++;
#define PRINTERR(bit, str) \
if (rxstat & (bit)) \
- printf("%s: receive error: %s\n", \
- device_xname(sc->sc_dev), str)
+ aprint_error_dev(sc->sc_dev, \
+ "%s\n", str)
PRINTERR(R0_TL, "frame too long");
PRINTERR(R0_RF, "runt frame");
PRINTERR(R0_CE, "bad FCS");
@@ -1232,7 +1274,8 @@ rxintr(struct kse_softc *sc)
if_percpuq_enqueue(ifp->if_percpuq, m);
#ifdef KSEDIAGNOSTIC
if (kse_monitor_rxintr > 0) {
- printf("m stat %x data %p len %d\n",
+ aprint_error_dev(sc->sc_dev,
+ "m stat %x data %p len %d\n",
rxstat, m->m_data, m->m_len);
}
#endif
@@ -1283,27 +1326,26 @@ lnkchg(struct kse_softc *sc)
{
struct ifmediareq ifmr;
-#if KSE_LINKDEBUG > 0
-printf("link change detected\n");
+#if KSE_LINKDEBUG == 1
+ uint16_t p1sr = CSR_READ_2(sc, P1SR);
+printf("link %s detected\n", (p1sr & PxSR_LINKUP) ? "up" : "down");
#endif
- ksephy_status(&sc->sc_ethercom.ec_if, &ifmr);
+ kse_ifmedia_sts(&sc->sc_ethercom.ec_if, &ifmr);
}
static int
-ksephy_change(struct ifnet *ifp)
+kse_ifmedia_upd(struct ifnet *ifp)
{
struct kse_softc *sc = ifp->if_softc;
- struct ifmedia *ifm = &sc->sc_media;
+ struct ifmedia *ifm = &sc->sc_mii.mii_media;
uint16_t p1cr4;
-#if KSE_LINKDEBUG > 0
-printf("ifm_media: %x\n", ifm->ifm_cur->ifm_media);
-#endif
+
p1cr4 = 0;
if (IFM_SUBTYPE(ifm->ifm_cur->ifm_media) == IFM_AUTO) {
p1cr4 |= PxCR_STARTNEG; /* restart AN */
p1cr4 |= PxCR_AUTOEN; /* enable AN */
p1cr4 |= PxCR_USEFC; /* advertise flow control pause */
- p1cr4 |= 0xf; /* advertise 100-FDX,100-HDX,10-FDX,10-HDX */
+ p1cr4 |= 0xf; /* adv. 100FDX,100HDX,10FDX,10HDX */
} else {
if (IFM_SUBTYPE(ifm->ifm_cur->ifm_media) == IFM_100_TX)
p1cr4 |= PxCR_SPD100;
@@ -1311,81 +1353,139 @@ printf("ifm_media: %x\n", ifm->ifm_cur->
p1cr4 |= PxCR_USEFDX;
}
CSR_WRITE_2(sc, P1CR4, p1cr4);
-#if KSE_LINKDEBUG > 0
+#if KSE_LINKDEBUG == 1
printf("P1CR4: %04x\n", p1cr4);
#endif
return 0;
}
static void
-ksephy_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+kse_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct kse_softc *sc = ifp->if_softc;
- int media_status;
- u_int media_active;
- uint16_t p1cr4, p1sr;
-
- media_status = IFM_AVALID;
- media_active = IFM_ETHER;
-
- p1cr4 = CSR_READ_2(sc, P1CR4);
- p1sr = CSR_READ_2(sc, P1SR);
-#if KSE_LINKDEBUG > 0
-printf("P1SR: %04x link %s\n", p1sr, (p1sr & PxSR_LINKUP) ? "up" : "down");
-#endif
- sc->sc_linkstatus = p1sr;
- if (p1sr & PxSR_LINKUP)
- media_status |= IFM_ACTIVE;
-
- if (p1cr4 & PxCR_AUTOEN) {
- if ((p1sr & PxSR_ACOMP) == 0) {
- media_active |= IFM_NONE;
- goto out; /* Negotiation in progress */
- }
- }
+ struct mii_data *mii = &sc->sc_mii;
- media_active |= (p1sr & PxSR_SPD100) ? IFM_100_TX : IFM_10_T;
- if (p1sr & PxSR_FDX)
- media_active |= IFM_FDX;
- if (p1sr & PxSR_RXFLOW)
- media_active |= IFM_FLOW | IFM_ETH_RXPAUSE;
- if (p1sr & PxSR_TXFLOW)
- media_active |= IFM_FLOW | IFM_ETH_TXPAUSE;
- out:
- ifmr->ifm_active = media_active;
- ifmr->ifm_status = media_status;
+ mii_pollstat(mii);
+ ifmr->ifm_status = mii->mii_media_status;
+ ifmr->ifm_active = (mii->mii_media_active & ~IFM_ETH_FMASK) |
+ sc->sc_flowflags;
}
static void
-nopifm_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+nopifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
struct kse_softc *sc = ifp->if_softc;
struct ifmedia *ifm = &sc->sc_media;
-#if KSE_LINKDEBUG > 1
+#if KSE_LINKDEBUG == 2
printf("p1sr: %04x, p2sr: %04x\n", CSR_READ_2(sc, P1SR), CSR_READ_2(sc, P2SR));
#endif
/* 8842 MAC pretends 100FDX all the time */
- ifmr->ifm_active = ifm->ifm_cur->ifm_media;
ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+ ifmr->ifm_active = ifm->ifm_cur->ifm_media |
+ IFM_FLOW | IFM_ETH_RXPAUSE | IFM_ETH_TXPAUSE;
}
static void
phy_tick(void *arg)
{
struct kse_softc *sc = arg;
- struct ifmediareq ifmr;
+ struct mii_data *mii = &sc->sc_mii;
int s;
- uint16_t p1sr;
s = splnet();
- p1sr = CSR_READ_2(sc, P1SR);
- if (sc->sc_linkstatus != p1sr)
- ksephy_status(&sc->sc_ethercom.ec_if, &ifmr);
+ mii_tick(mii);
splx(s);
- callout_reset(&sc->sc_callout, hz, phy_tick, sc);
+ callout_schedule(&sc->sc_tick_ch, hz);
+}
+
+static const uint16_t phy1csr[] = {
+ /* 0 BMCR */ 0x4d0,
+ /* 1 BMSR */ 0x4d2,
+ /* 2 PHYID1 */ 0x4d6, /* 0x0022 - PHY1HR */
+ /* 3 PHYID2 */ 0x4d4, /* 0x1430 - PHY1LR */
+ /* 4 ANAR */ 0x4d8,
+ /* 5 ANLPAR */ 0x4da,
+};
+
+int
+kse_mii_readreg(device_t self, int phy, int reg, uint16_t *val)
+{
+ struct kse_softc *sc = device_private(self);
+
+ if (phy != 1 || reg >= __arraycount(phy1csr) || reg < 0)
+ return EINVAL;
+ *val = CSR_READ_2(sc, phy1csr[reg]);
+ return 0;
+}
+
+int
+kse_mii_writereg(device_t self, int phy, int reg, uint16_t val)
+{
+ struct kse_softc *sc = device_private(self);
+
+ if (phy != 1 || reg >= __arraycount(phy1csr) || reg < 0)
+ return EINVAL;
+ CSR_WRITE_2(sc, phy1csr[reg], val);
+ return 0;
+}
+
+void
+kse_mii_statchg(struct ifnet *ifp)
+{
+ struct kse_softc *sc = ifp->if_softc;
+ struct mii_data *mii = &sc->sc_mii;
+
+#if KSE_LINKDEBUG == 1
+ /* decode P1SR register value */
+ uint16_t p1sr = CSR_READ_2(sc, P1SR);
+ printf("P1SR %04x, spd%d", p1sr, (p1sr & PxSR_SPD100) ? 100 : 10);
+ if (p1sr & PxSR_FDX)
+ printf(",full-duplex");
+ if (p1sr & PxSR_RXFLOW)
+ printf(",rxpause");
+ if (p1sr & PxSR_TXFLOW)
+ printf(",txpause");
+ printf("\n");
+ /* show resolved mii(4) parameters to compare against above */
+ printf("MII spd%d",
+ (int)(sc->sc_ethercom.ec_if.if_baudrate / IF_Mbps(1)));
+ if (mii->mii_media_active & IFM_FDX)
+ printf(",full-duplex");
+ if (mii->mii_media_active & IFM_FLOW) {
+ printf(",flowcontrol");
+ if (mii->mii_media_active & IFM_ETH_RXPAUSE)
+ printf(",rxpause");
+ if (mii->mii_media_active & IFM_ETH_TXPAUSE)
+ printf(",txpause");
+ }
+ printf("\n");
+#endif
+ /* Get flow control negotiation result. */
+ if (IFM_SUBTYPE(mii->mii_media.ifm_cur->ifm_media) == IFM_AUTO &&
+ (mii->mii_media_active & IFM_ETH_FMASK) != sc->sc_flowflags)
+ sc->sc_flowflags = mii->mii_media_active & IFM_ETH_FMASK;
+
+ /* Adjust MAC PAUSE flow control. */
+ if ((mii->mii_media_active & IFM_FDX)
+ && (sc->sc_flowflags & IFM_ETH_TXPAUSE))
+ sc->sc_txc |= TXC_FCE;
+ else
+ sc->sc_txc &= ~TXC_FCE;
+ if ((mii->mii_media_active & IFM_FDX)
+ && (sc->sc_flowflags & IFM_ETH_RXPAUSE))
+ sc->sc_rxc |= RXC_FCE;
+ else
+ sc->sc_rxc &= ~RXC_FCE;
+ CSR_WRITE_4(sc, MDTXC, sc->sc_txc);
+ CSR_WRITE_4(sc, MDRXC, sc->sc_rxc);
+#if KSE_LINKDEBUG == 1
+ printf("%ctxfce, %crxfce\n",
+ (sc->sc_txc & TXC_FCE) ? '+' : '-',
+ (sc->sc_rxc & RXC_FCE) ? '+' : '-');
+#endif
}
#ifdef KSE_EVENT_COUNTERS
@@ -1419,7 +1519,7 @@ stat_tick(void *arg)
CSR_WRITE_2(sc, IACR, 0x1c00 + 0x100 + p * 3 + 1);
ee->pev[p][33].ev_count = CSR_READ_2(sc, IADR4); /* 33 */
}
- callout_reset(&sc->sc_stat_ch, hz * 60, stat_tick, arg);
+ callout_schedule(&sc->sc_stat_ch, hz * 60);
}
static void