diff options
| author | Herbert Xu <herbert@gondor.apana.org.au> | 2009-12-01 02:16:22 -0500 |
|---|---|---|
| committer | Herbert Xu <herbert@gondor.apana.org.au> | 2009-12-01 02:16:22 -0500 |
| commit | 838632438145ac6863377eb12d8b8eef9c55d288 (patch) | |
| tree | fbb0757df837f3c75a99c518a3596c38daef162d /drivers/rtc | |
| parent | 9996508b3353063f2d6c48c1a28a84543d72d70b (diff) | |
| parent | 29e553631b2a0d4eebd23db630572e1027a9967a (diff) | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 49 | ||||
| -rw-r--r-- | drivers/rtc/Makefile | 17 | ||||
| -rw-r--r-- | drivers/rtc/interface.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 24 | ||||
| -rw-r--r-- | drivers/rtc/rtc-bfin.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-coh901331.c | 322 | ||||
| -rw-r--r-- | drivers/rtc/rtc-dev.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds1305.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds1307.c | 3 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds1390.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds3234.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ep93xx.c | 14 | ||||
| -rw-r--r-- | drivers/rtc/rtc-m41t94.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-max6902.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-mxc.c | 507 | ||||
| -rw-r--r-- | drivers/rtc/rtc-omap.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pcap.c | 224 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pcf2123.c | 364 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pcf50633.c | 7 | ||||
| -rw-r--r-- | drivers/rtc/rtc-pxa.c | 27 | ||||
| -rw-r--r-- | drivers/rtc/rtc-r9701.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-rs5c348.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-sa1100.c | 23 | ||||
| -rw-r--r-- | drivers/rtc/rtc-stmp3xxx.c | 304 | ||||
| -rw-r--r-- | drivers/rtc/rtc-sysfs.c | 14 | ||||
| -rw-r--r-- | drivers/rtc/rtc-v3020.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-vr41xx.c | 9 |
27 files changed, 1869 insertions, 54 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 73771b09fbd3..3c20dae43ce2 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
| @@ -378,6 +378,15 @@ config RTC_DRV_DS3234 | |||
| 378 | This driver can also be built as a module. If so, the module | 378 | This driver can also be built as a module. If so, the module |
| 379 | will be called rtc-ds3234. | 379 | will be called rtc-ds3234. |
| 380 | 380 | ||
| 381 | config RTC_DRV_PCF2123 | ||
| 382 | tristate "NXP PCF2123" | ||
| 383 | help | ||
| 384 | If you say yes here you get support for the NXP PCF2123 | ||
| 385 | RTC chip. | ||
| 386 | |||
| 387 | This driver can also be built as a module. If so, the module | ||
| 388 | will be called rtc-pcf2123. | ||
| 389 | |||
| 381 | endif # SPI_MASTER | 390 | endif # SPI_MASTER |
| 382 | 391 | ||
| 383 | comment "Platform RTC drivers" | 392 | comment "Platform RTC drivers" |
| @@ -500,6 +509,17 @@ config RTC_DRV_M48T59 | |||
| 500 | This driver can also be built as a module, if so, the module | 509 | This driver can also be built as a module, if so, the module |
| 501 | will be called "rtc-m48t59". | 510 | will be called "rtc-m48t59". |
| 502 | 511 | ||
| 512 | config RTC_MXC | ||
| 513 | tristate "Freescale MXC Real Time Clock" | ||
| 514 | depends on ARCH_MXC | ||
| 515 | depends on RTC_CLASS | ||
| 516 | help | ||
| 517 | If you say yes here you get support for the Freescale MXC | ||
| 518 | RTC module. | ||
| 519 | |||
| 520 | This driver can also be built as a module, if so, the module | ||
| 521 | will be called "rtc-mxc". | ||
| 522 | |||
| 503 | config RTC_DRV_BQ4802 | 523 | config RTC_DRV_BQ4802 |
| 504 | tristate "TI BQ4802" | 524 | tristate "TI BQ4802" |
| 505 | help | 525 | help |
| @@ -778,4 +798,33 @@ config RTC_DRV_PS3 | |||
| 778 | This driver can also be built as a module. If so, the module | 798 | This driver can also be built as a module. If so, the module |
| 779 | will be called rtc-ps3. | 799 | will be called rtc-ps3. |
| 780 | 800 | ||
| 801 | config RTC_DRV_COH901331 | ||
| 802 | tristate "ST-Ericsson COH 901 331 RTC" | ||
| 803 | depends on ARCH_U300 | ||
| 804 | help | ||
| 805 | If you say Y here you will get access to ST-Ericsson | ||
| 806 | COH 901 331 RTC clock found in some ST-Ericsson Mobile | ||
| 807 | Platforms. | ||
| 808 | |||
| 809 | This driver can also be built as a module. If so, the module | ||
| 810 | will be called "rtc-coh901331". | ||
| 811 | |||
| 812 | |||
| 813 | config RTC_DRV_STMP | ||
| 814 | tristate "Freescale STMP3xxx RTC" | ||
| 815 | depends on ARCH_STMP3XXX | ||
| 816 | help | ||
| 817 | If you say yes here you will get support for the onboard | ||
| 818 | STMP3xxx RTC. | ||
| 819 | |||
| 820 | This driver can also be built as a module. If so, the module | ||
| 821 | will be called rtc-stmp3xxx. | ||
| 822 | |||
| 823 | config RTC_DRV_PCAP | ||
| 824 | tristate "PCAP RTC" | ||
| 825 | depends on EZX_PCAP | ||
| 826 | help | ||
| 827 | If you say Y here you will get support for the RTC found on | ||
| 828 | the PCAP2 ASIC used on some Motorola phones. | ||
| 829 | |||
| 781 | endif # RTC_CLASS | 830 | endif # RTC_CLASS |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5e152ffe5058..aa3fbd5517a1 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
| @@ -23,7 +23,9 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o | |||
| 23 | obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o | 23 | obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o |
| 24 | obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o | 24 | obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o |
| 25 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o | 25 | obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o |
| 26 | obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o | ||
| 26 | obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o | 27 | obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o |
| 28 | obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o | ||
| 27 | obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o | 29 | obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o |
| 28 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o | 30 | obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o |
| 29 | obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o | 31 | obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o |
| @@ -40,24 +42,26 @@ obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o | |||
| 40 | obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o | 42 | obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o |
| 41 | obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o | 43 | obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o |
| 42 | obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o | 44 | obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o |
| 45 | obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o | ||
| 43 | obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o | 46 | obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o |
| 44 | obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o | 47 | obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o |
| 45 | obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o | 48 | obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o |
| 46 | obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o | 49 | obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o |
| 47 | obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o | 50 | obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o |
| 48 | obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o | 51 | obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o |
| 49 | obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o | 52 | obj-$(CONFIG_RTC_MXC) += rtc-mxc.o |
| 50 | obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o | ||
| 51 | obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o | ||
| 52 | obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o | 53 | obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o |
| 53 | obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o | 54 | obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o |
| 54 | obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o | 55 | obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o |
| 55 | obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o | 56 | obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o |
| 57 | obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o | ||
| 56 | obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o | 58 | obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o |
| 57 | obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o | 59 | obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o |
| 60 | obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o | ||
| 61 | obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o | ||
| 58 | obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o | 62 | obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o |
| 59 | obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o | 63 | obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o |
| 60 | obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o | 64 | obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o |
| 61 | obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o | 65 | obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o |
| 62 | obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o | 66 | obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o |
| 63 | obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o | 67 | obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o |
| @@ -69,7 +73,10 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o | |||
| 69 | obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o | 73 | obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o |
| 70 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o | 74 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o |
| 71 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o | 75 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o |
| 76 | obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o | ||
| 72 | obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o | 77 | obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o |
| 78 | obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o | ||
| 79 | obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o | ||
| 73 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o | 80 | obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o |
| 74 | obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o | 81 | obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o |
| 75 | obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o | 82 | obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o |
| @@ -78,5 +85,3 @@ obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o | |||
| 78 | obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o | 85 | obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o |
| 79 | obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o | 86 | obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o |
| 80 | obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o | 87 | obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o |
| 81 | obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o | ||
| 82 | obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o | ||
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 4cdb31a362ca..a0c816238aa9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | */ | 12 | */ |
| 13 | 13 | ||
| 14 | #include <linux/rtc.h> | 14 | #include <linux/rtc.h> |
| 15 | #include <linux/sched.h> | ||
| 15 | #include <linux/log2.h> | 16 | #include <linux/log2.h> |
| 16 | 17 | ||
| 17 | int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) | 18 | int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm) |
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index b5bf93706913..bc8bbca9a2e2 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c | |||
| @@ -289,7 +289,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) | |||
| 289 | AT91_RTC_CALEV); | 289 | AT91_RTC_CALEV); |
| 290 | 290 | ||
| 291 | ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, | 291 | ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt, |
| 292 | IRQF_DISABLED | IRQF_SHARED, | 292 | IRQF_SHARED, |
| 293 | "at91_rtc", pdev); | 293 | "at91_rtc", pdev); |
| 294 | if (ret) { | 294 | if (ret) { |
| 295 | printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", | 295 | printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n", |
| @@ -340,7 +340,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev) | |||
| 340 | 340 | ||
| 341 | static u32 at91_rtc_imr; | 341 | static u32 at91_rtc_imr; |
| 342 | 342 | ||
| 343 | static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 343 | static int at91_rtc_suspend(struct device *dev) |
| 344 | { | 344 | { |
| 345 | /* this IRQ is shared with DBGU and other hardware which isn't | 345 | /* this IRQ is shared with DBGU and other hardware which isn't |
| 346 | * necessarily doing PM like we are... | 346 | * necessarily doing PM like we are... |
| @@ -348,7 +348,7 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) | |||
| 348 | at91_rtc_imr = at91_sys_read(AT91_RTC_IMR) | 348 | at91_rtc_imr = at91_sys_read(AT91_RTC_IMR) |
| 349 | & (AT91_RTC_ALARM|AT91_RTC_SECEV); | 349 | & (AT91_RTC_ALARM|AT91_RTC_SECEV); |
| 350 | if (at91_rtc_imr) { | 350 | if (at91_rtc_imr) { |
| 351 | if (device_may_wakeup(&pdev->dev)) | 351 | if (device_may_wakeup(dev)) |
| 352 | enable_irq_wake(AT91_ID_SYS); | 352 | enable_irq_wake(AT91_ID_SYS); |
| 353 | else | 353 | else |
| 354 | at91_sys_write(AT91_RTC_IDR, at91_rtc_imr); | 354 | at91_sys_write(AT91_RTC_IDR, at91_rtc_imr); |
| @@ -356,28 +356,34 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state) | |||
| 356 | return 0; | 356 | return 0; |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | static int at91_rtc_resume(struct platform_device *pdev) | 359 | static int at91_rtc_resume(struct device *dev) |
| 360 | { | 360 | { |
| 361 | if (at91_rtc_imr) { | 361 | if (at91_rtc_imr) { |
| 362 | if (device_may_wakeup(&pdev->dev)) | 362 | if (device_may_wakeup(dev)) |
| 363 | disable_irq_wake(AT91_ID_SYS); | 363 | disable_irq_wake(AT91_ID_SYS); |
| 364 | else | 364 | else |
| 365 | at91_sys_write(AT91_RTC_IER, at91_rtc_imr); | 365 | at91_sys_write(AT91_RTC_IER, at91_rtc_imr); |
| 366 | } | 366 | } |
| 367 | return 0; | 367 | return 0; |
| 368 | } | 368 | } |
| 369 | |||
| 370 | static const struct dev_pm_ops at91_rtc_pm = { | ||
| 371 | .suspend = at91_rtc_suspend, | ||
| 372 | .resume = at91_rtc_resume, | ||
| 373 | }; | ||
| 374 | |||
| 375 | #define at91_rtc_pm_ptr &at91_rtc_pm | ||
| 376 | |||
| 369 | #else | 377 | #else |
| 370 | #define at91_rtc_suspend NULL | 378 | #define at91_rtc_pm_ptr NULL |
| 371 | #define at91_rtc_resume NULL | ||
| 372 | #endif | 379 | #endif |
| 373 | 380 | ||
| 374 | static struct platform_driver at91_rtc_driver = { | 381 | static struct platform_driver at91_rtc_driver = { |
| 375 | .remove = __exit_p(at91_rtc_remove), | 382 | .remove = __exit_p(at91_rtc_remove), |
| 376 | .suspend = at91_rtc_suspend, | ||
| 377 | .resume = at91_rtc_resume, | ||
| 378 | .driver = { | 383 | .driver = { |
| 379 | .name = "at91_rtc", | 384 | .name = "at91_rtc", |
| 380 | .owner = THIS_MODULE, | 385 | .owner = THIS_MODULE, |
| 386 | .pm = at91_rtc_pm_ptr, | ||
| 381 | }, | 387 | }, |
| 382 | }; | 388 | }; |
| 383 | 389 | ||
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c index a118eb0f1e67..b11485b9f21c 100644 --- a/drivers/rtc/rtc-bfin.c +++ b/drivers/rtc/rtc-bfin.c | |||
| @@ -383,7 +383,7 @@ static int __devinit bfin_rtc_probe(struct platform_device *pdev) | |||
| 383 | } | 383 | } |
| 384 | 384 | ||
| 385 | /* Grab the IRQ and init the hardware */ | 385 | /* Grab the IRQ and init the hardware */ |
| 386 | ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, IRQF_SHARED, pdev->name, dev); | 386 | ret = request_irq(IRQ_RTC, bfin_rtc_interrupt, 0, pdev->name, dev); |
| 387 | if (unlikely(ret)) | 387 | if (unlikely(ret)) |
| 388 | goto err_reg; | 388 | goto err_reg; |
| 389 | /* sometimes the bootloader touched things, but the write complete was not | 389 | /* sometimes the bootloader touched things, but the write complete was not |
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c new file mode 100644 index 000000000000..03ea530981d1 --- /dev/null +++ b/drivers/rtc/rtc-coh901331.c | |||
| @@ -0,0 +1,322 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2007-2009 ST-Ericsson AB | ||
| 3 | * License terms: GNU General Public License (GPL) version 2 | ||
| 4 | * Real Time Clock interface for ST-Ericsson AB COH 901 331 RTC. | ||
| 5 | * Author: Linus Walleij <linus.walleij@stericsson.com> | ||
| 6 | * Based on rtc-pl031.c by Deepak Saxena <dsaxena@plexity.net> | ||
| 7 | * Copyright 2006 (c) MontaVista Software, Inc. | ||
| 8 | */ | ||
| 9 | #include <linux/init.h> | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/rtc.h> | ||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/pm.h> | ||
| 15 | #include <linux/platform_device.h> | ||
| 16 | #include <linux/io.h> | ||
| 17 | |||
| 18 | /* | ||
| 19 | * Registers in the COH 901 331 | ||
| 20 | */ | ||
| 21 | /* Alarm value 32bit (R/W) */ | ||
| 22 | #define COH901331_ALARM 0x00U | ||
| 23 | /* Used to set current time 32bit (R/W) */ | ||
| 24 | #define COH901331_SET_TIME 0x04U | ||
| 25 | /* Indication if current time is valid 32bit (R/-) */ | ||
| 26 | #define COH901331_VALID 0x08U | ||
| 27 | /* Read the current time 32bit (R/-) */ | ||
| 28 | #define COH901331_CUR_TIME 0x0cU | ||
| 29 | /* Event register for the "alarm" interrupt */ | ||
| 30 | #define COH901331_IRQ_EVENT 0x10U | ||
| 31 | /* Mask register for the "alarm" interrupt */ | ||
| 32 | #define COH901331_IRQ_MASK 0x14U | ||
| 33 | /* Force register for the "alarm" interrupt */ | ||
| 34 | #define COH901331_IRQ_FORCE 0x18U | ||
| 35 | |||
| 36 | /* | ||
| 37 | * Reference to RTC block clock | ||
| 38 | * Notice that the frequent clk_enable()/clk_disable() on this | ||
| 39 | * clock is mainly to be able to turn on/off other clocks in the | ||
| 40 | * hierarchy as needed, the RTC clock is always on anyway. | ||
| 41 | */ | ||
| 42 | struct coh901331_port { | ||
| 43 | struct rtc_device *rtc; | ||
| 44 | struct clk *clk; | ||
| 45 | u32 phybase; | ||
| 46 | u32 physize; | ||
| 47 | void __iomem *virtbase; | ||
| 48 | int irq; | ||
| 49 | #ifdef CONFIG_PM | ||
| 50 | u32 irqmaskstore; | ||
| 51 | #endif | ||
| 52 | }; | ||
| 53 | |||
| 54 | static irqreturn_t coh901331_interrupt(int irq, void *data) | ||
| 55 | { | ||
| 56 | struct coh901331_port *rtap = data; | ||
| 57 | |||
| 58 | clk_enable(rtap->clk); | ||
| 59 | /* Ack IRQ */ | ||
| 60 | writel(1, rtap->virtbase + COH901331_IRQ_EVENT); | ||
| 61 | /* | ||
| 62 | * Disable the interrupt. This is necessary because | ||
| 63 | * the RTC lives on a lower-clocked line and will | ||
| 64 | * not release the IRQ line until after a few (slower) | ||
| 65 | * clock cycles. The interrupt will be re-enabled when | ||
| 66 | * a new alarm is set anyway. | ||
| 67 | */ | ||
| 68 | writel(0, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 69 | clk_disable(rtap->clk); | ||
| 70 | |||
| 71 | /* Set alarm flag */ | ||
| 72 | rtc_update_irq(rtap->rtc, 1, RTC_AF); | ||
| 73 | |||
| 74 | return IRQ_HANDLED; | ||
| 75 | } | ||
| 76 | |||
| 77 | static int coh901331_read_time(struct device *dev, struct rtc_time *tm) | ||
| 78 | { | ||
| 79 | struct coh901331_port *rtap = dev_get_drvdata(dev); | ||
| 80 | |||
| 81 | clk_enable(rtap->clk); | ||
| 82 | /* Check if the time is valid */ | ||
| 83 | if (readl(rtap->virtbase + COH901331_VALID)) { | ||
| 84 | rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm); | ||
| 85 | clk_disable(rtap->clk); | ||
| 86 | return rtc_valid_tm(tm); | ||
| 87 | } | ||
| 88 | clk_disable(rtap->clk); | ||
| 89 | return -EINVAL; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int coh901331_set_mmss(struct device *dev, unsigned long secs) | ||
| 93 | { | ||
| 94 | struct coh901331_port *rtap = dev_get_drvdata(dev); | ||
| 95 | |||
| 96 | clk_enable(rtap->clk); | ||
| 97 | writel(secs, rtap->virtbase + COH901331_SET_TIME); | ||
| 98 | clk_disable(rtap->clk); | ||
| 99 | |||
| 100 | return 0; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int coh901331_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
| 104 | { | ||
| 105 | struct coh901331_port *rtap = dev_get_drvdata(dev); | ||
| 106 | |||
| 107 | clk_enable(rtap->clk); | ||
| 108 | rtc_time_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time); | ||
| 109 | alarm->pending = readl(rtap->virtbase + COH901331_IRQ_EVENT) & 1U; | ||
| 110 | alarm->enabled = readl(rtap->virtbase + COH901331_IRQ_MASK) & 1U; | ||
| 111 | clk_disable(rtap->clk); | ||
| 112 | |||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | static int coh901331_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) | ||
| 117 | { | ||
| 118 | struct coh901331_port *rtap = dev_get_drvdata(dev); | ||
| 119 | unsigned long time; | ||
| 120 | |||
| 121 | rtc_tm_to_time(&alarm->time, &time); | ||
| 122 | clk_enable(rtap->clk); | ||
| 123 | writel(time, rtap->virtbase + COH901331_ALARM); | ||
| 124 | writel(alarm->enabled, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 125 | clk_disable(rtap->clk); | ||
| 126 | |||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
| 131 | { | ||
| 132 | struct coh901331_port *rtap = dev_get_drvdata(dev); | ||
| 133 | |||
| 134 | clk_enable(rtap->clk); | ||
| 135 | if (enabled) | ||
| 136 | writel(1, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 137 | else | ||
| 138 | writel(0, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 139 | clk_disable(rtap->clk); | ||
| 140 | |||
| 141 | return 0; | ||
| 142 | } | ||
| 143 | |||
| 144 | static struct rtc_class_ops coh901331_ops = { | ||
| 145 | .read_time = coh901331_read_time, | ||
| 146 | .set_mmss = coh901331_set_mmss, | ||
| 147 | .read_alarm = coh901331_read_alarm, | ||
| 148 | .set_alarm = coh901331_set_alarm, | ||
| 149 | .alarm_irq_enable = coh901331_alarm_irq_enable, | ||
| 150 | }; | ||
| 151 | |||
| 152 | static int __exit coh901331_remove(struct platform_device *pdev) | ||
| 153 | { | ||
| 154 | struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); | ||
| 155 | |||
| 156 | if (rtap) { | ||
| 157 | free_irq(rtap->irq, rtap); | ||
| 158 | rtc_device_unregister(rtap->rtc); | ||
| 159 | clk_put(rtap->clk); | ||
| 160 | iounmap(rtap->virtbase); | ||
| 161 | release_mem_region(rtap->phybase, rtap->physize); | ||
| 162 | platform_set_drvdata(pdev, NULL); | ||
| 163 | kfree(rtap); | ||
| 164 | } | ||
| 165 | |||
| 166 | return 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | |||
| 170 | static int __init coh901331_probe(struct platform_device *pdev) | ||
| 171 | { | ||
| 172 | int ret; | ||
| 173 | struct coh901331_port *rtap; | ||
| 174 | struct resource *res; | ||
| 175 | |||
| 176 | rtap = kzalloc(sizeof(struct coh901331_port), GFP_KERNEL); | ||
| 177 | if (!rtap) | ||
| 178 | return -ENOMEM; | ||
| 179 | |||
| 180 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 181 | if (!res) { | ||
| 182 | ret = -ENOENT; | ||
| 183 | goto out_no_resource; | ||
| 184 | } | ||
| 185 | rtap->phybase = res->start; | ||
| 186 | rtap->physize = resource_size(res); | ||
| 187 | |||
| 188 | if (request_mem_region(rtap->phybase, rtap->physize, | ||
| 189 | "rtc-coh901331") == NULL) { | ||
| 190 | ret = -EBUSY; | ||
| 191 | goto out_no_memregion; | ||
| 192 | } | ||
| 193 | |||
| 194 | rtap->virtbase = ioremap(rtap->phybase, rtap->physize); | ||
| 195 | if (!rtap->virtbase) { | ||
| 196 | ret = -ENOMEM; | ||
| 197 | goto out_no_remap; | ||
| 198 | } | ||
| 199 | |||
| 200 | rtap->irq = platform_get_irq(pdev, 0); | ||
| 201 | if (request_irq(rtap->irq, coh901331_interrupt, IRQF_DISABLED, | ||
| 202 | "RTC COH 901 331 Alarm", rtap)) { | ||
| 203 | ret = -EIO; | ||
| 204 | goto out_no_irq; | ||
| 205 | } | ||
| 206 | |||
| 207 | rtap->clk = clk_get(&pdev->dev, NULL); | ||
| 208 | if (IS_ERR(rtap->clk)) { | ||
| 209 | ret = PTR_ERR(rtap->clk); | ||
| 210 | dev_err(&pdev->dev, "could not get clock\n"); | ||
| 211 | goto out_no_clk; | ||
| 212 | } | ||
| 213 | |||
| 214 | /* We enable/disable the clock only to assure it works */ | ||
| 215 | ret = clk_enable(rtap->clk); | ||
| 216 | if (ret) { | ||
| 217 | dev_err(&pdev->dev, "could not enable clock\n"); | ||
| 218 | goto out_no_clk_enable; | ||
| 219 | } | ||
| 220 | clk_disable(rtap->clk); | ||
| 221 | |||
| 222 | rtap->rtc = rtc_device_register("coh901331", &pdev->dev, &coh901331_ops, | ||
| 223 | THIS_MODULE); | ||
| 224 | if (IS_ERR(rtap->rtc)) { | ||
| 225 | ret = PTR_ERR(rtap->rtc); | ||
| 226 | goto out_no_rtc; | ||
| 227 | } | ||
| 228 | |||
| 229 | platform_set_drvdata(pdev, rtap); | ||
| 230 | |||
| 231 | return 0; | ||
| 232 | |||
| 233 | out_no_rtc: | ||
| 234 | out_no_clk_enable: | ||
| 235 | clk_put(rtap->clk); | ||
| 236 | out_no_clk: | ||
| 237 | free_irq(rtap->irq, rtap); | ||
| 238 | out_no_irq: | ||
| 239 | iounmap(rtap->virtbase); | ||
| 240 | out_no_remap: | ||
| 241 | platform_set_drvdata(pdev, NULL); | ||
| 242 | out_no_memregion: | ||
| 243 | release_mem_region(rtap->phybase, SZ_4K); | ||
| 244 | out_no_resource: | ||
| 245 | kfree(rtap); | ||
| 246 | return ret; | ||
| 247 | } | ||
| 248 | |||
| 249 | #ifdef CONFIG_PM | ||
| 250 | static int coh901331_suspend(struct platform_device *pdev, pm_message_t state) | ||
| 251 | { | ||
| 252 | struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); | ||
| 253 | |||
| 254 | /* | ||
| 255 | * If this RTC alarm will be used for waking the system up, | ||
| 256 | * don't disable it of course. Else we just disable the alarm | ||
| 257 | * and await suspension. | ||
| 258 | */ | ||
| 259 | if (device_may_wakeup(&pdev->dev)) { | ||
| 260 | enable_irq_wake(rtap->irq); | ||
| 261 | } else { | ||
| 262 | clk_enable(rtap->clk); | ||
| 263 | rtap->irqmaskstore = readl(rtap->virtbase + COH901331_IRQ_MASK); | ||
| 264 | writel(0, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 265 | clk_disable(rtap->clk); | ||
| 266 | } | ||
| 267 | return 0; | ||
| 268 | } | ||
| 269 | |||
| 270 | static int coh901331_resume(struct platform_device *pdev) | ||
| 271 | { | ||
| 272 | struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); | ||
| 273 | |||
| 274 | if (device_may_wakeup(&pdev->dev)) | ||
| 275 | disable_irq_wake(rtap->irq); | ||
| 276 | else | ||
| 277 | clk_enable(rtap->clk); | ||
| 278 | writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 279 | clk_disable(rtap->clk); | ||
| 280 | return 0; | ||
| 281 | } | ||
| 282 | #else | ||
| 283 | #define coh901331_suspend NULL | ||
| 284 | #define coh901331_resume NULL | ||
| 285 | #endif | ||
| 286 | |||
| 287 | static void coh901331_shutdown(struct platform_device *pdev) | ||
| 288 | { | ||
| 289 | struct coh901331_port *rtap = dev_get_drvdata(&pdev->dev); | ||
| 290 | |||
| 291 | clk_enable(rtap->clk); | ||
| 292 | writel(0, rtap->virtbase + COH901331_IRQ_MASK); | ||
| 293 | clk_disable(rtap->clk); | ||
| 294 | } | ||
| 295 | |||
| 296 | static struct platform_driver coh901331_driver = { | ||
| 297 | .driver = { | ||
| 298 | .name = "rtc-coh901331", | ||
| 299 | .owner = THIS_MODULE, | ||
| 300 | }, | ||
| 301 | .remove = __exit_p(coh901331_remove), | ||
| 302 | .suspend = coh901331_suspend, | ||
| 303 | .resume = coh901331_resume, | ||
| 304 | .shutdown = coh901331_shutdown, | ||
| 305 | }; | ||
| 306 | |||
| 307 | static int __init coh901331_init(void) | ||
| 308 | { | ||
| 309 | return platform_driver_probe(&coh901331_driver, coh901331_probe); | ||
| 310 | } | ||
| 311 | |||
| 312 | static void __exit coh901331_exit(void) | ||
| 313 | { | ||
| 314 | platform_driver_unregister(&coh901331_driver); | ||
| 315 | } | ||
| 316 | |||
| 317 | module_init(coh901331_init); | ||
| 318 | module_exit(coh901331_exit); | ||
| 319 | |||
| 320 | MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); | ||
| 321 | MODULE_DESCRIPTION("ST-Ericsson AB COH 901 331 RTC Driver"); | ||
| 322 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index 8a11de9552cd..62227cd52410 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | #include <linux/module.h> | 14 | #include <linux/module.h> |
| 15 | #include <linux/rtc.h> | 15 | #include <linux/rtc.h> |
| 16 | #include <linux/sched.h> | ||
| 16 | #include "rtc-core.h" | 17 | #include "rtc-core.h" |
| 17 | 18 | ||
| 18 | static dev_t rtc_devt; | 19 | static dev_t rtc_devt; |
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c index 8f410e59d9f5..2736b11a1b1e 100644 --- a/drivers/rtc/rtc-ds1305.c +++ b/drivers/rtc/rtc-ds1305.c | |||
| @@ -841,3 +841,4 @@ module_exit(ds1305_exit); | |||
| 841 | 841 | ||
| 842 | MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); | 842 | MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips"); |
| 843 | MODULE_LICENSE("GPL"); | 843 | MODULE_LICENSE("GPL"); |
| 844 | MODULE_ALIAS("spi:rtc-ds1305"); | ||
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 47a93c022d91..eb99ee4fa0f5 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c | |||
| @@ -896,8 +896,7 @@ read_rtc: | |||
| 896 | return 0; | 896 | return 0; |
| 897 | 897 | ||
| 898 | exit_irq: | 898 | exit_irq: |
| 899 | if (ds1307->rtc) | 899 | rtc_device_unregister(ds1307->rtc); |
| 900 | rtc_device_unregister(ds1307->rtc); | ||
| 901 | exit_free: | 900 | exit_free: |
| 902 | kfree(ds1307); | 901 | kfree(ds1307); |
| 903 | return err; | 902 | return err; |
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index e01b955db077..cdb705057091 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c | |||
| @@ -189,3 +189,4 @@ module_exit(ds1390_exit); | |||
| 189 | MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver"); | 189 | MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver"); |
| 190 | MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); | 190 | MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); |
| 191 | MODULE_LICENSE("GPL"); | 191 | MODULE_LICENSE("GPL"); |
| 192 | MODULE_ALIAS("spi:rtc-ds1390"); | ||
diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c index c51589ede5b7..a774ca35b5f7 100644 --- a/drivers/rtc/rtc-ds3234.c +++ b/drivers/rtc/rtc-ds3234.c | |||
| @@ -188,3 +188,4 @@ module_exit(ds3234_exit); | |||
| 188 | MODULE_DESCRIPTION("DS3234 SPI RTC driver"); | 188 | MODULE_DESCRIPTION("DS3234 SPI RTC driver"); |
| 189 | MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); | 189 | MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); |
| 190 | MODULE_LICENSE("GPL"); | 190 | MODULE_LICENSE("GPL"); |
| 191 | MODULE_ALIAS("spi:ds3234"); | ||
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c index 551332e4ed02..9da02d108b73 100644 --- a/drivers/rtc/rtc-ep93xx.c +++ b/drivers/rtc/rtc-ep93xx.c | |||
| @@ -128,12 +128,16 @@ static int __init ep93xx_rtc_probe(struct platform_device *pdev) | |||
| 128 | return -ENOMEM; | 128 | return -ENOMEM; |
| 129 | 129 | ||
| 130 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 130 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 131 | if (res == NULL) | 131 | if (res == NULL) { |
| 132 | return -ENXIO; | 132 | err = -ENXIO; |
| 133 | goto fail_free; | ||
| 134 | } | ||
| 133 | 135 | ||
| 134 | res = request_mem_region(res->start, resource_size(res), pdev->name); | 136 | res = request_mem_region(res->start, resource_size(res), pdev->name); |
| 135 | if (res == NULL) | 137 | if (res == NULL) { |
| 136 | return -EBUSY; | 138 | err = -EBUSY; |
| 139 | goto fail_free; | ||
| 140 | } | ||
| 137 | 141 | ||
| 138 | ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res)); | 142 | ep93xx_rtc->mmio_base = ioremap(res->start, resource_size(res)); |
| 139 | if (ep93xx_rtc->mmio_base == NULL) { | 143 | if (ep93xx_rtc->mmio_base == NULL) { |
| @@ -169,6 +173,8 @@ fail: | |||
| 169 | pdev->dev.platform_data = NULL; | 173 | pdev->dev.platform_data = NULL; |
| 170 | } | 174 | } |
| 171 | release_mem_region(res->start, resource_size(res)); | 175 | release_mem_region(res->start, resource_size(res)); |
| 176 | fail_free: | ||
| 177 | kfree(ep93xx_rtc); | ||
| 172 | return err; | 178 | return err; |
| 173 | } | 179 | } |
| 174 | 180 | ||
diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c index c3a18c58daf6..c8c97a4169d4 100644 --- a/drivers/rtc/rtc-m41t94.c +++ b/drivers/rtc/rtc-m41t94.c | |||
| @@ -171,3 +171,4 @@ module_exit(m41t94_exit); | |||
| 171 | MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); | 171 | MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>"); |
| 172 | MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); | 172 | MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC"); |
| 173 | MODULE_LICENSE("GPL"); | 173 | MODULE_LICENSE("GPL"); |
| 174 | MODULE_ALIAS("spi:rtc-m41t94"); | ||
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c index 36a8ea9ed8ba..657403ebd54a 100644 --- a/drivers/rtc/rtc-max6902.c +++ b/drivers/rtc/rtc-max6902.c | |||
| @@ -175,3 +175,4 @@ module_exit(max6902_exit); | |||
| 175 | MODULE_DESCRIPTION ("max6902 spi RTC driver"); | 175 | MODULE_DESCRIPTION ("max6902 spi RTC driver"); |
| 176 | MODULE_AUTHOR ("Raphael Assenat"); | 176 | MODULE_AUTHOR ("Raphael Assenat"); |
| 177 | MODULE_LICENSE ("GPL"); | 177 | MODULE_LICENSE ("GPL"); |
| 178 | MODULE_ALIAS("spi:rtc-max6902"); | ||
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c new file mode 100644 index 000000000000..6bd5072d4eb7 --- /dev/null +++ b/drivers/rtc/rtc-mxc.c | |||
| @@ -0,0 +1,507 @@ | |||
| 1 | /* | ||
| 2 | * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. | ||
| 3 | * | ||
| 4 | * The code contained herein is licensed under the GNU General Public | ||
| 5 | * License. You may obtain a copy of the GNU General Public License | ||
| 6 | * Version 2 or later at the following locations: | ||
| 7 | * | ||
| 8 | * http://www.opensource.org/licenses/gpl-license.html | ||
| 9 | * http://www.gnu.org/copyleft/gpl.html | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/io.h> | ||
| 13 | #include <linux/rtc.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/interrupt.h> | ||
| 16 | #include <linux/platform_device.h> | ||
| 17 | #include <linux/clk.h> | ||
| 18 | |||
| 19 | #include <mach/hardware.h> | ||
| 20 | |||
| 21 | #define RTC_INPUT_CLK_32768HZ (0x00 << 5) | ||
| 22 | #define RTC_INPUT_CLK_32000HZ (0x01 << 5) | ||
| 23 | #define RTC_INPUT_CLK_38400HZ (0x02 << 5) | ||
| 24 | |||
| 25 | #define RTC_SW_BIT (1 << 0) | ||
| 26 | #define RTC_ALM_BIT (1 << 2) | ||
| 27 | #define RTC_1HZ_BIT (1 << 4) | ||
| 28 | #define RTC_2HZ_BIT (1 << 7) | ||
| 29 | #define RTC_SAM0_BIT (1 << 8) | ||
| 30 | #define RTC_SAM1_BIT (1 << 9) | ||
| 31 | #define RTC_SAM2_BIT (1 << 10) | ||
| 32 | #define RTC_SAM3_BIT (1 << 11) | ||
| 33 | #define RTC_SAM4_BIT (1 << 12) | ||
| 34 | #define RTC_SAM5_BIT (1 << 13) | ||
| 35 | #define RTC_SAM6_BIT (1 << 14) | ||
| 36 | #define RTC_SAM7_BIT (1 << 15) | ||
| 37 | #define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \ | ||
| 38 | RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \ | ||
| 39 | RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT) | ||
| 40 | |||
| 41 | #define RTC_ENABLE_BIT (1 << 7) | ||
| 42 | |||
| 43 | #define MAX_PIE_NUM 9 | ||
| 44 | #define MAX_PIE_FREQ 512 | ||
| 45 | static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = { | ||
| 46 | { 2, RTC_2HZ_BIT }, | ||
| 47 | { 4, RTC_SAM0_BIT }, | ||
| 48 | { 8, RTC_SAM1_BIT }, | ||
| 49 | { 16, RTC_SAM2_BIT }, | ||
| 50 | { 32, RTC_SAM3_BIT }, | ||
| 51 | { 64, RTC_SAM4_BIT }, | ||
| 52 | { 128, RTC_SAM5_BIT }, | ||
| 53 | { 256, RTC_SAM6_BIT }, | ||
| 54 | { MAX_PIE_FREQ, RTC_SAM7_BIT }, | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* Those are the bits from a classic RTC we want to mimic */ | ||
| 58 | #define RTC_IRQF 0x80 /* any of the following 3 is active */ | ||
| 59 | #define RTC_PF 0x40 /* Periodic interrupt */ | ||
| 60 | #define RTC_AF 0x20 /* Alarm interrupt */ | ||
| 61 | #define RTC_UF 0x10 /* Update interrupt for 1Hz RTC */ | ||
| 62 | |||
| 63 | #define MXC_RTC_TIME 0 | ||
| 64 | #define MXC_RTC_ALARM 1 | ||
| 65 | |||
| 66 | #define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */ | ||
| 67 | #define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */ | ||
| 68 | #define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */ | ||
| 69 | #define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */ | ||
| 70 | #define RTC_RTCCTL 0x10 /* 32bit rtc control reg */ | ||
| 71 | #define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */ | ||
| 72 | #define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */ | ||
| 73 | #define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */ | ||
| 74 | #define RTC_DAYR 0x20 /* 32bit rtc days counter reg */ | ||
| 75 | #define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */ | ||
| 76 | #define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */ | ||
| 77 | #define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */ | ||
| 78 | #define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */ | ||
| 79 | |||
| 80 | struct rtc_plat_data { | ||
| 81 | struct rtc_device *rtc; | ||
| 82 | void __iomem *ioaddr; | ||
| 83 | int irq; | ||
| 84 | struct clk *clk; | ||
| 85 | unsigned int irqen; | ||
| 86 | int alrm_sec; | ||
| 87 | int alrm_min; | ||
| 88 | int alrm_hour; | ||
| 89 | int alrm_mday; | ||
| 90 | struct timespec mxc_rtc_delta; | ||
| 91 | struct rtc_time g_rtc_alarm; | ||
| 92 | }; | ||
| 93 | |||
| 94 | /* | ||
| 95 | * This function is used to obtain the RTC time or the alarm value in | ||
| 96 | * second. | ||
| 97 | */ | ||
| 98 | static u32 get_alarm_or_time(struct device *dev, int time_alarm) | ||
| 99 | { | ||
| 100 | struct platform_device *pdev = to_platform_device(dev); | ||
| 101 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 102 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 103 | u32 day = 0, hr = 0, min = 0, sec = 0, hr_min = 0; | ||
| 104 | |||
| 105 | switch (time_alarm) { | ||
| 106 | case MXC_RTC_TIME: | ||
| 107 | day = readw(ioaddr + RTC_DAYR); | ||
| 108 | hr_min = readw(ioaddr + RTC_HOURMIN); | ||
| 109 | sec = readw(ioaddr + RTC_SECOND); | ||
| 110 | break; | ||
| 111 | case MXC_RTC_ALARM: | ||
| 112 | day = readw(ioaddr + RTC_DAYALARM); | ||
| 113 | hr_min = readw(ioaddr + RTC_ALRM_HM) & 0xffff; | ||
| 114 | sec = readw(ioaddr + RTC_ALRM_SEC); | ||
| 115 | break; | ||
| 116 | } | ||
| 117 | |||
| 118 | hr = hr_min >> 8; | ||
| 119 | min = hr_min & 0xff; | ||
| 120 | |||
| 121 | return (((day * 24 + hr) * 60) + min) * 60 + sec; | ||
| 122 | } | ||
| 123 | |||
| 124 | /* | ||
| 125 | * This function sets the RTC alarm value or the time value. | ||
| 126 | */ | ||
| 127 | static void set_alarm_or_time(struct device *dev, int time_alarm, u32 time) | ||
| 128 | { | ||
| 129 | u32 day, hr, min, sec, temp; | ||
| 130 | struct platform_device *pdev = to_platform_device(dev); | ||
| 131 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 132 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 133 | |||
| 134 | day = time / 86400; | ||
| 135 | time -= day * 86400; | ||
| 136 | |||
| 137 | /* time is within a day now */ | ||
| 138 | hr = time / 3600; | ||
| 139 | time -= hr * 3600; | ||
| 140 | |||
| 141 | /* time is within an hour now */ | ||
| 142 | min = time / 60; | ||
| 143 | sec = time - min * 60; | ||
| 144 | |||
| 145 | temp = (hr << 8) + min; | ||
| 146 | |||
| 147 | switch (time_alarm) { | ||
| 148 | case MXC_RTC_TIME: | ||
| 149 | writew(day, ioaddr + RTC_DAYR); | ||
| 150 | writew(sec, ioaddr + RTC_SECOND); | ||
| 151 | writew(temp, ioaddr + RTC_HOURMIN); | ||
| 152 | break; | ||
| 153 | case MXC_RTC_ALARM: | ||
| 154 | writew(day, ioaddr + RTC_DAYALARM); | ||
| 155 | writew(sec, ioaddr + RTC_ALRM_SEC); | ||
| 156 | writew(temp, ioaddr + RTC_ALRM_HM); | ||
| 157 | break; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | /* | ||
| 162 | * This function updates the RTC alarm registers and then clears all the | ||
| 163 | * interrupt status bits. | ||
| 164 | */ | ||
| 165 | static int rtc_update_alarm(struct device *dev, struct rtc_time *alrm) | ||
| 166 | { | ||
| 167 | struct rtc_time alarm_tm, now_tm; | ||
| 168 | unsigned long now, time; | ||
| 169 | int ret; | ||
| 170 | struct platform_device *pdev = to_platform_device(dev); | ||
| 171 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 172 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 173 | |||
| 174 | now = get_alarm_or_time(dev, MXC_RTC_TIME); | ||
| 175 | rtc_time_to_tm(now, &now_tm); | ||
| 176 | alarm_tm.tm_year = now_tm.tm_year; | ||
| 177 | alarm_tm.tm_mon = now_tm.tm_mon; | ||
| 178 | alarm_tm.tm_mday = now_tm.tm_mday; | ||
| 179 | alarm_tm.tm_hour = alrm->tm_hour; | ||
| 180 | alarm_tm.tm_min = alrm->tm_min; | ||
| 181 | alarm_tm.tm_sec = alrm->tm_sec; | ||
| 182 | rtc_tm_to_time(&now_tm, &now); | ||
| 183 | rtc_tm_to_time(&alarm_tm, &time); | ||
| 184 | |||
| 185 | if (time < now) { | ||
| 186 | time += 60 * 60 * 24; | ||
| 187 | rtc_time_to_tm(time, &alarm_tm); | ||
| 188 | } | ||
| 189 | |||
| 190 | ret = rtc_tm_to_time(&alarm_tm, &time); | ||
| 191 | |||
| 192 | /* clear all the interrupt status bits */ | ||
| 193 | writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR); | ||
| 194 | set_alarm_or_time(dev, MXC_RTC_ALARM, time); | ||
| 195 | |||
| 196 | return ret; | ||
| 197 | } | ||
| 198 | |||
| 199 | /* This function is the RTC interrupt service routine. */ | ||
| 200 | static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) | ||
| 201 | { | ||
| 202 | struct platform_device *pdev = dev_id; | ||
| 203 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 204 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 205 | u32 status; | ||
| 206 | u32 events = 0; | ||
| 207 | |||
| 208 | spin_lock_irq(&pdata->rtc->irq_lock); | ||
| 209 | status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR); | ||
| 210 | /* clear interrupt sources */ | ||
| 211 | writew(status, ioaddr + RTC_RTCISR); | ||
| 212 | |||
| 213 | /* clear alarm interrupt if it has occurred */ | ||
| 214 | if (status & RTC_ALM_BIT) | ||
| 215 | status &= ~RTC_ALM_BIT; | ||
| 216 | |||
| 217 | /* update irq data & counter */ | ||
| 218 | if (status & RTC_ALM_BIT) | ||
| 219 | events |= (RTC_AF | RTC_IRQF); | ||
| 220 | |||
| 221 | if (status & RTC_1HZ_BIT) | ||
| 222 | events |= (RTC_UF | RTC_IRQF); | ||
| 223 | |||
| 224 | if (status & PIT_ALL_ON) | ||
| 225 | events |= (RTC_PF | RTC_IRQF); | ||
| 226 | |||
| 227 | if ((status & RTC_ALM_BIT) && rtc_valid_tm(&pdata->g_rtc_alarm)) | ||
| 228 | rtc_update_alarm(&pdev->dev, &pdata->g_rtc_alarm); | ||
| 229 | |||
| 230 | rtc_update_irq(pdata->rtc, 1, events); | ||
| 231 | spin_unlock_irq(&pdata->rtc->irq_lock); | ||
| 232 | |||
| 233 | return IRQ_HANDLED; | ||
| 234 | } | ||
| 235 | |||
| 236 | /* | ||
| 237 | * Clear all interrupts and release the IRQ | ||
| 238 | */ | ||
| 239 | static void mxc_rtc_release(struct device *dev) | ||
| 240 | { | ||
| 241 | struct platform_device *pdev = to_platform_device(dev); | ||
| 242 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 243 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 244 | |||
| 245 | spin_lock_irq(&pdata->rtc->irq_lock); | ||
| 246 | |||
| 247 | /* Disable all rtc interrupts */ | ||
| 248 | writew(0, ioaddr + RTC_RTCIENR); | ||
| 249 | |||
| 250 | /* Clear all interrupt status */ | ||
| 251 | writew(0xffffffff, ioaddr + RTC_RTCISR); | ||
| 252 | |||
| 253 | spin_unlock_irq(&pdata->rtc->irq_lock); | ||
| 254 | } | ||
| 255 | |||
| 256 | static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit, | ||
| 257 | unsigned int enabled) | ||
| 258 | { | ||
| 259 | struct platform_device *pdev = to_platform_device(dev); | ||
| 260 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 261 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 262 | u32 reg; | ||
| 263 | |||
| 264 | spin_lock_irq(&pdata->rtc->irq_lock); | ||
| 265 | reg = readw(ioaddr + RTC_RTCIENR); | ||
| 266 | |||
| 267 | if (enabled) | ||
| 268 | reg |= bit; | ||
| 269 | else | ||
| 270 | reg &= ~bit; | ||
| 271 | |||
| 272 | writew(reg, ioaddr + RTC_RTCIENR); | ||
| 273 | spin_unlock_irq(&pdata->rtc->irq_lock); | ||
| 274 | } | ||
| 275 | |||
| 276 | static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
| 277 | { | ||
| 278 | mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled); | ||
| 279 | return 0; | ||
| 280 | } | ||
| 281 | |||
| 282 | static int mxc_rtc_update_irq_enable(struct device *dev, unsigned int enabled) | ||
| 283 | { | ||
| 284 | mxc_rtc_irq_enable(dev, RTC_1HZ_BIT, enabled); | ||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | /* | ||
| 289 | * This function reads the current RTC time into tm in Gregorian date. | ||
| 290 | */ | ||
| 291 | static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 292 | { | ||
| 293 | u32 val; | ||
| 294 | |||
| 295 | /* Avoid roll-over from reading the different registers */ | ||
| 296 | do { | ||
| 297 | val = get_alarm_or_time(dev, MXC_RTC_TIME); | ||
| 298 | } while (val != get_alarm_or_time(dev, MXC_RTC_TIME)); | ||
| 299 | |||
| 300 | rtc_time_to_tm(val, tm); | ||
| 301 | |||
| 302 | return 0; | ||
| 303 | } | ||
| 304 | |||
| 305 | /* | ||
| 306 | * This function sets the internal RTC time based on tm in Gregorian date. | ||
| 307 | */ | ||
| 308 | static int mxc_rtc_set_mmss(struct device *dev, unsigned long time) | ||
| 309 | { | ||
| 310 | /* Avoid roll-over from reading the different registers */ | ||
| 311 | do { | ||
| 312 | set_alarm_or_time(dev, MXC_RTC_TIME, time); | ||
| 313 | } while (time != get_alarm_or_time(dev, MXC_RTC_TIME)); | ||
| 314 | |||
| 315 | return 0; | ||
| 316 | } | ||
| 317 | |||
| 318 | /* | ||
| 319 | * This function reads the current alarm value into the passed in 'alrm' | ||
| 320 | * argument. It updates the alrm's pending field value based on the whether | ||
| 321 | * an alarm interrupt occurs or not. | ||
| 322 | */ | ||
| 323 | static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
| 324 | { | ||
| 325 | struct platform_device *pdev = to_platform_device(dev); | ||
| 326 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 327 | void __iomem *ioaddr = pdata->ioaddr; | ||
| 328 | |||
| 329 | rtc_time_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time); | ||
| 330 | alrm->pending = ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT)) ? 1 : 0; | ||
| 331 | |||
| 332 | return 0; | ||
| 333 | } | ||
| 334 | |||
| 335 | /* | ||
| 336 | * This function sets the RTC alarm based on passed in alrm. | ||
| 337 | */ | ||
| 338 | static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
| 339 | { | ||
| 340 | struct platform_device *pdev = to_platform_device(dev); | ||
| 341 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 342 | int ret; | ||
| 343 | |||
| 344 | if (rtc_valid_tm(&alrm->time)) { | ||
| 345 | if (alrm->time.tm_sec > 59 || | ||
| 346 | alrm->time.tm_hour > 23 || | ||
| 347 | alrm->time.tm_min > 59) | ||
| 348 | return -EINVAL; | ||
| 349 | |||
| 350 | ret = rtc_update_alarm(dev, &alrm->time); | ||
| 351 | } else { | ||
| 352 | ret = rtc_valid_tm(&alrm->time); | ||
| 353 | if (ret) | ||
| 354 | return ret; | ||
| 355 | |||
| 356 | ret = rtc_update_alarm(dev, &alrm->time); | ||
| 357 | } | ||
| 358 | |||
| 359 | if (ret) | ||
| 360 | return ret; | ||
| 361 | |||
| 362 | memcpy(&pdata->g_rtc_alarm, &alrm->time, sizeof(struct rtc_time)); | ||
| 363 | mxc_rtc_irq_enable(dev, RTC_ALM_BIT, alrm->enabled); | ||
| 364 | |||
| 365 | return 0; | ||
| 366 | } | ||
| 367 | |||
| 368 | /* RTC layer */ | ||
| 369 | static struct rtc_class_ops mxc_rtc_ops = { | ||
| 370 | .release = mxc_rtc_release, | ||
| 371 | .read_time = mxc_rtc_read_time, | ||
| 372 | .set_mmss = mxc_rtc_set_mmss, | ||
| 373 | .read_alarm = mxc_rtc_read_alarm, | ||
| 374 | .set_alarm = mxc_rtc_set_alarm, | ||
| 375 | .alarm_irq_enable = mxc_rtc_alarm_irq_enable, | ||
| 376 | .update_irq_enable = mxc_rtc_update_irq_enable, | ||
| 377 | }; | ||
| 378 | |||
| 379 | static int __init mxc_rtc_probe(struct platform_device *pdev) | ||
| 380 | { | ||
| 381 | struct clk *clk; | ||
| 382 | struct resource *res; | ||
| 383 | struct rtc_device *rtc; | ||
| 384 | struct rtc_plat_data *pdata = NULL; | ||
| 385 | u32 reg; | ||
| 386 | int ret, rate; | ||
| 387 | |||
| 388 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 389 | if (!res) | ||
| 390 | return -ENODEV; | ||
| 391 | |||
| 392 | pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); | ||
| 393 | if (!pdata) | ||
| 394 | return -ENOMEM; | ||
| 395 | |||
| 396 | pdata->ioaddr = ioremap(res->start, resource_size(res)); | ||
| 397 | |||
| 398 | clk = clk_get(&pdev->dev, "ckil"); | ||
| 399 | if (IS_ERR(clk)) | ||
| 400 | return PTR_ERR(clk); | ||
| 401 | |||
| 402 | rate = clk_get_rate(clk); | ||
| 403 | clk_put(clk); | ||
| 404 | |||
| 405 | if (rate == 32768) | ||
| 406 | reg = RTC_INPUT_CLK_32768HZ; | ||
| 407 | else if (rate == 32000) | ||
| 408 | reg = RTC_INPUT_CLK_32000HZ; | ||
| 409 | else if (rate == 38400) | ||
| 410 | reg = RTC_INPUT_CLK_38400HZ; | ||
| 411 | else { | ||
| 412 | dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", | ||
| 413 | clk_get_rate(clk)); | ||
| 414 | ret = -EINVAL; | ||
| 415 | goto exit_free_pdata; | ||
| 416 | } | ||
| 417 | |||
| 418 | reg |= RTC_ENABLE_BIT; | ||
| 419 | writew(reg, (pdata->ioaddr + RTC_RTCCTL)); | ||
| 420 | if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) { | ||
| 421 | dev_err(&pdev->dev, "hardware module can't be enabled!\n"); | ||
| 422 | ret = -EIO; | ||
| 423 | goto exit_free_pdata; | ||
| 424 | } | ||
| 425 | |||
| 426 | pdata->clk = clk_get(&pdev->dev, "rtc"); | ||
| 427 | if (IS_ERR(pdata->clk)) { | ||
| 428 | dev_err(&pdev->dev, "unable to get clock!\n"); | ||
| 429 | ret = PTR_ERR(pdata->clk); | ||
| 430 | goto exit_free_pdata; | ||
| 431 | } | ||
| 432 | |||
| 433 | clk_enable(pdata->clk); | ||
| 434 | |||
| 435 | rtc = rtc_device_register(pdev->name, &pdev->dev, &mxc_rtc_ops, | ||
| 436 | THIS_MODULE); | ||
| 437 | if (IS_ERR(rtc)) { | ||
| 438 | ret = PTR_ERR(rtc); | ||
| 439 | goto exit_put_clk; | ||
| 440 | } | ||
| 441 | |||
| 442 | pdata->rtc = rtc; | ||
| 443 | platform_set_drvdata(pdev, pdata); | ||
| 444 | |||
| 445 | /* Configure and enable the RTC */ | ||
| 446 | pdata->irq = platform_get_irq(pdev, 0); | ||
| 447 | |||
| 448 | if (pdata->irq >= 0 && | ||
| 449 | request_irq(pdata->irq, mxc_rtc_interrupt, IRQF_SHARED, | ||
| 450 | pdev->name, pdev) < 0) { | ||
| 451 | dev_warn(&pdev->dev, "interrupt not available.\n"); | ||
| 452 | pdata->irq = -1; | ||
| 453 | } | ||
| 454 | |||
| 455 | return 0; | ||
| 456 | |||
| 457 | exit_put_clk: | ||
| 458 | clk_put(pdata->clk); | ||
| 459 | |||
| 460 | exit_free_pdata: | ||
| 461 | kfree(pdata); | ||
| 462 | |||
| 463 | return ret; | ||
| 464 | } | ||
| 465 | |||
| 466 | static int __exit mxc_rtc_remove(struct platform_device *pdev) | ||
| 467 | { | ||
| 468 | struct rtc_plat_data *pdata = platform_get_drvdata(pdev); | ||
| 469 | |||
| 470 | rtc_device_unregister(pdata->rtc); | ||
| 471 | |||
| 472 | if (pdata->irq >= 0) | ||
| 473 | free_irq(pdata->irq, pdev); | ||
| 474 | |||
| 475 | clk_disable(pdata->clk); | ||
| 476 | clk_put(pdata->clk); | ||
| 477 | kfree(pdata); | ||
| 478 | platform_set_drvdata(pdev, NULL); | ||
| 479 | |||
| 480 | return 0; | ||
| 481 | } | ||
| 482 | |||
| 483 | static struct platform_driver mxc_rtc_driver = { | ||
| 484 | .driver = { | ||
| 485 | .name = "mxc_rtc", | ||
| 486 | .owner = THIS_MODULE, | ||
| 487 | }, | ||
| 488 | .remove = __exit_p(mxc_rtc_remove), | ||
| 489 | }; | ||
| 490 | |||
| 491 | static int __init mxc_rtc_init(void) | ||
| 492 | { | ||
| 493 | return platform_driver_probe(&mxc_rtc_driver, mxc_rtc_probe); | ||
| 494 | } | ||
| 495 | |||
| 496 | static void __exit mxc_rtc_exit(void) | ||
| 497 | { | ||
| 498 | platform_driver_unregister(&mxc_rtc_driver); | ||
| 499 | } | ||
| 500 | |||
| 501 | module_init(mxc_rtc_init); | ||
| 502 | module_exit(mxc_rtc_exit); | ||
| 503 | |||
| 504 | MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); | ||
| 505 | MODULE_DESCRIPTION("RTC driver for Freescale MXC"); | ||
| 506 | MODULE_LICENSE("GPL"); | ||
| 507 | |||
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index bd1ce8e2bc18..0587d53987fe 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c | |||
| @@ -430,7 +430,7 @@ fail: | |||
| 430 | 430 | ||
| 431 | static int __exit omap_rtc_remove(struct platform_device *pdev) | 431 | static int __exit omap_rtc_remove(struct platform_device *pdev) |
| 432 | { | 432 | { |
| 433 | struct rtc_device *rtc = platform_get_drvdata(pdev);; | 433 | struct rtc_device *rtc = platform_get_drvdata(pdev); |
| 434 | 434 | ||
| 435 | device_init_wakeup(&pdev->dev, 0); | 435 | device_init_wakeup(&pdev->dev, 0); |
| 436 | 436 | ||
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c new file mode 100644 index 000000000000..a99c28992d21 --- /dev/null +++ b/drivers/rtc/rtc-pcap.c | |||
| @@ -0,0 +1,224 @@ | |||
| 1 | /* | ||
| 2 | * pcap rtc code for Motorola EZX phones | ||
| 3 | * | ||
| 4 | * Copyright (c) 2008 guiming zhuo <gmzhuo@gmail.com> | ||
| 5 | * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com> | ||
| 6 | * | ||
| 7 | * Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/init.h> | ||
| 18 | #include <linux/mfd/ezx-pcap.h> | ||
| 19 | #include <linux/rtc.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | |||
| 22 | struct pcap_rtc { | ||
| 23 | struct pcap_chip *pcap; | ||
| 24 | struct rtc_device *rtc; | ||
| 25 | }; | ||
| 26 | |||
| 27 | static irqreturn_t pcap_rtc_irq(int irq, void *_pcap_rtc) | ||
| 28 | { | ||
| 29 | struct pcap_rtc *pcap_rtc = _pcap_rtc; | ||
| 30 | unsigned long rtc_events; | ||
| 31 | |||
| 32 | if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ)) | ||
| 33 | rtc_events = RTC_IRQF | RTC_UF; | ||
| 34 | else if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA)) | ||
| 35 | rtc_events = RTC_IRQF | RTC_AF; | ||
| 36 | else | ||
| 37 | rtc_events = 0; | ||
| 38 | |||
| 39 | rtc_update_irq(pcap_rtc->rtc, 1, rtc_events); | ||
| 40 | return IRQ_HANDLED; | ||
| 41 | } | ||
| 42 | |||
| 43 | static int pcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
| 44 | { | ||
| 45 | struct platform_device *pdev = to_platform_device(dev); | ||
| 46 | struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); | ||
| 47 | struct rtc_time *tm = &alrm->time; | ||
| 48 | unsigned long secs; | ||
| 49 | u32 tod; /* time of day, seconds since midnight */ | ||
| 50 | u32 days; /* days since 1/1/1970 */ | ||
| 51 | |||
| 52 | ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TODA, &tod); | ||
| 53 | secs = tod & PCAP_RTC_TOD_MASK; | ||
| 54 | |||
| 55 | ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days); | ||
| 56 | secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; | ||
| 57 | |||
| 58 | rtc_time_to_tm(secs, tm); | ||
| 59 | |||
| 60 | return 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||
| 64 | { | ||
| 65 | struct platform_device *pdev = to_platform_device(dev); | ||
| 66 | struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); | ||
| 67 | struct rtc_time *tm = &alrm->time; | ||
| 68 | unsigned long secs; | ||
| 69 | u32 tod, days; | ||
| 70 | |||
| 71 | rtc_tm_to_time(tm, &secs); | ||
| 72 | |||
| 73 | tod = secs % SEC_PER_DAY; | ||
| 74 | ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod); | ||
| 75 | |||
| 76 | days = secs / SEC_PER_DAY; | ||
| 77 | ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, days); | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 82 | static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 83 | { | ||
| 84 | struct platform_device *pdev = to_platform_device(dev); | ||
| 85 | struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); | ||
| 86 | unsigned long secs; | ||
| 87 | u32 tod, days; | ||
| 88 | |||
| 89 | ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TOD, &tod); | ||
| 90 | secs = tod & PCAP_RTC_TOD_MASK; | ||
| 91 | |||
| 92 | ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days); | ||
| 93 | secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY; | ||
| 94 | |||
| 95 | rtc_time_to_tm(secs, tm); | ||
| 96 | |||
| 97 | return rtc_valid_tm(tm); | ||
| 98 | } | ||
| 99 | |||
| 100 | static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs) | ||
| 101 | { | ||
| 102 | struct platform_device *pdev = to_platform_device(dev); | ||
| 103 | struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); | ||
| 104 | u32 tod, days; | ||
| 105 | |||
| 106 | tod = secs % SEC_PER_DAY; | ||
| 107 | ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TOD, tod); | ||
| 108 | |||
| 109 | days = secs / SEC_PER_DAY; | ||
| 110 | ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAY, days); | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static int pcap_rtc_irq_enable(struct device *dev, int pirq, unsigned int en) | ||
| 116 | { | ||
| 117 | struct platform_device *pdev = to_platform_device(dev); | ||
| 118 | struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); | ||
| 119 | |||
| 120 | if (en) | ||
| 121 | enable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); | ||
| 122 | else | ||
| 123 | disable_irq(pcap_to_irq(pcap_rtc->pcap, pirq)); | ||
| 124 | |||
| 125 | return 0; | ||
| 126 | } | ||
| 127 | |||
| 128 | static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en) | ||
| 129 | { | ||
| 130 | return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en); | ||
| 131 | } | ||
| 132 | |||
| 133 | static int pcap_rtc_update_irq_enable(struct device *dev, unsigned int en) | ||
| 134 | { | ||
| 135 | return pcap_rtc_irq_enable(dev, PCAP_IRQ_1HZ, en); | ||
| 136 | } | ||
| 137 | |||
| 138 | static const struct rtc_class_ops pcap_rtc_ops = { | ||
| 139 | .read_time = pcap_rtc_read_time, | ||
| 140 | .read_alarm = pcap_rtc_read_alarm, | ||
| 141 | .set_alarm = pcap_rtc_set_alarm, | ||
| 142 | .set_mmss = pcap_rtc_set_mmss, | ||
| 143 | .alarm_irq_enable = pcap_rtc_alarm_irq_enable, | ||
| 144 | .update_irq_enable = pcap_rtc_update_irq_enable, | ||
| 145 | }; | ||
| 146 | |||
| 147 | static int __devinit pcap_rtc_probe(struct platform_device *pdev) | ||
| 148 | { | ||
| 149 | struct pcap_rtc *pcap_rtc; | ||
| 150 | int timer_irq, alarm_irq; | ||
| 151 | int err = -ENOMEM; | ||
| 152 | |||
| 153 | pcap_rtc = kmalloc(sizeof(struct pcap_rtc), GFP_KERNEL); | ||
| 154 | if (!pcap_rtc) | ||
| 155 | return err; | ||
| 156 | |||
| 157 | pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent); | ||
| 158 | |||
| 159 | pcap_rtc->rtc = rtc_device_register("pcap", &pdev->dev, | ||
| 160 | &pcap_rtc_ops, THIS_MODULE); | ||
| 161 | if (IS_ERR(pcap_rtc->rtc)) { | ||
| 162 | err = PTR_ERR(pcap_rtc->rtc); | ||
| 163 | goto fail_rtc; | ||
| 164 | } | ||
| 165 | |||
| 166 | platform_set_drvdata(pdev, pcap_rtc); | ||
| 167 | |||
| 168 | timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ); | ||
| 169 | alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA); | ||
| 170 | |||
| 171 | err = request_irq(timer_irq, pcap_rtc_irq, 0, "RTC Timer", pcap_rtc); | ||
| 172 | if (err) | ||
| 173 | goto fail_timer; | ||
| 174 | |||
| 175 | err = request_irq(alarm_irq, pcap_rtc_irq, 0, "RTC Alarm", pcap_rtc); | ||
| 176 | if (err) | ||
| 177 | goto fail_alarm; | ||
| 178 | |||
| 179 | return 0; | ||
| 180 | fail_alarm: | ||
| 181 | free_irq(timer_irq, pcap_rtc); | ||
| 182 | fail_timer: | ||
| 183 | rtc_device_unregister(pcap_rtc->rtc); | ||
| 184 | fail_rtc: | ||
| 185 | kfree(pcap_rtc); | ||
| 186 | return err; | ||
| 187 | } | ||
| 188 | |||
| 189 | static int __devexit pcap_rtc_remove(struct platform_device *pdev) | ||
| 190 | { | ||
| 191 | struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev); | ||
| 192 | |||
| 193 | free_irq(pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ), pcap_rtc); | ||
| 194 | free_irq(pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA), pcap_rtc); | ||
| 195 | rtc_device_unregister(pcap_rtc->rtc); | ||
| 196 | kfree(pcap_rtc); | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static struct platform_driver pcap_rtc_driver = { | ||
| 202 | .remove = __devexit_p(pcap_rtc_remove), | ||
| 203 | .driver = { | ||
| 204 | .name = "pcap-rtc", | ||
| 205 | .owner = THIS_MODULE, | ||
| 206 | }, | ||
| 207 | }; | ||
| 208 | |||
| 209 | static int __init rtc_pcap_init(void) | ||
| 210 | { | ||
| 211 | return platform_driver_probe(&pcap_rtc_driver, pcap_rtc_probe); | ||
| 212 | } | ||
| 213 | |||
| 214 | static void __exit rtc_pcap_exit(void) | ||
| 215 | { | ||
| 216 | platform_driver_unregister(&pcap_rtc_driver); | ||
| 217 | } | ||
| 218 | |||
| 219 | module_init(rtc_pcap_init); | ||
| 220 | module_exit(rtc_pcap_exit); | ||
| 221 | |||
| 222 | MODULE_DESCRIPTION("Motorola pcap rtc driver"); | ||
| 223 | MODULE_AUTHOR("guiming zhuo <gmzhuo@gmail.com>"); | ||
| 224 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c new file mode 100644 index 000000000000..e75df9d50e27 --- /dev/null +++ b/drivers/rtc/rtc-pcf2123.c | |||
| @@ -0,0 +1,364 @@ | |||
| 1 | /* | ||
| 2 | * An SPI driver for the Philips PCF2123 RTC | ||
| 3 | * Copyright 2009 Cyber Switching, Inc. | ||
| 4 | * | ||
| 5 | * Author: Chris Verges <chrisv@cyberswitching.com> | ||
| 6 | * Maintainers: http://www.cyberswitching.com | ||
| 7 | * | ||
| 8 | * based on the RS5C348 driver in this same directory. | ||
| 9 | * | ||
| 10 | * Thanks to Christian Pellegrin <chripell@fsfe.org> for | ||
| 11 | * the sysfs contributions to this driver. | ||
| 12 | * | ||
| 13 | * This program is free software; you can redistribute it and/or modify | ||
| 14 | * it under the terms of the GNU General Public License version 2 as | ||
| 15 | * published by the Free Software Foundation. | ||
| 16 | * | ||
| 17 | * Please note that the CS is active high, so platform data | ||
| 18 | * should look something like: | ||
| 19 | * | ||
| 20 | * static struct spi_board_info ek_spi_devices[] = { | ||
| 21 | * ... | ||
| 22 | * { | ||
| 23 | * .modalias = "rtc-pcf2123", | ||
| 24 | * .chip_select = 1, | ||
| 25 | * .controller_data = (void *)AT91_PIN_PA10, | ||
| 26 | * .max_speed_hz = 1000 * 1000, | ||
| 27 | * .mode = SPI_CS_HIGH, | ||
| 28 | * .bus_num = 0, | ||
| 29 | * }, | ||
| 30 | * ... | ||
| 31 | *}; | ||
| 32 | * | ||
| 33 | */ | ||
| 34 | |||
| 35 | #include <linux/bcd.h> | ||
| 36 | #include <linux/delay.h> | ||
| 37 | #include <linux/device.h> | ||
| 38 | #include <linux/errno.h> | ||
| 39 | #include <linux/init.h> | ||
| 40 | #include <linux/kernel.h> | ||
| 41 | #include <linux/string.h> | ||
| 42 | #include <linux/rtc.h> | ||
| 43 | #include <linux/spi/spi.h> | ||
| 44 | |||
| 45 | #define DRV_VERSION "0.6" | ||
| 46 | |||
| 47 | #define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */ | ||
| 48 | #define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */ | ||
| 49 | #define PCF2123_REG_SC (0x02) /* datetime */ | ||
| 50 | #define PCF2123_REG_MN (0x03) | ||
| 51 | #define PCF2123_REG_HR (0x04) | ||
| 52 | #define PCF2123_REG_DM (0x05) | ||
| 53 | #define PCF2123_REG_DW (0x06) | ||
| 54 | #define PCF2123_REG_MO (0x07) | ||
| 55 | #define PCF2123_REG_YR (0x08) | ||
| 56 | |||
| 57 | #define PCF2123_SUBADDR (1 << 4) | ||
| 58 | #define PCF2123_WRITE ((0 << 7) | PCF2123_SUBADDR) | ||
| 59 | #define PCF2123_READ ((1 << 7) | PCF2123_SUBADDR) | ||
| 60 | |||
| 61 | static struct spi_driver pcf2123_driver; | ||
| 62 | |||
| 63 | struct pcf2123_sysfs_reg { | ||
| 64 | struct device_attribute attr; | ||
| 65 | char name[2]; | ||
| 66 | }; | ||
| 67 | |||
| 68 | struct pcf2123_plat_data { | ||
| 69 | struct rtc_device *rtc; | ||
| 70 | struct pcf2123_sysfs_reg regs[16]; | ||
| 71 | }; | ||
| 72 | |||
| 73 | /* | ||
| 74 | * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select | ||
| 75 | * is released properly after an SPI write. This function should be | ||
| 76 | * called after EVERY read/write call over SPI. | ||
| 77 | */ | ||
| 78 | static inline void pcf2123_delay_trec(void) | ||
| 79 | { | ||
| 80 | ndelay(30); | ||
| 81 | } | ||
| 82 | |||
| 83 | static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr, | ||
| 84 | char *buffer) | ||
| 85 | { | ||
| 86 | struct spi_device *spi = to_spi_device(dev); | ||
| 87 | struct pcf2123_sysfs_reg *r; | ||
| 88 | u8 txbuf[1], rxbuf[1]; | ||
| 89 | unsigned long reg; | ||
| 90 | int ret; | ||
| 91 | |||
| 92 | r = container_of(attr, struct pcf2123_sysfs_reg, attr); | ||
| 93 | |||
| 94 | if (strict_strtoul(r->name, 16, ®)) | ||
| 95 | return -EINVAL; | ||
| 96 | |||
| 97 | txbuf[0] = PCF2123_READ | reg; | ||
| 98 | ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1); | ||
| 99 | if (ret < 0) | ||
| 100 | return -EIO; | ||
| 101 | pcf2123_delay_trec(); | ||
| 102 | return sprintf(buffer, "0x%x\n", rxbuf[0]); | ||
| 103 | } | ||
| 104 | |||
| 105 | static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr, | ||
| 106 | const char *buffer, size_t count) { | ||
| 107 | struct spi_device *spi = to_spi_device(dev); | ||
| 108 | struct pcf2123_sysfs_reg *r; | ||
| 109 | u8 txbuf[2]; | ||
| 110 | unsigned long reg; | ||
| 111 | unsigned long val; | ||
| 112 | |||
| 113 | int ret; | ||
| 114 | |||
| 115 | r = container_of(attr, struct pcf2123_sysfs_reg, attr); | ||
| 116 | |||
| 117 | if (strict_strtoul(r->name, 16, ®) | ||
| 118 | || strict_strtoul(buffer, 10, &val)) | ||
| 119 | return -EINVAL; | ||
| 120 | |||
| 121 | txbuf[0] = PCF2123_WRITE | reg; | ||
| 122 | txbuf[1] = val; | ||
| 123 | ret = spi_write(spi, txbuf, sizeof(txbuf)); | ||
| 124 | if (ret < 0) | ||
| 125 | return -EIO; | ||
| 126 | pcf2123_delay_trec(); | ||
| 127 | return count; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
| 131 | { | ||
| 132 | struct spi_device *spi = to_spi_device(dev); | ||
| 133 | u8 txbuf[1], rxbuf[7]; | ||
| 134 | int ret; | ||
| 135 | |||
| 136 | txbuf[0] = PCF2123_READ | PCF2123_REG_SC; | ||
| 137 | ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), | ||
| 138 | rxbuf, sizeof(rxbuf)); | ||
| 139 | if (ret < 0) | ||
| 140 | return ret; | ||
| 141 | pcf2123_delay_trec(); | ||
| 142 | |||
| 143 | tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F); | ||
| 144 | tm->tm_min = bcd2bin(rxbuf[1] & 0x7F); | ||
| 145 | tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */ | ||
| 146 | tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F); | ||
| 147 | tm->tm_wday = rxbuf[4] & 0x07; | ||
| 148 | tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */ | ||
| 149 | tm->tm_year = bcd2bin(rxbuf[6]); | ||
| 150 | if (tm->tm_year < 70) | ||
| 151 | tm->tm_year += 100; /* assume we are in 1970...2069 */ | ||
| 152 | |||
| 153 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | ||
| 154 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 155 | __func__, | ||
| 156 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 157 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 158 | |||
| 159 | /* the clock can give out invalid datetime, but we cannot return | ||
| 160 | * -EINVAL otherwise hwclock will refuse to set the time on bootup. | ||
| 161 | */ | ||
| 162 | if (rtc_valid_tm(tm) < 0) | ||
| 163 | dev_err(dev, "retrieved date/time is not valid.\n"); | ||
| 164 | |||
| 165 | return 0; | ||
| 166 | } | ||
| 167 | |||
| 168 | static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
| 169 | { | ||
| 170 | struct spi_device *spi = to_spi_device(dev); | ||
| 171 | u8 txbuf[8]; | ||
| 172 | int ret; | ||
| 173 | |||
| 174 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | ||
| 175 | "mday=%d, mon=%d, year=%d, wday=%d\n", | ||
| 176 | __func__, | ||
| 177 | tm->tm_sec, tm->tm_min, tm->tm_hour, | ||
| 178 | tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); | ||
| 179 | |||
| 180 | /* Stop the counter first */ | ||
| 181 | txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; | ||
| 182 | txbuf[1] = 0x20; | ||
| 183 | ret = spi_write(spi, txbuf, 2); | ||
| 184 | if (ret < 0) | ||
| 185 | return ret; | ||
| 186 | pcf2123_delay_trec(); | ||
| 187 | |||
| 188 | /* Set the new time */ | ||
| 189 | txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC; | ||
| 190 | txbuf[1] = bin2bcd(tm->tm_sec & 0x7F); | ||
| 191 | txbuf[2] = bin2bcd(tm->tm_min & 0x7F); | ||
| 192 | txbuf[3] = bin2bcd(tm->tm_hour & 0x3F); | ||
| 193 | txbuf[4] = bin2bcd(tm->tm_mday & 0x3F); | ||
| 194 | txbuf[5] = tm->tm_wday & 0x07; | ||
| 195 | txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */ | ||
| 196 | txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100); | ||
| 197 | |||
| 198 | ret = spi_write(spi, txbuf, sizeof(txbuf)); | ||
| 199 | if (ret < 0) | ||
| 200 | return ret; | ||
| 201 | pcf2123_delay_trec(); | ||
| 202 | |||
| 203 | /* Start the counter */ | ||
| 204 | txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; | ||
| 205 | txbuf[1] = 0x00; | ||
| 206 | ret = spi_write(spi, txbuf, 2); | ||
| 207 | if (ret < 0) | ||
| 208 | return ret; | ||
| 209 | pcf2123_delay_trec(); | ||
| 210 | |||
| 211 | return 0; | ||
| 212 | } | ||
| 213 | |||
| 214 | static const struct rtc_class_ops pcf2123_rtc_ops = { | ||
| 215 | .read_time = pcf2123_rtc_read_time, | ||
| 216 | .set_time = pcf2123_rtc_set_time, | ||
| 217 | }; | ||
| 218 | |||
| 219 | static int __devinit pcf2123_probe(struct spi_device *spi) | ||
| 220 | { | ||
| 221 | struct rtc_device *rtc; | ||
| 222 | struct pcf2123_plat_data *pdata; | ||
| 223 | u8 txbuf[2], rxbuf[2]; | ||
| 224 | int ret, i; | ||
| 225 | |||
| 226 | pdata = kzalloc(sizeof(struct pcf2123_plat_data), GFP_KERNEL); | ||
| 227 | if (!pdata) | ||
| 228 | return -ENOMEM; | ||
| 229 | spi->dev.platform_data = pdata; | ||
| 230 | |||
| 231 | /* Send a software reset command */ | ||
| 232 | txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; | ||
| 233 | txbuf[1] = 0x58; | ||
| 234 | dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n", | ||
| 235 | txbuf[0], txbuf[1]); | ||
| 236 | ret = spi_write(spi, txbuf, 2 * sizeof(u8)); | ||
| 237 | if (ret < 0) | ||
| 238 | goto kfree_exit; | ||
| 239 | pcf2123_delay_trec(); | ||
| 240 | |||
| 241 | /* Stop the counter */ | ||
| 242 | txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; | ||
| 243 | txbuf[1] = 0x20; | ||
| 244 | dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n", | ||
| 245 | txbuf[0], txbuf[1]); | ||
| 246 | ret = spi_write(spi, txbuf, 2 * sizeof(u8)); | ||
| 247 | if (ret < 0) | ||
| 248 | goto kfree_exit; | ||
| 249 | pcf2123_delay_trec(); | ||
| 250 | |||
| 251 | /* See if the counter was actually stopped */ | ||
| 252 | txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1; | ||
| 253 | dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n", | ||
| 254 | txbuf[0]); | ||
| 255 | ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8), | ||
| 256 | rxbuf, 2 * sizeof(u8)); | ||
| 257 | dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n", | ||
| 258 | rxbuf[0], rxbuf[1]); | ||
| 259 | if (ret < 0) | ||
| 260 | goto kfree_exit; | ||
| 261 | pcf2123_delay_trec(); | ||
| 262 | |||
| 263 | if (!(rxbuf[0] & 0x20)) { | ||
| 264 | dev_err(&spi->dev, "chip not found\n"); | ||
| 265 | goto kfree_exit; | ||
| 266 | } | ||
| 267 | |||
| 268 | dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n"); | ||
| 269 | dev_info(&spi->dev, "spiclk %u KHz.\n", | ||
| 270 | (spi->max_speed_hz + 500) / 1000); | ||
| 271 | |||
| 272 | /* Start the counter */ | ||
| 273 | txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1; | ||
| 274 | txbuf[1] = 0x00; | ||
| 275 | ret = spi_write(spi, txbuf, sizeof(txbuf)); | ||
| 276 | if (ret < 0) | ||
| 277 | goto kfree_exit; | ||
| 278 | pcf2123_delay_trec(); | ||
| 279 | |||
| 280 | /* Finalize the initialization */ | ||
| 281 | rtc = rtc_device_register(pcf2123_driver.driver.name, &spi->dev, | ||
| 282 | &pcf2123_rtc_ops, THIS_MODULE); | ||
| 283 | |||
| 284 | if (IS_ERR(rtc)) { | ||
| 285 | dev_err(&spi->dev, "failed to register.\n"); | ||
| 286 | ret = PTR_ERR(rtc); | ||
| 287 | goto kfree_exit; | ||
| 288 | } | ||
| 289 | |||
| 290 | pdata->rtc = rtc; | ||
| 291 | |||
| 292 | for (i = 0; i < 16; i++) { | ||
| 293 | sprintf(pdata->regs[i].name, "%1x", i); | ||
| 294 | pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR; | ||
| 295 | pdata->regs[i].attr.attr.name = pdata->regs[i].name; | ||
| 296 | pdata->regs[i].attr.show = pcf2123_show; | ||
| 297 | pdata->regs[i].attr.store = pcf2123_store; | ||
| 298 | ret = device_create_file(&spi->dev, &pdata->regs[i].attr); | ||
| 299 | if (ret) { | ||
| 300 | dev_err(&spi->dev, "Unable to create sysfs %s\n", | ||
| 301 | pdata->regs[i].name); | ||
| 302 | goto sysfs_exit; | ||
| 303 | } | ||
| 304 | } | ||
| 305 | |||
| 306 | return 0; | ||
| 307 | |||
| 308 | sysfs_exit: | ||
| 309 | for (i--; i >= 0; i--) | ||
| 310 | device_remove_file(&spi->dev, &pdata->regs[i].attr); | ||
| 311 | |||
| 312 | kfree_exit: | ||
| 313 | kfree(pdata); | ||
| 314 | spi->dev.platform_data = NULL; | ||
| 315 | return ret; | ||
| 316 | } | ||
| 317 | |||
| 318 | static int pcf2123_remove(struct spi_device *spi) | ||
| 319 | { | ||
| 320 | struct pcf2123_plat_data *pdata = spi->dev.platform_data; | ||
| 321 | int i; | ||
| 322 | |||
| 323 | if (pdata) { | ||
| 324 | struct rtc_device *rtc = pdata->rtc; | ||
| 325 | |||
| 326 | if (rtc) | ||
| 327 | rtc_device_unregister(rtc); | ||
| 328 | for (i = 0; i < 16; i++) | ||
| 329 | if (pdata->regs[i].name[0]) | ||
| 330 | device_remove_file(&spi->dev, | ||
| 331 | &pdata->regs[i].attr); | ||
| 332 | kfree(pdata); | ||
| 333 | } | ||
| 334 | |||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | |||
| 338 | static struct spi_driver pcf2123_driver = { | ||
| 339 | .driver = { | ||
| 340 | .name = "rtc-pcf2123", | ||
| 341 | .bus = &spi_bus_type, | ||
| 342 | .owner = THIS_MODULE, | ||
| 343 | }, | ||
| 344 | .probe = pcf2123_probe, | ||
| 345 | .remove = __devexit_p(pcf2123_remove), | ||
| 346 | }; | ||
| 347 | |||
| 348 | static int __init pcf2123_init(void) | ||
| 349 | { | ||
| 350 | return spi_register_driver(&pcf2123_driver); | ||
| 351 | } | ||
| 352 | |||
| 353 | static void __exit pcf2123_exit(void) | ||
| 354 | { | ||
| 355 | spi_unregister_driver(&pcf2123_driver); | ||
| 356 | } | ||
| 357 | |||
| 358 | MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>"); | ||
| 359 | MODULE_DESCRIPTION("NXP PCF2123 RTC driver"); | ||
| 360 | MODULE_LICENSE("GPL"); | ||
| 361 | MODULE_VERSION(DRV_VERSION); | ||
| 362 | |||
| 363 | module_init(pcf2123_init); | ||
| 364 | module_exit(pcf2123_exit); | ||
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c index f4dd87e29075..33a10c47260e 100644 --- a/drivers/rtc/rtc-pcf50633.c +++ b/drivers/rtc/rtc-pcf50633.c | |||
| @@ -70,7 +70,7 @@ static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf) | |||
| 70 | rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]); | 70 | rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]); |
| 71 | rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]); | 71 | rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]); |
| 72 | rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]); | 72 | rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]); |
| 73 | rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]); | 73 | rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1; |
| 74 | rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100; | 74 | rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| @@ -81,7 +81,7 @@ static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc) | |||
| 81 | pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour); | 81 | pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour); |
| 82 | pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday); | 82 | pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday); |
| 83 | pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday); | 83 | pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday); |
| 84 | pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon); | 84 | pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1); |
| 85 | pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100); | 85 | pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100); |
| 86 | } | 86 | } |
| 87 | 87 | ||
| @@ -245,8 +245,9 @@ static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
| 245 | ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA, | 245 | ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA, |
| 246 | PCF50633_TI_EXTENT, &pcf_tm.time[0]); | 246 | PCF50633_TI_EXTENT, &pcf_tm.time[0]); |
| 247 | 247 | ||
| 248 | if (!alarm_masked) | 248 | if (!alarm_masked || alrm->enabled) |
| 249 | pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM); | 249 | pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM); |
| 250 | rtc->alarm_enabled = alrm->enabled; | ||
| 250 | 251 | ||
| 251 | return ret; | 252 | return ret; |
| 252 | } | 253 | } |
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c index bb8cc05605ac..747ca194fad4 100644 --- a/drivers/rtc/rtc-pxa.c +++ b/drivers/rtc/rtc-pxa.c | |||
| @@ -438,34 +438,37 @@ static int __exit pxa_rtc_remove(struct platform_device *pdev) | |||
| 438 | } | 438 | } |
| 439 | 439 | ||
| 440 | #ifdef CONFIG_PM | 440 | #ifdef CONFIG_PM |
| 441 | static int pxa_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 441 | static int pxa_rtc_suspend(struct device *dev) |
| 442 | { | 442 | { |
| 443 | struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); | 443 | struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); |
| 444 | 444 | ||
| 445 | if (device_may_wakeup(&pdev->dev)) | 445 | if (device_may_wakeup(dev)) |
| 446 | enable_irq_wake(pxa_rtc->irq_Alrm); | 446 | enable_irq_wake(pxa_rtc->irq_Alrm); |
| 447 | return 0; | 447 | return 0; |
| 448 | } | 448 | } |
| 449 | 449 | ||
| 450 | static int pxa_rtc_resume(struct platform_device *pdev) | 450 | static int pxa_rtc_resume(struct device *dev) |
| 451 | { | 451 | { |
| 452 | struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev); | 452 | struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev); |
| 453 | 453 | ||
| 454 | if (device_may_wakeup(&pdev->dev)) | 454 | if (device_may_wakeup(dev)) |
| 455 | disable_irq_wake(pxa_rtc->irq_Alrm); | 455 | disable_irq_wake(pxa_rtc->irq_Alrm); |
| 456 | return 0; | 456 | return 0; |
| 457 | } | 457 | } |
| 458 | #else | 458 | |
| 459 | #define pxa_rtc_suspend NULL | 459 | static struct dev_pm_ops pxa_rtc_pm_ops = { |
| 460 | #define pxa_rtc_resume NULL | 460 | .suspend = pxa_rtc_suspend, |
| 461 | .resume = pxa_rtc_resume, | ||
| 462 | }; | ||
| 461 | #endif | 463 | #endif |
| 462 | 464 | ||
| 463 | static struct platform_driver pxa_rtc_driver = { | 465 | static struct platform_driver pxa_rtc_driver = { |
| 464 | .remove = __exit_p(pxa_rtc_remove), | 466 | .remove = __exit_p(pxa_rtc_remove), |
| 465 | .suspend = pxa_rtc_suspend, | ||
| 466 | .resume = pxa_rtc_resume, | ||
| 467 | .driver = { | 467 | .driver = { |
| 468 | .name = "pxa-rtc", | 468 | .name = "pxa-rtc", |
| 469 | #ifdef CONFIG_PM | ||
| 470 | .pm = &pxa_rtc_pm_ops, | ||
| 471 | #endif | ||
| 469 | }, | 472 | }, |
| 470 | }; | 473 | }; |
| 471 | 474 | ||
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c index 42028f233bef..9beba49c3c5b 100644 --- a/drivers/rtc/rtc-r9701.c +++ b/drivers/rtc/rtc-r9701.c | |||
| @@ -174,3 +174,4 @@ module_exit(r9701_exit); | |||
| 174 | MODULE_DESCRIPTION("r9701 spi RTC driver"); | 174 | MODULE_DESCRIPTION("r9701 spi RTC driver"); |
| 175 | MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); | 175 | MODULE_AUTHOR("Magnus Damm <damm@opensource.se>"); |
| 176 | MODULE_LICENSE("GPL"); | 176 | MODULE_LICENSE("GPL"); |
| 177 | MODULE_ALIAS("spi:rtc-r9701"); | ||
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c index dd1e2bc7a472..2099037cb3ea 100644 --- a/drivers/rtc/rtc-rs5c348.c +++ b/drivers/rtc/rtc-rs5c348.c | |||
| @@ -251,3 +251,4 @@ MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>"); | |||
| 251 | MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); | 251 | MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver"); |
| 252 | MODULE_LICENSE("GPL"); | 252 | MODULE_LICENSE("GPL"); |
| 253 | MODULE_VERSION(DRV_VERSION); | 253 | MODULE_VERSION(DRV_VERSION); |
| 254 | MODULE_ALIAS("spi:rtc-rs5c348"); | ||
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 021b2928f0b9..29f98a70586e 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c | |||
| @@ -393,31 +393,34 @@ static int sa1100_rtc_remove(struct platform_device *pdev) | |||
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | #ifdef CONFIG_PM | 395 | #ifdef CONFIG_PM |
| 396 | static int sa1100_rtc_suspend(struct platform_device *pdev, pm_message_t state) | 396 | static int sa1100_rtc_suspend(struct device *dev) |
| 397 | { | 397 | { |
| 398 | if (device_may_wakeup(&pdev->dev)) | 398 | if (device_may_wakeup(dev)) |
| 399 | enable_irq_wake(IRQ_RTCAlrm); | 399 | enable_irq_wake(IRQ_RTCAlrm); |
| 400 | return 0; | 400 | return 0; |
| 401 | } | 401 | } |
| 402 | 402 | ||
| 403 | static int sa1100_rtc_resume(struct platform_device *pdev) | 403 | static int sa1100_rtc_resume(struct device *dev) |
| 404 | { | 404 | { |
| 405 | if (device_may_wakeup(&pdev->dev)) | 405 | if (device_may_wakeup(dev)) |
| 406 | disable_irq_wake(IRQ_RTCAlrm); | 406 | disable_irq_wake(IRQ_RTCAlrm); |
| 407 | return 0; | 407 | return 0; |
| 408 | } | 408 | } |
| 409 | #else | 409 | |
| 410 | #define sa1100_rtc_suspend NULL | 410 | static struct dev_pm_ops sa1100_rtc_pm_ops = { |
| 411 | #define sa1100_rtc_resume NULL | 411 | .suspend = sa1100_rtc_suspend, |
| 412 | .resume = sa1100_rtc_resume, | ||
| 413 | }; | ||
| 412 | #endif | 414 | #endif |
| 413 | 415 | ||
| 414 | static struct platform_driver sa1100_rtc_driver = { | 416 | static struct platform_driver sa1100_rtc_driver = { |
| 415 | .probe = sa1100_rtc_probe, | 417 | .probe = sa1100_rtc_probe, |
| 416 | .remove = sa1100_rtc_remove, | 418 | .remove = sa1100_rtc_remove, |
| 417 | .suspend = sa1100_rtc_suspend, | ||
| 418 | .resume = sa1100_rtc_resume, | ||
| 419 | .driver = { | 419 | .driver = { |
| 420 | .name = "sa1100-rtc", | 420 | .name = "sa1100-rtc", |
| 421 | #ifdef CONFIG_PM | ||
| 422 | .pm = &sa1100_rtc_pm_ops, | ||
| 423 | #endif | ||
| 421 | }, | 424 | }, |
| 422 | }; | 425 | }; |
| 423 | 426 | ||
diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c new file mode 100644 index 000000000000..d7ce1a5c857d --- /dev/null +++ b/drivers/rtc/rtc-stmp3xxx.c | |||
| @@ -0,0 +1,304 @@ | |||
| 1 | /* | ||
| 2 | * Freescale STMP37XX/STMP378X Real Time Clock driver | ||
| 3 | * | ||
| 4 | * Copyright (c) 2007 Sigmatel, Inc. | ||
| 5 | * Peter Hartley, <peter.hartley@sigmatel.com> | ||
| 6 | * | ||
| 7 | * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. | ||
| 8 | * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. | ||
| 9 | */ | ||
| 10 | |||
| 11 | /* | ||
| 12 | * The code contained herein is licensed under the GNU General Public | ||
| 13 | * License. You may obtain a copy of the GNU General Public License | ||
| 14 | * Version 2 or later at the following locations: | ||
| 15 | * | ||
| 16 | * http://www.opensource.org/licenses/gpl-license.html | ||
| 17 | * http://www.gnu.org/copyleft/gpl.html | ||
| 18 | */ | ||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/interrupt.h> | ||
| 24 | #include <linux/rtc.h> | ||
| 25 | |||
| 26 | #include <mach/platform.h> | ||
| 27 | #include <mach/stmp3xxx.h> | ||
| 28 | #include <mach/regs-rtc.h> | ||
| 29 | |||
| 30 | struct stmp3xxx_rtc_data { | ||
| 31 | struct rtc_device *rtc; | ||
| 32 | unsigned irq_count; | ||
| 33 | void __iomem *io; | ||
| 34 | int irq_alarm, irq_1msec; | ||
| 35 | }; | ||
| 36 | |||
| 37 | static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) | ||
| 38 | { | ||
| 39 | /* | ||
| 40 | * The datasheet doesn't say which way round the | ||
| 41 | * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, | ||
| 42 | * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS | ||
| 43 | */ | ||
| 44 | while (__raw_readl(rtc_data->io + HW_RTC_STAT) & | ||
| 45 | BF(0x80, RTC_STAT_STALE_REGS)) | ||
| 46 | cpu_relax(); | ||
| 47 | } | ||
| 48 | |||
| 49 | /* Time read/write */ | ||
| 50 | static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | ||
| 51 | { | ||
| 52 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); | ||
| 53 | |||
| 54 | stmp3xxx_wait_time(rtc_data); | ||
| 55 | rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_SECONDS), rtc_tm); | ||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | |||
| 59 | static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) | ||
| 60 | { | ||
| 61 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); | ||
| 62 | |||
| 63 | __raw_writel(t, rtc_data->io + HW_RTC_SECONDS); | ||
| 64 | stmp3xxx_wait_time(rtc_data); | ||
| 65 | return 0; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* interrupt(s) handler */ | ||
| 69 | static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) | ||
| 70 | { | ||
| 71 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); | ||
| 72 | u32 status; | ||
| 73 | u32 events = 0; | ||
| 74 | |||
| 75 | status = __raw_readl(rtc_data->io + HW_RTC_CTRL) & | ||
| 76 | (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); | ||
| 77 | |||
| 78 | if (status & BM_RTC_CTRL_ALARM_IRQ) { | ||
| 79 | stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ, | ||
| 80 | rtc_data->io + HW_RTC_CTRL); | ||
| 81 | events |= RTC_AF | RTC_IRQF; | ||
| 82 | } | ||
| 83 | |||
| 84 | if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { | ||
| 85 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ, | ||
| 86 | rtc_data->io + HW_RTC_CTRL); | ||
| 87 | if (++rtc_data->irq_count % 1000 == 0) { | ||
| 88 | events |= RTC_UF | RTC_IRQF; | ||
| 89 | rtc_data->irq_count = 0; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | if (events) | ||
| 94 | rtc_update_irq(rtc_data->rtc, 1, events); | ||
| 95 | |||
| 96 | return IRQ_HANDLED; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||
| 100 | { | ||
| 101 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); | ||
| 102 | void __iomem *p = rtc_data->io + HW_RTC_PERSISTENT0, | ||
| 103 | *ctl = rtc_data->io + HW_RTC_CTRL; | ||
| 104 | |||
| 105 | if (enabled) { | ||
| 106 | stmp3xxx_setl(BM_RTC_PERSISTENT0_ALARM_EN | | ||
| 107 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); | ||
| 108 | stmp3xxx_setl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); | ||
| 109 | } else { | ||
| 110 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | | ||
| 111 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); | ||
| 112 | stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); | ||
| 113 | } | ||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | static int stmp3xxx_update_irq_enable(struct device *dev, unsigned int enabled) | ||
| 118 | { | ||
| 119 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); | ||
| 120 | |||
| 121 | if (enabled) | ||
| 122 | stmp3xxx_setl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, | ||
| 123 | rtc_data->io + HW_RTC_CTRL); | ||
| 124 | else | ||
| 125 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, | ||
| 126 | rtc_data->io + HW_RTC_CTRL); | ||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) | ||
| 131 | { | ||
| 132 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); | ||
| 133 | |||
| 134 | rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); | ||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) | ||
| 139 | { | ||
| 140 | unsigned long t; | ||
| 141 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); | ||
| 142 | |||
| 143 | rtc_tm_to_time(&alm->time, &t); | ||
| 144 | __raw_writel(t, rtc_data->io + HW_RTC_ALARM); | ||
| 145 | return 0; | ||
| 146 | } | ||
| 147 | |||
| 148 | static struct rtc_class_ops stmp3xxx_rtc_ops = { | ||
| 149 | .alarm_irq_enable = | ||
| 150 | stmp3xxx_alarm_irq_enable, | ||
| 151 | .update_irq_enable = | ||
| 152 | stmp3xxx_update_irq_enable, | ||
| 153 | .read_time = stmp3xxx_rtc_gettime, | ||
| 154 | .set_mmss = stmp3xxx_rtc_set_mmss, | ||
| 155 | .read_alarm = stmp3xxx_rtc_read_alarm, | ||
| 156 | .set_alarm = stmp3xxx_rtc_set_alarm, | ||
| 157 | }; | ||
| 158 | |||
| 159 | static int stmp3xxx_rtc_remove(struct platform_device *pdev) | ||
| 160 | { | ||
| 161 | struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); | ||
| 162 | |||
| 163 | if (!rtc_data) | ||
| 164 | return 0; | ||
| 165 | |||
| 166 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, | ||
| 167 | rtc_data->io + HW_RTC_CTRL); | ||
| 168 | free_irq(rtc_data->irq_alarm, &pdev->dev); | ||
| 169 | free_irq(rtc_data->irq_1msec, &pdev->dev); | ||
| 170 | rtc_device_unregister(rtc_data->rtc); | ||
| 171 | iounmap(rtc_data->io); | ||
| 172 | kfree(rtc_data); | ||
| 173 | |||
| 174 | return 0; | ||
| 175 | } | ||
| 176 | |||
| 177 | static int stmp3xxx_rtc_probe(struct platform_device *pdev) | ||
| 178 | { | ||
| 179 | struct stmp3xxx_rtc_data *rtc_data; | ||
| 180 | struct resource *r; | ||
| 181 | int err; | ||
| 182 | |||
| 183 | rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); | ||
| 184 | if (!rtc_data) | ||
| 185 | return -ENOMEM; | ||
| 186 | |||
| 187 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 188 | if (!r) { | ||
| 189 | dev_err(&pdev->dev, "failed to get resource\n"); | ||
| 190 | err = -ENXIO; | ||
| 191 | goto out_free; | ||
| 192 | } | ||
| 193 | |||
| 194 | rtc_data->io = ioremap(r->start, resource_size(r)); | ||
| 195 | if (!rtc_data->io) { | ||
| 196 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
| 197 | err = -EIO; | ||
| 198 | goto out_free; | ||
| 199 | } | ||
| 200 | |||
| 201 | rtc_data->irq_alarm = platform_get_irq(pdev, 0); | ||
| 202 | rtc_data->irq_1msec = platform_get_irq(pdev, 1); | ||
| 203 | |||
| 204 | if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & | ||
| 205 | BM_RTC_STAT_RTC_PRESENT)) { | ||
| 206 | dev_err(&pdev->dev, "no device onboard\n"); | ||
| 207 | err = -ENODEV; | ||
| 208 | goto out_remap; | ||
| 209 | } | ||
| 210 | |||
| 211 | stmp3xxx_reset_block(rtc_data->io, true); | ||
| 212 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | | ||
| 213 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN | | ||
| 214 | BM_RTC_PERSISTENT0_ALARM_WAKE, | ||
| 215 | rtc_data->io + HW_RTC_PERSISTENT0); | ||
| 216 | rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, | ||
| 217 | &stmp3xxx_rtc_ops, THIS_MODULE); | ||
| 218 | if (IS_ERR(rtc_data->rtc)) { | ||
| 219 | err = PTR_ERR(rtc_data->rtc); | ||
| 220 | goto out_remap; | ||
| 221 | } | ||
| 222 | |||
| 223 | rtc_data->irq_count = 0; | ||
| 224 | err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, | ||
| 225 | IRQF_DISABLED, "RTC alarm", &pdev->dev); | ||
| 226 | if (err) { | ||
| 227 | dev_err(&pdev->dev, "Cannot claim IRQ%d\n", | ||
| 228 | rtc_data->irq_alarm); | ||
| 229 | goto out_irq_alarm; | ||
| 230 | } | ||
| 231 | err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, | ||
| 232 | IRQF_DISABLED, "RTC tick", &pdev->dev); | ||
| 233 | if (err) { | ||
| 234 | dev_err(&pdev->dev, "Cannot claim IRQ%d\n", | ||
| 235 | rtc_data->irq_1msec); | ||
| 236 | goto out_irq1; | ||
| 237 | } | ||
| 238 | |||
| 239 | platform_set_drvdata(pdev, rtc_data); | ||
| 240 | |||
| 241 | return 0; | ||
| 242 | |||
| 243 | out_irq1: | ||
| 244 | free_irq(rtc_data->irq_alarm, &pdev->dev); | ||
| 245 | out_irq_alarm: | ||
| 246 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, | ||
| 247 | rtc_data->io + HW_RTC_CTRL); | ||
| 248 | rtc_device_unregister(rtc_data->rtc); | ||
| 249 | out_remap: | ||
| 250 | iounmap(rtc_data->io); | ||
| 251 | out_free: | ||
| 252 | kfree(rtc_data); | ||
| 253 | return err; | ||
| 254 | } | ||
| 255 | |||
| 256 | #ifdef CONFIG_PM | ||
| 257 | static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) | ||
| 258 | { | ||
| 259 | return 0; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int stmp3xxx_rtc_resume(struct platform_device *dev) | ||
| 263 | { | ||
| 264 | struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); | ||
| 265 | |||
| 266 | stmp3xxx_reset_block(rtc_data->io, true); | ||
| 267 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | | ||
| 268 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN | | ||
| 269 | BM_RTC_PERSISTENT0_ALARM_WAKE, | ||
| 270 | rtc_data->io + HW_RTC_PERSISTENT0); | ||
| 271 | return 0; | ||
| 272 | } | ||
| 273 | #else | ||
| 274 | #define stmp3xxx_rtc_suspend NULL | ||
| 275 | #define stmp3xxx_rtc_resume NULL | ||
| 276 | #endif | ||
| 277 | |||
| 278 | static struct platform_driver stmp3xxx_rtcdrv = { | ||
| 279 | .probe = stmp3xxx_rtc_probe, | ||
| 280 | .remove = stmp3xxx_rtc_remove, | ||
| 281 | .suspend = stmp3xxx_rtc_suspend, | ||
| 282 | .resume = stmp3xxx_rtc_resume, | ||
| 283 | .driver = { | ||
| 284 | .name = "stmp3xxx-rtc", | ||
| 285 | .owner = THIS_MODULE, | ||
| 286 | }, | ||
| 287 | }; | ||
| 288 | |||
| 289 | static int __init stmp3xxx_rtc_init(void) | ||
| 290 | { | ||
| 291 | return platform_driver_register(&stmp3xxx_rtcdrv); | ||
| 292 | } | ||
| 293 | |||
| 294 | static void __exit stmp3xxx_rtc_exit(void) | ||
| 295 | { | ||
| 296 | platform_driver_unregister(&stmp3xxx_rtcdrv); | ||
| 297 | } | ||
| 298 | |||
| 299 | module_init(stmp3xxx_rtc_init); | ||
| 300 | module_exit(stmp3xxx_rtc_exit); | ||
| 301 | |||
| 302 | MODULE_DESCRIPTION("STMP3xxx RTC Driver"); | ||
| 303 | MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); | ||
| 304 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 2531ce4c9db0..7dd23a6fc825 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c | |||
| @@ -102,6 +102,19 @@ rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr, | |||
| 102 | return n; | 102 | return n; |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | static ssize_t | ||
| 106 | rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr, | ||
| 107 | char *buf) | ||
| 108 | { | ||
| 109 | #ifdef CONFIG_RTC_HCTOSYS_DEVICE | ||
| 110 | if (strcmp(dev_name(&to_rtc_device(dev)->dev), | ||
| 111 | CONFIG_RTC_HCTOSYS_DEVICE) == 0) | ||
| 112 | return sprintf(buf, "1\n"); | ||
| 113 | else | ||
| 114 | #endif | ||
| 115 | return sprintf(buf, "0\n"); | ||
| 116 | } | ||
| 117 | |||
| 105 | static struct device_attribute rtc_attrs[] = { | 118 | static struct device_attribute rtc_attrs[] = { |
| 106 | __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), | 119 | __ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL), |
| 107 | __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), | 120 | __ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL), |
| @@ -109,6 +122,7 @@ static struct device_attribute rtc_attrs[] = { | |||
| 109 | __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), | 122 | __ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL), |
| 110 | __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, | 123 | __ATTR(max_user_freq, S_IRUGO | S_IWUSR, rtc_sysfs_show_max_user_freq, |
| 111 | rtc_sysfs_set_max_user_freq), | 124 | rtc_sysfs_set_max_user_freq), |
| 125 | __ATTR(hctosys, S_IRUGO, rtc_sysfs_show_hctosys, NULL), | ||
| 112 | { }, | 126 | { }, |
| 113 | }; | 127 | }; |
| 114 | 128 | ||
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c index ad164056feb6..423cd5a30b10 100644 --- a/drivers/rtc/rtc-v3020.c +++ b/drivers/rtc/rtc-v3020.c | |||
| @@ -96,7 +96,7 @@ static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit) | |||
| 96 | 96 | ||
| 97 | static unsigned char v3020_mmio_read_bit(struct v3020 *chip) | 97 | static unsigned char v3020_mmio_read_bit(struct v3020 *chip) |
| 98 | { | 98 | { |
| 99 | return readl(chip->ioaddress) & (1 << chip->leftshift); | 99 | return !!(readl(chip->ioaddress) & (1 << chip->leftshift)); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | static struct v3020_chip_ops v3020_mmio_ops = { | 102 | static struct v3020_chip_ops v3020_mmio_ops = { |
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c index 2c839d0d21bd..fadddac1e5a4 100644 --- a/drivers/rtc/rtc-vr41xx.c +++ b/drivers/rtc/rtc-vr41xx.c | |||
| @@ -209,19 +209,18 @@ static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) | |||
| 209 | 209 | ||
| 210 | static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq) | 210 | static int vr41xx_rtc_irq_set_freq(struct device *dev, int freq) |
| 211 | { | 211 | { |
| 212 | unsigned long count; | 212 | u64 count; |
| 213 | 213 | ||
| 214 | if (!is_power_of_2(freq)) | 214 | if (!is_power_of_2(freq)) |
| 215 | return -EINVAL; | 215 | return -EINVAL; |
| 216 | count = RTC_FREQUENCY; | 216 | count = RTC_FREQUENCY; |
| 217 | do_div(count, freq); | 217 | do_div(count, freq); |
| 218 | 218 | ||
| 219 | periodic_count = count; | ||
| 220 | |||
| 221 | spin_lock_irq(&rtc_lock); | 219 | spin_lock_irq(&rtc_lock); |
| 222 | 220 | ||
| 223 | rtc1_write(RTCL1LREG, count); | 221 | periodic_count = count; |
| 224 | rtc1_write(RTCL1HREG, count >> 16); | 222 | rtc1_write(RTCL1LREG, periodic_count); |
| 223 | rtc1_write(RTCL1HREG, periodic_count >> 16); | ||
| 225 | 224 | ||
| 226 | spin_unlock_irq(&rtc_lock); | 225 | spin_unlock_irq(&rtc_lock); |
| 227 | 226 | ||
