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

Reply via email to