brintos

brintos / linux-shallow public Read only

0
0
Text · 13.1 KiB · cb6fc2f Raw
541 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Ingenic XBurst SoCs SYSOST clocks driver4 * Copyright (c) 2020 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>5 */6 7#include <linux/bitfield.h>8#include <linux/bitops.h>9#include <linux/clk.h>10#include <linux/clk-provider.h>11#include <linux/clockchips.h>12#include <linux/clocksource.h>13#include <linux/interrupt.h>14#include <linux/mfd/syscon.h>15#include <linux/of_address.h>16#include <linux/of_irq.h>17#include <linux/sched_clock.h>18#include <linux/slab.h>19#include <linux/syscore_ops.h>20 21#include <dt-bindings/clock/ingenic,sysost.h>22 23/* OST register offsets */24#define OST_REG_OSTCCR			0x0025#define OST_REG_OSTCR			0x0826#define OST_REG_OSTFR			0x0c27#define OST_REG_OSTMR			0x1028#define OST_REG_OST1DFR			0x1429#define OST_REG_OST1CNT			0x1830#define OST_REG_OST2CNTL		0x2031#define OST_REG_OSTCNT2HBUF		0x2432#define OST_REG_OSTESR			0x3433#define OST_REG_OSTECR			0x3834 35/* bits within the OSTCCR register */36#define OSTCCR_PRESCALE1_MASK	0x337#define OSTCCR_PRESCALE2_MASK	0xc38 39/* bits within the OSTCR register */40#define OSTCR_OST1CLR			BIT(0)41#define OSTCR_OST2CLR			BIT(1)42 43/* bits within the OSTFR register */44#define OSTFR_FFLAG				BIT(0)45 46/* bits within the OSTMR register */47#define OSTMR_FMASK				BIT(0)48 49/* bits within the OSTESR register */50#define OSTESR_OST1ENS			BIT(0)51#define OSTESR_OST2ENS			BIT(1)52 53/* bits within the OSTECR register */54#define OSTECR_OST1ENC			BIT(0)55#define OSTECR_OST2ENC			BIT(1)56 57struct ingenic_soc_info {58	unsigned int num_channels;59};60 61struct ingenic_ost_clk_info {62	struct clk_init_data init_data;63	u8 ostccr_reg;64};65 66struct ingenic_ost_clk {67	struct clk_hw hw;68	unsigned int idx;69	struct ingenic_ost *ost;70	const struct ingenic_ost_clk_info *info;71};72 73struct ingenic_ost {74	void __iomem *base;75	const struct ingenic_soc_info *soc_info;76	struct clk *clk, *percpu_timer_clk, *global_timer_clk;77	struct clock_event_device cevt;78	struct clocksource cs;79	char name[20];80 81	struct clk_hw_onecell_data *clocks;82};83 84static struct ingenic_ost *ingenic_ost;85 86static inline struct ingenic_ost_clk *to_ost_clk(struct clk_hw *hw)87{88	return container_of(hw, struct ingenic_ost_clk, hw);89}90 91static unsigned long ingenic_ost_percpu_timer_recalc_rate(struct clk_hw *hw,92		unsigned long parent_rate)93{94	struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);95	const struct ingenic_ost_clk_info *info = ost_clk->info;96	unsigned int prescale;97 98	prescale = readl(ost_clk->ost->base + info->ostccr_reg);99 100	prescale = FIELD_GET(OSTCCR_PRESCALE1_MASK, prescale);101 102	return parent_rate >> (prescale * 2);103}104 105static unsigned long ingenic_ost_global_timer_recalc_rate(struct clk_hw *hw,106		unsigned long parent_rate)107{108	struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);109	const struct ingenic_ost_clk_info *info = ost_clk->info;110	unsigned int prescale;111 112	prescale = readl(ost_clk->ost->base + info->ostccr_reg);113 114	prescale = FIELD_GET(OSTCCR_PRESCALE2_MASK, prescale);115 116	return parent_rate >> (prescale * 2);117}118 119static u8 ingenic_ost_get_prescale(unsigned long rate, unsigned long req_rate)120{121	u8 prescale;122 123	for (prescale = 0; prescale < 2; prescale++)124		if ((rate >> (prescale * 2)) <= req_rate)125			return prescale;126 127	return 2; /* /16 divider */128}129 130static long ingenic_ost_round_rate(struct clk_hw *hw, unsigned long req_rate,131		unsigned long *parent_rate)132{133	unsigned long rate = *parent_rate;134	u8 prescale;135 136	if (req_rate > rate)137		return rate;138 139	prescale = ingenic_ost_get_prescale(rate, req_rate);140 141	return rate >> (prescale * 2);142}143 144static int ingenic_ost_percpu_timer_set_rate(struct clk_hw *hw, unsigned long req_rate,145		unsigned long parent_rate)146{147	struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);148	const struct ingenic_ost_clk_info *info = ost_clk->info;149	u8 prescale = ingenic_ost_get_prescale(parent_rate, req_rate);150	int val;151 152	val = readl(ost_clk->ost->base + info->ostccr_reg);153	val &= ~OSTCCR_PRESCALE1_MASK;154	val |= FIELD_PREP(OSTCCR_PRESCALE1_MASK, prescale);155	writel(val, ost_clk->ost->base + info->ostccr_reg);156 157	return 0;158}159 160static int ingenic_ost_global_timer_set_rate(struct clk_hw *hw, unsigned long req_rate,161		unsigned long parent_rate)162{163	struct ingenic_ost_clk *ost_clk = to_ost_clk(hw);164	const struct ingenic_ost_clk_info *info = ost_clk->info;165	u8 prescale = ingenic_ost_get_prescale(parent_rate, req_rate);166	int val;167 168	val = readl(ost_clk->ost->base + info->ostccr_reg);169	val &= ~OSTCCR_PRESCALE2_MASK;170	val |= FIELD_PREP(OSTCCR_PRESCALE2_MASK, prescale);171	writel(val, ost_clk->ost->base + info->ostccr_reg);172 173	return 0;174}175 176static const struct clk_ops ingenic_ost_percpu_timer_ops = {177	.recalc_rate	= ingenic_ost_percpu_timer_recalc_rate,178	.round_rate		= ingenic_ost_round_rate,179	.set_rate		= ingenic_ost_percpu_timer_set_rate,180};181 182static const struct clk_ops ingenic_ost_global_timer_ops = {183	.recalc_rate	= ingenic_ost_global_timer_recalc_rate,184	.round_rate		= ingenic_ost_round_rate,185	.set_rate		= ingenic_ost_global_timer_set_rate,186};187 188static const char * const ingenic_ost_clk_parents[] = { "ext" };189 190static const struct ingenic_ost_clk_info x1000_ost_clk_info[] = {191	[OST_CLK_PERCPU_TIMER] = {192		.init_data = {193			.name = "percpu timer",194			.parent_names = ingenic_ost_clk_parents,195			.num_parents = ARRAY_SIZE(ingenic_ost_clk_parents),196			.ops = &ingenic_ost_percpu_timer_ops,197			.flags = CLK_SET_RATE_UNGATE,198		},199		.ostccr_reg = OST_REG_OSTCCR,200	},201 202	[OST_CLK_GLOBAL_TIMER] = {203		.init_data = {204			.name = "global timer",205			.parent_names = ingenic_ost_clk_parents,206			.num_parents = ARRAY_SIZE(ingenic_ost_clk_parents),207			.ops = &ingenic_ost_global_timer_ops,208			.flags = CLK_SET_RATE_UNGATE,209		},210		.ostccr_reg = OST_REG_OSTCCR,211	},212};213 214static u64 notrace ingenic_ost_global_timer_read_cntl(void)215{216	struct ingenic_ost *ost = ingenic_ost;217	unsigned int count;218 219	count = readl(ost->base + OST_REG_OST2CNTL);220 221	return count;222}223 224static u64 notrace ingenic_ost_clocksource_read(struct clocksource *cs)225{226	return ingenic_ost_global_timer_read_cntl();227}228 229static inline struct ingenic_ost *to_ingenic_ost(struct clock_event_device *evt)230{231	return container_of(evt, struct ingenic_ost, cevt);232}233 234static int ingenic_ost_cevt_set_state_shutdown(struct clock_event_device *evt)235{236	struct ingenic_ost *ost = to_ingenic_ost(evt);237 238	writel(OSTECR_OST1ENC, ost->base + OST_REG_OSTECR);239 240	return 0;241}242 243static int ingenic_ost_cevt_set_next(unsigned long next,244				     struct clock_event_device *evt)245{246	struct ingenic_ost *ost = to_ingenic_ost(evt);247 248	writel((u32)~OSTFR_FFLAG, ost->base + OST_REG_OSTFR);249	writel(next, ost->base + OST_REG_OST1DFR);250	writel(OSTCR_OST1CLR, ost->base + OST_REG_OSTCR);251	writel(OSTESR_OST1ENS, ost->base + OST_REG_OSTESR);252	writel((u32)~OSTMR_FMASK, ost->base + OST_REG_OSTMR);253 254	return 0;255}256 257static irqreturn_t ingenic_ost_cevt_cb(int irq, void *dev_id)258{259	struct clock_event_device *evt = dev_id;260	struct ingenic_ost *ost = to_ingenic_ost(evt);261 262	writel(OSTECR_OST1ENC, ost->base + OST_REG_OSTECR);263 264	if (evt->event_handler)265		evt->event_handler(evt);266 267	return IRQ_HANDLED;268}269 270static int __init ingenic_ost_register_clock(struct ingenic_ost *ost,271			unsigned int idx, const struct ingenic_ost_clk_info *info,272			struct clk_hw_onecell_data *clocks)273{274	struct ingenic_ost_clk *ost_clk;275	int val, err;276 277	ost_clk = kzalloc(sizeof(*ost_clk), GFP_KERNEL);278	if (!ost_clk)279		return -ENOMEM;280 281	ost_clk->hw.init = &info->init_data;282	ost_clk->idx = idx;283	ost_clk->info = info;284	ost_clk->ost = ost;285 286	/* Reset clock divider */287	val = readl(ost->base + info->ostccr_reg);288	val &= ~(OSTCCR_PRESCALE1_MASK | OSTCCR_PRESCALE2_MASK);289	writel(val, ost->base + info->ostccr_reg);290 291	err = clk_hw_register(NULL, &ost_clk->hw);292	if (err) {293		kfree(ost_clk);294		return err;295	}296 297	clocks->hws[idx] = &ost_clk->hw;298 299	return 0;300}301 302static struct clk * __init ingenic_ost_get_clock(struct device_node *np, int id)303{304	struct of_phandle_args args;305 306	args.np = np;307	args.args_count = 1;308	args.args[0] = id;309 310	return of_clk_get_from_provider(&args);311}312 313static int __init ingenic_ost_percpu_timer_init(struct device_node *np,314					 struct ingenic_ost *ost)315{316	unsigned int timer_virq, channel = OST_CLK_PERCPU_TIMER;317	unsigned long rate;318	int err;319 320	ost->percpu_timer_clk = ingenic_ost_get_clock(np, channel);321	if (IS_ERR(ost->percpu_timer_clk))322		return PTR_ERR(ost->percpu_timer_clk);323 324	err = clk_prepare_enable(ost->percpu_timer_clk);325	if (err)326		goto err_clk_put;327 328	rate = clk_get_rate(ost->percpu_timer_clk);329	if (!rate) {330		err = -EINVAL;331		goto err_clk_disable;332	}333 334	timer_virq = of_irq_get(np, 0);335	if (!timer_virq) {336		err = -EINVAL;337		goto err_clk_disable;338	}339 340	snprintf(ost->name, sizeof(ost->name), "OST percpu timer");341 342	err = request_irq(timer_virq, ingenic_ost_cevt_cb, IRQF_TIMER,343			  ost->name, &ost->cevt);344	if (err)345		goto err_irq_dispose_mapping;346 347	ost->cevt.cpumask = cpumask_of(smp_processor_id());348	ost->cevt.features = CLOCK_EVT_FEAT_ONESHOT;349	ost->cevt.name = ost->name;350	ost->cevt.rating = 400;351	ost->cevt.set_state_shutdown = ingenic_ost_cevt_set_state_shutdown;352	ost->cevt.set_next_event = ingenic_ost_cevt_set_next;353 354	clockevents_config_and_register(&ost->cevt, rate, 4, 0xffffffff);355 356	return 0;357 358err_irq_dispose_mapping:359	irq_dispose_mapping(timer_virq);360err_clk_disable:361	clk_disable_unprepare(ost->percpu_timer_clk);362err_clk_put:363	clk_put(ost->percpu_timer_clk);364	return err;365}366 367static int __init ingenic_ost_global_timer_init(struct device_node *np,368					       struct ingenic_ost *ost)369{370	unsigned int channel = OST_CLK_GLOBAL_TIMER;371	struct clocksource *cs = &ost->cs;372	unsigned long rate;373	int err;374 375	ost->global_timer_clk = ingenic_ost_get_clock(np, channel);376	if (IS_ERR(ost->global_timer_clk))377		return PTR_ERR(ost->global_timer_clk);378 379	err = clk_prepare_enable(ost->global_timer_clk);380	if (err)381		goto err_clk_put;382 383	rate = clk_get_rate(ost->global_timer_clk);384	if (!rate) {385		err = -EINVAL;386		goto err_clk_disable;387	}388 389	/* Clear counter CNT registers */390	writel(OSTCR_OST2CLR, ost->base + OST_REG_OSTCR);391 392	/* Enable OST channel */393	writel(OSTESR_OST2ENS, ost->base + OST_REG_OSTESR);394 395	cs->name = "ingenic-ost";396	cs->rating = 400;397	cs->flags = CLOCK_SOURCE_IS_CONTINUOUS;398	cs->mask = CLOCKSOURCE_MASK(32);399	cs->read = ingenic_ost_clocksource_read;400 401	err = clocksource_register_hz(cs, rate);402	if (err)403		goto err_clk_disable;404 405	return 0;406 407err_clk_disable:408	clk_disable_unprepare(ost->global_timer_clk);409err_clk_put:410	clk_put(ost->global_timer_clk);411	return err;412}413 414static const struct ingenic_soc_info x1000_soc_info = {415	.num_channels = 2,416};417 418static const struct of_device_id __maybe_unused ingenic_ost_of_matches[] __initconst = {419	{ .compatible = "ingenic,x1000-ost", .data = &x1000_soc_info },420	{ /* sentinel */ }421};422 423static int __init ingenic_ost_probe(struct device_node *np)424{425	const struct of_device_id *id = of_match_node(ingenic_ost_of_matches, np);426	struct ingenic_ost *ost;427	unsigned int i;428	int ret;429 430	ost = kzalloc(sizeof(*ost), GFP_KERNEL);431	if (!ost)432		return -ENOMEM;433 434	ost->base = of_io_request_and_map(np, 0, of_node_full_name(np));435	if (IS_ERR(ost->base)) {436		pr_err("%s: Failed to map OST registers\n", __func__);437		ret = PTR_ERR(ost->base);438		goto err_free_ost;439	}440 441	ost->clk = of_clk_get_by_name(np, "ost");442	if (IS_ERR(ost->clk)) {443		ret = PTR_ERR(ost->clk);444		pr_crit("%s: Cannot get OST clock\n", __func__);445		goto err_free_ost;446	}447 448	ret = clk_prepare_enable(ost->clk);449	if (ret) {450		pr_crit("%s: Unable to enable OST clock\n", __func__);451		goto err_put_clk;452	}453 454	ost->soc_info = id->data;455 456	ost->clocks = kzalloc(struct_size(ost->clocks, hws, ost->soc_info->num_channels),457			      GFP_KERNEL);458	if (!ost->clocks) {459		ret = -ENOMEM;460		goto err_clk_disable;461	}462 463	ost->clocks->num = ost->soc_info->num_channels;464 465	for (i = 0; i < ost->clocks->num; i++) {466		ret = ingenic_ost_register_clock(ost, i, &x1000_ost_clk_info[i], ost->clocks);467		if (ret) {468			pr_crit("%s: Cannot register clock %d\n", __func__, i);469			goto err_unregister_ost_clocks;470		}471	}472 473	ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, ost->clocks);474	if (ret) {475		pr_crit("%s: Cannot add OF clock provider\n", __func__);476		goto err_unregister_ost_clocks;477	}478 479	ingenic_ost = ost;480 481	return 0;482 483err_unregister_ost_clocks:484	for (i = 0; i < ost->clocks->num; i++)485		if (ost->clocks->hws[i])486			clk_hw_unregister(ost->clocks->hws[i]);487	kfree(ost->clocks);488err_clk_disable:489	clk_disable_unprepare(ost->clk);490err_put_clk:491	clk_put(ost->clk);492err_free_ost:493	kfree(ost);494	return ret;495}496 497static int __init ingenic_ost_init(struct device_node *np)498{499	struct ingenic_ost *ost;500	unsigned long rate;501	int ret;502 503	ret = ingenic_ost_probe(np);504	if (ret) {505		pr_crit("%s: Failed to initialize OST clocks: %d\n", __func__, ret);506		return ret;507	}508 509	of_node_clear_flag(np, OF_POPULATED);510 511	ost = ingenic_ost;512	if (IS_ERR(ost))513		return PTR_ERR(ost);514 515	ret = ingenic_ost_global_timer_init(np, ost);516	if (ret) {517		pr_crit("%s: Unable to init global timer: %x\n", __func__, ret);518		goto err_free_ingenic_ost;519	}520 521	ret = ingenic_ost_percpu_timer_init(np, ost);522	if (ret)523		goto err_ost_global_timer_cleanup;524 525	/* Register the sched_clock at the end as there's no way to undo it */526	rate = clk_get_rate(ost->global_timer_clk);527	sched_clock_register(ingenic_ost_global_timer_read_cntl, 32, rate);528 529	return 0;530 531err_ost_global_timer_cleanup:532	clocksource_unregister(&ost->cs);533	clk_disable_unprepare(ost->global_timer_clk);534	clk_put(ost->global_timer_clk);535err_free_ingenic_ost:536	kfree(ost);537	return ret;538}539 540TIMER_OF_DECLARE(x1000_ost,  "ingenic,x1000-ost",  ingenic_ost_init);541