Commit 655887fe authored by David S. Miller's avatar David S. Miller

Merge branch 'complex-c45-phys'

Heiner Kallweit says:

====================
net: phy: improve handling of more complex C45 PHY's

This series tries to address few problematic aspects raised by
Russell. Concrete example is the Marvell 88x3310, the changes
should be helpful for other complex C45 PHY's too.

v2:
- added patch enabling interrupts also if phylib state machine
  isn't started
- removed patch dealing with the double link status read
  This one needs little bit more thinking and will go separately.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 8e2ea3ea 97b33bdf
......@@ -29,6 +29,8 @@
#include <linux/uaccess.h>
#include <linux/atomic.h>
#define PHY_STATE_TIME HZ
#define PHY_STATE_STR(_state) \
case PHY_##_state: \
return __stringify(_state); \
......@@ -478,12 +480,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
}
EXPORT_SYMBOL(phy_mii_ioctl);
static void phy_queue_state_machine(struct phy_device *phydev,
unsigned int secs)
void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies)
{
mod_delayed_work(system_power_efficient_wq, &phydev->state_queue,
secs * HZ);
jiffies);
}
EXPORT_SYMBOL(phy_queue_state_machine);
static void phy_trigger_machine(struct phy_device *phydev)
{
......@@ -772,8 +774,13 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev))
return IRQ_NONE;
/* reschedule state queue work to run as soon as possible */
phy_trigger_machine(phydev);
if (phydev->drv->handle_interrupt) {
if (phydev->drv->handle_interrupt(phydev))
goto phy_err;
} else {
/* reschedule state queue work to run as soon as possible */
phy_trigger_machine(phydev);
}
if (phy_clear_interrupt(phydev))
goto phy_err;
......@@ -799,10 +806,10 @@ static int phy_enable_interrupts(struct phy_device *phydev)
}
/**
* phy_request_interrupt - request interrupt for a PHY device
* phy_request_interrupt - request and enable interrupt for a PHY device
* @phydev: target phy_device struct
*
* Description: Request the interrupt for the given PHY.
* Description: Request and enable the interrupt for the given PHY.
* If this fails, then we set irq to PHY_POLL.
* This should only be called with a valid IRQ number.
*/
......@@ -817,10 +824,30 @@ void phy_request_interrupt(struct phy_device *phydev)
phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n",
err, phydev->irq);
phydev->irq = PHY_POLL;
} else {
if (phy_enable_interrupts(phydev)) {
phydev_warn(phydev, "Can't enable interrupt, falling back to polling\n");
phy_free_interrupt(phydev);
phydev->irq = PHY_POLL;
}
}
}
EXPORT_SYMBOL(phy_request_interrupt);
/**
* phy_free_interrupt - disable and free interrupt for a PHY device
* @phydev: target phy_device struct
*
* Description: Disable and free the interrupt for the given PHY.
* This should only be called with a valid IRQ number.
*/
void phy_free_interrupt(struct phy_device *phydev)
{
phy_disable_interrupts(phydev);
free_irq(phydev->irq, phydev);
}
EXPORT_SYMBOL(phy_free_interrupt);
/**
* phy_stop - Bring down the PHY link, and stop checking the status
* @phydev: target phy_device struct
......@@ -835,9 +862,6 @@ void phy_stop(struct phy_device *phydev)
mutex_lock(&phydev->lock);
if (phy_interrupt_is_valid(phydev))
phy_disable_interrupts(phydev);
phydev->state = PHY_HALTED;
mutex_unlock(&phydev->lock);
......@@ -864,8 +888,6 @@ EXPORT_SYMBOL(phy_stop);
*/
void phy_start(struct phy_device *phydev)
{
int err;
mutex_lock(&phydev->lock);
if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) {
......@@ -877,13 +899,6 @@ void phy_start(struct phy_device *phydev)
/* if phy was suspended, bring the physical link up again */
__phy_resume(phydev);
/* make sure interrupts are enabled for the PHY */
if (phy_interrupt_is_valid(phydev)) {
err = phy_enable_interrupts(phydev);
if (err < 0)
goto out;
}
phydev->state = PHY_UP;
phy_start_machine(phydev);
......
......@@ -1016,7 +1016,7 @@ void phy_disconnect(struct phy_device *phydev)
phy_stop(phydev);
if (phy_interrupt_is_valid(phydev))
free_irq(phydev->irq, phydev);
phy_free_interrupt(phydev);
phydev->adjust_link = NULL;
......
......@@ -188,7 +188,6 @@ static inline const char *phy_modes(phy_interface_t interface)
#define PHY_INIT_TIMEOUT 100000
#define PHY_STATE_TIME 1
#define PHY_FORCE_TIMEOUT 10
#define PHY_MAX_ADDR 32
......@@ -537,6 +536,9 @@ struct phy_driver {
*/
int (*did_interrupt)(struct phy_device *phydev);
/* Override default interrupt handling */
int (*handle_interrupt)(struct phy_device *phydev);
/* Clears up any memory if needed */
void (*remove)(struct phy_device *phydev);
......@@ -1137,6 +1139,7 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
int phy_drivers_register(struct phy_driver *new_driver, int n,
struct module *owner);
void phy_state_machine(struct work_struct *work);
void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies);
void phy_mac_interrupt(struct phy_device *phydev);
void phy_start_machine(struct phy_device *phydev);
void phy_stop_machine(struct phy_device *phydev);
......@@ -1147,6 +1150,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
const struct ethtool_link_ksettings *cmd);
int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
void phy_request_interrupt(struct phy_device *phydev);
void phy_free_interrupt(struct phy_device *phydev);
void phy_print_status(struct phy_device *phydev);
int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment