clk.c revision b8700d506ac4050fd96ce9305df04df811365326
1/* 2 * 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 */ 16 17#include <linux/clk.h> 18#include <linux/clk-provider.h> 19#include <linux/of.h> 20#include <linux/clk/tegra.h> 21 22#include "clk.h" 23 24#define CLK_OUT_ENB_L 0x010 25#define CLK_OUT_ENB_H 0x014 26#define CLK_OUT_ENB_U 0x018 27#define CLK_OUT_ENB_V 0x360 28#define CLK_OUT_ENB_W 0x364 29#define CLK_OUT_ENB_X 0x280 30#define CLK_OUT_ENB_SET_L 0x320 31#define CLK_OUT_ENB_CLR_L 0x324 32#define CLK_OUT_ENB_SET_H 0x328 33#define CLK_OUT_ENB_CLR_H 0x32c 34#define CLK_OUT_ENB_SET_U 0x330 35#define CLK_OUT_ENB_CLR_U 0x334 36#define CLK_OUT_ENB_SET_V 0x440 37#define CLK_OUT_ENB_CLR_V 0x444 38#define CLK_OUT_ENB_SET_W 0x448 39#define CLK_OUT_ENB_CLR_W 0x44c 40#define CLK_OUT_ENB_SET_X 0x284 41#define CLK_OUT_ENB_CLR_X 0x288 42 43#define RST_DEVICES_L 0x004 44#define RST_DEVICES_H 0x008 45#define RST_DEVICES_U 0x00C 46#define RST_DFLL_DVCO 0x2F4 47#define RST_DEVICES_V 0x358 48#define RST_DEVICES_W 0x35C 49#define RST_DEVICES_X 0x28C 50#define RST_DEVICES_SET_L 0x300 51#define RST_DEVICES_CLR_L 0x304 52#define RST_DEVICES_SET_H 0x308 53#define RST_DEVICES_CLR_H 0x30c 54#define RST_DEVICES_SET_U 0x310 55#define RST_DEVICES_CLR_U 0x314 56#define RST_DEVICES_SET_V 0x430 57#define RST_DEVICES_CLR_V 0x434 58#define RST_DEVICES_SET_W 0x438 59#define RST_DEVICES_CLR_W 0x43c 60 61/* Global data of Tegra CPU CAR ops */ 62static struct tegra_cpu_car_ops dummy_car_ops; 63struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops; 64 65int *periph_clk_enb_refcnt; 66static int periph_banks; 67static struct clk **clks; 68static int clk_num; 69static struct clk_onecell_data clk_data; 70 71static struct tegra_clk_periph_regs periph_regs[] = { 72 [0] = { 73 .enb_reg = CLK_OUT_ENB_L, 74 .enb_set_reg = CLK_OUT_ENB_SET_L, 75 .enb_clr_reg = CLK_OUT_ENB_CLR_L, 76 .rst_reg = RST_DEVICES_L, 77 .rst_set_reg = RST_DEVICES_SET_L, 78 .rst_clr_reg = RST_DEVICES_CLR_L, 79 }, 80 [1] = { 81 .enb_reg = CLK_OUT_ENB_H, 82 .enb_set_reg = CLK_OUT_ENB_SET_H, 83 .enb_clr_reg = CLK_OUT_ENB_CLR_H, 84 .rst_reg = RST_DEVICES_H, 85 .rst_set_reg = RST_DEVICES_SET_H, 86 .rst_clr_reg = RST_DEVICES_CLR_H, 87 }, 88 [2] = { 89 .enb_reg = CLK_OUT_ENB_U, 90 .enb_set_reg = CLK_OUT_ENB_SET_U, 91 .enb_clr_reg = CLK_OUT_ENB_CLR_U, 92 .rst_reg = RST_DEVICES_U, 93 .rst_set_reg = RST_DEVICES_SET_U, 94 .rst_clr_reg = RST_DEVICES_CLR_U, 95 }, 96 [3] = { 97 .enb_reg = CLK_OUT_ENB_V, 98 .enb_set_reg = CLK_OUT_ENB_SET_V, 99 .enb_clr_reg = CLK_OUT_ENB_CLR_V, 100 .rst_reg = RST_DEVICES_V, 101 .rst_set_reg = RST_DEVICES_SET_V, 102 .rst_clr_reg = RST_DEVICES_CLR_V, 103 }, 104 [4] = { 105 .enb_reg = CLK_OUT_ENB_W, 106 .enb_set_reg = CLK_OUT_ENB_SET_W, 107 .enb_clr_reg = CLK_OUT_ENB_CLR_W, 108 .rst_reg = RST_DEVICES_W, 109 .rst_set_reg = RST_DEVICES_SET_W, 110 .rst_clr_reg = RST_DEVICES_CLR_W, 111 }, 112}; 113 114struct tegra_clk_periph_regs *get_reg_bank(int clkid) 115{ 116 int reg_bank = clkid / 32; 117 118 if (reg_bank < periph_banks) 119 return &periph_regs[reg_bank]; 120 else { 121 WARN_ON(1); 122 return NULL; 123 } 124} 125 126struct clk ** __init tegra_clk_init(int num, int banks) 127{ 128 if (WARN_ON(banks > ARRAY_SIZE(periph_regs))) 129 return NULL; 130 131 periph_clk_enb_refcnt = kzalloc(32 * banks * 132 sizeof(*periph_clk_enb_refcnt), GFP_KERNEL); 133 if (!periph_clk_enb_refcnt) 134 return NULL; 135 136 periph_banks = banks; 137 138 clks = kzalloc(num * sizeof(struct clk *), GFP_KERNEL); 139 if (!clks) 140 kfree(periph_clk_enb_refcnt); 141 142 clk_num = num; 143 144 return clks; 145} 146 147void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list, 148 struct clk *clks[], int clk_max) 149{ 150 struct clk *clk; 151 152 for (; dup_list->clk_id < clk_max; dup_list++) { 153 clk = clks[dup_list->clk_id]; 154 dup_list->lookup.clk = clk; 155 clkdev_add(&dup_list->lookup); 156 } 157} 158 159void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, 160 struct clk *clks[], int clk_max) 161{ 162 struct clk *clk; 163 164 for (; tbl->clk_id < clk_max; tbl++) { 165 clk = clks[tbl->clk_id]; 166 if (IS_ERR_OR_NULL(clk)) 167 return; 168 169 if (tbl->parent_id < clk_max) { 170 struct clk *parent = clks[tbl->parent_id]; 171 if (clk_set_parent(clk, parent)) { 172 pr_err("%s: Failed to set parent %s of %s\n", 173 __func__, __clk_get_name(parent), 174 __clk_get_name(clk)); 175 WARN_ON(1); 176 } 177 } 178 179 if (tbl->rate) 180 if (clk_set_rate(clk, tbl->rate)) { 181 pr_err("%s: Failed to set rate %lu of %s\n", 182 __func__, tbl->rate, 183 __clk_get_name(clk)); 184 WARN_ON(1); 185 } 186 187 if (tbl->state) 188 if (clk_prepare_enable(clk)) { 189 pr_err("%s: Failed to enable %s\n", __func__, 190 __clk_get_name(clk)); 191 WARN_ON(1); 192 } 193 } 194} 195 196void __init tegra_add_of_provider(struct device_node *np) 197{ 198 int i; 199 200 for (i = 0; i < clk_num; i++) { 201 if (IS_ERR(clks[i])) { 202 pr_err 203 ("Tegra clk %d: register failed with %ld\n", 204 i, PTR_ERR(clks[i])); 205 } 206 if (!clks[i]) 207 clks[i] = ERR_PTR(-EINVAL); 208 } 209 210 clk_data.clks = clks; 211 clk_data.clk_num = clk_num; 212 of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 213} 214 215struct clk ** __init tegra_lookup_dt_id(int clk_id, 216 struct tegra_clk *tegra_clk) 217{ 218 if (tegra_clk[clk_id].present) 219 return &clks[tegra_clk[clk_id].dt_id]; 220 else 221 return NULL; 222} 223 224tegra_clk_apply_init_table_func tegra_clk_apply_init_table; 225 226void __init tegra_clocks_apply_init_table(void) 227{ 228 if (!tegra_clk_apply_init_table) 229 return; 230 231 tegra_clk_apply_init_table(); 232} 233