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
56e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixnerstatic struct k_clock sgi_clock;
57e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsextern unsigned long sn_rtc_cycles_per_second;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RTC_COUNTER_ADDR        ((long *)LOCAL_MMR_ADDR(SH_RTC))
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define rtc_time()              (*RTC_COUNTER_ADDR)
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
64613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmannstatic DEFINE_MUTEX(mmtimer_mutex);
654cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Coxstatic long mmtimer_ioctl(struct file *file, unsigned int cmd,
664cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox						unsigned long arg);
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mmtimer_mmap(struct file *file, struct vm_area_struct *vma);
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Period in femtoseconds (10^-15 s)
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned long mmtimer_femtoperiod = 0;
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7462322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations mmtimer_fops = {
754cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox	.owner = THIS_MODULE,
764cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox	.mmap =	mmtimer_mmap,
774cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox	.unlocked_ioctl = mmtimer_ioctl,
786038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek = noop_llseek,
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * We only have comparison registers RTC1-4 currently available per
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * node.  RTC0 is used by SAL.
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Check for an RTC interrupt pending */
86cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic int mmtimer_int_pending(int comparator)
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (HUB_L((unsigned long *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)) &
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator)
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
94cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Clear the RTC interrupt pending bit */
96cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_clr_int_pending(int comparator)
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS),
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		SH_EVENT_OCCURRED_RTC1_INT_MASK << comparator);
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Setup timer on comparator RTC1 */
103cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_setup_int_0(int cpu, u64 expires)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u64 val;
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Disable interrupt */
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 0UL);
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize comparator value */
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), -1L);
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Clear pending bit */
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_clr_int_pending(0);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC1_INT_CONFIG_IDX_SHFT) |
117cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		((u64)cpu_physical_id(cpu) <<
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SH_RTC1_INT_CONFIG_PID_SHFT);
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set configuration */
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_CONFIG), val);
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Enable RTC interrupts */
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE), 1UL);
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize comparator value */
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPB), expires);
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Setup timer on comparator RTC2 */
133cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_setup_int_1(int cpu, u64 expires)
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u64 val;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 0UL);
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), -1L);
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_clr_int_pending(1);
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC2_INT_CONFIG_IDX_SHFT) |
144cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		((u64)cpu_physical_id(cpu) <<
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SH_RTC2_INT_CONFIG_PID_SHFT);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_CONFIG), val);
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE), 1UL);
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPC), expires);
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Setup timer on comparator RTC3 */
155cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_setup_int_2(int cpu, u64 expires)
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u64 val;
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 0UL);
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), -1L);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_clr_int_pending(2);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	val = ((u64)SGI_MMTIMER_VECTOR << SH_RTC3_INT_CONFIG_IDX_SHFT) |
166cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		((u64)cpu_physical_id(cpu) <<
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			SH_RTC3_INT_CONFIG_PID_SHFT);
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_CONFIG), val);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE), 1UL);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	HUB_S((u64 *)LOCAL_MMR_ADDR(SH_INT_CMPD), expires);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function must be called with interrupts disabled and preemption off
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in order to insure that the setup succeeds in a deterministic time frame.
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * It will check if the interrupt setup succeeded.
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
181dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanichstatic int mmtimer_setup(int cpu, int comparator, unsigned long expires,
182dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	u64 *set_completion_time)
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (comparator) {
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
186cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		mmtimer_setup_int_0(cpu, expires);
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
189cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		mmtimer_setup_int_1(cpu, expires);
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 2:
192cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		mmtimer_setup_int_2(cpu, expires);
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* We might've missed our expiration time */
196dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	*set_completion_time = rtc_time();
197dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	if (*set_completion_time <= expires)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 1;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * If an interrupt is already pending then its okay
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * if not then we failed
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return mmtimer_int_pending(comparator);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
207cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic int mmtimer_disable_int(long nasid, int comparator)
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (comparator) {
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 0:
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC1_INT_ENABLE),
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			0UL) : REMOTE_HUB_S(nasid, SH_RTC1_INT_ENABLE, 0UL);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 1:
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC2_INT_ENABLE),
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			0UL) : REMOTE_HUB_S(nasid, SH_RTC2_INT_ENABLE, 0UL);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case 2:
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		nasid == -1 ? HUB_S((u64 *)LOCAL_MMR_ADDR(SH_RTC3_INT_ENABLE),
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			0UL) : REMOTE_HUB_S(nasid, SH_RTC3_INT_ENABLE, 0UL);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EFAULT;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
228cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich#define COMPARATOR	1		/* The comparator to use */
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
230cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich#define TIMER_OFF	0xbadcabLL	/* Timer is not setup */
231cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich#define TIMER_SET	0		/* Comparator is set for this timer */
232cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
233dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich#define MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT 40
234dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich
235cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich/* There is one of these for each timer */
236cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstruct mmtimer {
237cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct rb_node list;
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct k_itimer *timer;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int cpu;
240cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich};
241cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
242cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstruct mmtimer_node {
243cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spinlock_t lock ____cacheline_aligned;
244cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct rb_root timer_head;
245cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct rb_node *next;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct tasklet_struct tasklet;
247cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich};
248cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic struct mmtimer_node *timers;
249cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
250dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanichstatic unsigned mmtimer_interval_retry_increment =
251dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	MMTIMER_INTERVAL_RETRY_INCREMENT_DEFAULT;
252dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanichmodule_param(mmtimer_interval_retry_increment, uint, 0644);
253dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri SivanichMODULE_PARM_DESC(mmtimer_interval_retry_increment,
254dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	"RTC ticks to add to expiration on interval retry (default 40)");
255cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
256cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich/*
257cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * Add a new mmtimer struct to the node's mmtimer list.
258cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * This function assumes the struct mmtimer_node is locked.
259cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich */
260cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_add_list(struct mmtimer *n)
261cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich{
262cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	int nodeid = n->timer->it.mmtimer.node;
263cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	unsigned long expires = n->timer->it.mmtimer.expires;
264cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct rb_node **link = &timers[nodeid].timer_head.rb_node;
265cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct rb_node *parent = NULL;
266cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct mmtimer *x;
267cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
268cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/*
269cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	 * Find the right place in the rbtree:
270cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	 */
271cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	while (*link) {
272cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		parent = *link;
273cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		x = rb_entry(parent, struct mmtimer, list);
274cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
275cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if (expires < x->timer->it.mmtimer.expires)
276cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			link = &(*link)->rb_left;
277cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		else
278cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			link = &(*link)->rb_right;
279cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	}
280cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
281cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/*
282cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	 * Insert the timer to the rbtree and check whether it
283cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	 * replaces the first pending timer
284cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	 */
285cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	rb_link_node(&n->list, parent, link);
286cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	rb_insert_color(&n->list, &timers[nodeid].timer_head);
287cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
288cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (!timers[nodeid].next || expires < rb_entry(timers[nodeid].next,
289cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			struct mmtimer, list)->timer->it.mmtimer.expires)
290cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		timers[nodeid].next = &n->list;
291cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich}
292cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
293cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich/*
294cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * Set the comparator for the next timer.
295cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich * This function assumes the struct mmtimer_node is locked.
296cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich */
297cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_set_next_timer(int nodeid)
298cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich{
299cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct mmtimer_node *n = &timers[nodeid];
300cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct mmtimer *x;
301cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct k_itimer *t;
302dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	u64 expires, exp, set_completion_time;
303dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	int i;
304cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
305cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichrestart:
306cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (n->next == NULL)
307cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		return;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
309cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	x = rb_entry(n->next, struct mmtimer, list);
310cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	t = x->timer;
311cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (!t->it.mmtimer.incr) {
312cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		/* Not an interval timer */
313cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if (!mmtimer_setup(x->cpu, COMPARATOR,
314dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich					t->it.mmtimer.expires,
315dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich					&set_completion_time)) {
316cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			/* Late setup, fire now */
317cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			tasklet_schedule(&n->tasklet);
318cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		}
319cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		return;
320cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	}
321cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
322cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* Interval timer */
323dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	i = 0;
324dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	expires = exp = t->it.mmtimer.expires;
325dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich	while (!mmtimer_setup(x->cpu, COMPARATOR, expires,
326dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich				&set_completion_time)) {
327dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		int to;
328dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich
329dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		i++;
330dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		expires = set_completion_time +
331dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich				mmtimer_interval_retry_increment + (1 << i);
332dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		/* Calculate overruns as we go. */
333dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		to = ((u64)(expires - exp) / t->it.mmtimer.incr);
334dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		if (to) {
335dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich			t->it_overrun += to;
336dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich			t->it.mmtimer.expires += t->it.mmtimer.incr * to;
337dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich			exp = t->it.mmtimer.expires;
338dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		}
339dcade5ed16cce572e375bf4e63dd2150c351bf49Dimitri Sivanich		if (i > 20) {
340cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			printk(KERN_ALERT "mmtimer: cannot reschedule timer\n");
341cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			t->it.mmtimer.clock = TIMER_OFF;
342cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			n->next = rb_next(&x->list);
343cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			rb_erase(&x->list, &n->timer_head);
344cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			kfree(x);
345cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			goto restart;
346cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		}
347cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	}
348cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich}
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_ioctl - ioctl interface for /dev/mmtimer
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @file: file structure for the device
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @cmd: command to execute
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @arg: optional argument to command
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Executes the command specified by @cmd.  Returns 0 for success, < 0 for
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * failure.
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Valid commands:
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETOFFSET - Should return the offset (relative to the start
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * of the page where the registers are mapped) for the counter in question.
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETRES - Returns the resolution of the clock in femto (10^-15)
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * seconds
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETFREQ - Copies the frequency of the clock in Hz to the address
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * specified by @arg
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETBITS - Returns the number of bits in the clock's counter
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_MMAPAVAIL - Returns 1 if the registers can be mmap'd into userspace
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * %MMTIMER_GETCOUNTER - Gets the current value in the counter and places it
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the address specified by @arg.
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3774cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Coxstatic long mmtimer_ioctl(struct file *file, unsigned int cmd,
3784cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox						unsigned long arg)
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ret = 0;
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
382613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann	mutex_lock(&mmtimer_mutex);
3834cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MMTIMER_GETOFFSET:	/* offset of the counter */
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * SN RTC registers are on their own 64k page
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(PAGE_SIZE <= (1 << 16))
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ret = (((long)RTC_COUNTER_ADDR) & (PAGE_SIZE-1)) / 8;
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ret = -ENOSYS;
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MMTIMER_GETRES: /* resolution of the clock in 10^-15 s */
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(copy_to_user((unsigned long __user *)arg,
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				&mmtimer_femtoperiod, sizeof(unsigned long)))
3984cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox			ret = -EFAULT;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MMTIMER_GETFREQ: /* frequency in Hz */
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(copy_to_user((unsigned long __user *)arg,
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				&sn_rtc_cycles_per_second,
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sizeof(unsigned long)))
4054cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox			ret = -EFAULT;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MMTIMER_GETBITS: /* number of bits in the clock */
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = RTC_BITS;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MMTIMER_MMAPAVAIL: /* can we mmap the clock into userspace? */
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret = (PAGE_SIZE <= (1 << 16)) ? 1 : 0;
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	case MMTIMER_GETCOUNTER:
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if(copy_to_user((unsigned long __user *)arg,
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				RTC_COUNTER_ADDR, sizeof(unsigned long)))
4194cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox			ret = -EFAULT;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
4224cddb886a4d0e5cc7a790151740bfb87b568c97dAlan Cox		ret = -ENOTTY;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		break;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
425613655fa39ff6957754fa8ceb8559980920eb8eeArnd Bergmann	mutex_unlock(&mmtimer_mutex);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_mmap - maps the clock's registers into userspace
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @file: file structure for the device
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @vma: VMA to map the registers into
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Calls remap_pfn_range() to map the clock's registers into
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the calling process' address space.
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int mmtimer_mmap(struct file *file, struct vm_area_struct *vma)
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long mmtimer_addr;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vma->vm_end - vma->vm_start != PAGE_SIZE)
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (vma->vm_flags & VM_WRITE)
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EPERM;
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (PAGE_SIZE > (1 << 16))
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOSYS;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_addr = __pa(RTC_COUNTER_ADDR);
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_addr &= ~(PAGE_SIZE - 1);
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_addr &= 0xfffffffffffffffUL;
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (remap_pfn_range(vma, vma->vm_start, mmtimer_addr >> PAGE_SHIFT,
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					PAGE_SIZE, vma->vm_page_prot)) {
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "remap_pfn_range failed in mmtimer.c\n");
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EAGAIN;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice mmtimer_miscdev = {
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	SGI_MMTIMER,
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	MMTIMER_NAME,
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	&mmtimer_fops
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct timespec sgi_clock_offset;
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_period;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Posix Timer Interface
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct timespec sgi_clock_offset;
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_period;
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_clock_get(clockid_t clockid, struct timespec *tp)
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u64 nsec;
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nsec = rtc_time() * sgi_clock_period
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			+ sgi_clock_offset.tv_nsec;
487f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	*tp = ns_to_timespec(nsec);
488f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	tp->tv_sec += sgi_clock_offset.tv_sec;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4921e6d767924c74929c0cfe839ae8f37bcee9e544eRichard Cochranstatic int sgi_clock_set(const clockid_t clockid, const struct timespec *tp)
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u64 nsec;
496f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	u32 rem;
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	nsec = rtc_time() * sgi_clock_period;
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
500f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	sgi_clock_offset.tv_sec = tp->tv_sec - div_u64_rem(nsec, NSEC_PER_SEC, &rem);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rem <= tp->tv_nsec)
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sgi_clock_offset.tv_nsec = tp->tv_sec - rem;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else {
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sgi_clock_offset.tv_nsec = tp->tv_sec + NSEC_PER_SEC - rem;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sgi_clock_offset.tv_sec--;
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_interrupt - timer interrupt handler
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @irq: irq received
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * @dev_id: device the irq came from
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Called when one of the comarators matches the counter, This
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * routine will send signals to processes that have requested
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * them.
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This interrupt is run in an interrupt context
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * by the SHUB. It is therefore safe to locally access SHub
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * registers.
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic irqreturn_t
5257d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsmmtimer_interrupt(int irq, void *dev_id)
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long expires = 0;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int result = IRQ_NONE;
52976832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	unsigned indx = cpu_to_node(smp_processor_id());
530cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct mmtimer *base;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
532cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_lock(&timers[indx].lock);
533cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	base = rb_entry(timers[indx].next, struct mmtimer, list);
534cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (base == NULL) {
535cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		spin_unlock(&timers[indx].lock);
536cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		return result;
537cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	}
538cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
539cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (base->cpu == smp_processor_id()) {
540cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if (base->timer)
541cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			expires = base->timer->it.mmtimer.expires;
542cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		/* expires test won't work with shared irqs */
543cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if ((mmtimer_int_pending(COMPARATOR) > 0) ||
544cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			(expires && (expires <= rtc_time()))) {
545cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			mmtimer_clr_int_pending(COMPARATOR);
546cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			tasklet_schedule(&timers[indx].tasklet);
547cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			result = IRQ_HANDLED;
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
550cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_unlock(&timers[indx].lock);
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return result;
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
554cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanichstatic void mmtimer_tasklet(unsigned long data)
555cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich{
556cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	int nodeid = data;
557cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct mmtimer_node *mn = &timers[nodeid];
5580fbcae222b8aa3a47034a484e02e7fc14050c783Julia Lawall	struct mmtimer *x;
559cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct k_itimer *t;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Send signal and deal with periodic signals */
563cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_lock_irqsave(&mn->lock, flags);
564cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (!mn->next)
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
567cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	x = rb_entry(mn->next, struct mmtimer, list);
568cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	t = x->timer;
569cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
570cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (t->it.mmtimer.clock == TIMER_OFF)
571cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		goto out;
572cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
573cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	t->it_overrun = 0;
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
575cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	mn->next = rb_next(&x->list);
576cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	rb_erase(&x->list, &mn->timer_head);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
578cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (posix_timer_event(t, 0) != 0)
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t->it_overrun++;
580cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if(t->it.mmtimer.incr) {
582cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		t->it.mmtimer.expires += t->it.mmtimer.incr;
583cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		mmtimer_add_list(x);
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Ensure we don't false trigger in mmtimer_interrupt */
586cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		t->it.mmtimer.clock = TIMER_OFF;
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		t->it.mmtimer.expires = 0;
588cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		kfree(x);
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
590cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* Set comparator for next timer, if there is one */
591cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	mmtimer_set_next_timer(nodeid);
592cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	t->it_overrun_last = t->it_overrun;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
595cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_unlock_irqrestore(&mn->lock, flags);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_timer_create(struct k_itimer *timer)
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Insure that a newly created timer is off */
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timer->it.mmtimer.clock = TIMER_OFF;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* This does not really delete a timer. It just insures
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that the timer is not active
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Assumption: it_lock is already held with irq's disabled
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_timer_del(struct k_itimer *timr)
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cnodeid_t nodeid = timr->it.mmtimer.node;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long irqflags;
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
615cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_lock_irqsave(&timers[nodeid].lock, irqflags);
616cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (timr->it.mmtimer.clock != TIMER_OFF) {
617cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		unsigned long expires = timr->it.mmtimer.expires;
618cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		struct rb_node *n = timers[nodeid].timer_head.rb_node;
619cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		struct mmtimer *uninitialized_var(t);
620cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		int r = 0;
621cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		timr->it.mmtimer.clock = TIMER_OFF;
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		timr->it.mmtimer.expires = 0;
624cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
625cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		while (n) {
626cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			t = rb_entry(n, struct mmtimer, list);
627cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			if (t->timer == timr)
628cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich				break;
629cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
630cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			if (expires < t->timer->it.mmtimer.expires)
631cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich				n = n->rb_left;
632cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			else
633cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich				n = n->rb_right;
634cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		}
635cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
636cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if (!n) {
637cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			spin_unlock_irqrestore(&timers[nodeid].lock, irqflags);
638cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			return 0;
639cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		}
640cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
641cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if (timers[nodeid].next == n) {
642cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			timers[nodeid].next = rb_next(n);
643cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			r = 1;
644cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		}
645cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
646cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		rb_erase(n, &timers[nodeid].timer_head);
647cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		kfree(t);
648cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
649cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		if (r) {
650cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			mmtimer_disable_int(cnodeid_to_nasid(nodeid),
651cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich				COMPARATOR);
652cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			mmtimer_set_next_timer(nodeid);
653cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		}
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
655cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_unlock_irqrestore(&timers[nodeid].lock, irqflags);
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Assumption: it_lock is already held with irq's disabled */
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void sgi_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting)
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (timr->it.mmtimer.clock == TIMER_OFF) {
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_setting->it_interval.tv_nsec = 0;
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_setting->it_interval.tv_sec = 0;
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_setting->it_value.tv_nsec = 0;
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_setting->it_value.tv_sec =0;
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	cur_setting->it_interval = ns_to_timespec(timr->it.mmtimer.incr * sgi_clock_period);
672f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	cur_setting->it_value = ns_to_timespec((timr->it.mmtimer.expires - rtc_time()) * sgi_clock_period);
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int sgi_timer_set(struct k_itimer *timr, int flags,
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct itimerspec * new_setting,
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct itimerspec * old_setting)
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long when, period, irqflags;
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err = 0;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cnodeid_t nodeid;
683cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct mmtimer *base;
684cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	struct rb_node *n;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (old_setting)
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sgi_timer_get(timr, old_setting);
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sgi_timer_del(timr);
690f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	when = timespec_to_ns(&new_setting->it_value);
691f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel	period = timespec_to_ns(&new_setting->it_interval);
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (when == 0)
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Clear timer */
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
697cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	base = kmalloc(sizeof(struct mmtimer), GFP_KERNEL);
698cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (base == NULL)
699cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		return -ENOMEM;
700cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (flags & TIMER_ABSTIME) {
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		struct timespec n;
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned long now;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		getnstimeofday(&n);
706f8bd2258e2d520dff28c855658bd24bdafb5102dRoman Zippel		now = timespec_to_ns(&n);
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (when > now)
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			when -= now;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Fire the timer immediately */
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			when = 0;
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Convert to sgi clock period. Need to keep rtc_time() as near as possible
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * to getnstimeofday() in order to be as faithful as possible to the time
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * specified.
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	when = (when + sgi_clock_period - 1) / sgi_clock_period + rtc_time();
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	period = (period + sgi_clock_period - 1)  / sgi_clock_period;
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We are allocating a local SHub comparator. If we would be moved to another
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * cpu then another SHub may be local to us. Prohibit that by switching off
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * preemption.
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	preempt_disable();
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
72955642d36cd0f626c614f6dfa1151a5af1ff84f36Tony Luck	nodeid =  cpu_to_node(smp_processor_id());
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* Lock the node timer structure */
732cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_lock_irqsave(&timers[nodeid].lock, irqflags);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
73476832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	base->timer = timr;
73576832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	base->cpu = smp_processor_id();
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
737cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	timr->it.mmtimer.clock = TIMER_SET;
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timr->it.mmtimer.node = nodeid;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timr->it.mmtimer.incr = period;
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timr->it.mmtimer.expires = when;
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
742cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	n = timers[nodeid].next;
743cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
744cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* Add the new struct mmtimer to node's timer list */
745cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	mmtimer_add_list(base);
746cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
747cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (timers[nodeid].next == n) {
748cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		/* No need to reprogram comparator for now */
749cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		spin_unlock_irqrestore(&timers[nodeid].lock, irqflags);
750cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		preempt_enable();
751cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		return err;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
754cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* We need to reprogram the comparator */
755cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	if (n)
756cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		mmtimer_disable_int(cnodeid_to_nasid(nodeid), COMPARATOR);
757cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
758cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	mmtimer_set_next_timer(nodeid);
759cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich
760cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* Unlock the node timer structure */
761cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	spin_unlock_irqrestore(&timers[nodeid].lock, irqflags);
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	preempt_enable();
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
768e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixnerstatic int sgi_clock_getres(const clockid_t which_clock, struct timespec *tp)
769e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner{
770e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner	tp->tv_sec = 0;
771ebaac757acae0431e2c79c00e09f1debdabbddd7Thomas Gleixner	tp->tv_nsec = sgi_clock_period;
772e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner	return 0;
773e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner}
774e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct k_clock sgi_clock = {
7762fd1f04089cb657c5d6c484b280ec4d3398aa157Thomas Gleixner	.clock_set	= sgi_clock_set,
7772fd1f04089cb657c5d6c484b280ec4d3398aa157Thomas Gleixner	.clock_get	= sgi_clock_get,
778e5e542eea9075dd008993c2ee80b2cc9f31fc494Thomas Gleixner	.clock_getres	= sgi_clock_getres,
7792fd1f04089cb657c5d6c484b280ec4d3398aa157Thomas Gleixner	.timer_create	= sgi_timer_create,
7802fd1f04089cb657c5d6c484b280ec4d3398aa157Thomas Gleixner	.timer_set	= sgi_timer_set,
7812fd1f04089cb657c5d6c484b280ec4d3398aa157Thomas Gleixner	.timer_del	= sgi_timer_del,
7822fd1f04089cb657c5d6c484b280ec4d3398aa157Thomas Gleixner	.timer_get	= sgi_timer_get
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/**
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * mmtimer_init - device initialization routine
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Does initial setup for the mmtimer device.
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init mmtimer_init(void)
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
79276832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	cnodeid_t node, maxn = -1;
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ia64_platform_is("sn2"))
795f032f90809ebbbd28feb90f97add2e0a869a42edBjorn Helgaas		return 0;
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Sanity check the cycles/sec variable
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sn_rtc_cycles_per_second < 100000) {
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "%s: unable to determine clock frequency\n",
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       MMTIMER_NAME);
8035d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman		goto out1;
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mmtimer_femtoperiod = ((unsigned long)1E15 + sn_rtc_cycles_per_second /
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			       2) / sn_rtc_cycles_per_second;
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8090f2ed4c6bae23d2b7ef0ea2d272377e3de700c0cThomas Gleixner	if (request_irq(SGI_MMTIMER_VECTOR, mmtimer_interrupt, IRQF_PERCPU, MMTIMER_NAME, NULL)) {
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "%s: unable to allocate interrupt.",
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			MMTIMER_NAME);
8125d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman		goto out1;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (misc_register(&mmtimer_miscdev)) {
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "%s: failed to register device\n",
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       MMTIMER_NAME);
8185d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman		goto out2;
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
82176832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	/* Get max numbered node, calculate slots needed */
82276832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	for_each_online_node(node) {
82376832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich		maxn = node;
82476832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	}
82576832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	maxn++;
82676832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich
82776832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	/* Allocate list of node ptrs to mmtimer_t's */
828cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL);
82908b79dd10bb24f5fb12446d21d32c87a8136c770Peter Senna Tschudin	if (!timers) {
83076832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich		printk(KERN_ERR "%s: failed to allocate memory for device\n",
83176832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich				MMTIMER_NAME);
8325d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman		goto out3;
83376832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	}
83476832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich
835cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich	/* Initialize struct mmtimer's for each online node */
83676832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	for_each_online_node(node) {
837cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		spin_lock_init(&timers[node].lock);
838cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich		tasklet_init(&timers[node].tasklet, mmtimer_tasklet,
839cbacdd9572285c86848dd323dc764abb3681ddbcDimitri Sivanich			(unsigned long) node);
84076832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich	}
84176832c28de4fabbf32fe1e5a25194724a3430070Dimitri Sivanich
842ebaac757acae0431e2c79c00e09f1debdabbddd7Thomas Gleixner	sgi_clock_period = NSEC_PER_SEC / sn_rtc_cycles_per_second;
843527087374faa488776a789375a7d6ea74fda6f71Thomas Gleixner	posix_timers_register_clock(CLOCK_SGI_CYCLE, &sgi_clock);
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "%s: v%s, %ld MHz\n", MMTIMER_DESC, MMTIMER_VERSION,
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       sn_rtc_cycles_per_second/(unsigned long)1E6);
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8495d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman
8505d469ec0f40d65b2a0a704402990a43b2dafe197Neil Hormanout3:
8515d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman	misc_deregister(&mmtimer_miscdev);
8525d469ec0f40d65b2a0a704402990a43b2dafe197Neil Hormanout2:
8535d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman	free_irq(SGI_MMTIMER_VECTOR, NULL);
8545d469ec0f40d65b2a0a704402990a43b2dafe197Neil Hormanout1:
8555d469ec0f40d65b2a0a704402990a43b2dafe197Neil Horman	return -1;
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(mmtimer_init);
859