/*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/export.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <asm/div64.h>
#include "clk-rcg.h"
#include "common.h"
static u32 ns_to_src(struct src_sel *s, u32 ns)
{
ns >>= s->src_sel_shift;
ns &= SRC_SEL_MASK;
return ns;
}
static u32 src_to_ns(struct src_sel *s, u8 src, u32 ns)
{
u32 mask;
mask = SRC_SEL_MASK;
mask <<= s->src_sel_shift;
ns &= ~mask;
ns |= src << s->src_sel_shift;
return ns;
}
static u8 clk_rcg_get_parent(struct clk_hw *hw)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
int num_parents = clk_hw_get_num_parents(hw);
u32 ns;
int i, ret;
ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
if (ret)
goto err;
ns = ns_to_src(&rcg->s, ns);
for (i = 0; i < num_parents; i++)
if (ns == rcg->s.parent_map[i].cfg)
return i;
err:
pr_debug("%s: Clock %s has invalid parent, using default.\n",
__func__, clk_hw_get_name(hw));
return 0;
}
static int reg_to_bank(struct clk_dyn_rcg *rcg, u32 bank)
{
bank &= BIT(rcg->mux_sel_bit);
return !!bank;
}
static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw)
{
struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw);
int num_parents = clk_hw_get_num_parents(hw);
u32 ns, reg;
int bank;
int i, ret;
struct src_sel *s;
ret = regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®);
if (ret)
goto err;
bank = reg_to_bank(rcg, reg);
s = &rcg->s[bank];
ret = regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns);
if (ret)
goto err;
ns = ns_to_src(s, ns);
for (i = 0; i < num_parents; i++)
if (ns == s->parent_map[i].cfg)
return i;
err:
pr_debug("%s: Clock %s has invalid parent, using default.\n",
__func__, clk_hw_get_name(hw));
return 0;
}
static int clk_rcg_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_rcg *rcg = to_clk_rcg(hw);
u32 ns;
regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns);
ns = src_to_ns(&rcg->s, rcg->s.parent_map[index].cfg, ns);
regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns);
return 0;
}
static u32 md_to_m(struct mn *mn, u32 md)
{
md >>= mn->m_val_shift;
md &= BIT(mn->width) - 1;
return md;
}
static u32 ns_to_pre_div(struct pre_div *p, u32 ns)
{
ns >>= p->pre_div_shift;
ns &= BIT(p->pre_div_width) - 1;
return ns;
}
static u32 pre_div_to_ns(struct pre_div *p, u8 pre_div, u32 ns)
{
u32 mask;
mask = BIT(p->pre_div_width) - 1;
mask <<= p->pre_div_shift;
ns &= ~mask;
ns |= pre_div << p->pre_div_shift;
return ns;
}
static u32 mn_to_md(struct mn *mn, u32 m, u32 n, u32 md)
{
u32 mask, mask_w;
mask_w = BIT(mn->width) - 1;
mask = (mask_w << mn->m_val_shift) | mask_w;
md &= ~mask;
if (n) {
m <<= mn->m_val_shift;
md |= m;
md |= ~n & mask_w