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

Commit e1e609be authored by Yoshihiro Shimoda's avatar Yoshihiro Shimoda Committed by Greg Kroah-Hartman
Browse files

USB: r8a66597-hcd: suspend/resume support



Fix the problem that system cannot suspend.

Signed-off-by: default avatarYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 71d2718f
Loading
Loading
Loading
Loading
+100 −1
Original line number Diff line number Diff line
@@ -1013,6 +1013,9 @@ static void r8a66597_check_syssts(struct r8a66597 *r8a66597, int port,

	r8a66597_write(r8a66597, ~DTCH, get_intsts_reg(port));
	r8a66597_bset(r8a66597, DTCHE, get_intenb_reg(port));

	if (r8a66597->bus_suspended)
		usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
}

/* this function must be called with interrupt disabled */
@@ -1614,6 +1617,11 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
			r8a66597_bclr(r8a66597, DTCHE, INTENB2);
			r8a66597_usb_disconnect(r8a66597, 1);
		}
		if (mask2 & BCHG) {
			r8a66597_write(r8a66597, ~BCHG, INTSTS2);
			r8a66597_bclr(r8a66597, BCHGE, INTENB2);
			usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
		}
	}

	if (mask1) {
@@ -1629,6 +1637,12 @@ static irqreturn_t r8a66597_irq(struct usb_hcd *hcd)
			r8a66597_bclr(r8a66597, DTCHE, INTENB1);
			r8a66597_usb_disconnect(r8a66597, 0);
		}
		if (mask1 & BCHG) {
			r8a66597_write(r8a66597, ~BCHG, INTSTS1);
			r8a66597_bclr(r8a66597, BCHGE, INTENB1);
			usb_hcd_resume_root_hub(r8a66597_to_hcd(r8a66597));
		}

		if (mask1 & SIGN) {
			r8a66597_write(r8a66597, ~SIGN, INTSTS1);
			status = get_urb_error(r8a66597, 0);
@@ -2140,7 +2154,7 @@ static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,

		switch (wValue) {
		case USB_PORT_FEAT_ENABLE:
			rh->port &= (1 << USB_PORT_FEAT_POWER);
			rh->port &= ~(1 << USB_PORT_FEAT_POWER);
			break;
		case USB_PORT_FEAT_SUSPEND:
			break;
@@ -2212,6 +2226,68 @@ error:
	return ret;
}

#if defined(CONFIG_PM)
static int r8a66597_bus_suspend(struct usb_hcd *hcd)
{
	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
	int port;

	dbg("%s", __func__);

	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
		struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
		unsigned long dvstctr_reg = get_dvstctr_reg(port);

		if (!(rh->port & (1 << USB_PORT_FEAT_ENABLE)))
			continue;

		dbg("suspend port = %d", port);
		r8a66597_bclr(r8a66597, UACT, dvstctr_reg);	/* suspend */
		rh->port |= 1 << USB_PORT_FEAT_SUSPEND;

		if (rh->dev->udev->do_remote_wakeup) {
			msleep(3);	/* waiting last SOF */
			r8a66597_bset(r8a66597, RWUPE, dvstctr_reg);
			r8a66597_write(r8a66597, ~BCHG, get_intsts_reg(port));
			r8a66597_bset(r8a66597, BCHGE, get_intenb_reg(port));
		}
	}

	r8a66597->bus_suspended = 1;

	return 0;
}

static int r8a66597_bus_resume(struct usb_hcd *hcd)
{
	struct r8a66597 *r8a66597 = hcd_to_r8a66597(hcd);
	int port;

	dbg("%s", __func__);

	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
		struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];
		unsigned long dvstctr_reg = get_dvstctr_reg(port);

		if (!(rh->port & (1 << USB_PORT_FEAT_SUSPEND)))
			continue;

		dbg("resume port = %d", port);
		rh->port &= ~(1 << USB_PORT_FEAT_SUSPEND);
		rh->port |= 1 << USB_PORT_FEAT_C_SUSPEND;
		r8a66597_mdfy(r8a66597, RESUME, RESUME | UACT, dvstctr_reg);
		msleep(50);
		r8a66597_mdfy(r8a66597, UACT, RESUME | UACT, dvstctr_reg);
	}

	return 0;

}
#else
#define	r8a66597_bus_suspend	NULL
#define	r8a66597_bus_resume	NULL
#endif

static struct hc_driver r8a66597_hc_driver = {
	.description =		hcd_name,
	.hcd_priv_size =	sizeof(struct r8a66597),
@@ -2242,16 +2318,39 @@ static struct hc_driver r8a66597_hc_driver = {
	 */
	.hub_status_data =	r8a66597_hub_status_data,
	.hub_control =		r8a66597_hub_control,
	.bus_suspend =		r8a66597_bus_suspend,
	.bus_resume =		r8a66597_bus_resume,
};

#if defined(CONFIG_PM)
static int r8a66597_suspend(struct platform_device *pdev, pm_message_t state)
{
	struct r8a66597		*r8a66597 = dev_get_drvdata(&pdev->dev);
	int port;

	dbg("%s", __func__);

	disable_controller(r8a66597);

	for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) {
		struct r8a66597_root_hub *rh = &r8a66597->root_hub[port];

		rh->port = 0x00000000;
	}

	return 0;
}

static int r8a66597_resume(struct platform_device *pdev)
{
	struct r8a66597		*r8a66597 = dev_get_drvdata(&pdev->dev);
	struct usb_hcd		*hcd = r8a66597_to_hcd(r8a66597);

	dbg("%s", __func__);

	enable_controller(r8a66597);
	usb_root_hub_lost_power(hcd->self.root_hub);

	return 0;
}
#else	/* if defined(CONFIG_PM) */
+2 −0
Original line number Diff line number Diff line
@@ -504,6 +504,8 @@ struct r8a66597 {

	struct list_head child_device;
	unsigned long child_connect_map[4];

	unsigned bus_suspended:1;
};

static inline struct r8a66597 *hcd_to_r8a66597(struct usb_hcd *hcd)