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 13 #include "pmc.h" 14 15 #define MASTER_PRES_MASK 0x7 16 #define MASTER_PRES_MAX MASTER_PRES_MASK 17 #define MASTER_DIV_SHIFT 8 18 #define MASTER_DIV_MASK 0x3 19 20 #define to_clk_master(hw) container_of(hw, struct clk_master, hw) 21 22 struct clk_master { 23 struct clk_hw hw; 24 struct regmap *regmap; 25 const struct clk_master_layout *layout; 26 const struct clk_master_characteristics *characteristics; 27 u32 mckr; 28 }; 29 30 static inline bool clk_master_ready(struct regmap *regmap) 31 { 32 unsigned int status; 33 34 regmap_read(regmap, AT91_PMC_SR, &status); 35 36 return status & AT91_PMC_MCKRDY ? 1 : 0; 37 } 38 39 static int clk_master_prepare(struct clk_hw *hw) 40 { 41 struct clk_master *master = to_clk_master(hw); 42 43 while (!clk_master_ready(master->regmap)) 44 cpu_relax(); 45 46 return 0; 47 } 48 49 static int clk_master_is_prepared(struct clk_hw *hw) 50 { 51 struct clk_master *master = to_clk_master(hw); 52 53 return clk_master_ready(master->regmap); 54 } 55 56 static unsigned long clk_master_recalc_rate(struct clk_hw *hw, 57 unsigned long parent_rate) 58 { 59 u8 pres; 60 u8 div; 61 unsigned long rate = parent_rate; 62 struct clk_master *master = to_clk_master(hw); 63 const struct clk_master_layout *layout = master->layout; 64 const struct clk_master_characteristics *characteristics = 65 master->characteristics; 66 unsigned int mckr; 67 68 regmap_read(master->regmap, master->layout->offset, &mckr); 69 mckr &= layout->mask; 70 71 pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; 72 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 73 74 if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) 75 rate /= 3; 76 else 77 rate >>= pres; 78 79 rate /= characteristics->divisors[div]; 80 81 if (rate < characteristics->output.min) 82 pr_warn("master clk is underclocked"); 83 else if (rate > characteristics->output.max) 84 pr_warn("master clk is overclocked"); 85 86 return rate; 87 } 88 89 static u8 clk_master_get_parent(struct clk_hw *hw) 90 { 91 struct clk_master *master = to_clk_master(hw); 92 unsigned int mckr; 93 94 regmap_read(master->regmap, master->layout->offset, &mckr); 95 96 return mckr & AT91_PMC_CSS; 97 } 98 99 static const struct clk_ops master_ops = { 100 .prepare = clk_master_prepare, 101 .is_prepared = clk_master_is_prepared, 102 .recalc_rate = clk_master_recalc_rate, 103 .get_parent = clk_master_get_parent, 104 }; 105 106 struct clk_hw * __init 107 at91_clk_register_master(struct regmap *regmap, 108 const char *name, int num_parents, 109 const char **parent_names, 110 const struct clk_master_layout *layout, 111 const struct clk_master_characteristics *characteristics) 112 { 113 struct clk_master *master; 114 struct clk_init_data init; 115 struct clk_hw *hw; 116 int ret; 117 118 if (!name || !num_parents || !parent_names) 119 return ERR_PTR(-EINVAL); 120 121 master = kzalloc(sizeof(*master), GFP_KERNEL); 122 if (!master) 123 return ERR_PTR(-ENOMEM); 124 125 init.name = name; 126 init.ops = &master_ops; 127 init.parent_names = parent_names; 128 init.num_parents = num_parents; 129 init.flags = 0; 130 131 master->hw.init = &init; 132 master->layout = layout; 133 master->characteristics = characteristics; 134 master->regmap = regmap; 135 136 hw = &master->hw; 137 ret = clk_hw_register(NULL, &master->hw); 138 if (ret) { 139 kfree(master); 140 hw = ERR_PTR(ret); 141 } 142 143 return hw; 144 } 145 146 const struct clk_master_layout at91rm9200_master_layout = { 147 .mask = 0x31F, 148 .pres_shift = 2, 149 .offset = AT91_PMC_MCKR, 150 }; 151 152 const struct clk_master_layout at91sam9x5_master_layout = { 153 .mask = 0x373, 154 .pres_shift = 4, 155 .offset = AT91_PMC_MCKR, 156 }; 157