summaryrefslogtreecommitdiffstats
path: root/drivers/clk/at91/sckc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/at91/sckc.c')
-rw-r--r--drivers/clk/at91/sckc.c387
1 files changed, 250 insertions, 137 deletions
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
index bac28999ea..1a33a64421 100644
--- a/drivers/clk/at91/sckc.c
+++ b/drivers/clk/at91/sckc.c
@@ -1,13 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* drivers/clk/at91/sckc.c
*
* Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
- *
- * 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.
- *
*/
#include <common.h>
@@ -25,18 +20,22 @@
#define SLOW_CLOCK_FREQ 32768
#define SLOWCK_SW_CYCLES 5
-#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * SECOND) / \
+#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
SLOW_CLOCK_FREQ)
#define AT91_SCKC_CR 0x00
-#define AT91_SCKC_RCEN (1 << 0)
-#define AT91_SCKC_OSC32EN (1 << 1)
-#define AT91_SCKC_OSC32BYP (1 << 2)
-#define AT91_SCKC_OSCSEL (1 << 3)
+
+struct clk_slow_bits {
+ u32 cr_rcen;
+ u32 cr_osc32en;
+ u32 cr_osc32byp;
+ u32 cr_oscsel;
+};
struct clk_slow_osc {
struct clk clk;
void __iomem *sckcr;
+ const struct clk_slow_bits *bits;
unsigned long startup_usec;
const char *parent_name;
};
@@ -46,6 +45,7 @@ struct clk_slow_osc {
struct clk_sama5d4_slow_osc {
struct clk clk;
void __iomem *sckcr;
+ const struct clk_slow_bits *bits;
unsigned long startup_usec;
bool prepared;
const char *parent_name;
@@ -56,6 +56,7 @@ struct clk_sama5d4_slow_osc {
struct clk_slow_rc_osc {
struct clk clk;
void __iomem *sckcr;
+ const struct clk_slow_bits *bits;
unsigned long frequency;
unsigned long startup_usec;
const char *parent_name;
@@ -66,6 +67,7 @@ struct clk_slow_rc_osc {
struct clk_sam9x5_slow {
struct clk clk;
void __iomem *sckcr;
+ const struct clk_slow_bits *bits;
u8 parent;
const char *parent_names[2];
};
@@ -78,10 +80,10 @@ static int clk_slow_osc_enable(struct clk *clk)
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
- if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
+ if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en))
return 0;
- writel(tmp | AT91_SCKC_OSC32EN, sckcr);
+ writel(tmp | osc->bits->cr_osc32en, sckcr);
udelay(osc->startup_usec);
@@ -94,10 +96,10 @@ static void clk_slow_osc_disable(struct clk *clk)
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
- if (tmp & AT91_SCKC_OSC32BYP)
+ if (tmp & osc->bits->cr_osc32byp)
return;
- writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
+ writel(tmp & ~osc->bits->cr_osc32en, sckcr);
}
static int clk_slow_osc_is_enabled(struct clk *clk)
@@ -106,10 +108,10 @@ static int clk_slow_osc_is_enabled(struct clk *clk)
void __iomem *sckcr = osc->sckcr;
u32 tmp = readl(sckcr);
- if (tmp & AT91_SCKC_OSC32BYP)
+ if (tmp & osc->bits->cr_osc32byp)
return 1;
- return !!(tmp & AT91_SCKC_OSC32EN);
+ return !!(tmp & osc->bits->cr_osc32en);
}
static const struct clk_ops slow_osc_ops = {
@@ -118,12 +120,13 @@ static const struct clk_ops slow_osc_ops = {
.is_enabled = clk_slow_osc_is_enabled,
};
-static struct clk *
+static struct clk * __init
at91_clk_register_slow_osc(void __iomem *sckcr,
const char *name,
const char *parent_name,
unsigned long startup,
- bool bypass)
+ bool bypass,
+ const struct clk_slow_bits *bits)
{
int ret;
struct clk_slow_osc *osc;
@@ -138,13 +141,15 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
osc->parent_name = parent_name;
osc->clk.parent_names = &osc->parent_name;
osc->clk.num_parents = 1;
+ /* osc->clk.flags = CLK_IGNORE_UNUSED; */
osc->sckcr = sckcr;
osc->startup_usec = startup;
+ osc->bits = bits;
if (bypass)
- writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
- sckcr);
+ writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
+ osc->bits->cr_osc32byp, sckcr);
ret = clk_register(&osc->clk);
if (ret) {
@@ -155,26 +160,12 @@ at91_clk_register_slow_osc(void __iomem *sckcr,
return &osc->clk;
}
-static void
-of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
+static void at91_clk_unregister_slow_osc(struct clk *clk)
{
- struct clk *clk;
- const char *parent_name;
- const char *name = np->name;
- u32 startup;
- bool bypass;
-
- parent_name = of_clk_get_parent_name(np, 0);
- of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "atmel,startup-time-usec", &startup);
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
-
- clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
- bypass);
- if (IS_ERR(clk))
- return;
+ struct clk_slow_osc *osc = to_clk_slow_osc(clk);
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ clk_unregister(clk);
+ kfree(osc);
}
static unsigned long clk_slow_rc_osc_recalc_rate(struct clk *clk,
@@ -190,7 +181,7 @@ static int clk_slow_rc_osc_enable(struct clk *clk)
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
void __iomem *sckcr = osc->sckcr;
- writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
+ writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
udelay(osc->startup_usec);
@@ -202,14 +193,14 @@ static void clk_slow_rc_osc_disable(struct clk *clk)
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
void __iomem *sckcr = osc->sckcr;
- writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
+ writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
}
static int clk_slow_rc_osc_is_enabled(struct clk *clk)
{
struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
- return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
+ return !!(readl(osc->sckcr) & osc->bits->cr_rcen);
}
static const struct clk_ops slow_rc_osc_ops = {
@@ -219,11 +210,13 @@ static const struct clk_ops slow_rc_osc_ops = {
.recalc_rate = clk_slow_rc_osc_recalc_rate,
};
-static struct clk *
+static struct clk * __init
at91_clk_register_slow_rc_osc(void __iomem *sckcr,
const char *name,
unsigned long frequency,
- unsigned long startup)
+ unsigned long accuracy,
+ unsigned long startup,
+ const struct clk_slow_bits *bits)
{
struct clk_slow_rc_osc *osc;
int ret;
@@ -239,6 +232,7 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
/* init.flags = CLK_IGNORE_UNUSED; */
osc->sckcr = sckcr;
+ osc->bits = bits;
osc->frequency = frequency;
osc->startup_usec = startup;
@@ -251,23 +245,12 @@ at91_clk_register_slow_rc_osc(void __iomem *sckcr,
return &osc->clk;
}
-static void
-of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
+static void at91_clk_unregister_slow_rc_osc(struct clk *clk)
{
- struct clk *clk;
- u32 frequency = 0;
- u32 startup = 0;
- const char *name = np->name;
-
- of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "clock-frequency", &frequency);
- of_property_read_u32(np, "atmel,startup-time-usec", &startup);
-
- clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, startup);
- if (IS_ERR(clk))
- return;
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
- of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ clk_unregister(clk);
+ kfree(osc);
}
static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index)
@@ -281,14 +264,14 @@ static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index)
tmp = readl(sckcr);
- if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
- (index && (tmp & AT91_SCKC_OSCSEL)))
+ if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||
+ (index && (tmp & slowck->bits->cr_oscsel)))
return 0;
if (index)
- tmp |= AT91_SCKC_OSCSEL;
+ tmp |= slowck->bits->cr_oscsel;
else
- tmp &= ~AT91_SCKC_OSCSEL;
+ tmp &= ~slowck->bits->cr_oscsel;
writel(tmp, sckcr);
@@ -301,7 +284,7 @@ static int clk_sam9x5_slow_get_parent(struct clk *clk)
{
struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
- return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
+ return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);
}
static const struct clk_ops sam9x5_slow_ops = {
@@ -309,11 +292,12 @@ static const struct clk_ops sam9x5_slow_ops = {
.get_parent = clk_sam9x5_slow_get_parent,
};
-static struct clk *
+static struct clk * __init
at91_clk_register_sam9x5_slow(void __iomem *sckcr,
const char *name,
const char **parent_names,
- int num_parents)
+ int num_parents,
+ const struct clk_slow_bits *bits)
{
struct clk_sam9x5_slow *slowck;
int ret;
@@ -330,7 +314,8 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
slowck->clk.parent_names = slowck->parent_names;
slowck->clk.num_parents = num_parents;
slowck->sckcr = sckcr;
- slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
+ slowck->bits = bits;
+ slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
ret = clk_register(&slowck->clk);
if (ret) {
@@ -341,69 +326,183 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr,
return &slowck->clk;
}
-static int
-of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
+static void at91_clk_unregister_sam9x5_slow(struct clk *clk)
{
- struct clk *clk;
- const char *parent_names[2];
- unsigned int num_parents;
- const char *name = np->name;
+ struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
- num_parents = of_clk_get_parent_count(np);
- if (num_parents == 0 || num_parents > 2)
- return -EINVAL;
+ clk_unregister(clk);
+ kfree(slowck);
+}
+
+static void __init at91sam9x5_sckc_register(struct device_node *np,
+ unsigned int rc_osc_startup_us,
+ const struct clk_slow_bits *bits)
+{
+ const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
+ void __iomem *regbase = of_iomap(np, 0);
+ struct device_node *child = NULL;
+ const char *xtal_name;
+ struct clk *slow_rc, *slow_osc, *slowck;
+ bool bypass;
+ int ret;
+
+ if (!regbase)
+ return;
+
+ slow_rc = at91_clk_register_slow_rc_osc(regbase, parent_names[0],
+ 32768, 50000000,
+ rc_osc_startup_us, bits);
+ if (IS_ERR(slow_rc))
+ return;
+
+ xtal_name = of_clk_get_parent_name(np, 0);
+ if (!xtal_name) {
+ /* DT backward compatibility */
+ child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
+ if (!child)
+ goto unregister_slow_rc;
+
+ xtal_name = of_clk_get_parent_name(child, 0);
+ bypass = of_property_read_bool(child, "atmel,osc-bypass");
+
+ child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");
+ } else {
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+ }
+
+ if (!xtal_name)
+ goto unregister_slow_rc;
+
+ slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
+ xtal_name, 1200000, bypass, bits);
+ if (IS_ERR(slow_osc))
+ goto unregister_slow_rc;
+
+ slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names,
+ 2, bits);
+ if (IS_ERR(slowck))
+ goto unregister_slow_osc;
+
+ /* DT backward compatibility */
+ if (child)
+ ret = of_clk_add_provider(child, of_clk_src_simple_get,
+ slowck);
+ else
+ ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck);
+
+ if (WARN_ON(ret))
+ goto unregister_slowck;
- of_clk_parent_fill(np, parent_names, num_parents);
+ return;
- of_property_read_string(np, "clock-output-names", &name);
+unregister_slowck:
+ at91_clk_unregister_sam9x5_slow(slowck);
+unregister_slow_osc:
+ at91_clk_unregister_slow_osc(slow_osc);
+unregister_slow_rc:
+ at91_clk_unregister_slow_rc_osc(slow_rc);
+}
+
+static const struct clk_slow_bits at91sam9x5_bits = {
+ .cr_rcen = BIT(0),
+ .cr_osc32en = BIT(1),
+ .cr_osc32byp = BIT(2),
+ .cr_oscsel = BIT(3),
+};
- clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
- num_parents);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+static void __init of_at91sam9x5_sckc_setup(struct device_node *np)
+{
+ at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
+ of_at91sam9x5_sckc_setup);
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+static void __init of_sama5d3_sckc_setup(struct device_node *np)
+{
+ at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);
}
+CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
+ of_sama5d3_sckc_setup);
-static const struct of_device_id sckc_clk_ids[] = {
- /* Slow clock */
- {
- .compatible = "atmel,at91sam9x5-clk-slow-osc",
- .data = of_at91sam9x5_clk_slow_osc_setup,
- },
- {
- .compatible = "atmel,at91sam9x5-clk-slow-rc-osc",
- .data = of_at91sam9x5_clk_slow_rc_osc_setup,
- },
- {
- .compatible = "atmel,at91sam9x5-clk-slow",
- .data = of_at91sam9x5_clk_slow_setup,
- },
- { /*sentinel*/ }
+static const struct clk_slow_bits at91sam9x60_bits = {
+ .cr_osc32en = BIT(1),
+ .cr_osc32byp = BIT(2),
+ .cr_oscsel = BIT(24),
};
-static int of_at91sam9x5_sckc_setup(struct device_node *np)
+static void __init of_sam9x60_sckc_setup(struct device_node *np)
{
- struct device_node *childnp;
- void (*clk_setup)(struct device_node *, void __iomem *);
- const struct of_device_id *clk_id;
void __iomem *regbase = of_iomap(np, 0);
+ struct clk_onecell_data *clk_data;
+ struct clk *slow_rc, *slow_osc;
+ const char *xtal_name;
+ const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
+ bool bypass;
+ int ret;
if (!regbase)
- return -ENOMEM;
-
- for_each_child_of_node(np, childnp) {
- clk_id = of_match_node(sckc_clk_ids, childnp);
- if (!clk_id)
- continue;
- clk_setup = clk_id->data;
- clk_setup(childnp, regbase);
- }
+ return;
- return 0;
+ slow_rc = clk_register_fixed_rate(parent_names[0], NULL, 0,
+ 32768);
+ if (IS_ERR(slow_rc))
+ return;
+
+ xtal_name = of_clk_get_parent_name(np, 0);
+ if (!xtal_name)
+ goto unregister_slow_rc;
+
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+ slow_osc = at91_clk_register_slow_osc(regbase, parent_names[1],
+ xtal_name, 5000000, bypass,
+ &at91sam9x60_bits);
+ if (IS_ERR(slow_osc))
+ goto unregister_slow_rc;
+
+ clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+ if (!clk_data)
+ goto unregister_slow_osc;
+
+ /* MD_SLCK and TD_SLCK. */
+ clk_data->clk_num = 2;
+ clk_data->clks = kcalloc(clk_data->clk_num,
+ sizeof(*clk_data->clks), GFP_KERNEL);
+ if (!clk_data->clks)
+ goto clk_data_free;
+
+ clk_data->clks[0] = clk_register_fixed_rate("md_slck",
+ parent_names[0],
+ 0, 32768);
+ if (IS_ERR(clk_data->clks[0]))
+ goto clks_free;
+
+ clk_data->clks[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
+ parent_names, 2,
+ &at91sam9x60_bits);
+ if (IS_ERR(clk_data->clks[1]))
+ goto unregister_md_slck;
+
+ ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+ if (WARN_ON(ret))
+ goto unregister_td_slck;
+
+ return;
+
+unregister_td_slck:
+ at91_clk_unregister_sam9x5_slow(clk_data->clks[1]);
+unregister_md_slck:
+ clk_unregister(clk_data->clks[0]);
+clks_free:
+ kfree(clk_data->clks);
+clk_data_free:
+ kfree(clk_data);
+unregister_slow_osc:
+ at91_clk_unregister_slow_osc(slow_osc);
+unregister_slow_rc:
+ clk_unregister(slow_rc);
}
-CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
- of_at91sam9x5_sckc_setup);
+CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
+ of_sam9x60_sckc_setup);
static int clk_sama5d4_slow_osc_enable(struct clk *clk)
{
@@ -416,7 +515,7 @@ static int clk_sama5d4_slow_osc_enable(struct clk *clk)
* Assume that if it has already been selected (for example by the
* bootloader), enough time has aready passed.
*/
- if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
+ if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
osc->prepared = true;
return 0;
}
@@ -439,23 +538,24 @@ static const struct clk_ops sama5d4_slow_osc_ops = {
.is_enabled = clk_sama5d4_slow_osc_is_enabled,
};
-static int of_sama5d4_sckc_setup(struct device_node *np)
+static const struct clk_slow_bits at91sama5d4_bits = {
+ .cr_oscsel = BIT(3),
+};
+
+static void __init of_sama5d4_sckc_setup(struct device_node *np)
{
void __iomem *regbase = of_iomap(np, 0);
- struct clk *clk;
+ struct clk *slow_rc, *slowck;
struct clk_sama5d4_slow_osc *osc;
const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
- bool bypass;
int ret;
if (!regbase)
- return -ENOMEM;
-
- clk = clk_fixed(parent_names[0], 32768);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
+ return;
- bypass = of_property_read_bool(np, "atmel,osc-bypass");
+ slow_rc = clk_fixed(parent_names[0], 32768);
+ if (IS_ERR(slow_rc))
+ return;
osc = xzalloc(sizeof(*osc));
osc->parent_name = of_clk_get_parent_name(np, 0);
@@ -463,23 +563,36 @@ static int of_sama5d4_sckc_setup(struct device_node *np)
osc->clk.ops = &sama5d4_slow_osc_ops;
osc->clk.parent_names = &osc->parent_name;
osc->clk.num_parents = 1;
+
+ /* osc->clk.flags = CLK_IGNORE_UNUSED; */
+
osc->sckcr = regbase;
osc->startup_usec = 1200000;
-
- if (bypass)
- writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
+ osc->bits = &at91sama5d4_bits;
ret = clk_register(&osc->clk);
- if (ret) {
- kfree(osc);
- return ret;
- }
-
- clk = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
-
- return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+ if (ret)
+ goto free_slow_osc_data;
+
+ slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
+ parent_names, 2,
+ &at91sama5d4_bits);
+ if (IS_ERR(slowck))
+ goto unregister_slow_osc;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get, slowck);
+ if (WARN_ON(ret))
+ goto unregister_slowck;
+
+ return;
+
+unregister_slowck:
+ at91_clk_unregister_sam9x5_slow(slowck);
+unregister_slow_osc:
+ clk_unregister(&osc->clk);
+free_slow_osc_data:
+ kfree(osc);
+ clk_unregister(slow_rc);
}
CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
of_sama5d4_sckc_setup);