On Jan 26, 2011, at 10:52 PM, Mingkai Hu wrote: > 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 >
General question should we use DTSEC name to match p4080 manual for this ? > 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 { Wondering if we should use dtsec_ here since that matches P4080 Manual and possible removes some confusion with tsec enet controller. > + 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> We need to utilize <linux/mii.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