[U-Boot] [PATCH 02/14] powerpc/fman: add PHY support for dTSEC
Kumar Gala
galak at kernel.crashing.org
Thu Jan 27 07:15:47 CET 2011
On Jan 26, 2011, at 10:52 PM, Mingkai Hu wrote:
> From: Kumar Gala <galak at kernel.crashing.org>
>
> Add VSC8244 and VSC8234 phy support, this will be reused
> by etsec code.
>
> Signed-off-by: Kumar Gala <galak at kernel.crashing.org>
> Signed-off-by: Mingkai Hu <Mingkai.hu at 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 <b18070 at freescale.com>
> + * Mingkai Hu <Mingkai.hu at 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 <b18070 at freescale.com>
> + * Mingkai Hu <Mingkai.hu at 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
>
More information about the U-Boot
mailing list