mmtimer.c revision 613655fa39ff6957754fa8ceb8559980920eb8ee
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 276832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich * Timer device implementation for SGI SN platforms. 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This file is subject to the terms and conditions of the GNU General Public 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * License. See the file "COPYING" in the main directory of this archive 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for more details. 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 876832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich * Copyright (c) 2001-2006 Silicon Graphics, Inc. All rights reserved. 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This driver exports an API that should be supportable by any HPET or IA-PC 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * multimedia timer. The code below is currently specific to the SGI Altix 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * SHub RTC, however. 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 11/01/01 - jbarnes - initial revision 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 9/10/04 - Christoph Lameter - remove interrupt support for kernel inclusion 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 10/1/04 - Christoph Lameter - provide posix clock CLOCK_SGI_CYCLE 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 10/13/04 - Christoph Lameter, Dimitri Sivanich - provide timer interrupt 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * support via the posix timer interface 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioctl.h> 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h> 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mm.h> 284e950f6f0189f65f8bf069cf2272649ef418f5e4Alexey Dobriyan#include <linux/fs.h> 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mmtimer.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/posix-timers.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h> 33f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel#include <linux/time.h> 34f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel#include <linux/math64.h> 35613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann#include <linux/mutex.h> 365a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sn/addrs.h> 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sn/intr.h> 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sn/shub_mmr.h> 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sn/nodepda.h> 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/sn/shubio.h> 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Jesse Barnes <jbarnes@sgi.com>"); 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("SGI Altix RTC Timer"); 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* name of the device, usually in /dev */ 501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MMTIMER_NAME "mmtimer" 511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MMTIMER_DESC "SGI Altix RTC Timer" 5276832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich#define MMTIMER_VERSION "2.1" 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_BITS 55 /* 55 bits for this implementation */ 551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsextern unsigned long sn_rtc_cycles_per_second; 571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_COUNTER_ADDR ((long *)LOCAL_MMR_ADDR(SH_RTC)) 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define rtc_time() (*RTC_COUNTER_ADDR) 611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 62613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmannstatic DEFINE_MUTEX(mmtimer_mutex); 634cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Coxstatic long mmtimer_ioctl(struct file *file, unsigned int cmd, 644cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox unsigned long arg); 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mmtimer_mmap(struct file *file, struct vm_area_struct *vma); 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Period in femtoseconds (10^-15 s) 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long mmtimer_femtoperiod = 0; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7262322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations mmtimer_fops = { 734cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox .owner = THIS_MODULE, 744cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox .mmap = mmtimer_mmap, 754cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox .unlocked_ioctl = mmtimer_ioctl, 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We only have comparison registers RTC1-4 currently available per 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * node. RTC0 is used by SAL. 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Check for an RTC interrupt pending */ 83cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic int mmtimer_int_pending(int comparator) 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) & 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator) 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 91cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Clear the RTC interrupt pending bit */ 93cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_clr_int_pending(int comparator) 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS), 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator); 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Setup timer on comparator RTC1 */ 100cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_setup_int_0(int cpu, u64 expires) 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u64 val; 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable interrupt */ 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 0UL); 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Initialize comparator value */ 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), -1L); 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Clear pending bit */ 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_clr_int_pending(0); 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC1_INT_CONFIG_IDX_SHFT) | 114cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich ((u64)cpu_physical_id(cpu) << 1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds SH_RTC1_INT_CONFIG_PID_SHFT); 1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set configuration */ 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_CONFIG), val); 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Enable RTC interrupts */ 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 1UL); 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Initialize comparator value */ 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), expires); 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Setup timer on comparator RTC2 */ 130cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_setup_int_1(int cpu, u64 expires) 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u64 val; 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 0UL); 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), -1L); 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_clr_int_pending(1); 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC2_INT_CONFIG_IDX_SHFT) | 141cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich ((u64)cpu_physical_id(cpu) << 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds SH_RTC2_INT_CONFIG_PID_SHFT); 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_CONFIG), val); 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 1UL); 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), expires); 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Setup timer on comparator RTC3 */ 152cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_setup_int_2(int cpu, u64 expires) 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u64 val; 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 0UL); 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), -1L); 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_clr_int_pending(2); 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC3_INT_CONFIG_IDX_SHFT) | 163cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich ((u64)cpu_physical_id(cpu) << 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds SH_RTC3_INT_CONFIG_PID_SHFT); 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_CONFIG), val); 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 1UL); 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), expires); 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function must be called with interrupts disabled and preemption off 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in order to insure that the setup succeeds in a deterministic time frame. 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * It will check if the interrupt setup succeeded. 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 178cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic int mmtimer_setup(int cpu, int comparator, unsigned long expires) 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (comparator) { 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 183cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_setup_int_0(cpu, expires); 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 1: 186cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_setup_int_1(cpu, expires); 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 2: 189cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_setup_int_2(cpu, expires); 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* We might've missed our expiration time */ 193cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (rtc_time() <= expires) 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 1; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * If an interrupt is already pending then its okay 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * if not then we failed 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return mmtimer_int_pending(comparator); 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 203cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic int mmtimer_disable_int(long nasid, int comparator) 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (comparator) { 2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 0: 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0UL) : REMOTE_HUB_S(nasid, SH_RTC1_INT_ENABLE, 0UL); 2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 1: 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0UL) : REMOTE_HUB_S(nasid, SH_RTC2_INT_ENABLE, 0UL); 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case 2: 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 0UL) : REMOTE_HUB_S(nasid, SH_RTC3_INT_ENABLE, 0UL); 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EFAULT; 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 224cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich#define COMPARATOR 1 /* The comparator to use */ 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 226cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich#define TIMER_OFF 0xbadcabLL /* Timer is not setup */ 227cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich#define TIMER_SET 0 /* Comparator is set for this timer */ 228cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 229cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich/* There is one of these for each timer */ 230cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstruct mmtimer { 231cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node list; 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct k_itimer *timer; 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int cpu; 234cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich}; 235cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 236cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstruct mmtimer_node { 237cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spinlock_t lock ____cacheline_aligned; 238cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_root timer_head; 239cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node *next; 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct tasklet_struct tasklet; 241cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich}; 242cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic struct mmtimer_node *timers; 243cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 244cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 245cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich/* 246cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * Add a new mmtimer struct to the node's mmtimer list. 247cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * This function assumes the struct mmtimer_node is locked. 248cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich */ 249cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_add_list(struct mmtimer *n) 250cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich{ 251cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich int nodeid = n->timer->it.mmtimer.node; 252cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich unsigned long expires = n->timer->it.mmtimer.expires; 253cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node **link = &timers[nodeid].timer_head.rb_node; 254cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node *parent = NULL; 255cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer *x; 256cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 257cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* 258cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * Find the right place in the rbtree: 259cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich */ 260cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich while (*link) { 261cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich parent = *link; 262cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich x = rb_entry(parent, struct mmtimer, list); 263cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 264cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (expires < x->timer->it.mmtimer.expires) 265cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich link = &(*link)->rb_left; 266cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich else 267cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich link = &(*link)->rb_right; 268cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 269cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 270cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* 271cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * Insert the timer to the rbtree and check whether it 272cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * replaces the first pending timer 273cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich */ 274cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich rb_link_node(&n->list, parent, link); 275cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich rb_insert_color(&n->list, &timers[nodeid].timer_head); 276cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 277cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (!timers[nodeid].next || expires < rb_entry(timers[nodeid].next, 278cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer, list)->timer->it.mmtimer.expires) 279cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich timers[nodeid].next = &n->list; 280cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich} 281cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 282cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich/* 283cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * Set the comparator for the next timer. 284cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * This function assumes the struct mmtimer_node is locked. 285cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich */ 286cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_set_next_timer(int nodeid) 287cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich{ 288cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer_node *n = &timers[nodeid]; 289cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer *x; 290cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct k_itimer *t; 291cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich int o; 292cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 293cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichrestart: 294cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (n->next == NULL) 295cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich return; 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 297cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich x = rb_entry(n->next, struct mmtimer, list); 298cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t = x->timer; 299cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (!t->it.mmtimer.incr) { 300cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Not an interval timer */ 301cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (!mmtimer_setup(x->cpu, COMPARATOR, 302cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it.mmtimer.expires)) { 303cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Late setup, fire now */ 304cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich tasklet_schedule(&n->tasklet); 305cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 306cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich return; 307cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 308cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 309cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Interval timer */ 310cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich o = 0; 311cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich while (!mmtimer_setup(x->cpu, COMPARATOR, t->it.mmtimer.expires)) { 312cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich unsigned long e, e1; 313cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node *next; 314cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it.mmtimer.expires += t->it.mmtimer.incr << o; 315cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it_overrun += 1 << o; 316cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich o++; 317cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (o > 20) { 318cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich printk(KERN_ALERT "mmtimer: cannot reschedule timer\n"); 319cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it.mmtimer.clock = TIMER_OFF; 320cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich n->next = rb_next(&x->list); 321cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich rb_erase(&x->list, &n->timer_head); 322cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich kfree(x); 323cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich goto restart; 324cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 325cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 326cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich e = t->it.mmtimer.expires; 327cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich next = rb_next(&x->list); 328cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 329cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (next == NULL) 330cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich continue; 331cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 332cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich e1 = rb_entry(next, struct mmtimer, list)-> 333cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich timer->it.mmtimer.expires; 334cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (e > e1) { 335cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich n->next = next; 336cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich rb_erase(&x->list, &n->timer_head); 337cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_add_list(x); 338cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich goto restart; 339cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 340cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 341cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich} 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/** 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_ioctl - ioctl interface for /dev/mmtimer 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @file: file structure for the device 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @cmd: command to execute 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @arg: optional argument to command 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Executes the command specified by @cmd. Returns 0 for success, < 0 for 3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * failure. 3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Valid commands: 3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETOFFSET - Should return the offset (relative to the start 3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the page where the registers are mapped) for the counter in question. 3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15) 3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * seconds 3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address 3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * specified by @arg 3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter 3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_MMAPAVAIL - Returns 1 if the registers can be mmap'd into userspace 3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it 3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the address specified by @arg. 3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3704cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Coxstatic long mmtimer_ioctl(struct file *file, unsigned int cmd, 3714cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox unsigned long arg) 3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int ret = 0; 3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 375613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann mutex_lock(&mmtimer_mutex); 3764cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox 3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds switch (cmd) { 3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case MMTIMER_GETOFFSET: /* offset of the counter */ 3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * SN RTC registers are on their own 64k page 3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(PAGE_SIZE <= (1 << 16)) 3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = (((long)RTC_COUNTER_ADDR) & (PAGE_SIZE-1)) / 8; 3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = -ENOSYS; 3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */ 3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(copy_to_user((unsigned long __user *)arg, 3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds &mmtimer_femtoperiod, sizeof(unsigned long))) 3914cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox ret = -EFAULT; 3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case MMTIMER_GETFREQ: /* frequency in Hz */ 3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(copy_to_user((unsigned long __user *)arg, 3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds &sn_rtc_cycles_per_second, 3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sizeof(unsigned long))) 3984cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox ret = -EFAULT; 3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case MMTIMER_GETBITS: /* number of bits in the clock */ 4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = RTC_BITS; 4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */ 4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0; 4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds case MMTIMER_GETCOUNTER: 4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(copy_to_user((unsigned long __user *)arg, 4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds RTC_COUNTER_ADDR, sizeof(unsigned long))) 4124cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox ret = -EFAULT; 4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds default: 4154cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox ret = -ENOTTY; 4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 418613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann mutex_unlock(&mmtimer_mutex); 4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/** 4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_mmap - maps the clock's registers into userspace 4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @file: file structure for the device 4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @vma: VMA to map the registers into 4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Calls remap_pfn_range() to map the clock's registers into 4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the calling process' address space. 4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mmtimer_mmap(struct file *file, struct vm_area_struct *vma) 4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long mmtimer_addr; 4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (vma->vm_end - vma->vm_start != PAGE_SIZE) 4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (vma->vm_flags & VM_WRITE) 4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EPERM; 4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (PAGE_SIZE > (1 << 16)) 4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENOSYS; 4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_addr = __pa(RTC_COUNTER_ADDR); 4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_addr &= ~(PAGE_SIZE - 1); 4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_addr &= 0xfffffffffffffffUL; 4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (remap_pfn_range(vma, vma->vm_start, mmtimer_addr >> PAGE_SHIFT, 4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds PAGE_SIZE, vma->vm_page_prot)) { 4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "remap_pfn_range failed in mmtimer.c\n"); 4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EAGAIN; 4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice mmtimer_miscdev = { 4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds SGI_MMTIMER, 4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MMTIMER_NAME, 4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds &mmtimer_fops 4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct timespec sgi_clock_offset; 4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_period; 4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Posix Timer Interface 4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct timespec sgi_clock_offset; 4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_period; 4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_get(clockid_t clockid, struct timespec *tp) 4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u64 nsec; 4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nsec = rtc_time() * sgi_clock_period 4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds + sgi_clock_offset.tv_nsec; 480f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel *tp = ns_to_timespec(nsec); 481f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel tp->tv_sec += sgi_clock_offset.tv_sec; 4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_set(clockid_t clockid, struct timespec *tp) 4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u64 nsec; 489f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel u32 rem; 4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds nsec = rtc_time() * sgi_clock_period; 4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 493f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel sgi_clock_offset.tv_sec = tp->tv_sec - div_u64_rem(nsec, NSEC_PER_SEC, &rem); 4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (rem <= tp->tv_nsec) 4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sgi_clock_offset.tv_nsec = tp->tv_sec - rem; 4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else { 4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sgi_clock_offset.tv_nsec = tp->tv_sec + NSEC_PER_SEC - rem; 4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sgi_clock_offset.tv_sec--; 5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/** 5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_interrupt - timer interrupt handler 5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @irq: irq received 5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @dev_id: device the irq came from 5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Called when one of the comarators matches the counter, This 5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * routine will send signals to processes that have requested 5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * them. 5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This interrupt is run in an interrupt context 5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * by the SHUB. It is therefore safe to locally access SHub 5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * registers. 5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic irqreturn_t 5187d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsmmtimer_interrupt(int irq, void *dev_id) 5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long expires = 0; 5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int result = IRQ_NONE; 52276832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich unsigned indx = cpu_to_node(smp_processor_id()); 523cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer *base; 5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 525cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_lock(&timers[indx].lock); 526cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich base = rb_entry(timers[indx].next, struct mmtimer, list); 527cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (base == NULL) { 528cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock(&timers[indx].lock); 529cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich return result; 530cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 531cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 532cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (base->cpu == smp_processor_id()) { 533cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (base->timer) 534cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich expires = base->timer->it.mmtimer.expires; 535cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* expires test won't work with shared irqs */ 536cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if ((mmtimer_int_pending(COMPARATOR) > 0) || 537cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich (expires && (expires <= rtc_time()))) { 538cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_clr_int_pending(COMPARATOR); 539cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich tasklet_schedule(&timers[indx].tasklet); 540cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich result = IRQ_HANDLED; 5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 543cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock(&timers[indx].lock); 5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return result; 5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 547cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_tasklet(unsigned long data) 548cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich{ 549cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich int nodeid = data; 550cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer_node *mn = &timers[nodeid]; 5510fbcae222b8aa3a47034a484e02e7fc14050c783Julia Lawall struct mmtimer *x; 552cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct k_itimer *t; 5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long flags; 5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Send signal and deal with periodic signals */ 556cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_lock_irqsave(&mn->lock, flags); 557cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (!mn->next) 5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 560cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich x = rb_entry(mn->next, struct mmtimer, list); 561cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t = x->timer; 562cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 563cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (t->it.mmtimer.clock == TIMER_OFF) 564cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich goto out; 565cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 566cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it_overrun = 0; 5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 568cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mn->next = rb_next(&x->list); 569cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich rb_erase(&x->list, &mn->timer_head); 5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 571cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (posix_timer_event(t, 0) != 0) 5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t->it_overrun++; 573cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if(t->it.mmtimer.incr) { 575cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it.mmtimer.expires += t->it.mmtimer.incr; 576cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_add_list(x); 5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } else { 5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Ensure we don't false trigger in mmtimer_interrupt */ 579cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t->it.mmtimer.clock = TIMER_OFF; 5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t->it.mmtimer.expires = 0; 581cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich kfree(x); 5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 583cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Set comparator for next timer, if there is one */ 584cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_set_next_timer(nodeid); 585cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds t->it_overrun_last = t->it_overrun; 5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout: 588cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock_irqrestore(&mn->lock, flags); 5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_timer_create(struct k_itimer *timer) 5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Insure that a newly created timer is off */ 5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timer->it.mmtimer.clock = TIMER_OFF; 5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This does not really delete a timer. It just insures 5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that the timer is not active 6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Assumption: it_lock is already held with irq's disabled 6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_timer_del(struct k_itimer *timr) 6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cnodeid_t nodeid = timr->it.mmtimer.node; 6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long irqflags; 6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 608cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_lock_irqsave(&timers[nodeid].lock, irqflags); 609cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (timr->it.mmtimer.clock != TIMER_OFF) { 610cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich unsigned long expires = timr->it.mmtimer.expires; 611cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node *n = timers[nodeid].timer_head.rb_node; 612cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer *uninitialized_var(t); 613cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich int r = 0; 614cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timr->it.mmtimer.clock = TIMER_OFF; 6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timr->it.mmtimer.expires = 0; 617cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 618cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich while (n) { 619cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich t = rb_entry(n, struct mmtimer, list); 620cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (t->timer == timr) 621cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich break; 622cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 623cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (expires < t->timer->it.mmtimer.expires) 624cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich n = n->rb_left; 625cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich else 626cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich n = n->rb_right; 627cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 628cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 629cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (!n) { 630cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); 631cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich return 0; 632cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 633cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 634cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (timers[nodeid].next == n) { 635cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich timers[nodeid].next = rb_next(n); 636cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich r = 1; 637cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 638cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 639cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich rb_erase(n, &timers[nodeid].timer_head); 640cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich kfree(t); 641cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 642cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (r) { 643cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_disable_int(cnodeid_to_nasid(nodeid), 644cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich COMPARATOR); 645cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_set_next_timer(nodeid); 646cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich } 6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 648cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); 6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Assumption: it_lock is already held with irq's disabled */ 6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) 6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (timr->it.mmtimer.clock == TIMER_OFF) { 6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_setting->it_interval.tv_nsec = 0; 6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_setting->it_interval.tv_sec = 0; 6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_setting->it_value.tv_nsec = 0; 6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cur_setting->it_value.tv_sec =0; 6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 664f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel cur_setting->it_interval = ns_to_timespec(timr->it.mmtimer.incr * sgi_clock_period); 665f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel cur_setting->it_value = ns_to_timespec((timr->it.mmtimer.expires - rtc_time()) * sgi_clock_period); 6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_timer_set(struct k_itimer *timr, int flags, 6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct itimerspec * new_setting, 6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct itimerspec * old_setting) 6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long when, period, irqflags; 6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err = 0; 6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cnodeid_t nodeid; 676cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct mmtimer *base; 677cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich struct rb_node *n; 6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (old_setting) 6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sgi_timer_get(timr, old_setting); 6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sgi_timer_del(timr); 683f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel when = timespec_to_ns(&new_setting->it_value); 684f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel period = timespec_to_ns(&new_setting->it_interval); 6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (when == 0) 6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Clear timer */ 6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 690cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich base = kmalloc(sizeof(struct mmtimer), GFP_KERNEL); 691cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (base == NULL) 692cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich return -ENOMEM; 693cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (flags & TIMER_ABSTIME) { 6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct timespec n; 6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long now; 6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds getnstimeofday(&n); 699f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel now = timespec_to_ns(&n); 7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (when > now) 7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds when -= now; 7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else 7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Fire the timer immediately */ 7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds when = 0; 7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Convert to sgi clock period. Need to keep rtc_time() as near as possible 7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to getnstimeofday() in order to be as faithful as possible to the time 7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * specified. 7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds when = (when + sgi_clock_period - 1) / sgi_clock_period + rtc_time(); 7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds period = (period + sgi_clock_period - 1) / sgi_clock_period; 7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We are allocating a local SHub comparator. If we would be moved to another 7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cpu then another SHub may be local to us. Prohibit that by switching off 7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * preemption. 7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds preempt_disable(); 7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 72255642d36cd0f626c614f6dfa1151a5af1ff84f36Tony Luck nodeid = cpu_to_node(smp_processor_id()); 7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 724cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Lock the node timer structure */ 725cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_lock_irqsave(&timers[nodeid].lock, irqflags); 7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 72776832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich base->timer = timr; 72876832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich base->cpu = smp_processor_id(); 7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 730cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich timr->it.mmtimer.clock = TIMER_SET; 7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timr->it.mmtimer.node = nodeid; 7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timr->it.mmtimer.incr = period; 7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds timr->it.mmtimer.expires = when; 7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 735cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich n = timers[nodeid].next; 736cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 737cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Add the new struct mmtimer to node's timer list */ 738cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_add_list(base); 739cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 740cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (timers[nodeid].next == n) { 741cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* No need to reprogram comparator for now */ 742cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); 743cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich preempt_enable(); 744cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich return err; 7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 747cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* We need to reprogram the comparator */ 748cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich if (n) 749cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_disable_int(cnodeid_to_nasid(nodeid), COMPARATOR); 750cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 751cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich mmtimer_set_next_timer(nodeid); 752cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich 753cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Unlock the node timer structure */ 754cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_unlock_irqrestore(&timers[nodeid].lock, irqflags); 7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds preempt_enable(); 7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return err; 7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct k_clock sgi_clock = { 7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .res = 0, 7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .clock_set = sgi_clock_set, 7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .clock_get = sgi_clock_get, 7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .timer_create = sgi_timer_create, 7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .nsleep = do_posix_clock_nonanosleep, 7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .timer_set = sgi_timer_set, 7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .timer_del = sgi_timer_del, 7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .timer_get = sgi_timer_get 7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/** 7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_init - device initialization routine 7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Does initial setup for the mmtimer device. 7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init mmtimer_init(void) 7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 77976832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich cnodeid_t node, maxn = -1; 7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!ia64_platform_is("sn2")) 782f032f90809ebbbd28feb90f97add2e0a869a42edBjorn Helgaas return 0; 7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Sanity check the cycles/sec variable 7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (sn_rtc_cycles_per_second < 100000) { 7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "%s: unable to determine clock frequency\n", 7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MMTIMER_NAME); 7905d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman goto out1; 7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second / 7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2) / sn_rtc_cycles_per_second; 7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 7960f2ed4c6bae23d2b7ef0ea2d272377e3de700c0cThomas Gleixner if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) { 7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING "%s: unable to allocate interrupt.", 7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MMTIMER_NAME); 7995d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman goto out1; 8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (misc_register(&mmtimer_miscdev)) { 8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR "%s: failed to register device\n", 8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds MMTIMER_NAME); 8055d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman goto out2; 8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 80876832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich /* Get max numbered node, calculate slots needed */ 80976832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich for_each_online_node(node) { 81076832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich maxn = node; 81176832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich } 81276832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich maxn++; 81376832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich 81476832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich /* Allocate list of node ptrs to mmtimer_t's */ 815cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL); 81676832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich if (timers == NULL) { 81776832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich printk(KERN_ERR "%s: failed to allocate memory for device\n", 81876832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich MMTIMER_NAME); 8195d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman goto out3; 82076832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich } 82176832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich 822cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich /* Initialize struct mmtimer's for each online node */ 82376832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich for_each_online_node(node) { 824cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich spin_lock_init(&timers[node].lock); 825cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich tasklet_init(&timers[node].tasklet, mmtimer_tasklet, 826cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich (unsigned long) node); 82776832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich } 82876832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich 8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sgi_clock_period = sgi_clock.res = NSEC_PER_SEC / sn_rtc_cycles_per_second; 8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds register_posix_clock(CLOCK_SGI_CYCLE, &sgi_clock); 8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION, 8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds sn_rtc_cycles_per_second/(unsigned long)1E6); 8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 8365d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman 8375d469ec0f40d65b2a0a704402990a43b2dafe197Neil Hormanout3: 838cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich kfree(timers); 8395d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman misc_deregister(&mmtimer_miscdev); 8405d469ec0f40d65b2a0a704402990a43b2dafe197Neil Hormanout2: 8415d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman free_irq(SGI_MMTIMER_VECTOR, NULL); 8425d469ec0f40d65b2a0a704402990a43b2dafe197Neil Hormanout1: 8435d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman return -1; 8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(mmtimer_init); 847