Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.5 296 lines 6.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 4 */ 5 6#include <linux/clk-provider.h> 7#include <linux/clkdev.h> 8#include <linux/clk/at91_pmc.h> 9#include <linux/of.h> 10#include <linux/mfd/syscon.h> 11#include <linux/regmap.h> 12#include <soc/at91/atmel-sfr.h> 13 14#include "pmc.h" 15 16/* 17 * The purpose of this clock is to generate a 480 MHz signal. A different 18 * rate can't be configured. 19 */ 20#define UTMI_RATE 480000000 21 22struct clk_utmi { 23 struct clk_hw hw; 24 struct regmap *regmap_pmc; 25 struct regmap *regmap_sfr; 26 struct at91_clk_pms pms; 27}; 28 29#define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) 30 31static inline bool clk_utmi_ready(struct regmap *regmap) 32{ 33 unsigned int status; 34 35 regmap_read(regmap, AT91_PMC_SR, &status); 36 37 return status & AT91_PMC_LOCKU; 38} 39 40static int clk_utmi_prepare(struct clk_hw *hw) 41{ 42 struct clk_hw *hw_parent; 43 struct clk_utmi *utmi = to_clk_utmi(hw); 44 unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | 45 AT91_PMC_BIASEN; 46 unsigned int utmi_ref_clk_freq; 47 unsigned long parent_rate; 48 49 /* 50 * If mainck rate is different from 12 MHz, we have to configure the 51 * FREQ field of the SFR_UTMICKTRIM register to generate properly 52 * the utmi clock. 53 */ 54 hw_parent = clk_hw_get_parent(hw); 55 parent_rate = clk_hw_get_rate(hw_parent); 56 57 switch (parent_rate) { 58 case 12000000: 59 utmi_ref_clk_freq = 0; 60 break; 61 case 16000000: 62 utmi_ref_clk_freq = 1; 63 break; 64 case 24000000: 65 utmi_ref_clk_freq = 2; 66 break; 67 /* 68 * Not supported on SAMA5D2 but it's not an issue since MAINCK 69 * maximum value is 24 MHz. 70 */ 71 case 48000000: 72 utmi_ref_clk_freq = 3; 73 break; 74 default: 75 pr_err("UTMICK: unsupported mainck rate\n"); 76 return -EINVAL; 77 } 78 79 if (utmi->regmap_sfr) { 80 regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, 81 AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); 82 } else if (utmi_ref_clk_freq) { 83 pr_err("UTMICK: sfr node required\n"); 84 return -EINVAL; 85 } 86 87 regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); 88 89 while (!clk_utmi_ready(utmi->regmap_pmc)) 90 cpu_relax(); 91 92 return 0; 93} 94 95static int clk_utmi_is_prepared(struct clk_hw *hw) 96{ 97 struct clk_utmi *utmi = to_clk_utmi(hw); 98 99 return clk_utmi_ready(utmi->regmap_pmc); 100} 101 102static void clk_utmi_unprepare(struct clk_hw *hw) 103{ 104 struct clk_utmi *utmi = to_clk_utmi(hw); 105 106 regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, 107 AT91_PMC_UPLLEN, 0); 108} 109 110static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, 111 unsigned long parent_rate) 112{ 113 /* UTMI clk rate is fixed. */ 114 return UTMI_RATE; 115} 116 117static int clk_utmi_save_context(struct clk_hw *hw) 118{ 119 struct clk_utmi *utmi = to_clk_utmi(hw); 120 121 utmi->pms.status = clk_utmi_is_prepared(hw); 122 123 return 0; 124} 125 126static void clk_utmi_restore_context(struct clk_hw *hw) 127{ 128 struct clk_utmi *utmi = to_clk_utmi(hw); 129 130 if (utmi->pms.status) 131 clk_utmi_prepare(hw); 132} 133 134static const struct clk_ops utmi_ops = { 135 .prepare = clk_utmi_prepare, 136 .unprepare = clk_utmi_unprepare, 137 .is_prepared = clk_utmi_is_prepared, 138 .recalc_rate = clk_utmi_recalc_rate, 139 .save_context = clk_utmi_save_context, 140 .restore_context = clk_utmi_restore_context, 141}; 142 143static struct clk_hw * __init 144at91_clk_register_utmi_internal(struct regmap *regmap_pmc, 145 struct regmap *regmap_sfr, 146 const char *name, const char *parent_name, 147 struct clk_hw *parent_hw, 148 const struct clk_ops *ops, unsigned long flags) 149{ 150 struct clk_utmi *utmi; 151 struct clk_hw *hw; 152 struct clk_init_data init = {}; 153 int ret; 154 155 if (!(parent_name || parent_hw)) 156 return ERR_PTR(-EINVAL); 157 158 utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); 159 if (!utmi) 160 return ERR_PTR(-ENOMEM); 161 162 init.name = name; 163 init.ops = ops; 164 if (parent_hw) { 165 init.parent_hws = parent_hw ? (const struct clk_hw **)&parent_hw : NULL; 166 init.num_parents = parent_hw ? 1 : 0; 167 } else { 168 init.parent_names = parent_name ? &parent_name : NULL; 169 init.num_parents = parent_name ? 1 : 0; 170 } 171 init.flags = flags; 172 173 utmi->hw.init = &init; 174 utmi->regmap_pmc = regmap_pmc; 175 utmi->regmap_sfr = regmap_sfr; 176 177 hw = &utmi->hw; 178 ret = clk_hw_register(NULL, &utmi->hw); 179 if (ret) { 180 kfree(utmi); 181 hw = ERR_PTR(ret); 182 } 183 184 return hw; 185} 186 187struct clk_hw * __init 188at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, 189 const char *name, const char *parent_name, 190 struct clk_hw *parent_hw) 191{ 192 return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name, 193 parent_name, parent_hw, &utmi_ops, CLK_SET_RATE_GATE); 194} 195 196static int clk_utmi_sama7g5_prepare(struct clk_hw *hw) 197{ 198 struct clk_utmi *utmi = to_clk_utmi(hw); 199 struct clk_hw *hw_parent; 200 unsigned long parent_rate; 201 unsigned int val; 202 203 hw_parent = clk_hw_get_parent(hw); 204 parent_rate = clk_hw_get_rate(hw_parent); 205 206 switch (parent_rate) { 207 case 16000000: 208 val = 0; 209 break; 210 case 20000000: 211 val = 2; 212 break; 213 case 24000000: 214 val = 3; 215 break; 216 case 32000000: 217 val = 5; 218 break; 219 default: 220 pr_err("UTMICK: unsupported main_xtal rate\n"); 221 return -EINVAL; 222 } 223 224 regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val); 225 226 return 0; 227 228} 229 230static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw) 231{ 232 struct clk_utmi *utmi = to_clk_utmi(hw); 233 struct clk_hw *hw_parent; 234 unsigned long parent_rate; 235 unsigned int val; 236 237 hw_parent = clk_hw_get_parent(hw); 238 parent_rate = clk_hw_get_rate(hw_parent); 239 240 regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val); 241 switch (val & 0x7) { 242 case 0: 243 if (parent_rate == 16000000) 244 return 1; 245 break; 246 case 2: 247 if (parent_rate == 20000000) 248 return 1; 249 break; 250 case 3: 251 if (parent_rate == 24000000) 252 return 1; 253 break; 254 case 5: 255 if (parent_rate == 32000000) 256 return 1; 257 break; 258 default: 259 break; 260 } 261 262 return 0; 263} 264 265static int clk_utmi_sama7g5_save_context(struct clk_hw *hw) 266{ 267 struct clk_utmi *utmi = to_clk_utmi(hw); 268 269 utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw); 270 271 return 0; 272} 273 274static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw) 275{ 276 struct clk_utmi *utmi = to_clk_utmi(hw); 277 278 if (utmi->pms.status) 279 clk_utmi_sama7g5_prepare(hw); 280} 281 282static const struct clk_ops sama7g5_utmi_ops = { 283 .prepare = clk_utmi_sama7g5_prepare, 284 .is_prepared = clk_utmi_sama7g5_is_prepared, 285 .recalc_rate = clk_utmi_recalc_rate, 286 .save_context = clk_utmi_sama7g5_save_context, 287 .restore_context = clk_utmi_sama7g5_restore_context, 288}; 289 290struct clk_hw * __init 291at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name, 292 const char *parent_name, struct clk_hw *parent_hw) 293{ 294 return at91_clk_register_utmi_internal(regmap_pmc, NULL, name, 295 parent_name, parent_hw, &sama7g5_utmi_ops, 0); 296}