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

usbnet: smsc95xx: 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 06a221be
...@@ -73,20 +73,26 @@ static bool turbo_mode = true; ...@@ -73,20 +73,26 @@ 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 smsc95xx_read_reg(struct usbnet *dev, u32 index, static int __must_check __smsc95xx_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, "Failed to read register index 0x%08x\n", index); netdev_warn(dev->net,
"Failed to read reg index 0x%08x: %d", index, ret);
le32_to_cpus(&buf); le32_to_cpus(&buf);
*data = buf; *data = buf;
...@@ -94,35 +100,64 @@ static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index, ...@@ -94,35 +100,64 @@ static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index,
return ret; return ret;
} }
static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index, static int __must_check __smsc95xx_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 = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT
ret = usbnet_write_cmd(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
USB_DIR_OUT | USB_TYPE_VENDOR | 0, index, &buf, 4);
USB_RECIP_DEVICE,
0, index, &buf, 4);
if (unlikely(ret < 0)) if (unlikely(ret < 0))
netdev_warn(dev->net, "Failed to write register index 0x%08x\n", index); netdev_warn(dev->net,
"Failed to write reg index 0x%08x: %d", index, ret);
return ret; return ret;
} }
static int __must_check smsc95xx_read_reg_nopm(struct usbnet *dev, u32 index,
u32 *data)
{
return __smsc95xx_read_reg(dev, index, data, 1);
}
static int __must_check smsc95xx_write_reg_nopm(struct usbnet *dev, u32 index,
u32 data)
{
return __smsc95xx_write_reg(dev, index, data, 1);
}
static int __must_check smsc95xx_read_reg(struct usbnet *dev, u32 index,
u32 *data)
{
return __smsc95xx_read_reg(dev, index, data, 0);
}
static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index,
u32 data)
{
return __smsc95xx_write_reg(dev, index, data, 0);
}
static int smsc95xx_set_feature(struct usbnet *dev, u32 feature) static int smsc95xx_set_feature(struct usbnet *dev, u32 feature)
{ {
if (WARN_ON_ONCE(!dev)) if (WARN_ON_ONCE(!dev))
return -EINVAL; return -EINVAL;
return usbnet_write_cmd(dev, USB_REQ_SET_FEATURE, return usbnet_write_cmd_nopm(dev, USB_REQ_SET_FEATURE,
USB_RECIP_DEVICE, feature, 0, NULL, 0); USB_RECIP_DEVICE, feature, 0,
NULL, 0);
} }
static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature) static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature)
...@@ -130,8 +165,9 @@ static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature) ...@@ -130,8 +165,9 @@ static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature)
if (WARN_ON_ONCE(!dev)) if (WARN_ON_ONCE(!dev))
return -EINVAL; return -EINVAL;
return usbnet_write_cmd(dev, USB_REQ_CLEAR_FEATURE, return usbnet_write_cmd_nopm(dev, USB_REQ_CLEAR_FEATURE,
USB_RECIP_DEVICE, feature, 0, NULL, 0); USB_RECIP_DEVICE, feature,
0, NULL, 0);
} }
/* Loop until the read is completed with timeout /* Loop until the read is completed with timeout
...@@ -708,7 +744,7 @@ static int smsc95xx_start_tx_path(struct usbnet *dev) ...@@ -708,7 +744,7 @@ static int smsc95xx_start_tx_path(struct usbnet *dev)
} }
/* Starts the Receive path */ /* Starts the Receive path */
static int smsc95xx_start_rx_path(struct usbnet *dev) static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm)
{ {
struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
unsigned long flags; unsigned long flags;
...@@ -718,7 +754,7 @@ static int smsc95xx_start_rx_path(struct usbnet *dev) ...@@ -718,7 +754,7 @@ static int smsc95xx_start_rx_path(struct usbnet *dev)
pdata->mac_cr |= MAC_CR_RXEN_; pdata->mac_cr |= MAC_CR_RXEN_;
spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); spin_unlock_irqrestore(&pdata->mac_cr_lock, flags);
ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); ret = __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm);
check_warn_return(ret, "Failed to write MAC_CR: %d\n", ret); check_warn_return(ret, "Failed to write MAC_CR: %d\n", ret);
return 0; return 0;
...@@ -937,7 +973,7 @@ static int smsc95xx_reset(struct usbnet *dev) ...@@ -937,7 +973,7 @@ static int smsc95xx_reset(struct usbnet *dev)
ret = smsc95xx_start_tx_path(dev); ret = smsc95xx_start_tx_path(dev);
check_warn_return(ret, "Failed to start TX path"); check_warn_return(ret, "Failed to start TX path");
ret = smsc95xx_start_rx_path(dev); ret = smsc95xx_start_rx_path(dev, 0);
check_warn_return(ret, "Failed to start RX path"); check_warn_return(ret, "Failed to start RX path");
netif_dbg(dev, ifup, dev->net, "smsc95xx_reset, return 0\n"); netif_dbg(dev, ifup, dev->net, "smsc95xx_reset, return 0\n");
...@@ -1039,30 +1075,30 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1039,30 +1075,30 @@ static int smsc95xx_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 = smsc95xx_read_reg(dev, WUCSR, &val); ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_); val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_);
ret = smsc95xx_write_reg(dev, WUCSR, val); ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
ret = smsc95xx_read_reg(dev, PM_CTRL, &val); ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
check_warn_return(ret, "Error reading PM_CTRL"); check_warn_return(ret, "Error reading PM_CTRL");
val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_); val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_);
ret = smsc95xx_write_reg(dev, PM_CTRL, val); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL"); check_warn_return(ret, "Error writing PM_CTRL");
/* enter suspend2 mode */ /* enter suspend2 mode */
ret = smsc95xx_read_reg(dev, PM_CTRL, &val); ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
check_warn_return(ret, "Error reading PM_CTRL"); check_warn_return(ret, "Error reading PM_CTRL");
val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_);
val |= PM_CTL_SUS_MODE_2; val |= PM_CTL_SUS_MODE_2;
ret = smsc95xx_write_reg(dev, PM_CTRL, val); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL"); check_warn_return(ret, "Error writing PM_CTRL");
return 0; return 0;
...@@ -1131,7 +1167,7 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1131,7 +1167,7 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
} }
for (i = 0; i < (pdata->wuff_filter_count * 4); i++) { for (i = 0; i < (pdata->wuff_filter_count * 4); i++) {
ret = smsc95xx_write_reg(dev, WUFF, filter_mask[i]); ret = smsc95xx_write_reg_nopm(dev, WUFF, filter_mask[i]);
if (ret < 0) if (ret < 0)
kfree(filter_mask); kfree(filter_mask);
check_warn_return(ret, "Error writing WUFF"); check_warn_return(ret, "Error writing WUFF");
...@@ -1139,43 +1175,43 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1139,43 +1175,43 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
kfree(filter_mask); kfree(filter_mask);
for (i = 0; i < (pdata->wuff_filter_count / 4); i++) { for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
ret = smsc95xx_write_reg(dev, WUFF, command[i]); ret = smsc95xx_write_reg_nopm(dev, WUFF, command[i]);
check_warn_return(ret, "Error writing WUFF"); check_warn_return(ret, "Error writing WUFF");
} }
for (i = 0; i < (pdata->wuff_filter_count / 4); i++) { for (i = 0; i < (pdata->wuff_filter_count / 4); i++) {
ret = smsc95xx_write_reg(dev, WUFF, offset[i]); ret = smsc95xx_write_reg_nopm(dev, WUFF, offset[i]);
check_warn_return(ret, "Error writing WUFF"); check_warn_return(ret, "Error writing WUFF");
} }
for (i = 0; i < (pdata->wuff_filter_count / 2); i++) { for (i = 0; i < (pdata->wuff_filter_count / 2); i++) {
ret = smsc95xx_write_reg(dev, WUFF, crc[i]); ret = smsc95xx_write_reg_nopm(dev, WUFF, crc[i]);
check_warn_return(ret, "Error writing WUFF"); check_warn_return(ret, "Error writing WUFF");
} }
/* clear any pending pattern match packet status */ /* clear any pending pattern match packet status */
ret = smsc95xx_read_reg(dev, WUCSR, &val); ret = smsc95xx_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 = smsc95xx_write_reg(dev, WUCSR, val); ret = smsc95xx_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) {
/* clear any pending magic packet status */ /* clear any pending magic packet status */
ret = smsc95xx_read_reg(dev, WUCSR, &val); ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val |= WUCSR_MPR_; val |= WUCSR_MPR_;
ret = smsc95xx_write_reg(dev, WUCSR, val); ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
} }
/* enable/disable wakeup sources */ /* enable/disable wakeup sources */
ret = smsc95xx_read_reg(dev, WUCSR, &val); ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) {
...@@ -1194,41 +1230,41 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) ...@@ -1194,41 +1230,41 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message)
val &= ~WUCSR_MPEN_; val &= ~WUCSR_MPEN_;
} }
ret = smsc95xx_write_reg(dev, WUCSR, val); ret = smsc95xx_write_reg_nopm(dev, WUCSR, val);
check_warn_return(ret, "Error writing WUCSR"); check_warn_return(ret, "Error writing WUCSR");
/* enable wol wakeup source */ /* enable wol wakeup source */
ret = smsc95xx_read_reg(dev, PM_CTRL, &val); ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
check_warn_return(ret, "Error reading PM_CTRL"); check_warn_return(ret, "Error reading PM_CTRL");
val |= PM_CTL_WOL_EN_; val |= PM_CTL_WOL_EN_;
ret = smsc95xx_write_reg(dev, PM_CTRL, val); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL"); check_warn_return(ret, "Error writing PM_CTRL");
/* enable receiver to enable frame reception */ /* enable receiver to enable frame reception */
smsc95xx_start_rx_path(dev); smsc95xx_start_rx_path(dev, 1);
/* 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 = smsc95xx_read_reg(dev, PM_CTRL, &val); ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
check_warn_return(ret, "Error reading PM_CTRL"); check_warn_return(ret, "Error reading PM_CTRL");
val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_)); val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_));
val |= PM_CTL_SUS_MODE_0; val |= PM_CTL_SUS_MODE_0;
ret = smsc95xx_write_reg(dev, PM_CTRL, val); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL"); check_warn_return(ret, "Error writing PM_CTRL");
/* clear wol status */ /* clear wol status */
val &= ~PM_CTL_WUPS_; val &= ~PM_CTL_WUPS_;
val |= PM_CTL_WUPS_WOL_; val |= PM_CTL_WUPS_WOL_;
ret = smsc95xx_write_reg(dev, PM_CTRL, val); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL"); check_warn_return(ret, "Error writing PM_CTRL");
/* read back PM_CTRL */ /* read back PM_CTRL */
ret = smsc95xx_read_reg(dev, PM_CTRL, &val); ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
check_warn_return(ret, "Error reading PM_CTRL"); check_warn_return(ret, "Error reading PM_CTRL");
smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
...@@ -1249,22 +1285,22 @@ static int smsc95xx_resume(struct usb_interface *intf) ...@@ -1249,22 +1285,22 @@ static int smsc95xx_resume(struct usb_interface *intf)
smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
/* clear wake-up sources */ /* clear wake-up sources */
ret = smsc95xx_read_reg(dev, WUCSR, &val); ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val);
check_warn_return(ret, "Error reading WUCSR"); check_warn_return(ret, "Error reading WUCSR");
val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_); val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_);
ret = smsc95xx_write_reg(dev, WUCSR, val); ret = smsc95xx_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 = smsc95xx_read_reg(dev, PM_CTRL, &val); ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val);
check_warn_return(ret, "Error reading PM_CTRL"); check_warn_return(ret, "Error reading PM_CTRL");
val &= ~PM_CTL_WOL_EN_; val &= ~PM_CTL_WOL_EN_;
val |= PM_CTL_WUPS_; val |= PM_CTL_WUPS_;
ret = smsc95xx_write_reg(dev, PM_CTRL, val); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val);
check_warn_return(ret, "Error writing PM_CTRL"); check_warn_return(ret, "Error writing PM_CTRL");
} }
......
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