clk-h32mx.c (2524B)
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 long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate, 44 unsigned long *parent_rate) 45{ 46 unsigned long div; 47 48 if (rate > *parent_rate) 49 return *parent_rate; 50 div = *parent_rate / 2; 51 if (rate < div) 52 return div; 53 54 if (rate - div < *parent_rate - rate) 55 return div; 56 57 return *parent_rate; 58} 59 60static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate, 61 unsigned long parent_rate) 62{ 63 struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw); 64 u32 mckr = 0; 65 66 if (parent_rate != rate && (parent_rate / 2) != rate) 67 return -EINVAL; 68 69 if ((parent_rate / 2) == rate) 70 mckr = AT91_PMC_H32MXDIV; 71 72 regmap_update_bits(h32mxclk->regmap, AT91_PMC_MCKR, 73 AT91_PMC_H32MXDIV, mckr); 74 75 return 0; 76} 77 78static const struct clk_ops h32mx_ops = { 79 .recalc_rate = clk_sama5d4_h32mx_recalc_rate, 80 .round_rate = clk_sama5d4_h32mx_round_rate, 81 .set_rate = clk_sama5d4_h32mx_set_rate, 82}; 83 84struct clk_hw * __init 85at91_clk_register_h32mx(struct regmap *regmap, const char *name, 86 const char *parent_name) 87{ 88 struct clk_sama5d4_h32mx *h32mxclk; 89 struct clk_init_data init; 90 int ret; 91 92 h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL); 93 if (!h32mxclk) 94 return ERR_PTR(-ENOMEM); 95 96 init.name = name; 97 init.ops = &h32mx_ops; 98 init.parent_names = parent_name ? &parent_name : NULL; 99 init.num_parents = parent_name ? 1 : 0; 100 init.flags = CLK_SET_RATE_GATE; 101 102 h32mxclk->hw.init = &init; 103 h32mxclk->regmap = regmap; 104 105 ret = clk_hw_register(NULL, &h32mxclk->hw); 106 if (ret) { 107 kfree(h32mxclk); 108 return ERR_PTR(ret); 109 } 110 111 return &h32mxclk->hw; 112}