1 /* 2 * Copyright (C) 2016 Socionext Inc. 3 * Author: Masahiro Yamada <yamada.masahiro@socionext.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16 #include <linux/clk-provider.h> 17 #include <linux/device.h> 18 #include <linux/regmap.h> 19 20 #include "clk-uniphier.h" 21 22 #define UNIPHIER_CLK_CPUGEAR_STAT 0 /* status */ 23 #define UNIPHIER_CLK_CPUGEAR_SET 4 /* set */ 24 #define UNIPHIER_CLK_CPUGEAR_UPD 8 /* update */ 25 #define UNIPHIER_CLK_CPUGEAR_UPD_BIT BIT(0) 26 27 struct uniphier_clk_cpugear { 28 struct clk_hw hw; 29 struct regmap *regmap; 30 unsigned int regbase; 31 unsigned int mask; 32 }; 33 34 #define to_uniphier_clk_cpugear(_hw) \ 35 container_of(_hw, struct uniphier_clk_cpugear, hw) 36 37 static int uniphier_clk_cpugear_set_parent(struct clk_hw *hw, u8 index) 38 { 39 struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw); 40 int ret; 41 unsigned int val; 42 43 ret = regmap_write_bits(gear->regmap, 44 gear->regbase + UNIPHIER_CLK_CPUGEAR_SET, 45 gear->mask, index); 46 if (ret) 47 return ret; 48 49 ret = regmap_write_bits(gear->regmap, 50 gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD, 51 UNIPHIER_CLK_CPUGEAR_UPD_BIT, 52 UNIPHIER_CLK_CPUGEAR_UPD_BIT); 53 if (ret) 54 return ret; 55 56 return regmap_read_poll_timeout(gear->regmap, 57 gear->regbase + UNIPHIER_CLK_CPUGEAR_UPD, 58 val, !(val & UNIPHIER_CLK_CPUGEAR_UPD_BIT), 59 0, 1); 60 } 61 62 static u8 uniphier_clk_cpugear_get_parent(struct clk_hw *hw) 63 { 64 struct uniphier_clk_cpugear *gear = to_uniphier_clk_cpugear(hw); 65 int num_parents = clk_hw_get_num_parents(hw); 66 int ret; 67 unsigned int val; 68 69 ret = regmap_read(gear->regmap, 70 gear->regbase + UNIPHIER_CLK_CPUGEAR_STAT, &val); 71 if (ret) 72 return ret; 73 74 val &= gear->mask; 75 76 return val < num_parents ? val : -EINVAL; 77 } 78 79 static const struct clk_ops uniphier_clk_cpugear_ops = { 80 .determine_rate = __clk_mux_determine_rate, 81 .set_parent = uniphier_clk_cpugear_set_parent, 82 .get_parent = uniphier_clk_cpugear_get_parent, 83 }; 84 85 struct clk_hw *uniphier_clk_register_cpugear(struct device *dev, 86 struct regmap *regmap, 87 const char *name, 88 const struct uniphier_clk_cpugear_data *data) 89 { 90 struct uniphier_clk_cpugear *gear; 91 struct clk_init_data init; 92 int ret; 93 94 gear = devm_kzalloc(dev, sizeof(*gear), GFP_KERNEL); 95 if (!gear) 96 return ERR_PTR(-ENOMEM); 97 98 init.name = name; 99 init.ops = &uniphier_clk_cpugear_ops; 100 init.flags = CLK_SET_RATE_PARENT; 101 init.parent_names = data->parent_names; 102 init.num_parents = data->num_parents, 103 104 gear->regmap = regmap; 105 gear->regbase = data->regbase; 106 gear->mask = data->mask; 107 gear->hw.init = &init; 108 109 ret = devm_clk_hw_register(dev, &gear->hw); 110 if (ret) 111 return ERR_PTR(ret); 112 113 return &gear->hw; 114 } 115