brintos

brintos / linux-shallow public Read only

0
0
Text · 2.5 KiB · 1e6c12e Raw
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