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

Commit 7c38e698 authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville
Browse files

brcmfmac: usb suspend/resume.



Add support for usb suspend/resume.

Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarFranky Lin <frankyl@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 83bc9c31
Loading
Loading
Loading
Loading
+53 −83
Original line number Diff line number Diff line
@@ -67,16 +67,6 @@
#define BRCMF_USB_43236_FW_NAME	"brcm/brcmfmac43236b.bin"
#define BRCMF_USB_43242_FW_NAME	"brcm/brcmfmac43242a.bin"

enum usbdev_suspend_state {
	USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow
						  suspend */
	USBOS_SUSPEND_STATE_SUSPEND_PENDING,	/* Device is idle, can be
						 * suspended. Wating PM to
						 * suspend the device
						 */
	USBOS_SUSPEND_STATE_SUSPENDED	/* Device suspended */
};

struct brcmf_usb_image {
	struct list_head list;
	s8 *fwname;
@@ -97,10 +87,8 @@ struct brcmf_usbdev_info {
	struct list_head rx_postq;
	struct list_head tx_freeq;
	struct list_head tx_postq;
	enum usbdev_suspend_state suspend_state;
	uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2;

	bool activity;
	int rx_low_watermark;
	int tx_low_watermark;
	int tx_high_watermark;
@@ -213,11 +201,6 @@ brcmf_usb_ctlwrite_complete(struct urb *urb)
		urb->status);
}

static int brcmf_usb_pnp(struct brcmf_usbdev_info *devinfo, uint state)
{
	return 0;
}

static int
brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
{
@@ -229,14 +212,6 @@ brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
	    len == 0 || devinfo->ctl_urb == NULL)
		return -EINVAL;

	/* If the USB/HSIC bus in sleep state, wake it up */
	if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPENDED)
		if (brcmf_usb_pnp(devinfo, BCMFMAC_USB_PNP_RESUME) != 0) {
			brcmf_dbg(ERROR, "Could not Resume the bus!\n");
			return -EIO;
		}

	devinfo->activity = true;
	size = len;
	devinfo->ctl_write.wLength = cpu_to_le16p(&size);
	devinfo->ctl_urb->transfer_buffer_length = size;
@@ -299,10 +274,8 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);

	brcmf_dbg(USB, "Enter\n");
	if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) {
		/* TODO: handle suspend/resume */
	if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
		return -EIO;
	}

	if (test_and_set_bit(0, &devinfo->ctl_op))
		return -EIO;
@@ -330,10 +303,9 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);

	brcmf_dbg(USB, "Enter\n");
	if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) {
		/* TODO: handle suspend/resume */
	if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
		return -EIO;
	}

	if (test_and_set_bit(0, &devinfo->ctl_op))
		return -EIO;

@@ -499,7 +471,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
		return;
	}

	if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) {
	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
		skb_put(skb, urb->actual_length);
		if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) {
			brcmf_dbg(ERROR, "rx protocol error\n");
@@ -552,8 +524,8 @@ static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo)
{
	struct brcmf_usbreq *req;

	if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) {
		brcmf_dbg(ERROR, "bus is not up\n");
	if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
		brcmf_dbg(ERROR, "bus is not up=%d\n", devinfo->bus_pub.state);
		return;
	}
	while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL)
@@ -573,20 +545,15 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
		return;

	old_state = devinfo->bus_pub.state;

	/* Don't update state if it's PnP firmware re-download */
	if (state != BCMFMAC_USB_STATE_PNP_FWDL) /* TODO */
	devinfo->bus_pub.state = state;

	if ((old_state  == BCMFMAC_USB_STATE_SLEEP)
		&& (state == BCMFMAC_USB_STATE_UP)) {
		brcmf_usb_rx_fill_all(devinfo);
	}

	/* update state of upper layer */
	if (state == BCMFMAC_USB_STATE_DOWN) {
	if (state == BRCMFMAC_USB_STATE_DOWN) {
		brcmf_dbg(USB, "DBUS is down\n");
		bcmf_bus->state = BRCMF_BUS_DOWN;
	} else if (state == BRCMFMAC_USB_STATE_UP) {
		brcmf_dbg(USB, "DBUS is up\n");
		bcmf_bus->state = BRCMF_BUS_DATA;
	} else {
		brcmf_dbg(USB, "DBUS current state=%d\n", state);
	}
@@ -597,7 +564,7 @@ brcmf_usb_intr_complete(struct urb *urb)
{
	struct brcmf_usbdev_info *devinfo =
			(struct brcmf_usbdev_info *)urb->context;
	bool killed;
	int err;

	brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);

@@ -605,24 +572,24 @@ brcmf_usb_intr_complete(struct urb *urb)
		return;

	if (unlikely(urb->status)) {
		if (devinfo->suspend_state ==
			USBOS_SUSPEND_STATE_SUSPEND_PENDING)
			killed = true;

		if ((urb->status == -ENOENT && (!killed))
			|| urb->status == -ESHUTDOWN ||
		if (urb->status == -ENOENT ||
		    urb->status == -ESHUTDOWN ||
		    urb->status == -ENODEV) {
			brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN);
			brcmf_usb_state_change(devinfo,
					       BRCMFMAC_USB_STATE_DOWN);
		}
	}

	if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN) {
	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) {
		brcmf_dbg(ERROR, "intr cb when DBUS down, ignoring\n");
		return;
	}

	if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP)
		usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC);
	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
		err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC);
		if (err)
			brcmf_dbg(ERROR, "usb_submit_urb, err=%d\n", err);
	}
}

