aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-spear.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/rtc-spear.c')
-rw-r--r--drivers/rtc/rtc-spear.c233
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),
},
};