iBoot/platform/s7002/pmgr/pmgr.c

1174 lines
33 KiB
C

/*
* Copyright (C) 2013-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 <debug.h>
#include <drivers/power.h>
#include <platform.h>
#include <platform/clocks.h>
#include <platform/gpio.h>
#include <platform/gpiodef.h>
#include <platform/power.h>
#include <platform/timer.h>
#include <platform/soc/chipid.h>
#include <platform/soc/hwclocks.h>
#include <platform/soc/miu.h>
#include <platform/soc/pmgr.h>
#include <platform/int.h>
#include <sys/boot.h>
#include <target.h>
#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))
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[8]; // List of sources
};
/* ******************************************************************************** */
#if APPLICATION_IBOOT
/* PLL0 @532.8MHz */
#define PLL0 0
#define PLL0_O OSC_FREQ
#define PLL0_P 5
#define PLL0_M 111
#define PLL0_S 0
#define PLL0_V PLL_VCO_TARGET(PLL0)
#define PLL0_T PLL_FREQ_TARGET(PLL0)
static const uint32_t clk_divs_active[PMGR_CLK_CFG_COUNT] = {
0x80100000, // gpio on 24MHz
0x83100000, // mcu on 533MHz, mcu_cfg_sel 0 (highest freq)
0x81100000, // mcu_fixed on 533MHz
0x84100000, // gfx on 133.25MHz
0x83100000, // mipi_dsi on 533MHz
0x83100000, // vid on 22.21MHz
0x83100000, // media on 133.25MHz
0x83100000, // disp on 106.6MHz
0x84100000, // sdio on 88.8MHz
0x83100000, // ans on 106.6MHz
0x83100000, // pio on 106.6MHz
0x83100000, // lio on 106.6MHz
0x83100000, // aue on 106.6MHz
0x83100000, // usb on 53.3MHz
0x83100000, // spi0 on 53.3MHz
0x83100000, // spi1 on 53.3MHz
0x83100000, // nco_ref0 on 266.5MHz
0x83100000, // nco_ref1 on 266.5MHz
0x80100000, // nco_alg0 on 24MHz
0x80100000, // nco_alg1 on 24MHz
0x83100000, // mca0_m on nco_ref0
0x84100000 // mca1_m on nco_ref1
};
static const uint32_t spare_divs_active[PMGR_SPARE_CLK_CFG_COUNT] = {
0x00000000, // s0 off
0x00000000 // s1 off
};
#endif /* APPLICATION_IBOOT */
/* ******************************************************************************** */
#if APPLICATION_SECUREROM
/* PLL0 @266.4MHz */
#define PLL0 0
#define PLL0_O OSC_FREQ
#define PLL0_P 10
#define PLL0_M 111
#define PLL0_S 0
#define PLL0_V PLL_VCO_TARGET(PLL0)
#define PLL0_T PLL_FREQ_TARGET(PLL0)
// 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, // gpio on 24MHz
0x83100000, // mcu on 266.4MHz, mcu_cfg_sel 0 (highest freq)
0x81100000, // mcu_fixed on 266.4MHz
0x80100000, // gfx on 24MHz
0x80100000, // mipi_dsi on 24MHz
0x80100000, // vid on 24MHz
0x80100000, // media on 24MHz
0x80100000, // disp on 24MHz
0x80100000, // sdio on 24MHz
0x83100000, // ans on 53.3MHz
0x83100000, // pio on 53.3MHz
0x80100000, // lio on 24MHz
0x80100000, // aue on 24MHz
0x84100000, // usb on 53.3MHz
0x80100000, // spi0 on 24MHz
0x80100000, // spi1 on 24MHz
0x80100000, // nco_ref0 on 24MHz
0x80100000, // nco_ref1 on 24MHz
0x80100000, // nco_alg0 on 24MHz
0x80100000, // nco_alg1 on 24MHz
0x80100000, // mca0_m on 24MHz
0x80100000 // mca1_m on 24MHz
};
static const uint32_t spare_divs_active[PMGR_SPARE_CLK_CFG_COUNT] = {
0x80000001, // s0 on 266.4MHz
0x80000001 // s1 on 266.4MHz
};
#endif /* APPLICATION_SECUREROM */
/* ******************************************************************************** */
#define CLOCK_SOURCES_MAX 8
static const struct clock_config clk_configs[PMGR_CLK_COUNT] = {
[PMGR_CLK_GPIO] = {
&rPMGR_GPIO_CLK_CFG,
{
{PMGR_CLK_OSC, 1}
}
},
[PMGR_CLK_MCU] = {
&rPMGR_MCU_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 1},
{PMGR_CLK_PLL0, 2},
{PMGR_CLK_PLL0, 3},
{PMGR_CLK_PLL0, 4},
{PMGR_CLK_PLL0, 5},
}
},
[PMGR_CLK_MCU_FIXED] = {
&rPMGR_MCU_FIXED_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_PLL0, 1},
}
},
[PMGR_CLK_GFX] = {
&rPMGR_GFX_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 3},
{PMGR_CLK_PLL0, 4},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
}
},
[PMGR_CLK_MIPI_DSI] = {
&rPMGR_MIPI_DSI_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 1},
{PMGR_CLK_PLL0, 2},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
}
},
[PMGR_CLK_VID] = {
&rPMGR_VID_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 24},
{PMGR_CLK_OSC, 2},
{PMGR_CLK_OSC, 3},
{PMGR_CLK_OSC, 4},
}
},
[PMGR_CLK_MEDIA] = {
&rPMGR_MEDIA_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 4},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
}
},
[PMGR_CLK_DISP] = {
&rPMGR_DISP_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
{PMGR_CLK_PLL0, 10},
}
},
[PMGR_CLK_SDIO] = {
&rPMGR_SDIO_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
{PMGR_CLK_PLL0, 10},
}
},
[PMGR_CLK_ANS] = {
&rPMGR_ANS_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
{PMGR_CLK_PLL0, 10},
}
},
[PMGR_CLK_PIO] = {
&rPMGR_PIO_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
{PMGR_CLK_PLL0, 10},
{PMGR_CLK_OSC, 4},
}
},
[PMGR_CLK_LIO] = {
&rPMGR_LIO_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 6},
{PMGR_CLK_PLL0, 8},
{PMGR_CLK_PLL0, 10},
{PMGR_CLK_OSC, 4},
}
},
[PMGR_CLK_AUE] = {
&rPMGR_AUE_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 5},
{PMGR_CLK_PLL0, 4},
}
},
[PMGR_CLK_USB] = {
&rPMGR_USB_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 10},
{PMGR_CLK_PLL0, 5},
}
},
[PMGR_CLK_SPI0_N] = {
&rPMGR_SPI0_N_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 10},
{PMGR_CLK_PLL0, 20},
}
},
[PMGR_CLK_SPI1_N] = {
&rPMGR_SPI1_N_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 10},
{PMGR_CLK_PLL0, 20},
}
},
[PMGR_CLK_NCO_REF0] = {
&rPMGR_NCO_REF0_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 2},
{PMGR_CLK_PLL0, 4},
}
},
[PMGR_CLK_NCO_REF1] = {
&rPMGR_NCO_REF1_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 2},
{PMGR_CLK_PLL0, 4},
}
},
[PMGR_CLK_NCO_ALG0] = {
&rPMGR_NCO_ALG0_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 4},
{PMGR_CLK_PLL0, 10},
}
},
[PMGR_CLK_NCO_ALG1] = {
&rPMGR_NCO_ALG1_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_PLL0, 4},
{PMGR_CLK_PLL0, 10},
}
},
[PMGR_CLK_MCA0_M] = {
&rPMGR_MCA0_M_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_NOT_SUPPORTED, 1},
{PMGR_CLK_NOT_SUPPORTED, 1},
{PMGR_CLK_OSC, 2},
{PMGR_CLK_OSC, 4},
}
},
[PMGR_CLK_MCA1_M] = {
&rPMGR_MCA1_M_CLK_CFG,
{
{PMGR_CLK_OSC, 1},
{PMGR_CLK_S0, 1},
{PMGR_CLK_S1, 1},
{PMGR_CLK_NOT_SUPPORTED, 1},
{PMGR_CLK_NOT_SUPPORTED, 1},
{PMGR_CLK_OSC, 2},
{PMGR_CLK_OSC, 4},
}
},
[PMGR_CLK_S0] = {
&rPMGR_S0_CLK_CFG,
{
{PMGR_CLK_PLL0, 1},
}
},
[PMGR_CLK_S1] = {
&rPMGR_S1_CLK_CFG,
{
{PMGR_CLK_PLL0, 1}, }
},
};
/* ******************************************************************************** */
static void set_pll(uint32_t p, uint32_t m, uint32_t s);
static uint32_t get_pll_frequency();
static uint32_t get_spare_frequency(uint32_t cnt);
static void clocks_get_frequencies(void);
static void clocks_get_frequencies_range(uint32_t start_clk, uint32_t end_clk);
static void clocks_quiesce_internal(void);
static void apply_pmgr_tunables();
static void update_memory_clk_config(uint32_t performance_level);
// current clock frequencies
static uint32_t clks[PMGR_CLK_COUNT + 1];
/* ******************************************************************************** */
static void set_pll(uint32_t p, uint32_t m, uint32_t s)
{
rPMGR_PLL0_CTL = (PMGR_PLL_P(p) | PMGR_PLL_M(m) | PMGR_PLL_S(s) | PMGR_PLL_LOAD);
while ((rPMGR_PLL0_CTL & PMGR_PLL_PENDING) == 1);
rPMGR_PLL0_EXT_BYPASS_CFG &= ~PMGR_PLL_EXT_BYPASS;
while ((rPMGR_PLL0_EXT_BYPASS_CFG & PMGR_PLL_BYP_ENABLED) != 0);
rPMGR_PLL0_CTL |= PMGR_PLL_ENABLE;
while ((rPMGR_PLL0_CTL & PMGR_PLL_PENDING) == 1);
}
static uint32_t get_pll_frequency()
{
uint32_t pllctl, bypcfg;
uint64_t freq = 0;
pllctl = rPMGR_PLL0_CTL;
bypcfg = rPMGR_PLL0_EXT_BYPASS_CFG;
// If PLL is not enabled, check for External Bypass
if ((pllctl & PMGR_PLL_ENABLE) == 0) {
if ((bypcfg & PMGR_PLL_EXT_BYPASS) == 0)
return 0;
else
return OSC_FREQ;
}
freq = OSC_FREQ;
freq *= ((pllctl >> PMGR_PLL_M_SHIFT) & PMGR_PLL_M_MASK);
freq /= ((pllctl >> PMGR_PLL_P_SHIFT) & PMGR_PLL_P_MASK);
freq /= (1 + ((pllctl >> PMGR_PLL_S_SHIFT) & PMGR_PLL_S_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_frequency(uint32_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 = 0; // Source index is always 0 because only spare source is PLL0
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_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_GPIO || end_clk > PMGR_CLK_MCA1_M)
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;
}
}
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_MCU] = 10000000;
clks[PMGR_CLK_MCU_FIXED]= 10000000;
clks[PMGR_CLK_USB] = 12000000;
#elif CONFIG_SIM
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_frerquency() to establish the frequency (unconfigured PLLs will bypass OSC)
clks[PMGR_CLK_PLL0] = get_pll_frequency();
// Use get_spare_frequencies() 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_frequency(cnt);
}
clocks_get_frequencies_range(PMGR_CLK_GPIO, PMGR_CLK_MCA1_M);
#endif
}
int clocks_init(void)
{
#if (APPLICATION_IBOOT && (PRODUCT_IBOOT || PRODUCT_IBEC || WITH_RECOVERY_MODE_IBSS))
clocks_get_frequencies();
#endif /* (APPLICATION_IBOOT && (PRODUCT_IBOOT || PRODUCT_IBEC || WITH_RECOVERY_MODE_IBSS)) */
return 0;
}
static bool clk_mipi_req_on = false;
static bool clk_disp0_req_on = false;
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
if (reg > PMGR_LAST_PS)
return;
// Workaround for <rdar://problem/16051906> / <rdar://problem/15883349>
// Keep track of the pending requests for CLK_DISP0 and CLK_MIPI_DSI
switch(device)
{
case CLK_DISP0:
clk_disp0_req_on = enable;
break;
case CLK_MIPI_DSI:
clk_mipi_req_on = enable;
break;
}
if (!enable && ((device == CLK_DISP0) || (device == CLK_MIPI_DSI)))
{
// Workaround for <rdar://problem/16051906>
// if requesting DISP0 or MIPI off, make sure both are requested off before effecting the change.
if (clk_disp0_req_on || clk_mipi_req_on)
return;
// Otherwise clock_gate the other one before clock gating this one.
int device2 = (device == CLK_DISP0) ? CLK_MIPI_DSI : CLK_DISP0;
volatile uint32_t *reg2 = (volatile uint32_t *)((uint64_t *)PMGR_FIRST_PS + device2);
*reg2 &= ~PMGR_PS_RUN_MAX;
while ((*reg2 & PMGR_PS_MANUAL_PS_MASK) != ((*reg2 >> PMGR_PS_ACTUAL_PS_SHIFT) & PMGR_PS_ACTUAL_PS_MASK));
}
// Set the PS field to the requested level
if (enable)
*reg |= PMGR_PS_RUN_MAX;
else
*reg &= ~PMGR_PS_RUN_MAX; // i.e. set PMGR_PS_POWER_OFF
// Wait for the MANUAL_PS and ACTUAL_PS fields to be equal
while ((*reg & PMGR_PS_MANUAL_PS_MASK) != ((*reg >> PMGR_PS_ACTUAL_PS_SHIFT) & PMGR_PS_ACTUAL_PS_MASK));
// Workaround for <rdar://problem/15883349>
// If device == MIPI_DSI | DISP, turn the enable the other one as well
if (enable && ((device == CLK_DISP0) || (device == CLK_MIPI_DSI)))
{
int device2 = (device == CLK_DISP0) ? CLK_MIPI_DSI : CLK_DISP0;
volatile uint32_t *reg2 = (volatile uint32_t *)((uint64_t *)PMGR_FIRST_PS + device2);
*reg2 |= PMGR_PS_RUN_MAX;
while ((*reg2 & PMGR_PS_MANUAL_PS_MASK) != ((*reg2 >> PMGR_PS_ACTUAL_PS_SHIFT) & PMGR_PS_ACTUAL_PS_MASK));
}
}
static void restore_clock_config_reset_state()
{
volatile uint32_t *clkcfgs = PMGR_FIRST_CLK_CFG;
uint32_t reg;
// 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_POWER_CONFIG = 0;
// 5. Write reset value for all mux clock configs (excluding spares, mcu, mcu_fixed)
reg = PMGR_CLK_NUM(GPIO);
while (reg <= PMGR_CLK_NUM(MCA1_M)) {
if ((reg == PMGR_CLK_NUM(MCU)) || (reg == PMGR_CLK_NUM(MCU_FIXED))) goto skip;
clkcfgs[reg] = 0x80100000;
while (clkcfgs[reg] & PMGR_CLK_CFG_PENDING);
skip:
reg++;
}
// 6. Write to MCU and MCU_FIXED to allow memory to run at 24MHz
update_memory_clk_config(kPerformanceMemoryLow);
// 7. Write PLL0_EXT_BYPASS_CFG.EXT_BYPASS to 1
rPMGR_PLL0_EXT_BYPASS_CFG |= PMGR_PLL_EXT_BYPASS;
while ((rPMGR_PLL0_EXT_BYPASS_CFG & PMGR_PLL_BYP_ENABLED) == 0);
// 8. Write reset value to PLL0_CTL
rPMGR_PLL0_CTL = 0x00001010;
// 10. Write reset value spares
reg = PMGR_SPARE_CLK_NUM(S0);
while (reg <= PMGR_SPARE_CLK_NUM(S1)) {
clkcfgs[reg] = 0x80000001;
while (clkcfgs[reg] & PMGR_CLK_CFG_PENDING);
reg++;
}
}
static void clocks_quiesce_internal(void)
{
//clock_gate(PMGR_CPU, 1);
clock_gate(CLK_AIC, 1);
clock_gate(CLK_LIO, 1);
clock_gate(CLK_GPIO, 1);
clock_gate(CLK_MCU, 1);
clock_gate(CLK_AMP, 1);
clock_gate(CLK_PIOSYS, 1);
clock_gate(CLK_SPU, 1);
clock_gate(CLK_SPU_SGPIO, 1);
clock_gate(CLK_AUE, 1);
clock_gate(CLK_USB_M7, 1);
clock_gate(CLK_AES0, 1);
clock_gate(CLK_DOCKFIFO, 1);
clock_gate(CLK_ANS, 1);
// DEBUG_PS lives in SPU, enable it as well
rSPU_PMGR_DEBUG_PS |= PMGR_PS_RUN_MAX;
while ((rSPU_PMGR_DEBUG_PS & PMGR_PS_MANUAL_PS_MASK) != ((rSPU_PMGR_DEBUG_PS >> PMGR_PS_ACTUAL_PS_SHIFT) & PMGR_PS_ACTUAL_PS_MASK));
clock_gate(CLK_SPU_AKF, 0);
// <rdar://problem/17191216> SPU_SMB_PS needs to be clocked on in iBoot and/or not touched by iOS sleep/wake
// SPU_*_PS should be on by default, without them in EDT the OS should then leave them alone.
clock_gate(CLK_SPU_UART0, 1);
clock_gate(CLK_SPU_UART1, 1);
clock_gate(CLK_SPU_SMB, 1);
clock_gate(CLK_DISP0, 0);
clock_gate(CLK_MIPI_DSI, 0);
clock_gate(CLK_MSR, 0);
clock_gate(CLK_GFX, 0);
clock_gate(CLK_SDIO, 0);
clock_gate(CLK_MCA0, 0);
clock_gate(CLK_MCA1, 0);
clock_gate(CLK_SPI0, 0);
clock_gate(CLK_SPI1, 0);
clock_gate(CLK_DMATX, 0);
clock_gate(CLK_DMARX, 0);
clock_gate(CLK_UART0, 0);
clock_gate(CLK_UART1, 0);
clock_gate(CLK_UART2, 0);
clock_gate(CLK_UART3, 0);
clock_gate(CLK_UART4, 0);
clock_gate(CLK_I2C0, 0);
clock_gate(CLK_I2C1, 0);
clock_gate(CLK_PWM0, 0);
clock_gate(CLK_ETH, 0);
clock_gate(CLK_VDEC, 0);
restore_clock_config_reset_state();
}
static void apply_pmgr_tunables()
{
#define CLAMP_TIME_MASK (((1 << 8) - 1) << 8)
#define CLK_EN_TIME_MASK (((1 << 8) - 1) << 16)
#define SRAM_PG_EN_TIME_MASK (((1 << 3) - 1) << 24)
#define SRAM_PG_DIS_TIME_MASK (((1 << 3) - 1) << 27)
#define _PWRGATE_CFG0(rCFG0, CLAMP_TIME, CLK_EN_TIME, SRAM_PG_EN_TIME, SRAM_PG_DIS_TIME) \
regTemp = rCFG0; \
regTemp &= ~CLAMP_TIME_MASK; \
regTemp |= (CLAMP_TIME << 8); \
regTemp &= ~CLK_EN_TIME_MASK; \
regTemp |= (CLK_EN_TIME << 16); \
regTemp &= ~SRAM_PG_EN_TIME_MASK; \
regTemp |= (SRAM_PG_EN_TIME << 24); \
regTemp &= ~SRAM_PG_DIS_TIME_MASK; \
regTemp |= (SRAM_PG_DIS_TIME << 27); \
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_PRE_TIME, RAMP_ALL_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;
#define RESET_DOWN_TIME_MASK (0xFF << 0)
#define RESET_UP_TIME_MASK (0xFF << 8)
#define RESET_OFF_TIME_MASK (0xFF << 16)
#define _PWRGATE_CFG2(rCFG2, RESET_DOWN_TIME, RESET_UP_TIME, RESET_OFF_TIME) \
regTemp = rCFG2; \
regTemp &= ~RESET_DOWN_TIME_MASK; \
regTemp |= (RESET_DOWN_TIME << 0); \
regTemp &= ~RESET_UP_TIME_MASK; \
regTemp |= (RESET_UP_TIME << 8); \
regTemp &= ~RESET_OFF_TIME_MASK; \
regTemp |= (RESET_OFF_TIME << 16); \
rCFG2 = regTemp;
uint32_t regTemp;
_PWRGATE_CFG0(rPMGR_PWR_CPU_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_CPU_CFG1, 0x4, 0x6b);
_PWRGATE_CFG2(rPMGR_PWR_CPU_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_AMCPIO_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_AMCPIO_CFG1, 0x0, 0x1);
_PWRGATE_CFG2(rPMGR_PWR_AMCPIO_CFG2, 0x28, 0x28, 0x28);
_PWRGATE_CFG0(rPMGR_PWR_DISP_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_DISP_CFG1, 0x5, 0x66);
_PWRGATE_CFG2(rPMGR_PWR_DISP_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_DISP_BE_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_DISP_BE_CFG1, 0x1, 0x1b);
_PWRGATE_CFG2(rPMGR_PWR_DISP_BE_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_MSR_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_MSR_CFG1, 0x3, 0x3a);
_PWRGATE_CFG2(rPMGR_PWR_MSR_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_ANS_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_ANS_CFG1, 0x2, 0x78);
_PWRGATE_CFG2(rPMGR_PWR_ANS_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_GFX_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_GFX_CFG1, 0x3, 0x49);
_PWRGATE_CFG2(rPMGR_PWR_GFX_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_SDIO_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_SDIO_CFG1, 0x2, 0x3a);
_PWRGATE_CFG2(rPMGR_PWR_SDIO_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_LIO_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_LIO_CFG1, 0x1, 0x31);
_PWRGATE_CFG2(rPMGR_PWR_LIO_CFG2, 0x8, 0xc, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_AUE_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_AUE_CFG1, 0x1, 0x1f);
_PWRGATE_CFG2(rPMGR_PWR_AUE_CFG2, 0x8, 0x8, 0x8);
_PWRGATE_CFG0(rPMGR_PWR_VDEC_CFG0, 0x3, 0x4, 0x4, 0x4);
_PWRGATE_CFG1(rPMGR_PWR_VDEC_CFG1, 0x3, 0x30);
_PWRGATE_CFG2(rPMGR_PWR_VDEC_CFG2, 0x8, 0x8, 0x8);
rPMGR_MCU_ASYNC_RESET = (0x1 << 28) | (0x1 << 24) | (0x1 << 20) | (0x1 << 16) | (0x1 << 8) | (0x1 << 4) | (0x1 << 0);
rPMGR_MISC_GFX_CTL = (0x1 << 0);
}
static void update_memory_clk_config(uint32_t performance_level)
{
int32_t cfg_sel = -1;
uint32_t src_index;
volatile uint32_t *clkcfgs = PMGR_FIRST_CLK_CFG;
uint32_t mcu_clk_cfg_reg, mcu_fixed_clk_cfg_reg;
uint8_t current_mcu_fixed_clk;
switch(performance_level) {
case kPerformanceMemoryLow:
cfg_sel = 3;
break;
case kPerformanceMemoryFull:
cfg_sel = 0;
break;
}
if (cfg_sel == -1)
panic("pmgr:cfg_sel not set correctly for configuring amc clock");
src_index = (performance_level == kPerformanceMemoryLow) ? 0 : 3;
mcu_clk_cfg_reg = clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)];
mcu_fixed_clk_cfg_reg = clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU_FIXED)];
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);
current_mcu_fixed_clk = (mcu_fixed_clk_cfg_reg >> PMGR_CLK_CFG_SRC_SEL_SHIFT) & 0x1;
mcu_fixed_clk_cfg_reg &= ~(1 << PMGR_CLK_CFG_SRC_SEL_SHIFT);
mcu_fixed_clk_cfg_reg |= ((src_index & 0x1) << PMGR_CLK_CFG_SRC_SEL_SHIFT);
if (current_mcu_fixed_clk == 0) {
clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU_FIXED)] = mcu_fixed_clk_cfg_reg;
while ((clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU_FIXED)] >> PMGR_CLK_CFG_PENDING_SHIFT) & 0x1);
clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)] = mcu_clk_cfg_reg;
while ((clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)] >> PMGR_CLK_CFG_PENDING_SHIFT) & 0x1);
}
else {
clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)] = mcu_clk_cfg_reg;
while ((clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU)] >> PMGR_CLK_CFG_PENDING_SHIFT) & 0x1);
clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU_FIXED)] = mcu_fixed_clk_cfg_reg;
while ((clkcfgs[PMGR_CLK_CFG_INDEX(PMGR_CLK_MCU_FIXED)] >> PMGR_CLK_CFG_PENDING_SHIFT) & 0x1);
}
}
static void set_nco_clocks(void)
{
// Enable this NCO with alg_ref0_clk and nco_ref0_clk.
rPMGR_NCO_CLK_CFG(0) |= (1 << 31);
rPMGR_NCO_CLK_CFG(1) |= (1 << 31);
}
/*
* 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;
volatile uint32_t *spare_clkcfgs = PMGR_FIRST_SPARE_CLK_CFG;
volatile uint32_t *clkcfgs = PMGR_FIRST_CLK_CFG;
clks[PMGR_CLK_OSC] = OSC_FREQ;
// Change all clocks to something safe
clocks_quiesce_internal();
#ifdef PLL0_T
set_pll(PLL0_P, PLL0_M, PLL0_S);
#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];
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];
while ((clkcfgs[cnt] & PMGR_CLK_CFG_PENDING) != 0);
}
clocks_get_frequencies();
#if (APPLICATION_IBOOT && !PRODUCT_IBOOT && !PRODUCT_IBEC)
apply_pmgr_tunables();
#endif
return 0;
}
void clocks_quiesce(void)
{
clocks_quiesce_internal();
}
uint32_t clocks_set_performance(uint32_t performance_level)
{
#if APPLICATION_IBOOT
if (performance_level == kPerformanceMemoryLow || performance_level == kPerformanceMemoryFull) {
update_memory_clk_config(performance_level);
clocks_get_frequencies_range(PMGR_CLK_MCU_FIXED, PMGR_CLK_MCU);
}
#endif
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 = OSC_FREQ;
switch (clock) {
case CLK_NCLK:
case CLK_FIXED:
case CLK_TIMEBASE:
freq = clks[PMGR_CLK_OSC];
break;
case CLK_ANS_LINK:
freq = clks[PMGR_CLK_ANS];
break;
case CLK_BUS:
case CLK_PCLK:
case CLK_PERIPH:
freq = clks[PMGR_CLK_LIO];
break;
case CLK_MEM:
freq = clks[PMGR_CLK_MCU];
break;
case CLK_CPU:
freq = clks[PMGR_CLK_MCU];
break;
case CLK_MIPI:
freq = clks[PMGR_CLK_MIPI_DSI];
break;
default:
break;
}
return freq;
}
static void clock_update_frequency(uint32_t clk, uint32_t freq)
{
uint32_t src_idx, src_clk, src_factor, reg;
bool freq_supported = false;
volatile uint32_t *clkcfg = clk_configs[clk].clock_reg;
if (freq == 0)
{
return;
}
for (src_idx = 0; src_idx < CLOCK_SOURCES_MAX && clk_configs[clk].sources[src_idx].factor != 0; src_idx++)
{
src_clk = clk_configs[clk].sources[src_idx].src_clk;
src_factor = clk_configs[clk].sources[src_idx].factor;
// Round the requested frequency to closest MHz value and check if we have a match
if ((freq / 1000000) == ((clks[src_clk] / src_factor) / 1000000))
{
freq_supported = true;
break;
}
}
if (freq_supported)
{
// Configure clock
reg = *clkcfg;
reg &= ~(PMGR_CLK_CFG_SRC_SEL_MASK << PMGR_CLK_CFG_SRC_SEL_SHIFT);
reg |= (src_idx & PMGR_CLK_CFG_SRC_SEL_MASK) << PMGR_CLK_CFG_SRC_SEL_SHIFT;
*clkcfg = reg;
while (*clkcfg & PMGR_CLK_CFG_PENDING);
}
}
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)
{
uint32_t clk = PMGR_CLK_OSC;
switch (clock) {
case CLK_VCLK0:
clk = PMGR_CLK_VID;
break;
default:
break;
}
if (clk >= PMGR_CLK_SOURCE_FIRST && clk <= PMGR_CLK_SOURCE_LAST) {
clock_update_frequency(clk, pll_t);
clocks_get_frequencies_range(clk, clk);
}
}
void clock_reset_device(int device)
{
volatile uint32_t *reg = (volatile uint32_t *)((uint64_t *)PMGR_FIRST_PS + device);
switch (device) {
case CLK_MCU:
default:
*reg |= PMGR_PS_RESET;
spin(1);
*reg &= ~PMGR_PS_RESET;
break;
}
}
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)
{
}
void platform_power_spin(uint32_t usecs)
{
extern void aic_spin(uint32_t usecs);
aic_spin(usecs);
}
#if WITH_DEVICETREE
void pmgr_update_device_tree(DTNode *pmgr_node)
{
}
#endif
/*
* Timer support
*/
#define DECR_MAX_COUNT (INT32_MAX)
static void timer_deadline(void *arg);
static void (* timer_deadline_func)(void);
int timer_init(uint32_t timer)
{
/* Allow only banked access to CPU's own timer */
if (timer != 0) return -1;
rPMGR_INTERVAL_TMR_CTL = (1 << 0) | (1 << 8);
return 0;
}
void timer_stop_all(void)
{
rPMGR_INTERVAL_TMR_CTL &= ~(1 << 0);
rPMGR_INTERVAL_TMR_CTL = (1 << 8);
}
void timer_deadline_enter(uint64_t deadline, void (* func)(void))
{
uint64_t ticks;
uint32_t decr;
timer_deadline_func = func;
if (func) {
// printf("installing deadline %p\n", func);
/* convert absolute deadline to relative time */
ticks = timer_get_ticks();
if (deadline <= ticks) {
// If the desired deadline would be 0 or negative, we ensure it is at least 1 tick
// in order to fire the timer immediately and not wrap around.
deadline = 1;
} else {
deadline -= ticks;
}
/* clamp the deadline to our maximum, which is about 178 seconds */
decr = (deadline > DECR_MAX_COUNT) ? DECR_MAX_COUNT : deadline;
// printf(" using decrementer count %u\n", decr);
/* Reprogramme the desired count */
rPMGR_INTERVAL_TMR = DECR_MAX_COUNT;
rPMGR_INTERVAL_TMR_CTL = (1 << 0) | (1 << 8);
rPMGR_INTERVAL_TMR = decr;
} else {
rPMGR_INTERVAL_TMR_CTL &= ~(1 << 0);
rPMGR_INTERVAL_TMR_CTL = (1 << 8);
}
}
void platform_fiq()
{
/* clear FIQ and disable decrementer */
rPMGR_INTERVAL_TMR_CTL &= ~(1 << 0);
rPMGR_INTERVAL_TMR_CTL = (1 << 8);
/* if we have a callback, invoke it now */
if (timer_deadline_func) {
timer_deadline_func();
}
}
utime_t timer_ticks_to_usecs(uint64_t ticks)
{
#if (OSC_FREQ % 1000000) == 0
return ticks / (OSC_FREQ / 1000000);
#else
#error "The code below has overflow issues. Make sure you really want to use it"
return (ticks * 1000 * 1000) / OSC_FREQ;
#endif
}
uint64_t timer_usecs_to_ticks(utime_t usecs)
{
#if (OSC_FREQ % 1000000) == 0
return usecs * (OSC_FREQ / 1000000);
#else
#error "The code below has overflow issues. Make sure you really want to use it"
uint64_t timer_scale = ((uint64_t)(OSC_FREQ) << 20) / (1000 * 1000);
return (ticks * timer_scale) >> 20;
#endif
}
uint32_t timer_get_tick_rate(void)
{
return OSC_FREQ;
}
uint64_t timer_get_ticks(void)
{
extern uint64_t aic_get_ticks(void);
return (aic_get_ticks());
}