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