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