xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c (revision b694e3c604e999343258c49e574abd7be012e726)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2010 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo /* ****************** SDIO CARD Interface Functions **************************/
605491d2cSKalle Valo 
705491d2cSKalle Valo #include <linux/types.h>
805491d2cSKalle Valo #include <linux/netdevice.h>
905491d2cSKalle Valo #include <linux/pci.h>
1005491d2cSKalle Valo #include <linux/pci_ids.h>
1105491d2cSKalle Valo #include <linux/sched.h>
1205491d2cSKalle Valo #include <linux/completion.h>
13282ccf6eSFlorian Westphal #include <linux/interrupt.h>
1405491d2cSKalle Valo #include <linux/scatterlist.h>
1505491d2cSKalle Valo #include <linux/mmc/sdio.h>
1605491d2cSKalle Valo #include <linux/mmc/core.h>
1705491d2cSKalle Valo #include <linux/mmc/sdio_func.h>
1805491d2cSKalle Valo #include <linux/mmc/card.h>
1905491d2cSKalle Valo #include <linux/mmc/host.h>
2005491d2cSKalle Valo #include <linux/pm_runtime.h>
2105491d2cSKalle Valo #include <linux/suspend.h>
2205491d2cSKalle Valo #include <linux/errno.h>
2305491d2cSKalle Valo #include <linux/module.h>
2405491d2cSKalle Valo #include <linux/acpi.h>
2505491d2cSKalle Valo #include <net/cfg80211.h>
2605491d2cSKalle Valo 
2705491d2cSKalle Valo #include <defs.h>
2805491d2cSKalle Valo #include <brcm_hw_ids.h>
2905491d2cSKalle Valo #include <brcmu_utils.h>
3005491d2cSKalle Valo #include <brcmu_wifi.h>
3105491d2cSKalle Valo #include <chipcommon.h>
3205491d2cSKalle Valo #include <soc.h>
3305491d2cSKalle Valo #include "chip.h"
3405491d2cSKalle Valo #include "bus.h"
3505491d2cSKalle Valo #include "debug.h"
3605491d2cSKalle Valo #include "sdio.h"
377d34b056SHante Meuleman #include "core.h"
387d34b056SHante Meuleman #include "common.h"
3905491d2cSKalle Valo 
4005491d2cSKalle Valo #define SDIOH_API_ACCESS_RETRY_LIMIT	2
4105491d2cSKalle Valo 
4205491d2cSKalle Valo #define DMA_ALIGN_MASK	0x03
4305491d2cSKalle Valo 
4405491d2cSKalle Valo #define SDIO_FUNC1_BLOCKSIZE		64
4505491d2cSKalle Valo #define SDIO_FUNC2_BLOCKSIZE		512
462a7621deSWright Feng #define SDIO_4373_FUNC2_BLOCKSIZE	256
472bee4127SFrank Kao #define SDIO_435X_FUNC2_BLOCKSIZE	256
48cc95fa81SDmitry Osipenko #define SDIO_4329_FUNC2_BLOCKSIZE	128
4905491d2cSKalle Valo /* Maximum milliseconds to wait for F2 to come up */
5005491d2cSKalle Valo #define SDIO_WAIT_F2RDY	3000
5105491d2cSKalle Valo 
5205491d2cSKalle Valo #define BRCMF_DEFAULT_RXGLOM_SIZE	32  /* max rx frames in glom chain */
5305491d2cSKalle Valo 
5405491d2cSKalle Valo struct brcmf_sdiod_freezer {
5505491d2cSKalle Valo 	atomic_t freezing;
5605491d2cSKalle Valo 	atomic_t thread_count;
5705491d2cSKalle Valo 	u32 frozen_count;
5805491d2cSKalle Valo 	wait_queue_head_t thread_freeze;
5905491d2cSKalle Valo 	struct completion resumed;
6005491d2cSKalle Valo };
6105491d2cSKalle Valo 
brcmf_sdiod_oob_irqhandler(int irq,void * dev_id)6205491d2cSKalle Valo static irqreturn_t brcmf_sdiod_oob_irqhandler(int irq, void *dev_id)
6305491d2cSKalle Valo {
6405491d2cSKalle Valo 	struct brcmf_bus *bus_if = dev_get_drvdata(dev_id);
6505491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
6605491d2cSKalle Valo 
6705491d2cSKalle Valo 	brcmf_dbg(INTR, "OOB intr triggered\n");
6805491d2cSKalle Valo 
6905491d2cSKalle Valo 	/* out-of-band interrupt is level-triggered which won't
7005491d2cSKalle Valo 	 * be cleared until dpc
7105491d2cSKalle Valo 	 */
7205491d2cSKalle Valo 	if (sdiodev->irq_en) {
7305491d2cSKalle Valo 		disable_irq_nosync(irq);
7405491d2cSKalle Valo 		sdiodev->irq_en = false;
7505491d2cSKalle Valo 	}
7605491d2cSKalle Valo 
77d067c0faSSebastian Andrzej Siewior 	brcmf_sdio_isr(sdiodev->bus, true);
7805491d2cSKalle Valo 
7905491d2cSKalle Valo 	return IRQ_HANDLED;
8005491d2cSKalle Valo }
8105491d2cSKalle Valo 
brcmf_sdiod_ib_irqhandler(struct sdio_func * func)8205491d2cSKalle Valo static void brcmf_sdiod_ib_irqhandler(struct sdio_func *func)
8305491d2cSKalle Valo {
8405491d2cSKalle Valo 	struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev);
8505491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
8605491d2cSKalle Valo 
8705491d2cSKalle Valo 	brcmf_dbg(INTR, "IB intr triggered\n");
8805491d2cSKalle Valo 
89d067c0faSSebastian Andrzej Siewior 	brcmf_sdio_isr(sdiodev->bus, false);
9005491d2cSKalle Valo }
9105491d2cSKalle Valo 
9205491d2cSKalle Valo /* dummy handler for SDIO function 2 interrupt */
brcmf_sdiod_dummy_irqhandler(struct sdio_func * func)9305491d2cSKalle Valo static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func)
9405491d2cSKalle Valo {
9505491d2cSKalle Valo }
9605491d2cSKalle Valo 
brcmf_sdiod_intr_register(struct brcmf_sdio_dev * sdiodev)9705491d2cSKalle Valo int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev)
9805491d2cSKalle Valo {
994d792895SHante Meuleman 	struct brcmfmac_sdio_pd *pdata;
10005491d2cSKalle Valo 	int ret = 0;
10105491d2cSKalle Valo 	u8 data;
10205491d2cSKalle Valo 	u32 addr, gpiocontrol;
10305491d2cSKalle Valo 
104af5b5e62SHante Meuleman 	pdata = &sdiodev->settings->bus.sdio;
105af5b5e62SHante Meuleman 	if (pdata->oob_irq_supported) {
10605491d2cSKalle Valo 		brcmf_dbg(SDIO, "Enter, register OOB IRQ %d\n",
1078ea56be0SHante Meuleman 			  pdata->oob_irq_nr);
1083f426c96SMichał Mirosław 		spin_lock_init(&sdiodev->irq_en_lock);
1093f426c96SMichał Mirosław 		sdiodev->irq_en = true;
1103f426c96SMichał Mirosław 
1118ea56be0SHante Meuleman 		ret = request_irq(pdata->oob_irq_nr, brcmf_sdiod_oob_irqhandler,
1128ea56be0SHante Meuleman 				  pdata->oob_irq_flags, "brcmf_oob_intr",
113c9aa7a91SArend Van Spriel 				  &sdiodev->func1->dev);
11405491d2cSKalle Valo 		if (ret != 0) {
11505491d2cSKalle Valo 			brcmf_err("request_irq failed %d\n", ret);
11605491d2cSKalle Valo 			return ret;
11705491d2cSKalle Valo 		}
11805491d2cSKalle Valo 		sdiodev->oob_irq_requested = true;
11905491d2cSKalle Valo 
1208ea56be0SHante Meuleman 		ret = enable_irq_wake(pdata->oob_irq_nr);
12105491d2cSKalle Valo 		if (ret != 0) {
12205491d2cSKalle Valo 			brcmf_err("enable_irq_wake failed %d\n", ret);
12305491d2cSKalle Valo 			return ret;
12405491d2cSKalle Valo 		}
125a32de68eSDmitry Osipenko 		disable_irq_wake(pdata->oob_irq_nr);
12605491d2cSKalle Valo 
127c9aa7a91SArend Van Spriel 		sdio_claim_host(sdiodev->func1);
12805491d2cSKalle Valo 
12905491d2cSKalle Valo 		if (sdiodev->bus_if->chip == BRCM_CC_43362_CHIP_ID) {
13005491d2cSKalle Valo 			/* assign GPIO to SDIO core */
1311ce050c1SArend van Spriel 			addr = brcmf_chip_enum_base(sdiodev->func1->device);
1321ce050c1SArend van Spriel 			addr = CORE_CC_REG(addr, gpiocontrol);
13371bd508dSIan Molton 			gpiocontrol = brcmf_sdiod_readl(sdiodev, addr, &ret);
13405491d2cSKalle Valo 			gpiocontrol |= 0x2;
13571bd508dSIan Molton 			brcmf_sdiod_writel(sdiodev, addr, gpiocontrol, &ret);
13605491d2cSKalle Valo 
13771bd508dSIan Molton 			brcmf_sdiod_writeb(sdiodev, SBSDIO_GPIO_SELECT,
13871bd508dSIan Molton 					   0xf, &ret);
13971bd508dSIan Molton 			brcmf_sdiod_writeb(sdiodev, SBSDIO_GPIO_OUT, 0, &ret);
14071bd508dSIan Molton 			brcmf_sdiod_writeb(sdiodev, SBSDIO_GPIO_EN, 0x2, &ret);
14105491d2cSKalle Valo 		}
14205491d2cSKalle Valo 
14305491d2cSKalle Valo 		/* must configure SDIO_CCCR_IENx to enable irq */
14471bd508dSIan Molton 		data = brcmf_sdiod_func0_rb(sdiodev, SDIO_CCCR_IENx, &ret);
1459c3438edSIan Molton 		data |= SDIO_CCCR_IEN_FUNC1 | SDIO_CCCR_IEN_FUNC2 |
1469c3438edSIan Molton 			SDIO_CCCR_IEN_FUNC0;
14771bd508dSIan Molton 		brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_IENx, data, &ret);
14805491d2cSKalle Valo 
14905491d2cSKalle Valo 		/* redirect, configure and enable io for interrupt signal */
150eeef8a5dSIan Molton 		data = SDIO_CCCR_BRCM_SEPINT_MASK | SDIO_CCCR_BRCM_SEPINT_OE;
1518ea56be0SHante Meuleman 		if (pdata->oob_irq_flags & IRQF_TRIGGER_HIGH)
152eeef8a5dSIan Molton 			data |= SDIO_CCCR_BRCM_SEPINT_ACT_HI;
15371bd508dSIan Molton 		brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_BRCM_SEPINT,
15471bd508dSIan Molton 				     data, &ret);
155c9aa7a91SArend Van Spriel 		sdio_release_host(sdiodev->func1);
15605491d2cSKalle Valo 	} else {
15705491d2cSKalle Valo 		brcmf_dbg(SDIO, "Entering\n");
158c9aa7a91SArend Van Spriel 		sdio_claim_host(sdiodev->func1);
159c9aa7a91SArend Van Spriel 		sdio_claim_irq(sdiodev->func1, brcmf_sdiod_ib_irqhandler);
160c9aa7a91SArend Van Spriel 		sdio_claim_irq(sdiodev->func2, brcmf_sdiod_dummy_irqhandler);
161c9aa7a91SArend Van Spriel 		sdio_release_host(sdiodev->func1);
162b88a2e80SChristian Daudt 		sdiodev->sd_irq_requested = true;
16305491d2cSKalle Valo 	}
16405491d2cSKalle Valo 
16505491d2cSKalle Valo 	return 0;
16605491d2cSKalle Valo }
16705491d2cSKalle Valo 
brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev * sdiodev)168b7467401SChristian Daudt void brcmf_sdiod_intr_unregister(struct brcmf_sdio_dev *sdiodev)
16905491d2cSKalle Valo {
170b88a2e80SChristian Daudt 
171b88a2e80SChristian Daudt 	brcmf_dbg(SDIO, "Entering oob=%d sd=%d\n",
172b88a2e80SChristian Daudt 		  sdiodev->oob_irq_requested,
173b88a2e80SChristian Daudt 		  sdiodev->sd_irq_requested);
174b88a2e80SChristian Daudt 
175b88a2e80SChristian Daudt 	if (sdiodev->oob_irq_requested) {
1764d792895SHante Meuleman 		struct brcmfmac_sdio_pd *pdata;
1778ea56be0SHante Meuleman 
178af5b5e62SHante Meuleman 		pdata = &sdiodev->settings->bus.sdio;
179c9aa7a91SArend Van Spriel 		sdio_claim_host(sdiodev->func1);
18071bd508dSIan Molton 		brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL);
18171bd508dSIan Molton 		brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_IENx, 0, NULL);
182c9aa7a91SArend Van Spriel 		sdio_release_host(sdiodev->func1);
18305491d2cSKalle Valo 
18405491d2cSKalle Valo 		sdiodev->oob_irq_requested = false;
185c9aa7a91SArend Van Spriel 		free_irq(pdata->oob_irq_nr, &sdiodev->func1->dev);
18605491d2cSKalle Valo 		sdiodev->irq_en = false;
187b7467401SChristian Daudt 		sdiodev->oob_irq_requested = false;
18805491d2cSKalle Valo 	}
189b88a2e80SChristian Daudt 
190b88a2e80SChristian Daudt 	if (sdiodev->sd_irq_requested) {
191c9aa7a91SArend Van Spriel 		sdio_claim_host(sdiodev->func1);
192c9aa7a91SArend Van Spriel 		sdio_release_irq(sdiodev->func2);
193c9aa7a91SArend Van Spriel 		sdio_release_irq(sdiodev->func1);
194c9aa7a91SArend Van Spriel 		sdio_release_host(sdiodev->func1);
195b7467401SChristian Daudt 		sdiodev->sd_irq_requested = false;
19605491d2cSKalle Valo 	}
19705491d2cSKalle Valo }
19805491d2cSKalle Valo 
brcmf_sdiod_change_state(struct brcmf_sdio_dev * sdiodev,enum brcmf_sdiod_state state)19905491d2cSKalle Valo void brcmf_sdiod_change_state(struct brcmf_sdio_dev *sdiodev,
20005491d2cSKalle Valo 			      enum brcmf_sdiod_state state)
20105491d2cSKalle Valo {
20205491d2cSKalle Valo 	if (sdiodev->state == BRCMF_SDIOD_NOMEDIUM ||
20305491d2cSKalle Valo 	    state == sdiodev->state)
20405491d2cSKalle Valo 		return;
20505491d2cSKalle Valo 
20605491d2cSKalle Valo 	brcmf_dbg(TRACE, "%d -> %d\n", sdiodev->state, state);
20705491d2cSKalle Valo 	switch (sdiodev->state) {
20805491d2cSKalle Valo 	case BRCMF_SDIOD_DATA:
20905491d2cSKalle Valo 		/* any other state means bus interface is down */
21005491d2cSKalle Valo 		brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN);
21105491d2cSKalle Valo 		break;
21205491d2cSKalle Valo 	case BRCMF_SDIOD_DOWN:
21305491d2cSKalle Valo 		/* transition from DOWN to DATA means bus interface is up */
21405491d2cSKalle Valo 		if (state == BRCMF_SDIOD_DATA)
21505491d2cSKalle Valo 			brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_UP);
21605491d2cSKalle Valo 		break;
21705491d2cSKalle Valo 	default:
21805491d2cSKalle Valo 		break;
21905491d2cSKalle Valo 	}
22005491d2cSKalle Valo 	sdiodev->state = state;
22105491d2cSKalle Valo }
22205491d2cSKalle Valo 
brcmf_sdiod_set_backplane_window(struct brcmf_sdio_dev * sdiodev,u32 addr)223a7c3aa15SIan Molton static int brcmf_sdiod_set_backplane_window(struct brcmf_sdio_dev *sdiodev,
224a7c3aa15SIan Molton 					    u32 addr)
225993a98a4SIan Molton {
226a7c3aa15SIan Molton 	u32 v, bar0 = addr & SBSDIO_SBWINDOW_MASK;
227993a98a4SIan Molton 	int err = 0, i;
228993a98a4SIan Molton 
229a7c3aa15SIan Molton 	if (bar0 == sdiodev->sbwad)
230993a98a4SIan Molton 		return 0;
231a7c3aa15SIan Molton 
232a7c3aa15SIan Molton 	v = bar0 >> 8;
233a7c3aa15SIan Molton 
234a7c3aa15SIan Molton 	for (i = 0 ; i < 3 && !err ; i++, v >>= 8)
235a7c3aa15SIan Molton 		brcmf_sdiod_writeb(sdiodev, SBSDIO_FUNC1_SBADDRLOW + i,
236a7c3aa15SIan Molton 				   v & 0xff, &err);
237a7c3aa15SIan Molton 
238a7c3aa15SIan Molton 	if (!err)
239a7c3aa15SIan Molton 		sdiodev->sbwad = bar0;
240a7c3aa15SIan Molton 
241a7c3aa15SIan Molton 	return err;
242993a98a4SIan Molton }
243993a98a4SIan Molton 
brcmf_sdiod_readl(struct brcmf_sdio_dev * sdiodev,u32 addr,int * ret)24471bd508dSIan Molton u32 brcmf_sdiod_readl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret)
24505491d2cSKalle Valo {
24671bd508dSIan Molton 	u32 data = 0;
24705491d2cSKalle Valo 	int retval;
24805491d2cSKalle Valo 
249a7c3aa15SIan Molton 	retval = brcmf_sdiod_set_backplane_window(sdiodev, addr);
250a7c3aa15SIan Molton 	if (retval)
251a7c3aa15SIan Molton 		goto out;
25271bd508dSIan Molton 
253a7c3aa15SIan Molton 	addr &= SBSDIO_SB_OFT_ADDR_MASK;
254a7c3aa15SIan Molton 	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
255a7c3aa15SIan Molton 
256c9aa7a91SArend Van Spriel 	data = sdio_readl(sdiodev->func1, addr, &retval);
25705491d2cSKalle Valo 
258a7c3aa15SIan Molton out:
25905491d2cSKalle Valo 	if (ret)
26005491d2cSKalle Valo 		*ret = retval;
26105491d2cSKalle Valo 
26205491d2cSKalle Valo 	return data;
26305491d2cSKalle Valo }
26405491d2cSKalle Valo 
brcmf_sdiod_writel(struct brcmf_sdio_dev * sdiodev,u32 addr,u32 data,int * ret)26571bd508dSIan Molton void brcmf_sdiod_writel(struct brcmf_sdio_dev *sdiodev, u32 addr,
26605491d2cSKalle Valo 			u32 data, int *ret)
26705491d2cSKalle Valo {
26805491d2cSKalle Valo 	int retval;
26905491d2cSKalle Valo 
270a7c3aa15SIan Molton 	retval = brcmf_sdiod_set_backplane_window(sdiodev, addr);
271a7c3aa15SIan Molton 	if (retval)
272a7c3aa15SIan Molton 		goto out;
27371bd508dSIan Molton 
274a7c3aa15SIan Molton 	addr &= SBSDIO_SB_OFT_ADDR_MASK;
275a7c3aa15SIan Molton 	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
276a7c3aa15SIan Molton 
277c9aa7a91SArend Van Spriel 	sdio_writel(sdiodev->func1, data, addr, &retval);
27805491d2cSKalle Valo 
279a7c3aa15SIan Molton out:
28005491d2cSKalle Valo 	if (ret)
28105491d2cSKalle Valo 		*ret = retval;
28205491d2cSKalle Valo }
28305491d2cSKalle Valo 
brcmf_sdiod_skbuff_read(struct brcmf_sdio_dev * sdiodev,struct sdio_func * func,u32 addr,struct sk_buff * skb)284378f6a16SArend Van Spriel static int brcmf_sdiod_skbuff_read(struct brcmf_sdio_dev *sdiodev,
28500eb62cfSIan Molton 				   struct sdio_func *func, u32 addr,
286378f6a16SArend Van Spriel 				   struct sk_buff *skb)
28705491d2cSKalle Valo {
28805491d2cSKalle Valo 	unsigned int req_sz;
28905491d2cSKalle Valo 	int err;
29005491d2cSKalle Valo 
29105491d2cSKalle Valo 	/* Single skb use the standard mmc interface */
292378f6a16SArend Van Spriel 	req_sz = skb->len + 3;
29305491d2cSKalle Valo 	req_sz &= (uint)~3;
29405491d2cSKalle Valo 
29500eb62cfSIan Molton 	switch (func->num) {
29600eb62cfSIan Molton 	case 1:
297378f6a16SArend Van Spriel 		err = sdio_memcpy_fromio(func, ((u8 *)(skb->data)), addr,
29800eb62cfSIan Molton 					 req_sz);
29900eb62cfSIan Molton 		break;
30000eb62cfSIan Molton 	case 2:
301378f6a16SArend Van Spriel 		err = sdio_readsb(func, ((u8 *)(skb->data)), addr, req_sz);
30200eb62cfSIan Molton 		break;
30300eb62cfSIan Molton 	default:
30400eb62cfSIan Molton 		/* bail out as things are really fishy here */
30500eb62cfSIan Molton 		WARN(1, "invalid sdio function number: %d\n", func->num);
30600eb62cfSIan Molton 		err = -ENOMEDIUM;
307999eb686SYueHaibing 	}
3088f13c87cSIan Molton 
30905491d2cSKalle Valo 	if (err == -ENOMEDIUM)
31005491d2cSKalle Valo 		brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
3118f13c87cSIan Molton 
3128f13c87cSIan Molton 	return err;
3138f13c87cSIan Molton }
3148f13c87cSIan Molton 
brcmf_sdiod_skbuff_write(struct brcmf_sdio_dev * sdiodev,struct sdio_func * func,u32 addr,struct sk_buff * skb)315378f6a16SArend Van Spriel static int brcmf_sdiod_skbuff_write(struct brcmf_sdio_dev *sdiodev,
31600eb62cfSIan Molton 				    struct sdio_func *func, u32 addr,
317378f6a16SArend Van Spriel 				    struct sk_buff *skb)
3188f13c87cSIan Molton {
3198f13c87cSIan Molton 	unsigned int req_sz;
3208f13c87cSIan Molton 	int err;
3218f13c87cSIan Molton 
3228f13c87cSIan Molton 	/* Single skb use the standard mmc interface */
323378f6a16SArend Van Spriel 	req_sz = skb->len + 3;
3248f13c87cSIan Molton 	req_sz &= (uint)~3;
3258f13c87cSIan Molton 
326378f6a16SArend Van Spriel 	err = sdio_memcpy_toio(func, addr, ((u8 *)(skb->data)), req_sz);
3278f13c87cSIan Molton 
3288f13c87cSIan Molton 	if (err == -ENOMEDIUM)
3298f13c87cSIan Molton 		brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
3308f13c87cSIan Molton 
33105491d2cSKalle Valo 	return err;
33205491d2cSKalle Valo }
33305491d2cSKalle Valo 
mmc_submit_one(struct mmc_data * md,struct mmc_request * mr,struct mmc_command * mc,int sg_cnt,int req_sz,int func_blk_sz,u32 * addr,struct brcmf_sdio_dev * sdiodev,struct sdio_func * func,int write)3344a5a553dSDavid S. Miller static int mmc_submit_one(struct mmc_data *md, struct mmc_request *mr,
3354a5a553dSDavid S. Miller 			  struct mmc_command *mc, int sg_cnt, int req_sz,
3364a5a553dSDavid S. Miller 			  int func_blk_sz, u32 *addr,
3374a5a553dSDavid S. Miller 			  struct brcmf_sdio_dev *sdiodev,
3384a5a553dSDavid S. Miller 			  struct sdio_func *func, int write)
3394a5a553dSDavid S. Miller {
3404a5a553dSDavid S. Miller 	int ret;
3414a5a553dSDavid S. Miller 
3424a5a553dSDavid S. Miller 	md->sg_len = sg_cnt;
3434a5a553dSDavid S. Miller 	md->blocks = req_sz / func_blk_sz;
3444a5a553dSDavid S. Miller 	mc->arg |= (*addr & 0x1FFFF) << 9;	/* address */
3454a5a553dSDavid S. Miller 	mc->arg |= md->blocks & 0x1FF;	/* block count */
3464a5a553dSDavid S. Miller 	/* incrementing addr for function 1 */
3474a5a553dSDavid S. Miller 	if (func->num == 1)
3484a5a553dSDavid S. Miller 		*addr += req_sz;
3494a5a553dSDavid S. Miller 
3504a5a553dSDavid S. Miller 	mmc_set_data_timeout(md, func->card);
3514a5a553dSDavid S. Miller 	mmc_wait_for_req(func->card->host, mr);
3524a5a553dSDavid S. Miller 
3534a5a553dSDavid S. Miller 	ret = mc->error ? mc->error : md->error;
3544a5a553dSDavid S. Miller 	if (ret == -ENOMEDIUM) {
3554a5a553dSDavid S. Miller 		brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_NOMEDIUM);
3564a5a553dSDavid S. Miller 	} else if (ret != 0) {
3574a5a553dSDavid S. Miller 		brcmf_err("CMD53 sg block %s failed %d\n",
3584a5a553dSDavid S. Miller 			  write ? "write" : "read", ret);
3594a5a553dSDavid S. Miller 		ret = -EIO;
3604a5a553dSDavid S. Miller 	}
3614a5a553dSDavid S. Miller 
3624a5a553dSDavid S. Miller 	return ret;
3634a5a553dSDavid S. Miller }
3644a5a553dSDavid S. Miller 
36505491d2cSKalle Valo /**
36605491d2cSKalle Valo  * brcmf_sdiod_sglist_rw - SDIO interface function for block data access
36705491d2cSKalle Valo  * @sdiodev: brcmfmac sdio device
36800eb62cfSIan Molton  * @func: SDIO function
36905491d2cSKalle Valo  * @write: direction flag
37005491d2cSKalle Valo  * @addr: dongle memory address as source/destination
3716f0d044fSLee Jones  * @pktlist: skb buffer head pointer
37205491d2cSKalle Valo  *
37305491d2cSKalle Valo  * This function takes the respbonsibility as the interface function to MMC
37405491d2cSKalle Valo  * stack for block data access. It assumes that the skb passed down by the
37505491d2cSKalle Valo  * caller has already been padded and aligned.
37605491d2cSKalle Valo  */
brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev * sdiodev,struct sdio_func * func,bool write,u32 addr,struct sk_buff_head * pktlist)37700eb62cfSIan Molton static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev,
37800eb62cfSIan Molton 				 struct sdio_func *func,
37905491d2cSKalle Valo 				 bool write, u32 addr,
38005491d2cSKalle Valo 				 struct sk_buff_head *pktlist)
38105491d2cSKalle Valo {
38205491d2cSKalle Valo 	unsigned int req_sz, func_blk_sz, sg_cnt, sg_data_sz, pkt_offset;
3834a5a553dSDavid S. Miller 	unsigned int max_req_sz, src_offset, dst_offset;
38405491d2cSKalle Valo 	unsigned char *pkt_data, *orig_data, *dst_data;
38505491d2cSKalle Valo 	struct sk_buff_head local_list, *target_list;
3864a5a553dSDavid S. Miller 	struct sk_buff *pkt_next = NULL, *src;
3874a5a553dSDavid S. Miller 	unsigned short max_seg_cnt;
38805491d2cSKalle Valo 	struct mmc_request mmc_req;
38905491d2cSKalle Valo 	struct mmc_command mmc_cmd;
39005491d2cSKalle Valo 	struct mmc_data mmc_dat;
39105491d2cSKalle Valo 	struct scatterlist *sgl;
39205491d2cSKalle Valo 	int ret = 0;
39305491d2cSKalle Valo 
39405491d2cSKalle Valo 	if (!pktlist->qlen)
39505491d2cSKalle Valo 		return -EINVAL;
39605491d2cSKalle Valo 
39705491d2cSKalle Valo 	target_list = pktlist;
39805491d2cSKalle Valo 	/* for host with broken sg support, prepare a page aligned list */
39905491d2cSKalle Valo 	__skb_queue_head_init(&local_list);
400af5b5e62SHante Meuleman 	if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
40105491d2cSKalle Valo 		req_sz = 0;
40205491d2cSKalle Valo 		skb_queue_walk(pktlist, pkt_next)
40305491d2cSKalle Valo 			req_sz += pkt_next->len;
40400eb62cfSIan Molton 		req_sz = ALIGN(req_sz, func->cur_blksize);
40505491d2cSKalle Valo 		while (req_sz > PAGE_SIZE) {
40605491d2cSKalle Valo 			pkt_next = brcmu_pkt_buf_get_skb(PAGE_SIZE);
40705491d2cSKalle Valo 			if (pkt_next == NULL) {
40805491d2cSKalle Valo 				ret = -ENOMEM;
40905491d2cSKalle Valo 				goto exit;
41005491d2cSKalle Valo 			}
41105491d2cSKalle Valo 			__skb_queue_tail(&local_list, pkt_next);
41205491d2cSKalle Valo 			req_sz -= PAGE_SIZE;
41305491d2cSKalle Valo 		}
41405491d2cSKalle Valo 		pkt_next = brcmu_pkt_buf_get_skb(req_sz);
41505491d2cSKalle Valo 		if (pkt_next == NULL) {
41605491d2cSKalle Valo 			ret = -ENOMEM;
41705491d2cSKalle Valo 			goto exit;
41805491d2cSKalle Valo 		}
41905491d2cSKalle Valo 		__skb_queue_tail(&local_list, pkt_next);
42005491d2cSKalle Valo 		target_list = &local_list;
42105491d2cSKalle Valo 	}
42205491d2cSKalle Valo 
42300eb62cfSIan Molton 	func_blk_sz = func->cur_blksize;
42405491d2cSKalle Valo 	max_req_sz = sdiodev->max_request_size;
42505491d2cSKalle Valo 	max_seg_cnt = min_t(unsigned short, sdiodev->max_segment_count,
42605491d2cSKalle Valo 			    target_list->qlen);
42705491d2cSKalle Valo 
42805491d2cSKalle Valo 	memset(&mmc_req, 0, sizeof(struct mmc_request));
42905491d2cSKalle Valo 	memset(&mmc_cmd, 0, sizeof(struct mmc_command));
43005491d2cSKalle Valo 	memset(&mmc_dat, 0, sizeof(struct mmc_data));
43105491d2cSKalle Valo 
43205491d2cSKalle Valo 	mmc_dat.sg = sdiodev->sgtable.sgl;
43305491d2cSKalle Valo 	mmc_dat.blksz = func_blk_sz;
43405491d2cSKalle Valo 	mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ;
43505491d2cSKalle Valo 	mmc_cmd.opcode = SD_IO_RW_EXTENDED;
43605491d2cSKalle Valo 	mmc_cmd.arg = write ? 1<<31 : 0;	/* write flag  */
43700eb62cfSIan Molton 	mmc_cmd.arg |= (func->num & 0x7) << 28;	/* SDIO func num */
43805491d2cSKalle Valo 	mmc_cmd.arg |= 1 << 27;			/* block mode */
43905491d2cSKalle Valo 	/* for function 1 the addr will be incremented */
44000eb62cfSIan Molton 	mmc_cmd.arg |= (func->num == 1) ? 1 << 26 : 0;
44105491d2cSKalle Valo 	mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC;
44205491d2cSKalle Valo 	mmc_req.cmd = &mmc_cmd;
44305491d2cSKalle Valo 	mmc_req.data = &mmc_dat;
44405491d2cSKalle Valo 
44505491d2cSKalle Valo 	req_sz = 0;
44605491d2cSKalle Valo 	sg_cnt = 0;
44705491d2cSKalle Valo 	sgl = sdiodev->sgtable.sgl;
4484a5a553dSDavid S. Miller 	skb_queue_walk(target_list, pkt_next) {
4494a5a553dSDavid S. Miller 		pkt_offset = 0;
4504a5a553dSDavid S. Miller 		while (pkt_offset < pkt_next->len) {
45105491d2cSKalle Valo 			pkt_data = pkt_next->data + pkt_offset;
45205491d2cSKalle Valo 			sg_data_sz = pkt_next->len - pkt_offset;
45305491d2cSKalle Valo 			if (sg_data_sz > sdiodev->max_segment_size)
45405491d2cSKalle Valo 				sg_data_sz = sdiodev->max_segment_size;
45505491d2cSKalle Valo 			if (sg_data_sz > max_req_sz - req_sz)
45605491d2cSKalle Valo 				sg_data_sz = max_req_sz - req_sz;
45705491d2cSKalle Valo 
45805491d2cSKalle Valo 			sg_set_buf(sgl, pkt_data, sg_data_sz);
45905491d2cSKalle Valo 			sg_cnt++;
4604a5a553dSDavid S. Miller 
46105491d2cSKalle Valo 			sgl = sg_next(sgl);
46205491d2cSKalle Valo 			req_sz += sg_data_sz;
46305491d2cSKalle Valo 			pkt_offset += sg_data_sz;
4644a5a553dSDavid S. Miller 			if (req_sz >= max_req_sz || sg_cnt >= max_seg_cnt) {
4654a5a553dSDavid S. Miller 				ret = mmc_submit_one(&mmc_dat, &mmc_req, &mmc_cmd,
4664a5a553dSDavid S. Miller 						     sg_cnt, req_sz, func_blk_sz,
4674a5a553dSDavid S. Miller 						     &addr, sdiodev, func, write);
4684a5a553dSDavid S. Miller 				if (ret)
4694a5a553dSDavid S. Miller 					goto exit_queue_walk;
4704a5a553dSDavid S. Miller 				req_sz = 0;
4714a5a553dSDavid S. Miller 				sg_cnt = 0;
4724a5a553dSDavid S. Miller 				sgl = sdiodev->sgtable.sgl;
47305491d2cSKalle Valo 			}
47405491d2cSKalle Valo 		}
4754a5a553dSDavid S. Miller 	}
4764a5a553dSDavid S. Miller 	if (sg_cnt)
4774a5a553dSDavid S. Miller 		ret = mmc_submit_one(&mmc_dat, &mmc_req, &mmc_cmd,
4784a5a553dSDavid S. Miller 				     sg_cnt, req_sz, func_blk_sz,
4794a5a553dSDavid S. Miller 				     &addr, sdiodev, func, write);
4804a5a553dSDavid S. Miller exit_queue_walk:
481af5b5e62SHante Meuleman 	if (!write && sdiodev->settings->bus.sdio.broken_sg_support) {
4824a5a553dSDavid S. Miller 		src = __skb_peek(&local_list);
4834a5a553dSDavid S. Miller 		src_offset = 0;
48405491d2cSKalle Valo 		skb_queue_walk(pktlist, pkt_next) {
48505491d2cSKalle Valo 			dst_offset = 0;
4864a5a553dSDavid S. Miller 
4874a5a553dSDavid S. Miller 			/* This is safe because we must have enough SKB data
4884a5a553dSDavid S. Miller 			 * in the local list to cover everything in pktlist.
4894a5a553dSDavid S. Miller 			 */
4904a5a553dSDavid S. Miller 			while (1) {
4914a5a553dSDavid S. Miller 				req_sz = pkt_next->len - dst_offset;
4924a5a553dSDavid S. Miller 				if (req_sz > src->len - src_offset)
4934a5a553dSDavid S. Miller 					req_sz = src->len - src_offset;
4944a5a553dSDavid S. Miller 
4954a5a553dSDavid S. Miller 				orig_data = src->data + src_offset;
49605491d2cSKalle Valo 				dst_data = pkt_next->data + dst_offset;
49705491d2cSKalle Valo 				memcpy(dst_data, orig_data, req_sz);
4984a5a553dSDavid S. Miller 
4994a5a553dSDavid S. Miller 				src_offset += req_sz;
5004a5a553dSDavid S. Miller 				if (src_offset == src->len) {
5014a5a553dSDavid S. Miller 					src_offset = 0;
5024a5a553dSDavid S. Miller 					src = skb_peek_next(src, &local_list);
50305491d2cSKalle Valo 				}
5044a5a553dSDavid S. Miller 				dst_offset += req_sz;
50505491d2cSKalle Valo 				if (dst_offset == pkt_next->len)
50605491d2cSKalle Valo 					break;
5074a5a553dSDavid S. Miller 			}
50805491d2cSKalle Valo 		}
50905491d2cSKalle Valo 	}
51005491d2cSKalle Valo 
51105491d2cSKalle Valo exit:
51205491d2cSKalle Valo 	sg_init_table(sdiodev->sgtable.sgl, sdiodev->sgtable.orig_nents);
51305491d2cSKalle Valo 	while ((pkt_next = __skb_dequeue(&local_list)) != NULL)
51405491d2cSKalle Valo 		brcmu_pkt_buf_free_skb(pkt_next);
51505491d2cSKalle Valo 
51605491d2cSKalle Valo 	return ret;
51705491d2cSKalle Valo }
51805491d2cSKalle Valo 
brcmf_sdiod_recv_buf(struct brcmf_sdio_dev * sdiodev,u8 * buf,uint nbytes)51905491d2cSKalle Valo int brcmf_sdiod_recv_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
52005491d2cSKalle Valo {
52105491d2cSKalle Valo 	struct sk_buff *mypkt;
52205491d2cSKalle Valo 	int err;
52305491d2cSKalle Valo 
52405491d2cSKalle Valo 	mypkt = brcmu_pkt_buf_get_skb(nbytes);
52505491d2cSKalle Valo 	if (!mypkt) {
52605491d2cSKalle Valo 		brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
52705491d2cSKalle Valo 			  nbytes);
52805491d2cSKalle Valo 		return -EIO;
52905491d2cSKalle Valo 	}
53005491d2cSKalle Valo 
53105491d2cSKalle Valo 	err = brcmf_sdiod_recv_pkt(sdiodev, mypkt);
53205491d2cSKalle Valo 	if (!err)
53305491d2cSKalle Valo 		memcpy(buf, mypkt->data, nbytes);
53405491d2cSKalle Valo 
53505491d2cSKalle Valo 	brcmu_pkt_buf_free_skb(mypkt);
53605491d2cSKalle Valo 	return err;
53705491d2cSKalle Valo }
53805491d2cSKalle Valo 
brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev * sdiodev,struct sk_buff * pkt)53905491d2cSKalle Valo int brcmf_sdiod_recv_pkt(struct brcmf_sdio_dev *sdiodev, struct sk_buff *pkt)
54005491d2cSKalle Valo {
541874bb8e4SIan Molton 	u32 addr = sdiodev->cc_core->base;
54205491d2cSKalle Valo 	int err = 0;
54305491d2cSKalle Valo 
54405491d2cSKalle Valo 	brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pkt->len);
54505491d2cSKalle Valo 
546a7c3aa15SIan Molton 	err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
54705491d2cSKalle Valo 	if (err)
54805491d2cSKalle Valo 		goto done;
54905491d2cSKalle Valo 
550a7c3aa15SIan Molton 	addr &= SBSDIO_SB_OFT_ADDR_MASK;
551a7c3aa15SIan Molton 	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
552a7c3aa15SIan Molton 
553378f6a16SArend Van Spriel 	err = brcmf_sdiod_skbuff_read(sdiodev, sdiodev->func2, addr, pkt);
55405491d2cSKalle Valo 
55505491d2cSKalle Valo done:
55605491d2cSKalle Valo 	return err;
55705491d2cSKalle Valo }
55805491d2cSKalle Valo 
brcmf_sdiod_recv_chain(struct brcmf_sdio_dev * sdiodev,struct sk_buff_head * pktq,uint totlen)55905491d2cSKalle Valo int brcmf_sdiod_recv_chain(struct brcmf_sdio_dev *sdiodev,
56005491d2cSKalle Valo 			   struct sk_buff_head *pktq, uint totlen)
56105491d2cSKalle Valo {
5625ea59db8SPeter S. Housel 	struct sk_buff *glom_skb = NULL;
56305491d2cSKalle Valo 	struct sk_buff *skb;
564874bb8e4SIan Molton 	u32 addr = sdiodev->cc_core->base;
56505491d2cSKalle Valo 	int err = 0;
56605491d2cSKalle Valo 
56705491d2cSKalle Valo 	brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n",
56805491d2cSKalle Valo 		  addr, pktq->qlen);
56905491d2cSKalle Valo 
570a7c3aa15SIan Molton 	err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
57105491d2cSKalle Valo 	if (err)
57205491d2cSKalle Valo 		goto done;
57305491d2cSKalle Valo 
574a7c3aa15SIan Molton 	addr &= SBSDIO_SB_OFT_ADDR_MASK;
575a7c3aa15SIan Molton 	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
576a7c3aa15SIan Molton 
57705491d2cSKalle Valo 	if (pktq->qlen == 1)
578378f6a16SArend Van Spriel 		err = brcmf_sdiod_skbuff_read(sdiodev, sdiodev->func2, addr,
579776f07eeSDavid S. Miller 					      __skb_peek(pktq));
58005491d2cSKalle Valo 	else if (!sdiodev->sg_support) {
58105491d2cSKalle Valo 		glom_skb = brcmu_pkt_buf_get_skb(totlen);
58205491d2cSKalle Valo 		if (!glom_skb)
58305491d2cSKalle Valo 			return -ENOMEM;
584378f6a16SArend Van Spriel 		err = brcmf_sdiod_skbuff_read(sdiodev, sdiodev->func2, addr,
58505491d2cSKalle Valo 					      glom_skb);
5865ea59db8SPeter S. Housel 		if (err)
58705491d2cSKalle Valo 			goto done;
58805491d2cSKalle Valo 
58905491d2cSKalle Valo 		skb_queue_walk(pktq, skb) {
59005491d2cSKalle Valo 			memcpy(skb->data, glom_skb->data, skb->len);
59105491d2cSKalle Valo 			skb_pull(glom_skb, skb->len);
59205491d2cSKalle Valo 		}
59305491d2cSKalle Valo 	} else
594c9aa7a91SArend Van Spriel 		err = brcmf_sdiod_sglist_rw(sdiodev, sdiodev->func2, false,
59500eb62cfSIan Molton 					    addr, pktq);
59605491d2cSKalle Valo 
59705491d2cSKalle Valo done:
5985ea59db8SPeter S. Housel 	brcmu_pkt_buf_free_skb(glom_skb);
59905491d2cSKalle Valo 	return err;
60005491d2cSKalle Valo }
60105491d2cSKalle Valo 
brcmf_sdiod_send_buf(struct brcmf_sdio_dev * sdiodev,u8 * buf,uint nbytes)60205491d2cSKalle Valo int brcmf_sdiod_send_buf(struct brcmf_sdio_dev *sdiodev, u8 *buf, uint nbytes)
60305491d2cSKalle Valo {
60405491d2cSKalle Valo 	struct sk_buff *mypkt;
605874bb8e4SIan Molton 	u32 addr = sdiodev->cc_core->base;
60605491d2cSKalle Valo 	int err;
60705491d2cSKalle Valo 
60805491d2cSKalle Valo 	mypkt = brcmu_pkt_buf_get_skb(nbytes);
6096e24dd01SIan Molton 
61005491d2cSKalle Valo 	if (!mypkt) {
61105491d2cSKalle Valo 		brcmf_err("brcmu_pkt_buf_get_skb failed: len %d\n",
61205491d2cSKalle Valo 			  nbytes);
61305491d2cSKalle Valo 		return -EIO;
61405491d2cSKalle Valo 	}
61505491d2cSKalle Valo 
61605491d2cSKalle Valo 	memcpy(mypkt->data, buf, nbytes);
61705491d2cSKalle Valo 
618a7c3aa15SIan Molton 	err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
619a7c3aa15SIan Molton 	if (err)
620a927e8d8SColin Ian King 		goto out;
621a7c3aa15SIan Molton 
622a7c3aa15SIan Molton 	addr &= SBSDIO_SB_OFT_ADDR_MASK;
623a7c3aa15SIan Molton 	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
62405491d2cSKalle Valo 
625a927e8d8SColin Ian King 	err = brcmf_sdiod_skbuff_write(sdiodev, sdiodev->func2, addr, mypkt);
626a927e8d8SColin Ian King out:
62705491d2cSKalle Valo 	brcmu_pkt_buf_free_skb(mypkt);
62805491d2cSKalle Valo 
6296e24dd01SIan Molton 	return err;
63005491d2cSKalle Valo }
63105491d2cSKalle Valo 
brcmf_sdiod_send_pkt(struct brcmf_sdio_dev * sdiodev,struct sk_buff_head * pktq)63205491d2cSKalle Valo int brcmf_sdiod_send_pkt(struct brcmf_sdio_dev *sdiodev,
63305491d2cSKalle Valo 			 struct sk_buff_head *pktq)
63405491d2cSKalle Valo {
63505491d2cSKalle Valo 	struct sk_buff *skb;
636874bb8e4SIan Molton 	u32 addr = sdiodev->cc_core->base;
63705491d2cSKalle Valo 	int err;
63805491d2cSKalle Valo 
63905491d2cSKalle Valo 	brcmf_dbg(SDIO, "addr = 0x%x, size = %d\n", addr, pktq->qlen);
64005491d2cSKalle Valo 
641a7c3aa15SIan Molton 	err = brcmf_sdiod_set_backplane_window(sdiodev, addr);
64205491d2cSKalle Valo 	if (err)
64305491d2cSKalle Valo 		return err;
64405491d2cSKalle Valo 
645a7c3aa15SIan Molton 	addr &= SBSDIO_SB_OFT_ADDR_MASK;
646a7c3aa15SIan Molton 	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
647a7c3aa15SIan Molton 
648a7323378SIan Molton 	if (pktq->qlen == 1 || !sdiodev->sg_support) {
64905491d2cSKalle Valo 		skb_queue_walk(pktq, skb) {
650378f6a16SArend Van Spriel 			err = brcmf_sdiod_skbuff_write(sdiodev, sdiodev->func2,
65105491d2cSKalle Valo 						       addr, skb);
65205491d2cSKalle Valo 			if (err)
65305491d2cSKalle Valo 				break;
65405491d2cSKalle Valo 		}
655a7323378SIan Molton 	} else {
656c9aa7a91SArend Van Spriel 		err = brcmf_sdiod_sglist_rw(sdiodev, sdiodev->func2, true,
65700eb62cfSIan Molton 					    addr, pktq);
658a7323378SIan Molton 	}
65905491d2cSKalle Valo 
66005491d2cSKalle Valo 	return err;
66105491d2cSKalle Valo }
66205491d2cSKalle Valo 
66305491d2cSKalle Valo int
brcmf_sdiod_ramrw(struct brcmf_sdio_dev * sdiodev,bool write,u32 address,u8 * data,uint size)66405491d2cSKalle Valo brcmf_sdiod_ramrw(struct brcmf_sdio_dev *sdiodev, bool write, u32 address,
66505491d2cSKalle Valo 		  u8 *data, uint size)
66605491d2cSKalle Valo {
667dd8a2d49SIan Molton 	int err = 0;
66805491d2cSKalle Valo 	struct sk_buff *pkt;
66905491d2cSKalle Valo 	u32 sdaddr;
67005491d2cSKalle Valo 	uint dsize;
67105491d2cSKalle Valo 
67205491d2cSKalle Valo 	dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
67305491d2cSKalle Valo 	pkt = dev_alloc_skb(dsize);
67405491d2cSKalle Valo 	if (!pkt) {
67505491d2cSKalle Valo 		brcmf_err("dev_alloc_skb failed: len %d\n", dsize);
67605491d2cSKalle Valo 		return -EIO;
67705491d2cSKalle Valo 	}
67805491d2cSKalle Valo 	pkt->priority = 0;
67905491d2cSKalle Valo 
68005491d2cSKalle Valo 	/* Determine initial transfer parameters */
68105491d2cSKalle Valo 	sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
68205491d2cSKalle Valo 	if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
68305491d2cSKalle Valo 		dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
68405491d2cSKalle Valo 	else
68505491d2cSKalle Valo 		dsize = size;
68605491d2cSKalle Valo 
687c9aa7a91SArend Van Spriel 	sdio_claim_host(sdiodev->func1);
68805491d2cSKalle Valo 
68905491d2cSKalle Valo 	/* Do the transfer(s) */
69005491d2cSKalle Valo 	while (size) {
69105491d2cSKalle Valo 		/* Set the backplane window to include the start address */
692a7c3aa15SIan Molton 		err = brcmf_sdiod_set_backplane_window(sdiodev, address);
693dd8a2d49SIan Molton 		if (err)
69405491d2cSKalle Valo 			break;
69505491d2cSKalle Valo 
69605491d2cSKalle Valo 		brcmf_dbg(SDIO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
69705491d2cSKalle Valo 			  write ? "write" : "read", dsize,
69805491d2cSKalle Valo 			  sdaddr, address & SBSDIO_SBWINDOW_MASK);
69905491d2cSKalle Valo 
70005491d2cSKalle Valo 		sdaddr &= SBSDIO_SB_OFT_ADDR_MASK;
70105491d2cSKalle Valo 		sdaddr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
70205491d2cSKalle Valo 
70305491d2cSKalle Valo 		skb_put(pkt, dsize);
7048f13c87cSIan Molton 
7058f13c87cSIan Molton 		if (write) {
70605491d2cSKalle Valo 			memcpy(pkt->data, data, dsize);
707378f6a16SArend Van Spriel 			err = brcmf_sdiod_skbuff_write(sdiodev, sdiodev->func1,
7088f13c87cSIan Molton 						       sdaddr, pkt);
7098f13c87cSIan Molton 		} else {
710378f6a16SArend Van Spriel 			err = brcmf_sdiod_skbuff_read(sdiodev, sdiodev->func1,
7118f13c87cSIan Molton 						      sdaddr, pkt);
7128f13c87cSIan Molton 		}
7138f13c87cSIan Molton 
714dd8a2d49SIan Molton 		if (err) {
71505491d2cSKalle Valo 			brcmf_err("membytes transfer failed\n");
71605491d2cSKalle Valo 			break;
71705491d2cSKalle Valo 		}
71805491d2cSKalle Valo 		if (!write)
71905491d2cSKalle Valo 			memcpy(data, pkt->data, dsize);
72005491d2cSKalle Valo 		skb_trim(pkt, 0);
72105491d2cSKalle Valo 
72205491d2cSKalle Valo 		/* Adjust for next transfer (if any) */
72305491d2cSKalle Valo 		size -= dsize;
72405491d2cSKalle Valo 		if (size) {
72505491d2cSKalle Valo 			data += dsize;
72605491d2cSKalle Valo 			address += dsize;
72705491d2cSKalle Valo 			sdaddr = 0;
72805491d2cSKalle Valo 			dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
72905491d2cSKalle Valo 		}
73005491d2cSKalle Valo 	}
73105491d2cSKalle Valo 
73205491d2cSKalle Valo 	dev_kfree_skb(pkt);
73305491d2cSKalle Valo 
734c9aa7a91SArend Van Spriel 	sdio_release_host(sdiodev->func1);
73505491d2cSKalle Valo 
736dd8a2d49SIan Molton 	return err;
73705491d2cSKalle Valo }
73805491d2cSKalle Valo 
brcmf_sdiod_abort(struct brcmf_sdio_dev * sdiodev,struct sdio_func * func)73900eb62cfSIan Molton int brcmf_sdiod_abort(struct brcmf_sdio_dev *sdiodev, struct sdio_func *func)
74005491d2cSKalle Valo {
74105491d2cSKalle Valo 	brcmf_dbg(SDIO, "Enter\n");
74205491d2cSKalle Valo 
74371bd508dSIan Molton 	/* Issue abort cmd52 command through F0 */
74400eb62cfSIan Molton 	brcmf_sdiod_func0_wb(sdiodev, SDIO_CCCR_ABORT, func->num, NULL);
74505491d2cSKalle Valo 
74605491d2cSKalle Valo 	brcmf_dbg(SDIO, "Exit\n");
74705491d2cSKalle Valo 	return 0;
74805491d2cSKalle Valo }
74905491d2cSKalle Valo 
brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev * sdiodev)750e0045bf8SHante Meuleman void brcmf_sdiod_sgtable_alloc(struct brcmf_sdio_dev *sdiodev)
75105491d2cSKalle Valo {
752e0045bf8SHante Meuleman 	struct sdio_func *func;
753e0045bf8SHante Meuleman 	struct mmc_host *host;
754e0045bf8SHante Meuleman 	uint max_blocks;
75505491d2cSKalle Valo 	uint nents;
75605491d2cSKalle Valo 	int err;
75705491d2cSKalle Valo 
758c9aa7a91SArend Van Spriel 	func = sdiodev->func2;
759e0045bf8SHante Meuleman 	host = func->card->host;
760e0045bf8SHante Meuleman 	sdiodev->sg_support = host->max_segs > 1;
761e0045bf8SHante Meuleman 	max_blocks = min_t(uint, host->max_blk_count, 511u);
762e0045bf8SHante Meuleman 	sdiodev->max_request_size = min_t(uint, host->max_req_size,
763e0045bf8SHante Meuleman 					  max_blocks * func->cur_blksize);
764e0045bf8SHante Meuleman 	sdiodev->max_segment_count = min_t(uint, host->max_segs,
765e0045bf8SHante Meuleman 					   SG_MAX_SINGLE_ALLOC);
766e0045bf8SHante Meuleman 	sdiodev->max_segment_size = host->max_seg_size;
767e0045bf8SHante Meuleman 
76805491d2cSKalle Valo 	if (!sdiodev->sg_support)
76905491d2cSKalle Valo 		return;
77005491d2cSKalle Valo 
7717d34b056SHante Meuleman 	nents = max_t(uint, BRCMF_DEFAULT_RXGLOM_SIZE,
772af5b5e62SHante Meuleman 		      sdiodev->settings->bus.sdio.txglomsz);
77307c020c6SNorbert van Bolhuis 	nents *= 2;
77405491d2cSKalle Valo 
77505491d2cSKalle Valo 	WARN_ON(nents > sdiodev->max_segment_count);
77605491d2cSKalle Valo 
77705491d2cSKalle Valo 	brcmf_dbg(TRACE, "nents=%d\n", nents);
77805491d2cSKalle Valo 	err = sg_alloc_table(&sdiodev->sgtable, nents, GFP_KERNEL);
77905491d2cSKalle Valo 	if (err < 0) {
78005491d2cSKalle Valo 		brcmf_err("allocation failed: disable scatter-gather");
78105491d2cSKalle Valo 		sdiodev->sg_support = false;
78205491d2cSKalle Valo 	}
78305491d2cSKalle Valo 
784af5b5e62SHante Meuleman 	sdiodev->txglomsz = sdiodev->settings->bus.sdio.txglomsz;
78505491d2cSKalle Valo }
78605491d2cSKalle Valo 
brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev * sdiodev)78705491d2cSKalle Valo static int brcmf_sdiod_freezer_attach(struct brcmf_sdio_dev *sdiodev)
78805491d2cSKalle Valo {
78902a186f1SPaul Cercueil 	if (!IS_ENABLED(CONFIG_PM_SLEEP))
79002a186f1SPaul Cercueil 		return 0;
79102a186f1SPaul Cercueil 
79205491d2cSKalle Valo 	sdiodev->freezer = kzalloc(sizeof(*sdiodev->freezer), GFP_KERNEL);
79305491d2cSKalle Valo 	if (!sdiodev->freezer)
79405491d2cSKalle Valo 		return -ENOMEM;
79505491d2cSKalle Valo 	atomic_set(&sdiodev->freezer->thread_count, 0);
79605491d2cSKalle Valo 	atomic_set(&sdiodev->freezer->freezing, 0);
79705491d2cSKalle Valo 	init_waitqueue_head(&sdiodev->freezer->thread_freeze);
79805491d2cSKalle Valo 	init_completion(&sdiodev->freezer->resumed);
79905491d2cSKalle Valo 	return 0;
80005491d2cSKalle Valo }
80105491d2cSKalle Valo 
brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev * sdiodev)80205491d2cSKalle Valo static void brcmf_sdiod_freezer_detach(struct brcmf_sdio_dev *sdiodev)
80305491d2cSKalle Valo {
80405491d2cSKalle Valo 	if (sdiodev->freezer) {
80505491d2cSKalle Valo 		WARN_ON(atomic_read(&sdiodev->freezer->freezing));
80605491d2cSKalle Valo 		kfree(sdiodev->freezer);
807cb774bd3SDanny van Heumen 		sdiodev->freezer = NULL;
80805491d2cSKalle Valo 	}
80905491d2cSKalle Valo }
81005491d2cSKalle Valo 
brcmf_sdiod_freezer_on(struct brcmf_sdio_dev * sdiodev)81105491d2cSKalle Valo static int brcmf_sdiod_freezer_on(struct brcmf_sdio_dev *sdiodev)
81205491d2cSKalle Valo {
81305491d2cSKalle Valo 	atomic_t *expect = &sdiodev->freezer->thread_count;
81405491d2cSKalle Valo 	int res = 0;
81505491d2cSKalle Valo 
81605491d2cSKalle Valo 	sdiodev->freezer->frozen_count = 0;
81705491d2cSKalle Valo 	reinit_completion(&sdiodev->freezer->resumed);
81805491d2cSKalle Valo 	atomic_set(&sdiodev->freezer->freezing, 1);
81905491d2cSKalle Valo 	brcmf_sdio_trigger_dpc(sdiodev->bus);
82005491d2cSKalle Valo 	wait_event(sdiodev->freezer->thread_freeze,
82105491d2cSKalle Valo 		   atomic_read(expect) == sdiodev->freezer->frozen_count);
822c9aa7a91SArend Van Spriel 	sdio_claim_host(sdiodev->func1);
82305491d2cSKalle Valo 	res = brcmf_sdio_sleep(sdiodev->bus, true);
824c9aa7a91SArend Van Spriel 	sdio_release_host(sdiodev->func1);
82505491d2cSKalle Valo 	return res;
82605491d2cSKalle Valo }
82705491d2cSKalle Valo 
brcmf_sdiod_freezer_off(struct brcmf_sdio_dev * sdiodev)82805491d2cSKalle Valo static void brcmf_sdiod_freezer_off(struct brcmf_sdio_dev *sdiodev)
82905491d2cSKalle Valo {
830c9aa7a91SArend Van Spriel 	sdio_claim_host(sdiodev->func1);
83105491d2cSKalle Valo 	brcmf_sdio_sleep(sdiodev->bus, false);
832c9aa7a91SArend Van Spriel 	sdio_release_host(sdiodev->func1);
83305491d2cSKalle Valo 	atomic_set(&sdiodev->freezer->freezing, 0);
83405491d2cSKalle Valo 	complete_all(&sdiodev->freezer->resumed);
83505491d2cSKalle Valo }
83605491d2cSKalle Valo 
brcmf_sdiod_freezing(struct brcmf_sdio_dev * sdiodev)83705491d2cSKalle Valo bool brcmf_sdiod_freezing(struct brcmf_sdio_dev *sdiodev)
83805491d2cSKalle Valo {
83902a186f1SPaul Cercueil 	return IS_ENABLED(CONFIG_PM_SLEEP) &&
84002a186f1SPaul Cercueil 		atomic_read(&sdiodev->freezer->freezing);
84105491d2cSKalle Valo }
84205491d2cSKalle Valo 
brcmf_sdiod_try_freeze(struct brcmf_sdio_dev * sdiodev)84305491d2cSKalle Valo void brcmf_sdiod_try_freeze(struct brcmf_sdio_dev *sdiodev)
84405491d2cSKalle Valo {
84505491d2cSKalle Valo 	if (!brcmf_sdiod_freezing(sdiodev))
84605491d2cSKalle Valo 		return;
84705491d2cSKalle Valo 	sdiodev->freezer->frozen_count++;
84805491d2cSKalle Valo 	wake_up(&sdiodev->freezer->thread_freeze);
84905491d2cSKalle Valo 	wait_for_completion(&sdiodev->freezer->resumed);
85005491d2cSKalle Valo }
85105491d2cSKalle Valo 
brcmf_sdiod_freezer_count(struct brcmf_sdio_dev * sdiodev)85205491d2cSKalle Valo void brcmf_sdiod_freezer_count(struct brcmf_sdio_dev *sdiodev)
85305491d2cSKalle Valo {
85402a186f1SPaul Cercueil 	if (IS_ENABLED(CONFIG_PM_SLEEP))
85505491d2cSKalle Valo 		atomic_inc(&sdiodev->freezer->thread_count);
85605491d2cSKalle Valo }
85705491d2cSKalle Valo 
brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev * sdiodev)85805491d2cSKalle Valo void brcmf_sdiod_freezer_uncount(struct brcmf_sdio_dev *sdiodev)
85905491d2cSKalle Valo {
86002a186f1SPaul Cercueil 	if (IS_ENABLED(CONFIG_PM_SLEEP))
86105491d2cSKalle Valo 		atomic_dec(&sdiodev->freezer->thread_count);
86205491d2cSKalle Valo }
86305491d2cSKalle Valo 
brcmf_sdiod_remove(struct brcmf_sdio_dev * sdiodev)8647836102aSChi-Hsien Lin int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev)
86505491d2cSKalle Valo {
86605491d2cSKalle Valo 	sdiodev->state = BRCMF_SDIOD_DOWN;
86705491d2cSKalle Valo 	if (sdiodev->bus) {
86805491d2cSKalle Valo 		brcmf_sdio_remove(sdiodev->bus);
86905491d2cSKalle Valo 		sdiodev->bus = NULL;
87005491d2cSKalle Valo 	}
87105491d2cSKalle Valo 
87205491d2cSKalle Valo 	brcmf_sdiod_freezer_detach(sdiodev);
87305491d2cSKalle Valo 
874cb774bd3SDanny van Heumen 	/* Disable functions 2 then 1. */
875c9aa7a91SArend Van Spriel 	sdio_claim_host(sdiodev->func1);
876cb774bd3SDanny van Heumen 	sdio_disable_func(sdiodev->func2);
877c9aa7a91SArend Van Spriel 	sdio_disable_func(sdiodev->func1);
878c9aa7a91SArend Van Spriel 	sdio_release_host(sdiodev->func1);
87905491d2cSKalle Valo 
88005491d2cSKalle Valo 	sg_free_table(&sdiodev->sgtable);
88105491d2cSKalle Valo 	sdiodev->sbwad = 0;
88205491d2cSKalle Valo 
883c9aa7a91SArend Van Spriel 	pm_runtime_allow(sdiodev->func1->card->host->parent);
88405491d2cSKalle Valo 	return 0;
88505491d2cSKalle Valo }
88605491d2cSKalle Valo 
brcmf_sdiod_host_fixup(struct mmc_host * host)88705491d2cSKalle Valo static void brcmf_sdiod_host_fixup(struct mmc_host *host)
88805491d2cSKalle Valo {
88905491d2cSKalle Valo 	/* runtime-pm powers off the device */
89005491d2cSKalle Valo 	pm_runtime_forbid(host->parent);
89105491d2cSKalle Valo 	/* avoid removal detection upon resume */
89205491d2cSKalle Valo 	host->caps |= MMC_CAP_NONREMOVABLE;
89305491d2cSKalle Valo }
89405491d2cSKalle Valo 
brcmf_sdiod_probe(struct brcmf_sdio_dev * sdiodev)8957836102aSChi-Hsien Lin int brcmf_sdiod_probe(struct brcmf_sdio_dev *sdiodev)
89605491d2cSKalle Valo {
89705491d2cSKalle Valo 	int ret = 0;
898172f6854SChung-Hsien Hsu 	unsigned int f2_blksz = SDIO_FUNC2_BLOCKSIZE;
89905491d2cSKalle Valo 
900c9aa7a91SArend Van Spriel 	sdio_claim_host(sdiodev->func1);
90105491d2cSKalle Valo 
902c9aa7a91SArend Van Spriel 	ret = sdio_set_block_size(sdiodev->func1, SDIO_FUNC1_BLOCKSIZE);
90305491d2cSKalle Valo 	if (ret) {
90405491d2cSKalle Valo 		brcmf_err("Failed to set F1 blocksize\n");
905c9aa7a91SArend Van Spriel 		sdio_release_host(sdiodev->func1);
906cb774bd3SDanny van Heumen 		return ret;
90705491d2cSKalle Valo 	}
9082a7621deSWright Feng 	switch (sdiodev->func2->device) {
909cb8e59ccSLinus Torvalds 	case SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373:
9102a7621deSWright Feng 		f2_blksz = SDIO_4373_FUNC2_BLOCKSIZE;
9112a7621deSWright Feng 		break;
9122a7621deSWright Feng 	case SDIO_DEVICE_ID_BROADCOM_4359:
9132bee4127SFrank Kao 	case SDIO_DEVICE_ID_BROADCOM_4354:
9142bee4127SFrank Kao 	case SDIO_DEVICE_ID_BROADCOM_4356:
9152bee4127SFrank Kao 		f2_blksz = SDIO_435X_FUNC2_BLOCKSIZE;
9162a7621deSWright Feng 		break;
917cc95fa81SDmitry Osipenko 	case SDIO_DEVICE_ID_BROADCOM_4329:
918cc95fa81SDmitry Osipenko 		f2_blksz = SDIO_4329_FUNC2_BLOCKSIZE;
919cc95fa81SDmitry Osipenko 		break;
9202a7621deSWright Feng 	default:
9212a7621deSWright Feng 		break;
9222a7621deSWright Feng 	}
9232a7621deSWright Feng 
924172f6854SChung-Hsien Hsu 	ret = sdio_set_block_size(sdiodev->func2, f2_blksz);
92505491d2cSKalle Valo 	if (ret) {
92605491d2cSKalle Valo 		brcmf_err("Failed to set F2 blocksize\n");
927c9aa7a91SArend Van Spriel 		sdio_release_host(sdiodev->func1);
928cb774bd3SDanny van Heumen 		return ret;
9292a7621deSWright Feng 	} else {
9302a7621deSWright Feng 		brcmf_dbg(SDIO, "set F2 blocksize to %d\n", f2_blksz);
93105491d2cSKalle Valo 	}
93205491d2cSKalle Valo 
93305491d2cSKalle Valo 	/* increase F2 timeout */
934c9aa7a91SArend Van Spriel 	sdiodev->func2->enable_timeout = SDIO_WAIT_F2RDY;
93505491d2cSKalle Valo 
93605491d2cSKalle Valo 	/* Enable Function 1 */
937c9aa7a91SArend Van Spriel 	ret = sdio_enable_func(sdiodev->func1);
938c9aa7a91SArend Van Spriel 	sdio_release_host(sdiodev->func1);
93905491d2cSKalle Valo 	if (ret) {
94005491d2cSKalle Valo 		brcmf_err("Failed to enable F1: err=%d\n", ret);
94105491d2cSKalle Valo 		goto out;
94205491d2cSKalle Valo 	}
94305491d2cSKalle Valo 
94405491d2cSKalle Valo 	ret = brcmf_sdiod_freezer_attach(sdiodev);
94505491d2cSKalle Valo 	if (ret)
94605491d2cSKalle Valo 		goto out;
94705491d2cSKalle Valo 
94805491d2cSKalle Valo 	/* try to attach to the target device */
94905491d2cSKalle Valo 	sdiodev->bus = brcmf_sdio_probe(sdiodev);
95005491d2cSKalle Valo 	if (!sdiodev->bus) {
95105491d2cSKalle Valo 		ret = -ENODEV;
95205491d2cSKalle Valo 		goto out;
95305491d2cSKalle Valo 	}
954c9aa7a91SArend Van Spriel 	brcmf_sdiod_host_fixup(sdiodev->func2->card->host);
95505491d2cSKalle Valo out:
95605491d2cSKalle Valo 	if (ret)
95705491d2cSKalle Valo 		brcmf_sdiod_remove(sdiodev);
95805491d2cSKalle Valo 
95905491d2cSKalle Valo 	return ret;
96005491d2cSKalle Valo }
96105491d2cSKalle Valo 
962f74f1ec2SArend van Spriel #define BRCMF_SDIO_DEVICE(dev_id, fw_vend) \
963da6d9c8eSArend van Spriel 	{ \
964da6d9c8eSArend van Spriel 		SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id), \
965f74f1ec2SArend van Spriel 		.driver_data = BRCMF_FWVENDOR_ ## fw_vend \
966da6d9c8eSArend van Spriel 	}
96705491d2cSKalle Valo 
968cc4cffc3SMarek Vasut #define CYW_SDIO_DEVICE(dev_id, fw_vend) \
969cc4cffc3SMarek Vasut 	{ \
970cc4cffc3SMarek Vasut 		SDIO_DEVICE(SDIO_VENDOR_ID_CYPRESS, dev_id), \
971cc4cffc3SMarek Vasut 		.driver_data = BRCMF_FWVENDOR_ ## fw_vend \
972cc4cffc3SMarek Vasut 	}
973cc4cffc3SMarek Vasut 
97405491d2cSKalle Valo /* devices we support, null terminated */
97505491d2cSKalle Valo static const struct sdio_device_id brcmf_sdmmc_ids[] = {
976f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143, WCC),
977f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241, WCC),
978f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329, WCC),
979f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330, WCC),
980f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334, WCC),
981f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340, WCC),
982f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341, WCC),
983f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362, WCC),
984f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43364, WCC),
985f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339, WCC),
986f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4339, WCC),
987f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43430, WCC),
988cc4cffc3SMarek Vasut 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43439, WCC),
989f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4345, WCC),
990f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43455, WCC),
991f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354, WCC),
992f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4356, WCC),
993f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4359, WCC),
994f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_4373, CYW),
995f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43012, CYW),
996f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43752, CYW),
997f74f1ec2SArend van Spriel 	BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_89359, CYW),
998cc4cffc3SMarek Vasut 	CYW_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_CYPRESS_43439, CYW),
99905491d2cSKalle Valo 	{ /* end: all zeroes */ }
100005491d2cSKalle Valo };
100105491d2cSKalle Valo MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
100205491d2cSKalle Valo 
100305491d2cSKalle Valo 
brcmf_sdiod_acpi_save_power_manageable(struct brcmf_sdio_dev * sdiodev)1004e4efa515SHans de Goede static void brcmf_sdiod_acpi_save_power_manageable(struct brcmf_sdio_dev *sdiodev)
100505491d2cSKalle Valo {
100605491d2cSKalle Valo #if IS_ENABLED(CONFIG_ACPI)
100705491d2cSKalle Valo 	struct acpi_device *adev;
100805491d2cSKalle Valo 
1009e4efa515SHans de Goede 	adev = ACPI_COMPANION(&sdiodev->func1->dev);
101005491d2cSKalle Valo 	if (adev)
1011e4efa515SHans de Goede 		sdiodev->func1_power_manageable = adev->flags.power_manageable;
1012e4efa515SHans de Goede 
1013e4efa515SHans de Goede 	adev = ACPI_COMPANION(&sdiodev->func2->dev);
1014e4efa515SHans de Goede 	if (adev)
1015e4efa515SHans de Goede 		sdiodev->func2_power_manageable = adev->flags.power_manageable;
1016e4efa515SHans de Goede #endif
1017e4efa515SHans de Goede }
1018e4efa515SHans de Goede 
brcmf_sdiod_acpi_set_power_manageable(struct brcmf_sdio_dev * sdiodev,int enable)1019e4efa515SHans de Goede static void brcmf_sdiod_acpi_set_power_manageable(struct brcmf_sdio_dev *sdiodev,
1020e4efa515SHans de Goede 						  int enable)
1021e4efa515SHans de Goede {
1022e4efa515SHans de Goede #if IS_ENABLED(CONFIG_ACPI)
1023e4efa515SHans de Goede 	struct acpi_device *adev;
1024e4efa515SHans de Goede 
1025e4efa515SHans de Goede 	adev = ACPI_COMPANION(&sdiodev->func1->dev);
1026e4efa515SHans de Goede 	if (adev)
1027e4efa515SHans de Goede 		adev->flags.power_manageable = enable ? sdiodev->func1_power_manageable : 0;
1028e4efa515SHans de Goede 
1029e4efa515SHans de Goede 	adev = ACPI_COMPANION(&sdiodev->func2->dev);
1030e4efa515SHans de Goede 	if (adev)
1031e4efa515SHans de Goede 		adev->flags.power_manageable = enable ? sdiodev->func2_power_manageable : 0;
103205491d2cSKalle Valo #endif
103305491d2cSKalle Valo }
103405491d2cSKalle Valo 
brcmf_ops_sdio_probe(struct sdio_func * func,const struct sdio_device_id * id)103505491d2cSKalle Valo static int brcmf_ops_sdio_probe(struct sdio_func *func,
103605491d2cSKalle Valo 				const struct sdio_device_id *id)
103705491d2cSKalle Valo {
103805491d2cSKalle Valo 	int err;
103905491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev;
104005491d2cSKalle Valo 	struct brcmf_bus *bus_if;
104105491d2cSKalle Valo 
104260fc756fSHans de Goede 	if (!id) {
104360fc756fSHans de Goede 		dev_err(&func->dev, "Error no sdio_device_id passed for %x:%x\n", func->vendor, func->device);
104460fc756fSHans de Goede 		return -ENODEV;
104560fc756fSHans de Goede 	}
104660fc756fSHans de Goede 
104705491d2cSKalle Valo 	brcmf_dbg(SDIO, "Enter\n");
104805491d2cSKalle Valo 	brcmf_dbg(SDIO, "Class=%x\n", func->class);
104905491d2cSKalle Valo 	brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
105005491d2cSKalle Valo 	brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
105105491d2cSKalle Valo 	brcmf_dbg(SDIO, "Function#: %d\n", func->num);
105205491d2cSKalle Valo 
1053508422f3SIan Molton 	/* Set MMC_QUIRK_LENIENT_FN0 for this card */
1054508422f3SIan Molton 	func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
1055508422f3SIan Molton 
105605491d2cSKalle Valo 	/* Consume func num 1 but dont do anything with it. */
105705491d2cSKalle Valo 	if (func->num == 1)
105805491d2cSKalle Valo 		return 0;
105905491d2cSKalle Valo 
106005491d2cSKalle Valo 	/* Ignore anything but func 2 */
106105491d2cSKalle Valo 	if (func->num != 2)
106205491d2cSKalle Valo 		return -ENODEV;
106305491d2cSKalle Valo 
106405491d2cSKalle Valo 	bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL);
106505491d2cSKalle Valo 	if (!bus_if)
106605491d2cSKalle Valo 		return -ENOMEM;
106705491d2cSKalle Valo 	sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL);
106805491d2cSKalle Valo 	if (!sdiodev) {
106905491d2cSKalle Valo 		kfree(bus_if);
107005491d2cSKalle Valo 		return -ENOMEM;
107105491d2cSKalle Valo 	}
107205491d2cSKalle Valo 
107305491d2cSKalle Valo 	/* store refs to functions used. mmc_card does
107405491d2cSKalle Valo 	 * not hold the F0 function pointer.
107505491d2cSKalle Valo 	 */
1076c9aa7a91SArend Van Spriel 	sdiodev->func1 = func->card->sdio_func[0];
1077c9aa7a91SArend Van Spriel 	sdiodev->func2 = func;
107805491d2cSKalle Valo 
107905491d2cSKalle Valo 	sdiodev->bus_if = bus_if;
108005491d2cSKalle Valo 	bus_if->bus_priv.sdio = sdiodev;
108105491d2cSKalle Valo 	bus_if->proto_type = BRCMF_PROTO_BCDC;
1082da6d9c8eSArend van Spriel 	bus_if->fwvid = id->driver_data;
108305491d2cSKalle Valo 	dev_set_drvdata(&func->dev, bus_if);
1084c9aa7a91SArend Van Spriel 	dev_set_drvdata(&sdiodev->func1->dev, bus_if);
1085c9aa7a91SArend Van Spriel 	sdiodev->dev = &sdiodev->func1->dev;
108605491d2cSKalle Valo 
1087e4efa515SHans de Goede 	brcmf_sdiod_acpi_save_power_manageable(sdiodev);
108805491d2cSKalle Valo 	brcmf_sdiod_change_state(sdiodev, BRCMF_SDIOD_DOWN);
108905491d2cSKalle Valo 
109005491d2cSKalle Valo 	brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n");
109105491d2cSKalle Valo 	err = brcmf_sdiod_probe(sdiodev);
109205491d2cSKalle Valo 	if (err) {
109305491d2cSKalle Valo 		brcmf_err("F2 error, probe failed %d...\n", err);
109405491d2cSKalle Valo 		goto fail;
109505491d2cSKalle Valo 	}
109605491d2cSKalle Valo 
109705491d2cSKalle Valo 	brcmf_dbg(SDIO, "F2 init completed...\n");
109805491d2cSKalle Valo 	return 0;
109905491d2cSKalle Valo 
110005491d2cSKalle Valo fail:
110105491d2cSKalle Valo 	dev_set_drvdata(&func->dev, NULL);
1102c9aa7a91SArend Van Spriel 	dev_set_drvdata(&sdiodev->func1->dev, NULL);
110305491d2cSKalle Valo 	kfree(sdiodev);
110405491d2cSKalle Valo 	kfree(bus_if);
110505491d2cSKalle Valo 	return err;
110605491d2cSKalle Valo }
110705491d2cSKalle Valo 
brcmf_ops_sdio_remove(struct sdio_func * func)110805491d2cSKalle Valo static void brcmf_ops_sdio_remove(struct sdio_func *func)
110905491d2cSKalle Valo {
111005491d2cSKalle Valo 	struct brcmf_bus *bus_if;
111105491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev;
111205491d2cSKalle Valo 
111305491d2cSKalle Valo 	brcmf_dbg(SDIO, "Enter\n");
111405491d2cSKalle Valo 	brcmf_dbg(SDIO, "sdio vendor ID: 0x%04x\n", func->vendor);
111505491d2cSKalle Valo 	brcmf_dbg(SDIO, "sdio device ID: 0x%04x\n", func->device);
111605491d2cSKalle Valo 	brcmf_dbg(SDIO, "Function: %d\n", func->num);
111705491d2cSKalle Valo 
111805491d2cSKalle Valo 	bus_if = dev_get_drvdata(&func->dev);
111905491d2cSKalle Valo 	if (bus_if) {
112005491d2cSKalle Valo 		sdiodev = bus_if->bus_priv.sdio;
1121b7467401SChristian Daudt 
1122b7467401SChristian Daudt 		/* start by unregistering irqs */
1123b7467401SChristian Daudt 		brcmf_sdiod_intr_unregister(sdiodev);
1124b7467401SChristian Daudt 
1125b7467401SChristian Daudt 		if (func->num != 1)
1126b7467401SChristian Daudt 			return;
1127b7467401SChristian Daudt 
1128b7467401SChristian Daudt 		/* only proceed with rest of cleanup if func 1 */
112905491d2cSKalle Valo 		brcmf_sdiod_remove(sdiodev);
113005491d2cSKalle Valo 
1131c9aa7a91SArend Van Spriel 		dev_set_drvdata(&sdiodev->func1->dev, NULL);
1132c9aa7a91SArend Van Spriel 		dev_set_drvdata(&sdiodev->func2->dev, NULL);
113305491d2cSKalle Valo 
113405491d2cSKalle Valo 		kfree(bus_if);
113505491d2cSKalle Valo 		kfree(sdiodev);
113605491d2cSKalle Valo 	}
113705491d2cSKalle Valo 
113805491d2cSKalle Valo 	brcmf_dbg(SDIO, "Exit\n");
113905491d2cSKalle Valo }
114005491d2cSKalle Valo 
brcmf_sdio_wowl_config(struct device * dev,bool enabled)114105491d2cSKalle Valo void brcmf_sdio_wowl_config(struct device *dev, bool enabled)
114205491d2cSKalle Valo {
114305491d2cSKalle Valo 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
114405491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
114592cadeddSUlf Hansson 	mmc_pm_flag_t pm_caps = sdio_get_host_pm_caps(sdiodev->func1);
114605491d2cSKalle Valo 
114792cadeddSUlf Hansson 	/* Power must be preserved to be able to support WOWL. */
114892cadeddSUlf Hansson 	if (!(pm_caps & MMC_PM_KEEP_POWER))
114992cadeddSUlf Hansson 		goto notsup;
115092cadeddSUlf Hansson 
115192cadeddSUlf Hansson 	if (sdiodev->settings->bus.sdio.oob_irq_supported ||
115292cadeddSUlf Hansson 	    pm_caps & MMC_PM_WAKE_SDIO_IRQ) {
1153e4efa515SHans de Goede 		/* Stop ACPI from turning off the device when wowl is enabled */
1154e4efa515SHans de Goede 		brcmf_sdiod_acpi_set_power_manageable(sdiodev, !enabled);
115505491d2cSKalle Valo 		sdiodev->wowl_enabled = enabled;
115692cadeddSUlf Hansson 		brcmf_dbg(SDIO, "Configuring WOWL, enabled=%d\n", enabled);
115792cadeddSUlf Hansson 		return;
115892cadeddSUlf Hansson 	}
115992cadeddSUlf Hansson 
116092cadeddSUlf Hansson notsup:
116192cadeddSUlf Hansson 	brcmf_dbg(SDIO, "WOWL not supported\n");
116205491d2cSKalle Valo }
116305491d2cSKalle Valo 
brcmf_ops_sdio_suspend(struct device * dev)116405491d2cSKalle Valo static int brcmf_ops_sdio_suspend(struct device *dev)
116505491d2cSKalle Valo {
116605491d2cSKalle Valo 	struct sdio_func *func;
116705491d2cSKalle Valo 	struct brcmf_bus *bus_if;
116805491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev;
116992cadeddSUlf Hansson 	mmc_pm_flag_t sdio_flags;
1170*ec7315e6SMatthias Proske 	bool cap_power_off;
1171e0ae4bacSAdrian Ratiu 	int ret = 0;
117205491d2cSKalle Valo 
117305491d2cSKalle Valo 	func = container_of(dev, struct sdio_func, dev);
117405491d2cSKalle Valo 	brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
117500eb62cfSIan Molton 	if (func->num != 1)
117605491d2cSKalle Valo 		return 0;
117705491d2cSKalle Valo 
1178*ec7315e6SMatthias Proske 	cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);
117905491d2cSKalle Valo 
118005491d2cSKalle Valo 	bus_if = dev_get_drvdata(dev);
118105491d2cSKalle Valo 	sdiodev = bus_if->bus_priv.sdio;
118205491d2cSKalle Valo 
1183*ec7315e6SMatthias Proske 	if (sdiodev->wowl_enabled || !cap_power_off) {
118405491d2cSKalle Valo 		brcmf_sdiod_freezer_on(sdiodev);
118505491d2cSKalle Valo 		brcmf_sdio_wd_timer(sdiodev->bus, 0);
118605491d2cSKalle Valo 
118705491d2cSKalle Valo 		sdio_flags = MMC_PM_KEEP_POWER;
1188*ec7315e6SMatthias Proske 
1189*ec7315e6SMatthias Proske 		if (sdiodev->wowl_enabled) {
1190af5b5e62SHante Meuleman 			if (sdiodev->settings->bus.sdio.oob_irq_supported)
1191af5b5e62SHante Meuleman 				enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
119205491d2cSKalle Valo 			else
119305491d2cSKalle Valo 				sdio_flags |= MMC_PM_WAKE_SDIO_IRQ;
1194*ec7315e6SMatthias Proske 		}
1195e0ae4bacSAdrian Ratiu 
1196c9aa7a91SArend Van Spriel 		if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags))
119705491d2cSKalle Valo 			brcmf_err("Failed to set pm_flags %x\n", sdio_flags);
1198e0ae4bacSAdrian Ratiu 
1199e0ae4bacSAdrian Ratiu 	} else {
1200e0ae4bacSAdrian Ratiu 		/* power will be cut so remove device, probe again in resume */
1201e0ae4bacSAdrian Ratiu 		brcmf_sdiod_intr_unregister(sdiodev);
1202e0ae4bacSAdrian Ratiu 		ret = brcmf_sdiod_remove(sdiodev);
1203e0ae4bacSAdrian Ratiu 		if (ret)
1204e0ae4bacSAdrian Ratiu 			brcmf_err("Failed to remove device on suspend\n");
1205e0ae4bacSAdrian Ratiu 	}
1206e0ae4bacSAdrian Ratiu 
1207e0ae4bacSAdrian Ratiu 	return ret;
120805491d2cSKalle Valo }
120905491d2cSKalle Valo 
brcmf_ops_sdio_resume(struct device * dev)121005491d2cSKalle Valo static int brcmf_ops_sdio_resume(struct device *dev)
121105491d2cSKalle Valo {
121205491d2cSKalle Valo 	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
121305491d2cSKalle Valo 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
121405491d2cSKalle Valo 	struct sdio_func *func = container_of(dev, struct sdio_func, dev);
1215e0ae4bacSAdrian Ratiu 	int ret = 0;
1216*ec7315e6SMatthias Proske 	bool cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD);
121705491d2cSKalle Valo 
121805491d2cSKalle Valo 	brcmf_dbg(SDIO, "Enter: F%d\n", func->num);
121900eb62cfSIan Molton 	if (func->num != 2)
122005491d2cSKalle Valo 		return 0;
122105491d2cSKalle Valo 
1222*ec7315e6SMatthias Proske 	if (!sdiodev->wowl_enabled && cap_power_off) {
1223e0ae4bacSAdrian Ratiu 		/* bus was powered off and device removed, probe again */
1224e0ae4bacSAdrian Ratiu 		ret = brcmf_sdiod_probe(sdiodev);
1225e0ae4bacSAdrian Ratiu 		if (ret)
1226e0ae4bacSAdrian Ratiu 			brcmf_err("Failed to probe device on resume\n");
1227e0ae4bacSAdrian Ratiu 	} else {
1228*ec7315e6SMatthias Proske 		if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported)
1229a32de68eSDmitry Osipenko 			disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr);
1230a32de68eSDmitry Osipenko 
123105491d2cSKalle Valo 		brcmf_sdiod_freezer_off(sdiodev);
1232e0ae4bacSAdrian Ratiu 	}
1233e0ae4bacSAdrian Ratiu 
1234e0ae4bacSAdrian Ratiu 	return ret;
123505491d2cSKalle Valo }
123605491d2cSKalle Valo 
123702a186f1SPaul Cercueil static DEFINE_SIMPLE_DEV_PM_OPS(brcmf_sdio_pm_ops,
123802a186f1SPaul Cercueil 				brcmf_ops_sdio_suspend,
123902a186f1SPaul Cercueil 				brcmf_ops_sdio_resume);
124005491d2cSKalle Valo 
124105491d2cSKalle Valo static struct sdio_driver brcmf_sdmmc_driver = {
124205491d2cSKalle Valo 	.probe = brcmf_ops_sdio_probe,
124305491d2cSKalle Valo 	.remove = brcmf_ops_sdio_remove,
12448ea56be0SHante Meuleman 	.name = KBUILD_MODNAME,
124505491d2cSKalle Valo 	.id_table = brcmf_sdmmc_ids,
124605491d2cSKalle Valo 	.drv = {
124705491d2cSKalle Valo 		.owner = THIS_MODULE,
124802a186f1SPaul Cercueil 		.pm = pm_sleep_ptr(&brcmf_sdio_pm_ops),
12498e072168SArend Van Spriel 		.coredump = brcmf_dev_coredump,
125005491d2cSKalle Valo 	},
125105491d2cSKalle Valo };
125205491d2cSKalle Valo 
brcmf_sdio_register(void)1253419b4a14SGreg Kroah-Hartman int brcmf_sdio_register(void)
125405491d2cSKalle Valo {
1255419b4a14SGreg Kroah-Hartman 	return sdio_register_driver(&brcmf_sdmmc_driver);
125605491d2cSKalle Valo }
125705491d2cSKalle Valo 
brcmf_sdio_exit(void)125805491d2cSKalle Valo void brcmf_sdio_exit(void)
125905491d2cSKalle Valo {
126005491d2cSKalle Valo 	brcmf_dbg(SDIO, "Enter\n");
126105491d2cSKalle Valo 
126205491d2cSKalle Valo 	sdio_unregister_driver(&brcmf_sdmmc_driver);
126305491d2cSKalle Valo }
126405491d2cSKalle Valo 
1265