brintos

brintos / linux-shallow public Read only

0
0
Text · 15.0 KiB · 7741d8f Raw
656 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * drivers/clk/at91/sckc.c4 *5 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>6 */7 8#include <linux/clk-provider.h>9#include <linux/clkdev.h>10#include <linux/delay.h>11#include <linux/of.h>12#include <linux/of_address.h>13#include <linux/io.h>14 15#define SLOW_CLOCK_FREQ		3276816#define SLOWCK_SW_CYCLES	517#define SLOWCK_SW_TIME_USEC	((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \18				 SLOW_CLOCK_FREQ)19 20#define	AT91_SCKC_CR			0x0021 22struct clk_slow_bits {23	u32 cr_rcen;24	u32 cr_osc32en;25	u32 cr_osc32byp;26	u32 cr_oscsel;27};28 29struct clk_slow_osc {30	struct clk_hw hw;31	void __iomem *sckcr;32	const struct clk_slow_bits *bits;33	unsigned long startup_usec;34};35 36#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)37 38struct clk_sama5d4_slow_osc {39	struct clk_hw hw;40	void __iomem *sckcr;41	const struct clk_slow_bits *bits;42	unsigned long startup_usec;43	bool prepared;44};45 46#define to_clk_sama5d4_slow_osc(hw) container_of(hw, struct clk_sama5d4_slow_osc, hw)47 48struct clk_slow_rc_osc {49	struct clk_hw hw;50	void __iomem *sckcr;51	const struct clk_slow_bits *bits;52	unsigned long frequency;53	unsigned long accuracy;54	unsigned long startup_usec;55};56 57#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)58 59struct clk_sam9x5_slow {60	struct clk_hw hw;61	void __iomem *sckcr;62	const struct clk_slow_bits *bits;63	u8 parent;64};65 66#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)67 68static int clk_slow_osc_prepare(struct clk_hw *hw)69{70	struct clk_slow_osc *osc = to_clk_slow_osc(hw);71	void __iomem *sckcr = osc->sckcr;72	u32 tmp = readl(sckcr);73 74	if (tmp & (osc->bits->cr_osc32byp | osc->bits->cr_osc32en))75		return 0;76 77	writel(tmp | osc->bits->cr_osc32en, sckcr);78 79	if (system_state < SYSTEM_RUNNING)80		udelay(osc->startup_usec);81	else82		usleep_range(osc->startup_usec, osc->startup_usec + 1);83 84	return 0;85}86 87static void clk_slow_osc_unprepare(struct clk_hw *hw)88{89	struct clk_slow_osc *osc = to_clk_slow_osc(hw);90	void __iomem *sckcr = osc->sckcr;91	u32 tmp = readl(sckcr);92 93	if (tmp & osc->bits->cr_osc32byp)94		return;95 96	writel(tmp & ~osc->bits->cr_osc32en, sckcr);97}98 99static int clk_slow_osc_is_prepared(struct clk_hw *hw)100{101	struct clk_slow_osc *osc = to_clk_slow_osc(hw);102	void __iomem *sckcr = osc->sckcr;103	u32 tmp = readl(sckcr);104 105	if (tmp & osc->bits->cr_osc32byp)106		return 1;107 108	return !!(tmp & osc->bits->cr_osc32en);109}110 111static const struct clk_ops slow_osc_ops = {112	.prepare = clk_slow_osc_prepare,113	.unprepare = clk_slow_osc_unprepare,114	.is_prepared = clk_slow_osc_is_prepared,115};116 117static struct clk_hw * __init118at91_clk_register_slow_osc(void __iomem *sckcr,119			   const char *name,120			   const struct clk_parent_data *parent_data,121			   unsigned long startup,122			   bool bypass,123			   const struct clk_slow_bits *bits)124{125	struct clk_slow_osc *osc;126	struct clk_hw *hw;127	struct clk_init_data init = {};128	int ret;129 130	if (!sckcr || !name || !parent_data)131		return ERR_PTR(-EINVAL);132 133	osc = kzalloc(sizeof(*osc), GFP_KERNEL);134	if (!osc)135		return ERR_PTR(-ENOMEM);136 137	init.name = name;138	init.ops = &slow_osc_ops;139	init.parent_data = parent_data;140	init.num_parents = 1;141	init.flags = CLK_IGNORE_UNUSED;142 143	osc->hw.init = &init;144	osc->sckcr = sckcr;145	osc->startup_usec = startup;146	osc->bits = bits;147 148	if (bypass)149		writel((readl(sckcr) & ~osc->bits->cr_osc32en) |150					osc->bits->cr_osc32byp, sckcr);151 152	hw = &osc->hw;153	ret = clk_hw_register(NULL, &osc->hw);154	if (ret) {155		kfree(osc);156		hw = ERR_PTR(ret);157	}158 159	return hw;160}161 162static void at91_clk_unregister_slow_osc(struct clk_hw *hw)163{164	struct clk_slow_osc *osc = to_clk_slow_osc(hw);165 166	clk_hw_unregister(hw);167	kfree(osc);168}169 170static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,171						 unsigned long parent_rate)172{173	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);174 175	return osc->frequency;176}177 178static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,179						     unsigned long parent_acc)180{181	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);182 183	return osc->accuracy;184}185 186static int clk_slow_rc_osc_prepare(struct clk_hw *hw)187{188	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);189	void __iomem *sckcr = osc->sckcr;190 191	writel(readl(sckcr) | osc->bits->cr_rcen, sckcr);192 193	if (system_state < SYSTEM_RUNNING)194		udelay(osc->startup_usec);195	else196		usleep_range(osc->startup_usec, osc->startup_usec + 1);197 198	return 0;199}200 201static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)202{203	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);204	void __iomem *sckcr = osc->sckcr;205 206	writel(readl(sckcr) & ~osc->bits->cr_rcen, sckcr);207}208 209static int clk_slow_rc_osc_is_prepared(struct clk_hw *hw)210{211	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);212 213	return !!(readl(osc->sckcr) & osc->bits->cr_rcen);214}215 216static const struct clk_ops slow_rc_osc_ops = {217	.prepare = clk_slow_rc_osc_prepare,218	.unprepare = clk_slow_rc_osc_unprepare,219	.is_prepared = clk_slow_rc_osc_is_prepared,220	.recalc_rate = clk_slow_rc_osc_recalc_rate,221	.recalc_accuracy = clk_slow_rc_osc_recalc_accuracy,222};223 224static struct clk_hw * __init225at91_clk_register_slow_rc_osc(void __iomem *sckcr,226			      const char *name,227			      unsigned long frequency,228			      unsigned long accuracy,229			      unsigned long startup,230			      const struct clk_slow_bits *bits)231{232	struct clk_slow_rc_osc *osc;233	struct clk_hw *hw;234	struct clk_init_data init;235	int ret;236 237	if (!sckcr || !name)238		return ERR_PTR(-EINVAL);239 240	osc = kzalloc(sizeof(*osc), GFP_KERNEL);241	if (!osc)242		return ERR_PTR(-ENOMEM);243 244	init.name = name;245	init.ops = &slow_rc_osc_ops;246	init.parent_names = NULL;247	init.num_parents = 0;248	init.flags = CLK_IGNORE_UNUSED;249 250	osc->hw.init = &init;251	osc->sckcr = sckcr;252	osc->bits = bits;253	osc->frequency = frequency;254	osc->accuracy = accuracy;255	osc->startup_usec = startup;256 257	hw = &osc->hw;258	ret = clk_hw_register(NULL, &osc->hw);259	if (ret) {260		kfree(osc);261		hw = ERR_PTR(ret);262	}263 264	return hw;265}266 267static void at91_clk_unregister_slow_rc_osc(struct clk_hw *hw)268{269	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);270 271	clk_hw_unregister(hw);272	kfree(osc);273}274 275static int clk_sam9x5_slow_set_parent(struct clk_hw *hw, u8 index)276{277	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);278	void __iomem *sckcr = slowck->sckcr;279	u32 tmp;280 281	if (index > 1)282		return -EINVAL;283 284	tmp = readl(sckcr);285 286	if ((!index && !(tmp & slowck->bits->cr_oscsel)) ||287	    (index && (tmp & slowck->bits->cr_oscsel)))288		return 0;289 290	if (index)291		tmp |= slowck->bits->cr_oscsel;292	else293		tmp &= ~slowck->bits->cr_oscsel;294 295	writel(tmp, sckcr);296 297	if (system_state < SYSTEM_RUNNING)298		udelay(SLOWCK_SW_TIME_USEC);299	else300		usleep_range(SLOWCK_SW_TIME_USEC, SLOWCK_SW_TIME_USEC + 1);301 302	return 0;303}304 305static u8 clk_sam9x5_slow_get_parent(struct clk_hw *hw)306{307	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);308 309	return !!(readl(slowck->sckcr) & slowck->bits->cr_oscsel);310}311 312static const struct clk_ops sam9x5_slow_ops = {313	.determine_rate = clk_hw_determine_rate_no_reparent,314	.set_parent = clk_sam9x5_slow_set_parent,315	.get_parent = clk_sam9x5_slow_get_parent,316};317 318static struct clk_hw * __init319at91_clk_register_sam9x5_slow(void __iomem *sckcr,320			      const char *name,321			      const struct clk_hw **parent_hws,322			      int num_parents,323			      const struct clk_slow_bits *bits)324{325	struct clk_sam9x5_slow *slowck;326	struct clk_hw *hw;327	struct clk_init_data init = {};328	int ret;329 330	if (!sckcr || !name || !parent_hws || !num_parents)331		return ERR_PTR(-EINVAL);332 333	slowck = kzalloc(sizeof(*slowck), GFP_KERNEL);334	if (!slowck)335		return ERR_PTR(-ENOMEM);336 337	init.name = name;338	init.ops = &sam9x5_slow_ops;339	init.parent_hws = parent_hws;340	init.num_parents = num_parents;341	init.flags = 0;342 343	slowck->hw.init = &init;344	slowck->sckcr = sckcr;345	slowck->bits = bits;346	slowck->parent = !!(readl(sckcr) & slowck->bits->cr_oscsel);347 348	hw = &slowck->hw;349	ret = clk_hw_register(NULL, &slowck->hw);350	if (ret) {351		kfree(slowck);352		hw = ERR_PTR(ret);353	}354 355	return hw;356}357 358static void at91_clk_unregister_sam9x5_slow(struct clk_hw *hw)359{360	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(hw);361 362	clk_hw_unregister(hw);363	kfree(slowck);364}365 366static void __init at91sam9x5_sckc_register(struct device_node *np,367					    unsigned int rc_osc_startup_us,368					    const struct clk_slow_bits *bits)369{370	void __iomem *regbase = of_iomap(np, 0);371	struct device_node *child = NULL;372	const char *xtal_name;373	struct clk_hw *slow_rc, *slow_osc, *slowck;374	static struct clk_parent_data parent_data = {375		.name = "slow_xtal",376	};377	const struct clk_hw *parent_hws[2];378	bool bypass;379	int ret;380 381	if (!regbase)382		return;383 384	slow_rc = at91_clk_register_slow_rc_osc(regbase, "slow_rc_osc",385						32768, 50000000,386						rc_osc_startup_us, bits);387	if (IS_ERR(slow_rc))388		return;389 390	xtal_name = of_clk_get_parent_name(np, 0);391	if (!xtal_name) {392		/* DT backward compatibility */393		child = of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow-osc");394		if (!child)395			goto unregister_slow_rc;396 397		xtal_name = of_clk_get_parent_name(child, 0);398		bypass = of_property_read_bool(child, "atmel,osc-bypass");399 400		child =  of_get_compatible_child(np, "atmel,at91sam9x5-clk-slow");401	} else {402		bypass = of_property_read_bool(np, "atmel,osc-bypass");403	}404 405	if (!xtal_name)406		goto unregister_slow_rc;407 408	parent_data.fw_name = xtal_name;409 410	slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",411					      &parent_data, 1200000, bypass, bits);412	if (IS_ERR(slow_osc))413		goto unregister_slow_rc;414 415	parent_hws[0] = slow_rc;416	parent_hws[1] = slow_osc;417	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_hws,418					       2, bits);419	if (IS_ERR(slowck))420		goto unregister_slow_osc;421 422	/* DT backward compatibility */423	if (child)424		ret = of_clk_add_hw_provider(child, of_clk_hw_simple_get,425					     slowck);426	else427		ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);428 429	if (WARN_ON(ret))430		goto unregister_slowck;431 432	return;433 434unregister_slowck:435	at91_clk_unregister_sam9x5_slow(slowck);436unregister_slow_osc:437	at91_clk_unregister_slow_osc(slow_osc);438unregister_slow_rc:439	at91_clk_unregister_slow_rc_osc(slow_rc);440}441 442static const struct clk_slow_bits at91sam9x5_bits = {443	.cr_rcen = BIT(0),444	.cr_osc32en = BIT(1),445	.cr_osc32byp = BIT(2),446	.cr_oscsel = BIT(3),447};448 449static void __init of_at91sam9x5_sckc_setup(struct device_node *np)450{451	at91sam9x5_sckc_register(np, 75, &at91sam9x5_bits);452}453CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",454	       of_at91sam9x5_sckc_setup);455 456static void __init of_sama5d3_sckc_setup(struct device_node *np)457{458	at91sam9x5_sckc_register(np, 500, &at91sam9x5_bits);459}460CLK_OF_DECLARE(sama5d3_clk_sckc, "atmel,sama5d3-sckc",461	       of_sama5d3_sckc_setup);462 463static const struct clk_slow_bits at91sam9x60_bits = {464	.cr_osc32en = BIT(1),465	.cr_osc32byp = BIT(2),466	.cr_oscsel = BIT(24),467};468 469static void __init of_sam9x60_sckc_setup(struct device_node *np)470{471	void __iomem *regbase = of_iomap(np, 0);472	struct clk_hw_onecell_data *clk_data;473	struct clk_hw *slow_rc, *slow_osc;474	const char *xtal_name;475	const struct clk_hw *parent_hws[2];476	static struct clk_parent_data parent_data = {477		.name = "slow_xtal",478	};479	bool bypass;480	int ret;481 482	if (!regbase)483		return;484 485	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL, "slow_rc_osc",486							   NULL, 0, 32768,487							   93750000);488	if (IS_ERR(slow_rc))489		return;490 491	xtal_name = of_clk_get_parent_name(np, 0);492	if (!xtal_name)493		goto unregister_slow_rc;494 495	parent_data.fw_name = xtal_name;496	bypass = of_property_read_bool(np, "atmel,osc-bypass");497	slow_osc = at91_clk_register_slow_osc(regbase, "slow_osc",498					      &parent_data, 5000000, bypass,499					      &at91sam9x60_bits);500	if (IS_ERR(slow_osc))501		goto unregister_slow_rc;502 503	clk_data = kzalloc(struct_size(clk_data, hws, 2), GFP_KERNEL);504	if (!clk_data)505		goto unregister_slow_osc;506 507	/* MD_SLCK and TD_SLCK. */508	clk_data->num = 2;509	clk_data->hws[0] = clk_hw_register_fixed_rate_parent_hw(NULL, "md_slck",510								slow_rc,511								0, 32768);512	if (IS_ERR(clk_data->hws[0]))513		goto clk_data_free;514 515	parent_hws[0] = slow_rc;516	parent_hws[1] = slow_osc;517	clk_data->hws[1] = at91_clk_register_sam9x5_slow(regbase, "td_slck",518							 parent_hws, 2,519							 &at91sam9x60_bits);520	if (IS_ERR(clk_data->hws[1]))521		goto unregister_md_slck;522 523	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);524	if (WARN_ON(ret))525		goto unregister_td_slck;526 527	return;528 529unregister_td_slck:530	at91_clk_unregister_sam9x5_slow(clk_data->hws[1]);531unregister_md_slck:532	clk_hw_unregister(clk_data->hws[0]);533clk_data_free:534	kfree(clk_data);535unregister_slow_osc:536	at91_clk_unregister_slow_osc(slow_osc);537unregister_slow_rc:538	clk_hw_unregister(slow_rc);539}540CLK_OF_DECLARE(sam9x60_clk_sckc, "microchip,sam9x60-sckc",541	       of_sam9x60_sckc_setup);542 543static int clk_sama5d4_slow_osc_prepare(struct clk_hw *hw)544{545	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);546 547	if (osc->prepared)548		return 0;549 550	/*551	 * Assume that if it has already been selected (for example by the552	 * bootloader), enough time has already passed.553	 */554	if ((readl(osc->sckcr) & osc->bits->cr_oscsel)) {555		osc->prepared = true;556		return 0;557	}558 559	if (system_state < SYSTEM_RUNNING)560		udelay(osc->startup_usec);561	else562		usleep_range(osc->startup_usec, osc->startup_usec + 1);563	osc->prepared = true;564 565	return 0;566}567 568static int clk_sama5d4_slow_osc_is_prepared(struct clk_hw *hw)569{570	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(hw);571 572	return osc->prepared;573}574 575static const struct clk_ops sama5d4_slow_osc_ops = {576	.prepare = clk_sama5d4_slow_osc_prepare,577	.is_prepared = clk_sama5d4_slow_osc_is_prepared,578};579 580static const struct clk_slow_bits at91sama5d4_bits = {581	.cr_oscsel = BIT(3),582};583 584static void __init of_sama5d4_sckc_setup(struct device_node *np)585{586	void __iomem *regbase = of_iomap(np, 0);587	struct clk_hw *slow_rc, *slowck;588	struct clk_sama5d4_slow_osc *osc;589	struct clk_init_data init = {};590	const char *xtal_name;591	const struct clk_hw *parent_hws[2];592	static struct clk_parent_data parent_data = {593		.name = "slow_xtal",594	};595	int ret;596 597	if (!regbase)598		return;599 600	slow_rc = clk_hw_register_fixed_rate_with_accuracy(NULL,601							   "slow_rc_osc",602							   NULL, 0, 32768,603							   250000000);604	if (IS_ERR(slow_rc))605		return;606 607	xtal_name = of_clk_get_parent_name(np, 0);608	if (!xtal_name)609		goto unregister_slow_rc;610	parent_data.fw_name = xtal_name;611 612	osc = kzalloc(sizeof(*osc), GFP_KERNEL);613	if (!osc)614		goto unregister_slow_rc;615 616	init.name = "slow_osc";617	init.ops = &sama5d4_slow_osc_ops;618	init.parent_data = &parent_data;619	init.num_parents = 1;620	init.flags = CLK_IGNORE_UNUSED;621 622	osc->hw.init = &init;623	osc->sckcr = regbase;624	osc->startup_usec = 1200000;625	osc->bits = &at91sama5d4_bits;626 627	ret = clk_hw_register(NULL, &osc->hw);628	if (ret)629		goto free_slow_osc_data;630 631	parent_hws[0] = slow_rc;632	parent_hws[1] = &osc->hw;633	slowck = at91_clk_register_sam9x5_slow(regbase, "slowck",634					       parent_hws, 2,635					       &at91sama5d4_bits);636	if (IS_ERR(slowck))637		goto unregister_slow_osc;638 639	ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, slowck);640	if (WARN_ON(ret))641		goto unregister_slowck;642 643	return;644 645unregister_slowck:646	at91_clk_unregister_sam9x5_slow(slowck);647unregister_slow_osc:648	clk_hw_unregister(&osc->hw);649free_slow_osc_data:650	kfree(osc);651unregister_slow_rc:652	clk_hw_unregister(slow_rc);653}654CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",655	       of_sama5d4_sckc_setup);656