brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 277a240 Raw
289 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2015 Maxime Ripard4 *5 * Maxime Ripard <maxime.ripard@free-electrons.com>6 */7 8#include <linux/clk-provider.h>9#include <linux/io.h>10#include <linux/of.h>11#include <linux/of_address.h>12#include <linux/slab.h>13#include <linux/spinlock.h>14 15#define TCON_CH1_SCLK2_PARENTS		416 17#define TCON_CH1_SCLK2_GATE_BIT		BIT(31)18#define TCON_CH1_SCLK2_MUX_MASK		319#define TCON_CH1_SCLK2_MUX_SHIFT	2420#define TCON_CH1_SCLK2_DIV_MASK		0xf21#define TCON_CH1_SCLK2_DIV_SHIFT	022 23#define TCON_CH1_SCLK1_GATE_BIT		BIT(15)24#define TCON_CH1_SCLK1_HALF_BIT		BIT(11)25 26struct tcon_ch1_clk {27	struct clk_hw	hw;28	spinlock_t	lock;29	void __iomem	*reg;30};31 32#define hw_to_tclk(hw)	container_of(hw, struct tcon_ch1_clk, hw)33 34static void tcon_ch1_disable(struct clk_hw *hw)35{36	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);37	unsigned long flags;38	u32 reg;39 40	spin_lock_irqsave(&tclk->lock, flags);41	reg = readl(tclk->reg);42	reg &= ~(TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);43	writel(reg, tclk->reg);44	spin_unlock_irqrestore(&tclk->lock, flags);45}46 47static int tcon_ch1_enable(struct clk_hw *hw)48{49	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);50	unsigned long flags;51	u32 reg;52 53	spin_lock_irqsave(&tclk->lock, flags);54	reg = readl(tclk->reg);55	reg |= TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT;56	writel(reg, tclk->reg);57	spin_unlock_irqrestore(&tclk->lock, flags);58 59	return 0;60}61 62static int tcon_ch1_is_enabled(struct clk_hw *hw)63{64	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);65	u32 reg;66 67	reg = readl(tclk->reg);68	return reg & (TCON_CH1_SCLK2_GATE_BIT | TCON_CH1_SCLK1_GATE_BIT);69}70 71static u8 tcon_ch1_get_parent(struct clk_hw *hw)72{73	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);74	u32 reg;75 76	reg = readl(tclk->reg) >> TCON_CH1_SCLK2_MUX_SHIFT;77	reg &= reg >> TCON_CH1_SCLK2_MUX_MASK;78 79	return reg;80}81 82static int tcon_ch1_set_parent(struct clk_hw *hw, u8 index)83{84	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);85	unsigned long flags;86	u32 reg;87 88	spin_lock_irqsave(&tclk->lock, flags);89	reg = readl(tclk->reg);90	reg &= ~(TCON_CH1_SCLK2_MUX_MASK << TCON_CH1_SCLK2_MUX_SHIFT);91	reg |= index << TCON_CH1_SCLK2_MUX_SHIFT;92	writel(reg, tclk->reg);93	spin_unlock_irqrestore(&tclk->lock, flags);94 95	return 0;96};97 98static unsigned long tcon_ch1_calc_divider(unsigned long rate,99					   unsigned long parent_rate,100					   u8 *div,101					   bool *half)102{103	unsigned long best_rate = 0;104	u8 best_m = 0, m;105	bool is_double;106 107	for (m = 1; m < 16; m++) {108		u8 d;109 110		for (d = 1; d < 3; d++) {111			unsigned long tmp_rate;112 113			tmp_rate = parent_rate / m / d;114 115			if (tmp_rate > rate)116				continue;117 118			if (!best_rate ||119			    (rate - tmp_rate) < (rate - best_rate)) {120				best_rate = tmp_rate;121				best_m = m;122				is_double = d;123			}124		}125	}126 127	if (div && half) {128		*div = best_m;129		*half = is_double;130	}131 132	return best_rate;133}134 135static int tcon_ch1_determine_rate(struct clk_hw *hw,136				   struct clk_rate_request *req)137{138	long best_rate = -EINVAL;139	int i;140 141	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {142		unsigned long parent_rate;143		unsigned long tmp_rate;144		struct clk_hw *parent;145 146		parent = clk_hw_get_parent_by_index(hw, i);147		if (!parent)148			continue;149 150		parent_rate = clk_hw_get_rate(parent);151 152		tmp_rate = tcon_ch1_calc_divider(req->rate, parent_rate,153						 NULL, NULL);154 155		if (best_rate < 0 ||156		    (req->rate - tmp_rate) < (req->rate - best_rate)) {157			best_rate = tmp_rate;158			req->best_parent_rate = parent_rate;159			req->best_parent_hw = parent;160		}161	}162 163	if (best_rate < 0)164		return best_rate;165 166	req->rate = best_rate;167	return 0;168}169 170static unsigned long tcon_ch1_recalc_rate(struct clk_hw *hw,171					  unsigned long parent_rate)172{173	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);174	u32 reg;175 176	reg = readl(tclk->reg);177 178	parent_rate /= (reg & TCON_CH1_SCLK2_DIV_MASK) + 1;179 180	if (reg & TCON_CH1_SCLK1_HALF_BIT)181		parent_rate /= 2;182 183	return parent_rate;184}185 186static int tcon_ch1_set_rate(struct clk_hw *hw, unsigned long rate,187			     unsigned long parent_rate)188{189	struct tcon_ch1_clk *tclk = hw_to_tclk(hw);190	unsigned long flags;191	bool half;192	u8 div_m;193	u32 reg;194 195	tcon_ch1_calc_divider(rate, parent_rate, &div_m, &half);196 197	spin_lock_irqsave(&tclk->lock, flags);198	reg = readl(tclk->reg);199	reg &= ~(TCON_CH1_SCLK2_DIV_MASK | TCON_CH1_SCLK1_HALF_BIT);200	reg |= (div_m - 1) & TCON_CH1_SCLK2_DIV_MASK;201 202	if (half)203		reg |= TCON_CH1_SCLK1_HALF_BIT;204 205	writel(reg, tclk->reg);206	spin_unlock_irqrestore(&tclk->lock, flags);207 208	return 0;209}210 211static const struct clk_ops tcon_ch1_ops = {212	.disable	= tcon_ch1_disable,213	.enable		= tcon_ch1_enable,214	.is_enabled	= tcon_ch1_is_enabled,215 216	.get_parent	= tcon_ch1_get_parent,217	.set_parent	= tcon_ch1_set_parent,218 219	.determine_rate	= tcon_ch1_determine_rate,220	.recalc_rate	= tcon_ch1_recalc_rate,221	.set_rate	= tcon_ch1_set_rate,222};223 224static void __init tcon_ch1_setup(struct device_node *node)225{226	const char *parents[TCON_CH1_SCLK2_PARENTS];227	const char *clk_name = node->name;228	struct clk_init_data init;229	struct tcon_ch1_clk *tclk;230	struct resource res;231	struct clk *clk;232	void __iomem *reg;233	int ret;234 235	of_property_read_string(node, "clock-output-names", &clk_name);236 237	reg = of_io_request_and_map(node, 0, of_node_full_name(node));238	if (IS_ERR(reg)) {239		pr_err("%s: Could not map the clock registers\n", clk_name);240		return;241	}242 243	ret = of_clk_parent_fill(node, parents, TCON_CH1_SCLK2_PARENTS);244	if (ret != TCON_CH1_SCLK2_PARENTS) {245		pr_err("%s Could not retrieve the parents\n", clk_name);246		goto err_unmap;247	}248 249	tclk = kzalloc(sizeof(*tclk), GFP_KERNEL);250	if (!tclk)251		goto err_unmap;252 253	init.name = clk_name;254	init.ops = &tcon_ch1_ops;255	init.parent_names = parents;256	init.num_parents = TCON_CH1_SCLK2_PARENTS;257	init.flags = CLK_SET_RATE_PARENT;258 259	tclk->reg = reg;260	tclk->hw.init = &init;261	spin_lock_init(&tclk->lock);262 263	clk = clk_register(NULL, &tclk->hw);264	if (IS_ERR(clk)) {265		pr_err("%s: Couldn't register the clock\n", clk_name);266		goto err_free_data;267	}268 269	ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);270	if (ret) {271		pr_err("%s: Couldn't register our clock provider\n", clk_name);272		goto err_unregister_clk;273	}274 275	return;276 277err_unregister_clk:278	clk_unregister(clk);279err_free_data:280	kfree(tclk);281err_unmap:282	iounmap(reg);283	of_address_to_resource(node, 0, &res);284	release_mem_region(res.start, resource_size(&res));285}286 287CLK_OF_DECLARE(tcon_ch1, "allwinner,sun4i-a10-tcon-ch1-clk",288	       tcon_ch1_setup);289