Newer
Older
/*
* drivers/net/phy/phy.c
*
* Framework for configuring and reading PHY devices
* Based on code in sungem_phy.c and gianfar_phy.c
*
* Author: Andy Fleming
*
* Copyright (c) 2004 Freescale Semiconductor, Inc.
* Copyright (c) 2006, 2007 Maciej W. Rozycki
*
* 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/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.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/mm.h>
#include <linux/module.h>
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/phy.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <linux/mdio.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
/**
* phy_print_status - Convenience function to print out the current phy status
* @phydev: the phy_device struct
*/
void phy_print_status(struct phy_device *phydev)
{
if (phydev->link)
pr_info("%s - Link is Up - %d/%s\n",
dev_name(&phydev->dev),
phydev->speed,
DUPLEX_FULL == phydev->duplex ? "Full" : "Half");
else
pr_info("%s - Link is Down\n", dev_name(&phydev->dev));
/**
* phy_clear_interrupt - Ack the phy device's interrupt
* @phydev: the phy_device struct
*
* If the @phydev driver has an ack_interrupt function, call it to
* ack and clear the phy device's interrupt.
*
* Returns 0 on success on < 0 on error.
*/
static int phy_clear_interrupt(struct phy_device *phydev)
{
int err = 0;
if (phydev->drv->ack_interrupt)
err = phydev->drv->ack_interrupt(phydev);
return err;
}
/**
* phy_config_interrupt - configure the PHY device for the requested interrupts
* @phydev: the phy_device struct
* @interrupts: interrupt flags to configure for this @phydev
*
* Returns 0 on success on < 0 on error.
*/
static int phy_config_interrupt(struct phy_device *phydev, u32 interrupts)
{
int err = 0;
phydev->interrupts = interrupts;
if (phydev->drv->config_intr)
err = phydev->drv->config_intr(phydev);
return err;
}
/**
* phy_aneg_done - return auto-negotiation status
* @phydev: target phy_device struct
* Description: Reads the status register and returns 0 either if
* auto-negotiation is incomplete, or if there was an error.
* Returns BMSR_ANEGCOMPLETE if auto-negotiation is done.
*/
static inline int phy_aneg_done(struct phy_device *phydev)
{
int retval;
retval = phy_read(phydev, MII_BMSR);
return (retval < 0) ? retval : (retval & BMSR_ANEGCOMPLETE);
}
/* A structure for mapping a particular speed and duplex
* combination to a particular SUPPORTED and ADVERTISED value */
struct phy_setting {
int speed;
int duplex;
u32 setting;
};
/* A mapping of all SUPPORTED settings to speed/duplex */
static const struct phy_setting settings[] = {
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
{
.speed = 10000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_10000baseT_Full,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_1000baseT_Full,
},
{
.speed = SPEED_1000,
.duplex = DUPLEX_HALF,
.setting = SUPPORTED_1000baseT_Half,
},
{
.speed = SPEED_100,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_100baseT_Full,
},
{
.speed = SPEED_100,
.duplex = DUPLEX_HALF,
.setting = SUPPORTED_100baseT_Half,
},
{
.speed = SPEED_10,
.duplex = DUPLEX_FULL,
.setting = SUPPORTED_10baseT_Full,
},
{
.speed = SPEED_10,
.duplex = DUPLEX_HALF,
.setting = SUPPORTED_10baseT_Half,
},
};
#define MAX_NUM_SETTINGS ARRAY_SIZE(settings)
/**
* phy_find_setting - find a PHY settings array entry that matches speed & duplex
* @speed: speed to match
* @duplex: duplex to match
* Description: Searches the settings array for the setting which
* matches the desired speed and duplex, and returns the index
* of that setting. Returns the index of the last setting if
* none of the others match.
*/
static inline int phy_find_setting(int speed, int duplex)
{
int idx = 0;
while (idx < ARRAY_SIZE(settings) &&
(settings[idx].speed != speed ||
settings[idx].duplex != duplex))
idx++;
return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
}
/**
* phy_find_valid - find a PHY setting that matches the requested features mask
* @idx: The first index in settings[] to search
* @features: A mask of the valid settings
* Description: Returns the index of the first valid setting less
* than or equal to the one pointed to by idx, as determined by
* the mask in features. Returns the index of the last setting
* if nothing else matches.
*/
static inline int phy_find_valid(int idx, u32 features)
{
while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features))
idx++;
Loading
Loading full blame...