brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 0a90452 Raw
263 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Marvell Dove PMU Core PLL divider driver4 *5 * Cleaned up by substantially rewriting, and converted to DT by6 * Russell King.  Origin is not known.7 */8#include <linux/clk-provider.h>9#include <linux/delay.h>10#include <linux/io.h>11#include <linux/kernel.h>12#include <linux/of.h>13#include <linux/of_address.h>14 15#include "dove-divider.h"16 17struct dove_clk {18	const char *name;19	struct clk_hw hw;20	void __iomem *base;21	spinlock_t *lock;22	u8 div_bit_start;23	u8 div_bit_end;24	u8 div_bit_load;25	u8 div_bit_size;26	u32 *divider_table;27};28 29enum {30	DIV_CTRL0 = 0,31	DIV_CTRL1 = 4,32	DIV_CTRL1_N_RESET_MASK = BIT(10),33};34 35#define to_dove_clk(hw) container_of(hw, struct dove_clk, hw)36 37static void dove_load_divider(void __iomem *base, u32 val, u32 mask, u32 load)38{39	u32 v;40 41	v = readl_relaxed(base + DIV_CTRL1) | DIV_CTRL1_N_RESET_MASK;42	writel_relaxed(v, base + DIV_CTRL1);43 44	v = (readl_relaxed(base + DIV_CTRL0) & ~(mask | load)) | val;45	writel_relaxed(v, base + DIV_CTRL0);46	writel_relaxed(v | load, base + DIV_CTRL0);47	ndelay(250);48	writel_relaxed(v, base + DIV_CTRL0);49}50 51static unsigned int dove_get_divider(struct dove_clk *dc)52{53	unsigned int divider;54	u32 val;55 56	val = readl_relaxed(dc->base + DIV_CTRL0);57	val >>= dc->div_bit_start;58 59	divider = val & ~(~0 << dc->div_bit_size);60 61	if (dc->divider_table)62		divider = dc->divider_table[divider];63 64	return divider;65}66 67static int dove_calc_divider(const struct dove_clk *dc, unsigned long rate,68			     unsigned long parent_rate, bool set)69{70	unsigned int divider, max;71 72	divider = DIV_ROUND_CLOSEST(parent_rate, rate);73 74	if (dc->divider_table) {75		unsigned int i;76 77		for (i = 0; dc->divider_table[i]; i++)78			if (divider == dc->divider_table[i]) {79				divider = i;80				break;81			}82 83		if (!dc->divider_table[i])84			return -EINVAL;85	} else {86		max = 1 << dc->div_bit_size;87 88		if (set && (divider == 0 || divider >= max))89			return -EINVAL;90		if (divider >= max)91			divider = max - 1;92		else if (divider == 0)93			divider = 1;94	}95 96	return divider;97}98 99static unsigned long dove_recalc_rate(struct clk_hw *hw, unsigned long parent)100{101	struct dove_clk *dc = to_dove_clk(hw);102	unsigned int divider = dove_get_divider(dc);103	unsigned long rate = DIV_ROUND_CLOSEST(parent, divider);104 105	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",106		 __func__, dc->name, divider, parent, rate);107 108	return rate;109}110 111static long dove_round_rate(struct clk_hw *hw, unsigned long rate,112			    unsigned long *parent)113{114	struct dove_clk *dc = to_dove_clk(hw);115	unsigned long parent_rate = *parent;116	int divider;117 118	divider = dove_calc_divider(dc, rate, parent_rate, false);119	if (divider < 0)120		return divider;121 122	rate = DIV_ROUND_CLOSEST(parent_rate, divider);123 124	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",125		 __func__, dc->name, divider, parent_rate, rate);126 127	return rate;128}129 130static int dove_set_clock(struct clk_hw *hw, unsigned long rate,131			  unsigned long parent_rate)132{133	struct dove_clk *dc = to_dove_clk(hw);134	u32 mask, load, div;135	int divider;136 137	divider = dove_calc_divider(dc, rate, parent_rate, true);138	if (divider < 0)139		return divider;140 141	pr_debug("%s(): %s divider=%u parent=%lu rate=%lu\n",142		 __func__, dc->name, divider, parent_rate, rate);143 144	div = (u32)divider << dc->div_bit_start;145	mask = ~(~0 << dc->div_bit_size) << dc->div_bit_start;146	load = BIT(dc->div_bit_load);147 148	spin_lock(dc->lock);149	dove_load_divider(dc->base, div, mask, load);150	spin_unlock(dc->lock);151 152	return 0;153}154 155static const struct clk_ops dove_divider_ops = {156	.set_rate	= dove_set_clock,157	.round_rate	= dove_round_rate,158	.recalc_rate	= dove_recalc_rate,159};160 161static struct clk *clk_register_dove_divider(struct device *dev,162	struct dove_clk *dc, const char **parent_names, size_t num_parents,163	void __iomem *base)164{165	char name[32];166	struct clk_init_data init = {167		.name = name,168		.ops = &dove_divider_ops,169		.parent_names = parent_names,170		.num_parents = num_parents,171	};172 173	strscpy(name, dc->name, sizeof(name));174 175	dc->hw.init = &init;176	dc->base = base;177	dc->div_bit_size = dc->div_bit_end - dc->div_bit_start + 1;178 179	return clk_register(dev, &dc->hw);180}181 182static DEFINE_SPINLOCK(dove_divider_lock);183 184static u32 axi_divider[] = {-1, 2, 1, 3, 4, 6, 5, 7, 8, 10, 9, 0};185 186static struct dove_clk dove_hw_clocks[4] = {187	{188		.name = "axi",189		.lock = &dove_divider_lock,190		.div_bit_start = 1,191		.div_bit_end = 6,192		.div_bit_load = 7,193		.divider_table = axi_divider,194	}, {195		.name = "gpu",196		.lock = &dove_divider_lock,197		.div_bit_start = 8,198		.div_bit_end = 13,199		.div_bit_load = 14,200	}, {201		.name = "vmeta",202		.lock = &dove_divider_lock,203		.div_bit_start = 15,204		.div_bit_end = 20,205		.div_bit_load = 21,206	}, {207		.name = "lcd",208		.lock = &dove_divider_lock,209		.div_bit_start = 22,210		.div_bit_end = 27,211		.div_bit_load = 28,212	},213};214 215static const char *core_pll[] = {216	"core-pll",217};218 219static int dove_divider_init(struct device *dev, void __iomem *base,220	struct clk **clks)221{222	struct clk *clk;223	int i;224 225	/*226	 * Create the core PLL clock.  We treat this as a fixed rate227	 * clock as we don't know any better, and documentation is sparse.228	 */229	clk = clk_register_fixed_rate(dev, core_pll[0], NULL, 0, 2000000000UL);230	if (IS_ERR(clk))231		return PTR_ERR(clk);232 233	for (i = 0; i < ARRAY_SIZE(dove_hw_clocks); i++)234		clks[i] = clk_register_dove_divider(dev, &dove_hw_clocks[i],235						    core_pll,236						    ARRAY_SIZE(core_pll), base);237 238	return 0;239}240 241static struct clk *dove_divider_clocks[4];242 243static struct clk_onecell_data dove_divider_data = {244	.clks = dove_divider_clocks,245	.clk_num = ARRAY_SIZE(dove_divider_clocks),246};247 248void __init dove_divider_clk_init(struct device_node *np)249{250	void __iomem *base;251 252	base = of_iomap(np, 0);253	if (WARN_ON(!base))254		return;255 256	if (WARN_ON(dove_divider_init(NULL, base, dove_divider_clocks))) {257		iounmap(base);258		return;259	}260 261	of_clk_add_provider(np, of_clk_src_onecell_get, &dove_divider_data);262}263