brintos

brintos / linux-shallow public Read only

0
0
Text · 8.6 KiB · 3f929cf Raw
341 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Toshiba Visconti PLL driver4 *5 * Copyright (c) 2021 TOSHIBA CORPORATION6 * Copyright (c) 2021 Toshiba Electronic Devices & Storage Corporation7 *8 * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>9 */10 11#include <linux/bitfield.h>12#include <linux/clk-provider.h>13#include <linux/delay.h>14#include <linux/slab.h>15#include <linux/io.h>16 17#include "pll.h"18 19struct visconti_pll {20	struct clk_hw	hw;21	void __iomem	*pll_base;22	spinlock_t	*lock;23	unsigned long flags;24	const struct visconti_pll_rate_table *rate_table;25	size_t rate_count;26	struct visconti_pll_provider *ctx;27};28 29#define PLL_CONF_REG		0x000030#define PLL_CTRL_REG		0x000431#define PLL_FRACMODE_REG	0x001032#define PLL_INTIN_REG		0x001433#define PLL_FRACIN_REG		0x001834#define PLL_REFDIV_REG		0x001c35#define PLL_POSTDIV_REG		0x002036 37#define PLL_CONFIG_SEL		BIT(0)38#define PLL_PLLEN		BIT(4)39#define PLL_BYPASS		BIT(16)40#define PLL_INTIN_MASK		GENMASK(11, 0)41#define PLL_FRACIN_MASK		GENMASK(23, 0)42#define PLL_REFDIV_MASK		GENMASK(5, 0)43#define PLL_POSTDIV_MASK	GENMASK(2, 0)44 45#define PLL0_FRACMODE_DACEN	BIT(4)46#define PLL0_FRACMODE_DSMEN	BIT(0)47 48#define PLL_CREATE_FRACMODE(table) (table->dacen << 4 | table->dsmen)49#define PLL_CREATE_OSTDIV(table) (table->postdiv2 << 4 | table->postdiv1)50 51static inline struct visconti_pll *to_visconti_pll(struct clk_hw *hw)52{53	return container_of(hw, struct visconti_pll, hw);54}55 56static void visconti_pll_get_params(struct visconti_pll *pll,57				    struct visconti_pll_rate_table *rate_table)58{59	u32 postdiv, val;60 61	val = readl(pll->pll_base + PLL_FRACMODE_REG);62 63	rate_table->dacen = FIELD_GET(PLL0_FRACMODE_DACEN, val);64	rate_table->dsmen = FIELD_GET(PLL0_FRACMODE_DSMEN, val);65 66	rate_table->fracin = readl(pll->pll_base + PLL_FRACIN_REG) & PLL_FRACIN_MASK;67	rate_table->intin = readl(pll->pll_base + PLL_INTIN_REG) & PLL_INTIN_MASK;68	rate_table->refdiv = readl(pll->pll_base + PLL_REFDIV_REG) & PLL_REFDIV_MASK;69 70	postdiv = readl(pll->pll_base + PLL_POSTDIV_REG);71	rate_table->postdiv1 = postdiv & PLL_POSTDIV_MASK;72	rate_table->postdiv2 = (postdiv >> 4) & PLL_POSTDIV_MASK;73}74 75static const struct visconti_pll_rate_table *visconti_get_pll_settings(struct visconti_pll *pll,76								       unsigned long rate)77{78	const struct visconti_pll_rate_table *rate_table = pll->rate_table;79	int i;80 81	for (i = 0; i < pll->rate_count; i++)82		if (rate == rate_table[i].rate)83			return &rate_table[i];84 85	return NULL;86}87 88static unsigned long visconti_get_pll_rate_from_data(struct visconti_pll *pll,89						     const struct visconti_pll_rate_table *rate)90{91	const struct visconti_pll_rate_table *rate_table = pll->rate_table;92	int i;93 94	for (i = 0; i < pll->rate_count; i++)95		if (memcmp(&rate_table[i].dacen, &rate->dacen,96			sizeof(*rate) - sizeof(unsigned long)) == 0)97			return rate_table[i].rate;98 99	/* set default */100	return rate_table[0].rate;101}102 103static long visconti_pll_round_rate(struct clk_hw *hw,104				    unsigned long rate, unsigned long *prate)105{106	struct visconti_pll *pll = to_visconti_pll(hw);107	const struct visconti_pll_rate_table *rate_table = pll->rate_table;108	int i;109 110	/* Assumming rate_table is in descending order */111	for (i = 0; i < pll->rate_count; i++)112		if (rate >= rate_table[i].rate)113			return rate_table[i].rate;114 115	/* return minimum supported value */116	return rate_table[i - 1].rate;117}118 119static unsigned long visconti_pll_recalc_rate(struct clk_hw *hw,120					      unsigned long parent_rate)121{122	struct visconti_pll *pll = to_visconti_pll(hw);123	struct visconti_pll_rate_table rate_table;124 125	memset(&rate_table, 0, sizeof(rate_table));126	visconti_pll_get_params(pll, &rate_table);127 128	return visconti_get_pll_rate_from_data(pll, &rate_table);129}130 131static int visconti_pll_set_params(struct visconti_pll *pll,132				   const struct visconti_pll_rate_table *rate_table)133{134	writel(PLL_CREATE_FRACMODE(rate_table), pll->pll_base + PLL_FRACMODE_REG);135	writel(PLL_CREATE_OSTDIV(rate_table), pll->pll_base + PLL_POSTDIV_REG);136	writel(rate_table->intin, pll->pll_base + PLL_INTIN_REG);137	writel(rate_table->fracin, pll->pll_base + PLL_FRACIN_REG);138	writel(rate_table->refdiv, pll->pll_base + PLL_REFDIV_REG);139 140	return 0;141}142 143static int visconti_pll_set_rate(struct clk_hw *hw, unsigned long rate,144			    unsigned long parent_rate)145{146	struct visconti_pll *pll = to_visconti_pll(hw);147	const struct visconti_pll_rate_table *rate_table;148 149	rate_table = visconti_get_pll_settings(pll, rate);150	if (!rate_table)151		return -EINVAL;152 153	return visconti_pll_set_params(pll, rate_table);154}155 156static int visconti_pll_is_enabled(struct clk_hw *hw)157{158	struct visconti_pll *pll = to_visconti_pll(hw);159	u32 reg;160 161	reg = readl(pll->pll_base + PLL_CTRL_REG);162 163	return (reg & PLL_PLLEN);164}165 166static int visconti_pll_enable(struct clk_hw *hw)167{168	struct visconti_pll *pll = to_visconti_pll(hw);169	const struct visconti_pll_rate_table *rate_table = pll->rate_table;170	unsigned long flags;171	u32 reg;172 173	if (visconti_pll_is_enabled(hw))174		return 0;175 176	spin_lock_irqsave(pll->lock, flags);177 178	writel(PLL_CONFIG_SEL, pll->pll_base + PLL_CONF_REG);179 180	reg = readl(pll->pll_base + PLL_CTRL_REG);181	reg |= PLL_BYPASS;182	writel(reg, pll->pll_base + PLL_CTRL_REG);183 184	visconti_pll_set_params(pll, &rate_table[0]);185 186	reg = readl(pll->pll_base + PLL_CTRL_REG);187	reg &= ~PLL_PLLEN;188	writel(reg, pll->pll_base + PLL_CTRL_REG);189 190	udelay(1);191 192	reg = readl(pll->pll_base + PLL_CTRL_REG);193	reg |= PLL_PLLEN;194	writel(reg, pll->pll_base + PLL_CTRL_REG);195 196	udelay(40);197 198	reg = readl(pll->pll_base + PLL_CTRL_REG);199	reg &= ~PLL_BYPASS;200	writel(reg, pll->pll_base + PLL_CTRL_REG);201 202	spin_unlock_irqrestore(pll->lock, flags);203 204	return 0;205}206 207static void visconti_pll_disable(struct clk_hw *hw)208{209	struct visconti_pll *pll = to_visconti_pll(hw);210	unsigned long flags;211	u32 reg;212 213	if (!visconti_pll_is_enabled(hw))214		return;215 216	spin_lock_irqsave(pll->lock, flags);217 218	writel(PLL_CONFIG_SEL, pll->pll_base + PLL_CONF_REG);219 220	reg = readl(pll->pll_base + PLL_CTRL_REG);221	reg |= PLL_BYPASS;222	writel(reg, pll->pll_base + PLL_CTRL_REG);223 224	reg = readl(pll->pll_base + PLL_CTRL_REG);225	reg &= ~PLL_PLLEN;226	writel(reg, pll->pll_base + PLL_CTRL_REG);227 228	spin_unlock_irqrestore(pll->lock, flags);229}230 231static const struct clk_ops visconti_pll_ops = {232	.enable = visconti_pll_enable,233	.disable = visconti_pll_disable,234	.is_enabled = visconti_pll_is_enabled,235	.round_rate = visconti_pll_round_rate,236	.recalc_rate = visconti_pll_recalc_rate,237	.set_rate = visconti_pll_set_rate,238};239 240static struct clk_hw *visconti_register_pll(struct visconti_pll_provider *ctx,241					    const char *name,242					    const char *parent_name,243					    int offset,244					    const struct visconti_pll_rate_table *rate_table,245					    spinlock_t *lock)246{247	struct clk_init_data init;248	struct visconti_pll *pll;249	struct clk_hw *pll_hw_clk;250	size_t len;251	int ret;252 253	pll = kzalloc(sizeof(*pll), GFP_KERNEL);254	if (!pll)255		return ERR_PTR(-ENOMEM);256 257	init.name = name;258	init.flags = CLK_IGNORE_UNUSED;259	init.parent_names = &parent_name;260	init.num_parents = 1;261 262	for (len = 0; rate_table[len].rate != 0; )263		len++;264	pll->rate_count = len;265	pll->rate_table = kmemdup_array(rate_table,266					pll->rate_count, sizeof(*pll->rate_table),267					GFP_KERNEL);268	WARN(!pll->rate_table, "%s: could not allocate rate table for %s\n", __func__, name);269 270	init.ops = &visconti_pll_ops;271	pll->hw.init = &init;272	pll->pll_base = ctx->reg_base + offset;273	pll->lock = lock;274	pll->ctx = ctx;275 276	pll_hw_clk = &pll->hw;277	ret = clk_hw_register(NULL, &pll->hw);278	if (ret) {279		pr_err("failed to register pll clock %s : %d\n", name, ret);280		kfree(pll->rate_table);281		kfree(pll);282		pll_hw_clk = ERR_PTR(ret);283	}284 285	return pll_hw_clk;286}287 288static void visconti_pll_add_lookup(struct visconti_pll_provider *ctx,289				    struct clk_hw *hw_clk,290				    unsigned int id)291{292	if (id)293		ctx->clk_data.hws[id] = hw_clk;294}295 296void __init visconti_register_plls(struct visconti_pll_provider *ctx,297				   const struct visconti_pll_info *list,298				   unsigned int nr_plls,299				   spinlock_t *lock)300{301	int idx;302 303	for (idx = 0; idx < nr_plls; idx++, list++) {304		struct clk_hw *clk;305 306		clk = visconti_register_pll(ctx,307					    list->name,308					    list->parent,309					    list->base_reg,310					    list->rate_table,311					    lock);312		if (IS_ERR(clk)) {313			pr_err("failed to register clock %s\n", list->name);314			continue;315		}316 317		visconti_pll_add_lookup(ctx, clk, list->id);318	}319}320 321struct visconti_pll_provider * __init visconti_init_pll(struct device_node *np,322							void __iomem *base,323							unsigned long nr_plls)324{325	struct visconti_pll_provider *ctx;326	int i;327 328	ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_plls), GFP_KERNEL);329	if (!ctx)330		return ERR_PTR(-ENOMEM);331 332	ctx->node = np;333	ctx->reg_base = base;334	ctx->clk_data.num = nr_plls;335 336	for (i = 0; i < nr_plls; ++i)337		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);338 339	return ctx;340}341