/* * Copyright (C) 2013 Spreadtrum Communications Inc. * * Filename : slp_mgr.c * Abstract : This file is a implementation for sleep manager * * Authors : sam.sun * * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * 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 #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include "../sdio/sdiohal.h" #include "slp_mgr.h" #include "slp_sdio.h" #include "wcn_glb.h" struct slp_mgr_t slp_mgr; struct slp_mgr_t *slp_get_info(void) { return &slp_mgr; } void slp_mgr_drv_sleep(enum slp_subsys subsys, bool enable) { mutex_lock(&(slp_mgr.drv_slp_lock)); if (enable) slp_mgr.active_module &= ~(BIT(subsys)); else slp_mgr.active_module |= (BIT(subsys)); if (slp_mgr.active_module == 0) { /* If pubint pin is valid (not used as bt_wake_host pin), * packet mode will enter sleep when pub int irq coming. */ if (marlin_get_bt_wl_wake_host_en()) { slp_allow_sleep(); atomic_set(&(slp_mgr.cp2_state), STAY_SLPING); } else if (subsys > PACKER_DT_RX) { slp_allow_sleep(); atomic_set(&(slp_mgr.cp2_state), STAY_SLPING); } } mutex_unlock(&(slp_mgr.drv_slp_lock)); } int slp_mgr_wakeup(enum slp_subsys subsys) { unsigned char slp_sts; int ret; int do_dump = 0; ktime_t time_end; #if KERNEL_VERSION(3, 16, 75) > LINUX_VERSION_CODE ktime_t time_cmp; #endif mutex_lock(&(slp_mgr.wakeup_lock)); if (STAY_SLPING == (atomic_read(&(slp_mgr.cp2_state)))) { ap_wakeup_cp(); time_end = ktime_add_ns(ktime_get(), 30 * NSEC_PER_MSEC); #ifndef CONFIG_CHECK_DRIVER_BY_CHIPID #ifdef CONFIG_UWE5623 /*select btwf_slp_status*/ sprdwcn_bus_aon_writeb(REG_CP_PMU_SEL_CTL, 6); #endif #else /*CONFIG_CHECK_DRIVER_BY_CHIPID*/ if (wcn_get_chip_model() == WCN_CHIP_MARLIN3E) sprdwcn_bus_aon_writeb(REG_CP_PMU_SEL_CTL, 6); #endif while (1) { ret = sprdwcn_bus_aon_readb(REG_BTWF_SLP_STS, &slp_sts); if (ret < 0) { SLP_MGR_ERR("read slp sts err:%d", ret); usleep_range(40, 80); goto try_timeout; } slp_sts &= SLEEP_STATUS_FLAG; #ifndef CONFIG_CHECK_DRIVER_BY_CHIPID if ((slp_sts != BTWF_IN_DEEPSLEEP) && (slp_sts != BTWF_IN_DEEPSLEEP_XLT_ON) && #if defined(CONFIG_UWE5622) || defined(CONFIG_UWE5623) (slp_sts != BTWF_PLL_PWR_WAIT) && (slp_sts != BTWF_XLT_WAIT) && (slp_sts != BTWF_XLTBUF_WAIT)) #else (slp_sts != BTWF_IN_DEEPSLEEP_XLT_ON)) #endif { #if defined(CONFIG_UWE5623) unsigned int reg_val = 0; sprdwcn_bus_reg_read(CP_WAKE_STATUS, ®_val, 4); if ((reg_val & BIT(31)) == 0) #endif break; } #else if ((slp_sts != BTWF_IN_DEEPSLEEP) && (slp_sts != BTWF_IN_DEEPSLEEP_XLT_ON) && ((((wcn_get_chip_model() == WCN_CHIP_MARLIN3E) || (wcn_get_chip_model() == WCN_CHIP_MARLIN3L)) && (slp_sts != BTWF_PLL_PWR_WAIT) && (slp_sts != BTWF_XLT_WAIT) && (slp_sts != BTWF_XLTBUF_WAIT)) || (wcn_get_chip_model() == WCN_CHIP_MARLIN3))) { if (wcn_get_chip_model() == WCN_CHIP_MARLIN3E) { unsigned int reg_val = 0; sprdwcn_bus_reg_read(CP_WAKE_STATUS, ®_val, 4); if ((reg_val & BIT(31)) == 0) break; } else break; } #endif try_timeout: //SLP_MGR_INFO("slp_sts-0x%x", slp_sts); if (do_dump) { atomic_set(&(slp_mgr.cp2_state), STAY_AWAKING); SLP_MGR_INFO("wakeup fail, slp_sts-0x%x", slp_sts); sdiohal_dump_aon_reg(); mutex_unlock(&(slp_mgr.wakeup_lock)); return -1; } #if KERNEL_VERSION(3, 16, 75) <= LINUX_VERSION_CODE /* kernelv3.16.75 add ktime_after function. */ if (ktime_after(ktime_get(), time_end)) do_dump = 1; #else time_cmp = ktime_get(); if (time_cmp.tv64 > time_end.tv64) do_dump = 1; #endif } atomic_set(&(slp_mgr.cp2_state), STAY_AWAKING); } mutex_unlock(&(slp_mgr.wakeup_lock)); return 0; } /* called after chip power on, and reset sleep status */ void slp_mgr_reset(void) { atomic_set(&(slp_mgr.cp2_state), STAY_AWAKING); reinit_completion(&(slp_mgr.wakeup_ack_completion)); } int slp_mgr_init(void) { SLP_MGR_DBG("%s enter\n", __func__); atomic_set(&(slp_mgr.cp2_state), STAY_AWAKING); mutex_init(&(slp_mgr.drv_slp_lock)); mutex_init(&(slp_mgr.wakeup_lock)); init_completion(&(slp_mgr.wakeup_ack_completion)); slp_pub_int_RegCb(); #ifdef SLP_MGR_TEST slp_test_init(); #endif SLP_MGR_DBG("%s ok!\n", __func__); return 0; } EXPORT_SYMBOL(slp_mgr_init); int slp_mgr_deinit(void) { SLP_MGR_DBG("%s enter\n", __func__); atomic_set(&(slp_mgr.cp2_state), STAY_SLPING); slp_mgr.active_module = 0; mutex_destroy(&(slp_mgr.drv_slp_lock)); mutex_destroy(&(slp_mgr.wakeup_lock)); SLP_MGR_DBG("%s ok!\n", __func__); return 0; } EXPORT_SYMBOL(slp_mgr_deinit);