162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Cirrus Logic CLPS711X clocksource driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/clockchips.h> 1062306a36Sopenharmony_ci#include <linux/clocksource.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/of_irq.h> 1562306a36Sopenharmony_ci#include <linux/sched_clock.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cienum { 1962306a36Sopenharmony_ci CLPS711X_CLKSRC_CLOCKSOURCE, 2062306a36Sopenharmony_ci CLPS711X_CLKSRC_CLOCKEVENT, 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic void __iomem *tcd; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic u64 notrace clps711x_sched_clock_read(void) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci return ~readw(tcd); 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void __init clps711x_clksrc_init(struct clk *clock, void __iomem *base) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci unsigned long rate = clk_get_rate(clock); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci tcd = base; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci clocksource_mmio_init(tcd, "clps711x-clocksource", rate, 300, 16, 3762306a36Sopenharmony_ci clocksource_mmio_readw_down); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci sched_clock_register(clps711x_sched_clock_read, 16, rate); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic irqreturn_t clps711x_timer_interrupt(int irq, void *dev_id) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct clock_event_device *evt = dev_id; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci evt->event_handler(evt); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return IRQ_HANDLED; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic int __init _clps711x_clkevt_init(struct clk *clock, void __iomem *base, 5262306a36Sopenharmony_ci unsigned int irq) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct clock_event_device *clkevt; 5562306a36Sopenharmony_ci unsigned long rate; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci clkevt = kzalloc(sizeof(*clkevt), GFP_KERNEL); 5862306a36Sopenharmony_ci if (!clkevt) 5962306a36Sopenharmony_ci return -ENOMEM; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci rate = clk_get_rate(clock); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* Set Timer prescaler */ 6462306a36Sopenharmony_ci writew(DIV_ROUND_CLOSEST(rate, HZ), base); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci clkevt->name = "clps711x-clockevent"; 6762306a36Sopenharmony_ci clkevt->rating = 300; 6862306a36Sopenharmony_ci clkevt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP; 6962306a36Sopenharmony_ci clkevt->cpumask = cpumask_of(0); 7062306a36Sopenharmony_ci clockevents_config_and_register(clkevt, HZ, 0, 0); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return request_irq(irq, clps711x_timer_interrupt, IRQF_TIMER, 7362306a36Sopenharmony_ci "clps711x-timer", clkevt); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int __init clps711x_timer_init(struct device_node *np) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned int irq = irq_of_parse_and_map(np, 0); 7962306a36Sopenharmony_ci struct clk *clock = of_clk_get(np, 0); 8062306a36Sopenharmony_ci void __iomem *base = of_iomap(np, 0); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci if (!base) 8362306a36Sopenharmony_ci return -ENOMEM; 8462306a36Sopenharmony_ci if (!irq) 8562306a36Sopenharmony_ci return -EINVAL; 8662306a36Sopenharmony_ci if (IS_ERR(clock)) 8762306a36Sopenharmony_ci return PTR_ERR(clock); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci switch (of_alias_get_id(np, "timer")) { 9062306a36Sopenharmony_ci case CLPS711X_CLKSRC_CLOCKSOURCE: 9162306a36Sopenharmony_ci clps711x_clksrc_init(clock, base); 9262306a36Sopenharmony_ci break; 9362306a36Sopenharmony_ci case CLPS711X_CLKSRC_CLOCKEVENT: 9462306a36Sopenharmony_ci return _clps711x_clkevt_init(clock, base, irq); 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci return -EINVAL; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ciTIMER_OF_DECLARE(clps711x, "cirrus,ep7209-timer", clps711x_timer_init); 102