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 @@ ...@@ -29,6 +29,8 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#define PHY_STATE_TIME HZ
#define PHY_STATE_STR(_state) \ #define PHY_STATE_STR(_state) \
case PHY_##_state: \ case PHY_##_state: \
return __stringify(_state); \ return __stringify(_state); \
...@@ -478,12 +480,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd) ...@@ -478,12 +480,12 @@ int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd)
} }
EXPORT_SYMBOL(phy_mii_ioctl); EXPORT_SYMBOL(phy_mii_ioctl);
static void phy_queue_state_machine(struct phy_device *phydev, void phy_queue_state_machine(struct phy_device *phydev, unsigned long jiffies)
unsigned int secs)
{ {
mod_delayed_work(system_power_efficient_wq, &phydev->state_queue, 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) static void phy_trigger_machine(struct phy_device *phydev)
{ {
...@@ -772,8 +774,13 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) ...@@ -772,8 +774,13 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev)) if (phydev->drv->did_interrupt && !phydev->drv->did_interrupt(phydev))
return IRQ_NONE; return IRQ_NONE;
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 */ /* reschedule state queue work to run as soon as possible */
phy_trigger_machine(phydev); phy_trigger_machine(phydev);
}
if (phy_clear_interrupt(phydev)) if (phy_clear_interrupt(phydev))
goto phy_err; goto phy_err;
...@@ -799,10 +806,10 @@ static int phy_enable_interrupts(struct phy_device *phydev) ...@@ -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 * @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. * If this fails, then we set irq to PHY_POLL.
* This should only be called with a valid IRQ number. * This should only be called with a valid IRQ number.
*/ */
...@@ -817,10 +824,30 @@ void phy_request_interrupt(struct phy_device *phydev) ...@@ -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", phydev_warn(phydev, "Error %d requesting IRQ %d, falling back to polling\n",
err, phydev->irq); err, phydev->irq);
phydev->irq = PHY_POLL; 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); 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 * phy_stop - Bring down the PHY link, and stop checking the status
* @phydev: target phy_device struct * @phydev: target phy_device struct
...@@ -835,9 +862,6 @@ void phy_stop(struct phy_device *phydev) ...@@ -835,9 +862,6 @@ void phy_stop(struct phy_device *phydev)
mutex_lock(&phydev->lock); mutex_lock(&phydev->lock);
if (phy_interrupt_is_valid(phydev))
phy_disable_interrupts(phydev);
phydev->state = PHY_HALTED; phydev->state = PHY_HALTED;
mutex_unlock(&phydev->lock); mutex_unlock(&phydev->lock);
...@@ -864,8 +888,6 @@ EXPORT_SYMBOL(phy_stop); ...@@ -864,8 +888,6 @@ EXPORT_SYMBOL(phy_stop);
*/ */
void phy_start(struct phy_device *phydev) void phy_start(struct phy_device *phydev)
{ {
int err;
mutex_lock(&phydev->lock); mutex_lock(&phydev->lock);
if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) { if (phydev->state != PHY_READY && phydev->state != PHY_HALTED) {
...@@ -877,13 +899,6 @@ void phy_start(struct phy_device *phydev) ...@@ -877,13 +899,6 @@ void phy_start(struct phy_device *phydev)
/* if phy was suspended, bring the physical link up again */ /* if phy was suspended, bring the physical link up again */
__phy_resume(phydev); __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; phydev->state = PHY_UP;
phy_start_machine(phydev); phy_start_machine(phydev);
......
...@@ -1016,7 +1016,7 @@ void phy_disconnect(struct phy_device *phydev) ...@@ -1016,7 +1016,7 @@ void phy_disconnect(struct phy_device *phydev)
phy_stop(phydev); phy_stop(phydev);
if (phy_interrupt_is_valid(phydev)) if (phy_interrupt_is_valid(phydev))
free_irq(phydev->irq, phydev); phy_free_interrupt(phydev);
phydev->adjust_link = NULL; phydev->adjust_link = NULL;
......
...@@ -188,7 +188,6 @@ static inline const char *phy_modes(phy_interface_t interface) ...@@ -188,7 +188,6 @@ static inline const char *phy_modes(phy_interface_t interface)
#define PHY_INIT_TIMEOUT 100000 #define PHY_INIT_TIMEOUT 100000
#define PHY_STATE_TIME 1
#define PHY_FORCE_TIMEOUT 10 #define PHY_FORCE_TIMEOUT 10
#define PHY_MAX_ADDR 32 #define PHY_MAX_ADDR 32
...@@ -537,6 +536,9 @@ struct phy_driver { ...@@ -537,6 +536,9 @@ struct phy_driver {
*/ */
int (*did_interrupt)(struct phy_device *phydev); int (*did_interrupt)(struct phy_device *phydev);
/* Override default interrupt handling */
int (*handle_interrupt)(struct phy_device *phydev);
/* Clears up any memory if needed */ /* Clears up any memory if needed */
void (*remove)(struct phy_device *phydev); void (*remove)(struct phy_device *phydev);
...@@ -1137,6 +1139,7 @@ int phy_driver_register(struct phy_driver *new_driver, struct module *owner); ...@@ -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, int phy_drivers_register(struct phy_driver *new_driver, int n,
struct module *owner); struct module *owner);
void phy_state_machine(struct work_struct *work); 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_mac_interrupt(struct phy_device *phydev);
void phy_start_machine(struct phy_device *phydev); void phy_start_machine(struct phy_device *phydev);
void phy_stop_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, ...@@ -1147,6 +1150,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
const struct ethtool_link_ksettings *cmd); const struct ethtool_link_ksettings *cmd);
int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd); int phy_mii_ioctl(struct phy_device *phydev, struct ifreq *ifr, int cmd);
void phy_request_interrupt(struct phy_device *phydev); void phy_request_interrupt(struct phy_device *phydev);
void phy_free_interrupt(struct phy_device *phydev);
void phy_print_status(struct phy_device *phydev); void phy_print_status(struct phy_device *phydev);
int phy_set_max_speed(struct phy_device *phydev, u32 max_speed); int phy_set_max_speed(struct phy_device *phydev, u32 max_speed);
void phy_remove_link_mode(struct phy_device *phydev, u32 link_mode); 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