/*++
linux/arch/arm/mach-wmt/wmt_clock.c
Copyright (c) 2013 WonderMedia Technologies, Inc.
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.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see .
WonderMedia Technologies, Inc.
10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C.
--*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define DEBUG
#ifdef DEBUG
#define fq_dbg(fmt, args...) \
printk(KERN_ERR "[%s]_%d_%d: " fmt, __func__ , __LINE__, smp_processor_id(), ## args)
#define fq_trace() printk(KERN_ERR "trace in %s %d\n", __func__, __LINE__)
#else
#define fq_dbg(fmt, args...)
#define fq_trace()
#endif
#define DEV_NAME "smp_twd"
static struct clk_lookup twd_lookup = {
.dev_id = DEV_NAME,
};
static int wmt_twd_clk_enable(struct clk_hw *hw)
{
return 0;
}
static void wmt_twd_clk_disable(struct clk_hw *hw)
{
return ;
}
static int wmt_twd_clk_is_enabled(struct clk_hw *hw)
{
return true;
}
static unsigned long
wmt_twd_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
int freq = 0;
/* return the ARM CPU clock rate */
freq = auto_pll_divisor(DEV_ARM, GET_CPUTIMER, 0, 0) / 2;
if (freq < 0)
freq = 0;
return freq;
}
static struct clk_ops wmt_twd_clk_ops = {
.enable = wmt_twd_clk_enable,
.disable = wmt_twd_clk_disable,
.is_enabled = wmt_twd_clk_is_enabled,
.recalc_rate = wmt_twd_clk_recalc_rate,
};
static int wmt3498_register_twd_clk(struct clk_ops *ops, struct clk_lookup *cl)
{
struct clk_hw *hw = NULL;
clkdev_add(cl);
/* register twd clk here */
hw = kzalloc(sizeof(*hw), GFP_KERNEL);
if (!hw) {
printk(KERN_ERR "Register smp_twd clock failed!\n");
return -ENOMEM;
}
cl->clk = clk_register(NULL, DEV_NAME, ops, hw, NULL, 0, CLK_IS_ROOT);
if (!cl->clk) {
printk(KERN_ERR "Register smp_twd clock failed!\n");
kfree(hw);
return -ENOMEM;
}
return 0;
}
static void wmt3498_register_clocks(void)
{
wmt3498_register_twd_clk(&wmt_twd_clk_ops, &twd_lookup);
return ;
}
static int wmt3498_setup_twd_clk(void)
{
return 0;
}
static void wmt3498_setup_clocks(void)
{
wmt3498_setup_twd_clk();
return ;
}
/* this func should be called in machine early_init or io_map */
void wmt3498_init_clocks(void)
{
wmt3498_register_clocks();
wmt3498_setup_clocks();
return ;
}
/* updates twd frequency when the cpu frequency changes. */
static int wmt_twd_cpufreq_transition(struct notifier_block *nb,
unsigned long state, void *data)
{
struct clk *wmt_twd_clk = twd_lookup.clk;
struct cpufreq_freqs *freqs = data;
if (!wmt_twd_clk)
goto out;
if (state == CPUFREQ_POSTCHANGE || state == CPUFREQ_RESUMECHANGE) {
// only boot-cpu do clk rate update
if (freqs->cpu == 0) {
wmt_twd_clk->rate = auto_pll_divisor(DEV_ARM, GET_CPUTIMER, 0, 0) / 2;
wmt_twd_clk->new_rate = wmt_twd_clk->rate;
}
}
out:
return NOTIFY_OK;
}
static struct notifier_block wmt_twd_cpufreq_nb = {
.notifier_call = wmt_twd_cpufreq_transition,
.priority = 10, /* higher than twd */
};
static int wmt_twd_cpufreq_init(void)
{
return cpufreq_register_notifier(&wmt_twd_cpufreq_nb, CPUFREQ_TRANSITION_NOTIFIER);
}
core_initcall(wmt_twd_cpufreq_init);
MODULE_AUTHOR("WonderMedia Technologies, Inc");
MODULE_DESCRIPTION("WMT Common Clock Driver");
MODULE_LICENSE("Dual BSD/GPL");