static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
@@ -632,10 +599,8 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
	int ret;

	brcmf_dbg(USB, "Enter, skb=%p\n", skb);
	if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) {
		/* TODO: handle suspend/resume */
	if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
		return -EIO;
	}

	req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq,
					&devinfo->tx_freecount);
@@ -675,26 +640,16 @@ static int brcmf_usb_up(struct device *dev)
{
	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
	u16 ifnum;
	int ret;

	brcmf_dbg(USB, "Enter\n");
	if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP)
	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP)
		return 0;

	/* If the USB/HSIC bus in sleep state, wake it up */
	if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPENDED) {
		if (brcmf_usb_pnp(devinfo, BCMFMAC_USB_PNP_RESUME) != 0) {
			brcmf_dbg(ERROR, "Could not Resume the bus!\n");
			return -EIO;
		}
	}
	devinfo->activity = true;

	/* Success, indicate devinfo is fully up */
	brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_UP);
	brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP);

	if (devinfo->intr_urb) {
		int ret;

		usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev,
			devinfo->intr_pipe,
			&devinfo->intr,
@@ -743,10 +698,10 @@ static void brcmf_usb_down(struct device *dev)
	if (devinfo == NULL)
		return;

	if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN)
	if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN)
		return;

	brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN);
	brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
	if (devinfo->intr_urb)
		usb_kill_urb(devinfo->intr_urb);

@@ -1006,9 +961,9 @@ static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len)

	err = brcmf_usb_dl_writeimage(devinfo, fw, len);
	if (err == 0)
		devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_DONE;
		devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE;
	else
		devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_PENDING;
		devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL;
	brcmf_dbg(USB, "Exit, err=%d\n", err);

	return err;
@@ -1221,6 +1176,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
	devinfo->rx_low_watermark = nrxq / 2;
	devinfo->bus_pub.devinfo = devinfo;
	devinfo->bus_pub.ntxq = ntxq;
	devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN;

	/* flow control when too many tx urbs posted */
	devinfo->tx_low_watermark = ntxq / 4;
@@ -1491,7 +1447,7 @@ brcmf_usb_disconnect(struct usb_interface *intf)
}

/*
 *	only need to signal the bus being down and update the suspend state.
 * only need to signal the bus being down and update the state.
 */
static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
{
@@ -1499,13 +1455,13 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);

	brcmf_dbg(USB, "Enter\n");
	devinfo->bus_pub.state = BCMFMAC_USB_STATE_DOWN;
	devinfo->suspend_state = USBOS_SUSPEND_STATE_SUSPENDED;
	devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
	brcmf_detach(&usb->dev);
	return 0;
}

/*
 *	mark suspend state active and crank up the bus.
 * (re-) start the bus.
 */
static int brcmf_usb_resume(struct usb_interface *intf)
{
@@ -1513,11 +1469,25 @@ static int brcmf_usb_resume(struct usb_interface *intf)
	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);

	brcmf_dbg(USB, "Enter\n");
	devinfo->suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE;
	brcmf_bus_start(&usb->dev);
	if (!brcmf_attach(0, devinfo->dev))
		return brcmf_bus_start(&usb->dev);

	return 0;
}

static int brcmf_usb_reset_resume(struct usb_interface *intf)
{
	struct usb_device *usb = interface_to_usbdev(intf);
	struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);

	brcmf_dbg(USB, "Enter\n");

	if (!brcmf_usb_fw_download(devinfo))
		return brcmf_usb_resume(intf);

	return -EIO;
}

#define BRCMF_USB_VENDOR_ID_BROADCOM	0x0a5c
#define BRCMF_USB_DEVICE_ID_43143	0xbd1e
#define BRCMF_USB_DEVICE_ID_43236	0xbd17
@@ -1537,7 +1507,6 @@ MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);

/* TODO: suspend and resume entries */
static struct usb_driver brcmf_usbdrvr = {
	.name = KBUILD_MODNAME,
	.probe = brcmf_usb_probe,
@@ -1545,6 +1514,7 @@ static struct usb_driver brcmf_usbdrvr = {
	.id_table = brcmf_usb_devid_table,
	.suspend = brcmf_usb_suspend,
	.resume = brcmf_usb_resume,
	.reset_resume = brcmf_usb_reset_resume,
	.supports_autosuspend = 1,
	.disable_hub_initiated_lpm = 1,
};
+5 −13
Original line number Diff line number Diff line
@@ -17,19 +17,11 @@
#define BRCMFMAC_USB_H

enum brcmf_usb_state {
	BCMFMAC_USB_STATE_DL_PENDING,
	BCMFMAC_USB_STATE_DL_DONE,
	BCMFMAC_USB_STATE_UP,
	BCMFMAC_USB_STATE_DOWN,
	BCMFMAC_USB_STATE_PNP_FWDL,
	BCMFMAC_USB_STATE_DISCONNECT,
	BCMFMAC_USB_STATE_SLEEP
};

enum brcmf_usb_pnp_state {
	BCMFMAC_USB_PNP_DISCONNECT,
	BCMFMAC_USB_PNP_SLEEP,
	BCMFMAC_USB_PNP_RESUME,
	BRCMFMAC_USB_STATE_DOWN,
	BRCMFMAC_USB_STATE_DL_FAIL,
	BRCMFMAC_USB_STATE_DL_DONE,
	BRCMFMAC_USB_STATE_UP,
	BRCMFMAC_USB_STATE_SLEEP
};

struct brcmf_stats {