1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * clk-h32mx.c 4 * 5 * Copyright (C) 2014 Atmel 6 * 7 * Alexandre Belloni <alexandre.belloni@free-electrons.com> 8 */ 9 10 #include <linux/clk-provider.h> 11 #include <linux/clkdev.h> 12 #include <linux/clk/at91_pmc.h> 13 #include <linux/of.h> 14 #include <linux/regmap.h> 15 #include <linux/mfd/syscon.h> 16 17 #include "pmc.h" 18 19 #define H32MX_MAX_FREQ 90000000 20 21 struct clk_sama5d4_h32mx { 22 struct clk_hw hw; 23 struct regmap *regmap; 24 }; 25 26 #define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw) 27 28 static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw, 29 unsigned long parent_rate) 30 { 31 struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); 32 unsigned int mckr; 33 34 regmap_read(h32mxclk->regmap, AT91_PMC_MCKR, &mckr); 35 if (mckr & AT91_PMC_H32MXDIV) 36 return parent_rate / 2; 37 38 if (parent_rate > H32MX_MAX_FREQ) 39 pr_warn("H32MX clock is too fast\n"); 40 return parent_rate; 41 } 42 43 static int clk_sama5d4_h32mx_determine_rate(struct clk_hw *hw, 44 struct clk_rate_request *req) 45 { 46 unsigned long div; 47 48 if (req->rate > req->best_parent_rate) { 49 req->rate = req->best_parent_rate; 50 51 return 0; 52 } 53 div = req->best_parent_rate / 2; 54 if (req->rate < div) { 55 req->rate = div; 56 57 return 0; 58 } 59 60 if (req->rate - div < req->best_parent_rate - req->rate) { 61 req->rate = div; 62 63 return 0; 64 } 65 66 req->rate = req->best_parent_rate; 67 68 return 0; 69 } 70 71 static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate, 72 unsigned long parent_rate) 73 { 74 struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); 75 u32 mckr = 0; 76 77 if (parent_rate != rate && (parent_rate / 2) != rate) 78 return -EINVAL; 79 80 if ((parent_rate / 2) == rate) 81 mckr = AT91_PMC_H32MXDIV; 82 83 regmap_update_bits(h32mxclk->regmap, AT91_PMC_MCKR, 84 AT91_PMC_H32MXDIV, mckr); 85 86 return 0; 87 } 88 89 static const struct clk_ops h32mx_ops = { 90 .recalc_rate = clk_sama5d4_h32mx_recalc_rate, 91 .determine_rate = clk_sama5d4_h32mx_determine_rate, 92 .set_rate = clk_sama5d4_h32mx_set_rate, 93 }; 94 95 struct clk_hw * __init 96 at91_clk_register_h32mx(struct regmap *regmap, const char *name, 97 const char *parent_name) 98 { 99 struct clk_sama5d4_h32mx *h32mxclk; 100 struct clk_init_data init; 101 int ret; 102 103 h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL); 104 if (!h32mxclk) 105 return ERR_PTR(-ENOMEM); 106 107 init.name = name; 108 init.ops = &h32mx_ops; 109 init.parent_names = parent_name ? &parent_name : NULL; 110 init.num_parents = parent_name ? 1 : 0; 111 init.flags = CLK_SET_RATE_GATE; 112 113 h32mxclk->hw.init = &init; 114 h32mxclk->regmap = regmap; 115 116 ret = clk_hw_register(NULL, &h32mxclk->hw); 117 if (ret) { 118 kfree(h32mxclk); 119 return ERR_PTR(ret); 120 } 121 122 return &h32mxclk->hw; 123 } 124