/*
* drivers/rtc/rtc-ricoh619.c
*
* Real time clock driver for RICOH R5T619 power management chip.
*
* Copyright (C) 2012-2014 RICOH COMPANY,LTD
*
* Based on code
* Copyright (C) 2011 NVIDIA Corporation
*
* this program is free software; you can redistribute it and/or modify
* it under the terms of the gnu general public license as published by
* the free software foundation; either version 2 of the license, or
* (at your option) any later version.
*
* this program is distributed in the hope that it will be useful, but without
* any warranty; without even the implied warranty of merchantability or
* fitness for a particular purpose. see the gnu general public license for
* more details.
*
* you should have received a copy of the gnu general public license
* along with this program. If not, see .
*
*/
/* #define debug 1 */
/* #define verbose_debug 1 */
#include
#include
#include
#include
#include
#include
#include
#include
#include
struct ricoh61x_rtc {
int irq;
struct rtc_device *rtc;
bool irq_en;
};
static int ricoh61x_read_regs(struct device *dev, int reg, int len,
uint8_t *val)
{
int ret;
ret = ricoh61x_bulk_reads(dev->parent, reg, len, val);
if (ret < 0) {
dev_err(dev->parent, "PMU: %s failed reading from 0x%02x\n",
__func__, reg);
WARN_ON(1);
}
return ret;
}
static int ricoh61x_write_regs(struct device *dev, int reg, int len,
uint8_t *val)
{
int ret;
ret = ricoh61x_bulk_writes(dev->parent, reg, len, val);
if (ret < 0) {
dev_err(dev->parent, "PMU: %s failed writing\n", __func__);
WARN_ON(1);
}
return ret;
}
/* 0=OK, -EINVAL= FAIL */
static int ricoh61x_rtc_valid_tm(struct device *dev, struct rtc_time *tm)
{
if (tm->tm_year > 199 || tm->tm_year < 70
|| tm->tm_mon > 11 || tm->tm_mon < 0
|| tm->tm_mday < 1
|| tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + os_ref_year)
|| tm->tm_hour >= 24 || tm->tm_hour < 0
|| tm->tm_min < 0 || tm->tm_min >= 60
|| tm->tm_sec < 0 || tm->tm_sec >= 60
) {
dev_err(dev->parent, "PMU: %s *** Returning error due to time, %d/%d/%d %d:%d:%d *****\n",
__func__, tm->tm_mon, tm->tm_mday, tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
return -EINVAL;
}
return 0;
}
static u8 dec2bcd(u8 dec)
{
return ((dec/10)<<4)+(dec%10);
}
static u8 bcd2dec(u8 bcd)
{
return (bcd >> 4)*10+(bcd & 0xf);
}
static void convert_bcd_to_decimal(u8 *buf, u8 len)
{
int i = 0;
for (i = 0; i < len; i++)
buf[i] = bcd2dec(buf[i]);
}
static void convert_decimal_to_bcd(u8 *buf, u8 len)
{
int i = 0;
for (i = 0; i < len; i++)
buf[i] = dec2bcd(buf[i]);
}
static void print_time(struct device *dev, struct rtc_time *tm)
{
dev_info(dev, "PMU: %s *** rtc-time : %d/%d/%d %d:%d:%d *****\n",
__func__, (tm->tm_mon), tm->tm_mday, (tm->tm_year + os_ref_year), tm->tm_hour, tm->tm_min, tm->tm_sec);
}
static int ricoh61x_rtc_periodic_disable(struct device *dev)
{
int err;
uint8_t reg_data;
/* disable function */
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s read rtc_ctrl1 error=0x%x\n", __func__, err);
return err;
}
reg_data &= 0xf8;
err = ricoh61x_write_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s write rtc_ctrl1 error=0x%x\n", __func__, err);
return err;
}
/* clear alarm flag and CTFG */
err = ricoh61x_read_regs(dev, rtc_ctrl2, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s read rtc_ctrl2 error=0x%x\n", __func__, err);
return err;
}
reg_data &= ~0x85; /* 1000-0101 */
err = ricoh61x_write_regs(dev, rtc_ctrl2, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s write rtc_ctrl2 error=0x%x\n", __func__, err);
return err;
}
return 0;
}
static int ricoh61x_rtc_clk_adjust(struct device *dev, uint8_t clk)
{
return ricoh61x_write_regs(dev, rtc_adjust, 1, &clk);
}
static int ricoh61x_rtc_Pon_get_clr(struct device *dev, uint8_t *Pon_f)
{
int err;
uint8_t reg_data;
err = ricoh61x_read_regs(dev, rtc_ctrl2, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "rtc_ctrl1 read err=0x%x\n", err);
return err;
}
printk(KERN_INFO "%s, PON=1 -- CTRL2=0x%x\n", __func__, reg_data);
if (reg_data & 0x10) {
*Pon_f = 1;
/* clear VDET PON */
reg_data &= ~0x5b; /* 0101-1011 */
reg_data |= 0x20; /* 0010-0000 */
err = ricoh61x_write_regs(dev, rtc_ctrl2, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s rtc_ctrl1 write err=0x%x\n", __func__, err);
}
} else {
*Pon_f = 0;
}
return err;
}
/* 0-12hour, 1-24hour */
static int ricoh61x_rtc_hour_mode_get(struct device *dev, int *mode)
{
int err;
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, mode);
if (err < 0)
dev_err(dev->parent, "PMU: %s read rtc ctrl1 error\n", __func__);
if (*mode & 0x20)
*mode = 1;
else
*mode = 0;
return err;
}
/* 0-12hour, 1-24hour */
static int ricoh61x_rtc_hour_mode_set(struct device *dev, int mode)
{
uint8_t reg_data;
int err;
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s read rtc_ctrl1 error\n", __func__);
return err;
}
if (mode == 0)
reg_data &= 0xDF;
else
reg_data |= 0x20;
err = ricoh61x_write_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s write rtc_ctrl1 error\n", __func__);
}
return err;
}
static int ricoh61x_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
u8 buff[7];
int err;
int cent_flag;
// printk(KERN_INFO "PMU: %s\n", __func__);
err = ricoh61x_read_regs(dev, rtc_seconds_reg, sizeof(buff), buff);
if (err < 0) {
dev_err(dev->parent, "PMU: %s *** failed to read time *****\n", __func__);
return err;
}
if (buff[5] & 0x80)
cent_flag = 1;
else
cent_flag = 0;
buff[5] = buff[5]&0x1f; /* bit5 19_20 */
convert_bcd_to_decimal(buff, sizeof(buff));
tm->tm_sec = buff[0];
tm->tm_min = buff[1];
tm->tm_hour = buff[2]; /* bit5 PA_H20 */
tm->tm_wday = buff[3];
tm->tm_mday = buff[4];
tm->tm_mon = buff[5]; /* for print */
tm->tm_year = buff[6] + 100 * cent_flag;
// print_time(dev, tm); /* for print */
tm->tm_mon = buff[5] - 1; /* back to system 0-11 */
return 0;
}
static int ricoh61x_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
u8 buff[7];
int err;
int cent_flag;
printk(KERN_INFO "PMU: %s\n", __func__);
if (ricoh61x_rtc_valid_tm(dev, tm) != 0) {
return -EINVAL;
}
if (tm->tm_year >= 100)
cent_flag = 1;
else
cent_flag = 0;
tm->tm_mon = tm->tm_mon + 1;
buff[0] = tm->tm_sec;
buff[1] = tm->tm_min;
buff[2] = tm->tm_hour;
buff[3] = tm->tm_wday;
buff[4] = tm->tm_mday;
buff[5] = tm->tm_mon; /* system set 0-11 */
buff[6] = tm->tm_year - 100 * cent_flag;
print_time(dev, tm); /* RTC_TEST */
convert_decimal_to_bcd(buff, sizeof(buff));
if (1 == cent_flag)
buff[5] |= 0x80;
err = ricoh61x_write_regs(dev, rtc_seconds_reg, sizeof(buff), buff);
if (err < 0) {
dev_err(dev->parent, "PMU: %s failed to program new time\n", __func__);
return err;
}
return 0;
}
static int ricoh61x_rtc_alarm_is_enabled(struct device *dev, uint8_t *enabled)
{
struct ricoh61x_rtc *rtc = dev_get_drvdata(dev);
int err;
uint8_t reg_data;
err = 0;
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, ®_data);
if (err) {
dev_err(dev->parent, "read rtc_ctrl1 error 0x%lx\n", err);
*enabled = 0;
} else {
if (reg_data & 0x40)
*enabled = 1;
else
*enabled = 0;
}
return err;
}
/* 0-disable, 1-enable */
static int ricoh61x_rtc_alarm_enable(struct device *dev, unsigned int enabled)
{
struct ricoh61x_rtc *rtc = dev_get_drvdata(dev);
int err;
uint8_t reg_data;
printk(KERN_INFO "PMU: %s :%d\n", __func__, enabled);
err = 0;
if (enabled) {
rtc->irq_en = 1;
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s read rtc_ctrl1 error 0x%lx\n", __func__, err);
goto ERR;
}
reg_data |= 0x40; /* set DALE */
err = ricoh61x_write_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0)
dev_err(dev->parent, "PMU: %s write rtc_ctrl1 error 0x%lx\n", __func__, err);
} else {
rtc->irq_en = 0;
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0) {
dev_err(dev->parent, "PMU: %s read rtc_ctrl1 error 0x%lx\n", __func__, err);
goto ERR;
}
reg_data &= 0xbf;/* clear DALE */
err = ricoh61x_write_regs(dev, rtc_ctrl1, 1, ®_data);
if (err < 0)
dev_err(dev->parent, "PMU: %s write rtc_ctrl1 error 0x%lx\n", __func__, err);
}
ERR:
return err;
}
static int ricoh61x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
u8 buff[6];
u8 buff_cent;
int err;
int cent_flag;
unsigned char enabled_flag;
printk(KERN_INFO "PMU: %s\n", __func__);
err = 0;
alrm->time.tm_sec = 0;
alrm->time.tm_min = 0;
alrm->time.tm_hour = 0;
alrm->time.tm_mday = 0;
alrm->time.tm_mon = 0;
alrm->time.tm_year = 0;
alrm->enabled = 0;
err = ricoh61x_read_regs(dev, rtc_month_reg, 1, &buff_cent);
if (err < 0) {
dev_err(dev->parent, "PMU: %s *** failed to read time *****\n", __func__);
return err;
}
if (buff_cent & 0x80)
cent_flag = 1;
else
cent_flag = 0;
err = ricoh61x_read_regs(dev, rtc_alarm_y_sec, sizeof(buff), buff);
if (err) {
dev_err(dev->parent, "RTC: %s *** read rtc_alarm timer error 0x%lx\n", __func__, err);
return err;
}
err = ricoh61x_read_regs(dev, rtc_ctrl1, 1, &enabled_flag);
if (err) {
dev_err(dev->parent, "RTC: %s *** read rtc_enable flag error 0x%lx\n", __func__, err);
return err;
}
if (enabled_flag & 0x40)
enabled_flag = 1;
else
enabled_flag = 0;
buff[3] &= ~0x80; /* clear DAL_EXT */
buff[3] = buff[3]&0x3f;
convert_bcd_to_decimal(buff, sizeof(buff));
alrm->time.tm_sec = buff[0];
alrm->time.tm_min = buff[1];
alrm->time.tm_hour = buff[2];
alrm->time.tm_mday = buff[3];
alrm->time.tm_mon = buff[4]; /* for print */
alrm->time.tm_year = buff[5] + 100 * cent_flag;
dev_info(dev, "PMU: read alarm: %d/%d/%d %d:%d:%d *****\n",
(alrm->time.tm_mon), alrm->time.tm_mday, (alrm->time.tm_year + os_ref_year), alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec);
alrm->time.tm_mon = buff[4] - 1;
alrm->enabled = enabled_flag;
return 0;
}
static int ricoh61x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct ricoh61x_rtc *rtc = dev_get_drvdata(dev);
u8 buff[6];
int err;
int cent_flag;
printk(KERN_INFO "PMU: %s\n", __func__);
err = 0;
ricoh61x_rtc_alarm_enable(dev, 0);
if (rtc->irq == -1) {
err = -EIO;
goto ERR;
}
if (alrm->enabled == 0)
return 0;
if (alrm->time.tm_year >= 100)
cent_flag = 1;
else
cent_flag = 0;
alrm->time.tm_mon += 1;
print_time(dev->parent, &alrm->time);
buff[0] = alrm->time.tm_sec;
buff[1] = alrm->time.tm_min;
buff[2] = alrm->time.tm_hour;
buff[3] = alrm->time.tm_mday;
buff[4] = alrm->time.tm_mon;
buff[5] = alrm->time.tm_year - 100 * cent_flag;
convert_decimal_to_bcd(buff, sizeof(buff));
buff[3] |= 0x80; /* set DAL_EXT */
err = ricoh61x_write_regs(dev, rtc_alarm_y_sec, sizeof(buff), buff);
if (err) {
dev_err(dev->parent, "PMU: %s unable to set alarm\n", __func__);
err = -EBUSY;
goto ERR;
}
ricoh61x_rtc_alarm_enable(dev, alrm->enabled);
ERR:
return err;
}
static int ricoh61x_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
if (cmd == RTC_WAKEUP_FLAG) {
extern int g_wakeup_by_alarm;
put_user(g_wakeup_by_alarm, (unsigned long __user *) arg);
g_wakeup_by_alarm = 0;
return 0;
}
return -ENOIOCTLCMD;
}
static const struct rtc_class_ops ricoh61x_rtc_ops = {
.read_time = ricoh61x_rtc_read_time,
.set_time = ricoh61x_rtc_set_time,
.set_alarm = ricoh61x_rtc_set_alarm,
.read_alarm = ricoh61x_rtc_read_alarm,
.alarm_irq_enable = ricoh61x_rtc_alarm_enable,
.ioctl = ricoh61x_rtc_ioctl,
};
static int ricoh61x_rtc_alarm_flag_clr(struct device *dev)
{
int err;
uint8_t reg_data;
/* clear alarm-D status bits.*/
err = ricoh61x_read_regs(dev, rtc_ctrl2, 1, ®_data);
if (err)
dev_err(dev->parent, "unable to read rtc_ctrl2 reg\n");
/* to clear alarm-D flag, and set adjustment parameter */
reg_data &= ~0x81;
err = ricoh61x_write_regs(dev, rtc_ctrl2, 1, ®_data);
if (err)
dev_err(dev->parent, "unable to program rtc_status reg\n");
return err;
}
static irqreturn_t ricoh61x_rtc_irq(int irq, void *data)
{
struct device *dev = data;
struct ricoh61x_rtc *rtc = dev_get_drvdata(dev);
extern int g_wakeup_by_alarm;
g_wakeup_by_alarm = 1;
// printk(KERN_INFO "PMU: %s\n", __func__);
ricoh61x_rtc_alarm_flag_clr(dev);
rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
static int rtc_adjust_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
uint8_t reg_data;
reg_data = simple_strtoul(buf, NULL, 10);
printk ("ricoh61x set rtc_adjust %d\n", reg_data);
ricoh61x_write_regs(dev, rtc_adjust, 1, ®_data);
return count;
}
static int rtc_adjust_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
uint8_t reg_data;
ricoh61x_read_regs(dev, rtc_adjust, 1, ®_data);
printk ("ricoh61x_rtc_adjust %d\n", reg_data);
sprintf (buf, "%d", reg_data);
return strlen(buf);
}
static DEVICE_ATTR(ricoh61x_rtc_adjust, 0666, rtc_adjust_show, rtc_adjust_store);
static int __devinit ricoh61x_rtc_probe(struct platform_device *pdev)
{
struct ricoh619_rtc_platform_data *pdata = pdev->dev.platform_data;
struct ricoh61x_rtc *rtc;
struct rtc_time tm;
uint8_t Pon_flag, Alarm_flag;
int err;
uint8_t buff[6];
printk(KERN_INFO "PMU RTC: %s, ricoh61x driver run at 24H-mode\n", __func__);
printk(KERN_INFO "PMU RTC: we never using periodic function and interrupt\n");
if (!pdata) {
dev_err(&pdev->dev, "no platform_data specified\n");
return -EINVAL;
}
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
dev_err(&pdev->dev, "no enough memory for ricoh61x_rtc using\n");
return err;
}
dev_set_drvdata(&pdev->dev, rtc);
if (IS_ERR(rtc->rtc)) {
err = PTR_ERR(rtc->rtc);
goto fail;
}
if (pdata->irq < 0) {
dev_err(&pdev->dev, "no irq specified, wakeup is disabled\n");
rtc->irq = -1;
rtc->irq_en = 0;
} else {
rtc->irq = pdata->irq;
rtc->irq_en = 1;
}
/* get interrupt flag */
err = ricoh61x_rtc_alarm_is_enabled(&pdev->dev, &Alarm_flag);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: Disable alarm interrupt error\n");
goto fail;
}
/* get PON flag */
err = ricoh61x_rtc_Pon_get_clr(&pdev->dev, &Pon_flag);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: get PON flag error\n");
goto fail;
}
/* disable rtc periodic function */
err = ricoh61x_rtc_periodic_disable(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: disable rtc periodic int error\n");
goto fail;
}
/* clearing RTC Adjust register */
err = ricoh61x_rtc_clk_adjust(&pdev->dev, 0);
if (err) {
dev_err(&pdev->dev, "unable to program rtc_adjust reg\n");
err = -EBUSY;
goto fail;
}
/* disable interrupt */
err = ricoh61x_rtc_alarm_enable(&pdev->dev, 0);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: Disable alarm interrupt error\n");
goto fail;
}
/* PON=1 */
if (Pon_flag) {
Alarm_flag = 0;
/* clear int flag */
err = ricoh61x_rtc_alarm_flag_clr(&pdev->dev);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: Pon=1 clear alarm flag error\n");
goto fail;
}
/* using 24h-mode */
err = ricoh61x_rtc_hour_mode_set(&pdev->dev, 1);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: Pon=1 set 24h-mode error\n");
goto fail;
}
/* setting the default year */
printk(KERN_INFO "PMU: %s Set default time\n", __func__);
pdata->time.tm_sec = 0;
pdata->time.tm_min = 0;
pdata->time.tm_hour = 0;
pdata->time.tm_wday = 6;
pdata->time.tm_mday = 1;
pdata->time.tm_mon = 1;
pdata->time.tm_year = 2012;
pdata->time.tm_year -= os_ref_year;
if (ricoh61x_rtc_valid_tm(&pdev->dev, &(pdata->time)) == 0) {
tm.tm_sec = pdata->time.tm_sec;
tm.tm_min = pdata->time.tm_min;
tm.tm_hour = pdata->time.tm_hour;
tm.tm_wday = pdata->time.tm_wday;
tm.tm_mday = pdata->time.tm_mday;
tm.tm_mon = pdata->time.tm_mon-1;
tm.tm_year = pdata->time.tm_year;
} else {
/* using the ricoh default time instead of board default time */
dev_err(&pdev->dev, "board rtc default is erro\n");
tm.tm_sec = 0;
tm.tm_min = 0;
tm.tm_hour = 0;
tm.tm_wday = 4;
tm.tm_mday = 1;
tm.tm_mon = 0;
tm.tm_year = 70;
}
/* set default alarm time */
if (tm.tm_year >= 100)
buff[5] = tm.tm_year-100-1;
else
buff[5] = tm.tm_year-1;
buff[0] = tm.tm_sec;
buff[1] = tm.tm_min;
buff[2] = tm.tm_hour;
buff[3] = tm.tm_mday;
buff[4] = tm.tm_mon + 1;
err = ricoh61x_rtc_set_time(&pdev->dev, &tm);
if (err) {
dev_err(&pdev->dev, "5t61x RTC:\n failed to set time\n");
goto fail;
}
convert_decimal_to_bcd(buff, sizeof(buff));
buff[3] |= 0x80; /* set DAL_EXT */
err = ricoh61x_write_regs(&pdev->dev, rtc_alarm_y_sec, sizeof(buff), buff);
if (err)
printk(KERN_INFO "\n unable to set alarm\n");
}
device_init_wakeup(&pdev->dev, 1);
printk(KERN_INFO "PMU: %s register rtc device\n", __func__);
rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
&ricoh61x_rtc_ops, THIS_MODULE);
/* set interrupt and enable it */
if (rtc->irq != -1) {
rtc->irq = rtc->irq + RICOH61x_IRQ_DALE;
err = request_threaded_irq(rtc->irq, NULL, ricoh61x_rtc_irq,
IRQF_ONESHOT, "rtc_ricoh61x", &pdev->dev);
if (err) {
dev_err(&pdev->dev, "request IRQ:%d fail\n", rtc->irq);
rtc->irq = -1;
err = ricoh61x_rtc_alarm_enable(&pdev->dev, 0);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: enable rtc alarm error\n");
goto fail;
}
} else {
/* enable wake */
enable_irq_wake(rtc->irq);
/* enable alarm_d */
err = ricoh61x_rtc_alarm_enable(&pdev->dev, Alarm_flag);
if (err) {
dev_err(&pdev->dev, "failed rtc setup\n");
err = -EBUSY;
goto fail;
}
}
} else {
/* system don't want to using alarm interrupt, so close it */
err = ricoh61x_rtc_alarm_enable(&pdev->dev, 0);
if (err) {
dev_err(&pdev->dev, "5T61x RTC: Disable rtc alarm error\n");
goto fail;
}
dev_err(&pdev->dev, "ricoh61x interrupt is disabled\n");
}
printk(KERN_INFO "RICOH61x RTC Register Success\n");
device_create_file(&pdev->dev, &dev_attr_ricoh61x_rtc_adjust);
return 0;
fail:
if (!IS_ERR_OR_NULL(rtc->rtc))
rtc_device_unregister(rtc->rtc);
kfree(rtc);
return err;
}
static int __devexit ricoh61x_rtc_remove(struct platform_device *pdev)
{
struct ricoh61x_rtc *rtc = dev_get_drvdata(&pdev->dev);
if (rtc->irq != -1)
free_irq(rtc->irq, rtc);
rtc_device_unregister(rtc->rtc);
kfree(rtc);
return 0;
}
static struct platform_driver ricoh61x_rtc_driver = {
.driver = {
.name = "rtc_ricoh619",
.owner = THIS_MODULE,
},
.probe = ricoh61x_rtc_probe,
.remove = __devexit_p(ricoh61x_rtc_remove),
};
static int __init ricoh61x_rtc_init(void)
{
return platform_driver_register(&ricoh61x_rtc_driver);
}
subsys_initcall_sync(ricoh61x_rtc_init);
static void __exit ricoh61x_rtc_exit(void)
{
platform_driver_unregister(&ricoh61x_rtc_driver);
}
module_exit(ricoh61x_rtc_exit);
MODULE_DESCRIPTION("RICOH RICOH619 RTC driver");
MODULE_ALIAS("platform:rtc_ricoh619");
MODULE_LICENSE("GPL");