113 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * clk-h32mx.c4 *5 * Copyright (C) 2014 Atmel6 *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 9000000020 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 * __init85at91_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}113