Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/net/wireless/libra/Makefile b/drivers/net/wireless/libra/Makefile
new file mode 100644
index 0000000..3c606ba
--- /dev/null
+++ b/drivers/net/wireless/libra/Makefile
@@ -0,0 +1,14 @@
+
+# Makefile for wlan sdio if driver
+
+librasdioif-objs += libra_sdioif.o
+
+ifdef CONFIG_ARCH_MSM8X60
+ librasdioif-objs += qcomwlan_pwrif.o
+endif
+
+ifdef CONFIG_ARCH_MSM7X27A
+ librasdioif-objs += qcomwlan7x27a_pwrif.o
+endif
+
+obj-$(CONFIG_LIBRA_SDIOIF) += librasdioif.o
diff --git a/drivers/net/wireless/libra/libra_sdioif.c b/drivers/net/wireless/libra/libra_sdioif.c
new file mode 100644
index 0000000..3955642
--- /dev/null
+++ b/drivers/net/wireless/libra/libra_sdioif.c
@@ -0,0 +1,481 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/libra_sdioif.h>
+#include <linux/delay.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+/* Libra SDIO function device */
+static struct sdio_func *libra_sdio_func;
+static struct mmc_host *libra_mmc_host;
+static int libra_mmc_host_index;
+
+/* SDIO Card ID / Device ID */
+static unsigned short libra_sdio_card_id;
+
+static suspend_handler_t *libra_suspend_hldr;
+static resume_handler_t *libra_resume_hldr;
+
+/**
+ * libra_sdio_configure() - Function to configure the SDIO device param
+ * @libra_sdio_rxhandler Rx handler
+ * @func_drv_fn Function driver function for special setup
+ * @funcdrv_timeout Function Enable timeout
+ * @blksize Block size
+ *
+ * Configure SDIO device, enable function and set block size
+ */
+int libra_sdio_configure(sdio_irq_handler_t libra_sdio_rxhandler,
+ void (*func_drv_fn)(int *status),
+ unsigned int funcdrv_timeout, unsigned int blksize)
+{
+ int err_ret = 0;
+ struct sdio_func *func = libra_sdio_func;
+
+ if (libra_sdio_func == NULL) {
+ printk(KERN_ERR "%s: Error SDIO card not detected\n", __func__);
+ goto cfg_error;
+ }
+
+ sdio_claim_host(func);
+
+ /* Currently block sizes are set here. */
+ func->max_blksize = blksize;
+ if (sdio_set_block_size(func, blksize)) {
+ printk(KERN_ERR "%s: Unable to set the block size.\n",
+ __func__);
+ sdio_release_host(func);
+ goto cfg_error;
+ }
+
+ /* Function driver specific configuration. */
+ if (func_drv_fn) {
+ (*func_drv_fn)(&err_ret);
+ if (err_ret) {
+ printk(KERN_ERR "%s: function driver provided configure function error=%d\n",
+ __func__, err_ret);
+ sdio_release_host(func);
+ goto cfg_error;
+ }
+ }
+
+ /* We set this based on the function card. */
+ func->enable_timeout = funcdrv_timeout;
+ err_ret = sdio_enable_func(func);
+ if (err_ret != 0) {
+ printk(KERN_ERR "%s: Unable to enable function %d\n",
+ __func__, err_ret);
+ sdio_release_host(func);
+ goto cfg_error;
+ }
+
+ if (sdio_claim_irq(func, libra_sdio_rxhandler)) {
+ sdio_disable_func(func);
+ printk(KERN_ERR "%s: Unable to claim irq.\n", __func__);
+ sdio_release_host(func);
+ goto cfg_error;
+ }
+
+ sdio_release_host(func);
+
+ return 0;
+
+cfg_error:
+ return -1;
+
+}
+EXPORT_SYMBOL(libra_sdio_configure);
+
+int libra_sdio_configure_suspend_resume(
+ suspend_handler_t *libra_sdio_suspend_hdlr,
+ resume_handler_t *libra_sdio_resume_hdlr)
+{
+ libra_suspend_hldr = libra_sdio_suspend_hdlr;
+ libra_resume_hldr = libra_sdio_resume_hdlr;
+ return 0;
+}
+EXPORT_SYMBOL(libra_sdio_configure_suspend_resume);
+
+/*
+ * libra_sdio_deconfigure() - Function to reset the SDIO device param
+ */
+void libra_sdio_deconfigure(struct sdio_func *func)
+{
+ if (NULL == libra_sdio_func)
+ return;
+
+ sdio_claim_host(func);
+ sdio_release_irq(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+}
+EXPORT_SYMBOL(libra_sdio_deconfigure);
+
+int libra_enable_sdio_irq(struct sdio_func *func, u8 enable)
+{
+ if (libra_mmc_host && libra_mmc_host->ops &&
+ libra_mmc_host->ops->enable_sdio_irq) {
+ libra_mmc_host->ops->enable_sdio_irq(libra_mmc_host, enable);
+ return 0;
+ }
+
+ printk(KERN_ERR "%s: Could not enable disable irq\n", __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(libra_enable_sdio_irq);
+
+int libra_disable_sdio_irq_capability(struct sdio_func *func, u8 disable)
+{
+ if (libra_mmc_host) {
+ if (disable)
+ libra_mmc_host->caps &= ~MMC_CAP_SDIO_IRQ;
+ else
+ libra_mmc_host->caps |= MMC_CAP_SDIO_IRQ;
+ return 0;
+ }
+ printk(KERN_ERR "%s: Could not change sdio capabilities to polling\n",
+ __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(libra_disable_sdio_irq_capability);
+
+/*
+ * libra_sdio_release_irq() - Function to release IRQ
+ */
+void libra_sdio_release_irq(struct sdio_func *func)
+{
+ if (NULL == libra_sdio_func)
+ return;
+
+ sdio_release_irq(func);
+}
+EXPORT_SYMBOL(libra_sdio_release_irq);
+
+/*
+ * libra_sdio_disable_func() - Function to disable sdio func
+ */
+void libra_sdio_disable_func(struct sdio_func *func)
+{
+ if (NULL == libra_sdio_func)
+ return;
+
+ sdio_disable_func(func);
+}
+EXPORT_SYMBOL(libra_sdio_disable_func);
+
+/*
+ * Return the SDIO Function device
+ */
+struct sdio_func *libra_getsdio_funcdev(void)
+{
+ return libra_sdio_func;
+}
+EXPORT_SYMBOL(libra_getsdio_funcdev);
+
+/*
+ * Set function driver as the private data for the function device
+ */
+void libra_sdio_setprivdata(struct sdio_func *sdio_func_dev,
+ void *padapter)
+{
+ if (NULL == libra_sdio_func)
+ return;
+
+ sdio_set_drvdata(sdio_func_dev, padapter);
+}
+EXPORT_SYMBOL(libra_sdio_setprivdata);
+
+/*
+ * Return private data of the function device.
+ */
+void *libra_sdio_getprivdata(struct sdio_func *sdio_func_dev)
+{
+ return sdio_get_drvdata(sdio_func_dev);
+}
+EXPORT_SYMBOL(libra_sdio_getprivdata);
+
+/*
+ * Function driver claims the SDIO device
+ */
+void libra_claim_host(struct sdio_func *sdio_func_dev,
+ pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
+{
+ if (NULL == libra_sdio_func)
+ return;
+
+ if (*curr_claimed == current_pid) {
+ atomic_inc(claim_count);
+ return;
+ }
+
+ /* Go ahead and claim the host if not locked by anybody. */
+ sdio_claim_host(sdio_func_dev);
+
+ *curr_claimed = current_pid;
+ atomic_inc(claim_count);
+
+}
+EXPORT_SYMBOL(libra_claim_host);
+
+/*
+ * Function driver releases the SDIO device
+ */
+void libra_release_host(struct sdio_func *sdio_func_dev,
+ pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
+{
+
+ if (NULL == libra_sdio_func)
+ return;
+
+ if (*curr_claimed != current_pid) {
+ /* Dont release */
+ return;
+ }
+
+ atomic_dec(claim_count);
+ if (atomic_read(claim_count) == 0) {
+ *curr_claimed = 0;
+ sdio_release_host(sdio_func_dev);
+ }
+}
+EXPORT_SYMBOL(libra_release_host);
+
+void libra_sdiocmd52(struct sdio_func *sdio_func_dev, unsigned int addr,
+ u8 *byte_var, int write, int *err_ret)
+{
+ if (write)
+ sdio_writeb(sdio_func_dev, byte_var[0], addr, err_ret);
+ else
+ byte_var[0] = sdio_readb(sdio_func_dev, addr, err_ret);
+}
+EXPORT_SYMBOL(libra_sdiocmd52);
+
+u8 libra_sdio_readsb(struct sdio_func *func, void *dst,
+ unsigned int addr, int count)
+{
+ return sdio_readsb(func, dst, addr, count);
+}
+EXPORT_SYMBOL(libra_sdio_readsb);
+
+int libra_sdio_memcpy_fromio(struct sdio_func *func,
+ void *dst, unsigned int addr, int count)
+{
+ return sdio_memcpy_fromio(func, dst, addr, count);
+}
+EXPORT_SYMBOL(libra_sdio_memcpy_fromio);
+
+int libra_sdio_writesb(struct sdio_func *func,
+ unsigned int addr, void *src, int count)
+{
+ return sdio_writesb(func, addr, src, count);
+}
+EXPORT_SYMBOL(libra_sdio_writesb);
+
+int libra_sdio_memcpy_toio(struct sdio_func *func,
+ unsigned int addr, void *src, int count)
+{
+ return sdio_memcpy_toio(func, addr, src, count);
+}
+EXPORT_SYMBOL(libra_sdio_memcpy_toio);
+
+int libra_detect_card_change(void)
+{
+ if (libra_mmc_host) {
+ if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
+ && (libra_mmc_host_index == libra_mmc_host->index)) {
+ mmc_detect_change(libra_mmc_host, 0);
+ return 0;
+ }
+ }
+
+ printk(KERN_ERR "%s: Could not trigger card change\n", __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(libra_detect_card_change);
+
+int libra_sdio_enable_polling(void)
+{
+ if (libra_mmc_host) {
+ if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
+ && (libra_mmc_host_index == libra_mmc_host->index)) {
+ libra_mmc_host->caps |= MMC_CAP_NEEDS_POLL;
+ mmc_detect_change(libra_mmc_host, 0);
+ return 0;
+ }
+ }
+
+ printk(KERN_ERR "%s: Could not trigger SDIO scan\n", __func__);
+ return -1;
+}
+EXPORT_SYMBOL(libra_sdio_enable_polling);
+
+void libra_sdio_set_clock(struct sdio_func *func, unsigned int clk_freq)
+{
+ struct mmc_host *host = func->card->host;
+ host->ios.clock = clk_freq;
+ host->ops->set_ios(host, &host->ios);
+
+}
+EXPORT_SYMBOL(libra_sdio_set_clock);
+
+/*
+ * API to get SDIO Device Card ID
+ */
+void libra_sdio_get_card_id(struct sdio_func *func, unsigned short *card_id)
+{
+ if (card_id)
+ *card_id = libra_sdio_card_id;
+}
+EXPORT_SYMBOL(libra_sdio_get_card_id);
+
+/*
+ * SDIO Probe
+ */
+static int libra_sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *sdio_dev_id)
+{
+ libra_mmc_host = func->card->host;
+ libra_mmc_host_index = libra_mmc_host->index;
+ libra_sdio_func = func;
+ libra_sdio_card_id = sdio_dev_id->device;
+
+ printk(KERN_INFO "%s: success with block size of %d device_id=0x%x\n",
+ __func__,
+ func->cur_blksize,
+ sdio_dev_id->device);
+
+ /* Turn off SDIO polling from now on */
+ libra_mmc_host->caps &= ~MMC_CAP_NEEDS_POLL;
+ return 0;
+}
+
+static void libra_sdio_remove(struct sdio_func *func)
+{
+ libra_sdio_func = NULL;
+
+ printk(KERN_INFO "%s : Module removed.\n", __func__);
+}
+
+#ifdef CONFIG_PM
+static int libra_sdio_suspend(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ int ret = 0;
+
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+ if (ret) {
+ printk(KERN_ERR "%s: Error Host doesn't support the keep power capability\n" ,
+ __func__);
+ return ret;
+ }
+ if (libra_suspend_hldr) {
+ /* Disable SDIO IRQ when driver is being suspended */
+ libra_enable_sdio_irq(func, 0);
+ ret = libra_suspend_hldr(func);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: Libra driver is not able to suspend\n" , __func__);
+ /* Error - Restore SDIO IRQ */
+ libra_enable_sdio_irq(func, 1);
+ return ret;
+ }
+ }
+
+
+ return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+}
+
+static int libra_sdio_resume(struct device *dev)
+{
+ struct sdio_func *func = dev_to_sdio_func(dev);
+
+ if (libra_resume_hldr) {
+ libra_resume_hldr(func);
+ /* Restore SDIO IRQ */
+ libra_enable_sdio_irq(func, 1);
+ }
+
+ return 0;
+}
+#else
+#define libra_sdio_suspend 0
+#define libra_sdio_resume 0
+#endif
+
+static struct sdio_device_id libra_sdioid[] = {
+ {.class = 0, .vendor = LIBRA_MAN_ID, .device = LIBRA_REV_1_0_CARD_ID},
+ {.class = 0, .vendor = VOLANS_MAN_ID, .device = VOLANS_REV_2_0_CARD_ID},
+ {}
+};
+
+static const struct dev_pm_ops libra_sdio_pm_ops = {
+ .suspend = libra_sdio_suspend,
+ .resume = libra_sdio_resume,
+};
+
+static struct sdio_driver libra_sdiofn_driver = {
+ .name = "libra_sdiofn",
+ .id_table = libra_sdioid,
+ .probe = libra_sdio_probe,
+ .remove = libra_sdio_remove,
+ .drv.pm = &libra_sdio_pm_ops,
+};
+
+static int __init libra_sdioif_init(void)
+{
+ libra_sdio_func = NULL;
+ libra_mmc_host = NULL;
+ libra_mmc_host_index = -1;
+ libra_suspend_hldr = NULL;
+ libra_resume_hldr = NULL;
+
+ sdio_register_driver(&libra_sdiofn_driver);
+
+ printk(KERN_INFO "%s: Loaded Successfully\n", __func__);
+
+ return 0;
+}
+
+static void __exit libra_sdioif_exit(void)
+{
+ unsigned int attempts = 0;
+
+ if (!libra_detect_card_change()) {
+ do {
+ ++attempts;
+ msleep(500);
+ } while (libra_sdio_func != NULL && attempts < 3);
+ }
+
+ if (libra_sdio_func != NULL)
+ printk(KERN_ERR "%s: Card removal not detected\n", __func__);
+
+ sdio_unregister_driver(&libra_sdiofn_driver);
+
+ libra_sdio_func = NULL;
+ libra_mmc_host = NULL;
+ libra_mmc_host_index = -1;
+
+ printk(KERN_INFO "%s: Unloaded Successfully\n", __func__);
+}
+
+module_init(libra_sdioif_init);
+module_exit(libra_sdioif_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("WLAN SDIODriver");
diff --git a/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c b/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c
new file mode 100644
index 0000000..ca2680f
--- /dev/null
+++ b/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c
@@ -0,0 +1,172 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <mach/vreg.h>
+#include <linux/gpio.h>
+#include <mach/rpc_pmapp.h>
+#include <linux/err.h>
+#include <linux/qcomwlan7x27a_pwrif.h>
+
+#define WLAN_GPIO_EXT_POR_N 134
+
+static const char *id = "WLAN";
+
+enum {
+ WLAN_VREG_L17 = 0,
+ WLAN_VREG_S3,
+ WLAN_VREG_TCXO_L11,
+ WLAN_VREG_L19,
+ WLAN_VREG_L5,
+ WLAN_VREG_L6
+};
+
+struct wlan_vreg_info {
+ const char *vreg_id;
+ unsigned int vreg_level;
+ unsigned int pmapp_id;
+ unsigned int is_vreg_pin_controlled;
+ struct vreg *vreg;
+};
+
+
+static struct wlan_vreg_info vreg_info[] = {
+ {"bt", 3050, 56, 0, NULL},
+ {"msme1", 1800, 2, 0, NULL},
+ {"wlan_tcx0", 1800, 53, 0, NULL},
+ {"wlan4", 1200, 57, 0, NULL},
+ {"wlan2", 1350, 45, 0, NULL},
+ {"wlan3", 1200, 51, 0, NULL} };
+
+int chip_power_qrf6285(bool on)
+{
+ int rc = 0, index = 0;
+
+ if (on) {
+ rc = gpio_request(WLAN_GPIO_EXT_POR_N, "WLAN_DEEP_SLEEP_N");
+
+ if (rc) {
+ pr_err("WLAN reset GPIO %d request failed %d\n",
+ WLAN_GPIO_EXT_POR_N, rc);
+ goto fail;
+ }
+ rc = gpio_direction_output(WLAN_GPIO_EXT_POR_N, 1);
+ if (rc < 0) {
+ pr_err("WLAN reset GPIO %d set direction failed %d\n",
+ WLAN_GPIO_EXT_POR_N, rc);
+ goto fail_gpio_dir_out;
+ }
+ } else {
+ gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
+ gpio_free(WLAN_GPIO_EXT_POR_N);
+ }
+
+
+ for (index = 0; index < ARRAY_SIZE(vreg_info); index++) {
+ vreg_info[index].vreg = vreg_get(NULL,
+ vreg_info[index].vreg_id);
+ if (IS_ERR(vreg_info[index].vreg)) {
+ pr_err("%s:%s vreg get failed %ld\n",
+ __func__, vreg_info[index].vreg_id,
+ PTR_ERR(vreg_info[index].vreg));
+ rc = PTR_ERR(vreg_info[index].vreg);
+ if (on)
+ goto vreg_fail;
+ else
+ continue;
+ }
+ if (on) {
+ rc = vreg_set_level(vreg_info[index].vreg,
+ vreg_info[index].vreg_level);
+ if (rc) {
+ pr_err("%s:%s vreg set level failed %d\n",
+ __func__, vreg_info[index].vreg_id, rc);
+ goto vreg_fail;
+ }
+ if (vreg_info[index].is_vreg_pin_controlled) {
+ rc = pmapp_vreg_pincntrl_vote(id,
+ vreg_info[index].pmapp_id,
+ PMAPP_CLOCK_ID_A0, 1);
+ if (rc) {
+ pr_err("%s:%s pmapp_vreg_pincntrl_vote"
+ " for enable failed %d\n",
+ __func__,
+ vreg_info[index].vreg_id, rc);
+ goto vreg_fail;
+ }
+ } else {
+ rc = vreg_enable(vreg_info[index].vreg);
+ if (rc) {
+ pr_err("%s:%s vreg enable failed %d\n",
+ __func__,
+ vreg_info[index].vreg_id, rc);
+ goto vreg_fail;
+ }
+ }
+
+ if (WLAN_VREG_TCXO_L11 == index) {
+ /*
+ * Configure TCXO to be slave to
+ * WLAN_CLK_PWR_REQ
+` */
+ rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
+ PMAPP_CLOCK_VOTE_PIN_CTRL);
+ if (rc) {
+ pr_err("%s: Configuring TCXO to Pin"
+ " controllable failed %d\n",
+ __func__, rc);
+ goto vreg_clock_vote_fail;
+ }
+ }
+
+ } else {
+
+ if (vreg_info[index].is_vreg_pin_controlled) {
+ rc = pmapp_vreg_pincntrl_vote(id,
+ vreg_info[index].pmapp_id,
+ PMAPP_CLOCK_ID_A0, 0);
+ if (rc) {
+ pr_err("%s:%s pmapp_vreg_pincntrl_vote"
+ " for disable failed %d\n",
+ __func__,
+ vreg_info[index].vreg_id, rc);
+ }
+ } else {
+ rc = vreg_disable(vreg_info[index].vreg);
+ if (rc) {
+ pr_err("%s:%s vreg disable failed %d\n",
+ __func__,
+ vreg_info[index].vreg_id, rc);
+ }
+ }
+ }
+ }
+ return 0;
+vreg_fail:
+ index--;
+vreg_clock_vote_fail:
+ while (index > 0) {
+ rc = vreg_disable(vreg_info[index].vreg);
+ if (rc) {
+ pr_err("%s:%s vreg disable failed %d\n",
+ __func__, vreg_info[index].vreg_id, rc);
+ }
+ index--;
+ }
+ if (!on)
+ goto fail;
+fail_gpio_dir_out:
+ gpio_free(WLAN_GPIO_EXT_POR_N);
+fail:
+ return rc;
+}
+EXPORT_SYMBOL(chip_power_qrf6285);
diff --git a/drivers/net/wireless/libra/qcomwlan_pwrif.c b/drivers/net/wireless/libra/qcomwlan_pwrif.c
new file mode 100644
index 0000000..bb5e135
--- /dev/null
+++ b/drivers/net/wireless/libra/qcomwlan_pwrif.c
@@ -0,0 +1,256 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/qcomwlan_pwrif.h>
+
+#define GPIO_WLAN_DEEP_SLEEP_N 230
+#define WLAN_RESET_OUT 1
+#define WLAN_RESET 0
+
+static const char *id = "WLAN";
+
+/**
+ * vos_chip_power_qrf8615() - WLAN Power Up Seq for WCN1314 rev 2.0 on QRF 8615
+ * @on - Turn WLAN ON/OFF (1 or 0)
+ *
+ * Power up/down WLAN by turning on/off various regs and asserting/deasserting
+ * Power-on-reset pin. Also, put XO A0 buffer as slave to wlan_clk_pwr_req while
+ * turning ON WLAN and vice-versa.
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+int vos_chip_power_qrf8615(int on)
+{
+ static char wlan_on;
+ static const char *vregs_qwlan_name[] = {
+ "8058_l20",
+ "8058_l8",
+ "8901_s4",
+ "8901_lvs1",
+ "8901_l0",
+ "8058_s2",
+ "8058_s1",
+ };
+ static const int vregs_qwlan_val_min[] = {
+ 1800000,
+ 3050000,
+ 1225000,
+ 0,
+ 1200000,
+ 1300000,
+ 500000,
+ };
+ static const int vregs_qwlan_val_max[] = {
+ 1800000,
+ 3050000,
+ 1225000,
+ 0,
+ 1200000,
+ 1300000,
+ 1250000,
+ };
+ static const bool vregs_is_pin_controlled[] = {
+ 1,
+ 1,
+ 0,
+ 0,
+ 1,
+ 1,
+ 0,
+ };
+ static struct regulator *vregs_qwlan[ARRAY_SIZE(vregs_qwlan_name)];
+ static struct msm_xo_voter *wlan_clock;
+ int ret, i, rc = 0;
+
+ /* WLAN RESET and CLK settings */
+ if (on && !wlan_on) {
+ /*
+ * Program U12 GPIO expander pin IO1 to de-assert (drive 0)
+ * WLAN_EXT_POR_N to put WLAN in reset
+ */
+ rc = gpio_request(GPIO_WLAN_DEEP_SLEEP_N, "WLAN_DEEP_SLEEP_N");
+ if (rc) {
+ pr_err("WLAN reset GPIO %d request failed\n",
+ GPIO_WLAN_DEEP_SLEEP_N);
+ goto fail;
+ }
+ rc = gpio_direction_output(GPIO_WLAN_DEEP_SLEEP_N,
+ WLAN_RESET_OUT);
+ if (rc < 0) {
+ pr_err("WLAN reset GPIO %d set output direction failed",
+ GPIO_WLAN_DEEP_SLEEP_N);
+ goto fail_gpio_dir_out;
+ }
+
+ /* Configure TCXO to be slave to WLAN_CLK_PWR_REQ */
+ if (wlan_clock == NULL) {
+ wlan_clock = msm_xo_get(MSM_XO_TCXO_A0, id);
+ if (IS_ERR(wlan_clock)) {
+ pr_err("Failed to get TCXO_A0 voter (%ld)\n",
+ PTR_ERR(wlan_clock));
+ goto fail_gpio_dir_out;
+ }
+ }
+
+ rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_PIN_CTRL);
+ if (rc < 0) {
+ pr_err("Configuring TCXO to Pin controllable failed"
+ "(%d)\n", rc);
+ goto fail_xo_mode_vote;
+ }
+ } else if (!on && wlan_on) {
+ if (wlan_clock != NULL)
+ msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_OFF);
+ gpio_set_value_cansleep(GPIO_WLAN_DEEP_SLEEP_N, WLAN_RESET);
+ gpio_free(GPIO_WLAN_DEEP_SLEEP_N);
+ }
+
+ /* WLAN VREG settings */
+ for (i = 0; i < ARRAY_SIZE(vregs_qwlan_name); i++) {
+ if (vregs_qwlan[i] == NULL) {
+ vregs_qwlan[i] = regulator_get(NULL,
+ vregs_qwlan_name[i]);
+ if (IS_ERR(vregs_qwlan[i])) {
+ pr_err("regulator get of %s failed (%ld)\n",
+ vregs_qwlan_name[i],
+ PTR_ERR(vregs_qwlan[i]));
+ rc = PTR_ERR(vregs_qwlan[i]);
+ goto vreg_get_fail;
+ }
+ if (vregs_qwlan_val_min[i] || vregs_qwlan_val_max[i]) {
+ rc = regulator_set_voltage(vregs_qwlan[i],
+ vregs_qwlan_val_min[i],
+ vregs_qwlan_val_max[i]);
+ if (rc) {
+ pr_err("regulator_set_voltage(%s) failed\n",
+ vregs_qwlan_name[i]);
+ goto vreg_fail;
+ }
+ }
+ /* vote for pin control (if needed) */
+ if (vregs_is_pin_controlled[i]) {
+ rc = regulator_set_mode(vregs_qwlan[i],
+ REGULATOR_MODE_IDLE);
+ if (rc) {
+ pr_err("regulator_set_mode(%s) failed\n",
+ vregs_qwlan_name[i]);
+ goto vreg_fail;
+ }
+ }
+ }
+ if (on && !wlan_on) {
+ rc = regulator_enable(vregs_qwlan[i]);
+ if (rc < 0) {
+ pr_err("vreg %s enable failed (%d)\n",
+ vregs_qwlan_name[i], rc);
+ goto vreg_fail;
+ }
+ } else if (!on && wlan_on) {
+ rc = regulator_disable(vregs_qwlan[i]);
+ if (rc < 0) {
+ pr_err("vreg %s disable failed (%d)\n",
+ vregs_qwlan_name[i], rc);
+ goto vreg_fail;
+ }
+ }
+ }
+ if (on)
+ wlan_on = true;
+ else
+ wlan_on = false;
+ return 0;
+
+vreg_fail:
+ regulator_put(vregs_qwlan[i]);
+vreg_get_fail:
+ i--;
+ while (i) {
+ ret = !on ? regulator_enable(vregs_qwlan[i]) :
+ regulator_disable(vregs_qwlan[i]);
+ if (ret < 0) {
+ pr_err("vreg %s %s failed (%d) in err path\n",
+ vregs_qwlan_name[i],
+ !on ? "enable" : "disable", ret);
+ }
+ regulator_put(vregs_qwlan[i]);
+ i--;
+ }
+ if (!on)
+ goto fail;
+fail_xo_mode_vote:
+ msm_xo_put(wlan_clock);
+fail_gpio_dir_out:
+ gpio_free(GPIO_WLAN_DEEP_SLEEP_N);
+fail:
+ return rc;
+}
+EXPORT_SYMBOL(vos_chip_power_qrf8615);
+
+/**
+ * qcomwlan_pmic_xo_core_force_enable() - Force XO Core of PMIC to be ALWAYS ON
+ * @on - Force XO Core ON/OFF (1 or 0)
+ *
+ * The XO_CORE controls the XO feeding the TCXO buffers (A0, A1, etc.). WLAN
+ * wants to keep the XO core on even though our buffer A0 is in pin control
+ * because it can take a long time turn the XO back on and warm up the buffers.
+ * This helps in optimizing power in BMPS (power save) mode of WLAN.
+ * The WLAN driver wrapper function takes care that this API is not called
+ * consecutively.
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+int qcomwlan_pmic_xo_core_force_enable(int on)
+{
+ static struct msm_xo_voter *wlan_ps;
+ int rc = 0;
+
+ if (wlan_ps == NULL) {
+ wlan_ps = msm_xo_get(MSM_XO_CORE, id);
+ if (IS_ERR(wlan_ps)) {
+ pr_err("Failed to get XO CORE voter (%ld)\n",
+ PTR_ERR(wlan_ps));
+ goto fail;
+ }
+ }
+
+ if (on)
+ rc = msm_xo_mode_vote(wlan_ps, MSM_XO_MODE_ON);
+ else
+ rc = msm_xo_mode_vote(wlan_ps, MSM_XO_MODE_OFF);
+
+ if (rc < 0) {
+ pr_err("XO Core %s failed (%d)\n",
+ on ? "enable" : "disable", rc);
+ goto fail_xo_mode_vote;
+ }
+ return 0;
+fail_xo_mode_vote:
+ msm_xo_put(wlan_ps);
+fail:
+ return rc;
+}
+EXPORT_SYMBOL(qcomwlan_pmic_xo_core_force_enable);
+
+
+/**
+ * qcomwlan_freq_change_1p3v_supply() - function to change the freq for 1.3V RF supply.
+ * @freq - freq of the 1.3V Supply
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+
+int qcomwlan_freq_change_1p3v_supply(enum rpm_vreg_freq freq)
+{
+ return rpm_vreg_set_frequency(RPM_VREG_ID_PM8058_S2, freq);
+}
+EXPORT_SYMBOL(qcomwlan_freq_change_1p3v_supply);