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