18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2012-2013 Freescale Semiconductor, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 78c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/of_address.h> 108c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 118c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * Each pit takes 0x10 Bytes register space 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci#define PITMCR 0x00 178c2ecf20Sopenharmony_ci#define PIT0_OFFSET 0x100 188c2ecf20Sopenharmony_ci#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n)) 198c2ecf20Sopenharmony_ci#define PITLDVAL 0x00 208c2ecf20Sopenharmony_ci#define PITCVAL 0x04 218c2ecf20Sopenharmony_ci#define PITTCTRL 0x08 228c2ecf20Sopenharmony_ci#define PITTFLG 0x0c 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define PITMCR_MDIS (0x1 << 1) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define PITTCTRL_TEN (0x1 << 0) 278c2ecf20Sopenharmony_ci#define PITTCTRL_TIE (0x1 << 1) 288c2ecf20Sopenharmony_ci#define PITCTRL_CHN (0x1 << 2) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define PITTFLG_TIF 0x1 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void __iomem *clksrc_base; 338c2ecf20Sopenharmony_cistatic void __iomem *clkevt_base; 348c2ecf20Sopenharmony_cistatic unsigned long cycle_per_jiffy; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline void pit_timer_enable(void) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline void pit_timer_disable(void) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci __raw_writel(0, clkevt_base + PITTCTRL); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic inline void pit_irq_acknowledge(void) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic u64 notrace pit_read_sched_clock(void) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci return ~__raw_readl(clksrc_base + PITCVAL); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic int __init pit_clocksource_init(unsigned long rate) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci /* set the max load value and start the clock source counter */ 598c2ecf20Sopenharmony_ci __raw_writel(0, clksrc_base + PITTCTRL); 608c2ecf20Sopenharmony_ci __raw_writel(~0UL, clksrc_base + PITLDVAL); 618c2ecf20Sopenharmony_ci __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci sched_clock_register(pit_read_sched_clock, 32, rate); 648c2ecf20Sopenharmony_ci return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, 658c2ecf20Sopenharmony_ci 300, 32, clocksource_mmio_readl_down); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int pit_set_next_event(unsigned long delta, 698c2ecf20Sopenharmony_ci struct clock_event_device *unused) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * set a new value to PITLDVAL register will not restart the timer, 738c2ecf20Sopenharmony_ci * to abort the current cycle and start a timer period with the new 748c2ecf20Sopenharmony_ci * value, the timer must be disabled and enabled again. 758c2ecf20Sopenharmony_ci * and the PITLAVAL should be set to delta minus one according to pit 768c2ecf20Sopenharmony_ci * hardware requirement. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_ci pit_timer_disable(); 798c2ecf20Sopenharmony_ci __raw_writel(delta - 1, clkevt_base + PITLDVAL); 808c2ecf20Sopenharmony_ci pit_timer_enable(); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic int pit_shutdown(struct clock_event_device *evt) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci pit_timer_disable(); 888c2ecf20Sopenharmony_ci return 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic int pit_set_periodic(struct clock_event_device *evt) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci pit_set_next_event(cycle_per_jiffy, evt); 948c2ecf20Sopenharmony_ci return 0; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic irqreturn_t pit_timer_interrupt(int irq, void *dev_id) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci pit_irq_acknowledge(); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* 1048c2ecf20Sopenharmony_ci * pit hardware doesn't support oneshot, it will generate an interrupt 1058c2ecf20Sopenharmony_ci * and reload the counter value from PITLDVAL when PITCVAL reach zero, 1068c2ecf20Sopenharmony_ci * and start the counter again. So software need to disable the timer 1078c2ecf20Sopenharmony_ci * to stop the counter loop in ONESHOT mode. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci if (likely(clockevent_state_oneshot(evt))) 1108c2ecf20Sopenharmony_ci pit_timer_disable(); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci evt->event_handler(evt); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic struct clock_event_device clockevent_pit = { 1188c2ecf20Sopenharmony_ci .name = "VF pit timer", 1198c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 1208c2ecf20Sopenharmony_ci .set_state_shutdown = pit_shutdown, 1218c2ecf20Sopenharmony_ci .set_state_periodic = pit_set_periodic, 1228c2ecf20Sopenharmony_ci .set_next_event = pit_set_next_event, 1238c2ecf20Sopenharmony_ci .rating = 300, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic int __init pit_clockevent_init(unsigned long rate, int irq) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci __raw_writel(0, clkevt_base + PITTCTRL); 1298c2ecf20Sopenharmony_ci __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 1328c2ecf20Sopenharmony_ci "VF pit timer", &clockevent_pit)); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci clockevent_pit.cpumask = cpumask_of(0); 1358c2ecf20Sopenharmony_ci clockevent_pit.irq = irq; 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * The value for the LDVAL register trigger is calculated as: 1388c2ecf20Sopenharmony_ci * LDVAL trigger = (period / clock period) - 1 1398c2ecf20Sopenharmony_ci * The pit is a 32-bit down count timer, when the conter value 1408c2ecf20Sopenharmony_ci * reaches 0, it will generate an interrupt, thus the minimal 1418c2ecf20Sopenharmony_ci * LDVAL trigger value is 1. And then the min_delta is 1428c2ecf20Sopenharmony_ci * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int __init pit_timer_init(struct device_node *np) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct clk *pit_clk; 1528c2ecf20Sopenharmony_ci void __iomem *timer_base; 1538c2ecf20Sopenharmony_ci unsigned long clk_rate; 1548c2ecf20Sopenharmony_ci int irq, ret; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci timer_base = of_iomap(np, 0); 1578c2ecf20Sopenharmony_ci if (!timer_base) { 1588c2ecf20Sopenharmony_ci pr_err("Failed to iomap\n"); 1598c2ecf20Sopenharmony_ci return -ENXIO; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* 1638c2ecf20Sopenharmony_ci * PIT0 and PIT1 can be chained to build a 64-bit timer, 1648c2ecf20Sopenharmony_ci * so choose PIT2 as clocksource, PIT3 as clockevent device, 1658c2ecf20Sopenharmony_ci * and leave PIT0 and PIT1 unused for anyone else who needs them. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci clksrc_base = timer_base + PITn_OFFSET(2); 1688c2ecf20Sopenharmony_ci clkevt_base = timer_base + PITn_OFFSET(3); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 1718c2ecf20Sopenharmony_ci if (irq <= 0) 1728c2ecf20Sopenharmony_ci return -EINVAL; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci pit_clk = of_clk_get(np, 0); 1758c2ecf20Sopenharmony_ci if (IS_ERR(pit_clk)) 1768c2ecf20Sopenharmony_ci return PTR_ERR(pit_clk); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = clk_prepare_enable(pit_clk); 1798c2ecf20Sopenharmony_ci if (ret) 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(pit_clk); 1838c2ecf20Sopenharmony_ci cycle_per_jiffy = clk_rate / (HZ); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* enable the pit module */ 1868c2ecf20Sopenharmony_ci __raw_writel(~PITMCR_MDIS, timer_base + PITMCR); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ret = pit_clocksource_init(clk_rate); 1898c2ecf20Sopenharmony_ci if (ret) 1908c2ecf20Sopenharmony_ci return ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci return pit_clockevent_init(clk_rate, irq); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init); 195