162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Ralink RT2880 timer
462306a36Sopenharmony_ci * Author: John Crispin
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2013 John Crispin <john@phrozen.org>
762306a36Sopenharmony_ci*/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/platform_device.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/timer.h>
1262306a36Sopenharmony_ci#include <linux/of_gpio.h>
1362306a36Sopenharmony_ci#include <linux/clk.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/mach-ralink/ralink_regs.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define TIMER_REG_TMRSTAT		0x00
1862306a36Sopenharmony_ci#define TIMER_REG_TMR0LOAD		0x10
1962306a36Sopenharmony_ci#define TIMER_REG_TMR0CTL		0x18
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define TMRSTAT_TMR0INT			BIT(0)
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define TMR0CTL_ENABLE			BIT(7)
2462306a36Sopenharmony_ci#define TMR0CTL_MODE_PERIODIC		BIT(4)
2562306a36Sopenharmony_ci#define TMR0CTL_PRESCALER		1
2662306a36Sopenharmony_ci#define TMR0CTL_PRESCALE_VAL		(0xf - TMR0CTL_PRESCALER)
2762306a36Sopenharmony_ci#define TMR0CTL_PRESCALE_DIV		(65536 / BIT(TMR0CTL_PRESCALER))
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct rt_timer {
3062306a36Sopenharmony_ci	struct device	*dev;
3162306a36Sopenharmony_ci	void __iomem	*membase;
3262306a36Sopenharmony_ci	int		irq;
3362306a36Sopenharmony_ci	unsigned long	timer_freq;
3462306a36Sopenharmony_ci	unsigned long	timer_div;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic inline void rt_timer_w32(struct rt_timer *rt, u8 reg, u32 val)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	__raw_writel(val, rt->membase + reg);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic inline u32 rt_timer_r32(struct rt_timer *rt, u8 reg)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return __raw_readl(rt->membase + reg);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic irqreturn_t rt_timer_irq(int irq, void *_rt)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct rt_timer *rt =  (struct rt_timer *) _rt;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
5262306a36Sopenharmony_ci	rt_timer_w32(rt, TIMER_REG_TMRSTAT, TMRSTAT_TMR0INT);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return IRQ_HANDLED;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic int rt_timer_request(struct rt_timer *rt)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	int err = request_irq(rt->irq, rt_timer_irq, 0,
6162306a36Sopenharmony_ci						dev_name(rt->dev), rt);
6262306a36Sopenharmony_ci	if (err) {
6362306a36Sopenharmony_ci		dev_err(rt->dev, "failed to request irq\n");
6462306a36Sopenharmony_ci	} else {
6562306a36Sopenharmony_ci		u32 t = TMR0CTL_MODE_PERIODIC | TMR0CTL_PRESCALE_VAL;
6662306a36Sopenharmony_ci		rt_timer_w32(rt, TIMER_REG_TMR0CTL, t);
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	return err;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int rt_timer_config(struct rt_timer *rt, unsigned long divisor)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	if (rt->timer_freq < divisor)
7462306a36Sopenharmony_ci		rt->timer_div = rt->timer_freq;
7562306a36Sopenharmony_ci	else
7662306a36Sopenharmony_ci		rt->timer_div = divisor;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return 0;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int rt_timer_enable(struct rt_timer *rt)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	u32 t;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	rt_timer_w32(rt, TIMER_REG_TMR0LOAD, rt->timer_freq / rt->timer_div);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	t = rt_timer_r32(rt, TIMER_REG_TMR0CTL);
9062306a36Sopenharmony_ci	t |= TMR0CTL_ENABLE;
9162306a36Sopenharmony_ci	rt_timer_w32(rt, TIMER_REG_TMR0CTL, t);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return 0;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic int rt_timer_probe(struct platform_device *pdev)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct rt_timer *rt;
9962306a36Sopenharmony_ci	struct clk *clk;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	rt = devm_kzalloc(&pdev->dev, sizeof(*rt), GFP_KERNEL);
10262306a36Sopenharmony_ci	if (!rt) {
10362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate memory\n");
10462306a36Sopenharmony_ci		return -ENOMEM;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	rt->irq = platform_get_irq(pdev, 0);
10862306a36Sopenharmony_ci	if (rt->irq < 0)
10962306a36Sopenharmony_ci		return rt->irq;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	rt->membase = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
11262306a36Sopenharmony_ci	if (IS_ERR(rt->membase))
11362306a36Sopenharmony_ci		return PTR_ERR(rt->membase);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	clk = devm_clk_get(&pdev->dev, NULL);
11662306a36Sopenharmony_ci	if (IS_ERR(clk)) {
11762306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed get clock rate\n");
11862306a36Sopenharmony_ci		return PTR_ERR(clk);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	rt->timer_freq = clk_get_rate(clk) / TMR0CTL_PRESCALE_DIV;
12262306a36Sopenharmony_ci	if (!rt->timer_freq)
12362306a36Sopenharmony_ci		return -EINVAL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	rt->dev = &pdev->dev;
12662306a36Sopenharmony_ci	platform_set_drvdata(pdev, rt);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	rt_timer_request(rt);
12962306a36Sopenharmony_ci	rt_timer_config(rt, 2);
13062306a36Sopenharmony_ci	rt_timer_enable(rt);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	dev_info(&pdev->dev, "maximum frequency is %luHz\n", rt->timer_freq);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic const struct of_device_id rt_timer_match[] = {
13862306a36Sopenharmony_ci	{ .compatible = "ralink,rt2880-timer" },
13962306a36Sopenharmony_ci	{},
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic struct platform_driver rt_timer_driver = {
14362306a36Sopenharmony_ci	.probe = rt_timer_probe,
14462306a36Sopenharmony_ci	.driver = {
14562306a36Sopenharmony_ci		.name			= "rt-timer",
14662306a36Sopenharmony_ci		.of_match_table		= rt_timer_match,
14762306a36Sopenharmony_ci		.suppress_bind_attrs	= true,
14862306a36Sopenharmony_ci	},
14962306a36Sopenharmony_ci};
15062306a36Sopenharmony_cibuiltin_platform_driver(rt_timer_driver);
151