789 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			789 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /*
 | |
|  * Misc utility routines for accessing PMU corerev specific features
 | |
|  * of the SiliconBackplane-based Broadcom chips.
 | |
|  *
 | |
|  * Copyright (C) 1999-2019, Broadcom.
 | |
|  *
 | |
|  *      Unless you and Broadcom execute a separate written software license
 | |
|  * agreement governing use of this software, this software is licensed to you
 | |
|  * under the terms of the GNU General Public License version 2 (the "GPL"),
 | |
|  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
 | |
|  * following added to such license:
 | |
|  *
 | |
|  *      As a special exception, the copyright holders of this software give you
 | |
|  * permission to link this software with independent modules, and to copy and
 | |
|  * distribute the resulting executable under terms of your choice, provided that
 | |
|  * you also meet, for each linked independent module, the terms and conditions of
 | |
|  * the license of that module.  An independent module is a module which is not
 | |
|  * derived from this software.  The special exception does not apply to any
 | |
|  * modifications of the software.
 | |
|  *
 | |
|  *      Notwithstanding the above, under no circumstances may you combine this
 | |
|  * software in any way with any other Broadcom software provided under a license
 | |
|  * other than the GPL, without Broadcom's express prior written consent.
 | |
|  *
 | |
|  *
 | |
|  * <<Broadcom-WL-IPTag/Open:>>
 | |
|  *
 | |
|  * $Id: hndpmu.c 783841 2018-10-09 06:24:16Z $
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @file
 | |
|  * Note: this file contains PLL/FLL related functions. A chip can contain multiple PLLs/FLLs.
 | |
|  * However, in the context of this file the baseband ('BB') PLL/FLL is referred to.
 | |
|  *
 | |
|  * Throughout this code, the prefixes 'pmu1_' and 'pmu2_' are used.
 | |
|  * They refer to different revisions of the PMU (which is at revision 18 @ Apr 25, 2012)
 | |
|  * pmu1_ marks the transition from PLL to ADFLL (Digital Frequency Locked Loop). It supports
 | |
|  * fractional frequency generation. pmu2_ does not support fractional frequency generation.
 | |
|  */
 | |
| 
 | |
| #include <bcm_cfg.h>
 | |
| #include <typedefs.h>
 | |
| #include <bcmdefs.h>
 | |
| #include <osl.h>
 | |
| #include <bcmutils.h>
 | |
| #include <siutils.h>
 | |
| #include <bcmdevs.h>
 | |
| #include <hndsoc.h>
 | |
| #include <sbchipc.h>
 | |
| #include <hndchipc.h>
 | |
| #include <hndpmu.h>
 | |
| #include <hndlhl.h>
 | |
| #if defined(BCMULP)
 | |
| #include <ulp.h>
 | |
| #endif /* defined(BCMULP) */
 | |
| #include <sbgci.h>
 | |
| #ifdef EVENT_LOG_COMPILE
 | |
| #include <event_log.h>
 | |
| #endif // endif
 | |
| #include <sbgci.h>
 | |
| #include <lpflags.h>
 | |
| 
 | |
| #define	PMU_ERROR(args)
 | |
| 
 | |
| #define	PMU_MSG(args)
 | |
| 
 | |
| /* To check in verbose debugging messages not intended
 | |
|  * to be on except on private builds.
 | |
|  */
 | |
| #define	PMU_NONE(args)
 | |
| #define flags_shift	14
 | |
| 
 | |
| /** contains resource bit positions for a specific chip */
 | |
| struct rsc_per_chip_s {
 | |
| 	uint8 ht_avail;
 | |
| 	uint8 macphy_clkavail;
 | |
| 	uint8 ht_start;
 | |
| 	uint8 otp_pu;
 | |
| 	uint8 macphy_aux_clkavail;
 | |
| };
 | |
| 
 | |
| typedef struct rsc_per_chip_s rsc_per_chip_t;
 | |
| 
 | |
| #if defined(BCMPMU_STATS) && !defined(BCMPMU_STATS_DISABLED)
 | |
