diff options
Diffstat (limited to 'drivers/rtc/rtc-spear.c')
| -rw-r--r-- | drivers/rtc/rtc-spear.c | 233 |
1 files changed, 109 insertions, 124 deletions
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index 19a28a671a8..d2cdb9823a1 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -16,6 +16,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/rtc.h> #include <linux/slab.h> @@ -77,9 +78,11 @@ #define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE) struct spear_rtc_config { + struct rtc_device *rtc; struct clk *clk; spinlock_t lock; void __iomem *ioaddr; + unsigned int irq_wake; }; static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config) @@ -149,8 +152,7 @@ static void rtc_wait_not_busy(struct spear_rtc_config *config) static irqreturn_t spear_rtc_irq(int irq, void *dev_id) { - struct rtc_device *rtc = (struct rtc_device *)dev_id; - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_id; unsigned long flags, events = 0; unsigned int irq_data; @@ -161,7 +163,7 @@ static irqreturn_t spear_rtc_irq(int irq, void *dev_id) if ((irq_data & RTC_INT_MASK)) { spear_rtc_clear_interrupt(config); events = RTC_IRQF | RTC_AF; - rtc_update_irq(rtc, 1, events); + rtc_update_irq(config->rtc, 1, events); return IRQ_HANDLED; } else return IRQ_NONE; @@ -203,9 +205,7 @@ static void bcd2tm(struct rtc_time *tm) */ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_get_drvdata(dev); unsigned int time, date; /* we don't report wday/yday/isdst ... */ @@ -234,10 +234,8 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm) */ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); - unsigned int time, date, err = 0; + struct spear_rtc_config *config = dev_get_drvdata(dev); + unsigned int time, date; if (tm2bcd(tm) < 0) return -EINVAL; @@ -249,11 +247,8 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) (tm->tm_year << YEAR_SHIFT); writel(time, config->ioaddr + TIME_REG); writel(date, config->ioaddr + DATE_REG); - err = is_write_complete(config); - if (err < 0) - return err; - return 0; + return is_write_complete(config); } /* @@ -266,9 +261,7 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm) */ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = dev_get_drvdata(dev); unsigned int time, date; rtc_wait_not_busy(config); @@ -298,10 +291,9 @@ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) */ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) { - struct platform_device *pdev = to_platform_device(dev); - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); - unsigned int time, date, err = 0; + struct spear_rtc_config *config = dev_get_drvdata(dev); + unsigned int time, date; + int err; if (tm2bcd(&alm->time) < 0) return -EINVAL; @@ -326,19 +318,44 @@ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) return 0; } + +static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct spear_rtc_config *config = dev_get_drvdata(dev); + int ret = 0; + + spear_rtc_clear_interrupt(config); + + switch (enabled) { + case 0: + /* alarm off */ + spear_rtc_disable_interrupt(config); + break; + case 1: + /* alarm on */ + spear_rtc_enable_interrupt(config); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static struct rtc_class_ops spear_rtc_ops = { .read_time = spear_rtc_read_time, .set_time = spear_rtc_set_time, .read_alarm = spear_rtc_read_alarm, .set_alarm = spear_rtc_set_alarm, + .alarm_irq_enable = spear_alarm_irq_enable, }; -static int __devinit spear_rtc_probe(struct platform_device *pdev) +static int spear_rtc_probe(struct platform_device *pdev) { struct resource *res; - struct rtc_device *rtc; struct spear_rtc_config *config; - unsigned int status = 0; + int status = 0; int irq; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -346,126 +363,86 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "no resource defined\n"); return -EBUSY; } - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - dev_err(&pdev->dev, "rtc region already claimed\n"); - return -EBUSY; - } - config = kzalloc(sizeof(*config), GFP_KERNEL); - if (!config) { - dev_err(&pdev->dev, "out of memory\n"); - status = -ENOMEM; - goto err_release_region; + config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + /* alarm irqs */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no update irq?\n"); + return irq; } - config->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(config->clk)) { - status = PTR_ERR(config->clk); - goto err_kfree; + status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name, + config); + if (status) { + dev_err(&pdev->dev, "Alarm interrupt IRQ%d already claimed\n", + irq); + return status; } - status = clk_enable(config->clk); - if (status < 0) - goto err_clk_put; + config->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(config->ioaddr)) + return PTR_ERR(config->ioaddr); - config->ioaddr = ioremap(res->start, resource_size(res)); - if (!config->ioaddr) { - dev_err(&pdev->dev, "ioremap fail\n"); - status = -ENOMEM; - goto err_disable_clock; - } + config->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(config->clk)) + return PTR_ERR(config->clk); + + status = clk_prepare_enable(config->clk); + if (status < 0) + return status; spin_lock_init(&config->lock); + platform_set_drvdata(pdev, config); - rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops, - THIS_MODULE); - if (IS_ERR(rtc)) { + config->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &spear_rtc_ops, THIS_MODULE); + if (IS_ERR(config->rtc)) { dev_err(&pdev->dev, "can't register RTC device, err %ld\n", - PTR_ERR(rtc)); - status = PTR_ERR(rtc); - goto err_iounmap; - } - - platform_set_drvdata(pdev, rtc); - dev_set_drvdata(&rtc->dev, config); - - /* alarm irqs */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "no update irq?\n"); - status = irq; - goto err_clear_platdata; + PTR_ERR(config->rtc)); + status = PTR_ERR(config->rtc); + goto err_disable_clock; } - status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc); - if (status) { - dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \ - claimed\n", irq); - goto err_clear_platdata; - } + config->rtc->uie_unsupported = 1; if (!device_can_wakeup(&pdev->dev)) device_init_wakeup(&pdev->dev, 1); return 0; -err_clear_platdata: - platform_set_drvdata(pdev, NULL); - dev_set_drvdata(&rtc->dev, NULL); - rtc_device_unregister(rtc); -err_iounmap: - iounmap(config->ioaddr); err_disable_clock: - clk_disable(config->clk); -err_clk_put: - clk_put(config->clk); -err_kfree: - kfree(config); -err_release_region: - release_mem_region(res->start, resource_size(res)); + clk_disable_unprepare(config->clk); return status; } -static int __devexit spear_rtc_remove(struct platform_device *pdev) +static int spear_rtc_remove(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); - int irq; - struct resource *res; + struct spear_rtc_config *config = platform_get_drvdata(pdev); - /* leave rtc running, but disable irqs */ spear_rtc_disable_interrupt(config); + clk_disable_unprepare(config->clk); device_init_wakeup(&pdev->dev, 0); - irq = platform_get_irq(pdev, 0); - if (irq) - free_irq(irq, pdev); - clk_disable(config->clk); - clk_put(config->clk); - iounmap(config->ioaddr); - kfree(config); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res) - release_mem_region(res->start, resource_size(res)); - platform_set_drvdata(pdev, NULL); - dev_set_drvdata(&rtc->dev, NULL); - rtc_device_unregister(rtc); return 0; } -#ifdef CONFIG_PM - -static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int spear_rtc_suspend(struct device *dev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct platform_device *pdev = to_platform_device(dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); int irq; irq = platform_get_irq(pdev, 0); - if (device_may_wakeup(&pdev->dev)) - enable_irq_wake(irq); - else { + if (device_may_wakeup(&pdev->dev)) { + if (!enable_irq_wake(irq)) + config->irq_wake = 1; + } else { spear_rtc_disable_interrupt(config); clk_disable(config->clk); } @@ -473,46 +450,54 @@ static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int spear_rtc_resume(struct platform_device *pdev) +static int spear_rtc_resume(struct device *dev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct platform_device *pdev = to_platform_device(dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); int irq; irq = platform_get_irq(pdev, 0); - if (device_may_wakeup(&pdev->dev)) - disable_irq_wake(irq); - else { + if (device_may_wakeup(&pdev->dev)) { + if (config->irq_wake) { + disable_irq_wake(irq); + config->irq_wake = 0; + } + } else { clk_enable(config->clk); spear_rtc_enable_interrupt(config); } return 0; } - -#else -#define spear_rtc_suspend NULL -#define spear_rtc_resume NULL #endif +static SIMPLE_DEV_PM_OPS(spear_rtc_pm_ops, spear_rtc_suspend, spear_rtc_resume); + static void spear_rtc_shutdown(struct platform_device *pdev) { - struct rtc_device *rtc = platform_get_drvdata(pdev); - struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev); + struct spear_rtc_config *config = platform_get_drvdata(pdev); spear_rtc_disable_interrupt(config); clk_disable(config->clk); } +#ifdef CONFIG_OF +static const struct of_device_id spear_rtc_id_table[] = { + { .compatible = "st,spear600-rtc" }, + {} +}; +MODULE_DEVICE_TABLE(of, spear_rtc_id_table); +#endif + static struct platform_driver spear_rtc_driver = { .probe = spear_rtc_probe, - .remove = __devexit_p(spear_rtc_remove), - .suspend = spear_rtc_suspend, - .resume = spear_rtc_resume, + .remove = spear_rtc_remove, .shutdown = spear_rtc_shutdown, .driver = { .name = "rtc-spear", + .pm = &spear_rtc_pm_ops, + .of_match_table = of_match_ptr(spear_rtc_id_table), }, }; |
