summaryrefslogtreecommitdiff
path: root/drivers/clk/at91/clk-main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/at91/clk-main.c')
-rw-r--r--drivers/clk/at91/clk-main.c116
1 files changed, 100 insertions, 16 deletions
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 37c22667e831..9b462becc693 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -28,6 +28,7 @@
struct clk_main_osc {
struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
};
#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
@@ -37,6 +38,7 @@ struct clk_main_rc_osc {
struct regmap *regmap;
unsigned long frequency;
unsigned long accuracy;
+ struct at91_clk_pms pms;
};
#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)
@@ -51,6 +53,7 @@ struct clk_rm9200_main {
struct clk_sam9x5_main {
struct clk_hw hw;
struct regmap *regmap;
+ struct at91_clk_pms pms;
u8 parent;
};
@@ -120,24 +123,44 @@ static int clk_main_osc_is_prepared(struct clk_hw *hw)
return (status & AT91_PMC_MOSCS) && clk_main_parent_select(tmp);
}
+static int clk_main_osc_save_context(struct clk_hw *hw)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
+
+ osc->pms.status = clk_main_osc_is_prepared(hw);
+
+ return 0;
+}
+
+static void clk_main_osc_restore_context(struct clk_hw *hw)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
+
+ if (osc->pms.status)
+ clk_main_osc_prepare(hw);
+}
+
static const struct clk_ops main_osc_ops = {
.prepare = clk_main_osc_prepare,
.unprepare = clk_main_osc_unprepare,
.is_prepared = clk_main_osc_is_prepared,
+ .save_context = clk_main_osc_save_context,
+ .restore_context = clk_main_osc_restore_context,
};
struct clk_hw * __init
at91_clk_register_main_osc(struct regmap *regmap,
const char *name,
const char *parent_name,
+ struct clk_parent_data *parent_data,
bool bypass)
{
struct clk_main_osc *osc;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
- if (!name || !parent_name)
+ if (!name || !(parent_name || parent_data))
return ERR_PTR(-EINVAL);
osc = kzalloc(sizeof(*osc), GFP_KERNEL);
@@ -146,7 +169,10 @@ at91_clk_register_main_osc(struct regmap *regmap,
init.name = name;
init.ops = &main_osc_ops;
- init.parent_names = &parent_name;
+ if (parent_data)
+ init.parent_data = (const struct clk_parent_data *)parent_data;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = CLK_IGNORE_UNUSED;
@@ -175,7 +201,7 @@ static bool clk_main_rc_osc_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MOSCRCS;
+ return !!(status & AT91_PMC_MOSCRCS);
}
static int clk_main_rc_osc_prepare(struct clk_hw *hw)
@@ -240,12 +266,31 @@ static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw,
return osc->accuracy;
}
+static int clk_main_rc_osc_save_context(struct clk_hw *hw)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+
+ osc->pms.status = clk_main_rc_osc_is_prepared(hw);
+
+ return 0;
+}
+
+static void clk_main_rc_osc_restore_context(struct clk_hw *hw)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+
+ if (osc->pms.status)
+ clk_main_rc_osc_prepare(hw);
+}
+
static const struct clk_ops main_rc_osc_ops = {
.prepare = clk_main_rc_osc_prepare,
.unprepare = clk_main_rc_osc_unprepare,
.is_prepared = clk_main_rc_osc_is_prepared,
.recalc_rate = clk_main_rc_osc_recalc_rate,
.recalc_accuracy = clk_main_rc_osc_recalc_accuracy,
+ .save_context = clk_main_rc_osc_save_context,
+ .restore_context = clk_main_rc_osc_restore_context,
};
struct clk_hw * __init
@@ -336,7 +381,7 @@ static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
- return status & AT91_PMC_MAINRDY ? 1 : 0;
+ return !!(status & AT91_PMC_MAINRDY);
}
static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
@@ -356,17 +401,18 @@ static const struct clk_ops rm9200_main_ops = {
struct clk_hw * __init
at91_clk_register_rm9200_main(struct regmap *regmap,
const char *name,
- const char *parent_name)
+ const char *parent_name,
+ struct clk_hw *parent_hw)
{
struct clk_rm9200_main *clkmain;
- struct clk_init_data init;
+ struct clk_init_data init = {};
struct clk_hw *hw;
int ret;
if (!name)
return ERR_PTR(-EINVAL);
- if (!parent_name)
+ if (!(parent_name || parent_hw))
return ERR_PTR(-EINVAL);
clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
@@ -375,7 +421,10 @@ at91_clk_register_rm9200_main(struct regmap *regmap,
init.name = name;
init.ops = &rm9200_main_ops;
- init.parent_names = &parent_name;
+ if (parent_hw)
+ init.parent_hws = (const struct clk_hw **)&parent_hw;
+ else
+ init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = 0;
@@ -398,7 +447,7 @@ static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
regmap_read(regmap, AT91_PMC_SR, &status);
- return status & AT91_PMC_MOSCSELS ? 1 : 0;
+ return !!(status & AT91_PMC_MOSCSELS);
}
static int clk_sam9x5_main_prepare(struct clk_hw *hw)
@@ -437,12 +486,17 @@ static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)
return -EINVAL;
regmap_read(regmap, AT91_CKGR_MOR, &tmp);
- tmp &= ~MOR_KEY_MASK;
if (index && !(tmp & AT91_PMC_MOSCSEL))
- regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+ tmp = AT91_PMC_MOSCSEL;
else if (!index && (tmp & AT91_PMC_MOSCSEL))
- regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+ tmp = 0;
+ else
+ return 0;
+
+ regmap_update_bits(regmap, AT91_CKGR_MOR,
+ AT91_PMC_MOSCSEL | MOR_KEY_MASK,
+ tmp | AT91_PMC_KEY);
while (!clk_sam9x5_main_ready(regmap))
cpu_relax();
@@ -460,22 +514,49 @@ static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw)
return clk_main_parent_select(status);
}
+static int clk_sam9x5_main_save_context(struct clk_hw *hw)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
+
+ clkmain->pms.status = clk_main_rc_osc_is_prepared(&clkmain->hw);
+ clkmain->pms.parent = clk_sam9x5_main_get_parent(&clkmain->hw);
+
+ return 0;
+}
+
+static void clk_sam9x5_main_restore_context(struct clk_hw *hw)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
+ int ret;
+
+ ret = clk_sam9x5_main_set_parent(hw, clkmain->pms.parent);
+ if (ret)
+ return;
+
+ if (clkmain->pms.status)
+ clk_sam9x5_main_prepare(hw);
+}
+
static const struct clk_ops sam9x5_main_ops = {
.prepare = clk_sam9x5_main_prepare,
.is_prepared = clk_sam9x5_main_is_prepared,
.recalc_rate = clk_sam9x5_main_recalc_rate,
+ .determine_rate = clk_hw_determine_rate_no_reparent,
.set_parent = clk_sam9x5_main_set_parent,
.get_parent = clk_sam9x5_main_get_parent,
+ .save_context = clk_sam9x5_main_save_context,
+ .restore_context = clk_sam9x5_main_restore_context,
};
struct clk_hw * __init
at91_clk_register_sam9x5_main(struct regmap *regmap,
const char *name,
const char **parent_names,
+ struct clk_hw **parent_hws,
int num_parents)
{
struct clk_sam9x5_main *clkmain;
- struct clk_init_data init;
+ struct clk_init_data init = {};
unsigned int status;
struct clk_hw *hw;
int ret;
@@ -483,7 +564,7 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
if (!name)
return ERR_PTR(-EINVAL);
- if (!parent_names || !num_parents)
+ if (!(parent_hws || parent_names) || !num_parents)
return ERR_PTR(-EINVAL);
clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
@@ -492,7 +573,10 @@ at91_clk_register_sam9x5_main(struct regmap *regmap,
init.name = name;
init.ops = &sam9x5_main_ops;
- init.parent_names = parent_names;
+ if (parent_hws)
+ init.parent_hws = (const struct clk_hw **)parent_hws;
+ else
+ init.parent_names = parent_names;
init.num_parents = num_parents;
init.flags = CLK_SET_PARENT_GATE;