forked from Minki/linux
d9cc6f76b6
Add basic tegra30 clock framework. Not all clocks are supported yet. Mainly clocks requiring voltage scaling are not yet implemented in this version. Based on work by Alex Frid <afrid@nvidia.com> and Scott Williams <scwilliams@nvidia.com>. Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com> Tested-by: Stephen Warren <swarren@nvidia.com> Acked-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Olof Johansson <olof@lixom.net>
3100 lines
90 KiB
C
3100 lines
90 KiB
C
/*
|
|
* arch/arm/mach-tegra/tegra30_clocks.c
|
|
*
|
|
* Copyright (c) 2010-2011 NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/list.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/err.h>
|
|
#include <linux/io.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/cpufreq.h>
|
|
#include <linux/syscore_ops.h>
|
|
|
|
#include <asm/clkdev.h>
|
|
|
|
#include <mach/iomap.h>
|
|
|
|
#include "clock.h"
|
|
#include "fuse.h"
|
|
|
|
#define USE_PLL_LOCK_BITS 0
|
|
|
|
#define RST_DEVICES_L 0x004
|
|
#define RST_DEVICES_H 0x008
|
|
#define RST_DEVICES_U 0x00C
|
|
#define RST_DEVICES_V 0x358
|
|
#define RST_DEVICES_W 0x35C
|
|
#define RST_DEVICES_SET_L 0x300
|
|
#define RST_DEVICES_CLR_L 0x304
|
|
#define RST_DEVICES_SET_V 0x430
|
|
#define RST_DEVICES_CLR_V 0x434
|
|
#define RST_DEVICES_NUM 5
|
|
|
|
#define CLK_OUT_ENB_L 0x010
|
|
#define CLK_OUT_ENB_H 0x014
|
|
#define CLK_OUT_ENB_U 0x018
|
|
#define CLK_OUT_ENB_V 0x360
|
|
#define CLK_OUT_ENB_W 0x364
|
|
#define CLK_OUT_ENB_SET_L 0x320
|
|
#define CLK_OUT_ENB_CLR_L 0x324
|
|
#define CLK_OUT_ENB_SET_V 0x440
|
|
#define CLK_OUT_ENB_CLR_V 0x444
|
|
#define CLK_OUT_ENB_NUM 5
|
|
|
|
#define RST_DEVICES_V_SWR_CPULP_RST_DIS (0x1 << 1)
|
|
#define CLK_OUT_ENB_V_CLK_ENB_CPULP_EN (0x1 << 1)
|
|
|
|
#define PERIPH_CLK_TO_BIT(c) (1 << (c->u.periph.clk_num % 32))
|
|
#define PERIPH_CLK_TO_RST_REG(c) \
|
|
periph_clk_to_reg((c), RST_DEVICES_L, RST_DEVICES_V, 4)
|
|
#define PERIPH_CLK_TO_RST_SET_REG(c) \
|
|
periph_clk_to_reg((c), RST_DEVICES_SET_L, RST_DEVICES_SET_V, 8)
|
|
#define PERIPH_CLK_TO_RST_CLR_REG(c) \
|
|
periph_clk_to_reg((c), RST_DEVICES_CLR_L, RST_DEVICES_CLR_V, 8)
|
|
|
|
#define PERIPH_CLK_TO_ENB_REG(c) \
|
|
periph_clk_to_reg((c), CLK_OUT_ENB_L, CLK_OUT_ENB_V, 4)
|
|
#define PERIPH_CLK_TO_ENB_SET_REG(c) \
|
|
periph_clk_to_reg((c), CLK_OUT_ENB_SET_L, CLK_OUT_ENB_SET_V, 8)
|
|
#define PERIPH_CLK_TO_ENB_CLR_REG(c) \
|
|
periph_clk_to_reg((c), CLK_OUT_ENB_CLR_L, CLK_OUT_ENB_CLR_V, 8)
|
|
|
|
#define CLK_MASK_ARM 0x44
|
|
#define MISC_CLK_ENB 0x48
|
|
|
|
#define OSC_CTRL 0x50
|
|
#define OSC_CTRL_OSC_FREQ_MASK (0xF<<28)
|
|
#define OSC_CTRL_OSC_FREQ_13MHZ (0x0<<28)
|
|
#define OSC_CTRL_OSC_FREQ_19_2MHZ (0x4<<28)
|
|
#define OSC_CTRL_OSC_FREQ_12MHZ (0x8<<28)
|
|
#define OSC_CTRL_OSC_FREQ_26MHZ (0xC<<28)
|
|
#define OSC_CTRL_OSC_FREQ_16_8MHZ (0x1<<28)
|
|
#define OSC_CTRL_OSC_FREQ_38_4MHZ (0x5<<28)
|
|
#define OSC_CTRL_OSC_FREQ_48MHZ (0x9<<28)
|
|
#define OSC_CTRL_MASK (0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
|
|
|
|
#define OSC_CTRL_PLL_REF_DIV_MASK (3<<26)
|
|
#define OSC_CTRL_PLL_REF_DIV_1 (0<<26)
|
|
#define OSC_CTRL_PLL_REF_DIV_2 (1<<26)
|
|
#define OSC_CTRL_PLL_REF_DIV_4 (2<<26)
|
|
|
|
#define OSC_FREQ_DET 0x58
|
|
#define OSC_FREQ_DET_TRIG (1<<31)
|
|
|
|
#define OSC_FREQ_DET_STATUS 0x5C
|
|
#define OSC_FREQ_DET_BUSY (1<<31)
|
|
#define OSC_FREQ_DET_CNT_MASK 0xFFFF
|
|
|
|
#define PERIPH_CLK_SOURCE_I2S1 0x100
|
|
#define PERIPH_CLK_SOURCE_EMC 0x19c
|
|
#define PERIPH_CLK_SOURCE_OSC 0x1fc
|
|
#define PERIPH_CLK_SOURCE_NUM1 \
|
|
((PERIPH_CLK_SOURCE_OSC - PERIPH_CLK_SOURCE_I2S1) / 4)
|
|
|
|
#define PERIPH_CLK_SOURCE_G3D2 0x3b0
|
|
#define PERIPH_CLK_SOURCE_SE 0x42c
|
|
#define PERIPH_CLK_SOURCE_NUM2 \
|
|
((PERIPH_CLK_SOURCE_SE - PERIPH_CLK_SOURCE_G3D2) / 4 + 1)
|
|
|
|
#define AUDIO_DLY_CLK 0x49c
|
|
#define AUDIO_SYNC_CLK_SPDIF 0x4b4
|
|
#define PERIPH_CLK_SOURCE_NUM3 \
|
|
((AUDIO_SYNC_CLK_SPDIF - AUDIO_DLY_CLK) / 4 + 1)
|
|
|
|
#define PERIPH_CLK_SOURCE_NUM (PERIPH_CLK_SOURCE_NUM1 + \
|
|
PERIPH_CLK_SOURCE_NUM2 + \
|
|
PERIPH_CLK_SOURCE_NUM3)
|
|
|
|
#define CPU_SOFTRST_CTRL 0x380
|
|
|
|
#define PERIPH_CLK_SOURCE_DIVU71_MASK 0xFF
|
|
#define PERIPH_CLK_SOURCE_DIVU16_MASK 0xFFFF
|
|
#define PERIPH_CLK_SOURCE_DIV_SHIFT 0
|
|
#define PERIPH_CLK_SOURCE_DIVIDLE_SHIFT 8
|
|
#define PERIPH_CLK_SOURCE_DIVIDLE_VAL 50
|
|
#define PERIPH_CLK_UART_DIV_ENB (1<<24)
|
|
#define PERIPH_CLK_VI_SEL_EX_SHIFT 24
|
|
#define PERIPH_CLK_VI_SEL_EX_MASK (0x3<<PERIPH_CLK_VI_SEL_EX_SHIFT)
|
|
#define PERIPH_CLK_NAND_DIV_EX_ENB (1<<8)
|
|
#define PERIPH_CLK_DTV_POLARITY_INV (1<<25)
|
|
|
|
#define AUDIO_SYNC_SOURCE_MASK 0x0F
|
|
#define AUDIO_SYNC_DISABLE_BIT 0x10
|
|
#define AUDIO_SYNC_TAP_NIBBLE_SHIFT(c) ((c->reg_shift - 24) * 4)
|
|
|
|
#define PLL_BASE 0x0
|
|
#define PLL_BASE_BYPASS (1<<31)
|
|
#define PLL_BASE_ENABLE (1<<30)
|
|
#define PLL_BASE_REF_ENABLE (1<<29)
|
|
#define PLL_BASE_OVERRIDE (1<<28)
|
|
#define PLL_BASE_LOCK (1<<27)
|
|
#define PLL_BASE_DIVP_MASK (0x7<<20)
|
|
#define PLL_BASE_DIVP_SHIFT 20
|
|
#define PLL_BASE_DIVN_MASK (0x3FF<<8)
|
|
#define PLL_BASE_DIVN_SHIFT 8
|
|
#define PLL_BASE_DIVM_MASK (0x1F)
|
|
#define PLL_BASE_DIVM_SHIFT 0
|
|
|
|
#define PLL_OUT_RATIO_MASK (0xFF<<8)
|
|
#define PLL_OUT_RATIO_SHIFT 8
|
|
#define PLL_OUT_OVERRIDE (1<<2)
|
|
#define PLL_OUT_CLKEN (1<<1)
|
|
#define PLL_OUT_RESET_DISABLE (1<<0)
|
|
|
|
#define PLL_MISC(c) \
|
|
(((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc)
|
|
#define PLL_MISC_LOCK_ENABLE(c) \
|
|
(((c)->flags & (PLLU | PLLD)) ? (1<<22) : (1<<18))
|
|
|
|
#define PLL_MISC_DCCON_SHIFT 20
|
|
#define PLL_MISC_CPCON_SHIFT 8
|
|
#define PLL_MISC_CPCON_MASK (0xF<<PLL_MISC_CPCON_SHIFT)
|
|
#define PLL_MISC_LFCON_SHIFT 4
|
|
#define PLL_MISC_LFCON_MASK (0xF<<PLL_MISC_LFCON_SHIFT)
|
|
#define PLL_MISC_VCOCON_SHIFT 0
|
|
#define PLL_MISC_VCOCON_MASK (0xF<<PLL_MISC_VCOCON_SHIFT)
|
|
#define PLLD_MISC_CLKENABLE (1<<30)
|
|
|
|
#define PLLU_BASE_POST_DIV (1<<20)
|
|
|
|
#define PLLD_BASE_DSIB_MUX_SHIFT 25
|
|
#define PLLD_BASE_DSIB_MUX_MASK (1<<PLLD_BASE_DSIB_MUX_SHIFT)
|
|
#define PLLD_BASE_CSI_CLKENABLE (1<<26)
|
|
#define PLLD_MISC_DSI_CLKENABLE (1<<30)
|
|
#define PLLD_MISC_DIV_RST (1<<23)
|
|
#define PLLD_MISC_DCCON_SHIFT 12
|
|
|
|
#define PLLDU_LFCON_SET_DIVN 600
|
|
|
|
/* FIXME: OUT_OF_TABLE_CPCON per pll */
|
|
#define OUT_OF_TABLE_CPCON 0x8
|
|
|
|
#define SUPER_CLK_MUX 0x00
|
|
#define SUPER_STATE_SHIFT 28
|
|
#define SUPER_STATE_MASK (0xF << SUPER_STATE_SHIFT)
|
|
#define SUPER_STATE_STANDBY (0x0 << SUPER_STATE_SHIFT)
|
|
#define SUPER_STATE_IDLE (0x1 << SUPER_STATE_SHIFT)
|
|
#define SUPER_STATE_RUN (0x2 << SUPER_STATE_SHIFT)
|
|
#define SUPER_STATE_IRQ (0x3 << SUPER_STATE_SHIFT)
|
|
#define SUPER_STATE_FIQ (0x4 << SUPER_STATE_SHIFT)
|
|
#define SUPER_LP_DIV2_BYPASS (0x1 << 16)
|
|
#define SUPER_SOURCE_MASK 0xF
|
|
#define SUPER_FIQ_SOURCE_SHIFT 12
|
|
#define SUPER_IRQ_SOURCE_SHIFT 8
|
|
#define SUPER_RUN_SOURCE_SHIFT 4
|
|
#define SUPER_IDLE_SOURCE_SHIFT 0
|
|
|
|
#define SUPER_CLK_DIVIDER 0x04
|
|
#define SUPER_CLOCK_DIV_U71_SHIFT 16
|
|
#define SUPER_CLOCK_DIV_U71_MASK (0xff << SUPER_CLOCK_DIV_U71_SHIFT)
|
|
/* guarantees safe cpu backup */
|
|
#define SUPER_CLOCK_DIV_U71_MIN 0x2
|
|
|
|
#define BUS_CLK_DISABLE (1<<3)
|
|
#define BUS_CLK_DIV_MASK 0x3
|
|
|
|
#define PMC_CTRL 0x0
|
|
#define PMC_CTRL_BLINK_ENB (1 << 7)
|
|
|
|
#define PMC_DPD_PADS_ORIDE 0x1c
|
|
#define PMC_DPD_PADS_ORIDE_BLINK_ENB (1 << 20)
|
|
|
|
#define PMC_BLINK_TIMER_DATA_ON_SHIFT 0
|
|
#define PMC_BLINK_TIMER_DATA_ON_MASK 0x7fff
|
|
#define PMC_BLINK_TIMER_ENB (1 << 15)
|
|
#define PMC_BLINK_TIMER_DATA_OFF_SHIFT 16
|
|
#define PMC_BLINK_TIMER_DATA_OFF_MASK 0xffff
|
|
|
|
#define PMC_PLLP_WB0_OVERRIDE 0xf8
|
|
#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE (1 << 12)
|
|
|
|
#define UTMIP_PLL_CFG2 0x488
|
|
#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xfff) << 6)
|
|
#define UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(x) (((x) & 0x3f) << 18)
|
|
#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN (1 << 0)
|
|
#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN (1 << 2)
|
|
#define UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN (1 << 4)
|
|
|
|
#define UTMIP_PLL_CFG1 0x484
|
|
#define UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
|
|
#define UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
|
|
#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN (1 << 14)
|
|
#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN (1 << 12)
|
|
#define UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN (1 << 16)
|
|
|
|
#define PLLE_BASE_CML_ENABLE (1<<31)
|
|
#define PLLE_BASE_ENABLE (1<<30)
|
|
#define PLLE_BASE_DIVCML_SHIFT 24
|
|
#define PLLE_BASE_DIVCML_MASK (0xf<<PLLE_BASE_DIVCML_SHIFT)
|
|
#define PLLE_BASE_DIVP_SHIFT 16
|
|
#define PLLE_BASE_DIVP_MASK (0x3f<<PLLE_BASE_DIVP_SHIFT)
|
|
#define PLLE_BASE_DIVN_SHIFT 8
|
|
#define PLLE_BASE_DIVN_MASK (0xFF<<PLLE_BASE_DIVN_SHIFT)
|
|
#define PLLE_BASE_DIVM_SHIFT 0
|
|
#define PLLE_BASE_DIVM_MASK (0xFF<<PLLE_BASE_DIVM_SHIFT)
|
|
#define PLLE_BASE_DIV_MASK \
|
|
(PLLE_BASE_DIVCML_MASK | PLLE_BASE_DIVP_MASK | \
|
|
PLLE_BASE_DIVN_MASK | PLLE_BASE_DIVM_MASK)
|
|
#define PLLE_BASE_DIV(m, n, p, cml) \
|
|
(((cml)<<PLLE_BASE_DIVCML_SHIFT) | ((p)<<PLLE_BASE_DIVP_SHIFT) | \
|
|
((n)<<PLLE_BASE_DIVN_SHIFT) | ((m)<<PLLE_BASE_DIVM_SHIFT))
|
|
|
|
#define PLLE_MISC_SETUP_BASE_SHIFT 16
|
|
#define PLLE_MISC_SETUP_BASE_MASK (0xFFFF<<PLLE_MISC_SETUP_BASE_SHIFT)
|
|
#define PLLE_MISC_READY (1<<15)
|
|
#define PLLE_MISC_LOCK (1<<11)
|
|
#define PLLE_MISC_LOCK_ENABLE (1<<9)
|
|
#define PLLE_MISC_SETUP_EX_SHIFT 2
|
|
#define PLLE_MISC_SETUP_EX_MASK (0x3<<PLLE_MISC_SETUP_EX_SHIFT)
|
|
#define PLLE_MISC_SETUP_MASK \
|
|
(PLLE_MISC_SETUP_BASE_MASK | PLLE_MISC_SETUP_EX_MASK)
|
|
#define PLLE_MISC_SETUP_VALUE \
|
|
((0x7<<PLLE_MISC_SETUP_BASE_SHIFT) | (0x0<<PLLE_MISC_SETUP_EX_SHIFT))
|
|
|
|
#define PLLE_SS_CTRL 0x68
|
|
#define PLLE_SS_INCINTRV_SHIFT 24
|
|
#define PLLE_SS_INCINTRV_MASK (0x3f<<PLLE_SS_INCINTRV_SHIFT)
|
|
#define PLLE_SS_INC_SHIFT 16
|
|
#define PLLE_SS_INC_MASK (0xff<<PLLE_SS_INC_SHIFT)
|
|
#define PLLE_SS_MAX_SHIFT 0
|
|
#define PLLE_SS_MAX_MASK (0x1ff<<PLLE_SS_MAX_SHIFT)
|
|
#define PLLE_SS_COEFFICIENTS_MASK \
|
|
(PLLE_SS_INCINTRV_MASK | PLLE_SS_INC_MASK | PLLE_SS_MAX_MASK)
|
|
#define PLLE_SS_COEFFICIENTS_12MHZ \
|
|
((0x18<<PLLE_SS_INCINTRV_SHIFT) | (0x1<<PLLE_SS_INC_SHIFT) | \
|
|
(0x24<<PLLE_SS_MAX_SHIFT))
|
|
#define PLLE_SS_DISABLE ((1<<12) | (1<<11) | (1<<10))
|
|
|
|
#define PLLE_AUX 0x48c
|
|
#define PLLE_AUX_PLLP_SEL (1<<2)
|
|
#define PLLE_AUX_CML_SATA_ENABLE (1<<1)
|
|
#define PLLE_AUX_CML_PCIE_ENABLE (1<<0)
|
|
|
|
#define PMC_SATA_PWRGT 0x1ac
|
|
#define PMC_SATA_PWRGT_PLLE_IDDQ_VALUE (1<<5)
|
|
#define PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL (1<<4)
|
|
|
|
#define ROUND_DIVIDER_UP 0
|
|
#define ROUND_DIVIDER_DOWN 1
|
|
|
|
/* FIXME: recommended safety delay after lock is detected */
|
|
#define PLL_POST_LOCK_DELAY 100
|
|
|
|
/**
|
|
* Structure defining the fields for USB UTMI clocks Parameters.
|
|
*/
|
|
struct utmi_clk_param {
|
|
/* Oscillator Frequency in KHz */
|
|
u32 osc_frequency;
|
|
/* UTMIP PLL Enable Delay Count */
|
|
u8 enable_delay_count;
|
|
/* UTMIP PLL Stable count */
|
|
u8 stable_count;
|
|
/* UTMIP PLL Active delay count */
|
|
u8 active_delay_count;
|
|
/* UTMIP PLL Xtal frequency count */
|
|
u8 xtal_freq_count;
|
|
};
|
|
|
|
static const struct utmi_clk_param utmi_parameters[] = {
|
|
{
|
|
.osc_frequency = 13000000,
|
|
.enable_delay_count = 0x02,
|
|
.stable_count = 0x33,
|
|
.active_delay_count = 0x05,
|
|
.xtal_freq_count = 0x7F
|
|
},
|
|
{
|
|
.osc_frequency = 19200000,
|
|
.enable_delay_count = 0x03,
|
|
.stable_count = 0x4B,
|
|
.active_delay_count = 0x06,
|
|
.xtal_freq_count = 0xBB},
|
|
{
|
|
.osc_frequency = 12000000,
|
|
.enable_delay_count = 0x02,
|
|
.stable_count = 0x2F,
|
|
.active_delay_count = 0x04,
|
|
.xtal_freq_count = 0x76
|
|
},
|
|
{
|
|
.osc_frequency = 26000000,
|
|
.enable_delay_count = 0x04,
|
|
.stable_count = 0x66,
|
|
.active_delay_count = 0x09,
|
|
.xtal_freq_count = 0xFE
|
|
},
|
|
{
|
|
.osc_frequency = 16800000,
|
|
.enable_delay_count = 0x03,
|
|
.stable_count = 0x41,
|
|
.active_delay_count = 0x0A,
|
|
.xtal_freq_count = 0xA4
|
|
},
|
|
};
|
|
|
|
static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
|
|
static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
|
|
static void __iomem *misc_gp_hidrev_base = IO_ADDRESS(TEGRA_APB_MISC_BASE);
|
|
|
|
#define MISC_GP_HIDREV 0x804
|
|
|
|
/*
|
|
* Some peripheral clocks share an enable bit, so refcount the enable bits
|
|
* in registers CLK_ENABLE_L, ... CLK_ENABLE_W
|
|
*/
|
|
static int tegra_periph_clk_enable_refcount[CLK_OUT_ENB_NUM * 32];
|
|
|
|
#define clk_writel(value, reg) \
|
|
__raw_writel(value, (u32)reg_clk_base + (reg))
|
|
#define clk_readl(reg) \
|
|
__raw_readl((u32)reg_clk_base + (reg))
|
|
#define pmc_writel(value, reg) \
|
|
__raw_writel(value, (u32)reg_pmc_base + (reg))
|
|
#define pmc_readl(reg) \
|
|
__raw_readl((u32)reg_pmc_base + (reg))
|
|
#define chipid_readl() \
|
|
__raw_readl((u32)misc_gp_hidrev_base + MISC_GP_HIDREV)
|
|
|
|
#define clk_writel_delay(value, reg) \
|
|
do { \
|
|
__raw_writel((value), (u32)reg_clk_base + (reg)); \
|
|
udelay(2); \
|
|
} while (0)
|
|
|
|
|
|
static inline int clk_set_div(struct clk *c, u32 n)
|
|
{
|
|
return clk_set_rate(c, (clk_get_rate(c->parent) + n-1) / n);
|
|
}
|
|
|
|
static inline u32 periph_clk_to_reg(
|
|
struct clk *c, u32 reg_L, u32 reg_V, int offs)
|
|
{
|
|
u32 reg = c->u.periph.clk_num / 32;
|
|
BUG_ON(reg >= RST_DEVICES_NUM);
|
|
if (reg < 3)
|
|
reg = reg_L + (reg * offs);
|
|
else
|
|
reg = reg_V + ((reg - 3) * offs);
|
|
return reg;
|
|
}
|
|
|
|
static unsigned long clk_measure_input_freq(void)
|
|
{
|
|
u32 clock_autodetect;
|
|
clk_writel(OSC_FREQ_DET_TRIG | 1, OSC_FREQ_DET);
|
|
do {} while (clk_readl(OSC_FREQ_DET_STATUS) & OSC_FREQ_DET_BUSY);
|
|
clock_autodetect = clk_readl(OSC_FREQ_DET_STATUS);
|
|
if (clock_autodetect >= 732 - 3 && clock_autodetect <= 732 + 3) {
|
|
return 12000000;
|
|
} else if (clock_autodetect >= 794 - 3 && clock_autodetect <= 794 + 3) {
|
|
return 13000000;
|
|
} else if (clock_autodetect >= 1172 - 3 && clock_autodetect <= 1172 + 3) {
|
|
return 19200000;
|
|
} else if (clock_autodetect >= 1587 - 3 && clock_autodetect <= 1587 + 3) {
|
|
return 26000000;
|
|
} else if (clock_autodetect >= 1025 - 3 && clock_autodetect <= 1025 + 3) {
|
|
return 16800000;
|
|
} else if (clock_autodetect >= 2344 - 3 && clock_autodetect <= 2344 + 3) {
|
|
return 38400000;
|
|
} else if (clock_autodetect >= 2928 - 3 && clock_autodetect <= 2928 + 3) {
|
|
return 48000000;
|
|
} else {
|
|
pr_err("%s: Unexpected clock autodetect value %d", __func__,
|
|
clock_autodetect);
|
|
BUG();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int clk_div71_get_divider(unsigned long parent_rate, unsigned long rate,
|
|
u32 flags, u32 round_mode)
|
|
{
|
|
s64 divider_u71 = parent_rate;
|
|
if (!rate)
|
|
return -EINVAL;
|
|
|
|
if (!(flags & DIV_U71_INT))
|
|
divider_u71 *= 2;
|
|
if (round_mode == ROUND_DIVIDER_UP)
|
|
divider_u71 += rate - 1;
|
|
do_div(divider_u71, rate);
|
|
if (flags & DIV_U71_INT)
|
|
divider_u71 *= 2;
|
|
|
|
if (divider_u71 - 2 < 0)
|
|
return 0;
|
|
|
|
if (divider_u71 - 2 > 255)
|
|
return -EINVAL;
|
|
|
|
return divider_u71 - 2;
|
|
}
|
|
|
|
static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
|
|
{
|
|
s64 divider_u16;
|
|
|
|
divider_u16 = parent_rate;
|
|
if (!rate)
|
|
return -EINVAL;
|
|
divider_u16 += rate - 1;
|
|
do_div(divider_u16, rate);
|
|
|
|
if (divider_u16 - 1 < 0)
|
|
return 0;
|
|
|
|
if (divider_u16 - 1 > 0xFFFF)
|
|
return -EINVAL;
|
|
|
|
return divider_u16 - 1;
|
|
}
|
|
|
|
/* clk_m functions */
|
|
static unsigned long tegra30_clk_m_autodetect_rate(struct clk *c)
|
|
{
|
|
u32 osc_ctrl = clk_readl(OSC_CTRL);
|
|
u32 auto_clock_control = osc_ctrl & ~OSC_CTRL_OSC_FREQ_MASK;
|
|
u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
|
|
|
|
c->rate = clk_measure_input_freq();
|
|
switch (c->rate) {
|
|
case 12000000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_12MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
|
|
break;
|
|
case 13000000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_13MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
|
|
break;
|
|
case 19200000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_19_2MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
|
|
break;
|
|
case 26000000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_26MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
|
|
break;
|
|
case 16800000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_16_8MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
|
|
break;
|
|
case 38400000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_38_4MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_2);
|
|
break;
|
|
case 48000000:
|
|
auto_clock_control |= OSC_CTRL_OSC_FREQ_48MHZ;
|
|
BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_4);
|
|
break;
|
|
default:
|
|
pr_err("%s: Unexpected clock rate %ld", __func__, c->rate);
|
|
BUG();
|
|
}
|
|
clk_writel(auto_clock_control, OSC_CTRL);
|
|
return c->rate;
|
|
}
|
|
|
|
static void tegra30_clk_m_init(struct clk *c)
|
|
{
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
tegra30_clk_m_autodetect_rate(c);
|
|
}
|
|
|
|
static int tegra30_clk_m_enable(struct clk *c)
|
|
{
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_clk_m_disable(struct clk *c)
|
|
{
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
WARN(1, "Attempting to disable main SoC clock\n");
|
|
}
|
|
|
|
static struct clk_ops tegra_clk_m_ops = {
|
|
.init = tegra30_clk_m_init,
|
|
.enable = tegra30_clk_m_enable,
|
|
.disable = tegra30_clk_m_disable,
|
|
};
|
|
|
|
static struct clk_ops tegra_clk_m_div_ops = {
|
|
.enable = tegra30_clk_m_enable,
|
|
};
|
|
|
|
/* PLL reference divider functions */
|
|
static void tegra30_pll_ref_init(struct clk *c)
|
|
{
|
|
u32 pll_ref_div = clk_readl(OSC_CTRL) & OSC_CTRL_PLL_REF_DIV_MASK;
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
switch (pll_ref_div) {
|
|
case OSC_CTRL_PLL_REF_DIV_1:
|
|
c->div = 1;
|
|
break;
|
|
case OSC_CTRL_PLL_REF_DIV_2:
|
|
c->div = 2;
|
|
break;
|
|
case OSC_CTRL_PLL_REF_DIV_4:
|
|
c->div = 4;
|
|
break;
|
|
default:
|
|
pr_err("%s: Invalid pll ref divider %d", __func__, pll_ref_div);
|
|
BUG();
|
|
}
|
|
c->mul = 1;
|
|
c->state = ON;
|
|
}
|
|
|
|
static struct clk_ops tegra_pll_ref_ops = {
|
|
.init = tegra30_pll_ref_init,
|
|
.enable = tegra30_clk_m_enable,
|
|
.disable = tegra30_clk_m_disable,
|
|
};
|
|
|
|
/* super clock functions */
|
|
/* "super clocks" on tegra30 have two-stage muxes, fractional 7.1 divider and
|
|
* clock skipping super divider. We will ignore the clock skipping divider,
|
|
* since we can't lower the voltage when using the clock skip, but we can if
|
|
* we lower the PLL frequency. We will use 7.1 divider for CPU super-clock
|
|
* only when its parent is a fixed rate PLL, since we can't change PLL rate
|
|
* in this case.
|
|
*/
|
|
static void tegra30_super_clk_init(struct clk *c)
|
|
{
|
|
u32 val;
|
|
int source;
|
|
int shift;
|
|
const struct clk_mux_sel *sel;
|
|
val = clk_readl(c->reg + SUPER_CLK_MUX);
|
|
c->state = ON;
|
|
BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
|
|
((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
|
|
shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
|
|
SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
|
|
source = (val >> shift) & SUPER_SOURCE_MASK;
|
|
if (c->flags & DIV_2)
|
|
source |= val & SUPER_LP_DIV2_BYPASS;
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (sel->value == source)
|
|
break;
|
|
}
|
|
BUG_ON(sel->input == NULL);
|
|
c->parent = sel->input;
|
|
|
|
if (c->flags & DIV_U71) {
|
|
/* Init safe 7.1 divider value (does not affect PLLX path) */
|
|
clk_writel(SUPER_CLOCK_DIV_U71_MIN << SUPER_CLOCK_DIV_U71_SHIFT,
|
|
c->reg + SUPER_CLK_DIVIDER);
|
|
c->mul = 2;
|
|
c->div = 2;
|
|
if (!(c->parent->flags & PLLX))
|
|
c->div += SUPER_CLOCK_DIV_U71_MIN;
|
|
} else
|
|
clk_writel(0, c->reg + SUPER_CLK_DIVIDER);
|
|
}
|
|
|
|
static int tegra30_super_clk_enable(struct clk *c)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_super_clk_disable(struct clk *c)
|
|
{
|
|
/* since tegra 3 has 2 CPU super clocks - low power lp-mode clock and
|
|
geared up g-mode super clock - mode switch may request to disable
|
|
either of them; accept request with no affect on h/w */
|
|
}
|
|
|
|
static int tegra30_super_clk_set_parent(struct clk *c, struct clk *p)
|
|
{
|
|
u32 val;
|
|
const struct clk_mux_sel *sel;
|
|
int shift;
|
|
|
|
val = clk_readl(c->reg + SUPER_CLK_MUX);
|
|
BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
|
|
((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
|
|
shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
|
|
SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (sel->input == p) {
|
|
/* For LP mode super-clock switch between PLLX direct
|
|
and divided-by-2 outputs is allowed only when other
|
|
than PLLX clock source is current parent */
|
|
if ((c->flags & DIV_2) && (p->flags & PLLX) &&
|
|
((sel->value ^ val) & SUPER_LP_DIV2_BYPASS)) {
|
|
if (c->parent->flags & PLLX)
|
|
return -EINVAL;
|
|
val ^= SUPER_LP_DIV2_BYPASS;
|
|
clk_writel_delay(val, c->reg);
|
|
}
|
|
val &= ~(SUPER_SOURCE_MASK << shift);
|
|
val |= (sel->value & SUPER_SOURCE_MASK) << shift;
|
|
|
|
/* 7.1 divider for CPU super-clock does not affect
|
|
PLLX path */
|
|
if (c->flags & DIV_U71) {
|
|
u32 div = 0;
|
|
if (!(p->flags & PLLX)) {
|
|
div = clk_readl(c->reg +
|
|
SUPER_CLK_DIVIDER);
|
|
div &= SUPER_CLOCK_DIV_U71_MASK;
|
|
div >>= SUPER_CLOCK_DIV_U71_SHIFT;
|
|
}
|
|
c->div = div + 2;
|
|
c->mul = 2;
|
|
}
|
|
|
|
if (c->refcnt)
|
|
clk_enable(p);
|
|
|
|
clk_writel_delay(val, c->reg);
|
|
|
|
if (c->refcnt && c->parent)
|
|
clk_disable(c->parent);
|
|
|
|
clk_reparent(c, p);
|
|
return 0;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Do not use super clocks "skippers", since dividing using a clock skipper
|
|
* does not allow the voltage to be scaled down. Instead adjust the rate of
|
|
* the parent clock. This requires that the parent of a super clock have no
|
|
* other children, otherwise the rate will change underneath the other
|
|
* children. Special case: if fixed rate PLL is CPU super clock parent the
|
|
* rate of this PLL can't be changed, and it has many other children. In
|
|
* this case use 7.1 fractional divider to adjust the super clock rate.
|
|
*/
|
|
static int tegra30_super_clk_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
if ((c->flags & DIV_U71) && (c->parent->flags & PLL_FIXED)) {
|
|
int div = clk_div71_get_divider(c->parent->u.pll.fixed_rate,
|
|
rate, c->flags, ROUND_DIVIDER_DOWN);
|
|
div = max(div, SUPER_CLOCK_DIV_U71_MIN);
|
|
|
|
clk_writel(div << SUPER_CLOCK_DIV_U71_SHIFT,
|
|
c->reg + SUPER_CLK_DIVIDER);
|
|
c->div = div + 2;
|
|
c->mul = 2;
|
|
return 0;
|
|
}
|
|
return clk_set_rate(c->parent, rate);
|
|
}
|
|
|
|
static struct clk_ops tegra_super_ops = {
|
|
.init = tegra30_super_clk_init,
|
|
.enable = tegra30_super_clk_enable,
|
|
.disable = tegra30_super_clk_disable,
|
|
.set_parent = tegra30_super_clk_set_parent,
|
|
.set_rate = tegra30_super_clk_set_rate,
|
|
};
|
|
|
|
static int tegra30_twd_clk_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
/* The input value 'rate' is the clock rate of the CPU complex. */
|
|
c->rate = (rate * c->mul) / c->div;
|
|
return 0;
|
|
}
|
|
|
|
static struct clk_ops tegra30_twd_ops = {
|
|
.set_rate = tegra30_twd_clk_set_rate,
|
|
};
|
|
|
|
/* Blink output functions */
|
|
|
|
static void tegra30_blink_clk_init(struct clk *c)
|
|
{
|
|
u32 val;
|
|
|
|
val = pmc_readl(PMC_CTRL);
|
|
c->state = (val & PMC_CTRL_BLINK_ENB) ? ON : OFF;
|
|
c->mul = 1;
|
|
val = pmc_readl(c->reg);
|
|
|
|
if (val & PMC_BLINK_TIMER_ENB) {
|
|
unsigned int on_off;
|
|
|
|
on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
|
|
PMC_BLINK_TIMER_DATA_ON_MASK;
|
|
val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
|
|
val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
|
|
on_off += val;
|
|
/* each tick in the blink timer is 4 32KHz clocks */
|
|
c->div = on_off * 4;
|
|
} else {
|
|
c->div = 1;
|
|
}
|
|
}
|
|
|
|
static int tegra30_blink_clk_enable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
|
|
val = pmc_readl(PMC_DPD_PADS_ORIDE);
|
|
pmc_writel(val | PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
|
|
|
|
val = pmc_readl(PMC_CTRL);
|
|
pmc_writel(val | PMC_CTRL_BLINK_ENB, PMC_CTRL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_blink_clk_disable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
|
|
val = pmc_readl(PMC_CTRL);
|
|
pmc_writel(val & ~PMC_CTRL_BLINK_ENB, PMC_CTRL);
|
|
|
|
val = pmc_readl(PMC_DPD_PADS_ORIDE);
|
|
pmc_writel(val & ~PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
|
|
}
|
|
|
|
static int tegra30_blink_clk_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
unsigned long parent_rate = clk_get_rate(c->parent);
|
|
if (rate >= parent_rate) {
|
|
c->div = 1;
|
|
pmc_writel(0, c->reg);
|
|
} else {
|
|
unsigned int on_off;
|
|
u32 val;
|
|
|
|
on_off = DIV_ROUND_UP(parent_rate / 8, rate);
|
|
c->div = on_off * 8;
|
|
|
|
val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
|
|
PMC_BLINK_TIMER_DATA_ON_SHIFT;
|
|
on_off &= PMC_BLINK_TIMER_DATA_OFF_MASK;
|
|
on_off <<= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
|
|
val |= on_off;
|
|
val |= PMC_BLINK_TIMER_ENB;
|
|
pmc_writel(val, c->reg);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct clk_ops tegra_blink_clk_ops = {
|
|
.init = &tegra30_blink_clk_init,
|
|
.enable = &tegra30_blink_clk_enable,
|
|
.disable = &tegra30_blink_clk_disable,
|
|
.set_rate = &tegra30_blink_clk_set_rate,
|
|
};
|
|
|
|
/* PLL Functions */
|
|
static int tegra30_pll_clk_wait_for_lock(struct clk *c, u32 lock_reg,
|
|
u32 lock_bit)
|
|
{
|
|
#if USE_PLL_LOCK_BITS
|
|
int i;
|
|
for (i = 0; i < c->u.pll.lock_delay; i++) {
|
|
if (clk_readl(lock_reg) & lock_bit) {
|
|
udelay(PLL_POST_LOCK_DELAY);
|
|
return 0;
|
|
}
|
|
udelay(2); /* timeout = 2 * lock time */
|
|
}
|
|
pr_err("Timed out waiting for lock bit on pll %s", c->name);
|
|
return -1;
|
|
#endif
|
|
udelay(c->u.pll.lock_delay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void tegra30_utmi_param_configure(struct clk *c)
|
|
{
|
|
u32 reg;
|
|
int i;
|
|
unsigned long main_rate =
|
|
clk_get_rate(c->parent->parent);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(utmi_parameters); i++) {
|
|
if (main_rate == utmi_parameters[i].osc_frequency)
|
|
break;
|
|
}
|
|
|
|
if (i >= ARRAY_SIZE(utmi_parameters)) {
|
|
pr_err("%s: Unexpected main rate %lu\n", __func__, main_rate);
|
|
return;
|
|
}
|
|
|
|
reg = clk_readl(UTMIP_PLL_CFG2);
|
|
|
|
/* Program UTMIP PLL stable and active counts */
|
|
/* [FIXME] arclk_rst.h says WRONG! This should be 1ms -> 0x50 Check! */
|
|
reg &= ~UTMIP_PLL_CFG2_STABLE_COUNT(~0);
|
|
reg |= UTMIP_PLL_CFG2_STABLE_COUNT(
|
|
utmi_parameters[i].stable_count);
|
|
|
|
reg &= ~UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(~0);
|
|
|
|
reg |= UTMIP_PLL_CFG2_ACTIVE_DLY_COUNT(
|
|
utmi_parameters[i].active_delay_count);
|
|
|
|
/* Remove power downs from UTMIP PLL control bits */
|
|
reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_A_POWERDOWN;
|
|
reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_B_POWERDOWN;
|
|
reg &= ~UTMIP_PLL_CFG2_FORCE_PD_SAMP_C_POWERDOWN;
|
|
|
|
clk_writel(reg, UTMIP_PLL_CFG2);
|
|
|
|
/* Program UTMIP PLL delay and oscillator frequency counts */
|
|
reg = clk_readl(UTMIP_PLL_CFG1);
|
|
reg &= ~UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(~0);
|
|
|
|
reg |= UTMIP_PLL_CFG1_ENABLE_DLY_COUNT(
|
|
utmi_parameters[i].enable_delay_count);
|
|
|
|
reg &= ~UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(~0);
|
|
reg |= UTMIP_PLL_CFG1_XTAL_FREQ_COUNT(
|
|
utmi_parameters[i].xtal_freq_count);
|
|
|
|
/* Remove power downs from UTMIP PLL control bits */
|
|
reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN;
|
|
reg &= ~UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN;
|
|
reg &= ~UTMIP_PLL_CFG1_FORCE_PLLU_POWERDOWN;
|
|
|
|
clk_writel(reg, UTMIP_PLL_CFG1);
|
|
}
|
|
|
|
static void tegra30_pll_clk_init(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg + PLL_BASE);
|
|
|
|
c->state = (val & PLL_BASE_ENABLE) ? ON : OFF;
|
|
|
|
if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
|
|
const struct clk_pll_freq_table *sel;
|
|
unsigned long input_rate = clk_get_rate(c->parent);
|
|
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
|
|
if (sel->input_rate == input_rate &&
|
|
sel->output_rate == c->u.pll.fixed_rate) {
|
|
c->mul = sel->n;
|
|
c->div = sel->m * sel->p;
|
|
return;
|
|
}
|
|
}
|
|
pr_err("Clock %s has unknown fixed frequency\n", c->name);
|
|
BUG();
|
|
} else if (val & PLL_BASE_BYPASS) {
|
|
c->mul = 1;
|
|
c->div = 1;
|
|
} else {
|
|
c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT;
|
|
c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT;
|
|
if (c->flags & PLLU)
|
|
c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2;
|
|
else
|
|
c->div *= (0x1 << ((val & PLL_BASE_DIVP_MASK) >>
|
|
PLL_BASE_DIVP_SHIFT));
|
|
if (c->flags & PLL_FIXED) {
|
|
unsigned long rate = clk_get_rate_locked(c);
|
|
BUG_ON(rate != c->u.pll.fixed_rate);
|
|
}
|
|
}
|
|
|
|
if (c->flags & PLLU)
|
|
tegra30_utmi_param_configure(c);
|
|
}
|
|
|
|
static int tegra30_pll_clk_enable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
#if USE_PLL_LOCK_BITS
|
|
val = clk_readl(c->reg + PLL_MISC(c));
|
|
val |= PLL_MISC_LOCK_ENABLE(c);
|
|
clk_writel(val, c->reg + PLL_MISC(c));
|
|
#endif
|
|
val = clk_readl(c->reg + PLL_BASE);
|
|
val &= ~PLL_BASE_BYPASS;
|
|
val |= PLL_BASE_ENABLE;
|
|
clk_writel(val, c->reg + PLL_BASE);
|
|
|
|
if (c->flags & PLLM) {
|
|
val = pmc_readl(PMC_PLLP_WB0_OVERRIDE);
|
|
val |= PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
|
|
pmc_writel(val, PMC_PLLP_WB0_OVERRIDE);
|
|
}
|
|
|
|
tegra30_pll_clk_wait_for_lock(c, c->reg + PLL_BASE, PLL_BASE_LOCK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_pll_clk_disable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
val = clk_readl(c->reg);
|
|
val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
|
|
clk_writel(val, c->reg);
|
|
|
|
if (c->flags & PLLM) {
|
|
val = pmc_readl(PMC_PLLP_WB0_OVERRIDE);
|
|
val &= ~PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE;
|
|
pmc_writel(val, PMC_PLLP_WB0_OVERRIDE);
|
|
}
|
|
}
|
|
|
|
static int tegra30_pll_clk_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
u32 val, p_div, old_base;
|
|
unsigned long input_rate;
|
|
const struct clk_pll_freq_table *sel;
|
|
struct clk_pll_freq_table cfg;
|
|
|
|
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
|
|
|
|
if (c->flags & PLL_FIXED) {
|
|
int ret = 0;
|
|
if (rate != c->u.pll.fixed_rate) {
|
|
pr_err("%s: Can not change %s fixed rate %lu to %lu\n",
|
|
__func__, c->name, c->u.pll.fixed_rate, rate);
|
|
ret = -EINVAL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
if (c->flags & PLLM) {
|
|
if (rate != clk_get_rate_locked(c)) {
|
|
pr_err("%s: Can not change memory %s rate in flight\n",
|
|
__func__, c->name);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
p_div = 0;
|
|
input_rate = clk_get_rate(c->parent);
|
|
|
|
/* Check if the target rate is tabulated */
|
|
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
|
|
if (sel->input_rate == input_rate && sel->output_rate == rate) {
|
|
if (c->flags & PLLU) {
|
|
BUG_ON(sel->p < 1 || sel->p > 2);
|
|
if (sel->p == 1)
|
|
p_div = PLLU_BASE_POST_DIV;
|
|
} else {
|
|
BUG_ON(sel->p < 1);
|
|
for (val = sel->p; val > 1; val >>= 1)
|
|
p_div++;
|
|
p_div <<= PLL_BASE_DIVP_SHIFT;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Configure out-of-table rate */
|
|
if (sel->input_rate == 0) {
|
|
unsigned long cfreq;
|
|
BUG_ON(c->flags & PLLU);
|
|
sel = &cfg;
|
|
|
|
switch (input_rate) {
|
|
case 12000000:
|
|
case 26000000:
|
|
cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2000000;
|
|
break;
|
|
case 13000000:
|
|
cfreq = (rate <= 1000000 * 1000) ? 1000000 : 2600000;
|
|
break;
|
|
case 16800000:
|
|
case 19200000:
|
|
cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
|
|
break;
|
|
default:
|
|
pr_err("%s: Unexpected reference rate %lu\n",
|
|
__func__, input_rate);
|
|
BUG();
|
|
}
|
|
|
|
/* Raise VCO to guarantee 0.5% accuracy */
|
|
for (cfg.output_rate = rate; cfg.output_rate < 200 * cfreq;
|
|
cfg.output_rate <<= 1)
|
|
p_div++;
|
|
|
|
cfg.p = 0x1 << p_div;
|
|
cfg.m = input_rate / cfreq;
|
|
cfg.n = cfg.output_rate / cfreq;
|
|
cfg.cpcon = OUT_OF_TABLE_CPCON;
|
|
|
|
if ((cfg.m > (PLL_BASE_DIVM_MASK >> PLL_BASE_DIVM_SHIFT)) ||
|
|
(cfg.n > (PLL_BASE_DIVN_MASK >> PLL_BASE_DIVN_SHIFT)) ||
|
|
(p_div > (PLL_BASE_DIVP_MASK >> PLL_BASE_DIVP_SHIFT)) ||
|
|
(cfg.output_rate > c->u.pll.vco_max)) {
|
|
pr_err("%s: Failed to set %s out-of-table rate %lu\n",
|
|
__func__, c->name, rate);
|
|
return -EINVAL;
|
|
}
|
|
p_div <<= PLL_BASE_DIVP_SHIFT;
|
|
}
|
|
|
|
c->mul = sel->n;
|
|
c->div = sel->m * sel->p;
|
|
|
|
old_base = val = clk_readl(c->reg + PLL_BASE);
|
|
val &= ~(PLL_BASE_DIVM_MASK | PLL_BASE_DIVN_MASK |
|
|
((c->flags & PLLU) ? PLLU_BASE_POST_DIV : PLL_BASE_DIVP_MASK));
|
|
val |= (sel->m << PLL_BASE_DIVM_SHIFT) |
|
|
(sel->n << PLL_BASE_DIVN_SHIFT) | p_div;
|
|
if (val == old_base)
|
|
return 0;
|
|
|
|
if (c->state == ON) {
|
|
tegra30_pll_clk_disable(c);
|
|
val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
|
|
}
|
|
clk_writel(val, c->reg + PLL_BASE);
|
|
|
|
if (c->flags & PLL_HAS_CPCON) {
|
|
val = clk_readl(c->reg + PLL_MISC(c));
|
|
val &= ~PLL_MISC_CPCON_MASK;
|
|
val |= sel->cpcon << PLL_MISC_CPCON_SHIFT;
|
|
if (c->flags & (PLLU | PLLD)) {
|
|
val &= ~PLL_MISC_LFCON_MASK;
|
|
if (sel->n >= PLLDU_LFCON_SET_DIVN)
|
|
val |= 0x1 << PLL_MISC_LFCON_SHIFT;
|
|
} else if (c->flags & (PLLX | PLLM)) {
|
|
val &= ~(0x1 << PLL_MISC_DCCON_SHIFT);
|
|
if (rate >= (c->u.pll.vco_max >> 1))
|
|
val |= 0x1 << PLL_MISC_DCCON_SHIFT;
|
|
}
|
|
clk_writel(val, c->reg + PLL_MISC(c));
|
|
}
|
|
|
|
if (c->state == ON)
|
|
tegra30_pll_clk_enable(c);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct clk_ops tegra_pll_ops = {
|
|
.init = tegra30_pll_clk_init,
|
|
.enable = tegra30_pll_clk_enable,
|
|
.disable = tegra30_pll_clk_disable,
|
|
.set_rate = tegra30_pll_clk_set_rate,
|
|
};
|
|
|
|
static int
|
|
tegra30_plld_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
|
|
{
|
|
u32 val, mask, reg;
|
|
|
|
switch (p) {
|
|
case TEGRA_CLK_PLLD_CSI_OUT_ENB:
|
|
mask = PLLD_BASE_CSI_CLKENABLE;
|
|
reg = c->reg + PLL_BASE;
|
|
break;
|
|
case TEGRA_CLK_PLLD_DSI_OUT_ENB:
|
|
mask = PLLD_MISC_DSI_CLKENABLE;
|
|
reg = c->reg + PLL_MISC(c);
|
|
break;
|
|
case TEGRA_CLK_PLLD_MIPI_MUX_SEL:
|
|
if (!(c->flags & PLL_ALT_MISC_REG)) {
|
|
mask = PLLD_BASE_DSIB_MUX_MASK;
|
|
reg = c->reg + PLL_BASE;
|
|
break;
|
|
}
|
|
/* fall through - error since PLLD2 does not have MUX_SEL control */
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
val = clk_readl(reg);
|
|
if (setting)
|
|
val |= mask;
|
|
else
|
|
val &= ~mask;
|
|
clk_writel(val, reg);
|
|
return 0;
|
|
}
|
|
|
|
static struct clk_ops tegra_plld_ops = {
|
|
.init = tegra30_pll_clk_init,
|
|
.enable = tegra30_pll_clk_enable,
|
|
.disable = tegra30_pll_clk_disable,
|
|
.set_rate = tegra30_pll_clk_set_rate,
|
|
.clk_cfg_ex = tegra30_plld_clk_cfg_ex,
|
|
};
|
|
|
|
static void tegra30_plle_clk_init(struct clk *c)
|
|
{
|
|
u32 val;
|
|
|
|
val = clk_readl(PLLE_AUX);
|
|
c->parent = (val & PLLE_AUX_PLLP_SEL) ?
|
|
tegra_get_clock_by_name("pll_p") :
|
|
tegra_get_clock_by_name("pll_ref");
|
|
|
|
val = clk_readl(c->reg + PLL_BASE);
|
|
c->state = (val & PLLE_BASE_ENABLE) ? ON : OFF;
|
|
c->mul = (val & PLLE_BASE_DIVN_MASK) >> PLLE_BASE_DIVN_SHIFT;
|
|
c->div = (val & PLLE_BASE_DIVM_MASK) >> PLLE_BASE_DIVM_SHIFT;
|
|
c->div *= (val & PLLE_BASE_DIVP_MASK) >> PLLE_BASE_DIVP_SHIFT;
|
|
}
|
|
|
|
static void tegra30_plle_clk_disable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
val = clk_readl(c->reg + PLL_BASE);
|
|
val &= ~(PLLE_BASE_CML_ENABLE | PLLE_BASE_ENABLE);
|
|
clk_writel(val, c->reg + PLL_BASE);
|
|
}
|
|
|
|
static void tegra30_plle_training(struct clk *c)
|
|
{
|
|
u32 val;
|
|
|
|
/* PLLE is already disabled, and setup cleared;
|
|
* create falling edge on PLLE IDDQ input */
|
|
val = pmc_readl(PMC_SATA_PWRGT);
|
|
val |= PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
|
|
pmc_writel(val, PMC_SATA_PWRGT);
|
|
|
|
val = pmc_readl(PMC_SATA_PWRGT);
|
|
val |= PMC_SATA_PWRGT_PLLE_IDDQ_SWCTL;
|
|
pmc_writel(val, PMC_SATA_PWRGT);
|
|
|
|
val = pmc_readl(PMC_SATA_PWRGT);
|
|
val &= ~PMC_SATA_PWRGT_PLLE_IDDQ_VALUE;
|
|
pmc_writel(val, PMC_SATA_PWRGT);
|
|
|
|
do {
|
|
val = clk_readl(c->reg + PLL_MISC(c));
|
|
} while (!(val & PLLE_MISC_READY));
|
|
}
|
|
|
|
static int tegra30_plle_configure(struct clk *c, bool force_training)
|
|
{
|
|
u32 val;
|
|
const struct clk_pll_freq_table *sel;
|
|
unsigned long rate = c->u.pll.fixed_rate;
|
|
unsigned long input_rate = clk_get_rate(c->parent);
|
|
|
|
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
|
|
if (sel->input_rate == input_rate && sel->output_rate == rate)
|
|
break;
|
|
}
|
|
|
|
if (sel->input_rate == 0)
|
|
return -ENOSYS;
|
|
|
|
/* disable PLLE, clear setup fiels */
|
|
tegra30_plle_clk_disable(c);
|
|
|
|
val = clk_readl(c->reg + PLL_MISC(c));
|
|
val &= ~(PLLE_MISC_LOCK_ENABLE | PLLE_MISC_SETUP_MASK);
|
|
clk_writel(val, c->reg + PLL_MISC(c));
|
|
|
|
/* training */
|
|
val = clk_readl(c->reg + PLL_MISC(c));
|
|
if (force_training || (!(val & PLLE_MISC_READY)))
|
|
tegra30_plle_training(c);
|
|
|
|
/* configure dividers, setup, disable SS */
|
|
val = clk_readl(c->reg + PLL_BASE);
|
|
val &= ~PLLE_BASE_DIV_MASK;
|
|
val |= PLLE_BASE_DIV(sel->m, sel->n, sel->p, sel->cpcon);
|
|
clk_writel(val, c->reg + PLL_BASE);
|
|
c->mul = sel->n;
|
|
c->div = sel->m * sel->p;
|
|
|
|
val = clk_readl(c->reg + PLL_MISC(c));
|
|
val |= PLLE_MISC_SETUP_VALUE;
|
|
val |= PLLE_MISC_LOCK_ENABLE;
|
|
clk_writel(val, c->reg + PLL_MISC(c));
|
|
|
|
val = clk_readl(PLLE_SS_CTRL);
|
|
val |= PLLE_SS_DISABLE;
|
|
clk_writel(val, PLLE_SS_CTRL);
|
|
|
|
/* enable and lock PLLE*/
|
|
val = clk_readl(c->reg + PLL_BASE);
|
|
val |= (PLLE_BASE_CML_ENABLE | PLLE_BASE_ENABLE);
|
|
clk_writel(val, c->reg + PLL_BASE);
|
|
|
|
tegra30_pll_clk_wait_for_lock(c, c->reg + PLL_MISC(c), PLLE_MISC_LOCK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra30_plle_clk_enable(struct clk *c)
|
|
{
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
return tegra30_plle_configure(c, !c->set);
|
|
}
|
|
|
|
static struct clk_ops tegra_plle_ops = {
|
|
.init = tegra30_plle_clk_init,
|
|
.enable = tegra30_plle_clk_enable,
|
|
.disable = tegra30_plle_clk_disable,
|
|
};
|
|
|
|
/* Clock divider ops */
|
|
static void tegra30_pll_div_clk_init(struct clk *c)
|
|
{
|
|
if (c->flags & DIV_U71) {
|
|
u32 divu71;
|
|
u32 val = clk_readl(c->reg);
|
|
val >>= c->reg_shift;
|
|
c->state = (val & PLL_OUT_CLKEN) ? ON : OFF;
|
|
if (!(val & PLL_OUT_RESET_DISABLE))
|
|
c->state = OFF;
|
|
|
|
divu71 = (val & PLL_OUT_RATIO_MASK) >> PLL_OUT_RATIO_SHIFT;
|
|
c->div = (divu71 + 2);
|
|
c->mul = 2;
|
|
} else if (c->flags & DIV_2) {
|
|
c->state = ON;
|
|
if (c->flags & (PLLD | PLLX)) {
|
|
c->div = 2;
|
|
c->mul = 1;
|
|
} else
|
|
BUG();
|
|
} else {
|
|
c->state = ON;
|
|
c->div = 1;
|
|
c->mul = 1;
|
|
}
|
|
}
|
|
|
|
static int tegra30_pll_div_clk_enable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
u32 new_val;
|
|
|
|
pr_debug("%s: %s\n", __func__, c->name);
|
|
if (c->flags & DIV_U71) {
|
|
val = clk_readl(c->reg);
|
|
new_val = val >> c->reg_shift;
|
|
new_val &= 0xFFFF;
|
|
|
|
new_val |= PLL_OUT_CLKEN | PLL_OUT_RESET_DISABLE;
|
|
|
|
val &= ~(0xFFFF << c->reg_shift);
|
|
val |= new_val << c->reg_shift;
|
|
clk_writel_delay(val, c->reg);
|
|
return 0;
|
|
} else if (c->flags & DIV_2) {
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void tegra30_pll_div_clk_disable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
u32 new_val;
|
|
|
|
pr_debug("%s: %s\n", __func__, c->name);
|
|
if (c->flags & DIV_U71) {
|
|
val = clk_readl(c->reg);
|
|
new_val = val >> c->reg_shift;
|
|
new_val &= 0xFFFF;
|
|
|
|
new_val &= ~(PLL_OUT_CLKEN | PLL_OUT_RESET_DISABLE);
|
|
|
|
val &= ~(0xFFFF << c->reg_shift);
|
|
val |= new_val << c->reg_shift;
|
|
clk_writel_delay(val, c->reg);
|
|
}
|
|
}
|
|
|
|
static int tegra30_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
u32 val;
|
|
u32 new_val;
|
|
int divider_u71;
|
|
unsigned long parent_rate = clk_get_rate(c->parent);
|
|
|
|
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
|
|
if (c->flags & DIV_U71) {
|
|
divider_u71 = clk_div71_get_divider(
|
|
parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
|
|
if (divider_u71 >= 0) {
|
|
val = clk_readl(c->reg);
|
|
new_val = val >> c->reg_shift;
|
|
new_val &= 0xFFFF;
|
|
if (c->flags & DIV_U71_FIXED)
|
|
new_val |= PLL_OUT_OVERRIDE;
|
|
new_val &= ~PLL_OUT_RATIO_MASK;
|
|
new_val |= divider_u71 << PLL_OUT_RATIO_SHIFT;
|
|
|
|
val &= ~(0xFFFF << c->reg_shift);
|
|
val |= new_val << c->reg_shift;
|
|
clk_writel_delay(val, c->reg);
|
|
c->div = divider_u71 + 2;
|
|
c->mul = 2;
|
|
return 0;
|
|
}
|
|
} else if (c->flags & DIV_2)
|
|
return clk_set_rate(c->parent, rate * 2);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static long tegra30_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
int divider;
|
|
unsigned long parent_rate = clk_get_rate(c->parent);
|
|
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
|
|
|
|
if (c->flags & DIV_U71) {
|
|
divider = clk_div71_get_divider(
|
|
parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
|
|
if (divider < 0)
|
|
return divider;
|
|
return DIV_ROUND_UP(parent_rate * 2, divider + 2);
|
|
} else if (c->flags & DIV_2)
|
|
/* no rounding - fixed DIV_2 dividers pass rate to parent PLL */
|
|
return rate;
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_pll_div_ops = {
|
|
.init = tegra30_pll_div_clk_init,
|
|
.enable = tegra30_pll_div_clk_enable,
|
|
.disable = tegra30_pll_div_clk_disable,
|
|
.set_rate = tegra30_pll_div_clk_set_rate,
|
|
.round_rate = tegra30_pll_div_clk_round_rate,
|
|
};
|
|
|
|
/* Periph clk ops */
|
|
static inline u32 periph_clk_source_mask(struct clk *c)
|
|
{
|
|
if (c->flags & MUX8)
|
|
return 7 << 29;
|
|
else if (c->flags & MUX_PWM)
|
|
return 3 << 28;
|
|
else if (c->flags & MUX_CLK_OUT)
|
|
return 3 << (c->u.periph.clk_num + 4);
|
|
else if (c->flags & PLLD)
|
|
return PLLD_BASE_DSIB_MUX_MASK;
|
|
else
|
|
return 3 << 30;
|
|
}
|
|
|
|
static inline u32 periph_clk_source_shift(struct clk *c)
|
|
{
|
|
if (c->flags & MUX8)
|
|
return 29;
|
|
else if (c->flags & MUX_PWM)
|
|
return 28;
|
|
else if (c->flags & MUX_CLK_OUT)
|
|
return c->u.periph.clk_num + 4;
|
|
else if (c->flags & PLLD)
|
|
return PLLD_BASE_DSIB_MUX_SHIFT;
|
|
else
|
|
return 30;
|
|
}
|
|
|
|
static void tegra30_periph_clk_init(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
const struct clk_mux_sel *mux = 0;
|
|
const struct clk_mux_sel *sel;
|
|
if (c->flags & MUX) {
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (((val & periph_clk_source_mask(c)) >>
|
|
periph_clk_source_shift(c)) == sel->value)
|
|
mux = sel;
|
|
}
|
|
BUG_ON(!mux);
|
|
|
|
c->parent = mux->input;
|
|
} else {
|
|
c->parent = c->inputs[0].input;
|
|
}
|
|
|
|
if (c->flags & DIV_U71) {
|
|
u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK;
|
|
if ((c->flags & DIV_U71_UART) &&
|
|
(!(val & PERIPH_CLK_UART_DIV_ENB))) {
|
|
divu71 = 0;
|
|
}
|
|
if (c->flags & DIV_U71_IDLE) {
|
|
val &= ~(PERIPH_CLK_SOURCE_DIVU71_MASK <<
|
|
PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
|
|
val |= (PERIPH_CLK_SOURCE_DIVIDLE_VAL <<
|
|
PERIPH_CLK_SOURCE_DIVIDLE_SHIFT);
|
|
clk_writel(val, c->reg);
|
|
}
|
|
c->div = divu71 + 2;
|
|
c->mul = 2;
|
|
} else if (c->flags & DIV_U16) {
|
|
u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK;
|
|
c->div = divu16 + 1;
|
|
c->mul = 1;
|
|
} else {
|
|
c->div = 1;
|
|
c->mul = 1;
|
|
}
|
|
|
|
c->state = ON;
|
|
if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
|
|
c->state = OFF;
|
|
if (!(c->flags & PERIPH_NO_RESET))
|
|
if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) & PERIPH_CLK_TO_BIT(c))
|
|
c->state = OFF;
|
|
}
|
|
|
|
static int tegra30_periph_clk_enable(struct clk *c)
|
|
{
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
|
|
if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1)
|
|
return 0;
|
|
|
|
clk_writel_delay(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_SET_REG(c));
|
|
if (!(c->flags & PERIPH_NO_RESET) &&
|
|
!(c->flags & PERIPH_MANUAL_RESET)) {
|
|
if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) &
|
|
PERIPH_CLK_TO_BIT(c)) {
|
|
udelay(5); /* reset propagation delay */
|
|
clk_writel(PERIPH_CLK_TO_BIT(c),
|
|
PERIPH_CLK_TO_RST_CLR_REG(c));
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_periph_clk_disable(struct clk *c)
|
|
{
|
|
unsigned long val;
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
if (c->refcnt)
|
|
tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
|
|
|
|
if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0) {
|
|
/* If peripheral is in the APB bus then read the APB bus to
|
|
* flush the write operation in apb bus. This will avoid the
|
|
* peripheral access after disabling clock*/
|
|
if (c->flags & PERIPH_ON_APB)
|
|
val = chipid_readl();
|
|
|
|
clk_writel_delay(
|
|
PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
|
|
}
|
|
}
|
|
|
|
static void tegra30_periph_clk_reset(struct clk *c, bool assert)
|
|
{
|
|
unsigned long val;
|
|
pr_debug("%s %s on clock %s\n", __func__,
|
|
assert ? "assert" : "deassert", c->name);
|
|
|
|
if (!(c->flags & PERIPH_NO_RESET)) {
|
|
if (assert) {
|
|
/* If peripheral is in the APB bus then read the APB
|
|
* bus to flush the write operation in apb bus. This
|
|
* will avoid the peripheral access after disabling
|
|
* clock */
|
|
if (c->flags & PERIPH_ON_APB)
|
|
val = chipid_readl();
|
|
|
|
clk_writel(PERIPH_CLK_TO_BIT(c),
|
|
PERIPH_CLK_TO_RST_SET_REG(c));
|
|
} else
|
|
clk_writel(PERIPH_CLK_TO_BIT(c),
|
|
PERIPH_CLK_TO_RST_CLR_REG(c));
|
|
}
|
|
}
|
|
|
|
static int tegra30_periph_clk_set_parent(struct clk *c, struct clk *p)
|
|
{
|
|
u32 val;
|
|
const struct clk_mux_sel *sel;
|
|
pr_debug("%s: %s %s\n", __func__, c->name, p->name);
|
|
|
|
if (!(c->flags & MUX))
|
|
return (p == c->parent) ? 0 : (-EINVAL);
|
|
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (sel->input == p) {
|
|
val = clk_readl(c->reg);
|
|
val &= ~periph_clk_source_mask(c);
|
|
val |= (sel->value << periph_clk_source_shift(c));
|
|
|
|
if (c->refcnt)
|
|
clk_enable(p);
|
|
|
|
clk_writel_delay(val, c->reg);
|
|
|
|
if (c->refcnt && c->parent)
|
|
clk_disable(c->parent);
|
|
|
|
clk_reparent(c, p);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int tegra30_periph_clk_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
u32 val;
|
|
int divider;
|
|
unsigned long parent_rate = clk_get_rate(c->parent);
|
|
|
|
if (c->flags & DIV_U71) {
|
|
divider = clk_div71_get_divider(
|
|
parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
|
|
if (divider >= 0) {
|
|
val = clk_readl(c->reg);
|
|
val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
|
|
val |= divider;
|
|
if (c->flags & DIV_U71_UART) {
|
|
if (divider)
|
|
val |= PERIPH_CLK_UART_DIV_ENB;
|
|
else
|
|
val &= ~PERIPH_CLK_UART_DIV_ENB;
|
|
}
|
|
clk_writel_delay(val, c->reg);
|
|
c->div = divider + 2;
|
|
c->mul = 2;
|
|
return 0;
|
|
}
|
|
} else if (c->flags & DIV_U16) {
|
|
divider = clk_div16_get_divider(parent_rate, rate);
|
|
if (divider >= 0) {
|
|
val = clk_readl(c->reg);
|
|
val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
|
|
val |= divider;
|
|
clk_writel_delay(val, c->reg);
|
|
c->div = divider + 1;
|
|
c->mul = 1;
|
|
return 0;
|
|
}
|
|
} else if (parent_rate <= rate) {
|
|
c->div = 1;
|
|
c->mul = 1;
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static long tegra30_periph_clk_round_rate(struct clk *c,
|
|
unsigned long rate)
|
|
{
|
|
int divider;
|
|
unsigned long parent_rate = clk_get_rate(c->parent);
|
|
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
|
|
|
|
if (c->flags & DIV_U71) {
|
|
divider = clk_div71_get_divider(
|
|
parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
|
|
if (divider < 0)
|
|
return divider;
|
|
|
|
return DIV_ROUND_UP(parent_rate * 2, divider + 2);
|
|
} else if (c->flags & DIV_U16) {
|
|
divider = clk_div16_get_divider(parent_rate, rate);
|
|
if (divider < 0)
|
|
return divider;
|
|
return DIV_ROUND_UP(parent_rate, divider + 1);
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_periph_clk_ops = {
|
|
.init = &tegra30_periph_clk_init,
|
|
.enable = &tegra30_periph_clk_enable,
|
|
.disable = &tegra30_periph_clk_disable,
|
|
.set_parent = &tegra30_periph_clk_set_parent,
|
|
.set_rate = &tegra30_periph_clk_set_rate,
|
|
.round_rate = &tegra30_periph_clk_round_rate,
|
|
.reset = &tegra30_periph_clk_reset,
|
|
};
|
|
|
|
|
|
/* Periph extended clock configuration ops */
|
|
static int
|
|
tegra30_vi_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
|
|
{
|
|
if (p == TEGRA_CLK_VI_INP_SEL) {
|
|
u32 val = clk_readl(c->reg);
|
|
val &= ~PERIPH_CLK_VI_SEL_EX_MASK;
|
|
val |= (setting << PERIPH_CLK_VI_SEL_EX_SHIFT) &
|
|
PERIPH_CLK_VI_SEL_EX_MASK;
|
|
clk_writel(val, c->reg);
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_vi_clk_ops = {
|
|
.init = &tegra30_periph_clk_init,
|
|
.enable = &tegra30_periph_clk_enable,
|
|
.disable = &tegra30_periph_clk_disable,
|
|
.set_parent = &tegra30_periph_clk_set_parent,
|
|
.set_rate = &tegra30_periph_clk_set_rate,
|
|
.round_rate = &tegra30_periph_clk_round_rate,
|
|
.clk_cfg_ex = &tegra30_vi_clk_cfg_ex,
|
|
.reset = &tegra30_periph_clk_reset,
|
|
};
|
|
|
|
static int
|
|
tegra30_nand_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
|
|
{
|
|
if (p == TEGRA_CLK_NAND_PAD_DIV2_ENB) {
|
|
u32 val = clk_readl(c->reg);
|
|
if (setting)
|
|
val |= PERIPH_CLK_NAND_DIV_EX_ENB;
|
|
else
|
|
val &= ~PERIPH_CLK_NAND_DIV_EX_ENB;
|
|
clk_writel(val, c->reg);
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_nand_clk_ops = {
|
|
.init = &tegra30_periph_clk_init,
|
|
.enable = &tegra30_periph_clk_enable,
|
|
.disable = &tegra30_periph_clk_disable,
|
|
.set_parent = &tegra30_periph_clk_set_parent,
|
|
.set_rate = &tegra30_periph_clk_set_rate,
|
|
.round_rate = &tegra30_periph_clk_round_rate,
|
|
.clk_cfg_ex = &tegra30_nand_clk_cfg_ex,
|
|
.reset = &tegra30_periph_clk_reset,
|
|
};
|
|
|
|
|
|
static int
|
|
tegra30_dtv_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
|
|
{
|
|
if (p == TEGRA_CLK_DTV_INVERT) {
|
|
u32 val = clk_readl(c->reg);
|
|
if (setting)
|
|
val |= PERIPH_CLK_DTV_POLARITY_INV;
|
|
else
|
|
val &= ~PERIPH_CLK_DTV_POLARITY_INV;
|
|
clk_writel(val, c->reg);
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_dtv_clk_ops = {
|
|
.init = &tegra30_periph_clk_init,
|
|
.enable = &tegra30_periph_clk_enable,
|
|
.disable = &tegra30_periph_clk_disable,
|
|
.set_parent = &tegra30_periph_clk_set_parent,
|
|
.set_rate = &tegra30_periph_clk_set_rate,
|
|
.round_rate = &tegra30_periph_clk_round_rate,
|
|
.clk_cfg_ex = &tegra30_dtv_clk_cfg_ex,
|
|
.reset = &tegra30_periph_clk_reset,
|
|
};
|
|
|
|
static int tegra30_dsib_clk_set_parent(struct clk *c, struct clk *p)
|
|
{
|
|
const struct clk_mux_sel *sel;
|
|
struct clk *d = tegra_get_clock_by_name("pll_d");
|
|
|
|
pr_debug("%s: %s %s\n", __func__, c->name, p->name);
|
|
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (sel->input == p) {
|
|
if (c->refcnt)
|
|
clk_enable(p);
|
|
|
|
/* The DSIB parent selection bit is in PLLD base
|
|
register - can not do direct r-m-w, must be
|
|
protected by PLLD lock */
|
|
tegra_clk_cfg_ex(
|
|
d, TEGRA_CLK_PLLD_MIPI_MUX_SEL, sel->value);
|
|
|
|
if (c->refcnt && c->parent)
|
|
clk_disable(c->parent);
|
|
|
|
clk_reparent(c, p);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_dsib_clk_ops = {
|
|
.init = &tegra30_periph_clk_init,
|
|
.enable = &tegra30_periph_clk_enable,
|
|
.disable = &tegra30_periph_clk_disable,
|
|
.set_parent = &tegra30_dsib_clk_set_parent,
|
|
.set_rate = &tegra30_periph_clk_set_rate,
|
|
.round_rate = &tegra30_periph_clk_round_rate,
|
|
.reset = &tegra30_periph_clk_reset,
|
|
};
|
|
|
|
/* pciex clock support only reset function */
|
|
static struct clk_ops tegra_pciex_clk_ops = {
|
|
.reset = tegra30_periph_clk_reset,
|
|
};
|
|
|
|
/* Output clock ops */
|
|
|
|
static DEFINE_SPINLOCK(clk_out_lock);
|
|
|
|
static void tegra30_clk_out_init(struct clk *c)
|
|
{
|
|
const struct clk_mux_sel *mux = 0;
|
|
const struct clk_mux_sel *sel;
|
|
u32 val = pmc_readl(c->reg);
|
|
|
|
c->state = (val & (0x1 << c->u.periph.clk_num)) ? ON : OFF;
|
|
c->mul = 1;
|
|
c->div = 1;
|
|
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (((val & periph_clk_source_mask(c)) >>
|
|
periph_clk_source_shift(c)) == sel->value)
|
|
mux = sel;
|
|
}
|
|
BUG_ON(!mux);
|
|
c->parent = mux->input;
|
|
}
|
|
|
|
static int tegra30_clk_out_enable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
unsigned long flags;
|
|
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
spin_lock_irqsave(&clk_out_lock, flags);
|
|
val = pmc_readl(c->reg);
|
|
val |= (0x1 << c->u.periph.clk_num);
|
|
pmc_writel(val, c->reg);
|
|
spin_unlock_irqrestore(&clk_out_lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_clk_out_disable(struct clk *c)
|
|
{
|
|
u32 val;
|
|
unsigned long flags;
|
|
|
|
pr_debug("%s on clock %s\n", __func__, c->name);
|
|
|
|
spin_lock_irqsave(&clk_out_lock, flags);
|
|
val = pmc_readl(c->reg);
|
|
val &= ~(0x1 << c->u.periph.clk_num);
|
|
pmc_writel(val, c->reg);
|
|
spin_unlock_irqrestore(&clk_out_lock, flags);
|
|
}
|
|
|
|
static int tegra30_clk_out_set_parent(struct clk *c, struct clk *p)
|
|
{
|
|
u32 val;
|
|
unsigned long flags;
|
|
const struct clk_mux_sel *sel;
|
|
|
|
pr_debug("%s: %s %s\n", __func__, c->name, p->name);
|
|
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (sel->input == p) {
|
|
if (c->refcnt)
|
|
clk_enable(p);
|
|
|
|
spin_lock_irqsave(&clk_out_lock, flags);
|
|
val = pmc_readl(c->reg);
|
|
val &= ~periph_clk_source_mask(c);
|
|
val |= (sel->value << periph_clk_source_shift(c));
|
|
pmc_writel(val, c->reg);
|
|
spin_unlock_irqrestore(&clk_out_lock, flags);
|
|
|
|
if (c->refcnt && c->parent)
|
|
clk_disable(c->parent);
|
|
|
|
clk_reparent(c, p);
|
|
return 0;
|
|
}
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_clk_out_ops = {
|
|
.init = &tegra30_clk_out_init,
|
|
.enable = &tegra30_clk_out_enable,
|
|
.disable = &tegra30_clk_out_disable,
|
|
.set_parent = &tegra30_clk_out_set_parent,
|
|
};
|
|
|
|
|
|
/* Clock doubler ops */
|
|
static void tegra30_clk_double_init(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
c->mul = val & (0x1 << c->reg_shift) ? 1 : 2;
|
|
c->div = 1;
|
|
c->state = ON;
|
|
if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
|
|
c->state = OFF;
|
|
};
|
|
|
|
static int tegra30_clk_double_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
u32 val;
|
|
unsigned long parent_rate = clk_get_rate(c->parent);
|
|
if (rate == parent_rate) {
|
|
val = clk_readl(c->reg) | (0x1 << c->reg_shift);
|
|
clk_writel(val, c->reg);
|
|
c->mul = 1;
|
|
c->div = 1;
|
|
return 0;
|
|
} else if (rate == 2 * parent_rate) {
|
|
val = clk_readl(c->reg) & (~(0x1 << c->reg_shift));
|
|
clk_writel(val, c->reg);
|
|
c->mul = 2;
|
|
c->div = 1;
|
|
return 0;
|
|
}
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_clk_double_ops = {
|
|
.init = &tegra30_clk_double_init,
|
|
.enable = &tegra30_periph_clk_enable,
|
|
.disable = &tegra30_periph_clk_disable,
|
|
.set_rate = &tegra30_clk_double_set_rate,
|
|
};
|
|
|
|
/* Audio sync clock ops */
|
|
static int tegra30_sync_source_set_rate(struct clk *c, unsigned long rate)
|
|
{
|
|
c->rate = rate;
|
|
return 0;
|
|
}
|
|
|
|
static struct clk_ops tegra_sync_source_ops = {
|
|
.set_rate = &tegra30_sync_source_set_rate,
|
|
};
|
|
|
|
static void tegra30_audio_sync_clk_init(struct clk *c)
|
|
{
|
|
int source;
|
|
const struct clk_mux_sel *sel;
|
|
u32 val = clk_readl(c->reg);
|
|
c->state = (val & AUDIO_SYNC_DISABLE_BIT) ? OFF : ON;
|
|
source = val & AUDIO_SYNC_SOURCE_MASK;
|
|
for (sel = c->inputs; sel->input != NULL; sel++)
|
|
if (sel->value == source)
|
|
break;
|
|
BUG_ON(sel->input == NULL);
|
|
c->parent = sel->input;
|
|
}
|
|
|
|
static int tegra30_audio_sync_clk_enable(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
clk_writel((val & (~AUDIO_SYNC_DISABLE_BIT)), c->reg);
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_audio_sync_clk_disable(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
clk_writel((val | AUDIO_SYNC_DISABLE_BIT), c->reg);
|
|
}
|
|
|
|
static int tegra30_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
|
|
{
|
|
u32 val;
|
|
const struct clk_mux_sel *sel;
|
|
for (sel = c->inputs; sel->input != NULL; sel++) {
|
|
if (sel->input == p) {
|
|
val = clk_readl(c->reg);
|
|
val &= ~AUDIO_SYNC_SOURCE_MASK;
|
|
val |= sel->value;
|
|
|
|
if (c->refcnt)
|
|
clk_enable(p);
|
|
|
|
clk_writel(val, c->reg);
|
|
|
|
if (c->refcnt && c->parent)
|
|
clk_disable(c->parent);
|
|
|
|
clk_reparent(c, p);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct clk_ops tegra_audio_sync_clk_ops = {
|
|
.init = tegra30_audio_sync_clk_init,
|
|
.enable = tegra30_audio_sync_clk_enable,
|
|
.disable = tegra30_audio_sync_clk_disable,
|
|
.set_parent = tegra30_audio_sync_clk_set_parent,
|
|
};
|
|
|
|
/* cml0 (pcie), and cml1 (sata) clock ops */
|
|
static void tegra30_cml_clk_init(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
c->state = val & (0x1 << c->u.periph.clk_num) ? ON : OFF;
|
|
}
|
|
|
|
static int tegra30_cml_clk_enable(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
val |= (0x1 << c->u.periph.clk_num);
|
|
clk_writel(val, c->reg);
|
|
return 0;
|
|
}
|
|
|
|
static void tegra30_cml_clk_disable(struct clk *c)
|
|
{
|
|
u32 val = clk_readl(c->reg);
|
|
val &= ~(0x1 << c->u.periph.clk_num);
|
|
clk_writel(val, c->reg);
|
|
}
|
|
|
|
static struct clk_ops tegra_cml_clk_ops = {
|
|
.init = &tegra30_cml_clk_init,
|
|
.enable = &tegra30_cml_clk_enable,
|
|
.disable = &tegra30_cml_clk_disable,
|
|
};
|
|
|
|
/* Clock definitions */
|
|
static struct clk tegra_clk_32k = {
|
|
.name = "clk_32k",
|
|
.rate = 32768,
|
|
.ops = NULL,
|
|
.max_rate = 32768,
|
|
};
|
|
|
|
static struct clk tegra_clk_m = {
|
|
.name = "clk_m",
|
|
.flags = ENABLE_ON_INIT,
|
|
.ops = &tegra_clk_m_ops,
|
|
.reg = 0x1fc,
|
|
.reg_shift = 28,
|
|
.max_rate = 48000000,
|
|
};
|
|
|
|
static struct clk tegra_clk_m_div2 = {
|
|
.name = "clk_m_div2",
|
|
.ops = &tegra_clk_m_div_ops,
|
|
.parent = &tegra_clk_m,
|
|
.mul = 1,
|
|
.div = 2,
|
|
.state = ON,
|
|
.max_rate = 24000000,
|
|
};
|
|
|
|
static struct clk tegra_clk_m_div4 = {
|
|
.name = "clk_m_div4",
|
|
.ops = &tegra_clk_m_div_ops,
|
|
.parent = &tegra_clk_m,
|
|
.mul = 1,
|
|
.div = 4,
|
|
.state = ON,
|
|
.max_rate = 12000000,
|
|
};
|
|
|
|
static struct clk tegra_pll_ref = {
|
|
.name = "pll_ref",
|
|
.flags = ENABLE_ON_INIT,
|
|
.ops = &tegra_pll_ref_ops,
|
|
.parent = &tegra_clk_m,
|
|
.max_rate = 26000000,
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
|
|
{ 12000000, 1040000000, 520, 6, 1, 8},
|
|
{ 13000000, 1040000000, 480, 6, 1, 8},
|
|
{ 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */
|
|
{ 19200000, 1040000000, 325, 6, 1, 6},
|
|
{ 26000000, 1040000000, 520, 13, 1, 8},
|
|
|
|
{ 12000000, 832000000, 416, 6, 1, 8},
|
|
{ 13000000, 832000000, 832, 13, 1, 8},
|
|
{ 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */
|
|
{ 19200000, 832000000, 260, 6, 1, 8},
|
|
{ 26000000, 832000000, 416, 13, 1, 8},
|
|
|
|
{ 12000000, 624000000, 624, 12, 1, 8},
|
|
{ 13000000, 624000000, 624, 13, 1, 8},
|
|
{ 16800000, 600000000, 520, 14, 1, 8},
|
|
{ 19200000, 624000000, 520, 16, 1, 8},
|
|
{ 26000000, 624000000, 624, 26, 1, 8},
|
|
|
|
{ 12000000, 600000000, 600, 12, 1, 8},
|
|
{ 13000000, 600000000, 600, 13, 1, 8},
|
|
{ 16800000, 600000000, 500, 14, 1, 8},
|
|
{ 19200000, 600000000, 375, 12, 1, 6},
|
|
{ 26000000, 600000000, 600, 26, 1, 8},
|
|
|
|
{ 12000000, 520000000, 520, 12, 1, 8},
|
|
{ 13000000, 520000000, 520, 13, 1, 8},
|
|
{ 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */
|
|
{ 19200000, 520000000, 325, 12, 1, 6},
|
|
{ 26000000, 520000000, 520, 26, 1, 8},
|
|
|
|
{ 12000000, 416000000, 416, 12, 1, 8},
|
|
{ 13000000, 416000000, 416, 13, 1, 8},
|
|
{ 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */
|
|
{ 19200000, 416000000, 260, 12, 1, 6},
|
|
{ 26000000, 416000000, 416, 26, 1, 8},
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_c = {
|
|
.name = "pll_c",
|
|
.flags = PLL_HAS_CPCON,
|
|
.ops = &tegra_pll_ops,
|
|
.reg = 0x80,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 1400000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 31000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 20000000,
|
|
.vco_max = 1400000000,
|
|
.freq_table = tegra_pll_c_freq_table,
|
|
.lock_delay = 300,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_c_out1 = {
|
|
.name = "pll_c_out1",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = DIV_U71,
|
|
.parent = &tegra_pll_c,
|
|
.reg = 0x84,
|
|
.reg_shift = 0,
|
|
.max_rate = 700000000,
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_m_freq_table[] = {
|
|
{ 12000000, 666000000, 666, 12, 1, 8},
|
|
{ 13000000, 666000000, 666, 13, 1, 8},
|
|
{ 16800000, 666000000, 555, 14, 1, 8},
|
|
{ 19200000, 666000000, 555, 16, 1, 8},
|
|
{ 26000000, 666000000, 666, 26, 1, 8},
|
|
{ 12000000, 600000000, 600, 12, 1, 8},
|
|
{ 13000000, 600000000, 600, 13, 1, 8},
|
|
{ 16800000, 600000000, 500, 14, 1, 8},
|
|
{ 19200000, 600000000, 375, 12, 1, 6},
|
|
{ 26000000, 600000000, 600, 26, 1, 8},
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_m = {
|
|
.name = "pll_m",
|
|
.flags = PLL_HAS_CPCON | PLLM,
|
|
.ops = &tegra_pll_ops,
|
|
.reg = 0x90,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 800000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 31000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 20000000,
|
|
.vco_max = 1200000000,
|
|
.freq_table = tegra_pll_m_freq_table,
|
|
.lock_delay = 300,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_m_out1 = {
|
|
.name = "pll_m_out1",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = DIV_U71,
|
|
.parent = &tegra_pll_m,
|
|
.reg = 0x94,
|
|
.reg_shift = 0,
|
|
.max_rate = 600000000,
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_p_freq_table[] = {
|
|
{ 12000000, 216000000, 432, 12, 2, 8},
|
|
{ 13000000, 216000000, 432, 13, 2, 8},
|
|
{ 16800000, 216000000, 360, 14, 2, 8},
|
|
{ 19200000, 216000000, 360, 16, 2, 8},
|
|
{ 26000000, 216000000, 432, 26, 2, 8},
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_p = {
|
|
.name = "pll_p",
|
|
.flags = ENABLE_ON_INIT | PLL_FIXED | PLL_HAS_CPCON,
|
|
.ops = &tegra_pll_ops,
|
|
.reg = 0xa0,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 432000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 31000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 20000000,
|
|
.vco_max = 1400000000,
|
|
.freq_table = tegra_pll_p_freq_table,
|
|
.lock_delay = 300,
|
|
.fixed_rate = 408000000,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_p_out1 = {
|
|
.name = "pll_p_out1",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
|
|
.parent = &tegra_pll_p,
|
|
.reg = 0xa4,
|
|
.reg_shift = 0,
|
|
.max_rate = 432000000,
|
|
};
|
|
|
|
static struct clk tegra_pll_p_out2 = {
|
|
.name = "pll_p_out2",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
|
|
.parent = &tegra_pll_p,
|
|
.reg = 0xa4,
|
|
.reg_shift = 16,
|
|
.max_rate = 432000000,
|
|
};
|
|
|
|
static struct clk tegra_pll_p_out3 = {
|
|
.name = "pll_p_out3",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
|
|
.parent = &tegra_pll_p,
|
|
.reg = 0xa8,
|
|
.reg_shift = 0,
|
|
.max_rate = 432000000,
|
|
};
|
|
|
|
static struct clk tegra_pll_p_out4 = {
|
|
.name = "pll_p_out4",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
|
|
.parent = &tegra_pll_p,
|
|
.reg = 0xa8,
|
|
.reg_shift = 16,
|
|
.max_rate = 432000000,
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_a_freq_table[] = {
|
|
{ 9600000, 564480000, 294, 5, 1, 4},
|
|
{ 9600000, 552960000, 288, 5, 1, 4},
|
|
{ 9600000, 24000000, 5, 2, 1, 1},
|
|
|
|
{ 28800000, 56448000, 49, 25, 1, 1},
|
|
{ 28800000, 73728000, 64, 25, 1, 1},
|
|
{ 28800000, 24000000, 5, 6, 1, 1},
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_a = {
|
|
.name = "pll_a",
|
|
.flags = PLL_HAS_CPCON,
|
|
.ops = &tegra_pll_ops,
|
|
.reg = 0xb0,
|
|
.parent = &tegra_pll_p_out1,
|
|
.max_rate = 700000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 31000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 20000000,
|
|
.vco_max = 1400000000,
|
|
.freq_table = tegra_pll_a_freq_table,
|
|
.lock_delay = 300,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_a_out0 = {
|
|
.name = "pll_a_out0",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = DIV_U71,
|
|
.parent = &tegra_pll_a,
|
|
.reg = 0xb4,
|
|
.reg_shift = 0,
|
|
.max_rate = 100000000,
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
|
|
{ 12000000, 216000000, 216, 12, 1, 4},
|
|
{ 13000000, 216000000, 216, 13, 1, 4},
|
|
{ 16800000, 216000000, 180, 14, 1, 4},
|
|
{ 19200000, 216000000, 180, 16, 1, 4},
|
|
{ 26000000, 216000000, 216, 26, 1, 4},
|
|
|
|
{ 12000000, 594000000, 594, 12, 1, 8},
|
|
{ 13000000, 594000000, 594, 13, 1, 8},
|
|
{ 16800000, 594000000, 495, 14, 1, 8},
|
|
{ 19200000, 594000000, 495, 16, 1, 8},
|
|
{ 26000000, 594000000, 594, 26, 1, 8},
|
|
|
|
{ 12000000, 1000000000, 1000, 12, 1, 12},
|
|
{ 13000000, 1000000000, 1000, 13, 1, 12},
|
|
{ 19200000, 1000000000, 625, 12, 1, 8},
|
|
{ 26000000, 1000000000, 1000, 26, 1, 12},
|
|
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_d = {
|
|
.name = "pll_d",
|
|
.flags = PLL_HAS_CPCON | PLLD,
|
|
.ops = &tegra_plld_ops,
|
|
.reg = 0xd0,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 1000000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 40000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 40000000,
|
|
.vco_max = 1000000000,
|
|
.freq_table = tegra_pll_d_freq_table,
|
|
.lock_delay = 1000,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_d_out0 = {
|
|
.name = "pll_d_out0",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = DIV_2 | PLLD,
|
|
.parent = &tegra_pll_d,
|
|
.max_rate = 500000000,
|
|
};
|
|
|
|
static struct clk tegra_pll_d2 = {
|
|
.name = "pll_d2",
|
|
.flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG | PLLD,
|
|
.ops = &tegra_plld_ops,
|
|
.reg = 0x4b8,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 1000000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 40000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 40000000,
|
|
.vco_max = 1000000000,
|
|
.freq_table = tegra_pll_d_freq_table,
|
|
.lock_delay = 1000,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_d2_out0 = {
|
|
.name = "pll_d2_out0",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = DIV_2 | PLLD,
|
|
.parent = &tegra_pll_d2,
|
|
.max_rate = 500000000,
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_u_freq_table[] = {
|
|
{ 12000000, 480000000, 960, 12, 2, 12},
|
|
{ 13000000, 480000000, 960, 13, 2, 12},
|
|
{ 16800000, 480000000, 400, 7, 2, 5},
|
|
{ 19200000, 480000000, 200, 4, 2, 3},
|
|
{ 26000000, 480000000, 960, 26, 2, 12},
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_u = {
|
|
.name = "pll_u",
|
|
.flags = PLL_HAS_CPCON | PLLU,
|
|
.ops = &tegra_pll_ops,
|
|
.reg = 0xc0,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 480000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 40000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 480000000,
|
|
.vco_max = 960000000,
|
|
.freq_table = tegra_pll_u_freq_table,
|
|
.lock_delay = 1000,
|
|
},
|
|
};
|
|
|
|
static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
|
|
/* 1.7 GHz */
|
|
{ 12000000, 1700000000, 850, 6, 1, 8},
|
|
{ 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */
|
|
{ 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */
|
|
{ 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */
|
|
{ 26000000, 1700000000, 850, 13, 1, 8},
|
|
|
|
/* 1.6 GHz */
|
|
{ 12000000, 1600000000, 800, 6, 1, 8},
|
|
{ 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */
|
|
{ 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */
|
|
{ 19200000, 1600000000, 500, 6, 1, 8},
|
|
{ 26000000, 1600000000, 800, 13, 1, 8},
|
|
|
|
/* 1.5 GHz */
|
|
{ 12000000, 1500000000, 750, 6, 1, 8},
|
|
{ 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */
|
|
{ 16800000, 1500000000, 625, 7, 1, 8},
|
|
{ 19200000, 1500000000, 625, 8, 1, 8},
|
|
{ 26000000, 1500000000, 750, 13, 1, 8},
|
|
|
|
/* 1.4 GHz */
|
|
{ 12000000, 1400000000, 700, 6, 1, 8},
|
|
{ 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */
|
|
{ 16800000, 1400000000, 1000, 12, 1, 8},
|
|
{ 19200000, 1400000000, 875, 12, 1, 8},
|
|
{ 26000000, 1400000000, 700, 13, 1, 8},
|
|
|
|
/* 1.3 GHz */
|
|
{ 12000000, 1300000000, 975, 9, 1, 8},
|
|
{ 13000000, 1300000000, 1000, 10, 1, 8},
|
|
{ 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */
|
|
{ 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */
|
|
{ 26000000, 1300000000, 650, 13, 1, 8},
|
|
|
|
/* 1.2 GHz */
|
|
{ 12000000, 1200000000, 1000, 10, 1, 8},
|
|
{ 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */
|
|
{ 16800000, 1200000000, 1000, 14, 1, 8},
|
|
{ 19200000, 1200000000, 1000, 16, 1, 8},
|
|
{ 26000000, 1200000000, 600, 13, 1, 8},
|
|
|
|
/* 1.1 GHz */
|
|
{ 12000000, 1100000000, 825, 9, 1, 8},
|
|
{ 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */
|
|
{ 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */
|
|
{ 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */
|
|
{ 26000000, 1100000000, 550, 13, 1, 8},
|
|
|
|
/* 1 GHz */
|
|
{ 12000000, 1000000000, 1000, 12, 1, 8},
|
|
{ 13000000, 1000000000, 1000, 13, 1, 8},
|
|
{ 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */
|
|
{ 19200000, 1000000000, 625, 12, 1, 8},
|
|
{ 26000000, 1000000000, 1000, 26, 1, 8},
|
|
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_x = {
|
|
.name = "pll_x",
|
|
.flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG | PLLX,
|
|
.ops = &tegra_pll_ops,
|
|
.reg = 0xe0,
|
|
.parent = &tegra_pll_ref,
|
|
.max_rate = 1700000000,
|
|
.u.pll = {
|
|
.input_min = 2000000,
|
|
.input_max = 31000000,
|
|
.cf_min = 1000000,
|
|
.cf_max = 6000000,
|
|
.vco_min = 20000000,
|
|
.vco_max = 1700000000,
|
|
.freq_table = tegra_pll_x_freq_table,
|
|
.lock_delay = 300,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pll_x_out0 = {
|
|
.name = "pll_x_out0",
|
|
.ops = &tegra_pll_div_ops,
|
|
.flags = DIV_2 | PLLX,
|
|
.parent = &tegra_pll_x,
|
|
.max_rate = 850000000,
|
|
};
|
|
|
|
|
|
static struct clk_pll_freq_table tegra_pll_e_freq_table[] = {
|
|
/* PLLE special case: use cpcon field to store cml divider value */
|
|
{ 12000000, 100000000, 150, 1, 18, 11},
|
|
{ 216000000, 100000000, 200, 18, 24, 13},
|
|
{ 0, 0, 0, 0, 0, 0 },
|
|
};
|
|
|
|
static struct clk tegra_pll_e = {
|
|
.name = "pll_e",
|
|
.flags = PLL_ALT_MISC_REG,
|
|
.ops = &tegra_plle_ops,
|
|
.reg = 0xe8,
|
|
.max_rate = 100000000,
|
|
.u.pll = {
|
|
.input_min = 12000000,
|
|
.input_max = 216000000,
|
|
.cf_min = 12000000,
|
|
.cf_max = 12000000,
|
|
.vco_min = 1200000000,
|
|
.vco_max = 2400000000U,
|
|
.freq_table = tegra_pll_e_freq_table,
|
|
.lock_delay = 300,
|
|
.fixed_rate = 100000000,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_cml0_clk = {
|
|
.name = "cml0",
|
|
.parent = &tegra_pll_e,
|
|
.ops = &tegra_cml_clk_ops,
|
|
.reg = PLLE_AUX,
|
|
.max_rate = 100000000,
|
|
.u.periph = {
|
|
.clk_num = 0,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_cml1_clk = {
|
|
.name = "cml1",
|
|
.parent = &tegra_pll_e,
|
|
.ops = &tegra_cml_clk_ops,
|
|
.reg = PLLE_AUX,
|
|
.max_rate = 100000000,
|
|
.u.periph = {
|
|
.clk_num = 1,
|
|
},
|
|
};
|
|
|
|
static struct clk tegra_pciex_clk = {
|
|
.name = "pciex",
|
|
.parent = &tegra_pll_e,
|
|
.ops = &tegra_pciex_clk_ops,
|
|
.max_rate = 100000000,
|
|
.u.periph = {
|
|
.clk_num = 74,
|
|
},
|
|
};
|
|
|
|
/* Audio sync clocks */
|
|
#define SYNC_SOURCE(_id) \
|
|
{ \
|
|
.name = #_id "_sync", \
|
|
.rate = 24000000, \
|
|
.max_rate = 24000000, \
|
|
.ops = &tegra_sync_source_ops \
|
|
}
|
|
static struct clk tegra_sync_source_list[] = {
|
|
SYNC_SOURCE(spdif_in),
|
|
SYNC_SOURCE(i2s0),
|
|
SYNC_SOURCE(i2s1),
|
|
SYNC_SOURCE(i2s2),
|
|
SYNC_SOURCE(i2s3),
|
|
SYNC_SOURCE(i2s4),
|
|
SYNC_SOURCE(vimclk),
|
|
};
|
|
|
|
static struct clk_mux_sel mux_audio_sync_clk[] = {
|
|
{ .input = &tegra_sync_source_list[0], .value = 0},
|
|
{ .input = &tegra_sync_source_list[1], .value = 1},
|
|
{ .input = &tegra_sync_source_list[2], .value = 2},
|
|
{ .input = &tegra_sync_source_list[3], .value = 3},
|
|
{ .input = &tegra_sync_source_list[4], .value = 4},
|
|
{ .input = &tegra_sync_source_list[5], .value = 5},
|
|
{ .input = &tegra_pll_a_out0, .value = 6},
|
|
{ .input = &tegra_sync_source_list[6], .value = 7},
|
|
{ 0, 0 }
|
|
};
|
|
|
|
#define AUDIO_SYNC_CLK(_id, _index) \
|
|
{ \
|
|
.name = #_id, \
|
|
.inputs = mux_audio_sync_clk, \
|
|
.reg = 0x4A0 + (_index) * 4, \
|
|
.max_rate = 24000000, \
|
|
.ops = &tegra_audio_sync_clk_ops \
|
|
}
|
|
static struct clk tegra_clk_audio_list[] = {
|
|
AUDIO_SYNC_CLK(audio0, 0),
|
|
AUDIO_SYNC_CLK(audio1, 1),
|
|
AUDIO_SYNC_CLK(audio2, 2),
|
|
AUDIO_SYNC_CLK(audio3, 3),
|
|
AUDIO_SYNC_CLK(audio4, 4),
|
|
AUDIO_SYNC_CLK(audio, 5), /* SPDIF */
|
|
};
|
|
|
|
#define AUDIO_SYNC_2X_CLK(_id, _index) \
|
|
{ \
|
|
.name = #_id "_2x", \
|
|
.flags = PERIPH_NO_RESET, \
|
|
.max_rate = 48000000, \
|
|
.ops = &tegra_clk_double_ops, \
|
|
.reg = 0x49C, \
|
|
.reg_shift = 24 + (_index), \
|
|
.parent = &tegra_clk_audio_list[(_index)], \
|
|
.u.periph = { \
|
|
.clk_num = 113 + (_index), \
|
|
}, \
|
|
}
|
|
static struct clk tegra_clk_audio_2x_list[] = {
|
|
AUDIO_SYNC_2X_CLK(audio0, 0),
|
|
AUDIO_SYNC_2X_CLK(audio1, 1),
|
|
AUDIO_SYNC_2X_CLK(audio2, 2),
|
|
AUDIO_SYNC_2X_CLK(audio3, 3),
|
|
AUDIO_SYNC_2X_CLK(audio4, 4),
|
|
AUDIO_SYNC_2X_CLK(audio, 5), /* SPDIF */
|
|
};
|
|
|
|
#define MUX_I2S_SPDIF(_id, _index) \
|
|
static struct clk_mux_sel mux_pllaout0_##_id##_2x_pllp_clkm[] = { \
|
|
{.input = &tegra_pll_a_out0, .value = 0}, \
|
|
{.input = &tegra_clk_audio_2x_list[(_index)], .value = 1}, \
|
|
{.input = &tegra_pll_p, .value = 2}, \
|
|
{.input = &tegra_clk_m, .value = 3}, \
|
|
{ 0, 0}, \
|
|
}
|
|
MUX_I2S_SPDIF(audio0, 0);
|
|
MUX_I2S_SPDIF(audio1, 1);
|
|
MUX_I2S_SPDIF(audio2, 2);
|
|
MUX_I2S_SPDIF(audio3, 3);
|
|
MUX_I2S_SPDIF(audio4, 4);
|
|
MUX_I2S_SPDIF(audio, 5); /* SPDIF */
|
|
|
|
/* External clock outputs (through PMC) */
|
|
#define MUX_EXTERN_OUT(_id) \
|
|
static struct clk_mux_sel mux_clkm_clkm2_clkm4_extern##_id[] = { \
|
|
{.input = &tegra_clk_m, .value = 0}, \
|
|
{.input = &tegra_clk_m_div2, .value = 1}, \
|
|
{.input = &tegra_clk_m_div4, .value = 2}, \
|
|
{.input = NULL, .value = 3}, /* placeholder */ \
|
|
{ 0, 0}, \
|
|
}
|
|
MUX_EXTERN_OUT(1);
|
|
MUX_EXTERN_OUT(2);
|
|
MUX_EXTERN_OUT(3);
|
|
|
|
static struct clk_mux_sel *mux_extern_out_list[] = {
|
|
mux_clkm_clkm2_clkm4_extern1,
|
|
mux_clkm_clkm2_clkm4_extern2,
|
|
mux_clkm_clkm2_clkm4_extern3,
|
|
};
|
|
|
|
#define CLK_OUT_CLK(_id) \
|
|
{ \
|
|
.name = "clk_out_" #_id, \
|
|
.lookup = { \
|
|
.dev_id = "clk_out_" #_id, \
|
|
.con_id = "extern" #_id, \
|
|
}, \
|
|
.ops = &tegra_clk_out_ops, \
|
|
.reg = 0x1a8, \
|
|
.inputs = mux_clkm_clkm2_clkm4_extern##_id, \
|
|
.flags = MUX_CLK_OUT, \
|
|
.max_rate = 216000000, \
|
|
.u.periph = { \
|
|
.clk_num = (_id - 1) * 8 + 2, \
|
|
}, \
|
|
}
|
|
static struct clk tegra_clk_out_list[] = {
|
|
CLK_OUT_CLK(1),
|
|
CLK_OUT_CLK(2),
|
|
CLK_OUT_CLK(3),
|
|
};
|
|
|
|
/* called after peripheral external clocks are initialized */
|
|
static void init_clk_out_mux(void)
|
|
{
|
|
int i;
|
|
struct clk *c;
|
|
|
|
/* output clock con_id is the name of peripheral
|
|
external clock connected to input 3 of the output mux */
|
|
for (i = 0; i < ARRAY_SIZE(tegra_clk_out_list); i++) {
|
|
c = tegra_get_clock_by_name(
|
|
tegra_clk_out_list[i].lookup.con_id);
|
|
if (!c)
|
|
pr_err("%s: could not find clk %s\n", __func__,
|
|
tegra_clk_out_list[i].lookup.con_id);
|
|
mux_extern_out_list[i][3].input = c;
|
|
}
|
|
}
|
|
|
|
/* Peripheral muxes */
|
|
static struct clk_mux_sel mux_sclk[] = {
|
|
{ .input = &tegra_clk_m, .value = 0},
|
|
{ .input = &tegra_pll_c_out1, .value = 1},
|
|
{ .input = &tegra_pll_p_out4, .value = 2},
|
|
{ .input = &tegra_pll_p_out3, .value = 3},
|
|
{ .input = &tegra_pll_p_out2, .value = 4},
|
|
/* { .input = &tegra_clk_d, .value = 5}, - no use on tegra30 */
|
|
{ .input = &tegra_clk_32k, .value = 6},
|
|
{ .input = &tegra_pll_m_out1, .value = 7},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk tegra_clk_sclk = {
|
|
.name = "sclk",
|
|
.inputs = mux_sclk,
|
|
.reg = 0x28,
|
|
.ops = &tegra_super_ops,
|
|
.max_rate = 334000000,
|
|
.min_rate = 40000000,
|
|
};
|
|
|
|
static struct clk tegra_clk_blink = {
|
|
.name = "blink",
|
|
.parent = &tegra_clk_32k,
|
|
.reg = 0x40,
|
|
.ops = &tegra_blink_clk_ops,
|
|
.max_rate = 32768,
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
|
|
{ .input = &tegra_pll_m, .value = 0},
|
|
{ .input = &tegra_pll_c, .value = 1},
|
|
{ .input = &tegra_pll_p, .value = 2},
|
|
{ .input = &tegra_pll_a_out0, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_pllc_pllm_clkm[] = {
|
|
{ .input = &tegra_pll_p, .value = 0},
|
|
{ .input = &tegra_pll_c, .value = 1},
|
|
{ .input = &tegra_pll_m, .value = 2},
|
|
{ .input = &tegra_clk_m, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_clkm[] = {
|
|
{ .input = &tegra_pll_p, .value = 0},
|
|
{ .input = &tegra_clk_m, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_plld_pllc_clkm[] = {
|
|
{.input = &tegra_pll_p, .value = 0},
|
|
{.input = &tegra_pll_d_out0, .value = 1},
|
|
{.input = &tegra_pll_c, .value = 2},
|
|
{.input = &tegra_clk_m, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_pllm_plld_plla_pllc_plld2_clkm[] = {
|
|
{.input = &tegra_pll_p, .value = 0},
|
|
{.input = &tegra_pll_m, .value = 1},
|
|
{.input = &tegra_pll_d_out0, .value = 2},
|
|
{.input = &tegra_pll_a_out0, .value = 3},
|
|
{.input = &tegra_pll_c, .value = 4},
|
|
{.input = &tegra_pll_d2_out0, .value = 5},
|
|
{.input = &tegra_clk_m, .value = 6},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_plla_pllc_pllp_clkm[] = {
|
|
{ .input = &tegra_pll_a_out0, .value = 0},
|
|
/* { .input = &tegra_pll_c, .value = 1}, no use on tegra30 */
|
|
{ .input = &tegra_pll_p, .value = 2},
|
|
{ .input = &tegra_clk_m, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_pllc_clk32_clkm[] = {
|
|
{.input = &tegra_pll_p, .value = 0},
|
|
{.input = &tegra_pll_c, .value = 1},
|
|
{.input = &tegra_clk_32k, .value = 2},
|
|
{.input = &tegra_clk_m, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_pllc_clkm_clk32[] = {
|
|
{.input = &tegra_pll_p, .value = 0},
|
|
{.input = &tegra_pll_c, .value = 1},
|
|
{.input = &tegra_clk_m, .value = 2},
|
|
{.input = &tegra_clk_32k, .value = 3},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_pllc_pllm[] = {
|
|
{.input = &tegra_pll_p, .value = 0},
|
|
{.input = &tegra_pll_c, .value = 1},
|
|
{.input = &tegra_pll_m, .value = 2},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_clk_m[] = {
|
|
{ .input = &tegra_clk_m, .value = 0},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_pllp_out3[] = {
|
|
{ .input = &tegra_pll_p_out3, .value = 0},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_plld_out0[] = {
|
|
{ .input = &tegra_pll_d_out0, .value = 0},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_plld_out0_plld2_out0[] = {
|
|
{ .input = &tegra_pll_d_out0, .value = 0},
|
|
{ .input = &tegra_pll_d2_out0, .value = 1},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_clk_32k[] = {
|
|
{ .input = &tegra_clk_32k, .value = 0},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_plla_clk32_pllp_clkm_plle[] = {
|
|
{ .input = &tegra_pll_a_out0, .value = 0},
|
|
{ .input = &tegra_clk_32k, .value = 1},
|
|
{ .input = &tegra_pll_p, .value = 2},
|
|
{ .input = &tegra_clk_m, .value = 3},
|
|
{ .input = &tegra_pll_e, .value = 4},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk_mux_sel mux_cclk_g[] = {
|
|
{ .input = &tegra_clk_m, .value = 0},
|
|
{ .input = &tegra_pll_c, .value = 1},
|
|
{ .input = &tegra_clk_32k, .value = 2},
|
|
{ .input = &tegra_pll_m, .value = 3},
|
|
{ .input = &tegra_pll_p, .value = 4},
|
|
{ .input = &tegra_pll_p_out4, .value = 5},
|
|
{ .input = &tegra_pll_p_out3, .value = 6},
|
|
{ .input = &tegra_pll_x, .value = 8},
|
|
{ 0, 0},
|
|
};
|
|
|
|
static struct clk tegra_clk_cclk_g = {
|
|
.name = "cclk_g",
|
|
.flags = DIV_U71 | DIV_U71_INT,
|
|
.inputs = mux_cclk_g,
|
|
.reg = 0x368,
|
|
.ops = &tegra_super_ops,
|
|
.max_rate = 1700000000,
|
|
};
|
|
|
|
static struct clk tegra30_clk_twd = {
|
|
.parent = &tegra_clk_cclk_g,
|
|
.name = "twd",
|
|
.ops = &tegra30_twd_ops,
|
|
.max_rate = 1400000000, /* Same as tegra_clk_cpu_cmplx.max_rate */
|
|
.mul = 1,
|
|
.div = 2,
|
|
};
|
|
|
|
#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
|
|
{ \
|
|
.name = _name, \
|
|
.lookup = { \
|
|
.dev_id = _dev, \
|
|
.con_id = _con, \
|
|
}, \
|
|
.ops = &tegra_periph_clk_ops, \
|
|
.reg = _reg, \
|
|
.inputs = _inputs, \
|
|
.flags = _flags, \
|
|
.max_rate = _max, \
|
|
.u.periph = { \
|
|
.clk_num = _clk_num, \
|
|
}, \
|
|
}
|
|
|
|
#define PERIPH_CLK_EX(_name, _dev, _con, _clk_num, _reg, _max, _inputs, \
|
|
_flags, _ops) \
|
|
{ \
|
|
.name = _name, \
|
|
.lookup = { \
|
|
.dev_id = _dev, \
|
|
.con_id = _con, \
|
|
}, \
|
|
.ops = _ops, \
|
|
.reg = _reg, \
|
|
.inputs = _inputs, \
|
|
.flags = _flags, \
|
|
.max_rate = _max, \
|
|
.u.periph = { \
|
|
.clk_num = _clk_num, \
|
|
}, \
|
|
}
|
|
|
|
#define SHARED_CLK(_name, _dev, _con, _parent, _id, _div, _mode)\
|
|
{ \
|
|
.name = _name, \
|
|
.lookup = { \
|
|
.dev_id = _dev, \
|
|
.con_id = _con, \
|
|
}, \
|
|
.ops = &tegra_clk_shared_bus_ops, \
|
|
.parent = _parent, \
|
|
.u.shared_bus_user = { \
|
|
.client_id = _id, \
|
|
.client_div = _div, \
|
|
.mode = _mode, \
|
|
}, \
|
|
}
|
|
struct clk tegra_list_clks[] = {
|
|
PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 26000000, mux_clk_m, 0),
|
|
PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET | PERIPH_ON_APB),
|
|
PERIPH_CLK("kbc", "tegra-kbc", NULL, 36, 0, 32768, mux_clk_32k, PERIPH_NO_RESET | PERIPH_ON_APB),
|
|
PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
|
|
PERIPH_CLK("kfuse", "kfuse-tegra", NULL, 40, 0, 26000000, mux_clk_m, 0),
|
|
PERIPH_CLK("fuse", "fuse-tegra", "fuse", 39, 0, 26000000, mux_clk_m, PERIPH_ON_APB),
|
|
PERIPH_CLK("fuse_burn", "fuse-tegra", "fuse_burn", 39, 0, 26000000, mux_clk_m, PERIPH_ON_APB),
|
|
PERIPH_CLK("apbif", "tegra30-ahub", "apbif", 107, 0, 26000000, mux_clk_m, 0),
|
|
PERIPH_CLK("i2s0", "tegra30-i2s.0", NULL, 30, 0x1d8, 26000000, mux_pllaout0_audio0_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2s1", "tegra30-i2s.1", NULL, 11, 0x100, 26000000, mux_pllaout0_audio1_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2s2", "tegra30-i2s.2", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2s3", "tegra30-i2s.3", NULL, 101, 0x3bc, 26000000, mux_pllaout0_audio3_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2s4", "tegra30-i2s.4", NULL, 102, 0x3c0, 26000000, mux_pllaout0_audio4_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("spdif_out", "tegra30-spdif", "spdif_out", 10, 0x108, 100000000, mux_pllaout0_audio_2x_pllp_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("spdif_in", "tegra30-spdif", "spdif_in", 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_clk32_clkm, MUX | MUX_PWM | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("d_audio", "tegra30-ahub", "d_audio", 106, 0x3d0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("dam0", "tegra30-dam.0", NULL, 108, 0x3d8, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("dam1", "tegra30-dam.1", NULL, 109, 0x3dc, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("dam2", "tegra30-dam.2", NULL, 110, 0x3e0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("hda", "tegra30-hda", "hda", 125, 0x428, 108000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("hda2codec_2x", "tegra30-hda", "hda2codec", 111, 0x3e4, 48000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("hda2hdmi", "tegra30-hda", "hda2hdmi", 128, 0, 48000000, mux_clk_m, 0),
|
|
PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sbc5", "spi_tegra.4", NULL, 104, 0x3c8, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sbc6", "spi_tegra.5", NULL, 105, 0x3cc, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sata_oob", "tegra_sata_oob", NULL, 123, 0x420, 216000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("sata", "tegra_sata", NULL, 124, 0x424, 216000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("sata_cold", "tegra_sata_cold", NULL, 129, 0, 48000000, mux_clk_m, 0),
|
|
PERIPH_CLK_EX("ndflash", "tegra_nand", NULL, 13, 0x160, 240000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71, &tegra_nand_clk_ops),
|
|
PERIPH_CLK("ndspeed", "tegra_nand_speed", NULL, 80, 0x3f8, 240000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 208000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
|
|
PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 104000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
|
|
PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 208000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
|
|
PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x164, 104000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
|
|
PERIPH_CLK("vcp", "tegra-avp", "vcp", 29, 0, 250000000, mux_clk_m, 0),
|
|
PERIPH_CLK("bsea", "tegra-avp", "bsea", 62, 0, 250000000, mux_clk_m, 0),
|
|
PERIPH_CLK("bsev", "tegra-aes", "bsev", 63, 0, 250000000, mux_clk_m, 0),
|
|
PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, 520000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_INT),
|
|
PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */
|
|
PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, 127000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
|
|
PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB), /* scales with voltage */
|
|
PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2c4", "tegra-i2c.3", NULL, 103, 0x3c4, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
|
|
PERIPH_CLK("i2c5", "tegra-i2c.4", NULL, 47, 0x128, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB),
|
|
PERIPH_CLK("uarta", "tegra_uart.0", NULL, 6, 0x178, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uartb", "tegra_uart.1", NULL, 7, 0x17c, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uartc", "tegra_uart.2", NULL, 55, 0x1a0, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uartd", "tegra_uart.3", NULL, 65, 0x1c0, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uarte", "tegra_uart.4", NULL, 66, 0x1c4, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uarta_dbg", "serial8250.0", "uarta", 6, 0x178, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uartb_dbg", "serial8250.0", "uartb", 7, 0x17c, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uartc_dbg", "serial8250.0", "uartc", 55, 0x1a0, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uartd_dbg", "serial8250.0", "uartd", 65, 0x1c0, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK("uarte_dbg", "serial8250.0", "uarte", 66, 0x1c4, 800000000, mux_pllp_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB),
|
|
PERIPH_CLK_EX("vi", "tegra_camera", "vi", 20, 0x148, 425000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT, &tegra_vi_clk_ops),
|
|
PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT | DIV_U71_IDLE | PERIPH_MANUAL_RESET),
|
|
PERIPH_CLK("3d2", "3d2", NULL, 98, 0x3b0, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT | DIV_U71_IDLE | PERIPH_MANUAL_RESET),
|
|
PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT | DIV_U71_IDLE),
|
|
PERIPH_CLK("vi_sensor", "tegra_camera", "vi_sensor", 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET),
|
|
PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT),
|
|
PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 520000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT),
|
|
PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 260000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | DIV_U71_INT),
|
|
PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
|
|
PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
|
|
PERIPH_CLK_EX("dtv", "dtv", NULL, 79, 0x1dc, 250000000, mux_clk_m, 0, &tegra_dtv_clk_ops),
|
|
PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 148500000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8 | DIV_U71),
|
|
PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 220000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
|
|
PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 600000000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8),
|
|
PERIPH_CLK("disp2", "tegradc.1", NULL, 26, 0x13c, 600000000, mux_pllp_pllm_plld_plla_pllc_plld2_clkm, MUX | MUX8),
|
|
PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
|
|
PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
|
|
PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
|
|
PERIPH_CLK("dsia", "tegradc.0", "dsia", 48, 0, 500000000, mux_plld_out0, 0),
|
|
PERIPH_CLK_EX("dsib", "tegradc.1", "dsib", 82, 0xd0, 500000000, mux_plld_out0_plld2_out0, MUX | PLLD, &tegra_dsib_clk_ops),
|
|
PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 102000000, mux_pllp_out3, 0),
|
|
PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
|
|
PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET),
|
|
|
|
PERIPH_CLK("tsensor", "tegra-tsensor", NULL, 100, 0x3b8, 216000000, mux_pllp_pllc_clkm_clk32, MUX | DIV_U71),
|
|
PERIPH_CLK("actmon", "actmon", NULL, 119, 0x3e8, 216000000, mux_pllp_pllc_clk32_clkm, MUX | DIV_U71),
|
|
PERIPH_CLK("extern1", "extern1", NULL, 120, 0x3ec, 216000000, mux_plla_clk32_pllp_clkm_plle, MUX | MUX8 | DIV_U71),
|
|
PERIPH_CLK("extern2", "extern2", NULL, 121, 0x3f0, 216000000, mux_plla_clk32_pllp_clkm_plle, MUX | MUX8 | DIV_U71),
|
|
PERIPH_CLK("extern3", "extern3", NULL, 122, 0x3f4, 216000000, mux_plla_clk32_pllp_clkm_plle, MUX | MUX8 | DIV_U71),
|
|
PERIPH_CLK("i2cslow", "i2cslow", NULL, 81, 0x3fc, 26000000, mux_pllp_pllc_clk32_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
|
|
PERIPH_CLK("pcie", "tegra-pcie", "pcie", 70, 0, 250000000, mux_clk_m, 0),
|
|
PERIPH_CLK("afi", "tegra-pcie", "afi", 72, 0, 250000000, mux_clk_m, 0),
|
|
PERIPH_CLK("se", "se", NULL, 127, 0x42c, 520000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_INT),
|
|
};
|
|
|
|
#define CLK_DUPLICATE(_name, _dev, _con) \
|
|
{ \
|
|
.name = _name, \
|
|
.lookup = { \
|
|
.dev_id = _dev, \
|
|
.con_id = _con, \
|
|
}, \
|
|
}
|
|
|
|
/* Some clocks may be used by different drivers depending on the board
|
|
* configuration. List those here to register them twice in the clock lookup
|
|
* table under two names.
|
|
*/
|
|
struct clk_duplicate tegra_clk_duplicates[] = {
|
|
CLK_DUPLICATE("usbd", "utmip-pad", NULL),
|
|
CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
|
|
CLK_DUPLICATE("usbd", "tegra-otg", NULL),
|
|
CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
|
|
CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
|
|
CLK_DUPLICATE("dsib", "tegradc.0", "dsib"),
|
|
CLK_DUPLICATE("dsia", "tegradc.1", "dsia"),
|
|
CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
|
|
CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
|
|
CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
|
|
CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
|
|
CLK_DUPLICATE("bsev", "tegra-avp", "bsev"),
|
|
CLK_DUPLICATE("bsev", "nvavp", "bsev"),
|
|
CLK_DUPLICATE("vde", "tegra-aes", "vde"),
|
|
CLK_DUPLICATE("bsea", "tegra-aes", "bsea"),
|
|
CLK_DUPLICATE("bsea", "nvavp", "bsea"),
|
|
CLK_DUPLICATE("cml1", "tegra_sata_cml", NULL),
|
|
CLK_DUPLICATE("cml0", "tegra_pcie", "cml"),
|
|
CLK_DUPLICATE("pciex", "tegra_pcie", "pciex"),
|
|
CLK_DUPLICATE("i2c1", "tegra-i2c-slave.0", NULL),
|
|
CLK_DUPLICATE("i2c2", "tegra-i2c-slave.1", NULL),
|
|
CLK_DUPLICATE("i2c3", "tegra-i2c-slave.2", NULL),
|
|
CLK_DUPLICATE("i2c4", "tegra-i2c-slave.3", NULL),
|
|
CLK_DUPLICATE("i2c5", "tegra-i2c-slave.4", NULL),
|
|
CLK_DUPLICATE("sbc1", "spi_slave_tegra.0", NULL),
|
|
CLK_DUPLICATE("sbc2", "spi_slave_tegra.1", NULL),
|
|
CLK_DUPLICATE("sbc3", "spi_slave_tegra.2", NULL),
|
|
CLK_DUPLICATE("sbc4", "spi_slave_tegra.3", NULL),
|
|
CLK_DUPLICATE("sbc5", "spi_slave_tegra.4", NULL),
|
|
CLK_DUPLICATE("sbc6", "spi_slave_tegra.5", NULL),
|
|
CLK_DUPLICATE("twd", "smp_twd", NULL),
|
|
CLK_DUPLICATE("vcp", "nvavp", "vcp"),
|
|
};
|
|
|
|
struct clk *tegra_ptr_clks[] = {
|
|
&tegra_clk_32k,
|
|
&tegra_clk_m,
|
|
&tegra_clk_m_div2,
|
|
&tegra_clk_m_div4,
|
|
&tegra_pll_ref,
|
|
&tegra_pll_m,
|
|
&tegra_pll_m_out1,
|
|
&tegra_pll_c,
|
|
&tegra_pll_c_out1,
|
|
&tegra_pll_p,
|
|
&tegra_pll_p_out1,
|
|
&tegra_pll_p_out2,
|
|
&tegra_pll_p_out3,
|
|
&tegra_pll_p_out4,
|
|
&tegra_pll_a,
|
|
&tegra_pll_a_out0,
|
|
&tegra_pll_d,
|
|
&tegra_pll_d_out0,
|
|
&tegra_pll_d2,
|
|
&tegra_pll_d2_out0,
|
|
&tegra_pll_u,
|
|
&tegra_pll_x,
|
|
&tegra_pll_x_out0,
|
|
&tegra_pll_e,
|
|
&tegra_clk_cclk_g,
|
|
&tegra_cml0_clk,
|
|
&tegra_cml1_clk,
|
|
&tegra_pciex_clk,
|
|
&tegra_clk_sclk,
|
|
&tegra_clk_blink,
|
|
&tegra30_clk_twd,
|
|
};
|
|
|
|
|
|
static void tegra30_init_one_clock(struct clk *c)
|
|
{
|
|
clk_init(c);
|
|
INIT_LIST_HEAD(&c->shared_bus_list);
|
|
if (!c->lookup.dev_id && !c->lookup.con_id)
|
|
c->lookup.con_id = c->name;
|
|
c->lookup.clk = c;
|
|
clkdev_add(&c->lookup);
|
|
}
|
|
|
|
void __init tegra30_init_clocks(void)
|
|
{
|
|
int i;
|
|
struct clk *c;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
|
|
tegra30_init_one_clock(tegra_ptr_clks[i]);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
|
|
tegra30_init_one_clock(&tegra_list_clks[i]);
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
|
|
c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
|
|
if (!c) {
|
|
pr_err("%s: Unknown duplicate clock %s\n", __func__,
|
|
tegra_clk_duplicates[i].name);
|
|
continue;
|
|
}
|
|
|
|
tegra_clk_duplicates[i].lookup.clk = c;
|
|
clkdev_add(&tegra_clk_duplicates[i].lookup);
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SIZE(tegra_sync_source_list); i++)
|
|
tegra30_init_one_clock(&tegra_sync_source_list[i]);
|
|
for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_list); i++)
|
|
tegra30_init_one_clock(&tegra_clk_audio_list[i]);
|
|
for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_2x_list); i++)
|
|
tegra30_init_one_clock(&tegra_clk_audio_2x_list[i]);
|
|
|
|
init_clk_out_mux();
|
|
for (i = 0; i < ARRAY_SIZE(tegra_clk_out_list); i++)
|
|
tegra30_init_one_clock(&tegra_clk_out_list[i]);
|
|
|
|
}
|