1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Master clock support for AT91 architectures. 4 * 5 * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries 6 * 7 * Author: Claudiu Beznea <claudiu.beznea@microchip.com> 8 * 9 * Based on drivers/clk/at91/clk-master.c from Linux. 10 */ 11 12#include <asm/processor.h> 13#include <clk-uclass.h> 14#include <common.h> 15#include <div64.h> 16#include <dm.h> 17#include <linux/clk-provider.h> 18#include <linux/clk/at91_pmc.h> 19 20#include "pmc.h" 21 22#define UBOOT_DM_CLK_AT91_MASTER_PRES "at91-master-clk-pres" 23#define UBOOT_DM_CLK_AT91_MASTER_DIV "at91-master-clk-div" 24#define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER "at91-sama7g5-master-clk" 25 26#define MASTER_PRES_MASK 0x7 27#define MASTER_PRES_MAX MASTER_PRES_MASK 28#define MASTER_DIV_SHIFT 8 29#define MASTER_DIV_MASK 0x7 30 31#define PMC_MCR 0x30 32#define PMC_MCR_ID_MSK GENMASK(3, 0) 33#define PMC_MCR_CMD BIT(7) 34#define PMC_MCR_DIV GENMASK(10, 8) 35#define PMC_MCR_CSS GENMASK(20, 16) 36#define PMC_MCR_CSS_SHIFT (16) 37#define PMC_MCR_EN BIT(28) 38 39#define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK) 40 41#define MASTER_MAX_ID 4 42 43struct clk_master { 44 void __iomem *base; 45 const struct clk_master_layout *layout; 46 const struct clk_master_characteristics *characteristics; 47 const u32 *mux_table; 48 const u32 *clk_mux_table; 49 u32 num_parents; 50 struct clk clk; 51 u8 id; 52}; 53 54#define to_clk_master(_clk) container_of(_clk, struct clk_master, clk) 55 56static inline bool clk_master_ready(struct clk_master *master) 57{ 58 unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY; 59 unsigned int status; 60 61 pmc_read(master->base, AT91_PMC_SR, &status); 62 63 return !!(status & bit); 64} 65 66static int clk_master_enable(struct clk *clk) 67{ 68 struct clk_master *master = to_clk_master(clk); 69 70 while (!clk_master_ready(master)) { 71 debug("waiting for mck %d\n", master->id); 72 cpu_relax(); 73 } 74 75 return 0; 76} 77 78static ulong clk_master_pres_get_rate(struct clk *clk) 79{ 80 struct clk_master *master = to_clk_master(clk); 81 const struct clk_master_layout *layout = master->layout; 82 const struct clk_master_characteristics *characteristics = 83 master->characteristics; 84 ulong rate = clk_get_parent_rate(clk); 85 unsigned int mckr; 86 u8 pres; 87 88 if (!rate) 89 return 0; 90 91 pmc_read(master->base, master->layout->offset, &mckr); 92 mckr &= layout->mask; 93 94 pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; 95 96 if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) 97 pres = 3; 98 else 99 pres = (1 << pres); 100 101 return DIV_ROUND_CLOSEST_ULL(rate, pres); 102} 103 104static const struct clk_ops master_pres_ops = { 105 .enable = clk_master_enable, 106 .get_rate = clk_master_pres_get_rate, 107}; 108 109struct clk *at91_clk_register_master_pres(void __iomem *base, 110 const char *name, const char * const *parent_names, 111 int num_parents, const struct clk_master_layout *layout, 112 const struct clk_master_characteristics *characteristics, 113 const u32 *mux_table) 114{ 115 struct clk_master *master; 116 struct clk *clk; 117 unsigned int val; 118 int ret; 119 120 if (!base || !name || !num_parents || !parent_names || 121 !layout || !characteristics || !mux_table) 122 return ERR_PTR(-EINVAL); 123 124 master = kzalloc(sizeof(*master), GFP_KERNEL); 125 if (!master) 126 return ERR_PTR(-ENOMEM); 127 128 master->layout = layout; 129 master->characteristics = characteristics; 130 master->base = base; 131 master->num_parents = num_parents; 132 master->mux_table = mux_table; 133 134 pmc_read(master->base, master->layout->offset, &val); 135 clk = &master->clk; 136 clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL; 137 ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_PRES, name, 138 parent_names[val & AT91_PMC_CSS]); 139 if (ret) { 140 kfree(master); 141 clk = ERR_PTR(ret); 142 } 143 144 return clk; 145} 146 147U_BOOT_DRIVER(at91_master_pres_clk) = { 148 .name = UBOOT_DM_CLK_AT91_MASTER_PRES, 149 .id = UCLASS_CLK, 150 .ops = &master_pres_ops, 151 .flags = DM_FLAG_PRE_RELOC, 152}; 153 154static ulong clk_master_div_get_rate(struct clk *clk) 155{ 156 struct clk_master *master = to_clk_master(clk); 157 const struct clk_master_layout *layout = master->layout; 158 const struct clk_master_characteristics *characteristics = 159 master->characteristics; 160 ulong rate = clk_get_parent_rate(clk); 161 unsigned int mckr; 162 u8 div; 163 164 if (!rate) 165 return 0; 166 167 pmc_read(master->base, master->layout->offset, &mckr); 168 mckr &= layout->mask; 169 div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 170 171 rate = DIV_ROUND_CLOSEST_ULL(rate, characteristics->divisors[div]); 172 if (rate < characteristics->output.min) 173 pr_warn("master clk is underclocked"); 174 else if (rate > characteristics->output.max) 175 pr_warn("master clk is overclocked"); 176 177 return rate; 178} 179 180static const struct clk_ops master_div_ops = { 181 .enable = clk_master_enable, 182 .get_rate = clk_master_div_get_rate, 183}; 184 185struct clk *at91_clk_register_master_div(void __iomem *base, 186 const char *name, const char *parent_name, 187 const struct clk_master_layout *layout, 188 const struct clk_master_characteristics *characteristics) 189{ 190 struct clk_master *master; 191 struct clk *clk; 192 int ret; 193 194 if (!base || !name || !parent_name || !layout || !characteristics) 195 return ERR_PTR(-EINVAL); 196 197 master = kzalloc(sizeof(*master), GFP_KERNEL); 198 if (!master) 199 return ERR_PTR(-ENOMEM); 200 201 master->layout = layout; 202 master->characteristics = characteristics; 203 master->base = base; 204 master->num_parents = 1; 205 206 clk = &master->clk; 207 clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL; 208 ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_DIV, name, 209 parent_name); 210 if (ret) { 211 kfree(master); 212 clk = ERR_PTR(ret); 213 } 214 215 return clk; 216} 217 218U_BOOT_DRIVER(at91_master_div_clk) = { 219 .name = UBOOT_DM_CLK_AT91_MASTER_DIV, 220 .id = UCLASS_CLK, 221 .ops = &master_div_ops, 222 .flags = DM_FLAG_PRE_RELOC, 223}; 224 225static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent) 226{ 227 struct clk_master *master = to_clk_master(clk); 228 int index; 229 230 index = at91_clk_mux_val_to_index(master->clk_mux_table, 231 master->num_parents, parent->id); 232 if (index < 0) 233 return index; 234 235 index = at91_clk_mux_index_to_val(master->mux_table, 236 master->num_parents, index); 237 if (index < 0) 238 return index; 239 240 pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id)); 241 pmc_update_bits(master->base, PMC_MCR, 242 PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK, 243 (index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD | 244 PMC_MCR_ID(master->id)); 245 return 0; 246} 247 248static int clk_sama7g5_master_enable(struct clk *clk) 249{ 250 struct clk_master *master = to_clk_master(clk); 251 252 pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id)); 253 pmc_update_bits(master->base, PMC_MCR, 254 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK, 255 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id)); 256 257 return 0; 258} 259 260static int clk_sama7g5_master_disable(struct clk *clk) 261{ 262 struct clk_master *master = to_clk_master(clk); 263 264 pmc_write(master->base, PMC_MCR, master->id); 265 pmc_update_bits(master->base, PMC_MCR, 266 PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK, 267 PMC_MCR_CMD | PMC_MCR_ID(master->id)); 268 269 return 0; 270} 271 272static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate) 273{ 274 struct clk_master *master = to_clk_master(clk); 275 ulong parent_rate = clk_get_parent_rate(clk); 276 ulong div, rrate; 277 278 if (!parent_rate) 279 return 0; 280 281 div = DIV_ROUND_CLOSEST(parent_rate, rate); 282 if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) { 283 return 0; 284 } else if (div == 3) { 285 rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX); 286 div = MASTER_PRES_MAX; 287 } else { 288 rrate = DIV_ROUND_CLOSEST(parent_rate, div); 289 div = ffs(div) - 1; 290 } 291 292 pmc_write(master->base, PMC_MCR, master->id); 293 pmc_update_bits(master->base, PMC_MCR, 294 PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK, 295 (div << MASTER_DIV_SHIFT) | PMC_MCR_CMD | 296 PMC_MCR_ID(master->id)); 297 298 return rrate; 299} 300 301static ulong clk_sama7g5_master_get_rate(struct clk *clk) 302{ 303 struct clk_master *master = to_clk_master(clk); 304 ulong parent_rate = clk_get_parent_rate(clk); 305 unsigned int val; 306 ulong div; 307 308 if (!parent_rate) 309 return 0; 310 311 pmc_write(master->base, PMC_MCR, master->id); 312 pmc_read(master->base, PMC_MCR, &val); 313 314 div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 315 316 if (div == MASTER_PRES_MAX) 317 div = 3; 318 else 319 div = 1 << div; 320 321 return DIV_ROUND_CLOSEST(parent_rate, div); 322} 323 324static const struct clk_ops sama7g5_master_ops = { 325 .enable = clk_sama7g5_master_enable, 326 .disable = clk_sama7g5_master_disable, 327 .set_rate = clk_sama7g5_master_set_rate, 328 .get_rate = clk_sama7g5_master_get_rate, 329 .set_parent = clk_sama7g5_master_set_parent, 330}; 331 332struct clk *at91_clk_sama7g5_register_master(void __iomem *base, 333 const char *name, const char * const *parent_names, 334 int num_parents, const u32 *mux_table, const u32 *clk_mux_table, 335 bool critical, u8 id) 336{ 337 struct clk_master *master; 338 struct clk *clk; 339 u32 val, index; 340 int ret; 341 342 if (!base || !name || !num_parents || !parent_names || 343 !mux_table || !clk_mux_table || id > MASTER_MAX_ID) 344 return ERR_PTR(-EINVAL); 345 346 master = kzalloc(sizeof(*master), GFP_KERNEL); 347 if (!master) 348 return ERR_PTR(-ENOMEM); 349 350 master->base = base; 351 master->id = id; 352 master->mux_table = mux_table; 353 master->clk_mux_table = clk_mux_table; 354 master->num_parents = num_parents; 355 356 pmc_write(master->base, PMC_MCR, master->id); 357 pmc_read(master->base, PMC_MCR, &val); 358 359 index = at91_clk_mux_val_to_index(master->mux_table, 360 master->num_parents, 361 (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT); 362 if (index < 0) { 363 kfree(master); 364 return ERR_PTR(index); 365 } 366 367 clk = &master->clk; 368 clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0); 369 370 ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name, 371 parent_names[index]); 372 if (ret) { 373 kfree(master); 374 clk = ERR_PTR(ret); 375 } 376 377 return clk; 378} 379 380U_BOOT_DRIVER(at91_sama7g5_master_clk) = { 381 .name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, 382 .id = UCLASS_CLK, 383 .ops = &sama7g5_master_ops, 384 .flags = DM_FLAG_PRE_RELOC, 385}; 386 387const struct clk_master_layout at91rm9200_master_layout = { 388 .mask = 0x31F, 389 .pres_shift = 2, 390 .offset = AT91_PMC_MCKR, 391}; 392 393const struct clk_master_layout at91sam9x5_master_layout = { 394 .mask = 0x373, 395 .pres_shift = 4, 396 .offset = AT91_PMC_MCKR, 397}; 398