summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c')
-rw-r--r--drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c198
1 files changed, 117 insertions, 81 deletions
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
index ac02244a6fdf..6a3f187320fc 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c
@@ -455,6 +455,11 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev,
if (sg_data_sz > max_req_sz - req_sz)
sg_data_sz = max_req_sz - req_sz;
+ if (!sgl) {
+ /* out of (pre-allocated) scatterlist entries */
+ ret = -ENOMEM;
+ goto exit;
+ }
sg_set_buf(sgl, pkt_data, sg_data_sz);
sg_cnt++;
@@ -770,7 +775,7 @@ void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
sdiodev->settings->bus.sdio.txglomsz);
- nents += (nents >> 4) + 1;
+ nents *= 2;
WARN_ON(nents > sdiodev->max_segment_count);
@@ -784,9 +789,11 @@ void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
sdiodev->txglomsz = sdiodev->settings->bus.sdio.txglomsz;
}
-#ifdef CONFIG_PM_SLEEP
static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
{
+ if (!IS_ENABLED(CONFIG_PM_SLEEP))
+ return 0;
+
sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL);
if (!sdiodev->freezer)
return -ENOMEM;
@@ -802,6 +809,7 @@ static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
if (sdiodev->freezer) {
WARN_ON(atomic_read(&sdiodev->freezer->freezing));
kfree(sdiodev->freezer);
+ sdiodev->freezer = NULL;
}
}
@@ -833,7 +841,8 @@ static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev)
bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
{
- return atomic_read(&sdiodev->freezer->freezing);
+ return IS_ENABLED(CONFIG_PM_SLEEP) &&
+ atomic_read(&sdiodev->freezer->freezing);
}
void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
@@ -847,23 +856,15 @@ void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
{
- atomic_inc(&sdiodev->freezer->thread_count);
+ if (IS_ENABLED(CONFIG_PM_SLEEP))
+ atomic_inc(&sdiodev->freezer->thread_count);
}
void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
{
- atomic_dec(&sdiodev->freezer->thread_count);
-}
-#else
-static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
-{
- return 0;
-}
-
-static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
-{
+ if (IS_ENABLED(CONFIG_PM_SLEEP))
+ atomic_dec(&sdiodev->freezer->thread_count);
}
-#endif /* CONFIG_PM_SLEEP */
int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
{
@@ -875,13 +876,9 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
brcmf_sdiod_freezer_detach(sdiodev);
- /* Disable Function 2 */
- sdio_claim_host(sdiodev->func2);
- sdio_disable_func(sdiodev->func2);
- sdio_release_host(sdiodev->func2);
-
- /* Disable Function 1 */
+ /* Disable functions 2 then 1. */
sdio_claim_host(sdiodev->func1);
+ sdio_disable_func(sdiodev->func2);
sdio_disable_func(sdiodev->func1);
sdio_release_host(sdiodev->func1);
@@ -911,7 +908,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
if (ret) {
brcmf_err("Failed to set F1 blocksize\n");
sdio_release_host(sdiodev->func1);
- goto out;
+ return ret;
}
switch (sdiodev->func2->device) {
case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
@@ -933,7 +930,7 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
if (ret) {
brcmf_err("Failed to set F2 blocksize\n");
sdio_release_host(sdiodev->func1);
- goto out;
+ return ret;
} else {
brcmf_dbg(SDIO, "set F2 blocksize to %d\n", f2_blksz);
}
@@ -955,8 +952,8 @@ int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
/* try to attach to the target device */
sdiodev->bus = brcmf_sdio_probe(sdiodev);
- if (!sdiodev->bus) {
- ret = -ENODEV;
+ if (IS_ERR(sdiodev->bus)) {
+ ret = PTR_ERR(sdiodev->bus);
goto out;
}
brcmf_sdiod_host_fixup(sdiodev->func2->card->host);
@@ -967,46 +964,77 @@ out:
return ret;
}
-#define BRCMF_SDIO_DEVICE(dev_id) \
- {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)}
+#define BRCMF_SDIO_DEVICE(dev_id, fw_vend) \
+ { \
+ SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id), \
+ .driver_data = BRCMF_FWVENDOR_ ## fw_vend \
+ }
+
+#define CYW_SDIO_DEVICE(dev_id, fw_vend) \
+ { \
+ SDIO_DEVICE(SDIO_VENDOR_ID_CYPRESS, dev_id), \
+ .driver_data = BRCMF_FWVENDOR_ ## fw_vend \
+ }
/* devices we support, null terminated */
static const struct sdio_device_id brcmf_sdmmc_ids[] = {
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752),
- BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43439, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43751, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43752, WCC),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373, CYW),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012, CYW),
+ BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359, CYW),
+ CYW_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439, CYW),
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
-static void brcmf_sdiod_acpi_set_power_manageable(struct device *dev,
- int val)
+static void brcmf_sdiod_acpi_save_power_manageable(struct brcmf_sdio_dev *sdiodev)
{
#if IS_ENABLED(CONFIG_ACPI)
struct acpi_device *adev;
- adev = ACPI_COMPANION(dev);
+ adev = ACPI_COMPANION(&sdiodev->func1->dev);
if (adev)
- adev->flags.power_manageable = 0;
+ sdiodev->func1_power_manageable = adev->flags.power_manageable;
+
+ adev = ACPI_COMPANION(&sdiodev->func2->dev);
+ if (adev)
+ sdiodev->func2_power_manageable = adev->flags.power_manageable;
+#endif
+}
+
+static void brcmf_sdiod_acpi_set_power_manageable(struct brcmf_sdio_dev *sdiodev,
+ int enable)
+{
+#if IS_ENABLED(CONFIG_ACPI)
+ struct acpi_device *adev;
+
+ adev = ACPI_COMPANION(&sdiodev->func1->dev);
+ if (adev)
+ adev->flags.power_manageable = enable ? sdiodev->func1_power_manageable : 0;
+
+ adev = ACPI_COMPANION(&sdiodev->func2->dev);
+ if (adev)
+ adev->flags.power_manageable = enable ? sdiodev->func2_power_manageable : 0;
#endif
}
@@ -1016,7 +1044,11 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
int err;
struct brcmf_sdio_dev *sdiodev;
struct brcmf_bus *bus_if;
- struct device *dev;
+
+ if (!id) {
+ dev_err(&func->dev, "Error no sdio_device_id passed for %x:%x\n", func->vendor, func->device);
+ return -ENODEV;
+ }
brcmf_dbg(SDIO, "Enter\n");
brcmf_dbg(SDIO, "Class=%x\n", func->class);
@@ -1024,14 +1056,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
brcmf_dbg(SDIO, "Function#: %d\n", func->num);
- dev = &func->dev;
-
/* Set MMC_QUIRK_LENIENT_FN0 for this card */
func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
- /* prohibit ACPI power management for this device */
- brcmf_sdiod_acpi_set_power_manageable(dev, 0);
-
/* Consume func num 1 but dont do anything with it. */
if (func->num == 1)
return 0;
@@ -1040,10 +1067,10 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
if (func->num != 2)
return -ENODEV;
- bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
+ bus_if = kzalloc(sizeof(*bus_if), GFP_KERNEL);
if (!bus_if)
return -ENOMEM;
- sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
+ sdiodev = kzalloc(sizeof(*sdiodev), GFP_KERNEL);
if (!sdiodev) {
kfree(bus_if);
return -ENOMEM;
@@ -1058,10 +1085,12 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func,
sdiodev->bus_if = bus_if;
bus_if->bus_priv.sdio = sdiodev;
bus_if->proto_type = BRCMF_PROTO_BCDC;
+ bus_if->fwvid = id->driver_data;
dev_set_drvdata(&func->dev, bus_if);
dev_set_drvdata(&sdiodev->func1->dev, bus_if);
sdiodev->dev = &sdiodev->func1->dev;
+ brcmf_sdiod_acpi_save_power_manageable(sdiodev);
brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
@@ -1119,18 +1148,32 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
{
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
+ mmc_pm_flag_t pm_caps = sdio_get_host_pm_caps(sdiodev->func1);
+
+ /* Power must be preserved to be able to support WOWL. */
+ if (!(pm_caps & MMC_PM_KEEP_POWER))
+ goto notsup;
+
+ if (sdiodev->settings->bus.sdio.oob_irq_supported ||
+ pm_caps & MMC_PM_WAKE_SDIO_IRQ) {
+ /* Stop ACPI from turning off the device when wowl is enabled */
+ brcmf_sdiod_acpi_set_power_manageable(sdiodev, !enabled);
+ sdiodev->wowl_enabled = enabled;
+ brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
+ return;
+ }
- brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
- sdiodev->wowl_enabled = enabled;
+notsup:
+ brcmf_dbg(SDIO, "WOWL not supported\n");
}
-#ifdef CONFIG_PM_SLEEP
static int brcmf_ops_sdio_suspend(struct device *dev)
{
struct sdio_func *func;
struct brcmf_bus *bus_if;
struct brcmf_sdio_dev *sdiodev;
- mmc_pm_flag_t pm_caps, sdio_flags;
+ mmc_pm_flag_t sdio_flags;
+ bool cap_power_off;
int ret = 0;
func = container_of(dev, struct sdio_func, dev);
@@ -1138,18 +1181,17 @@ static int brcmf_ops_sdio_suspend(struct device *dev)
if (func->num != 1)
return 0;
+ cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);
bus_if = dev_get_drvdata(dev);
sdiodev = bus_if->bus_priv.sdio;
- pm_caps = sdio_get_host_pm_caps(func);
-
- if (pm_caps & MMC_PM_KEEP_POWER) {
- /* preserve card power during suspend */
+ if (sdiodev->wowl_enabled || !cap_power_off) {
brcmf_sdiod_freezer_on(sdiodev);
brcmf_sdio_wd_timer(sdiodev->bus, 0);
sdio_flags = MMC_PM_KEEP_POWER;
+
if (sdiodev->wowl_enabled) {
if (sdiodev->settings->bus.sdio.oob_irq_supported)
enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
@@ -1176,21 +1218,20 @@ static int brcmf_ops_sdio_resume(struct device *dev)
struct brcmf_bus *bus_if = dev_get_drvdata(dev);
struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
struct sdio_func *func = container_of(dev, struct sdio_func, dev);
- mmc_pm_flag_t pm_caps = sdio_get_host_pm_caps(func);
int ret = 0;
+ bool cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);
brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
if (func->num != 2)
return 0;
- if (!(pm_caps & MMC_PM_KEEP_POWER)) {
+ if (!sdiodev->wowl_enabled && cap_power_off) {
/* bus was powered off and device removed, probe again */
ret = brcmf_sdiod_probe(sdiodev);
if (ret)
brcmf_err("Failed to probe device on resume\n");
} else {
- if (sdiodev->wowl_enabled &&
- sdiodev->settings->bus.sdio.oob_irq_supported)
+ if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
brcmf_sdiod_freezer_off(sdiodev);
@@ -1199,11 +1240,9 @@ static int brcmf_ops_sdio_resume(struct device *dev)
return ret;
}
-static const struct dev_pm_ops brcmf_sdio_pm_ops = {
- .suspend = brcmf_ops_sdio_suspend,
- .resume = brcmf_ops_sdio_resume,
-};
-#endif /* CONFIG_PM_SLEEP */
+static DEFINE_SIMPLE_DEV_PM_OPS(brcmf_sdio_pm_ops,
+ brcmf_ops_sdio_suspend,
+ brcmf_ops_sdio_resume);
static struct sdio_driver brcmf_sdmmc_driver = {
.probe = brcmf_ops_sdio_probe,
@@ -1211,10 +1250,7 @@ static struct sdio_driver brcmf_sdmmc_driver = {
.name = KBUILD_MODNAME,
.id_table = brcmf_sdmmc_ids,
.drv = {
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM_SLEEP
- .pm = &brcmf_sdio_pm_ops,
-#endif /* CONFIG_PM_SLEEP */
+ .pm = pm_sleep_ptr(&brcmf_sdio_pm_ops),
.coredump = brcmf_dev_coredump,
},
};