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