brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · efe8cad Raw
274 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2015 ARM Limited4 *5 * Author: Vladimir Murzin <vladimir.murzin@arm.com>6 */7 8#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt9 10#include <linux/clk.h>11#include <linux/clockchips.h>12#include <linux/clocksource.h>13#include <linux/err.h>14#include <linux/interrupt.h>15#include <linux/io.h>16#include <linux/irq.h>17#include <linux/of_address.h>18#include <linux/of.h>19#include <linux/of_irq.h>20#include <linux/sched_clock.h>21#include <linux/slab.h>22 23#define TIMER_CTRL		0x024#define TIMER_CTRL_ENABLE	BIT(0)25#define TIMER_CTRL_IE		BIT(3)26 27#define TIMER_VALUE		0x428#define TIMER_RELOAD		0x829#define TIMER_INT		0xc30 31struct clockevent_mps2 {32	void __iomem *reg;33	u32 clock_count_per_tick;34	struct clock_event_device clkevt;35};36 37static void __iomem *sched_clock_base;38 39static u64 notrace mps2_sched_read(void)40{41	return ~readl_relaxed(sched_clock_base + TIMER_VALUE);42}43 44static inline struct clockevent_mps2 *to_mps2_clkevt(struct clock_event_device *c)45{46	return container_of(c, struct clockevent_mps2, clkevt);47}48 49static void clockevent_mps2_writel(u32 val, struct clock_event_device *c, u32 offset)50{51	writel_relaxed(val, to_mps2_clkevt(c)->reg + offset);52}53 54static int mps2_timer_shutdown(struct clock_event_device *ce)55{56	clockevent_mps2_writel(0, ce, TIMER_RELOAD);57	clockevent_mps2_writel(0, ce, TIMER_CTRL);58 59	return 0;60}61 62static int mps2_timer_set_next_event(unsigned long next, struct clock_event_device *ce)63{64	clockevent_mps2_writel(next, ce, TIMER_VALUE);65	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);66 67	return 0;68}69 70static int mps2_timer_set_periodic(struct clock_event_device *ce)71{72	u32 clock_count_per_tick = to_mps2_clkevt(ce)->clock_count_per_tick;73 74	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_RELOAD);75	clockevent_mps2_writel(clock_count_per_tick, ce, TIMER_VALUE);76	clockevent_mps2_writel(TIMER_CTRL_IE | TIMER_CTRL_ENABLE, ce, TIMER_CTRL);77 78	return 0;79}80 81static irqreturn_t mps2_timer_interrupt(int irq, void *dev_id)82{83	struct clockevent_mps2 *ce = dev_id;84	u32 status = readl_relaxed(ce->reg + TIMER_INT);85 86	if (!status) {87		pr_warn("spurious interrupt\n");88		return IRQ_NONE;89	}90 91	writel_relaxed(1, ce->reg + TIMER_INT);92 93	ce->clkevt.event_handler(&ce->clkevt);94 95	return IRQ_HANDLED;96}97 98static int __init mps2_clockevent_init(struct device_node *np)99{100	void __iomem *base;101	struct clk *clk = NULL;102	struct clockevent_mps2 *ce;103	u32 rate;104	int irq, ret;105	const char *name = "mps2-clkevt";106 107	ret = of_property_read_u32(np, "clock-frequency", &rate);108	if (ret) {109		clk = of_clk_get(np, 0);110		if (IS_ERR(clk)) {111			ret = PTR_ERR(clk);112			pr_err("failed to get clock for clockevent: %d\n", ret);113			goto out;114		}115 116		ret = clk_prepare_enable(clk);117		if (ret) {118			pr_err("failed to enable clock for clockevent: %d\n", ret);119			goto out_clk_put;120		}121 122		rate = clk_get_rate(clk);123	}124 125	base = of_iomap(np, 0);126	if (!base) {127		ret = -EADDRNOTAVAIL;128		pr_err("failed to map register for clockevent: %d\n", ret);129		goto out_clk_disable;130	}131 132	irq = irq_of_parse_and_map(np, 0);133	if (!irq) {134		ret = -ENOENT;135		pr_err("failed to get irq for clockevent: %d\n", ret);136		goto out_iounmap;137	}138 139	ce = kzalloc(sizeof(*ce), GFP_KERNEL);140	if (!ce) {141		ret = -ENOMEM;142		goto out_iounmap;143	}144 145	ce->reg = base;146	ce->clock_count_per_tick = DIV_ROUND_CLOSEST(rate, HZ);147	ce->clkevt.irq = irq;148	ce->clkevt.name = name;149	ce->clkevt.rating = 200;150	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;151	ce->clkevt.cpumask = cpu_possible_mask;152	ce->clkevt.set_state_shutdown	= mps2_timer_shutdown;153	ce->clkevt.set_state_periodic	= mps2_timer_set_periodic;154	ce->clkevt.set_state_oneshot	= mps2_timer_shutdown;155	ce->clkevt.set_next_event	= mps2_timer_set_next_event;156 157	/* Ensure timer is disabled */158	writel_relaxed(0, base + TIMER_CTRL);159 160	ret = request_irq(irq, mps2_timer_interrupt, IRQF_TIMER, name, ce);161	if (ret) {162		pr_err("failed to request irq for clockevent: %d\n", ret);163		goto out_kfree;164	}165 166	clockevents_config_and_register(&ce->clkevt, rate, 0xf, 0xffffffff);167 168	return 0;169 170out_kfree:171	kfree(ce);172out_iounmap:173	iounmap(base);174out_clk_disable:175	/* clk_{disable, unprepare, put}() can handle NULL as a parameter */176	clk_disable_unprepare(clk);177out_clk_put:178	clk_put(clk);179out:180	return ret;181}182 183static int __init mps2_clocksource_init(struct device_node *np)184{185	void __iomem *base;186	struct clk *clk = NULL;187	u32 rate;188	int ret;189	const char *name = "mps2-clksrc";190 191	ret = of_property_read_u32(np, "clock-frequency", &rate);192	if (ret) {193		clk = of_clk_get(np, 0);194		if (IS_ERR(clk)) {195			ret = PTR_ERR(clk);196			pr_err("failed to get clock for clocksource: %d\n", ret);197			goto out;198		}199 200		ret = clk_prepare_enable(clk);201		if (ret) {202			pr_err("failed to enable clock for clocksource: %d\n", ret);203			goto out_clk_put;204		}205 206		rate = clk_get_rate(clk);207	}208 209	base = of_iomap(np, 0);210	if (!base) {211		ret = -EADDRNOTAVAIL;212		pr_err("failed to map register for clocksource: %d\n", ret);213		goto out_clk_disable;214	}215 216	/* Ensure timer is disabled */217	writel_relaxed(0, base + TIMER_CTRL);218 219	/* ... and set it up as free-running clocksource */220	writel_relaxed(0xffffffff, base + TIMER_VALUE);221	writel_relaxed(0xffffffff, base + TIMER_RELOAD);222 223	writel_relaxed(TIMER_CTRL_ENABLE, base + TIMER_CTRL);224 225	ret = clocksource_mmio_init(base + TIMER_VALUE, name,226				    rate, 200, 32,227				    clocksource_mmio_readl_down);228	if (ret) {229		pr_err("failed to init clocksource: %d\n", ret);230		goto out_iounmap;231	}232 233	sched_clock_base = base;234	sched_clock_register(mps2_sched_read, 32, rate);235 236	return 0;237 238out_iounmap:239	iounmap(base);240out_clk_disable:241	/* clk_{disable, unprepare, put}() can handle NULL as a parameter */242	clk_disable_unprepare(clk);243out_clk_put:244	clk_put(clk);245out:246	return ret;247}248 249static int __init mps2_timer_init(struct device_node *np)250{251	static int has_clocksource, has_clockevent;252	int ret;253 254	if (!has_clocksource) {255		ret = mps2_clocksource_init(np);256		if (!ret) {257			has_clocksource = 1;258			return 0;259		}260	}261 262	if (!has_clockevent) {263		ret = mps2_clockevent_init(np);264		if (!ret) {265			has_clockevent = 1;266			return 0;267		}268	}269 270	return 0;271}272 273TIMER_OF_DECLARE(mps2_timer, "arm,mps2-timer", mps2_timer_init);274