timer-vf-pit.c (5001B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright 2012-2013 Freescale Semiconductor, Inc. 4 */ 5 6#include <linux/interrupt.h> 7#include <linux/clockchips.h> 8#include <linux/clk.h> 9#include <linux/of_address.h> 10#include <linux/of_irq.h> 11#include <linux/sched_clock.h> 12 13/* 14 * Each pit takes 0x10 Bytes register space 15 */ 16#define PITMCR 0x00 17#define PIT0_OFFSET 0x100 18#define PITn_OFFSET(n) (PIT0_OFFSET + 0x10 * (n)) 19#define PITLDVAL 0x00 20#define PITCVAL 0x04 21#define PITTCTRL 0x08 22#define PITTFLG 0x0c 23 24#define PITMCR_MDIS (0x1 << 1) 25 26#define PITTCTRL_TEN (0x1 << 0) 27#define PITTCTRL_TIE (0x1 << 1) 28#define PITCTRL_CHN (0x1 << 2) 29 30#define PITTFLG_TIF 0x1 31 32static void __iomem *clksrc_base; 33static void __iomem *clkevt_base; 34static unsigned long cycle_per_jiffy; 35 36static inline void pit_timer_enable(void) 37{ 38 __raw_writel(PITTCTRL_TEN | PITTCTRL_TIE, clkevt_base + PITTCTRL); 39} 40 41static inline void pit_timer_disable(void) 42{ 43 __raw_writel(0, clkevt_base + PITTCTRL); 44} 45 46static inline void pit_irq_acknowledge(void) 47{ 48 __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); 49} 50 51static u64 notrace pit_read_sched_clock(void) 52{ 53 return ~__raw_readl(clksrc_base + PITCVAL); 54} 55 56static int __init pit_clocksource_init(unsigned long rate) 57{ 58 /* set the max load value and start the clock source counter */ 59 __raw_writel(0, clksrc_base + PITTCTRL); 60 __raw_writel(~0UL, clksrc_base + PITLDVAL); 61 __raw_writel(PITTCTRL_TEN, clksrc_base + PITTCTRL); 62 63 sched_clock_register(pit_read_sched_clock, 32, rate); 64 return clocksource_mmio_init(clksrc_base + PITCVAL, "vf-pit", rate, 65 300, 32, clocksource_mmio_readl_down); 66} 67 68static int pit_set_next_event(unsigned long delta, 69 struct clock_event_device *unused) 70{ 71 /* 72 * set a new value to PITLDVAL register will not restart the timer, 73 * to abort the current cycle and start a timer period with the new 74 * value, the timer must be disabled and enabled again. 75 * and the PITLAVAL should be set to delta minus one according to pit 76 * hardware requirement. 77 */ 78 pit_timer_disable(); 79 __raw_writel(delta - 1, clkevt_base + PITLDVAL); 80 pit_timer_enable(); 81 82 return 0; 83} 84 85static int pit_shutdown(struct clock_event_device *evt) 86{ 87 pit_timer_disable(); 88 return 0; 89} 90 91static int pit_set_periodic(struct clock_event_device *evt) 92{ 93 pit_set_next_event(cycle_per_jiffy, evt); 94 return 0; 95} 96 97static irqreturn_t pit_timer_interrupt(int irq, void *dev_id) 98{ 99 struct clock_event_device *evt = dev_id; 100 101 pit_irq_acknowledge(); 102 103 /* 104 * pit hardware doesn't support oneshot, it will generate an interrupt 105 * and reload the counter value from PITLDVAL when PITCVAL reach zero, 106 * and start the counter again. So software need to disable the timer 107 * to stop the counter loop in ONESHOT mode. 108 */ 109 if (likely(clockevent_state_oneshot(evt))) 110 pit_timer_disable(); 111 112 evt->event_handler(evt); 113 114 return IRQ_HANDLED; 115} 116 117static struct clock_event_device clockevent_pit = { 118 .name = "VF pit timer", 119 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 120 .set_state_shutdown = pit_shutdown, 121 .set_state_periodic = pit_set_periodic, 122 .set_next_event = pit_set_next_event, 123 .rating = 300, 124}; 125 126static int __init pit_clockevent_init(unsigned long rate, int irq) 127{ 128 __raw_writel(0, clkevt_base + PITTCTRL); 129 __raw_writel(PITTFLG_TIF, clkevt_base + PITTFLG); 130 131 BUG_ON(request_irq(irq, pit_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 132 "VF pit timer", &clockevent_pit)); 133 134 clockevent_pit.cpumask = cpumask_of(0); 135 clockevent_pit.irq = irq; 136 /* 137 * The value for the LDVAL register trigger is calculated as: 138 * LDVAL trigger = (period / clock period) - 1 139 * The pit is a 32-bit down count timer, when the counter value 140 * reaches 0, it will generate an interrupt, thus the minimal 141 * LDVAL trigger value is 1. And then the min_delta is 142 * minimal LDVAL trigger value + 1, and the max_delta is full 32-bit. 143 */ 144 clockevents_config_and_register(&clockevent_pit, rate, 2, 0xffffffff); 145 146 return 0; 147} 148 149static int __init pit_timer_init(struct device_node *np) 150{ 151 struct clk *pit_clk; 152 void __iomem *timer_base; 153 unsigned long clk_rate; 154 int irq, ret; 155 156 timer_base = of_iomap(np, 0); 157 if (!timer_base) { 158 pr_err("Failed to iomap\n"); 159 return -ENXIO; 160 } 161 162 /* 163 * PIT0 and PIT1 can be chained to build a 64-bit timer, 164 * so choose PIT2 as clocksource, PIT3 as clockevent device, 165 * and leave PIT0 and PIT1 unused for anyone else who needs them. 166 */ 167 clksrc_base = timer_base + PITn_OFFSET(2); 168 clkevt_base = timer_base + PITn_OFFSET(3); 169 170 irq = irq_of_parse_and_map(np, 0); 171 if (irq <= 0) 172 return -EINVAL; 173 174 pit_clk = of_clk_get(np, 0); 175 if (IS_ERR(pit_clk)) 176 return PTR_ERR(pit_clk); 177 178 ret = clk_prepare_enable(pit_clk); 179 if (ret) 180 return ret; 181 182 clk_rate = clk_get_rate(pit_clk); 183 cycle_per_jiffy = clk_rate / (HZ); 184 185 /* enable the pit module */ 186 __raw_writel(~PITMCR_MDIS, timer_base + PITMCR); 187 188 ret = pit_clocksource_init(clk_rate); 189 if (ret) 190 return ret; 191 192 return pit_clockevent_init(clk_rate, irq); 193} 194TIMER_OF_DECLARE(vf610, "fsl,vf610-pit", pit_timer_init);