xref: /openbmc/linux/drivers/clk/at91/clk-slow.c (revision f5644f10dcfbab90ffd27da1d8d51ffc13e1bc84)
180eded6cSBoris BREZILLON /*
280eded6cSBoris BREZILLON  * drivers/clk/at91/clk-slow.c
380eded6cSBoris BREZILLON  *
480eded6cSBoris BREZILLON  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
580eded6cSBoris BREZILLON  *
680eded6cSBoris BREZILLON  * This program is free software; you can redistribute it and/or modify
780eded6cSBoris BREZILLON  * it under the terms of the GNU General Public License as published by
880eded6cSBoris BREZILLON  * the Free Software Foundation; either version 2 of the License, or
980eded6cSBoris BREZILLON  * (at your option) any later version.
1080eded6cSBoris BREZILLON  *
1180eded6cSBoris BREZILLON  */
1280eded6cSBoris BREZILLON 
1380eded6cSBoris BREZILLON #include <linux/clk-provider.h>
1480eded6cSBoris BREZILLON #include <linux/clkdev.h>
1580eded6cSBoris BREZILLON #include <linux/clk/at91_pmc.h>
1680eded6cSBoris BREZILLON #include <linux/delay.h>
1780eded6cSBoris BREZILLON #include <linux/of.h>
181bdf0232SBoris Brezillon #include <linux/mfd/syscon.h>
191bdf0232SBoris Brezillon #include <linux/regmap.h>
2080eded6cSBoris BREZILLON 
2180eded6cSBoris BREZILLON #include "pmc.h"
2280eded6cSBoris BREZILLON #include "sckc.h"
2380eded6cSBoris BREZILLON 
2480eded6cSBoris BREZILLON #define SLOW_CLOCK_FREQ		32768
2580eded6cSBoris BREZILLON #define SLOWCK_SW_CYCLES	5
2680eded6cSBoris BREZILLON #define SLOWCK_SW_TIME_USEC	((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
2780eded6cSBoris BREZILLON 				 SLOW_CLOCK_FREQ)
2880eded6cSBoris BREZILLON 
2980eded6cSBoris BREZILLON #define	AT91_SCKC_CR			0x00
3080eded6cSBoris BREZILLON #define		AT91_SCKC_RCEN		(1 << 0)
3180eded6cSBoris BREZILLON #define		AT91_SCKC_OSC32EN	(1 << 1)
3280eded6cSBoris BREZILLON #define		AT91_SCKC_OSC32BYP	(1 << 2)
3380eded6cSBoris BREZILLON #define		AT91_SCKC_OSCSEL	(1 << 3)
3480eded6cSBoris BREZILLON 
3580eded6cSBoris BREZILLON struct clk_slow_osc {
3680eded6cSBoris BREZILLON 	struct clk_hw hw;
3780eded6cSBoris BREZILLON 	void __iomem *sckcr;
3880eded6cSBoris BREZILLON 	unsigned long startup_usec;
3980eded6cSBoris BREZILLON };
4080eded6cSBoris BREZILLON 
4180eded6cSBoris BREZILLON #define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
4280eded6cSBoris BREZILLON 
4380eded6cSBoris BREZILLON struct clk_slow_rc_osc {
4480eded6cSBoris BREZILLON 	struct clk_hw hw;
4580eded6cSBoris BREZILLON 	void __iomem *sckcr;
4680eded6cSBoris BREZILLON 	unsigned long frequency;
4780eded6cSBoris BREZILLON 	unsigned long accuracy;
4880eded6cSBoris BREZILLON 	unsigned long startup_usec;
4980eded6cSBoris BREZILLON };
5080eded6cSBoris BREZILLON 
5180eded6cSBoris BREZILLON #define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
5280eded6cSBoris BREZILLON 
5380eded6cSBoris BREZILLON struct clk_sam9260_slow {
5480eded6cSBoris BREZILLON 	struct clk_hw hw;
551bdf0232SBoris Brezillon 	struct regmap *regmap;
5680eded6cSBoris BREZILLON };
5780eded6cSBoris BREZILLON 
5880eded6cSBoris BREZILLON #define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw)
5980eded6cSBoris BREZILLON 
6080eded6cSBoris BREZILLON struct clk_sam9x5_slow {
6180eded6cSBoris BREZILLON 	struct clk_hw hw;
6280eded6cSBoris BREZILLON 	void __iomem *sckcr;
6380eded6cSBoris BREZILLON 	u8 parent;
6480eded6cSBoris BREZILLON };
6580eded6cSBoris BREZILLON 
6680eded6cSBoris BREZILLON #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
6780eded6cSBoris BREZILLON 
6880eded6cSBoris BREZILLON static int clk_slow_osc_prepare(struct clk_hw *hw)
6980eded6cSBoris BREZILLON {
7080eded6cSBoris BREZILLON 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
7180eded6cSBoris BREZILLON 	void __iomem *sckcr = osc->sckcr;
7280eded6cSBoris BREZILLON 	u32 tmp = readl(sckcr);
7380eded6cSBoris BREZILLON 
7480eded6cSBoris BREZILLON 	if (tmp & AT91_SCKC_OSC32BYP)
7580eded6cSBoris BREZILLON 		return 0;
7680eded6cSBoris BREZILLON 
7780eded6cSBoris BREZILLON 	writel(tmp | AT91_SCKC_OSC32EN, sckcr);
7880eded6cSBoris BREZILLON 
7980eded6cSBoris BREZILLON 	usleep_range(osc->startup_usec, osc->startup_usec + 1);
8080eded6cSBoris BREZILLON 
8180eded6cSBoris BREZILLON 	return 0;
8280eded6cSBoris BREZILLON }
8380eded6cSBoris BREZILLON 
8480eded6cSBoris BREZILLON static void clk_slow_osc_unprepare(struct clk_hw *hw)
8580eded6cSBoris BREZILLON {
8680eded6cSBoris BREZILLON 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
8780eded6cSBoris BREZILLON 	void __iomem *sckcr = osc->sckcr;
8880eded6cSBoris BREZILLON 	u32 tmp = readl(sckcr);
8980eded6cSBoris BREZILLON 
9080eded6cSBoris BREZILLON 	if (tmp & AT91_SCKC_OSC32BYP)
9180eded6cSBoris BREZILLON 		return;
9280eded6cSBoris BREZILLON 
9380eded6cSBoris BREZILLON 	writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
9480eded6cSBoris BREZILLON }
9580eded6cSBoris BREZILLON 
9680eded6cSBoris BREZILLON static int clk_slow_osc_is_prepared(struct clk_hw *hw)
9780eded6cSBoris BREZILLON {
9880eded6cSBoris BREZILLON 	struct clk_slow_osc *osc = to_clk_slow_osc(hw);
9980eded6cSBoris BREZILLON 	void __iomem *sckcr = osc->sckcr;
10080eded6cSBoris BREZILLON 	u32 tmp = readl(sckcr);
10180eded6cSBoris BREZILLON 
10280eded6cSBoris BREZILLON 	if (tmp & AT91_SCKC_OSC32BYP)
10380eded6cSBoris BREZILLON 		return 1;
10480eded6cSBoris BREZILLON 
10580eded6cSBoris BREZILLON 	return !!(tmp & AT91_SCKC_OSC32EN);
10680eded6cSBoris BREZILLON }
10780eded6cSBoris BREZILLON 
10880eded6cSBoris BREZILLON static const struct clk_ops slow_osc_ops = {
10980eded6cSBoris BREZILLON 	.prepare = clk_slow_osc_prepare,
11080eded6cSBoris BREZILLON 	.unprepare = clk_slow_osc_unprepare,
11180eded6cSBoris BREZILLON 	.is_prepared = clk_slow_osc_is_prepared,
11280eded6cSBoris BREZILLON };
11380eded6cSBoris BREZILLON 
114*f5644f10SStephen Boyd static struct clk_hw * __init
11580eded6cSBoris BREZILLON at91_clk_register_slow_osc(void __iomem *sckcr,
11680eded6cSBoris BREZILLON 			   const char *name,
11780eded6cSBoris BREZILLON 			   const char *parent_name,
11880eded6cSBoris BREZILLON 			   unsigned long startup,
11980eded6cSBoris BREZILLON 			   bool bypass)
12080eded6cSBoris BREZILLON {
12180eded6cSBoris BREZILLON 	struct clk_slow_osc *osc;
122*f5644f10SStephen Boyd 	struct clk_hw *hw;
12380eded6cSBoris BREZILLON 	struct clk_init_data init;
124*f5644f10SStephen Boyd 	int ret;
12580eded6cSBoris BREZILLON 
12680eded6cSBoris BREZILLON 	if (!sckcr || !name || !parent_name)
12780eded6cSBoris BREZILLON 		return ERR_PTR(-EINVAL);
12880eded6cSBoris BREZILLON 
12980eded6cSBoris BREZILLON 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
13080eded6cSBoris BREZILLON 	if (!osc)
13180eded6cSBoris BREZILLON 		return ERR_PTR(-ENOMEM);
13280eded6cSBoris BREZILLON 
13380eded6cSBoris BREZILLON 	init.name = name;
13480eded6cSBoris BREZILLON 	init.ops = &slow_osc_ops;
13580eded6cSBoris BREZILLON 	init.parent_names = &parent_name;
13680eded6cSBoris BREZILLON 	init.num_parents = 1;
13780eded6cSBoris BREZILLON 	init.flags = CLK_IGNORE_UNUSED;
13880eded6cSBoris BREZILLON 
13980eded6cSBoris BREZILLON 	osc->hw.init = &init;
14080eded6cSBoris BREZILLON 	osc->sckcr = sckcr;
14180eded6cSBoris BREZILLON 	osc->startup_usec = startup;
14280eded6cSBoris BREZILLON 
14380eded6cSBoris BREZILLON 	if (bypass)
14480eded6cSBoris BREZILLON 		writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
14580eded6cSBoris BREZILLON 		       sckcr);
14680eded6cSBoris BREZILLON 
147*f5644f10SStephen Boyd 	hw = &osc->hw;
148*f5644f10SStephen Boyd 	ret = clk_hw_register(NULL, &osc->hw);
149*f5644f10SStephen Boyd 	if (ret) {
15080eded6cSBoris BREZILLON 		kfree(osc);
151*f5644f10SStephen Boyd 		hw = ERR_PTR(ret);
152*f5644f10SStephen Boyd 	}
15380eded6cSBoris BREZILLON 
154*f5644f10SStephen Boyd 	return hw;
15580eded6cSBoris BREZILLON }
15680eded6cSBoris BREZILLON 
15780eded6cSBoris BREZILLON void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np,
15880eded6cSBoris BREZILLON 					     void __iomem *sckcr)
15980eded6cSBoris BREZILLON {
160*f5644f10SStephen Boyd 	struct clk_hw *hw;
16180eded6cSBoris BREZILLON 	const char *parent_name;
16280eded6cSBoris BREZILLON 	const char *name = np->name;
16380eded6cSBoris BREZILLON 	u32 startup;
16480eded6cSBoris BREZILLON 	bool bypass;
16580eded6cSBoris BREZILLON 
16680eded6cSBoris BREZILLON 	parent_name = of_clk_get_parent_name(np, 0);
16780eded6cSBoris BREZILLON 	of_property_read_string(np, "clock-output-names", &name);
16880eded6cSBoris BREZILLON 	of_property_read_u32(np, "atmel,startup-time-usec", &startup);
16980eded6cSBoris BREZILLON 	bypass = of_property_read_bool(np, "atmel,osc-bypass");
17080eded6cSBoris BREZILLON 
171*f5644f10SStephen Boyd 	hw = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
17280eded6cSBoris BREZILLON 					 bypass);
173*f5644f10SStephen Boyd 	if (IS_ERR(hw))
17480eded6cSBoris BREZILLON 		return;
17580eded6cSBoris BREZILLON 
176*f5644f10SStephen Boyd 	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
17780eded6cSBoris BREZILLON }
17880eded6cSBoris BREZILLON 
17980eded6cSBoris BREZILLON static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
18080eded6cSBoris BREZILLON 						 unsigned long parent_rate)
18180eded6cSBoris BREZILLON {
18280eded6cSBoris BREZILLON 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
18380eded6cSBoris BREZILLON 
18480eded6cSBoris BREZILLON 	return osc->frequency;
18580eded6cSBoris BREZILLON }
18680eded6cSBoris BREZILLON 
18780eded6cSBoris BREZILLON static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
18880eded6cSBoris BREZILLON 						     unsigned long parent_acc)
18980eded6cSBoris BREZILLON {
19080eded6cSBoris BREZILLON 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
19180eded6cSBoris BREZILLON 
19280eded6cSBoris BREZILLON 	return osc->accuracy;
19380eded6cSBoris BREZILLON }
19480eded6cSBoris BREZILLON 
19580eded6cSBoris BREZILLON static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
19680eded6cSBoris BREZILLON {
19780eded6cSBoris BREZILLON 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
19880eded6cSBoris BREZILLON 	void __iomem *sckcr = osc->sckcr;
19980eded6cSBoris BREZILLON 
20080eded6cSBoris BREZILLON 	writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
20180eded6cSBoris BREZILLON 
20280eded6cSBoris BREZILLON 	usleep_range(osc->startup_usec, osc->startup_usec + 1);
20380eded6cSBoris BREZILLON 
20480eded6cSBoris BREZILLON 	return 0;
20580eded6cSBoris BREZILLON }
20680eded6cSBoris BREZILLON 
20780eded6cSBoris BREZILLON static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
20880eded6cSBoris BREZILLON {
20980eded6cSBoris BREZILLON 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
21080eded6cSBoris BREZILLON 	void __iomem *sckcr = osc->sckcr;
21180eded6cSBoris BREZILLON 
21280eded6cSBoris BREZILLON 	writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
21380eded6cSBoris BREZILLON }
21480eded6cSBoris BREZILLON 
21580eded6cSBoris BREZILLON static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)
21680eded6cSBoris BREZILLON {
21780eded6cSBoris BREZILLON 	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
21880eded6cSBoris BREZILLON 
21980eded6cSBoris BREZILLON 	return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
22080eded6cSBoris BREZILLON }
22180eded6cSBoris BREZILLON 
22280eded6cSBoris BREZILLON static const struct clk_ops slow_rc_osc_ops = {
22380eded6cSBoris BREZILLON 	.prepare = clk_slow_rc_osc_prepare,
22480eded6cSBoris BREZILLON 	.unprepare = clk_slow_rc_osc_unprepare,
22580eded6cSBoris BREZILLON 	.is_prepared = clk_slow_rc_osc_is_prepared,
22680eded6cSBoris BREZILLON 	.recalc_rate = clk_slow_rc_osc_recalc_rate,
22780eded6cSBoris BREZILLON 	.recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,
22880eded6cSBoris BREZILLON };
22980eded6cSBoris BREZILLON 
230*f5644f10SStephen Boyd static struct clk_hw * __init
23180eded6cSBoris BREZILLON at91_clk_register_slow_rc_osc(void __iomem *sckcr,
23280eded6cSBoris BREZILLON 			      const char *name,
23380eded6cSBoris BREZILLON 			      unsigned long frequency,
23480eded6cSBoris BREZILLON 			      unsigned long accuracy,
23580eded6cSBoris BREZILLON 			      unsigned long startup)
23680eded6cSBoris BREZILLON {
23780eded6cSBoris BREZILLON 	struct clk_slow_rc_osc *osc;
238*f5644f10SStephen Boyd 	struct clk_hw *hw;
23980eded6cSBoris BREZILLON 	struct clk_init_data init;
240*f5644f10SStephen Boyd 	int ret;
24180eded6cSBoris BREZILLON 
24280eded6cSBoris BREZILLON 	if (!sckcr || !name)
24380eded6cSBoris BREZILLON 		return ERR_PTR(-EINVAL);
24480eded6cSBoris BREZILLON 
24580eded6cSBoris BREZILLON 	osc = kzalloc(sizeof(*osc), GFP_KERNEL);
24680eded6cSBoris BREZILLON 	if (!osc)
24780eded6cSBoris BREZILLON 		return ERR_PTR(-ENOMEM);
24880eded6cSBoris BREZILLON 
24980eded6cSBoris BREZILLON 	init.name = name;
25080eded6cSBoris BREZILLON 	init.ops = &slow_rc_osc_ops;
25180eded6cSBoris BREZILLON 	init.parent_names = NULL;
25280eded6cSBoris BREZILLON 	init.num_parents = 0;
253a9bb2ef7SStephen Boyd 	init.flags = CLK_IGNORE_UNUSED;
25480eded6cSBoris BREZILLON 
25580eded6cSBoris BREZILLON 	osc->hw.init = &init;
25680eded6cSBoris BREZILLON 	osc->sckcr = sckcr;
25780eded6cSBoris BREZILLON 	osc->frequency = frequency;
25880eded6cSBoris BREZILLON 	osc->accuracy = accuracy;
25980eded6cSBoris BREZILLON 	osc->startup_usec = startup;
26080eded6cSBoris BREZILLON 
261*f5644f10SStephen Boyd 	hw = &osc->hw;
262*f5644f10SStephen Boyd 	ret = clk_hw_register(NULL, &osc->hw);
263*f5644f10SStephen Boyd 	if (ret) {
26480eded6cSBoris BREZILLON 		kfree(osc);
265*f5644f10SStephen Boyd 		hw = ERR_PTR(ret);
266*f5644f10SStephen Boyd 	}
26780eded6cSBoris BREZILLON 
268*f5644f10SStephen Boyd 	return hw;
26980eded6cSBoris BREZILLON }
27080eded6cSBoris BREZILLON 
27180eded6cSBoris BREZILLON void __init of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np,
27280eded6cSBoris BREZILLON 						void __iomem *sckcr)
27380eded6cSBoris BREZILLON {
274*f5644f10SStephen Boyd 	struct clk_hw *hw;
27580eded6cSBoris BREZILLON 	u32 frequency = 0;
27680eded6cSBoris BREZILLON 	u32 accuracy = 0;
27780eded6cSBoris BREZILLON 	u32 startup = 0;
27880eded6cSBoris BREZILLON 	const char *name = np->name;
27980eded6cSBoris BREZILLON 
28080eded6cSBoris BREZILLON 	of_property_read_string(np, "clock-output-names", &name);
28180eded6cSBoris BREZILLON 	of_property_read_u32(np, "clock-frequency", &frequency);
28280eded6cSBoris BREZILLON 	of_property_read_u32(np, "clock-accuracy", &accuracy);
28380eded6cSBoris BREZILLON 	of_property_read_u32(np, "atmel,startup-time-usec", &startup);
28480eded6cSBoris BREZILLON 
285*f5644f10SStephen Boyd 	hw = at91_clk_register_slow_rc_osc(sckcr, name, frequency, accuracy,
28680eded6cSBoris BREZILLON 					    startup);
287*f5644f10SStephen Boyd 	if (IS_ERR(hw))
28880eded6cSBoris BREZILLON 		return;
28980eded6cSBoris BREZILLON 
290*f5644f10SStephen Boyd 	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
29180eded6cSBoris BREZILLON }
29280eded6cSBoris BREZILLON 
29380eded6cSBoris BREZILLON static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)
29480eded6cSBoris BREZILLON {
29580eded6cSBoris BREZILLON 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
29680eded6cSBoris BREZILLON 	void __iomem *sckcr = slowck->sckcr;
29780eded6cSBoris BREZILLON 	u32 tmp;
29880eded6cSBoris BREZILLON 
29980eded6cSBoris BREZILLON 	if (index > 1)
30080eded6cSBoris BREZILLON 		return -EINVAL;
30180eded6cSBoris BREZILLON 
30280eded6cSBoris BREZILLON 	tmp = readl(sckcr);
30380eded6cSBoris BREZILLON 
30480eded6cSBoris BREZILLON 	if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
30580eded6cSBoris BREZILLON 	    (index && (tmp & AT91_SCKC_OSCSEL)))
30680eded6cSBoris BREZILLON 		return 0;
30780eded6cSBoris BREZILLON 
30880eded6cSBoris BREZILLON 	if (index)
30980eded6cSBoris BREZILLON 		tmp |= AT91_SCKC_OSCSEL;
31080eded6cSBoris BREZILLON 	else
31180eded6cSBoris BREZILLON 		tmp &= ~AT91_SCKC_OSCSEL;
31280eded6cSBoris BREZILLON 
31380eded6cSBoris BREZILLON 	writel(tmp, sckcr);
31480eded6cSBoris BREZILLON 
31580eded6cSBoris BREZILLON 	usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);
31680eded6cSBoris BREZILLON 
31780eded6cSBoris BREZILLON 	return 0;
31880eded6cSBoris BREZILLON }
31980eded6cSBoris BREZILLON 
32080eded6cSBoris BREZILLON static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)
32180eded6cSBoris BREZILLON {
32280eded6cSBoris BREZILLON 	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);
32380eded6cSBoris BREZILLON 
32480eded6cSBoris BREZILLON 	return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
32580eded6cSBoris BREZILLON }
32680eded6cSBoris BREZILLON 
32780eded6cSBoris BREZILLON static const struct clk_ops sam9x5_slow_ops = {
32880eded6cSBoris BREZILLON 	.set_parent = clk_sam9x5_slow_set_parent,
32980eded6cSBoris BREZILLON 	.get_parent = clk_sam9x5_slow_get_parent,
33080eded6cSBoris BREZILLON };
33180eded6cSBoris BREZILLON 
332*f5644f10SStephen Boyd static struct clk_hw * __init
33380eded6cSBoris BREZILLON at91_clk_register_sam9x5_slow(void __iomem *sckcr,
33480eded6cSBoris BREZILLON 			      const char *name,
33580eded6cSBoris BREZILLON 			      const char **parent_names,
33680eded6cSBoris BREZILLON 			      int num_parents)
33780eded6cSBoris BREZILLON {
33880eded6cSBoris BREZILLON 	struct clk_sam9x5_slow *slowck;
339*f5644f10SStephen Boyd 	struct clk_hw *hw;
34080eded6cSBoris BREZILLON 	struct clk_init_data init;
341*f5644f10SStephen Boyd 	int ret;
34280eded6cSBoris BREZILLON 
34380eded6cSBoris BREZILLON 	if (!sckcr || !name || !parent_names || !num_parents)
34480eded6cSBoris BREZILLON 		return ERR_PTR(-EINVAL);
34580eded6cSBoris BREZILLON 
34680eded6cSBoris BREZILLON 	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
34780eded6cSBoris BREZILLON 	if (!slowck)
34880eded6cSBoris BREZILLON 		return ERR_PTR(-ENOMEM);
34980eded6cSBoris BREZILLON 
35080eded6cSBoris BREZILLON 	init.name = name;
35180eded6cSBoris BREZILLON 	init.ops = &sam9x5_slow_ops;
35280eded6cSBoris BREZILLON 	init.parent_names = parent_names;
35380eded6cSBoris BREZILLON 	init.num_parents = num_parents;
35480eded6cSBoris BREZILLON 	init.flags = 0;
35580eded6cSBoris BREZILLON 
35680eded6cSBoris BREZILLON 	slowck->hw.init = &init;
35780eded6cSBoris BREZILLON 	slowck->sckcr = sckcr;
35880eded6cSBoris BREZILLON 	slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
35980eded6cSBoris BREZILLON 
360*f5644f10SStephen Boyd 	hw = &slowck->hw;
361*f5644f10SStephen Boyd 	ret = clk_hw_register(NULL, &slowck->hw);
362*f5644f10SStephen Boyd 	if (ret) {
36380eded6cSBoris BREZILLON 		kfree(slowck);
364*f5644f10SStephen Boyd 		hw = ERR_PTR(ret);
365*f5644f10SStephen Boyd 	}
36680eded6cSBoris BREZILLON 
367*f5644f10SStephen Boyd 	return hw;
36880eded6cSBoris BREZILLON }
36980eded6cSBoris BREZILLON 
37080eded6cSBoris BREZILLON void __init of_at91sam9x5_clk_slow_setup(struct device_node *np,
37180eded6cSBoris BREZILLON 					 void __iomem *sckcr)
37280eded6cSBoris BREZILLON {
373*f5644f10SStephen Boyd 	struct clk_hw *hw;
37480eded6cSBoris BREZILLON 	const char *parent_names[2];
3758c1b1e54SStephen Boyd 	unsigned int num_parents;
37680eded6cSBoris BREZILLON 	const char *name = np->name;
37780eded6cSBoris BREZILLON 
37851a43be9SGeert Uytterhoeven 	num_parents = of_clk_get_parent_count(np);
3798c1b1e54SStephen Boyd 	if (num_parents == 0 || num_parents > 2)
38080eded6cSBoris BREZILLON 		return;
38180eded6cSBoris BREZILLON 
382f0557fbeSDinh Nguyen 	of_clk_parent_fill(np, parent_names, num_parents);
38380eded6cSBoris BREZILLON 
38480eded6cSBoris BREZILLON 	of_property_read_string(np, "clock-output-names", &name);
38580eded6cSBoris BREZILLON 
386*f5644f10SStephen Boyd 	hw = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
38780eded6cSBoris BREZILLON 					    num_parents);
388*f5644f10SStephen Boyd 	if (IS_ERR(hw))
38980eded6cSBoris BREZILLON 		return;
39080eded6cSBoris BREZILLON 
391*f5644f10SStephen Boyd 	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
39280eded6cSBoris BREZILLON }
39380eded6cSBoris BREZILLON 
39480eded6cSBoris BREZILLON static u8 clk_sam9260_slow_get_parent(struct clk_hw *hw)
39580eded6cSBoris BREZILLON {
39680eded6cSBoris BREZILLON 	struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(hw);
3971bdf0232SBoris Brezillon 	unsigned int status;
39880eded6cSBoris BREZILLON 
3991bdf0232SBoris Brezillon 	regmap_read(slowck->regmap, AT91_PMC_SR, &status);
4001bdf0232SBoris Brezillon 
4011bdf0232SBoris Brezillon 	return status & AT91_PMC_OSCSEL ? 1 : 0;
40280eded6cSBoris BREZILLON }
40380eded6cSBoris BREZILLON 
40480eded6cSBoris BREZILLON static const struct clk_ops sam9260_slow_ops = {
40580eded6cSBoris BREZILLON 	.get_parent = clk_sam9260_slow_get_parent,
40680eded6cSBoris BREZILLON };
40780eded6cSBoris BREZILLON 
408*f5644f10SStephen Boyd static struct clk_hw * __init
4091bdf0232SBoris Brezillon at91_clk_register_sam9260_slow(struct regmap *regmap,
41080eded6cSBoris BREZILLON 			       const char *name,
41180eded6cSBoris BREZILLON 			       const char **parent_names,
41280eded6cSBoris BREZILLON 			       int num_parents)
41380eded6cSBoris BREZILLON {
41480eded6cSBoris BREZILLON 	struct clk_sam9260_slow *slowck;
415*f5644f10SStephen Boyd 	struct clk_hw *hw;
41680eded6cSBoris BREZILLON 	struct clk_init_data init;
417*f5644f10SStephen Boyd 	int ret;
41880eded6cSBoris BREZILLON 
4191bdf0232SBoris Brezillon 	if (!name)
42080eded6cSBoris BREZILLON 		return ERR_PTR(-EINVAL);
42180eded6cSBoris BREZILLON 
42280eded6cSBoris BREZILLON 	if (!parent_names || !num_parents)
42380eded6cSBoris BREZILLON 		return ERR_PTR(-EINVAL);
42480eded6cSBoris BREZILLON 
42580eded6cSBoris BREZILLON 	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);
42680eded6cSBoris BREZILLON 	if (!slowck)
42780eded6cSBoris BREZILLON 		return ERR_PTR(-ENOMEM);
42880eded6cSBoris BREZILLON 
42980eded6cSBoris BREZILLON 	init.name = name;
43080eded6cSBoris BREZILLON 	init.ops = &sam9260_slow_ops;
43180eded6cSBoris BREZILLON 	init.parent_names = parent_names;
43280eded6cSBoris BREZILLON 	init.num_parents = num_parents;
43380eded6cSBoris BREZILLON 	init.flags = 0;
43480eded6cSBoris BREZILLON 
43580eded6cSBoris BREZILLON 	slowck->hw.init = &init;
4361bdf0232SBoris Brezillon 	slowck->regmap = regmap;
43780eded6cSBoris BREZILLON 
438*f5644f10SStephen Boyd 	hw = &slowck->hw;
439*f5644f10SStephen Boyd 	ret = clk_hw_register(NULL, &slowck->hw);
440*f5644f10SStephen Boyd 	if (ret) {
44180eded6cSBoris BREZILLON 		kfree(slowck);
442*f5644f10SStephen Boyd 		hw = ERR_PTR(ret);
443*f5644f10SStephen Boyd 	}
44480eded6cSBoris BREZILLON 
445*f5644f10SStephen Boyd 	return hw;
44680eded6cSBoris BREZILLON }
44780eded6cSBoris BREZILLON 
4481bdf0232SBoris Brezillon static void __init of_at91sam9260_clk_slow_setup(struct device_node *np)
44980eded6cSBoris BREZILLON {
450*f5644f10SStephen Boyd 	struct clk_hw *hw;
45180eded6cSBoris BREZILLON 	const char *parent_names[2];
4528c1b1e54SStephen Boyd 	unsigned int num_parents;
45380eded6cSBoris BREZILLON 	const char *name = np->name;
4541bdf0232SBoris Brezillon 	struct regmap *regmap;
45580eded6cSBoris BREZILLON 
45651a43be9SGeert Uytterhoeven 	num_parents = of_clk_get_parent_count(np);
457e8531ac8SBoris BREZILLON 	if (num_parents != 2)
45880eded6cSBoris BREZILLON 		return;
45980eded6cSBoris BREZILLON 
460f0557fbeSDinh Nguyen 	of_clk_parent_fill(np, parent_names, num_parents);
4611bdf0232SBoris Brezillon 	regmap = syscon_node_to_regmap(of_get_parent(np));
4621bdf0232SBoris Brezillon 	if (IS_ERR(regmap))
4631bdf0232SBoris Brezillon 		return;
46480eded6cSBoris BREZILLON 
46580eded6cSBoris BREZILLON 	of_property_read_string(np, "clock-output-names", &name);
46680eded6cSBoris BREZILLON 
467*f5644f10SStephen Boyd 	hw = at91_clk_register_sam9260_slow(regmap, name, parent_names,
46880eded6cSBoris BREZILLON 					     num_parents);
469*f5644f10SStephen Boyd 	if (IS_ERR(hw))
47080eded6cSBoris BREZILLON 		return;
47180eded6cSBoris BREZILLON 
472*f5644f10SStephen Boyd 	of_clk_add_hw_provider(np, of_clk_hw_simple_get, hw);
47380eded6cSBoris BREZILLON }
4741bdf0232SBoris Brezillon 
4751bdf0232SBoris Brezillon CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
4761bdf0232SBoris Brezillon 	       of_at91sam9260_clk_slow_setup);
477