[RFC] m68k/parisc: Convert hp_sdc_rtc driver to rtc framework
diff mbox series

Message ID 20181127210107.GA11661@ls3530.dellerweb.de
State New
Headers show
Series
  • [RFC] m68k/parisc: Convert hp_sdc_rtc driver to rtc framework
Related show

Commit Message

Helge Deller Nov. 27, 2018, 9:01 p.m. UTC
This patch is a first step to convert the hp_sdc_rtc driver to the new
rtc framework.

The HP SDC RTC driver is available on HP machines only, which run either
a parisc or a m68k processor.  I did tested this patch on a parisc
machine, but it would be nice if someone with a m68k machine could test
it too.
    
Signed-off-by: Helge Deller <deller@gmx.de>

Comments

Finn Thain Nov. 27, 2018, 9:17 p.m. UTC | #1
Adding Kars to Cc.

On Tue, 27 Nov 2018, Helge Deller wrote:

> This patch is a first step to convert the hp_sdc_rtc driver to the new
> rtc framework.
> 
> The HP SDC RTC driver is available on HP machines only, which run either
> a parisc or a m68k processor.  I did tested this patch on a parisc
> machine, but it would be nice if someone with a m68k machine could test
> it too.
>     
> Signed-off-by: Helge Deller <deller@gmx.de>
> 
> diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
> index 47eb8ca729fe..c787bde12fce 100644
> --- a/drivers/input/misc/hp_sdc_rtc.c
> +++ b/drivers/input/misc/hp_sdc_rtc.c
> @@ -34,51 +34,27 @@
>   */
>  
>  #include <linux/hp_sdc.h>
> -#include <linux/errno.h>
> -#include <linux/types.h>
> -#include <linux/init.h>
>  #include <linux/module.h>
> -#include <linux/time.h>
> -#include <linux/miscdevice.h>
> -#include <linux/proc_fs.h>
> -#include <linux/seq_file.h>
> -#include <linux/poll.h>
>  #include <linux/rtc.h>
> -#include <linux/mutex.h>
>  #include <linux/semaphore.h>
>  
>  MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
>  MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
>  MODULE_LICENSE("Dual BSD/GPL");
>  
> -#define RTC_VERSION "1.10d"
> +#define RTC_VERSION "1.2"
>  
> -static DEFINE_MUTEX(hp_sdc_rtc_mutex);
> -static unsigned long epoch = 2000;
> +static struct rtc_device *rtc;
>  
>  static struct semaphore i8042tregs;
>  
> -static hp_sdc_irqhook hp_sdc_rtc_isr;
> -
> -static struct fasync_struct *hp_sdc_rtc_async_queue;
> -
>  static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
>  
> -static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
> -			       size_t count, loff_t *ppos);
> -
> -static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
> -				      unsigned int cmd, unsigned long arg);
> -
> -static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
> -
> -static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
> -static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
> -
>  static void hp_sdc_rtc_isr (int irq, void *dev_id, 
>  			    uint8_t status, uint8_t data) 
>  {
> -	return;
> +	/* Notify RTC core on alarm event */
> +	rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
>  }
>  
>  static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
> @@ -105,17 +81,18 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
>  	t.act.semaphore =	&tsem;
>  	sema_init(&tsem, 0);
>  	
> -	if (hp_sdc_enqueue_transaction(&t)) return -1;
> +	if (hp_sdc_enqueue_transaction(&t))
> +		return -EIO;
>  	
>  	/* Put ourselves to sleep for results. */
>  	if (WARN_ON(down_interruptible(&tsem)))
> -		return -1;
> +		return -EIO;
>  	
>  	/* Check for nonpresence of BBRTC */
>  	if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
>  	       tseq[55] | tseq[62] | tseq[34] | tseq[41] |
>  	       tseq[20] | tseq[27] | tseq[6]  | tseq[13]) & 0x0f))
> -		return -1;
> +		return -ENODEV;
>  
>  	memset(rtctm, 0, sizeof(struct rtc_time));
>  	rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
> @@ -132,17 +109,24 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
>  static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
>  {
>  	struct rtc_time tm, tm_last;
> -	int i = 0;
> +	int i = 0, ret;
>  
>  	/* MSM-58321 has no read latch, so must read twice and compare. */
>  
> -	if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
> -	if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
> +	ret = hp_sdc_rtc_do_read_bbrtc(&tm_last);
> +	if (ret)
> +		return ret;
> +	ret = hp_sdc_rtc_do_read_bbrtc(&tm);
> +	if (ret)
> +		return ret;
>  
>  	while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
> -		if (i++ > 4) return -1;
> +		if (i++ > 4)
> +			return -EAGAIN;
>  		memcpy(&tm_last, &tm, sizeof(struct rtc_time));
> -		if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
> +		ret = hp_sdc_rtc_do_read_bbrtc(&tm);
> +		if (ret)
> +			return ret;
>  	}
>  
>  	memcpy(rtctm, &tm, sizeof(struct rtc_time));
> @@ -150,7 +134,6 @@ static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
>  	return 0;
>  }
>  
> -
>  static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
>  {
>  	hp_sdc_transaction t;
> @@ -178,16 +161,16 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
>  
>  	/* Sleep if output regs in use. */
>  	if (WARN_ON(down_interruptible(&i8042tregs)))
> -		return -1;
> +		return -EIO;
>  
>  	if (hp_sdc_enqueue_transaction(&t)) {
>  		up(&i8042tregs);
> -		return -1;
> +		return -EIO;
>  	}
>  	
>  	/* Sleep until results come back. */
>  	if (WARN_ON(down_interruptible(&i8042tregs)))
> -		return -1;
> +		return -EIO;
>  
>  	up(&i8042tregs);
>  
> @@ -198,13 +181,15 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
>  
>  
>  /* Read the i8042 real-time clock */
> -static inline int hp_sdc_rtc_read_rt(struct timespec64 *res) {
> +static int hp_sdc_rtc_read_rt(struct timespec64 *res)
> +{
>  	int64_t raw;
>  	uint32_t tenms; 
>  	unsigned int days;
>  
>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
> -	if (raw < 0) return -1;
> +	if (raw < 0)
> +		return -EIO;
>  
>  	tenms = (uint32_t)raw & 0xffffff;
>  	days  = (unsigned int)(raw >> 24) & 0xffff;
> @@ -222,7 +207,8 @@ static inline int hp_sdc_rtc_read_fhs(struct timespec64 *res) {
>  	unsigned int tenms;
>  
>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
> -	if (raw < 0) return -1;
> +	if (raw < 0)
> +		return -EIO;
>  
>  	tenms = (unsigned int)raw & 0xffff;
>  
> @@ -239,7 +225,8 @@ static inline int hp_sdc_rtc_read_mt(struct timespec64 *res) {
>  	uint32_t tenms; 
>  
>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
> -	if (raw < 0) return -1;
> +	if (raw < 0)
> +		return -EIO;
>  
>  	tenms = (uint32_t)raw & 0xffffff;
>  
> @@ -256,7 +243,8 @@ static inline int hp_sdc_rtc_read_dt(struct timespec64 *res) {
>  	uint32_t tenms;
>  
>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
> -	if (raw < 0) return -1;
> +	if (raw < 0)
> +		return -EIO;
>  
>  	tenms = (uint32_t)raw & 0xffffff;
>  
> @@ -273,7 +261,8 @@ static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
>  	uint32_t tenms;
>  
>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
> -	if (raw < 0) return -1;
> +	if (raw < 0)
> +		return -EIO;
>  
>  	tenms = (uint32_t)raw & 0xffffff;
>  
> @@ -284,11 +273,11 @@ static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
>  }
>  
>  
> -#if 0 /* not used yet */
>  /* Set the i8042 real-time clock */
> -static int hp_sdc_rtc_set_rt (struct timeval *setto)
> +static int hp_sdc_rtc_set_rt(struct rtc_time *tm)
>  {
>  	uint32_t tenms;
> +	time64_t seconds;
>  	unsigned int days;
>  	hp_sdc_transaction t;
>  	uint8_t tseq[11] = {
> @@ -300,17 +289,13 @@ static int hp_sdc_rtc_set_rt (struct timeval *setto)
>  
>  	t.endidx = 10;
>  
> -	if (0xffff < setto->tv_sec / 86400) return -1;
> -	days = setto->tv_sec / 86400;
> -	if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
> -	days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
> -	if (days > 0xffff) return -1;
> -
> -	if (0xffffff < setto->tv_sec) return -1;
> -	tenms  = setto->tv_sec * 100;
> -	if (0xffffff < setto->tv_usec / 10000) return -1;
> -	tenms += setto->tv_usec / 10000;
> -	if (tenms > 0xffffff) return -1;
> +	seconds = rtc_tm_to_time64(tm);
> +	days = seconds / 86400;
> +	if (days > 0xffff)
> +		return -EINVAL;
> +	tenms  = (seconds % 86400) * 100;
> +	if (tenms > 0xffffff)
> +		return -EINVAL;
>  
>  	tseq[3] = (uint8_t)(tenms & 0xff);
>  	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
> @@ -321,12 +306,14 @@ static int hp_sdc_rtc_set_rt (struct timeval *setto)
>  
>  	t.seq =	tseq;
>  
> -	if (hp_sdc_enqueue_transaction(&t)) return -1;
> +	if (hp_sdc_enqueue_transaction(&t))
> +		return -EIO;
>  	return 0;
>  }
>  
>  /* Set the i8042 fast handshake timer */
> -static int hp_sdc_rtc_set_fhs (struct timeval *setto)
> +#if 0
> +static int hp_sdc_rtc_set_fhs(struct timespec64 *setto)
>  {
>  	uint32_t tenms;
>  	hp_sdc_transaction t;
> @@ -337,36 +324,36 @@ static int hp_sdc_rtc_set_fhs (struct timeval *setto)
>  
>  	t.endidx = 4;
>  
> -	if (0xffff < setto->tv_sec) return -1;
>  	tenms  = setto->tv_sec * 100;
> -	if (0xffff < setto->tv_usec / 10000) return -1;
> -	tenms += setto->tv_usec / 10000;
> -	if (tenms > 0xffff) return -1;
> +	tenms += setto->tv_nsec / 10000 / 1000;
> +	if (tenms > 0xffff)
> +		return -EINVAL;
>  
>  	tseq[3] = (uint8_t)(tenms & 0xff);
>  	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
>  
>  	t.seq =	tseq;
>  
> -	if (hp_sdc_enqueue_transaction(&t)) return -1;
> +	if (hp_sdc_enqueue_transaction(&t))
> +		return -EIO;
>  	return 0;
>  }
> -
> +#endif
>  
>  /* Set the i8042 match timer (a.k.a. alarm) */
> -#define hp_sdc_rtc_set_mt (setto) \
> +#define hp_sdc_rtc_set_mt(setto) \
>  	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
>  
>  /* Set the i8042 delay timer */
> -#define hp_sdc_rtc_set_dt (setto) \
> +#define hp_sdc_rtc_set_dt(setto) \
>  	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
>  
>  /* Set the i8042 cycle timer (a.k.a. periodic) */
> -#define hp_sdc_rtc_set_ct (setto) \
> +#define hp_sdc_rtc_set_ct(setto) \
>  	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
>  
>  /* Set one of the i8042 3-byte wide timers */
> -static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
> +static int hp_sdc_rtc_set_i8042timer(struct timespec64 *setto, uint8_t setcmd)
>  {
>  	uint32_t tenms;
>  	hp_sdc_transaction t;
> @@ -377,58 +364,96 @@ static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
>  
>  	t.endidx = 6;
>  
> -	if (0xffffff < setto->tv_sec) return -1;
> +	if (setto->tv_sec > 0xffffff)
> +		return -EINVAL;
>  	tenms  = setto->tv_sec * 100;
> -	if (0xffffff < setto->tv_usec / 10000) return -1;
> -	tenms += setto->tv_usec / 10000;
> -	if (tenms > 0xffffff) return -1;
> +	if (setto->tv_nsec / 10000 / 1000 > 0xffffff)
> +		return -EINVAL;
> +	tenms += setto->tv_nsec / 10000 / 1000;
> +	if (tenms > 0xffffff)
> +		return -EINVAL;
>  
>  	tseq[1] = setcmd;
>  	tseq[3] = (uint8_t)(tenms & 0xff);
>  	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
>  	tseq[5] = (uint8_t)((tenms >> 16)  & 0xff);
>  
> -	t.seq =			tseq;
> +	t.seq =	tseq;
>  
>  	if (hp_sdc_enqueue_transaction(&t)) { 
> -		return -1;
> +		return -EIO;
>  	}
>  	return 0;
>  }
> -#endif
>  
> -static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
> -			       size_t count, loff_t *ppos) {
> -	ssize_t retval;
> -
> -        if (count < sizeof(unsigned long))
> -                return -EINVAL;
> -
> -	retval = put_user(68, (unsigned long __user *)buf);
> -	return retval;
> +static int hp_sdc_rtc_get_time(struct device *dev, struct rtc_time *tm)
> +{
> +	return hp_sdc_rtc_read_bbrtc(tm);
>  }
>  
> -static __poll_t hp_sdc_rtc_poll(struct file *file, poll_table *wait)
> +static int hp_sdc_rtc_set_time(struct device *dev, struct rtc_time *tm)
>  {
> -        unsigned long l;
> +	if (!rtc_valid_tm(tm))
> +		return -EINVAL;
>  
> -	l = 0;
> -        if (l != 0)
> -                return EPOLLIN | EPOLLRDNORM;
> -        return 0;
> +	return hp_sdc_rtc_set_rt(tm);
>  }
>  
> -static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
> +static int hp_sdc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wa)
>  {
> -        return 0;
> +	/* Read the present alarm time */
> +	int ret;
> +	struct timespec64 ttime;
> +	struct rtc_time *wtime = &wa->time;
> +
> +	ret = hp_sdc_rtc_read_mt(&ttime);
> +	if (ret)
> +		return ret;
> +	ret = hp_sdc_rtc_read_bbrtc(wtime);
> +	if (ret)
> +		return ret;
> +
> +	wtime->tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
> +	wtime->tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
> +	wtime->tm_sec  = ttime.tv_sec;
> +
> +	wa->enabled = 0;
> +	wa->pending = 0;
> +
> +	return 0;
>  }
>  
> -static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
> +static int hp_sdc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wa)
>  {
> -        return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
> +	/* Set the alarm time */
> +	struct timespec64 ttime;
> +
> +	/*
> +	 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
> +	 * "don't care" or "match all" for PC timers.  The HP SDC
> +	 * does not support that perk, but it could be emulated fairly
> +	 * easily.  Only the tm_hour, tm_min and tm_sec are used.
> +	 * We could do it with 10ms accuracy with the HP SDC, if the
> +	 * rtc interface left us a way to do that.
> +	 */
> +
> +	if (wa->time.tm_hour > 23)
> +		return -EINVAL;
> +	if (wa->time.tm_min  > 59)
> +		return -EINVAL;
> +	if (wa->time.tm_sec  > 59)
> +		return -EINVAL;
> +
> +	ttime.tv_sec = wa->time.tm_hour * 3600 +
> +	  wa->time.tm_min * 60 + wa->time.tm_sec;
> +	ttime.tv_nsec = 0;
> +	if (hp_sdc_rtc_set_mt(&ttime))
> +		return -EIO;
> +
> +	return 0;
>  }
>  
> -static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
> +static int hp_sdc_rtc_proc(struct device *dev, struct seq_file *m)
>  {
>  #define YN(bit) ("no")
>  #define NY(bit) ("yes")
> @@ -442,11 +467,10 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
>  	} else {
>  		seq_printf(m,
>  			     "rtc_time\t: %02d:%02d:%02d\n"
> -			     "rtc_date\t: %04d-%02d-%02d\n"
> -			     "rtc_epoch\t: %04lu\n",
> +			     "rtc_date\t: %04d-%02d-%02d\n",
>  			     tm.tm_hour, tm.tm_min, tm.tm_sec,
>  			     tm.tm_year + 1900, tm.tm_mon + 1, 
> -			     tm.tm_mday, epoch);
> +			     tm.tm_mday);
>  	}
>  
>  	if (hp_sdc_rtc_read_rt(&tv)) {
> @@ -509,185 +533,42 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
>  #undef NY
>  }
>  
> -static int hp_sdc_rtc_ioctl(struct file *file, 
> +static int hp_sdc_rtc_ioctl(struct device *dev,
>  			    unsigned int cmd, unsigned long arg)
>  {
> -#if 1
> -	return -EINVAL;
> -#else
> -	
> -        struct rtc_time wtime; 
> -	struct timeval ttime;
> -	int use_wtime = 0;
> -
> -	/* This needs major work. */
> -
> -        switch (cmd) {
> -
> -        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
> -        case RTC_AIE_ON:        /* Allow alarm interrupts.      */
> +	switch (cmd) {
> +	case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
> +	case RTC_AIE_ON:        /* Allow alarm interrupts.      */
>  	case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
> -        case RTC_PIE_ON:        /* Allow periodic ints          */
> -        case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
> -        case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
> -        {
> +	case RTC_PIE_ON:        /* Allow periodic ints          */
> +	case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
> +	case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
> +	{
>  		/* We cannot mask individual user timers and we
>  		   cannot tell them apart when they occur, so it 
>  		   would be disingenuous to succeed these IOCTLs */
>  		return -EINVAL;
> -        }
> -        case RTC_ALM_READ:      /* Read the present alarm time */
> -        {
> -		if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
> -		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
> -
> -		wtime.tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
> -		wtime.tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
> -		wtime.tm_sec  = ttime.tv_sec;
> -                
> -		break;
> -        }
> -        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
> -        {
> -                return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
> -        }
> -        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
> -        {
> -                /* 
> -                 * The max we can do is 100Hz.
> -		 */
> -
> -                if ((arg < 1) || (arg > 100)) return -EINVAL;
> -		ttime.tv_sec = 0;
> -		ttime.tv_usec = 1000000 / arg;
> -		if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
> -		hp_sdc_rtc_freq = arg;
> -                return 0;
> -        }
> -        case RTC_ALM_SET:       /* Store a time into the alarm */
> -        {
> -                /*
> -                 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
> -                 * "don't care" or "match all" for PC timers.  The HP SDC
> -		 * does not support that perk, but it could be emulated fairly
> -		 * easily.  Only the tm_hour, tm_min and tm_sec are used.
> -		 * We could do it with 10ms accuracy with the HP SDC, if the 
> -		 * rtc interface left us a way to do that.
> -                 */
> -                struct hp_sdc_rtc_time alm_tm;
> -
> -                if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
> -                                   sizeof(struct hp_sdc_rtc_time)))
> -                       return -EFAULT;
> -
> -                if (alm_tm.tm_hour > 23) return -EINVAL;
> -		if (alm_tm.tm_min  > 59) return -EINVAL;
> -		if (alm_tm.tm_sec  > 59) return -EINVAL;  
> -
> -		ttime.sec = alm_tm.tm_hour * 3600 + 
> -		  alm_tm.tm_min * 60 + alm_tm.tm_sec;
> -		ttime.usec = 0;
> -		if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
> -                return 0;
> -        }
> -        case RTC_RD_TIME:       /* Read the time/date from RTC  */
> -        {
> -		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
> -                break;
> -        }
> -        case RTC_SET_TIME:      /* Set the RTC */
> -        {
> -                struct rtc_time hp_sdc_rtc_tm;
> -                unsigned char mon, day, hrs, min, sec, leap_yr;
> -                unsigned int yrs;
> -
> -                if (!capable(CAP_SYS_TIME))
> -                        return -EACCES;
> -		if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
> -                                   sizeof(struct rtc_time)))
> -                        return -EFAULT;
> -
> -                yrs = hp_sdc_rtc_tm.tm_year + 1900;
> -                mon = hp_sdc_rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
> -                day = hp_sdc_rtc_tm.tm_mday;
> -                hrs = hp_sdc_rtc_tm.tm_hour;
> -                min = hp_sdc_rtc_tm.tm_min;
> -                sec = hp_sdc_rtc_tm.tm_sec;
> -
> -                if (yrs < 1970)
> -                        return -EINVAL;
> -
> -                leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
> -
> -                if ((mon > 12) || (day == 0))
> -                        return -EINVAL;
> -                if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
> -                        return -EINVAL;
> -		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
> -                        return -EINVAL;
> -
> -                if ((yrs -= eH) > 255)    /* They are unsigned */
> -                        return -EINVAL;
> -
> -
> -                return 0;
> -        }
> -        case RTC_EPOCH_READ:    /* Read the epoch.      */
> -        {
> -                return put_user (epoch, (unsigned long *)arg);
> -        }
> -        case RTC_EPOCH_SET:     /* Set the epoch.       */
> -        {
> -                /* 
> -                 * There were no RTC clocks before 1900.
> -                 */
> -                if (arg < 1900)
> -		  return -EINVAL;
> -		if (!capable(CAP_SYS_TIME))
> -		  return -EACCES;
> -		
> -                epoch = arg;
> -                return 0;
> -        }
> -        default:
> -                return -EINVAL;
> -        }
> -        return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
> -#endif
> -}
> -
> -static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
> -				      unsigned int cmd, unsigned long arg)
> -{
> -	int ret;
> -
> -	mutex_lock(&hp_sdc_rtc_mutex);
> -	ret = hp_sdc_rtc_ioctl(file, cmd, arg);
> -	mutex_unlock(&hp_sdc_rtc_mutex);
> -
> -	return ret;
> +	}
> +	default:
> +		return -ENOIOCTLCMD;
> +	}
> +	return 0;
>  }
>  
> -
> -static const struct file_operations hp_sdc_rtc_fops = {
> -        .owner =		THIS_MODULE,
> -        .llseek =		no_llseek,
> -        .read =			hp_sdc_rtc_read,
> -        .poll =			hp_sdc_rtc_poll,
> -        .unlocked_ioctl =	hp_sdc_rtc_unlocked_ioctl,
> -        .open =			hp_sdc_rtc_open,
> -        .fasync =		hp_sdc_rtc_fasync,
> -};
> -
> -static struct miscdevice hp_sdc_rtc_dev = {
> -        .minor =	RTC_MINOR,
> -        .name =		"rtc_HIL",
> -        .fops =		&hp_sdc_rtc_fops
> +static const struct rtc_class_ops hp_sdc_rtc_ops = {
> +	.ioctl = hp_sdc_rtc_ioctl,
> +	.proc = hp_sdc_rtc_proc,
> +	.read_time = hp_sdc_rtc_get_time,
> +	.set_time = hp_sdc_rtc_set_time,
> +	.read_alarm = hp_sdc_rtc_read_alarm,
> +	.set_alarm = hp_sdc_rtc_set_alarm,
> +	// int (*alarm_irq_enable)(struct device *, unsigned int enabled);
>  };
>  
>  static int __init hp_sdc_rtc_init(void)
>  {
>  	int ret;
> +	struct rtc_time tm;
>  
>  #ifdef __mc68000__
>  	if (!MACH_IS_HP300)
> @@ -696,14 +577,22 @@ static int __init hp_sdc_rtc_init(void)
>  
>  	sema_init(&i8042tregs, 1);
>  
> +	/* check if BBRTC is available */
> +	ret = hp_sdc_rtc_read_bbrtc(&tm);
> +	if (ret) {
> +		pr_info("hil-rtc: BBRTC not available.");
> +		return -ENODEV;
> +	}
> +
>  	if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
>  		return ret;
> -	if (misc_register(&hp_sdc_rtc_dev) != 0)
> -		printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
>  
> -        proc_create_single("driver/rtc", 0, NULL, hp_sdc_rtc_proc_show);
> +	rtc = devm_rtc_device_register(hp_sdc_get_sdc_device(),
> +			"hil-rtc", &hp_sdc_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rtc))
> +		return PTR_ERR(rtc);
>  
> -	printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
> +	pr_info("hil-rtc: HP i8042 SDC + MSM-58321 RTC support loaded "
>  			 "(RTC v " RTC_VERSION ")\n");
>  
>  	return 0;
> @@ -711,10 +600,9 @@ static int __init hp_sdc_rtc_init(void)
>  
>  static void __exit hp_sdc_rtc_exit(void)
>  {
> -	remove_proc_entry ("driver/rtc", NULL);
> -        misc_deregister(&hp_sdc_rtc_dev);
> +	devm_rtc_device_unregister(hp_sdc_get_sdc_device(), rtc);
>  	hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
> -        printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
> +	pr_info("hil-rtc: HP i8042 SDC + MSM-58321 RTC support unloaded\n");
>  }
>  
>  module_init(hp_sdc_rtc_init);
> diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
> index 0b8a25c58d02..3e0ad6a4907f 100644
> --- a/drivers/input/serio/hp_sdc.c
> +++ b/drivers/input/serio/hp_sdc.c
> @@ -110,6 +110,17 @@ MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver.");
>  
>  static hp_i8042_sdc	hp_sdc;	/* All driver state is kept in here. */
>  
> +struct device *hp_sdc_get_sdc_device(void)
> +{
> +#if defined(__hppa__)
> +	return &hp_sdc.dev->dev;
> +#elif defined(__mc68000__)
> +	/* hp_sdc.dev is 1, so return NULL instead. */
> +	return NULL;
> +#endif
> +}
> +EXPORT_SYMBOL(hp_sdc_get_sdc_device);
> +
>  /*************** primitives for use in any context *********************/
>  static inline uint8_t hp_sdc_status_in8(void)
>  {
> diff --git a/include/linux/hp_sdc.h b/include/linux/hp_sdc.h
> index 6f1dee7e67e0..8988e32adebd 100644
> --- a/include/linux/hp_sdc.h
> +++ b/include/linux/hp_sdc.h
> @@ -298,4 +298,6 @@ typedef struct {
>  
>  } hp_i8042_sdc;
>  
> +struct device *hp_sdc_get_sdc_device(void);
> +
>  #endif /* _LINUX_HP_SDC_H */
>
Helge Deller Jan. 7, 2019, 7:31 a.m. UTC | #2
On 27.11.18 22:17, Finn Thain wrote:
> Adding Kars to Cc.

Any chance this can get tested on m68k?

Helge

> On Tue, 27 Nov 2018, Helge Deller wrote:
> 
>> This patch is a first step to convert the hp_sdc_rtc driver to the new
>> rtc framework.
>>
>> The HP SDC RTC driver is available on HP machines only, which run either
>> a parisc or a m68k processor.  I did tested this patch on a parisc
>> machine, but it would be nice if someone with a m68k machine could test
>> it too.
>>     
>> Signed-off-by: Helge Deller <deller@gmx.de>
>>
>> diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
>> index 47eb8ca729fe..c787bde12fce 100644
>> --- a/drivers/input/misc/hp_sdc_rtc.c
>> +++ b/drivers/input/misc/hp_sdc_rtc.c
>> @@ -34,51 +34,27 @@
>>   */
>>  
>>  #include <linux/hp_sdc.h>
>> -#include <linux/errno.h>
>> -#include <linux/types.h>
>> -#include <linux/init.h>
>>  #include <linux/module.h>
>> -#include <linux/time.h>
>> -#include <linux/miscdevice.h>
>> -#include <linux/proc_fs.h>
>> -#include <linux/seq_file.h>
>> -#include <linux/poll.h>
>>  #include <linux/rtc.h>
>> -#include <linux/mutex.h>
>>  #include <linux/semaphore.h>
>>  
>>  MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
>>  MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
>>  MODULE_LICENSE("Dual BSD/GPL");
>>  
>> -#define RTC_VERSION "1.10d"
>> +#define RTC_VERSION "1.2"
>>  
>> -static DEFINE_MUTEX(hp_sdc_rtc_mutex);
>> -static unsigned long epoch = 2000;
>> +static struct rtc_device *rtc;
>>  
>>  static struct semaphore i8042tregs;
>>  
>> -static hp_sdc_irqhook hp_sdc_rtc_isr;
>> -
>> -static struct fasync_struct *hp_sdc_rtc_async_queue;
>> -
>>  static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
>>  
>> -static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
>> -			       size_t count, loff_t *ppos);
>> -
>> -static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
>> -				      unsigned int cmd, unsigned long arg);
>> -
>> -static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
>> -
>> -static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
>> -static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
>> -
>>  static void hp_sdc_rtc_isr (int irq, void *dev_id, 
>>  			    uint8_t status, uint8_t data) 
>>  {
>> -	return;
>> +	/* Notify RTC core on alarm event */
>> +	rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
>>  }
>>  
>>  static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
>> @@ -105,17 +81,18 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
>>  	t.act.semaphore =	&tsem;
>>  	sema_init(&tsem, 0);
>>  	
>> -	if (hp_sdc_enqueue_transaction(&t)) return -1;
>> +	if (hp_sdc_enqueue_transaction(&t))
>> +		return -EIO;
>>  	
>>  	/* Put ourselves to sleep for results. */
>>  	if (WARN_ON(down_interruptible(&tsem)))
>> -		return -1;
>> +		return -EIO;
>>  	
>>  	/* Check for nonpresence of BBRTC */
>>  	if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
>>  	       tseq[55] | tseq[62] | tseq[34] | tseq[41] |
>>  	       tseq[20] | tseq[27] | tseq[6]  | tseq[13]) & 0x0f))
>> -		return -1;
>> +		return -ENODEV;
>>  
>>  	memset(rtctm, 0, sizeof(struct rtc_time));
>>  	rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
>> @@ -132,17 +109,24 @@ static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
>>  static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
>>  {
>>  	struct rtc_time tm, tm_last;
>> -	int i = 0;
>> +	int i = 0, ret;
>>  
>>  	/* MSM-58321 has no read latch, so must read twice and compare. */
>>  
>> -	if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
>> -	if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
>> +	ret = hp_sdc_rtc_do_read_bbrtc(&tm_last);
>> +	if (ret)
>> +		return ret;
>> +	ret = hp_sdc_rtc_do_read_bbrtc(&tm);
>> +	if (ret)
>> +		return ret;
>>  
>>  	while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
>> -		if (i++ > 4) return -1;
>> +		if (i++ > 4)
>> +			return -EAGAIN;
>>  		memcpy(&tm_last, &tm, sizeof(struct rtc_time));
>> -		if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
>> +		ret = hp_sdc_rtc_do_read_bbrtc(&tm);
>> +		if (ret)
>> +			return ret;
>>  	}
>>  
>>  	memcpy(rtctm, &tm, sizeof(struct rtc_time));
>> @@ -150,7 +134,6 @@ static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
>>  	return 0;
>>  }
>>  
>> -
>>  static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
>>  {
>>  	hp_sdc_transaction t;
>> @@ -178,16 +161,16 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
>>  
>>  	/* Sleep if output regs in use. */
>>  	if (WARN_ON(down_interruptible(&i8042tregs)))
>> -		return -1;
>> +		return -EIO;
>>  
>>  	if (hp_sdc_enqueue_transaction(&t)) {
>>  		up(&i8042tregs);
>> -		return -1;
>> +		return -EIO;
>>  	}
>>  	
>>  	/* Sleep until results come back. */
>>  	if (WARN_ON(down_interruptible(&i8042tregs)))
>> -		return -1;
>> +		return -EIO;
>>  
>>  	up(&i8042tregs);
>>  
>> @@ -198,13 +181,15 @@ static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
>>  
>>  
>>  /* Read the i8042 real-time clock */
>> -static inline int hp_sdc_rtc_read_rt(struct timespec64 *res) {
>> +static int hp_sdc_rtc_read_rt(struct timespec64 *res)
>> +{
>>  	int64_t raw;
>>  	uint32_t tenms; 
>>  	unsigned int days;
>>  
>>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
>> -	if (raw < 0) return -1;
>> +	if (raw < 0)
>> +		return -EIO;
>>  
>>  	tenms = (uint32_t)raw & 0xffffff;
>>  	days  = (unsigned int)(raw >> 24) & 0xffff;
>> @@ -222,7 +207,8 @@ static inline int hp_sdc_rtc_read_fhs(struct timespec64 *res) {
>>  	unsigned int tenms;
>>  
>>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
>> -	if (raw < 0) return -1;
>> +	if (raw < 0)
>> +		return -EIO;
>>  
>>  	tenms = (unsigned int)raw & 0xffff;
>>  
>> @@ -239,7 +225,8 @@ static inline int hp_sdc_rtc_read_mt(struct timespec64 *res) {
>>  	uint32_t tenms; 
>>  
>>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
>> -	if (raw < 0) return -1;
>> +	if (raw < 0)
>> +		return -EIO;
>>  
>>  	tenms = (uint32_t)raw & 0xffffff;
>>  
>> @@ -256,7 +243,8 @@ static inline int hp_sdc_rtc_read_dt(struct timespec64 *res) {
>>  	uint32_t tenms;
>>  
>>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
>> -	if (raw < 0) return -1;
>> +	if (raw < 0)
>> +		return -EIO;
>>  
>>  	tenms = (uint32_t)raw & 0xffffff;
>>  
>> @@ -273,7 +261,8 @@ static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
>>  	uint32_t tenms;
>>  
>>  	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
>> -	if (raw < 0) return -1;
>> +	if (raw < 0)
>> +		return -EIO;
>>  
>>  	tenms = (uint32_t)raw & 0xffffff;
>>  
>> @@ -284,11 +273,11 @@ static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
>>  }
>>  
>>  
>> -#if 0 /* not used yet */
>>  /* Set the i8042 real-time clock */
>> -static int hp_sdc_rtc_set_rt (struct timeval *setto)
>> +static int hp_sdc_rtc_set_rt(struct rtc_time *tm)
>>  {
>>  	uint32_t tenms;
>> +	time64_t seconds;
>>  	unsigned int days;
>>  	hp_sdc_transaction t;
>>  	uint8_t tseq[11] = {
>> @@ -300,17 +289,13 @@ static int hp_sdc_rtc_set_rt (struct timeval *setto)
>>  
>>  	t.endidx = 10;
>>  
>> -	if (0xffff < setto->tv_sec / 86400) return -1;
>> -	days = setto->tv_sec / 86400;
>> -	if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
>> -	days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
>> -	if (days > 0xffff) return -1;
>> -
>> -	if (0xffffff < setto->tv_sec) return -1;
>> -	tenms  = setto->tv_sec * 100;
>> -	if (0xffffff < setto->tv_usec / 10000) return -1;
>> -	tenms += setto->tv_usec / 10000;
>> -	if (tenms > 0xffffff) return -1;
>> +	seconds = rtc_tm_to_time64(tm);
>> +	days = seconds / 86400;
>> +	if (days > 0xffff)
>> +		return -EINVAL;
>> +	tenms  = (seconds % 86400) * 100;
>> +	if (tenms > 0xffffff)
>> +		return -EINVAL;
>>  
>>  	tseq[3] = (uint8_t)(tenms & 0xff);
>>  	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
>> @@ -321,12 +306,14 @@ static int hp_sdc_rtc_set_rt (struct timeval *setto)
>>  
>>  	t.seq =	tseq;
>>  
>> -	if (hp_sdc_enqueue_transaction(&t)) return -1;
>> +	if (hp_sdc_enqueue_transaction(&t))
>> +		return -EIO;
>>  	return 0;
>>  }
>>  
>>  /* Set the i8042 fast handshake timer */
>> -static int hp_sdc_rtc_set_fhs (struct timeval *setto)
>> +#if 0
>> +static int hp_sdc_rtc_set_fhs(struct timespec64 *setto)
>>  {
>>  	uint32_t tenms;
>>  	hp_sdc_transaction t;
>> @@ -337,36 +324,36 @@ static int hp_sdc_rtc_set_fhs (struct timeval *setto)
>>  
>>  	t.endidx = 4;
>>  
>> -	if (0xffff < setto->tv_sec) return -1;
>>  	tenms  = setto->tv_sec * 100;
>> -	if (0xffff < setto->tv_usec / 10000) return -1;
>> -	tenms += setto->tv_usec / 10000;
>> -	if (tenms > 0xffff) return -1;
>> +	tenms += setto->tv_nsec / 10000 / 1000;
>> +	if (tenms > 0xffff)
>> +		return -EINVAL;
>>  
>>  	tseq[3] = (uint8_t)(tenms & 0xff);
>>  	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
>>  
>>  	t.seq =	tseq;
>>  
>> -	if (hp_sdc_enqueue_transaction(&t)) return -1;
>> +	if (hp_sdc_enqueue_transaction(&t))
>> +		return -EIO;
>>  	return 0;
>>  }
>> -
>> +#endif
>>  
>>  /* Set the i8042 match timer (a.k.a. alarm) */
>> -#define hp_sdc_rtc_set_mt (setto) \
>> +#define hp_sdc_rtc_set_mt(setto) \
>>  	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
>>  
>>  /* Set the i8042 delay timer */
>> -#define hp_sdc_rtc_set_dt (setto) \
>> +#define hp_sdc_rtc_set_dt(setto) \
>>  	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
>>  
>>  /* Set the i8042 cycle timer (a.k.a. periodic) */
>> -#define hp_sdc_rtc_set_ct (setto) \
>> +#define hp_sdc_rtc_set_ct(setto) \
>>  	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
>>  
>>  /* Set one of the i8042 3-byte wide timers */
>> -static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
>> +static int hp_sdc_rtc_set_i8042timer(struct timespec64 *setto, uint8_t setcmd)
>>  {
>>  	uint32_t tenms;
>>  	hp_sdc_transaction t;
>> @@ -377,58 +364,96 @@ static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
>>  
>>  	t.endidx = 6;
>>  
>> -	if (0xffffff < setto->tv_sec) return -1;
>> +	if (setto->tv_sec > 0xffffff)
>> +		return -EINVAL;
>>  	tenms  = setto->tv_sec * 100;
>> -	if (0xffffff < setto->tv_usec / 10000) return -1;
>> -	tenms += setto->tv_usec / 10000;
>> -	if (tenms > 0xffffff) return -1;
>> +	if (setto->tv_nsec / 10000 / 1000 > 0xffffff)
>> +		return -EINVAL;
>> +	tenms += setto->tv_nsec / 10000 / 1000;
>> +	if (tenms > 0xffffff)
>> +		return -EINVAL;
>>  
>>  	tseq[1] = setcmd;
>>  	tseq[3] = (uint8_t)(tenms & 0xff);
>>  	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
>>  	tseq[5] = (uint8_t)((tenms >> 16)  & 0xff);
>>  
>> -	t.seq =			tseq;
>> +	t.seq =	tseq;
>>  
>>  	if (hp_sdc_enqueue_transaction(&t)) { 
>> -		return -1;
>> +		return -EIO;
>>  	}
>>  	return 0;
>>  }
>> -#endif
>>  
>> -static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
>> -			       size_t count, loff_t *ppos) {
>> -	ssize_t retval;
>> -
>> -        if (count < sizeof(unsigned long))
>> -                return -EINVAL;
>> -
>> -	retval = put_user(68, (unsigned long __user *)buf);
>> -	return retval;
>> +static int hp_sdc_rtc_get_time(struct device *dev, struct rtc_time *tm)
>> +{
>> +	return hp_sdc_rtc_read_bbrtc(tm);
>>  }
>>  
>> -static __poll_t hp_sdc_rtc_poll(struct file *file, poll_table *wait)
>> +static int hp_sdc_rtc_set_time(struct device *dev, struct rtc_time *tm)
>>  {
>> -        unsigned long l;
>> +	if (!rtc_valid_tm(tm))
>> +		return -EINVAL;
>>  
>> -	l = 0;
>> -        if (l != 0)
>> -                return EPOLLIN | EPOLLRDNORM;
>> -        return 0;
>> +	return hp_sdc_rtc_set_rt(tm);
>>  }
>>  
>> -static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
>> +static int hp_sdc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wa)
>>  {
>> -        return 0;
>> +	/* Read the present alarm time */
>> +	int ret;
>> +	struct timespec64 ttime;
>> +	struct rtc_time *wtime = &wa->time;
>> +
>> +	ret = hp_sdc_rtc_read_mt(&ttime);
>> +	if (ret)
>> +		return ret;
>> +	ret = hp_sdc_rtc_read_bbrtc(wtime);
>> +	if (ret)
>> +		return ret;
>> +
>> +	wtime->tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
>> +	wtime->tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
>> +	wtime->tm_sec  = ttime.tv_sec;
>> +
>> +	wa->enabled = 0;
>> +	wa->pending = 0;
>> +
>> +	return 0;
>>  }
>>  
>> -static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
>> +static int hp_sdc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wa)
>>  {
>> -        return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
>> +	/* Set the alarm time */
>> +	struct timespec64 ttime;
>> +
>> +	/*
>> +	 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
>> +	 * "don't care" or "match all" for PC timers.  The HP SDC
>> +	 * does not support that perk, but it could be emulated fairly
>> +	 * easily.  Only the tm_hour, tm_min and tm_sec are used.
>> +	 * We could do it with 10ms accuracy with the HP SDC, if the
>> +	 * rtc interface left us a way to do that.
>> +	 */
>> +
>> +	if (wa->time.tm_hour > 23)
>> +		return -EINVAL;
>> +	if (wa->time.tm_min  > 59)
>> +		return -EINVAL;
>> +	if (wa->time.tm_sec  > 59)
>> +		return -EINVAL;
>> +
>> +	ttime.tv_sec = wa->time.tm_hour * 3600 +
>> +	  wa->time.tm_min * 60 + wa->time.tm_sec;
>> +	ttime.tv_nsec = 0;
>> +	if (hp_sdc_rtc_set_mt(&ttime))
>> +		return -EIO;
>> +
>> +	return 0;
>>  }
>>  
>> -static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
>> +static int hp_sdc_rtc_proc(struct device *dev, struct seq_file *m)
>>  {
>>  #define YN(bit) ("no")
>>  #define NY(bit) ("yes")
>> @@ -442,11 +467,10 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
>>  	} else {
>>  		seq_printf(m,
>>  			     "rtc_time\t: %02d:%02d:%02d\n"
>> -			     "rtc_date\t: %04d-%02d-%02d\n"
>> -			     "rtc_epoch\t: %04lu\n",
>> +			     "rtc_date\t: %04d-%02d-%02d\n",
>>  			     tm.tm_hour, tm.tm_min, tm.tm_sec,
>>  			     tm.tm_year + 1900, tm.tm_mon + 1, 
>> -			     tm.tm_mday, epoch);
>> +			     tm.tm_mday);
>>  	}
>>  
>>  	if (hp_sdc_rtc_read_rt(&tv)) {
>> @@ -509,185 +533,42 @@ static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
>>  #undef NY
>>  }
>>  
>> -static int hp_sdc_rtc_ioctl(struct file *file, 
>> +static int hp_sdc_rtc_ioctl(struct device *dev,
>>  			    unsigned int cmd, unsigned long arg)
>>  {
>> -#if 1
>> -	return -EINVAL;
>> -#else
>> -	
>> -        struct rtc_time wtime; 
>> -	struct timeval ttime;
>> -	int use_wtime = 0;
>> -
>> -	/* This needs major work. */
>> -
>> -        switch (cmd) {
>> -
>> -        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
>> -        case RTC_AIE_ON:        /* Allow alarm interrupts.      */
>> +	switch (cmd) {
>> +	case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
>> +	case RTC_AIE_ON:        /* Allow alarm interrupts.      */
>>  	case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
>> -        case RTC_PIE_ON:        /* Allow periodic ints          */
>> -        case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
>> -        case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
>> -        {
>> +	case RTC_PIE_ON:        /* Allow periodic ints          */
>> +	case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
>> +	case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
>> +	{
>>  		/* We cannot mask individual user timers and we
>>  		   cannot tell them apart when they occur, so it 
>>  		   would be disingenuous to succeed these IOCTLs */
>>  		return -EINVAL;
>> -        }
>> -        case RTC_ALM_READ:      /* Read the present alarm time */
>> -        {
>> -		if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
>> -		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
>> -
>> -		wtime.tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
>> -		wtime.tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
>> -		wtime.tm_sec  = ttime.tv_sec;
>> -                
>> -		break;
>> -        }
>> -        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
>> -        {
>> -                return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
>> -        }
>> -        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
>> -        {
>> -                /* 
>> -                 * The max we can do is 100Hz.
>> -		 */
>> -
>> -                if ((arg < 1) || (arg > 100)) return -EINVAL;
>> -		ttime.tv_sec = 0;
>> -		ttime.tv_usec = 1000000 / arg;
>> -		if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
>> -		hp_sdc_rtc_freq = arg;
>> -                return 0;
>> -        }
>> -        case RTC_ALM_SET:       /* Store a time into the alarm */
>> -        {
>> -                /*
>> -                 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
>> -                 * "don't care" or "match all" for PC timers.  The HP SDC
>> -		 * does not support that perk, but it could be emulated fairly
>> -		 * easily.  Only the tm_hour, tm_min and tm_sec are used.
>> -		 * We could do it with 10ms accuracy with the HP SDC, if the 
>> -		 * rtc interface left us a way to do that.
>> -                 */
>> -                struct hp_sdc_rtc_time alm_tm;
>> -
>> -                if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
>> -                                   sizeof(struct hp_sdc_rtc_time)))
>> -                       return -EFAULT;
>> -
>> -                if (alm_tm.tm_hour > 23) return -EINVAL;
>> -		if (alm_tm.tm_min  > 59) return -EINVAL;
>> -		if (alm_tm.tm_sec  > 59) return -EINVAL;  
>> -
>> -		ttime.sec = alm_tm.tm_hour * 3600 + 
>> -		  alm_tm.tm_min * 60 + alm_tm.tm_sec;
>> -		ttime.usec = 0;
>> -		if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
>> -                return 0;
>> -        }
>> -        case RTC_RD_TIME:       /* Read the time/date from RTC  */
>> -        {
>> -		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
>> -                break;
>> -        }
>> -        case RTC_SET_TIME:      /* Set the RTC */
>> -        {
>> -                struct rtc_time hp_sdc_rtc_tm;
>> -                unsigned char mon, day, hrs, min, sec, leap_yr;
>> -                unsigned int yrs;
>> -
>> -                if (!capable(CAP_SYS_TIME))
>> -                        return -EACCES;
>> -		if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
>> -                                   sizeof(struct rtc_time)))
>> -                        return -EFAULT;
>> -
>> -                yrs = hp_sdc_rtc_tm.tm_year + 1900;
>> -                mon = hp_sdc_rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
>> -                day = hp_sdc_rtc_tm.tm_mday;
>> -                hrs = hp_sdc_rtc_tm.tm_hour;
>> -                min = hp_sdc_rtc_tm.tm_min;
>> -                sec = hp_sdc_rtc_tm.tm_sec;
>> -
>> -                if (yrs < 1970)
>> -                        return -EINVAL;
>> -
>> -                leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
>> -
>> -                if ((mon > 12) || (day == 0))
>> -                        return -EINVAL;
>> -                if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
>> -                        return -EINVAL;
>> -		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
>> -                        return -EINVAL;
>> -
>> -                if ((yrs -= eH) > 255)    /* They are unsigned */
>> -                        return -EINVAL;
>> -
>> -
>> -                return 0;
>> -        }
>> -        case RTC_EPOCH_READ:    /* Read the epoch.      */
>> -        {
>> -                return put_user (epoch, (unsigned long *)arg);
>> -        }
>> -        case RTC_EPOCH_SET:     /* Set the epoch.       */
>> -        {
>> -                /* 
>> -                 * There were no RTC clocks before 1900.
>> -                 */
>> -                if (arg < 1900)
>> -		  return -EINVAL;
>> -		if (!capable(CAP_SYS_TIME))
>> -		  return -EACCES;
>> -		
>> -                epoch = arg;
>> -                return 0;
>> -        }
>> -        default:
>> -                return -EINVAL;
>> -        }
>> -        return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
>> -#endif
>> -}
>> -
>> -static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
>> -				      unsigned int cmd, unsigned long arg)
>> -{
>> -	int ret;
>> -
>> -	mutex_lock(&hp_sdc_rtc_mutex);
>> -	ret = hp_sdc_rtc_ioctl(file, cmd, arg);
>> -	mutex_unlock(&hp_sdc_rtc_mutex);
>> -
>> -	return ret;
>> +	}
>> +	default:
>> +		return -ENOIOCTLCMD;
>> +	}
>> +	return 0;
>>  }
>>  
>> -
>> -static const struct file_operations hp_sdc_rtc_fops = {
>> -        .owner =		THIS_MODULE,
>> -        .llseek =		no_llseek,
>> -        .read =			hp_sdc_rtc_read,
>> -        .poll =			hp_sdc_rtc_poll,
>> -        .unlocked_ioctl =	hp_sdc_rtc_unlocked_ioctl,
>> -        .open =			hp_sdc_rtc_open,
>> -        .fasync =		hp_sdc_rtc_fasync,
>> -};
>> -
>> -static struct miscdevice hp_sdc_rtc_dev = {
>> -        .minor =	RTC_MINOR,
>> -        .name =		"rtc_HIL",
>> -        .fops =		&hp_sdc_rtc_fops
>> +static const struct rtc_class_ops hp_sdc_rtc_ops = {
>> +	.ioctl = hp_sdc_rtc_ioctl,
>> +	.proc = hp_sdc_rtc_proc,
>> +	.read_time = hp_sdc_rtc_get_time,
>> +	.set_time = hp_sdc_rtc_set_time,
>> +	.read_alarm = hp_sdc_rtc_read_alarm,
>> +	.set_alarm = hp_sdc_rtc_set_alarm,
>> +	// int (*alarm_irq_enable)(struct device *, unsigned int enabled);
>>  };
>>  
>>  static int __init hp_sdc_rtc_init(void)
>>  {
>>  	int ret;
>> +	struct rtc_time tm;
>>  
>>  #ifdef __mc68000__
>>  	if (!MACH_IS_HP300)
>> @@ -696,14 +577,22 @@ static int __init hp_sdc_rtc_init(void)
>>  
>>  	sema_init(&i8042tregs, 1);
>>  
>> +	/* check if BBRTC is available */
>> +	ret = hp_sdc_rtc_read_bbrtc(&tm);
>> +	if (ret) {
>> +		pr_info("hil-rtc: BBRTC not available.");
>> +		return -ENODEV;
>> +	}
>> +
>>  	if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
>>  		return ret;
>> -	if (misc_register(&hp_sdc_rtc_dev) != 0)
>> -		printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
>>  
>> -        proc_create_single("driver/rtc", 0, NULL, hp_sdc_rtc_proc_show);
>> +	rtc = devm_rtc_device_register(hp_sdc_get_sdc_device(),
>> +			"hil-rtc", &hp_sdc_rtc_ops, THIS_MODULE);
>> +	if (IS_ERR(rtc))
>> +		return PTR_ERR(rtc);
>>  
>> -	printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
>> +	pr_info("hil-rtc: HP i8042 SDC + MSM-58321 RTC support loaded "
>>  			 "(RTC v " RTC_VERSION ")\n");
>>  
>>  	return 0;
>> @@ -711,10 +600,9 @@ static int __init hp_sdc_rtc_init(void)
>>  
>>  static void __exit hp_sdc_rtc_exit(void)
>>  {
>> -	remove_proc_entry ("driver/rtc", NULL);
>> -        misc_deregister(&hp_sdc_rtc_dev);
>> +	devm_rtc_device_unregister(hp_sdc_get_sdc_device(), rtc);
>>  	hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
>> -        printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
>> +	pr_info("hil-rtc: HP i8042 SDC + MSM-58321 RTC support unloaded\n");
>>  }
>>  
>>  module_init(hp_sdc_rtc_init);
>> diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
>> index 0b8a25c58d02..3e0ad6a4907f 100644
>> --- a/drivers/input/serio/hp_sdc.c
>> +++ b/drivers/input/serio/hp_sdc.c
>> @@ -110,6 +110,17 @@ MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver.");
>>  
>>  static hp_i8042_sdc	hp_sdc;	/* All driver state is kept in here. */
>>  
>> +struct device *hp_sdc_get_sdc_device(void)
>> +{
>> +#if defined(__hppa__)
>> +	return &hp_sdc.dev->dev;
>> +#elif defined(__mc68000__)
>> +	/* hp_sdc.dev is 1, so return NULL instead. */
>> +	return NULL;
>> +#endif
>> +}
>> +EXPORT_SYMBOL(hp_sdc_get_sdc_device);
>> +
>>  /*************** primitives for use in any context *********************/
>>  static inline uint8_t hp_sdc_status_in8(void)
>>  {
>> diff --git a/include/linux/hp_sdc.h b/include/linux/hp_sdc.h
>> index 6f1dee7e67e0..8988e32adebd 100644
>> --- a/include/linux/hp_sdc.h
>> +++ b/include/linux/hp_sdc.h
>> @@ -298,4 +298,6 @@ typedef struct {
>>  
>>  } hp_i8042_sdc;
>>  
>> +struct device *hp_sdc_get_sdc_device(void);
>> +
>>  #endif /* _LINUX_HP_SDC_H */
>>
Finn Thain Jan. 7, 2019, 9:59 p.m. UTC | #3
On Mon, 7 Jan 2019, Helge Deller wrote:

> On 27.11.18 22:17, Finn Thain wrote:
> > Adding Kars to Cc.
> 
> Any chance this can get tested on m68k?
> 

Sorry, I don't have any HP9000/300 hardware. I think that Kars does, which 
is why I Cc'd him...
Kars de Jong Jan. 8, 2019, 8:34 a.m. UTC | #4
Op ma 7 jan. 2019 om 22:59 schreef Finn Thain <fthain@telegraphics.com.au>:
>
> On Mon, 7 Jan 2019, Helge Deller wrote:
>
> > On 27.11.18 22:17, Finn Thain wrote:
> > > Adding Kars to Cc.
> >
> > Any chance this can get tested on m68k?
> >
>
> Sorry, I don't have any HP9000/300 hardware. I think that Kars does, which
> is why I Cc'd him...
>

Yes, I do still have the hardware, but I haven't booted Linux on it
for a long time. I planned on doing it during the holidays, but didn't
get around to it. Maybe I can test it this weekend, but don't hold
your breath...

Kind regards,

Kars.
Kars de Jong Jan. 21, 2019, 8:45 a.m. UTC | #5
Op di 8 jan. 2019 om 09:34 schreef Kars de Jong <jongk@linux-m68k.org>:
> Op ma 7 jan. 2019 om 22:59 schreef Finn Thain <fthain@telegraphics.com.au>:
> >
> > On Mon, 7 Jan 2019, Helge Deller wrote:
> >
> > > On 27.11.18 22:17, Finn Thain wrote:
> > > > Adding Kars to Cc.
> > >
> > > Any chance this can get tested on m68k?
> > >
> >
> > Sorry, I don't have any HP9000/300 hardware. I think that Kars does, which
> > is why I Cc'd him...
> >
>
> Yes, I do still have the hardware, but I haven't booted Linux on it
> for a long time. I planned on doing it during the holidays, but didn't
> get around to it. Maybe I can test it this weekend, but don't hold
> your breath...

I tried to test the patch this weekend. I first started with an
unpatched kernel. Unfortunately, there are hardware issues:

1. The battery of the RTC is dead.
2. There seems to be a problem which causes autovector interrupt 1 to
fire spuriously, which is the keyboard and DMA interrupt. At least,
that's what the OpenBSD kernel reports. It seems to cause the Linux
kernel to freeze.
3. The harddisk (original 200 MB Rodime RO3000T) is failing, so I
can't fully boot OpenBSD to further verify this problem.

Anyway, I don't think your patch it will work.

When running on m68k, you're passing a NULL device pointer to
devm_rtc_device_register(), which eventually gets passed to
devres.c:add_dr() and that is definitely not OK.

I think this calls for a proper platform device on the m68k side.

Kind regards,

Kars.
Helge Deller Jan. 26, 2019, 1:47 p.m. UTC | #6
Hello Kars,

On 21.01.19 09:45, Kars de Jong wrote:
> Op di 8 jan. 2019 om 09:34 schreef Kars de Jong <jongk@linux-m68k.org>:
>> Op ma 7 jan. 2019 om 22:59 schreef Finn Thain <fthain@telegraphics.com.au>:
>>>
>>> On Mon, 7 Jan 2019, Helge Deller wrote:
>>>
>>>> On 27.11.18 22:17, Finn Thain wrote:
>>>>> Adding Kars to Cc.
>>>>
>>>> Any chance this can get tested on m68k?
>>>>
>>>
>>> Sorry, I don't have any HP9000/300 hardware. I think that Kars does, which
>>> is why I Cc'd him...
>>>
>>
>> Yes, I do still have the hardware, but I haven't booted Linux on it
>> for a long time. I planned on doing it during the holidays, but didn't
>> get around to it. Maybe I can test it this weekend, but don't hold
>> your breath...
> 
> I tried to test the patch this weekend. I first started with an
> unpatched kernel. Unfortunately, there are hardware issues:
> 
> 1. The battery of the RTC is dead.
> 2. There seems to be a problem which causes autovector interrupt 1 to
> fire spuriously, which is the keyboard and DMA interrupt. At least,
> that's what the OpenBSD kernel reports. It seems to cause the Linux
> kernel to freeze.
> 3. The harddisk (original 200 MB Rodime RO3000T) is failing, so I
> can't fully boot OpenBSD to further verify this problem.

Thanks for the willingness to test, and sorry to hear about that issues!
I hope you get them fixed at some point.
Testing on real hardware is always useful.
 
> Anyway, I don't think your patch it will work.
> 
> When running on m68k, you're passing a NULL device pointer to
> devm_rtc_device_register(), which eventually gets passed to
> devres.c:add_dr() and that is definitely not OK.

I was aware of the NULL pointer, which is why I wanted real
feedback about my patch. Ideally someone with platform knowhow
could add the missing pieces to fix this NULL pointer code. 

> I think this calls for a proper platform device on the m68k side.

Yes. Who could add that?

Helge
Kars de Jong Jan. 28, 2019, 8:35 a.m. UTC | #7
Op za 26 jan. 2019 om 14:47 schreef Helge Deller <deller@gmx.de>:
>
> Hello Kars,
>
> On 21.01.19 09:45, Kars de Jong wrote:
> > I tried to test the patch this weekend. I first started with an
> > unpatched kernel. Unfortunately, there are hardware issues:
> >
> > 1. The battery of the RTC is dead.
> > 2. There seems to be a problem which causes autovector interrupt 1 to
> > fire spuriously, which is the keyboard and DMA interrupt. At least,
> > that's what the OpenBSD kernel reports. It seems to cause the Linux
> > kernel to freeze.
> > 3. The harddisk (original 200 MB Rodime RO3000T) is failing, so I
> > can't fully boot OpenBSD to further verify this problem.
>
> Thanks for the willingness to test, and sorry to hear about that issues!
> I hope you get them fixed at some point.
> Testing on real hardware is always useful.

The issues seem to be less severe than I thought. A new battery is on
its way, the interrupt problem was caused by an issue when using the
keyboard in the OpenBSD boot loader and I don't need the harddisk for
testing Linux.
My netboot environment is back up and running.
The last kernel I ever ran on the box (a patched Linux 2.6.20) booted
to multi user. That one was built in March 2007, which according to my
NFS root file system was also the last time the box actually ran
Linux...

There's now also a working HP9000/360 emulation in MAME (it's capable
of running HP-UX 9 including HP VUE so it's pretty complete) so that
may also become a viable option for testing.

> > Anyway, I don't think your patch it will work.
> >
> > When running on m68k, you're passing a NULL device pointer to
> > devm_rtc_device_register(), which eventually gets passed to
> > devres.c:add_dr() and that is definitely not OK.
>
> I was aware of the NULL pointer, which is why I wanted real
> feedback about my patch. Ideally someone with platform knowhow
> could add the missing pieces to fix this NULL pointer code.
>
> > I think this calls for a proper platform device on the m68k side.
>
> Yes. Who could add that?

I can. I would first like to have a more recent kernel that actually
boots though. It looks like the hp300_defconfig (which is what I used)
isn't quite set up correctly.

Kind regards,

Kars.

Patch
diff mbox series

diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
index 47eb8ca729fe..c787bde12fce 100644
--- a/drivers/input/misc/hp_sdc_rtc.c
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -34,51 +34,27 @@ 
  */
 
 #include <linux/hp_sdc.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/init.h>
 #include <linux/module.h>
-#include <linux/time.h>
-#include <linux/miscdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/seq_file.h>
-#include <linux/poll.h>
 #include <linux/rtc.h>
-#include <linux/mutex.h>
 #include <linux/semaphore.h>
 
 MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
 MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
 MODULE_LICENSE("Dual BSD/GPL");
 
-#define RTC_VERSION "1.10d"
+#define RTC_VERSION "1.2"
 
-static DEFINE_MUTEX(hp_sdc_rtc_mutex);
-static unsigned long epoch = 2000;
+static struct rtc_device *rtc;
 
 static struct semaphore i8042tregs;
 
-static hp_sdc_irqhook hp_sdc_rtc_isr;
-
-static struct fasync_struct *hp_sdc_rtc_async_queue;
-
 static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
 
-static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
-			       size_t count, loff_t *ppos);
-
-static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
-				      unsigned int cmd, unsigned long arg);
-
-static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
-
-static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
-static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
-
 static void hp_sdc_rtc_isr (int irq, void *dev_id, 
 			    uint8_t status, uint8_t data) 
 {
-	return;
+	/* Notify RTC core on alarm event */
+	rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
 }
 
 static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
@@ -105,17 +81,18 @@  static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
 	t.act.semaphore =	&tsem;
 	sema_init(&tsem, 0);
 	
-	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	if (hp_sdc_enqueue_transaction(&t))
+		return -EIO;
 	
 	/* Put ourselves to sleep for results. */
 	if (WARN_ON(down_interruptible(&tsem)))
-		return -1;
+		return -EIO;
 	
 	/* Check for nonpresence of BBRTC */
 	if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
 	       tseq[55] | tseq[62] | tseq[34] | tseq[41] |
 	       tseq[20] | tseq[27] | tseq[6]  | tseq[13]) & 0x0f))
