Commit 7f1d4230 authored by Wright Feng's avatar Wright Feng Committed by Kalle Valo

brcmfmac: add USB autosuspend feature support

We add enable dynamic suspend (autosuspend) support in host driver, and
it can let platform cut down idle power consumption.
To support autosuspend feature in host driver, kernel need to be built
with CONFIG_USB_SUSPEND and autosuspend need to be turn on.
And we also replace wowl feature with adding "needs_remote_wakeup", so
that host still can be waken by wireless device.
Signed-off-by: default avatarWright Feng <wright.feng@cypress.com>
Signed-off-by: default avatarChi-Hsien Lin <chi-hsien.lin@cypress.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/1585124429-97371-6-git-send-email-chi-hsien.lin@cypress.com
parent 2bc50d88
...@@ -164,7 +164,6 @@ struct brcmf_usbdev_info { ...@@ -164,7 +164,6 @@ struct brcmf_usbdev_info {
struct urb *bulk_urb; /* used for FW download */ struct urb *bulk_urb; /* used for FW download */
bool wowl_enabled;
struct brcmf_mp_device *settings; struct brcmf_mp_device *settings;
}; };
...@@ -312,28 +311,43 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len) ...@@ -312,28 +311,43 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len)
int err = 0; int err = 0;
int timeout = 0; int timeout = 0;
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
struct usb_interface *intf = to_usb_interface(dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
return -EIO;
if (test_and_set_bit(0, &devinfo->ctl_op)) err = usb_autopm_get_interface(intf);
return -EIO; if (err)
goto out;
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
err = -EIO;
goto fail;
}
if (test_and_set_bit(0, &devinfo->ctl_op)) {
err = -EIO;
goto fail;
}
devinfo->ctl_completed = false; devinfo->ctl_completed = false;
err = brcmf_usb_send_ctl(devinfo, buf, len); err = brcmf_usb_send_ctl(devinfo, buf, len);
if (err) { if (err) {
brcmf_err("fail %d bytes: %d\n", err, len); brcmf_err("fail %d bytes: %d\n", err, len);
clear_bit(0, &devinfo->ctl_op); clear_bit(0, &devinfo->ctl_op);
return err; goto fail;
} }
timeout = brcmf_usb_ioctl_resp_wait(devinfo); timeout = brcmf_usb_ioctl_resp_wait(devinfo);
if (!timeout) { if (!timeout) {
brcmf_err("Txctl wait timed out\n"); brcmf_err("Txctl wait timed out\n");
usb_kill_urb(devinfo->ctl_urb); usb_kill_urb(devinfo->ctl_urb);
err = -EIO; err = -EIO;
goto fail;
} }
clear_bit(0, &devinfo->ctl_op); clear_bit(0, &devinfo->ctl_op);
fail:
usb_autopm_put_interface(intf);
out:
return err; return err;
} }
...@@ -342,20 +356,30 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) ...@@ -342,20 +356,30 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
int err = 0; int err = 0;
int timeout = 0; int timeout = 0;
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
struct usb_interface *intf = to_usb_interface(dev);
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP)
return -EIO;
if (test_and_set_bit(0, &devinfo->ctl_op)) err = usb_autopm_get_interface(intf);
return -EIO; if (err)
goto out;
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
err = -EIO;
goto fail;
}
if (test_and_set_bit(0, &devinfo->ctl_op)) {
err = -EIO;
goto fail;
}
devinfo->ctl_completed = false; devinfo->ctl_completed = false;
err = brcmf_usb_recv_ctl(devinfo, buf, len); err = brcmf_usb_recv_ctl(devinfo, buf, len);
if (err) { if (err) {
brcmf_err("fail %d bytes: %d\n", err, len); brcmf_err("fail %d bytes: %d\n", err, len);
clear_bit(0, &devinfo->ctl_op); clear_bit(0, &devinfo->ctl_op);
return err; goto fail;
} }
timeout = brcmf_usb_ioctl_resp_wait(devinfo); timeout = brcmf_usb_ioctl_resp_wait(devinfo);
err = devinfo->ctl_urb_status; err = devinfo->ctl_urb_status;
...@@ -363,11 +387,14 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) ...@@ -363,11 +387,14 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len)
brcmf_err("rxctl wait timed out\n"); brcmf_err("rxctl wait timed out\n");
usb_kill_urb(devinfo->ctl_urb); usb_kill_urb(devinfo->ctl_urb);
err = -EIO; err = -EIO;
goto fail;
} }
clear_bit(0, &devinfo->ctl_op); clear_bit(0, &devinfo->ctl_op);
fail:
usb_autopm_put_interface(intf);
if (!err) if (!err)
return devinfo->ctl_urb_actual_length; return devinfo->ctl_urb_actual_length;
else out:
return err; return err;
} }
...@@ -502,10 +529,12 @@ static void brcmf_usb_rx_complete(struct urb *urb) ...@@ -502,10 +529,12 @@ static void brcmf_usb_rx_complete(struct urb *urb)
return; return;
} }
if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP ||
devinfo->bus_pub.state == BRCMFMAC_USB_STATE_SLEEP) {
skb_put(skb, urb->actual_length); skb_put(skb, urb->actual_length);
brcmf_rx_frame(devinfo->dev, skb, true); brcmf_rx_frame(devinfo->dev, skb, true);
brcmf_usb_rx_refill(devinfo, req); brcmf_usb_rx_refill(devinfo, req);
usb_mark_last_busy(urb->dev);
} else { } else {
brcmu_pkt_buf_free_skb(skb); brcmu_pkt_buf_free_skb(skb);
brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL); brcmf_usb_enq(devinfo, &devinfo->rx_freeq, req, NULL);
...@@ -589,6 +618,11 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) ...@@ -589,6 +618,11 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
struct brcmf_usbreq *req; struct brcmf_usbreq *req;
int ret; int ret;
unsigned long flags; unsigned long flags;
struct usb_interface *intf = to_usb_interface(dev);
ret = usb_autopm_get_interface(intf);
if (ret)
goto out;
brcmf_dbg(USB, "Enter, skb=%p\n", skb); brcmf_dbg(USB, "Enter, skb=%p\n", skb);
if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) {
...@@ -627,9 +661,10 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) ...@@ -627,9 +661,10 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb)
devinfo->tx_flowblock = true; devinfo->tx_flowblock = true;
} }
spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags); spin_unlock_irqrestore(&devinfo->tx_flowblock_lock, flags);
return 0;
fail: fail:
usb_autopm_put_interface(intf);
out:
return ret; return ret;
} }
...@@ -993,20 +1028,32 @@ static int ...@@ -993,20 +1028,32 @@ static int
brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo)
{ {
int err; int err;
struct usb_interface *intf;
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
if (devinfo == NULL) if (!devinfo) {
return -ENODEV; err = -ENODEV;
goto out;
}
if (!devinfo->image) { if (!devinfo->image) {
brcmf_err("No firmware!\n"); brcmf_err("No firmware!\n");
return -ENOENT; err = -ENOENT;
goto out;
} }
intf = to_usb_interface(devinfo->dev);
err = usb_autopm_get_interface(intf);
if (err)
goto out;
err = brcmf_usb_dlstart(devinfo, err = brcmf_usb_dlstart(devinfo,
(u8 *)devinfo->image, devinfo->image_len); (u8 *)devinfo->image, devinfo->image_len);
if (err == 0) if (err == 0)
err = brcmf_usb_dlrun(devinfo); err = brcmf_usb_dlrun(devinfo);
usb_autopm_put_interface(intf);
out:
return err; return err;
} }
...@@ -1107,18 +1154,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, ...@@ -1107,18 +1154,6 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo,
return NULL; return NULL;
} }
static void brcmf_usb_wowl_config(struct device *dev, bool enabled)
{
struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev);
brcmf_dbg(USB, "Configuring WOWL, enabled=%d\n", enabled);
devinfo->wowl_enabled = enabled;
if (enabled)
device_set_wakeup_enable(devinfo->dev, true);
else
device_set_wakeup_enable(devinfo->dev, false);
}
static static
int brcmf_usb_get_fwname(struct device *dev, const char *ext, u8 *fw_name) int brcmf_usb_get_fwname(struct device *dev, const char *ext, u8 *fw_name)
{ {
...@@ -1145,7 +1180,6 @@ static const struct brcmf_bus_ops brcmf_usb_bus_ops = { ...@@ -1145,7 +1180,6 @@ static const struct brcmf_bus_ops brcmf_usb_bus_ops = {
.txdata = brcmf_usb_tx, .txdata = brcmf_usb_tx,
.txctl = brcmf_usb_tx_ctlpkt, .txctl = brcmf_usb_tx_ctlpkt,
.rxctl = brcmf_usb_rx_ctlpkt, .rxctl = brcmf_usb_rx_ctlpkt,
.wowl_config = brcmf_usb_wowl_config,
.get_fwname = brcmf_usb_get_fwname, .get_fwname = brcmf_usb_get_fwname,
}; };
...@@ -1334,6 +1368,8 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) ...@@ -1334,6 +1368,8 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
usb_set_intfdata(intf, devinfo); usb_set_intfdata(intf, devinfo);
intf->needs_remote_wakeup = 1;
/* Check that the device supports only one configuration */ /* Check that the device supports only one configuration */
if (usb->descriptor.bNumConfigurations != 1) { if (usb->descriptor.bNumConfigurations != 1) {
brcmf_err("Number of configurations: %d not supported\n", brcmf_err("Number of configurations: %d not supported\n",
...@@ -1447,12 +1483,8 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) ...@@ -1447,12 +1483,8 @@ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state)
brcmf_dbg(USB, "Enter\n"); brcmf_dbg(USB, "Enter\n");
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP;
if (devinfo->wowl_enabled) {
brcmf_cancel_all_urbs(devinfo); brcmf_cancel_all_urbs(devinfo);
} else { device_set_wakeup_enable(devinfo->dev, true);
brcmf_detach(&usb->dev);
brcmf_free(&usb->dev);
}
return 0; return 0;
} }
...@@ -1465,22 +1497,10 @@ static int brcmf_usb_resume(struct usb_interface *intf) ...@@ -1465,22 +1497,10 @@ 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");
if (!devinfo->wowl_enabled) {
int err;
err = brcmf_alloc(&usb->dev, devinfo->settings);
if (err)
return err;
err = brcmf_attach(devinfo->dev);
if (err) {
brcmf_free(devinfo->dev);
return err;
}
}
devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP; devinfo->bus_pub.state = BRCMFMAC_USB_STATE_UP;
brcmf_usb_rx_fill_all(devinfo); brcmf_usb_rx_fill_all(devinfo);
device_set_wakeup_enable(devinfo->dev, false);
return 0; return 0;
} }
...@@ -1537,6 +1557,7 @@ static struct usb_driver brcmf_usbdrvr = { ...@@ -1537,6 +1557,7 @@ static struct usb_driver brcmf_usbdrvr = {
.suspend = brcmf_usb_suspend, .suspend = brcmf_usb_suspend,
.resume = brcmf_usb_resume, .resume = brcmf_usb_resume,
.reset_resume = brcmf_usb_reset_resume, .reset_resume = brcmf_usb_reset_resume,
.supports_autosuspend = true,
.disable_hub_initiated_lpm = 1, .disable_hub_initiated_lpm = 1,
}; };
......
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