16260fb04SPeter Tyser /* 26260fb04SPeter Tyser * (C) Copyright 2007 Michal Simek 36260fb04SPeter Tyser * 46260fb04SPeter Tyser * Michal SIMEK <monstr@monstr.eu> 56260fb04SPeter Tyser * 61a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 76260fb04SPeter Tyser */ 86260fb04SPeter Tyser 96260fb04SPeter Tyser #include <common.h> 10*9aa65cabSMichal Simek #include <fdtdec.h> 116260fb04SPeter Tyser #include <asm/microblaze_timer.h> 126260fb04SPeter Tyser #include <asm/microblaze_intc.h> 136260fb04SPeter Tyser 14*9aa65cabSMichal Simek DECLARE_GLOBAL_DATA_PTR; 15*9aa65cabSMichal Simek 166260fb04SPeter Tyser volatile int timestamp = 0; 17bcbb046bSMichal Simek microblaze_timer_t *tmr; 186260fb04SPeter Tyser 196260fb04SPeter Tyser ulong get_timer (ulong base) 206260fb04SPeter Tyser { 21bcbb046bSMichal Simek if (tmr) 22bcbb046bSMichal Simek return timestamp - base; 23bcbb046bSMichal Simek return timestamp++ - base; 246260fb04SPeter Tyser } 256260fb04SPeter Tyser 26779bf42cSMichal Simek void __udelay(unsigned long usec) 27779bf42cSMichal Simek { 28bcbb046bSMichal Simek u32 i; 29779bf42cSMichal Simek 30bcbb046bSMichal Simek if (tmr) { 31779bf42cSMichal Simek i = get_timer(0); 32779bf42cSMichal Simek while ((get_timer(0) - i) < (usec / 1000)) 33779bf42cSMichal Simek ; 34bcbb046bSMichal Simek } else { 35*9aa65cabSMichal Simek #ifndef CONFIG_OF_CONTROL 36bcbb046bSMichal Simek for (i = 0; i < (usec * XILINX_CLOCK_FREQ / 10000000); i++) 37779bf42cSMichal Simek ; 38*9aa65cabSMichal Simek #endif 39779bf42cSMichal Simek } 40bcbb046bSMichal Simek } 41779bf42cSMichal Simek 429d242745SMichal Simek #ifndef CONFIG_SPL_BUILD 43bcbb046bSMichal Simek static void timer_isr(void *arg) 446260fb04SPeter Tyser { 456260fb04SPeter Tyser timestamp++; 466260fb04SPeter Tyser tmr->control = tmr->control | TIMER_INTERRUPT; 476260fb04SPeter Tyser } 486260fb04SPeter Tyser 495bbcb6cfSMichal Simek int timer_init (void) 506260fb04SPeter Tyser { 51bcbb046bSMichal Simek int irq = -1; 52bcbb046bSMichal Simek u32 preload = 0; 53bcbb046bSMichal Simek u32 ret = 0; 54bcbb046bSMichal Simek 55*9aa65cabSMichal Simek #ifdef CONFIG_OF_CONTROL 56*9aa65cabSMichal Simek const void *blob = gd->fdt_blob; 57*9aa65cabSMichal Simek int node = 0; 58*9aa65cabSMichal Simek u32 cell[2]; 59*9aa65cabSMichal Simek 60*9aa65cabSMichal Simek debug("TIMER: Initialization\n"); 61*9aa65cabSMichal Simek 62*9aa65cabSMichal Simek node = fdt_node_offset_by_compatible(blob, node, 63*9aa65cabSMichal Simek "xlnx,xps-timer-1.00.a"); 64*9aa65cabSMichal Simek if (node != -1) { 65*9aa65cabSMichal Simek fdt_addr_t base = fdtdec_get_addr(blob, node, "reg"); 66*9aa65cabSMichal Simek if (base == FDT_ADDR_T_NONE) 67*9aa65cabSMichal Simek return -1; 68*9aa65cabSMichal Simek 69*9aa65cabSMichal Simek debug("TIMER: Base addr %lx\n", base); 70*9aa65cabSMichal Simek tmr = (microblaze_timer_t *)base; 71*9aa65cabSMichal Simek 72*9aa65cabSMichal Simek ret = fdtdec_get_int_array(blob, node, "interrupts", 73*9aa65cabSMichal Simek cell, ARRAY_SIZE(cell)); 74*9aa65cabSMichal Simek if (ret) 75*9aa65cabSMichal Simek return ret; 76*9aa65cabSMichal Simek 77*9aa65cabSMichal Simek irq = cell[0]; 78*9aa65cabSMichal Simek debug("TIMER: IRQ %x\n", irq); 79*9aa65cabSMichal Simek 80*9aa65cabSMichal Simek preload = fdtdec_get_int(blob, node, "clock-frequency", 0); 81*9aa65cabSMichal Simek preload /= CONFIG_SYS_HZ; 82*9aa65cabSMichal Simek } else { 83*9aa65cabSMichal Simek return node; 84*9aa65cabSMichal Simek } 85*9aa65cabSMichal Simek 86*9aa65cabSMichal Simek #else 87bcbb046bSMichal Simek #if defined(CONFIG_SYS_TIMER_0_ADDR) && defined(CONFIG_SYS_INTC_0_NUM) 88bcbb046bSMichal Simek preload = XILINX_CLOCK_FREQ / CONFIG_SYS_HZ; 89bcbb046bSMichal Simek irq = CONFIG_SYS_TIMER_0_IRQ; 90bcbb046bSMichal Simek tmr = (microblaze_timer_t *) (CONFIG_SYS_TIMER_0_ADDR); 91bcbb046bSMichal Simek #endif 92*9aa65cabSMichal Simek #endif 93bcbb046bSMichal Simek if (tmr && preload && irq >= 0) { 94bcbb046bSMichal Simek tmr->loadreg = preload; 956260fb04SPeter Tyser tmr->control = TIMER_INTERRUPT | TIMER_RESET; 96bcbb046bSMichal Simek tmr->control = TIMER_ENABLE | TIMER_ENABLE_INTR |\ 97bcbb046bSMichal Simek TIMER_RELOAD | TIMER_DOWN_COUNT; 984769be21SGraeme Russ timestamp = 0; 99bcbb046bSMichal Simek ret = install_interrupt_handler (irq, timer_isr, (void *)tmr); 100bcbb046bSMichal Simek if (ret) 101bcbb046bSMichal Simek tmr = NULL; 102bcbb046bSMichal Simek } 103bcbb046bSMichal Simek /* No problem if timer is not found/initialized */ 1045bbcb6cfSMichal Simek return 0; 1056260fb04SPeter Tyser } 1069d242745SMichal Simek #else 1079d242745SMichal Simek int timer_init(void) 1089d242745SMichal Simek { 1099d242745SMichal Simek return 0; 1109d242745SMichal Simek } 1119d242745SMichal Simek #endif 112b9f0b730SStephan Linz 113b9f0b730SStephan Linz /* 114b9f0b730SStephan Linz * This function is derived from PowerPC code (read timebase as long long). 115b9f0b730SStephan Linz * On Microblaze it just returns the timer value. 116b9f0b730SStephan Linz */ 117b9f0b730SStephan Linz unsigned long long get_ticks(void) 118b9f0b730SStephan Linz { 119b9f0b730SStephan Linz return get_timer(0); 120b9f0b730SStephan Linz } 121b9f0b730SStephan Linz 122b9f0b730SStephan Linz /* 123b9f0b730SStephan Linz * This function is derived from PowerPC code (timebase clock frequency). 124b9f0b730SStephan Linz * On Microblaze it returns the number of timer ticks per second. 125b9f0b730SStephan Linz */ 126b9f0b730SStephan Linz ulong get_tbclk(void) 127b9f0b730SStephan Linz { 128b9f0b730SStephan Linz return CONFIG_SYS_HZ; 129b9f0b730SStephan Linz } 130