blob: a7559e9e3793f445c549484a6447c362f4dc4e87 [file] [log] [blame]
/*
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
* Copyright 2020, HENSOLDT Cyber GmbH
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <platsupport/i2c.h>
#include "../../arch/arm/clock.h"
#include "../../services.h"
#include <assert.h>
#include <string.h>
#include <utils/util.h>
#define CCM_PADDR 0x020C4000
#define CCM_SIZE 0x1000
#define CCM_ANALOG_PADDR 0x020C8000
#define CCM_ANALOG_SIZE 0x1000
/* common flags for all PLLs */
#define PLL_LOCK BIT(31)
#define PLL_BYPASS BIT(16)
#define PLL_GET_BYPASS_SRC(x) (((x) >> 14) & 0x3)
#define PLL_ENABLE BIT(13)
#define PLL_PWR_DOWN BIT(12)
/* PLL_ARM (PLL1), up to 1.3 GHz */
#define PLL_ARM_DIV_MASK 0x7F
#define PLL_ARM_GET_DIV(x) ((x) & PLL_ARM_DIV_MASK)
/* PLL_SYS (also called PLL2 or PLL_528) runs at a fixed multiplier 22 based
* on the 24 MHz oscillator to create a 528 MHz a reference clock
* (24 MHz * 22 = 528 MHz). Other values than 22 are not supposed to be used.
*/
#define PLL_SYS_DIV_MASK BIT(0)
#define PLL_SYS_GET_DIV(x) ((x) & PLL_SYS_DIV_MASK)
/* PLL_SYS spread spectrum control (CCM_ANALOG_PLL_SYS_SS) */
#define PLL_SYS_SS_STOP(x) (((x) & 0xffff) << 16)
#define PLL_SYS_SS_EN BIT(15)
#define PLL_SYS_SS_STEP(x) ((x) & 0x7fff)
/* PLL_ENET (PLL6), disabled on reset, runs at a fixed multiplier 20+(5/6)
* which based on the 24 MHz oscillator as reference clock gives a 500 MHz clock
* (24 MHz * (20+(5/6)) = 500 MHz). This use used to generate the frequencies
* 125 MHz (for PCIe and gigabit ethernet), 100 MHz (for SATA) and 50 or 25 MHz
* for the external ethernet interface.
*/
#define PLL_ENET_ENABLE_100M BIT(20)
#define PLL_ENET_ENABLE_125M BIT(19)
#define PLL_ENET_DIV_MASK 0x3
#define PLL_ENET_GET_DIV(x) ((x) & PLL_ENET_DIV_MASK)
/* PLL_USB. There is PLL3 (USB1_PLL or 480 PLL) that runs at a fixed multiplier
* 20 and based on the 24 MHz oscillator creates a 480 MHz reference clock
* (24 MHz * 20 = 480 MHz). It is used for USB0 PHY (OTG PHY) and also to create
* clocks for UART, CAN, other serial interfaces and audio interfaces.
* There is also 480_PLL2 (USB2_PLL), which provides clock exclusively to
* USB2 PHY (also known as HOST PHY) that also runs at a fixed multiplier of 20
* to generate a 480 MHz clock.
*/
#define PLL_USB_ENABLE_CLKS BIT(6)
#define PLL_USB_DIV_MASK 0x3
#define PLL_USB_GET_DIV(x) ((x) & PLL_USB_DIV_MASK)
/* clock gating in CCM_CCGRn */
#define CLKGATE_MODE_OFF 0x0
#define CLKGATE_MODE_ON_RUN 0x1
#define CLKGATE_MODE_RESERVED 0x2
#define CLKGATE_MODE_ON_ALL 0x3
#define CLKGATE_MODE_MASK 0x3
/* CCM Clock Output Source (CCM_CCOSR) */
#define CLKO1_SRC_AHB 0xBU
#define CLKO1_SRC_IPG 0xCU
#define CLKO1_SRC_MASK 0xFU /* bit 0-3 */
#define CLKO1_ENABLE BIT(7)
#define CLKO_SEL BIT(8) /* select CCM_CLKO1 or CCM_CLKO2 */
#define CLKO2_SRC_MASK (0x1FU << 16) /* bit 16-20 */
#define CLKO2_SRC_MMDC_CH0 (0U << 16) /* b00000 for mmdc_ch0_clk_root */
#define CLKO2_ENABLE BIT(24)
typedef volatile struct {
uint32_t ccr; /* 0x000 */
uint32_t ccdr; /* 0x004 */
uint32_t csr; /* 0x008 */
uint32_t ccsr; /* 0x00C */
uint32_t cacrr; /* 0x010 */
uint32_t cbcdr; /* 0x014 */
uint32_t cbcmr; /* 0x018 */
uint32_t cscmr1; /* 0x01C */
uint32_t cscmr2; /* 0x020 */
uint32_t cscdr1; /* 0x024 */
uint32_t cs1cdr; /* 0x028 */
uint32_t cs2cdr; /* 0x02C */
uint32_t cdcdr; /* 0x030 */
uint32_t chsccdr; /* 0x034 */
uint32_t cscdr2; /* 0x038 */
uint32_t cscdr3; /* 0x03C */
uint32_t res0[2];
uint32_t cdhipr; /* 0x048 */
uint32_t res1[1];
uint32_t ctor; /* 0x050 */
uint32_t clpcr; /* 0x054 */
uint32_t cisr; /* 0x058 */
uint32_t cimr; /* 0x05C */
uint32_t ccosr; /* 0x060 */
uint32_t cgpr; /* 0x064 */
uint32_t ccgr[7]; /* 0x068 */
uint32_t res2[1];
uint32_t cmeor; /* 0x088 */
} ccm_regs_t;
typedef volatile struct {
uint32_t val; /* 0x00 */
uint32_t set; /* 0x04 */
uint32_t clr; /* 0x08 */
uint32_t tog; /* 0x0C */
} alg_sct_t;
typedef volatile struct {
alg_sct_t vbus_detect; /* 0x00 */
alg_sct_t chrg_detect; /* 0x10 */
uint32_t vbus_detect_stat; /* 0x20 */
uint32_t res0[3];
uint32_t chrg_detect_stat; /* 0x30 */
uint32_t res1[3];
uint32_t res2[4];
alg_sct_t misc; /* +0x50 */
} ccm_alg_usbphy_regs_t;
typedef volatile struct {
/* PLL_ARM */
alg_sct_t pll_arm; /* 0x000 */
/* PLL_USB * 2 */
alg_sct_t pll_usb[2]; /* 0x010 */
/* PLL_SYS */
alg_sct_t pll_sys; /* 0x030 */
uint32_t pll_sys_ss; /* 0x040 */
uint32_t res0[3];
uint32_t pll_sys_num; /* 0x050 */
uint32_t res1[3];
uint32_t pll_sys_denom; /* 0x060 */
uint32_t res2[3];
/* PLL_AUDIO */
alg_sct_t pll_audio; /* 0x070 */
uint32_t pll_audio_num; /* 0x080 */
uint32_t res3[3];
uint32_t pll_audio_denom; /* 0x090 */
uint32_t res4[3];
/* PLL_VIDIO */
alg_sct_t pll_video; /* 0x0A0 */
uint32_t pll_video_num; /* 0x0B0 */
uint32_t res5[3];
uint32_t pll_video_denom; /* 0x0C0 */
uint32_t res6[3];
/* PLL_MLB */
alg_sct_t pll_mlb; /* 0x0D0 */
/* PLL_ENET */
alg_sct_t pll_enet; /* 0x0E0 */
/* PDF_480 */
alg_sct_t pfd_480; /* 0x0F0 */
/* PDF_528 */
alg_sct_t pfd_528; /* 0x100 */
uint32_t res7[16];
/* MISC0 */
alg_sct_t misc0; /* 0x150 */
uint32_t res8[4];
/* MISC2 */
alg_sct_t misc2; /* 0x170 */
uint32_t res9[8];
/* USB phy control is implemented here for the sake of componentisation
* since it shares the same register space.
*/
ccm_alg_usbphy_regs_t phy[2]; /* 0x1a0, 0x200 */
uint32_t digprog; /* 0x260 */
} ccm_alg_regs_t;
static struct {
ccm_regs_t *ccm;
ccm_alg_regs_t *alg;
} clk_regs = { .ccm = NULL, .alg = NULL};
static struct clock master_clk = { CLK_OPS_DEFAULT(MASTER) };
static int change_pll(alg_sct_t *pll, uint32_t div_mask, uint32_t div)
{
/* div must be within the mask */
if (0 != (div & (~div_mask))) {
ZF_LOGE("invaid PLL setting, mask=0x%x, div=0x%x", div_mask, div);
assert(0);
return -1;
}
/* bypass on during clock manipulation */
pll->set = PLL_BYPASS;
/* power down the PLL */
pll->set = PLL_PWR_DOWN;
/* set the divisor */
pll->clr = div_mask;
pll->set = div;
/* power up the PLL */
pll->clr = PLL_PWR_DOWN;
/* Wait for the PLL to stabilize. */
for (unsigned int loop_cnt = 0; /* nothing */ ; loop_cnt++) {
if (pll->val & PLL_LOCK) {
ZF_LOGD("got PLL_LOCK after %u loops", loop_cnt);
break;
}
/* The PLL usually stabilizes after a few thousand cycles. On the i.MX6
* Saber Light board it takes a bit over 1500 loop iterations. Abort
* waiting if the PLL does not stabilize. Leave the bypass as source,
* disable the PLL and also disable the output.
*/
if (loop_cnt > 50000) {
pll->set = PLL_PWR_DOWN;
pll->clr = PLL_ENABLE;
ZF_LOGE("waiting for PLL_LOCK aborted");
return -1;
}
}
/* bypass off */
pll-> clr = PLL_BYPASS;
/* ensure PLL output is enabled */
pll->set = PLL_ENABLE;
return 0;
}
/*
*------------------------------------------------------------------------------
* ARM_CLK
*------------------------------------------------------------------------------
*/
static freq_t _arm_get_freq(clk_t *clk)
{
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
uint32_t v = clk_regs.alg->pll_arm.val;
/* clock output enabled? */
if (!(v & PLL_ENABLE)) {
/* we should never be here, because we are running on the ARM core */
return 0;
}
if (v & PLL_BYPASS) {
/* bypass on
* 0x0 source is 24MHz oscillator
* 0x1 source is CLK1_N/CLK1_P
* 0x2 source is CLK2_N/CLK2_P
* 0x3 source is CLK1_N/CLK1_P XOR CLK2_N/CLK2_P
*/
unsigned int src = PLL_GET_BYPASS_SRC(v);
switch (src) {
case 0:
return 24 * MHZ;
default:
break;
}
ZF_LOGE("can't determine frequency for PLL_ARM bypass sources %u", src);
return 0;
}
/* PLL enabled, but powered down? */
if (v & PLL_PWR_DOWN) {
/* we should never be here, because we are running on the ARM core */
return 0;
}
/* valid divider range 54 to 108, F_out = F_in * div_select / 2 */
unsigned int div = PLL_ARM_GET_DIV(v);
if ((div < 54) || (div > 108)) {
ZF_LOGE("PLL_ARM divider out of valid range 54-108: %u", div);
}
freq_t f_in = clk_get_freq(clk->parent);
return f_in * div / 2;
}
static freq_t _arm_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change PLL_ARM ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
alg_sct_t *pll = &clk_regs.alg->pll_arm;
freq_t fin = clk_get_freq(clk->parent);
unsigned int div = 2 * hz / fin;
if ((div < 54) || (div > 108)) {
ZF_LOGE("PLL_ARM divider out of valid range 54-108: %u", div);
return 0;
}
int ret = change_pll(pll, PLL_ARM_DIV_MASK, div);
if (0 != ret) {
ZF_LOGE("PLL_ARM change failed, code %d", ret);
return 0;
}
freq_t f_new = clk_get_freq(clk);
ZF_LOGD("PLL_ARM now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
}
static void _arm_recal(clk_t *clk UNUSED)
{
ZF_LOGE("PLL_ARM recal is not supported");
assert(0);
}
static clk_t *_arm_init(clk_t *clk)
{
if (clk->priv == NULL) {
clk_t *parent;
parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MASTER);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
return clk;
}
static struct clock arm_clk = { CLK_OPS(ARM, arm, NULL) };
/*
*------------------------------------------------------------------------------
* ENET_CLK
*------------------------------------------------------------------------------
*/
static freq_t _enet_get_freq(clk_t *clk)
{
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
uint32_t v = clk_regs.alg->pll_enet.val;
/* clock output enabled? */
if (!(v & PLL_ENABLE)) {
return 0;
}
if (v & PLL_BYPASS) {
/* bypass on
* 0x0 source is 24MHz oscillator
* 0x1 source is CLK1_N/CLK1_P
* 0x2 source is CLK2_N/CLK2_P
* 0x3 source is CLK1_N/CLK1_P XOR CLK2_N/CLK2_P
*/
unsigned int src = PLL_GET_BYPASS_SRC(v);
switch (src) {
case 0:
return 24 * MHZ;
default:
break;
}
ZF_LOGE("can't determine frequency for PLL_ENET bypass sources %u", src);
return 0;
}
/* PLL enabled, but powered down? */
if (v & PLL_PWR_DOWN) {
return 0;
}
unsigned int div = PLL_ENET_GET_DIV(v);
switch (div) {
case 0:
return 25 * MHZ;
case 1:
return 50 * MHZ;
case 2:
return 100 * MHZ;
case 3:
return 125 * MHZ;
default:
break;
}
ZF_LOGE("unsupported PLL_ENET divider %u", v);
return 0;
}
static freq_t _enet_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change PLL_ENET ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
#if defined(CONFIG_PLAT_IMX6DQ)
alg_sct_t *pll = &clk_regs.alg->pll_enet;
uint32_t div;
switch (hz) {
case 125 * MHZ:
div = 3;
break;
case 100 * MHZ:
div = 2;
break;
case 50 * MHZ:
div = 1;
break;
case 25 * MHZ:
div = 0;
break;
default:
ZF_LOGE("unsupported PLL_ENET clock frequency %"PRIu64, (uint64_t)hz);
return 0;
}
// ENET requires ahb_clk_root to be at least 125 MHz. In the clock tree
// PLL_SYS feeds CLK_MMDC_CH0 which feeds CLK_AHB. For CLK_AHB the default
// divider is 4, set via CBCDR.AHB_PODF, so setting PLL_SYS to the standard
// 528 MHz will result in an AHB clock of 132 MHz.
clk_t *clk_ahb = clk_get_clock(clk->clk_sys, CLK_AHB);
freq_t f_ahb = clk_get_freq(clk_ahb);
if (f_ahb < (125 * MHZ)) {
ZF_LOGI("AHB clock is %u.%06u MHz (< 125 MHz), setting system PLL to 528 MHz",
(unsigned int)(f_ahb / MHZ),
(unsigned int)(f_ahb % MHZ));
clk_set_freq(clk_get_clock(clk->clk_sys, CLK_PLL2), 528 * MHZ);
f_ahb = clk_get_freq(clk_ahb);
if (f_ahb < (125 * MHZ)) {
ZF_LOGE("AHB clock is %u.%06u Hz, still < 125 MHz)",
(unsigned int)(f_ahb / MHZ),
(unsigned int)(f_ahb % MHZ));
return 0;
}
}
int ret = change_pll(pll, PLL_ENET_DIV_MASK, div);
if (0 != ret) {
ZF_LOGE("PLL_ENET change failed, code %d", ret);
return 0;
}
clk_gate_enable(clk_get_clock_sys(clk), enet_clock, CLKGATE_ON);
#elif defined(CONFIG_PLAT_IMX6SX)
/* ENET requires enet_clk_root to be at least 133 MHz. However, we don't do
* anything here and just trust u-boot to have set things up properly.
*
* clk_regs.ccm->chsccdr = 0x00021148
* 15:17 Selector for ENET root clock pre-multiplexer, derive clock from
* b000 PLL2
* b001 pll3_sw_clk
* b010 PLL5
* b011 PLL2 PFD0
* b100 PLL2 PFD2
* b101 PLL3 PFD2
* b110-b111 Reserved
* 14:12 Divider for ENET clock divider, should be updated when output
* clock is gated. Divider is 1 to 8 calculated as "bxxx +1"
* 9:11 ENET root clock multiplexer, derive clock from
* b000 divided pre-muxed ENET clock
* b001 ipp_di0_clk
* b010 ipp_di1_clk
* b011 ldb_di0_clk
* b100 ldb_di1_clk
* b101-b111 Reserved
*
* CCM_CCGR3 relevant for ENETs:
* 18 ENET2_TX_CLK_DIR = 0 (disable ENET2 TX_CLK output driver)
* 17 ENET1_TX_CLK_DIR = 1 (enable ENET2 TX_CLK output driver)
* 14 ENET2_CLK_SEL = 0 (ENET2 TX reference clock driven by ref_enetpll1)
* 13 ENET1_CLK_SEL = 0 (ENET1 TX reference clock driven by ref_enetpll0)
*
* CCM_ANALOG_PLL_ENETn = 0x8030200f, relevant bits for ENETs:
* 31 PLL LOCK = 1
* 21 ENET_25M_REF_EN = 1
* 20 ENET2_125M_EN = 1
* 13 ENET1_125M_EN = 1
* 4:5 ENET2_DIV_SELECT = b11 (125 MHZ)
* 0:1 ENET1_DIV_SELECT = b11 (125 MHZ)
*/
ZF_LOGW("changing PLL_ENET not is currently not implemented");
#else
#error "unknown i.MX6 SOC"
#endif
freq_t f_new = clk_get_freq(clk);
ZF_LOGD("PLL_ENET now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
}
static void _enet_recal(clk_t *clk UNUSED)
{
ZF_LOGE("PLL_ENET recal is not supported");
assert(0);
}
static clk_t *_enet_init(clk_t *clk)
{
if (clk->priv == NULL) {
clk_t *parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MASTER);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
return clk;
}
static struct clock enet_clk = { CLK_OPS(ENET, enet, NULL) };
/*
*------------------------------------------------------------------------------
* PLL_SYS
*------------------------------------------------------------------------------
*/
static freq_t _pll_sys_get_freq(clk_t *clk)
{
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
uint32_t v = clk_regs.alg->pll_sys.val;
/* clock output enabled? */
if (!(v & PLL_ENABLE)) {
return 0;
}
if (v & PLL_BYPASS) {
/* bypass on
* 0x0 source is 24MHz oscillator
* 0x1 source is CLK1_N/CLK1_P
* 0x2 source is CLK2_N/CLK2_P
* 0x3 source is CLK1_N/CLK1_P XOR CLK2_N/CLK2_P
*/
unsigned int src = PLL_GET_BYPASS_SRC(v);
switch (src) {
case 0:
return 24 * MHZ;
default:
break;
}
ZF_LOGE("can't determine frequency for PLL_SYS bypass sources %u", src);
return 0;
}
/* PLL enabled, but powered down? */
if (v & PLL_PWR_DOWN) {
return 0;
}
freq_t f_parent = clk_get_freq(clk->parent);
unsigned int div = PLL_SYS_GET_DIV(v);
switch (div) {
case 0:
return f_parent * 20;
case 1:
return f_parent * 22;
default:
break;
}
ZF_LOGE("unsupported PLL_SYS divisor %u", div);
return 0;
}
static freq_t _pll_sys_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change PLL_SYS ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
alg_sct_t *pll = &clk_regs.alg->pll_sys;
if (24 * MHZ == hz) {
/* bypass PLL and use 24 MHz oscillator */
pll->set = PLL_BYPASS;
pll->set = PLL_PWR_DOWN;
pll->clr = PLL_ENET_DIV_MASK;
pll->set = PLL_ENABLE;
} else {
uint32_t div;
switch (hz) {
case 480 * MHZ:
div = 0;
break; // 480 MHz = 20 * 24 MHz
case 528 * MHZ:
div = 1;
break; // 528 MHz = 22 * 24 MHz
default:
ZF_LOGE("unsupported PLL_SYS clock frequency %"PRIu64, (uint64_t)hz);
return 0;
}
int ret = change_pll(pll, PLL_SYS_DIV_MASK, div);
if (0 != ret) {
ZF_LOGE("PLL_SYS change failed, code %d", ret);
return 0;
}
}
freq_t f_new = clk_get_freq(clk);
ZF_LOGD("PLL_SYS now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
}
static void _pll_sys_recal(clk_t *clk UNUSED)
{
ZF_LOGE("PLL_SYS recal is not supported");
assert(0);
}
static clk_t *_pll_sys_init(clk_t *clk)
{
if (clk->parent == NULL) {
clk_t *parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MASTER);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
// After a reset, the system PLL is not active and the clock signal comes
// from the 24 MHz oscillator. We don't reconfigure anything here, enabling
// the 528 MHz PLL must be done manually somewhere. Note that if U-Boot is
// used, it may have done this already.
return clk;
}
static struct clock pll_sys_clk = { CLK_OPS(PLL2, pll_sys, NULL) };
/*
*------------------------------------------------------------------------------
* MMDC_CH0_CLK
*------------------------------------------------------------------------------
*/
static freq_t _mmdc_ch0_get_freq(clk_t *clk)
{
return clk_get_freq(clk->parent);
}
static freq_t _mmdc_ch0_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change MMDC_CH0_CLK ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
/* TODO: there are the two MUXers here:
* - CCM Bus Clock Multiplexer CBCMR.PRE_PERIPH_CLK_SEL
* - CCM Bus Clock Divider CBCDR.PERIPH_CLK_SEL
* by default they route the clock PLL_SYS (PLL2)
*/
assert(hz == 528 * MHZ);
freq_t f_new_parent = clk_set_freq(clk->parent, hz);
freq_t f_new = f_new_parent;
ZF_LOGD("MMDC_CH0_CLK now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
}
static void _mmdc_ch0_recal(clk_t *clk UNUSED)
{
ZF_LOGE("MMDC_CH0_CLK recal is not supported");
assert(0);
}
static clk_t *_mmdc_ch0_init(clk_t *clk)
{
if (clk->parent == NULL) {
clk_t *parent = clk_get_clock(clk_get_clock_sys(clk), CLK_PLL2);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
return clk;
}
static struct clock mmdc_ch0_clk = { CLK_OPS(MMDC_CH0, mmdc_ch0, NULL) };
/*
*------------------------------------------------------------------------------
* AHB_CLK_ROOT
*------------------------------------------------------------------------------
*/
static freq_t _ahb_get_freq(clk_t *clk)
{
/* ToDo: read divider from CBCDR.AHB_PODF */
return clk_get_freq(clk->parent) / 4;
}
static freq_t _ahb_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change AHB_CLK_ROOT ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
/* ToDo: we assume the default value CBCDR.AHB_PODF = b011 has not been
* changed and thus the divider is 4 (132 MHZ * 4 = 528 MHz).
*/
assert(hz == 132 * MHZ);
freq_t f_new_parent = clk_set_freq(clk->parent, hz * 4);
freq_t f_new = f_new_parent / 4;
ZF_LOGD("AHB_CLK_ROOT now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
}
static void _ahb_recal(clk_t *clk UNUSED)
{
ZF_LOGE("AHB_CLK_ROOT recal is not supported");
assert(0);
}
static clk_t *_ahb_init(clk_t *clk)
{
if (clk->parent == NULL) {
clk_t *parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MMDC_CH0);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
return clk;
}
static struct clock ahb_clk = { CLK_OPS(AHB, ahb, NULL) };
/*
*------------------------------------------------------------------------------
* IPG_CLK_ROOT
*------------------------------------------------------------------------------
*/
static freq_t _ipg_get_freq(clk_t *clk)
{
return clk_get_freq(clk->parent) / 2;
};
static freq_t _ipg_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change IPG_CLK_ROOT ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
freq_t f_new_parent = clk_set_freq(clk->parent, hz * 2);
freq_t f_new = f_new_parent / 2;
ZF_LOGD("IPG_CLK_ROOT now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
};
static void _ipg_recal(clk_t *clk UNUSED)
{
ZF_LOGE("IPG_CLK_ROOT recal is not supported");
assert(0);
}
static clk_t *_ipg_init(clk_t *clk)
{
if (clk->parent == NULL) {
clk_t *parent = clk_get_clock(clk_get_clock_sys(clk), CLK_AHB);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
return clk;
}
static struct clock ipg_clk = { CLK_OPS(IPG, ipg, NULL) };
/*
*------------------------------------------------------------------------------
* USB_CLK
*------------------------------------------------------------------------------
*/
static freq_t _usb_get_freq(clk_t *clk)
{
unsigned int idx;
switch (clk->id) {
case CLK_USB1:
idx = 0;
break;
case CLK_USB2:
idx = 1;
break;
default:
ZF_LOGE("invalid USB clock ID: %u", clk->id);
return 0;
}
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return 0;
}
uint32_t v = clk_regs.alg->pll_usb[idx].val;
/* clock output enabled? */
if (!(v & PLL_ENABLE)) {
return 0;
}
if (v & PLL_BYPASS) {
/* bypass on
* 0x0 source is 24MHz oscillator
* 0x1 source is CLK1_N/CLK1_P
* 0x2 source is CLK2_N/CLK2_P
* 0x3 source is CLK1_N/CLK1_P XOR CLK2_N/CLK2_P
*/
unsigned int src = PLL_GET_BYPASS_SRC(v);
switch (src) {
case 0:
return 24 * MHZ;
default:
break;
}
ZF_LOGE("can't determine frequency for PLL_USB bypass sources %u", src);
return 0;
}
/* PLL enabled, but powered down? */
if (v & PLL_PWR_DOWN) {
return 0;
}
freq_t f_parent = clk_get_freq(clk->parent);
unsigned int div = PLL_USB_GET_DIV(v);
switch (div) {
case 0:
return f_parent * 20;
case 1:
return f_parent * 22;
default:
break;
}
ZF_LOGE("unsupported PLL_USB divider %u", v);
return 0;
}
static freq_t _usb_set_freq(clk_t *clk, freq_t hz UNUSED)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change PLL_USB ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
ZF_LOGE("PLL_USB changing is not supported");
assert(0);
return f_pre;
}
static void _usb_recal(clk_t *clk UNUSED)
{
ZF_LOGE("PLL_USB recal is not supported");
assert(0);
}
static clk_t *_usb_init(clk_t *clk)
{
if (clk->parent == NULL) {
clk_t *parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MASTER);
clk_register_child(parent, clk);
clk->priv = (void *)&clk_regs;
}
unsigned int idx;
switch (clk->id) {
case CLK_USB1:
idx = 0;
break;
case CLK_USB2:
idx = 1;
break;
default:
ZF_LOGE("invalid USB clock ID: %u", clk->id);
return NULL;
}
if (!clk_regs.alg) {
ZF_LOGE("clk_regs.alg is NULL, clocks not initialised properly");
return NULL;
}
alg_sct_t *pll = &clk_regs.alg->pll_usb[idx];
/* While we are here, gate the clocks */
pll->clr = PLL_BYPASS;
pll->set = PLL_ENABLE | PLL_PWR_DOWN | PLL_USB_ENABLE_CLKS;
clk_gate_enable(clk_get_clock_sys(clk), usboh3, CLKGATE_ON);
return clk;
}
static struct clock usb1_clk = { CLK_OPS(USB1, usb, NULL) };
static struct clock usb2_clk = { CLK_OPS(USB2, usb, NULL) };
/*
*------------------------------------------------------------------------------
* CLK_Ox
*------------------------------------------------------------------------------
*/
static int get_clko_bit_offset(unsigned int id)
{
switch (id) {
case CLK_CLKO1:
return 4;
case CLK_CLKO2:
return 21;
}
ZF_LOGE("Invalid CLK_Ox id 0x%x", id);
return -1;
}
static freq_t _clko_get_freq(clk_t *clk)
{
int shift = get_clko_bit_offset(clk->id);
if (shift < 0) {
ZF_LOGE("could not get CLK_Ox 0x%x clock bit offset", clk->id);
return 0;
}
uint32_t div = (clk_regs.ccm->ccosr >> shift) & 0x7;
freq_t f_parent = clk_get_freq(clk->parent);
return f_parent / (div + 1);
}
static freq_t _clko_set_freq(clk_t *clk, freq_t hz)
{
freq_t f_pre = clk_get_freq(clk);
ZF_LOGD("change CLK_Ox ('%s') from %u.%06u MHz to %u.%06u MHz",
clk->name,
(unsigned int)(f_pre / MHZ),
(unsigned int)(f_pre % MHZ),
(unsigned int)(hz / MHZ),
(unsigned int)(hz % MHZ));
int shift = get_clko_bit_offset(clk->id);
if (shift < 0) {
ZF_LOGE("could not get CLK_Ox 0x%x clock bit offset", clk->id);
return 0;
}
freq_t f_parent = clk_get_freq(clk->parent);
uint32_t div = (f_parent / hz) + 1;
if (div > 0x7) {
ZF_LOGW("required CLK_Ox clock divisor %u not possible, using max possible value 7", div);
div = 0x7;
}
uint32_t v = clk_regs.ccm->ccosr;
v &= ~(0x7U << shift);
v |= div << shift;
clk_regs.ccm->ccosr = v;
freq_t f_new = clk_get_freq(clk);
ZF_LOGD("CLK_Ox now running at %u.%06u MHz",
(unsigned int)(f_new / MHZ),
(unsigned int)(f_new % MHZ));
return f_new;
}
static void _clko_recal(clk_t *clk UNUSED)
{
ZF_LOGE("CLK_Ox recal is not supported");
assert(0);
}
static clk_t *_clko_init(clk_t *clk)
{
assert(clk_get_clock_sys(clk));
if (clk->parent == NULL) {
/* We currently only support 1 src, but there are many to choose from */
clk_t *parent;
uint32_t v = clk_regs.ccm->ccosr;
switch (clk->id) {
case CLK_CLKO1:
parent = clk_get_clock(clk_get_clock_sys(clk), CLK_IPG);
/* set source */
v &= ~CLKO1_SRC_MASK;
v |= CLKO1_SRC_IPG;
/* Enable */
v |= CLKO1_ENABLE;
/* Output to CCM_CLKO1 output */
v &= ~CLKO_SEL;
break;
case CLK_CLKO2:
/* set source */
parent = clk_get_clock(clk_get_clock_sys(clk), CLK_MMDC_CH0);
v &= ~CLKO2_SRC_MASK;
v |= CLKO2_SRC_MMDC_CH0;
/* Enable */
v |= CLKO2_ENABLE;
break;
default:
ZF_LOGE("Invalid CLK_Ox clock id 0x%x", clk->id);
assert(0);
return NULL;
}
clk_regs.ccm->ccosr = v;
clk_register_child(parent, clk);
}
return clk;
}
static struct clock clko1_clk = { CLK_OPS(CLKO1, clko, NULL) };
static struct clock clko2_clk = { CLK_OPS(CLKO2, clko, NULL) };
static int imx6_gate_enable(
clock_sys_t *clock_sys,
enum clock_gate gate,
enum clock_gate_mode mode)
{
assert(clk_regs.ccm);
if (gate > 112) {
ZF_LOGE("invalid gate %d", gate);
return -1;
}
uint32_t m;
switch (mode) {
case CLKGATE_ON:
m = CLKGATE_MODE_ON_ALL;
break;
case CLKGATE_IDLE:
ZF_LOGE("CLKGATE_IDLE not supported for gate %d", gate);
return -1;
case CLKGATE_SLEEP:
m = CLKGATE_MODE_ON_RUN;
break;
case CLKGATE_OFF:
m = CLKGATE_MODE_OFF;
break;
default:
ZF_LOGE("Invalid gate mode 0x%x for gate %d", mode, gate);
assert(0);
return -1;
}
uint32_t volatile *reg = &clk_regs.ccm->ccgr[gate / 16];
uint32_t shift = (gate & 0xf) * 2;
/* clear mask and set net clock gating mode */
*reg = (*reg & ~(CLKGATE_MODE_MASK << shift)) | (m << shift);
return 0;
}
int clock_sys_init(ps_io_ops_t *o, clock_sys_t *clock_sys)
{
MAP_IF_NULL(o, CCM, clk_regs.ccm);
MAP_IF_NULL(o, CCM_ANALOG, clk_regs.alg);
clock_sys->priv = (void *)&clk_regs;
clock_sys->get_clock = &ps_get_clock;
clock_sys->gate_enable = &imx6_gate_enable;
return 0;
}
void clk_print_clock_tree(clock_sys_t *sys)
{
clk_t *clk = clk_get_clock(sys, CLK_MASTER);
clk_print_tree(clk, "");
}
clk_t *ps_clocks[] = {
[CLK_MASTER] = &master_clk,
[CLK_PLL2] = &pll_sys_clk,
[CLK_MMDC_CH0] = &mmdc_ch0_clk,
[CLK_AHB] = &ahb_clk,
[CLK_IPG] = &ipg_clk,
[CLK_ARM] = &arm_clk,
[CLK_ENET] = &enet_clk,
[CLK_USB1] = &usb1_clk,
[CLK_USB2] = &usb2_clk,
[CLK_CLKO1] = &clko1_clk,
[CLK_CLKO2] = &clko2_clk,
};
/* These frequencies are NOT the recommended frequencies. They are to be used
* when we need to make assumptions about what u-boot has left us with.
*/
freq_t ps_freq_default[] = {
[CLK_MASTER] = 24 * MHZ,
[CLK_PLL2] = 528 * MHZ, /* PLL_SYS */
[CLK_MMDC_CH0] = 528 * MHZ,
[CLK_AHB] = 132 * MHZ,
[CLK_IPG] = 66 * MHZ,
[CLK_ARM] = 792 * MHZ,
[CLK_ENET] = 48 * MHZ,
[CLK_USB1] = 480 * MHZ,
[CLK_USB2] = 480 * MHZ,
[CLK_CLKO1] = 66 * MHZ,
[CLK_CLKO2] = 528 * MHZ,
};