gianfar_phy.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 662 行 · 第 1/2 页

C
662
字号
/*  * drivers/net/gianfar_phy.c * * Gianfar Ethernet Driver -- PHY handling * Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560 * Based on 8260_io/fcc_enet.c * * Author: Andy Fleming * Maintainer: Kumar Gala (kumar.gala@freescale.com) * * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. * * 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. * */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <linux/mm.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <linux/module.h>#include <linux/version.h>#include <linux/crc32.h>#include <linux/mii.h>#include "gianfar.h"#include "gianfar_phy.h"static void config_genmii_advert(struct gfar_mii_info *mii_info);static void genmii_setup_forced(struct gfar_mii_info *mii_info);static void genmii_restart_aneg(struct gfar_mii_info *mii_info);static int gbit_config_aneg(struct gfar_mii_info *mii_info);static int genmii_config_aneg(struct gfar_mii_info *mii_info);static int genmii_update_link(struct gfar_mii_info *mii_info);static int genmii_read_status(struct gfar_mii_info *mii_info);u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);/* Write value to the PHY for this device to the register at regnum, *//* waiting until the write is done before it returns.  All PHY *//* configuration has to be done through the TSEC1 MIIM regs */void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value){	struct gfar_private *priv = netdev_priv(dev);	struct gfar *regbase = priv->phyregs;	/* Set the PHY address and the register address we want to write */	gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);	/* Write out the value we want */	gfar_write(&regbase->miimcon, value);	/* Wait for the transaction to finish */	while (gfar_read(&regbase->miimind) & MIIMIND_BUSY)		cpu_relax();}/* Reads from register regnum in the PHY for device dev, *//* returning the value.  Clears miimcom first.  All PHY *//* configuration has to be done through the TSEC1 MIIM regs */int read_phy_reg(struct net_device *dev, int mii_id, int regnum){	struct gfar_private *priv = netdev_priv(dev);	struct gfar *regbase = priv->phyregs;	u16 value;	/* Set the PHY address and the register address we want to read */	gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);	/* Clear miimcom, and then initiate a read */	gfar_write(&regbase->miimcom, 0);	gfar_write(&regbase->miimcom, MII_READ_COMMAND);	/* Wait for the transaction to finish */	while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))		cpu_relax();	/* Grab the value of the register from miimstat */	value = gfar_read(&regbase->miimstat);	return value;}void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info){	if(mii_info->phyinfo->ack_interrupt)		mii_info->phyinfo->ack_interrupt(mii_info);}void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts){	mii_info->interrupts = interrupts;	if(mii_info->phyinfo->config_intr)		mii_info->phyinfo->config_intr(mii_info);}/* Writes MII_ADVERTISE with the appropriate values, after * sanitizing advertise to make sure only supported features * are advertised  */static void config_genmii_advert(struct gfar_mii_info *mii_info){	u32 advertise;	u16 adv;	/* Only allow advertising what this PHY supports */	mii_info->advertising &= mii_info->phyinfo->features;	advertise = mii_info->advertising;	/* Setup standard advertisement */	adv = phy_read(mii_info, MII_ADVERTISE);	adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);	if (advertise & ADVERTISED_10baseT_Half)		adv |= ADVERTISE_10HALF;	if (advertise & ADVERTISED_10baseT_Full)		adv |= ADVERTISE_10FULL;	if (advertise & ADVERTISED_100baseT_Half)		adv |= ADVERTISE_100HALF;	if (advertise & ADVERTISED_100baseT_Full)		adv |= ADVERTISE_100FULL;	phy_write(mii_info, MII_ADVERTISE, adv);}static void genmii_setup_forced(struct gfar_mii_info *mii_info){	u16 ctrl;	u32 features = mii_info->phyinfo->features;		ctrl = phy_read(mii_info, MII_BMCR);	ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);	ctrl |= BMCR_RESET;	switch(mii_info->speed) {		case SPEED_1000:			if(features & (SUPPORTED_1000baseT_Half						| SUPPORTED_1000baseT_Full)) {				ctrl |= BMCR_SPEED1000;				break;			}			mii_info->speed = SPEED_100;		case SPEED_100:			if (features & (SUPPORTED_100baseT_Half						| SUPPORTED_100baseT_Full)) {				ctrl |= BMCR_SPEED100;				break;			}			mii_info->speed = SPEED_10;		case SPEED_10:			if (features & (SUPPORTED_10baseT_Half						| SUPPORTED_10baseT_Full))				break;		default: /* Unsupported speed! */			printk(KERN_ERR "%s: Bad speed!\n", 					mii_info->dev->name);			break;	}	phy_write(mii_info, MII_BMCR, ctrl);}/* Enable and Restart Autonegotiation */static void genmii_restart_aneg(struct gfar_mii_info *mii_info){	u16 ctl;	ctl = phy_read(mii_info, MII_BMCR);	ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);	phy_write(mii_info, MII_BMCR, ctl);}static int gbit_config_aneg(struct gfar_mii_info *mii_info){	u16 adv;	u32 advertise;	if(mii_info->autoneg) {		/* Configure the ADVERTISE register */		config_genmii_advert(mii_info);		advertise = mii_info->advertising;		adv = phy_read(mii_info, MII_1000BASETCONTROL);		adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |				MII_1000BASETCONTROL_HALFDUPLEXCAP);		if (advertise & SUPPORTED_1000baseT_Half)			adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;		if (advertise & SUPPORTED_1000baseT_Full)			adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;		phy_write(mii_info, MII_1000BASETCONTROL, adv);		/* Start/Restart aneg */		genmii_restart_aneg(mii_info);	} else		genmii_setup_forced(mii_info);	return 0;}static int marvell_config_aneg(struct gfar_mii_info *mii_info){	/* The Marvell PHY has an errata which requires	 * that certain registers get written in order	 * to restart autonegotiation */	phy_write(mii_info, MII_BMCR, BMCR_RESET);	phy_write(mii_info, 0x1d, 0x1f);	phy_write(mii_info, 0x1e, 0x200c);	phy_write(mii_info, 0x1d, 0x5);	phy_write(mii_info, 0x1e, 0);	phy_write(mii_info, 0x1e, 0x100);	gbit_config_aneg(mii_info);	return 0;}static int genmii_config_aneg(struct gfar_mii_info *mii_info){	if (mii_info->autoneg) {		config_genmii_advert(mii_info);		genmii_restart_aneg(mii_info);	} else		genmii_setup_forced(mii_info);	return 0;}static int genmii_update_link(struct gfar_mii_info *mii_info){	u16 status;	/* Do a fake read */	phy_read(mii_info, MII_BMSR);	/* Read link and autonegotiation status */	status = phy_read(mii_info, MII_BMSR);	if ((status & BMSR_LSTATUS) == 0)		mii_info->link = 0;	else		mii_info->link = 1;	/* If we are autonegotiating, and not done, 	 * return an error */	if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))		return -EAGAIN;	return 0;}static int genmii_read_status(struct gfar_mii_info *mii_info){	u16 status;	int err;	/* Update the link, but return if there	 * was an error */	err = genmii_update_link(mii_info);	if (err)		return err;	if (mii_info->autoneg) {		status = phy_read(mii_info, MII_LPA);		if (status & (LPA_10FULL | LPA_100FULL))			mii_info->duplex = DUPLEX_FULL;		else			mii_info->duplex = DUPLEX_HALF;		if (status & (LPA_100FULL | LPA_100HALF))			mii_info->speed = SPEED_100;		else			mii_info->speed = SPEED_10;		mii_info->pause = 0;	}	/* On non-aneg, we assume what we put in BMCR is the speed,	 * though magic-aneg shouldn't prevent this case from occurring	 */	return 0;}static int marvell_read_status(struct gfar_mii_info *mii_info){	u16 status;	int err;	/* Update the link, but return if there	 * was an error */	err = genmii_update_link(mii_info);	if (err)		return err;	/* If the link is up, read the speed and duplex */	/* If we aren't autonegotiating, assume speeds 	 * are as set */	if (mii_info->autoneg && mii_info->link) {		int speed;		status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);#if 0		/* If speed and duplex aren't resolved,		 * return an error.  Isn't this handled		 * by checking aneg?		 */		if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)			return -EAGAIN;#endif		/* Get the duplexity */		if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)			mii_info->duplex = DUPLEX_FULL;		else

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?