brintos

brintos / linux-shallow public Read only

0
0
Text · 3.4 KiB · 83ef41d Raw
146 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>4 */5 6#include <linux/clk-provider.h>7#include <linux/err.h>8#include <linux/io.h>9#include <linux/mfd/syscon.h>10#include <linux/of.h>11#include <linux/of_address.h>12#include <linux/regmap.h>13#include <linux/slab.h>14 15#define PMU_XTAL_FREQ_RATIO			0x66c16#define  XTAL_ALP_PER_4ILP			0x00001fff17#define  XTAL_CTL_EN				0x8000000018#define PMU_SLOW_CLK_PERIOD			0x6dc19 20struct bcm53573_ilp {21	struct clk_hw hw;22	struct regmap *regmap;23};24 25static int bcm53573_ilp_enable(struct clk_hw *hw)26{27	struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);28 29	regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0x10199);30	regmap_write(ilp->regmap, 0x674, 0x10000);31 32	return 0;33}34 35static void bcm53573_ilp_disable(struct clk_hw *hw)36{37	struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);38 39	regmap_write(ilp->regmap, PMU_SLOW_CLK_PERIOD, 0);40	regmap_write(ilp->regmap, 0x674, 0);41}42 43static unsigned long bcm53573_ilp_recalc_rate(struct clk_hw *hw,44					      unsigned long parent_rate)45{46	struct bcm53573_ilp *ilp = container_of(hw, struct bcm53573_ilp, hw);47	struct regmap *regmap = ilp->regmap;48	u32 last_val, cur_val;49	int sum = 0, num = 0, loop_num = 0;50	int avg;51 52	/* Enable measurement */53	regmap_write(regmap, PMU_XTAL_FREQ_RATIO, XTAL_CTL_EN);54 55	/* Read initial value */56	regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &last_val);57	last_val &= XTAL_ALP_PER_4ILP;58 59	/*60	 * At minimum we should loop for a bit to let hardware do the61	 * measurement. This isn't very accurate however, so for a better62	 * precision lets try getting 20 different values for and use average.63	 */64	while (num < 20) {65		regmap_read(regmap, PMU_XTAL_FREQ_RATIO, &cur_val);66		cur_val &= XTAL_ALP_PER_4ILP;67 68		if (cur_val != last_val) {69			/* Got different value, use it */70			sum += cur_val;71			num++;72			loop_num = 0;73			last_val = cur_val;74		} else if (++loop_num > 5000) {75			/* Same value over and over, give up */76			sum += cur_val;77			num++;78			break;79		}80 81		cpu_relax();82	}83 84	/* Disable measurement to save power */85	regmap_write(regmap, PMU_XTAL_FREQ_RATIO, 0x0);86 87	avg = sum / num;88 89	return parent_rate * 4 / avg;90}91 92static const struct clk_ops bcm53573_ilp_clk_ops = {93	.enable = bcm53573_ilp_enable,94	.disable = bcm53573_ilp_disable,95	.recalc_rate = bcm53573_ilp_recalc_rate,96};97 98static void bcm53573_ilp_init(struct device_node *np)99{100	struct bcm53573_ilp *ilp;101	struct clk_init_data init = { };102	const char *parent_name;103	int err;104 105	ilp = kzalloc(sizeof(*ilp), GFP_KERNEL);106	if (!ilp)107		return;108 109	parent_name = of_clk_get_parent_name(np, 0);110	if (!parent_name) {111		err = -ENOENT;112		goto err_free_ilp;113	}114 115	ilp->regmap = syscon_node_to_regmap(np->parent);116	if (IS_ERR(ilp->regmap)) {117		err = PTR_ERR(ilp->regmap);118		goto err_free_ilp;119	}120 121	init.name = np->name;122	init.ops = &bcm53573_ilp_clk_ops;123	init.parent_names = &parent_name;124	init.num_parents = 1;125 126	ilp->hw.init = &init;127	err = clk_hw_register(NULL, &ilp->hw);128	if (err)129		goto err_free_ilp;130 131	err = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &ilp->hw);132	if (err)133		goto err_clk_hw_unregister;134 135	return;136 137err_clk_hw_unregister:138	clk_hw_unregister(&ilp->hw);139err_free_ilp:140	kfree(ilp);141	pr_err("Failed to init ILP clock: %d\n", err);142}143 144/* We need it very early for arch code, before device model gets ready */145CLK_OF_DECLARE(bcm53573_ilp_clk, "brcm,bcm53573-ilp", bcm53573_ilp_init);146