-		return -1;
+		return -ENODEV;
 
 	memset(rtctm, 0, sizeof(struct rtc_time));
 	rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
@@ -132,17 +109,24 @@  static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
 static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
 {
 	struct rtc_time tm, tm_last;
-	int i = 0;
+	int i = 0, ret;
 
 	/* MSM-58321 has no read latch, so must read twice and compare. */
 
-	if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
-	if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+	ret = hp_sdc_rtc_do_read_bbrtc(&tm_last);
+	if (ret)
+		return ret;
+	ret = hp_sdc_rtc_do_read_bbrtc(&tm);
+	if (ret)
+		return ret;
 
 	while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
-		if (i++ > 4) return -1;
+		if (i++ > 4)
+			return -EAGAIN;
 		memcpy(&tm_last, &tm, sizeof(struct rtc_time));
-		if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+		ret = hp_sdc_rtc_do_read_bbrtc(&tm);
+		if (ret)
+			return ret;
 	}
 
 	memcpy(rtctm, &tm, sizeof(struct rtc_time));
@@ -150,7 +134,6 @@  static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
 	return 0;
 }
 
-
 static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
 {
 	hp_sdc_transaction t;
@@ -178,16 +161,16 @@  static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
 
 	/* Sleep if output regs in use. */
 	if (WARN_ON(down_interruptible(&i8042tregs)))
-		return -1;
+		return -EIO;
 
 	if (hp_sdc_enqueue_transaction(&t)) {
 		up(&i8042tregs);
-		return -1;
+		return -EIO;
 	}
 	
 	/* Sleep until results come back. */
 	if (WARN_ON(down_interruptible(&i8042tregs)))
-		return -1;
+		return -EIO;
 
 	up(&i8042tregs);
 
@@ -198,13 +181,15 @@  static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
 
 
 /* Read the i8042 real-time clock */
-static inline int hp_sdc_rtc_read_rt(struct timespec64 *res) {
+static int hp_sdc_rtc_read_rt(struct timespec64 *res)
+{
 	int64_t raw;
 	uint32_t tenms; 
 	unsigned int days;
 
 	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
-	if (raw < 0) return -1;
+	if (raw < 0)
+		return -EIO;
 
 	tenms = (uint32_t)raw & 0xffffff;
 	days  = (unsigned int)(raw >> 24) & 0xffff;
@@ -222,7 +207,8 @@  static inline int hp_sdc_rtc_read_fhs(struct timespec64 *res) {
 	unsigned int tenms;
 
 	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
-	if (raw < 0) return -1;
+	if (raw < 0)
+		return -EIO;
 
 	tenms = (unsigned int)raw & 0xffff;
 
@@ -239,7 +225,8 @@  static inline int hp_sdc_rtc_read_mt(struct timespec64 *res) {
 	uint32_t tenms; 
 
 	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
-	if (raw < 0) return -1;
+	if (raw < 0)
+		return -EIO;
 
 	tenms = (uint32_t)raw & 0xffffff;
 
@@ -256,7 +243,8 @@  static inline int hp_sdc_rtc_read_dt(struct timespec64 *res) {
 	uint32_t tenms;
 
 	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
-	if (raw < 0) return -1;
+	if (raw < 0)
+		return -EIO;
 
 	tenms = (uint32_t)raw & 0xffffff;
 
@@ -273,7 +261,8 @@  static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
 	uint32_t tenms;
 
 	raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
-	if (raw < 0) return -1;
+	if (raw < 0)
+		return -EIO;
 
 	tenms = (uint32_t)raw & 0xffffff;
 
@@ -284,11 +273,11 @@  static inline int hp_sdc_rtc_read_ct(struct timespec64 *res) {
 }
 
 
-#if 0 /* not used yet */
 /* Set the i8042 real-time clock */
-static int hp_sdc_rtc_set_rt (struct timeval *setto)
+static int hp_sdc_rtc_set_rt(struct rtc_time *tm)
 {
 	uint32_t tenms;
+	time64_t seconds;
 	unsigned int days;
 	hp_sdc_transaction t;
 	uint8_t tseq[11] = {
@@ -300,17 +289,13 @@  static int hp_sdc_rtc_set_rt (struct timeval *setto)
 
 	t.endidx = 10;
 
-	if (0xffff < setto->tv_sec / 86400) return -1;
-	days = setto->tv_sec / 86400;
-	if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
-	days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
-	if (days > 0xffff) return -1;
-
-	if (0xffffff < setto->tv_sec) return -1;
-	tenms  = setto->tv_sec * 100;
-	if (0xffffff < setto->tv_usec / 10000) return -1;
-	tenms += setto->tv_usec / 10000;
-	if (tenms > 0xffffff) return -1;
+	seconds = rtc_tm_to_time64(tm);
+	days = seconds / 86400;
+	if (days > 0xffff)
+		return -EINVAL;
+	tenms  = (seconds % 86400) * 100;
+	if (tenms > 0xffffff)
+		return -EINVAL;
 
 	tseq[3] = (uint8_t)(tenms & 0xff);
 	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
@@ -321,12 +306,14 @@  static int hp_sdc_rtc_set_rt (struct timeval *setto)
 
 	t.seq =	tseq;
 
-	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	if (hp_sdc_enqueue_transaction(&t))
+		return -EIO;
 	return 0;
 }
 
 /* Set the i8042 fast handshake timer */
-static int hp_sdc_rtc_set_fhs (struct timeval *setto)
+#if 0
+static int hp_sdc_rtc_set_fhs(struct timespec64 *setto)
 {
 	uint32_t tenms;
 	hp_sdc_transaction t;
@@ -337,36 +324,36 @@  static int hp_sdc_rtc_set_fhs (struct timeval *setto)
 
 	t.endidx = 4;
 
-	if (0xffff < setto->tv_sec) return -1;
 	tenms  = setto->tv_sec * 100;
-	if (0xffff < setto->tv_usec / 10000) return -1;
-	tenms += setto->tv_usec / 10000;
-	if (tenms > 0xffff) return -1;
+	tenms += setto->tv_nsec / 10000 / 1000;
+	if (tenms > 0xffff)
+		return -EINVAL;
 
 	tseq[3] = (uint8_t)(tenms & 0xff);
 	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
 
 	t.seq =	tseq;
 
-	if (hp_sdc_enqueue_transaction(&t)) return -1;
+	if (hp_sdc_enqueue_transaction(&t))
+		return -EIO;
 	return 0;
 }
-
+#endif
 
 /* Set the i8042 match timer (a.k.a. alarm) */
-#define hp_sdc_rtc_set_mt (setto) \
+#define hp_sdc_rtc_set_mt(setto) \
 	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
 
 /* Set the i8042 delay timer */
-#define hp_sdc_rtc_set_dt (setto) \
+#define hp_sdc_rtc_set_dt(setto) \
 	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
 
 /* Set the i8042 cycle timer (a.k.a. periodic) */
-#define hp_sdc_rtc_set_ct (setto) \
+#define hp_sdc_rtc_set_ct(setto) \
 	hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
 
 /* Set one of the i8042 3-byte wide timers */
-static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
+static int hp_sdc_rtc_set_i8042timer(struct timespec64 *setto, uint8_t setcmd)
 {
 	uint32_t tenms;
 	hp_sdc_transaction t;
@@ -377,58 +364,96 @@  static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
 
 	t.endidx = 6;
 
-	if (0xffffff < setto->tv_sec) return -1;
+	if (setto->tv_sec > 0xffffff)
+		return -EINVAL;
 	tenms  = setto->tv_sec * 100;
-	if (0xffffff < setto->tv_usec / 10000) return -1;
-	tenms += setto->tv_usec / 10000;
-	if (tenms > 0xffffff) return -1;
+	if (setto->tv_nsec / 10000 / 1000 > 0xffffff)
+		return -EINVAL;
+	tenms += setto->tv_nsec / 10000 / 1000;
+	if (tenms > 0xffffff)
+		return -EINVAL;
 
 	tseq[1] = setcmd;
 	tseq[3] = (uint8_t)(tenms & 0xff);
 	tseq[4] = (uint8_t)((tenms >> 8)  & 0xff);
 	tseq[5] = (uint8_t)((tenms >> 16)  & 0xff);
 
-	t.seq =			tseq;
+	t.seq =	tseq;
 
 	if (hp_sdc_enqueue_transaction(&t)) { 
-		return -1;
+		return -EIO;
 	}
 	return 0;
 }
-#endif
 
-static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
-			       size_t count, loff_t *ppos) {
-	ssize_t retval;
-
-        if (count < sizeof(unsigned long))
-                return -EINVAL;
-
-	retval = put_user(68, (unsigned long __user *)buf);
-	return retval;
+static int hp_sdc_rtc_get_time(struct device *dev, struct rtc_time *tm)
+{
+	return hp_sdc_rtc_read_bbrtc(tm);
 }
 
-static __poll_t hp_sdc_rtc_poll(struct file *file, poll_table *wait)
+static int hp_sdc_rtc_set_time(struct device *dev, struct rtc_time *tm)
 {
-        unsigned long l;
+	if (!rtc_valid_tm(tm))
+		return -EINVAL;
 
-	l = 0;
-        if (l != 0)
-                return EPOLLIN | EPOLLRDNORM;
-        return 0;
+	return hp_sdc_rtc_set_rt(tm);
 }
 
-static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
+static int hp_sdc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wa)
 {
-        return 0;
+	/* Read the present alarm time */
+	int ret;
+	struct timespec64 ttime;
+	struct rtc_time *wtime = &wa->time;
+
+	ret = hp_sdc_rtc_read_mt(&ttime);
+	if (ret)
+		return ret;
+	ret = hp_sdc_rtc_read_bbrtc(wtime);
+	if (ret)
+		return ret;
+
+	wtime->tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
+	wtime->tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
+	wtime->tm_sec  = ttime.tv_sec;
+
+	wa->enabled = 0;
+	wa->pending = 0;
+
+	return 0;
 }
 
-static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
+static int hp_sdc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wa)
 {
-        return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
+	/* Set the alarm time */
+	struct timespec64 ttime;
+
+	/*
+	 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
+	 * "don't care" or "match all" for PC timers.  The HP SDC
+	 * does not support that perk, but it could be emulated fairly
+	 * easily.  Only the tm_hour, tm_min and tm_sec are used.
+	 * We could do it with 10ms accuracy with the HP SDC, if the
+	 * rtc interface left us a way to do that.
+	 */
+
+	if (wa->time.tm_hour > 23)
+		return -EINVAL;
+	if (wa->time.tm_min  > 59)
+		return -EINVAL;
+	if (wa->time.tm_sec  > 59)
+		return -EINVAL;
+
+	ttime.tv_sec = wa->time.tm_hour * 3600 +
+	  wa->time.tm_min * 60 + wa->time.tm_sec;
+	ttime.tv_nsec = 0;
+	if (hp_sdc_rtc_set_mt(&ttime))
+		return -EIO;
+
+	return 0;
 }
 
-static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
+static int hp_sdc_rtc_proc(struct device *dev, struct seq_file *m)
 {
 #define YN(bit) ("no")
 #define NY(bit) ("yes")
@@ -442,11 +467,10 @@  static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
 	} else {
 		seq_printf(m,
 			     "rtc_time\t: %02d:%02d:%02d\n"
-			     "rtc_date\t: %04d-%02d-%02d\n"
-			     "rtc_epoch\t: %04lu\n",
+			     "rtc_date\t: %04d-%02d-%02d\n",
 			     tm.tm_hour, tm.tm_min, tm.tm_sec,
 			     tm.tm_year + 1900, tm.tm_mon + 1, 
-			     tm.tm_mday, epoch);
+			     tm.tm_mday);
 	}
 
 	if (hp_sdc_rtc_read_rt(&tv)) {
@@ -509,185 +533,42 @@  static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
 #undef NY
 }
 
-static int hp_sdc_rtc_ioctl(struct file *file, 
+static int hp_sdc_rtc_ioctl(struct device *dev,
 			    unsigned int cmd, unsigned long arg)
 {
-#if 1
-	return -EINVAL;
-#else
-	
-        struct rtc_time wtime; 
-	struct timeval ttime;
-	int use_wtime = 0;
-
-	/* This needs major work. */
-
-        switch (cmd) {
-
-        case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
-        case RTC_AIE_ON:        /* Allow alarm interrupts.      */
+	switch (cmd) {
+	case RTC_AIE_OFF:       /* Mask alarm int. enab. bit    */
+	case RTC_AIE_ON:        /* Allow alarm interrupts.      */
 	case RTC_PIE_OFF:       /* Mask periodic int. enab. bit */
-        case RTC_PIE_ON:        /* Allow periodic ints          */
-        case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
-        case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
-        {
+	case RTC_PIE_ON:        /* Allow periodic ints          */
+	case RTC_UIE_ON:        /* Allow ints for RTC updates.  */
+	case RTC_UIE_OFF:       /* Allow ints for RTC updates.  */
+	{
 		/* We cannot mask individual user timers and we
 		   cannot tell them apart when they occur, so it 
 		   would be disingenuous to succeed these IOCTLs */
 		return -EINVAL;
-        }
-        case RTC_ALM_READ:      /* Read the present alarm time */
-        {
-		if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
-		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
-
-		wtime.tm_hour = ttime.tv_sec / 3600;  ttime.tv_sec %= 3600;
-		wtime.tm_min  = ttime.tv_sec / 60;    ttime.tv_sec %= 60;
-		wtime.tm_sec  = ttime.tv_sec;
-                
-		break;
-        }
-        case RTC_IRQP_READ:     /* Read the periodic IRQ rate.  */
-        {
-                return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
-        }
-        case RTC_IRQP_SET:      /* Set periodic IRQ rate.       */
-        {
-                /* 
-                 * The max we can do is 100Hz.
-		 */
-
-                if ((arg < 1) || (arg > 100)) return -EINVAL;
-		ttime.tv_sec = 0;
-		ttime.tv_usec = 1000000 / arg;
-		if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
-		hp_sdc_rtc_freq = arg;
-                return 0;
-        }
-        case RTC_ALM_SET:       /* Store a time into the alarm */
-        {
-                /*
-                 * This expects a struct hp_sdc_rtc_time. Writing 0xff means
-                 * "don't care" or "match all" for PC timers.  The HP SDC
-		 * does not support that perk, but it could be emulated fairly
-		 * easily.  Only the tm_hour, tm_min and tm_sec are used.
-		 * We could do it with 10ms accuracy with the HP SDC, if the 
-		 * rtc interface left us a way to do that.
-                 */
-                struct hp_sdc_rtc_time alm_tm;
-
-                if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
-                                   sizeof(struct hp_sdc_rtc_time)))
-                       return -EFAULT;
-
-                if (alm_tm.tm_hour > 23) return -EINVAL;
-		if (alm_tm.tm_min  > 59) return -EINVAL;
-		if (alm_tm.tm_sec  > 59) return -EINVAL;  
-
-		ttime.sec = alm_tm.tm_hour * 3600 + 
-		  alm_tm.tm_min * 60 + alm_tm.tm_sec;
-		ttime.usec = 0;
-		if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
-                return 0;
-        }
-        case RTC_RD_TIME:       /* Read the time/date from RTC  */
-        {
-		if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
-                break;
-        }
-        case RTC_SET_TIME:      /* Set the RTC */
-        {
-                struct rtc_time hp_sdc_rtc_tm;
-                unsigned char mon, day, hrs, min, sec, leap_yr;
-                unsigned int yrs;
-
-                if (!capable(CAP_SYS_TIME))
-                        return -EACCES;
-		if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
-                                   sizeof(struct rtc_time)))
-                        return -EFAULT;
-
-                yrs = hp_sdc_rtc_tm.tm_year + 1900;
-                mon = hp_sdc_rtc_tm.tm_mon + 1;   /* tm_mon starts at zero */
-                day = hp_sdc_rtc_tm.tm_mday;
-                hrs = hp_sdc_rtc_tm.tm_hour;
-                min = hp_sdc_rtc_tm.tm_min;
-                sec = hp_sdc_rtc_tm.tm_sec;
-
-                if (yrs < 1970)
-                        return -EINVAL;
-
-                leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
-
-                if ((mon > 12) || (day == 0))
-                        return -EINVAL;
-                if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
-                        return -EINVAL;
-		if ((hrs >= 24) || (min >= 60) || (sec >= 60))
-                        return -EINVAL;
-
-                if ((yrs -= eH) > 255)    /* They are unsigned */
-                        return -EINVAL;
-
-
-                return 0;
-        }
-        case RTC_EPOCH_READ:    /* Read the epoch.      */
-        {
-                return put_user (epoch, (unsigned long *)arg);
-        }
-        case RTC_EPOCH_SET:     /* Set the epoch.       */
-        {
-                /* 
-                 * There were no RTC clocks before 1900.
-                 */
-                if (arg < 1900)
-		  return -EINVAL;
-		if (!capable(CAP_SYS_TIME))
-		  return -EACCES;
-		
-                epoch = arg;
-                return 0;
-        }
-        default:
-                return -EINVAL;
-        }
-        return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
-#endif
-}
-
-static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
-				      unsigned int cmd, unsigned long arg)
-{
-	int ret;
-
-	mutex_lock(&hp_sdc_rtc_mutex);
-	ret = hp_sdc_rtc_ioctl(file, cmd, arg);
-	mutex_unlock(&hp_sdc_rtc_mutex);
-
-	return ret;
+	}
+	default:
+		return -ENOIOCTLCMD;
+	}
+	return 0;
 }
 
