162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * drivers/clk/at91/sckc.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/clkdev.h>
1062306a36Sopenharmony_ci#include <linux/delay.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/of_address.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define SLOW_CLOCK_FREQ		32768
1662306a36Sopenharmony_ci#define SLOWCK_SW_CYCLES	5
1762306a36Sopenharmony_ci#define SLOWCK_SW_TIME_USEC	((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
1862306a36Sopenharmony_ci				 SLOW_CLOCK_FREQ)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define	AT91_SCKC_CR			0x00
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct clk_slow_bits {
2362306a36Sopenharmony_ci	u32 cr_rcen;
2462306a36Sopenharmony_ci	u32 cr_osc32en;
2562306a36Sopenharmony_ci	u32 cr_osc32byp;
2662306a36Sopenharmony_ci	u32 cr_oscsel;
2762306a36Sopenharmony_ci};
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct clk_slow_osc {
3062306a36Sopenharmony_ci	struct clk_hw hw;
3162306a36Sopenharmony_ci	void __iomem *sckcr;
3262306a36Sopenharmony_ci	const struct clk_slow_bits *bits;
3362306a36Sopenharmony_ci	unsigned long startup_usec;
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct clk_sama5d4_slow_osc {
3962306a36Sopenharmony_ci	struct clk_hw hw;
4062306a36Sopenharmony_ci	void __iomem *sckcr;
4162306a36Sopenharmony_ci	const struct clk_slow_bits *bits;
4262306a36Sopenharmony_ci	unsigned long startup_usec;
4362306a36Sopenharmony_ci	bool prepared;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct clk_slow_rc_osc {
4962306a36Sopenharmony_ci	struct clk_hw hw;
5062306a36Sopenharmony_ci	void __iomem *sckcr;
5162306a36Sopenharmony_ci	const struct clk_slow_bits *bits;
5262306a36Sopenharmony_ci	unsigned long frequency;
5362306a36Sopenharmony_ci	unsigned long accuracy;
5462306a36Sopenharmony_ci	unsigned long startup_usec;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct clk_sam9x5_slow {
6062306a36Sopenharmony_ci	struct clk_hw hw;
6162306a36Sopenharmony_ci	void __iomem *sckcr;
6262306a36Sopenharmony_ci	const struct clk_slow_bits *bits;
6362306a36Sopenharmony_ci	u8 parent;
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int clk_slow_osc_prepare(struct clk_hw *hw)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
7162306a36Sopenharmony_ci	void __iomem *sckcr = osc->sckcr;
7262306a36Sopenharmony_ci	u32 tmp = readl(sckcr);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en))
7562306a36Sopenharmony_ci		return 0;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	writel(tmp | osc->bits->cr_osc32en, sckcr);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (system_state < SYSTEM_RUNNING)
8062306a36Sopenharmony_ci		udelay(osc->startup_usec);
8162306a36Sopenharmony_ci	else
8262306a36Sopenharmony_ci		usleep_range(osc->startup_usec, osc->startup_usec + 1);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return 0;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void clk_slow_osc_unprepare(struct clk_hw *hw)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
9062306a36Sopenharmony_ci	void __iomem *sckcr = osc->sckcr;
9162306a36Sopenharmony_ci	u32 tmp = readl(sckcr);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (tmp & osc->bits->cr_osc32byp)
9462306a36Sopenharmony_ci		return;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	writel(tmp & ~osc->bits->cr_osc32en, sckcr);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int clk_slow_osc_is_prepared(struct clk_hw *hw)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
10262306a36Sopenharmony_ci	void __iomem *sckcr = osc->sckcr;
10362306a36Sopenharmony_ci	u32 tmp = readl(sckcr);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (tmp & osc->bits->cr_osc32byp)
10662306a36Sopenharmony_ci		return 1;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return !!(tmp & osc->bits->cr_osc32en);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic const struct clk_ops slow_osc_ops = {
11262306a36Sopenharmony_ci	.prepare = clk_slow_osc_prepare,
11362306a36Sopenharmony_ci	.unprepare = clk_slow_osc_unprepare,
11462306a36Sopenharmony_ci	.is_prepared = clk_slow_osc_is_prepared,
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic struct clk_hw * __init
11862306a36Sopenharmony_ciat91_clk_register_slow_osc(void __iomem *sckcr,
11962306a36Sopenharmony_ci			   const char *name,
12062306a36Sopenharmony_ci			   const struct clk_parent_data *parent_data,
12162306a36Sopenharmony_ci			   unsigned long startup,
12262306a36Sopenharmony_ci			   bool bypass,
12362306a36Sopenharmony_ci			   const struct clk_slow_bits *bits)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct clk_slow_osc *osc;
12662306a36Sopenharmony_ci	struct clk_hw *hw;
12762306a36Sopenharmony_ci	struct clk_init_data init = {};
12862306a36Sopenharmony_ci	int ret;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!sckcr || !name || !parent_data)
13162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
13462306a36Sopenharmony_ci	if (!osc)
13562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	init.name = name;
13862306a36Sopenharmony_ci	init.ops = &slow_osc_ops;
13962306a36Sopenharmony_ci	init.parent_data = parent_data;
14062306a36Sopenharmony_ci	init.num_parents = 1;
14162306a36Sopenharmony_ci	init.flags = CLK_IGNORE_UNUSED;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	osc->hw.init = &init;
14462306a36Sopenharmony_ci	osc->sckcr = sckcr;
14562306a36Sopenharmony_ci	osc->startup_usec = startup;
14662306a36Sopenharmony_ci	osc->bits = bits;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (bypass)
14962306a36Sopenharmony_ci		writel((readl(sckcr) & ~osc->bits->cr_osc32en) |
15062306a36Sopenharmony_ci					osc->bits->cr_osc32byp, sckcr);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	hw = &osc->hw;
15362306a36Sopenharmony_ci	ret = clk_hw_register(NULL, &osc->hw);
15462306a36Sopenharmony_ci	if (ret) {
15562306a36Sopenharmony_ci		kfree(osc);
15662306a36Sopenharmony_ci		hw = ERR_PTR(ret);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	return hw;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void at91_clk_unregister_slow_osc(struct clk_hw *hw)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	clk_hw_unregister(hw);
16762306a36Sopenharmony_ci	kfree(osc);
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
17162306a36Sopenharmony_ci						 unsigned long parent_rate)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return osc->frequency;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
17962306a36Sopenharmony_ci						     unsigned long parent_acc)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return osc->accuracy;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_cistatic int clk_slow_rc_osc_prepare(struct clk_hw *hw)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
18962306a36Sopenharmony_ci	void __iomem *sckcr = osc->sckcr;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (system_state < SYSTEM_RUNNING)
19462306a36Sopenharmony_ci		udelay(osc->startup_usec);
19562306a36Sopenharmony_ci	else
19662306a36Sopenharmony_ci		usleep_range(osc->startup_usec, osc->startup_usec + 1);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
20462306a36Sopenharmony_ci	void __iomem *sckcr = osc->sckcr;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return !!(readl(osc->sckcr) & osc->bits->cr_rcen);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic const struct clk_ops slow_rc_osc_ops = {
21762306a36Sopenharmony_ci	.prepare = clk_slow_rc_osc_prepare,
21862306a36Sopenharmony_ci	.unprepare = clk_slow_rc_osc_unprepare,
21962306a36Sopenharmony_ci	.is_prepared = clk_slow_rc_osc_is_prepared,
22062306a36Sopenharmony_ci	.recalc_rate = clk_slow_rc_osc_recalc_rate,
22162306a36Sopenharmony_ci	.recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
22262306a36Sopenharmony_ci};
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic struct clk_hw * __init
22562306a36Sopenharmony_ciat91_clk_register_slow_rc_osc(void __iomem *sckcr,
22662306a36Sopenharmony_ci			      const char *name,
22762306a36Sopenharmony_ci			      unsigned long frequency,
22862306a36Sopenharmony_ci			      unsigned long accuracy,
22962306a36Sopenharmony_ci			      unsigned long startup,
23062306a36Sopenharmony_ci			      const struct clk_slow_bits *bits)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc;
23362306a36Sopenharmony_ci	struct clk_hw *hw;
23462306a36Sopenharmony_ci	struct clk_init_data init;
23562306a36Sopenharmony_ci	int ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	if (!sckcr || !name)
23862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
24162306a36Sopenharmony_ci	if (!osc)
24262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	init.name = name;
24562306a36Sopenharmony_ci	init.ops = &slow_rc_osc_ops;
24662306a36Sopenharmony_ci	init.parent_names = NULL;
24762306a36Sopenharmony_ci	init.num_parents = 0;
24862306a36Sopenharmony_ci	init.flags = CLK_IGNORE_UNUSED;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	osc->hw.init = &init;
25162306a36Sopenharmony_ci	osc->sckcr = sckcr;
25262306a36Sopenharmony_ci	osc->bits = bits;
25362306a36Sopenharmony_ci	osc->frequency = frequency;
25462306a36Sopenharmony_ci	osc->accuracy = accuracy;
25562306a36Sopenharmony_ci	osc->startup_usec = startup;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	hw = &osc->hw;
25862306a36Sopenharmony_ci	ret = clk_hw_register(NULL, &osc->hw);
25962306a36Sopenharmony_ci	if (ret) {
26062306a36Sopenharmony_ci		kfree(osc);
26162306a36Sopenharmony_ci		hw = ERR_PTR(ret);
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return hw;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	clk_hw_unregister(hw);
27262306a36Sopenharmony_ci	kfree(osc);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
27862306a36Sopenharmony_ci	void __iomem *sckcr = slowck->sckcr;
27962306a36Sopenharmony_ci	u32 tmp;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (index > 1)
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	tmp = readl(sckcr);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||
28762306a36Sopenharmony_ci	    (index && (tmp & slowck->bits->cr_oscsel)))
28862306a36Sopenharmony_ci		return 0;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (index)
29162306a36Sopenharmony_ci		tmp |= slowck->bits->cr_oscsel;
29262306a36Sopenharmony_ci	else
29362306a36Sopenharmony_ci		tmp &= ~slowck->bits->cr_oscsel;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	writel(tmp, sckcr);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (system_state < SYSTEM_RUNNING)
29862306a36Sopenharmony_ci		udelay(SLOWCK_SW_TIME_USEC);
29962306a36Sopenharmony_ci	else
30062306a36Sopenharmony_ci		usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return 0;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic const struct clk_ops sam9x5_slow_ops = {
31362306a36Sopenharmony_ci	.determine_rate = clk_hw_determine_rate_no_reparent,
31462306a36Sopenharmony_ci	.set_parent = clk_sam9x5_slow_set_parent,
31562306a36Sopenharmony_ci	.get_parent = clk_sam9x5_slow_get_parent,
31662306a36Sopenharmony_ci};
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic struct clk_hw * __init
31962306a36Sopenharmony_ciat91_clk_register_sam9x5_slow(void __iomem *sckcr,
32062306a36Sopenharmony_ci			      const char *name,
32162306a36Sopenharmony_ci			      const struct clk_hw **parent_hws,
32262306a36Sopenharmony_ci			      int num_parents,
32362306a36Sopenharmony_ci			      const struct clk_slow_bits *bits)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct clk_sam9x5_slow *slowck;
32662306a36Sopenharmony_ci	struct clk_hw *hw;
32762306a36Sopenharmony_ci	struct clk_init_data init = {};
32862306a36Sopenharmony_ci	int ret;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!sckcr || !name || !parent_hws || !num_parents)
33162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
33462306a36Sopenharmony_ci	if (!slowck)
33562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	init.name = name;
33862306a36Sopenharmony_ci	init.ops = &sam9x5_slow_ops;
33962306a36Sopenharmony_ci	init.parent_hws = parent_hws;
34062306a36Sopenharmony_ci	init.num_parents = num_parents;
34162306a36Sopenharmony_ci	init.flags = 0;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	slowck->hw.init = &init;
34462306a36Sopenharmony_ci	slowck->sckcr = sckcr;
34562306a36Sopenharmony_ci	slowck->bits = bits;
34662306a36Sopenharmony_ci	slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	hw = &slowck->hw;
34962306a36Sopenharmony_ci	ret = clk_hw_register(NULL, &slowck->hw);
35062306a36Sopenharmony_ci	if (ret) {
35162306a36Sopenharmony_ci		kfree(slowck);
35262306a36Sopenharmony_ci		hw = ERR_PTR(ret);
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return hw;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	clk_hw_unregister(hw);
36362306a36Sopenharmony_ci	kfree(slowck);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void __init at91sam9x5_sckc_register(struct device_node *np,
36762306a36Sopenharmony_ci					    unsigned int rc_osc_startup_us,
36862306a36Sopenharmony_ci					    const struct clk_slow_bits *bits)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	void __iomem *regbase = of_iomap(np, 0);
37162306a36Sopenharmony_ci	struct device_node *child = NULL;
37262306a36Sopenharmony_ci	const char *xtal_name;
37362306a36Sopenharmony_ci	struct clk_hw *slow_rc, *slow_osc, *slowck;
37462306a36Sopenharmony_ci	static struct clk_parent_data parent_data = {
37562306a36Sopenharmony_ci		.name = "slow_xtal",
37662306a36Sopenharmony_ci	};
37762306a36Sopenharmony_ci	const struct clk_hw *parent_hws[2];
37862306a36Sopenharmony_ci	bool bypass;
37962306a36Sopenharmony_ci	int ret;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (!regbase)
38262306a36Sopenharmony_ci		return;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc",
38562306a36Sopenharmony_ci						32768, 50000000,
38662306a36Sopenharmony_ci						rc_osc_startup_us, bits);
38762306a36Sopenharmony_ci	if (IS_ERR(slow_rc))
38862306a36Sopenharmony_ci		return;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	xtal_name = of_clk_get_parent_name(np, 0);
39162306a36Sopenharmony_ci	if (!xtal_name) {
39262306a36Sopenharmony_ci		/* DT backward compatibility */
39362306a36Sopenharmony_ci		child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");
39462306a36Sopenharmony_ci		if (!child)
39562306a36Sopenharmony_ci			goto unregister_slow_rc;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		xtal_name = of_clk_get_parent_name(child, 0);
39862306a36Sopenharmony_ci		bypass = of_property_read_bool(child, "atmel,osc-bypass");
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		child =  of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");
40162306a36Sopenharmony_ci	} else {
40262306a36Sopenharmony_ci		bypass = of_property_read_bool(np, "atmel,osc-bypass");
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!xtal_name)
40662306a36Sopenharmony_ci		goto unregister_slow_rc;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	parent_data.fw_name = xtal_name;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
41162306a36Sopenharmony_ci					      &parent_data, 1200000, bypass, bits);
41262306a36Sopenharmony_ci	if (IS_ERR(slow_osc))
41362306a36Sopenharmony_ci		goto unregister_slow_rc;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	parent_hws[0] = slow_rc;
41662306a36Sopenharmony_ci	parent_hws[1] = slow_osc;
41762306a36Sopenharmony_ci	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws,
41862306a36Sopenharmony_ci					       2, bits);
41962306a36Sopenharmony_ci	if (IS_ERR(slowck))
42062306a36Sopenharmony_ci		goto unregister_slow_osc;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* DT backward compatibility */
42362306a36Sopenharmony_ci	if (child)
42462306a36Sopenharmony_ci		ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,
42562306a36Sopenharmony_ci					     slowck);
42662306a36Sopenharmony_ci	else
42762306a36Sopenharmony_ci		ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (WARN_ON(ret))
43062306a36Sopenharmony_ci		goto unregister_slowck;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ciunregister_slowck:
43562306a36Sopenharmony_ci	at91_clk_unregister_sam9x5_slow(slowck);
43662306a36Sopenharmony_ciunregister_slow_osc:
43762306a36Sopenharmony_ci	at91_clk_unregister_slow_osc(slow_osc);
43862306a36Sopenharmony_ciunregister_slow_rc:
43962306a36Sopenharmony_ci	at91_clk_unregister_slow_rc_osc(slow_rc);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic const struct clk_slow_bits at91sam9x5_bits = {
44362306a36Sopenharmony_ci	.cr_rcen = BIT(0),
44462306a36Sopenharmony_ci	.cr_osc32en = BIT(1),
44562306a36Sopenharmony_ci	.cr_osc32byp = BIT(2),
44662306a36Sopenharmony_ci	.cr_oscsel = BIT(3),
44762306a36Sopenharmony_ci};
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_cistatic void __init of_at91sam9x5_sckc_setup(struct device_node *np)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ciCLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
45462306a36Sopenharmony_ci	       of_at91sam9x5_sckc_setup);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic void __init of_sama5d3_sckc_setup(struct device_node *np)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ciCLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",
46162306a36Sopenharmony_ci	       of_sama5d3_sckc_setup);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic const struct clk_slow_bits at91sam9x60_bits = {
46462306a36Sopenharmony_ci	.cr_osc32en = BIT(1),
46562306a36Sopenharmony_ci	.cr_osc32byp = BIT(2),
46662306a36Sopenharmony_ci	.cr_oscsel = BIT(24),
46762306a36Sopenharmony_ci};
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic void __init of_sam9x60_sckc_setup(struct device_node *np)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	void __iomem *regbase = of_iomap(np, 0);
47262306a36Sopenharmony_ci	struct clk_hw_onecell_data *clk_data;
47362306a36Sopenharmony_ci	struct clk_hw *slow_rc, *slow_osc;
47462306a36Sopenharmony_ci	const char *xtal_name;
47562306a36Sopenharmony_ci	const struct clk_hw *parent_hws[2];
47662306a36Sopenharmony_ci	static struct clk_parent_data parent_data = {
47762306a36Sopenharmony_ci		.name = "slow_xtal",
47862306a36Sopenharmony_ci	};
47962306a36Sopenharmony_ci	bool bypass;
48062306a36Sopenharmony_ci	int ret;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (!regbase)
48362306a36Sopenharmony_ci		return;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc",
48662306a36Sopenharmony_ci							   NULL, 0, 32768,
48762306a36Sopenharmony_ci							   93750000);
48862306a36Sopenharmony_ci	if (IS_ERR(slow_rc))
48962306a36Sopenharmony_ci		return;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	xtal_name = of_clk_get_parent_name(np, 0);
49262306a36Sopenharmony_ci	if (!xtal_name)
49362306a36Sopenharmony_ci		goto unregister_slow_rc;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	parent_data.fw_name = xtal_name;
49662306a36Sopenharmony_ci	bypass = of_property_read_bool(np, "atmel,osc-bypass");
49762306a36Sopenharmony_ci	slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",
49862306a36Sopenharmony_ci					      &parent_data, 5000000, bypass,
49962306a36Sopenharmony_ci					      &at91sam9x60_bits);
50062306a36Sopenharmony_ci	if (IS_ERR(slow_osc))
50162306a36Sopenharmony_ci		goto unregister_slow_rc;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);
50462306a36Sopenharmony_ci	if (!clk_data)
50562306a36Sopenharmony_ci		goto unregister_slow_osc;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	/* MD_SLCK and TD_SLCK. */
50862306a36Sopenharmony_ci	clk_data->num = 2;
50962306a36Sopenharmony_ci	clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck",
51062306a36Sopenharmony_ci								slow_rc,
51162306a36Sopenharmony_ci								0, 32768);
51262306a36Sopenharmony_ci	if (IS_ERR(clk_data->hws[0]))
51362306a36Sopenharmony_ci		goto clk_data_free;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	parent_hws[0] = slow_rc;
51662306a36Sopenharmony_ci	parent_hws[1] = slow_osc;
51762306a36Sopenharmony_ci	clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",
51862306a36Sopenharmony_ci							 parent_hws, 2,
51962306a36Sopenharmony_ci							 &at91sam9x60_bits);
52062306a36Sopenharmony_ci	if (IS_ERR(clk_data->hws[1]))
52162306a36Sopenharmony_ci		goto unregister_md_slck;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
52462306a36Sopenharmony_ci	if (WARN_ON(ret))
52562306a36Sopenharmony_ci		goto unregister_td_slck;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	return;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ciunregister_td_slck:
53062306a36Sopenharmony_ci	at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);
53162306a36Sopenharmony_ciunregister_md_slck:
53262306a36Sopenharmony_ci	clk_hw_unregister(clk_data->hws[0]);
53362306a36Sopenharmony_ciclk_data_free:
53462306a36Sopenharmony_ci	kfree(clk_data);
53562306a36Sopenharmony_ciunregister_slow_osc:
53662306a36Sopenharmony_ci	at91_clk_unregister_slow_osc(slow_osc);
53762306a36Sopenharmony_ciunregister_slow_rc:
53862306a36Sopenharmony_ci	clk_hw_unregister(slow_rc);
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ciCLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",
54162306a36Sopenharmony_ci	       of_sam9x60_sckc_setup);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (osc->prepared)
54862306a36Sopenharmony_ci		return 0;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/*
55162306a36Sopenharmony_ci	 * Assume that if it has already been selected (for example by the
55262306a36Sopenharmony_ci	 * bootloader), enough time has already passed.
55362306a36Sopenharmony_ci	 */
55462306a36Sopenharmony_ci	if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {
55562306a36Sopenharmony_ci		osc->prepared = true;
55662306a36Sopenharmony_ci		return 0;
55762306a36Sopenharmony_ci	}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	if (system_state < SYSTEM_RUNNING)
56062306a36Sopenharmony_ci		udelay(osc->startup_usec);
56162306a36Sopenharmony_ci	else
56262306a36Sopenharmony_ci		usleep_range(osc->startup_usec, osc->startup_usec + 1);
56362306a36Sopenharmony_ci	osc->prepared = true;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return osc->prepared;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic const struct clk_ops sama5d4_slow_osc_ops = {
57662306a36Sopenharmony_ci	.prepare = clk_sama5d4_slow_osc_prepare,
57762306a36Sopenharmony_ci	.is_prepared = clk_sama5d4_slow_osc_is_prepared,
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic const struct clk_slow_bits at91sama5d4_bits = {
58162306a36Sopenharmony_ci	.cr_oscsel = BIT(3),
58262306a36Sopenharmony_ci};
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void __init of_sama5d4_sckc_setup(struct device_node *np)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	void __iomem *regbase = of_iomap(np, 0);
58762306a36Sopenharmony_ci	struct clk_hw *slow_rc, *slowck;
58862306a36Sopenharmony_ci	struct clk_sama5d4_slow_osc *osc;
58962306a36Sopenharmony_ci	struct clk_init_data init = {};
59062306a36Sopenharmony_ci	const char *xtal_name;
59162306a36Sopenharmony_ci	const struct clk_hw *parent_hws[2];
59262306a36Sopenharmony_ci	static struct clk_parent_data parent_data = {
59362306a36Sopenharmony_ci		.name = "slow_xtal",
59462306a36Sopenharmony_ci	};
59562306a36Sopenharmony_ci	int ret;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (!regbase)
59862306a36Sopenharmony_ci		return;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,
60162306a36Sopenharmony_ci							   "slow_rc_osc",
60262306a36Sopenharmony_ci							   NULL, 0, 32768,
60362306a36Sopenharmony_ci							   250000000);
60462306a36Sopenharmony_ci	if (IS_ERR(slow_rc))
60562306a36Sopenharmony_ci		return;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	xtal_name = of_clk_get_parent_name(np, 0);
60862306a36Sopenharmony_ci	if (!xtal_name)
60962306a36Sopenharmony_ci		goto unregister_slow_rc;
61062306a36Sopenharmony_ci	parent_data.fw_name = xtal_name;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
61362306a36Sopenharmony_ci	if (!osc)
61462306a36Sopenharmony_ci		goto unregister_slow_rc;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	init.name = "slow_osc";
61762306a36Sopenharmony_ci	init.ops = &sama5d4_slow_osc_ops;
61862306a36Sopenharmony_ci	init.parent_data = &parent_data;
61962306a36Sopenharmony_ci	init.num_parents = 1;
62062306a36Sopenharmony_ci	init.flags = CLK_IGNORE_UNUSED;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	osc->hw.init = &init;
62362306a36Sopenharmony_ci	osc->sckcr = regbase;
62462306a36Sopenharmony_ci	osc->startup_usec = 1200000;
62562306a36Sopenharmony_ci	osc->bits = &at91sama5d4_bits;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	ret = clk_hw_register(NULL, &osc->hw);
62862306a36Sopenharmony_ci	if (ret)
62962306a36Sopenharmony_ci		goto free_slow_osc_data;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	parent_hws[0] = slow_rc;
63262306a36Sopenharmony_ci	parent_hws[1] = &osc->hw;
63362306a36Sopenharmony_ci	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",
63462306a36Sopenharmony_ci					       parent_hws, 2,
63562306a36Sopenharmony_ci					       &at91sama5d4_bits);
63662306a36Sopenharmony_ci	if (IS_ERR(slowck))
63762306a36Sopenharmony_ci		goto unregister_slow_osc;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);
64062306a36Sopenharmony_ci	if (WARN_ON(ret))
64162306a36Sopenharmony_ci		goto unregister_slowck;
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return;
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ciunregister_slowck:
64662306a36Sopenharmony_ci	at91_clk_unregister_sam9x5_slow(slowck);
64762306a36Sopenharmony_ciunregister_slow_osc:
64862306a36Sopenharmony_ci	clk_hw_unregister(&osc->hw);
64962306a36Sopenharmony_cifree_slow_osc_data:
65062306a36Sopenharmony_ci	kfree(osc);
65162306a36Sopenharmony_ciunregister_slow_rc:
65262306a36Sopenharmony_ci	clk_hw_unregister(slow_rc);
65362306a36Sopenharmony_ci}
65462306a36Sopenharmony_ciCLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
65562306a36Sopenharmony_ci	       of_sama5d4_sckc_setup);
656