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