1 /* 2 * Copyright (c) 2019-2020 Cobham Gaisler AB 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 */ 6 7 /* 8 * This driver uses two independent GPTIMER subtimers in the following way: 9 * - subtimer 0 generates periodic interrupts and the ISR announces ticks. 10 * - subtimer 1 is used as up-counter. 11 */ 12 13 #define DT_DRV_COMPAT gaisler_gptimer 14 15 #include <drivers/timer/system_timer.h> 16 #include <sys_clock.h> 17 18 /* GPTIMER subtimer increments each microsecond. */ 19 #define PRESCALER (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC / 1000000) 20 21 /* GPTIMER Timer instance */ 22 struct gptimer_timer_regs { 23 uint32_t counter; 24 uint32_t reload; 25 uint32_t ctrl; 26 uint32_t latch; 27 }; 28 29 /* A GPTIMER can have maximum of 7 subtimers. */ 30 #define GPTIMER_MAX_SUBTIMERS 7 31 32 /* GPTIMER common registers */ 33 struct gptimer_regs { 34 uint32_t scaler_value; 35 uint32_t scaler_reload; 36 uint32_t cfg; 37 uint32_t latch_cfg; 38 struct gptimer_timer_regs timer[GPTIMER_MAX_SUBTIMERS]; 39 }; 40 41 #define GPTIMER_CTRL_WN (1 << 7) 42 #define GPTIMER_CTRL_IP (1 << 4) 43 #define GPTIMER_CTRL_IE (1 << 3) 44 #define GPTIMER_CTRL_LD (1 << 2) 45 #define GPTIMER_CTRL_RS (1 << 1) 46 #define GPTIMER_CTRL_EN (1 << 0) 47 #define GPTIMER_CFG_EL (1 << 11) 48 #define GPTIMER_CFG_DF (1 << 9) 49 #define GPTIMER_CFG_SI (1 << 8) 50 #define GPTIMER_CFG_IRQ (0x1f << 3) 51 #define GPTIMER_CFG_TIMERS (7 << 0) 52 get_regs(void)53static volatile struct gptimer_regs *get_regs(void) 54 { 55 return (struct gptimer_regs *) DT_INST_REG_ADDR(0); 56 } 57 get_timer_irq(void)58static int get_timer_irq(void) 59 { 60 return DT_INST_IRQN(0); 61 } 62 63 static uint32_t gptimer_ctrl_clear_ip; 64 timer_isr(const void * unused)65static void timer_isr(const void *unused) 66 { 67 ARG_UNUSED(unused); 68 volatile struct gptimer_regs *regs = get_regs(); 69 volatile struct gptimer_timer_regs *tmr = ®s->timer[0]; 70 uint32_t ctrl; 71 72 ctrl = tmr->ctrl; 73 if ((ctrl & GPTIMER_CTRL_IP) == 0) { 74 return; /* interrupt not for us */ 75 } 76 77 /* Clear pending */ 78 tmr->ctrl = GPTIMER_CTRL_IE | GPTIMER_CTRL_RS | 79 GPTIMER_CTRL_EN | gptimer_ctrl_clear_ip; 80 81 sys_clock_announce(1); 82 } 83 sys_clock_elapsed(void)84uint32_t sys_clock_elapsed(void) 85 { 86 return 0; 87 } 88 sys_clock_cycle_get_32(void)89uint32_t sys_clock_cycle_get_32(void) 90 { 91 volatile struct gptimer_regs *regs = get_regs(); 92 volatile struct gptimer_timer_regs *tmr = ®s->timer[1]; 93 uint32_t counter = tmr->counter; 94 95 return (0 - counter) * PRESCALER; 96 } 97 init_downcounter(volatile struct gptimer_timer_regs * tmr)98static void init_downcounter(volatile struct gptimer_timer_regs *tmr) 99 { 100 tmr->reload = 0xFFFFFFFF; 101 tmr->ctrl = GPTIMER_CTRL_LD | GPTIMER_CTRL_RS | GPTIMER_CTRL_EN; 102 } 103 sys_clock_driver_init(const struct device * dev)104int sys_clock_driver_init(const struct device *dev) 105 { 106 ARG_UNUSED(dev); 107 const int timer_interrupt = get_timer_irq(); 108 volatile struct gptimer_regs *regs = get_regs(); 109 volatile struct gptimer_timer_regs *tmr = ®s->timer[0]; 110 111 init_downcounter(®s->timer[1]); 112 113 /* Stop timer and probe how CTRL_IP is cleared (write 1 or 0). */ 114 tmr->ctrl = GPTIMER_CTRL_IP; 115 if ((tmr->ctrl & GPTIMER_CTRL_IP) == 0) { 116 /* IP bit is cleared by setting it to 1. */ 117 gptimer_ctrl_clear_ip = GPTIMER_CTRL_IP; 118 } 119 120 /* Configure timer scaler for 1 MHz subtimer tick */ 121 regs->scaler_reload = PRESCALER - 1; 122 tmr->reload = 1000000U / CONFIG_SYS_CLOCK_TICKS_PER_SEC - 1; 123 tmr->ctrl = GPTIMER_CTRL_IE | GPTIMER_CTRL_LD | GPTIMER_CTRL_RS | 124 GPTIMER_CTRL_EN; 125 126 irq_connect_dynamic(timer_interrupt, 0, timer_isr, NULL, 0); 127 irq_enable(timer_interrupt); 128 return 0; 129 } 130