brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · afa5760 Raw
385 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2013 Samsung Electronics Co., Ltd.4 * Copyright (c) 2013 Linaro Ltd.5 * Author: Thomas Abraham <thomas.ab@samsung.com>6 *7 * This file includes utility functions to register clocks to common8 * clock framework for Samsung platforms.9*/10 11#include <linux/slab.h>12#include <linux/clkdev.h>13#include <linux/clk.h>14#include <linux/clk-provider.h>15#include <linux/io.h>16#include <linux/of_address.h>17#include <linux/syscore_ops.h>18 19#include "clk.h"20 21static LIST_HEAD(clock_reg_cache_list);22 23void samsung_clk_save(void __iomem *base,24				    struct samsung_clk_reg_dump *rd,25				    unsigned int num_regs)26{27	for (; num_regs > 0; --num_regs, ++rd)28		rd->value = readl(base + rd->offset);29}30 31void samsung_clk_restore(void __iomem *base,32				      const struct samsung_clk_reg_dump *rd,33				      unsigned int num_regs)34{35	for (; num_regs > 0; --num_regs, ++rd)36		writel(rd->value, base + rd->offset);37}38 39struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(40						const unsigned long *rdump,41						unsigned long nr_rdump)42{43	struct samsung_clk_reg_dump *rd;44	unsigned int i;45 46	rd = kcalloc(nr_rdump, sizeof(*rd), GFP_KERNEL);47	if (!rd)48		return NULL;49 50	for (i = 0; i < nr_rdump; ++i)51		rd[i].offset = rdump[i];52 53	return rd;54}55 56/**57 * samsung_clk_init() - Create and initialize a clock provider object58 * @dev:	CMU device to enable runtime PM, or NULL if RPM is not needed59 * @base:	Start address (mapped) of CMU registers60 * @nr_clks:	Total clock count to allocate in clock provider object61 *62 * Setup the essentials required to support clock lookup using Common Clock63 * Framework.64 *65 * Return: Allocated and initialized clock provider object.66 */67struct samsung_clk_provider * __init samsung_clk_init(struct device *dev,68			void __iomem *base, unsigned long nr_clks)69{70	struct samsung_clk_provider *ctx;71	int i;72 73	ctx = kzalloc(struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL);74	if (!ctx)75		panic("could not allocate clock provider context.\n");76 77	for (i = 0; i < nr_clks; ++i)78		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);79 80	ctx->dev = dev;81	ctx->reg_base = base;82	ctx->clk_data.num = nr_clks;83	spin_lock_init(&ctx->lock);84 85	return ctx;86}87 88void __init samsung_clk_of_add_provider(struct device_node *np,89				struct samsung_clk_provider *ctx)90{91	if (np) {92		if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get,93					&ctx->clk_data))94			panic("could not register clk provider\n");95	}96}97 98/* add a clock instance to the clock lookup table used for dt based lookup */99void samsung_clk_add_lookup(struct samsung_clk_provider *ctx,100			    struct clk_hw *clk_hw, unsigned int id)101{102	if (id)103		ctx->clk_data.hws[id] = clk_hw;104}105 106/* register a list of aliases */107void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,108				const struct samsung_clock_alias *list,109				unsigned int nr_clk)110{111	struct clk_hw *clk_hw;112	unsigned int idx, ret;113 114	for (idx = 0; idx < nr_clk; idx++, list++) {115		if (!list->id) {116			pr_err("%s: clock id missing for index %d\n", __func__,117				idx);118			continue;119		}120 121		clk_hw = ctx->clk_data.hws[list->id];122		if (!clk_hw) {123			pr_err("%s: failed to find clock %d\n", __func__,124				list->id);125			continue;126		}127 128		ret = clk_hw_register_clkdev(clk_hw, list->alias,129					     list->dev_name);130		if (ret)131			pr_err("%s: failed to register lookup %s\n",132					__func__, list->alias);133	}134}135 136/* register a list of fixed clocks */137void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,138		const struct samsung_fixed_rate_clock *list,139		unsigned int nr_clk)140{141	struct clk_hw *clk_hw;142	unsigned int idx;143 144	for (idx = 0; idx < nr_clk; idx++, list++) {145		clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name,146			list->parent_name, list->flags, list->fixed_rate);147		if (IS_ERR(clk_hw)) {148			pr_err("%s: failed to register clock %s\n", __func__,149				list->name);150			continue;151		}152 153		samsung_clk_add_lookup(ctx, clk_hw, list->id);154	}155}156 157/* register a list of fixed factor clocks */158void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,159		const struct samsung_fixed_factor_clock *list, unsigned int nr_clk)160{161	struct clk_hw *clk_hw;162	unsigned int idx;163 164	for (idx = 0; idx < nr_clk; idx++, list++) {165		clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name,166			list->parent_name, list->flags, list->mult, list->div);167		if (IS_ERR(clk_hw)) {168			pr_err("%s: failed to register clock %s\n", __func__,169				list->name);170			continue;171		}172 173		samsung_clk_add_lookup(ctx, clk_hw, list->id);174	}175}176 177/* register a list of mux clocks */178void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,179				const struct samsung_mux_clock *list,180				unsigned int nr_clk)181{182	struct clk_hw *clk_hw;183	unsigned int idx;184 185	for (idx = 0; idx < nr_clk; idx++, list++) {186		clk_hw = clk_hw_register_mux(ctx->dev, list->name,187			list->parent_names, list->num_parents, list->flags,188			ctx->reg_base + list->offset,189			list->shift, list->width, list->mux_flags, &ctx->lock);190		if (IS_ERR(clk_hw)) {191			pr_err("%s: failed to register clock %s\n", __func__,192				list->name);193			continue;194		}195 196		samsung_clk_add_lookup(ctx, clk_hw, list->id);197	}198}199 200/* register a list of div clocks */201void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,202				const struct samsung_div_clock *list,203				unsigned int nr_clk)204{205	struct clk_hw *clk_hw;206	unsigned int idx;207 208	for (idx = 0; idx < nr_clk; idx++, list++) {209		if (list->table)210			clk_hw = clk_hw_register_divider_table(ctx->dev,211				list->name, list->parent_name, list->flags,212				ctx->reg_base + list->offset,213				list->shift, list->width, list->div_flags,214				list->table, &ctx->lock);215		else216			clk_hw = clk_hw_register_divider(ctx->dev, list->name,217				list->parent_name, list->flags,218				ctx->reg_base + list->offset, list->shift,219				list->width, list->div_flags, &ctx->lock);220		if (IS_ERR(clk_hw)) {221			pr_err("%s: failed to register clock %s\n", __func__,222				list->name);223			continue;224		}225 226		samsung_clk_add_lookup(ctx, clk_hw, list->id);227	}228}229 230/* register a list of gate clocks */231void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,232				const struct samsung_gate_clock *list,233				unsigned int nr_clk)234{235	struct clk_hw *clk_hw;236	unsigned int idx;237 238	for (idx = 0; idx < nr_clk; idx++, list++) {239		clk_hw = clk_hw_register_gate(ctx->dev, list->name, list->parent_name,240				list->flags, ctx->reg_base + list->offset,241				list->bit_idx, list->gate_flags, &ctx->lock);242		if (IS_ERR(clk_hw)) {243			pr_err("%s: failed to register clock %s\n", __func__,244				list->name);245			continue;246		}247 248		samsung_clk_add_lookup(ctx, clk_hw, list->id);249	}250}251 252/*253 * obtain the clock speed of all external fixed clock sources from device254 * tree and register it255 */256void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,257			struct samsung_fixed_rate_clock *fixed_rate_clk,258			unsigned int nr_fixed_rate_clk,259			const struct of_device_id *clk_matches)260{261	const struct of_device_id *match;262	struct device_node *clk_np;263	u32 freq;264 265	for_each_matching_node_and_match(clk_np, clk_matches, &match) {266		if (of_property_read_u32(clk_np, "clock-frequency", &freq))267			continue;268		fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;269	}270	samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);271}272 273#ifdef CONFIG_PM_SLEEP274static int samsung_clk_suspend(void)275{276	struct samsung_clock_reg_cache *reg_cache;277 278	list_for_each_entry(reg_cache, &clock_reg_cache_list, node) {279		samsung_clk_save(reg_cache->reg_base, reg_cache->rdump,280				reg_cache->rd_num);281		samsung_clk_restore(reg_cache->reg_base, reg_cache->rsuspend,282				reg_cache->rsuspend_num);283	}284	return 0;285}286 287static void samsung_clk_resume(void)288{289	struct samsung_clock_reg_cache *reg_cache;290 291	list_for_each_entry(reg_cache, &clock_reg_cache_list, node)292		samsung_clk_restore(reg_cache->reg_base, reg_cache->rdump,293				reg_cache->rd_num);294}295 296static struct syscore_ops samsung_clk_syscore_ops = {297	.suspend = samsung_clk_suspend,298	.resume = samsung_clk_resume,299};300 301void samsung_clk_extended_sleep_init(void __iomem *reg_base,302			const unsigned long *rdump,303			unsigned long nr_rdump,304			const struct samsung_clk_reg_dump *rsuspend,305			unsigned long nr_rsuspend)306{307	struct samsung_clock_reg_cache *reg_cache;308 309	reg_cache = kzalloc(sizeof(struct samsung_clock_reg_cache),310			GFP_KERNEL);311	if (!reg_cache)312		panic("could not allocate register reg_cache.\n");313	reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);314 315	if (!reg_cache->rdump)316		panic("could not allocate register dump storage.\n");317 318	if (list_empty(&clock_reg_cache_list))319		register_syscore_ops(&samsung_clk_syscore_ops);320 321	reg_cache->reg_base = reg_base;322	reg_cache->rd_num = nr_rdump;323	reg_cache->rsuspend = rsuspend;324	reg_cache->rsuspend_num = nr_rsuspend;325	list_add_tail(&reg_cache->node, &clock_reg_cache_list);326}327#endif328 329/**330 * samsung_cmu_register_clocks() - Register all clocks provided in CMU object331 * @ctx: Clock provider object332 * @cmu: CMU object with clocks to register333 */334void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx,335					const struct samsung_cmu_info *cmu)336{337	if (cmu->pll_clks)338		samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks);339	if (cmu->mux_clks)340		samsung_clk_register_mux(ctx, cmu->mux_clks, cmu->nr_mux_clks);341	if (cmu->div_clks)342		samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);343	if (cmu->gate_clks)344		samsung_clk_register_gate(ctx, cmu->gate_clks,345					  cmu->nr_gate_clks);346	if (cmu->fixed_clks)347		samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,348						cmu->nr_fixed_clks);349	if (cmu->fixed_factor_clks)350		samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,351						  cmu->nr_fixed_factor_clks);352	if (cmu->cpu_clks)353		samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks);354}355 356/*357 * Common function which registers plls, muxes, dividers and gates358 * for each CMU. It also add CMU register list to register cache.359 */360struct samsung_clk_provider * __init samsung_cmu_register_one(361			struct device_node *np,362			const struct samsung_cmu_info *cmu)363{364	void __iomem *reg_base;365	struct samsung_clk_provider *ctx;366 367	reg_base = of_iomap(np, 0);368	if (!reg_base) {369		panic("%s: failed to map registers\n", __func__);370		return NULL;371	}372 373	ctx = samsung_clk_init(NULL, reg_base, cmu->nr_clk_ids);374	samsung_cmu_register_clocks(ctx, cmu);375 376	if (cmu->clk_regs)377		samsung_clk_extended_sleep_init(reg_base,378			cmu->clk_regs, cmu->nr_clk_regs,379			cmu->suspend_regs, cmu->nr_suspend_regs);380 381	samsung_clk_of_add_provider(np, ctx);382 383	return ctx;384}385