1e442d234SBoris BREZILLON /* 2e442d234SBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 3e442d234SBoris BREZILLON * 4e442d234SBoris BREZILLON * This program is free software; you can redistribute it and/or modify 5e442d234SBoris BREZILLON * it under the terms of the GNU General Public License as published by 6e442d234SBoris BREZILLON * the Free Software Foundation; either version 2 of the License, or 7e442d234SBoris BREZILLON * (at your option) any later version. 8e442d234SBoris BREZILLON * 9e442d234SBoris BREZILLON */ 10e442d234SBoris BREZILLON 11e442d234SBoris BREZILLON #include <linux/clk-provider.h> 12e442d234SBoris BREZILLON #include <linux/clkdev.h> 13e442d234SBoris BREZILLON #include <linux/clk/at91_pmc.h> 14e442d234SBoris BREZILLON #include <linux/of.h> 151bdf0232SBoris Brezillon #include <linux/mfd/syscon.h> 161bdf0232SBoris Brezillon #include <linux/regmap.h> 17e442d234SBoris BREZILLON 18e442d234SBoris BREZILLON #include "pmc.h" 19e442d234SBoris BREZILLON 20e442d234SBoris BREZILLON #define MASTER_PRES_MASK 0x7 21e442d234SBoris BREZILLON #define MASTER_PRES_MAX MASTER_PRES_MASK 22e442d234SBoris BREZILLON #define MASTER_DIV_SHIFT 8 23e442d234SBoris BREZILLON #define MASTER_DIV_MASK 0x3 24e442d234SBoris BREZILLON 25e442d234SBoris BREZILLON #define to_clk_master(hw) container_of(hw, struct clk_master, hw) 26e442d234SBoris BREZILLON 27e442d234SBoris BREZILLON struct clk_master { 28e442d234SBoris BREZILLON struct clk_hw hw; 291bdf0232SBoris Brezillon struct regmap *regmap; 30e442d234SBoris BREZILLON const struct clk_master_layout *layout; 31e442d234SBoris BREZILLON const struct clk_master_characteristics *characteristics; 32e5be5370SAlexandre Belloni u32 mckr; 33e442d234SBoris BREZILLON }; 34e442d234SBoris BREZILLON 351bdf0232SBoris Brezillon static inline bool clk_master_ready(struct regmap *regmap) 361bdf0232SBoris Brezillon { 371bdf0232SBoris Brezillon unsigned int status; 381bdf0232SBoris Brezillon 391bdf0232SBoris Brezillon regmap_read(regmap, AT91_PMC_SR, &status); 401bdf0232SBoris Brezillon 411bdf0232SBoris Brezillon return status & AT91_PMC_MCKRDY ? 1 : 0; 421bdf0232SBoris Brezillon } 431bdf0232SBoris Brezillon 44e442d234SBoris BREZILLON static int clk_master_prepare(struct clk_hw *hw) 45e442d234SBoris BREZILLON { 46e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 47e442d234SBoris BREZILLON 4899a81706SAlexandre Belloni while (!clk_master_ready(master->regmap)) 4999a81706SAlexandre Belloni cpu_relax(); 50e442d234SBoris BREZILLON 51e442d234SBoris BREZILLON return 0; 52e442d234SBoris BREZILLON } 53e442d234SBoris BREZILLON 54e442d234SBoris BREZILLON static int clk_master_is_prepared(struct clk_hw *hw) 55e442d234SBoris BREZILLON { 56e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 57e442d234SBoris BREZILLON 581bdf0232SBoris Brezillon return clk_master_ready(master->regmap); 59e442d234SBoris BREZILLON } 60e442d234SBoris BREZILLON 61e442d234SBoris BREZILLON static unsigned long clk_master_recalc_rate(struct clk_hw *hw, 62e442d234SBoris BREZILLON unsigned long parent_rate) 63e442d234SBoris BREZILLON { 64e442d234SBoris BREZILLON u8 pres; 65e442d234SBoris BREZILLON u8 div; 66e442d234SBoris BREZILLON unsigned long rate = parent_rate; 67e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 68e442d234SBoris BREZILLON const struct clk_master_layout *layout = master->layout; 69e442d234SBoris BREZILLON const struct clk_master_characteristics *characteristics = 70e442d234SBoris BREZILLON master->characteristics; 711bdf0232SBoris Brezillon unsigned int mckr; 72e442d234SBoris BREZILLON 73e5be5370SAlexandre Belloni regmap_read(master->regmap, master->layout->offset, &mckr); 741bdf0232SBoris Brezillon mckr &= layout->mask; 75e442d234SBoris BREZILLON 761bdf0232SBoris Brezillon pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; 771bdf0232SBoris Brezillon div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 78e442d234SBoris BREZILLON 79e442d234SBoris BREZILLON if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) 80e442d234SBoris BREZILLON rate /= 3; 81e442d234SBoris BREZILLON else 82e442d234SBoris BREZILLON rate >>= pres; 83e442d234SBoris BREZILLON 84e442d234SBoris BREZILLON rate /= characteristics->divisors[div]; 85e442d234SBoris BREZILLON 86e442d234SBoris BREZILLON if (rate < characteristics->output.min) 87e442d234SBoris BREZILLON pr_warn("master clk is underclocked"); 88e442d234SBoris BREZILLON else if (rate > characteristics->output.max) 89e442d234SBoris BREZILLON pr_warn("master clk is overclocked"); 90e442d234SBoris BREZILLON 91e442d234SBoris BREZILLON return rate; 92e442d234SBoris BREZILLON } 93e442d234SBoris BREZILLON 94e442d234SBoris BREZILLON static u8 clk_master_get_parent(struct clk_hw *hw) 95e442d234SBoris BREZILLON { 96e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 971bdf0232SBoris Brezillon unsigned int mckr; 98e442d234SBoris BREZILLON 99e5be5370SAlexandre Belloni regmap_read(master->regmap, master->layout->offset, &mckr); 1001bdf0232SBoris Brezillon 1011bdf0232SBoris Brezillon return mckr & AT91_PMC_CSS; 102e442d234SBoris BREZILLON } 103e442d234SBoris BREZILLON 104e442d234SBoris BREZILLON static const struct clk_ops master_ops = { 105e442d234SBoris BREZILLON .prepare = clk_master_prepare, 106e442d234SBoris BREZILLON .is_prepared = clk_master_is_prepared, 107e442d234SBoris BREZILLON .recalc_rate = clk_master_recalc_rate, 108e442d234SBoris BREZILLON .get_parent = clk_master_get_parent, 109e442d234SBoris BREZILLON }; 110e442d234SBoris BREZILLON 111b2e39dc0SAlexandre Belloni struct clk_hw * __init 11299a81706SAlexandre Belloni at91_clk_register_master(struct regmap *regmap, 113e442d234SBoris BREZILLON const char *name, int num_parents, 114e442d234SBoris BREZILLON const char **parent_names, 115e442d234SBoris BREZILLON const struct clk_master_layout *layout, 116e442d234SBoris BREZILLON const struct clk_master_characteristics *characteristics) 117e442d234SBoris BREZILLON { 118e442d234SBoris BREZILLON struct clk_master *master; 119e442d234SBoris BREZILLON struct clk_init_data init; 120f5644f10SStephen Boyd struct clk_hw *hw; 121f5644f10SStephen Boyd int ret; 122e442d234SBoris BREZILLON 1231bdf0232SBoris Brezillon if (!name || !num_parents || !parent_names) 124e442d234SBoris BREZILLON return ERR_PTR(-EINVAL); 125e442d234SBoris BREZILLON 126e442d234SBoris BREZILLON master = kzalloc(sizeof(*master), GFP_KERNEL); 127e442d234SBoris BREZILLON if (!master) 128e442d234SBoris BREZILLON return ERR_PTR(-ENOMEM); 129e442d234SBoris BREZILLON 130e442d234SBoris BREZILLON init.name = name; 131e442d234SBoris BREZILLON init.ops = &master_ops; 132e442d234SBoris BREZILLON init.parent_names = parent_names; 133e442d234SBoris BREZILLON init.num_parents = num_parents; 134e442d234SBoris BREZILLON init.flags = 0; 135e442d234SBoris BREZILLON 136e442d234SBoris BREZILLON master->hw.init = &init; 137e442d234SBoris BREZILLON master->layout = layout; 138e442d234SBoris BREZILLON master->characteristics = characteristics; 1391bdf0232SBoris Brezillon master->regmap = regmap; 140e442d234SBoris BREZILLON 141f5644f10SStephen Boyd hw = &master->hw; 142f5644f10SStephen Boyd ret = clk_hw_register(NULL, &master->hw); 143f5644f10SStephen Boyd if (ret) { 144e442d234SBoris BREZILLON kfree(master); 145f5644f10SStephen Boyd hw = ERR_PTR(ret); 146c76a024eSDavid Dueck } 147e442d234SBoris BREZILLON 148f5644f10SStephen Boyd return hw; 149e442d234SBoris BREZILLON } 150e442d234SBoris BREZILLON 151b2e39dc0SAlexandre Belloni const struct clk_master_layout at91rm9200_master_layout = { 152e442d234SBoris BREZILLON .mask = 0x31F, 153e442d234SBoris BREZILLON .pres_shift = 2, 154e5be5370SAlexandre Belloni .offset = AT91_PMC_MCKR, 155e442d234SBoris BREZILLON }; 156e442d234SBoris BREZILLON 157b2e39dc0SAlexandre Belloni const struct clk_master_layout at91sam9x5_master_layout = { 158e442d234SBoris BREZILLON .mask = 0x373, 159e442d234SBoris BREZILLON .pres_shift = 4, 160e5be5370SAlexandre Belloni .offset = AT91_PMC_MCKR, 161e442d234SBoris BREZILLON }; 162