Commit 47bbea41 authored by Ming Lei's avatar Ming Lei Committed by David S. Miller

usbnet: smsc75xx: apply the introduced usbnet_{read|write}_cmd_nopm

This patch applies the introduced usbnet_read_cmd_nopm() and
usbnet_write_cmd_nopm() in the callback of resume and suspend
to avoid deadlock if USB runtime PM is considered into
usbnet_read_cmd() and usbnet_write_cmd().

Cc: Steve Glendinning <steve.glendinning@shawell.net>
Signed-off-by: default avatarMing Lei <ming.lei@canonical.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0547fad2
...@@ -85,18 +85,23 @@ static bool turbo_mode = true; ...@@ -85,18 +85,23 @@ static bool turbo_mode = true;
module_param(turbo_mode, bool, 0644); module_param(turbo_mode, bool, 0644);
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");
static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index,
u32 *data) u32 *data, int in_pm)
{ {
u32 buf; u32 buf;
int ret; int ret;
int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
BUG_ON(!dev); BUG_ON(!dev);
ret = usbnet_read_cmd(dev, USB_VENDOR_REQUEST_READ_REGISTER, if (!in_pm)
USB_DIR_IN | USB_TYPE_VENDOR | fn = usbnet_read_cmd;
USB_RECIP_DEVICE, else
0, index, &buf, 4); fn = usbnet_read_cmd_nopm;
ret = fn(dev, USB_VENDOR_REQUEST_READ_REGISTER, USB_DIR_IN
| USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0, index, &buf, 4);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
netdev_warn(dev->net, netdev_warn(dev->net,
"Failed to read reg index 0x%08x: %d", index, ret); "Failed to read reg index 0x%08x: %d", index, ret);
...@@ -107,21 +112,26 @@ static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, ...@@ -107,21 +112,26 @@ static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index,
return ret; return ret;
} }
static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, static int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index,
u32 data) u32 data, int in_pm)
{ {
u32 buf; u32 buf;
int ret; int ret;
int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
BUG_ON(!dev); BUG_ON(!dev);
if (!in_pm)
fn = usbnet_write_cmd;
else
fn = usbnet_write_cmd_nopm;
buf = data; buf = data;
cpu_to_le32s(&buf); cpu_to_le32s(&buf);
ret = usbnet_write_cmd(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT
USB_DIR_OUT | USB_TYPE_VENDOR | | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
USB_RECIP_DEVICE, 0, index, &buf, 4);
0, index, &buf, 4);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
netdev_warn(dev->net, netdev_warn(dev->net,
"Failed to write reg index 0x%08x: %d", index, ret); "Failed to write reg index 0x%08x: %d", index, ret);
...@@ -129,16 +139,38 @@ static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, ...@@ -129,16 +139,38 @@ static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index,
return ret; return ret;
} }
static int __must_check smsc75xx_read_reg_nopm(struct usbnet *dev, u32 index,
u32 *data)
{
return __smsc75xx_read_reg(dev, index, data, 1);
}
static int __must_check smsc75xx_write_reg_nopm(struct usbnet *dev, u32 index,
u32 data)
{
return __smsc75xx_write_reg(dev, index, data, 1);
}
static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index,
u32 *data)
{
return __smsc75xx_read_reg(dev, index, data, 0);
}
static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index,
u32 data)
{
return __smsc75xx_write_reg(dev, index, data, 0);
}
static int smsc75xx_set_feature(struct usbnet *dev, u32 feature) static int smsc75xx_set_feature(struct usbnet *dev, u32 feature)
{ {
if (WARN_ON_ONCE(!dev)) if (WARN_ON_ONCE(!dev))
return -EINVAL; return -EINVAL;
cpu_to_le32s(&feature); return usbnet_write_cmd_nopm(dev, USB_REQ_SET_FEATURE,
USB_DIR_OUT | USB_RECIP_DEVICE,
return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), feature, 0, NULL, 0);
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
} }
static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature) static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature)
...@@ -146,11 +178,9 @@ static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature) ...@@ -146,11 +178,9 @@ static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature)
if (WARN_ON_ONCE(!dev)) if (WARN_ON_ONCE(!dev))
return -EINVAL; return -EINVAL;
cpu_to_le32s(&feature); return usbnet_write_cmd_nopm(dev, USB_REQ_CLEAR_FEATURE,
USB_DIR_OUT | USB_RECIP_DEVICE,
return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), feature, 0, NULL, 0);
USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
} }
/* Loop until the read is completed with timeout /* Loop until the read is completed with timeout
...@@ -796,13 +826,16 @@ static int smsc75xx_set_features(struct net_device *netdev, ...@@ -796,13 +826,16 @@ static int smsc75xx_set_features(struct net_device *netdev,
return 0; return 0;
} }
static int smsc75xx_wait_ready(struct usbnet *dev) static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm)
{ {
int timeout = 0; int timeout = 0;
do { do {
u32 buf; u32 buf;
int ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); int ret;
ret = __smsc75xx_read_reg(dev, PMT_CTL, &buf, in_pm);
check_warn_return(ret, "Failed to read PMT_CTL: %d", ret); check_warn_return(ret, "Failed to read PMT_CTL: %d", ret);
if (buf & PMT_CTL_DEV_RDY) if (buf & PMT_CTL_DEV_RDY)
...@@ -824,7 +857,7 @@ static int smsc75xx_reset(struct usbnet *dev) ...@@ -824,7 +857,7 @@ static int smsc75xx_reset(struct usbnet *dev)
netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset"); netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset");
ret = smsc75xx_wait_ready(dev); ret = smsc75xx_wait_ready(dev, 0);
check_warn_return(ret, "device not ready in smsc75xx_reset"); check_warn_return(ret, "device not ready in smsc75xx_reset");
ret = smsc75xx_read_reg(dev, HW_CFG, &buf); ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
...@@ -1191,30 +1224,30 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1191,30 +1224,30 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
netdev_info(dev->net, "entering SUSPEND2 mode"); netdev_info(dev->net, "entering SUSPEND2 mode");
/* disable energy detect (link up) & wake up events */ /* disable energy detect (link up) & wake up events */
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val &= ~(WUCSR_MPEN | WUCSR_WUEN); val &= ~(WUCSR_MPEN | WUCSR_WUEN);
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
ret = smsc75xx_read_reg(dev, PMT_CTL, &val); ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL"); check_warn_return(ret, "Error reading PMT_CTL");
val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN); val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN);
ret = smsc75xx_write_reg(dev, PMT_CTL, val); ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL"); check_warn_return(ret, "Error writing PMT_CTL");
/* enter suspend2 mode */ /* enter suspend2 mode */
ret = smsc75xx_read_reg(dev, PMT_CTL, &val); ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL"); check_warn_return(ret, "Error reading PMT_CTL");
val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
val |= PMT_CTL_SUS_MODE_2; val |= PMT_CTL_SUS_MODE_2;
ret = smsc75xx_write_reg(dev, PMT_CTL, val); ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL"); check_warn_return(ret, "Error writing PMT_CTL");
return 0; return 0;
...@@ -1225,7 +1258,7 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1225,7 +1258,7 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
/* disable all filters */ /* disable all filters */
for (i = 0; i < WUF_NUM; i++) { for (i = 0; i < WUF_NUM; i++) {
ret = smsc75xx_write_reg(dev, WUF_CFGX + i * 4, 0); ret = smsc75xx_write_reg_nopm(dev, WUF_CFGX + i * 4, 0);
check_warn_return(ret, "Error writing WUF_CFGX"); check_warn_return(ret, "Error writing WUF_CFGX");
} }
...@@ -1250,95 +1283,95 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1250,95 +1283,95 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
} }
/* clear any pending pattern match packet status */ /* clear any pending pattern match packet status */
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val |= WUCSR_WUFR; val |= WUCSR_WUFR;
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
netdev_info(dev->net, "enabling packet match detection"); netdev_info(dev->net, "enabling packet match detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val |= WUCSR_WUEN; val |= WUCSR_WUEN;
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
} else { } else {
netdev_info(dev->net, "disabling packet match detection"); netdev_info(dev->net, "disabling packet match detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val &= ~WUCSR_WUEN; val &= ~WUCSR_WUEN;
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
} }
/* disable magic, bcast & unicast wakeup sources */ /* disable magic, bcast & unicast wakeup sources */
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN); val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN);
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
if (pdata->wolopts & WAKE_MAGIC) { if (pdata->wolopts & WAKE_MAGIC) {
netdev_info(dev->net, "enabling magic packet wakeup"); netdev_info(dev->net, "enabling magic packet wakeup");
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
/* clear any pending magic packet status */ /* clear any pending magic packet status */
val |= WUCSR_MPR | WUCSR_MPEN; val |= WUCSR_MPR | WUCSR_MPEN;
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
} }
if (pdata->wolopts & WAKE_BCAST) { if (pdata->wolopts & WAKE_BCAST) {
netdev_info(dev->net, "enabling broadcast detection"); netdev_info(dev->net, "enabling broadcast detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val |= WUCSR_BCAST_FR | WUCSR_BCST_EN; val |= WUCSR_BCAST_FR | WUCSR_BCST_EN;
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
} }
if (pdata->wolopts & WAKE_UCAST) { if (pdata->wolopts & WAKE_UCAST) {
netdev_info(dev->net, "enabling unicast detection"); netdev_info(dev->net, "enabling unicast detection");
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val |= WUCSR_WUFR | WUCSR_PFDA_EN; val |= WUCSR_WUFR | WUCSR_PFDA_EN;
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
} }
/* enable receiver to enable frame reception */ /* enable receiver to enable frame reception */
ret = smsc75xx_read_reg(dev, MAC_RX, &val); ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val);
check_warn_return(ret, "Failed to read MAC_RX: %d", ret); check_warn_return(ret, "Failed to read MAC_RX: %d", ret);
val |= MAC_RX_RXEN; val |= MAC_RX_RXEN;
ret = smsc75xx_write_reg(dev, MAC_RX, val); ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val);
check_warn_return(ret, "Failed to write MAC_RX: %d", ret); check_warn_return(ret, "Failed to write MAC_RX: %d", ret);
/* some wol options are enabled, so enter SUSPEND0 */ /* some wol options are enabled, so enter SUSPEND0 */
netdev_info(dev->net, "entering SUSPEND0 mode"); netdev_info(dev->net, "entering SUSPEND0 mode");
ret = smsc75xx_read_reg(dev, PMT_CTL, &val); ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL"); check_warn_return(ret, "Error reading PMT_CTL");
val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST)); val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST));
val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS; val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS;
ret = smsc75xx_write_reg(dev, PMT_CTL, val); ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL"); check_warn_return(ret, "Error writing PMT_CTL");
smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
...@@ -1359,37 +1392,37 @@ static int smsc75xx_resume(struct usb_interface *intf) ...@@ -1359,37 +1392,37 @@ static int smsc75xx_resume(struct usb_interface *intf)
smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
/* Disable wakeup sources */ /* Disable wakeup sources */
ret = smsc75xx_read_reg(dev, WUCSR, &val); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN
| WUCSR_BCST_EN); | WUCSR_BCST_EN);
ret = smsc75xx_write_reg(dev, WUCSR, val); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
/* clear wake-up status */ /* clear wake-up status */
ret = smsc75xx_read_reg(dev, PMT_CTL, &val); ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL"); check_warn_return(ret, "Error reading PMT_CTL");
val &= ~PMT_CTL_WOL_EN; val &= ~PMT_CTL_WOL_EN;
val |= PMT_CTL_WUPS; val |= PMT_CTL_WUPS;
ret = smsc75xx_write_reg(dev, PMT_CTL, val); ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL"); check_warn_return(ret, "Error writing PMT_CTL");
} else { } else {
netdev_info(dev->net, "resuming from SUSPEND2"); netdev_info(dev->net, "resuming from SUSPEND2");
ret = smsc75xx_read_reg(dev, PMT_CTL, &val); ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val);
check_warn_return(ret, "Error reading PMT_CTL"); check_warn_return(ret, "Error reading PMT_CTL");
val |= PMT_CTL_PHY_PWRUP; val |= PMT_CTL_PHY_PWRUP;
ret = smsc75xx_write_reg(dev, PMT_CTL, val); ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val);
check_warn_return(ret, "Error writing PMT_CTL"); check_warn_return(ret, "Error writing PMT_CTL");
} }
ret = smsc75xx_wait_ready(dev); ret = smsc75xx_wait_ready(dev, 1);
check_warn_return(ret, "device not ready in smsc75xx_resume"); check_warn_return(ret, "device not ready in smsc75xx_resume");
return usbnet_resume(intf); return usbnet_resume(intf);
......
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