-
-static const struct file_operations hp_sdc_rtc_fops = {
-        .owner =		THIS_MODULE,
-        .llseek =		no_llseek,
-        .read =			hp_sdc_rtc_read,
-        .poll =			hp_sdc_rtc_poll,
-        .unlocked_ioctl =	hp_sdc_rtc_unlocked_ioctl,
-        .open =			hp_sdc_rtc_open,
-        .fasync =		hp_sdc_rtc_fasync,
-};
-
-static struct miscdevice hp_sdc_rtc_dev = {
-        .minor =	RTC_MINOR,
-        .name =		"rtc_HIL",
-        .fops =		&hp_sdc_rtc_fops
+static const struct rtc_class_ops hp_sdc_rtc_ops = {
+	.ioctl = hp_sdc_rtc_ioctl,
+	.proc = hp_sdc_rtc_proc,
+	.read_time = hp_sdc_rtc_get_time,
+	.set_time = hp_sdc_rtc_set_time,
+	.read_alarm = hp_sdc_rtc_read_alarm,
+	.set_alarm = hp_sdc_rtc_set_alarm,
+	// int (*alarm_irq_enable)(struct device *, unsigned int enabled);
 };
 
 static int __init hp_sdc_rtc_init(void)
 {
 	int ret;
+	struct rtc_time tm;
 
 #ifdef __mc68000__
 	if (!MACH_IS_HP300)
@@ -696,14 +577,22 @@  static int __init hp_sdc_rtc_init(void)
 
 	sema_init(&i8042tregs, 1);
 
+	/* check if BBRTC is available */
+	ret = hp_sdc_rtc_read_bbrtc(&tm);
+	if (ret) {
+		pr_info("hil-rtc: BBRTC not available.");
+		return -ENODEV;
+	}
+
 	if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
 		return ret;
-	if (misc_register(&hp_sdc_rtc_dev) != 0)
-		printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
 
-        proc_create_single("driver/rtc", 0, NULL, hp_sdc_rtc_proc_show);
+	rtc = devm_rtc_device_register(hp_sdc_get_sdc_device(),
+			"hil-rtc", &hp_sdc_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc))
+		return PTR_ERR(rtc);
 
-	printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
+	pr_info("hil-rtc: HP i8042 SDC + MSM-58321 RTC support loaded "
 			 "(RTC v " RTC_VERSION ")\n");
 
 	return 0;
@@ -711,10 +600,9 @@  static int __init hp_sdc_rtc_init(void)
 
 static void __exit hp_sdc_rtc_exit(void)
 {
-	remove_proc_entry ("driver/rtc", NULL);
-        misc_deregister(&hp_sdc_rtc_dev);
+	devm_rtc_device_unregister(hp_sdc_get_sdc_device(), rtc);
 	hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
-        printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
+	pr_info("hil-rtc: HP i8042 SDC + MSM-58321 RTC support unloaded\n");
 }
 
 module_init(hp_sdc_rtc_init);
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index 0b8a25c58d02..3e0ad6a4907f 100644
--- a/drivers/input/serio/hp_sdc.c
+++ b/drivers/input/serio/hp_sdc.c
@@ -110,6 +110,17 @@  MODULE_PARM_DESC(no_hpsdc, "Do not enable HP SDC driver.");
 
 static hp_i8042_sdc	hp_sdc;	/* All driver state is kept in here. */
 
+struct device *hp_sdc_get_sdc_device(void)
+{
+#if defined(__hppa__)
+	return &hp_sdc.dev->dev;
+#elif defined(__mc68000__)
+	/* hp_sdc.dev is 1, so return NULL instead. */
+	return NULL;
+#endif
+}
+EXPORT_SYMBOL(hp_sdc_get_sdc_device);
+
 /*************** primitives for use in any context *********************/
 static inline uint8_t hp_sdc_status_in8(void)
 {
diff --git a/include/linux/hp_sdc.h b/include/linux/hp_sdc.h
index 6f1dee7e67e0..8988e32adebd 100644
--- a/include/linux/hp_sdc.h
+++ b/include/linux/hp_sdc.h
@@ -298,4 +298,6 @@  typedef struct {
 
 } hp_i8042_sdc;
 
+struct device *hp_sdc_get_sdc_device(void);
+
 #endif /* _LINUX_HP_SDC_H */