Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4eb3af7c authored by Hante Meuleman's avatar Hante Meuleman Committed by John W. Linville
Browse files

brcmfmac: Add wowl support for PCIE devices.



Add basic wowl (magic packet and disconnect) support. This patch adds
this support only for PCIE bus devices. This feature requires FW which
has support for wowl built in.

Reviewed-by: default avatarArend Van Spriel <arend@broadcom.com>
Reviewed-by: default avatarFranky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: default avatarPieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: default avatarDaniel (Deognyoun) Kim <dekim@broadcom.com>
Signed-off-by: default avatarHante Meuleman <meuleman@broadcom.com>
Signed-off-by: default avatarArend van Spriel <arend@broadcom.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 58b2251e
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ struct brcmf_bus_dcmd {
 * @txctl: transmit a control request message to dongle.
 * @rxctl: receive a control response message from dongle.
 * @gettxq: obtain a reference of bus transmit queue (optional).
 * @wowl_config: specify if dongle is configured for wowl when going to suspend
 *
 * This structure provides an abstract interface towards the
 * bus specific driver. For control messages to common driver
@@ -80,6 +81,7 @@ struct brcmf_bus_ops {
	int (*txctl)(struct device *dev, unsigned char *msg, uint len);
	int (*rxctl)(struct device *dev, unsigned char *msg, uint len);
	struct pktq * (*gettxq)(struct device *dev);
	void (*wowl_config)(struct device *dev, bool enabled);
};


@@ -114,6 +116,7 @@ struct brcmf_bus_msgbuf {
 * @dstats: dongle-based statistical data.
 * @dcmd_list: bus/device specific dongle initialization commands.
 * @chip: device identifier of the dongle chip.
 * @wowl_supported: is wowl supported by bus driver.
 * @chiprev: revision of the dongle chip.
 */
struct brcmf_bus {
@@ -131,6 +134,7 @@ struct brcmf_bus {
	u32 chip;
	u32 chiprev;
	bool always_use_fws_queue;
	bool wowl_supported;

	struct brcmf_bus_ops *ops;
	struct brcmf_bus_msgbuf *msgbuf;
@@ -177,6 +181,13 @@ struct pktq *brcmf_bus_gettxq(struct brcmf_bus *bus)
	return bus->ops->gettxq(bus->dev);
}

static inline
void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled)
{
	if (bus->ops->wowl_config)
		bus->ops->wowl_config(bus->dev, enabled);
}

static inline bool brcmf_bus_ready(struct brcmf_bus *bus)
{
	return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA;
+2 −0
Original line number Diff line number Diff line
@@ -107,6 +107,8 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
	struct brcmf_if *ifp = drvr->iflist[0];

	brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan");
	if (drvr->bus_if->wowl_supported)
		brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");

	/* set chip related quirks */
	switch (drvr->bus_if->chip) {
+2 −1
Original line number Diff line number Diff line
@@ -22,7 +22,8 @@
 * MCHAN: multi-channel for concurrent P2P.
 */
#define BRCMF_FEAT_LIST \
	BRCMF_FEAT_DEF(MCHAN)
	BRCMF_FEAT_DEF(MCHAN) \
	BRCMF_FEAT_DEF(WOWL)
/*
 * Quirks:
 *
+56 −0
Original line number Diff line number Diff line
@@ -53,6 +53,62 @@
#define BRCMF_OBSS_COEX_OFF		0
#define BRCMF_OBSS_COEX_ON		1

/* WOWL bits */
/* Wakeup on Magic packet: */
#define WL_WOWL_MAGIC			(1 << 0)
/* Wakeup on Netpattern */
#define WL_WOWL_NET			(1 << 1)
/* Wakeup on loss-of-link due to Disassoc/Deauth: */
#define WL_WOWL_DIS			(1 << 2)
/* Wakeup on retrograde TSF: */
#define WL_WOWL_RETR			(1 << 3)
/* Wakeup on loss of beacon: */
#define WL_WOWL_BCN			(1 << 4)
/* Wakeup after test: */
#define WL_WOWL_TST			(1 << 5)
/* Wakeup after PTK refresh: */
#define WL_WOWL_M1			(1 << 6)
/* Wakeup after receipt of EAP-Identity Req: */
#define WL_WOWL_EAPID			(1 << 7)
/* Wakeind via PME(0) or GPIO(1): */
#define WL_WOWL_PME_GPIO		(1 << 8)
/* need tkip phase 1 key to be updated by the driver: */
#define WL_WOWL_NEEDTKIP1		(1 << 9)
/* enable wakeup if GTK fails: */
#define WL_WOWL_GTK_FAILURE		(1 << 10)
/* support extended magic packets: */
#define WL_WOWL_EXTMAGPAT		(1 << 11)
/* support ARP/NS/keepalive offloading: */
#define WL_WOWL_ARPOFFLOAD		(1 << 12)
/* read protocol version for EAPOL frames: */
#define WL_WOWL_WPA2			(1 << 13)
/* If the bit is set, use key rotaton: */
#define WL_WOWL_KEYROT			(1 << 14)
/* If the bit is set, frm received was bcast frame: */
#define WL_WOWL_BCAST			(1 << 15)
/* If the bit is set, scan offload is enabled: */
#define WL_WOWL_SCANOL			(1 << 16)
/* Wakeup on tcpkeep alive timeout: */
#define WL_WOWL_TCPKEEP_TIME		(1 << 17)
/* Wakeup on mDNS Conflict Resolution: */
#define WL_WOWL_MDNS_CONFLICT		(1 << 18)
/* Wakeup on mDNS Service Connect: */
#define WL_WOWL_MDNS_SERVICE		(1 << 19)
/* tcp keepalive got data: */
#define WL_WOWL_TCPKEEP_DATA		(1 << 20)
/* Firmware died in wowl mode: */
#define WL_WOWL_FW_HALT			(1 << 21)
/* Enable detection of radio button changes: */
#define WL_WOWL_ENAB_HWRADIO		(1 << 22)
/* Offloads detected MIC failure(s): */
#define WL_WOWL_MIC_FAIL		(1 << 23)
/* Wakeup in Unassociated state (Net/Magic Pattern): */
#define WL_WOWL_UNASSOC			(1 << 24)
/* Wakeup if received matched secured pattern: */
#define WL_WOWL_SECURE			(1 << 25)
/* Link Down indication in WoWL mode: */
#define WL_WOWL_LINKDOWN		(1 << 31)

/* join preference types for join_pref iovar */
enum brcmf_join_pref_types {
	BRCMF_JOIN_PREF_RSSI = 1,
+61 −13
Original line number Diff line number Diff line
@@ -165,6 +165,8 @@ enum brcmf_pcie_state {

#define BRCMF_H2D_HOST_D3_INFORM		0x00000001
#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

@@ -243,6 +245,7 @@ struct brcmf_pciedev_info {
	wait_queue_head_t mbdata_resp_wait;
	bool mbdata_completed;
	bool irq_allocated;
	bool wowl_enabled;
};

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)
{
	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);
		i++;
		if (i > 100)
			break;
			return -EIO;
		cur_htod_mb_data = brcmf_pcie_read_tcm32(devinfo, addr);
	}

	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);

	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 = {
	.txdata = brcmf_pcie_tx,
	.stop = brcmf_pcie_down,
	.txctl = brcmf_pcie_tx_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->proto_type = BRCMF_PROTO_MSGBUF;
	bus->chip = devinfo->coreid;
	bus->wowl_supported = pci_pme_capable(pdev, PCI_D3hot);
	dev_set_drvdata(&pdev->dev, bus);

	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");
		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);
	if (err) {
	if (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;
	}

	return pci_prepare_to_sleep(pdev);
}


static int brcmf_pcie_resume(struct pci_dev *pdev)
{
	struct brcmf_pciedev_info *devinfo;
	struct brcmf_bus *bus;
	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);
	if (err) {
		brcmf_err("pci_set_power_state failed, err=%d\n", err);
		return err;
		goto cleanup;
	}
	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);
	if (err)
		brcmf_err("probe after resume failed, err=%d\n", err);
Loading