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

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
...@@ -67,16 +67,6 @@ ...@@ -67,16 +67,6 @@
#define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" #define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin"
#define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.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 brcmf_usb_image {
struct list_head list; struct list_head list;
s8 *fwname; s8 *fwname;
...@@ -97,10 +87,8 @@ struct brcmf_usbdev_info { ...@@ -97,10 +87,8 @@ struct brcmf_usbdev_info {
struct list_head rx_postq; struct list_head rx_postq;
struct list_head tx_freeq; struct list_head tx_freeq;
struct list_head tx_postq; struct list_head tx_postq;
enum usbdev_suspend_state suspend_state;
uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2;
bool activity;
int rx_low_watermark; int rx_low_watermark;
int tx_low_watermark; int tx_low_watermark;
int tx_high_watermark; int tx_high_watermark;
...@@ -213,11 +201,6 @@ brcmf_usb_ctlwrite_complete(struct urb *urb) ...@@ -213,11 +201,6 @@ brcmf_usb_ctlwrite_complete(struct urb *urb)
urb->status); urb->status);
} }
static int brcmf_usb_pnp(struct brcmf_usbdev_info *devinfo, uint state)
{
return 0;
}
static int static int
brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) 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) ...@@ -229,14 +212,6 @@ brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len)
len == 0 || devinfo->ctl_urb == NULL) len == 0 || devinfo->ctl_urb == NULL)
return -EINVAL; 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; size = len;
devinfo->ctl_write.wLength = cpu_to_le16p(&size); devinfo->ctl_write.wLength = cpu_to_le16p(&size);
devinfo->ctl_urb->transfer_buffer_length = 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) ...@@ -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); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
/* TODO: handle suspend/resume */
return -EIO; return -EIO;
}
if (test_and_set_bit(0, &devinfo->ctl_op)) if (test_and_set_bit(0, &devinfo->ctl_op))
return -EIO; return -EIO;
...@@ -330,10 +303,9 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) ...@@ -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); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
/* TODO: handle suspend/resume */
return -EIO; return -EIO;
}
if (test_and_set_bit(0, &devinfo->ctl_op)) if (test_and_set_bit(0, &devinfo->ctl_op))
return -EIO; return -EIO;
...@@ -499,7 +471,7 @@ static void brcmf_usb_rx_complete(struct urb *urb) ...@@ -499,7 +471,7 @@ static void brcmf_usb_rx_complete(struct urb *urb)
return; 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); skb_put(skb, urb->actual_length);
if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) { if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) {
brcmf_dbg(ERROR, "rx protocol error\n"); brcmf_dbg(ERROR, "rx protocol error\n");
...@@ -552,8 +524,8 @@ static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo) ...@@ -552,8 +524,8 @@ static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo)
{ {
struct brcmf_usbreq *req; struct brcmf_usbreq *req;
if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
brcmf_dbg(ERROR, "bus is not up\n"); brcmf_dbg(ERROR, "bus is not up=%d\n", devinfo->bus_pub.state);
return; return;
} }
while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL) 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) ...@@ -573,20 +545,15 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state)
return; return;
old_state = devinfo->bus_pub.state; 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; 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 */ /* update state of upper layer */
if (state == BCMFMAC_USB_STATE_DOWN) { if (state == BRCMFMAC_USB_STATE_DOWN) {
brcmf_dbg(USB, "DBUS is down\n"); brcmf_dbg(USB, "DBUS is down\n");
bcmf_bus->state = BRCMF_BUS_DOWN; 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 { } else {
brcmf_dbg(USB, "DBUS current state=%d\n", state); brcmf_dbg(USB, "DBUS current state=%d\n", state);
} }
...@@ -597,7 +564,7 @@ brcmf_usb_intr_complete(struct urb *urb) ...@@ -597,7 +564,7 @@ brcmf_usb_intr_complete(struct urb *urb)
{ {
struct brcmf_usbdev_info *devinfo = struct brcmf_usbdev_info *devinfo =
(struct brcmf_usbdev_info *)urb->context; (struct brcmf_usbdev_info *)urb->context;
bool killed; int err;
brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status);
...@@ -605,24 +572,24 @@ brcmf_usb_intr_complete(struct urb *urb) ...@@ -605,24 +572,24 @@ brcmf_usb_intr_complete(struct urb *urb)
return; return;
if (unlikely(urb->status)) { if (unlikely(urb->status)) {
if (devinfo->suspend_state == if (urb->status == -ENOENT ||
USBOS_SUSPEND_STATE_SUSPEND_PENDING) urb->status == -ESHUTDOWN ||
killed = true;
if ((urb->status == -ENOENT && (!killed))
|| urb->status == -ESHUTDOWN ||
urb->status == -ENODEV) { 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"); brcmf_dbg(ERROR, "intr cb when DBUS down, ignoring\n");
return; return;
} }
if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); 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) 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) ...@@ -632,10 +599,8 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
int ret; int ret;
brcmf_dbg(USB, "Enter, skb=%p\n", skb); brcmf_dbg(USB, "Enter, skb=%p\n", skb);
if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
/* TODO: handle suspend/resume */
return -EIO; return -EIO;
}
req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq, req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq,
&devinfo->tx_freecount); &devinfo->tx_freecount);
...@@ -675,26 +640,16 @@ static int brcmf_usb_up(struct device *dev) ...@@ -675,26 +640,16 @@ static int brcmf_usb_up(struct device *dev)
{ {
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
u16 ifnum; u16 ifnum;
int ret;
brcmf_dbg(USB, "Enter\n"); 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; 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 */ /* 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) { if (devinfo->intr_urb) {
int ret;
usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev, usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev,
devinfo->intr_pipe, devinfo->intr_pipe,
&devinfo->intr, &devinfo->intr,
...@@ -743,10 +698,10 @@ static void brcmf_usb_down(struct device *dev) ...@@ -743,10 +698,10 @@ static void brcmf_usb_down(struct device *dev)
if (devinfo == NULL) if (devinfo == NULL)
return; return;
if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN) if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN)
return; return;
brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN); brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN);
if (devinfo->intr_urb) if (devinfo->intr_urb)
usb_kill_urb(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) ...@@ -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); err = brcmf_usb_dl_writeimage(devinfo, fw, len);
if (err == 0) if (err == 0)
devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_DONE; devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE;
else 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); brcmf_dbg(USB, "Exit, err=%d\n", err);
return err; return err;
...@@ -1221,6 +1176,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, ...@@ -1221,6 +1176,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
devinfo->rx_low_watermark = nrxq / 2; devinfo->rx_low_watermark = nrxq / 2;
devinfo->bus_pub.devinfo = devinfo; devinfo->bus_pub.devinfo = devinfo;
devinfo->bus_pub.ntxq = ntxq; devinfo->bus_pub.ntxq = ntxq;
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN;
/* flow control when too many tx urbs posted */ /* flow control when too many tx urbs posted */
devinfo->tx_low_watermark = ntxq / 4; devinfo->tx_low_watermark = ntxq / 4;
...@@ -1491,7 +1447,7 @@ brcmf_usb_disconnect(struct usb_interface *intf) ...@@ -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) 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) ...@@ -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); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
devinfo->bus_pub.state = BCMFMAC_USB_STATE_DOWN; devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
devinfo->suspend_state = USBOS_SUSPEND_STATE_SUSPENDED; brcmf_detach(&usb->dev);
return 0; return 0;
} }
/* /*
* mark suspend state active and crank up the bus. * (re-) start the bus.
*/ */
static int brcmf_usb_resume(struct usb_interface *intf) static int brcmf_usb_resume(struct usb_interface *intf)
{ {
...@@ -1513,11 +1469,25 @@ 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); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
devinfo->suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; if (!brcmf_attach(0, devinfo->dev))
brcmf_bus_start(&usb->dev); return brcmf_bus_start(&usb->dev);
return 0; 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_VENDOR_ID_BROADCOM 0x0a5c
#define BRCMF_USB_DEVICE_ID_43143 0xbd1e #define BRCMF_USB_DEVICE_ID_43143 0xbd1e
#define BRCMF_USB_DEVICE_ID_43236 0xbd17 #define BRCMF_USB_DEVICE_ID_43236 0xbd17
...@@ -1537,7 +1507,6 @@ MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); ...@@ -1537,7 +1507,6 @@ MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME);
MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME);
/* TODO: suspend and resume entries */
static struct usb_driver brcmf_usbdrvr = { static struct usb_driver brcmf_usbdrvr = {
.name = KBUILD_MODNAME, .name = KBUILD_MODNAME,
.probe = brcmf_usb_probe, .probe = brcmf_usb_probe,
...@@ -1545,6 +1514,7 @@ static struct usb_driver brcmf_usbdrvr = { ...@@ -1545,6 +1514,7 @@ static struct usb_driver brcmf_usbdrvr = {
.id_table = brcmf_usb_devid_table, .id_table = brcmf_usb_devid_table,
.suspend = brcmf_usb_suspend, .suspend = brcmf_usb_suspend,
.resume = brcmf_usb_resume, .resume = brcmf_usb_resume,
.reset_resume = brcmf_usb_reset_resume,
.supports_autosuspend = 1, .supports_autosuspend = 1,
.disable_hub_initiated_lpm = 1, .disable_hub_initiated_lpm = 1,
}; };
......
...@@ -17,19 +17,11 @@ ...@@ -17,19 +17,11 @@
#define BRCMFMAC_USB_H #define BRCMFMAC_USB_H
enum brcmf_usb_state { enum brcmf_usb_state {
BCMFMAC_USB_STATE_DL_PENDING, BRCMFMAC_USB_STATE_DOWN,
BCMFMAC_USB_STATE_DL_DONE, BRCMFMAC_USB_STATE_DL_FAIL,
BCMFMAC_USB_STATE_UP, BRCMFMAC_USB_STATE_DL_DONE,
BCMFMAC_USB_STATE_DOWN, BRCMFMAC_USB_STATE_UP,
BCMFMAC_USB_STATE_PNP_FWDL, BRCMFMAC_USB_STATE_SLEEP
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,
}; };
struct brcmf_stats { struct brcmf_stats {
......
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