From: Kumar Gala <ga...@kernel.crashing.org>

Add VSC8244 and VSC8234 phy support, this will be reused
by etsec code.

Signed-off-by: Kumar Gala <ga...@kernel.crashing.org>
Signed-off-by: Mingkai Hu <mingkai...@freescale.com>
---
 arch/powerpc/include/asm/fsl_enet.h |   10 +
 drivers/net/fsl_phy.c               |  353 +++++++++++++++++++++++++++++++++++
 drivers/net/fsl_phy.h               |  176 +++++++++++++++++
 3 files changed, 539 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/fsl_phy.c
 create mode 100644 drivers/net/fsl_phy.h

diff --git a/arch/powerpc/include/asm/fsl_enet.h 
b/arch/powerpc/include/asm/fsl_enet.h
index 4fb2857..9475249 100644
--- a/arch/powerpc/include/asm/fsl_enet.h
+++ b/arch/powerpc/include/asm/fsl_enet.h
@@ -28,6 +28,16 @@ enum fsl_phy_enet_if {
        FSL_ETH_IF_NONE,
 };
 
+typedef struct tsec_mii_mng {
+       u32 miimcfg;            /* MII management configuration reg */
+       u32 miimcom;            /* MII management command reg */
+       u32 miimadd;            /* MII management address reg */
+       u32 miimcon;            /* MII management control reg */
+       u32 miimstat;           /* MII management status reg  */
+       u32 miimind;            /* MII management indication reg */
+       u32 ifstat;             /* Interface Status Register */
+} __attribute__ ((packed))tsec_mii_t;
+
 int fdt_fixup_phy_connection(void *blob, int offset, enum fsl_phy_enet_if 
phyc);
 
 #endif /* __ASM_PPC_FSL_ENET_H */
