/* * Copyright (C) 2011-2014 Apple Inc. All rights reserved. * * This document is the property of Apple Inc. * It is considered confidential and proprietary. * * This document may not be reproduced or transmitted in any form, * in whole or in part, without the express written permission of * Apple Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern void arm_no_wfe_spin(uint32_t usecs); struct clock_source { uint32_t src_clk; uint32_t factor; }; struct clock_config { volatile uint32_t *clock_reg; // CLK_CFG Register struct clock_source sources[12]; // List of sources }; #define PLL_VCO_TARGET(pllx) ((2ULL * (pllx##_O) * (pllx##_M)) / (pllx##_P)) #define PLL_FREQ_TARGET(pllx) (((pllx##_O) * (pllx##_M)) / (pllx##_P) / ((pllx##_S) + 1)) #if APPLICATION_SECUREROM static uint32_t active_state = kDVFM_STATE_SECUREROM; #endif #if APPLICATION_IBOOT static uint32_t active_state = kDVFM_STATE_IBOOT; #endif static uint64_t ccc_dvfm_states[] = { #if APPLICATION_SECUREROM // BIU div = 1, disable voltage change sequence, Clk src = Ref clk. [kDVFM_STATE_BYPASS] = (2ULL << 18) | (1ULL << 22), // CCC @ 300 MHz, disable voltage change sequence, Clk src = PLL, BIU div = 1. [kDVFM_STATE_SECUREROM] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(50) | CCC_DVFM_ST_PLL_S(3) | (1ULL << 22) | (1ULL << 21) | (2ULL << 18), #else // BIU div = 1, Clk src = Ref clk. [kDVFM_STATE_BYPASS] = (2ULL << 18), #endif #if APPLICATION_IBOOT #if SUB_TARGET_N51 || SUB_TARGET_N53 || SUB_TARGET_J85 || SUB_TARGET_J86 || SUB_TARGET_J87 || SUB_TARGET_J85M || SUB_TARGET_J86M || SUB_TARGET_J87M // CCC V0 // CCC @ 396 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 1. [kDVFM_STATE_IBOOT] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(33) | CCC_DVFM_ST_PLL_S(1) | (1ULL << 21) | (2ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x6) | CCC_DVFM_ST_VOLADJ2(0xB), // CCC V1 // CCC @ 600 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 1. [kDVFM_STATE_V1] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(25) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (2ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x6) | CCC_DVFM_ST_VOLADJ2(0xB), // CCC V2 // CCC @ 840 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2. [kDVFM_STATE_V2] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(35) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x4) | CCC_DVFM_ST_VOLADJ2(0x8), // CCC V3 // CCC @ 1128 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2 [kDVFM_STATE_V3] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(47) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x4) | CCC_DVFM_ST_VOLADJ2(0x8), // CCC V4 // CCC @ 1296 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2.5 [kDVFM_STATE_V4] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(54) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (5ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x0) | CCC_DVFM_ST_VOLADJ2(0x0) #elif SUB_TARGET_J34 || SUB_TARGET_J34M || SUB_TARGET_J71 || SUB_TARGET_J72 || SUB_TARGET_J73 // CCC V0 // CCC @ 600 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 1. [kDVFM_STATE_IBOOT] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(25) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (2ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x6) | CCC_DVFM_ST_VOLADJ2(0xB), // CCC V1 // CCC @ 840 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2. [kDVFM_STATE_V1] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(35) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x4) | CCC_DVFM_ST_VOLADJ2(0x8), // CCC V2 // CCC @ 1128 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2 [kDVFM_STATE_V2] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(47) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x4) | CCC_DVFM_ST_VOLADJ2(0x8), // CCC V3 // CCC @ 1296 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2.5 [kDVFM_STATE_V3] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(54) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (5ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x0) | CCC_DVFM_ST_VOLADJ2(0x0), // CCC V4 // CCC @ 1392 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 3 [kDVFM_STATE_V4] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(58) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (6ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x0) | CCC_DVFM_ST_VOLADJ2(0x0) #else // CCC V0 // CCC @ 600 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 1. [kDVFM_STATE_IBOOT] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(25) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (2ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x6) | CCC_DVFM_ST_VOLADJ2(0xB), // CCC V1 // CCC @ 672 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2. [kDVFM_STATE_V1] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(28) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x6) | CCC_DVFM_ST_VOLADJ2(0xB), // CCC V2 // CCC @ 696 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2 [kDVFM_STATE_V2] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(29) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x4) | CCC_DVFM_ST_VOLADJ2(0x8), // CCC V3 // CCC @ 840 MHz, enable voltage change sequence, Clk src = PLL, BIU div = 2 [kDVFM_STATE_V3] = CCC_DVFM_ST_PLL_P(1) | CCC_DVFM_ST_PLL_M(35) | CCC_DVFM_ST_PLL_S(0) | (1ULL << 21) | (4ULL << 18) | CCC_DVFM_ST_VOLADJ0(0x0) | CCC_DVFM_ST_VOLADJ1(0x4) | CCC_DVFM_ST_VOLADJ2(0x8) #endif #endif }; #if APPLICATION_IBOOT /* PLL0 @840MHz */ #define PLL0 0 #define PLL0_O OSC_FREQ #define PLL0_P 4 #define PLL0_M 279 #define PLL0_S 1 #define PLL0_V PLL_VCO_TARGET(PLL0) #define PLL0_T PLL_FREQ_TARGET(PLL0) #if TARGET_DDR_784M #define PLL1 1 #define PLL1_O OSC_FREQ #define PLL1_P 3 #define PLL1_M 98 #define PLL1_S 0 #define PLL1_V PLL_VCO_TARGET(PLL1) #define PLL1_T PLL_FREQ_TARGET(PLL1) #elif TARGET_DDR_740M /* PLL1 @740MHz */ #define PLL1 1 #define PLL1_O OSC_FREQ #define PLL1_P 6 #define PLL1_M 185 #define PLL1_S 0 #define PLL1_V PLL_VCO_TARGET(PLL1) #define PLL1_T PLL_FREQ_TARGET(PLL1) #elif TARGET_DDR_798M /* PLL1 @798MHz */ #define PLL1 1 #define PLL1_O OSC_FREQ #define PLL1_P 4 #define PLL1_M 133 #define PLL1_S 0 #define PLL1_V PLL_VCO_TARGET(PLL1) #define PLL1_T PLL_FREQ_TARGET(PLL1) #else /* PLL1 @800MHz */ #define PLL1 1 #define PLL1_O OSC_FREQ #define PLL1_P 3 #define PLL1_M 100 #define PLL1_S 0 #define PLL1_V PLL_VCO_TARGET(PLL1) #define PLL1_T PLL_FREQ_TARGET(PLL1) #endif #if SUB_TARGET_J34 || SUB_TARGET_J34M /* PLL2 @297MHz */ #define PLL2 2 #define PLL2_O OSC_FREQ #define PLL2_P 2 #define PLL2_M 99 #define PLL2_S 15 #define PLL2_V PLL_VCO_TARGET(PLL2) #define PLL2_T PLL_FREQ_TARGET(PLL2) #endif /* PLL4 @2400MHz (VCO output) */ #define PLL4 4 #define PLL4_O OSC_FREQ #define PLL4_P 1 #define PLL4_M 50 #define PLL4_S 0 #define PLL4_V PLL_VCO_TARGET(PLL4) #define PLL4_T PLL_FREQ_TARGET(PLL4) #define PLL4_VCO_ENABLED 1 #ifndef TARGET_SPARE0_CLK_CFG #define TARGET_SPARE0_CLK_CFG 0x00100000 #endif #define VID0_CLKD TARGET_VID0_CLK_CFG static const uint32_t clk_divs_active[PMGR_CLK_CFG_COUNT] = { 0x81100000, 0x81100000, 0x80100000, 0x86100000, // 0x10000: mcu_fixed, mcu, mipi_dsi, nco_ref0, 0x85100000, 0x80100000, 0x88100000, 0x85100000, // 0x10010: nco_ref1, nco_alg0, nco_alg1, hsciphy_ref_12m, 0x85100000, 0x85100000, 0x85100000, 0x85100000, // 0x10020: usb480_0, usb480_1, usb_ohci_48m, usb, 0x85100000, 0x00100000, 0x00100000, 0x00100000, // 0x10030: usb_free_60m, adsp_t, adsp_p, adsp_ts, 0x85100000, 0x80100000, 0x85100000, 0x85100000, // 0x10040: sio_c, sio_p, isp_c, isp, 0x81100000, 0x82100000, 0x85100000, 0x85100000, // 0x10050: isp_sensor0_ref, isp_sensor1_ref, vdec, venc, VID0_CLKD, 0x85100000, 0x85100000, 0x85100000, // 0x10060: vid0, disp0, disp1, ajpeg_ip, 0x85100000, 0x85100000, 0x85100000, 0x00100000, // 0x10070: ajpeg_wrap, msr, af, ans_dll, 0x8A100000, 0x85100000, 0x85100000, 0x85100000, // 0x10080: ans_c, ans_link, lio, mca0_m, 0x86100000, 0x87100000, 0x88100000, 0x89100000, // 0x10090: mca1_m, mca2_m, mca3_m, mca4_m, 0x85100000, 0x86100000, 0x85100000, 0x85100000, // 0x100a0: sep, gpio, spi0_n, spi1_n, 0x85100000, 0x85100000, 0x85100000 // 0x100b0: spi2_n, spi3_n, debug }; static const uint32_t spare_divs_active[PMGR_SPARE_CLK_CFG_COUNT] = { TARGET_SPARE0_CLK_CFG, // 0x10200: spare0 0x00000000, // 0x10204: spare1 0x00000000, // 0x10208: spare2 0x00000000, // 0x1020c: spare3 0x80100028, // 0x10210: isp_ref0 0x80100028 // 0x10214: isp_ref1 }; /* GFX table. The voltage information is maintained in chipid.c */ struct gfx_state_info { uint32_t dwi_val; /* will be populated by determining the binning info */ uint32_t fb_div; uint32_t pre_div; uint32_t op_div; }; static struct gfx_state_info gfx_states[] = { [0] = {0, 0, 0, 0}, /* Power off Rogue state.*/ [1] = {0, 55, 2, 1}, /* 330 MHz */ [2] = {0, 65, 2, 1}, /* 390 MHz */ [3] = {0, 75, 2, 1}, /* 450 MHz */ [4] = {0, 55, 2, 3}, /* 165 MHz */ [5] = {0, 65, 2, 3}, /* 195 MHz */ [6] = {0, 75, 2, 3}, /* 225 MHz */ #if 0 [7] = {0, 0, 0, 0}, /* Debug state. chipid.c has a valid voltage for this state. */ #endif }; static void set_gfx_perf_state(uint32_t state_num, struct gfx_state_info *gfx_state); static int get_amc_cfg_sel(uint32_t performance_level); static void set_amc_clk_config(uint32_t src_index, uint32_t cfg_sel); #endif /* APPLICATION_IBOOT */ #if APPLICATION_SECUREROM /* We need SoC PLL only. */ /* PLL4 @1200MHz (VCO output) */ #define PLL4 4 #define PLL4_O OSC_FREQ #define PLL4_P 1 #define PLL4_M 50 #define PLL4_S 0 #define PLL4_V PLL_VCO_TARGET(PLL4) #define PLL4_T PLL_FREQ_TARGET(PLL4) #define PLL4_VCO_ENABLED 0 // We won't touch the clk gen's that aren't necessary during SecureROM. static const uint32_t clk_divs_active[PMGR_CLK_CFG_COUNT] = { 0x80100000, 0x80100000, 0x80100000, 0x80100000, // 0x10000: mcu_fixed, mcu, mipi_dsi, nco_ref0, 0x80100000, 0x80100000, 0x80100000, 0x80100000, // 0x10010: nco_ref1, nco_alg0, nco_alg1, hsciphy_ref_12m, 0x80100000, 0x80100000, 0x80100000, 0x86100000, // 0x10020: usb480_0, usb480_1, usb_ohci_48m, usb, 0x80100000, 0x80100000, 0x80100000, 0x80100000, // 0x10030: usb_free_60m, adsp_t, adsp_p, adsp_ts, 0x85100000, 0x85100000, 0x80100000, 0x80100000, // 0x10040: sio_c, sio_p, isp_c, isp, 0x80100000, 0x80100000, 0x80100000, 0x80100000, // 0x10050: isp_sensor0_ref, isp_sensor1_ref, vdec, venc, 0x80100000, 0x80100000, 0x80100000, 0x80100000, // 0x10060: vid0, disp0, disp1, ajpeg_ip, 0x80100000, 0x80100000, 0x85100000, 0x80100000, // 0x10070: ajpeg_wrap, msr, af, ans_dll, 0x85100000, 0x85100000, 0x85100000, 0x80100000, // 0x10080: ans_c, ans_link, lio, mca0_m, 0x80100000, 0x80100000, 0x80100000, 0x80100000, // 0x10090: mca1_m, mca2_m, mca3_m, mca4_m, 0x85100000, 0x86100000, 0x80100000, 0x80100000, // 0x100a0: sep, gpio, spi0_n, spi1_n, 0x80100000, 0x80100000, 0x80100000 // 0x100b0: spi2_n, spi3_n, debug }; static const uint32_t spare_divs_active[PMGR_SPARE_CLK_CFG_COUNT] = { 0x80000001, 0x80000001, 0x80000001, 0x80000001, // 0x10200: s0, s1, s2, s3, 0x80000001, 0x80000001 // 0x10210: isp_ref0, isp_ref1 }; #endif /* APPLICATION_SECUREROM */ static const struct clock_config clk_configs[PMGR_CLK_COUNT] = { [PMGR_CLK_MCU_FIXED] = { &rPMGR_MCU_FIXED_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_PLL1, 1} } }, [PMGR_CLK_MCU] = { &rPMGR_MCU_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_PLL1, 1}, {PMGR_CLK_PLL1, 2}, {PMGR_CLK_PLL1, 4}, {PMGR_CLK_PLL1, 8}, {PMGR_CLK_PLL1, 16}, {PMGR_CLK_PLL4, 48} } }, [PMGR_CLK_MIPI_DSI] = { &rPMGR_MIPI_DSI_CLK_CFG, { {PMGR_CLK_PLL0, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL2, 1} } }, [PMGR_CLK_NCO_REF0] = { &rPMGR_NCO_REF0_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 2}, {PMGR_CLK_PLL4, 7} } }, [PMGR_CLK_NCO_REF1] = { &rPMGR_NCO_REF1_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 2}, {PMGR_CLK_PLL4, 7} } }, [PMGR_CLK_NCO_ALG0] = { &rPMGR_NCO_ALG0_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_NCO_ALG1] = { &rPMGR_NCO_ALG1_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_HSICPHY_REF_12M] = { &rPMGR_HSICPHY_REF_12M_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_OSC, 2} } }, [PMGR_CLK_USB480_0] = { &rPMGR_USB480_0_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 5} } }, [PMGR_CLK_USB480_1] = { &rPMGR_USB480_1_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 5} } }, [PMGR_CLK_USB_OHCI_48M] = { &rPMGR_USB_OHCI_48M_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_USB] = { &rPMGR_USB_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 20}, {PMGR_CLK_PLL4, 24}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 50}, {PMGR_CLK_PLL4, 72}, {PMGR_CLK_PLL4, 80} } }, [PMGR_CLK_USB_FREE_60M] = { &rPMGR_USB_FREE_60M_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 40} } }, [PMGR_CLK_ADSP_T] = { &rPMGR_ADSP_T_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 11}, {PMGR_CLK_PLL4, 14}, {PMGR_CLK_PLL4, 16} } }, [PMGR_CLK_ADSP_P] = { &rPMGR_ADSP_P_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 18}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_ADSP_TS] = { &rPMGR_ADSP_TS_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 9}, } }, [PMGR_CLK_SIO_C] = { &rPMGR_SIO_C_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 5}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 48} } }, [PMGR_CLK_SIO_P] = { &rPMGR_SIO_P_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 20}, {PMGR_CLK_PLL4, 24}, {PMGR_CLK_PLL4, 30}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 48}, {PMGR_CLK_OSC, 2}, {PMGR_CLK_OSC, 4} } }, [PMGR_CLK_ISP_C] = { &rPMGR_ISP_C_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 5}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 48} } }, [PMGR_CLK_ISP] = { &rPMGR_ISP_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 12} } }, [PMGR_CLK_ISP_SENSOR0_REF] = { &rPMGR_ISP_SENSOR0_REF_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_ISP_REF0, 1}, {PMGR_CLK_ISP_REF1, 1} } }, [PMGR_CLK_ISP_SENSOR1_REF] = { &rPMGR_ISP_SENSOR1_REF_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_ISP_REF0, 1}, {PMGR_CLK_ISP_REF1, 1} } }, [PMGR_CLK_VDEC] = { &rPMGR_VDEC_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_VENC] = { &rPMGR_VENC_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_VID0] = { &rPMGR_VID0_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 11}, {PMGR_CLK_PLL4, 39} } }, [PMGR_CLK_DISP0] = { &rPMGR_DISP0_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 14}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_DISP1] = { &rPMGR_DISP1_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 14}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_AJPEG_IP] = { &rPMGR_AJPEG_IP_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 11}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_AJPEG_WRAP] = { &rPMGR_AJPEG_WRAP_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 11}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_MSR] = { &rPMGR_MSR_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_AF] = { &rPMGR_AF_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 5}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 12} } }, [PMGR_CLK_ANS_DLL] = { &rPMGR_ANS_DLL_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 6} } }, [PMGR_CLK_ANS_C] = { &rPMGR_ANS_C_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 5}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 48} } }, [PMGR_CLK_ANC_LINK] = { &rPMGR_ANC_LINK_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 9}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 7}, // TODO: rdar://problem/10749966 {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 18}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_LIO] = { &rPMGR_LIO_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 10}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 14}, {PMGR_CLK_PLL4, 16}, {PMGR_CLK_PLL4, 18}, {PMGR_CLK_PLL4, 20}, {PMGR_CLK_PLL4, 48} } }, [PMGR_CLK_MCA0_M] = { &rPMGR_NCO_CLK_CFG(0), { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_OSC, 2}, {PMGR_CLK_OSC, 4} } }, [PMGR_CLK_MCA1_M] = { &rPMGR_NCO_CLK_CFG(1), { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_OSC, 2}, {PMGR_CLK_OSC, 4} } }, [PMGR_CLK_MCA2_M] = { &rPMGR_NCO_CLK_CFG(2), { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_OSC, 2}, {PMGR_CLK_OSC, 4} } }, [PMGR_CLK_MCA3_M] = { &rPMGR_NCO_CLK_CFG(3), { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_OSC, 2}, {PMGR_CLK_OSC, 4} } }, [PMGR_CLK_MCA4_M] = { &rPMGR_NCO_CLK_CFG(4), { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_NOT_SUPPORTED, 1}, {PMGR_CLK_OSC, 2}, {PMGR_CLK_OSC, 4} } }, [PMGR_CLK_SEP] = { &rPMGR_SEP_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 6}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 12}, {PMGR_CLK_PLL4, 24} } }, [PMGR_CLK_GPIO] = { &rPMGR_GPIO_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 100}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_SPI0_N] = { &rPMGR_SPI0_N_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 48}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_SPI1_N] = { &rPMGR_SPI1_N_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 48}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_SPI2_N] = { &rPMGR_SPI2_N_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 48}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_SPI3_N] = { &rPMGR_SPI3_N_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 48}, {PMGR_CLK_PLL4, 50} } }, [PMGR_CLK_DEBUG] = { &rPMGR_DEBUG_CLK_CFG, { {PMGR_CLK_OSC, 1}, {PMGR_CLK_S0, 1}, {PMGR_CLK_S1, 1}, {PMGR_CLK_S2, 1}, {PMGR_CLK_S3, 1}, {PMGR_CLK_PLL4, 20}, {PMGR_CLK_PLL4, 24}, {PMGR_CLK_PLL4, 30}, {PMGR_CLK_PLL4, 40}, {PMGR_CLK_PLL4, 48} } }, [PMGR_CLK_S0] = { &rPMGR_S0_CLK_CFG, { {PMGR_CLK_PLL1, 1}, {PMGR_CLK_PLL2, 1}, {PMGR_CLK_PLL3, 1}, {PMGR_CLK_PLL4, 2} } }, [PMGR_CLK_S1] = { &rPMGR_S1_CLK_CFG, { {PMGR_CLK_PLL1, 1}, {PMGR_CLK_PLL2, 1}, {PMGR_CLK_PLL3, 1}, {PMGR_CLK_PLL4, 2} } }, [PMGR_CLK_S2] = { &rPMGR_S2_CLK_CFG, { {PMGR_CLK_PLL1, 1}, {PMGR_CLK_PLL2, 1}, {PMGR_CLK_PLL3, 1}, {PMGR_CLK_PLL4, 2} } }, [PMGR_CLK_S3] = { &rPMGR_S3_CLK_CFG, { {PMGR_CLK_PLL1, 1}, {PMGR_CLK_PLL2, 1}, {PMGR_CLK_PLL3, 1}, {PMGR_CLK_PLL4, 2} } }, [PMGR_CLK_ISP_REF0] = { &rPMGR_ISP_REF0_CLK_CFG, { {PMGR_CLK_PLL4, 5}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9} } }, [PMGR_CLK_ISP_REF1] = { &rPMGR_ISP_REF1_CLK_CFG, { {PMGR_CLK_PLL4, 5}, {PMGR_CLK_PLL4, 7}, {PMGR_CLK_PLL4, 8}, {PMGR_CLK_PLL4, 9} } }, }; static uint32_t get_apsc_ccc_state(void); static void set_apsc_ccc_state(uint32_t target_state); #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) static void init_thermal_sensors(void); static void init_sochot(void); static void init_ccc_thermal_sensors(void); static void init_ccc_sochot(void); #endif static void clocks_get_frequencies(void); static void clocks_get_frequencies_range(uint32_t start_clk, uint32_t end_clk); static uint32_t get_pll(int32_t pll); static uint32_t get_pll_cpu(void); static void set_pll(int32_t pll, uint32_t p, uint32_t m, uint32_t s, bool vco_output); static uint32_t get_spare(int32_t spare); static void clocks_set_gates(uint64_t *devices); static void clocks_quiesce_internal(void); static void power_on_sep(void); // current clock frequencies static uint32_t clks[PMGR_CLK_COUNT + 1]; void platform_power_spin(uint32_t usecs) { arm_no_wfe_spin(usecs); } int clocks_init(void) { #if (APPLICATION_IBOOT && (PRODUCT_IBOOT || PRODUCT_IBEC || WITH_RECOVERY_MODE_IBSS)) clks[PMGR_CLK_OSC] = OSC_FREQ; clocks_get_frequencies(); #endif /* (APPLICATION_IBOOT && (PRODUCT_IBOOT || PRODUCT_IBEC || WITH_RECOVERY_MODE_IBSS)) */ return 0; } #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) /* * Look into rdar://problem/11647718 for further details. * S/W workaround below: * We need to turn ON the power partitions once. * Note: CCC, GFX are not effected so we won't touch them. * ADSP is not used so we leave it that way. * After SecureROM hands off control we have the following partitions ON * - Always on (Obviously!) * - AMC * - USB * - ACS * - ANS * So we will power ON * - DISP0 * - DISP1 * - MEDIA * - ISP * - VDEC * - VENC. * and power them down. */ static void enable_bira_work_around(void) { // Turn on the blocks clock_gate(CLK_DISP_BUSMUX, true); clock_gate(CLK_DISP1, true); clock_gate(CLK_MEDIA, true); clock_gate(CLK_VDEC, true); clock_gate(CLK_VENC, true); // FPGA doesn't have ISP #if !SUPPORT_FPGA clock_gate(CLK_ISP, true); clock_gate(CLK_ISP, false); #endif // Power down in reverse order. // Note: The PWR_XXX_CFG0 register for these partitions // enable power down. clock_gate(CLK_VENC, false); clock_gate(CLK_VDEC, false); clock_gate(CLK_MEDIA, false); clock_gate(CLK_DISP1, false); clock_gate(CLK_DISP_BUSMUX, false); } /* * To avoid di/dt voltage drop at the end of PLL relock. rdar://problem/13112194. */ static void set_ccc_pll_relock_div2(void) { uint32_t reg; reg = rCCC_PLL_CFG2; reg &= ~CCC_PLL_CFG2_RELOCKBYS2_S4_MASK; reg |= CCC_PLL_CFG2_RELOCKBYS2_S4_DIV2; rCCC_PLL_CFG2 = reg; } static void set_nco_clocks(void) { uint32_t i; // Enable this NCO with alg_ref0_clk and nco_ref0_clk. for(i = 0; i < 5; i++) rPMGR_NCO_CLK_CFG(i) |= (1 << 31); } static void apply_pmgr_tunables() { #define RESET_TIME_MASK (((1 << 8) - 1) << 0) #define CLAMP_TIME_MASK (((1 << 8) - 1) << 8) #define CLK_EN_TIME_MASK (((1 << 8) - 1) << 16) #define _PWRGATE_CFG0(rCFG0, CLK_EN_TIME, CLAMP_TIME, RESET_TIME) \ regTemp = rCFG0; \ regTemp &= ~RESET_TIME_MASK; \ regTemp |= (RESET_TIME << 0); \ regTemp &= ~CLAMP_TIME_MASK; \ regTemp |= (CLAMP_TIME << 8); \ regTemp &= ~CLK_EN_TIME_MASK; \ regTemp |= (CLK_EN_TIME << 16); \ rCFG0 = regTemp; #define RAMP_PRE_TIME_MASK (((1 << 12) - 1) << 0) #define RAMP_ALL_TIME_MASK (((1 << 12) - 1) << 16) #define _PWRGATE_CFG1(rCFG1, RAMP_ALL_TIME, RAMP_PRE_TIME) \ regTemp = rCFG1; \ regTemp &= ~RAMP_PRE_TIME_MASK; \ regTemp |= (RAMP_PRE_TIME << 0); \ regTemp &= ~RAMP_ALL_TIME_MASK; \ regTemp |= (RAMP_ALL_TIME << 16); \ rCFG1 = regTemp; uint32_t regTemp, i; // B0, B1 tunables. _PWRGATE_CFG0(rPMGR_PWRGATE_AMC_CFG0, 0x2, 0x2, 0x8); _PWRGATE_CFG1(rPMGR_PWRGATE_AMC_CFG1, 0x25, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_USB_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_USB_CFG1, 0xd, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_ACS_CFG0, 0x2, 0x2, 0x7); _PWRGATE_CFG1(rPMGR_PWRGATE_ACS_CFG1, 0x21, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_DISP0_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_DISP0_CFG1, 0x6a, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_DISP1_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_DISP1_CFG1, 0x40, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_ISP_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_ISP_CFG1, 0x63, 0x1); _PWRGATE_CFG0(rPMGR_PWRGATE_MEDIA_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_MEDIA_CFG1, 0x50, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_DEC_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_DEC_CFG1, 0x5a, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_ENC_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_ENC_CFG1, 0x67, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_ANS_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_ANS_CFG1, 0x49, 0x2); _PWRGATE_CFG0(rPMGR_PWRGATE_ADSP_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_ADSP_CFG1, 0x1c, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_GFX_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_GFX_CFG1, 0x16, 0x0); _PWRGATE_CFG0(rPMGR_PWRGATE_SEP_CFG0, 0x3, 0x2, 0x4); _PWRGATE_CFG1(rPMGR_PWRGATE_SEP_CFG1, 0x3c, 0x0); // 2. Apply MCU Async reset timing. rPMGR_MCU_ASYNC_RESET = (0x1 << 0) | (0x1 << 4) | (0x1 << 8); // 3. Apply VolMan tunables rPMGR_VOLMAN_SOC_DELAY = (0x5 << 0) | (0x6b << 10) | (0xbb8 << 20); rPMGR_VOLMAN_SRAM_DELAY = (0x5 << 0) | (0x6b << 10) | (0xbb8 << 20); rPMGR_VOLMAN_GFX_DELAY = (0x5 << 0) | (0x6b << 10) | (0xbb8 << 20); rPMGR_VOLMAN_CPU_DELAY = (0x5 << 0) | (0x6b << 10) | (0xbb8 << 20); // 4. Apply GFX EMA0 & EMA1 tunables. rPMGR_EMA_GFX0 = (0x4 << 0) | (0x4 << 5) | (0x4 << 11) | (0x4 << 16) | (0xa << 22) | (0x7 << 26) | (0x7 << 29); rPMGR_EMA_GFX1 = (0x5 << 0) | (0x4 << 3) | (0x1 << 6) | (0x4 << 10) | (0x4 << 13) | (0x5 << 20) | (0x4 << 23) | (0x3 << 29); // 5. PLLx_ANA_PARAMSy tunables for (i = 0; i < PMGR_PLL_COUNT; i++) { rPMGR_PLL_ANA_PARAMS(0, i) = 0x61a8308; rPMGR_PLL_ANA_PARAMS(1, i) = 0xcec0000; } // WA for the lack of a clean SW reset of PD_ANS. // By enabling power gating we will have a reset when // PD_ANS is turned back on. // , // has further details on this WA. rPMGR_PWRGATE_ANS_CFG0 |= (1 << 31); return; } static void apply_ccc_tunables(void) { rCCC_PRE_TD_TMR = 0x4; rCCC_PRE_FLUSH_TMR = 0x1000; } #endif /* * clocks_set_default - called by SecureROM, LLB, iBSS main via * platform_init_setup_clocks, so the current state of the chip is * either POR, or whatever 'quiesce' did when leaving SecureROM. */ int clocks_set_default(void) { uint32_t cnt; #if APPLICATION_IBOOT u_int32_t cpu_vid[kDVFM_STATE_IBOOT_CNT]; u_int32_t gpu_vid[kPMGR_GFX_STATE_MAX]; #endif clks[PMGR_CLK_OSC] = OSC_FREQ; volatile uint32_t *clkcfgs = PMGR_FIRST_CLK_CFG; volatile uint32_t *spare_clkcfgs = PMGR_FIRST_SPARE_CLK_CFG; // Setup bypass DVFM state rCCC_DVFM_ST(kDVFM_STATE_BYPASS) = ccc_dvfm_states[kDVFM_STATE_BYPASS]; #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) if (platform_get_chip_revision() > CHIP_REVISION_B0) { set_ccc_pll_relock_div2(); } #endif // Change all clocks to something safe clocks_quiesce_internal(); // Setup active DVFM state for the stage of boot. #if APPLICATION_SECUREROM rCCC_DVFM_ST(kDVFM_STATE_SECUREROM) = ccc_dvfm_states[kDVFM_STATE_SECUREROM]; #endif #if APPLICATION_IBOOT #ifndef BUCK_CPU #error BUCK_CPU not defined for this platform #endif // Get the binned voltages and update the CCC DVFM state registers. platform_get_cpu_voltages(kDVFM_STATE_IBOOT_CNT, cpu_vid); platform_convert_voltages(BUCK_CPU, kDVFM_STATE_IBOOT_CNT, cpu_vid); for (cnt = kDVFM_STATE_IBOOT; cnt < kDVFM_STATE_IBOOT_CNT; cnt++) { rCCC_DVFM_ST(cnt) = ccc_dvfm_states[cnt] | ((unsigned long long)cpu_vid[cnt] << CCC_DVFM_ST_SAFE_VOL_SHIFT); } // APSC sleep state will use the bypass state with V0. rCCC_DVFM_ST(kDVFM_STATE_BYPASS) = ccc_dvfm_states[kDVFM_STATE_BYPASS] | ((unsigned long long)cpu_vid[kDVFM_STATE_V0] << CCC_DVFM_ST_SAFE_VOL_SHIFT); // To prevent crashes/hangs during update install due to // mismatch of CCC clock config info between an old LLB and a new EDT+OS // we can populate the unused entries of the DVFM table with that of // Vmin, Fmin. That way we will always have a workable entry in the DVFM // table. // Assumptions: // There is always a valid state for iBoot and we will use that to populate the // empty table entries. for (cnt = kDVFM_STATE_IBOOT_CNT; cnt < CCC_DVFM_STATE_COUNT; cnt++) { rCCC_DVFM_ST(cnt) = ccc_dvfm_states[kDVFM_STATE_IBOOT] | ((unsigned long long)cpu_vid[kDVFM_STATE_IBOOT] << CCC_DVFM_ST_SAFE_VOL_SHIFT); } // Using one point calibration at the lower reading. uint32_t tempOffset0 = (rCCC_DVFM_EFUSE_TADC0 & 0x1FF) >> 2; uint32_t tempOffset1 = (rCCC_DVFM_EFUSE_TADC1 & 0x1FF) >> 2; // If device is calibrated with DVFM FUSE values, then we need to subtract this offset. if (0 != tempOffset0) { tempOffset0 = (0x38 - tempOffset0) & 0x7F; } if (0 != tempOffset1) { tempOffset1 = (0x38 - tempOffset1) & 0x7F; } // Program TVM thresholds rCCC_DVFM_CFG |= CCC_DVFM_CFG_TEMPTHRES0(0x34) | CCC_DVFM_CFG_TEMPTHRES1(0x48) | CCC_DVFM_CFG_TEMPOFFST0(tempOffset0) | CCC_DVFM_CFG_TEMPOFFST1(tempOffset1); // Using default programmed value of 1ms. // rCCC_DVFM_DLY = 0x180000; #endif #if APPLICATION_IBOOT #if WITH_HW_DWI extern int dwi_init(void); dwi_init(); #endif #endif set_apsc_ccc_state(active_state); // CCC clock set for this stage of boot. #if APPLICATION_IBOOT #ifndef BUCK_GPU #error BUCK_GPU not defined for this platform #endif platform_get_gpu_voltages(sizeof(gfx_states)/sizeof(gfx_states[0]), gpu_vid); platform_convert_voltages(BUCK_GPU, sizeof(gfx_states)/sizeof(gfx_states[0]), gpu_vid); for (cnt = 0; cnt < sizeof(gfx_states)/sizeof(gfx_states[0]); cnt++) { if (cnt == 0) gfx_states[cnt].dwi_val = 0; else gfx_states[cnt].dwi_val = gpu_vid[cnt]; set_gfx_perf_state(cnt, gfx_states); } // As per GFX Performance state software Sequence // Clear the bypass bit in PLL5 rPMGR_PLL_CTL(5) &= ~(1 << 27); // PLL5 relock mode is set to 1 to switch to bypass mode while re-locking. rPMGR_PLL_CFG(5) |= (1 << 24); // Enable performance state table to control PLL5 rPMGR_GFX_PERF_STATE_CTL |= (1 << 31); // The initial SRAM, SOC, CPU voltages are set by IIC writes to the PMU (in the pmu driver). #endif #ifdef PLL0_T //MIPI #if WITH_HW_AGC_MIPI rPMGR_AGILE_CLK_CTL = (1 << 28); rPMGR_PLL_ACG_CFG |= PMGR_ACG_CFG_PLLX_AUTO_DISABLE(0); #endif set_pll(0, PLL0_P, PLL0_M, PLL0_S, 0); #endif #ifdef PLL1_T //Mem set_pll(1, PLL1_P, PLL1_M, PLL1_S, 0); #endif #ifdef PLL2_T //Unused set_pll(2, PLL2_P, PLL2_M, PLL2_S, 0); #endif #ifdef PLL3_T //Unused set_pll(3, PLL3_P, PLL3_M, PLL3_S, 0); #endif #ifdef PLL4_T // SOC set_pll(4, PLL4_P, PLL4_M, PLL4_S, PLL4_VCO_ENABLED); #endif #ifdef PLL5_T // GFX set_pll(5, PLL5_P, PLL5_M, PLL5_S, 0); #endif #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) // Turn on NCO clocks before enabling MCA clocks. set_nco_clocks(); #endif // Set all spare clock divs to their active values for (cnt = 0; cnt < PMGR_SPARE_CLK_CFG_COUNT; cnt++) { spare_clkcfgs[cnt] = spare_divs_active[cnt]; SPIN_W_TMO_WHILE((spare_clkcfgs[cnt] & PMGR_CLK_CFG_PENDING) != 0); } // Set all but the spare clock divs to their active values for (cnt = 0; cnt < PMGR_CLK_CFG_COUNT; cnt++) { clkcfgs[cnt] = clk_divs_active[cnt]; SPIN_W_TMO_WHILE((clkcfgs[cnt] & PMGR_CLK_CFG_PENDING) != 0); } power_on_sep(); clocks_get_frequencies(); #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) apply_pmgr_tunables(); apply_ccc_tunables(); #endif return 0; } static uint32_t get_apsc_ccc_state(void) { return ((rCCC_DVFM_CFG_SEL >> 3) & 0x7); } static void set_apsc_ccc_state(uint32_t target_state) { rCCC_APSC_SCR = CCC_APSC_MANUAL_CHANGE(target_state); SPIN_W_TMO_WHILE((rCCC_APSC_SCR & CCC_APSC_PENDING) != 0); return; } static void set_pll(int32_t pll, uint32_t p, uint32_t m, uint32_t s, bool vco_output) { if (pll >= PMGR_PLL_COUNT) return; if (vco_output) rPMGR_PLL_CFG(pll) |= PMGR_PLL_VCO_OUT_SEL; rPMGR_PLL_CTL(pll) = (PMGR_PLL_P(p) | PMGR_PLL_M(m) | PMGR_PLL_S(s) | PMGR_PLL_ENABLE | PMGR_PLL_LOAD); SPIN_W_TMO_WHILE((rPMGR_PLL_CTL(pll) & PMGR_PLL_LOCKED) == 0); } static uint32_t get_pll_cpu(void) { uint32_t freq; uint32_t pllctl; pllctl = rCCC_PWRCTRL_PLL_SCR1; // Fcpu <= ((OSC * M) / P / S+1) freq = OSC_FREQ; freq *= (pllctl >> CCC_PLL_M_SHIFT) & CCC_PLL_M_MASK; freq /= (pllctl >> CCC_PLL_P_SHIFT) & CCC_PLL_P_MASK; freq /= 1 + ((pllctl >> CCC_PLL_S_SHIFT) & CCC_PLL_S_MASK); return freq; } static uint32_t get_pll(int32_t pll) { uint32_t pllctl; uint64_t freq = 0; pllctl = rPMGR_PLL_CTL(pll); // If PLL is not enabled, check for External Bypass if ((pllctl & PMGR_PLL_ENABLE) == 0) { if ((pllctl & PMGR_PLL_EXT_BYPASS) == 0) return 0; else return OSC_FREQ; } freq = OSC_FREQ; freq *= ((pllctl >> PMGR_PLL_M_SHIFT) & PMGR_PLL_M_MASK); if ((rPMGR_PLL_CFG(pll) & PMGR_PLL_VCO_OUT_SEL) == 0) { freq /= (1 + ((pllctl >> PMGR_PLL_S_SHIFT) & PMGR_PLL_S_MASK)); } else { freq *= 2; } freq /= ((pllctl >> PMGR_PLL_P_SHIFT) & PMGR_PLL_P_MASK); #if DEBUG_BUILD if (freq> 0xFFFFFFFF) panic("Frequency value does not fit in uint32_t"); #endif return (uint32_t)freq; } static uint32_t get_spare(int32_t spare) { uint32_t reg_val, src_idx, src_clk, src_factor, div; volatile uint32_t *spare_clkcfg = clk_configs[PMGR_CLK_S0 + spare].clock_reg; reg_val = *spare_clkcfg; div = reg_val & 0x3F; if (((reg_val & PMGR_CLK_CFG_ENABLE) == 0) || div == 0) return 0; src_idx = (reg_val >> 24) & PMGR_CLK_CFG_SRC_SEL_MASK; src_clk = clk_configs[PMGR_CLK_S0 + spare].sources[src_idx].src_clk; src_factor = clk_configs[PMGR_CLK_S0 + spare].sources[src_idx].factor; return (clks[src_clk] / src_factor) / div; } static void clocks_get_frequencies(void) { #if SUPPORT_FPGA uint32_t cnt; uint32_t freq = OSC_FREQ; for (cnt = 0; cnt < PMGR_CLK_COUNT; cnt++) clks[cnt] = freq; clks[PMGR_CLK_CPU] = 5000000; clks[PMGR_CLK_MCU] = 10000000; clks[PMGR_CLK_MCU_FIXED]= 10000000; clks[PMGR_CLK_USB] = 12000000; #elif SUB_TARGET_CYCLONIC uint32_t cnt; uint32_t freq = OSC_FREQ; for (cnt = 0; cnt < PMGR_CLK_COUNT; cnt++) clks[cnt] = freq; #else uint32_t cnt; clks[PMGR_CLK_OSC] = OSC_FREQ; // Use get_pll() to establish the frequencies (unconfigured PLLs will bypass OSC) for (cnt = 0; cnt < PMGR_PLL_COUNT; cnt++) clks[PMGR_CLK_PLL0 + cnt] = get_pll(cnt); // Use get_spare() to establish the frequencies for spare clocks (unconfigured will be skipped) for (cnt = 0; cnt < PMGR_SPARE_CLK_CFG_COUNT; cnt++) clks[PMGR_CLK_S0 + cnt] = get_spare(cnt); clks[PMGR_CLK_CPU] = get_pll_cpu(); clocks_get_frequencies_range(PMGR_CLK_MCU_FIXED, PMGR_CLK_DEBUG); #endif } static void clocks_get_frequencies_range(uint32_t start_clk, uint32_t end_clk) { volatile uint32_t *reg; uint32_t cnt, val, src_idx, src_clk, src_factor; if (start_clk < PMGR_CLK_MCU_FIXED || end_clk > PMGR_CLK_DEBUG) return; for (cnt = start_clk; cnt <= end_clk; cnt++) { reg = clk_configs[cnt].clock_reg; val = *reg; if ((val & PMGR_CLK_CFG_ENABLE) == 0) { clks[cnt] = 0; continue; } src_idx = (val >> 24) & PMGR_CLK_CFG_SRC_SEL_MASK; src_clk = clk_configs[cnt].sources[src_idx].src_clk; src_factor = clk_configs[cnt].sources[src_idx].factor; clks[cnt] = clks[src_clk] / src_factor; } } void dump_clock_frequencies() { uint32_t i; for (i = 0; i < PMGR_CLK_COUNT; i++) { dprintf(DEBUG_CRITICAL, "clk[%d] -> %u\n", i, clks[i]); } } static void clock_update_range(uint32_t first, uint32_t last, const uint32_t clkdata) { volatile uint32_t *clkcfgs = PMGR_FIRST_CLK_CFG; uint32_t reg; reg = first; while (reg <= last) { clkcfgs[reg] = clkdata; SPIN_W_TMO_WHILE(clkcfgs[reg] & PMGR_CLK_CFG_PENDING); reg++; } } static void restore_clock_config_state(void) { uint32_t cnt; // 2. Write reset value to ACG, CLK_DIVIDER_ACG_CFG, CLK_DIVIDER_ACG_CFG1, and PLL_ACG_CFG rPMGR_MISC_ACG = 0; rPMGR_CLK_DIVIDER_ACG_CFG = 0; rPMGR_CLK_DIVIDER_ACG_CFG1 = 0; rPMGR_PLL_ACG_CFG = 0; // 5. Write reset value for all mux clock configs (excluding spares, mcu, mcu_fixed) clock_update_range(PMGR_CLK_NUM(MIPI_DSI), PMGR_CLK_NUM(DEBUG), 0x80100000); // 6. Write all PLLx_CTL.EXT_BYPASS to 1 for (cnt = 0; cnt < PMGR_PLL_COUNT; cnt++) { if (cnt == 1) continue; rPMGR_PLL_CTL(cnt) |= PMGR_PLL_EXT_BYPASS; SPIN_W_TMO_WHILE((rPMGR_PLL_CTL(cnt) & PMGR_PLL_BYP_ENABLED) == 0); } // 8. Make sure PLLs OFF mode is set to powered down. for (cnt = 0; cnt < PMGR_PLL_COUNT; cnt++) { if (cnt == 1) continue; // Mem PLL rPMGR_PLL_CFG(cnt) |= (2 << 30); // PLL.OFF_MODE = Powered down. rPMGR_PLL_CTL(cnt) |= (PMGR_PLL_ENABLE | PMGR_PLL_LOAD); SPIN_W_TMO_WHILE((rPMGR_PLL_CTL(cnt) & PMGR_PLL_LOCKED) == 0); } // 9. Write reset value to all PLLx_CTL for (cnt = 0; cnt < PMGR_PLL_COUNT; cnt++) { if (cnt == 1) continue; // Mem PLL rPMGR_PLL_CTL(cnt) = 0x0A001010; // Ext bypass, fb_div = 1, pre_div = 1 SPIN_W_TMO_WHILE((rPMGR_PLL_CTL(cnt) & PMGR_PLL_BYP_ENABLED) == 0); } // 11. Write reset value to spare and ISP_REF0/1 clock_update_range(PMGR_CLK_NUM(S0), PMGR_CLK_NUM(ISP_REF1), 0x80000001); // 12. Put CPU clock back into its reset default. rCCC_DVFM_SCR = (1 << 2); // CPU PLL goes to reset mode when off. set_apsc_ccc_state(kDVFM_STATE_BYPASS); } static void power_on_sep(void) { volatile uint32_t *reg = (volatile uint32_t *)((uint64_t *)PMGR_FIRST_PS + CLK_SEP); uint32_t val = *reg; val &= ~(1 << 28); // Clear Auto_PM_EN *reg = val; SPIN_W_TMO_WHILE(((*reg >> 4) & 0xF) != 0xF); // Wait for SEP to turn on. return; } static void clocks_quiesce_internal(void) { uint64_t devices[2]; // Disable all voltage changes everywhere! rPMGR_VOLMAN_CTL |= PMGR_VOLMAN_DISABLE_VOL_CHANGE; #if APPLICATION_IBOOT rPMGR_VOLMAN_CTL |= 1; // Temporary till B0 chip arrives #endif // We don't touch CPU0, CPU1, CPM // Following devices needs to be on: // LIO, IOMUX, AIC, DEBUG // GPIO, SIO_P, SIO, MCC, MCU, // AMP, USB, USBCTRL, USB OTG, SMX, SF, CP devices[0] = (0x1ULL << 3) | (0x1ULL << 4) | (0x1ULL << 5) | (0x1ULL << 6) | (0x1ULL << 8) | (0x1ULL << 33) | (0x1ULL << 34) | (0x1ULL << 40) | (0x1ULL << 41) | (0x1ULL << 42) | (0x1ULL << 43) | (0x1ULL << 44) | (0x1ULL << 49) | (0x1ULL << 50) | (0x1ULL << 51) | (0x1ULL << 52); // ANS. devices[1] = (0x1ULL << 0); // Turn on/off critical device clocks clocks_set_gates(devices); // Disable performance state table to control PLL5 rPMGR_GFX_PERF_STATE_CTL = 0; restore_clock_config_state(); } void clocks_quiesce(void) { clocks_quiesce_internal(); } uint32_t clocks_set_performance(uint32_t performance_level) { #if APPLICATION_IBOOT int cfg_sel = -1; uint32_t src_index; #endif uint32_t ccc_state = get_apsc_ccc_state(); if (ccc_state != active_state) set_apsc_ccc_state(active_state); #if APPLICATION_IBOOT if (performance_level == kPerformanceMemoryLow || performance_level == kPerformanceMemoryFull) { cfg_sel = get_amc_cfg_sel(performance_level); if (cfg_sel == -1) panic("pmgr:cfg_sel not set correctly for configuring amc clock"); src_index = (performance_level == kPerformanceMemoryLow) ? 6 : 1; set_amc_clk_config(src_index, cfg_sel); clocks_get_frequencies_range(PMGR_CLK_MCU_FIXED, PMGR_CLK_MCU); } #endif // At this point we should have CCC clock set for this stage of boot. return kPerformanceHigh; } void clock_get_frequencies(uint32_t *clocks, uint32_t count) { uint32_t cnt = PMGR_CLK_COUNT; if (cnt > count) cnt = count; memcpy(clocks, clks, cnt * sizeof(uint32_t)); } uint32_t clock_get_frequency(int clock) { uint32_t freq = 0; // XXX TODO switch (clock) { case CLK_NCLK: case CLK_FIXED: case CLK_TIMEBASE: freq = clks[PMGR_CLK_OSC]; break; case CLK_PCLK: case CLK_PERIPH: case CLK_I2C0: case CLK_I2C1: case CLK_I2C2: freq = clks[PMGR_CLK_SIO_P]; break; case CLK_MEM: freq = clks[PMGR_CLK_MCU]; break; case CLK_ANS_LINK: freq = clks[PMGR_CLK_ANC_LINK]; break; case CLK_BUS: freq = clks[PMGR_CLK_LIO]; break; case CLK_CPU: freq = clks[PMGR_CLK_CPU]; break; case CLK_MIPI: freq = clks[PMGR_CLK_MIPI_DSI]; break; default: break; } return freq; } void clock_set_frequency(int clock, uint32_t divider, uint32_t pll_p, uint32_t pll_m, uint32_t pll_s, uint32_t pll_t) { return; } void clock_gate(int device, bool enable) { volatile uint32_t *reg = (volatile uint32_t *)((uint64_t *)PMGR_FIRST_PS + device); // Make sure we are within limits. Also we won't touch ADSP. if ((reg > PMGR_LAST_PS) || (device == CLK_ADSP)) return; // Set the PS field to the requested level if (enable) *reg |= PMGR_PS_RUN_MAX; else *reg &= ~PMGR_PS_RUN_MAX; // Wait for the MANUAL_PS and ACTUAL_PS fields to be equal SPIN_W_TMO_WHILE((*reg & PMGR_PS_MANUAL_PS_MASK) != ((*reg >> PMGR_PS_ACTUAL_PS_SHIFT) & PMGR_PS_ACTUAL_PS_MASK)); } static void clocks_set_gates(uint64_t *devices) { uint32_t i, idx = 1; // Turn off devices hi to lo order (to meet dependencies). for (i = PMGR_LAST_DEVICE - 1; i >= CLK_LIO;) { clock_gate(i, ( (devices[idx] >> ((uint64_t) (i % 64)) ) & 0x1) ); if (i && ((i % 64) == 0)) idx--; i--; } idx = 0; // Turn on devices from lo to hi (to meet dependencies). for (i = CLK_LIO; ( (i < 128) && (i < PMGR_LAST_DEVICE) ); ) { if (i && ((i % 64) == 0)) idx++; clock_gate(i, ( (devices[idx] >> ((uint64_t) (i % 64)) ) & 0x1) ); i++; } return; } void platform_system_reset(bool panic) { #if WITH_BOOT_STAGE if (!panic) boot_set_stage(kPowerNVRAMiBootStageOff); #endif wdt_system_reset(); while (1); } void platform_reset(bool panic) { #if WITH_BOOT_STAGE if (!panic) boot_set_stage(kPowerNVRAMiBootStageOff); #endif wdt_chip_reset(); while (1); } void platform_power_init(void) { /* Disable Power gating for CPU0, this is needed to avoid WFI from powering down the core */ /* Remove this once following is addressed: Core 0 power gating enabled by default */ rPMGR_PWRGATE_CPU0_CFG0 &= ~(1UL << 31); } void thermal_init(void) { #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) /* Read thermal fused values and store into thermal registers */ init_thermal_sensors(); /* Read thermal fused values and store into thermal registers & init instantenous read for sochot */ init_ccc_thermal_sensors(); /* Setup SoC Sochot 0 & 1 */ init_sochot(); /* Setup CCC Sochot 0 & 1 */ init_ccc_sochot(); #endif } void platform_watchdog_tickle(void) { // Varies by target. This layer between is necessary so that // we don't go straight from generic code to target. target_watchdog_tickle(); } void clock_reset_device(int device) { volatile uint32_t *reg = (volatile uint32_t *)((uint64_t *)PMGR_FIRST_PS + device); // Make sure we are within limits. Also we won't touch ADSP. if ((device >= PMGR_LAST_DEVICE) || (device == CLK_ADSP)) return; // XXX TODO switch(device) { case CLK_MCU: case CLK_ANS: case CLK_SIO: *reg |= PMGR_PS_RESET; spin(1); *reg &= ~PMGR_PS_RESET; break; default: break; } } #if APPLICATION_IBOOT static void set_gfx_perf_state(uint32_t state_num, struct gfx_state_info *gfx_state) { uint32_t pll_enable = 0; if (state_num >= sizeof(gfx_states)/sizeof(gfx_states[0])) return; // This is deductive. If feedback divider is 0 the PLL shouldn't output anything. pll_enable = gfx_state[state_num].fb_div ? 1 : 0; rPMGR_GFX_PERF_STATE_ENTRY(state_num) = ((gfx_state[state_num].dwi_val & 0xFF) << 24) | ((pll_enable & 0x1) << 21) | ((gfx_state[state_num].fb_div & 0x1FF) << 12) | ((gfx_state[state_num].pre_div & 0x1F) << 4) | (gfx_state[state_num].op_div & 0xF); return; } static int get_amc_cfg_sel(uint32_t performance_level) { int cfg_sel; switch(performance_level) { case kPerformanceMemoryLow: cfg_sel = 3; break; case kPerformanceMemoryFull: cfg_sel = 0; break; default: cfg_sel = -1; break; } return cfg_sel; } static void set_amc_clk_config(uint32_t src_index, uint32_t cfg_sel) { volatile uint32_t *clkcfgs = PMGR_FIRST_CLK_CFG; uint32_t mcu_clk_cfg_reg; mcu_clk_cfg_reg = clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)]; mcu_clk_cfg_reg &= ~(0x7 << PMGR_CLK_CFG_SRC_SEL_SHIFT); mcu_clk_cfg_reg &= ~(0x3 << PMGR_CLK_CFG_CFG_SEL_SHIFT); mcu_clk_cfg_reg |= ((src_index & 0x7) << PMGR_CLK_CFG_SRC_SEL_SHIFT); mcu_clk_cfg_reg |= ((cfg_sel & 0x3) << PMGR_CLK_CFG_CFG_SEL_SHIFT); clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)] = mcu_clk_cfg_reg; // Wait for Pending bit to clear. SPIN_W_TMO_WHILE((clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)] >> PMGR_CLK_CFG_PENDING_SHIFT) & 0x1); return; } #endif #if WITH_DEVICETREE static uint64_t get_freq_from_ccc_state(uint64_t state_entry) { // Fcpu <= ((OSC * M) / P / S+1) uint64_t freq = OSC_FREQ; freq *= ((state_entry >> 4) & 0x1FF); freq /= ((state_entry >> 13) & 0x1F) ? ((state_entry >> 13) & 0x1F) : 1; freq /= (1 + ((state_entry >> 0) & 0xF)); return freq; } void pmgr_update_device_tree(DTNode *pmgr_node) { uint32_t num_freqs = 0; uint32_t propSize; uint64_t period_ns; uint64_t freq = 0; uint32_t volt; uint32_t cpu_vid[kDVFM_STATE_IBOOT_CNT]; char *propName; void *propData; // Populate the devicetree with relevant values. propName = "nominal-performance1"; if (FindProperty(pmgr_node, &propName, &propData, &propSize)) { if (propSize != sizeof(uint32_t)) panic("pmgr property nominal-performance1 is of wrong size."); freq = get_freq_from_ccc_state(ccc_dvfm_states[kDVFM_STATE_VNOM]); if (freq == 0) panic("pmgr Fnom Operating point not defined correctly"); period_ns = 1000000000ULL << 16; period_ns /= freq; ((uint32_t *)propData)[0] = period_ns; } else { panic("pmgr property nominal-performance1 not found."); } propName = "boost-performance1"; // Note: Boost is not mandatory in all platforms. if (FindProperty(pmgr_node, &propName, &propData, &propSize)) { if (propSize != sizeof(uint32_t)) panic("pmgr property boost-performance1 is of wrong size"); freq = get_freq_from_ccc_state(ccc_dvfm_states[kDVFM_STATE_VBOOST]); if (freq == 0) panic("pmgr Fboost Operating point not defined correctly"); period_ns = 1000000000ULL << 16; period_ns /= freq; ((uint32_t *)propData)[0] = period_ns; } propName = "voltage-states1"; if (FindProperty(pmgr_node, &propName, &propData, &propSize)) { if (propSize/sizeof(uint32_t) < kVOLTAGE_STATES1_SIZE) { panic("pmgr number of states less than required for voltage-states1"); } num_freqs = kVOLTAGE_STATES1_COUNT; platform_get_cpu_voltages(kDVFM_STATE_IBOOT_CNT, cpu_vid); for (int32_t i = num_freqs - 1, j = 0; i >= 0; i--, j++) { freq = get_freq_from_ccc_state(ccc_dvfm_states[kDVFM_STATE_VMAX - j]); volt = cpu_vid[kDVFM_STATE_VMAX - j]; if (freq != 0) { period_ns = 1000000000ULL << 16; period_ns /= freq; ((uint32_t *)propData)[2*i] = period_ns; ((uint32_t *)propData)[2*i+1] = volt; } } } propName = "total-rails-leakage"; if (FindProperty(pmgr_node, &propName, &propData, &propSize)) { if (propSize >= sizeof(uint32_t)) { *(uint32_t *)propData = chipid_get_total_rails_leakage(); } } return; } void pmgr_gfx_update_device_tree(DTNode *gfx_node) { u_int32_t count, propSize, num_states = 0, state_val; char *propName; void *propData; propName = "perf-states"; if (FindProperty(gfx_node, &propName, &propData, &propSize)) { if (propSize != (2 * sizeof(u_int32_t) * kPMGR_GFX_STATE_MAX)) { panic("gfx property perf-state has wrong size"); } // Read the values programmed into the GFX perf state table // and populate the device tree. for (count = 0; count < kPMGR_GFX_STATE_MAX; count++) { state_val = rPMGR_GFX_PERF_STATE_ENTRY(count); // Any but the first entry with a value of 0 marks the end of the number of valid states. if ((count != 0) && (state_val == 0)) { num_states = count; break; } ((u_int32_t *)propData)[count * 2 + 0] = PMGR_PLL_FREQ((state_val >> 12) & 0x1FF, (state_val >> 4) & 0x1F, state_val & 0xF); ((u_int32_t *)propData)[count * 2 + 1] = platform_get_dwi_to_mv(BUCK_GPU, (state_val >> 24) & 0xFF); } // If all the entries are valid. if (count == kPMGR_GFX_STATE_MAX) { num_states = kPMGR_GFX_STATE_MAX; } } propName = "perf-state-count"; if (FindProperty(gfx_node, &propName, &propData, &propSize)) { *(u_int32_t *)propData = num_states; } return; } static void override_device_tree_property_uint32(DTNode *node, char *propName, uint32_t propValue, bool panicIfMissing) { void *propData; uint32_t propSize; if (FindProperty(node, &propName, &propData, &propSize)) { if (propSize != sizeof(uint32_t)) { panic("%s property is of wrong size.", propName); } ((uint32_t *)propData)[0] = propValue; } else if (panicIfMissing) { panic("%s property not found.", propName); } } void sochot_pmgr_update_device_tree(DTNode *node) { } void sochot_ccc_update_device_tree(DTNode *node) { } void temp_sensor_pmgr_update_device_tree(DTNode *node) { } void temp_sensor_ccc_update_device_tree(DTNode *node) { } #endif #if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC) void init_thermal_sensors(void) { // Bits [1 - 0] PWRDN Mode and enable. // Bits [ 2 ] Reserved. // Bits [ 3 ] Stat mode for Avg Max on. // Bits [18 - 4] TADC_CFG Tmpsadc configuration bits. // Bits [31 - 19] Misc interrupt/Alarm stick bits not used. rPMGR_THERMAL0_CTL0 = 0x5278; rPMGR_THERMAL1_CTL0 = 0x5278; // Bits [15 - 0] PWRDN_START 20us wait // Bits [31 - 16] PWRDN_GAP Gap between two readings. rPMGR_THERMAL0_CTL1 = 0x019401E4; rPMGR_THERMAL1_CTL1 = 0x03A801E4; // Bits [7 - 0] Conv_Cycle, cycles to wait before data is valid. // Bits [15 - 8] Enable_Cycle, cycles to wait before adc_en can be deasserted. // Bits [23 - 16] Finish_gap, Cycles to wait before adc_en can be reasserted. // Bits [31 - 24] Reserved. // // Use default // for (u_int32_t sensorID = 0; sensorID < 2; sensorID++) { u_int32_t fusedTempValueAt70 = chipid_get_fused_pmgr_thermal_sensor_cal_70C(sensorID); u_int32_t fusedTempValueAt25 = chipid_get_fused_pmgr_thermal_sensor_cal_25C(sensorID); u_int32_t tempSlope = 0x100; if ((fusedTempValueAt25 == 0) || (fusedTempValueAt70 == 0)) { if (chipid_valid_thermal_sensor_cal_data_expected()) { panic("SoC temperature sensor calibration data not found"); } } // Should probably make sure we don't divide by zero. if (fusedTempValueAt25 == fusedTempValueAt70) { fusedTempValueAt70 = 70; fusedTempValueAt25 = 25; dprintf(DEBUG_INFO, "Invalid soc thermal fuse values\n"); } // 45 = 70 - 25 tempSlope = (45*256) / (fusedTempValueAt70 - fusedTempValueAt25); // Calculate the real offset. u_int32_t realOffset = 25 - ((tempSlope * fusedTempValueAt25) / 256); // Bits 23 16 // Temp_OFFSET => [8 bit signed integer] // Bits 9 0 // TEMP_SLOPE => [2 bit integer| 8 bit decimal] switch (sensorID) { case 0: rPMGR_THERMAL0_CTL3 = (realOffset & 0xFF) << 16; rPMGR_THERMAL0_CTL3 |= tempSlope & 0x3FF; break; case 1: rPMGR_THERMAL1_CTL3 = (realOffset & 0xFF) << 16; rPMGR_THERMAL1_CTL3 |= tempSlope & 0x3FF; break; } } // Per 13799299, enable the temperature sensors after applying calibration efuse info and before enabling DVTM rPMGR_THERMAL0_CTL0 |= 0x01; rPMGR_THERMAL1_CTL0 |= 0x01; } void init_sochot(void) { // Majority of work done in kext with EDT configuration // Setup GFX clock divider rPMGR_GFX_PERF_STATE_SOCHOT = 0x1; } void init_ccc_thermal_sensors(void) { // Per 13146352, CCC thermal sensors for B0+ adopt same register interface as PMGR sensors rCCC_THRM0_CTL0 |= 0x08; rCCC_THRM1_CTL0 |= 0x08; // Bits [15 - 0] PWRDN_START 20us wait // Bits [31 - 16] PWRDN_GAP Gap between two readings. rCCC_THRM0_CTL1 = 0x019401E4; rCCC_THRM1_CTL1 = 0x019401E4; // Bits [7 - 0] Conv_Cycle, cycles to wait before data is valid. // Bits [15 - 8] Enable_Cycle, cycles to wait before adc_en can be deasserted. // Bits [23 - 16] Finish_gap, Cycles to wait before adc_en can be reasserted. // Bits [31 - 24] Reserved. // // Use default // for (u_int32_t sensorID = 0; sensorID < 2; sensorID++) { u_int32_t fusedTempValueAt70 = chipid_get_fused_ccc_thermal_sensor_cal_70C(sensorID); u_int32_t fusedTempValueAt25 = chipid_get_fused_ccc_thermal_sensor_cal_25C(sensorID); u_int32_t tempSlope = 0x100; if ((fusedTempValueAt25 == 0) || (fusedTempValueAt70 == 0)) { if (chipid_valid_thermal_sensor_cal_data_expected()) { panic("CCC temperature sensor calibration data not found"); } } // Should probably make sure we don't divide by zero. if (fusedTempValueAt25 == fusedTempValueAt70) { fusedTempValueAt70 = 70; fusedTempValueAt25 = 25; dprintf(DEBUG_INFO, "Invalid soc thermal fuse values\n"); } // 45 = 70 - 25 tempSlope = (45*256) / (fusedTempValueAt70 - fusedTempValueAt25); // Calculate the real offset. u_int32_t realOffset = 25 - ((tempSlope * fusedTempValueAt25) / 256); // Bits 23 16 // Temp_OFFSET => [8 bit signed integer] // Bits 9 0 // TEMP_SLOPE => [2 bit integer| 8 bit decimal] switch (sensorID) { case 0: rCCC_THRM0_CTL3 = (realOffset & 0xFF) << 16; rCCC_THRM0_CTL3 |= tempSlope & 0x3FF; break; case 1: rCCC_THRM1_CTL3 = (realOffset & 0xFF) << 16; rCCC_THRM1_CTL3 |= tempSlope & 0x3FF; break; } } // Per 13799299, enable the temperature sensors after applying calibration efuse info and before enabling DVTM rCCC_THRM0_CTL0 |= 0x01; rCCC_THRM1_CTL0 |= 0x01; } void init_ccc_sochot(void) { // Setup CCC clock frequency by specifying index into DVFM table. rCCC_DVFM_FSHOT_IDX = 0x2; } #endif