Commit 79ba3fda authored by Mike Turquette's avatar Mike Turquette

Merge branch 'clk-tegra-next' of...

Merge branch 'clk-tegra-next' of git://nv-tegra.nvidia.com/user/pdeschrijver/linux into clk-next-tegra
parents 1d9438f7 62ce7cd6
NVIDIA Tegra124 Clock And Reset Controller
This binding uses the common clock binding:
Documentation/devicetree/bindings/clock/clock-bindings.txt
The CAR (Clock And Reset) Controller on Tegra is the HW module responsible
for muxing and gating Tegra's clocks, and setting their rates.
Required properties :
- compatible : Should be "nvidia,tegra124-car"
- reg : Should contain CAR registers location and length
- clocks : Should contain phandle and clock specifiers for two clocks:
the 32 KHz "32k_in", and the board-specific oscillator "osc".
- #clock-cells : Should be 1.
In clock consumers, this cell represents the clock ID exposed by the
CAR. The assignments may be found in header file
<dt-bindings/clock/tegra124-car.h>.
Example SoC include file:
/ {
tegra_car: clock {
compatible = "nvidia,tegra124-car";
reg = <0x60006000 0x1000>;
#clock-cells = <1>;
};
usb@c5004000 {
clocks = <&tegra_car TEGRA124_CLK_USB2>;
};
};
Example board file:
/ {
clocks {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
osc: clock@0 {
compatible = "fixed-clock";
reg = <0>;
#clock-cells = <0>;
clock-frequency = <112400000>;
};
clk_32k: clock@1 {
compatible = "fixed-clock";
reg = <1>;
#clock-cells = <0>;
clock-frequency = <32768>;
};
};
&tegra_car {
clocks = <&clk_32k> <&osc>;
};
};
......@@ -6,7 +6,12 @@ obj-y += clk-periph-gate.o
obj-y += clk-pll.o
obj-y += clk-pll-out.o
obj-y += clk-super.o
obj-y += clk-tegra-audio.o
obj-y += clk-tegra-periph.o
obj-y += clk-tegra-pmc.o
obj-y += clk-tegra-fixed.o
obj-y += clk-tegra-super-gen4.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o
obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o
/*
* This header provides IDs for clocks common between several Tegra SoCs
*/
#ifndef _TEGRA_CLK_ID_H
#define _TEGRA_CLK_ID_H
enum clk_id {
tegra_clk_actmon,
tegra_clk_adx,
tegra_clk_adx1,
tegra_clk_afi,
tegra_clk_amx,
tegra_clk_amx1,
tegra_clk_apbdma,
tegra_clk_apbif,
tegra_clk_audio0,
tegra_clk_audio0_2x,
tegra_clk_audio0_mux,
tegra_clk_audio1,
tegra_clk_audio1_2x,
tegra_clk_audio1_mux,
tegra_clk_audio2,
tegra_clk_audio2_2x,
tegra_clk_audio2_mux,
tegra_clk_audio3,
tegra_clk_audio3_2x,
tegra_clk_audio3_mux,
tegra_clk_audio4,
tegra_clk_audio4_2x,
tegra_clk_audio4_mux,
tegra_clk_blink,
tegra_clk_bsea,
tegra_clk_bsev,
tegra_clk_cclk_g,
tegra_clk_cclk_lp,
tegra_clk_cilab,
tegra_clk_cilcd,
tegra_clk_cile,
tegra_clk_clk_32k,
tegra_clk_clk72Mhz,
tegra_clk_clk_m,
tegra_clk_clk_m_div2,
tegra_clk_clk_m_div4,
tegra_clk_clk_out_1,
tegra_clk_clk_out_1_mux,
tegra_clk_clk_out_2,
tegra_clk_clk_out_2_mux,
tegra_clk_clk_out_3,
tegra_clk_clk_out_3_mux,
tegra_clk_cml0,
tegra_clk_cml1,
tegra_clk_csi,
tegra_clk_csite,
tegra_clk_csus,
tegra_clk_cve,
tegra_clk_dam0,
tegra_clk_dam1,
tegra_clk_dam2,
tegra_clk_d_audio,
tegra_clk_dds,
tegra_clk_dfll_ref,
tegra_clk_dfll_soc,
tegra_clk_disp1,
tegra_clk_disp2,
tegra_clk_dp2,
tegra_clk_dpaux,
tegra_clk_dsia,
tegra_clk_dsialp,
tegra_clk_dsia_mux,
tegra_clk_dsib,
tegra_clk_dsiblp,
tegra_clk_dsib_mux,
tegra_clk_dtv,
tegra_clk_emc,
tegra_clk_entropy,
tegra_clk_epp,
tegra_clk_epp_8,
tegra_clk_extern1,
tegra_clk_extern2,
tegra_clk_extern3,
tegra_clk_fuse,
tegra_clk_fuse_burn,
tegra_clk_gpu,
tegra_clk_gr2d,
tegra_clk_gr2d_8,
tegra_clk_gr3d,
tegra_clk_gr3d_8,
tegra_clk_hclk,
tegra_clk_hda,
tegra_clk_hda2codec_2x,
tegra_clk_hda2hdmi,
tegra_clk_hdmi,
tegra_clk_hdmi_audio,
tegra_clk_host1x,
tegra_clk_host1x_8,
tegra_clk_i2c1,
tegra_clk_i2c2,
tegra_clk_i2c3,
tegra_clk_i2c4,
tegra_clk_i2c5,
tegra_clk_i2c6,
tegra_clk_i2cslow,
tegra_clk_i2s0,
tegra_clk_i2s0_sync,
tegra_clk_i2s1,
tegra_clk_i2s1_sync,
tegra_clk_i2s2,
tegra_clk_i2s2_sync,
tegra_clk_i2s3,
tegra_clk_i2s3_sync,
tegra_clk_i2s4,
tegra_clk_i2s4_sync,
tegra_clk_isp,
tegra_clk_isp_8,
tegra_clk_ispb,
tegra_clk_kbc,
tegra_clk_kfuse,
tegra_clk_la,
tegra_clk_mipi,
tegra_clk_mipi_cal,
tegra_clk_mpe,
tegra_clk_mselect,
tegra_clk_msenc,
tegra_clk_ndflash,
tegra_clk_ndflash_8,
tegra_clk_ndspeed,
tegra_clk_ndspeed_8,
tegra_clk_nor,
tegra_clk_owr,
tegra_clk_pcie,
tegra_clk_pclk,
tegra_clk_pll_a,
tegra_clk_pll_a_out0,
tegra_clk_pll_c,
tegra_clk_pll_c2,
tegra_clk_pll_c3,
tegra_clk_pll_c4,
tegra_clk_pll_c_out1,
tegra_clk_pll_d,
tegra_clk_pll_d2,
tegra_clk_pll_d2_out0,
tegra_clk_pll_d_out0,
tegra_clk_pll_dp,
tegra_clk_pll_e_out0,
tegra_clk_pll_m,
tegra_clk_pll_m_out1,
tegra_clk_pll_p,
tegra_clk_pll_p_out1,
tegra_clk_pll_p_out2,
tegra_clk_pll_p_out2_int,
tegra_clk_pll_p_out3,
tegra_clk_pll_p_out4,
tegra_clk_pll_p_out5,
tegra_clk_pll_ref,
tegra_clk_pll_re_out,
tegra_clk_pll_re_vco,
tegra_clk_pll_u,
tegra_clk_pll_u_12m,
tegra_clk_pll_u_480m,
tegra_clk_pll_u_48m,
tegra_clk_pll_u_60m,
tegra_clk_pll_x,
tegra_clk_pll_x_out0,
tegra_clk_pwm,
tegra_clk_rtc,
tegra_clk_sata,
tegra_clk_sata_cold,
tegra_clk_sata_oob,
tegra_clk_sbc1,
tegra_clk_sbc1_8,
tegra_clk_sbc2,
tegra_clk_sbc2_8,
tegra_clk_sbc3,
tegra_clk_sbc3_8,
tegra_clk_sbc4,
tegra_clk_sbc4_8,
tegra_clk_sbc5,
tegra_clk_sbc5_8,
tegra_clk_sbc6,
tegra_clk_sbc6_8,
tegra_clk_sclk,
tegra_clk_sdmmc1,
tegra_clk_sdmmc2,
tegra_clk_sdmmc3,
tegra_clk_sdmmc4,
tegra_clk_se,
tegra_clk_soc_therm,
tegra_clk_sor0,
tegra_clk_sor0_lvds,
tegra_clk_spdif,
tegra_clk_spdif_2x,
tegra_clk_spdif_in,
tegra_clk_spdif_in_sync,
tegra_clk_spdif_mux,
tegra_clk_spdif_out,
tegra_clk_timer,
tegra_clk_trace,
tegra_clk_tsec,
tegra_clk_tsensor,
tegra_clk_tvdac,
tegra_clk_tvo,
tegra_clk_uarta,
tegra_clk_uartb,
tegra_clk_uartc,
tegra_clk_uartd,
tegra_clk_uarte,
tegra_clk_usb2,
tegra_clk_usb3,
tegra_clk_usbd,
tegra_clk_vcp,
tegra_clk_vde,
tegra_clk_vde_8,
tegra_clk_vfir,
tegra_clk_vi,
tegra_clk_vi_8,
tegra_clk_vi_9,
tegra_clk_vic03,
tegra_clk_vim2_clk,
tegra_clk_vimclk_sync,
tegra_clk_vi_sensor,
tegra_clk_vi_sensor2,
tegra_clk_vi_sensor_8,
tegra_clk_xusb_dev,
tegra_clk_xusb_dev_src,
tegra_clk_xusb_falcon_src,
tegra_clk_xusb_fs_src,
tegra_clk_xusb_host,
tegra_clk_xusb_host_src,
tegra_clk_xusb_hs_src,
tegra_clk_xusb_ss,
tegra_clk_xusb_ss_src,
tegra_clk_max,
};
#endif /* _TEGRA_CLK_ID_H */
......@@ -151,12 +151,16 @@ const struct clk_ops tegra_clk_periph_gate_ops = {
struct clk *tegra_clk_register_periph_gate(const char *name,
const char *parent_name, u8 gate_flags, void __iomem *clk_base,
unsigned long flags, int clk_num,
struct tegra_clk_periph_regs *pregs, int *enable_refcnt)
unsigned long flags, int clk_num, int *enable_refcnt)
{
struct tegra_clk_periph_gate *gate;
struct clk *clk;
struct clk_init_data init;
struct tegra_clk_periph_regs *pregs;
pregs = get_reg_bank(clk_num);
if (!pregs)
return ERR_PTR(-EINVAL);
gate = kzalloc(sizeof(*gate), GFP_KERNEL);
if (!gate) {
......
......@@ -170,27 +170,50 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = {
.disable = clk_periph_disable,
};
const struct clk_ops tegra_clk_periph_no_gate_ops = {
.get_parent = clk_periph_get_parent,
.set_parent = clk_periph_set_parent,
.recalc_rate = clk_periph_recalc_rate,
.round_rate = clk_periph_round_rate,
.set_rate = clk_periph_set_rate,
};
static struct clk *_tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph,
void __iomem *clk_base, u32 offset, bool div,
void __iomem *clk_base, u32 offset,
unsigned long flags)
{
struct clk *clk;
struct clk_init_data init;
struct tegra_clk_periph_regs *bank;
bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
flags |= CLK_SET_RATE_PARENT;
init.ops = &tegra_clk_periph_nodiv_ops;
} else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
init.ops = &tegra_clk_periph_no_gate_ops;
else
init.ops = &tegra_clk_periph_ops;
init.name = name;
init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
bank = get_reg_bank(periph->gate.clk_num);
if (!bank)
return ERR_PTR(-EINVAL);
/* Data in .init is copied by clk_register(), so stack variable OK */
periph->hw.init = &init;
periph->magic = TEGRA_CLK_PERIPH_MAGIC;
periph->mux.reg = clk_base + offset;
periph->divider.reg = div ? (clk_base + offset) : NULL;
periph->gate.clk_base = clk_base;
periph->gate.regs = bank;
periph->gate.enable_refcnt = periph_clk_enb_refcnt;
clk = clk_register(NULL, &periph->hw);
if (IS_ERR(clk))
......@@ -209,7 +232,7 @@ struct clk *tegra_clk_register_periph(const char *name,
u32 offset, unsigned long flags)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, true, flags);
periph, clk_base, offset, flags);
}
struct clk *tegra_clk_register_periph_nodiv(const char *name,
......@@ -217,6 +240,7 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset)
{
periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, false, CLK_SET_RATE_PARENT);
periph, clk_base, offset, CLK_SET_RATE_PARENT);
}
This diff is collapsed.
/*
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clk/tegra.h>
#include "clk.h"
#include "clk-id.h"
#define AUDIO_SYNC_CLK_I2S0 0x4a0
#define AUDIO_SYNC_CLK_I2S1 0x4a4
#define AUDIO_SYNC_CLK_I2S2 0x4a8
#define AUDIO_SYNC_CLK_I2S3 0x4ac
#define AUDIO_SYNC_CLK_I2S4 0x4b0
#define AUDIO_SYNC_CLK_SPDIF 0x4b4
#define AUDIO_SYNC_DOUBLER 0x49c
#define PLLA_OUT 0xb4
struct tegra_sync_source_initdata {
char *name;
unsigned long rate;
unsigned long max_rate;
int clk_id;
};
#define SYNC(_name) \
{\
.name = #_name,\
.rate = 24000000,\
.max_rate = 24000000,\
.clk_id = tegra_clk_ ## _name,\
}
struct tegra_audio_clk_initdata {
char *gate_name;
char *mux_name;
u32 offset;
int gate_clk_id;
int mux_clk_id;
};
#define AUDIO(_name, _offset) \
{\
.gate_name = #_name,\
.mux_name = #_name"_mux",\
.offset = _offset,\
.gate_clk_id = tegra_clk_ ## _name,\
.mux_clk_id = tegra_clk_ ## _name ## _mux,\
}
struct tegra_audio2x_clk_initdata {
char *parent;
char *gate_name;
char *name_2x;
char *div_name;
int clk_id;
int clk_num;
u8 div_offset;
};
#define AUDIO2X(_name, _num, _offset) \
{\
.parent = #_name,\
.gate_name = #_name"_2x",\
.name_2x = #_name"_doubler",\
.div_name = #_name"_div",\
.clk_id = tegra_clk_ ## _name ## _2x,\
.clk_num = _num,\
.div_offset = _offset,\
}
static DEFINE_SPINLOCK(clk_doubler_lock);
static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync",
"i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
};
static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
SYNC(spdif_in_sync),
SYNC(i2s0_sync),
SYNC(i2s1_sync),
SYNC(i2s2_sync),
SYNC(i2s3_sync),
SYNC(i2s4_sync),
SYNC(vimclk_sync),
};
static struct tegra_audio_clk_initdata audio_clks[] = {
AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
};
static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
AUDIO2X(audio0, 113, 24),
AUDIO2X(audio1, 114, 25),
AUDIO2X(audio2, 115, 26),
AUDIO2X(audio3, 116, 27),
AUDIO2X(audio4, 117, 28),
AUDIO2X(spdif, 118, 29),
};
void __init tegra_audio_clk_init(void __iomem *clk_base,
void __iomem *pmc_base, struct tegra_clk *tegra_clks,
struct tegra_clk_pll_params *pll_a_params)
{
struct clk *clk;
struct clk **dt_clk;
int i;
/* PLLA */
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a, tegra_clks);
if (dt_clk) {
clk = tegra_clk_register_pll("pll_a", "pll_p_out1", clk_base,
pmc_base, 0, pll_a_params, NULL);
*dt_clk = clk;
}
/* PLLA_OUT0 */
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
if (dt_clk) {
clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
8, 8, 1, NULL);
clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
CLK_SET_RATE_PARENT, 0, NULL);
*dt_clk = clk;
}
for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
struct tegra_sync_source_initdata *data;
data = &sync_source_clks[i];
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
if (!dt_clk)
continue;
clk = tegra_clk_register_sync_source(data->name,
data->rate, data->max_rate);
*dt_clk = clk;
}
for (i = 0; i < ARRAY_SIZE(audio_clks); i++) {
struct tegra_audio_clk_initdata *data;
data = &audio_clks[i];
dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
if (!dt_clk)
continue;
clk = clk_register_mux(NULL, data->mux_name, mux_audio_sync_clk,
ARRAY_SIZE(mux_audio_sync_clk),
CLK_SET_RATE_NO_REPARENT,
clk_base + data->offset, 0, 3, 0,
NULL);
*dt_clk = clk;
dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
if (!dt_clk)
continue;
clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
0, clk_base + data->offset, 4,
CLK_GATE_SET_TO_DISABLE, NULL);
*dt_clk = clk;
}
for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
struct tegra_audio2x_clk_initdata *data;
data = &audio2x_clks[i];
dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
if (!dt_clk)
continue;
clk = clk_register_fixed_factor(NULL, data->name_2x,
data->parent, CLK_SET_RATE_PARENT, 2, 1);
clk = tegra_clk_register_divider(data->div_name,
data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
0, 0, data->div_offset, 1, 0,
&clk_doubler_lock);
clk = tegra_clk_register_periph_gate(data->gate_name,
data->div_name, TEGRA_PERIPH_NO_RESET,
clk_base, CLK_SET_RATE_PARENT, data->clk_num,
periph_clk_enb_refcnt);
*dt_clk = clk;
}
}
/*
* Copyright (c) 2012, 2013, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>.
*/
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/clk/tegra.h>
#include "clk.h"
#include "clk-id.h"
#define OSC_CTRL 0x50
#define OSC_CTRL_OSC_FREQ_SHIFT 28
#define OSC_CTRL_PLL_REF_DIV_SHIFT 26
int __init tegra_osc_clk_init(void __iomem *clk_base,
struct tegra_clk *tegra_clks,
unsigned long *input_freqs, int num,
unsigned long *osc_freq,
unsigned long *pll_ref_freq)
{
struct clk *clk;
struct clk **dt_clk;
u32 val, pll_ref_div;
unsigned osc_idx;
val = readl_relaxed(clk_base + OSC_CTRL);
osc_idx = val >> OSC_CTRL_OSC_FREQ_SHIFT;
if (osc_idx < num)
*osc_freq = input_freqs[osc_idx];
else
*osc_freq = 0;
if (!*osc_freq) {
WARN_ON(1);
return -EINVAL;
}
dt_clk = tegra_lookup_dt_id(tegra_clk_clk_m, tegra_clks);
if (!dt_clk)
return 0;
clk = clk_register_fixed_rate(NULL, "clk_m", NULL, CLK_IS_ROOT,
*osc_freq);
*dt_clk = clk;
/* pll_ref */
val = (val >> OSC_CTRL_PLL_REF_DIV_SHIFT) & 3;
pll_ref_div = 1 << val;
dt_clk = tegra_lookup_dt_id(tegra_clk_pll_ref, tegra_clks);
if (!dt_clk)
return 0;
clk = clk_register_fixed_factor(NULL, "pll_ref", "clk_m",