brintos

brintos / linux-shallow public Read only

0
0
Text · 13.4 KiB · 9b462be Raw
597 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>4 */5 6#include <linux/clk-provider.h>7#include <linux/clkdev.h>8#include <linux/clk/at91_pmc.h>9#include <linux/delay.h>10#include <linux/mfd/syscon.h>11#include <linux/regmap.h>12 13#include "pmc.h"14 15#define SLOW_CLOCK_FREQ		3276816#define MAINF_DIV		1617#define MAINFRDY_TIMEOUT	(((MAINF_DIV + 1) * USEC_PER_SEC) / \18				 SLOW_CLOCK_FREQ)19#define MAINF_LOOP_MIN_WAIT	(USEC_PER_SEC / SLOW_CLOCK_FREQ)20#define MAINF_LOOP_MAX_WAIT	MAINFRDY_TIMEOUT21 22#define MOR_KEY_MASK		(0xff << 16)23 24#define clk_main_parent_select(s)	(((s) & \25					(AT91_PMC_MOSCEN | \26					AT91_PMC_OSCBYPASS)) ? 1 : 0)27 28struct clk_main_osc {29	struct clk_hw hw;30	struct regmap *regmap;31	struct at91_clk_pms pms;32};33 34#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)35 36struct clk_main_rc_osc {37	struct clk_hw hw;38	struct regmap *regmap;39	unsigned long frequency;40	unsigned long accuracy;41	struct at91_clk_pms pms;42};43 44#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)45 46struct clk_rm9200_main {47	struct clk_hw hw;48	struct regmap *regmap;49};50 51#define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw)52 53struct clk_sam9x5_main {54	struct clk_hw hw;55	struct regmap *regmap;56	struct at91_clk_pms pms;57	u8 parent;58};59 60#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw)61 62static inline bool clk_main_osc_ready(struct regmap *regmap)63{64	unsigned int status;65 66	regmap_read(regmap, AT91_PMC_SR, &status);67 68	return status & AT91_PMC_MOSCS;69}70 71static int clk_main_osc_prepare(struct clk_hw *hw)72{73	struct clk_main_osc *osc = to_clk_main_osc(hw);74	struct regmap *regmap = osc->regmap;75	u32 tmp;76 77	regmap_read(regmap, AT91_CKGR_MOR, &tmp);78	tmp &= ~MOR_KEY_MASK;79 80	if (tmp & AT91_PMC_OSCBYPASS)81		return 0;82 83	if (!(tmp & AT91_PMC_MOSCEN)) {84		tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY;85		regmap_write(regmap, AT91_CKGR_MOR, tmp);86	}87 88	while (!clk_main_osc_ready(regmap))89		cpu_relax();90 91	return 0;92}93 94static void clk_main_osc_unprepare(struct clk_hw *hw)95{96	struct clk_main_osc *osc = to_clk_main_osc(hw);97	struct regmap *regmap = osc->regmap;98	u32 tmp;99 100	regmap_read(regmap, AT91_CKGR_MOR, &tmp);101	if (tmp & AT91_PMC_OSCBYPASS)102		return;103 104	if (!(tmp & AT91_PMC_MOSCEN))105		return;106 107	tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);108	regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);109}110 111static int clk_main_osc_is_prepared(struct clk_hw *hw)112{113	struct clk_main_osc *osc = to_clk_main_osc(hw);114	struct regmap *regmap = osc->regmap;115	u32 tmp, status;116 117	regmap_read(regmap, AT91_CKGR_MOR, &tmp);118	if (tmp & AT91_PMC_OSCBYPASS)119		return 1;120 121	regmap_read(regmap, AT91_PMC_SR, &status);122 123	return (status & AT91_PMC_MOSCS) && clk_main_parent_select(tmp);124}125 126static int clk_main_osc_save_context(struct clk_hw *hw)127{128	struct clk_main_osc *osc = to_clk_main_osc(hw);129 130	osc->pms.status = clk_main_osc_is_prepared(hw);131 132	return 0;133}134 135static void clk_main_osc_restore_context(struct clk_hw *hw)136{137	struct clk_main_osc *osc = to_clk_main_osc(hw);138 139	if (osc->pms.status)140		clk_main_osc_prepare(hw);141}142 143static const struct clk_ops main_osc_ops = {144	.prepare = clk_main_osc_prepare,145	.unprepare = clk_main_osc_unprepare,146	.is_prepared = clk_main_osc_is_prepared,147	.save_context = clk_main_osc_save_context,148	.restore_context = clk_main_osc_restore_context,149};150 151struct clk_hw * __init152at91_clk_register_main_osc(struct regmap *regmap,153			   const char *name,154			   const char *parent_name,155			   struct clk_parent_data *parent_data,156			   bool bypass)157{158	struct clk_main_osc *osc;159	struct clk_init_data init = {};160	struct clk_hw *hw;161	int ret;162 163	if (!name || !(parent_name || parent_data))164		return ERR_PTR(-EINVAL);165 166	osc = kzalloc(sizeof(*osc), GFP_KERNEL);167	if (!osc)168		return ERR_PTR(-ENOMEM);169 170	init.name = name;171	init.ops = &main_osc_ops;172	if (parent_data)173		init.parent_data = (const struct clk_parent_data *)parent_data;174	else175		init.parent_names = &parent_name;176	init.num_parents = 1;177	init.flags = CLK_IGNORE_UNUSED;178 179	osc->hw.init = &init;180	osc->regmap = regmap;181 182	if (bypass)183		regmap_update_bits(regmap,184				   AT91_CKGR_MOR, MOR_KEY_MASK |185				   AT91_PMC_OSCBYPASS,186				   AT91_PMC_OSCBYPASS | AT91_PMC_KEY);187 188	hw = &osc->hw;189	ret = clk_hw_register(NULL, &osc->hw);190	if (ret) {191		kfree(osc);192		hw = ERR_PTR(ret);193	}194 195	return hw;196}197 198static bool clk_main_rc_osc_ready(struct regmap *regmap)199{200	unsigned int status;201 202	regmap_read(regmap, AT91_PMC_SR, &status);203 204	return !!(status & AT91_PMC_MOSCRCS);205}206 207static int clk_main_rc_osc_prepare(struct clk_hw *hw)208{209	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);210	struct regmap *regmap = osc->regmap;211	unsigned int mor;212 213	regmap_read(regmap, AT91_CKGR_MOR, &mor);214 215	if (!(mor & AT91_PMC_MOSCRCEN))216		regmap_update_bits(regmap, AT91_CKGR_MOR,217				   MOR_KEY_MASK | AT91_PMC_MOSCRCEN,218				   AT91_PMC_MOSCRCEN | AT91_PMC_KEY);219 220	while (!clk_main_rc_osc_ready(regmap))221		cpu_relax();222 223	return 0;224}225 226static void clk_main_rc_osc_unprepare(struct clk_hw *hw)227{228	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);229	struct regmap *regmap = osc->regmap;230	unsigned int mor;231 232	regmap_read(regmap, AT91_CKGR_MOR, &mor);233 234	if (!(mor & AT91_PMC_MOSCRCEN))235		return;236 237	regmap_update_bits(regmap, AT91_CKGR_MOR,238			   MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);239}240 241static int clk_main_rc_osc_is_prepared(struct clk_hw *hw)242{243	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);244	struct regmap *regmap = osc->regmap;245	unsigned int mor, status;246 247	regmap_read(regmap, AT91_CKGR_MOR, &mor);248	regmap_read(regmap, AT91_PMC_SR, &status);249 250	return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS);251}252 253static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw,254						 unsigned long parent_rate)255{256	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);257 258	return osc->frequency;259}260 261static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw,262						     unsigned long parent_acc)263{264	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);265 266	return osc->accuracy;267}268 269static int clk_main_rc_osc_save_context(struct clk_hw *hw)270{271	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);272 273	osc->pms.status = clk_main_rc_osc_is_prepared(hw);274 275	return 0;276}277 278static void clk_main_rc_osc_restore_context(struct clk_hw *hw)279{280	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);281 282	if (osc->pms.status)283		clk_main_rc_osc_prepare(hw);284}285 286static const struct clk_ops main_rc_osc_ops = {287	.prepare = clk_main_rc_osc_prepare,288	.unprepare = clk_main_rc_osc_unprepare,289	.is_prepared = clk_main_rc_osc_is_prepared,290	.recalc_rate = clk_main_rc_osc_recalc_rate,291	.recalc_accuracy = clk_main_rc_osc_recalc_accuracy,292	.save_context = clk_main_rc_osc_save_context,293	.restore_context = clk_main_rc_osc_restore_context,294};295 296struct clk_hw * __init297at91_clk_register_main_rc_osc(struct regmap *regmap,298			      const char *name,299			      u32 frequency, u32 accuracy)300{301	struct clk_main_rc_osc *osc;302	struct clk_init_data init;303	struct clk_hw *hw;304	int ret;305 306	if (!name || !frequency)307		return ERR_PTR(-EINVAL);308 309	osc = kzalloc(sizeof(*osc), GFP_KERNEL);310	if (!osc)311		return ERR_PTR(-ENOMEM);312 313	init.name = name;314	init.ops = &main_rc_osc_ops;315	init.parent_names = NULL;316	init.num_parents = 0;317	init.flags = CLK_IGNORE_UNUSED;318 319	osc->hw.init = &init;320	osc->regmap = regmap;321	osc->frequency = frequency;322	osc->accuracy = accuracy;323 324	hw = &osc->hw;325	ret = clk_hw_register(NULL, hw);326	if (ret) {327		kfree(osc);328		hw = ERR_PTR(ret);329	}330 331	return hw;332}333 334static int clk_main_probe_frequency(struct regmap *regmap)335{336	unsigned long prep_time, timeout;337	unsigned int mcfr;338 339	timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT);340	do {341		prep_time = jiffies;342		regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);343		if (mcfr & AT91_PMC_MAINRDY)344			return 0;345		if (system_state < SYSTEM_RUNNING)346			udelay(MAINF_LOOP_MIN_WAIT);347		else348			usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);349	} while (time_before(prep_time, timeout));350 351	return -ETIMEDOUT;352}353 354static unsigned long clk_main_recalc_rate(struct regmap *regmap,355					  unsigned long parent_rate)356{357	unsigned int mcfr;358 359	if (parent_rate)360		return parent_rate;361 362	pr_warn("Main crystal frequency not set, using approximate value\n");363	regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);364	if (!(mcfr & AT91_PMC_MAINRDY))365		return 0;366 367	return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV;368}369 370static int clk_rm9200_main_prepare(struct clk_hw *hw)371{372	struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);373 374	return clk_main_probe_frequency(clkmain->regmap);375}376 377static int clk_rm9200_main_is_prepared(struct clk_hw *hw)378{379	struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);380	unsigned int status;381 382	regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);383 384	return !!(status & AT91_PMC_MAINRDY);385}386 387static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,388						 unsigned long parent_rate)389{390	struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);391 392	return clk_main_recalc_rate(clkmain->regmap, parent_rate);393}394 395static const struct clk_ops rm9200_main_ops = {396	.prepare = clk_rm9200_main_prepare,397	.is_prepared = clk_rm9200_main_is_prepared,398	.recalc_rate = clk_rm9200_main_recalc_rate,399};400 401struct clk_hw * __init402at91_clk_register_rm9200_main(struct regmap *regmap,403			      const char *name,404			      const char *parent_name,405			      struct clk_hw *parent_hw)406{407	struct clk_rm9200_main *clkmain;408	struct clk_init_data init = {};409	struct clk_hw *hw;410	int ret;411 412	if (!name)413		return ERR_PTR(-EINVAL);414 415	if (!(parent_name || parent_hw))416		return ERR_PTR(-EINVAL);417 418	clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);419	if (!clkmain)420		return ERR_PTR(-ENOMEM);421 422	init.name = name;423	init.ops = &rm9200_main_ops;424	if (parent_hw)425		init.parent_hws = (const struct clk_hw **)&parent_hw;426	else427		init.parent_names = &parent_name;428	init.num_parents = 1;429	init.flags = 0;430 431	clkmain->hw.init = &init;432	clkmain->regmap = regmap;433 434	hw = &clkmain->hw;435	ret = clk_hw_register(NULL, &clkmain->hw);436	if (ret) {437		kfree(clkmain);438		hw = ERR_PTR(ret);439	}440 441	return hw;442}443 444static inline bool clk_sam9x5_main_ready(struct regmap *regmap)445{446	unsigned int status;447 448	regmap_read(regmap, AT91_PMC_SR, &status);449 450	return !!(status & AT91_PMC_MOSCSELS);451}452 453static int clk_sam9x5_main_prepare(struct clk_hw *hw)454{455	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);456	struct regmap *regmap = clkmain->regmap;457 458	while (!clk_sam9x5_main_ready(regmap))459		cpu_relax();460 461	return clk_main_probe_frequency(regmap);462}463 464static int clk_sam9x5_main_is_prepared(struct clk_hw *hw)465{466	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);467 468	return clk_sam9x5_main_ready(clkmain->regmap);469}470 471static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw,472						 unsigned long parent_rate)473{474	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);475 476	return clk_main_recalc_rate(clkmain->regmap, parent_rate);477}478 479static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)480{481	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);482	struct regmap *regmap = clkmain->regmap;483	unsigned int tmp;484 485	if (index > 1)486		return -EINVAL;487 488	regmap_read(regmap, AT91_CKGR_MOR, &tmp);489 490	if (index && !(tmp & AT91_PMC_MOSCSEL))491		tmp = AT91_PMC_MOSCSEL;492	else if (!index && (tmp & AT91_PMC_MOSCSEL))493		tmp = 0;494	else495		return 0;496 497	regmap_update_bits(regmap, AT91_CKGR_MOR,498			   AT91_PMC_MOSCSEL | MOR_KEY_MASK,499			   tmp | AT91_PMC_KEY);500 501	while (!clk_sam9x5_main_ready(regmap))502		cpu_relax();503 504	return 0;505}506 507static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw)508{509	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);510	unsigned int status;511 512	regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);513 514	return clk_main_parent_select(status);515}516 517static int clk_sam9x5_main_save_context(struct clk_hw *hw)518{519	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);520 521	clkmain->pms.status = clk_main_rc_osc_is_prepared(&clkmain->hw);522	clkmain->pms.parent = clk_sam9x5_main_get_parent(&clkmain->hw);523 524	return 0;525}526 527static void clk_sam9x5_main_restore_context(struct clk_hw *hw)528{529	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);530	int ret;531 532	ret = clk_sam9x5_main_set_parent(hw, clkmain->pms.parent);533	if (ret)534		return;535 536	if (clkmain->pms.status)537		clk_sam9x5_main_prepare(hw);538}539 540static const struct clk_ops sam9x5_main_ops = {541	.prepare = clk_sam9x5_main_prepare,542	.is_prepared = clk_sam9x5_main_is_prepared,543	.recalc_rate = clk_sam9x5_main_recalc_rate,544	.determine_rate = clk_hw_determine_rate_no_reparent,545	.set_parent = clk_sam9x5_main_set_parent,546	.get_parent = clk_sam9x5_main_get_parent,547	.save_context = clk_sam9x5_main_save_context,548	.restore_context = clk_sam9x5_main_restore_context,549};550 551struct clk_hw * __init552at91_clk_register_sam9x5_main(struct regmap *regmap,553			      const char *name,554			      const char **parent_names,555			      struct clk_hw **parent_hws,556			      int num_parents)557{558	struct clk_sam9x5_main *clkmain;559	struct clk_init_data init = {};560	unsigned int status;561	struct clk_hw *hw;562	int ret;563 564	if (!name)565		return ERR_PTR(-EINVAL);566 567	if (!(parent_hws || parent_names) || !num_parents)568		return ERR_PTR(-EINVAL);569 570	clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);571	if (!clkmain)572		return ERR_PTR(-ENOMEM);573 574	init.name = name;575	init.ops = &sam9x5_main_ops;576	if (parent_hws)577		init.parent_hws = (const struct clk_hw **)parent_hws;578	else579		init.parent_names = parent_names;580	init.num_parents = num_parents;581	init.flags = CLK_SET_PARENT_GATE;582 583	clkmain->hw.init = &init;584	clkmain->regmap = regmap;585	regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);586	clkmain->parent = clk_main_parent_select(status);587 588	hw = &clkmain->hw;589	ret = clk_hw_register(NULL, &clkmain->hw);590	if (ret) {591		kfree(clkmain);592		hw = ERR_PTR(ret);593	}594 595	return hw;596}597