brcmfmac: usb suspend/resume.
Add support for usb suspend/resume. Reviewed-by: Arend Van Spriel <arend@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Franky Lin <frankyl@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
parent
83bc9c313d
commit
7c38e69825
|
@ -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);
|
||||
}
|
||||
devinfo->bus_pub.state = state;
|
||||
|
||||
/* 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 ||
|
||||
urb->status == -ENODEV) {
|
||||
brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN);
|
||||
if (urb->status == -ENOENT ||
|
||||
urb->status == -ESHUTDOWN ||
|
||||
urb->status == -ENODEV) {
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue