mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
5.0 KiB
187 lines
5.0 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (c) 2018 BayLibre, SAS. |
|
* Author: Jerome Brunet <[email protected]> |
|
*/ |
|
|
|
#include <linux/module.h> |
|
#include "clk-regmap.h" |
|
|
|
static int clk_regmap_gate_endisable(struct clk_hw *hw, int enable) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); |
|
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; |
|
|
|
set ^= enable; |
|
|
|
return regmap_update_bits(clk->map, gate->offset, BIT(gate->bit_idx), |
|
set ? BIT(gate->bit_idx) : 0); |
|
} |
|
|
|
static int clk_regmap_gate_enable(struct clk_hw *hw) |
|
{ |
|
return clk_regmap_gate_endisable(hw, 1); |
|
} |
|
|
|
static void clk_regmap_gate_disable(struct clk_hw *hw) |
|
{ |
|
clk_regmap_gate_endisable(hw, 0); |
|
} |
|
|
|
static int clk_regmap_gate_is_enabled(struct clk_hw *hw) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_gate_data *gate = clk_get_regmap_gate_data(clk); |
|
unsigned int val; |
|
|
|
regmap_read(clk->map, gate->offset, &val); |
|
if (gate->flags & CLK_GATE_SET_TO_DISABLE) |
|
val ^= BIT(gate->bit_idx); |
|
|
|
val &= BIT(gate->bit_idx); |
|
|
|
return val ? 1 : 0; |
|
} |
|
|
|
const struct clk_ops clk_regmap_gate_ops = { |
|
.enable = clk_regmap_gate_enable, |
|
.disable = clk_regmap_gate_disable, |
|
.is_enabled = clk_regmap_gate_is_enabled, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_regmap_gate_ops); |
|
|
|
const struct clk_ops clk_regmap_gate_ro_ops = { |
|
.is_enabled = clk_regmap_gate_is_enabled, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_regmap_gate_ro_ops); |
|
|
|
static unsigned long clk_regmap_div_recalc_rate(struct clk_hw *hw, |
|
unsigned long prate) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); |
|
unsigned int val; |
|
int ret; |
|
|
|
ret = regmap_read(clk->map, div->offset, &val); |
|
if (ret) |
|
/* Gives a hint that something is wrong */ |
|
return 0; |
|
|
|
val >>= div->shift; |
|
val &= clk_div_mask(div->width); |
|
return divider_recalc_rate(hw, prate, val, div->table, div->flags, |
|
div->width); |
|
} |
|
|
|
static long clk_regmap_div_round_rate(struct clk_hw *hw, unsigned long rate, |
|
unsigned long *prate) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); |
|
unsigned int val; |
|
int ret; |
|
|
|
/* if read only, just return current value */ |
|
if (div->flags & CLK_DIVIDER_READ_ONLY) { |
|
ret = regmap_read(clk->map, div->offset, &val); |
|
if (ret) |
|
/* Gives a hint that something is wrong */ |
|
return 0; |
|
|
|
val >>= div->shift; |
|
val &= clk_div_mask(div->width); |
|
|
|
return divider_ro_round_rate(hw, rate, prate, div->table, |
|
div->width, div->flags, val); |
|
} |
|
|
|
return divider_round_rate(hw, rate, prate, div->table, div->width, |
|
div->flags); |
|
} |
|
|
|
static int clk_regmap_div_set_rate(struct clk_hw *hw, unsigned long rate, |
|
unsigned long parent_rate) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_div_data *div = clk_get_regmap_div_data(clk); |
|
unsigned int val; |
|
int ret; |
|
|
|
ret = divider_get_val(rate, parent_rate, div->table, div->width, |
|
div->flags); |
|
if (ret < 0) |
|
return ret; |
|
|
|
val = (unsigned int)ret << div->shift; |
|
return regmap_update_bits(clk->map, div->offset, |
|
clk_div_mask(div->width) << div->shift, val); |
|
}; |
|
|
|
/* Would prefer clk_regmap_div_ro_ops but clashes with qcom */ |
|
|
|
const struct clk_ops clk_regmap_divider_ops = { |
|
.recalc_rate = clk_regmap_div_recalc_rate, |
|
.round_rate = clk_regmap_div_round_rate, |
|
.set_rate = clk_regmap_div_set_rate, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_regmap_divider_ops); |
|
|
|
const struct clk_ops clk_regmap_divider_ro_ops = { |
|
.recalc_rate = clk_regmap_div_recalc_rate, |
|
.round_rate = clk_regmap_div_round_rate, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_regmap_divider_ro_ops); |
|
|
|
static u8 clk_regmap_mux_get_parent(struct clk_hw *hw) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); |
|
unsigned int val; |
|
int ret; |
|
|
|
ret = regmap_read(clk->map, mux->offset, &val); |
|
if (ret) |
|
return ret; |
|
|
|
val >>= mux->shift; |
|
val &= mux->mask; |
|
return clk_mux_val_to_index(hw, mux->table, mux->flags, val); |
|
} |
|
|
|
static int clk_regmap_mux_set_parent(struct clk_hw *hw, u8 index) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); |
|
unsigned int val = clk_mux_index_to_val(mux->table, mux->flags, index); |
|
|
|
return regmap_update_bits(clk->map, mux->offset, |
|
mux->mask << mux->shift, |
|
val << mux->shift); |
|
} |
|
|
|
static int clk_regmap_mux_determine_rate(struct clk_hw *hw, |
|
struct clk_rate_request *req) |
|
{ |
|
struct clk_regmap *clk = to_clk_regmap(hw); |
|
struct clk_regmap_mux_data *mux = clk_get_regmap_mux_data(clk); |
|
|
|
return clk_mux_determine_rate_flags(hw, req, mux->flags); |
|
} |
|
|
|
const struct clk_ops clk_regmap_mux_ops = { |
|
.get_parent = clk_regmap_mux_get_parent, |
|
.set_parent = clk_regmap_mux_set_parent, |
|
.determine_rate = clk_regmap_mux_determine_rate, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_regmap_mux_ops); |
|
|
|
const struct clk_ops clk_regmap_mux_ro_ops = { |
|
.get_parent = clk_regmap_mux_get_parent, |
|
}; |
|
EXPORT_SYMBOL_GPL(clk_regmap_mux_ro_ops); |
|
|
|
MODULE_DESCRIPTION("Amlogic regmap backed clock driver"); |
|
MODULE_AUTHOR("Jerome Brunet <[email protected]>"); |
|
MODULE_LICENSE("GPL v2");
|
|
|