162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2012-2013 Freescale Semiconductor, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/interrupt.h> 762306a36Sopenharmony_ci#include <linux/clockchips.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/of_address.h> 1062306a36Sopenharmony_ci#include <linux/of_irq.h> 1162306a36Sopenharmony_ci#include <linux/sched_clock.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Each pit takes 0x10 Bytes register space 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci#define PITMCR 0x00 1762306a36Sopenharmony_ci#define PIT0_OFFSET 0x100 1862306a36Sopenharmony_ci#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n)) 1962306a36Sopenharmony_ci#define PITLDVAL 0x00 2062306a36Sopenharmony_ci#define PITCVAL 0x04 2162306a36Sopenharmony_ci#define PITTCTRL 0x08 2262306a36Sopenharmony_ci#define PITTFLG 0x0c 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define PITMCR_MDIS (0x1 << 1) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define PITTCTRL_TEN (0x1 << 0) 2762306a36Sopenharmony_ci#define PITTCTRL_TIE (0x1 << 1) 2862306a36Sopenharmony_ci#define PITCTRL_CHN (0x1 << 2) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define PITTFLG_TIF 0x1 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void __iomem *clksrc_base; 3362306a36Sopenharmony_cistatic void __iomem *clkevt_base; 3462306a36Sopenharmony_cistatic unsigned long cycle_per_jiffy; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic inline void pit_timer_enable(void) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic inline void pit_timer_disable(void) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci __raw_writel(0, clkevt_base + PITTCTRL); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic inline void pit_irq_acknowledge(void) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic u64 notrace pit_read_sched_clock(void) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci return ~__raw_readl(clksrc_base + PITCVAL); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int __init pit_clocksource_init(unsigned long rate) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci /* set the max load value and start the clock source counter */ 5962306a36Sopenharmony_ci __raw_writel(0, clksrc_base + PITTCTRL); 6062306a36Sopenharmony_ci __raw_writel(~0UL, clksrc_base + PITLDVAL); 6162306a36Sopenharmony_ci __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci sched_clock_register(pit_read_sched_clock, 32, rate); 6462306a36Sopenharmony_ci return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, 6562306a36Sopenharmony_ci 300, 32, clocksource_mmio_readl_down); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int pit_set_next_event(unsigned long delta, 6962306a36Sopenharmony_ci struct clock_event_device *unused) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * set a new value to PITLDVAL register will not restart the timer, 7362306a36Sopenharmony_ci * to abort the current cycle and start a timer period with the new 7462306a36Sopenharmony_ci * value, the timer must be disabled and enabled again. 7562306a36Sopenharmony_ci * and the PITLAVAL should be set to delta minus one according to pit 7662306a36Sopenharmony_ci * hardware requirement. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci pit_timer_disable(); 7962306a36Sopenharmony_ci __raw_writel(delta - 1, clkevt_base + PITLDVAL); 8062306a36Sopenharmony_ci pit_timer_enable(); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int pit_shutdown(struct clock_event_device *evt) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci pit_timer_disable(); 8862306a36Sopenharmony_ci return 0; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int pit_set_periodic(struct clock_event_device *evt) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci pit_set_next_event(cycle_per_jiffy, evt); 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic irqreturn_t pit_timer_interrupt(int irq, void *dev_id) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct clock_event_device *evt = dev_id; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci pit_irq_acknowledge(); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * pit hardware doesn't support oneshot, it will generate an interrupt 10562306a36Sopenharmony_ci * and reload the counter value from PITLDVAL when PITCVAL reach zero, 10662306a36Sopenharmony_ci * and start the counter again. So software need to disable the timer 10762306a36Sopenharmony_ci * to stop the counter loop in ONESHOT mode. 10862306a36Sopenharmony_ci */ 10962306a36Sopenharmony_ci if (likely(clockevent_state_oneshot(evt))) 11062306a36Sopenharmony_ci pit_timer_disable(); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci evt->event_handler(evt); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci return IRQ_HANDLED; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic struct clock_event_device clockevent_pit = { 11862306a36Sopenharmony_ci .name = "VF pit timer", 11962306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 12062306a36Sopenharmony_ci .set_state_shutdown = pit_shutdown, 12162306a36Sopenharmony_ci .set_state_periodic = pit_set_periodic, 12262306a36Sopenharmony_ci .set_next_event = pit_set_next_event, 12362306a36Sopenharmony_ci .rating = 300, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int __init pit_clockevent_init(unsigned long rate, int irq) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci __raw_writel(0, clkevt_base + PITTCTRL); 12962306a36Sopenharmony_ci __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 13262306a36Sopenharmony_ci "VF pit timer", &clockevent_pit)); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci clockevent_pit.cpumask = cpumask_of(0); 13562306a36Sopenharmony_ci clockevent_pit.irq = irq; 13662306a36Sopenharmony_ci /* 13762306a36Sopenharmony_ci * The value for the LDVAL register trigger is calculated as: 13862306a36Sopenharmony_ci * LDVAL trigger = (period / clock period) - 1 13962306a36Sopenharmony_ci * The pit is a 32-bit down count timer, when the counter value 14062306a36Sopenharmony_ci * reaches 0, it will generate an interrupt, thus the minimal 14162306a36Sopenharmony_ci * LDVAL trigger value is 1. And then the min_delta is 14262306a36Sopenharmony_ci * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci return 0; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic int __init pit_timer_init(struct device_node *np) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct clk *pit_clk; 15262306a36Sopenharmony_ci void __iomem *timer_base; 15362306a36Sopenharmony_ci unsigned long clk_rate; 15462306a36Sopenharmony_ci int irq, ret; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci timer_base = of_iomap(np, 0); 15762306a36Sopenharmony_ci if (!timer_base) { 15862306a36Sopenharmony_ci pr_err("Failed to iomap\n"); 15962306a36Sopenharmony_ci return -ENXIO; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* 16362306a36Sopenharmony_ci * PIT0 and PIT1 can be chained to build a 64-bit timer, 16462306a36Sopenharmony_ci * so choose PIT2 as clocksource, PIT3 as clockevent device, 16562306a36Sopenharmony_ci * and leave PIT0 and PIT1 unused for anyone else who needs them. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci clksrc_base = timer_base + PITn_OFFSET(2); 16862306a36Sopenharmony_ci clkevt_base = timer_base + PITn_OFFSET(3); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 17162306a36Sopenharmony_ci if (irq <= 0) 17262306a36Sopenharmony_ci return -EINVAL; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci pit_clk = of_clk_get(np, 0); 17562306a36Sopenharmony_ci if (IS_ERR(pit_clk)) 17662306a36Sopenharmony_ci return PTR_ERR(pit_clk); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = clk_prepare_enable(pit_clk); 17962306a36Sopenharmony_ci if (ret) 18062306a36Sopenharmony_ci return ret; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci clk_rate = clk_get_rate(pit_clk); 18362306a36Sopenharmony_ci cycle_per_jiffy = clk_rate / (HZ); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* enable the pit module */ 18662306a36Sopenharmony_ci __raw_writel(~PITMCR_MDIS, timer_base + PITMCR); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = pit_clocksource_init(clk_rate); 18962306a36Sopenharmony_ci if (ret) 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return pit_clockevent_init(clk_rate, irq); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ciTIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); 195