2874c5fd28
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
113 lines
2.9 KiB
C
113 lines
2.9 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2016-2017 Imagination Technologies
|
|
* Author: Paul Burton <paul.burton@mips.com>
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "clk-boston: " fmt
|
|
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/of.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/mfd/syscon.h>
|
|
|
|
#include <dt-bindings/clock/boston-clock.h>
|
|
|
|
#define BOSTON_PLAT_MMCMDIV 0x30
|
|
# define BOSTON_PLAT_MMCMDIV_CLK0DIV (0xff << 0)
|
|
# define BOSTON_PLAT_MMCMDIV_INPUT (0xff << 8)
|
|
# define BOSTON_PLAT_MMCMDIV_MUL (0xff << 16)
|
|
# define BOSTON_PLAT_MMCMDIV_CLK1DIV (0xff << 24)
|
|
|
|
#define BOSTON_CLK_COUNT 3
|
|
|
|
static u32 ext_field(u32 val, u32 mask)
|
|
{
|
|
return (val & mask) >> (ffs(mask) - 1);
|
|
}
|
|
|
|
static void __init clk_boston_setup(struct device_node *np)
|
|
{
|
|
unsigned long in_freq, cpu_freq, sys_freq;
|
|
uint mmcmdiv, mul, cpu_div, sys_div;
|
|
struct clk_hw_onecell_data *onecell;
|
|
struct regmap *regmap;
|
|
struct clk_hw *hw;
|
|
int err;
|
|
|
|
regmap = syscon_node_to_regmap(np->parent);
|
|
if (IS_ERR(regmap)) {
|
|
pr_err("failed to find regmap\n");
|
|
return;
|
|
}
|
|
|
|
err = regmap_read(regmap, BOSTON_PLAT_MMCMDIV, &mmcmdiv);
|
|
if (err) {
|
|
pr_err("failed to read mmcm_div register: %d\n", err);
|
|
return;
|
|
}
|
|
|
|
in_freq = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_INPUT) * 1000000;
|
|
mul = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_MUL);
|
|
|
|
sys_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK0DIV);
|
|
sys_freq = mult_frac(in_freq, mul, sys_div);
|
|
|
|
cpu_div = ext_field(mmcmdiv, BOSTON_PLAT_MMCMDIV_CLK1DIV);
|
|
cpu_freq = mult_frac(in_freq, mul, cpu_div);
|
|
|
|
onecell = kzalloc(sizeof(*onecell) +
|
|
(BOSTON_CLK_COUNT * sizeof(struct clk_hw *)),
|
|
GFP_KERNEL);
|
|
if (!onecell)
|
|
return;
|
|
|
|
onecell->num = BOSTON_CLK_COUNT;
|
|
|
|
hw = clk_hw_register_fixed_rate(NULL, "input", NULL, 0, in_freq);
|
|
if (IS_ERR(hw)) {
|
|
pr_err("failed to register input clock: %ld\n", PTR_ERR(hw));
|
|
goto fail_input;
|
|
}
|
|
onecell->hws[BOSTON_CLK_INPUT] = hw;
|
|
|
|
hw = clk_hw_register_fixed_rate(NULL, "sys", "input", 0, sys_freq);
|
|
if (IS_ERR(hw)) {
|
|
pr_err("failed to register sys clock: %ld\n", PTR_ERR(hw));
|
|
goto fail_sys;
|
|
}
|
|
onecell->hws[BOSTON_CLK_SYS] = hw;
|
|
|
|
hw = clk_hw_register_fixed_rate(NULL, "cpu", "input", 0, cpu_freq);
|
|
if (IS_ERR(hw)) {
|
|
pr_err("failed to register cpu clock: %ld\n", PTR_ERR(hw));
|
|
goto fail_cpu;
|
|
}
|
|
onecell->hws[BOSTON_CLK_CPU] = hw;
|
|
|
|
err = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, onecell);
|
|
if (err) {
|
|
pr_err("failed to add DT provider: %d\n", err);
|
|
goto fail_clk_add;
|
|
}
|
|
|
|
return;
|
|
|
|
fail_clk_add:
|
|
clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_CPU]);
|
|
fail_cpu:
|
|
clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_SYS]);
|
|
fail_sys:
|
|
clk_hw_unregister_fixed_rate(onecell->hws[BOSTON_CLK_INPUT]);
|
|
fail_input:
|
|
kfree(onecell);
|
|
}
|
|
|
|
/*
|
|
* Use CLK_OF_DECLARE so that this driver is probed early enough to provide the
|
|
* CPU frequency for use with the GIC or cop0 counters/timers.
|
|
*/
|
|
CLK_OF_DECLARE(clk_boston, "img,boston-clock", clk_boston_setup);
|