11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * HP i8042 SDC + MSM-58321 BBRTC driver. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * Copyright (c) 2001 Brian S. Julin 51da177e4SLinus Torvalds * All rights reserved. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or without 81da177e4SLinus Torvalds * modification, are permitted provided that the following conditions 91da177e4SLinus Torvalds * are met: 101da177e4SLinus Torvalds * 1. Redistributions of source code must retain the above copyright 111da177e4SLinus Torvalds * notice, this list of conditions, and the following disclaimer, 121da177e4SLinus Torvalds * without modification. 131da177e4SLinus Torvalds * 2. The name of the author may not be used to endorse or promote products 141da177e4SLinus Torvalds * derived from this software without specific prior written permission. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * Alternatively, this software may be distributed under the terms of the 171da177e4SLinus Torvalds * GNU General Public License ("GPL"). 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 201da177e4SLinus Torvalds * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 211da177e4SLinus Torvalds * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 221da177e4SLinus Torvalds * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 231da177e4SLinus Torvalds * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 241da177e4SLinus Torvalds * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 251da177e4SLinus Torvalds * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 261da177e4SLinus Torvalds * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 271da177e4SLinus Torvalds * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 281da177e4SLinus Torvalds * 291da177e4SLinus Torvalds * References: 301da177e4SLinus Torvalds * System Device Controller Microprocessor Firmware Theory of Operation 311da177e4SLinus Torvalds * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2 321da177e4SLinus Torvalds * efirtc.c by Stephane Eranian/Hewlett Packard 331da177e4SLinus Torvalds * 341da177e4SLinus Torvalds */ 351da177e4SLinus Torvalds 361da177e4SLinus Torvalds #include <linux/hp_sdc.h> 371da177e4SLinus Torvalds #include <linux/errno.h> 38986f8b8cSArnd Bergmann #include <linux/smp_lock.h> 391da177e4SLinus Torvalds #include <linux/types.h> 401da177e4SLinus Torvalds #include <linux/init.h> 411da177e4SLinus Torvalds #include <linux/module.h> 421da177e4SLinus Torvalds #include <linux/time.h> 431da177e4SLinus Torvalds #include <linux/miscdevice.h> 441da177e4SLinus Torvalds #include <linux/proc_fs.h> 451da177e4SLinus Torvalds #include <linux/poll.h> 461da177e4SLinus Torvalds #include <linux/rtc.h> 470f17e4c7SMatthew Wilcox #include <linux/semaphore.h> 481da177e4SLinus Torvalds 491da177e4SLinus Torvalds MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>"); 501da177e4SLinus Torvalds MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver"); 511da177e4SLinus Torvalds MODULE_LICENSE("Dual BSD/GPL"); 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds #define RTC_VERSION "1.10d" 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds static unsigned long epoch = 2000; 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds static struct semaphore i8042tregs; 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds static hp_sdc_irqhook hp_sdc_rtc_isr; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds static struct fasync_struct *hp_sdc_rtc_async_queue; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait); 641da177e4SLinus Torvalds 656ce6b3aeSAl Viro static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, 661da177e4SLinus Torvalds size_t count, loff_t *ppos); 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file, 691da177e4SLinus Torvalds unsigned int cmd, unsigned long arg); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait); 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds static int hp_sdc_rtc_open(struct inode *inode, struct file *file); 741da177e4SLinus Torvalds static int hp_sdc_rtc_release(struct inode *inode, struct file *file); 751da177e4SLinus Torvalds static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on); 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off, 781da177e4SLinus Torvalds int count, int *eof, void *data); 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static void hp_sdc_rtc_isr (int irq, void *dev_id, 811da177e4SLinus Torvalds uint8_t status, uint8_t data) 821da177e4SLinus Torvalds { 831da177e4SLinus Torvalds return; 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm) 871da177e4SLinus Torvalds { 881da177e4SLinus Torvalds struct semaphore tsem; 891da177e4SLinus Torvalds hp_sdc_transaction t; 901da177e4SLinus Torvalds uint8_t tseq[91]; 911da177e4SLinus Torvalds int i; 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds i = 0; 941da177e4SLinus Torvalds while (i < 91) { 951da177e4SLinus Torvalds tseq[i++] = HP_SDC_ACT_DATAREG | 961da177e4SLinus Torvalds HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN; 971da177e4SLinus Torvalds tseq[i++] = 0x01; /* write i8042[0x70] */ 981da177e4SLinus Torvalds tseq[i] = i / 7; /* BBRTC reg address */ 991da177e4SLinus Torvalds i++; 1001da177e4SLinus Torvalds tseq[i++] = HP_SDC_CMD_DO_RTCR; /* Trigger command */ 1011da177e4SLinus Torvalds tseq[i++] = 2; /* expect 1 stat/dat pair back. */ 1021da177e4SLinus Torvalds i++; i++; /* buffer for stat/dat pair */ 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds tseq[84] |= HP_SDC_ACT_SEMAPHORE; 1051da177e4SLinus Torvalds t.endidx = 91; 1061da177e4SLinus Torvalds t.seq = tseq; 1071da177e4SLinus Torvalds t.act.semaphore = &tsem; 1081da177e4SLinus Torvalds init_MUTEX_LOCKED(&tsem); 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds if (hp_sdc_enqueue_transaction(&t)) return -1; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds down_interruptible(&tsem); /* Put ourselves to sleep for results. */ 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds /* Check for nonpresence of BBRTC */ 1151da177e4SLinus Torvalds if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] | 1161da177e4SLinus Torvalds tseq[55] | tseq[62] | tseq[34] | tseq[41] | 1171da177e4SLinus Torvalds tseq[20] | tseq[27] | tseq[6] | tseq[13]) & 0x0f)) 1181da177e4SLinus Torvalds return -1; 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds memset(rtctm, 0, sizeof(struct rtc_time)); 1211da177e4SLinus Torvalds rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10; 1221da177e4SLinus Torvalds rtctm->tm_mon = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10; 1231da177e4SLinus Torvalds rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10; 1241da177e4SLinus Torvalds rtctm->tm_wday = (tseq[48] & 0x0f); 1251da177e4SLinus Torvalds rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10; 1261da177e4SLinus Torvalds rtctm->tm_min = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10; 1271da177e4SLinus Torvalds rtctm->tm_sec = (tseq[6] & 0x0f) + (tseq[13] & 0x0f) * 10; 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds return 0; 1301da177e4SLinus Torvalds } 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm) 1331da177e4SLinus Torvalds { 1341da177e4SLinus Torvalds struct rtc_time tm, tm_last; 1351da177e4SLinus Torvalds int i = 0; 1361da177e4SLinus Torvalds 1371da177e4SLinus Torvalds /* MSM-58321 has no read latch, so must read twice and compare. */ 1381da177e4SLinus Torvalds 1391da177e4SLinus Torvalds if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1; 1401da177e4SLinus Torvalds if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1; 1411da177e4SLinus Torvalds 1421da177e4SLinus Torvalds while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) { 1431da177e4SLinus Torvalds if (i++ > 4) return -1; 1441da177e4SLinus Torvalds memcpy(&tm_last, &tm, sizeof(struct rtc_time)); 1451da177e4SLinus Torvalds if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds memcpy(rtctm, &tm, sizeof(struct rtc_time)); 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds return 0; 1511da177e4SLinus Torvalds } 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg) 1551da177e4SLinus Torvalds { 1561da177e4SLinus Torvalds hp_sdc_transaction t; 1571da177e4SLinus Torvalds uint8_t tseq[26] = { 1581da177e4SLinus Torvalds HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 1591da177e4SLinus Torvalds 0, 1601da177e4SLinus Torvalds HP_SDC_CMD_READ_T1, 2, 0, 0, 1611da177e4SLinus Torvalds HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 1621da177e4SLinus Torvalds HP_SDC_CMD_READ_T2, 2, 0, 0, 1631da177e4SLinus Torvalds HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 1641da177e4SLinus Torvalds HP_SDC_CMD_READ_T3, 2, 0, 0, 1651da177e4SLinus Torvalds HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 1661da177e4SLinus Torvalds HP_SDC_CMD_READ_T4, 2, 0, 0, 1671da177e4SLinus Torvalds HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN, 1681da177e4SLinus Torvalds HP_SDC_CMD_READ_T5, 2, 0, 0 1691da177e4SLinus Torvalds }; 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds t.endidx = numreg * 5; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds tseq[1] = loadcmd; 1741da177e4SLinus Torvalds tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */ 1751da177e4SLinus Torvalds 1761da177e4SLinus Torvalds t.seq = tseq; 1771da177e4SLinus Torvalds t.act.semaphore = &i8042tregs; 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds down_interruptible(&i8042tregs); /* Sleep if output regs in use. */ 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds if (hp_sdc_enqueue_transaction(&t)) return -1; 1821da177e4SLinus Torvalds 1831da177e4SLinus Torvalds down_interruptible(&i8042tregs); /* Sleep until results come back. */ 1841da177e4SLinus Torvalds up(&i8042tregs); 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds return (tseq[5] | 1871da177e4SLinus Torvalds ((uint64_t)(tseq[10]) << 8) | ((uint64_t)(tseq[15]) << 16) | 1881da177e4SLinus Torvalds ((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32)); 1891da177e4SLinus Torvalds } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds /* Read the i8042 real-time clock */ 1931da177e4SLinus Torvalds static inline int hp_sdc_rtc_read_rt(struct timeval *res) { 1941da177e4SLinus Torvalds int64_t raw; 1951da177e4SLinus Torvalds uint32_t tenms; 1961da177e4SLinus Torvalds unsigned int days; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5); 1991da177e4SLinus Torvalds if (raw < 0) return -1; 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds tenms = (uint32_t)raw & 0xffffff; 2021da177e4SLinus Torvalds days = (unsigned int)(raw >> 24) & 0xffff; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds res->tv_usec = (suseconds_t)(tenms % 100) * 10000; 2051da177e4SLinus Torvalds res->tv_sec = (time_t)(tenms / 100) + days * 86400; 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds return 0; 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds /* Read the i8042 fast handshake timer */ 2121da177e4SLinus Torvalds static inline int hp_sdc_rtc_read_fhs(struct timeval *res) { 2131da177e4SLinus Torvalds uint64_t raw; 2141da177e4SLinus Torvalds unsigned int tenms; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2); 2171da177e4SLinus Torvalds if (raw < 0) return -1; 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds tenms = (unsigned int)raw & 0xffff; 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds res->tv_usec = (suseconds_t)(tenms % 100) * 10000; 2221da177e4SLinus Torvalds res->tv_sec = (time_t)(tenms / 100); 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds return 0; 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds 2281da177e4SLinus Torvalds /* Read the i8042 match timer (a.k.a. alarm) */ 2291da177e4SLinus Torvalds static inline int hp_sdc_rtc_read_mt(struct timeval *res) { 2301da177e4SLinus Torvalds int64_t raw; 2311da177e4SLinus Torvalds uint32_t tenms; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3); 2341da177e4SLinus Torvalds if (raw < 0) return -1; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds tenms = (uint32_t)raw & 0xffffff; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds res->tv_usec = (suseconds_t)(tenms % 100) * 10000; 2391da177e4SLinus Torvalds res->tv_sec = (time_t)(tenms / 100); 2401da177e4SLinus Torvalds 2411da177e4SLinus Torvalds return 0; 2421da177e4SLinus Torvalds } 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds /* Read the i8042 delay timer */ 2461da177e4SLinus Torvalds static inline int hp_sdc_rtc_read_dt(struct timeval *res) { 2471da177e4SLinus Torvalds int64_t raw; 2481da177e4SLinus Torvalds uint32_t tenms; 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3); 2511da177e4SLinus Torvalds if (raw < 0) return -1; 2521da177e4SLinus Torvalds 2531da177e4SLinus Torvalds tenms = (uint32_t)raw & 0xffffff; 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds res->tv_usec = (suseconds_t)(tenms % 100) * 10000; 2561da177e4SLinus Torvalds res->tv_sec = (time_t)(tenms / 100); 2571da177e4SLinus Torvalds 2581da177e4SLinus Torvalds return 0; 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds /* Read the i8042 cycle timer (a.k.a. periodic) */ 2631da177e4SLinus Torvalds static inline int hp_sdc_rtc_read_ct(struct timeval *res) { 2641da177e4SLinus Torvalds int64_t raw; 2651da177e4SLinus Torvalds uint32_t tenms; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3); 2681da177e4SLinus Torvalds if (raw < 0) return -1; 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds tenms = (uint32_t)raw & 0xffffff; 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds res->tv_usec = (suseconds_t)(tenms % 100) * 10000; 2731da177e4SLinus Torvalds res->tv_sec = (time_t)(tenms / 100); 2741da177e4SLinus Torvalds 2751da177e4SLinus Torvalds return 0; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds /* Set the i8042 real-time clock */ 2801da177e4SLinus Torvalds static int hp_sdc_rtc_set_rt (struct timeval *setto) 2811da177e4SLinus Torvalds { 2821da177e4SLinus Torvalds uint32_t tenms; 2831da177e4SLinus Torvalds unsigned int days; 2841da177e4SLinus Torvalds hp_sdc_transaction t; 2851da177e4SLinus Torvalds uint8_t tseq[11] = { 2861da177e4SLinus Torvalds HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, 2871da177e4SLinus Torvalds HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0, 2881da177e4SLinus Torvalds HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, 2891da177e4SLinus Torvalds HP_SDC_CMD_SET_RTD, 2, 0, 0 2901da177e4SLinus Torvalds }; 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds t.endidx = 10; 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds if (0xffff < setto->tv_sec / 86400) return -1; 2951da177e4SLinus Torvalds days = setto->tv_sec / 86400; 2961da177e4SLinus Torvalds if (0xffff < setto->tv_usec / 1000000 / 86400) return -1; 2971da177e4SLinus Torvalds days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400; 2981da177e4SLinus Torvalds if (days > 0xffff) return -1; 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds if (0xffffff < setto->tv_sec) return -1; 3011da177e4SLinus Torvalds tenms = setto->tv_sec * 100; 3021da177e4SLinus Torvalds if (0xffffff < setto->tv_usec / 10000) return -1; 3031da177e4SLinus Torvalds tenms += setto->tv_usec / 10000; 3041da177e4SLinus Torvalds if (tenms > 0xffffff) return -1; 3051da177e4SLinus Torvalds 3061da177e4SLinus Torvalds tseq[3] = (uint8_t)(tenms & 0xff); 3071da177e4SLinus Torvalds tseq[4] = (uint8_t)((tenms >> 8) & 0xff); 3081da177e4SLinus Torvalds tseq[5] = (uint8_t)((tenms >> 16) & 0xff); 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds tseq[9] = (uint8_t)(days & 0xff); 3111da177e4SLinus Torvalds tseq[10] = (uint8_t)((days >> 8) & 0xff); 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds t.seq = tseq; 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds if (hp_sdc_enqueue_transaction(&t)) return -1; 3161da177e4SLinus Torvalds return 0; 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds /* Set the i8042 fast handshake timer */ 3201da177e4SLinus Torvalds static int hp_sdc_rtc_set_fhs (struct timeval *setto) 3211da177e4SLinus Torvalds { 3221da177e4SLinus Torvalds uint32_t tenms; 3231da177e4SLinus Torvalds hp_sdc_transaction t; 3241da177e4SLinus Torvalds uint8_t tseq[5] = { 3251da177e4SLinus Torvalds HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, 3261da177e4SLinus Torvalds HP_SDC_CMD_SET_FHS, 2, 0, 0 3271da177e4SLinus Torvalds }; 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds t.endidx = 4; 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds if (0xffff < setto->tv_sec) return -1; 3321da177e4SLinus Torvalds tenms = setto->tv_sec * 100; 3331da177e4SLinus Torvalds if (0xffff < setto->tv_usec / 10000) return -1; 3341da177e4SLinus Torvalds tenms += setto->tv_usec / 10000; 3351da177e4SLinus Torvalds if (tenms > 0xffff) return -1; 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds tseq[3] = (uint8_t)(tenms & 0xff); 3381da177e4SLinus Torvalds tseq[4] = (uint8_t)((tenms >> 8) & 0xff); 3391da177e4SLinus Torvalds 3401da177e4SLinus Torvalds t.seq = tseq; 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds if (hp_sdc_enqueue_transaction(&t)) return -1; 3431da177e4SLinus Torvalds return 0; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 3461da177e4SLinus Torvalds 3471da177e4SLinus Torvalds /* Set the i8042 match timer (a.k.a. alarm) */ 3481da177e4SLinus Torvalds #define hp_sdc_rtc_set_mt (setto) \ 3491da177e4SLinus Torvalds hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT) 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds /* Set the i8042 delay timer */ 3521da177e4SLinus Torvalds #define hp_sdc_rtc_set_dt (setto) \ 3531da177e4SLinus Torvalds hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT) 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds /* Set the i8042 cycle timer (a.k.a. periodic) */ 3561da177e4SLinus Torvalds #define hp_sdc_rtc_set_ct (setto) \ 3571da177e4SLinus Torvalds hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT) 3581da177e4SLinus Torvalds 3591da177e4SLinus Torvalds /* Set one of the i8042 3-byte wide timers */ 3601da177e4SLinus Torvalds static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd) 3611da177e4SLinus Torvalds { 3621da177e4SLinus Torvalds uint32_t tenms; 3631da177e4SLinus Torvalds hp_sdc_transaction t; 3641da177e4SLinus Torvalds uint8_t tseq[6] = { 3651da177e4SLinus Torvalds HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT, 3661da177e4SLinus Torvalds 0, 3, 0, 0, 0 3671da177e4SLinus Torvalds }; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds t.endidx = 6; 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds if (0xffffff < setto->tv_sec) return -1; 3721da177e4SLinus Torvalds tenms = setto->tv_sec * 100; 3731da177e4SLinus Torvalds if (0xffffff < setto->tv_usec / 10000) return -1; 3741da177e4SLinus Torvalds tenms += setto->tv_usec / 10000; 3751da177e4SLinus Torvalds if (tenms > 0xffffff) return -1; 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds tseq[1] = setcmd; 3781da177e4SLinus Torvalds tseq[3] = (uint8_t)(tenms & 0xff); 3791da177e4SLinus Torvalds tseq[4] = (uint8_t)((tenms >> 8) & 0xff); 3801da177e4SLinus Torvalds tseq[5] = (uint8_t)((tenms >> 16) & 0xff); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds t.seq = tseq; 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds if (hp_sdc_enqueue_transaction(&t)) { 3851da177e4SLinus Torvalds return -1; 3861da177e4SLinus Torvalds } 3871da177e4SLinus Torvalds return 0; 3881da177e4SLinus Torvalds } 3891da177e4SLinus Torvalds 3906ce6b3aeSAl Viro static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf, 3911da177e4SLinus Torvalds size_t count, loff_t *ppos) { 3921da177e4SLinus Torvalds ssize_t retval; 3931da177e4SLinus Torvalds 3941da177e4SLinus Torvalds if (count < sizeof(unsigned long)) 3951da177e4SLinus Torvalds return -EINVAL; 3961da177e4SLinus Torvalds 3976ce6b3aeSAl Viro retval = put_user(68, (unsigned long __user *)buf); 3981da177e4SLinus Torvalds return retval; 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait) 4021da177e4SLinus Torvalds { 4031da177e4SLinus Torvalds unsigned long l; 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds l = 0; 4061da177e4SLinus Torvalds if (l != 0) 4071da177e4SLinus Torvalds return POLLIN | POLLRDNORM; 4081da177e4SLinus Torvalds return 0; 4091da177e4SLinus Torvalds } 4101da177e4SLinus Torvalds 4111da177e4SLinus Torvalds static int hp_sdc_rtc_open(struct inode *inode, struct file *file) 4121da177e4SLinus Torvalds { 413986f8b8cSArnd Bergmann cycle_kernel_lock(); 4141da177e4SLinus Torvalds return 0; 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds static int hp_sdc_rtc_release(struct inode *inode, struct file *file) 4181da177e4SLinus Torvalds { 4191da177e4SLinus Torvalds /* Turn off interrupts? */ 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds if (file->f_flags & FASYNC) { 4221da177e4SLinus Torvalds hp_sdc_rtc_fasync (-1, file, 0); 4231da177e4SLinus Torvalds } 4241da177e4SLinus Torvalds 4251da177e4SLinus Torvalds return 0; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on) 4291da177e4SLinus Torvalds { 4301da177e4SLinus Torvalds return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue); 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds static int hp_sdc_rtc_proc_output (char *buf) 4341da177e4SLinus Torvalds { 4351da177e4SLinus Torvalds #define YN(bit) ("no") 4361da177e4SLinus Torvalds #define NY(bit) ("yes") 4371da177e4SLinus Torvalds char *p; 4381da177e4SLinus Torvalds struct rtc_time tm; 4391da177e4SLinus Torvalds struct timeval tv; 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds memset(&tm, 0, sizeof(struct rtc_time)); 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds p = buf; 4441da177e4SLinus Torvalds 4451da177e4SLinus Torvalds if (hp_sdc_rtc_read_bbrtc(&tm)) { 4461da177e4SLinus Torvalds p += sprintf(p, "BBRTC\t\t: READ FAILED!\n"); 4471da177e4SLinus Torvalds } else { 4481da177e4SLinus Torvalds p += sprintf(p, 4491da177e4SLinus Torvalds "rtc_time\t: %02d:%02d:%02d\n" 4501da177e4SLinus Torvalds "rtc_date\t: %04d-%02d-%02d\n" 4511da177e4SLinus Torvalds "rtc_epoch\t: %04lu\n", 4521da177e4SLinus Torvalds tm.tm_hour, tm.tm_min, tm.tm_sec, 4531da177e4SLinus Torvalds tm.tm_year + 1900, tm.tm_mon + 1, 4541da177e4SLinus Torvalds tm.tm_mday, epoch); 4551da177e4SLinus Torvalds } 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds if (hp_sdc_rtc_read_rt(&tv)) { 4581da177e4SLinus Torvalds p += sprintf(p, "i8042 rtc\t: READ FAILED!\n"); 4591da177e4SLinus Torvalds } else { 4601da177e4SLinus Torvalds p += sprintf(p, "i8042 rtc\t: %ld.%02d seconds\n", 4617477fb6fSGeert Uytterhoeven tv.tv_sec, (int)tv.tv_usec/1000); 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds if (hp_sdc_rtc_read_fhs(&tv)) { 4651da177e4SLinus Torvalds p += sprintf(p, "handshake\t: READ FAILED!\n"); 4661da177e4SLinus Torvalds } else { 4671da177e4SLinus Torvalds p += sprintf(p, "handshake\t: %ld.%02d seconds\n", 4687477fb6fSGeert Uytterhoeven tv.tv_sec, (int)tv.tv_usec/1000); 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds if (hp_sdc_rtc_read_mt(&tv)) { 4721da177e4SLinus Torvalds p += sprintf(p, "alarm\t\t: READ FAILED!\n"); 4731da177e4SLinus Torvalds } else { 4741da177e4SLinus Torvalds p += sprintf(p, "alarm\t\t: %ld.%02d seconds\n", 4757477fb6fSGeert Uytterhoeven tv.tv_sec, (int)tv.tv_usec/1000); 4761da177e4SLinus Torvalds } 4771da177e4SLinus Torvalds 4781da177e4SLinus Torvalds if (hp_sdc_rtc_read_dt(&tv)) { 4791da177e4SLinus Torvalds p += sprintf(p, "delay\t\t: READ FAILED!\n"); 4801da177e4SLinus Torvalds } else { 4811da177e4SLinus Torvalds p += sprintf(p, "delay\t\t: %ld.%02d seconds\n", 4827477fb6fSGeert Uytterhoeven tv.tv_sec, (int)tv.tv_usec/1000); 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds if (hp_sdc_rtc_read_ct(&tv)) { 4861da177e4SLinus Torvalds p += sprintf(p, "periodic\t: READ FAILED!\n"); 4871da177e4SLinus Torvalds } else { 4881da177e4SLinus Torvalds p += sprintf(p, "periodic\t: %ld.%02d seconds\n", 4897477fb6fSGeert Uytterhoeven tv.tv_sec, (int)tv.tv_usec/1000); 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds p += sprintf(p, 4931da177e4SLinus Torvalds "DST_enable\t: %s\n" 4941da177e4SLinus Torvalds "BCD\t\t: %s\n" 4951da177e4SLinus Torvalds "24hr\t\t: %s\n" 4961da177e4SLinus Torvalds "square_wave\t: %s\n" 4971da177e4SLinus Torvalds "alarm_IRQ\t: %s\n" 4981da177e4SLinus Torvalds "update_IRQ\t: %s\n" 4991da177e4SLinus Torvalds "periodic_IRQ\t: %s\n" 5001da177e4SLinus Torvalds "periodic_freq\t: %ld\n" 5011da177e4SLinus Torvalds "batt_status\t: %s\n", 5021da177e4SLinus Torvalds YN(RTC_DST_EN), 5031da177e4SLinus Torvalds NY(RTC_DM_BINARY), 5041da177e4SLinus Torvalds YN(RTC_24H), 5051da177e4SLinus Torvalds YN(RTC_SQWE), 5061da177e4SLinus Torvalds YN(RTC_AIE), 5071da177e4SLinus Torvalds YN(RTC_UIE), 5081da177e4SLinus Torvalds YN(RTC_PIE), 5091da177e4SLinus Torvalds 1UL, 5101da177e4SLinus Torvalds 1 ? "okay" : "dead"); 5111da177e4SLinus Torvalds 5121da177e4SLinus Torvalds return p - buf; 5131da177e4SLinus Torvalds #undef YN 5141da177e4SLinus Torvalds #undef NY 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds static int hp_sdc_rtc_read_proc(char *page, char **start, off_t off, 5181da177e4SLinus Torvalds int count, int *eof, void *data) 5191da177e4SLinus Torvalds { 5201da177e4SLinus Torvalds int len = hp_sdc_rtc_proc_output (page); 5211da177e4SLinus Torvalds if (len <= off+count) *eof = 1; 5221da177e4SLinus Torvalds *start = page + off; 5231da177e4SLinus Torvalds len -= off; 5241da177e4SLinus Torvalds if (len>count) len = count; 5251da177e4SLinus Torvalds if (len<0) len = 0; 5261da177e4SLinus Torvalds return len; 5271da177e4SLinus Torvalds } 5281da177e4SLinus Torvalds 5291da177e4SLinus Torvalds static int hp_sdc_rtc_ioctl(struct inode *inode, struct file *file, 5301da177e4SLinus Torvalds unsigned int cmd, unsigned long arg) 5311da177e4SLinus Torvalds { 5321da177e4SLinus Torvalds #if 1 5331da177e4SLinus Torvalds return -EINVAL; 5341da177e4SLinus Torvalds #else 5351da177e4SLinus Torvalds 5361da177e4SLinus Torvalds struct rtc_time wtime; 5371da177e4SLinus Torvalds struct timeval ttime; 5381da177e4SLinus Torvalds int use_wtime = 0; 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds /* This needs major work. */ 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds switch (cmd) { 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ 5451da177e4SLinus Torvalds case RTC_AIE_ON: /* Allow alarm interrupts. */ 5461da177e4SLinus Torvalds case RTC_PIE_OFF: /* Mask periodic int. enab. bit */ 5471da177e4SLinus Torvalds case RTC_PIE_ON: /* Allow periodic ints */ 5481da177e4SLinus Torvalds case RTC_UIE_ON: /* Allow ints for RTC updates. */ 5491da177e4SLinus Torvalds case RTC_UIE_OFF: /* Allow ints for RTC updates. */ 5501da177e4SLinus Torvalds { 5511da177e4SLinus Torvalds /* We cannot mask individual user timers and we 5521da177e4SLinus Torvalds cannot tell them apart when they occur, so it 5531da177e4SLinus Torvalds would be disingenuous to succeed these IOCTLs */ 5541da177e4SLinus Torvalds return -EINVAL; 5551da177e4SLinus Torvalds } 5561da177e4SLinus Torvalds case RTC_ALM_READ: /* Read the present alarm time */ 5571da177e4SLinus Torvalds { 5581da177e4SLinus Torvalds if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT; 5591da177e4SLinus Torvalds if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT; 5601da177e4SLinus Torvalds 5611da177e4SLinus Torvalds wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600; 5621da177e4SLinus Torvalds wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60; 5631da177e4SLinus Torvalds wtime.tm_sec = ttime.tv_sec; 5641da177e4SLinus Torvalds 5651da177e4SLinus Torvalds break; 5661da177e4SLinus Torvalds } 5671da177e4SLinus Torvalds case RTC_IRQP_READ: /* Read the periodic IRQ rate. */ 5681da177e4SLinus Torvalds { 5691da177e4SLinus Torvalds return put_user(hp_sdc_rtc_freq, (unsigned long *)arg); 5701da177e4SLinus Torvalds } 5711da177e4SLinus Torvalds case RTC_IRQP_SET: /* Set periodic IRQ rate. */ 5721da177e4SLinus Torvalds { 5731da177e4SLinus Torvalds /* 5741da177e4SLinus Torvalds * The max we can do is 100Hz. 5751da177e4SLinus Torvalds */ 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds if ((arg < 1) || (arg > 100)) return -EINVAL; 5781da177e4SLinus Torvalds ttime.tv_sec = 0; 5791da177e4SLinus Torvalds ttime.tv_usec = 1000000 / arg; 5801da177e4SLinus Torvalds if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT; 5811da177e4SLinus Torvalds hp_sdc_rtc_freq = arg; 5821da177e4SLinus Torvalds return 0; 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds case RTC_ALM_SET: /* Store a time into the alarm */ 5851da177e4SLinus Torvalds { 5861da177e4SLinus Torvalds /* 5871da177e4SLinus Torvalds * This expects a struct hp_sdc_rtc_time. Writing 0xff means 5881da177e4SLinus Torvalds * "don't care" or "match all" for PC timers. The HP SDC 5891da177e4SLinus Torvalds * does not support that perk, but it could be emulated fairly 5901da177e4SLinus Torvalds * easily. Only the tm_hour, tm_min and tm_sec are used. 5911da177e4SLinus Torvalds * We could do it with 10ms accuracy with the HP SDC, if the 5921da177e4SLinus Torvalds * rtc interface left us a way to do that. 5931da177e4SLinus Torvalds */ 5941da177e4SLinus Torvalds struct hp_sdc_rtc_time alm_tm; 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg, 5971da177e4SLinus Torvalds sizeof(struct hp_sdc_rtc_time))) 5981da177e4SLinus Torvalds return -EFAULT; 5991da177e4SLinus Torvalds 6001da177e4SLinus Torvalds if (alm_tm.tm_hour > 23) return -EINVAL; 6011da177e4SLinus Torvalds if (alm_tm.tm_min > 59) return -EINVAL; 6021da177e4SLinus Torvalds if (alm_tm.tm_sec > 59) return -EINVAL; 6031da177e4SLinus Torvalds 6041da177e4SLinus Torvalds ttime.sec = alm_tm.tm_hour * 3600 + 6051da177e4SLinus Torvalds alm_tm.tm_min * 60 + alm_tm.tm_sec; 6061da177e4SLinus Torvalds ttime.usec = 0; 6071da177e4SLinus Torvalds if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT; 6081da177e4SLinus Torvalds return 0; 6091da177e4SLinus Torvalds } 6101da177e4SLinus Torvalds case RTC_RD_TIME: /* Read the time/date from RTC */ 6111da177e4SLinus Torvalds { 6121da177e4SLinus Torvalds if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT; 6131da177e4SLinus Torvalds break; 6141da177e4SLinus Torvalds } 6151da177e4SLinus Torvalds case RTC_SET_TIME: /* Set the RTC */ 6161da177e4SLinus Torvalds { 6171da177e4SLinus Torvalds struct rtc_time hp_sdc_rtc_tm; 6181da177e4SLinus Torvalds unsigned char mon, day, hrs, min, sec, leap_yr; 6191da177e4SLinus Torvalds unsigned int yrs; 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds if (!capable(CAP_SYS_TIME)) 6221da177e4SLinus Torvalds return -EACCES; 6231da177e4SLinus Torvalds if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg, 6241da177e4SLinus Torvalds sizeof(struct rtc_time))) 6251da177e4SLinus Torvalds return -EFAULT; 6261da177e4SLinus Torvalds 6271da177e4SLinus Torvalds yrs = hp_sdc_rtc_tm.tm_year + 1900; 6281da177e4SLinus Torvalds mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ 6291da177e4SLinus Torvalds day = hp_sdc_rtc_tm.tm_mday; 6301da177e4SLinus Torvalds hrs = hp_sdc_rtc_tm.tm_hour; 6311da177e4SLinus Torvalds min = hp_sdc_rtc_tm.tm_min; 6321da177e4SLinus Torvalds sec = hp_sdc_rtc_tm.tm_sec; 6331da177e4SLinus Torvalds 6341da177e4SLinus Torvalds if (yrs < 1970) 6351da177e4SLinus Torvalds return -EINVAL; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); 6381da177e4SLinus Torvalds 6391da177e4SLinus Torvalds if ((mon > 12) || (day == 0)) 6401da177e4SLinus Torvalds return -EINVAL; 6411da177e4SLinus Torvalds if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) 6421da177e4SLinus Torvalds return -EINVAL; 6431da177e4SLinus Torvalds if ((hrs >= 24) || (min >= 60) || (sec >= 60)) 6441da177e4SLinus Torvalds return -EINVAL; 6451da177e4SLinus Torvalds 6461da177e4SLinus Torvalds if ((yrs -= eH) > 255) /* They are unsigned */ 6471da177e4SLinus Torvalds return -EINVAL; 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds 6501da177e4SLinus Torvalds return 0; 6511da177e4SLinus Torvalds } 6521da177e4SLinus Torvalds case RTC_EPOCH_READ: /* Read the epoch. */ 6531da177e4SLinus Torvalds { 6541da177e4SLinus Torvalds return put_user (epoch, (unsigned long *)arg); 6551da177e4SLinus Torvalds } 6561da177e4SLinus Torvalds case RTC_EPOCH_SET: /* Set the epoch. */ 6571da177e4SLinus Torvalds { 6581da177e4SLinus Torvalds /* 6591da177e4SLinus Torvalds * There were no RTC clocks before 1900. 6601da177e4SLinus Torvalds */ 6611da177e4SLinus Torvalds if (arg < 1900) 6621da177e4SLinus Torvalds return -EINVAL; 6631da177e4SLinus Torvalds if (!capable(CAP_SYS_TIME)) 6641da177e4SLinus Torvalds return -EACCES; 6651da177e4SLinus Torvalds 6661da177e4SLinus Torvalds epoch = arg; 6671da177e4SLinus Torvalds return 0; 6681da177e4SLinus Torvalds } 6691da177e4SLinus Torvalds default: 6701da177e4SLinus Torvalds return -EINVAL; 6711da177e4SLinus Torvalds } 6721da177e4SLinus Torvalds return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; 6731da177e4SLinus Torvalds #endif 6741da177e4SLinus Torvalds } 6751da177e4SLinus Torvalds 6762b8693c0SArjan van de Ven static const struct file_operations hp_sdc_rtc_fops = { 6771da177e4SLinus Torvalds .owner = THIS_MODULE, 67870c00ba0SMarcelo Tosatti .llseek = no_llseek, 6791da177e4SLinus Torvalds .read = hp_sdc_rtc_read, 6801da177e4SLinus Torvalds .poll = hp_sdc_rtc_poll, 6811da177e4SLinus Torvalds .ioctl = hp_sdc_rtc_ioctl, 6821da177e4SLinus Torvalds .open = hp_sdc_rtc_open, 6831da177e4SLinus Torvalds .release = hp_sdc_rtc_release, 6841da177e4SLinus Torvalds .fasync = hp_sdc_rtc_fasync, 6851da177e4SLinus Torvalds }; 6861da177e4SLinus Torvalds 6871da177e4SLinus Torvalds static struct miscdevice hp_sdc_rtc_dev = { 6881da177e4SLinus Torvalds .minor = RTC_MINOR, 6891da177e4SLinus Torvalds .name = "rtc_HIL", 6901da177e4SLinus Torvalds .fops = &hp_sdc_rtc_fops 6911da177e4SLinus Torvalds }; 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds static int __init hp_sdc_rtc_init(void) 6941da177e4SLinus Torvalds { 6951da177e4SLinus Torvalds int ret; 6961da177e4SLinus Torvalds 697eb98630bSGeert Uytterhoeven #ifdef __mc68000__ 698eb98630bSGeert Uytterhoeven if (!MACH_IS_HP300) 699eb98630bSGeert Uytterhoeven return -ENODEV; 700eb98630bSGeert Uytterhoeven #endif 701eb98630bSGeert Uytterhoeven 7021da177e4SLinus Torvalds init_MUTEX(&i8042tregs); 7031da177e4SLinus Torvalds 7041da177e4SLinus Torvalds if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr))) 7051da177e4SLinus Torvalds return ret; 7065d469ec0SNeil Horman if (misc_register(&hp_sdc_rtc_dev) != 0) 7075d469ec0SNeil Horman printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n"); 7085d469ec0SNeil Horman 7096ce6b3aeSAl Viro create_proc_read_entry ("driver/rtc", 0, NULL, 7101da177e4SLinus Torvalds hp_sdc_rtc_read_proc, NULL); 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded " 7131da177e4SLinus Torvalds "(RTC v " RTC_VERSION ")\n"); 7141da177e4SLinus Torvalds 7151da177e4SLinus Torvalds return 0; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds 7181da177e4SLinus Torvalds static void __exit hp_sdc_rtc_exit(void) 7191da177e4SLinus Torvalds { 7201da177e4SLinus Torvalds remove_proc_entry ("driver/rtc", NULL); 7211da177e4SLinus Torvalds misc_deregister(&hp_sdc_rtc_dev); 7221da177e4SLinus Torvalds hp_sdc_release_timer_irq(hp_sdc_rtc_isr); 7231da177e4SLinus Torvalds printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n"); 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 7261da177e4SLinus Torvalds module_init(hp_sdc_rtc_init); 7271da177e4SLinus Torvalds module_exit(hp_sdc_rtc_exit); 728