1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * linux/arch/arm/mach-footbridge/dc21285-timer.c 4 * 5 * Copyright (C) 1998 Russell King. 6 * Copyright (C) 1998 Phil Blundell 7 */ 8 #include <linux/clockchips.h> 9 #include <linux/clocksource.h> 10 #include <linux/init.h> 11 #include <linux/interrupt.h> 12 #include <linux/irq.h> 13 #include <linux/sched_clock.h> 14 15 #include <asm/irq.h> 16 17 #include <asm/hardware/dec21285.h> 18 #include <asm/mach/time.h> 19 #include <asm/system_info.h> 20 21 #include "common.h" 22 23 static u64 cksrc_dc21285_read(struct clocksource *cs) 24 { 25 return cs->mask - *CSR_TIMER2_VALUE; 26 } 27 28 static int cksrc_dc21285_enable(struct clocksource *cs) 29 { 30 *CSR_TIMER2_LOAD = cs->mask; 31 *CSR_TIMER2_CLR = 0; 32 *CSR_TIMER2_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16; 33 return 0; 34 } 35 36 static void cksrc_dc21285_disable(struct clocksource *cs) 37 { 38 *CSR_TIMER2_CNTL = 0; 39 } 40 41 static struct clocksource cksrc_dc21285 = { 42 .name = "dc21285_timer2", 43 .rating = 200, 44 .read = cksrc_dc21285_read, 45 .enable = cksrc_dc21285_enable, 46 .disable = cksrc_dc21285_disable, 47 .mask = CLOCKSOURCE_MASK(24), 48 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 49 }; 50 51 static int ckevt_dc21285_set_next_event(unsigned long delta, 52 struct clock_event_device *c) 53 { 54 *CSR_TIMER1_CLR = 0; 55 *CSR_TIMER1_LOAD = delta; 56 *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16; 57 58 return 0; 59 } 60 61 static int ckevt_dc21285_shutdown(struct clock_event_device *c) 62 { 63 *CSR_TIMER1_CNTL = 0; 64 return 0; 65 } 66 67 static int ckevt_dc21285_set_periodic(struct clock_event_device *c) 68 { 69 *CSR_TIMER1_CLR = 0; 70 *CSR_TIMER1_LOAD = (mem_fclk_21285 + 8 * HZ) / (16 * HZ); 71 *CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | 72 TIMER_CNTL_DIV16; 73 return 0; 74 } 75 76 static struct clock_event_device ckevt_dc21285 = { 77 .name = "dc21285_timer1", 78 .features = CLOCK_EVT_FEAT_PERIODIC | 79 CLOCK_EVT_FEAT_ONESHOT, 80 .rating = 200, 81 .irq = IRQ_TIMER1, 82 .set_next_event = ckevt_dc21285_set_next_event, 83 .set_state_shutdown = ckevt_dc21285_shutdown, 84 .set_state_periodic = ckevt_dc21285_set_periodic, 85 .set_state_oneshot = ckevt_dc21285_shutdown, 86 .tick_resume = ckevt_dc21285_set_periodic, 87 }; 88 89 static irqreturn_t timer1_interrupt(int irq, void *dev_id) 90 { 91 struct clock_event_device *ce = dev_id; 92 93 *CSR_TIMER1_CLR = 0; 94 95 /* Stop the timer if in one-shot mode */ 96 if (clockevent_state_oneshot(ce)) 97 *CSR_TIMER1_CNTL = 0; 98 99 ce->event_handler(ce); 100 101 return IRQ_HANDLED; 102 } 103 104 /* 105 * Set up timer interrupt. 106 */ 107 void __init footbridge_timer_init(void) 108 { 109 struct clock_event_device *ce = &ckevt_dc21285; 110 unsigned rate = DIV_ROUND_CLOSEST(mem_fclk_21285, 16); 111 112 clocksource_register_hz(&cksrc_dc21285, rate); 113 114 if (request_irq(ce->irq, timer1_interrupt, IRQF_TIMER | IRQF_IRQPOLL, 115 "dc21285_timer1", &ckevt_dc21285)) 116 pr_err("Failed to request irq %d (dc21285_timer1)", ce->irq); 117 118 ce->cpumask = cpumask_of(smp_processor_id()); 119 clockevents_config_and_register(ce, rate, 0x4, 0xffffff); 120 } 121 122 static u64 notrace footbridge_read_sched_clock(void) 123 { 124 return ~*CSR_TIMER3_VALUE; 125 } 126 127 void __init footbridge_sched_clock(void) 128 { 129 unsigned rate = DIV_ROUND_CLOSEST(mem_fclk_21285, 16); 130 131 *CSR_TIMER3_LOAD = 0; 132 *CSR_TIMER3_CLR = 0; 133 *CSR_TIMER3_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV16; 134 135 sched_clock_register(footbridge_read_sched_clock, 24, rate); 136 } 137