brintos

brintos / linux-shallow public Read only

0
0
Text · 12.8 KiB · 7d04ef4 Raw
497 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * JZ47xx SoCs TCU clocks driver4 * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>5 */6 7#include <linux/clk.h>8#include <linux/clk-provider.h>9#include <linux/clockchips.h>10#include <linux/mfd/ingenic-tcu.h>11#include <linux/mfd/syscon.h>12#include <linux/regmap.h>13#include <linux/slab.h>14#include <linux/syscore_ops.h>15 16#include <dt-bindings/clock/ingenic,tcu.h>17 18/* 8 channels max + watchdog + OST */19#define TCU_CLK_COUNT	1020 21#undef pr_fmt22#define pr_fmt(fmt) "ingenic-tcu-clk: " fmt23 24enum tcu_clk_parent {25	TCU_PARENT_PCLK,26	TCU_PARENT_RTC,27	TCU_PARENT_EXT,28};29 30struct ingenic_soc_info {31	unsigned int num_channels;32	bool has_ost;33	bool has_tcu_clk;34	bool allow_missing_tcu_clk;35};36 37struct ingenic_tcu_clk_info {38	struct clk_init_data init_data;39	u8 gate_bit;40	u8 tcsr_reg;41};42 43struct ingenic_tcu_clk {44	struct clk_hw hw;45	unsigned int idx;46	struct ingenic_tcu *tcu;47	const struct ingenic_tcu_clk_info *info;48};49 50struct ingenic_tcu {51	const struct ingenic_soc_info *soc_info;52	struct regmap *map;53	struct clk *clk;54 55	struct clk_hw_onecell_data *clocks;56};57 58static struct ingenic_tcu *ingenic_tcu;59 60static inline struct ingenic_tcu_clk *to_tcu_clk(struct clk_hw *hw)61{62	return container_of(hw, struct ingenic_tcu_clk, hw);63}64 65static int ingenic_tcu_enable(struct clk_hw *hw)66{67	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);68	const struct ingenic_tcu_clk_info *info = tcu_clk->info;69	struct ingenic_tcu *tcu = tcu_clk->tcu;70 71	regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));72 73	return 0;74}75 76static void ingenic_tcu_disable(struct clk_hw *hw)77{78	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);79	const struct ingenic_tcu_clk_info *info = tcu_clk->info;80	struct ingenic_tcu *tcu = tcu_clk->tcu;81 82	regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));83}84 85static int ingenic_tcu_is_enabled(struct clk_hw *hw)86{87	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);88	const struct ingenic_tcu_clk_info *info = tcu_clk->info;89	unsigned int value;90 91	regmap_read(tcu_clk->tcu->map, TCU_REG_TSR, &value);92 93	return !(value & BIT(info->gate_bit));94}95 96static bool ingenic_tcu_enable_regs(struct clk_hw *hw)97{98	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);99	const struct ingenic_tcu_clk_info *info = tcu_clk->info;100	struct ingenic_tcu *tcu = tcu_clk->tcu;101	bool enabled = false;102 103	/*104	 * According to the programming manual, a timer channel's registers can105	 * only be accessed when the channel's stop bit is clear.106	 */107	enabled = !!ingenic_tcu_is_enabled(hw);108	regmap_write(tcu->map, TCU_REG_TSCR, BIT(info->gate_bit));109 110	return enabled;111}112 113static void ingenic_tcu_disable_regs(struct clk_hw *hw)114{115	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);116	const struct ingenic_tcu_clk_info *info = tcu_clk->info;117	struct ingenic_tcu *tcu = tcu_clk->tcu;118 119	regmap_write(tcu->map, TCU_REG_TSSR, BIT(info->gate_bit));120}121 122static u8 ingenic_tcu_get_parent(struct clk_hw *hw)123{124	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);125	const struct ingenic_tcu_clk_info *info = tcu_clk->info;126	unsigned int val = 0;127	int ret;128 129	ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &val);130	WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);131 132	return ffs(val & TCU_TCSR_PARENT_CLOCK_MASK) - 1;133}134 135static int ingenic_tcu_set_parent(struct clk_hw *hw, u8 idx)136{137	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);138	const struct ingenic_tcu_clk_info *info = tcu_clk->info;139	bool was_enabled;140	int ret;141 142	was_enabled = ingenic_tcu_enable_regs(hw);143 144	ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,145				 TCU_TCSR_PARENT_CLOCK_MASK, BIT(idx));146	WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);147 148	if (!was_enabled)149		ingenic_tcu_disable_regs(hw);150 151	return 0;152}153 154static unsigned long ingenic_tcu_recalc_rate(struct clk_hw *hw,155		unsigned long parent_rate)156{157	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);158	const struct ingenic_tcu_clk_info *info = tcu_clk->info;159	unsigned int prescale;160	int ret;161 162	ret = regmap_read(tcu_clk->tcu->map, info->tcsr_reg, &prescale);163	WARN_ONCE(ret < 0, "Unable to read TCSR %d", tcu_clk->idx);164 165	prescale = (prescale & TCU_TCSR_PRESCALE_MASK) >> TCU_TCSR_PRESCALE_LSB;166 167	return parent_rate >> (prescale * 2);168}169 170static u8 ingenic_tcu_get_prescale(unsigned long rate, unsigned long req_rate)171{172	u8 prescale;173 174	for (prescale = 0; prescale < 5; prescale++)175		if ((rate >> (prescale * 2)) <= req_rate)176			return prescale;177 178	return 5; /* /1024 divider */179}180 181static int ingenic_tcu_determine_rate(struct clk_hw *hw,182				      struct clk_rate_request *req)183{184	unsigned long rate = req->best_parent_rate;185	u8 prescale;186 187	if (req->rate > rate) {188		req->rate = rate;189		return 0;190	}191 192	prescale = ingenic_tcu_get_prescale(rate, req->rate);193 194	req->rate = rate >> (prescale * 2);195	return 0;196}197 198static int ingenic_tcu_set_rate(struct clk_hw *hw, unsigned long req_rate,199		unsigned long parent_rate)200{201	struct ingenic_tcu_clk *tcu_clk = to_tcu_clk(hw);202	const struct ingenic_tcu_clk_info *info = tcu_clk->info;203	u8 prescale = ingenic_tcu_get_prescale(parent_rate, req_rate);204	bool was_enabled;205	int ret;206 207	was_enabled = ingenic_tcu_enable_regs(hw);208 209	ret = regmap_update_bits(tcu_clk->tcu->map, info->tcsr_reg,210				 TCU_TCSR_PRESCALE_MASK,211				 prescale << TCU_TCSR_PRESCALE_LSB);212	WARN_ONCE(ret < 0, "Unable to update TCSR %d", tcu_clk->idx);213 214	if (!was_enabled)215		ingenic_tcu_disable_regs(hw);216 217	return 0;218}219 220static const struct clk_ops ingenic_tcu_clk_ops = {221	.get_parent	= ingenic_tcu_get_parent,222	.set_parent	= ingenic_tcu_set_parent,223 224	.recalc_rate	= ingenic_tcu_recalc_rate,225	.determine_rate	= ingenic_tcu_determine_rate,226	.set_rate	= ingenic_tcu_set_rate,227 228	.enable		= ingenic_tcu_enable,229	.disable	= ingenic_tcu_disable,230	.is_enabled	= ingenic_tcu_is_enabled,231};232 233static const char * const ingenic_tcu_timer_parents[] = {234	[TCU_PARENT_PCLK] = "pclk",235	[TCU_PARENT_RTC]  = "rtc",236	[TCU_PARENT_EXT]  = "ext",237};238 239#define DEF_TIMER(_name, _gate_bit, _tcsr)				\240	{								\241		.init_data = {						\242			.name = _name,					\243			.parent_names = ingenic_tcu_timer_parents,	\244			.num_parents = ARRAY_SIZE(ingenic_tcu_timer_parents),\245			.ops = &ingenic_tcu_clk_ops,			\246			.flags = CLK_SET_RATE_UNGATE,			\247		},							\248		.gate_bit = _gate_bit,					\249		.tcsr_reg = _tcsr,					\250	}251static const struct ingenic_tcu_clk_info ingenic_tcu_clk_info[] = {252	[TCU_CLK_TIMER0] = DEF_TIMER("timer0", 0, TCU_REG_TCSRc(0)),253	[TCU_CLK_TIMER1] = DEF_TIMER("timer1", 1, TCU_REG_TCSRc(1)),254	[TCU_CLK_TIMER2] = DEF_TIMER("timer2", 2, TCU_REG_TCSRc(2)),255	[TCU_CLK_TIMER3] = DEF_TIMER("timer3", 3, TCU_REG_TCSRc(3)),256	[TCU_CLK_TIMER4] = DEF_TIMER("timer4", 4, TCU_REG_TCSRc(4)),257	[TCU_CLK_TIMER5] = DEF_TIMER("timer5", 5, TCU_REG_TCSRc(5)),258	[TCU_CLK_TIMER6] = DEF_TIMER("timer6", 6, TCU_REG_TCSRc(6)),259	[TCU_CLK_TIMER7] = DEF_TIMER("timer7", 7, TCU_REG_TCSRc(7)),260};261 262static const struct ingenic_tcu_clk_info ingenic_tcu_watchdog_clk_info =263					 DEF_TIMER("wdt", 16, TCU_REG_WDT_TCSR);264static const struct ingenic_tcu_clk_info ingenic_tcu_ost_clk_info =265					 DEF_TIMER("ost", 15, TCU_REG_OST_TCSR);266#undef DEF_TIMER267 268static int __init ingenic_tcu_register_clock(struct ingenic_tcu *tcu,269			unsigned int idx, enum tcu_clk_parent parent,270			const struct ingenic_tcu_clk_info *info,271			struct clk_hw_onecell_data *clocks)272{273	struct ingenic_tcu_clk *tcu_clk;274	int err;275 276	tcu_clk = kzalloc(sizeof(*tcu_clk), GFP_KERNEL);277	if (!tcu_clk)278		return -ENOMEM;279 280	tcu_clk->hw.init = &info->init_data;281	tcu_clk->idx = idx;282	tcu_clk->info = info;283	tcu_clk->tcu = tcu;284 285	/* Reset channel and clock divider, set default parent */286	ingenic_tcu_enable_regs(&tcu_clk->hw);287	regmap_update_bits(tcu->map, info->tcsr_reg, 0xffff, BIT(parent));288	ingenic_tcu_disable_regs(&tcu_clk->hw);289 290	err = clk_hw_register(NULL, &tcu_clk->hw);291	if (err) {292		kfree(tcu_clk);293		return err;294	}295 296	clocks->hws[idx] = &tcu_clk->hw;297 298	return 0;299}300 301static const struct ingenic_soc_info jz4740_soc_info = {302	.num_channels = 8,303	.has_ost = false,304	.has_tcu_clk = true,305};306 307static const struct ingenic_soc_info jz4725b_soc_info = {308	.num_channels = 6,309	.has_ost = true,310	.has_tcu_clk = true,311};312 313static const struct ingenic_soc_info jz4770_soc_info = {314	.num_channels = 8,315	.has_ost = true,316	.has_tcu_clk = false,317};318 319static const struct ingenic_soc_info x1000_soc_info = {320	.num_channels = 8,321	.has_ost = false, /* X1000 has OST, but it not belong TCU */322	.has_tcu_clk = true,323	.allow_missing_tcu_clk = true,324};325 326static const struct of_device_id __maybe_unused ingenic_tcu_of_match[] __initconst = {327	{ .compatible = "ingenic,jz4740-tcu", .data = &jz4740_soc_info, },328	{ .compatible = "ingenic,jz4725b-tcu", .data = &jz4725b_soc_info, },329	{ .compatible = "ingenic,jz4760-tcu", .data = &jz4770_soc_info, },330	{ .compatible = "ingenic,jz4770-tcu", .data = &jz4770_soc_info, },331	{ .compatible = "ingenic,x1000-tcu", .data = &x1000_soc_info, },332	{ /* sentinel */ }333};334 335static int __init ingenic_tcu_probe(struct device_node *np)336{337	const struct of_device_id *id = of_match_node(ingenic_tcu_of_match, np);338	struct ingenic_tcu *tcu;339	struct regmap *map;340	unsigned int i;341	int ret;342 343	map = device_node_to_regmap(np);344	if (IS_ERR(map))345		return PTR_ERR(map);346 347	tcu = kzalloc(sizeof(*tcu), GFP_KERNEL);348	if (!tcu)349		return -ENOMEM;350 351	tcu->map = map;352	tcu->soc_info = id->data;353 354	if (tcu->soc_info->has_tcu_clk) {355		tcu->clk = of_clk_get_by_name(np, "tcu");356		if (IS_ERR(tcu->clk)) {357			ret = PTR_ERR(tcu->clk);358 359			/*360			 * Old device trees for some SoCs did not include the361			 * TCU clock because this driver (incorrectly) didn't362			 * use it. In this case we complain loudly and attempt363			 * to continue without the clock, which might work if364			 * booting with workarounds like "clk_ignore_unused".365			 */366			if (tcu->soc_info->allow_missing_tcu_clk && ret == -EINVAL) {367				pr_warn("TCU clock missing from device tree, please update your device tree\n");368				tcu->clk = NULL;369			} else {370				pr_crit("Cannot get TCU clock from device tree\n");371				goto err_free_tcu;372			}373		} else {374			ret = clk_prepare_enable(tcu->clk);375			if (ret) {376				pr_crit("Unable to enable TCU clock\n");377				goto err_put_clk;378			}379		}380	}381 382	tcu->clocks = kzalloc(struct_size(tcu->clocks, hws, TCU_CLK_COUNT),383			      GFP_KERNEL);384	if (!tcu->clocks) {385		ret = -ENOMEM;386		goto err_clk_disable;387	}388 389	tcu->clocks->num = TCU_CLK_COUNT;390 391	for (i = 0; i < tcu->soc_info->num_channels; i++) {392		ret = ingenic_tcu_register_clock(tcu, i, TCU_PARENT_EXT,393						 &ingenic_tcu_clk_info[i],394						 tcu->clocks);395		if (ret) {396			pr_crit("cannot register clock %d\n", i);397			goto err_unregister_timer_clocks;398		}399	}400 401	/*402	 * We set EXT as the default parent clock for all the TCU clocks403	 * except for the watchdog one, where we set the RTC clock as the404	 * parent. Since the EXT and PCLK are much faster than the RTC clock,405	 * the watchdog would kick after a maximum time of 5s, and we might406	 * want a slower kicking time.407	 */408	ret = ingenic_tcu_register_clock(tcu, TCU_CLK_WDT, TCU_PARENT_RTC,409					 &ingenic_tcu_watchdog_clk_info,410					 tcu->clocks);411	if (ret) {412		pr_crit("cannot register watchdog clock\n");413		goto err_unregister_timer_clocks;414	}415 416	if (tcu->soc_info->has_ost) {417		ret = ingenic_tcu_register_clock(tcu, TCU_CLK_OST,418						 TCU_PARENT_EXT,419						 &ingenic_tcu_ost_clk_info,420						 tcu->clocks);421		if (ret) {422			pr_crit("cannot register ost clock\n");423			goto err_unregister_watchdog_clock;424		}425	}426 427	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, tcu->clocks);428	if (ret) {429		pr_crit("cannot add OF clock provider\n");430		goto err_unregister_ost_clock;431	}432 433	ingenic_tcu = tcu;434 435	return 0;436 437err_unregister_ost_clock:438	if (tcu->soc_info->has_ost)439		clk_hw_unregister(tcu->clocks->hws[i + 1]);440err_unregister_watchdog_clock:441	clk_hw_unregister(tcu->clocks->hws[i]);442err_unregister_timer_clocks:443	for (i = 0; i < tcu->clocks->num; i++)444		if (tcu->clocks->hws[i])445			clk_hw_unregister(tcu->clocks->hws[i]);446	kfree(tcu->clocks);447err_clk_disable:448	if (tcu->clk)449		clk_disable_unprepare(tcu->clk);450err_put_clk:451	if (tcu->clk)452		clk_put(tcu->clk);453err_free_tcu:454	kfree(tcu);455	return ret;456}457 458static int __maybe_unused tcu_pm_suspend(void)459{460	struct ingenic_tcu *tcu = ingenic_tcu;461 462	if (tcu->clk)463		clk_disable(tcu->clk);464 465	return 0;466}467 468static void __maybe_unused tcu_pm_resume(void)469{470	struct ingenic_tcu *tcu = ingenic_tcu;471 472	if (tcu->clk)473		clk_enable(tcu->clk);474}475 476static struct syscore_ops __maybe_unused tcu_pm_ops = {477	.suspend = tcu_pm_suspend,478	.resume = tcu_pm_resume,479};480 481static void __init ingenic_tcu_init(struct device_node *np)482{483	int ret = ingenic_tcu_probe(np);484 485	if (ret)486		pr_crit("Failed to initialize TCU clocks: %d\n", ret);487 488	if (IS_ENABLED(CONFIG_PM_SLEEP))489		register_syscore_ops(&tcu_pm_ops);490}491 492CLK_OF_DECLARE_DRIVER(jz4740_cgu, "ingenic,jz4740-tcu", ingenic_tcu_init);493CLK_OF_DECLARE_DRIVER(jz4725b_cgu, "ingenic,jz4725b-tcu", ingenic_tcu_init);494CLK_OF_DECLARE_DRIVER(jz4760_cgu, "ingenic,jz4760-tcu", ingenic_tcu_init);495CLK_OF_DECLARE_DRIVER(jz4770_cgu, "ingenic,jz4770-tcu", ingenic_tcu_init);496CLK_OF_DECLARE_DRIVER(x1000_cgu, "ingenic,x1000-tcu", ingenic_tcu_init);497