| bool	_pmustatsenab = TRUE;
 | |
| #else
 | |
| bool	_pmustatsenab = FALSE;
 | |
| #endif /* BCMPMU_STATS */
 | |
| 
 | |
| /**
 | |
|  * Balance between stable SDIO operation and power consumption is achieved using this function.
 | |
|  * Note that each drive strength table is for a specific VDDIO of the SDIO pads, ideally this
 | |
|  * function should read the VDDIO itself to select the correct table. For now it has been solved
 | |
|  * with the 'BCM_SDIO_VDDIO' preprocessor constant.
 | |
|  *
 | |
|  * 'drivestrength': desired pad drive strength in mA. Drive strength of 0 requests tri-state (if
 | |
|  *		    hardware supports this), if no hw support drive strength is not programmed.
 | |
|  */
 | |
| void
 | |
| si_sdiod_drive_strength_init(si_t *sih, osl_t *osh, uint32 drivestrength)
 | |
| {
 | |
| 	/*
 | |
| 	 * Note:
 | |
| 	 * This function used to set the SDIO drive strength via PMU_CHIPCTL1 for the
 | |
| 	 * 43143, 4330, 4334, 4336, 43362 chips.  These chips are now no longer supported, so
 | |
| 	 * the code has been deleted.
 | |
| 	 * Newer chips have the SDIO drive strength setting via a GCI Chip Control register,
 | |
| 	 * but the bit definitions are chip-specific.  We are keeping this function available
 | |
| 	 * (accessed via DHD 'sdiod_drive' IOVar) in case these newer chips need to provide access.
 | |
| 	 */
 | |
| 	UNUSED_PARAMETER(sih);
 | |
| 	UNUSED_PARAMETER(osh);
 | |
| 	UNUSED_PARAMETER(drivestrength);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_switch_pmu_dependency(si_t *sih, uint mode)
 | |
| {
 | |
| #ifdef DUAL_PMU_SEQUENCE
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 	uint32 current_res_state;
 | |
| 	uint32 min_mask, max_mask;
 | |
| 	const pmu_res_depend_t *pmu_res_depend_table = NULL;
 | |
| 	uint pmu_res_depend_table_sz = 0;
 | |
| 	uint origidx;
 | |
| 	pmuregs_t *pmu;
 | |
| 	chipcregs_t *cc;
 | |
| 	BCM_REFERENCE(cc);
 | |
| 
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 		cc  = si_setcore(sih, CC_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 		cc  = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	current_res_state = R_REG(osh, &pmu->res_state);
 | |
| 	min_mask = R_REG(osh, &pmu->min_res_mask);
 | |
| 	max_mask = R_REG(osh, &pmu->max_res_mask);
 | |
| 	W_REG(osh, &pmu->min_res_mask, (min_mask | current_res_state));
 | |
| 	switch (mode) {
 | |
| 		case PMU_4364_1x1_MODE:
 | |
| 		{
 | |
| 			if (CHIPID(sih->chip) == BCM4364_CHIP_ID) {
 | |
| 					pmu_res_depend_table = bcm4364a0_res_depend_1x1;
 | |
| 					pmu_res_depend_table_sz =
 | |
| 						ARRAYSIZE(bcm4364a0_res_depend_1x1);
 | |
| 			max_mask = PMU_4364_MAX_MASK_1x1;
 | |
| 			W_REG(osh, &pmu->res_table_sel, RES4364_SR_SAVE_RESTORE);
 | |
| 			W_REG(osh, &pmu->res_updn_timer, PMU_4364_SAVE_RESTORE_UPDNTIME_1x1);
 | |
| #if defined(SAVERESTORE)
 | |
| 				if (SR_ENAB()) {
 | |
| 					/* Disable 3x3 SR engine */
 | |
| 					W_REG(osh, &cc->sr1_control0,
 | |
| 					CC_SR0_4364_SR_ENG_CLK_EN |
 | |
| 					CC_SR0_4364_SR_RSRC_TRIGGER |
 | |
| 					CC_SR0_4364_SR_WD_MEM_MIN_DIV |
 | |
| 					CC_SR0_4364_SR_INVERT_CLK |
 | |
| 					CC_SR0_4364_SR_ENABLE_HT |
 | |
| 					CC_SR0_4364_SR_ALLOW_PIC |
 | |
| 					CC_SR0_4364_SR_PMU_MEM_DISABLE);
 | |
| 				}
 | |
| #endif /* SAVERESTORE */
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 		case PMU_4364_3x3_MODE:
 | |
| 		{
 | |
| 			if (CHIPID(sih->chip) == BCM4364_CHIP_ID) {
 | |
| 				W_REG(osh, &pmu->res_table_sel, RES4364_SR_SAVE_RESTORE);
 | |
| 				W_REG(osh, &pmu->res_updn_timer,
 | |
| 					PMU_4364_SAVE_RESTORE_UPDNTIME_3x3);
 | |
| 				/* Change the dependency table only if required */
 | |
| 				if ((max_mask != PMU_4364_MAX_MASK_3x3) ||
 | |
| 					(max_mask != PMU_4364_MAX_MASK_RSDB)) {
 | |
| 						pmu_res_depend_table = bcm4364a0_res_depend_rsdb;
 | |
| 						pmu_res_depend_table_sz =
 | |
| 							ARRAYSIZE(bcm4364a0_res_depend_rsdb);
 | |
| 						max_mask = PMU_4364_MAX_MASK_3x3;
 | |
| 				}
 | |
| #if defined(SAVERESTORE)
 | |
| 				if (SR_ENAB()) {
 | |
| 					/* Enable 3x3 SR engine */
 | |
| 					W_REG(osh, &cc->sr1_control0,
 | |
| 					CC_SR0_4364_SR_ENG_CLK_EN |
 | |
| 					CC_SR0_4364_SR_RSRC_TRIGGER |
 | |
| 					CC_SR0_4364_SR_WD_MEM_MIN_DIV |
 | |
| 					CC_SR0_4364_SR_INVERT_CLK |
 | |
| 					CC_SR0_4364_SR_ENABLE_HT |
 | |
| 					CC_SR0_4364_SR_ALLOW_PIC |
 | |
| 					CC_SR0_4364_SR_PMU_MEM_DISABLE |
 | |
| 					CC_SR0_4364_SR_ENG_EN_MASK);
 | |
| 				}
 | |
| #endif /* SAVERESTORE */
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 		case PMU_4364_RSDB_MODE:
 | |
| 		default:
 | |
| 		{
 | |
| 			if (CHIPID(sih->chip) == BCM4364_CHIP_ID) {
 | |
| 				W_REG(osh, &pmu->res_table_sel, RES4364_SR_SAVE_RESTORE);
 | |
| 				W_REG(osh, &pmu->res_updn_timer,
 | |
| 					PMU_4364_SAVE_RESTORE_UPDNTIME_3x3);
 | |
| 				/* Change the dependency table only if required */
 | |
| 				if ((max_mask != PMU_4364_MAX_MASK_3x3) ||
 | |
| 					(max_mask != PMU_4364_MAX_MASK_RSDB)) {
 | |
| 						pmu_res_depend_table =
 | |
| 							bcm4364a0_res_depend_rsdb;
 | |
| 						pmu_res_depend_table_sz =
 | |
| 							ARRAYSIZE(bcm4364a0_res_depend_rsdb);
 | |
| 						max_mask = PMU_4364_MAX_MASK_RSDB;
 | |
| 				}
 | |
| #if defined(SAVERESTORE)
 | |
| 			if (SR_ENAB()) {
 | |
| 					/* Enable 3x3 SR engine */
 | |
| 					W_REG(osh, &cc->sr1_control0,
 | |
| 					CC_SR0_4364_SR_ENG_CLK_EN |
 | |
| 					CC_SR0_4364_SR_RSRC_TRIGGER |
 | |
| 					CC_SR0_4364_SR_WD_MEM_MIN_DIV |
 | |
| 					CC_SR0_4364_SR_INVERT_CLK |
 | |
| 					CC_SR0_4364_SR_ENABLE_HT |
 | |
| 					CC_SR0_4364_SR_ALLOW_PIC |
 | |
| 					CC_SR0_4364_SR_PMU_MEM_DISABLE |
 | |
| 					CC_SR0_4364_SR_ENG_EN_MASK);
 | |
| 				}
 | |
| #endif /* SAVERESTORE */
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	si_pmu_resdeptbl_upd(sih, osh, pmu, pmu_res_depend_table, pmu_res_depend_table_sz);
 | |
| 	W_REG(osh, &pmu->max_res_mask, max_mask);
 | |
| 	W_REG(osh, &pmu->min_res_mask, min_mask);
 | |
| 	si_pmu_wait_for_steady_state(sih, osh, pmu);
 | |
| 	/* Add some delay; allow resources to come up and settle. */
 | |
| 	OSL_DELAY(200);
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| #endif /* DUAL_PMU_SEQUENCE */
 | |
| }
 | |
| 
 | |
| #if defined(BCMULP)
 | |
| 
 | |
| int
 | |
| si_pmu_ulp_register(si_t *sih)
 | |
| {
 | |
| 	return ulp_p1_module_register(ULP_MODULE_ID_PMU, &ulp_pmu_ctx, (void *)sih);
 | |
| }
 | |
| 
 | |
| static uint
 | |
| si_pmu_ulp_get_retention_size_cb(void *handle, ulp_ext_info_t *einfo)
 | |
| {
 | |
| 	ULP_DBG(("%s: sz: %d\n", __FUNCTION__, sizeof(si_pmu_ulp_cr_dat_t)));
 | |
| 	return sizeof(si_pmu_ulp_cr_dat_t);
 | |
| }
 | |
| 
 | |
| static int
 | |
| si_pmu_ulp_enter_cb(void *handle, ulp_ext_info_t *einfo, uint8 *cache_data)
 | |
| {
 | |
| 	si_pmu_ulp_cr_dat_t crinfo = {0};
 | |
| 	crinfo.ilpcycles_per_sec = ilpcycles_per_sec;
 | |
| 	ULP_DBG(("%s: ilpcycles_per_sec: %x\n", __FUNCTION__, ilpcycles_per_sec));
 | |
| 	memcpy(cache_data, (void*)&crinfo, sizeof(crinfo));
 | |
| 	return BCME_OK;
 | |
| }
 | |
| 
 | |
| static int
 | |
| si_pmu_ulp_exit_cb(void *handle, uint8 *cache_data,
 | |
| 	uint8 *p2_cache_data)
 | |
| {
 | |
| 	si_pmu_ulp_cr_dat_t *crinfo = (si_pmu_ulp_cr_dat_t *)cache_data;
 | |
| 
 | |
| 	ilpcycles_per_sec = crinfo->ilpcycles_per_sec;
 | |
| 	ULP_DBG(("%s: ilpcycles_per_sec: %x, cache_data: %p\n", __FUNCTION__,
 | |
| 		ilpcycles_per_sec, cache_data));
 | |
| 	return BCME_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmu_ulp_chipconfig(si_t *sih, osl_t *osh)
 | |
| {
 | |
| 	uint32 reg_val;
 | |
| 
 | |
| 	BCM_REFERENCE(reg_val);
 | |
| 
 | |
| 	if (CHIPID(sih->chip) == BCM43012_CHIP_ID) {
 | |
| 		/* DS1 reset and clk enable init value config */
 | |
| 		si_pmu_chipcontrol(sih, PMU_CHIPCTL14, ~0x0,
 | |
| 			(PMUCCTL14_43012_ARMCM3_RESET_INITVAL |
 | |
| 			PMUCCTL14_43012_DOT11MAC_CLKEN_INITVAL |
 | |
| 			PMUCCTL14_43012_SDIOD_RESET_INIVAL |
 | |
| 			PMUCCTL14_43012_SDIO_CLK_DMN_RESET_INITVAL |
 | |
| 			PMUCCTL14_43012_SOCRAM_CLKEN_INITVAL |
 | |
| 			PMUCCTL14_43012_M2MDMA_RESET_INITVAL |
 | |
| 			PMUCCTL14_43012_DOT11MAC_PHY_CLK_EN_INITVAL |
 | |
| 			PMUCCTL14_43012_DOT11MAC_PHY_CNTL_EN_INITVAL));
 | |
| 
 | |
| 		/* Clear SFlash clock request and enable High Quality clock */
 | |
| 		CHIPC_REG(sih, clk_ctl_st, CCS_SFLASH_CLKREQ | CCS_HQCLKREQ, CCS_HQCLKREQ);
 | |
| 
 | |
| 		reg_val = PMU_REG(sih, min_res_mask, ~0x0, ULP_MIN_RES_MASK);
 | |
| 		ULP_DBG(("si_pmu_ulp_chipconfig: min_res_mask: 0x%08x\n", reg_val));
 | |
| 
 | |
| 		/* Force power switch off */
 | |
| 		si_pmu_chipcontrol(sih, PMU_CHIPCTL2,
 | |
| 				(PMUCCTL02_43012_SUBCORE_PWRSW_FORCE_ON |
 | |
| 				PMUCCTL02_43012_PHY_PWRSW_FORCE_ON), 0);
 | |
| 
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmu_ulp_ilp_config(si_t *sih, osl_t *osh, uint32 ilp_period)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	pmu = si_setcoreidx(sih, si_findcoreidx(sih, PMU_CORE_ID, 0));
 | |
| 	W_REG(osh, &pmu->ILPPeriod, ilp_period);
 | |
| 	si_lhl_ilp_config(sih, osh, ilp_period);
 | |
| }
 | |
| 
 | |
| /** Initialize DS1 PMU hardware resources */
 | |
| void
 | |
| si_pmu_ds1_res_init(si_t *sih, osl_t *osh)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	const pmu_res_updown_t *pmu_res_updown_table = NULL;
 | |
| 	uint pmu_res_updown_table_sz = 0;
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	switch (CHIPID(sih->chip)) {
 | |
| 	case BCM43012_CHIP_ID:
 | |
| 		pmu_res_updown_table = bcm43012a0_res_updown_ds1;
 | |
| 		pmu_res_updown_table_sz = ARRAYSIZE(bcm43012a0_res_updown_ds1);
 | |
| 		break;
 | |
| 
 | |
| 	default:
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* Program up/down timers */
 | |
| 	while (pmu_res_updown_table_sz--) {
 | |
| 		ASSERT(pmu_res_updown_table != NULL);
 | |
| 		PMU_MSG(("DS1: Changing rsrc %d res_updn_timer to 0x%x\n",
 | |
| 			pmu_res_updown_table[pmu_res_updown_table_sz].resnum,
 | |
| 			pmu_res_updown_table[pmu_res_updown_table_sz].updown));
 | |
| 		W_REG(osh, &pmu->res_table_sel,
 | |
| 			pmu_res_updown_table[pmu_res_updown_table_sz].resnum);
 | |
| 		W_REG(osh, &pmu->res_updn_timer,
 | |
| 			pmu_res_updown_table[pmu_res_updown_table_sz].updown);
 | |
| 	}
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| #endif /* defined(BCMULP) */
 | |
| 
 | |
| uint32
 | |
| si_pmu_wake_bit_offset(si_t *sih)
 | |
| {
 | |
| 	uint32 wakebit;
 | |
| 
 | |
| 	switch (CHIPID(sih->chip)) {
 | |
| 	case BCM4347_CHIP_GRPID:
 | |
| 		wakebit = CC2_4347_GCI2WAKE_MASK;
 | |
| 		break;
 | |
| 	default:
 | |
| 		wakebit = 0;
 | |
| 		ASSERT(0);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return wakebit;
 | |
| }
 | |
| 
 | |
| void si_pmu_set_min_res_mask(si_t *sih, osl_t *osh, uint min_res_mask)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	}
 | |
| 	else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	W_REG(osh, &pmu->min_res_mask, min_res_mask);
 | |
| 	OSL_DELAY(100);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| bool
 | |
| si_pmu_cap_fast_lpo(si_t *sih)
 | |
| {
 | |
| 	return (PMU_REG(sih, core_cap_ext, 0, 0) & PCAP_EXT_USE_MUXED_ILP_CLK_MASK) ? TRUE : FALSE;
 | |
| }
 | |
| 
 | |
| int
 | |
| si_pmu_fast_lpo_disable(si_t *sih)
 | |
| {
 | |
| 	if (!si_pmu_cap_fast_lpo(sih)) {
 | |
| 		PMU_ERROR(("%s: No Fast LPO capability\n", __FUNCTION__));
 | |
| 		return BCME_ERROR;
 | |
| 	}
 | |
| 
 | |
| 	PMU_REG(sih, pmucontrol_ext,
 | |
| 		PCTL_EXT_FASTLPO_ENAB |
 | |
| 		PCTL_EXT_FASTLPO_SWENAB |
 | |
| 		PCTL_EXT_FASTLPO_PCIE_SWENAB,
 | |
| 		0);
 | |
| 	OSL_DELAY(1000);
 | |
| 	return BCME_OK;
 | |
| }
 | |
| 
 | |
| #ifdef BCMPMU_STATS
 | |
| /*
 | |
|  * 8 pmu statistics timer default map
 | |
|  *
 | |
|  * for CORE_RDY_AUX measure, set as below for timer 6 and 7 instead of CORE_RDY_MAIN.
 | |
|  *	//core-n active duration : pmu_rsrc_state(CORE_RDY_AUX)
 | |
|  *	{ SRC_CORE_RDY_AUX, FALSE, TRUE, LEVEL_HIGH},
 | |
|  *	//core-n active duration : pmu_rsrc_state(CORE_RDY_AUX)
 | |
|  *	{ SRC_CORE_RDY_AUX, FALSE, TRUE, EDGE_RISE}
 | |
|  */
 | |
| static pmu_stats_timer_t pmustatstimer[] = {
 | |
| 	{ SRC_LINK_IN_L12, FALSE, TRUE, PMU_STATS_LEVEL_HIGH},	//link_in_l12
 | |
| 	{ SRC_LINK_IN_L23, FALSE, TRUE, PMU_STATS_LEVEL_HIGH},	//link_in_l23
 | |
| 	{ SRC_PM_ST_IN_D0, FALSE, TRUE, PMU_STATS_LEVEL_HIGH},	//pm_st_in_d0
 | |
| 	{ SRC_PM_ST_IN_D3, FALSE, TRUE, PMU_STATS_LEVEL_HIGH},	//pm_st_in_d3
 | |
| 	//deep-sleep duration : pmu_rsrc_state(XTAL_PU)
 | |
| 	{ SRC_XTAL_PU, FALSE, TRUE, PMU_STATS_LEVEL_LOW},
 | |
| 	//deep-sleep entry count : pmu_rsrc_state(XTAL_PU)
 | |
| 	{ SRC_XTAL_PU, FALSE, TRUE, PMU_STATS_EDGE_FALL},
 | |
| 	//core-n active duration : pmu_rsrc_state(CORE_RDY_MAIN)
 | |
| 	{ SRC_CORE_RDY_MAIN, FALSE, TRUE, PMU_STATS_LEVEL_HIGH},
 | |
| 	//core-n active duration : pmu_rsrc_state(CORE_RDY_MAIN)
 | |
| 	{ SRC_CORE_RDY_MAIN, FALSE, TRUE, PMU_STATS_EDGE_RISE}
 | |
| };
 | |
| 
 | |
| static void
 | |
| si_pmustatstimer_update(osl_t *osh, pmuregs_t *pmu, uint8 timerid)
 | |
| {
 | |
| 	uint32 stats_timer_ctrl;
 | |
| 
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_addr, timerid);
 | |
| 	stats_timer_ctrl =
 | |
| 		((pmustatstimer[timerid].src_num << PMU_ST_SRC_SHIFT) &
 | |
| 			PMU_ST_SRC_MASK) |
 | |
| 		((pmustatstimer[timerid].cnt_mode << PMU_ST_CNT_MODE_SHIFT) &
 | |
| 			PMU_ST_CNT_MODE_MASK) |
 | |
| 		((pmustatstimer[timerid].enable << PMU_ST_EN_SHIFT) & PMU_ST_EN_MASK) |
 | |
| 		((pmustatstimer[timerid].int_enable << PMU_ST_INT_EN_SHIFT) & PMU_ST_INT_EN_MASK);
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_ctrl, stats_timer_ctrl);
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_N, 0);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_int_enable(si_t *sih)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	OR_REG(osh, &pmu->pmuintmask0, PMU_INT_STAT_TIMER_INT_MASK);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_int_disable(si_t *sih)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	AND_REG(osh, &pmu->pmuintmask0, ~PMU_INT_STAT_TIMER_INT_MASK);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_init(si_t *sih)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 	uint32 core_cap_ext;
 | |
| 	uint8 max_stats_timer_num;
 | |
| 	int8 i;
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	core_cap_ext = R_REG(osh, &pmu->core_cap_ext);
 | |
| 
 | |
| 	max_stats_timer_num = ((core_cap_ext & PCAP_EXT_ST_NUM_MASK) >> PCAP_EXT_ST_NUM_SHIFT) + 1;
 | |
| 
 | |
| 	for (i = 0; i < max_stats_timer_num; i++) {
 | |
| 		si_pmustatstimer_update(osh, pmu, i);
 | |
| 	}
 | |
| 
 | |
| 	OR_REG(osh, &pmu->pmuintmask0, PMU_INT_STAT_TIMER_INT_MASK);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_dump(si_t *sih)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 	uint32 core_cap_ext, pmucapabilities, AlpPeriod, ILPPeriod, pmuintmask0, pmuintstatus;
 | |
| 	uint8 max_stats_timer_num, max_stats_timer_src_num;
 | |
| 	uint32 stat_timer_ctrl, stat_timer_N;
 | |
| 	uint8 i;
 | |
| 	uint32 current_time_ms = OSL_SYSUPTIME();
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	pmucapabilities = R_REG(osh, &pmu->pmucapabilities);
 | |
| 	core_cap_ext = R_REG(osh, &pmu->core_cap_ext);
 | |
| 	AlpPeriod = R_REG(osh, &pmu->slowclkperiod);
 | |
| 	ILPPeriod = R_REG(osh, &pmu->ILPPeriod);
 | |
| 
 | |
| 	max_stats_timer_num = ((core_cap_ext & PCAP_EXT_ST_NUM_MASK) >>
 | |
| 		PCAP_EXT_ST_NUM_SHIFT) + 1;
 | |
| 	max_stats_timer_src_num = ((core_cap_ext & PCAP_EXT_ST_SRC_NUM_MASK) >>
 | |
| 		PCAP_EXT_ST_SRC_NUM_SHIFT) + 1;
 | |
| 
 | |
| 	pmuintstatus = R_REG(osh, &pmu->pmuintstatus);
 | |
| 	pmuintmask0 = R_REG(osh, &pmu->pmuintmask0);
 | |
| 
 | |
| 	PMU_ERROR(("%s : TIME %d\n", __FUNCTION__, current_time_ms));
 | |
| 
 | |
| 	PMU_ERROR(("\tMAX Timer Num %d, MAX Source Num %d\n",
 | |
| 		max_stats_timer_num, max_stats_timer_src_num));
 | |
| 	PMU_ERROR(("\tpmucapabilities 0x%8x, core_cap_ext 0x%8x, AlpPeriod 0x%8x, ILPPeriod 0x%8x, "
 | |
| 		"pmuintmask0 0x%8x, pmuintstatus 0x%8x, pmurev %d\n",
 | |
| 		pmucapabilities, core_cap_ext, AlpPeriod, ILPPeriod,
 | |
| 		pmuintmask0, pmuintstatus, PMUREV(sih->pmurev)));
 | |
| 
 | |
| 	for (i = 0; i < max_stats_timer_num; i++) {
 | |
| 		W_REG(osh, &pmu->pmu_statstimer_addr, i);
 | |
| 		stat_timer_ctrl = R_REG(osh, &pmu->pmu_statstimer_ctrl);
 | |
| 		stat_timer_N = R_REG(osh, &pmu->pmu_statstimer_N);
 | |
| 		PMU_ERROR(("\t Timer %d : control 0x%8x, %d\n",
 | |
| 			i, stat_timer_ctrl, stat_timer_N));
 | |
| 	}
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_start(si_t *sih, uint8 timerid)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	pmustatstimer[timerid].enable = TRUE;
 | |
| 
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_addr, timerid);
 | |
| 	OR_REG(osh, &pmu->pmu_statstimer_ctrl, PMU_ST_ENAB << PMU_ST_EN_SHIFT);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_stop(si_t *sih, uint8 timerid)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	pmustatstimer[timerid].enable = FALSE;
 | |
| 
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_addr, timerid);
 | |
| 	AND_REG(osh, &pmu->pmu_statstimer_ctrl, ~(PMU_ST_ENAB << PMU_ST_EN_SHIFT));
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_clear(si_t *sih, uint8 timerid)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_addr, timerid);
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_N, 0);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_clear_overflow(si_t *sih)
 | |
| {
 | |
| 	uint8 i;
 | |
| 	uint32 core_cap_ext;
 | |
| 	uint8 max_stats_timer_num;
 | |
| 	uint32 timerN;
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	core_cap_ext = R_REG(osh, &pmu->core_cap_ext);
 | |
| 	max_stats_timer_num = ((core_cap_ext & PCAP_EXT_ST_NUM_MASK) >> PCAP_EXT_ST_NUM_SHIFT) + 1;
 | |
| 
 | |
| 	for (i = 0; i < max_stats_timer_num; i++) {
 | |
| 		W_REG(osh, &pmu->pmu_statstimer_addr, i);
 | |
| 		timerN = R_REG(osh, &pmu->pmu_statstimer_N);
 | |
| 		if (timerN == 0xFFFFFFFF) {
 | |
| 			PMU_ERROR(("pmustatstimer overflow clear - timerid : %d\n", i));
 | |
| 			si_pmustatstimer_clear(sih, i);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| uint32
 | |
| si_pmustatstimer_read(si_t *sih, uint8 timerid)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 	uint32 stats_timer_N;
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	W_REG(osh, &pmu->pmu_statstimer_addr, timerid);
 | |
| 	stats_timer_N = R_REG(osh, &pmu->pmu_statstimer_N);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| 
 | |
| 	return stats_timer_N;
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_cfg_src_num(si_t *sih, uint8 src_num, uint8 timerid)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	pmustatstimer[timerid].src_num = src_num;
 | |
| 	si_pmustatstimer_update(osh, pmu, timerid);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| 
 | |
| void
 | |
| si_pmustatstimer_cfg_cnt_mode(si_t *sih, uint8 cnt_mode, uint8 timerid)
 | |
| {
 | |
| 	pmuregs_t *pmu;
 | |
| 	uint origidx;
 | |
| 	osl_t *osh = si_osh(sih);
 | |
| 
 | |
| 	/* Remember original core before switch to chipc/pmu */
 | |
| 	origidx = si_coreidx(sih);
 | |
| 	if (AOB_ENAB(sih)) {
 | |
| 		pmu = si_setcore(sih, PMU_CORE_ID, 0);
 | |
| 	} else {
 | |
| 		pmu = si_setcoreidx(sih, SI_CC_IDX);
 | |
| 	}
 | |
| 	ASSERT(pmu != NULL);
 | |
| 
 | |
| 	pmustatstimer[timerid].cnt_mode = cnt_mode;
 | |
| 	si_pmustatstimer_update(osh, pmu, timerid);
 | |
| 
 | |
| 	/* Return to original core */
 | |
| 	si_setcoreidx(sih, origidx);
 | |
| }
 | |
| #endif /* BCMPMU_STATS */
 | 
