.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. |
---|
3 | | - * |
---|
4 | | - * This program is free software; you can redistribute it and/or modify it |
---|
5 | | - * under the terms and conditions of the GNU General Public License, |
---|
6 | | - * version 2, as published by the Free Software Foundation. |
---|
7 | | - * |
---|
8 | | - * This program is distributed in the hope it will be useful, but WITHOUT |
---|
9 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
---|
10 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
---|
11 | | - * more details. |
---|
12 | | - * |
---|
13 | | - * You should have received a copy of the GNU General Public License |
---|
14 | | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
15 | 4 | */ |
---|
16 | 5 | |
---|
17 | 6 | #include <linux/io.h> |
---|
.. | .. |
---|
140 | 129 | |
---|
141 | 130 | static void __iomem *clk_base; |
---|
142 | 131 | static void __iomem *pmc_base; |
---|
143 | | - |
---|
144 | | -static DEFINE_SPINLOCK(emc_lock); |
---|
145 | 132 | |
---|
146 | 133 | #define TEGRA_INIT_DATA_MUX(_name, _parents, _offset, \ |
---|
147 | 134 | _clk_num, _gate_flags, _clk_id) \ |
---|
.. | .. |
---|
404 | 391 | .lock_delay = 300, |
---|
405 | 392 | .freq_table = pll_x_freq_table, |
---|
406 | 393 | .flags = TEGRA_PLL_HAS_CPCON | TEGRA_PLL_HAS_LOCK_ENABLE, |
---|
| 394 | + .pre_rate_change = tegra_cclk_pre_pllx_rate_change, |
---|
| 395 | + .post_rate_change = tegra_cclk_post_pllx_rate_change, |
---|
407 | 396 | }; |
---|
408 | 397 | |
---|
409 | 398 | static struct tegra_clk_pll_params pll_e_params = { |
---|
.. | .. |
---|
471 | 460 | { .con_id = "cdev1", .dt_id = TEGRA20_CLK_CDEV1 }, |
---|
472 | 461 | { .con_id = "cdev2", .dt_id = TEGRA20_CLK_CDEV2 }, |
---|
473 | 462 | { .con_id = "clk_32k", .dt_id = TEGRA20_CLK_CLK_32K }, |
---|
474 | | - { .con_id = "blink", .dt_id = TEGRA20_CLK_BLINK }, |
---|
475 | 463 | { .con_id = "clk_m", .dt_id = TEGRA20_CLK_CLK_M }, |
---|
476 | 464 | { .con_id = "pll_ref", .dt_id = TEGRA20_CLK_PLL_REF }, |
---|
477 | 465 | { .dev_id = "tegra20-i2s.0", .dt_id = TEGRA20_CLK_I2S1 }, |
---|
.. | .. |
---|
550 | 538 | [tegra_clk_csi] = { .dt_id = TEGRA20_CLK_CSI, .present = true }, |
---|
551 | 539 | [tegra_clk_isp] = { .dt_id = TEGRA20_CLK_ISP, .present = true }, |
---|
552 | 540 | [tegra_clk_clk_32k] = { .dt_id = TEGRA20_CLK_CLK_32K, .present = true }, |
---|
553 | | - [tegra_clk_blink] = { .dt_id = TEGRA20_CLK_BLINK, .present = true }, |
---|
554 | 541 | [tegra_clk_hclk] = { .dt_id = TEGRA20_CLK_HCLK, .present = true }, |
---|
555 | 542 | [tegra_clk_pclk] = { .dt_id = TEGRA20_CLK_PCLK, .present = true }, |
---|
556 | 543 | [tegra_clk_pll_p_out1] = { .dt_id = TEGRA20_CLK_PLL_P_OUT1, .present = true }, |
---|
.. | .. |
---|
717 | 704 | struct clk *clk; |
---|
718 | 705 | |
---|
719 | 706 | /* CCLK */ |
---|
720 | | - clk = tegra_clk_register_super_mux("cclk", cclk_parents, |
---|
| 707 | + clk = tegra_clk_register_super_cclk("cclk", cclk_parents, |
---|
721 | 708 | ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, |
---|
722 | | - clk_base + CCLK_BURST_POLICY, 0, 4, 0, 0, NULL); |
---|
| 709 | + clk_base + CCLK_BURST_POLICY, TEGRA20_SUPER_CLK, |
---|
| 710 | + NULL); |
---|
723 | 711 | clks[TEGRA20_CLK_CCLK] = clk; |
---|
724 | 712 | |
---|
725 | 713 | /* SCLK */ |
---|
.. | .. |
---|
771 | 759 | static const char *mux_pllpcm_clkm[] = { "pll_p", "pll_c", "pll_m", "clk_m" }; |
---|
772 | 760 | static const char *mux_pllpdc_clkm[] = { "pll_p", "pll_d_out0", "pll_c", |
---|
773 | 761 | "clk_m" }; |
---|
774 | | -static const char *mux_pllmcp_clkm[] = { "pll_m", "pll_c", "pll_p", "clk_m" }; |
---|
775 | 762 | |
---|
776 | 763 | static struct tegra_periph_init_data tegra_periph_clk_list[] = { |
---|
777 | 764 | TEGRA_INIT_DATA_MUX("i2s1", i2s1_parents, CLK_SOURCE_I2S1, 11, TEGRA_PERIPH_ON_APB, TEGRA20_CLK_I2S1), |
---|
.. | .. |
---|
798 | 785 | TEGRA_INIT_DATA_NODIV("disp2", mux_pllpdc_clkm, CLK_SOURCE_DISP2, 30, 2, 26, 0, TEGRA20_CLK_DISP2), |
---|
799 | 786 | }; |
---|
800 | 787 | |
---|
801 | | -static void __init tegra20_emc_clk_init(void) |
---|
802 | | -{ |
---|
803 | | - struct clk *clk; |
---|
804 | | - |
---|
805 | | - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, |
---|
806 | | - ARRAY_SIZE(mux_pllmcp_clkm), |
---|
807 | | - CLK_SET_RATE_NO_REPARENT, |
---|
808 | | - clk_base + CLK_SOURCE_EMC, |
---|
809 | | - 30, 2, 0, &emc_lock); |
---|
810 | | - |
---|
811 | | - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, |
---|
812 | | - &emc_lock); |
---|
813 | | - clks[TEGRA20_CLK_MC] = clk; |
---|
814 | | - |
---|
815 | | - /* |
---|
816 | | - * Note that 'emc_mux' source and 'emc' rate shouldn't be changed at |
---|
817 | | - * the same time due to a HW bug, this won't happen because we're |
---|
818 | | - * defining 'emc_mux' and 'emc' as distinct clocks. |
---|
819 | | - */ |
---|
820 | | - clk = tegra_clk_register_divider("emc", "emc_mux", |
---|
821 | | - clk_base + CLK_SOURCE_EMC, CLK_IS_CRITICAL, |
---|
822 | | - TEGRA_DIVIDER_INT, 0, 8, 1, &emc_lock); |
---|
823 | | - clks[TEGRA20_CLK_EMC] = clk; |
---|
824 | | -} |
---|
825 | | - |
---|
826 | 788 | static void __init tegra20_periph_clk_init(void) |
---|
827 | 789 | { |
---|
828 | 790 | struct tegra_periph_init_data *data; |
---|
.. | .. |
---|
836 | 798 | clks[TEGRA20_CLK_AC97] = clk; |
---|
837 | 799 | |
---|
838 | 800 | /* emc */ |
---|
839 | | - tegra20_emc_clk_init(); |
---|
| 801 | + clk = tegra20_clk_register_emc(clk_base + CLK_SOURCE_EMC, false); |
---|
| 802 | + |
---|
| 803 | + clks[TEGRA20_CLK_EMC] = clk; |
---|
| 804 | + |
---|
| 805 | + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, |
---|
| 806 | + NULL); |
---|
| 807 | + clks[TEGRA20_CLK_MC] = clk; |
---|
840 | 808 | |
---|
841 | 809 | /* dsi */ |
---|
842 | 810 | clk = tegra_clk_register_periph_gate("dsi", "pll_d", 0, clk_base, 0, |
---|
.. | .. |
---|
988 | 956 | static void tegra20_cpu_clock_resume(void) |
---|
989 | 957 | { |
---|
990 | 958 | unsigned int reg, policy; |
---|
| 959 | + u32 misc, base; |
---|
991 | 960 | |
---|
992 | 961 | /* Is CPU complex already running on PLLX? */ |
---|
993 | 962 | reg = readl(clk_base + CCLK_BURST_POLICY); |
---|
.. | .. |
---|
1001 | 970 | BUG(); |
---|
1002 | 971 | |
---|
1003 | 972 | if (reg != CCLK_BURST_POLICY_PLLX) { |
---|
1004 | | - /* restore PLLX settings if CPU is on different PLL */ |
---|
1005 | | - writel(tegra20_cpu_clk_sctx.pllx_misc, |
---|
1006 | | - clk_base + PLLX_MISC); |
---|
1007 | | - writel(tegra20_cpu_clk_sctx.pllx_base, |
---|
1008 | | - clk_base + PLLX_BASE); |
---|
| 973 | + misc = readl_relaxed(clk_base + PLLX_MISC); |
---|
| 974 | + base = readl_relaxed(clk_base + PLLX_BASE); |
---|
1009 | 975 | |
---|
1010 | | - /* wait for PLL stabilization if PLLX was enabled */ |
---|
1011 | | - if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30)) |
---|
1012 | | - udelay(300); |
---|
| 976 | + if (misc != tegra20_cpu_clk_sctx.pllx_misc || |
---|
| 977 | + base != tegra20_cpu_clk_sctx.pllx_base) { |
---|
| 978 | + /* restore PLLX settings if CPU is on different PLL */ |
---|
| 979 | + writel(tegra20_cpu_clk_sctx.pllx_misc, |
---|
| 980 | + clk_base + PLLX_MISC); |
---|
| 981 | + writel(tegra20_cpu_clk_sctx.pllx_base, |
---|
| 982 | + clk_base + PLLX_BASE); |
---|
| 983 | + |
---|
| 984 | + /* wait for PLL stabilization if PLLX was enabled */ |
---|
| 985 | + if (tegra20_cpu_clk_sctx.pllx_base & (1 << 30)) |
---|
| 986 | + udelay(300); |
---|
| 987 | + } |
---|
1013 | 988 | } |
---|
1014 | 989 | |
---|
1015 | 990 | /* |
---|
.. | .. |
---|
1057 | 1032 | { TEGRA20_CLK_UARTC, TEGRA20_CLK_PLL_P, 0, 0 }, |
---|
1058 | 1033 | { TEGRA20_CLK_UARTD, TEGRA20_CLK_PLL_P, 0, 0 }, |
---|
1059 | 1034 | { TEGRA20_CLK_UARTE, TEGRA20_CLK_PLL_P, 0, 0 }, |
---|
1060 | | - { TEGRA20_CLK_PLL_A, TEGRA20_CLK_CLK_MAX, 56448000, 1 }, |
---|
1061 | | - { TEGRA20_CLK_PLL_A_OUT0, TEGRA20_CLK_CLK_MAX, 11289600, 1 }, |
---|
1062 | | - { TEGRA20_CLK_CDEV1, TEGRA20_CLK_CLK_MAX, 0, 1 }, |
---|
1063 | | - { TEGRA20_CLK_BLINK, TEGRA20_CLK_CLK_MAX, 32768, 1 }, |
---|
| 1035 | + { TEGRA20_CLK_PLL_A, TEGRA20_CLK_CLK_MAX, 56448000, 0 }, |
---|
| 1036 | + { TEGRA20_CLK_PLL_A_OUT0, TEGRA20_CLK_CLK_MAX, 11289600, 0 }, |
---|
1064 | 1037 | { TEGRA20_CLK_I2S1, TEGRA20_CLK_PLL_A_OUT0, 11289600, 0 }, |
---|
1065 | 1038 | { TEGRA20_CLK_I2S2, TEGRA20_CLK_PLL_A_OUT0, 11289600, 0 }, |
---|
1066 | 1039 | { TEGRA20_CLK_SDMMC1, TEGRA20_CLK_PLL_P, 48000000, 0 }, |
---|
.. | .. |
---|
1072 | 1045 | { TEGRA20_CLK_SBC3, TEGRA20_CLK_PLL_P, 100000000, 0 }, |
---|
1073 | 1046 | { TEGRA20_CLK_SBC4, TEGRA20_CLK_PLL_P, 100000000, 0 }, |
---|
1074 | 1047 | { TEGRA20_CLK_HOST1X, TEGRA20_CLK_PLL_C, 150000000, 0 }, |
---|
1075 | | - { TEGRA20_CLK_DISP1, TEGRA20_CLK_PLL_P, 600000000, 0 }, |
---|
1076 | | - { TEGRA20_CLK_DISP2, TEGRA20_CLK_PLL_P, 600000000, 0 }, |
---|
1077 | 1048 | { TEGRA20_CLK_GR2D, TEGRA20_CLK_PLL_C, 300000000, 0 }, |
---|
1078 | 1049 | { TEGRA20_CLK_GR3D, TEGRA20_CLK_PLL_C, 300000000, 0 }, |
---|
1079 | | - { TEGRA20_CLK_VDE, TEGRA20_CLK_CLK_MAX, 300000000, 0 }, |
---|
| 1050 | + { TEGRA20_CLK_VDE, TEGRA20_CLK_PLL_C, 300000000, 0 }, |
---|
1080 | 1051 | /* must be the last entry */ |
---|
1081 | 1052 | { TEGRA20_CLK_CLK_MAX, TEGRA20_CLK_CLK_MAX, 0, 0 }, |
---|
1082 | 1053 | }; |
---|
.. | .. |
---|
1116 | 1087 | if (IS_ERR(clk)) |
---|
1117 | 1088 | return clk; |
---|
1118 | 1089 | |
---|
| 1090 | + hw = __clk_get_hw(clk); |
---|
| 1091 | + |
---|
1119 | 1092 | /* |
---|
1120 | 1093 | * Tegra20 CDEV1 and CDEV2 clocks are a bit special case, their parent |
---|
1121 | 1094 | * clock is created by the pinctrl driver. It is possible for clk user |
---|
.. | .. |
---|
1125 | 1098 | */ |
---|
1126 | 1099 | if (clkspec->args[0] == TEGRA20_CLK_CDEV1 || |
---|
1127 | 1100 | clkspec->args[0] == TEGRA20_CLK_CDEV2) { |
---|
1128 | | - hw = __clk_get_hw(clk); |
---|
1129 | | - |
---|
1130 | 1101 | parent_hw = clk_hw_get_parent(hw); |
---|
1131 | 1102 | if (!parent_hw) |
---|
| 1103 | + return ERR_PTR(-EPROBE_DEFER); |
---|
| 1104 | + } |
---|
| 1105 | + |
---|
| 1106 | + if (clkspec->args[0] == TEGRA20_CLK_EMC) { |
---|
| 1107 | + if (!tegra20_clk_emc_driver_available(hw)) |
---|
1132 | 1108 | return ERR_PTR(-EPROBE_DEFER); |
---|
1133 | 1109 | } |
---|
1134 | 1110 | |
---|
.. | .. |
---|
1152 | 1128 | } |
---|
1153 | 1129 | |
---|
1154 | 1130 | pmc_base = of_iomap(node, 0); |
---|
| 1131 | + of_node_put(node); |
---|
1155 | 1132 | if (!pmc_base) { |
---|
1156 | 1133 | pr_err("Can't map pmc registers\n"); |
---|
1157 | 1134 | BUG(); |
---|
.. | .. |
---|
1169 | 1146 | tegra_super_clk_gen4_init(clk_base, pmc_base, tegra20_clks, NULL); |
---|
1170 | 1147 | tegra20_periph_clk_init(); |
---|
1171 | 1148 | tegra20_audio_clk_init(); |
---|
1172 | | - tegra_pmc_clk_init(pmc_base, tegra20_clks); |
---|
1173 | 1149 | |
---|
1174 | 1150 | tegra_init_dup_clks(tegra_clk_duplicates, clks, TEGRA20_CLK_CLK_MAX); |
---|
1175 | 1151 | |
---|