|
@@ -165,6 +165,8 @@ enum brcmf_pcie_state {
|
|
|
|
|
|
|
|
#define BRCMF_H2D_HOST_D3_INFORM 0x00000001
|
|
#define BRCMF_H2D_HOST_D3_INFORM 0x00000001
|
|
|
#define BRCMF_H2D_HOST_DS_ACK 0x00000002
|
|
#define BRCMF_H2D_HOST_DS_ACK 0x00000002
|
|
|
|
|
+#define BRCMF_H2D_HOST_D0_INFORM_IN_USE 0x00000008
|
|
|
|
|
+#define BRCMF_H2D_HOST_D0_INFORM 0x00000010
|
|
|
|
|
|
|
|
#define BRCMF_PCIE_MBDATA_TIMEOUT 2000
|
|
#define BRCMF_PCIE_MBDATA_TIMEOUT 2000
|
|
|
|
|
|
|
@@ -243,6 +245,7 @@ struct brcmf_pciedev_info {
|
|
|
wait_queue_head_t mbdata_resp_wait;
|
|
wait_queue_head_t mbdata_resp_wait;
|
|
|
bool mbdata_completed;
|
|
bool mbdata_completed;
|
|
|
bool irq_allocated;
|
|
bool irq_allocated;
|
|
|
|
|
+ bool wowl_enabled;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
struct brcmf_pcie_ringbuf {
|
|
struct brcmf_pcie_ringbuf {
|
|
@@ -537,7 +540,7 @@ static int brcmf_pcie_exit_download_state(struct brcmf_pciedev_info *devinfo,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-static void
|
|
|
|
|
|
|
+static int
|
|
|
brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
|
|
brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
|
|
|
{
|
|
{
|
|
|
struct brcmf_pcie_shared_info *shared;
|
|
struct brcmf_pcie_shared_info *shared;
|
|
@@ -558,13 +561,15 @@ brcmf_pcie_send_mb_data(struct brcmf_pciedev_info *devinfo, u32 htod_mb_data)
|
|
|
msleep(10);
|
|
msleep(10);
|
|
|
i++;
|
|
i++;
|
|
|
if (i > 100)
|
|
if (i > 100)
|
|
|
- break;
|
|
|
|
|
|
|
+ return -EIO;
|
|
|
cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
|
|
cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
|
|
brcmf_pcie_write_tcm32(devinfo, addr, htod_mb_data);
|
|
|
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
|
|
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
|
|
|
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
|
|
pci_write_config_dword(devinfo->pdev, BRCMF_PCIE_REG_SBMBX, 1);
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1229,11 +1234,27 @@ static int brcmf_pcie_rx_ctlpkt(struct device *dev, unsigned char *msg,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+static void brcmf_pcie_wowl_config(struct device *dev, bool enabled)
|
|
|
|
|
+{
|
|
|
|
|
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
|
|
|
|
|
+ struct brcmf_pciedev *buspub = bus_if->bus_priv.pcie;
|
|
|
|
|
+ struct brcmf_pciedev_info *devinfo = buspub->devinfo;
|
|
|
|
|
+
|
|
|
|
|
+ brcmf_dbg(PCIE, "Configuring WOWL, enabled=%d\n", enabled);
|
|
|
|
|
+ devinfo->wowl_enabled = enabled;
|
|
|
|
|
+ if (enabled)
|
|
|
|
|
+ device_set_wakeup_enable(&devinfo->pdev->dev, true);
|
|
|
|
|
+ else
|
|
|
|
|
+ device_set_wakeup_enable(&devinfo->pdev->dev, false);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
|
|
static struct brcmf_bus_ops brcmf_pcie_bus_ops = {
|
|
|
.txdata = brcmf_pcie_tx,
|
|
.txdata = brcmf_pcie_tx,
|
|
|
.stop = brcmf_pcie_down,
|
|
.stop = brcmf_pcie_down,
|
|
|
.txctl = brcmf_pcie_tx_ctlpkt,
|
|
.txctl = brcmf_pcie_tx_ctlpkt,
|
|
|
.rxctl = brcmf_pcie_rx_ctlpkt,
|
|
.rxctl = brcmf_pcie_rx_ctlpkt,
|
|
|
|
|
+ .wowl_config = brcmf_pcie_wowl_config,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1668,6 +1689,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
|
bus->ops = &brcmf_pcie_bus_ops;
|
|
bus->ops = &brcmf_pcie_bus_ops;
|
|
|
bus->proto_type = BRCMF_PROTO_MSGBUF;
|
|
bus->proto_type = BRCMF_PROTO_MSGBUF;
|
|
|
bus->chip = devinfo->coreid;
|
|
bus->chip = devinfo->coreid;
|
|
|
|
|
+ bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
|
|
|
dev_set_drvdata(&pdev->dev, bus);
|
|
dev_set_drvdata(&pdev->dev, bus);
|
|
|
|
|
|
|
|
ret = brcmf_pcie_get_fwnames(devinfo);
|
|
ret = brcmf_pcie_get_fwnames(devinfo);
|
|
@@ -1759,36 +1781,62 @@ static int brcmf_pcie_suspend(struct pci_dev *pdev, pm_message_t state)
|
|
|
brcmf_err("Timeout on response for entering D3 substate\n");
|
|
brcmf_err("Timeout on response for entering D3 substate\n");
|
|
|
return -EIO;
|
|
return -EIO;
|
|
|
}
|
|
}
|
|
|
- brcmf_pcie_release_irq(devinfo);
|
|
|
|
|
|
|
+ brcmf_pcie_send_mb_data(devinfo, BRCMF_H2D_HOST_D0_INFORM_IN_USE);
|
|
|
|
|
|
|
|
err = pci_save_state(pdev);
|
|
err = pci_save_state(pdev);
|
|
|
- if (err) {
|
|
|
|
|
|
|
+ if (err)
|
|
|
brcmf_err("pci_save_state failed, err=%d\n", err);
|
|
brcmf_err("pci_save_state failed, err=%d\n", err);
|
|
|
- return err;
|
|
|
|
|
|
|
+ if ((err) || (!devinfo->wowl_enabled)) {
|
|
|
|
|
+ brcmf_chip_detach(devinfo->ci);
|
|
|
|
|
+ devinfo->ci = NULL;
|
|
|
|
|
+ brcmf_pcie_remove(pdev);
|
|
|
|
|
+ return 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- brcmf_chip_detach(devinfo->ci);
|
|
|
|
|
- devinfo->ci = NULL;
|
|
|
|
|
-
|
|
|
|
|
- brcmf_pcie_remove(pdev);
|
|
|
|
|
-
|
|
|
|
|
return pci_prepare_to_sleep(pdev);
|
|
return pci_prepare_to_sleep(pdev);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-
|
|
|
|
|
static int brcmf_pcie_resume(struct pci_dev *pdev)
|
|
static int brcmf_pcie_resume(struct pci_dev *pdev)
|
|
|
{
|
|
{
|
|
|
|
|
+ struct brcmf_pciedev_info *devinfo;
|
|
|
|
|
+ struct brcmf_bus *bus;
|
|
|
int err;
|
|
int err;
|
|
|
|
|
|
|
|
- brcmf_dbg(PCIE, "Enter, pdev=%p\n", pdev);
|
|
|
|
|
|
|
+ bus = dev_get_drvdata(&pdev->dev);
|
|
|
|
|
+ brcmf_dbg(PCIE, "Enter, pdev=%p, bus=%p\n", pdev, bus);
|
|
|
|
|
|
|
|
err = pci_set_power_state(pdev, PCI_D0);
|
|
err = pci_set_power_state(pdev, PCI_D0);
|
|
|
if (err) {
|
|
if (err) {
|
|
|
brcmf_err("pci_set_power_state failed, err=%d\n", err);
|
|
brcmf_err("pci_set_power_state failed, err=%d\n", err);
|
|
|
- return err;
|
|
|
|
|
|
|
+ goto cleanup;
|
|
|
}
|
|
}
|
|
|
pci_restore_state(pdev);
|
|
pci_restore_state(pdev);
|
|
|
|
|
+ pci_enable_wake(pdev, PCI_D3hot, false);
|
|
|
|
|
+ pci_enable_wake(pdev, PCI_D3cold, false);
|
|
|
|
|
+
|
|
|
|
|
+ /* Check if device is still up and running, if so we are ready */
|
|
|
|
|
+ if (bus) {
|
|
|
|
|
+ devinfo = bus->bus_priv.pcie->devinfo;
|
|
|
|
|
+ if (brcmf_pcie_read_reg32(devinfo,
|
|
|
|
|
+ BRCMF_PCIE_PCIE2REG_INTMASK) != 0) {
|
|
|
|
|
+ if (brcmf_pcie_send_mb_data(devinfo,
|
|
|
|
|
+ BRCMF_H2D_HOST_D0_INFORM))
|
|
|
|
|
+ goto cleanup;
|
|
|
|
|
+ brcmf_dbg(PCIE, "Hot resume, continue....\n");
|
|
|
|
|
+ brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2);
|
|
|
|
|
+ brcmf_bus_change_state(bus, BRCMF_BUS_DATA);
|
|
|
|
|
+ brcmf_pcie_intr_enable(devinfo);
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+cleanup:
|
|
|
|
|
+ if (bus) {
|
|
|
|
|
+ devinfo = bus->bus_priv.pcie->devinfo;
|
|
|
|
|
+ brcmf_chip_detach(devinfo->ci);
|
|
|
|
|
+ devinfo->ci = NULL;
|
|
|
|
|
+ brcmf_pcie_remove(pdev);
|
|
|
|
|
+ }
|
|
|
err = brcmf_pcie_probe(pdev, NULL);
|
|
err = brcmf_pcie_probe(pdev, NULL);
|
|
|
if (err)
|
|
if (err)
|
|
|
brcmf_err("probe after resume failed, err=%d\n", err);
|
|
brcmf_err("probe after resume failed, err=%d\n", err);
|