[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