M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
@@ -0,0 +1,14 @@
# Makefile for wlan sdio if driver
librasdioif-objs += libra_sdioif.o ../wcnss/qcomwlan_secif.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
@@ -0,0 +1,553 @@
/* Copyright (c) 2009-2012, The Linux Foundation. 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>
#include <linux/module.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;
/* completion variables */
struct completion gCard_rem_event_var;
EXPORT_SYMBOL(gCard_rem_event_var);
struct completion gShutdown_event_var;
EXPORT_SYMBOL(gShutdown_event_var);
static suspend_handler_t *libra_suspend_hldr;
static resume_handler_t *libra_resume_hldr;
static notify_card_removal_t *libra_notify_card_removal_hdlr;
static shutdown_handler_t *libra_sdio_shutdown_hdlr;
int libra_enable_sdio_irq_in_chip(struct sdio_func *func, u8 enable)
{
unsigned char reg = 0;
int err = 0;
sdio_claim_host(func);
/* Read the value into reg */
libra_sdiocmd52(func, SDIO_CCCR_IENx, &reg, 0, &err);
if (err)
printk(KERN_ERR "%s: Could not read SDIO_CCCR_IENx register "
"err=%d\n", __func__, err);
if (libra_mmc_host) {
if (enable) {
reg |= 1 << func->num;
reg |= 1;
} else {
reg &= ~(1 << func->num);
}
libra_sdiocmd52(func, SDIO_CCCR_IENx, &reg, 1, &err);
if (err)
printk(KERN_ERR "%s: Could not enable/disable irq "
"err=%d\n", __func__, err);
}
sdio_release_host(func);
return err;
}
EXPORT_SYMBOL(libra_enable_sdio_irq_in_chip);
/**
* 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;
}
libra_enable_sdio_irq_in_chip(func, 0);
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)
{
if (libra_notify_card_removal_hdlr)
libra_notify_card_removal_hdlr();
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 void libra_sdio_shutdown(struct device *dev)
{
if (libra_sdio_shutdown_hdlr) {
libra_sdio_shutdown_hdlr();
printk(KERN_INFO "%s : Notified shutdown event to Libra driver.\n",
__func__);
}
}
int libra_sdio_register_shutdown_hdlr(
shutdown_handler_t *libra_shutdown_hdlr)
{
libra_sdio_shutdown_hdlr = libra_shutdown_hdlr;
return 0;
}
EXPORT_SYMBOL(libra_sdio_register_shutdown_hdlr);
int libra_sdio_notify_card_removal(
notify_card_removal_t *libra_sdio_notify_card_removal_hdlr)
{
libra_notify_card_removal_hdlr = libra_sdio_notify_card_removal_hdlr;
return 0;
}
EXPORT_SYMBOL(libra_sdio_notify_card_removal);
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,
.drv.shutdown = libra_sdio_shutdown,
};
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;
libra_notify_card_removal_hdlr = NULL;
libra_sdio_shutdown_hdlr = 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");
@@ -0,0 +1,225 @@
/* Copyright (c) 2011, The Linux Foundation. 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 <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <mach/rpc_pmapp.h>
#include <linux/err.h>
#include <linux/qcomwlan7x27a_pwrif.h>
#include <linux/module.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 level_min;
unsigned int level_max;
unsigned int pmapp_id;
unsigned int is_vreg_pin_controlled;
struct regulator *reg;
};
static struct wlan_vreg_info vreg_info[] = {
{"bt", 3050000, 3050000, 21, 1, NULL},
{"msme1", 1800000, 1800000, 2, 0, NULL},
{"wlan_tcx0", 1800000, 1800000, 53, 0, NULL},
{"wlan4", 1200000, 1200000, 23, 0, NULL},
{"wlan2", 1350000, 1350000, 9, 1, NULL},
{"wlan3", 1200000, 1200000, 10, 1, NULL},
};
static int qrf6285_init_regs(void)
{
struct regulator_bulk_data regs[ARRAY_SIZE(vreg_info)];
int i, rc;
for (i = 0; i < ARRAY_SIZE(regs); i++) {
regs[i].supply = vreg_info[i].vreg_id;
regs[i].min_uV = vreg_info[i].level_min;
regs[i].max_uV = vreg_info[i].level_max;
}
rc = regulator_bulk_get(NULL, ARRAY_SIZE(regs), regs);
if (rc) {
pr_err("%s: could not get regulators: %d\n", __func__, rc);
goto out;
}
for (i = 0; i < ARRAY_SIZE(regs); i++)
vreg_info[i].reg = regs[i].consumer;
return 0;
out:
return rc;
}
int chip_power_qrf6285(bool on)
{
static bool init_done;
int rc = 0, index = 0;
if (unlikely(!init_done)) {
rc = qrf6285_init_regs();
if (rc)
return rc;
else
init_done = true;
}
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;
}
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
PMAPP_CLOCK_VOTE_ON);
if (rc) {
pr_err("%s: Configuring A0 to always"
" on failed %d\n", __func__, rc);
goto clock_vote_fail;
}
} else {
gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
rc = gpio_direction_input(WLAN_GPIO_EXT_POR_N);
if (rc) {
pr_err("WLAN reset GPIO %d set direction failed %d\n",
WLAN_GPIO_EXT_POR_N, rc);
}
gpio_free(WLAN_GPIO_EXT_POR_N);
rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
PMAPP_CLOCK_VOTE_OFF);
if (rc) {
pr_err("%s: Configuring A0 to turn OFF"
" failed %d\n", __func__, rc);
}
}
for (index = 0; index < ARRAY_SIZE(vreg_info); index++) {
if (on) {
rc = regulator_set_voltage(vreg_info[index].reg,
vreg_info[index].level_min,
vreg_info[index].level_max);
if (rc) {
pr_err("%s:%s set voltage failed %d\n",
__func__, vreg_info[index].vreg_id, rc);
goto vreg_fail;
}
rc = regulator_enable(vreg_info[index].reg);
if (rc) {
pr_err("%s:%s vreg enable failed %d\n",
__func__, vreg_info[index].vreg_id, rc);
goto vreg_fail;
}
if (vreg_info[index].is_vreg_pin_controlled) {
rc = pmapp_vreg_lpm_pincntrl_vote(id,
vreg_info[index].pmapp_id,
PMAPP_CLOCK_ID_A0, 1);
if (rc) {
pr_err("%s:%s pmapp_vreg_lpm_pincntrl"
" for enable failed %d\n",
__func__,
vreg_info[index].vreg_id, rc);
goto vreg_clock_vote_fail;
}
}
/*At this point CLK_PWR_REQ is high*/
if (WLAN_VREG_L6 == index) {
/*
* Configure A0 clock 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 A0 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_lpm_pincntrl_vote(id,
vreg_info[index].pmapp_id,
PMAPP_CLOCK_ID_A0, 0);
if (rc) {
pr_err("%s:%s pmapp_vreg_lpm_pincntrl"
" for disable failed %d\n",
__func__,
vreg_info[index].vreg_id, rc);
}
}
rc = regulator_disable(vreg_info[index].reg);
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 = regulator_disable(vreg_info[index].reg);
if (rc) {
pr_err("%s:%s vreg disable failed %d\n",
__func__, vreg_info[index].vreg_id, rc);
}
index--;
}
if (!on)
goto fail;
clock_vote_fail:
gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
rc = gpio_direction_input(WLAN_GPIO_EXT_POR_N);
if (rc) {
pr_err("WLAN reset GPIO %d set direction failed %d\n",
WLAN_GPIO_EXT_POR_N, rc);
}
fail_gpio_dir_out:
gpio_free(WLAN_GPIO_EXT_POR_N);
fail:
return rc;
}
EXPORT_SYMBOL(chip_power_qrf6285);
@@ -0,0 +1,359 @@
/* Copyright (c) 2010-2012, The Linux Foundation. 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>
#include <linux/export.h>
#define GPIO_WLAN_DEEP_SLEEP_N 230
#define GPIO_WLAN_DEEP_SLEEP_N_DRAGON 82
#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 char *vregs_qwlan_pc_name[] = {
"8058_l20_pc",
"8058_l8_pc",
NULL,
NULL,
"8901_l0_pc",
"8058_s2_pc",
NULL,
};
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 int vregs_qwlan_peek_current[] = {
4000,
150000,
60000,
0,
32000,
130000,
0,
};
static const bool vregs_is_pin_controlled_default[] = {
1,
1,
0,
0,
1,
1,
0,
};
static const bool vregs_is_pin_controlled_dragon[] = {
0,
0,
0,
0,
0,
1,
0,
};
bool const *vregs_is_pin_controlled;
static struct regulator *vregs_qwlan[ARRAY_SIZE(vregs_qwlan_name)];
static struct regulator *vregs_pc_qwlan[ARRAY_SIZE(vregs_qwlan_name)];
static struct msm_xo_voter *wlan_clock;
int ret, i, rc = 0;
unsigned wlan_gpio_deep_sleep = GPIO_WLAN_DEEP_SLEEP_N;
vregs_is_pin_controlled = vregs_is_pin_controlled_default;
if (machine_is_msm8x60_dragon()) {
wlan_gpio_deep_sleep = GPIO_WLAN_DEEP_SLEEP_N_DRAGON;
vregs_is_pin_controlled = vregs_is_pin_controlled_dragon;
}
/* 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(wlan_gpio_deep_sleep, "WLAN_DEEP_SLEEP_N");
if (rc) {
pr_err("WLAN reset GPIO %d request failed\n",
wlan_gpio_deep_sleep);
goto fail;
}
rc = gpio_direction_output(wlan_gpio_deep_sleep,
WLAN_RESET);
if (rc < 0) {
pr_err("WLAN reset GPIO %d set output direction failed",
wlan_gpio_deep_sleep);
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(wlan_gpio_deep_sleep, WLAN_RESET);
gpio_free(wlan_gpio_deep_sleep);
}
/* WLAN VREG settings */
for (i = 0; i < ARRAY_SIZE(vregs_qwlan_name); i++) {
if (on && !wlan_on) {
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]) {
vregs_pc_qwlan[i] = regulator_get(NULL,
vregs_qwlan_pc_name[i]);
if (IS_ERR(vregs_pc_qwlan[i])) {
pr_err("regulator get of %s failed "
"(%ld)\n",
vregs_qwlan_pc_name[i],
PTR_ERR(vregs_pc_qwlan[i]));
rc = PTR_ERR(vregs_pc_qwlan[i]);
goto vreg_fail;
}
}
if (vregs_qwlan_peek_current[i]) {
rc = regulator_set_optimum_mode(vregs_qwlan[i],
vregs_qwlan_peek_current[i]);
if (rc < 0)
pr_err("vreg %s set optimum mode"
" failed to %d (%d)\n",
vregs_qwlan_name[i], rc,
vregs_qwlan_peek_current[i]);
}
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;
}
if (vregs_is_pin_controlled[i]) {
rc = regulator_enable(vregs_pc_qwlan[i]);
if (rc < 0) {
pr_err("vreg %s enable failed (%d)\n",
vregs_qwlan_pc_name[i], rc);
goto vreg_fail;
}
}
} else if (!on && wlan_on) {
if (vregs_qwlan_peek_current[i]) {
/* For legacy reasons we pass 1mA current to
* put regulator in LPM mode.
*/
rc = regulator_set_optimum_mode(vregs_qwlan[i],
1000);
if (rc < 0)
pr_info("vreg %s set optimum mode"
"failed (%d)\n",
vregs_qwlan_name[i], rc);
rc = regulator_set_voltage(vregs_qwlan[i], 0 ,
vregs_qwlan_val_max[i]);
if (rc)
pr_err("regulator_set_voltage(%s)"
"failed (%d)\n",
vregs_qwlan_name[i], rc);
}
if (vregs_is_pin_controlled[i]) {
rc = regulator_disable(vregs_pc_qwlan[i]);
if (rc < 0) {
pr_err("vreg %s disable failed (%d)\n",
vregs_qwlan_pc_name[i], rc);
goto vreg_fail;
}
regulator_put(vregs_pc_qwlan[i]);
}
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;
}
regulator_put(vregs_qwlan[i]);
}
}
if (on) {
gpio_set_value_cansleep(wlan_gpio_deep_sleep, WLAN_RESET_OUT);
wlan_on = true;
}
else
wlan_on = false;
return 0;
vreg_fail:
regulator_put(vregs_qwlan[i]);
if (vregs_is_pin_controlled[i])
regulator_put(vregs_pc_qwlan[i]);
vreg_get_fail:
i--;
while (i >= 0) {
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);
}
if (vregs_is_pin_controlled[i]) {
ret = !on ? regulator_enable(vregs_pc_qwlan[i]) :
regulator_disable(vregs_pc_qwlan[i]);
if (ret < 0) {
pr_err("vreg %s %s failed (%d) in err path\n",
vregs_qwlan_pc_name[i],
!on ? "enable" : "disable", ret);
}
}
regulator_put(vregs_qwlan[i]);
if (vregs_is_pin_controlled[i])
regulator_put(vregs_pc_qwlan[i]);
i--;
}
if (!on)
goto fail;
fail_xo_mode_vote:
msm_xo_put(wlan_clock);
fail_gpio_dir_out:
gpio_free(wlan_gpio_deep_sleep);
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);