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/bitops.h> 7 #include <linux/clk-provider.h> 8 #include <linux/clkdev.h> 9 #include <linux/clk/at91_pmc.h> 10 #include <linux/of.h> 11 #include <linux/mfd/syscon.h> 12 #include <linux/regmap.h> 13 14 #include "pmc.h" 15 16 DEFINE_SPINLOCK(pmc_pcr_lock); 17 18 #define PERIPHERAL_ID_MIN 2 19 #define PERIPHERAL_ID_MAX 31 20 #define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) 21 22 #define PERIPHERAL_MAX_SHIFT 3 23 24 struct clk_peripheral { 25 struct clk_hw hw; 26 struct regmap *regmap; 27 u32 id; 28 }; 29 30 #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw) 31 32 struct clk_sam9x5_peripheral { 33 struct clk_hw hw; 34 struct regmap *regmap; 35 struct clk_range range; 36 spinlock_t *lock; 37 u32 id; 38 u32 div; 39 const struct clk_pcr_layout *layout; 40 bool auto_div; 41 }; 42 43 #define to_clk_sam9x5_peripheral(hw) \ 44 container_of(hw, struct clk_sam9x5_peripheral, hw) 45 46 static int clk_peripheral_enable(struct clk_hw *hw) 47 { 48 struct clk_peripheral *periph = to_clk_peripheral(hw); 49 int offset = AT91_PMC_PCER; 50 u32 id = periph->id; 51 52 if (id < PERIPHERAL_ID_MIN) 53 return 0; 54 if (id > PERIPHERAL_ID_MAX) 55 offset = AT91_PMC_PCER1; 56 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); 57 58 return 0; 59 } 60 61 static void clk_peripheral_disable(struct clk_hw *hw) 62 { 63 struct clk_peripheral *periph = to_clk_peripheral(hw); 64 int offset = AT91_PMC_PCDR; 65 u32 id = periph->id; 66 67 if (id < PERIPHERAL_ID_MIN) 68 return; 69 if (id > PERIPHERAL_ID_MAX) 70 offset = AT91_PMC_PCDR1; 71 regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); 72 } 73 74 static int clk_peripheral_is_enabled(struct clk_hw *hw) 75 { 76 struct clk_peripheral *periph = to_clk_peripheral(hw); 77 int offset = AT91_PMC_PCSR; 78 unsigned int status; 79 u32 id = periph->id; 80 81 if (id < PERIPHERAL_ID_MIN) 82 return 1; 83 if (id > PERIPHERAL_ID_MAX) 84 offset = AT91_PMC_PCSR1; 85 regmap_read(periph->regmap, offset, &status); 86 87 return status & PERIPHERAL_MASK(id) ? 1 : 0; 88 } 89 90 static const struct clk_ops peripheral_ops = { 91 .enable = clk_peripheral_enable, 92 .disable = clk_peripheral_disable, 93 .is_enabled = clk_peripheral_is_enabled, 94 }; 95 96 struct clk_hw * __init 97 at91_clk_register_peripheral(struct regmap *regmap, const char *name, 98 const char *parent_name, u32 id) 99 { 100 struct clk_peripheral *periph; 101 struct clk_init_data init; 102 struct clk_hw *hw; 103 int ret; 104 105 if (!name || !parent_name || id > PERIPHERAL_ID_MAX) 106 return ERR_PTR(-EINVAL); 107 108 periph = kzalloc(sizeof(*periph), GFP_KERNEL); 109 if (!periph) 110 return ERR_PTR(-ENOMEM); 111 112 init.name = name; 113 init.ops = &peripheral_ops; 114 init.parent_names = (parent_name ? &parent_name : NULL); 115 init.num_parents = (parent_name ? 1 : 0); 116 init.flags = 0; 117 118 periph->id = id; 119 periph->hw.init = &init; 120 periph->regmap = regmap; 121 122 hw = &periph->hw; 123 ret = clk_hw_register(NULL, &periph->hw); 124 if (ret) { 125 kfree(periph); 126 hw = ERR_PTR(ret); 127 } 128 129 return hw; 130 } 131 132 static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) 133 { 134 struct clk_hw *parent; 135 unsigned long parent_rate; 136 int shift = 0; 137 138 if (!periph->auto_div) 139 return; 140 141 if (periph->range.max) { 142 parent = clk_hw_get_parent_by_index(&periph->hw, 0); 143 parent_rate = clk_hw_get_rate(parent); 144 if (!parent_rate) 145 return; 146 147 for (; shift < PERIPHERAL_MAX_SHIFT; shift++) { 148 if (parent_rate >> shift <= periph->range.max) 149 break; 150 } 151 } 152 153 periph->auto_div = false; 154 periph->div = shift; 155 } 156 157 static int clk_sam9x5_peripheral_enable(struct clk_hw *hw) 158 { 159 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 160 unsigned long flags; 161 162 if (periph->id < PERIPHERAL_ID_MIN) 163 return 0; 164 165 spin_lock_irqsave(periph->lock, flags); 166 regmap_write(periph->regmap, periph->layout->offset, 167 (periph->id & periph->layout->pid_mask)); 168 regmap_update_bits(periph->regmap, periph->layout->offset, 169 periph->layout->div_mask | periph->layout->cmd | 170 AT91_PMC_PCR_EN, 171 field_prep(periph->layout->div_mask, periph->div) | 172 periph->layout->cmd | 173 AT91_PMC_PCR_EN); 174 spin_unlock_irqrestore(periph->lock, flags); 175 176 return 0; 177 } 178 179 static void clk_sam9x5_peripheral_disable(struct clk_hw *hw) 180 { 181 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 182 unsigned long flags; 183 184 if (periph->id < PERIPHERAL_ID_MIN) 185 return; 186 187 spin_lock_irqsave(periph->lock, flags); 188 regmap_write(periph->regmap, periph->layout->offset, 189 (periph->id & periph->layout->pid_mask)); 190 regmap_update_bits(periph->regmap, periph->layout->offset, 191 AT91_PMC_PCR_EN | periph->layout->cmd, 192 periph->layout->cmd); 193 spin_unlock_irqrestore(periph->lock, flags); 194 } 195 196 static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) 197 { 198 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 199 unsigned long flags; 200 unsigned int status; 201 202 if (periph->id < PERIPHERAL_ID_MIN) 203 return 1; 204 205 spin_lock_irqsave(periph->lock, flags); 206 regmap_write(periph->regmap, periph->layout->offset, 207 (periph->id & periph->layout->pid_mask)); 208 regmap_read(periph->regmap, periph->layout->offset, &status); 209 spin_unlock_irqrestore(periph->lock, flags); 210 211 return status & AT91_PMC_PCR_EN ? 1 : 0; 212 } 213 214 static unsigned long 215 clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, 216 unsigned long parent_rate) 217 { 218 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 219 unsigned long flags; 220 unsigned int status; 221 222 if (periph->id < PERIPHERAL_ID_MIN) 223 return parent_rate; 224 225 spin_lock_irqsave(periph->lock, flags); 226 regmap_write(periph->regmap, periph->layout->offset, 227 (periph->id & periph->layout->pid_mask)); 228 regmap_read(periph->regmap, periph->layout->offset, &status); 229 spin_unlock_irqrestore(periph->lock, flags); 230 231 if (status & AT91_PMC_PCR_EN) { 232 periph->div = field_get(periph->layout->div_mask, status); 233 periph->auto_div = false; 234 } else { 235 clk_sam9x5_peripheral_autodiv(periph); 236 } 237 238 return parent_rate >> periph->div; 239 } 240 241 static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw, 242 unsigned long rate, 243 unsigned long *parent_rate) 244 { 245 int shift = 0; 246 unsigned long best_rate; 247 unsigned long best_diff; 248 unsigned long cur_rate = *parent_rate; 249 unsigned long cur_diff; 250 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 251 252 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) 253 return *parent_rate; 254 255 if (periph->range.max) { 256 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 257 cur_rate = *parent_rate >> shift; 258 if (cur_rate <= periph->range.max) 259 break; 260 } 261 } 262 263 if (rate >= cur_rate) 264 return cur_rate; 265 266 best_diff = cur_rate - rate; 267 best_rate = cur_rate; 268 for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 269 cur_rate = *parent_rate >> shift; 270 if (cur_rate < rate) 271 cur_diff = rate - cur_rate; 272 else 273 cur_diff = cur_rate - rate; 274 275 if (cur_diff < best_diff) { 276 best_diff = cur_diff; 277 best_rate = cur_rate; 278 } 279 280 if (!best_diff || cur_rate < rate) 281 break; 282 } 283 284 return best_rate; 285 } 286 287 static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw, 288 unsigned long rate, 289 unsigned long parent_rate) 290 { 291 int shift; 292 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 293 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) { 294 if (parent_rate == rate) 295 return 0; 296 else 297 return -EINVAL; 298 } 299 300 if (periph->range.max && rate > periph->range.max) 301 return -EINVAL; 302 303 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 304 if (parent_rate >> shift == rate) { 305 periph->auto_div = false; 306 periph->div = shift; 307 return 0; 308 } 309 } 310 311 return -EINVAL; 312 } 313 314 static const struct clk_ops sam9x5_peripheral_ops = { 315 .enable = clk_sam9x5_peripheral_enable, 316 .disable = clk_sam9x5_peripheral_disable, 317 .is_enabled = clk_sam9x5_peripheral_is_enabled, 318 .recalc_rate = clk_sam9x5_peripheral_recalc_rate, 319 .round_rate = clk_sam9x5_peripheral_round_rate, 320 .set_rate = clk_sam9x5_peripheral_set_rate, 321 }; 322 323 struct clk_hw * __init 324 at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, 325 const struct clk_pcr_layout *layout, 326 const char *name, const char *parent_name, 327 u32 id, const struct clk_range *range) 328 { 329 struct clk_sam9x5_peripheral *periph; 330 struct clk_init_data init; 331 struct clk_hw *hw; 332 int ret; 333 334 if (!name || !parent_name) 335 return ERR_PTR(-EINVAL); 336 337 periph = kzalloc(sizeof(*periph), GFP_KERNEL); 338 if (!periph) 339 return ERR_PTR(-ENOMEM); 340 341 init.name = name; 342 init.ops = &sam9x5_peripheral_ops; 343 init.parent_names = (parent_name ? &parent_name : NULL); 344 init.num_parents = (parent_name ? 1 : 0); 345 init.flags = 0; 346 347 periph->id = id; 348 periph->hw.init = &init; 349 periph->div = 0; 350 periph->regmap = regmap; 351 periph->lock = lock; 352 if (layout->div_mask) 353 periph->auto_div = true; 354 periph->layout = layout; 355 periph->range = *range; 356 357 hw = &periph->hw; 358 ret = clk_hw_register(NULL, &periph->hw); 359 if (ret) { 360 kfree(periph); 361 hw = ERR_PTR(ret); 362 } else { 363 clk_sam9x5_peripheral_autodiv(periph); 364 pmc_register_id(id); 365 } 366 367 return hw; 368 } 369