[U-Boot] [PATCH] Add MII bus implementation for FCC ports (using bitbanging)

Luigi 'Comio' Mantellini luigi.mantellini.ml at gmail.com
Sat Sep 19 17:42:06 CEST 2009


Signed-off-by: Luigi 'Comio' Mantellini <luigi.mantellini at idf-hit.com>
---
 cpu/mpc85xx/ether_fcc.c |  400 ++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 379 insertions(+), 21 deletions(-)

diff --git a/cpu/mpc85xx/ether_fcc.c b/cpu/mpc85xx/ether_fcc.c
index 5f1414d..7d8234e 100644
--- a/cpu/mpc85xx/ether_fcc.c
+++ b/cpu/mpc85xx/ether_fcc.c
@@ -1,5 +1,9 @@
 /*
  * MPC8560 FCC Fast Ethernet
+ *
+ * Copyright (c) 2009 Industrie Dial Face S.p.A.
+ * Luigi 'Comio' Mantellini <luigi.mantellini at idf-hit.com>
+ *
  * Copyright (c) 2003 Motorola,Inc.
  * Xianghua Xiao, (X.Xiao at motorola.com)
  *
@@ -55,7 +59,11 @@
 #if defined(CONFIG_ETHER_ON_FCC) && defined(CONFIG_CMD_NET) && \
 	defined(CONFIG_NET_MULTI)
 
-static struct ether_fcc_info_s
+extern int mpc85xx_init_phy(struct eth_device *dev);
+
+struct ether_fcc_info_s;
+
+struct ether_fcc_info_s
 {
 	int ether_index;
 	int proff_enet;
@@ -63,39 +71,60 @@ static struct ether_fcc_info_s
 	ulong cpm_cr_enet_page;
 	ulong cmxfcr_mask;
 	ulong cmxfcr_value;
-}
-	ether_fcc_info[] =
+#ifdef CONFIG_MPC85XX_BITBANGMII
+	int mdiopin;
+	int mdcpin;
+	uint phyaddr;
+#endif
+};
+
+static struct ether_fcc_info_s ether_fcc_info[] =
 {
 #ifdef CONFIG_ETHER_ON_FCC1
 {
-	0,
-	PROFF_FCC1,
-	CPM_CR_FCC1_SBLOCK,
-	CPM_CR_FCC1_PAGE,
-	CONFIG_SYS_CMXFCR_MASK1,
-	CONFIG_SYS_CMXFCR_VALUE1
+	.ether_index = 0,
+	.proff_enet = PROFF_FCC1,
+	.cpm_cr_enet_sblock = CPM_CR_FCC1_SBLOCK,
+	.cpm_cr_enet_page = CPM_CR_FCC1_PAGE,
+	.cmxfcr_mask = CONFIG_SYS_CMXFCR_MASK1,
+	.cmxfcr_value = CONFIG_SYS_CMXFCR_VALUE1,
+#ifdef CONFIG_MPC85XX_BITBANGMII
+	.mdiopin = CONFIG_SYS_FCC1_MDIO,
+	.mdcpin = CONFIG_SYS_FCC1_MDC,
+	.phyaddr = CONFIG_SYS_FCC1_PHYADDR
+#endif
 },
 #endif
 
 #ifdef CONFIG_ETHER_ON_FCC2
 {
-	1,
-	PROFF_FCC2,
-	CPM_CR_FCC2_SBLOCK,
-	CPM_CR_FCC2_PAGE,
-	CONFIG_SYS_CMXFCR_MASK2,
-	CONFIG_SYS_CMXFCR_VALUE2
+	.ether_index = 1,
+	.proff_enet = PROFF_FCC2,
+	.cpm_cr_enet_sblock = CPM_CR_FCC2_SBLOCK,
+	.cpm_cr_enet_page = CPM_CR_FCC2_PAGE,
+	.cmxfcr_mask = CONFIG_SYS_CMXFCR_MASK2,
+	.cmxfcr_value = CONFIG_SYS_CMXFCR_VALUE2,
+#ifdef CONFIG_MPC85XX_BITBANGMII
+	.mdiopin =CONFIG_SYS_FCC2_MDIO,
+	.mdcpin = CONFIG_SYS_FCC2_MDC,
+	.phyaddr = CONFIG_SYS_FCC2_PHYADDR
+#endif
 },
 #endif
 
 #ifdef CONFIG_ETHER_ON_FCC3
 {
-	2,
-	PROFF_FCC3,
-	CPM_CR_FCC3_SBLOCK,
-	CPM_CR_FCC3_PAGE,
-	CONFIG_SYS_CMXFCR_MASK3,
-	CONFIG_SYS_CMXFCR_VALUE3
+	.ether_index = 2,
+	.proff_enet = PROFF_FCC3,
+	.cpm_cr_enet_sblock = CPM_CR_FCC3_SBLOCK,
+	.cpm_cr_enet_page = CPM_CR_FCC3_PAGE,
+	.cmxfcr_mask = CONFIG_SYS_CMXFCR_MASK3,
+	.cmxfcr_value = CONFIG_SYS_CMXFCR_VALUE3,
+#ifdef CONFIG_MPC85XX_BITBANGMII
+	.mdiopin =CONFIG_SYS_FCC3_MDIO,
+	.mdcpin = CONFIG_SYS_FCC3_MDC,
+	.phyaddr = CONFIG_SYS_FCC3_PHYADDR
+#endif
 },
 #endif
 };
@@ -139,6 +168,329 @@ static RTXBD rtx __attribute__ ((aligned(8)));
 
 #undef ET_DEBUG
 
+#ifdef CONFIG_MPC85XX_BITBANGMII
+
+#include <asm/iopin_85xx.h>
+
+#define DECLARE_IOPIN(iopin, pin) \
+	iopin_t iopin; \
+	iopin.port = pin / 32; \
+	iopin.pin = pin % 32; \
+	iopin.flag = 0;
+
+static inline void mpc85xx_udelay(unsigned long delay)
+{
+	udelay(delay);
+}
+
+static void mpc85xx_mdio_active(int pin)
+{
+	DECLARE_IOPIN(iopin, pin);
+
+	iopin_set_act(&iopin);
+	iopin_set_gen(&iopin);
+	iopin_set_out(&iopin);
+	iopin_set_high(&iopin);
+}
+
+static inline void mpc85xx_mdc_active(int pin)
+{
+	DECLARE_IOPIN(iopin, pin);
+
+	iopin_set_act(&iopin);
+	iopin_set_gen(&iopin);
+	iopin_set_out(&iopin);
+	iopin_set_high(&iopin);
+}
+
+static void mpc85xx_mdio_tristate(int pin)
+{
+	DECLARE_IOPIN(iopin, pin);
+
+	iopin_set_odr(&iopin);
+	iopin_set_gen(&iopin);
+	iopin_set_in(&iopin);
+	iopin_set_high(&iopin);
+}
+
+static void mpc85xx_mdio_set(int pin, int value)
+{
+	DECLARE_IOPIN(iopin, pin);
+
+	if (value)
+		iopin_set_high(&iopin);
+	else
+		iopin_set_low(&iopin);
+}
+
+static inline void mpc85xx_mdc_set(int pin, int value)
+{
+	mpc85xx_mdio_set(pin, value);
+}
+
+static int mpc85xx_mdio_get(int pin)
+{
+	DECLARE_IOPIN(iopin, pin);
+
+	if (iopin_is_high(&iopin))
+		return 1;
+	else
+		return 0;
+}
+
+/*****************************************************************************
+ *
+ * Utility to send the preamble, address, and register (common to read
+ * and write).
+ */
+static void mpc85xx_miiphy_pre (struct ether_fcc_info_s *info, char read, unsigned char addr, unsigned char reg)
+{
+	int j;			/* counter */
+
+	int mdio = info->mdiopin;
+	int mdc  = info->mdcpin;
+
+	/*
+	 * Send a 32 bit preamble ('1's) with an extra '1' bit for good measure.
+	 * The IEEE spec says this is a PHY optional requirement.  The AMD
+	 * 79C874 requires one after power up and one after a MII communications
+	 * error.  This means that we are doing more preambles than we need,
+	 * but it is safer and will be much more robust.
+	 */
+
+	mpc85xx_mdio_active(mdio);
+	mpc85xx_mdc_active(mdc);
+	mpc85xx_mdio_set(mdio, 1);
+
+	for (j = 0; j < 32; j++) {
+		mpc85xx_mdc_set(mdc, 0);
+		mpc85xx_udelay(1);
+		mpc85xx_mdc_set(mdc, 1);
+		mpc85xx_udelay(1);
+	}
+
+	/* send the start bit (01) and the read opcode (10) or write (10) */
+	mpc85xx_mdc_set(mdc, 0);
+	mpc85xx_mdio_set(mdio, 0);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 0);
+	mpc85xx_mdio_set(mdio, 1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 0);
+	mpc85xx_mdio_set(mdio, read);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 0);
+	mpc85xx_mdio_set(mdio, !read);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 1);
+	mpc85xx_udelay(1);
+
+	/* send the PHY address */
+	for (j = 0; j < 5; j++) {
+		mpc85xx_mdc_set(mdc, 0);
+		if ((addr & 0x10) == 0) {
+			mpc85xx_mdio_set(mdio, 0);
+		} else {
+			mpc85xx_mdio_set(mdio, 1);
+		}
+		mpc85xx_udelay(1);
+		mpc85xx_mdc_set(mdc, 1);
+		mpc85xx_udelay(1);
+		addr <<= 1;
+	}
+
+	/* send the register address */
+	for (j = 0; j < 5; j++) {
+		mpc85xx_mdc_set(mdc, 0);
+		if ((reg & 0x10) == 0) {
+			mpc85xx_mdio_set(mdio, 0);
+		} else {
+			mpc85xx_mdio_set(mdio, 1);
+		}
+		mpc85xx_udelay(1);
+		mpc85xx_mdc_set(mdc, 1);
+		mpc85xx_udelay(1);
+		reg <<= 1;
+	}
+}
+
+/*****************************************************************************
+ *
+ * Read a MII PHY register.
+ *
+ * Returns:
+ *   0 on success
+ */
+int mpc85xx_bb_miiphy_read (char *devname, unsigned char addr,
+		unsigned char reg, unsigned short *value)
+{
+	short rdreg;		/* register working value */
+	int j;			/* counter */
+
+	struct eth_device *dev;
+	struct ether_fcc_info_s *info;
+
+	int mdio;
+	int mdc;
+
+	if ((value == NULL) || (devname == NULL)) {
+		puts("NULL value pointer\n");
+		return (-1);
+	}
+
+	dev = eth_get_dev_by_name(devname);
+
+	if (dev == NULL) {
+		puts("Unknown net device: ");
+		puts(devname);
+		puts("\n");
+		return (-1);
+	}
+
+	info = (struct ether_fcc_info_s *)(dev->priv);
+
+	mdio = info->mdiopin;
+	mdc = info->mdcpin;
+
+	mpc85xx_miiphy_pre (info, 1, addr, reg);
+
+	/* tri-state our MDIO I/O pin so we can read */
+	mpc85xx_mdc_set(mdc, 0);
+	mpc85xx_mdio_tristate(mdio);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc, 1);
+	mpc85xx_udelay(1);
+
+	/* check the turnaround bit: the PHY should be driving it to zero */
+	if (mpc85xx_mdio_get(mdio) != 0) {
+		/* puts ("PHY didn't drive TA low\n"); */
+		for (j = 0; j < 32; j++) {
+			mpc85xx_mdc_set(mdc, 0);
+			mpc85xx_udelay(1);
+			mpc85xx_mdc_set(mdc, 1);
+			mpc85xx_udelay(1);
+		}
+		/* There is no PHY, set value to 0xFFFF and return */
+		*value = 0xFFFF;
+		return (-1);
+	}
+
+	mpc85xx_mdc_set(mdc, 0);
+	mpc85xx_udelay(1);
+
+	/* read 16 bits of register data, MSB first */
+	rdreg = 0;
+	for (j = 0; j < 16; j++) {
+		mpc85xx_mdc_set(mdc, 1);
+		mpc85xx_udelay(1);
+		rdreg <<= 1;
+		rdreg |= mpc85xx_mdio_get(mdio);
+		mpc85xx_mdc_set(mdc, 0);
+		mpc85xx_udelay(1);
+	}
+
+	mpc85xx_mdc_set(mdc,  1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc,  0);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc,  1);
+	mpc85xx_udelay(1);
+
+	*value = rdreg;
+
+#ifdef ET_DEBUG
+	printf ("miiphy_read(0x%x) @ 0x%x = 0x%04x\n", reg, addr, *value);
+#endif
+
+	return 0;
+}
+
+
+/*****************************************************************************
+ *
+ * Write a MII PHY register.
+ *
+ * Returns:
+ *   0 on success
+ */
+int mpc85xx_bb_miiphy_write (char *devname, unsigned char addr,
+		unsigned char reg, unsigned short value)
+{
+	int j;			/* counter */
+
+	struct eth_device *dev;
+	struct ether_fcc_info_s *info;
+
+	int mdio;
+	int mdc;
+
+	if (devname == NULL) {
+		puts("NULL value pointer\n");
+		return (-1);
+	}
+
+	dev = eth_get_dev_by_name(devname);
+
+	if (dev == NULL) {
+		puts("Unknown net device: ");
+		puts(devname);
+		puts("\n");
+		return (-1);
+	}
+
+	info = (struct ether_fcc_info_s *)(dev->priv);
+
+	mdio = info->mdiopin;
+	mdc = info->mdcpin;
+
+	mpc85xx_miiphy_pre (info, 0, addr, reg);
+
+	/* send the turnaround (10) */
+	mpc85xx_mdc_set(mdc,  0);
+	mpc85xx_mdio_set(mdio, 1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc,  1);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc,  0);
+	mpc85xx_mdio_set(mdio, 0);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc,  1);
+	mpc85xx_udelay(1);
+
+	/* write 16 bits of register data, MSB first */
+	for (j = 0; j < 16; j++) {
+		mpc85xx_mdc_set(mdc,  0);
+		if ((value & 0x00008000) == 0) {
+			mpc85xx_mdio_set(mdio, 0);
+		} else {
+			mpc85xx_mdio_set(mdio, 1);
+		}
+		mpc85xx_udelay(1);
+		mpc85xx_mdc_set(mdc,  1);
+		mpc85xx_udelay(1);
+		value <<= 1;
+	}
+
+	/*
+	 * Tri-state the MDIO line.
+	 */
+	mpc85xx_mdio_tristate(mdio);
+	mpc85xx_mdc_set(mdc,  0);
+	mpc85xx_udelay(1);
+	mpc85xx_mdc_set(mdc,  1);
+	mpc85xx_udelay(1);
+
+	return 0;
+}
+
+#endif
+
 static int fec_send(struct eth_device* dev, volatile void *packet, int length)
 {
     int i = 0;
@@ -461,6 +813,12 @@ int fec_initialize(bd_t *bis)
 		miiphy_register(dev->name,
 				bb_miiphy_read,	bb_miiphy_write);
 #endif
+#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII)) \
+		&& defined(CONFIG_MPC85XX_BITBANGMII)
+		miiphy_register(dev->name,
+				mpc85xx_bb_miiphy_read,	mpc85xx_bb_miiphy_write);
+		mpc85xx_init_phy(dev);
+#endif
 	}
 
 	return 1;
-- 
1.6.3.3



More information about the U-Boot mailing list