/* Timer/counter utilities Copyright (C) 1996 David S. Miller 1996 Jakub Jelinek This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "silo.h" struct sun4m_timer_regs { volatile unsigned int limit; volatile unsigned int count; volatile unsigned int noclear; volatile unsigned int foobar; volatile unsigned int cfg; }; struct sun4c_timer_info { volatile unsigned int count10; volatile unsigned int limit10; volatile unsigned int count14; volatile unsigned int limit14; }; static volatile struct sun4m_timer_regs *sun4m_timer; static volatile struct sun4c_timer_info *sun4c_timer; static unsigned char *addr_to_free = 0; static int len_to_free; #define TICKER_VIRTUAL 0xfc000000 #define SUN4C_TIMER_PHYSADDR 0xf3000000 static int sun4m_init_timer (struct linux_romvec *promvec) { int reg_count; struct linux_prom_registers cnt_regs[PROMREG_MAX]; int obio_node, cnt_node; extern int num_obio_ranges; cnt_node = 0; if ((obio_node = prom_searchsiblings (prom_getchild (prom_root_node), "obio")) == 0 || (obio_node = prom_getchild (obio_node)) == 0 || (cnt_node = prom_searchsiblings (obio_node, "counter")) == 0) { return -1; } reg_count = prom_getproperty (cnt_node, "reg", (void *) cnt_regs, sizeof (cnt_regs)); reg_count = (reg_count / sizeof (struct linux_prom_registers)); prom_apply_obio_ranges (cnt_regs, reg_count); sun4m_timer = (struct sun4m_timer_regs *)(*promvec->pv_v2devops.v2_dumb_mmap)((char *)TICKER_VIRTUAL, cnt_regs[reg_count-1].which_io, (unsigned)cnt_regs[reg_count-1].phys_addr, cnt_regs[reg_count-1].reg_size); if (!sun4m_timer) return -1; sun4m_timer->limit = 0x7FFFFFFF; addr_to_free = (unsigned char *)sun4m_timer; len_to_free = cnt_regs[reg_count-1].reg_size; return 0; } static int sun4c_init_timer (struct linux_romvec *promvec) { if (promvec->pv_romvers >= 2) { sun4c_timer = (struct sun4c_timer_info *)(*promvec->pv_v2devops.v2_dumb_mmap)((char *)TICKER_VIRTUAL, 0, SUN4C_TIMER_PHYSADDR, sizeof (struct sun4c_timer_info)); if (!sun4c_timer) return -1; addr_to_free = (unsigned char *)sun4c_timer; len_to_free = sizeof (struct sun4c_timer_info); } else { sun4c_timer = (struct sun4c_timer_info *)TICKER_VIRTUAL; if (sun4c_mapio (SUN4C_TIMER_PHYSADDR, TICKER_VIRTUAL, 0) < 0) return -1; addr_to_free = (unsigned char *)0xffffffff; } sun4c_timer->limit10 = 0x7FFFFFFF; return 0; } int init_timer (struct linux_romvec *promvec) { int retval; switch (architecture) { case sun4c: retval = sun4c_init_timer (promvec); break; case sun4m: retval = sun4m_init_timer (promvec); break; default: retval = -1; break; } if (retval < 0) { addr_to_free = 0; return -1; } return 0; } void close_timer (struct linux_romvec *promvec) { if (addr_to_free) { if (addr_to_free == (unsigned char *)0xffffffff) sun4c_unmapio (TICKER_VIRTUAL); else (*promvec->pv_v2devops.v2_dumb_munmap)(addr_to_free, len_to_free); addr_to_free = 0; } } static long long ticks = 0; void reset_ticks (void) { ticks = 0; } /* 10ms ticks */ int get_ticks (void) { unsigned int i; static unsigned int lasti = 0; switch (architecture) { case sun4c: i = sun4c_timer->count10; break; case sun4m: i = sun4m_timer->count; break; default: return 0; } i >>= 9; if (i >= lasti) ticks += i - lasti; else ticks += (1 << 22) + i - lasti - 1; lasti = i; return (int)(ticks / 20000); }