diff options
Diffstat (limited to 'drivers/rtc/rtc-s3c.c')
| -rw-r--r-- | drivers/rtc/rtc-s3c.c | 432 |
1 files changed, 82 insertions, 350 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index e1b50e682fc4..291c0ccb0acd 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -23,7 +23,6 @@ #include <linux/log2.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/uaccess.h> #include <linux/io.h> @@ -42,26 +41,15 @@ struct s3c_rtc { const struct s3c_rtc_data *data; int irq_alarm; - int irq_tick; - - spinlock_t pie_lock; spinlock_t alarm_lock; - int ticnt_save; - int ticnt_en_save; bool wake_en; }; struct s3c_rtc_data { - int max_user_freq; bool needs_src_clk; void (*irq_handler) (struct s3c_rtc *info, int mask); - void (*set_freq) (struct s3c_rtc *info, int freq); - void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); - void (*select_tick_clk) (struct s3c_rtc *info); - void (*save_tick_cnt) (struct s3c_rtc *info); - void (*restore_tick_cnt) (struct s3c_rtc *info); void (*enable) (struct s3c_rtc *info); void (*disable) (struct s3c_rtc *info); }; @@ -91,17 +79,7 @@ static void s3c_rtc_disable_clk(struct s3c_rtc *info) clk_disable(info->rtc_clk); } -/* IRQ Handlers */ -static irqreturn_t s3c_rtc_tickirq(int irq, void *id) -{ - struct s3c_rtc *info = (struct s3c_rtc *)id; - - if (info->data->irq_handler) - info->data->irq_handler(info, S3C2410_INTP_TIC); - - return IRQ_HANDLED; -} - +/* IRQ Handler */ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) { struct s3c_rtc *info = (struct s3c_rtc *)id; @@ -148,32 +126,9 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) return ret; } -/* Set RTC frequency */ -static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) -{ - int ret; - - if (!is_power_of_2(freq)) - return -EINVAL; - - ret = s3c_rtc_enable_clk(info); - if (ret) - return ret; - spin_lock_irq(&info->pie_lock); - - if (info->data->set_freq) - info->data->set_freq(info, freq); - - spin_unlock_irq(&info->pie_lock); - s3c_rtc_disable_clk(info); - - return 0; -} - -/* Time read/write */ -static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +/* Read time from RTC and convert it from BCD */ +static int s3c_rtc_read_time(struct s3c_rtc *info, struct rtc_time *tm) { - struct s3c_rtc *info = dev_get_drvdata(dev); unsigned int have_retried = 0; int ret; @@ -182,54 +137,40 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) return ret; retry_get_time: - rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN); - rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); - rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE); - rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON); - rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR); - rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC); - - /* the only way to work out whether the system was mid-update + tm->tm_min = readb(info->base + S3C2410_RTCMIN); + tm->tm_hour = readb(info->base + S3C2410_RTCHOUR); + tm->tm_mday = readb(info->base + S3C2410_RTCDATE); + tm->tm_mon = readb(info->base + S3C2410_RTCMON); + tm->tm_year = readb(info->base + S3C2410_RTCYEAR); + tm->tm_sec = readb(info->base + S3C2410_RTCSEC); + + /* + * The only way to work out whether the system was mid-update * when we read it is to check the second counter, and if it * is zero, then we re-try the entire read */ - - if (rtc_tm->tm_sec == 0 && !have_retried) { + if (tm->tm_sec == 0 && !have_retried) { have_retried = 1; goto retry_get_time; } - rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); - rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); - rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); - rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); - rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); - rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); - s3c_rtc_disable_clk(info); - rtc_tm->tm_year += 100; - rtc_tm->tm_mon -= 1; + tm->tm_sec = bcd2bin(tm->tm_sec); + tm->tm_min = bcd2bin(tm->tm_min); + tm->tm_hour = bcd2bin(tm->tm_hour); + tm->tm_mday = bcd2bin(tm->tm_mday); + tm->tm_mon = bcd2bin(tm->tm_mon); + tm->tm_year = bcd2bin(tm->tm_year); - dev_dbg(dev, "read time %ptR\n", rtc_tm); return 0; } -static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) +/* Convert time to BCD and write it to RTC */ +static int s3c_rtc_write_time(struct s3c_rtc *info, const struct rtc_time *tm) { - struct s3c_rtc *info = dev_get_drvdata(dev); - int year = tm->tm_year - 100; int ret; - dev_dbg(dev, "set time %ptR\n", tm); - - /* we get around y2k by simply not supporting it */ - - if (year < 0 || year >= 100) { - dev_err(dev, "rtc only supports 100 years\n"); - return -EINVAL; - } - ret = s3c_rtc_enable_clk(info); if (ret) return ret; @@ -238,14 +179,48 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN); writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR); writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE); - writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON); - writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR); + writeb(bin2bcd(tm->tm_mon), info->base + S3C2410_RTCMON); + writeb(bin2bcd(tm->tm_year), info->base + S3C2410_RTCYEAR); s3c_rtc_disable_clk(info); return 0; } +static int s3c_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + int ret; + + ret = s3c_rtc_read_time(info, tm); + if (ret) + return ret; + + /* Convert internal representation to actual date/time */ + tm->tm_year += 100; + tm->tm_mon -= 1; + + dev_dbg(dev, "read time %ptR\n", tm); + return 0; +} + +static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct s3c_rtc *info = dev_get_drvdata(dev); + struct rtc_time rtc_tm = *tm; + + dev_dbg(dev, "set time %ptR\n", tm); + + /* + * Convert actual date/time to internal representation. + * We get around Y2K by simply not supporting it. + */ + rtc_tm.tm_year -= 100; + rtc_tm.tm_mon += 1; + + return s3c_rtc_write_time(info, &rtc_tm); +} + static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) { struct s3c_rtc *info = dev_get_drvdata(dev); @@ -348,33 +323,15 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) return 0; } -static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) -{ - struct s3c_rtc *info = dev_get_drvdata(dev); - int ret; - - ret = s3c_rtc_enable_clk(info); - if (ret) - return ret; - - if (info->data->enable_tick) - info->data->enable_tick(info, seq); - - s3c_rtc_disable_clk(info); - - return 0; -} - static const struct rtc_class_ops s3c_rtcops = { .read_time = s3c_rtc_gettime, .set_time = s3c_rtc_settime, .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_setalarm, - .proc = s3c_rtc_proc, .alarm_irq_enable = s3c_rtc_setaie, }; -static void s3c24xx_rtc_enable(struct s3c_rtc *info) +static void s3c6410_rtc_enable(struct s3c_rtc *info) { unsigned int con, tmp; @@ -404,19 +361,6 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info) } } -static void s3c24xx_rtc_disable(struct s3c_rtc *info) -{ - unsigned int con; - - con = readw(info->base + S3C2410_RTCCON); - con &= ~S3C2410_RTCCON_RTCEN; - writew(con, info->base + S3C2410_RTCCON); - - con = readb(info->base + S3C2410_TICNT); - con &= ~S3C2410_TICNT_ENABLE; - writeb(con, info->base + S3C2410_TICNT); -} - static void s3c6410_rtc_disable(struct s3c_rtc *info) { unsigned int con; @@ -427,7 +371,7 @@ static void s3c6410_rtc_disable(struct s3c_rtc *info) writew(con, info->base + S3C2410_RTCCON); } -static int s3c_rtc_remove(struct platform_device *pdev) +static void s3c_rtc_remove(struct platform_device *pdev) { struct s3c_rtc *info = platform_get_drvdata(pdev); @@ -436,32 +380,23 @@ static int s3c_rtc_remove(struct platform_device *pdev) if (info->data->needs_src_clk) clk_unprepare(info->rtc_src_clk); clk_unprepare(info->rtc_clk); - - return 0; } static int s3c_rtc_probe(struct platform_device *pdev) { struct s3c_rtc *info = NULL; - struct rtc_time rtc_tm; int ret; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; - /* find the IRQs */ - info->irq_tick = platform_get_irq(pdev, 1); - if (info->irq_tick < 0) - return info->irq_tick; - info->dev = &pdev->dev; info->data = of_device_get_match_data(&pdev->dev); if (!info->data) { dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); return -EINVAL; } - spin_lock_init(&info->pie_lock); spin_lock_init(&info->alarm_lock); platform_set_drvdata(pdev, info); @@ -470,8 +405,7 @@ static int s3c_rtc_probe(struct platform_device *pdev) if (info->irq_alarm < 0) return info->irq_alarm; - dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n", - info->irq_tick, info->irq_alarm); + dev_dbg(&pdev->dev, "s3c2410_rtc: alarm irq %d\n", info->irq_alarm); /* get the memory region */ info->base = devm_platform_ioremap_resource(pdev, 0); @@ -479,14 +413,9 @@ static int s3c_rtc_probe(struct platform_device *pdev) return PTR_ERR(info->base); info->rtc_clk = devm_clk_get(&pdev->dev, "rtc"); - if (IS_ERR(info->rtc_clk)) { - ret = PTR_ERR(info->rtc_clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to find rtc clock\n"); - else - dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n"); - return ret; - } + if (IS_ERR(info->rtc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_clk), + "failed to find rtc clock\n"); ret = clk_prepare_enable(info->rtc_clk); if (ret) return ret; @@ -494,13 +423,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) if (info->data->needs_src_clk) { info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); if (IS_ERR(info->rtc_src_clk)) { - ret = PTR_ERR(info->rtc_src_clk); - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to find rtc source clock\n"); - else - dev_dbg(&pdev->dev, - "probe deferred due to missing rtc src clk\n"); + ret = dev_err_probe(&pdev->dev, PTR_ERR(info->rtc_src_clk), + "failed to find rtc source clock\n"); goto err_src_clk; } ret = clk_prepare_enable(info->rtc_src_clk); @@ -508,6 +432,10 @@ static int s3c_rtc_probe(struct platform_device *pdev) goto err_src_clk; } + /* disable RTC enable bits potentially set by the bootloader */ + if (info->data->disable) + info->data->disable(info); + /* check to see if everything is setup correctly */ if (info->data->enable) info->data->enable(info); @@ -515,31 +443,22 @@ static int s3c_rtc_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", readw(info->base + S3C2410_RTCCON)); - device_init_wakeup(&pdev->dev, 1); - - /* Check RTC Time */ - if (s3c_rtc_gettime(&pdev->dev, &rtc_tm)) { - rtc_tm.tm_year = 100; - rtc_tm.tm_mon = 0; - rtc_tm.tm_mday = 1; - rtc_tm.tm_hour = 0; - rtc_tm.tm_min = 0; - rtc_tm.tm_sec = 0; - - s3c_rtc_settime(&pdev->dev, &rtc_tm); + device_init_wakeup(&pdev->dev, true); - dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); - } - - /* register RTC and exit */ - info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops, - THIS_MODULE); + info->rtc = devm_rtc_allocate_device(&pdev->dev); if (IS_ERR(info->rtc)) { - dev_err(&pdev->dev, "cannot attach rtc\n"); ret = PTR_ERR(info->rtc); goto err_nortc; } + info->rtc->ops = &s3c_rtcops; + info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + info->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = devm_rtc_register_device(info->rtc); + if (ret) + goto err_nortc; + ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq, 0, "s3c2410-rtc alarm", info); if (ret) { @@ -547,18 +466,6 @@ static int s3c_rtc_probe(struct platform_device *pdev) goto err_nortc; } - ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq, - 0, "s3c2410-rtc tick", info); - if (ret) { - dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret); - goto err_nortc; - } - - if (info->data->select_tick_clk) - info->data->select_tick_clk(info); - - s3c_rtc_setfreq(info, 1); - s3c_rtc_disable_clk(info); return 0; @@ -586,10 +493,6 @@ static int s3c_rtc_suspend(struct device *dev) if (ret) return ret; - /* save TICNT for anyone using periodic interrupts */ - if (info->data->save_tick_cnt) - info->data->save_tick_cnt(info); - if (info->data->disable) info->data->disable(info); @@ -610,9 +513,6 @@ static int s3c_rtc_resume(struct device *dev) if (info->data->enable) info->data->enable(info); - if (info->data->restore_tick_cnt) - info->data->restore_tick_cnt(info); - s3c_rtc_disable_clk(info); if (device_may_wakeup(dev) && info->wake_en) { @@ -625,188 +525,21 @@ static int s3c_rtc_resume(struct device *dev) #endif static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); -static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) -{ - rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); -} - static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) { rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); writeb(mask, info->base + S3C2410_INTP); } -static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) -{ - unsigned int tmp = 0; - int val; - - tmp = readb(info->base + S3C2410_TICNT); - tmp &= S3C2410_TICNT_ENABLE; - - val = (info->rtc->max_user_freq / freq) - 1; - tmp |= val; - - writel(tmp, info->base + S3C2410_TICNT); -} - -static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) -{ - unsigned int tmp = 0; - int val; - - tmp = readb(info->base + S3C2410_TICNT); - tmp &= S3C2410_TICNT_ENABLE; - - val = (info->rtc->max_user_freq / freq) - 1; - - tmp |= S3C2443_TICNT_PART(val); - writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); - - writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); - - writel(tmp, info->base + S3C2410_TICNT); -} - -static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) -{ - unsigned int tmp = 0; - int val; - - tmp = readb(info->base + S3C2410_TICNT); - tmp &= S3C2410_TICNT_ENABLE; - - val = (info->rtc->max_user_freq / freq) - 1; - - tmp |= S3C2443_TICNT_PART(val); - writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); - - writel(tmp, info->base + S3C2410_TICNT); -} - -static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) -{ - int val; - - val = (info->rtc->max_user_freq / freq) - 1; - writel(val, info->base + S3C2410_TICNT); -} - -static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) -{ - unsigned int ticnt; - - ticnt = readb(info->base + S3C2410_TICNT); - ticnt &= S3C2410_TICNT_ENABLE; - - seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); -} - -static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) -{ - unsigned int con; - - con = readw(info->base + S3C2410_RTCCON); - con |= S3C2443_RTCCON_TICSEL; - writew(con, info->base + S3C2410_RTCCON); -} - -static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) -{ - unsigned int ticnt; - - ticnt = readw(info->base + S3C2410_RTCCON); - ticnt &= S3C64XX_RTCCON_TICEN; - - seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); -} - -static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) -{ - info->ticnt_save = readb(info->base + S3C2410_TICNT); -} - -static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) -{ - writeb(info->ticnt_save, info->base + S3C2410_TICNT); -} - -static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) -{ - info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); - info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; - info->ticnt_save = readl(info->base + S3C2410_TICNT); -} - -static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) -{ - unsigned int con; - - writel(info->ticnt_save, info->base + S3C2410_TICNT); - if (info->ticnt_en_save) { - con = readw(info->base + S3C2410_RTCCON); - writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON); - } -} - -static struct s3c_rtc_data const s3c2410_rtc_data = { - .max_user_freq = 128, - .irq_handler = s3c24xx_rtc_irq, - .set_freq = s3c2410_rtc_setfreq, - .enable_tick = s3c24xx_rtc_enable_tick, - .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, - .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, - .enable = s3c24xx_rtc_enable, - .disable = s3c24xx_rtc_disable, -}; - -static struct s3c_rtc_data const s3c2416_rtc_data = { - .max_user_freq = 32768, - .irq_handler = s3c24xx_rtc_irq, - .set_freq = s3c2416_rtc_setfreq, - .enable_tick = s3c24xx_rtc_enable_tick, - .select_tick_clk = s3c2416_rtc_select_tick_clk, - .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, - .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, - .enable = s3c24xx_rtc_enable, - .disable = s3c24xx_rtc_disable, -}; - -static struct s3c_rtc_data const s3c2443_rtc_data = { - .max_user_freq = 32768, - .irq_handler = s3c24xx_rtc_irq, - .set_freq = s3c2443_rtc_setfreq, - .enable_tick = s3c24xx_rtc_enable_tick, - .select_tick_clk = s3c2416_rtc_select_tick_clk, - .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, - .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, - .enable = s3c24xx_rtc_enable, - .disable = s3c24xx_rtc_disable, -}; - -static struct s3c_rtc_data const s3c6410_rtc_data = { - .max_user_freq = 32768, +static const struct s3c_rtc_data s3c6410_rtc_data = { .needs_src_clk = true, .irq_handler = s3c6410_rtc_irq, - .set_freq = s3c6410_rtc_setfreq, - .enable_tick = s3c6410_rtc_enable_tick, - .save_tick_cnt = s3c6410_rtc_save_tick_cnt, - .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, - .enable = s3c24xx_rtc_enable, + .enable = s3c6410_rtc_enable, .disable = s3c6410_rtc_disable, }; -static const struct of_device_id s3c_rtc_dt_match[] = { +static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = { { - .compatible = "samsung,s3c2410-rtc", - .data = &s3c2410_rtc_data, - }, { - .compatible = "samsung,s3c2416-rtc", - .data = &s3c2416_rtc_data, - }, { - .compatible = "samsung,s3c2443-rtc", - .data = &s3c2443_rtc_data, - }, { .compatible = "samsung,s3c6410-rtc", .data = &s3c6410_rtc_data, }, { @@ -831,4 +564,3 @@ module_platform_driver(s3c_rtc_driver); MODULE_DESCRIPTION("Samsung S3C RTC Driver"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:s3c2410-rtc"); |
