18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci	drivers/net/ethernet/dec/tulip/media.c
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci	Copyright 2000,2001  The Linux Kernel Team
58c2ecf20Sopenharmony_ci	Written/copyright 1994-2001 by Donald Becker.
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci	This software may be used and distributed according to the terms
88c2ecf20Sopenharmony_ci	of the GNU General Public License, incorporated herein by reference.
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci	Please submit bugs to http://bugzilla.kernel.org/ .
118c2ecf20Sopenharmony_ci*/
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/mii.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include "tulip.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/* The maximum data clock rate is 2.5 Mhz.  The minimum timing is usually
218c2ecf20Sopenharmony_ci   met by back-to-back PCI I/O cycles, but we insert a delay to avoid
228c2ecf20Sopenharmony_ci   "overclocking" issues or future 66Mhz PCI. */
238c2ecf20Sopenharmony_ci#define mdio_delay() ioread32(mdio_addr)
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* Read and write the MII registers using software-generated serial
268c2ecf20Sopenharmony_ci   MDIO protocol.  It is just different enough from the EEPROM protocol
278c2ecf20Sopenharmony_ci   to not share code.  The maxium data clock rate is 2.5 Mhz. */
288c2ecf20Sopenharmony_ci#define MDIO_SHIFT_CLK		0x10000
298c2ecf20Sopenharmony_ci#define MDIO_DATA_WRITE0	0x00000
308c2ecf20Sopenharmony_ci#define MDIO_DATA_WRITE1	0x20000
318c2ecf20Sopenharmony_ci#define MDIO_ENB		0x00000 /* Ignore the 0x02000 databook setting. */
328c2ecf20Sopenharmony_ci#define MDIO_ENB_IN		0x40000
338c2ecf20Sopenharmony_ci#define MDIO_DATA_READ		0x80000
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic const unsigned char comet_miireg2offset[32] = {
368c2ecf20Sopenharmony_ci	0xB4, 0xB8, 0xBC, 0xC0,  0xC4, 0xC8, 0xCC, 0,  0,0,0,0,  0,0,0,0,
378c2ecf20Sopenharmony_ci	0,0xD0,0,0,  0,0,0,0,  0,0,0,0, 0, 0xD4, 0xD8, 0xDC, };
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* MII transceiver control section.
418c2ecf20Sopenharmony_ci   Read and write the MII registers using software-generated serial
428c2ecf20Sopenharmony_ci   MDIO protocol.
438c2ecf20Sopenharmony_ci   See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions")
448c2ecf20Sopenharmony_ci   or DP83840A data sheet for more details.
458c2ecf20Sopenharmony_ci   */
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ciint tulip_mdio_read(struct net_device *dev, int phy_id, int location)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
508c2ecf20Sopenharmony_ci	int i;
518c2ecf20Sopenharmony_ci	int read_cmd = (0xf6 << 10) | ((phy_id & 0x1f) << 5) | location;
528c2ecf20Sopenharmony_ci	int retval = 0;
538c2ecf20Sopenharmony_ci	void __iomem *ioaddr = tp->base_addr;
548c2ecf20Sopenharmony_ci	void __iomem *mdio_addr = ioaddr + CSR9;
558c2ecf20Sopenharmony_ci	unsigned long flags;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	if (location & ~0x1f)
588c2ecf20Sopenharmony_ci		return 0xffff;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (tp->chip_id == COMET  &&  phy_id == 30) {
618c2ecf20Sopenharmony_ci		if (comet_miireg2offset[location])
628c2ecf20Sopenharmony_ci			return ioread32(ioaddr + comet_miireg2offset[location]);
638c2ecf20Sopenharmony_ci		return 0xffff;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tp->mii_lock, flags);
678c2ecf20Sopenharmony_ci	if (tp->chip_id == LC82C168) {
688c2ecf20Sopenharmony_ci		iowrite32(0x60020000 + (phy_id<<23) + (location<<18), ioaddr + 0xA0);
698c2ecf20Sopenharmony_ci		ioread32(ioaddr + 0xA0);
708c2ecf20Sopenharmony_ci		ioread32(ioaddr + 0xA0);
718c2ecf20Sopenharmony_ci		for (i = 1000; i >= 0; --i) {
728c2ecf20Sopenharmony_ci			barrier();
738c2ecf20Sopenharmony_ci			if ( ! ((retval = ioread32(ioaddr + 0xA0)) & 0x80000000))
748c2ecf20Sopenharmony_ci				break;
758c2ecf20Sopenharmony_ci		}
768c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&tp->mii_lock, flags);
778c2ecf20Sopenharmony_ci		return retval & 0xffff;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Establish sync by sending at least 32 logic ones. */
818c2ecf20Sopenharmony_ci	for (i = 32; i >= 0; i--) {
828c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
838c2ecf20Sopenharmony_ci		mdio_delay();
848c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
858c2ecf20Sopenharmony_ci		mdio_delay();
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	/* Shift the read command bits out. */
888c2ecf20Sopenharmony_ci	for (i = 15; i >= 0; i--) {
898c2ecf20Sopenharmony_ci		int dataval = (read_cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | dataval, mdio_addr);
928c2ecf20Sopenharmony_ci		mdio_delay();
938c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
948c2ecf20Sopenharmony_ci		mdio_delay();
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	/* Read the two transition, 16 data, and wire-idle bits. */
978c2ecf20Sopenharmony_ci	for (i = 19; i > 0; i--) {
988c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB_IN, mdio_addr);
998c2ecf20Sopenharmony_ci		mdio_delay();
1008c2ecf20Sopenharmony_ci		retval = (retval << 1) | ((ioread32(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
1018c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
1028c2ecf20Sopenharmony_ci		mdio_delay();
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tp->mii_lock, flags);
1068c2ecf20Sopenharmony_ci	return (retval>>1) & 0xffff;
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_civoid tulip_mdio_write(struct net_device *dev, int phy_id, int location, int val)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
1128c2ecf20Sopenharmony_ci	int i;
1138c2ecf20Sopenharmony_ci	int cmd = (0x5002 << 16) | ((phy_id & 0x1f) << 23) | (location<<18) | (val & 0xffff);
1148c2ecf20Sopenharmony_ci	void __iomem *ioaddr = tp->base_addr;
1158c2ecf20Sopenharmony_ci	void __iomem *mdio_addr = ioaddr + CSR9;
1168c2ecf20Sopenharmony_ci	unsigned long flags;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (location & ~0x1f)
1198c2ecf20Sopenharmony_ci		return;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (tp->chip_id == COMET && phy_id == 30) {
1228c2ecf20Sopenharmony_ci		if (comet_miireg2offset[location])
1238c2ecf20Sopenharmony_ci			iowrite32(val, ioaddr + comet_miireg2offset[location]);
1248c2ecf20Sopenharmony_ci		return;
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	spin_lock_irqsave(&tp->mii_lock, flags);
1288c2ecf20Sopenharmony_ci	if (tp->chip_id == LC82C168) {
1298c2ecf20Sopenharmony_ci		iowrite32(cmd, ioaddr + 0xA0);
1308c2ecf20Sopenharmony_ci		for (i = 1000; i >= 0; --i) {
1318c2ecf20Sopenharmony_ci			barrier();
1328c2ecf20Sopenharmony_ci			if ( ! (ioread32(ioaddr + 0xA0) & 0x80000000))
1338c2ecf20Sopenharmony_ci				break;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&tp->mii_lock, flags);
1368c2ecf20Sopenharmony_ci		return;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	/* Establish sync by sending 32 logic ones. */
1408c2ecf20Sopenharmony_ci	for (i = 32; i >= 0; i--) {
1418c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1, mdio_addr);
1428c2ecf20Sopenharmony_ci		mdio_delay();
1438c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
1448c2ecf20Sopenharmony_ci		mdio_delay();
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci	/* Shift the command bits out. */
1478c2ecf20Sopenharmony_ci	for (i = 31; i >= 0; i--) {
1488c2ecf20Sopenharmony_ci		int dataval = (cmd & (1 << i)) ? MDIO_DATA_WRITE1 : 0;
1498c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | dataval, mdio_addr);
1508c2ecf20Sopenharmony_ci		mdio_delay();
1518c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB | dataval | MDIO_SHIFT_CLK, mdio_addr);
1528c2ecf20Sopenharmony_ci		mdio_delay();
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	/* Clear out extra bits. */
1558c2ecf20Sopenharmony_ci	for (i = 2; i > 0; i--) {
1568c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB_IN, mdio_addr);
1578c2ecf20Sopenharmony_ci		mdio_delay();
1588c2ecf20Sopenharmony_ci		iowrite32(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
1598c2ecf20Sopenharmony_ci		mdio_delay();
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&tp->mii_lock, flags);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/* Set up the transceiver control registers for the selected media type. */
1678c2ecf20Sopenharmony_civoid tulip_select_media(struct net_device *dev, int startup)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
1708c2ecf20Sopenharmony_ci	void __iomem *ioaddr = tp->base_addr;
1718c2ecf20Sopenharmony_ci	struct mediatable *mtable = tp->mtable;
1728c2ecf20Sopenharmony_ci	u32 new_csr6;
1738c2ecf20Sopenharmony_ci	int i;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (mtable) {
1768c2ecf20Sopenharmony_ci		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
1778c2ecf20Sopenharmony_ci		unsigned char *p = mleaf->leafdata;
1788c2ecf20Sopenharmony_ci		switch (mleaf->type) {
1798c2ecf20Sopenharmony_ci		case 0:					/* 21140 non-MII xcvr. */
1808c2ecf20Sopenharmony_ci			if (tulip_debug > 1)
1818c2ecf20Sopenharmony_ci				netdev_dbg(dev, "Using a 21140 non-MII transceiver with control setting %02x\n",
1828c2ecf20Sopenharmony_ci					   p[1]);
1838c2ecf20Sopenharmony_ci			dev->if_port = p[0];
1848c2ecf20Sopenharmony_ci			if (startup)
1858c2ecf20Sopenharmony_ci				iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12);
1868c2ecf20Sopenharmony_ci			iowrite32(p[1], ioaddr + CSR12);
1878c2ecf20Sopenharmony_ci			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
1888c2ecf20Sopenharmony_ci			break;
1898c2ecf20Sopenharmony_ci		case 2: case 4: {
1908c2ecf20Sopenharmony_ci			u16 setup[5];
1918c2ecf20Sopenharmony_ci			u32 csr13val, csr14val, csr15dir, csr15val;
1928c2ecf20Sopenharmony_ci			for (i = 0; i < 5; i++)
1938c2ecf20Sopenharmony_ci				setup[i] = get_u16(&p[i*2 + 1]);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci			dev->if_port = p[0] & MEDIA_MASK;
1968c2ecf20Sopenharmony_ci			if (tulip_media_cap[dev->if_port] & MediaAlwaysFD)
1978c2ecf20Sopenharmony_ci				tp->full_duplex = 1;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci			if (startup && mtable->has_reset) {
2008c2ecf20Sopenharmony_ci				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
2018c2ecf20Sopenharmony_ci				unsigned char *rst = rleaf->leafdata;
2028c2ecf20Sopenharmony_ci				if (tulip_debug > 1)
2038c2ecf20Sopenharmony_ci					netdev_dbg(dev, "Resetting the transceiver\n");
2048c2ecf20Sopenharmony_ci				for (i = 0; i < rst[0]; i++)
2058c2ecf20Sopenharmony_ci					iowrite32(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
2068c2ecf20Sopenharmony_ci			}
2078c2ecf20Sopenharmony_ci			if (tulip_debug > 1)
2088c2ecf20Sopenharmony_ci				netdev_dbg(dev, "21143 non-MII %s transceiver control %04x/%04x\n",
2098c2ecf20Sopenharmony_ci					   medianame[dev->if_port],
2108c2ecf20Sopenharmony_ci					   setup[0], setup[1]);
2118c2ecf20Sopenharmony_ci			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
2128c2ecf20Sopenharmony_ci				csr13val = setup[0];
2138c2ecf20Sopenharmony_ci				csr14val = setup[1];
2148c2ecf20Sopenharmony_ci				csr15dir = (setup[3]<<16) | setup[2];
2158c2ecf20Sopenharmony_ci				csr15val = (setup[4]<<16) | setup[2];
2168c2ecf20Sopenharmony_ci				iowrite32(0, ioaddr + CSR13);
2178c2ecf20Sopenharmony_ci				iowrite32(csr14val, ioaddr + CSR14);
2188c2ecf20Sopenharmony_ci				iowrite32(csr15dir, ioaddr + CSR15);	/* Direction */
2198c2ecf20Sopenharmony_ci				iowrite32(csr15val, ioaddr + CSR15);	/* Data */
2208c2ecf20Sopenharmony_ci				iowrite32(csr13val, ioaddr + CSR13);
2218c2ecf20Sopenharmony_ci			} else {
2228c2ecf20Sopenharmony_ci				csr13val = 1;
2238c2ecf20Sopenharmony_ci				csr14val = 0;
2248c2ecf20Sopenharmony_ci				csr15dir = (setup[0]<<16) | 0x0008;
2258c2ecf20Sopenharmony_ci				csr15val = (setup[1]<<16) | 0x0008;
2268c2ecf20Sopenharmony_ci				if (dev->if_port <= 4)
2278c2ecf20Sopenharmony_ci					csr14val = t21142_csr14[dev->if_port];
2288c2ecf20Sopenharmony_ci				if (startup) {
2298c2ecf20Sopenharmony_ci					iowrite32(0, ioaddr + CSR13);
2308c2ecf20Sopenharmony_ci					iowrite32(csr14val, ioaddr + CSR14);
2318c2ecf20Sopenharmony_ci				}
2328c2ecf20Sopenharmony_ci				iowrite32(csr15dir, ioaddr + CSR15);	/* Direction */
2338c2ecf20Sopenharmony_ci				iowrite32(csr15val, ioaddr + CSR15);	/* Data */
2348c2ecf20Sopenharmony_ci				if (startup) iowrite32(csr13val, ioaddr + CSR13);
2358c2ecf20Sopenharmony_ci			}
2368c2ecf20Sopenharmony_ci			if (tulip_debug > 1)
2378c2ecf20Sopenharmony_ci				netdev_dbg(dev, "Setting CSR15 to %08x/%08x\n",
2388c2ecf20Sopenharmony_ci					   csr15dir, csr15val);
2398c2ecf20Sopenharmony_ci			if (mleaf->type == 4)
2408c2ecf20Sopenharmony_ci				new_csr6 = 0x82020000 | ((setup[2] & 0x71) << 18);
2418c2ecf20Sopenharmony_ci			else
2428c2ecf20Sopenharmony_ci				new_csr6 = 0x82420000;
2438c2ecf20Sopenharmony_ci			break;
2448c2ecf20Sopenharmony_ci		}
2458c2ecf20Sopenharmony_ci		case 1: case 3: {
2468c2ecf20Sopenharmony_ci			int phy_num = p[0];
2478c2ecf20Sopenharmony_ci			int init_length = p[1];
2488c2ecf20Sopenharmony_ci			u16 *misc_info, tmp_info;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci			dev->if_port = 11;
2518c2ecf20Sopenharmony_ci			new_csr6 = 0x020E0000;
2528c2ecf20Sopenharmony_ci			if (mleaf->type == 3) {	/* 21142 */
2538c2ecf20Sopenharmony_ci				u16 *init_sequence = (u16*)(p+2);
2548c2ecf20Sopenharmony_ci				u16 *reset_sequence = &((u16*)(p+3))[init_length];
2558c2ecf20Sopenharmony_ci				int reset_length = p[2 + init_length*2];
2568c2ecf20Sopenharmony_ci				misc_info = reset_sequence + reset_length;
2578c2ecf20Sopenharmony_ci				if (startup) {
2588c2ecf20Sopenharmony_ci					int timeout = 10;	/* max 1 ms */
2598c2ecf20Sopenharmony_ci					for (i = 0; i < reset_length; i++)
2608c2ecf20Sopenharmony_ci						iowrite32(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci					/* flush posted writes */
2638c2ecf20Sopenharmony_ci					ioread32(ioaddr + CSR15);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci					/* Sect 3.10.3 in DP83840A.pdf (p39) */
2668c2ecf20Sopenharmony_ci					udelay(500);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci					/* Section 4.2 in DP83840A.pdf (p43) */
2698c2ecf20Sopenharmony_ci					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
2708c2ecf20Sopenharmony_ci					while (timeout-- &&
2718c2ecf20Sopenharmony_ci						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
2728c2ecf20Sopenharmony_ci						udelay(100);
2738c2ecf20Sopenharmony_ci				}
2748c2ecf20Sopenharmony_ci				for (i = 0; i < init_length; i++)
2758c2ecf20Sopenharmony_ci					iowrite32(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci				ioread32(ioaddr + CSR15);	/* flush posted writes */
2788c2ecf20Sopenharmony_ci			} else {
2798c2ecf20Sopenharmony_ci				u8 *init_sequence = p + 2;
2808c2ecf20Sopenharmony_ci				u8 *reset_sequence = p + 3 + init_length;
2818c2ecf20Sopenharmony_ci				int reset_length = p[2 + init_length];
2828c2ecf20Sopenharmony_ci				misc_info = (u16*)(reset_sequence + reset_length);
2838c2ecf20Sopenharmony_ci				if (startup) {
2848c2ecf20Sopenharmony_ci					int timeout = 10;	/* max 1 ms */
2858c2ecf20Sopenharmony_ci					iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12);
2868c2ecf20Sopenharmony_ci					for (i = 0; i < reset_length; i++)
2878c2ecf20Sopenharmony_ci						iowrite32(reset_sequence[i], ioaddr + CSR12);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci					/* flush posted writes */
2908c2ecf20Sopenharmony_ci					ioread32(ioaddr + CSR12);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci					/* Sect 3.10.3 in DP83840A.pdf (p39) */
2938c2ecf20Sopenharmony_ci					udelay(500);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci					/* Section 4.2 in DP83840A.pdf (p43) */
2968c2ecf20Sopenharmony_ci					/* and IEEE 802.3 "22.2.4.1.1 Reset" */
2978c2ecf20Sopenharmony_ci					while (timeout-- &&
2988c2ecf20Sopenharmony_ci						(tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET))
2998c2ecf20Sopenharmony_ci						udelay(100);
3008c2ecf20Sopenharmony_ci				}
3018c2ecf20Sopenharmony_ci				for (i = 0; i < init_length; i++)
3028c2ecf20Sopenharmony_ci					iowrite32(init_sequence[i], ioaddr + CSR12);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci				ioread32(ioaddr + CSR12);	/* flush posted writes */
3058c2ecf20Sopenharmony_ci			}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci			tmp_info = get_u16(&misc_info[1]);
3088c2ecf20Sopenharmony_ci			if (tmp_info)
3098c2ecf20Sopenharmony_ci				tp->advertising[phy_num] = tmp_info | 1;
3108c2ecf20Sopenharmony_ci			if (tmp_info && startup < 2) {
3118c2ecf20Sopenharmony_ci				if (tp->mii_advertise == 0)
3128c2ecf20Sopenharmony_ci					tp->mii_advertise = tp->advertising[phy_num];
3138c2ecf20Sopenharmony_ci				if (tulip_debug > 1)
3148c2ecf20Sopenharmony_ci					netdev_dbg(dev, " Advertising %04x on MII %d\n",
3158c2ecf20Sopenharmony_ci						   tp->mii_advertise,
3168c2ecf20Sopenharmony_ci						   tp->phys[phy_num]);
3178c2ecf20Sopenharmony_ci				tulip_mdio_write(dev, tp->phys[phy_num], 4, tp->mii_advertise);
3188c2ecf20Sopenharmony_ci			}
3198c2ecf20Sopenharmony_ci			break;
3208c2ecf20Sopenharmony_ci		}
3218c2ecf20Sopenharmony_ci		case 5: case 6: {
3228c2ecf20Sopenharmony_ci			new_csr6 = 0; /* FIXME */
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci			if (startup && mtable->has_reset) {
3258c2ecf20Sopenharmony_ci				struct medialeaf *rleaf = &mtable->mleaf[mtable->has_reset];
3268c2ecf20Sopenharmony_ci				unsigned char *rst = rleaf->leafdata;
3278c2ecf20Sopenharmony_ci				if (tulip_debug > 1)
3288c2ecf20Sopenharmony_ci					netdev_dbg(dev, "Resetting the transceiver\n");
3298c2ecf20Sopenharmony_ci				for (i = 0; i < rst[0]; i++)
3308c2ecf20Sopenharmony_ci					iowrite32(get_u16(rst + 1 + (i<<1)) << 16, ioaddr + CSR15);
3318c2ecf20Sopenharmony_ci			}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci			break;
3348c2ecf20Sopenharmony_ci		}
3358c2ecf20Sopenharmony_ci		default:
3368c2ecf20Sopenharmony_ci			netdev_dbg(dev, " Invalid media table selection %d\n",
3378c2ecf20Sopenharmony_ci				   mleaf->type);
3388c2ecf20Sopenharmony_ci			new_csr6 = 0x020E0000;
3398c2ecf20Sopenharmony_ci		}
3408c2ecf20Sopenharmony_ci		if (tulip_debug > 1)
3418c2ecf20Sopenharmony_ci			netdev_dbg(dev, "Using media type %s, CSR12 is %02x\n",
3428c2ecf20Sopenharmony_ci				   medianame[dev->if_port],
3438c2ecf20Sopenharmony_ci				   ioread32(ioaddr + CSR12) & 0xff);
3448c2ecf20Sopenharmony_ci	} else if (tp->chip_id == LC82C168) {
3458c2ecf20Sopenharmony_ci		if (startup && ! tp->medialock)
3468c2ecf20Sopenharmony_ci			dev->if_port = tp->mii_cnt ? 11 : 0;
3478c2ecf20Sopenharmony_ci		if (tulip_debug > 1)
3488c2ecf20Sopenharmony_ci			netdev_dbg(dev, "PNIC PHY status is %3.3x, media %s\n",
3498c2ecf20Sopenharmony_ci				   ioread32(ioaddr + 0xB8),
3508c2ecf20Sopenharmony_ci				   medianame[dev->if_port]);
3518c2ecf20Sopenharmony_ci		if (tp->mii_cnt) {
3528c2ecf20Sopenharmony_ci			new_csr6 = 0x810C0000;
3538c2ecf20Sopenharmony_ci			iowrite32(0x0001, ioaddr + CSR15);
3548c2ecf20Sopenharmony_ci			iowrite32(0x0201B07A, ioaddr + 0xB8);
3558c2ecf20Sopenharmony_ci		} else if (startup) {
3568c2ecf20Sopenharmony_ci			/* Start with 10mbps to do autonegotiation. */
3578c2ecf20Sopenharmony_ci			iowrite32(0x32, ioaddr + CSR12);
3588c2ecf20Sopenharmony_ci			new_csr6 = 0x00420000;
3598c2ecf20Sopenharmony_ci			iowrite32(0x0001B078, ioaddr + 0xB8);
3608c2ecf20Sopenharmony_ci			iowrite32(0x0201B078, ioaddr + 0xB8);
3618c2ecf20Sopenharmony_ci		} else if (dev->if_port == 3  ||  dev->if_port == 5) {
3628c2ecf20Sopenharmony_ci			iowrite32(0x33, ioaddr + CSR12);
3638c2ecf20Sopenharmony_ci			new_csr6 = 0x01860000;
3648c2ecf20Sopenharmony_ci			/* Trigger autonegotiation. */
3658c2ecf20Sopenharmony_ci			iowrite32(startup ? 0x0201F868 : 0x0001F868, ioaddr + 0xB8);
3668c2ecf20Sopenharmony_ci		} else {
3678c2ecf20Sopenharmony_ci			iowrite32(0x32, ioaddr + CSR12);
3688c2ecf20Sopenharmony_ci			new_csr6 = 0x00420000;
3698c2ecf20Sopenharmony_ci			iowrite32(0x1F078, ioaddr + 0xB8);
3708c2ecf20Sopenharmony_ci		}
3718c2ecf20Sopenharmony_ci	} else {					/* Unknown chip type with no media table. */
3728c2ecf20Sopenharmony_ci		if (tp->default_port == 0)
3738c2ecf20Sopenharmony_ci			dev->if_port = tp->mii_cnt ? 11 : 3;
3748c2ecf20Sopenharmony_ci		if (tulip_media_cap[dev->if_port] & MediaIsMII) {
3758c2ecf20Sopenharmony_ci			new_csr6 = 0x020E0000;
3768c2ecf20Sopenharmony_ci		} else if (tulip_media_cap[dev->if_port] & MediaIsFx) {
3778c2ecf20Sopenharmony_ci			new_csr6 = 0x02860000;
3788c2ecf20Sopenharmony_ci		} else
3798c2ecf20Sopenharmony_ci			new_csr6 = 0x03860000;
3808c2ecf20Sopenharmony_ci		if (tulip_debug > 1)
3818c2ecf20Sopenharmony_ci			netdev_dbg(dev, "No media description table, assuming %s transceiver, CSR12 %02x\n",
3828c2ecf20Sopenharmony_ci				   medianame[dev->if_port],
3838c2ecf20Sopenharmony_ci				   ioread32(ioaddr + CSR12));
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	mdelay(1);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci/*
3928c2ecf20Sopenharmony_ci  Check the MII negotiated duplex and change the CSR6 setting if
3938c2ecf20Sopenharmony_ci  required.
3948c2ecf20Sopenharmony_ci  Return 0 if everything is OK.
3958c2ecf20Sopenharmony_ci  Return < 0 if the transceiver is missing or has no link beat.
3968c2ecf20Sopenharmony_ci  */
3978c2ecf20Sopenharmony_ciint tulip_check_duplex(struct net_device *dev)
3988c2ecf20Sopenharmony_ci{
3998c2ecf20Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
4008c2ecf20Sopenharmony_ci	unsigned int bmsr, lpa, negotiated, new_csr6;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR);
4038c2ecf20Sopenharmony_ci	lpa = tulip_mdio_read(dev, tp->phys[0], MII_LPA);
4048c2ecf20Sopenharmony_ci	if (tulip_debug > 1)
4058c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "MII status %04x, Link partner report %04x\n",
4068c2ecf20Sopenharmony_ci			 bmsr, lpa);
4078c2ecf20Sopenharmony_ci	if (bmsr == 0xffff)
4088c2ecf20Sopenharmony_ci		return -2;
4098c2ecf20Sopenharmony_ci	if ((bmsr & BMSR_LSTATUS) == 0) {
4108c2ecf20Sopenharmony_ci		int new_bmsr = tulip_mdio_read(dev, tp->phys[0], MII_BMSR);
4118c2ecf20Sopenharmony_ci		if ((new_bmsr & BMSR_LSTATUS) == 0) {
4128c2ecf20Sopenharmony_ci			if (tulip_debug  > 1)
4138c2ecf20Sopenharmony_ci				dev_info(&dev->dev,
4148c2ecf20Sopenharmony_ci					 "No link beat on the MII interface, status %04x\n",
4158c2ecf20Sopenharmony_ci					 new_bmsr);
4168c2ecf20Sopenharmony_ci			return -1;
4178c2ecf20Sopenharmony_ci		}
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci	negotiated = lpa & tp->advertising[0];
4208c2ecf20Sopenharmony_ci	tp->full_duplex = mii_duplex(tp->full_duplex_lock, negotiated);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	new_csr6 = tp->csr6;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (negotiated & LPA_100) new_csr6 &= ~TxThreshold;
4258c2ecf20Sopenharmony_ci	else			  new_csr6 |= TxThreshold;
4268c2ecf20Sopenharmony_ci	if (tp->full_duplex) new_csr6 |= FullDuplex;
4278c2ecf20Sopenharmony_ci	else		     new_csr6 &= ~FullDuplex;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (new_csr6 != tp->csr6) {
4308c2ecf20Sopenharmony_ci		tp->csr6 = new_csr6;
4318c2ecf20Sopenharmony_ci		tulip_restart_rxtx(tp);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci		if (tulip_debug > 0)
4348c2ecf20Sopenharmony_ci			dev_info(&dev->dev,
4358c2ecf20Sopenharmony_ci				 "Setting %s-duplex based on MII#%d link partner capability of %04x\n",
4368c2ecf20Sopenharmony_ci				 tp->full_duplex ? "full" : "half",
4378c2ecf20Sopenharmony_ci				 tp->phys[0], lpa);
4388c2ecf20Sopenharmony_ci		return 1;
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	return 0;
4428c2ecf20Sopenharmony_ci}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_civoid tulip_find_mii(struct net_device *dev, int board_idx)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	struct tulip_private *tp = netdev_priv(dev);
4478c2ecf20Sopenharmony_ci	int phyn, phy_idx = 0;
4488c2ecf20Sopenharmony_ci	int mii_reg0;
4498c2ecf20Sopenharmony_ci	int mii_advert;
4508c2ecf20Sopenharmony_ci	unsigned int to_advert, new_bmcr, ane_switch;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* Find the connected MII xcvrs.
4538c2ecf20Sopenharmony_ci	   Doing this in open() would allow detecting external xcvrs later,
4548c2ecf20Sopenharmony_ci	   but takes much time. */
4558c2ecf20Sopenharmony_ci	for (phyn = 1; phyn <= 32 && phy_idx < ARRAY_SIZE(tp->phys); phyn++) {
4568c2ecf20Sopenharmony_ci		int phy = phyn & 0x1f;
4578c2ecf20Sopenharmony_ci		int mii_status = tulip_mdio_read (dev, phy, MII_BMSR);
4588c2ecf20Sopenharmony_ci		if ((mii_status & 0x8301) == 0x8001 ||
4598c2ecf20Sopenharmony_ci		    ((mii_status & BMSR_100BASE4) == 0 &&
4608c2ecf20Sopenharmony_ci		     (mii_status & 0x7800) != 0)) {
4618c2ecf20Sopenharmony_ci			/* preserve Becker logic, gain indentation level */
4628c2ecf20Sopenharmony_ci		} else {
4638c2ecf20Sopenharmony_ci			continue;
4648c2ecf20Sopenharmony_ci		}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		mii_reg0 = tulip_mdio_read (dev, phy, MII_BMCR);
4678c2ecf20Sopenharmony_ci		mii_advert = tulip_mdio_read (dev, phy, MII_ADVERTISE);
4688c2ecf20Sopenharmony_ci		ane_switch = 0;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		/* if not advertising at all, gen an
4718c2ecf20Sopenharmony_ci		 * advertising value from the capability
4728c2ecf20Sopenharmony_ci		 * bits in BMSR
4738c2ecf20Sopenharmony_ci		 */
4748c2ecf20Sopenharmony_ci		if ((mii_advert & ADVERTISE_ALL) == 0) {
4758c2ecf20Sopenharmony_ci			unsigned int tmpadv = tulip_mdio_read (dev, phy, MII_BMSR);
4768c2ecf20Sopenharmony_ci			mii_advert = ((tmpadv >> 6) & 0x3e0) | 1;
4778c2ecf20Sopenharmony_ci		}
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci		if (tp->mii_advertise) {
4808c2ecf20Sopenharmony_ci			tp->advertising[phy_idx] =
4818c2ecf20Sopenharmony_ci			to_advert = tp->mii_advertise;
4828c2ecf20Sopenharmony_ci		} else if (tp->advertising[phy_idx]) {
4838c2ecf20Sopenharmony_ci			to_advert = tp->advertising[phy_idx];
4848c2ecf20Sopenharmony_ci		} else {
4858c2ecf20Sopenharmony_ci			tp->advertising[phy_idx] =
4868c2ecf20Sopenharmony_ci			tp->mii_advertise =
4878c2ecf20Sopenharmony_ci			to_advert = mii_advert;
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		tp->phys[phy_idx++] = phy;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci		pr_info("tulip%d:  MII transceiver #%d config %04x status %04x advertising %04x\n",
4938c2ecf20Sopenharmony_ci			board_idx, phy, mii_reg0, mii_status, mii_advert);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		/* Fixup for DLink with miswired PHY. */
4968c2ecf20Sopenharmony_ci		if (mii_advert != to_advert) {
4978c2ecf20Sopenharmony_ci			pr_debug("tulip%d:  Advertising %04x on PHY %d, previously advertising %04x\n",
4988c2ecf20Sopenharmony_ci				 board_idx, to_advert, phy, mii_advert);
4998c2ecf20Sopenharmony_ci			tulip_mdio_write (dev, phy, 4, to_advert);
5008c2ecf20Sopenharmony_ci		}
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		/* Enable autonegotiation: some boards default to off. */
5038c2ecf20Sopenharmony_ci		if (tp->default_port == 0) {
5048c2ecf20Sopenharmony_ci			new_bmcr = mii_reg0 | BMCR_ANENABLE;
5058c2ecf20Sopenharmony_ci			if (new_bmcr != mii_reg0) {
5068c2ecf20Sopenharmony_ci				new_bmcr |= BMCR_ANRESTART;
5078c2ecf20Sopenharmony_ci				ane_switch = 1;
5088c2ecf20Sopenharmony_ci			}
5098c2ecf20Sopenharmony_ci		}
5108c2ecf20Sopenharmony_ci		/* ...or disable nway, if forcing media */
5118c2ecf20Sopenharmony_ci		else {
5128c2ecf20Sopenharmony_ci			new_bmcr = mii_reg0 & ~BMCR_ANENABLE;
5138c2ecf20Sopenharmony_ci			if (new_bmcr != mii_reg0)
5148c2ecf20Sopenharmony_ci				ane_switch = 1;
5158c2ecf20Sopenharmony_ci		}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		/* clear out bits we never want at this point */
5188c2ecf20Sopenharmony_ci		new_bmcr &= ~(BMCR_CTST | BMCR_FULLDPLX | BMCR_ISOLATE |
5198c2ecf20Sopenharmony_ci			      BMCR_PDOWN | BMCR_SPEED100 | BMCR_LOOPBACK |
5208c2ecf20Sopenharmony_ci			      BMCR_RESET);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci		if (tp->full_duplex)
5238c2ecf20Sopenharmony_ci			new_bmcr |= BMCR_FULLDPLX;
5248c2ecf20Sopenharmony_ci		if (tulip_media_cap[tp->default_port] & MediaIs100)
5258c2ecf20Sopenharmony_ci			new_bmcr |= BMCR_SPEED100;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci		if (new_bmcr != mii_reg0) {
5288c2ecf20Sopenharmony_ci			/* some phys need the ANE switch to
5298c2ecf20Sopenharmony_ci			 * happen before forced media settings
5308c2ecf20Sopenharmony_ci			 * will "take."  However, we write the
5318c2ecf20Sopenharmony_ci			 * same value twice in order not to
5328c2ecf20Sopenharmony_ci			 * confuse the sane phys.
5338c2ecf20Sopenharmony_ci			 */
5348c2ecf20Sopenharmony_ci			if (ane_switch) {
5358c2ecf20Sopenharmony_ci				tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr);
5368c2ecf20Sopenharmony_ci				udelay (10);
5378c2ecf20Sopenharmony_ci			}
5388c2ecf20Sopenharmony_ci			tulip_mdio_write (dev, phy, MII_BMCR, new_bmcr);
5398c2ecf20Sopenharmony_ci		}
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci	tp->mii_cnt = phy_idx;
5428c2ecf20Sopenharmony_ci	if (tp->mtable && tp->mtable->has_mii && phy_idx == 0) {
5438c2ecf20Sopenharmony_ci		pr_info("tulip%d: ***WARNING***: No MII transceiver found!\n",
5448c2ecf20Sopenharmony_ci			board_idx);
5458c2ecf20Sopenharmony_ci		tp->phys[0] = 1;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci}
548