diff --git a/drivers/net/fsl_phy.c b/drivers/net/fsl_phy.c
new file mode 100644
index 0000000..1ba0ce1
--- /dev/null
+++ b/drivers/net/fsl_phy.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *     Jun-jie Zhang <b18...@freescale.com>
+ *     Mingkai Hu <mingkai...@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#include <common.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+
+#include "fsl_phy.h"
+
+void tsec_local_mdio_write(tsec_mii_t *phyregs, int port_addr,
+               int dev_addr, int regnum, int value)
+{
+       int timeout = 1000000;
+
+       out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f));
+       out_be32(&phyregs->miimcon, value);
+       asm("sync");
+
+       while ((in_be32(&phyregs->miimind) & MIIMIND_BUSY) && timeout--);
+}
+
+int tsec_local_mdio_read(tsec_mii_t *phyregs, int port_addr,
+               int dev_addr, int regnum)
+{
+       int value;
+       int timeout = 1000000;
+
+       /* Put the address of the phy, and the register
+        * number into MIIMADD */
+       out_be32(&phyregs->miimadd, (port_addr << 8) | (regnum & 0x1f));
+
+       /* Clear the command register, and wait */
+       out_be32(&phyregs->miimcom, 0);
+       asm("sync");
+
+       /* Initiate a read command, and wait */
+       out_be32(&phyregs->miimcom, MIIMCOM_READ_CYCLE);
+       asm("sync");
+
+       /* Wait for the the indication that the read is done */
+       while ((in_be32(&phyregs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
+                       && timeout--);
+
+       /* Grab the value read from the PHY */
+       value = in_be32(&phyregs->miimstat);
+
+       return value;
+}
+
+int tsec_phy_read(struct mii_info *mii_info, int dev_addr, int regnum)
+{
+       tsec_mii_t *phyregs = mii_info->phyregs;
+
+       return tsec_local_mdio_read(phyregs, mii_info->mii_id, dev_addr,
+                       regnum);
+}
+
+int tsec_phy_write(struct mii_info *mii_info, int dev_addr, int regnum,
+                       int value)
+{
+       tsec_mii_t *phyregs = mii_info->phyregs;
+
+       tsec_local_mdio_write(phyregs, mii_info->mii_id, dev_addr, regnum,
+               value);
+
+       return 0;
+}
+
+static int genphy_config(struct mii_info *mii_info)
+{
+       return tsec_phy_write(mii_info, 0, MII_BMCR, MII_BMCR_INIT);
+}
+
+/**
+ * genphy_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Update the value in phydev->link to reflect the
+ *   current link value.  In order to do this, we need to read
+ *   the status register twice, keeping the second value.
+ */
+static int genphy_update_link(struct mii_info *phydev)
+{
+       unsigned int mii_reg;
+
+       /*
+        * Wait if the link is up, and autonegotiation is in progress
+        * (ie - we're capable and it's not done)
+        */
+       mii_reg = tsec_phy_read(phydev, 0, MII_BMSR);
+
+       /*
+        * If we already saw the link up, and it hasn't gone down, then
+        * we don't need to wait for autoneg again
+        */
+       if (phydev->link && mii_reg & BMSR_LSTATUS)
+               return 0;
+
+       if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
+               int i = 0;
+
+               printf("%s Waiting for PHY auto negotiation to complete",
+                       phydev->dev->name);
+               while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
+                       /*
+                        * Timeout reached ?
+                        */
+                       if (i > 5000) {
+                               printf(" TIMEOUT !\n");
+                               phydev->link = 0;
+                               return 0;
+                       }
+
+                       if (ctrlc()) {
+                               puts("user interrupt!\n");
+                               phydev->link = 0;
+                               return -EINTR;
+                       }
+
+                       if ((i++ % 500) == 0)
+                               printf(".");
+
+                       udelay(1000);   /* 1 ms */
+                       mii_reg = tsec_phy_read(phydev, 0, MII_BMSR);
+               }
+               printf(" done\n");
+               phydev->link = 1;
+               udelay(500000); /* another 500 ms (results in faster booting) */
+       } else {
+               /* Read the link a second time to clear the latched state */
+               mii_reg = tsec_phy_read(phydev, 0, MII_BMSR);
+
+               if (mii_reg & BMSR_LSTATUS)
+                       phydev->link = 1;
+               else
+                       phydev->link = 0;
+       }
+
+       return 0;
+}
+
+/*
+ * Generic function which updates the speed and duplex.  If
+ * autonegotiation is enabled, it uses the AND of the link
+ * partner's advertised capabilities and our advertised
+ * capabilities.  If autonegotiation is disabled, we use the
+ * appropriate bits in the control register.
+ *
+ * Stolen from Linux's mii.c and phy_device.c
+ */
+static int genphy_parse_link(struct mii_info *phydev)
+{
+       int mii_reg = tsec_phy_read(phydev, 0, MII_BMSR);
+
+       /* We're using autonegotiation */
+       if (mii_reg & BMSR_ANEGCAPABLE) {
+               u32 lpa = 0;
+               u32 gblpa = 0;
+
+               /* Check for gigabit capability */
+               if (mii_reg & BMSR_ERCAP) {
+                       /* We want a list of states supported by
+                        * both PHYs in the link
+                        */
+                       gblpa = tsec_phy_read(phydev, 0, MII_STAT1000);
+                       gblpa &= tsec_phy_read(phydev, 0, MII_CTRL1000) << 2;
+               }
+
+               /* Set the baseline so we only have to set them
+                * if they're different
+                */
+               phydev->speed = SPEED_10;
+               phydev->duplex = DUPLEX_HALF;
+
+               /* Check the gigabit fields */
+               if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {
+                       phydev->speed = SPEED_1000;
+
+                       if (gblpa & PHY_1000BTSR_1000FD)
+                               phydev->duplex = DUPLEX_FULL;
+
+                       /* We're done! */
+                       return 0;
+               }
+
+               lpa = tsec_phy_read(phydev, 0, MII_ADVERTISE);
+               lpa &= tsec_phy_read(phydev, 0, MII_LPA);
+
+               if (lpa & (LPA_100FULL | LPA_100HALF)) {
+                       phydev->speed = SPEED_100;
+
+                       if (lpa & LPA_100FULL)
+                               phydev->duplex = DUPLEX_FULL;
+
+               } else if (lpa & LPA_10FULL)
+                       phydev->duplex = DUPLEX_FULL;
+       } else {
+               u32 bmcr = tsec_phy_read(phydev, 0, MII_BMCR);
+
+               phydev->speed = SPEED_10;
+               phydev->duplex = DUPLEX_HALF;
+
+               if (bmcr & BMCR_FULLDPLX)
+                       phydev->duplex = DUPLEX_FULL;
+
+               if (bmcr & BMCR_SPEED1000)
+                       phydev->speed = SPEED_1000;
+               else if (bmcr & BMCR_SPEED100)
+                       phydev->speed = SPEED_100;
+       }
+
+       return 0;
+}
+
+static int genphy_startup(struct mii_info *phydev)
+{
+       genphy_update_link(phydev);
+       genphy_parse_link(phydev);
+
+       return 0;
+}
+
+static int genphy_shutdown(struct mii_info *phydev)
+{
+       return 0;
+}
+
+/* Vitesse VSC8244 */
+static int vsc8244_parse_status(struct mii_info *mii_info)
+{
+       unsigned int speed;
+       unsigned int mii_reg;
+
+       mii_reg = tsec_phy_read(mii_info, 0, MIIM_VSC8244_AUX_CONSTAT);
+
+       if (mii_reg & MIIM_VSC8244_AUXCONSTAT_DUPLEX)
+               mii_info->duplex = DUPLEX_FULL;
+       else
+               mii_info->duplex = DUPLEX_HALF;
+
+       speed = mii_reg & MIIM_VSC8244_AUXCONSTAT_SPEED;
+       switch (speed) {
+       case MIIM_VSC8244_AUXCONSTAT_GBIT:
+               mii_info->speed = SPEED_1000;
+               break;
+       case MIIM_VSC8244_AUXCONSTAT_100:
+               mii_info->speed = SPEED_100;
+               break;
+       default:
+               mii_info->speed = SPEED_10;
+               break;
+       }
+
+       return 0;
+}
+
+static int vsc8244_startup(struct mii_info *mii_info)
+{
+       /* Read the Status (2x to make sure link is right) */
+       genphy_update_link(mii_info);
+       vsc8244_parse_status(mii_info);
+
+       return 0;
+}
+
+static struct phy_info phy_info_VSC8244 = {
+       "Vitesse VSC8244",
+       0xfc6c0,
+       0xffff0,
+       &genphy_config,
+       &vsc8244_startup,
+       &genphy_shutdown,
+};
+
+static struct phy_info phy_info_VSC8234 = {
+       "Vitesse VSC8234",
+       0xfc620,
+       0xffff0,
+       &genphy_config,
+       &vsc8244_startup,
+       &genphy_shutdown,
+};
+
+static struct phy_info phy_info_generic = {
+       "Unknown/Generic PHY",
+       0x0,
+       0x0,
+       &genphy_config,
+       &genphy_startup,
+       &genphy_shutdown,
+};
+
+static struct phy_info *phy_info[] = {
+       &phy_info_VSC8244,
+       &phy_info_VSC8234,
+       &phy_info_generic
+};
+
+/*
+ * Use the PHY ID registers to determine what type of PHY is attached
+ * to device dev.  return a struct phy_info structure describing that PHY
+ */
+struct phy_info *tsec_get_phy_info(struct mii_info *mii_info)
+{
+       u16 phy_reg;
+       u32 phy_ID;
+       int i;
+       struct phy_info *theInfo = NULL;
+
+       /* Grab the bits from PHYIR1, and put them in the upper half */
+       phy_reg = tsec_phy_read(mii_info, 0, MII_PHYSID1);
+       phy_ID = (phy_reg & 0xffff) << 16;
+
+       /* Grab the bits from PHYIR2, and put them in the lower half */
+       phy_reg = tsec_phy_read(mii_info, 0, MII_PHYSID2);
+       phy_ID |= (phy_reg & 0xffff);
+
+       /* loop through all the known PHY types, and find one that */
+       /* matches the ID we read from the PHY. */
+       for (i = 0; i < ARRAY_SIZE(phy_info); i++)
+               if ((phy_info[i]->uid & phy_info[i]->mask) ==
+                               (phy_ID & phy_info[i]->mask)) {
+                       theInfo = phy_info[i];
+                       break;
+               }
+
+       if (theInfo == &phy_info_generic) {
+               printf("%s: No support for PHY id %x; assuming generic\n",
+                               mii_info->dev->name, phy_ID);
+       } else {
+               printf("%s: PHY is %s (%x)\n", mii_info->dev->name,
+                               theInfo->name, phy_ID);
+       }
+
+       return theInfo;
+}
diff --git a/drivers/net/fsl_phy.h b/drivers/net/fsl_phy.h
new file mode 100644
index 0000000..8a8b6e8
--- /dev/null
+++ b/drivers/net/fsl_phy.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2009-2010 Freescale Semiconductor, Inc.
+ *     Jun-jie Zhang <b18...@freescale.com>
+ *     Mingkai Hu <mingkai...@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#ifndef __FSL_PHY_H__
+#define __FSL_PHY_H__
+
+#include <net.h>
+#include <miiphy.h>
+#include <asm/fsl_enet.h>
+
+/* Duplex, half or full. */
+#define DUPLEX_HALF            0x00
+#define DUPLEX_FULL            0x01
+
+#define PORT_TP                0x00
+#define PORT_AUI       0x01
+#define PORT_MII       0x02
+#define PORT_FIBRE     0x03
+
+#define MII_BMCR_INIT           0x00001140
+
+/* MII Management Configuration Register */
+#define MIIMCFG_RESET_MGMT          0x80000000
+#define MIIMCFG_MGMT_CLOCK_SELECT   0x00000007
+#define MIIMCFG_INIT_VALUE         0x00000003
+
+/* MII Management Command Register */
+#define MIIMCOM_READ_CYCLE     0x00000001
+#define MIIMCOM_SCAN_CYCLE     0x00000002
+
+/* MII Management Address Register */
+#define MIIMADD_PHY_ADDR_SHIFT 8
+
+/* MII Management Indicator Register */
+#define MIIMIND_BUSY           0x00000001
+#define MIIMIND_NOTVALID       0x00000004
+
+#define AUTONEG_DISABLE                0x00
+#define AUTONEG_ENABLE         0x01
+#define SPEED_10               10
+#define SPEED_100              100
+#define SPEED_1000             1000
+#define SPEED_2500             2500
+#define SPEED_10000            10000
+#define SUPPORTED_10baseT_Half         (1 << 0)
+#define SUPPORTED_10baseT_Full         (1 << 1)
+#define SUPPORTED_100baseT_Half                (1 << 2)
+#define SUPPORTED_100baseT_Full                (1 << 3)
+#define SUPPORTED_1000baseT_Half       (1 << 4)
+#define SUPPORTED_1000baseT_Full       (1 << 5)
+#define SUPPORTED_Autoneg              (1 << 6)
+#define SUPPORTED_TP                   (1 << 7)
+#define SUPPORTED_AUI                  (1 << 8)
+#define SUPPORTED_MII                  (1 << 9)
+#define SUPPORTED_FIBRE                        (1 << 10)
+#define SUPPORTED_BNC                  (1 << 11)
+#define SUPPORTED_10000baseT_Full      (1 << 12)
+#define SUPPORTED_Pause                        (1 << 13)
+#define SUPPORTED_Asym_Pause           (1 << 14)
+#define SUPPORTED_2500baseX_Full       (1 << 15)
+
+/* Indicates what features are advertised by the interface. */
+#define ADVERTISED_10baseT_Half                (1 << 0)
+#define ADVERTISED_10baseT_Full                (1 << 1)
+#define ADVERTISED_100baseT_Half       (1 << 2)
+#define ADVERTISED_100baseT_Full       (1 << 3)
+#define ADVERTISED_1000baseT_Half      (1 << 4)
+#define ADVERTISED_1000baseT_Full      (1 << 5)
+#define ADVERTISED_Autoneg             (1 << 6)
+#define ADVERTISED_TP                  (1 << 7)
+#define ADVERTISED_AUI                 (1 << 8)
+#define ADVERTISED_MII                 (1 << 9)
+#define ADVERTISED_FIBRE               (1 << 10)
+#define ADVERTISED_BNC                 (1 << 11)
+#define ADVERTISED_10000baseT_Full     (1 << 12)
+#define ADVERTISED_Pause               (1 << 13)
+#define ADVERTISED_Asym_Pause          (1 << 14)
+#define ADVERTISED_2500baseX_Full      (1 << 15)
+
+#define PHY_AUTONEGOTIATE_TIMEOUT      5000 /* in ms */
+
+/* Entry for Vitesse VSC8244 regs starts here */
+/* Vitesse VSC8244 Auxiliary Control/Status Register */
+#define MIIM_VSC8244_AUX_CONSTAT       0x1c
+#define MIIM_VSC8244_AUXCONSTAT_INIT   0x0000
+#define MIIM_VSC8244_AUXCONSTAT_DUPLEX 0x0020
+#define MIIM_VSC8244_AUXCONSTAT_SPEED  0x0018
+#define MIIM_VSC8244_AUXCONSTAT_GBIT   0x0010
+#define MIIM_VSC8244_AUXCONSTAT_100    0x0008
+#define MIIM_CONTROL_INIT_LOOPBACK     0x4000
+
+struct mii_info {
+       /* Information about the PHY type */
+       /* And management functions */
+       struct phy_info *phyinfo;
+       void *phyregs;
+
+       struct eth_device *dev;
+
+       /* forced speed & duplex (no autoneg)
+        * partner speed & duplex & pause (autoneg)
+        */
+       int speed;
+       int duplex;
+
+       /* The most recently read link state */
+       int link;
+       int port;
+
+       u32 advertising;
+       int autoneg;
+       int mii_id;
+       u32 flags;
+
+       /* private data pointer */
+       /* For use by PHYs to maintain extra state */
+       void *priv;
+
+       /* Provided by ethernet driver */
+       int (*mdio_read)(struct eth_device *dev, int mii_id, int reg);
+       void (*mdio_write)(struct eth_device *dev, int mii_id, int reg,
+                           int val);
+};
+
+/* struct phy_info: a structure which defines attributes for a PHY
+ *
+ * id will contain a number which represents the PHY.  During
+ * startup, the driver will poll the PHY to find out what its
+ * UID--as defined by registers 2 and 3--is.  The 32-bit result
+ * gotten from the PHY will be masked to
+ * discard any bits which may change based on revision numbers
+ * unimportant to functionality
+ *
+ */
+struct phy_info {
+       char *name;
+       unsigned int uid;
+       unsigned int mask;
+       /* Called to configure the PHY, and modify the controller
+        * based on the results */
+       int (*config)(struct mii_info *mii_info);
+
+       /* Called when starting up the controller */
+       int (*startup)(struct mii_info *mii_info);
+
+       /* Called when bringing down the controller */
+       int (*shutdown)(struct mii_info *mii_info);
+};
+
+struct phy_info *tsec_get_phy_info(struct mii_info *mii_info);
+void tsec_local_mdio_write(tsec_mii_t *phyregs, int port_addr, int dev_addr,
+               int reg, int value);
+int tsec_local_mdio_read(tsec_mii_t *phyregs, int port_addr, int dev_addr,
+               int regnum);
+int tsec_phy_read(struct mii_info *mii_info, int dev_addr, int regnum);
+int tsec_phy_write(struct mii_info *mii_info, int dev_addr, int regnum,
+               int value);
+#endif /* __FSL_PHY_H__ */
+
-- 
1.6.4


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to