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(®base->miimadd, (mii_id << 8) | regnum); /* Write out the value we want */ gfar_write(®base->miimcon, value); /* Wait for the transaction to finish */ while (gfar_read(®base->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(®base->miimadd, (mii_id << 8) | regnum); /* Clear miimcom, and then initiate a read */ gfar_write(®base->miimcom, 0); gfar_write(®base->miimcom, MII_READ_COMMAND); /* Wait for the transaction to finish */ while (gfar_read(®base->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY)) cpu_relax(); /* Grab the value of the register from miimstat */ value = gfar_read(®base->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 + -
显示快捷键?