FILE
clk-h32mx.c
File preview with syntax highlighting.
linux-6.19.14/drivers/clk/at91/clk-h32mx.c
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 21struct 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 28static 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 43static 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 71static 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 89static 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 95struct clk_hw * __init 96at91_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}
~/
Read-only file preview.