diff mbox

[U-Boot,02/14] powerpc/fman: add PHY support for dTSEC

Message ID 1296103972-2696-3-git-send-email-Mingkai.hu@freescale.com
State Superseded, archived
Delegated to: Andy Fleming
Headers show

Commit Message

Mingkai Hu Jan. 27, 2011, 4:52 a.m. UTC
From: Kumar Gala <galak@kernel.crashing.org>

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

Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Signed-off-by: Mingkai Hu <Mingkai.hu@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

Comments

Kumar Gala Jan. 27, 2011, 6:15 a.m. UTC | #1
On Jan 26, 2011, at 10:52 PM, Mingkai Hu wrote:

> From: Kumar Gala <galak@kernel.crashing.org>
> 
> Add VSC8244 and VSC8234 phy support, this will be reused
> by etsec code.
> 
> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
> Signed-off-by: Mingkai Hu <Mingkai.hu@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@freescale.com>
> + *	Mingkai Hu <Mingkai.hu@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@freescale.com>
> + *	Mingkai Hu <Mingkai.hu@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
>
Timur Tabi Jan. 27, 2011, 4:10 p.m. UTC | #2
Mingkai Hu wrote:
> +	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);

The "& 0xffff" is unnecessary since phy_reg is a u16, so it can never be larger
than 0xffff.

> +			theInfo = phy_info[i];

CamelCase variables?

> + * Copyright 2009-2010 Freescale Semiconductor, Inc.

Should any of these be 2011?
diff mbox

Patch

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 <b18070@freescale.com>
+ *	Mingkai Hu <Mingkai.hu@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@freescale.com>
+ *	Mingkai Hu <Mingkai.hu@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__ */
+