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 v4.9-rc8 193 lines 4.3 kB view raw
1/* 2 * Copyright (C) 2016 Free Electrons 3 * Copyright (C) 2016 NextThing Co 4 * 5 * Maxime Ripard <maxime.ripard@free-electrons.com> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 */ 12 13#include <linux/clk-provider.h> 14#include <linux/regmap.h> 15 16#include "sun4i_tcon.h" 17#include "sun4i_dotclock.h" 18 19struct sun4i_dclk { 20 struct clk_hw hw; 21 struct regmap *regmap; 22}; 23 24static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw) 25{ 26 return container_of(hw, struct sun4i_dclk, hw); 27} 28 29static void sun4i_dclk_disable(struct clk_hw *hw) 30{ 31 struct sun4i_dclk *dclk = hw_to_dclk(hw); 32 33 regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 34 BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0); 35} 36 37static int sun4i_dclk_enable(struct clk_hw *hw) 38{ 39 struct sun4i_dclk *dclk = hw_to_dclk(hw); 40 41 return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 42 BIT(SUN4I_TCON0_DCLK_GATE_BIT), 43 BIT(SUN4I_TCON0_DCLK_GATE_BIT)); 44} 45 46static int sun4i_dclk_is_enabled(struct clk_hw *hw) 47{ 48 struct sun4i_dclk *dclk = hw_to_dclk(hw); 49 u32 val; 50 51 regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val); 52 53 return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT); 54} 55 56static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw, 57 unsigned long parent_rate) 58{ 59 struct sun4i_dclk *dclk = hw_to_dclk(hw); 60 u32 val; 61 62 regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val); 63 64 val >>= SUN4I_TCON0_DCLK_DIV_SHIFT; 65 val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1; 66 67 if (!val) 68 val = 1; 69 70 return parent_rate / val; 71} 72 73static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate, 74 unsigned long *parent_rate) 75{ 76 unsigned long best_parent = 0; 77 u8 best_div = 1; 78 int i; 79 80 for (i = 6; i <= 127; i++) { 81 unsigned long ideal = rate * i; 82 unsigned long rounded; 83 84 rounded = clk_hw_round_rate(clk_hw_get_parent(hw), 85 ideal); 86 87 if (rounded == ideal) { 88 best_parent = rounded; 89 best_div = i; 90 goto out; 91 } 92 93 if (abs(rate - rounded / i) < 94 abs(rate - best_parent / best_div)) { 95 best_parent = rounded; 96 best_div = i; 97 } 98 } 99 100out: 101 *parent_rate = best_parent; 102 103 return best_parent / best_div; 104} 105 106static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate, 107 unsigned long parent_rate) 108{ 109 struct sun4i_dclk *dclk = hw_to_dclk(hw); 110 u8 div = parent_rate / rate; 111 112 return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG, 113 GENMASK(6, 0), div); 114} 115 116static int sun4i_dclk_get_phase(struct clk_hw *hw) 117{ 118 struct sun4i_dclk *dclk = hw_to_dclk(hw); 119 u32 val; 120 121 regmap_read(dclk->regmap, SUN4I_TCON0_IO_POL_REG, &val); 122 123 val >>= 28; 124 val &= 3; 125 126 return val * 120; 127} 128 129static int sun4i_dclk_set_phase(struct clk_hw *hw, int degrees) 130{ 131 struct sun4i_dclk *dclk = hw_to_dclk(hw); 132 133 regmap_update_bits(dclk->regmap, SUN4I_TCON0_IO_POL_REG, 134 GENMASK(29, 28), 135 degrees / 120); 136 137 return 0; 138} 139 140static const struct clk_ops sun4i_dclk_ops = { 141 .disable = sun4i_dclk_disable, 142 .enable = sun4i_dclk_enable, 143 .is_enabled = sun4i_dclk_is_enabled, 144 145 .recalc_rate = sun4i_dclk_recalc_rate, 146 .round_rate = sun4i_dclk_round_rate, 147 .set_rate = sun4i_dclk_set_rate, 148 149 .get_phase = sun4i_dclk_get_phase, 150 .set_phase = sun4i_dclk_set_phase, 151}; 152 153int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon) 154{ 155 const char *clk_name, *parent_name; 156 struct clk_init_data init; 157 struct sun4i_dclk *dclk; 158 int ret; 159 160 parent_name = __clk_get_name(tcon->sclk0); 161 ret = of_property_read_string_index(dev->of_node, 162 "clock-output-names", 0, 163 &clk_name); 164 if (ret) 165 return ret; 166 167 dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL); 168 if (!dclk) 169 return -ENOMEM; 170 171 init.name = clk_name; 172 init.ops = &sun4i_dclk_ops; 173 init.parent_names = &parent_name; 174 init.num_parents = 1; 175 init.flags = CLK_SET_RATE_PARENT; 176 177 dclk->regmap = tcon->regs; 178 dclk->hw.init = &init; 179 180 tcon->dclk = clk_register(dev, &dclk->hw); 181 if (IS_ERR(tcon->dclk)) 182 return PTR_ERR(tcon->dclk); 183 184 return 0; 185} 186EXPORT_SYMBOL(sun4i_dclk_create); 187 188int sun4i_dclk_free(struct sun4i_tcon *tcon) 189{ 190 clk_unregister(tcon->dclk); 191 return 0; 192} 193EXPORT_SYMBOL(sun4i_dclk_free);