diff options
Diffstat (limited to 'drivers/rtc/rtc-ds1553.c')
| -rw-r--r-- | drivers/rtc/rtc-ds1553.c | 149 |
1 files changed, 68 insertions, 81 deletions
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index 717288527c6b..ed1ef7c9cc06 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c | |||
| @@ -18,7 +18,7 @@ | |||
| 18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
| 19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
| 20 | 20 | ||
| 21 | #define DRV_VERSION "0.2" | 21 | #define DRV_VERSION "0.3" |
| 22 | 22 | ||
| 23 | #define RTC_REG_SIZE 0x2000 | 23 | #define RTC_REG_SIZE 0x2000 |
| 24 | #define RTC_OFFSET 0x1ff0 | 24 | #define RTC_OFFSET 0x1ff0 |
| @@ -61,7 +61,6 @@ | |||
| 61 | struct rtc_plat_data { | 61 | struct rtc_plat_data { |
| 62 | struct rtc_device *rtc; | 62 | struct rtc_device *rtc; |
| 63 | void __iomem *ioaddr; | 63 | void __iomem *ioaddr; |
| 64 | resource_size_t baseaddr; | ||
| 65 | unsigned long last_jiffies; | 64 | unsigned long last_jiffies; |
| 66 | int irq; | 65 | int irq; |
| 67 | unsigned int irqen; | 66 | unsigned int irqen; |
| @@ -69,6 +68,7 @@ struct rtc_plat_data { | |||
| 69 | int alrm_min; | 68 | int alrm_min; |
| 70 | int alrm_hour; | 69 | int alrm_hour; |
| 71 | int alrm_mday; | 70 | int alrm_mday; |
| 71 | spinlock_t lock; | ||
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm) | 74 | 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 | void __iomem *ioaddr = pdata->ioaddr; | 139 | void __iomem *ioaddr = pdata->ioaddr; |
| 140 | unsigned long flags; | 140 | unsigned long flags; |
| 141 | 141 | ||
| 142 | spin_lock_irqsave(&pdata->rtc->irq_lock, flags); | 142 | spin_lock_irqsave(&pdata->lock, flags); |
| 143 | writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? | 143 | writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ? |
| 144 | 0x80 : bin2bcd(pdata->alrm_mday), | 144 | 0x80 : bin2bcd(pdata->alrm_mday), |
| 145 | ioaddr + RTC_DATE_ALARM); | 145 | ioaddr + RTC_DATE_ALARM); |
| @@ -154,7 +154,7 @@ static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata) | |||
| 154 | ioaddr + RTC_SECONDS_ALARM); | 154 | ioaddr + RTC_SECONDS_ALARM); |
| 155 | writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS); | 155 | writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS); |
| 156 | readb(ioaddr + RTC_FLAGS); /* clear interrupts */ | 156 | readb(ioaddr + RTC_FLAGS); /* clear interrupts */ |
| 157 | spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags); | 157 | spin_unlock_irqrestore(&pdata->lock, flags); |
| 158 | } | 158 | } |
| 159 | 159 | ||
| 160 | static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | 160 | static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
| @@ -194,64 +194,69 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id) | |||
| 194 | struct platform_device *pdev = dev_id; | 194 | struct platform_device *pdev = dev_id; |
| 195 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | 195 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
| 196 | void __iomem *ioaddr = pdata->ioaddr; | 196 | void __iomem *ioaddr = pdata->ioaddr; |
| 197 | unsigned long events = RTC_IRQF; | 197 | unsigned long events = 0; |
| 198 | 198 | ||
| 199 | spin_lock(&pdata->lock); | ||
| 199 | /* read and clear interrupt */ | 200 | /* read and clear interrupt */ |
| 200 | if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF)) | 201 | if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) { |
| 201 | return IRQ_NONE; | 202 | events = RTC_IRQF; |
| 202 | if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) | 203 | if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80) |
| 203 | events |= RTC_UF; | 204 | events |= RTC_UF; |
| 204 | else | 205 | else |
| 205 | events |= RTC_AF; | 206 | events |= RTC_AF; |
| 206 | rtc_update_irq(pdata->rtc, 1, events); | 207 | if (likely(pdata->rtc)) |
| 207 | return IRQ_HANDLED; | 208 | rtc_update_irq(pdata->rtc, 1, events); |
| 209 | } | ||
| 210 | spin_unlock(&pdata->lock); | ||
| 211 | return events ? IRQ_HANDLED : IRQ_NONE; | ||
| 208 | } | 212 | } |
| 209 | 213 | ||
| 210 | static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd, | 214 | static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
| 211 | unsigned long arg) | ||
| 212 | { | 215 | { |
| 213 | struct platform_device *pdev = to_platform_device(dev); | 216 | struct platform_device *pdev = to_platform_device(dev); |
| 214 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | 217 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
| 215 | 218 | ||
| 216 | if (pdata->irq <= 0) | 219 | if (pdata->irq <= 0) |
| 217 | return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */ | 220 | return -EINVAL; |
| 218 | switch (cmd) { | 221 | if (enabled) |
| 219 | case RTC_AIE_OFF: | ||
| 220 | pdata->irqen &= ~RTC_AF; | ||
| 221 | ds1553_rtc_update_alarm(pdata); | ||
| 222 | break; | ||
| 223 | case RTC_AIE_ON: | ||
| 224 | pdata->irqen |= RTC_AF; | 222 | pdata->irqen |= RTC_AF; |
| 225 | ds1553_rtc_update_alarm(pdata); | 223 | else |
| 226 | break; | 224 | pdata->irqen &= ~RTC_AF; |
| 227 | case RTC_UIE_OFF: | 225 | ds1553_rtc_update_alarm(pdata); |
| 228 | pdata->irqen &= ~RTC_UF; | 226 | return 0; |
| 229 | ds1553_rtc_update_alarm(pdata); | 227 | } |
| 230 | break; | 228 | |
| 231 | case RTC_UIE_ON: | 229 | static int ds1553_rtc_update_irq_enable(struct device *dev, |
| 230 | unsigned int enabled) | ||
| 231 | { | ||
| 232 | struct platform_device *pdev = to_platform_device(dev); | ||
| 233 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 234 | |||
| 235 | if (pdata->irq <= 0) | ||
| 236 | return -EINVAL; | ||
| 237 | if (enabled) | ||
| 232 | pdata->irqen |= RTC_UF; | 238 | pdata->irqen |= RTC_UF; |
| 233 | ds1553_rtc_update_alarm(pdata); | 239 | else |
| 234 | break; | 240 | pdata->irqen &= ~RTC_UF; |
| 235 | default: | 241 | ds1553_rtc_update_alarm(pdata); |
| 236 | return -ENOIOCTLCMD; | ||
| 237 | } | ||
| 238 | return 0; | 242 | return 0; |
| 239 | } | 243 | } |
| 240 | 244 | ||
| 241 | static const struct rtc_class_ops ds1553_rtc_ops = { | 245 | static const struct rtc_class_ops ds1553_rtc_ops = { |
| 242 | .read_time = ds1553_rtc_read_time, | 246 | .read_time = ds1553_rtc_read_time, |
| 243 | .set_time = ds1553_rtc_set_time, | 247 | .set_time = ds1553_rtc_set_time, |
| 244 | .read_alarm = ds1553_rtc_read_alarm, | 248 | .read_alarm = ds1553_rtc_read_alarm, |
| 245 | .set_alarm = ds1553_rtc_set_alarm, | 249 | .set_alarm = ds1553_rtc_set_alarm, |
| 246 | .ioctl = ds1553_rtc_ioctl, | 250 | .alarm_irq_enable = ds1553_rtc_alarm_irq_enable, |
| 251 | .update_irq_enable = ds1553_rtc_update_irq_enable, | ||
| 247 | }; | 252 | }; |
| 248 | 253 | ||
| 249 | static ssize_t ds1553_nvram_read(struct kobject *kobj, | 254 | static ssize_t ds1553_nvram_read(struct kobject *kobj, |
| 250 | struct bin_attribute *bin_attr, | 255 | struct bin_attribute *bin_attr, |
| 251 | char *buf, loff_t pos, size_t size) | 256 | char *buf, loff_t pos, size_t size) |
| 252 | { | 257 | { |
| 253 | struct platform_device *pdev = | 258 | struct device *dev = container_of(kobj, struct device, kobj); |
| 254 | to_platform_device(container_of(kobj, struct device, kobj)); | 259 | struct platform_device *pdev = to_platform_device(dev); |
| 255 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | 260 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
| 256 | void __iomem *ioaddr = pdata->ioaddr; | 261 | void __iomem *ioaddr = pdata->ioaddr; |
| 257 | ssize_t count; | 262 | ssize_t count; |
| @@ -265,8 +270,8 @@ static ssize_t ds1553_nvram_write(struct kobject *kobj, | |||
| 265 | struct bin_attribute *bin_attr, | 270 | struct bin_attribute *bin_attr, |
| 266 | char *buf, loff_t pos, size_t size) | 271 | char *buf, loff_t pos, size_t size) |
| 267 | { | 272 | { |
| 268 | struct platform_device *pdev = | 273 | struct device *dev = container_of(kobj, struct device, kobj); |
| 269 | to_platform_device(container_of(kobj, struct device, kobj)); | 274 | struct platform_device *pdev = to_platform_device(dev); |
| 270 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | 275 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); |
| 271 | void __iomem *ioaddr = pdata->ioaddr; | 276 | void __iomem *ioaddr = pdata->ioaddr; |
| 272 | ssize_t count; | 277 | ssize_t count; |
| @@ -291,26 +296,23 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) | |||
| 291 | struct rtc_device *rtc; | 296 | struct rtc_device *rtc; |
| 292 | struct resource *res; | 297 | struct resource *res; |
| 293 | unsigned int cen, sec; | 298 | unsigned int cen, sec; |
| 294 | struct rtc_plat_data *pdata = NULL; | 299 | struct rtc_plat_data *pdata; |
| 295 | void __iomem *ioaddr = NULL; | 300 | void __iomem *ioaddr; |
| 296 | int ret = 0; | 301 | int ret = 0; |
| 297 | 302 | ||
| 298 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 303 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 299 | if (!res) | 304 | if (!res) |
| 300 | return -ENODEV; | 305 | return -ENODEV; |
| 301 | pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); | 306 | pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); |
| 302 | if (!pdata) | 307 | if (!pdata) |
| 303 | return -ENOMEM; | 308 | return -ENOMEM; |
| 304 | if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) { | 309 | if (!devm_request_mem_region(&pdev->dev, res->start, RTC_REG_SIZE, |
| 305 | ret = -EBUSY; | 310 | pdev->name)) |
| 306 | goto out; | 311 | return -EBUSY; |
| 307 | } | 312 | |
| 308 | pdata->baseaddr = res->start; | 313 | ioaddr = devm_ioremap(&pdev->dev, res->start, RTC_REG_SIZE); |
| 309 | ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE); | 314 | if (!ioaddr) |
| 310 | if (!ioaddr) { | 315 | return -ENOMEM; |
| 311 | ret = -ENOMEM; | ||
| 312 | goto out; | ||
| 313 | } | ||
| 314 | pdata->ioaddr = ioaddr; | 316 | pdata->ioaddr = ioaddr; |
| 315 | pdata->irq = platform_get_irq(pdev, 0); | 317 | pdata->irq = platform_get_irq(pdev, 0); |
| 316 | 318 | ||
| @@ -326,9 +328,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) | |||
| 326 | if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF) | 328 | if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF) |
| 327 | dev_warn(&pdev->dev, "voltage-low detected.\n"); | 329 | dev_warn(&pdev->dev, "voltage-low detected.\n"); |
| 328 | 330 | ||
| 331 | spin_lock_init(&pdata->lock); | ||
| 332 | pdata->last_jiffies = jiffies; | ||
| 333 | platform_set_drvdata(pdev, pdata); | ||
| 329 | if (pdata->irq > 0) { | 334 | if (pdata->irq > 0) { |
| 330 | writeb(0, ioaddr + RTC_INTERRUPTS); | 335 | writeb(0, ioaddr + RTC_INTERRUPTS); |
| 331 | if (request_irq(pdata->irq, ds1553_rtc_interrupt, | 336 | if (devm_request_irq(&pdev->dev, pdata->irq, |
| 337 | ds1553_rtc_interrupt, | ||
| 332 | IRQF_DISABLED, pdev->name, pdev) < 0) { | 338 | IRQF_DISABLED, pdev->name, pdev) < 0) { |
| 333 | dev_warn(&pdev->dev, "interrupt not available.\n"); | 339 | dev_warn(&pdev->dev, "interrupt not available.\n"); |
| 334 | pdata->irq = 0; | 340 | pdata->irq = 0; |
| @@ -337,27 +343,13 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev) | |||
| 337 | 343 | ||
| 338 | rtc = rtc_device_register(pdev->name, &pdev->dev, | 344 | rtc = rtc_device_register(pdev->name, &pdev->dev, |
| 339 | &ds1553_rtc_ops, THIS_MODULE); | 345 | &ds1553_rtc_ops, THIS_MODULE); |
| 340 | if (IS_ERR(rtc)) { | 346 | if (IS_ERR(rtc)) |
| 341 | ret = PTR_ERR(rtc); | 347 | return PTR_ERR(rtc); |
| 342 | goto out; | ||
| 343 | } | ||
| 344 | pdata->rtc = rtc; | 348 | pdata->rtc = rtc; |
| 345 | pdata->last_jiffies = jiffies; | 349 | |
| 346 | platform_set_drvdata(pdev, pdata); | ||
| 347 | ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); | 350 | ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); |
| 348 | if (ret) | 351 | if (ret) |
| 349 | goto out; | 352 | rtc_device_unregister(rtc); |
| 350 | return 0; | ||
| 351 | out: | ||
| 352 | if (pdata->rtc) | ||
| 353 | rtc_device_unregister(pdata->rtc); | ||
| 354 | if (pdata->irq > 0) | ||
| 355 | free_irq(pdata->irq, pdev); | ||
| 356 | if (ioaddr) | ||
| 357 | iounmap(ioaddr); | ||
| 358 | if (pdata->baseaddr) | ||
| 359 | release_mem_region(pdata->baseaddr, RTC_REG_SIZE); | ||
| 360 | kfree(pdata); | ||
| 361 | return ret; | 353 | return ret; |
| 362 | } | 354 | } |
| 363 | 355 | ||
| @@ -367,13 +359,8 @@ static int __devexit ds1553_rtc_remove(struct platform_device *pdev) | |||
| 367 | 359 | ||
| 368 | sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); | 360 | sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr); |
| 369 | rtc_device_unregister(pdata->rtc); | 361 | rtc_device_unregister(pdata->rtc); |
| 370 | if (pdata->irq > 0) { | 362 | if (pdata->irq > 0) |
| 371 | writeb(0, pdata->ioaddr + RTC_INTERRUPTS); | 363 | writeb(0, pdata->ioaddr + RTC_INTERRUPTS); |
| 372 | free_irq(pdata->irq, pdev); | ||
| 373 | } | ||
| 374 | iounmap(pdata->ioaddr); | ||
| 375 | release_mem_region(pdata->baseaddr, RTC_REG_SIZE); | ||
| 376 | kfree(pdata); | ||
| 377 | return 0; | 364 | return 0; |
| 378 | } | 365 | } |
| 379 | 366 | ||
