cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

timer-sun5i.c (9482B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Allwinner SoCs hstimer driver.
      4 *
      5 * Copyright (C) 2013 Maxime Ripard
      6 *
      7 * Maxime Ripard <maxime.ripard@free-electrons.com>
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/clockchips.h>
     12#include <linux/clocksource.h>
     13#include <linux/delay.h>
     14#include <linux/interrupt.h>
     15#include <linux/irq.h>
     16#include <linux/irqreturn.h>
     17#include <linux/reset.h>
     18#include <linux/slab.h>
     19#include <linux/of.h>
     20#include <linux/of_address.h>
     21#include <linux/of_irq.h>
     22
     23#define TIMER_IRQ_EN_REG		0x00
     24#define TIMER_IRQ_EN(val)			BIT(val)
     25#define TIMER_IRQ_ST_REG		0x04
     26#define TIMER_CTL_REG(val)		(0x20 * (val) + 0x10)
     27#define TIMER_CTL_ENABLE			BIT(0)
     28#define TIMER_CTL_RELOAD			BIT(1)
     29#define TIMER_CTL_CLK_PRES(val)			(((val) & 0x7) << 4)
     30#define TIMER_CTL_ONESHOT			BIT(7)
     31#define TIMER_INTVAL_LO_REG(val)	(0x20 * (val) + 0x14)
     32#define TIMER_INTVAL_HI_REG(val)	(0x20 * (val) + 0x18)
     33#define TIMER_CNTVAL_LO_REG(val)	(0x20 * (val) + 0x1c)
     34#define TIMER_CNTVAL_HI_REG(val)	(0x20 * (val) + 0x20)
     35
     36#define TIMER_SYNC_TICKS	3
     37
     38struct sun5i_timer {
     39	void __iomem		*base;
     40	struct clk		*clk;
     41	struct notifier_block	clk_rate_cb;
     42	u32			ticks_per_jiffy;
     43};
     44
     45#define to_sun5i_timer(x) \
     46	container_of(x, struct sun5i_timer, clk_rate_cb)
     47
     48struct sun5i_timer_clksrc {
     49	struct sun5i_timer	timer;
     50	struct clocksource	clksrc;
     51};
     52
     53#define to_sun5i_timer_clksrc(x) \
     54	container_of(x, struct sun5i_timer_clksrc, clksrc)
     55
     56struct sun5i_timer_clkevt {
     57	struct sun5i_timer		timer;
     58	struct clock_event_device	clkevt;
     59};
     60
     61#define to_sun5i_timer_clkevt(x) \
     62	container_of(x, struct sun5i_timer_clkevt, clkevt)
     63
     64/*
     65 * When we disable a timer, we need to wait at least for 2 cycles of
     66 * the timer source clock. We will use for that the clocksource timer
     67 * that is already setup and runs at the same frequency than the other
     68 * timers, and we never will be disabled.
     69 */
     70static void sun5i_clkevt_sync(struct sun5i_timer_clkevt *ce)
     71{
     72	u32 old = readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1));
     73
     74	while ((old - readl(ce->timer.base + TIMER_CNTVAL_LO_REG(1))) < TIMER_SYNC_TICKS)
     75		cpu_relax();
     76}
     77
     78static void sun5i_clkevt_time_stop(struct sun5i_timer_clkevt *ce, u8 timer)
     79{
     80	u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
     81	writel(val & ~TIMER_CTL_ENABLE, ce->timer.base + TIMER_CTL_REG(timer));
     82
     83	sun5i_clkevt_sync(ce);
     84}
     85
     86static void sun5i_clkevt_time_setup(struct sun5i_timer_clkevt *ce, u8 timer, u32 delay)
     87{
     88	writel(delay, ce->timer.base + TIMER_INTVAL_LO_REG(timer));
     89}
     90
     91static void sun5i_clkevt_time_start(struct sun5i_timer_clkevt *ce, u8 timer, bool periodic)
     92{
     93	u32 val = readl(ce->timer.base + TIMER_CTL_REG(timer));
     94
     95	if (periodic)
     96		val &= ~TIMER_CTL_ONESHOT;
     97	else
     98		val |= TIMER_CTL_ONESHOT;
     99
    100	writel(val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
    101	       ce->timer.base + TIMER_CTL_REG(timer));
    102}
    103
    104static int sun5i_clkevt_shutdown(struct clock_event_device *clkevt)
    105{
    106	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
    107
    108	sun5i_clkevt_time_stop(ce, 0);
    109	return 0;
    110}
    111
    112static int sun5i_clkevt_set_oneshot(struct clock_event_device *clkevt)
    113{
    114	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
    115
    116	sun5i_clkevt_time_stop(ce, 0);
    117	sun5i_clkevt_time_start(ce, 0, false);
    118	return 0;
    119}
    120
    121static int sun5i_clkevt_set_periodic(struct clock_event_device *clkevt)
    122{
    123	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
    124
    125	sun5i_clkevt_time_stop(ce, 0);
    126	sun5i_clkevt_time_setup(ce, 0, ce->timer.ticks_per_jiffy);
    127	sun5i_clkevt_time_start(ce, 0, true);
    128	return 0;
    129}
    130
    131static int sun5i_clkevt_next_event(unsigned long evt,
    132				   struct clock_event_device *clkevt)
    133{
    134	struct sun5i_timer_clkevt *ce = to_sun5i_timer_clkevt(clkevt);
    135
    136	sun5i_clkevt_time_stop(ce, 0);
    137	sun5i_clkevt_time_setup(ce, 0, evt - TIMER_SYNC_TICKS);
    138	sun5i_clkevt_time_start(ce, 0, false);
    139
    140	return 0;
    141}
    142
    143static irqreturn_t sun5i_timer_interrupt(int irq, void *dev_id)
    144{
    145	struct sun5i_timer_clkevt *ce = (struct sun5i_timer_clkevt *)dev_id;
    146
    147	writel(0x1, ce->timer.base + TIMER_IRQ_ST_REG);
    148	ce->clkevt.event_handler(&ce->clkevt);
    149
    150	return IRQ_HANDLED;
    151}
    152
    153static u64 sun5i_clksrc_read(struct clocksource *clksrc)
    154{
    155	struct sun5i_timer_clksrc *cs = to_sun5i_timer_clksrc(clksrc);
    156
    157	return ~readl(cs->timer.base + TIMER_CNTVAL_LO_REG(1));
    158}
    159
    160static int sun5i_rate_cb_clksrc(struct notifier_block *nb,
    161				unsigned long event, void *data)
    162{
    163	struct clk_notifier_data *ndata = data;
    164	struct sun5i_timer *timer = to_sun5i_timer(nb);
    165	struct sun5i_timer_clksrc *cs = container_of(timer, struct sun5i_timer_clksrc, timer);
    166
    167	switch (event) {
    168	case PRE_RATE_CHANGE:
    169		clocksource_unregister(&cs->clksrc);
    170		break;
    171
    172	case POST_RATE_CHANGE:
    173		clocksource_register_hz(&cs->clksrc, ndata->new_rate);
    174		break;
    175
    176	default:
    177		break;
    178	}
    179
    180	return NOTIFY_DONE;
    181}
    182
    183static int __init sun5i_setup_clocksource(struct device_node *node,
    184					  void __iomem *base,
    185					  struct clk *clk, int irq)
    186{
    187	struct sun5i_timer_clksrc *cs;
    188	unsigned long rate;
    189	int ret;
    190
    191	cs = kzalloc(sizeof(*cs), GFP_KERNEL);
    192	if (!cs)
    193		return -ENOMEM;
    194
    195	ret = clk_prepare_enable(clk);
    196	if (ret) {
    197		pr_err("Couldn't enable parent clock\n");
    198		goto err_free;
    199	}
    200
    201	rate = clk_get_rate(clk);
    202	if (!rate) {
    203		pr_err("Couldn't get parent clock rate\n");
    204		ret = -EINVAL;
    205		goto err_disable_clk;
    206	}
    207
    208	cs->timer.base = base;
    209	cs->timer.clk = clk;
    210	cs->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clksrc;
    211	cs->timer.clk_rate_cb.next = NULL;
    212
    213	ret = clk_notifier_register(clk, &cs->timer.clk_rate_cb);
    214	if (ret) {
    215		pr_err("Unable to register clock notifier.\n");
    216		goto err_disable_clk;
    217	}
    218
    219	writel(~0, base + TIMER_INTVAL_LO_REG(1));
    220	writel(TIMER_CTL_ENABLE | TIMER_CTL_RELOAD,
    221	       base + TIMER_CTL_REG(1));
    222
    223	cs->clksrc.name = node->name;
    224	cs->clksrc.rating = 340;
    225	cs->clksrc.read = sun5i_clksrc_read;
    226	cs->clksrc.mask = CLOCKSOURCE_MASK(32);
    227	cs->clksrc.flags = CLOCK_SOURCE_IS_CONTINUOUS;
    228
    229	ret = clocksource_register_hz(&cs->clksrc, rate);
    230	if (ret) {
    231		pr_err("Couldn't register clock source.\n");
    232		goto err_remove_notifier;
    233	}
    234
    235	return 0;
    236
    237err_remove_notifier:
    238	clk_notifier_unregister(clk, &cs->timer.clk_rate_cb);
    239err_disable_clk:
    240	clk_disable_unprepare(clk);
    241err_free:
    242	kfree(cs);
    243	return ret;
    244}
    245
    246static int sun5i_rate_cb_clkevt(struct notifier_block *nb,
    247				unsigned long event, void *data)
    248{
    249	struct clk_notifier_data *ndata = data;
    250	struct sun5i_timer *timer = to_sun5i_timer(nb);
    251	struct sun5i_timer_clkevt *ce = container_of(timer, struct sun5i_timer_clkevt, timer);
    252
    253	if (event == POST_RATE_CHANGE) {
    254		clockevents_update_freq(&ce->clkevt, ndata->new_rate);
    255		ce->timer.ticks_per_jiffy = DIV_ROUND_UP(ndata->new_rate, HZ);
    256	}
    257
    258	return NOTIFY_DONE;
    259}
    260
    261static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base,
    262					 struct clk *clk, int irq)
    263{
    264	struct sun5i_timer_clkevt *ce;
    265	unsigned long rate;
    266	int ret;
    267	u32 val;
    268
    269	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
    270	if (!ce)
    271		return -ENOMEM;
    272
    273	ret = clk_prepare_enable(clk);
    274	if (ret) {
    275		pr_err("Couldn't enable parent clock\n");
    276		goto err_free;
    277	}
    278
    279	rate = clk_get_rate(clk);
    280	if (!rate) {
    281		pr_err("Couldn't get parent clock rate\n");
    282		ret = -EINVAL;
    283		goto err_disable_clk;
    284	}
    285
    286	ce->timer.base = base;
    287	ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
    288	ce->timer.clk = clk;
    289	ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt;
    290	ce->timer.clk_rate_cb.next = NULL;
    291
    292	ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb);
    293	if (ret) {
    294		pr_err("Unable to register clock notifier.\n");
    295		goto err_disable_clk;
    296	}
    297
    298	ce->clkevt.name = node->name;
    299	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
    300	ce->clkevt.set_next_event = sun5i_clkevt_next_event;
    301	ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown;
    302	ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic;
    303	ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot;
    304	ce->clkevt.tick_resume = sun5i_clkevt_shutdown;
    305	ce->clkevt.rating = 340;
    306	ce->clkevt.irq = irq;
    307	ce->clkevt.cpumask = cpu_possible_mask;
    308
    309	/* Enable timer0 interrupt */
    310	val = readl(base + TIMER_IRQ_EN_REG);
    311	writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG);
    312
    313	clockevents_config_and_register(&ce->clkevt, rate,
    314					TIMER_SYNC_TICKS, 0xffffffff);
    315
    316	ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
    317			  "sun5i_timer0", ce);
    318	if (ret) {
    319		pr_err("Unable to register interrupt\n");
    320		goto err_remove_notifier;
    321	}
    322
    323	return 0;
    324
    325err_remove_notifier:
    326	clk_notifier_unregister(clk, &ce->timer.clk_rate_cb);
    327err_disable_clk:
    328	clk_disable_unprepare(clk);
    329err_free:
    330	kfree(ce);
    331	return ret;
    332}
    333
    334static int __init sun5i_timer_init(struct device_node *node)
    335{
    336	struct reset_control *rstc;
    337	void __iomem *timer_base;
    338	struct clk *clk;
    339	int irq, ret;
    340
    341	timer_base = of_io_request_and_map(node, 0, of_node_full_name(node));
    342	if (IS_ERR(timer_base)) {
    343		pr_err("Can't map registers\n");
    344		return PTR_ERR(timer_base);
    345	}
    346
    347	irq = irq_of_parse_and_map(node, 0);
    348	if (irq <= 0) {
    349		pr_err("Can't parse IRQ\n");
    350		return -EINVAL;
    351	}
    352
    353	clk = of_clk_get(node, 0);
    354	if (IS_ERR(clk)) {
    355		pr_err("Can't get timer clock\n");
    356		return PTR_ERR(clk);
    357	}
    358
    359	rstc = of_reset_control_get(node, NULL);
    360	if (!IS_ERR(rstc))
    361		reset_control_deassert(rstc);
    362
    363	ret = sun5i_setup_clocksource(node, timer_base, clk, irq);
    364	if (ret)
    365		return ret;
    366
    367	return sun5i_setup_clockevent(node, timer_base, clk, irq);
    368}
    369TIMER_OF_DECLARE(sun5i_a13, "allwinner,sun5i-a13-hstimer",
    370			   sun5i_timer_init);
    371TIMER_OF_DECLARE(sun7i_a20, "allwinner,sun7i-a20-hstimer",
    372			   sun5i_timer_init);