1 /* 2 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 */ 10 11 #include <linux/clk-provider.h> 12 #include <linux/clkdev.h> 13 #include <linux/clk/at91_pmc.h> 14 #include <linux/of.h> 15 #include <linux/mfd/syscon.h> 16 #include <linux/regmap.h> 17 18 #include "pmc.h" 19 20 #define UTMI_FIXED_MUL 40 21 22 struct clk_utmi { 23 struct clk_hw hw; 24 struct regmap *regmap; 25 }; 26 27 #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) 28 29 static inline bool clk_utmi_ready(struct regmap *regmap) 30 { 31 unsigned int status; 32 33 regmap_read(regmap, AT91_PMC_SR, &status); 34 35 return status & AT91_PMC_LOCKU; 36 } 37 38 static int clk_utmi_prepare(struct clk_hw *hw) 39 { 40 struct clk_utmi *utmi = to_clk_utmi(hw); 41 unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | 42 AT91_PMC_BIASEN; 43 44 regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr); 45 46 while (!clk_utmi_ready(utmi->regmap)) 47 cpu_relax(); 48 49 return 0; 50 } 51 52 static int clk_utmi_is_prepared(struct clk_hw *hw) 53 { 54 struct clk_utmi *utmi = to_clk_utmi(hw); 55 56 return clk_utmi_ready(utmi->regmap); 57 } 58 59 static void clk_utmi_unprepare(struct clk_hw *hw) 60 { 61 struct clk_utmi *utmi = to_clk_utmi(hw); 62 63 regmap_update_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0); 64 } 65 66 static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, 67 unsigned long parent_rate) 68 { 69 /* UTMI clk is a fixed clk multiplier */ 70 return parent_rate * UTMI_FIXED_MUL; 71 } 72 73 static const struct clk_ops utmi_ops = { 74 .prepare = clk_utmi_prepare, 75 .unprepare = clk_utmi_unprepare, 76 .is_prepared = clk_utmi_is_prepared, 77 .recalc_rate = clk_utmi_recalc_rate, 78 }; 79 80 static struct clk_hw * __init 81 at91_clk_register_utmi(struct regmap *regmap, 82 const char *name, const char *parent_name) 83 { 84 struct clk_utmi *utmi; 85 struct clk_hw *hw; 86 struct clk_init_data init; 87 int ret; 88 89 utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); 90 if (!utmi) 91 return ERR_PTR(-ENOMEM); 92 93 init.name = name; 94 init.ops = &utmi_ops; 95 init.parent_names = parent_name ? &parent_name : NULL; 96 init.num_parents = parent_name ? 1 : 0; 97 init.flags = CLK_SET_RATE_GATE; 98 99 utmi->hw.init = &init; 100 utmi->regmap = regmap; 101 102 hw = &utmi->hw; 103 ret = clk_hw_register(NULL, &utmi->hw); 104 if (ret) { 105 kfree(utmi); 106 hw = ERR_PTR(ret); 107 } 108 109 return hw; 110 } 111 112 static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np) 113 { 114 struct clk_hw *hw; 115 const char *parent_name; 116 const char *name = np->name; 117 struct regmap *regmap; 118 119 parent_name = of_clk_get_parent_name(np, 0); 120 121 of_property_read_string(np, "clock-output-names", &name); 122 123 regmap = syscon_node_to_regmap(of_get_parent(np)); 124 if (IS_ERR(regmap)) 125 return; 126 127 hw = at91_clk_register_utmi(regmap, name, parent_name); 128 if (IS_ERR(hw)) 129 return; 130 131 of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw); 132 return; 133 } 134 CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi", 135 of_at91sam9x5_clk_utmi_setup); 136