Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+90 −57
Original line number Diff line number Diff line
@@ -85,17 +85,22 @@ static bool turbo_mode = true;
module_param(turbo_mode, bool, 0644);
MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction");

static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index,
					  u32 *data)
static int __must_check __smsc75xx_read_reg(struct usbnet *dev, u32 index,
					    u32 *data, int in_pm)
{
	u32 buf;
	int ret;
	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);

	BUG_ON(!dev);

	ret = usbnet_read_cmd(dev, USB_VENDOR_REQUEST_READ_REGISTER,
			      USB_DIR_IN | USB_TYPE_VENDOR |
			      USB_RECIP_DEVICE,
	if (!in_pm)
		fn = usbnet_read_cmd;
	else
		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))
		netdev_warn(dev->net,
@@ -107,20 +112,25 @@ static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index,
	return ret;
}

static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index,
					   u32 data)
static int __must_check __smsc75xx_write_reg(struct usbnet *dev, u32 index,
					     u32 data, int in_pm)
{
	u32 buf;
	int ret;
	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);

	BUG_ON(!dev);

	if (!in_pm)
		fn = usbnet_write_cmd;
	else
		fn = usbnet_write_cmd_nopm;

	buf = data;
	cpu_to_le32s(&buf);

	ret = usbnet_write_cmd(dev, USB_VENDOR_REQUEST_WRITE_REGISTER,
			       USB_DIR_OUT | USB_TYPE_VENDOR |
			       USB_RECIP_DEVICE,
	ret = fn(dev, USB_VENDOR_REQUEST_WRITE_REGISTER, USB_DIR_OUT
		 | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
		 0, index, &buf, 4);
	if (unlikely(ret < 0))
		netdev_warn(dev->net,
@@ -129,16 +139,38 @@ static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index,
	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)
{
	if (WARN_ON_ONCE(!dev))
		return -EINVAL;

	cpu_to_le32s(&feature);

	return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
		USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
		USB_CTRL_SET_TIMEOUT);
	return usbnet_write_cmd_nopm(dev, USB_REQ_SET_FEATURE,
				     USB_DIR_OUT | USB_RECIP_DEVICE,
				     feature, 0, NULL, 0);
}

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))
		return -EINVAL;

	cpu_to_le32s(&feature);

	return usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
		USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, feature, 0, NULL, 0,
		USB_CTRL_SET_TIMEOUT);
	return usbnet_write_cmd_nopm(dev, USB_REQ_CLEAR_FEATURE,
				     USB_DIR_OUT | USB_RECIP_DEVICE,
				     feature, 0, NULL, 0);
}

/* Loop until the read is completed with timeout
@@ -796,13 +826,16 @@ static int smsc75xx_set_features(struct net_device *netdev,
	return 0;
}

static int smsc75xx_wait_ready(struct usbnet *dev)
static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm)
{
	int timeout = 0;

	do {
		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);

		if (buf & PMT_CTL_DEV_RDY)
@@ -824,7 +857,7 @@ static int smsc75xx_reset(struct usbnet *dev)

	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");

	ret = smsc75xx_read_reg(dev, HW_CFG, &buf);
@@ -1191,30 +1224,30 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
		netdev_info(dev->net, "entering SUSPEND2 mode");

		/* 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");

		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");

		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");

		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");

		/* 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");

		val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST);
		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");

		return 0;
@@ -1225,7 +1258,7 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)

		/* disable all filters */
		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");
		}

@@ -1250,95 +1283,95 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message)
		}

		/* 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");

		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");

		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");

		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");
	} else {
		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");

		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");
	}

	/* 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");

	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");

	if (pdata->wolopts & WAKE_MAGIC) {
		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");

		/* clear any pending magic packet status */
		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");
	}

	if (pdata->wolopts & WAKE_BCAST) {
		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");

		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");
	}

	if (pdata->wolopts & WAKE_UCAST) {
		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");

		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");
	}

	/* 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);

	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);

	/* some wol options are enabled, so enter SUSPEND0 */
	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");

	val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST));
	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");

	smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP);
@@ -1359,37 +1392,37 @@ static int smsc75xx_resume(struct usb_interface *intf)
		smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP);

		/* 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");

		val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_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");

		/* 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");

		val &= ~PMT_CTL_WOL_EN;
		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");
	} else {
		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");

		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");
	}

	ret = smsc75xx_wait_ready(dev);
	ret = smsc75xx_wait_ready(dev, 1);
	check_warn_return(ret, "device not ready in smsc75xx_resume");

	return usbnet_resume(intf);