Commit 618161f7 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Linus Torvalds

rtc-ds1553: fix races around device registration

* Call dev_set_drvdata before rtc device creation
* Use its own spinlock instead of rtc->irq_lock
* Check pdata->rtc before calling rtc_update_irq
* Use {alarm,update}_irq_enable and remove ioctl routine
* Use devres APIs and simplify error/remove path
Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Acked-by: default avatarAlessandro Zummo <a.zummo@towertech.it>
Cc: David Brownell <david-b@pacbell.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent af69a180
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/io.h> #include <linux/io.h>
#define DRV_VERSION "0.2" #define DRV_VERSION "0.3"
#define RTC_REG_SIZE 0x2000 #define RTC_REG_SIZE 0x2000
#define RTC_OFFSET 0x1ff0 #define RTC_OFFSET 0x1ff0
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
struct rtc_plat_data { struct rtc_plat_data {
struct rtc_device *rtc; struct rtc_device *rtc;
void __iomem *ioaddr; void __iomem *ioaddr;
resource_size_t baseaddr;
unsigned long last_jiffies; unsigned long last_jiffies;
int irq; int irq;
unsigned int irqen; unsigned int irqen;
...@@ -69,6 +68,7 @@ struct rtc_plat_data { ...@@ -69,6 +68,7 @@ struct rtc_plat_data {
int alrm_min; int alrm_min;
int alrm_hour; int alrm_hour;
int alrm_mday; int alrm_mday;
spinlock_t lock;
}; };
static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm) static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm)
...@@ -139,7 +139,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) ...@@ -139,7 +139,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
void __iomem *ioaddr = pdata->ioaddr; void __iomem *ioaddr = pdata->ioaddr;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&pdata->rtc->irq_lock, flags); spin_lock_irqsave(&pdata->lock, flags);
writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
0x80 : bin2bcd(pdata->alrm_mday), 0x80 : bin2bcd(pdata->alrm_mday),
ioaddr + RTC_DATE_ALARM); ioaddr + RTC_DATE_ALARM);
...@@ -154,7 +154,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) ...@@ -154,7 +154,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
ioaddr + RTC_SECONDS_ALARM); ioaddr + RTC_SECONDS_ALARM);
writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS); writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
readb(ioaddr + RTC_FLAGS); /* clear interrupts */ readb(ioaddr + RTC_FLAGS); /* clear interrupts */
spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags); spin_unlock_irqrestore(&pdata->lock, flags);
} }
static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
...@@ -194,56 +194,61 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) ...@@ -194,56 +194,61 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
struct platform_device *pdev = dev_id; struct platform_device *pdev = dev_id;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr; void __iomem *ioaddr = pdata->ioaddr;
unsigned long events = RTC_IRQF; unsigned long events = 0;
spin_lock(&pdata->lock);
/* read and clear interrupt */ /* read and clear interrupt */
if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF)) if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
return IRQ_NONE; events = RTC_IRQF;
if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
events |= RTC_UF; events |= RTC_UF;
else else
events |= RTC_AF; events |= RTC_AF;
rtc_update_irq(pdata->rtc, 1, events); if (likely(pdata->rtc))
return IRQ_HANDLED; rtc_update_irq(pdata->rtc, 1, events);
}
spin_unlock(&pdata->lock);
return events ? IRQ_HANDLED : IRQ_NONE;
} }
static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd, static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
unsigned long arg)
{ {
struct platform_device *pdev = to_platform_device(dev); struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev); struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq <= 0) if (pdata->irq <= 0)
return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */ return -EINVAL;
switch (cmd) { if (enabled)
case RTC_AIE_OFF:
pdata->irqen &= ~RTC_AF;
ds1553_rtc_update_alarm(pdata);
break;
case RTC_AIE_ON:
pdata->irqen |= RTC_AF; pdata->irqen |= RTC_AF;
ds1553_rtc_update_alarm(pdata); else
break; pdata->irqen &= ~RTC_AF;
case RTC_UIE_OFF: ds1553_rtc_update_alarm(pdata);
pdata->irqen &= ~RTC_UF; return 0;
ds1553_rtc_update_alarm(pdata); }
break;
case RTC_UIE_ON: static int ds1553_rtc_update_irq_enable(struct device *dev,
unsigned int enabled)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq <= 0)
return -EINVAL;
if (enabled)
pdata->irqen |= RTC_UF; pdata->irqen |= RTC_UF;
ds1553_rtc_update_alarm(pdata); else
break; pdata->irqen &= ~RTC_UF;
default: ds1553_rtc_update_alarm(pdata);
return -ENOIOCTLCMD;
}
return 0; return 0;
} }
static const struct rtc_class_ops ds1553_rtc_ops = { static const struct rtc_class_ops ds1553_rtc_ops = {
.read_time = ds1553_rtc_read_time, .read_time = ds1553_rtc_read_time,
.set_time = ds1553_rtc_set_time, .set_time = ds1553_rtc_set_time,
.read_alarm = ds1553_rtc_read_alarm, .read_alarm = ds1553_rtc_read_alarm,
.set_alarm = ds1553_rtc_set_alarm, .set_alarm = ds1553_rtc_set_alarm,
.ioctl = ds1553_rtc_ioctl, .alarm_irq_enable = ds1553_rtc_alarm_irq_enable,
.update_irq_enable = ds1553_rtc_update_irq_enable,
}; };
static ssize_t ds1553_nvram_read(struct kobject *kobj, static ssize_t ds1553_nvram_read(struct kobject *kobj,
...@@ -291,26 +296,23 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) ...@@ -291,26 +296,23 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
struct rtc_device *rtc; struct rtc_device *rtc;
struct resource *res; struct resource *res;
unsigned int cen, sec; unsigned int cen, sec;
struct rtc_plat_data *pdata = NULL; struct rtc_plat_data *pdata;
void __iomem *ioaddr = NULL; void __iomem *ioaddr;
int ret = 0; int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) if (!res)
return -ENODEV; return -ENODEV;
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata) if (!pdata)
return -ENOMEM; return -ENOMEM;
if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) { if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE,
ret = -EBUSY; pdev->name))
goto out; return -EBUSY;
}
pdata->baseaddr = res->start; ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE);
ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE); if (!ioaddr)
if (!ioaddr) { return -ENOMEM;
ret = -ENOMEM;
goto out;
}
pdata->ioaddr = ioaddr; pdata->ioaddr = ioaddr;
pdata->irq = platform_get_irq(pdev, 0); pdata->irq = platform_get_irq(pdev, 0);
...@@ -326,9 +328,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) ...@@ -326,9 +328,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF) if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF)
dev_warn(&pdev->dev, "voltage-low detected.\n"); dev_warn(&pdev->dev, "voltage-low detected.\n");
spin_lock_init(&pdata->lock);
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
if (pdata->irq > 0) { if (pdata->irq > 0) {
writeb(0, ioaddr + RTC_INTERRUPTS); writeb(0, ioaddr + RTC_INTERRUPTS);
if (request_irq(pdata->irq, ds1553_rtc_interrupt, if (devm_request_irq(&pdev->dev, pdata->irq,
ds1553_rtc_interrupt,
IRQF_DISABLED, pdev->name, pdev) < 0) { IRQF_DISABLED, pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n"); dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = 0; pdata->irq = 0;
...@@ -337,27 +343,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) ...@@ -337,27 +343,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
rtc = rtc_device_register(pdev->name, &pdev->dev, rtc = rtc_device_register(pdev->name, &pdev->dev,
&ds1553_rtc_ops, THIS_MODULE); &ds1553_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) { if (IS_ERR(rtc))
ret = PTR_ERR(rtc); return PTR_ERR(rtc);
goto out;
}
pdata->rtc = rtc; pdata->rtc = rtc;
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
if (ret) if (ret)
goto out; rtc_device_unregister(rtc);
return 0;
out:
if (pdata->rtc)
rtc_device_unregister(pdata->rtc);
if (pdata->irq > 0)
free_irq(pdata->irq, pdev);
if (ioaddr)
iounmap(ioaddr);
if (pdata->baseaddr)
release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
kfree(pdata);
return ret; return ret;
} }
...@@ -367,13 +359,8 @@ static int __devexit ds1553_rtc_remove(struct platform_device *pdev) ...@@ -367,13 +359,8 @@ static int __devexit ds1553_rtc_remove(struct platform_device *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
rtc_device_unregister(pdata->rtc); rtc_device_unregister(pdata->rtc);
if (pdata->irq > 0) { if (pdata->irq > 0)
writeb(0, pdata->ioaddr + RTC_INTERRUPTS); writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
free_irq(pdata->irq, pdev);
}
iounmap(pdata->ioaddr);
release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
kfree(pdata);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment