summaryrefslogtreecommitdiff
path: root/drivers/rtc
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-08-05 17:04:01 -0300
commit57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch)
tree5e910f0e82173f4ef4f51111366a3f1299037a7b /drivers/rtc
Initial import
Diffstat (limited to 'drivers/rtc')
-rw-r--r--drivers/rtc/Kconfig1559
-rw-r--r--drivers/rtc/Makefile157
-rw-r--r--drivers/rtc/class.c382
-rw-r--r--drivers/rtc/hctosys.c70
-rw-r--r--drivers/rtc/interface.c989
-rw-r--r--drivers/rtc/rtc-88pm80x.c366
-rw-r--r--drivers/rtc/rtc-88pm860x.c461
-rw-r--r--drivers/rtc/rtc-ab-b5ze-s3.c1035
-rw-r--r--drivers/rtc/rtc-ab3100.c252
-rw-r--r--drivers/rtc/rtc-ab8500.c532
-rw-r--r--drivers/rtc/rtc-abx80x.c307
-rw-r--r--drivers/rtc/rtc-armada38x.c320
-rw-r--r--drivers/rtc/rtc-as3722.c261
-rw-r--r--drivers/rtc/rtc-at32ap700x.c287
-rw-r--r--drivers/rtc/rtc-at91rm9200.c538
-rw-r--r--drivers/rtc/rtc-at91rm9200.h75
-rw-r--r--drivers/rtc/rtc-at91sam9.c586
-rw-r--r--drivers/rtc/rtc-au1xxx.c128
-rw-r--r--drivers/rtc/rtc-bfin.c448
-rw-r--r--drivers/rtc/rtc-bq32k.c247
-rw-r--r--drivers/rtc/rtc-bq4802.c199
-rw-r--r--drivers/rtc/rtc-cmos.c1273
-rw-r--r--drivers/rtc/rtc-coh901331.c281
-rw-r--r--drivers/rtc/rtc-core.h70
-rw-r--r--drivers/rtc/rtc-da9052.c331
-rw-r--r--drivers/rtc/rtc-da9055.c403
-rw-r--r--drivers/rtc/rtc-da9063.c352
-rw-r--r--drivers/rtc/rtc-davinci.c560
-rw-r--r--drivers/rtc/rtc-dev.c511
-rw-r--r--drivers/rtc/rtc-digicolor.c227
-rw-r--r--drivers/rtc/rtc-dm355evm.c155
-rw-r--r--drivers/rtc/rtc-ds1216.c182
-rw-r--r--drivers/rtc/rtc-ds1286.c369
-rw-r--r--drivers/rtc/rtc-ds1302.c255
-rw-r--r--drivers/rtc/rtc-ds1305.c803
-rw-r--r--drivers/rtc/rtc-ds1307.c1258
-rw-r--r--drivers/rtc/rtc-ds1343.c760
-rw-r--r--drivers/rtc/rtc-ds1347.c166
-rw-r--r--drivers/rtc/rtc-ds1374.c724
-rw-r--r--drivers/rtc/rtc-ds1390.c169
-rw-r--r--drivers/rtc/rtc-ds1511.c572
-rw-r--r--drivers/rtc/rtc-ds1553.c362
-rw-r--r--drivers/rtc/rtc-ds1672.c215
-rw-r--r--drivers/rtc/rtc-ds1685.c2251
-rw-r--r--drivers/rtc/rtc-ds1742.c243
-rw-r--r--drivers/rtc/rtc-ds2404.c284
-rw-r--r--drivers/rtc/rtc-ds3232.c515
-rw-r--r--drivers/rtc/rtc-ds3234.c172
-rw-r--r--drivers/rtc/rtc-efi-platform.c34
-rw-r--r--drivers/rtc/rtc-efi.c239
-rw-r--r--drivers/rtc/rtc-em3027.c162
-rw-r--r--drivers/rtc/rtc-ep93xx.c188
-rw-r--r--drivers/rtc/rtc-fm3130.c537
-rw-r--r--drivers/rtc/rtc-generic.c62
-rw-r--r--drivers/rtc/rtc-hid-sensor-time.c342
-rw-r--r--drivers/rtc/rtc-hym8563.c612
-rw-r--r--drivers/rtc/rtc-imxdi.c548
-rw-r--r--drivers/rtc/rtc-isl12022.c306
-rw-r--r--drivers/rtc/rtc-isl12057.c674
-rw-r--r--drivers/rtc/rtc-isl1208.c716
-rw-r--r--drivers/rtc/rtc-jz4740.c311
-rw-r--r--drivers/rtc/rtc-lib.c152
-rw-r--r--drivers/rtc/rtc-lp8788.c326
-rw-r--r--drivers/rtc/rtc-lpc32xx.c392
-rw-r--r--drivers/rtc/rtc-ls1x.c198
-rw-r--r--drivers/rtc/rtc-m41t80.c741
-rw-r--r--drivers/rtc/rtc-m41t93.c210
-rw-r--r--drivers/rtc/rtc-m41t94.c150
-rw-r--r--drivers/rtc/rtc-m48t35.c194
-rw-r--r--drivers/rtc/rtc-m48t59.c516
-rw-r--r--drivers/rtc/rtc-m48t86.c182
-rw-r--r--drivers/rtc/rtc-max6900.c251
-rw-r--r--drivers/rtc/rtc-max6902.c159
-rw-r--r--drivers/rtc/rtc-max77686.c528
-rw-r--r--drivers/rtc/rtc-max77802.c501
-rw-r--r--drivers/rtc/rtc-max8907.c224
-rw-r--r--drivers/rtc/rtc-max8925.c327
-rw-r--r--drivers/rtc/rtc-max8997.c538
-rw-r--r--drivers/rtc/rtc-max8998.c326
-rw-r--r--drivers/rtc/rtc-mc13xxx.c376
-rw-r--r--drivers/rtc/rtc-mcp795.c199
-rw-r--r--drivers/rtc/rtc-moxart.c327
-rw-r--r--drivers/rtc/rtc-mpc5121.c423
-rw-r--r--drivers/rtc/rtc-mrst.c545
-rw-r--r--drivers/rtc/rtc-msm6242.c238
-rw-r--r--drivers/rtc/rtc-mv.c341
-rw-r--r--drivers/rtc/rtc-mxc.c488
-rw-r--r--drivers/rtc/rtc-nuc900.c273
-rw-r--r--drivers/rtc/rtc-omap.c774
-rw-r--r--drivers/rtc/rtc-opal.c262
-rw-r--r--drivers/rtc/rtc-palmas.c377
-rw-r--r--drivers/rtc/rtc-pcap.c194
-rw-r--r--drivers/rtc/rtc-pcf2123.c367
-rw-r--r--drivers/rtc/rtc-pcf2127.c232
-rw-r--r--drivers/rtc/rtc-pcf50633.c295
-rw-r--r--drivers/rtc/rtc-pcf85063.c204
-rw-r--r--drivers/rtc/rtc-pcf8523.c347
-rw-r--r--drivers/rtc/rtc-pcf8563.c503
-rw-r--r--drivers/rtc/rtc-pcf8583.c322
-rw-r--r--drivers/rtc/rtc-pl030.c187
-rw-r--r--drivers/rtc/rtc-pl031.c481
-rw-r--r--drivers/rtc/rtc-pm8xxx.c544
-rw-r--r--drivers/rtc/rtc-proc.c148
-rw-r--r--drivers/rtc/rtc-ps3.c85
-rw-r--r--drivers/rtc/rtc-puv3.c339
-rw-r--r--drivers/rtc/rtc-pxa.c435
-rw-r--r--drivers/rtc/rtc-r9701.c186
-rw-r--r--drivers/rtc/rtc-rc5t583.c321
-rw-r--r--drivers/rtc/rtc-rk808.c420
-rw-r--r--drivers/rtc/rtc-rp5c01.c285
-rw-r--r--drivers/rtc/rtc-rs5c313.c414
-rw-r--r--drivers/rtc/rtc-rs5c348.c235
-rw-r--r--drivers/rtc/rtc-rs5c372.c699
-rw-r--r--drivers/rtc/rtc-rv3029c2.c427
-rw-r--r--drivers/rtc/rtc-rx4581.c305
-rw-r--r--drivers/rtc/rtc-rx8025.c642
-rw-r--r--drivers/rtc/rtc-rx8581.c329
-rw-r--r--drivers/rtc/rtc-s35390a.c434
-rw-r--r--drivers/rtc/rtc-s3c.c822
-rw-r--r--drivers/rtc/rtc-s3c.h70
-rw-r--r--drivers/rtc/rtc-s5m.c817
-rw-r--r--drivers/rtc/rtc-sa1100.c364
-rw-r--r--drivers/rtc/rtc-sh.c798
-rw-r--r--drivers/rtc/rtc-sirfsoc.c458
-rw-r--r--drivers/rtc/rtc-snvs.c391
-rw-r--r--drivers/rtc/rtc-spear.c509
-rw-r--r--drivers/rtc/rtc-starfire.c60
-rw-r--r--drivers/rtc/rtc-stk17ta8.c369
-rw-r--r--drivers/rtc/rtc-stmp3xxx.c408
-rw-r--r--drivers/rtc/rtc-sun4v.c105
-rw-r--r--drivers/rtc/rtc-sun6i.c447
-rw-r--r--drivers/rtc/rtc-sunxi.c522
-rw-r--r--drivers/rtc/rtc-sysfs.c265
-rw-r--r--drivers/rtc/rtc-tegra.c434
-rw-r--r--drivers/rtc/rtc-test.c201
-rw-r--r--drivers/rtc/rtc-tile.c146
-rw-r--r--drivers/rtc/rtc-tps6586x.c350
-rw-r--r--drivers/rtc/rtc-tps65910.c337
-rw-r--r--drivers/rtc/rtc-tps80031.c337
-rw-r--r--drivers/rtc/rtc-twl.c621
-rw-r--r--drivers/rtc/rtc-tx4939.c298
-rw-r--r--drivers/rtc/rtc-v3020.c393
-rw-r--r--drivers/rtc/rtc-vr41xx.c381
-rw-r--r--drivers/rtc/rtc-vt8500.c289
-rw-r--r--drivers/rtc/rtc-wm831x.c487
-rw-r--r--drivers/rtc/rtc-wm8350.c485
-rw-r--r--drivers/rtc/rtc-x1205.c696
-rw-r--r--drivers/rtc/rtc-xgene.c277
-rw-r--r--drivers/rtc/systohc.c47
149 files changed, 59495 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
new file mode 100644
index 000000000..0fe4ad882
--- /dev/null
+++ b/drivers/rtc/Kconfig
@@ -0,0 +1,1559 @@
+#
+# RTC class/drivers configuration
+#
+
+config RTC_LIB
+ bool
+
+menuconfig RTC_CLASS
+ bool "Real Time Clock"
+ default n
+ depends on !S390 && !UML
+ select RTC_LIB
+ help
+ Generic RTC class support. If you say yes here, you will
+ be allowed to plug one or more RTCs to your system. You will
+ probably want to enable one or more of the interfaces below.
+
+if RTC_CLASS
+
+config RTC_HCTOSYS
+ bool "Set system time from RTC on startup and resume"
+ default y
+ help
+ If you say yes here, the system time (wall clock) will be set using
+ the value read from a specified RTC device. This is useful to avoid
+ unnecessary fsck runs at boot time, and to network better.
+
+config RTC_SYSTOHC
+ bool "Set the RTC time based on NTP synchronization"
+ default y
+ help
+ If you say yes here, the system time (wall clock) will be stored
+ in the RTC specified by RTC_HCTOSYS_DEVICE approximately every 11
+ minutes if userspace reports synchronized NTP status.
+
+config RTC_HCTOSYS_DEVICE
+ string "RTC used to set the system time"
+ depends on RTC_HCTOSYS = y || RTC_SYSTOHC = y
+ default "rtc0"
+ help
+ The RTC device that will be used to (re)initialize the system
+ clock, usually rtc0. Initialization is done when the system
+ starts up, and when it resumes from a low power state. This
+ device should record time in UTC, since the kernel won't do
+ timezone correction.
+
+ The driver for this RTC device must be loaded before late_initcall
+ functions run, so it must usually be statically linked.
+
+ This clock should be battery-backed, so that it reads the correct
+ time when the system boots from a power-off state. Otherwise, your
+ system will need an external clock source (like an NTP server).
+
+ If the clock you specify here is not battery backed, it may still
+ be useful to reinitialize system time when resuming from system
+ sleep states. Do not specify an RTC here unless it stays powered
+ during all this system's supported sleep states.
+
+config RTC_DEBUG
+ bool "RTC debug support"
+ help
+ Say yes here to enable debugging support in the RTC framework
+ and individual RTC drivers.
+
+comment "RTC interfaces"
+
+config RTC_INTF_SYSFS
+ bool "/sys/class/rtc/rtcN (sysfs)"
+ depends on SYSFS
+ default RTC_CLASS
+ help
+ Say yes here if you want to use your RTCs using sysfs interfaces,
+ /sys/class/rtc/rtc0 through /sys/.../rtcN.
+
+ If unsure, say Y.
+
+config RTC_INTF_PROC
+ bool "/proc/driver/rtc (procfs for rtcN)"
+ depends on PROC_FS
+ default RTC_CLASS
+ help
+ Say yes here if you want to use your system clock RTC through
+ the proc interface, /proc/driver/rtc.
+ Other RTCs will not be available through that API.
+ If there is no RTC for the system clock, then the first RTC(rtc0)
+ is used by default.
+
+ If unsure, say Y.
+
+config RTC_INTF_DEV
+ bool "/dev/rtcN (character devices)"
+ default RTC_CLASS
+ help
+ Say yes here if you want to use your RTCs using the /dev
+ interfaces, which "udev" sets up as /dev/rtc0 through
+ /dev/rtcN.
+
+ You may want to set up a symbolic link so one of these
+ can be accessed as /dev/rtc, which is a name
+ expected by "hwclock" and some other programs. Recent
+ versions of "udev" are known to set up the symlink for you.
+
+ If unsure, say Y.
+
+config RTC_INTF_DEV_UIE_EMUL
+ bool "RTC UIE emulation on dev interface"
+ depends on RTC_INTF_DEV
+ help
+ Provides an emulation for RTC_UIE if the underlying rtc chip
+ driver does not expose RTC_UIE ioctls. Those requests generate
+ once-per-second update interrupts, used for synchronization.
+
+ The emulation code will read the time from the hardware
+ clock several times per second, please enable this option
+ only if you know that you really need it.
+
+config RTC_DRV_TEST
+ tristate "Test driver/device"
+ help
+ If you say yes here you get support for the
+ RTC test driver. It's a software RTC which can be
+ used to test the RTC subsystem APIs. It gets
+ the time from the system clock.
+ You want this driver only if you are doing development
+ on the RTC subsystem. Please read the source code
+ for further details.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-test.
+
+comment "I2C RTC drivers"
+ depends on I2C
+
+if I2C
+
+config RTC_DRV_88PM860X
+ tristate "Marvell 88PM860x"
+ depends on I2C && MFD_88PM860X
+ help
+ If you say yes here you get support for RTC function in Marvell
+ 88PM860x chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-88pm860x.
+
+config RTC_DRV_88PM80X
+ tristate "Marvell 88PM80x"
+ depends on I2C && MFD_88PM800
+ help
+ If you say yes here you get support for RTC function in Marvell
+ 88PM80x chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-88pm80x.
+
+config RTC_DRV_ABB5ZES3
+ depends on I2C
+ select REGMAP_I2C
+ tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3"
+ help
+ If you say yes here you get support for the Abracon
+ AB-RTCMC-32.768kHz-B5ZE-S3 I2C RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ab-b5ze-s3.
+
+config RTC_DRV_ABX80X
+ tristate "Abracon ABx80x"
+ help
+ If you say yes here you get support for Abracon AB080X and AB180X
+ families of ultra-low-power battery- and capacitor-backed real-time
+ clock chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-abx80x.
+
+config RTC_DRV_AS3722
+ tristate "ams AS3722 RTC driver"
+ depends on MFD_AS3722
+ help
+ If you say yes here you get support for the RTC of ams AS3722 PMIC
+ chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-as3722.
+
+config RTC_DRV_DS1307
+ tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00, EPSON RX-8025"
+ help
+ If you say yes here you get support for various compatible RTC
+ chips (often with battery backup) connected with I2C. This driver
+ should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00,
+ EPSON RX-8025 and probably other chips. In some cases the RTC
+ must already have been initialized (by manufacturing or a
+ bootloader).
+
+ The first seven registers on these chips hold an RTC, and other
+ registers may add features such as NVRAM, a trickle charger for
+ the RTC/NVRAM backup power, and alarms. NVRAM is visible in
+ sysfs, but other chip features may not be available.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1307.
+
+config RTC_DRV_DS1374
+ tristate "Dallas/Maxim DS1374"
+ depends on I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS1374 real-time clock chips. If an interrupt is associated
+ with the device, the alarm functionality is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1374.
+
+config RTC_DRV_DS1374_WDT
+ bool "Dallas/Maxim DS1374 watchdog timer"
+ depends on RTC_DRV_DS1374
+ help
+ If you say Y here you will get support for the
+ watchdog timer in the Dallas Semiconductor DS1374
+ real-time clock chips.
+
+config RTC_DRV_DS1672
+ tristate "Dallas/Maxim DS1672"
+ help
+ If you say yes here you get support for the
+ Dallas/Maxim DS1672 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1672.
+
+config RTC_DRV_DS3232
+ tristate "Dallas/Maxim DS3232"
+ depends on I2C
+ help
+ If you say yes here you get support for Dallas Semiconductor
+ DS3232 real-time clock chips. If an interrupt is associated
+ with the device, the alarm functionality is supported.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3232.
+
+config RTC_DRV_HYM8563
+ tristate "Haoyu Microelectronics HYM8563"
+ depends on I2C && OF
+ help
+ Say Y to enable support for the HYM8563 I2C RTC chip. Apart
+ from the usual rtc functions it provides a clock output of
+ up to 32kHz.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-hym8563.
+
+config RTC_DRV_LP8788
+ tristate "TI LP8788 RTC driver"
+ depends on MFD_LP8788
+ help
+ Say Y to enable support for the LP8788 RTC/ALARM driver.
+
+config RTC_DRV_MAX6900
+ tristate "Maxim MAX6900"
+ help
+ If you say yes here you will get support for the
+ Maxim MAX6900 I2C RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max6900.
+
+config RTC_DRV_MAX8907
+ tristate "Maxim MAX8907"
+ depends on MFD_MAX8907
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8907 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8907.
+
+config RTC_DRV_MAX8925
+ tristate "Maxim MAX8925"
+ depends on MFD_MAX8925
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8925 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8925.
+
+config RTC_DRV_MAX8998
+ tristate "Maxim MAX8998"
+ depends on MFD_MAX8998
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8998 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8998.
+
+config RTC_DRV_MAX8997
+ tristate "Maxim MAX8997"
+ depends on MFD_MAX8997
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX8997 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max8997.
+
+config RTC_DRV_MAX77686
+ tristate "Maxim MAX77686"
+ depends on MFD_MAX77686
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77686 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77686.
+
+config RTC_DRV_RK808
+ tristate "Rockchip RK808 RTC"
+ depends on MFD_RK808
+ help
+ If you say yes here you will get support for the
+ RTC of RK808 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rk808-rtc.
+
+config RTC_DRV_MAX77802
+ tristate "Maxim 77802 RTC"
+ depends on MFD_MAX77686
+ help
+ If you say yes here you will get support for the
+ RTC of Maxim MAX77802 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max77802.
+
+config RTC_DRV_RS5C372
+ tristate "Ricoh R2025S/D, RS5C372A/B, RV5C386, RV5C387A"
+ help
+ If you say yes here you get support for the
+ Ricoh R2025S/D, RS5C372A, RS5C372B, RV5C386, and RV5C387A RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rs5c372.
+
+config RTC_DRV_ISL1208
+ tristate "Intersil ISL1208"
+ help
+ If you say yes here you get support for the
+ Intersil ISL1208 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl1208.
+
+config RTC_DRV_ISL12022
+ tristate "Intersil ISL12022"
+ help
+ If you say yes here you get support for the
+ Intersil ISL12022 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12022.
+
+config RTC_DRV_ISL12057
+ depends on I2C
+ select REGMAP_I2C
+ tristate "Intersil ISL12057"
+ help
+ If you say yes here you get support for the Intersil ISL12057
+ I2C RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-isl12057.
+
+config RTC_DRV_X1205
+ tristate "Xicor/Intersil X1205"
+ help
+ If you say yes here you get support for the
+ Xicor/Intersil X1205 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-x1205.
+
+config RTC_DRV_PALMAS
+ tristate "TI Palmas RTC driver"
+ depends on MFD_PALMAS
+ help
+ If you say yes here you get support for the RTC of TI PALMA series PMIC
+ chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-palma.
+
+config RTC_DRV_PCF2127
+ tristate "NXP PCF2127"
+ help
+ If you say yes here you get support for the NXP PCF2127/29 RTC
+ chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf2127.
+
+config RTC_DRV_PCF8523
+ tristate "NXP PCF8523"
+ help
+ If you say yes here you get support for the NXP PCF8523 RTC
+ chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf8523.
+
+config RTC_DRV_PCF8563
+ tristate "Philips PCF8563/Epson RTC8564"
+ help
+ If you say yes here you get support for the
+ Philips PCF8563 RTC chip. The Epson RTC8564
+ should work as well.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf8563.
+
+config RTC_DRV_PCF85063
+ tristate "nxp PCF85063"
+ help
+ If you say yes here you get support for the PCF85063 RTC chip
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf85063.
+
+config RTC_DRV_PCF8583
+ tristate "Philips PCF8583"
+ help
+ If you say yes here you get support for the Philips PCF8583
+ RTC chip found on Acorn RiscPCs. This driver supports the
+ platform specific method of retrieving the current year from
+ the RTC's SRAM. It will work on other platforms with the same
+ chip, but the year will probably have to be tweaked.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf8583.
+
+config RTC_DRV_M41T80
+ tristate "ST M41T62/65/M41T80/81/82/83/84/85/87 and compatible"
+ help
+ If you say Y here you will get support for the ST M41T60
+ and M41T80 RTC chips series. Currently, the following chips are
+ supported: M41T62, M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84,
+ M41ST85, M41ST87, and MicroCrystal RV4162.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m41t80.
+
+config RTC_DRV_M41T80_WDT
+ bool "ST M41T65/M41T80 series RTC watchdog timer"
+ depends on RTC_DRV_M41T80
+ help
+ If you say Y here you will get support for the
+ watchdog timer in the ST M41T60 and M41T80 RTC chips series.
+
+config RTC_DRV_BQ32K
+ tristate "TI BQ32000"
+ help
+ If you say Y here you will get support for the TI
+ BQ32000 I2C RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bq32k.
+
+config RTC_DRV_DM355EVM
+ tristate "TI DaVinci DM355 EVM RTC"
+ depends on MFD_DM355EVM_MSP
+ help
+ Supports the RTC firmware in the MSP430 on the DM355 EVM.
+
+config RTC_DRV_TWL92330
+ bool "TI TWL92330/Menelaus"
+ depends on MENELAUS
+ help
+ If you say yes here you get support for the RTC on the
+ TWL92330 "Menelaus" power management chip, used with OMAP2
+ platforms. The support is integrated with the rest of
+ the Menelaus driver; it's not separate module.
+
+config RTC_DRV_TWL4030
+ tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
+ depends on TWL4030_CORE
+ help
+ If you say yes here you get support for the RTC on the
+ TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-twl.
+
+config RTC_DRV_TPS6586X
+ tristate "TI TPS6586X RTC driver"
+ depends on MFD_TPS6586X
+ help
+ TI Power Management IC TPS6586X supports RTC functionality
+ along with alarm. This driver supports the RTC driver for
+ the TPS6586X RTC module.
+
+config RTC_DRV_TPS65910
+ tristate "TI TPS65910 RTC driver"
+ depends on RTC_CLASS && MFD_TPS65910
+ help
+ If you say yes here you get support for the RTC on the
+ TPS65910 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-tps65910.
+
+config RTC_DRV_TPS80031
+ tristate "TI TPS80031/TPS80032 RTC driver"
+ depends on MFD_TPS80031
+ help
+ TI Power Management IC TPS80031 supports RTC functionality
+ along with alarm. This driver supports the RTC driver for
+ the TPS80031 RTC module.
+
+config RTC_DRV_RC5T583
+ tristate "RICOH 5T583 RTC driver"
+ depends on MFD_RC5T583
+ help
+ If you say yes here you get support for the RTC on the
+ RICOH 5T583 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rc5t583.
+
+config RTC_DRV_S35390A
+ tristate "Seiko Instruments S-35390A"
+ select BITREVERSE
+ help
+ If you say yes here you will get support for the Seiko
+ Instruments S-35390A.
+
+ This driver can also be built as a module. If so the module
+ will be called rtc-s35390a.
+
+config RTC_DRV_FM3130
+ tristate "Ramtron FM3130"
+ help
+ If you say Y here you will get support for the
+ Ramtron FM3130 RTC chips.
+ Ramtron FM3130 is a chip with two separate devices inside,
+ RTC clock and FRAM. This driver provides only RTC functionality.
+
+ This driver can also be built as a module. If so the module
+ will be called rtc-fm3130.
+
+config RTC_DRV_RX8581
+ tristate "Epson RX-8581"
+ help
+ If you say yes here you will get support for the Epson RX-8581.
+
+ This driver can also be built as a module. If so the module
+ will be called rtc-rx8581.
+
+config RTC_DRV_RX8025
+ tristate "Epson RX-8025SA/NB"
+ help
+ If you say yes here you get support for the Epson
+ RX-8025SA/NB RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rx8025.
+
+config RTC_DRV_EM3027
+ tristate "EM Microelectronic EM3027"
+ help
+ If you say yes here you get support for the EM
+ Microelectronic EM3027 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-em3027.
+
+config RTC_DRV_RV3029C2
+ tristate "Micro Crystal RTC"
+ help
+ If you say yes here you get support for the Micro Crystal
+ RV3029-C2 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rv3029c2.
+
+config RTC_DRV_S5M
+ tristate "Samsung S2M/S5M series"
+ depends on MFD_SEC_CORE
+ help
+ If you say yes here you will get support for the
+ RTC of Samsung S2MPS14 and S5M PMIC series.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-s5m.
+
+endif # I2C
+
+comment "SPI RTC drivers"
+
+if SPI_MASTER
+
+config RTC_DRV_M41T93
+ tristate "ST M41T93"
+ help
+ If you say yes here you will get support for the
+ ST M41T93 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m41t93.
+
+config RTC_DRV_M41T94
+ tristate "ST M41T94"
+ help
+ If you say yes here you will get support for the
+ ST M41T94 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m41t94.
+
+config RTC_DRV_DS1305
+ tristate "Dallas/Maxim DS1305/DS1306"
+ help
+ Select this driver to get support for the Dallas/Maxim DS1305
+ and DS1306 real time clock chips. These support a trickle
+ charger, alarms, and NVRAM in addition to the clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1305.
+
+config RTC_DRV_DS1343
+ select REGMAP_SPI
+ tristate "Dallas/Maxim DS1343/DS1344"
+ help
+ If you say yes here you get support for the
+ Dallas/Maxim DS1343 and DS1344 real time clock chips.
+ Support for trickle charger, alarm is provided.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1343.
+
+config RTC_DRV_DS1347
+ tristate "Dallas/Maxim DS1347"
+ help
+ If you say yes here you get support for the
+ Dallas/Maxim DS1347 chips.
+
+ This driver only supports the RTC feature, and not other chip
+ features such as alarms.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1347.
+
+config RTC_DRV_DS1390
+ tristate "Dallas/Maxim DS1390/93/94"
+ help
+ If you say yes here you get support for the
+ Dallas/Maxim DS1390/93/94 chips.
+
+ This driver only supports the RTC feature, and not other chip
+ features such as alarms and trickle charging.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1390.
+
+config RTC_DRV_MAX6902
+ tristate "Maxim MAX6902"
+ help
+ If you say yes here you will get support for the
+ Maxim MAX6902 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-max6902.
+
+config RTC_DRV_R9701
+ tristate "Epson RTC-9701JE"
+ help
+ If you say yes here you will get support for the
+ Epson RTC-9701JE SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-r9701.
+
+config RTC_DRV_RS5C348
+ tristate "Ricoh RS5C348A/B"
+ help
+ If you say yes here you get support for the
+ Ricoh RS5C348A and RS5C348B RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rs5c348.
+
+config RTC_DRV_DS3234
+ tristate "Maxim/Dallas DS3234"
+ help
+ If you say yes here you get support for the
+ Maxim/Dallas DS3234 SPI RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds3234.
+
+config RTC_DRV_PCF2123
+ tristate "NXP PCF2123"
+ help
+ If you say yes here you get support for the NXP PCF2123
+ RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pcf2123.
+
+config RTC_DRV_RX4581
+ tristate "Epson RX-4581"
+ help
+ If you say yes here you will get support for the Epson RX-4581.
+
+ This driver can also be built as a module. If so the module
+ will be called rtc-rx4581.
+
+config RTC_DRV_MCP795
+ tristate "Microchip MCP795"
+ help
+ If you say yes here you will get support for the Microchip MCP795.
+
+ This driver can also be built as a module. If so the module
+ will be called rtc-mcp795.
+
+endif # SPI_MASTER
+
+comment "Platform RTC drivers"
+
+# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
+# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
+# global rtc_lock ... it's not yet just another platform_device.
+
+config RTC_DRV_CMOS
+ tristate "PC-style 'CMOS'"
+ depends on X86 || ARM || M32R || PPC || MIPS || SPARC64
+ default y if X86
+ help
+ Say "yes" here to get direct support for the real time clock
+ found in every PC or ACPI-based system, and some other boards.
+ Specifically the original MC146818, compatibles like those in
+ PC south bridges, the DS12887 or M48T86, some multifunction
+ or LPC bus chips, and so on.
+
+ Your system will need to define the platform device used by
+ this driver, otherwise it won't be accessible. This means
+ you can safely enable this driver if you don't know whether
+ or not your board has this kind of hardware.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-cmos.
+
+config RTC_DRV_ALPHA
+ bool "Alpha PC-style CMOS"
+ depends on ALPHA
+ default y
+ help
+ Direct support for the real-time clock found on every Alpha
+ system, specifically MC146818 compatibles. If in doubt, say Y.
+
+config RTC_DRV_VRTC
+ tristate "Virtual RTC for Intel MID platforms"
+ depends on X86_INTEL_MID
+ default y if X86_INTEL_MID
+
+ help
+ Say "yes" here to get direct support for the real time clock
+ found on Moorestown platforms. The VRTC is a emulated RTC that
+ derives its clock source from a real RTC in the PMIC. The MC146818
+ style programming interface is mostly conserved, but any
+ updates are done via IPC calls to the system controller FW.
+
+config RTC_DRV_DS1216
+ tristate "Dallas DS1216"
+ depends on SNI_RM
+ help
+ If you say yes here you get support for the Dallas DS1216 RTC chips.
+
+config RTC_DRV_DS1286
+ tristate "Dallas DS1286"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the Dallas DS1286 RTC chips.
+
+config RTC_DRV_DS1302
+ tristate "Dallas DS1302"
+ depends on SH_SECUREEDGE5410
+ help
+ If you say yes here you get support for the Dallas DS1302 RTC chips.
+
+config RTC_DRV_DS1511
+ tristate "Dallas DS1511"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the
+ Dallas DS1511 timekeeping/watchdog chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1511.
+
+config RTC_DRV_DS1553
+ tristate "Maxim/Dallas DS1553"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the
+ Maxim/Dallas DS1553 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1553.
+
+config RTC_DRV_DS1685_FAMILY
+ tristate "Dallas/Maxim DS1685 Family"
+ help
+ If you say yes here you get support for the Dallas/Maxim DS1685
+ family of real time chips. This family includes the DS1685/DS1687,
+ DS1689/DS1693, DS17285/DS17287, DS17485/DS17487, and
+ DS17885/DS17887 chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1685.
+
+choice
+ prompt "Subtype"
+ depends on RTC_DRV_DS1685_FAMILY
+ default RTC_DRV_DS1685
+
+config RTC_DRV_DS1685
+ bool "DS1685/DS1687"
+ help
+ This enables support for the Dallas/Maxim DS1685/DS1687 real time
+ clock chip.
+
+ This chip is commonly found in SGI O2 (IP32) and SGI Octane (IP30)
+ systems, as well as EPPC-405-UC modules by electronic system design
+ GmbH.
+
+config RTC_DRV_DS1689
+ bool "DS1689/DS1693"
+ help
+ This enables support for the Dallas/Maxim DS1689/DS1693 real time
+ clock chip.
+
+ This is an older RTC chip, supplanted by the DS1685/DS1687 above,
+ which supports a few minor features such as Vcc, Vbat, and Power
+ Cycle counters, plus a customer-specific, 8-byte ROM/Serial number.
+
+ It also works for the even older DS1688/DS1691 RTC chips, which are
+ virtually the same and carry the same model number. Both chips
+ have 114 bytes of user NVRAM.
+
+config RTC_DRV_DS17285
+ bool "DS17285/DS17287"
+ help
+ This enables support for the Dallas/Maxim DS17285/DS17287 real time
+ clock chip.
+
+ This chip features 2kb of extended NV-SRAM. It may possibly be
+ found in some SGI O2 systems (rare).
+
+config RTC_DRV_DS17485
+ bool "DS17485/DS17487"
+ help
+ This enables support for the Dallas/Maxim DS17485/DS17487 real time
+ clock chip.
+
+ This chip features 4kb of extended NV-SRAM.
+
+config RTC_DRV_DS17885
+ bool "DS17885/DS17887"
+ help
+ This enables support for the Dallas/Maxim DS17885/DS17887 real time
+ clock chip.
+
+ This chip features 8kb of extended NV-SRAM.
+
+endchoice
+
+config RTC_DS1685_PROC_REGS
+ bool "Display register values in /proc"
+ depends on RTC_DRV_DS1685_FAMILY && PROC_FS
+ help
+ Enable this to display a readout of all of the RTC registers in
+ /proc/drivers/rtc. Keep in mind that this can potentially lead
+ to lost interrupts, as reading Control Register C will clear
+ all pending IRQ flags.
+
+ Unless you are debugging this driver, choose N.
+
+config RTC_DS1685_SYSFS_REGS
+ bool "SysFS access to RTC register bits"
+ depends on RTC_DRV_DS1685_FAMILY && SYSFS
+ help
+ Enable this to provide access to the RTC control register bits
+ in /sys. Some of the bits are read-write, others are read-only.
+
+ Keep in mind that reading Control C's bits automatically clears
+ all pending IRQ flags - this can cause lost interrupts.
+
+ If you know that you need access to these bits, choose Y, Else N.
+
+config RTC_DRV_DS1742
+ tristate "Maxim/Dallas DS1742/1743"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the
+ Maxim/Dallas DS1742/1743 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds1742.
+
+config RTC_DRV_DS2404
+ tristate "Maxim/Dallas DS2404"
+ help
+ If you say yes here you get support for the
+ Dallas DS2404 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ds2404.
+
+config RTC_DRV_DA9052
+ tristate "Dialog DA9052/DA9053 RTC"
+ depends on PMIC_DA9052
+ help
+ Say y here to support the RTC driver for Dialog Semiconductor
+ DA9052-BC and DA9053-AA/Bx PMICs.
+
+config RTC_DRV_DA9055
+ tristate "Dialog Semiconductor DA9055 RTC"
+ depends on MFD_DA9055
+ help
+ If you say yes here you will get support for the
+ RTC of the Dialog DA9055 PMIC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-da9055
+
+config RTC_DRV_DA9063
+ tristate "Dialog Semiconductor DA9063 RTC"
+ depends on MFD_DA9063
+ help
+ If you say yes here you will get support for the RTC subsystem
+ of the Dialog Semiconductor DA9063.
+
+ This driver can also be built as a module. If so, the module
+ will be called "rtc-da9063".
+
+config RTC_DRV_EFI
+ tristate "EFI RTC"
+ depends on EFI && !X86
+ help
+ If you say yes here you will get support for the EFI
+ Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-efi.
+
+config RTC_DRV_STK17TA8
+ tristate "Simtek STK17TA8"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the
+ Simtek STK17TA8 timekeeping chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-stk17ta8.
+
+config RTC_DRV_M48T86
+ tristate "ST M48T86/Dallas DS12887"
+ help
+ If you say Y here you will get support for the
+ ST M48T86 and Dallas DS12887 RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-m48t86.
+
+config RTC_DRV_M48T35
+ tristate "ST M48T35"
+ depends on HAS_IOMEM
+ help
+ If you say Y here you will get support for the
+ ST M48T35 RTC chip.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-m48t35".
+
+config RTC_DRV_M48T59
+ tristate "ST M48T59/M48T08/M48T02"
+ depends on HAS_IOMEM
+ help
+ If you say Y here you will get support for the
+ ST M48T59 RTC chip and compatible ST M48T08 and M48T02.
+
+ These chips are usually found in Sun SPARC and UltraSPARC
+ workstations.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-m48t59".
+
+config RTC_DRV_MSM6242
+ tristate "Oki MSM6242"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the Oki MSM6242
+ timekeeping chip. It is used in some Amiga models (e.g. A2000).
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-msm6242.
+
+config RTC_DRV_BQ4802
+ tristate "TI BQ4802"
+ depends on HAS_IOMEM
+ help
+ If you say Y here you will get support for the TI
+ BQ4802 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bq4802.
+
+config RTC_DRV_RP5C01
+ tristate "Ricoh RP5C01"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the Ricoh RP5C01
+ timekeeping chip. It is used in some Amiga models (e.g. A3000
+ and A4000).
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rp5c01.
+
+config RTC_DRV_V3020
+ tristate "EM Microelectronic V3020"
+ help
+ If you say yes here you will get support for the
+ EM Microelectronic v3020 RTC chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-v3020.
+
+config RTC_DRV_WM831X
+ tristate "Wolfson Microelectronics WM831x RTC"
+ depends on MFD_WM831X
+ help
+ If you say yes here you will get support for the RTC subsystem
+ of the Wolfson Microelectronics WM831X series PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called "rtc-wm831x".
+
+config RTC_DRV_WM8350
+ tristate "Wolfson Microelectronics WM8350 RTC"
+ depends on MFD_WM8350
+ help
+ If you say yes here you will get support for the RTC subsystem
+ of the Wolfson Microelectronics WM8350.
+
+ This driver can also be built as a module. If so, the module
+ will be called "rtc-wm8350".
+
+config RTC_DRV_SPEAR
+ tristate "SPEAR ST RTC"
+ depends on PLAT_SPEAR
+ default y
+ help
+ If you say Y here you will get support for the RTC found on
+ spear
+
+config RTC_DRV_PCF50633
+ depends on MFD_PCF50633
+ tristate "NXP PCF50633 RTC"
+ help
+ If you say yes here you get support for the RTC subsystem of the
+ NXP PCF50633 used in embedded systems.
+
+config RTC_DRV_AB3100
+ tristate "ST-Ericsson AB3100 RTC"
+ depends on AB3100_CORE
+ default y if AB3100_CORE
+ help
+ Select this to enable the ST-Ericsson AB3100 Mixed Signal IC RTC
+ support. This chip contains a battery- and capacitor-backed RTC.
+
+config RTC_DRV_AB8500
+ tristate "ST-Ericsson AB8500 RTC"
+ depends on AB8500_CORE
+ select RTC_INTF_DEV
+ select RTC_INTF_DEV_UIE_EMUL
+ help
+ Select this to enable the ST-Ericsson AB8500 power management IC RTC
+ support. This chip contains a battery- and capacitor-backed RTC.
+
+config RTC_DRV_NUC900
+ tristate "NUC910/NUC920 RTC driver"
+ depends on ARCH_W90X900
+ help
+ If you say yes here you get support for the RTC subsystem of the
+ NUC910/NUC920 used in embedded systems.
+
+config RTC_DRV_OPAL
+ tristate "IBM OPAL RTC driver"
+ depends on PPC_POWERNV
+ default y
+ help
+ If you say yes here you get support for the PowerNV platform RTC
+ driver based on OPAL interfaces.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-opal.
+
+comment "on-CPU RTC drivers"
+
+config RTC_DRV_DAVINCI
+ tristate "TI DaVinci RTC"
+ depends on ARCH_DAVINCI_DM365
+ help
+ If you say yes here you get support for the RTC on the
+ DaVinci platforms (DM365).
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-davinci.
+
+config RTC_DRV_DIGICOLOR
+ tristate "Conexant Digicolor RTC"
+ depends on ARCH_DIGICOLOR
+ help
+ If you say yes here you get support for the RTC on Conexant
+ Digicolor platforms. This currently includes the CX92755 SoC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-digicolor.
+
+config RTC_DRV_IMXDI
+ tristate "Freescale IMX DryIce Real Time Clock"
+ depends on ARCH_MXC
+ help
+ Support for Freescale IMX DryIce RTC
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-imxdi".
+
+config RTC_DRV_OMAP
+ tristate "TI OMAP Real Time Clock"
+ depends on ARCH_OMAP || ARCH_DAVINCI
+ help
+ Say "yes" here to support the on chip real time clock
+ present on TI OMAP1, AM33xx, DA8xx/OMAP-L13x, AM43xx and DRA7xx.
+
+ This driver can also be built as a module, if so, module
+ will be called rtc-omap.
+
+config HAVE_S3C_RTC
+ bool
+ help
+ This will include RTC support for Samsung SoCs. If
+ you want to include RTC support for any machine, kindly
+ select this in the respective mach-XXXX/Kconfig file.
+
+config RTC_DRV_S3C
+ tristate "Samsung S3C series SoC RTC"
+ depends on ARCH_S3C64XX || HAVE_S3C_RTC
+ help
+ RTC (Realtime Clock) driver for the clock inbuilt into the
+ Samsung S3C24XX series of SoCs. This can provide periodic
+ interrupt rates from 1Hz to 64Hz for user programs, and
+ wakeup from Alarm.
+
+ The driver currently supports the common features on all the
+ S3C24XX range, such as the S3C2410, S3C2412, S3C2413, S3C2440
+ and S3C2442.
+
+ This driver can also be build as a module. If so, the module
+ will be called rtc-s3c.
+
+config RTC_DRV_EP93XX
+ tristate "Cirrus Logic EP93XX"
+ depends on ARCH_EP93XX
+ help
+ If you say yes here you get support for the
+ RTC embedded in the Cirrus Logic EP93XX processors.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ep93xx.
+
+config RTC_DRV_SA1100
+ tristate "SA11x0/PXA2xx/PXA910"
+ depends on ARCH_SA1100 || ARCH_PXA || ARCH_MMP
+ help
+ If you say Y here you will get access to the real time clock
+ built into your SA11x0 or PXA2xx CPU.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-sa1100.
+
+config RTC_DRV_SH
+ tristate "SuperH On-Chip RTC"
+ depends on SUPERH && HAVE_CLK
+ help
+ Say Y here to enable support for the on-chip RTC found in
+ most SuperH processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-sh.
+
+config RTC_DRV_VR41XX
+ tristate "NEC VR41XX"
+ depends on CPU_VR41XX
+ help
+ If you say Y here you will get access to the real time clock
+ built into your NEC VR41XX CPU.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-vr41xx.
+
+config RTC_DRV_PL030
+ tristate "ARM AMBA PL030 RTC"
+ depends on ARM_AMBA
+ help
+ If you say Y here you will get access to ARM AMBA
+ PrimeCell PL030 RTC found on certain ARM SOCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-pl030.
+
+config RTC_DRV_PL031
+ tristate "ARM AMBA PL031 RTC"
+ depends on ARM_AMBA
+ help
+ If you say Y here you will get access to ARM AMBA
+ PrimeCell PL031 RTC found on certain ARM SOCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-pl031.
+
+config RTC_DRV_AT32AP700X
+ tristate "AT32AP700X series RTC"
+ depends on PLATFORM_AT32AP
+ help
+ Driver for the internal RTC (Realtime Clock) on Atmel AVR32
+ AT32AP700x family processors.
+
+config RTC_DRV_AT91RM9200
+ tristate "AT91RM9200 or some AT91SAM9 RTC"
+ depends on ARCH_AT91
+ help
+ Driver for the internal RTC (Realtime Clock) module found on
+ Atmel AT91RM9200's and some AT91SAM9 chips. On AT91SAM9 chips
+ this is powered by the backup power supply.
+
+config RTC_DRV_AT91SAM9
+ tristate "AT91SAM9 RTT as RTC"
+ depends on ARCH_AT91
+ select MFD_SYSCON
+ help
+ Some AT91SAM9 SoCs provide an RTT (Real Time Timer) block which
+ can be used as an RTC thanks to the backup power supply (e.g. a
+ small coin cell battery) which keeps this block and the GPBR
+ (General Purpose Backup Registers) block powered when the device
+ is shutdown.
+ Some AT91SAM9 SoCs provide a real RTC block, on those ones you'd
+ probably want to use the real RTC block instead of the "RTT as an
+ RTC" driver.
+
+config RTC_DRV_AU1XXX
+ tristate "Au1xxx Counter0 RTC support"
+ depends on MIPS_ALCHEMY
+ help
+ This is a driver for the Au1xxx on-chip Counter0 (Time-Of-Year
+ counter) to be used as a RTC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-au1xxx.
+
+config RTC_DRV_BFIN
+ tristate "Blackfin On-Chip RTC"
+ depends on BLACKFIN && !BF561
+ help
+ If you say yes here you will get support for the
+ Blackfin On-Chip Real Time Clock.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-bfin.
+
+config RTC_DRV_RS5C313
+ tristate "Ricoh RS5C313"
+ depends on SH_LANDISK
+ help
+ If you say yes here you get support for the Ricoh RS5C313 RTC chips.
+
+config RTC_DRV_GENERIC
+ tristate "Generic RTC support"
+ # Please consider writing a new RTC driver instead of using the generic
+ # RTC abstraction
+ depends on PARISC || M68K || PPC || SUPERH32
+ help
+ Say Y or M here to enable RTC support on systems using the generic
+ RTC abstraction. If you do not know what you are doing, you should
+ just say Y.
+
+config RTC_DRV_PXA
+ tristate "PXA27x/PXA3xx"
+ depends on ARCH_PXA
+ help
+ If you say Y here you will get access to the real time clock
+ built into your PXA27x or PXA3xx CPU.
+
+ This RTC driver uses PXA RTC registers available since pxa27x
+ series (RDxR, RYxR) instead of legacy RCNR, RTAR.
+
+config RTC_DRV_VT8500
+ tristate "VIA/WonderMedia 85xx SoC RTC"
+ depends on ARCH_VT8500
+ help
+ If you say Y here you will get access to the real time clock
+ built into your VIA VT8500 SoC or its relatives.
+
+
+config RTC_DRV_SUN4V
+ bool "SUN4V Hypervisor RTC"
+ depends on SPARC64
+ help
+ If you say Y here you will get support for the Hypervisor
+ based RTC on SUN4V systems.
+
+config RTC_DRV_SUN6I
+ tristate "Allwinner A31 RTC"
+ depends on MACH_SUN6I || MACH_SUN8I
+ help
+ If you say Y here you will get support for the RTC found on
+ Allwinner A31.
+
+config RTC_DRV_SUNXI
+ tristate "Allwinner sun4i/sun7i RTC"
+ depends on MACH_SUN4I || MACH_SUN7I
+ help
+ If you say Y here you will get support for the RTC found on
+ Allwinner A10/A20.
+
+config RTC_DRV_STARFIRE
+ bool "Starfire RTC"
+ depends on SPARC64
+ help
+ If you say Y here you will get support for the RTC found on
+ Starfire systems.
+
+config RTC_DRV_TX4939
+ tristate "TX4939 SoC"
+ depends on SOC_TX4939
+ help
+ Driver for the internal RTC (Realtime Clock) module found on
+ Toshiba TX4939 SoC.
+
+config RTC_DRV_MV
+ tristate "Marvell SoC RTC"
+ depends on ARCH_DOVE || ARCH_MVEBU
+ help
+ If you say yes here you will get support for the in-chip RTC
+ that can be found in some of Marvell's SoC devices, such as
+ the Kirkwood 88F6281 and 88F6192.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-mv.
+
+config RTC_DRV_ARMADA38X
+ tristate "Armada 38x Marvell SoC RTC"
+ depends on ARCH_MVEBU
+ help
+ If you say yes here you will get support for the in-chip RTC
+ that can be found in the Armada 38x Marvell's SoC device
+
+ This driver can also be built as a module. If so, the module
+ will be called armada38x-rtc.
+
+config RTC_DRV_PS3
+ tristate "PS3 RTC"
+ depends on PPC_PS3
+ help
+ If you say yes here you will get support for the RTC on PS3.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ps3.
+
+config RTC_DRV_COH901331
+ tristate "ST-Ericsson COH 901 331 RTC"
+ depends on ARCH_U300
+ help
+ If you say Y here you will get access to ST-Ericsson
+ COH 901 331 RTC clock found in some ST-Ericsson Mobile
+ Platforms.
+
+ This driver can also be built as a module. If so, the module
+ will be called "rtc-coh901331".
+
+
+config RTC_DRV_STMP
+ tristate "Freescale STMP3xxx/i.MX23/i.MX28 RTC"
+ depends on ARCH_MXS
+ help
+ If you say yes here you will get support for the onboard
+ STMP3xxx/i.MX23/i.MX28 RTC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-stmp3xxx.
+
+config RTC_DRV_PCAP
+ tristate "PCAP RTC"
+ depends on EZX_PCAP
+ help
+ If you say Y here you will get support for the RTC found on
+ the PCAP2 ASIC used on some Motorola phones.
+
+config RTC_DRV_MC13XXX
+ depends on MFD_MC13XXX
+ tristate "Freescale MC13xxx RTC"
+ help
+ This enables support for the RTCs found on Freescale's PMICs
+ MC13783 and MC13892.
+
+config RTC_DRV_MPC5121
+ tristate "Freescale MPC5121 built-in RTC"
+ depends on PPC_MPC512x || PPC_MPC52xx
+ help
+ If you say yes here you will get support for the
+ built-in RTC on MPC5121 or on MPC5200.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-mpc5121.
+
+config RTC_DRV_JZ4740
+ tristate "Ingenic JZ4740 SoC"
+ depends on MACH_JZ4740
+ help
+ If you say yes here you get support for the Ingenic JZ4740 SoC RTC
+ controller.
+
+ This driver can also be buillt as a module. If so, the module
+ will be called rtc-jz4740.
+
+config RTC_DRV_LPC32XX
+ depends on ARCH_LPC32XX
+ tristate "NXP LPC32XX RTC"
+ help
+ This enables support for the NXP RTC in the LPC32XX
+
+ This driver can also be buillt as a module. If so, the module
+ will be called rtc-lpc32xx.
+
+config RTC_DRV_PM8XXX
+ tristate "Qualcomm PMIC8XXX RTC"
+ depends on MFD_PM8XXX || MFD_SPMI_PMIC
+ help
+ If you say yes here you get support for the
+ Qualcomm PMIC8XXX RTC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-pm8xxx.
+
+config RTC_DRV_TEGRA
+ tristate "NVIDIA Tegra Internal RTC driver"
+ depends on ARCH_TEGRA
+ help
+ If you say yes here you get support for the
+ Tegra 200 series internal RTC module.
+
+ This drive can also be built as a module. If so, the module
+ will be called rtc-tegra.
+
+config RTC_DRV_TILE
+ tristate "Tilera hypervisor RTC support"
+ depends on TILE
+ help
+ Enable support for the Linux driver side of the Tilera
+ hypervisor's real-time clock interface.
+
+config RTC_DRV_PUV3
+ tristate "PKUnity v3 RTC support"
+ depends on ARCH_PUV3
+ help
+ This enables support for the RTC in the PKUnity-v3 SoCs.
+
+ This drive can also be built as a module. If so, the module
+ will be called rtc-puv3.
+
+config RTC_DRV_LOONGSON1
+ tristate "loongson1 RTC support"
+ depends on MACH_LOONGSON1
+ help
+ This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year
+ counter) to be used as a RTC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ls1x.
+
+config RTC_DRV_MXC
+ tristate "Freescale MXC Real Time Clock"
+ depends on ARCH_MXC
+ help
+ If you say yes here you get support for the Freescale MXC
+ RTC module.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-mxc".
+
+config RTC_DRV_SNVS
+ tristate "Freescale SNVS RTC support"
+ depends on HAS_IOMEM
+ depends on OF
+ help
+ If you say yes here you get support for the Freescale SNVS
+ Low Power (LP) RTC module.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-snvs".
+
+config RTC_DRV_SIRFSOC
+ tristate "SiRFSOC RTC"
+ depends on ARCH_SIRF
+ help
+ Say "yes" here to support the real time clock on SiRF SOC chips.
+ This driver can also be built as a module called rtc-sirfsoc.
+
+config RTC_DRV_MOXART
+ tristate "MOXA ART RTC"
+ depends on ARCH_MOXART || COMPILE_TEST
+ help
+ If you say yes here you get support for the MOXA ART
+ RTC module.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-moxart
+
+config RTC_DRV_XGENE
+ tristate "APM X-Gene RTC"
+ depends on HAS_IOMEM
+ help
+ If you say yes here you get support for the APM X-Gene SoC real time
+ clock.
+
+ This driver can also be built as a module, if so, the module
+ will be called "rtc-xgene".
+
+comment "HID Sensor RTC drivers"
+
+config RTC_DRV_HID_SENSOR_TIME
+ tristate "HID Sensor Time"
+ depends on USB_HID
+ select IIO
+ select HID_SENSOR_HUB
+ select HID_SENSOR_IIO_COMMON
+ help
+ Say yes here to build support for the HID Sensors of type Time.
+ This drivers makes such sensors available as RTCs.
+
+ If this driver is compiled as a module, it will be named
+ rtc-hid-sensor-time.
+
+
+endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
new file mode 100644
index 000000000..2b82e2b03
--- /dev/null
+++ b/drivers/rtc/Makefile
@@ -0,0 +1,157 @@
+#
+# Makefile for RTC class/drivers.
+#
+
+ccflags-$(CONFIG_RTC_DEBUG) := -DDEBUG
+
+obj-$(CONFIG_RTC_LIB) += rtc-lib.o
+obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
+obj-$(CONFIG_RTC_SYSTOHC) += systohc.o
+obj-$(CONFIG_RTC_CLASS) += rtc-core.o
+rtc-core-y := class.o interface.o
+
+ifdef CONFIG_RTC_DRV_EFI
+rtc-core-y += rtc-efi-platform.o
+endif
+
+rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
+rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
+rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
+
+# Keep the list ordered.
+
+obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
+obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
+obj-$(CONFIG_RTC_DRV_AB3100) += rtc-ab3100.o
+obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
+obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
+obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o
+obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
+obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
+obj-$(CONFIG_RTC_DRV_AT32AP700X)+= rtc-at32ap700x.o
+obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
+obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
+obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
+obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
+obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
+obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
+obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
+obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
+obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
+obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
+obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
+obj-$(CONFIG_RTC_DRV_DIGICOLOR) += rtc-digicolor.o
+obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
+obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
+obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
+obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
+obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
+obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o
+obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
+obj-$(CONFIG_RTC_DRV_DS1343) += rtc-ds1343.o
+obj-$(CONFIG_RTC_DRV_DS1347) += rtc-ds1347.o
+obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o
+obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o
+obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
+obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
+obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
+obj-$(CONFIG_RTC_DRV_DS1685_FAMILY) += rtc-ds1685.o
+obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
+obj-$(CONFIG_RTC_DRV_DS2404) += rtc-ds2404.o
+obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
+obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
+obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
+obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
+obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
+obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
+obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
+obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
+obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
+obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o
+obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
+obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
+obj-$(CONFIG_RTC_DRV_ISL12057) += rtc-isl12057.o
+obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
+obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o
+obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
+obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o
+obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
+obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o
+obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
+obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
+obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
+obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
+obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
+obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
+obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o
+obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
+obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
+obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o
+obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
+obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o
+obj-$(CONFIG_RTC_DRV_MAX77802) += rtc-max77802.o
+obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o
+obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o
+obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
+obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
+obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
+obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
+obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
+obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
+obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
+obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
+obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
+obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o
+obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
+obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
+obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
+obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
+obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
+obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
+obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
+obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
+obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
+obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
+obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
+obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
+obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
+obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
+obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
+obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
+obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
+obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
+obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o
+obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
+obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
+obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
+obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
+obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
+obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
+obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
+obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
+obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
+obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
+obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
+obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
+obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
+obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
+obj-$(CONFIG_RTC_DRV_SUN6I) += rtc-sun6i.o
+obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
+obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
+obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
+obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o
+obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
+obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o
+obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
+obj-$(CONFIG_RTC_DRV_TPS80031) += rtc-tps80031.o
+obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
+obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
+obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
+obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
+obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
+obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
+obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
+obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
+obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
new file mode 100644
index 000000000..ea2a315df
--- /dev/null
+++ b/drivers/rtc/class.c
@@ -0,0 +1,382 @@
+/*
+ * RTC subsystem, base class
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * class skeleton from drivers/hwmon/hwmon.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/rtc.h>
+#include <linux/kdev_t.h>
+#include <linux/idr.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include "rtc-core.h"
+
+
+static DEFINE_IDA(rtc_ida);
+struct class *rtc_class;
+
+static void rtc_device_release(struct device *dev)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ ida_simple_remove(&rtc_ida, rtc->id);
+ kfree(rtc);
+}
+
+#ifdef CONFIG_RTC_HCTOSYS_DEVICE
+/* Result of the last RTC to system clock attempt. */
+int rtc_hctosys_ret = -ENODEV;
+#endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
+/*
+ * On suspend(), measure the delta between one RTC and the
+ * system's wall clock; restore it on resume().
+ */
+
+static struct timespec64 old_rtc, old_system, old_delta;
+
+
+static int rtc_suspend(struct device *dev)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ struct rtc_time tm;
+ struct timespec64 delta, delta_delta;
+ int err;
+
+ if (timekeeping_rtc_skipsuspend())
+ return 0;
+
+ if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
+ return 0;
+
+ /* snapshot the current RTC and system time at suspend*/
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0) {
+ pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
+ getnstimeofday64(&old_system);
+ old_rtc.tv_sec = rtc_tm_to_time64(&tm);
+
+
+ /*
+ * To avoid drift caused by repeated suspend/resumes,
+ * which each can add ~1 second drift error,
+ * try to compensate so the difference in system time
+ * and rtc time stays close to constant.
+ */
+ delta = timespec64_sub(old_system, old_rtc);
+ delta_delta = timespec64_sub(delta, old_delta);
+ if (delta_delta.tv_sec < -2 || delta_delta.tv_sec >= 2) {
+ /*
+ * if delta_delta is too large, assume time correction
+ * has occured and set old_delta to the current delta.
+ */
+ old_delta = delta;
+ } else {
+ /* Otherwise try to adjust old_system to compensate */
+ old_system = timespec64_sub(old_system, delta_delta);
+ }
+
+ return 0;
+}
+
+static int rtc_resume(struct device *dev)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ struct rtc_time tm;
+ struct timespec64 new_system, new_rtc;
+ struct timespec64 sleep_time;
+ int err;
+
+ if (timekeeping_rtc_skipresume())
+ return 0;
+
+ rtc_hctosys_ret = -ENODEV;
+ if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
+ return 0;
+
+ /* snapshot the current rtc and system time at resume */
+ getnstimeofday64(&new_system);
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0) {
+ pr_debug("%s: fail to read rtc time\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
+ new_rtc.tv_sec = rtc_tm_to_time64(&tm);
+ new_rtc.tv_nsec = 0;
+
+ if (new_rtc.tv_sec < old_rtc.tv_sec) {
+ pr_debug("%s: time travel!\n", dev_name(&rtc->dev));
+ return 0;
+ }
+
+ /* calculate the RTC time delta (sleep time)*/
+ sleep_time = timespec64_sub(new_rtc, old_rtc);
+
+ /*
+ * Since these RTC suspend/resume handlers are not called
+ * at the very end of suspend or the start of resume,
+ * some run-time may pass on either sides of the sleep time
+ * so subtract kernel run-time between rtc_suspend to rtc_resume
+ * to keep things accurate.
+ */
+ sleep_time = timespec64_sub(sleep_time,
+ timespec64_sub(new_system, old_system));
+
+ if (sleep_time.tv_sec >= 0)
+ timekeeping_inject_sleeptime64(&sleep_time);
+ rtc_hctosys_ret = 0;
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume);
+#define RTC_CLASS_DEV_PM_OPS (&rtc_class_dev_pm_ops)
+#else
+#define RTC_CLASS_DEV_PM_OPS NULL
+#endif
+
+
+/**
+ * rtc_device_register - register w/ RTC class
+ * @dev: the device to register
+ *
+ * rtc_device_unregister() must be called when the class device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new struct class device.
+ */
+struct rtc_device *rtc_device_register(const char *name, struct device *dev,
+ const struct rtc_class_ops *ops,
+ struct module *owner)
+{
+ struct rtc_device *rtc;
+ struct rtc_wkalrm alrm;
+ int of_id = -1, id = -1, err;
+
+ if (dev->of_node)
+ of_id = of_alias_get_id(dev->of_node, "rtc");
+ else if (dev->parent && dev->parent->of_node)
+ of_id = of_alias_get_id(dev->parent->of_node, "rtc");
+
+ if (of_id >= 0) {
+ id = ida_simple_get(&rtc_ida, of_id, of_id + 1,
+ GFP_KERNEL);
+ if (id < 0)
+ dev_warn(dev, "/aliases ID %d not available\n",
+ of_id);
+ }
+
+ if (id < 0) {
+ id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ err = id;
+ goto exit;
+ }
+ }
+
+ rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
+ if (rtc == NULL) {
+ err = -ENOMEM;
+ goto exit_ida;
+ }
+
+ rtc->id = id;
+ rtc->ops = ops;
+ rtc->owner = owner;
+ rtc->irq_freq = 1;
+ rtc->max_user_freq = 64;
+ rtc->dev.parent = dev;
+ rtc->dev.class = rtc_class;
+ rtc->dev.release = rtc_device_release;
+
+ mutex_init(&rtc->ops_lock);
+ spin_lock_init(&rtc->irq_lock);
+ spin_lock_init(&rtc->irq_task_lock);
+ init_waitqueue_head(&rtc->irq_queue);
+
+ /* Init timerqueue */
+ timerqueue_init_head(&rtc->timerqueue);
+ INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
+ /* Init aie timer */
+ rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
+ /* Init uie timer */
+ rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
+ /* Init pie timer */
+ hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ rtc->pie_timer.function = rtc_pie_update_irq;
+ rtc->pie_enabled = 0;
+
+ strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
+ dev_set_name(&rtc->dev, "rtc%d", id);
+
+ /* Check to see if there is an ALARM already set in hw */
+ err = __rtc_read_alarm(rtc, &alrm);
+
+ if (!err && !rtc_valid_tm(&alrm.time))
+ rtc_initialize_alarm(rtc, &alrm);
+
+ rtc_dev_prepare(rtc);
+
+ err = device_register(&rtc->dev);
+ if (err) {
+ put_device(&rtc->dev);
+ goto exit_kfree;
+ }
+
+ rtc_dev_add_device(rtc);
+ rtc_sysfs_add_device(rtc);
+ rtc_proc_add_device(rtc);
+
+ dev_info(dev, "rtc core: registered %s as %s\n",
+ rtc->name, dev_name(&rtc->dev));
+
+ return rtc;
+
+exit_kfree:
+ kfree(rtc);
+
+exit_ida:
+ ida_simple_remove(&rtc_ida, id);
+
+exit:
+ dev_err(dev, "rtc core: unable to register %s, err = %d\n",
+ name, err);
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(rtc_device_register);
+
+
+/**
+ * rtc_device_unregister - removes the previously registered RTC class device
+ *
+ * @rtc: the RTC class device to destroy
+ */
+void rtc_device_unregister(struct rtc_device *rtc)
+{
+ if (get_device(&rtc->dev) != NULL) {
+ mutex_lock(&rtc->ops_lock);
+ /* remove innards of this RTC, then disable it, before
+ * letting any rtc_class_open() users access it again
+ */
+ rtc_sysfs_del_device(rtc);
+ rtc_dev_del_device(rtc);
+ rtc_proc_del_device(rtc);
+ device_unregister(&rtc->dev);
+ rtc->ops = NULL;
+ mutex_unlock(&rtc->ops_lock);
+ put_device(&rtc->dev);
+ }
+}
+EXPORT_SYMBOL_GPL(rtc_device_unregister);
+
+static void devm_rtc_device_release(struct device *dev, void *res)
+{
+ struct rtc_device *rtc = *(struct rtc_device **)res;
+
+ rtc_device_unregister(rtc);
+}
+
+static int devm_rtc_device_match(struct device *dev, void *res, void *data)
+{
+ struct rtc **r = res;
+
+ return *r == data;
+}
+
+/**
+ * devm_rtc_device_register - resource managed rtc_device_register()
+ * @dev: the device to register
+ * @name: the name of the device
+ * @ops: the rtc operations structure
+ * @owner: the module owner
+ *
+ * @return a struct rtc on success, or an ERR_PTR on error
+ *
+ * Managed rtc_device_register(). The rtc_device returned from this function
+ * are automatically freed on driver detach. See rtc_device_register()
+ * for more information.
+ */
+
+struct rtc_device *devm_rtc_device_register(struct device *dev,
+ const char *name,
+ const struct rtc_class_ops *ops,
+ struct module *owner)
+{
+ struct rtc_device **ptr, *rtc;
+
+ ptr = devres_alloc(devm_rtc_device_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ rtc = rtc_device_register(name, dev, ops, owner);
+ if (!IS_ERR(rtc)) {
+ *ptr = rtc;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return rtc;
+}
+EXPORT_SYMBOL_GPL(devm_rtc_device_register);
+
+/**
+ * devm_rtc_device_unregister - resource managed devm_rtc_device_unregister()
+ * @dev: the device to unregister
+ * @rtc: the RTC class device to unregister
+ *
+ * Deallocated a rtc allocated with devm_rtc_device_register(). Normally this
+ * function will not need to be called and the resource management code will
+ * ensure that the resource is freed.
+ */
+void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_rtc_device_release,
+ devm_rtc_device_match, rtc);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
+
+static int __init rtc_init(void)
+{
+ rtc_class = class_create(THIS_MODULE, "rtc");
+ if (IS_ERR(rtc_class)) {
+ pr_err("couldn't create class\n");
+ return PTR_ERR(rtc_class);
+ }
+ rtc_class->pm = RTC_CLASS_DEV_PM_OPS;
+ rtc_dev_init();
+ rtc_sysfs_init(rtc_class);
+ return 0;
+}
+
+static void __exit rtc_exit(void)
+{
+ rtc_dev_exit();
+ class_destroy(rtc_class);
+ ida_destroy(&rtc_ida);
+}
+
+subsys_initcall(rtc_init);
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("RTC class support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c
new file mode 100644
index 000000000..e1cfa0681
--- /dev/null
+++ b/drivers/rtc/hctosys.c
@@ -0,0 +1,70 @@
+/*
+ * RTC subsystem, initialize system time on startup
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/rtc.h>
+
+/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
+ * whether it stores the most close value or the value with partial
+ * seconds truncated. However, it is important that we use it to store
+ * the truncated value. This is because otherwise it is necessary,
+ * in an rtc sync function, to read both xtime.tv_sec and
+ * xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
+ * of >32bits is not possible. So storing the most close value would
+ * slow down the sync API. So here we have the truncated value and
+ * the best guess is to add 0.5s.
+ */
+
+static int __init rtc_hctosys(void)
+{
+ int err = -ENODEV;
+ struct rtc_time tm;
+ struct timespec64 tv64 = {
+ .tv_nsec = NSEC_PER_SEC >> 1,
+ };
+ struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+
+ if (rtc == NULL) {
+ pr_info("unable to open rtc device (%s)\n",
+ CONFIG_RTC_HCTOSYS_DEVICE);
+ goto err_open;
+ }
+
+ err = rtc_read_time(rtc, &tm);
+ if (err) {
+ dev_err(rtc->dev.parent,
+ "hctosys: unable to read the hardware clock\n");
+ goto err_read;
+
+ }
+
+ tv64.tv_sec = rtc_tm_to_time64(&tm);
+
+ err = do_settimeofday64(&tv64);
+
+ dev_info(rtc->dev.parent,
+ "setting system clock to "
+ "%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ (long long) tv64.tv_sec);
+
+err_read:
+ rtc_class_close(rtc);
+
+err_open:
+ rtc_hctosys_ret = err;
+
+ return err;
+}
+
+late_initcall(rtc_hctosys);
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
new file mode 100644
index 000000000..166fc60d8
--- /dev/null
+++ b/drivers/rtc/interface.c
@@ -0,0 +1,989 @@
+/*
+ * RTC subsystem, interface functions
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/rtc.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/log2.h>
+#include <linux/workqueue.h>
+
+static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer);
+static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer);
+
+static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ int err;
+ if (!rtc->ops)
+ err = -ENODEV;
+ else if (!rtc->ops->read_time)
+ err = -EINVAL;
+ else {
+ memset(tm, 0, sizeof(struct rtc_time));
+ err = rtc->ops->read_time(rtc->dev.parent, tm);
+ if (err < 0) {
+ dev_dbg(&rtc->dev, "read_time: fail to read: %d\n",
+ err);
+ return err;
+ }
+
+ err = rtc_valid_tm(tm);
+ if (err < 0)
+ dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");
+ }
+ return err;
+}
+
+int rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ int err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ err = __rtc_read_time(rtc, tm);
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_read_time);
+
+int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm)
+{
+ int err;
+
+ err = rtc_valid_tm(tm);
+ if (err != 0)
+ return err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ if (!rtc->ops)
+ err = -ENODEV;
+ else if (rtc->ops->set_time)
+ err = rtc->ops->set_time(rtc->dev.parent, tm);
+ else if (rtc->ops->set_mmss64) {
+ time64_t secs64 = rtc_tm_to_time64(tm);
+
+ err = rtc->ops->set_mmss64(rtc->dev.parent, secs64);
+ } else if (rtc->ops->set_mmss) {
+ time64_t secs64 = rtc_tm_to_time64(tm);
+ err = rtc->ops->set_mmss(rtc->dev.parent, secs64);
+ } else
+ err = -EINVAL;
+
+ pm_stay_awake(rtc->dev.parent);
+ mutex_unlock(&rtc->ops_lock);
+ /* A timer might have just expired */
+ schedule_work(&rtc->irqwork);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_set_time);
+
+int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
+{
+ int err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ if (!rtc->ops)
+ err = -ENODEV;
+ else if (rtc->ops->set_mmss64)
+ err = rtc->ops->set_mmss64(rtc->dev.parent, secs);
+ else if (rtc->ops->set_mmss)
+ err = rtc->ops->set_mmss(rtc->dev.parent, secs);
+ else if (rtc->ops->read_time && rtc->ops->set_time) {
+ struct rtc_time new, old;
+
+ err = rtc->ops->read_time(rtc->dev.parent, &old);
+ if (err == 0) {
+ rtc_time64_to_tm(secs, &new);
+
+ /*
+ * avoid writing when we're going to change the day of
+ * the month. We will retry in the next minute. This
+ * basically means that if the RTC must not drift
+ * by more than 1 minute in 11 minutes.
+ */
+ if (!((old.tm_hour == 23 && old.tm_min == 59) ||
+ (new.tm_hour == 23 && new.tm_min == 59)))
+ err = rtc->ops->set_time(rtc->dev.parent,
+ &new);
+ }
+ } else {
+ err = -EINVAL;
+ }
+
+ pm_stay_awake(rtc->dev.parent);
+ mutex_unlock(&rtc->ops_lock);
+ /* A timer might have just expired */
+ schedule_work(&rtc->irqwork);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_set_mmss);
+
+static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ if (rtc->ops == NULL)
+ err = -ENODEV;
+ else if (!rtc->ops->read_alarm)
+ err = -EINVAL;
+ else {
+ memset(alarm, 0, sizeof(struct rtc_wkalrm));
+ err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
+ }
+
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+
+int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+ struct rtc_time before, now;
+ int first_time = 1;
+ time64_t t_now, t_alm;
+ enum { none, day, month, year } missing = none;
+ unsigned days;
+
+ /* The lower level RTC driver may return -1 in some fields,
+ * creating invalid alarm->time values, for reasons like:
+ *
+ * - The hardware may not be capable of filling them in;
+ * many alarms match only on time-of-day fields, not
+ * day/month/year calendar data.
+ *
+ * - Some hardware uses illegal values as "wildcard" match
+ * values, which non-Linux firmware (like a BIOS) may try
+ * to set up as e.g. "alarm 15 minutes after each hour".
+ * Linux uses only oneshot alarms.
+ *
+ * When we see that here, we deal with it by using values from
+ * a current RTC timestamp for any missing (-1) values. The
+ * RTC driver prevents "periodic alarm" modes.
+ *
+ * But this can be racey, because some fields of the RTC timestamp
+ * may have wrapped in the interval since we read the RTC alarm,
+ * which would lead to us inserting inconsistent values in place
+ * of the -1 fields.
+ *
+ * Reading the alarm and timestamp in the reverse sequence
+ * would have the same race condition, and not solve the issue.
+ *
+ * So, we must first read the RTC timestamp,
+ * then read the RTC alarm value,
+ * and then read a second RTC timestamp.
+ *
+ * If any fields of the second timestamp have changed
+ * when compared with the first timestamp, then we know
+ * our timestamp may be inconsistent with that used by
+ * the low-level rtc_read_alarm_internal() function.
+ *
+ * So, when the two timestamps disagree, we just loop and do
+ * the process again to get a fully consistent set of values.
+ *
+ * This could all instead be done in the lower level driver,
+ * but since more than one lower level RTC implementation needs it,
+ * then it's probably best best to do it here instead of there..
+ */
+
+ /* Get the "before" timestamp */
+ err = rtc_read_time(rtc, &before);
+ if (err < 0)
+ return err;
+ do {
+ if (!first_time)
+ memcpy(&before, &now, sizeof(struct rtc_time));
+ first_time = 0;
+
+ /* get the RTC alarm values, which may be incomplete */
+ err = rtc_read_alarm_internal(rtc, alarm);
+ if (err)
+ return err;
+
+ /* full-function RTCs won't have such missing fields */
+ if (rtc_valid_tm(&alarm->time) == 0)
+ return 0;
+
+ /* get the "after" timestamp, to detect wrapped fields */
+ err = rtc_read_time(rtc, &now);
+ if (err < 0)
+ return err;
+
+ /* note that tm_sec is a "don't care" value here: */
+ } while ( before.tm_min != now.tm_min
+ || before.tm_hour != now.tm_hour
+ || before.tm_mon != now.tm_mon
+ || before.tm_year != now.tm_year);
+
+ /* Fill in the missing alarm fields using the timestamp; we
+ * know there's at least one since alarm->time is invalid.
+ */
+ if (alarm->time.tm_sec == -1)
+ alarm->time.tm_sec = now.tm_sec;
+ if (alarm->time.tm_min == -1)
+ alarm->time.tm_min = now.tm_min;
+ if (alarm->time.tm_hour == -1)
+ alarm->time.tm_hour = now.tm_hour;
+
+ /* For simplicity, only support date rollover for now */
+ if (alarm->time.tm_mday < 1 || alarm->time.tm_mday > 31) {
+ alarm->time.tm_mday = now.tm_mday;
+ missing = day;
+ }
+ if ((unsigned)alarm->time.tm_mon >= 12) {
+ alarm->time.tm_mon = now.tm_mon;
+ if (missing == none)
+ missing = month;
+ }
+ if (alarm->time.tm_year == -1) {
+ alarm->time.tm_year = now.tm_year;
+ if (missing == none)
+ missing = year;
+ }
+
+ /* with luck, no rollover is needed */
+ t_now = rtc_tm_to_time64(&now);
+ t_alm = rtc_tm_to_time64(&alarm->time);
+ if (t_now < t_alm)
+ goto done;
+
+ switch (missing) {
+
+ /* 24 hour rollover ... if it's now 10am Monday, an alarm that
+ * that will trigger at 5am will do so at 5am Tuesday, which
+ * could also be in the next month or year. This is a common
+ * case, especially for PCs.
+ */
+ case day:
+ dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day");
+ t_alm += 24 * 60 * 60;
+ rtc_time64_to_tm(t_alm, &alarm->time);
+ break;
+
+ /* Month rollover ... if it's the 31th, an alarm on the 3rd will
+ * be next month. An alarm matching on the 30th, 29th, or 28th
+ * may end up in the month after that! Many newer PCs support
+ * this type of alarm.
+ */
+ case month:
+ dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month");
+ do {
+ if (alarm->time.tm_mon < 11)
+ alarm->time.tm_mon++;
+ else {
+ alarm->time.tm_mon = 0;
+ alarm->time.tm_year++;
+ }
+ days = rtc_month_days(alarm->time.tm_mon,
+ alarm->time.tm_year);
+ } while (days < alarm->time.tm_mday);
+ break;
+
+ /* Year rollover ... easy except for leap years! */
+ case year:
+ dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year");
+ do {
+ alarm->time.tm_year++;
+ } while (!is_leap_year(alarm->time.tm_year + 1900)
+ && rtc_valid_tm(&alarm->time) != 0);
+ break;
+
+ default:
+ dev_warn(&rtc->dev, "alarm rollover not handled\n");
+ }
+
+done:
+ err = rtc_valid_tm(&alarm->time);
+
+ if (err) {
+ dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
+ alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
+ alarm->time.tm_mday, alarm->time.tm_hour, alarm->time.tm_min,
+ alarm->time.tm_sec);
+ }
+
+ return err;
+}
+
+int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+ if (rtc->ops == NULL)
+ err = -ENODEV;
+ else if (!rtc->ops->read_alarm)
+ err = -EINVAL;
+ else {
+ memset(alarm, 0, sizeof(struct rtc_wkalrm));
+ alarm->enabled = rtc->aie_timer.enabled;
+ alarm->time = rtc_ktime_to_tm(rtc->aie_timer.node.expires);
+ }
+ mutex_unlock(&rtc->ops_lock);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_read_alarm);
+
+static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ struct rtc_time tm;
+ time64_t now, scheduled;
+ int err;
+
+ err = rtc_valid_tm(&alarm->time);
+ if (err)
+ return err;
+ scheduled = rtc_tm_to_time64(&alarm->time);
+
+ /* Make sure we're not setting alarms in the past */
+ err = __rtc_read_time(rtc, &tm);
+ if (err)
+ return err;
+ now = rtc_tm_to_time64(&tm);
+ if (scheduled <= now)
+ return -ETIME;
+ /*
+ * XXX - We just checked to make sure the alarm time is not
+ * in the past, but there is still a race window where if
+ * the is alarm set for the next second and the second ticks
+ * over right here, before we set the alarm.
+ */
+
+ if (!rtc->ops)
+ err = -ENODEV;
+ else if (!rtc->ops->set_alarm)
+ err = -EINVAL;
+ else
+ err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
+
+ return err;
+}
+
+int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+
+ err = rtc_valid_tm(&alarm->time);
+ if (err != 0)
+ return err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+ if (rtc->aie_timer.enabled)
+ rtc_timer_remove(rtc, &rtc->aie_timer);
+
+ rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
+ rtc->aie_timer.period = ktime_set(0, 0);
+ if (alarm->enabled)
+ err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
+
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_set_alarm);
+
+/* Called once per device from rtc_device_register */
+int rtc_initialize_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
+{
+ int err;
+ struct rtc_time now;
+
+ err = rtc_valid_tm(&alarm->time);
+ if (err != 0)
+ return err;
+
+ err = rtc_read_time(rtc, &now);
+ if (err)
+ return err;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ rtc->aie_timer.node.expires = rtc_tm_to_ktime(alarm->time);
+ rtc->aie_timer.period = ktime_set(0, 0);
+
+ /* Alarm has to be enabled & in the futrure for us to enqueue it */
+ if (alarm->enabled && (rtc_tm_to_ktime(now).tv64 <
+ rtc->aie_timer.node.expires.tv64)) {
+
+ rtc->aie_timer.enabled = 1;
+ timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node);
+ }
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_initialize_alarm);
+
+
+
+int rtc_alarm_irq_enable(struct rtc_device *rtc, unsigned int enabled)
+{
+ int err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ if (rtc->aie_timer.enabled != enabled) {
+ if (enabled)
+ err = rtc_timer_enqueue(rtc, &rtc->aie_timer);
+ else
+ rtc_timer_remove(rtc, &rtc->aie_timer);
+ }
+
+ if (err)
+ /* nothing */;
+ else if (!rtc->ops)
+ err = -ENODEV;
+ else if (!rtc->ops->alarm_irq_enable)
+ err = -EINVAL;
+ else
+ err = rtc->ops->alarm_irq_enable(rtc->dev.parent, enabled);
+
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_alarm_irq_enable);
+
+int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
+{
+ int err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ if (enabled == 0 && rtc->uie_irq_active) {
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_dev_update_irq_enable_emul(rtc, 0);
+ }
+#endif
+ /* make sure we're changing state */
+ if (rtc->uie_rtctimer.enabled == enabled)
+ goto out;
+
+ if (rtc->uie_unsupported) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (enabled) {
+ struct rtc_time tm;
+ ktime_t now, onesec;
+
+ __rtc_read_time(rtc, &tm);
+ onesec = ktime_set(1, 0);
+ now = rtc_tm_to_ktime(tm);
+ rtc->uie_rtctimer.node.expires = ktime_add(now, onesec);
+ rtc->uie_rtctimer.period = ktime_set(1, 0);
+ err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer);
+ } else
+ rtc_timer_remove(rtc, &rtc->uie_rtctimer);
+
+out:
+ mutex_unlock(&rtc->ops_lock);
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ /*
+ * Enable emulation if the driver did not provide
+ * the update_irq_enable function pointer or if returned
+ * -EINVAL to signal that it has been configured without
+ * interrupts or that are not available at the moment.
+ */
+ if (err == -EINVAL)
+ err = rtc_dev_update_irq_enable_emul(rtc, enabled);
+#endif
+ return err;
+
+}
+EXPORT_SYMBOL_GPL(rtc_update_irq_enable);
+
+
+/**
+ * rtc_handle_legacy_irq - AIE, UIE and PIE event hook
+ * @rtc: pointer to the rtc device
+ *
+ * This function is called when an AIE, UIE or PIE mode interrupt
+ * has occurred (or been emulated).
+ *
+ * Triggers the registered irq_task function callback.
+ */
+void rtc_handle_legacy_irq(struct rtc_device *rtc, int num, int mode)
+{
+ unsigned long flags;
+
+ /* mark one irq of the appropriate mode */
+ spin_lock_irqsave(&rtc->irq_lock, flags);
+ rtc->irq_data = (rtc->irq_data + (num << 8)) | (RTC_IRQF|mode);
+ spin_unlock_irqrestore(&rtc->irq_lock, flags);
+
+ /* call the task func */
+ spin_lock_irqsave(&rtc->irq_task_lock, flags);
+ if (rtc->irq_task)
+ rtc->irq_task->func(rtc->irq_task->private_data);
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+
+ wake_up_interruptible(&rtc->irq_queue);
+ kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
+}
+
+
+/**
+ * rtc_aie_update_irq - AIE mode rtctimer hook
+ * @private: pointer to the rtc_device
+ *
+ * This functions is called when the aie_timer expires.
+ */
+void rtc_aie_update_irq(void *private)
+{
+ struct rtc_device *rtc = (struct rtc_device *)private;
+ rtc_handle_legacy_irq(rtc, 1, RTC_AF);
+}
+
+
+/**
+ * rtc_uie_update_irq - UIE mode rtctimer hook
+ * @private: pointer to the rtc_device
+ *
+ * This functions is called when the uie_timer expires.
+ */
+void rtc_uie_update_irq(void *private)
+{
+ struct rtc_device *rtc = (struct rtc_device *)private;
+ rtc_handle_legacy_irq(rtc, 1, RTC_UF);
+}
+
+
+/**
+ * rtc_pie_update_irq - PIE mode hrtimer hook
+ * @timer: pointer to the pie mode hrtimer
+ *
+ * This function is used to emulate PIE mode interrupts
+ * using an hrtimer. This function is called when the periodic
+ * hrtimer expires.
+ */
+enum hrtimer_restart rtc_pie_update_irq(struct hrtimer *timer)
+{
+ struct rtc_device *rtc;
+ ktime_t period;
+ int count;
+ rtc = container_of(timer, struct rtc_device, pie_timer);
+
+ period = ktime_set(0, NSEC_PER_SEC/rtc->irq_freq);
+ count = hrtimer_forward_now(timer, period);
+
+ rtc_handle_legacy_irq(rtc, count, RTC_PF);
+
+ return HRTIMER_RESTART;
+}
+
+/**
+ * rtc_update_irq - Triggered when a RTC interrupt occurs.
+ * @rtc: the rtc device
+ * @num: how many irqs are being reported (usually one)
+ * @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
+ * Context: any
+ */
+void rtc_update_irq(struct rtc_device *rtc,
+ unsigned long num, unsigned long events)
+{
+ if (unlikely(IS_ERR_OR_NULL(rtc)))
+ return;
+
+ pm_stay_awake(rtc->dev.parent);
+ schedule_work(&rtc->irqwork);
+}
+EXPORT_SYMBOL_GPL(rtc_update_irq);
+
+static int __rtc_match(struct device *dev, const void *data)
+{
+ const char *name = data;
+
+ if (strcmp(dev_name(dev), name) == 0)
+ return 1;
+ return 0;
+}
+
+struct rtc_device *rtc_class_open(const char *name)
+{
+ struct device *dev;
+ struct rtc_device *rtc = NULL;
+
+ dev = class_find_device(rtc_class, NULL, name, __rtc_match);
+ if (dev)
+ rtc = to_rtc_device(dev);
+
+ if (rtc) {
+ if (!try_module_get(rtc->owner)) {
+ put_device(dev);
+ rtc = NULL;
+ }
+ }
+
+ return rtc;
+}
+EXPORT_SYMBOL_GPL(rtc_class_open);
+
+void rtc_class_close(struct rtc_device *rtc)
+{
+ module_put(rtc->owner);
+ put_device(&rtc->dev);
+}
+EXPORT_SYMBOL_GPL(rtc_class_close);
+
+int rtc_irq_register(struct rtc_device *rtc, struct rtc_task *task)
+{
+ int retval = -EBUSY;
+
+ if (task == NULL || task->func == NULL)
+ return -EINVAL;
+
+ /* Cannot register while the char dev is in use */
+ if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))
+ return -EBUSY;
+
+ spin_lock_irq(&rtc->irq_task_lock);
+ if (rtc->irq_task == NULL) {
+ rtc->irq_task = task;
+ retval = 0;
+ }
+ spin_unlock_irq(&rtc->irq_task_lock);
+
+ clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(rtc_irq_register);
+
+void rtc_irq_unregister(struct rtc_device *rtc, struct rtc_task *task)
+{
+ spin_lock_irq(&rtc->irq_task_lock);
+ if (rtc->irq_task == task)
+ rtc->irq_task = NULL;
+ spin_unlock_irq(&rtc->irq_task_lock);
+}
+EXPORT_SYMBOL_GPL(rtc_irq_unregister);
+
+static int rtc_update_hrtimer(struct rtc_device *rtc, int enabled)
+{
+ /*
+ * We always cancel the timer here first, because otherwise
+ * we could run into BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
+ * when we manage to start the timer before the callback
+ * returns HRTIMER_RESTART.
+ *
+ * We cannot use hrtimer_cancel() here as a running callback
+ * could be blocked on rtc->irq_task_lock and hrtimer_cancel()
+ * would spin forever.
+ */
+ if (hrtimer_try_to_cancel(&rtc->pie_timer) < 0)
+ return -1;
+
+ if (enabled) {
+ ktime_t period = ktime_set(0, NSEC_PER_SEC / rtc->irq_freq);
+
+ hrtimer_start(&rtc->pie_timer, period, HRTIMER_MODE_REL);
+ }
+ return 0;
+}
+
+/**
+ * rtc_irq_set_state - enable/disable 2^N Hz periodic IRQs
+ * @rtc: the rtc device
+ * @task: currently registered with rtc_irq_register()
+ * @enabled: true to enable periodic IRQs
+ * Context: any
+ *
+ * Note that rtc_irq_set_freq() should previously have been used to
+ * specify the desired frequency of periodic IRQ task->func() callbacks.
+ */
+int rtc_irq_set_state(struct rtc_device *rtc, struct rtc_task *task, int enabled)
+{
+ int err = 0;
+ unsigned long flags;
+
+retry:
+ spin_lock_irqsave(&rtc->irq_task_lock, flags);
+ if (rtc->irq_task != NULL && task == NULL)
+ err = -EBUSY;
+ else if (rtc->irq_task != task)
+ err = -EACCES;
+ else {
+ if (rtc_update_hrtimer(rtc, enabled) < 0) {
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+ cpu_relax();
+ goto retry;
+ }
+ rtc->pie_enabled = enabled;
+ }
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_irq_set_state);
+
+/**
+ * rtc_irq_set_freq - set 2^N Hz periodic IRQ frequency for IRQ
+ * @rtc: the rtc device
+ * @task: currently registered with rtc_irq_register()
+ * @freq: positive frequency with which task->func() will be called
+ * Context: any
+ *
+ * Note that rtc_irq_set_state() is used to enable or disable the
+ * periodic IRQs.
+ */
+int rtc_irq_set_freq(struct rtc_device *rtc, struct rtc_task *task, int freq)
+{
+ int err = 0;
+ unsigned long flags;
+
+ if (freq <= 0 || freq > RTC_MAX_FREQ)
+ return -EINVAL;
+retry:
+ spin_lock_irqsave(&rtc->irq_task_lock, flags);
+ if (rtc->irq_task != NULL && task == NULL)
+ err = -EBUSY;
+ else if (rtc->irq_task != task)
+ err = -EACCES;
+ else {
+ rtc->irq_freq = freq;
+ if (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) {
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+ cpu_relax();
+ goto retry;
+ }
+ }
+ spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
+ return err;
+}
+EXPORT_SYMBOL_GPL(rtc_irq_set_freq);
+
+/**
+ * rtc_timer_enqueue - Adds a rtc_timer to the rtc_device timerqueue
+ * @rtc rtc device
+ * @timer timer being added.
+ *
+ * Enqueues a timer onto the rtc devices timerqueue and sets
+ * the next alarm event appropriately.
+ *
+ * Sets the enabled bit on the added timer.
+ *
+ * Must hold ops_lock for proper serialization of timerqueue
+ */
+static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
+{
+ timer->enabled = 1;
+ timerqueue_add(&rtc->timerqueue, &timer->node);
+ if (&timer->node == timerqueue_getnext(&rtc->timerqueue)) {
+ struct rtc_wkalrm alarm;
+ int err;
+ alarm.time = rtc_ktime_to_tm(timer->node.expires);
+ alarm.enabled = 1;
+ err = __rtc_set_alarm(rtc, &alarm);
+ if (err == -ETIME) {
+ pm_stay_awake(rtc->dev.parent);
+ schedule_work(&rtc->irqwork);
+ } else if (err) {
+ timerqueue_del(&rtc->timerqueue, &timer->node);
+ timer->enabled = 0;
+ return err;
+ }
+ }
+ return 0;
+}
+
+static void rtc_alarm_disable(struct rtc_device *rtc)
+{
+ if (!rtc->ops || !rtc->ops->alarm_irq_enable)
+ return;
+
+ rtc->ops->alarm_irq_enable(rtc->dev.parent, false);
+}
+
+/**
+ * rtc_timer_remove - Removes a rtc_timer from the rtc_device timerqueue
+ * @rtc rtc device
+ * @timer timer being removed.
+ *
+ * Removes a timer onto the rtc devices timerqueue and sets
+ * the next alarm event appropriately.
+ *
+ * Clears the enabled bit on the removed timer.
+ *
+ * Must hold ops_lock for proper serialization of timerqueue
+ */
+static void rtc_timer_remove(struct rtc_device *rtc, struct rtc_timer *timer)
+{
+ struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue);
+ timerqueue_del(&rtc->timerqueue, &timer->node);
+ timer->enabled = 0;
+ if (next == &timer->node) {
+ struct rtc_wkalrm alarm;
+ int err;
+ next = timerqueue_getnext(&rtc->timerqueue);
+ if (!next) {
+ rtc_alarm_disable(rtc);
+ return;
+ }
+ alarm.time = rtc_ktime_to_tm(next->expires);
+ alarm.enabled = 1;
+ err = __rtc_set_alarm(rtc, &alarm);
+ if (err == -ETIME) {
+ pm_stay_awake(rtc->dev.parent);
+ schedule_work(&rtc->irqwork);
+ }
+ }
+}
+
+/**
+ * rtc_timer_do_work - Expires rtc timers
+ * @rtc rtc device
+ * @timer timer being removed.
+ *
+ * Expires rtc timers. Reprograms next alarm event if needed.
+ * Called via worktask.
+ *
+ * Serializes access to timerqueue via ops_lock mutex
+ */
+void rtc_timer_do_work(struct work_struct *work)
+{
+ struct rtc_timer *timer;
+ struct timerqueue_node *next;
+ ktime_t now;
+ struct rtc_time tm;
+
+ struct rtc_device *rtc =
+ container_of(work, struct rtc_device, irqwork);
+
+ mutex_lock(&rtc->ops_lock);
+again:
+ __rtc_read_time(rtc, &tm);
+ now = rtc_tm_to_ktime(tm);
+ while ((next = timerqueue_getnext(&rtc->timerqueue))) {
+ if (next->expires.tv64 > now.tv64)
+ break;
+
+ /* expire timer */
+ timer = container_of(next, struct rtc_timer, node);
+ timerqueue_del(&rtc->timerqueue, &timer->node);
+ timer->enabled = 0;
+ if (timer->task.func)
+ timer->task.func(timer->task.private_data);
+
+ /* Re-add/fwd periodic timers */
+ if (ktime_to_ns(timer->period)) {
+ timer->node.expires = ktime_add(timer->node.expires,
+ timer->period);
+ timer->enabled = 1;
+ timerqueue_add(&rtc->timerqueue, &timer->node);
+ }
+ }
+
+ /* Set next alarm */
+ if (next) {
+ struct rtc_wkalrm alarm;
+ int err;
+ int retry = 3;
+
+ alarm.time = rtc_ktime_to_tm(next->expires);
+ alarm.enabled = 1;
+reprogram:
+ err = __rtc_set_alarm(rtc, &alarm);
+ if (err == -ETIME)
+ goto again;
+ else if (err) {
+ if (retry-- > 0)
+ goto reprogram;
+
+ timer = container_of(next, struct rtc_timer, node);
+ timerqueue_del(&rtc->timerqueue, &timer->node);
+ timer->enabled = 0;
+ dev_err(&rtc->dev, "__rtc_set_alarm: err=%d\n", err);
+ goto again;
+ }
+ } else
+ rtc_alarm_disable(rtc);
+
+ pm_relax(rtc->dev.parent);
+ mutex_unlock(&rtc->ops_lock);
+}
+
+
+/* rtc_timer_init - Initializes an rtc_timer
+ * @timer: timer to be intiialized
+ * @f: function pointer to be called when timer fires
+ * @data: private data passed to function pointer
+ *
+ * Kernel interface to initializing an rtc_timer.
+ */
+void rtc_timer_init(struct rtc_timer *timer, void (*f)(void *p), void *data)
+{
+ timerqueue_init(&timer->node);
+ timer->enabled = 0;
+ timer->task.func = f;
+ timer->task.private_data = data;
+}
+
+/* rtc_timer_start - Sets an rtc_timer to fire in the future
+ * @ rtc: rtc device to be used
+ * @ timer: timer being set
+ * @ expires: time at which to expire the timer
+ * @ period: period that the timer will recur
+ *
+ * Kernel interface to set an rtc_timer
+ */
+int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer,
+ ktime_t expires, ktime_t period)
+{
+ int ret = 0;
+ mutex_lock(&rtc->ops_lock);
+ if (timer->enabled)
+ rtc_timer_remove(rtc, timer);
+
+ timer->node.expires = expires;
+ timer->period = period;
+
+ ret = rtc_timer_enqueue(rtc, timer);
+
+ mutex_unlock(&rtc->ops_lock);
+ return ret;
+}
+
+/* rtc_timer_cancel - Stops an rtc_timer
+ * @ rtc: rtc device to be used
+ * @ timer: timer being set
+ *
+ * Kernel interface to cancel an rtc_timer
+ */
+int rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer)
+{
+ int ret = 0;
+ mutex_lock(&rtc->ops_lock);
+ if (timer->enabled)
+ rtc_timer_remove(rtc, timer);
+ mutex_unlock(&rtc->ops_lock);
+ return ret;
+}
+
+
diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c
new file mode 100644
index 000000000..7df0579d9
--- /dev/null
+++ b/drivers/rtc/rtc-88pm80x.c
@@ -0,0 +1,366 @@
+/*
+ * Real Time Clock driver for Marvell 88PM80x PMIC
+ *
+ * Copyright (c) 2012 Marvell International Ltd.
+ * Wenzeng Chen<wzch@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/rtc.h>
+
+#define PM800_RTC_COUNTER1 (0xD1)
+#define PM800_RTC_COUNTER2 (0xD2)
+#define PM800_RTC_COUNTER3 (0xD3)
+#define PM800_RTC_COUNTER4 (0xD4)
+#define PM800_RTC_EXPIRE1_1 (0xD5)
+#define PM800_RTC_EXPIRE1_2 (0xD6)
+#define PM800_RTC_EXPIRE1_3 (0xD7)
+#define PM800_RTC_EXPIRE1_4 (0xD8)
+#define PM800_RTC_TRIM1 (0xD9)
+#define PM800_RTC_TRIM2 (0xDA)
+#define PM800_RTC_TRIM3 (0xDB)
+#define PM800_RTC_TRIM4 (0xDC)
+#define PM800_RTC_EXPIRE2_1 (0xDD)
+#define PM800_RTC_EXPIRE2_2 (0xDE)
+#define PM800_RTC_EXPIRE2_3 (0xDF)
+#define PM800_RTC_EXPIRE2_4 (0xE0)
+
+#define PM800_POWER_DOWN_LOG1 (0xE5)
+#define PM800_POWER_DOWN_LOG2 (0xE6)
+
+struct pm80x_rtc_info {
+ struct pm80x_chip *chip;
+ struct regmap *map;
+ struct rtc_device *rtc_dev;
+ struct device *dev;
+ struct delayed_work calib_work;
+
+ int irq;
+ int vrtc;
+};
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data;
+ int mask;
+
+ mask = PM800_ALARM | PM800_ALARM_WAKEUP;
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN,
+ mask);
+ rtc_update_irq(info->rtc_dev, 1, RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ regmap_update_bits(info->map, PM800_RTC_CONTROL,
+ PM800_ALARM1_EN, PM800_ALARM1_EN);
+ else
+ regmap_update_bits(info->map, PM800_RTC_CONTROL,
+ PM800_ALARM1_EN, 0);
+ return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ */
+static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
+ struct rtc_time *alrm)
+{
+ unsigned long next_time;
+ unsigned long now_time;
+
+ next->tm_year = now->tm_year;
+ next->tm_mon = now->tm_mon;
+ next->tm_mday = now->tm_mday;
+ next->tm_hour = alrm->tm_hour;
+ next->tm_min = alrm->tm_min;
+ next->tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(now, &now_time);
+ rtc_tm_to_time(next, &next_time);
+
+ if (next_time < now_time) {
+ /* Advance one day */
+ next_time += 60 * 60 * 24;
+ rtc_time_to_tm(next_time, next);
+ }
+}
+
+static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+ base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ /* load 32-bit read-only counter */
+ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+ rtc_time_to_tm(ticks, tm);
+ return 0;
+}
+
+static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+ if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ dev_dbg(info->dev,
+ "Set time %d out of range. Please set time between 1970 to 2038.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ rtc_tm_to_time(tm, &ticks);
+
+ /* load 32-bit read-only counter */
+ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ticks - data;
+ dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+ buf[0] = base & 0xFF;
+ buf[1] = (base >> 8) & 0xFF;
+ buf[2] = (base >> 16) & 0xFF;
+ buf[3] = (base >> 24) & 0xFF;
+ regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+
+ return 0;
+}
+
+static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+ int ret;
+
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+ base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &alrm->time);
+ regmap_read(info->map, PM800_RTC_CONTROL, &ret);
+ alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0;
+ alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0;
+ return 0;
+}
+
+static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm80x_rtc_info *info = dev_get_drvdata(dev);
+ struct rtc_time now_tm, alarm_tm;
+ unsigned long ticks, base, data;
+ unsigned char buf[4];
+ int mask;
+
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
+
+ regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
+ base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
+
+ /* load 32-bit read-only counter */
+ regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &now_tm);
+ dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks);
+ rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
+ /* get new ticks for alarm in 24 hours */
+ rtc_tm_to_time(&alarm_tm, &ticks);
+ dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks);
+ data = ticks - base;
+
+ buf[0] = data & 0xff;
+ buf[1] = (data >> 8) & 0xff;
+ buf[2] = (data >> 16) & 0xff;
+ buf[3] = (data >> 24) & 0xff;
+ regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
+ if (alrm->enabled) {
+ mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask);
+ } else {
+ mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, mask,
+ PM800_ALARM | PM800_ALARM_WAKEUP);
+ }
+ return 0;
+}
+
+static const struct rtc_class_ops pm80x_rtc_ops = {
+ .read_time = pm80x_rtc_read_time,
+ .set_time = pm80x_rtc_set_time,
+ .read_alarm = pm80x_rtc_read_alarm,
+ .set_alarm = pm80x_rtc_set_alarm,
+ .alarm_irq_enable = pm80x_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int pm80x_rtc_suspend(struct device *dev)
+{
+ return pm80x_dev_suspend(dev);
+}
+
+static int pm80x_rtc_resume(struct device *dev)
+{
+ return pm80x_dev_resume(dev);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume);
+
+static int pm80x_rtc_probe(struct platform_device *pdev)
+{
+ struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm80x_platform_data *pm80x_pdata =
+ dev_get_platdata(pdev->dev.parent);
+ struct pm80x_rtc_pdata *pdata = NULL;
+ struct pm80x_rtc_info *info;
+ struct rtc_time tm;
+ unsigned long ticks = 0;
+ int ret;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (pdata == NULL)
+ dev_warn(&pdev->dev, "No platform data!\n");
+
+ info =
+ devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ info->chip = chip;
+ info->map = chip->regmap;
+ if (!info->map) {
+ dev_err(&pdev->dev, "no regmap!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ info->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, info);
+
+ ret = pm80x_request_irq(chip, info->irq, rtc_update_handler,
+ IRQF_ONESHOT, "rtc", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, ret);
+ goto out;
+ }
+
+ ret = pm80x_rtc_read_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read initial time.\n");
+ goto out_rtc;
+ }
+ if ((tm.tm_year < 70) || (tm.tm_year > 138)) {
+ tm.tm_year = 70;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ ret = pm80x_rtc_set_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set initial time.\n");
+ goto out_rtc;
+ }
+ }
+ rtc_tm_to_time(&tm, &ticks);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "88pm80x-rtc",
+ &pm80x_rtc_ops, THIS_MODULE);
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ goto out_rtc;
+ }
+ /*
+ * enable internal XO instead of internal 3.25MHz clock since it can
+ * free running in PMIC power-down state.
+ */
+ regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO,
+ PM800_RTC1_USE_XO);
+
+ if (pm80x_pdata) {
+ pdata = pm80x_pdata->rtc;
+ if (pdata)
+ info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+out_rtc:
+ pm80x_free_irq(chip, info->irq, info);
+out:
+ return ret;
+}
+
+static int pm80x_rtc_remove(struct platform_device *pdev)
+{
+ struct pm80x_rtc_info *info = platform_get_drvdata(pdev);
+ pm80x_free_irq(info->chip, info->irq, info);
+ return 0;
+}
+
+static struct platform_driver pm80x_rtc_driver = {
+ .driver = {
+ .name = "88pm80x-rtc",
+ .pm = &pm80x_rtc_pm_ops,
+ },
+ .probe = pm80x_rtc_probe,
+ .remove = pm80x_rtc_remove,
+};
+
+module_platform_driver(pm80x_rtc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x RTC driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-rtc");
diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c
new file mode 100644
index 000000000..19e53b3b8
--- /dev/null
+++ b/drivers/rtc/rtc-88pm860x.c
@@ -0,0 +1,461 @@
+/*
+ * Real Time Clock driver for Marvell 88PM860x PMIC
+ *
+ * Copyright (c) 2010 Marvell International Ltd.
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/88pm860x.h>
+
+#define VRTC_CALIBRATION
+
+struct pm860x_rtc_info {
+ struct pm860x_chip *chip;
+ struct i2c_client *i2c;
+ struct rtc_device *rtc_dev;
+ struct device *dev;
+ struct delayed_work calib_work;
+
+ int irq;
+ int vrtc;
+ int (*sync)(unsigned int ticks);
+};
+
+#define REG_VRTC_MEAS1 0x7D
+
+#define REG0_ADDR 0xB0
+#define REG1_ADDR 0xB2
+#define REG2_ADDR 0xB4
+#define REG3_ADDR 0xB6
+
+#define REG0_DATA 0xB1
+#define REG1_DATA 0xB3
+#define REG2_DATA 0xB5
+#define REG3_DATA 0xB7
+
+/* bit definitions of Measurement Enable Register 2 (0x51) */
+#define MEAS2_VRTC (1 << 0)
+
+/* bit definitions of RTC Register 1 (0xA0) */
+#define ALARM_EN (1 << 3)
+#define ALARM_WAKEUP (1 << 4)
+#define ALARM (1 << 5)
+#define RTC1_USE_XO (1 << 7)
+
+#define VRTC_CALIB_INTERVAL (HZ * 60 * 10) /* 10 minutes */
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct pm860x_rtc_info *info = (struct pm860x_rtc_info *)data;
+ int mask;
+
+ mask = ALARM | ALARM_WAKEUP;
+ pm860x_set_bits(info->i2c, PM8607_RTC1, mask | ALARM_EN, mask);
+ rtc_update_irq(info->rtc_dev, 1, RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int pm860x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, ALARM_EN);
+ else
+ pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0);
+ return 0;
+}
+
+/*
+ * Calculate the next alarm time given the requested alarm time mask
+ * and the current time.
+ */
+static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
+ struct rtc_time *alrm)
+{
+ unsigned long next_time;
+ unsigned long now_time;
+
+ next->tm_year = now->tm_year;
+ next->tm_mon = now->tm_mon;
+ next->tm_mday = now->tm_mday;
+ next->tm_hour = alrm->tm_hour;
+ next->tm_min = alrm->tm_min;
+ next->tm_sec = alrm->tm_sec;
+
+ rtc_tm_to_time(now, &now_time);
+ rtc_tm_to_time(next, &next_time);
+
+ if (next_time < now_time) {
+ /* Advance one day */
+ next_time += 60 * 60 * 24;
+ rtc_time_to_tm(next_time, next);
+ }
+}
+
+static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[8];
+ unsigned long ticks, base, data;
+
+ pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
+ dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+
+ /* load 32-bit read-only counter */
+ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, tm);
+
+ return 0;
+}
+
+static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[4];
+ unsigned long ticks, base, data;
+
+ if ((tm->tm_year < 70) || (tm->tm_year > 138)) {
+ dev_dbg(info->dev, "Set time %d out of range. "
+ "Please set time between 1970 to 2038.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ rtc_tm_to_time(tm, &ticks);
+
+ /* load 32-bit read-only counter */
+ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ base = ticks - data;
+ dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ pm860x_page_reg_write(info->i2c, REG0_DATA, (base >> 24) & 0xFF);
+ pm860x_page_reg_write(info->i2c, REG1_DATA, (base >> 16) & 0xFF);
+ pm860x_page_reg_write(info->i2c, REG2_DATA, (base >> 8) & 0xFF);
+ pm860x_page_reg_write(info->i2c, REG3_DATA, base & 0xFF);
+
+ if (info->sync)
+ info->sync(ticks);
+ return 0;
+}
+
+static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[8];
+ unsigned long ticks, base, data;
+ int ret;
+
+ pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
+ dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+
+ pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &alrm->time);
+ ret = pm860x_reg_read(info->i2c, PM8607_RTC1);
+ alrm->enabled = (ret & ALARM_EN) ? 1 : 0;
+ alrm->pending = (ret & (ALARM | ALARM_WAKEUP)) ? 1 : 0;
+ return 0;
+}
+
+static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pm860x_rtc_info *info = dev_get_drvdata(dev);
+ struct rtc_time now_tm, alarm_tm;
+ unsigned long ticks, base, data;
+ unsigned char buf[8];
+ int mask;
+
+ pm860x_set_bits(info->i2c, PM8607_RTC1, ALARM_EN, 0);
+
+ pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
+ dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
+ buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+ base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
+
+ /* load 32-bit read-only counter */
+ pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
+ data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+ ticks = base + data;
+ dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
+ base, data, ticks);
+
+ rtc_time_to_tm(ticks, &now_tm);
+ rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
+ /* get new ticks for alarm in 24 hours */
+ rtc_tm_to_time(&alarm_tm, &ticks);
+ data = ticks - base;
+
+ buf[0] = data & 0xff;
+ buf[1] = (data >> 8) & 0xff;
+ buf[2] = (data >> 16) & 0xff;
+ buf[3] = (data >> 24) & 0xff;
+ pm860x_bulk_write(info->i2c, PM8607_RTC_EXPIRE1, 4, buf);
+ if (alrm->enabled) {
+ mask = ALARM | ALARM_WAKEUP | ALARM_EN;
+ pm860x_set_bits(info->i2c, PM8607_RTC1, mask, mask);
+ } else {
+ mask = ALARM | ALARM_WAKEUP | ALARM_EN;
+ pm860x_set_bits(info->i2c, PM8607_RTC1, mask,
+ ALARM | ALARM_WAKEUP);
+ }
+ return 0;
+}
+
+static const struct rtc_class_ops pm860x_rtc_ops = {
+ .read_time = pm860x_rtc_read_time,
+ .set_time = pm860x_rtc_set_time,
+ .read_alarm = pm860x_rtc_read_alarm,
+ .set_alarm = pm860x_rtc_set_alarm,
+ .alarm_irq_enable = pm860x_rtc_alarm_irq_enable,
+};
+
+#ifdef VRTC_CALIBRATION
+static void calibrate_vrtc_work(struct work_struct *work)
+{
+ struct pm860x_rtc_info *info = container_of(work,
+ struct pm860x_rtc_info, calib_work.work);
+ unsigned char buf[2];
+ unsigned int sum, data, mean, vrtc_set;
+ int i;
+
+ for (i = 0, sum = 0; i < 16; i++) {
+ msleep(100);
+ pm860x_bulk_read(info->i2c, REG_VRTC_MEAS1, 2, buf);
+ data = (buf[0] << 4) | buf[1];
+ data = (data * 5400) >> 12; /* convert to mv */
+ sum += data;
+ }
+ mean = sum >> 4;
+ vrtc_set = 2700 + (info->vrtc & 0x3) * 200;
+ dev_dbg(info->dev, "mean:%d, vrtc_set:%d\n", mean, vrtc_set);
+
+ sum = pm860x_reg_read(info->i2c, PM8607_RTC_MISC1);
+ data = sum & 0x3;
+ if ((mean + 200) < vrtc_set) {
+ /* try higher voltage */
+ if (++data == 4)
+ goto out;
+ data = (sum & 0xf8) | (data & 0x3);
+ pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data);
+ } else if ((mean - 200) > vrtc_set) {
+ /* try lower voltage */
+ if (data-- == 0)
+ goto out;
+ data = (sum & 0xf8) | (data & 0x3);
+ pm860x_reg_write(info->i2c, PM8607_RTC_MISC1, data);
+ } else
+ goto out;
+ dev_dbg(info->dev, "set 0x%x to RTC_MISC1\n", data);
+ /* trigger next calibration since VRTC is updated */
+ schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL);
+ return;
+out:
+ /* disable measurement */
+ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0);
+ dev_dbg(info->dev, "finish VRTC calibration\n");
+ return;
+}
+#endif
+
+#ifdef CONFIG_OF
+static int pm860x_rtc_dt_init(struct platform_device *pdev,
+ struct pm860x_rtc_info *info)
+{
+ struct device_node *np = pdev->dev.parent->of_node;
+ int ret;
+ if (!np)
+ return -ENODEV;
+ np = of_get_child_by_name(np, "rtc");
+ if (!np) {
+ dev_err(&pdev->dev, "failed to find rtc node\n");
+ return -ENODEV;
+ }
+ ret = of_property_read_u32(np, "marvell,88pm860x-vrtc", &info->vrtc);
+ if (ret)
+ info->vrtc = 0;
+ of_node_put(np);
+ return 0;
+}
+#else
+#define pm860x_rtc_dt_init(x, y) (-1)
+#endif
+
+static int pm860x_rtc_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_rtc_pdata *pdata = NULL;
+ struct pm860x_rtc_info *info;
+ struct rtc_time tm;
+ unsigned long ticks = 0;
+ int ret;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ return info->irq;
+ }
+
+ info->chip = chip;
+ info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+ info->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, info);
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+ rtc_update_handler, IRQF_ONESHOT, "rtc",
+ info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, ret);
+ return ret;
+ }
+
+ /* set addresses of 32-bit base value for RTC time */
+ pm860x_page_reg_write(info->i2c, REG0_ADDR, REG0_DATA);
+ pm860x_page_reg_write(info->i2c, REG1_ADDR, REG1_DATA);
+ pm860x_page_reg_write(info->i2c, REG2_ADDR, REG2_DATA);
+ pm860x_page_reg_write(info->i2c, REG3_ADDR, REG3_DATA);
+
+ ret = pm860x_rtc_read_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read initial time.\n");
+ return ret;
+ }
+ if ((tm.tm_year < 70) || (tm.tm_year > 138)) {
+ tm.tm_year = 70;
+ tm.tm_mon = 0;
+ tm.tm_mday = 1;
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ ret = pm860x_rtc_set_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to set initial time.\n");
+ return ret;
+ }
+ }
+ rtc_tm_to_time(&tm, &ticks);
+ if (pm860x_rtc_dt_init(pdev, info)) {
+ if (pdata && pdata->sync) {
+ pdata->sync(ticks);
+ info->sync = pdata->sync;
+ }
+ }
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "88pm860x-rtc",
+ &pm860x_rtc_ops, THIS_MODULE);
+ ret = PTR_ERR(info->rtc_dev);
+ if (IS_ERR(info->rtc_dev)) {
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * enable internal XO instead of internal 3.25MHz clock since it can
+ * free running in PMIC power-down state.
+ */
+ pm860x_set_bits(info->i2c, PM8607_RTC1, RTC1_USE_XO, RTC1_USE_XO);
+
+#ifdef VRTC_CALIBRATION
+ /* <00> -- 2.7V, <01> -- 2.9V, <10> -- 3.1V, <11> -- 3.3V */
+ if (pm860x_rtc_dt_init(pdev, info)) {
+ if (pdata && pdata->vrtc)
+ info->vrtc = pdata->vrtc & 0x3;
+ else
+ info->vrtc = 1;
+ }
+ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, MEAS2_VRTC);
+
+ /* calibrate VRTC */
+ INIT_DELAYED_WORK(&info->calib_work, calibrate_vrtc_work);
+ schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL);
+#endif /* VRTC_CALIBRATION */
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+}
+
+static int pm860x_rtc_remove(struct platform_device *pdev)
+{
+ struct pm860x_rtc_info *info = platform_get_drvdata(pdev);
+
+#ifdef VRTC_CALIBRATION
+ flush_scheduled_work();
+ /* disable measurement */
+ pm860x_set_bits(info->i2c, PM8607_MEAS_EN2, MEAS2_VRTC, 0);
+#endif /* VRTC_CALIBRATION */
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag |= 1 << PM8607_IRQ_RTC;
+ return 0;
+}
+static int pm860x_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag &= ~(1 << PM8607_IRQ_RTC);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_rtc_pm_ops, pm860x_rtc_suspend, pm860x_rtc_resume);
+
+static struct platform_driver pm860x_rtc_driver = {
+ .driver = {
+ .name = "88pm860x-rtc",
+ .pm = &pm860x_rtc_pm_ops,
+ },
+ .probe = pm860x_rtc_probe,
+ .remove = pm860x_rtc_remove,
+};
+
+module_platform_driver(pm860x_rtc_driver);
+
+MODULE_DESCRIPTION("Marvell 88PM860x RTC driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ab-b5ze-s3.c b/drivers/rtc/rtc-ab-b5ze-s3.c
new file mode 100644
index 000000000..b5cbc1bf5
--- /dev/null
+++ b/drivers/rtc/rtc-ab-b5ze-s3.c
@@ -0,0 +1,1035 @@
+/*
+ * rtc-ab-b5ze-s3 - Driver for Abracon AB-RTCMC-32.768Khz-B5ZE-S3
+ * I2C RTC / Alarm chip
+ *
+ * Copyright (C) 2014, Arnaud EBALARD <arno@natisbad.org>
+ *
+ * Detailed datasheet of the chip is available here:
+ *
+ * http://www.abracon.com/realtimeclock/AB-RTCMC-32.768kHz-B5ZE-S3-Application-Manual.pdf
+ *
+ * This work is based on ISL12057 driver (drivers/rtc/rtc-isl12057.c).
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/interrupt.h>
+
+#define DRV_NAME "rtc-ab-b5ze-s3"
+
+/* Control section */
+#define ABB5ZES3_REG_CTRL1 0x00 /* Control 1 register */
+#define ABB5ZES3_REG_CTRL1_CIE BIT(0) /* Pulse interrupt enable */
+#define ABB5ZES3_REG_CTRL1_AIE BIT(1) /* Alarm interrupt enable */
+#define ABB5ZES3_REG_CTRL1_SIE BIT(2) /* Second interrupt enable */
+#define ABB5ZES3_REG_CTRL1_PM BIT(3) /* 24h/12h mode */
+#define ABB5ZES3_REG_CTRL1_SR BIT(4) /* Software reset */
+#define ABB5ZES3_REG_CTRL1_STOP BIT(5) /* RTC circuit enable */
+#define ABB5ZES3_REG_CTRL1_CAP BIT(7)
+
+#define ABB5ZES3_REG_CTRL2 0x01 /* Control 2 register */
+#define ABB5ZES3_REG_CTRL2_CTBIE BIT(0) /* Countdown timer B int. enable */
+#define ABB5ZES3_REG_CTRL2_CTAIE BIT(1) /* Countdown timer A int. enable */
+#define ABB5ZES3_REG_CTRL2_WTAIE BIT(2) /* Watchdog timer A int. enable */
+#define ABB5ZES3_REG_CTRL2_AF BIT(3) /* Alarm interrupt status */
+#define ABB5ZES3_REG_CTRL2_SF BIT(4) /* Second interrupt status */
+#define ABB5ZES3_REG_CTRL2_CTBF BIT(5) /* Countdown timer B int. status */
+#define ABB5ZES3_REG_CTRL2_CTAF BIT(6) /* Countdown timer A int. status */
+#define ABB5ZES3_REG_CTRL2_WTAF BIT(7) /* Watchdog timer A int. status */
+
+#define ABB5ZES3_REG_CTRL3 0x02 /* Control 3 register */
+#define ABB5ZES3_REG_CTRL3_PM2 BIT(7) /* Power Management bit 2 */
+#define ABB5ZES3_REG_CTRL3_PM1 BIT(6) /* Power Management bit 1 */
+#define ABB5ZES3_REG_CTRL3_PM0 BIT(5) /* Power Management bit 0 */
+#define ABB5ZES3_REG_CTRL3_BSF BIT(3) /* Battery switchover int. status */
+#define ABB5ZES3_REG_CTRL3_BLF BIT(2) /* Battery low int. status */
+#define ABB5ZES3_REG_CTRL3_BSIE BIT(1) /* Battery switchover int. enable */
+#define ABB5ZES3_REG_CTRL3_BLIE BIT(0) /* Battery low int. enable */
+
+#define ABB5ZES3_CTRL_SEC_LEN 3
+
+/* RTC section */
+#define ABB5ZES3_REG_RTC_SC 0x03 /* RTC Seconds register */
+#define ABB5ZES3_REG_RTC_SC_OSC BIT(7) /* Clock integrity status */
+#define ABB5ZES3_REG_RTC_MN 0x04 /* RTC Minutes register */
+#define ABB5ZES3_REG_RTC_HR 0x05 /* RTC Hours register */
+#define ABB5ZES3_REG_RTC_HR_PM BIT(5) /* RTC Hours PM bit */
+#define ABB5ZES3_REG_RTC_DT 0x06 /* RTC Date register */
+#define ABB5ZES3_REG_RTC_DW 0x07 /* RTC Day of the week register */
+#define ABB5ZES3_REG_RTC_MO 0x08 /* RTC Month register */
+#define ABB5ZES3_REG_RTC_YR 0x09 /* RTC Year register */
+
+#define ABB5ZES3_RTC_SEC_LEN 7
+
+/* Alarm section (enable bits are all active low) */
+#define ABB5ZES3_REG_ALRM_MN 0x0A /* Alarm - minute register */
+#define ABB5ZES3_REG_ALRM_MN_AE BIT(7) /* Minute enable */
+#define ABB5ZES3_REG_ALRM_HR 0x0B /* Alarm - hours register */
+#define ABB5ZES3_REG_ALRM_HR_AE BIT(7) /* Hour enable */
+#define ABB5ZES3_REG_ALRM_DT 0x0C /* Alarm - date register */
+#define ABB5ZES3_REG_ALRM_DT_AE BIT(7) /* Date (day of the month) enable */
+#define ABB5ZES3_REG_ALRM_DW 0x0D /* Alarm - day of the week reg. */
+#define ABB5ZES3_REG_ALRM_DW_AE BIT(7) /* Day of the week enable */
+
+#define ABB5ZES3_ALRM_SEC_LEN 4
+
+/* Frequency offset section */
+#define ABB5ZES3_REG_FREQ_OF 0x0E /* Frequency offset register */
+#define ABB5ZES3_REG_FREQ_OF_MODE 0x0E /* Offset mode: 2 hours / minute */
+
+/* CLOCKOUT section */
+#define ABB5ZES3_REG_TIM_CLK 0x0F /* Timer & Clockout register */
+#define ABB5ZES3_REG_TIM_CLK_TAM BIT(7) /* Permanent/pulsed timer A/int. 2 */
+#define ABB5ZES3_REG_TIM_CLK_TBM BIT(6) /* Permanent/pulsed timer B */
+#define ABB5ZES3_REG_TIM_CLK_COF2 BIT(5) /* Clkout Freq bit 2 */
+#define ABB5ZES3_REG_TIM_CLK_COF1 BIT(4) /* Clkout Freq bit 1 */
+#define ABB5ZES3_REG_TIM_CLK_COF0 BIT(3) /* Clkout Freq bit 0 */
+#define ABB5ZES3_REG_TIM_CLK_TAC1 BIT(2) /* Timer A: - 01 : countdown */
+#define ABB5ZES3_REG_TIM_CLK_TAC0 BIT(1) /* - 10 : timer */
+#define ABB5ZES3_REG_TIM_CLK_TBC BIT(0) /* Timer B enable */
+
+/* Timer A Section */
+#define ABB5ZES3_REG_TIMA_CLK 0x10 /* Timer A clock register */
+#define ABB5ZES3_REG_TIMA_CLK_TAQ2 BIT(2) /* Freq bit 2 */
+#define ABB5ZES3_REG_TIMA_CLK_TAQ1 BIT(1) /* Freq bit 1 */
+#define ABB5ZES3_REG_TIMA_CLK_TAQ0 BIT(0) /* Freq bit 0 */
+#define ABB5ZES3_REG_TIMA 0x11 /* Timer A register */
+
+#define ABB5ZES3_TIMA_SEC_LEN 2
+
+/* Timer B Section */
+#define ABB5ZES3_REG_TIMB_CLK 0x12 /* Timer B clock register */
+#define ABB5ZES3_REG_TIMB_CLK_TBW2 BIT(6)
+#define ABB5ZES3_REG_TIMB_CLK_TBW1 BIT(5)
+#define ABB5ZES3_REG_TIMB_CLK_TBW0 BIT(4)
+#define ABB5ZES3_REG_TIMB_CLK_TAQ2 BIT(2)
+#define ABB5ZES3_REG_TIMB_CLK_TAQ1 BIT(1)
+#define ABB5ZES3_REG_TIMB_CLK_TAQ0 BIT(0)
+#define ABB5ZES3_REG_TIMB 0x13 /* Timer B register */
+#define ABB5ZES3_TIMB_SEC_LEN 2
+
+#define ABB5ZES3_MEM_MAP_LEN 0x14
+
+struct abb5zes3_rtc_data {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+ struct mutex lock;
+
+ int irq;
+
+ bool battery_low;
+ bool timer_alarm; /* current alarm is via timer A */
+};
+
+/*
+ * Try and match register bits w/ fixed null values to see whether we
+ * are dealing with an ABB5ZES3. Note: this function is called early
+ * during init and hence does need mutex protection.
+ */
+static int abb5zes3_i2c_validate_chip(struct regmap *regmap)
+{
+ u8 regs[ABB5ZES3_MEM_MAP_LEN];
+ static const u8 mask[ABB5ZES3_MEM_MAP_LEN] = { 0x00, 0x00, 0x10, 0x00,
+ 0x80, 0xc0, 0xc0, 0xf8,
+ 0xe0, 0x00, 0x00, 0x40,
+ 0x40, 0x78, 0x00, 0x00,
+ 0xf8, 0x00, 0x88, 0x00 };
+ int ret, i;
+
+ ret = regmap_bulk_read(regmap, 0, regs, ABB5ZES3_MEM_MAP_LEN);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ABB5ZES3_MEM_MAP_LEN; ++i) {
+ if (regs[i] & mask[i]) /* check if bits are cleared */
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Clear alarm status bit. */
+static int _abb5zes3_rtc_clear_alarm(struct device *dev)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_CTRL2,
+ ABB5ZES3_REG_CTRL2_AF, 0);
+ if (ret)
+ dev_err(dev, "%s: clearing alarm failed (%d)\n", __func__, ret);
+
+ return ret;
+}
+
+/* Enable or disable alarm (i.e. alarm interrupt generation) */
+static int _abb5zes3_rtc_update_alarm(struct device *dev, bool enable)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_CTRL1,
+ ABB5ZES3_REG_CTRL1_AIE,
+ enable ? ABB5ZES3_REG_CTRL1_AIE : 0);
+ if (ret)
+ dev_err(dev, "%s: writing alarm INT failed (%d)\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/* Enable or disable timer (watchdog timer A interrupt generation) */
+static int _abb5zes3_rtc_update_timer(struct device *dev, bool enable)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_CTRL2,
+ ABB5ZES3_REG_CTRL2_WTAIE,
+ enable ? ABB5ZES3_REG_CTRL2_WTAIE : 0);
+ if (ret)
+ dev_err(dev, "%s: writing timer INT failed (%d)\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/*
+ * Note: we only read, so regmap inner lock protection is sufficient, i.e.
+ * we do not need driver's main lock protection.
+ */
+static int _abb5zes3_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ u8 regs[ABB5ZES3_REG_RTC_SC + ABB5ZES3_RTC_SEC_LEN];
+ int ret;
+
+ /*
+ * As we need to read CTRL1 register anyway to access 24/12h
+ * mode bit, we do a single bulk read of both control and RTC
+ * sections (they are consecutive). This also ease indexing
+ * of register values after bulk read.
+ */
+ ret = regmap_bulk_read(data->regmap, ABB5ZES3_REG_CTRL1, regs,
+ sizeof(regs));
+ if (ret) {
+ dev_err(dev, "%s: reading RTC time failed (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* If clock integrity is not guaranteed, do not return a time value */
+ if (regs[ABB5ZES3_REG_RTC_SC] & ABB5ZES3_REG_RTC_SC_OSC) {
+ ret = -ENODATA;
+ goto err;
+ }
+
+ tm->tm_sec = bcd2bin(regs[ABB5ZES3_REG_RTC_SC] & 0x7F);
+ tm->tm_min = bcd2bin(regs[ABB5ZES3_REG_RTC_MN]);
+
+ if (regs[ABB5ZES3_REG_CTRL1] & ABB5ZES3_REG_CTRL1_PM) { /* 12hr mode */
+ tm->tm_hour = bcd2bin(regs[ABB5ZES3_REG_RTC_HR] & 0x1f);
+ if (regs[ABB5ZES3_REG_RTC_HR] & ABB5ZES3_REG_RTC_HR_PM) /* PM */
+ tm->tm_hour += 12;
+ } else { /* 24hr mode */
+ tm->tm_hour = bcd2bin(regs[ABB5ZES3_REG_RTC_HR]);
+ }
+
+ tm->tm_mday = bcd2bin(regs[ABB5ZES3_REG_RTC_DT]);
+ tm->tm_wday = bcd2bin(regs[ABB5ZES3_REG_RTC_DW]);
+ tm->tm_mon = bcd2bin(regs[ABB5ZES3_REG_RTC_MO]) - 1; /* starts at 1 */
+ tm->tm_year = bcd2bin(regs[ABB5ZES3_REG_RTC_YR]) + 100;
+
+ ret = rtc_valid_tm(tm);
+
+err:
+ return ret;
+}
+
+static int abb5zes3_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ u8 regs[ABB5ZES3_REG_RTC_SC + ABB5ZES3_RTC_SEC_LEN];
+ int ret;
+
+ /*
+ * Year register is 8-bit wide and bcd-coded, i.e records values
+ * between 0 and 99. tm_year is an offset from 1900 and we are
+ * interested in the 2000-2099 range, so any value less than 100
+ * is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ regs[ABB5ZES3_REG_RTC_SC] = bin2bcd(tm->tm_sec); /* MSB=0 clears OSC */
+ regs[ABB5ZES3_REG_RTC_MN] = bin2bcd(tm->tm_min);
+ regs[ABB5ZES3_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */
+ regs[ABB5ZES3_REG_RTC_DT] = bin2bcd(tm->tm_mday);
+ regs[ABB5ZES3_REG_RTC_DW] = bin2bcd(tm->tm_wday);
+ regs[ABB5ZES3_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1);
+ regs[ABB5ZES3_REG_RTC_YR] = bin2bcd(tm->tm_year - 100);
+
+ mutex_lock(&data->lock);
+ ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_RTC_SC,
+ regs + ABB5ZES3_REG_RTC_SC,
+ ABB5ZES3_RTC_SEC_LEN);
+ mutex_unlock(&data->lock);
+
+
+ return ret;
+}
+
+/*
+ * Set provided TAQ and Timer A registers (TIMA_CLK and TIMA) based on
+ * given number of seconds.
+ */
+static inline void sec_to_timer_a(u8 secs, u8 *taq, u8 *timer_a)
+{
+ *taq = ABB5ZES3_REG_TIMA_CLK_TAQ1; /* 1Hz */
+ *timer_a = secs;
+}
+
+/*
+ * Return current number of seconds in Timer A. As we only use
+ * timer A with a 1Hz freq, this is what we expect to have.
+ */
+static inline int sec_from_timer_a(u8 *secs, u8 taq, u8 timer_a)
+{
+ if (taq != ABB5ZES3_REG_TIMA_CLK_TAQ1) /* 1Hz */
+ return -EINVAL;
+
+ *secs = timer_a;
+
+ return 0;
+}
+
+/*
+ * Read alarm currently configured via a watchdog timer using timer A. This
+ * is done by reading current RTC time and adding remaining timer time.
+ */
+static int _abb5zes3_rtc_read_timer(struct device *dev,
+ struct rtc_wkalrm *alarm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time rtc_tm, *alarm_tm = &alarm->time;
+ u8 regs[ABB5ZES3_TIMA_SEC_LEN + 1];
+ unsigned long rtc_secs;
+ unsigned int reg;
+ u8 timer_secs;
+ int ret;
+
+ /*
+ * Instead of doing two separate calls, because they are consecutive,
+ * we grab both clockout register and Timer A section. The latter is
+ * used to decide if timer A is enabled (as a watchdog timer).
+ */
+ ret = regmap_bulk_read(data->regmap, ABB5ZES3_REG_TIM_CLK, regs,
+ ABB5ZES3_TIMA_SEC_LEN + 1);
+ if (ret) {
+ dev_err(dev, "%s: reading Timer A section failed (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* get current time ... */
+ ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ goto err;
+
+ /* ... convert to seconds ... */
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err;
+
+ /* ... add remaining timer A time ... */
+ ret = sec_from_timer_a(&timer_secs, regs[1], regs[2]);
+ if (ret)
+ goto err;
+
+ /* ... and convert back. */
+ rtc_time_to_tm(rtc_secs + timer_secs, alarm_tm);
+
+ ret = regmap_read(data->regmap, ABB5ZES3_REG_CTRL2, &reg);
+ if (ret) {
+ dev_err(dev, "%s: reading ctrl reg failed (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ alarm->enabled = !!(reg & ABB5ZES3_REG_CTRL2_WTAIE);
+
+err:
+ return ret;
+}
+
+/* Read alarm currently configured via a RTC alarm registers. */
+static int _abb5zes3_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *alarm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time rtc_tm, *alarm_tm = &alarm->time;
+ unsigned long rtc_secs, alarm_secs;
+ u8 regs[ABB5ZES3_ALRM_SEC_LEN];
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, ABB5ZES3_REG_ALRM_MN, regs,
+ ABB5ZES3_ALRM_SEC_LEN);
+ if (ret) {
+ dev_err(dev, "%s: reading alarm section failed (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ alarm_tm->tm_sec = 0;
+ alarm_tm->tm_min = bcd2bin(regs[0] & 0x7f);
+ alarm_tm->tm_hour = bcd2bin(regs[1] & 0x3f);
+ alarm_tm->tm_mday = bcd2bin(regs[2] & 0x3f);
+ alarm_tm->tm_wday = -1;
+
+ /*
+ * The alarm section does not store year/month. We use the ones in rtc
+ * section as a basis and increment month and then year if needed to get
+ * alarm after current time.
+ */
+ ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ goto err;
+
+ alarm_tm->tm_year = rtc_tm.tm_year;
+ alarm_tm->tm_mon = rtc_tm.tm_mon;
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err;
+
+ ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
+ if (ret)
+ goto err;
+
+ if (alarm_secs < rtc_secs) {
+ if (alarm_tm->tm_mon == 11) {
+ alarm_tm->tm_mon = 0;
+ alarm_tm->tm_year += 1;
+ } else {
+ alarm_tm->tm_mon += 1;
+ }
+ }
+
+ ret = regmap_read(data->regmap, ABB5ZES3_REG_CTRL1, &reg);
+ if (ret) {
+ dev_err(dev, "%s: reading ctrl reg failed (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ alarm->enabled = !!(reg & ABB5ZES3_REG_CTRL1_AIE);
+
+err:
+ return ret;
+}
+
+/*
+ * As the Alarm mechanism supported by the chip is only accurate to the
+ * minute, we use the watchdog timer mechanism provided by timer A
+ * (up to 256 seconds w/ a second accuracy) for low alarm values (below
+ * 4 minutes). Otherwise, we use the common alarm mechanism provided
+ * by the chip. In order for that to work, we keep track of currently
+ * configured timer type via 'timer_alarm' flag in our private data
+ * structure.
+ */
+static int abb5zes3_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ if (data->timer_alarm)
+ ret = _abb5zes3_rtc_read_timer(dev, alarm);
+ else
+ ret = _abb5zes3_rtc_read_alarm(dev, alarm);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+/*
+ * Set alarm using chip alarm mechanism. It is only accurate to the
+ * minute (not the second). The function expects alarm interrupt to
+ * be disabled.
+ */
+static int _abb5zes3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time *alarm_tm = &alarm->time;
+ unsigned long rtc_secs, alarm_secs;
+ u8 regs[ABB5ZES3_ALRM_SEC_LEN];
+ struct rtc_time rtc_tm;
+ int ret, enable = 1;
+
+ ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ goto err;
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err;
+
+ ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
+ if (ret)
+ goto err;
+
+ /* If alarm time is before current time, disable the alarm */
+ if (!alarm->enabled || alarm_secs <= rtc_secs) {
+ enable = 0;
+ } else {
+ /*
+ * Chip only support alarms up to one month in the future. Let's
+ * return an error if we get something after that limit.
+ * Comparison is done by incrementing rtc_tm month field by one
+ * and checking alarm value is still below.
+ */
+ if (rtc_tm.tm_mon == 11) { /* handle year wrapping */
+ rtc_tm.tm_mon = 0;
+ rtc_tm.tm_year += 1;
+ } else {
+ rtc_tm.tm_mon += 1;
+ }
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err;
+
+ if (alarm_secs > rtc_secs) {
+ dev_err(dev, "%s: alarm maximum is one month in the "
+ "future (%d)\n", __func__, ret);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+
+ /*
+ * Program all alarm registers but DW one. For each register, setting
+ * MSB to 0 enables associated alarm.
+ */
+ regs[0] = bin2bcd(alarm_tm->tm_min) & 0x7f;
+ regs[1] = bin2bcd(alarm_tm->tm_hour) & 0x3f;
+ regs[2] = bin2bcd(alarm_tm->tm_mday) & 0x3f;
+ regs[3] = ABB5ZES3_REG_ALRM_DW_AE; /* do not match day of the week */
+
+ ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_ALRM_MN, regs,
+ ABB5ZES3_ALRM_SEC_LEN);
+ if (ret < 0) {
+ dev_err(dev, "%s: writing ALARM section failed (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* Record currently configured alarm is not a timer */
+ data->timer_alarm = 0;
+
+ /* Enable or disable alarm interrupt generation */
+ ret = _abb5zes3_rtc_update_alarm(dev, enable);
+
+err:
+ return ret;
+}
+
+/*
+ * Set alarm using timer watchdog (via timer A) mechanism. The function expects
+ * timer A interrupt to be disabled.
+ */
+static int _abb5zes3_rtc_set_timer(struct device *dev, struct rtc_wkalrm *alarm,
+ u8 secs)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ u8 regs[ABB5ZES3_TIMA_SEC_LEN];
+ u8 mask = ABB5ZES3_REG_TIM_CLK_TAC0 | ABB5ZES3_REG_TIM_CLK_TAC1;
+ int ret = 0;
+
+ /* Program given number of seconds to Timer A registers */
+ sec_to_timer_a(secs, &regs[0], &regs[1]);
+ ret = regmap_bulk_write(data->regmap, ABB5ZES3_REG_TIMA_CLK, regs,
+ ABB5ZES3_TIMA_SEC_LEN);
+ if (ret < 0) {
+ dev_err(dev, "%s: writing timer section failed\n", __func__);
+ goto err;
+ }
+
+ /* Configure Timer A as a watchdog timer */
+ ret = regmap_update_bits(data->regmap, ABB5ZES3_REG_TIM_CLK,
+ mask, ABB5ZES3_REG_TIM_CLK_TAC1);
+ if (ret)
+ dev_err(dev, "%s: failed to update timer\n", __func__);
+
+ /* Record currently configured alarm is a timer */
+ data->timer_alarm = 1;
+
+ /* Enable or disable timer interrupt generation */
+ ret = _abb5zes3_rtc_update_timer(dev, alarm->enabled);
+
+err:
+ return ret;
+}
+
+/*
+ * The chip has an alarm which is only accurate to the minute. In order to
+ * handle alarms below that limit, we use the watchdog timer function of
+ * timer A. More precisely, the timer method is used for alarms below 240
+ * seconds.
+ */
+static int abb5zes3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time *alarm_tm = &alarm->time;
+ unsigned long rtc_secs, alarm_secs;
+ struct rtc_time rtc_tm;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = _abb5zes3_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ goto err;
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err;
+
+ ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
+ if (ret)
+ goto err;
+
+ /* Let's first disable both the alarm and the timer interrupts */
+ ret = _abb5zes3_rtc_update_alarm(dev, false);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to disable alarm (%d)\n", __func__,
+ ret);
+ goto err;
+ }
+ ret = _abb5zes3_rtc_update_timer(dev, false);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to disable timer (%d)\n", __func__,
+ ret);
+ goto err;
+ }
+
+ data->timer_alarm = 0;
+
+ /*
+ * Let's now configure the alarm; if we are expected to ring in
+ * more than 240s, then we setup an alarm. Otherwise, a timer.
+ */
+ if ((alarm_secs > rtc_secs) && ((alarm_secs - rtc_secs) <= 240))
+ ret = _abb5zes3_rtc_set_timer(dev, alarm,
+ alarm_secs - rtc_secs);
+ else
+ ret = _abb5zes3_rtc_set_alarm(dev, alarm);
+
+ err:
+ mutex_unlock(&data->lock);
+
+ if (ret)
+ dev_err(dev, "%s: unable to configure alarm (%d)\n", __func__,
+ ret);
+
+ return ret;
+ }
+
+/* Enable or disable battery low irq generation */
+static inline int _abb5zes3_rtc_battery_low_irq_enable(struct regmap *regmap,
+ bool enable)
+{
+ return regmap_update_bits(regmap, ABB5ZES3_REG_CTRL3,
+ ABB5ZES3_REG_CTRL3_BLIE,
+ enable ? ABB5ZES3_REG_CTRL3_BLIE : 0);
+}
+
+/*
+ * Check current RTC status and enable/disable what needs to be. Return 0 if
+ * everything went ok and a negative value upon error. Note: this function
+ * is called early during init and hence does need mutex protection.
+ */
+static int abb5zes3_rtc_check_setup(struct device *dev)
+{
+ struct abb5zes3_rtc_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int reg;
+ int ret;
+ u8 mask;
+
+ /*
+ * By default, the devices generates a 32.768KHz signal on IRQ#1 pin. It
+ * is disabled here to prevent polluting the interrupt line and
+ * uselessly triggering the IRQ handler we install for alarm and battery
+ * low events. Note: this is done before clearing int. status below
+ * in this function.
+ * We also disable all timers and set timer interrupt to permanent (not
+ * pulsed).
+ */
+ mask = (ABB5ZES3_REG_TIM_CLK_TBC | ABB5ZES3_REG_TIM_CLK_TAC0 |
+ ABB5ZES3_REG_TIM_CLK_TAC1 | ABB5ZES3_REG_TIM_CLK_COF0 |
+ ABB5ZES3_REG_TIM_CLK_COF1 | ABB5ZES3_REG_TIM_CLK_COF2 |
+ ABB5ZES3_REG_TIM_CLK_TBM | ABB5ZES3_REG_TIM_CLK_TAM);
+ ret = regmap_update_bits(regmap, ABB5ZES3_REG_TIM_CLK, mask,
+ ABB5ZES3_REG_TIM_CLK_COF0 | ABB5ZES3_REG_TIM_CLK_COF1 |
+ ABB5ZES3_REG_TIM_CLK_COF2);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to initialize clkout register (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * Each component of the alarm (MN, HR, DT, DW) can be enabled/disabled
+ * individually by clearing/setting MSB of each associated register. So,
+ * we set all alarm enable bits to disable current alarm setting.
+ */
+ mask = (ABB5ZES3_REG_ALRM_MN_AE | ABB5ZES3_REG_ALRM_HR_AE |
+ ABB5ZES3_REG_ALRM_DT_AE | ABB5ZES3_REG_ALRM_DW_AE);
+ ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL2, mask, mask);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to disable alarm setting (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Set Control 1 register (RTC enabled, 24hr mode, all int. disabled) */
+ mask = (ABB5ZES3_REG_CTRL1_CIE | ABB5ZES3_REG_CTRL1_AIE |
+ ABB5ZES3_REG_CTRL1_SIE | ABB5ZES3_REG_CTRL1_PM |
+ ABB5ZES3_REG_CTRL1_CAP | ABB5ZES3_REG_CTRL1_STOP);
+ ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL1, mask, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to initialize CTRL1 register (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * Set Control 2 register (timer int. disabled, alarm status cleared).
+ * WTAF is read-only and cleared automatically by reading the register.
+ */
+ mask = (ABB5ZES3_REG_CTRL2_CTBIE | ABB5ZES3_REG_CTRL2_CTAIE |
+ ABB5ZES3_REG_CTRL2_WTAIE | ABB5ZES3_REG_CTRL2_AF |
+ ABB5ZES3_REG_CTRL2_SF | ABB5ZES3_REG_CTRL2_CTBF |
+ ABB5ZES3_REG_CTRL2_CTAF);
+ ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL2, mask, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to initialize CTRL2 register (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /*
+ * Enable battery low detection function and battery switchover function
+ * (standard mode). Disable associated interrupts. Clear battery
+ * switchover flag but not battery low flag. The latter is checked
+ * later below.
+ */
+ mask = (ABB5ZES3_REG_CTRL3_PM0 | ABB5ZES3_REG_CTRL3_PM1 |
+ ABB5ZES3_REG_CTRL3_PM2 | ABB5ZES3_REG_CTRL3_BLIE |
+ ABB5ZES3_REG_CTRL3_BSIE| ABB5ZES3_REG_CTRL3_BSF);
+ ret = regmap_update_bits(regmap, ABB5ZES3_REG_CTRL3, mask, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to initialize CTRL3 register (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Check oscillator integrity flag */
+ ret = regmap_read(regmap, ABB5ZES3_REG_RTC_SC, &reg);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to read osc. integrity flag (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (reg & ABB5ZES3_REG_RTC_SC_OSC) {
+ dev_err(dev, "clock integrity not guaranteed. Osc. has stopped "
+ "or has been interrupted.\n");
+ dev_err(dev, "change battery (if not already done) and "
+ "then set time to reset osc. failure flag.\n");
+ }
+
+ /*
+ * Check battery low flag at startup: this allows reporting battery
+ * is low at startup when IRQ line is not connected. Note: we record
+ * current status to avoid reenabling this interrupt later in probe
+ * function if battery is low.
+ */
+ ret = regmap_read(regmap, ABB5ZES3_REG_CTRL3, &reg);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to read battery low flag (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ data->battery_low = reg & ABB5ZES3_REG_CTRL3_BLF;
+ if (data->battery_low) {
+ dev_err(dev, "RTC battery is low; please, consider "
+ "changing it!\n");
+
+ ret = _abb5zes3_rtc_battery_low_irq_enable(regmap, false);
+ if (ret)
+ dev_err(dev, "%s: disabling battery low interrupt "
+ "generation failed (%d)\n", __func__, ret);
+ }
+
+ return ret;
+}
+
+static int abb5zes3_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enable)
+{
+ struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (rtc_data->irq) {
+ mutex_lock(&rtc_data->lock);
+ if (rtc_data->timer_alarm)
+ ret = _abb5zes3_rtc_update_timer(dev, enable);
+ else
+ ret = _abb5zes3_rtc_update_alarm(dev, enable);
+ mutex_unlock(&rtc_data->lock);
+ }
+
+ return ret;
+}
+
+static irqreturn_t _abb5zes3_rtc_interrupt(int irq, void *data)
+{
+ struct i2c_client *client = data;
+ struct device *dev = &client->dev;
+ struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev);
+ struct rtc_device *rtc = rtc_data->rtc;
+ u8 regs[ABB5ZES3_CTRL_SEC_LEN];
+ int ret, handled = IRQ_NONE;
+
+ ret = regmap_bulk_read(rtc_data->regmap, 0, regs,
+ ABB5ZES3_CTRL_SEC_LEN);
+ if (ret) {
+ dev_err(dev, "%s: unable to read control section (%d)!\n",
+ __func__, ret);
+ return handled;
+ }
+
+ /*
+ * Check battery low detection flag and disable battery low interrupt
+ * generation if flag is set (interrupt can only be cleared when
+ * battery is replaced).
+ */
+ if (regs[ABB5ZES3_REG_CTRL3] & ABB5ZES3_REG_CTRL3_BLF) {
+ dev_err(dev, "RTC battery is low; please change it!\n");
+
+ _abb5zes3_rtc_battery_low_irq_enable(rtc_data->regmap, false);
+
+ handled = IRQ_HANDLED;
+ }
+
+ /* Check alarm flag */
+ if (regs[ABB5ZES3_REG_CTRL2] & ABB5ZES3_REG_CTRL2_AF) {
+ dev_dbg(dev, "RTC alarm!\n");
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
+ /* Acknowledge and disable the alarm */
+ _abb5zes3_rtc_clear_alarm(dev);
+ _abb5zes3_rtc_update_alarm(dev, 0);
+
+ handled = IRQ_HANDLED;
+ }
+
+ /* Check watchdog Timer A flag */
+ if (regs[ABB5ZES3_REG_CTRL2] & ABB5ZES3_REG_CTRL2_WTAF) {
+ dev_dbg(dev, "RTC timer!\n");
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
+ /*
+ * Acknowledge and disable the alarm. Note: WTAF
+ * flag had been cleared when reading CTRL2
+ */
+ _abb5zes3_rtc_update_timer(dev, 0);
+
+ rtc_data->timer_alarm = 0;
+
+ handled = IRQ_HANDLED;
+ }
+
+ return handled;
+}
+
+static const struct rtc_class_ops rtc_ops = {
+ .read_time = _abb5zes3_rtc_read_time,
+ .set_time = abb5zes3_rtc_set_time,
+ .read_alarm = abb5zes3_rtc_read_alarm,
+ .set_alarm = abb5zes3_rtc_set_alarm,
+ .alarm_irq_enable = abb5zes3_rtc_alarm_irq_enable,
+};
+
+static const struct regmap_config abb5zes3_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int abb5zes3_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct abb5zes3_rtc_data *data = NULL;
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ regmap = devm_regmap_init_i2c(client, &abb5zes3_rtc_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "%s: regmap allocation failed: %d\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = abb5zes3_i2c_validate_chip(regmap);
+ if (ret)
+ goto err;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ mutex_init(&data->lock);
+ data->regmap = regmap;
+ dev_set_drvdata(dev, data);
+
+ ret = abb5zes3_rtc_check_setup(dev);
+ if (ret)
+ goto err;
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ _abb5zes3_rtc_interrupt,
+ IRQF_SHARED|IRQF_ONESHOT,
+ DRV_NAME, client);
+ if (!ret) {
+ device_init_wakeup(dev, true);
+ data->irq = client->irq;
+ dev_dbg(dev, "%s: irq %d used by RTC\n", __func__,
+ client->irq);
+ } else {
+ dev_err(dev, "%s: irq %d unavailable (%d)\n",
+ __func__, client->irq, ret);
+ goto err;
+ }
+ }
+
+ data->rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops,
+ THIS_MODULE);
+ ret = PTR_ERR_OR_ZERO(data->rtc);
+ if (ret) {
+ dev_err(dev, "%s: unable to register RTC device (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* Enable battery low detection interrupt if battery not already low */
+ if (!data->battery_low && data->irq) {
+ ret = _abb5zes3_rtc_battery_low_irq_enable(regmap, true);
+ if (ret) {
+ dev_err(dev, "%s: enabling battery low interrupt "
+ "generation failed (%d)\n", __func__, ret);
+ goto err;
+ }
+ }
+
+err:
+ if (ret && data && data->irq)
+ device_init_wakeup(dev, false);
+ return ret;
+}
+
+static int abb5zes3_remove(struct i2c_client *client)
+{
+ struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(&client->dev);
+
+ if (rtc_data->irq > 0)
+ device_init_wakeup(&client->dev, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int abb5zes3_rtc_suspend(struct device *dev)
+{
+ struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ return enable_irq_wake(rtc_data->irq);
+
+ return 0;
+}
+
+static int abb5zes3_rtc_resume(struct device *dev)
+{
+ struct abb5zes3_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ return disable_irq_wake(rtc_data->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(abb5zes3_rtc_pm_ops, abb5zes3_rtc_suspend,
+ abb5zes3_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id abb5zes3_dt_match[] = {
+ { .compatible = "abracon,abb5zes3" },
+ { },
+};
+#endif
+
+static const struct i2c_device_id abb5zes3_id[] = {
+ { "abb5zes3", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abb5zes3_id);
+
+static struct i2c_driver abb5zes3_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &abb5zes3_rtc_pm_ops,
+ .of_match_table = of_match_ptr(abb5zes3_dt_match),
+ },
+ .probe = abb5zes3_probe,
+ .remove = abb5zes3_remove,
+ .id_table = abb5zes3_id,
+};
+module_i2c_driver(abb5zes3_driver);
+
+MODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>");
+MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-B5ZE-S3 RTC/Alarm driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ab3100.c b/drivers/rtc/rtc-ab3100.c
new file mode 100644
index 000000000..9b725c553
--- /dev/null
+++ b/drivers/rtc/rtc-ab3100.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2007-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * RTC clock driver for the AB3100 Analog Baseband Chip
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/mfd/abx500.h>
+
+/* Clock rate in Hz */
+#define AB3100_RTC_CLOCK_RATE 32768
+
+/*
+ * The AB3100 RTC registers. These are the same for
+ * AB3000 and AB3100.
+ * Control register:
+ * Bit 0: RTC Monitor cleared=0, active=1, if you set it
+ * to 1 it remains active until RTC power is lost.
+ * Bit 1: 32 kHz Oscillator, 0 = on, 1 = bypass
+ * Bit 2: Alarm on, 0 = off, 1 = on
+ * Bit 3: 32 kHz buffer disabling, 0 = enabled, 1 = disabled
+ */
+#define AB3100_RTC 0x53
+/* default setting, buffer disabled, alarm on */
+#define RTC_SETTING 0x30
+/* Alarm when AL0-AL3 == TI0-TI3 */
+#define AB3100_AL0 0x56
+#define AB3100_AL1 0x57
+#define AB3100_AL2 0x58
+#define AB3100_AL3 0x59
+/* This 48-bit register that counts up at 32768 Hz */
+#define AB3100_TI0 0x5a
+#define AB3100_TI1 0x5b
+#define AB3100_TI2 0x5c
+#define AB3100_TI3 0x5d
+#define AB3100_TI4 0x5e
+#define AB3100_TI5 0x5f
+
+/*
+ * RTC clock functions and device struct declaration
+ */
+static int ab3100_rtc_set_mmss(struct device *dev, time64_t secs)
+{
+ u8 regs[] = {AB3100_TI0, AB3100_TI1, AB3100_TI2,
+ AB3100_TI3, AB3100_TI4, AB3100_TI5};
+ unsigned char buf[6];
+ u64 hw_counter = secs * AB3100_RTC_CLOCK_RATE * 2;
+ int err = 0;
+ int i;
+
+ buf[0] = (hw_counter) & 0xFF;
+ buf[1] = (hw_counter >> 8) & 0xFF;
+ buf[2] = (hw_counter >> 16) & 0xFF;
+ buf[3] = (hw_counter >> 24) & 0xFF;
+ buf[4] = (hw_counter >> 32) & 0xFF;
+ buf[5] = (hw_counter >> 40) & 0xFF;
+
+ for (i = 0; i < 6; i++) {
+ err = abx500_set_register_interruptible(dev, 0,
+ regs[i], buf[i]);
+ if (err)
+ return err;
+ }
+
+ /* Set the flag to mark that the clock is now set */
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC,
+ 0x01, 0x01);
+
+}
+
+static int ab3100_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ time64_t time;
+ u8 rtcval;
+ int err;
+
+ err = abx500_get_register_interruptible(dev, 0,
+ AB3100_RTC, &rtcval);
+ if (err)
+ return err;
+
+ if (!(rtcval & 0x01)) {
+ dev_info(dev, "clock not set (lost power)");
+ return -EINVAL;
+ } else {
+ u64 hw_counter;
+ u8 buf[6];
+
+ /* Read out time registers */
+ err = abx500_get_register_page_interruptible(dev, 0,
+ AB3100_TI0,
+ buf, 6);
+ if (err != 0)
+ return err;
+
+ hw_counter = ((u64) buf[5] << 40) | ((u64) buf[4] << 32) |
+ ((u64) buf[3] << 24) | ((u64) buf[2] << 16) |
+ ((u64) buf[1] << 8) | (u64) buf[0];
+ time = hw_counter / (u64) (AB3100_RTC_CLOCK_RATE * 2);
+ }
+
+ rtc_time64_to_tm(time, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int ab3100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ time64_t time;
+ u64 hw_counter;
+ u8 buf[6];
+ u8 rtcval;
+ int err;
+
+ /* Figure out if alarm is enabled or not */
+ err = abx500_get_register_interruptible(dev, 0,
+ AB3100_RTC, &rtcval);
+ if (err)
+ return err;
+ if (rtcval & 0x04)
+ alarm->enabled = 1;
+ else
+ alarm->enabled = 0;
+ /* No idea how this could be represented */
+ alarm->pending = 0;
+ /* Read out alarm registers, only 4 bytes */
+ err = abx500_get_register_page_interruptible(dev, 0,
+ AB3100_AL0, buf, 4);
+ if (err)
+ return err;
+ hw_counter = ((u64) buf[3] << 40) | ((u64) buf[2] << 32) |
+ ((u64) buf[1] << 24) | ((u64) buf[0] << 16);
+ time = hw_counter / (u64) (AB3100_RTC_CLOCK_RATE * 2);
+
+ rtc_time64_to_tm(time, &alarm->time);
+
+ return rtc_valid_tm(&alarm->time);
+}
+
+static int ab3100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ u8 regs[] = {AB3100_AL0, AB3100_AL1, AB3100_AL2, AB3100_AL3};
+ unsigned char buf[4];
+ time64_t secs;
+ u64 hw_counter;
+ int err;
+ int i;
+
+ secs = rtc_tm_to_time64(&alarm->time);
+ hw_counter = secs * AB3100_RTC_CLOCK_RATE * 2;
+ buf[0] = (hw_counter >> 16) & 0xFF;
+ buf[1] = (hw_counter >> 24) & 0xFF;
+ buf[2] = (hw_counter >> 32) & 0xFF;
+ buf[3] = (hw_counter >> 40) & 0xFF;
+
+ /* Set the alarm */
+ for (i = 0; i < 4; i++) {
+ err = abx500_set_register_interruptible(dev, 0,
+ regs[i], buf[i]);
+ if (err)
+ return err;
+ }
+ /* Then enable the alarm */
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC, (1 << 2),
+ alarm->enabled << 2);
+}
+
+static int ab3100_rtc_irq_enable(struct device *dev, unsigned int enabled)
+{
+ /*
+ * It's not possible to enable/disable the alarm IRQ for this RTC.
+ * It does not actually trigger any IRQ: instead its only function is
+ * to power up the system, if it wasn't on. This will manifest as
+ * a "power up cause" in the AB3100 power driver (battery charging etc)
+ * and need to be handled there instead.
+ */
+ if (enabled)
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC, (1 << 2),
+ 1 << 2);
+ else
+ return abx500_mask_and_set_register_interruptible(dev, 0,
+ AB3100_RTC, (1 << 2),
+ 0);
+}
+
+static const struct rtc_class_ops ab3100_rtc_ops = {
+ .read_time = ab3100_rtc_read_time,
+ .set_mmss64 = ab3100_rtc_set_mmss,
+ .read_alarm = ab3100_rtc_read_alarm,
+ .set_alarm = ab3100_rtc_set_alarm,
+ .alarm_irq_enable = ab3100_rtc_irq_enable,
+};
+
+static int __init ab3100_rtc_probe(struct platform_device *pdev)
+{
+ int err;
+ u8 regval;
+ struct rtc_device *rtc;
+
+ /* The first RTC register needs special treatment */
+ err = abx500_get_register_interruptible(&pdev->dev, 0,
+ AB3100_RTC, &regval);
+ if (err) {
+ dev_err(&pdev->dev, "unable to read RTC register\n");
+ return -ENODEV;
+ }
+
+ if ((regval & 0xFE) != RTC_SETTING) {
+ dev_warn(&pdev->dev, "not default value in RTC reg 0x%x\n",
+ regval);
+ }
+
+ if ((regval & 1) == 0) {
+ /*
+ * Set bit to detect power loss.
+ * This bit remains until RTC power is lost.
+ */
+ regval = 1 | RTC_SETTING;
+ err = abx500_set_register_interruptible(&pdev->dev, 0,
+ AB3100_RTC, regval);
+ /* Ignore any error on this write */
+ }
+
+ rtc = devm_rtc_device_register(&pdev->dev, "ab3100-rtc",
+ &ab3100_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ err = PTR_ERR(rtc);
+ return err;
+ }
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver ab3100_rtc_driver = {
+ .driver = {
+ .name = "ab3100-rtc",
+ },
+};
+
+module_platform_driver_probe(ab3100_rtc_driver, ab3100_rtc_probe);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("AB3100 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
new file mode 100644
index 000000000..6856f0a3a
--- /dev/null
+++ b/drivers/rtc/rtc-ab8500.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>
+ *
+ * RTC clock driver for the RTC part of the AB8500 Power management chip.
+ * Based on RTC clock driver for the AB3100 Analog Baseband Chip by
+ * Linus Walleij <linus.walleij@stericsson.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+
+#define AB8500_RTC_SOFF_STAT_REG 0x00
+#define AB8500_RTC_CC_CONF_REG 0x01
+#define AB8500_RTC_READ_REQ_REG 0x02
+#define AB8500_RTC_WATCH_TSECMID_REG 0x03
+#define AB8500_RTC_WATCH_TSECHI_REG 0x04
+#define AB8500_RTC_WATCH_TMIN_LOW_REG 0x05
+#define AB8500_RTC_WATCH_TMIN_MID_REG 0x06
+#define AB8500_RTC_WATCH_TMIN_HI_REG 0x07
+#define AB8500_RTC_ALRM_MIN_LOW_REG 0x08
+#define AB8500_RTC_ALRM_MIN_MID_REG 0x09
+#define AB8500_RTC_ALRM_MIN_HI_REG 0x0A
+#define AB8500_RTC_STAT_REG 0x0B
+#define AB8500_RTC_BKUP_CHG_REG 0x0C
+#define AB8500_RTC_FORCE_BKUP_REG 0x0D
+#define AB8500_RTC_CALIB_REG 0x0E
+#define AB8500_RTC_SWITCH_STAT_REG 0x0F
+#define AB8540_RTC_ALRM_SEC 0x22
+#define AB8540_RTC_ALRM_MIN_LOW_REG 0x23
+#define AB8540_RTC_ALRM_MIN_MID_REG 0x24
+#define AB8540_RTC_ALRM_MIN_HI_REG 0x25
+
+/* RtcReadRequest bits */
+#define RTC_READ_REQUEST 0x01
+#define RTC_WRITE_REQUEST 0x02
+
+/* RtcCtrl bits */
+#define RTC_ALARM_ENA 0x04
+#define RTC_STATUS_DATA 0x01
+
+#define COUNTS_PER_SEC (0xF000 / 60)
+#define AB8500_RTC_EPOCH 2000
+
+static const u8 ab8500_rtc_time_regs[] = {
+ AB8500_RTC_WATCH_TMIN_HI_REG, AB8500_RTC_WATCH_TMIN_MID_REG,
+ AB8500_RTC_WATCH_TMIN_LOW_REG, AB8500_RTC_WATCH_TSECHI_REG,
+ AB8500_RTC_WATCH_TSECMID_REG
+};
+
+static const u8 ab8500_rtc_alarm_regs[] = {
+ AB8500_RTC_ALRM_MIN_HI_REG, AB8500_RTC_ALRM_MIN_MID_REG,
+ AB8500_RTC_ALRM_MIN_LOW_REG
+};
+
+static const u8 ab8540_rtc_alarm_regs[] = {
+ AB8540_RTC_ALRM_MIN_HI_REG, AB8540_RTC_ALRM_MIN_MID_REG,
+ AB8540_RTC_ALRM_MIN_LOW_REG, AB8540_RTC_ALRM_SEC
+};
+
+/* Calculate the seconds from 1970 to 01-01-2000 00:00:00 */
+static unsigned long get_elapsed_seconds(int year)
+{
+ unsigned long secs;
+ struct rtc_time tm = {
+ .tm_year = year - 1900,
+ .tm_mday = 1,
+ };
+
+ /*
+ * This function calculates secs from 1970 and not from
+ * 1900, even if we supply the offset from year 1900.
+ */
+ rtc_tm_to_time(&tm, &secs);
+ return secs;
+}
+
+static int ab8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long timeout = jiffies + HZ;
+ int retval, i;
+ unsigned long mins, secs;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
+ u8 value;
+
+ /* Request a data read */
+ retval = abx500_set_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, RTC_READ_REQUEST);
+ if (retval < 0)
+ return retval;
+
+ /* Wait for some cycles after enabling the rtc read in ab8500 */
+ while (time_before(jiffies, timeout)) {
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, AB8500_RTC_READ_REQ_REG, &value);
+ if (retval < 0)
+ return retval;
+
+ if (!(value & RTC_READ_REQUEST))
+ break;
+
+ usleep_range(1000, 5000);
+ }
+
+ /* Read the Watchtime registers */
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
+ retval = abx500_get_register_interruptible(dev,
+ AB8500_RTC, ab8500_rtc_time_regs[i], &value);
+ if (retval < 0)
+ return retval;
+ buf[i] = value;
+ }
+
+ mins = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+
+ secs = (buf[3] << 8) | buf[4];
+ secs = secs / COUNTS_PER_SEC;
+ secs = secs + (mins * 60);
+
+ /* Add back the initially subtracted number of seconds */
+ secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ rtc_time_to_tm(secs, tm);
+ return rtc_valid_tm(tm);
+}
+
+static int ab8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ int retval, i;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_time_regs)];
+ unsigned long no_secs, no_mins, secs = 0;
+
+ if (tm->tm_year < (AB8500_RTC_EPOCH - 1900)) {
+ dev_dbg(dev, "year should be equal to or greater than %d\n",
+ AB8500_RTC_EPOCH);
+ return -EINVAL;
+ }
+
+ /* Get the number of seconds since 1970 */
+ rtc_tm_to_time(tm, &secs);
+
+ /*
+ * Convert it to the number of seconds since 01-01-2000 00:00:00, since
+ * we only have a small counter in the RTC.
+ */
+ secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ no_mins = secs / 60;
+
+ no_secs = secs % 60;
+ /* Make the seconds count as per the RTC resolution */
+ no_secs = no_secs * COUNTS_PER_SEC;
+
+ buf[4] = no_secs & 0xFF;
+ buf[3] = (no_secs >> 8) & 0xFF;
+
+ buf[2] = no_mins & 0xFF;
+ buf[1] = (no_mins >> 8) & 0xFF;
+ buf[0] = (no_mins >> 16) & 0xFF;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_time_regs); i++) {
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_time_regs[i], buf[i]);
+ if (retval < 0)
+ return retval;
+ }
+
+ /* Request a data write */
+ return abx500_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_READ_REQ_REG, RTC_WRITE_REQUEST);
+}
+
+static int ab8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int retval, i;
+ u8 rtc_ctrl, value;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
+ unsigned long secs, mins;
+
+ /* Check if the alarm is enabled or not */
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, &rtc_ctrl);
+ if (retval < 0)
+ return retval;
+
+ if (rtc_ctrl & RTC_ALARM_ENA)
+ alarm->enabled = 1;
+ else
+ alarm->enabled = 0;
+
+ alarm->pending = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], &value);
+ if (retval < 0)
+ return retval;
+ buf[i] = value;
+ }
+
+ mins = (buf[0] << 16) | (buf[1] << 8) | (buf[2]);
+ secs = mins * 60;
+
+ /* Add back the initially subtracted number of seconds */
+ secs += get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ rtc_time_to_tm(secs, &alarm->time);
+
+ return rtc_valid_tm(&alarm->time);
+}
+
+static int ab8500_rtc_irq_enable(struct device *dev, unsigned int enabled)
+{
+ return abx500_mask_and_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, RTC_ALARM_ENA,
+ enabled ? RTC_ALARM_ENA : 0);
+}
+
+static int ab8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int retval, i;
+ unsigned char buf[ARRAY_SIZE(ab8500_rtc_alarm_regs)];
+ unsigned long mins, secs = 0, cursec = 0;
+ struct rtc_time curtm;
+
+ if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
+ dev_dbg(dev, "year should be equal to or greater than %d\n",
+ AB8500_RTC_EPOCH);
+ return -EINVAL;
+ }
+
+ /* Get the number of seconds since 1970 */
+ rtc_tm_to_time(&alarm->time, &secs);
+
+ /*
+ * Check whether alarm is set less than 1min.
+ * Since our RTC doesn't support alarm resolution less than 1min,
+ * return -EINVAL, so UIE EMUL can take it up, incase of UIE_ON
+ */
+ ab8500_rtc_read_time(dev, &curtm); /* Read current time */
+ rtc_tm_to_time(&curtm, &cursec);
+ if ((secs - cursec) < 59) {
+ dev_dbg(dev, "Alarm less than 1 minute not supported\r\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Convert it to the number of seconds since 01-01-2000 00:00:00, since
+ * we only have a small counter in the RTC.
+ */
+ secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+
+ mins = secs / 60;
+
+ buf[2] = mins & 0xFF;
+ buf[1] = (mins >> 8) & 0xFF;
+ buf[0] = (mins >> 16) & 0xFF;
+
+ /* Set the alarm time */
+ for (i = 0; i < ARRAY_SIZE(ab8500_rtc_alarm_regs); i++) {
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8500_rtc_alarm_regs[i], buf[i]);
+ if (retval < 0)
+ return retval;
+ }
+
+ return ab8500_rtc_irq_enable(dev, alarm->enabled);
+}
+
+static int ab8540_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int retval, i;
+ unsigned char buf[ARRAY_SIZE(ab8540_rtc_alarm_regs)];
+ unsigned long mins, secs = 0;
+
+ if (alarm->time.tm_year < (AB8500_RTC_EPOCH - 1900)) {
+ dev_dbg(dev, "year should be equal to or greater than %d\n",
+ AB8500_RTC_EPOCH);
+ return -EINVAL;
+ }
+
+ /* Get the number of seconds since 1970 */
+ rtc_tm_to_time(&alarm->time, &secs);
+
+ /*
+ * Convert it to the number of seconds since 01-01-2000 00:00:00
+ */
+ secs -= get_elapsed_seconds(AB8500_RTC_EPOCH);
+ mins = secs / 60;
+
+ buf[3] = secs % 60;
+ buf[2] = mins & 0xFF;
+ buf[1] = (mins >> 8) & 0xFF;
+ buf[0] = (mins >> 16) & 0xFF;
+
+ /* Set the alarm time */
+ for (i = 0; i < ARRAY_SIZE(ab8540_rtc_alarm_regs); i++) {
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ ab8540_rtc_alarm_regs[i], buf[i]);
+ if (retval < 0)
+ return retval;
+ }
+
+ return ab8500_rtc_irq_enable(dev, alarm->enabled);
+}
+
+static int ab8500_rtc_set_calibration(struct device *dev, int calibration)
+{
+ int retval;
+ u8 rtccal = 0;
+
+ /*
+ * Check that the calibration value (which is in units of 0.5
+ * parts-per-million) is in the AB8500's range for RtcCalibration
+ * register. -128 (0x80) is not permitted because the AB8500 uses
+ * a sign-bit rather than two's complement, so 0x80 is just another
+ * representation of zero.
+ */
+ if ((calibration < -127) || (calibration > 127)) {
+ dev_err(dev, "RtcCalibration value outside permitted range\n");
+ return -EINVAL;
+ }
+
+ /*
+ * The AB8500 uses sign (in bit7) and magnitude (in bits0-7)
+ * so need to convert to this sort of representation before writing
+ * into RtcCalibration register...
+ */
+ if (calibration >= 0)
+ rtccal = 0x7F & calibration;
+ else
+ rtccal = ~(calibration - 1) | 0x80;
+
+ retval = abx500_set_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_CALIB_REG, rtccal);
+
+ return retval;
+}
+
+static int ab8500_rtc_get_calibration(struct device *dev, int *calibration)
+{
+ int retval;
+ u8 rtccal = 0;
+
+ retval = abx500_get_register_interruptible(dev, AB8500_RTC,
+ AB8500_RTC_CALIB_REG, &rtccal);
+ if (retval >= 0) {
+ /*
+ * The AB8500 uses sign (in bit7) and magnitude (in bits0-7)
+ * so need to convert value from RtcCalibration register into
+ * a two's complement signed value...
+ */
+ if (rtccal & 0x80)
+ *calibration = 0 - (rtccal & 0x7F);
+ else
+ *calibration = 0x7F & rtccal;
+ }
+
+ return retval;
+}
+
+static ssize_t ab8500_sysfs_store_rtc_calibration(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ int calibration = 0;
+
+ if (sscanf(buf, " %i ", &calibration) != 1) {
+ dev_err(dev, "Failed to store RTC calibration attribute\n");
+ return -EINVAL;
+ }
+
+ retval = ab8500_rtc_set_calibration(dev, calibration);
+
+ return retval ? retval : count;
+}
+
+static ssize_t ab8500_sysfs_show_rtc_calibration(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int retval = 0;
+ int calibration = 0;
+
+ retval = ab8500_rtc_get_calibration(dev, &calibration);
+ if (retval < 0) {
+ dev_err(dev, "Failed to read RTC calibration attribute\n");
+ sprintf(buf, "0\n");
+ return retval;
+ }
+
+ return sprintf(buf, "%d\n", calibration);
+}
+
+static DEVICE_ATTR(rtc_calibration, S_IRUGO | S_IWUSR,
+ ab8500_sysfs_show_rtc_calibration,
+ ab8500_sysfs_store_rtc_calibration);
+
+static int ab8500_sysfs_rtc_register(struct device *dev)
+{
+ return device_create_file(dev, &dev_attr_rtc_calibration);
+}
+
+static void ab8500_sysfs_rtc_unregister(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_rtc_calibration);
+}
+
+static irqreturn_t rtc_alarm_handler(int irq, void *data)
+{
+ struct rtc_device *rtc = data;
+ unsigned long events = RTC_IRQF | RTC_AF;
+
+ dev_dbg(&rtc->dev, "%s\n", __func__);
+ rtc_update_irq(rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops ab8500_rtc_ops = {
+ .read_time = ab8500_rtc_read_time,
+ .set_time = ab8500_rtc_set_time,
+ .read_alarm = ab8500_rtc_read_alarm,
+ .set_alarm = ab8500_rtc_set_alarm,
+ .alarm_irq_enable = ab8500_rtc_irq_enable,
+};
+
+static const struct rtc_class_ops ab8540_rtc_ops = {
+ .read_time = ab8500_rtc_read_time,
+ .set_time = ab8500_rtc_set_time,
+ .read_alarm = ab8500_rtc_read_alarm,
+ .set_alarm = ab8540_rtc_set_alarm,
+ .alarm_irq_enable = ab8500_rtc_irq_enable,
+};
+
+static struct platform_device_id ab85xx_rtc_ids[] = {
+ { "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, },
+ { "ab8540-rtc", (kernel_ulong_t)&ab8540_rtc_ops, },
+};
+
+static int ab8500_rtc_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *platid = platform_get_device_id(pdev);
+ int err;
+ struct rtc_device *rtc;
+ u8 rtc_ctrl;
+ int irq;
+
+ irq = platform_get_irq_byname(pdev, "ALARM");
+ if (irq < 0)
+ return irq;
+
+ /* For RTC supply test */
+ err = abx500_mask_and_set_register_interruptible(&pdev->dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, RTC_STATUS_DATA, RTC_STATUS_DATA);
+ if (err < 0)
+ return err;
+
+ /* Wait for reset by the PorRtc */
+ usleep_range(1000, 5000);
+
+ err = abx500_get_register_interruptible(&pdev->dev, AB8500_RTC,
+ AB8500_RTC_STAT_REG, &rtc_ctrl);
+ if (err < 0)
+ return err;
+
+ /* Check if the RTC Supply fails */
+ if (!(rtc_ctrl & RTC_STATUS_DATA)) {
+ dev_err(&pdev->dev, "RTC supply failure\n");
+ return -ENODEV;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+
+ rtc = devm_rtc_device_register(&pdev->dev, "ab8500-rtc",
+ (struct rtc_class_ops *)platid->driver_data,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "Registration failed\n");
+ err = PTR_ERR(rtc);
+ return err;
+ }
+
+ err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ rtc_alarm_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "ab8500-rtc", rtc);
+ if (err < 0)
+ return err;
+
+ platform_set_drvdata(pdev, rtc);
+
+ err = ab8500_sysfs_rtc_register(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "sysfs RTC failed to register\n");
+ return err;
+ }
+
+ rtc->uie_unsupported = 1;
+
+ return 0;
+}
+
+static int ab8500_rtc_remove(struct platform_device *pdev)
+{
+ ab8500_sysfs_rtc_unregister(&pdev->dev);
+
+ return 0;
+}
+
+static struct platform_driver ab8500_rtc_driver = {
+ .driver = {
+ .name = "ab8500-rtc",
+ },
+ .probe = ab8500_rtc_probe,
+ .remove = ab8500_rtc_remove,
+ .id_table = ab85xx_rtc_ids,
+};
+
+module_platform_driver(ab8500_rtc_driver);
+
+MODULE_AUTHOR("Virupax Sadashivpetimath <virupax.sadashivpetimath@stericsson.com>");
+MODULE_DESCRIPTION("AB8500 RTC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c
new file mode 100644
index 000000000..4337c3bc6
--- /dev/null
+++ b/drivers/rtc/rtc-abx80x.c
@@ -0,0 +1,307 @@
+/*
+ * A driver for the I2C members of the Abracon AB x8xx RTC family,
+ * and compatible: AB 1805 and AB 0805
+ *
+ * Copyright 2014-2015 Macq S.A.
+ *
+ * Author: Philippe De Muyter <phdm@macqel.be>
+ * Author: Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#define ABX8XX_REG_HTH 0x00
+#define ABX8XX_REG_SC 0x01
+#define ABX8XX_REG_MN 0x02
+#define ABX8XX_REG_HR 0x03
+#define ABX8XX_REG_DA 0x04
+#define ABX8XX_REG_MO 0x05
+#define ABX8XX_REG_YR 0x06
+#define ABX8XX_REG_WD 0x07
+
+#define ABX8XX_REG_CTRL1 0x10
+#define ABX8XX_CTRL_WRITE BIT(1)
+#define ABX8XX_CTRL_12_24 BIT(6)
+
+#define ABX8XX_REG_CFG_KEY 0x1f
+#define ABX8XX_CFG_KEY_MISC 0x9d
+
+#define ABX8XX_REG_ID0 0x28
+
+#define ABX8XX_REG_TRICKLE 0x20
+#define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0
+#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
+#define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4
+
+static u8 trickle_resistors[] = {0, 3, 6, 11};
+
+enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
+ AB1801, AB1803, AB1804, AB1805, ABX80X};
+
+struct abx80x_cap {
+ u16 pn;
+ bool has_tc;
+};
+
+static struct abx80x_cap abx80x_caps[] = {
+ [AB0801] = {.pn = 0x0801},
+ [AB0803] = {.pn = 0x0803},
+ [AB0804] = {.pn = 0x0804, .has_tc = true},
+ [AB0805] = {.pn = 0x0805, .has_tc = true},
+ [AB1801] = {.pn = 0x1801},
+ [AB1803] = {.pn = 0x1803},
+ [AB1804] = {.pn = 0x1804, .has_tc = true},
+ [AB1805] = {.pn = 0x1805, .has_tc = true},
+ [ABX80X] = {.pn = 0}
+};
+
+static struct i2c_driver abx80x_driver;
+
+static int abx80x_enable_trickle_charger(struct i2c_client *client,
+ u8 trickle_cfg)
+{
+ int err;
+
+ /*
+ * Write the configuration key register to enable access to the Trickle
+ * register
+ */
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY,
+ ABX8XX_CFG_KEY_MISC);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write configuration key\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_TRICKLE,
+ ABX8XX_TRICKLE_CHARGE_ENABLE |
+ trickle_cfg);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write trickle register\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int abx80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[8];
+ int err;
+
+ err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_HTH,
+ sizeof(buf), buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to read date\n");
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[ABX8XX_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ABX8XX_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ABX8XX_REG_HR] & 0x3F);
+ tm->tm_wday = buf[ABX8XX_REG_WD] & 0x7;
+ tm->tm_mday = bcd2bin(buf[ABX8XX_REG_DA] & 0x3F);
+ tm->tm_mon = bcd2bin(buf[ABX8XX_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ABX8XX_REG_YR]) + 100;
+
+ err = rtc_valid_tm(tm);
+ if (err < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return err;
+}
+
+static int abx80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[8];
+ int err;
+
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ buf[ABX8XX_REG_HTH] = 0;
+ buf[ABX8XX_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ABX8XX_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ABX8XX_REG_HR] = bin2bcd(tm->tm_hour);
+ buf[ABX8XX_REG_DA] = bin2bcd(tm->tm_mday);
+ buf[ABX8XX_REG_MO] = bin2bcd(tm->tm_mon + 1);
+ buf[ABX8XX_REG_YR] = bin2bcd(tm->tm_year - 100);
+ buf[ABX8XX_REG_WD] = tm->tm_wday;
+
+ err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_HTH,
+ sizeof(buf), buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write to date registers\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops abx80x_rtc_ops = {
+ .read_time = abx80x_rtc_read_time,
+ .set_time = abx80x_rtc_set_time,
+};
+
+static int abx80x_dt_trickle_cfg(struct device_node *np)
+{
+ const char *diode;
+ int trickle_cfg = 0;
+ int i, ret;
+ u32 tmp;
+
+ ret = of_property_read_string(np, "abracon,tc-diode", &diode);
+ if (ret)
+ return ret;
+
+ if (!strcmp(diode, "standard"))
+ trickle_cfg |= ABX8XX_TRICKLE_STANDARD_DIODE;
+ else if (!strcmp(diode, "schottky"))
+ trickle_cfg |= ABX8XX_TRICKLE_SCHOTTKY_DIODE;
+ else
+ return -EINVAL;
+
+ ret = of_property_read_u32(np, "abracon,tc-resistor", &tmp);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < sizeof(trickle_resistors); i++)
+ if (trickle_resistors[i] == tmp)
+ break;
+
+ if (i == sizeof(trickle_resistors))
+ return -EINVAL;
+
+ return (trickle_cfg | i);
+}
+
+static int abx80x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ struct rtc_device *rtc;
+ int i, data, err, trickle_cfg = -EINVAL;
+ char buf[7];
+ unsigned int part = id->driver_data;
+ unsigned int partnumber;
+ unsigned int majrev, minrev;
+ unsigned int lot;
+ unsigned int wafer;
+ unsigned int uid;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ID0,
+ sizeof(buf), buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to read partnumber\n");
+ return -EIO;
+ }
+
+ partnumber = (buf[0] << 8) | buf[1];
+ majrev = buf[2] >> 3;
+ minrev = buf[2] & 0x7;
+ lot = ((buf[4] & 0x80) << 2) | ((buf[6] & 0x80) << 1) | buf[3];
+ uid = ((buf[4] & 0x7f) << 8) | buf[5];
+ wafer = (buf[6] & 0x7c) >> 2;
+ dev_info(&client->dev, "model %04x, revision %u.%u, lot %x, wafer %x, uid %x\n",
+ partnumber, majrev, minrev, lot, wafer, uid);
+
+ data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL1);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read control register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL1,
+ ((data & ~ABX8XX_CTRL_12_24) |
+ ABX8XX_CTRL_WRITE));
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write control register\n");
+ return -EIO;
+ }
+
+ /* part autodetection */
+ if (part == ABX80X) {
+ for (i = 0; abx80x_caps[i].pn; i++)
+ if (partnumber == abx80x_caps[i].pn)
+ break;
+ if (abx80x_caps[i].pn == 0) {
+ dev_err(&client->dev, "Unknown part: %04x\n",
+ partnumber);
+ return -EINVAL;
+ }
+ part = i;
+ }
+
+ if (partnumber != abx80x_caps[part].pn) {
+ dev_err(&client->dev, "partnumber mismatch %04x != %04x\n",
+ partnumber, abx80x_caps[part].pn);
+ return -EINVAL;
+ }
+
+ if (np && abx80x_caps[part].has_tc)
+ trickle_cfg = abx80x_dt_trickle_cfg(np);
+
+ if (trickle_cfg > 0) {
+ dev_info(&client->dev, "Enabling trickle charger: %02x\n",
+ trickle_cfg);
+ abx80x_enable_trickle_charger(client, trickle_cfg);
+ }
+
+ rtc = devm_rtc_device_register(&client->dev, abx80x_driver.driver.name,
+ &abx80x_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static int abx80x_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id abx80x_id[] = {
+ { "abx80x", ABX80X },
+ { "ab0801", AB0801 },
+ { "ab0803", AB0803 },
+ { "ab0804", AB0804 },
+ { "ab0805", AB0805 },
+ { "ab1801", AB1801 },
+ { "ab1803", AB1803 },
+ { "ab1804", AB1804 },
+ { "ab1805", AB1805 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abx80x_id);
+
+static struct i2c_driver abx80x_driver = {
+ .driver = {
+ .name = "rtc-abx80x",
+ },
+ .probe = abx80x_probe,
+ .remove = abx80x_remove,
+ .id_table = abx80x_id,
+};
+
+module_i2c_driver(abx80x_driver);
+
+MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
+MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
+MODULE_DESCRIPTION("Abracon ABX80X RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c
new file mode 100644
index 000000000..4b62d1a87
--- /dev/null
+++ b/drivers/rtc/rtc-armada38x.c
@@ -0,0 +1,320 @@
+/*
+ * RTC driver for the Armada 38x Marvell SoCs
+ *
+ * Copyright (C) 2015 Marvell
+ *
+ * Gregory Clement <gregory.clement@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#define RTC_STATUS 0x0
+#define RTC_STATUS_ALARM1 BIT(0)
+#define RTC_STATUS_ALARM2 BIT(1)
+#define RTC_IRQ1_CONF 0x4
+#define RTC_IRQ1_AL_EN BIT(0)
+#define RTC_IRQ1_FREQ_EN BIT(1)
+#define RTC_IRQ1_FREQ_1HZ BIT(2)
+#define RTC_TIME 0xC
+#define RTC_ALARM1 0x10
+
+#define SOC_RTC_INTERRUPT 0x8
+#define SOC_RTC_ALARM1 BIT(0)
+#define SOC_RTC_ALARM2 BIT(1)
+#define SOC_RTC_ALARM1_MASK BIT(2)
+#define SOC_RTC_ALARM2_MASK BIT(3)
+
+struct armada38x_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *regs;
+ void __iomem *regs_soc;
+ spinlock_t lock;
+ /*
+ * While setting the time, the RTC TIME register should not be
+ * accessed. Setting the RTC time involves sleeping during
+ * 100ms, so a mutex instead of a spinlock is used to protect
+ * it
+ */
+ struct mutex mutex_time;
+ int irq;
+};
+
+/*
+ * According to the datasheet, the OS should wait 5us after every
+ * register write to the RTC hard macro so that the required update
+ * can occur without holding off the system bus
+ */
+static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset)
+{
+ writel(val, rtc->regs + offset);
+ udelay(5);
+}
+
+static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long time, time_check;
+
+ mutex_lock(&rtc->mutex_time);
+ time = readl(rtc->regs + RTC_TIME);
+ /*
+ * WA for failing time set attempts. As stated in HW ERRATA if
+ * more than one second between two time reads is detected
+ * then read once again.
+ */
+ time_check = readl(rtc->regs + RTC_TIME);
+ if ((time_check - time) > 1)
+ time_check = readl(rtc->regs + RTC_TIME);
+
+ mutex_unlock(&rtc->mutex_time);
+
+ rtc_time_to_tm(time_check, tm);
+
+ return 0;
+}
+
+static int armada38x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+ int ret = 0;
+ unsigned long time, flags;
+
+ ret = rtc_tm_to_time(tm, &time);
+
+ if (ret)
+ goto out;
+ /*
+ * Setting the RTC time not always succeeds. According to the
+ * errata we need to first write on the status register and
+ * then wait for 100ms before writing to the time register to be
+ * sure that the data will be taken into account.
+ */
+ mutex_lock(&rtc->mutex_time);
+ rtc_delayed_write(0, rtc, RTC_STATUS);
+ msleep(100);
+ rtc_delayed_write(time, rtc, RTC_TIME);
+ mutex_unlock(&rtc->mutex_time);
+
+out:
+ return ret;
+}
+
+static int armada38x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long time, flags;
+ u32 val;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ time = readl(rtc->regs + RTC_ALARM1);
+ val = readl(rtc->regs + RTC_IRQ1_CONF) & RTC_IRQ1_AL_EN;
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ alrm->enabled = val ? 1 : 0;
+ rtc_time_to_tm(time, &alrm->time);
+
+ return 0;
+}
+
+static int armada38x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long time, flags;
+ int ret = 0;
+ u32 val;
+
+ ret = rtc_tm_to_time(&alrm->time, &time);
+
+ if (ret)
+ goto out;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ rtc_delayed_write(time, rtc, RTC_ALARM1);
+
+ if (alrm->enabled) {
+ rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
+ val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
+ writel(val | SOC_RTC_ALARM1_MASK,
+ rtc->regs_soc + SOC_RTC_INTERRUPT);
+ }
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+out:
+ return ret;
+}
+
+static int armada38x_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ if (enabled)
+ rtc_delayed_write(RTC_IRQ1_AL_EN, rtc, RTC_IRQ1_CONF);
+ else
+ rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return 0;
+}
+
+static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
+{
+ struct armada38x_rtc *rtc = data;
+ u32 val;
+ int event = RTC_IRQF | RTC_AF;
+
+ dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq);
+
+ spin_lock(&rtc->lock);
+
+ val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT);
+
+ writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT);
+ val = readl(rtc->regs + RTC_IRQ1_CONF);
+ /* disable all the interrupts for alarm 1 */
+ rtc_delayed_write(0, rtc, RTC_IRQ1_CONF);
+ /* Ack the event */
+ rtc_delayed_write(RTC_STATUS_ALARM1, rtc, RTC_STATUS);
+
+ spin_unlock(&rtc->lock);
+
+ if (val & RTC_IRQ1_FREQ_EN) {
+ if (val & RTC_IRQ1_FREQ_1HZ)
+ event |= RTC_UF;
+ else
+ event |= RTC_PF;
+ }
+
+ rtc_update_irq(rtc->rtc_dev, 1, event);
+
+ return IRQ_HANDLED;
+}
+
+static struct rtc_class_ops armada38x_rtc_ops = {
+ .read_time = armada38x_rtc_read_time,
+ .set_time = armada38x_rtc_set_time,
+ .read_alarm = armada38x_rtc_read_alarm,
+ .set_alarm = armada38x_rtc_set_alarm,
+ .alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
+};
+
+static __init int armada38x_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct armada38x_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc),
+ GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ spin_lock_init(&rtc->lock);
+ mutex_init(&rtc->mutex_time);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc");
+ rtc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->regs))
+ return PTR_ERR(rtc->regs);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rtc-soc");
+ rtc->regs_soc = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->regs_soc))
+ return PTR_ERR(rtc->regs_soc);
+
+ rtc->irq = platform_get_irq(pdev, 0);
+
+ if (rtc->irq < 0) {
+ dev_err(&pdev->dev, "no irq\n");
+ return rtc->irq;
+ }
+ if (devm_request_irq(&pdev->dev, rtc->irq, armada38x_rtc_alarm_irq,
+ 0, pdev->name, rtc) < 0) {
+ dev_warn(&pdev->dev, "Interrupt not available.\n");
+ rtc->irq = -1;
+ /*
+ * If there is no interrupt available then we can't
+ * use the alarm
+ */
+ armada38x_rtc_ops.set_alarm = NULL;
+ armada38x_rtc_ops.alarm_irq_enable = NULL;
+ }
+ platform_set_drvdata(pdev, rtc);
+ if (rtc->irq != -1)
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &armada38x_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ ret = PTR_ERR(rtc->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int armada38x_rtc_suspend(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+
+ return enable_irq_wake(rtc->irq);
+ }
+
+ return 0;
+}
+
+static int armada38x_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct armada38x_rtc *rtc = dev_get_drvdata(dev);
+
+ return disable_irq_wake(rtc->irq);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops,
+ armada38x_rtc_suspend, armada38x_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id armada38x_rtc_of_match_table[] = {
+ { .compatible = "marvell,armada-380-rtc", },
+ {}
+};
+#endif
+
+static struct platform_driver armada38x_rtc_driver = {
+ .driver = {
+ .name = "armada38x-rtc",
+ .pm = &armada38x_rtc_pm_ops,
+ .of_match_table = of_match_ptr(armada38x_rtc_of_match_table),
+ },
+};
+
+module_platform_driver_probe(armada38x_rtc_driver, armada38x_rtc_probe);
+
+MODULE_DESCRIPTION("Marvell Armada 38x RTC driver");
+MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-as3722.c b/drivers/rtc/rtc-as3722.c
new file mode 100644
index 000000000..9f38eda69
--- /dev/null
+++ b/drivers/rtc/rtc-as3722.c
@@ -0,0 +1,261 @@
+/*
+ * rtc-as3722.c - Real Time Clock driver for ams AS3722 PMICs
+ *
+ * Copyright (C) 2013 ams AG
+ * Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
+ *
+ * Author: Florian Lobmaier <florian.lobmaier@ams.com>
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bcd.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/as3722.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/time.h>
+
+#define AS3722_RTC_START_YEAR 2000
+struct as3722_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ struct as3722 *as3722;
+ int alarm_irq;
+ bool irq_enable;
+};
+
+static void as3722_time_to_reg(u8 *rbuff, struct rtc_time *tm)
+{
+ rbuff[0] = bin2bcd(tm->tm_sec);
+ rbuff[1] = bin2bcd(tm->tm_min);
+ rbuff[2] = bin2bcd(tm->tm_hour);
+ rbuff[3] = bin2bcd(tm->tm_mday);
+ rbuff[4] = bin2bcd(tm->tm_mon);
+ rbuff[5] = bin2bcd(tm->tm_year - (AS3722_RTC_START_YEAR - 1900));
+}
+
+static void as3722_reg_to_time(u8 *rbuff, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(rbuff[0] & 0x7F);
+ tm->tm_min = bcd2bin(rbuff[1] & 0x7F);
+ tm->tm_hour = bcd2bin(rbuff[2] & 0x3F);
+ tm->tm_mday = bcd2bin(rbuff[3] & 0x3F);
+ tm->tm_mon = bcd2bin(rbuff[4] & 0x1F);
+ tm->tm_year = (AS3722_RTC_START_YEAR - 1900) + bcd2bin(rbuff[5] & 0x7F);
+ return;
+}
+
+static int as3722_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
+ u8 as_time_array[6];
+ int ret;
+
+ ret = as3722_block_read(as3722, AS3722_RTC_SECOND_REG,
+ 6, as_time_array);
+ if (ret < 0) {
+ dev_err(dev, "RTC_SECOND reg block read failed %d\n", ret);
+ return ret;
+ }
+ as3722_reg_to_time(as_time_array, tm);
+ return 0;
+}
+
+static int as3722_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
+ u8 as_time_array[6];
+ int ret;
+
+ if (tm->tm_year < (AS3722_RTC_START_YEAR - 1900))
+ return -EINVAL;
+
+ as3722_time_to_reg(as_time_array, tm);
+ ret = as3722_block_write(as3722, AS3722_RTC_SECOND_REG, 6,
+ as_time_array);
+ if (ret < 0)
+ dev_err(dev, "RTC_SECOND reg block write failed %d\n", ret);
+ return ret;
+}
+
+static int as3722_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+
+ if (enabled && !as3722_rtc->irq_enable) {
+ enable_irq(as3722_rtc->alarm_irq);
+ as3722_rtc->irq_enable = true;
+ } else if (!enabled && as3722_rtc->irq_enable) {
+ disable_irq(as3722_rtc->alarm_irq);
+ as3722_rtc->irq_enable = false;
+ }
+ return 0;
+}
+
+static int as3722_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
+ u8 as_time_array[6];
+ int ret;
+
+ ret = as3722_block_read(as3722, AS3722_RTC_ALARM_SECOND_REG, 6,
+ as_time_array);
+ if (ret < 0) {
+ dev_err(dev, "RTC_ALARM_SECOND block read failed %d\n", ret);
+ return ret;
+ }
+
+ as3722_reg_to_time(as_time_array, &alrm->time);
+ return 0;
+}
+
+static int as3722_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+ struct as3722 *as3722 = as3722_rtc->as3722;
+ u8 as_time_array[6];
+ int ret;
+
+ if (alrm->time.tm_year < (AS3722_RTC_START_YEAR - 1900))
+ return -EINVAL;
+
+ ret = as3722_rtc_alarm_irq_enable(dev, 0);
+ if (ret < 0) {
+ dev_err(dev, "Disable RTC alarm failed\n");
+ return ret;
+ }
+
+ as3722_time_to_reg(as_time_array, &alrm->time);
+ ret = as3722_block_write(as3722, AS3722_RTC_ALARM_SECOND_REG, 6,
+ as_time_array);
+ if (ret < 0) {
+ dev_err(dev, "RTC_ALARM_SECOND block write failed %d\n", ret);
+ return ret;
+ }
+
+ if (alrm->enabled)
+ ret = as3722_rtc_alarm_irq_enable(dev, alrm->enabled);
+ return ret;
+}
+
+static irqreturn_t as3722_alarm_irq(int irq, void *data)
+{
+ struct as3722_rtc *as3722_rtc = data;
+
+ rtc_update_irq(as3722_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops as3722_rtc_ops = {
+ .read_time = as3722_rtc_read_time,
+ .set_time = as3722_rtc_set_time,
+ .read_alarm = as3722_rtc_read_alarm,
+ .set_alarm = as3722_rtc_set_alarm,
+ .alarm_irq_enable = as3722_rtc_alarm_irq_enable,
+};
+
+static int as3722_rtc_probe(struct platform_device *pdev)
+{
+ struct as3722 *as3722 = dev_get_drvdata(pdev->dev.parent);
+ struct as3722_rtc *as3722_rtc;
+ int ret;
+
+ as3722_rtc = devm_kzalloc(&pdev->dev, sizeof(*as3722_rtc), GFP_KERNEL);
+ if (!as3722_rtc)
+ return -ENOMEM;
+
+ as3722_rtc->as3722 = as3722;
+ as3722_rtc->dev = &pdev->dev;
+ platform_set_drvdata(pdev, as3722_rtc);
+
+ /* Enable the RTC to make sure it is running. */
+ ret = as3722_update_bits(as3722, AS3722_RTC_CONTROL_REG,
+ AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN,
+ AS3722_RTC_ON | AS3722_RTC_ALARM_WAKEUP_EN);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RTC_CONTROL reg write failed: %d\n", ret);
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ as3722_rtc->rtc = devm_rtc_device_register(&pdev->dev, "as3722-rtc",
+ &as3722_rtc_ops, THIS_MODULE);
+ if (IS_ERR(as3722_rtc->rtc)) {
+ ret = PTR_ERR(as3722_rtc->rtc);
+ dev_err(&pdev->dev, "RTC register failed: %d\n", ret);
+ return ret;
+ }
+
+ as3722_rtc->alarm_irq = platform_get_irq(pdev, 0);
+ dev_info(&pdev->dev, "RTC interrupt %d\n", as3722_rtc->alarm_irq);
+
+ ret = devm_request_threaded_irq(&pdev->dev, as3722_rtc->alarm_irq, NULL,
+ as3722_alarm_irq, IRQF_ONESHOT | IRQF_EARLY_RESUME,
+ "rtc-alarm", as3722_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ as3722_rtc->alarm_irq, ret);
+ return ret;
+ }
+ disable_irq(as3722_rtc->alarm_irq);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int as3722_rtc_suspend(struct device *dev)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(as3722_rtc->alarm_irq);
+
+ return 0;
+}
+
+static int as3722_rtc_resume(struct device *dev)
+{
+ struct as3722_rtc *as3722_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(as3722_rtc->alarm_irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(as3722_rtc_pm_ops, as3722_rtc_suspend,
+ as3722_rtc_resume);
+
+static struct platform_driver as3722_rtc_driver = {
+ .probe = as3722_rtc_probe,
+ .driver = {
+ .name = "as3722-rtc",
+ .pm = &as3722_rtc_pm_ops,
+ },
+};
+module_platform_driver(as3722_rtc_driver);
+
+MODULE_DESCRIPTION("RTC driver for AS3722 PMICs");
+MODULE_ALIAS("platform:as3722-rtc");
+MODULE_AUTHOR("Florian Lobmaier <florian.lobmaier@ams.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-at32ap700x.c b/drivers/rtc/rtc-at32ap700x.c
new file mode 100644
index 000000000..d618d6c7e
--- /dev/null
+++ b/drivers/rtc/rtc-at32ap700x.c
@@ -0,0 +1,287 @@
+/*
+ * An RTC driver for the AVR32 AT32AP700x processor series.
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/io.h>
+
+/*
+ * This is a bare-bones RTC. It runs during most system sleep states, but has
+ * no battery backup and gets reset during system restart. It must be
+ * initialized from an external clock (network, I2C, etc) before it can be of
+ * much use.
+ *
+ * The alarm functionality is limited by the hardware, not supporting
+ * periodic interrupts.
+ */
+
+#define RTC_CTRL 0x00
+#define RTC_CTRL_EN 0
+#define RTC_CTRL_PCLR 1
+#define RTC_CTRL_TOPEN 2
+#define RTC_CTRL_PSEL 8
+
+#define RTC_VAL 0x04
+
+#define RTC_TOP 0x08
+
+#define RTC_IER 0x10
+#define RTC_IER_TOPI 0
+
+#define RTC_IDR 0x14
+#define RTC_IDR_TOPI 0
+
+#define RTC_IMR 0x18
+#define RTC_IMR_TOPI 0
+
+#define RTC_ISR 0x1c
+#define RTC_ISR_TOPI 0
+
+#define RTC_ICR 0x20
+#define RTC_ICR_TOPI 0
+
+#define RTC_BIT(name) (1 << RTC_##name)
+#define RTC_BF(name, value) ((value) << RTC_##name)
+
+#define rtc_readl(dev, reg) \
+ __raw_readl((dev)->regs + RTC_##reg)
+#define rtc_writel(dev, reg, value) \
+ __raw_writel((value), (dev)->regs + RTC_##reg)
+
+struct rtc_at32ap700x {
+ struct rtc_device *rtc;
+ void __iomem *regs;
+ unsigned long alarm_time;
+ unsigned long irq;
+ /* Protect against concurrent register access. */
+ spinlock_t lock;
+};
+
+static int at32_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
+ unsigned long now;
+
+ now = rtc_readl(rtc, VAL);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+static int at32_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
+ unsigned long now;
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &now);
+ if (ret == 0)
+ rtc_writel(rtc, VAL, now);
+
+ return ret;
+}
+
+static int at32_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&rtc->lock);
+ rtc_time_to_tm(rtc->alarm_time, &alrm->time);
+ alrm->enabled = rtc_readl(rtc, IMR) & RTC_BIT(IMR_TOPI) ? 1 : 0;
+ alrm->pending = rtc_readl(rtc, ISR) & RTC_BIT(ISR_TOPI) ? 1 : 0;
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int at32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
+ unsigned long rtc_unix_time;
+ unsigned long alarm_unix_time;
+ int ret;
+
+ rtc_unix_time = rtc_readl(rtc, VAL);
+
+ ret = rtc_tm_to_time(&alrm->time, &alarm_unix_time);
+ if (ret)
+ return ret;
+
+ if (alarm_unix_time < rtc_unix_time)
+ return -EINVAL;
+
+ spin_lock_irq(&rtc->lock);
+ rtc->alarm_time = alarm_unix_time;
+ rtc_writel(rtc, TOP, rtc->alarm_time);
+ if (alrm->enabled)
+ rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
+ | RTC_BIT(CTRL_TOPEN));
+ else
+ rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
+ & ~RTC_BIT(CTRL_TOPEN));
+ spin_unlock_irq(&rtc->lock);
+
+ return ret;
+}
+
+static int at32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct rtc_at32ap700x *rtc = dev_get_drvdata(dev);
+ int ret = 0;
+
+ spin_lock_irq(&rtc->lock);
+
+ if (enabled) {
+ if (rtc_readl(rtc, VAL) > rtc->alarm_time) {
+ ret = -EINVAL;
+ goto out;
+ }
+ rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
+ | RTC_BIT(CTRL_TOPEN));
+ rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
+ rtc_writel(rtc, IER, RTC_BIT(IER_TOPI));
+ } else {
+ rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
+ & ~RTC_BIT(CTRL_TOPEN));
+ rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
+ rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
+ }
+out:
+ spin_unlock_irq(&rtc->lock);
+
+ return ret;
+}
+
+static irqreturn_t at32_rtc_interrupt(int irq, void *dev_id)
+{
+ struct rtc_at32ap700x *rtc = (struct rtc_at32ap700x *)dev_id;
+ unsigned long isr = rtc_readl(rtc, ISR);
+ unsigned long events = 0;
+ int ret = IRQ_NONE;
+
+ spin_lock(&rtc->lock);
+
+ if (isr & RTC_BIT(ISR_TOPI)) {
+ rtc_writel(rtc, ICR, RTC_BIT(ICR_TOPI));
+ rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
+ rtc_writel(rtc, CTRL, rtc_readl(rtc, CTRL)
+ & ~RTC_BIT(CTRL_TOPEN));
+ rtc_writel(rtc, VAL, rtc->alarm_time);
+ events = RTC_AF | RTC_IRQF;
+ rtc_update_irq(rtc->rtc, 1, events);
+ ret = IRQ_HANDLED;
+ }
+
+ spin_unlock(&rtc->lock);
+
+ return ret;
+}
+
+static struct rtc_class_ops at32_rtc_ops = {
+ .read_time = at32_rtc_readtime,
+ .set_time = at32_rtc_settime,
+ .read_alarm = at32_rtc_readalarm,
+ .set_alarm = at32_rtc_setalarm,
+ .alarm_irq_enable = at32_rtc_alarm_irq_enable,
+};
+
+static int __init at32_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *regs;
+ struct rtc_at32ap700x *rtc;
+ int irq;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct rtc_at32ap700x),
+ GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_dbg(&pdev->dev, "no mmio resource defined\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_dbg(&pdev->dev, "could not get irq\n");
+ return -ENXIO;
+ }
+
+ rtc->irq = irq;
+ rtc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
+ if (!rtc->regs) {
+ dev_dbg(&pdev->dev, "could not map I/O memory\n");
+ return -ENOMEM;
+ }
+ spin_lock_init(&rtc->lock);
+
+ /*
+ * Maybe init RTC: count from zero at 1 Hz, disable wrap irq.
+ *
+ * Do not reset VAL register, as it can hold an old time
+ * from last JTAG reset.
+ */
+ if (!(rtc_readl(rtc, CTRL) & RTC_BIT(CTRL_EN))) {
+ rtc_writel(rtc, CTRL, RTC_BIT(CTRL_PCLR));
+ rtc_writel(rtc, IDR, RTC_BIT(IDR_TOPI));
+ rtc_writel(rtc, CTRL, RTC_BF(CTRL_PSEL, 0xe)
+ | RTC_BIT(CTRL_EN));
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, at32_rtc_interrupt, IRQF_SHARED,
+ "rtc", rtc);
+ if (ret) {
+ dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &at32_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ dev_dbg(&pdev->dev, "could not register rtc device\n");
+ return PTR_ERR(rtc->rtc);
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ dev_info(&pdev->dev, "Atmel RTC for AT32AP700x at %08lx irq %ld\n",
+ (unsigned long)rtc->regs, rtc->irq);
+
+ return 0;
+}
+
+static int __exit at32_rtc_remove(struct platform_device *pdev)
+{
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:at32ap700x_rtc");
+
+static struct platform_driver at32_rtc_driver = {
+ .remove = __exit_p(at32_rtc_remove),
+ .driver = {
+ .name = "at32ap700x_rtc",
+ },
+};
+
+module_platform_driver_probe(at32_rtc_driver, at32_rtc_probe);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
+MODULE_DESCRIPTION("Real time clock for AVR32 AT32AP700x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c
new file mode 100644
index 000000000..35efd3f75
--- /dev/null
+++ b/drivers/rtc/rtc-at91rm9200.c
@@ -0,0 +1,538 @@
+/*
+ * Real Time Clock interface for Linux on Atmel AT91RM9200
+ *
+ * Copyright (C) 2002 Rick Bronson
+ *
+ * Converted to RTC class model by Andrew Victor
+ *
+ * Ported to Linux 2.6 by Steven Scholz
+ * Based on s3c2410-rtc.c Simtec Electronics
+ *
+ * Based on sa1100-rtc.c by Nils Faerber
+ * Based on rtc.c by Paul Gortmaker
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/suspend.h>
+#include <linux/uaccess.h>
+
+#include "rtc-at91rm9200.h"
+
+#define at91_rtc_read(field) \
+ readl_relaxed(at91_rtc_regs + field)
+#define at91_rtc_write(field, val) \
+ writel_relaxed((val), at91_rtc_regs + field)
+
+#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */
+
+struct at91_rtc_config {
+ bool use_shadow_imr;
+};
+
+static const struct at91_rtc_config *at91_rtc_config;
+static DECLARE_COMPLETION(at91_rtc_updated);
+static DECLARE_COMPLETION(at91_rtc_upd_rdy);
+static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
+static void __iomem *at91_rtc_regs;
+static int irq;
+static DEFINE_SPINLOCK(at91_rtc_lock);
+static u32 at91_rtc_shadow_imr;
+static bool suspended;
+static DEFINE_SPINLOCK(suspended_lock);
+static unsigned long cached_events;
+static u32 at91_rtc_imr;
+
+static void at91_rtc_write_ier(u32 mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&at91_rtc_lock, flags);
+ at91_rtc_shadow_imr |= mask;
+ at91_rtc_write(AT91_RTC_IER, mask);
+ spin_unlock_irqrestore(&at91_rtc_lock, flags);
+}
+
+static void at91_rtc_write_idr(u32 mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&at91_rtc_lock, flags);
+ at91_rtc_write(AT91_RTC_IDR, mask);
+ /*
+ * Register read back (of any RTC-register) needed to make sure
+ * IDR-register write has reached the peripheral before updating
+ * shadow mask.
+ *
+ * Note that there is still a possibility that the mask is updated
+ * before interrupts have actually been disabled in hardware. The only
+ * way to be certain would be to poll the IMR-register, which is is
+ * the very register we are trying to emulate. The register read back
+ * is a reasonable heuristic.
+ */
+ at91_rtc_read(AT91_RTC_SR);
+ at91_rtc_shadow_imr &= ~mask;
+ spin_unlock_irqrestore(&at91_rtc_lock, flags);
+}
+
+static u32 at91_rtc_read_imr(void)
+{
+ unsigned long flags;
+ u32 mask;
+
+ if (at91_rtc_config->use_shadow_imr) {
+ spin_lock_irqsave(&at91_rtc_lock, flags);
+ mask = at91_rtc_shadow_imr;
+ spin_unlock_irqrestore(&at91_rtc_lock, flags);
+ } else {
+ mask = at91_rtc_read(AT91_RTC_IMR);
+ }
+
+ return mask;
+}
+
+/*
+ * Decode time/date into rtc_time structure
+ */
+static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg,
+ struct rtc_time *tm)
+{
+ unsigned int time, date;
+
+ /* must read twice in case it changes */
+ do {
+ time = at91_rtc_read(timereg);
+ date = at91_rtc_read(calreg);
+ } while ((time != at91_rtc_read(timereg)) ||
+ (date != at91_rtc_read(calreg)));
+
+ tm->tm_sec = bcd2bin((time & AT91_RTC_SEC) >> 0);
+ tm->tm_min = bcd2bin((time & AT91_RTC_MIN) >> 8);
+ tm->tm_hour = bcd2bin((time & AT91_RTC_HOUR) >> 16);
+
+ /*
+ * The Calendar Alarm register does not have a field for
+ * the year - so these will return an invalid value. When an
+ * alarm is set, at91_alarm_year will store the current year.
+ */
+ tm->tm_year = bcd2bin(date & AT91_RTC_CENT) * 100; /* century */
+ tm->tm_year += bcd2bin((date & AT91_RTC_YEAR) >> 8); /* year */
+
+ tm->tm_wday = bcd2bin((date & AT91_RTC_DAY) >> 21) - 1; /* day of the week [0-6], Sunday=0 */
+ tm->tm_mon = bcd2bin((date & AT91_RTC_MONTH) >> 16) - 1;
+ tm->tm_mday = bcd2bin((date & AT91_RTC_DATE) >> 24);
+}
+
+/*
+ * Read current time and date in RTC
+ */
+static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm);
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year = tm->tm_year - 1900;
+
+ dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long cr;
+
+ dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ wait_for_completion(&at91_rtc_upd_rdy);
+
+ /* Stop Time/Calendar from counting */
+ cr = at91_rtc_read(AT91_RTC_CR);
+ at91_rtc_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM);
+
+ at91_rtc_write_ier(AT91_RTC_ACKUPD);
+ wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */
+ at91_rtc_write_idr(AT91_RTC_ACKUPD);
+
+ at91_rtc_write(AT91_RTC_TIMR,
+ bin2bcd(tm->tm_sec) << 0
+ | bin2bcd(tm->tm_min) << 8
+ | bin2bcd(tm->tm_hour) << 16);
+
+ at91_rtc_write(AT91_RTC_CALR,
+ bin2bcd((tm->tm_year + 1900) / 100) /* century */
+ | bin2bcd(tm->tm_year % 100) << 8 /* year */
+ | bin2bcd(tm->tm_mon + 1) << 16 /* tm_mon starts at zero */
+ | bin2bcd(tm->tm_wday + 1) << 21 /* day of the week [0-6], Sunday=0 */
+ | bin2bcd(tm->tm_mday) << 24);
+
+ /* Restart Time/Calendar */
+ cr = at91_rtc_read(AT91_RTC_CR);
+ at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_SECEV);
+ at91_rtc_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM));
+ at91_rtc_write_ier(AT91_RTC_SECEV);
+
+ return 0;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *tm = &alrm->time;
+
+ at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year = at91_alarm_year - 1900;
+
+ alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
+ ? 1 : 0;
+
+ dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+/*
+ * Set alarm time and date in RTC
+ */
+static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time tm;
+
+ at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
+
+ at91_alarm_year = tm.tm_year;
+
+ tm.tm_mon = alrm->time.tm_mon;
+ tm.tm_mday = alrm->time.tm_mday;
+ tm.tm_hour = alrm->time.tm_hour;
+ tm.tm_min = alrm->time.tm_min;
+ tm.tm_sec = alrm->time.tm_sec;
+
+ at91_rtc_write_idr(AT91_RTC_ALARM);
+ at91_rtc_write(AT91_RTC_TIMALR,
+ bin2bcd(tm.tm_sec) << 0
+ | bin2bcd(tm.tm_min) << 8
+ | bin2bcd(tm.tm_hour) << 16
+ | AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN);
+ at91_rtc_write(AT91_RTC_CALALR,
+ bin2bcd(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */
+ | bin2bcd(tm.tm_mday) << 24
+ | AT91_RTC_DATEEN | AT91_RTC_MTHEN);
+
+ if (alrm->enabled) {
+ at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
+ at91_rtc_write_ier(AT91_RTC_ALARM);
+ }
+
+ dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
+ at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+
+ return 0;
+}
+
+static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ dev_dbg(dev, "%s(): cmd=%08x\n", __func__, enabled);
+
+ if (enabled) {
+ at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
+ at91_rtc_write_ier(AT91_RTC_ALARM);
+ } else
+ at91_rtc_write_idr(AT91_RTC_ALARM);
+
+ return 0;
+}
+/*
+ * Provide additional RTC information in /proc/driver/rtc
+ */
+static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ unsigned long imr = at91_rtc_read_imr();
+
+ seq_printf(seq, "update_IRQ\t: %s\n",
+ (imr & AT91_RTC_ACKUPD) ? "yes" : "no");
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (imr & AT91_RTC_SECEV) ? "yes" : "no");
+
+ return 0;
+}
+
+/*
+ * IRQ handler for the RTC
+ */
+static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ unsigned int rtsr;
+ unsigned long events = 0;
+ int ret = IRQ_NONE;
+
+ spin_lock(&suspended_lock);
+ rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr();
+ if (rtsr) { /* this interrupt is shared! Is it ours? */
+ if (rtsr & AT91_RTC_ALARM)
+ events |= (RTC_AF | RTC_IRQF);
+ if (rtsr & AT91_RTC_SECEV) {
+ complete(&at91_rtc_upd_rdy);
+ at91_rtc_write_idr(AT91_RTC_SECEV);
+ }
+ if (rtsr & AT91_RTC_ACKUPD)
+ complete(&at91_rtc_updated);
+
+ at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
+
+ if (!suspended) {
+ rtc_update_irq(rtc, 1, events);
+
+ dev_dbg(&pdev->dev, "%s(): num=%ld, events=0x%02lx\n",
+ __func__, events >> 8, events & 0x000000FF);
+ } else {
+ cached_events |= events;
+ at91_rtc_write_idr(at91_rtc_imr);
+ pm_system_wakeup();
+ }
+
+ ret = IRQ_HANDLED;
+ }
+ spin_unlock(&suspended_lock);
+
+ return ret;
+}
+
+static const struct at91_rtc_config at91rm9200_config = {
+};
+
+static const struct at91_rtc_config at91sam9x5_config = {
+ .use_shadow_imr = true,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id at91_rtc_dt_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-rtc",
+ .data = &at91rm9200_config,
+ }, {
+ .compatible = "atmel,at91sam9x5-rtc",
+ .data = &at91sam9x5_config,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
+#endif
+
+static const struct at91_rtc_config *
+at91_rtc_get_config(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ if (pdev->dev.of_node) {
+ match = of_match_node(at91_rtc_dt_ids, pdev->dev.of_node);
+ if (!match)
+ return NULL;
+ return (const struct at91_rtc_config *)match->data;
+ }
+
+ return &at91rm9200_config;
+}
+
+static const struct rtc_class_ops at91_rtc_ops = {
+ .read_time = at91_rtc_readtime,
+ .set_time = at91_rtc_settime,
+ .read_alarm = at91_rtc_readalarm,
+ .set_alarm = at91_rtc_setalarm,
+ .proc = at91_rtc_proc,
+ .alarm_irq_enable = at91_rtc_alarm_irq_enable,
+};
+
+/*
+ * Initialize and install RTC driver
+ */
+static int __init at91_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *regs;
+ int ret = 0;
+
+ at91_rtc_config = at91_rtc_get_config(pdev);
+ if (!at91_rtc_config)
+ return -ENODEV;
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!regs) {
+ dev_err(&pdev->dev, "no mmio resource defined\n");
+ return -ENXIO;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq resource defined\n");
+ return -ENXIO;
+ }
+
+ at91_rtc_regs = devm_ioremap(&pdev->dev, regs->start,
+ resource_size(regs));
+ if (!at91_rtc_regs) {
+ dev_err(&pdev->dev, "failed to map registers, aborting.\n");
+ return -ENOMEM;
+ }
+
+ at91_rtc_write(AT91_RTC_CR, 0);
+ at91_rtc_write(AT91_RTC_MR, 0); /* 24 hour mode */
+
+ /* Disable all interrupts */
+ at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+ AT91_RTC_SECEV | AT91_RTC_TIMEV |
+ AT91_RTC_CALEV);
+
+ ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt,
+ IRQF_SHARED | IRQF_COND_SUSPEND,
+ "at91_rtc", pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ %d already in use.\n", irq);
+ return ret;
+ }
+
+ /* cpu init code should really have flagged this device as
+ * being wake-capable; if it didn't, do that here.
+ */
+ if (!device_can_wakeup(&pdev->dev))
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &at91_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+ platform_set_drvdata(pdev, rtc);
+
+ /* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy
+ * completion.
+ */
+ at91_rtc_write_ier(AT91_RTC_SECEV);
+
+ dev_info(&pdev->dev, "AT91 Real Time Clock driver.\n");
+ return 0;
+}
+
+/*
+ * Disable and remove the RTC driver
+ */
+static int __exit at91_rtc_remove(struct platform_device *pdev)
+{
+ /* Disable all interrupts */
+ at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+ AT91_RTC_SECEV | AT91_RTC_TIMEV |
+ AT91_RTC_CALEV);
+
+ return 0;
+}
+
+static void at91_rtc_shutdown(struct platform_device *pdev)
+{
+ /* Disable all interrupts */
+ at91_rtc_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
+ AT91_RTC_SECEV | AT91_RTC_TIMEV |
+ AT91_RTC_CALEV);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/* AT91RM9200 RTC Power management control */
+
+static int at91_rtc_suspend(struct device *dev)
+{
+ /* this IRQ is shared with DBGU and other hardware which isn't
+ * necessarily doing PM like we are...
+ */
+ at91_rtc_imr = at91_rtc_read_imr()
+ & (AT91_RTC_ALARM|AT91_RTC_SECEV);
+ if (at91_rtc_imr) {
+ if (device_may_wakeup(dev)) {
+ unsigned long flags;
+
+ enable_irq_wake(irq);
+
+ spin_lock_irqsave(&suspended_lock, flags);
+ suspended = true;
+ spin_unlock_irqrestore(&suspended_lock, flags);
+ } else {
+ at91_rtc_write_idr(at91_rtc_imr);
+ }
+ }
+ return 0;
+}
+
+static int at91_rtc_resume(struct device *dev)
+{
+ struct rtc_device *rtc = dev_get_drvdata(dev);
+
+ if (at91_rtc_imr) {
+ if (device_may_wakeup(dev)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&suspended_lock, flags);
+
+ if (cached_events) {
+ rtc_update_irq(rtc, 1, cached_events);
+ cached_events = 0;
+ }
+
+ suspended = false;
+ spin_unlock_irqrestore(&suspended_lock, flags);
+
+ disable_irq_wake(irq);
+ }
+ at91_rtc_write_ier(at91_rtc_imr);
+ }
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
+
+static struct platform_driver at91_rtc_driver = {
+ .remove = __exit_p(at91_rtc_remove),
+ .shutdown = at91_rtc_shutdown,
+ .driver = {
+ .name = "at91_rtc",
+ .pm = &at91_rtc_pm_ops,
+ .of_match_table = of_match_ptr(at91_rtc_dt_ids),
+ },
+};
+
+module_platform_driver_probe(at91_rtc_driver, at91_rtc_probe);
+
+MODULE_AUTHOR("Rick Bronson");
+MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_rtc");
diff --git a/drivers/rtc/rtc-at91rm9200.h b/drivers/rtc/rtc-at91rm9200.h
new file mode 100644
index 000000000..da1945e5f
--- /dev/null
+++ b/drivers/rtc/rtc-at91rm9200.h
@@ -0,0 +1,75 @@
+/*
+ * arch/arm/mach-at91/include/mach/at91_rtc.h
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * Real Time Clock (RTC) - System peripheral registers.
+ * Based on AT91RM9200 datasheet revision E.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91_RTC_H
+#define AT91_RTC_H
+
+#define AT91_RTC_CR 0x00 /* Control Register */
+#define AT91_RTC_UPDTIM (1 << 0) /* Update Request Time Register */
+#define AT91_RTC_UPDCAL (1 << 1) /* Update Request Calendar Register */
+#define AT91_RTC_TIMEVSEL (3 << 8) /* Time Event Selection */
+#define AT91_RTC_TIMEVSEL_MINUTE (0 << 8)
+#define AT91_RTC_TIMEVSEL_HOUR (1 << 8)
+#define AT91_RTC_TIMEVSEL_DAY24 (2 << 8)
+#define AT91_RTC_TIMEVSEL_DAY12 (3 << 8)
+#define AT91_RTC_CALEVSEL (3 << 16) /* Calendar Event Selection */
+#define AT91_RTC_CALEVSEL_WEEK (0 << 16)
+#define AT91_RTC_CALEVSEL_MONTH (1 << 16)
+#define AT91_RTC_CALEVSEL_YEAR (2 << 16)
+
+#define AT91_RTC_MR 0x04 /* Mode Register */
+#define AT91_RTC_HRMOD (1 << 0) /* 12/24 Hour Mode */
+
+#define AT91_RTC_TIMR 0x08 /* Time Register */
+#define AT91_RTC_SEC (0x7f << 0) /* Current Second */
+#define AT91_RTC_MIN (0x7f << 8) /* Current Minute */
+#define AT91_RTC_HOUR (0x3f << 16) /* Current Hour */
+#define AT91_RTC_AMPM (1 << 22) /* Ante Meridiem Post Meridiem Indicator */
+
+#define AT91_RTC_CALR 0x0c /* Calendar Register */
+#define AT91_RTC_CENT (0x7f << 0) /* Current Century */
+#define AT91_RTC_YEAR (0xff << 8) /* Current Year */
+#define AT91_RTC_MONTH (0x1f << 16) /* Current Month */
+#define AT91_RTC_DAY (7 << 21) /* Current Day */
+#define AT91_RTC_DATE (0x3f << 24) /* Current Date */
+
+#define AT91_RTC_TIMALR 0x10 /* Time Alarm Register */
+#define AT91_RTC_SECEN (1 << 7) /* Second Alarm Enable */
+#define AT91_RTC_MINEN (1 << 15) /* Minute Alarm Enable */
+#define AT91_RTC_HOUREN (1 << 23) /* Hour Alarm Enable */
+
+#define AT91_RTC_CALALR 0x14 /* Calendar Alarm Register */
+#define AT91_RTC_MTHEN (1 << 23) /* Month Alarm Enable */
+#define AT91_RTC_DATEEN (1 << 31) /* Date Alarm Enable */
+
+#define AT91_RTC_SR 0x18 /* Status Register */
+#define AT91_RTC_ACKUPD (1 << 0) /* Acknowledge for Update */
+#define AT91_RTC_ALARM (1 << 1) /* Alarm Flag */
+#define AT91_RTC_SECEV (1 << 2) /* Second Event */
+#define AT91_RTC_TIMEV (1 << 3) /* Time Event */
+#define AT91_RTC_CALEV (1 << 4) /* Calendar Event */
+
+#define AT91_RTC_SCCR 0x1c /* Status Clear Command Register */
+#define AT91_RTC_IER 0x20 /* Interrupt Enable Register */
+#define AT91_RTC_IDR 0x24 /* Interrupt Disable Register */
+#define AT91_RTC_IMR 0x28 /* Interrupt Mask Register */
+
+#define AT91_RTC_VER 0x2c /* Valid Entry Register */
+#define AT91_RTC_NVTIM (1 << 0) /* Non valid Time */
+#define AT91_RTC_NVCAL (1 << 1) /* Non valid Calendar */
+#define AT91_RTC_NVTIMALR (1 << 2) /* Non valid Time Alarm */
+#define AT91_RTC_NVCALALR (1 << 3) /* Non valid Calendar Alarm */
+
+#endif
diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
new file mode 100644
index 000000000..5ccaee32d
--- /dev/null
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -0,0 +1,586 @@
+/*
+ * "RTT as Real Time Clock" driver for AT91SAM9 SoC family
+ *
+ * (C) 2007 Michel Benoit
+ *
+ * Based on rtc-at91rm9200.c by Rick Bronson
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/platform_data/atmel.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/suspend.h>
+#include <linux/clk.h>
+
+/*
+ * This driver uses two configurable hardware resources that live in the
+ * AT91SAM9 backup power domain (intended to be powered at all times)
+ * to implement the Real Time Clock interfaces
+ *
+ * - A "Real-time Timer" (RTT) counts up in seconds from a base time.
+ * We can't assign the counter value (CRTV) ... but we can reset it.
+ *
+ * - One of the "General Purpose Backup Registers" (GPBRs) holds the
+ * base time, normally an offset from the beginning of the POSIX
+ * epoch (1970-Jan-1 00:00:00 UTC). Some systems also include the
+ * local timezone's offset.
+ *
+ * The RTC's value is the RTT counter plus that offset. The RTC's alarm
+ * is likewise a base (ALMV) plus that offset.
+ *
+ * Not all RTTs will be used as RTCs; some systems have multiple RTTs to
+ * choose from, or a "real" RTC module. All systems have multiple GPBR
+ * registers available, likewise usable for more than "RTC" support.
+ */
+
+#define AT91_RTT_MR 0x00 /* Real-time Mode Register */
+#define AT91_RTT_RTPRES (0xffff << 0) /* Real-time Timer Prescaler Value */
+#define AT91_RTT_ALMIEN (1 << 16) /* Alarm Interrupt Enable */
+#define AT91_RTT_RTTINCIEN (1 << 17) /* Real Time Timer Increment Interrupt Enable */
+#define AT91_RTT_RTTRST (1 << 18) /* Real Time Timer Restart */
+
+#define AT91_RTT_AR 0x04 /* Real-time Alarm Register */
+#define AT91_RTT_ALMV (0xffffffff) /* Alarm Value */
+
+#define AT91_RTT_VR 0x08 /* Real-time Value Register */
+#define AT91_RTT_CRTV (0xffffffff) /* Current Real-time Value */
+
+#define AT91_RTT_SR 0x0c /* Real-time Status Register */
+#define AT91_RTT_ALMS (1 << 0) /* Real-time Alarm Status */
+#define AT91_RTT_RTTINC (1 << 1) /* Real-time Timer Increment */
+
+/*
+ * We store ALARM_DISABLED in ALMV to record that no alarm is set.
+ * It's also the reset value for that field.
+ */
+#define ALARM_DISABLED ((u32)~0)
+
+
+struct sam9_rtc {
+ void __iomem *rtt;
+ struct rtc_device *rtcdev;
+ u32 imr;
+ struct regmap *gpbr;
+ unsigned int gpbr_offset;
+ int irq;
+ struct clk *sclk;
+ bool suspended;
+ unsigned long events;
+ spinlock_t lock;
+};
+
+#define rtt_readl(rtc, field) \
+ readl((rtc)->rtt + AT91_RTT_ ## field)
+#define rtt_writel(rtc, field, val) \
+ writel((val), (rtc)->rtt + AT91_RTT_ ## field)
+
+static inline unsigned int gpbr_readl(struct sam9_rtc *rtc)
+{
+ unsigned int val;
+
+ regmap_read(rtc->gpbr, rtc->gpbr_offset, &val);
+
+ return val;
+}
+
+static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val)
+{
+ regmap_write(rtc->gpbr, rtc->gpbr_offset, val);
+}
+
+/*
+ * Read current time and date in RTC
+ */
+static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ u32 secs, secs2;
+ u32 offset;
+
+ /* read current time offset */
+ offset = gpbr_readl(rtc);
+ if (offset == 0)
+ return -EILSEQ;
+
+ /* reread the counter to help sync the two clock domains */
+ secs = rtt_readl(rtc, VR);
+ secs2 = rtt_readl(rtc, VR);
+ if (secs != secs2)
+ secs = rtt_readl(rtc, VR);
+
+ rtc_time_to_tm(offset + secs, tm);
+
+ dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readtime",
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ int err;
+ u32 offset, alarm, mr;
+ unsigned long secs;
+
+ dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "settime",
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ err = rtc_tm_to_time(tm, &secs);
+ if (err != 0)
+ return err;
+
+ mr = rtt_readl(rtc, MR);
+
+ /* disable interrupts */
+ rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
+
+ /* read current time offset */
+ offset = gpbr_readl(rtc);
+
+ /* store the new base time in a battery backup register */
+ secs += 1;
+ gpbr_writel(rtc, secs);
+
+ /* adjust the alarm time for the new base */
+ alarm = rtt_readl(rtc, AR);
+ if (alarm != ALARM_DISABLED) {
+ if (offset > secs) {
+ /* time jumped backwards, increase time until alarm */
+ alarm += (offset - secs);
+ } else if ((alarm + offset) > secs) {
+ /* time jumped forwards, decrease time until alarm */
+ alarm -= (secs - offset);
+ } else {
+ /* time jumped past the alarm, disable alarm */
+ alarm = ALARM_DISABLED;
+ mr &= ~AT91_RTT_ALMIEN;
+ }
+ rtt_writel(rtc, AR, alarm);
+ }
+
+ /* reset the timer, and re-enable interrupts */
+ rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST);
+
+ return 0;
+}
+
+static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ struct rtc_time *tm = &alrm->time;
+ u32 alarm = rtt_readl(rtc, AR);
+ u32 offset;
+
+ offset = gpbr_readl(rtc);
+ if (offset == 0)
+ return -EILSEQ;
+
+ memset(alrm, 0, sizeof(*alrm));
+ if (alarm != ALARM_DISABLED && offset != 0) {
+ rtc_time_to_tm(offset + alarm, tm);
+
+ dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "readalarm",
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN)
+ alrm->enabled = 1;
+ }
+
+ return 0;
+}
+
+static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ struct rtc_time *tm = &alrm->time;
+ unsigned long secs;
+ u32 offset;
+ u32 mr;
+ int err;
+
+ err = rtc_tm_to_time(tm, &secs);
+ if (err != 0)
+ return err;
+
+ offset = gpbr_readl(rtc);
+ if (offset == 0) {
+ /* time is not set */
+ return -EILSEQ;
+ }
+ mr = rtt_readl(rtc, MR);
+ rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN);
+
+ /* alarm in the past? finish and leave disabled */
+ if (secs <= offset) {
+ rtt_writel(rtc, AR, ALARM_DISABLED);
+ return 0;
+ }
+
+ /* else set alarm and maybe enable it */
+ rtt_writel(rtc, AR, secs - offset);
+ if (alrm->enabled)
+ rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
+
+ dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d\n", "setalarm",
+ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ u32 mr = rtt_readl(rtc, MR);
+
+ dev_dbg(dev, "alarm_irq_enable: enabled=%08x, mr %08x\n", enabled, mr);
+ if (enabled)
+ rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
+ else
+ rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN);
+ return 0;
+}
+
+/*
+ * Provide additional RTC information in /proc/driver/rtc
+ */
+static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ u32 mr = mr = rtt_readl(rtc, MR);
+
+ seq_printf(seq, "update_IRQ\t: %s\n",
+ (mr & AT91_RTT_RTTINCIEN) ? "yes" : "no");
+ return 0;
+}
+
+static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc)
+{
+ u32 sr, mr;
+
+ /* Shared interrupt may be for another device. Note: reading
+ * SR clears it, so we must only read it in this irq handler!
+ */
+ mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
+ sr = rtt_readl(rtc, SR) & (mr >> 16);
+ if (!sr)
+ return IRQ_NONE;
+
+ /* alarm status */
+ if (sr & AT91_RTT_ALMS)
+ rtc->events |= (RTC_AF | RTC_IRQF);
+
+ /* timer update/increment */
+ if (sr & AT91_RTT_RTTINC)
+ rtc->events |= (RTC_UF | RTC_IRQF);
+
+ return IRQ_HANDLED;
+}
+
+static void at91_rtc_flush_events(struct sam9_rtc *rtc)
+{
+ if (!rtc->events)
+ return;
+
+ rtc_update_irq(rtc->rtcdev, 1, rtc->events);
+ rtc->events = 0;
+
+ pr_debug("%s: num=%ld, events=0x%02lx\n", __func__,
+ rtc->events >> 8, rtc->events & 0x000000FF);
+}
+
+/*
+ * IRQ handler for the RTC
+ */
+static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
+{
+ struct sam9_rtc *rtc = _rtc;
+ int ret;
+
+ spin_lock(&rtc->lock);
+
+ ret = at91_rtc_cache_events(rtc);
+
+ /* We're called in suspended state */
+ if (rtc->suspended) {
+ /* Mask irqs coming from this peripheral */
+ rtt_writel(rtc, MR,
+ rtt_readl(rtc, MR) &
+ ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
+ /* Trigger a system wakeup */
+ pm_system_wakeup();
+ } else {
+ at91_rtc_flush_events(rtc);
+ }
+
+ spin_unlock(&rtc->lock);
+
+ return ret;
+}
+
+static const struct rtc_class_ops at91_rtc_ops = {
+ .read_time = at91_rtc_readtime,
+ .set_time = at91_rtc_settime,
+ .read_alarm = at91_rtc_readalarm,
+ .set_alarm = at91_rtc_setalarm,
+ .proc = at91_rtc_proc,
+ .alarm_irq_enable = at91_rtc_alarm_irq_enable,
+};
+
+static const struct regmap_config gpbr_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+/*
+ * Initialize and install RTC driver
+ */
+static int at91_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *r;
+ struct sam9_rtc *rtc;
+ int ret, irq;
+ u32 mr;
+ unsigned int sclk_rate;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get interrupt resource\n");
+ return irq;
+ }
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->irq = irq;
+
+ /* platform setup code should have handled this; sigh */
+ if (!device_can_wakeup(&pdev->dev))
+ device_init_wakeup(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, rtc);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->rtt = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(rtc->rtt))
+ return PTR_ERR(rtc->rtt);
+
+ if (!pdev->dev.of_node) {
+ /*
+ * TODO: Remove this code chunk when removing non DT board
+ * support. Remember to remove the gpbr_regmap_config
+ * variable too.
+ */
+ void __iomem *gpbr;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ gpbr = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(gpbr))
+ return PTR_ERR(gpbr);
+
+ rtc->gpbr = regmap_init_mmio(NULL, gpbr,
+ &gpbr_regmap_config);
+ } else {
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+ "atmel,rtt-rtc-time-reg", 1, 0,
+ &args);
+ if (ret)
+ return ret;
+
+ rtc->gpbr = syscon_node_to_regmap(args.np);
+ rtc->gpbr_offset = args.args[0];
+ }
+
+ if (IS_ERR(rtc->gpbr)) {
+ dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.\n");
+ return -ENOMEM;
+ }
+
+ rtc->sclk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rtc->sclk))
+ return PTR_ERR(rtc->sclk);
+
+ sclk_rate = clk_get_rate(rtc->sclk);
+ if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) {
+ dev_err(&pdev->dev, "Invalid slow clock rate\n");
+ return -EINVAL;
+ }
+
+ ret = clk_prepare_enable(rtc->sclk);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not enable slow clock\n");
+ return ret;
+ }
+
+ mr = rtt_readl(rtc, MR);
+
+ /* unless RTT is counting at 1 Hz, re-initialize it */
+ if ((mr & AT91_RTT_RTPRES) != sclk_rate) {
+ mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES);
+ gpbr_writel(rtc, 0);
+ }
+
+ /* disable all interrupts (same as on shutdown path) */
+ mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
+ rtt_writel(rtc, MR, mr);
+
+ rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &at91_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtcdev))
+ return PTR_ERR(rtc->rtcdev);
+
+ /* register irq handler after we know what name we'll use */
+ ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt,
+ IRQF_SHARED | IRQF_COND_SUSPEND,
+ dev_name(&rtc->rtcdev->dev), rtc);
+ if (ret) {
+ dev_dbg(&pdev->dev, "can't share IRQ %d?\n", rtc->irq);
+ return ret;
+ }
+
+ /* NOTE: sam9260 rev A silicon has a ROM bug which resets the
+ * RTT on at least some reboots. If you have that chip, you must
+ * initialize the time from some external source like a GPS, wall
+ * clock, discrete RTC, etc
+ */
+
+ if (gpbr_readl(rtc) == 0)
+ dev_warn(&pdev->dev, "%s: SET TIME!\n",
+ dev_name(&rtc->rtcdev->dev));
+
+ return 0;
+}
+
+/*
+ * Disable and remove the RTC driver
+ */
+static int at91_rtc_remove(struct platform_device *pdev)
+{
+ struct sam9_rtc *rtc = platform_get_drvdata(pdev);
+ u32 mr = rtt_readl(rtc, MR);
+
+ /* disable all interrupts */
+ rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
+
+ if (!IS_ERR(rtc->sclk))
+ clk_disable_unprepare(rtc->sclk);
+
+ return 0;
+}
+
+static void at91_rtc_shutdown(struct platform_device *pdev)
+{
+ struct sam9_rtc *rtc = platform_get_drvdata(pdev);
+ u32 mr = rtt_readl(rtc, MR);
+
+ rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
+ rtt_writel(rtc, MR, mr & ~rtc->imr);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/* AT91SAM9 RTC Power management control */
+
+static int at91_rtc_suspend(struct device *dev)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ u32 mr = rtt_readl(rtc, MR);
+
+ /*
+ * This IRQ is shared with DBGU and other hardware which isn't
+ * necessarily a wakeup event source.
+ */
+ rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
+ if (rtc->imr) {
+ if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) {
+ unsigned long flags;
+
+ enable_irq_wake(rtc->irq);
+ spin_lock_irqsave(&rtc->lock, flags);
+ rtc->suspended = true;
+ spin_unlock_irqrestore(&rtc->lock, flags);
+ /* don't let RTTINC cause wakeups */
+ if (mr & AT91_RTT_RTTINCIEN)
+ rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
+ } else
+ rtt_writel(rtc, MR, mr & ~rtc->imr);
+ }
+
+ return 0;
+}
+
+static int at91_rtc_resume(struct device *dev)
+{
+ struct sam9_rtc *rtc = dev_get_drvdata(dev);
+ u32 mr;
+
+ if (rtc->imr) {
+ unsigned long flags;
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq);
+ mr = rtt_readl(rtc, MR);
+ rtt_writel(rtc, MR, mr | rtc->imr);
+
+ spin_lock_irqsave(&rtc->lock, flags);
+ rtc->suspended = false;
+ at91_rtc_cache_events(rtc);
+ at91_rtc_flush_events(rtc);
+ spin_unlock_irqrestore(&rtc->lock, flags);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id at91_rtc_dt_ids[] = {
+ { .compatible = "atmel,at91sam9260-rtt" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
+#endif
+
+static struct platform_driver at91_rtc_driver = {
+ .probe = at91_rtc_probe,
+ .remove = at91_rtc_remove,
+ .shutdown = at91_rtc_shutdown,
+ .driver = {
+ .name = "rtc-at91sam9",
+ .pm = &at91_rtc_pm_ops,
+ .of_match_table = of_match_ptr(at91_rtc_dt_ids),
+ },
+};
+
+module_platform_driver(at91_rtc_driver);
+
+MODULE_AUTHOR("Michel Benoit");
+MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-au1xxx.c b/drivers/rtc/rtc-au1xxx.c
new file mode 100644
index 000000000..84d6e0267
--- /dev/null
+++ b/drivers/rtc/rtc-au1xxx.c
@@ -0,0 +1,128 @@
+/*
+ * Au1xxx counter0 (aka Time-Of-Year counter) RTC interface driver.
+ *
+ * Copyright (C) 2008 Manuel Lauss <mano@roarinelk.homelinux.net>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+/* All current Au1xxx SoCs have 2 counters fed by an external 32.768 kHz
+ * crystal. Counter 0, which keeps counting during sleep/powerdown, is
+ * used to count seconds since the beginning of the unix epoch.
+ *
+ * The counters must be configured and enabled by bootloader/board code;
+ * no checks as to whether they really get a proper 32.768kHz clock are
+ * made as this would take far too long.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <asm/mach-au1x00/au1000.h>
+
+/* 32kHz clock enabled and detected */
+#define CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
+
+static int au1xtoy_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long t;
+
+ t = alchemy_rdsys(AU1000_SYS_TOYREAD);
+
+ rtc_time_to_tm(t, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int au1xtoy_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long t;
+
+ rtc_tm_to_time(tm, &t);
+
+ alchemy_wrsys(t, AU1000_SYS_TOYWRITE);
+
+ /* wait for the pending register write to succeed. This can
+ * take up to 6 seconds...
+ */
+ while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S)
+ msleep(1);
+
+ return 0;
+}
+
+static struct rtc_class_ops au1xtoy_rtc_ops = {
+ .read_time = au1xtoy_rtc_read_time,
+ .set_time = au1xtoy_rtc_set_time,
+};
+
+static int au1xtoy_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtcdev;
+ unsigned long t;
+ int ret;
+
+ t = alchemy_rdsys(AU1000_SYS_CNTRCTRL);
+ if (!(t & CNTR_OK)) {
+ dev_err(&pdev->dev, "counters not working; aborting.\n");
+ ret = -ENODEV;
+ goto out_err;
+ }
+
+ ret = -ETIMEDOUT;
+
+ /* set counter0 tickrate to 1Hz if necessary */
+ if (alchemy_rdsys(AU1000_SYS_TOYTRIM) != 32767) {
+ /* wait until hardware gives access to TRIM register */
+ t = 0x00100000;
+ while ((alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_T0S) && --t)
+ msleep(1);
+
+ if (!t) {
+ /* timed out waiting for register access; assume
+ * counters are unusable.
+ */
+ dev_err(&pdev->dev, "timeout waiting for access\n");
+ goto out_err;
+ }
+
+ /* set 1Hz TOY tick rate */
+ alchemy_wrsys(32767, AU1000_SYS_TOYTRIM);
+ }
+
+ /* wait until the hardware allows writes to the counter reg */
+ while (alchemy_rdsys(AU1000_SYS_CNTRCTRL) & SYS_CNTRL_C0S)
+ msleep(1);
+
+ rtcdev = devm_rtc_device_register(&pdev->dev, "rtc-au1xxx",
+ &au1xtoy_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtcdev)) {
+ ret = PTR_ERR(rtcdev);
+ goto out_err;
+ }
+
+ platform_set_drvdata(pdev, rtcdev);
+
+ return 0;
+
+out_err:
+ return ret;
+}
+
+static struct platform_driver au1xrtc_driver = {
+ .driver = {
+ .name = "rtc-au1xxx",
+ },
+};
+
+module_platform_driver_probe(au1xrtc_driver, au1xtoy_rtc_probe);
+
+MODULE_DESCRIPTION("Au1xxx TOY-counter-based RTC driver");
+MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-au1xxx");
diff --git a/drivers/rtc/rtc-bfin.c b/drivers/rtc/rtc-bfin.c
new file mode 100644
index 000000000..3d44b1172
--- /dev/null
+++ b/drivers/rtc/rtc-bfin.c
@@ -0,0 +1,448 @@
+/*
+ * Blackfin On-Chip Real Time Clock Driver
+ * Supports BF51x/BF52x/BF53[123]/BF53[467]/BF54x
+ *
+ * Copyright 2004-2010 Analog Devices Inc.
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+/* The biggest issue we deal with in this driver is that register writes are
+ * synced to the RTC frequency of 1Hz. So if you write to a register and
+ * attempt to write again before the first write has completed, the new write
+ * is simply discarded. This can easily be troublesome if userspace disables
+ * one event (say periodic) and then right after enables an event (say alarm).
+ * Since all events are maintained in the same interrupt mask register, if
+ * we wrote to it to disable the first event and then wrote to it again to
+ * enable the second event, that second event would not be enabled as the
+ * write would be discarded and things quickly fall apart.
+ *
+ * To keep this delay from significantly degrading performance (we, in theory,
+ * would have to sleep for up to 1 second every time we wanted to write a
+ * register), we only check the write pending status before we start to issue
+ * a new write. We bank on the idea that it doesn't matter when the sync
+ * happens so long as we don't attempt another write before it does. The only
+ * time userspace would take this penalty is when they try and do multiple
+ * operations right after another ... but in this case, they need to take the
+ * sync penalty, so we should be OK.
+ *
+ * Also note that the RTC_ISTAT register does not suffer this penalty; its
+ * writes to clear status registers complete immediately.
+ */
+
+/* It may seem odd that there is no SWCNT code in here (which would be exposed
+ * via the periodic interrupt event, or PIE). Since the Blackfin RTC peripheral
+ * runs in units of seconds (N/HZ) but the Linux framework runs in units of HZ
+ * (2^N HZ), there is no point in keeping code that only provides 1 HZ PIEs.
+ * The same exact behavior can be accomplished by using the update interrupt
+ * event (UIE). Maybe down the line the RTC peripheral will suck less in which
+ * case we can re-introduce PIE support.
+ */
+
+#include <linux/bcd.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+
+#include <asm/blackfin.h>
+
+#define dev_dbg_stamp(dev) dev_dbg(dev, "%s:%i: here i am\n", __func__, __LINE__)
+
+struct bfin_rtc {
+ struct rtc_device *rtc_dev;
+ struct rtc_time rtc_alarm;
+ u16 rtc_wrote_regs;
+};
+
+/* Bit values for the ISTAT / ICTL registers */
+#define RTC_ISTAT_WRITE_COMPLETE 0x8000
+#define RTC_ISTAT_WRITE_PENDING 0x4000
+#define RTC_ISTAT_ALARM_DAY 0x0040
+#define RTC_ISTAT_24HR 0x0020
+#define RTC_ISTAT_HOUR 0x0010
+#define RTC_ISTAT_MIN 0x0008
+#define RTC_ISTAT_SEC 0x0004
+#define RTC_ISTAT_ALARM 0x0002
+#define RTC_ISTAT_STOPWATCH 0x0001
+
+/* Shift values for RTC_STAT register */
+#define DAY_BITS_OFF 17
+#define HOUR_BITS_OFF 12
+#define MIN_BITS_OFF 6
+#define SEC_BITS_OFF 0
+
+/* Some helper functions to convert between the common RTC notion of time
+ * and the internal Blackfin notion that is encoded in 32bits.
+ */
+static inline u32 rtc_time_to_bfin(unsigned long now)
+{
+ u32 sec = (now % 60);
+ u32 min = (now % (60 * 60)) / 60;
+ u32 hour = (now % (60 * 60 * 24)) / (60 * 60);
+ u32 days = (now / (60 * 60 * 24));
+ return (sec << SEC_BITS_OFF) +
+ (min << MIN_BITS_OFF) +
+ (hour << HOUR_BITS_OFF) +
+ (days << DAY_BITS_OFF);
+}
+static inline unsigned long rtc_bfin_to_time(u32 rtc_bfin)
+{
+ return (((rtc_bfin >> SEC_BITS_OFF) & 0x003F)) +
+ (((rtc_bfin >> MIN_BITS_OFF) & 0x003F) * 60) +
+ (((rtc_bfin >> HOUR_BITS_OFF) & 0x001F) * 60 * 60) +
+ (((rtc_bfin >> DAY_BITS_OFF) & 0x7FFF) * 60 * 60 * 24);
+}
+static inline void rtc_bfin_to_tm(u32 rtc_bfin, struct rtc_time *tm)
+{
+ rtc_time_to_tm(rtc_bfin_to_time(rtc_bfin), tm);
+}
+
+/**
+ * bfin_rtc_sync_pending - make sure pending writes have complete
+ *
+ * Wait for the previous write to a RTC register to complete.
+ * Unfortunately, we can't sleep here as that introduces a race condition when
+ * turning on interrupt events. Consider this:
+ * - process sets alarm
+ * - process enables alarm
+ * - process sleeps while waiting for rtc write to sync
+ * - interrupt fires while process is sleeping
+ * - interrupt acks the event by writing to ISTAT
+ * - interrupt sets the WRITE PENDING bit
+ * - interrupt handler finishes
+ * - process wakes up, sees WRITE PENDING bit set, goes to sleep
+ * - interrupt fires while process is sleeping
+ * If anyone can point out the obvious solution here, i'm listening :). This
+ * shouldn't be an issue on an SMP or preempt system as this function should
+ * only be called with the rtc lock held.
+ *
+ * Other options:
+ * - disable PREN so the sync happens at 32.768kHZ ... but this changes the
+ * inc rate for all RTC registers from 1HZ to 32.768kHZ ...
+ * - use the write complete IRQ
+ */
+/*
+static void bfin_rtc_sync_pending_polled(void)
+{
+ while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_COMPLETE))
+ if (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING))
+ break;
+ bfin_write_RTC_ISTAT(RTC_ISTAT_WRITE_COMPLETE);
+}
+*/
+static DECLARE_COMPLETION(bfin_write_complete);
+static void bfin_rtc_sync_pending(struct device *dev)
+{
+ dev_dbg_stamp(dev);
+ while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)
+ wait_for_completion_timeout(&bfin_write_complete, HZ * 5);
+ dev_dbg_stamp(dev);
+}
+
+/**
+ * bfin_rtc_reset - set RTC to sane/known state
+ *
+ * Initialize the RTC. Enable pre-scaler to scale RTC clock
+ * to 1Hz and clear interrupt/status registers.
+ */
+static void bfin_rtc_reset(struct device *dev, u16 rtc_ictl)
+{
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+ dev_dbg_stamp(dev);
+ bfin_rtc_sync_pending(dev);
+ bfin_write_RTC_PREN(0x1);
+ bfin_write_RTC_ICTL(rtc_ictl);
+ bfin_write_RTC_ALARM(0);
+ bfin_write_RTC_ISTAT(0xFFFF);
+ rtc->rtc_wrote_regs = 0;
+}
+
+/**
+ * bfin_rtc_interrupt - handle interrupt from RTC
+ *
+ * Since we handle all RTC events here, we have to make sure the requested
+ * interrupt is enabled (in RTC_ICTL) as the event status register (RTC_ISTAT)
+ * always gets updated regardless of the interrupt being enabled. So when one
+ * even we care about (e.g. stopwatch) goes off, we don't want to turn around
+ * and say that other events have happened as well (e.g. second). We do not
+ * have to worry about pending writes to the RTC_ICTL register as interrupts
+ * only fire if they are enabled in the RTC_ICTL register.
+ */
+static irqreturn_t bfin_rtc_interrupt(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long events = 0;
+ bool write_complete = false;
+ u16 rtc_istat, rtc_istat_clear, rtc_ictl, bits;
+
+ dev_dbg_stamp(dev);
+
+ rtc_istat = bfin_read_RTC_ISTAT();
+ rtc_ictl = bfin_read_RTC_ICTL();
+ rtc_istat_clear = 0;
+
+ bits = RTC_ISTAT_WRITE_COMPLETE;
+ if (rtc_istat & bits) {
+ rtc_istat_clear |= bits;
+ write_complete = true;
+ complete(&bfin_write_complete);
+ }
+
+ bits = (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY);
+ if (rtc_ictl & bits) {
+ if (rtc_istat & bits) {
+ rtc_istat_clear |= bits;
+ events |= RTC_AF | RTC_IRQF;
+ }
+ }
+
+ bits = RTC_ISTAT_SEC;
+ if (rtc_ictl & bits) {
+ if (rtc_istat & bits) {
+ rtc_istat_clear |= bits;
+ events |= RTC_UF | RTC_IRQF;
+ }
+ }
+
+ if (events)
+ rtc_update_irq(rtc->rtc_dev, 1, events);
+
+ if (write_complete || events) {
+ bfin_write_RTC_ISTAT(rtc_istat_clear);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+}
+
+static void bfin_rtc_int_set(u16 rtc_int)
+{
+ bfin_write_RTC_ISTAT(rtc_int);
+ bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() | rtc_int);
+}
+static void bfin_rtc_int_clear(u16 rtc_int)
+{
+ bfin_write_RTC_ICTL(bfin_read_RTC_ICTL() & rtc_int);
+}
+static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc)
+{
+ /* Blackfin has different bits for whether the alarm is
+ * more than 24 hours away.
+ */
+ bfin_rtc_int_set(rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY);
+}
+
+static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+
+ dev_dbg_stamp(dev);
+ if (enabled)
+ bfin_rtc_int_set_alarm(rtc);
+ else
+ bfin_rtc_int_clear(~(RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
+
+ return 0;
+}
+
+static int bfin_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+
+ dev_dbg_stamp(dev);
+
+ if (rtc->rtc_wrote_regs & 0x1)
+ bfin_rtc_sync_pending(dev);
+
+ rtc_bfin_to_tm(bfin_read_RTC_STAT(), tm);
+
+ return 0;
+}
+
+static int bfin_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+ unsigned long now;
+
+ dev_dbg_stamp(dev);
+
+ ret = rtc_tm_to_time(tm, &now);
+ if (ret == 0) {
+ if (rtc->rtc_wrote_regs & 0x1)
+ bfin_rtc_sync_pending(dev);
+ bfin_write_RTC_STAT(rtc_time_to_bfin(now));
+ rtc->rtc_wrote_regs = 0x1;
+ }
+
+ return ret;
+}
+
+static int bfin_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+ dev_dbg_stamp(dev);
+ alrm->time = rtc->rtc_alarm;
+ bfin_rtc_sync_pending(dev);
+ alrm->enabled = !!(bfin_read_RTC_ICTL() & (RTC_ISTAT_ALARM | RTC_ISTAT_ALARM_DAY));
+ return 0;
+}
+
+static int bfin_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct bfin_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long rtc_alarm;
+
+ dev_dbg_stamp(dev);
+
+ if (rtc_tm_to_time(&alrm->time, &rtc_alarm))
+ return -EINVAL;
+
+ rtc->rtc_alarm = alrm->time;
+
+ bfin_rtc_sync_pending(dev);
+ bfin_write_RTC_ALARM(rtc_time_to_bfin(rtc_alarm));
+ if (alrm->enabled)
+ bfin_rtc_int_set_alarm(rtc);
+
+ return 0;
+}
+
+static int bfin_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+#define yesno(x) ((x) ? "yes" : "no")
+ u16 ictl = bfin_read_RTC_ICTL();
+ dev_dbg_stamp(dev);
+ seq_printf(seq,
+ "alarm_IRQ\t: %s\n"
+ "wkalarm_IRQ\t: %s\n"
+ "seconds_IRQ\t: %s\n",
+ yesno(ictl & RTC_ISTAT_ALARM),
+ yesno(ictl & RTC_ISTAT_ALARM_DAY),
+ yesno(ictl & RTC_ISTAT_SEC));
+ return 0;
+#undef yesno
+}
+
+static struct rtc_class_ops bfin_rtc_ops = {
+ .read_time = bfin_rtc_read_time,
+ .set_time = bfin_rtc_set_time,
+ .read_alarm = bfin_rtc_read_alarm,
+ .set_alarm = bfin_rtc_set_alarm,
+ .proc = bfin_rtc_proc,
+ .alarm_irq_enable = bfin_rtc_alarm_irq_enable,
+};
+
+static int bfin_rtc_probe(struct platform_device *pdev)
+{
+ struct bfin_rtc *rtc;
+ struct device *dev = &pdev->dev;
+ int ret;
+ unsigned long timeout = jiffies + HZ;
+
+ dev_dbg_stamp(dev);
+
+ /* Allocate memory for our RTC struct */
+ rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc))
+ return -ENOMEM;
+ platform_set_drvdata(pdev, rtc);
+ device_init_wakeup(dev, 1);
+
+ /* Register our RTC with the RTC framework */
+ rtc->rtc_dev = devm_rtc_device_register(dev, pdev->name, &bfin_rtc_ops,
+ THIS_MODULE);
+ if (unlikely(IS_ERR(rtc->rtc_dev)))
+ return PTR_ERR(rtc->rtc_dev);
+
+ /* Grab the IRQ and init the hardware */
+ ret = devm_request_irq(dev, IRQ_RTC, bfin_rtc_interrupt, 0,
+ pdev->name, dev);
+ if (unlikely(ret))
+ dev_err(&pdev->dev,
+ "unable to request IRQ; alarm won't work, "
+ "and writes will be delayed\n");
+
+ /* sometimes the bootloader touched things, but the write complete was not
+ * enabled, so let's just do a quick timeout here since the IRQ will not fire ...
+ */
+ while (bfin_read_RTC_ISTAT() & RTC_ISTAT_WRITE_PENDING)
+ if (time_after(jiffies, timeout))
+ break;
+ bfin_rtc_reset(dev, RTC_ISTAT_WRITE_COMPLETE);
+ bfin_write_RTC_SWCNT(0);
+
+ return 0;
+}
+
+static int bfin_rtc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ bfin_rtc_reset(dev, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int bfin_rtc_suspend(struct device *dev)
+{
+ dev_dbg_stamp(dev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(IRQ_RTC);
+ bfin_rtc_sync_pending(dev);
+ } else
+ bfin_rtc_int_clear(0);
+
+ return 0;
+}
+
+static int bfin_rtc_resume(struct device *dev)
+{
+ dev_dbg_stamp(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(IRQ_RTC);
+
+ /*
+ * Since only some of the RTC bits are maintained externally in the
+ * Vbat domain, we need to wait for the RTC MMRs to be synced into
+ * the core after waking up. This happens every RTC 1HZ. Once that
+ * has happened, we can go ahead and re-enable the important write
+ * complete interrupt event.
+ */
+ while (!(bfin_read_RTC_ISTAT() & RTC_ISTAT_SEC))
+ continue;
+ bfin_rtc_int_set(RTC_ISTAT_WRITE_COMPLETE);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(bfin_rtc_pm_ops, bfin_rtc_suspend, bfin_rtc_resume);
+
+static struct platform_driver bfin_rtc_driver = {
+ .driver = {
+ .name = "rtc-bfin",
+ .pm = &bfin_rtc_pm_ops,
+ },
+ .probe = bfin_rtc_probe,
+ .remove = bfin_rtc_remove,
+};
+
+module_platform_driver(bfin_rtc_driver);
+
+MODULE_DESCRIPTION("Blackfin On-Chip Real Time Clock Driver");
+MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-bfin");
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
new file mode 100644
index 000000000..92679df6d
--- /dev/null
+++ b/drivers/rtc/rtc-bq32k.c
@@ -0,0 +1,247 @@
+/*
+ * Driver for TI BQ32000 RTC.
+ *
+ * Copyright (C) 2009 Semihalf.
+ * Copyright (C) 2014 Pavel Machek <pavel@denx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You can get hardware description at
+ * http://www.ti.com/lit/ds/symlink/bq32000.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/bcd.h>
+
+#define BQ32K_SECONDS 0x00 /* Seconds register address */
+#define BQ32K_SECONDS_MASK 0x7F /* Mask over seconds value */
+#define BQ32K_STOP 0x80 /* Oscillator Stop flat */
+
+#define BQ32K_MINUTES 0x01 /* Minutes register address */
+#define BQ32K_MINUTES_MASK 0x7F /* Mask over minutes value */
+#define BQ32K_OF 0x80 /* Oscillator Failure flag */
+
+#define BQ32K_HOURS_MASK 0x3F /* Mask over hours value */
+#define BQ32K_CENT 0x40 /* Century flag */
+#define BQ32K_CENT_EN 0x80 /* Century flag enable bit */
+
+#define BQ32K_CALIBRATION 0x07 /* CAL_CFG1, calibration and control */
+#define BQ32K_TCH2 0x08 /* Trickle charge enable */
+#define BQ32K_CFG2 0x09 /* Trickle charger control */
+
+struct bq32k_regs {
+ uint8_t seconds;
+ uint8_t minutes;
+ uint8_t cent_hours;
+ uint8_t day;
+ uint8_t date;
+ uint8_t month;
+ uint8_t years;
+};
+
+static struct i2c_driver bq32k_driver;
+
+static int bq32k_read(struct device *dev, void *data, uint8_t off, uint8_t len)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &off,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = data,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msgs, 2) == 2)
+ return 0;
+
+ return -EIO;
+}
+
+static int bq32k_write(struct device *dev, void *data, uint8_t off, uint8_t len)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ uint8_t buffer[len + 1];
+
+ buffer[0] = off;
+ memcpy(&buffer[1], data, len);
+
+ if (i2c_master_send(client, buffer, len + 1) == len + 1)
+ return 0;
+
+ return -EIO;
+}
+
+static int bq32k_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct bq32k_regs regs;
+ int error;
+
+ error = bq32k_read(dev, &regs, 0, sizeof(regs));
+ if (error)
+ return error;
+
+ tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK);
+ tm->tm_min = bcd2bin(regs.minutes & BQ32K_SECONDS_MASK);
+ tm->tm_hour = bcd2bin(regs.cent_hours & BQ32K_HOURS_MASK);
+ tm->tm_mday = bcd2bin(regs.date);
+ tm->tm_wday = bcd2bin(regs.day) - 1;
+ tm->tm_mon = bcd2bin(regs.month) - 1;
+ tm->tm_year = bcd2bin(regs.years) +
+ ((regs.cent_hours & BQ32K_CENT) ? 100 : 0);
+
+ return rtc_valid_tm(tm);
+}
+
+static int bq32k_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct bq32k_regs regs;
+
+ regs.seconds = bin2bcd(tm->tm_sec);
+ regs.minutes = bin2bcd(tm->tm_min);
+ regs.cent_hours = bin2bcd(tm->tm_hour) | BQ32K_CENT_EN;
+ regs.day = bin2bcd(tm->tm_wday + 1);
+ regs.date = bin2bcd(tm->tm_mday);
+ regs.month = bin2bcd(tm->tm_mon + 1);
+
+ if (tm->tm_year >= 100) {
+ regs.cent_hours |= BQ32K_CENT;
+ regs.years = bin2bcd(tm->tm_year - 100);
+ } else
+ regs.years = bin2bcd(tm->tm_year);
+
+ return bq32k_write(dev, &regs, 0, sizeof(regs));
+}
+
+static const struct rtc_class_ops bq32k_rtc_ops = {
+ .read_time = bq32k_rtc_read_time,
+ .set_time = bq32k_rtc_set_time,
+};
+
+static int trickle_charger_of_init(struct device *dev, struct device_node *node)
+{
+ unsigned char reg;
+ int error;
+ u32 ohms = 0;
+
+ if (of_property_read_u32(node, "trickle-resistor-ohms" , &ohms))
+ return 0;
+
+ switch (ohms) {
+ case 180+940:
+ /*
+ * TCHE[3:0] == 0x05, TCH2 == 1, TCFE == 0 (charging
+ * over diode and 940ohm resistor)
+ */
+
+ if (of_property_read_bool(node, "trickle-diode-disable")) {
+ dev_err(dev, "diode and resistor mismatch\n");
+ return -EINVAL;
+ }
+ reg = 0x05;
+ break;
+
+ case 180+20000:
+ /* diode disabled */
+
+ if (!of_property_read_bool(node, "trickle-diode-disable")) {
+ dev_err(dev, "bq32k: diode and resistor mismatch\n");
+ return -EINVAL;
+ }
+ reg = 0x45;
+ break;
+
+ default:
+ dev_err(dev, "invalid resistor value (%d)\n", ohms);
+ return -EINVAL;
+ }
+
+ error = bq32k_write(dev, &reg, BQ32K_CFG2, 1);
+ if (error)
+ return error;
+
+ reg = 0x20;
+ error = bq32k_write(dev, &reg, BQ32K_TCH2, 1);
+ if (error)
+ return error;
+
+ dev_info(dev, "Enabled trickle RTC battery charge.\n");
+ return 0;
+}
+
+static int bq32k_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct rtc_device *rtc;
+ uint8_t reg;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ /* Check Oscillator Stop flag */
+ error = bq32k_read(dev, &reg, BQ32K_SECONDS, 1);
+ if (!error && (reg & BQ32K_STOP)) {
+ dev_warn(dev, "Oscillator was halted. Restarting...\n");
+ reg &= ~BQ32K_STOP;
+ error = bq32k_write(dev, &reg, BQ32K_SECONDS, 1);
+ }
+ if (error)
+ return error;
+
+ /* Check Oscillator Failure flag */
+ error = bq32k_read(dev, &reg, BQ32K_MINUTES, 1);
+ if (!error && (reg & BQ32K_OF)) {
+ dev_warn(dev, "Oscillator Failure. Check RTC battery.\n");
+ reg &= ~BQ32K_OF;
+ error = bq32k_write(dev, &reg, BQ32K_MINUTES, 1);
+ }
+ if (error)
+ return error;
+
+ if (client && client->dev.of_node)
+ trickle_charger_of_init(dev, client->dev.of_node);
+
+ rtc = devm_rtc_device_register(&client->dev, bq32k_driver.driver.name,
+ &bq32k_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static const struct i2c_device_id bq32k_id[] = {
+ { "bq32000", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bq32k_id);
+
+static struct i2c_driver bq32k_driver = {
+ .driver = {
+ .name = "bq32k",
+ .owner = THIS_MODULE,
+ },
+ .probe = bq32k_probe,
+ .id_table = bq32k_id,
+};
+
+module_i2c_driver(bq32k_driver);
+
+MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>");
+MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-bq4802.c b/drivers/rtc/rtc-bq4802.c
new file mode 100644
index 000000000..bd170cb33
--- /dev/null
+++ b/drivers/rtc/rtc-bq4802.c
@@ -0,0 +1,199 @@
+/* rtc-bq4802.c: TI BQ4802 RTC driver.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("TI BQ4802 RTC driver");
+MODULE_LICENSE("GPL");
+
+struct bq4802 {
+ void __iomem *regs;
+ unsigned long ioport;
+ struct rtc_device *rtc;
+ spinlock_t lock;
+ struct resource *r;
+ u8 (*read)(struct bq4802 *, int);
+ void (*write)(struct bq4802 *, int, u8);
+};
+
+static u8 bq4802_read_io(struct bq4802 *p, int off)
+{
+ return inb(p->ioport + off);
+}
+
+static void bq4802_write_io(struct bq4802 *p, int off, u8 val)
+{
+ outb(val, p->ioport + off);
+}
+
+static u8 bq4802_read_mem(struct bq4802 *p, int off)
+{
+ return readb(p->regs + off);
+}
+
+static void bq4802_write_mem(struct bq4802 *p, int off, u8 val)
+{
+ writeb(val, p->regs + off);
+}
+
+static int bq4802_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bq4802 *p = platform_get_drvdata(pdev);
+ unsigned long flags;
+ unsigned int century;
+ u8 val;
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ val = p->read(p, 0x0e);
+ p->write(p, 0xe, val | 0x08);
+
+ tm->tm_sec = p->read(p, 0x00);
+ tm->tm_min = p->read(p, 0x02);
+ tm->tm_hour = p->read(p, 0x04);
+ tm->tm_mday = p->read(p, 0x06);
+ tm->tm_mon = p->read(p, 0x09);
+ tm->tm_year = p->read(p, 0x0a);
+ tm->tm_wday = p->read(p, 0x08);
+ century = p->read(p, 0x0f);
+
+ p->write(p, 0x0e, val);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ tm->tm_sec = bcd2bin(tm->tm_sec);
+ tm->tm_min = bcd2bin(tm->tm_min);
+ tm->tm_hour = bcd2bin(tm->tm_hour);
+ tm->tm_mday = bcd2bin(tm->tm_mday);
+ tm->tm_mon = bcd2bin(tm->tm_mon);
+ tm->tm_year = bcd2bin(tm->tm_year);
+ tm->tm_wday = bcd2bin(tm->tm_wday);
+ century = bcd2bin(century);
+
+ tm->tm_year += (century * 100);
+ tm->tm_year -= 1900;
+
+ tm->tm_mon--;
+
+ return 0;
+}
+
+static int bq4802_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bq4802 *p = platform_get_drvdata(pdev);
+ u8 sec, min, hrs, day, mon, yrs, century, val;
+ unsigned long flags;
+ unsigned int year;
+
+ year = tm->tm_year + 1900;
+ century = year / 100;
+ yrs = year % 100;
+
+ mon = tm->tm_mon + 1; /* tm_mon starts at zero */
+ day = tm->tm_mday;
+ hrs = tm->tm_hour;
+ min = tm->tm_min;
+ sec = tm->tm_sec;
+
+ sec = bin2bcd(sec);
+ min = bin2bcd(min);
+ hrs = bin2bcd(hrs);
+ day = bin2bcd(day);
+ mon = bin2bcd(mon);
+ yrs = bin2bcd(yrs);
+ century = bin2bcd(century);
+
+ spin_lock_irqsave(&p->lock, flags);
+
+ val = p->read(p, 0x0e);
+ p->write(p, 0x0e, val | 0x08);
+
+ p->write(p, 0x00, sec);
+ p->write(p, 0x02, min);
+ p->write(p, 0x04, hrs);
+ p->write(p, 0x06, day);
+ p->write(p, 0x09, mon);
+ p->write(p, 0x0a, yrs);
+ p->write(p, 0x0f, century);
+
+ p->write(p, 0x0e, val);
+
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ return 0;
+}
+
+static const struct rtc_class_ops bq4802_ops = {
+ .read_time = bq4802_read_time,
+ .set_time = bq4802_set_time,
+};
+
+static int bq4802_probe(struct platform_device *pdev)
+{
+ struct bq4802 *p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
+ int err = -ENOMEM;
+
+ if (!p)
+ goto out;
+
+ spin_lock_init(&p->lock);
+
+ p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!p->r) {
+ p->r = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ err = -EINVAL;
+ if (!p->r)
+ goto out;
+ }
+ if (p->r->flags & IORESOURCE_IO) {
+ p->ioport = p->r->start;
+ p->read = bq4802_read_io;
+ p->write = bq4802_write_io;
+ } else if (p->r->flags & IORESOURCE_MEM) {
+ p->regs = devm_ioremap(&pdev->dev, p->r->start,
+ resource_size(p->r));
+ p->read = bq4802_read_mem;
+ p->write = bq4802_write_mem;
+ } else {
+ err = -EINVAL;
+ goto out;
+ }
+
+ platform_set_drvdata(pdev, p);
+
+ p->rtc = devm_rtc_device_register(&pdev->dev, "bq4802",
+ &bq4802_ops, THIS_MODULE);
+ if (IS_ERR(p->rtc)) {
+ err = PTR_ERR(p->rtc);
+ goto out;
+ }
+
+ err = 0;
+out:
+ return err;
+
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc-bq4802");
+
+static struct platform_driver bq4802_driver = {
+ .driver = {
+ .name = "rtc-bq4802",
+ },
+ .probe = bq4802_probe,
+};
+
+module_platform_driver(bq4802_driver);
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
new file mode 100644
index 000000000..a82556a07
--- /dev/null
+++ b/drivers/rtc/rtc-cmos.c
@@ -0,0 +1,1273 @@
+/*
+ * RTC class driver for "CMOS RTC": PCs, ACPI, etc
+ *
+ * Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c)
+ * Copyright (C) 2006 David Brownell (convert to new framework)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * The original "cmos clock" chip was an MC146818 chip, now obsolete.
+ * That defined the register interface now provided by all PCs, some
+ * non-PC systems, and incorporated into ACPI. Modern PC chipsets
+ * integrate an MC146818 clone in their southbridge, and boards use
+ * that instead of discrete clones like the DS12887 or M48T86. There
+ * are also clones that connect using the LPC bus.
+ *
+ * That register API is also used directly by various other drivers
+ * (notably for integrated NVRAM), infrastructure (x86 has code to
+ * bypass the RTC framework, directly reading the RTC during boot
+ * and updating minutes/seconds for systems using NTP synch) and
+ * utilities (like userspace 'hwclock', if no /dev node exists).
+ *
+ * So **ALL** calls to CMOS_READ and CMOS_WRITE must be done with
+ * interrupts disabled, holding the global rtc_lock, to exclude those
+ * other drivers and utilities on correctly configured systems.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/log2.h>
+#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/dmi.h>
+
+/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
+#include <asm-generic/rtc.h>
+
+struct cmos_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ int irq;
+ struct resource *iomem;
+
+ void (*wake_on)(struct device *);
+ void (*wake_off)(struct device *);
+
+ u8 enabled_wake;
+ u8 suspend_ctrl;
+
+ /* newer hardware extends the original register set */
+ u8 day_alrm;
+ u8 mon_alrm;
+ u8 century;
+};
+
+/* both platform and pnp busses use negative numbers for invalid irqs */
+#define is_valid_irq(n) ((n) > 0)
+
+static const char driver_name[] = "rtc_cmos";
+
+/* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear;
+ * always mask it against the irq enable bits in RTC_CONTROL. Bit values
+ * are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both.
+ */
+#define RTC_IRQMASK (RTC_PF | RTC_AF | RTC_UF)
+
+static inline int is_intr(u8 rtc_intr)
+{
+ if (!(rtc_intr & RTC_IRQF))
+ return 0;
+ return rtc_intr & RTC_IRQMASK;
+}
+
+/*----------------------------------------------------------------*/
+
+/* Much modern x86 hardware has HPETs (10+ MHz timers) which, because
+ * many BIOS programmers don't set up "sane mode" IRQ routing, are mostly
+ * used in a broken "legacy replacement" mode. The breakage includes
+ * HPET #1 hijacking the IRQ for this RTC, and being unavailable for
+ * other (better) use.
+ *
+ * When that broken mode is in use, platform glue provides a partial
+ * emulation of hardware RTC IRQ facilities using HPET #1. We don't
+ * want to use HPET for anything except those IRQs though...
+ */
+#ifdef CONFIG_HPET_EMULATE_RTC
+#include <asm/hpet.h>
+#else
+
+static inline int is_hpet_enabled(void)
+{
+ return 0;
+}
+
+static inline int hpet_mask_rtc_irq_bit(unsigned long mask)
+{
+ return 0;
+}
+
+static inline int hpet_set_rtc_irq_bit(unsigned long mask)
+{
+ return 0;
+}
+
+static inline int
+hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec)
+{
+ return 0;
+}
+
+static inline int hpet_set_periodic_freq(unsigned long freq)
+{
+ return 0;
+}
+
+static inline int hpet_rtc_dropped_irq(void)
+{
+ return 0;
+}
+
+static inline int hpet_rtc_timer_init(void)
+{
+ return 0;
+}
+
+extern irq_handler_t hpet_rtc_interrupt;
+
+static inline int hpet_register_irq_handler(irq_handler_t handler)
+{
+ return 0;
+}
+
+static inline int hpet_unregister_irq_handler(irq_handler_t handler)
+{
+ return 0;
+}
+
+#endif
+
+/*----------------------------------------------------------------*/
+
+#ifdef RTC_PORT
+
+/* Most newer x86 systems have two register banks, the first used
+ * for RTC and NVRAM and the second only for NVRAM. Caller must
+ * own rtc_lock ... and we won't worry about access during NMI.
+ */
+#define can_bank2 true
+
+static inline unsigned char cmos_read_bank2(unsigned char addr)
+{
+ outb(addr, RTC_PORT(2));
+ return inb(RTC_PORT(3));
+}
+
+static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
+{
+ outb(addr, RTC_PORT(2));
+ outb(val, RTC_PORT(3));
+}
+
+#else
+
+#define can_bank2 false
+
+static inline unsigned char cmos_read_bank2(unsigned char addr)
+{
+ return 0;
+}
+
+static inline void cmos_write_bank2(unsigned char val, unsigned char addr)
+{
+}
+
+#endif
+
+/*----------------------------------------------------------------*/
+
+static int cmos_read_time(struct device *dev, struct rtc_time *t)
+{
+ /* REVISIT: if the clock has a "century" register, use
+ * that instead of the heuristic in get_rtc_time().
+ * That'll make Y3K compatility (year > 2070) easy!
+ */
+ get_rtc_time(t);
+ return 0;
+}
+
+static int cmos_set_time(struct device *dev, struct rtc_time *t)
+{
+ /* REVISIT: set the "century" register if available
+ *
+ * NOTE: this ignores the issue whereby updating the seconds
+ * takes effect exactly 500ms after we write the register.
+ * (Also queueing and other delays before we get this far.)
+ */
+ return set_rtc_time(t);
+}
+
+static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned char rtc_control;
+
+ if (!is_valid_irq(cmos->irq))
+ return -EIO;
+
+ /* Basic alarms only support hour, minute, and seconds fields.
+ * Some also support day and month, for alarms up to a year in
+ * the future.
+ */
+ t->time.tm_mday = -1;
+ t->time.tm_mon = -1;
+
+ spin_lock_irq(&rtc_lock);
+ t->time.tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
+ t->time.tm_min = CMOS_READ(RTC_MINUTES_ALARM);
+ t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM);
+
+ if (cmos->day_alrm) {
+ /* ignore upper bits on readback per ACPI spec */
+ t->time.tm_mday = CMOS_READ(cmos->day_alrm) & 0x3f;
+ if (!t->time.tm_mday)
+ t->time.tm_mday = -1;
+
+ if (cmos->mon_alrm) {
+ t->time.tm_mon = CMOS_READ(cmos->mon_alrm);
+ if (!t->time.tm_mon)
+ t->time.tm_mon = -1;
+ }
+ }
+
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ spin_unlock_irq(&rtc_lock);
+
+ if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ if (((unsigned)t->time.tm_sec) < 0x60)
+ t->time.tm_sec = bcd2bin(t->time.tm_sec);
+ else
+ t->time.tm_sec = -1;
+ if (((unsigned)t->time.tm_min) < 0x60)
+ t->time.tm_min = bcd2bin(t->time.tm_min);
+ else
+ t->time.tm_min = -1;
+ if (((unsigned)t->time.tm_hour) < 0x24)
+ t->time.tm_hour = bcd2bin(t->time.tm_hour);
+ else
+ t->time.tm_hour = -1;
+
+ if (cmos->day_alrm) {
+ if (((unsigned)t->time.tm_mday) <= 0x31)
+ t->time.tm_mday = bcd2bin(t->time.tm_mday);
+ else
+ t->time.tm_mday = -1;
+
+ if (cmos->mon_alrm) {
+ if (((unsigned)t->time.tm_mon) <= 0x12)
+ t->time.tm_mon = bcd2bin(t->time.tm_mon)-1;
+ else
+ t->time.tm_mon = -1;
+ }
+ }
+ }
+ t->time.tm_year = -1;
+
+ t->enabled = !!(rtc_control & RTC_AIE);
+ t->pending = 0;
+
+ return 0;
+}
+
+static void cmos_checkintr(struct cmos_rtc *cmos, unsigned char rtc_control)
+{
+ unsigned char rtc_intr;
+
+ /* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
+ * allegedly some older rtcs need that to handle irqs properly
+ */
+ rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
+
+ if (is_hpet_enabled())
+ return;
+
+ rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+ if (is_intr(rtc_intr))
+ rtc_update_irq(cmos->rtc, 1, rtc_intr);
+}
+
+static void cmos_irq_enable(struct cmos_rtc *cmos, unsigned char mask)
+{
+ unsigned char rtc_control;
+
+ /* flush any pending IRQ status, notably for update irqs,
+ * before we enable new IRQs
+ */
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ cmos_checkintr(cmos, rtc_control);
+
+ rtc_control |= mask;
+ CMOS_WRITE(rtc_control, RTC_CONTROL);
+ hpet_set_rtc_irq_bit(mask);
+
+ cmos_checkintr(cmos, rtc_control);
+}
+
+static void cmos_irq_disable(struct cmos_rtc *cmos, unsigned char mask)
+{
+ unsigned char rtc_control;
+
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ rtc_control &= ~mask;
+ CMOS_WRITE(rtc_control, RTC_CONTROL);
+ hpet_mask_rtc_irq_bit(mask);
+
+ cmos_checkintr(cmos, rtc_control);
+}
+
+static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned char mon, mday, hrs, min, sec, rtc_control;
+
+ if (!is_valid_irq(cmos->irq))
+ return -EIO;
+
+ mon = t->time.tm_mon + 1;
+ mday = t->time.tm_mday;
+ hrs = t->time.tm_hour;
+ min = t->time.tm_min;
+ sec = t->time.tm_sec;
+
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ /* Writing 0xff means "don't care" or "match all". */
+ mon = (mon <= 12) ? bin2bcd(mon) : 0xff;
+ mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
+ hrs = (hrs < 24) ? bin2bcd(hrs) : 0xff;
+ min = (min < 60) ? bin2bcd(min) : 0xff;
+ sec = (sec < 60) ? bin2bcd(sec) : 0xff;
+ }
+
+ spin_lock_irq(&rtc_lock);
+
+ /* next rtc irq must not be from previous alarm setting */
+ cmos_irq_disable(cmos, RTC_AIE);
+
+ /* update alarm */
+ CMOS_WRITE(hrs, RTC_HOURS_ALARM);
+ CMOS_WRITE(min, RTC_MINUTES_ALARM);
+ CMOS_WRITE(sec, RTC_SECONDS_ALARM);
+
+ /* the system may support an "enhanced" alarm */
+ if (cmos->day_alrm) {
+ CMOS_WRITE(mday, cmos->day_alrm);
+ if (cmos->mon_alrm)
+ CMOS_WRITE(mon, cmos->mon_alrm);
+ }
+
+ /* FIXME the HPET alarm glue currently ignores day_alrm
+ * and mon_alrm ...
+ */
+ hpet_set_alarm_time(t->time.tm_hour, t->time.tm_min, t->time.tm_sec);
+
+ if (t->enabled)
+ cmos_irq_enable(cmos, RTC_AIE);
+
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+}
+
+/*
+ * Do not disable RTC alarm on shutdown - workaround for b0rked BIOSes.
+ */
+static bool alarm_disable_quirk;
+
+static int __init set_alarm_disable_quirk(const struct dmi_system_id *id)
+{
+ alarm_disable_quirk = true;
+ pr_info("BIOS has alarm-disable quirk - RTC alarms disabled\n");
+ return 0;
+}
+
+static const struct dmi_system_id rtc_quirks[] __initconst = {
+ /* https://bugzilla.novell.com/show_bug.cgi?id=805740 */
+ {
+ .callback = set_alarm_disable_quirk,
+ .ident = "IBM Truman",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "4852570"),
+ },
+ },
+ /* https://bugzilla.novell.com/show_bug.cgi?id=812592 */
+ {
+ .callback = set_alarm_disable_quirk,
+ .ident = "Gigabyte GA-990XA-UD3",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "Gigabyte Technology Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "GA-990XA-UD3"),
+ },
+ },
+ /* http://permalink.gmane.org/gmane.linux.kernel/1604474 */
+ {
+ .callback = set_alarm_disable_quirk,
+ .ident = "Toshiba Satellite L300",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite L300"),
+ },
+ },
+ {}
+};
+
+static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ if (!is_valid_irq(cmos->irq))
+ return -EINVAL;
+
+ if (alarm_disable_quirk)
+ return 0;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+
+ if (enabled)
+ cmos_irq_enable(cmos, RTC_AIE);
+ else
+ cmos_irq_disable(cmos, RTC_AIE);
+
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ return 0;
+}
+
+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
+
+static int cmos_procfs(struct device *dev, struct seq_file *seq)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned char rtc_control, valid;
+
+ spin_lock_irq(&rtc_lock);
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ valid = CMOS_READ(RTC_VALID);
+ spin_unlock_irq(&rtc_lock);
+
+ /* NOTE: at least ICH6 reports battery status using a different
+ * (non-RTC) bit; and SQWE is ignored on many current systems.
+ */
+ seq_printf(seq,
+ "periodic_IRQ\t: %s\n"
+ "update_IRQ\t: %s\n"
+ "HPET_emulated\t: %s\n"
+ // "square_wave\t: %s\n"
+ "BCD\t\t: %s\n"
+ "DST_enable\t: %s\n"
+ "periodic_freq\t: %d\n"
+ "batt_status\t: %s\n",
+ (rtc_control & RTC_PIE) ? "yes" : "no",
+ (rtc_control & RTC_UIE) ? "yes" : "no",
+ is_hpet_enabled() ? "yes" : "no",
+ // (rtc_control & RTC_SQWE) ? "yes" : "no",
+ (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
+ (rtc_control & RTC_DST_EN) ? "yes" : "no",
+ cmos->rtc->irq_freq,
+ (valid & RTC_VRT) ? "okay" : "dead");
+
+ return 0;
+}
+
+#else
+#define cmos_procfs NULL
+#endif
+
+static const struct rtc_class_ops cmos_rtc_ops = {
+ .read_time = cmos_read_time,
+ .set_time = cmos_set_time,
+ .read_alarm = cmos_read_alarm,
+ .set_alarm = cmos_set_alarm,
+ .proc = cmos_procfs,
+ .alarm_irq_enable = cmos_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------*/
+
+/*
+ * All these chips have at least 64 bytes of address space, shared by
+ * RTC registers and NVRAM. Most of those bytes of NVRAM are used
+ * by boot firmware. Modern chips have 128 or 256 bytes.
+ */
+
+#define NVRAM_OFFSET (RTC_REG_D + 1)
+
+static ssize_t
+cmos_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int retval;
+
+ if (unlikely(off >= attr->size))
+ return 0;
+ if (unlikely(off < 0))
+ return -EINVAL;
+ if ((off + count) > attr->size)
+ count = attr->size - off;
+
+ off += NVRAM_OFFSET;
+ spin_lock_irq(&rtc_lock);
+ for (retval = 0; count; count--, off++, retval++) {
+ if (off < 128)
+ *buf++ = CMOS_READ(off);
+ else if (can_bank2)
+ *buf++ = cmos_read_bank2(off);
+ else
+ break;
+ }
+ spin_unlock_irq(&rtc_lock);
+
+ return retval;
+}
+
+static ssize_t
+cmos_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct cmos_rtc *cmos;
+ int retval;
+
+ cmos = dev_get_drvdata(container_of(kobj, struct device, kobj));
+ if (unlikely(off >= attr->size))
+ return -EFBIG;
+ if (unlikely(off < 0))
+ return -EINVAL;
+ if ((off + count) > attr->size)
+ count = attr->size - off;
+
+ /* NOTE: on at least PCs and Ataris, the boot firmware uses a
+ * checksum on part of the NVRAM data. That's currently ignored
+ * here. If userspace is smart enough to know what fields of
+ * NVRAM to update, updating checksums is also part of its job.
+ */
+ off += NVRAM_OFFSET;
+ spin_lock_irq(&rtc_lock);
+ for (retval = 0; count; count--, off++, retval++) {
+ /* don't trash RTC registers */
+ if (off == cmos->day_alrm
+ || off == cmos->mon_alrm
+ || off == cmos->century)
+ buf++;
+ else if (off < 128)
+ CMOS_WRITE(*buf++, off);
+ else if (can_bank2)
+ cmos_write_bank2(*buf++, off);
+ else
+ break;
+ }
+ spin_unlock_irq(&rtc_lock);
+
+ return retval;
+}
+
+static struct bin_attribute nvram = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+
+ .read = cmos_nvram_read,
+ .write = cmos_nvram_write,
+ /* size gets set up later */
+};
+
+/*----------------------------------------------------------------*/
+
+static struct cmos_rtc cmos_rtc;
+
+static irqreturn_t cmos_interrupt(int irq, void *p)
+{
+ u8 irqstat;
+ u8 rtc_control;
+
+ spin_lock(&rtc_lock);
+
+ /* When the HPET interrupt handler calls us, the interrupt
+ * status is passed as arg1 instead of the irq number. But
+ * always clear irq status, even when HPET is in the way.
+ *
+ * Note that HPET and RTC are almost certainly out of phase,
+ * giving different IRQ status ...
+ */
+ irqstat = CMOS_READ(RTC_INTR_FLAGS);
+ rtc_control = CMOS_READ(RTC_CONTROL);
+ if (is_hpet_enabled())
+ irqstat = (unsigned long)irq & 0xF0;
+
+ /* If we were suspended, RTC_CONTROL may not be accurate since the
+ * bios may have cleared it.
+ */
+ if (!cmos_rtc.suspend_ctrl)
+ irqstat &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+ else
+ irqstat &= (cmos_rtc.suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
+
+ /* All Linux RTC alarms should be treated as if they were oneshot.
+ * Similar code may be needed in system wakeup paths, in case the
+ * alarm woke the system.
+ */
+ if (irqstat & RTC_AIE) {
+ cmos_rtc.suspend_ctrl &= ~RTC_AIE;
+ rtc_control &= ~RTC_AIE;
+ CMOS_WRITE(rtc_control, RTC_CONTROL);
+ hpet_mask_rtc_irq_bit(RTC_AIE);
+ CMOS_READ(RTC_INTR_FLAGS);
+ }
+ spin_unlock(&rtc_lock);
+
+ if (is_intr(irqstat)) {
+ rtc_update_irq(p, 1, irqstat);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+}
+
+#ifdef CONFIG_PNP
+#define INITSECTION
+
+#else
+#define INITSECTION __init
+#endif
+
+static int INITSECTION
+cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
+{
+ struct cmos_rtc_board_info *info = dev_get_platdata(dev);
+ int retval = 0;
+ unsigned char rtc_control;
+ unsigned address_space;
+ u32 flags = 0;
+
+ /* there can be only one ... */
+ if (cmos_rtc.dev)
+ return -EBUSY;
+
+ if (!ports)
+ return -ENODEV;
+
+ /* Claim I/O ports ASAP, minimizing conflict with legacy driver.
+ *
+ * REVISIT non-x86 systems may instead use memory space resources
+ * (needing ioremap etc), not i/o space resources like this ...
+ */
+ if (RTC_IOMAPPED)
+ ports = request_region(ports->start, resource_size(ports),
+ driver_name);
+ else
+ ports = request_mem_region(ports->start, resource_size(ports),
+ driver_name);
+ if (!ports) {
+ dev_dbg(dev, "i/o registers already in use\n");
+ return -EBUSY;
+ }
+
+ cmos_rtc.irq = rtc_irq;
+ cmos_rtc.iomem = ports;
+
+ /* Heuristic to deduce NVRAM size ... do what the legacy NVRAM
+ * driver did, but don't reject unknown configs. Old hardware
+ * won't address 128 bytes. Newer chips have multiple banks,
+ * though they may not be listed in one I/O resource.
+ */
+#if defined(CONFIG_ATARI)
+ address_space = 64;
+#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) \
+ || defined(__sparc__) || defined(__mips__) \
+ || defined(__powerpc__)
+ address_space = 128;
+#else
+#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
+ address_space = 128;
+#endif
+ if (can_bank2 && ports->end > (ports->start + 1))
+ address_space = 256;
+
+ /* For ACPI systems extension info comes from the FADT. On others,
+ * board specific setup provides it as appropriate. Systems where
+ * the alarm IRQ isn't automatically a wakeup IRQ (like ACPI, and
+ * some almost-clones) can provide hooks to make that behave.
+ *
+ * Note that ACPI doesn't preclude putting these registers into
+ * "extended" areas of the chip, including some that we won't yet
+ * expect CMOS_READ and friends to handle.
+ */
+ if (info) {
+ if (info->flags)
+ flags = info->flags;
+ if (info->address_space)
+ address_space = info->address_space;
+
+ if (info->rtc_day_alarm && info->rtc_day_alarm < 128)
+ cmos_rtc.day_alrm = info->rtc_day_alarm;
+ if (info->rtc_mon_alarm && info->rtc_mon_alarm < 128)
+ cmos_rtc.mon_alrm = info->rtc_mon_alarm;
+ if (info->rtc_century && info->rtc_century < 128)
+ cmos_rtc.century = info->rtc_century;
+
+ if (info->wake_on && info->wake_off) {
+ cmos_rtc.wake_on = info->wake_on;
+ cmos_rtc.wake_off = info->wake_off;
+ }
+ }
+
+ cmos_rtc.dev = dev;
+ dev_set_drvdata(dev, &cmos_rtc);
+
+ cmos_rtc.rtc = rtc_device_register(driver_name, dev,
+ &cmos_rtc_ops, THIS_MODULE);
+ if (IS_ERR(cmos_rtc.rtc)) {
+ retval = PTR_ERR(cmos_rtc.rtc);
+ goto cleanup0;
+ }
+
+ rename_region(ports, dev_name(&cmos_rtc.rtc->dev));
+
+ spin_lock_irq(&rtc_lock);
+
+ if (!(flags & CMOS_RTC_FLAGS_NOFREQ)) {
+ /* force periodic irq to CMOS reset default of 1024Hz;
+ *
+ * REVISIT it's been reported that at least one x86_64 ALI
+ * mobo doesn't use 32KHz here ... for portability we might
+ * need to do something about other clock frequencies.
+ */
+ cmos_rtc.rtc->irq_freq = 1024;
+ hpet_set_periodic_freq(cmos_rtc.rtc->irq_freq);
+ CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
+ }
+
+ /* disable irqs */
+ if (is_valid_irq(rtc_irq))
+ cmos_irq_disable(&cmos_rtc, RTC_PIE | RTC_AIE | RTC_UIE);
+
+ rtc_control = CMOS_READ(RTC_CONTROL);
+
+ spin_unlock_irq(&rtc_lock);
+
+ /* FIXME:
+ * <asm-generic/rtc.h> doesn't know 12-hour mode either.
+ */
+ if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) {
+ dev_warn(dev, "only 24-hr supported\n");
+ retval = -ENXIO;
+ goto cleanup1;
+ }
+
+ if (is_valid_irq(rtc_irq)) {
+ irq_handler_t rtc_cmos_int_handler;
+
+ if (is_hpet_enabled()) {
+ rtc_cmos_int_handler = hpet_rtc_interrupt;
+ retval = hpet_register_irq_handler(cmos_interrupt);
+ if (retval) {
+ dev_warn(dev, "hpet_register_irq_handler "
+ " failed in rtc_init().");
+ goto cleanup1;
+ }
+ } else
+ rtc_cmos_int_handler = cmos_interrupt;
+
+ retval = request_irq(rtc_irq, rtc_cmos_int_handler,
+ 0, dev_name(&cmos_rtc.rtc->dev),
+ cmos_rtc.rtc);
+ if (retval < 0) {
+ dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
+ goto cleanup1;
+ }
+ }
+ hpet_rtc_timer_init();
+
+ /* export at least the first block of NVRAM */
+ nvram.size = address_space - NVRAM_OFFSET;
+ retval = sysfs_create_bin_file(&dev->kobj, &nvram);
+ if (retval < 0) {
+ dev_dbg(dev, "can't create nvram file? %d\n", retval);
+ goto cleanup2;
+ }
+
+ dev_info(dev, "%s%s, %zd bytes nvram%s\n",
+ !is_valid_irq(rtc_irq) ? "no alarms" :
+ cmos_rtc.mon_alrm ? "alarms up to one year" :
+ cmos_rtc.day_alrm ? "alarms up to one month" :
+ "alarms up to one day",
+ cmos_rtc.century ? ", y3k" : "",
+ nvram.size,
+ is_hpet_enabled() ? ", hpet irqs" : "");
+
+ return 0;
+
+cleanup2:
+ if (is_valid_irq(rtc_irq))
+ free_irq(rtc_irq, cmos_rtc.rtc);
+cleanup1:
+ cmos_rtc.dev = NULL;
+ rtc_device_unregister(cmos_rtc.rtc);
+cleanup0:
+ if (RTC_IOMAPPED)
+ release_region(ports->start, resource_size(ports));
+ else
+ release_mem_region(ports->start, resource_size(ports));
+ return retval;
+}
+
+static void cmos_do_shutdown(int rtc_irq)
+{
+ spin_lock_irq(&rtc_lock);
+ if (is_valid_irq(rtc_irq))
+ cmos_irq_disable(&cmos_rtc, RTC_IRQMASK);
+ spin_unlock_irq(&rtc_lock);
+}
+
+static void __exit cmos_do_remove(struct device *dev)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ struct resource *ports;
+
+ cmos_do_shutdown(cmos->irq);
+
+ sysfs_remove_bin_file(&dev->kobj, &nvram);
+
+ if (is_valid_irq(cmos->irq)) {
+ free_irq(cmos->irq, cmos->rtc);
+ hpet_unregister_irq_handler(cmos_interrupt);
+ }
+
+ rtc_device_unregister(cmos->rtc);
+ cmos->rtc = NULL;
+
+ ports = cmos->iomem;
+ if (RTC_IOMAPPED)
+ release_region(ports->start, resource_size(ports));
+ else
+ release_mem_region(ports->start, resource_size(ports));
+ cmos->iomem = NULL;
+
+ cmos->dev = NULL;
+}
+
+#ifdef CONFIG_PM
+
+static int cmos_suspend(struct device *dev)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned char tmp;
+
+ /* only the alarm might be a wakeup event source */
+ spin_lock_irq(&rtc_lock);
+ cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
+ if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
+ unsigned char mask;
+
+ if (device_may_wakeup(dev))
+ mask = RTC_IRQMASK & ~RTC_AIE;
+ else
+ mask = RTC_IRQMASK;
+ tmp &= ~mask;
+ CMOS_WRITE(tmp, RTC_CONTROL);
+ hpet_mask_rtc_irq_bit(mask);
+
+ cmos_checkintr(cmos, tmp);
+ }
+ spin_unlock_irq(&rtc_lock);
+
+ if (tmp & RTC_AIE) {
+ cmos->enabled_wake = 1;
+ if (cmos->wake_on)
+ cmos->wake_on(dev);
+ else
+ enable_irq_wake(cmos->irq);
+ }
+
+ dev_dbg(dev, "suspend%s, ctrl %02x\n",
+ (tmp & RTC_AIE) ? ", alarm may wake" : "",
+ tmp);
+
+ return 0;
+}
+
+/* We want RTC alarms to wake us from e.g. ACPI G2/S5 "soft off", even
+ * after a detour through G3 "mechanical off", although the ACPI spec
+ * says wakeup should only work from G1/S4 "hibernate". To most users,
+ * distinctions between S4 and S5 are pointless. So when the hardware
+ * allows, don't draw that distinction.
+ */
+static inline int cmos_poweroff(struct device *dev)
+{
+ return cmos_suspend(dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int cmos_resume(struct device *dev)
+{
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+ unsigned char tmp;
+
+ if (cmos->enabled_wake) {
+ if (cmos->wake_off)
+ cmos->wake_off(dev);
+ else
+ disable_irq_wake(cmos->irq);
+ cmos->enabled_wake = 0;
+ }
+
+ spin_lock_irq(&rtc_lock);
+ tmp = cmos->suspend_ctrl;
+ cmos->suspend_ctrl = 0;
+ /* re-enable any irqs previously active */
+ if (tmp & RTC_IRQMASK) {
+ unsigned char mask;
+
+ if (device_may_wakeup(dev))
+ hpet_rtc_timer_init();
+
+ do {
+ CMOS_WRITE(tmp, RTC_CONTROL);
+ hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK);
+
+ mask = CMOS_READ(RTC_INTR_FLAGS);
+ mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
+ if (!is_hpet_enabled() || !is_intr(mask))
+ break;
+
+ /* force one-shot behavior if HPET blocked
+ * the wake alarm's irq
+ */
+ rtc_update_irq(cmos->rtc, 1, mask);
+ tmp &= ~RTC_AIE;
+ hpet_mask_rtc_irq_bit(RTC_AIE);
+ } while (mask & RTC_AIE);
+ }
+ spin_unlock_irq(&rtc_lock);
+
+ dev_dbg(dev, "resume, ctrl %02x\n", tmp);
+
+ return 0;
+}
+
+#endif
+#else
+
+static inline int cmos_poweroff(struct device *dev)
+{
+ return -ENOSYS;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume);
+
+/*----------------------------------------------------------------*/
+
+/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus.
+ * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs
+ * probably list them in similar PNPBIOS tables; so PNP is more common.
+ *
+ * We don't use legacy "poke at the hardware" probing. Ancient PCs that
+ * predate even PNPBIOS should set up platform_bus devices.
+ */
+
+#ifdef CONFIG_ACPI
+
+#include <linux/acpi.h>
+
+static u32 rtc_handler(void *context)
+{
+ struct device *dev = context;
+
+ pm_wakeup_event(dev, 0);
+ acpi_clear_event(ACPI_EVENT_RTC);
+ acpi_disable_event(ACPI_EVENT_RTC, 0);
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+static inline void rtc_wake_setup(struct device *dev)
+{
+ acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, dev);
+ /*
+ * After the RTC handler is installed, the Fixed_RTC event should
+ * be disabled. Only when the RTC alarm is set will it be enabled.
+ */
+ acpi_clear_event(ACPI_EVENT_RTC);
+ acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_on(struct device *dev)
+{
+ acpi_clear_event(ACPI_EVENT_RTC);
+ acpi_enable_event(ACPI_EVENT_RTC, 0);
+}
+
+static void rtc_wake_off(struct device *dev)
+{
+ acpi_disable_event(ACPI_EVENT_RTC, 0);
+}
+
+/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
+ * its device node and pass extra config data. This helps its driver use
+ * capabilities that the now-obsolete mc146818 didn't have, and informs it
+ * that this board's RTC is wakeup-capable (per ACPI spec).
+ */
+static struct cmos_rtc_board_info acpi_rtc_info;
+
+static void cmos_wake_setup(struct device *dev)
+{
+ if (acpi_disabled)
+ return;
+
+ rtc_wake_setup(dev);
+ acpi_rtc_info.wake_on = rtc_wake_on;
+ acpi_rtc_info.wake_off = rtc_wake_off;
+
+ /* workaround bug in some ACPI tables */
+ if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
+ dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
+ acpi_gbl_FADT.month_alarm);
+ acpi_gbl_FADT.month_alarm = 0;
+ }
+
+ acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
+ acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
+ acpi_rtc_info.rtc_century = acpi_gbl_FADT.century;
+
+ /* NOTE: S4_RTC_WAKE is NOT currently useful to Linux */
+ if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
+ dev_info(dev, "RTC can wake from S4\n");
+
+ dev->platform_data = &acpi_rtc_info;
+
+ /* RTC always wakes from S1/S2/S3, and often S4/STD */
+ device_init_wakeup(dev, 1);
+}
+
+#else
+
+static void cmos_wake_setup(struct device *dev)
+{
+}
+
+#endif
+
+#ifdef CONFIG_PNP
+
+#include <linux/pnp.h>
+
+static int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
+{
+ cmos_wake_setup(&pnp->dev);
+
+ if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0))
+ /* Some machines contain a PNP entry for the RTC, but
+ * don't define the IRQ. It should always be safe to
+ * hardcode it in these cases
+ */
+ return cmos_do_probe(&pnp->dev,
+ pnp_get_resource(pnp, IORESOURCE_IO, 0), 8);
+ else
+ return cmos_do_probe(&pnp->dev,
+ pnp_get_resource(pnp, IORESOURCE_IO, 0),
+ pnp_irq(pnp, 0));
+}
+
+static void __exit cmos_pnp_remove(struct pnp_dev *pnp)
+{
+ cmos_do_remove(&pnp->dev);
+}
+
+static void cmos_pnp_shutdown(struct pnp_dev *pnp)
+{
+ struct device *dev = &pnp->dev;
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+
+ if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev))
+ return;
+
+ cmos_do_shutdown(cmos->irq);
+}
+
+static const struct pnp_device_id rtc_ids[] = {
+ { .id = "PNP0b00", },
+ { .id = "PNP0b01", },
+ { .id = "PNP0b02", },
+ { },
+};
+MODULE_DEVICE_TABLE(pnp, rtc_ids);
+
+static struct pnp_driver cmos_pnp_driver = {
+ .name = (char *) driver_name,
+ .id_table = rtc_ids,
+ .probe = cmos_pnp_probe,
+ .remove = __exit_p(cmos_pnp_remove),
+ .shutdown = cmos_pnp_shutdown,
+
+ /* flag ensures resume() gets called, and stops syslog spam */
+ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
+ .driver = {
+ .pm = &cmos_pm_ops,
+ },
+};
+
+#endif /* CONFIG_PNP */
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cmos_match[] = {
+ {
+ .compatible = "motorola,mc146818",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_cmos_match);
+
+static __init void cmos_of_init(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct rtc_time time;
+ int ret;
+ const __be32 *val;
+
+ if (!node)
+ return;
+
+ val = of_get_property(node, "ctrl-reg", NULL);
+ if (val)
+ CMOS_WRITE(be32_to_cpup(val), RTC_CONTROL);
+
+ val = of_get_property(node, "freq-reg", NULL);
+ if (val)
+ CMOS_WRITE(be32_to_cpup(val), RTC_FREQ_SELECT);
+
+ get_rtc_time(&time);
+ ret = rtc_valid_tm(&time);
+ if (ret) {
+ struct rtc_time def_time = {
+ .tm_year = 1,
+ .tm_mday = 1,
+ };
+ set_rtc_time(&def_time);
+ }
+}
+#else
+static inline void cmos_of_init(struct platform_device *pdev) {}
+#endif
+/*----------------------------------------------------------------*/
+
+/* Platform setup should have set up an RTC device, when PNP is
+ * unavailable ... this could happen even on (older) PCs.
+ */
+
+static int __init cmos_platform_probe(struct platform_device *pdev)
+{
+ struct resource *resource;
+ int irq;
+
+ cmos_of_init(pdev);
+ cmos_wake_setup(&pdev->dev);
+
+ if (RTC_IOMAPPED)
+ resource = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ else
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ irq = -1;
+
+ return cmos_do_probe(&pdev->dev, resource, irq);
+}
+
+static int __exit cmos_platform_remove(struct platform_device *pdev)
+{
+ cmos_do_remove(&pdev->dev);
+ return 0;
+}
+
+static void cmos_platform_shutdown(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cmos_rtc *cmos = dev_get_drvdata(dev);
+
+ if (system_state == SYSTEM_POWER_OFF && !cmos_poweroff(dev))
+ return;
+
+ cmos_do_shutdown(cmos->irq);
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc_cmos");
+
+static struct platform_driver cmos_platform_driver = {
+ .remove = __exit_p(cmos_platform_remove),
+ .shutdown = cmos_platform_shutdown,
+ .driver = {
+ .name = driver_name,
+#ifdef CONFIG_PM
+ .pm = &cmos_pm_ops,
+#endif
+ .of_match_table = of_match_ptr(of_cmos_match),
+ }
+};
+
+#ifdef CONFIG_PNP
+static bool pnp_driver_registered;
+#endif
+static bool platform_driver_registered;
+
+static int __init cmos_init(void)
+{
+ int retval = 0;
+
+#ifdef CONFIG_PNP
+ retval = pnp_register_driver(&cmos_pnp_driver);
+ if (retval == 0)
+ pnp_driver_registered = true;
+#endif
+
+ if (!cmos_rtc.dev) {
+ retval = platform_driver_probe(&cmos_platform_driver,
+ cmos_platform_probe);
+ if (retval == 0)
+ platform_driver_registered = true;
+ }
+
+ dmi_check_system(rtc_quirks);
+
+ if (retval == 0)
+ return 0;
+
+#ifdef CONFIG_PNP
+ if (pnp_driver_registered)
+ pnp_unregister_driver(&cmos_pnp_driver);
+#endif
+ return retval;
+}
+module_init(cmos_init);
+
+static void __exit cmos_exit(void)
+{
+#ifdef CONFIG_PNP
+ if (pnp_driver_registered)
+ pnp_unregister_driver(&cmos_pnp_driver);
+#endif
+ if (platform_driver_registered)
+ platform_driver_unregister(&cmos_platform_driver);
+}
+module_exit(cmos_exit);
+
+
+MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
new file mode 100644
index 000000000..56343b2fb
--- /dev/null
+++ b/drivers/rtc/rtc-coh901331.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2007-2009 ST-Ericsson AB
+ * License terms: GNU General Public License (GPL) version 2
+ * Real Time Clock interface for ST-Ericsson AB COH 901 331 RTC.
+ * Author: Linus Walleij <linus.walleij@stericsson.com>
+ * Based on rtc-pl031.c by Deepak Saxena <dsaxena@plexity.net>
+ * Copyright 2006 (c) MontaVista Software, Inc.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/*
+ * Registers in the COH 901 331
+ */
+/* Alarm value 32bit (R/W) */
+#define COH901331_ALARM 0x00U
+/* Used to set current time 32bit (R/W) */
+#define COH901331_SET_TIME 0x04U
+/* Indication if current time is valid 32bit (R/-) */
+#define COH901331_VALID 0x08U
+/* Read the current time 32bit (R/-) */
+#define COH901331_CUR_TIME 0x0cU
+/* Event register for the "alarm" interrupt */
+#define COH901331_IRQ_EVENT 0x10U
+/* Mask register for the "alarm" interrupt */
+#define COH901331_IRQ_MASK 0x14U
+/* Force register for the "alarm" interrupt */
+#define COH901331_IRQ_FORCE 0x18U
+
+/*
+ * Reference to RTC block clock
+ * Notice that the frequent clk_enable()/clk_disable() on this
+ * clock is mainly to be able to turn on/off other clocks in the
+ * hierarchy as needed, the RTC clock is always on anyway.
+ */
+struct coh901331_port {
+ struct rtc_device *rtc;
+ struct clk *clk;
+ void __iomem *virtbase;
+ int irq;
+#ifdef CONFIG_PM_SLEEP
+ u32 irqmaskstore;
+#endif
+};
+
+static irqreturn_t coh901331_interrupt(int irq, void *data)
+{
+ struct coh901331_port *rtap = data;
+
+ clk_enable(rtap->clk);
+ /* Ack IRQ */
+ writel(1, rtap->virtbase + COH901331_IRQ_EVENT);
+ /*
+ * Disable the interrupt. This is necessary because
+ * the RTC lives on a lower-clocked line and will
+ * not release the IRQ line until after a few (slower)
+ * clock cycles. The interrupt will be re-enabled when
+ * a new alarm is set anyway.
+ */
+ writel(0, rtap->virtbase + COH901331_IRQ_MASK);
+ clk_disable(rtap->clk);
+
+ /* Set alarm flag */
+ rtc_update_irq(rtap->rtc, 1, RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int coh901331_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+
+ clk_enable(rtap->clk);
+ /* Check if the time is valid */
+ if (readl(rtap->virtbase + COH901331_VALID)) {
+ rtc_time_to_tm(readl(rtap->virtbase + COH901331_CUR_TIME), tm);
+ clk_disable(rtap->clk);
+ return rtc_valid_tm(tm);
+ }
+ clk_disable(rtap->clk);
+ return -EINVAL;
+}
+
+static int coh901331_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+
+ clk_enable(rtap->clk);
+ writel(secs, rtap->virtbase + COH901331_SET_TIME);
+ clk_disable(rtap->clk);
+
+ return 0;
+}
+
+static int coh901331_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+
+ clk_enable(rtap->clk);
+ rtc_time_to_tm(readl(rtap->virtbase + COH901331_ALARM), &alarm->time);
+ alarm->pending = readl(rtap->virtbase + COH901331_IRQ_EVENT) & 1U;
+ alarm->enabled = readl(rtap->virtbase + COH901331_IRQ_MASK) & 1U;
+ clk_disable(rtap->clk);
+
+ return 0;
+}
+
+static int coh901331_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+ unsigned long time;
+
+ rtc_tm_to_time(&alarm->time, &time);
+ clk_enable(rtap->clk);
+ writel(time, rtap->virtbase + COH901331_ALARM);
+ writel(alarm->enabled, rtap->virtbase + COH901331_IRQ_MASK);
+ clk_disable(rtap->clk);
+
+ return 0;
+}
+
+static int coh901331_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+
+ clk_enable(rtap->clk);
+ if (enabled)
+ writel(1, rtap->virtbase + COH901331_IRQ_MASK);
+ else
+ writel(0, rtap->virtbase + COH901331_IRQ_MASK);
+ clk_disable(rtap->clk);
+
+ return 0;
+}
+
+static struct rtc_class_ops coh901331_ops = {
+ .read_time = coh901331_read_time,
+ .set_mmss = coh901331_set_mmss,
+ .read_alarm = coh901331_read_alarm,
+ .set_alarm = coh901331_set_alarm,
+ .alarm_irq_enable = coh901331_alarm_irq_enable,
+};
+
+static int __exit coh901331_remove(struct platform_device *pdev)
+{
+ struct coh901331_port *rtap = platform_get_drvdata(pdev);
+
+ if (rtap)
+ clk_unprepare(rtap->clk);
+
+ return 0;
+}
+
+
+static int __init coh901331_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct coh901331_port *rtap;
+ struct resource *res;
+
+ rtap = devm_kzalloc(&pdev->dev,
+ sizeof(struct coh901331_port), GFP_KERNEL);
+ if (!rtap)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtap->virtbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtap->virtbase))
+ return PTR_ERR(rtap->virtbase);
+
+ rtap->irq = platform_get_irq(pdev, 0);
+ if (devm_request_irq(&pdev->dev, rtap->irq, coh901331_interrupt, 0,
+ "RTC COH 901 331 Alarm", rtap))
+ return -EIO;
+
+ rtap->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(rtap->clk)) {
+ ret = PTR_ERR(rtap->clk);
+ dev_err(&pdev->dev, "could not get clock\n");
+ return ret;
+ }
+
+ /* We enable/disable the clock only to assure it works */
+ ret = clk_prepare_enable(rtap->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "could not enable clock\n");
+ return ret;
+ }
+ clk_disable(rtap->clk);
+
+ platform_set_drvdata(pdev, rtap);
+ rtap->rtc = devm_rtc_device_register(&pdev->dev, "coh901331",
+ &coh901331_ops, THIS_MODULE);
+ if (IS_ERR(rtap->rtc)) {
+ ret = PTR_ERR(rtap->rtc);
+ goto out_no_rtc;
+ }
+
+ return 0;
+
+ out_no_rtc:
+ clk_unprepare(rtap->clk);
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int coh901331_suspend(struct device *dev)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+
+ /*
+ * If this RTC alarm will be used for waking the system up,
+ * don't disable it of course. Else we just disable the alarm
+ * and await suspension.
+ */
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(rtap->irq);
+ } else {
+ clk_enable(rtap->clk);
+ rtap->irqmaskstore = readl(rtap->virtbase + COH901331_IRQ_MASK);
+ writel(0, rtap->virtbase + COH901331_IRQ_MASK);
+ clk_disable(rtap->clk);
+ }
+ clk_unprepare(rtap->clk);
+ return 0;
+}
+
+static int coh901331_resume(struct device *dev)
+{
+ struct coh901331_port *rtap = dev_get_drvdata(dev);
+
+ clk_prepare(rtap->clk);
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(rtap->irq);
+ } else {
+ clk_enable(rtap->clk);
+ writel(rtap->irqmaskstore, rtap->virtbase + COH901331_IRQ_MASK);
+ clk_disable(rtap->clk);
+ }
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(coh901331_pm_ops, coh901331_suspend, coh901331_resume);
+
+static void coh901331_shutdown(struct platform_device *pdev)
+{
+ struct coh901331_port *rtap = platform_get_drvdata(pdev);
+
+ clk_enable(rtap->clk);
+ writel(0, rtap->virtbase + COH901331_IRQ_MASK);
+ clk_disable_unprepare(rtap->clk);
+}
+
+static const struct of_device_id coh901331_dt_match[] = {
+ { .compatible = "stericsson,coh901331" },
+ {},
+};
+
+static struct platform_driver coh901331_driver = {
+ .driver = {
+ .name = "rtc-coh901331",
+ .pm = &coh901331_pm_ops,
+ .of_match_table = coh901331_dt_match,
+ },
+ .remove = __exit_p(coh901331_remove),
+ .shutdown = coh901331_shutdown,
+};
+
+module_platform_driver_probe(coh901331_driver, coh901331_probe);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson AB COH 901 331 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-core.h b/drivers/rtc/rtc-core.h
new file mode 100644
index 000000000..5f9df7430
--- /dev/null
+++ b/drivers/rtc/rtc-core.h
@@ -0,0 +1,70 @@
+#ifdef CONFIG_RTC_INTF_DEV
+
+extern void __init rtc_dev_init(void);
+extern void __exit rtc_dev_exit(void);
+extern void rtc_dev_prepare(struct rtc_device *rtc);
+extern void rtc_dev_add_device(struct rtc_device *rtc);
+extern void rtc_dev_del_device(struct rtc_device *rtc);
+
+#else
+
+static inline void rtc_dev_init(void)
+{
+}
+
+static inline void rtc_dev_exit(void)
+{
+}
+
+static inline void rtc_dev_prepare(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_dev_add_device(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_dev_del_device(struct rtc_device *rtc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_RTC_INTF_PROC
+
+extern void rtc_proc_add_device(struct rtc_device *rtc);
+extern void rtc_proc_del_device(struct rtc_device *rtc);
+
+#else
+
+static inline void rtc_proc_add_device(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_proc_del_device(struct rtc_device *rtc)
+{
+}
+
+#endif
+
+#ifdef CONFIG_RTC_INTF_SYSFS
+
+extern void __init rtc_sysfs_init(struct class *);
+extern void rtc_sysfs_add_device(struct rtc_device *rtc);
+extern void rtc_sysfs_del_device(struct rtc_device *rtc);
+
+#else
+
+static inline void rtc_sysfs_init(struct class *rtc)
+{
+}
+
+static inline void rtc_sysfs_add_device(struct rtc_device *rtc)
+{
+}
+
+static inline void rtc_sysfs_del_device(struct rtc_device *rtc)
+{
+}
+
+#endif
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
new file mode 100644
index 000000000..1ba4371cb
--- /dev/null
+++ b/drivers/rtc/rtc-da9052.c
@@ -0,0 +1,331 @@
+/*
+ * Real time clock driver for DA9052
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+#define rtc_err(rtc, fmt, ...) \
+ dev_err(rtc->da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
+
+#define DA9052_GET_TIME_RETRIES 5
+
+struct da9052_rtc {
+ struct rtc_device *rtc;
+ struct da9052 *da9052;
+};
+
+static int da9052_rtc_enable_alarm(struct da9052_rtc *rtc, bool enable)
+{
+ int ret;
+ if (enable) {
+ ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
+ DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON,
+ DA9052_ALARM_Y_ALARM_ON);
+ if (ret != 0)
+ rtc_err(rtc, "Failed to enable ALM: %d\n", ret);
+ } else {
+ ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
+ DA9052_ALARM_Y_ALARM_ON|DA9052_ALARM_Y_TICK_ON, 0);
+ if (ret != 0)
+ rtc_err(rtc, "Write error: %d\n", ret);
+ }
+ return ret;
+}
+
+static irqreturn_t da9052_rtc_irq(int irq, void *data)
+{
+ struct da9052_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int da9052_read_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm)
+{
+ int ret;
+ uint8_t v[2][5];
+ int idx = 1;
+ int timeout = DA9052_GET_TIME_RETRIES;
+
+ ret = da9052_group_read(rtc->da9052, DA9052_ALARM_MI_REG, 5, &v[0][0]);
+ if (ret) {
+ rtc_err(rtc, "Failed to group read ALM: %d\n", ret);
+ return ret;
+ }
+
+ do {
+ ret = da9052_group_read(rtc->da9052,
+ DA9052_ALARM_MI_REG, 5, &v[idx][0]);
+ if (ret) {
+ rtc_err(rtc, "Failed to group read ALM: %d\n", ret);
+ return ret;
+ }
+
+ if (memcmp(&v[0][0], &v[1][0], 5) == 0) {
+ rtc_tm->tm_year = (v[0][4] & DA9052_RTC_YEAR) + 100;
+ rtc_tm->tm_mon = (v[0][3] & DA9052_RTC_MONTH) - 1;
+ rtc_tm->tm_mday = v[0][2] & DA9052_RTC_DAY;
+ rtc_tm->tm_hour = v[0][1] & DA9052_RTC_HOUR;
+ rtc_tm->tm_min = v[0][0] & DA9052_RTC_MIN;
+
+ ret = rtc_valid_tm(rtc_tm);
+ return ret;
+ }
+
+ idx = (1-idx);
+ msleep(20);
+
+ } while (timeout--);
+
+ rtc_err(rtc, "Timed out reading alarm time\n");
+
+ return -EIO;
+}
+
+static int da9052_set_alarm(struct da9052_rtc *rtc, struct rtc_time *rtc_tm)
+{
+ struct da9052 *da9052 = rtc->da9052;
+ unsigned long alm_time;
+ int ret;
+ uint8_t v[3];
+
+ ret = rtc_tm_to_time(rtc_tm, &alm_time);
+ if (ret != 0)
+ return ret;
+
+ if (rtc_tm->tm_sec > 0) {
+ alm_time += 60 - rtc_tm->tm_sec;
+ rtc_time_to_tm(alm_time, rtc_tm);
+ }
+ BUG_ON(rtc_tm->tm_sec); /* it will cause repeated irqs if not zero */
+
+ rtc_tm->tm_year -= 100;
+ rtc_tm->tm_mon += 1;
+
+ ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG,
+ DA9052_RTC_MIN, rtc_tm->tm_min);
+ if (ret != 0) {
+ rtc_err(rtc, "Failed to write ALRM MIN: %d\n", ret);
+ return ret;
+ }
+
+ v[0] = rtc_tm->tm_hour;
+ v[1] = rtc_tm->tm_mday;
+ v[2] = rtc_tm->tm_mon;
+
+ ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
+ DA9052_RTC_YEAR, rtc_tm->tm_year);
+ if (ret != 0)
+ rtc_err(rtc, "Failed to write ALRM YEAR: %d\n", ret);
+
+ return ret;
+}
+
+static int da9052_rtc_get_alarm_status(struct da9052_rtc *rtc)
+{
+ int ret;
+
+ ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_Y_REG);
+ if (ret < 0) {
+ rtc_err(rtc, "Failed to read ALM: %d\n", ret);
+ return ret;
+ }
+
+ return !!(ret&DA9052_ALARM_Y_ALARM_ON);
+}
+
+static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+ uint8_t v[2][6];
+ int idx = 1;
+ int timeout = DA9052_GET_TIME_RETRIES;
+
+ ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, &v[0][0]);
+ if (ret) {
+ rtc_err(rtc, "Failed to read RTC time : %d\n", ret);
+ return ret;
+ }
+
+ do {
+ ret = da9052_group_read(rtc->da9052,
+ DA9052_COUNT_S_REG, 6, &v[idx][0]);
+ if (ret) {
+ rtc_err(rtc, "Failed to read RTC time : %d\n", ret);
+ return ret;
+ }
+
+ if (memcmp(&v[0][0], &v[1][0], 6) == 0) {
+ rtc_tm->tm_year = (v[0][5] & DA9052_RTC_YEAR) + 100;
+ rtc_tm->tm_mon = (v[0][4] & DA9052_RTC_MONTH) - 1;
+ rtc_tm->tm_mday = v[0][3] & DA9052_RTC_DAY;
+ rtc_tm->tm_hour = v[0][2] & DA9052_RTC_HOUR;
+ rtc_tm->tm_min = v[0][1] & DA9052_RTC_MIN;
+ rtc_tm->tm_sec = v[0][0] & DA9052_RTC_SEC;
+
+ ret = rtc_valid_tm(rtc_tm);
+ return ret;
+ }
+
+ idx = (1-idx);
+ msleep(20);
+
+ } while (timeout--);
+
+ rtc_err(rtc, "Timed out reading time\n");
+
+ return -EIO;
+}
+
+static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9052_rtc *rtc;
+ uint8_t v[6];
+ int ret;
+
+ /* DA9052 only has 6 bits for year - to represent 2000-2063 */
+ if ((tm->tm_year < 100) || (tm->tm_year > 163))
+ return -EINVAL;
+
+ rtc = dev_get_drvdata(dev);
+
+ v[0] = tm->tm_sec;
+ v[1] = tm->tm_min;
+ v[2] = tm->tm_hour;
+ v[3] = tm->tm_mday;
+ v[4] = tm->tm_mon + 1;
+ v[5] = tm->tm_year - 100;
+
+ ret = da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
+ if (ret < 0)
+ rtc_err(rtc, "failed to set RTC time: %d\n", ret);
+ return ret;
+}
+
+static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+
+ ret = da9052_read_alarm(rtc, tm);
+ if (ret < 0) {
+ rtc_err(rtc, "failed to read RTC alarm: %d\n", ret);
+ return ret;
+ }
+
+ alrm->enabled = da9052_rtc_get_alarm_status(rtc);
+ return 0;
+}
+
+static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+
+ /* DA9052 only has 6 bits for year - to represent 2000-2063 */
+ if ((tm->tm_year < 100) || (tm->tm_year > 163))
+ return -EINVAL;
+
+ ret = da9052_rtc_enable_alarm(rtc, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_set_alarm(rtc, tm);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_rtc_enable_alarm(rtc, 1);
+ return ret;
+}
+
+static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+
+ return da9052_rtc_enable_alarm(rtc, enabled);
+}
+
+static const struct rtc_class_ops da9052_rtc_ops = {
+ .read_time = da9052_rtc_read_time,
+ .set_time = da9052_rtc_set_time,
+ .read_alarm = da9052_rtc_read_alarm,
+ .set_alarm = da9052_rtc_set_alarm,
+ .alarm_irq_enable = da9052_rtc_alarm_irq_enable,
+};
+
+static int da9052_rtc_probe(struct platform_device *pdev)
+{
+ struct da9052_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, rtc);
+
+ ret = da9052_reg_write(rtc->da9052, DA9052_BBAT_CONT_REG, 0xFE);
+ if (ret < 0) {
+ rtc_err(rtc,
+ "Failed to setup RTC battery charging: %d\n", ret);
+ return ret;
+ }
+
+ ret = da9052_reg_update(rtc->da9052, DA9052_ALARM_Y_REG,
+ DA9052_ALARM_Y_TICK_ON, 0);
+ if (ret != 0)
+ rtc_err(rtc, "Failed to disable TICKS: %d\n", ret);
+
+ ret = da9052_request_irq(rtc->da9052, DA9052_IRQ_ALARM, "ALM",
+ da9052_rtc_irq, rtc);
+ if (ret != 0) {
+ rtc_err(rtc, "irq registration failed: %d\n", ret);
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, true);
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &da9052_rtc_ops, THIS_MODULE);
+ return PTR_ERR_OR_ZERO(rtc->rtc);
+}
+
+static struct platform_driver da9052_rtc_driver = {
+ .probe = da9052_rtc_probe,
+ .driver = {
+ .name = "da9052-rtc",
+ },
+};
+
+module_platform_driver(da9052_rtc_driver);
+
+MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
+MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-rtc");
diff --git a/drivers/rtc/rtc-da9055.c b/drivers/rtc/rtc-da9055.c
new file mode 100644
index 000000000..7ec0872d5
--- /dev/null
+++ b/drivers/rtc/rtc-da9055.c
@@ -0,0 +1,403 @@
+/*
+ * Real time clock driver for DA9055
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+#include <linux/mfd/da9055/pdata.h>
+
+struct da9055_rtc {
+ struct rtc_device *rtc;
+ struct da9055 *da9055;
+ int alarm_enable;
+};
+
+static int da9055_rtc_enable_alarm(struct da9055_rtc *rtc, bool enable)
+{
+ int ret;
+ if (enable) {
+ ret = da9055_reg_update(rtc->da9055, DA9055_REG_ALARM_Y,
+ DA9055_RTC_ALM_EN,
+ DA9055_RTC_ALM_EN);
+ if (ret != 0)
+ dev_err(rtc->da9055->dev, "Failed to enable ALM: %d\n",
+ ret);
+ rtc->alarm_enable = 1;
+ } else {
+ ret = da9055_reg_update(rtc->da9055, DA9055_REG_ALARM_Y,
+ DA9055_RTC_ALM_EN, 0);
+ if (ret != 0)
+ dev_err(rtc->da9055->dev,
+ "Failed to disable ALM: %d\n", ret);
+ rtc->alarm_enable = 0;
+ }
+ return ret;
+}
+
+static irqreturn_t da9055_rtc_alm_irq(int irq, void *data)
+{
+ struct da9055_rtc *rtc = data;
+
+ da9055_rtc_enable_alarm(rtc, 0);
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int da9055_read_alarm(struct da9055 *da9055, struct rtc_time *rtc_tm)
+{
+ int ret;
+ uint8_t v[5];
+
+ ret = da9055_group_read(da9055, DA9055_REG_ALARM_MI, 5, v);
+ if (ret != 0) {
+ dev_err(da9055->dev, "Failed to group read ALM: %d\n", ret);
+ return ret;
+ }
+
+ rtc_tm->tm_year = (v[4] & DA9055_RTC_ALM_YEAR) + 100;
+ rtc_tm->tm_mon = (v[3] & DA9055_RTC_ALM_MONTH) - 1;
+ rtc_tm->tm_mday = v[2] & DA9055_RTC_ALM_DAY;
+ rtc_tm->tm_hour = v[1] & DA9055_RTC_ALM_HOUR;
+ rtc_tm->tm_min = v[0] & DA9055_RTC_ALM_MIN;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int da9055_set_alarm(struct da9055 *da9055, struct rtc_time *rtc_tm)
+{
+ int ret;
+ uint8_t v[2];
+
+ rtc_tm->tm_year -= 100;
+ rtc_tm->tm_mon += 1;
+
+ ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MI,
+ DA9055_RTC_ALM_MIN, rtc_tm->tm_min);
+ if (ret != 0) {
+ dev_err(da9055->dev, "Failed to write ALRM MIN: %d\n", ret);
+ return ret;
+ }
+
+ v[0] = rtc_tm->tm_hour;
+ v[1] = rtc_tm->tm_mday;
+
+ ret = da9055_group_write(da9055, DA9055_REG_ALARM_H, 2, v);
+ if (ret < 0)
+ return ret;
+
+ ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MO,
+ DA9055_RTC_ALM_MONTH, rtc_tm->tm_mon);
+ if (ret < 0)
+ dev_err(da9055->dev, "Failed to write ALM Month:%d\n", ret);
+
+ ret = da9055_reg_update(da9055, DA9055_REG_ALARM_Y,
+ DA9055_RTC_ALM_YEAR, rtc_tm->tm_year);
+ if (ret < 0)
+ dev_err(da9055->dev, "Failed to write ALM Year:%d\n", ret);
+
+ return ret;
+}
+
+static int da9055_rtc_get_alarm_status(struct da9055 *da9055)
+{
+ int ret;
+
+ ret = da9055_reg_read(da9055, DA9055_REG_ALARM_Y);
+ if (ret < 0) {
+ dev_err(da9055->dev, "Failed to read ALM: %d\n", ret);
+ return ret;
+ }
+ ret &= DA9055_RTC_ALM_EN;
+ return (ret > 0) ? 1 : 0;
+}
+
+static int da9055_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct da9055_rtc *rtc = dev_get_drvdata(dev);
+ uint8_t v[6];
+ int ret;
+
+ ret = da9055_reg_read(rtc->da9055, DA9055_REG_COUNT_S);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Registers are only valid when RTC_READ
+ * status bit is asserted
+ */
+ if (!(ret & DA9055_RTC_READ))
+ return -EBUSY;
+
+ ret = da9055_group_read(rtc->da9055, DA9055_REG_COUNT_S, 6, v);
+ if (ret < 0) {
+ dev_err(rtc->da9055->dev, "Failed to read RTC time : %d\n",
+ ret);
+ return ret;
+ }
+
+ rtc_tm->tm_year = (v[5] & DA9055_RTC_YEAR) + 100;
+ rtc_tm->tm_mon = (v[4] & DA9055_RTC_MONTH) - 1;
+ rtc_tm->tm_mday = v[3] & DA9055_RTC_DAY;
+ rtc_tm->tm_hour = v[2] & DA9055_RTC_HOUR;
+ rtc_tm->tm_min = v[1] & DA9055_RTC_MIN;
+ rtc_tm->tm_sec = v[0] & DA9055_RTC_SEC;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int da9055_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9055_rtc *rtc;
+ uint8_t v[6];
+
+ rtc = dev_get_drvdata(dev);
+
+ v[0] = tm->tm_sec;
+ v[1] = tm->tm_min;
+ v[2] = tm->tm_hour;
+ v[3] = tm->tm_mday;
+ v[4] = tm->tm_mon + 1;
+ v[5] = tm->tm_year - 100;
+
+ return da9055_group_write(rtc->da9055, DA9055_REG_COUNT_S, 6, v);
+}
+
+static int da9055_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9055_rtc *rtc = dev_get_drvdata(dev);
+
+ ret = da9055_read_alarm(rtc->da9055, tm);
+
+ if (ret)
+ return ret;
+
+ alrm->enabled = da9055_rtc_get_alarm_status(rtc->da9055);
+
+ return 0;
+}
+
+static int da9055_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9055_rtc *rtc = dev_get_drvdata(dev);
+
+ ret = da9055_rtc_enable_alarm(rtc, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = da9055_set_alarm(rtc->da9055, tm);
+ if (ret)
+ return ret;
+
+ ret = da9055_rtc_enable_alarm(rtc, 1);
+
+ return ret;
+}
+
+static int da9055_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct da9055_rtc *rtc = dev_get_drvdata(dev);
+
+ return da9055_rtc_enable_alarm(rtc, enabled);
+}
+
+static const struct rtc_class_ops da9055_rtc_ops = {
+ .read_time = da9055_rtc_read_time,
+ .set_time = da9055_rtc_set_time,
+ .read_alarm = da9055_rtc_read_alarm,
+ .set_alarm = da9055_rtc_set_alarm,
+ .alarm_irq_enable = da9055_rtc_alarm_irq_enable,
+};
+
+static int da9055_rtc_device_init(struct da9055 *da9055,
+ struct da9055_pdata *pdata)
+{
+ int ret;
+
+ /* Enable RTC and the internal Crystal */
+ ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
+ DA9055_RTC_EN, DA9055_RTC_EN);
+ if (ret < 0)
+ return ret;
+ ret = da9055_reg_update(da9055, DA9055_REG_EN_32K,
+ DA9055_CRYSTAL_EN, DA9055_CRYSTAL_EN);
+ if (ret < 0)
+ return ret;
+
+ /* Enable RTC in Power Down mode */
+ ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
+ DA9055_RTC_MODE_PD, DA9055_RTC_MODE_PD);
+ if (ret < 0)
+ return ret;
+
+ /* Enable RTC in Reset mode */
+ if (pdata && pdata->reset_enable) {
+ ret = da9055_reg_update(da9055, DA9055_REG_CONTROL_B,
+ DA9055_RTC_MODE_SD,
+ DA9055_RTC_MODE_SD <<
+ DA9055_RTC_MODE_SD_SHIFT);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Disable the RTC TICK ALM */
+ ret = da9055_reg_update(da9055, DA9055_REG_ALARM_MO,
+ DA9055_RTC_TICK_WAKE_MASK, 0);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int da9055_rtc_probe(struct platform_device *pdev)
+{
+ struct da9055_rtc *rtc;
+ struct da9055_pdata *pdata = NULL;
+ int ret, alm_irq;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9055_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->da9055 = dev_get_drvdata(pdev->dev.parent);
+ pdata = dev_get_platdata(rtc->da9055->dev);
+ platform_set_drvdata(pdev, rtc);
+
+ ret = da9055_rtc_device_init(rtc->da9055, pdata);
+ if (ret < 0)
+ goto err_rtc;
+
+ ret = da9055_reg_read(rtc->da9055, DA9055_REG_ALARM_Y);
+ if (ret < 0)
+ goto err_rtc;
+
+ if (ret & DA9055_RTC_ALM_EN)
+ rtc->alarm_enable = 1;
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &da9055_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ goto err_rtc;
+ }
+
+ alm_irq = platform_get_irq_byname(pdev, "ALM");
+ if (alm_irq < 0)
+ return alm_irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
+ da9055_rtc_alm_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ALM", rtc);
+ if (ret != 0)
+ dev_err(rtc->da9055->dev, "irq registration failed: %d\n", ret);
+
+err_rtc:
+ return ret;
+
+}
+
+#ifdef CONFIG_PM
+/* Turn off the alarm if it should not be a wake source. */
+static int da9055_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ /* Disable the ALM IRQ */
+ ret = da9055_rtc_enable_alarm(rtc, 0);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to disable RTC ALM\n");
+ }
+
+ return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int da9055_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ if (rtc->alarm_enable) {
+ ret = da9055_rtc_enable_alarm(rtc, 1);
+ if (ret < 0)
+ dev_err(&pdev->dev,
+ "Failed to restart RTC ALM\n");
+ }
+ }
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int da9055_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct da9055_rtc *rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ ret = da9055_rtc_enable_alarm(rtc, 0);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to freeze RTC ALMs\n");
+
+ return 0;
+
+}
+#else
+#define da9055_rtc_suspend NULL
+#define da9055_rtc_resume NULL
+#define da9055_rtc_freeze NULL
+#endif
+
+static const struct dev_pm_ops da9055_rtc_pm_ops = {
+ .suspend = da9055_rtc_suspend,
+ .resume = da9055_rtc_resume,
+
+ .freeze = da9055_rtc_freeze,
+ .thaw = da9055_rtc_resume,
+ .restore = da9055_rtc_resume,
+
+ .poweroff = da9055_rtc_suspend,
+};
+
+static struct platform_driver da9055_rtc_driver = {
+ .probe = da9055_rtc_probe,
+ .driver = {
+ .name = "da9055-rtc",
+ .pm = &da9055_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(da9055_rtc_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("RTC driver for Dialog DA9055 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-rtc");
diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c
new file mode 100644
index 000000000..7ffc5707f
--- /dev/null
+++ b/drivers/rtc/rtc-da9063.c
@@ -0,0 +1,352 @@
+/* rtc-da9063.c - Real time clock device driver for DA9063
+ * Copyright (C) 2013-14 Dialog Semiconductor Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/mfd/da9063/registers.h>
+#include <linux/mfd/da9063/core.h>
+
+#define YEARS_TO_DA9063(year) ((year) - 100)
+#define MONTHS_TO_DA9063(month) ((month) + 1)
+#define YEARS_FROM_DA9063(year) ((year) + 100)
+#define MONTHS_FROM_DA9063(month) ((month) - 1)
+
+#define RTC_ALARM_DATA_LEN (DA9063_AD_REG_ALARM_Y - DA9063_AD_REG_ALARM_MI + 1)
+
+#define RTC_DATA_LEN (DA9063_REG_COUNT_Y - DA9063_REG_COUNT_S + 1)
+#define RTC_SEC 0
+#define RTC_MIN 1
+#define RTC_HOUR 2
+#define RTC_DAY 3
+#define RTC_MONTH 4
+#define RTC_YEAR 5
+
+struct da9063_rtc {
+ struct rtc_device *rtc_dev;
+ struct da9063 *hw;
+ struct rtc_time alarm_time;
+ bool rtc_sync;
+ int alarm_year;
+ int alarm_start;
+ int alarm_len;
+ int data_start;
+};
+
+static void da9063_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+ tm->tm_sec = data[RTC_SEC] & DA9063_COUNT_SEC_MASK;
+ tm->tm_min = data[RTC_MIN] & DA9063_COUNT_MIN_MASK;
+ tm->tm_hour = data[RTC_HOUR] & DA9063_COUNT_HOUR_MASK;
+ tm->tm_mday = data[RTC_DAY] & DA9063_COUNT_DAY_MASK;
+ tm->tm_mon = MONTHS_FROM_DA9063(data[RTC_MONTH] &
+ DA9063_COUNT_MONTH_MASK);
+ tm->tm_year = YEARS_FROM_DA9063(data[RTC_YEAR] &
+ DA9063_COUNT_YEAR_MASK);
+}
+
+static void da9063_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] &= ~DA9063_COUNT_SEC_MASK;
+ data[RTC_SEC] |= tm->tm_sec & DA9063_COUNT_SEC_MASK;
+
+ data[RTC_MIN] &= ~DA9063_COUNT_MIN_MASK;
+ data[RTC_MIN] |= tm->tm_min & DA9063_COUNT_MIN_MASK;
+
+ data[RTC_HOUR] &= ~DA9063_COUNT_HOUR_MASK;
+ data[RTC_HOUR] |= tm->tm_hour & DA9063_COUNT_HOUR_MASK;
+
+ data[RTC_DAY] &= ~DA9063_COUNT_DAY_MASK;
+ data[RTC_DAY] |= tm->tm_mday & DA9063_COUNT_DAY_MASK;
+
+ data[RTC_MONTH] &= ~DA9063_COUNT_MONTH_MASK;
+ data[RTC_MONTH] |= MONTHS_TO_DA9063(tm->tm_mon) &
+ DA9063_COUNT_MONTH_MASK;
+
+ data[RTC_YEAR] &= ~DA9063_COUNT_YEAR_MASK;
+ data[RTC_YEAR] |= YEARS_TO_DA9063(tm->tm_year) &
+ DA9063_COUNT_YEAR_MASK;
+}
+
+static int da9063_rtc_stop_alarm(struct device *dev)
+{
+ struct da9063_rtc *rtc = dev_get_drvdata(dev);
+
+ return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year,
+ DA9063_ALARM_ON, 0);
+}
+
+static int da9063_rtc_start_alarm(struct device *dev)
+{
+ struct da9063_rtc *rtc = dev_get_drvdata(dev);
+
+ return regmap_update_bits(rtc->hw->regmap, rtc->alarm_year,
+ DA9063_ALARM_ON, DA9063_ALARM_ON);
+}
+
+static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9063_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long tm_secs;
+ unsigned long al_secs;
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ ret = regmap_bulk_read(rtc->hw->regmap, DA9063_REG_COUNT_S,
+ data, RTC_DATA_LEN);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read RTC time data: %d\n", ret);
+ return ret;
+ }
+
+ if (!(data[RTC_SEC] & DA9063_RTC_READ)) {
+ dev_dbg(dev, "RTC not yet ready to be read by the host\n");
+ return -EINVAL;
+ }
+
+ da9063_data_to_tm(data, tm);
+
+ rtc_tm_to_time(tm, &tm_secs);
+ rtc_tm_to_time(&rtc->alarm_time, &al_secs);
+
+ /* handle the rtc synchronisation delay */
+ if (rtc->rtc_sync == true && al_secs - tm_secs == 1)
+ memcpy(tm, &rtc->alarm_time, sizeof(struct rtc_time));
+ else
+ rtc->rtc_sync = false;
+
+ return rtc_valid_tm(tm);
+}
+
+static int da9063_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9063_rtc *rtc = dev_get_drvdata(dev);
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ da9063_tm_to_data(tm, data);
+ ret = regmap_bulk_write(rtc->hw->regmap, DA9063_REG_COUNT_S,
+ data, RTC_DATA_LEN);
+ if (ret < 0)
+ dev_err(dev, "Failed to set RTC time data: %d\n", ret);
+
+ return ret;
+}
+
+static int da9063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct da9063_rtc *rtc = dev_get_drvdata(dev);
+ u8 data[RTC_DATA_LEN];
+ int ret;
+ unsigned int val;
+
+ data[RTC_SEC] = 0;
+ ret = regmap_bulk_read(rtc->hw->regmap, rtc->alarm_start,
+ &data[rtc->data_start], rtc->alarm_len);
+ if (ret < 0)
+ return ret;
+
+ da9063_data_to_tm(data, &alrm->time);
+
+ alrm->enabled = !!(data[RTC_YEAR] & DA9063_ALARM_ON);
+
+ ret = regmap_read(rtc->hw->regmap, DA9063_REG_EVENT_A, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & (DA9063_E_ALARM))
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+
+ return 0;
+}
+
+static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct da9063_rtc *rtc = dev_get_drvdata(dev);
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ da9063_tm_to_data(&alrm->time, data);
+
+ ret = da9063_rtc_stop_alarm(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(rtc->hw->regmap, rtc->alarm_start,
+ &data[rtc->data_start], rtc->alarm_len);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write alarm: %d\n", ret);
+ return ret;
+ }
+
+ da9063_data_to_tm(data, &rtc->alarm_time);
+
+ if (alrm->enabled) {
+ ret = da9063_rtc_start_alarm(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int da9063_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ if (enabled)
+ return da9063_rtc_start_alarm(dev);
+ else
+ return da9063_rtc_stop_alarm(dev);
+}
+
+static irqreturn_t da9063_alarm_event(int irq, void *data)
+{
+ struct da9063_rtc *rtc = data;
+
+ regmap_update_bits(rtc->hw->regmap, rtc->alarm_year,
+ DA9063_ALARM_ON, 0);
+
+ rtc->rtc_sync = true;
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops da9063_rtc_ops = {
+ .read_time = da9063_rtc_read_time,
+ .set_time = da9063_rtc_set_time,
+ .read_alarm = da9063_rtc_read_alarm,
+ .set_alarm = da9063_rtc_set_alarm,
+ .alarm_irq_enable = da9063_rtc_alarm_irq_enable,
+};
+
+static int da9063_rtc_probe(struct platform_device *pdev)
+{
+ struct da9063 *da9063 = dev_get_drvdata(pdev->dev.parent);
+ struct da9063_rtc *rtc;
+ int irq_alarm;
+ u8 data[RTC_DATA_LEN];
+ int ret;
+
+ ret = regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_E,
+ DA9063_RTC_EN, DA9063_RTC_EN);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to enable RTC\n");
+ goto err;
+ }
+
+ ret = regmap_update_bits(da9063->regmap, DA9063_REG_EN_32K,
+ DA9063_CRYSTAL, DA9063_CRYSTAL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to run 32kHz oscillator\n");
+ goto err;
+ }
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ if (da9063->variant_code == PMIC_DA9063_AD) {
+ rtc->alarm_year = DA9063_AD_REG_ALARM_Y;
+ rtc->alarm_start = DA9063_AD_REG_ALARM_MI;
+ rtc->alarm_len = RTC_ALARM_DATA_LEN;
+ rtc->data_start = RTC_MIN;
+ } else {
+ rtc->alarm_year = DA9063_BB_REG_ALARM_Y;
+ rtc->alarm_start = DA9063_BB_REG_ALARM_S;
+ rtc->alarm_len = RTC_DATA_LEN;
+ rtc->data_start = RTC_SEC;
+ }
+
+ ret = regmap_update_bits(da9063->regmap, rtc->alarm_start,
+ DA9063_ALARM_STATUS_TICK | DA9063_ALARM_STATUS_ALARM,
+ 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
+ goto err;
+ }
+
+ ret = regmap_update_bits(da9063->regmap, rtc->alarm_start,
+ DA9063_ALARM_STATUS_ALARM,
+ DA9063_ALARM_STATUS_ALARM);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to access RTC alarm register\n");
+ goto err;
+ }
+
+ ret = regmap_update_bits(da9063->regmap, rtc->alarm_year,
+ DA9063_TICK_ON, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to disable TICKs\n");
+ goto err;
+ }
+
+ data[RTC_SEC] = 0;
+ ret = regmap_bulk_read(da9063->regmap, rtc->alarm_start,
+ &data[rtc->data_start], rtc->alarm_len);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n",
+ ret);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ irq_alarm = platform_get_irq_byname(pdev, "ALARM");
+ ret = devm_request_threaded_irq(&pdev->dev, irq_alarm, NULL,
+ da9063_alarm_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "ALARM", rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request ALARM IRQ %d: %d\n",
+ irq_alarm, ret);
+ goto err;
+ }
+
+ rtc->hw = da9063;
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, DA9063_DRVNAME_RTC,
+ &da9063_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ da9063_data_to_tm(data, &rtc->alarm_time);
+ rtc->rtc_sync = false;
+err:
+ return ret;
+}
+
+static struct platform_driver da9063_rtc_driver = {
+ .probe = da9063_rtc_probe,
+ .driver = {
+ .name = DA9063_DRVNAME_RTC,
+ },
+};
+
+module_platform_driver(da9063_rtc_driver);
+
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC);
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
new file mode 100644
index 000000000..c84f46168
--- /dev/null
+++ b/drivers/rtc/rtc-davinci.c
@@ -0,0 +1,560 @@
+/*
+ * DaVinci Power Management and Real Time Clock Driver for TI platforms
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc
+ *
+ * Author: Miguel Aguilar <miguel.aguilar@ridgerun.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+/*
+ * The DaVinci RTC is a simple RTC with the following
+ * Sec: 0 - 59 : BCD count
+ * Min: 0 - 59 : BCD count
+ * Hour: 0 - 23 : BCD count
+ * Day: 0 - 0x7FFF(32767) : Binary count ( Over 89 years )
+ */
+
+/* PRTC interface registers */
+#define DAVINCI_PRTCIF_PID 0x00
+#define PRTCIF_CTLR 0x04
+#define PRTCIF_LDATA 0x08
+#define PRTCIF_UDATA 0x0C
+#define PRTCIF_INTEN 0x10
+#define PRTCIF_INTFLG 0x14
+
+/* PRTCIF_CTLR bit fields */
+#define PRTCIF_CTLR_BUSY BIT(31)
+#define PRTCIF_CTLR_SIZE BIT(25)
+#define PRTCIF_CTLR_DIR BIT(24)
+#define PRTCIF_CTLR_BENU_MSB BIT(23)
+#define PRTCIF_CTLR_BENU_3RD_BYTE BIT(22)
+#define PRTCIF_CTLR_BENU_2ND_BYTE BIT(21)
+#define PRTCIF_CTLR_BENU_LSB BIT(20)
+#define PRTCIF_CTLR_BENU_MASK (0x00F00000)
+#define PRTCIF_CTLR_BENL_MSB BIT(19)
+#define PRTCIF_CTLR_BENL_3RD_BYTE BIT(18)
+#define PRTCIF_CTLR_BENL_2ND_BYTE BIT(17)
+#define PRTCIF_CTLR_BENL_LSB BIT(16)
+#define PRTCIF_CTLR_BENL_MASK (0x000F0000)
+
+/* PRTCIF_INTEN bit fields */
+#define PRTCIF_INTEN_RTCSS BIT(1)
+#define PRTCIF_INTEN_RTCIF BIT(0)
+#define PRTCIF_INTEN_MASK (PRTCIF_INTEN_RTCSS \
+ | PRTCIF_INTEN_RTCIF)
+
+/* PRTCIF_INTFLG bit fields */
+#define PRTCIF_INTFLG_RTCSS BIT(1)
+#define PRTCIF_INTFLG_RTCIF BIT(0)
+#define PRTCIF_INTFLG_MASK (PRTCIF_INTFLG_RTCSS \
+ | PRTCIF_INTFLG_RTCIF)
+
+/* PRTC subsystem registers */
+#define PRTCSS_RTC_INTC_EXTENA1 (0x0C)
+#define PRTCSS_RTC_CTRL (0x10)
+#define PRTCSS_RTC_WDT (0x11)
+#define PRTCSS_RTC_TMR0 (0x12)
+#define PRTCSS_RTC_TMR1 (0x13)
+#define PRTCSS_RTC_CCTRL (0x14)
+#define PRTCSS_RTC_SEC (0x15)
+#define PRTCSS_RTC_MIN (0x16)
+#define PRTCSS_RTC_HOUR (0x17)
+#define PRTCSS_RTC_DAY0 (0x18)
+#define PRTCSS_RTC_DAY1 (0x19)
+#define PRTCSS_RTC_AMIN (0x1A)
+#define PRTCSS_RTC_AHOUR (0x1B)
+#define PRTCSS_RTC_ADAY0 (0x1C)
+#define PRTCSS_RTC_ADAY1 (0x1D)
+#define PRTCSS_RTC_CLKC_CNT (0x20)
+
+/* PRTCSS_RTC_INTC_EXTENA1 */
+#define PRTCSS_RTC_INTC_EXTENA1_MASK (0x07)
+
+/* PRTCSS_RTC_CTRL bit fields */
+#define PRTCSS_RTC_CTRL_WDTBUS BIT(7)
+#define PRTCSS_RTC_CTRL_WEN BIT(6)
+#define PRTCSS_RTC_CTRL_WDRT BIT(5)
+#define PRTCSS_RTC_CTRL_WDTFLG BIT(4)
+#define PRTCSS_RTC_CTRL_TE BIT(3)
+#define PRTCSS_RTC_CTRL_TIEN BIT(2)
+#define PRTCSS_RTC_CTRL_TMRFLG BIT(1)
+#define PRTCSS_RTC_CTRL_TMMD BIT(0)
+
+/* PRTCSS_RTC_CCTRL bit fields */
+#define PRTCSS_RTC_CCTRL_CALBUSY BIT(7)
+#define PRTCSS_RTC_CCTRL_DAEN BIT(5)
+#define PRTCSS_RTC_CCTRL_HAEN BIT(4)
+#define PRTCSS_RTC_CCTRL_MAEN BIT(3)
+#define PRTCSS_RTC_CCTRL_ALMFLG BIT(2)
+#define PRTCSS_RTC_CCTRL_AIEN BIT(1)
+#define PRTCSS_RTC_CCTRL_CAEN BIT(0)
+
+static DEFINE_SPINLOCK(davinci_rtc_lock);
+
+struct davinci_rtc {
+ struct rtc_device *rtc;
+ void __iomem *base;
+ int irq;
+};
+
+static inline void rtcif_write(struct davinci_rtc *davinci_rtc,
+ u32 val, u32 addr)
+{
+ writel(val, davinci_rtc->base + addr);
+}
+
+static inline u32 rtcif_read(struct davinci_rtc *davinci_rtc, u32 addr)
+{
+ return readl(davinci_rtc->base + addr);
+}
+
+static inline void rtcif_wait(struct davinci_rtc *davinci_rtc)
+{
+ while (rtcif_read(davinci_rtc, PRTCIF_CTLR) & PRTCIF_CTLR_BUSY)
+ cpu_relax();
+}
+
+static inline void rtcss_write(struct davinci_rtc *davinci_rtc,
+ unsigned long val, u8 addr)
+{
+ rtcif_wait(davinci_rtc);
+
+ rtcif_write(davinci_rtc, PRTCIF_CTLR_BENL_LSB | addr, PRTCIF_CTLR);
+ rtcif_write(davinci_rtc, val, PRTCIF_LDATA);
+
+ rtcif_wait(davinci_rtc);
+}
+
+static inline u8 rtcss_read(struct davinci_rtc *davinci_rtc, u8 addr)
+{
+ rtcif_wait(davinci_rtc);
+
+ rtcif_write(davinci_rtc, PRTCIF_CTLR_DIR | PRTCIF_CTLR_BENL_LSB | addr,
+ PRTCIF_CTLR);
+
+ rtcif_wait(davinci_rtc);
+
+ return rtcif_read(davinci_rtc, PRTCIF_LDATA);
+}
+
+static inline void davinci_rtcss_calendar_wait(struct davinci_rtc *davinci_rtc)
+{
+ while (rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) &
+ PRTCSS_RTC_CCTRL_CALBUSY)
+ cpu_relax();
+}
+
+static irqreturn_t davinci_rtc_interrupt(int irq, void *class_dev)
+{
+ struct davinci_rtc *davinci_rtc = class_dev;
+ unsigned long events = 0;
+ u32 irq_flg;
+ u8 alm_irq, tmr_irq;
+ u8 rtc_ctrl, rtc_cctrl;
+ int ret = IRQ_NONE;
+
+ irq_flg = rtcif_read(davinci_rtc, PRTCIF_INTFLG) &
+ PRTCIF_INTFLG_RTCSS;
+
+ alm_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL) &
+ PRTCSS_RTC_CCTRL_ALMFLG;
+
+ tmr_irq = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL) &
+ PRTCSS_RTC_CTRL_TMRFLG;
+
+ if (irq_flg) {
+ if (alm_irq) {
+ events |= RTC_IRQF | RTC_AF;
+ rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
+ rtc_cctrl |= PRTCSS_RTC_CCTRL_ALMFLG;
+ rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
+ } else if (tmr_irq) {
+ events |= RTC_IRQF | RTC_PF;
+ rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
+ rtc_ctrl |= PRTCSS_RTC_CTRL_TMRFLG;
+ rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
+ }
+
+ rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS,
+ PRTCIF_INTFLG);
+ rtc_update_irq(davinci_rtc->rtc, 1, events);
+
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+static int
+davinci_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u8 rtc_ctrl;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ rtc_ctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CTRL);
+
+ switch (cmd) {
+ case RTC_WIE_ON:
+ rtc_ctrl |= PRTCSS_RTC_CTRL_WEN | PRTCSS_RTC_CTRL_WDTFLG;
+ break;
+ case RTC_WIE_OFF:
+ rtc_ctrl &= ~PRTCSS_RTC_CTRL_WEN;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ }
+
+ rtcss_write(davinci_rtc, rtc_ctrl, PRTCSS_RTC_CTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return ret;
+}
+
+static int convertfromdays(u16 days, struct rtc_time *tm)
+{
+ int tmp_days, year, mon;
+
+ for (year = 2000;; year++) {
+ tmp_days = rtc_year_days(1, 12, year);
+ if (days >= tmp_days)
+ days -= tmp_days;
+ else {
+ for (mon = 0;; mon++) {
+ tmp_days = rtc_month_days(mon, year);
+ if (days >= tmp_days) {
+ days -= tmp_days;
+ } else {
+ tm->tm_year = year - 1900;
+ tm->tm_mon = mon;
+ tm->tm_mday = days + 1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static int convert2days(u16 *days, struct rtc_time *tm)
+{
+ int i;
+ *days = 0;
+
+ /* epoch == 1900 */
+ if (tm->tm_year < 100 || tm->tm_year > 199)
+ return -EINVAL;
+
+ for (i = 2000; i < 1900 + tm->tm_year; i++)
+ *days += rtc_year_days(1, 12, i);
+
+ *days += rtc_year_days(tm->tm_mday, tm->tm_mon, 1900 + tm->tm_year);
+
+ return 0;
+}
+
+static int davinci_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u16 days = 0;
+ u8 day0, day1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ tm->tm_sec = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_SEC));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ tm->tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_MIN));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ tm->tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_HOUR));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_DAY1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ days |= day1;
+ days <<= 8;
+ days |= day0;
+
+ if (convertfromdays(days, tm) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int davinci_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u16 days;
+ u8 rtc_cctrl;
+ unsigned long flags;
+
+ if (convert2days(&days, tm) < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(tm->tm_sec), PRTCSS_RTC_SEC);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(tm->tm_min), PRTCSS_RTC_MIN);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(tm->tm_hour), PRTCSS_RTC_HOUR);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_DAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_DAY1);
+
+ rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
+ rtc_cctrl |= PRTCSS_RTC_CCTRL_CAEN;
+ rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static int davinci_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+ u8 rtc_cctrl = rtcss_read(davinci_rtc, PRTCSS_RTC_CCTRL);
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ if (enabled)
+ rtc_cctrl |= PRTCSS_RTC_CCTRL_DAEN |
+ PRTCSS_RTC_CCTRL_HAEN |
+ PRTCSS_RTC_CCTRL_MAEN |
+ PRTCSS_RTC_CCTRL_ALMFLG |
+ PRTCSS_RTC_CCTRL_AIEN;
+ else
+ rtc_cctrl &= ~PRTCSS_RTC_CCTRL_AIEN;
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, rtc_cctrl, PRTCSS_RTC_CCTRL);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static int davinci_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ u16 days = 0;
+ u8 day0, day1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ alm->time.tm_min = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AMIN));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ alm->time.tm_hour = bcd2bin(rtcss_read(davinci_rtc, PRTCSS_RTC_AHOUR));
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day0 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ day1 = rtcss_read(davinci_rtc, PRTCSS_RTC_ADAY1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+ days |= day1;
+ days <<= 8;
+ days |= day0;
+
+ if (convertfromdays(days, &alm->time) < 0)
+ return -EINVAL;
+
+ alm->pending = !!(rtcss_read(davinci_rtc,
+ PRTCSS_RTC_CCTRL) &
+ PRTCSS_RTC_CCTRL_AIEN);
+ alm->enabled = alm->pending && device_may_wakeup(dev);
+
+ return 0;
+}
+
+static int davinci_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct davinci_rtc *davinci_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+ u16 days;
+
+ if (alm->time.tm_mday <= 0 && alm->time.tm_mon < 0
+ && alm->time.tm_year < 0) {
+ struct rtc_time tm;
+ unsigned long now, then;
+
+ davinci_rtc_read_time(dev, &tm);
+ rtc_tm_to_time(&tm, &now);
+
+ alm->time.tm_mday = tm.tm_mday;
+ alm->time.tm_mon = tm.tm_mon;
+ alm->time.tm_year = tm.tm_year;
+ rtc_tm_to_time(&alm->time, &then);
+
+ if (then < now) {
+ rtc_time_to_tm(now + 24 * 60 * 60, &tm);
+ alm->time.tm_mday = tm.tm_mday;
+ alm->time.tm_mon = tm.tm_mon;
+ alm->time.tm_year = tm.tm_year;
+ }
+ }
+
+ if (convert2days(&days, &alm->time) < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&davinci_rtc_lock, flags);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_min), PRTCSS_RTC_AMIN);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, bin2bcd(alm->time.tm_hour), PRTCSS_RTC_AHOUR);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, days & 0xFF, PRTCSS_RTC_ADAY0);
+
+ davinci_rtcss_calendar_wait(davinci_rtc);
+ rtcss_write(davinci_rtc, (days & 0xFF00) >> 8, PRTCSS_RTC_ADAY1);
+
+ spin_unlock_irqrestore(&davinci_rtc_lock, flags);
+
+ return 0;
+}
+
+static struct rtc_class_ops davinci_rtc_ops = {
+ .ioctl = davinci_rtc_ioctl,
+ .read_time = davinci_rtc_read_time,
+ .set_time = davinci_rtc_set_time,
+ .alarm_irq_enable = davinci_rtc_alarm_irq_enable,
+ .read_alarm = davinci_rtc_read_alarm,
+ .set_alarm = davinci_rtc_set_alarm,
+};
+
+static int __init davinci_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct davinci_rtc *davinci_rtc;
+ struct resource *res;
+ int ret = 0;
+
+ davinci_rtc = devm_kzalloc(&pdev->dev, sizeof(struct davinci_rtc), GFP_KERNEL);
+ if (!davinci_rtc)
+ return -ENOMEM;
+
+ davinci_rtc->irq = platform_get_irq(pdev, 0);
+ if (davinci_rtc->irq < 0) {
+ dev_err(dev, "no RTC irq\n");
+ return davinci_rtc->irq;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ davinci_rtc->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(davinci_rtc->base))
+ return PTR_ERR(davinci_rtc->base);
+
+ platform_set_drvdata(pdev, davinci_rtc);
+
+ davinci_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &davinci_rtc_ops, THIS_MODULE);
+ if (IS_ERR(davinci_rtc->rtc)) {
+ dev_err(dev, "unable to register RTC device, err %d\n",
+ ret);
+ return PTR_ERR(davinci_rtc->rtc);
+ }
+
+ rtcif_write(davinci_rtc, PRTCIF_INTFLG_RTCSS, PRTCIF_INTFLG);
+ rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
+ rtcss_write(davinci_rtc, 0, PRTCSS_RTC_INTC_EXTENA1);
+
+ rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CTRL);
+ rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL);
+
+ ret = devm_request_irq(dev, davinci_rtc->irq, davinci_rtc_interrupt,
+ 0, "davinci_rtc", davinci_rtc);
+ if (ret < 0) {
+ dev_err(dev, "unable to register davinci RTC interrupt\n");
+ return ret;
+ }
+
+ /* Enable interrupts */
+ rtcif_write(davinci_rtc, PRTCIF_INTEN_RTCSS, PRTCIF_INTEN);
+ rtcss_write(davinci_rtc, PRTCSS_RTC_INTC_EXTENA1_MASK,
+ PRTCSS_RTC_INTC_EXTENA1);
+
+ rtcss_write(davinci_rtc, PRTCSS_RTC_CCTRL_CAEN, PRTCSS_RTC_CCTRL);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+static int __exit davinci_rtc_remove(struct platform_device *pdev)
+{
+ struct davinci_rtc *davinci_rtc = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ rtcif_write(davinci_rtc, 0, PRTCIF_INTEN);
+
+ return 0;
+}
+
+static struct platform_driver davinci_rtc_driver = {
+ .probe = davinci_rtc_probe,
+ .remove = __exit_p(davinci_rtc_remove),
+ .driver = {
+ .name = "rtc_davinci",
+ },
+};
+
+module_platform_driver_probe(davinci_rtc_driver, davinci_rtc_probe);
+
+MODULE_AUTHOR("Miguel Aguilar <miguel.aguilar@ridgerun.com>");
+MODULE_DESCRIPTION("Texas Instruments DaVinci PRTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
new file mode 100644
index 000000000..799c34bcb
--- /dev/null
+++ b/drivers/rtc/rtc-dev.c
@@ -0,0 +1,511 @@
+/*
+ * RTC subsystem, dev interface
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/sched.h>
+#include "rtc-core.h"
+
+static dev_t rtc_devt;
+
+#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */
+
+static int rtc_dev_open(struct inode *inode, struct file *file)
+{
+ int err;
+ struct rtc_device *rtc = container_of(inode->i_cdev,
+ struct rtc_device, char_dev);
+ const struct rtc_class_ops *ops = rtc->ops;
+
+ if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags))
+ return -EBUSY;
+
+ file->private_data = rtc;
+
+ err = ops->open ? ops->open(rtc->dev.parent) : 0;
+ if (err == 0) {
+ spin_lock_irq(&rtc->irq_lock);
+ rtc->irq_data = 0;
+ spin_unlock_irq(&rtc->irq_lock);
+
+ return 0;
+ }
+
+ /* something has gone wrong */
+ clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
+ return err;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+/*
+ * Routine to poll RTC seconds field for change as often as possible,
+ * after first RTC_UIE use timer to reduce polling
+ */
+static void rtc_uie_task(struct work_struct *work)
+{
+ struct rtc_device *rtc =
+ container_of(work, struct rtc_device, uie_task);
+ struct rtc_time tm;
+ int num = 0;
+ int err;
+
+ err = rtc_read_time(rtc, &tm);
+
+ spin_lock_irq(&rtc->irq_lock);
+ if (rtc->stop_uie_polling || err) {
+ rtc->uie_task_active = 0;
+ } else if (rtc->oldsecs != tm.tm_sec) {
+ num = (tm.tm_sec + 60 - rtc->oldsecs) % 60;
+ rtc->oldsecs = tm.tm_sec;
+ rtc->uie_timer.expires = jiffies + HZ - (HZ/10);
+ rtc->uie_timer_active = 1;
+ rtc->uie_task_active = 0;
+ add_timer(&rtc->uie_timer);
+ } else if (schedule_work(&rtc->uie_task) == 0) {
+ rtc->uie_task_active = 0;
+ }
+ spin_unlock_irq(&rtc->irq_lock);
+ if (num)
+ rtc_handle_legacy_irq(rtc, num, RTC_UF);
+}
+static void rtc_uie_timer(unsigned long data)
+{
+ struct rtc_device *rtc = (struct rtc_device *)data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc->irq_lock, flags);
+ rtc->uie_timer_active = 0;
+ rtc->uie_task_active = 1;
+ if ((schedule_work(&rtc->uie_task) == 0))
+ rtc->uie_task_active = 0;
+ spin_unlock_irqrestore(&rtc->irq_lock, flags);
+}
+
+static int clear_uie(struct rtc_device *rtc)
+{
+ spin_lock_irq(&rtc->irq_lock);
+ if (rtc->uie_irq_active) {
+ rtc->stop_uie_polling = 1;
+ if (rtc->uie_timer_active) {
+ spin_unlock_irq(&rtc->irq_lock);
+ del_timer_sync(&rtc->uie_timer);
+ spin_lock_irq(&rtc->irq_lock);
+ rtc->uie_timer_active = 0;
+ }
+ if (rtc->uie_task_active) {
+ spin_unlock_irq(&rtc->irq_lock);
+ flush_scheduled_work();
+ spin_lock_irq(&rtc->irq_lock);
+ }
+ rtc->uie_irq_active = 0;
+ }
+ spin_unlock_irq(&rtc->irq_lock);
+ return 0;
+}
+
+static int set_uie(struct rtc_device *rtc)
+{
+ struct rtc_time tm;
+ int err;
+
+ err = rtc_read_time(rtc, &tm);
+ if (err)
+ return err;
+ spin_lock_irq(&rtc->irq_lock);
+ if (!rtc->uie_irq_active) {
+ rtc->uie_irq_active = 1;
+ rtc->stop_uie_polling = 0;
+ rtc->oldsecs = tm.tm_sec;
+ rtc->uie_task_active = 1;
+ if (schedule_work(&rtc->uie_task) == 0)
+ rtc->uie_task_active = 0;
+ }
+ rtc->irq_data = 0;
+ spin_unlock_irq(&rtc->irq_lock);
+ return 0;
+}
+
+int rtc_dev_update_irq_enable_emul(struct rtc_device *rtc, unsigned int enabled)
+{
+ if (enabled)
+ return set_uie(rtc);
+ else
+ return clear_uie(rtc);
+}
+EXPORT_SYMBOL(rtc_dev_update_irq_enable_emul);
+
+#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */
+
+static ssize_t
+rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct rtc_device *rtc = file->private_data;
+
+ DECLARE_WAITQUEUE(wait, current);
+ unsigned long data;
+ ssize_t ret;
+
+ if (count != sizeof(unsigned int) && count < sizeof(unsigned long))
+ return -EINVAL;
+
+ add_wait_queue(&rtc->irq_queue, &wait);
+ do {
+ __set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_irq(&rtc->irq_lock);
+ data = rtc->irq_data;
+ rtc->irq_data = 0;
+ spin_unlock_irq(&rtc->irq_lock);
+
+ if (data != 0) {
+ ret = 0;
+ break;
+ }
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ } while (1);
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rtc->irq_queue, &wait);
+
+ if (ret == 0) {
+ /* Check for any data updates */
+ if (rtc->ops->read_callback)
+ data = rtc->ops->read_callback(rtc->dev.parent,
+ data);
+
+ if (sizeof(int) != sizeof(long) &&
+ count == sizeof(unsigned int))
+ ret = put_user(data, (unsigned int __user *)buf) ?:
+ sizeof(unsigned int);
+ else
+ ret = put_user(data, (unsigned long __user *)buf) ?:
+ sizeof(unsigned long);
+ }
+ return ret;
+}
+
+static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
+{
+ struct rtc_device *rtc = file->private_data;
+ unsigned long data;
+
+ poll_wait(file, &rtc->irq_queue, wait);
+
+ data = rtc->irq_data;
+
+ return (data != 0) ? (POLLIN | POLLRDNORM) : 0;
+}
+
+static long rtc_dev_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err = 0;
+ struct rtc_device *rtc = file->private_data;
+ const struct rtc_class_ops *ops = rtc->ops;
+ struct rtc_time tm;
+ struct rtc_wkalrm alarm;
+ void __user *uarg = (void __user *) arg;
+
+ err = mutex_lock_interruptible(&rtc->ops_lock);
+ if (err)
+ return err;
+
+ /* check that the calling task has appropriate permissions
+ * for certain ioctls. doing this check here is useful
+ * to avoid duplicate code in each driver.
+ */
+ switch (cmd) {
+ case RTC_EPOCH_SET:
+ case RTC_SET_TIME:
+ if (!capable(CAP_SYS_TIME))
+ err = -EACCES;
+ break;
+
+ case RTC_IRQP_SET:
+ if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
+ err = -EACCES;
+ break;
+
+ case RTC_PIE_ON:
+ if (rtc->irq_freq > rtc->max_user_freq &&
+ !capable(CAP_SYS_RESOURCE))
+ err = -EACCES;
+ break;
+ }
+
+ if (err)
+ goto done;
+
+ /*
+ * Drivers *SHOULD NOT* provide ioctl implementations
+ * for these requests. Instead, provide methods to
+ * support the following code, so that the RTC's main
+ * features are accessible without using ioctls.
+ *
+ * RTC and alarm times will be in UTC, by preference,
+ * but dual-booting with MS-Windows implies RTCs must
+ * use the local wall clock time.
+ */
+
+ switch (cmd) {
+ case RTC_ALM_READ:
+ mutex_unlock(&rtc->ops_lock);
+
+ err = rtc_read_alarm(rtc, &alarm);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
+ err = -EFAULT;
+ return err;
+
+ case RTC_ALM_SET:
+ mutex_unlock(&rtc->ops_lock);
+
+ if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
+ return -EFAULT;
+
+ alarm.enabled = 0;
+ alarm.pending = 0;
+ alarm.time.tm_wday = -1;
+ alarm.time.tm_yday = -1;
+ alarm.time.tm_isdst = -1;
+
+ /* RTC_ALM_SET alarms may be up to 24 hours in the future.
+ * Rather than expecting every RTC to implement "don't care"
+ * for day/month/year fields, just force the alarm to have
+ * the right values for those fields.
+ *
+ * RTC_WKALM_SET should be used instead. Not only does it
+ * eliminate the need for a separate RTC_AIE_ON call, it
+ * doesn't have the "alarm 23:59:59 in the future" race.
+ *
+ * NOTE: some legacy code may have used invalid fields as
+ * wildcards, exposing hardware "periodic alarm" capabilities.
+ * Not supported here.
+ */
+ {
+ time64_t now, then;
+
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0)
+ return err;
+ now = rtc_tm_to_time64(&tm);
+
+ alarm.time.tm_mday = tm.tm_mday;
+ alarm.time.tm_mon = tm.tm_mon;
+ alarm.time.tm_year = tm.tm_year;
+ err = rtc_valid_tm(&alarm.time);
+ if (err < 0)
+ return err;
+ then = rtc_tm_to_time64(&alarm.time);
+
+ /* alarm may need to wrap into tomorrow */
+ if (then < now) {
+ rtc_time64_to_tm(now + 24 * 60 * 60, &tm);
+ alarm.time.tm_mday = tm.tm_mday;
+ alarm.time.tm_mon = tm.tm_mon;
+ alarm.time.tm_year = tm.tm_year;
+ }
+ }
+
+ return rtc_set_alarm(rtc, &alarm);
+
+ case RTC_RD_TIME:
+ mutex_unlock(&rtc->ops_lock);
+
+ err = rtc_read_time(rtc, &tm);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(uarg, &tm, sizeof(tm)))
+ err = -EFAULT;
+ return err;
+
+ case RTC_SET_TIME:
+ mutex_unlock(&rtc->ops_lock);
+
+ if (copy_from_user(&tm, uarg, sizeof(tm)))
+ return -EFAULT;
+
+ return rtc_set_time(rtc, &tm);
+
+ case RTC_PIE_ON:
+ err = rtc_irq_set_state(rtc, NULL, 1);
+ break;
+
+ case RTC_PIE_OFF:
+ err = rtc_irq_set_state(rtc, NULL, 0);
+ break;
+
+ case RTC_AIE_ON:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_alarm_irq_enable(rtc, 1);
+
+ case RTC_AIE_OFF:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_alarm_irq_enable(rtc, 0);
+
+ case RTC_UIE_ON:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_update_irq_enable(rtc, 1);
+
+ case RTC_UIE_OFF:
+ mutex_unlock(&rtc->ops_lock);
+ return rtc_update_irq_enable(rtc, 0);
+
+ case RTC_IRQP_SET:
+ err = rtc_irq_set_freq(rtc, NULL, arg);
+ break;
+
+ case RTC_IRQP_READ:
+ err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
+ break;
+
+ case RTC_WKALM_SET:
+ mutex_unlock(&rtc->ops_lock);
+ if (copy_from_user(&alarm, uarg, sizeof(alarm)))
+ return -EFAULT;
+
+ return rtc_set_alarm(rtc, &alarm);
+
+ case RTC_WKALM_RD:
+ mutex_unlock(&rtc->ops_lock);
+ err = rtc_read_alarm(rtc, &alarm);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(uarg, &alarm, sizeof(alarm)))
+ err = -EFAULT;
+ return err;
+
+ default:
+ /* Finally try the driver's ioctl interface */
+ if (ops->ioctl) {
+ err = ops->ioctl(rtc->dev.parent, cmd, arg);
+ if (err == -ENOIOCTLCMD)
+ err = -ENOTTY;
+ } else
+ err = -ENOTTY;
+ break;
+ }
+
+done:
+ mutex_unlock(&rtc->ops_lock);
+ return err;
+}
+
+static int rtc_dev_fasync(int fd, struct file *file, int on)
+{
+ struct rtc_device *rtc = file->private_data;
+ return fasync_helper(fd, file, on, &rtc->async_queue);
+}
+
+static int rtc_dev_release(struct inode *inode, struct file *file)
+{
+ struct rtc_device *rtc = file->private_data;
+
+ /* We shut down the repeating IRQs that userspace enabled,
+ * since nothing is listening to them.
+ * - Update (UIE) ... currently only managed through ioctls
+ * - Periodic (PIE) ... also used through rtc_*() interface calls
+ *
+ * Leave the alarm alone; it may be set to trigger a system wakeup
+ * later, or be used by kernel code, and is a one-shot event anyway.
+ */
+
+ /* Keep ioctl until all drivers are converted */
+ rtc_dev_ioctl(file, RTC_UIE_OFF, 0);
+ rtc_update_irq_enable(rtc, 0);
+ rtc_irq_set_state(rtc, NULL, 0);
+
+ if (rtc->ops->release)
+ rtc->ops->release(rtc->dev.parent);
+
+ clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
+ return 0;
+}
+
+static const struct file_operations rtc_dev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = rtc_dev_read,
+ .poll = rtc_dev_poll,
+ .unlocked_ioctl = rtc_dev_ioctl,
+ .open = rtc_dev_open,
+ .release = rtc_dev_release,
+ .fasync = rtc_dev_fasync,
+};
+
+/* insertion/removal hooks */
+
+void rtc_dev_prepare(struct rtc_device *rtc)
+{
+ if (!rtc_devt)
+ return;
+
+ if (rtc->id >= RTC_DEV_MAX) {
+ dev_dbg(&rtc->dev, "%s: too many RTC devices\n", rtc->name);
+ return;
+ }
+
+ rtc->dev.devt = MKDEV(MAJOR(rtc_devt), rtc->id);
+
+#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
+ INIT_WORK(&rtc->uie_task, rtc_uie_task);
+ setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
+#endif
+
+ cdev_init(&rtc->char_dev, &rtc_dev_fops);
+ rtc->char_dev.owner = rtc->owner;
+}
+
+void rtc_dev_add_device(struct rtc_device *rtc)
+{
+ if (cdev_add(&rtc->char_dev, rtc->dev.devt, 1))
+ dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
+ rtc->name, MAJOR(rtc_devt), rtc->id);
+ else
+ dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
+ MAJOR(rtc_devt), rtc->id);
+}
+
+void rtc_dev_del_device(struct rtc_device *rtc)
+{
+ if (rtc->dev.devt)
+ cdev_del(&rtc->char_dev);
+}
+
+void __init rtc_dev_init(void)
+{
+ int err;
+
+ err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
+ if (err < 0)
+ pr_err("failed to allocate char dev region\n");
+}
+
+void __exit rtc_dev_exit(void)
+{
+ if (rtc_devt)
+ unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
+}
diff --git a/drivers/rtc/rtc-digicolor.c b/drivers/rtc/rtc-digicolor.c
new file mode 100644
index 000000000..8d05596a6
--- /dev/null
+++ b/drivers/rtc/rtc-digicolor.c
@@ -0,0 +1,227 @@
+/*
+ * Real Time Clock driver for Conexant Digicolor
+ *
+ * Copyright (C) 2015 Paradox Innovation Ltd.
+ *
+ * Author: Baruch Siach <baruch@tkos.co.il>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+
+#define DC_RTC_CONTROL 0x0
+#define DC_RTC_TIME 0x8
+#define DC_RTC_REFERENCE 0xc
+#define DC_RTC_ALARM 0x10
+#define DC_RTC_INTFLAG_CLEAR 0x14
+#define DC_RTC_INTENABLE 0x16
+
+#define DC_RTC_CMD_MASK 0xf
+#define DC_RTC_GO_BUSY BIT(7)
+
+#define CMD_NOP 0
+#define CMD_RESET 1
+#define CMD_WRITE 3
+#define CMD_READ 4
+
+#define CMD_DELAY_US (10*1000)
+#define CMD_TIMEOUT_US (500*CMD_DELAY_US)
+
+struct dc_rtc {
+ struct rtc_device *rtc_dev;
+ void __iomem *regs;
+};
+
+static int dc_rtc_cmds(struct dc_rtc *rtc, const u8 *cmds, int len)
+{
+ u8 val;
+ int i, ret;
+
+ for (i = 0; i < len; i++) {
+ writeb_relaxed((cmds[i] & DC_RTC_CMD_MASK) | DC_RTC_GO_BUSY,
+ rtc->regs + DC_RTC_CONTROL);
+ ret = readb_relaxed_poll_timeout(
+ rtc->regs + DC_RTC_CONTROL, val,
+ !(val & DC_RTC_GO_BUSY), CMD_DELAY_US, CMD_TIMEOUT_US);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dc_rtc_read(struct dc_rtc *rtc, unsigned long *val)
+{
+ static const u8 read_cmds[] = {CMD_READ, CMD_NOP};
+ u32 reference, time1, time2;
+ int ret;
+
+ ret = dc_rtc_cmds(rtc, read_cmds, ARRAY_SIZE(read_cmds));
+ if (ret < 0)
+ return ret;
+
+ reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE);
+ time1 = readl_relaxed(rtc->regs + DC_RTC_TIME);
+ /* Read twice to ensure consistency */
+ while (1) {
+ time2 = readl_relaxed(rtc->regs + DC_RTC_TIME);
+ if (time1 == time2)
+ break;
+ time1 = time2;
+ }
+
+ *val = reference + time1;
+ return 0;
+}
+
+static int dc_rtc_write(struct dc_rtc *rtc, u32 val)
+{
+ static const u8 write_cmds[] = {CMD_WRITE, CMD_NOP, CMD_RESET, CMD_NOP};
+
+ writel_relaxed(val, rtc->regs + DC_RTC_REFERENCE);
+ return dc_rtc_cmds(rtc, write_cmds, ARRAY_SIZE(write_cmds));
+}
+
+static int dc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct dc_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long now;
+ int ret;
+
+ ret = dc_rtc_read(rtc, &now);
+ if (ret < 0)
+ return ret;
+ rtc_time64_to_tm(now, tm);
+
+ return 0;
+}
+
+static int dc_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct dc_rtc *rtc = dev_get_drvdata(dev);
+
+ return dc_rtc_write(rtc, secs);
+}
+
+static int dc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct dc_rtc *rtc = dev_get_drvdata(dev);
+ u32 alarm_reg, reference;
+ unsigned long now;
+ int ret;
+
+ alarm_reg = readl_relaxed(rtc->regs + DC_RTC_ALARM);
+ reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE);
+ rtc_time64_to_tm(reference + alarm_reg, &alarm->time);
+
+ ret = dc_rtc_read(rtc, &now);
+ if (ret < 0)
+ return ret;
+
+ alarm->pending = alarm_reg + reference > now;
+ alarm->enabled = readl_relaxed(rtc->regs + DC_RTC_INTENABLE);
+
+ return 0;
+}
+
+static int dc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct dc_rtc *rtc = dev_get_drvdata(dev);
+ time64_t alarm_time;
+ u32 reference;
+
+ alarm_time = rtc_tm_to_time64(&alarm->time);
+
+ reference = readl_relaxed(rtc->regs + DC_RTC_REFERENCE);
+ writel_relaxed(alarm_time - reference, rtc->regs + DC_RTC_ALARM);
+
+ writeb_relaxed(!!alarm->enabled, rtc->regs + DC_RTC_INTENABLE);
+
+ return 0;
+}
+
+static int dc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct dc_rtc *rtc = dev_get_drvdata(dev);
+
+ writeb_relaxed(!!enabled, rtc->regs + DC_RTC_INTENABLE);
+
+ return 0;
+}
+
+static struct rtc_class_ops dc_rtc_ops = {
+ .read_time = dc_rtc_read_time,
+ .set_mmss = dc_rtc_set_mmss,
+ .read_alarm = dc_rtc_read_alarm,
+ .set_alarm = dc_rtc_set_alarm,
+ .alarm_irq_enable = dc_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t dc_rtc_irq(int irq, void *dev_id)
+{
+ struct dc_rtc *rtc = dev_id;
+
+ writeb_relaxed(1, rtc->regs + DC_RTC_INTFLAG_CLEAR);
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+
+ return IRQ_HANDLED;
+}
+
+static int __init dc_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct dc_rtc *rtc;
+ int irq, ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->regs))
+ return PTR_ERR(rtc->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+ ret = devm_request_irq(&pdev->dev, irq, dc_rtc_irq, 0, pdev->name, rtc);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &dc_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ return 0;
+}
+
+static const struct of_device_id dc_dt_ids[] = {
+ { .compatible = "cnxt,cx92755-rtc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dc_dt_ids);
+
+static struct platform_driver dc_rtc_driver = {
+ .driver = {
+ .name = "digicolor_rtc",
+ .of_match_table = of_match_ptr(dc_dt_ids),
+ },
+};
+module_platform_driver_probe(dc_rtc_driver, dc_rtc_probe);
+
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("Conexant Digicolor Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-dm355evm.c b/drivers/rtc/rtc-dm355evm.c
new file mode 100644
index 000000000..94067f8ee
--- /dev/null
+++ b/drivers/rtc/rtc-dm355evm.c
@@ -0,0 +1,155 @@
+/*
+ * rtc-dm355evm.c - access battery-backed counter in MSP430 firmware
+ *
+ * Copyright (c) 2008 by David Brownell
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <linux/i2c/dm355evm_msp.h>
+#include <linux/module.h>
+
+
+/*
+ * The MSP430 firmware on the DM355 EVM uses a watch crystal to feed
+ * a 1 Hz counter. When a backup battery is supplied, that makes a
+ * reasonable RTC for applications where alarms and non-NTP drift
+ * compensation aren't important.
+ *
+ * The only real glitch is the inability to read or write all four
+ * counter bytes atomically: the count may increment in the middle
+ * of an operation, causing trouble when the LSB rolls over.
+ *
+ * This driver was tested with firmware revision A4.
+ */
+union evm_time {
+ u8 bytes[4];
+ u32 value;
+};
+
+static int dm355evm_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ union evm_time time;
+ int status;
+ int tries = 0;
+
+ do {
+ /*
+ * Read LSB(0) to MSB(3) bytes. Defend against the counter
+ * rolling over by re-reading until the value is stable,
+ * and assuming the four reads take at most a few seconds.
+ */
+ status = dm355evm_msp_read(DM355EVM_MSP_RTC_0);
+ if (status < 0)
+ return status;
+ if (tries && time.bytes[0] == status)
+ break;
+ time.bytes[0] = status;
+
+ status = dm355evm_msp_read(DM355EVM_MSP_RTC_1);
+ if (status < 0)
+ return status;
+ if (tries && time.bytes[1] == status)
+ break;
+ time.bytes[1] = status;
+
+ status = dm355evm_msp_read(DM355EVM_MSP_RTC_2);
+ if (status < 0)
+ return status;
+ if (tries && time.bytes[2] == status)
+ break;
+ time.bytes[2] = status;
+
+ status = dm355evm_msp_read(DM355EVM_MSP_RTC_3);
+ if (status < 0)
+ return status;
+ if (tries && time.bytes[3] == status)
+ break;
+ time.bytes[3] = status;
+
+ } while (++tries < 5);
+
+ dev_dbg(dev, "read timestamp %08x\n", time.value);
+
+ rtc_time_to_tm(le32_to_cpu(time.value), tm);
+ return 0;
+}
+
+static int dm355evm_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ union evm_time time;
+ unsigned long value;
+ int status;
+
+ rtc_tm_to_time(tm, &value);
+ time.value = cpu_to_le32(value);
+
+ dev_dbg(dev, "write timestamp %08x\n", time.value);
+
+ /*
+ * REVISIT handle non-atomic writes ... maybe just retry until
+ * byte[1] sticks (no rollover)?
+ */
+ status = dm355evm_msp_write(time.bytes[0], DM355EVM_MSP_RTC_0);
+ if (status < 0)
+ return status;
+
+ status = dm355evm_msp_write(time.bytes[1], DM355EVM_MSP_RTC_1);
+ if (status < 0)
+ return status;
+
+ status = dm355evm_msp_write(time.bytes[2], DM355EVM_MSP_RTC_2);
+ if (status < 0)
+ return status;
+
+ status = dm355evm_msp_write(time.bytes[3], DM355EVM_MSP_RTC_3);
+ if (status < 0)
+ return status;
+
+ return 0;
+}
+
+static struct rtc_class_ops dm355evm_rtc_ops = {
+ .read_time = dm355evm_rtc_read_time,
+ .set_time = dm355evm_rtc_set_time,
+};
+
+/*----------------------------------------------------------------------*/
+
+static int dm355evm_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &dm355evm_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+ PTR_ERR(rtc));
+ return PTR_ERR(rtc);
+ }
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+}
+
+/*
+ * I2C is used to talk to the MSP430, but this platform device is
+ * exposed by an MFD driver that manages I2C communications.
+ */
+static struct platform_driver rtc_dm355evm_driver = {
+ .probe = dm355evm_rtc_probe,
+ .driver = {
+ .name = "rtc-dm355evm",
+ },
+};
+
+module_platform_driver(rtc_dm355evm_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1216.c b/drivers/rtc/rtc-ds1216.c
new file mode 100644
index 000000000..d16f55089
--- /dev/null
+++ b/drivers/rtc/rtc-ds1216.c
@@ -0,0 +1,182 @@
+/*
+ * Dallas DS1216 RTC driver
+ *
+ * Copyright (c) 2007 Thomas Bogendoerfer
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#define DRV_VERSION "0.2"
+
+struct ds1216_regs {
+ u8 tsec;
+ u8 sec;
+ u8 min;
+ u8 hour;
+ u8 wday;
+ u8 mday;
+ u8 month;
+ u8 year;
+};
+
+#define DS1216_HOUR_1224 (1 << 7)
+#define DS1216_HOUR_AMPM (1 << 5)
+
+struct ds1216_priv {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+};
+
+static const u8 magic[] = {
+ 0xc5, 0x3a, 0xa3, 0x5c, 0xc5, 0x3a, 0xa3, 0x5c
+};
+
+/*
+ * Read the 64 bit we'd like to have - It a series
+ * of 64 bits showing up in the LSB of the base register.
+ *
+ */
+static void ds1216_read(u8 __iomem *ioaddr, u8 *buf)
+{
+ unsigned char c;
+ int i, j;
+
+ for (i = 0; i < 8; i++) {
+ c = 0;
+ for (j = 0; j < 8; j++)
+ c |= (readb(ioaddr) & 0x1) << j;
+ buf[i] = c;
+ }
+}
+
+static void ds1216_write(u8 __iomem *ioaddr, const u8 *buf)
+{
+ unsigned char c;
+ int i, j;
+
+ for (i = 0; i < 8; i++) {
+ c = buf[i];
+ for (j = 0; j < 8; j++) {
+ writeb(c, ioaddr);
+ c = c >> 1;
+ }
+ }
+}
+
+static void ds1216_switch_ds_to_clock(u8 __iomem *ioaddr)
+{
+ /* Reset magic pointer */
+ readb(ioaddr);
+ /* Write 64 bit magic to DS1216 */
+ ds1216_write(ioaddr, magic);
+}
+
+static int ds1216_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1216_priv *priv = platform_get_drvdata(pdev);
+ struct ds1216_regs regs;
+
+ ds1216_switch_ds_to_clock(priv->ioaddr);
+ ds1216_read(priv->ioaddr, (u8 *)&regs);
+
+ tm->tm_sec = bcd2bin(regs.sec);
+ tm->tm_min = bcd2bin(regs.min);
+ if (regs.hour & DS1216_HOUR_1224) {
+ /* AM/PM mode */
+ tm->tm_hour = bcd2bin(regs.hour & 0x1f);
+ if (regs.hour & DS1216_HOUR_AMPM)
+ tm->tm_hour += 12;
+ } else
+ tm->tm_hour = bcd2bin(regs.hour & 0x3f);
+ tm->tm_wday = (regs.wday & 7) - 1;
+ tm->tm_mday = bcd2bin(regs.mday & 0x3f);
+ tm->tm_mon = bcd2bin(regs.month & 0x1f);
+ tm->tm_year = bcd2bin(regs.year);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+
+ return rtc_valid_tm(tm);
+}
+
+static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1216_priv *priv = platform_get_drvdata(pdev);
+ struct ds1216_regs regs;
+
+ ds1216_switch_ds_to_clock(priv->ioaddr);
+ ds1216_read(priv->ioaddr, (u8 *)&regs);
+
+ regs.tsec = 0; /* clear 0.1 and 0.01 seconds */
+ regs.sec = bin2bcd(tm->tm_sec);
+ regs.min = bin2bcd(tm->tm_min);
+ regs.hour &= DS1216_HOUR_1224;
+ if (regs.hour && tm->tm_hour > 12) {
+ regs.hour |= DS1216_HOUR_AMPM;
+ tm->tm_hour -= 12;
+ }
+ regs.hour |= bin2bcd(tm->tm_hour);
+ regs.wday &= ~7;
+ regs.wday |= tm->tm_wday;
+ regs.mday = bin2bcd(tm->tm_mday);
+ regs.month = bin2bcd(tm->tm_mon);
+ regs.year = bin2bcd(tm->tm_year % 100);
+
+ ds1216_switch_ds_to_clock(priv->ioaddr);
+ ds1216_write(priv->ioaddr, (u8 *)&regs);
+ return 0;
+}
+
+static const struct rtc_class_ops ds1216_rtc_ops = {
+ .read_time = ds1216_rtc_read_time,
+ .set_time = ds1216_rtc_set_time,
+};
+
+static int __init ds1216_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct ds1216_priv *priv;
+ u8 dummy[8];
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->ioaddr))
+ return PTR_ERR(priv->ioaddr);
+
+ priv->rtc = devm_rtc_device_register(&pdev->dev, "ds1216",
+ &ds1216_rtc_ops, THIS_MODULE);
+ if (IS_ERR(priv->rtc))
+ return PTR_ERR(priv->rtc);
+
+ /* dummy read to get clock into a known state */
+ ds1216_read(priv->ioaddr, dummy);
+ return 0;
+}
+
+static struct platform_driver ds1216_rtc_platform_driver = {
+ .driver = {
+ .name = "rtc-ds1216",
+ },
+};
+
+module_platform_driver_probe(ds1216_rtc_platform_driver, ds1216_rtc_probe);
+
+MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
+MODULE_DESCRIPTION("DS1216 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:rtc-ds1216");
diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c
new file mode 100644
index 000000000..2fe537f4e
--- /dev/null
+++ b/drivers/rtc/rtc-ds1286.c
@@ -0,0 +1,369 @@
+/*
+ * DS1286 Real Time Clock interface for Linux
+ *
+ * Copyright (C) 1998, 1999, 2000 Ralf Baechle
+ * Copyright (C) 2008 Thomas Bogendoerfer
+ *
+ * Based on code written by Paul Gortmaker.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/bcd.h>
+#include <linux/ds1286.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define DRV_VERSION "1.0"
+
+struct ds1286_priv {
+ struct rtc_device *rtc;
+ u32 __iomem *rtcregs;
+ spinlock_t lock;
+};
+
+static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg)
+{
+ return __raw_readl(&priv->rtcregs[reg]) & 0xff;
+}
+
+static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg)
+{
+ __raw_writel(data, &priv->rtcregs[reg]);
+}
+
+
+static int ds1286_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+ unsigned char val;
+
+ /* Allow or mask alarm interrupts */
+ spin_lock_irqsave(&priv->lock, flags);
+ val = ds1286_rtc_read(priv, RTC_CMD);
+ if (enabled)
+ val &= ~RTC_TDM;
+ else
+ val |= RTC_TDM;
+ ds1286_rtc_write(priv, val, RTC_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return 0;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+
+static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+ unsigned char val;
+
+ switch (cmd) {
+ case RTC_WIE_OFF:
+ /* Mask watchdog int. enab. bit */
+ spin_lock_irqsave(&priv->lock, flags);
+ val = ds1286_rtc_read(priv, RTC_CMD);
+ val |= RTC_WAM;
+ ds1286_rtc_write(priv, val, RTC_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ case RTC_WIE_ON:
+ /* Allow watchdog interrupts. */
+ spin_lock_irqsave(&priv->lock, flags);
+ val = ds1286_rtc_read(priv, RTC_CMD);
+ val &= ~RTC_WAM;
+ ds1286_rtc_write(priv, val, RTC_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+#else
+#define ds1286_ioctl NULL
+#endif
+
+#ifdef CONFIG_PROC_FS
+
+static int ds1286_proc(struct device *dev, struct seq_file *seq)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned char month, cmd, amode;
+ const char *s;
+
+ month = ds1286_rtc_read(priv, RTC_MONTH);
+ seq_printf(seq,
+ "oscillator\t: %s\n"
+ "square_wave\t: %s\n",
+ (month & RTC_EOSC) ? "disabled" : "enabled",
+ (month & RTC_ESQW) ? "disabled" : "enabled");
+
+ amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) |
+ ((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) |
+ ((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7);
+ switch (amode) {
+ case 7:
+ s = "each minute";
+ break;
+ case 3:
+ s = "minutes match";
+ break;
+ case 1:
+ s = "hours and minutes match";
+ break;
+ case 0:
+ s = "days, hours and minutes match";
+ break;
+ default:
+ s = "invalid";
+ break;
+ }
+ seq_printf(seq, "alarm_mode\t: %s\n", s);
+
+ cmd = ds1286_rtc_read(priv, RTC_CMD);
+ seq_printf(seq,
+ "alarm_enable\t: %s\n"
+ "wdog_alarm\t: %s\n"
+ "alarm_mask\t: %s\n"
+ "wdog_alarm_mask\t: %s\n"
+ "interrupt_mode\t: %s\n"
+ "INTB_mode\t: %s_active\n"
+ "interrupt_pins\t: %s\n",
+ (cmd & RTC_TDF) ? "yes" : "no",
+ (cmd & RTC_WAF) ? "yes" : "no",
+ (cmd & RTC_TDM) ? "disabled" : "enabled",
+ (cmd & RTC_WAM) ? "disabled" : "enabled",
+ (cmd & RTC_PU_LVL) ? "pulse" : "level",
+ (cmd & RTC_IBH_LO) ? "low" : "high",
+ (cmd & RTC_IPSW) ? "unswapped" : "swapped");
+ return 0;
+}
+
+#else
+#define ds1286_proc NULL
+#endif
+
+static int ds1286_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned char save_control;
+ unsigned long flags;
+ unsigned long uip_watchdog = jiffies;
+
+ /*
+ * read RTC once any update in progress is done. The update
+ * can take just over 2ms. We wait 10 to 20ms. There is no need to
+ * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
+ * If you need to know *exactly* when a second has started, enable
+ * periodic update complete interrupts, (via ioctl) and then
+ * immediately read /dev/rtc which will block until you get the IRQ.
+ * Once the read clears, read the RTC time (again via ioctl). Easy.
+ */
+
+ if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE)
+ while (time_before(jiffies, uip_watchdog + 2*HZ/100))
+ barrier();
+
+ /*
+ * Only the values that we read from the RTC are set. We leave
+ * tm_wday, tm_yday and tm_isdst untouched. Even though the
+ * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
+ * by the RTC when initially set to a non-zero value.
+ */
+ spin_lock_irqsave(&priv->lock, flags);
+ save_control = ds1286_rtc_read(priv, RTC_CMD);
+ ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD);
+
+ tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS);
+ tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES);
+ tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f;
+ tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE);
+ tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f;
+ tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR);
+
+ ds1286_rtc_write(priv, save_control, RTC_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ tm->tm_sec = bcd2bin(tm->tm_sec);
+ tm->tm_min = bcd2bin(tm->tm_min);
+ tm->tm_hour = bcd2bin(tm->tm_hour);
+ tm->tm_mday = bcd2bin(tm->tm_mday);
+ tm->tm_mon = bcd2bin(tm->tm_mon);
+ tm->tm_year = bcd2bin(tm->tm_year);
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ if (tm->tm_year < 45)
+ tm->tm_year += 30;
+ tm->tm_year += 40;
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+
+ tm->tm_mon--;
+
+ return rtc_valid_tm(tm);
+}
+
+static int ds1286_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned char mon, day, hrs, min, sec;
+ unsigned char save_control;
+ unsigned int yrs;
+ unsigned long flags;
+
+ yrs = tm->tm_year + 1900;
+ mon = tm->tm_mon + 1; /* tm_mon starts at zero */
+ day = tm->tm_mday;
+ hrs = tm->tm_hour;
+ min = tm->tm_min;
+ sec = tm->tm_sec;
+
+ if (yrs < 1970)
+ return -EINVAL;
+
+ yrs -= 1940;
+ if (yrs > 255) /* They are unsigned */
+ return -EINVAL;
+
+ if (yrs >= 100)
+ yrs -= 100;
+
+ sec = bin2bcd(sec);
+ min = bin2bcd(min);
+ hrs = bin2bcd(hrs);
+ day = bin2bcd(day);
+ mon = bin2bcd(mon);
+ yrs = bin2bcd(yrs);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ save_control = ds1286_rtc_read(priv, RTC_CMD);
+ ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD);
+
+ ds1286_rtc_write(priv, yrs, RTC_YEAR);
+ ds1286_rtc_write(priv, mon, RTC_MONTH);
+ ds1286_rtc_write(priv, day, RTC_DATE);
+ ds1286_rtc_write(priv, hrs, RTC_HOURS);
+ ds1286_rtc_write(priv, min, RTC_MINUTES);
+ ds1286_rtc_write(priv, sec, RTC_SECONDS);
+ ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND);
+
+ ds1286_rtc_write(priv, save_control, RTC_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return 0;
+}
+
+static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ /*
+ * Only the values that we read from the RTC are set. That
+ * means only tm_wday, tm_hour, tm_min.
+ */
+ spin_lock_irqsave(&priv->lock, flags);
+ alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f;
+ alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x1f;
+ alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x07;
+ ds1286_rtc_read(priv, RTC_CMD);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ alm->time.tm_min = bcd2bin(alm->time.tm_min);
+ alm->time.tm_hour = bcd2bin(alm->time.tm_hour);
+ alm->time.tm_sec = 0;
+ return 0;
+}
+
+static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct ds1286_priv *priv = dev_get_drvdata(dev);
+ unsigned char hrs, min, sec;
+
+ hrs = alm->time.tm_hour;
+ min = alm->time.tm_min;
+ sec = alm->time.tm_sec;
+
+ if (hrs >= 24)
+ hrs = 0xff;
+
+ if (min >= 60)
+ min = 0xff;
+
+ if (sec != 0)
+ return -EINVAL;
+
+ min = bin2bcd(min);
+ hrs = bin2bcd(hrs);
+
+ spin_lock(&priv->lock);
+ ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM);
+ ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM);
+ spin_unlock(&priv->lock);
+
+ return 0;
+}
+
+static const struct rtc_class_ops ds1286_ops = {
+ .ioctl = ds1286_ioctl,
+ .proc = ds1286_proc,
+ .read_time = ds1286_read_time,
+ .set_time = ds1286_set_time,
+ .read_alarm = ds1286_read_alarm,
+ .set_alarm = ds1286_set_alarm,
+ .alarm_irq_enable = ds1286_alarm_irq_enable,
+};
+
+static int ds1286_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ struct ds1286_priv *priv;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ds1286_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->rtcregs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->rtcregs))
+ return PTR_ERR(priv->rtcregs);
+
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+ rtc = devm_rtc_device_register(&pdev->dev, "ds1286", &ds1286_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+ priv->rtc = rtc;
+ return 0;
+}
+
+static struct platform_driver ds1286_platform_driver = {
+ .driver = {
+ .name = "rtc-ds1286",
+ },
+ .probe = ds1286_probe,
+};
+
+module_platform_driver(ds1286_platform_driver);
+
+MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
+MODULE_DESCRIPTION("DS1286 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:rtc-ds1286");
diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c
new file mode 100644
index 000000000..6bef7a523
--- /dev/null
+++ b/drivers/rtc/rtc-ds1302.c
@@ -0,0 +1,255 @@
+/*
+ * Dallas DS1302 RTC Support
+ *
+ * Copyright (C) 2002 David McCullough
+ * Copyright (C) 2003 - 2007 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2. See the file "COPYING" in the main directory of
+ * this archive for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+
+#define DRV_NAME "rtc-ds1302"
+#define DRV_VERSION "0.1.1"
+
+#define RTC_CMD_READ 0x81 /* Read command */
+#define RTC_CMD_WRITE 0x80 /* Write command */
+
+#define RTC_CMD_WRITE_ENABLE 0x00 /* Write enable */
+#define RTC_CMD_WRITE_DISABLE 0x80 /* Write disable */
+
+#define RTC_ADDR_RAM0 0x20 /* Address of RAM0 */
+#define RTC_ADDR_TCR 0x08 /* Address of trickle charge register */
+#define RTC_ADDR_CTRL 0x07 /* Address of control register */
+#define RTC_ADDR_YEAR 0x06 /* Address of year register */
+#define RTC_ADDR_DAY 0x05 /* Address of day of week register */
+#define RTC_ADDR_MON 0x04 /* Address of month register */
+#define RTC_ADDR_DATE 0x03 /* Address of day of month register */
+#define RTC_ADDR_HOUR 0x02 /* Address of hour register */
+#define RTC_ADDR_MIN 0x01 /* Address of minute register */
+#define RTC_ADDR_SEC 0x00 /* Address of second register */
+
+#ifdef CONFIG_SH_SECUREEDGE5410
+#include <asm/rtc.h>
+#include <mach/secureedge5410.h>
+
+#define RTC_RESET 0x1000
+#define RTC_IODATA 0x0800
+#define RTC_SCLK 0x0400
+
+#define set_dp(x) SECUREEDGE_WRITE_IOPORT(x, 0x1c00)
+#define get_dp() SECUREEDGE_READ_IOPORT()
+#define ds1302_set_tx()
+#define ds1302_set_rx()
+
+static inline int ds1302_hw_init(void)
+{
+ return 0;
+}
+
+static inline void ds1302_reset(void)
+{
+ set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK));
+}
+
+static inline void ds1302_clock(void)
+{
+ set_dp(get_dp() | RTC_SCLK); /* clock high */
+ set_dp(get_dp() & ~RTC_SCLK); /* clock low */
+}
+
+static inline void ds1302_start(void)
+{
+ set_dp(get_dp() | RTC_RESET);
+}
+
+static inline void ds1302_stop(void)
+{
+ set_dp(get_dp() & ~RTC_RESET);
+}
+
+static inline void ds1302_txbit(int bit)
+{
+ set_dp((get_dp() & ~RTC_IODATA) | (bit ? RTC_IODATA : 0));
+}
+
+static inline int ds1302_rxbit(void)
+{
+ return !!(get_dp() & RTC_IODATA);
+}
+
+#else
+#error "Add support for your platform"
+#endif
+
+static void ds1302_sendbits(unsigned int val)
+{
+ int i;
+
+ ds1302_set_tx();
+
+ for (i = 8; (i); i--, val >>= 1) {
+ ds1302_txbit(val & 0x1);
+ ds1302_clock();
+ }
+}
+
+static unsigned int ds1302_recvbits(void)
+{
+ unsigned int val;
+ int i;
+
+ ds1302_set_rx();
+
+ for (i = 0, val = 0; (i < 8); i++) {
+ val |= (ds1302_rxbit() << i);
+ ds1302_clock();
+ }
+
+ return val;
+}
+
+static unsigned int ds1302_readbyte(unsigned int addr)
+{
+ unsigned int val;
+
+ ds1302_reset();
+
+ ds1302_start();
+ ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_READ);
+ val = ds1302_recvbits();
+ ds1302_stop();
+
+ return val;
+}
+
+static void ds1302_writebyte(unsigned int addr, unsigned int val)
+{
+ ds1302_reset();
+
+ ds1302_start();
+ ds1302_sendbits(((addr & 0x3f) << 1) | RTC_CMD_WRITE);
+ ds1302_sendbits(val);
+ ds1302_stop();
+}
+
+static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(ds1302_readbyte(RTC_ADDR_SEC));
+ tm->tm_min = bcd2bin(ds1302_readbyte(RTC_ADDR_MIN));
+ tm->tm_hour = bcd2bin(ds1302_readbyte(RTC_ADDR_HOUR));
+ tm->tm_wday = bcd2bin(ds1302_readbyte(RTC_ADDR_DAY));
+ tm->tm_mday = bcd2bin(ds1302_readbyte(RTC_ADDR_DATE));
+ tm->tm_mon = bcd2bin(ds1302_readbyte(RTC_ADDR_MON)) - 1;
+ tm->tm_year = bcd2bin(ds1302_readbyte(RTC_ADDR_YEAR));
+
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+
+ dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
+
+ return rtc_valid_tm(tm);
+}
+
+static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ ds1302_writebyte(RTC_ADDR_CTRL, RTC_CMD_WRITE_ENABLE);
+ /* Stop RTC */
+ ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80);
+
+ ds1302_writebyte(RTC_ADDR_SEC, bin2bcd(tm->tm_sec));
+ ds1302_writebyte(RTC_ADDR_MIN, bin2bcd(tm->tm_min));
+ ds1302_writebyte(RTC_ADDR_HOUR, bin2bcd(tm->tm_hour));
+ ds1302_writebyte(RTC_ADDR_DAY, bin2bcd(tm->tm_wday));
+ ds1302_writebyte(RTC_ADDR_DATE, bin2bcd(tm->tm_mday));
+ ds1302_writebyte(RTC_ADDR_MON, bin2bcd(tm->tm_mon + 1));
+ ds1302_writebyte(RTC_ADDR_YEAR, bin2bcd(tm->tm_year % 100));
+
+ /* Start RTC */
+ ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80);
+
+ ds1302_writebyte(RTC_ADDR_CTRL, RTC_CMD_WRITE_DISABLE);
+
+ return 0;
+}
+
+static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+#ifdef RTC_SET_CHARGE
+ case RTC_SET_CHARGE:
+ {
+ int tcs_val;
+
+ if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int)))
+ return -EFAULT;
+
+ ds1302_writebyte(RTC_ADDR_TCR, (0xa0 | tcs_val * 0xf));
+ return 0;
+ }
+#endif
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static struct rtc_class_ops ds1302_rtc_ops = {
+ .read_time = ds1302_rtc_read_time,
+ .set_time = ds1302_rtc_set_time,
+ .ioctl = ds1302_rtc_ioctl,
+};
+
+static int __init ds1302_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ if (ds1302_hw_init()) {
+ dev_err(&pdev->dev, "Failed to init communication channel");
+ return -EINVAL;
+ }
+
+ /* Reset */
+ ds1302_reset();
+
+ /* Write a magic value to the DS1302 RAM, and see if it sticks. */
+ ds1302_writebyte(RTC_ADDR_RAM0, 0x42);
+ if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42) {
+ dev_err(&pdev->dev, "Failed to probe");
+ return -ENODEV;
+ }
+
+ rtc = devm_rtc_device_register(&pdev->dev, "ds1302",
+ &ds1302_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver ds1302_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+};
+
+module_platform_driver_probe(ds1302_platform_driver, ds1302_rtc_probe);
+
+MODULE_DESCRIPTION("Dallas DS1302 RTC driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("Paul Mundt, David McCullough");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
new file mode 100644
index 000000000..12b07158a
--- /dev/null
+++ b/drivers/rtc/rtc-ds1305.c
@@ -0,0 +1,803 @@
+/*
+ * rtc-ds1305.c -- driver for DS1305 and DS1306 SPI RTC chips
+ *
+ * Copyright (C) 2008 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/ds1305.h>
+#include <linux/module.h>
+
+
+/*
+ * Registers ... mask DS1305_WRITE into register address to write,
+ * otherwise you're reading it. All non-bitmask values are BCD.
+ */
+#define DS1305_WRITE 0x80
+
+
+/* RTC date/time ... the main special cases are that we:
+ * - Need fancy "hours" encoding in 12hour mode
+ * - Don't rely on the "day-of-week" field (or tm_wday)
+ * - Are a 21st-century clock (2000 <= year < 2100)
+ */
+#define DS1305_RTC_LEN 7 /* bytes for RTC regs */
+
+#define DS1305_SEC 0x00 /* register addresses */
+#define DS1305_MIN 0x01
+#define DS1305_HOUR 0x02
+# define DS1305_HR_12 0x40 /* set == 12 hr mode */
+# define DS1305_HR_PM 0x20 /* set == PM (12hr mode) */
+#define DS1305_WDAY 0x03
+#define DS1305_MDAY 0x04
+#define DS1305_MON 0x05
+#define DS1305_YEAR 0x06
+
+
+/* The two alarms have only sec/min/hour/wday fields (ALM_LEN).
+ * DS1305_ALM_DISABLE disables a match field (some combos are bad).
+ *
+ * NOTE that since we don't use WDAY, we limit ourselves to alarms
+ * only one day into the future (vs potentially up to a week).
+ *
+ * NOTE ALSO that while we could generate once-a-second IRQs (UIE), we
+ * don't currently support them. We'd either need to do it only when
+ * no alarm is pending (not the standard model), or to use the second
+ * alarm (implying that this is a DS1305 not DS1306, *and* that either
+ * it's wired up a second IRQ we know, or that INTCN is set)
+ */
+#define DS1305_ALM_LEN 4 /* bytes for ALM regs */
+#define DS1305_ALM_DISABLE 0x80
+
+#define DS1305_ALM0(r) (0x07 + (r)) /* register addresses */
+#define DS1305_ALM1(r) (0x0b + (r))
+
+
+/* three control registers */
+#define DS1305_CONTROL_LEN 3 /* bytes of control regs */
+
+#define DS1305_CONTROL 0x0f /* register addresses */
+# define DS1305_nEOSC 0x80 /* low enables oscillator */
+# define DS1305_WP 0x40 /* write protect */
+# define DS1305_INTCN 0x04 /* clear == only int0 used */
+# define DS1306_1HZ 0x04 /* enable 1Hz output */
+# define DS1305_AEI1 0x02 /* enable ALM1 IRQ */
+# define DS1305_AEI0 0x01 /* enable ALM0 IRQ */
+#define DS1305_STATUS 0x10
+/* status has just AEIx bits, mirrored as IRQFx */
+#define DS1305_TRICKLE 0x11
+/* trickle bits are defined in <linux/spi/ds1305.h> */
+
+/* a bunch of NVRAM */
+#define DS1305_NVRAM_LEN 96 /* bytes of NVRAM */
+
+#define DS1305_NVRAM 0x20 /* register addresses */
+
+
+struct ds1305 {
+ struct spi_device *spi;
+ struct rtc_device *rtc;
+
+ struct work_struct work;
+
+ unsigned long flags;
+#define FLAG_EXITING 0
+
+ bool hr12;
+ u8 ctrl[DS1305_CONTROL_LEN];
+};
+
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Utilities ... tolerate 12-hour AM/PM notation in case of non-Linux
+ * software (like a bootloader) which may require it.
+ */
+
+static unsigned bcd2hour(u8 bcd)
+{
+ if (bcd & DS1305_HR_12) {
+ unsigned hour = 0;
+
+ bcd &= ~DS1305_HR_12;
+ if (bcd & DS1305_HR_PM) {
+ hour = 12;
+ bcd &= ~DS1305_HR_PM;
+ }
+ hour += bcd2bin(bcd);
+ return hour - 1;
+ }
+ return bcd2bin(bcd);
+}
+
+static u8 hour2bcd(bool hr12, int hour)
+{
+ if (hr12) {
+ hour++;
+ if (hour <= 12)
+ return DS1305_HR_12 | bin2bcd(hour);
+ hour -= 12;
+ return DS1305_HR_12 | DS1305_HR_PM | bin2bcd(hour);
+ }
+ return bin2bcd(hour);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Interface to RTC framework
+ */
+
+static int ds1305_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ u8 buf[2];
+ long err = -EINVAL;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+
+ if (enabled) {
+ if (ds1305->ctrl[0] & DS1305_AEI0)
+ goto done;
+ buf[1] |= DS1305_AEI0;
+ } else {
+ if (!(buf[1] & DS1305_AEI0))
+ goto done;
+ buf[1] &= ~DS1305_AEI0;
+ }
+ err = spi_write_then_read(ds1305->spi, buf, sizeof(buf), NULL, 0);
+ if (err >= 0)
+ ds1305->ctrl[0] = buf[1];
+done:
+ return err;
+
+}
+
+
+/*
+ * Get/set of date and time is pretty normal.
+ */
+
+static int ds1305_get_time(struct device *dev, struct rtc_time *time)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ u8 addr = DS1305_SEC;
+ u8 buf[DS1305_RTC_LEN];
+ int status;
+
+ /* Use write-then-read to get all the date/time registers
+ * since dma from stack is nonportable
+ */
+ status = spi_write_then_read(ds1305->spi, &addr, sizeof(addr),
+ buf, sizeof(buf));
+ if (status < 0)
+ return status;
+
+ dev_vdbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n",
+ "read", buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6]);
+
+ /* Decode the registers */
+ time->tm_sec = bcd2bin(buf[DS1305_SEC]);
+ time->tm_min = bcd2bin(buf[DS1305_MIN]);
+ time->tm_hour = bcd2hour(buf[DS1305_HOUR]);
+ time->tm_wday = buf[DS1305_WDAY] - 1;
+ time->tm_mday = bcd2bin(buf[DS1305_MDAY]);
+ time->tm_mon = bcd2bin(buf[DS1305_MON]) - 1;
+ time->tm_year = bcd2bin(buf[DS1305_YEAR]) + 100;
+
+ dev_vdbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", time->tm_sec, time->tm_min,
+ time->tm_hour, time->tm_mday,
+ time->tm_mon, time->tm_year, time->tm_wday);
+
+ /* Time may not be set */
+ return rtc_valid_tm(time);
+}
+
+static int ds1305_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ u8 buf[1 + DS1305_RTC_LEN];
+ u8 *bp = buf;
+
+ dev_vdbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", time->tm_sec, time->tm_min,
+ time->tm_hour, time->tm_mday,
+ time->tm_mon, time->tm_year, time->tm_wday);
+
+ /* Write registers starting at the first time/date address. */
+ *bp++ = DS1305_WRITE | DS1305_SEC;
+
+ *bp++ = bin2bcd(time->tm_sec);
+ *bp++ = bin2bcd(time->tm_min);
+ *bp++ = hour2bcd(ds1305->hr12, time->tm_hour);
+ *bp++ = (time->tm_wday < 7) ? (time->tm_wday + 1) : 1;
+ *bp++ = bin2bcd(time->tm_mday);
+ *bp++ = bin2bcd(time->tm_mon + 1);
+ *bp++ = bin2bcd(time->tm_year - 100);
+
+ dev_dbg(dev, "%s: %02x %02x %02x, %02x %02x %02x %02x\n",
+ "write", buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ /* use write-then-read since dma from stack is nonportable */
+ return spi_write_then_read(ds1305->spi, buf, sizeof(buf),
+ NULL, 0);
+}
+
+/*
+ * Get/set of alarm is a bit funky:
+ *
+ * - First there's the inherent raciness of getting the (partitioned)
+ * status of an alarm that could trigger while we're reading parts
+ * of that status.
+ *
+ * - Second there's its limited range (we could increase it a bit by
+ * relying on WDAY), which means it will easily roll over.
+ *
+ * - Third there's the choice of two alarms and alarm signals.
+ * Here we use ALM0 and expect that nINT0 (open drain) is used;
+ * that's the only real option for DS1306 runtime alarms, and is
+ * natural on DS1305.
+ *
+ * - Fourth, there's also ALM1, and a second interrupt signal:
+ * + On DS1305 ALM1 uses nINT1 (when INTCN=1) else nINT0;
+ * + On DS1306 ALM1 only uses INT1 (an active high pulse)
+ * and it won't work when VCC1 is active.
+ *
+ * So to be most general, we should probably set both alarms to the
+ * same value, letting ALM1 be the wakeup event source on DS1306
+ * and handling several wiring options on DS1305.
+ *
+ * - Fifth, we support the polled mode (as well as possible; why not?)
+ * even when no interrupt line is wired to an IRQ.
+ */
+
+/*
+ * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl)
+ */
+static int ds1305_get_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ struct spi_device *spi = ds1305->spi;
+ u8 addr;
+ int status;
+ u8 buf[DS1305_ALM_LEN];
+
+ /* Refresh control register cache BEFORE reading ALM0 registers,
+ * since reading alarm registers acks any pending IRQ. That
+ * makes returning "pending" status a bit of a lie, but that bit
+ * of EFI status is at best fragile anyway (given IRQ handlers).
+ */
+ addr = DS1305_CONTROL;
+ status = spi_write_then_read(spi, &addr, sizeof(addr),
+ ds1305->ctrl, sizeof(ds1305->ctrl));
+ if (status < 0)
+ return status;
+
+ alm->enabled = !!(ds1305->ctrl[0] & DS1305_AEI0);
+ alm->pending = !!(ds1305->ctrl[1] & DS1305_AEI0);
+
+ /* get and check ALM0 registers */
+ addr = DS1305_ALM0(DS1305_SEC);
+ status = spi_write_then_read(spi, &addr, sizeof(addr),
+ buf, sizeof(buf));
+ if (status < 0)
+ return status;
+
+ dev_vdbg(dev, "%s: %02x %02x %02x %02x\n",
+ "alm0 read", buf[DS1305_SEC], buf[DS1305_MIN],
+ buf[DS1305_HOUR], buf[DS1305_WDAY]);
+
+ if ((DS1305_ALM_DISABLE & buf[DS1305_SEC])
+ || (DS1305_ALM_DISABLE & buf[DS1305_MIN])
+ || (DS1305_ALM_DISABLE & buf[DS1305_HOUR]))
+ return -EIO;
+
+ /* Stuff these values into alm->time and let RTC framework code
+ * fill in the rest ... and also handle rollover to tomorrow when
+ * that's needed.
+ */
+ alm->time.tm_sec = bcd2bin(buf[DS1305_SEC]);
+ alm->time.tm_min = bcd2bin(buf[DS1305_MIN]);
+ alm->time.tm_hour = bcd2hour(buf[DS1305_HOUR]);
+ alm->time.tm_mday = -1;
+ alm->time.tm_mon = -1;
+ alm->time.tm_year = -1;
+ /* next three fields are unused by Linux */
+ alm->time.tm_wday = -1;
+ alm->time.tm_mday = -1;
+ alm->time.tm_isdst = -1;
+
+ return 0;
+}
+
+/*
+ * Context: caller holds rtc->ops_lock (to protect ds1305->ctrl)
+ */
+static int ds1305_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ struct spi_device *spi = ds1305->spi;
+ unsigned long now, later;
+ struct rtc_time tm;
+ int status;
+ u8 buf[1 + DS1305_ALM_LEN];
+
+ /* convert desired alarm to time_t */
+ status = rtc_tm_to_time(&alm->time, &later);
+ if (status < 0)
+ return status;
+
+ /* Read current time as time_t */
+ status = ds1305_get_time(dev, &tm);
+ if (status < 0)
+ return status;
+ status = rtc_tm_to_time(&tm, &now);
+ if (status < 0)
+ return status;
+
+ /* make sure alarm fires within the next 24 hours */
+ if (later <= now)
+ return -EINVAL;
+ if ((later - now) > 24 * 60 * 60)
+ return -EDOM;
+
+ /* disable alarm if needed */
+ if (ds1305->ctrl[0] & DS1305_AEI0) {
+ ds1305->ctrl[0] &= ~DS1305_AEI0;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0);
+ if (status < 0)
+ return status;
+ }
+
+ /* write alarm */
+ buf[0] = DS1305_WRITE | DS1305_ALM0(DS1305_SEC);
+ buf[1 + DS1305_SEC] = bin2bcd(alm->time.tm_sec);
+ buf[1 + DS1305_MIN] = bin2bcd(alm->time.tm_min);
+ buf[1 + DS1305_HOUR] = hour2bcd(ds1305->hr12, alm->time.tm_hour);
+ buf[1 + DS1305_WDAY] = DS1305_ALM_DISABLE;
+
+ dev_dbg(dev, "%s: %02x %02x %02x %02x\n",
+ "alm0 write", buf[1 + DS1305_SEC], buf[1 + DS1305_MIN],
+ buf[1 + DS1305_HOUR], buf[1 + DS1305_WDAY]);
+
+ status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
+ if (status < 0)
+ return status;
+
+ /* enable alarm if requested */
+ if (alm->enabled) {
+ ds1305->ctrl[0] |= DS1305_AEI0;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ status = spi_write_then_read(ds1305->spi, buf, 2, NULL, 0);
+ }
+
+ return status;
+}
+
+#ifdef CONFIG_PROC_FS
+
+static int ds1305_proc(struct device *dev, struct seq_file *seq)
+{
+ struct ds1305 *ds1305 = dev_get_drvdata(dev);
+ char *diodes = "no";
+ char *resistors = "";
+
+ /* ctrl[2] is treated as read-only; no locking needed */
+ if ((ds1305->ctrl[2] & 0xf0) == DS1305_TRICKLE_MAGIC) {
+ switch (ds1305->ctrl[2] & 0x0c) {
+ case DS1305_TRICKLE_DS2:
+ diodes = "2 diodes, ";
+ break;
+ case DS1305_TRICKLE_DS1:
+ diodes = "1 diode, ";
+ break;
+ default:
+ goto done;
+ }
+ switch (ds1305->ctrl[2] & 0x03) {
+ case DS1305_TRICKLE_2K:
+ resistors = "2k Ohm";
+ break;
+ case DS1305_TRICKLE_4K:
+ resistors = "4k Ohm";
+ break;
+ case DS1305_TRICKLE_8K:
+ resistors = "8k Ohm";
+ break;
+ default:
+ diodes = "no";
+ break;
+ }
+ }
+
+done:
+ seq_printf(seq, "trickle_charge\t: %s%s\n", diodes, resistors);
+
+ return 0;
+}
+
+#else
+#define ds1305_proc NULL
+#endif
+
+static const struct rtc_class_ops ds1305_ops = {
+ .read_time = ds1305_get_time,
+ .set_time = ds1305_set_time,
+ .read_alarm = ds1305_get_alarm,
+ .set_alarm = ds1305_set_alarm,
+ .proc = ds1305_proc,
+ .alarm_irq_enable = ds1305_alarm_irq_enable,
+};
+
+static void ds1305_work(struct work_struct *work)
+{
+ struct ds1305 *ds1305 = container_of(work, struct ds1305, work);
+ struct mutex *lock = &ds1305->rtc->ops_lock;
+ struct spi_device *spi = ds1305->spi;
+ u8 buf[3];
+ int status;
+
+ /* lock to protect ds1305->ctrl */
+ mutex_lock(lock);
+
+ /* Disable the IRQ, and clear its status ... for now, we "know"
+ * that if more than one alarm is active, they're in sync.
+ * Note that reading ALM data registers also clears IRQ status.
+ */
+ ds1305->ctrl[0] &= ~(DS1305_AEI1 | DS1305_AEI0);
+ ds1305->ctrl[1] = 0;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ buf[2] = 0;
+
+ status = spi_write_then_read(spi, buf, sizeof(buf),
+ NULL, 0);
+ if (status < 0)
+ dev_dbg(&spi->dev, "clear irq --> %d\n", status);
+
+ mutex_unlock(lock);
+
+ if (!test_bit(FLAG_EXITING, &ds1305->flags))
+ enable_irq(spi->irq);
+
+ rtc_update_irq(ds1305->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+/*
+ * This "real" IRQ handler hands off to a workqueue mostly to allow
+ * mutex locking for ds1305->ctrl ... unlike I2C, we could issue async
+ * I/O requests in IRQ context (to clear the IRQ status).
+ */
+static irqreturn_t ds1305_irq(int irq, void *p)
+{
+ struct ds1305 *ds1305 = p;
+
+ disable_irq(irq);
+ schedule_work(&ds1305->work);
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Interface for NVRAM
+ */
+
+static void msg_init(struct spi_message *m, struct spi_transfer *x,
+ u8 *addr, size_t count, char *tx, char *rx)
+{
+ spi_message_init(m);
+ memset(x, 0, 2 * sizeof(*x));
+
+ x->tx_buf = addr;
+ x->len = 1;
+ spi_message_add_tail(x, m);
+
+ x++;
+
+ x->tx_buf = tx;
+ x->rx_buf = rx;
+ x->len = count;
+ spi_message_add_tail(x, m);
+}
+
+static ssize_t
+ds1305_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct spi_device *spi;
+ u8 addr;
+ struct spi_message m;
+ struct spi_transfer x[2];
+ int status;
+
+ spi = container_of(kobj, struct spi_device, dev.kobj);
+
+ if (unlikely(off >= DS1305_NVRAM_LEN))
+ return 0;
+ if (count >= DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN;
+ if ((off + count) > DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN - off;
+ if (unlikely(!count))
+ return count;
+
+ addr = DS1305_NVRAM + off;
+ msg_init(&m, x, &addr, count, NULL, buf);
+
+ status = spi_sync(spi, &m);
+ if (status < 0)
+ dev_err(&spi->dev, "nvram %s error %d\n", "read", status);
+ return (status < 0) ? status : count;
+}
+
+static ssize_t
+ds1305_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct spi_device *spi;
+ u8 addr;
+ struct spi_message m;
+ struct spi_transfer x[2];
+ int status;
+
+ spi = container_of(kobj, struct spi_device, dev.kobj);
+
+ if (unlikely(off >= DS1305_NVRAM_LEN))
+ return -EFBIG;
+ if (count >= DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN;
+ if ((off + count) > DS1305_NVRAM_LEN)
+ count = DS1305_NVRAM_LEN - off;
+ if (unlikely(!count))
+ return count;
+
+ addr = (DS1305_WRITE | DS1305_NVRAM) + off;
+ msg_init(&m, x, &addr, count, buf, NULL);
+
+ status = spi_sync(spi, &m);
+ if (status < 0)
+ dev_err(&spi->dev, "nvram %s error %d\n", "write", status);
+ return (status < 0) ? status : count;
+}
+
+static struct bin_attribute nvram = {
+ .attr.name = "nvram",
+ .attr.mode = S_IRUGO | S_IWUSR,
+ .read = ds1305_nvram_read,
+ .write = ds1305_nvram_write,
+ .size = DS1305_NVRAM_LEN,
+};
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Interface to SPI stack
+ */
+
+static int ds1305_probe(struct spi_device *spi)
+{
+ struct ds1305 *ds1305;
+ int status;
+ u8 addr, value;
+ struct ds1305_platform_data *pdata = dev_get_platdata(&spi->dev);
+ bool write_ctrl = false;
+
+ /* Sanity check board setup data. This may be hooked up
+ * in 3wire mode, but we don't care. Note that unless
+ * there's an inverter in place, this needs SPI_CS_HIGH!
+ */
+ if ((spi->bits_per_word && spi->bits_per_word != 8)
+ || (spi->max_speed_hz > 2000000)
+ || !(spi->mode & SPI_CPHA))
+ return -EINVAL;
+
+ /* set up driver data */
+ ds1305 = devm_kzalloc(&spi->dev, sizeof(*ds1305), GFP_KERNEL);
+ if (!ds1305)
+ return -ENOMEM;
+ ds1305->spi = spi;
+ spi_set_drvdata(spi, ds1305);
+
+ /* read and cache control registers */
+ addr = DS1305_CONTROL;
+ status = spi_write_then_read(spi, &addr, sizeof(addr),
+ ds1305->ctrl, sizeof(ds1305->ctrl));
+ if (status < 0) {
+ dev_dbg(&spi->dev, "can't %s, %d\n",
+ "read", status);
+ return status;
+ }
+
+ dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "read", ds1305->ctrl);
+
+ /* Sanity check register values ... partially compensating for the
+ * fact that SPI has no device handshake. A pullup on MISO would
+ * make these tests fail; but not all systems will have one. If
+ * some register is neither 0x00 nor 0xff, a chip is likely there.
+ */
+ if ((ds1305->ctrl[0] & 0x38) != 0 || (ds1305->ctrl[1] & 0xfc) != 0) {
+ dev_dbg(&spi->dev, "RTC chip is not present\n");
+ return -ENODEV;
+ }
+ if (ds1305->ctrl[2] == 0)
+ dev_dbg(&spi->dev, "chip may not be present\n");
+
+ /* enable writes if needed ... if we were paranoid it would
+ * make sense to enable them only when absolutely necessary.
+ */
+ if (ds1305->ctrl[0] & DS1305_WP) {
+ u8 buf[2];
+
+ ds1305->ctrl[0] &= ~DS1305_WP;
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
+
+ dev_dbg(&spi->dev, "clear WP --> %d\n", status);
+ if (status < 0)
+ return status;
+ }
+
+ /* on DS1305, maybe start oscillator; like most low power
+ * oscillators, it may take a second to stabilize
+ */
+ if (ds1305->ctrl[0] & DS1305_nEOSC) {
+ ds1305->ctrl[0] &= ~DS1305_nEOSC;
+ write_ctrl = true;
+ dev_warn(&spi->dev, "SET TIME!\n");
+ }
+
+ /* ack any pending IRQs */
+ if (ds1305->ctrl[1]) {
+ ds1305->ctrl[1] = 0;
+ write_ctrl = true;
+ }
+
+ /* this may need one-time (re)init */
+ if (pdata) {
+ /* maybe enable trickle charge */
+ if (((ds1305->ctrl[2] & 0xf0) != DS1305_TRICKLE_MAGIC)) {
+ ds1305->ctrl[2] = DS1305_TRICKLE_MAGIC
+ | pdata->trickle;
+ write_ctrl = true;
+ }
+
+ /* on DS1306, configure 1 Hz signal */
+ if (pdata->is_ds1306) {
+ if (pdata->en_1hz) {
+ if (!(ds1305->ctrl[0] & DS1306_1HZ)) {
+ ds1305->ctrl[0] |= DS1306_1HZ;
+ write_ctrl = true;
+ }
+ } else {
+ if (ds1305->ctrl[0] & DS1306_1HZ) {
+ ds1305->ctrl[0] &= ~DS1306_1HZ;
+ write_ctrl = true;
+ }
+ }
+ }
+ }
+
+ if (write_ctrl) {
+ u8 buf[4];
+
+ buf[0] = DS1305_WRITE | DS1305_CONTROL;
+ buf[1] = ds1305->ctrl[0];
+ buf[2] = ds1305->ctrl[1];
+ buf[3] = ds1305->ctrl[2];
+ status = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
+ if (status < 0) {
+ dev_dbg(&spi->dev, "can't %s, %d\n",
+ "write", status);
+ return status;
+ }
+
+ dev_dbg(&spi->dev, "ctrl %s: %3ph\n", "write", ds1305->ctrl);
+ }
+
+ /* see if non-Linux software set up AM/PM mode */
+ addr = DS1305_HOUR;
+ status = spi_write_then_read(spi, &addr, sizeof(addr),
+ &value, sizeof(value));
+ if (status < 0) {
+ dev_dbg(&spi->dev, "read HOUR --> %d\n", status);
+ return status;
+ }
+
+ ds1305->hr12 = (DS1305_HR_12 & value) != 0;
+ if (ds1305->hr12)
+ dev_dbg(&spi->dev, "AM/PM\n");
+
+ /* register RTC ... from here on, ds1305->ctrl needs locking */
+ ds1305->rtc = devm_rtc_device_register(&spi->dev, "ds1305",
+ &ds1305_ops, THIS_MODULE);
+ if (IS_ERR(ds1305->rtc)) {
+ status = PTR_ERR(ds1305->rtc);
+ dev_dbg(&spi->dev, "register rtc --> %d\n", status);
+ return status;
+ }
+
+ /* Maybe set up alarm IRQ; be ready to handle it triggering right
+ * away. NOTE that we don't share this. The signal is active low,
+ * and we can't ack it before a SPI message delay. We temporarily
+ * disable the IRQ until it's acked, which lets us work with more
+ * IRQ trigger modes (not all IRQ controllers can do falling edge).
+ */
+ if (spi->irq) {
+ INIT_WORK(&ds1305->work, ds1305_work);
+ status = devm_request_irq(&spi->dev, spi->irq, ds1305_irq,
+ 0, dev_name(&ds1305->rtc->dev), ds1305);
+ if (status < 0) {
+ dev_err(&spi->dev, "request_irq %d --> %d\n",
+ spi->irq, status);
+ } else {
+ device_set_wakeup_capable(&spi->dev, 1);
+ }
+ }
+
+ /* export NVRAM */
+ status = sysfs_create_bin_file(&spi->dev.kobj, &nvram);
+ if (status < 0) {
+ dev_err(&spi->dev, "register nvram --> %d\n", status);
+ }
+
+ return 0;
+}
+
+static int ds1305_remove(struct spi_device *spi)
+{
+ struct ds1305 *ds1305 = spi_get_drvdata(spi);
+
+ sysfs_remove_bin_file(&spi->dev.kobj, &nvram);
+
+ /* carefully shut down irq and workqueue, if present */
+ if (spi->irq) {
+ set_bit(FLAG_EXITING, &ds1305->flags);
+ devm_free_irq(&spi->dev, spi->irq, ds1305);
+ cancel_work_sync(&ds1305->work);
+ }
+
+ return 0;
+}
+
+static struct spi_driver ds1305_driver = {
+ .driver.name = "rtc-ds1305",
+ .driver.owner = THIS_MODULE,
+ .probe = ds1305_probe,
+ .remove = ds1305_remove,
+ /* REVISIT add suspend/resume */
+};
+
+module_spi_driver(ds1305_driver);
+
+MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-ds1305");
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
new file mode 100644
index 000000000..4ffabb322
--- /dev/null
+++ b/drivers/rtc/rtc-ds1307.c
@@ -0,0 +1,1258 @@
+/*
+ * rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
+ *
+ * Copyright (C) 2005 James Chapman (ds1337 core)
+ * Copyright (C) 2006 David Brownell
+ * Copyright (C) 2009 Matthias Fuchs (rx8025 support)
+ * Copyright (C) 2012 Bertrand Achard (nvram access fixes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/rtc/ds1307.h>
+
+/*
+ * We can't determine type by probing, but if we expect pre-Linux code
+ * to have set the chip up as a clock (turning on the oscillator and
+ * setting the date and time), Linux can ignore the non-clock features.
+ * That's a natural job for a factory or repair bench.
+ */
+enum ds_type {
+ ds_1307,
+ ds_1337,
+ ds_1338,
+ ds_1339,
+ ds_1340,
+ ds_1388,
+ ds_3231,
+ m41t00,
+ mcp794xx,
+ rx_8025,
+ last_ds_type /* always last */
+ /* rs5c372 too? different address... */
+};
+
+
+/* RTC registers don't differ much, except for the century flag */
+#define DS1307_REG_SECS 0x00 /* 00-59 */
+# define DS1307_BIT_CH 0x80
+# define DS1340_BIT_nEOSC 0x80
+# define MCP794XX_BIT_ST 0x80
+#define DS1307_REG_MIN 0x01 /* 00-59 */
+#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */
+# define DS1307_BIT_12HR 0x40 /* in REG_HOUR */
+# define DS1307_BIT_PM 0x20 /* in REG_HOUR */
+# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */
+# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */
+#define DS1307_REG_WDAY 0x03 /* 01-07 */
+# define MCP794XX_BIT_VBATEN 0x08
+#define DS1307_REG_MDAY 0x04 /* 01-31 */
+#define DS1307_REG_MONTH 0x05 /* 01-12 */
+# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
+#define DS1307_REG_YEAR 0x06 /* 00-99 */
+
+/*
+ * Other registers (control, status, alarms, trickle charge, NVRAM, etc)
+ * start at 7, and they differ a LOT. Only control and status matter for
+ * basic RTC date and time functionality; be careful using them.
+ */
+#define DS1307_REG_CONTROL 0x07 /* or ds1338 */
+# define DS1307_BIT_OUT 0x80
+# define DS1338_BIT_OSF 0x20
+# define DS1307_BIT_SQWE 0x10
+# define DS1307_BIT_RS1 0x02
+# define DS1307_BIT_RS0 0x01
+#define DS1337_REG_CONTROL 0x0e
+# define DS1337_BIT_nEOSC 0x80
+# define DS1339_BIT_BBSQI 0x20
+# define DS3231_BIT_BBSQW 0x40 /* same as BBSQI */
+# define DS1337_BIT_RS2 0x10
+# define DS1337_BIT_RS1 0x08
+# define DS1337_BIT_INTCN 0x04
+# define DS1337_BIT_A2IE 0x02
+# define DS1337_BIT_A1IE 0x01
+#define DS1340_REG_CONTROL 0x07
+# define DS1340_BIT_OUT 0x80
+# define DS1340_BIT_FT 0x40
+# define DS1340_BIT_CALIB_SIGN 0x20
+# define DS1340_M_CALIBRATION 0x1f
+#define DS1340_REG_FLAG 0x09
+# define DS1340_BIT_OSF 0x80
+#define DS1337_REG_STATUS 0x0f
+# define DS1337_BIT_OSF 0x80
+# define DS1337_BIT_A2I 0x02
+# define DS1337_BIT_A1I 0x01
+#define DS1339_REG_ALARM1_SECS 0x07
+
+#define DS13XX_TRICKLE_CHARGER_MAGIC 0xa0
+
+#define RX8025_REG_CTRL1 0x0e
+# define RX8025_BIT_2412 0x20
+#define RX8025_REG_CTRL2 0x0f
+# define RX8025_BIT_PON 0x10
+# define RX8025_BIT_VDET 0x40
+# define RX8025_BIT_XST 0x20
+
+
+struct ds1307 {
+ u8 offset; /* register's offset */
+ u8 regs[11];
+ u16 nvram_offset;
+ struct bin_attribute *nvram;
+ enum ds_type type;
+ unsigned long flags;
+#define HAS_NVRAM 0 /* bit 0 == sysfs file active */
+#define HAS_ALARM 1 /* bit 1 == irq claimed */
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+ s32 (*read_block_data)(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values);
+ s32 (*write_block_data)(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values);
+};
+
+struct chip_desc {
+ unsigned alarm:1;
+ u16 nvram_offset;
+ u16 nvram_size;
+ u16 trickle_charger_reg;
+ u8 trickle_charger_setup;
+ u8 (*do_trickle_setup)(struct i2c_client *, uint32_t, bool);
+};
+
+static u8 do_trickle_setup_ds1339(struct i2c_client *,
+ uint32_t ohms, bool diode);
+
+static struct chip_desc chips[last_ds_type] = {
+ [ds_1307] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1337] = {
+ .alarm = 1,
+ },
+ [ds_1338] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1339] = {
+ .alarm = 1,
+ .trickle_charger_reg = 0x10,
+ .do_trickle_setup = &do_trickle_setup_ds1339,
+ },
+ [ds_1340] = {
+ .trickle_charger_reg = 0x08,
+ },
+ [ds_1388] = {
+ .trickle_charger_reg = 0x0a,
+ },
+ [ds_3231] = {
+ .alarm = 1,
+ },
+ [mcp794xx] = {
+ .alarm = 1,
+ /* this is battery backed SRAM */
+ .nvram_offset = 0x20,
+ .nvram_size = 0x40,
+ },
+};
+
+static const struct i2c_device_id ds1307_id[] = {
+ { "ds1307", ds_1307 },
+ { "ds1337", ds_1337 },
+ { "ds1338", ds_1338 },
+ { "ds1339", ds_1339 },
+ { "ds1388", ds_1388 },
+ { "ds1340", ds_1340 },
+ { "ds3231", ds_3231 },
+ { "m41t00", m41t00 },
+ { "mcp7940x", mcp794xx },
+ { "mcp7941x", mcp794xx },
+ { "pt7c4338", ds_1307 },
+ { "rx8025", rx_8025 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1307_id);
+
+/*----------------------------------------------------------------------*/
+
+#define BLOCK_DATA_MAX_TRIES 10
+
+static s32 ds1307_read_block_data_once(const struct i2c_client *client,
+ u8 command, u8 length, u8 *values)
+{
+ s32 i, data;
+
+ for (i = 0; i < length; i++) {
+ data = i2c_smbus_read_byte_data(client, command + i);
+ if (data < 0)
+ return data;
+ values[i] = data;
+ }
+ return i;
+}
+
+static s32 ds1307_read_block_data(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values)
+{
+ u8 oldvalues[255];
+ s32 ret;
+ int tries = 0;
+
+ dev_dbg(&client->dev, "ds1307_read_block_data (length=%d)\n", length);
+ ret = ds1307_read_block_data_once(client, command, length, values);
+ if (ret < 0)
+ return ret;
+ do {
+ if (++tries > BLOCK_DATA_MAX_TRIES) {
+ dev_err(&client->dev,
+ "ds1307_read_block_data failed\n");
+ return -EIO;
+ }
+ memcpy(oldvalues, values, length);
+ ret = ds1307_read_block_data_once(client, command, length,
+ values);
+ if (ret < 0)
+ return ret;
+ } while (memcmp(oldvalues, values, length));
+ return length;
+}
+
+static s32 ds1307_write_block_data(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values)
+{
+ u8 currvalues[255];
+ int tries = 0;
+
+ dev_dbg(&client->dev, "ds1307_write_block_data (length=%d)\n", length);
+ do {
+ s32 i, ret;
+
+ if (++tries > BLOCK_DATA_MAX_TRIES) {
+ dev_err(&client->dev,
+ "ds1307_write_block_data failed\n");
+ return -EIO;
+ }
+ for (i = 0; i < length; i++) {
+ ret = i2c_smbus_write_byte_data(client, command + i,
+ values[i]);
+ if (ret < 0)
+ return ret;
+ }
+ ret = ds1307_read_block_data_once(client, command, length,
+ currvalues);
+ if (ret < 0)
+ return ret;
+ } while (memcmp(currvalues, values, length));
+ return length;
+}
+
+/*----------------------------------------------------------------------*/
+
+/* These RTC devices are not designed to be connected to a SMbus adapter.
+ SMbus limits block operations length to 32 bytes, whereas it's not
+ limited on I2C buses. As a result, accesses may exceed 32 bytes;
+ in that case, split them into smaller blocks */
+
+static s32 ds1307_native_smbus_write_block_data(const struct i2c_client *client,
+ u8 command, u8 length, const u8 *values)
+{
+ u8 suboffset = 0;
+
+ if (length <= I2C_SMBUS_BLOCK_MAX)
+ return i2c_smbus_write_i2c_block_data(client,
+ command, length, values);
+
+ while (suboffset < length) {
+ s32 retval = i2c_smbus_write_i2c_block_data(client,
+ command + suboffset,
+ min(I2C_SMBUS_BLOCK_MAX, length - suboffset),
+ values + suboffset);
+ if (retval < 0)
+ return retval;
+
+ suboffset += I2C_SMBUS_BLOCK_MAX;
+ }
+ return length;
+}
+
+static s32 ds1307_native_smbus_read_block_data(const struct i2c_client *client,
+ u8 command, u8 length, u8 *values)
+{
+ u8 suboffset = 0;
+
+ if (length <= I2C_SMBUS_BLOCK_MAX)
+ return i2c_smbus_read_i2c_block_data(client,
+ command, length, values);
+
+ while (suboffset < length) {
+ s32 retval = i2c_smbus_read_i2c_block_data(client,
+ command + suboffset,
+ min(I2C_SMBUS_BLOCK_MAX, length - suboffset),
+ values + suboffset);
+ if (retval < 0)
+ return retval;
+
+ suboffset += I2C_SMBUS_BLOCK_MAX;
+ }
+ return length;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * The IRQ logic includes a "real" handler running in IRQ context just
+ * long enough to schedule this workqueue entry. We need a task context
+ * to talk to the RTC, since I2C I/O calls require that; and disable the
+ * IRQ until we clear its status on the chip, so that this handler can
+ * work with any type of triggering (not just falling edge).
+ *
+ * The ds1337 and ds1339 both have two alarms, but we only use the first
+ * one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
+ * signal; ds1339 chips have only one alarm signal.
+ */
+static void ds1307_work(struct work_struct *work)
+{
+ struct ds1307 *ds1307;
+ struct i2c_client *client;
+ struct mutex *lock;
+ int stat, control;
+
+ ds1307 = container_of(work, struct ds1307, work);
+ client = ds1307->client;
+ lock = &ds1307->rtc->ops_lock;
+
+ mutex_lock(lock);
+ stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
+ if (stat < 0)
+ goto out;
+
+ if (stat & DS1337_BIT_A1I) {
+ stat &= ~DS1337_BIT_A1I;
+ i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat);
+
+ control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
+ if (control < 0)
+ goto out;
+
+ control &= ~DS1337_BIT_A1IE;
+ i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control);
+
+ rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (test_bit(HAS_ALARM, &ds1307->flags))
+ enable_irq(client->irq);
+ mutex_unlock(lock);
+}
+
+static irqreturn_t ds1307_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds1307->work);
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int ds1307_get_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int tmp;
+
+ /* read the RTC date and time registers all at once */
+ tmp = ds1307->read_block_data(ds1307->client,
+ ds1307->offset, 7, ds1307->regs);
+ if (tmp != 7) {
+ dev_err(dev, "%s error %d\n", "read", tmp);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "%s: %7ph\n", "read", ds1307->regs);
+
+ t->tm_sec = bcd2bin(ds1307->regs[DS1307_REG_SECS] & 0x7f);
+ t->tm_min = bcd2bin(ds1307->regs[DS1307_REG_MIN] & 0x7f);
+ tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;
+ t->tm_hour = bcd2bin(tmp);
+ t->tm_wday = bcd2bin(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1;
+ t->tm_mday = bcd2bin(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
+ tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
+ t->tm_mon = bcd2bin(tmp) - 1;
+
+ /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
+ t->tm_year = bcd2bin(ds1307->regs[DS1307_REG_YEAR]) + 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ /* initial clock setting can be undefined */
+ return rtc_valid_tm(t);
+}
+
+static int ds1307_set_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307 *ds1307 = dev_get_drvdata(dev);
+ int result;
+ int tmp;
+ u8 *buf = ds1307->regs;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ buf[DS1307_REG_SECS] = bin2bcd(t->tm_sec);
+ buf[DS1307_REG_MIN] = bin2bcd(t->tm_min);
+ buf[DS1307_REG_HOUR] = bin2bcd(t->tm_hour);
+ buf[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1);
+ buf[DS1307_REG_MDAY] = bin2bcd(t->tm_mday);
+ buf[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1);
+
+ /* assume 20YY not 19YY */
+ tmp = t->tm_year - 100;
+ buf[DS1307_REG_YEAR] = bin2bcd(tmp);
+
+ switch (ds1307->type) {
+ case ds_1337:
+ case ds_1339:
+ case ds_3231:
+ buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
+ break;
+ case ds_1340:
+ buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
+ | DS1340_BIT_CENTURY;
+ break;
+ case mcp794xx:
+ /*
+ * these bits were cleared when preparing the date/time
+ * values and need to be set again before writing the
+ * buffer out to the device.
+ */
+ buf[DS1307_REG_SECS] |= MCP794XX_BIT_ST;
+ buf[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN;
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%s: %7ph\n", "write", buf);
+
+ result = ds1307->write_block_data(ds1307->client,
+ ds1307->offset, 7, buf);
+ if (result < 0) {
+ dev_err(dev, "%s error %d\n", "write", result);
+ return result;
+ }
+ return 0;
+}
+
+static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ /* read all ALARM1, ALARM2, and status registers at once */
+ ret = ds1307->read_block_data(client,
+ DS1339_REG_ALARM1_SECS, 9, ds1307->regs);
+ if (ret != 9) {
+ dev_err(dev, "%s error %d\n", "alarm read", ret);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "%s: %02x %02x %02x %02x, %02x %02x %02x, %02x %02x\n",
+ "alarm read",
+ ds1307->regs[0], ds1307->regs[1],
+ ds1307->regs[2], ds1307->regs[3],
+ ds1307->regs[4], ds1307->regs[5],
+ ds1307->regs[6], ds1307->regs[7],
+ ds1307->regs[8]);
+
+ /*
+ * report alarm time (ALARM1); assume 24 hour and day-of-month modes,
+ * and that all four fields are checked matches
+ */
+ t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f);
+ t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f);
+ t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f);
+ t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f);
+ t->time.tm_mon = -1;
+ t->time.tm_year = -1;
+ t->time.tm_wday = -1;
+ t->time.tm_yday = -1;
+ t->time.tm_isdst = -1;
+
+ /* ... and status */
+ t->enabled = !!(ds1307->regs[7] & DS1337_BIT_A1IE);
+ t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I);
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, enabled=%d, pending=%d\n",
+ "alarm read", t->time.tm_sec, t->time.tm_min,
+ t->time.tm_hour, t->time.tm_mday,
+ t->enabled, t->pending);
+
+ return 0;
+}
+
+static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ unsigned char *buf = ds1307->regs;
+ u8 control, status;
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, enabled=%d, pending=%d\n",
+ "alarm set", t->time.tm_sec, t->time.tm_min,
+ t->time.tm_hour, t->time.tm_mday,
+ t->enabled, t->pending);
+
+ /* read current status of both alarms and the chip */
+ ret = ds1307->read_block_data(client,
+ DS1339_REG_ALARM1_SECS, 9, buf);
+ if (ret != 9) {
+ dev_err(dev, "%s error %d\n", "alarm write", ret);
+ return -EIO;
+ }
+ control = ds1307->regs[7];
+ status = ds1307->regs[8];
+
+ dev_dbg(dev, "%s: %02x %02x %02x %02x, %02x %02x %02x, %02x %02x\n",
+ "alarm set (old status)",
+ ds1307->regs[0], ds1307->regs[1],
+ ds1307->regs[2], ds1307->regs[3],
+ ds1307->regs[4], ds1307->regs[5],
+ ds1307->regs[6], control, status);
+
+ /* set ALARM1, using 24 hour and day-of-month modes */
+ buf[0] = bin2bcd(t->time.tm_sec);
+ buf[1] = bin2bcd(t->time.tm_min);
+ buf[2] = bin2bcd(t->time.tm_hour);
+ buf[3] = bin2bcd(t->time.tm_mday);
+
+ /* set ALARM2 to non-garbage */
+ buf[4] = 0;
+ buf[5] = 0;
+ buf[6] = 0;
+
+ /* optionally enable ALARM1 */
+ buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);
+ if (t->enabled) {
+ dev_dbg(dev, "alarm IRQ armed\n");
+ buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */
+ }
+ buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
+
+ ret = ds1307->write_block_data(client,
+ DS1339_REG_ALARM1_SECS, 9, buf);
+ if (ret < 0) {
+ dev_err(dev, "can't set alarm time\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -ENOTTY;
+
+ ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
+ if (ret < 0)
+ return ret;
+
+ if (enabled)
+ ret |= DS1337_BIT_A1IE;
+ else
+ ret &= ~DS1337_BIT_A1IE;
+
+ ret = i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, ret);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct rtc_class_ops ds13xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = ds1337_read_alarm,
+ .set_alarm = ds1337_set_alarm,
+ .alarm_irq_enable = ds1307_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Alarm support for mcp794xx devices.
+ */
+
+#define MCP794XX_REG_CONTROL 0x07
+# define MCP794XX_BIT_ALM0_EN 0x10
+# define MCP794XX_BIT_ALM1_EN 0x20
+#define MCP794XX_REG_ALARM0_BASE 0x0a
+#define MCP794XX_REG_ALARM0_CTRL 0x0d
+#define MCP794XX_REG_ALARM1_BASE 0x11
+#define MCP794XX_REG_ALARM1_CTRL 0x14
+# define MCP794XX_BIT_ALMX_IF (1 << 3)
+# define MCP794XX_BIT_ALMX_C0 (1 << 4)
+# define MCP794XX_BIT_ALMX_C1 (1 << 5)
+# define MCP794XX_BIT_ALMX_C2 (1 << 6)
+# define MCP794XX_BIT_ALMX_POL (1 << 7)
+# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \
+ MCP794XX_BIT_ALMX_C1 | \
+ MCP794XX_BIT_ALMX_C2)
+
+static void mcp794xx_work(struct work_struct *work)
+{
+ struct ds1307 *ds1307 = container_of(work, struct ds1307, work);
+ struct i2c_client *client = ds1307->client;
+ int reg, ret;
+
+ mutex_lock(&ds1307->rtc->ops_lock);
+
+ /* Check and clear alarm 0 interrupt flag. */
+ reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_ALARM0_CTRL);
+ if (reg < 0)
+ goto out;
+ if (!(reg & MCP794XX_BIT_ALMX_IF))
+ goto out;
+ reg &= ~MCP794XX_BIT_ALMX_IF;
+ ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_ALARM0_CTRL, reg);
+ if (ret < 0)
+ goto out;
+
+ /* Disable alarm 0. */
+ reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL);
+ if (reg < 0)
+ goto out;
+ reg &= ~MCP794XX_BIT_ALM0_EN;
+ ret = i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg);
+ if (ret < 0)
+ goto out;
+
+ rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
+
+out:
+ if (test_bit(HAS_ALARM, &ds1307->flags))
+ enable_irq(client->irq);
+ mutex_unlock(&ds1307->rtc->ops_lock);
+}
+
+static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ u8 *regs = ds1307->regs;
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ /* Read control and alarm 0 registers. */
+ ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
+ if (ret < 0)
+ return ret;
+
+ t->enabled = !!(regs[0] & MCP794XX_BIT_ALM0_EN);
+
+ /* Report alarm 0 time assuming 24-hour and day-of-month modes. */
+ t->time.tm_sec = bcd2bin(ds1307->regs[3] & 0x7f);
+ t->time.tm_min = bcd2bin(ds1307->regs[4] & 0x7f);
+ t->time.tm_hour = bcd2bin(ds1307->regs[5] & 0x3f);
+ t->time.tm_wday = bcd2bin(ds1307->regs[6] & 0x7) - 1;
+ t->time.tm_mday = bcd2bin(ds1307->regs[7] & 0x3f);
+ t->time.tm_mon = bcd2bin(ds1307->regs[8] & 0x1f) - 1;
+ t->time.tm_year = -1;
+ t->time.tm_yday = -1;
+ t->time.tm_isdst = -1;
+
+ dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+ "enabled=%d polarity=%d irq=%d match=%d\n", __func__,
+ t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_wday, t->time.tm_mday, t->time.tm_mon, t->enabled,
+ !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_POL),
+ !!(ds1307->regs[6] & MCP794XX_BIT_ALMX_IF),
+ (ds1307->regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4);
+
+ return 0;
+}
+
+static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ unsigned char *regs = ds1307->regs;
+ int ret;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
+ "enabled=%d pending=%d\n", __func__,
+ t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_wday, t->time.tm_mday, t->time.tm_mon,
+ t->enabled, t->pending);
+
+ /* Read control and alarm 0 registers. */
+ ret = ds1307->read_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
+ if (ret < 0)
+ return ret;
+
+ /* Set alarm 0, using 24-hour and day-of-month modes. */
+ regs[3] = bin2bcd(t->time.tm_sec);
+ regs[4] = bin2bcd(t->time.tm_min);
+ regs[5] = bin2bcd(t->time.tm_hour);
+ regs[6] = bin2bcd(t->time.tm_wday) + 1;
+ regs[7] = bin2bcd(t->time.tm_mday);
+ regs[8] = bin2bcd(t->time.tm_mon) + 1;
+
+ /* Clear the alarm 0 interrupt flag. */
+ regs[6] &= ~MCP794XX_BIT_ALMX_IF;
+ /* Set alarm match: second, minute, hour, day, date, month. */
+ regs[6] |= MCP794XX_MSK_ALMX_MATCH;
+
+ if (t->enabled)
+ regs[0] |= MCP794XX_BIT_ALM0_EN;
+ else
+ regs[0] &= ~MCP794XX_BIT_ALM0_EN;
+
+ ret = ds1307->write_block_data(client, MCP794XX_REG_CONTROL, 10, regs);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ int reg;
+
+ if (!test_bit(HAS_ALARM, &ds1307->flags))
+ return -EINVAL;
+
+ reg = i2c_smbus_read_byte_data(client, MCP794XX_REG_CONTROL);
+ if (reg < 0)
+ return reg;
+
+ if (enabled)
+ reg |= MCP794XX_BIT_ALM0_EN;
+ else
+ reg &= ~MCP794XX_BIT_ALM0_EN;
+
+ return i2c_smbus_write_byte_data(client, MCP794XX_REG_CONTROL, reg);
+}
+
+static const struct rtc_class_ops mcp794xx_rtc_ops = {
+ .read_time = ds1307_get_time,
+ .set_time = ds1307_set_time,
+ .read_alarm = mcp794xx_read_alarm,
+ .set_alarm = mcp794xx_set_alarm,
+ .alarm_irq_enable = mcp794xx_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------------*/
+
+static ssize_t
+ds1307_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client;
+ struct ds1307 *ds1307;
+ int result;
+
+ client = kobj_to_i2c_client(kobj);
+ ds1307 = i2c_get_clientdata(client);
+
+ if (unlikely(off >= ds1307->nvram->size))
+ return 0;
+ if ((off + count) > ds1307->nvram->size)
+ count = ds1307->nvram->size - off;
+ if (unlikely(!count))
+ return count;
+
+ result = ds1307->read_block_data(client, ds1307->nvram_offset + off,
+ count, buf);
+ if (result < 0)
+ dev_err(&client->dev, "%s error %d\n", "nvram read", result);
+ return result;
+}
+
+static ssize_t
+ds1307_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client;
+ struct ds1307 *ds1307;
+ int result;
+
+ client = kobj_to_i2c_client(kobj);
+ ds1307 = i2c_get_clientdata(client);
+
+ if (unlikely(off >= ds1307->nvram->size))
+ return -EFBIG;
+ if ((off + count) > ds1307->nvram->size)
+ count = ds1307->nvram->size - off;
+ if (unlikely(!count))
+ return count;
+
+ result = ds1307->write_block_data(client, ds1307->nvram_offset + off,
+ count, buf);
+ if (result < 0) {
+ dev_err(&client->dev, "%s error %d\n", "nvram write", result);
+ return result;
+ }
+ return count;
+}
+
+
+/*----------------------------------------------------------------------*/
+
+static u8 do_trickle_setup_ds1339(struct i2c_client *client,
+ uint32_t ohms, bool diode)
+{
+ u8 setup = (diode) ? DS1307_TRICKLE_CHARGER_DIODE :
+ DS1307_TRICKLE_CHARGER_NO_DIODE;
+
+ switch (ohms) {
+ case 250:
+ setup |= DS1307_TRICKLE_CHARGER_250_OHM;
+ break;
+ case 2000:
+ setup |= DS1307_TRICKLE_CHARGER_2K_OHM;
+ break;
+ case 4000:
+ setup |= DS1307_TRICKLE_CHARGER_4K_OHM;
+ break;
+ default:
+ dev_warn(&client->dev,
+ "Unsupported ohm value %u in dt\n", ohms);
+ return 0;
+ }
+ return setup;
+}
+
+static void ds1307_trickle_of_init(struct i2c_client *client,
+ struct chip_desc *chip)
+{
+ uint32_t ohms = 0;
+ bool diode = true;
+
+ if (!chip->do_trickle_setup)
+ goto out;
+ if (of_property_read_u32(client->dev.of_node, "trickle-resistor-ohms" , &ohms))
+ goto out;
+ if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
+ diode = false;
+ chip->trickle_charger_setup = chip->do_trickle_setup(client,
+ ohms, diode);
+out:
+ return;
+}
+
+static int ds1307_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds1307 *ds1307;
+ int err = -ENODEV;
+ int tmp;
+ struct chip_desc *chip = &chips[id->driver_data];
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ bool want_irq = false;
+ unsigned char *buf;
+ struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev);
+ static const int bbsqi_bitpos[] = {
+ [ds_1337] = 0,
+ [ds_1339] = DS1339_BIT_BBSQI,
+ [ds_3231] = DS3231_BIT_BBSQW,
+ };
+ const struct rtc_class_ops *rtc_ops = &ds13xx_rtc_ops;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)
+ && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EIO;
+
+ ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL);
+ if (!ds1307)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, ds1307);
+
+ ds1307->client = client;
+ ds1307->type = id->driver_data;
+
+ if (!pdata && client->dev.of_node)
+ ds1307_trickle_of_init(client, chip);
+ else if (pdata && pdata->trickle_charger_setup)
+ chip->trickle_charger_setup = pdata->trickle_charger_setup;
+
+ if (chip->trickle_charger_setup && chip->trickle_charger_reg) {
+ dev_dbg(&client->dev, "writing trickle charger info 0x%x to 0x%x\n",
+ DS13XX_TRICKLE_CHARGER_MAGIC | chip->trickle_charger_setup,
+ chip->trickle_charger_reg);
+ i2c_smbus_write_byte_data(client, chip->trickle_charger_reg,
+ DS13XX_TRICKLE_CHARGER_MAGIC |
+ chip->trickle_charger_setup);
+ }
+
+ buf = ds1307->regs;
+ if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ ds1307->read_block_data = ds1307_native_smbus_read_block_data;
+ ds1307->write_block_data = ds1307_native_smbus_write_block_data;
+ } else {
+ ds1307->read_block_data = ds1307_read_block_data;
+ ds1307->write_block_data = ds1307_write_block_data;
+ }
+
+ switch (ds1307->type) {
+ case ds_1337:
+ case ds_1339:
+ case ds_3231:
+ /* get registers that the "rtc" read below won't read... */
+ tmp = ds1307->read_block_data(ds1307->client,
+ DS1337_REG_CONTROL, 2, buf);
+ if (tmp != 2) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit;
+ }
+
+ /* oscillator off? turn it on, so clock can tick. */
+ if (ds1307->regs[0] & DS1337_BIT_nEOSC)
+ ds1307->regs[0] &= ~DS1337_BIT_nEOSC;
+
+ /*
+ * Using IRQ? Disable the square wave and both alarms.
+ * For some variants, be sure alarms can trigger when we're
+ * running on Vbackup (BBSQI/BBSQW)
+ */
+ if (ds1307->client->irq > 0 && chip->alarm) {
+ INIT_WORK(&ds1307->work, ds1307_work);
+
+ ds1307->regs[0] |= DS1337_BIT_INTCN
+ | bbsqi_bitpos[ds1307->type];
+ ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
+
+ want_irq = true;
+ }
+
+ i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
+ ds1307->regs[0]);
+
+ /* oscillator fault? clear flag, and warn */
+ if (ds1307->regs[1] & DS1337_BIT_OSF) {
+ i2c_smbus_write_byte_data(client, DS1337_REG_STATUS,
+ ds1307->regs[1] & ~DS1337_BIT_OSF);
+ dev_warn(&client->dev, "SET TIME!\n");
+ }
+ break;
+
+ case rx_8025:
+ tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
+ RX8025_REG_CTRL1 << 4 | 0x08, 2, buf);
+ if (tmp != 2) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit;
+ }
+
+ /* oscillator off? turn it on, so clock can tick. */
+ if (!(ds1307->regs[1] & RX8025_BIT_XST)) {
+ ds1307->regs[1] |= RX8025_BIT_XST;
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(&client->dev,
+ "oscillator stop detected - SET TIME!\n");
+ }
+
+ if (ds1307->regs[1] & RX8025_BIT_PON) {
+ ds1307->regs[1] &= ~RX8025_BIT_PON;
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(&client->dev, "power-on detected\n");
+ }
+
+ if (ds1307->regs[1] & RX8025_BIT_VDET) {
+ ds1307->regs[1] &= ~RX8025_BIT_VDET;
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL2 << 4 | 0x08,
+ ds1307->regs[1]);
+ dev_warn(&client->dev, "voltage drop detected\n");
+ }
+
+ /* make sure we are running in 24hour mode */
+ if (!(ds1307->regs[0] & RX8025_BIT_2412)) {
+ u8 hour;
+
+ /* switch to 24 hour mode */
+ i2c_smbus_write_byte_data(client,
+ RX8025_REG_CTRL1 << 4 | 0x08,
+ ds1307->regs[0] |
+ RX8025_BIT_2412);
+
+ tmp = i2c_smbus_read_i2c_block_data(ds1307->client,
+ RX8025_REG_CTRL1 << 4 | 0x08, 2, buf);
+ if (tmp != 2) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit;
+ }
+
+ /* correct hour */
+ hour = bcd2bin(ds1307->regs[DS1307_REG_HOUR]);
+ if (hour == 12)
+ hour = 0;
+ if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
+ hour += 12;
+
+ i2c_smbus_write_byte_data(client,
+ DS1307_REG_HOUR << 4 | 0x08,
+ hour);
+ }
+ break;
+ case ds_1388:
+ ds1307->offset = 1; /* Seconds starts at 1 */
+ break;
+ case mcp794xx:
+ rtc_ops = &mcp794xx_rtc_ops;
+ if (ds1307->client->irq > 0 && chip->alarm) {
+ INIT_WORK(&ds1307->work, mcp794xx_work);
+ want_irq = true;
+ }
+ break;
+ default:
+ break;
+ }
+
+read_rtc:
+ /* read RTC registers */
+ tmp = ds1307->read_block_data(ds1307->client, ds1307->offset, 8, buf);
+ if (tmp != 8) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit;
+ }
+
+ /*
+ * minimal sanity checking; some chips (like DS1340) don't
+ * specify the extra bits as must-be-zero, but there are
+ * still a few values that are clearly out-of-range.
+ */
+ tmp = ds1307->regs[DS1307_REG_SECS];
+ switch (ds1307->type) {
+ case ds_1307:
+ case m41t00:
+ /* clock halted? turn it on, so clock can tick. */
+ if (tmp & DS1307_BIT_CH) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+ dev_warn(&client->dev, "SET TIME!\n");
+ goto read_rtc;
+ }
+ break;
+ case ds_1338:
+ /* clock halted? turn it on, so clock can tick. */
+ if (tmp & DS1307_BIT_CH)
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+
+ /* oscillator fault? clear flag, and warn */
+ if (ds1307->regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_CONTROL,
+ ds1307->regs[DS1307_REG_CONTROL]
+ & ~DS1338_BIT_OSF);
+ dev_warn(&client->dev, "SET TIME!\n");
+ goto read_rtc;
+ }
+ break;
+ case ds_1340:
+ /* clock halted? turn it on, so clock can tick. */
+ if (tmp & DS1340_BIT_nEOSC)
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS, 0);
+
+ tmp = i2c_smbus_read_byte_data(client, DS1340_REG_FLAG);
+ if (tmp < 0) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit;
+ }
+
+ /* oscillator fault? clear flag, and warn */
+ if (tmp & DS1340_BIT_OSF) {
+ i2c_smbus_write_byte_data(client, DS1340_REG_FLAG, 0);
+ dev_warn(&client->dev, "SET TIME!\n");
+ }
+ break;
+ case mcp794xx:
+ /* make sure that the backup battery is enabled */
+ if (!(ds1307->regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_WDAY,
+ ds1307->regs[DS1307_REG_WDAY]
+ | MCP794XX_BIT_VBATEN);
+ }
+
+ /* clock halted? turn it on, so clock can tick. */
+ if (!(tmp & MCP794XX_BIT_ST)) {
+ i2c_smbus_write_byte_data(client, DS1307_REG_SECS,
+ MCP794XX_BIT_ST);
+ dev_warn(&client->dev, "SET TIME!\n");
+ goto read_rtc;
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ tmp = ds1307->regs[DS1307_REG_HOUR];
+ switch (ds1307->type) {
+ case ds_1340:
+ case m41t00:
+ /*
+ * NOTE: ignores century bits; fix before deploying
+ * systems that will run through year 2100.
+ */
+ break;
+ case rx_8025:
+ break;
+ default:
+ if (!(tmp & DS1307_BIT_12HR))
+ break;
+
+ /*
+ * Be sure we're in 24 hour mode. Multi-master systems
+ * take note...
+ */
+ tmp = bcd2bin(tmp & 0x1f);
+ if (tmp == 12)
+ tmp = 0;
+ if (ds1307->regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
+ tmp += 12;
+ i2c_smbus_write_byte_data(client,
+ ds1307->offset + DS1307_REG_HOUR,
+ bin2bcd(tmp));
+ }
+
+ device_set_wakeup_capable(&client->dev, want_irq);
+ ds1307->rtc = devm_rtc_device_register(&client->dev, client->name,
+ rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds1307->rtc)) {
+ return PTR_ERR(ds1307->rtc);
+ }
+
+ if (want_irq) {
+ err = request_irq(client->irq, ds1307_irq, IRQF_SHARED,
+ ds1307->rtc->name, client);
+ if (err) {
+ client->irq = 0;
+ dev_err(&client->dev, "unable to request IRQ!\n");
+ } else {
+
+ set_bit(HAS_ALARM, &ds1307->flags);
+ dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
+ }
+ }
+
+ if (chip->nvram_size) {
+
+ ds1307->nvram = devm_kzalloc(&client->dev,
+ sizeof(struct bin_attribute),
+ GFP_KERNEL);
+ if (!ds1307->nvram) {
+ dev_err(&client->dev, "cannot allocate memory for nvram sysfs\n");
+ } else {
+
+ ds1307->nvram->attr.name = "nvram";
+ ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR;
+
+ sysfs_bin_attr_init(ds1307->nvram);
+
+ ds1307->nvram->read = ds1307_nvram_read;
+ ds1307->nvram->write = ds1307_nvram_write;
+ ds1307->nvram->size = chip->nvram_size;
+ ds1307->nvram_offset = chip->nvram_offset;
+
+ err = sysfs_create_bin_file(&client->dev.kobj,
+ ds1307->nvram);
+ if (err) {
+ dev_err(&client->dev,
+ "unable to create sysfs file: %s\n",
+ ds1307->nvram->attr.name);
+ } else {
+ set_bit(HAS_NVRAM, &ds1307->flags);
+ dev_info(&client->dev, "%zu bytes nvram\n",
+ ds1307->nvram->size);
+ }
+ }
+ }
+
+ return 0;
+
+exit:
+ return err;
+}
+
+static int ds1307_remove(struct i2c_client *client)
+{
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
+
+ if (test_and_clear_bit(HAS_ALARM, &ds1307->flags)) {
+ free_irq(client->irq, client);
+ cancel_work_sync(&ds1307->work);
+ }
+
+ if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags))
+ sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram);
+
+ return 0;
+}
+
+static struct i2c_driver ds1307_driver = {
+ .driver = {
+ .name = "rtc-ds1307",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds1307_probe,
+ .remove = ds1307_remove,
+ .id_table = ds1307_id,
+};
+
+module_i2c_driver(ds1307_driver);
+
+MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c
new file mode 100644
index 000000000..ae9f99722
--- /dev/null
+++ b/drivers/rtc/rtc-ds1343.c
@@ -0,0 +1,760 @@
+/* rtc-ds1343.c
+ *
+ * Driver for Dallas Semiconductor DS1343 Low Current, SPI Compatible
+ * Real Time Clock
+ *
+ * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
+ * Ankur Srivastava <sankurece@gmail.com> : DS1343 Nvram Support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#define DS1343_DRV_VERSION "01.00"
+#define DALLAS_MAXIM_DS1343 0
+#define DALLAS_MAXIM_DS1344 1
+
+/* RTC DS1343 Registers */
+#define DS1343_SECONDS_REG 0x00
+#define DS1343_MINUTES_REG 0x01
+#define DS1343_HOURS_REG 0x02
+#define DS1343_DAY_REG 0x03
+#define DS1343_DATE_REG 0x04
+#define DS1343_MONTH_REG 0x05
+#define DS1343_YEAR_REG 0x06
+#define DS1343_ALM0_SEC_REG 0x07
+#define DS1343_ALM0_MIN_REG 0x08
+#define DS1343_ALM0_HOUR_REG 0x09
+#define DS1343_ALM0_DAY_REG 0x0A
+#define DS1343_ALM1_SEC_REG 0x0B
+#define DS1343_ALM1_MIN_REG 0x0C
+#define DS1343_ALM1_HOUR_REG 0x0D
+#define DS1343_ALM1_DAY_REG 0x0E
+#define DS1343_CONTROL_REG 0x0F
+#define DS1343_STATUS_REG 0x10
+#define DS1343_TRICKLE_REG 0x11
+#define DS1343_NVRAM 0x20
+
+#define DS1343_NVRAM_LEN 96
+
+/* DS1343 Control Registers bits */
+#define DS1343_EOSC 0x80
+#define DS1343_DOSF 0x20
+#define DS1343_EGFIL 0x10
+#define DS1343_SQW 0x08
+#define DS1343_INTCN 0x04
+#define DS1343_A1IE 0x02
+#define DS1343_A0IE 0x01
+
+/* DS1343 Status Registers bits */
+#define DS1343_OSF 0x80
+#define DS1343_IRQF1 0x02
+#define DS1343_IRQF0 0x01
+
+/* DS1343 Trickle Charger Registers bits */
+#define DS1343_TRICKLE_MAGIC 0xa0
+#define DS1343_TRICKLE_DS1 0x08
+#define DS1343_TRICKLE_1K 0x01
+#define DS1343_TRICKLE_2K 0x02
+#define DS1343_TRICKLE_4K 0x03
+
+static const struct spi_device_id ds1343_id[] = {
+ { "ds1343", DALLAS_MAXIM_DS1343 },
+ { "ds1344", DALLAS_MAXIM_DS1344 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ds1343_id);
+
+struct ds1343_priv {
+ struct spi_device *spi;
+ struct rtc_device *rtc;
+ struct regmap *map;
+ struct mutex mutex;
+ unsigned int irqen;
+ int irq;
+ int alarm_sec;
+ int alarm_min;
+ int alarm_hour;
+ int alarm_mday;
+};
+
+static int ds1343_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+#ifdef RTC_SET_CHARGE
+ case RTC_SET_CHARGE:
+ {
+ int val;
+
+ if (copy_from_user(&val, (int __user *)arg, sizeof(int)))
+ return -EFAULT;
+
+ return regmap_write(priv->map, DS1343_TRICKLE_REG, val);
+ }
+ break;
+#endif
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static ssize_t ds1343_show_glitchfilter(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int glitch_filt_status, data;
+
+ regmap_read(priv->map, DS1343_CONTROL_REG, &data);
+
+ glitch_filt_status = !!(data & DS1343_EGFIL);
+
+ if (glitch_filt_status)
+ return sprintf(buf, "enabled\n");
+ else
+ return sprintf(buf, "disabled\n");
+}
+
+static ssize_t ds1343_store_glitchfilter(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int data;
+
+ regmap_read(priv->map, DS1343_CONTROL_REG, &data);
+
+ if (strncmp(buf, "enabled", 7) == 0)
+ data |= DS1343_EGFIL;
+
+ else if (strncmp(buf, "disabled", 8) == 0)
+ data &= ~(DS1343_EGFIL);
+
+ else
+ return -EINVAL;
+
+ regmap_write(priv->map, DS1343_CONTROL_REG, data);
+
+ return count;
+}
+
+static DEVICE_ATTR(glitch_filter, S_IRUGO | S_IWUSR, ds1343_show_glitchfilter,
+ ds1343_store_glitchfilter);
+
+static ssize_t ds1343_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ unsigned char address;
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+
+ if ((count + off) > DS1343_NVRAM_LEN)
+ count = DS1343_NVRAM_LEN - off;
+
+ address = DS1343_NVRAM + off;
+
+ ret = regmap_bulk_write(priv->map, address, buf, count);
+ if (ret < 0)
+ dev_err(&priv->spi->dev, "Error in nvram write %d", ret);
+
+ return (ret < 0) ? ret : count;
+}
+
+
+static ssize_t ds1343_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ int ret;
+ unsigned char address;
+ struct device *dev = kobj_to_dev(kobj);
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ if (unlikely(!count))
+ return count;
+
+ if ((count + off) > DS1343_NVRAM_LEN)
+ count = DS1343_NVRAM_LEN - off;
+
+ address = DS1343_NVRAM + off;
+
+ ret = regmap_bulk_read(priv->map, address, buf, count);
+ if (ret < 0)
+ dev_err(&priv->spi->dev, "Error in nvram read %d\n", ret);
+
+ return (ret < 0) ? ret : count;
+}
+
+
+static struct bin_attribute nvram_attr = {
+ .attr.name = "nvram",
+ .attr.mode = S_IRUGO | S_IWUSR,
+ .read = ds1343_nvram_read,
+ .write = ds1343_nvram_write,
+ .size = DS1343_NVRAM_LEN,
+};
+
+static ssize_t ds1343_show_alarmstatus(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int alarmstatus, data;
+
+ regmap_read(priv->map, DS1343_CONTROL_REG, &data);
+
+ alarmstatus = !!(data & DS1343_A0IE);
+
+ if (alarmstatus)
+ return sprintf(buf, "enabled\n");
+ else
+ return sprintf(buf, "disabled\n");
+}
+
+static DEVICE_ATTR(alarm_status, S_IRUGO, ds1343_show_alarmstatus, NULL);
+
+static ssize_t ds1343_show_alarmmode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int alarm_mode, data;
+ char *alarm_str;
+
+ regmap_read(priv->map, DS1343_ALM0_SEC_REG, &data);
+ alarm_mode = (data & 0x80) >> 4;
+
+ regmap_read(priv->map, DS1343_ALM0_MIN_REG, &data);
+ alarm_mode |= (data & 0x80) >> 5;
+
+ regmap_read(priv->map, DS1343_ALM0_HOUR_REG, &data);
+ alarm_mode |= (data & 0x80) >> 6;
+
+ regmap_read(priv->map, DS1343_ALM0_DAY_REG, &data);
+ alarm_mode |= (data & 0x80) >> 7;
+
+ switch (alarm_mode) {
+ case 15:
+ alarm_str = "each second";
+ break;
+
+ case 7:
+ alarm_str = "seconds match";
+ break;
+
+ case 3:
+ alarm_str = "minutes and seconds match";
+ break;
+
+ case 1:
+ alarm_str = "hours, minutes and seconds match";
+ break;
+
+ case 0:
+ alarm_str = "day, hours, minutes and seconds match";
+ break;
+
+ default:
+ alarm_str = "invalid";
+ break;
+ }
+
+ return sprintf(buf, "%s\n", alarm_str);
+}
+
+static DEVICE_ATTR(alarm_mode, S_IRUGO, ds1343_show_alarmmode, NULL);
+
+static ssize_t ds1343_show_tricklecharger(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int data;
+ char *diodes = "disabled", *resistors = " ";
+
+ regmap_read(priv->map, DS1343_TRICKLE_REG, &data);
+
+ if ((data & 0xf0) == DS1343_TRICKLE_MAGIC) {
+ switch (data & 0x0c) {
+ case DS1343_TRICKLE_DS1:
+ diodes = "one diode,";
+ break;
+
+ default:
+ diodes = "no diode,";
+ break;
+ }
+
+ switch (data & 0x03) {
+ case DS1343_TRICKLE_1K:
+ resistors = "1k Ohm";
+ break;
+
+ case DS1343_TRICKLE_2K:
+ resistors = "2k Ohm";
+ break;
+
+ case DS1343_TRICKLE_4K:
+ resistors = "4k Ohm";
+ break;
+
+ default:
+ diodes = "disabled";
+ break;
+ }
+ }
+
+ return sprintf(buf, "%s %s\n", diodes, resistors);
+}
+
+static DEVICE_ATTR(trickle_charger, S_IRUGO, ds1343_show_tricklecharger, NULL);
+
+static int ds1343_sysfs_register(struct device *dev)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int err;
+
+ err = device_create_file(dev, &dev_attr_glitch_filter);
+ if (err)
+ return err;
+
+ err = device_create_file(dev, &dev_attr_trickle_charger);
+ if (err)
+ goto error1;
+
+ err = device_create_bin_file(dev, &nvram_attr);
+ if (err)
+ goto error2;
+
+ if (priv->irq <= 0)
+ return err;
+
+ err = device_create_file(dev, &dev_attr_alarm_mode);
+ if (err)
+ goto error3;
+
+ err = device_create_file(dev, &dev_attr_alarm_status);
+ if (!err)
+ return err;
+
+ device_remove_file(dev, &dev_attr_alarm_mode);
+
+error3:
+ device_remove_bin_file(dev, &nvram_attr);
+
+error2:
+ device_remove_file(dev, &dev_attr_trickle_charger);
+
+error1:
+ device_remove_file(dev, &dev_attr_glitch_filter);
+
+ return err;
+}
+
+static void ds1343_sysfs_unregister(struct device *dev)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+
+ device_remove_file(dev, &dev_attr_glitch_filter);
+ device_remove_file(dev, &dev_attr_trickle_charger);
+ device_remove_bin_file(dev, &nvram_attr);
+
+ if (priv->irq <= 0)
+ return;
+
+ device_remove_file(dev, &dev_attr_alarm_status);
+ device_remove_file(dev, &dev_attr_alarm_mode);
+}
+
+static int ds1343_read_time(struct device *dev, struct rtc_time *dt)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ unsigned char buf[7];
+ int res;
+
+ res = regmap_bulk_read(priv->map, DS1343_SECONDS_REG, buf, 7);
+ if (res)
+ return res;
+
+ dt->tm_sec = bcd2bin(buf[0]);
+ dt->tm_min = bcd2bin(buf[1]);
+ dt->tm_hour = bcd2bin(buf[2] & 0x3F);
+ dt->tm_wday = bcd2bin(buf[3]) - 1;
+ dt->tm_mday = bcd2bin(buf[4]);
+ dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1;
+ dt->tm_year = bcd2bin(buf[6]) + 100; /* year offset from 1900 */
+
+ return rtc_valid_tm(dt);
+}
+
+static int ds1343_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int res;
+
+ res = regmap_write(priv->map, DS1343_SECONDS_REG,
+ bin2bcd(dt->tm_sec));
+ if (res)
+ return res;
+
+ res = regmap_write(priv->map, DS1343_MINUTES_REG,
+ bin2bcd(dt->tm_min));
+ if (res)
+ return res;
+
+ res = regmap_write(priv->map, DS1343_HOURS_REG,
+ bin2bcd(dt->tm_hour) & 0x3F);
+ if (res)
+ return res;
+
+ res = regmap_write(priv->map, DS1343_DAY_REG,
+ bin2bcd(dt->tm_wday + 1));
+ if (res)
+ return res;
+
+ res = regmap_write(priv->map, DS1343_DATE_REG,
+ bin2bcd(dt->tm_mday));
+ if (res)
+ return res;
+
+ res = regmap_write(priv->map, DS1343_MONTH_REG,
+ bin2bcd(dt->tm_mon + 1));
+ if (res)
+ return res;
+
+ dt->tm_year %= 100;
+
+ res = regmap_write(priv->map, DS1343_YEAR_REG,
+ bin2bcd(dt->tm_year));
+ if (res)
+ return res;
+
+ return 0;
+}
+
+static int ds1343_update_alarm(struct device *dev)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ unsigned int control, stat;
+ unsigned char buf[4];
+ int res = 0;
+
+ res = regmap_read(priv->map, DS1343_CONTROL_REG, &control);
+ if (res)
+ return res;
+
+ res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
+ if (res)
+ return res;
+
+ control &= ~(DS1343_A0IE);
+ stat &= ~(DS1343_IRQF0);
+
+ res = regmap_write(priv->map, DS1343_CONTROL_REG, control);
+ if (res)
+ return res;
+
+ res = regmap_write(priv->map, DS1343_STATUS_REG, stat);
+ if (res)
+ return res;
+
+ buf[0] = priv->alarm_sec < 0 || (priv->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(priv->alarm_sec) & 0x7F;
+ buf[1] = priv->alarm_min < 0 || (priv->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(priv->alarm_min) & 0x7F;
+ buf[2] = priv->alarm_hour < 0 || (priv->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(priv->alarm_hour) & 0x3F;
+ buf[3] = priv->alarm_mday < 0 || (priv->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(priv->alarm_mday) & 0x7F;
+
+ res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
+ if (res)
+ return res;
+
+ if (priv->irqen) {
+ control |= DS1343_A0IE;
+ res = regmap_write(priv->map, DS1343_CONTROL_REG, control);
+ }
+
+ return res;
+}
+
+static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int res = 0;
+ unsigned int stat;
+
+ if (priv->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->mutex);
+
+ res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
+ if (res)
+ goto out;
+
+ alarm->enabled = !!(priv->irqen & RTC_AF);
+ alarm->pending = !!(stat & DS1343_IRQF0);
+
+ alarm->time.tm_sec = priv->alarm_sec < 0 ? 0 : priv->alarm_sec;
+ alarm->time.tm_min = priv->alarm_min < 0 ? 0 : priv->alarm_min;
+ alarm->time.tm_hour = priv->alarm_hour < 0 ? 0 : priv->alarm_hour;
+ alarm->time.tm_mday = priv->alarm_mday < 0 ? 0 : priv->alarm_mday;
+
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_year = -1;
+ alarm->time.tm_wday = -1;
+ alarm->time.tm_yday = -1;
+ alarm->time.tm_isdst = -1;
+
+out:
+ mutex_unlock(&priv->mutex);
+ return res;
+}
+
+static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int res = 0;
+
+ if (priv->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->mutex);
+
+ priv->alarm_sec = alarm->time.tm_sec;
+ priv->alarm_min = alarm->time.tm_min;
+ priv->alarm_hour = alarm->time.tm_hour;
+ priv->alarm_mday = alarm->time.tm_mday;
+
+ if (alarm->enabled)
+ priv->irqen |= RTC_AF;
+
+ res = ds1343_update_alarm(dev);
+
+ mutex_unlock(&priv->mutex);
+
+ return res;
+}
+
+static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1343_priv *priv = dev_get_drvdata(dev);
+ int res = 0;
+
+ if (priv->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&priv->mutex);
+
+ if (enabled)
+ priv->irqen |= RTC_AF;
+ else
+ priv->irqen &= ~RTC_AF;
+
+ res = ds1343_update_alarm(dev);
+
+ mutex_unlock(&priv->mutex);
+
+ return res;
+}
+
+static irqreturn_t ds1343_thread(int irq, void *dev_id)
+{
+ struct ds1343_priv *priv = dev_id;
+ unsigned int stat, control;
+ int res = 0;
+
+ mutex_lock(&priv->mutex);
+
+ res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
+ if (res)
+ goto out;
+
+ if (stat & DS1343_IRQF0) {
+ stat &= ~DS1343_IRQF0;
+ regmap_write(priv->map, DS1343_STATUS_REG, stat);
+
+ res = regmap_read(priv->map, DS1343_CONTROL_REG, &control);
+ if (res)
+ goto out;
+
+ control &= ~DS1343_A0IE;
+ regmap_write(priv->map, DS1343_CONTROL_REG, control);
+
+ rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ mutex_unlock(&priv->mutex);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops ds1343_rtc_ops = {
+ .ioctl = ds1343_ioctl,
+ .read_time = ds1343_read_time,
+ .set_time = ds1343_set_time,
+ .read_alarm = ds1343_read_alarm,
+ .set_alarm = ds1343_set_alarm,
+ .alarm_irq_enable = ds1343_alarm_irq_enable,
+};
+
+static int ds1343_probe(struct spi_device *spi)
+{
+ struct ds1343_priv *priv;
+ struct regmap_config config;
+ unsigned int data;
+ int res;
+
+ memset(&config, 0, sizeof(config));
+ config.reg_bits = 8;
+ config.val_bits = 8;
+ config.write_flag_mask = 0x80;
+
+ priv = devm_kzalloc(&spi->dev, sizeof(struct ds1343_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->spi = spi;
+ mutex_init(&priv->mutex);
+
+ /* RTC DS1347 works in spi mode 3 and
+ * its chip select is active high
+ */
+ spi->mode = SPI_MODE_3 | SPI_CS_HIGH;
+ spi->bits_per_word = 8;
+ res = spi_setup(spi);
+ if (res)
+ return res;
+
+ spi_set_drvdata(spi, priv);
+
+ priv->map = devm_regmap_init_spi(spi, &config);
+
+ if (IS_ERR(priv->map)) {
+ dev_err(&spi->dev, "spi regmap init failed for rtc ds1343\n");
+ return PTR_ERR(priv->map);
+ }
+
+ res = regmap_read(priv->map, DS1343_SECONDS_REG, &data);
+ if (res)
+ return res;
+
+ regmap_read(priv->map, DS1343_CONTROL_REG, &data);
+ data |= DS1343_INTCN;
+ data &= ~(DS1343_EOSC | DS1343_A1IE | DS1343_A0IE);
+ regmap_write(priv->map, DS1343_CONTROL_REG, data);
+
+ regmap_read(priv->map, DS1343_STATUS_REG, &data);
+ data &= ~(DS1343_OSF | DS1343_IRQF1 | DS1343_IRQF0);
+ regmap_write(priv->map, DS1343_STATUS_REG, data);
+
+ priv->rtc = devm_rtc_device_register(&spi->dev, "ds1343",
+ &ds1343_rtc_ops, THIS_MODULE);
+ if (IS_ERR(priv->rtc)) {
+ dev_err(&spi->dev, "unable to register rtc ds1343\n");
+ return PTR_ERR(priv->rtc);
+ }
+
+ priv->irq = spi->irq;
+
+ if (priv->irq >= 0) {
+ res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
+ ds1343_thread,
+ IRQF_NO_SUSPEND | IRQF_ONESHOT,
+ "ds1343", priv);
+ if (res) {
+ priv->irq = -1;
+ dev_err(&spi->dev,
+ "unable to request irq for rtc ds1343\n");
+ } else {
+ device_set_wakeup_capable(&spi->dev, 1);
+ }
+ }
+
+ res = ds1343_sysfs_register(&spi->dev);
+ if (res)
+ dev_err(&spi->dev,
+ "unable to create sysfs entries for rtc ds1343\n");
+
+ return 0;
+}
+
+static int ds1343_remove(struct spi_device *spi)
+{
+ struct ds1343_priv *priv = spi_get_drvdata(spi);
+
+ if (spi->irq) {
+ mutex_lock(&priv->mutex);
+ priv->irqen &= ~RTC_AF;
+ mutex_unlock(&priv->mutex);
+
+ devm_free_irq(&spi->dev, spi->irq, priv);
+ }
+
+ spi_set_drvdata(spi, NULL);
+
+ ds1343_sysfs_unregister(&spi->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int ds1343_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ if (spi->irq >= 0 && device_may_wakeup(dev))
+ enable_irq_wake(spi->irq);
+
+ return 0;
+}
+
+static int ds1343_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ if (spi->irq >= 0 && device_may_wakeup(dev))
+ disable_irq_wake(spi->irq);
+
+ return 0;
+}
+
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1343_pm, ds1343_suspend, ds1343_resume);
+
+static struct spi_driver ds1343_driver = {
+ .driver = {
+ .name = "ds1343",
+ .owner = THIS_MODULE,
+ .pm = &ds1343_pm,
+ },
+ .probe = ds1343_probe,
+ .remove = ds1343_remove,
+ .id_table = ds1343_id,
+};
+
+module_spi_driver(ds1343_driver);
+
+MODULE_DESCRIPTION("DS1343 RTC SPI Driver");
+MODULE_AUTHOR("Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>,"
+ "Ankur Srivastava <sankurece@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DS1343_DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c
new file mode 100644
index 000000000..c82b4c050
--- /dev/null
+++ b/drivers/rtc/rtc-ds1347.c
@@ -0,0 +1,166 @@
+/* rtc-ds1347.c
+ *
+ * Driver for Dallas Semiconductor DS1347 Low Current, SPI Compatible
+ * Real Time Clock
+ *
+ * Author : Raghavendra Chandra Ganiga <ravi23ganiga@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+/* Registers in ds1347 rtc */
+
+#define DS1347_SECONDS_REG 0x01
+#define DS1347_MINUTES_REG 0x03
+#define DS1347_HOURS_REG 0x05
+#define DS1347_DATE_REG 0x07
+#define DS1347_MONTH_REG 0x09
+#define DS1347_DAY_REG 0x0B
+#define DS1347_YEAR_REG 0x0D
+#define DS1347_CONTROL_REG 0x0F
+#define DS1347_STATUS_REG 0x17
+#define DS1347_CLOCK_BURST 0x3F
+
+static int ds1347_read_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ *data = address | 0x80;
+
+ return spi_write_then_read(spi, data, 1, data, 1);
+}
+
+static int ds1347_write_reg(struct device *dev, unsigned char address,
+ unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ buf[0] = address & 0x7F;
+ buf[1] = data;
+
+ return spi_write_then_read(spi, buf, 2, NULL, 0);
+}
+
+static int ds1347_read_time(struct device *dev, struct rtc_time *dt)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int err;
+ unsigned char buf[8];
+
+ buf[0] = DS1347_CLOCK_BURST | 0x80;
+
+ err = spi_write_then_read(spi, buf, 1, buf, 8);
+ if (err)
+ return err;
+
+ dt->tm_sec = bcd2bin(buf[0]);
+ dt->tm_min = bcd2bin(buf[1]);
+ dt->tm_hour = bcd2bin(buf[2] & 0x3F);
+ dt->tm_mday = bcd2bin(buf[3]);
+ dt->tm_mon = bcd2bin(buf[4]) - 1;
+ dt->tm_wday = bcd2bin(buf[5]) - 1;
+ dt->tm_year = bcd2bin(buf[6]) + 100;
+
+ return rtc_valid_tm(dt);
+}
+
+static int ds1347_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[9];
+
+ buf[0] = DS1347_CLOCK_BURST & 0x7F;
+ buf[1] = bin2bcd(dt->tm_sec);
+ buf[2] = bin2bcd(dt->tm_min);
+ buf[3] = (bin2bcd(dt->tm_hour) & 0x3F);
+ buf[4] = bin2bcd(dt->tm_mday);
+ buf[5] = bin2bcd(dt->tm_mon + 1);
+ buf[6] = bin2bcd(dt->tm_wday + 1);
+
+ /* year in linux is from 1900 i.e in range of 100
+ in rtc it is from 00 to 99 */
+ dt->tm_year = dt->tm_year % 100;
+
+ buf[7] = bin2bcd(dt->tm_year);
+ buf[8] = bin2bcd(0x00);
+
+ /* write the rtc settings */
+ return spi_write_then_read(spi, buf, 9, NULL, 0);
+}
+
+static const struct rtc_class_ops ds1347_rtc_ops = {
+ .read_time = ds1347_read_time,
+ .set_time = ds1347_set_time,
+};
+
+static int ds1347_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ unsigned char data;
+ int res;
+
+ /* spi setup with ds1347 in mode 3 and bits per word as 8 */
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ /* RTC Settings */
+ res = ds1347_read_reg(&spi->dev, DS1347_SECONDS_REG, &data);
+ if (res)
+ return res;
+
+ /* Disable the write protect of rtc */
+ ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+ data = data & ~(1<<7);
+ ds1347_write_reg(&spi->dev, DS1347_CONTROL_REG, data);
+
+ /* Enable the oscillator , disable the oscillator stop flag,
+ and glitch filter to reduce current consumption */
+ ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+ data = data & 0x1B;
+ ds1347_write_reg(&spi->dev, DS1347_STATUS_REG, data);
+
+ /* display the settings */
+ ds1347_read_reg(&spi->dev, DS1347_CONTROL_REG, &data);
+ dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data);
+
+ ds1347_read_reg(&spi->dev, DS1347_STATUS_REG, &data);
+ dev_info(&spi->dev, "DS1347 RTC Status Reg = 0x%02x\n", data);
+
+ rtc = devm_rtc_device_register(&spi->dev, "ds1347",
+ &ds1347_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static struct spi_driver ds1347_driver = {
+ .driver = {
+ .name = "ds1347",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds1347_probe,
+};
+
+module_spi_driver(ds1347_driver);
+
+MODULE_DESCRIPTION("DS1347 SPI RTC DRIVER");
+MODULE_AUTHOR("Raghavendra C Ganiga <ravi23ganiga@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
new file mode 100644
index 000000000..167783fa7
--- /dev/null
+++ b/drivers/rtc/rtc-ds1374.c
@@ -0,0 +1,724 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS1374 Real-Time Clock over I2C
+ *
+ * Based on code by Randy Vinson <rvinson@mvista.com>,
+ * which was based on the m41t00.c by Mark Greer <mgreer@mvista.com>.
+ *
+ * Copyright (C) 2014 Rose Technology
+ * Copyright (C) 2006-2007 Freescale Semiconductor
+ *
+ * 2005 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#ifdef CONFIG_RTC_DRV_DS1374_WDT
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+#endif
+
+#define DS1374_REG_TOD0 0x00 /* Time of Day */
+#define DS1374_REG_TOD1 0x01
+#define DS1374_REG_TOD2 0x02
+#define DS1374_REG_TOD3 0x03
+#define DS1374_REG_WDALM0 0x04 /* Watchdog/Alarm */
+#define DS1374_REG_WDALM1 0x05
+#define DS1374_REG_WDALM2 0x06
+#define DS1374_REG_CR 0x07 /* Control */
+#define DS1374_REG_CR_AIE 0x01 /* Alarm Int. Enable */
+#define DS1374_REG_CR_WDALM 0x20 /* 1=Watchdog, 0=Alarm */
+#define DS1374_REG_CR_WACE 0x40 /* WD/Alarm counter enable */
+#define DS1374_REG_SR 0x08 /* Status */
+#define DS1374_REG_SR_OSF 0x80 /* Oscillator Stop Flag */
+#define DS1374_REG_SR_AF 0x01 /* Alarm Flag */
+#define DS1374_REG_TCR 0x09 /* Trickle Charge */
+
+static const struct i2c_device_id ds1374_id[] = {
+ { "ds1374", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds1374_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id ds1374_of_match[] = {
+ { .compatible = "dallas,ds1374" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ds1374_of_match);
+#endif
+
+struct ds1374 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ int exiting;
+};
+
+static struct i2c_driver ds1374_driver;
+
+static int ds1374_read_rtc(struct i2c_client *client, u32 *time,
+ int reg, int nbytes)
+{
+ u8 buf[4];
+ int ret;
+ int i;
+
+ if (nbytes > 4) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, nbytes, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < nbytes)
+ return -EIO;
+
+ for (i = nbytes - 1, *time = 0; i >= 0; i--)
+ *time = (*time << 8) | buf[i];
+
+ return 0;
+}
+
+static int ds1374_write_rtc(struct i2c_client *client, u32 time,
+ int reg, int nbytes)
+{
+ u8 buf[4];
+ int i;
+
+ if (nbytes > 4) {
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ buf[i] = time & 0xff;
+ time >>= 8;
+ }
+
+ return i2c_smbus_write_i2c_block_data(client, reg, nbytes, buf);
+}
+
+static int ds1374_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS1374_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, time unreliable\n");
+
+ stat &= ~(DS1374_REG_SR_OSF | DS1374_REG_SR_AF);
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
+ return i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
+}
+
+static int ds1374_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u32 itime;
+ int ret;
+
+ ret = ds1374_read_rtc(client, &itime, DS1374_REG_TOD0, 4);
+ if (!ret)
+ rtc_time_to_tm(itime, time);
+
+ return ret;
+}
+
+static int ds1374_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned long itime;
+
+ rtc_tm_to_time(time, &itime);
+ return ds1374_write_rtc(client, itime, DS1374_REG_TOD0, 4);
+}
+
+#ifndef CONFIG_RTC_DRV_DS1374_WDT
+/* The ds1374 has a decrementer for an alarm, rather than a comparator.
+ * If the time of day is changed, then the alarm will need to be
+ * reset.
+ */
+static int ds1374_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ u32 now, cur_alarm;
+ int cr, sr;
+ int ret = 0;
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&ds1374->mutex);
+
+ cr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ sr = ret = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ if (ret < 0)
+ goto out;
+
+ ret = ds1374_read_rtc(client, &now, DS1374_REG_TOD0, 4);
+ if (ret)
+ goto out;
+
+ ret = ds1374_read_rtc(client, &cur_alarm, DS1374_REG_WDALM0, 3);
+ if (ret)
+ goto out;
+
+ rtc_time_to_tm(now + cur_alarm, &alarm->time);
+ alarm->enabled = !!(cr & DS1374_REG_CR_WACE);
+ alarm->pending = !!(sr & DS1374_REG_SR_AF);
+
+out:
+ mutex_unlock(&ds1374->mutex);
+ return ret;
+}
+
+static int ds1374_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ struct rtc_time now;
+ unsigned long new_alarm, itime;
+ int cr;
+ int ret = 0;
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ ret = ds1374_read_time(dev, &now);
+ if (ret < 0)
+ return ret;
+
+ rtc_tm_to_time(&alarm->time, &new_alarm);
+ rtc_tm_to_time(&now, &itime);
+
+ /* This can happen due to races, in addition to dates that are
+ * truly in the past. To avoid requiring the caller to check for
+ * races, dates in the past are assumed to be in the recent past
+ * (i.e. not something that we'd rather the caller know about via
+ * an error), and the alarm is set to go off as soon as possible.
+ */
+ if (time_before_eq(new_alarm, itime))
+ new_alarm = 1;
+ else
+ new_alarm -= itime;
+
+ mutex_lock(&ds1374->mutex);
+
+ ret = cr = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ /* Disable any existing alarm before setting the new one
+ * (or lack thereof). */
+ cr &= ~DS1374_REG_CR_WACE;
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
+ if (ret < 0)
+ goto out;
+
+ ret = ds1374_write_rtc(client, new_alarm, DS1374_REG_WDALM0, 3);
+ if (ret)
+ goto out;
+
+ if (alarm->enabled) {
+ cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
+ cr &= ~DS1374_REG_CR_WDALM;
+
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, cr);
+ }
+
+out:
+ mutex_unlock(&ds1374->mutex);
+ return ret;
+}
+#endif
+
+static irqreturn_t ds1374_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&ds1374->work);
+ return IRQ_HANDLED;
+}
+
+static void ds1374_work(struct work_struct *work)
+{
+ struct ds1374 *ds1374 = container_of(work, struct ds1374, work);
+ struct i2c_client *client = ds1374->client;
+ int stat, control;
+
+ mutex_lock(&ds1374->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS1374_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS1374_REG_SR_AF) {
+ stat &= ~DS1374_REG_SR_AF;
+ i2c_smbus_write_byte_data(client, DS1374_REG_SR, stat);
+
+ control = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (control < 0)
+ goto out;
+
+ control &= ~(DS1374_REG_CR_WACE | DS1374_REG_CR_AIE);
+ i2c_smbus_write_byte_data(client, DS1374_REG_CR, control);
+
+ rtc_update_irq(ds1374->rtc, 1, RTC_AF | RTC_IRQF);
+ }
+
+out:
+ if (!ds1374->exiting)
+ enable_irq(client->irq);
+unlock:
+ mutex_unlock(&ds1374->mutex);
+}
+
+#ifndef CONFIG_RTC_DRV_DS1374_WDT
+static int ds1374_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+ int ret;
+
+ mutex_lock(&ds1374->mutex);
+
+ ret = i2c_smbus_read_byte_data(client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ if (enabled) {
+ ret |= DS1374_REG_CR_WACE | DS1374_REG_CR_AIE;
+ ret &= ~DS1374_REG_CR_WDALM;
+ } else {
+ ret &= ~DS1374_REG_CR_WACE;
+ }
+ ret = i2c_smbus_write_byte_data(client, DS1374_REG_CR, ret);
+
+out:
+ mutex_unlock(&ds1374->mutex);
+ return ret;
+}
+#endif
+
+static const struct rtc_class_ops ds1374_rtc_ops = {
+ .read_time = ds1374_read_time,
+ .set_time = ds1374_set_time,
+#ifndef CONFIG_RTC_DRV_DS1374_WDT
+ .read_alarm = ds1374_read_alarm,
+ .set_alarm = ds1374_set_alarm,
+ .alarm_irq_enable = ds1374_alarm_irq_enable,
+#endif
+};
+
+#ifdef CONFIG_RTC_DRV_DS1374_WDT
+/*
+ *****************************************************************************
+ *
+ * Watchdog Driver
+ *
+ *****************************************************************************
+ */
+static struct i2c_client *save_client;
+/* Default margin */
+#define WD_TIMO 131762
+
+#define DRV_NAME "DS1374 Watchdog"
+
+static int wdt_margin = WD_TIMO;
+static unsigned long wdt_is_open;
+module_param(wdt_margin, int, 0);
+MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 32s)");
+
+static const struct watchdog_info ds1374_wdt_info = {
+ .identity = "DS1374 WTD",
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+ WDIOF_MAGICCLOSE,
+};
+
+static int ds1374_wdt_settimeout(unsigned int timeout)
+{
+ int ret = -ENOIOCTLCMD;
+ int cr;
+
+ ret = cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
+ if (ret < 0)
+ goto out;
+
+ /* Disable any existing watchdog/alarm before setting the new one */
+ cr &= ~DS1374_REG_CR_WACE;
+
+ ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
+ if (ret < 0)
+ goto out;
+
+ /* Set new watchdog time */
+ ret = ds1374_write_rtc(save_client, timeout, DS1374_REG_WDALM0, 3);
+ if (ret) {
+ pr_info("couldn't set new watchdog time\n");
+ goto out;
+ }
+
+ /* Enable watchdog timer */
+ cr |= DS1374_REG_CR_WACE | DS1374_REG_CR_WDALM;
+ cr &= ~DS1374_REG_CR_AIE;
+
+ ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
+ if (ret < 0)
+ goto out;
+
+ return 0;
+out:
+ return ret;
+}
+
+
+/*
+ * Reload the watchdog timer. (ie, pat the watchdog)
+ */
+static void ds1374_wdt_ping(void)
+{
+ u32 val;
+ int ret = 0;
+
+ ret = ds1374_read_rtc(save_client, &val, DS1374_REG_WDALM0, 3);
+ if (ret)
+ pr_info("WD TICK FAIL!!!!!!!!!! %i\n", ret);
+}
+
+static void ds1374_wdt_disable(void)
+{
+ int ret = -ENOIOCTLCMD;
+ int cr;
+
+ cr = i2c_smbus_read_byte_data(save_client, DS1374_REG_CR);
+ /* Disable watchdog timer */
+ cr &= ~DS1374_REG_CR_WACE;
+
+ ret = i2c_smbus_write_byte_data(save_client, DS1374_REG_CR, cr);
+}
+
+/*
+ * Watchdog device is opened, and watchdog starts running.
+ */
+static int ds1374_wdt_open(struct inode *inode, struct file *file)
+{
+ struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
+
+ if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
+ mutex_lock(&ds1374->mutex);
+ if (test_and_set_bit(0, &wdt_is_open)) {
+ mutex_unlock(&ds1374->mutex);
+ return -EBUSY;
+ }
+ /*
+ * Activate
+ */
+ wdt_is_open = 1;
+ mutex_unlock(&ds1374->mutex);
+ return nonseekable_open(inode, file);
+ }
+ return -ENODEV;
+}
+
+/*
+ * Close the watchdog device.
+ */
+static int ds1374_wdt_release(struct inode *inode, struct file *file)
+{
+ if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
+ clear_bit(0, &wdt_is_open);
+
+ return 0;
+}
+
+/*
+ * Pat the watchdog whenever device is written to.
+ */
+static ssize_t ds1374_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len) {
+ ds1374_wdt_ping();
+ return 1;
+ }
+ return 0;
+}
+
+static ssize_t ds1374_wdt_read(struct file *file, char __user *data,
+ size_t len, loff_t *ppos)
+{
+ return 0;
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long ds1374_wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_margin, options;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg,
+ &ds1374_wdt_info, sizeof(ds1374_wdt_info)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ ds1374_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+
+ if (new_margin < 1 || new_margin > 16777216)
+ return -EINVAL;
+
+ wdt_margin = new_margin;
+ ds1374_wdt_settimeout(new_margin);
+ ds1374_wdt_ping();
+ /* fallthrough */
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_margin, (int __user *)arg);
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&options, (int __user *)arg, sizeof(int)))
+ return -EFAULT;
+
+ if (options & WDIOS_DISABLECARD) {
+ pr_info("disable watchdog\n");
+ ds1374_wdt_disable();
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ pr_info("enable watchdog\n");
+ ds1374_wdt_settimeout(wdt_margin);
+ ds1374_wdt_ping();
+ }
+
+ return -EINVAL;
+ }
+ return -ENOTTY;
+}
+
+static long ds1374_wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct ds1374 *ds1374 = i2c_get_clientdata(save_client);
+
+ mutex_lock(&ds1374->mutex);
+ ret = ds1374_wdt_ioctl(file, cmd, arg);
+ mutex_unlock(&ds1374->mutex);
+
+ return ret;
+}
+
+static int ds1374_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ /* Disable Watchdog */
+ ds1374_wdt_disable();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations ds1374_wdt_fops = {
+ .owner = THIS_MODULE,
+ .read = ds1374_wdt_read,
+ .unlocked_ioctl = ds1374_wdt_unlocked_ioctl,
+ .write = ds1374_wdt_write,
+ .open = ds1374_wdt_open,
+ .release = ds1374_wdt_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice ds1374_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &ds1374_wdt_fops,
+};
+
+static struct notifier_block ds1374_wdt_notifier = {
+ .notifier_call = ds1374_wdt_notify_sys,
+};
+
+#endif /*CONFIG_RTC_DRV_DS1374_WDT*/
+/*
+ *****************************************************************************
+ *
+ * Driver Interface
+ *
+ *****************************************************************************
+ */
+static int ds1374_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds1374 *ds1374;
+ int ret;
+
+ ds1374 = devm_kzalloc(&client->dev, sizeof(struct ds1374), GFP_KERNEL);
+ if (!ds1374)
+ return -ENOMEM;
+
+ ds1374->client = client;
+ i2c_set_clientdata(client, ds1374);
+
+ INIT_WORK(&ds1374->work, ds1374_work);
+ mutex_init(&ds1374->mutex);
+
+ ret = ds1374_check_rtc_status(client);
+ if (ret)
+ return ret;
+
+ if (client->irq > 0) {
+ ret = devm_request_irq(&client->dev, client->irq, ds1374_irq, 0,
+ "ds1374", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ return ret;
+ }
+
+ device_set_wakeup_capable(&client->dev, 1);
+ }
+
+ ds1374->rtc = devm_rtc_device_register(&client->dev, client->name,
+ &ds1374_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ds1374->rtc)) {
+ dev_err(&client->dev, "unable to register the class device\n");
+ return PTR_ERR(ds1374->rtc);
+ }
+
+#ifdef CONFIG_RTC_DRV_DS1374_WDT
+ save_client = client;
+ ret = misc_register(&ds1374_miscdev);
+ if (ret)
+ return ret;
+ ret = register_reboot_notifier(&ds1374_wdt_notifier);
+ if (ret) {
+ misc_deregister(&ds1374_miscdev);
+ return ret;
+ }
+ ds1374_wdt_settimeout(131072);
+#endif
+
+ return 0;
+}
+
+static int ds1374_remove(struct i2c_client *client)
+{
+ struct ds1374 *ds1374 = i2c_get_clientdata(client);
+#ifdef CONFIG_RTC_DRV_DS1374_WDT
+ int res;
+
+ res = misc_deregister(&ds1374_miscdev);
+ if (!res)
+ ds1374_miscdev.parent = NULL;
+ unregister_reboot_notifier(&ds1374_wdt_notifier);
+#endif
+
+ if (client->irq > 0) {
+ mutex_lock(&ds1374->mutex);
+ ds1374->exiting = 1;
+ mutex_unlock(&ds1374->mutex);
+
+ devm_free_irq(&client->dev, client->irq, client);
+ cancel_work_sync(&ds1374->work);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ds1374_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (client->irq >= 0 && device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+ return 0;
+}
+
+static int ds1374_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (client->irq >= 0 && device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ds1374_pm, ds1374_suspend, ds1374_resume);
+
+static struct i2c_driver ds1374_driver = {
+ .driver = {
+ .name = "rtc-ds1374",
+ .owner = THIS_MODULE,
+ .pm = &ds1374_pm,
+ },
+ .probe = ds1374_probe,
+ .remove = ds1374_remove,
+ .id_table = ds1374_id,
+};
+
+module_i2c_driver(ds1374_driver);
+
+MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c
new file mode 100644
index 000000000..e67bfcb3a
--- /dev/null
+++ b/drivers/rtc/rtc-ds1390.c
@@ -0,0 +1,169 @@
+/*
+ * rtc-ds1390.c -- driver for the Dallas/Maxim DS1390/93/94 SPI RTC
+ *
+ * Copyright (C) 2008 Mercury IMC Ltd
+ * Written by Mark Jackson <mpfj@mimc.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NOTE: Currently this driver only supports the bare minimum for read
+ * and write the RTC. The extra features provided by the chip family
+ * (alarms, trickle charger, different control registers) are unavailable.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#define DS1390_REG_100THS 0x00
+#define DS1390_REG_SECONDS 0x01
+#define DS1390_REG_MINUTES 0x02
+#define DS1390_REG_HOURS 0x03
+#define DS1390_REG_DAY 0x04
+#define DS1390_REG_DATE 0x05
+#define DS1390_REG_MONTH_CENT 0x06
+#define DS1390_REG_YEAR 0x07
+
+#define DS1390_REG_ALARM_100THS 0x08
+#define DS1390_REG_ALARM_SECONDS 0x09
+#define DS1390_REG_ALARM_MINUTES 0x0A
+#define DS1390_REG_ALARM_HOURS 0x0B
+#define DS1390_REG_ALARM_DAY_DATE 0x0C
+
+#define DS1390_REG_CONTROL 0x0D
+#define DS1390_REG_STATUS 0x0E
+#define DS1390_REG_TRICKLE 0x0F
+
+struct ds1390 {
+ struct rtc_device *rtc;
+ u8 txrx_buf[9]; /* cmd + 8 registers */
+};
+
+static int ds1390_get_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ds1390 *chip = dev_get_drvdata(dev);
+ int status;
+
+ if (!data)
+ return -EINVAL;
+
+ /* Clear MSB to indicate read */
+ chip->txrx_buf[0] = address & 0x7f;
+ /* do the i/o */
+ status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 1);
+ if (status != 0)
+ return status;
+
+ *data = chip->txrx_buf[1];
+
+ return 0;
+}
+
+static int ds1390_read_time(struct device *dev, struct rtc_time *dt)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ds1390 *chip = dev_get_drvdata(dev);
+ int status;
+
+ /* build the message */
+ chip->txrx_buf[0] = DS1390_REG_SECONDS;
+
+ /* do the i/o */
+ status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 8);
+ if (status != 0)
+ return status;
+
+ /* The chip sends data in this order:
+ * Seconds, Minutes, Hours, Day, Date, Month / Century, Year */
+ dt->tm_sec = bcd2bin(chip->txrx_buf[0]);
+ dt->tm_min = bcd2bin(chip->txrx_buf[1]);
+ dt->tm_hour = bcd2bin(chip->txrx_buf[2]);
+ dt->tm_wday = bcd2bin(chip->txrx_buf[3]);
+ dt->tm_mday = bcd2bin(chip->txrx_buf[4]);
+ /* mask off century bit */
+ dt->tm_mon = bcd2bin(chip->txrx_buf[5] & 0x7f) - 1;
+ /* adjust for century bit */
+ dt->tm_year = bcd2bin(chip->txrx_buf[6]) + ((chip->txrx_buf[5] & 0x80) ? 100 : 0);
+
+ return rtc_valid_tm(dt);
+}
+
+static int ds1390_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct ds1390 *chip = dev_get_drvdata(dev);
+
+ /* build the message */
+ chip->txrx_buf[0] = DS1390_REG_SECONDS | 0x80;
+ chip->txrx_buf[1] = bin2bcd(dt->tm_sec);
+ chip->txrx_buf[2] = bin2bcd(dt->tm_min);
+ chip->txrx_buf[3] = bin2bcd(dt->tm_hour);
+ chip->txrx_buf[4] = bin2bcd(dt->tm_wday);
+ chip->txrx_buf[5] = bin2bcd(dt->tm_mday);
+ chip->txrx_buf[6] = bin2bcd(dt->tm_mon + 1) |
+ ((dt->tm_year > 99) ? 0x80 : 0x00);
+ chip->txrx_buf[7] = bin2bcd(dt->tm_year % 100);
+
+ /* do the i/o */
+ return spi_write_then_read(spi, chip->txrx_buf, 8, NULL, 0);
+}
+
+static const struct rtc_class_ops ds1390_rtc_ops = {
+ .read_time = ds1390_read_time,
+ .set_time = ds1390_set_time,
+};
+
+static int ds1390_probe(struct spi_device *spi)
+{
+ unsigned char tmp;
+ struct ds1390 *chip;
+ int res;
+
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ chip = devm_kzalloc(&spi->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, chip);
+
+ res = ds1390_get_reg(&spi->dev, DS1390_REG_SECONDS, &tmp);
+ if (res != 0) {
+ dev_err(&spi->dev, "unable to read device\n");
+ return res;
+ }
+
+ chip->rtc = devm_rtc_device_register(&spi->dev, "ds1390",
+ &ds1390_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ dev_err(&spi->dev, "unable to register device\n");
+ res = PTR_ERR(chip->rtc);
+ }
+
+ return res;
+}
+
+static struct spi_driver ds1390_driver = {
+ .driver = {
+ .name = "rtc-ds1390",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds1390_probe,
+};
+
+module_spi_driver(ds1390_driver);
+
+MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver");
+MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-ds1390");
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
new file mode 100644
index 000000000..7415c2b4d
--- /dev/null
+++ b/drivers/rtc/rtc-ds1511.c
@@ -0,0 +1,572 @@
+/*
+ * An rtc driver for the Dallas DS1511
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ * Copyright (C) 2007 Andrew Sharp <andy.sharp@lsi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Real time clock driver for the Dallas 1511 chip, which also
+ * contains a watchdog timer. There is a tiny amount of code that
+ * platform code could use to mess with the watchdog device a little
+ * bit, but not a full watchdog driver.
+ */
+
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.6"
+
+enum ds1511reg {
+ DS1511_SEC = 0x0,
+ DS1511_MIN = 0x1,
+ DS1511_HOUR = 0x2,
+ DS1511_DOW = 0x3,
+ DS1511_DOM = 0x4,
+ DS1511_MONTH = 0x5,
+ DS1511_YEAR = 0x6,
+ DS1511_CENTURY = 0x7,
+ DS1511_AM1_SEC = 0x8,
+ DS1511_AM2_MIN = 0x9,
+ DS1511_AM3_HOUR = 0xa,
+ DS1511_AM4_DATE = 0xb,
+ DS1511_WD_MSEC = 0xc,
+ DS1511_WD_SEC = 0xd,
+ DS1511_CONTROL_A = 0xe,
+ DS1511_CONTROL_B = 0xf,
+ DS1511_RAMADDR_LSB = 0x10,
+ DS1511_RAMDATA = 0x13
+};
+
+#define DS1511_BLF1 0x80
+#define DS1511_BLF2 0x40
+#define DS1511_PRS 0x20
+#define DS1511_PAB 0x10
+#define DS1511_TDF 0x08
+#define DS1511_KSF 0x04
+#define DS1511_WDF 0x02
+#define DS1511_IRQF 0x01
+#define DS1511_TE 0x80
+#define DS1511_CS 0x40
+#define DS1511_BME 0x20
+#define DS1511_TPE 0x10
+#define DS1511_TIE 0x08
+#define DS1511_KIE 0x04
+#define DS1511_WDE 0x02
+#define DS1511_WDS 0x01
+#define DS1511_RAM_MAX 0xff
+
+#define RTC_CMD DS1511_CONTROL_B
+#define RTC_CMD1 DS1511_CONTROL_A
+
+#define RTC_ALARM_SEC DS1511_AM1_SEC
+#define RTC_ALARM_MIN DS1511_AM2_MIN
+#define RTC_ALARM_HOUR DS1511_AM3_HOUR
+#define RTC_ALARM_DATE DS1511_AM4_DATE
+
+#define RTC_SEC DS1511_SEC
+#define RTC_MIN DS1511_MIN
+#define RTC_HOUR DS1511_HOUR
+#define RTC_DOW DS1511_DOW
+#define RTC_DOM DS1511_DOM
+#define RTC_MON DS1511_MONTH
+#define RTC_YEAR DS1511_YEAR
+#define RTC_CENTURY DS1511_CENTURY
+
+#define RTC_TIE DS1511_TIE
+#define RTC_TE DS1511_TE
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr; /* virtual base address */
+ int irq;
+ unsigned int irqen;
+ int alrm_sec;
+ int alrm_min;
+ int alrm_hour;
+ int alrm_mday;
+ spinlock_t lock;
+};
+
+static DEFINE_SPINLOCK(ds1511_lock);
+
+static __iomem char *ds1511_base;
+static u32 reg_spacing = 1;
+
+static noinline void
+rtc_write(uint8_t val, uint32_t reg)
+{
+ writeb(val, ds1511_base + (reg * reg_spacing));
+}
+
+static inline void
+rtc_write_alarm(uint8_t val, enum ds1511reg reg)
+{
+ rtc_write((val | 0x80), reg);
+}
+
+static noinline uint8_t
+rtc_read(enum ds1511reg reg)
+{
+ return readb(ds1511_base + (reg * reg_spacing));
+}
+
+static inline void
+rtc_disable_update(void)
+{
+ rtc_write((rtc_read(RTC_CMD) & ~RTC_TE), RTC_CMD);
+}
+
+static void
+rtc_enable_update(void)
+{
+ rtc_write((rtc_read(RTC_CMD) | RTC_TE), RTC_CMD);
+}
+
+/*
+ * #define DS1511_WDOG_RESET_SUPPORT
+ *
+ * Uncomment this if you want to use these routines in
+ * some platform code.
+ */
+#ifdef DS1511_WDOG_RESET_SUPPORT
+/*
+ * just enough code to set the watchdog timer so that it
+ * will reboot the system
+ */
+void
+ds1511_wdog_set(unsigned long deciseconds)
+{
+ /*
+ * the wdog timer can take 99.99 seconds
+ */
+ deciseconds %= 10000;
+ /*
+ * set the wdog values in the wdog registers
+ */
+ rtc_write(bin2bcd(deciseconds % 100), DS1511_WD_MSEC);
+ rtc_write(bin2bcd(deciseconds / 100), DS1511_WD_SEC);
+ /*
+ * set wdog enable and wdog 'steering' bit to issue a reset
+ */
+ rtc_write(DS1511_WDE | DS1511_WDS, RTC_CMD);
+}
+
+void
+ds1511_wdog_disable(void)
+{
+ /*
+ * clear wdog enable and wdog 'steering' bits
+ */
+ rtc_write(rtc_read(RTC_CMD) & ~(DS1511_WDE | DS1511_WDS), RTC_CMD);
+ /*
+ * clear the wdog counter
+ */
+ rtc_write(0, DS1511_WD_MSEC);
+ rtc_write(0, DS1511_WD_SEC);
+}
+#endif
+
+/*
+ * set the rtc chip's idea of the time.
+ * stupidly, some callers call with year unmolested;
+ * and some call with year = year - 1900. thanks.
+ */
+static int ds1511_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ u8 mon, day, dow, hrs, min, sec, yrs, cen;
+ unsigned long flags;
+
+ /*
+ * won't have to change this for a while
+ */
+ if (rtc_tm->tm_year < 1900)
+ rtc_tm->tm_year += 1900;
+
+ if (rtc_tm->tm_year < 1970)
+ return -EINVAL;
+
+ yrs = rtc_tm->tm_year % 100;
+ cen = rtc_tm->tm_year / 100;
+ mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */
+ day = rtc_tm->tm_mday;
+ dow = rtc_tm->tm_wday & 0x7; /* automatic BCD */
+ hrs = rtc_tm->tm_hour;
+ min = rtc_tm->tm_min;
+ sec = rtc_tm->tm_sec;
+
+ if ((mon > 12) || (day == 0))
+ return -EINVAL;
+
+ if (day > rtc_month_days(rtc_tm->tm_mon, rtc_tm->tm_year))
+ return -EINVAL;
+
+ if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+ return -EINVAL;
+
+ /*
+ * each register is a different number of valid bits
+ */
+ sec = bin2bcd(sec) & 0x7f;
+ min = bin2bcd(min) & 0x7f;
+ hrs = bin2bcd(hrs) & 0x3f;
+ day = bin2bcd(day) & 0x3f;
+ mon = bin2bcd(mon) & 0x1f;
+ yrs = bin2bcd(yrs) & 0xff;
+ cen = bin2bcd(cen) & 0xff;
+
+ spin_lock_irqsave(&ds1511_lock, flags);
+ rtc_disable_update();
+ rtc_write(cen, RTC_CENTURY);
+ rtc_write(yrs, RTC_YEAR);
+ rtc_write((rtc_read(RTC_MON) & 0xe0) | mon, RTC_MON);
+ rtc_write(day, RTC_DOM);
+ rtc_write(hrs, RTC_HOUR);
+ rtc_write(min, RTC_MIN);
+ rtc_write(sec, RTC_SEC);
+ rtc_write(dow, RTC_DOW);
+ rtc_enable_update();
+ spin_unlock_irqrestore(&ds1511_lock, flags);
+
+ return 0;
+}
+
+static int ds1511_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ unsigned int century;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ds1511_lock, flags);
+ rtc_disable_update();
+
+ rtc_tm->tm_sec = rtc_read(RTC_SEC) & 0x7f;
+ rtc_tm->tm_min = rtc_read(RTC_MIN) & 0x7f;
+ rtc_tm->tm_hour = rtc_read(RTC_HOUR) & 0x3f;
+ rtc_tm->tm_mday = rtc_read(RTC_DOM) & 0x3f;
+ rtc_tm->tm_wday = rtc_read(RTC_DOW) & 0x7;
+ rtc_tm->tm_mon = rtc_read(RTC_MON) & 0x1f;
+ rtc_tm->tm_year = rtc_read(RTC_YEAR) & 0x7f;
+ century = rtc_read(RTC_CENTURY);
+
+ rtc_enable_update();
+ spin_unlock_irqrestore(&ds1511_lock, flags);
+
+ rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
+ rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
+ rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
+ rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
+ rtc_tm->tm_wday = bcd2bin(rtc_tm->tm_wday);
+ rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
+ rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
+ century = bcd2bin(century) * 100;
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ century += rtc_tm->tm_year;
+ rtc_tm->tm_year = century - 1900;
+
+ rtc_tm->tm_mon--;
+
+ if (rtc_valid_tm(rtc_tm) < 0) {
+ dev_err(dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, rtc_tm);
+ }
+ return 0;
+}
+
+/*
+ * write the alarm register settings
+ *
+ * we only have the use to interrupt every second, otherwise
+ * known as the update interrupt, or the interrupt if the whole
+ * date/hours/mins/secs matches. the ds1511 has many more
+ * permutations, but the kernel doesn't.
+ */
+static void
+ds1511_rtc_update_alarm(struct rtc_plat_data *pdata)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+ rtc_write(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_mday) & 0x3f,
+ RTC_ALARM_DATE);
+ rtc_write(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_hour) & 0x3f,
+ RTC_ALARM_HOUR);
+ rtc_write(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_min) & 0x7f,
+ RTC_ALARM_MIN);
+ rtc_write(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_sec) & 0x7f,
+ RTC_ALARM_SEC);
+ rtc_write(rtc_read(RTC_CMD) | (pdata->irqen ? RTC_TIE : 0), RTC_CMD);
+ rtc_read(RTC_CMD1); /* clear interrupts */
+ spin_unlock_irqrestore(&pdata->lock, flags);
+}
+
+static int
+ds1511_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+
+ pdata->alrm_mday = alrm->time.tm_mday;
+ pdata->alrm_hour = alrm->time.tm_hour;
+ pdata->alrm_min = alrm->time.tm_min;
+ pdata->alrm_sec = alrm->time.tm_sec;
+ if (alrm->enabled)
+ pdata->irqen |= RTC_AF;
+
+ ds1511_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static int
+ds1511_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+
+ alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
+ alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
+ alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
+ alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
+ alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
+ return 0;
+}
+
+static irqreturn_t
+ds1511_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ unsigned long events = 0;
+
+ spin_lock(&pdata->lock);
+ /*
+ * read and clear interrupt
+ */
+ if (rtc_read(RTC_CMD1) & DS1511_IRQF) {
+ events = RTC_IRQF;
+ if (rtc_read(RTC_ALARM_SEC) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ rtc_update_irq(pdata->rtc, 1, events);
+ }
+ spin_unlock(&pdata->lock);
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int ds1511_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ if (enabled)
+ pdata->irqen |= RTC_AF;
+ else
+ pdata->irqen &= ~RTC_AF;
+ ds1511_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static const struct rtc_class_ops ds1511_rtc_ops = {
+ .read_time = ds1511_rtc_read_time,
+ .set_time = ds1511_rtc_set_time,
+ .read_alarm = ds1511_rtc_read_alarm,
+ .set_alarm = ds1511_rtc_set_alarm,
+ .alarm_irq_enable = ds1511_rtc_alarm_irq_enable,
+};
+
+static ssize_t
+ds1511_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *ba,
+ char *buf, loff_t pos, size_t size)
+{
+ ssize_t count;
+
+ /*
+ * if count is more than one, turn on "burst" mode
+ * turn it off when you're done
+ */
+ if (size > 1)
+ rtc_write((rtc_read(RTC_CMD) | DS1511_BME), RTC_CMD);
+
+ if (pos > DS1511_RAM_MAX)
+ pos = DS1511_RAM_MAX;
+
+ if (size + pos > DS1511_RAM_MAX + 1)
+ size = DS1511_RAM_MAX - pos + 1;
+
+ rtc_write(pos, DS1511_RAMADDR_LSB);
+ for (count = 0; size > 0; count++, size--)
+ *buf++ = rtc_read(DS1511_RAMDATA);
+
+ if (count > 1)
+ rtc_write((rtc_read(RTC_CMD) & ~DS1511_BME), RTC_CMD);
+
+ return count;
+}
+
+static ssize_t
+ds1511_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ ssize_t count;
+
+ /*
+ * if count is more than one, turn on "burst" mode
+ * turn it off when you're done
+ */
+ if (size > 1)
+ rtc_write((rtc_read(RTC_CMD) | DS1511_BME), RTC_CMD);
+
+ if (pos > DS1511_RAM_MAX)
+ pos = DS1511_RAM_MAX;
+
+ if (size + pos > DS1511_RAM_MAX + 1)
+ size = DS1511_RAM_MAX - pos + 1;
+
+ rtc_write(pos, DS1511_RAMADDR_LSB);
+ for (count = 0; size > 0; count++, size--)
+ rtc_write(*buf++, DS1511_RAMDATA);
+
+ if (count > 1)
+ rtc_write((rtc_read(RTC_CMD) & ~DS1511_BME), RTC_CMD);
+
+ return count;
+}
+
+static struct bin_attribute ds1511_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = DS1511_RAM_MAX,
+ .read = ds1511_nvram_read,
+ .write = ds1511_nvram_write,
+};
+
+static int ds1511_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct rtc_plat_data *pdata;
+ int ret = 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ds1511_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ds1511_base))
+ return PTR_ERR(ds1511_base);
+ pdata->ioaddr = ds1511_base;
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ /*
+ * turn on the clock and the crystal, etc.
+ */
+ rtc_write(0, RTC_CMD);
+ rtc_write(0, RTC_CMD1);
+ /*
+ * clear the wdog counter
+ */
+ rtc_write(0, DS1511_WD_MSEC);
+ rtc_write(0, DS1511_WD_SEC);
+ /*
+ * start the clock
+ */
+ rtc_enable_update();
+
+ /*
+ * check for a dying bat-tree
+ */
+ if (rtc_read(RTC_CMD1) & DS1511_BLF1)
+ dev_warn(&pdev->dev, "voltage-low detected.\n");
+
+ spin_lock_init(&pdata->lock);
+ platform_set_drvdata(pdev, pdata);
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &ds1511_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
+ /*
+ * if the platform has an interrupt in mind for this device,
+ * then by all means, set it
+ */
+ if (pdata->irq > 0) {
+ rtc_read(RTC_CMD1);
+ if (devm_request_irq(&pdev->dev, pdata->irq, ds1511_interrupt,
+ IRQF_SHARED, pdev->name, pdev) < 0) {
+
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = 0;
+ }
+ }
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
+ if (ret)
+ dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n",
+ ds1511_nvram_attr.attr.name);
+
+ return 0;
+}
+
+static int ds1511_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
+ if (pdata->irq > 0) {
+ /*
+ * disable the alarm interrupt
+ */
+ rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD);
+ rtc_read(RTC_CMD1);
+ }
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:ds1511");
+
+static struct platform_driver ds1511_rtc_driver = {
+ .probe = ds1511_rtc_probe,
+ .remove = ds1511_rtc_remove,
+ .driver = {
+ .name = "ds1511",
+ },
+};
+
+module_platform_driver(ds1511_rtc_driver);
+
+MODULE_AUTHOR("Andrew Sharp <andy.sharp@lsi.com>");
+MODULE_DESCRIPTION("Dallas DS1511 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
new file mode 100644
index 000000000..a24e091bc
--- /dev/null
+++ b/drivers/rtc/rtc-ds1553.c
@@ -0,0 +1,362 @@
+/*
+ * An rtc driver for the Dallas DS1553
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.3"
+
+#define RTC_REG_SIZE 0x2000
+#define RTC_OFFSET 0x1ff0
+
+#define RTC_FLAGS (RTC_OFFSET + 0)
+#define RTC_SECONDS_ALARM (RTC_OFFSET + 2)
+#define RTC_MINUTES_ALARM (RTC_OFFSET + 3)
+#define RTC_HOURS_ALARM (RTC_OFFSET + 4)
+#define RTC_DATE_ALARM (RTC_OFFSET + 5)
+#define RTC_INTERRUPTS (RTC_OFFSET + 6)
+#define RTC_WATCHDOG (RTC_OFFSET + 7)
+#define RTC_CONTROL (RTC_OFFSET + 8)
+#define RTC_CENTURY (RTC_OFFSET + 8)
+#define RTC_SECONDS (RTC_OFFSET + 9)
+#define RTC_MINUTES (RTC_OFFSET + 10)
+#define RTC_HOURS (RTC_OFFSET + 11)
+#define RTC_DAY (RTC_OFFSET + 12)
+#define RTC_DATE (RTC_OFFSET + 13)
+#define RTC_MONTH (RTC_OFFSET + 14)
+#define RTC_YEAR (RTC_OFFSET + 15)
+
+#define RTC_CENTURY_MASK 0x3f
+#define RTC_SECONDS_MASK 0x7f
+#define RTC_DAY_MASK 0x07
+
+/* Bits in the Control/Century register */
+#define RTC_WRITE 0x80
+#define RTC_READ 0x40
+
+/* Bits in the Seconds register */
+#define RTC_STOP 0x80
+
+/* Bits in the Flags register */
+#define RTC_FLAGS_AF 0x40
+#define RTC_FLAGS_BLF 0x10
+
+/* Bits in the Interrupts register */
+#define RTC_INTS_AE 0x80
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long last_jiffies;
+ int irq;
+ unsigned int irqen;
+ int alrm_sec;
+ int alrm_min;
+ int alrm_hour;
+ int alrm_mday;
+ spinlock_t lock;
+};
+
+static int ds1553_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u8 century;
+
+ century = bin2bcd((tm->tm_year + 1900) / 100);
+
+ writeb(RTC_WRITE, pdata->ioaddr + RTC_CONTROL);
+
+ writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR);
+ writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH);
+ writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
+ writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE);
+ writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS);
+ writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES);
+ writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
+
+ /* RTC_CENTURY and RTC_CONTROL share same register */
+ writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY);
+ writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ return 0;
+}
+
+static int ds1553_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned int year, month, day, hour, minute, second, week;
+ unsigned int century;
+
+ /* give enough time to update RTC in case of continuous read */
+ if (pdata->last_jiffies == jiffies)
+ msleep(1);
+ pdata->last_jiffies = jiffies;
+ writeb(RTC_READ, ioaddr + RTC_CONTROL);
+ second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
+ minute = readb(ioaddr + RTC_MINUTES);
+ hour = readb(ioaddr + RTC_HOURS);
+ day = readb(ioaddr + RTC_DATE);
+ week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
+ month = readb(ioaddr + RTC_MONTH);
+ year = readb(ioaddr + RTC_YEAR);
+ century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(0, ioaddr + RTC_CONTROL);
+ tm->tm_sec = bcd2bin(second);
+ tm->tm_min = bcd2bin(minute);
+ tm->tm_hour = bcd2bin(hour);
+ tm->tm_mday = bcd2bin(day);
+ tm->tm_wday = bcd2bin(week);
+ tm->tm_mon = bcd2bin(month) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+ return 0;
+}
+
+static void ds1553_rtc_update_alarm(struct rtc_plat_data *pdata)
+{
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+ writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_mday),
+ ioaddr + RTC_DATE_ALARM);
+ writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_hour),
+ ioaddr + RTC_HOURS_ALARM);
+ writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_min),
+ ioaddr + RTC_MINUTES_ALARM);
+ writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_sec),
+ ioaddr + RTC_SECONDS_ALARM);
+ writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
+ readb(ioaddr + RTC_FLAGS); /* clear interrupts */
+ spin_unlock_irqrestore(&pdata->lock, flags);
+}
+
+static int ds1553_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ pdata->alrm_mday = alrm->time.tm_mday;
+ pdata->alrm_hour = alrm->time.tm_hour;
+ pdata->alrm_min = alrm->time.tm_min;
+ pdata->alrm_sec = alrm->time.tm_sec;
+ if (alrm->enabled)
+ pdata->irqen |= RTC_AF;
+ ds1553_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static int ds1553_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
+ alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
+ alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
+ alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
+ alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
+ return 0;
+}
+
+static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long events = 0;
+
+ spin_lock(&pdata->lock);
+ /* read and clear interrupt */
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
+ events = RTC_IRQF;
+ if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ rtc_update_irq(pdata->rtc, 1, events);
+ }
+ spin_unlock(&pdata->lock);
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int ds1553_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ if (enabled)
+ pdata->irqen |= RTC_AF;
+ else
+ pdata->irqen &= ~RTC_AF;
+ ds1553_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static const struct rtc_class_ops ds1553_rtc_ops = {
+ .read_time = ds1553_rtc_read_time,
+ .set_time = ds1553_rtc_set_time,
+ .read_alarm = ds1553_rtc_read_alarm,
+ .set_alarm = ds1553_rtc_set_alarm,
+ .alarm_irq_enable = ds1553_rtc_alarm_irq_enable,
+};
+
+static ssize_t ds1553_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ *buf++ = readb(ioaddr + pos++);
+ return count;
+}
+
+static ssize_t ds1553_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ writeb(*buf++, ioaddr + pos++);
+ return count;
+}
+
+static struct bin_attribute ds1553_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = RTC_OFFSET,
+ .read = ds1553_nvram_read,
+ .write = ds1553_nvram_write,
+};
+
+static int ds1553_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned int cen, sec;
+ struct rtc_plat_data *pdata;
+ void __iomem *ioaddr;
+ int ret = 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ioaddr))
+ return PTR_ERR(ioaddr);
+ pdata->ioaddr = ioaddr;
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ /* turn RTC on if it was not on */
+ sec = readb(ioaddr + RTC_SECONDS);
+ if (sec & RTC_STOP) {
+ sec &= RTC_SECONDS_MASK;
+ cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
+ writeb(sec, ioaddr + RTC_SECONDS);
+ writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ }
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_BLF)
+ dev_warn(&pdev->dev, "voltage-low detected.\n");
+
+ spin_lock_init(&pdata->lock);
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &ds1553_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
+ if (pdata->irq > 0) {
+ writeb(0, ioaddr + RTC_INTERRUPTS);
+ if (devm_request_irq(&pdev->dev, pdata->irq,
+ ds1553_rtc_interrupt,
+ 0, pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = 0;
+ }
+ }
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ if (ret)
+ dev_err(&pdev->dev, "unable to create sysfs file: %s\n",
+ ds1553_nvram_attr.attr.name);
+
+ return 0;
+}
+
+static int ds1553_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ if (pdata->irq > 0)
+ writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc-ds1553");
+
+static struct platform_driver ds1553_rtc_driver = {
+ .probe = ds1553_rtc_probe,
+ .remove = ds1553_rtc_remove,
+ .driver = {
+ .name = "rtc-ds1553",
+ },
+};
+
+module_platform_driver(ds1553_rtc_driver);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Dallas DS1553 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
new file mode 100644
index 000000000..a4888dbca
--- /dev/null
+++ b/drivers/rtc/rtc-ds1672.c
@@ -0,0 +1,215 @@
+/*
+ * An rtc/i2c driver for the Dallas DS1672
+ * Copyright 2005-06 Tower Technologies
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.4"
+
+/* Registers */
+
+#define DS1672_REG_CNT_BASE 0
+#define DS1672_REG_CONTROL 4
+#define DS1672_REG_TRICKLE 5
+
+#define DS1672_REG_CONTROL_EOSC 0x80
+
+static struct i2c_driver ds1672_driver;
+
+/*
+ * In the routines that deal directly with the ds1672 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+ * Epoch is initialized as 2000. Time is set to UTC.
+ */
+static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ unsigned long time;
+ unsigned char addr = DS1672_REG_CNT_BASE;
+ unsigned char buf[4];
+
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = &addr
+ },
+ {/* read date */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 4,
+ .buf = buf
+ },
+ };
+
+ /* read date registers */
+ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw read data - counters=%02x,%02x,%02x,%02x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+
+ time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ rtc_time_to_tm(time, tm);
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__, tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ return 0;
+}
+
+static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs)
+{
+ int xfer;
+ unsigned char buf[6];
+
+ buf[0] = DS1672_REG_CNT_BASE;
+ buf[1] = secs & 0x000000FF;
+ buf[2] = (secs & 0x0000FF00) >> 8;
+ buf[3] = (secs & 0x00FF0000) >> 16;
+ buf[4] = (secs & 0xFF000000) >> 24;
+ buf[5] = 0; /* set control reg to enable counting */
+
+ xfer = i2c_master_send(client, buf, 6);
+ if (xfer != 6) {
+ dev_err(&client->dev, "%s: send: %d\n", __func__, xfer);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return ds1672_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ return ds1672_set_mmss(to_i2c_client(dev), secs);
+}
+
+static int ds1672_get_control(struct i2c_client *client, u8 *status)
+{
+ unsigned char addr = DS1672_REG_CONTROL;
+
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = &addr
+ },
+ {/* read control */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = status
+ },
+ };
+
+ /* read control register */
+ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/* following are the sysfs callback functions */
+static ssize_t show_control(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 control;
+ int err;
+
+ err = ds1672_get_control(client, &control);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%s\n", (control & DS1672_REG_CONTROL_EOSC)
+ ? "disabled" : "enabled");
+}
+
+static DEVICE_ATTR(control, S_IRUGO, show_control, NULL);
+
+static const struct rtc_class_ops ds1672_rtc_ops = {
+ .read_time = ds1672_rtc_read_time,
+ .set_mmss = ds1672_rtc_set_mmss,
+};
+
+static int ds1672_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ u8 control;
+ struct rtc_device *rtc;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ rtc = devm_rtc_device_register(&client->dev, ds1672_driver.driver.name,
+ &ds1672_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ /* read control register */
+ err = ds1672_get_control(client, &control);
+ if (err) {
+ dev_warn(&client->dev, "Unable to read the control register\n");
+ }
+
+ if (control & DS1672_REG_CONTROL_EOSC)
+ dev_warn(&client->dev, "Oscillator not enabled. "
+ "Set time to enable.\n");
+
+ /* Register sysfs hooks */
+ err = device_create_file(&client->dev, &dev_attr_control);
+ if (err)
+ dev_err(&client->dev, "Unable to create sysfs entry: %s\n",
+ dev_attr_control.attr.name);
+
+ return 0;
+}
+
+static struct i2c_device_id ds1672_id[] = {
+ { "ds1672", 0 },
+ { }
+};
+
+static struct i2c_driver ds1672_driver = {
+ .driver = {
+ .name = "rtc-ds1672",
+ },
+ .probe = &ds1672_probe,
+ .id_table = ds1672_id,
+};
+
+module_i2c_driver(ds1672_driver);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c
new file mode 100644
index 000000000..818a3635a
--- /dev/null
+++ b/drivers/rtc/rtc-ds1685.c
@@ -0,0 +1,2251 @@
+/*
+ * An rtc driver for the Dallas/Maxim DS1685/DS1687 and related real-time
+ * chips.
+ *
+ * Copyright (C) 2011-2014 Joshua Kinard <kumba@gentoo.org>.
+ * Copyright (C) 2009 Matthias Fuchs <matthias.fuchs@esd-electronics.com>.
+ *
+ * References:
+ * DS1685/DS1687 3V/5V Real-Time Clocks, 19-5215, Rev 4/10.
+ * DS17x85/DS17x87 3V/5V Real-Time Clocks, 19-5222, Rev 4/10.
+ * DS1689/DS1693 3V/5V Serialized Real-Time Clocks, Rev 112105.
+ * Application Note 90, Using the Multiplex Bus RTC Extended Features.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+
+#include <linux/rtc/ds1685.h>
+
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
+
+#define DRV_VERSION "0.42.0"
+
+
+/* ----------------------------------------------------------------------- */
+/* Standard read/write functions if platform does not provide overrides */
+
+/**
+ * ds1685_read - read a value from an rtc register.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @reg: the register address to read.
+ */
+static u8
+ds1685_read(struct ds1685_priv *rtc, int reg)
+{
+ return readb((u8 __iomem *)rtc->regs +
+ (reg * rtc->regstep));
+}
+
+/**
+ * ds1685_write - write a value to an rtc register.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @reg: the register address to write.
+ * @value: value to write to the register.
+ */
+static void
+ds1685_write(struct ds1685_priv *rtc, int reg, u8 value)
+{
+ writeb(value, ((u8 __iomem *)rtc->regs +
+ (reg * rtc->regstep)));
+}
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* Inlined functions */
+
+/**
+ * ds1685_rtc_bcd2bin - bcd2bin wrapper in case platform doesn't support BCD.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @val: u8 time value to consider converting.
+ * @bcd_mask: u8 mask value if BCD mode is used.
+ * @bin_mask: u8 mask value if BIN mode is used.
+ *
+ * Returns the value, converted to BIN if originally in BCD and bcd_mode TRUE.
+ */
+static inline u8
+ds1685_rtc_bcd2bin(struct ds1685_priv *rtc, u8 val, u8 bcd_mask, u8 bin_mask)
+{
+ if (rtc->bcd_mode)
+ return (bcd2bin(val) & bcd_mask);
+
+ return (val & bin_mask);
+}
+
+/**
+ * ds1685_rtc_bin2bcd - bin2bcd wrapper in case platform doesn't support BCD.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @val: u8 time value to consider converting.
+ * @bin_mask: u8 mask value if BIN mode is used.
+ * @bcd_mask: u8 mask value if BCD mode is used.
+ *
+ * Returns the value, converted to BCD if originally in BIN and bcd_mode TRUE.
+ */
+static inline u8
+ds1685_rtc_bin2bcd(struct ds1685_priv *rtc, u8 val, u8 bin_mask, u8 bcd_mask)
+{
+ if (rtc->bcd_mode)
+ return (bin2bcd(val) & bcd_mask);
+
+ return (val & bin_mask);
+}
+
+/**
+ * ds1685_rtc_switch_to_bank0 - switch the rtc to bank 0.
+ * @rtc: pointer to the ds1685 rtc structure.
+ */
+static inline void
+ds1685_rtc_switch_to_bank0(struct ds1685_priv *rtc)
+{
+ rtc->write(rtc, RTC_CTRL_A,
+ (rtc->read(rtc, RTC_CTRL_A) & ~(RTC_CTRL_A_DV0)));
+}
+
+/**
+ * ds1685_rtc_switch_to_bank1 - switch the rtc to bank 1.
+ * @rtc: pointer to the ds1685 rtc structure.
+ */
+static inline void
+ds1685_rtc_switch_to_bank1(struct ds1685_priv *rtc)
+{
+ rtc->write(rtc, RTC_CTRL_A,
+ (rtc->read(rtc, RTC_CTRL_A) | RTC_CTRL_A_DV0));
+}
+
+/**
+ * ds1685_rtc_begin_data_access - prepare the rtc for data access.
+ * @rtc: pointer to the ds1685 rtc structure.
+ *
+ * This takes several steps to prepare the rtc for access to get/set time
+ * and alarm values from the rtc registers:
+ * - Sets the SET bit in Control Register B.
+ * - Reads Ext Control Register 4A and checks the INCR bit.
+ * - If INCR is active, a short delay is added before Ext Control Register 4A
+ * is read again in a loop until INCR is inactive.
+ * - Switches the rtc to bank 1. This allows access to all relevant
+ * data for normal rtc operation, as bank 0 contains only the nvram.
+ */
+static inline void
+ds1685_rtc_begin_data_access(struct ds1685_priv *rtc)
+{
+ /* Set the SET bit in Ctrl B */
+ rtc->write(rtc, RTC_CTRL_B,
+ (rtc->read(rtc, RTC_CTRL_B) | RTC_CTRL_B_SET));
+
+ /* Read Ext Ctrl 4A and check the INCR bit to avoid a lockout. */
+ while (rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_INCR)
+ cpu_relax();
+
+ /* Switch to Bank 1 */
+ ds1685_rtc_switch_to_bank1(rtc);
+}
+
+/**
+ * ds1685_rtc_end_data_access - end data access on the rtc.
+ * @rtc: pointer to the ds1685 rtc structure.
+ *
+ * This ends what was started by ds1685_rtc_begin_data_access:
+ * - Switches the rtc back to bank 0.
+ * - Clears the SET bit in Control Register B.
+ */
+static inline void
+ds1685_rtc_end_data_access(struct ds1685_priv *rtc)
+{
+ /* Switch back to Bank 0 */
+ ds1685_rtc_switch_to_bank1(rtc);
+
+ /* Clear the SET bit in Ctrl B */
+ rtc->write(rtc, RTC_CTRL_B,
+ (rtc->read(rtc, RTC_CTRL_B) & ~(RTC_CTRL_B_SET)));
+}
+
+/**
+ * ds1685_rtc_begin_ctrl_access - prepare the rtc for ctrl access.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @flags: irq flags variable for spin_lock_irqsave.
+ *
+ * This takes several steps to prepare the rtc for access to read just the
+ * control registers:
+ * - Sets a spinlock on the rtc IRQ.
+ * - Switches the rtc to bank 1. This allows access to the two extended
+ * control registers.
+ *
+ * Only use this where you are certain another lock will not be held.
+ */
+static inline void
+ds1685_rtc_begin_ctrl_access(struct ds1685_priv *rtc, unsigned long flags)
+{
+ spin_lock_irqsave(&rtc->lock, flags);
+ ds1685_rtc_switch_to_bank1(rtc);
+}
+
+/**
+ * ds1685_rtc_end_ctrl_access - end ctrl access on the rtc.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @flags: irq flags variable for spin_unlock_irqrestore.
+ *
+ * This ends what was started by ds1685_rtc_begin_ctrl_access:
+ * - Switches the rtc back to bank 0.
+ * - Unsets the spinlock on the rtc IRQ.
+ */
+static inline void
+ds1685_rtc_end_ctrl_access(struct ds1685_priv *rtc, unsigned long flags)
+{
+ ds1685_rtc_switch_to_bank0(rtc);
+ spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+/**
+ * ds1685_rtc_get_ssn - retrieve the silicon serial number.
+ * @rtc: pointer to the ds1685 rtc structure.
+ * @ssn: u8 array to hold the bits of the silicon serial number.
+ *
+ * This number starts at 0x40, and is 8-bytes long, ending at 0x47. The
+ * first byte is the model number, the next six bytes are the serial number
+ * digits, and the final byte is a CRC check byte. Together, they form the
+ * silicon serial number.
+ *
+ * These values are stored in bank1, so ds1685_rtc_switch_to_bank1 must be
+ * called first before calling this function, else data will be read out of
+ * the bank0 NVRAM. Be sure to call ds1685_rtc_switch_to_bank0 when done.
+ */
+static inline void
+ds1685_rtc_get_ssn(struct ds1685_priv *rtc, u8 *ssn)
+{
+ ssn[0] = rtc->read(rtc, RTC_BANK1_SSN_MODEL);
+ ssn[1] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_1);
+ ssn[2] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_2);
+ ssn[3] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_3);
+ ssn[4] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_4);
+ ssn[5] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_5);
+ ssn[6] = rtc->read(rtc, RTC_BANK1_SSN_BYTE_6);
+ ssn[7] = rtc->read(rtc, RTC_BANK1_SSN_CRC);
+}
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* Read/Set Time & Alarm functions */
+
+/**
+ * ds1685_rtc_read_time - reads the time registers.
+ * @dev: pointer to device structure.
+ * @tm: pointer to rtc_time structure.
+ */
+static int
+ds1685_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrlb, century;
+ u8 seconds, minutes, hours, wday, mday, month, years;
+
+ /* Fetch the time info from the RTC registers. */
+ ds1685_rtc_begin_data_access(rtc);
+ seconds = rtc->read(rtc, RTC_SECS);
+ minutes = rtc->read(rtc, RTC_MINS);
+ hours = rtc->read(rtc, RTC_HRS);
+ wday = rtc->read(rtc, RTC_WDAY);
+ mday = rtc->read(rtc, RTC_MDAY);
+ month = rtc->read(rtc, RTC_MONTH);
+ years = rtc->read(rtc, RTC_YEAR);
+ century = rtc->read(rtc, RTC_CENTURY);
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ ds1685_rtc_end_data_access(rtc);
+
+ /* bcd2bin if needed, perform fixups, and store to rtc_time. */
+ years = ds1685_rtc_bcd2bin(rtc, years, RTC_YEAR_BCD_MASK,
+ RTC_YEAR_BIN_MASK);
+ century = ds1685_rtc_bcd2bin(rtc, century, RTC_CENTURY_MASK,
+ RTC_CENTURY_MASK);
+ tm->tm_sec = ds1685_rtc_bcd2bin(rtc, seconds, RTC_SECS_BCD_MASK,
+ RTC_SECS_BIN_MASK);
+ tm->tm_min = ds1685_rtc_bcd2bin(rtc, minutes, RTC_MINS_BCD_MASK,
+ RTC_MINS_BIN_MASK);
+ tm->tm_hour = ds1685_rtc_bcd2bin(rtc, hours, RTC_HRS_24_BCD_MASK,
+ RTC_HRS_24_BIN_MASK);
+ tm->tm_wday = (ds1685_rtc_bcd2bin(rtc, wday, RTC_WDAY_MASK,
+ RTC_WDAY_MASK) - 1);
+ tm->tm_mday = ds1685_rtc_bcd2bin(rtc, mday, RTC_MDAY_BCD_MASK,
+ RTC_MDAY_BIN_MASK);
+ tm->tm_mon = (ds1685_rtc_bcd2bin(rtc, month, RTC_MONTH_BCD_MASK,
+ RTC_MONTH_BIN_MASK) - 1);
+ tm->tm_year = ((years + (century * 100)) - 1900);
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_isdst = 0; /* RTC has hardcoded timezone, so don't use. */
+
+ return rtc_valid_tm(tm);
+}
+
+/**
+ * ds1685_rtc_set_time - sets the time registers.
+ * @dev: pointer to device structure.
+ * @tm: pointer to rtc_time structure.
+ */
+static int
+ds1685_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrlb, seconds, minutes, hours, wday, mday, month, years, century;
+
+ /* Fetch the time info from rtc_time. */
+ seconds = ds1685_rtc_bin2bcd(rtc, tm->tm_sec, RTC_SECS_BIN_MASK,
+ RTC_SECS_BCD_MASK);
+ minutes = ds1685_rtc_bin2bcd(rtc, tm->tm_min, RTC_MINS_BIN_MASK,
+ RTC_MINS_BCD_MASK);
+ hours = ds1685_rtc_bin2bcd(rtc, tm->tm_hour, RTC_HRS_24_BIN_MASK,
+ RTC_HRS_24_BCD_MASK);
+ wday = ds1685_rtc_bin2bcd(rtc, (tm->tm_wday + 1), RTC_WDAY_MASK,
+ RTC_WDAY_MASK);
+ mday = ds1685_rtc_bin2bcd(rtc, tm->tm_mday, RTC_MDAY_BIN_MASK,
+ RTC_MDAY_BCD_MASK);
+ month = ds1685_rtc_bin2bcd(rtc, (tm->tm_mon + 1), RTC_MONTH_BIN_MASK,
+ RTC_MONTH_BCD_MASK);
+ years = ds1685_rtc_bin2bcd(rtc, (tm->tm_year % 100),
+ RTC_YEAR_BIN_MASK, RTC_YEAR_BCD_MASK);
+ century = ds1685_rtc_bin2bcd(rtc, ((tm->tm_year + 1900) / 100),
+ RTC_CENTURY_MASK, RTC_CENTURY_MASK);
+
+ /*
+ * Perform Sanity Checks:
+ * - Months: !> 12, Month Day != 0.
+ * - Month Day !> Max days in current month.
+ * - Hours !>= 24, Mins !>= 60, Secs !>= 60, & Weekday !> 7.
+ */
+ if ((tm->tm_mon > 11) || (mday == 0))
+ return -EDOM;
+
+ if (tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year))
+ return -EDOM;
+
+ if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) ||
+ (tm->tm_sec >= 60) || (wday > 7))
+ return -EDOM;
+
+ /*
+ * Set the data mode to use and store the time values in the
+ * RTC registers.
+ */
+ ds1685_rtc_begin_data_access(rtc);
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ if (rtc->bcd_mode)
+ ctrlb &= ~(RTC_CTRL_B_DM);
+ else
+ ctrlb |= RTC_CTRL_B_DM;
+ rtc->write(rtc, RTC_CTRL_B, ctrlb);
+ rtc->write(rtc, RTC_SECS, seconds);
+ rtc->write(rtc, RTC_MINS, minutes);
+ rtc->write(rtc, RTC_HRS, hours);
+ rtc->write(rtc, RTC_WDAY, wday);
+ rtc->write(rtc, RTC_MDAY, mday);
+ rtc->write(rtc, RTC_MONTH, month);
+ rtc->write(rtc, RTC_YEAR, years);
+ rtc->write(rtc, RTC_CENTURY, century);
+ ds1685_rtc_end_data_access(rtc);
+
+ return 0;
+}
+
+/**
+ * ds1685_rtc_read_alarm - reads the alarm registers.
+ * @dev: pointer to device structure.
+ * @alrm: pointer to rtc_wkalrm structure.
+ *
+ * There are three primary alarm registers: seconds, minutes, and hours.
+ * A fourth alarm register for the month date is also available in bank1 for
+ * kickstart/wakeup features. The DS1685/DS1687 manual states that a
+ * "don't care" value ranging from 0xc0 to 0xff may be written into one or
+ * more of the three alarm bytes to act as a wildcard value. The fourth
+ * byte doesn't support a "don't care" value.
+ */
+static int
+ds1685_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 seconds, minutes, hours, mday, ctrlb, ctrlc;
+
+ /* Fetch the alarm info from the RTC alarm registers. */
+ ds1685_rtc_begin_data_access(rtc);
+ seconds = rtc->read(rtc, RTC_SECS_ALARM);
+ minutes = rtc->read(rtc, RTC_MINS_ALARM);
+ hours = rtc->read(rtc, RTC_HRS_ALARM);
+ mday = rtc->read(rtc, RTC_MDAY_ALARM);
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ ctrlc = rtc->read(rtc, RTC_CTRL_C);
+ ds1685_rtc_end_data_access(rtc);
+
+ /* Check month date. */
+ if (!(mday >= 1) && (mday <= 31))
+ return -EDOM;
+
+ /*
+ * Check the three alarm bytes.
+ *
+ * The Linux RTC system doesn't support the "don't care" capability
+ * of this RTC chip. We check for it anyways in case support is
+ * added in the future.
+ */
+ if (unlikely(seconds >= 0xc0))
+ alrm->time.tm_sec = -1;
+ else
+ alrm->time.tm_sec = ds1685_rtc_bcd2bin(rtc, seconds,
+ RTC_SECS_BCD_MASK,
+ RTC_SECS_BIN_MASK);
+
+ if (unlikely(minutes >= 0xc0))
+ alrm->time.tm_min = -1;
+ else
+ alrm->time.tm_min = ds1685_rtc_bcd2bin(rtc, minutes,
+ RTC_MINS_BCD_MASK,
+ RTC_MINS_BIN_MASK);
+
+ if (unlikely(hours >= 0xc0))
+ alrm->time.tm_hour = -1;
+ else
+ alrm->time.tm_hour = ds1685_rtc_bcd2bin(rtc, hours,
+ RTC_HRS_24_BCD_MASK,
+ RTC_HRS_24_BIN_MASK);
+
+ /* Write the data to rtc_wkalrm. */
+ alrm->time.tm_mday = ds1685_rtc_bcd2bin(rtc, mday, RTC_MDAY_BCD_MASK,
+ RTC_MDAY_BIN_MASK);
+ alrm->time.tm_mon = -1;
+ alrm->time.tm_year = -1;
+ alrm->time.tm_wday = -1;
+ alrm->time.tm_yday = -1;
+ alrm->time.tm_isdst = -1;
+ alrm->enabled = !!(ctrlb & RTC_CTRL_B_AIE);
+ alrm->pending = !!(ctrlc & RTC_CTRL_C_AF);
+
+ return 0;
+}
+
+/**
+ * ds1685_rtc_set_alarm - sets the alarm in registers.
+ * @dev: pointer to device structure.
+ * @alrm: pointer to rtc_wkalrm structure.
+ */
+static int
+ds1685_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrlb, seconds, minutes, hours, mday;
+
+ /* Fetch the alarm info and convert to BCD. */
+ seconds = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_sec,
+ RTC_SECS_BIN_MASK,
+ RTC_SECS_BCD_MASK);
+ minutes = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_min,
+ RTC_MINS_BIN_MASK,
+ RTC_MINS_BCD_MASK);
+ hours = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_hour,
+ RTC_HRS_24_BIN_MASK,
+ RTC_HRS_24_BCD_MASK);
+ mday = ds1685_rtc_bin2bcd(rtc, alrm->time.tm_mday,
+ RTC_MDAY_BIN_MASK,
+ RTC_MDAY_BCD_MASK);
+
+ /* Check the month date for validity. */
+ if (!(mday >= 1) && (mday <= 31))
+ return -EDOM;
+
+ /*
+ * Check the three alarm bytes.
+ *
+ * The Linux RTC system doesn't support the "don't care" capability
+ * of this RTC chip because rtc_valid_tm tries to validate every
+ * field, and we only support four fields. We put the support
+ * here anyways for the future.
+ */
+ if (unlikely(seconds >= 0xc0))
+ seconds = 0xff;
+
+ if (unlikely(minutes >= 0xc0))
+ minutes = 0xff;
+
+ if (unlikely(hours >= 0xc0))
+ hours = 0xff;
+
+ alrm->time.tm_mon = -1;
+ alrm->time.tm_year = -1;
+ alrm->time.tm_wday = -1;
+ alrm->time.tm_yday = -1;
+ alrm->time.tm_isdst = -1;
+
+ /* Disable the alarm interrupt first. */
+ ds1685_rtc_begin_data_access(rtc);
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ rtc->write(rtc, RTC_CTRL_B, (ctrlb & ~(RTC_CTRL_B_AIE)));
+
+ /* Read ctrlc to clear RTC_CTRL_C_AF. */
+ rtc->read(rtc, RTC_CTRL_C);
+
+ /*
+ * Set the data mode to use and store the time values in the
+ * RTC registers.
+ */
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ if (rtc->bcd_mode)
+ ctrlb &= ~(RTC_CTRL_B_DM);
+ else
+ ctrlb |= RTC_CTRL_B_DM;
+ rtc->write(rtc, RTC_CTRL_B, ctrlb);
+ rtc->write(rtc, RTC_SECS_ALARM, seconds);
+ rtc->write(rtc, RTC_MINS_ALARM, minutes);
+ rtc->write(rtc, RTC_HRS_ALARM, hours);
+ rtc->write(rtc, RTC_MDAY_ALARM, mday);
+
+ /* Re-enable the alarm if needed. */
+ if (alrm->enabled) {
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ ctrlb |= RTC_CTRL_B_AIE;
+ rtc->write(rtc, RTC_CTRL_B, ctrlb);
+ }
+
+ /* Done! */
+ ds1685_rtc_end_data_access(rtc);
+
+ return 0;
+}
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* /dev/rtcX Interface functions */
+
+/**
+ * ds1685_rtc_alarm_irq_enable - replaces ioctl() RTC_AIE on/off.
+ * @dev: pointer to device structure.
+ * @enabled: flag indicating whether to enable or disable.
+ */
+static int
+ds1685_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ unsigned long flags = 0;
+
+ /* Enable/disable the Alarm IRQ-Enable flag. */
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ /* Flip the requisite interrupt-enable bit. */
+ if (enabled)
+ rtc->write(rtc, RTC_CTRL_B, (rtc->read(rtc, RTC_CTRL_B) |
+ RTC_CTRL_B_AIE));
+ else
+ rtc->write(rtc, RTC_CTRL_B, (rtc->read(rtc, RTC_CTRL_B) &
+ ~(RTC_CTRL_B_AIE)));
+
+ /* Read Control C to clear all the flag bits. */
+ rtc->read(rtc, RTC_CTRL_C);
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return 0;
+}
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* IRQ handler & workqueue. */
+
+/**
+ * ds1685_rtc_irq_handler - IRQ handler.
+ * @irq: IRQ number.
+ * @dev_id: platform device pointer.
+ */
+static irqreturn_t
+ds1685_rtc_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrlb, ctrlc;
+ unsigned long events = 0;
+ u8 num_irqs = 0;
+
+ /* Abort early if the device isn't ready yet (i.e., DEBUG_SHIRQ). */
+ if (unlikely(!rtc))
+ return IRQ_HANDLED;
+
+ /* Ctrlb holds the interrupt-enable bits and ctrlc the flag bits. */
+ spin_lock(&rtc->lock);
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ ctrlc = rtc->read(rtc, RTC_CTRL_C);
+
+ /* Is the IRQF bit set? */
+ if (likely(ctrlc & RTC_CTRL_C_IRQF)) {
+ /*
+ * We need to determine if it was one of the standard
+ * events: PF, AF, or UF. If so, we handle them and
+ * update the RTC core.
+ */
+ if (likely(ctrlc & RTC_CTRL_B_PAU_MASK)) {
+ events = RTC_IRQF;
+
+ /* Check for a periodic interrupt. */
+ if ((ctrlb & RTC_CTRL_B_PIE) &&
+ (ctrlc & RTC_CTRL_C_PF)) {
+ events |= RTC_PF;
+ num_irqs++;
+ }
+
+ /* Check for an alarm interrupt. */
+ if ((ctrlb & RTC_CTRL_B_AIE) &&
+ (ctrlc & RTC_CTRL_C_AF)) {
+ events |= RTC_AF;
+ num_irqs++;
+ }
+
+ /* Check for an update interrupt. */
+ if ((ctrlb & RTC_CTRL_B_UIE) &&
+ (ctrlc & RTC_CTRL_C_UF)) {
+ events |= RTC_UF;
+ num_irqs++;
+ }
+
+ rtc_update_irq(rtc->dev, num_irqs, events);
+ } else {
+ /*
+ * One of the "extended" interrupts was received that
+ * is not recognized by the RTC core. These need to
+ * be handled in task context as they can call other
+ * functions and the time spent in irq context needs
+ * to be minimized. Schedule them into a workqueue
+ * and inform the RTC core that the IRQs were handled.
+ */
+ spin_unlock(&rtc->lock);
+ schedule_work(&rtc->work);
+ rtc_update_irq(rtc->dev, 0, 0);
+ return IRQ_HANDLED;
+ }
+ }
+ spin_unlock(&rtc->lock);
+
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+/**
+ * ds1685_rtc_work_queue - work queue handler.
+ * @work: work_struct containing data to work on in task context.
+ */
+static void
+ds1685_rtc_work_queue(struct work_struct *work)
+{
+ struct ds1685_priv *rtc = container_of(work,
+ struct ds1685_priv, work);
+ struct platform_device *pdev = to_platform_device(&rtc->dev->dev);
+ struct mutex *rtc_mutex = &rtc->dev->ops_lock;
+ u8 ctrl4a, ctrl4b;
+
+ mutex_lock(rtc_mutex);
+
+ ds1685_rtc_switch_to_bank1(rtc);
+ ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
+ ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B);
+
+ /*
+ * Check for a kickstart interrupt. With Vcc applied, this
+ * typically means that the power button was pressed, so we
+ * begin the shutdown sequence.
+ */
+ if ((ctrl4b & RTC_CTRL_4B_KSE) && (ctrl4a & RTC_CTRL_4A_KF)) {
+ /* Briefly disable kickstarts to debounce button presses. */
+ rtc->write(rtc, RTC_EXT_CTRL_4B,
+ (rtc->read(rtc, RTC_EXT_CTRL_4B) &
+ ~(RTC_CTRL_4B_KSE)));
+
+ /* Clear the kickstart flag. */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (ctrl4a & ~(RTC_CTRL_4A_KF)));
+
+
+ /*
+ * Sleep 500ms before re-enabling kickstarts. This allows
+ * adequate time to avoid reading signal jitter as additional
+ * button presses.
+ */
+ msleep(500);
+ rtc->write(rtc, RTC_EXT_CTRL_4B,
+ (rtc->read(rtc, RTC_EXT_CTRL_4B) |
+ RTC_CTRL_4B_KSE));
+
+ /* Call the platform pre-poweroff function. Else, shutdown. */
+ if (rtc->prepare_poweroff != NULL)
+ rtc->prepare_poweroff();
+ else
+ ds1685_rtc_poweroff(pdev);
+ }
+
+ /*
+ * Check for a wake-up interrupt. With Vcc applied, this is
+ * essentially a second alarm interrupt, except it takes into
+ * account the 'date' register in bank1 in addition to the
+ * standard three alarm registers.
+ */
+ if ((ctrl4b & RTC_CTRL_4B_WIE) && (ctrl4a & RTC_CTRL_4A_WF)) {
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (ctrl4a & ~(RTC_CTRL_4A_WF)));
+
+ /* Call the platform wake_alarm function if defined. */
+ if (rtc->wake_alarm != NULL)
+ rtc->wake_alarm();
+ else
+ dev_warn(&pdev->dev,
+ "Wake Alarm IRQ just occurred!\n");
+ }
+
+ /*
+ * Check for a ram-clear interrupt. This happens if RIE=1 and RF=0
+ * when RCE=1 in 4B. This clears all NVRAM bytes in bank0 by setting
+ * each byte to a logic 1. This has no effect on any extended
+ * NV-SRAM that might be present, nor on the time/calendar/alarm
+ * registers. After a ram-clear is completed, there is a minimum
+ * recovery time of ~150ms in which all reads/writes are locked out.
+ * NOTE: A ram-clear can still occur if RCE=1 and RIE=0. We cannot
+ * catch this scenario.
+ */
+ if ((ctrl4b & RTC_CTRL_4B_RIE) && (ctrl4a & RTC_CTRL_4A_RF)) {
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (ctrl4a & ~(RTC_CTRL_4A_RF)));
+ msleep(150);
+
+ /* Call the platform post_ram_clear function if defined. */
+ if (rtc->post_ram_clear != NULL)
+ rtc->post_ram_clear();
+ else
+ dev_warn(&pdev->dev,
+ "RAM-Clear IRQ just occurred!\n");
+ }
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ mutex_unlock(rtc_mutex);
+}
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* ProcFS interface */
+
+#ifdef CONFIG_PROC_FS
+#define NUM_REGS 6 /* Num of control registers. */
+#define NUM_BITS 8 /* Num bits per register. */
+#define NUM_SPACES 4 /* Num spaces between each bit. */
+
+/*
+ * Periodic Interrupt Rates.
+ */
+static const char *ds1685_rtc_pirq_rate[16] = {
+ "none", "3.90625ms", "7.8125ms", "0.122070ms", "0.244141ms",
+ "0.488281ms", "0.9765625ms", "1.953125ms", "3.90625ms", "7.8125ms",
+ "15.625ms", "31.25ms", "62.5ms", "125ms", "250ms", "500ms"
+};
+
+/*
+ * Square-Wave Output Frequencies.
+ */
+static const char *ds1685_rtc_sqw_freq[16] = {
+ "none", "256Hz", "128Hz", "8192Hz", "4096Hz", "2048Hz", "1024Hz",
+ "512Hz", "256Hz", "128Hz", "64Hz", "32Hz", "16Hz", "8Hz", "4Hz", "2Hz"
+};
+
+#ifdef CONFIG_RTC_DS1685_PROC_REGS
+/**
+ * ds1685_rtc_print_regs - helper function to print register values.
+ * @hex: hex byte to convert into binary bits.
+ * @dest: destination char array.
+ *
+ * This is basically a hex->binary function, just with extra spacing between
+ * the digits. It only works on 1-byte values (8 bits).
+ */
+static char*
+ds1685_rtc_print_regs(u8 hex, char *dest)
+{
+ u32 i, j;
+ char *tmp = dest;
+
+ for (i = 0; i < NUM_BITS; i++) {
+ *tmp++ = ((hex & 0x80) != 0 ? '1' : '0');
+ for (j = 0; j < NUM_SPACES; j++)
+ *tmp++ = ' ';
+ hex <<= 1;
+ }
+ *tmp++ = '\0';
+
+ return dest;
+}
+#endif
+
+/**
+ * ds1685_rtc_proc - procfs access function.
+ * @dev: pointer to device structure.
+ * @seq: pointer to seq_file structure.
+ */
+static int
+ds1685_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrla, ctrlb, ctrlc, ctrld, ctrl4a, ctrl4b, ssn[8];
+ char *model;
+#ifdef CONFIG_RTC_DS1685_PROC_REGS
+ char bits[NUM_REGS][(NUM_BITS * NUM_SPACES) + NUM_BITS + 1];
+#endif
+
+ /* Read all the relevant data from the control registers. */
+ ds1685_rtc_switch_to_bank1(rtc);
+ ds1685_rtc_get_ssn(rtc, ssn);
+ ctrla = rtc->read(rtc, RTC_CTRL_A);
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ ctrlc = rtc->read(rtc, RTC_CTRL_C);
+ ctrld = rtc->read(rtc, RTC_CTRL_D);
+ ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
+ ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B);
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ /* Determine the RTC model. */
+ switch (ssn[0]) {
+ case RTC_MODEL_DS1685:
+ model = "DS1685/DS1687\0";
+ break;
+ case RTC_MODEL_DS1689:
+ model = "DS1689/DS1693\0";
+ break;
+ case RTC_MODEL_DS17285:
+ model = "DS17285/DS17287\0";
+ break;
+ case RTC_MODEL_DS17485:
+ model = "DS17485/DS17487\0";
+ break;
+ case RTC_MODEL_DS17885:
+ model = "DS17885/DS17887\0";
+ break;
+ default:
+ model = "Unknown\0";
+ break;
+ }
+
+ /* Print out the information. */
+ seq_printf(seq,
+ "Model\t\t: %s\n"
+ "Oscillator\t: %s\n"
+ "12/24hr\t\t: %s\n"
+ "DST\t\t: %s\n"
+ "Data mode\t: %s\n"
+ "Battery\t\t: %s\n"
+ "Aux batt\t: %s\n"
+ "Update IRQ\t: %s\n"
+ "Periodic IRQ\t: %s\n"
+ "Periodic Rate\t: %s\n"
+ "SQW Freq\t: %s\n"
+#ifdef CONFIG_RTC_DS1685_PROC_REGS
+ "Serial #\t: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n"
+ "Register Status\t:\n"
+ " Ctrl A\t: UIP DV2 DV1 DV0 RS3 RS2 RS1 RS0\n"
+ "\t\t: %s\n"
+ " Ctrl B\t: SET PIE AIE UIE SQWE DM 2412 DSE\n"
+ "\t\t: %s\n"
+ " Ctrl C\t: IRQF PF AF UF --- --- --- ---\n"
+ "\t\t: %s\n"
+ " Ctrl D\t: VRT --- --- --- --- --- --- ---\n"
+ "\t\t: %s\n"
+#if !defined(CONFIG_RTC_DRV_DS1685) && !defined(CONFIG_RTC_DRV_DS1689)
+ " Ctrl 4A\t: VRT2 INCR BME --- PAB RF WF KF\n"
+#else
+ " Ctrl 4A\t: VRT2 INCR --- --- PAB RF WF KF\n"
+#endif
+ "\t\t: %s\n"
+ " Ctrl 4B\t: ABE E32k CS RCE PRS RIE WIE KSE\n"
+ "\t\t: %s\n",
+#else
+ "Serial #\t: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+#endif
+ model,
+ ((ctrla & RTC_CTRL_A_DV1) ? "enabled" : "disabled"),
+ ((ctrlb & RTC_CTRL_B_2412) ? "24-hour" : "12-hour"),
+ ((ctrlb & RTC_CTRL_B_DSE) ? "enabled" : "disabled"),
+ ((ctrlb & RTC_CTRL_B_DM) ? "binary" : "BCD"),
+ ((ctrld & RTC_CTRL_D_VRT) ? "ok" : "exhausted or n/a"),
+ ((ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "exhausted or n/a"),
+ ((ctrlb & RTC_CTRL_B_UIE) ? "yes" : "no"),
+ ((ctrlb & RTC_CTRL_B_PIE) ? "yes" : "no"),
+ (!(ctrl4b & RTC_CTRL_4B_E32K) ?
+ ds1685_rtc_pirq_rate[(ctrla & RTC_CTRL_A_RS_MASK)] : "none"),
+ (!((ctrl4b & RTC_CTRL_4B_E32K)) ?
+ ds1685_rtc_sqw_freq[(ctrla & RTC_CTRL_A_RS_MASK)] : "32768Hz"),
+#ifdef CONFIG_RTC_DS1685_PROC_REGS
+ ssn[0], ssn[1], ssn[2], ssn[3], ssn[4], ssn[5], ssn[6], ssn[7],
+ ds1685_rtc_print_regs(ctrla, bits[0]),
+ ds1685_rtc_print_regs(ctrlb, bits[1]),
+ ds1685_rtc_print_regs(ctrlc, bits[2]),
+ ds1685_rtc_print_regs(ctrld, bits[3]),
+ ds1685_rtc_print_regs(ctrl4a, bits[4]),
+ ds1685_rtc_print_regs(ctrl4b, bits[5]));
+#else
+ ssn[0], ssn[1], ssn[2], ssn[3], ssn[4], ssn[5], ssn[6], ssn[7]);
+#endif
+ return 0;
+}
+#else
+#define ds1685_rtc_proc NULL
+#endif /* CONFIG_PROC_FS */
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* RTC Class operations */
+
+static const struct rtc_class_ops
+ds1685_rtc_ops = {
+ .proc = ds1685_rtc_proc,
+ .read_time = ds1685_rtc_read_time,
+ .set_time = ds1685_rtc_set_time,
+ .read_alarm = ds1685_rtc_read_alarm,
+ .set_alarm = ds1685_rtc_set_alarm,
+ .alarm_irq_enable = ds1685_rtc_alarm_irq_enable,
+};
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* SysFS interface */
+
+#ifdef CONFIG_SYSFS
+/**
+ * ds1685_rtc_sysfs_nvram_read - reads rtc nvram via sysfs.
+ * @file: pointer to file structure.
+ * @kobj: pointer to kobject structure.
+ * @bin_attr: pointer to bin_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ * @pos: current file position pointer.
+ * @size: size of the data to read.
+ */
+static ssize_t
+ds1685_rtc_sysfs_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ ssize_t count;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ /* Read NVRAM in time and bank0 registers. */
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ_BANK0;
+ count++, size--) {
+ if (count < NVRAM_SZ_TIME)
+ *buf++ = rtc->read(rtc, (NVRAM_TIME_BASE + pos++));
+ else
+ *buf++ = rtc->read(rtc, (NVRAM_BANK0_BASE + pos++));
+ }
+
+#ifndef CONFIG_RTC_DRV_DS1689
+ if (size > 0) {
+ ds1685_rtc_switch_to_bank1(rtc);
+
+#ifndef CONFIG_RTC_DRV_DS1685
+ /* Enable burst-mode on DS17x85/DS17x87 */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (rtc->read(rtc, RTC_EXT_CTRL_4A) |
+ RTC_CTRL_4A_BME));
+
+ /* We need one write to RTC_BANK1_RAM_ADDR_LSB to start
+ * reading with burst-mode */
+ rtc->write(rtc, RTC_BANK1_RAM_ADDR_LSB,
+ (pos - NVRAM_TOTAL_SZ_BANK0));
+#endif
+
+ /* Read NVRAM in bank1 registers. */
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ;
+ count++, size--) {
+#ifdef CONFIG_RTC_DRV_DS1685
+ /* DS1685/DS1687 has to write to RTC_BANK1_RAM_ADDR
+ * before each read. */
+ rtc->write(rtc, RTC_BANK1_RAM_ADDR,
+ (pos - NVRAM_TOTAL_SZ_BANK0));
+#endif
+ *buf++ = rtc->read(rtc, RTC_BANK1_RAM_DATA_PORT);
+ pos++;
+ }
+
+#ifndef CONFIG_RTC_DRV_DS1685
+ /* Disable burst-mode on DS17x85/DS17x87 */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (rtc->read(rtc, RTC_EXT_CTRL_4A) &
+ ~(RTC_CTRL_4A_BME)));
+#endif
+ ds1685_rtc_switch_to_bank0(rtc);
+ }
+#endif /* !CONFIG_RTC_DRV_DS1689 */
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ /*
+ * XXX: Bug? this appears to cause the function to get executed
+ * several times in succession. But it's the only way to actually get
+ * data written out to a file.
+ */
+ return count;
+}
+
+/**
+ * ds1685_rtc_sysfs_nvram_write - writes rtc nvram via sysfs.
+ * @file: pointer to file structure.
+ * @kobj: pointer to kobject structure.
+ * @bin_attr: pointer to bin_attribute structure.
+ * @buf: pointer to char array to hold the input.
+ * @pos: current file position pointer.
+ * @size: size of the data to write.
+ */
+static ssize_t
+ds1685_rtc_sysfs_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t pos, size_t size)
+{
+ struct platform_device *pdev =
+ to_platform_device(container_of(kobj, struct device, kobj));
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ ssize_t count;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ /* Write NVRAM in time and bank0 registers. */
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ_BANK0;
+ count++, size--)
+ if (count < NVRAM_SZ_TIME)
+ rtc->write(rtc, (NVRAM_TIME_BASE + pos++),
+ *buf++);
+ else
+ rtc->write(rtc, (NVRAM_BANK0_BASE), *buf++);
+
+#ifndef CONFIG_RTC_DRV_DS1689
+ if (size > 0) {
+ ds1685_rtc_switch_to_bank1(rtc);
+
+#ifndef CONFIG_RTC_DRV_DS1685
+ /* Enable burst-mode on DS17x85/DS17x87 */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (rtc->read(rtc, RTC_EXT_CTRL_4A) |
+ RTC_CTRL_4A_BME));
+
+ /* We need one write to RTC_BANK1_RAM_ADDR_LSB to start
+ * writing with burst-mode */
+ rtc->write(rtc, RTC_BANK1_RAM_ADDR_LSB,
+ (pos - NVRAM_TOTAL_SZ_BANK0));
+#endif
+
+ /* Write NVRAM in bank1 registers. */
+ for (count = 0; size > 0 && pos < NVRAM_TOTAL_SZ;
+ count++, size--) {
+#ifdef CONFIG_RTC_DRV_DS1685
+ /* DS1685/DS1687 has to write to RTC_BANK1_RAM_ADDR
+ * before each read. */
+ rtc->write(rtc, RTC_BANK1_RAM_ADDR,
+ (pos - NVRAM_TOTAL_SZ_BANK0));
+#endif
+ rtc->write(rtc, RTC_BANK1_RAM_DATA_PORT, *buf++);
+ pos++;
+ }
+
+#ifndef CONFIG_RTC_DRV_DS1685
+ /* Disable burst-mode on DS17x85/DS17x87 */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (rtc->read(rtc, RTC_EXT_CTRL_4A) &
+ ~(RTC_CTRL_4A_BME)));
+#endif
+ ds1685_rtc_switch_to_bank0(rtc);
+ }
+#endif /* !CONFIG_RTC_DRV_DS1689 */
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return count;
+}
+
+/**
+ * struct ds1685_rtc_sysfs_nvram_attr - sysfs attributes for rtc nvram.
+ * @attr: nvram attributes.
+ * @read: nvram read function.
+ * @write: nvram write function.
+ * @size: nvram total size (bank0 + extended).
+ */
+static struct bin_attribute
+ds1685_rtc_sysfs_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .read = ds1685_rtc_sysfs_nvram_read,
+ .write = ds1685_rtc_sysfs_nvram_write,
+ .size = NVRAM_TOTAL_SZ
+};
+
+/**
+ * ds1685_rtc_sysfs_battery_show - sysfs file for main battery status.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ */
+static ssize_t
+ds1685_rtc_sysfs_battery_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrld;
+
+ ctrld = rtc->read(rtc, RTC_CTRL_D);
+
+ return snprintf(buf, 13, "%s\n",
+ (ctrld & RTC_CTRL_D_VRT) ? "ok" : "not ok or N/A");
+}
+static DEVICE_ATTR(battery, S_IRUGO, ds1685_rtc_sysfs_battery_show, NULL);
+
+/**
+ * ds1685_rtc_sysfs_auxbatt_show - sysfs file for aux battery status.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ */
+static ssize_t
+ds1685_rtc_sysfs_auxbatt_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ctrl4a;
+
+ ds1685_rtc_switch_to_bank1(rtc);
+ ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ return snprintf(buf, 13, "%s\n",
+ (ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "not ok or N/A");
+}
+static DEVICE_ATTR(auxbatt, S_IRUGO, ds1685_rtc_sysfs_auxbatt_show, NULL);
+
+/**
+ * ds1685_rtc_sysfs_serial_show - sysfs file for silicon serial number.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ */
+static ssize_t
+ds1685_rtc_sysfs_serial_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+ u8 ssn[8];
+
+ ds1685_rtc_switch_to_bank1(rtc);
+ ds1685_rtc_get_ssn(rtc, ssn);
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ return snprintf(buf, 24, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+ ssn[0], ssn[1], ssn[2], ssn[3], ssn[4], ssn[5],
+ ssn[6], ssn[7]);
+
+ return 0;
+}
+static DEVICE_ATTR(serial, S_IRUGO, ds1685_rtc_sysfs_serial_show, NULL);
+
+/**
+ * struct ds1685_rtc_sysfs_misc_attrs - list for misc RTC features.
+ */
+static struct attribute*
+ds1685_rtc_sysfs_misc_attrs[] = {
+ &dev_attr_battery.attr,
+ &dev_attr_auxbatt.attr,
+ &dev_attr_serial.attr,
+ NULL,
+};
+
+/**
+ * struct ds1685_rtc_sysfs_misc_grp - attr group for misc RTC features.
+ */
+static const struct attribute_group
+ds1685_rtc_sysfs_misc_grp = {
+ .name = "misc",
+ .attrs = ds1685_rtc_sysfs_misc_attrs,
+};
+
+#ifdef CONFIG_RTC_DS1685_SYSFS_REGS
+/**
+ * struct ds1685_rtc_ctrl_regs.
+ * @name: char pointer for the bit name.
+ * @reg: control register the bit is in.
+ * @bit: the bit's offset in the register.
+ */
+struct ds1685_rtc_ctrl_regs {
+ const char *name;
+ const u8 reg;
+ const u8 bit;
+};
+
+/*
+ * Ctrl register bit lookup table.
+ */
+static const struct ds1685_rtc_ctrl_regs
+ds1685_ctrl_regs_table[] = {
+ { "uip", RTC_CTRL_A, RTC_CTRL_A_UIP },
+ { "dv2", RTC_CTRL_A, RTC_CTRL_A_DV2 },
+ { "dv1", RTC_CTRL_A, RTC_CTRL_A_DV1 },
+ { "dv0", RTC_CTRL_A, RTC_CTRL_A_DV0 },
+ { "rs3", RTC_CTRL_A, RTC_CTRL_A_RS3 },
+ { "rs2", RTC_CTRL_A, RTC_CTRL_A_RS2 },
+ { "rs1", RTC_CTRL_A, RTC_CTRL_A_RS1 },
+ { "rs0", RTC_CTRL_A, RTC_CTRL_A_RS0 },
+ { "set", RTC_CTRL_B, RTC_CTRL_B_SET },
+ { "pie", RTC_CTRL_B, RTC_CTRL_B_PIE },
+ { "aie", RTC_CTRL_B, RTC_CTRL_B_AIE },
+ { "uie", RTC_CTRL_B, RTC_CTRL_B_UIE },
+ { "sqwe", RTC_CTRL_B, RTC_CTRL_B_SQWE },
+ { "dm", RTC_CTRL_B, RTC_CTRL_B_DM },
+ { "2412", RTC_CTRL_B, RTC_CTRL_B_2412 },
+ { "dse", RTC_CTRL_B, RTC_CTRL_B_DSE },
+ { "irqf", RTC_CTRL_C, RTC_CTRL_C_IRQF },
+ { "pf", RTC_CTRL_C, RTC_CTRL_C_PF },
+ { "af", RTC_CTRL_C, RTC_CTRL_C_AF },
+ { "uf", RTC_CTRL_C, RTC_CTRL_C_UF },
+ { "vrt", RTC_CTRL_D, RTC_CTRL_D_VRT },
+ { "vrt2", RTC_EXT_CTRL_4A, RTC_CTRL_4A_VRT2 },
+ { "incr", RTC_EXT_CTRL_4A, RTC_CTRL_4A_INCR },
+ { "pab", RTC_EXT_CTRL_4A, RTC_CTRL_4A_PAB },
+ { "rf", RTC_EXT_CTRL_4A, RTC_CTRL_4A_RF },
+ { "wf", RTC_EXT_CTRL_4A, RTC_CTRL_4A_WF },
+ { "kf", RTC_EXT_CTRL_4A, RTC_CTRL_4A_KF },
+#if !defined(CONFIG_RTC_DRV_DS1685) && !defined(CONFIG_RTC_DRV_DS1689)
+ { "bme", RTC_EXT_CTRL_4A, RTC_CTRL_4A_BME },
+#endif
+ { "abe", RTC_EXT_CTRL_4B, RTC_CTRL_4B_ABE },
+ { "e32k", RTC_EXT_CTRL_4B, RTC_CTRL_4B_E32K },
+ { "cs", RTC_EXT_CTRL_4B, RTC_CTRL_4B_CS },
+ { "rce", RTC_EXT_CTRL_4B, RTC_CTRL_4B_RCE },
+ { "prs", RTC_EXT_CTRL_4B, RTC_CTRL_4B_PRS },
+ { "rie", RTC_EXT_CTRL_4B, RTC_CTRL_4B_RIE },
+ { "wie", RTC_EXT_CTRL_4B, RTC_CTRL_4B_WIE },
+ { "kse", RTC_EXT_CTRL_4B, RTC_CTRL_4B_KSE },
+ { NULL, 0, 0 },
+};
+
+/**
+ * ds1685_rtc_sysfs_ctrl_regs_lookup - ctrl register bit lookup function.
+ * @name: ctrl register bit to look up in ds1685_ctrl_regs_table.
+ */
+static const struct ds1685_rtc_ctrl_regs*
+ds1685_rtc_sysfs_ctrl_regs_lookup(const char *name)
+{
+ const struct ds1685_rtc_ctrl_regs *p = ds1685_ctrl_regs_table;
+
+ for (; p->name != NULL; ++p)
+ if (strcmp(p->name, name) == 0)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * ds1685_rtc_sysfs_ctrl_regs_show - reads a ctrl register bit via sysfs.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ */
+static ssize_t
+ds1685_rtc_sysfs_ctrl_regs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 tmp;
+ struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ const struct ds1685_rtc_ctrl_regs *reg_info =
+ ds1685_rtc_sysfs_ctrl_regs_lookup(attr->attr.name);
+
+ /* Make sure we actually matched something. */
+ if (!reg_info)
+ return -EINVAL;
+
+ /* No spinlock during a read -- mutex is already held. */
+ ds1685_rtc_switch_to_bank1(rtc);
+ tmp = rtc->read(rtc, reg_info->reg) & reg_info->bit;
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ return snprintf(buf, 2, "%d\n", (tmp ? 1 : 0));
+}
+
+/**
+ * ds1685_rtc_sysfs_ctrl_regs_store - writes a ctrl register bit via sysfs.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ * @count: number of bytes written.
+ */
+static ssize_t
+ds1685_rtc_sysfs_ctrl_regs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ u8 reg = 0, bit = 0, tmp;
+ unsigned long flags = 0;
+ long int val = 0;
+ const struct ds1685_rtc_ctrl_regs *reg_info =
+ ds1685_rtc_sysfs_ctrl_regs_lookup(attr->attr.name);
+
+ /* We only accept numbers. */
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ /* bits are binary, 0 or 1 only. */
+ if ((val != 0) && (val != 1))
+ return -ERANGE;
+
+ /* Make sure we actually matched something. */
+ if (!reg_info)
+ return -EINVAL;
+
+ reg = reg_info->reg;
+ bit = reg_info->bit;
+
+ /* Safe to spinlock during a write. */
+ ds1685_rtc_begin_ctrl_access(rtc, flags);
+ tmp = rtc->read(rtc, reg);
+ rtc->write(rtc, reg, (val ? (tmp | bit) : (tmp & ~(bit))));
+ ds1685_rtc_end_ctrl_access(rtc, flags);
+
+ return count;
+}
+
+/**
+ * DS1685_RTC_SYSFS_CTRL_REG_RO - device_attribute for read-only register bit.
+ * @bit: bit to read.
+ */
+#define DS1685_RTC_SYSFS_CTRL_REG_RO(bit) \
+ static DEVICE_ATTR(bit, S_IRUGO, \
+ ds1685_rtc_sysfs_ctrl_regs_show, NULL)
+
+/**
+ * DS1685_RTC_SYSFS_CTRL_REG_RW - device_attribute for read-write register bit.
+ * @bit: bit to read or write.
+ */
+#define DS1685_RTC_SYSFS_CTRL_REG_RW(bit) \
+ static DEVICE_ATTR(bit, S_IRUGO | S_IWUSR, \
+ ds1685_rtc_sysfs_ctrl_regs_show, \
+ ds1685_rtc_sysfs_ctrl_regs_store)
+
+/*
+ * Control Register A bits.
+ */
+DS1685_RTC_SYSFS_CTRL_REG_RO(uip);
+DS1685_RTC_SYSFS_CTRL_REG_RW(dv2);
+DS1685_RTC_SYSFS_CTRL_REG_RW(dv1);
+DS1685_RTC_SYSFS_CTRL_REG_RO(dv0);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rs3);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rs2);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rs1);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rs0);
+
+static struct attribute*
+ds1685_rtc_sysfs_ctrla_attrs[] = {
+ &dev_attr_uip.attr,
+ &dev_attr_dv2.attr,
+ &dev_attr_dv1.attr,
+ &dev_attr_dv0.attr,
+ &dev_attr_rs3.attr,
+ &dev_attr_rs2.attr,
+ &dev_attr_rs1.attr,
+ &dev_attr_rs0.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_ctrla_grp = {
+ .name = "ctrla",
+ .attrs = ds1685_rtc_sysfs_ctrla_attrs,
+};
+
+
+/*
+ * Control Register B bits.
+ */
+DS1685_RTC_SYSFS_CTRL_REG_RO(set);
+DS1685_RTC_SYSFS_CTRL_REG_RW(pie);
+DS1685_RTC_SYSFS_CTRL_REG_RW(aie);
+DS1685_RTC_SYSFS_CTRL_REG_RW(uie);
+DS1685_RTC_SYSFS_CTRL_REG_RW(sqwe);
+DS1685_RTC_SYSFS_CTRL_REG_RO(dm);
+DS1685_RTC_SYSFS_CTRL_REG_RO(2412);
+DS1685_RTC_SYSFS_CTRL_REG_RO(dse);
+
+static struct attribute*
+ds1685_rtc_sysfs_ctrlb_attrs[] = {
+ &dev_attr_set.attr,
+ &dev_attr_pie.attr,
+ &dev_attr_aie.attr,
+ &dev_attr_uie.attr,
+ &dev_attr_sqwe.attr,
+ &dev_attr_dm.attr,
+ &dev_attr_2412.attr,
+ &dev_attr_dse.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_ctrlb_grp = {
+ .name = "ctrlb",
+ .attrs = ds1685_rtc_sysfs_ctrlb_attrs,
+};
+
+/*
+ * Control Register C bits.
+ *
+ * Reading Control C clears these bits! Reading them individually can
+ * possibly cause an interrupt to be missed. Use the /proc interface
+ * to see all the bits in this register simultaneously.
+ */
+DS1685_RTC_SYSFS_CTRL_REG_RO(irqf);
+DS1685_RTC_SYSFS_CTRL_REG_RO(pf);
+DS1685_RTC_SYSFS_CTRL_REG_RO(af);
+DS1685_RTC_SYSFS_CTRL_REG_RO(uf);
+
+static struct attribute*
+ds1685_rtc_sysfs_ctrlc_attrs[] = {
+ &dev_attr_irqf.attr,
+ &dev_attr_pf.attr,
+ &dev_attr_af.attr,
+ &dev_attr_uf.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_ctrlc_grp = {
+ .name = "ctrlc",
+ .attrs = ds1685_rtc_sysfs_ctrlc_attrs,
+};
+
+/*
+ * Control Register D bits.
+ */
+DS1685_RTC_SYSFS_CTRL_REG_RO(vrt);
+
+static struct attribute*
+ds1685_rtc_sysfs_ctrld_attrs[] = {
+ &dev_attr_vrt.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_ctrld_grp = {
+ .name = "ctrld",
+ .attrs = ds1685_rtc_sysfs_ctrld_attrs,
+};
+
+/*
+ * Control Register 4A bits.
+ */
+DS1685_RTC_SYSFS_CTRL_REG_RO(vrt2);
+DS1685_RTC_SYSFS_CTRL_REG_RO(incr);
+DS1685_RTC_SYSFS_CTRL_REG_RW(pab);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rf);
+DS1685_RTC_SYSFS_CTRL_REG_RW(wf);
+DS1685_RTC_SYSFS_CTRL_REG_RW(kf);
+#if !defined(CONFIG_RTC_DRV_DS1685) && !defined(CONFIG_RTC_DRV_DS1689)
+DS1685_RTC_SYSFS_CTRL_REG_RO(bme);
+#endif
+
+static struct attribute*
+ds1685_rtc_sysfs_ctrl4a_attrs[] = {
+ &dev_attr_vrt2.attr,
+ &dev_attr_incr.attr,
+ &dev_attr_pab.attr,
+ &dev_attr_rf.attr,
+ &dev_attr_wf.attr,
+ &dev_attr_kf.attr,
+#if !defined(CONFIG_RTC_DRV_DS1685) && !defined(CONFIG_RTC_DRV_DS1689)
+ &dev_attr_bme.attr,
+#endif
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_ctrl4a_grp = {
+ .name = "ctrl4a",
+ .attrs = ds1685_rtc_sysfs_ctrl4a_attrs,
+};
+
+/*
+ * Control Register 4B bits.
+ */
+DS1685_RTC_SYSFS_CTRL_REG_RW(abe);
+DS1685_RTC_SYSFS_CTRL_REG_RW(e32k);
+DS1685_RTC_SYSFS_CTRL_REG_RO(cs);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rce);
+DS1685_RTC_SYSFS_CTRL_REG_RW(prs);
+DS1685_RTC_SYSFS_CTRL_REG_RW(rie);
+DS1685_RTC_SYSFS_CTRL_REG_RW(wie);
+DS1685_RTC_SYSFS_CTRL_REG_RW(kse);
+
+static struct attribute*
+ds1685_rtc_sysfs_ctrl4b_attrs[] = {
+ &dev_attr_abe.attr,
+ &dev_attr_e32k.attr,
+ &dev_attr_cs.attr,
+ &dev_attr_rce.attr,
+ &dev_attr_prs.attr,
+ &dev_attr_rie.attr,
+ &dev_attr_wie.attr,
+ &dev_attr_kse.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_ctrl4b_grp = {
+ .name = "ctrl4b",
+ .attrs = ds1685_rtc_sysfs_ctrl4b_attrs,
+};
+
+
+/**
+ * struct ds1685_rtc_ctrl_regs.
+ * @name: char pointer for the bit name.
+ * @reg: control register the bit is in.
+ * @bit: the bit's offset in the register.
+ */
+struct ds1685_rtc_time_regs {
+ const char *name;
+ const u8 reg;
+ const u8 mask;
+ const u8 min;
+ const u8 max;
+};
+
+/*
+ * Time/Date register lookup tables.
+ */
+static const struct ds1685_rtc_time_regs
+ds1685_time_regs_bcd_table[] = {
+ { "seconds", RTC_SECS, RTC_SECS_BCD_MASK, 0, 59 },
+ { "minutes", RTC_MINS, RTC_MINS_BCD_MASK, 0, 59 },
+ { "hours", RTC_HRS, RTC_HRS_24_BCD_MASK, 0, 23 },
+ { "wday", RTC_WDAY, RTC_WDAY_MASK, 1, 7 },
+ { "mday", RTC_MDAY, RTC_MDAY_BCD_MASK, 1, 31 },
+ { "month", RTC_MONTH, RTC_MONTH_BCD_MASK, 1, 12 },
+ { "year", RTC_YEAR, RTC_YEAR_BCD_MASK, 0, 99 },
+ { "century", RTC_CENTURY, RTC_CENTURY_MASK, 0, 99 },
+ { "alarm_seconds", RTC_SECS_ALARM, RTC_SECS_BCD_MASK, 0, 59 },
+ { "alarm_minutes", RTC_MINS_ALARM, RTC_MINS_BCD_MASK, 0, 59 },
+ { "alarm_hours", RTC_HRS_ALARM, RTC_HRS_24_BCD_MASK, 0, 23 },
+ { "alarm_mday", RTC_MDAY_ALARM, RTC_MDAY_ALARM_MASK, 1, 31 },
+ { NULL, 0, 0, 0, 0 },
+};
+
+static const struct ds1685_rtc_time_regs
+ds1685_time_regs_bin_table[] = {
+ { "seconds", RTC_SECS, RTC_SECS_BIN_MASK, 0x00, 0x3b },
+ { "minutes", RTC_MINS, RTC_MINS_BIN_MASK, 0x00, 0x3b },
+ { "hours", RTC_HRS, RTC_HRS_24_BIN_MASK, 0x00, 0x17 },
+ { "wday", RTC_WDAY, RTC_WDAY_MASK, 0x01, 0x07 },
+ { "mday", RTC_MDAY, RTC_MDAY_BIN_MASK, 0x01, 0x1f },
+ { "month", RTC_MONTH, RTC_MONTH_BIN_MASK, 0x01, 0x0c },
+ { "year", RTC_YEAR, RTC_YEAR_BIN_MASK, 0x00, 0x63 },
+ { "century", RTC_CENTURY, RTC_CENTURY_MASK, 0x00, 0x63 },
+ { "alarm_seconds", RTC_SECS_ALARM, RTC_SECS_BIN_MASK, 0x00, 0x3b },
+ { "alarm_minutes", RTC_MINS_ALARM, RTC_MINS_BIN_MASK, 0x00, 0x3b },
+ { "alarm_hours", RTC_HRS_ALARM, RTC_HRS_24_BIN_MASK, 0x00, 0x17 },
+ { "alarm_mday", RTC_MDAY_ALARM, RTC_MDAY_ALARM_MASK, 0x01, 0x1f },
+ { NULL, 0, 0, 0x00, 0x00 },
+};
+
+/**
+ * ds1685_rtc_sysfs_time_regs_bcd_lookup - time/date reg bit lookup function.
+ * @name: register bit to look up in ds1685_time_regs_bcd_table.
+ */
+static const struct ds1685_rtc_time_regs*
+ds1685_rtc_sysfs_time_regs_lookup(const char *name, bool bcd_mode)
+{
+ const struct ds1685_rtc_time_regs *p;
+
+ if (bcd_mode)
+ p = ds1685_time_regs_bcd_table;
+ else
+ p = ds1685_time_regs_bin_table;
+
+ for (; p->name != NULL; ++p)
+ if (strcmp(p->name, name) == 0)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * ds1685_rtc_sysfs_time_regs_show - reads a time/date register via sysfs.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ */
+static ssize_t
+ds1685_rtc_sysfs_time_regs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 tmp;
+ struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ const struct ds1685_rtc_time_regs *bcd_reg_info =
+ ds1685_rtc_sysfs_time_regs_lookup(attr->attr.name, true);
+ const struct ds1685_rtc_time_regs *bin_reg_info =
+ ds1685_rtc_sysfs_time_regs_lookup(attr->attr.name, false);
+
+ /* Make sure we actually matched something. */
+ if (!bcd_reg_info || !bin_reg_info)
+ return -EINVAL;
+
+ /* bcd_reg_info->reg == bin_reg_info->reg. */
+ ds1685_rtc_begin_data_access(rtc);
+ tmp = rtc->read(rtc, bcd_reg_info->reg);
+ ds1685_rtc_end_data_access(rtc);
+
+ tmp = ds1685_rtc_bcd2bin(rtc, tmp, bcd_reg_info->mask,
+ bin_reg_info->mask);
+
+ return snprintf(buf, 4, "%d\n", tmp);
+}
+
+/**
+ * ds1685_rtc_sysfs_time_regs_store - writes a time/date register via sysfs.
+ * @dev: pointer to device structure.
+ * @attr: pointer to device_attribute structure.
+ * @buf: pointer to char array to hold the output.
+ * @count: number of bytes written.
+ */
+static ssize_t
+ds1685_rtc_sysfs_time_regs_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long int val = 0;
+ struct ds1685_priv *rtc = dev_get_drvdata(dev);
+ const struct ds1685_rtc_time_regs *bcd_reg_info =
+ ds1685_rtc_sysfs_time_regs_lookup(attr->attr.name, true);
+ const struct ds1685_rtc_time_regs *bin_reg_info =
+ ds1685_rtc_sysfs_time_regs_lookup(attr->attr.name, false);
+
+ /* We only accept numbers. */
+ if (kstrtol(buf, 10, &val) < 0)
+ return -EINVAL;
+
+ /* Make sure we actually matched something. */
+ if (!bcd_reg_info || !bin_reg_info)
+ return -EINVAL;
+
+ /* Check for a valid range. */
+ if (rtc->bcd_mode) {
+ if ((val < bcd_reg_info->min) || (val > bcd_reg_info->max))
+ return -ERANGE;
+ } else {
+ if ((val < bin_reg_info->min) || (val > bin_reg_info->max))
+ return -ERANGE;
+ }
+
+ val = ds1685_rtc_bin2bcd(rtc, val, bin_reg_info->mask,
+ bcd_reg_info->mask);
+
+ /* bcd_reg_info->reg == bin_reg_info->reg. */
+ ds1685_rtc_begin_data_access(rtc);
+ rtc->write(rtc, bcd_reg_info->reg, val);
+ ds1685_rtc_end_data_access(rtc);
+
+ return count;
+}
+
+/**
+ * DS1685_RTC_SYSFS_REG_RW - device_attribute for a read-write time register.
+ * @reg: time/date register to read or write.
+ */
+#define DS1685_RTC_SYSFS_TIME_REG_RW(reg) \
+ static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, \
+ ds1685_rtc_sysfs_time_regs_show, \
+ ds1685_rtc_sysfs_time_regs_store)
+
+/*
+ * Time/Date Register bits.
+ */
+DS1685_RTC_SYSFS_TIME_REG_RW(seconds);
+DS1685_RTC_SYSFS_TIME_REG_RW(minutes);
+DS1685_RTC_SYSFS_TIME_REG_RW(hours);
+DS1685_RTC_SYSFS_TIME_REG_RW(wday);
+DS1685_RTC_SYSFS_TIME_REG_RW(mday);
+DS1685_RTC_SYSFS_TIME_REG_RW(month);
+DS1685_RTC_SYSFS_TIME_REG_RW(year);
+DS1685_RTC_SYSFS_TIME_REG_RW(century);
+DS1685_RTC_SYSFS_TIME_REG_RW(alarm_seconds);
+DS1685_RTC_SYSFS_TIME_REG_RW(alarm_minutes);
+DS1685_RTC_SYSFS_TIME_REG_RW(alarm_hours);
+DS1685_RTC_SYSFS_TIME_REG_RW(alarm_mday);
+
+static struct attribute*
+ds1685_rtc_sysfs_time_attrs[] = {
+ &dev_attr_seconds.attr,
+ &dev_attr_minutes.attr,
+ &dev_attr_hours.attr,
+ &dev_attr_wday.attr,
+ &dev_attr_mday.attr,
+ &dev_attr_month.attr,
+ &dev_attr_year.attr,
+ &dev_attr_century.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_time_grp = {
+ .name = "datetime",
+ .attrs = ds1685_rtc_sysfs_time_attrs,
+};
+
+static struct attribute*
+ds1685_rtc_sysfs_alarm_attrs[] = {
+ &dev_attr_alarm_seconds.attr,
+ &dev_attr_alarm_minutes.attr,
+ &dev_attr_alarm_hours.attr,
+ &dev_attr_alarm_mday.attr,
+ NULL,
+};
+
+static const struct attribute_group
+ds1685_rtc_sysfs_alarm_grp = {
+ .name = "alarm",
+ .attrs = ds1685_rtc_sysfs_alarm_attrs,
+};
+#endif /* CONFIG_RTC_DS1685_SYSFS_REGS */
+
+
+/**
+ * ds1685_rtc_sysfs_register - register sysfs files.
+ * @dev: pointer to device structure.
+ */
+static int
+ds1685_rtc_sysfs_register(struct device *dev)
+{
+ int ret = 0;
+
+ sysfs_bin_attr_init(&ds1685_rtc_sysfs_nvram_attr);
+ ret = sysfs_create_bin_file(&dev->kobj, &ds1685_rtc_sysfs_nvram_attr);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_misc_grp);
+ if (ret)
+ return ret;
+
+#ifdef CONFIG_RTC_DS1685_SYSFS_REGS
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_ctrla_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_ctrlb_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_ctrlc_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_ctrld_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_ctrl4a_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_ctrl4b_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_time_grp);
+ if (ret)
+ return ret;
+
+ ret = sysfs_create_group(&dev->kobj, &ds1685_rtc_sysfs_alarm_grp);
+ if (ret)
+ return ret;
+#endif
+ return 0;
+}
+
+/**
+ * ds1685_rtc_sysfs_unregister - unregister sysfs files.
+ * @dev: pointer to device structure.
+ */
+static int
+ds1685_rtc_sysfs_unregister(struct device *dev)
+{
+ sysfs_remove_bin_file(&dev->kobj, &ds1685_rtc_sysfs_nvram_attr);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_misc_grp);
+
+#ifdef CONFIG_RTC_DS1685_SYSFS_REGS
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_ctrla_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_ctrlb_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_ctrlc_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_ctrld_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_ctrl4a_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_ctrl4b_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_time_grp);
+ sysfs_remove_group(&dev->kobj, &ds1685_rtc_sysfs_alarm_grp);
+#endif
+
+ return 0;
+}
+#endif /* CONFIG_SYSFS */
+
+
+
+/* ----------------------------------------------------------------------- */
+/* Driver Probe/Removal */
+
+/**
+ * ds1685_rtc_probe - initializes rtc driver.
+ * @pdev: pointer to platform_device structure.
+ */
+static int
+ds1685_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc_dev;
+ struct resource *res;
+ struct ds1685_priv *rtc;
+ struct ds1685_rtc_platform_data *pdata;
+ u8 ctrla, ctrlb, hours;
+ unsigned char am_pm;
+ int ret = 0;
+
+ /* Get the platform data. */
+ pdata = (struct ds1685_rtc_platform_data *) pdev->dev.platform_data;
+ if (!pdata)
+ return -ENODEV;
+
+ /* Allocate memory for the rtc device. */
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ /*
+ * Allocate/setup any IORESOURCE_MEM resources, if required. Not all
+ * platforms put the RTC in an easy-access place. Like the SGI Octane,
+ * which attaches the RTC to a "ByteBus", hooked to a SuperIO chip
+ * that sits behind the IOC3 PCI metadevice.
+ */
+ if (pdata->alloc_io_resources) {
+ /* Get the platform resources. */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+ rtc->size = resource_size(res);
+
+ /* Request a memory region. */
+ /* XXX: mmio-only for now. */
+ if (!devm_request_mem_region(&pdev->dev, res->start, rtc->size,
+ pdev->name))
+ return -EBUSY;
+
+ /*
+ * Set the base address for the rtc, and ioremap its
+ * registers.
+ */
+ rtc->baseaddr = res->start;
+ rtc->regs = devm_ioremap(&pdev->dev, res->start, rtc->size);
+ if (!rtc->regs)
+ return -ENOMEM;
+ }
+ rtc->alloc_io_resources = pdata->alloc_io_resources;
+
+ /* Get the register step size. */
+ if (pdata->regstep > 0)
+ rtc->regstep = pdata->regstep;
+ else
+ rtc->regstep = 1;
+
+ /* Platform read function, else default if mmio setup */
+ if (pdata->plat_read)
+ rtc->read = pdata->plat_read;
+ else
+ if (pdata->alloc_io_resources)
+ rtc->read = ds1685_read;
+ else
+ return -ENXIO;
+
+ /* Platform write function, else default if mmio setup */
+ if (pdata->plat_write)
+ rtc->write = pdata->plat_write;
+ else
+ if (pdata->alloc_io_resources)
+ rtc->write = ds1685_write;
+ else
+ return -ENXIO;
+
+ /* Platform pre-shutdown function, if defined. */
+ if (pdata->plat_prepare_poweroff)
+ rtc->prepare_poweroff = pdata->plat_prepare_poweroff;
+
+ /* Platform wake_alarm function, if defined. */
+ if (pdata->plat_wake_alarm)
+ rtc->wake_alarm = pdata->plat_wake_alarm;
+
+ /* Platform post_ram_clear function, if defined. */
+ if (pdata->plat_post_ram_clear)
+ rtc->post_ram_clear = pdata->plat_post_ram_clear;
+
+ /* Init the spinlock, workqueue, & set the driver data. */
+ spin_lock_init(&rtc->lock);
+ INIT_WORK(&rtc->work, ds1685_rtc_work_queue);
+ platform_set_drvdata(pdev, rtc);
+
+ /* Turn the oscillator on if is not already on (DV1 = 1). */
+ ctrla = rtc->read(rtc, RTC_CTRL_A);
+ if (!(ctrla & RTC_CTRL_A_DV1))
+ ctrla |= RTC_CTRL_A_DV1;
+
+ /* Enable the countdown chain (DV2 = 0) */
+ ctrla &= ~(RTC_CTRL_A_DV2);
+
+ /* Clear RS3-RS0 in Control A. */
+ ctrla &= ~(RTC_CTRL_A_RS_MASK);
+
+ /*
+ * All done with Control A. Switch to Bank 1 for the remainder of
+ * the RTC setup so we have access to the extended functions.
+ */
+ ctrla |= RTC_CTRL_A_DV0;
+ rtc->write(rtc, RTC_CTRL_A, ctrla);
+
+ /* Default to 32768kHz output. */
+ rtc->write(rtc, RTC_EXT_CTRL_4B,
+ (rtc->read(rtc, RTC_EXT_CTRL_4B) | RTC_CTRL_4B_E32K));
+
+ /* Set the SET bit in Control B so we can do some housekeeping. */
+ rtc->write(rtc, RTC_CTRL_B,
+ (rtc->read(rtc, RTC_CTRL_B) | RTC_CTRL_B_SET));
+
+ /* Read Ext Ctrl 4A and check the INCR bit to avoid a lockout. */
+ while (rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_INCR)
+ cpu_relax();
+
+ /*
+ * If the platform supports BCD mode, then set DM=0 in Control B.
+ * Otherwise, set DM=1 for BIN mode.
+ */
+ ctrlb = rtc->read(rtc, RTC_CTRL_B);
+ if (pdata->bcd_mode)
+ ctrlb &= ~(RTC_CTRL_B_DM);
+ else
+ ctrlb |= RTC_CTRL_B_DM;
+ rtc->bcd_mode = pdata->bcd_mode;
+
+ /*
+ * Disable Daylight Savings Time (DSE = 0).
+ * The RTC has hardcoded timezone information that is rendered
+ * obselete. We'll let the OS deal with DST settings instead.
+ */
+ if (ctrlb & RTC_CTRL_B_DSE)
+ ctrlb &= ~(RTC_CTRL_B_DSE);
+
+ /* Force 24-hour mode (2412 = 1). */
+ if (!(ctrlb & RTC_CTRL_B_2412)) {
+ /* Reinitialize the time hours. */
+ hours = rtc->read(rtc, RTC_HRS);
+ am_pm = hours & RTC_HRS_AMPM_MASK;
+ hours = ds1685_rtc_bcd2bin(rtc, hours, RTC_HRS_12_BCD_MASK,
+ RTC_HRS_12_BIN_MASK);
+ hours = ((hours == 12) ? 0 : ((am_pm) ? hours + 12 : hours));
+
+ /* Enable 24-hour mode. */
+ ctrlb |= RTC_CTRL_B_2412;
+
+ /* Write back to Control B, including DM & DSE bits. */
+ rtc->write(rtc, RTC_CTRL_B, ctrlb);
+
+ /* Write the time hours back. */
+ rtc->write(rtc, RTC_HRS,
+ ds1685_rtc_bin2bcd(rtc, hours,
+ RTC_HRS_24_BIN_MASK,
+ RTC_HRS_24_BCD_MASK));
+
+ /* Reinitialize the alarm hours. */
+ hours = rtc->read(rtc, RTC_HRS_ALARM);
+ am_pm = hours & RTC_HRS_AMPM_MASK;
+ hours = ds1685_rtc_bcd2bin(rtc, hours, RTC_HRS_12_BCD_MASK,
+ RTC_HRS_12_BIN_MASK);
+ hours = ((hours == 12) ? 0 : ((am_pm) ? hours + 12 : hours));
+
+ /* Write the alarm hours back. */
+ rtc->write(rtc, RTC_HRS_ALARM,
+ ds1685_rtc_bin2bcd(rtc, hours,
+ RTC_HRS_24_BIN_MASK,
+ RTC_HRS_24_BCD_MASK));
+ } else {
+ /* 24-hour mode is already set, so write Control B back. */
+ rtc->write(rtc, RTC_CTRL_B, ctrlb);
+ }
+
+ /* Unset the SET bit in Control B so the RTC can update. */
+ rtc->write(rtc, RTC_CTRL_B,
+ (rtc->read(rtc, RTC_CTRL_B) & ~(RTC_CTRL_B_SET)));
+
+ /* Check the main battery. */
+ if (!(rtc->read(rtc, RTC_CTRL_D) & RTC_CTRL_D_VRT))
+ dev_warn(&pdev->dev,
+ "Main battery is exhausted! RTC may be invalid!\n");
+
+ /* Check the auxillary battery. It is optional. */
+ if (!(rtc->read(rtc, RTC_EXT_CTRL_4A) & RTC_CTRL_4A_VRT2))
+ dev_warn(&pdev->dev,
+ "Aux battery is exhausted or not available.\n");
+
+ /* Read Ctrl B and clear PIE/AIE/UIE. */
+ rtc->write(rtc, RTC_CTRL_B,
+ (rtc->read(rtc, RTC_CTRL_B) & ~(RTC_CTRL_B_PAU_MASK)));
+
+ /* Reading Ctrl C auto-clears PF/AF/UF. */
+ rtc->read(rtc, RTC_CTRL_C);
+
+ /* Read Ctrl 4B and clear RIE/WIE/KSE. */
+ rtc->write(rtc, RTC_EXT_CTRL_4B,
+ (rtc->read(rtc, RTC_EXT_CTRL_4B) & ~(RTC_CTRL_4B_RWK_MASK)));
+
+ /* Clear RF/WF/KF in Ctrl 4A. */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (rtc->read(rtc, RTC_EXT_CTRL_4A) & ~(RTC_CTRL_4A_RWK_MASK)));
+
+ /*
+ * Re-enable KSE to handle power button events. We do not enable
+ * WIE or RIE by default.
+ */
+ rtc->write(rtc, RTC_EXT_CTRL_4B,
+ (rtc->read(rtc, RTC_EXT_CTRL_4B) | RTC_CTRL_4B_KSE));
+
+ /*
+ * Fetch the IRQ and setup the interrupt handler.
+ *
+ * Not all platforms have the IRQF pin tied to something. If not, the
+ * RTC will still set the *IE / *F flags and raise IRQF in ctrlc, but
+ * there won't be an automatic way of notifying the kernel about it,
+ * unless ctrlc is explicitly polled.
+ */
+ if (!pdata->no_irq) {
+ ret = platform_get_irq(pdev, 0);
+ if (ret > 0) {
+ rtc->irq_num = ret;
+
+ /* Request an IRQ. */
+ ret = devm_request_irq(&pdev->dev, rtc->irq_num,
+ ds1685_rtc_irq_handler,
+ IRQF_SHARED, pdev->name, pdev);
+
+ /* Check to see if something came back. */
+ if (unlikely(ret)) {
+ dev_warn(&pdev->dev,
+ "RTC interrupt not available\n");
+ rtc->irq_num = 0;
+ }
+ } else
+ return ret;
+ }
+ rtc->no_irq = pdata->no_irq;
+
+ /* Setup complete. */
+ ds1685_rtc_switch_to_bank0(rtc);
+
+ /* Register the device as an RTC. */
+ rtc_dev = rtc_device_register(pdev->name, &pdev->dev,
+ &ds1685_rtc_ops, THIS_MODULE);
+
+ /* Success? */
+ if (IS_ERR(rtc_dev))
+ return PTR_ERR(rtc_dev);
+
+ /* Maximum periodic rate is 8192Hz (0.122070ms). */
+ rtc_dev->max_user_freq = RTC_MAX_USER_FREQ;
+
+ /* See if the platform doesn't support UIE. */
+ if (pdata->uie_unsupported)
+ rtc_dev->uie_unsupported = 1;
+ rtc->uie_unsupported = pdata->uie_unsupported;
+
+ rtc->dev = rtc_dev;
+
+#ifdef CONFIG_SYSFS
+ ret = ds1685_rtc_sysfs_register(&pdev->dev);
+ if (ret)
+ rtc_device_unregister(rtc->dev);
+#endif
+
+ /* Done! */
+ return ret;
+}
+
+/**
+ * ds1685_rtc_remove - removes rtc driver.
+ * @pdev: pointer to platform_device structure.
+ */
+static int
+ds1685_rtc_remove(struct platform_device *pdev)
+{
+ struct ds1685_priv *rtc = platform_get_drvdata(pdev);
+
+#ifdef CONFIG_SYSFS
+ ds1685_rtc_sysfs_unregister(&pdev->dev);
+#endif
+
+ rtc_device_unregister(rtc->dev);
+
+ /* Read Ctrl B and clear PIE/AIE/UIE. */
+ rtc->write(rtc, RTC_CTRL_B,
+ (rtc->read(rtc, RTC_CTRL_B) &
+ ~(RTC_CTRL_B_PAU_MASK)));
+
+ /* Reading Ctrl C auto-clears PF/AF/UF. */
+ rtc->read(rtc, RTC_CTRL_C);
+
+ /* Read Ctrl 4B and clear RIE/WIE/KSE. */
+ rtc->write(rtc, RTC_EXT_CTRL_4B,
+ (rtc->read(rtc, RTC_EXT_CTRL_4B) &
+ ~(RTC_CTRL_4B_RWK_MASK)));
+
+ /* Manually clear RF/WF/KF in Ctrl 4A. */
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (rtc->read(rtc, RTC_EXT_CTRL_4A) &
+ ~(RTC_CTRL_4A_RWK_MASK)));
+
+ cancel_work_sync(&rtc->work);
+
+ return 0;
+}
+
+/**
+ * ds1685_rtc_driver - rtc driver properties.
+ */
+static struct platform_driver ds1685_rtc_driver = {
+ .driver = {
+ .name = "rtc-ds1685",
+ },
+ .probe = ds1685_rtc_probe,
+ .remove = ds1685_rtc_remove,
+};
+
+/**
+ * ds1685_rtc_init - rtc module init.
+ */
+static int __init
+ds1685_rtc_init(void)
+{
+ return platform_driver_register(&ds1685_rtc_driver);
+}
+
+/**
+ * ds1685_rtc_exit - rtc module exit.
+ */
+static void __exit
+ds1685_rtc_exit(void)
+{
+ platform_driver_unregister(&ds1685_rtc_driver);
+}
+
+module_init(ds1685_rtc_init);
+module_exit(ds1685_rtc_exit);
+/* ----------------------------------------------------------------------- */
+
+
+/* ----------------------------------------------------------------------- */
+/* Poweroff function */
+
+/**
+ * ds1685_rtc_poweroff - uses the RTC chip to power the system off.
+ * @pdev: pointer to platform_device structure.
+ */
+void __noreturn
+ds1685_rtc_poweroff(struct platform_device *pdev)
+{
+ u8 ctrla, ctrl4a, ctrl4b;
+ struct ds1685_priv *rtc;
+
+ /* Check for valid RTC data, else, spin forever. */
+ if (unlikely(!pdev)) {
+ pr_emerg("platform device data not available, spinning forever ...\n");
+ unreachable();
+ } else {
+ /* Get the rtc data. */
+ rtc = platform_get_drvdata(pdev);
+
+ /*
+ * Disable our IRQ. We're powering down, so we're not
+ * going to worry about cleaning up. Most of that should
+ * have been taken care of by the shutdown scripts and this
+ * is the final function call.
+ */
+ if (!rtc->no_irq)
+ disable_irq_nosync(rtc->irq_num);
+
+ /* Oscillator must be on and the countdown chain enabled. */
+ ctrla = rtc->read(rtc, RTC_CTRL_A);
+ ctrla |= RTC_CTRL_A_DV1;
+ ctrla &= ~(RTC_CTRL_A_DV2);
+ rtc->write(rtc, RTC_CTRL_A, ctrla);
+
+ /*
+ * Read Control 4A and check the status of the auxillary
+ * battery. This must be present and working (VRT2 = 1)
+ * for wakeup and kickstart functionality to be useful.
+ */
+ ds1685_rtc_switch_to_bank1(rtc);
+ ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
+ if (ctrl4a & RTC_CTRL_4A_VRT2) {
+ /* Clear all of the interrupt flags on Control 4A. */
+ ctrl4a &= ~(RTC_CTRL_4A_RWK_MASK);
+ rtc->write(rtc, RTC_EXT_CTRL_4A, ctrl4a);
+
+ /*
+ * The auxillary battery is present and working.
+ * Enable extended functions (ABE=1), enable
+ * wake-up (WIE=1), and enable kickstart (KSE=1)
+ * in Control 4B.
+ */
+ ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B);
+ ctrl4b |= (RTC_CTRL_4B_ABE | RTC_CTRL_4B_WIE |
+ RTC_CTRL_4B_KSE);
+ rtc->write(rtc, RTC_EXT_CTRL_4B, ctrl4b);
+ }
+
+ /* Set PAB to 1 in Control 4A to power the system down. */
+ dev_warn(&pdev->dev, "Powerdown.\n");
+ msleep(20);
+ rtc->write(rtc, RTC_EXT_CTRL_4A,
+ (ctrl4a | RTC_CTRL_4A_PAB));
+
+ /* Spin ... we do not switch back to bank0. */
+ unreachable();
+ }
+}
+EXPORT_SYMBOL(ds1685_rtc_poweroff);
+/* ----------------------------------------------------------------------- */
+
+
+MODULE_AUTHOR("Joshua Kinard <kumba@gentoo.org>");
+MODULE_AUTHOR("Matthias Fuchs <matthias.fuchs@esd-electronics.com>");
+MODULE_DESCRIPTION("Dallas/Maxim DS1685/DS1687-series RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:rtc-ds1685");
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
new file mode 100644
index 000000000..0f8d8ace1
--- /dev/null
+++ b/drivers/rtc/rtc-ds1742.c
@@ -0,0 +1,243 @@
+/*
+ * An rtc driver for the Dallas DS1742
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Copyright (C) 2006 Torsten Ertbjerg Rasmussen <tr@newtec.dk>
+ * - nvram size determined from resource
+ * - this ds1742 driver now supports ds1743.
+ */
+
+#include <linux/bcd.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.4"
+
+#define RTC_SIZE 8
+
+#define RTC_CONTROL 0
+#define RTC_CENTURY 0
+#define RTC_SECONDS 1
+#define RTC_MINUTES 2
+#define RTC_HOURS 3
+#define RTC_DAY 4
+#define RTC_DATE 5
+#define RTC_MONTH 6
+#define RTC_YEAR 7
+
+#define RTC_CENTURY_MASK 0x3f
+#define RTC_SECONDS_MASK 0x7f
+#define RTC_DAY_MASK 0x07
+
+/* Bits in the Control/Century register */
+#define RTC_WRITE 0x80
+#define RTC_READ 0x40
+
+/* Bits in the Seconds register */
+#define RTC_STOP 0x80
+
+/* Bits in the Day register */
+#define RTC_BATT_FLAG 0x80
+
+struct rtc_plat_data {
+ void __iomem *ioaddr_nvram;
+ void __iomem *ioaddr_rtc;
+ size_t size_nvram;
+ unsigned long last_jiffies;
+ struct bin_attribute nvram_attr;
+};
+
+static int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr_rtc;
+ u8 century;
+
+ century = bin2bcd((tm->tm_year + 1900) / 100);
+
+ writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
+
+ writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR);
+ writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH);
+ writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
+ writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE);
+ writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS);
+ writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES);
+ writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
+
+ /* RTC_CENTURY and RTC_CONTROL share same register */
+ writeb(RTC_WRITE | (century & RTC_CENTURY_MASK), ioaddr + RTC_CENTURY);
+ writeb(century & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ return 0;
+}
+
+static int ds1742_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr_rtc;
+ unsigned int year, month, day, hour, minute, second, week;
+ unsigned int century;
+
+ /* give enough time to update RTC in case of continuous read */
+ if (pdata->last_jiffies == jiffies)
+ msleep(1);
+ pdata->last_jiffies = jiffies;
+ writeb(RTC_READ, ioaddr + RTC_CONTROL);
+ second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
+ minute = readb(ioaddr + RTC_MINUTES);
+ hour = readb(ioaddr + RTC_HOURS);
+ day = readb(ioaddr + RTC_DATE);
+ week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
+ month = readb(ioaddr + RTC_MONTH);
+ year = readb(ioaddr + RTC_YEAR);
+ century = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(0, ioaddr + RTC_CONTROL);
+ tm->tm_sec = bcd2bin(second);
+ tm->tm_min = bcd2bin(minute);
+ tm->tm_hour = bcd2bin(hour);
+ tm->tm_mday = bcd2bin(day);
+ tm->tm_wday = bcd2bin(week);
+ tm->tm_mon = bcd2bin(month) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
+
+ return rtc_valid_tm(tm);
+}
+
+static const struct rtc_class_ops ds1742_rtc_ops = {
+ .read_time = ds1742_rtc_read_time,
+ .set_time = ds1742_rtc_set_time,
+};
+
+static ssize_t ds1742_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr_nvram;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < pdata->size_nvram; count++, size--)
+ *buf++ = readb(ioaddr + pos++);
+ return count;
+}
+
+static ssize_t ds1742_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr_nvram;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < pdata->size_nvram; count++, size--)
+ writeb(*buf++, ioaddr + pos++);
+ return count;
+}
+
+static int ds1742_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ unsigned int cen, sec;
+ struct rtc_plat_data *pdata;
+ void __iomem *ioaddr;
+ int ret = 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ioaddr))
+ return PTR_ERR(ioaddr);
+
+ pdata->ioaddr_nvram = ioaddr;
+ pdata->size_nvram = resource_size(res) - RTC_SIZE;
+ pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
+
+ sysfs_bin_attr_init(&pdata->nvram_attr);
+ pdata->nvram_attr.attr.name = "nvram";
+ pdata->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ pdata->nvram_attr.read = ds1742_nvram_read;
+ pdata->nvram_attr.write = ds1742_nvram_write;
+ pdata->nvram_attr.size = pdata->size_nvram;
+
+ /* turn RTC on if it was not on */
+ ioaddr = pdata->ioaddr_rtc;
+ sec = readb(ioaddr + RTC_SECONDS);
+ if (sec & RTC_STOP) {
+ sec &= RTC_SECONDS_MASK;
+ cen = readb(ioaddr + RTC_CENTURY) & RTC_CENTURY_MASK;
+ writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
+ writeb(sec, ioaddr + RTC_SECONDS);
+ writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
+ }
+ if (!(readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG))
+ dev_warn(&pdev->dev, "voltage-low detected.\n");
+
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &ds1742_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
+ if (ret)
+ dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n",
+ pdata->nvram_attr.attr.name);
+
+ return 0;
+}
+
+static int ds1742_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pdata->nvram_attr);
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused ds1742_rtc_of_match[] = {
+ { .compatible = "maxim,ds1742", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ds1742_rtc_of_match);
+
+static struct platform_driver ds1742_rtc_driver = {
+ .probe = ds1742_rtc_probe,
+ .remove = ds1742_rtc_remove,
+ .driver = {
+ .name = "rtc-ds1742",
+ .of_match_table = of_match_ptr(ds1742_rtc_of_match),
+ },
+};
+
+module_platform_driver(ds1742_rtc_driver);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Dallas DS1742 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:rtc-ds1742");
diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c
new file mode 100644
index 000000000..7885edd3d
--- /dev/null
+++ b/drivers/rtc/rtc-ds2404.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2012 Sven Schnelle <svens@stackframe.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+#include <linux/bcd.h>
+#include <linux/rtc-ds2404.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+
+#define DS2404_STATUS_REG 0x200
+#define DS2404_CONTROL_REG 0x201
+#define DS2404_RTC_REG 0x202
+
+#define DS2404_WRITE_SCRATCHPAD_CMD 0x0f
+#define DS2404_READ_SCRATCHPAD_CMD 0xaa
+#define DS2404_COPY_SCRATCHPAD_CMD 0x55
+#define DS2404_READ_MEMORY_CMD 0xf0
+
+struct ds2404;
+
+struct ds2404_chip_ops {
+ int (*map_io)(struct ds2404 *chip, struct platform_device *pdev,
+ struct ds2404_platform_data *pdata);
+ void (*unmap_io)(struct ds2404 *chip);
+};
+
+#define DS2404_RST 0
+#define DS2404_CLK 1
+#define DS2404_DQ 2
+
+struct ds2404_gpio {
+ const char *name;
+ unsigned int gpio;
+};
+
+struct ds2404 {
+ struct ds2404_gpio *gpio;
+ struct ds2404_chip_ops *ops;
+ struct rtc_device *rtc;
+};
+
+static struct ds2404_gpio ds2404_gpio[] = {
+ { "RTC RST", 0 },
+ { "RTC CLK", 0 },
+ { "RTC DQ", 0 },
+};
+
+static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev,
+ struct ds2404_platform_data *pdata)
+{
+ int i, err;
+
+ ds2404_gpio[DS2404_RST].gpio = pdata->gpio_rst;
+ ds2404_gpio[DS2404_CLK].gpio = pdata->gpio_clk;
+ ds2404_gpio[DS2404_DQ].gpio = pdata->gpio_dq;
+
+ for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) {
+ err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name);
+ if (err) {
+ dev_err(&pdev->dev, "error mapping gpio %s: %d\n",
+ ds2404_gpio[i].name, err);
+ goto err_request;
+ }
+ if (i != DS2404_DQ)
+ gpio_direction_output(ds2404_gpio[i].gpio, 1);
+ }
+
+ chip->gpio = ds2404_gpio;
+ return 0;
+
+err_request:
+ while (--i >= 0)
+ gpio_free(ds2404_gpio[i].gpio);
+ return err;
+}
+
+static void ds2404_gpio_unmap(struct ds2404 *chip)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++)
+ gpio_free(ds2404_gpio[i].gpio);
+}
+
+static struct ds2404_chip_ops ds2404_gpio_ops = {
+ .map_io = ds2404_gpio_map,
+ .unmap_io = ds2404_gpio_unmap,
+};
+
+static void ds2404_reset(struct device *dev)
+{
+ gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0);
+ udelay(1000);
+ gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 1);
+ gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
+ gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 0);
+ udelay(10);
+}
+
+static void ds2404_write_byte(struct device *dev, u8 byte)
+{
+ int i;
+
+ gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 1);
+ for (i = 0; i < 8; i++) {
+ gpio_set_value(ds2404_gpio[DS2404_DQ].gpio, byte & (1 << i));
+ udelay(10);
+ gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1);
+ udelay(10);
+ gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
+ udelay(10);
+ }
+}
+
+static u8 ds2404_read_byte(struct device *dev)
+{
+ int i;
+ u8 ret = 0;
+
+ gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio);
+
+ for (i = 0; i < 8; i++) {
+ gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
+ udelay(10);
+ if (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio))
+ ret |= 1 << i;
+ gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1);
+ udelay(10);
+ }
+ return ret;
+}
+
+static void ds2404_read_memory(struct device *dev, u16 offset,
+ int length, u8 *out)
+{
+ ds2404_reset(dev);
+ ds2404_write_byte(dev, DS2404_READ_MEMORY_CMD);
+ ds2404_write_byte(dev, offset & 0xff);
+ ds2404_write_byte(dev, (offset >> 8) & 0xff);
+ while (length--)
+ *out++ = ds2404_read_byte(dev);
+}
+
+static void ds2404_write_memory(struct device *dev, u16 offset,
+ int length, u8 *out)
+{
+ int i;
+ u8 ta01, ta02, es;
+
+ ds2404_reset(dev);
+ ds2404_write_byte(dev, DS2404_WRITE_SCRATCHPAD_CMD);
+ ds2404_write_byte(dev, offset & 0xff);
+ ds2404_write_byte(dev, (offset >> 8) & 0xff);
+
+ for (i = 0; i < length; i++)
+ ds2404_write_byte(dev, out[i]);
+
+ ds2404_reset(dev);
+ ds2404_write_byte(dev, DS2404_READ_SCRATCHPAD_CMD);
+
+ ta01 = ds2404_read_byte(dev);
+ ta02 = ds2404_read_byte(dev);
+ es = ds2404_read_byte(dev);
+
+ for (i = 0; i < length; i++) {
+ if (out[i] != ds2404_read_byte(dev)) {
+ dev_err(dev, "read invalid data\n");
+ return;
+ }
+ }
+
+ ds2404_reset(dev);
+ ds2404_write_byte(dev, DS2404_COPY_SCRATCHPAD_CMD);
+ ds2404_write_byte(dev, ta01);
+ ds2404_write_byte(dev, ta02);
+ ds2404_write_byte(dev, es);
+
+ gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio);
+ while (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio))
+ ;
+}
+
+static void ds2404_enable_osc(struct device *dev)
+{
+ u8 in[1] = { 0x10 }; /* enable oscillator */
+ ds2404_write_memory(dev, 0x201, 1, in);
+}
+
+static int ds2404_read_time(struct device *dev, struct rtc_time *dt)
+{
+ unsigned long time = 0;
+
+ ds2404_read_memory(dev, 0x203, 4, (u8 *)&time);
+ time = le32_to_cpu(time);
+
+ rtc_time_to_tm(time, dt);
+ return rtc_valid_tm(dt);
+}
+
+static int ds2404_set_mmss(struct device *dev, unsigned long secs)
+{
+ u32 time = cpu_to_le32(secs);
+ ds2404_write_memory(dev, 0x203, 4, (u8 *)&time);
+ return 0;
+}
+
+static const struct rtc_class_ops ds2404_rtc_ops = {
+ .read_time = ds2404_read_time,
+ .set_mmss = ds2404_set_mmss,
+};
+
+static int rtc_probe(struct platform_device *pdev)
+{
+ struct ds2404_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct ds2404 *chip;
+ int retval = -EBUSY;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(struct ds2404), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ chip->ops = &ds2404_gpio_ops;
+
+ retval = chip->ops->map_io(chip, pdev, pdata);
+ if (retval)
+ goto err_chip;
+
+ dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n",
+ chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio,
+ chip->gpio[DS2404_DQ].gpio);
+
+ platform_set_drvdata(pdev, chip);
+
+ chip->rtc = devm_rtc_device_register(&pdev->dev, "ds2404",
+ &ds2404_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ retval = PTR_ERR(chip->rtc);
+ goto err_io;
+ }
+
+ ds2404_enable_osc(&pdev->dev);
+ return 0;
+
+err_io:
+ chip->ops->unmap_io(chip);
+err_chip:
+ return retval;
+}
+
+static int rtc_remove(struct platform_device *dev)
+{
+ struct ds2404 *chip = platform_get_drvdata(dev);
+
+ chip->ops->unmap_io(chip);
+
+ return 0;
+}
+
+static struct platform_driver rtc_device_driver = {
+ .probe = rtc_probe,
+ .remove = rtc_remove,
+ .driver = {
+ .name = "ds2404",
+ },
+};
+module_platform_driver(rtc_device_driver);
+
+MODULE_DESCRIPTION("DS2404 RTC");
+MODULE_AUTHOR("Sven Schnelle");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ds2404");
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
new file mode 100644
index 000000000..7e48e5322
--- /dev/null
+++ b/drivers/rtc/rtc-ds3232.c
@@ -0,0 +1,515 @@
+/*
+ * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C
+ *
+ * Copyright (C) 2009-2011 Freescale Semiconductor.
+ * Author: Jack Lan <jack.lan@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+/*
+ * It would be more efficient to use i2c msgs/i2c_transfer directly but, as
+ * recommened in .../Documentation/i2c/writing-clients section
+ * "Sending and receiving", using SMBus level communication is preferred.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#define DS3232_REG_SECONDS 0x00
+#define DS3232_REG_MINUTES 0x01
+#define DS3232_REG_HOURS 0x02
+#define DS3232_REG_AMPM 0x02
+#define DS3232_REG_DAY 0x03
+#define DS3232_REG_DATE 0x04
+#define DS3232_REG_MONTH 0x05
+#define DS3232_REG_CENTURY 0x05
+#define DS3232_REG_YEAR 0x06
+#define DS3232_REG_ALARM1 0x07 /* Alarm 1 BASE */
+#define DS3232_REG_ALARM2 0x0B /* Alarm 2 BASE */
+#define DS3232_REG_CR 0x0E /* Control register */
+# define DS3232_REG_CR_nEOSC 0x80
+# define DS3232_REG_CR_INTCN 0x04
+# define DS3232_REG_CR_A2IE 0x02
+# define DS3232_REG_CR_A1IE 0x01
+
+#define DS3232_REG_SR 0x0F /* control/status register */
+# define DS3232_REG_SR_OSF 0x80
+# define DS3232_REG_SR_BSY 0x04
+# define DS3232_REG_SR_A2F 0x02
+# define DS3232_REG_SR_A1F 0x01
+
+struct ds3232 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+
+ /* The mutex protects alarm operations, and prevents a race
+ * between the enable_irq() in the workqueue and the free_irq()
+ * in the remove function.
+ */
+ struct mutex mutex;
+ bool suspended;
+ int exiting;
+};
+
+static struct i2c_driver ds3232_driver;
+
+static int ds3232_check_rtc_status(struct i2c_client *client)
+{
+ int ret = 0;
+ int control, stat;
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ return stat;
+
+ if (stat & DS3232_REG_SR_OSF)
+ dev_warn(&client->dev,
+ "oscillator discontinuity flagged, "
+ "time unreliable\n");
+
+ stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ return ret;
+
+ /* If the alarm is pending, clear it before requesting
+ * the interrupt, so an interrupt event isn't reported
+ * before everything is initialized.
+ */
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ return control;
+
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ control |= DS3232_REG_CR_INTCN;
+
+ return i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+}
+
+static int ds3232_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+ u8 buf[7];
+ unsigned int year, month, day, hour, minute, second;
+ unsigned int week, twelve_hr, am_pm;
+ unsigned int century, add_century = 0;
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_SECONDS, 7, buf);
+
+ if (ret < 0)
+ return ret;
+ if (ret < 7)
+ return -EIO;
+
+ second = buf[0];
+ minute = buf[1];
+ hour = buf[2];
+ week = buf[3];
+ day = buf[4];
+ month = buf[5];
+ year = buf[6];
+
+ /* Extract additional information for AM/PM and century */
+
+ twelve_hr = hour & 0x40;
+ am_pm = hour & 0x20;
+ century = month & 0x80;
+
+ /* Write to rtc_time structure */
+
+ time->tm_sec = bcd2bin(second);
+ time->tm_min = bcd2bin(minute);
+ if (twelve_hr) {
+ /* Convert to 24 hr */
+ if (am_pm)
+ time->tm_hour = bcd2bin(hour & 0x1F) + 12;
+ else
+ time->tm_hour = bcd2bin(hour & 0x1F);
+ } else {
+ time->tm_hour = bcd2bin(hour);
+ }
+
+ /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
+ time->tm_wday = bcd2bin(week) - 1;
+ time->tm_mday = bcd2bin(day);
+ /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
+ time->tm_mon = bcd2bin(month & 0x7F) - 1;
+ if (century)
+ add_century = 100;
+
+ time->tm_year = bcd2bin(year) + add_century;
+
+ return rtc_valid_tm(time);
+}
+
+static int ds3232_set_time(struct device *dev, struct rtc_time *time)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 buf[7];
+
+ /* Extract time from rtc_time and load into ds3232*/
+
+ buf[0] = bin2bcd(time->tm_sec);
+ buf[1] = bin2bcd(time->tm_min);
+ buf[2] = bin2bcd(time->tm_hour);
+ /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */
+ buf[3] = bin2bcd(time->tm_wday + 1);
+ buf[4] = bin2bcd(time->tm_mday); /* Date */
+ /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */
+ buf[5] = bin2bcd(time->tm_mon + 1);
+ if (time->tm_year >= 100) {
+ buf[5] |= 0x80;
+ buf[6] = bin2bcd(time->tm_year - 100);
+ } else {
+ buf[6] = bin2bcd(time->tm_year);
+ }
+
+ return i2c_smbus_write_i2c_block_data(client,
+ DS3232_REG_SECONDS, 7, buf);
+}
+
+/*
+ * DS3232 has two alarm, we only use alarm1
+ * According to linux specification, only support one-shot alarm
+ * no periodic alarm mode
+ */
+static int ds3232_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control, stat;
+ int ret;
+ u8 buf[4];
+
+ mutex_lock(&ds3232->mutex);
+
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (ret < 0)
+ goto out;
+ stat = ret;
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (ret < 0)
+ goto out;
+ control = ret;
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto out;
+
+ alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
+ alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
+ alarm->time.tm_hour = bcd2bin(buf[2] & 0x7F);
+ alarm->time.tm_mday = bcd2bin(buf[3] & 0x7F);
+
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_year = -1;
+ alarm->time.tm_wday = -1;
+ alarm->time.tm_yday = -1;
+ alarm->time.tm_isdst = -1;
+
+ alarm->enabled = !!(control & DS3232_REG_CR_A1IE);
+ alarm->pending = !!(stat & DS3232_REG_SR_A1F);
+
+ ret = 0;
+out:
+ mutex_unlock(&ds3232->mutex);
+ return ret;
+}
+
+/*
+ * linux rtc-module does not support wday alarm
+ * and only 24h time mode supported indeed
+ */
+static int ds3232_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control, stat;
+ int ret;
+ u8 buf[4];
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ mutex_lock(&ds3232->mutex);
+
+ buf[0] = bin2bcd(alarm->time.tm_sec);
+ buf[1] = bin2bcd(alarm->time.tm_min);
+ buf[2] = bin2bcd(alarm->time.tm_hour);
+ buf[3] = bin2bcd(alarm->time.tm_mday);
+
+ /* clear alarm interrupt enable bit */
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (ret < 0)
+ goto out;
+ control = ret;
+ control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE);
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+ if (ret < 0)
+ goto out;
+
+ /* clear any pending alarm flag */
+ ret = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (ret < 0)
+ goto out;
+ stat = ret;
+ stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F);
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+ if (ret < 0)
+ goto out;
+
+ ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+
+ if (alarm->enabled) {
+ control |= DS3232_REG_CR_A1IE;
+ ret = i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+ }
+out:
+ mutex_unlock(&ds3232->mutex);
+ return ret;
+}
+
+static void ds3232_update_alarm(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+ int control;
+ int ret;
+ u8 buf[4];
+
+ mutex_lock(&ds3232->mutex);
+
+ ret = i2c_smbus_read_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto unlock;
+
+ buf[0] = bcd2bin(buf[0]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[0];
+ buf[1] = bcd2bin(buf[1]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[1];
+ buf[2] = bcd2bin(buf[2]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[2];
+ buf[3] = bcd2bin(buf[3]) < 0 || (ds3232->rtc->irq_data & RTC_UF) ?
+ 0x80 : buf[3];
+
+ ret = i2c_smbus_write_i2c_block_data(client, DS3232_REG_ALARM1, 4, buf);
+ if (ret < 0)
+ goto unlock;
+
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0)
+ goto unlock;
+
+ if (ds3232->rtc->irq_data & (RTC_AF | RTC_UF))
+ /* enable alarm1 interrupt */
+ control |= DS3232_REG_CR_A1IE;
+ else
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR, control);
+
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ if (enabled)
+ ds3232->rtc->irq_data |= RTC_AF;
+ else
+ ds3232->rtc->irq_data &= ~RTC_AF;
+
+ ds3232_update_alarm(client);
+ return 0;
+}
+
+static irqreturn_t ds3232_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+
+ /*
+ * If rtc as a wakeup source, can't schedule the work
+ * at system resume flow, because at this time the i2c bus
+ * has not been resumed.
+ */
+ if (!ds3232->suspended)
+ schedule_work(&ds3232->work);
+
+ return IRQ_HANDLED;
+}
+
+static void ds3232_work(struct work_struct *work)
+{
+ struct ds3232 *ds3232 = container_of(work, struct ds3232, work);
+ struct i2c_client *client = ds3232->client;
+ int stat, control;
+
+ mutex_lock(&ds3232->mutex);
+
+ stat = i2c_smbus_read_byte_data(client, DS3232_REG_SR);
+ if (stat < 0)
+ goto unlock;
+
+ if (stat & DS3232_REG_SR_A1F) {
+ control = i2c_smbus_read_byte_data(client, DS3232_REG_CR);
+ if (control < 0) {
+ pr_warn("Read Control Register error - Disable IRQ%d\n",
+ client->irq);
+ } else {
+ /* disable alarm1 interrupt */
+ control &= ~(DS3232_REG_CR_A1IE);
+ i2c_smbus_write_byte_data(client, DS3232_REG_CR,
+ control);
+
+ /* clear the alarm pend flag */
+ stat &= ~DS3232_REG_SR_A1F;
+ i2c_smbus_write_byte_data(client, DS3232_REG_SR, stat);
+
+ rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
+
+ if (!ds3232->exiting)
+ enable_irq(client->irq);
+ }
+ }
+
+unlock:
+ mutex_unlock(&ds3232->mutex);
+}
+
+static const struct rtc_class_ops ds3232_rtc_ops = {
+ .read_time = ds3232_read_time,
+ .set_time = ds3232_set_time,
+ .read_alarm = ds3232_read_alarm,
+ .set_alarm = ds3232_set_alarm,
+ .alarm_irq_enable = ds3232_alarm_irq_enable,
+};
+
+static int ds3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ds3232 *ds3232;
+ int ret;
+
+ ds3232 = devm_kzalloc(&client->dev, sizeof(struct ds3232), GFP_KERNEL);
+ if (!ds3232)
+ return -ENOMEM;
+
+ ds3232->client = client;
+ i2c_set_clientdata(client, ds3232);
+
+ INIT_WORK(&ds3232->work, ds3232_work);
+ mutex_init(&ds3232->mutex);
+
+ ret = ds3232_check_rtc_status(client);
+ if (ret)
+ return ret;
+
+ if (client->irq > 0) {
+ ret = devm_request_irq(&client->dev, client->irq, ds3232_irq,
+ IRQF_SHARED, "ds3232", client);
+ if (ret) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ }
+ device_init_wakeup(&client->dev, 1);
+ }
+ ds3232->rtc = devm_rtc_device_register(&client->dev, client->name,
+ &ds3232_rtc_ops, THIS_MODULE);
+ return PTR_ERR_OR_ZERO(ds3232->rtc);
+}
+
+static int ds3232_remove(struct i2c_client *client)
+{
+ struct ds3232 *ds3232 = i2c_get_clientdata(client);
+
+ if (client->irq >= 0) {
+ mutex_lock(&ds3232->mutex);
+ ds3232->exiting = 1;
+ mutex_unlock(&ds3232->mutex);
+
+ devm_free_irq(&client->dev, client->irq, client);
+ cancel_work_sync(&ds3232->work);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ds3232_suspend(struct device *dev)
+{
+ struct ds3232 *ds3232 = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_can_wakeup(dev)) {
+ ds3232->suspended = true;
+ irq_set_irq_wake(client->irq, 1);
+ }
+
+ return 0;
+}
+
+static int ds3232_resume(struct device *dev)
+{
+ struct ds3232 *ds3232 = dev_get_drvdata(dev);
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (ds3232->suspended) {
+ ds3232->suspended = false;
+
+ /* Clear the hardware alarm pend flag */
+ schedule_work(&ds3232->work);
+
+ irq_set_irq_wake(client->irq, 0);
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops ds3232_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume)
+};
+
+static const struct i2c_device_id ds3232_id[] = {
+ { "ds3232", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ds3232_id);
+
+static struct i2c_driver ds3232_driver = {
+ .driver = {
+ .name = "rtc-ds3232",
+ .owner = THIS_MODULE,
+ .pm = &ds3232_pm_ops,
+ },
+ .probe = ds3232_probe,
+ .remove = ds3232_remove,
+ .id_table = ds3232_id,
+};
+
+module_i2c_driver(ds3232_driver);
+
+MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
+MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c
new file mode 100644
index 000000000..4c9ba5368
--- /dev/null
+++ b/drivers/rtc/rtc-ds3234.c
@@ -0,0 +1,172 @@
+/* rtc-ds3234.c
+ *
+ * Driver for Dallas Semiconductor (DS3234) SPI RTC with Integrated Crystal
+ * and SRAM.
+ *
+ * Copyright (C) 2008 MIMOMax Wireless Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+#define DS3234_REG_SECONDS 0x00
+#define DS3234_REG_MINUTES 0x01
+#define DS3234_REG_HOURS 0x02
+#define DS3234_REG_DAY 0x03
+#define DS3234_REG_DATE 0x04
+#define DS3234_REG_MONTH 0x05
+#define DS3234_REG_YEAR 0x06
+#define DS3234_REG_CENTURY (1 << 7) /* Bit 7 of the Month register */
+
+#define DS3234_REG_CONTROL 0x0E
+#define DS3234_REG_CONT_STAT 0x0F
+
+static int ds3234_set_reg(struct device *dev, unsigned char address,
+ unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ /* MSB must be '1' to indicate write */
+ buf[0] = address | 0x80;
+ buf[1] = data;
+
+ return spi_write_then_read(spi, buf, 2, NULL, 0);
+}
+
+static int ds3234_get_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ *data = address & 0x7f;
+
+ return spi_write_then_read(spi, data, 1, data, 1);
+}
+
+static int ds3234_read_time(struct device *dev, struct rtc_time *dt)
+{
+ int err;
+ unsigned char buf[8];
+ struct spi_device *spi = to_spi_device(dev);
+
+ buf[0] = 0x00; /* Start address */
+
+ err = spi_write_then_read(spi, buf, 1, buf, 8);
+ if (err != 0)
+ return err;
+
+ /* Seconds, Minutes, Hours, Day, Date, Month, Year */
+ dt->tm_sec = bcd2bin(buf[0]);
+ dt->tm_min = bcd2bin(buf[1]);
+ dt->tm_hour = bcd2bin(buf[2] & 0x3f);
+ dt->tm_wday = bcd2bin(buf[3]) - 1; /* 0 = Sun */
+ dt->tm_mday = bcd2bin(buf[4]);
+ dt->tm_mon = bcd2bin(buf[5] & 0x1f) - 1; /* 0 = Jan */
+ dt->tm_year = bcd2bin(buf[6] & 0xff) + 100; /* Assume 20YY */
+
+ return rtc_valid_tm(dt);
+}
+
+static int ds3234_set_time(struct device *dev, struct rtc_time *dt)
+{
+ ds3234_set_reg(dev, DS3234_REG_SECONDS, bin2bcd(dt->tm_sec));
+ ds3234_set_reg(dev, DS3234_REG_MINUTES, bin2bcd(dt->tm_min));
+ ds3234_set_reg(dev, DS3234_REG_HOURS, bin2bcd(dt->tm_hour) & 0x3f);
+
+ /* 0 = Sun */
+ ds3234_set_reg(dev, DS3234_REG_DAY, bin2bcd(dt->tm_wday + 1));
+ ds3234_set_reg(dev, DS3234_REG_DATE, bin2bcd(dt->tm_mday));
+
+ /* 0 = Jan */
+ ds3234_set_reg(dev, DS3234_REG_MONTH, bin2bcd(dt->tm_mon + 1));
+
+ /* Assume 20YY although we just want to make sure not to go negative. */
+ if (dt->tm_year > 100)
+ dt->tm_year -= 100;
+
+ ds3234_set_reg(dev, DS3234_REG_YEAR, bin2bcd(dt->tm_year));
+
+ return 0;
+}
+
+static const struct rtc_class_ops ds3234_rtc_ops = {
+ .read_time = ds3234_read_time,
+ .set_time = ds3234_set_time,
+};
+
+static int ds3234_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ unsigned char tmp;
+ int res;
+
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ res = ds3234_get_reg(&spi->dev, DS3234_REG_SECONDS, &tmp);
+ if (res != 0)
+ return res;
+
+ /* Control settings
+ *
+ * CONTROL_REG
+ * BIT 7 6 5 4 3 2 1 0
+ * EOSC BBSQW CONV RS2 RS1 INTCN A2IE A1IE
+ *
+ * 0 0 0 1 1 1 0 0
+ *
+ * CONTROL_STAT_REG
+ * BIT 7 6 5 4 3 2 1 0
+ * OSF BB32kHz CRATE1 CRATE0 EN32kHz BSY A2F A1F
+ *
+ * 1 0 0 0 1 0 0 0
+ */
+ ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp);
+ ds3234_set_reg(&spi->dev, DS3234_REG_CONTROL, tmp & 0x1c);
+
+ ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp);
+ ds3234_set_reg(&spi->dev, DS3234_REG_CONT_STAT, tmp & 0x88);
+
+ /* Print our settings */
+ ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp);
+ dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp);
+
+ ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp);
+ dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp);
+
+ rtc = devm_rtc_device_register(&spi->dev, "ds3234",
+ &ds3234_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static struct spi_driver ds3234_driver = {
+ .driver = {
+ .name = "ds3234",
+ .owner = THIS_MODULE,
+ },
+ .probe = ds3234_probe,
+};
+
+module_spi_driver(ds3234_driver);
+
+MODULE_DESCRIPTION("DS3234 SPI RTC driver");
+MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:ds3234");
diff --git a/drivers/rtc/rtc-efi-platform.c b/drivers/rtc/rtc-efi-platform.c
new file mode 100644
index 000000000..1a7f1d1bc
--- /dev/null
+++ b/drivers/rtc/rtc-efi-platform.c
@@ -0,0 +1,34 @@
+/*
+ * Moved from arch/ia64/kernel/time.c
+ *
+ * Copyright (C) 1998-2003 Hewlett-Packard Co
+ * Stephane Eranian <eranian@hpl.hp.com>
+ * David Mosberger <davidm@hpl.hp.com>
+ * Copyright (C) 1999 Don Dugger <don.dugger@intel.com>
+ * Copyright (C) 1999-2000 VA Linux Systems
+ * Copyright (C) 1999-2000 Walt Drummond <drummond@valinux.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/efi.h>
+#include <linux/platform_device.h>
+
+static struct platform_device rtc_efi_dev = {
+ .name = "rtc-efi",
+ .id = -1,
+};
+
+static int __init rtc_init(void)
+{
+ if (efi_enabled(EFI_RUNTIME_SERVICES))
+ if (platform_device_register(&rtc_efi_dev) < 0)
+ pr_err("unable to register rtc device...\n");
+
+ /* not necessarily an error */
+ return 0;
+}
+module_init(rtc_init);
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 000000000..cb989cd00
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,239 @@
+/*
+ * rtc-efi: RTC Class Driver for EFI-based systems
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * Author: dann frazier <dannf@hp.com>
+ * Based on efirtc.c by Stephane Eranian
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/stringify.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/efi.h>
+
+#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
+/*
+ * EFI Epoch is 1/1/1998
+ */
+#define EFI_RTC_EPOCH 1998
+
+/*
+ * returns day of the year [0-365]
+ */
+static inline int
+compute_yday(efi_time_t *eft)
+{
+ /* efi_time_t.month is in the [1-12] so, we need -1 */
+ return rtc_year_days(eft->day, eft->month - 1, eft->year);
+}
+/*
+ * returns day of the week [0-6] 0=Sunday
+ *
+ * Don't try to provide a year that's before 1998, please !
+ */
+static int
+compute_wday(efi_time_t *eft)
+{
+ int y;
+ int ndays = 0;
+
+ if (eft->year < EFI_RTC_EPOCH) {
+ pr_err("EFI year < " __stringify(EFI_RTC_EPOCH) ", invalid date\n");
+ return -1;
+ }
+
+ for (y = EFI_RTC_EPOCH; y < eft->year; y++)
+ ndays += 365 + (is_leap_year(y) ? 1 : 0);
+
+ ndays += compute_yday(eft);
+
+ /*
+ * 4=1/1/1998 was a Thursday
+ */
+ return (ndays + 4) % 7;
+}
+
+static void
+convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
+{
+ eft->year = wtime->tm_year + 1900;
+ eft->month = wtime->tm_mon + 1;
+ eft->day = wtime->tm_mday;
+ eft->hour = wtime->tm_hour;
+ eft->minute = wtime->tm_min;
+ eft->second = wtime->tm_sec;
+ eft->nanosecond = 0;
+ eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
+ eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
+}
+
+static bool
+convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
+{
+ memset(wtime, 0, sizeof(*wtime));
+
+ if (eft->second >= 60)
+ return false;
+ wtime->tm_sec = eft->second;
+
+ if (eft->minute >= 60)
+ return false;
+ wtime->tm_min = eft->minute;
+
+ if (eft->hour >= 24)
+ return false;
+ wtime->tm_hour = eft->hour;
+
+ if (!eft->day || eft->day > 31)
+ return false;
+ wtime->tm_mday = eft->day;
+
+ if (!eft->month || eft->month > 12)
+ return false;
+ wtime->tm_mon = eft->month - 1;
+ wtime->tm_year = eft->year - 1900;
+
+ /* day of the week [0-6], Sunday=0 */
+ wtime->tm_wday = compute_wday(eft);
+ if (wtime->tm_wday < 0)
+ return false;
+
+ /* day in the year [1-365]*/
+ wtime->tm_yday = compute_yday(eft);
+
+
+ switch (eft->daylight & EFI_ISDST) {
+ case EFI_ISDST:
+ wtime->tm_isdst = 1;
+ break;
+ case EFI_TIME_ADJUST_DAYLIGHT:
+ wtime->tm_isdst = 0;
+ break;
+ default:
+ wtime->tm_isdst = -1;
+ }
+
+ return true;
+}
+
+static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ /*
+ * As of EFI v1.10, this call always returns an unsupported status
+ */
+ status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
+ (efi_bool_t *)&wkalrm->pending, &eft);
+
+ if (status != EFI_SUCCESS)
+ return -EINVAL;
+
+ if (!convert_from_efi_time(&eft, &wkalrm->time))
+ return -EIO;
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ efi_time_t eft;
+ efi_status_t status;
+
+ convert_to_efi_time(&wkalrm->time, &eft);
+
+ /*
+ * XXX Fixme:
+ * As of EFI 0.92 with the firmware I have on my
+ * machine this call does not seem to work quite
+ * right
+ *
+ * As of v1.10, this call always returns an unsupported status
+ */
+ status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
+
+ dev_warn(dev, "write status is %d\n", (int)status);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static int efi_read_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+ efi_time_cap_t cap;
+
+ status = efi.get_time(&eft, &cap);
+
+ if (status != EFI_SUCCESS) {
+ /* should never happen */
+ dev_err(dev, "can't read time\n");
+ return -EINVAL;
+ }
+
+ if (!convert_from_efi_time(&eft, tm))
+ return -EIO;
+
+ return rtc_valid_tm(tm);
+}
+
+static int efi_set_time(struct device *dev, struct rtc_time *tm)
+{
+ efi_status_t status;
+ efi_time_t eft;
+
+ convert_to_efi_time(tm, &eft);
+
+ status = efi.set_time(&eft);
+
+ return status == EFI_SUCCESS ? 0 : -EINVAL;
+}
+
+static const struct rtc_class_ops efi_rtc_ops = {
+ .read_time = efi_read_time,
+ .set_time = efi_set_time,
+ .read_alarm = efi_read_alarm,
+ .set_alarm = efi_set_alarm,
+};
+
+static int __init efi_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&dev->dev, "rtc-efi", &efi_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->uie_unsupported = 1;
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver efi_rtc_driver = {
+ .driver = {
+ .name = "rtc-efi",
+ },
+};
+
+module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe);
+
+MODULE_ALIAS("platform:rtc-efi");
+MODULE_AUTHOR("dann frazier <dannf@hp.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("EFI RTC driver");
+MODULE_ALIAS("platform:rtc-efi");
diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c
new file mode 100644
index 000000000..4f4930a20
--- /dev/null
+++ b/drivers/rtc/rtc-em3027.c
@@ -0,0 +1,162 @@
+/*
+ * An rtc/i2c driver for the EM Microelectronic EM3027
+ * Copyright 2011 CompuLab, Ltd.
+ *
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on rtc-ds1672.c by Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+/* Registers */
+#define EM3027_REG_ON_OFF_CTRL 0x00
+#define EM3027_REG_IRQ_CTRL 0x01
+#define EM3027_REG_IRQ_FLAGS 0x02
+#define EM3027_REG_STATUS 0x03
+#define EM3027_REG_RST_CTRL 0x04
+
+#define EM3027_REG_WATCH_SEC 0x08
+#define EM3027_REG_WATCH_MIN 0x09
+#define EM3027_REG_WATCH_HOUR 0x0a
+#define EM3027_REG_WATCH_DATE 0x0b
+#define EM3027_REG_WATCH_DAY 0x0c
+#define EM3027_REG_WATCH_MON 0x0d
+#define EM3027_REG_WATCH_YEAR 0x0e
+
+#define EM3027_REG_ALARM_SEC 0x10
+#define EM3027_REG_ALARM_MIN 0x11
+#define EM3027_REG_ALARM_HOUR 0x12
+#define EM3027_REG_ALARM_DATE 0x13
+#define EM3027_REG_ALARM_DAY 0x14
+#define EM3027_REG_ALARM_MON 0x15
+#define EM3027_REG_ALARM_YEAR 0x16
+
+static struct i2c_driver em3027_driver;
+
+static int em3027_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ unsigned char addr = EM3027_REG_WATCH_SEC;
+ unsigned char buf[7];
+
+ struct i2c_msg msgs[] = {
+ {/* setup read addr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = &addr
+ },
+ {/* read time/date */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 7,
+ .buf = buf
+ },
+ };
+
+ /* read time/date registers */
+ if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[0]);
+ tm->tm_min = bcd2bin(buf[1]);
+ tm->tm_hour = bcd2bin(buf[2]);
+ tm->tm_mday = bcd2bin(buf[3]);
+ tm->tm_wday = bcd2bin(buf[4]);
+ tm->tm_mon = bcd2bin(buf[5]);
+ tm->tm_year = bcd2bin(buf[6]) + 100;
+
+ return 0;
+}
+
+static int em3027_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[8];
+
+ struct i2c_msg msg = {
+ .addr = client->addr,
+ .len = 8,
+ .buf = buf, /* write time/date */
+ };
+
+ buf[0] = EM3027_REG_WATCH_SEC;
+ buf[1] = bin2bcd(tm->tm_sec);
+ buf[2] = bin2bcd(tm->tm_min);
+ buf[3] = bin2bcd(tm->tm_hour);
+ buf[4] = bin2bcd(tm->tm_mday);
+ buf[5] = bin2bcd(tm->tm_wday);
+ buf[6] = bin2bcd(tm->tm_mon);
+ buf[7] = bin2bcd(tm->tm_year % 100);
+
+ /* write time/date registers */
+ if ((i2c_transfer(client->adapter, &msg, 1)) != 1) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops em3027_rtc_ops = {
+ .read_time = em3027_get_time,
+ .set_time = em3027_set_time,
+};
+
+static int em3027_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rtc_device *rtc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ rtc = devm_rtc_device_register(&client->dev, em3027_driver.driver.name,
+ &em3027_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static struct i2c_device_id em3027_id[] = {
+ { "em3027", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, em3027_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id em3027_of_match[] = {
+ { .compatible = "emmicro,em3027", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, em3027_of_match);
+#endif
+
+static struct i2c_driver em3027_driver = {
+ .driver = {
+ .name = "rtc-em3027",
+ .of_match_table = of_match_ptr(em3027_of_match),
+ },
+ .probe = &em3027_probe,
+ .id_table = em3027_id,
+};
+
+module_i2c_driver(em3027_driver);
+
+MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
+MODULE_DESCRIPTION("EM Microelectronic EM3027 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
new file mode 100644
index 000000000..de325d68c
--- /dev/null
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -0,0 +1,188 @@
+/*
+ * A driver for the RTC embedded in the Cirrus Logic EP93XX processors
+ * Copyright (c) 2006 Tower Technologies
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gfp.h>
+
+#define EP93XX_RTC_DATA 0x000
+#define EP93XX_RTC_MATCH 0x004
+#define EP93XX_RTC_STATUS 0x008
+#define EP93XX_RTC_STATUS_INTR (1<<0)
+#define EP93XX_RTC_LOAD 0x00C
+#define EP93XX_RTC_CONTROL 0x010
+#define EP93XX_RTC_CONTROL_MIE (1<<0)
+#define EP93XX_RTC_SWCOMP 0x108
+#define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000
+#define EP93XX_RTC_SWCOMP_DEL_SHIFT 16
+#define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff
+#define EP93XX_RTC_SWCOMP_INT_SHIFT 0
+
+#define DRV_VERSION "0.3"
+
+/*
+ * struct device dev.platform_data is used to store our private data
+ * because struct rtc_device does not have a variable to hold it.
+ */
+struct ep93xx_rtc {
+ void __iomem *mmio_base;
+ struct rtc_device *rtc;
+};
+
+static int ep93xx_rtc_get_swcomp(struct device *dev, unsigned short *preload,
+ unsigned short *delete)
+{
+ struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
+ unsigned long comp;
+
+ comp = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_SWCOMP);
+
+ if (preload)
+ *preload = (comp & EP93XX_RTC_SWCOMP_INT_MASK)
+ >> EP93XX_RTC_SWCOMP_INT_SHIFT;
+
+ if (delete)
+ *delete = (comp & EP93XX_RTC_SWCOMP_DEL_MASK)
+ >> EP93XX_RTC_SWCOMP_DEL_SHIFT;
+
+ return 0;
+}
+
+static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
+ unsigned long time;
+
+ time = __raw_readl(ep93xx_rtc->mmio_base + EP93XX_RTC_DATA);
+
+ rtc_time_to_tm(time, tm);
+ return 0;
+}
+
+static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct ep93xx_rtc *ep93xx_rtc = dev_get_platdata(dev);
+
+ __raw_writel(secs + 1, ep93xx_rtc->mmio_base + EP93XX_RTC_LOAD);
+ return 0;
+}
+
+static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ unsigned short preload, delete;
+
+ ep93xx_rtc_get_swcomp(dev, &preload, &delete);
+
+ seq_printf(seq, "preload\t\t: %d\n", preload);
+ seq_printf(seq, "delete\t\t: %d\n", delete);
+
+ return 0;
+}
+
+static const struct rtc_class_ops ep93xx_rtc_ops = {
+ .read_time = ep93xx_rtc_read_time,
+ .set_mmss = ep93xx_rtc_set_mmss,
+ .proc = ep93xx_rtc_proc,
+};
+
+static ssize_t ep93xx_rtc_show_comp_preload(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned short preload;
+
+ ep93xx_rtc_get_swcomp(dev, &preload, NULL);
+
+ return sprintf(buf, "%d\n", preload);
+}
+static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_rtc_show_comp_preload, NULL);
+
+static ssize_t ep93xx_rtc_show_comp_delete(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned short delete;
+
+ ep93xx_rtc_get_swcomp(dev, NULL, &delete);
+
+ return sprintf(buf, "%d\n", delete);
+}
+static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_rtc_show_comp_delete, NULL);
+
+static struct attribute *ep93xx_rtc_attrs[] = {
+ &dev_attr_comp_preload.attr,
+ &dev_attr_comp_delete.attr,
+ NULL
+};
+
+static const struct attribute_group ep93xx_rtc_sysfs_files = {
+ .attrs = ep93xx_rtc_attrs,
+};
+
+static int ep93xx_rtc_probe(struct platform_device *pdev)
+{
+ struct ep93xx_rtc *ep93xx_rtc;
+ struct resource *res;
+ int err;
+
+ ep93xx_rtc = devm_kzalloc(&pdev->dev, sizeof(*ep93xx_rtc), GFP_KERNEL);
+ if (!ep93xx_rtc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ep93xx_rtc->mmio_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ep93xx_rtc->mmio_base))
+ return PTR_ERR(ep93xx_rtc->mmio_base);
+
+ pdev->dev.platform_data = ep93xx_rtc;
+ platform_set_drvdata(pdev, ep93xx_rtc);
+
+ ep93xx_rtc->rtc = devm_rtc_device_register(&pdev->dev,
+ pdev->name, &ep93xx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ep93xx_rtc->rtc)) {
+ err = PTR_ERR(ep93xx_rtc->rtc);
+ goto exit;
+ }
+
+ err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
+ if (err)
+ goto exit;
+
+ return 0;
+
+exit:
+ pdev->dev.platform_data = NULL;
+ return err;
+}
+
+static int ep93xx_rtc_remove(struct platform_device *pdev)
+{
+ sysfs_remove_group(&pdev->dev.kobj, &ep93xx_rtc_sysfs_files);
+ pdev->dev.platform_data = NULL;
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_rtc_driver = {
+ .driver = {
+ .name = "ep93xx-rtc",
+ },
+ .probe = ep93xx_rtc_probe,
+ .remove = ep93xx_rtc_remove,
+};
+
+module_platform_driver(ep93xx_rtc_driver);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("EP93XX RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:ep93xx-rtc");
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
new file mode 100644
index 000000000..83c3b3029
--- /dev/null
+++ b/drivers/rtc/rtc-fm3130.c
@@ -0,0 +1,537 @@
+/*
+ * rtc-fm3130.c - RTC driver for Ramtron FM3130 I2C chip.
+ *
+ * Copyright (C) 2008 Sergey Lapin
+ * Based on ds1307 driver by James Chapman and David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#define FM3130_RTC_CONTROL (0x0)
+#define FM3130_CAL_CONTROL (0x1)
+#define FM3130_RTC_SECONDS (0x2)
+#define FM3130_RTC_MINUTES (0x3)
+#define FM3130_RTC_HOURS (0x4)
+#define FM3130_RTC_DAY (0x5)
+#define FM3130_RTC_DATE (0x6)
+#define FM3130_RTC_MONTHS (0x7)
+#define FM3130_RTC_YEARS (0x8)
+
+#define FM3130_ALARM_SECONDS (0x9)
+#define FM3130_ALARM_MINUTES (0xa)
+#define FM3130_ALARM_HOURS (0xb)
+#define FM3130_ALARM_DATE (0xc)
+#define FM3130_ALARM_MONTHS (0xd)
+#define FM3130_ALARM_WP_CONTROL (0xe)
+
+#define FM3130_CAL_CONTROL_BIT_nOSCEN (1 << 7) /* Osciallator enabled */
+#define FM3130_RTC_CONTROL_BIT_LB (1 << 7) /* Low battery */
+#define FM3130_RTC_CONTROL_BIT_AF (1 << 6) /* Alarm flag */
+#define FM3130_RTC_CONTROL_BIT_CF (1 << 5) /* Century overflow */
+#define FM3130_RTC_CONTROL_BIT_POR (1 << 4) /* Power on reset */
+#define FM3130_RTC_CONTROL_BIT_AEN (1 << 3) /* Alarm enable */
+#define FM3130_RTC_CONTROL_BIT_CAL (1 << 2) /* Calibration mode */
+#define FM3130_RTC_CONTROL_BIT_WRITE (1 << 1) /* W=1 -> write mode W=0 normal */
+#define FM3130_RTC_CONTROL_BIT_READ (1 << 0) /* R=1 -> read mode R=0 normal */
+
+#define FM3130_CLOCK_REGS 7
+#define FM3130_ALARM_REGS 5
+
+struct fm3130 {
+ u8 reg_addr_time;
+ u8 reg_addr_alarm;
+ u8 regs[15];
+ struct i2c_msg msg[4];
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ int alarm_valid;
+ int data_valid;
+};
+static const struct i2c_device_id fm3130_id[] = {
+ { "fm3130", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fm3130_id);
+
+#define FM3130_MODE_NORMAL 0
+#define FM3130_MODE_WRITE 1
+#define FM3130_MODE_READ 2
+
+static void fm3130_rtc_mode(struct device *dev, int mode)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+ switch (mode) {
+ case FM3130_MODE_NORMAL:
+ fm3130->regs[FM3130_RTC_CONTROL] &=
+ ~(FM3130_RTC_CONTROL_BIT_WRITE |
+ FM3130_RTC_CONTROL_BIT_READ);
+ break;
+ case FM3130_MODE_WRITE:
+ fm3130->regs[FM3130_RTC_CONTROL] |= FM3130_RTC_CONTROL_BIT_WRITE;
+ break;
+ case FM3130_MODE_READ:
+ fm3130->regs[FM3130_RTC_CONTROL] |= FM3130_RTC_CONTROL_BIT_READ;
+ break;
+ default:
+ dev_dbg(dev, "invalid mode %d\n", mode);
+ break;
+ }
+
+ i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
+}
+
+static int fm3130_get_time(struct device *dev, struct rtc_time *t)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int tmp;
+
+ if (!fm3130->data_valid) {
+ /* We have invalid data in RTC, probably due
+ to battery faults or other problems. Return EIO
+ for now, it will allow us to set data later instead
+ of error during probing which disables device */
+ return -EIO;
+ }
+ fm3130_rtc_mode(dev, FM3130_MODE_READ);
+
+ /* read the RTC date and time registers all at once */
+ tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
+ fm3130->msg, 2);
+ if (tmp != 2) {
+ dev_err(dev, "%s error %d\n", "read", tmp);
+ return -EIO;
+ }
+
+ fm3130_rtc_mode(dev, FM3130_MODE_NORMAL);
+
+ dev_dbg(dev, "%s: %15ph\n", "read", fm3130->regs);
+
+ t->tm_sec = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ t->tm_min = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ tmp = fm3130->regs[FM3130_RTC_HOURS] & 0x3f;
+ t->tm_hour = bcd2bin(tmp);
+ t->tm_wday = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x07) - 1;
+ t->tm_mday = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ tmp = fm3130->regs[FM3130_RTC_MONTHS] & 0x1f;
+ t->tm_mon = bcd2bin(tmp) - 1;
+
+ /* assume 20YY not 19YY, and ignore CF bit */
+ t->tm_year = bcd2bin(fm3130->regs[FM3130_RTC_YEARS]) + 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ /* initial clock setting can be undefined */
+ return rtc_valid_tm(t);
+}
+
+
+static int fm3130_set_time(struct device *dev, struct rtc_time *t)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int tmp, i;
+ u8 *buf = fm3130->regs;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", t->tm_sec, t->tm_min,
+ t->tm_hour, t->tm_mday,
+ t->tm_mon, t->tm_year, t->tm_wday);
+
+ /* first register addr */
+ buf[FM3130_RTC_SECONDS] = bin2bcd(t->tm_sec);
+ buf[FM3130_RTC_MINUTES] = bin2bcd(t->tm_min);
+ buf[FM3130_RTC_HOURS] = bin2bcd(t->tm_hour);
+ buf[FM3130_RTC_DAY] = bin2bcd(t->tm_wday + 1);
+ buf[FM3130_RTC_DATE] = bin2bcd(t->tm_mday);
+ buf[FM3130_RTC_MONTHS] = bin2bcd(t->tm_mon + 1);
+
+ /* assume 20YY not 19YY */
+ tmp = t->tm_year - 100;
+ buf[FM3130_RTC_YEARS] = bin2bcd(tmp);
+
+ dev_dbg(dev, "%s: %15ph\n", "write", buf);
+
+ fm3130_rtc_mode(dev, FM3130_MODE_WRITE);
+
+ /* Writing time registers, we don't support multibyte transfers */
+ for (i = 0; i < FM3130_CLOCK_REGS; i++) {
+ i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_SECONDS + i,
+ fm3130->regs[FM3130_RTC_SECONDS + i]);
+ }
+
+ fm3130_rtc_mode(dev, FM3130_MODE_NORMAL);
+
+ /* We assume here that data are valid once written */
+ if (!fm3130->data_valid)
+ fm3130->data_valid = 1;
+ return 0;
+}
+
+static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int tmp;
+ struct rtc_time *tm = &alrm->time;
+
+ if (!fm3130->alarm_valid) {
+ /*
+ * We have invalid alarm in RTC, probably due to battery faults
+ * or other problems. Return EIO for now, it will allow us to
+ * set alarm value later instead of error during probing which
+ * disables device
+ */
+ return -EIO;
+ }
+
+ /* read the RTC alarm registers all at once */
+ tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
+ &fm3130->msg[2], 2);
+ if (tmp != 2) {
+ dev_err(dev, "%s error %d\n", "read", tmp);
+ return -EIO;
+ }
+ dev_dbg(dev, "alarm read %02x %02x %02x %02x %02x\n",
+ fm3130->regs[FM3130_ALARM_SECONDS],
+ fm3130->regs[FM3130_ALARM_MINUTES],
+ fm3130->regs[FM3130_ALARM_HOURS],
+ fm3130->regs[FM3130_ALARM_DATE],
+ fm3130->regs[FM3130_ALARM_MONTHS]);
+
+ tm->tm_sec = bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
+ tm->tm_min = bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
+ tm->tm_hour = bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
+ tm->tm_mday = bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
+ tm->tm_mon = bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
+
+ if (tm->tm_mon > 0)
+ tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read alarm", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* check if alarm enabled */
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) &&
+ (~fm3130->regs[FM3130_RTC_CONTROL] &
+ FM3130_RTC_CONTROL_BIT_CAL)) {
+ alrm->enabled = 1;
+ }
+
+ return 0;
+}
+
+static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ struct rtc_time *tm = &alrm->time;
+ int i;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write alarm", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ fm3130->regs[FM3130_ALARM_SECONDS] =
+ (tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;
+
+ fm3130->regs[FM3130_ALARM_MINUTES] =
+ (tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;
+
+ fm3130->regs[FM3130_ALARM_HOURS] =
+ (tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;
+
+ fm3130->regs[FM3130_ALARM_DATE] =
+ (tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;
+
+ fm3130->regs[FM3130_ALARM_MONTHS] =
+ (tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;
+
+ dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
+ fm3130->regs[FM3130_ALARM_SECONDS],
+ fm3130->regs[FM3130_ALARM_MINUTES],
+ fm3130->regs[FM3130_ALARM_HOURS],
+ fm3130->regs[FM3130_ALARM_DATE],
+ fm3130->regs[FM3130_ALARM_MONTHS]);
+ /* Writing time registers, we don't support multibyte transfers */
+ for (i = 0; i < FM3130_ALARM_REGS; i++) {
+ i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_ALARM_SECONDS + i,
+ fm3130->regs[FM3130_ALARM_SECONDS + i]);
+ }
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ /* enable or disable alarm */
+ if (alrm->enabled) {
+ i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
+ (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ } else {
+ i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ }
+
+ /* We assume here that data is valid once written */
+ if (!fm3130->alarm_valid)
+ fm3130->alarm_valid = 1;
+
+ return 0;
+}
+
+static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct fm3130 *fm3130 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL);
+
+ dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n",
+ enabled, fm3130->regs[FM3130_RTC_CONTROL]);
+
+ switch (enabled) {
+ case 0: /* alarm off */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL) &
+ ~(FM3130_RTC_CONTROL_BIT_AEN));
+ break;
+ case 1: /* alarm on */
+ ret = i2c_smbus_write_byte_data(fm3130->client,
+ FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL)) |
+ FM3130_RTC_CONTROL_BIT_AEN);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct rtc_class_ops fm3130_rtc_ops = {
+ .read_time = fm3130_get_time,
+ .set_time = fm3130_set_time,
+ .read_alarm = fm3130_read_alarm,
+ .set_alarm = fm3130_set_alarm,
+ .alarm_irq_enable = fm3130_alarm_irq_enable,
+};
+
+static struct i2c_driver fm3130_driver;
+
+static int fm3130_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fm3130 *fm3130;
+ int err = -ENODEV;
+ int tmp;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+
+ if (!i2c_check_functionality(adapter,
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
+ return -EIO;
+
+ fm3130 = devm_kzalloc(&client->dev, sizeof(struct fm3130), GFP_KERNEL);
+
+ if (!fm3130)
+ return -ENOMEM;
+
+ fm3130->client = client;
+ i2c_set_clientdata(client, fm3130);
+ fm3130->reg_addr_time = FM3130_RTC_SECONDS;
+ fm3130->reg_addr_alarm = FM3130_ALARM_SECONDS;
+
+ /* Messages to read time */
+ fm3130->msg[0].addr = client->addr;
+ fm3130->msg[0].flags = 0;
+ fm3130->msg[0].len = 1;
+ fm3130->msg[0].buf = &fm3130->reg_addr_time;
+
+ fm3130->msg[1].addr = client->addr;
+ fm3130->msg[1].flags = I2C_M_RD;
+ fm3130->msg[1].len = FM3130_CLOCK_REGS;
+ fm3130->msg[1].buf = &fm3130->regs[FM3130_RTC_SECONDS];
+
+ /* Messages to read alarm */
+ fm3130->msg[2].addr = client->addr;
+ fm3130->msg[2].flags = 0;
+ fm3130->msg[2].len = 1;
+ fm3130->msg[2].buf = &fm3130->reg_addr_alarm;
+
+ fm3130->msg[3].addr = client->addr;
+ fm3130->msg[3].flags = I2C_M_RD;
+ fm3130->msg[3].len = FM3130_ALARM_REGS;
+ fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
+
+ fm3130->alarm_valid = 0;
+ fm3130->data_valid = 0;
+
+ tmp = i2c_transfer(adapter, fm3130->msg, 4);
+ if (tmp != 4) {
+ dev_dbg(&client->dev, "read error %d\n", tmp);
+ err = -EIO;
+ goto exit_free;
+ }
+
+ fm3130->regs[FM3130_RTC_CONTROL] =
+ i2c_smbus_read_byte_data(client, FM3130_RTC_CONTROL);
+ fm3130->regs[FM3130_CAL_CONTROL] =
+ i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL);
+
+ /* Disabling calibration mode */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_CAL));
+ dev_warn(&client->dev, "Disabling calibration mode!\n");
+ }
+
+ /* Disabling read and write modes */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_WRITE ||
+ fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_READ) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_READ |
+ FM3130_RTC_CONTROL_BIT_WRITE));
+ dev_warn(&client->dev, "Disabling READ or WRITE mode!\n");
+ }
+
+ /* oscillator off? turn it on, so clock can tick. */
+ if (fm3130->regs[FM3130_CAL_CONTROL] & FM3130_CAL_CONTROL_BIT_nOSCEN)
+ i2c_smbus_write_byte_data(client, FM3130_CAL_CONTROL,
+ fm3130->regs[FM3130_CAL_CONTROL] &
+ ~(FM3130_CAL_CONTROL_BIT_nOSCEN));
+
+ /* low battery? clear flag, and warn */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~(FM3130_RTC_CONTROL_BIT_LB));
+ dev_warn(&client->dev, "Low battery!\n");
+ }
+
+ /* check if Power On Reset bit is set */
+ if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
+ i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
+ fm3130->regs[FM3130_RTC_CONTROL] &
+ ~FM3130_RTC_CONTROL_BIT_POR);
+ dev_dbg(&client->dev, "POR bit is set\n");
+ }
+ /* ACS is controlled by alarm */
+ i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
+
+ /* alarm registers sanity check */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_alarm;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_alarm;
+
+ fm3130->alarm_valid = 1;
+
+bad_alarm:
+
+ /* clock registers sanity chek */
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
+ if (tmp > 59)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f);
+ if (tmp > 23)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7);
+ if (tmp == 0 || tmp > 7)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
+ if (tmp == 0 || tmp > 31)
+ goto bad_clock;
+
+ tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
+ if (tmp == 0 || tmp > 12)
+ goto bad_clock;
+
+ fm3130->data_valid = 1;
+
+bad_clock:
+
+ if (!fm3130->data_valid || !fm3130->alarm_valid)
+ dev_dbg(&client->dev, "%s: %15ph\n", "bogus registers",
+ fm3130->regs);
+
+ /* We won't bail out here because we just got invalid data.
+ Time setting from u-boot doesn't work anyway */
+ fm3130->rtc = devm_rtc_device_register(&client->dev, client->name,
+ &fm3130_rtc_ops, THIS_MODULE);
+ if (IS_ERR(fm3130->rtc)) {
+ err = PTR_ERR(fm3130->rtc);
+ dev_err(&client->dev,
+ "unable to register the class device\n");
+ goto exit_free;
+ }
+ return 0;
+exit_free:
+ return err;
+}
+
+static struct i2c_driver fm3130_driver = {
+ .driver = {
+ .name = "rtc-fm3130",
+ .owner = THIS_MODULE,
+ },
+ .probe = fm3130_probe,
+ .id_table = fm3130_id,
+};
+
+module_i2c_driver(fm3130_driver);
+
+MODULE_DESCRIPTION("RTC driver for FM3130");
+MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c
new file mode 100644
index 000000000..e782ebd71
--- /dev/null
+++ b/drivers/rtc/rtc-generic.c
@@ -0,0 +1,62 @@
+/* rtc-generic: RTC driver using the generic RTC abstraction
+ *
+ * Copyright (C) 2008 Kyle McMartin <kyle@mcmartin.ca>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <asm/rtc.h>
+
+static int generic_get_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned int ret = get_rtc_time(tm);
+
+ if (ret & RTC_BATT_BAD)
+ return -EOPNOTSUPP;
+
+ return rtc_valid_tm(tm);
+}
+
+static int generic_set_time(struct device *dev, struct rtc_time *tm)
+{
+ if (set_rtc_time(tm) < 0)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+
+static const struct rtc_class_ops generic_rtc_ops = {
+ .read_time = generic_get_time,
+ .set_time = generic_set_time,
+};
+
+static int __init generic_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&dev->dev, "rtc-generic",
+ &generic_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver generic_rtc_driver = {
+ .driver = {
+ .name = "rtc-generic",
+ },
+};
+
+module_platform_driver_probe(generic_rtc_driver, generic_rtc_probe);
+
+MODULE_AUTHOR("Kyle McMartin <kyle@mcmartin.ca>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic RTC driver");
+MODULE_ALIAS("platform:rtc-generic");
diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c
new file mode 100644
index 000000000..af4f85a66
--- /dev/null
+++ b/drivers/rtc/rtc-hid-sensor-time.c
@@ -0,0 +1,342 @@
+/*
+ * HID Sensor Time Driver
+ * Copyright (c) 2012, Alexander Holler.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/rtc.h>
+
+enum hid_time_channel {
+ CHANNEL_SCAN_INDEX_YEAR,
+ CHANNEL_SCAN_INDEX_MONTH,
+ CHANNEL_SCAN_INDEX_DAY,
+ CHANNEL_SCAN_INDEX_HOUR,
+ CHANNEL_SCAN_INDEX_MINUTE,
+ CHANNEL_SCAN_INDEX_SECOND,
+ TIME_RTC_CHANNEL_MAX,
+};
+
+struct hid_time_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_common common_attributes;
+ struct hid_sensor_hub_attribute_info info[TIME_RTC_CHANNEL_MAX];
+ struct rtc_time last_time;
+ spinlock_t lock_last_time;
+ struct completion comp_last_time;
+ struct rtc_time time_buf;
+ struct rtc_device *rtc;
+};
+
+static const u32 hid_time_addresses[TIME_RTC_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_TIME_YEAR,
+ HID_USAGE_SENSOR_TIME_MONTH,
+ HID_USAGE_SENSOR_TIME_DAY,
+ HID_USAGE_SENSOR_TIME_HOUR,
+ HID_USAGE_SENSOR_TIME_MINUTE,
+ HID_USAGE_SENSOR_TIME_SECOND,
+};
+
+/* Channel names for verbose error messages */
+static const char * const hid_time_channel_names[TIME_RTC_CHANNEL_MAX] = {
+ "year", "month", "day", "hour", "minute", "second",
+};
+
+/* Callback handler to send event after all samples are received and captured */
+static int hid_time_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id, void *priv)
+{
+ unsigned long flags;
+ struct hid_time_state *time_state = platform_get_drvdata(priv);
+
+ spin_lock_irqsave(&time_state->lock_last_time, flags);
+ time_state->last_time = time_state->time_buf;
+ spin_unlock_irqrestore(&time_state->lock_last_time, flags);
+ complete(&time_state->comp_last_time);
+ return 0;
+}
+
+static u32 hid_time_value(size_t raw_len, char *raw_data)
+{
+ switch (raw_len) {
+ case 1:
+ return *(u8 *)raw_data;
+ case 2:
+ return *(u16 *)raw_data;
+ case 4:
+ return *(u32 *)raw_data;
+ default:
+ return (u32)(~0U); /* 0xff... or -1 to denote an error */
+ }
+}
+
+static int hid_time_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id, size_t raw_len,
+ char *raw_data, void *priv)
+{
+ struct hid_time_state *time_state = platform_get_drvdata(priv);
+ struct rtc_time *time_buf = &time_state->time_buf;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_TIME_YEAR:
+ /*
+ * The draft for HID-sensors (HUTRR39) currently doesn't define
+ * the range for the year attribute. Therefor we support
+ * 8 bit (0-99) and 16 or 32 bits (full) as size for the year.
+ */
+ if (raw_len == 1) {
+ time_buf->tm_year = *(u8 *)raw_data;
+ if (time_buf->tm_year < 70)
+ /* assume we are in 1970...2069 */
+ time_buf->tm_year += 100;
+ } else
+ time_buf->tm_year =
+ (int)hid_time_value(raw_len, raw_data)-1900;
+ break;
+ case HID_USAGE_SENSOR_TIME_MONTH:
+ /* sensors are sending the month as 1-12, we need 0-11 */
+ time_buf->tm_mon = (int)hid_time_value(raw_len, raw_data)-1;
+ break;
+ case HID_USAGE_SENSOR_TIME_DAY:
+ time_buf->tm_mday = (int)hid_time_value(raw_len, raw_data);
+ break;
+ case HID_USAGE_SENSOR_TIME_HOUR:
+ time_buf->tm_hour = (int)hid_time_value(raw_len, raw_data);
+ break;
+ case HID_USAGE_SENSOR_TIME_MINUTE:
+ time_buf->tm_min = (int)hid_time_value(raw_len, raw_data);
+ break;
+ case HID_USAGE_SENSOR_TIME_SECOND:
+ time_buf->tm_sec = (int)hid_time_value(raw_len, raw_data);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* small helper, haven't found any other way */
+static const char *hid_time_attrib_name(u32 attrib_id)
+{
+ static const char unknown[] = "unknown";
+ unsigned i;
+
+ for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) {
+ if (hid_time_addresses[i] == attrib_id)
+ return hid_time_channel_names[i];
+ }
+ return unknown; /* should never happen */
+}
+
+static int hid_time_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ struct hid_time_state *time_state)
+{
+ int report_id, i;
+
+ for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i)
+ if (sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT, usage_id,
+ hid_time_addresses[i],
+ &time_state->info[i]) < 0)
+ return -EINVAL;
+ /* Check the (needed) attributes for sanity */
+ report_id = time_state->info[0].report_id;
+ if (report_id < 0) {
+ dev_err(&pdev->dev, "bad report ID!\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < TIME_RTC_CHANNEL_MAX; ++i) {
+ if (time_state->info[i].report_id != report_id) {
+ dev_err(&pdev->dev,
+ "not all needed attributes inside the same report!\n");
+ return -EINVAL;
+ }
+ if (time_state->info[i].size == 3 ||
+ time_state->info[i].size > 4) {
+ dev_err(&pdev->dev,
+ "attribute '%s' not 8, 16 or 32 bits wide!\n",
+ hid_time_attrib_name(
+ time_state->info[i].attrib_id));
+ return -EINVAL;
+ }
+ if (time_state->info[i].units !=
+ HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED &&
+ /* allow attribute seconds with unit seconds */
+ !(time_state->info[i].attrib_id ==
+ HID_USAGE_SENSOR_TIME_SECOND &&
+ time_state->info[i].units ==
+ HID_USAGE_SENSOR_UNITS_SECOND)) {
+ dev_err(&pdev->dev,
+ "attribute '%s' hasn't a unit of type 'none'!\n",
+ hid_time_attrib_name(
+ time_state->info[i].attrib_id));
+ return -EINVAL;
+ }
+ if (time_state->info[i].unit_expo) {
+ dev_err(&pdev->dev,
+ "attribute '%s' hasn't a unit exponent of 1!\n",
+ hid_time_attrib_name(
+ time_state->info[i].attrib_id));
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int hid_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long flags;
+ struct hid_time_state *time_state =
+ platform_get_drvdata(to_platform_device(dev));
+ int ret;
+
+ reinit_completion(&time_state->comp_last_time);
+ /* get a report with all values through requesting one value */
+ sensor_hub_input_attr_get_raw_value(time_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_TIME, hid_time_addresses[0],
+ time_state->info[0].report_id, SENSOR_HUB_SYNC);
+ /* wait for all values (event) */
+ ret = wait_for_completion_killable_timeout(
+ &time_state->comp_last_time, HZ*6);
+ if (ret > 0) {
+ /* no error */
+ spin_lock_irqsave(&time_state->lock_last_time, flags);
+ *tm = time_state->last_time;
+ spin_unlock_irqrestore(&time_state->lock_last_time, flags);
+ return 0;
+ }
+ if (!ret)
+ return -EIO; /* timeouted */
+ return ret; /* killed (-ERESTARTSYS) */
+}
+
+static const struct rtc_class_ops hid_time_rtc_ops = {
+ .read_time = hid_rtc_read_time,
+};
+
+static int hid_time_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
+ struct hid_time_state *time_state = devm_kzalloc(&pdev->dev,
+ sizeof(struct hid_time_state), GFP_KERNEL);
+
+ if (time_state == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, time_state);
+
+ spin_lock_init(&time_state->lock_last_time);
+ init_completion(&time_state->comp_last_time);
+ time_state->common_attributes.hsdev = hsdev;
+ time_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_TIME,
+ &time_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes!\n");
+ return ret;
+ }
+
+ ret = hid_time_parse_report(pdev, hsdev, HID_USAGE_SENSOR_TIME,
+ time_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes!\n");
+ return ret;
+ }
+
+ time_state->callbacks.send_event = hid_time_proc_event;
+ time_state->callbacks.capture_sample = hid_time_capture_sample;
+ time_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_TIME,
+ &time_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "register callback failed!\n");
+ return ret;
+ }
+
+ ret = sensor_hub_device_open(hsdev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to open sensor hub device!\n");
+ goto err_open;
+ }
+
+ /*
+ * Enable HID input processing early in order to be able to read the
+ * clock already in devm_rtc_device_register().
+ */
+ hid_device_io_start(hsdev->hdev);
+
+ time_state->rtc = devm_rtc_device_register(&pdev->dev,
+ "hid-sensor-time", &hid_time_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR_OR_NULL(time_state->rtc)) {
+ hid_device_io_stop(hsdev->hdev);
+ ret = time_state->rtc ? PTR_ERR(time_state->rtc) : -ENODEV;
+ time_state->rtc = NULL;
+ dev_err(&pdev->dev, "rtc device register failed!\n");
+ goto err_rtc;
+ }
+
+ return ret;
+
+err_rtc:
+ sensor_hub_device_close(hsdev);
+err_open:
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
+ return ret;
+}
+
+static int hid_time_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
+
+ sensor_hub_device_close(hsdev);
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_TIME);
+
+ return 0;
+}
+
+static struct platform_device_id hid_time_ids[] = {
+ {
+ /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
+ .name = "HID-SENSOR-2000a0",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, hid_time_ids);
+
+static struct platform_driver hid_time_platform_driver = {
+ .id_table = hid_time_ids,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+ .probe = hid_time_probe,
+ .remove = hid_time_remove,
+};
+module_platform_driver(hid_time_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Time");
+MODULE_AUTHOR("Alexander Holler <holler@ahsoftware.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-hym8563.c b/drivers/rtc/rtc-hym8563.c
new file mode 100644
index 000000000..0f710e985
--- /dev/null
+++ b/drivers/rtc/rtc-hym8563.c
@@ -0,0 +1,612 @@
+/*
+ * Haoyu HYM8563 RTC driver
+ *
+ * Copyright (C) 2013 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on rtc-HYM8563
+ * Copyright (C) 2010 ROCKCHIP, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/clk-provider.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
+#define HYM8563_CTL1 0x00
+#define HYM8563_CTL1_TEST BIT(7)
+#define HYM8563_CTL1_STOP BIT(5)
+#define HYM8563_CTL1_TESTC BIT(3)
+
+#define HYM8563_CTL2 0x01
+#define HYM8563_CTL2_TI_TP BIT(4)
+#define HYM8563_CTL2_AF BIT(3)
+#define HYM8563_CTL2_TF BIT(2)
+#define HYM8563_CTL2_AIE BIT(1)
+#define HYM8563_CTL2_TIE BIT(0)
+
+#define HYM8563_SEC 0x02
+#define HYM8563_SEC_VL BIT(7)
+#define HYM8563_SEC_MASK 0x7f
+
+#define HYM8563_MIN 0x03
+#define HYM8563_MIN_MASK 0x7f
+
+#define HYM8563_HOUR 0x04
+#define HYM8563_HOUR_MASK 0x3f
+
+#define HYM8563_DAY 0x05
+#define HYM8563_DAY_MASK 0x3f
+
+#define HYM8563_WEEKDAY 0x06
+#define HYM8563_WEEKDAY_MASK 0x07
+
+#define HYM8563_MONTH 0x07
+#define HYM8563_MONTH_CENTURY BIT(7)
+#define HYM8563_MONTH_MASK 0x1f
+
+#define HYM8563_YEAR 0x08
+
+#define HYM8563_ALM_MIN 0x09
+#define HYM8563_ALM_HOUR 0x0a
+#define HYM8563_ALM_DAY 0x0b
+#define HYM8563_ALM_WEEK 0x0c
+
+/* Each alarm check can be disabled by setting this bit in the register */
+#define HYM8563_ALM_BIT_DISABLE BIT(7)
+
+#define HYM8563_CLKOUT 0x0d
+#define HYM8563_CLKOUT_ENABLE BIT(7)
+#define HYM8563_CLKOUT_32768 0
+#define HYM8563_CLKOUT_1024 1
+#define HYM8563_CLKOUT_32 2
+#define HYM8563_CLKOUT_1 3
+#define HYM8563_CLKOUT_MASK 3
+
+#define HYM8563_TMR_CTL 0x0e
+#define HYM8563_TMR_CTL_ENABLE BIT(7)
+#define HYM8563_TMR_CTL_4096 0
+#define HYM8563_TMR_CTL_64 1
+#define HYM8563_TMR_CTL_1 2
+#define HYM8563_TMR_CTL_1_60 3
+#define HYM8563_TMR_CTL_MASK 3
+
+#define HYM8563_TMR_CNT 0x0f
+
+struct hym8563 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ bool valid;
+#ifdef CONFIG_COMMON_CLK
+ struct clk_hw clkout_hw;
+#endif
+};
+
+/*
+ * RTC handling
+ */
+
+static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct hym8563 *hym8563 = i2c_get_clientdata(client);
+ u8 buf[7];
+ int ret;
+
+ if (!hym8563->valid) {
+ dev_warn(&client->dev, "no valid clock/calendar values available\n");
+ return -EPERM;
+ }
+
+ ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf);
+
+ tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK);
+ tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK);
+ tm->tm_hour = bcd2bin(buf[2] & HYM8563_HOUR_MASK);
+ tm->tm_mday = bcd2bin(buf[3] & HYM8563_DAY_MASK);
+ tm->tm_wday = bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */
+ tm->tm_mon = bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */
+ tm->tm_year = bcd2bin(buf[6]) + 100;
+
+ return 0;
+}
+
+static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct hym8563 *hym8563 = i2c_get_clientdata(client);
+ u8 buf[7];
+ int ret;
+
+ /* Years >= 2100 are to far in the future, 19XX is to early */
+ if (tm->tm_year < 100 || tm->tm_year >= 200)
+ return -EINVAL;
+
+ buf[0] = bin2bcd(tm->tm_sec);
+ buf[1] = bin2bcd(tm->tm_min);
+ buf[2] = bin2bcd(tm->tm_hour);
+ buf[3] = bin2bcd(tm->tm_mday);
+ buf[4] = bin2bcd(tm->tm_wday);
+ buf[5] = bin2bcd(tm->tm_mon + 1);
+
+ /*
+ * While the HYM8563 has a century flag in the month register,
+ * it does not seem to carry it over a subsequent write/read.
+ * So we'll limit ourself to 100 years, starting at 2000 for now.
+ */
+ buf[6] = tm->tm_year - 100;
+
+ /*
+ * CTL1 only contains TEST-mode bits apart from stop,
+ * so no need to read the value first
+ */
+ ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1,
+ HYM8563_CTL1_STOP);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, 7, buf);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0);
+ if (ret < 0)
+ return ret;
+
+ hym8563->valid = true;
+
+ return 0;
+}
+
+static int hym8563_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int data;
+
+ data = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+ if (data < 0)
+ return data;
+
+ if (enabled)
+ data |= HYM8563_CTL2_AIE;
+ else
+ data &= ~HYM8563_CTL2_AIE;
+
+ return i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);
+};
+
+static int hym8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rtc_time *alm_tm = &alm->time;
+ u8 buf[4];
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf);
+ if (ret < 0)
+ return ret;
+
+ /* The alarm only has a minute accuracy */
+ alm_tm->tm_sec = -1;
+
+ alm_tm->tm_min = (buf[0] & HYM8563_ALM_BIT_DISABLE) ?
+ -1 :
+ bcd2bin(buf[0] & HYM8563_MIN_MASK);
+ alm_tm->tm_hour = (buf[1] & HYM8563_ALM_BIT_DISABLE) ?
+ -1 :
+ bcd2bin(buf[1] & HYM8563_HOUR_MASK);
+ alm_tm->tm_mday = (buf[2] & HYM8563_ALM_BIT_DISABLE) ?
+ -1 :
+ bcd2bin(buf[2] & HYM8563_DAY_MASK);
+ alm_tm->tm_wday = (buf[3] & HYM8563_ALM_BIT_DISABLE) ?
+ -1 :
+ bcd2bin(buf[3] & HYM8563_WEEKDAY_MASK);
+
+ alm_tm->tm_mon = -1;
+ alm_tm->tm_year = -1;
+
+ ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+ if (ret < 0)
+ return ret;
+
+ if (ret & HYM8563_CTL2_AIE)
+ alm->enabled = 1;
+
+ return 0;
+}
+
+static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rtc_time *alm_tm = &alm->time;
+ u8 buf[4];
+ int ret;
+
+ /*
+ * The alarm has no seconds so deal with it
+ */
+ if (alm_tm->tm_sec) {
+ alm_tm->tm_sec = 0;
+ alm_tm->tm_min++;
+ if (alm_tm->tm_min >= 60) {
+ alm_tm->tm_min = 0;
+ alm_tm->tm_hour++;
+ if (alm_tm->tm_hour >= 24) {
+ alm_tm->tm_hour = 0;
+ alm_tm->tm_mday++;
+ if (alm_tm->tm_mday > 31)
+ alm_tm->tm_mday = 0;
+ }
+ }
+ }
+
+ ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~HYM8563_CTL2_AIE;
+
+ ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret);
+ if (ret < 0)
+ return ret;
+
+ buf[0] = (alm_tm->tm_min < 60 && alm_tm->tm_min >= 0) ?
+ bin2bcd(alm_tm->tm_min) : HYM8563_ALM_BIT_DISABLE;
+
+ buf[1] = (alm_tm->tm_hour < 24 && alm_tm->tm_hour >= 0) ?
+ bin2bcd(alm_tm->tm_hour) : HYM8563_ALM_BIT_DISABLE;
+
+ buf[2] = (alm_tm->tm_mday <= 31 && alm_tm->tm_mday >= 1) ?
+ bin2bcd(alm_tm->tm_mday) : HYM8563_ALM_BIT_DISABLE;
+
+ buf[3] = (alm_tm->tm_wday < 7 && alm_tm->tm_wday >= 0) ?
+ bin2bcd(alm_tm->tm_wday) : HYM8563_ALM_BIT_DISABLE;
+
+ ret = i2c_smbus_write_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf);
+ if (ret < 0)
+ return ret;
+
+ return hym8563_rtc_alarm_irq_enable(dev, alm->enabled);
+}
+
+static const struct rtc_class_ops hym8563_rtc_ops = {
+ .read_time = hym8563_rtc_read_time,
+ .set_time = hym8563_rtc_set_time,
+ .alarm_irq_enable = hym8563_rtc_alarm_irq_enable,
+ .read_alarm = hym8563_rtc_read_alarm,
+ .set_alarm = hym8563_rtc_set_alarm,
+};
+
+/*
+ * Handling of the clkout
+ */
+
+#ifdef CONFIG_COMMON_CLK
+#define clkout_hw_to_hym8563(_hw) container_of(_hw, struct hym8563, clkout_hw)
+
+static int clkout_rates[] = {
+ 32768,
+ 1024,
+ 32,
+ 1,
+};
+
+static unsigned long hym8563_clkout_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);
+ struct i2c_client *client = hym8563->client;
+ int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);
+
+ if (ret < 0)
+ return 0;
+
+ ret &= HYM8563_CLKOUT_MASK;
+ return clkout_rates[ret];
+}
+
+static long hym8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
+ if (clkout_rates[i] <= rate)
+ return clkout_rates[i];
+
+ return 0;
+}
+
+static int hym8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);
+ struct i2c_client *client = hym8563->client;
+ int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);
+ int i;
+
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
+ if (clkout_rates[i] == rate) {
+ ret &= ~HYM8563_CLKOUT_MASK;
+ ret |= i;
+ return i2c_smbus_write_byte_data(client,
+ HYM8563_CLKOUT, ret);
+ }
+
+ return -EINVAL;
+}
+
+static int hym8563_clkout_control(struct clk_hw *hw, bool enable)
+{
+ struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);
+ struct i2c_client *client = hym8563->client;
+ int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);
+
+ if (ret < 0)
+ return ret;
+
+ if (enable)
+ ret |= HYM8563_CLKOUT_ENABLE;
+ else
+ ret &= ~HYM8563_CLKOUT_ENABLE;
+
+ return i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, ret);
+}
+
+static int hym8563_clkout_prepare(struct clk_hw *hw)
+{
+ return hym8563_clkout_control(hw, 1);
+}
+
+static void hym8563_clkout_unprepare(struct clk_hw *hw)
+{
+ hym8563_clkout_control(hw, 0);
+}
+
+static int hym8563_clkout_is_prepared(struct clk_hw *hw)
+{
+ struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);
+ struct i2c_client *client = hym8563->client;
+ int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);
+
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & HYM8563_CLKOUT_ENABLE);
+}
+
+static const struct clk_ops hym8563_clkout_ops = {
+ .prepare = hym8563_clkout_prepare,
+ .unprepare = hym8563_clkout_unprepare,
+ .is_prepared = hym8563_clkout_is_prepared,
+ .recalc_rate = hym8563_clkout_recalc_rate,
+ .round_rate = hym8563_clkout_round_rate,
+ .set_rate = hym8563_clkout_set_rate,
+};
+
+static struct clk *hym8563_clkout_register_clk(struct hym8563 *hym8563)
+{
+ struct i2c_client *client = hym8563->client;
+ struct device_node *node = client->dev.of_node;
+ struct clk *clk;
+ struct clk_init_data init;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, HYM8563_CLKOUT,
+ 0);
+ if (ret < 0)
+ return ERR_PTR(ret);
+
+ init.name = "hym8563-clkout";
+ init.ops = &hym8563_clkout_ops;
+ init.flags = CLK_IS_ROOT;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ hym8563->clkout_hw.init = &init;
+
+ /* optional override of the clockname */
+ of_property_read_string(node, "clock-output-names", &init.name);
+
+ /* register the clock */
+ clk = clk_register(&client->dev, &hym8563->clkout_hw);
+
+ if (!IS_ERR(clk))
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ return clk;
+}
+#endif
+
+/*
+ * The alarm interrupt is implemented as a level-low interrupt in the
+ * hym8563, while the timer interrupt uses a falling edge.
+ * We don't use the timer at all, so the interrupt is requested to
+ * use the level-low trigger.
+ */
+static irqreturn_t hym8563_irq(int irq, void *dev_id)
+{
+ struct hym8563 *hym8563 = (struct hym8563 *)dev_id;
+ struct i2c_client *client = hym8563->client;
+ struct mutex *lock = &hym8563->rtc->ops_lock;
+ int data, ret;
+
+ mutex_lock(lock);
+
+ /* Clear the alarm flag */
+
+ data = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+ if (data < 0) {
+ dev_err(&client->dev, "%s: error reading i2c data %d\n",
+ __func__, data);
+ goto out;
+ }
+
+ data &= ~HYM8563_CTL2_AF;
+
+ ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: error writing i2c data %d\n",
+ __func__, ret);
+ }
+
+out:
+ mutex_unlock(lock);
+ return IRQ_HANDLED;
+}
+
+static int hym8563_init_device(struct i2c_client *client)
+{
+ int ret;
+
+ /* Clear stop flag if present */
+ ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);
+ if (ret < 0)
+ return ret;
+
+ /* Disable alarm and timer interrupts */
+ ret &= ~HYM8563_CTL2_AIE;
+ ret &= ~HYM8563_CTL2_TIE;
+
+ /* Clear any pending alarm and timer flags */
+ if (ret & HYM8563_CTL2_AF)
+ ret &= ~HYM8563_CTL2_AF;
+
+ if (ret & HYM8563_CTL2_TF)
+ ret &= ~HYM8563_CTL2_TF;
+
+ ret &= ~HYM8563_CTL2_TI_TP;
+
+ return i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int hym8563_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ if (device_may_wakeup(dev)) {
+ ret = enable_irq_wake(client->irq);
+ if (ret) {
+ dev_err(dev, "enable_irq_wake failed, %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int hym8563_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume);
+
+static int hym8563_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct hym8563 *hym8563;
+ int ret;
+
+ hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL);
+ if (!hym8563)
+ return -ENOMEM;
+
+ hym8563->client = client;
+ i2c_set_clientdata(client, hym8563);
+
+ device_set_wakeup_capable(&client->dev, true);
+
+ ret = hym8563_init_device(client);
+ if (ret) {
+ dev_err(&client->dev, "could not init device, %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, hym8563_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ client->name, hym8563);
+ if (ret < 0) {
+ dev_err(&client->dev, "irq %d request failed, %d\n",
+ client->irq, ret);
+ return ret;
+ }
+
+ /* check state of calendar information */
+ ret = i2c_smbus_read_byte_data(client, HYM8563_SEC);
+ if (ret < 0)
+ return ret;
+
+ hym8563->valid = !(ret & HYM8563_SEC_VL);
+ dev_dbg(&client->dev, "rtc information is %s\n",
+ hym8563->valid ? "valid" : "invalid");
+
+ hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,
+ &hym8563_rtc_ops, THIS_MODULE);
+ if (IS_ERR(hym8563->rtc))
+ return PTR_ERR(hym8563->rtc);
+
+ /* the hym8563 alarm only supports a minute accuracy */
+ hym8563->rtc->uie_unsupported = 1;
+
+#ifdef CONFIG_COMMON_CLK
+ hym8563_clkout_register_clk(hym8563);
+#endif
+
+ return 0;
+}
+
+static const struct i2c_device_id hym8563_id[] = {
+ { "hym8563", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, hym8563_id);
+
+static const struct of_device_id hym8563_dt_idtable[] = {
+ { .compatible = "haoyu,hym8563" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hym8563_dt_idtable);
+
+static struct i2c_driver hym8563_driver = {
+ .driver = {
+ .name = "rtc-hym8563",
+ .owner = THIS_MODULE,
+ .pm = &hym8563_pm_ops,
+ .of_match_table = hym8563_dt_idtable,
+ },
+ .probe = hym8563_probe,
+ .id_table = hym8563_id,
+};
+
+module_i2c_driver(hym8563_driver);
+
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("HYM8563 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-imxdi.c b/drivers/rtc/rtc-imxdi.c
new file mode 100644
index 000000000..c666eab98
--- /dev/null
+++ b/drivers/rtc/rtc-imxdi.c
@@ -0,0 +1,548 @@
+/*
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2010 Orex Computed Radiography
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/* based on rtc-mc13892.c */
+
+/*
+ * This driver uses the 47-bit 32 kHz counter in the Freescale DryIce block
+ * to implement a Linux RTC. Times and alarms are truncated to seconds.
+ * Since the RTC framework performs API locking via rtc->ops_lock the
+ * only simultaneous accesses we need to deal with is updating DryIce
+ * registers while servicing an alarm.
+ *
+ * Note that reading the DSR (DryIce Status Register) automatically clears
+ * the WCF (Write Complete Flag). All DryIce writes are synchronized to the
+ * LP (Low Power) domain and set the WCF upon completion. Writes to the
+ * DIER (DryIce Interrupt Enable Register) are the only exception. These
+ * occur at normal bus speeds and do not set WCF. Periodic interrupts are
+ * not supported by the hardware.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/of.h>
+
+/* DryIce Register Definitions */
+
+#define DTCMR 0x00 /* Time Counter MSB Reg */
+#define DTCLR 0x04 /* Time Counter LSB Reg */
+
+#define DCAMR 0x08 /* Clock Alarm MSB Reg */
+#define DCALR 0x0c /* Clock Alarm LSB Reg */
+#define DCAMR_UNSET 0xFFFFFFFF /* doomsday - 1 sec */
+
+#define DCR 0x10 /* Control Reg */
+#define DCR_TDCHL (1 << 30) /* Tamper-detect configuration hard lock */
+#define DCR_TDCSL (1 << 29) /* Tamper-detect configuration soft lock */
+#define DCR_KSSL (1 << 27) /* Key-select soft lock */
+#define DCR_MCHL (1 << 20) /* Monotonic-counter hard lock */
+#define DCR_MCSL (1 << 19) /* Monotonic-counter soft lock */
+#define DCR_TCHL (1 << 18) /* Timer-counter hard lock */
+#define DCR_TCSL (1 << 17) /* Timer-counter soft lock */
+#define DCR_FSHL (1 << 16) /* Failure state hard lock */
+#define DCR_TCE (1 << 3) /* Time Counter Enable */
+#define DCR_MCE (1 << 2) /* Monotonic Counter Enable */
+
+#define DSR 0x14 /* Status Reg */
+#define DSR_WTD (1 << 23) /* Wire-mesh tamper detected */
+#define DSR_ETBD (1 << 22) /* External tamper B detected */
+#define DSR_ETAD (1 << 21) /* External tamper A detected */
+#define DSR_EBD (1 << 20) /* External boot detected */
+#define DSR_SAD (1 << 19) /* SCC alarm detected */
+#define DSR_TTD (1 << 18) /* Temperatur tamper detected */
+#define DSR_CTD (1 << 17) /* Clock tamper detected */
+#define DSR_VTD (1 << 16) /* Voltage tamper detected */
+#define DSR_WBF (1 << 10) /* Write Busy Flag (synchronous) */
+#define DSR_WNF (1 << 9) /* Write Next Flag (synchronous) */
+#define DSR_WCF (1 << 8) /* Write Complete Flag (synchronous)*/
+#define DSR_WEF (1 << 7) /* Write Error Flag */
+#define DSR_CAF (1 << 4) /* Clock Alarm Flag */
+#define DSR_MCO (1 << 3) /* monotonic counter overflow */
+#define DSR_TCO (1 << 2) /* time counter overflow */
+#define DSR_NVF (1 << 1) /* Non-Valid Flag */
+#define DSR_SVF (1 << 0) /* Security Violation Flag */
+
+#define DIER 0x18 /* Interrupt Enable Reg (synchronous) */
+#define DIER_WNIE (1 << 9) /* Write Next Interrupt Enable */
+#define DIER_WCIE (1 << 8) /* Write Complete Interrupt Enable */
+#define DIER_WEIE (1 << 7) /* Write Error Interrupt Enable */
+#define DIER_CAIE (1 << 4) /* Clock Alarm Interrupt Enable */
+#define DIER_SVIE (1 << 0) /* Security-violation Interrupt Enable */
+
+#define DMCR 0x1c /* DryIce Monotonic Counter Reg */
+
+#define DTCR 0x28 /* DryIce Tamper Configuration Reg */
+#define DTCR_MOE (1 << 9) /* monotonic overflow enabled */
+#define DTCR_TOE (1 << 8) /* time overflow enabled */
+#define DTCR_WTE (1 << 7) /* wire-mesh tamper enabled */
+#define DTCR_ETBE (1 << 6) /* external B tamper enabled */
+#define DTCR_ETAE (1 << 5) /* external A tamper enabled */
+#define DTCR_EBE (1 << 4) /* external boot tamper enabled */
+#define DTCR_SAIE (1 << 3) /* SCC enabled */
+#define DTCR_TTE (1 << 2) /* temperature tamper enabled */
+#define DTCR_CTE (1 << 1) /* clock tamper enabled */
+#define DTCR_VTE (1 << 0) /* voltage tamper enabled */
+
+#define DGPR 0x3c /* DryIce General Purpose Reg */
+
+/**
+ * struct imxdi_dev - private imxdi rtc data
+ * @pdev: pionter to platform dev
+ * @rtc: pointer to rtc struct
+ * @ioaddr: IO registers pointer
+ * @irq: dryice normal interrupt
+ * @clk: input reference clock
+ * @dsr: copy of the DSR register
+ * @irq_lock: interrupt enable register (DIER) lock
+ * @write_wait: registers write complete queue
+ * @write_mutex: serialize registers write
+ * @work: schedule alarm work
+ */
+struct imxdi_dev {
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+ u32 dsr;
+ spinlock_t irq_lock;
+ wait_queue_head_t write_wait;
+ struct mutex write_mutex;
+ struct work_struct work;
+};
+
+/*
+ * enable a dryice interrupt
+ */
+static void di_int_enable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) | intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * disable a dryice interrupt
+ */
+static void di_int_disable(struct imxdi_dev *imxdi, u32 intr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&imxdi->irq_lock, flags);
+ __raw_writel(__raw_readl(imxdi->ioaddr + DIER) & ~intr,
+ imxdi->ioaddr + DIER);
+ spin_unlock_irqrestore(&imxdi->irq_lock, flags);
+}
+
+/*
+ * This function attempts to clear the dryice write-error flag.
+ *
+ * A dryice write error is similar to a bus fault and should not occur in
+ * normal operation. Clearing the flag requires another write, so the root
+ * cause of the problem may need to be fixed before the flag can be cleared.
+ */
+static void clear_write_error(struct imxdi_dev *imxdi)
+{
+ int cnt;
+
+ dev_warn(&imxdi->pdev->dev, "WARNING: Register write error!\n");
+
+ /* clear the write error flag */
+ __raw_writel(DSR_WEF, imxdi->ioaddr + DSR);
+
+ /* wait for it to take effect */
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if ((__raw_readl(imxdi->ioaddr + DSR) & DSR_WEF) == 0)
+ return;
+ udelay(10);
+ }
+ dev_err(&imxdi->pdev->dev,
+ "ERROR: Cannot clear write-error flag!\n");
+}
+
+/*
+ * Write a dryice register and wait until it completes.
+ *
+ * This function uses interrupts to determine when the
+ * write has completed.
+ */
+static int di_write_wait(struct imxdi_dev *imxdi, u32 val, int reg)
+{
+ int ret;
+ int rc = 0;
+
+ /* serialize register writes */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* enable the write-complete interrupt */
+ di_int_enable(imxdi, DIER_WCIE);
+
+ imxdi->dsr = 0;
+
+ /* do the register write */
+ __raw_writel(val, imxdi->ioaddr + reg);
+
+ /* wait for the write to finish */
+ ret = wait_event_interruptible_timeout(imxdi->write_wait,
+ imxdi->dsr & (DSR_WCF | DSR_WEF), msecs_to_jiffies(1));
+ if (ret < 0) {
+ rc = ret;
+ goto out;
+ } else if (ret == 0) {
+ dev_warn(&imxdi->pdev->dev,
+ "Write-wait timeout "
+ "val = 0x%08x reg = 0x%08x\n", val, reg);
+ }
+
+ /* check for write error */
+ if (imxdi->dsr & DSR_WEF) {
+ clear_write_error(imxdi);
+ rc = -EIO;
+ }
+
+out:
+ mutex_unlock(&imxdi->write_mutex);
+
+ return rc;
+}
+
+/*
+ * read the seconds portion of the current time from the dryice time counter
+ */
+static int dryice_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ rtc_time_to_tm(now, tm);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice time counter and clear the
+ * fractional part.
+ */
+static int dryice_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ int rc;
+
+ /* zero the fractional part first */
+ rc = di_write_wait(imxdi, 0, DTCLR);
+ if (rc == 0)
+ rc = di_write_wait(imxdi, secs, DTCMR);
+
+ return rc;
+}
+
+static int dryice_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+
+ if (enabled)
+ di_int_enable(imxdi, DIER_CAIE);
+ else
+ di_int_disable(imxdi, DIER_CAIE);
+
+ return 0;
+}
+
+/*
+ * read the seconds portion of the alarm register.
+ * the fractional part of the alarm register is always zero.
+ */
+static int dryice_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ u32 dcamr;
+
+ dcamr = __raw_readl(imxdi->ioaddr + DCAMR);
+ rtc_time_to_tm(dcamr, &alarm->time);
+
+ /* alarm is enabled if the interrupt is enabled */
+ alarm->enabled = (__raw_readl(imxdi->ioaddr + DIER) & DIER_CAIE) != 0;
+
+ /* don't allow the DSR read to mess up DSR_WCF */
+ mutex_lock(&imxdi->write_mutex);
+
+ /* alarm is pending if the alarm flag is set */
+ alarm->pending = (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) != 0;
+
+ mutex_unlock(&imxdi->write_mutex);
+
+ return 0;
+}
+
+/*
+ * set the seconds portion of dryice alarm register
+ */
+static int dryice_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct imxdi_dev *imxdi = dev_get_drvdata(dev);
+ unsigned long now;
+ unsigned long alarm_time;
+ int rc;
+
+ rc = rtc_tm_to_time(&alarm->time, &alarm_time);
+ if (rc)
+ return rc;
+
+ /* don't allow setting alarm in the past */
+ now = __raw_readl(imxdi->ioaddr + DTCMR);
+ if (alarm_time < now)
+ return -EINVAL;
+
+ /* write the new alarm time */
+ rc = di_write_wait(imxdi, (u32)alarm_time, DCAMR);
+ if (rc)
+ return rc;
+
+ if (alarm->enabled)
+ di_int_enable(imxdi, DIER_CAIE); /* enable alarm intr */
+ else
+ di_int_disable(imxdi, DIER_CAIE); /* disable alarm intr */
+
+ return 0;
+}
+
+static struct rtc_class_ops dryice_rtc_ops = {
+ .read_time = dryice_rtc_read_time,
+ .set_mmss = dryice_rtc_set_mmss,
+ .alarm_irq_enable = dryice_rtc_alarm_irq_enable,
+ .read_alarm = dryice_rtc_read_alarm,
+ .set_alarm = dryice_rtc_set_alarm,
+};
+
+/*
+ * dryice "normal" interrupt handler
+ */
+static irqreturn_t dryice_norm_irq(int irq, void *dev_id)
+{
+ struct imxdi_dev *imxdi = dev_id;
+ u32 dsr, dier;
+ irqreturn_t rc = IRQ_NONE;
+
+ dier = __raw_readl(imxdi->ioaddr + DIER);
+
+ /* handle write complete and write error cases */
+ if (dier & DIER_WCIE) {
+ /*If the write wait queue is empty then there is no pending
+ operations. It means the interrupt is for DryIce -Security.
+ IRQ must be returned as none.*/
+ if (list_empty_careful(&imxdi->write_wait.task_list))
+ return rc;
+
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if (dsr & (DSR_WCF | DSR_WEF)) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_WCIE);
+
+ /* save the dsr value for the wait queue */
+ imxdi->dsr |= dsr;
+
+ wake_up_interruptible(&imxdi->write_wait);
+ rc = IRQ_HANDLED;
+ }
+ }
+
+ /* handle the alarm case */
+ if (dier & DIER_CAIE) {
+ /* DSR_WCF clears itself on DSR read */
+ dsr = __raw_readl(imxdi->ioaddr + DSR);
+ if (dsr & DSR_CAF) {
+ /* mask the interrupt */
+ di_int_disable(imxdi, DIER_CAIE);
+
+ /* finish alarm in user context */
+ schedule_work(&imxdi->work);
+ rc = IRQ_HANDLED;
+ }
+ }
+ return rc;
+}
+
+/*
+ * post the alarm event from user context so it can sleep
+ * on the write completion.
+ */
+static void dryice_work(struct work_struct *work)
+{
+ struct imxdi_dev *imxdi = container_of(work,
+ struct imxdi_dev, work);
+
+ /* dismiss the interrupt (ignore error) */
+ di_write_wait(imxdi, DSR_CAF, DSR);
+
+ /* pass the alarm event to the rtc framework. */
+ rtc_update_irq(imxdi->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+/*
+ * probe for dryice rtc device
+ */
+static int __init dryice_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct imxdi_dev *imxdi;
+ int rc;
+
+ imxdi = devm_kzalloc(&pdev->dev, sizeof(*imxdi), GFP_KERNEL);
+ if (!imxdi)
+ return -ENOMEM;
+
+ imxdi->pdev = pdev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ imxdi->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(imxdi->ioaddr))
+ return PTR_ERR(imxdi->ioaddr);
+
+ spin_lock_init(&imxdi->irq_lock);
+
+ imxdi->irq = platform_get_irq(pdev, 0);
+ if (imxdi->irq < 0)
+ return imxdi->irq;
+
+ init_waitqueue_head(&imxdi->write_wait);
+
+ INIT_WORK(&imxdi->work, dryice_work);
+
+ mutex_init(&imxdi->write_mutex);
+
+ imxdi->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(imxdi->clk))
+ return PTR_ERR(imxdi->clk);
+ rc = clk_prepare_enable(imxdi->clk);
+ if (rc)
+ return rc;
+
+ /*
+ * Initialize dryice hardware
+ */
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ rc = devm_request_irq(&pdev->dev, imxdi->irq, dryice_norm_irq,
+ IRQF_SHARED, pdev->name, imxdi);
+ if (rc) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ goto err;
+ }
+
+ /* put dryice into valid state */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_NVF) {
+ rc = di_write_wait(imxdi, DSR_NVF | DSR_SVF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* initialize alarm */
+ rc = di_write_wait(imxdi, DCAMR_UNSET, DCAMR);
+ if (rc)
+ goto err;
+ rc = di_write_wait(imxdi, 0, DCALR);
+ if (rc)
+ goto err;
+
+ /* clear alarm flag */
+ if (__raw_readl(imxdi->ioaddr + DSR) & DSR_CAF) {
+ rc = di_write_wait(imxdi, DSR_CAF, DSR);
+ if (rc)
+ goto err;
+ }
+
+ /* the timer won't count if it has never been written to */
+ if (__raw_readl(imxdi->ioaddr + DTCMR) == 0) {
+ rc = di_write_wait(imxdi, 0, DTCMR);
+ if (rc)
+ goto err;
+ }
+
+ /* start keeping time */
+ if (!(__raw_readl(imxdi->ioaddr + DCR) & DCR_TCE)) {
+ rc = di_write_wait(imxdi,
+ __raw_readl(imxdi->ioaddr + DCR) | DCR_TCE,
+ DCR);
+ if (rc)
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, imxdi);
+ imxdi->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &dryice_rtc_ops, THIS_MODULE);
+ if (IS_ERR(imxdi->rtc)) {
+ rc = PTR_ERR(imxdi->rtc);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ clk_disable_unprepare(imxdi->clk);
+
+ return rc;
+}
+
+static int __exit dryice_rtc_remove(struct platform_device *pdev)
+{
+ struct imxdi_dev *imxdi = platform_get_drvdata(pdev);
+
+ flush_work(&imxdi->work);
+
+ /* mask all interrupts */
+ __raw_writel(0, imxdi->ioaddr + DIER);
+
+ clk_disable_unprepare(imxdi->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id dryice_dt_ids[] = {
+ { .compatible = "fsl,imx25-rtc" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, dryice_dt_ids);
+#endif
+
+static struct platform_driver dryice_rtc_driver = {
+ .driver = {
+ .name = "imxdi_rtc",
+ .of_match_table = of_match_ptr(dryice_dt_ids),
+ },
+ .remove = __exit_p(dryice_rtc_remove),
+};
+
+module_platform_driver_probe(dryice_rtc_driver, dryice_rtc_probe);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
+MODULE_DESCRIPTION("IMX DryIce Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
new file mode 100644
index 000000000..f9b082784
--- /dev/null
+++ b/drivers/rtc/rtc-isl12022.c
@@ -0,0 +1,306 @@
+/*
+ * An I2C driver for the Intersil ISL 12022
+ *
+ * Author: Roman Fietze <roman.fietze@telemotive.de>
+ *
+ * Based on the Philips PCF8563 RTC
+ * by Alessandro Zummo <a.zummo@towertech.it>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#define DRV_VERSION "0.1"
+
+/* ISL register offsets */
+#define ISL12022_REG_SC 0x00
+#define ISL12022_REG_MN 0x01
+#define ISL12022_REG_HR 0x02
+#define ISL12022_REG_DT 0x03
+#define ISL12022_REG_MO 0x04
+#define ISL12022_REG_YR 0x05
+#define ISL12022_REG_DW 0x06
+
+#define ISL12022_REG_SR 0x07
+#define ISL12022_REG_INT 0x08
+
+/* ISL register bits */
+#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
+
+#define ISL12022_SR_LBAT85 (1 << 2)
+#define ISL12022_SR_LBAT75 (1 << 1)
+
+#define ISL12022_INT_WRTC (1 << 6)
+
+
+static struct i2c_driver isl12022_driver;
+
+struct isl12022 {
+ struct rtc_device *rtc;
+
+ bool write_enabled; /* true if write enable is set */
+};
+
+
+static int isl12022_read_regs(struct i2c_client *client, uint8_t reg,
+ uint8_t *data, size_t n)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = data
+ }, /* setup read ptr */
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = n,
+ .buf = data
+ }
+ };
+
+ int ret;
+
+ data[0] = reg;
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: read error, ret=%d\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static int isl12022_write_reg(struct i2c_client *client,
+ uint8_t reg, uint8_t val)
+{
+ uint8_t data[2] = { reg, val };
+ int err;
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+/*
+ * In the routines that deal directly with the isl12022 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int isl12022_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ uint8_t buf[ISL12022_REG_INT + 1];
+ int ret;
+
+ ret = isl12022_read_regs(client, ISL12022_REG_SC, buf, sizeof(buf));
+ if (ret)
+ return ret;
+
+ if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
+ dev_warn(&client->dev,
+ "voltage dropped below %u%%, "
+ "date and time is not reliable.\n",
+ buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, mon=%02x, year=%02x, wday=%02x, "
+ "sr=%02x, int=%02x",
+ __func__,
+ buf[ISL12022_REG_SC],
+ buf[ISL12022_REG_MN],
+ buf[ISL12022_REG_HR],
+ buf[ISL12022_REG_DT],
+ buf[ISL12022_REG_MO],
+ buf[ISL12022_REG_YR],
+ buf[ISL12022_REG_DW],
+ buf[ISL12022_REG_SR],
+ buf[ISL12022_REG_INT]);
+
+ tm->tm_sec = bcd2bin(buf[ISL12022_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[ISL12022_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[ISL12022_REG_HR] & 0x3F);
+ tm->tm_mday = bcd2bin(buf[ISL12022_REG_DT] & 0x3F);
+ tm->tm_wday = buf[ISL12022_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[ISL12022_REG_MO] & 0x1F) - 1;
+ tm->tm_year = bcd2bin(buf[ISL12022_REG_YR]) + 100;
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* The clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup. */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date and time is invalid.\n");
+
+ return 0;
+}
+
+static int isl12022_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct isl12022 *isl12022 = i2c_get_clientdata(client);
+ size_t i;
+ int ret;
+ uint8_t buf[ISL12022_REG_DW + 1];
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (!isl12022->write_enabled) {
+
+ ret = isl12022_read_regs(client, ISL12022_REG_INT, buf, 1);
+ if (ret)
+ return ret;
+
+ /* Check if WRTC (write rtc enable) is set factory default is
+ * 0 (not set) */
+ if (!(buf[0] & ISL12022_INT_WRTC)) {
+ dev_info(&client->dev,
+ "init write enable and 24 hour format\n");
+
+ /* Set the write enable bit. */
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_INT,
+ buf[0] | ISL12022_INT_WRTC);
+ if (ret)
+ return ret;
+
+ /* Write to any RTC register to start RTC, we use the
+ * HR register, setting the MIL bit to use the 24 hour
+ * format. */
+ ret = isl12022_read_regs(client, ISL12022_REG_HR,
+ buf, 1);
+ if (ret)
+ return ret;
+
+ ret = isl12022_write_reg(client,
+ ISL12022_REG_HR,
+ buf[0] | ISL12022_HR_MIL);
+ if (ret)
+ return ret;
+ }
+
+ isl12022->write_enabled = 1;
+ }
+
+ /* hours, minutes and seconds */
+ buf[ISL12022_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[ISL12022_REG_MN] = bin2bcd(tm->tm_min);
+ buf[ISL12022_REG_HR] = bin2bcd(tm->tm_hour) | ISL12022_HR_MIL;
+
+ buf[ISL12022_REG_DT] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[ISL12022_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[ISL12022_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
+
+ /* write register's data */
+ for (i = 0; i < ARRAY_SIZE(buf); i++) {
+ ret = isl12022_write_reg(client, ISL12022_REG_SC + i,
+ buf[ISL12022_REG_SC + i]);
+ if (ret)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl12022_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops isl12022_rtc_ops = {
+ .read_time = isl12022_rtc_read_time,
+ .set_time = isl12022_rtc_set_time,
+};
+
+static int isl12022_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl12022 *isl12022;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ isl12022 = devm_kzalloc(&client->dev, sizeof(struct isl12022),
+ GFP_KERNEL);
+ if (!isl12022)
+ return -ENOMEM;
+
+ dev_dbg(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, isl12022);
+
+ isl12022->rtc = devm_rtc_device_register(&client->dev,
+ isl12022_driver.driver.name,
+ &isl12022_rtc_ops, THIS_MODULE);
+ return PTR_ERR_OR_ZERO(isl12022->rtc);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id isl12022_dt_match[] = {
+ { .compatible = "isl,isl12022" }, /* for backward compat., don't use */
+ { .compatible = "isil,isl12022" },
+ { },
+};
+#endif
+
+static const struct i2c_device_id isl12022_id[] = {
+ { "isl12022", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl12022_id);
+
+static struct i2c_driver isl12022_driver = {
+ .driver = {
+ .name = "rtc-isl12022",
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(isl12022_dt_match),
+#endif
+ },
+ .probe = isl12022_probe,
+ .id_table = isl12022_id,
+};
+
+module_i2c_driver(isl12022_driver);
+
+MODULE_AUTHOR("roman.fietze@telemotive.de");
+MODULE_DESCRIPTION("ISL 12022 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c
new file mode 100644
index 000000000..da818d333
--- /dev/null
+++ b/drivers/rtc/rtc-isl12057.c
@@ -0,0 +1,674 @@
+/*
+ * rtc-isl12057 - Driver for Intersil ISL12057 I2C Real Time Clock
+ *
+ * Copyright (C) 2013, Arnaud EBALARD <arno@natisbad.org>
+ *
+ * This work is largely based on Intersil ISL1208 driver developed by
+ * Hebert Valerio Riedel <hvr@gnu.org>.
+ *
+ * Detailed datasheet on which this development is based is available here:
+ *
+ * http://natisbad.org/NAS2/refs/ISL12057.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+#define DRV_NAME "rtc-isl12057"
+
+/* RTC section */
+#define ISL12057_REG_RTC_SC 0x00 /* Seconds */
+#define ISL12057_REG_RTC_MN 0x01 /* Minutes */
+#define ISL12057_REG_RTC_HR 0x02 /* Hours */
+#define ISL12057_REG_RTC_HR_PM BIT(5) /* AM/PM bit in 12h format */
+#define ISL12057_REG_RTC_HR_MIL BIT(6) /* 24h/12h format */
+#define ISL12057_REG_RTC_DW 0x03 /* Day of the Week */
+#define ISL12057_REG_RTC_DT 0x04 /* Date */
+#define ISL12057_REG_RTC_MO 0x05 /* Month */
+#define ISL12057_REG_RTC_MO_CEN BIT(7) /* Century bit */
+#define ISL12057_REG_RTC_YR 0x06 /* Year */
+#define ISL12057_RTC_SEC_LEN 7
+
+/* Alarm 1 section */
+#define ISL12057_REG_A1_SC 0x07 /* Alarm 1 Seconds */
+#define ISL12057_REG_A1_MN 0x08 /* Alarm 1 Minutes */
+#define ISL12057_REG_A1_HR 0x09 /* Alarm 1 Hours */
+#define ISL12057_REG_A1_HR_PM BIT(5) /* AM/PM bit in 12h format */
+#define ISL12057_REG_A1_HR_MIL BIT(6) /* 24h/12h format */
+#define ISL12057_REG_A1_DWDT 0x0A /* Alarm 1 Date / Day of the week */
+#define ISL12057_REG_A1_DWDT_B BIT(6) /* DW / DT selection bit */
+#define ISL12057_A1_SEC_LEN 4
+
+/* Alarm 2 section */
+#define ISL12057_REG_A2_MN 0x0B /* Alarm 2 Minutes */
+#define ISL12057_REG_A2_HR 0x0C /* Alarm 2 Hours */
+#define ISL12057_REG_A2_DWDT 0x0D /* Alarm 2 Date / Day of the week */
+#define ISL12057_A2_SEC_LEN 3
+
+/* Control/Status registers */
+#define ISL12057_REG_INT 0x0E
+#define ISL12057_REG_INT_A1IE BIT(0) /* Alarm 1 interrupt enable bit */
+#define ISL12057_REG_INT_A2IE BIT(1) /* Alarm 2 interrupt enable bit */
+#define ISL12057_REG_INT_INTCN BIT(2) /* Interrupt control enable bit */
+#define ISL12057_REG_INT_RS1 BIT(3) /* Freq out control bit 1 */
+#define ISL12057_REG_INT_RS2 BIT(4) /* Freq out control bit 2 */
+#define ISL12057_REG_INT_EOSC BIT(7) /* Oscillator enable bit */
+
+#define ISL12057_REG_SR 0x0F
+#define ISL12057_REG_SR_A1F BIT(0) /* Alarm 1 interrupt bit */
+#define ISL12057_REG_SR_A2F BIT(1) /* Alarm 2 interrupt bit */
+#define ISL12057_REG_SR_OSF BIT(7) /* Oscillator failure bit */
+
+/* Register memory map length */
+#define ISL12057_MEM_MAP_LEN 0x10
+
+struct isl12057_rtc_data {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+ struct mutex lock;
+ int irq;
+};
+
+static void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs)
+{
+ tm->tm_sec = bcd2bin(regs[ISL12057_REG_RTC_SC]);
+ tm->tm_min = bcd2bin(regs[ISL12057_REG_RTC_MN]);
+
+ if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_MIL) { /* AM/PM */
+ tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x1f);
+ if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_PM)
+ tm->tm_hour += 12;
+ } else { /* 24 hour mode */
+ tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x3f);
+ }
+
+ tm->tm_mday = bcd2bin(regs[ISL12057_REG_RTC_DT]);
+ tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */
+ tm->tm_mon = bcd2bin(regs[ISL12057_REG_RTC_MO] & 0x1f) - 1; /* ditto */
+ tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100;
+
+ /* Check if years register has overflown from 99 to 00 */
+ if (regs[ISL12057_REG_RTC_MO] & ISL12057_REG_RTC_MO_CEN)
+ tm->tm_year += 100;
+}
+
+static int isl12057_rtc_tm_to_regs(u8 *regs, struct rtc_time *tm)
+{
+ u8 century_bit;
+
+ /*
+ * The clock has an 8 bit wide bcd-coded register for the year.
+ * It also has a century bit encoded in MO flag which provides
+ * information about overflow of year register from 99 to 00.
+ * tm_year is an offset from 1900 and we are interested in the
+ * 2000-2199 range, so any value less than 100 or larger than
+ * 299 is invalid.
+ */
+ if (tm->tm_year < 100 || tm->tm_year > 299)
+ return -EINVAL;
+
+ century_bit = (tm->tm_year > 199) ? ISL12057_REG_RTC_MO_CEN : 0;
+
+ regs[ISL12057_REG_RTC_SC] = bin2bcd(tm->tm_sec);
+ regs[ISL12057_REG_RTC_MN] = bin2bcd(tm->tm_min);
+ regs[ISL12057_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */
+ regs[ISL12057_REG_RTC_DT] = bin2bcd(tm->tm_mday);
+ regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1) | century_bit;
+ regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year % 100);
+ regs[ISL12057_REG_RTC_DW] = bin2bcd(tm->tm_wday + 1);
+
+ return 0;
+}
+
+/*
+ * Try and match register bits w/ fixed null values to see whether we
+ * are dealing with an ISL12057. Note: this function is called early
+ * during init and hence does need mutex protection.
+ */
+static int isl12057_i2c_validate_chip(struct regmap *regmap)
+{
+ u8 regs[ISL12057_MEM_MAP_LEN];
+ static const u8 mask[ISL12057_MEM_MAP_LEN] = { 0x80, 0x80, 0x80, 0xf8,
+ 0xc0, 0x60, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x60, 0x7c };
+ int ret, i;
+
+ ret = regmap_bulk_read(regmap, 0, regs, ISL12057_MEM_MAP_LEN);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ISL12057_MEM_MAP_LEN; ++i) {
+ if (regs[i] & mask[i]) /* check if bits are cleared */
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int _isl12057_rtc_clear_alarm(struct device *dev)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ISL12057_REG_SR,
+ ISL12057_REG_SR_A1F, 0);
+ if (ret)
+ dev_err(dev, "%s: clearing alarm failed (%d)\n", __func__, ret);
+
+ return ret;
+}
+
+static int _isl12057_rtc_update_alarm(struct device *dev, int enable)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, ISL12057_REG_INT,
+ ISL12057_REG_INT_A1IE,
+ enable ? ISL12057_REG_INT_A1IE : 0);
+ if (ret)
+ dev_err(dev, "%s: changing alarm interrupt flag failed (%d)\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/*
+ * Note: as we only read from device and do not perform any update, there is
+ * no need for an equivalent function which would try and get driver's main
+ * lock. Here, it is safe for everyone if we just use regmap internal lock
+ * on the device when reading.
+ */
+static int _isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ u8 regs[ISL12057_RTC_SEC_LEN];
+ unsigned int sr;
+ int ret;
+
+ ret = regmap_read(data->regmap, ISL12057_REG_SR, &sr);
+ if (ret) {
+ dev_err(dev, "%s: unable to read oscillator status flag (%d)\n",
+ __func__, ret);
+ goto out;
+ } else {
+ if (sr & ISL12057_REG_SR_OSF) {
+ ret = -ENODATA;
+ goto out;
+ }
+ }
+
+ ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs,
+ ISL12057_RTC_SEC_LEN);
+ if (ret)
+ dev_err(dev, "%s: unable to read RTC time section (%d)\n",
+ __func__, ret);
+
+out:
+ if (ret)
+ return ret;
+
+ isl12057_rtc_regs_to_tm(tm, regs);
+
+ return rtc_valid_tm(tm);
+}
+
+static int isl12057_rtc_update_alarm(struct device *dev, int enable)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = _isl12057_rtc_update_alarm(dev, enable);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int isl12057_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time rtc_tm, *alarm_tm = &alarm->time;
+ unsigned long rtc_secs, alarm_secs;
+ u8 regs[ISL12057_A1_SEC_LEN];
+ unsigned int ir;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_bulk_read(data->regmap, ISL12057_REG_A1_SC, regs,
+ ISL12057_A1_SEC_LEN);
+ if (ret) {
+ dev_err(dev, "%s: reading alarm section failed (%d)\n",
+ __func__, ret);
+ goto err_unlock;
+ }
+
+ alarm_tm->tm_sec = bcd2bin(regs[0] & 0x7f);
+ alarm_tm->tm_min = bcd2bin(regs[1] & 0x7f);
+ alarm_tm->tm_hour = bcd2bin(regs[2] & 0x3f);
+ alarm_tm->tm_mday = bcd2bin(regs[3] & 0x3f);
+ alarm_tm->tm_wday = -1;
+
+ /*
+ * The alarm section does not store year/month. We use the ones in rtc
+ * section as a basis and increment month and then year if needed to get
+ * alarm after current time.
+ */
+ ret = _isl12057_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ goto err_unlock;
+
+ alarm_tm->tm_year = rtc_tm.tm_year;
+ alarm_tm->tm_mon = rtc_tm.tm_mon;
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err_unlock;
+
+ ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
+ if (ret)
+ goto err_unlock;
+
+ if (alarm_secs < rtc_secs) {
+ if (alarm_tm->tm_mon == 11) {
+ alarm_tm->tm_mon = 0;
+ alarm_tm->tm_year += 1;
+ } else {
+ alarm_tm->tm_mon += 1;
+ }
+ }
+
+ ret = regmap_read(data->regmap, ISL12057_REG_INT, &ir);
+ if (ret) {
+ dev_err(dev, "%s: reading alarm interrupt flag failed (%d)\n",
+ __func__, ret);
+ goto err_unlock;
+ }
+
+ alarm->enabled = !!(ir & ISL12057_REG_INT_A1IE);
+
+err_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int isl12057_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time *alarm_tm = &alarm->time;
+ unsigned long rtc_secs, alarm_secs;
+ u8 regs[ISL12057_A1_SEC_LEN];
+ struct rtc_time rtc_tm;
+ int ret, enable = 1;
+
+ mutex_lock(&data->lock);
+ ret = _isl12057_rtc_read_time(dev, &rtc_tm);
+ if (ret)
+ goto err_unlock;
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err_unlock;
+
+ ret = rtc_tm_to_time(alarm_tm, &alarm_secs);
+ if (ret)
+ goto err_unlock;
+
+ /* If alarm time is before current time, disable the alarm */
+ if (!alarm->enabled || alarm_secs <= rtc_secs) {
+ enable = 0;
+ } else {
+ /*
+ * Chip only support alarms up to one month in the future. Let's
+ * return an error if we get something after that limit.
+ * Comparison is done by incrementing rtc_tm month field by one
+ * and checking alarm value is still below.
+ */
+ if (rtc_tm.tm_mon == 11) { /* handle year wrapping */
+ rtc_tm.tm_mon = 0;
+ rtc_tm.tm_year += 1;
+ } else {
+ rtc_tm.tm_mon += 1;
+ }
+
+ ret = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (ret)
+ goto err_unlock;
+
+ if (alarm_secs > rtc_secs) {
+ dev_err(dev, "%s: max for alarm is one month (%d)\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ }
+
+ /* Disable the alarm before modifying it */
+ ret = _isl12057_rtc_update_alarm(dev, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to disable the alarm (%d)\n",
+ __func__, ret);
+ goto err_unlock;
+ }
+
+ /* Program alarm registers */
+ regs[0] = bin2bcd(alarm_tm->tm_sec) & 0x7f;
+ regs[1] = bin2bcd(alarm_tm->tm_min) & 0x7f;
+ regs[2] = bin2bcd(alarm_tm->tm_hour) & 0x3f;
+ regs[3] = bin2bcd(alarm_tm->tm_mday) & 0x3f;
+
+ ret = regmap_bulk_write(data->regmap, ISL12057_REG_A1_SC, regs,
+ ISL12057_A1_SEC_LEN);
+ if (ret < 0) {
+ dev_err(dev, "%s: writing alarm section failed (%d)\n",
+ __func__, ret);
+ goto err_unlock;
+ }
+
+ /* Enable or disable alarm */
+ ret = _isl12057_rtc_update_alarm(dev, enable);
+
+err_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+ u8 regs[ISL12057_RTC_SEC_LEN];
+ int ret;
+
+ ret = isl12057_rtc_tm_to_regs(regs, tm);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs,
+ ISL12057_RTC_SEC_LEN);
+ if (ret) {
+ dev_err(dev, "%s: unable to write RTC time section (%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ /*
+ * Now that RTC time has been updated, let's clear oscillator
+ * failure flag, if needed.
+ */
+ ret = regmap_update_bits(data->regmap, ISL12057_REG_SR,
+ ISL12057_REG_SR_OSF, 0);
+ if (ret < 0)
+ dev_err(dev, "%s: unable to clear osc. failure bit (%d)\n",
+ __func__, ret);
+
+out:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+/*
+ * Check current RTC status and enable/disable what needs to be. Return 0 if
+ * everything went ok and a negative value upon error. Note: this function
+ * is called early during init and hence does need mutex protection.
+ */
+static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap)
+{
+ int ret;
+
+ /* Enable oscillator if not already running */
+ ret = regmap_update_bits(regmap, ISL12057_REG_INT,
+ ISL12057_REG_INT_EOSC, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to enable oscillator (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* Clear alarm bit if needed */
+ ret = regmap_update_bits(regmap, ISL12057_REG_SR,
+ ISL12057_REG_SR_A1F, 0);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to clear alarm bit (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+/*
+ * One would expect the device to be marked as a wakeup source only
+ * when an IRQ pin of the RTC is routed to an interrupt line of the
+ * CPU. In practice, such an IRQ pin can be connected to a PMIC and
+ * this allows the device to be powered up when RTC alarm rings. This
+ * is for instance the case on ReadyNAS 102, 104 and 2120. On those
+ * devices with no IRQ driectly connected to the SoC, the RTC chip
+ * can be forced as a wakeup source by stating that explicitly in
+ * the device's .dts file using the "isil,irq2-can-wakeup-machine"
+ * boolean property. This will guarantee 'wakealarm' sysfs entry is
+ * available on the device.
+ *
+ * The function below returns 1, i.e. the capability of the chip to
+ * wakeup the device, based on IRQ availability or if the boolean
+ * property has been set in the .dts file. Otherwise, it returns 0.
+ */
+
+static bool isl12057_can_wakeup_machine(struct device *dev)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+
+ return (data->irq || of_property_read_bool(dev->of_node,
+ "isil,irq2-can-wakeup-machine"));
+}
+#else
+static bool isl12057_can_wakeup_machine(struct device *dev)
+{
+ struct isl12057_rtc_data *data = dev_get_drvdata(dev);
+
+ return !!data->irq;
+}
+#endif
+
+static int isl12057_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enable)
+{
+ struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
+ int ret = -ENOTTY;
+
+ if (rtc_data->irq)
+ ret = isl12057_rtc_update_alarm(dev, enable);
+
+ return ret;
+}
+
+static irqreturn_t isl12057_rtc_interrupt(int irq, void *data)
+{
+ struct i2c_client *client = data;
+ struct isl12057_rtc_data *rtc_data = dev_get_drvdata(&client->dev);
+ struct rtc_device *rtc = rtc_data->rtc;
+ int ret, handled = IRQ_NONE;
+ unsigned int sr;
+
+ ret = regmap_read(rtc_data->regmap, ISL12057_REG_SR, &sr);
+ if (!ret && (sr & ISL12057_REG_SR_A1F)) {
+ dev_dbg(&client->dev, "RTC alarm!\n");
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
+ /* Acknowledge and disable the alarm */
+ _isl12057_rtc_clear_alarm(&client->dev);
+ _isl12057_rtc_update_alarm(&client->dev, 0);
+
+ handled = IRQ_HANDLED;
+ }
+
+ return handled;
+}
+
+static const struct rtc_class_ops rtc_ops = {
+ .read_time = _isl12057_rtc_read_time,
+ .set_time = isl12057_rtc_set_time,
+ .read_alarm = isl12057_rtc_read_alarm,
+ .set_alarm = isl12057_rtc_set_alarm,
+ .alarm_irq_enable = isl12057_rtc_alarm_irq_enable,
+};
+
+static const struct regmap_config isl12057_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int isl12057_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct isl12057_rtc_data *data;
+ struct regmap *regmap;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -ENODEV;
+
+ regmap = devm_regmap_init_i2c(client, &isl12057_rtc_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ dev_err(dev, "%s: regmap allocation failed (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = isl12057_i2c_validate_chip(regmap);
+ if (ret)
+ return ret;
+
+ ret = isl12057_check_rtc_status(dev, regmap);
+ if (ret)
+ return ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ mutex_init(&data->lock);
+ data->regmap = regmap;
+ dev_set_drvdata(dev, data);
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(dev, client->irq, NULL,
+ isl12057_rtc_interrupt,
+ IRQF_SHARED|IRQF_ONESHOT,
+ DRV_NAME, client);
+ if (!ret)
+ data->irq = client->irq;
+ else
+ dev_err(dev, "%s: irq %d unavailable (%d)\n", __func__,
+ client->irq, ret);
+ }
+
+ if (isl12057_can_wakeup_machine(dev))
+ device_init_wakeup(dev, true);
+
+ data->rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops,
+ THIS_MODULE);
+ ret = PTR_ERR_OR_ZERO(data->rtc);
+ if (ret) {
+ dev_err(dev, "%s: unable to register RTC device (%d)\n",
+ __func__, ret);
+ goto err;
+ }
+
+ /* We cannot support UIE mode if we do not have an IRQ line */
+ if (!data->irq)
+ data->rtc->uie_unsupported = 1;
+
+err:
+ return ret;
+}
+
+static int isl12057_remove(struct i2c_client *client)
+{
+ if (isl12057_can_wakeup_machine(&client->dev))
+ device_init_wakeup(&client->dev, false);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int isl12057_rtc_suspend(struct device *dev)
+{
+ struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ if (rtc_data->irq && device_may_wakeup(dev))
+ return enable_irq_wake(rtc_data->irq);
+
+ return 0;
+}
+
+static int isl12057_rtc_resume(struct device *dev)
+{
+ struct isl12057_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ if (rtc_data->irq && device_may_wakeup(dev))
+ return disable_irq_wake(rtc_data->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(isl12057_rtc_pm_ops, isl12057_rtc_suspend,
+ isl12057_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id isl12057_dt_match[] = {
+ { .compatible = "isl,isl12057" }, /* for backward compat., don't use */
+ { .compatible = "isil,isl12057" },
+ { },
+};
+#endif
+
+static const struct i2c_device_id isl12057_id[] = {
+ { "isl12057", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl12057_id);
+
+static struct i2c_driver isl12057_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &isl12057_rtc_pm_ops,
+ .of_match_table = of_match_ptr(isl12057_dt_match),
+ },
+ .probe = isl12057_probe,
+ .remove = isl12057_remove,
+ .id_table = isl12057_id,
+};
+module_i2c_driver(isl12057_driver);
+
+MODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>");
+MODULE_DESCRIPTION("Intersil ISL12057 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
new file mode 100644
index 000000000..c3c549d51
--- /dev/null
+++ b/drivers/rtc/rtc-isl1208.c
@@ -0,0 +1,716 @@
+/*
+ * Intersil ISL1208 rtc class driver
+ *
+ * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
+#define DRV_VERSION "0.3"
+
+/* Register map */
+/* rtc section */
+#define ISL1208_REG_SC 0x00
+#define ISL1208_REG_MN 0x01
+#define ISL1208_REG_HR 0x02
+#define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */
+#define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
+#define ISL1208_REG_DT 0x03
+#define ISL1208_REG_MO 0x04
+#define ISL1208_REG_YR 0x05
+#define ISL1208_REG_DW 0x06
+#define ISL1208_RTC_SECTION_LEN 7
+
+/* control/status section */
+#define ISL1208_REG_SR 0x07
+#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */
+#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */
+#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */
+#define ISL1208_REG_SR_ALM (1<<2) /* alarm */
+#define ISL1208_REG_SR_BAT (1<<1) /* battery */
+#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
+#define ISL1208_REG_INT 0x08
+#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */
+#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */
+#define ISL1208_REG_09 0x09 /* reserved */
+#define ISL1208_REG_ATR 0x0a
+#define ISL1208_REG_DTR 0x0b
+
+/* alarm section */
+#define ISL1208_REG_SCA 0x0c
+#define ISL1208_REG_MNA 0x0d
+#define ISL1208_REG_HRA 0x0e
+#define ISL1208_REG_DTA 0x0f
+#define ISL1208_REG_MOA 0x10
+#define ISL1208_REG_DWA 0x11
+#define ISL1208_ALARM_SECTION_LEN 6
+
+/* user section */
+#define ISL1208_REG_USR1 0x12
+#define ISL1208_REG_USR2 0x13
+#define ISL1208_USR_SECTION_LEN 2
+
+static struct i2c_driver isl1208_driver;
+
+/* block read */
+static int
+isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
+ unsigned len)
+{
+ u8 reg_addr[1] = { reg };
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(reg_addr),
+ .buf = reg_addr
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf
+ }
+ };
+ int ret;
+
+ BUG_ON(reg > ISL1208_REG_USR2);
+ BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret > 0)
+ ret = 0;
+ return ret;
+}
+
+/* block write */
+static int
+isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
+ unsigned len)
+{
+ u8 i2c_buf[ISL1208_REG_USR2 + 2];
+ struct i2c_msg msgs[1] = {
+ {
+ .addr = client->addr,
+ .len = len + 1,
+ .buf = i2c_buf
+ }
+ };
+ int ret;
+
+ BUG_ON(reg > ISL1208_REG_USR2);
+ BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
+
+ i2c_buf[0] = reg;
+ memcpy(&i2c_buf[1], &buf[0], len);
+
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret > 0)
+ ret = 0;
+ return ret;
+}
+
+/* simple check to see whether we have a isl1208 */
+static int
+isl1208_i2c_validate_client(struct i2c_client *client)
+{
+ u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
+ u8 zero_mask[ISL1208_RTC_SECTION_LEN] = {
+ 0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8
+ };
+ int i;
+ int ret;
+
+ ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) {
+ if (regs[i] & zero_mask[i]) /* check if bits are cleared */
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int
+isl1208_i2c_get_sr(struct i2c_client *client)
+{
+ return i2c_smbus_read_byte_data(client, ISL1208_REG_SR);
+}
+
+static int
+isl1208_i2c_get_atr(struct i2c_client *client)
+{
+ int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR);
+ if (atr < 0)
+ return atr;
+
+ /* The 6bit value in the ATR register controls the load
+ * capacitance C_load * in steps of 0.25pF
+ *
+ * bit (1<<5) of the ATR register is inverted
+ *
+ * C_load(ATR=0x20) = 4.50pF
+ * C_load(ATR=0x00) = 12.50pF
+ * C_load(ATR=0x1f) = 20.25pF
+ *
+ */
+
+ atr &= 0x3f; /* mask out lsb */
+ atr ^= 1 << 5; /* invert 6th bit */
+ atr += 2 * 9; /* add offset of 4.5pF; unit[atr] = 0.25pF */
+
+ return atr;
+}
+
+static int
+isl1208_i2c_get_dtr(struct i2c_client *client)
+{
+ int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR);
+ if (dtr < 0)
+ return -EIO;
+
+ /* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
+ dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1);
+
+ return dtr;
+}
+
+static int
+isl1208_i2c_get_usr(struct i2c_client *client)
+{
+ u8 buf[ISL1208_USR_SECTION_LEN] = { 0, };
+ int ret;
+
+ ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1, buf,
+ ISL1208_USR_SECTION_LEN);
+ if (ret < 0)
+ return ret;
+
+ return (buf[1] << 8) | buf[0];
+}
+
+static int
+isl1208_i2c_set_usr(struct i2c_client *client, u16 usr)
+{
+ u8 buf[ISL1208_USR_SECTION_LEN];
+
+ buf[0] = usr & 0xff;
+ buf[1] = (usr >> 8) & 0xff;
+
+ return isl1208_i2c_set_regs(client, ISL1208_REG_USR1, buf,
+ ISL1208_USR_SECTION_LEN);
+}
+
+static int
+isl1208_rtc_toggle_alarm(struct i2c_client *client, int enable)
+{
+ int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
+
+ if (icr < 0) {
+ dev_err(&client->dev, "%s: reading INT failed\n", __func__);
+ return icr;
+ }
+
+ if (enable)
+ icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM;
+ else
+ icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM);
+
+ icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr);
+ if (icr < 0) {
+ dev_err(&client->dev, "%s: writing INT failed\n", __func__);
+ return icr;
+ }
+
+ return 0;
+}
+
+static int
+isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct i2c_client *const client = to_i2c_client(dev);
+ int sr, dtr, atr, usr;
+
+ sr = isl1208_i2c_get_sr(client);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return sr;
+ }
+
+ seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n",
+ (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "",
+ (sr & ISL1208_REG_SR_BAT) ? " BAT" : "",
+ (sr & ISL1208_REG_SR_ALM) ? " ALM" : "",
+ (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "",
+ (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "",
+ (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", sr);
+
+ seq_printf(seq, "batt_status\t: %s\n",
+ (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay");
+
+ dtr = isl1208_i2c_get_dtr(client);
+ if (dtr >= 0 - 1)
+ seq_printf(seq, "digital_trim\t: %d ppm\n", dtr);
+
+ atr = isl1208_i2c_get_atr(client);
+ if (atr >= 0)
+ seq_printf(seq, "analog_trim\t: %d.%.2d pF\n",
+ atr >> 2, (atr & 0x3) * 25);
+
+ usr = isl1208_i2c_get_usr(client);
+ if (usr >= 0)
+ seq_printf(seq, "user_data\t: 0x%.4x\n", usr);
+
+ return 0;
+}
+
+static int
+isl1208_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+{
+ int sr;
+ u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
+
+ sr = isl1208_i2c_get_sr(client);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+
+ sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading RTC section failed\n",
+ __func__);
+ return sr;
+ }
+
+ tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]);
+ tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]);
+
+ /* HR field has a more complex interpretation */
+ {
+ const u8 _hr = regs[ISL1208_REG_HR];
+ if (_hr & ISL1208_REG_HR_MIL) /* 24h format */
+ tm->tm_hour = bcd2bin(_hr & 0x3f);
+ else {
+ /* 12h format */
+ tm->tm_hour = bcd2bin(_hr & 0x1f);
+ if (_hr & ISL1208_REG_HR_PM) /* PM flag set */
+ tm->tm_hour += 12;
+ }
+ }
+
+ tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]);
+ tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */
+ tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 100;
+ tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]);
+
+ return 0;
+}
+
+static int
+isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
+{
+ struct rtc_time *const tm = &alarm->time;
+ u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
+ int icr, yr, sr = isl1208_i2c_get_sr(client);
+
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return sr;
+ }
+
+ sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs,
+ ISL1208_ALARM_SECTION_LEN);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading alarm section failed\n",
+ __func__);
+ return sr;
+ }
+
+ /* MSB of each alarm register is an enable bit */
+ tm->tm_sec = bcd2bin(regs[ISL1208_REG_SCA - ISL1208_REG_SCA] & 0x7f);
+ tm->tm_min = bcd2bin(regs[ISL1208_REG_MNA - ISL1208_REG_SCA] & 0x7f);
+ tm->tm_hour = bcd2bin(regs[ISL1208_REG_HRA - ISL1208_REG_SCA] & 0x3f);
+ tm->tm_mday = bcd2bin(regs[ISL1208_REG_DTA - ISL1208_REG_SCA] & 0x3f);
+ tm->tm_mon =
+ bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1;
+ tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03);
+
+ /* The alarm doesn't store the year so get it from the rtc section */
+ yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR);
+ if (yr < 0) {
+ dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__);
+ return yr;
+ }
+ tm->tm_year = bcd2bin(yr) + 100;
+
+ icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);
+ if (icr < 0) {
+ dev_err(&client->dev, "%s: reading INT failed\n", __func__);
+ return icr;
+ }
+ alarm->enabled = !!(icr & ISL1208_REG_INT_ALME);
+
+ return 0;
+}
+
+static int
+isl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
+{
+ struct rtc_time *alarm_tm = &alarm->time;
+ u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
+ const int offs = ISL1208_REG_SCA;
+ unsigned long rtc_secs, alarm_secs;
+ struct rtc_time rtc_tm;
+ int err, enable;
+
+ err = isl1208_i2c_read_time(client, &rtc_tm);
+ if (err)
+ return err;
+ err = rtc_tm_to_time(&rtc_tm, &rtc_secs);
+ if (err)
+ return err;
+ err = rtc_tm_to_time(alarm_tm, &alarm_secs);
+ if (err)
+ return err;
+
+ /* If the alarm time is before the current time disable the alarm */
+ if (!alarm->enabled || alarm_secs <= rtc_secs)
+ enable = 0x00;
+ else
+ enable = 0x80;
+
+ /* Program the alarm and enable it for each setting */
+ regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable;
+ regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable;
+ regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) |
+ ISL1208_REG_HR_MIL | enable;
+
+ regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable;
+ regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable;
+ regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable;
+
+ /* write ALARM registers */
+ err = isl1208_i2c_set_regs(client, offs, regs,
+ ISL1208_ALARM_SECTION_LEN);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: writing ALARM section failed\n",
+ __func__);
+ return err;
+ }
+
+ err = isl1208_rtc_toggle_alarm(client, enable);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int
+isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl1208_i2c_read_time(to_i2c_client(dev), tm);
+}
+
+static int
+isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
+{
+ int sr;
+ u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
+
+ /* The clock has an 8 bit wide bcd-coded register (they never learn)
+ * for the year. tm_year is an offset from 1900 and we are interested
+ * in the 2000-2099 range, so any value less than 100 is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ regs[ISL1208_REG_SC] = bin2bcd(tm->tm_sec);
+ regs[ISL1208_REG_MN] = bin2bcd(tm->tm_min);
+ regs[ISL1208_REG_HR] = bin2bcd(tm->tm_hour) | ISL1208_REG_HR_MIL;
+
+ regs[ISL1208_REG_DT] = bin2bcd(tm->tm_mday);
+ regs[ISL1208_REG_MO] = bin2bcd(tm->tm_mon + 1);
+ regs[ISL1208_REG_YR] = bin2bcd(tm->tm_year - 100);
+
+ regs[ISL1208_REG_DW] = bin2bcd(tm->tm_wday & 7);
+
+ sr = isl1208_i2c_get_sr(client);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return sr;
+ }
+
+ /* set WRTC */
+ sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
+ sr | ISL1208_REG_SR_WRTC);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: writing SR failed\n", __func__);
+ return sr;
+ }
+
+ /* write RTC registers */
+ sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: writing RTC section failed\n",
+ __func__);
+ return sr;
+ }
+
+ /* clear WRTC again */
+ sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR,
+ sr & ~ISL1208_REG_SR_WRTC);
+ if (sr < 0) {
+ dev_err(&client->dev, "%s: writing SR failed\n", __func__);
+ return sr;
+ }
+
+ return 0;
+}
+
+
+static int
+isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return isl1208_i2c_set_time(to_i2c_client(dev), tm);
+}
+
+static int
+isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
+}
+
+static int
+isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm);
+}
+
+static irqreturn_t
+isl1208_rtc_interrupt(int irq, void *data)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
+ struct i2c_client *client = data;
+ struct rtc_device *rtc = i2c_get_clientdata(client);
+ int handled = 0, sr, err;
+
+ /*
+ * I2C reads get NAK'ed if we read straight away after an interrupt?
+ * Using a mdelay/msleep didn't seem to help either, so we work around
+ * this by continually trying to read the register for a short time.
+ */
+ while (1) {
+ sr = isl1208_i2c_get_sr(client);
+ if (sr >= 0)
+ break;
+
+ if (time_after(jiffies, timeout)) {
+ dev_err(&client->dev, "%s: reading SR failed\n",
+ __func__);
+ return sr;
+ }
+ }
+
+ if (sr & ISL1208_REG_SR_ALM) {
+ dev_dbg(&client->dev, "alarm!\n");
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
+ /* Clear the alarm */
+ sr &= ~ISL1208_REG_SR_ALM;
+ sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
+ if (sr < 0)
+ dev_err(&client->dev, "%s: writing SR failed\n",
+ __func__);
+ else
+ handled = 1;
+
+ /* Disable the alarm */
+ err = isl1208_rtc_toggle_alarm(client, 0);
+ if (err)
+ return err;
+ }
+
+ return handled ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static const struct rtc_class_ops isl1208_rtc_ops = {
+ .proc = isl1208_rtc_proc,
+ .read_time = isl1208_rtc_read_time,
+ .set_time = isl1208_rtc_set_time,
+ .read_alarm = isl1208_rtc_read_alarm,
+ .set_alarm = isl1208_rtc_set_alarm,
+};
+
+/* sysfs interface */
+
+static ssize_t
+isl1208_sysfs_show_atrim(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int atr = isl1208_i2c_get_atr(to_i2c_client(dev));
+ if (atr < 0)
+ return atr;
+
+ return sprintf(buf, "%d.%.2d pF\n", atr >> 2, (atr & 0x3) * 25);
+}
+
+static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL);
+
+static ssize_t
+isl1208_sysfs_show_dtrim(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int dtr = isl1208_i2c_get_dtr(to_i2c_client(dev));
+ if (dtr < 0)
+ return dtr;
+
+ return sprintf(buf, "%d ppm\n", dtr);
+}
+
+static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
+
+static ssize_t
+isl1208_sysfs_show_usr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int usr = isl1208_i2c_get_usr(to_i2c_client(dev));
+ if (usr < 0)
+ return usr;
+
+ return sprintf(buf, "0x%.4x\n", usr);
+}
+
+static ssize_t
+isl1208_sysfs_store_usr(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int usr = -1;
+
+ if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {
+ if (sscanf(buf, "%x", &usr) != 1)
+ return -EINVAL;
+ } else {
+ if (sscanf(buf, "%d", &usr) != 1)
+ return -EINVAL;
+ }
+
+ if (usr < 0 || usr > 0xffff)
+ return -EINVAL;
+
+ return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count;
+}
+
+static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
+ isl1208_sysfs_store_usr);
+
+static struct attribute *isl1208_rtc_attrs[] = {
+ &dev_attr_atrim.attr,
+ &dev_attr_dtrim.attr,
+ &dev_attr_usr.attr,
+ NULL
+};
+
+static const struct attribute_group isl1208_rtc_sysfs_files = {
+ .attrs = isl1208_rtc_attrs,
+};
+
+static int
+isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct rtc_device *rtc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ if (isl1208_i2c_validate_client(client) < 0)
+ return -ENODEV;
+
+ dev_info(&client->dev,
+ "chip found, driver version " DRV_VERSION "\n");
+
+ if (client->irq > 0) {
+ rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ isl1208_rtc_interrupt,
+ IRQF_SHARED,
+ isl1208_driver.driver.name,
+ client);
+ if (!rc) {
+ device_init_wakeup(&client->dev, 1);
+ enable_irq_wake(client->irq);
+ } else {
+ dev_err(&client->dev,
+ "Unable to request irq %d, no alarm support\n",
+ client->irq);
+ client->irq = 0;
+ }
+ }
+
+ rtc = devm_rtc_device_register(&client->dev, isl1208_driver.driver.name,
+ &isl1208_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ rc = isl1208_i2c_get_sr(client);
+ if (rc < 0) {
+ dev_err(&client->dev, "reading status failed\n");
+ return rc;
+ }
+
+ if (rc & ISL1208_REG_SR_RTCF)
+ dev_warn(&client->dev, "rtc power failure detected, "
+ "please set clock.\n");
+
+ rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int
+isl1208_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files);
+
+ return 0;
+}
+
+static const struct i2c_device_id isl1208_id[] = {
+ { "isl1208", 0 },
+ { "isl1218", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, isl1208_id);
+
+static struct i2c_driver isl1208_driver = {
+ .driver = {
+ .name = "rtc-isl1208",
+ },
+ .probe = isl1208_probe,
+ .remove = isl1208_remove,
+ .id_table = isl1208_id,
+};
+
+module_i2c_driver(isl1208_driver);
+
+MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
+MODULE_DESCRIPTION("Intersil ISL1208 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
new file mode 100644
index 000000000..b2bcfc0bf
--- /dev/null
+++ b/drivers/rtc/rtc-jz4740.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
+ * JZ4740 SoC RTC driver
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#define JZ_REG_RTC_CTRL 0x00
+#define JZ_REG_RTC_SEC 0x04
+#define JZ_REG_RTC_SEC_ALARM 0x08
+#define JZ_REG_RTC_REGULATOR 0x0C
+#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_SCRATCHPAD 0x34
+
+#define JZ_RTC_CTRL_WRDY BIT(7)
+#define JZ_RTC_CTRL_1HZ BIT(6)
+#define JZ_RTC_CTRL_1HZ_IRQ BIT(5)
+#define JZ_RTC_CTRL_AF BIT(4)
+#define JZ_RTC_CTRL_AF_IRQ BIT(3)
+#define JZ_RTC_CTRL_AE BIT(2)
+#define JZ_RTC_CTRL_ENABLE BIT(0)
+
+struct jz4740_rtc {
+ void __iomem *base;
+
+ struct rtc_device *rtc;
+
+ int irq;
+
+ spinlock_t lock;
+};
+
+static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
+{
+ return readl(rtc->base + reg);
+}
+
+static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
+{
+ uint32_t ctrl;
+ int timeout = 1000;
+
+ do {
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+ } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout);
+
+ return timeout ? 0 : -EIO;
+}
+
+static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
+ uint32_t val)
+{
+ int ret;
+ ret = jz4740_rtc_wait_write_ready(rtc);
+ if (ret == 0)
+ writel(val, rtc->base + reg);
+
+ return ret;
+}
+
+static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask,
+ bool set)
+{
+ int ret;
+ unsigned long flags;
+ uint32_t ctrl;
+
+ spin_lock_irqsave(&rtc->lock, flags);
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ /* Don't clear interrupt flags by accident */
+ ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF;
+
+ if (set)
+ ctrl |= mask;
+ else
+ ctrl &= ~mask;
+
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl);
+
+ spin_unlock_irqrestore(&rtc->lock, flags);
+
+ return ret;
+}
+
+static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ uint32_t secs, secs2;
+ int timeout = 5;
+
+ /* If the seconds register is read while it is updated, it can contain a
+ * bogus value. This can be avoided by making sure that two consecutive
+ * reads have the same value.
+ */
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+
+ while (secs != secs2 && --timeout) {
+ secs = secs2;
+ secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC);
+ }
+
+ if (timeout == 0)
+ return -EIO;
+
+ rtc_time_to_tm(secs, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+
+ return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs);
+}
+
+static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ uint32_t secs;
+ uint32_t ctrl;
+
+ secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM);
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE);
+ alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF);
+
+ rtc_time_to_tm(secs, &alrm->time);
+
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long secs;
+
+ rtc_tm_to_time(&alrm->time, &secs);
+
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs);
+ if (!ret)
+ ret = jz4740_rtc_ctrl_set_bits(rtc,
+ JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, alrm->enabled);
+
+ return ret;
+}
+
+static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable);
+}
+
+static struct rtc_class_ops jz4740_rtc_ops = {
+ .read_time = jz4740_rtc_read_time,
+ .set_mmss = jz4740_rtc_set_mmss,
+ .read_alarm = jz4740_rtc_read_alarm,
+ .set_alarm = jz4740_rtc_set_alarm,
+ .alarm_irq_enable = jz4740_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t jz4740_rtc_irq(int irq, void *data)
+{
+ struct jz4740_rtc *rtc = data;
+ uint32_t ctrl;
+ unsigned long events = 0;
+
+ ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
+
+ if (ctrl & JZ_RTC_CTRL_1HZ)
+ events |= (RTC_UF | RTC_IRQF);
+
+ if (ctrl & JZ_RTC_CTRL_AF)
+ events |= (RTC_AF | RTC_IRQF);
+
+ rtc_update_irq(rtc->rtc, 1, events);
+
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false);
+
+ return IRQ_HANDLED;
+}
+
+void jz4740_rtc_poweroff(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+ jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1);
+}
+EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff);
+
+static int jz4740_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct jz4740_rtc *rtc;
+ uint32_t scratchpad;
+ struct resource *mem;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->irq = platform_get_irq(pdev, 0);
+ if (rtc->irq < 0) {
+ dev_err(&pdev->dev, "Failed to get platform irq\n");
+ return -ENOENT;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(rtc->base))
+ return PTR_ERR(rtc->base);
+
+ spin_lock_init(&rtc->lock);
+
+ platform_set_drvdata(pdev, rtc);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &jz4740_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, rtc->irq, jz4740_rtc_irq, 0,
+ pdev->name, rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret);
+ return ret;
+ }
+
+ scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD);
+ if (scratchpad != 0x12345678) {
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
+ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not write write to RTC registers\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int jz4740_rtc_suspend(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->irq);
+ return 0;
+}
+
+static int jz4740_rtc_resume(struct device *dev)
+{
+ struct jz4740_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq);
+ return 0;
+}
+
+static const struct dev_pm_ops jz4740_pm_ops = {
+ .suspend = jz4740_rtc_suspend,
+ .resume = jz4740_rtc_resume,
+};
+#define JZ4740_RTC_PM_OPS (&jz4740_pm_ops)
+
+#else
+#define JZ4740_RTC_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver jz4740_rtc_driver = {
+ .probe = jz4740_rtc_probe,
+ .driver = {
+ .name = "jz4740-rtc",
+ .pm = JZ4740_RTC_PM_OPS,
+ },
+};
+
+module_platform_driver(jz4740_rtc_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
+MODULE_ALIAS("platform:jz4740-rtc");
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
new file mode 100644
index 000000000..e6bfb9c42
--- /dev/null
+++ b/drivers/rtc/rtc-lib.c
@@ -0,0 +1,152 @@
+/*
+ * rtc and date/time utility functions
+ *
+ * Copyright (C) 2005-06 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c and other bits
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+static const unsigned char rtc_days_in_month[] = {
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static const unsigned short rtc_ydays[2][13] = {
+ /* Normal years */
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
+ /* Leap years */
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
+};
+
+#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
+
+/*
+ * The number of days in the month.
+ */
+int rtc_month_days(unsigned int month, unsigned int year)
+{
+ return rtc_days_in_month[month] + (is_leap_year(year) && month == 1);
+}
+EXPORT_SYMBOL(rtc_month_days);
+
+/*
+ * The number of days since January 1. (0 to 365)
+ */
+int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
+{
+ return rtc_ydays[is_leap_year(year)][month] + day-1;
+}
+EXPORT_SYMBOL(rtc_year_days);
+
+
+/*
+ * rtc_time_to_tm64 - Converts time64_t to rtc_time.
+ * Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
+ */
+void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
+{
+ unsigned int month, year;
+ unsigned long secs;
+ int days;
+
+ /* time must be positive */
+ days = div_s64(time, 86400);
+ secs = time - (unsigned int) days * 86400;
+
+ /* day of the week, 1970-01-01 was a Thursday */
+ tm->tm_wday = (days + 4) % 7;
+
+ year = 1970 + days / 365;
+ days -= (year - 1970) * 365
+ + LEAPS_THRU_END_OF(year - 1)
+ - LEAPS_THRU_END_OF(1970 - 1);
+ if (days < 0) {
+ year -= 1;
+ days += 365 + is_leap_year(year);
+ }
+ tm->tm_year = year - 1900;
+ tm->tm_yday = days + 1;
+
+ for (month = 0; month < 11; month++) {
+ int newdays;
+
+ newdays = days - rtc_month_days(month, year);
+ if (newdays < 0)
+ break;
+ days = newdays;
+ }
+ tm->tm_mon = month;
+ tm->tm_mday = days + 1;
+
+ tm->tm_hour = secs / 3600;
+ secs -= tm->tm_hour * 3600;
+ tm->tm_min = secs / 60;
+ tm->tm_sec = secs - tm->tm_min * 60;
+
+ tm->tm_isdst = 0;
+}
+EXPORT_SYMBOL(rtc_time64_to_tm);
+
+/*
+ * Does the rtc_time represent a valid date/time?
+ */
+int rtc_valid_tm(struct rtc_time *tm)
+{
+ if (tm->tm_year < 70
+ || ((unsigned)tm->tm_mon) >= 12
+ || tm->tm_mday < 1
+ || tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
+ || ((unsigned)tm->tm_hour) >= 24
+ || ((unsigned)tm->tm_min) >= 60
+ || ((unsigned)tm->tm_sec) >= 60)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(rtc_valid_tm);
+
+/*
+ * rtc_tm_to_time64 - Converts rtc_time to time64_t.
+ * Convert Gregorian date to seconds since 01-01-1970 00:00:00.
+ */
+time64_t rtc_tm_to_time64(struct rtc_time *tm)
+{
+ return mktime64(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+EXPORT_SYMBOL(rtc_tm_to_time64);
+
+/*
+ * Convert rtc_time to ktime
+ */
+ktime_t rtc_tm_to_ktime(struct rtc_time tm)
+{
+ return ktime_set(rtc_tm_to_time64(&tm), 0);
+}
+EXPORT_SYMBOL_GPL(rtc_tm_to_ktime);
+
+/*
+ * Convert ktime to rtc_time
+ */
+struct rtc_time rtc_ktime_to_tm(ktime_t kt)
+{
+ struct timespec64 ts;
+ struct rtc_time ret;
+
+ ts = ktime_to_timespec64(kt);
+ /* Round up any ns */
+ if (ts.tv_nsec)
+ ts.tv_sec++;
+ rtc_time64_to_tm(ts.tv_sec, &ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rtc_ktime_to_tm);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-lp8788.c b/drivers/rtc/rtc-lp8788.c
new file mode 100644
index 000000000..e20e7bd82
--- /dev/null
+++ b/drivers/rtc/rtc-lp8788.c
@@ -0,0 +1,326 @@
+/*
+ * TI LP8788 MFD - rtc driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/irqdomain.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+/* register address */
+#define LP8788_INTEN_3 0x05
+#define LP8788_RTC_UNLOCK 0x64
+#define LP8788_RTC_SEC 0x70
+#define LP8788_ALM1_SEC 0x77
+#define LP8788_ALM1_EN 0x7D
+#define LP8788_ALM2_SEC 0x7E
+#define LP8788_ALM2_EN 0x84
+
+/* mask/shift bits */
+#define LP8788_INT_RTC_ALM1_M BIT(1) /* Addr 05h */
+#define LP8788_INT_RTC_ALM1_S 1
+#define LP8788_INT_RTC_ALM2_M BIT(2) /* Addr 05h */
+#define LP8788_INT_RTC_ALM2_S 2
+#define LP8788_ALM_EN_M BIT(7) /* Addr 7Dh or 84h */
+#define LP8788_ALM_EN_S 7
+
+#define DEFAULT_ALARM_SEL LP8788_ALARM_1
+#define LP8788_MONTH_OFFSET 1
+#define LP8788_BASE_YEAR 2000
+#define MAX_WDAY_BITS 7
+#define LP8788_WDAY_SET 1
+#define RTC_UNLOCK 0x1
+#define RTC_LATCH 0x2
+#define ALARM_IRQ_FLAG (RTC_IRQF | RTC_AF)
+
+enum lp8788_time {
+ LPTIME_SEC,
+ LPTIME_MIN,
+ LPTIME_HOUR,
+ LPTIME_MDAY,
+ LPTIME_MON,
+ LPTIME_YEAR,
+ LPTIME_WDAY,
+ LPTIME_MAX,
+};
+
+struct lp8788_rtc {
+ struct lp8788 *lp;
+ struct rtc_device *rdev;
+ enum lp8788_alarm_sel alarm;
+ int irq;
+};
+
+static const u8 addr_alarm_sec[LP8788_ALARM_MAX] = {
+ LP8788_ALM1_SEC,
+ LP8788_ALM2_SEC,
+};
+
+static const u8 addr_alarm_en[LP8788_ALARM_MAX] = {
+ LP8788_ALM1_EN,
+ LP8788_ALM2_EN,
+};
+
+static const u8 mask_alarm_en[LP8788_ALARM_MAX] = {
+ LP8788_INT_RTC_ALM1_M,
+ LP8788_INT_RTC_ALM2_M,
+};
+
+static const u8 shift_alarm_en[LP8788_ALARM_MAX] = {
+ LP8788_INT_RTC_ALM1_S,
+ LP8788_INT_RTC_ALM2_S,
+};
+
+static int _to_tm_wday(u8 lp8788_wday)
+{
+ int i;
+
+ if (lp8788_wday == 0)
+ return 0;
+
+ /* lookup defined weekday from read register value */
+ for (i = 0; i < MAX_WDAY_BITS; i++) {
+ if ((lp8788_wday >> i) == LP8788_WDAY_SET)
+ break;
+ }
+
+ return i + 1;
+}
+
+static inline int _to_lp8788_wday(int tm_wday)
+{
+ return LP8788_WDAY_SET << (tm_wday - 1);
+}
+
+static void lp8788_rtc_unlock(struct lp8788 *lp)
+{
+ lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_UNLOCK);
+ lp8788_write_byte(lp, LP8788_RTC_UNLOCK, RTC_LATCH);
+}
+
+static int lp8788_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct lp8788_rtc *rtc = dev_get_drvdata(dev);
+ struct lp8788 *lp = rtc->lp;
+ u8 data[LPTIME_MAX];
+ int ret;
+
+ lp8788_rtc_unlock(lp);
+
+ ret = lp8788_read_multi_bytes(lp, LP8788_RTC_SEC, data, LPTIME_MAX);
+ if (ret)
+ return ret;
+
+ tm->tm_sec = data[LPTIME_SEC];
+ tm->tm_min = data[LPTIME_MIN];
+ tm->tm_hour = data[LPTIME_HOUR];
+ tm->tm_mday = data[LPTIME_MDAY];
+ tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET;
+ tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900;
+ tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]);
+
+ return 0;
+}
+
+static int lp8788_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct lp8788_rtc *rtc = dev_get_drvdata(dev);
+ struct lp8788 *lp = rtc->lp;
+ u8 data[LPTIME_MAX - 1];
+ int ret, i, year;
+
+ year = tm->tm_year + 1900 - LP8788_BASE_YEAR;
+ if (year < 0) {
+ dev_err(lp->dev, "invalid year: %d\n", year);
+ return -EINVAL;
+ }
+
+ /* because rtc weekday is a readonly register, do not update */
+ data[LPTIME_SEC] = tm->tm_sec;
+ data[LPTIME_MIN] = tm->tm_min;
+ data[LPTIME_HOUR] = tm->tm_hour;
+ data[LPTIME_MDAY] = tm->tm_mday;
+ data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET;
+ data[LPTIME_YEAR] = year;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ ret = lp8788_write_byte(lp, LP8788_RTC_SEC + i, data[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lp8788_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct lp8788_rtc *rtc = dev_get_drvdata(dev);
+ struct lp8788 *lp = rtc->lp;
+ struct rtc_time *tm = &alarm->time;
+ u8 addr, data[LPTIME_MAX];
+ int ret;
+
+ addr = addr_alarm_sec[rtc->alarm];
+ ret = lp8788_read_multi_bytes(lp, addr, data, LPTIME_MAX);
+ if (ret)
+ return ret;
+
+ tm->tm_sec = data[LPTIME_SEC];
+ tm->tm_min = data[LPTIME_MIN];
+ tm->tm_hour = data[LPTIME_HOUR];
+ tm->tm_mday = data[LPTIME_MDAY];
+ tm->tm_mon = data[LPTIME_MON] - LP8788_MONTH_OFFSET;
+ tm->tm_year = data[LPTIME_YEAR] + LP8788_BASE_YEAR - 1900;
+ tm->tm_wday = _to_tm_wday(data[LPTIME_WDAY]);
+ alarm->enabled = data[LPTIME_WDAY] & LP8788_ALM_EN_M;
+
+ return 0;
+}
+
+static int lp8788_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct lp8788_rtc *rtc = dev_get_drvdata(dev);
+ struct lp8788 *lp = rtc->lp;
+ struct rtc_time *tm = &alarm->time;
+ u8 addr, data[LPTIME_MAX];
+ int ret, i, year;
+
+ year = tm->tm_year + 1900 - LP8788_BASE_YEAR;
+ if (year < 0) {
+ dev_err(lp->dev, "invalid year: %d\n", year);
+ return -EINVAL;
+ }
+
+ data[LPTIME_SEC] = tm->tm_sec;
+ data[LPTIME_MIN] = tm->tm_min;
+ data[LPTIME_HOUR] = tm->tm_hour;
+ data[LPTIME_MDAY] = tm->tm_mday;
+ data[LPTIME_MON] = tm->tm_mon + LP8788_MONTH_OFFSET;
+ data[LPTIME_YEAR] = year;
+ data[LPTIME_WDAY] = _to_lp8788_wday(tm->tm_wday);
+
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ addr = addr_alarm_sec[rtc->alarm] + i;
+ ret = lp8788_write_byte(lp, addr, data[i]);
+ if (ret)
+ return ret;
+ }
+
+ alarm->enabled = 1;
+ addr = addr_alarm_en[rtc->alarm];
+
+ return lp8788_update_bits(lp, addr, LP8788_ALM_EN_M,
+ alarm->enabled << LP8788_ALM_EN_S);
+}
+
+static int lp8788_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct lp8788_rtc *rtc = dev_get_drvdata(dev);
+ struct lp8788 *lp = rtc->lp;
+ u8 mask, shift;
+
+ if (!rtc->irq)
+ return -EIO;
+
+ mask = mask_alarm_en[rtc->alarm];
+ shift = shift_alarm_en[rtc->alarm];
+
+ return lp8788_update_bits(lp, LP8788_INTEN_3, mask, enable << shift);
+}
+
+static const struct rtc_class_ops lp8788_rtc_ops = {
+ .read_time = lp8788_rtc_read_time,
+ .set_time = lp8788_rtc_set_time,
+ .read_alarm = lp8788_read_alarm,
+ .set_alarm = lp8788_set_alarm,
+ .alarm_irq_enable = lp8788_alarm_irq_enable,
+};
+
+static irqreturn_t lp8788_alarm_irq_handler(int irq, void *ptr)
+{
+ struct lp8788_rtc *rtc = ptr;
+
+ rtc_update_irq(rtc->rdev, 1, ALARM_IRQ_FLAG);
+ return IRQ_HANDLED;
+}
+
+static int lp8788_alarm_irq_register(struct platform_device *pdev,
+ struct lp8788_rtc *rtc)
+{
+ struct resource *r;
+ struct lp8788 *lp = rtc->lp;
+ struct irq_domain *irqdm = lp->irqdm;
+ int irq;
+
+ rtc->irq = 0;
+
+ /* even the alarm IRQ number is not specified, rtc time should work */
+ r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, LP8788_ALM_IRQ);
+ if (!r)
+ return 0;
+
+ if (rtc->alarm == LP8788_ALARM_1)
+ irq = r->start;
+ else
+ irq = r->end;
+
+ rtc->irq = irq_create_mapping(irqdm, irq);
+
+ return devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+ lp8788_alarm_irq_handler,
+ 0, LP8788_ALM_IRQ, rtc);
+}
+
+static int lp8788_rtc_probe(struct platform_device *pdev)
+{
+ struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+ struct lp8788_rtc *rtc;
+ struct device *dev = &pdev->dev;
+
+ rtc = devm_kzalloc(dev, sizeof(struct lp8788_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->lp = lp;
+ rtc->alarm = lp->pdata ? lp->pdata->alarm_sel : DEFAULT_ALARM_SEL;
+ platform_set_drvdata(pdev, rtc);
+
+ device_init_wakeup(dev, 1);
+
+ rtc->rdev = devm_rtc_device_register(dev, "lp8788_rtc",
+ &lp8788_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rdev)) {
+ dev_err(dev, "can not register rtc device\n");
+ return PTR_ERR(rtc->rdev);
+ }
+
+ if (lp8788_alarm_irq_register(pdev, rtc))
+ dev_warn(lp->dev, "no rtc irq handler\n");
+
+ return 0;
+}
+
+static struct platform_driver lp8788_rtc_driver = {
+ .probe = lp8788_rtc_probe,
+ .driver = {
+ .name = LP8788_DEV_RTC,
+ },
+};
+module_platform_driver(lp8788_rtc_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 RTC Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-rtc");
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
new file mode 100644
index 000000000..f923f7324
--- /dev/null
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+/*
+ * Clock and Power control register offsets
+ */
+#define LPC32XX_RTC_UCOUNT 0x00
+#define LPC32XX_RTC_DCOUNT 0x04
+#define LPC32XX_RTC_MATCH0 0x08
+#define LPC32XX_RTC_MATCH1 0x0C
+#define LPC32XX_RTC_CTRL 0x10
+#define LPC32XX_RTC_INTSTAT 0x14
+#define LPC32XX_RTC_KEY 0x18
+#define LPC32XX_RTC_SRAM 0x80
+
+#define LPC32XX_RTC_CTRL_MATCH0 (1 << 0)
+#define LPC32XX_RTC_CTRL_MATCH1 (1 << 1)
+#define LPC32XX_RTC_CTRL_ONSW_MATCH0 (1 << 2)
+#define LPC32XX_RTC_CTRL_ONSW_MATCH1 (1 << 3)
+#define LPC32XX_RTC_CTRL_SW_RESET (1 << 4)
+#define LPC32XX_RTC_CTRL_CNTR_DIS (1 << 6)
+#define LPC32XX_RTC_CTRL_ONSW_FORCE_HI (1 << 7)
+
+#define LPC32XX_RTC_INTSTAT_MATCH0 (1 << 0)
+#define LPC32XX_RTC_INTSTAT_MATCH1 (1 << 1)
+#define LPC32XX_RTC_INTSTAT_ONSW (1 << 2)
+
+#define LPC32XX_RTC_KEY_ONSW_LOADVAL 0xB5C13F27
+
+#define RTC_NAME "rtc-lpc32xx"
+
+#define rtc_readl(dev, reg) \
+ __raw_readl((dev)->rtc_base + (reg))
+#define rtc_writel(dev, reg, val) \
+ __raw_writel((val), (dev)->rtc_base + (reg))
+
+struct lpc32xx_rtc {
+ void __iomem *rtc_base;
+ int irq;
+ unsigned char alarm_enabled;
+ struct rtc_device *rtc;
+ spinlock_t lock;
+};
+
+static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long elapsed_sec;
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
+ rtc_time_to_tm(elapsed_sec, time);
+
+ return rtc_valid_tm(time);
+}
+
+static int lpc32xx_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* RTC must be disabled during count update */
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS);
+ rtc_writel(rtc, LPC32XX_RTC_UCOUNT, secs);
+ rtc_writel(rtc, LPC32XX_RTC_DCOUNT, 0xFFFFFFFF - secs);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time);
+ wkalrm->enabled = rtc->alarm_enabled;
+ wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) &
+ LPC32XX_RTC_INTSTAT_MATCH0);
+
+ return rtc_valid_tm(&wkalrm->time);
+}
+
+static int lpc32xx_rtc_set_alarm(struct device *dev,
+ struct rtc_wkalrm *wkalrm)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long alarmsecs;
+ u32 tmp;
+ int ret;
+
+ ret = rtc_tm_to_time(&wkalrm->time, &alarmsecs);
+ if (ret < 0) {
+ dev_warn(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Disable alarm during update */
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp & ~LPC32XX_RTC_CTRL_MATCH0);
+
+ rtc_writel(rtc, LPC32XX_RTC_MATCH0, alarmsecs);
+
+ rtc->alarm_enabled = wkalrm->enabled;
+ if (wkalrm->enabled) {
+ rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
+ LPC32XX_RTC_INTSTAT_MATCH0);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp |
+ LPC32XX_RTC_CTRL_MATCH0);
+ }
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
+ u32 tmp;
+
+ spin_lock_irq(&rtc->lock);
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+
+ if (enabled) {
+ rtc->alarm_enabled = 1;
+ tmp |= LPC32XX_RTC_CTRL_MATCH0;
+ } else {
+ rtc->alarm_enabled = 0;
+ tmp &= ~LPC32XX_RTC_CTRL_MATCH0;
+ }
+
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev)
+{
+ struct lpc32xx_rtc *rtc = dev;
+
+ spin_lock(&rtc->lock);
+
+ /* Disable alarm interrupt */
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ rtc_readl(rtc, LPC32XX_RTC_CTRL) &
+ ~LPC32XX_RTC_CTRL_MATCH0);
+ rtc->alarm_enabled = 0;
+
+ /*
+ * Write a large value to the match value so the RTC won't
+ * keep firing the match status
+ */
+ rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, LPC32XX_RTC_INTSTAT, LPC32XX_RTC_INTSTAT_MATCH0);
+
+ spin_unlock(&rtc->lock);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops lpc32xx_rtc_ops = {
+ .read_time = lpc32xx_rtc_read_time,
+ .set_mmss = lpc32xx_rtc_set_mmss,
+ .read_alarm = lpc32xx_rtc_read_alarm,
+ .set_alarm = lpc32xx_rtc_set_alarm,
+ .alarm_irq_enable = lpc32xx_rtc_alarm_irq_enable,
+};
+
+static int lpc32xx_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct lpc32xx_rtc *rtc;
+ int rtcirq;
+ u32 tmp;
+
+ rtcirq = platform_get_irq(pdev, 0);
+ if (rtcirq < 0 || rtcirq >= NR_IRQS) {
+ dev_warn(&pdev->dev, "Can't get interrupt resource\n");
+ rtcirq = -1;
+ }
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc))
+ return -ENOMEM;
+
+ rtc->irq = rtcirq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->rtc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->rtc_base))
+ return PTR_ERR(rtc->rtc_base);
+
+ spin_lock_init(&rtc->lock);
+
+ /*
+ * The RTC is on a separate power domain and can keep it's state
+ * across a chip power cycle. If the RTC has never been previously
+ * setup, then set it up now for the first time.
+ */
+ tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
+ if (rtc_readl(rtc, LPC32XX_RTC_KEY) != LPC32XX_RTC_KEY_ONSW_LOADVAL) {
+ tmp &= ~(LPC32XX_RTC_CTRL_SW_RESET |
+ LPC32XX_RTC_CTRL_CNTR_DIS |
+ LPC32XX_RTC_CTRL_MATCH0 |
+ LPC32XX_RTC_CTRL_MATCH1 |
+ LPC32XX_RTC_CTRL_ONSW_MATCH0 |
+ LPC32XX_RTC_CTRL_ONSW_MATCH1 |
+ LPC32XX_RTC_CTRL_ONSW_FORCE_HI);
+ rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
+
+ /* Clear latched interrupt states */
+ rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
+ rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
+ LPC32XX_RTC_INTSTAT_MATCH0 |
+ LPC32XX_RTC_INTSTAT_MATCH1 |
+ LPC32XX_RTC_INTSTAT_ONSW);
+
+ /* Write key value to RTC so it won't reload on reset */
+ rtc_writel(rtc, LPC32XX_RTC_KEY,
+ LPC32XX_RTC_KEY_ONSW_LOADVAL);
+ } else {
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ tmp & ~LPC32XX_RTC_CTRL_MATCH0);
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, RTC_NAME,
+ &lpc32xx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ dev_err(&pdev->dev, "Can't get RTC\n");
+ return PTR_ERR(rtc->rtc);
+ }
+
+ /*
+ * IRQ is enabled after device registration in case alarm IRQ
+ * is pending upon suspend exit.
+ */
+ if (rtc->irq >= 0) {
+ if (devm_request_irq(&pdev->dev, rtc->irq,
+ lpc32xx_rtc_alarm_interrupt,
+ 0, pdev->name, rtc) < 0) {
+ dev_warn(&pdev->dev, "Can't request interrupt.\n");
+ rtc->irq = -1;
+ } else {
+ device_init_wakeup(&pdev->dev, 1);
+ }
+ }
+
+ return 0;
+}
+
+static int lpc32xx_rtc_remove(struct platform_device *pdev)
+{
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->irq >= 0)
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int lpc32xx_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->irq >= 0) {
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(rtc->irq);
+ else
+ disable_irq_wake(rtc->irq);
+ }
+
+ return 0;
+}
+
+static int lpc32xx_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->irq >= 0 && device_may_wakeup(&pdev->dev))
+ disable_irq_wake(rtc->irq);
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int lpc32xx_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ spin_lock_irq(&rtc->lock);
+
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ rtc_readl(rtc, LPC32XX_RTC_CTRL) &
+ ~LPC32XX_RTC_CTRL_MATCH0);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int lpc32xx_rtc_thaw(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct lpc32xx_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (rtc->alarm_enabled) {
+ spin_lock_irq(&rtc->lock);
+
+ rtc_writel(rtc, LPC32XX_RTC_CTRL,
+ rtc_readl(rtc, LPC32XX_RTC_CTRL) |
+ LPC32XX_RTC_CTRL_MATCH0);
+
+ spin_unlock_irq(&rtc->lock);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
+ .suspend = lpc32xx_rtc_suspend,
+ .resume = lpc32xx_rtc_resume,
+ .freeze = lpc32xx_rtc_freeze,
+ .thaw = lpc32xx_rtc_thaw,
+ .restore = lpc32xx_rtc_resume
+};
+
+#define LPC32XX_RTC_PM_OPS (&lpc32xx_rtc_pm_ops)
+#else
+#define LPC32XX_RTC_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc32xx_rtc_match[] = {
+ { .compatible = "nxp,lpc3220-rtc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lpc32xx_rtc_match);
+#endif
+
+static struct platform_driver lpc32xx_rtc_driver = {
+ .probe = lpc32xx_rtc_probe,
+ .remove = lpc32xx_rtc_remove,
+ .driver = {
+ .name = RTC_NAME,
+ .pm = LPC32XX_RTC_PM_OPS,
+ .of_match_table = of_match_ptr(lpc32xx_rtc_match),
+ },
+};
+
+module_platform_driver(lpc32xx_rtc_driver);
+
+MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com");
+MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-lpc32xx");
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c
new file mode 100644
index 000000000..8445e5640
--- /dev/null
+++ b/drivers/rtc/rtc-ls1x.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com>
+ *
+ * Derived from driver/rtc/rtc-au1xxx.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <asm/mach-loongson1/loongson1.h>
+
+#define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20)
+#define LS1X_RTC_REGS(x) \
+ ((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x)))
+
+/*RTC programmable counters 0 and 1*/
+#define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20))
+#define SYS_CNTRL_ERS (1 << 23)
+#define SYS_CNTRL_RTS (1 << 20)
+#define SYS_CNTRL_RM2 (1 << 19)
+#define SYS_CNTRL_RM1 (1 << 18)
+#define SYS_CNTRL_RM0 (1 << 17)
+#define SYS_CNTRL_RS (1 << 16)
+#define SYS_CNTRL_BP (1 << 14)
+#define SYS_CNTRL_REN (1 << 13)
+#define SYS_CNTRL_BRT (1 << 12)
+#define SYS_CNTRL_TEN (1 << 11)
+#define SYS_CNTRL_BTT (1 << 10)
+#define SYS_CNTRL_E0 (1 << 8)
+#define SYS_CNTRL_ETS (1 << 7)
+#define SYS_CNTRL_32S (1 << 5)
+#define SYS_CNTRL_TTS (1 << 4)
+#define SYS_CNTRL_TM2 (1 << 3)
+#define SYS_CNTRL_TM1 (1 << 2)
+#define SYS_CNTRL_TM0 (1 << 1)
+#define SYS_CNTRL_TS (1 << 0)
+
+/* Programmable Counter 0 Registers */
+#define SYS_TOYTRIM (LS1X_RTC_REGS(0))
+#define SYS_TOYWRITE0 (LS1X_RTC_REGS(4))
+#define SYS_TOYWRITE1 (LS1X_RTC_REGS(8))
+#define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC))
+#define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10))
+#define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14))
+#define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18))
+#define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C))
+
+/* Programmable Counter 1 Registers */
+#define SYS_RTCTRIM (LS1X_RTC_REGS(0x40))
+#define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44))
+#define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48))
+#define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C))
+#define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50))
+#define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54))
+
+#define LS1X_SEC_OFFSET (4)
+#define LS1X_MIN_OFFSET (10)
+#define LS1X_HOUR_OFFSET (16)
+#define LS1X_DAY_OFFSET (21)
+#define LS1X_MONTH_OFFSET (26)
+
+
+#define LS1X_SEC_MASK (0x3f)
+#define LS1X_MIN_MASK (0x3f)
+#define LS1X_HOUR_MASK (0x1f)
+#define LS1X_DAY_MASK (0x1f)
+#define LS1X_MONTH_MASK (0x3f)
+#define LS1X_YEAR_MASK (0xffffffff)
+
+#define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK)
+#define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK)
+#define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK)
+#define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK)
+#define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK)
+
+#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
+
+static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm)
+{
+ unsigned long v, t;
+
+ v = readl(SYS_TOYREAD0);
+ t = readl(SYS_TOYREAD1);
+
+ memset(rtm, 0, sizeof(struct rtc_time));
+ t = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v),
+ ls1x_get_day(v), ls1x_get_hour(v),
+ ls1x_get_min(v), ls1x_get_sec(v));
+ rtc_time_to_tm(t, rtm);
+
+ return rtc_valid_tm(rtm);
+}
+
+static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm)
+{
+ unsigned long v, t, c;
+ int ret = -ETIMEDOUT;
+
+ v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET)
+ | (rtm->tm_mday << LS1X_DAY_OFFSET)
+ | (rtm->tm_hour << LS1X_HOUR_OFFSET)
+ | (rtm->tm_min << LS1X_MIN_OFFSET)
+ | (rtm->tm_sec << LS1X_SEC_OFFSET);
+
+ writel(v, SYS_TOYWRITE0);
+ c = 0x10000;
+ /* add timeout check counter, for more safe */
+ while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c)
+ usleep_range(1000, 3000);
+
+ if (!c) {
+ dev_err(dev, "set time timeout!\n");
+ goto err;
+ }
+
+ t = rtm->tm_year + 1900;
+ writel(t, SYS_TOYWRITE1);
+ c = 0x10000;
+ while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c)
+ usleep_range(1000, 3000);
+
+ if (!c) {
+ dev_err(dev, "set time timeout!\n");
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+}
+
+static struct rtc_class_ops ls1x_rtc_ops = {
+ .read_time = ls1x_rtc_read_time,
+ .set_time = ls1x_rtc_set_time,
+};
+
+static int ls1x_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtcdev;
+ unsigned long v;
+ int ret;
+
+ v = readl(SYS_COUNTER_CNTRL);
+ if (!(v & RTC_CNTR_OK)) {
+ dev_err(&pdev->dev, "rtc counters not working\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ ret = -ETIMEDOUT;
+ /* set to 1 HZ if needed */
+ if (readl(SYS_TOYTRIM) != 32767) {
+ v = 0x100000;
+ while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
+ usleep_range(1000, 3000);
+
+ if (!v) {
+ dev_err(&pdev->dev, "time out\n");
+ goto err;
+ }
+ writel(32767, SYS_TOYTRIM);
+ }
+ /* this loop coundn't be endless */
+ while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
+ usleep_range(1000, 3000);
+
+ rtcdev = devm_rtc_device_register(&pdev->dev, "ls1x-rtc",
+ &ls1x_rtc_ops , THIS_MODULE);
+ if (IS_ERR(rtcdev)) {
+ ret = PTR_ERR(rtcdev);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, rtcdev);
+ return 0;
+err:
+ return ret;
+}
+
+static struct platform_driver ls1x_rtc_driver = {
+ .driver = {
+ .name = "ls1x-rtc",
+ },
+ .probe = ls1x_rtc_probe,
+};
+
+module_platform_driver(ls1x_rtc_driver);
+
+MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
new file mode 100644
index 000000000..a82937e2f
--- /dev/null
+++ b/drivers/rtc/rtc-m41t80.c
@@ -0,0 +1,741 @@
+/*
+ * I2C client/driver for the ST M41T80 family of i2c rtc chips.
+ *
+ * Author: Alexander Bigga <ab@mycable.de>
+ *
+ * Based on m41t00.c by Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2006 (c) mycable GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#ifdef CONFIG_RTC_DRV_M41T80_WDT
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/miscdevice.h>
+#include <linux/reboot.h>
+#include <linux/watchdog.h>
+#endif
+
+#define M41T80_REG_SSEC 0
+#define M41T80_REG_SEC 1
+#define M41T80_REG_MIN 2
+#define M41T80_REG_HOUR 3
+#define M41T80_REG_WDAY 4
+#define M41T80_REG_DAY 5
+#define M41T80_REG_MON 6
+#define M41T80_REG_YEAR 7
+#define M41T80_REG_ALARM_MON 0xa
+#define M41T80_REG_ALARM_DAY 0xb
+#define M41T80_REG_ALARM_HOUR 0xc
+#define M41T80_REG_ALARM_MIN 0xd
+#define M41T80_REG_ALARM_SEC 0xe
+#define M41T80_REG_FLAGS 0xf
+#define M41T80_REG_SQW 0x13
+
+#define M41T80_DATETIME_REG_SIZE (M41T80_REG_YEAR + 1)
+#define M41T80_ALARM_REG_SIZE \
+ (M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
+
+#define M41T80_SEC_ST (1 << 7) /* ST: Stop Bit */
+#define M41T80_ALMON_AFE (1 << 7) /* AFE: AF Enable Bit */
+#define M41T80_ALMON_SQWE (1 << 6) /* SQWE: SQW Enable Bit */
+#define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */
+#define M41T80_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */
+#define M41T80_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */
+#define M41T80_WATCHDOG_RB2 (1 << 7) /* RB: Watchdog resolution */
+#define M41T80_WATCHDOG_RB1 (1 << 1) /* RB: Watchdog resolution */
+#define M41T80_WATCHDOG_RB0 (1 << 0) /* RB: Watchdog resolution */
+
+#define M41T80_FEATURE_HT (1 << 0) /* Halt feature */
+#define M41T80_FEATURE_BL (1 << 1) /* Battery low indicator */
+#define M41T80_FEATURE_SQ (1 << 2) /* Squarewave feature */
+#define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */
+#define M41T80_FEATURE_SQ_ALT (1 << 4) /* RSx bits are in reg 4 */
+
+static DEFINE_MUTEX(m41t80_rtc_mutex);
+static const struct i2c_device_id m41t80_id[] = {
+ { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
+ { "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
+ { "m41t80", M41T80_FEATURE_SQ },
+ { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ},
+ { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "rv4162", M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, m41t80_id);
+
+struct m41t80_data {
+ u8 features;
+ struct rtc_device *rtc;
+};
+
+static int m41t80_get_datetime(struct i2c_client *client,
+ struct rtc_time *tm)
+{
+ u8 buf[M41T80_DATETIME_REG_SIZE], dt_addr[1] = { M41T80_REG_SEC };
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = dt_addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
+ .buf = buf + M41T80_REG_SEC,
+ },
+ };
+
+ if (i2c_transfer(client->adapter, msgs, 2) < 0) {
+ dev_err(&client->dev, "read error\n");
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[M41T80_REG_SEC] & 0x7f);
+ tm->tm_min = bcd2bin(buf[M41T80_REG_MIN] & 0x7f);
+ tm->tm_hour = bcd2bin(buf[M41T80_REG_HOUR] & 0x3f);
+ tm->tm_mday = bcd2bin(buf[M41T80_REG_DAY] & 0x3f);
+ tm->tm_wday = buf[M41T80_REG_WDAY] & 0x07;
+ tm->tm_mon = bcd2bin(buf[M41T80_REG_MON] & 0x1f) - 1;
+
+ /* assume 20YY not 19YY, and ignore the Century Bit */
+ tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100;
+ return rtc_valid_tm(tm);
+}
+
+/* Sets the given date and time to the real time clock. */
+static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ u8 wbuf[1 + M41T80_DATETIME_REG_SIZE];
+ u8 *buf = &wbuf[1];
+ u8 dt_addr[1] = { M41T80_REG_SEC };
+ struct i2c_msg msgs_in[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = dt_addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = M41T80_DATETIME_REG_SIZE - M41T80_REG_SEC,
+ .buf = buf + M41T80_REG_SEC,
+ },
+ };
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1 + M41T80_DATETIME_REG_SIZE,
+ .buf = wbuf,
+ },
+ };
+
+ /* Read current reg values into buf[1..7] */
+ if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
+ dev_err(&client->dev, "read error\n");
+ return -EIO;
+ }
+
+ wbuf[0] = 0; /* offset into rtc's regs */
+ /* Merge time-data and register flags into buf[0..7] */
+ buf[M41T80_REG_SSEC] = 0;
+ buf[M41T80_REG_SEC] =
+ bin2bcd(tm->tm_sec) | (buf[M41T80_REG_SEC] & ~0x7f);
+ buf[M41T80_REG_MIN] =
+ bin2bcd(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f);
+ buf[M41T80_REG_HOUR] =
+ bin2bcd(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f);
+ buf[M41T80_REG_WDAY] =
+ (tm->tm_wday & 0x07) | (buf[M41T80_REG_WDAY] & ~0x07);
+ buf[M41T80_REG_DAY] =
+ bin2bcd(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f);
+ buf[M41T80_REG_MON] =
+ bin2bcd(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f);
+ /* assume 20YY not 19YY */
+ buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year % 100);
+
+ if (i2c_transfer(client->adapter, msgs, 1) != 1) {
+ dev_err(&client->dev, "write error\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
+static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct m41t80_data *clientdata = i2c_get_clientdata(client);
+ u8 reg;
+
+ if (clientdata->features & M41T80_FEATURE_BL) {
+ reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
+ seq_printf(seq, "battery\t\t: %s\n",
+ (reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok");
+ }
+ return 0;
+}
+#else
+#define m41t80_rtc_proc NULL
+#endif
+
+static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return m41t80_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return m41t80_set_datetime(to_i2c_client(dev), tm);
+}
+
+/*
+ * XXX - m41t80 alarm functionality is reported broken.
+ * until it is fixed, don't register alarm functions.
+ */
+static struct rtc_class_ops m41t80_rtc_ops = {
+ .read_time = m41t80_rtc_read_time,
+ .set_time = m41t80_rtc_set_time,
+ .proc = m41t80_rtc_proc,
+};
+
+#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
+static ssize_t m41t80_sysfs_show_flags(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int val;
+
+ val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
+ if (val < 0)
+ return val;
+ return sprintf(buf, "%#x\n", val);
+}
+static DEVICE_ATTR(flags, S_IRUGO, m41t80_sysfs_show_flags, NULL);
+
+static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct m41t80_data *clientdata = i2c_get_clientdata(client);
+ int val, reg_sqw;
+
+ if (!(clientdata->features & M41T80_FEATURE_SQ))
+ return -EINVAL;
+
+ reg_sqw = M41T80_REG_SQW;
+ if (clientdata->features & M41T80_FEATURE_SQ_ALT)
+ reg_sqw = M41T80_REG_WDAY;
+ val = i2c_smbus_read_byte_data(client, reg_sqw);
+ if (val < 0)
+ return val;
+ val = (val >> 4) & 0xf;
+ switch (val) {
+ case 0:
+ break;
+ case 1:
+ val = 32768;
+ break;
+ default:
+ val = 32768 >> val;
+ }
+ return sprintf(buf, "%d\n", val);
+}
+static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct m41t80_data *clientdata = i2c_get_clientdata(client);
+ int almon, sqw, reg_sqw, rc;
+ int val = simple_strtoul(buf, NULL, 0);
+
+ if (!(clientdata->features & M41T80_FEATURE_SQ))
+ return -EINVAL;
+
+ if (val) {
+ if (!is_power_of_2(val))
+ return -EINVAL;
+ val = ilog2(val);
+ if (val == 15)
+ val = 1;
+ else if (val < 14)
+ val = 15 - val;
+ else
+ return -EINVAL;
+ }
+ /* disable SQW, set SQW frequency & re-enable */
+ almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
+ if (almon < 0)
+ return almon;
+ reg_sqw = M41T80_REG_SQW;
+ if (clientdata->features & M41T80_FEATURE_SQ_ALT)
+ reg_sqw = M41T80_REG_WDAY;
+ sqw = i2c_smbus_read_byte_data(client, reg_sqw);
+ if (sqw < 0)
+ return sqw;
+ sqw = (sqw & 0x0f) | (val << 4);
+
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
+ almon & ~M41T80_ALMON_SQWE);
+ if (rc < 0)
+ return rc;
+
+ if (val) {
+ rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw);
+ if (rc < 0)
+ return rc;
+
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
+ almon | M41T80_ALMON_SQWE);
+ if (rc <0)
+ return rc;
+ }
+ return count;
+}
+static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR,
+ m41t80_sysfs_show_sqwfreq, m41t80_sysfs_set_sqwfreq);
+
+static struct attribute *attrs[] = {
+ &dev_attr_flags.attr,
+ &dev_attr_sqwfreq.attr,
+ NULL,
+};
+static struct attribute_group attr_group = {
+ .attrs = attrs,
+};
+
+static int m41t80_sysfs_register(struct device *dev)
+{
+ return sysfs_create_group(&dev->kobj, &attr_group);
+}
+#else
+static int m41t80_sysfs_register(struct device *dev)
+{
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_RTC_DRV_M41T80_WDT
+/*
+ *****************************************************************************
+ *
+ * Watchdog Driver
+ *
+ *****************************************************************************
+ */
+static struct i2c_client *save_client;
+
+/* Default margin */
+#define WD_TIMO 60 /* 1..31 seconds */
+
+static int wdt_margin = WD_TIMO;
+module_param(wdt_margin, int, 0);
+MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)");
+
+static unsigned long wdt_is_open;
+static int boot_flag;
+
+/**
+ * wdt_ping:
+ *
+ * Reload counter one with the watchdog timeout. We don't bother reloading
+ * the cascade counter.
+ */
+static void wdt_ping(void)
+{
+ unsigned char i2c_data[2];
+ struct i2c_msg msgs1[1] = {
+ {
+ .addr = save_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = i2c_data,
+ },
+ };
+ struct m41t80_data *clientdata = i2c_get_clientdata(save_client);
+
+ i2c_data[0] = 0x09; /* watchdog register */
+
+ if (wdt_margin > 31)
+ i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */
+ else
+ /*
+ * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02)
+ */
+ i2c_data[1] = wdt_margin<<2 | 0x82;
+
+ /*
+ * M41T65 has three bits for watchdog resolution. Don't set bit 7, as
+ * that would be an invalid resolution.
+ */
+ if (clientdata->features & M41T80_FEATURE_WD)
+ i2c_data[1] &= ~M41T80_WATCHDOG_RB2;
+
+ i2c_transfer(save_client->adapter, msgs1, 1);
+}
+
+/**
+ * wdt_disable:
+ *
+ * disables watchdog.
+ */
+static void wdt_disable(void)
+{
+ unsigned char i2c_data[2], i2c_buf[0x10];
+ struct i2c_msg msgs0[2] = {
+ {
+ .addr = save_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = i2c_data,
+ },
+ {
+ .addr = save_client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = i2c_buf,
+ },
+ };
+ struct i2c_msg msgs1[1] = {
+ {
+ .addr = save_client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = i2c_data,
+ },
+ };
+
+ i2c_data[0] = 0x09;
+ i2c_transfer(save_client->adapter, msgs0, 2);
+
+ i2c_data[0] = 0x09;
+ i2c_data[1] = 0x00;
+ i2c_transfer(save_client->adapter, msgs1, 1);
+}
+
+/**
+ * wdt_write:
+ * @file: file handle to the watchdog
+ * @buf: buffer to write (unused as data does not matter here
+ * @count: count of bytes
+ * @ppos: pointer to the position to write. No seeks allowed
+ *
+ * A write to a watchdog device is defined as a keepalive signal. Any
+ * write of data will do, as we we don't define content meaning.
+ */
+static ssize_t wdt_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ if (count) {
+ wdt_ping();
+ return 1;
+ }
+ return 0;
+}
+
+static ssize_t wdt_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return 0;
+}
+
+/**
+ * wdt_ioctl:
+ * @inode: inode of the device
+ * @file: file handle to the device
+ * @cmd: watchdog command
+ * @arg: argument pointer
+ *
+ * The watchdog API defines a common set of functions for all watchdogs
+ * according to their available features. We only actually usefully support
+ * querying capabilities and current status.
+ */
+static int wdt_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int new_margin, rv;
+ static struct watchdog_info ident = {
+ .options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT,
+ .firmware_version = 1,
+ .identity = "M41T80 WTD"
+ };
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(boot_flag, (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+ /* Arbitrary, can't find the card's limits */
+ if (new_margin < 1 || new_margin > 124)
+ return -EINVAL;
+ wdt_margin = new_margin;
+ wdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(wdt_margin, (int __user *)arg);
+
+ case WDIOC_SETOPTIONS:
+ if (copy_from_user(&rv, (int __user *)arg, sizeof(int)))
+ return -EFAULT;
+
+ if (rv & WDIOS_DISABLECARD) {
+ pr_info("disable watchdog\n");
+ wdt_disable();
+ }
+
+ if (rv & WDIOS_ENABLECARD) {
+ pr_info("enable watchdog\n");
+ wdt_ping();
+ }
+
+ return -EINVAL;
+ }
+ return -ENOTTY;
+}
+
+static long wdt_unlocked_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+
+ mutex_lock(&m41t80_rtc_mutex);
+ ret = wdt_ioctl(file, cmd, arg);
+ mutex_unlock(&m41t80_rtc_mutex);
+
+ return ret;
+}
+
+/**
+ * wdt_open:
+ * @inode: inode of device
+ * @file: file handle to device
+ *
+ */
+static int wdt_open(struct inode *inode, struct file *file)
+{
+ if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {
+ mutex_lock(&m41t80_rtc_mutex);
+ if (test_and_set_bit(0, &wdt_is_open)) {
+ mutex_unlock(&m41t80_rtc_mutex);
+ return -EBUSY;
+ }
+ /*
+ * Activate
+ */
+ wdt_is_open = 1;
+ mutex_unlock(&m41t80_rtc_mutex);
+ return nonseekable_open(inode, file);
+ }
+ return -ENODEV;
+}
+
+/**
+ * wdt_close:
+ * @inode: inode to board
+ * @file: file handle to board
+ *
+ */
+static int wdt_release(struct inode *inode, struct file *file)
+{
+ if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)
+ clear_bit(0, &wdt_is_open);
+ return 0;
+}
+
+/**
+ * notify_sys:
+ * @this: our notifier block
+ * @code: the event being reported
+ * @unused: unused
+ *
+ * Our notifier is called on system shutdowns. We want to turn the card
+ * off at reboot otherwise the machine will reboot again during memory
+ * test or worse yet during the following fsck. This would suck, in fact
+ * trust me - if it happens it does suck.
+ */
+static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
+ void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ /* Disable Watchdog */
+ wdt_disable();
+ return NOTIFY_DONE;
+}
+
+static const struct file_operations wdt_fops = {
+ .owner = THIS_MODULE,
+ .read = wdt_read,
+ .unlocked_ioctl = wdt_unlocked_ioctl,
+ .write = wdt_write,
+ .open = wdt_open,
+ .release = wdt_release,
+ .llseek = no_llseek,
+};
+
+static struct miscdevice wdt_dev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &wdt_fops,
+};
+
+/*
+ * The WDT card needs to learn about soft shutdowns in order to
+ * turn the timebomb registers off.
+ */
+static struct notifier_block wdt_notifier = {
+ .notifier_call = wdt_notify_sys,
+};
+#endif /* CONFIG_RTC_DRV_M41T80_WDT */
+
+/*
+ *****************************************************************************
+ *
+ * Driver Interface
+ *
+ *****************************************************************************
+ */
+static int m41t80_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int rc = 0;
+ struct rtc_device *rtc = NULL;
+ struct rtc_time tm;
+ struct m41t80_data *clientdata = NULL;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C
+ | I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata),
+ GFP_KERNEL);
+ if (!clientdata)
+ return -ENOMEM;
+
+ clientdata->features = id->driver_data;
+ i2c_set_clientdata(client, clientdata);
+
+ rtc = devm_rtc_device_register(&client->dev, client->name,
+ &m41t80_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ clientdata->rtc = rtc;
+
+ /* Make sure HT (Halt Update) bit is cleared */
+ rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
+
+ if (rc >= 0 && rc & M41T80_ALHOUR_HT) {
+ if (clientdata->features & M41T80_FEATURE_HT) {
+ m41t80_get_datetime(client, &tm);
+ dev_info(&client->dev, "HT bit was set!\n");
+ dev_info(&client->dev,
+ "Power Down at "
+ "%04i-%02i-%02i %02i:%02i:%02i\n",
+ tm.tm_year + 1900,
+ tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+ }
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_HOUR,
+ rc & ~M41T80_ALHOUR_HT);
+ }
+
+ if (rc < 0) {
+ dev_err(&client->dev, "Can't clear HT bit\n");
+ return rc;
+ }
+
+ /* Make sure ST (stop) bit is cleared */
+ rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC);
+
+ if (rc >= 0 && rc & M41T80_SEC_ST)
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_SEC,
+ rc & ~M41T80_SEC_ST);
+ if (rc < 0) {
+ dev_err(&client->dev, "Can't clear ST bit\n");
+ return rc;
+ }
+
+ rc = m41t80_sysfs_register(&client->dev);
+ if (rc)
+ return rc;
+
+#ifdef CONFIG_RTC_DRV_M41T80_WDT
+ if (clientdata->features & M41T80_FEATURE_HT) {
+ save_client = client;
+ rc = misc_register(&wdt_dev);
+ if (rc)
+ return rc;
+ rc = register_reboot_notifier(&wdt_notifier);
+ if (rc) {
+ misc_deregister(&wdt_dev);
+ return rc;
+ }
+ }
+#endif
+ return 0;
+}
+
+static int m41t80_remove(struct i2c_client *client)
+{
+#ifdef CONFIG_RTC_DRV_M41T80_WDT
+ struct m41t80_data *clientdata = i2c_get_clientdata(client);
+
+ if (clientdata->features & M41T80_FEATURE_HT) {
+ misc_deregister(&wdt_dev);
+ unregister_reboot_notifier(&wdt_notifier);
+ }
+#endif
+
+ return 0;
+}
+
+static struct i2c_driver m41t80_driver = {
+ .driver = {
+ .name = "rtc-m41t80",
+ },
+ .probe = m41t80_probe,
+ .remove = m41t80_remove,
+ .id_table = m41t80_id,
+};
+
+module_i2c_driver(m41t80_driver);
+
+MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>");
+MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
new file mode 100644
index 000000000..4698c7e34
--- /dev/null
+++ b/drivers/rtc/rtc-m41t93.c
@@ -0,0 +1,210 @@
+/*
+ *
+ * Driver for ST M41T93 SPI RTC
+ *
+ * (c) 2010 Nikolaus Voss, Weinmann Medical GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+
+#define M41T93_REG_SSEC 0
+#define M41T93_REG_ST_SEC 1
+#define M41T93_REG_MIN 2
+#define M41T93_REG_CENT_HOUR 3
+#define M41T93_REG_WDAY 4
+#define M41T93_REG_DAY 5
+#define M41T93_REG_MON 6
+#define M41T93_REG_YEAR 7
+
+
+#define M41T93_REG_ALM_HOUR_HT 0xc
+#define M41T93_REG_FLAGS 0xf
+
+#define M41T93_FLAG_ST (1 << 7)
+#define M41T93_FLAG_OF (1 << 2)
+#define M41T93_FLAG_BL (1 << 4)
+#define M41T93_FLAG_HT (1 << 6)
+
+static inline int m41t93_set_reg(struct spi_device *spi, u8 addr, u8 data)
+{
+ u8 buf[2];
+
+ /* MSB must be '1' to write */
+ buf[0] = addr | 0x80;
+ buf[1] = data;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int m41t93_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int tmp;
+ u8 buf[9] = {0x80}; /* write cmd + 8 data bytes */
+ u8 * const data = &buf[1]; /* ptr to first data byte */
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (tm->tm_year < 100) {
+ dev_warn(&spi->dev, "unsupported date (before 2000-01-01).\n");
+ return -EINVAL;
+ }
+
+ tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+ if (tmp < 0)
+ return tmp;
+
+ if (tmp & M41T93_FLAG_OF) {
+ dev_warn(&spi->dev, "OF bit is set, resetting.\n");
+ m41t93_set_reg(spi, M41T93_REG_FLAGS, tmp & ~M41T93_FLAG_OF);
+
+ tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+ if (tmp < 0) {
+ return tmp;
+ } else if (tmp & M41T93_FLAG_OF) {
+ /* OF cannot be immediately reset: oscillator has to be
+ * restarted. */
+ u8 reset_osc = buf[M41T93_REG_ST_SEC] | M41T93_FLAG_ST;
+
+ dev_warn(&spi->dev,
+ "OF bit is still set, kickstarting clock.\n");
+ m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+ reset_osc &= ~M41T93_FLAG_ST;
+ m41t93_set_reg(spi, M41T93_REG_ST_SEC, reset_osc);
+ }
+ }
+
+ data[M41T93_REG_SSEC] = 0;
+ data[M41T93_REG_ST_SEC] = bin2bcd(tm->tm_sec);
+ data[M41T93_REG_MIN] = bin2bcd(tm->tm_min);
+ data[M41T93_REG_CENT_HOUR] = bin2bcd(tm->tm_hour) |
+ ((tm->tm_year/100-1) << 6);
+ data[M41T93_REG_DAY] = bin2bcd(tm->tm_mday);
+ data[M41T93_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
+ data[M41T93_REG_MON] = bin2bcd(tm->tm_mon + 1);
+ data[M41T93_REG_YEAR] = bin2bcd(tm->tm_year % 100);
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+
+static int m41t93_get_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ const u8 start_addr = 0;
+ u8 buf[8];
+ int century_after_1900;
+ int tmp;
+ int ret = 0;
+
+ /* Check status of clock. Two states must be considered:
+ 1. halt bit (HT) is set: the clock is running but update of readout
+ registers has been disabled due to power failure. This is normal
+ case after poweron. Time is valid after resetting HT bit.
+ 2. oscillator fail bit (OF) is set: time is invalid.
+ */
+ tmp = spi_w8r8(spi, M41T93_REG_ALM_HOUR_HT);
+ if (tmp < 0)
+ return tmp;
+
+ if (tmp & M41T93_FLAG_HT) {
+ dev_dbg(&spi->dev, "HT bit is set, reenable clock update.\n");
+ m41t93_set_reg(spi, M41T93_REG_ALM_HOUR_HT,
+ tmp & ~M41T93_FLAG_HT);
+ }
+
+ tmp = spi_w8r8(spi, M41T93_REG_FLAGS);
+ if (tmp < 0)
+ return tmp;
+
+ if (tmp & M41T93_FLAG_OF) {
+ ret = -EINVAL;
+ dev_warn(&spi->dev, "OF bit is set, write time to restart.\n");
+ }
+
+ if (tmp & M41T93_FLAG_BL)
+ dev_warn(&spi->dev, "BL bit is set, replace battery.\n");
+
+ /* read actual time/date */
+ tmp = spi_write_then_read(spi, &start_addr, 1, buf, sizeof(buf));
+ if (tmp < 0)
+ return tmp;
+
+ tm->tm_sec = bcd2bin(buf[M41T93_REG_ST_SEC]);
+ tm->tm_min = bcd2bin(buf[M41T93_REG_MIN]);
+ tm->tm_hour = bcd2bin(buf[M41T93_REG_CENT_HOUR] & 0x3f);
+ tm->tm_mday = bcd2bin(buf[M41T93_REG_DAY]);
+ tm->tm_mon = bcd2bin(buf[M41T93_REG_MON]) - 1;
+ tm->tm_wday = bcd2bin(buf[M41T93_REG_WDAY] & 0x0f) - 1;
+
+ century_after_1900 = (buf[M41T93_REG_CENT_HOUR] >> 6) + 1;
+ tm->tm_year = bcd2bin(buf[M41T93_REG_YEAR]) + century_after_1900 * 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ return ret < 0 ? ret : rtc_valid_tm(tm);
+}
+
+
+static const struct rtc_class_ops m41t93_rtc_ops = {
+ .read_time = m41t93_get_time,
+ .set_time = m41t93_set_time,
+};
+
+static struct spi_driver m41t93_driver;
+
+static int m41t93_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ int res;
+
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ res = spi_w8r8(spi, M41T93_REG_WDAY);
+ if (res < 0 || (res & 0xf8) != 0) {
+ dev_err(&spi->dev, "not found 0x%x.\n", res);
+ return -ENODEV;
+ }
+
+ rtc = devm_rtc_device_register(&spi->dev, m41t93_driver.driver.name,
+ &m41t93_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static struct spi_driver m41t93_driver = {
+ .driver = {
+ .name = "rtc-m41t93",
+ .owner = THIS_MODULE,
+ },
+ .probe = m41t93_probe,
+};
+
+module_spi_driver(m41t93_driver);
+
+MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
+MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-m41t93");
diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c
new file mode 100644
index 000000000..8d800b1bf
--- /dev/null
+++ b/drivers/rtc/rtc-m41t94.c
@@ -0,0 +1,150 @@
+/*
+ * Driver for ST M41T94 SPI RTC
+ *
+ * Copyright (C) 2008 Kim B. Heino
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+#define M41T94_REG_SECONDS 0x01
+#define M41T94_REG_MINUTES 0x02
+#define M41T94_REG_HOURS 0x03
+#define M41T94_REG_WDAY 0x04
+#define M41T94_REG_DAY 0x05
+#define M41T94_REG_MONTH 0x06
+#define M41T94_REG_YEAR 0x07
+#define M41T94_REG_HT 0x0c
+
+#define M41T94_BIT_HALT 0x40
+#define M41T94_BIT_STOP 0x80
+#define M41T94_BIT_CB 0x40
+#define M41T94_BIT_CEB 0x80
+
+static int m41t94_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[8]; /* write cmd + 7 registers */
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "write", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ buf[0] = 0x80 | M41T94_REG_SECONDS; /* write time + date */
+ buf[M41T94_REG_SECONDS] = bin2bcd(tm->tm_sec);
+ buf[M41T94_REG_MINUTES] = bin2bcd(tm->tm_min);
+ buf[M41T94_REG_HOURS] = bin2bcd(tm->tm_hour);
+ buf[M41T94_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
+ buf[M41T94_REG_DAY] = bin2bcd(tm->tm_mday);
+ buf[M41T94_REG_MONTH] = bin2bcd(tm->tm_mon + 1);
+
+ buf[M41T94_REG_HOURS] |= M41T94_BIT_CEB;
+ if (tm->tm_year >= 100)
+ buf[M41T94_REG_HOURS] |= M41T94_BIT_CB;
+ buf[M41T94_REG_YEAR] = bin2bcd(tm->tm_year % 100);
+
+ return spi_write(spi, buf, 8);
+}
+
+static int m41t94_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 buf[2];
+ int ret, hour;
+
+ /* clear halt update bit */
+ ret = spi_w8r8(spi, M41T94_REG_HT);
+ if (ret < 0)
+ return ret;
+ if (ret & M41T94_BIT_HALT) {
+ buf[0] = 0x80 | M41T94_REG_HT;
+ buf[1] = ret & ~M41T94_BIT_HALT;
+ spi_write(spi, buf, 2);
+ }
+
+ /* clear stop bit */
+ ret = spi_w8r8(spi, M41T94_REG_SECONDS);
+ if (ret < 0)
+ return ret;
+ if (ret & M41T94_BIT_STOP) {
+ buf[0] = 0x80 | M41T94_REG_SECONDS;
+ buf[1] = ret & ~M41T94_BIT_STOP;
+ spi_write(spi, buf, 2);
+ }
+
+ tm->tm_sec = bcd2bin(spi_w8r8(spi, M41T94_REG_SECONDS));
+ tm->tm_min = bcd2bin(spi_w8r8(spi, M41T94_REG_MINUTES));
+ hour = spi_w8r8(spi, M41T94_REG_HOURS);
+ tm->tm_hour = bcd2bin(hour & 0x3f);
+ tm->tm_wday = bcd2bin(spi_w8r8(spi, M41T94_REG_WDAY)) - 1;
+ tm->tm_mday = bcd2bin(spi_w8r8(spi, M41T94_REG_DAY));
+ tm->tm_mon = bcd2bin(spi_w8r8(spi, M41T94_REG_MONTH)) - 1;
+ tm->tm_year = bcd2bin(spi_w8r8(spi, M41T94_REG_YEAR));
+ if ((hour & M41T94_BIT_CB) || !(hour & M41T94_BIT_CEB))
+ tm->tm_year += 100;
+
+ dev_dbg(dev, "%s secs=%d, mins=%d, "
+ "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
+ "read", tm->tm_sec, tm->tm_min,
+ tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* initial clock setting can be undefined */
+ return rtc_valid_tm(tm);
+}
+
+static const struct rtc_class_ops m41t94_rtc_ops = {
+ .read_time = m41t94_read_time,
+ .set_time = m41t94_set_time,
+};
+
+static struct spi_driver m41t94_driver;
+
+static int m41t94_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ int res;
+
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ res = spi_w8r8(spi, M41T94_REG_SECONDS);
+ if (res < 0) {
+ dev_err(&spi->dev, "not found.\n");
+ return res;
+ }
+
+ rtc = devm_rtc_device_register(&spi->dev, m41t94_driver.driver.name,
+ &m41t94_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static struct spi_driver m41t94_driver = {
+ .driver = {
+ .name = "rtc-m41t94",
+ .owner = THIS_MODULE,
+ },
+ .probe = m41t94_probe,
+};
+
+module_spi_driver(m41t94_driver);
+
+MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>");
+MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-m41t94");
diff --git a/drivers/rtc/rtc-m48t35.c b/drivers/rtc/rtc-m48t35.c
new file mode 100644
index 000000000..c62b51217
--- /dev/null
+++ b/drivers/rtc/rtc-m48t35.c
@@ -0,0 +1,194 @@
+/*
+ * Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
+ *
+ * Copyright (C) 2000 Silicon Graphics, Inc.
+ * Written by Ulf Carlsson (ulfc@engr.sgi.com)
+ *
+ * Copyright (C) 2008 Thomas Bogendoerfer
+ *
+ * Based on code written by Paul Gortmaker.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/bcd.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#define DRV_VERSION "1.0"
+
+struct m48t35_rtc {
+ u8 pad[0x7ff8]; /* starts at 0x7ff8 */
+ u8 control;
+ u8 sec;
+ u8 min;
+ u8 hour;
+ u8 day;
+ u8 date;
+ u8 month;
+ u8 year;
+};
+
+#define M48T35_RTC_SET 0x80
+#define M48T35_RTC_READ 0x40
+
+struct m48t35_priv {
+ struct rtc_device *rtc;
+ struct m48t35_rtc __iomem *reg;
+ size_t size;
+ unsigned long baseaddr;
+ spinlock_t lock;
+};
+
+static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct m48t35_priv *priv = dev_get_drvdata(dev);
+ u8 control;
+
+ /*
+ * Only the values that we read from the RTC are set. We leave
+ * tm_wday, tm_yday and tm_isdst untouched. Even though the
+ * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
+ * by the RTC when initially set to a non-zero value.
+ */
+ spin_lock_irq(&priv->lock);
+ control = readb(&priv->reg->control);
+ writeb(control | M48T35_RTC_READ, &priv->reg->control);
+ tm->tm_sec = readb(&priv->reg->sec);
+ tm->tm_min = readb(&priv->reg->min);
+ tm->tm_hour = readb(&priv->reg->hour);
+ tm->tm_mday = readb(&priv->reg->date);
+ tm->tm_mon = readb(&priv->reg->month);
+ tm->tm_year = readb(&priv->reg->year);
+ writeb(control, &priv->reg->control);
+ spin_unlock_irq(&priv->lock);
+
+ tm->tm_sec = bcd2bin(tm->tm_sec);
+ tm->tm_min = bcd2bin(tm->tm_min);
+ tm->tm_hour = bcd2bin(tm->tm_hour);
+ tm->tm_mday = bcd2bin(tm->tm_mday);
+ tm->tm_mon = bcd2bin(tm->tm_mon);
+ tm->tm_year = bcd2bin(tm->tm_year);
+
+ /*
+ * Account for differences between how the RTC uses the values
+ * and how they are defined in a struct rtc_time;
+ */
+ tm->tm_year += 70;
+ if (tm->tm_year <= 69)
+ tm->tm_year += 100;
+
+ tm->tm_mon--;
+ return rtc_valid_tm(tm);
+}
+
+static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct m48t35_priv *priv = dev_get_drvdata(dev);
+ unsigned char mon, day, hrs, min, sec;
+ unsigned int yrs;
+ u8 control;
+
+ yrs = tm->tm_year + 1900;
+ mon = tm->tm_mon + 1; /* tm_mon starts at zero */
+ day = tm->tm_mday;
+ hrs = tm->tm_hour;
+ min = tm->tm_min;
+ sec = tm->tm_sec;
+
+ if (yrs < 1970)
+ return -EINVAL;
+
+ yrs -= 1970;
+ if (yrs > 255) /* They are unsigned */
+ return -EINVAL;
+
+ if (yrs > 169)
+ return -EINVAL;
+
+ if (yrs >= 100)
+ yrs -= 100;
+
+ sec = bin2bcd(sec);
+ min = bin2bcd(min);
+ hrs = bin2bcd(hrs);
+ day = bin2bcd(day);
+ mon = bin2bcd(mon);
+ yrs = bin2bcd(yrs);
+
+ spin_lock_irq(&priv->lock);
+ control = readb(&priv->reg->control);
+ writeb(control | M48T35_RTC_SET, &priv->reg->control);
+ writeb(yrs, &priv->reg->year);
+ writeb(mon, &priv->reg->month);
+ writeb(day, &priv->reg->date);
+ writeb(hrs, &priv->reg->hour);
+ writeb(min, &priv->reg->min);
+ writeb(sec, &priv->reg->sec);
+ writeb(control, &priv->reg->control);
+ spin_unlock_irq(&priv->lock);
+ return 0;
+}
+
+static const struct rtc_class_ops m48t35_ops = {
+ .read_time = m48t35_read_time,
+ .set_time = m48t35_set_time,
+};
+
+static int m48t35_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct m48t35_priv *priv;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct m48t35_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->size = resource_size(res);
+ /*
+ * kludge: remove the #ifndef after ioc3 resource
+ * conflicts are resolved
+ */
+#ifndef CONFIG_SGI_IP27
+ if (!devm_request_mem_region(&pdev->dev, res->start, priv->size,
+ pdev->name))
+ return -EBUSY;
+#endif
+ priv->baseaddr = res->start;
+ priv->reg = devm_ioremap(&pdev->dev, priv->baseaddr, priv->size);
+ if (!priv->reg)
+ return -ENOMEM;
+
+ spin_lock_init(&priv->lock);
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->rtc = devm_rtc_device_register(&pdev->dev, "m48t35",
+ &m48t35_ops, THIS_MODULE);
+ return PTR_ERR_OR_ZERO(priv->rtc);
+}
+
+static struct platform_driver m48t35_platform_driver = {
+ .driver = {
+ .name = "rtc-m48t35",
+ },
+ .probe = m48t35_probe,
+};
+
+module_platform_driver(m48t35_platform_driver);
+
+MODULE_AUTHOR("Thomas Bogendoerfer <tsbogend@alpha.franken.de>");
+MODULE_DESCRIPTION("M48T35 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:rtc-m48t35");
diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c
new file mode 100644
index 000000000..90abb5bd5
--- /dev/null
+++ b/drivers/rtc/rtc-m48t59.c
@@ -0,0 +1,516 @@
+/*
+ * ST M48T59 RTC driver
+ *
+ * Copyright (c) 2007 Wind River Systems, Inc.
+ *
+ * Author: Mark Zhan <rongkai.zhan@windriver.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/rtc/m48t59.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#ifndef NO_IRQ
+#define NO_IRQ (-1)
+#endif
+
+#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg))
+#define M48T59_WRITE(val, reg) \
+ (pdata->write_byte(dev, pdata->offset + reg, val))
+
+#define M48T59_SET_BITS(mask, reg) \
+ M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg))
+#define M48T59_CLEAR_BITS(mask, reg) \
+ M48T59_WRITE((M48T59_READ(reg) & ~(mask)), (reg))
+
+struct m48t59_private {
+ void __iomem *ioaddr;
+ int irq;
+ struct rtc_device *rtc;
+ spinlock_t lock; /* serialize the NVRAM and RTC access */
+};
+
+/*
+ * This is the generic access method when the chip is memory-mapped
+ */
+static void
+m48t59_mem_writeb(struct device *dev, u32 ofs, u8 val)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+
+ writeb(val, m48t59->ioaddr+ofs);
+}
+
+static u8
+m48t59_mem_readb(struct device *dev, u32 ofs)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+
+ return readb(m48t59->ioaddr+ofs);
+}
+
+/*
+ * NOTE: M48T59 only uses BCD mode
+ */
+static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ unsigned long flags;
+ u8 val;
+
+ spin_lock_irqsave(&m48t59->lock, flags);
+ /* Issue the READ command */
+ M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL);
+
+ tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR));
+ /* tm_mon is 0-11 */
+ tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
+ tm->tm_mday = bcd2bin(M48T59_READ(M48T59_MDAY));
+
+ val = M48T59_READ(M48T59_WDAY);
+ if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
+ (val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
+ dev_dbg(dev, "Century bit is enabled\n");
+ tm->tm_year += 100; /* one century */
+ }
+#ifdef CONFIG_SPARC
+ /* Sun SPARC machines count years since 1968 */
+ tm->tm_year += 68;
+#endif
+
+ tm->tm_wday = bcd2bin(val & 0x07);
+ tm->tm_hour = bcd2bin(M48T59_READ(M48T59_HOUR) & 0x3F);
+ tm->tm_min = bcd2bin(M48T59_READ(M48T59_MIN) & 0x7F);
+ tm->tm_sec = bcd2bin(M48T59_READ(M48T59_SEC) & 0x7F);
+
+ /* Clear the READ bit */
+ M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+
+ dev_dbg(dev, "RTC read time %04d-%02d-%02d %02d/%02d/%02d\n",
+ tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return rtc_valid_tm(tm);
+}
+
+static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ unsigned long flags;
+ u8 val = 0;
+ int year = tm->tm_year;
+
+#ifdef CONFIG_SPARC
+ /* Sun SPARC machines count years since 1968 */
+ year -= 68;
+#endif
+
+ dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n",
+ year + 1900, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ if (year < 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&m48t59->lock, flags);
+ /* Issue the WRITE command */
+ M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
+
+ M48T59_WRITE((bin2bcd(tm->tm_sec) & 0x7F), M48T59_SEC);
+ M48T59_WRITE((bin2bcd(tm->tm_min) & 0x7F), M48T59_MIN);
+ M48T59_WRITE((bin2bcd(tm->tm_hour) & 0x3F), M48T59_HOUR);
+ M48T59_WRITE((bin2bcd(tm->tm_mday) & 0x3F), M48T59_MDAY);
+ /* tm_mon is 0-11 */
+ M48T59_WRITE((bin2bcd(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
+ M48T59_WRITE(bin2bcd(year % 100), M48T59_YEAR);
+
+ if (pdata->type == M48T59RTC_TYPE_M48T59 && (year / 100))
+ val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
+ val |= (bin2bcd(tm->tm_wday) & 0x07);
+ M48T59_WRITE(val, M48T59_WDAY);
+
+ /* Clear the WRITE bit */
+ M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+ return 0;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ struct rtc_time *tm = &alrm->time;
+ unsigned long flags;
+ u8 val;
+
+ /* If no irq, we don't support ALARM */
+ if (m48t59->irq == NO_IRQ)
+ return -EIO;
+
+ spin_lock_irqsave(&m48t59->lock, flags);
+ /* Issue the READ command */
+ M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL);
+
+ tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR));
+#ifdef CONFIG_SPARC
+ /* Sun SPARC machines count years since 1968 */
+ tm->tm_year += 68;
+#endif
+ /* tm_mon is 0-11 */
+ tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
+
+ val = M48T59_READ(M48T59_WDAY);
+ if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB))
+ tm->tm_year += 100; /* one century */
+
+ tm->tm_mday = bcd2bin(M48T59_READ(M48T59_ALARM_DATE));
+ tm->tm_hour = bcd2bin(M48T59_READ(M48T59_ALARM_HOUR));
+ tm->tm_min = bcd2bin(M48T59_READ(M48T59_ALARM_MIN));
+ tm->tm_sec = bcd2bin(M48T59_READ(M48T59_ALARM_SEC));
+
+ /* Clear the READ bit */
+ M48T59_CLEAR_BITS(M48T59_CNTL_READ, M48T59_CNTL);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+
+ dev_dbg(dev, "RTC read alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
+ tm->tm_year + 1900, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return rtc_valid_tm(tm);
+}
+
+/*
+ * Set alarm time and date in RTC
+ */
+static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ struct rtc_time *tm = &alrm->time;
+ u8 mday, hour, min, sec;
+ unsigned long flags;
+ int year = tm->tm_year;
+
+#ifdef CONFIG_SPARC
+ /* Sun SPARC machines count years since 1968 */
+ year -= 68;
+#endif
+
+ /* If no irq, we don't support ALARM */
+ if (m48t59->irq == NO_IRQ)
+ return -EIO;
+
+ if (year < 0)
+ return -EINVAL;
+
+ /*
+ * 0xff means "always match"
+ */
+ mday = tm->tm_mday;
+ mday = (mday >= 1 && mday <= 31) ? bin2bcd(mday) : 0xff;
+ if (mday == 0xff)
+ mday = M48T59_READ(M48T59_MDAY);
+
+ hour = tm->tm_hour;
+ hour = (hour < 24) ? bin2bcd(hour) : 0x00;
+
+ min = tm->tm_min;
+ min = (min < 60) ? bin2bcd(min) : 0x00;
+
+ sec = tm->tm_sec;
+ sec = (sec < 60) ? bin2bcd(sec) : 0x00;
+
+ spin_lock_irqsave(&m48t59->lock, flags);
+ /* Issue the WRITE command */
+ M48T59_SET_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
+
+ M48T59_WRITE(mday, M48T59_ALARM_DATE);
+ M48T59_WRITE(hour, M48T59_ALARM_HOUR);
+ M48T59_WRITE(min, M48T59_ALARM_MIN);
+ M48T59_WRITE(sec, M48T59_ALARM_SEC);
+
+ /* Clear the WRITE bit */
+ M48T59_CLEAR_BITS(M48T59_CNTL_WRITE, M48T59_CNTL);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+
+ dev_dbg(dev, "RTC set alarm time %04d-%02d-%02d %02d/%02d/%02d\n",
+ year + 1900, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ return 0;
+}
+
+/*
+ * Handle commands from user-space
+ */
+static int m48t59_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&m48t59->lock, flags);
+ if (enabled)
+ M48T59_WRITE(M48T59_INTR_AFE, M48T59_INTR);
+ else
+ M48T59_WRITE(0x00, M48T59_INTR);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+
+ return 0;
+}
+
+static int m48t59_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ unsigned long flags;
+ u8 val;
+
+ spin_lock_irqsave(&m48t59->lock, flags);
+ val = M48T59_READ(M48T59_FLAGS);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+
+ seq_printf(seq, "battery\t\t: %s\n",
+ (val & M48T59_FLAGS_BF) ? "low" : "normal");
+ return 0;
+}
+
+/*
+ * IRQ handler for the RTC
+ */
+static irqreturn_t m48t59_rtc_interrupt(int irq, void *dev_id)
+{
+ struct device *dev = (struct device *)dev_id;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ u8 event;
+
+ spin_lock(&m48t59->lock);
+ event = M48T59_READ(M48T59_FLAGS);
+ spin_unlock(&m48t59->lock);
+
+ if (event & M48T59_FLAGS_AF) {
+ rtc_update_irq(m48t59->rtc, 1, (RTC_AF | RTC_IRQF));
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static const struct rtc_class_ops m48t59_rtc_ops = {
+ .read_time = m48t59_rtc_read_time,
+ .set_time = m48t59_rtc_set_time,
+ .read_alarm = m48t59_rtc_readalarm,
+ .set_alarm = m48t59_rtc_setalarm,
+ .proc = m48t59_rtc_proc,
+ .alarm_irq_enable = m48t59_rtc_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops m48t02_rtc_ops = {
+ .read_time = m48t59_rtc_read_time,
+ .set_time = m48t59_rtc_set_time,
+};
+
+static ssize_t m48t59_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ ssize_t cnt = 0;
+ unsigned long flags;
+
+ for (; size > 0 && pos < pdata->offset; cnt++, size--) {
+ spin_lock_irqsave(&m48t59->lock, flags);
+ *buf++ = M48T59_READ(cnt);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+ }
+
+ return cnt;
+}
+
+static ssize_t m48t59_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
+ ssize_t cnt = 0;
+ unsigned long flags;
+
+ for (; size > 0 && pos < pdata->offset; cnt++, size--) {
+ spin_lock_irqsave(&m48t59->lock, flags);
+ M48T59_WRITE(*buf++, cnt);
+ spin_unlock_irqrestore(&m48t59->lock, flags);
+ }
+
+ return cnt;
+}
+
+static struct bin_attribute m48t59_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .read = m48t59_nvram_read,
+ .write = m48t59_nvram_write,
+};
+
+static int m48t59_rtc_probe(struct platform_device *pdev)
+{
+ struct m48t59_plat_data *pdata = dev_get_platdata(&pdev->dev);
+ struct m48t59_private *m48t59 = NULL;
+ struct resource *res;
+ int ret = -ENOMEM;
+ char *name;
+ const struct rtc_class_ops *ops;
+
+ /* This chip could be memory-mapped or I/O-mapped */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EINVAL;
+ }
+
+ if (res->flags & IORESOURCE_IO) {
+ /* If we are I/O-mapped, the platform should provide
+ * the operations accessing chip registers.
+ */
+ if (!pdata || !pdata->write_byte || !pdata->read_byte)
+ return -EINVAL;
+ } else if (res->flags & IORESOURCE_MEM) {
+ /* we are memory-mapped */
+ if (!pdata) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ /* Ensure we only kmalloc platform data once */
+ pdev->dev.platform_data = pdata;
+ }
+ if (!pdata->type)
+ pdata->type = M48T59RTC_TYPE_M48T59;
+
+ /* Try to use the generic memory read/write ops */
+ if (!pdata->write_byte)
+ pdata->write_byte = m48t59_mem_writeb;
+ if (!pdata->read_byte)
+ pdata->read_byte = m48t59_mem_readb;
+ }
+
+ m48t59 = devm_kzalloc(&pdev->dev, sizeof(*m48t59), GFP_KERNEL);
+ if (!m48t59)
+ return -ENOMEM;
+
+ m48t59->ioaddr = pdata->ioaddr;
+
+ if (!m48t59->ioaddr) {
+ /* ioaddr not mapped externally */
+ m48t59->ioaddr = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!m48t59->ioaddr)
+ return ret;
+ }
+
+ /* Try to get irq number. We also can work in
+ * the mode without IRQ.
+ */
+ m48t59->irq = platform_get_irq(pdev, 0);
+ if (m48t59->irq <= 0)
+ m48t59->irq = NO_IRQ;
+
+ if (m48t59->irq != NO_IRQ) {
+ ret = devm_request_irq(&pdev->dev, m48t59->irq,
+ m48t59_rtc_interrupt, IRQF_SHARED,
+ "rtc-m48t59", &pdev->dev);
+ if (ret)
+ return ret;
+ }
+ switch (pdata->type) {
+ case M48T59RTC_TYPE_M48T59:
+ name = "m48t59";
+ ops = &m48t59_rtc_ops;
+ pdata->offset = 0x1ff0;
+ break;
+ case M48T59RTC_TYPE_M48T02:
+ name = "m48t02";
+ ops = &m48t02_rtc_ops;
+ pdata->offset = 0x7f0;
+ break;
+ case M48T59RTC_TYPE_M48T08:
+ name = "m48t08";
+ ops = &m48t02_rtc_ops;
+ pdata->offset = 0x1ff0;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown RTC type\n");
+ return -ENODEV;
+ }
+
+ spin_lock_init(&m48t59->lock);
+ platform_set_drvdata(pdev, m48t59);
+
+ m48t59->rtc = devm_rtc_device_register(&pdev->dev, name, ops,
+ THIS_MODULE);
+ if (IS_ERR(m48t59->rtc))
+ return PTR_ERR(m48t59->rtc);
+
+ m48t59_nvram_attr.size = pdata->offset;
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int m48t59_rtc_remove(struct platform_device *pdev)
+{
+ sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:rtc-m48t59");
+
+static struct platform_driver m48t59_rtc_driver = {
+ .driver = {
+ .name = "rtc-m48t59",
+ },
+ .probe = m48t59_rtc_probe,
+ .remove = m48t59_rtc_remove,
+};
+
+module_platform_driver(m48t59_rtc_driver);
+
+MODULE_AUTHOR("Mark Zhan <rongkai.zhan@windriver.com>");
+MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
new file mode 100644
index 000000000..a17b7a3ce
--- /dev/null
+++ b/drivers/rtc/rtc-m48t86.c
@@ -0,0 +1,182 @@
+/*
+ * ST M48T86 / Dallas DS12887 RTC driver
+ * Copyright (c) 2006 Tower Technologies
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This drivers only supports the clock running in BCD and 24H mode.
+ * If it will be ever adapted to binary and 12H mode, care must be taken
+ * to not introduce bugs.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/m48t86.h>
+#include <linux/bcd.h>
+
+#define M48T86_REG_SEC 0x00
+#define M48T86_REG_SECALRM 0x01
+#define M48T86_REG_MIN 0x02
+#define M48T86_REG_MINALRM 0x03
+#define M48T86_REG_HOUR 0x04
+#define M48T86_REG_HOURALRM 0x05
+#define M48T86_REG_DOW 0x06 /* 1 = sunday */
+#define M48T86_REG_DOM 0x07
+#define M48T86_REG_MONTH 0x08 /* 1 - 12 */
+#define M48T86_REG_YEAR 0x09 /* 0 - 99 */
+#define M48T86_REG_A 0x0A
+#define M48T86_REG_B 0x0B
+#define M48T86_REG_C 0x0C
+#define M48T86_REG_D 0x0D
+
+#define M48T86_REG_B_H24 (1 << 1)
+#define M48T86_REG_B_DM (1 << 2)
+#define M48T86_REG_B_SET (1 << 7)
+#define M48T86_REG_D_VRT (1 << 7)
+
+#define DRV_VERSION "0.1"
+
+
+static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char reg;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t86_ops *ops = dev_get_platdata(&pdev->dev);
+
+ reg = ops->readbyte(M48T86_REG_B);
+
+ if (reg & M48T86_REG_B_DM) {
+ /* data (binary) mode */
+ tm->tm_sec = ops->readbyte(M48T86_REG_SEC);
+ tm->tm_min = ops->readbyte(M48T86_REG_MIN);
+ tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F;
+ tm->tm_mday = ops->readbyte(M48T86_REG_DOM);
+ /* tm_mon is 0-11 */
+ tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1;
+ tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100;
+ tm->tm_wday = ops->readbyte(M48T86_REG_DOW);
+ } else {
+ /* bcd mode */
+ tm->tm_sec = bcd2bin(ops->readbyte(M48T86_REG_SEC));
+ tm->tm_min = bcd2bin(ops->readbyte(M48T86_REG_MIN));
+ tm->tm_hour = bcd2bin(ops->readbyte(M48T86_REG_HOUR) & 0x3F);
+ tm->tm_mday = bcd2bin(ops->readbyte(M48T86_REG_DOM));
+ /* tm_mon is 0-11 */
+ tm->tm_mon = bcd2bin(ops->readbyte(M48T86_REG_MONTH)) - 1;
+ tm->tm_year = bcd2bin(ops->readbyte(M48T86_REG_YEAR)) + 100;
+ tm->tm_wday = bcd2bin(ops->readbyte(M48T86_REG_DOW));
+ }
+
+ /* correct the hour if the clock is in 12h mode */
+ if (!(reg & M48T86_REG_B_H24))
+ if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
+ tm->tm_hour += 12;
+
+ return rtc_valid_tm(tm);
+}
+
+static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char reg;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t86_ops *ops = dev_get_platdata(&pdev->dev);
+
+ reg = ops->readbyte(M48T86_REG_B);
+
+ /* update flag and 24h mode */
+ reg |= M48T86_REG_B_SET | M48T86_REG_B_H24;
+ ops->writebyte(reg, M48T86_REG_B);
+
+ if (reg & M48T86_REG_B_DM) {
+ /* data (binary) mode */
+ ops->writebyte(tm->tm_sec, M48T86_REG_SEC);
+ ops->writebyte(tm->tm_min, M48T86_REG_MIN);
+ ops->writebyte(tm->tm_hour, M48T86_REG_HOUR);
+ ops->writebyte(tm->tm_mday, M48T86_REG_DOM);
+ ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH);
+ ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR);
+ ops->writebyte(tm->tm_wday, M48T86_REG_DOW);
+ } else {
+ /* bcd mode */
+ ops->writebyte(bin2bcd(tm->tm_sec), M48T86_REG_SEC);
+ ops->writebyte(bin2bcd(tm->tm_min), M48T86_REG_MIN);
+ ops->writebyte(bin2bcd(tm->tm_hour), M48T86_REG_HOUR);
+ ops->writebyte(bin2bcd(tm->tm_mday), M48T86_REG_DOM);
+ ops->writebyte(bin2bcd(tm->tm_mon + 1), M48T86_REG_MONTH);
+ ops->writebyte(bin2bcd(tm->tm_year % 100), M48T86_REG_YEAR);
+ ops->writebyte(bin2bcd(tm->tm_wday), M48T86_REG_DOW);
+ }
+
+ /* update ended */
+ reg &= ~M48T86_REG_B_SET;
+ ops->writebyte(reg, M48T86_REG_B);
+
+ return 0;
+}
+
+static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ unsigned char reg;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct m48t86_ops *ops = dev_get_platdata(&pdev->dev);
+
+ reg = ops->readbyte(M48T86_REG_B);
+
+ seq_printf(seq, "mode\t\t: %s\n",
+ (reg & M48T86_REG_B_DM) ? "binary" : "bcd");
+
+ reg = ops->readbyte(M48T86_REG_D);
+
+ seq_printf(seq, "battery\t\t: %s\n",
+ (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted");
+
+ return 0;
+}
+
+static const struct rtc_class_ops m48t86_rtc_ops = {
+ .read_time = m48t86_rtc_read_time,
+ .set_time = m48t86_rtc_set_time,
+ .proc = m48t86_rtc_proc,
+};
+
+static int m48t86_rtc_probe(struct platform_device *dev)
+{
+ unsigned char reg;
+ struct m48t86_ops *ops = dev_get_platdata(&dev->dev);
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&dev->dev, "m48t86",
+ &m48t86_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+
+ /* read battery status */
+ reg = ops->readbyte(M48T86_REG_D);
+ dev_info(&dev->dev, "battery %s\n",
+ (reg & M48T86_REG_D_VRT) ? "ok" : "exhausted");
+
+ return 0;
+}
+
+static struct platform_driver m48t86_rtc_platform_driver = {
+ .driver = {
+ .name = "rtc-m48t86",
+ },
+ .probe = m48t86_rtc_probe,
+};
+
+module_platform_driver(m48t86_rtc_platform_driver);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("M48T86 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("platform:rtc-m48t86");
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
new file mode 100644
index 000000000..4804985b8
--- /dev/null
+++ b/drivers/rtc/rtc-max6900.c
@@ -0,0 +1,251 @@
+/*
+ * rtc class driver for the Maxim MAX6900 chip
+ *
+ * Author: Dale Farnsworth <dale@farnsworth.org>
+ *
+ * based on previously existing rtc class drivers
+ *
+ * 2007 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+
+#define DRV_VERSION "0.2"
+
+/*
+ * register indices
+ */
+#define MAX6900_REG_SC 0 /* seconds 00-59 */
+#define MAX6900_REG_MN 1 /* minutes 00-59 */
+#define MAX6900_REG_HR 2 /* hours 00-23 */
+#define MAX6900_REG_DT 3 /* day of month 00-31 */
+#define MAX6900_REG_MO 4 /* month 01-12 */
+#define MAX6900_REG_DW 5 /* day of week 1-7 */
+#define MAX6900_REG_YR 6 /* year 00-99 */
+#define MAX6900_REG_CT 7 /* control */
+ /* register 8 is undocumented */
+#define MAX6900_REG_CENTURY 9 /* century */
+#define MAX6900_REG_LEN 10
+
+#define MAX6900_BURST_LEN 8 /* can burst r/w first 8 regs */
+
+#define MAX6900_REG_CT_WP (1 << 7) /* Write Protect */
+
+/*
+ * register read/write commands
+ */
+#define MAX6900_REG_CONTROL_WRITE 0x8e
+#define MAX6900_REG_CENTURY_WRITE 0x92
+#define MAX6900_REG_CENTURY_READ 0x93
+#define MAX6900_REG_RESERVED_READ 0x96
+#define MAX6900_REG_BURST_WRITE 0xbe
+#define MAX6900_REG_BURST_READ 0xbf
+
+#define MAX6900_IDLE_TIME_AFTER_WRITE 3 /* specification says 2.5 mS */
+
+static struct i2c_driver max6900_driver;
+
+static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf)
+{
+ u8 reg_burst_read[1] = { MAX6900_REG_BURST_READ };
+ u8 reg_century_read[1] = { MAX6900_REG_CENTURY_READ };
+ struct i2c_msg msgs[4] = {
+ {
+ .addr = client->addr,
+ .flags = 0, /* write */
+ .len = sizeof(reg_burst_read),
+ .buf = reg_burst_read}
+ ,
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = MAX6900_BURST_LEN,
+ .buf = buf}
+ ,
+ {
+ .addr = client->addr,
+ .flags = 0, /* write */
+ .len = sizeof(reg_century_read),
+ .buf = reg_century_read}
+ ,
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(buf[MAX6900_REG_CENTURY]),
+ .buf = &buf[MAX6900_REG_CENTURY]
+ }
+ };
+ int rc;
+
+ rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (rc != ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: register read failed\n", __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf)
+{
+ u8 i2c_century_buf[1 + 1] = { MAX6900_REG_CENTURY_WRITE };
+ struct i2c_msg century_msgs[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0, /* write */
+ .len = sizeof(i2c_century_buf),
+ .buf = i2c_century_buf}
+ };
+ u8 i2c_burst_buf[MAX6900_BURST_LEN + 1] = { MAX6900_REG_BURST_WRITE };
+ struct i2c_msg burst_msgs[1] = {
+ {
+ .addr = client->addr,
+ .flags = 0, /* write */
+ .len = sizeof(i2c_burst_buf),
+ .buf = i2c_burst_buf}
+ };
+ int rc;
+
+ /*
+ * We have to make separate calls to i2c_transfer because of
+ * the need to delay after each write to the chip. Also,
+ * we write the century byte first, since we set the write-protect
+ * bit as part of the burst write.
+ */
+ i2c_century_buf[1] = buf[MAX6900_REG_CENTURY];
+
+ rc = i2c_transfer(client->adapter, century_msgs,
+ ARRAY_SIZE(century_msgs));
+ if (rc != ARRAY_SIZE(century_msgs))
+ goto write_failed;
+
+ msleep(MAX6900_IDLE_TIME_AFTER_WRITE);
+
+ memcpy(&i2c_burst_buf[1], buf, MAX6900_BURST_LEN);
+
+ rc = i2c_transfer(client->adapter, burst_msgs, ARRAY_SIZE(burst_msgs));
+ if (rc != ARRAY_SIZE(burst_msgs))
+ goto write_failed;
+ msleep(MAX6900_IDLE_TIME_AFTER_WRITE);
+
+ return 0;
+
+ write_failed:
+ dev_err(&client->dev, "%s: register write failed\n", __func__);
+ return -EIO;
+}
+
+static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+{
+ int rc;
+ u8 regs[MAX6900_REG_LEN];
+
+ rc = max6900_i2c_read_regs(client, regs);
+ if (rc < 0)
+ return rc;
+
+ tm->tm_sec = bcd2bin(regs[MAX6900_REG_SC]);
+ tm->tm_min = bcd2bin(regs[MAX6900_REG_MN]);
+ tm->tm_hour = bcd2bin(regs[MAX6900_REG_HR] & 0x3f);
+ tm->tm_mday = bcd2bin(regs[MAX6900_REG_DT]);
+ tm->tm_mon = bcd2bin(regs[MAX6900_REG_MO]) - 1;
+ tm->tm_year = bcd2bin(regs[MAX6900_REG_YR]) +
+ bcd2bin(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
+ tm->tm_wday = bcd2bin(regs[MAX6900_REG_DW]);
+
+ return rtc_valid_tm(tm);
+}
+
+static int max6900_i2c_clear_write_protect(struct i2c_client *client)
+{
+ return i2c_smbus_write_byte_data(client, MAX6900_REG_CONTROL_WRITE, 0);
+}
+
+static int
+max6900_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
+{
+ u8 regs[MAX6900_REG_LEN];
+ int rc;
+
+ rc = max6900_i2c_clear_write_protect(client);
+ if (rc < 0)
+ return rc;
+
+ regs[MAX6900_REG_SC] = bin2bcd(tm->tm_sec);
+ regs[MAX6900_REG_MN] = bin2bcd(tm->tm_min);
+ regs[MAX6900_REG_HR] = bin2bcd(tm->tm_hour);
+ regs[MAX6900_REG_DT] = bin2bcd(tm->tm_mday);
+ regs[MAX6900_REG_MO] = bin2bcd(tm->tm_mon + 1);
+ regs[MAX6900_REG_DW] = bin2bcd(tm->tm_wday);
+ regs[MAX6900_REG_YR] = bin2bcd(tm->tm_year % 100);
+ regs[MAX6900_REG_CENTURY] = bin2bcd((tm->tm_year + 1900) / 100);
+ /* set write protect */
+ regs[MAX6900_REG_CT] = MAX6900_REG_CT_WP;
+
+ rc = max6900_i2c_write_regs(client, regs);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int max6900_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return max6900_i2c_read_time(to_i2c_client(dev), tm);
+}
+
+static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return max6900_i2c_set_time(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops max6900_rtc_ops = {
+ .read_time = max6900_rtc_read_time,
+ .set_time = max6900_rtc_set_time,
+};
+
+static int
+max6900_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct rtc_device *rtc;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ rtc = devm_rtc_device_register(&client->dev, max6900_driver.driver.name,
+ &max6900_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static struct i2c_device_id max6900_id[] = {
+ { "max6900", 0 },
+ { }
+};
+
+static struct i2c_driver max6900_driver = {
+ .driver = {
+ .name = "rtc-max6900",
+ },
+ .probe = max6900_probe,
+ .id_table = max6900_id,
+};
+
+module_i2c_driver(max6900_driver);
+
+MODULE_DESCRIPTION("Maxim MAX6900 RTC driver");
+MODULE_AUTHOR("Dale Farnsworth <dale@farnsworth.org>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
new file mode 100644
index 000000000..ac3f41918
--- /dev/null
+++ b/drivers/rtc/rtc-max6902.c
@@ -0,0 +1,159 @@
+/* drivers/rtc/rtc-max6902.c
+ *
+ * Copyright (C) 2006 8D Technologies inc.
+ * Copyright (C) 2004 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for MAX6902 spi RTC
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+#define MAX6902_REG_SECONDS 0x01
+#define MAX6902_REG_MINUTES 0x03
+#define MAX6902_REG_HOURS 0x05
+#define MAX6902_REG_DATE 0x07
+#define MAX6902_REG_MONTH 0x09
+#define MAX6902_REG_DAY 0x0B
+#define MAX6902_REG_YEAR 0x0D
+#define MAX6902_REG_CONTROL 0x0F
+#define MAX6902_REG_CENTURY 0x13
+
+static int max6902_set_reg(struct device *dev, unsigned char address,
+ unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ /* MSB must be '0' to write */
+ buf[0] = address & 0x7f;
+ buf[1] = data;
+
+ return spi_write_then_read(spi, buf, 2, NULL, 0);
+}
+
+static int max6902_get_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /* Set MSB to indicate read */
+ *data = address | 0x80;
+
+ return spi_write_then_read(spi, data, 1, data, 1);
+}
+
+static int max6902_read_time(struct device *dev, struct rtc_time *dt)
+{
+ int err, century;
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[8];
+
+ buf[0] = 0xbf; /* Burst read */
+
+ err = spi_write_then_read(spi, buf, 1, buf, 8);
+ if (err != 0)
+ return err;
+
+ /* The chip sends data in this order:
+ * Seconds, Minutes, Hours, Date, Month, Day, Year */
+ dt->tm_sec = bcd2bin(buf[0]);
+ dt->tm_min = bcd2bin(buf[1]);
+ dt->tm_hour = bcd2bin(buf[2]);
+ dt->tm_mday = bcd2bin(buf[3]);
+ dt->tm_mon = bcd2bin(buf[4]) - 1;
+ dt->tm_wday = bcd2bin(buf[5]);
+ dt->tm_year = bcd2bin(buf[6]);
+
+ /* Read century */
+ err = max6902_get_reg(dev, MAX6902_REG_CENTURY, &buf[0]);
+ if (err != 0)
+ return err;
+
+ century = bcd2bin(buf[0]) * 100;
+
+ dt->tm_year += century;
+ dt->tm_year -= 1900;
+
+ return rtc_valid_tm(dt);
+}
+
+static int max6902_set_time(struct device *dev, struct rtc_time *dt)
+{
+ dt->tm_year = dt->tm_year + 1900;
+
+ /* Remove write protection */
+ max6902_set_reg(dev, MAX6902_REG_CONTROL, 0);
+
+ max6902_set_reg(dev, MAX6902_REG_SECONDS, bin2bcd(dt->tm_sec));
+ max6902_set_reg(dev, MAX6902_REG_MINUTES, bin2bcd(dt->tm_min));
+ max6902_set_reg(dev, MAX6902_REG_HOURS, bin2bcd(dt->tm_hour));
+
+ max6902_set_reg(dev, MAX6902_REG_DATE, bin2bcd(dt->tm_mday));
+ max6902_set_reg(dev, MAX6902_REG_MONTH, bin2bcd(dt->tm_mon + 1));
+ max6902_set_reg(dev, MAX6902_REG_DAY, bin2bcd(dt->tm_wday));
+ max6902_set_reg(dev, MAX6902_REG_YEAR, bin2bcd(dt->tm_year % 100));
+ max6902_set_reg(dev, MAX6902_REG_CENTURY, bin2bcd(dt->tm_year / 100));
+
+ /* Compulab used a delay here. However, the datasheet
+ * does not mention a delay being required anywhere... */
+ /* delay(2000); */
+
+ /* Write protect */
+ max6902_set_reg(dev, MAX6902_REG_CONTROL, 0x80);
+
+ return 0;
+}
+
+static const struct rtc_class_ops max6902_rtc_ops = {
+ .read_time = max6902_read_time,
+ .set_time = max6902_set_time,
+};
+
+static int max6902_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ unsigned char tmp;
+ int res;
+
+ spi->mode = SPI_MODE_3;
+ spi->bits_per_word = 8;
+ spi_setup(spi);
+
+ res = max6902_get_reg(&spi->dev, MAX6902_REG_SECONDS, &tmp);
+ if (res != 0)
+ return res;
+
+ rtc = devm_rtc_device_register(&spi->dev, "max6902",
+ &max6902_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+ return 0;
+}
+
+static struct spi_driver max6902_driver = {
+ .driver = {
+ .name = "rtc-max6902",
+ .owner = THIS_MODULE,
+ },
+ .probe = max6902_probe,
+};
+
+module_spi_driver(max6902_driver);
+
+MODULE_DESCRIPTION("max6902 spi RTC driver");
+MODULE_AUTHOR("Raphael Assenat");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-max6902");
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
new file mode 100644
index 000000000..7632a8778
--- /dev/null
+++ b/drivers/rtc/rtc-max77686.c
@@ -0,0 +1,528 @@
+/*
+ * RTC driver for Maxim MAX77686
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ * based on rtc-max8997.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT 0
+#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT 1
+#define MODEL24_MASK (1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT 0
+#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT 4
+#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT 6
+#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT 7
+#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
+
+#define MAX77686_RTC_UPDATE_DELAY 16
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_DATE,
+ RTC_NR_TIME
+};
+
+struct max77686_rtc_info {
+ struct device *dev;
+ struct max77686_dev *max77686;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ struct mutex lock;
+
+ struct regmap *regmap;
+
+ int virq;
+ int rtc_24hr_mode;
+};
+
+enum MAX77686_RTC_OP {
+ MAX77686_RTC_WRITE,
+ MAX77686_RTC_READ,
+};
+
+static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+ int rtc_24hr_mode)
+{
+ tm->tm_sec = data[RTC_SEC] & 0x7f;
+ tm->tm_min = data[RTC_MIN] & 0x7f;
+ if (rtc_24hr_mode)
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
+ else {
+ tm->tm_hour = data[RTC_HOUR] & 0x0f;
+ if (data[RTC_HOUR] & HOUR_PM_MASK)
+ tm->tm_hour += 12;
+ }
+
+ /* Only a single bit is set in data[], so fls() would be equivalent */
+ tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f) - 1;
+ tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+ tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+}
+
+static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = tm->tm_sec;
+ data[RTC_MIN] = tm->tm_min;
+ data[RTC_HOUR] = tm->tm_hour;
+ data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+ data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTH] = tm->tm_mon + 1;
+ data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
+
+ if (tm->tm_year < 100) {
+ pr_warn("RTC cannot handle the year %d. Assume it's 2000.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int max77686_rtc_update(struct max77686_rtc_info *info,
+ enum MAX77686_RTC_OP op)
+{
+ int ret;
+ unsigned int data;
+
+ if (op == MAX77686_RTC_WRITE)
+ data = 1 << RTC_UDR_SHIFT;
+ else
+ data = 1 << RTC_RBUDR_SHIFT;
+
+ ret = regmap_update_bits(info->max77686->rtc_regmap,
+ MAX77686_RTC_UPDATE0, data, data);
+ if (ret < 0)
+ dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
+ __func__, ret, data);
+ else {
+ /* Minimum 16ms delay required before RTC update. */
+ msleep(MAX77686_RTC_UPDATE_DELAY);
+ }
+
+ return ret;
+}
+
+static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_RTC_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+ ret = rtc_valid_tm(tm);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77686_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77686_rtc_tm_to_data(tm, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_RTC_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77686_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ unsigned int val;
+ int i, ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+ alrm->enabled = 0;
+ for (i = 0; i < RTC_NR_TIME; i++) {
+ if (data[i] & ALARM_ENABLE_MASK) {
+ alrm->enabled = 1;
+ break;
+ }
+ }
+
+ alrm->pending = 0;
+ ret = regmap_read(info->max77686->regmap, MAX77686_REG_STATUS2, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ if (val & (1 << 4)) /* RTCA1 */
+ alrm->pending = 1;
+
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info)
+{
+ u8 data[RTC_NR_TIME];
+ int ret, i;
+ struct rtc_time tm;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
+
+ for (i = 0; i < RTC_NR_TIME; i++)
+ data[i] &= ~ALARM_ENABLE_MASK;
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
+{
+ u8 data[RTC_NR_TIME];
+ int ret;
+ struct rtc_time tm;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ max77686_rtc_data_to_tm(data, &tm, info->rtc_24hr_mode);
+
+ data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
+ if (data[RTC_MONTH] & 0xf)
+ data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_YEAR] & 0x7f)
+ data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_DATE] & 0x1f)
+ data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77686_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77686_rtc_tm_to_data(&alrm->time, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77686_rtc_stop_alarm(info);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap,
+ MAX77686_ALARM1_SEC, data, RTC_NR_TIME);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+ if (ret < 0)
+ goto out;
+
+ if (alrm->enabled)
+ ret = max77686_rtc_start_alarm(info);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77686_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&info->lock);
+ if (enabled)
+ ret = max77686_rtc_start_alarm(info);
+ else
+ ret = max77686_rtc_stop_alarm(info);
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static irqreturn_t max77686_rtc_alarm_irq(int irq, void *data)
+{
+ struct max77686_rtc_info *info = data;
+
+ dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77686_rtc_ops = {
+ .read_time = max77686_rtc_read_time,
+ .set_time = max77686_rtc_set_time,
+ .read_alarm = max77686_rtc_read_alarm,
+ .set_alarm = max77686_rtc_set_alarm,
+ .alarm_irq_enable = max77686_rtc_alarm_irq_enable,
+};
+
+static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
+{
+ u8 data[2];
+ int ret;
+
+ /* Set RTC control register : Binary mode, 24hour mdoe */
+ data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+ info->rtc_24hr_mode = 1;
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap, MAX77686_RTC_CONTROLM, data, 2);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = max77686_rtc_update(info, MAX77686_RTC_WRITE);
+ return ret;
+}
+
+static int max77686_rtc_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *max77686 = dev_get_drvdata(pdev->dev.parent);
+ struct max77686_rtc_info *info;
+ int ret;
+
+ dev_info(&pdev->dev, "%s\n", __func__);
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct max77686_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ info->max77686 = max77686;
+ info->rtc = max77686->rtc;
+
+ platform_set_drvdata(pdev, info);
+
+ ret = max77686_rtc_init_reg(info);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+ goto err_rtc;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77686-rtc",
+ &max77686_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ if (ret == 0)
+ ret = -EINVAL;
+ goto err_rtc;
+ }
+
+ if (!max77686->rtc_irq_data) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "%s: no RTC regmap IRQ chip\n", __func__);
+ goto err_rtc;
+ }
+
+ info->virq = regmap_irq_get_virq(max77686->rtc_irq_data,
+ MAX77686_RTCIRQ_RTCA1);
+ if (!info->virq) {
+ ret = -ENXIO;
+ goto err_rtc;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL,
+ max77686_rtc_alarm_irq, 0, "rtc-alarm1", info);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->virq, ret);
+
+err_rtc:
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77686_rtc_suspend(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+
+ return enable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+
+static int max77686_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+
+ return disable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77686_rtc_pm_ops,
+ max77686_rtc_suspend, max77686_rtc_resume);
+
+static const struct platform_device_id rtc_id[] = {
+ { "max77686-rtc", 0 },
+ {},
+};
+
+static struct platform_driver max77686_rtc_driver = {
+ .driver = {
+ .name = "max77686-rtc",
+ .pm = &max77686_rtc_pm_ops,
+ },
+ .probe = max77686_rtc_probe,
+ .id_table = rtc_id,
+};
+
+module_platform_driver(max77686_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77686 RTC driver");
+MODULE_AUTHOR("Chiwoong Byun <woong.byun@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-max77802.c b/drivers/rtc/rtc-max77802.c
new file mode 100644
index 000000000..7f8adf8d6
--- /dev/null
+++ b/drivers/rtc/rtc-max77802.c
@@ -0,0 +1,501 @@
+/*
+ * RTC driver for Maxim MAX77802
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *
+ * based on rtc-max8997.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max77686-private.h>
+#include <linux/irqdomain.h>
+#include <linux/regmap.h>
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT 0
+#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT 1
+#define MODEL24_MASK (1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT 0
+#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
+#define RTC_RBUDR_SHIFT 4
+#define RTC_RBUDR_MASK (1 << RTC_RBUDR_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT 6
+#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT 7
+#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
+
+/* For the RTCAE1 register, we write this value to enable the alarm */
+#define ALARM_ENABLE_VALUE 0x77
+
+#define MAX77802_RTC_UPDATE_DELAY_US 200
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_DATE,
+ RTC_NR_TIME
+};
+
+struct max77802_rtc_info {
+ struct device *dev;
+ struct max77686_dev *max77802;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ struct mutex lock;
+
+ struct regmap *regmap;
+
+ int virq;
+ int rtc_24hr_mode;
+};
+
+enum MAX77802_RTC_OP {
+ MAX77802_RTC_WRITE,
+ MAX77802_RTC_READ,
+};
+
+static void max77802_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+ int rtc_24hr_mode)
+{
+ tm->tm_sec = data[RTC_SEC] & 0xff;
+ tm->tm_min = data[RTC_MIN] & 0xff;
+ if (rtc_24hr_mode)
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
+ else {
+ tm->tm_hour = data[RTC_HOUR] & 0x0f;
+ if (data[RTC_HOUR] & HOUR_PM_MASK)
+ tm->tm_hour += 12;
+ }
+
+ /* Only a single bit is set in data[], so fls() would be equivalent */
+ tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0xff) - 1;
+ tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+
+ tm->tm_year = data[RTC_YEAR] & 0xff;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+}
+
+static int max77802_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = tm->tm_sec;
+ data[RTC_MIN] = tm->tm_min;
+ data[RTC_HOUR] = tm->tm_hour;
+ data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+ data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTH] = tm->tm_mon + 1;
+ data[RTC_YEAR] = tm->tm_year;
+
+ return 0;
+}
+
+static int max77802_rtc_update(struct max77802_rtc_info *info,
+ enum MAX77802_RTC_OP op)
+{
+ int ret;
+ unsigned int data;
+
+ if (op == MAX77802_RTC_WRITE)
+ data = 1 << RTC_UDR_SHIFT;
+ else
+ data = 1 << RTC_RBUDR_SHIFT;
+
+ ret = regmap_update_bits(info->max77802->regmap,
+ MAX77802_RTC_UPDATE0, data, data);
+ if (ret < 0)
+ dev_err(info->dev, "%s: fail to write update reg(ret=%d, data=0x%x)\n",
+ __func__, ret, data);
+ else {
+ /* Minimum delay required before RTC update. */
+ usleep_range(MAX77802_RTC_UPDATE_DELAY_US,
+ MAX77802_RTC_UPDATE_DELAY_US * 2);
+ }
+
+ return ret;
+}
+
+static int max77802_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77802->regmap,
+ MAX77802_RTC_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ max77802_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+ ret = rtc_valid_tm(tm);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77802_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77802_rtc_tm_to_data(tm, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = regmap_bulk_write(info->max77802->regmap,
+ MAX77802_RTC_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77802_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ unsigned int val;
+ int ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_read(info->max77802->regmap,
+ MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ max77802_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+ alrm->enabled = 0;
+ ret = regmap_read(info->max77802->regmap,
+ MAX77802_RTC_AE1, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm enable(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+ if (val)
+ alrm->enabled = 1;
+
+ alrm->pending = 0;
+ ret = regmap_read(info->max77802->regmap, MAX77802_REG_STATUS2, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read status2 reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ if (val & (1 << 2)) /* RTCA1 */
+ alrm->pending = 1;
+
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int max77802_rtc_stop_alarm(struct max77802_rtc_info *info)
+{
+ int ret;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(info->max77802->regmap,
+ MAX77802_RTC_AE1, 0);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77802_rtc_start_alarm(struct max77802_rtc_info *info)
+{
+ int ret;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n",
+ __func__);
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_READ);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_write(info->max77802->regmap,
+ MAX77802_RTC_AE1,
+ ALARM_ENABLE_VALUE);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+out:
+ return ret;
+}
+
+static int max77802_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max77802_rtc_tm_to_data(&alrm->time, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max77802_rtc_stop_alarm(info);
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_bulk_write(info->max77802->regmap,
+ MAX77802_ALARM1_SEC, data, RTC_NR_TIME);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+ if (ret < 0)
+ goto out;
+
+ if (alrm->enabled)
+ ret = max77802_rtc_start_alarm(info);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max77802_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&info->lock);
+ if (enabled)
+ ret = max77802_rtc_start_alarm(info);
+ else
+ ret = max77802_rtc_stop_alarm(info);
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static irqreturn_t max77802_rtc_alarm_irq(int irq, void *data)
+{
+ struct max77802_rtc_info *info = data;
+
+ dev_dbg(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max77802_rtc_ops = {
+ .read_time = max77802_rtc_read_time,
+ .set_time = max77802_rtc_set_time,
+ .read_alarm = max77802_rtc_read_alarm,
+ .set_alarm = max77802_rtc_set_alarm,
+ .alarm_irq_enable = max77802_rtc_alarm_irq_enable,
+};
+
+static int max77802_rtc_init_reg(struct max77802_rtc_info *info)
+{
+ u8 data[2];
+ int ret;
+
+ max77802_rtc_update(info, MAX77802_RTC_READ);
+
+ /* Set RTC control register : Binary mode, 24hour mdoe */
+ data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+ info->rtc_24hr_mode = 1;
+
+ ret = regmap_bulk_write(info->max77802->regmap,
+ MAX77802_RTC_CONTROLM, data, ARRAY_SIZE(data));
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = max77802_rtc_update(info, MAX77802_RTC_WRITE);
+ return ret;
+}
+
+static int max77802_rtc_probe(struct platform_device *pdev)
+{
+ struct max77686_dev *max77802 = dev_get_drvdata(pdev->dev.parent);
+ struct max77802_rtc_info *info;
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s\n", __func__);
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct max77802_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ info->max77802 = max77802;
+ info->rtc = max77802->i2c;
+
+ platform_set_drvdata(pdev, info);
+
+ ret = max77802_rtc_init_reg(info);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77802-rtc",
+ &max77802_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ if (ret == 0)
+ ret = -EINVAL;
+ return ret;
+ }
+
+ if (!max77802->rtc_irq_data) {
+ dev_err(&pdev->dev, "No RTC regmap IRQ chip\n");
+ return -EINVAL;
+ }
+
+ info->virq = regmap_irq_get_virq(max77802->rtc_irq_data,
+ MAX77686_RTCIRQ_RTCA1);
+
+ if (info->virq <= 0) {
+ dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
+ MAX77686_RTCIRQ_RTCA1);
+ return -EINVAL;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->virq, NULL,
+ max77802_rtc_alarm_irq, 0, "rtc-alarm1",
+ info);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->virq, ret);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max77802_rtc_suspend(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+ return enable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+
+static int max77802_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev)) {
+ struct max77802_rtc_info *info = dev_get_drvdata(dev);
+
+ return disable_irq_wake(info->virq);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max77802_rtc_pm_ops,
+ max77802_rtc_suspend, max77802_rtc_resume);
+
+static const struct platform_device_id rtc_id[] = {
+ { "max77802-rtc", 0 },
+ {},
+};
+
+static struct platform_driver max77802_rtc_driver = {
+ .driver = {
+ .name = "max77802-rtc",
+ .pm = &max77802_rtc_pm_ops,
+ },
+ .probe = max77802_rtc_probe,
+ .id_table = rtc_id,
+};
+
+module_platform_driver(max77802_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX77802 RTC driver");
+MODULE_AUTHOR("Simon Glass <sjg@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-max8907.c b/drivers/rtc/rtc-max8907.c
new file mode 100644
index 000000000..19c29b725
--- /dev/null
+++ b/drivers/rtc/rtc-max8907.c
@@ -0,0 +1,224 @@
+/*
+ * RTC driver for Maxim MAX8907
+ *
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
+ *
+ * Based on drivers/rtc/rtc-max8925.c,
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+};
+
+#define TIME_NUM 8
+#define ALARM_1SEC (1 << 7)
+#define HOUR_12 (1 << 7)
+#define HOUR_AM_PM (1 << 5)
+#define ALARM0_IRQ (1 << 3)
+#define ALARM1_IRQ (1 << 2)
+#define ALARM0_STATUS (1 << 2)
+#define ALARM1_STATUS (1 << 1)
+
+struct max8907_rtc {
+ struct max8907 *max8907;
+ struct regmap *regmap;
+ struct rtc_device *rtc_dev;
+ int irq;
+};
+
+static irqreturn_t max8907_irq_handler(int irq, void *data)
+{
+ struct max8907_rtc *rtc = data;
+
+ regmap_write(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0);
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static void regs_to_tm(u8 *regs, struct rtc_time *tm)
+{
+ tm->tm_year = bcd2bin(regs[RTC_YEAR2]) * 100 +
+ bcd2bin(regs[RTC_YEAR1]) - 1900;
+ tm->tm_mon = bcd2bin(regs[RTC_MONTH] & 0x1f) - 1;
+ tm->tm_mday = bcd2bin(regs[RTC_DATE] & 0x3f);
+ tm->tm_wday = (regs[RTC_WEEKDAY] & 0x07);
+ if (regs[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = bcd2bin(regs[RTC_HOUR] & 0x01f);
+ if (tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ if (regs[RTC_HOUR] & HOUR_AM_PM)
+ tm->tm_hour += 12;
+ } else {
+ tm->tm_hour = bcd2bin(regs[RTC_HOUR] & 0x03f);
+ }
+ tm->tm_min = bcd2bin(regs[RTC_MIN] & 0x7f);
+ tm->tm_sec = bcd2bin(regs[RTC_SEC] & 0x7f);
+}
+
+static void tm_to_regs(struct rtc_time *tm, u8 *regs)
+{
+ u8 high, low;
+
+ high = (tm->tm_year + 1900) / 100;
+ low = tm->tm_year % 100;
+ regs[RTC_YEAR2] = bin2bcd(high);
+ regs[RTC_YEAR1] = bin2bcd(low);
+ regs[RTC_MONTH] = bin2bcd(tm->tm_mon + 1);
+ regs[RTC_DATE] = bin2bcd(tm->tm_mday);
+ regs[RTC_WEEKDAY] = tm->tm_wday;
+ regs[RTC_HOUR] = bin2bcd(tm->tm_hour);
+ regs[RTC_MIN] = bin2bcd(tm->tm_min);
+ regs[RTC_SEC] = bin2bcd(tm->tm_sec);
+}
+
+static int max8907_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8907_rtc *rtc = dev_get_drvdata(dev);
+ u8 regs[TIME_NUM];
+ int ret;
+
+ ret = regmap_bulk_read(rtc->regmap, MAX8907_REG_RTC_SEC, regs,
+ TIME_NUM);
+ if (ret < 0)
+ return ret;
+
+ regs_to_tm(regs, tm);
+
+ return 0;
+}
+
+static int max8907_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8907_rtc *rtc = dev_get_drvdata(dev);
+ u8 regs[TIME_NUM];
+
+ tm_to_regs(tm, regs);
+
+ return regmap_bulk_write(rtc->regmap, MAX8907_REG_RTC_SEC, regs,
+ TIME_NUM);
+}
+
+static int max8907_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8907_rtc *rtc = dev_get_drvdata(dev);
+ u8 regs[TIME_NUM];
+ unsigned int val;
+ int ret;
+
+ ret = regmap_bulk_read(rtc->regmap, MAX8907_REG_ALARM0_SEC, regs,
+ TIME_NUM);
+ if (ret < 0)
+ return ret;
+
+ regs_to_tm(regs, &alrm->time);
+
+ ret = regmap_read(rtc->regmap, MAX8907_REG_ALARM0_CNTL, &val);
+ if (ret < 0)
+ return ret;
+
+ alrm->enabled = !!(val & 0x7f);
+
+ return 0;
+}
+
+static int max8907_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8907_rtc *rtc = dev_get_drvdata(dev);
+ u8 regs[TIME_NUM];
+ int ret;
+
+ tm_to_regs(&alrm->time, regs);
+
+ /* Disable alarm while we update the target time */
+ ret = regmap_write(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_write(rtc->regmap, MAX8907_REG_ALARM0_SEC, regs,
+ TIME_NUM);
+ if (ret < 0)
+ return ret;
+
+ if (alrm->enabled)
+ ret = regmap_write(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0x77);
+
+ return ret;
+}
+
+static const struct rtc_class_ops max8907_rtc_ops = {
+ .read_time = max8907_rtc_read_time,
+ .set_time = max8907_rtc_set_time,
+ .read_alarm = max8907_rtc_read_alarm,
+ .set_alarm = max8907_rtc_set_alarm,
+};
+
+static int max8907_rtc_probe(struct platform_device *pdev)
+{
+ struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent);
+ struct max8907_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, rtc);
+
+ rtc->max8907 = max8907;
+ rtc->regmap = max8907->regmap_rtc;
+
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8907-rtc",
+ &max8907_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ ret = PTR_ERR(rtc->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+
+ rtc->irq = regmap_irq_get_virq(max8907->irqc_rtc,
+ MAX8907_IRQ_RTC_ALARM0);
+ if (rtc->irq < 0)
+ return rtc->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+ max8907_irq_handler,
+ IRQF_ONESHOT, "max8907-alarm0", rtc);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request IRQ%d: %d\n",
+ rtc->irq, ret);
+
+ return ret;
+}
+
+static struct platform_driver max8907_rtc_driver = {
+ .driver = {
+ .name = "max8907-rtc",
+ },
+ .probe = max8907_rtc_probe,
+};
+module_platform_driver(max8907_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX8907 RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c
new file mode 100644
index 000000000..16d129a0b
--- /dev/null
+++ b/drivers/rtc/rtc-max8925.c
@@ -0,0 +1,327 @@
+/*
+ * RTC driver for Maxim MAX8925
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max8925.h>
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+};
+
+#define MAX8925_RTC_SEC 0x00
+#define MAX8925_RTC_MIN 0x01
+#define MAX8925_RTC_HOUR 0x02
+#define MAX8925_RTC_WEEKDAY 0x03
+#define MAX8925_RTC_DATE 0x04
+#define MAX8925_RTC_MONTH 0x05
+#define MAX8925_RTC_YEAR1 0x06
+#define MAX8925_RTC_YEAR2 0x07
+#define MAX8925_ALARM0_SEC 0x08
+#define MAX8925_ALARM0_MIN 0x09
+#define MAX8925_ALARM0_HOUR 0x0a
+#define MAX8925_ALARM0_WEEKDAY 0x0b
+#define MAX8925_ALARM0_DATE 0x0c
+#define MAX8925_ALARM0_MON 0x0d
+#define MAX8925_ALARM0_YEAR1 0x0e
+#define MAX8925_ALARM0_YEAR2 0x0f
+#define MAX8925_ALARM1_SEC 0x10
+#define MAX8925_ALARM1_MIN 0x11
+#define MAX8925_ALARM1_HOUR 0x12
+#define MAX8925_ALARM1_WEEKDAY 0x13
+#define MAX8925_ALARM1_DATE 0x14
+#define MAX8925_ALARM1_MON 0x15
+#define MAX8925_ALARM1_YEAR1 0x16
+#define MAX8925_ALARM1_YEAR2 0x17
+#define MAX8925_RTC_CNTL 0x1b
+#define MAX8925_RTC_STATUS 0x20
+
+#define TIME_NUM 8
+#define ALARM_1SEC (1 << 7)
+#define HOUR_12 (1 << 7)
+#define HOUR_AM_PM (1 << 5)
+#define ALARM0_IRQ (1 << 3)
+#define ALARM1_IRQ (1 << 2)
+#define ALARM0_STATUS (1 << 2)
+#define ALARM1_STATUS (1 << 1)
+
+
+struct max8925_rtc_info {
+ struct rtc_device *rtc_dev;
+ struct max8925_chip *chip;
+ struct i2c_client *rtc;
+ struct device *dev;
+ int irq;
+};
+
+static irqreturn_t rtc_update_handler(int irq, void *data)
+{
+ struct max8925_rtc_info *info = (struct max8925_rtc_info *)data;
+
+ /* disable ALARM0 except for 1SEC alarm */
+ max8925_set_bits(info->rtc, MAX8925_ALARM0_CNTL, 0x7f, 0);
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int tm_calc(struct rtc_time *tm, unsigned char *buf, int len)
+{
+ if (len < TIME_NUM)
+ return -EINVAL;
+ tm->tm_year = (buf[RTC_YEAR2] >> 4) * 1000
+ + (buf[RTC_YEAR2] & 0xf) * 100
+ + (buf[RTC_YEAR1] >> 4) * 10
+ + (buf[RTC_YEAR1] & 0xf);
+ tm->tm_year -= 1900;
+ tm->tm_mon = ((buf[RTC_MONTH] >> 4) & 0x01) * 10
+ + (buf[RTC_MONTH] & 0x0f);
+ tm->tm_mday = ((buf[RTC_DATE] >> 4) & 0x03) * 10
+ + (buf[RTC_DATE] & 0x0f);
+ tm->tm_wday = buf[RTC_WEEKDAY] & 0x07;
+ if (buf[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x1) * 10
+ + (buf[RTC_HOUR] & 0x0f);
+ if (buf[RTC_HOUR] & HOUR_AM_PM)
+ tm->tm_hour += 12;
+ } else
+ tm->tm_hour = ((buf[RTC_HOUR] >> 4) & 0x03) * 10
+ + (buf[RTC_HOUR] & 0x0f);
+ tm->tm_min = ((buf[RTC_MIN] >> 4) & 0x7) * 10
+ + (buf[RTC_MIN] & 0x0f);
+ tm->tm_sec = ((buf[RTC_SEC] >> 4) & 0x7) * 10
+ + (buf[RTC_SEC] & 0x0f);
+ return 0;
+}
+
+static int data_calc(unsigned char *buf, struct rtc_time *tm, int len)
+{
+ unsigned char high, low;
+
+ if (len < TIME_NUM)
+ return -EINVAL;
+
+ high = (tm->tm_year + 1900) / 1000;
+ low = (tm->tm_year + 1900) / 100;
+ low = low - high * 10;
+ buf[RTC_YEAR2] = (high << 4) + low;
+ high = (tm->tm_year + 1900) / 10;
+ low = tm->tm_year + 1900;
+ low = low - high * 10;
+ high = high - (high / 10) * 10;
+ buf[RTC_YEAR1] = (high << 4) + low;
+ high = tm->tm_mon / 10;
+ low = tm->tm_mon;
+ low = low - high * 10;
+ buf[RTC_MONTH] = (high << 4) + low;
+ high = tm->tm_mday / 10;
+ low = tm->tm_mday;
+ low = low - high * 10;
+ buf[RTC_DATE] = (high << 4) + low;
+ buf[RTC_WEEKDAY] = tm->tm_wday;
+ high = tm->tm_hour / 10;
+ low = tm->tm_hour;
+ low = low - high * 10;
+ buf[RTC_HOUR] = (high << 4) + low;
+ high = tm->tm_min / 10;
+ low = tm->tm_min;
+ low = low - high * 10;
+ buf[RTC_MIN] = (high << 4) + low;
+ high = tm->tm_sec / 10;
+ low = tm->tm_sec;
+ low = low - high * 10;
+ buf[RTC_SEC] = (high << 4) + low;
+ return 0;
+}
+
+static int max8925_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = max8925_bulk_read(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ goto out;
+ ret = tm_calc(tm, buf, TIME_NUM);
+out:
+ return ret;
+}
+
+static int max8925_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = data_calc(buf, tm, TIME_NUM);
+ if (ret < 0)
+ goto out;
+ ret = max8925_bulk_write(info->rtc, MAX8925_RTC_SEC, TIME_NUM, buf);
+out:
+ return ret;
+}
+
+static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = max8925_bulk_read(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ goto out;
+ ret = tm_calc(&alrm->time, buf, TIME_NUM);
+ if (ret < 0)
+ goto out;
+ ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
+ if (ret < 0)
+ goto out;
+ if (ret & ALARM0_IRQ) {
+ alrm->enabled = 0;
+ } else {
+ ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL);
+ if (ret < 0)
+ goto out;
+ if (!ret)
+ alrm->enabled = 0;
+ else
+ alrm->enabled = 1;
+ }
+ ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
+ if (ret < 0)
+ goto out;
+ if (ret & ALARM0_STATUS)
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+ return 0;
+out:
+ return ret;
+}
+
+static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8925_rtc_info *info = dev_get_drvdata(dev);
+ unsigned char buf[TIME_NUM];
+ int ret;
+
+ ret = data_calc(buf, &alrm->time, TIME_NUM);
+ if (ret < 0)
+ goto out;
+ ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
+ if (ret < 0)
+ goto out;
+ if (alrm->enabled)
+ /* only enable alarm on year/month/day/hour/min/sec */
+ ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
+ else
+ ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0);
+ if (ret < 0)
+ goto out;
+out:
+ return ret;
+}
+
+static const struct rtc_class_ops max8925_rtc_ops = {
+ .read_time = max8925_rtc_read_time,
+ .set_time = max8925_rtc_set_time,
+ .read_alarm = max8925_rtc_read_alarm,
+ .set_alarm = max8925_rtc_set_alarm,
+};
+
+static int max8925_rtc_probe(struct platform_device *pdev)
+{
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct max8925_rtc_info *info;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct max8925_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->chip = chip;
+ info->rtc = chip->rtc;
+ info->dev = &pdev->dev;
+ info->irq = platform_get_irq(pdev, 0);
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+ rtc_update_handler, IRQF_ONESHOT,
+ "rtc-alarm0", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&pdev->dev, info);
+ /* XXX - isn't this redundant? */
+ platform_set_drvdata(pdev, info);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8925-rtc",
+ &max8925_rtc_ops, THIS_MODULE);
+ ret = PTR_ERR(info->rtc_dev);
+ if (IS_ERR(info->rtc_dev)) {
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max8925_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag |= 1 << MAX8925_IRQ_RTC_ALARM0;
+ return 0;
+}
+static int max8925_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag &= ~(1 << MAX8925_IRQ_RTC_ALARM0);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8925_rtc_pm_ops, max8925_rtc_suspend, max8925_rtc_resume);
+
+static struct platform_driver max8925_rtc_driver = {
+ .driver = {
+ .name = "max8925-rtc",
+ .pm = &max8925_rtc_pm_ops,
+ },
+ .probe = max8925_rtc_probe,
+};
+
+module_platform_driver(max8925_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX8925 RTC driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-max8997.c b/drivers/rtc/rtc-max8997.c
new file mode 100644
index 000000000..9e02bcda0
--- /dev/null
+++ b/drivers/rtc/rtc-max8997.c
@@ -0,0 +1,538 @@
+/*
+ * RTC driver for Maxim MAX8997
+ *
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ *
+ * based on rtc-max8998.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max8997-private.h>
+#include <linux/irqdomain.h>
+
+/* Module parameter for WTSR function control */
+static int wtsr_en = 1;
+module_param(wtsr_en, int, 0444);
+MODULE_PARM_DESC(wtsr_en, "Watchdog Timeout & Software Reset (default=on)");
+/* Module parameter for SMPL function control */
+static int smpl_en = 1;
+module_param(smpl_en, int, 0444);
+MODULE_PARM_DESC(smpl_en, "Sudden Momentary Power Loss (default=on)");
+
+/* RTC Control Register */
+#define BCD_EN_SHIFT 0
+#define BCD_EN_MASK (1 << BCD_EN_SHIFT)
+#define MODEL24_SHIFT 1
+#define MODEL24_MASK (1 << MODEL24_SHIFT)
+/* RTC Update Register1 */
+#define RTC_UDR_SHIFT 0
+#define RTC_UDR_MASK (1 << RTC_UDR_SHIFT)
+/* WTSR and SMPL Register */
+#define WTSRT_SHIFT 0
+#define SMPLT_SHIFT 2
+#define WTSR_EN_SHIFT 6
+#define SMPL_EN_SHIFT 7
+#define WTSRT_MASK (3 << WTSRT_SHIFT)
+#define SMPLT_MASK (3 << SMPLT_SHIFT)
+#define WTSR_EN_MASK (1 << WTSR_EN_SHIFT)
+#define SMPL_EN_MASK (1 << SMPL_EN_SHIFT)
+/* RTC Hour register */
+#define HOUR_PM_SHIFT 6
+#define HOUR_PM_MASK (1 << HOUR_PM_SHIFT)
+/* RTC Alarm Enable */
+#define ALARM_ENABLE_SHIFT 7
+#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_MONTH,
+ RTC_YEAR,
+ RTC_DATE,
+ RTC_NR_TIME
+};
+
+struct max8997_rtc_info {
+ struct device *dev;
+ struct max8997_dev *max8997;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ struct mutex lock;
+ int virq;
+ int rtc_24hr_mode;
+};
+
+static void max8997_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
+ int rtc_24hr_mode)
+{
+ tm->tm_sec = data[RTC_SEC] & 0x7f;
+ tm->tm_min = data[RTC_MIN] & 0x7f;
+ if (rtc_24hr_mode)
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
+ else {
+ tm->tm_hour = data[RTC_HOUR] & 0x0f;
+ if (data[RTC_HOUR] & HOUR_PM_MASK)
+ tm->tm_hour += 12;
+ }
+
+ tm->tm_wday = fls(data[RTC_WEEKDAY] & 0x7f) - 1;
+ tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+ tm->tm_year = (data[RTC_YEAR] & 0x7f) + 100;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+}
+
+static int max8997_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = tm->tm_sec;
+ data[RTC_MIN] = tm->tm_min;
+ data[RTC_HOUR] = tm->tm_hour;
+ data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+ data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTH] = tm->tm_mon + 1;
+ data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
+
+ if (tm->tm_year < 100) {
+ pr_warn("RTC cannot handle the year %d. Assume it's 2000.\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static inline int max8997_rtc_set_update_reg(struct max8997_rtc_info *info)
+{
+ int ret;
+
+ ret = max8997_write_reg(info->rtc, MAX8997_RTC_UPDATE1,
+ RTC_UDR_MASK);
+ if (ret < 0)
+ dev_err(info->dev, "%s: fail to write update reg(%d)\n",
+ __func__, ret);
+ else {
+ /* Minimum 16ms delay required before RTC update.
+ * Otherwise, we may read and update based on out-of-date
+ * value */
+ msleep(20);
+ }
+
+ return ret;
+}
+
+static int max8997_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8997_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ mutex_lock(&info->lock);
+ ret = max8997_bulk_read(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
+ mutex_unlock(&info->lock);
+
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read time reg(%d)\n", __func__,
+ ret);
+ return ret;
+ }
+
+ max8997_rtc_data_to_tm(data, tm, info->rtc_24hr_mode);
+
+ return rtc_valid_tm(tm);
+}
+
+static int max8997_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8997_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max8997_rtc_tm_to_data(tm, data);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max8997_bulk_write(info->rtc, MAX8997_RTC_SEC, RTC_NR_TIME, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write time reg(%d)\n", __func__,
+ ret);
+ goto out;
+ }
+
+ ret = max8997_rtc_set_update_reg(info);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max8997_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8997_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ u8 val;
+ int i, ret;
+
+ mutex_lock(&info->lock);
+
+ ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
+ data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read alarm reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ max8997_rtc_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+
+ alrm->enabled = 0;
+ for (i = 0; i < RTC_NR_TIME; i++) {
+ if (data[i] & ALARM_ENABLE_MASK) {
+ alrm->enabled = 1;
+ break;
+ }
+ }
+
+ alrm->pending = 0;
+ ret = max8997_read_reg(info->max8997->i2c, MAX8997_REG_STATUS1, &val);
+ if (ret < 0) {
+ dev_err(info->dev, "%s:%d fail to read status1 reg(%d)\n",
+ __func__, __LINE__, ret);
+ goto out;
+ }
+
+ if (val & (1 << 4)) /* RTCA1 */
+ alrm->pending = 1;
+
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int max8997_rtc_stop_alarm(struct max8997_rtc_info *info)
+{
+ u8 data[RTC_NR_TIME];
+ int ret, i;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
+ data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ for (i = 0; i < RTC_NR_TIME; i++)
+ data[i] &= ~ALARM_ENABLE_MASK;
+
+ ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
+ data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max8997_rtc_set_update_reg(info);
+out:
+ return ret;
+}
+
+static int max8997_rtc_start_alarm(struct max8997_rtc_info *info)
+{
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ if (!mutex_is_locked(&info->lock))
+ dev_warn(info->dev, "%s: should have mutex locked\n", __func__);
+
+ ret = max8997_bulk_read(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
+ data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
+ if (data[RTC_MONTH] & 0xf)
+ data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_YEAR] & 0x7f)
+ data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_DATE] & 0x1f)
+ data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
+
+ ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
+ data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max8997_rtc_set_update_reg(info);
+out:
+ return ret;
+}
+static int max8997_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8997_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[RTC_NR_TIME];
+ int ret;
+
+ ret = max8997_rtc_tm_to_data(&alrm->time, data);
+ if (ret < 0)
+ return ret;
+
+ dev_info(info->dev, "%s: %d-%02d-%02d %02d:%02d:%02d\n", __func__,
+ data[RTC_YEAR] + 2000, data[RTC_MONTH], data[RTC_DATE],
+ data[RTC_HOUR], data[RTC_MIN], data[RTC_SEC]);
+
+ mutex_lock(&info->lock);
+
+ ret = max8997_rtc_stop_alarm(info);
+ if (ret < 0)
+ goto out;
+
+ ret = max8997_bulk_write(info->rtc, MAX8997_RTC_ALARM1_SEC, RTC_NR_TIME,
+ data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
+ __func__, ret);
+ goto out;
+ }
+
+ ret = max8997_rtc_set_update_reg(info);
+ if (ret < 0)
+ goto out;
+
+ if (alrm->enabled)
+ ret = max8997_rtc_start_alarm(info);
+out:
+ mutex_unlock(&info->lock);
+ return ret;
+}
+
+static int max8997_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max8997_rtc_info *info = dev_get_drvdata(dev);
+ int ret;
+
+ mutex_lock(&info->lock);
+ if (enabled)
+ ret = max8997_rtc_start_alarm(info);
+ else
+ ret = max8997_rtc_stop_alarm(info);
+ mutex_unlock(&info->lock);
+
+ return ret;
+}
+
+static irqreturn_t max8997_rtc_alarm_irq(int irq, void *data)
+{
+ struct max8997_rtc_info *info = data;
+
+ dev_info(info->dev, "%s:irq(%d)\n", __func__, irq);
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max8997_rtc_ops = {
+ .read_time = max8997_rtc_read_time,
+ .set_time = max8997_rtc_set_time,
+ .read_alarm = max8997_rtc_read_alarm,
+ .set_alarm = max8997_rtc_set_alarm,
+ .alarm_irq_enable = max8997_rtc_alarm_irq_enable,
+};
+
+static void max8997_rtc_enable_wtsr(struct max8997_rtc_info *info, bool enable)
+{
+ int ret;
+ u8 val, mask;
+
+ if (!wtsr_en)
+ return;
+
+ if (enable)
+ val = (1 << WTSR_EN_SHIFT) | (3 << WTSRT_SHIFT);
+ else
+ val = 0;
+
+ mask = WTSR_EN_MASK | WTSRT_MASK;
+
+ dev_info(info->dev, "%s: %s WTSR\n", __func__,
+ enable ? "enable" : "disable");
+
+ ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to update WTSR reg(%d)\n",
+ __func__, ret);
+ return;
+ }
+
+ max8997_rtc_set_update_reg(info);
+}
+
+static void max8997_rtc_enable_smpl(struct max8997_rtc_info *info, bool enable)
+{
+ int ret;
+ u8 val, mask;
+
+ if (!smpl_en)
+ return;
+
+ if (enable)
+ val = (1 << SMPL_EN_SHIFT) | (0 << SMPLT_SHIFT);
+ else
+ val = 0;
+
+ mask = SMPL_EN_MASK | SMPLT_MASK;
+
+ dev_info(info->dev, "%s: %s SMPL\n", __func__,
+ enable ? "enable" : "disable");
+
+ ret = max8997_update_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, val, mask);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to update SMPL reg(%d)\n",
+ __func__, ret);
+ return;
+ }
+
+ max8997_rtc_set_update_reg(info);
+
+ val = 0;
+ max8997_read_reg(info->rtc, MAX8997_RTC_WTSR_SMPL, &val);
+ pr_info("WTSR_SMPL(0x%02x)\n", val);
+}
+
+static int max8997_rtc_init_reg(struct max8997_rtc_info *info)
+{
+ u8 data[2];
+ int ret;
+
+ /* Set RTC control register : Binary mode, 24hour mdoe */
+ data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+ info->rtc_24hr_mode = 1;
+
+ ret = max8997_bulk_write(info->rtc, MAX8997_RTC_CTRLMASK, 2, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = max8997_rtc_set_update_reg(info);
+ return ret;
+}
+
+static int max8997_rtc_probe(struct platform_device *pdev)
+{
+ struct max8997_dev *max8997 = dev_get_drvdata(pdev->dev.parent);
+ struct max8997_rtc_info *info;
+ int ret, virq;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct max8997_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ mutex_init(&info->lock);
+ info->dev = &pdev->dev;
+ info->max8997 = max8997;
+ info->rtc = max8997->rtc;
+
+ platform_set_drvdata(pdev, info);
+
+ ret = max8997_rtc_init_reg(info);
+
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to initialize RTC reg:%d\n", ret);
+ return ret;
+ }
+
+ max8997_rtc_enable_wtsr(info, true);
+ max8997_rtc_enable_smpl(info, true);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8997-rtc",
+ &max8997_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+
+ virq = irq_create_mapping(max8997->irq_domain, MAX8997_PMICIRQ_RTCA1);
+ if (!virq) {
+ dev_err(&pdev->dev, "Failed to create mapping alarm IRQ\n");
+ ret = -ENXIO;
+ goto err_out;
+ }
+ info->virq = virq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
+ max8997_rtc_alarm_irq, 0,
+ "rtc-alarm0", info);
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->virq, ret);
+
+err_out:
+ return ret;
+}
+
+static void max8997_rtc_shutdown(struct platform_device *pdev)
+{
+ struct max8997_rtc_info *info = platform_get_drvdata(pdev);
+
+ max8997_rtc_enable_wtsr(info, false);
+ max8997_rtc_enable_smpl(info, false);
+}
+
+static const struct platform_device_id rtc_id[] = {
+ { "max8997-rtc", 0 },
+ {},
+};
+
+static struct platform_driver max8997_rtc_driver = {
+ .driver = {
+ .name = "max8997-rtc",
+ },
+ .probe = max8997_rtc_probe,
+ .shutdown = max8997_rtc_shutdown,
+ .id_table = rtc_id,
+};
+
+module_platform_driver(max8997_rtc_driver);
+
+MODULE_DESCRIPTION("Maxim MAX8997 RTC driver");
+MODULE_AUTHOR("<ms925.kim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-max8998.c b/drivers/rtc/rtc-max8998.c
new file mode 100644
index 000000000..5726ef7bd
--- /dev/null
+++ b/drivers/rtc/rtc-max8998.c
@@ -0,0 +1,326 @@
+/*
+ * RTC driver for Maxim MAX8998
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Author: Minkyu Kang <mk7.kang@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/bcd.h>
+#include <linux/irqdomain.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+#include <linux/delay.h>
+
+#define MAX8998_RTC_SEC 0x00
+#define MAX8998_RTC_MIN 0x01
+#define MAX8998_RTC_HOUR 0x02
+#define MAX8998_RTC_WEEKDAY 0x03
+#define MAX8998_RTC_DATE 0x04
+#define MAX8998_RTC_MONTH 0x05
+#define MAX8998_RTC_YEAR1 0x06
+#define MAX8998_RTC_YEAR2 0x07
+#define MAX8998_ALARM0_SEC 0x08
+#define MAX8998_ALARM0_MIN 0x09
+#define MAX8998_ALARM0_HOUR 0x0a
+#define MAX8998_ALARM0_WEEKDAY 0x0b
+#define MAX8998_ALARM0_DATE 0x0c
+#define MAX8998_ALARM0_MONTH 0x0d
+#define MAX8998_ALARM0_YEAR1 0x0e
+#define MAX8998_ALARM0_YEAR2 0x0f
+#define MAX8998_ALARM1_SEC 0x10
+#define MAX8998_ALARM1_MIN 0x11
+#define MAX8998_ALARM1_HOUR 0x12
+#define MAX8998_ALARM1_WEEKDAY 0x13
+#define MAX8998_ALARM1_DATE 0x14
+#define MAX8998_ALARM1_MONTH 0x15
+#define MAX8998_ALARM1_YEAR1 0x16
+#define MAX8998_ALARM1_YEAR2 0x17
+#define MAX8998_ALARM0_CONF 0x18
+#define MAX8998_ALARM1_CONF 0x19
+#define MAX8998_RTC_STATUS 0x1a
+#define MAX8998_WTSR_SMPL_CNTL 0x1b
+#define MAX8998_TEST 0x1f
+
+#define HOUR_12 (1 << 7)
+#define HOUR_PM (1 << 5)
+#define ALARM0_STATUS (1 << 1)
+#define ALARM1_STATUS (1 << 2)
+
+enum {
+ RTC_SEC = 0,
+ RTC_MIN,
+ RTC_HOUR,
+ RTC_WEEKDAY,
+ RTC_DATE,
+ RTC_MONTH,
+ RTC_YEAR1,
+ RTC_YEAR2,
+};
+
+struct max8998_rtc_info {
+ struct device *dev;
+ struct max8998_dev *max8998;
+ struct i2c_client *rtc;
+ struct rtc_device *rtc_dev;
+ int irq;
+ bool lp3974_bug_workaround;
+};
+
+static void max8998_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(data[RTC_SEC]);
+ tm->tm_min = bcd2bin(data[RTC_MIN]);
+ if (data[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
+ if (data[RTC_HOUR] & HOUR_PM)
+ tm->tm_hour += 12;
+ } else
+ tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
+
+ tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
+ tm->tm_mday = bcd2bin(data[RTC_DATE]);
+ tm->tm_mon = bcd2bin(data[RTC_MONTH]);
+ tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
+ tm->tm_year -= 1900;
+}
+
+static void max8998_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = bin2bcd(tm->tm_sec);
+ data[RTC_MIN] = bin2bcd(tm->tm_min);
+ data[RTC_HOUR] = bin2bcd(tm->tm_hour);
+ data[RTC_WEEKDAY] = tm->tm_wday;
+ data[RTC_DATE] = bin2bcd(tm->tm_mday);
+ data[RTC_MONTH] = bin2bcd(tm->tm_mon);
+ data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
+ data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
+}
+
+static int max8998_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ int ret;
+
+ ret = max8998_bulk_read(info->rtc, MAX8998_RTC_SEC, 8, data);
+ if (ret < 0)
+ return ret;
+
+ max8998_data_to_tm(data, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int max8998_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ int ret;
+
+ max8998_tm_to_data(tm, data);
+
+ ret = max8998_bulk_write(info->rtc, MAX8998_RTC_SEC, 8, data);
+
+ if (info->lp3974_bug_workaround)
+ msleep(2000);
+
+ return ret;
+}
+
+static int max8998_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ u8 val;
+ int ret;
+
+ ret = max8998_bulk_read(info->rtc, MAX8998_ALARM0_SEC, 8, data);
+ if (ret < 0)
+ return ret;
+
+ max8998_data_to_tm(data, &alrm->time);
+
+ ret = max8998_read_reg(info->rtc, MAX8998_ALARM0_CONF, &val);
+ if (ret < 0)
+ return ret;
+
+ alrm->enabled = !!val;
+
+ ret = max8998_read_reg(info->rtc, MAX8998_RTC_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & ALARM0_STATUS)
+ alrm->pending = 1;
+ else
+ alrm->pending = 0;
+
+ return 0;
+}
+
+static int max8998_rtc_stop_alarm(struct max8998_rtc_info *info)
+{
+ int ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, 0);
+
+ if (info->lp3974_bug_workaround)
+ msleep(2000);
+
+ return ret;
+}
+
+static int max8998_rtc_start_alarm(struct max8998_rtc_info *info)
+{
+ int ret;
+ u8 alarm0_conf = 0x77;
+
+ /* LP3974 with delay bug chips has rtc alarm bugs with "MONTH" field */
+ if (info->lp3974_bug_workaround)
+ alarm0_conf = 0x57;
+
+ ret = max8998_write_reg(info->rtc, MAX8998_ALARM0_CONF, alarm0_conf);
+
+ if (info->lp3974_bug_workaround)
+ msleep(2000);
+
+ return ret;
+}
+
+static int max8998_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[8];
+ int ret;
+
+ max8998_tm_to_data(&alrm->time, data);
+
+ ret = max8998_rtc_stop_alarm(info);
+ if (ret < 0)
+ return ret;
+
+ ret = max8998_bulk_write(info->rtc, MAX8998_ALARM0_SEC, 8, data);
+ if (ret < 0)
+ return ret;
+
+ if (info->lp3974_bug_workaround)
+ msleep(2000);
+
+ if (alrm->enabled)
+ ret = max8998_rtc_start_alarm(info);
+
+ return ret;
+}
+
+static int max8998_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct max8998_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ return max8998_rtc_start_alarm(info);
+ else
+ return max8998_rtc_stop_alarm(info);
+}
+
+static irqreturn_t max8998_rtc_alarm_irq(int irq, void *data)
+{
+ struct max8998_rtc_info *info = data;
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops max8998_rtc_ops = {
+ .read_time = max8998_rtc_read_time,
+ .set_time = max8998_rtc_set_time,
+ .read_alarm = max8998_rtc_read_alarm,
+ .set_alarm = max8998_rtc_set_alarm,
+ .alarm_irq_enable = max8998_rtc_alarm_irq_enable,
+};
+
+static int max8998_rtc_probe(struct platform_device *pdev)
+{
+ struct max8998_dev *max8998 = dev_get_drvdata(pdev->dev.parent);
+ struct max8998_platform_data *pdata = max8998->pdata;
+ struct max8998_rtc_info *info;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct max8998_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &pdev->dev;
+ info->max8998 = max8998;
+ info->rtc = max8998->rtc;
+
+ platform_set_drvdata(pdev, info);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max8998-rtc",
+ &max8998_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
+ return ret;
+ }
+
+ if (!max8998->irq_domain)
+ goto no_irq;
+
+ info->irq = irq_create_mapping(max8998->irq_domain, MAX8998_IRQ_ALARM0);
+ if (!info->irq) {
+ dev_warn(&pdev->dev, "Failed to map alarm IRQ\n");
+ goto no_irq;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+ max8998_rtc_alarm_irq, 0, "rtc-alarm0", info);
+
+ if (ret < 0)
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->irq, ret);
+
+no_irq:
+ dev_info(&pdev->dev, "RTC CHIP NAME: %s\n", pdev->id_entry->name);
+ if (pdata && pdata->rtc_delay) {
+ info->lp3974_bug_workaround = true;
+ dev_warn(&pdev->dev, "LP3974 with RTC REGERR option."
+ " RTC updates will be extremely slow.\n");
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id max8998_rtc_id[] = {
+ { "max8998-rtc", TYPE_MAX8998 },
+ { "lp3974-rtc", TYPE_LP3974 },
+ { }
+};
+
+static struct platform_driver max8998_rtc_driver = {
+ .driver = {
+ .name = "max8998-rtc",
+ },
+ .probe = max8998_rtc_probe,
+ .id_table = max8998_rtc_id,
+};
+
+module_platform_driver(max8998_rtc_driver);
+
+MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Maxim MAX8998 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-mc13xxx.c b/drivers/rtc/rtc-mc13xxx.c
new file mode 100644
index 000000000..32df1d812
--- /dev/null
+++ b/drivers/rtc/rtc-mc13xxx.c
@@ -0,0 +1,376 @@
+/*
+ * Real Time Clock driver for Freescale MC13XXX PMIC
+ *
+ * (C) 2009 Sascha Hauer, Pengutronix
+ * (C) 2009 Uwe Kleine-Koenig, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mfd/mc13xxx.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+
+#define DRIVER_NAME "mc13xxx-rtc"
+
+#define MC13XXX_RTCTOD 20
+#define MC13XXX_RTCTODA 21
+#define MC13XXX_RTCDAY 22
+#define MC13XXX_RTCDAYA 23
+
+#define SEC_PER_DAY (24 * 60 * 60)
+
+struct mc13xxx_rtc {
+ struct rtc_device *rtc;
+ struct mc13xxx *mc13xxx;
+ int valid;
+};
+
+static int mc13xxx_rtc_irq_enable_unlocked(struct device *dev,
+ unsigned int enabled, int irq)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ int (*func)(struct mc13xxx *mc13xxx, int irq);
+
+ if (!priv->valid)
+ return -ENODATA;
+
+ func = enabled ? mc13xxx_irq_unmask : mc13xxx_irq_mask;
+ return func(priv->mc13xxx, irq);
+}
+
+static int mc13xxx_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ ret = mc13xxx_rtc_irq_enable_unlocked(dev, enabled, MC13XXX_IRQ_TODA);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned int seconds, days1, days2;
+
+ if (!priv->valid)
+ return -ENODATA;
+
+ do {
+ int ret;
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days1);
+ if (ret)
+ return ret;
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTOD, &seconds);
+ if (ret)
+ return ret;
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days2);
+ if (ret)
+ return ret;
+ } while (days1 != days2);
+
+ rtc_time64_to_tm((time64_t)days1 * SEC_PER_DAY + seconds, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int mc13xxx_rtc_set_mmss(struct device *dev, time64_t secs)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned int seconds, days;
+ unsigned int alarmseconds;
+ int ret;
+
+ days = div_s64_rem(secs, SEC_PER_DAY, &seconds);
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ /*
+ * temporarily invalidate alarm to prevent triggering it when the day is
+ * already updated while the time isn't yet.
+ */
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &alarmseconds);
+ if (unlikely(ret))
+ goto out;
+
+ if (alarmseconds < SEC_PER_DAY) {
+ ret = mc13xxx_reg_write(priv->mc13xxx,
+ MC13XXX_RTCTODA, 0x1ffff);
+ if (unlikely(ret))
+ goto out;
+ }
+
+ /*
+ * write seconds=0 to prevent a day switch between writing days
+ * and seconds below
+ */
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, 0);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAY, days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTOD, seconds);
+ if (unlikely(ret))
+ goto out;
+
+ /* restore alarm */
+ if (alarmseconds < SEC_PER_DAY) {
+ ret = mc13xxx_reg_write(priv->mc13xxx,
+ MC13XXX_RTCTODA, alarmseconds);
+ if (unlikely(ret))
+ goto out;
+ }
+
+ if (!priv->valid) {
+ ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_unmask(priv->mc13xxx, MC13XXX_IRQ_RTCRST);
+ }
+
+out:
+ priv->valid = !ret;
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ unsigned seconds, days;
+ time64_t s1970;
+ int enabled, pending;
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCTODA, &seconds);
+ if (unlikely(ret))
+ goto out;
+ if (seconds >= SEC_PER_DAY) {
+ ret = -ENODATA;
+ goto out;
+ }
+
+ ret = mc13xxx_reg_read(priv->mc13xxx, MC13XXX_RTCDAY, &days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_status(priv->mc13xxx, MC13XXX_IRQ_TODA,
+ &enabled, &pending);
+
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+
+ if (ret)
+ return ret;
+
+ alarm->enabled = enabled;
+ alarm->pending = pending;
+
+ s1970 = (time64_t)days * SEC_PER_DAY + seconds;
+
+ rtc_time64_to_tm(s1970, &alarm->time);
+ dev_dbg(dev, "%s: %lld\n", __func__, (long long)s1970);
+
+ return 0;
+}
+
+static int mc13xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mc13xxx_rtc *priv = dev_get_drvdata(dev);
+ time64_t s1970;
+ u32 seconds, days;
+ int ret;
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ /* disable alarm to prevent false triggering */
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, 0x1ffff);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_irq_ack(priv->mc13xxx, MC13XXX_IRQ_TODA);
+ if (unlikely(ret))
+ goto out;
+
+ s1970 = rtc_tm_to_time64(&alarm->time);
+
+ dev_dbg(dev, "%s: o%2.s %lld\n", __func__, alarm->enabled ? "n" : "ff",
+ (long long)s1970);
+
+ ret = mc13xxx_rtc_irq_enable_unlocked(dev, alarm->enabled,
+ MC13XXX_IRQ_TODA);
+ if (unlikely(ret))
+ goto out;
+
+ days = div_s64_rem(s1970, SEC_PER_DAY, &seconds);
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCDAYA, days);
+ if (unlikely(ret))
+ goto out;
+
+ ret = mc13xxx_reg_write(priv->mc13xxx, MC13XXX_RTCTODA, seconds);
+
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return ret;
+}
+
+static irqreturn_t mc13xxx_rtc_alarm_handler(int irq, void *dev)
+{
+ struct mc13xxx_rtc *priv = dev;
+ struct mc13xxx *mc13xxx = priv->mc13xxx;
+
+ rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
+
+ mc13xxx_irq_ack(mc13xxx, irq);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mc13xxx_rtc_update_handler(int irq, void *dev)
+{
+ struct mc13xxx_rtc *priv = dev;
+ struct mc13xxx *mc13xxx = priv->mc13xxx;
+
+ rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_UF);
+
+ mc13xxx_irq_ack(mc13xxx, irq);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops mc13xxx_rtc_ops = {
+ .read_time = mc13xxx_rtc_read_time,
+ .set_mmss64 = mc13xxx_rtc_set_mmss,
+ .read_alarm = mc13xxx_rtc_read_alarm,
+ .set_alarm = mc13xxx_rtc_set_alarm,
+ .alarm_irq_enable = mc13xxx_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t mc13xxx_rtc_reset_handler(int irq, void *dev)
+{
+ struct mc13xxx_rtc *priv = dev;
+ struct mc13xxx *mc13xxx = priv->mc13xxx;
+
+ priv->valid = 0;
+
+ mc13xxx_irq_mask(mc13xxx, irq);
+
+ return IRQ_HANDLED;
+}
+
+static int __init mc13xxx_rtc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct mc13xxx_rtc *priv;
+ struct mc13xxx *mc13xxx;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mc13xxx = dev_get_drvdata(pdev->dev.parent);
+ priv->mc13xxx = mc13xxx;
+ priv->valid = 1;
+
+ platform_set_drvdata(pdev, priv);
+
+ mc13xxx_lock(mc13xxx);
+
+ mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_RTCRST);
+
+ ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_RTCRST,
+ mc13xxx_rtc_reset_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_irq_request;
+
+ ret = mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_1HZ,
+ mc13xxx_rtc_update_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_irq_request;
+
+ ret = mc13xxx_irq_request_nounmask(mc13xxx, MC13XXX_IRQ_TODA,
+ mc13xxx_rtc_alarm_handler, DRIVER_NAME, priv);
+ if (ret)
+ goto err_irq_request;
+
+ mc13xxx_unlock(mc13xxx);
+
+ priv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &mc13xxx_rtc_ops, THIS_MODULE);
+
+ return 0;
+
+err_irq_request:
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_TODA, priv);
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_1HZ, priv);
+ mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_RTCRST, priv);
+
+ mc13xxx_unlock(mc13xxx);
+
+ return ret;
+}
+
+static int mc13xxx_rtc_remove(struct platform_device *pdev)
+{
+ struct mc13xxx_rtc *priv = platform_get_drvdata(pdev);
+
+ mc13xxx_lock(priv->mc13xxx);
+
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_TODA, priv);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_1HZ, priv);
+ mc13xxx_irq_free(priv->mc13xxx, MC13XXX_IRQ_RTCRST, priv);
+
+ mc13xxx_unlock(priv->mc13xxx);
+
+ return 0;
+}
+
+static const struct platform_device_id mc13xxx_rtc_idtable[] = {
+ {
+ .name = "mc13783-rtc",
+ }, {
+ .name = "mc13892-rtc",
+ }, {
+ .name = "mc34708-rtc",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
+
+static struct platform_driver mc13xxx_rtc_driver = {
+ .id_table = mc13xxx_rtc_idtable,
+ .remove = mc13xxx_rtc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+module_platform_driver_probe(mc13xxx_rtc_driver, &mc13xxx_rtc_probe);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MC13XXX PMIC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c
new file mode 100644
index 000000000..34295bf00
--- /dev/null
+++ b/drivers/rtc/rtc-mcp795.c
@@ -0,0 +1,199 @@
+/*
+ * SPI Driver for Microchip MCP795 RTC
+ *
+ * Copyright (C) Josef Gajdusek <atx@atx.name>
+ *
+ * based on other Linux RTC drivers
+ *
+ * Device datasheet:
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/22280A.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/spi/spi.h>
+#include <linux/rtc.h>
+
+/* MCP795 Instructions, see datasheet table 3-1 */
+#define MCP795_EEREAD 0x03
+#define MCP795_EEWRITE 0x02
+#define MCP795_EEWRDI 0x04
+#define MCP795_EEWREN 0x06
+#define MCP795_SRREAD 0x05
+#define MCP795_SRWRITE 0x01
+#define MCP795_READ 0x13
+#define MCP795_WRITE 0x12
+#define MCP795_UNLOCK 0x14
+#define MCP795_IDWRITE 0x32
+#define MCP795_IDREAD 0x33
+#define MCP795_CLRWDT 0x44
+#define MCP795_CLRRAM 0x54
+
+#define MCP795_ST_BIT 0x80
+#define MCP795_24_BIT 0x40
+
+static int mcp795_rtcc_read(struct device *dev, u8 addr, u8 *buf, u8 count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int ret;
+ u8 tx[2];
+
+ tx[0] = MCP795_READ;
+ tx[1] = addr;
+ ret = spi_write_then_read(spi, tx, sizeof(tx), buf, count);
+
+ if (ret)
+ dev_err(dev, "Failed reading %d bytes from address %x.\n",
+ count, addr);
+
+ return ret;
+}
+
+static int mcp795_rtcc_write(struct device *dev, u8 addr, u8 *data, u8 count)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int ret;
+ u8 tx[2 + count];
+
+ tx[0] = MCP795_WRITE;
+ tx[1] = addr;
+ memcpy(&tx[2], data, count);
+
+ ret = spi_write(spi, tx, 2 + count);
+
+ if (ret)
+ dev_err(dev, "Failed to write %d bytes to address %x.\n",
+ count, addr);
+
+ return ret;
+}
+
+static int mcp795_rtcc_set_bits(struct device *dev, u8 addr, u8 mask, u8 state)
+{
+ int ret;
+ u8 tmp;
+
+ ret = mcp795_rtcc_read(dev, addr, &tmp, 1);
+ if (ret)
+ return ret;
+
+ if ((tmp & mask) != state) {
+ tmp = (tmp & ~mask) | state;
+ ret = mcp795_rtcc_write(dev, addr, &tmp, 1);
+ }
+
+ return ret;
+}
+
+static int mcp795_set_time(struct device *dev, struct rtc_time *tim)
+{
+ int ret;
+ u8 data[7];
+
+ /* Read first, so we can leave config bits untouched */
+ ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data));
+
+ if (ret)
+ return ret;
+
+ data[0] = (data[0] & 0x80) | ((tim->tm_sec / 10) << 4) | (tim->tm_sec % 10);
+ data[1] = (data[1] & 0x80) | ((tim->tm_min / 10) << 4) | (tim->tm_min % 10);
+ data[2] = ((tim->tm_hour / 10) << 4) | (tim->tm_hour % 10);
+ data[4] = ((tim->tm_mday / 10) << 4) | ((tim->tm_mday) % 10);
+ data[5] = (data[5] & 0x10) | (tim->tm_mon / 10) | (tim->tm_mon % 10);
+
+ if (tim->tm_year > 100)
+ tim->tm_year -= 100;
+
+ data[6] = ((tim->tm_year / 10) << 4) | (tim->tm_year % 10);
+
+ ret = mcp795_rtcc_write(dev, 0x01, data, sizeof(data));
+
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "Set mcp795: %04d-%02d-%02d %02d:%02d:%02d\n",
+ tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
+ tim->tm_hour, tim->tm_min, tim->tm_sec);
+
+ return 0;
+}
+
+static int mcp795_read_time(struct device *dev, struct rtc_time *tim)
+{
+ int ret;
+ u8 data[7];
+
+ ret = mcp795_rtcc_read(dev, 0x01, data, sizeof(data));
+
+ if (ret)
+ return ret;
+
+ tim->tm_sec = ((data[0] & 0x70) >> 4) * 10 + (data[0] & 0x0f);
+ tim->tm_min = ((data[1] & 0x70) >> 4) * 10 + (data[1] & 0x0f);
+ tim->tm_hour = ((data[2] & 0x30) >> 4) * 10 + (data[2] & 0x0f);
+ tim->tm_mday = ((data[4] & 0x30) >> 4) * 10 + (data[4] & 0x0f);
+ tim->tm_mon = ((data[5] & 0x10) >> 4) * 10 + (data[5] & 0x0f);
+ tim->tm_year = ((data[6] & 0xf0) >> 4) * 10 + (data[6] & 0x0f) + 100; /* Assume we are in 20xx */
+
+ dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d %02d:%02d:%02d\n",
+ tim->tm_year + 1900, tim->tm_mon, tim->tm_mday,
+ tim->tm_hour, tim->tm_min, tim->tm_sec);
+
+ return rtc_valid_tm(tim);
+}
+
+static struct rtc_class_ops mcp795_rtc_ops = {
+ .read_time = mcp795_read_time,
+ .set_time = mcp795_set_time
+};
+
+static int mcp795_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ int ret;
+
+ spi->mode = SPI_MODE_0;
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret) {
+ dev_err(&spi->dev, "Unable to setup SPI\n");
+ return ret;
+ }
+
+ /* Start the oscillator */
+ mcp795_rtcc_set_bits(&spi->dev, 0x01, MCP795_ST_BIT, MCP795_ST_BIT);
+ /* Clear the 12 hour mode flag*/
+ mcp795_rtcc_set_bits(&spi->dev, 0x03, MCP795_24_BIT, 0);
+
+ rtc = devm_rtc_device_register(&spi->dev, "rtc-mcp795",
+ &mcp795_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static struct spi_driver mcp795_driver = {
+ .driver = {
+ .name = "rtc-mcp795",
+ .owner = THIS_MODULE,
+ },
+ .probe = mcp795_probe,
+};
+
+module_spi_driver(mcp795_driver);
+
+MODULE_DESCRIPTION("MCP795 RTC SPI Driver");
+MODULE_AUTHOR("Josef Gajdusek <atx@atx.name>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:mcp795");
diff --git a/drivers/rtc/rtc-moxart.c b/drivers/rtc/rtc-moxart.c
new file mode 100644
index 000000000..73759c9a4
--- /dev/null
+++ b/drivers/rtc/rtc-moxart.c
@@ -0,0 +1,327 @@
+/*
+ * MOXA ART RTC driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * Based on code from
+ * Moxa Technology Co., Ltd. <www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#define GPIO_RTC_RESERVED 0x0C
+#define GPIO_RTC_DATA_SET 0x10
+#define GPIO_RTC_DATA_CLEAR 0x14
+#define GPIO_RTC_PIN_PULL_ENABLE 0x18
+#define GPIO_RTC_PIN_PULL_TYPE 0x1C
+#define GPIO_RTC_INT_ENABLE 0x20
+#define GPIO_RTC_INT_RAW_STATE 0x24
+#define GPIO_RTC_INT_MASKED_STATE 0x28
+#define GPIO_RTC_INT_MASK 0x2C
+#define GPIO_RTC_INT_CLEAR 0x30
+#define GPIO_RTC_INT_TRIGGER 0x34
+#define GPIO_RTC_INT_BOTH 0x38
+#define GPIO_RTC_INT_RISE_NEG 0x3C
+#define GPIO_RTC_BOUNCE_ENABLE 0x40
+#define GPIO_RTC_BOUNCE_PRE_SCALE 0x44
+#define GPIO_RTC_PROTECT_W 0x8E
+#define GPIO_RTC_PROTECT_R 0x8F
+#define GPIO_RTC_YEAR_W 0x8C
+#define GPIO_RTC_YEAR_R 0x8D
+#define GPIO_RTC_DAY_W 0x8A
+#define GPIO_RTC_DAY_R 0x8B
+#define GPIO_RTC_MONTH_W 0x88
+#define GPIO_RTC_MONTH_R 0x89
+#define GPIO_RTC_DATE_W 0x86
+#define GPIO_RTC_DATE_R 0x87
+#define GPIO_RTC_HOURS_W 0x84
+#define GPIO_RTC_HOURS_R 0x85
+#define GPIO_RTC_MINUTES_W 0x82
+#define GPIO_RTC_MINUTES_R 0x83
+#define GPIO_RTC_SECONDS_W 0x80
+#define GPIO_RTC_SECONDS_R 0x81
+#define GPIO_RTC_DELAY_TIME 8
+
+struct moxart_rtc {
+ struct rtc_device *rtc;
+ spinlock_t rtc_lock;
+ int gpio_data, gpio_sclk, gpio_reset;
+};
+
+static int day_of_year[12] = { 0, 31, 59, 90, 120, 151, 181,
+ 212, 243, 273, 304, 334 };
+
+static void moxart_rtc_write_byte(struct device *dev, u8 data)
+{
+ struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < 8; i++, data >>= 1) {
+ gpio_set_value(moxart_rtc->gpio_sclk, 0);
+ gpio_set_value(moxart_rtc->gpio_data, ((data & 1) == 1));
+ udelay(GPIO_RTC_DELAY_TIME);
+ gpio_set_value(moxart_rtc->gpio_sclk, 1);
+ udelay(GPIO_RTC_DELAY_TIME);
+ }
+}
+
+static u8 moxart_rtc_read_byte(struct device *dev)
+{
+ struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev);
+ int i;
+ u8 data = 0;
+
+ for (i = 0; i < 8; i++) {
+ gpio_set_value(moxart_rtc->gpio_sclk, 0);
+ udelay(GPIO_RTC_DELAY_TIME);
+ gpio_set_value(moxart_rtc->gpio_sclk, 1);
+ udelay(GPIO_RTC_DELAY_TIME);
+ if (gpio_get_value(moxart_rtc->gpio_data))
+ data |= (1 << i);
+ udelay(GPIO_RTC_DELAY_TIME);
+ }
+ return data;
+}
+
+static u8 moxart_rtc_read_register(struct device *dev, u8 cmd)
+{
+ struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev);
+ u8 data;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ gpio_direction_output(moxart_rtc->gpio_data, 0);
+ gpio_set_value(moxart_rtc->gpio_reset, 1);
+ udelay(GPIO_RTC_DELAY_TIME);
+ moxart_rtc_write_byte(dev, cmd);
+ gpio_direction_input(moxart_rtc->gpio_data);
+ udelay(GPIO_RTC_DELAY_TIME);
+ data = moxart_rtc_read_byte(dev);
+ gpio_set_value(moxart_rtc->gpio_sclk, 0);
+ gpio_set_value(moxart_rtc->gpio_reset, 0);
+ udelay(GPIO_RTC_DELAY_TIME);
+
+ local_irq_restore(flags);
+
+ return data;
+}
+
+static void moxart_rtc_write_register(struct device *dev, u8 cmd, u8 data)
+{
+ struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ gpio_direction_output(moxart_rtc->gpio_data, 0);
+ gpio_set_value(moxart_rtc->gpio_reset, 1);
+ udelay(GPIO_RTC_DELAY_TIME);
+ moxart_rtc_write_byte(dev, cmd);
+ moxart_rtc_write_byte(dev, data);
+ gpio_set_value(moxart_rtc->gpio_sclk, 0);
+ gpio_set_value(moxart_rtc->gpio_reset, 0);
+ udelay(GPIO_RTC_DELAY_TIME);
+
+ local_irq_restore(flags);
+}
+
+static int moxart_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&moxart_rtc->rtc_lock);
+
+ moxart_rtc_write_register(dev, GPIO_RTC_PROTECT_W, 0);
+ moxart_rtc_write_register(dev, GPIO_RTC_YEAR_W,
+ (((tm->tm_year - 100) / 10) << 4) |
+ ((tm->tm_year - 100) % 10));
+
+ moxart_rtc_write_register(dev, GPIO_RTC_MONTH_W,
+ (((tm->tm_mon + 1) / 10) << 4) |
+ ((tm->tm_mon + 1) % 10));
+
+ moxart_rtc_write_register(dev, GPIO_RTC_DATE_W,
+ ((tm->tm_mday / 10) << 4) |
+ (tm->tm_mday % 10));
+
+ moxart_rtc_write_register(dev, GPIO_RTC_HOURS_W,
+ ((tm->tm_hour / 10) << 4) |
+ (tm->tm_hour % 10));
+
+ moxart_rtc_write_register(dev, GPIO_RTC_MINUTES_W,
+ ((tm->tm_min / 10) << 4) |
+ (tm->tm_min % 10));
+
+ moxart_rtc_write_register(dev, GPIO_RTC_SECONDS_W,
+ ((tm->tm_sec / 10) << 4) |
+ (tm->tm_sec % 10));
+
+ moxart_rtc_write_register(dev, GPIO_RTC_PROTECT_W, 0x80);
+
+ spin_unlock_irq(&moxart_rtc->rtc_lock);
+
+ dev_dbg(dev, "%s: success tm_year=%d tm_mon=%d\n"
+ "tm_mday=%d tm_hour=%d tm_min=%d tm_sec=%d\n",
+ __func__, tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return 0;
+}
+
+static int moxart_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct moxart_rtc *moxart_rtc = dev_get_drvdata(dev);
+ unsigned char v;
+
+ spin_lock_irq(&moxart_rtc->rtc_lock);
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_SECONDS_R);
+ tm->tm_sec = (((v & 0x70) >> 4) * 10) + (v & 0x0F);
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_MINUTES_R);
+ tm->tm_min = (((v & 0x70) >> 4) * 10) + (v & 0x0F);
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_HOURS_R);
+ if (v & 0x80) { /* 12-hour mode */
+ tm->tm_hour = (((v & 0x10) >> 4) * 10) + (v & 0x0F);
+ if (v & 0x20) { /* PM mode */
+ tm->tm_hour += 12;
+ if (tm->tm_hour >= 24)
+ tm->tm_hour = 0;
+ }
+ } else { /* 24-hour mode */
+ tm->tm_hour = (((v & 0x30) >> 4) * 10) + (v & 0x0F);
+ }
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_DATE_R);
+ tm->tm_mday = (((v & 0x30) >> 4) * 10) + (v & 0x0F);
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_MONTH_R);
+ tm->tm_mon = (((v & 0x10) >> 4) * 10) + (v & 0x0F);
+ tm->tm_mon--;
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_YEAR_R);
+ tm->tm_year = (((v & 0xF0) >> 4) * 10) + (v & 0x0F);
+ tm->tm_year += 100;
+ if (tm->tm_year <= 69)
+ tm->tm_year += 100;
+
+ v = moxart_rtc_read_register(dev, GPIO_RTC_DAY_R);
+ tm->tm_wday = (v & 0x0f) - 1;
+ tm->tm_yday = day_of_year[tm->tm_mon];
+ tm->tm_yday += (tm->tm_mday - 1);
+ if (tm->tm_mon >= 2) {
+ if (!(tm->tm_year % 4) && (tm->tm_year % 100))
+ tm->tm_yday++;
+ }
+
+ tm->tm_isdst = 0;
+
+ spin_unlock_irq(&moxart_rtc->rtc_lock);
+
+ return 0;
+}
+
+static const struct rtc_class_ops moxart_rtc_ops = {
+ .read_time = moxart_rtc_read_time,
+ .set_time = moxart_rtc_set_time,
+};
+
+static int moxart_rtc_probe(struct platform_device *pdev)
+{
+ struct moxart_rtc *moxart_rtc;
+ int ret = 0;
+
+ moxart_rtc = devm_kzalloc(&pdev->dev, sizeof(*moxart_rtc), GFP_KERNEL);
+ if (!moxart_rtc)
+ return -ENOMEM;
+
+ moxart_rtc->gpio_data = of_get_named_gpio(pdev->dev.of_node,
+ "gpio-rtc-data", 0);
+ if (!gpio_is_valid(moxart_rtc->gpio_data)) {
+ dev_err(&pdev->dev, "invalid gpio (data): %d\n",
+ moxart_rtc->gpio_data);
+ return moxart_rtc->gpio_data;
+ }
+
+ moxart_rtc->gpio_sclk = of_get_named_gpio(pdev->dev.of_node,
+ "gpio-rtc-sclk", 0);
+ if (!gpio_is_valid(moxart_rtc->gpio_sclk)) {
+ dev_err(&pdev->dev, "invalid gpio (sclk): %d\n",
+ moxart_rtc->gpio_sclk);
+ return moxart_rtc->gpio_sclk;
+ }
+
+ moxart_rtc->gpio_reset = of_get_named_gpio(pdev->dev.of_node,
+ "gpio-rtc-reset", 0);
+ if (!gpio_is_valid(moxart_rtc->gpio_reset)) {
+ dev_err(&pdev->dev, "invalid gpio (reset): %d\n",
+ moxart_rtc->gpio_reset);
+ return moxart_rtc->gpio_reset;
+ }
+
+ spin_lock_init(&moxart_rtc->rtc_lock);
+ platform_set_drvdata(pdev, moxart_rtc);
+
+ ret = devm_gpio_request(&pdev->dev, moxart_rtc->gpio_data, "rtc_data");
+ if (ret) {
+ dev_err(&pdev->dev, "can't get rtc_data gpio\n");
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(&pdev->dev, moxart_rtc->gpio_sclk,
+ GPIOF_DIR_OUT, "rtc_sclk");
+ if (ret) {
+ dev_err(&pdev->dev, "can't get rtc_sclk gpio\n");
+ return ret;
+ }
+
+ ret = devm_gpio_request_one(&pdev->dev, moxart_rtc->gpio_reset,
+ GPIOF_DIR_OUT, "rtc_reset");
+ if (ret) {
+ dev_err(&pdev->dev, "can't get rtc_reset gpio\n");
+ return ret;
+ }
+
+ moxart_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &moxart_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(moxart_rtc->rtc)) {
+ dev_err(&pdev->dev, "devm_rtc_device_register failed\n");
+ return PTR_ERR(moxart_rtc->rtc);
+ }
+
+ return 0;
+}
+
+static const struct of_device_id moxart_rtc_match[] = {
+ { .compatible = "moxa,moxart-rtc" },
+ { },
+};
+
+static struct platform_driver moxart_rtc_driver = {
+ .probe = moxart_rtc_probe,
+ .driver = {
+ .name = "moxart-rtc",
+ .of_match_table = moxart_rtc_match,
+ },
+};
+module_platform_driver(moxart_rtc_driver);
+
+MODULE_DESCRIPTION("MOXART RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
new file mode 100644
index 000000000..1767e18d5
--- /dev/null
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -0,0 +1,423 @@
+/*
+ * Real-time clock driver for MPC5121
+ *
+ * Copyright 2007, Domen Puncer <domen.puncer@telargo.com>
+ * Copyright 2008, Freescale Semiconductor, Inc. All rights reserved.
+ * Copyright 2011, Dmitry Eremin-Solenikov
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+struct mpc5121_rtc_regs {
+ u8 set_time; /* RTC + 0x00 */
+ u8 hour_set; /* RTC + 0x01 */
+ u8 minute_set; /* RTC + 0x02 */
+ u8 second_set; /* RTC + 0x03 */
+
+ u8 set_date; /* RTC + 0x04 */
+ u8 month_set; /* RTC + 0x05 */
+ u8 weekday_set; /* RTC + 0x06 */
+ u8 date_set; /* RTC + 0x07 */
+
+ u8 write_sw; /* RTC + 0x08 */
+ u8 sw_set; /* RTC + 0x09 */
+ u16 year_set; /* RTC + 0x0a */
+
+ u8 alm_enable; /* RTC + 0x0c */
+ u8 alm_hour_set; /* RTC + 0x0d */
+ u8 alm_min_set; /* RTC + 0x0e */
+ u8 int_enable; /* RTC + 0x0f */
+
+ u8 reserved1;
+ u8 hour; /* RTC + 0x11 */
+ u8 minute; /* RTC + 0x12 */
+ u8 second; /* RTC + 0x13 */
+
+ u8 month; /* RTC + 0x14 */
+ u8 wday_mday; /* RTC + 0x15 */
+ u16 year; /* RTC + 0x16 */
+
+ u8 int_alm; /* RTC + 0x18 */
+ u8 int_sw; /* RTC + 0x19 */
+ u8 alm_status; /* RTC + 0x1a */
+ u8 sw_minute; /* RTC + 0x1b */
+
+ u8 bus_error_1; /* RTC + 0x1c */
+ u8 int_day; /* RTC + 0x1d */
+ u8 int_min; /* RTC + 0x1e */
+ u8 int_sec; /* RTC + 0x1f */
+
+ /*
+ * target_time:
+ * intended to be used for hibernation but hibernation
+ * does not work on silicon rev 1.5 so use it for non-volatile
+ * storage of offset between the actual_time register and linux
+ * time
+ */
+ u32 target_time; /* RTC + 0x20 */
+ /*
+ * actual_time:
+ * readonly time since VBAT_RTC was last connected
+ */
+ u32 actual_time; /* RTC + 0x24 */
+ u32 keep_alive; /* RTC + 0x28 */
+};
+
+struct mpc5121_rtc_data {
+ unsigned irq;
+ unsigned irq_periodic;
+ struct mpc5121_rtc_regs __iomem *regs;
+ struct rtc_device *rtc;
+ struct rtc_wkalrm wkalarm;
+};
+
+/*
+ * Update second/minute/hour registers.
+ *
+ * This is just so alarm will work.
+ */
+static void mpc5121_rtc_update_smh(struct mpc5121_rtc_regs __iomem *regs,
+ struct rtc_time *tm)
+{
+ out_8(&regs->second_set, tm->tm_sec);
+ out_8(&regs->minute_set, tm->tm_min);
+ out_8(&regs->hour_set, tm->tm_hour);
+
+ /* set time sequence */
+ out_8(&regs->set_time, 0x1);
+ out_8(&regs->set_time, 0x3);
+ out_8(&regs->set_time, 0x1);
+ out_8(&regs->set_time, 0x0);
+}
+
+static int mpc5121_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ unsigned long now;
+
+ /*
+ * linux time is actual_time plus the offset saved in target_time
+ */
+ now = in_be32(&regs->actual_time) + in_be32(&regs->target_time);
+
+ rtc_time_to_tm(now, tm);
+
+ /*
+ * update second minute hour registers
+ * so alarms will work
+ */
+ mpc5121_rtc_update_smh(regs, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int mpc5121_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int ret;
+ unsigned long now;
+
+ /*
+ * The actual_time register is read only so we write the offset
+ * between it and linux time to the target_time register.
+ */
+ ret = rtc_tm_to_time(tm, &now);
+ if (ret == 0)
+ out_be32(&regs->target_time, now - in_be32(&regs->actual_time));
+
+ /*
+ * update second minute hour registers
+ * so alarms will work
+ */
+ mpc5121_rtc_update_smh(regs, tm);
+
+ return 0;
+}
+
+static int mpc5200_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int tmp;
+
+ tm->tm_sec = in_8(&regs->second);
+ tm->tm_min = in_8(&regs->minute);
+
+ /* 12 hour format? */
+ if (in_8(&regs->hour) & 0x20)
+ tm->tm_hour = (in_8(&regs->hour) >> 1) +
+ (in_8(&regs->hour) & 1 ? 12 : 0);
+ else
+ tm->tm_hour = in_8(&regs->hour);
+
+ tmp = in_8(&regs->wday_mday);
+ tm->tm_mday = tmp & 0x1f;
+ tm->tm_mon = in_8(&regs->month) - 1;
+ tm->tm_year = in_be16(&regs->year) - 1900;
+ tm->tm_wday = (tmp >> 5) % 7;
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_isdst = 0;
+
+ return 0;
+}
+
+static int mpc5200_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ mpc5121_rtc_update_smh(regs, tm);
+
+ /* date */
+ out_8(&regs->month_set, tm->tm_mon + 1);
+ out_8(&regs->weekday_set, tm->tm_wday ? tm->tm_wday : 7);
+ out_8(&regs->date_set, tm->tm_mday);
+ out_be16(&regs->year_set, tm->tm_year + 1900);
+
+ /* set date sequence */
+ out_8(&regs->set_date, 0x1);
+ out_8(&regs->set_date, 0x3);
+ out_8(&regs->set_date, 0x1);
+ out_8(&regs->set_date, 0x0);
+
+ return 0;
+}
+
+static int mpc5121_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ *alarm = rtc->wkalarm;
+
+ alarm->pending = in_8(&regs->alm_status);
+
+ return 0;
+}
+
+static int mpc5121_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ /*
+ * the alarm has no seconds so deal with it
+ */
+ if (alarm->time.tm_sec) {
+ alarm->time.tm_sec = 0;
+ alarm->time.tm_min++;
+ if (alarm->time.tm_min >= 60) {
+ alarm->time.tm_min = 0;
+ alarm->time.tm_hour++;
+ if (alarm->time.tm_hour >= 24)
+ alarm->time.tm_hour = 0;
+ }
+ }
+
+ alarm->time.tm_mday = -1;
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_year = -1;
+
+ out_8(&regs->alm_min_set, alarm->time.tm_min);
+ out_8(&regs->alm_hour_set, alarm->time.tm_hour);
+
+ out_8(&regs->alm_enable, alarm->enabled);
+
+ rtc->wkalarm = *alarm;
+ return 0;
+}
+
+static irqreturn_t mpc5121_rtc_handler(int irq, void *dev)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ if (in_8(&regs->int_alm)) {
+ /* acknowledge and clear status */
+ out_8(&regs->int_alm, 1);
+ out_8(&regs->alm_status, 1);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t mpc5121_rtc_handler_upd(int irq, void *dev)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata((struct device *)dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ if (in_8(&regs->int_sec) && (in_8(&regs->int_enable) & 0x1)) {
+ /* acknowledge */
+ out_8(&regs->int_sec, 1);
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_UF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int mpc5121_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct mpc5121_rtc_data *rtc = dev_get_drvdata(dev);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+ int val;
+
+ if (enabled)
+ val = 1;
+ else
+ val = 0;
+
+ out_8(&regs->alm_enable, val);
+ rtc->wkalarm.enabled = val;
+
+ return 0;
+}
+
+static const struct rtc_class_ops mpc5121_rtc_ops = {
+ .read_time = mpc5121_rtc_read_time,
+ .set_time = mpc5121_rtc_set_time,
+ .read_alarm = mpc5121_rtc_read_alarm,
+ .set_alarm = mpc5121_rtc_set_alarm,
+ .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops mpc5200_rtc_ops = {
+ .read_time = mpc5200_rtc_read_time,
+ .set_time = mpc5200_rtc_set_time,
+ .read_alarm = mpc5121_rtc_read_alarm,
+ .set_alarm = mpc5121_rtc_set_alarm,
+ .alarm_irq_enable = mpc5121_rtc_alarm_irq_enable,
+};
+
+static int mpc5121_rtc_probe(struct platform_device *op)
+{
+ struct mpc5121_rtc_data *rtc;
+ int err = 0;
+
+ rtc = devm_kzalloc(&op->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->regs = of_iomap(op->dev.of_node, 0);
+ if (!rtc->regs) {
+ dev_err(&op->dev, "%s: couldn't map io space\n", __func__);
+ return -ENOSYS;
+ }
+
+ device_init_wakeup(&op->dev, 1);
+
+ platform_set_drvdata(op, rtc);
+
+ rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);
+ err = request_irq(rtc->irq, mpc5121_rtc_handler, 0,
+ "mpc5121-rtc", &op->dev);
+ if (err) {
+ dev_err(&op->dev, "%s: could not request irq: %i\n",
+ __func__, rtc->irq);
+ goto out_dispose;
+ }
+
+ rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);
+ err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
+ 0, "mpc5121-rtc_upd", &op->dev);
+ if (err) {
+ dev_err(&op->dev, "%s: could not request irq: %i\n",
+ __func__, rtc->irq_periodic);
+ goto out_dispose2;
+ }
+
+ if (of_device_is_compatible(op->dev.of_node, "fsl,mpc5121-rtc")) {
+ u32 ka;
+ ka = in_be32(&rtc->regs->keep_alive);
+ if (ka & 0x02) {
+ dev_warn(&op->dev,
+ "mpc5121-rtc: Battery or oscillator failure!\n");
+ out_be32(&rtc->regs->keep_alive, ka);
+ }
+
+ rtc->rtc = devm_rtc_device_register(&op->dev, "mpc5121-rtc",
+ &mpc5121_rtc_ops, THIS_MODULE);
+ } else {
+ rtc->rtc = devm_rtc_device_register(&op->dev, "mpc5200-rtc",
+ &mpc5200_rtc_ops, THIS_MODULE);
+ }
+
+ if (IS_ERR(rtc->rtc)) {
+ err = PTR_ERR(rtc->rtc);
+ goto out_free_irq;
+ }
+ rtc->rtc->uie_unsupported = 1;
+
+ return 0;
+
+out_free_irq:
+ free_irq(rtc->irq_periodic, &op->dev);
+out_dispose2:
+ irq_dispose_mapping(rtc->irq_periodic);
+ free_irq(rtc->irq, &op->dev);
+out_dispose:
+ irq_dispose_mapping(rtc->irq);
+ iounmap(rtc->regs);
+
+ return err;
+}
+
+static int mpc5121_rtc_remove(struct platform_device *op)
+{
+ struct mpc5121_rtc_data *rtc = platform_get_drvdata(op);
+ struct mpc5121_rtc_regs __iomem *regs = rtc->regs;
+
+ /* disable interrupt, so there are no nasty surprises */
+ out_8(&regs->alm_enable, 0);
+ out_8(&regs->int_enable, in_8(&regs->int_enable) & ~0x1);
+
+ iounmap(rtc->regs);
+ free_irq(rtc->irq, &op->dev);
+ free_irq(rtc->irq_periodic, &op->dev);
+ irq_dispose_mapping(rtc->irq);
+ irq_dispose_mapping(rtc->irq_periodic);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mpc5121_rtc_match[] = {
+ { .compatible = "fsl,mpc5121-rtc", },
+ { .compatible = "fsl,mpc5200-rtc", },
+ {},
+};
+#endif
+
+static struct platform_driver mpc5121_rtc_driver = {
+ .driver = {
+ .name = "mpc5121-rtc",
+ .of_match_table = of_match_ptr(mpc5121_rtc_match),
+ },
+ .probe = mpc5121_rtc_probe,
+ .remove = mpc5121_rtc_remove,
+};
+
+module_platform_driver(mpc5121_rtc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("John Rigby <jcrigby@gmail.com>");
diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c
new file mode 100644
index 000000000..548ea6f6f
--- /dev/null
+++ b/drivers/rtc/rtc-mrst.c
@@ -0,0 +1,545 @@
+/*
+ * rtc-mrst.c: Driver for Moorestown virtual RTC
+ *
+ * (C) Copyright 2009 Intel Corporation
+ * Author: Jacob Pan (jacob.jun.pan@intel.com)
+ * Feng Tang (feng.tang@intel.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ *
+ * Note:
+ * VRTC is emulated by system controller firmware, the real HW
+ * RTC is located in the PMIC device. SCU FW shadows PMIC RTC
+ * in a memory mapped IO space that is visible to the host IA
+ * processor.
+ *
+ * This driver is based upon drivers/rtc/rtc-cmos.c
+ */
+
+/*
+ * Note:
+ * * vRTC only supports binary mode and 24H mode
+ * * vRTC only support PIE and AIE, no UIE, and its PIE only happens
+ * at 23:59:59pm everyday, no support for adjustable frequency
+ * * Alarm function is also limited to hr/min/sec.
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sfi.h>
+
+#include <asm-generic/rtc.h>
+#include <asm/intel_scu_ipc.h>
+#include <asm/intel-mid.h>
+#include <asm/intel_mid_vrtc.h>
+
+struct mrst_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ int irq;
+ struct resource *iomem;
+
+ u8 enabled_wake;
+ u8 suspend_ctrl;
+};
+
+static const char driver_name[] = "rtc_mrst";
+
+#define RTC_IRQMASK (RTC_PF | RTC_AF)
+
+static inline int is_intr(u8 rtc_intr)
+{
+ if (!(rtc_intr & RTC_IRQF))
+ return 0;
+ return rtc_intr & RTC_IRQMASK;
+}
+
+static inline unsigned char vrtc_is_updating(void)
+{
+ unsigned char uip;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ uip = (vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ return uip;
+}
+
+/*
+ * rtc_time's year contains the increment over 1900, but vRTC's YEAR
+ * register can't be programmed to value larger than 0x64, so vRTC
+ * driver chose to use 1972 (1970 is UNIX time start point) as the base,
+ * and does the translation at read/write time.
+ *
+ * Why not just use 1970 as the offset? it's because using 1972 will
+ * make it consistent in leap year setting for both vrtc and low-level
+ * physical rtc devices. Then why not use 1960 as the offset? If we use
+ * 1960, for a device's first use, its YEAR register is 0 and the system
+ * year will be parsed as 1960 which is not a valid UNIX time and will
+ * cause many applications to fail mysteriously.
+ */
+static int mrst_read_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long flags;
+
+ if (vrtc_is_updating())
+ mdelay(20);
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ time->tm_sec = vrtc_cmos_read(RTC_SECONDS);
+ time->tm_min = vrtc_cmos_read(RTC_MINUTES);
+ time->tm_hour = vrtc_cmos_read(RTC_HOURS);
+ time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH);
+ time->tm_mon = vrtc_cmos_read(RTC_MONTH);
+ time->tm_year = vrtc_cmos_read(RTC_YEAR);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ /* Adjust for the 1972/1900 */
+ time->tm_year += 72;
+ time->tm_mon--;
+ return rtc_valid_tm(time);
+}
+
+static int mrst_set_time(struct device *dev, struct rtc_time *time)
+{
+ int ret;
+ unsigned long flags;
+ unsigned char mon, day, hrs, min, sec;
+ unsigned int yrs;
+
+ yrs = time->tm_year;
+ mon = time->tm_mon + 1; /* tm_mon starts at zero */
+ day = time->tm_mday;
+ hrs = time->tm_hour;
+ min = time->tm_min;
+ sec = time->tm_sec;
+
+ if (yrs < 72 || yrs > 138)
+ return -EINVAL;
+ yrs -= 72;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+
+ vrtc_cmos_write(yrs, RTC_YEAR);
+ vrtc_cmos_write(mon, RTC_MONTH);
+ vrtc_cmos_write(day, RTC_DAY_OF_MONTH);
+ vrtc_cmos_write(hrs, RTC_HOURS);
+ vrtc_cmos_write(min, RTC_MINUTES);
+ vrtc_cmos_write(sec, RTC_SECONDS);
+
+ spin_unlock_irqrestore(&rtc_lock, flags);
+
+ ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME);
+ return ret;
+}
+
+static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ unsigned char rtc_control;
+
+ if (mrst->irq <= 0)
+ return -EIO;
+
+ /* Basic alarms only support hour, minute, and seconds fields.
+ * Some also support day and month, for alarms up to a year in
+ * the future.
+ */
+ t->time.tm_mday = -1;
+ t->time.tm_mon = -1;
+ t->time.tm_year = -1;
+
+ /* vRTC only supports binary mode */
+ spin_lock_irq(&rtc_lock);
+ t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM);
+ t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM);
+ t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM);
+
+ rtc_control = vrtc_cmos_read(RTC_CONTROL);
+ spin_unlock_irq(&rtc_lock);
+
+ t->enabled = !!(rtc_control & RTC_AIE);
+ t->pending = 0;
+
+ return 0;
+}
+
+static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control)
+{
+ unsigned char rtc_intr;
+
+ /*
+ * NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
+ * allegedly some older rtcs need that to handle irqs properly
+ */
+ rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS);
+ rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
+ if (is_intr(rtc_intr))
+ rtc_update_irq(mrst->rtc, 1, rtc_intr);
+}
+
+static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask)
+{
+ unsigned char rtc_control;
+
+ /*
+ * Flush any pending IRQ status, notably for update irqs,
+ * before we enable new IRQs
+ */
+ rtc_control = vrtc_cmos_read(RTC_CONTROL);
+ mrst_checkintr(mrst, rtc_control);
+
+ rtc_control |= mask;
+ vrtc_cmos_write(rtc_control, RTC_CONTROL);
+
+ mrst_checkintr(mrst, rtc_control);
+}
+
+static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask)
+{
+ unsigned char rtc_control;
+
+ rtc_control = vrtc_cmos_read(RTC_CONTROL);
+ rtc_control &= ~mask;
+ vrtc_cmos_write(rtc_control, RTC_CONTROL);
+ mrst_checkintr(mrst, rtc_control);
+}
+
+static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ unsigned char hrs, min, sec;
+ int ret = 0;
+
+ if (!mrst->irq)
+ return -EIO;
+
+ hrs = t->time.tm_hour;
+ min = t->time.tm_min;
+ sec = t->time.tm_sec;
+
+ spin_lock_irq(&rtc_lock);
+ /* Next rtc irq must not be from previous alarm setting */
+ mrst_irq_disable(mrst, RTC_AIE);
+
+ /* Update alarm */
+ vrtc_cmos_write(hrs, RTC_HOURS_ALARM);
+ vrtc_cmos_write(min, RTC_MINUTES_ALARM);
+ vrtc_cmos_write(sec, RTC_SECONDS_ALARM);
+
+ spin_unlock_irq(&rtc_lock);
+
+ ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM);
+ if (ret)
+ return ret;
+
+ spin_lock_irq(&rtc_lock);
+ if (t->enabled)
+ mrst_irq_enable(mrst, RTC_AIE);
+
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+}
+
+/* Currently, the vRTC doesn't support UIE ON/OFF */
+static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ if (enabled)
+ mrst_irq_enable(mrst, RTC_AIE);
+ else
+ mrst_irq_disable(mrst, RTC_AIE);
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ return 0;
+}
+
+
+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
+
+static int mrst_procfs(struct device *dev, struct seq_file *seq)
+{
+ unsigned char rtc_control, valid;
+
+ spin_lock_irq(&rtc_lock);
+ rtc_control = vrtc_cmos_read(RTC_CONTROL);
+ valid = vrtc_cmos_read(RTC_VALID);
+ spin_unlock_irq(&rtc_lock);
+
+ seq_printf(seq,
+ "periodic_IRQ\t: %s\n"
+ "alarm\t\t: %s\n"
+ "BCD\t\t: no\n"
+ "periodic_freq\t: daily (not adjustable)\n",
+ (rtc_control & RTC_PIE) ? "on" : "off",
+ (rtc_control & RTC_AIE) ? "on" : "off");
+
+ return 0;
+}
+
+#else
+#define mrst_procfs NULL
+#endif
+
+static const struct rtc_class_ops mrst_rtc_ops = {
+ .read_time = mrst_read_time,
+ .set_time = mrst_set_time,
+ .read_alarm = mrst_read_alarm,
+ .set_alarm = mrst_set_alarm,
+ .proc = mrst_procfs,
+ .alarm_irq_enable = mrst_rtc_alarm_irq_enable,
+};
+
+static struct mrst_rtc mrst_rtc;
+
+/*
+ * When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in
+ * Reg B, so no need for this driver to clear it
+ */
+static irqreturn_t mrst_rtc_irq(int irq, void *p)
+{
+ u8 irqstat;
+
+ spin_lock(&rtc_lock);
+ /* This read will clear all IRQ flags inside Reg C */
+ irqstat = vrtc_cmos_read(RTC_INTR_FLAGS);
+ spin_unlock(&rtc_lock);
+
+ irqstat &= RTC_IRQMASK | RTC_IRQF;
+ if (is_intr(irqstat)) {
+ rtc_update_irq(p, 1, irqstat);
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
+ int rtc_irq)
+{
+ int retval = 0;
+ unsigned char rtc_control;
+
+ /* There can be only one ... */
+ if (mrst_rtc.dev)
+ return -EBUSY;
+
+ if (!iomem)
+ return -ENODEV;
+
+ iomem = request_mem_region(iomem->start, resource_size(iomem),
+ driver_name);
+ if (!iomem) {
+ dev_dbg(dev, "i/o mem already in use.\n");
+ return -EBUSY;
+ }
+
+ mrst_rtc.irq = rtc_irq;
+ mrst_rtc.iomem = iomem;
+ mrst_rtc.dev = dev;
+ dev_set_drvdata(dev, &mrst_rtc);
+
+ mrst_rtc.rtc = rtc_device_register(driver_name, dev,
+ &mrst_rtc_ops, THIS_MODULE);
+ if (IS_ERR(mrst_rtc.rtc)) {
+ retval = PTR_ERR(mrst_rtc.rtc);
+ goto cleanup0;
+ }
+
+ rename_region(iomem, dev_name(&mrst_rtc.rtc->dev));
+
+ spin_lock_irq(&rtc_lock);
+ mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE);
+ rtc_control = vrtc_cmos_read(RTC_CONTROL);
+ spin_unlock_irq(&rtc_lock);
+
+ if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))
+ dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n");
+
+ if (rtc_irq) {
+ retval = request_irq(rtc_irq, mrst_rtc_irq,
+ 0, dev_name(&mrst_rtc.rtc->dev),
+ mrst_rtc.rtc);
+ if (retval < 0) {
+ dev_dbg(dev, "IRQ %d is already in use, err %d\n",
+ rtc_irq, retval);
+ goto cleanup1;
+ }
+ }
+ dev_dbg(dev, "initialised\n");
+ return 0;
+
+cleanup1:
+ rtc_device_unregister(mrst_rtc.rtc);
+cleanup0:
+ mrst_rtc.dev = NULL;
+ release_mem_region(iomem->start, resource_size(iomem));
+ dev_err(dev, "rtc-mrst: unable to initialise\n");
+ return retval;
+}
+
+static void rtc_mrst_do_shutdown(void)
+{
+ spin_lock_irq(&rtc_lock);
+ mrst_irq_disable(&mrst_rtc, RTC_IRQMASK);
+ spin_unlock_irq(&rtc_lock);
+}
+
+static void rtc_mrst_do_remove(struct device *dev)
+{
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ struct resource *iomem;
+
+ rtc_mrst_do_shutdown();
+
+ if (mrst->irq)
+ free_irq(mrst->irq, mrst->rtc);
+
+ rtc_device_unregister(mrst->rtc);
+ mrst->rtc = NULL;
+
+ iomem = mrst->iomem;
+ release_mem_region(iomem->start, resource_size(iomem));
+ mrst->iomem = NULL;
+
+ mrst->dev = NULL;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mrst_suspend(struct device *dev)
+{
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ unsigned char tmp;
+
+ /* Only the alarm might be a wakeup event source */
+ spin_lock_irq(&rtc_lock);
+ mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL);
+ if (tmp & (RTC_PIE | RTC_AIE)) {
+ unsigned char mask;
+
+ if (device_may_wakeup(dev))
+ mask = RTC_IRQMASK & ~RTC_AIE;
+ else
+ mask = RTC_IRQMASK;
+ tmp &= ~mask;
+ vrtc_cmos_write(tmp, RTC_CONTROL);
+
+ mrst_checkintr(mrst, tmp);
+ }
+ spin_unlock_irq(&rtc_lock);
+
+ if (tmp & RTC_AIE) {
+ mrst->enabled_wake = 1;
+ enable_irq_wake(mrst->irq);
+ }
+
+ dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x\n",
+ (tmp & RTC_AIE) ? ", alarm may wake" : "",
+ tmp);
+
+ return 0;
+}
+
+/*
+ * We want RTC alarms to wake us from the deep power saving state
+ */
+static inline int mrst_poweroff(struct device *dev)
+{
+ return mrst_suspend(dev);
+}
+
+static int mrst_resume(struct device *dev)
+{
+ struct mrst_rtc *mrst = dev_get_drvdata(dev);
+ unsigned char tmp = mrst->suspend_ctrl;
+
+ /* Re-enable any irqs previously active */
+ if (tmp & RTC_IRQMASK) {
+ unsigned char mask;
+
+ if (mrst->enabled_wake) {
+ disable_irq_wake(mrst->irq);
+ mrst->enabled_wake = 0;
+ }
+
+ spin_lock_irq(&rtc_lock);
+ do {
+ vrtc_cmos_write(tmp, RTC_CONTROL);
+
+ mask = vrtc_cmos_read(RTC_INTR_FLAGS);
+ mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
+ if (!is_intr(mask))
+ break;
+
+ rtc_update_irq(mrst->rtc, 1, mask);
+ tmp &= ~RTC_AIE;
+ } while (mask & RTC_AIE);
+ spin_unlock_irq(&rtc_lock);
+ }
+
+ dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x\n", tmp);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume);
+#define MRST_PM_OPS (&mrst_pm_ops)
+
+#else
+#define MRST_PM_OPS NULL
+
+static inline int mrst_poweroff(struct device *dev)
+{
+ return -ENOSYS;
+}
+
+#endif
+
+static int vrtc_mrst_platform_probe(struct platform_device *pdev)
+{
+ return vrtc_mrst_do_probe(&pdev->dev,
+ platform_get_resource(pdev, IORESOURCE_MEM, 0),
+ platform_get_irq(pdev, 0));
+}
+
+static int vrtc_mrst_platform_remove(struct platform_device *pdev)
+{
+ rtc_mrst_do_remove(&pdev->dev);
+ return 0;
+}
+
+static void vrtc_mrst_platform_shutdown(struct platform_device *pdev)
+{
+ if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev))
+ return;
+
+ rtc_mrst_do_shutdown();
+}
+
+MODULE_ALIAS("platform:vrtc_mrst");
+
+static struct platform_driver vrtc_mrst_platform_driver = {
+ .probe = vrtc_mrst_platform_probe,
+ .remove = vrtc_mrst_platform_remove,
+ .shutdown = vrtc_mrst_platform_shutdown,
+ .driver = {
+ .name = driver_name,
+ .pm = MRST_PM_OPS,
+ }
+};
+
+module_platform_driver(vrtc_mrst_platform_driver);
+
+MODULE_AUTHOR("Jacob Pan; Feng Tang");
+MODULE_DESCRIPTION("Driver for Moorestown virtual RTC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-msm6242.c b/drivers/rtc/rtc-msm6242.c
new file mode 100644
index 000000000..c1c5c4e3b
--- /dev/null
+++ b/drivers/rtc/rtc-msm6242.c
@@ -0,0 +1,238 @@
+/*
+ * Oki MSM6242 RTC Driver
+ *
+ * Copyright 2009 Geert Uytterhoeven
+ *
+ * Based on the A2000 TOD code in arch/m68k/amiga/config.c
+ * Copyright (C) 1993 Hamish Macdonald
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+
+enum {
+ MSM6242_SECOND1 = 0x0, /* 1-second digit register */
+ MSM6242_SECOND10 = 0x1, /* 10-second digit register */
+ MSM6242_MINUTE1 = 0x2, /* 1-minute digit register */
+ MSM6242_MINUTE10 = 0x3, /* 10-minute digit register */
+ MSM6242_HOUR1 = 0x4, /* 1-hour digit register */
+ MSM6242_HOUR10 = 0x5, /* PM/AM, 10-hour digit register */
+ MSM6242_DAY1 = 0x6, /* 1-day digit register */
+ MSM6242_DAY10 = 0x7, /* 10-day digit register */
+ MSM6242_MONTH1 = 0x8, /* 1-month digit register */
+ MSM6242_MONTH10 = 0x9, /* 10-month digit register */
+ MSM6242_YEAR1 = 0xa, /* 1-year digit register */
+ MSM6242_YEAR10 = 0xb, /* 10-year digit register */
+ MSM6242_WEEK = 0xc, /* Week register */
+ MSM6242_CD = 0xd, /* Control Register D */
+ MSM6242_CE = 0xe, /* Control Register E */
+ MSM6242_CF = 0xf, /* Control Register F */
+};
+
+#define MSM6242_HOUR10_AM (0 << 2)
+#define MSM6242_HOUR10_PM (1 << 2)
+#define MSM6242_HOUR10_HR_MASK (3 << 0)
+
+#define MSM6242_WEEK_SUNDAY 0
+#define MSM6242_WEEK_MONDAY 1
+#define MSM6242_WEEK_TUESDAY 2
+#define MSM6242_WEEK_WEDNESDAY 3
+#define MSM6242_WEEK_THURSDAY 4
+#define MSM6242_WEEK_FRIDAY 5
+#define MSM6242_WEEK_SATURDAY 6
+
+#define MSM6242_CD_30_S_ADJ (1 << 3) /* 30-second adjustment */
+#define MSM6242_CD_IRQ_FLAG (1 << 2)
+#define MSM6242_CD_BUSY (1 << 1)
+#define MSM6242_CD_HOLD (1 << 0)
+
+#define MSM6242_CE_T_MASK (3 << 2)
+#define MSM6242_CE_T_64HZ (0 << 2) /* period 1/64 second */
+#define MSM6242_CE_T_1HZ (1 << 2) /* period 1 second */
+#define MSM6242_CE_T_1MINUTE (2 << 2) /* period 1 minute */
+#define MSM6242_CE_T_1HOUR (3 << 2) /* period 1 hour */
+
+#define MSM6242_CE_ITRPT_STND (1 << 1)
+#define MSM6242_CE_MASK (1 << 0) /* STD.P output control */
+
+#define MSM6242_CF_TEST (1 << 3)
+#define MSM6242_CF_12H (0 << 2)
+#define MSM6242_CF_24H (1 << 2)
+#define MSM6242_CF_STOP (1 << 1)
+#define MSM6242_CF_REST (1 << 0) /* reset */
+
+
+struct msm6242_priv {
+ u32 __iomem *regs;
+ struct rtc_device *rtc;
+};
+
+static inline unsigned int msm6242_read(struct msm6242_priv *priv,
+ unsigned int reg)
+{
+ return __raw_readl(&priv->regs[reg]) & 0xf;
+}
+
+static inline void msm6242_write(struct msm6242_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ __raw_writel(val, &priv->regs[reg]);
+}
+
+static inline void msm6242_set(struct msm6242_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ msm6242_write(priv, msm6242_read(priv, reg) | val, reg);
+}
+
+static inline void msm6242_clear(struct msm6242_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ msm6242_write(priv, msm6242_read(priv, reg) & ~val, reg);
+}
+
+static void msm6242_lock(struct msm6242_priv *priv)
+{
+ int cnt = 5;
+
+ msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD);
+
+ while ((msm6242_read(priv, MSM6242_CD) & MSM6242_CD_BUSY) && cnt) {
+ msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD);
+ udelay(70);
+ msm6242_set(priv, MSM6242_CD_HOLD, MSM6242_CD);
+ cnt--;
+ }
+
+ if (!cnt)
+ pr_warn("timed out waiting for RTC (0x%x)\n",
+ msm6242_read(priv, MSM6242_CD));
+}
+
+static void msm6242_unlock(struct msm6242_priv *priv)
+{
+ msm6242_clear(priv, MSM6242_CD_HOLD, MSM6242_CD);
+}
+
+static int msm6242_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct msm6242_priv *priv = dev_get_drvdata(dev);
+
+ msm6242_lock(priv);
+
+ tm->tm_sec = msm6242_read(priv, MSM6242_SECOND10) * 10 +
+ msm6242_read(priv, MSM6242_SECOND1);
+ tm->tm_min = msm6242_read(priv, MSM6242_MINUTE10) * 10 +
+ msm6242_read(priv, MSM6242_MINUTE1);
+ tm->tm_hour = (msm6242_read(priv, MSM6242_HOUR10 & 3)) * 10 +
+ msm6242_read(priv, MSM6242_HOUR1);
+ tm->tm_mday = msm6242_read(priv, MSM6242_DAY10) * 10 +
+ msm6242_read(priv, MSM6242_DAY1);
+ tm->tm_wday = msm6242_read(priv, MSM6242_WEEK);
+ tm->tm_mon = msm6242_read(priv, MSM6242_MONTH10) * 10 +
+ msm6242_read(priv, MSM6242_MONTH1) - 1;
+ tm->tm_year = msm6242_read(priv, MSM6242_YEAR10) * 10 +
+ msm6242_read(priv, MSM6242_YEAR1);
+ if (tm->tm_year <= 69)
+ tm->tm_year += 100;
+
+ if (!(msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)) {
+ unsigned int pm = msm6242_read(priv, MSM6242_HOUR10) &
+ MSM6242_HOUR10_PM;
+ if (!pm && tm->tm_hour == 12)
+ tm->tm_hour = 0;
+ else if (pm && tm->tm_hour != 12)
+ tm->tm_hour += 12;
+ }
+
+ msm6242_unlock(priv);
+
+ return rtc_valid_tm(tm);
+}
+
+static int msm6242_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct msm6242_priv *priv = dev_get_drvdata(dev);
+
+ msm6242_lock(priv);
+
+ msm6242_write(priv, tm->tm_sec / 10, MSM6242_SECOND10);
+ msm6242_write(priv, tm->tm_sec % 10, MSM6242_SECOND1);
+ msm6242_write(priv, tm->tm_min / 10, MSM6242_MINUTE10);
+ msm6242_write(priv, tm->tm_min % 10, MSM6242_MINUTE1);
+ if (msm6242_read(priv, MSM6242_CF) & MSM6242_CF_24H)
+ msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
+ else if (tm->tm_hour >= 12)
+ msm6242_write(priv, MSM6242_HOUR10_PM + (tm->tm_hour - 12) / 10,
+ MSM6242_HOUR10);
+ else
+ msm6242_write(priv, tm->tm_hour / 10, MSM6242_HOUR10);
+ msm6242_write(priv, tm->tm_hour % 10, MSM6242_HOUR1);
+ msm6242_write(priv, tm->tm_mday / 10, MSM6242_DAY10);
+ msm6242_write(priv, tm->tm_mday % 10, MSM6242_DAY1);
+ if (tm->tm_wday != -1)
+ msm6242_write(priv, tm->tm_wday, MSM6242_WEEK);
+ msm6242_write(priv, (tm->tm_mon + 1) / 10, MSM6242_MONTH10);
+ msm6242_write(priv, (tm->tm_mon + 1) % 10, MSM6242_MONTH1);
+ if (tm->tm_year >= 100)
+ tm->tm_year -= 100;
+ msm6242_write(priv, tm->tm_year / 10, MSM6242_YEAR10);
+ msm6242_write(priv, tm->tm_year % 10, MSM6242_YEAR1);
+
+ msm6242_unlock(priv);
+ return 0;
+}
+
+static const struct rtc_class_ops msm6242_rtc_ops = {
+ .read_time = msm6242_read_time,
+ .set_time = msm6242_set_time,
+};
+
+static int __init msm6242_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct msm6242_priv *priv;
+ struct rtc_device *rtc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!priv->regs)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ rtc = devm_rtc_device_register(&pdev->dev, "rtc-msm6242",
+ &msm6242_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ priv->rtc = rtc;
+ return 0;
+}
+
+static struct platform_driver msm6242_rtc_driver = {
+ .driver = {
+ .name = "rtc-msm6242",
+ },
+};
+
+module_platform_driver_probe(msm6242_rtc_driver, msm6242_rtc_probe);
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Oki MSM6242 RTC driver");
+MODULE_ALIAS("platform:rtc-msm6242");
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
new file mode 100644
index 000000000..423762241
--- /dev/null
+++ b/drivers/rtc/rtc-mv.c
@@ -0,0 +1,341 @@
+/*
+ * Driver for the RTC in Marvell SoCs.
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/gfp.h>
+#include <linux/module.h>
+
+
+#define RTC_TIME_REG_OFFS 0
+#define RTC_SECONDS_OFFS 0
+#define RTC_MINUTES_OFFS 8
+#define RTC_HOURS_OFFS 16
+#define RTC_WDAY_OFFS 24
+#define RTC_HOURS_12H_MODE (1 << 22) /* 12 hours mode */
+
+#define RTC_DATE_REG_OFFS 4
+#define RTC_MDAY_OFFS 0
+#define RTC_MONTH_OFFS 8
+#define RTC_YEAR_OFFS 16
+
+#define RTC_ALARM_TIME_REG_OFFS 8
+#define RTC_ALARM_DATE_REG_OFFS 0xc
+#define RTC_ALARM_VALID (1 << 7)
+
+#define RTC_ALARM_INTERRUPT_MASK_REG_OFFS 0x10
+#define RTC_ALARM_INTERRUPT_CASUE_REG_OFFS 0x14
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+};
+
+static int mv_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 rtc_reg;
+
+ rtc_reg = (bin2bcd(tm->tm_sec) << RTC_SECONDS_OFFS) |
+ (bin2bcd(tm->tm_min) << RTC_MINUTES_OFFS) |
+ (bin2bcd(tm->tm_hour) << RTC_HOURS_OFFS) |
+ (bin2bcd(tm->tm_wday) << RTC_WDAY_OFFS);
+ writel(rtc_reg, ioaddr + RTC_TIME_REG_OFFS);
+
+ rtc_reg = (bin2bcd(tm->tm_mday) << RTC_MDAY_OFFS) |
+ (bin2bcd(tm->tm_mon + 1) << RTC_MONTH_OFFS) |
+ (bin2bcd(tm->tm_year % 100) << RTC_YEAR_OFFS);
+ writel(rtc_reg, ioaddr + RTC_DATE_REG_OFFS);
+
+ return 0;
+}
+
+static int mv_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 rtc_time, rtc_date;
+ unsigned int year, month, day, hour, minute, second, wday;
+
+ rtc_time = readl(ioaddr + RTC_TIME_REG_OFFS);
+ rtc_date = readl(ioaddr + RTC_DATE_REG_OFFS);
+
+ second = rtc_time & 0x7f;
+ minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
+ hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */
+ wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
+
+ day = rtc_date & 0x3f;
+ month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f;
+ year = (rtc_date >> RTC_YEAR_OFFS) & 0xff;
+
+ tm->tm_sec = bcd2bin(second);
+ tm->tm_min = bcd2bin(minute);
+ tm->tm_hour = bcd2bin(hour);
+ tm->tm_mday = bcd2bin(day);
+ tm->tm_wday = bcd2bin(wday);
+ tm->tm_mon = bcd2bin(month) - 1;
+ /* hw counts from year 2000, but tm_year is relative to 1900 */
+ tm->tm_year = bcd2bin(year) + 100;
+
+ return rtc_valid_tm(tm);
+}
+
+static int mv_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 rtc_time, rtc_date;
+ unsigned int year, month, day, hour, minute, second, wday;
+
+ rtc_time = readl(ioaddr + RTC_ALARM_TIME_REG_OFFS);
+ rtc_date = readl(ioaddr + RTC_ALARM_DATE_REG_OFFS);
+
+ second = rtc_time & 0x7f;
+ minute = (rtc_time >> RTC_MINUTES_OFFS) & 0x7f;
+ hour = (rtc_time >> RTC_HOURS_OFFS) & 0x3f; /* assume 24 hours mode */
+ wday = (rtc_time >> RTC_WDAY_OFFS) & 0x7;
+
+ day = rtc_date & 0x3f;
+ month = (rtc_date >> RTC_MONTH_OFFS) & 0x3f;
+ year = (rtc_date >> RTC_YEAR_OFFS) & 0xff;
+
+ alm->time.tm_sec = bcd2bin(second);
+ alm->time.tm_min = bcd2bin(minute);
+ alm->time.tm_hour = bcd2bin(hour);
+ alm->time.tm_mday = bcd2bin(day);
+ alm->time.tm_wday = bcd2bin(wday);
+ alm->time.tm_mon = bcd2bin(month) - 1;
+ /* hw counts from year 2000, but tm_year is relative to 1900 */
+ alm->time.tm_year = bcd2bin(year) + 100;
+
+ if (rtc_valid_tm(&alm->time) < 0) {
+ dev_err(dev, "retrieved alarm date/time is not valid.\n");
+ rtc_time_to_tm(0, &alm->time);
+ }
+
+ alm->enabled = !!readl(ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ return 0;
+}
+
+static int mv_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 rtc_reg = 0;
+
+ if (alm->time.tm_sec >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_sec))
+ << RTC_SECONDS_OFFS;
+ if (alm->time.tm_min >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_min))
+ << RTC_MINUTES_OFFS;
+ if (alm->time.tm_hour >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_hour))
+ << RTC_HOURS_OFFS;
+
+ writel(rtc_reg, ioaddr + RTC_ALARM_TIME_REG_OFFS);
+
+ if (alm->time.tm_mday >= 0)
+ rtc_reg = (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mday))
+ << RTC_MDAY_OFFS;
+ else
+ rtc_reg = 0;
+
+ if (alm->time.tm_mon >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_mon + 1))
+ << RTC_MONTH_OFFS;
+
+ if (alm->time.tm_year >= 0)
+ rtc_reg |= (RTC_ALARM_VALID | bin2bcd(alm->time.tm_year % 100))
+ << RTC_YEAR_OFFS;
+
+ writel(rtc_reg, ioaddr + RTC_ALARM_DATE_REG_OFFS);
+ writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
+ writel(alm->enabled ? 1 : 0,
+ ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+
+ return 0;
+}
+
+static int mv_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ if (pdata->irq < 0)
+ return -EINVAL; /* fall back into rtc-dev's emulation */
+
+ if (enabled)
+ writel(1, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ else
+ writel(0, ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ return 0;
+}
+
+static irqreturn_t mv_rtc_interrupt(int irq, void *data)
+{
+ struct rtc_plat_data *pdata = data;
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ /* alarm irq? */
+ if (!readl(ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS))
+ return IRQ_NONE;
+
+ /* clear interrupt */
+ writel(0, ioaddr + RTC_ALARM_INTERRUPT_CASUE_REG_OFFS);
+ rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops mv_rtc_ops = {
+ .read_time = mv_rtc_read_time,
+ .set_time = mv_rtc_set_time,
+};
+
+static const struct rtc_class_ops mv_rtc_alarm_ops = {
+ .read_time = mv_rtc_read_time,
+ .set_time = mv_rtc_set_time,
+ .read_alarm = mv_rtc_read_alarm,
+ .set_alarm = mv_rtc_set_alarm,
+ .alarm_irq_enable = mv_rtc_alarm_irq_enable,
+};
+
+static int __init mv_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct rtc_plat_data *pdata;
+ u32 rtc_time;
+ u32 rtc_date;
+ int ret = 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->ioaddr))
+ return PTR_ERR(pdata->ioaddr);
+
+ pdata->clk = devm_clk_get(&pdev->dev, NULL);
+ /* Not all SoCs require a clock.*/
+ if (!IS_ERR(pdata->clk))
+ clk_prepare_enable(pdata->clk);
+
+ /* make sure the 24 hours mode is enabled */
+ rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);
+ if (rtc_time & RTC_HOURS_12H_MODE) {
+ dev_err(&pdev->dev, "24 Hours mode not supported.\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* make sure it is actually functional */
+ if (rtc_time == 0x01000000) {
+ ssleep(1);
+ rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS);
+ if (rtc_time == 0x01000000) {
+ dev_err(&pdev->dev, "internal RTC not ticking\n");
+ ret = -ENODEV;
+ goto out;
+ }
+ }
+
+ /*
+ * A date after January 19th, 2038 does not fit on 32 bits and
+ * will confuse the kernel and userspace. Reset to a sane date
+ * (January 1st, 2013) if we're after 2038.
+ */
+ rtc_date = readl(pdata->ioaddr + RTC_DATE_REG_OFFS);
+ if (bcd2bin((rtc_date >> RTC_YEAR_OFFS) & 0xff) >= 38) {
+ dev_info(&pdev->dev, "invalid RTC date, resetting to January 1st, 2013\n");
+ writel(0x130101, pdata->ioaddr + RTC_DATE_REG_OFFS);
+ }
+
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ platform_set_drvdata(pdev, pdata);
+
+ if (pdata->irq >= 0) {
+ device_init_wakeup(&pdev->dev, 1);
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &mv_rtc_alarm_ops,
+ THIS_MODULE);
+ } else {
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &mv_rtc_ops, THIS_MODULE);
+ }
+ if (IS_ERR(pdata->rtc)) {
+ ret = PTR_ERR(pdata->rtc);
+ goto out;
+ }
+
+ if (pdata->irq >= 0) {
+ writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
+ if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt,
+ IRQF_SHARED,
+ pdev->name, pdata) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ }
+ }
+
+ return 0;
+out:
+ if (!IS_ERR(pdata->clk))
+ clk_disable_unprepare(pdata->clk);
+
+ return ret;
+}
+
+static int __exit mv_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq >= 0)
+ device_init_wakeup(&pdev->dev, 0);
+
+ if (!IS_ERR(pdata->clk))
+ clk_disable_unprepare(pdata->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtc_mv_of_match_table[] = {
+ { .compatible = "marvell,orion-rtc", },
+ {}
+};
+#endif
+
+static struct platform_driver mv_rtc_driver = {
+ .remove = __exit_p(mv_rtc_remove),
+ .driver = {
+ .name = "rtc-mv",
+ .of_match_table = of_match_ptr(rtc_mv_of_match_table),
+ },
+};
+
+module_platform_driver_probe(mv_rtc_driver, mv_rtc_probe);
+
+MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>");
+MODULE_DESCRIPTION("Marvell RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-mv");
diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c
new file mode 100644
index 000000000..09d422b9f
--- /dev/null
+++ b/drivers/rtc/rtc-mxc.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/io.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#define RTC_INPUT_CLK_32768HZ (0x00 << 5)
+#define RTC_INPUT_CLK_32000HZ (0x01 << 5)
+#define RTC_INPUT_CLK_38400HZ (0x02 << 5)
+
+#define RTC_SW_BIT (1 << 0)
+#define RTC_ALM_BIT (1 << 2)
+#define RTC_1HZ_BIT (1 << 4)
+#define RTC_2HZ_BIT (1 << 7)
+#define RTC_SAM0_BIT (1 << 8)
+#define RTC_SAM1_BIT (1 << 9)
+#define RTC_SAM2_BIT (1 << 10)
+#define RTC_SAM3_BIT (1 << 11)
+#define RTC_SAM4_BIT (1 << 12)
+#define RTC_SAM5_BIT (1 << 13)
+#define RTC_SAM6_BIT (1 << 14)
+#define RTC_SAM7_BIT (1 << 15)
+#define PIT_ALL_ON (RTC_2HZ_BIT | RTC_SAM0_BIT | RTC_SAM1_BIT | \
+ RTC_SAM2_BIT | RTC_SAM3_BIT | RTC_SAM4_BIT | \
+ RTC_SAM5_BIT | RTC_SAM6_BIT | RTC_SAM7_BIT)
+
+#define RTC_ENABLE_BIT (1 << 7)
+
+#define MAX_PIE_NUM 9
+#define MAX_PIE_FREQ 512
+static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = {
+ { 2, RTC_2HZ_BIT },
+ { 4, RTC_SAM0_BIT },
+ { 8, RTC_SAM1_BIT },
+ { 16, RTC_SAM2_BIT },
+ { 32, RTC_SAM3_BIT },
+ { 64, RTC_SAM4_BIT },
+ { 128, RTC_SAM5_BIT },
+ { 256, RTC_SAM6_BIT },
+ { MAX_PIE_FREQ, RTC_SAM7_BIT },
+};
+
+#define MXC_RTC_TIME 0
+#define MXC_RTC_ALARM 1
+
+#define RTC_HOURMIN 0x00 /* 32bit rtc hour/min counter reg */
+#define RTC_SECOND 0x04 /* 32bit rtc seconds counter reg */
+#define RTC_ALRM_HM 0x08 /* 32bit rtc alarm hour/min reg */
+#define RTC_ALRM_SEC 0x0C /* 32bit rtc alarm seconds reg */
+#define RTC_RTCCTL 0x10 /* 32bit rtc control reg */
+#define RTC_RTCISR 0x14 /* 32bit rtc interrupt status reg */
+#define RTC_RTCIENR 0x18 /* 32bit rtc interrupt enable reg */
+#define RTC_STPWCH 0x1C /* 32bit rtc stopwatch min reg */
+#define RTC_DAYR 0x20 /* 32bit rtc days counter reg */
+#define RTC_DAYALARM 0x24 /* 32bit rtc day alarm reg */
+#define RTC_TEST1 0x28 /* 32bit rtc test reg 1 */
+#define RTC_TEST2 0x2C /* 32bit rtc test reg 2 */
+#define RTC_TEST3 0x30 /* 32bit rtc test reg 3 */
+
+enum imx_rtc_type {
+ IMX1_RTC,
+ IMX21_RTC,
+};
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ struct clk *clk;
+ struct rtc_time g_rtc_alarm;
+ enum imx_rtc_type devtype;
+};
+
+static struct platform_device_id imx_rtc_devtype[] = {
+ {
+ .name = "imx1-rtc",
+ .driver_data = IMX1_RTC,
+ }, {
+ .name = "imx21-rtc",
+ .driver_data = IMX21_RTC,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, imx_rtc_devtype);
+
+static inline int is_imx1_rtc(struct rtc_plat_data *data)
+{
+ return data->devtype == IMX1_RTC;
+}
+
+/*
+ * This function is used to obtain the RTC time or the alarm value in
+ * second.
+ */
+static time64_t get_alarm_or_time(struct device *dev, int time_alarm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 day = 0, hr = 0, min = 0, sec = 0, hr_min = 0;
+
+ switch (time_alarm) {
+ case MXC_RTC_TIME:
+ day = readw(ioaddr + RTC_DAYR);
+ hr_min = readw(ioaddr + RTC_HOURMIN);
+ sec = readw(ioaddr + RTC_SECOND);
+ break;
+ case MXC_RTC_ALARM:
+ day = readw(ioaddr + RTC_DAYALARM);
+ hr_min = readw(ioaddr + RTC_ALRM_HM) & 0xffff;
+ sec = readw(ioaddr + RTC_ALRM_SEC);
+ break;
+ }
+
+ hr = hr_min >> 8;
+ min = hr_min & 0xff;
+
+ return ((((time64_t)day * 24 + hr) * 60) + min) * 60 + sec;
+}
+
+/*
+ * This function sets the RTC alarm value or the time value.
+ */
+static void set_alarm_or_time(struct device *dev, int time_alarm, time64_t time)
+{
+ u32 tod, day, hr, min, sec, temp;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ day = div_s64_rem(time, 86400, &tod);
+
+ /* time is within a day now */
+ hr = tod / 3600;
+ tod -= hr * 3600;
+
+ /* time is within an hour now */
+ min = tod / 60;
+ sec = tod - min * 60;
+
+ temp = (hr << 8) + min;
+
+ switch (time_alarm) {
+ case MXC_RTC_TIME:
+ writew(day, ioaddr + RTC_DAYR);
+ writew(sec, ioaddr + RTC_SECOND);
+ writew(temp, ioaddr + RTC_HOURMIN);
+ break;
+ case MXC_RTC_ALARM:
+ writew(day, ioaddr + RTC_DAYALARM);
+ writew(sec, ioaddr + RTC_ALRM_SEC);
+ writew(temp, ioaddr + RTC_ALRM_HM);
+ break;
+ }
+}
+
+/*
+ * This function updates the RTC alarm registers and then clears all the
+ * interrupt status bits.
+ */
+static void rtc_update_alarm(struct device *dev, struct rtc_time *alrm)
+{
+ time64_t time;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ time = rtc_tm_to_time64(alrm);
+
+ /* clear all the interrupt status bits */
+ writew(readw(ioaddr + RTC_RTCISR), ioaddr + RTC_RTCISR);
+ set_alarm_or_time(dev, MXC_RTC_ALARM, time);
+}
+
+static void mxc_rtc_irq_enable(struct device *dev, unsigned int bit,
+ unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u32 reg;
+
+ spin_lock_irq(&pdata->rtc->irq_lock);
+ reg = readw(ioaddr + RTC_RTCIENR);
+
+ if (enabled)
+ reg |= bit;
+ else
+ reg &= ~bit;
+
+ writew(reg, ioaddr + RTC_RTCIENR);
+ spin_unlock_irq(&pdata->rtc->irq_lock);
+}
+
+/* This function is the RTC interrupt service routine. */
+static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long flags;
+ u32 status;
+ u32 events = 0;
+
+ spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
+ status = readw(ioaddr + RTC_RTCISR) & readw(ioaddr + RTC_RTCIENR);
+ /* clear interrupt sources */
+ writew(status, ioaddr + RTC_RTCISR);
+
+ /* update irq data & counter */
+ if (status & RTC_ALM_BIT) {
+ events |= (RTC_AF | RTC_IRQF);
+ /* RTC alarm should be one-shot */
+ mxc_rtc_irq_enable(&pdev->dev, RTC_ALM_BIT, 0);
+ }
+
+ if (status & RTC_1HZ_BIT)
+ events |= (RTC_UF | RTC_IRQF);
+
+ if (status & PIT_ALL_ON)
+ events |= (RTC_PF | RTC_IRQF);
+
+ rtc_update_irq(pdata->rtc, 1, events);
+ spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Clear all interrupts and release the IRQ
+ */
+static void mxc_rtc_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ spin_lock_irq(&pdata->rtc->irq_lock);
+
+ /* Disable all rtc interrupts */
+ writew(0, ioaddr + RTC_RTCIENR);
+
+ /* Clear all interrupt status */
+ writew(0xffffffff, ioaddr + RTC_RTCISR);
+
+ spin_unlock_irq(&pdata->rtc->irq_lock);
+}
+
+static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ mxc_rtc_irq_enable(dev, RTC_ALM_BIT, enabled);
+ return 0;
+}
+
+/*
+ * This function reads the current RTC time into tm in Gregorian date.
+ */
+static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ time64_t val;
+
+ /* Avoid roll-over from reading the different registers */
+ do {
+ val = get_alarm_or_time(dev, MXC_RTC_TIME);
+ } while (val != get_alarm_or_time(dev, MXC_RTC_TIME));
+
+ rtc_time64_to_tm(val, tm);
+
+ return 0;
+}
+
+/*
+ * This function sets the internal RTC time based on tm in Gregorian date.
+ */
+static int mxc_rtc_set_mmss(struct device *dev, time64_t time)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ /*
+ * TTC_DAYR register is 9-bit in MX1 SoC, save time and day of year only
+ */
+ if (is_imx1_rtc(pdata)) {
+ struct rtc_time tm;
+
+ rtc_time64_to_tm(time, &tm);
+ tm.tm_year = 70;
+ time = rtc_tm_to_time64(&tm);
+ }
+
+ /* Avoid roll-over from reading the different registers */
+ do {
+ set_alarm_or_time(dev, MXC_RTC_TIME, time);
+ } while (time != get_alarm_or_time(dev, MXC_RTC_TIME));
+
+ return 0;
+}
+
+/*
+ * This function reads the current alarm value into the passed in 'alrm'
+ * argument. It updates the alrm's pending field value based on the whether
+ * an alarm interrupt occurs or not.
+ */
+static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+
+ rtc_time64_to_tm(get_alarm_or_time(dev, MXC_RTC_ALARM), &alrm->time);
+ alrm->pending = ((readw(ioaddr + RTC_RTCISR) & RTC_ALM_BIT)) ? 1 : 0;
+
+ return 0;
+}
+
+/*
+ * This function sets the RTC alarm based on passed in alrm.
+ */
+static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ rtc_update_alarm(dev, &alrm->time);
+
+ memcpy(&pdata->g_rtc_alarm, &alrm->time, sizeof(struct rtc_time));
+ mxc_rtc_irq_enable(dev, RTC_ALM_BIT, alrm->enabled);
+
+ return 0;
+}
+
+/* RTC layer */
+static struct rtc_class_ops mxc_rtc_ops = {
+ .release = mxc_rtc_release,
+ .read_time = mxc_rtc_read_time,
+ .set_mmss64 = mxc_rtc_set_mmss,
+ .read_alarm = mxc_rtc_read_alarm,
+ .set_alarm = mxc_rtc_set_alarm,
+ .alarm_irq_enable = mxc_rtc_alarm_irq_enable,
+};
+
+static int mxc_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct rtc_device *rtc;
+ struct rtc_plat_data *pdata = NULL;
+ u32 reg;
+ unsigned long rate;
+ int ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->devtype = pdev->id_entry->driver_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->ioaddr))
+ return PTR_ERR(pdata->ioaddr);
+
+ pdata->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pdata->clk)) {
+ dev_err(&pdev->dev, "unable to get clock!\n");
+ return PTR_ERR(pdata->clk);
+ }
+
+ ret = clk_prepare_enable(pdata->clk);
+ if (ret)
+ return ret;
+
+ rate = clk_get_rate(pdata->clk);
+
+ if (rate == 32768)
+ reg = RTC_INPUT_CLK_32768HZ;
+ else if (rate == 32000)
+ reg = RTC_INPUT_CLK_32000HZ;
+ else if (rate == 38400)
+ reg = RTC_INPUT_CLK_38400HZ;
+ else {
+ dev_err(&pdev->dev, "rtc clock is not valid (%lu)\n", rate);
+ ret = -EINVAL;
+ goto exit_put_clk;
+ }
+
+ reg |= RTC_ENABLE_BIT;
+ writew(reg, (pdata->ioaddr + RTC_RTCCTL));
+ if (((readw(pdata->ioaddr + RTC_RTCCTL)) & RTC_ENABLE_BIT) == 0) {
+ dev_err(&pdev->dev, "hardware module can't be enabled!\n");
+ ret = -EIO;
+ goto exit_put_clk;
+ }
+
+ platform_set_drvdata(pdev, pdata);
+
+ /* Configure and enable the RTC */
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ if (pdata->irq >= 0 &&
+ devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt,
+ IRQF_SHARED, pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = -1;
+ }
+
+ if (pdata->irq >= 0)
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &mxc_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto exit_put_clk;
+ }
+
+ pdata->rtc = rtc;
+
+ return 0;
+
+exit_put_clk:
+ clk_disable_unprepare(pdata->clk);
+
+ return ret;
+}
+
+static int mxc_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(pdata->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mxc_rtc_suspend(struct device *dev)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(pdata->irq);
+
+ return 0;
+}
+
+static int mxc_rtc_resume(struct device *dev)
+{
+ struct rtc_plat_data *pdata = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(pdata->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mxc_rtc_pm_ops, mxc_rtc_suspend, mxc_rtc_resume);
+
+static struct platform_driver mxc_rtc_driver = {
+ .driver = {
+ .name = "mxc_rtc",
+ .pm = &mxc_rtc_pm_ops,
+ },
+ .id_table = imx_rtc_devtype,
+ .probe = mxc_rtc_probe,
+ .remove = mxc_rtc_remove,
+};
+
+module_platform_driver(mxc_rtc_driver)
+
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_DESCRIPTION("RTC driver for Freescale MXC");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c
new file mode 100644
index 000000000..09fc1c19f
--- /dev/null
+++ b/drivers/rtc/rtc-nuc900.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2008-2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+
+/* RTC Control Registers */
+#define REG_RTC_INIR 0x00
+#define REG_RTC_AER 0x04
+#define REG_RTC_FCR 0x08
+#define REG_RTC_TLR 0x0C
+#define REG_RTC_CLR 0x10
+#define REG_RTC_TSSR 0x14
+#define REG_RTC_DWR 0x18
+#define REG_RTC_TAR 0x1C
+#define REG_RTC_CAR 0x20
+#define REG_RTC_LIR 0x24
+#define REG_RTC_RIER 0x28
+#define REG_RTC_RIIR 0x2C
+#define REG_RTC_TTR 0x30
+
+#define RTCSET 0x01
+#define AERRWENB 0x10000
+#define INIRRESET 0xa5eb1357
+#define AERPOWERON 0xA965
+#define AERPOWEROFF 0x0000
+#define LEAPYEAR 0x0001
+#define TICKENB 0x80
+#define TICKINTENB 0x0002
+#define ALARMINTENB 0x0001
+#define MODE24 0x0001
+
+struct nuc900_rtc {
+ int irq_num;
+ void __iomem *rtc_reg;
+ struct rtc_device *rtcdev;
+};
+
+struct nuc900_bcd_time {
+ int bcd_sec;
+ int bcd_min;
+ int bcd_hour;
+ int bcd_mday;
+ int bcd_mon;
+ int bcd_year;
+};
+
+static irqreturn_t nuc900_rtc_interrupt(int irq, void *_rtc)
+{
+ struct nuc900_rtc *rtc = _rtc;
+ unsigned long events = 0, rtc_irq;
+
+ rtc_irq = __raw_readl(rtc->rtc_reg + REG_RTC_RIIR);
+
+ if (rtc_irq & ALARMINTENB) {
+ rtc_irq &= ~ALARMINTENB;
+ __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
+ events |= RTC_AF | RTC_IRQF;
+ }
+
+ if (rtc_irq & TICKINTENB) {
+ rtc_irq &= ~TICKINTENB;
+ __raw_writel(rtc_irq, rtc->rtc_reg + REG_RTC_RIIR);
+ events |= RTC_UF | RTC_IRQF;
+ }
+
+ rtc_update_irq(rtc->rtcdev, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
+{
+ unsigned int timeout = 0x1000;
+ __raw_writel(INIRRESET, nuc900_rtc->rtc_reg + REG_RTC_INIR);
+
+ mdelay(10);
+
+ __raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
+
+ while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
+ && timeout--)
+ mdelay(1);
+
+ if (!timeout)
+ return ERR_PTR(-EPERM);
+
+ return NULL;
+}
+
+static int nuc900_rtc_bcd2bin(unsigned int timereg,
+ unsigned int calreg, struct rtc_time *tm)
+{
+ tm->tm_mday = bcd2bin(calreg >> 0);
+ tm->tm_mon = bcd2bin(calreg >> 8);
+ tm->tm_year = bcd2bin(calreg >> 16) + 100;
+
+ tm->tm_sec = bcd2bin(timereg >> 0);
+ tm->tm_min = bcd2bin(timereg >> 8);
+ tm->tm_hour = bcd2bin(timereg >> 16);
+
+ return rtc_valid_tm(tm);
+}
+
+static void nuc900_rtc_bin2bcd(struct device *dev, struct rtc_time *settm,
+ struct nuc900_bcd_time *gettm)
+{
+ gettm->bcd_mday = bin2bcd(settm->tm_mday) << 0;
+ gettm->bcd_mon = bin2bcd(settm->tm_mon) << 8;
+
+ if (settm->tm_year < 100) {
+ dev_warn(dev, "The year will be between 1970-1999, right?\n");
+ gettm->bcd_year = bin2bcd(settm->tm_year) << 16;
+ } else {
+ gettm->bcd_year = bin2bcd(settm->tm_year - 100) << 16;
+ }
+
+ gettm->bcd_sec = bin2bcd(settm->tm_sec) << 0;
+ gettm->bcd_min = bin2bcd(settm->tm_min) << 8;
+ gettm->bcd_hour = bin2bcd(settm->tm_hour) << 16;
+}
+
+static int nuc900_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)|
+ (ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
+ else
+ __raw_writel(__raw_readl(rtc->rtc_reg + REG_RTC_RIER)&
+ (~ALARMINTENB), rtc->rtc_reg + REG_RTC_RIER);
+
+ return 0;
+}
+
+static int nuc900_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int timeval, clrval;
+
+ timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TLR);
+ clrval = __raw_readl(rtc->rtc_reg + REG_RTC_CLR);
+
+ return nuc900_rtc_bcd2bin(timeval, clrval, tm);
+}
+
+static int nuc900_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ struct nuc900_bcd_time gettm;
+ unsigned long val;
+ int *err;
+
+ nuc900_rtc_bin2bcd(dev, tm, &gettm);
+
+ err = check_rtc_access_enable(rtc);
+ if (IS_ERR(err))
+ return PTR_ERR(err);
+
+ val = gettm.bcd_mday | gettm.bcd_mon | gettm.bcd_year;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_CLR);
+
+ val = gettm.bcd_sec | gettm.bcd_min | gettm.bcd_hour;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_TLR);
+
+ return 0;
+}
+
+static int nuc900_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int timeval, carval;
+
+ timeval = __raw_readl(rtc->rtc_reg + REG_RTC_TAR);
+ carval = __raw_readl(rtc->rtc_reg + REG_RTC_CAR);
+
+ return nuc900_rtc_bcd2bin(timeval, carval, &alrm->time);
+}
+
+static int nuc900_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct nuc900_rtc *rtc = dev_get_drvdata(dev);
+ struct nuc900_bcd_time tm;
+ unsigned long val;
+ int *err;
+
+ nuc900_rtc_bin2bcd(dev, &alrm->time, &tm);
+
+ err = check_rtc_access_enable(rtc);
+ if (IS_ERR(err))
+ return PTR_ERR(err);
+
+ val = tm.bcd_mday | tm.bcd_mon | tm.bcd_year;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_CAR);
+
+ val = tm.bcd_sec | tm.bcd_min | tm.bcd_hour;
+ __raw_writel(val, rtc->rtc_reg + REG_RTC_TAR);
+
+ return 0;
+}
+
+static struct rtc_class_ops nuc900_rtc_ops = {
+ .read_time = nuc900_rtc_read_time,
+ .set_time = nuc900_rtc_set_time,
+ .read_alarm = nuc900_rtc_read_alarm,
+ .set_alarm = nuc900_rtc_set_alarm,
+ .alarm_irq_enable = nuc900_alarm_irq_enable,
+};
+
+static int __init nuc900_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct nuc900_rtc *nuc900_rtc;
+
+ nuc900_rtc = devm_kzalloc(&pdev->dev, sizeof(struct nuc900_rtc),
+ GFP_KERNEL);
+ if (!nuc900_rtc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nuc900_rtc->rtc_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(nuc900_rtc->rtc_reg))
+ return PTR_ERR(nuc900_rtc->rtc_reg);
+
+ platform_set_drvdata(pdev, nuc900_rtc);
+
+ nuc900_rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &nuc900_rtc_ops, THIS_MODULE);
+ if (IS_ERR(nuc900_rtc->rtcdev)) {
+ dev_err(&pdev->dev, "rtc device register failed\n");
+ return PTR_ERR(nuc900_rtc->rtcdev);
+ }
+
+ __raw_writel(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_TSSR) | MODE24,
+ nuc900_rtc->rtc_reg + REG_RTC_TSSR);
+
+ nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
+ if (devm_request_irq(&pdev->dev, nuc900_rtc->irq_num,
+ nuc900_rtc_interrupt, 0, "nuc900rtc", nuc900_rtc)) {
+ dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static struct platform_driver nuc900_rtc_driver = {
+ .driver = {
+ .name = "nuc900-rtc",
+ },
+};
+
+module_platform_driver_probe(nuc900_rtc_driver, nuc900_rtc_probe);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("nuc910/nuc920 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-rtc");
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
new file mode 100644
index 000000000..8b6355ffa
--- /dev/null
+++ b/drivers/rtc/rtc-omap.c
@@ -0,0 +1,774 @@
+/*
+ * TI OMAP Real Time Clock interface for Linux
+ *
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
+ *
+ * Copyright (C) 2006 David Brownell (new RTC framework)
+ * Copyright (C) 2014 Johan Hovold <johan@kernel.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/io.h>
+
+/*
+ * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock
+ * with century-range alarm matching, driven by the 32kHz clock.
+ *
+ * The main user-visible ways it differs from PC RTCs are by omitting
+ * "don't care" alarm fields and sub-second periodic IRQs, and having
+ * an autoadjust mechanism to calibrate to the true oscillator rate.
+ *
+ * Board-specific wiring options include using split power mode with
+ * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset),
+ * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from
+ * low power modes) for OMAP1 boards (OMAP-L138 has this built into
+ * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment.
+ */
+
+/* RTC registers */
+#define OMAP_RTC_SECONDS_REG 0x00
+#define OMAP_RTC_MINUTES_REG 0x04
+#define OMAP_RTC_HOURS_REG 0x08
+#define OMAP_RTC_DAYS_REG 0x0C
+#define OMAP_RTC_MONTHS_REG 0x10
+#define OMAP_RTC_YEARS_REG 0x14
+#define OMAP_RTC_WEEKS_REG 0x18
+
+#define OMAP_RTC_ALARM_SECONDS_REG 0x20
+#define OMAP_RTC_ALARM_MINUTES_REG 0x24
+#define OMAP_RTC_ALARM_HOURS_REG 0x28
+#define OMAP_RTC_ALARM_DAYS_REG 0x2c
+#define OMAP_RTC_ALARM_MONTHS_REG 0x30
+#define OMAP_RTC_ALARM_YEARS_REG 0x34
+
+#define OMAP_RTC_CTRL_REG 0x40
+#define OMAP_RTC_STATUS_REG 0x44
+#define OMAP_RTC_INTERRUPTS_REG 0x48
+
+#define OMAP_RTC_COMP_LSB_REG 0x4c
+#define OMAP_RTC_COMP_MSB_REG 0x50
+#define OMAP_RTC_OSC_REG 0x54
+
+#define OMAP_RTC_KICK0_REG 0x6c
+#define OMAP_RTC_KICK1_REG 0x70
+
+#define OMAP_RTC_IRQWAKEEN 0x7c
+
+#define OMAP_RTC_ALARM2_SECONDS_REG 0x80
+#define OMAP_RTC_ALARM2_MINUTES_REG 0x84
+#define OMAP_RTC_ALARM2_HOURS_REG 0x88
+#define OMAP_RTC_ALARM2_DAYS_REG 0x8c
+#define OMAP_RTC_ALARM2_MONTHS_REG 0x90
+#define OMAP_RTC_ALARM2_YEARS_REG 0x94
+
+#define OMAP_RTC_PMIC_REG 0x98
+
+/* OMAP_RTC_CTRL_REG bit fields: */
+#define OMAP_RTC_CTRL_SPLIT BIT(7)
+#define OMAP_RTC_CTRL_DISABLE BIT(6)
+#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5)
+#define OMAP_RTC_CTRL_TEST BIT(4)
+#define OMAP_RTC_CTRL_MODE_12_24 BIT(3)
+#define OMAP_RTC_CTRL_AUTO_COMP BIT(2)
+#define OMAP_RTC_CTRL_ROUND_30S BIT(1)
+#define OMAP_RTC_CTRL_STOP BIT(0)
+
+/* OMAP_RTC_STATUS_REG bit fields: */
+#define OMAP_RTC_STATUS_POWER_UP BIT(7)
+#define OMAP_RTC_STATUS_ALARM2 BIT(7)
+#define OMAP_RTC_STATUS_ALARM BIT(6)
+#define OMAP_RTC_STATUS_1D_EVENT BIT(5)
+#define OMAP_RTC_STATUS_1H_EVENT BIT(4)
+#define OMAP_RTC_STATUS_1M_EVENT BIT(3)
+#define OMAP_RTC_STATUS_1S_EVENT BIT(2)
+#define OMAP_RTC_STATUS_RUN BIT(1)
+#define OMAP_RTC_STATUS_BUSY BIT(0)
+
+/* OMAP_RTC_INTERRUPTS_REG bit fields: */
+#define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4)
+#define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3)
+#define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2)
+
+/* OMAP_RTC_OSC_REG bit fields: */
+#define OMAP_RTC_OSC_32KCLK_EN BIT(6)
+
+/* OMAP_RTC_IRQWAKEEN bit fields: */
+#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1)
+
+/* OMAP_RTC_PMIC bit fields: */
+#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16)
+
+/* OMAP_RTC_KICKER values */
+#define KICK0_VALUE 0x83e70b13
+#define KICK1_VALUE 0x95a4f1e0
+
+struct omap_rtc;
+
+struct omap_rtc_device_type {
+ bool has_32kclk_en;
+ bool has_irqwakeen;
+ bool has_pmic_mode;
+ bool has_power_up_reset;
+ void (*lock)(struct omap_rtc *rtc);
+ void (*unlock)(struct omap_rtc *rtc);
+};
+
+struct omap_rtc {
+ struct rtc_device *rtc;
+ void __iomem *base;
+ int irq_alarm;
+ int irq_timer;
+ u8 interrupts_reg;
+ bool is_pmic_controller;
+ const struct omap_rtc_device_type *type;
+};
+
+static inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg)
+{
+ return readb(rtc->base + reg);
+}
+
+static inline u32 rtc_readl(struct omap_rtc *rtc, unsigned int reg)
+{
+ return readl(rtc->base + reg);
+}
+
+static inline void rtc_write(struct omap_rtc *rtc, unsigned int reg, u8 val)
+{
+ writeb(val, rtc->base + reg);
+}
+
+static inline void rtc_writel(struct omap_rtc *rtc, unsigned int reg, u32 val)
+{
+ writel(val, rtc->base + reg);
+}
+
+static void am3352_rtc_unlock(struct omap_rtc *rtc)
+{
+ rtc_writel(rtc, OMAP_RTC_KICK0_REG, KICK0_VALUE);
+ rtc_writel(rtc, OMAP_RTC_KICK1_REG, KICK1_VALUE);
+}
+
+static void am3352_rtc_lock(struct omap_rtc *rtc)
+{
+ rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0);
+ rtc_writel(rtc, OMAP_RTC_KICK1_REG, 0);
+}
+
+static void default_rtc_unlock(struct omap_rtc *rtc)
+{
+}
+
+static void default_rtc_lock(struct omap_rtc *rtc)
+{
+}
+
+/*
+ * We rely on the rtc framework to handle locking (rtc->ops_lock),
+ * so the only other requirement is that register accesses which
+ * require BUSY to be clear are made with IRQs locally disabled
+ */
+static void rtc_wait_not_busy(struct omap_rtc *rtc)
+{
+ int count;
+ u8 status;
+
+ /* BUSY may stay active for 1/32768 second (~30 usec) */
+ for (count = 0; count < 50; count++) {
+ status = rtc_read(rtc, OMAP_RTC_STATUS_REG);
+ if (!(status & OMAP_RTC_STATUS_BUSY))
+ break;
+ udelay(1);
+ }
+ /* now we have ~15 usec to read/write various registers */
+}
+
+static irqreturn_t rtc_irq(int irq, void *dev_id)
+{
+ struct omap_rtc *rtc = dev_id;
+ unsigned long events = 0;
+ u8 irq_data;
+
+ irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG);
+
+ /* alarm irq? */
+ if (irq_data & OMAP_RTC_STATUS_ALARM) {
+ rtc->type->unlock(rtc);
+ rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM);
+ rtc->type->lock(rtc);
+ events |= RTC_IRQF | RTC_AF;
+ }
+
+ /* 1/sec periodic/update irq? */
+ if (irq_data & OMAP_RTC_STATUS_1S_EVENT)
+ events |= RTC_IRQF | RTC_UF;
+
+ rtc_update_irq(rtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+ u8 reg, irqwake_reg = 0;
+
+ local_irq_disable();
+ rtc_wait_not_busy(rtc);
+ reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
+ if (rtc->type->has_irqwakeen)
+ irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN);
+
+ if (enabled) {
+ reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
+ irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN;
+ } else {
+ reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
+ irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN;
+ }
+ rtc_wait_not_busy(rtc);
+ rtc->type->unlock(rtc);
+ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg);
+ if (rtc->type->has_irqwakeen)
+ rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg);
+ rtc->type->lock(rtc);
+ local_irq_enable();
+
+ return 0;
+}
+
+/* this hardware doesn't support "don't care" alarm fields */
+static int tm2bcd(struct rtc_time *tm)
+{
+ if (rtc_valid_tm(tm) != 0)
+ return -EINVAL;
+
+ tm->tm_sec = bin2bcd(tm->tm_sec);
+ tm->tm_min = bin2bcd(tm->tm_min);
+ tm->tm_hour = bin2bcd(tm->tm_hour);
+ tm->tm_mday = bin2bcd(tm->tm_mday);
+
+ tm->tm_mon = bin2bcd(tm->tm_mon + 1);
+
+ /* epoch == 1900 */
+ if (tm->tm_year < 100 || tm->tm_year > 199)
+ return -EINVAL;
+ tm->tm_year = bin2bcd(tm->tm_year - 100);
+
+ return 0;
+}
+
+static void bcd2tm(struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(tm->tm_sec);
+ tm->tm_min = bcd2bin(tm->tm_min);
+ tm->tm_hour = bcd2bin(tm->tm_hour);
+ tm->tm_mday = bcd2bin(tm->tm_mday);
+ tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
+ /* epoch == 1900 */
+ tm->tm_year = bcd2bin(tm->tm_year) + 100;
+}
+
+static void omap_rtc_read_time_raw(struct omap_rtc *rtc, struct rtc_time *tm)
+{
+ tm->tm_sec = rtc_read(rtc, OMAP_RTC_SECONDS_REG);
+ tm->tm_min = rtc_read(rtc, OMAP_RTC_MINUTES_REG);
+ tm->tm_hour = rtc_read(rtc, OMAP_RTC_HOURS_REG);
+ tm->tm_mday = rtc_read(rtc, OMAP_RTC_DAYS_REG);
+ tm->tm_mon = rtc_read(rtc, OMAP_RTC_MONTHS_REG);
+ tm->tm_year = rtc_read(rtc, OMAP_RTC_YEARS_REG);
+}
+
+static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+
+ /* we don't report wday/yday/isdst ... */
+ local_irq_disable();
+ rtc_wait_not_busy(rtc);
+ omap_rtc_read_time_raw(rtc, tm);
+ local_irq_enable();
+
+ bcd2tm(tm);
+
+ return 0;
+}
+
+static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+
+ if (tm2bcd(tm) < 0)
+ return -EINVAL;
+
+ local_irq_disable();
+ rtc_wait_not_busy(rtc);
+
+ rtc->type->unlock(rtc);
+ rtc_write(rtc, OMAP_RTC_YEARS_REG, tm->tm_year);
+ rtc_write(rtc, OMAP_RTC_MONTHS_REG, tm->tm_mon);
+ rtc_write(rtc, OMAP_RTC_DAYS_REG, tm->tm_mday);
+ rtc_write(rtc, OMAP_RTC_HOURS_REG, tm->tm_hour);
+ rtc_write(rtc, OMAP_RTC_MINUTES_REG, tm->tm_min);
+ rtc_write(rtc, OMAP_RTC_SECONDS_REG, tm->tm_sec);
+ rtc->type->lock(rtc);
+
+ local_irq_enable();
+
+ return 0;
+}
+
+static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+ u8 interrupts;
+
+ local_irq_disable();
+ rtc_wait_not_busy(rtc);
+
+ alm->time.tm_sec = rtc_read(rtc, OMAP_RTC_ALARM_SECONDS_REG);
+ alm->time.tm_min = rtc_read(rtc, OMAP_RTC_ALARM_MINUTES_REG);
+ alm->time.tm_hour = rtc_read(rtc, OMAP_RTC_ALARM_HOURS_REG);
+ alm->time.tm_mday = rtc_read(rtc, OMAP_RTC_ALARM_DAYS_REG);
+ alm->time.tm_mon = rtc_read(rtc, OMAP_RTC_ALARM_MONTHS_REG);
+ alm->time.tm_year = rtc_read(rtc, OMAP_RTC_ALARM_YEARS_REG);
+
+ local_irq_enable();
+
+ bcd2tm(&alm->time);
+
+ interrupts = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
+ alm->enabled = !!(interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM);
+
+ return 0;
+}
+
+static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+ u8 reg, irqwake_reg = 0;
+
+ if (tm2bcd(&alm->time) < 0)
+ return -EINVAL;
+
+ local_irq_disable();
+ rtc_wait_not_busy(rtc);
+
+ rtc->type->unlock(rtc);
+ rtc_write(rtc, OMAP_RTC_ALARM_YEARS_REG, alm->time.tm_year);
+ rtc_write(rtc, OMAP_RTC_ALARM_MONTHS_REG, alm->time.tm_mon);
+ rtc_write(rtc, OMAP_RTC_ALARM_DAYS_REG, alm->time.tm_mday);
+ rtc_write(rtc, OMAP_RTC_ALARM_HOURS_REG, alm->time.tm_hour);
+ rtc_write(rtc, OMAP_RTC_ALARM_MINUTES_REG, alm->time.tm_min);
+ rtc_write(rtc, OMAP_RTC_ALARM_SECONDS_REG, alm->time.tm_sec);
+
+ reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
+ if (rtc->type->has_irqwakeen)
+ irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN);
+
+ if (alm->enabled) {
+ reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
+ irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN;
+ } else {
+ reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
+ irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN;
+ }
+ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg);
+ if (rtc->type->has_irqwakeen)
+ rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg);
+ rtc->type->lock(rtc);
+
+ local_irq_enable();
+
+ return 0;
+}
+
+static struct omap_rtc *omap_rtc_power_off_rtc;
+
+/*
+ * omap_rtc_poweroff: RTC-controlled power off
+ *
+ * The RTC can be used to control an external PMIC via the pmic_power_en pin,
+ * which can be configured to transition to OFF on ALARM2 events.
+ *
+ * Notes:
+ * The two-second alarm offset is the shortest offset possible as the alarm
+ * registers must be set before the next timer update and the offset
+ * calculation is too heavy for everything to be done within a single access
+ * period (~15 us).
+ *
+ * Called with local interrupts disabled.
+ */
+static void omap_rtc_power_off(void)
+{
+ struct omap_rtc *rtc = omap_rtc_power_off_rtc;
+ struct rtc_time tm;
+ unsigned long now;
+ u32 val;
+
+ rtc->type->unlock(rtc);
+ /* enable pmic_power_en control */
+ val = rtc_readl(rtc, OMAP_RTC_PMIC_REG);
+ rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN);
+
+ /* set alarm two seconds from now */
+ omap_rtc_read_time_raw(rtc, &tm);
+ bcd2tm(&tm);
+ rtc_tm_to_time(&tm, &now);
+ rtc_time_to_tm(now + 2, &tm);
+
+ if (tm2bcd(&tm) < 0) {
+ dev_err(&rtc->rtc->dev, "power off failed\n");
+ return;
+ }
+
+ rtc_wait_not_busy(rtc);
+
+ rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec);
+ rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min);
+ rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour);
+ rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday);
+ rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon);
+ rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year);
+
+ /*
+ * enable ALARM2 interrupt
+ *
+ * NOTE: this fails on AM3352 if rtc_write (writeb) is used
+ */
+ val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
+ rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG,
+ val | OMAP_RTC_INTERRUPTS_IT_ALARM2);
+ rtc->type->lock(rtc);
+
+ /*
+ * Wait for alarm to trigger (within two seconds) and external PMIC to
+ * power off the system. Add a 500 ms margin for external latencies
+ * (e.g. debounce circuits).
+ */
+ mdelay(2500);
+}
+
+static struct rtc_class_ops omap_rtc_ops = {
+ .read_time = omap_rtc_read_time,
+ .set_time = omap_rtc_set_time,
+ .read_alarm = omap_rtc_read_alarm,
+ .set_alarm = omap_rtc_set_alarm,
+ .alarm_irq_enable = omap_rtc_alarm_irq_enable,
+};
+
+static const struct omap_rtc_device_type omap_rtc_default_type = {
+ .has_power_up_reset = true,
+ .lock = default_rtc_lock,
+ .unlock = default_rtc_unlock,
+};
+
+static const struct omap_rtc_device_type omap_rtc_am3352_type = {
+ .has_32kclk_en = true,
+ .has_irqwakeen = true,
+ .has_pmic_mode = true,
+ .lock = am3352_rtc_lock,
+ .unlock = am3352_rtc_unlock,
+};
+
+static const struct omap_rtc_device_type omap_rtc_da830_type = {
+ .lock = am3352_rtc_lock,
+ .unlock = am3352_rtc_unlock,
+};
+
+static const struct platform_device_id omap_rtc_id_table[] = {
+ {
+ .name = "omap_rtc",
+ .driver_data = (kernel_ulong_t)&omap_rtc_default_type,
+ }, {
+ .name = "am3352-rtc",
+ .driver_data = (kernel_ulong_t)&omap_rtc_am3352_type,
+ }, {
+ .name = "da830-rtc",
+ .driver_data = (kernel_ulong_t)&omap_rtc_da830_type,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, omap_rtc_id_table);
+
+static const struct of_device_id omap_rtc_of_match[] = {
+ {
+ .compatible = "ti,am3352-rtc",
+ .data = &omap_rtc_am3352_type,
+ }, {
+ .compatible = "ti,da830-rtc",
+ .data = &omap_rtc_da830_type,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, omap_rtc_of_match);
+
+static int omap_rtc_probe(struct platform_device *pdev)
+{
+ struct omap_rtc *rtc;
+ struct resource *res;
+ u8 reg, mask, new_ctrl;
+ const struct platform_device_id *id_entry;
+ const struct of_device_id *of_id;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ of_id = of_match_device(omap_rtc_of_match, &pdev->dev);
+ if (of_id) {
+ rtc->type = of_id->data;
+ rtc->is_pmic_controller = rtc->type->has_pmic_mode &&
+ of_property_read_bool(pdev->dev.of_node,
+ "system-power-controller");
+ } else {
+ id_entry = platform_get_device_id(pdev);
+ rtc->type = (void *)id_entry->driver_data;
+ }
+
+ rtc->irq_timer = platform_get_irq(pdev, 0);
+ if (rtc->irq_timer <= 0)
+ return -ENOENT;
+
+ rtc->irq_alarm = platform_get_irq(pdev, 1);
+ if (rtc->irq_alarm <= 0)
+ return -ENOENT;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ rtc->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(rtc->base))
+ return PTR_ERR(rtc->base);
+
+ platform_set_drvdata(pdev, rtc);
+
+ /* Enable the clock/module so that we can access the registers */
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_sync(&pdev->dev);
+
+ rtc->type->unlock(rtc);
+
+ /*
+ * disable interrupts
+ *
+ * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used
+ */
+ rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, 0);
+
+ /* enable RTC functional clock */
+ if (rtc->type->has_32kclk_en) {
+ reg = rtc_read(rtc, OMAP_RTC_OSC_REG);
+ rtc_writel(rtc, OMAP_RTC_OSC_REG,
+ reg | OMAP_RTC_OSC_32KCLK_EN);
+ }
+
+ /* clear old status */
+ reg = rtc_read(rtc, OMAP_RTC_STATUS_REG);
+
+ mask = OMAP_RTC_STATUS_ALARM;
+
+ if (rtc->type->has_pmic_mode)
+ mask |= OMAP_RTC_STATUS_ALARM2;
+
+ if (rtc->type->has_power_up_reset) {
+ mask |= OMAP_RTC_STATUS_POWER_UP;
+ if (reg & OMAP_RTC_STATUS_POWER_UP)
+ dev_info(&pdev->dev, "RTC power up reset detected\n");
+ }
+
+ if (reg & mask)
+ rtc_write(rtc, OMAP_RTC_STATUS_REG, reg & mask);
+
+ /* On boards with split power, RTC_ON_NOFF won't reset the RTC */
+ reg = rtc_read(rtc, OMAP_RTC_CTRL_REG);
+ if (reg & OMAP_RTC_CTRL_STOP)
+ dev_info(&pdev->dev, "already running\n");
+
+ /* force to 24 hour mode */
+ new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP);
+ new_ctrl |= OMAP_RTC_CTRL_STOP;
+
+ /*
+ * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
+ *
+ * - Device wake-up capability setting should come through chip
+ * init logic. OMAP1 boards should initialize the "wakeup capable"
+ * flag in the platform device if the board is wired right for
+ * being woken up by RTC alarm. For OMAP-L138, this capability
+ * is built into the SoC by the "Deep Sleep" capability.
+ *
+ * - Boards wired so RTC_ON_nOFF is used as the reset signal,
+ * rather than nPWRON_RESET, should forcibly enable split
+ * power mode. (Some chip errata report that RTC_CTRL_SPLIT
+ * is write-only, and always reads as zero...)
+ */
+
+ if (new_ctrl & OMAP_RTC_CTRL_SPLIT)
+ dev_info(&pdev->dev, "split power mode\n");
+
+ if (reg != new_ctrl)
+ rtc_write(rtc, OMAP_RTC_CTRL_REG, new_ctrl);
+
+ rtc->type->lock(rtc);
+
+ device_init_wakeup(&pdev->dev, true);
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &omap_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ goto err;
+ }
+
+ /* handle periodic and alarm irqs */
+ ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0,
+ dev_name(&rtc->rtc->dev), rtc);
+ if (ret)
+ goto err;
+
+ if (rtc->irq_timer != rtc->irq_alarm) {
+ ret = devm_request_irq(&pdev->dev, rtc->irq_alarm, rtc_irq, 0,
+ dev_name(&rtc->rtc->dev), rtc);
+ if (ret)
+ goto err;
+ }
+
+ if (rtc->is_pmic_controller) {
+ if (!pm_power_off) {
+ omap_rtc_power_off_rtc = rtc;
+ pm_power_off = omap_rtc_power_off;
+ }
+ }
+
+ return 0;
+
+err:
+ device_init_wakeup(&pdev->dev, false);
+ rtc->type->lock(rtc);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int __exit omap_rtc_remove(struct platform_device *pdev)
+{
+ struct omap_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (pm_power_off == omap_rtc_power_off &&
+ omap_rtc_power_off_rtc == rtc) {
+ pm_power_off = NULL;
+ omap_rtc_power_off_rtc = NULL;
+ }
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ rtc->type->unlock(rtc);
+ /* leave rtc running, but disable irqs */
+ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0);
+
+ rtc->type->lock(rtc);
+
+ /* Disable the clock/module */
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int omap_rtc_suspend(struct device *dev)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc->interrupts_reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
+
+ rtc->type->unlock(rtc);
+ /*
+ * FIXME: the RTC alarm is not currently acting as a wakeup event
+ * source on some platforms, and in fact this enable() call is just
+ * saving a flag that's never used...
+ */
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->irq_alarm);
+ else
+ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0);
+ rtc->type->lock(rtc);
+
+ /* Disable the clock/module */
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int omap_rtc_resume(struct device *dev)
+{
+ struct omap_rtc *rtc = dev_get_drvdata(dev);
+
+ /* Enable the clock/module so that we can access the registers */
+ pm_runtime_get_sync(dev);
+
+ rtc->type->unlock(rtc);
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq_alarm);
+ else
+ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, rtc->interrupts_reg);
+ rtc->type->lock(rtc);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(omap_rtc_pm_ops, omap_rtc_suspend, omap_rtc_resume);
+
+static void omap_rtc_shutdown(struct platform_device *pdev)
+{
+ struct omap_rtc *rtc = platform_get_drvdata(pdev);
+ u8 mask;
+
+ /*
+ * Keep the ALARM interrupt enabled to allow the system to power up on
+ * alarm events.
+ */
+ rtc->type->unlock(rtc);
+ mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG);
+ mask &= OMAP_RTC_INTERRUPTS_IT_ALARM;
+ rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask);
+ rtc->type->lock(rtc);
+}
+
+static struct platform_driver omap_rtc_driver = {
+ .probe = omap_rtc_probe,
+ .remove = __exit_p(omap_rtc_remove),
+ .shutdown = omap_rtc_shutdown,
+ .driver = {
+ .name = "omap_rtc",
+ .pm = &omap_rtc_pm_ops,
+ .of_match_table = omap_rtc_of_match,
+ },
+ .id_table = omap_rtc_id_table,
+};
+
+module_platform_driver(omap_rtc_driver);
+
+MODULE_ALIAS("platform:omap_rtc");
+MODULE_AUTHOR("George G. Davis (and others)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c
new file mode 100644
index 000000000..7061dcae2
--- /dev/null
+++ b/drivers/rtc/rtc-opal.c
@@ -0,0 +1,262 @@
+/*
+ * IBM OPAL RTC driver
+ * Copyright (C) 2014 IBM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DRVNAME "rtc-opal"
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <asm/opal.h>
+#include <asm/firmware.h>
+
+static void opal_to_tm(u32 y_m_d, u64 h_m_s_ms, struct rtc_time *tm)
+{
+ tm->tm_year = ((bcd2bin(y_m_d >> 24) * 100) +
+ bcd2bin((y_m_d >> 16) & 0xff)) - 1900;
+ tm->tm_mon = bcd2bin((y_m_d >> 8) & 0xff) - 1;
+ tm->tm_mday = bcd2bin(y_m_d & 0xff);
+ tm->tm_hour = bcd2bin((h_m_s_ms >> 56) & 0xff);
+ tm->tm_min = bcd2bin((h_m_s_ms >> 48) & 0xff);
+ tm->tm_sec = bcd2bin((h_m_s_ms >> 40) & 0xff);
+
+ GregorianDay(tm);
+}
+
+static void tm_to_opal(struct rtc_time *tm, u32 *y_m_d, u64 *h_m_s_ms)
+{
+ *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) / 100)) << 24;
+ *y_m_d |= ((u32)bin2bcd((tm->tm_year + 1900) % 100)) << 16;
+ *y_m_d |= ((u32)bin2bcd((tm->tm_mon + 1))) << 8;
+ *y_m_d |= ((u32)bin2bcd(tm->tm_mday));
+
+ *h_m_s_ms |= ((u64)bin2bcd(tm->tm_hour)) << 56;
+ *h_m_s_ms |= ((u64)bin2bcd(tm->tm_min)) << 48;
+ *h_m_s_ms |= ((u64)bin2bcd(tm->tm_sec)) << 40;
+}
+
+static int opal_get_rtc_time(struct device *dev, struct rtc_time *tm)
+{
+ long rc = OPAL_BUSY;
+ u32 y_m_d;
+ u64 h_m_s_ms;
+ __be32 __y_m_d;
+ __be64 __h_m_s_ms;
+
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_rtc_read(&__y_m_d, &__h_m_s_ms);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ msleep(10);
+ }
+
+ if (rc != OPAL_SUCCESS)
+ return -EIO;
+
+ y_m_d = be32_to_cpu(__y_m_d);
+ h_m_s_ms = be64_to_cpu(__h_m_s_ms);
+ opal_to_tm(y_m_d, h_m_s_ms, tm);
+
+ return 0;
+}
+
+static int opal_set_rtc_time(struct device *dev, struct rtc_time *tm)
+{
+ long rc = OPAL_BUSY;
+ u32 y_m_d = 0;
+ u64 h_m_s_ms = 0;
+
+ tm_to_opal(tm, &y_m_d, &h_m_s_ms);
+ while (rc == OPAL_BUSY || rc == OPAL_BUSY_EVENT) {
+ rc = opal_rtc_write(y_m_d, h_m_s_ms);
+ if (rc == OPAL_BUSY_EVENT)
+ opal_poll_events(NULL);
+ else
+ msleep(10);
+ }
+
+ return rc == OPAL_SUCCESS ? 0 : -EIO;
+}
+
+/*
+ * TPO Timed Power-On
+ *
+ * TPO get/set OPAL calls care about the hour and min and to make it consistent
+ * with the rtc utility time conversion functions, we use the 'u64' to store
+ * its value and perform bit shift by 32 before use..
+ */
+static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ __be32 __y_m_d, __h_m;
+ struct opal_msg msg;
+ int rc, token;
+ u64 h_m_s_ms;
+ u32 y_m_d;
+
+ token = opal_async_get_token_interruptible();
+ if (token < 0) {
+ if (token != -ERESTARTSYS)
+ pr_err("Failed to get the async token\n");
+
+ return token;
+ }
+
+ rc = opal_tpo_read(token, &__y_m_d, &__h_m);
+ if (rc != OPAL_ASYNC_COMPLETION) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ rc = opal_async_wait_response(token, &msg);
+ if (rc) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ rc = be64_to_cpu(msg.params[1]);
+ if (rc != OPAL_SUCCESS) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ y_m_d = be32_to_cpu(__y_m_d);
+ h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
+ opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
+
+exit:
+ opal_async_release_token(token);
+ return rc;
+}
+
+/* Set Timed Power-On */
+static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ u64 h_m_s_ms = 0, token;
+ struct opal_msg msg;
+ u32 y_m_d = 0;
+ int rc;
+
+ tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
+
+ token = opal_async_get_token_interruptible();
+ if (token < 0) {
+ if (token != -ERESTARTSYS)
+ pr_err("Failed to get the async token\n");
+
+ return token;
+ }
+
+ /* TPO, we care about hour and minute */
+ rc = opal_tpo_write(token, y_m_d,
+ (u32)((h_m_s_ms >> 32) & 0xffff0000));
+ if (rc != OPAL_ASYNC_COMPLETION) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ rc = opal_async_wait_response(token, &msg);
+ if (rc) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ rc = be64_to_cpu(msg.params[1]);
+ if (rc != OPAL_SUCCESS)
+ rc = -EIO;
+
+exit:
+ opal_async_release_token(token);
+ return rc;
+}
+
+static const struct rtc_class_ops opal_rtc_ops = {
+ .read_time = opal_get_rtc_time,
+ .set_time = opal_set_rtc_time,
+ .read_alarm = opal_get_tpo_time,
+ .set_alarm = opal_set_tpo_time,
+};
+
+static int opal_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ if (pdev->dev.of_node && of_get_property(pdev->dev.of_node, "has-tpo",
+ NULL))
+ device_set_wakeup_capable(&pdev->dev, true);
+
+ rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ rtc->uie_unsupported = 1;
+
+ return 0;
+}
+
+static const struct of_device_id opal_rtc_match[] = {
+ {
+ .compatible = "ibm,opal-rtc",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, opal_rtc_match);
+
+static const struct platform_device_id opal_rtc_driver_ids[] = {
+ {
+ .name = "opal-rtc",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, opal_rtc_driver_ids);
+
+static struct platform_driver opal_rtc_driver = {
+ .probe = opal_rtc_probe,
+ .id_table = opal_rtc_driver_ids,
+ .driver = {
+ .name = DRVNAME,
+ .owner = THIS_MODULE,
+ .of_match_table = opal_rtc_match,
+ },
+};
+
+static int __init opal_rtc_init(void)
+{
+ if (!firmware_has_feature(FW_FEATURE_OPAL))
+ return -ENODEV;
+
+ return platform_driver_register(&opal_rtc_driver);
+}
+
+static void __exit opal_rtc_exit(void)
+{
+ platform_driver_unregister(&opal_rtc_driver);
+}
+
+MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("IBM OPAL RTC driver");
+MODULE_LICENSE("GPL");
+
+module_init(opal_rtc_init);
+module_exit(opal_rtc_exit);
diff --git a/drivers/rtc/rtc-palmas.c b/drivers/rtc/rtc-palmas.c
new file mode 100644
index 000000000..3b01d5674
--- /dev/null
+++ b/drivers/rtc/rtc-palmas.c
@@ -0,0 +1,377 @@
+/*
+ * rtc-palmas.c -- Palmas Real Time Clock driver.
+
+ * RTC driver for TI Palma series devices like TPS65913,
+ * TPS65914 power management IC.
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/bcd.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/palmas.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+
+struct palmas_rtc {
+ struct rtc_device *rtc;
+ struct device *dev;
+ unsigned int irq;
+};
+
+/* Total number of RTC registers needed to set time*/
+#define PALMAS_NUM_TIME_REGS (PALMAS_YEARS_REG - PALMAS_SECONDS_REG + 1)
+
+static int palmas_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ /* Copy RTC counting registers to static registers or latches */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_GET_TIME, PALMAS_RTC_CTRL_REG_GET_TIME);
+ if (ret < 0) {
+ dev_err(dev, "RTC CTRL reg update failed, err: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
+ rtc_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC_SECONDS reg read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_mday = bcd2bin(rtc_data[3]);
+ tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ return ret;
+}
+
+static int palmas_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[PALMAS_NUM_TIME_REGS];
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_mday);
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[5] = bin2bcd(tm->tm_year - 100);
+
+ /* Stop RTC while updating the RTC time registers */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_STOP_RTC, 0);
+ if (ret < 0) {
+ dev_err(dev, "RTC stop failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE, PALMAS_SECONDS_REG,
+ rtc_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC_SECONDS reg write failed, err = %d\n", ret);
+ return ret;
+ }
+
+ /* Start back RTC */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_STOP_RTC, PALMAS_RTC_CTRL_REG_STOP_RTC);
+ if (ret < 0)
+ dev_err(dev, "RTC start failed, err = %d\n", ret);
+ return ret;
+}
+
+static int palmas_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ u8 val;
+
+ val = enabled ? PALMAS_RTC_INTERRUPTS_REG_IT_ALARM : 0;
+ return palmas_write(palmas, PALMAS_RTC_BASE,
+ PALMAS_RTC_INTERRUPTS_REG, val);
+}
+
+static int palmas_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
+ u32 int_val;
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = palmas_bulk_read(palmas, PALMAS_RTC_BASE,
+ PALMAS_ALARM_SECONDS_REG,
+ alarm_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC_ALARM_SECONDS read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ alm->time.tm_sec = bcd2bin(alarm_data[0]);
+ alm->time.tm_min = bcd2bin(alarm_data[1]);
+ alm->time.tm_hour = bcd2bin(alarm_data[2]);
+ alm->time.tm_mday = bcd2bin(alarm_data[3]);
+ alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
+ alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
+
+ ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_INTERRUPTS_REG,
+ &int_val);
+ if (ret < 0) {
+ dev_err(dev, "RTC_INTERRUPTS reg read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ if (int_val & PALMAS_RTC_INTERRUPTS_REG_IT_ALARM)
+ alm->enabled = 1;
+ return ret;
+}
+
+static int palmas_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[PALMAS_NUM_TIME_REGS];
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = palmas_rtc_alarm_irq_enable(dev, 0);
+ if (ret < 0) {
+ dev_err(dev, "Disable RTC alarm failed\n");
+ return ret;
+ }
+
+ alarm_data[0] = bin2bcd(alm->time.tm_sec);
+ alarm_data[1] = bin2bcd(alm->time.tm_min);
+ alarm_data[2] = bin2bcd(alm->time.tm_hour);
+ alarm_data[3] = bin2bcd(alm->time.tm_mday);
+ alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
+ alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
+
+ ret = palmas_bulk_write(palmas, PALMAS_RTC_BASE,
+ PALMAS_ALARM_SECONDS_REG, alarm_data, PALMAS_NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "ALARM_SECONDS_REG write failed, err = %d\n", ret);
+ return ret;
+ }
+
+ if (alm->enabled)
+ ret = palmas_rtc_alarm_irq_enable(dev, 1);
+ return ret;
+}
+
+static int palmas_clear_interrupts(struct device *dev)
+{
+ struct palmas *palmas = dev_get_drvdata(dev->parent);
+ unsigned int rtc_reg;
+ int ret;
+
+ ret = palmas_read(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
+ &rtc_reg);
+ if (ret < 0) {
+ dev_err(dev, "RTC_STATUS read failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(palmas, PALMAS_RTC_BASE, PALMAS_RTC_STATUS_REG,
+ rtc_reg);
+ if (ret < 0) {
+ dev_err(dev, "RTC_STATUS write failed, err = %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static irqreturn_t palmas_rtc_interrupt(int irq, void *context)
+{
+ struct palmas_rtc *palmas_rtc = context;
+ struct device *dev = palmas_rtc->dev;
+ int ret;
+
+ ret = palmas_clear_interrupts(dev);
+ if (ret < 0) {
+ dev_err(dev, "RTC interrupt clear failed, err = %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ rtc_update_irq(palmas_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static struct rtc_class_ops palmas_rtc_ops = {
+ .read_time = palmas_rtc_read_time,
+ .set_time = palmas_rtc_set_time,
+ .read_alarm = palmas_rtc_read_alarm,
+ .set_alarm = palmas_rtc_set_alarm,
+ .alarm_irq_enable = palmas_rtc_alarm_irq_enable,
+};
+
+static int palmas_rtc_probe(struct platform_device *pdev)
+{
+ struct palmas *palmas = dev_get_drvdata(pdev->dev.parent);
+ struct palmas_rtc *palmas_rtc = NULL;
+ int ret;
+ bool enable_bb_charging = false;
+ bool high_bb_charging;
+
+ if (pdev->dev.of_node) {
+ enable_bb_charging = of_property_read_bool(pdev->dev.of_node,
+ "ti,backup-battery-chargeable");
+ high_bb_charging = of_property_read_bool(pdev->dev.of_node,
+ "ti,backup-battery-charge-high-current");
+ }
+
+ palmas_rtc = devm_kzalloc(&pdev->dev, sizeof(struct palmas_rtc),
+ GFP_KERNEL);
+ if (!palmas_rtc)
+ return -ENOMEM;
+
+ /* Clear pending interrupts */
+ ret = palmas_clear_interrupts(&pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clear RTC int failed, err = %d\n", ret);
+ return ret;
+ }
+
+ palmas_rtc->dev = &pdev->dev;
+ platform_set_drvdata(pdev, palmas_rtc);
+
+ if (enable_bb_charging) {
+ unsigned reg = PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG;
+
+ if (high_bb_charging)
+ reg = 0;
+
+ ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+ PALMAS_BACKUP_BATTERY_CTRL,
+ PALMAS_BACKUP_BATTERY_CTRL_BBS_BBC_LOW_ICHRG, reg);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "BACKUP_BATTERY_CTRL update failed, %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE,
+ PALMAS_BACKUP_BATTERY_CTRL,
+ PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN,
+ PALMAS_BACKUP_BATTERY_CTRL_BB_CHG_EN);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "BACKUP_BATTERY_CTRL update failed, %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Start RTC */
+ ret = palmas_update_bits(palmas, PALMAS_RTC_BASE, PALMAS_RTC_CTRL_REG,
+ PALMAS_RTC_CTRL_REG_STOP_RTC,
+ PALMAS_RTC_CTRL_REG_STOP_RTC);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "RTC_CTRL write failed, err = %d\n", ret);
+ return ret;
+ }
+
+ palmas_rtc->irq = platform_get_irq(pdev, 0);
+
+ device_init_wakeup(&pdev->dev, 1);
+ palmas_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &palmas_rtc_ops, THIS_MODULE);
+ if (IS_ERR(palmas_rtc->rtc)) {
+ ret = PTR_ERR(palmas_rtc->rtc);
+ dev_err(&pdev->dev, "RTC register failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, palmas_rtc->irq, NULL,
+ palmas_rtc_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT |
+ IRQF_EARLY_RESUME,
+ dev_name(&pdev->dev), palmas_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ request failed, err = %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int palmas_rtc_remove(struct platform_device *pdev)
+{
+ palmas_rtc_alarm_irq_enable(&pdev->dev, 0);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int palmas_rtc_suspend(struct device *dev)
+{
+ struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(palmas_rtc->irq);
+ return 0;
+}
+
+static int palmas_rtc_resume(struct device *dev)
+{
+ struct palmas_rtc *palmas_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(palmas_rtc->irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(palmas_rtc_pm_ops, palmas_rtc_suspend,
+ palmas_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_palmas_rtc_match[] = {
+ { .compatible = "ti,palmas-rtc"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_palmas_rtc_match);
+#endif
+
+static struct platform_driver palmas_rtc_driver = {
+ .probe = palmas_rtc_probe,
+ .remove = palmas_rtc_remove,
+ .driver = {
+ .name = "palmas-rtc",
+ .pm = &palmas_rtc_pm_ops,
+ .of_match_table = of_match_ptr(of_palmas_rtc_match),
+ },
+};
+
+module_platform_driver(palmas_rtc_driver);
+
+MODULE_ALIAS("platform:palmas_rtc");
+MODULE_DESCRIPTION("TI PALMAS series RTC driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-pcap.c b/drivers/rtc/rtc-pcap.c
new file mode 100644
index 000000000..c4433240d
--- /dev/null
+++ b/drivers/rtc/rtc-pcap.c
@@ -0,0 +1,194 @@
+/*
+ * pcap rtc code for Motorola EZX phones
+ *
+ * Copyright (c) 2008 guiming zhuo <gmzhuo@gmail.com>
+ * Copyright (c) 2009 Daniel Ribeiro <drwyrm@gmail.com>
+ *
+ * Based on Motorola's rtc.c Copyright (c) 2003-2005 Motorola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+struct pcap_rtc {
+ struct pcap_chip *pcap;
+ struct rtc_device *rtc;
+};
+
+static irqreturn_t pcap_rtc_irq(int irq, void *_pcap_rtc)
+{
+ struct pcap_rtc *pcap_rtc = _pcap_rtc;
+ unsigned long rtc_events;
+
+ if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ))
+ rtc_events = RTC_IRQF | RTC_UF;
+ else if (irq == pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA))
+ rtc_events = RTC_IRQF | RTC_AF;
+ else
+ rtc_events = 0;
+
+ rtc_update_irq(pcap_rtc->rtc, 1, rtc_events);
+ return IRQ_HANDLED;
+}
+
+static int pcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev);
+ struct rtc_time *tm = &alrm->time;
+ unsigned long secs;
+ u32 tod; /* time of day, seconds since midnight */
+ u32 days; /* days since 1/1/1970 */
+
+ ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TODA, &tod);
+ secs = tod & PCAP_RTC_TOD_MASK;
+
+ ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, &days);
+ secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY;
+
+ rtc_time_to_tm(secs, tm);
+
+ return 0;
+}
+
+static int pcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev);
+ struct rtc_time *tm = &alrm->time;
+ unsigned long secs;
+ u32 tod, days;
+
+ rtc_tm_to_time(tm, &secs);
+
+ tod = secs % SEC_PER_DAY;
+ ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TODA, tod);
+
+ days = secs / SEC_PER_DAY;
+ ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAYA, days);
+
+ return 0;
+}
+
+static int pcap_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev);
+ unsigned long secs;
+ u32 tod, days;
+
+ ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_TOD, &tod);
+ secs = tod & PCAP_RTC_TOD_MASK;
+
+ ezx_pcap_read(pcap_rtc->pcap, PCAP_REG_RTC_DAY, &days);
+ secs += (days & PCAP_RTC_DAY_MASK) * SEC_PER_DAY;
+
+ rtc_time_to_tm(secs, tm);
+
+ return rtc_valid_tm(tm);
+}
+
+static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev);
+ u32 tod, days;
+
+ tod = secs % SEC_PER_DAY;
+ ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_TOD, tod);
+
+ days = secs / SEC_PER_DAY;
+ ezx_pcap_write(pcap_rtc->pcap, PCAP_REG_RTC_DAY, days);
+
+ return 0;
+}
+
+static int pcap_rtc_irq_enable(struct device *dev, int pirq, unsigned int en)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pcap_rtc *pcap_rtc = platform_get_drvdata(pdev);
+
+ if (en)
+ enable_irq(pcap_to_irq(pcap_rtc->pcap, pirq));
+ else
+ disable_irq(pcap_to_irq(pcap_rtc->pcap, pirq));
+
+ return 0;
+}
+
+static int pcap_rtc_alarm_irq_enable(struct device *dev, unsigned int en)
+{
+ return pcap_rtc_irq_enable(dev, PCAP_IRQ_TODA, en);
+}
+
+static const struct rtc_class_ops pcap_rtc_ops = {
+ .read_time = pcap_rtc_read_time,
+ .read_alarm = pcap_rtc_read_alarm,
+ .set_alarm = pcap_rtc_set_alarm,
+ .set_mmss = pcap_rtc_set_mmss,
+ .alarm_irq_enable = pcap_rtc_alarm_irq_enable,
+};
+
+static int __init pcap_rtc_probe(struct platform_device *pdev)
+{
+ struct pcap_rtc *pcap_rtc;
+ int timer_irq, alarm_irq;
+ int err = -ENOMEM;
+
+ pcap_rtc = devm_kzalloc(&pdev->dev, sizeof(struct pcap_rtc),
+ GFP_KERNEL);
+ if (!pcap_rtc)
+ return err;
+
+ pcap_rtc->pcap = dev_get_drvdata(pdev->dev.parent);
+
+ platform_set_drvdata(pdev, pcap_rtc);
+
+ pcap_rtc->rtc = devm_rtc_device_register(&pdev->dev, "pcap",
+ &pcap_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pcap_rtc->rtc))
+ return PTR_ERR(pcap_rtc->rtc);
+
+ timer_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_1HZ);
+ alarm_irq = pcap_to_irq(pcap_rtc->pcap, PCAP_IRQ_TODA);
+
+ err = devm_request_irq(&pdev->dev, timer_irq, pcap_rtc_irq, 0,
+ "RTC Timer", pcap_rtc);
+ if (err)
+ return err;
+
+ err = devm_request_irq(&pdev->dev, alarm_irq, pcap_rtc_irq, 0,
+ "RTC Alarm", pcap_rtc);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+static int __exit pcap_rtc_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver pcap_rtc_driver = {
+ .remove = __exit_p(pcap_rtc_remove),
+ .driver = {
+ .name = "pcap-rtc",
+ },
+};
+
+module_platform_driver_probe(pcap_rtc_driver, pcap_rtc_probe);
+
+MODULE_DESCRIPTION("Motorola pcap rtc driver");
+MODULE_AUTHOR("guiming zhuo <gmzhuo@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
new file mode 100644
index 000000000..8a7556cbc
--- /dev/null
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -0,0 +1,367 @@
+/*
+ * An SPI driver for the Philips PCF2123 RTC
+ * Copyright 2009 Cyber Switching, Inc.
+ *
+ * Author: Chris Verges <chrisv@cyberswitching.com>
+ * Maintainers: http://www.cyberswitching.com
+ *
+ * based on the RS5C348 driver in this same directory.
+ *
+ * Thanks to Christian Pellegrin <chripell@fsfe.org> for
+ * the sysfs contributions to this driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Please note that the CS is active high, so platform data
+ * should look something like:
+ *
+ * static struct spi_board_info ek_spi_devices[] = {
+ * ...
+ * {
+ * .modalias = "rtc-pcf2123",
+ * .chip_select = 1,
+ * .controller_data = (void *)AT91_PIN_PA10,
+ * .max_speed_hz = 1000 * 1000,
+ * .mode = SPI_CS_HIGH,
+ * .bus_num = 0,
+ * },
+ * ...
+ *};
+ *
+ */
+
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/sysfs.h>
+
+#define DRV_VERSION "0.6"
+
+#define PCF2123_REG_CTRL1 (0x00) /* Control Register 1 */
+#define PCF2123_REG_CTRL2 (0x01) /* Control Register 2 */
+#define PCF2123_REG_SC (0x02) /* datetime */
+#define PCF2123_REG_MN (0x03)
+#define PCF2123_REG_HR (0x04)
+#define PCF2123_REG_DM (0x05)
+#define PCF2123_REG_DW (0x06)
+#define PCF2123_REG_MO (0x07)
+#define PCF2123_REG_YR (0x08)
+
+#define PCF2123_SUBADDR (1 << 4)
+#define PCF2123_WRITE ((0 << 7) | PCF2123_SUBADDR)
+#define PCF2123_READ ((1 << 7) | PCF2123_SUBADDR)
+
+static struct spi_driver pcf2123_driver;
+
+struct pcf2123_sysfs_reg {
+ struct device_attribute attr;
+ char name[2];
+};
+
+struct pcf2123_plat_data {
+ struct rtc_device *rtc;
+ struct pcf2123_sysfs_reg regs[16];
+};
+
+/*
+ * Causes a 30 nanosecond delay to ensure that the PCF2123 chip select
+ * is released properly after an SPI write. This function should be
+ * called after EVERY read/write call over SPI.
+ */
+static inline void pcf2123_delay_trec(void)
+{
+ ndelay(30);
+}
+
+static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr,
+ char *buffer)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct pcf2123_sysfs_reg *r;
+ u8 txbuf[1], rxbuf[1];
+ unsigned long reg;
+ int ret;
+
+ r = container_of(attr, struct pcf2123_sysfs_reg, attr);
+
+ ret = kstrtoul(r->name, 16, &reg);
+ if (ret)
+ return ret;
+
+ txbuf[0] = PCF2123_READ | reg;
+ ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1);
+ if (ret < 0)
+ return -EIO;
+ pcf2123_delay_trec();
+ return sprintf(buffer, "0x%x\n", rxbuf[0]);
+}
+
+static ssize_t pcf2123_store(struct device *dev, struct device_attribute *attr,
+ const char *buffer, size_t count) {
+ struct spi_device *spi = to_spi_device(dev);
+ struct pcf2123_sysfs_reg *r;
+ u8 txbuf[2];
+ unsigned long reg;
+ unsigned long val;
+
+ int ret;
+
+ r = container_of(attr, struct pcf2123_sysfs_reg, attr);
+
+ ret = kstrtoul(r->name, 16, &reg);
+ if (ret)
+ return ret;
+
+ ret = kstrtoul(buffer, 10, &val);
+ if (ret)
+ return ret;
+
+ txbuf[0] = PCF2123_WRITE | reg;
+ txbuf[1] = val;
+ ret = spi_write(spi, txbuf, sizeof(txbuf));
+ if (ret < 0)
+ return -EIO;
+ pcf2123_delay_trec();
+ return count;
+}
+
+static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 txbuf[1], rxbuf[7];
+ int ret;
+
+ txbuf[0] = PCF2123_READ | PCF2123_REG_SC;
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
+ rxbuf, sizeof(rxbuf));
+ if (ret < 0)
+ return ret;
+ pcf2123_delay_trec();
+
+ tm->tm_sec = bcd2bin(rxbuf[0] & 0x7F);
+ tm->tm_min = bcd2bin(rxbuf[1] & 0x7F);
+ tm->tm_hour = bcd2bin(rxbuf[2] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = bcd2bin(rxbuf[3] & 0x3F);
+ tm->tm_wday = rxbuf[4] & 0x07;
+ tm->tm_mon = bcd2bin(rxbuf[5] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(rxbuf[6]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+
+ dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* the clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup.
+ */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(dev, "retrieved date/time is not valid.\n");
+
+ return 0;
+}
+
+static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 txbuf[8];
+ int ret;
+
+ dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* Stop the counter first */
+ txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
+ txbuf[1] = 0x20;
+ ret = spi_write(spi, txbuf, 2);
+ if (ret < 0)
+ return ret;
+ pcf2123_delay_trec();
+
+ /* Set the new time */
+ txbuf[0] = PCF2123_WRITE | PCF2123_REG_SC;
+ txbuf[1] = bin2bcd(tm->tm_sec & 0x7F);
+ txbuf[2] = bin2bcd(tm->tm_min & 0x7F);
+ txbuf[3] = bin2bcd(tm->tm_hour & 0x3F);
+ txbuf[4] = bin2bcd(tm->tm_mday & 0x3F);
+ txbuf[5] = tm->tm_wday & 0x07;
+ txbuf[6] = bin2bcd((tm->tm_mon + 1) & 0x1F); /* rtc mn 1-12 */
+ txbuf[7] = bin2bcd(tm->tm_year < 100 ? tm->tm_year : tm->tm_year - 100);
+
+ ret = spi_write(spi, txbuf, sizeof(txbuf));
+ if (ret < 0)
+ return ret;
+ pcf2123_delay_trec();
+
+ /* Start the counter */
+ txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
+ txbuf[1] = 0x00;
+ ret = spi_write(spi, txbuf, 2);
+ if (ret < 0)
+ return ret;
+ pcf2123_delay_trec();
+
+ return 0;
+}
+
+static const struct rtc_class_ops pcf2123_rtc_ops = {
+ .read_time = pcf2123_rtc_read_time,
+ .set_time = pcf2123_rtc_set_time,
+};
+
+static int pcf2123_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ struct pcf2123_plat_data *pdata;
+ u8 txbuf[2], rxbuf[2];
+ int ret, i;
+
+ pdata = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_plat_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ spi->dev.platform_data = pdata;
+
+ /* Send a software reset command */
+ txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
+ txbuf[1] = 0x58;
+ dev_dbg(&spi->dev, "resetting RTC (0x%02X 0x%02X)\n",
+ txbuf[0], txbuf[1]);
+ ret = spi_write(spi, txbuf, 2 * sizeof(u8));
+ if (ret < 0)
+ goto kfree_exit;
+ pcf2123_delay_trec();
+
+ /* Stop the counter */
+ txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
+ txbuf[1] = 0x20;
+ dev_dbg(&spi->dev, "stopping RTC (0x%02X 0x%02X)\n",
+ txbuf[0], txbuf[1]);
+ ret = spi_write(spi, txbuf, 2 * sizeof(u8));
+ if (ret < 0)
+ goto kfree_exit;
+ pcf2123_delay_trec();
+
+ /* See if the counter was actually stopped */
+ txbuf[0] = PCF2123_READ | PCF2123_REG_CTRL1;
+ dev_dbg(&spi->dev, "checking for presence of RTC (0x%02X)\n",
+ txbuf[0]);
+ ret = spi_write_then_read(spi, txbuf, 1 * sizeof(u8),
+ rxbuf, 2 * sizeof(u8));
+ dev_dbg(&spi->dev, "received data from RTC (0x%02X 0x%02X)\n",
+ rxbuf[0], rxbuf[1]);
+ if (ret < 0)
+ goto kfree_exit;
+ pcf2123_delay_trec();
+
+ if (!(rxbuf[0] & 0x20)) {
+ dev_err(&spi->dev, "chip not found\n");
+ ret = -ENODEV;
+ goto kfree_exit;
+ }
+
+ dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
+ dev_info(&spi->dev, "spiclk %u KHz.\n",
+ (spi->max_speed_hz + 500) / 1000);
+
+ /* Start the counter */
+ txbuf[0] = PCF2123_WRITE | PCF2123_REG_CTRL1;
+ txbuf[1] = 0x00;
+ ret = spi_write(spi, txbuf, sizeof(txbuf));
+ if (ret < 0)
+ goto kfree_exit;
+ pcf2123_delay_trec();
+
+ /* Finalize the initialization */
+ rtc = devm_rtc_device_register(&spi->dev, pcf2123_driver.driver.name,
+ &pcf2123_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ dev_err(&spi->dev, "failed to register.\n");
+ ret = PTR_ERR(rtc);
+ goto kfree_exit;
+ }
+
+ pdata->rtc = rtc;
+
+ for (i = 0; i < 16; i++) {
+ sysfs_attr_init(&pdata->regs[i].attr.attr);
+ sprintf(pdata->regs[i].name, "%1x", i);
+ pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
+ pdata->regs[i].attr.attr.name = pdata->regs[i].name;
+ pdata->regs[i].attr.show = pcf2123_show;
+ pdata->regs[i].attr.store = pcf2123_store;
+ ret = device_create_file(&spi->dev, &pdata->regs[i].attr);
+ if (ret) {
+ dev_err(&spi->dev, "Unable to create sysfs %s\n",
+ pdata->regs[i].name);
+ goto sysfs_exit;
+ }
+ }
+
+ return 0;
+
+sysfs_exit:
+ for (i--; i >= 0; i--)
+ device_remove_file(&spi->dev, &pdata->regs[i].attr);
+
+kfree_exit:
+ spi->dev.platform_data = NULL;
+ return ret;
+}
+
+static int pcf2123_remove(struct spi_device *spi)
+{
+ struct pcf2123_plat_data *pdata = dev_get_platdata(&spi->dev);
+ int i;
+
+ if (pdata) {
+ for (i = 0; i < 16; i++)
+ if (pdata->regs[i].name[0])
+ device_remove_file(&spi->dev,
+ &pdata->regs[i].attr);
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf2123_dt_ids[] = {
+ { .compatible = "nxp,rtc-pcf2123", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, pcf2123_dt_ids);
+#endif
+
+static struct spi_driver pcf2123_driver = {
+ .driver = {
+ .name = "rtc-pcf2123",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf2123_dt_ids),
+ },
+ .probe = pcf2123_probe,
+ .remove = pcf2123_remove,
+};
+
+module_spi_driver(pcf2123_driver);
+
+MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
+MODULE_DESCRIPTION("NXP PCF2123 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c
new file mode 100644
index 000000000..9bd842e97
--- /dev/null
+++ b/drivers/rtc/rtc-pcf2127.c
@@ -0,0 +1,232 @@
+/*
+ * An I2C driver for the NXP PCF2127 RTC
+ * Copyright 2013 Til-Technologies
+ *
+ * Author: Renaud Cerrato <r.cerrato@til-technologies.fr>
+ *
+ * based on the other drivers in this same directory.
+ *
+ * http://www.nxp.com/documents/data_sheet/PCF2127AT.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define DRV_VERSION "0.0.1"
+
+#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */
+#define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */
+#define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */
+#define PCF2127_REG_SC (0x03) /* datetime */
+#define PCF2127_REG_MN (0x04)
+#define PCF2127_REG_HR (0x05)
+#define PCF2127_REG_DM (0x06)
+#define PCF2127_REG_DW (0x07)
+#define PCF2127_REG_MO (0x08)
+#define PCF2127_REG_YR (0x09)
+
+static struct i2c_driver pcf2127_driver;
+
+struct pcf2127 {
+ struct rtc_device *rtc;
+ int voltage_low; /* indicates if a low_voltage was detected */
+};
+
+/*
+ * In the routines that deal directly with the pcf2127 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf2127 *pcf2127 = i2c_get_clientdata(client);
+ unsigned char buf[10] = { PCF2127_REG_CTRL1 };
+
+ /* read registers */
+ if (i2c_master_send(client, buf, 1) != 1 ||
+ i2c_master_recv(client, buf, sizeof(buf)) != sizeof(buf)) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ if (buf[PCF2127_REG_CTRL3] & 0x04) {
+ pcf2127->voltage_low = 1;
+ dev_info(&client->dev,
+ "low voltage detected, date/time is not reliable.\n");
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is cr1=%02x, cr2=%02x, cr3=%02x, "
+ "sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
+ __func__,
+ buf[0], buf[1], buf[2],
+ buf[3], buf[4], buf[5],
+ buf[6], buf[7], buf[8], buf[9]);
+
+
+ tm->tm_sec = bcd2bin(buf[PCF2127_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[PCF2127_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[PCF2127_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = bcd2bin(buf[PCF2127_REG_DM] & 0x3F);
+ tm->tm_wday = buf[PCF2127_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[PCF2127_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(buf[PCF2127_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* the clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup.
+ */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return 0;
+}
+
+static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ unsigned char buf[8];
+ int i = 0, err;
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* start register address */
+ buf[i++] = PCF2127_REG_SC;
+
+ /* hours, minutes and seconds */
+ buf[i++] = bin2bcd(tm->tm_sec);
+ buf[i++] = bin2bcd(tm->tm_min);
+ buf[i++] = bin2bcd(tm->tm_hour);
+ buf[i++] = bin2bcd(tm->tm_mday);
+ buf[i++] = tm->tm_wday & 0x07;
+
+ /* month, 1 - 12 */
+ buf[i++] = bin2bcd(tm->tm_mon + 1);
+
+ /* year */
+ buf[i++] = bin2bcd(tm->tm_year % 100);
+
+ /* write register's data */
+ err = i2c_master_send(client, buf, i);
+ if (err != i) {
+ dev_err(&client->dev,
+ "%s: err=%d", __func__, err);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+static int pcf2127_rtc_ioctl(struct device *dev,
+ unsigned int cmd, unsigned long arg)
+{
+ struct pcf2127 *pcf2127 = i2c_get_clientdata(to_i2c_client(dev));
+
+ switch (cmd) {
+ case RTC_VL_READ:
+ if (pcf2127->voltage_low)
+ dev_info(dev, "low voltage detected, date/time is not reliable.\n");
+
+ if (copy_to_user((void __user *)arg, &pcf2127->voltage_low,
+ sizeof(int)))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+#else
+#define pcf2127_rtc_ioctl NULL
+#endif
+
+static int pcf2127_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf2127_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf2127_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops pcf2127_rtc_ops = {
+ .ioctl = pcf2127_rtc_ioctl,
+ .read_time = pcf2127_rtc_read_time,
+ .set_time = pcf2127_rtc_set_time,
+};
+
+static int pcf2127_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf2127 *pcf2127;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf2127 = devm_kzalloc(&client->dev, sizeof(struct pcf2127),
+ GFP_KERNEL);
+ if (!pcf2127)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, pcf2127);
+
+ pcf2127->rtc = devm_rtc_device_register(&client->dev,
+ pcf2127_driver.driver.name,
+ &pcf2127_rtc_ops, THIS_MODULE);
+
+ return PTR_ERR_OR_ZERO(pcf2127->rtc);
+}
+
+static const struct i2c_device_id pcf2127_id[] = {
+ { "pcf2127", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf2127_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf2127_of_match[] = {
+ { .compatible = "nxp,pcf2127" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcf2127_of_match);
+#endif
+
+static struct i2c_driver pcf2127_driver = {
+ .driver = {
+ .name = "rtc-pcf2127",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf2127_of_match),
+ },
+ .probe = pcf2127_probe,
+ .id_table = pcf2127_id,
+};
+
+module_i2c_driver(pcf2127_driver);
+
+MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>");
+MODULE_DESCRIPTION("NXP PCF2127 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c
new file mode 100644
index 000000000..e6b6911c8
--- /dev/null
+++ b/drivers/rtc/rtc-pcf50633.c
@@ -0,0 +1,295 @@
+/* NXP PCF50633 RTC Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/err.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+#define PCF50633_REG_RTCSC 0x59 /* Second */
+#define PCF50633_REG_RTCMN 0x5a /* Minute */
+#define PCF50633_REG_RTCHR 0x5b /* Hour */
+#define PCF50633_REG_RTCWD 0x5c /* Weekday */
+#define PCF50633_REG_RTCDT 0x5d /* Day */
+#define PCF50633_REG_RTCMT 0x5e /* Month */
+#define PCF50633_REG_RTCYR 0x5f /* Year */
+#define PCF50633_REG_RTCSCA 0x60 /* Alarm Second */
+#define PCF50633_REG_RTCMNA 0x61 /* Alarm Minute */
+#define PCF50633_REG_RTCHRA 0x62 /* Alarm Hour */
+#define PCF50633_REG_RTCWDA 0x63 /* Alarm Weekday */
+#define PCF50633_REG_RTCDTA 0x64 /* Alarm Day */
+#define PCF50633_REG_RTCMTA 0x65 /* Alarm Month */
+#define PCF50633_REG_RTCYRA 0x66 /* Alarm Year */
+
+enum pcf50633_time_indexes {
+ PCF50633_TI_SEC,
+ PCF50633_TI_MIN,
+ PCF50633_TI_HOUR,
+ PCF50633_TI_WKDAY,
+ PCF50633_TI_DAY,
+ PCF50633_TI_MONTH,
+ PCF50633_TI_YEAR,
+ PCF50633_TI_EXTENT /* always last */
+};
+
+struct pcf50633_time {
+ u_int8_t time[PCF50633_TI_EXTENT];
+};
+
+struct pcf50633_rtc {
+ int alarm_enabled;
+ int alarm_pending;
+
+ struct pcf50633 *pcf;
+ struct rtc_device *rtc_dev;
+};
+
+static void pcf2rtc_time(struct rtc_time *rtc, struct pcf50633_time *pcf)
+{
+ rtc->tm_sec = bcd2bin(pcf->time[PCF50633_TI_SEC]);
+ rtc->tm_min = bcd2bin(pcf->time[PCF50633_TI_MIN]);
+ rtc->tm_hour = bcd2bin(pcf->time[PCF50633_TI_HOUR]);
+ rtc->tm_wday = bcd2bin(pcf->time[PCF50633_TI_WKDAY]);
+ rtc->tm_mday = bcd2bin(pcf->time[PCF50633_TI_DAY]);
+ rtc->tm_mon = bcd2bin(pcf->time[PCF50633_TI_MONTH]) - 1;
+ rtc->tm_year = bcd2bin(pcf->time[PCF50633_TI_YEAR]) + 100;
+}
+
+static void rtc2pcf_time(struct pcf50633_time *pcf, struct rtc_time *rtc)
+{
+ pcf->time[PCF50633_TI_SEC] = bin2bcd(rtc->tm_sec);
+ pcf->time[PCF50633_TI_MIN] = bin2bcd(rtc->tm_min);
+ pcf->time[PCF50633_TI_HOUR] = bin2bcd(rtc->tm_hour);
+ pcf->time[PCF50633_TI_WKDAY] = bin2bcd(rtc->tm_wday);
+ pcf->time[PCF50633_TI_DAY] = bin2bcd(rtc->tm_mday);
+ pcf->time[PCF50633_TI_MONTH] = bin2bcd(rtc->tm_mon + 1);
+ pcf->time[PCF50633_TI_YEAR] = bin2bcd(rtc->tm_year % 100);
+}
+
+static int
+pcf50633_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pcf50633_rtc *rtc = dev_get_drvdata(dev);
+ int err;
+
+ if (enabled)
+ err = pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
+ else
+ err = pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ if (err < 0)
+ return err;
+
+ rtc->alarm_enabled = enabled;
+
+ return 0;
+}
+
+static int pcf50633_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pcf50633_rtc *rtc;
+ struct pcf50633_time pcf_tm;
+ int ret;
+
+ rtc = dev_get_drvdata(dev);
+
+ ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSC,
+ PCF50633_TI_EXTENT,
+ &pcf_tm.time[0]);
+ if (ret != PCF50633_TI_EXTENT) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
+ pcf_tm.time[PCF50633_TI_DAY],
+ pcf_tm.time[PCF50633_TI_MONTH],
+ pcf_tm.time[PCF50633_TI_YEAR],
+ pcf_tm.time[PCF50633_TI_HOUR],
+ pcf_tm.time[PCF50633_TI_MIN],
+ pcf_tm.time[PCF50633_TI_SEC]);
+
+ pcf2rtc_time(tm, &pcf_tm);
+
+ dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
+ tm->tm_mday, tm->tm_mon, tm->tm_year,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return rtc_valid_tm(tm);
+}
+
+static int pcf50633_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pcf50633_rtc *rtc;
+ struct pcf50633_time pcf_tm;
+ int alarm_masked, ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "RTC_TIME: %u.%u.%u %u:%u:%u\n",
+ tm->tm_mday, tm->tm_mon, tm->tm_year,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ rtc2pcf_time(&pcf_tm, tm);
+
+ dev_dbg(dev, "PCF_TIME: %02x.%02x.%02x %02x:%02x:%02x\n",
+ pcf_tm.time[PCF50633_TI_DAY],
+ pcf_tm.time[PCF50633_TI_MONTH],
+ pcf_tm.time[PCF50633_TI_YEAR],
+ pcf_tm.time[PCF50633_TI_HOUR],
+ pcf_tm.time[PCF50633_TI_MIN],
+ pcf_tm.time[PCF50633_TI_SEC]);
+
+
+ alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ if (!alarm_masked)
+ pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ /* Returns 0 on success */
+ ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSC,
+ PCF50633_TI_EXTENT,
+ &pcf_tm.time[0]);
+
+ if (!alarm_masked)
+ pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ return ret;
+}
+
+static int pcf50633_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pcf50633_rtc *rtc;
+ struct pcf50633_time pcf_tm;
+ int ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ alrm->enabled = rtc->alarm_enabled;
+ alrm->pending = rtc->alarm_pending;
+
+ ret = pcf50633_read_block(rtc->pcf, PCF50633_REG_RTCSCA,
+ PCF50633_TI_EXTENT, &pcf_tm.time[0]);
+ if (ret != PCF50633_TI_EXTENT) {
+ dev_err(dev, "Failed to read time\n");
+ return -EIO;
+ }
+
+ pcf2rtc_time(&alrm->time, &pcf_tm);
+
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int pcf50633_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pcf50633_rtc *rtc;
+ struct pcf50633_time pcf_tm;
+ int alarm_masked, ret = 0;
+
+ rtc = dev_get_drvdata(dev);
+
+ rtc2pcf_time(&pcf_tm, &alrm->time);
+
+ /* do like mktime does and ignore tm_wday */
+ pcf_tm.time[PCF50633_TI_WKDAY] = 7;
+
+ alarm_masked = pcf50633_irq_mask_get(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ /* disable alarm interrupt */
+ if (!alarm_masked)
+ pcf50633_irq_mask(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ /* Returns 0 on success */
+ ret = pcf50633_write_block(rtc->pcf, PCF50633_REG_RTCSCA,
+ PCF50633_TI_EXTENT, &pcf_tm.time[0]);
+ if (!alrm->enabled)
+ rtc->alarm_pending = 0;
+
+ if (!alarm_masked || alrm->enabled)
+ pcf50633_irq_unmask(rtc->pcf, PCF50633_IRQ_ALARM);
+ rtc->alarm_enabled = alrm->enabled;
+
+ return ret;
+}
+
+static struct rtc_class_ops pcf50633_rtc_ops = {
+ .read_time = pcf50633_rtc_read_time,
+ .set_time = pcf50633_rtc_set_time,
+ .read_alarm = pcf50633_rtc_read_alarm,
+ .set_alarm = pcf50633_rtc_set_alarm,
+ .alarm_irq_enable = pcf50633_rtc_alarm_irq_enable,
+};
+
+static void pcf50633_rtc_irq(int irq, void *data)
+{
+ struct pcf50633_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+ rtc->alarm_pending = 1;
+}
+
+static int pcf50633_rtc_probe(struct platform_device *pdev)
+{
+ struct pcf50633_rtc *rtc;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->pcf = dev_to_pcf50633(pdev->dev.parent);
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "pcf50633-rtc",
+ &pcf50633_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc->rtc_dev))
+ return PTR_ERR(rtc->rtc_dev);
+
+ pcf50633_register_irq(rtc->pcf, PCF50633_IRQ_ALARM,
+ pcf50633_rtc_irq, rtc);
+ return 0;
+}
+
+static int pcf50633_rtc_remove(struct platform_device *pdev)
+{
+ struct pcf50633_rtc *rtc;
+
+ rtc = platform_get_drvdata(pdev);
+ pcf50633_free_irq(rtc->pcf, PCF50633_IRQ_ALARM);
+
+ return 0;
+}
+
+static struct platform_driver pcf50633_rtc_driver = {
+ .driver = {
+ .name = "pcf50633-rtc",
+ },
+ .probe = pcf50633_rtc_probe,
+ .remove = pcf50633_rtc_remove,
+};
+
+module_platform_driver(pcf50633_rtc_driver);
+
+MODULE_DESCRIPTION("PCF50633 RTC driver");
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c
new file mode 100644
index 000000000..6a12bf62c
--- /dev/null
+++ b/drivers/rtc/rtc-pcf85063.c
@@ -0,0 +1,204 @@
+/*
+ * An I2C driver for the PCF85063 RTC
+ * Copyright 2014 Rose Technology
+ *
+ * Author: Søren Andersen <san@rosetechnology.dk>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * based on the other drivers in this same directory.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.0.1"
+
+#define PCF85063_REG_CTRL1 0x00 /* status */
+#define PCF85063_REG_CTRL2 0x01
+
+#define PCF85063_REG_SC 0x04 /* datetime */
+#define PCF85063_REG_MN 0x05
+#define PCF85063_REG_HR 0x06
+#define PCF85063_REG_DM 0x07
+#define PCF85063_REG_DW 0x08
+#define PCF85063_REG_MO 0x09
+#define PCF85063_REG_YR 0x0A
+
+#define PCF85063_MO_C 0x80 /* century */
+
+static struct i2c_driver pcf85063_driver;
+
+struct pcf85063 {
+ struct rtc_device *rtc;
+ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
+ int voltage_low; /* indicates if a low_voltage was detected */
+};
+
+/*
+ * In the routines that deal directly with the pcf85063 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf85063 *pcf85063 = i2c_get_clientdata(client);
+ unsigned char buf[13] = { PCF85063_REG_CTRL1 };
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = buf
+ },
+ {/* read status + date */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 13,
+ .buf = buf
+ },
+ };
+
+ /* read registers */
+ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ tm->tm_sec = bcd2bin(buf[PCF85063_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[PCF85063_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[PCF85063_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = bcd2bin(buf[PCF85063_REG_DM] & 0x3F);
+ tm->tm_wday = buf[PCF85063_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[PCF85063_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(buf[PCF85063_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+ /* detect the polarity heuristically. see note above. */
+ pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
+ (tm->tm_year >= 100) : (tm->tm_year < 100);
+
+ /* the clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup.
+ */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return 0;
+}
+
+static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ int i = 0, err = 0;
+ unsigned char buf[11];
+
+ /* Control & status */
+ buf[PCF85063_REG_CTRL1] = 0;
+ buf[PCF85063_REG_CTRL2] = 5;
+
+ /* hours, minutes and seconds */
+ buf[PCF85063_REG_SC] = bin2bcd(tm->tm_sec) & 0x7F;
+
+ buf[PCF85063_REG_MN] = bin2bcd(tm->tm_min);
+ buf[PCF85063_REG_HR] = bin2bcd(tm->tm_hour);
+
+ /* Day of month, 1 - 31 */
+ buf[PCF85063_REG_DM] = bin2bcd(tm->tm_mday);
+
+ /* Day, 0 - 6 */
+ buf[PCF85063_REG_DW] = tm->tm_wday & 0x07;
+
+ /* month, 1 - 12 */
+ buf[PCF85063_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[PCF85063_REG_YR] = bin2bcd(tm->tm_year % 100);
+
+ /* write register's data */
+ for (i = 0; i < sizeof(buf); i++) {
+ unsigned char data[2] = { i, buf[i] };
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev, "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf85063_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf85063_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops pcf85063_rtc_ops = {
+ .read_time = pcf85063_rtc_read_time,
+ .set_time = pcf85063_rtc_set_time
+};
+
+static int pcf85063_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf85063 *pcf85063;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf85063 = devm_kzalloc(&client->dev, sizeof(struct pcf85063),
+ GFP_KERNEL);
+ if (!pcf85063)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, pcf85063);
+
+ pcf85063->rtc = devm_rtc_device_register(&client->dev,
+ pcf85063_driver.driver.name,
+ &pcf85063_rtc_ops, THIS_MODULE);
+
+ return PTR_ERR_OR_ZERO(pcf85063->rtc);
+}
+
+static const struct i2c_device_id pcf85063_id[] = {
+ { "pcf85063", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf85063_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf85063_of_match[] = {
+ { .compatible = "nxp,pcf85063" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcf85063_of_match);
+#endif
+
+static struct i2c_driver pcf85063_driver = {
+ .driver = {
+ .name = "rtc-pcf85063",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf85063_of_match),
+ },
+ .probe = pcf85063_probe,
+ .id_table = pcf85063_id,
+};
+
+module_i2c_driver(pcf85063_driver);
+
+MODULE_AUTHOR("Søren Andersen <san@rosetechnology.dk>");
+MODULE_DESCRIPTION("PCF85063 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c
new file mode 100644
index 000000000..4cdb64be0
--- /dev/null
+++ b/drivers/rtc/rtc-pcf8523.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2012 Avionic Design GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/of.h>
+
+#define DRIVER_NAME "rtc-pcf8523"
+
+#define REG_CONTROL1 0x00
+#define REG_CONTROL1_CAP_SEL (1 << 7)
+#define REG_CONTROL1_STOP (1 << 5)
+
+#define REG_CONTROL3 0x02
+#define REG_CONTROL3_PM_BLD (1 << 7) /* battery low detection disabled */
+#define REG_CONTROL3_PM_VDD (1 << 6) /* switch-over disabled */
+#define REG_CONTROL3_PM_DSM (1 << 5) /* direct switching mode */
+#define REG_CONTROL3_PM_MASK 0xe0
+#define REG_CONTROL3_BLF (1 << 2) /* battery low bit, read-only */
+
+#define REG_SECONDS 0x03
+#define REG_SECONDS_OS (1 << 7)
+
+#define REG_MINUTES 0x04
+#define REG_HOURS 0x05
+#define REG_DAYS 0x06
+#define REG_WEEKDAYS 0x07
+#define REG_MONTHS 0x08
+#define REG_YEARS 0x09
+
+struct pcf8523 {
+ struct rtc_device *rtc;
+};
+
+static int pcf8523_read(struct i2c_client *client, u8 reg, u8 *valuep)
+{
+ struct i2c_msg msgs[2];
+ u8 value = 0;
+ int err;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = sizeof(reg);
+ msgs[0].buf = &reg;
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(value);
+ msgs[1].buf = &value;
+
+ err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (err < 0)
+ return err;
+
+ *valuep = value;
+
+ return 0;
+}
+
+static int pcf8523_write(struct i2c_client *client, u8 reg, u8 value)
+{
+ u8 buffer[2] = { reg, value };
+ struct i2c_msg msg;
+ int err;
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sizeof(buffer);
+ msg.buf = buffer;
+
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
+{
+ u8 value;
+ int err;
+
+ err = pcf8523_read(client, REG_CONTROL1, &value);
+ if (err < 0)
+ return err;
+
+ if (!high)
+ value &= ~REG_CONTROL1_CAP_SEL;
+ else
+ value |= REG_CONTROL1_CAP_SEL;
+
+ err = pcf8523_write(client, REG_CONTROL1, value);
+ if (err < 0)
+ return err;
+
+ return err;
+}
+
+static int pcf8523_set_pm(struct i2c_client *client, u8 pm)
+{
+ u8 value;
+ int err;
+
+ err = pcf8523_read(client, REG_CONTROL3, &value);
+ if (err < 0)
+ return err;
+
+ value = (value & ~REG_CONTROL3_PM_MASK) | pm;
+
+ err = pcf8523_write(client, REG_CONTROL3, value);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int pcf8523_stop_rtc(struct i2c_client *client)
+{
+ u8 value;
+ int err;
+
+ err = pcf8523_read(client, REG_CONTROL1, &value);
+ if (err < 0)
+ return err;
+
+ value |= REG_CONTROL1_STOP;
+
+ err = pcf8523_write(client, REG_CONTROL1, value);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int pcf8523_start_rtc(struct i2c_client *client)
+{
+ u8 value;
+ int err;
+
+ err = pcf8523_read(client, REG_CONTROL1, &value);
+ if (err < 0)
+ return err;
+
+ value &= ~REG_CONTROL1_STOP;
+
+ err = pcf8523_write(client, REG_CONTROL1, value);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int pcf8523_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 start = REG_SECONDS, regs[7];
+ struct i2c_msg msgs[2];
+ int err;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = 1;
+ msgs[0].buf = &start;
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].len = sizeof(regs);
+ msgs[1].buf = regs;
+
+ err = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (err < 0)
+ return err;
+
+ if (regs[0] & REG_SECONDS_OS) {
+ /*
+ * If the oscillator was stopped, try to clear the flag. Upon
+ * power-up the flag is always set, but if we cannot clear it
+ * the oscillator isn't running properly for some reason. The
+ * sensible thing therefore is to return an error, signalling
+ * that the clock cannot be assumed to be correct.
+ */
+
+ regs[0] &= ~REG_SECONDS_OS;
+
+ err = pcf8523_write(client, REG_SECONDS, regs[0]);
+ if (err < 0)
+ return err;
+
+ err = pcf8523_read(client, REG_SECONDS, &regs[0]);
+ if (err < 0)
+ return err;
+
+ if (regs[0] & REG_SECONDS_OS)
+ return -EAGAIN;
+ }
+
+ tm->tm_sec = bcd2bin(regs[0] & 0x7f);
+ tm->tm_min = bcd2bin(regs[1] & 0x7f);
+ tm->tm_hour = bcd2bin(regs[2] & 0x3f);
+ tm->tm_mday = bcd2bin(regs[3] & 0x3f);
+ tm->tm_wday = regs[4] & 0x7;
+ tm->tm_mon = bcd2bin(regs[5] & 0x1f) - 1;
+ tm->tm_year = bcd2bin(regs[6]) + 100;
+
+ return rtc_valid_tm(tm);
+}
+
+static int pcf8523_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msg;
+ u8 regs[8];
+ int err;
+
+ err = pcf8523_stop_rtc(client);
+ if (err < 0)
+ return err;
+
+ regs[0] = REG_SECONDS;
+ regs[1] = bin2bcd(tm->tm_sec);
+ regs[2] = bin2bcd(tm->tm_min);
+ regs[3] = bin2bcd(tm->tm_hour);
+ regs[4] = bin2bcd(tm->tm_mday);
+ regs[5] = tm->tm_wday;
+ regs[6] = bin2bcd(tm->tm_mon + 1);
+ regs[7] = bin2bcd(tm->tm_year - 100);
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = sizeof(regs);
+ msg.buf = regs;
+
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err < 0) {
+ /*
+ * If the time cannot be set, restart the RTC anyway. Note
+ * that errors are ignored if the RTC cannot be started so
+ * that we have a chance to propagate the original error.
+ */
+ pcf8523_start_rtc(client);
+ return err;
+ }
+
+ return pcf8523_start_rtc(client);
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 value;
+ int ret = 0, err;
+
+ switch (cmd) {
+ case RTC_VL_READ:
+ err = pcf8523_read(client, REG_CONTROL3, &value);
+ if (err < 0)
+ return err;
+
+ if (value & REG_CONTROL3_BLF)
+ ret = 1;
+
+ if (copy_to_user((void __user *)arg, &ret, sizeof(int)))
+ return -EFAULT;
+
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+#else
+#define pcf8523_rtc_ioctl NULL
+#endif
+
+static const struct rtc_class_ops pcf8523_rtc_ops = {
+ .read_time = pcf8523_rtc_read_time,
+ .set_time = pcf8523_rtc_set_time,
+ .ioctl = pcf8523_rtc_ioctl,
+};
+
+static int pcf8523_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf8523 *pcf;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf = devm_kzalloc(&client->dev, sizeof(*pcf), GFP_KERNEL);
+ if (!pcf)
+ return -ENOMEM;
+
+ err = pcf8523_select_capacitance(client, true);
+ if (err < 0)
+ return err;
+
+ err = pcf8523_set_pm(client, 0);
+ if (err < 0)
+ return err;
+
+ pcf->rtc = devm_rtc_device_register(&client->dev, DRIVER_NAME,
+ &pcf8523_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pcf->rtc))
+ return PTR_ERR(pcf->rtc);
+
+ i2c_set_clientdata(client, pcf);
+
+ return 0;
+}
+
+static const struct i2c_device_id pcf8523_id[] = {
+ { "pcf8523", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8523_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf8523_of_match[] = {
+ { .compatible = "nxp,pcf8523" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcf8523_of_match);
+#endif
+
+static struct i2c_driver pcf8523_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf8523_of_match),
+ },
+ .probe = pcf8523_probe,
+ .id_table = pcf8523_id,
+};
+module_i2c_driver(pcf8523_driver);
+
+MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
+MODULE_DESCRIPTION("NXP PCF8523 RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
new file mode 100644
index 000000000..0ba7e5992
--- /dev/null
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -0,0 +1,503 @@
+/*
+ * An I2C driver for the Philips PCF8563 RTC
+ * Copyright 2005-06 Tower Technologies
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * based on the other drivers in this same directory.
+ *
+ * http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/err.h>
+
+#define DRV_VERSION "0.4.3"
+
+#define PCF8563_REG_ST1 0x00 /* status */
+#define PCF8563_REG_ST2 0x01
+#define PCF8563_BIT_AIE (1 << 1)
+#define PCF8563_BIT_AF (1 << 3)
+#define PCF8563_BITS_ST2_N (7 << 5)
+
+#define PCF8563_REG_SC 0x02 /* datetime */
+#define PCF8563_REG_MN 0x03
+#define PCF8563_REG_HR 0x04
+#define PCF8563_REG_DM 0x05
+#define PCF8563_REG_DW 0x06
+#define PCF8563_REG_MO 0x07
+#define PCF8563_REG_YR 0x08
+
+#define PCF8563_REG_AMN 0x09 /* alarm */
+
+#define PCF8563_REG_CLKO 0x0D /* clock out */
+#define PCF8563_REG_TMRC 0x0E /* timer control */
+#define PCF8563_TMRC_ENABLE BIT(7)
+#define PCF8563_TMRC_4096 0
+#define PCF8563_TMRC_64 1
+#define PCF8563_TMRC_1 2
+#define PCF8563_TMRC_1_60 3
+#define PCF8563_TMRC_MASK 3
+
+#define PCF8563_REG_TMR 0x0F /* timer */
+
+#define PCF8563_SC_LV 0x80 /* low voltage */
+#define PCF8563_MO_C 0x80 /* century */
+
+static struct i2c_driver pcf8563_driver;
+
+struct pcf8563 {
+ struct rtc_device *rtc;
+ /*
+ * The meaning of MO_C bit varies by the chip type.
+ * From PCF8563 datasheet: this bit is toggled when the years
+ * register overflows from 99 to 00
+ * 0 indicates the century is 20xx
+ * 1 indicates the century is 19xx
+ * From RTC8564 datasheet: this bit indicates change of
+ * century. When the year digit data overflows from 99 to 00,
+ * this bit is set. By presetting it to 0 while still in the
+ * 20th century, it will be set in year 2000, ...
+ * There seems no reliable way to know how the system use this
+ * bit. So let's do it heuristically, assuming we are live in
+ * 1970...2069.
+ */
+ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
+ int voltage_low; /* incicates if a low_voltage was detected */
+
+ struct i2c_client *client;
+};
+
+static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
+ unsigned char length, unsigned char *buf)
+{
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 1,
+ .buf = &reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = buf
+ },
+ };
+
+ if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pcf8563_write_block_data(struct i2c_client *client,
+ unsigned char reg, unsigned char length,
+ unsigned char *buf)
+{
+ int i, err;
+
+ for (i = 0; i < length; i++) {
+ unsigned char data[2] = { reg + i, buf[i] };
+
+ err = i2c_master_send(client, data, sizeof(data));
+ if (err != sizeof(data)) {
+ dev_err(&client->dev,
+ "%s: err=%d addr=%02x, data=%02x\n",
+ __func__, err, data[0], data[1]);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on)
+{
+ unsigned char buf;
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
+ if (err < 0)
+ return err;
+
+ if (on)
+ buf |= PCF8563_BIT_AIE;
+ else
+ buf &= ~PCF8563_BIT_AIE;
+
+ buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N);
+
+ err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en,
+ unsigned char *pen)
+{
+ unsigned char buf;
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
+ if (err)
+ return err;
+
+ if (en)
+ *en = !!(buf & PCF8563_BIT_AIE);
+ if (pen)
+ *pen = !!(buf & PCF8563_BIT_AF);
+
+ return 0;
+}
+
+static irqreturn_t pcf8563_irq(int irq, void *dev_id)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id);
+ int err;
+ char pending;
+
+ err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending);
+ if (err)
+ return IRQ_NONE;
+
+ if (pending) {
+ rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF);
+ pcf8563_set_alarm_mode(pcf8563->client, 1);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/*
+ * In the routines that deal directly with the pcf8563 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
+ unsigned char buf[9];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf);
+ if (err)
+ return err;
+
+ if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
+ pcf8563->voltage_low = 1;
+ dev_info(&client->dev,
+ "low voltage detected, date/time is not reliable.\n");
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
+ __func__,
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8]);
+
+
+ tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_mday = bcd2bin(buf[PCF8563_REG_DM] & 0x3F);
+ tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
+ tm->tm_mon = bcd2bin(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(buf[PCF8563_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+ /* detect the polarity heuristically. see note above. */
+ pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
+ (tm->tm_year >= 100) : (tm->tm_year < 100);
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* the clock can give out invalid datetime, but we cannot return
+ * -EINVAL otherwise hwclock will refuse to set the time on bootup.
+ */
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return 0;
+}
+
+static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
+ unsigned char buf[9];
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* hours, minutes and seconds */
+ buf[PCF8563_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[PCF8563_REG_MN] = bin2bcd(tm->tm_min);
+ buf[PCF8563_REG_HR] = bin2bcd(tm->tm_hour);
+
+ buf[PCF8563_REG_DM] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[PCF8563_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[PCF8563_REG_YR] = bin2bcd(tm->tm_year % 100);
+ if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
+ buf[PCF8563_REG_MO] |= PCF8563_MO_C;
+
+ buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
+
+ return pcf8563_write_block_data(client, PCF8563_REG_SC,
+ 9 - PCF8563_REG_SC, buf + PCF8563_REG_SC);
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ struct pcf8563 *pcf8563 = i2c_get_clientdata(to_i2c_client(dev));
+ struct rtc_time tm;
+
+ switch (cmd) {
+ case RTC_VL_READ:
+ if (pcf8563->voltage_low)
+ dev_info(dev, "low voltage detected, date/time is not reliable.\n");
+
+ if (copy_to_user((void __user *)arg, &pcf8563->voltage_low,
+ sizeof(int)))
+ return -EFAULT;
+ return 0;
+ case RTC_VL_CLR:
+ /*
+ * Clear the VL bit in the seconds register in case
+ * the time has not been set already (which would
+ * have cleared it). This does not really matter
+ * because of the cached voltage_low value but do it
+ * anyway for consistency.
+ */
+ if (pcf8563_get_datetime(to_i2c_client(dev), &tm))
+ pcf8563_set_datetime(to_i2c_client(dev), &tm);
+
+ /* Clear the cached value. */
+ pcf8563->voltage_low = 0;
+
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+#else
+#define pcf8563_rtc_ioctl NULL
+#endif
+
+static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf8563_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return pcf8563_set_datetime(to_i2c_client(dev), tm);
+}
+
+static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[4];
+ int err;
+
+ err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf);
+ if (err)
+ return err;
+
+ dev_dbg(&client->dev,
+ "%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n",
+ __func__, buf[0], buf[1], buf[2], buf[3]);
+
+ tm->time.tm_min = bcd2bin(buf[0] & 0x7F);
+ tm->time.tm_hour = bcd2bin(buf[1] & 0x3F);
+ tm->time.tm_mday = bcd2bin(buf[2] & 0x3F);
+ tm->time.tm_wday = bcd2bin(buf[3] & 0x7);
+ tm->time.tm_mon = -1;
+ tm->time.tm_year = -1;
+ tm->time.tm_yday = -1;
+ tm->time.tm_isdst = -1;
+
+ err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending);
+ if (err < 0)
+ return err;
+
+ dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d,"
+ " enabled=%d, pending=%d\n", __func__, tm->time.tm_min,
+ tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday,
+ tm->enabled, tm->pending);
+
+ return 0;
+}
+
+static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char buf[4];
+ int err;
+ unsigned long alarm_time;
+
+ /* The alarm has no seconds, round up to nearest minute */
+ if (tm->time.tm_sec) {
+ rtc_tm_to_time(&tm->time, &alarm_time);
+ alarm_time += 60-tm->time.tm_sec;
+ rtc_time_to_tm(alarm_time, &tm->time);
+ }
+
+ dev_dbg(dev, "%s, min=%d hour=%d wday=%d mday=%d "
+ "enabled=%d pending=%d\n", __func__,
+ tm->time.tm_min, tm->time.tm_hour, tm->time.tm_wday,
+ tm->time.tm_mday, tm->enabled, tm->pending);
+
+ buf[0] = bin2bcd(tm->time.tm_min);
+ buf[1] = bin2bcd(tm->time.tm_hour);
+ buf[2] = bin2bcd(tm->time.tm_mday);
+ buf[3] = tm->time.tm_wday & 0x07;
+
+ err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf);
+ if (err)
+ return err;
+
+ return pcf8563_set_alarm_mode(client, 1);
+}
+
+static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
+{
+ dev_dbg(dev, "%s: en=%d\n", __func__, enabled);
+ return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
+}
+
+static const struct rtc_class_ops pcf8563_rtc_ops = {
+ .ioctl = pcf8563_rtc_ioctl,
+ .read_time = pcf8563_rtc_read_time,
+ .set_time = pcf8563_rtc_set_time,
+ .read_alarm = pcf8563_rtc_read_alarm,
+ .set_alarm = pcf8563_rtc_set_alarm,
+ .alarm_irq_enable = pcf8563_irq_enable,
+};
+
+static int pcf8563_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf8563 *pcf8563;
+ int err;
+ unsigned char buf;
+ unsigned char alm_pending;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563),
+ GFP_KERNEL);
+ if (!pcf8563)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ i2c_set_clientdata(client, pcf8563);
+ pcf8563->client = client;
+ device_set_wakeup_capable(&client->dev, 1);
+
+ /* Set timer to lowest frequency to save power (ref Haoyu datasheet) */
+ buf = PCF8563_TMRC_1_60;
+ err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return err;
+ }
+
+ err = pcf8563_get_alarm_mode(client, NULL, &alm_pending);
+ if (err < 0) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return err;
+ }
+ if (alm_pending)
+ pcf8563_set_alarm_mode(client, 0);
+
+ pcf8563->rtc = devm_rtc_device_register(&client->dev,
+ pcf8563_driver.driver.name,
+ &pcf8563_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(pcf8563->rtc))
+ return PTR_ERR(pcf8563->rtc);
+
+ if (client->irq > 0) {
+ err = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, pcf8563_irq,
+ IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
+ pcf8563->rtc->name, client);
+ if (err) {
+ dev_err(&client->dev, "unable to request IRQ %d\n",
+ client->irq);
+ return err;
+ }
+
+ }
+
+ /* the pcf8563 alarm only supports a minute accuracy */
+ pcf8563->rtc->uie_unsupported = 1;
+
+ return 0;
+}
+
+static const struct i2c_device_id pcf8563_id[] = {
+ { "pcf8563", 0 },
+ { "rtc8564", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8563_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf8563_of_match[] = {
+ { .compatible = "nxp,pcf8563" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pcf8563_of_match);
+#endif
+
+static struct i2c_driver pcf8563_driver = {
+ .driver = {
+ .name = "rtc-pcf8563",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pcf8563_of_match),
+ },
+ .probe = pcf8563_probe,
+ .id_table = pcf8563_id,
+};
+
+module_i2c_driver(pcf8563_driver);
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
new file mode 100644
index 000000000..5911a6dca
--- /dev/null
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -0,0 +1,322 @@
+/*
+ * drivers/rtc/rtc-pcf8583.c
+ *
+ * Copyright (C) 2000 Russell King
+ * Copyright (C) 2008 Wolfram Sang & Juergen Beisert, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for PCF8583 RTC & RAM chip
+ *
+ * Converted to the generic RTC susbsystem by G. Liakhovetski (2006)
+ */
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/bcd.h>
+
+struct rtc_mem {
+ unsigned int loc;
+ unsigned int nr;
+ unsigned char *data;
+};
+
+struct pcf8583 {
+ struct rtc_device *rtc;
+ unsigned char ctrl;
+};
+
+#define CTRL_STOP 0x80
+#define CTRL_HOLD 0x40
+#define CTRL_32KHZ 0x00
+#define CTRL_MASK 0x08
+#define CTRL_ALARMEN 0x04
+#define CTRL_ALARM 0x02
+#define CTRL_TIMER 0x01
+
+
+static struct i2c_driver pcf8583_driver;
+
+#define get_ctrl(x) ((struct pcf8583 *)i2c_get_clientdata(x))->ctrl
+#define set_ctrl(x, v) get_ctrl(x) = v
+
+#define CMOS_YEAR (64 + 128)
+#define CMOS_CHECKSUM (63)
+
+static int pcf8583_get_datetime(struct i2c_client *client, struct rtc_time *dt)
+{
+ unsigned char buf[8], addr[1] = { 1 };
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 6,
+ .buf = buf,
+ }
+ };
+ int ret;
+
+ memset(buf, 0, sizeof(buf));
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret == 2) {
+ dt->tm_year = buf[4] >> 6;
+ dt->tm_wday = buf[5] >> 5;
+
+ buf[4] &= 0x3f;
+ buf[5] &= 0x1f;
+
+ dt->tm_sec = bcd2bin(buf[1]);
+ dt->tm_min = bcd2bin(buf[2]);
+ dt->tm_hour = bcd2bin(buf[3]);
+ dt->tm_mday = bcd2bin(buf[4]);
+ dt->tm_mon = bcd2bin(buf[5]) - 1;
+ }
+
+ return ret == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo)
+{
+ unsigned char buf[8];
+ int ret, len = 6;
+
+ buf[0] = 0;
+ buf[1] = get_ctrl(client) | 0x80;
+ buf[2] = 0;
+ buf[3] = bin2bcd(dt->tm_sec);
+ buf[4] = bin2bcd(dt->tm_min);
+ buf[5] = bin2bcd(dt->tm_hour);
+
+ if (datetoo) {
+ len = 8;
+ buf[6] = bin2bcd(dt->tm_mday) | (dt->tm_year << 6);
+ buf[7] = bin2bcd(dt->tm_mon + 1) | (dt->tm_wday << 5);
+ }
+
+ ret = i2c_master_send(client, (char *)buf, len);
+ if (ret != len)
+ return -EIO;
+
+ buf[1] = get_ctrl(client);
+ ret = i2c_master_send(client, (char *)buf, 2);
+
+ return ret == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
+{
+ *ctrl = get_ctrl(client);
+ return 0;
+}
+
+static int pcf8583_set_ctrl(struct i2c_client *client, unsigned char *ctrl)
+{
+ unsigned char buf[2];
+
+ buf[0] = 0;
+ buf[1] = *ctrl;
+ set_ctrl(client, *ctrl);
+
+ return i2c_master_send(client, (char *)buf, 2);
+}
+
+static int pcf8583_read_mem(struct i2c_client *client, struct rtc_mem *mem)
+{
+ unsigned char addr[1];
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = addr,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = mem->nr,
+ .buf = mem->data,
+ }
+ };
+
+ if (mem->loc < 8)
+ return -EINVAL;
+
+ addr[0] = mem->loc;
+
+ return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
+}
+
+static int pcf8583_write_mem(struct i2c_client *client, struct rtc_mem *mem)
+{
+ unsigned char buf[9];
+ int ret;
+
+ if (mem->loc < 8 || mem->nr > 8)
+ return -EINVAL;
+
+ buf[0] = mem->loc;
+ memcpy(buf + 1, mem->data, mem->nr);
+
+ ret = i2c_master_send(client, buf, mem->nr + 1);
+ return ret == mem->nr + 1 ? 0 : -EIO;
+}
+
+static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char ctrl, year[2];
+ struct rtc_mem mem = {
+ .loc = CMOS_YEAR,
+ .nr = sizeof(year),
+ .data = year
+ };
+ int real_year, year_offset, err;
+
+ /*
+ * Ensure that the RTC is running.
+ */
+ pcf8583_get_ctrl(client, &ctrl);
+ if (ctrl & (CTRL_STOP | CTRL_HOLD)) {
+ unsigned char new_ctrl = ctrl & ~(CTRL_STOP | CTRL_HOLD);
+
+ dev_warn(dev, "resetting control %02x -> %02x\n",
+ ctrl, new_ctrl);
+
+ err = pcf8583_set_ctrl(client, &new_ctrl);
+ if (err < 0)
+ return err;
+ }
+
+ if (pcf8583_get_datetime(client, tm) ||
+ pcf8583_read_mem(client, &mem))
+ return -EIO;
+
+ real_year = year[0];
+
+ /*
+ * The RTC year holds the LSB two bits of the current
+ * year, which should reflect the LSB two bits of the
+ * CMOS copy of the year. Any difference indicates
+ * that we have to correct the CMOS version.
+ */
+ year_offset = tm->tm_year - (real_year & 3);
+ if (year_offset < 0)
+ /*
+ * RTC year wrapped. Adjust it appropriately.
+ */
+ year_offset += 4;
+
+ tm->tm_year = (real_year + year_offset + year[1] * 100) - 1900;
+
+ return 0;
+}
+
+static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ unsigned char year[2], chk;
+ struct rtc_mem cmos_year = {
+ .loc = CMOS_YEAR,
+ .nr = sizeof(year),
+ .data = year
+ };
+ struct rtc_mem cmos_check = {
+ .loc = CMOS_CHECKSUM,
+ .nr = 1,
+ .data = &chk
+ };
+ unsigned int proper_year = tm->tm_year + 1900;
+ int ret;
+
+ /*
+ * The RTC's own 2-bit year must reflect the least
+ * significant two bits of the CMOS year.
+ */
+
+ ret = pcf8583_set_datetime(client, tm, 1);
+ if (ret)
+ return ret;
+
+ ret = pcf8583_read_mem(client, &cmos_check);
+ if (ret)
+ return ret;
+
+ ret = pcf8583_read_mem(client, &cmos_year);
+ if (ret)
+ return ret;
+
+ chk -= year[1] + year[0];
+
+ year[1] = proper_year / 100;
+ year[0] = proper_year % 100;
+
+ chk += year[1] + year[0];
+
+ ret = pcf8583_write_mem(client, &cmos_year);
+
+ if (ret)
+ return ret;
+
+ ret = pcf8583_write_mem(client, &cmos_check);
+
+ return ret;
+}
+
+static const struct rtc_class_ops pcf8583_rtc_ops = {
+ .read_time = pcf8583_rtc_read_time,
+ .set_time = pcf8583_rtc_set_time,
+};
+
+static int pcf8583_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pcf8583 *pcf8583;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ pcf8583 = devm_kzalloc(&client->dev, sizeof(struct pcf8583),
+ GFP_KERNEL);
+ if (!pcf8583)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pcf8583);
+
+ pcf8583->rtc = devm_rtc_device_register(&client->dev,
+ pcf8583_driver.driver.name,
+ &pcf8583_rtc_ops, THIS_MODULE);
+
+ return PTR_ERR_OR_ZERO(pcf8583->rtc);
+}
+
+static const struct i2c_device_id pcf8583_id[] = {
+ { "pcf8583", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8583_id);
+
+static struct i2c_driver pcf8583_driver = {
+ .driver = {
+ .name = "pcf8583",
+ .owner = THIS_MODULE,
+ },
+ .probe = pcf8583_probe,
+ .id_table = pcf8583_id,
+};
+
+module_i2c_driver(pcf8583_driver);
+
+MODULE_AUTHOR("Russell King");
+MODULE_DESCRIPTION("PCF8583 I2C RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
new file mode 100644
index 000000000..f85a1a93e
--- /dev/null
+++ b/drivers/rtc/rtc-pl030.c
@@ -0,0 +1,187 @@
+/*
+ * linux/drivers/rtc/rtc-pl030.c
+ *
+ * Copyright (C) 2000-2001 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/amba/bus.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#define RTC_DR (0)
+#define RTC_MR (4)
+#define RTC_STAT (8)
+#define RTC_EOI (8)
+#define RTC_LR (12)
+#define RTC_CR (16)
+#define RTC_CR_MIE (1 << 0)
+
+struct pl030_rtc {
+ struct rtc_device *rtc;
+ void __iomem *base;
+};
+
+static irqreturn_t pl030_interrupt(int irq, void *dev_id)
+{
+ struct pl030_rtc *rtc = dev_id;
+ writel(0, rtc->base + RTC_EOI);
+ return IRQ_HANDLED;
+}
+
+static int pl030_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pl030_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(readl(rtc->base + RTC_MR), &alrm->time);
+ return 0;
+}
+
+static int pl030_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pl030_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long time;
+ int ret;
+
+ /*
+ * At the moment, we can only deal with non-wildcarded alarm times.
+ */
+ ret = rtc_valid_tm(&alrm->time);
+ if (ret == 0)
+ ret = rtc_tm_to_time(&alrm->time, &time);
+ if (ret == 0)
+ writel(time, rtc->base + RTC_MR);
+ return ret;
+}
+
+static int pl030_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl030_rtc *rtc = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(readl(rtc->base + RTC_DR), tm);
+
+ return 0;
+}
+
+/*
+ * Set the RTC time. Unfortunately, we can't accurately set
+ * the point at which the counter updates.
+ *
+ * Also, since RTC_LR is transferred to RTC_CR on next rising
+ * edge of the 1Hz clock, we must write the time one second
+ * in advance.
+ */
+static int pl030_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl030_rtc *rtc = dev_get_drvdata(dev);
+ unsigned long time;
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &time);
+ if (ret == 0)
+ writel(time + 1, rtc->base + RTC_LR);
+
+ return ret;
+}
+
+static const struct rtc_class_ops pl030_ops = {
+ .read_time = pl030_read_time,
+ .set_time = pl030_set_time,
+ .read_alarm = pl030_read_alarm,
+ .set_alarm = pl030_set_alarm,
+};
+
+static int pl030_probe(struct amba_device *dev, const struct amba_id *id)
+{
+ struct pl030_rtc *rtc;
+ int ret;
+
+ ret = amba_request_regions(dev, NULL);
+ if (ret)
+ goto err_req;
+
+ rtc = devm_kzalloc(&dev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc) {
+ ret = -ENOMEM;
+ goto err_rtc;
+ }
+
+ rtc->base = ioremap(dev->res.start, resource_size(&dev->res));
+ if (!rtc->base) {
+ ret = -ENOMEM;
+ goto err_rtc;
+ }
+
+ __raw_writel(0, rtc->base + RTC_CR);
+ __raw_writel(0, rtc->base + RTC_EOI);
+
+ amba_set_drvdata(dev, rtc);
+
+ ret = request_irq(dev->irq[0], pl030_interrupt, 0,
+ "rtc-pl030", rtc);
+ if (ret)
+ goto err_irq;
+
+ rtc->rtc = rtc_device_register("pl030", &dev->dev, &pl030_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ goto err_reg;
+ }
+
+ return 0;
+
+ err_reg:
+ free_irq(dev->irq[0], rtc);
+ err_irq:
+ iounmap(rtc->base);
+ err_rtc:
+ amba_release_regions(dev);
+ err_req:
+ return ret;
+}
+
+static int pl030_remove(struct amba_device *dev)
+{
+ struct pl030_rtc *rtc = amba_get_drvdata(dev);
+
+ writel(0, rtc->base + RTC_CR);
+
+ free_irq(dev->irq[0], rtc);
+ rtc_device_unregister(rtc->rtc);
+ iounmap(rtc->base);
+ amba_release_regions(dev);
+
+ return 0;
+}
+
+static struct amba_id pl030_ids[] = {
+ {
+ .id = 0x00041030,
+ .mask = 0x000fffff,
+ },
+ { 0, 0 },
+};
+
+MODULE_DEVICE_TABLE(amba, pl030_ids);
+
+static struct amba_driver pl030_driver = {
+ .drv = {
+ .name = "rtc-pl030",
+ },
+ .probe = pl030_probe,
+ .remove = pl030_remove,
+ .id_table = pl030_ids,
+};
+
+module_amba_driver(pl030_driver);
+
+MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
+MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
new file mode 100644
index 000000000..99181fff8
--- /dev/null
+++ b/drivers/rtc/rtc-pl031.c
@@ -0,0 +1,481 @@
+/*
+ * drivers/rtc/rtc-pl031.c
+ *
+ * Real Time Clock interface for ARM AMBA PrimeCell 031 RTC
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2006 (c) MontaVista Software, Inc.
+ *
+ * Author: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
+ * Copyright 2010 (c) ST-Ericsson AB
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/amba/bus.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+/*
+ * Register definitions
+ */
+#define RTC_DR 0x00 /* Data read register */
+#define RTC_MR 0x04 /* Match register */
+#define RTC_LR 0x08 /* Data load register */
+#define RTC_CR 0x0c /* Control register */
+#define RTC_IMSC 0x10 /* Interrupt mask and set register */
+#define RTC_RIS 0x14 /* Raw interrupt status register */
+#define RTC_MIS 0x18 /* Masked interrupt status register */
+#define RTC_ICR 0x1c /* Interrupt clear register */
+/* ST variants have additional timer functionality */
+#define RTC_TDR 0x20 /* Timer data read register */
+#define RTC_TLR 0x24 /* Timer data load register */
+#define RTC_TCR 0x28 /* Timer control register */
+#define RTC_YDR 0x30 /* Year data read register */
+#define RTC_YMR 0x34 /* Year match register */
+#define RTC_YLR 0x38 /* Year data load register */
+
+#define RTC_CR_EN (1 << 0) /* counter enable bit */
+#define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */
+
+#define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */
+
+/* Common bit definitions for Interrupt status and control registers */
+#define RTC_BIT_AI (1 << 0) /* Alarm interrupt bit */
+#define RTC_BIT_PI (1 << 1) /* Periodic interrupt bit. ST variants only. */
+
+/* Common bit definations for ST v2 for reading/writing time */
+#define RTC_SEC_SHIFT 0
+#define RTC_SEC_MASK (0x3F << RTC_SEC_SHIFT) /* Second [0-59] */
+#define RTC_MIN_SHIFT 6
+#define RTC_MIN_MASK (0x3F << RTC_MIN_SHIFT) /* Minute [0-59] */
+#define RTC_HOUR_SHIFT 12
+#define RTC_HOUR_MASK (0x1F << RTC_HOUR_SHIFT) /* Hour [0-23] */
+#define RTC_WDAY_SHIFT 17
+#define RTC_WDAY_MASK (0x7 << RTC_WDAY_SHIFT) /* Day of Week [1-7] 1=Sunday */
+#define RTC_MDAY_SHIFT 20
+#define RTC_MDAY_MASK (0x1F << RTC_MDAY_SHIFT) /* Day of Month [1-31] */
+#define RTC_MON_SHIFT 25
+#define RTC_MON_MASK (0xF << RTC_MON_SHIFT) /* Month [1-12] 1=January */
+
+#define RTC_TIMER_FREQ 32768
+
+/**
+ * struct pl031_vendor_data - per-vendor variations
+ * @ops: the vendor-specific operations used on this silicon version
+ * @clockwatch: if this is an ST Microelectronics silicon version with a
+ * clockwatch function
+ * @st_weekday: if this is an ST Microelectronics silicon version that need
+ * the weekday fix
+ * @irqflags: special IRQ flags per variant
+ */
+struct pl031_vendor_data {
+ struct rtc_class_ops ops;
+ bool clockwatch;
+ bool st_weekday;
+ unsigned long irqflags;
+};
+
+struct pl031_local {
+ struct pl031_vendor_data *vendor;
+ struct rtc_device *rtc;
+ void __iomem *base;
+};
+
+static int pl031_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long imsc;
+
+ /* Clear any pending alarm interrupts. */
+ writel(RTC_BIT_AI, ldata->base + RTC_ICR);
+
+ imsc = readl(ldata->base + RTC_IMSC);
+
+ if (enabled == 1)
+ writel(imsc | RTC_BIT_AI, ldata->base + RTC_IMSC);
+ else
+ writel(imsc & ~RTC_BIT_AI, ldata->base + RTC_IMSC);
+
+ return 0;
+}
+
+/*
+ * Convert Gregorian date to ST v2 RTC format.
+ */
+static int pl031_stv2_tm_to_time(struct device *dev,
+ struct rtc_time *tm, unsigned long *st_time,
+ unsigned long *bcd_year)
+{
+ int year = tm->tm_year + 1900;
+ int wday = tm->tm_wday;
+
+ /* wday masking is not working in hardware so wday must be valid */
+ if (wday < -1 || wday > 6) {
+ dev_err(dev, "invalid wday value %d\n", tm->tm_wday);
+ return -EINVAL;
+ } else if (wday == -1) {
+ /* wday is not provided, calculate it here */
+ unsigned long time;
+ struct rtc_time calc_tm;
+
+ rtc_tm_to_time(tm, &time);
+ rtc_time_to_tm(time, &calc_tm);
+ wday = calc_tm.tm_wday;
+ }
+
+ *bcd_year = (bin2bcd(year % 100) | bin2bcd(year / 100) << 8);
+
+ *st_time = ((tm->tm_mon + 1) << RTC_MON_SHIFT)
+ | (tm->tm_mday << RTC_MDAY_SHIFT)
+ | ((wday + 1) << RTC_WDAY_SHIFT)
+ | (tm->tm_hour << RTC_HOUR_SHIFT)
+ | (tm->tm_min << RTC_MIN_SHIFT)
+ | (tm->tm_sec << RTC_SEC_SHIFT);
+
+ return 0;
+}
+
+/*
+ * Convert ST v2 RTC format to Gregorian date.
+ */
+static int pl031_stv2_time_to_tm(unsigned long st_time, unsigned long bcd_year,
+ struct rtc_time *tm)
+{
+ tm->tm_year = bcd2bin(bcd_year) + (bcd2bin(bcd_year >> 8) * 100);
+ tm->tm_mon = ((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1;
+ tm->tm_mday = ((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT);
+ tm->tm_wday = ((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1;
+ tm->tm_hour = ((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT);
+ tm->tm_min = ((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT);
+ tm->tm_sec = ((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT);
+
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
+ tm->tm_year -= 1900;
+
+ return 0;
+}
+
+static int pl031_stv2_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ pl031_stv2_time_to_tm(readl(ldata->base + RTC_DR),
+ readl(ldata->base + RTC_YDR), tm);
+
+ return 0;
+}
+
+static int pl031_stv2_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ unsigned long bcd_year;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pl031_stv2_tm_to_time(dev, tm, &time, &bcd_year);
+ if (ret == 0) {
+ writel(bcd_year, ldata->base + RTC_YLR);
+ writel(time, ldata->base + RTC_LR);
+ }
+
+ return ret;
+}
+
+static int pl031_stv2_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pl031_stv2_time_to_tm(readl(ldata->base + RTC_MR),
+ readl(ldata->base + RTC_YMR), &alarm->time);
+
+ alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
+ alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
+
+ return ret;
+}
+
+static int pl031_stv2_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long time;
+ unsigned long bcd_year;
+ int ret;
+
+ /* At the moment, we can only deal with non-wildcarded alarm times. */
+ ret = rtc_valid_tm(&alarm->time);
+ if (ret == 0) {
+ ret = pl031_stv2_tm_to_time(dev, &alarm->time,
+ &time, &bcd_year);
+ if (ret == 0) {
+ writel(bcd_year, ldata->base + RTC_YMR);
+ writel(time, ldata->base + RTC_MR);
+
+ pl031_alarm_irq_enable(dev, alarm->enabled);
+ }
+ }
+
+ return ret;
+}
+
+static irqreturn_t pl031_interrupt(int irq, void *dev_id)
+{
+ struct pl031_local *ldata = dev_id;
+ unsigned long rtcmis;
+ unsigned long events = 0;
+
+ rtcmis = readl(ldata->base + RTC_MIS);
+ if (rtcmis & RTC_BIT_AI) {
+ writel(RTC_BIT_AI, ldata->base + RTC_ICR);
+ events |= (RTC_AF | RTC_IRQF);
+ rtc_update_irq(ldata->rtc, 1, events);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int pl031_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(readl(ldata->base + RTC_DR), tm);
+
+ return 0;
+}
+
+static int pl031_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &time);
+
+ if (ret == 0)
+ writel(time, ldata->base + RTC_LR);
+
+ return ret;
+}
+
+static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(readl(ldata->base + RTC_MR), &alarm->time);
+
+ alarm->pending = readl(ldata->base + RTC_RIS) & RTC_BIT_AI;
+ alarm->enabled = readl(ldata->base + RTC_IMSC) & RTC_BIT_AI;
+
+ return 0;
+}
+
+static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct pl031_local *ldata = dev_get_drvdata(dev);
+ unsigned long time;
+ int ret;
+
+ /* At the moment, we can only deal with non-wildcarded alarm times. */
+ ret = rtc_valid_tm(&alarm->time);
+ if (ret == 0) {
+ ret = rtc_tm_to_time(&alarm->time, &time);
+ if (ret == 0) {
+ writel(time, ldata->base + RTC_MR);
+ pl031_alarm_irq_enable(dev, alarm->enabled);
+ }
+ }
+
+ return ret;
+}
+
+static int pl031_remove(struct amba_device *adev)
+{
+ struct pl031_local *ldata = dev_get_drvdata(&adev->dev);
+
+ free_irq(adev->irq[0], ldata);
+ rtc_device_unregister(ldata->rtc);
+ iounmap(ldata->base);
+ kfree(ldata);
+ amba_release_regions(adev);
+
+ return 0;
+}
+
+static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
+{
+ int ret;
+ struct pl031_local *ldata;
+ struct pl031_vendor_data *vendor = id->data;
+ struct rtc_class_ops *ops = &vendor->ops;
+ unsigned long time, data;
+
+ ret = amba_request_regions(adev, NULL);
+ if (ret)
+ goto err_req;
+
+ ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL);
+ if (!ldata) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ ldata->vendor = vendor;
+
+ ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
+
+ if (!ldata->base) {
+ ret = -ENOMEM;
+ goto out_no_remap;
+ }
+
+ amba_set_drvdata(adev, ldata);
+
+ dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev));
+ dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev));
+
+ data = readl(ldata->base + RTC_CR);
+ /* Enable the clockwatch on ST Variants */
+ if (vendor->clockwatch)
+ data |= RTC_CR_CWEN;
+ else
+ data |= RTC_CR_EN;
+ writel(data, ldata->base + RTC_CR);
+
+ /*
+ * On ST PL031 variants, the RTC reset value does not provide correct
+ * weekday for 2000-01-01. Correct the erroneous sunday to saturday.
+ */
+ if (vendor->st_weekday) {
+ if (readl(ldata->base + RTC_YDR) == 0x2000) {
+ time = readl(ldata->base + RTC_DR);
+ if ((time &
+ (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK))
+ == 0x02120000) {
+ time = time | (0x7 << RTC_WDAY_SHIFT);
+ writel(0x2000, ldata->base + RTC_YLR);
+ writel(time, ldata->base + RTC_LR);
+ }
+ }
+ }
+
+ device_init_wakeup(&adev->dev, 1);
+ ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
+ THIS_MODULE);
+ if (IS_ERR(ldata->rtc)) {
+ ret = PTR_ERR(ldata->rtc);
+ goto out_no_rtc;
+ }
+
+ if (request_irq(adev->irq[0], pl031_interrupt,
+ vendor->irqflags, "rtc-pl031", ldata)) {
+ ret = -EIO;
+ goto out_no_irq;
+ }
+
+ return 0;
+
+out_no_irq:
+ rtc_device_unregister(ldata->rtc);
+out_no_rtc:
+ iounmap(ldata->base);
+out_no_remap:
+ kfree(ldata);
+out:
+ amba_release_regions(adev);
+err_req:
+
+ return ret;
+}
+
+/* Operations for the original ARM version */
+static struct pl031_vendor_data arm_pl031 = {
+ .ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ },
+ .irqflags = IRQF_NO_SUSPEND,
+};
+
+/* The First ST derivative */
+static struct pl031_vendor_data stv1_pl031 = {
+ .ops = {
+ .read_time = pl031_read_time,
+ .set_time = pl031_set_time,
+ .read_alarm = pl031_read_alarm,
+ .set_alarm = pl031_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ },
+ .clockwatch = true,
+ .st_weekday = true,
+ .irqflags = IRQF_NO_SUSPEND,
+};
+
+/* And the second ST derivative */
+static struct pl031_vendor_data stv2_pl031 = {
+ .ops = {
+ .read_time = pl031_stv2_read_time,
+ .set_time = pl031_stv2_set_time,
+ .read_alarm = pl031_stv2_read_alarm,
+ .set_alarm = pl031_stv2_set_alarm,
+ .alarm_irq_enable = pl031_alarm_irq_enable,
+ },
+ .clockwatch = true,
+ .st_weekday = true,
+ /*
+ * This variant shares the IRQ with another block and must not
+ * suspend that IRQ line.
+ */
+ .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND,
+};
+
+static struct amba_id pl031_ids[] = {
+ {
+ .id = 0x00041031,
+ .mask = 0x000fffff,
+ .data = &arm_pl031,
+ },
+ /* ST Micro variants */
+ {
+ .id = 0x00180031,
+ .mask = 0x00ffffff,
+ .data = &stv1_pl031,
+ },
+ {
+ .id = 0x00280031,
+ .mask = 0x00ffffff,
+ .data = &stv2_pl031,
+ },
+ {0, 0},
+};
+
+MODULE_DEVICE_TABLE(amba, pl031_ids);
+
+static struct amba_driver pl031_driver = {
+ .drv = {
+ .name = "rtc-pl031",
+ },
+ .id_table = pl031_ids,
+ .probe = pl031_probe,
+ .remove = pl031_remove,
+};
+
+module_amba_driver(pl031_driver);
+
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net");
+MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
new file mode 100644
index 000000000..795fcbd02
--- /dev/null
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -0,0 +1,544 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/of.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* RTC Register offsets from RTC CTRL REG */
+#define PM8XXX_ALARM_CTRL_OFFSET 0x01
+#define PM8XXX_RTC_WRITE_OFFSET 0x02
+#define PM8XXX_RTC_READ_OFFSET 0x06
+#define PM8XXX_ALARM_RW_OFFSET 0x0A
+
+/* RTC_CTRL register bit fields */
+#define PM8xxx_RTC_ENABLE BIT(7)
+#define PM8xxx_RTC_ALARM_CLEAR BIT(0)
+
+#define NUM_8_BIT_RTC_REGS 0x4
+
+/**
+ * struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions
+ * @ctrl: base address of control register
+ * @write: base address of write register
+ * @read: base address of read register
+ * @alarm_ctrl: base address of alarm control register
+ * @alarm_ctrl2: base address of alarm control2 register
+ * @alarm_rw: base address of alarm read-write register
+ * @alarm_en: alarm enable mask
+ */
+struct pm8xxx_rtc_regs {
+ unsigned int ctrl;
+ unsigned int write;
+ unsigned int read;
+ unsigned int alarm_ctrl;
+ unsigned int alarm_ctrl2;
+ unsigned int alarm_rw;
+ unsigned int alarm_en;
+};
+
+/**
+ * struct pm8xxx_rtc - rtc driver internal structure
+ * @rtc: rtc device for this driver.
+ * @regmap: regmap used to access RTC registers
+ * @allow_set_time: indicates whether writing to the RTC is allowed
+ * @rtc_alarm_irq: rtc alarm irq number.
+ * @ctrl_reg: rtc control register.
+ * @rtc_dev: device structure.
+ * @ctrl_reg_lock: spinlock protecting access to ctrl_reg.
+ */
+struct pm8xxx_rtc {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+ bool allow_set_time;
+ int rtc_alarm_irq;
+ const struct pm8xxx_rtc_regs *regs;
+ struct device *rtc_dev;
+ spinlock_t ctrl_reg_lock;
+};
+
+/*
+ * Steps to write the RTC registers.
+ * 1. Disable alarm if enabled.
+ * 2. Write 0x00 to LSB.
+ * 3. Write Byte[1], Byte[2], Byte[3] then Byte[0].
+ * 4. Enable alarm if disabled in step 1.
+ */
+static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ int rc, i;
+ unsigned long secs, irq_flags;
+ u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0;
+ unsigned int ctrl_reg;
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+
+ if (!rtc_dd->allow_set_time)
+ return -EACCES;
+
+ rtc_tm_to_time(tm, &secs);
+
+ for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
+ value[i] = secs & 0xFF;
+ secs >>= 8;
+ }
+
+ dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
+
+ spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+ rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
+
+ if (ctrl_reg & regs->alarm_en) {
+ alarm_enabled = 1;
+ ctrl_reg &= ~regs->alarm_en;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC control register failed\n");
+ goto rtc_rw_fail;
+ }
+ }
+
+ /* Write 0 to Byte[0] */
+ rc = regmap_write(rtc_dd->regmap, regs->write, 0);
+ if (rc) {
+ dev_err(dev, "Write to RTC write data register failed\n");
+ goto rtc_rw_fail;
+ }
+
+ /* Write Byte[1], Byte[2], Byte[3] */
+ rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
+ &value[1], sizeof(value) - 1);
+ if (rc) {
+ dev_err(dev, "Write to RTC write data register failed\n");
+ goto rtc_rw_fail;
+ }
+
+ /* Write Byte[0] */
+ rc = regmap_write(rtc_dd->regmap, regs->write, value[0]);
+ if (rc) {
+ dev_err(dev, "Write to RTC write data register failed\n");
+ goto rtc_rw_fail;
+ }
+
+ if (alarm_enabled) {
+ ctrl_reg |= regs->alarm_en;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC control register failed\n");
+ goto rtc_rw_fail;
+ }
+ }
+
+rtc_rw_fail:
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+ return rc;
+}
+
+static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int rc;
+ u8 value[NUM_8_BIT_RTC_REGS];
+ unsigned long secs;
+ unsigned int reg;
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+
+ rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
+ if (rc) {
+ dev_err(dev, "RTC read data register failed\n");
+ return rc;
+ }
+
+ /*
+ * Read the LSB again and check if there has been a carry over.
+ * If there is, redo the read operation.
+ */
+ rc = regmap_read(rtc_dd->regmap, regs->read, &reg);
+ if (rc < 0) {
+ dev_err(dev, "RTC read data register failed\n");
+ return rc;
+ }
+
+ if (unlikely(reg < value[0])) {
+ rc = regmap_bulk_read(rtc_dd->regmap, regs->read,
+ value, sizeof(value));
+ if (rc) {
+ dev_err(dev, "RTC read data register failed\n");
+ return rc;
+ }
+ }
+
+ secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+
+ rtc_time_to_tm(secs, tm);
+
+ rc = rtc_valid_tm(tm);
+ if (rc < 0) {
+ dev_err(dev, "Invalid time read from RTC\n");
+ return rc;
+ }
+
+ dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
+ secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
+ tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+ return 0;
+}
+
+static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int rc, i;
+ u8 value[NUM_8_BIT_RTC_REGS];
+ unsigned int ctrl_reg;
+ unsigned long secs, irq_flags;
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+
+ rtc_tm_to_time(&alarm->time, &secs);
+
+ for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
+ value[i] = secs & 0xFF;
+ secs >>= 8;
+ }
+
+ spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+ rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
+ sizeof(value));
+ if (rc) {
+ dev_err(dev, "Write to RTC ALARM register failed\n");
+ goto rtc_rw_fail;
+ }
+
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
+
+ if (alarm->enabled)
+ ctrl_reg |= regs->alarm_en;
+ else
+ ctrl_reg &= ~regs->alarm_en;
+
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC alarm control register failed\n");
+ goto rtc_rw_fail;
+ }
+
+ dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+ alarm->time.tm_hour, alarm->time.tm_min,
+ alarm->time.tm_sec, alarm->time.tm_mday,
+ alarm->time.tm_mon, alarm->time.tm_year);
+rtc_rw_fail:
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ return rc;
+}
+
+static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ int rc;
+ u8 value[NUM_8_BIT_RTC_REGS];
+ unsigned long secs;
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+
+ rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
+ sizeof(value));
+ if (rc) {
+ dev_err(dev, "RTC alarm time read failed\n");
+ return rc;
+ }
+
+ secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
+
+ rtc_time_to_tm(secs, &alarm->time);
+
+ rc = rtc_valid_tm(&alarm->time);
+ if (rc < 0) {
+ dev_err(dev, "Invalid alarm time read from RTC\n");
+ return rc;
+ }
+
+ dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+ alarm->time.tm_hour, alarm->time.tm_min,
+ alarm->time.tm_sec, alarm->time.tm_mday,
+ alarm->time.tm_mon, alarm->time.tm_year);
+
+ return 0;
+}
+
+static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ int rc;
+ unsigned long irq_flags;
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+ unsigned int ctrl_reg;
+
+ spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
+ if (rc)
+ goto rtc_rw_fail;
+
+ if (enable)
+ ctrl_reg |= regs->alarm_en;
+ else
+ ctrl_reg &= ~regs->alarm_en;
+
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
+ if (rc) {
+ dev_err(dev, "Write to RTC control register failed\n");
+ goto rtc_rw_fail;
+ }
+
+rtc_rw_fail:
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ return rc;
+}
+
+static const struct rtc_class_ops pm8xxx_rtc_ops = {
+ .read_time = pm8xxx_rtc_read_time,
+ .set_time = pm8xxx_rtc_set_time,
+ .set_alarm = pm8xxx_rtc_set_alarm,
+ .read_alarm = pm8xxx_rtc_read_alarm,
+ .alarm_irq_enable = pm8xxx_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
+{
+ struct pm8xxx_rtc *rtc_dd = dev_id;
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+ unsigned int ctrl_reg;
+ int rc;
+ unsigned long irq_flags;
+
+ rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
+
+ spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+ /* Clear the alarm enable bit */
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
+ if (rc) {
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ goto rtc_alarm_handled;
+ }
+
+ ctrl_reg &= ~regs->alarm_en;
+
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
+ if (rc) {
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+ dev_err(rtc_dd->rtc_dev,
+ "Write to alarm control register failed\n");
+ goto rtc_alarm_handled;
+ }
+
+ spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
+
+ /* Clear RTC alarm register */
+ rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl2, &ctrl_reg);
+ if (rc) {
+ dev_err(rtc_dd->rtc_dev,
+ "RTC Alarm control2 register read failed\n");
+ goto rtc_alarm_handled;
+ }
+
+ ctrl_reg |= PM8xxx_RTC_ALARM_CLEAR;
+ rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl2, ctrl_reg);
+ if (rc)
+ dev_err(rtc_dd->rtc_dev,
+ "Write to RTC Alarm control2 register failed\n");
+
+rtc_alarm_handled:
+ return IRQ_HANDLED;
+}
+
+static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd)
+{
+ const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
+ unsigned int ctrl_reg;
+ int rc;
+
+ /* Check if the RTC is on, else turn it on */
+ rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg);
+ if (rc)
+ return rc;
+
+ if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
+ ctrl_reg |= PM8xxx_RTC_ENABLE;
+ rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
+ if (rc)
+ return rc;
+ }
+
+ return 0;
+}
+
+static const struct pm8xxx_rtc_regs pm8921_regs = {
+ .ctrl = 0x11d,
+ .write = 0x11f,
+ .read = 0x123,
+ .alarm_rw = 0x127,
+ .alarm_ctrl = 0x11d,
+ .alarm_ctrl2 = 0x11e,
+ .alarm_en = BIT(1),
+};
+
+static const struct pm8xxx_rtc_regs pm8058_regs = {
+ .ctrl = 0x1e8,
+ .write = 0x1ea,
+ .read = 0x1ee,
+ .alarm_rw = 0x1f2,
+ .alarm_ctrl = 0x1e8,
+ .alarm_ctrl2 = 0x1e9,
+ .alarm_en = BIT(1),
+};
+
+static const struct pm8xxx_rtc_regs pm8941_regs = {
+ .ctrl = 0x6046,
+ .write = 0x6040,
+ .read = 0x6048,
+ .alarm_rw = 0x6140,
+ .alarm_ctrl = 0x6146,
+ .alarm_ctrl2 = 0x6148,
+ .alarm_en = BIT(7),
+};
+
+/*
+ * Hardcoded RTC bases until IORESOURCE_REG mapping is figured out
+ */
+static const struct of_device_id pm8xxx_id_table[] = {
+ { .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
+ { .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
+ { .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
+ { },
+};
+MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
+
+static int pm8xxx_rtc_probe(struct platform_device *pdev)
+{
+ int rc;
+ struct pm8xxx_rtc *rtc_dd;
+ const struct of_device_id *match;
+
+ match = of_match_node(pm8xxx_id_table, pdev->dev.of_node);
+ if (!match)
+ return -ENXIO;
+
+ rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
+ if (rtc_dd == NULL)
+ return -ENOMEM;
+
+ /* Initialise spinlock to protect RTC control register */
+ spin_lock_init(&rtc_dd->ctrl_reg_lock);
+
+ rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!rtc_dd->regmap) {
+ dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+ return -ENXIO;
+ }
+
+ rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
+ if (rtc_dd->rtc_alarm_irq < 0) {
+ dev_err(&pdev->dev, "Alarm IRQ resource absent!\n");
+ return -ENXIO;
+ }
+
+ rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
+ "allow-set-time");
+
+ rtc_dd->regs = match->data;
+ rtc_dd->rtc_dev = &pdev->dev;
+
+ rc = pm8xxx_rtc_enable(rtc_dd);
+ if (rc)
+ return rc;
+
+ platform_set_drvdata(pdev, rtc_dd);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ /* Register the RTC device */
+ rtc_dd->rtc = devm_rtc_device_register(&pdev->dev, "pm8xxx_rtc",
+ &pm8xxx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc_dd->rtc)) {
+ dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
+ __func__, PTR_ERR(rtc_dd->rtc));
+ return PTR_ERR(rtc_dd->rtc);
+ }
+
+ /* Request the alarm IRQ */
+ rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->rtc_alarm_irq,
+ pm8xxx_alarm_trigger,
+ IRQF_TRIGGER_RISING,
+ "pm8xxx_rtc_alarm", rtc_dd);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
+ return rc;
+ }
+
+ dev_dbg(&pdev->dev, "Probe success !!\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm8xxx_rtc_resume(struct device *dev)
+{
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc_dd->rtc_alarm_irq);
+
+ return 0;
+}
+
+static int pm8xxx_rtc_suspend(struct device *dev)
+{
+ struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc_dd->rtc_alarm_irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops,
+ pm8xxx_rtc_suspend,
+ pm8xxx_rtc_resume);
+
+static struct platform_driver pm8xxx_rtc_driver = {
+ .probe = pm8xxx_rtc_probe,
+ .driver = {
+ .name = "rtc-pm8xxx",
+ .pm = &pm8xxx_rtc_pm_ops,
+ .of_match_table = pm8xxx_id_table,
+ },
+};
+
+module_platform_driver(pm8xxx_rtc_driver);
+
+MODULE_ALIAS("platform:rtc-pm8xxx");
+MODULE_DESCRIPTION("PMIC8xxx RTC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
new file mode 100644
index 000000000..ffa69e1c9
--- /dev/null
+++ b/drivers/rtc/rtc-proc.c
@@ -0,0 +1,148 @@
+/*
+ * RTC subsystem, proc interface
+ *
+ * Copyright (C) 2005-06 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on arch/arm/common/rtctime.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+
+#include "rtc-core.h"
+
+#define NAME_SIZE 10
+
+#if defined(CONFIG_RTC_HCTOSYS_DEVICE)
+static bool is_rtc_hctosys(struct rtc_device *rtc)
+{
+ int size;
+ char name[NAME_SIZE];
+
+ size = scnprintf(name, NAME_SIZE, "rtc%d", rtc->id);
+ if (size > NAME_SIZE)
+ return false;
+
+ return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE);
+}
+#else
+static bool is_rtc_hctosys(struct rtc_device *rtc)
+{
+ return (rtc->id == 0);
+}
+#endif
+
+static int rtc_proc_show(struct seq_file *seq, void *offset)
+{
+ int err;
+ struct rtc_device *rtc = seq->private;
+ const struct rtc_class_ops *ops = rtc->ops;
+ struct rtc_wkalrm alrm;
+ struct rtc_time tm;
+
+ err = rtc_read_time(rtc, &tm);
+ if (err == 0) {
+ seq_printf(seq,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ }
+
+ err = rtc_read_alarm(rtc, &alrm);
+ if (err == 0) {
+ seq_printf(seq, "alrm_time\t: ");
+ if ((unsigned int)alrm.time.tm_hour <= 24)
+ seq_printf(seq, "%02d:", alrm.time.tm_hour);
+ else
+ seq_printf(seq, "**:");
+ if ((unsigned int)alrm.time.tm_min <= 59)
+ seq_printf(seq, "%02d:", alrm.time.tm_min);
+ else
+ seq_printf(seq, "**:");
+ if ((unsigned int)alrm.time.tm_sec <= 59)
+ seq_printf(seq, "%02d\n", alrm.time.tm_sec);
+ else
+ seq_printf(seq, "**\n");
+
+ seq_printf(seq, "alrm_date\t: ");
+ if ((unsigned int)alrm.time.tm_year <= 200)
+ seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);
+ else
+ seq_printf(seq, "****-");
+ if ((unsigned int)alrm.time.tm_mon <= 11)
+ seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
+ else
+ seq_printf(seq, "**-");
+ if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)
+ seq_printf(seq, "%02d\n", alrm.time.tm_mday);
+ else
+ seq_printf(seq, "**\n");
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ alrm.enabled ? "yes" : "no");
+ seq_printf(seq, "alrm_pending\t: %s\n",
+ alrm.pending ? "yes" : "no");
+ seq_printf(seq, "update IRQ enabled\t: %s\n",
+ (rtc->uie_rtctimer.enabled) ? "yes" : "no");
+ seq_printf(seq, "periodic IRQ enabled\t: %s\n",
+ (rtc->pie_enabled) ? "yes" : "no");
+ seq_printf(seq, "periodic IRQ frequency\t: %d\n",
+ rtc->irq_freq);
+ seq_printf(seq, "max user IRQ frequency\t: %d\n",
+ rtc->max_user_freq);
+ }
+
+ seq_printf(seq, "24hr\t\t: yes\n");
+
+ if (ops->proc)
+ ops->proc(rtc->dev.parent, seq);
+
+ return 0;
+}
+
+static int rtc_proc_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct rtc_device *rtc = PDE_DATA(inode);
+
+ if (!try_module_get(THIS_MODULE))
+ return -ENODEV;
+
+ ret = single_open(file, rtc_proc_show, rtc);
+ if (ret)
+ module_put(THIS_MODULE);
+ return ret;
+}
+
+static int rtc_proc_release(struct inode *inode, struct file *file)
+{
+ int res = single_release(inode, file);
+ module_put(THIS_MODULE);
+ return res;
+}
+
+static const struct file_operations rtc_proc_fops = {
+ .open = rtc_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = rtc_proc_release,
+};
+
+void rtc_proc_add_device(struct rtc_device *rtc)
+{
+ if (is_rtc_hctosys(rtc))
+ proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);
+}
+
+void rtc_proc_del_device(struct rtc_device *rtc)
+{
+ if (is_rtc_hctosys(rtc))
+ remove_proc_entry("driver/rtc", NULL);
+}
diff --git a/drivers/rtc/rtc-ps3.c b/drivers/rtc/rtc-ps3.c
new file mode 100644
index 000000000..6a8f5d758
--- /dev/null
+++ b/drivers/rtc/rtc-ps3.c
@@ -0,0 +1,85 @@
+/*
+ * PS3 RTC Driver
+ *
+ * Copyright 2009 Sony Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.
+ * If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <asm/lv1call.h>
+#include <asm/ps3.h>
+
+
+static u64 read_rtc(void)
+{
+ int result;
+ u64 rtc_val;
+ u64 tb_val;
+
+ result = lv1_get_rtc(&rtc_val, &tb_val);
+ BUG_ON(result);
+
+ return rtc_val;
+}
+
+static int ps3_get_time(struct device *dev, struct rtc_time *tm)
+{
+ rtc_time_to_tm(read_rtc() + ps3_os_area_get_rtc_diff(), tm);
+ return rtc_valid_tm(tm);
+}
+
+static int ps3_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long now;
+
+ rtc_tm_to_time(tm, &now);
+ ps3_os_area_set_rtc_diff(now - read_rtc());
+ return 0;
+}
+
+static const struct rtc_class_ops ps3_rtc_ops = {
+ .read_time = ps3_get_time,
+ .set_time = ps3_set_time,
+};
+
+static int __init ps3_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&dev->dev, "rtc-ps3", &ps3_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+ return 0;
+}
+
+static struct platform_driver ps3_rtc_driver = {
+ .driver = {
+ .name = "rtc-ps3",
+ },
+};
+
+module_platform_driver_probe(ps3_rtc_driver, ps3_rtc_probe);
+
+MODULE_AUTHOR("Sony Corporation");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("ps3 RTC driver");
+MODULE_ALIAS("platform:rtc-ps3");
diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c
new file mode 100644
index 000000000..c0a6e638c
--- /dev/null
+++ b/drivers/rtc/rtc-puv3.c
@@ -0,0 +1,339 @@
+/*
+ * RTC driver code specific to PKUnity SoC and UniCore ISA
+ *
+ * Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
+ * Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include <mach/hardware.h>
+
+static struct resource *puv3_rtc_mem;
+
+static int puv3_rtc_alarmno = IRQ_RTCAlarm;
+static int puv3_rtc_tickno = IRQ_RTC;
+
+static DEFINE_SPINLOCK(puv3_rtc_pie_lock);
+
+/* IRQ Handlers */
+static irqreturn_t puv3_rtc_alarmirq(int irq, void *id)
+{
+ struct rtc_device *rdev = id;
+
+ writel(readl(RTC_RTSR) | RTC_RTSR_AL, RTC_RTSR);
+ rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t puv3_rtc_tickirq(int irq, void *id)
+{
+ struct rtc_device *rdev = id;
+
+ writel(readl(RTC_RTSR) | RTC_RTSR_HZ, RTC_RTSR);
+ rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF);
+ return IRQ_HANDLED;
+}
+
+/* Update control registers */
+static void puv3_rtc_setaie(struct device *dev, int to)
+{
+ unsigned int tmp;
+
+ dev_dbg(dev, "%s: aie=%d\n", __func__, to);
+
+ tmp = readl(RTC_RTSR) & ~RTC_RTSR_ALE;
+
+ if (to)
+ tmp |= RTC_RTSR_ALE;
+
+ writel(tmp, RTC_RTSR);
+}
+
+static int puv3_rtc_setpie(struct device *dev, int enabled)
+{
+ unsigned int tmp;
+
+ dev_dbg(dev, "%s: pie=%d\n", __func__, enabled);
+
+ spin_lock_irq(&puv3_rtc_pie_lock);
+ tmp = readl(RTC_RTSR) & ~RTC_RTSR_HZE;
+
+ if (enabled)
+ tmp |= RTC_RTSR_HZE;
+
+ writel(tmp, RTC_RTSR);
+ spin_unlock_irq(&puv3_rtc_pie_lock);
+
+ return 0;
+}
+
+/* Time read/write */
+static int puv3_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ rtc_time_to_tm(readl(RTC_RCNR), rtc_tm);
+
+ dev_dbg(dev, "read time %02x.%02x.%02x %02x/%02x/%02x\n",
+ rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
+ return 0;
+}
+
+static int puv3_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long rtc_count = 0;
+
+ dev_dbg(dev, "set time %02d.%02d.%02d %02d/%02d/%02d\n",
+ tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ rtc_tm_to_time(tm, &rtc_count);
+ writel(rtc_count, RTC_RCNR);
+
+ return 0;
+}
+
+static int puv3_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *alm_tm = &alrm->time;
+
+ rtc_time_to_tm(readl(RTC_RTAR), alm_tm);
+
+ alrm->enabled = readl(RTC_RTSR) & RTC_RTSR_ALE;
+
+ dev_dbg(dev, "read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
+ alrm->enabled,
+ alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+
+ return 0;
+}
+
+static int puv3_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rtc_time *tm = &alrm->time;
+ unsigned long rtcalarm_count = 0;
+
+ dev_dbg(dev, "puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
+ alrm->enabled,
+ tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
+ tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
+
+ rtc_tm_to_time(tm, &rtcalarm_count);
+ writel(rtcalarm_count, RTC_RTAR);
+
+ puv3_rtc_setaie(dev, alrm->enabled);
+
+ if (alrm->enabled)
+ enable_irq_wake(puv3_rtc_alarmno);
+ else
+ disable_irq_wake(puv3_rtc_alarmno);
+
+ return 0;
+}
+
+static int puv3_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (readl(RTC_RTSR) & RTC_RTSR_HZE) ? "yes" : "no");
+ return 0;
+}
+
+static int puv3_rtc_open(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq,
+ 0, "pkunity-rtc alarm", rtc_dev);
+
+ if (ret) {
+ dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret);
+ return ret;
+ }
+
+ ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq,
+ 0, "pkunity-rtc tick", rtc_dev);
+
+ if (ret) {
+ dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret);
+ goto tick_err;
+ }
+
+ return ret;
+
+ tick_err:
+ free_irq(puv3_rtc_alarmno, rtc_dev);
+ return ret;
+}
+
+static void puv3_rtc_release(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
+
+ /* do not clear AIE here, it may be needed for wake */
+ puv3_rtc_setpie(dev, 0);
+ free_irq(puv3_rtc_alarmno, rtc_dev);
+ free_irq(puv3_rtc_tickno, rtc_dev);
+}
+
+static const struct rtc_class_ops puv3_rtcops = {
+ .open = puv3_rtc_open,
+ .release = puv3_rtc_release,
+ .read_time = puv3_rtc_gettime,
+ .set_time = puv3_rtc_settime,
+ .read_alarm = puv3_rtc_getalarm,
+ .set_alarm = puv3_rtc_setalarm,
+ .proc = puv3_rtc_proc,
+};
+
+static void puv3_rtc_enable(struct device *dev, int en)
+{
+ if (!en) {
+ writel(readl(RTC_RTSR) & ~RTC_RTSR_HZE, RTC_RTSR);
+ } else {
+ /* re-enable the device, and check it is ok */
+ if ((readl(RTC_RTSR) & RTC_RTSR_HZE) == 0) {
+ dev_info(dev, "rtc disabled, re-enabling\n");
+ writel(readl(RTC_RTSR) | RTC_RTSR_HZE, RTC_RTSR);
+ }
+ }
+}
+
+static int puv3_rtc_remove(struct platform_device *dev)
+{
+ struct rtc_device *rtc = platform_get_drvdata(dev);
+
+ rtc_device_unregister(rtc);
+
+ puv3_rtc_setpie(&dev->dev, 0);
+ puv3_rtc_setaie(&dev->dev, 0);
+
+ release_resource(puv3_rtc_mem);
+ kfree(puv3_rtc_mem);
+
+ return 0;
+}
+
+static int puv3_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct resource *res;
+ int ret;
+
+ dev_dbg(&pdev->dev, "%s: probe=%p\n", __func__, pdev);
+
+ /* find the IRQs */
+ puv3_rtc_tickno = platform_get_irq(pdev, 1);
+ if (puv3_rtc_tickno < 0) {
+ dev_err(&pdev->dev, "no irq for rtc tick\n");
+ return -ENOENT;
+ }
+
+ puv3_rtc_alarmno = platform_get_irq(pdev, 0);
+ if (puv3_rtc_alarmno < 0) {
+ dev_err(&pdev->dev, "no irq for alarm\n");
+ return -ENOENT;
+ }
+
+ dev_dbg(&pdev->dev, "PKUnity_rtc: tick irq %d, alarm irq %d\n",
+ puv3_rtc_tickno, puv3_rtc_alarmno);
+
+ /* get the memory region */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get memory region resource\n");
+ return -ENOENT;
+ }
+
+ puv3_rtc_mem = request_mem_region(res->start, resource_size(res),
+ pdev->name);
+
+ if (puv3_rtc_mem == NULL) {
+ dev_err(&pdev->dev, "failed to reserve memory region\n");
+ ret = -ENOENT;
+ goto err_nores;
+ }
+
+ puv3_rtc_enable(&pdev->dev, 1);
+
+ /* register RTC and exit */
+ rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops,
+ THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "cannot attach rtc\n");
+ ret = PTR_ERR(rtc);
+ goto err_nortc;
+ }
+
+ /* platform setup code should have handled this; sigh */
+ if (!device_can_wakeup(&pdev->dev))
+ device_init_wakeup(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
+
+ err_nortc:
+ puv3_rtc_enable(&pdev->dev, 0);
+ release_resource(puv3_rtc_mem);
+
+ err_nores:
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ticnt_save;
+
+static int puv3_rtc_suspend(struct device *dev)
+{
+ /* save RTAR for anyone using periodic interrupts */
+ ticnt_save = readl(RTC_RTAR);
+ puv3_rtc_enable(dev, 0);
+ return 0;
+}
+
+static int puv3_rtc_resume(struct device *dev)
+{
+ puv3_rtc_enable(dev, 1);
+ writel(ticnt_save, RTC_RTAR);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(puv3_rtc_pm_ops, puv3_rtc_suspend, puv3_rtc_resume);
+
+static struct platform_driver puv3_rtc_driver = {
+ .probe = puv3_rtc_probe,
+ .remove = puv3_rtc_remove,
+ .driver = {
+ .name = "PKUnity-v3-RTC",
+ .pm = &puv3_rtc_pm_ops,
+ }
+};
+
+module_platform_driver(puv3_rtc_driver);
+
+MODULE_DESCRIPTION("RTC Driver for the PKUnity v3 chip");
+MODULE_AUTHOR("Hu Dongliang");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
new file mode 100644
index 000000000..4561f3753
--- /dev/null
+++ b/drivers/rtc/rtc-pxa.c
@@ -0,0 +1,435 @@
+/*
+ * Real Time Clock interface for XScale PXA27x and PXA3xx
+ *
+ * Copyright (C) 2008 Robert Jarzmik
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include <mach/hardware.h>
+
+#define RTC_DEF_DIVIDER (32768 - 1)
+#define RTC_DEF_TRIM 0
+#define MAXFREQ_PERIODIC 1000
+
+/*
+ * PXA Registers and bits definitions
+ */
+#define RTSR_PICE (1 << 15) /* Periodic interrupt count enable */
+#define RTSR_PIALE (1 << 14) /* Periodic interrupt Alarm enable */
+#define RTSR_PIAL (1 << 13) /* Periodic interrupt detected */
+#define RTSR_SWALE2 (1 << 11) /* RTC stopwatch alarm2 enable */
+#define RTSR_SWAL2 (1 << 10) /* RTC stopwatch alarm2 detected */
+#define RTSR_SWALE1 (1 << 9) /* RTC stopwatch alarm1 enable */
+#define RTSR_SWAL1 (1 << 8) /* RTC stopwatch alarm1 detected */
+#define RTSR_RDALE2 (1 << 7) /* RTC alarm2 enable */
+#define RTSR_RDAL2 (1 << 6) /* RTC alarm2 detected */
+#define RTSR_RDALE1 (1 << 5) /* RTC alarm1 enable */
+#define RTSR_RDAL1 (1 << 4) /* RTC alarm1 detected */
+#define RTSR_HZE (1 << 3) /* HZ interrupt enable */
+#define RTSR_ALE (1 << 2) /* RTC alarm interrupt enable */
+#define RTSR_HZ (1 << 1) /* HZ rising-edge detected */
+#define RTSR_AL (1 << 0) /* RTC alarm detected */
+#define RTSR_TRIG_MASK (RTSR_AL | RTSR_HZ | RTSR_RDAL1 | RTSR_RDAL2\
+ | RTSR_SWAL1 | RTSR_SWAL2)
+#define RYxR_YEAR_S 9
+#define RYxR_YEAR_MASK (0xfff << RYxR_YEAR_S)
+#define RYxR_MONTH_S 5
+#define RYxR_MONTH_MASK (0xf << RYxR_MONTH_S)
+#define RYxR_DAY_MASK 0x1f
+#define RDxR_WOM_S 20
+#define RDxR_WOM_MASK (0x7 << RDxR_WOM_S)
+#define RDxR_DOW_S 17
+#define RDxR_DOW_MASK (0x7 << RDxR_DOW_S)
+#define RDxR_HOUR_S 12
+#define RDxR_HOUR_MASK (0x1f << RDxR_HOUR_S)
+#define RDxR_MIN_S 6
+#define RDxR_MIN_MASK (0x3f << RDxR_MIN_S)
+#define RDxR_SEC_MASK 0x3f
+
+#define RTSR 0x08
+#define RTTR 0x0c
+#define RDCR 0x10
+#define RYCR 0x14
+#define RDAR1 0x18
+#define RYAR1 0x1c
+#define RTCPICR 0x34
+#define PIAR 0x38
+
+#define rtc_readl(pxa_rtc, reg) \
+ __raw_readl((pxa_rtc)->base + (reg))
+#define rtc_writel(pxa_rtc, reg, value) \
+ __raw_writel((value), (pxa_rtc)->base + (reg))
+
+struct pxa_rtc {
+ struct resource *ress;
+ void __iomem *base;
+ int irq_1Hz;
+ int irq_Alrm;
+ struct rtc_device *rtc;
+ spinlock_t lock; /* Protects this structure */
+};
+
+
+static u32 ryxr_calc(struct rtc_time *tm)
+{
+ return ((tm->tm_year + 1900) << RYxR_YEAR_S)
+ | ((tm->tm_mon + 1) << RYxR_MONTH_S)
+ | tm->tm_mday;
+}
+
+static u32 rdxr_calc(struct rtc_time *tm)
+{
+ return ((((tm->tm_mday + 6) / 7) << RDxR_WOM_S) & RDxR_WOM_MASK)
+ | (((tm->tm_wday + 1) << RDxR_DOW_S) & RDxR_DOW_MASK)
+ | (tm->tm_hour << RDxR_HOUR_S)
+ | (tm->tm_min << RDxR_MIN_S)
+ | tm->tm_sec;
+}
+
+static void tm_calc(u32 rycr, u32 rdcr, struct rtc_time *tm)
+{
+ tm->tm_year = ((rycr & RYxR_YEAR_MASK) >> RYxR_YEAR_S) - 1900;
+ tm->tm_mon = (((rycr & RYxR_MONTH_MASK) >> RYxR_MONTH_S)) - 1;
+ tm->tm_mday = (rycr & RYxR_DAY_MASK);
+ tm->tm_wday = ((rycr & RDxR_DOW_MASK) >> RDxR_DOW_S) - 1;
+ tm->tm_hour = (rdcr & RDxR_HOUR_MASK) >> RDxR_HOUR_S;
+ tm->tm_min = (rdcr & RDxR_MIN_MASK) >> RDxR_MIN_S;
+ tm->tm_sec = rdcr & RDxR_SEC_MASK;
+}
+
+static void rtsr_clear_bits(struct pxa_rtc *pxa_rtc, u32 mask)
+{
+ u32 rtsr;
+
+ rtsr = rtc_readl(pxa_rtc, RTSR);
+ rtsr &= ~RTSR_TRIG_MASK;
+ rtsr &= ~mask;
+ rtc_writel(pxa_rtc, RTSR, rtsr);
+}
+
+static void rtsr_set_bits(struct pxa_rtc *pxa_rtc, u32 mask)
+{
+ u32 rtsr;
+
+ rtsr = rtc_readl(pxa_rtc, RTSR);
+ rtsr &= ~RTSR_TRIG_MASK;
+ rtsr |= mask;
+ rtc_writel(pxa_rtc, RTSR, rtsr);
+}
+
+static irqreturn_t pxa_rtc_irq(int irq, void *dev_id)
+{
+ struct platform_device *pdev = to_platform_device(dev_id);
+ struct pxa_rtc *pxa_rtc = platform_get_drvdata(pdev);
+ u32 rtsr;
+ unsigned long events = 0;
+
+ spin_lock(&pxa_rtc->lock);
+
+ /* clear interrupt sources */
+ rtsr = rtc_readl(pxa_rtc, RTSR);
+ rtc_writel(pxa_rtc, RTSR, rtsr);
+
+ /* temporary disable rtc interrupts */
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1 | RTSR_PIALE | RTSR_HZE);
+
+ /* clear alarm interrupt if it has occurred */
+ if (rtsr & RTSR_RDAL1)
+ rtsr &= ~RTSR_RDALE1;
+
+ /* update irq data & counter */
+ if (rtsr & RTSR_RDAL1)
+ events |= RTC_AF | RTC_IRQF;
+ if (rtsr & RTSR_HZ)
+ events |= RTC_UF | RTC_IRQF;
+ if (rtsr & RTSR_PIAL)
+ events |= RTC_PF | RTC_IRQF;
+
+ rtc_update_irq(pxa_rtc->rtc, 1, events);
+
+ /* enable back rtc interrupts */
+ rtc_writel(pxa_rtc, RTSR, rtsr & ~RTSR_TRIG_MASK);
+
+ spin_unlock(&pxa_rtc->lock);
+ return IRQ_HANDLED;
+}
+
+static int pxa_rtc_open(struct device *dev)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = request_irq(pxa_rtc->irq_1Hz, pxa_rtc_irq, 0,
+ "rtc 1Hz", dev);
+ if (ret < 0) {
+ dev_err(dev, "can't get irq %i, err %d\n", pxa_rtc->irq_1Hz,
+ ret);
+ goto err_irq_1Hz;
+ }
+ ret = request_irq(pxa_rtc->irq_Alrm, pxa_rtc_irq, 0,
+ "rtc Alrm", dev);
+ if (ret < 0) {
+ dev_err(dev, "can't get irq %i, err %d\n", pxa_rtc->irq_Alrm,
+ ret);
+ goto err_irq_Alrm;
+ }
+
+ return 0;
+
+err_irq_Alrm:
+ free_irq(pxa_rtc->irq_1Hz, dev);
+err_irq_1Hz:
+ return ret;
+}
+
+static void pxa_rtc_release(struct device *dev)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+ rtsr_clear_bits(pxa_rtc, RTSR_PIALE | RTSR_RDALE1 | RTSR_HZE);
+ spin_unlock_irq(&pxa_rtc->lock);
+
+ free_irq(pxa_rtc->irq_Alrm, dev);
+ free_irq(pxa_rtc->irq_1Hz, dev);
+}
+
+static int pxa_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ if (enabled)
+ rtsr_set_bits(pxa_rtc, RTSR_RDALE1);
+ else
+ rtsr_clear_bits(pxa_rtc, RTSR_RDALE1);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+ return 0;
+}
+
+static int pxa_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+ u32 rycr, rdcr;
+
+ rycr = rtc_readl(pxa_rtc, RYCR);
+ rdcr = rtc_readl(pxa_rtc, RDCR);
+
+ tm_calc(rycr, rdcr, tm);
+ return 0;
+}
+
+static int pxa_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ rtc_writel(pxa_rtc, RYCR, ryxr_calc(tm));
+ rtc_writel(pxa_rtc, RDCR, rdxr_calc(tm));
+
+ return 0;
+}
+
+static int pxa_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+ u32 rtsr, ryar, rdar;
+
+ ryar = rtc_readl(pxa_rtc, RYAR1);
+ rdar = rtc_readl(pxa_rtc, RDAR1);
+ tm_calc(ryar, rdar, &alrm->time);
+
+ rtsr = rtc_readl(pxa_rtc, RTSR);
+ alrm->enabled = (rtsr & RTSR_RDALE1) ? 1 : 0;
+ alrm->pending = (rtsr & RTSR_RDAL1) ? 1 : 0;
+ return 0;
+}
+
+static int pxa_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+ u32 rtsr;
+
+ spin_lock_irq(&pxa_rtc->lock);
+
+ rtc_writel(pxa_rtc, RYAR1, ryxr_calc(&alrm->time));
+ rtc_writel(pxa_rtc, RDAR1, rdxr_calc(&alrm->time));
+
+ rtsr = rtc_readl(pxa_rtc, RTSR);
+ if (alrm->enabled)
+ rtsr |= RTSR_RDALE1;
+ else
+ rtsr &= ~RTSR_RDALE1;
+ rtc_writel(pxa_rtc, RTSR, rtsr);
+
+ spin_unlock_irq(&pxa_rtc->lock);
+
+ return 0;
+}
+
+static int pxa_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ seq_printf(seq, "trim/divider\t: 0x%08x\n", rtc_readl(pxa_rtc, RTTR));
+ seq_printf(seq, "update_IRQ\t: %s\n",
+ (rtc_readl(pxa_rtc, RTSR) & RTSR_HZE) ? "yes" : "no");
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (rtc_readl(pxa_rtc, RTSR) & RTSR_PIALE) ? "yes" : "no");
+ seq_printf(seq, "periodic_freq\t: %u\n", rtc_readl(pxa_rtc, PIAR));
+
+ return 0;
+}
+
+static const struct rtc_class_ops pxa_rtc_ops = {
+ .read_time = pxa_rtc_read_time,
+ .set_time = pxa_rtc_set_time,
+ .read_alarm = pxa_rtc_read_alarm,
+ .set_alarm = pxa_rtc_set_alarm,
+ .alarm_irq_enable = pxa_alarm_irq_enable,
+ .proc = pxa_rtc_proc,
+};
+
+static int __init pxa_rtc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa_rtc *pxa_rtc;
+ int ret;
+ u32 rttr;
+
+ pxa_rtc = devm_kzalloc(dev, sizeof(*pxa_rtc), GFP_KERNEL);
+ if (!pxa_rtc)
+ return -ENOMEM;
+
+ spin_lock_init(&pxa_rtc->lock);
+ platform_set_drvdata(pdev, pxa_rtc);
+
+ pxa_rtc->ress = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!pxa_rtc->ress) {
+ dev_err(dev, "No I/O memory resource defined\n");
+ return -ENXIO;
+ }
+
+ pxa_rtc->irq_1Hz = platform_get_irq(pdev, 0);
+ if (pxa_rtc->irq_1Hz < 0) {
+ dev_err(dev, "No 1Hz IRQ resource defined\n");
+ return -ENXIO;
+ }
+ pxa_rtc->irq_Alrm = platform_get_irq(pdev, 1);
+ if (pxa_rtc->irq_Alrm < 0) {
+ dev_err(dev, "No alarm IRQ resource defined\n");
+ return -ENXIO;
+ }
+ pxa_rtc_open(dev);
+ pxa_rtc->base = devm_ioremap(dev, pxa_rtc->ress->start,
+ resource_size(pxa_rtc->ress));
+ if (!pxa_rtc->base) {
+ dev_err(dev, "Unable to map pxa RTC I/O memory\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * If the clock divider is uninitialized then reset it to the
+ * default value to get the 1Hz clock.
+ */
+ if (rtc_readl(pxa_rtc, RTTR) == 0) {
+ rttr = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
+ rtc_writel(pxa_rtc, RTTR, rttr);
+ dev_warn(dev, "warning: initializing default clock"
+ " divider/trim value\n");
+ }
+
+ rtsr_clear_bits(pxa_rtc, RTSR_PIALE | RTSR_RDALE1 | RTSR_HZE);
+
+ pxa_rtc->rtc = devm_rtc_device_register(&pdev->dev, "pxa-rtc",
+ &pxa_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pxa_rtc->rtc)) {
+ ret = PTR_ERR(pxa_rtc->rtc);
+ dev_err(dev, "Failed to register RTC device -> %d\n", ret);
+ return ret;
+ }
+
+ device_init_wakeup(dev, 1);
+
+ return 0;
+}
+
+static int __exit pxa_rtc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ pxa_rtc_release(dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_rtc_dt_ids[] = {
+ { .compatible = "marvell,pxa-rtc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pxa_rtc_dt_ids);
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int pxa_rtc_suspend(struct device *dev)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(pxa_rtc->irq_Alrm);
+ return 0;
+}
+
+static int pxa_rtc_resume(struct device *dev)
+{
+ struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(pxa_rtc->irq_Alrm);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pxa_rtc_pm_ops, pxa_rtc_suspend, pxa_rtc_resume);
+
+static struct platform_driver pxa_rtc_driver = {
+ .remove = __exit_p(pxa_rtc_remove),
+ .driver = {
+ .name = "pxa-rtc",
+ .of_match_table = of_match_ptr(pxa_rtc_dt_ids),
+ .pm = &pxa_rtc_pm_ops,
+ },
+};
+
+module_platform_driver_probe(pxa_rtc_driver, pxa_rtc_probe);
+
+MODULE_AUTHOR("Robert Jarzmik <robert.jarzmik@free.fr>");
+MODULE_DESCRIPTION("PXA27x/PXA3xx Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa-rtc");
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c
new file mode 100644
index 000000000..feeedbd82
--- /dev/null
+++ b/drivers/rtc/rtc-r9701.c
@@ -0,0 +1,186 @@
+/*
+ * Driver for Epson RTC-9701JE
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on rtc-max6902.c
+ *
+ * Copyright (C) 2006 8D Technologies inc.
+ * Copyright (C) 2004 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#define RSECCNT 0x00 /* Second Counter */
+#define RMINCNT 0x01 /* Minute Counter */
+#define RHRCNT 0x02 /* Hour Counter */
+#define RWKCNT 0x03 /* Week Counter */
+#define RDAYCNT 0x04 /* Day Counter */
+#define RMONCNT 0x05 /* Month Counter */
+#define RYRCNT 0x06 /* Year Counter */
+#define R100CNT 0x07 /* Y100 Counter */
+#define RMINAR 0x08 /* Minute Alarm */
+#define RHRAR 0x09 /* Hour Alarm */
+#define RWKAR 0x0a /* Week/Day Alarm */
+#define RTIMCNT 0x0c /* Interval Timer */
+#define REXT 0x0d /* Extension Register */
+#define RFLAG 0x0e /* RTC Flag Register */
+#define RCR 0x0f /* RTC Control Register */
+
+static int write_reg(struct device *dev, int address, unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ buf[0] = address & 0x7f;
+ buf[1] = data;
+
+ return spi_write(spi, buf, ARRAY_SIZE(buf));
+}
+
+static int read_regs(struct device *dev, unsigned char *regs, int no_regs)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ u8 txbuf[1], rxbuf[1];
+ int k, ret;
+
+ ret = 0;
+
+ for (k = 0; ret == 0 && k < no_regs; k++) {
+ txbuf[0] = 0x80 | regs[k];
+ ret = spi_write_then_read(spi, txbuf, 1, rxbuf, 1);
+ regs[k] = rxbuf[0];
+ }
+
+ return ret;
+}
+
+static int r9701_get_datetime(struct device *dev, struct rtc_time *dt)
+{
+ int ret;
+ unsigned char buf[] = { RSECCNT, RMINCNT, RHRCNT,
+ RDAYCNT, RMONCNT, RYRCNT };
+
+ ret = read_regs(dev, buf, ARRAY_SIZE(buf));
+ if (ret)
+ return ret;
+
+ memset(dt, 0, sizeof(*dt));
+
+ dt->tm_sec = bcd2bin(buf[0]); /* RSECCNT */
+ dt->tm_min = bcd2bin(buf[1]); /* RMINCNT */
+ dt->tm_hour = bcd2bin(buf[2]); /* RHRCNT */
+
+ dt->tm_mday = bcd2bin(buf[3]); /* RDAYCNT */
+ dt->tm_mon = bcd2bin(buf[4]) - 1; /* RMONCNT */
+ dt->tm_year = bcd2bin(buf[5]) + 100; /* RYRCNT */
+
+ /* the rtc device may contain illegal values on power up
+ * according to the data sheet. make sure they are valid.
+ */
+
+ return rtc_valid_tm(dt);
+}
+
+static int r9701_set_datetime(struct device *dev, struct rtc_time *dt)
+{
+ int ret, year;
+
+ year = dt->tm_year + 1900;
+ if (year >= 2100 || year < 2000)
+ return -EINVAL;
+
+ ret = write_reg(dev, RHRCNT, bin2bcd(dt->tm_hour));
+ ret = ret ? ret : write_reg(dev, RMINCNT, bin2bcd(dt->tm_min));
+ ret = ret ? ret : write_reg(dev, RSECCNT, bin2bcd(dt->tm_sec));
+ ret = ret ? ret : write_reg(dev, RDAYCNT, bin2bcd(dt->tm_mday));
+ ret = ret ? ret : write_reg(dev, RMONCNT, bin2bcd(dt->tm_mon + 1));
+ ret = ret ? ret : write_reg(dev, RYRCNT, bin2bcd(dt->tm_year - 100));
+ ret = ret ? ret : write_reg(dev, RWKCNT, 1 << dt->tm_wday);
+
+ return ret;
+}
+
+static const struct rtc_class_ops r9701_rtc_ops = {
+ .read_time = r9701_get_datetime,
+ .set_time = r9701_set_datetime,
+};
+
+static int r9701_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ struct rtc_time dt;
+ unsigned char tmp;
+ int res;
+
+ tmp = R100CNT;
+ res = read_regs(&spi->dev, &tmp, 1);
+ if (res || tmp != 0x20) {
+ dev_err(&spi->dev, "cannot read RTC register\n");
+ return -ENODEV;
+ }
+
+ /*
+ * The device seems to be present. Now check if the registers
+ * contain invalid values. If so, try to write a default date:
+ * 2000/1/1 00:00:00
+ */
+ if (r9701_get_datetime(&spi->dev, &dt)) {
+ dev_info(&spi->dev, "trying to repair invalid date/time\n");
+ dt.tm_sec = 0;
+ dt.tm_min = 0;
+ dt.tm_hour = 0;
+ dt.tm_mday = 1;
+ dt.tm_mon = 0;
+ dt.tm_year = 100;
+
+ if (r9701_set_datetime(&spi->dev, &dt) ||
+ r9701_get_datetime(&spi->dev, &dt)) {
+ dev_err(&spi->dev, "cannot repair RTC register\n");
+ return -ENODEV;
+ }
+ }
+
+ rtc = devm_rtc_device_register(&spi->dev, "r9701",
+ &r9701_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+
+ return 0;
+}
+
+static int r9701_remove(struct spi_device *spi)
+{
+ return 0;
+}
+
+static struct spi_driver r9701_driver = {
+ .driver = {
+ .name = "rtc-r9701",
+ .owner = THIS_MODULE,
+ },
+ .probe = r9701_probe,
+ .remove = r9701_remove,
+};
+
+module_spi_driver(r9701_driver);
+
+MODULE_DESCRIPTION("r9701 spi RTC driver");
+MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-r9701");
diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c
new file mode 100644
index 000000000..f28d57788
--- /dev/null
+++ b/drivers/rtc/rtc-rc5t583.c
@@ -0,0 +1,321 @@
+/*
+ * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Venu Byravarasu <vbyravarasu@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/rc5t583.h>
+
+struct rc5t583_rtc {
+ struct rtc_device *rtc;
+ /* To store the list of enabled interrupts, during system suspend */
+ u32 irqen;
+};
+
+/* Total number of RTC registers needed to set time*/
+#define NUM_TIME_REGS (RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1)
+
+/* Total number of RTC registers needed to set Y-Alarm*/
+#define NUM_YAL_REGS (RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1)
+
+/* Set Y-Alarm interrupt */
+#define SET_YAL BIT(5)
+
+/* Get Y-Alarm interrupt status*/
+#define GET_YAL_STATUS BIT(3)
+
+static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ u8 val;
+
+ /* Set Y-Alarm, based on 'enabled' */
+ val = enabled ? SET_YAL : 0;
+
+ return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL,
+ val);
+}
+
+/*
+ * Gets current rc5t583 RTC time and date parameters.
+ *
+ * The RTC's time/alarm representation is not what gmtime(3) requires
+ * Linux to use:
+ *
+ * - Months are 1..12 vs Linux 0-11
+ * - Years are 0..99 vs Linux 1900..N (we assume 21st century)
+ */
+static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ u8 rtc_data[NUM_TIME_REGS];
+ int ret;
+
+ ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC read time failed with err:%d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_wday = bcd2bin(rtc_data[3]);
+ tm->tm_mday = bcd2bin(rtc_data[4]);
+ tm->tm_mon = bcd2bin(rtc_data[5]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[6]) + 100;
+
+ return ret;
+}
+
+static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ unsigned char rtc_data[NUM_TIME_REGS];
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_wday);
+ rtc_data[4] = bin2bcd(tm->tm_mday);
+ rtc_data[5] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[6] = bin2bcd(tm->tm_year - 100);
+
+ ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "RTC set time failed with error %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ unsigned char alarm_data[NUM_YAL_REGS];
+ u32 interrupt_enable;
+ int ret;
+
+ ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
+ NUM_YAL_REGS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_alarm error %d\n", ret);
+ return ret;
+ }
+
+ alm->time.tm_min = bcd2bin(alarm_data[0]);
+ alm->time.tm_hour = bcd2bin(alarm_data[1]);
+ alm->time.tm_mday = bcd2bin(alarm_data[2]);
+ alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1;
+ alm->time.tm_year = bcd2bin(alarm_data[4]) + 100;
+
+ ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable);
+ if (ret < 0)
+ return ret;
+
+ /* check if YALE is set */
+ if (interrupt_enable & SET_YAL)
+ alm->enabled = 1;
+
+ return ret;
+}
+
+static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ unsigned char alarm_data[NUM_YAL_REGS];
+ int ret;
+
+ ret = rc5t583_rtc_alarm_irq_enable(dev, 0);
+ if (ret)
+ return ret;
+
+ alarm_data[0] = bin2bcd(alm->time.tm_min);
+ alarm_data[1] = bin2bcd(alm->time.tm_hour);
+ alarm_data[2] = bin2bcd(alm->time.tm_mday);
+ alarm_data[3] = bin2bcd(alm->time.tm_mon + 1);
+ alarm_data[4] = bin2bcd(alm->time.tm_year - 100);
+
+ ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
+ NUM_YAL_REGS);
+ if (ret) {
+ dev_err(dev, "rtc_set_alarm error %d\n", ret);
+ return ret;
+ }
+
+ if (alm->enabled)
+ ret = rc5t583_rtc_alarm_irq_enable(dev, 1);
+
+ return ret;
+}
+
+static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc)
+{
+ struct device *dev = rtc;
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
+ unsigned long events = 0;
+ int ret;
+ u32 rtc_reg;
+
+ ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ if (rtc_reg & GET_YAL_STATUS) {
+ events = RTC_IRQF | RTC_AF;
+ /* clear pending Y-alarm interrupt bit */
+ rtc_reg &= ~GET_YAL_STATUS;
+ }
+
+ ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg);
+ if (ret)
+ return IRQ_NONE;
+
+ /* Notify RTC core on event */
+ rtc_update_irq(rc5t583_rtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops rc5t583_rtc_ops = {
+ .read_time = rc5t583_rtc_read_time,
+ .set_time = rc5t583_rtc_set_time,
+ .read_alarm = rc5t583_rtc_read_alarm,
+ .set_alarm = rc5t583_rtc_set_alarm,
+ .alarm_irq_enable = rc5t583_rtc_alarm_irq_enable,
+};
+
+static int rc5t583_rtc_probe(struct platform_device *pdev)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
+ struct rc5t583_rtc *ricoh_rtc;
+ struct rc5t583_platform_data *pmic_plat_data;
+ int ret;
+ int irq;
+
+ ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc),
+ GFP_KERNEL);
+ if (!ricoh_rtc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, ricoh_rtc);
+
+ /* Clear pending interrupts */
+ ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0);
+ if (ret < 0)
+ return ret;
+
+ /* clear RTC Adjust register */
+ ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to program rtc_adjust reg\n");
+ return -EBUSY;
+ }
+
+ pmic_plat_data = dev_get_platdata(rc5t583->dev);
+ irq = pmic_plat_data->irq_base;
+ if (irq <= 0) {
+ dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
+ irq);
+ return ret;
+ }
+
+ irq += RC5T583_IRQ_YALE;
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW,
+ "rtc-rc5t583", &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ is not free.\n");
+ return ret;
+ }
+ device_init_wakeup(&pdev->dev, 1);
+
+ ricoh_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &rc5t583_rtc_ops, THIS_MODULE);
+ if (IS_ERR(ricoh_rtc->rtc)) {
+ ret = PTR_ERR(ricoh_rtc->rtc);
+ dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Disable rc5t583 RTC interrupts.
+ * Sets status flag to free.
+ */
+static int rc5t583_rtc_remove(struct platform_device *pdev)
+{
+ struct rc5t583_rtc *rc5t583_rtc = platform_get_drvdata(pdev);
+
+ rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rc5t583_rtc_suspend(struct device *dev)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
+ int ret;
+
+ /* Store current list of enabled interrupts*/
+ ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1,
+ &rc5t583_rtc->irqen);
+ return ret;
+}
+
+static int rc5t583_rtc_resume(struct device *dev)
+{
+ struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
+ struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
+
+ /* Restore list of enabled interrupts before suspend */
+ return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1,
+ rc5t583_rtc->irqen);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend,
+ rc5t583_rtc_resume);
+
+static struct platform_driver rc5t583_rtc_driver = {
+ .probe = rc5t583_rtc_probe,
+ .remove = rc5t583_rtc_remove,
+ .driver = {
+ .name = "rtc-rc5t583",
+ .pm = &rc5t583_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(rc5t583_rtc_driver);
+MODULE_ALIAS("platform:rtc-rc5t583");
+MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-rk808.c b/drivers/rtc/rtc-rk808.c
new file mode 100644
index 000000000..91ca0bc1b
--- /dev/null
+++ b/drivers/rtc/rtc-rk808.c
@@ -0,0 +1,420 @@
+/*
+ * RTC driver for Rockchip RK808
+ *
+ * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ * Author: Zhang Qing <zhangqing@rock-chips.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/mfd/rk808.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+/* RTC_CTRL_REG bitfields */
+#define BIT_RTC_CTRL_REG_STOP_RTC_M BIT(0)
+
+/* RK808 has a shadowed register for saving a "frozen" RTC time.
+ * When user setting "GET_TIME" to 1, the time will save in this shadowed
+ * register. If set "READSEL" to 1, user read rtc time register, actually
+ * get the time of that moment. If we need the real time, clr this bit.
+ */
+#define BIT_RTC_CTRL_REG_RTC_GET_TIME BIT(6)
+#define BIT_RTC_CTRL_REG_RTC_READSEL_M BIT(7)
+#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M BIT(3)
+#define RTC_STATUS_MASK 0xFE
+
+#define SECONDS_REG_MSK 0x7F
+#define MINUTES_REG_MAK 0x7F
+#define HOURS_REG_MSK 0x3F
+#define DAYS_REG_MSK 0x3F
+#define MONTHS_REG_MSK 0x1F
+#define YEARS_REG_MSK 0xFF
+#define WEEKS_REG_MSK 0x7
+
+/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
+
+#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
+#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)
+
+struct rk808_rtc {
+ struct rk808 *rk808;
+ struct rtc_device *rtc;
+ int irq;
+};
+
+/* Read current time and date in RTC */
+static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 rtc_data[NUM_TIME_REGS];
+ int ret;
+
+ /* Force an update of the shadowed registers right now */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_RTC_GET_TIME,
+ BIT_RTC_CTRL_REG_RTC_GET_TIME);
+ if (ret) {
+ dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * After we set the GET_TIME bit, the rtc time can't be read
+ * immediately. So we should wait up to 31.25 us, about one cycle of
+ * 32khz. If we clear the GET_TIME bit here, the time of i2c transfer
+ * certainly more than 31.25us: 16 * 2.5us at 400kHz bus frequency.
+ */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_RTC_GET_TIME,
+ 0);
+ if (ret) {
+ dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_read(rk808->regmap, RK808_SECONDS_REG,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK);
+ tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK);
+ tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK);
+ tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK);
+ tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1;
+ tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100;
+ tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK);
+ dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
+
+ return ret;
+}
+
+/* Set current time and date in RTC */
+static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 rtc_data[NUM_TIME_REGS];
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_mday);
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[5] = bin2bcd(tm->tm_year - 100);
+ rtc_data[6] = bin2bcd(tm->tm_wday);
+ dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_wday, tm->tm_hour , tm->tm_min, tm->tm_sec);
+
+ /* Stop RTC while updating the RTC registers */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_STOP_RTC_M,
+ BIT_RTC_CTRL_REG_STOP_RTC_M);
+ if (ret) {
+ dev_err(dev, "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(rk808->regmap, RK808_SECONDS_REG,
+ rtc_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);
+ return ret;
+ }
+ /* Start RTC again */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_STOP_RTC_M, 0);
+ if (ret) {
+ dev_err(dev, "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+/* Read alarm time and date in RTC */
+static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 alrm_data[NUM_ALARM_REGS];
+ uint32_t int_reg;
+ int ret;
+
+ ret = regmap_bulk_read(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ alrm_data, NUM_ALARM_REGS);
+
+ alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);
+ alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK);
+ alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK);
+ alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK);
+ alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1;
+ alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;
+
+ ret = regmap_read(rk808->regmap, RK808_RTC_INT_REG, &int_reg);
+ if (ret) {
+ dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "alrm read RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec);
+
+ alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0;
+
+ return 0;
+}
+
+static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc)
+{
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ int ret;
+
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
+
+ return ret;
+}
+
+static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc)
+{
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ int ret;
+
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_INT_REG,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,
+ BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+
+ return ret;
+}
+
+static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ u8 alrm_data[NUM_ALARM_REGS];
+ int ret;
+
+ ret = rk808_rtc_stop_alarm(rk808_rtc);
+ if (ret) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "alrm set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",
+ 1900 + alrm->time.tm_year, alrm->time.tm_mon + 1,
+ alrm->time.tm_mday, alrm->time.tm_wday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec);
+
+ alrm_data[0] = bin2bcd(alrm->time.tm_sec);
+ alrm_data[1] = bin2bcd(alrm->time.tm_min);
+ alrm_data[2] = bin2bcd(alrm->time.tm_hour);
+ alrm_data[3] = bin2bcd(alrm->time.tm_mday);
+ alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);
+ alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);
+
+ ret = regmap_bulk_write(rk808->regmap, RK808_ALARM_SECONDS_REG,
+ alrm_data, NUM_ALARM_REGS);
+ if (ret) {
+ dev_err(dev, "Failed to bulk write: %d\n", ret);
+ return ret;
+ }
+ if (alrm->enabled) {
+ ret = rk808_rtc_start_alarm(rk808_rtc);
+ if (ret) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int rk808_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ return rk808_rtc_start_alarm(rk808_rtc);
+
+ return rk808_rtc_stop_alarm(rk808_rtc);
+}
+
+/*
+ * We will just handle setting the frequency and make use the framework for
+ * reading the periodic interupts.
+ *
+ * @freq: Current periodic IRQ freq:
+ * bit 0: every second
+ * bit 1: every minute
+ * bit 2: every hour
+ * bit 3: every day
+ */
+static irqreturn_t rk808_alarm_irq(int irq, void *data)
+{
+ struct rk808_rtc *rk808_rtc = data;
+ struct rk808 *rk808 = rk808_rtc->rk808;
+ struct i2c_client *client = rk808->i2c;
+ int ret;
+
+ ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ RTC_STATUS_MASK);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s:Failed to update RTC status: %d\n", __func__, ret);
+ return ret;
+ }
+
+ rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ dev_dbg(&client->dev,
+ "%s:irq=%d\n", __func__, irq);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops rk808_rtc_ops = {
+ .read_time = rk808_rtc_readtime,
+ .set_time = rk808_rtc_set_time,
+ .read_alarm = rk808_rtc_readalarm,
+ .set_alarm = rk808_rtc_setalarm,
+ .alarm_irq_enable = rk808_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+/* Turn off the alarm if it should not be a wake source. */
+static int rk808_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rk808_rtc->irq);
+
+ return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int rk808_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rk808_rtc *rk808_rtc = dev_get_drvdata(&pdev->dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rk808_rtc->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops,
+ rk808_rtc_suspend, rk808_rtc_resume);
+
+static int rk808_rtc_probe(struct platform_device *pdev)
+{
+ struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);
+ struct rk808_rtc *rk808_rtc;
+ struct rtc_time tm;
+ int ret;
+
+ rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL);
+ if (rk808_rtc == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, rk808_rtc);
+ rk808_rtc->rk808 = rk808;
+
+ /* start rtc running by default, and use shadowed timer. */
+ ret = regmap_update_bits(rk808->regmap, RK808_RTC_CTRL_REG,
+ BIT_RTC_CTRL_REG_STOP_RTC_M |
+ BIT_RTC_CTRL_REG_RTC_READSEL_M,
+ BIT_RTC_CTRL_REG_RTC_READSEL_M);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to update RTC control: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(rk808->regmap, RK808_RTC_STATUS_REG,
+ RTC_STATUS_MASK);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to write RTC status: %d\n", ret);
+ return ret;
+ }
+
+ /* set init time */
+ ret = rk808_rtc_readtime(&pdev->dev, &tm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to read RTC time\n");
+ return ret;
+ }
+ ret = rtc_valid_tm(&tm);
+ if (ret)
+ dev_warn(&pdev->dev, "invalid date/time\n");
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rk808_rtc->rtc = devm_rtc_device_register(&pdev->dev, "rk808-rtc",
+ &rk808_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rk808_rtc->rtc)) {
+ ret = PTR_ERR(rk808_rtc->rtc);
+ return ret;
+ }
+
+ rk808_rtc->irq = platform_get_irq(pdev, 0);
+ if (rk808_rtc->irq < 0) {
+ if (rk808_rtc->irq != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Wake up is not possible as irq = %d\n",
+ rk808_rtc->irq);
+ return rk808_rtc->irq;
+ }
+
+ /* request alarm irq of rk808 */
+ ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL,
+ rk808_alarm_irq, 0,
+ "RTC alarm", rk808_rtc);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ rk808_rtc->irq, ret);
+ }
+
+ return ret;
+}
+
+static struct platform_driver rk808_rtc_driver = {
+ .probe = rk808_rtc_probe,
+ .driver = {
+ .name = "rk808-rtc",
+ .pm = &rk808_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(rk808_rtc_driver);
+
+MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs");
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rk808-rtc");
diff --git a/drivers/rtc/rtc-rp5c01.c b/drivers/rtc/rtc-rp5c01.c
new file mode 100644
index 000000000..b548551f3
--- /dev/null
+++ b/drivers/rtc/rtc-rp5c01.c
@@ -0,0 +1,285 @@
+/*
+ * Ricoh RP5C01 RTC Driver
+ *
+ * Copyright 2009 Geert Uytterhoeven
+ *
+ * Based on the A3000 TOD code in arch/m68k/amiga/config.c
+ * Copyright (C) 1993 Hamish Macdonald
+ */
+
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+
+enum {
+ RP5C01_1_SECOND = 0x0, /* MODE 00 */
+ RP5C01_10_SECOND = 0x1, /* MODE 00 */
+ RP5C01_1_MINUTE = 0x2, /* MODE 00 and MODE 01 */
+ RP5C01_10_MINUTE = 0x3, /* MODE 00 and MODE 01 */
+ RP5C01_1_HOUR = 0x4, /* MODE 00 and MODE 01 */
+ RP5C01_10_HOUR = 0x5, /* MODE 00 and MODE 01 */
+ RP5C01_DAY_OF_WEEK = 0x6, /* MODE 00 and MODE 01 */
+ RP5C01_1_DAY = 0x7, /* MODE 00 and MODE 01 */
+ RP5C01_10_DAY = 0x8, /* MODE 00 and MODE 01 */
+ RP5C01_1_MONTH = 0x9, /* MODE 00 */
+ RP5C01_10_MONTH = 0xa, /* MODE 00 */
+ RP5C01_1_YEAR = 0xb, /* MODE 00 */
+ RP5C01_10_YEAR = 0xc, /* MODE 00 */
+
+ RP5C01_12_24_SELECT = 0xa, /* MODE 01 */
+ RP5C01_LEAP_YEAR = 0xb, /* MODE 01 */
+
+ RP5C01_MODE = 0xd, /* all modes */
+ RP5C01_TEST = 0xe, /* all modes */
+ RP5C01_RESET = 0xf, /* all modes */
+};
+
+#define RP5C01_12_24_SELECT_12 (0 << 0)
+#define RP5C01_12_24_SELECT_24 (1 << 0)
+
+#define RP5C01_10_HOUR_AM (0 << 1)
+#define RP5C01_10_HOUR_PM (1 << 1)
+
+#define RP5C01_MODE_TIMER_EN (1 << 3) /* timer enable */
+#define RP5C01_MODE_ALARM_EN (1 << 2) /* alarm enable */
+
+#define RP5C01_MODE_MODE_MASK (3 << 0)
+#define RP5C01_MODE_MODE00 (0 << 0) /* time */
+#define RP5C01_MODE_MODE01 (1 << 0) /* alarm, 12h/24h, leap year */
+#define RP5C01_MODE_RAM_BLOCK10 (2 << 0) /* RAM 4 bits x 13 */
+#define RP5C01_MODE_RAM_BLOCK11 (3 << 0) /* RAM 4 bits x 13 */
+
+#define RP5C01_RESET_1HZ_PULSE (1 << 3)
+#define RP5C01_RESET_16HZ_PULSE (1 << 2)
+#define RP5C01_RESET_SECOND (1 << 1) /* reset divider stages for */
+ /* seconds or smaller units */
+#define RP5C01_RESET_ALARM (1 << 0) /* reset all alarm registers */
+
+
+struct rp5c01_priv {
+ u32 __iomem *regs;
+ struct rtc_device *rtc;
+ spinlock_t lock; /* against concurrent RTC/NVRAM access */
+ struct bin_attribute nvram_attr;
+};
+
+static inline unsigned int rp5c01_read(struct rp5c01_priv *priv,
+ unsigned int reg)
+{
+ return __raw_readl(&priv->regs[reg]) & 0xf;
+}
+
+static inline void rp5c01_write(struct rp5c01_priv *priv, unsigned int val,
+ unsigned int reg)
+{
+ __raw_writel(val, &priv->regs[reg]);
+}
+
+static void rp5c01_lock(struct rp5c01_priv *priv)
+{
+ rp5c01_write(priv, RP5C01_MODE_MODE00, RP5C01_MODE);
+}
+
+static void rp5c01_unlock(struct rp5c01_priv *priv)
+{
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+}
+
+static int rp5c01_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+
+ spin_lock_irq(&priv->lock);
+ rp5c01_lock(priv);
+
+ tm->tm_sec = rp5c01_read(priv, RP5C01_10_SECOND) * 10 +
+ rp5c01_read(priv, RP5C01_1_SECOND);
+ tm->tm_min = rp5c01_read(priv, RP5C01_10_MINUTE) * 10 +
+ rp5c01_read(priv, RP5C01_1_MINUTE);
+ tm->tm_hour = rp5c01_read(priv, RP5C01_10_HOUR) * 10 +
+ rp5c01_read(priv, RP5C01_1_HOUR);
+ tm->tm_mday = rp5c01_read(priv, RP5C01_10_DAY) * 10 +
+ rp5c01_read(priv, RP5C01_1_DAY);
+ tm->tm_wday = rp5c01_read(priv, RP5C01_DAY_OF_WEEK);
+ tm->tm_mon = rp5c01_read(priv, RP5C01_10_MONTH) * 10 +
+ rp5c01_read(priv, RP5C01_1_MONTH) - 1;
+ tm->tm_year = rp5c01_read(priv, RP5C01_10_YEAR) * 10 +
+ rp5c01_read(priv, RP5C01_1_YEAR);
+ if (tm->tm_year <= 69)
+ tm->tm_year += 100;
+
+ rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
+
+ return rtc_valid_tm(tm);
+}
+
+static int rp5c01_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+
+ spin_lock_irq(&priv->lock);
+ rp5c01_lock(priv);
+
+ rp5c01_write(priv, tm->tm_sec / 10, RP5C01_10_SECOND);
+ rp5c01_write(priv, tm->tm_sec % 10, RP5C01_1_SECOND);
+ rp5c01_write(priv, tm->tm_min / 10, RP5C01_10_MINUTE);
+ rp5c01_write(priv, tm->tm_min % 10, RP5C01_1_MINUTE);
+ rp5c01_write(priv, tm->tm_hour / 10, RP5C01_10_HOUR);
+ rp5c01_write(priv, tm->tm_hour % 10, RP5C01_1_HOUR);
+ rp5c01_write(priv, tm->tm_mday / 10, RP5C01_10_DAY);
+ rp5c01_write(priv, tm->tm_mday % 10, RP5C01_1_DAY);
+ if (tm->tm_wday != -1)
+ rp5c01_write(priv, tm->tm_wday, RP5C01_DAY_OF_WEEK);
+ rp5c01_write(priv, (tm->tm_mon + 1) / 10, RP5C01_10_MONTH);
+ rp5c01_write(priv, (tm->tm_mon + 1) % 10, RP5C01_1_MONTH);
+ if (tm->tm_year >= 100)
+ tm->tm_year -= 100;
+ rp5c01_write(priv, tm->tm_year / 10, RP5C01_10_YEAR);
+ rp5c01_write(priv, tm->tm_year % 10, RP5C01_1_YEAR);
+
+ rp5c01_unlock(priv);
+ spin_unlock_irq(&priv->lock);
+ return 0;
+}
+
+static const struct rtc_class_ops rp5c01_rtc_ops = {
+ .read_time = rp5c01_read_time,
+ .set_time = rp5c01_set_time,
+};
+
+
+/*
+ * The NVRAM is organized as 2 blocks of 13 nibbles of 4 bits.
+ * We provide access to them like AmigaOS does: the high nibble of each 8-bit
+ * byte is stored in BLOCK10, the low nibble in BLOCK11.
+ */
+
+static ssize_t rp5c01_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ data = rp5c01_read(priv, pos) << 4;
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ data |= rp5c01_read(priv, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ *buf++ = data;
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static ssize_t rp5c01_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rp5c01_priv *priv = dev_get_drvdata(dev);
+ ssize_t count;
+
+ spin_lock_irq(&priv->lock);
+
+ for (count = 0; size > 0 && pos < RP5C01_MODE; count++, size--) {
+ u8 data = *buf++;
+
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK10,
+ RP5C01_MODE);
+ rp5c01_write(priv, data >> 4, pos);
+ rp5c01_write(priv,
+ RP5C01_MODE_TIMER_EN | RP5C01_MODE_RAM_BLOCK11,
+ RP5C01_MODE);
+ rp5c01_write(priv, data & 0xf, pos++);
+ rp5c01_write(priv, RP5C01_MODE_TIMER_EN | RP5C01_MODE_MODE01,
+ RP5C01_MODE);
+ }
+
+ spin_unlock_irq(&priv->lock);
+ return count;
+}
+
+static int __init rp5c01_rtc_probe(struct platform_device *dev)
+{
+ struct resource *res;
+ struct rp5c01_priv *priv;
+ struct rtc_device *rtc;
+ int error;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ priv = devm_kzalloc(&dev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regs = devm_ioremap(&dev->dev, res->start, resource_size(res));
+ if (!priv->regs)
+ return -ENOMEM;
+
+ sysfs_bin_attr_init(&priv->nvram_attr);
+ priv->nvram_attr.attr.name = "nvram";
+ priv->nvram_attr.attr.mode = S_IRUGO | S_IWUSR;
+ priv->nvram_attr.read = rp5c01_nvram_read;
+ priv->nvram_attr.write = rp5c01_nvram_write;
+ priv->nvram_attr.size = RP5C01_MODE;
+
+ spin_lock_init(&priv->lock);
+
+ platform_set_drvdata(dev, priv);
+
+ rtc = devm_rtc_device_register(&dev->dev, "rtc-rp5c01", &rp5c01_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+ priv->rtc = rtc;
+
+ error = sysfs_create_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int __exit rp5c01_rtc_remove(struct platform_device *dev)
+{
+ struct rp5c01_priv *priv = platform_get_drvdata(dev);
+
+ sysfs_remove_bin_file(&dev->dev.kobj, &priv->nvram_attr);
+ return 0;
+}
+
+static struct platform_driver rp5c01_rtc_driver = {
+ .driver = {
+ .name = "rtc-rp5c01",
+ },
+ .remove = __exit_p(rp5c01_rtc_remove),
+};
+
+module_platform_driver_probe(rp5c01_rtc_driver, rp5c01_rtc_probe);
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Ricoh RP5C01 RTC driver");
+MODULE_ALIAS("platform:rtc-rp5c01");
diff --git a/drivers/rtc/rtc-rs5c313.c b/drivers/rtc/rtc-rs5c313.c
new file mode 100644
index 000000000..5f48167c8
--- /dev/null
+++ b/drivers/rtc/rtc-rs5c313.c
@@ -0,0 +1,414 @@
+/*
+ * Ricoh RS5C313 RTC device/driver
+ * Copyright (C) 2007 Nobuhiro Iwamatsu
+ *
+ * 2005-09-19 modifed by kogiidena
+ *
+ * Based on the old drivers/char/rs5c313_rtc.c by:
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ *
+ * Based on code written by Paul Gortmaker.
+ * Copyright (C) 1996 Paul Gortmaker
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Based on other minimal char device drivers, like Alan's
+ * watchdog, Ted's random, etc. etc.
+ *
+ * 1.07 Paul Gortmaker.
+ * 1.08 Miquel van Smoorenburg: disallow certain things on the
+ * DEC Alpha as the CMOS clock is also used for other things.
+ * 1.09 Nikita Schmidt: epoch support and some Alpha cleanup.
+ * 1.09a Pete Zaitcev: Sun SPARC
+ * 1.09b Jeff Garzik: Modularize, init cleanup
+ * 1.09c Jeff Garzik: SMP cleanup
+ * 1.10 Paul Barton-Davis: add support for async I/O
+ * 1.10a Andrea Arcangeli: Alpha updates
+ * 1.10b Andrew Morton: SMP lock fix
+ * 1.10c Cesar Barros: SMP locking fixes and cleanup
+ * 1.10d Paul Gortmaker: delete paranoia check in rtc_exit
+ * 1.10e Maciej W. Rozycki: Handle DECstation's year weirdness.
+ * 1.11 Takashi Iwai: Kernel access functions
+ * rtc_register/rtc_unregister/rtc_control
+ * 1.11a Daniele Bellucci: Audit create_proc_read_entry in rtc_init
+ * 1.12 Venkatesh Pallipadi: Hooks for emulating rtc on HPET base-timer
+ * CONFIG_HPET_EMULATE_RTC
+ * 1.13 Nobuhiro Iwamatsu: Updata driver.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#define DRV_NAME "rs5c313"
+#define DRV_VERSION "1.13"
+
+#ifdef CONFIG_SH_LANDISK
+/*****************************************************/
+/* LANDISK dependence part of RS5C313 */
+/*****************************************************/
+
+#define SCSMR1 0xFFE00000
+#define SCSCR1 0xFFE00008
+#define SCSMR1_CA 0x80
+#define SCSCR1_CKE 0x03
+#define SCSPTR1 0xFFE0001C
+#define SCSPTR1_EIO 0x80
+#define SCSPTR1_SPB1IO 0x08
+#define SCSPTR1_SPB1DT 0x04
+#define SCSPTR1_SPB0IO 0x02
+#define SCSPTR1_SPB0DT 0x01
+
+#define SDA_OEN SCSPTR1_SPB1IO
+#define SDA SCSPTR1_SPB1DT
+#define SCL_OEN SCSPTR1_SPB0IO
+#define SCL SCSPTR1_SPB0DT
+
+/* RICOH RS5C313 CE port */
+#define RS5C313_CE 0xB0000003
+
+/* RICOH RS5C313 CE port bit */
+#define RS5C313_CE_RTCCE 0x02
+
+/* SCSPTR1 data */
+unsigned char scsptr1_data;
+
+#define RS5C313_CEENABLE __raw_writeb(RS5C313_CE_RTCCE, RS5C313_CE);
+#define RS5C313_CEDISABLE __raw_writeb(0x00, RS5C313_CE)
+#define RS5C313_MISCOP __raw_writeb(0x02, 0xB0000008)
+
+static void rs5c313_init_port(void)
+{
+ /* Set SCK as I/O port and Initialize SCSPTR1 data & I/O port. */
+ __raw_writeb(__raw_readb(SCSMR1) & ~SCSMR1_CA, SCSMR1);
+ __raw_writeb(__raw_readb(SCSCR1) & ~SCSCR1_CKE, SCSCR1);
+
+ /* And Initialize SCL for RS5C313 clock */
+ scsptr1_data = __raw_readb(SCSPTR1) | SCL; /* SCL:H */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ scsptr1_data = __raw_readb(SCSPTR1) | SCL_OEN; /* SCL output enable */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ RS5C313_CEDISABLE; /* CE:L */
+}
+
+static void rs5c313_write_data(unsigned char data)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ /* SDA:Write Data */
+ scsptr1_data = (scsptr1_data & ~SDA) |
+ ((((0x80 >> i) & data) >> (7 - i)) << 2);
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ if (i == 0) {
+ scsptr1_data |= SDA_OEN; /* SDA:output enable */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ }
+ ndelay(700);
+ scsptr1_data &= ~SCL; /* SCL:L */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ ndelay(700);
+ scsptr1_data |= SCL; /* SCL:H */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ }
+
+ scsptr1_data &= ~SDA_OEN; /* SDA:output disable */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+}
+
+static unsigned char rs5c313_read_data(void)
+{
+ int i;
+ unsigned char data = 0;
+
+ for (i = 0; i < 8; i++) {
+ ndelay(700);
+ /* SDA:Read Data */
+ data |= ((__raw_readb(SCSPTR1) & SDA) >> 2) << (7 - i);
+ scsptr1_data &= ~SCL; /* SCL:L */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ ndelay(700);
+ scsptr1_data |= SCL; /* SCL:H */
+ __raw_writeb(scsptr1_data, SCSPTR1);
+ }
+ return data & 0x0F;
+}
+
+#endif /* CONFIG_SH_LANDISK */
+
+/*****************************************************/
+/* machine independence part of RS5C313 */
+/*****************************************************/
+
+/* RICOH RS5C313 address */
+#define RS5C313_ADDR_SEC 0x00
+#define RS5C313_ADDR_SEC10 0x01
+#define RS5C313_ADDR_MIN 0x02
+#define RS5C313_ADDR_MIN10 0x03
+#define RS5C313_ADDR_HOUR 0x04
+#define RS5C313_ADDR_HOUR10 0x05
+#define RS5C313_ADDR_WEEK 0x06
+#define RS5C313_ADDR_INTINTVREG 0x07
+#define RS5C313_ADDR_DAY 0x08
+#define RS5C313_ADDR_DAY10 0x09
+#define RS5C313_ADDR_MON 0x0A
+#define RS5C313_ADDR_MON10 0x0B
+#define RS5C313_ADDR_YEAR 0x0C
+#define RS5C313_ADDR_YEAR10 0x0D
+#define RS5C313_ADDR_CNTREG 0x0E
+#define RS5C313_ADDR_TESTREG 0x0F
+
+/* RICOH RS5C313 control register */
+#define RS5C313_CNTREG_ADJ_BSY 0x01
+#define RS5C313_CNTREG_WTEN_XSTP 0x02
+#define RS5C313_CNTREG_12_24 0x04
+#define RS5C313_CNTREG_CTFG 0x08
+
+/* RICOH RS5C313 test register */
+#define RS5C313_TESTREG_TEST 0x01
+
+/* RICOH RS5C313 control bit */
+#define RS5C313_CNTBIT_READ 0x40
+#define RS5C313_CNTBIT_AD 0x20
+#define RS5C313_CNTBIT_DT 0x10
+
+static unsigned char rs5c313_read_reg(unsigned char addr)
+{
+
+ rs5c313_write_data(addr | RS5C313_CNTBIT_READ | RS5C313_CNTBIT_AD);
+ return rs5c313_read_data();
+}
+
+static void rs5c313_write_reg(unsigned char addr, unsigned char data)
+{
+ data &= 0x0f;
+ rs5c313_write_data(addr | RS5C313_CNTBIT_AD);
+ rs5c313_write_data(data | RS5C313_CNTBIT_DT);
+ return;
+}
+
+static inline unsigned char rs5c313_read_cntreg(void)
+{
+ return rs5c313_read_reg(RS5C313_ADDR_CNTREG);
+}
+
+static inline void rs5c313_write_cntreg(unsigned char data)
+{
+ rs5c313_write_reg(RS5C313_ADDR_CNTREG, data);
+}
+
+static inline void rs5c313_write_intintvreg(unsigned char data)
+{
+ rs5c313_write_reg(RS5C313_ADDR_INTINTVREG, data);
+}
+
+static int rs5c313_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ int data;
+ int cnt;
+
+ cnt = 0;
+ while (1) {
+ RS5C313_CEENABLE; /* CE:H */
+
+ /* Initialize control reg. 24 hour */
+ rs5c313_write_cntreg(0x04);
+
+ if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
+ break;
+
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+
+ if (cnt++ > 100) {
+ dev_err(dev, "%s: timeout error\n", __func__);
+ return -EIO;
+ }
+ }
+
+ data = rs5c313_read_reg(RS5C313_ADDR_SEC);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_SEC10) << 4);
+ tm->tm_sec = bcd2bin(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_MIN);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_MIN10) << 4);
+ tm->tm_min = bcd2bin(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_HOUR);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_HOUR10) << 4);
+ tm->tm_hour = bcd2bin(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_DAY);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_DAY10) << 4);
+ tm->tm_mday = bcd2bin(data);
+
+ data = rs5c313_read_reg(RS5C313_ADDR_MON);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_MON10) << 4);
+ tm->tm_mon = bcd2bin(data) - 1;
+
+ data = rs5c313_read_reg(RS5C313_ADDR_YEAR);
+ data |= (rs5c313_read_reg(RS5C313_ADDR_YEAR10) << 4);
+ tm->tm_year = bcd2bin(data);
+
+ if (tm->tm_year < 70)
+ tm->tm_year += 100;
+
+ data = rs5c313_read_reg(RS5C313_ADDR_WEEK);
+ tm->tm_wday = bcd2bin(data);
+
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+
+ return 0;
+}
+
+static int rs5c313_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ int data;
+ int cnt;
+
+ cnt = 0;
+ /* busy check. */
+ while (1) {
+ RS5C313_CEENABLE; /* CE:H */
+
+ /* Initiatlize control reg. 24 hour */
+ rs5c313_write_cntreg(0x04);
+
+ if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
+ break;
+ RS5C313_MISCOP;
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+
+ if (cnt++ > 100) {
+ dev_err(dev, "%s: timeout error\n", __func__);
+ return -EIO;
+ }
+ }
+
+ data = bin2bcd(tm->tm_sec);
+ rs5c313_write_reg(RS5C313_ADDR_SEC, data);
+ rs5c313_write_reg(RS5C313_ADDR_SEC10, (data >> 4));
+
+ data = bin2bcd(tm->tm_min);
+ rs5c313_write_reg(RS5C313_ADDR_MIN, data);
+ rs5c313_write_reg(RS5C313_ADDR_MIN10, (data >> 4));
+
+ data = bin2bcd(tm->tm_hour);
+ rs5c313_write_reg(RS5C313_ADDR_HOUR, data);
+ rs5c313_write_reg(RS5C313_ADDR_HOUR10, (data >> 4));
+
+ data = bin2bcd(tm->tm_mday);
+ rs5c313_write_reg(RS5C313_ADDR_DAY, data);
+ rs5c313_write_reg(RS5C313_ADDR_DAY10, (data >> 4));
+
+ data = bin2bcd(tm->tm_mon + 1);
+ rs5c313_write_reg(RS5C313_ADDR_MON, data);
+ rs5c313_write_reg(RS5C313_ADDR_MON10, (data >> 4));
+
+ data = bin2bcd(tm->tm_year % 100);
+ rs5c313_write_reg(RS5C313_ADDR_YEAR, data);
+ rs5c313_write_reg(RS5C313_ADDR_YEAR10, (data >> 4));
+
+ data = bin2bcd(tm->tm_wday);
+ rs5c313_write_reg(RS5C313_ADDR_WEEK, data);
+
+ RS5C313_CEDISABLE; /* CE:H */
+ ndelay(700);
+
+ return 0;
+}
+
+static void rs5c313_check_xstp_bit(void)
+{
+ struct rtc_time tm;
+ int cnt;
+
+ RS5C313_CEENABLE; /* CE:H */
+ if (rs5c313_read_cntreg() & RS5C313_CNTREG_WTEN_XSTP) {
+ /* INT interval reg. OFF */
+ rs5c313_write_intintvreg(0x00);
+ /* Initialize control reg. 24 hour & adjust */
+ rs5c313_write_cntreg(0x07);
+
+ /* busy check. */
+ for (cnt = 0; cnt < 100; cnt++) {
+ if (!(rs5c313_read_cntreg() & RS5C313_CNTREG_ADJ_BSY))
+ break;
+ RS5C313_MISCOP;
+ }
+
+ memset(&tm, 0, sizeof(struct rtc_time));
+ tm.tm_mday = 1;
+ tm.tm_mon = 1 - 1;
+ tm.tm_year = 2000 - 1900;
+
+ rs5c313_rtc_set_time(NULL, &tm);
+ pr_err("invalid value, resetting to 1 Jan 2000\n");
+ }
+ RS5C313_CEDISABLE;
+ ndelay(700); /* CE:L */
+}
+
+static const struct rtc_class_ops rs5c313_rtc_ops = {
+ .read_time = rs5c313_rtc_read_time,
+ .set_time = rs5c313_rtc_set_time,
+};
+
+static int rs5c313_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc = devm_rtc_device_register(&pdev->dev, "rs5c313",
+ &rs5c313_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver rs5c313_rtc_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ },
+ .probe = rs5c313_rtc_probe,
+};
+
+static int __init rs5c313_rtc_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&rs5c313_rtc_platform_driver);
+ if (err)
+ return err;
+
+ rs5c313_init_port();
+ rs5c313_check_xstp_bit();
+
+ return 0;
+}
+
+static void __exit rs5c313_rtc_exit(void)
+{
+ platform_driver_unregister(&rs5c313_rtc_platform_driver);
+}
+
+module_init(rs5c313_rtc_init);
+module_exit(rs5c313_rtc_exit);
+
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("kogiidena , Nobuhiro Iwamatsu <iwamatsu@nigauri.org>");
+MODULE_DESCRIPTION("Ricoh RS5C313 RTC device driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
new file mode 100644
index 000000000..090a101c1
--- /dev/null
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -0,0 +1,235 @@
+/*
+ * A SPI driver for the Ricoh RS5C348 RTC
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The board specific init code should provide characteristics of this
+ * device:
+ * Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS
+ */
+
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.2"
+
+#define RS5C348_REG_SECS 0
+#define RS5C348_REG_MINS 1
+#define RS5C348_REG_HOURS 2
+#define RS5C348_REG_WDAY 3
+#define RS5C348_REG_DAY 4
+#define RS5C348_REG_MONTH 5
+#define RS5C348_REG_YEAR 6
+#define RS5C348_REG_CTL1 14
+#define RS5C348_REG_CTL2 15
+
+#define RS5C348_SECS_MASK 0x7f
+#define RS5C348_MINS_MASK 0x7f
+#define RS5C348_HOURS_MASK 0x3f
+#define RS5C348_WDAY_MASK 0x03
+#define RS5C348_DAY_MASK 0x3f
+#define RS5C348_MONTH_MASK 0x1f
+
+#define RS5C348_BIT_PM 0x20 /* REG_HOURS */
+#define RS5C348_BIT_Y2K 0x80 /* REG_MONTH */
+#define RS5C348_BIT_24H 0x20 /* REG_CTL1 */
+#define RS5C348_BIT_XSTP 0x10 /* REG_CTL2 */
+#define RS5C348_BIT_VDET 0x40 /* REG_CTL2 */
+
+#define RS5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */
+#define RS5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */
+#define RS5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */
+#define RS5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */
+
+struct rs5c348_plat_data {
+ struct rtc_device *rtc;
+ int rtc_24h;
+};
+
+static int
+rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev);
+ u8 txbuf[5+7], *txp;
+ int ret;
+
+ /* Transfer 5 bytes before writing SEC. This gives 31us for carry. */
+ txp = txbuf;
+ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[1] = 0; /* dummy */
+ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[3] = 0; /* dummy */
+ txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */
+ txp = &txbuf[5];
+ txp[RS5C348_REG_SECS] = bin2bcd(tm->tm_sec);
+ txp[RS5C348_REG_MINS] = bin2bcd(tm->tm_min);
+ if (pdata->rtc_24h) {
+ txp[RS5C348_REG_HOURS] = bin2bcd(tm->tm_hour);
+ } else {
+ /* hour 0 is AM12, noon is PM12 */
+ txp[RS5C348_REG_HOURS] = bin2bcd((tm->tm_hour + 11) % 12 + 1) |
+ (tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0);
+ }
+ txp[RS5C348_REG_WDAY] = bin2bcd(tm->tm_wday);
+ txp[RS5C348_REG_DAY] = bin2bcd(tm->tm_mday);
+ txp[RS5C348_REG_MONTH] = bin2bcd(tm->tm_mon + 1) |
+ (tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0);
+ txp[RS5C348_REG_YEAR] = bin2bcd(tm->tm_year % 100);
+ /* write in one transfer to avoid data inconsistency */
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0);
+ udelay(62); /* Tcsr 62us */
+ return ret;
+}
+
+static int
+rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct rs5c348_plat_data *pdata = dev_get_platdata(&spi->dev);
+ u8 txbuf[5], rxbuf[7];
+ int ret;
+
+ /* Transfer 5 byte befores reading SEC. This gives 31us for carry. */
+ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[1] = 0; /* dummy */
+ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[3] = 0; /* dummy */
+ txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */
+
+ /* read in one transfer to avoid data inconsistency */
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
+ rxbuf, sizeof(rxbuf));
+ udelay(62); /* Tcsr 62us */
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = bcd2bin(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK);
+ tm->tm_min = bcd2bin(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
+ tm->tm_hour = bcd2bin(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
+ if (!pdata->rtc_24h) {
+ if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM) {
+ tm->tm_hour -= 20;
+ tm->tm_hour %= 12;
+ tm->tm_hour += 12;
+ } else
+ tm->tm_hour %= 12;
+ }
+ tm->tm_wday = bcd2bin(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
+ tm->tm_mday = bcd2bin(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
+ tm->tm_mon =
+ bcd2bin(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = bcd2bin(rxbuf[RS5C348_REG_YEAR]) +
+ ((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0);
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(&spi->dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops rs5c348_rtc_ops = {
+ .read_time = rs5c348_rtc_read_time,
+ .set_time = rs5c348_rtc_set_time,
+};
+
+static struct spi_driver rs5c348_driver;
+
+static int rs5c348_probe(struct spi_device *spi)
+{
+ int ret;
+ struct rtc_device *rtc;
+ struct rs5c348_plat_data *pdata;
+
+ pdata = devm_kzalloc(&spi->dev, sizeof(struct rs5c348_plat_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ spi->dev.platform_data = pdata;
+
+ /* Check D7 of SECOND register */
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS));
+ if (ret < 0 || (ret & 0x80)) {
+ dev_err(&spi->dev, "not found.\n");
+ goto kfree_exit;
+ }
+
+ dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
+ dev_info(&spi->dev, "spiclk %u KHz.\n",
+ (spi->max_speed_hz + 500) / 1000);
+
+ /* turn RTC on if it was not on */
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
+ if (ret < 0)
+ goto kfree_exit;
+ if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
+ u8 buf[2];
+ struct rtc_time tm;
+ if (ret & RS5C348_BIT_VDET)
+ dev_warn(&spi->dev, "voltage-low detected.\n");
+ if (ret & RS5C348_BIT_XSTP)
+ dev_warn(&spi->dev, "oscillator-stop detected.\n");
+ rtc_time_to_tm(0, &tm); /* 1970/1/1 */
+ ret = rs5c348_rtc_set_time(&spi->dev, &tm);
+ if (ret < 0)
+ goto kfree_exit;
+ buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
+ buf[1] = 0;
+ ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
+ if (ret < 0)
+ goto kfree_exit;
+ }
+
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1));
+ if (ret < 0)
+ goto kfree_exit;
+ if (ret & RS5C348_BIT_24H)
+ pdata->rtc_24h = 1;
+
+ rtc = devm_rtc_device_register(&spi->dev, rs5c348_driver.driver.name,
+ &rs5c348_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto kfree_exit;
+ }
+
+ pdata->rtc = rtc;
+
+ return 0;
+ kfree_exit:
+ return ret;
+}
+
+static struct spi_driver rs5c348_driver = {
+ .driver = {
+ .name = "rtc-rs5c348",
+ .owner = THIS_MODULE,
+ },
+ .probe = rs5c348_probe,
+};
+
+module_spi_driver(rs5c348_driver);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+MODULE_ALIAS("spi:rtc-rs5c348");
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
new file mode 100644
index 000000000..28871cd7e
--- /dev/null
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -0,0 +1,699 @@
+/*
+ * An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs
+ *
+ * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net>
+ * Copyright (C) 2006 Tower Technologies
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.6"
+
+
+/*
+ * Ricoh has a family of I2C based RTCs, which differ only slightly from
+ * each other. Differences center on pinout (e.g. how many interrupts,
+ * output clock, etc) and how the control registers are used. The '372
+ * is significant only because that's the one this driver first supported.
+ */
+#define RS5C372_REG_SECS 0
+#define RS5C372_REG_MINS 1
+#define RS5C372_REG_HOURS 2
+#define RS5C372_REG_WDAY 3
+#define RS5C372_REG_DAY 4
+#define RS5C372_REG_MONTH 5
+#define RS5C372_REG_YEAR 6
+#define RS5C372_REG_TRIM 7
+# define RS5C372_TRIM_XSL 0x80
+# define RS5C372_TRIM_MASK 0x7F
+
+#define RS5C_REG_ALARM_A_MIN 8 /* or ALARM_W */
+#define RS5C_REG_ALARM_A_HOURS 9
+#define RS5C_REG_ALARM_A_WDAY 10
+
+#define RS5C_REG_ALARM_B_MIN 11 /* or ALARM_D */
+#define RS5C_REG_ALARM_B_HOURS 12
+#define RS5C_REG_ALARM_B_WDAY 13 /* (ALARM_B only) */
+
+#define RS5C_REG_CTRL1 14
+# define RS5C_CTRL1_AALE (1 << 7) /* or WALE */
+# define RS5C_CTRL1_BALE (1 << 6) /* or DALE */
+# define RV5C387_CTRL1_24 (1 << 5)
+# define RS5C372A_CTRL1_SL1 (1 << 5)
+# define RS5C_CTRL1_CT_MASK (7 << 0)
+# define RS5C_CTRL1_CT0 (0 << 0) /* no periodic irq */
+# define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */
+#define RS5C_REG_CTRL2 15
+# define RS5C372_CTRL2_24 (1 << 5)
+# define R2025_CTRL2_XST (1 << 5)
+# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */
+# define RS5C_CTRL2_CTFG (1 << 2)
+# define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */
+# define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */
+
+
+/* to read (style 1) or write registers starting at R */
+#define RS5C_ADDR(R) (((R) << 4) | 0)
+
+
+enum rtc_type {
+ rtc_undef = 0,
+ rtc_r2025sd,
+ rtc_r2221tl,
+ rtc_rs5c372a,
+ rtc_rs5c372b,
+ rtc_rv5c386,
+ rtc_rv5c387a,
+};
+
+static const struct i2c_device_id rs5c372_id[] = {
+ { "r2025sd", rtc_r2025sd },
+ { "r2221tl", rtc_r2221tl },
+ { "rs5c372a", rtc_rs5c372a },
+ { "rs5c372b", rtc_rs5c372b },
+ { "rv5c386", rtc_rv5c386 },
+ { "rv5c387a", rtc_rv5c387a },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rs5c372_id);
+
+/* REVISIT: this assumes that:
+ * - we're in the 21st century, so it's safe to ignore the century
+ * bit for rv5c38[67] (REG_MONTH bit 7);
+ * - we should use ALARM_A not ALARM_B (may be wrong on some boards)
+ */
+struct rs5c372 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ enum rtc_type type;
+ unsigned time24:1;
+ unsigned has_irq:1;
+ unsigned smbus:1;
+ char buf[17];
+ char *regs;
+};
+
+static int rs5c_get_regs(struct rs5c372 *rs5c)
+{
+ struct i2c_client *client = rs5c->client;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(rs5c->buf),
+ .buf = rs5c->buf
+ },
+ };
+
+ /* This implements the third reading method from the datasheet, using
+ * an internal address that's reset after each transaction (by STOP)
+ * to 0x0f ... so we read extra registers, and skip the first one.
+ *
+ * The first method doesn't work with the iop3xx adapter driver, on at
+ * least 80219 chips; this works around that bug.
+ *
+ * The third method on the other hand doesn't work for the SMBus-only
+ * configurations, so we use the the first method there, stripping off
+ * the extra register in the process.
+ */
+ if (rs5c->smbus) {
+ int addr = RS5C_ADDR(RS5C372_REG_SECS);
+ int size = sizeof(rs5c->buf) - 1;
+
+ if (i2c_smbus_read_i2c_block_data(client, addr, size,
+ rs5c->buf + 1) != size) {
+ dev_warn(&client->dev, "can't read registers\n");
+ return -EIO;
+ }
+ } else {
+ if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
+ dev_warn(&client->dev, "can't read registers\n");
+ return -EIO;
+ }
+ }
+
+ dev_dbg(&client->dev,
+ "%3ph (%02x) %3ph (%02x), %3ph, %3ph; %02x %02x\n",
+ rs5c->regs + 0, rs5c->regs[3],
+ rs5c->regs + 4, rs5c->regs[7],
+ rs5c->regs + 8, rs5c->regs + 11,
+ rs5c->regs[14], rs5c->regs[15]);
+
+ return 0;
+}
+
+static unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg)
+{
+ unsigned hour;
+
+ if (rs5c->time24)
+ return bcd2bin(reg & 0x3f);
+
+ hour = bcd2bin(reg & 0x1f);
+ if (hour == 12)
+ hour = 0;
+ if (reg & 0x20)
+ hour += 12;
+ return hour;
+}
+
+static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour)
+{
+ if (rs5c->time24)
+ return bin2bcd(hour);
+
+ if (hour > 12)
+ return 0x20 | bin2bcd(hour - 12);
+ if (hour == 12)
+ return 0x20 | bin2bcd(12);
+ if (hour == 0)
+ return bin2bcd(12);
+ return bin2bcd(hour);
+}
+
+static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ int status = rs5c_get_regs(rs5c);
+
+ if (status < 0)
+ return status;
+
+ tm->tm_sec = bcd2bin(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
+ tm->tm_min = bcd2bin(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
+ tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
+
+ tm->tm_wday = bcd2bin(rs5c->regs[RS5C372_REG_WDAY] & 0x07);
+ tm->tm_mday = bcd2bin(rs5c->regs[RS5C372_REG_DAY] & 0x3f);
+
+ /* tm->tm_mon is zero-based */
+ tm->tm_mon = bcd2bin(rs5c->regs[RS5C372_REG_MONTH] & 0x1f) - 1;
+
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = bcd2bin(rs5c->regs[RS5C372_REG_YEAR]) + 100;
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* rtc might need initialization */
+ return rtc_valid_tm(tm);
+}
+
+static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ unsigned char buf[7];
+ int addr;
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ addr = RS5C_ADDR(RS5C372_REG_SECS);
+ buf[0] = bin2bcd(tm->tm_sec);
+ buf[1] = bin2bcd(tm->tm_min);
+ buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour);
+ buf[3] = bin2bcd(tm->tm_wday);
+ buf[4] = bin2bcd(tm->tm_mday);
+ buf[5] = bin2bcd(tm->tm_mon + 1);
+ buf[6] = bin2bcd(tm->tm_year - 100);
+
+ if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
+ dev_err(&client->dev, "%s: write error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
+#define NEED_TRIM
+#endif
+
+#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
+#define NEED_TRIM
+#endif
+
+#ifdef NEED_TRIM
+static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
+{
+ struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
+ u8 tmp = rs5c372->regs[RS5C372_REG_TRIM];
+
+ if (osc)
+ *osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;
+
+ if (trim) {
+ dev_dbg(&client->dev, "%s: raw trim=%x\n", __func__, tmp);
+ tmp &= RS5C372_TRIM_MASK;
+ if (tmp & 0x3e) {
+ int t = tmp & 0x3f;
+
+ if (tmp & 0x40)
+ t = (~t | (s8)0xc0) + 1;
+ else
+ t = t - 1;
+
+ tmp = t * 2;
+ } else
+ tmp = 0;
+ *trim = tmp;
+ }
+
+ return 0;
+}
+#endif
+
+static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return rs5c372_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return rs5c372_set_datetime(to_i2c_client(dev), tm);
+}
+
+
+static int rs5c_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ unsigned char buf;
+ int status, addr;
+
+ buf = rs5c->regs[RS5C_REG_CTRL1];
+
+ if (!rs5c->has_irq)
+ return -EINVAL;
+
+ status = rs5c_get_regs(rs5c);
+ if (status < 0)
+ return status;
+
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ if (enabled)
+ buf |= RS5C_CTRL1_AALE;
+ else
+ buf &= ~RS5C_CTRL1_AALE;
+
+ if (i2c_smbus_write_byte_data(client, addr, buf) < 0) {
+ dev_warn(dev, "can't update alarm\n");
+ status = -EIO;
+ } else
+ rs5c->regs[RS5C_REG_CTRL1] = buf;
+
+ return status;
+}
+
+
+/* NOTE: Since RTC_WKALM_{RD,SET} were originally defined for EFI,
+ * which only exposes a polled programming interface; and since
+ * these calls map directly to those EFI requests; we don't demand
+ * we have an IRQ for this chip when we go through this API.
+ *
+ * The older x86_pc derived RTC_ALM_{READ,SET} calls require irqs
+ * though, managed through RTC_AIE_{ON,OFF} requests.
+ */
+
+static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ int status;
+
+ status = rs5c_get_regs(rs5c);
+ if (status < 0)
+ return status;
+
+ /* report alarm time */
+ t->time.tm_sec = 0;
+ t->time.tm_min = bcd2bin(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
+ t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
+ t->time.tm_mday = -1;
+ t->time.tm_mon = -1;
+ t->time.tm_year = -1;
+ t->time.tm_wday = -1;
+ t->time.tm_yday = -1;
+ t->time.tm_isdst = -1;
+
+ /* ... and status */
+ t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
+ t->pending = !!(rs5c->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_AAFG);
+
+ return 0;
+}
+
+static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rs5c372 *rs5c = i2c_get_clientdata(client);
+ int status, addr, i;
+ unsigned char buf[3];
+
+ /* only handle up to 24 hours in the future, like RTC_ALM_SET */
+ if (t->time.tm_mday != -1
+ || t->time.tm_mon != -1
+ || t->time.tm_year != -1)
+ return -EINVAL;
+
+ /* REVISIT: round up tm_sec */
+
+ /* if needed, disable irq (clears pending status) */
+ status = rs5c_get_regs(rs5c);
+ if (status < 0)
+ return status;
+ if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) {
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
+ if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) {
+ dev_dbg(dev, "can't disable alarm\n");
+ return -EIO;
+ }
+ rs5c->regs[RS5C_REG_CTRL1] = buf[0];
+ }
+
+ /* set alarm */
+ buf[0] = bin2bcd(t->time.tm_min);
+ buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
+ buf[2] = 0x7f; /* any/all days */
+
+ for (i = 0; i < sizeof(buf); i++) {
+ addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
+ if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) {
+ dev_dbg(dev, "can't set alarm time\n");
+ return -EIO;
+ }
+ }
+
+ /* ... and maybe enable its irq */
+ if (t->enabled) {
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
+ if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0)
+ dev_warn(dev, "can't enable alarm\n");
+ rs5c->regs[RS5C_REG_CTRL1] = buf[0];
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
+
+static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ int err, osc, trim;
+
+ err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim);
+ if (err == 0) {
+ seq_printf(seq, "crystal\t\t: %d.%03d KHz\n",
+ osc / 1000, osc % 1000);
+ seq_printf(seq, "trim\t\t: %d\n", trim);
+ }
+
+ return 0;
+}
+
+#else
+#define rs5c372_rtc_proc NULL
+#endif
+
+static const struct rtc_class_ops rs5c372_rtc_ops = {
+ .proc = rs5c372_rtc_proc,
+ .read_time = rs5c372_rtc_read_time,
+ .set_time = rs5c372_rtc_set_time,
+ .read_alarm = rs5c_read_alarm,
+ .set_alarm = rs5c_set_alarm,
+ .alarm_irq_enable = rs5c_rtc_alarm_irq_enable,
+};
+
+#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
+
+static ssize_t rs5c372_sysfs_show_trim(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err, trim;
+
+ err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d\n", trim);
+}
+static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL);
+
+static ssize_t rs5c372_sysfs_show_osc(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err, osc;
+
+ err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000);
+}
+static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL);
+
+static int rs5c_sysfs_register(struct device *dev)
+{
+ int err;
+
+ err = device_create_file(dev, &dev_attr_trim);
+ if (err)
+ return err;
+ err = device_create_file(dev, &dev_attr_osc);
+ if (err)
+ device_remove_file(dev, &dev_attr_trim);
+
+ return err;
+}
+
+static void rs5c_sysfs_unregister(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_trim);
+ device_remove_file(dev, &dev_attr_osc);
+}
+
+#else
+static int rs5c_sysfs_register(struct device *dev)
+{
+ return 0;
+}
+
+static void rs5c_sysfs_unregister(struct device *dev)
+{
+ /* nothing */
+}
+#endif /* SYSFS */
+
+static struct i2c_driver rs5c372_driver;
+
+static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
+{
+ unsigned char buf[2];
+ int addr, i, ret = 0;
+
+ if (rs5c372->type == rtc_r2025sd) {
+ if (!(rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST))
+ return ret;
+ rs5c372->regs[RS5C_REG_CTRL2] &= ~R2025_CTRL2_XST;
+ } else {
+ if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP))
+ return ret;
+ rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
+ }
+
+ addr = RS5C_ADDR(RS5C_REG_CTRL1);
+ buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
+ buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
+
+ /* use 24hr mode */
+ switch (rs5c372->type) {
+ case rtc_rs5c372a:
+ case rtc_rs5c372b:
+ buf[1] |= RS5C372_CTRL2_24;
+ rs5c372->time24 = 1;
+ break;
+ case rtc_r2025sd:
+ case rtc_r2221tl:
+ case rtc_rv5c386:
+ case rtc_rv5c387a:
+ buf[0] |= RV5C387_CTRL1_24;
+ rs5c372->time24 = 1;
+ break;
+ default:
+ /* impossible */
+ break;
+ }
+
+ for (i = 0; i < sizeof(buf); i++) {
+ addr = RS5C_ADDR(RS5C_REG_CTRL1 + i);
+ ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]);
+ if (unlikely(ret < 0))
+ return ret;
+ }
+
+ rs5c372->regs[RS5C_REG_CTRL1] = buf[0];
+ rs5c372->regs[RS5C_REG_CTRL2] = buf[1];
+
+ return 0;
+}
+
+static int rs5c372_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ int smbus_mode = 0;
+ struct rs5c372 *rs5c372;
+ struct rtc_time tm;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ /*
+ * If we don't have any master mode adapter, try breaking
+ * it down in to the barest of capabilities.
+ */
+ if (i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ smbus_mode = 1;
+ else {
+ /* Still no good, give up */
+ err = -ENODEV;
+ goto exit;
+ }
+ }
+
+ rs5c372 = devm_kzalloc(&client->dev, sizeof(struct rs5c372),
+ GFP_KERNEL);
+ if (!rs5c372) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ rs5c372->client = client;
+ i2c_set_clientdata(client, rs5c372);
+ rs5c372->type = id->driver_data;
+
+ /* we read registers 0x0f then 0x00-0x0f; skip the first one */
+ rs5c372->regs = &rs5c372->buf[1];
+ rs5c372->smbus = smbus_mode;
+
+ err = rs5c_get_regs(rs5c372);
+ if (err < 0)
+ goto exit;
+
+ /* clock may be set for am/pm or 24 hr time */
+ switch (rs5c372->type) {
+ case rtc_rs5c372a:
+ case rtc_rs5c372b:
+ /* alarm uses ALARM_A; and nINTRA on 372a, nINTR on 372b.
+ * so does periodic irq, except some 327a modes.
+ */
+ if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24)
+ rs5c372->time24 = 1;
+ break;
+ case rtc_r2025sd:
+ case rtc_r2221tl:
+ case rtc_rv5c386:
+ case rtc_rv5c387a:
+ if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24)
+ rs5c372->time24 = 1;
+ /* alarm uses ALARM_W; and nINTRB for alarm and periodic
+ * irq, on both 386 and 387
+ */
+ break;
+ default:
+ dev_err(&client->dev, "unknown RTC type\n");
+ goto exit;
+ }
+
+ /* if the oscillator lost power and no other software (like
+ * the bootloader) set it up, do it here.
+ *
+ * The R2025S/D does this a little differently than the other
+ * parts, so we special case that..
+ */
+ err = rs5c_oscillator_setup(rs5c372);
+ if (unlikely(err < 0)) {
+ dev_err(&client->dev, "setup error\n");
+ goto exit;
+ }
+
+ if (rs5c372_get_datetime(client, &tm) < 0)
+ dev_warn(&client->dev, "clock needs to be set\n");
+
+ dev_info(&client->dev, "%s found, %s, driver version " DRV_VERSION "\n",
+ ({ char *s; switch (rs5c372->type) {
+ case rtc_r2025sd: s = "r2025sd"; break;
+ case rtc_r2221tl: s = "r2221tl"; break;
+ case rtc_rs5c372a: s = "rs5c372a"; break;
+ case rtc_rs5c372b: s = "rs5c372b"; break;
+ case rtc_rv5c386: s = "rv5c386"; break;
+ case rtc_rv5c387a: s = "rv5c387a"; break;
+ default: s = "chip"; break;
+ }; s;}),
+ rs5c372->time24 ? "24hr" : "am/pm"
+ );
+
+ /* REVISIT use client->irq to register alarm irq ... */
+ rs5c372->rtc = devm_rtc_device_register(&client->dev,
+ rs5c372_driver.driver.name,
+ &rs5c372_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rs5c372->rtc)) {
+ err = PTR_ERR(rs5c372->rtc);
+ goto exit;
+ }
+
+ err = rs5c_sysfs_register(&client->dev);
+ if (err)
+ goto exit;
+
+ return 0;
+
+exit:
+ return err;
+}
+
+static int rs5c372_remove(struct i2c_client *client)
+{
+ rs5c_sysfs_unregister(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver rs5c372_driver = {
+ .driver = {
+ .name = "rtc-rs5c372",
+ },
+ .probe = rs5c372_probe,
+ .remove = rs5c372_remove,
+ .id_table = rs5c372_id,
+};
+
+module_i2c_driver(rs5c372_driver);
+
+MODULE_AUTHOR(
+ "Pavel Mironchik <pmironchik@optifacio.net>, "
+ "Alessandro Zummo <a.zummo@towertech.it>, "
+ "Paul Mundt <lethal@linux-sh.org>");
+MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
new file mode 100644
index 000000000..e9ac5a43b
--- /dev/null
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -0,0 +1,427 @@
+/*
+ * Micro Crystal RV-3029C2 rtc class driver
+ *
+ * Author: Gregory Hermant <gregory.hermant@calao-systems.com>
+ *
+ * based on previously existing rtc class drivers
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * NOTE: Currently this driver only supports the bare minimum for read
+ * and write the RTC and alarms. The extra features provided by this chip
+ * (trickle charger, eeprom, T° compensation) are unavailable.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+
+/* Register map */
+/* control section */
+#define RV3029C2_ONOFF_CTRL 0x00
+#define RV3029C2_IRQ_CTRL 0x01
+#define RV3029C2_IRQ_CTRL_AIE (1 << 0)
+#define RV3029C2_IRQ_FLAGS 0x02
+#define RV3029C2_IRQ_FLAGS_AF (1 << 0)
+#define RV3029C2_STATUS 0x03
+#define RV3029C2_STATUS_VLOW1 (1 << 2)
+#define RV3029C2_STATUS_VLOW2 (1 << 3)
+#define RV3029C2_STATUS_SR (1 << 4)
+#define RV3029C2_STATUS_PON (1 << 5)
+#define RV3029C2_STATUS_EEBUSY (1 << 7)
+#define RV3029C2_RST_CTRL 0x04
+#define RV3029C2_CONTROL_SECTION_LEN 0x05
+
+/* watch section */
+#define RV3029C2_W_SEC 0x08
+#define RV3029C2_W_MINUTES 0x09
+#define RV3029C2_W_HOURS 0x0A
+#define RV3029C2_REG_HR_12_24 (1<<6) /* 24h/12h mode */
+#define RV3029C2_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
+#define RV3029C2_W_DATE 0x0B
+#define RV3029C2_W_DAYS 0x0C
+#define RV3029C2_W_MONTHS 0x0D
+#define RV3029C2_W_YEARS 0x0E
+#define RV3029C2_WATCH_SECTION_LEN 0x07
+
+/* alarm section */
+#define RV3029C2_A_SC 0x10
+#define RV3029C2_A_MN 0x11
+#define RV3029C2_A_HR 0x12
+#define RV3029C2_A_DT 0x13
+#define RV3029C2_A_DW 0x14
+#define RV3029C2_A_MO 0x15
+#define RV3029C2_A_YR 0x16
+#define RV3029C2_ALARM_SECTION_LEN 0x07
+
+/* timer section */
+#define RV3029C2_TIMER_LOW 0x18
+#define RV3029C2_TIMER_HIGH 0x19
+
+/* temperature section */
+#define RV3029C2_TEMP_PAGE 0x20
+
+/* eeprom data section */
+#define RV3029C2_E2P_EEDATA1 0x28
+#define RV3029C2_E2P_EEDATA2 0x29
+
+/* eeprom control section */
+#define RV3029C2_CONTROL_E2P_EECTRL 0x30
+#define RV3029C2_TRICKLE_1K (1<<0) /* 1K resistance */
+#define RV3029C2_TRICKLE_5K (1<<1) /* 5K resistance */
+#define RV3029C2_TRICKLE_20K (1<<2) /* 20K resistance */
+#define RV3029C2_TRICKLE_80K (1<<3) /* 80K resistance */
+#define RV3029C2_CONTROL_E2P_XTALOFFSET 0x31
+#define RV3029C2_CONTROL_E2P_QCOEF 0x32
+#define RV3029C2_CONTROL_E2P_TURNOVER 0x33
+
+/* user ram section */
+#define RV3029C2_USR1_RAM_PAGE 0x38
+#define RV3029C2_USR1_SECTION_LEN 0x04
+#define RV3029C2_USR2_RAM_PAGE 0x3C
+#define RV3029C2_USR2_SECTION_LEN 0x04
+
+static int
+rv3029c2_i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buf,
+ unsigned len)
+{
+ int ret;
+
+ if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
+ (reg + len > RV3029C2_USR1_RAM_PAGE + 8))
+ return -EINVAL;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
+ if (ret < 0)
+ return ret;
+ if (ret < len)
+ return -EIO;
+ return 0;
+}
+
+static int
+rv3029c2_i2c_write_regs(struct i2c_client *client, u8 reg, u8 const buf[],
+ unsigned len)
+{
+ if ((reg > RV3029C2_USR1_RAM_PAGE + 7) ||
+ (reg + len > RV3029C2_USR1_RAM_PAGE + 8))
+ return -EINVAL;
+
+ return i2c_smbus_write_i2c_block_data(client, reg, len, buf);
+}
+
+static int
+rv3029c2_i2c_get_sr(struct i2c_client *client, u8 *buf)
+{
+ int ret = rv3029c2_i2c_read_regs(client, RV3029C2_STATUS, buf, 1);
+
+ if (ret < 0)
+ return -EIO;
+ dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]);
+ return 0;
+}
+
+static int
+rv3029c2_i2c_set_sr(struct i2c_client *client, u8 val)
+{
+ u8 buf[1];
+ int sr;
+
+ buf[0] = val;
+ sr = rv3029c2_i2c_write_regs(client, RV3029C2_STATUS, buf, 1);
+ dev_dbg(&client->dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]);
+ if (sr < 0)
+ return -EIO;
+ return 0;
+}
+
+static int
+rv3029c2_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
+{
+ u8 buf[1];
+ int ret;
+ u8 regs[RV3029C2_WATCH_SECTION_LEN] = { 0, };
+
+ ret = rv3029c2_i2c_get_sr(client, buf);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_W_SEC , regs,
+ RV3029C2_WATCH_SECTION_LEN);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading RTC section failed\n",
+ __func__);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(regs[RV3029C2_W_SEC-RV3029C2_W_SEC]);
+ tm->tm_min = bcd2bin(regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC]);
+
+ /* HR field has a more complex interpretation */
+ {
+ const u8 _hr = regs[RV3029C2_W_HOURS-RV3029C2_W_SEC];
+ if (_hr & RV3029C2_REG_HR_12_24) {
+ /* 12h format */
+ tm->tm_hour = bcd2bin(_hr & 0x1f);
+ if (_hr & RV3029C2_REG_HR_PM) /* PM flag set */
+ tm->tm_hour += 12;
+ } else /* 24h format */
+ tm->tm_hour = bcd2bin(_hr & 0x3f);
+ }
+
+ tm->tm_mday = bcd2bin(regs[RV3029C2_W_DATE-RV3029C2_W_SEC]);
+ tm->tm_mon = bcd2bin(regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC]) - 1;
+ tm->tm_year = bcd2bin(regs[RV3029C2_W_YEARS-RV3029C2_W_SEC]) + 100;
+ tm->tm_wday = bcd2bin(regs[RV3029C2_W_DAYS-RV3029C2_W_SEC]) - 1;
+
+ return 0;
+}
+
+static int rv3029c2_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return rv3029c2_i2c_read_time(to_i2c_client(dev), tm);
+}
+
+static int
+rv3029c2_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm)
+{
+ struct rtc_time *const tm = &alarm->time;
+ int ret;
+ u8 regs[8];
+
+ ret = rv3029c2_i2c_get_sr(client, regs);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_A_SC, regs,
+ RV3029C2_ALARM_SECTION_LEN);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading alarm section failed\n",
+ __func__);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(regs[RV3029C2_A_SC-RV3029C2_A_SC] & 0x7f);
+ tm->tm_min = bcd2bin(regs[RV3029C2_A_MN-RV3029C2_A_SC] & 0x7f);
+ tm->tm_hour = bcd2bin(regs[RV3029C2_A_HR-RV3029C2_A_SC] & 0x3f);
+ tm->tm_mday = bcd2bin(regs[RV3029C2_A_DT-RV3029C2_A_SC] & 0x3f);
+ tm->tm_mon = bcd2bin(regs[RV3029C2_A_MO-RV3029C2_A_SC] & 0x1f) - 1;
+ tm->tm_year = bcd2bin(regs[RV3029C2_A_YR-RV3029C2_A_SC] & 0x7f) + 100;
+ tm->tm_wday = bcd2bin(regs[RV3029C2_A_DW-RV3029C2_A_SC] & 0x07) - 1;
+
+ return 0;
+}
+
+static int
+rv3029c2_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ return rv3029c2_i2c_read_alarm(to_i2c_client(dev), alarm);
+}
+
+static int rv3029c2_rtc_i2c_alarm_set_irq(struct i2c_client *client,
+ int enable)
+{
+ int ret;
+ u8 buf[1];
+
+ /* enable AIE irq */
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't read INT reg\n");
+ return ret;
+ }
+ if (enable)
+ buf[0] |= RV3029C2_IRQ_CTRL_AIE;
+ else
+ buf[0] &= ~RV3029C2_IRQ_CTRL_AIE;
+
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_CTRL, buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't set INT reg\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rv3029c2_rtc_i2c_set_alarm(struct i2c_client *client,
+ struct rtc_wkalrm *alarm)
+{
+ struct rtc_time *const tm = &alarm->time;
+ int ret;
+ u8 regs[8];
+
+ /*
+ * The clock has an 8 bit wide bcd-coded register (they never learn)
+ * for the year. tm_year is an offset from 1900 and we are interested
+ * in the 2000-2099 range, so any value less than 100 is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ ret = rv3029c2_i2c_get_sr(client, regs);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return -EIO;
+ }
+ regs[RV3029C2_A_SC-RV3029C2_A_SC] = bin2bcd(tm->tm_sec & 0x7f);
+ regs[RV3029C2_A_MN-RV3029C2_A_SC] = bin2bcd(tm->tm_min & 0x7f);
+ regs[RV3029C2_A_HR-RV3029C2_A_SC] = bin2bcd(tm->tm_hour & 0x3f);
+ regs[RV3029C2_A_DT-RV3029C2_A_SC] = bin2bcd(tm->tm_mday & 0x3f);
+ regs[RV3029C2_A_MO-RV3029C2_A_SC] = bin2bcd((tm->tm_mon & 0x1f) - 1);
+ regs[RV3029C2_A_DW-RV3029C2_A_SC] = bin2bcd((tm->tm_wday & 7) - 1);
+ regs[RV3029C2_A_YR-RV3029C2_A_SC] = bin2bcd((tm->tm_year & 0x7f) - 100);
+
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_A_SC, regs,
+ RV3029C2_ALARM_SECTION_LEN);
+ if (ret < 0)
+ return ret;
+
+ if (alarm->enabled) {
+ u8 buf[1];
+
+ /* clear AF flag */
+ ret = rv3029c2_i2c_read_regs(client, RV3029C2_IRQ_FLAGS,
+ buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't read alarm flag\n");
+ return ret;
+ }
+ buf[0] &= ~RV3029C2_IRQ_FLAGS_AF;
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_IRQ_FLAGS,
+ buf, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "can't set alarm flag\n");
+ return ret;
+ }
+ /* enable AIE irq */
+ ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 1);
+ if (ret)
+ return ret;
+
+ dev_dbg(&client->dev, "alarm IRQ armed\n");
+ } else {
+ /* disable AIE irq */
+ ret = rv3029c2_rtc_i2c_alarm_set_irq(client, 0);
+ if (ret)
+ return ret;
+
+ dev_dbg(&client->dev, "alarm IRQ disabled\n");
+ }
+
+ return 0;
+}
+
+static int rv3029c2_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ return rv3029c2_rtc_i2c_set_alarm(to_i2c_client(dev), alarm);
+}
+
+static int
+rv3029c2_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
+{
+ u8 regs[8];
+ int ret;
+
+ /*
+ * The clock has an 8 bit wide bcd-coded register (they never learn)
+ * for the year. tm_year is an offset from 1900 and we are interested
+ * in the 2000-2099 range, so any value less than 100 is invalid.
+ */
+ if (tm->tm_year < 100)
+ return -EINVAL;
+
+ regs[RV3029C2_W_SEC-RV3029C2_W_SEC] = bin2bcd(tm->tm_sec);
+ regs[RV3029C2_W_MINUTES-RV3029C2_W_SEC] = bin2bcd(tm->tm_min);
+ regs[RV3029C2_W_HOURS-RV3029C2_W_SEC] = bin2bcd(tm->tm_hour);
+ regs[RV3029C2_W_DATE-RV3029C2_W_SEC] = bin2bcd(tm->tm_mday);
+ regs[RV3029C2_W_MONTHS-RV3029C2_W_SEC] = bin2bcd(tm->tm_mon+1);
+ regs[RV3029C2_W_DAYS-RV3029C2_W_SEC] = bin2bcd((tm->tm_wday & 7)+1);
+ regs[RV3029C2_W_YEARS-RV3029C2_W_SEC] = bin2bcd(tm->tm_year - 100);
+
+ ret = rv3029c2_i2c_write_regs(client, RV3029C2_W_SEC, regs,
+ RV3029C2_WATCH_SECTION_LEN);
+ if (ret < 0)
+ return ret;
+
+ ret = rv3029c2_i2c_get_sr(client, regs);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return ret;
+ }
+ /* clear PON bit */
+ ret = rv3029c2_i2c_set_sr(client, (regs[0] & ~RV3029C2_STATUS_PON));
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: reading SR failed\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rv3029c2_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return rv3029c2_i2c_set_time(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops rv3029c2_rtc_ops = {
+ .read_time = rv3029c2_rtc_read_time,
+ .set_time = rv3029c2_rtc_set_time,
+ .read_alarm = rv3029c2_rtc_read_alarm,
+ .set_alarm = rv3029c2_rtc_set_alarm,
+};
+
+static struct i2c_device_id rv3029c2_id[] = {
+ { "rv3029c2", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rv3029c2_id);
+
+static int rv3029c2_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rtc_device *rtc;
+ int rc = 0;
+ u8 buf[1];
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_EMUL))
+ return -ENODEV;
+
+ rc = rv3029c2_i2c_get_sr(client, buf);
+ if (rc < 0) {
+ dev_err(&client->dev, "reading status failed\n");
+ return rc;
+ }
+
+ rtc = devm_rtc_device_register(&client->dev, client->name,
+ &rv3029c2_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ return 0;
+}
+
+static struct i2c_driver rv3029c2_driver = {
+ .driver = {
+ .name = "rtc-rv3029c2",
+ },
+ .probe = rv3029c2_probe,
+ .id_table = rv3029c2_id,
+};
+
+module_i2c_driver(rv3029c2_driver);
+
+MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>");
+MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c
new file mode 100644
index 000000000..6889222f9
--- /dev/null
+++ b/drivers/rtc/rtc-rx4581.c
@@ -0,0 +1,305 @@
+/* drivers/rtc/rtc-rx4581.c
+ *
+ * written by Torben Hohn <torbenh@linutronix.de>
+ *
+ * Based on:
+ * drivers/rtc/rtc-max6902.c
+ *
+ * Copyright (C) 2006 8D Technologies inc.
+ * Copyright (C) 2004 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for MAX6902 spi RTC
+ *
+ * and based on:
+ * drivers/rtc/rtc-rx8581.c
+ *
+ * An I2C driver for the Epson RX8581 RTC
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC)
+ * Copyright 2005-06 Tower Technologies
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/spi/spi.h>
+#include <linux/bcd.h>
+
+#define RX4581_REG_SC 0x00 /* Second in BCD */
+#define RX4581_REG_MN 0x01 /* Minute in BCD */
+#define RX4581_REG_HR 0x02 /* Hour in BCD */
+#define RX4581_REG_DW 0x03 /* Day of Week */
+#define RX4581_REG_DM 0x04 /* Day of Month in BCD */
+#define RX4581_REG_MO 0x05 /* Month in BCD */
+#define RX4581_REG_YR 0x06 /* Year in BCD */
+#define RX4581_REG_RAM 0x07 /* RAM */
+#define RX4581_REG_AMN 0x08 /* Alarm Min in BCD*/
+#define RX4581_REG_AHR 0x09 /* Alarm Hour in BCD */
+#define RX4581_REG_ADM 0x0A
+#define RX4581_REG_ADW 0x0A
+#define RX4581_REG_TMR0 0x0B
+#define RX4581_REG_TMR1 0x0C
+#define RX4581_REG_EXT 0x0D /* Extension Register */
+#define RX4581_REG_FLAG 0x0E /* Flag Register */
+#define RX4581_REG_CTRL 0x0F /* Control Register */
+
+
+/* Flag Register bit definitions */
+#define RX4581_FLAG_UF 0x20 /* Update */
+#define RX4581_FLAG_TF 0x10 /* Timer */
+#define RX4581_FLAG_AF 0x08 /* Alarm */
+#define RX4581_FLAG_VLF 0x02 /* Voltage Low */
+
+/* Control Register bit definitions */
+#define RX4581_CTRL_UIE 0x20 /* Update Interrupt Enable */
+#define RX4581_CTRL_TIE 0x10 /* Timer Interrupt Enable */
+#define RX4581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */
+#define RX4581_CTRL_STOP 0x02 /* STOP bit */
+#define RX4581_CTRL_RESET 0x01 /* RESET bit */
+
+static int rx4581_set_reg(struct device *dev, unsigned char address,
+ unsigned char data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ /* high nibble must be '0' to write */
+ buf[0] = address & 0x0f;
+ buf[1] = data;
+
+ return spi_write_then_read(spi, buf, 2, NULL, 0);
+}
+
+static int rx4581_get_reg(struct device *dev, unsigned char address,
+ unsigned char *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+
+ /* Set MSB to indicate read */
+ *data = address | 0x80;
+
+ return spi_write_then_read(spi, data, 1, data, 1);
+}
+
+/*
+ * In the routines that deal directly with the rx8581 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int rx4581_get_datetime(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char date[7];
+ unsigned char data;
+ int err;
+
+ /* First we ensure that the "update flag" is not set, we read the
+ * time and date then re-read the "update flag". If the update flag
+ * has been set, we know that the time has changed during the read so
+ * we repeat the whole process again.
+ */
+ err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data);
+ if (err != 0) {
+ dev_err(dev, "Unable to read device flags\n");
+ return -EIO;
+ }
+
+ do {
+ /* If update flag set, clear it */
+ if (data & RX4581_FLAG_UF) {
+ err = rx4581_set_reg(dev,
+ RX4581_REG_FLAG, (data & ~RX4581_FLAG_UF));
+ if (err != 0) {
+ dev_err(dev, "Unable to write device "
+ "flags\n");
+ return -EIO;
+ }
+ }
+
+ /* Now read time and date */
+ date[0] = 0x80;
+ err = spi_write_then_read(spi, date, 1, date, 7);
+ if (err < 0) {
+ dev_err(dev, "Unable to read date\n");
+ return -EIO;
+ }
+
+ /* Check flag register */
+ err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data);
+ if (err != 0) {
+ dev_err(dev, "Unable to read device flags\n");
+ return -EIO;
+ }
+ } while (data & RX4581_FLAG_UF);
+
+ if (data & RX4581_FLAG_VLF)
+ dev_info(dev,
+ "low voltage detected, date/time is not reliable.\n");
+
+ dev_dbg(dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "wday=%02x, mday=%02x, mon=%02x, year=%02x\n",
+ __func__,
+ date[0], date[1], date[2], date[3], date[4], date[5], date[6]);
+
+ tm->tm_sec = bcd2bin(date[RX4581_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(date[RX4581_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(date[RX4581_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_wday = ilog2(date[RX4581_REG_DW] & 0x7F);
+ tm->tm_mday = bcd2bin(date[RX4581_REG_DM] & 0x3F);
+ tm->tm_mon = bcd2bin(date[RX4581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(date[RX4581_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+
+
+ dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ err = rtc_valid_tm(tm);
+ if (err < 0)
+ dev_err(dev, "retrieved date/time is not valid.\n");
+
+ return err;
+}
+
+static int rx4581_set_datetime(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int err;
+ unsigned char buf[8], data;
+
+ dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ buf[0] = 0x00;
+ /* hours, minutes and seconds */
+ buf[RX4581_REG_SC+1] = bin2bcd(tm->tm_sec);
+ buf[RX4581_REG_MN+1] = bin2bcd(tm->tm_min);
+ buf[RX4581_REG_HR+1] = bin2bcd(tm->tm_hour);
+
+ buf[RX4581_REG_DM+1] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[RX4581_REG_MO+1] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[RX4581_REG_YR+1] = bin2bcd(tm->tm_year % 100);
+ buf[RX4581_REG_DW+1] = (0x1 << tm->tm_wday);
+
+ /* Stop the clock */
+ err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data);
+ if (err != 0) {
+ dev_err(dev, "Unable to read control register\n");
+ return -EIO;
+ }
+
+ err = rx4581_set_reg(dev, RX4581_REG_CTRL,
+ (data | RX4581_CTRL_STOP));
+ if (err != 0) {
+ dev_err(dev, "Unable to write control register\n");
+ return -EIO;
+ }
+
+ /* write register's data */
+ err = spi_write_then_read(spi, buf, 8, NULL, 0);
+ if (err != 0) {
+ dev_err(dev, "Unable to write to date registers\n");
+ return -EIO;
+ }
+
+ /* get VLF and clear it */
+ err = rx4581_get_reg(dev, RX4581_REG_FLAG, &data);
+ if (err != 0) {
+ dev_err(dev, "Unable to read flag register\n");
+ return -EIO;
+ }
+
+ err = rx4581_set_reg(dev, RX4581_REG_FLAG,
+ (data & ~(RX4581_FLAG_VLF)));
+ if (err != 0) {
+ dev_err(dev, "Unable to write flag register\n");
+ return -EIO;
+ }
+
+ /* Restart the clock */
+ err = rx4581_get_reg(dev, RX4581_REG_CTRL, &data);
+ if (err != 0) {
+ dev_err(dev, "Unable to read control register\n");
+ return -EIO;
+ }
+
+ err = rx4581_set_reg(dev, RX4581_REG_CTRL,
+ (data & ~(RX4581_CTRL_STOP)));
+ if (err != 0) {
+ dev_err(dev, "Unable to write control register\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static const struct rtc_class_ops rx4581_rtc_ops = {
+ .read_time = rx4581_get_datetime,
+ .set_time = rx4581_set_datetime,
+};
+
+static int rx4581_probe(struct spi_device *spi)
+{
+ struct rtc_device *rtc;
+ unsigned char tmp;
+ int res;
+
+ res = rx4581_get_reg(&spi->dev, RX4581_REG_SC, &tmp);
+ if (res != 0)
+ return res;
+
+ rtc = devm_rtc_device_register(&spi->dev, "rx4581",
+ &rx4581_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ spi_set_drvdata(spi, rtc);
+ return 0;
+}
+
+static const struct spi_device_id rx4581_id[] = {
+ { "rx4581", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, rx4581_id);
+
+static struct spi_driver rx4581_driver = {
+ .driver = {
+ .name = "rtc-rx4581",
+ .owner = THIS_MODULE,
+ },
+ .probe = rx4581_probe,
+ .id_table = rx4581_id,
+};
+
+module_spi_driver(rx4581_driver);
+
+MODULE_DESCRIPTION("rx4581 spi RTC driver");
+MODULE_AUTHOR("Torben Hohn");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:rtc-rx4581");
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
new file mode 100644
index 000000000..e6298e02b
--- /dev/null
+++ b/drivers/rtc/rtc-rx8025.c
@@ -0,0 +1,642 @@
+/*
+ * Driver for Epson's RTC module RX-8025 SA/NB
+ *
+ * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ * Copyright (C) 2005 by Digi International Inc.
+ * All rights reserved.
+ *
+ * Modified by fengjh at rising.com.cn
+ * <http://lists.lm-sensors.org/mailman/listinfo/lm-sensors>
+ * 2006.11
+ *
+ * Code cleanup by Sergei Poselenov, <sposelenov@emcraft.com>
+ * Converted to new style by Wolfgang Grandegger <wg@grandegger.com>
+ * Alarm and periodic interrupt added by Dmitry Rakhchev <rda@emcraft.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/bcd.h>
+#include <linux/i2c.h>
+#include <linux/list.h>
+#include <linux/rtc.h>
+
+/* Register definitions */
+#define RX8025_REG_SEC 0x00
+#define RX8025_REG_MIN 0x01
+#define RX8025_REG_HOUR 0x02
+#define RX8025_REG_WDAY 0x03
+#define RX8025_REG_MDAY 0x04
+#define RX8025_REG_MONTH 0x05
+#define RX8025_REG_YEAR 0x06
+#define RX8025_REG_DIGOFF 0x07
+#define RX8025_REG_ALWMIN 0x08
+#define RX8025_REG_ALWHOUR 0x09
+#define RX8025_REG_ALWWDAY 0x0a
+#define RX8025_REG_ALDMIN 0x0b
+#define RX8025_REG_ALDHOUR 0x0c
+/* 0x0d is reserved */
+#define RX8025_REG_CTRL1 0x0e
+#define RX8025_REG_CTRL2 0x0f
+
+#define RX8025_BIT_CTRL1_CT (7 << 0)
+/* 1 Hz periodic level irq */
+#define RX8025_BIT_CTRL1_CT_1HZ 4
+#define RX8025_BIT_CTRL1_TEST (1 << 3)
+#define RX8025_BIT_CTRL1_1224 (1 << 5)
+#define RX8025_BIT_CTRL1_DALE (1 << 6)
+#define RX8025_BIT_CTRL1_WALE (1 << 7)
+
+#define RX8025_BIT_CTRL2_DAFG (1 << 0)
+#define RX8025_BIT_CTRL2_WAFG (1 << 1)
+#define RX8025_BIT_CTRL2_CTFG (1 << 2)
+#define RX8025_BIT_CTRL2_PON (1 << 4)
+#define RX8025_BIT_CTRL2_XST (1 << 5)
+#define RX8025_BIT_CTRL2_VDET (1 << 6)
+
+/* Clock precision adjustment */
+#define RX8025_ADJ_RESOLUTION 3050 /* in ppb */
+#define RX8025_ADJ_DATA_MAX 62
+#define RX8025_ADJ_DATA_MIN -62
+
+static const struct i2c_device_id rx8025_id[] = {
+ { "rx8025", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rx8025_id);
+
+struct rx8025_data {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ struct work_struct work;
+ u8 ctrl1;
+ unsigned exiting:1;
+};
+
+static int rx8025_read_reg(struct i2c_client *client, int number, u8 *value)
+{
+ int ret = i2c_smbus_read_byte_data(client, (number << 4) | 0x08);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to read register #%d\n", number);
+ return ret;
+ }
+
+ *value = ret;
+ return 0;
+}
+
+static int rx8025_read_regs(struct i2c_client *client,
+ int number, u8 length, u8 *values)
+{
+ int ret = i2c_smbus_read_i2c_block_data(client, (number << 4) | 0x08,
+ length, values);
+
+ if (ret != length) {
+ dev_err(&client->dev, "Unable to read registers #%d..#%d\n",
+ number, number + length - 1);
+ return ret < 0 ? ret : -EIO;
+ }
+
+ return 0;
+}
+
+static int rx8025_write_reg(struct i2c_client *client, int number, u8 value)
+{
+ int ret = i2c_smbus_write_byte_data(client, number << 4, value);
+
+ if (ret)
+ dev_err(&client->dev, "Unable to write register #%d\n",
+ number);
+
+ return ret;
+}
+
+static int rx8025_write_regs(struct i2c_client *client,
+ int number, u8 length, u8 *values)
+{
+ int ret = i2c_smbus_write_i2c_block_data(client, (number << 4) | 0x08,
+ length, values);
+
+ if (ret)
+ dev_err(&client->dev, "Unable to write registers #%d..#%d\n",
+ number, number + length - 1);
+
+ return ret;
+}
+
+static irqreturn_t rx8025_irq(int irq, void *dev_id)
+{
+ struct i2c_client *client = dev_id;
+ struct rx8025_data *rx8025 = i2c_get_clientdata(client);
+
+ disable_irq_nosync(irq);
+ schedule_work(&rx8025->work);
+ return IRQ_HANDLED;
+}
+
+static void rx8025_work(struct work_struct *work)
+{
+ struct rx8025_data *rx8025 = container_of(work, struct rx8025_data,
+ work);
+ struct i2c_client *client = rx8025->client;
+ struct mutex *lock = &rx8025->rtc->ops_lock;
+ u8 status;
+
+ mutex_lock(lock);
+
+ if (rx8025_read_reg(client, RX8025_REG_CTRL2, &status))
+ goto out;
+
+ if (!(status & RX8025_BIT_CTRL2_XST))
+ dev_warn(&client->dev, "Oscillation stop was detected,"
+ "you may have to readjust the clock\n");
+
+ if (status & RX8025_BIT_CTRL2_CTFG) {
+ /* periodic */
+ status &= ~RX8025_BIT_CTRL2_CTFG;
+ local_irq_disable();
+ rtc_update_irq(rx8025->rtc, 1, RTC_PF | RTC_IRQF);
+ local_irq_enable();
+ }
+
+ if (status & RX8025_BIT_CTRL2_DAFG) {
+ /* alarm */
+ status &= RX8025_BIT_CTRL2_DAFG;
+ if (rx8025_write_reg(client, RX8025_REG_CTRL1,
+ rx8025->ctrl1 & ~RX8025_BIT_CTRL1_DALE))
+ goto out;
+ local_irq_disable();
+ rtc_update_irq(rx8025->rtc, 1, RTC_AF | RTC_IRQF);
+ local_irq_enable();
+ }
+
+ /* acknowledge IRQ */
+ rx8025_write_reg(client, RX8025_REG_CTRL2,
+ status | RX8025_BIT_CTRL2_XST);
+
+out:
+ if (!rx8025->exiting)
+ enable_irq(client->irq);
+
+ mutex_unlock(lock);
+}
+
+static int rx8025_get_time(struct device *dev, struct rtc_time *dt)
+{
+ struct rx8025_data *rx8025 = dev_get_drvdata(dev);
+ u8 date[7];
+ int err;
+
+ err = rx8025_read_regs(rx8025->client, RX8025_REG_SEC, 7, date);
+ if (err)
+ return err;
+
+ dev_dbg(dev, "%s: read 0x%02x 0x%02x "
+ "0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__,
+ date[0], date[1], date[2], date[3], date[4],
+ date[5], date[6]);
+
+ dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f);
+ dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f);
+ if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
+ dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f);
+ else
+ dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12
+ + (date[RX8025_REG_HOUR] & 0x20 ? 12 : 0);
+
+ dt->tm_mday = bcd2bin(date[RX8025_REG_MDAY] & 0x3f);
+ dt->tm_mon = bcd2bin(date[RX8025_REG_MONTH] & 0x1f) - 1;
+ dt->tm_year = bcd2bin(date[RX8025_REG_YEAR]);
+
+ if (dt->tm_year < 70)
+ dt->tm_year += 100;
+
+ dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__,
+ dt->tm_sec, dt->tm_min, dt->tm_hour,
+ dt->tm_mday, dt->tm_mon, dt->tm_year);
+
+ return rtc_valid_tm(dt);
+}
+
+static int rx8025_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct rx8025_data *rx8025 = dev_get_drvdata(dev);
+ u8 date[7];
+
+ /*
+ * BUG: The HW assumes every year that is a multiple of 4 to be a leap
+ * year. Next time this is wrong is 2100, which will not be a leap
+ * year.
+ */
+
+ /*
+ * Here the read-only bits are written as "0". I'm not sure if that
+ * is sound.
+ */
+ date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec);
+ date[RX8025_REG_MIN] = bin2bcd(dt->tm_min);
+ if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
+ date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour);
+ else
+ date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0)
+ | bin2bcd((dt->tm_hour + 11) % 12 + 1);
+
+ date[RX8025_REG_WDAY] = bin2bcd(dt->tm_wday);
+ date[RX8025_REG_MDAY] = bin2bcd(dt->tm_mday);
+ date[RX8025_REG_MONTH] = bin2bcd(dt->tm_mon + 1);
+ date[RX8025_REG_YEAR] = bin2bcd(dt->tm_year % 100);
+
+ dev_dbg(dev,
+ "%s: write 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ __func__,
+ date[0], date[1], date[2], date[3], date[4], date[5], date[6]);
+
+ return rx8025_write_regs(rx8025->client, RX8025_REG_SEC, 7, date);
+}
+
+static int rx8025_init_client(struct i2c_client *client, int *need_reset)
+{
+ struct rx8025_data *rx8025 = i2c_get_clientdata(client);
+ u8 ctrl[2], ctrl2;
+ int need_clear = 0;
+ int err;
+
+ err = rx8025_read_regs(rx8025->client, RX8025_REG_CTRL1, 2, ctrl);
+ if (err)
+ goto out;
+
+ /* Keep test bit zero ! */
+ rx8025->ctrl1 = ctrl[0] & ~RX8025_BIT_CTRL1_TEST;
+
+ if (ctrl[1] & RX8025_BIT_CTRL2_PON) {
+ dev_warn(&client->dev, "power-on reset was detected, "
+ "you may have to readjust the clock\n");
+ *need_reset = 1;
+ }
+
+ if (ctrl[1] & RX8025_BIT_CTRL2_VDET) {
+ dev_warn(&client->dev, "a power voltage drop was detected, "
+ "you may have to readjust the clock\n");
+ *need_reset = 1;
+ }
+
+ if (!(ctrl[1] & RX8025_BIT_CTRL2_XST)) {
+ dev_warn(&client->dev, "Oscillation stop was detected,"
+ "you may have to readjust the clock\n");
+ *need_reset = 1;
+ }
+
+ if (ctrl[1] & (RX8025_BIT_CTRL2_DAFG | RX8025_BIT_CTRL2_WAFG)) {
+ dev_warn(&client->dev, "Alarm was detected\n");
+ need_clear = 1;
+ }
+
+ if (!(ctrl[1] & RX8025_BIT_CTRL2_CTFG))
+ need_clear = 1;
+
+ if (*need_reset || need_clear) {
+ ctrl2 = ctrl[0];
+ ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET |
+ RX8025_BIT_CTRL2_CTFG | RX8025_BIT_CTRL2_WAFG |
+ RX8025_BIT_CTRL2_DAFG);
+ ctrl2 |= RX8025_BIT_CTRL2_XST;
+
+ err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2);
+ }
+out:
+ return err;
+}
+
+/* Alarm support */
+static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct rx8025_data *rx8025 = dev_get_drvdata(dev);
+ struct i2c_client *client = rx8025->client;
+ u8 ctrl2, ald[2];
+ int err;
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ err = rx8025_read_regs(client, RX8025_REG_ALDMIN, 2, ald);
+ if (err)
+ return err;
+
+ err = rx8025_read_reg(client, RX8025_REG_CTRL2, &ctrl2);
+ if (err)
+ return err;
+
+ dev_dbg(dev, "%s: read alarm 0x%02x 0x%02x ctrl2 %02x\n",
+ __func__, ald[0], ald[1], ctrl2);
+
+ /* Hardware alarms precision is 1 minute! */
+ t->time.tm_sec = 0;
+ t->time.tm_min = bcd2bin(ald[0] & 0x7f);
+ if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
+ t->time.tm_hour = bcd2bin(ald[1] & 0x3f);
+ else
+ t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12
+ + (ald[1] & 0x20 ? 12 : 0);
+
+ t->time.tm_wday = -1;
+ t->time.tm_mday = -1;
+ t->time.tm_mon = -1;
+ t->time.tm_year = -1;
+
+ dev_dbg(dev, "%s: date: %ds %dm %dh %dmd %dm %dy\n",
+ __func__,
+ t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
+ t->time.tm_mday, t->time.tm_mon, t->time.tm_year);
+ t->enabled = !!(rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE);
+ t->pending = (ctrl2 & RX8025_BIT_CTRL2_DAFG) && t->enabled;
+
+ return err;
+}
+
+static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct rx8025_data *rx8025 = dev_get_drvdata(dev);
+ u8 ald[2];
+ int err;
+
+ if (client->irq <= 0)
+ return -EINVAL;
+
+ /* Hardware alarm precision is 1 minute! */
+ ald[0] = bin2bcd(t->time.tm_min);
+ if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
+ ald[1] = bin2bcd(t->time.tm_hour);
+ else
+ ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0)
+ | bin2bcd((t->time.tm_hour + 11) % 12 + 1);
+
+ dev_dbg(dev, "%s: write 0x%02x 0x%02x\n", __func__, ald[0], ald[1]);
+
+ if (rx8025->ctrl1 & RX8025_BIT_CTRL1_DALE) {
+ rx8025->ctrl1 &= ~RX8025_BIT_CTRL1_DALE;
+ err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1,
+ rx8025->ctrl1);
+ if (err)
+ return err;
+ }
+ err = rx8025_write_regs(rx8025->client, RX8025_REG_ALDMIN, 2, ald);
+ if (err)
+ return err;
+
+ if (t->enabled) {
+ rx8025->ctrl1 |= RX8025_BIT_CTRL1_DALE;
+ err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1,
+ rx8025->ctrl1);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rx8025_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct rx8025_data *rx8025 = dev_get_drvdata(dev);
+ u8 ctrl1;
+ int err;
+
+ ctrl1 = rx8025->ctrl1;
+ if (enabled)
+ ctrl1 |= RX8025_BIT_CTRL1_DALE;
+ else
+ ctrl1 &= ~RX8025_BIT_CTRL1_DALE;
+
+ if (ctrl1 != rx8025->ctrl1) {
+ rx8025->ctrl1 = ctrl1;
+ err = rx8025_write_reg(rx8025->client, RX8025_REG_CTRL1,
+ rx8025->ctrl1);
+ if (err)
+ return err;
+ }
+ return 0;
+}
+
+static struct rtc_class_ops rx8025_rtc_ops = {
+ .read_time = rx8025_get_time,
+ .set_time = rx8025_set_time,
+ .read_alarm = rx8025_read_alarm,
+ .set_alarm = rx8025_set_alarm,
+ .alarm_irq_enable = rx8025_alarm_irq_enable,
+};
+
+/*
+ * Clock precision adjustment support
+ *
+ * According to the RX8025 SA/NB application manual the frequency and
+ * temperature characteristics can be approximated using the following
+ * equation:
+ *
+ * df = a * (ut - t)**2
+ *
+ * df: Frequency deviation in any temperature
+ * a : Coefficient = (-35 +-5) * 10**-9
+ * ut: Ultimate temperature in degree = +25 +-5 degree
+ * t : Any temperature in degree
+ *
+ * Note that the clock adjustment in ppb must be entered (which is
+ * the negative value of the deviation).
+ */
+static int rx8025_get_clock_adjust(struct device *dev, int *adj)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 digoff;
+ int err;
+
+ err = rx8025_read_reg(client, RX8025_REG_DIGOFF, &digoff);
+ if (err)
+ return err;
+
+ *adj = digoff >= 64 ? digoff - 128 : digoff;
+ if (*adj > 0)
+ (*adj)--;
+ *adj *= -RX8025_ADJ_RESOLUTION;
+
+ return 0;
+}
+
+static int rx8025_set_clock_adjust(struct device *dev, int adj)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ u8 digoff;
+ int err;
+
+ adj /= -RX8025_ADJ_RESOLUTION;
+ if (adj > RX8025_ADJ_DATA_MAX)
+ adj = RX8025_ADJ_DATA_MAX;
+ else if (adj < RX8025_ADJ_DATA_MIN)
+ adj = RX8025_ADJ_DATA_MIN;
+ else if (adj > 0)
+ adj++;
+ else if (adj < 0)
+ adj += 128;
+ digoff = adj;
+
+ err = rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff);
+ if (err)
+ return err;
+
+ dev_dbg(dev, "%s: write 0x%02x\n", __func__, digoff);
+
+ return 0;
+}
+
+static ssize_t rx8025_sysfs_show_clock_adjust(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int err, adj;
+
+ err = rx8025_get_clock_adjust(dev, &adj);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d\n", adj);
+}
+
+static ssize_t rx8025_sysfs_store_clock_adjust(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int adj, err;
+
+ if (sscanf(buf, "%i", &adj) != 1)
+ return -EINVAL;
+
+ err = rx8025_set_clock_adjust(dev, adj);
+
+ return err ? err : count;
+}
+
+static DEVICE_ATTR(clock_adjust_ppb, S_IRUGO | S_IWUSR,
+ rx8025_sysfs_show_clock_adjust,
+ rx8025_sysfs_store_clock_adjust);
+
+static int rx8025_sysfs_register(struct device *dev)
+{
+ return device_create_file(dev, &dev_attr_clock_adjust_ppb);
+}
+
+static void rx8025_sysfs_unregister(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_clock_adjust_ppb);
+}
+
+static int rx8025_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct rx8025_data *rx8025;
+ int err, need_reset = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
+ | I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ dev_err(&adapter->dev,
+ "doesn't support required functionality\n");
+ err = -EIO;
+ goto errout;
+ }
+
+ rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL);
+ if (!rx8025) {
+ err = -ENOMEM;
+ goto errout;
+ }
+
+ rx8025->client = client;
+ i2c_set_clientdata(client, rx8025);
+ INIT_WORK(&rx8025->work, rx8025_work);
+
+ err = rx8025_init_client(client, &need_reset);
+ if (err)
+ goto errout;
+
+ if (need_reset) {
+ struct rtc_time tm;
+ dev_info(&client->dev,
+ "bad conditions detected, resetting date\n");
+ rtc_time_to_tm(0, &tm); /* 1970/1/1 */
+ rx8025_set_time(&client->dev, &tm);
+ }
+
+ rx8025->rtc = devm_rtc_device_register(&client->dev, client->name,
+ &rx8025_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rx8025->rtc)) {
+ err = PTR_ERR(rx8025->rtc);
+ dev_err(&client->dev, "unable to register the class device\n");
+ goto errout;
+ }
+
+ if (client->irq > 0) {
+ dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
+ err = request_irq(client->irq, rx8025_irq,
+ 0, "rx8025", client);
+ if (err) {
+ dev_err(&client->dev, "unable to request IRQ\n");
+ goto errout;
+ }
+ }
+
+ rx8025->rtc->irq_freq = 1;
+ rx8025->rtc->max_user_freq = 1;
+
+ err = rx8025_sysfs_register(&client->dev);
+ if (err)
+ goto errout_irq;
+
+ return 0;
+
+errout_irq:
+ if (client->irq > 0)
+ free_irq(client->irq, client);
+
+errout:
+ dev_err(&adapter->dev, "probing for rx8025 failed\n");
+ return err;
+}
+
+static int rx8025_remove(struct i2c_client *client)
+{
+ struct rx8025_data *rx8025 = i2c_get_clientdata(client);
+ struct mutex *lock = &rx8025->rtc->ops_lock;
+
+ if (client->irq > 0) {
+ mutex_lock(lock);
+ rx8025->exiting = 1;
+ mutex_unlock(lock);
+
+ free_irq(client->irq, client);
+ cancel_work_sync(&rx8025->work);
+ }
+
+ rx8025_sysfs_unregister(&client->dev);
+ return 0;
+}
+
+static struct i2c_driver rx8025_driver = {
+ .driver = {
+ .name = "rtc-rx8025",
+ .owner = THIS_MODULE,
+ },
+ .probe = rx8025_probe,
+ .remove = rx8025_remove,
+ .id_table = rx8025_id,
+};
+
+module_i2c_driver(rx8025_driver);
+
+MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
+MODULE_DESCRIPTION("RX-8025 SA/NB RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c
new file mode 100644
index 000000000..de8d9c427
--- /dev/null
+++ b/drivers/rtc/rtc-rx8581.c
@@ -0,0 +1,329 @@
+/*
+ * An I2C driver for the Epson RX8581 RTC
+ *
+ * Author: Martyn Welch <martyn.welch@ge.com>
+ * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC)
+ * Copyright 2005-06 Tower Technologies
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/log2.h>
+
+#define DRV_VERSION "0.1"
+
+#define RX8581_REG_SC 0x00 /* Second in BCD */
+#define RX8581_REG_MN 0x01 /* Minute in BCD */
+#define RX8581_REG_HR 0x02 /* Hour in BCD */
+#define RX8581_REG_DW 0x03 /* Day of Week */
+#define RX8581_REG_DM 0x04 /* Day of Month in BCD */
+#define RX8581_REG_MO 0x05 /* Month in BCD */
+#define RX8581_REG_YR 0x06 /* Year in BCD */
+#define RX8581_REG_RAM 0x07 /* RAM */
+#define RX8581_REG_AMN 0x08 /* Alarm Min in BCD*/
+#define RX8581_REG_AHR 0x09 /* Alarm Hour in BCD */
+#define RX8581_REG_ADM 0x0A
+#define RX8581_REG_ADW 0x0A
+#define RX8581_REG_TMR0 0x0B
+#define RX8581_REG_TMR1 0x0C
+#define RX8581_REG_EXT 0x0D /* Extension Register */
+#define RX8581_REG_FLAG 0x0E /* Flag Register */
+#define RX8581_REG_CTRL 0x0F /* Control Register */
+
+
+/* Flag Register bit definitions */
+#define RX8581_FLAG_UF 0x20 /* Update */
+#define RX8581_FLAG_TF 0x10 /* Timer */
+#define RX8581_FLAG_AF 0x08 /* Alarm */
+#define RX8581_FLAG_VLF 0x02 /* Voltage Low */
+
+/* Control Register bit definitions */
+#define RX8581_CTRL_UIE 0x20 /* Update Interrupt Enable */
+#define RX8581_CTRL_TIE 0x10 /* Timer Interrupt Enable */
+#define RX8581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */
+#define RX8581_CTRL_STOP 0x02 /* STOP bit */
+#define RX8581_CTRL_RESET 0x01 /* RESET bit */
+
+struct rx8581 {
+ struct i2c_client *client;
+ struct rtc_device *rtc;
+ s32 (*read_block_data)(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values);
+ s32 (*write_block_data)(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values);
+};
+
+static struct i2c_driver rx8581_driver;
+
+static int rx8581_read_block_data(const struct i2c_client *client, u8 command,
+ u8 length, u8 *values)
+{
+ s32 i, data;
+
+ for (i = 0; i < length; i++) {
+ data = i2c_smbus_read_byte_data(client, command + i);
+ if (data < 0)
+ return data;
+ values[i] = data;
+ }
+ return i;
+}
+
+static int rx8581_write_block_data(const struct i2c_client *client, u8 command,
+ u8 length, const u8 *values)
+{
+ s32 i, ret;
+
+ for (i = 0; i < length; i++) {
+ ret = i2c_smbus_write_byte_data(client, command + i,
+ values[i]);
+ if (ret < 0)
+ return ret;
+ }
+ return length;
+}
+
+/*
+ * In the routines that deal directly with the rx8581 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
+ */
+static int rx8581_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ unsigned char date[7];
+ int data, err;
+ struct rx8581 *rx8581 = i2c_get_clientdata(client);
+
+ /* First we ensure that the "update flag" is not set, we read the
+ * time and date then re-read the "update flag". If the update flag
+ * has been set, we know that the time has changed during the read so
+ * we repeat the whole process again.
+ */
+ data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read device flags\n");
+ return -EIO;
+ }
+
+ do {
+ /* If update flag set, clear it */
+ if (data & RX8581_FLAG_UF) {
+ err = i2c_smbus_write_byte_data(client,
+ RX8581_REG_FLAG, (data & ~RX8581_FLAG_UF));
+ if (err != 0) {
+ dev_err(&client->dev, "Unable to write device flags\n");
+ return -EIO;
+ }
+ }
+
+ /* Now read time and date */
+ err = rx8581->read_block_data(client, RX8581_REG_SC,
+ 7, date);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to read date\n");
+ return -EIO;
+ }
+
+ /* Check flag register */
+ data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read device flags\n");
+ return -EIO;
+ }
+ } while (data & RX8581_FLAG_UF);
+
+ if (data & RX8581_FLAG_VLF)
+ dev_info(&client->dev,
+ "low voltage detected, date/time is not reliable.\n");
+
+ dev_dbg(&client->dev,
+ "%s: raw data is sec=%02x, min=%02x, hr=%02x, "
+ "wday=%02x, mday=%02x, mon=%02x, year=%02x\n",
+ __func__,
+ date[0], date[1], date[2], date[3], date[4], date[5], date[6]);
+
+ tm->tm_sec = bcd2bin(date[RX8581_REG_SC] & 0x7F);
+ tm->tm_min = bcd2bin(date[RX8581_REG_MN] & 0x7F);
+ tm->tm_hour = bcd2bin(date[RX8581_REG_HR] & 0x3F); /* rtc hr 0-23 */
+ tm->tm_wday = ilog2(date[RX8581_REG_DW] & 0x7F);
+ tm->tm_mday = bcd2bin(date[RX8581_REG_DM] & 0x3F);
+ tm->tm_mon = bcd2bin(date[RX8581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
+ tm->tm_year = bcd2bin(date[RX8581_REG_YR]);
+ if (tm->tm_year < 70)
+ tm->tm_year += 100; /* assume we are in 1970...2069 */
+
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ err = rtc_valid_tm(tm);
+ if (err < 0)
+ dev_err(&client->dev, "retrieved date/time is not valid.\n");
+
+ return err;
+}
+
+static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ int data, err;
+ unsigned char buf[7];
+ struct rx8581 *rx8581 = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ /* hours, minutes and seconds */
+ buf[RX8581_REG_SC] = bin2bcd(tm->tm_sec);
+ buf[RX8581_REG_MN] = bin2bcd(tm->tm_min);
+ buf[RX8581_REG_HR] = bin2bcd(tm->tm_hour);
+
+ buf[RX8581_REG_DM] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[RX8581_REG_MO] = bin2bcd(tm->tm_mon + 1);
+
+ /* year and century */
+ buf[RX8581_REG_YR] = bin2bcd(tm->tm_year % 100);
+ buf[RX8581_REG_DW] = (0x1 << tm->tm_wday);
+
+ /* Stop the clock */
+ data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read control register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, RX8581_REG_CTRL,
+ (data | RX8581_CTRL_STOP));
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write control register\n");
+ return -EIO;
+ }
+
+ /* write register's data */
+ err = rx8581->write_block_data(client, RX8581_REG_SC, 7, buf);
+ if (err < 0) {
+ dev_err(&client->dev, "Unable to write to date registers\n");
+ return -EIO;
+ }
+
+ /* get VLF and clear it */
+ data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read flag register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG,
+ (data & ~(RX8581_FLAG_VLF)));
+ if (err != 0) {
+ dev_err(&client->dev, "Unable to write flag register\n");
+ return -EIO;
+ }
+
+ /* Restart the clock */
+ data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL);
+ if (data < 0) {
+ dev_err(&client->dev, "Unable to read control register\n");
+ return -EIO;
+ }
+
+ err = i2c_smbus_write_byte_data(client, RX8581_REG_CTRL,
+ (data & ~(RX8581_CTRL_STOP)));
+ if (err != 0) {
+ dev_err(&client->dev, "Unable to write control register\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int rx8581_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return rx8581_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int rx8581_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return rx8581_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops rx8581_rtc_ops = {
+ .read_time = rx8581_rtc_read_time,
+ .set_time = rx8581_rtc_set_time,
+};
+
+static int rx8581_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rx8581 *rx8581;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)
+ && !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EIO;
+
+ rx8581 = devm_kzalloc(&client->dev, sizeof(struct rx8581), GFP_KERNEL);
+ if (!rx8581)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, rx8581);
+ rx8581->client = client;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ rx8581->read_block_data = i2c_smbus_read_i2c_block_data;
+ rx8581->write_block_data = i2c_smbus_write_i2c_block_data;
+ } else {
+ rx8581->read_block_data = rx8581_read_block_data;
+ rx8581->write_block_data = rx8581_write_block_data;
+ }
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ rx8581->rtc = devm_rtc_device_register(&client->dev,
+ rx8581_driver.driver.name, &rx8581_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rx8581->rtc)) {
+ dev_err(&client->dev,
+ "unable to register the class device\n");
+ return PTR_ERR(rx8581->rtc);
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id rx8581_id[] = {
+ { "rx8581", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rx8581_id);
+
+static struct i2c_driver rx8581_driver = {
+ .driver = {
+ .name = "rtc-rx8581",
+ .owner = THIS_MODULE,
+ },
+ .probe = rx8581_probe,
+ .id_table = rx8581_id,
+};
+
+module_i2c_driver(rx8581_driver);
+
+MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
+MODULE_DESCRIPTION("Epson RX-8581 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
new file mode 100644
index 000000000..f40afdd0e
--- /dev/null
+++ b/drivers/rtc/rtc-s35390a.c
@@ -0,0 +1,434 @@
+/*
+ * Seiko Instruments S-35390A RTC Driver
+ *
+ * Copyright (c) 2007 Byron Bradley
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bitrev.h>
+#include <linux/bcd.h>
+#include <linux/slab.h>
+
+#define S35390A_CMD_STATUS1 0
+#define S35390A_CMD_STATUS2 1
+#define S35390A_CMD_TIME1 2
+#define S35390A_CMD_TIME2 3
+#define S35390A_CMD_INT2_REG1 5
+
+#define S35390A_BYTE_YEAR 0
+#define S35390A_BYTE_MONTH 1
+#define S35390A_BYTE_DAY 2
+#define S35390A_BYTE_WDAY 3
+#define S35390A_BYTE_HOURS 4
+#define S35390A_BYTE_MINS 5
+#define S35390A_BYTE_SECS 6
+
+#define S35390A_ALRM_BYTE_WDAY 0
+#define S35390A_ALRM_BYTE_HOURS 1
+#define S35390A_ALRM_BYTE_MINS 2
+
+#define S35390A_FLAG_POC 0x01
+#define S35390A_FLAG_BLD 0x02
+#define S35390A_FLAG_24H 0x40
+#define S35390A_FLAG_RESET 0x80
+#define S35390A_FLAG_TEST 0x01
+
+#define S35390A_INT2_MODE_MASK 0xF0
+
+#define S35390A_INT2_MODE_NOINTR 0x00
+#define S35390A_INT2_MODE_FREQ 0x10
+#define S35390A_INT2_MODE_ALARM 0x40
+#define S35390A_INT2_MODE_PMIN_EDG 0x20
+
+static const struct i2c_device_id s35390a_id[] = {
+ { "s35390a", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, s35390a_id);
+
+struct s35390a {
+ struct i2c_client *client[8];
+ struct rtc_device *rtc;
+ int twentyfourhour;
+};
+
+static int s35390a_set_reg(struct s35390a *s35390a, int reg, char *buf, int len)
+{
+ struct i2c_client *client = s35390a->client[reg];
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .len = len,
+ .buf = buf
+ },
+ };
+
+ if ((i2c_transfer(client->adapter, msg, 1)) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
+{
+ struct i2c_client *client = s35390a->client[reg];
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf
+ },
+ };
+
+ if ((i2c_transfer(client->adapter, msg, 1)) != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int s35390a_reset(struct s35390a *s35390a)
+{
+ char buf[1];
+
+ if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf)) < 0)
+ return -EIO;
+
+ if (!(buf[0] & (S35390A_FLAG_POC | S35390A_FLAG_BLD)))
+ return 0;
+
+ buf[0] |= (S35390A_FLAG_RESET | S35390A_FLAG_24H);
+ buf[0] &= 0xf0;
+ return s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
+}
+
+static int s35390a_disable_test_mode(struct s35390a *s35390a)
+{
+ char buf[1];
+
+ if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)) < 0)
+ return -EIO;
+
+ if (!(buf[0] & S35390A_FLAG_TEST))
+ return 0;
+
+ buf[0] &= ~S35390A_FLAG_TEST;
+ return s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf));
+}
+
+static char s35390a_hr2reg(struct s35390a *s35390a, int hour)
+{
+ if (s35390a->twentyfourhour)
+ return bin2bcd(hour);
+
+ if (hour < 12)
+ return bin2bcd(hour);
+
+ return 0x40 | bin2bcd(hour - 12);
+}
+
+static int s35390a_reg2hr(struct s35390a *s35390a, char reg)
+{
+ unsigned hour;
+
+ if (s35390a->twentyfourhour)
+ return bcd2bin(reg & 0x3f);
+
+ hour = bcd2bin(reg & 0x3f);
+ if (reg & 0x40)
+ hour += 12;
+
+ return hour;
+}
+
+static int s35390a_set_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct s35390a *s35390a = i2c_get_clientdata(client);
+ int i, err;
+ char buf[7];
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, "
+ "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,
+ tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
+ tm->tm_wday);
+
+ buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100);
+ buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1);
+ buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday);
+ buf[S35390A_BYTE_WDAY] = bin2bcd(tm->tm_wday);
+ buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour);
+ buf[S35390A_BYTE_MINS] = bin2bcd(tm->tm_min);
+ buf[S35390A_BYTE_SECS] = bin2bcd(tm->tm_sec);
+
+ /* This chip expects the bits of each byte to be in reverse order */
+ for (i = 0; i < 7; ++i)
+ buf[i] = bitrev8(buf[i]);
+
+ err = s35390a_set_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
+
+ return err;
+}
+
+static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
+{
+ struct s35390a *s35390a = i2c_get_clientdata(client);
+ char buf[7];
+ int i, err;
+
+ err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
+ if (err < 0)
+ return err;
+
+ /* This chip returns the bits of each byte in reverse order */
+ for (i = 0; i < 7; ++i)
+ buf[i] = bitrev8(buf[i]);
+
+ tm->tm_sec = bcd2bin(buf[S35390A_BYTE_SECS]);
+ tm->tm_min = bcd2bin(buf[S35390A_BYTE_MINS]);
+ tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]);
+ tm->tm_wday = bcd2bin(buf[S35390A_BYTE_WDAY]);
+ tm->tm_mday = bcd2bin(buf[S35390A_BYTE_DAY]);
+ tm->tm_mon = bcd2bin(buf[S35390A_BYTE_MONTH]) - 1;
+ tm->tm_year = bcd2bin(buf[S35390A_BYTE_YEAR]) + 100;
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, "
+ "mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec,
+ tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
+ tm->tm_wday);
+
+ return rtc_valid_tm(tm);
+}
+
+static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
+{
+ struct s35390a *s35390a = i2c_get_clientdata(client);
+ char buf[3], sts = 0;
+ int err, i;
+
+ dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\
+ "mon=%d, year=%d, wday=%d\n", __func__, alm->time.tm_sec,
+ alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday,
+ alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday);
+
+ /* disable interrupt */
+ err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
+ if (err < 0)
+ return err;
+
+ /* clear pending interrupt, if any */
+ err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts));
+ if (err < 0)
+ return err;
+
+ if (alm->enabled)
+ sts = S35390A_INT2_MODE_ALARM;
+ else
+ sts = S35390A_INT2_MODE_NOINTR;
+
+ /* This chip expects the bits of each byte to be in reverse order */
+ sts = bitrev8(sts);
+
+ /* set interupt mode*/
+ err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
+ if (err < 0)
+ return err;
+
+ if (alm->time.tm_wday != -1)
+ buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80;
+
+ buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
+ alm->time.tm_hour) | 0x80;
+ buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80;
+
+ if (alm->time.tm_hour >= 12)
+ buf[S35390A_ALRM_BYTE_HOURS] |= 0x40;
+
+ for (i = 0; i < 3; ++i)
+ buf[i] = bitrev8(buf[i]);
+
+ err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf,
+ sizeof(buf));
+
+ return err;
+}
+
+static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
+{
+ struct s35390a *s35390a = i2c_get_clientdata(client);
+ char buf[3], sts;
+ int i, err;
+
+ err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
+ if (err < 0)
+ return err;
+
+ if (bitrev8(sts) != S35390A_INT2_MODE_ALARM)
+ return -EINVAL;
+
+ err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
+ if (err < 0)
+ return err;
+
+ /* This chip returns the bits of each byte in reverse order */
+ for (i = 0; i < 3; ++i) {
+ buf[i] = bitrev8(buf[i]);
+ buf[i] &= ~0x80;
+ }
+
+ alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]);
+ alm->time.tm_hour = s35390a_reg2hr(s35390a,
+ buf[S35390A_ALRM_BYTE_HOURS]);
+ alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]);
+
+ dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n",
+ __func__, alm->time.tm_min, alm->time.tm_hour,
+ alm->time.tm_wday);
+
+ return 0;
+}
+
+static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ return s35390a_read_alarm(to_i2c_client(dev), alm);
+}
+
+static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ return s35390a_set_alarm(to_i2c_client(dev), alm);
+}
+
+static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return s35390a_get_datetime(to_i2c_client(dev), tm);
+}
+
+static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return s35390a_set_datetime(to_i2c_client(dev), tm);
+}
+
+static const struct rtc_class_ops s35390a_rtc_ops = {
+ .read_time = s35390a_rtc_read_time,
+ .set_time = s35390a_rtc_set_time,
+ .set_alarm = s35390a_rtc_set_alarm,
+ .read_alarm = s35390a_rtc_read_alarm,
+
+};
+
+static struct i2c_driver s35390a_driver;
+
+static int s35390a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+ unsigned int i;
+ struct s35390a *s35390a;
+ struct rtc_time tm;
+ char buf[1];
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ s35390a = devm_kzalloc(&client->dev, sizeof(struct s35390a),
+ GFP_KERNEL);
+ if (!s35390a) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ s35390a->client[0] = client;
+ i2c_set_clientdata(client, s35390a);
+
+ /* This chip uses multiple addresses, use dummy devices for them */
+ for (i = 1; i < 8; ++i) {
+ s35390a->client[i] = i2c_new_dummy(client->adapter,
+ client->addr + i);
+ if (!s35390a->client[i]) {
+ dev_err(&client->dev, "Address %02x unavailable\n",
+ client->addr + i);
+ err = -EBUSY;
+ goto exit_dummy;
+ }
+ }
+
+ err = s35390a_reset(s35390a);
+ if (err < 0) {
+ dev_err(&client->dev, "error resetting chip\n");
+ goto exit_dummy;
+ }
+
+ err = s35390a_disable_test_mode(s35390a);
+ if (err < 0) {
+ dev_err(&client->dev, "error disabling test mode\n");
+ goto exit_dummy;
+ }
+
+ err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, buf, sizeof(buf));
+ if (err < 0) {
+ dev_err(&client->dev, "error checking 12/24 hour mode\n");
+ goto exit_dummy;
+ }
+ if (buf[0] & S35390A_FLAG_24H)
+ s35390a->twentyfourhour = 1;
+ else
+ s35390a->twentyfourhour = 0;
+
+ if (s35390a_get_datetime(client, &tm) < 0)
+ dev_warn(&client->dev, "clock needs to be set\n");
+
+ device_set_wakeup_capable(&client->dev, 1);
+
+ s35390a->rtc = devm_rtc_device_register(&client->dev,
+ s35390a_driver.driver.name,
+ &s35390a_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(s35390a->rtc)) {
+ err = PTR_ERR(s35390a->rtc);
+ goto exit_dummy;
+ }
+ return 0;
+
+exit_dummy:
+ for (i = 1; i < 8; ++i)
+ if (s35390a->client[i])
+ i2c_unregister_device(s35390a->client[i]);
+
+exit:
+ return err;
+}
+
+static int s35390a_remove(struct i2c_client *client)
+{
+ unsigned int i;
+ struct s35390a *s35390a = i2c_get_clientdata(client);
+
+ for (i = 1; i < 8; ++i)
+ if (s35390a->client[i])
+ i2c_unregister_device(s35390a->client[i]);
+
+ return 0;
+}
+
+static struct i2c_driver s35390a_driver = {
+ .driver = {
+ .name = "rtc-s35390a",
+ },
+ .probe = s35390a_probe,
+ .remove = s35390a_remove,
+ .id_table = s35390a_id,
+};
+
+module_i2c_driver(s35390a_driver);
+
+MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>");
+MODULE_DESCRIPTION("S35390A RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
new file mode 100644
index 000000000..76cbad7a9
--- /dev/null
+++ b/drivers/rtc/rtc-s3c.c
@@ -0,0 +1,822 @@
+/* drivers/rtc/rtc-s3c.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com/
+ *
+ * Copyright (c) 2004,2006 Simtec Electronics
+ * Ben Dooks, <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C2410/S3C2440/S3C24XX Internal RTC Driver
+*/
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/log2.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/irq.h>
+#include "rtc-s3c.h"
+
+struct s3c_rtc {
+ struct device *dev;
+ struct rtc_device *rtc;
+
+ void __iomem *base;
+ struct clk *rtc_clk;
+ struct clk *rtc_src_clk;
+
+ struct s3c_rtc_data *data;
+
+ int irq_alarm;
+ int irq_tick;
+
+ spinlock_t pie_lock;
+ spinlock_t alarm_clk_lock;
+
+ int ticnt_save, ticnt_en_save;
+ bool wake_en;
+};
+
+struct s3c_rtc_data {
+ int max_user_freq;
+ bool needs_src_clk;
+
+ void (*irq_handler) (struct s3c_rtc *info, int mask);
+ void (*set_freq) (struct s3c_rtc *info, int freq);
+ void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq);
+ void (*select_tick_clk) (struct s3c_rtc *info);
+ void (*save_tick_cnt) (struct s3c_rtc *info);
+ void (*restore_tick_cnt) (struct s3c_rtc *info);
+ void (*enable) (struct s3c_rtc *info);
+ void (*disable) (struct s3c_rtc *info);
+};
+
+static void s3c_rtc_enable_clk(struct s3c_rtc *info)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
+ clk_enable(info->rtc_clk);
+ if (info->data->needs_src_clk)
+ clk_enable(info->rtc_src_clk);
+ spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
+}
+
+static void s3c_rtc_disable_clk(struct s3c_rtc *info)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
+ if (info->data->needs_src_clk)
+ clk_disable(info->rtc_src_clk);
+ clk_disable(info->rtc_clk);
+ spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
+}
+
+/* IRQ Handlers */
+static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
+{
+ struct s3c_rtc *info = (struct s3c_rtc *)id;
+
+ if (info->data->irq_handler)
+ info->data->irq_handler(info, S3C2410_INTP_TIC);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
+{
+ struct s3c_rtc *info = (struct s3c_rtc *)id;
+
+ if (info->data->irq_handler)
+ info->data->irq_handler(info, S3C2410_INTP_ALM);
+
+ return IRQ_HANDLED;
+}
+
+/* Update control registers */
+static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
+
+ s3c_rtc_enable_clk(info);
+
+ tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
+
+ if (enabled)
+ tmp |= S3C2410_RTCALM_ALMEN;
+
+ writeb(tmp, info->base + S3C2410_RTCALM);
+
+ s3c_rtc_disable_clk(info);
+
+ return 0;
+}
+
+/* Set RTC frequency */
+static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ if (!is_power_of_2(freq))
+ return -EINVAL;
+
+ spin_lock_irq(&info->pie_lock);
+
+ if (info->data->set_freq)
+ info->data->set_freq(info, freq);
+
+ spin_unlock_irq(&info->pie_lock);
+
+ return 0;
+}
+
+/* Time read/write */
+static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+ unsigned int have_retried = 0;
+
+ s3c_rtc_enable_clk(info);
+
+ retry_get_time:
+ rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
+ rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
+ rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
+ rtc_tm->tm_mon = readb(info->base + S3C2410_RTCMON);
+ rtc_tm->tm_year = readb(info->base + S3C2410_RTCYEAR);
+ rtc_tm->tm_sec = readb(info->base + S3C2410_RTCSEC);
+
+ /* the only way to work out whether the system was mid-update
+ * when we read it is to check the second counter, and if it
+ * is zero, then we re-try the entire read
+ */
+
+ if (rtc_tm->tm_sec == 0 && !have_retried) {
+ have_retried = 1;
+ goto retry_get_time;
+ }
+
+ rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec);
+ rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min);
+ rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour);
+ rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday);
+ rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon);
+ rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
+
+ s3c_rtc_disable_clk(info);
+
+ rtc_tm->tm_year += 100;
+
+ dev_dbg(dev, "read time %04d.%02d.%02d %02d:%02d:%02d\n",
+ 1900 + rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
+ rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
+
+ rtc_tm->tm_mon -= 1;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+ int year = tm->tm_year - 100;
+
+ dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
+ 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ /* we get around y2k by simply not supporting it */
+
+ if (year < 0 || year >= 100) {
+ dev_err(dev, "rtc only supports 100 years\n");
+ return -EINVAL;
+ }
+
+ s3c_rtc_enable_clk(info);
+
+ writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC);
+ writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN);
+ writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_RTCHOUR);
+ writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_RTCDATE);
+ writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_RTCMON);
+ writeb(bin2bcd(year), info->base + S3C2410_RTCYEAR);
+
+ s3c_rtc_disable_clk(info);
+
+ return 0;
+}
+
+static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+ struct rtc_time *alm_tm = &alrm->time;
+ unsigned int alm_en;
+
+ s3c_rtc_enable_clk(info);
+
+ alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC);
+ alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN);
+ alm_tm->tm_hour = readb(info->base + S3C2410_ALMHOUR);
+ alm_tm->tm_mon = readb(info->base + S3C2410_ALMMON);
+ alm_tm->tm_mday = readb(info->base + S3C2410_ALMDATE);
+ alm_tm->tm_year = readb(info->base + S3C2410_ALMYEAR);
+
+ alm_en = readb(info->base + S3C2410_RTCALM);
+
+ s3c_rtc_disable_clk(info);
+
+ alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
+
+ dev_dbg(dev, "read alarm %d, %04d.%02d.%02d %02d:%02d:%02d\n",
+ alm_en,
+ 1900 + alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
+ alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
+
+ /* decode the alarm enable field */
+ if (alm_en & S3C2410_RTCALM_SECEN)
+ alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec);
+ else
+ alm_tm->tm_sec = -1;
+
+ if (alm_en & S3C2410_RTCALM_MINEN)
+ alm_tm->tm_min = bcd2bin(alm_tm->tm_min);
+ else
+ alm_tm->tm_min = -1;
+
+ if (alm_en & S3C2410_RTCALM_HOUREN)
+ alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour);
+ else
+ alm_tm->tm_hour = -1;
+
+ if (alm_en & S3C2410_RTCALM_DAYEN)
+ alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday);
+ else
+ alm_tm->tm_mday = -1;
+
+ if (alm_en & S3C2410_RTCALM_MONEN) {
+ alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon);
+ alm_tm->tm_mon -= 1;
+ } else {
+ alm_tm->tm_mon = -1;
+ }
+
+ if (alm_en & S3C2410_RTCALM_YEAREN)
+ alm_tm->tm_year = bcd2bin(alm_tm->tm_year);
+ else
+ alm_tm->tm_year = -1;
+
+ return 0;
+}
+
+static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+ struct rtc_time *tm = &alrm->time;
+ unsigned int alrm_en;
+
+ dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
+ alrm->enabled,
+ 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ s3c_rtc_enable_clk(info);
+
+ alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
+ writeb(0x00, info->base + S3C2410_RTCALM);
+
+ if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
+ alrm_en |= S3C2410_RTCALM_SECEN;
+ writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_ALMSEC);
+ }
+
+ if (tm->tm_min < 60 && tm->tm_min >= 0) {
+ alrm_en |= S3C2410_RTCALM_MINEN;
+ writeb(bin2bcd(tm->tm_min), info->base + S3C2410_ALMMIN);
+ }
+
+ if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
+ alrm_en |= S3C2410_RTCALM_HOUREN;
+ writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR);
+ }
+
+ dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
+
+ writeb(alrm_en, info->base + S3C2410_RTCALM);
+
+ s3c_rtc_disable_clk(info);
+
+ s3c_rtc_setaie(dev, alrm->enabled);
+
+ return 0;
+}
+
+static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+
+ s3c_rtc_enable_clk(info);
+
+ if (info->data->enable_tick)
+ info->data->enable_tick(info, seq);
+
+ s3c_rtc_disable_clk(info);
+
+ return 0;
+}
+
+static const struct rtc_class_ops s3c_rtcops = {
+ .read_time = s3c_rtc_gettime,
+ .set_time = s3c_rtc_settime,
+ .read_alarm = s3c_rtc_getalarm,
+ .set_alarm = s3c_rtc_setalarm,
+ .proc = s3c_rtc_proc,
+ .alarm_irq_enable = s3c_rtc_setaie,
+};
+
+static void s3c24xx_rtc_enable(struct s3c_rtc *info)
+{
+ unsigned int con, tmp;
+
+ con = readw(info->base + S3C2410_RTCCON);
+ /* re-enable the device, and check it is ok */
+ if ((con & S3C2410_RTCCON_RTCEN) == 0) {
+ dev_info(info->dev, "rtc disabled, re-enabling\n");
+
+ tmp = readw(info->base + S3C2410_RTCCON);
+ writew(tmp | S3C2410_RTCCON_RTCEN,
+ info->base + S3C2410_RTCCON);
+ }
+
+ if (con & S3C2410_RTCCON_CNTSEL) {
+ dev_info(info->dev, "removing RTCCON_CNTSEL\n");
+
+ tmp = readw(info->base + S3C2410_RTCCON);
+ writew(tmp & ~S3C2410_RTCCON_CNTSEL,
+ info->base + S3C2410_RTCCON);
+ }
+
+ if (con & S3C2410_RTCCON_CLKRST) {
+ dev_info(info->dev, "removing RTCCON_CLKRST\n");
+
+ tmp = readw(info->base + S3C2410_RTCCON);
+ writew(tmp & ~S3C2410_RTCCON_CLKRST,
+ info->base + S3C2410_RTCCON);
+ }
+}
+
+static void s3c24xx_rtc_disable(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ con = readw(info->base + S3C2410_RTCCON);
+ con &= ~S3C2410_RTCCON_RTCEN;
+ writew(con, info->base + S3C2410_RTCCON);
+
+ con = readb(info->base + S3C2410_TICNT);
+ con &= ~S3C2410_TICNT_ENABLE;
+ writeb(con, info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_disable(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ con = readw(info->base + S3C2410_RTCCON);
+ con &= ~S3C64XX_RTCCON_TICEN;
+ con &= ~S3C2410_RTCCON_RTCEN;
+ writew(con, info->base + S3C2410_RTCCON);
+}
+
+static int s3c_rtc_remove(struct platform_device *pdev)
+{
+ struct s3c_rtc *info = platform_get_drvdata(pdev);
+
+ s3c_rtc_setaie(info->dev, 0);
+
+ clk_unprepare(info->rtc_clk);
+ info->rtc_clk = NULL;
+
+ return 0;
+}
+
+static const struct of_device_id s3c_rtc_dt_match[];
+
+static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
+ return (struct s3c_rtc_data *)match->data;
+}
+
+static int s3c_rtc_probe(struct platform_device *pdev)
+{
+ struct s3c_rtc *info = NULL;
+ struct rtc_time rtc_tm;
+ struct resource *res;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ /* find the IRQs */
+ info->irq_tick = platform_get_irq(pdev, 1);
+ if (info->irq_tick < 0) {
+ dev_err(&pdev->dev, "no irq for rtc tick\n");
+ return info->irq_tick;
+ }
+
+ info->dev = &pdev->dev;
+ info->data = s3c_rtc_get_data(pdev);
+ if (!info->data) {
+ dev_err(&pdev->dev, "failed getting s3c_rtc_data\n");
+ return -EINVAL;
+ }
+ spin_lock_init(&info->pie_lock);
+ spin_lock_init(&info->alarm_clk_lock);
+
+ platform_set_drvdata(pdev, info);
+
+ info->irq_alarm = platform_get_irq(pdev, 0);
+ if (info->irq_alarm < 0) {
+ dev_err(&pdev->dev, "no irq for alarm\n");
+ return info->irq_alarm;
+ }
+
+ dev_dbg(&pdev->dev, "s3c2410_rtc: tick irq %d, alarm irq %d\n",
+ info->irq_tick, info->irq_alarm);
+
+ /* get the memory region */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->base))
+ return PTR_ERR(info->base);
+
+ info->rtc_clk = devm_clk_get(&pdev->dev, "rtc");
+ if (IS_ERR(info->rtc_clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock\n");
+ return PTR_ERR(info->rtc_clk);
+ }
+ clk_prepare_enable(info->rtc_clk);
+
+ if (info->data->needs_src_clk) {
+ info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
+ if (IS_ERR(info->rtc_src_clk)) {
+ dev_err(&pdev->dev,
+ "failed to find rtc source clock\n");
+ return PTR_ERR(info->rtc_src_clk);
+ }
+ clk_prepare_enable(info->rtc_src_clk);
+ }
+
+ /* check to see if everything is setup correctly */
+ if (info->data->enable)
+ info->data->enable(info);
+
+ dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n",
+ readw(info->base + S3C2410_RTCCON));
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ /* Check RTC Time */
+ if (s3c_rtc_gettime(&pdev->dev, &rtc_tm)) {
+ rtc_tm.tm_year = 100;
+ rtc_tm.tm_mon = 0;
+ rtc_tm.tm_mday = 1;
+ rtc_tm.tm_hour = 0;
+ rtc_tm.tm_min = 0;
+ rtc_tm.tm_sec = 0;
+
+ s3c_rtc_settime(&pdev->dev, &rtc_tm);
+
+ dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
+ }
+
+ /* register RTC and exit */
+ info->rtc = devm_rtc_device_register(&pdev->dev, "s3c", &s3c_rtcops,
+ THIS_MODULE);
+ if (IS_ERR(info->rtc)) {
+ dev_err(&pdev->dev, "cannot attach rtc\n");
+ ret = PTR_ERR(info->rtc);
+ goto err_nortc;
+ }
+
+ ret = devm_request_irq(&pdev->dev, info->irq_alarm, s3c_rtc_alarmirq,
+ 0, "s3c2410-rtc alarm", info);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_alarm, ret);
+ goto err_nortc;
+ }
+
+ ret = devm_request_irq(&pdev->dev, info->irq_tick, s3c_rtc_tickirq,
+ 0, "s3c2410-rtc tick", info);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ%d error %d\n", info->irq_tick, ret);
+ goto err_nortc;
+ }
+
+ if (info->data->select_tick_clk)
+ info->data->select_tick_clk(info);
+
+ s3c_rtc_setfreq(info, 1);
+
+ s3c_rtc_disable_clk(info);
+
+ return 0;
+
+ err_nortc:
+ if (info->data->disable)
+ info->data->disable(info);
+
+ if (info->data->needs_src_clk)
+ clk_disable_unprepare(info->rtc_src_clk);
+ clk_disable_unprepare(info->rtc_clk);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+static int s3c_rtc_suspend(struct device *dev)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+
+ s3c_rtc_enable_clk(info);
+
+ /* save TICNT for anyone using periodic interrupts */
+ if (info->data->save_tick_cnt)
+ info->data->save_tick_cnt(info);
+
+ if (info->data->disable)
+ info->data->disable(info);
+
+ if (device_may_wakeup(dev) && !info->wake_en) {
+ if (enable_irq_wake(info->irq_alarm) == 0)
+ info->wake_en = true;
+ else
+ dev_err(dev, "enable_irq_wake failed\n");
+ }
+
+ return 0;
+}
+
+static int s3c_rtc_resume(struct device *dev)
+{
+ struct s3c_rtc *info = dev_get_drvdata(dev);
+
+ if (info->data->enable)
+ info->data->enable(info);
+
+ if (info->data->restore_tick_cnt)
+ info->data->restore_tick_cnt(info);
+
+ s3c_rtc_disable_clk(info);
+
+ if (device_may_wakeup(dev) && info->wake_en) {
+ disable_irq_wake(info->irq_alarm);
+ info->wake_en = false;
+ }
+
+ return 0;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
+
+static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
+{
+ rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
+}
+
+static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
+{
+ rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
+ writeb(mask, info->base + S3C2410_INTP);
+}
+
+static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ unsigned int tmp = 0;
+ int val;
+
+ tmp = readb(info->base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+ tmp |= val;
+
+ writel(tmp, info->base + S3C2410_TICNT);
+}
+
+static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ unsigned int tmp = 0;
+ int val;
+
+ tmp = readb(info->base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+
+ tmp |= S3C2443_TICNT_PART(val);
+ writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);
+
+ writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2);
+
+ writel(tmp, info->base + S3C2410_TICNT);
+}
+
+static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ unsigned int tmp = 0;
+ int val;
+
+ tmp = readb(info->base + S3C2410_TICNT);
+ tmp &= S3C2410_TICNT_ENABLE;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+
+ tmp |= S3C2443_TICNT_PART(val);
+ writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1);
+
+ writel(tmp, info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq)
+{
+ int val;
+
+ val = (info->rtc->max_user_freq / freq) - 1;
+ writel(val, info->base + S3C2410_TICNT);
+}
+
+static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
+{
+ unsigned int ticnt;
+
+ ticnt = readb(info->base + S3C2410_TICNT);
+ ticnt &= S3C2410_TICNT_ENABLE;
+
+ seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
+}
+
+static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ con = readw(info->base + S3C2410_RTCCON);
+ con |= S3C2443_RTCCON_TICSEL;
+ writew(con, info->base + S3C2410_RTCCON);
+}
+
+static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq)
+{
+ unsigned int ticnt;
+
+ ticnt = readw(info->base + S3C2410_RTCCON);
+ ticnt &= S3C64XX_RTCCON_TICEN;
+
+ seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no");
+}
+
+static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info)
+{
+ info->ticnt_save = readb(info->base + S3C2410_TICNT);
+}
+
+static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info)
+{
+ writeb(info->ticnt_save, info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info)
+{
+ info->ticnt_en_save = readw(info->base + S3C2410_RTCCON);
+ info->ticnt_en_save &= S3C64XX_RTCCON_TICEN;
+ info->ticnt_save = readl(info->base + S3C2410_TICNT);
+}
+
+static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
+{
+ unsigned int con;
+
+ writel(info->ticnt_save, info->base + S3C2410_TICNT);
+ if (info->ticnt_en_save) {
+ con = readw(info->base + S3C2410_RTCCON);
+ writew(con | info->ticnt_en_save,
+ info->base + S3C2410_RTCCON);
+ }
+}
+
+static struct s3c_rtc_data const s3c2410_rtc_data = {
+ .max_user_freq = 128,
+ .irq_handler = s3c24xx_rtc_irq,
+ .set_freq = s3c2410_rtc_setfreq,
+ .enable_tick = s3c24xx_rtc_enable_tick,
+ .save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c24xx_rtc_disable,
+};
+
+static struct s3c_rtc_data const s3c2416_rtc_data = {
+ .max_user_freq = 32768,
+ .irq_handler = s3c24xx_rtc_irq,
+ .set_freq = s3c2416_rtc_setfreq,
+ .enable_tick = s3c24xx_rtc_enable_tick,
+ .select_tick_clk = s3c2416_rtc_select_tick_clk,
+ .save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c24xx_rtc_disable,
+};
+
+static struct s3c_rtc_data const s3c2443_rtc_data = {
+ .max_user_freq = 32768,
+ .irq_handler = s3c24xx_rtc_irq,
+ .set_freq = s3c2443_rtc_setfreq,
+ .enable_tick = s3c24xx_rtc_enable_tick,
+ .select_tick_clk = s3c2416_rtc_select_tick_clk,
+ .save_tick_cnt = s3c24xx_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c24xx_rtc_disable,
+};
+
+static struct s3c_rtc_data const s3c6410_rtc_data = {
+ .max_user_freq = 32768,
+ .needs_src_clk = true,
+ .irq_handler = s3c6410_rtc_irq,
+ .set_freq = s3c6410_rtc_setfreq,
+ .enable_tick = s3c6410_rtc_enable_tick,
+ .save_tick_cnt = s3c6410_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c6410_rtc_disable,
+};
+
+static struct s3c_rtc_data const exynos3250_rtc_data = {
+ .max_user_freq = 32768,
+ .needs_src_clk = true,
+ .irq_handler = s3c6410_rtc_irq,
+ .set_freq = s3c6410_rtc_setfreq,
+ .enable_tick = s3c6410_rtc_enable_tick,
+ .save_tick_cnt = s3c6410_rtc_save_tick_cnt,
+ .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt,
+ .enable = s3c24xx_rtc_enable,
+ .disable = s3c6410_rtc_disable,
+};
+
+static const struct of_device_id s3c_rtc_dt_match[] = {
+ {
+ .compatible = "samsung,s3c2410-rtc",
+ .data = (void *)&s3c2410_rtc_data,
+ }, {
+ .compatible = "samsung,s3c2416-rtc",
+ .data = (void *)&s3c2416_rtc_data,
+ }, {
+ .compatible = "samsung,s3c2443-rtc",
+ .data = (void *)&s3c2443_rtc_data,
+ }, {
+ .compatible = "samsung,s3c6410-rtc",
+ .data = (void *)&s3c6410_rtc_data,
+ }, {
+ .compatible = "samsung,exynos3250-rtc",
+ .data = (void *)&exynos3250_rtc_data,
+ },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
+
+static struct platform_driver s3c_rtc_driver = {
+ .probe = s3c_rtc_probe,
+ .remove = s3c_rtc_remove,
+ .driver = {
+ .name = "s3c-rtc",
+ .pm = &s3c_rtc_pm_ops,
+ .of_match_table = of_match_ptr(s3c_rtc_dt_match),
+ },
+};
+module_platform_driver(s3c_rtc_driver);
+
+MODULE_DESCRIPTION("Samsung S3C RTC Driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s3c2410-rtc");
diff --git a/drivers/rtc/rtc-s3c.h b/drivers/rtc/rtc-s3c.h
new file mode 100644
index 000000000..004b61a83
--- /dev/null
+++ b/drivers/rtc/rtc-s3c.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2003 Simtec Electronics <linux@simtec.co.uk>
+ * http://www.simtec.co.uk/products/SWLINUX/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * S3C2410 Internal RTC register definition
+*/
+
+#ifndef __ASM_ARCH_REGS_RTC_H
+#define __ASM_ARCH_REGS_RTC_H __FILE__
+
+#define S3C2410_RTCREG(x) (x)
+#define S3C2410_INTP S3C2410_RTCREG(0x30)
+#define S3C2410_INTP_ALM (1 << 1)
+#define S3C2410_INTP_TIC (1 << 0)
+
+#define S3C2410_RTCCON S3C2410_RTCREG(0x40)
+#define S3C2410_RTCCON_RTCEN (1 << 0)
+#define S3C2410_RTCCON_CNTSEL (1 << 2)
+#define S3C2410_RTCCON_CLKRST (1 << 3)
+#define S3C2443_RTCCON_TICSEL (1 << 4)
+#define S3C64XX_RTCCON_TICEN (1 << 8)
+
+#define S3C2410_TICNT S3C2410_RTCREG(0x44)
+#define S3C2410_TICNT_ENABLE (1 << 7)
+
+/* S3C2443: tick count is 15 bit wide
+ * TICNT[6:0] contains upper 7 bits
+ * TICNT1[7:0] contains lower 8 bits
+ */
+#define S3C2443_TICNT_PART(x) ((x & 0x7f00) >> 8)
+#define S3C2443_TICNT1 S3C2410_RTCREG(0x4C)
+#define S3C2443_TICNT1_PART(x) (x & 0xff)
+
+/* S3C2416: tick count is 32 bit wide
+ * TICNT[6:0] contains bits [14:8]
+ * TICNT1[7:0] contains lower 8 bits
+ * TICNT2[16:0] contains upper 17 bits
+ */
+#define S3C2416_TICNT2 S3C2410_RTCREG(0x48)
+#define S3C2416_TICNT2_PART(x) ((x & 0xffff8000) >> 15)
+
+#define S3C2410_RTCALM S3C2410_RTCREG(0x50)
+#define S3C2410_RTCALM_ALMEN (1 << 6)
+#define S3C2410_RTCALM_YEAREN (1 << 5)
+#define S3C2410_RTCALM_MONEN (1 << 4)
+#define S3C2410_RTCALM_DAYEN (1 << 3)
+#define S3C2410_RTCALM_HOUREN (1 << 2)
+#define S3C2410_RTCALM_MINEN (1 << 1)
+#define S3C2410_RTCALM_SECEN (1 << 0)
+
+#define S3C2410_ALMSEC S3C2410_RTCREG(0x54)
+#define S3C2410_ALMMIN S3C2410_RTCREG(0x58)
+#define S3C2410_ALMHOUR S3C2410_RTCREG(0x5c)
+
+#define S3C2410_ALMDATE S3C2410_RTCREG(0x60)
+#define S3C2410_ALMMON S3C2410_RTCREG(0x64)
+#define S3C2410_ALMYEAR S3C2410_RTCREG(0x68)
+
+#define S3C2410_RTCSEC S3C2410_RTCREG(0x70)
+#define S3C2410_RTCMIN S3C2410_RTCREG(0x74)
+#define S3C2410_RTCHOUR S3C2410_RTCREG(0x78)
+#define S3C2410_RTCDATE S3C2410_RTCREG(0x7c)
+#define S3C2410_RTCMON S3C2410_RTCREG(0x84)
+#define S3C2410_RTCYEAR S3C2410_RTCREG(0x88)
+
+#endif /* __ASM_ARCH_REGS_RTC_H */
diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c
new file mode 100644
index 000000000..8c70d785b
--- /dev/null
+++ b/drivers/rtc/rtc-s5m.c
@@ -0,0 +1,817 @@
+/*
+ * Copyright (c) 2013-2014 Samsung Electronics Co., Ltd
+ * http://www.samsung.com
+ *
+ * Copyright (C) 2013 Google, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/regmap.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/samsung/core.h>
+#include <linux/mfd/samsung/irq.h>
+#include <linux/mfd/samsung/rtc.h>
+#include <linux/mfd/samsung/s2mps14.h>
+
+/*
+ * Maximum number of retries for checking changes in UDR field
+ * of S5M_RTC_UDR_CON register (to limit possible endless loop).
+ *
+ * After writing to RTC registers (setting time or alarm) read the UDR field
+ * in S5M_RTC_UDR_CON register. UDR is auto-cleared when data have
+ * been transferred.
+ */
+#define UDR_READ_RETRY_CNT 5
+
+/* Registers used by the driver which are different between chipsets. */
+struct s5m_rtc_reg_config {
+ /* Number of registers used for setting time/alarm0/alarm1 */
+ unsigned int regs_count;
+ /* First register for time, seconds */
+ unsigned int time;
+ /* RTC control register */
+ unsigned int ctrl;
+ /* First register for alarm 0, seconds */
+ unsigned int alarm0;
+ /* First register for alarm 1, seconds */
+ unsigned int alarm1;
+ /*
+ * Register for update flag (UDR). Typically setting UDR field to 1
+ * will enable update of time or alarm register. Then it will be
+ * auto-cleared after successful update.
+ */
+ unsigned int rtc_udr_update;
+ /* Mask for UDR field in 'rtc_udr_update' register */
+ unsigned int rtc_udr_mask;
+};
+
+/* Register map for S5M8763 and S5M8767 */
+static const struct s5m_rtc_reg_config s5m_rtc_regs = {
+ .regs_count = 8,
+ .time = S5M_RTC_SEC,
+ .ctrl = S5M_ALARM1_CONF,
+ .alarm0 = S5M_ALARM0_SEC,
+ .alarm1 = S5M_ALARM1_SEC,
+ .rtc_udr_update = S5M_RTC_UDR_CON,
+ .rtc_udr_mask = S5M_RTC_UDR_MASK,
+};
+
+/*
+ * Register map for S2MPS14.
+ * It may be also suitable for S2MPS11 but this was not tested.
+ */
+static const struct s5m_rtc_reg_config s2mps_rtc_regs = {
+ .regs_count = 7,
+ .time = S2MPS_RTC_SEC,
+ .ctrl = S2MPS_RTC_CTRL,
+ .alarm0 = S2MPS_ALARM0_SEC,
+ .alarm1 = S2MPS_ALARM1_SEC,
+ .rtc_udr_update = S2MPS_RTC_UDR_CON,
+ .rtc_udr_mask = S2MPS_RTC_WUDR_MASK,
+};
+
+struct s5m_rtc_info {
+ struct device *dev;
+ struct i2c_client *i2c;
+ struct sec_pmic_dev *s5m87xx;
+ struct regmap *regmap;
+ struct rtc_device *rtc_dev;
+ int irq;
+ enum sec_device_type device_type;
+ int rtc_24hr_mode;
+ const struct s5m_rtc_reg_config *regs;
+};
+
+static const struct regmap_config s5m_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S5M_RTC_REG_MAX,
+};
+
+static const struct regmap_config s2mps14_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = S2MPS_RTC_REG_MAX,
+};
+
+static void s5m8767_data_to_tm(u8 *data, struct rtc_time *tm,
+ int rtc_24hr_mode)
+{
+ tm->tm_sec = data[RTC_SEC] & 0x7f;
+ tm->tm_min = data[RTC_MIN] & 0x7f;
+ if (rtc_24hr_mode) {
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
+ } else {
+ tm->tm_hour = data[RTC_HOUR] & 0x0f;
+ if (data[RTC_HOUR] & HOUR_PM_MASK)
+ tm->tm_hour += 12;
+ }
+
+ tm->tm_wday = ffs(data[RTC_WEEKDAY] & 0x7f);
+ tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
+ tm->tm_year = (data[RTC_YEAR1] & 0x7f) + 100;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+}
+
+static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = tm->tm_sec;
+ data[RTC_MIN] = tm->tm_min;
+
+ if (tm->tm_hour >= 12)
+ data[RTC_HOUR] = tm->tm_hour | HOUR_PM_MASK;
+ else
+ data[RTC_HOUR] = tm->tm_hour & ~HOUR_PM_MASK;
+
+ data[RTC_WEEKDAY] = 1 << tm->tm_wday;
+ data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTH] = tm->tm_mon + 1;
+ data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
+
+ if (tm->tm_year < 100) {
+ pr_err("RTC cannot handle the year %d\n",
+ 1900 + tm->tm_year);
+ return -EINVAL;
+ } else {
+ return 0;
+ }
+}
+
+/*
+ * Read RTC_UDR_CON register and wait till UDR field is cleared.
+ * This indicates that time/alarm update ended.
+ */
+static inline int s5m8767_wait_for_udr_update(struct s5m_rtc_info *info)
+{
+ int ret, retry = UDR_READ_RETRY_CNT;
+ unsigned int data;
+
+ do {
+ ret = regmap_read(info->regmap, info->regs->rtc_udr_update,
+ &data);
+ } while (--retry && (data & info->regs->rtc_udr_mask) && !ret);
+
+ if (!retry)
+ dev_err(info->dev, "waiting for UDR update, reached max number of retries\n");
+
+ return ret;
+}
+
+static inline int s5m_check_peding_alarm_interrupt(struct s5m_rtc_info *info,
+ struct rtc_wkalrm *alarm)
+{
+ int ret;
+ unsigned int val;
+
+ switch (info->device_type) {
+ case S5M8767X:
+ case S5M8763X:
+ ret = regmap_read(info->regmap, S5M_RTC_STATUS, &val);
+ val &= S5M_ALARM0_STATUS;
+ break;
+ case S2MPS14X:
+ case S2MPS13X:
+ ret = regmap_read(info->s5m87xx->regmap_pmic, S2MPS14_REG_ST2,
+ &val);
+ val &= S2MPS_ALARM0_STATUS;
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return ret;
+
+ if (val)
+ alarm->pending = 1;
+ else
+ alarm->pending = 0;
+
+ return 0;
+}
+
+static inline int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info)
+{
+ int ret;
+ unsigned int data;
+
+ ret = regmap_read(info->regmap, info->regs->rtc_udr_update, &data);
+ if (ret < 0) {
+ dev_err(info->dev, "failed to read update reg(%d)\n", ret);
+ return ret;
+ }
+
+ data |= info->regs->rtc_udr_mask;
+ if (info->device_type == S5M8763X || info->device_type == S5M8767X)
+ data |= S5M_RTC_TIME_EN_MASK;
+
+ ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data);
+ if (ret < 0) {
+ dev_err(info->dev, "failed to write update reg(%d)\n", ret);
+ return ret;
+ }
+
+ ret = s5m8767_wait_for_udr_update(info);
+
+ return ret;
+}
+
+static inline int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info)
+{
+ int ret;
+ unsigned int data;
+
+ ret = regmap_read(info->regmap, info->regs->rtc_udr_update, &data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read update reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ data |= info->regs->rtc_udr_mask;
+ switch (info->device_type) {
+ case S5M8763X:
+ case S5M8767X:
+ data &= ~S5M_RTC_TIME_EN_MASK;
+ break;
+ case S2MPS14X:
+ data |= S2MPS_RTC_RUDR_MASK;
+ break;
+ case S2MPS13X:
+ data |= S2MPS13_RTC_AUDR_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(info->regmap, info->regs->rtc_udr_update, data);
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write update reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = s5m8767_wait_for_udr_update(info);
+
+ /* On S2MPS13 the AUDR is not auto-cleared */
+ if (info->device_type == S2MPS13X)
+ regmap_update_bits(info->regmap, info->regs->rtc_udr_update,
+ S2MPS13_RTC_AUDR_MASK, 0);
+
+ return ret;
+}
+
+static void s5m8763_data_to_tm(u8 *data, struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(data[RTC_SEC]);
+ tm->tm_min = bcd2bin(data[RTC_MIN]);
+
+ if (data[RTC_HOUR] & HOUR_12) {
+ tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x1f);
+ if (data[RTC_HOUR] & HOUR_PM)
+ tm->tm_hour += 12;
+ } else {
+ tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f);
+ }
+
+ tm->tm_wday = data[RTC_WEEKDAY] & 0x07;
+ tm->tm_mday = bcd2bin(data[RTC_DATE]);
+ tm->tm_mon = bcd2bin(data[RTC_MONTH]);
+ tm->tm_year = bcd2bin(data[RTC_YEAR1]) + bcd2bin(data[RTC_YEAR2]) * 100;
+ tm->tm_year -= 1900;
+}
+
+static void s5m8763_tm_to_data(struct rtc_time *tm, u8 *data)
+{
+ data[RTC_SEC] = bin2bcd(tm->tm_sec);
+ data[RTC_MIN] = bin2bcd(tm->tm_min);
+ data[RTC_HOUR] = bin2bcd(tm->tm_hour);
+ data[RTC_WEEKDAY] = tm->tm_wday;
+ data[RTC_DATE] = bin2bcd(tm->tm_mday);
+ data[RTC_MONTH] = bin2bcd(tm->tm_mon);
+ data[RTC_YEAR1] = bin2bcd(tm->tm_year % 100);
+ data[RTC_YEAR2] = bin2bcd((tm->tm_year + 1900) / 100);
+}
+
+static int s5m_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[info->regs->regs_count];
+ int ret;
+
+ if (info->device_type == S2MPS14X || info->device_type == S2MPS13X) {
+ ret = regmap_update_bits(info->regmap,
+ info->regs->rtc_udr_update,
+ S2MPS_RTC_RUDR_MASK, S2MPS_RTC_RUDR_MASK);
+ if (ret) {
+ dev_err(dev,
+ "Failed to prepare registers for time reading: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ ret = regmap_bulk_read(info->regmap, info->regs->time, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ switch (info->device_type) {
+ case S5M8763X:
+ s5m8763_data_to_tm(data, tm);
+ break;
+
+ case S5M8767X:
+ case S2MPS14X:
+ case S2MPS13X:
+ s5m8767_data_to_tm(data, tm, info->rtc_24hr_mode);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+
+ return rtc_valid_tm(tm);
+}
+
+static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[info->regs->regs_count];
+ int ret = 0;
+
+ switch (info->device_type) {
+ case S5M8763X:
+ s5m8763_tm_to_data(tm, data);
+ break;
+ case S5M8767X:
+ case S2MPS14X:
+ case S2MPS13X:
+ ret = s5m8767_tm_to_data(tm, data);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
+
+ ret = regmap_raw_write(info->regmap, info->regs->time, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ ret = s5m8767_rtc_set_time_reg(info);
+
+ return ret;
+}
+
+static int s5m_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[info->regs->regs_count];
+ unsigned int val;
+ int ret, i;
+
+ ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ switch (info->device_type) {
+ case S5M8763X:
+ s5m8763_data_to_tm(data, &alrm->time);
+ ret = regmap_read(info->regmap, S5M_ALARM0_CONF, &val);
+ if (ret < 0)
+ return ret;
+
+ alrm->enabled = !!val;
+ break;
+
+ case S5M8767X:
+ case S2MPS14X:
+ case S2MPS13X:
+ s5m8767_data_to_tm(data, &alrm->time, info->rtc_24hr_mode);
+ alrm->enabled = 0;
+ for (i = 0; i < info->regs->regs_count; i++) {
+ if (data[i] & ALARM_ENABLE_MASK) {
+ alrm->enabled = 1;
+ break;
+ }
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
+ alrm->time.tm_mday, alrm->time.tm_hour,
+ alrm->time.tm_min, alrm->time.tm_sec,
+ alrm->time.tm_wday);
+
+ ret = s5m_check_peding_alarm_interrupt(info, alrm);
+
+ return 0;
+}
+
+static int s5m_rtc_stop_alarm(struct s5m_rtc_info *info)
+{
+ u8 data[info->regs->regs_count];
+ int ret, i;
+ struct rtc_time tm;
+
+ ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
+ dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
+
+ switch (info->device_type) {
+ case S5M8763X:
+ ret = regmap_write(info->regmap, S5M_ALARM0_CONF, 0);
+ break;
+
+ case S5M8767X:
+ case S2MPS14X:
+ case S2MPS13X:
+ for (i = 0; i < info->regs->regs_count; i++)
+ data[i] &= ~ALARM_ENABLE_MASK;
+
+ ret = regmap_raw_write(info->regmap, info->regs->alarm0, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ ret = s5m8767_rtc_set_alarm_reg(info);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int s5m_rtc_start_alarm(struct s5m_rtc_info *info)
+{
+ int ret;
+ u8 data[info->regs->regs_count];
+ u8 alarm0_conf;
+ struct rtc_time tm;
+
+ ret = regmap_bulk_read(info->regmap, info->regs->alarm0, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ s5m8767_data_to_tm(data, &tm, info->rtc_24hr_mode);
+ dev_dbg(info->dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ 1900 + tm.tm_year, 1 + tm.tm_mon, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, tm.tm_wday);
+
+ switch (info->device_type) {
+ case S5M8763X:
+ alarm0_conf = 0x77;
+ ret = regmap_write(info->regmap, S5M_ALARM0_CONF, alarm0_conf);
+ break;
+
+ case S5M8767X:
+ case S2MPS14X:
+ case S2MPS13X:
+ data[RTC_SEC] |= ALARM_ENABLE_MASK;
+ data[RTC_MIN] |= ALARM_ENABLE_MASK;
+ data[RTC_HOUR] |= ALARM_ENABLE_MASK;
+ data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
+ if (data[RTC_DATE] & 0x1f)
+ data[RTC_DATE] |= ALARM_ENABLE_MASK;
+ if (data[RTC_MONTH] & 0xf)
+ data[RTC_MONTH] |= ALARM_ENABLE_MASK;
+ if (data[RTC_YEAR1] & 0x7f)
+ data[RTC_YEAR1] |= ALARM_ENABLE_MASK;
+
+ ret = regmap_raw_write(info->regmap, info->regs->alarm0, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+ ret = s5m8767_rtc_set_alarm_reg(info);
+
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int s5m_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+ u8 data[info->regs->regs_count];
+ int ret;
+
+ switch (info->device_type) {
+ case S5M8763X:
+ s5m8763_tm_to_data(&alrm->time, data);
+ break;
+
+ case S5M8767X:
+ case S2MPS14X:
+ case S2MPS13X:
+ s5m8767_tm_to_data(&alrm->time, data);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%s: %d/%d/%d %d:%d:%d(%d)\n", __func__,
+ 1900 + alrm->time.tm_year, 1 + alrm->time.tm_mon,
+ alrm->time.tm_mday, alrm->time.tm_hour, alrm->time.tm_min,
+ alrm->time.tm_sec, alrm->time.tm_wday);
+
+ ret = s5m_rtc_stop_alarm(info);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_raw_write(info->regmap, info->regs->alarm0, data,
+ info->regs->regs_count);
+ if (ret < 0)
+ return ret;
+
+ ret = s5m8767_rtc_set_alarm_reg(info);
+ if (ret < 0)
+ return ret;
+
+ if (alrm->enabled)
+ ret = s5m_rtc_start_alarm(info);
+
+ return ret;
+}
+
+static int s5m_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+
+ if (enabled)
+ return s5m_rtc_start_alarm(info);
+ else
+ return s5m_rtc_stop_alarm(info);
+}
+
+static irqreturn_t s5m_rtc_alarm_irq(int irq, void *data)
+{
+ struct s5m_rtc_info *info = data;
+
+ rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops s5m_rtc_ops = {
+ .read_time = s5m_rtc_read_time,
+ .set_time = s5m_rtc_set_time,
+ .read_alarm = s5m_rtc_read_alarm,
+ .set_alarm = s5m_rtc_set_alarm,
+ .alarm_irq_enable = s5m_rtc_alarm_irq_enable,
+};
+
+static int s5m8767_rtc_init_reg(struct s5m_rtc_info *info)
+{
+ u8 data[2];
+ int ret;
+
+ switch (info->device_type) {
+ case S5M8763X:
+ case S5M8767X:
+ /* UDR update time. Default of 7.32 ms is too long. */
+ ret = regmap_update_bits(info->regmap, S5M_RTC_UDR_CON,
+ S5M_RTC_UDR_T_MASK, S5M_RTC_UDR_T_450_US);
+ if (ret < 0)
+ dev_err(info->dev, "%s: fail to change UDR time: %d\n",
+ __func__, ret);
+
+ /* Set RTC control register : Binary mode, 24hour mode */
+ data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+
+ ret = regmap_raw_write(info->regmap, S5M_ALARM0_CONF, data, 2);
+ break;
+
+ case S2MPS14X:
+ case S2MPS13X:
+ data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
+ ret = regmap_write(info->regmap, info->regs->ctrl, data[0]);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ info->rtc_24hr_mode = 1;
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to write controlm reg(%d)\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int s5m_rtc_probe(struct platform_device *pdev)
+{
+ struct sec_pmic_dev *s5m87xx = dev_get_drvdata(pdev->dev.parent);
+ struct sec_platform_data *pdata = s5m87xx->pdata;
+ struct s5m_rtc_info *info;
+ const struct regmap_config *regmap_cfg;
+ int ret, alarm_irq;
+
+ if (!pdata) {
+ dev_err(pdev->dev.parent, "Platform data not supplied\n");
+ return -ENODEV;
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ switch (platform_get_device_id(pdev)->driver_data) {
+ case S2MPS14X:
+ case S2MPS13X:
+ regmap_cfg = &s2mps14_rtc_regmap_config;
+ info->regs = &s2mps_rtc_regs;
+ alarm_irq = S2MPS14_IRQ_RTCA0;
+ break;
+ case S5M8763X:
+ regmap_cfg = &s5m_rtc_regmap_config;
+ info->regs = &s5m_rtc_regs;
+ alarm_irq = S5M8763_IRQ_ALARM0;
+ break;
+ case S5M8767X:
+ regmap_cfg = &s5m_rtc_regmap_config;
+ info->regs = &s5m_rtc_regs;
+ alarm_irq = S5M8767_IRQ_RTCA1;
+ break;
+ default:
+ dev_err(&pdev->dev,
+ "Device type %lu is not supported by RTC driver\n",
+ platform_get_device_id(pdev)->driver_data);
+ return -ENODEV;
+ }
+
+ info->i2c = i2c_new_dummy(s5m87xx->i2c->adapter, RTC_I2C_ADDR);
+ if (!info->i2c) {
+ dev_err(&pdev->dev, "Failed to allocate I2C for RTC\n");
+ return -ENODEV;
+ }
+
+ info->regmap = devm_regmap_init_i2c(info->i2c, regmap_cfg);
+ if (IS_ERR(info->regmap)) {
+ ret = PTR_ERR(info->regmap);
+ dev_err(&pdev->dev, "Failed to allocate RTC register map: %d\n",
+ ret);
+ goto err;
+ }
+
+ info->dev = &pdev->dev;
+ info->s5m87xx = s5m87xx;
+ info->device_type = platform_get_device_id(pdev)->driver_data;
+
+ if (s5m87xx->irq_data) {
+ info->irq = regmap_irq_get_virq(s5m87xx->irq_data, alarm_irq);
+ if (info->irq <= 0) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "Failed to get virtual IRQ %d\n",
+ alarm_irq);
+ goto err;
+ }
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ ret = s5m8767_rtc_init_reg(info);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc",
+ &s5m_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ goto err;
+ }
+
+ if (!info->irq) {
+ dev_info(&pdev->dev, "Alarm IRQ not available\n");
+ return 0;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+ s5m_rtc_alarm_irq, 0, "rtc-alarm0",
+ info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
+ info->irq, ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ i2c_unregister_device(info->i2c);
+
+ return ret;
+}
+
+static int s5m_rtc_remove(struct platform_device *pdev)
+{
+ struct s5m_rtc_info *info = platform_get_drvdata(pdev);
+
+ i2c_unregister_device(info->i2c);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int s5m_rtc_resume(struct device *dev)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (info->irq && device_may_wakeup(dev))
+ ret = disable_irq_wake(info->irq);
+
+ return ret;
+}
+
+static int s5m_rtc_suspend(struct device *dev)
+{
+ struct s5m_rtc_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (info->irq && device_may_wakeup(dev))
+ ret = enable_irq_wake(info->irq);
+
+ return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume);
+
+static const struct platform_device_id s5m_rtc_id[] = {
+ { "s5m-rtc", S5M8767X },
+ { "s2mps13-rtc", S2MPS13X },
+ { "s2mps14-rtc", S2MPS14X },
+ { },
+};
+
+static struct platform_driver s5m_rtc_driver = {
+ .driver = {
+ .name = "s5m-rtc",
+ .pm = &s5m_rtc_pm_ops,
+ },
+ .probe = s5m_rtc_probe,
+ .remove = s5m_rtc_remove,
+ .id_table = s5m_rtc_id,
+};
+
+module_platform_driver(s5m_rtc_driver);
+
+/* Module information */
+MODULE_AUTHOR("Sangbeom Kim <sbkim73@samsung.com>");
+MODULE_DESCRIPTION("Samsung S5M/S2MPS14 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:s5m-rtc");
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
new file mode 100644
index 000000000..b6e1ca08c
--- /dev/null
+++ b/drivers/rtc/rtc-sa1100.c
@@ -0,0 +1,364 @@
+/*
+ * Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx
+ *
+ * Copyright (c) 2000 Nils Faerber
+ *
+ * Based on rtc.c by Paul Gortmaker
+ *
+ * Original Driver by Nils Faerber <nils@kernelconcepts.de>
+ *
+ * Modifications from:
+ * CIH <cih@coventive.com>
+ * Nicolas Pitre <nico@fluxnic.net>
+ * Andrew Christian <andrew.christian@hp.com>
+ *
+ * Converted to the RTC subsystem and Driver Model
+ * by Richard Purdie <rpurdie@rpsys.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
+#include <mach/regs-rtc.h>
+#endif
+
+#define RTC_DEF_DIVIDER (32768 - 1)
+#define RTC_DEF_TRIM 0
+#define RTC_FREQ 1024
+
+struct sa1100_rtc {
+ spinlock_t lock;
+ int irq_1hz;
+ int irq_alarm;
+ struct rtc_device *rtc;
+ struct clk *clk;
+};
+
+static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev_id);
+ struct rtc_device *rtc = info->rtc;
+ unsigned int rtsr;
+ unsigned long events = 0;
+
+ spin_lock(&info->lock);
+
+ rtsr = RTSR;
+ /* clear interrupt sources */
+ RTSR = 0;
+ /* Fix for a nasty initialization problem the in SA11xx RTSR register.
+ * See also the comments in sa1100_rtc_probe(). */
+ if (rtsr & (RTSR_ALE | RTSR_HZE)) {
+ /* This is the original code, before there was the if test
+ * above. This code does not clear interrupts that were not
+ * enabled. */
+ RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2);
+ } else {
+ /* For some reason, it is possible to enter this routine
+ * without interruptions enabled, it has been tested with
+ * several units (Bug in SA11xx chip?).
+ *
+ * This situation leads to an infinite "loop" of interrupt
+ * routine calling and as a result the processor seems to
+ * lock on its first call to open(). */
+ RTSR = RTSR_AL | RTSR_HZ;
+ }
+
+ /* clear alarm interrupt if it has occurred */
+ if (rtsr & RTSR_AL)
+ rtsr &= ~RTSR_ALE;
+ RTSR = rtsr & (RTSR_ALE | RTSR_HZE);
+
+ /* update irq data & counter */
+ if (rtsr & RTSR_AL)
+ events |= RTC_AF | RTC_IRQF;
+ if (rtsr & RTSR_HZ)
+ events |= RTC_UF | RTC_IRQF;
+
+ rtc_update_irq(rtc, 1, events);
+
+ spin_unlock(&info->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int sa1100_rtc_open(struct device *dev)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+ struct rtc_device *rtc = info->rtc;
+ int ret;
+
+ ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev);
+ if (ret) {
+ dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz);
+ goto fail_ui;
+ }
+ ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev);
+ if (ret) {
+ dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm);
+ goto fail_ai;
+ }
+ rtc->max_user_freq = RTC_FREQ;
+ rtc_irq_set_freq(rtc, NULL, RTC_FREQ);
+
+ return 0;
+
+ fail_ai:
+ free_irq(info->irq_1hz, dev);
+ fail_ui:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static void sa1100_rtc_release(struct device *dev)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+
+ spin_lock_irq(&info->lock);
+ RTSR = 0;
+ spin_unlock_irq(&info->lock);
+
+ free_irq(info->irq_alarm, dev);
+ free_irq(info->irq_1hz, dev);
+}
+
+static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+
+ spin_lock_irq(&info->lock);
+ if (enabled)
+ RTSR |= RTSR_ALE;
+ else
+ RTSR &= ~RTSR_ALE;
+ spin_unlock_irq(&info->lock);
+ return 0;
+}
+
+static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ rtc_time_to_tm(RCNR, tm);
+ return 0;
+}
+
+static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long time;
+ int ret;
+
+ ret = rtc_tm_to_time(tm, &time);
+ if (ret == 0)
+ RCNR = time;
+ return ret;
+}
+
+static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ u32 rtsr;
+
+ rtsr = RTSR;
+ alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0;
+ alrm->pending = (rtsr & RTSR_AL) ? 1 : 0;
+ return 0;
+}
+
+static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+ unsigned long time;
+ int ret;
+
+ spin_lock_irq(&info->lock);
+ ret = rtc_tm_to_time(&alrm->time, &time);
+ if (ret != 0)
+ goto out;
+ RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
+ RTAR = time;
+ if (alrm->enabled)
+ RTSR |= RTSR_ALE;
+ else
+ RTSR &= ~RTSR_ALE;
+out:
+ spin_unlock_irq(&info->lock);
+
+ return ret;
+}
+
+static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ seq_printf(seq, "trim/divider\t\t: 0x%08x\n", (u32) RTTR);
+ seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", (u32)RTSR);
+
+ return 0;
+}
+
+static const struct rtc_class_ops sa1100_rtc_ops = {
+ .open = sa1100_rtc_open,
+ .release = sa1100_rtc_release,
+ .read_time = sa1100_rtc_read_time,
+ .set_time = sa1100_rtc_set_time,
+ .read_alarm = sa1100_rtc_read_alarm,
+ .set_alarm = sa1100_rtc_set_alarm,
+ .proc = sa1100_rtc_proc,
+ .alarm_irq_enable = sa1100_rtc_alarm_irq_enable,
+};
+
+static int sa1100_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct sa1100_rtc *info;
+ int irq_1hz, irq_alarm, ret = 0;
+
+ irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz");
+ irq_alarm = platform_get_irq_byname(pdev, "rtc alarm");
+ if (irq_1hz < 0 || irq_alarm < 0)
+ return -ENODEV;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct sa1100_rtc), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock source\n");
+ return PTR_ERR(info->clk);
+ }
+ info->irq_1hz = irq_1hz;
+ info->irq_alarm = irq_alarm;
+ spin_lock_init(&info->lock);
+ platform_set_drvdata(pdev, info);
+
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ return ret;
+ /*
+ * According to the manual we should be able to let RTTR be zero
+ * and then a default diviser for a 32.768KHz clock is used.
+ * Apparently this doesn't work, at least for my SA1110 rev 5.
+ * If the clock divider is uninitialized then reset it to the
+ * default value to get the 1Hz clock.
+ */
+ if (RTTR == 0) {
+ RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
+ dev_warn(&pdev->dev, "warning: "
+ "initializing default clock divider/trim value\n");
+ /* The current RTC value probably doesn't make sense either */
+ RCNR = 0;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &sa1100_rtc_ops,
+ THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto err_dev;
+ }
+ info->rtc = rtc;
+
+ /* Fix for a nasty initialization problem the in SA11xx RTSR register.
+ * See also the comments in sa1100_rtc_interrupt().
+ *
+ * Sometimes bit 1 of the RTSR (RTSR_HZ) will wake up 1, which means an
+ * interrupt pending, even though interrupts were never enabled.
+ * In this case, this bit it must be reset before enabling
+ * interruptions to avoid a nonexistent interrupt to occur.
+ *
+ * In principle, the same problem would apply to bit 0, although it has
+ * never been observed to happen.
+ *
+ * This issue is addressed both here and in sa1100_rtc_interrupt().
+ * If the issue is not addressed here, in the times when the processor
+ * wakes up with the bit set there will be one spurious interrupt.
+ *
+ * The issue is also dealt with in sa1100_rtc_interrupt() to be on the
+ * safe side, once the condition that lead to this strange
+ * initialization is unknown and could in principle happen during
+ * normal processing.
+ *
+ * Notice that clearing bit 1 and 0 is accomplished by writting ONES to
+ * the corresponding bits in RTSR. */
+ RTSR = RTSR_AL | RTSR_HZ;
+
+ return 0;
+err_dev:
+ clk_disable_unprepare(info->clk);
+ return ret;
+}
+
+static int sa1100_rtc_remove(struct platform_device *pdev)
+{
+ struct sa1100_rtc *info = platform_get_drvdata(pdev);
+
+ if (info)
+ clk_disable_unprepare(info->clk);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sa1100_rtc_suspend(struct device *dev)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+ if (device_may_wakeup(dev))
+ enable_irq_wake(info->irq_alarm);
+ return 0;
+}
+
+static int sa1100_rtc_resume(struct device *dev)
+{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+ if (device_may_wakeup(dev))
+ disable_irq_wake(info->irq_alarm);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sa1100_rtc_pm_ops, sa1100_rtc_suspend,
+ sa1100_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id sa1100_rtc_dt_ids[] = {
+ { .compatible = "mrvl,sa1100-rtc", },
+ { .compatible = "mrvl,mmp-rtc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids);
+#endif
+
+static struct platform_driver sa1100_rtc_driver = {
+ .probe = sa1100_rtc_probe,
+ .remove = sa1100_rtc_remove,
+ .driver = {
+ .name = "sa1100-rtc",
+ .pm = &sa1100_rtc_pm_ops,
+ .of_match_table = of_match_ptr(sa1100_rtc_dt_ids),
+ },
+};
+
+module_platform_driver(sa1100_rtc_driver);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sa1100-rtc");
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
new file mode 100644
index 000000000..2b81dd4ba
--- /dev/null
+++ b/drivers/rtc/rtc-sh.c
@@ -0,0 +1,798 @@
+/*
+ * SuperH On-Chip RTC Support
+ *
+ * Copyright (C) 2006 - 2009 Paul Mundt
+ * Copyright (C) 2006 Jamie Lenehan
+ * Copyright (C) 2008 Angelo Castello
+ *
+ * Based on the old arch/sh/kernel/cpu/rtc.c by:
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <asm/rtc.h>
+
+#define DRV_NAME "sh-rtc"
+#define DRV_VERSION "0.2.3"
+
+#define RTC_REG(r) ((r) * rtc_reg_size)
+
+#define R64CNT RTC_REG(0)
+
+#define RSECCNT RTC_REG(1) /* RTC sec */
+#define RMINCNT RTC_REG(2) /* RTC min */
+#define RHRCNT RTC_REG(3) /* RTC hour */
+#define RWKCNT RTC_REG(4) /* RTC week */
+#define RDAYCNT RTC_REG(5) /* RTC day */
+#define RMONCNT RTC_REG(6) /* RTC month */
+#define RYRCNT RTC_REG(7) /* RTC year */
+#define RSECAR RTC_REG(8) /* ALARM sec */
+#define RMINAR RTC_REG(9) /* ALARM min */
+#define RHRAR RTC_REG(10) /* ALARM hour */
+#define RWKAR RTC_REG(11) /* ALARM week */
+#define RDAYAR RTC_REG(12) /* ALARM day */
+#define RMONAR RTC_REG(13) /* ALARM month */
+#define RCR1 RTC_REG(14) /* Control */
+#define RCR2 RTC_REG(15) /* Control */
+
+/*
+ * Note on RYRAR and RCR3: Up until this point most of the register
+ * definitions are consistent across all of the available parts. However,
+ * the placement of the optional RYRAR and RCR3 (the RYRAR control
+ * register used to control RYRCNT/RYRAR compare) varies considerably
+ * across various parts, occasionally being mapped in to a completely
+ * unrelated address space. For proper RYRAR support a separate resource
+ * would have to be handed off, but as this is purely optional in
+ * practice, we simply opt not to support it, thereby keeping the code
+ * quite a bit more simplified.
+ */
+
+/* ALARM Bits - or with BCD encoded value */
+#define AR_ENB 0x80 /* Enable for alarm cmp */
+
+/* Period Bits */
+#define PF_HP 0x100 /* Enable Half Period to support 8,32,128Hz */
+#define PF_COUNT 0x200 /* Half periodic counter */
+#define PF_OXS 0x400 /* Periodic One x Second */
+#define PF_KOU 0x800 /* Kernel or User periodic request 1=kernel */
+#define PF_MASK 0xf00
+
+/* RCR1 Bits */
+#define RCR1_CF 0x80 /* Carry Flag */
+#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
+#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
+#define RCR1_AF 0x01 /* Alarm Flag */
+
+/* RCR2 Bits */
+#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
+#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
+#define RCR2_RTCEN 0x08 /* ENable RTC */
+#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
+#define RCR2_RESET 0x02 /* Reset bit */
+#define RCR2_START 0x01 /* Start bit */
+
+struct sh_rtc {
+ void __iomem *regbase;
+ unsigned long regsize;
+ struct resource *res;
+ int alarm_irq;
+ int periodic_irq;
+ int carry_irq;
+ struct clk *clk;
+ struct rtc_device *rtc_dev;
+ spinlock_t lock;
+ unsigned long capabilities; /* See asm/rtc.h for cap bits */
+ unsigned short periodic_freq;
+};
+
+static int __sh_rtc_interrupt(struct sh_rtc *rtc)
+{
+ unsigned int tmp, pending;
+
+ tmp = readb(rtc->regbase + RCR1);
+ pending = tmp & RCR1_CF;
+ tmp &= ~RCR1_CF;
+ writeb(tmp, rtc->regbase + RCR1);
+
+ /* Users have requested One x Second IRQ */
+ if (pending && rtc->periodic_freq & PF_OXS)
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
+
+ return pending;
+}
+
+static int __sh_rtc_alarm(struct sh_rtc *rtc)
+{
+ unsigned int tmp, pending;
+
+ tmp = readb(rtc->regbase + RCR1);
+ pending = tmp & RCR1_AF;
+ tmp &= ~(RCR1_AF | RCR1_AIE);
+ writeb(tmp, rtc->regbase + RCR1);
+
+ if (pending)
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
+
+ return pending;
+}
+
+static int __sh_rtc_periodic(struct sh_rtc *rtc)
+{
+ struct rtc_device *rtc_dev = rtc->rtc_dev;
+ struct rtc_task *irq_task;
+ unsigned int tmp, pending;
+
+ tmp = readb(rtc->regbase + RCR2);
+ pending = tmp & RCR2_PEF;
+ tmp &= ~RCR2_PEF;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ if (!pending)
+ return 0;
+
+ /* Half period enabled than one skipped and the next notified */
+ if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT))
+ rtc->periodic_freq &= ~PF_COUNT;
+ else {
+ if (rtc->periodic_freq & PF_HP)
+ rtc->periodic_freq |= PF_COUNT;
+ if (rtc->periodic_freq & PF_KOU) {
+ spin_lock(&rtc_dev->irq_task_lock);
+ irq_task = rtc_dev->irq_task;
+ if (irq_task)
+ irq_task->func(irq_task->private_data);
+ spin_unlock(&rtc_dev->irq_task_lock);
+ } else
+ rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF);
+ }
+
+ return pending;
+}
+
+static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_interrupt(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_alarm(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_periodic(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t sh_rtc_shared(int irq, void *dev_id)
+{
+ struct sh_rtc *rtc = dev_id;
+ int ret;
+
+ spin_lock(&rtc->lock);
+ ret = __sh_rtc_interrupt(rtc);
+ ret |= __sh_rtc_alarm(rtc);
+ ret |= __sh_rtc_periodic(rtc);
+ spin_unlock(&rtc->lock);
+
+ return IRQ_RETVAL(ret);
+}
+
+static int sh_rtc_irq_set_state(struct device *dev, int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR2);
+
+ if (enable) {
+ rtc->periodic_freq |= PF_KOU;
+ tmp &= ~RCR2_PEF; /* Clear PES bit */
+ tmp |= (rtc->periodic_freq & ~PF_HP); /* Set PES2-0 */
+ } else {
+ rtc->periodic_freq &= ~PF_KOU;
+ tmp &= ~(RCR2_PESMASK | RCR2_PEF);
+ }
+
+ writeb(tmp, rtc->regbase + RCR2);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static int sh_rtc_irq_set_freq(struct device *dev, int freq)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ int tmp, ret = 0;
+
+ spin_lock_irq(&rtc->lock);
+ tmp = rtc->periodic_freq & PF_MASK;
+
+ switch (freq) {
+ case 0:
+ rtc->periodic_freq = 0x00;
+ break;
+ case 1:
+ rtc->periodic_freq = 0x60;
+ break;
+ case 2:
+ rtc->periodic_freq = 0x50;
+ break;
+ case 4:
+ rtc->periodic_freq = 0x40;
+ break;
+ case 8:
+ rtc->periodic_freq = 0x30 | PF_HP;
+ break;
+ case 16:
+ rtc->periodic_freq = 0x30;
+ break;
+ case 32:
+ rtc->periodic_freq = 0x20 | PF_HP;
+ break;
+ case 64:
+ rtc->periodic_freq = 0x20;
+ break;
+ case 128:
+ rtc->periodic_freq = 0x10 | PF_HP;
+ break;
+ case 256:
+ rtc->periodic_freq = 0x10;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+
+ if (ret == 0)
+ rtc->periodic_freq |= tmp;
+
+ spin_unlock_irq(&rtc->lock);
+ return ret;
+}
+
+static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (enable)
+ tmp |= RCR1_AIE;
+ else
+ tmp &= ~RCR1_AIE;
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
+static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ tmp = readb(rtc->regbase + RCR1);
+ seq_printf(seq, "carry_IRQ\t: %s\n", (tmp & RCR1_CIE) ? "yes" : "no");
+
+ tmp = readb(rtc->regbase + RCR2);
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (tmp & RCR2_PESMASK) ? "yes" : "no");
+
+ return 0;
+}
+
+static inline void sh_rtc_setcie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (!enable)
+ tmp &= ~RCR1_CIE;
+ else
+ tmp |= RCR1_CIE;
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
+static int sh_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ sh_rtc_setaie(dev, enabled);
+ return 0;
+}
+
+static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int sec128, sec2, yr, yr100, cf_bit;
+
+ do {
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+ tmp &= ~RCR1_CF; /* Clear CF-bit */
+ tmp |= RCR1_CIE;
+ writeb(tmp, rtc->regbase + RCR1);
+
+ sec128 = readb(rtc->regbase + R64CNT);
+
+ tm->tm_sec = bcd2bin(readb(rtc->regbase + RSECCNT));
+ tm->tm_min = bcd2bin(readb(rtc->regbase + RMINCNT));
+ tm->tm_hour = bcd2bin(readb(rtc->regbase + RHRCNT));
+ tm->tm_wday = bcd2bin(readb(rtc->regbase + RWKCNT));
+ tm->tm_mday = bcd2bin(readb(rtc->regbase + RDAYCNT));
+ tm->tm_mon = bcd2bin(readb(rtc->regbase + RMONCNT)) - 1;
+
+ if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
+ yr = readw(rtc->regbase + RYRCNT);
+ yr100 = bcd2bin(yr >> 8);
+ yr &= 0xff;
+ } else {
+ yr = readb(rtc->regbase + RYRCNT);
+ yr100 = bcd2bin((yr == 0x99) ? 0x19 : 0x20);
+ }
+
+ tm->tm_year = (yr100 * 100 + bcd2bin(yr)) - 1900;
+
+ sec2 = readb(rtc->regbase + R64CNT);
+ cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF;
+
+ spin_unlock_irq(&rtc->lock);
+ } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
+
+#if RTC_BIT_INVERTED != 0
+ if ((sec128 & RTC_BIT_INVERTED))
+ tm->tm_sec--;
+#endif
+
+ /* only keep the carry interrupt enabled if UIE is on */
+ if (!(rtc->periodic_freq & PF_OXS))
+ sh_rtc_setcie(dev, 0);
+
+ dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
+
+ return rtc_valid_tm(tm);
+}
+
+static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int tmp;
+ int year;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Reset pre-scaler & stop RTC */
+ tmp = readb(rtc->regbase + RCR2);
+ tmp |= RCR2_RESET;
+ tmp &= ~RCR2_START;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ writeb(bin2bcd(tm->tm_sec), rtc->regbase + RSECCNT);
+ writeb(bin2bcd(tm->tm_min), rtc->regbase + RMINCNT);
+ writeb(bin2bcd(tm->tm_hour), rtc->regbase + RHRCNT);
+ writeb(bin2bcd(tm->tm_wday), rtc->regbase + RWKCNT);
+ writeb(bin2bcd(tm->tm_mday), rtc->regbase + RDAYCNT);
+ writeb(bin2bcd(tm->tm_mon + 1), rtc->regbase + RMONCNT);
+
+ if (rtc->capabilities & RTC_CAP_4_DIGIT_YEAR) {
+ year = (bin2bcd((tm->tm_year + 1900) / 100) << 8) |
+ bin2bcd(tm->tm_year % 100);
+ writew(year, rtc->regbase + RYRCNT);
+ } else {
+ year = tm->tm_year % 100;
+ writeb(bin2bcd(year), rtc->regbase + RYRCNT);
+ }
+
+ /* Start RTC */
+ tmp = readb(rtc->regbase + RCR2);
+ tmp &= ~RCR2_RESET;
+ tmp |= RCR2_RTCEN | RCR2_START;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off)
+{
+ unsigned int byte;
+ int value = 0xff; /* return 0xff for ignored values */
+
+ byte = readb(rtc->regbase + reg_off);
+ if (byte & AR_ENB) {
+ byte &= ~AR_ENB; /* strip the enable bit */
+ value = bcd2bin(byte);
+ }
+
+ return value;
+}
+
+static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ struct rtc_time *tm = &wkalrm->time;
+
+ spin_lock_irq(&rtc->lock);
+
+ tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR);
+ tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR);
+ tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR);
+ tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR);
+ tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR);
+ tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR);
+ if (tm->tm_mon > 0)
+ tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
+ tm->tm_year = 0xffff;
+
+ wkalrm->enabled = (readb(rtc->regbase + RCR1) & RCR1_AIE) ? 1 : 0;
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc,
+ int value, int reg_off)
+{
+ /* < 0 for a value that is ignored */
+ if (value < 0)
+ writeb(0, rtc->regbase + reg_off);
+ else
+ writeb(bin2bcd(value) | AR_ENB, rtc->regbase + reg_off);
+}
+
+static int sh_rtc_check_alarm(struct rtc_time *tm)
+{
+ /*
+ * The original rtc says anything > 0xc0 is "don't care" or "match
+ * all" - most users use 0xff but rtc-dev uses -1 for the same thing.
+ * The original rtc doesn't support years - some things use -1 and
+ * some 0xffff. We use -1 to make out tests easier.
+ */
+ if (tm->tm_year == 0xffff)
+ tm->tm_year = -1;
+ if (tm->tm_mon >= 0xff)
+ tm->tm_mon = -1;
+ if (tm->tm_mday >= 0xff)
+ tm->tm_mday = -1;
+ if (tm->tm_wday >= 0xff)
+ tm->tm_wday = -1;
+ if (tm->tm_hour >= 0xff)
+ tm->tm_hour = -1;
+ if (tm->tm_min >= 0xff)
+ tm->tm_min = -1;
+ if (tm->tm_sec >= 0xff)
+ tm->tm_sec = -1;
+
+ if (tm->tm_year > 9999 ||
+ tm->tm_mon >= 12 ||
+ tm->tm_mday == 0 || tm->tm_mday >= 32 ||
+ tm->tm_wday >= 7 ||
+ tm->tm_hour >= 24 ||
+ tm->tm_min >= 60 ||
+ tm->tm_sec >= 60)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int rcr1;
+ struct rtc_time *tm = &wkalrm->time;
+ int mon, err;
+
+ err = sh_rtc_check_alarm(tm);
+ if (unlikely(err < 0))
+ return err;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* disable alarm interrupt and clear the alarm flag */
+ rcr1 = readb(rtc->regbase + RCR1);
+ rcr1 &= ~(RCR1_AF | RCR1_AIE);
+ writeb(rcr1, rtc->regbase + RCR1);
+
+ /* set alarm time */
+ sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR);
+ sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR);
+ mon = tm->tm_mon;
+ if (mon >= 0)
+ mon += 1;
+ sh_rtc_write_alarm_value(rtc, mon, RMONAR);
+
+ if (wkalrm->enabled) {
+ rcr1 |= RCR1_AIE;
+ writeb(rcr1, rtc->regbase + RCR1);
+ }
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static struct rtc_class_ops sh_rtc_ops = {
+ .read_time = sh_rtc_read_time,
+ .set_time = sh_rtc_set_time,
+ .read_alarm = sh_rtc_read_alarm,
+ .set_alarm = sh_rtc_set_alarm,
+ .proc = sh_rtc_proc,
+ .alarm_irq_enable = sh_rtc_alarm_irq_enable,
+};
+
+static int __init sh_rtc_probe(struct platform_device *pdev)
+{
+ struct sh_rtc *rtc;
+ struct resource *res;
+ struct rtc_time r;
+ char clk_name[6];
+ int clk_id, ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (unlikely(!rtc))
+ return -ENOMEM;
+
+ spin_lock_init(&rtc->lock);
+
+ /* get periodic/carry/alarm irqs */
+ ret = platform_get_irq(pdev, 0);
+ if (unlikely(ret <= 0)) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return -ENOENT;
+ }
+
+ rtc->periodic_irq = ret;
+ rtc->carry_irq = platform_get_irq(pdev, 1);
+ rtc->alarm_irq = platform_get_irq(pdev, 2);
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (unlikely(res == NULL)) {
+ dev_err(&pdev->dev, "No IO resource\n");
+ return -ENOENT;
+ }
+
+ rtc->regsize = resource_size(res);
+
+ rtc->res = devm_request_mem_region(&pdev->dev, res->start,
+ rtc->regsize, pdev->name);
+ if (unlikely(!rtc->res))
+ return -EBUSY;
+
+ rtc->regbase = devm_ioremap_nocache(&pdev->dev, rtc->res->start,
+ rtc->regsize);
+ if (unlikely(!rtc->regbase))
+ return -EINVAL;
+
+ clk_id = pdev->id;
+ /* With a single device, the clock id is still "rtc0" */
+ if (clk_id < 0)
+ clk_id = 0;
+
+ snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id);
+
+ rtc->clk = devm_clk_get(&pdev->dev, clk_name);
+ if (IS_ERR(rtc->clk)) {
+ /*
+ * No error handling for rtc->clk intentionally, not all
+ * platforms will have a unique clock for the RTC, and
+ * the clk API can handle the struct clk pointer being
+ * NULL.
+ */
+ rtc->clk = NULL;
+ }
+
+ clk_enable(rtc->clk);
+
+ rtc->capabilities = RTC_DEF_CAPABILITIES;
+ if (dev_get_platdata(&pdev->dev)) {
+ struct sh_rtc_platform_info *pinfo =
+ dev_get_platdata(&pdev->dev);
+
+ /*
+ * Some CPUs have special capabilities in addition to the
+ * default set. Add those in here.
+ */
+ rtc->capabilities |= pinfo->capabilities;
+ }
+
+ if (rtc->carry_irq <= 0) {
+ /* register shared periodic/carry/alarm irq */
+ ret = devm_request_irq(&pdev->dev, rtc->periodic_irq,
+ sh_rtc_shared, 0, "sh-rtc", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request IRQ failed with %d, IRQ %d\n", ret,
+ rtc->periodic_irq);
+ goto err_unmap;
+ }
+ } else {
+ /* register periodic/carry/alarm irqs */
+ ret = devm_request_irq(&pdev->dev, rtc->periodic_irq,
+ sh_rtc_periodic, 0, "sh-rtc period", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request period IRQ failed with %d, IRQ %d\n",
+ ret, rtc->periodic_irq);
+ goto err_unmap;
+ }
+
+ ret = devm_request_irq(&pdev->dev, rtc->carry_irq,
+ sh_rtc_interrupt, 0, "sh-rtc carry", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request carry IRQ failed with %d, IRQ %d\n",
+ ret, rtc->carry_irq);
+ goto err_unmap;
+ }
+
+ ret = devm_request_irq(&pdev->dev, rtc->alarm_irq,
+ sh_rtc_alarm, 0, "sh-rtc alarm", rtc);
+ if (unlikely(ret)) {
+ dev_err(&pdev->dev,
+ "request alarm IRQ failed with %d, IRQ %d\n",
+ ret, rtc->alarm_irq);
+ goto err_unmap;
+ }
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ /* everything disabled by default */
+ sh_rtc_irq_set_freq(&pdev->dev, 0);
+ sh_rtc_irq_set_state(&pdev->dev, 0);
+ sh_rtc_setaie(&pdev->dev, 0);
+ sh_rtc_setcie(&pdev->dev, 0);
+
+ rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, "sh",
+ &sh_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc_dev)) {
+ ret = PTR_ERR(rtc->rtc_dev);
+ goto err_unmap;
+ }
+
+ rtc->rtc_dev->max_user_freq = 256;
+
+ /* reset rtc to epoch 0 if time is invalid */
+ if (rtc_read_time(rtc->rtc_dev, &r) < 0) {
+ rtc_time_to_tm(0, &r);
+ rtc_set_time(rtc->rtc_dev, &r);
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+ return 0;
+
+err_unmap:
+ clk_disable(rtc->clk);
+
+ return ret;
+}
+
+static int __exit sh_rtc_remove(struct platform_device *pdev)
+{
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+
+ sh_rtc_irq_set_state(&pdev->dev, 0);
+
+ sh_rtc_setaie(&pdev->dev, 0);
+ sh_rtc_setcie(&pdev->dev, 0);
+
+ clk_disable(rtc->clk);
+
+ return 0;
+}
+
+static void sh_rtc_set_irq_wake(struct device *dev, int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+
+ irq_set_irq_wake(rtc->periodic_irq, enabled);
+
+ if (rtc->carry_irq > 0) {
+ irq_set_irq_wake(rtc->carry_irq, enabled);
+ irq_set_irq_wake(rtc->alarm_irq, enabled);
+ }
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sh_rtc_suspend(struct device *dev)
+{
+ if (device_may_wakeup(dev))
+ sh_rtc_set_irq_wake(dev, 1);
+
+ return 0;
+}
+
+static int sh_rtc_resume(struct device *dev)
+{
+ if (device_may_wakeup(dev))
+ sh_rtc_set_irq_wake(dev, 0);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume);
+
+static struct platform_driver sh_rtc_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &sh_rtc_pm_ops,
+ },
+ .remove = __exit_p(sh_rtc_remove),
+};
+
+module_platform_driver_probe(sh_rtc_platform_driver, sh_rtc_probe);
+
+MODULE_DESCRIPTION("SuperH on-chip RTC driver");
+MODULE_VERSION(DRV_VERSION);
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, "
+ "Jamie Lenehan <lenehan@twibble.org>, "
+ "Angelo Castello <angelo.castello@st.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/rtc/rtc-sirfsoc.c b/drivers/rtc/rtc-sirfsoc.c
new file mode 100644
index 000000000..edc3b4328
--- /dev/null
+++ b/drivers/rtc/rtc-sirfsoc.c
@@ -0,0 +1,458 @@
+/*
+ * SiRFSoC Real Time Clock interface for Linux
+ *
+ * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/rtc/sirfsoc_rtciobrg.h>
+
+
+#define RTC_CN 0x00
+#define RTC_ALARM0 0x04
+#define RTC_ALARM1 0x18
+#define RTC_STATUS 0x08
+#define RTC_SW_VALUE 0x40
+#define SIRFSOC_RTC_AL1E (1<<6)
+#define SIRFSOC_RTC_AL1 (1<<4)
+#define SIRFSOC_RTC_HZE (1<<3)
+#define SIRFSOC_RTC_AL0E (1<<2)
+#define SIRFSOC_RTC_HZ (1<<1)
+#define SIRFSOC_RTC_AL0 (1<<0)
+#define RTC_DIV 0x0c
+#define RTC_DEEP_CTRL 0x14
+#define RTC_CLOCK_SWITCH 0x1c
+#define SIRFSOC_RTC_CLK 0x03 /* others are reserved */
+
+/* Refer to RTC DIV switch */
+#define RTC_HZ 16
+
+/* This macro is also defined in arch/arm/plat-sirfsoc/cpu.c */
+#define RTC_SHIFT 4
+
+#define INTR_SYSRTC_CN 0x48
+
+struct sirfsoc_rtc_drv {
+ struct rtc_device *rtc;
+ u32 rtc_base;
+ u32 irq;
+ unsigned irq_wake;
+ /* Overflow for every 8 years extra time */
+ u32 overflow_rtc;
+ spinlock_t lock;
+#ifdef CONFIG_PM
+ u32 saved_counter;
+ u32 saved_overflow_rtc;
+#endif
+};
+
+static int sirfsoc_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *alrm)
+{
+ unsigned long rtc_alarm, rtc_count;
+ struct sirfsoc_rtc_drv *rtcdrv;
+
+ rtcdrv = dev_get_drvdata(dev);
+
+ spin_lock_irq(&rtcdrv->lock);
+
+ rtc_count = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN);
+
+ rtc_alarm = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_ALARM0);
+ memset(alrm, 0, sizeof(struct rtc_wkalrm));
+
+ /*
+ * assume alarm interval not beyond one round counter overflow_rtc:
+ * 0->0xffffffff
+ */
+ /* if alarm is in next overflow cycle */
+ if (rtc_count > rtc_alarm)
+ rtc_time_to_tm((rtcdrv->overflow_rtc + 1)
+ << (BITS_PER_LONG - RTC_SHIFT)
+ | rtc_alarm >> RTC_SHIFT, &(alrm->time));
+ else
+ rtc_time_to_tm(rtcdrv->overflow_rtc
+ << (BITS_PER_LONG - RTC_SHIFT)
+ | rtc_alarm >> RTC_SHIFT, &(alrm->time));
+ if (sirfsoc_rtc_iobrg_readl(
+ rtcdrv->rtc_base + RTC_STATUS) & SIRFSOC_RTC_AL0E)
+ alrm->enabled = 1;
+
+ spin_unlock_irq(&rtcdrv->lock);
+
+ return 0;
+}
+
+static int sirfsoc_rtc_set_alarm(struct device *dev,
+ struct rtc_wkalrm *alrm)
+{
+ unsigned long rtc_status_reg, rtc_alarm;
+ struct sirfsoc_rtc_drv *rtcdrv;
+ rtcdrv = dev_get_drvdata(dev);
+
+ if (alrm->enabled) {
+ rtc_tm_to_time(&(alrm->time), &rtc_alarm);
+
+ spin_lock_irq(&rtcdrv->lock);
+
+ rtc_status_reg = sirfsoc_rtc_iobrg_readl(
+ rtcdrv->rtc_base + RTC_STATUS);
+ if (rtc_status_reg & SIRFSOC_RTC_AL0E) {
+ /*
+ * An ongoing alarm in progress - ingore it and not
+ * to return EBUSY
+ */
+ dev_info(dev, "An old alarm was set, will be replaced by a new one\n");
+ }
+
+ sirfsoc_rtc_iobrg_writel(
+ rtc_alarm << RTC_SHIFT, rtcdrv->rtc_base + RTC_ALARM0);
+ rtc_status_reg &= ~0x07; /* mask out the lower status bits */
+ /*
+ * This bit RTC_AL sets it as a wake-up source for Sleep Mode
+ * Writing 1 into this bit will clear it
+ */
+ rtc_status_reg |= SIRFSOC_RTC_AL0;
+ /* enable the RTC alarm interrupt */
+ rtc_status_reg |= SIRFSOC_RTC_AL0E;
+ sirfsoc_rtc_iobrg_writel(
+ rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS);
+
+ spin_unlock_irq(&rtcdrv->lock);
+ } else {
+ /*
+ * if this function was called with enabled=0
+ * then it could mean that the application is
+ * trying to cancel an ongoing alarm
+ */
+ spin_lock_irq(&rtcdrv->lock);
+
+ rtc_status_reg = sirfsoc_rtc_iobrg_readl(
+ rtcdrv->rtc_base + RTC_STATUS);
+ if (rtc_status_reg & SIRFSOC_RTC_AL0E) {
+ /* clear the RTC status register's alarm bit */
+ rtc_status_reg &= ~0x07;
+ /* write 1 into SIRFSOC_RTC_AL0 to force a clear */
+ rtc_status_reg |= (SIRFSOC_RTC_AL0);
+ /* Clear the Alarm enable bit */
+ rtc_status_reg &= ~(SIRFSOC_RTC_AL0E);
+
+ sirfsoc_rtc_iobrg_writel(rtc_status_reg,
+ rtcdrv->rtc_base + RTC_STATUS);
+ }
+
+ spin_unlock_irq(&rtcdrv->lock);
+ }
+
+ return 0;
+}
+
+static int sirfsoc_rtc_read_time(struct device *dev,
+ struct rtc_time *tm)
+{
+ unsigned long tmp_rtc = 0;
+ struct sirfsoc_rtc_drv *rtcdrv;
+ rtcdrv = dev_get_drvdata(dev);
+ /*
+ * This patch is taken from WinCE - Need to validate this for
+ * correctness. To work around sirfsoc RTC counter double sync logic
+ * fail, read several times to make sure get stable value.
+ */
+ do {
+ tmp_rtc = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN);
+ cpu_relax();
+ } while (tmp_rtc != sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN));
+
+ rtc_time_to_tm(rtcdrv->overflow_rtc << (BITS_PER_LONG - RTC_SHIFT) |
+ tmp_rtc >> RTC_SHIFT, tm);
+ return 0;
+}
+
+static int sirfsoc_rtc_set_time(struct device *dev,
+ struct rtc_time *tm)
+{
+ unsigned long rtc_time;
+ struct sirfsoc_rtc_drv *rtcdrv;
+ rtcdrv = dev_get_drvdata(dev);
+
+ rtc_tm_to_time(tm, &rtc_time);
+
+ rtcdrv->overflow_rtc = rtc_time >> (BITS_PER_LONG - RTC_SHIFT);
+
+ sirfsoc_rtc_iobrg_writel(rtcdrv->overflow_rtc,
+ rtcdrv->rtc_base + RTC_SW_VALUE);
+ sirfsoc_rtc_iobrg_writel(
+ rtc_time << RTC_SHIFT, rtcdrv->rtc_base + RTC_CN);
+
+ return 0;
+}
+
+static int sirfsoc_rtc_ioctl(struct device *dev, unsigned int cmd,
+ unsigned long arg)
+{
+ switch (cmd) {
+ case RTC_PIE_ON:
+ case RTC_PIE_OFF:
+ case RTC_UIE_ON:
+ case RTC_UIE_OFF:
+ case RTC_AIE_ON:
+ case RTC_AIE_OFF:
+ return 0;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int sirfsoc_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ unsigned long rtc_status_reg = 0x0;
+ struct sirfsoc_rtc_drv *rtcdrv;
+
+ rtcdrv = dev_get_drvdata(dev);
+
+ spin_lock_irq(&rtcdrv->lock);
+
+ rtc_status_reg = sirfsoc_rtc_iobrg_readl(
+ rtcdrv->rtc_base + RTC_STATUS);
+ if (enabled)
+ rtc_status_reg |= SIRFSOC_RTC_AL0E;
+ else
+ rtc_status_reg &= ~SIRFSOC_RTC_AL0E;
+
+ sirfsoc_rtc_iobrg_writel(rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS);
+
+ spin_unlock_irq(&rtcdrv->lock);
+
+ return 0;
+
+}
+
+static const struct rtc_class_ops sirfsoc_rtc_ops = {
+ .read_time = sirfsoc_rtc_read_time,
+ .set_time = sirfsoc_rtc_set_time,
+ .read_alarm = sirfsoc_rtc_read_alarm,
+ .set_alarm = sirfsoc_rtc_set_alarm,
+ .ioctl = sirfsoc_rtc_ioctl,
+ .alarm_irq_enable = sirfsoc_rtc_alarm_irq_enable
+};
+
+static irqreturn_t sirfsoc_rtc_irq_handler(int irq, void *pdata)
+{
+ struct sirfsoc_rtc_drv *rtcdrv = pdata;
+ unsigned long rtc_status_reg = 0x0;
+ unsigned long events = 0x0;
+
+ spin_lock(&rtcdrv->lock);
+
+ rtc_status_reg = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_STATUS);
+ /* this bit will be set ONLY if an alarm was active
+ * and it expired NOW
+ * So this is being used as an ASSERT
+ */
+ if (rtc_status_reg & SIRFSOC_RTC_AL0) {
+ /*
+ * clear the RTC status register's alarm bit
+ * mask out the lower status bits
+ */
+ rtc_status_reg &= ~0x07;
+ /* write 1 into SIRFSOC_RTC_AL0 to ACK the alarm interrupt */
+ rtc_status_reg |= (SIRFSOC_RTC_AL0);
+ /* Clear the Alarm enable bit */
+ rtc_status_reg &= ~(SIRFSOC_RTC_AL0E);
+ }
+ sirfsoc_rtc_iobrg_writel(rtc_status_reg, rtcdrv->rtc_base + RTC_STATUS);
+
+ spin_unlock(&rtcdrv->lock);
+
+ /* this should wake up any apps polling/waiting on the read
+ * after setting the alarm
+ */
+ events |= RTC_IRQF | RTC_AF;
+ rtc_update_irq(rtcdrv->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id sirfsoc_rtc_of_match[] = {
+ { .compatible = "sirf,prima2-sysrtc"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_rtc_of_match);
+
+static int sirfsoc_rtc_probe(struct platform_device *pdev)
+{
+ int err;
+ unsigned long rtc_div;
+ struct sirfsoc_rtc_drv *rtcdrv;
+ struct device_node *np = pdev->dev.of_node;
+
+ rtcdrv = devm_kzalloc(&pdev->dev,
+ sizeof(struct sirfsoc_rtc_drv), GFP_KERNEL);
+ if (rtcdrv == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&rtcdrv->lock);
+
+ err = of_property_read_u32(np, "reg", &rtcdrv->rtc_base);
+ if (err) {
+ dev_err(&pdev->dev, "unable to find base address of rtc node in dtb\n");
+ return err;
+ }
+
+ platform_set_drvdata(pdev, rtcdrv);
+
+ /* Register rtc alarm as a wakeup source */
+ device_init_wakeup(&pdev->dev, 1);
+
+ /*
+ * Set SYS_RTC counter in RTC_HZ HZ Units
+ * We are using 32K RTC crystal (32768 / RTC_HZ / 2) -1
+ * If 16HZ, therefore RTC_DIV = 1023;
+ */
+ rtc_div = ((32768 / RTC_HZ) / 2) - 1;
+ sirfsoc_rtc_iobrg_writel(rtc_div, rtcdrv->rtc_base + RTC_DIV);
+
+ /* 0x3 -> RTC_CLK */
+ sirfsoc_rtc_iobrg_writel(SIRFSOC_RTC_CLK,
+ rtcdrv->rtc_base + RTC_CLOCK_SWITCH);
+
+ /* reset SYS RTC ALARM0 */
+ sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM0);
+
+ /* reset SYS RTC ALARM1 */
+ sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM1);
+
+ /* Restore RTC Overflow From Register After Command Reboot */
+ rtcdrv->overflow_rtc =
+ sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE);
+
+ rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &sirfsoc_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtcdrv->rtc)) {
+ err = PTR_ERR(rtcdrv->rtc);
+ dev_err(&pdev->dev, "can't register RTC device\n");
+ return err;
+ }
+
+ rtcdrv->irq = platform_get_irq(pdev, 0);
+ err = devm_request_irq(
+ &pdev->dev,
+ rtcdrv->irq,
+ sirfsoc_rtc_irq_handler,
+ IRQF_SHARED,
+ pdev->name,
+ rtcdrv);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to register for the SiRF SOC RTC IRQ\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int sirfsoc_rtc_remove(struct platform_device *pdev)
+{
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sirfsoc_rtc_suspend(struct device *dev)
+{
+ struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev);
+ rtcdrv->overflow_rtc =
+ sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_SW_VALUE);
+
+ rtcdrv->saved_counter =
+ sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN);
+ rtcdrv->saved_overflow_rtc = rtcdrv->overflow_rtc;
+ if (device_may_wakeup(dev) && !enable_irq_wake(rtcdrv->irq))
+ rtcdrv->irq_wake = 1;
+
+ return 0;
+}
+
+static int sirfsoc_rtc_resume(struct device *dev)
+{
+ u32 tmp;
+ struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev);
+
+ /*
+ * if resume from snapshot and the rtc power is lost,
+ * restroe the rtc settings
+ */
+ if (SIRFSOC_RTC_CLK != sirfsoc_rtc_iobrg_readl(
+ rtcdrv->rtc_base + RTC_CLOCK_SWITCH)) {
+ u32 rtc_div;
+ /* 0x3 -> RTC_CLK */
+ sirfsoc_rtc_iobrg_writel(SIRFSOC_RTC_CLK,
+ rtcdrv->rtc_base + RTC_CLOCK_SWITCH);
+ /*
+ * Set SYS_RTC counter in RTC_HZ HZ Units
+ * We are using 32K RTC crystal (32768 / RTC_HZ / 2) -1
+ * If 16HZ, therefore RTC_DIV = 1023;
+ */
+ rtc_div = ((32768 / RTC_HZ) / 2) - 1;
+
+ sirfsoc_rtc_iobrg_writel(rtc_div, rtcdrv->rtc_base + RTC_DIV);
+
+ /* reset SYS RTC ALARM0 */
+ sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM0);
+
+ /* reset SYS RTC ALARM1 */
+ sirfsoc_rtc_iobrg_writel(0x0, rtcdrv->rtc_base + RTC_ALARM1);
+ }
+ rtcdrv->overflow_rtc = rtcdrv->saved_overflow_rtc;
+
+ /*
+ * if current counter is small than previous,
+ * it means overflow in sleep
+ */
+ tmp = sirfsoc_rtc_iobrg_readl(rtcdrv->rtc_base + RTC_CN);
+ if (tmp <= rtcdrv->saved_counter)
+ rtcdrv->overflow_rtc++;
+ /*
+ *PWRC Value Be Changed When Suspend, Restore Overflow
+ * In Memory To Register
+ */
+ sirfsoc_rtc_iobrg_writel(rtcdrv->overflow_rtc,
+ rtcdrv->rtc_base + RTC_SW_VALUE);
+
+ if (device_may_wakeup(dev) && rtcdrv->irq_wake) {
+ disable_irq_wake(rtcdrv->irq);
+ rtcdrv->irq_wake = 0;
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sirfsoc_rtc_pm_ops,
+ sirfsoc_rtc_suspend, sirfsoc_rtc_resume);
+
+static struct platform_driver sirfsoc_rtc_driver = {
+ .driver = {
+ .name = "sirfsoc-rtc",
+ .pm = &sirfsoc_rtc_pm_ops,
+ .of_match_table = sirfsoc_rtc_of_match,
+ },
+ .probe = sirfsoc_rtc_probe,
+ .remove = sirfsoc_rtc_remove,
+};
+module_platform_driver(sirfsoc_rtc_driver);
+
+MODULE_DESCRIPTION("SiRF SoC rtc driver");
+MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sirfsoc-rtc");
diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c
new file mode 100644
index 000000000..d87a85cef
--- /dev/null
+++ b/drivers/rtc/rtc-snvs.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/clk.h>
+
+/* These register offsets are relative to LP (Low Power) range */
+#define SNVS_LPCR 0x04
+#define SNVS_LPSR 0x18
+#define SNVS_LPSRTCMR 0x1c
+#define SNVS_LPSRTCLR 0x20
+#define SNVS_LPTAR 0x24
+#define SNVS_LPPGDR 0x30
+
+#define SNVS_LPCR_SRTC_ENV (1 << 0)
+#define SNVS_LPCR_LPTA_EN (1 << 1)
+#define SNVS_LPCR_LPWUI_EN (1 << 3)
+#define SNVS_LPSR_LPTA (1 << 0)
+
+#define SNVS_LPPGDR_INIT 0x41736166
+#define CNTR_TO_SECS_SH 15
+
+struct snvs_rtc_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ int irq;
+ spinlock_t lock;
+ struct clk *clk;
+};
+
+static u32 rtc_read_lp_counter(void __iomem *ioaddr)
+{
+ u64 read1, read2;
+
+ do {
+ read1 = readl(ioaddr + SNVS_LPSRTCMR);
+ read1 <<= 32;
+ read1 |= readl(ioaddr + SNVS_LPSRTCLR);
+
+ read2 = readl(ioaddr + SNVS_LPSRTCMR);
+ read2 <<= 32;
+ read2 |= readl(ioaddr + SNVS_LPSRTCLR);
+ } while (read1 != read2);
+
+ /* Convert 47-bit counter to 32-bit raw second count */
+ return (u32) (read1 >> CNTR_TO_SECS_SH);
+}
+
+static void rtc_write_sync_lp(void __iomem *ioaddr)
+{
+ u32 count1, count2, count3;
+ int i;
+
+ /* Wait for 3 CKIL cycles */
+ for (i = 0; i < 3; i++) {
+ do {
+ count1 = readl(ioaddr + SNVS_LPSRTCLR);
+ count2 = readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count1 != count2);
+
+ /* Now wait until counter value changes */
+ do {
+ do {
+ count2 = readl(ioaddr + SNVS_LPSRTCLR);
+ count3 = readl(ioaddr + SNVS_LPSRTCLR);
+ } while (count2 != count3);
+ } while (count3 == count1);
+ }
+}
+
+static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
+{
+ unsigned long flags;
+ int timeout = 1000;
+ u32 lpcr;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ if (enable)
+ lpcr |= SNVS_LPCR_SRTC_ENV;
+ else
+ lpcr &= ~SNVS_LPCR_SRTC_ENV;
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ while (--timeout) {
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+
+ if (enable) {
+ if (lpcr & SNVS_LPCR_SRTC_ENV)
+ break;
+ } else {
+ if (!(lpcr & SNVS_LPCR_SRTC_ENV))
+ break;
+ }
+ }
+
+ if (!timeout)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ unsigned long time = rtc_read_lp_counter(data->ioaddr);
+
+ rtc_time_to_tm(time, tm);
+
+ return 0;
+}
+
+static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ unsigned long time;
+
+ rtc_tm_to_time(tm, &time);
+
+ /* Disable RTC first */
+ snvs_rtc_enable(data, false);
+
+ /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
+ writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
+ writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
+
+ /* Enable RTC again */
+ snvs_rtc_enable(data, true);
+
+ return 0;
+}
+
+static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lptar, lpsr;
+
+ lptar = readl(data->ioaddr + SNVS_LPTAR);
+ rtc_time_to_tm(lptar, &alrm->time);
+
+ lpsr = readl(data->ioaddr + SNVS_LPSR);
+ alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
+
+ return 0;
+}
+
+static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lpcr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ if (enable)
+ lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ else
+ lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ rtc_write_sync_lp(data->ioaddr);
+
+ return 0;
+}
+
+static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &alrm->time;
+ unsigned long time;
+ unsigned long flags;
+ u32 lpcr;
+
+ rtc_tm_to_time(alrm_tm, &time);
+
+ spin_lock_irqsave(&data->lock, flags);
+
+ /* Have to clear LPTA_EN before programming new alarm time in LPTAR */
+ lpcr = readl(data->ioaddr + SNVS_LPCR);
+ lpcr &= ~SNVS_LPCR_LPTA_EN;
+ writel(lpcr, data->ioaddr + SNVS_LPCR);
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ writel(time, data->ioaddr + SNVS_LPTAR);
+
+ /* Clear alarm interrupt status bit */
+ writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
+
+ return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
+}
+
+static const struct rtc_class_ops snvs_rtc_ops = {
+ .read_time = snvs_rtc_read_time,
+ .set_time = snvs_rtc_set_time,
+ .read_alarm = snvs_rtc_read_alarm,
+ .set_alarm = snvs_rtc_set_alarm,
+ .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+ u32 lpsr;
+ u32 events = 0;
+
+ lpsr = readl(data->ioaddr + SNVS_LPSR);
+
+ if (lpsr & SNVS_LPSR_LPTA) {
+ events |= (RTC_AF | RTC_IRQF);
+
+ /* RTC alarm should be one-shot */
+ snvs_rtc_alarm_irq_enable(dev, 0);
+
+ rtc_update_irq(data->rtc, 1, events);
+ }
+
+ /* clear interrupt status */
+ writel(lpsr, data->ioaddr + SNVS_LPSR);
+
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int snvs_rtc_probe(struct platform_device *pdev)
+{
+ struct snvs_rtc_data *data;
+ struct resource *res;
+ int ret;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->ioaddr))
+ return PTR_ERR(data->ioaddr);
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0)
+ return data->irq;
+
+ data->clk = devm_clk_get(&pdev->dev, "snvs-rtc");
+ if (IS_ERR(data->clk)) {
+ data->clk = NULL;
+ } else {
+ ret = clk_prepare_enable(data->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the snvs clock\n");
+ return ret;
+ }
+ }
+
+ platform_set_drvdata(pdev, data);
+
+ spin_lock_init(&data->lock);
+
+ /* Initialize glitch detect */
+ writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
+
+ /* Clear interrupt status */
+ writel(0xffffffff, data->ioaddr + SNVS_LPSR);
+
+ /* Enable RTC */
+ snvs_rtc_enable(data, true);
+
+ device_init_wakeup(&pdev->dev, true);
+
+ ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
+ IRQF_SHARED, "rtc alarm", &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq %d: %d\n",
+ data->irq, ret);
+ goto error_rtc_device_register;
+ }
+
+ data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &snvs_rtc_ops, THIS_MODULE);
+ if (IS_ERR(data->rtc)) {
+ ret = PTR_ERR(data->rtc);
+ dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
+ goto error_rtc_device_register;
+ }
+
+ return 0;
+
+error_rtc_device_register:
+ if (data->clk)
+ clk_disable_unprepare(data->clk);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int snvs_rtc_suspend(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(data->irq);
+
+ return 0;
+}
+
+static int snvs_rtc_suspend_noirq(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (data->clk)
+ clk_disable_unprepare(data->clk);
+
+ return 0;
+}
+
+static int snvs_rtc_resume(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ return disable_irq_wake(data->irq);
+
+ return 0;
+}
+
+static int snvs_rtc_resume_noirq(struct device *dev)
+{
+ struct snvs_rtc_data *data = dev_get_drvdata(dev);
+
+ if (data->clk)
+ return clk_prepare_enable(data->clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops snvs_rtc_pm_ops = {
+ .suspend = snvs_rtc_suspend,
+ .suspend_noirq = snvs_rtc_suspend_noirq,
+ .resume = snvs_rtc_resume,
+ .resume_noirq = snvs_rtc_resume_noirq,
+};
+
+#define SNVS_RTC_PM_OPS (&snvs_rtc_pm_ops)
+
+#else
+
+#define SNVS_RTC_PM_OPS NULL
+
+#endif
+
+static const struct of_device_id snvs_dt_ids[] = {
+ { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, snvs_dt_ids);
+
+static struct platform_driver snvs_rtc_driver = {
+ .driver = {
+ .name = "snvs_rtc",
+ .pm = SNVS_RTC_PM_OPS,
+ .of_match_table = snvs_dt_ids,
+ },
+ .probe = snvs_rtc_probe,
+};
+module_platform_driver(snvs_rtc_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
new file mode 100644
index 000000000..d2cdb9823
--- /dev/null
+++ b/drivers/rtc/rtc-spear.c
@@ -0,0 +1,509 @@
+/*
+ * drivers/rtc/rtc-spear.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar<rajeev-dlh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* RTC registers */
+#define TIME_REG 0x00
+#define DATE_REG 0x04
+#define ALARM_TIME_REG 0x08
+#define ALARM_DATE_REG 0x0C
+#define CTRL_REG 0x10
+#define STATUS_REG 0x14
+
+/* TIME_REG & ALARM_TIME_REG */
+#define SECONDS_UNITS (0xf<<0) /* seconds units position */
+#define SECONDS_TENS (0x7<<4) /* seconds tens position */
+#define MINUTES_UNITS (0xf<<8) /* minutes units position */
+#define MINUTES_TENS (0x7<<12) /* minutes tens position */
+#define HOURS_UNITS (0xf<<16) /* hours units position */
+#define HOURS_TENS (0x3<<20) /* hours tens position */
+
+/* DATE_REG & ALARM_DATE_REG */
+#define DAYS_UNITS (0xf<<0) /* days units position */
+#define DAYS_TENS (0x3<<4) /* days tens position */
+#define MONTHS_UNITS (0xf<<8) /* months units position */
+#define MONTHS_TENS (0x1<<12) /* months tens position */
+#define YEARS_UNITS (0xf<<16) /* years units position */
+#define YEARS_TENS (0xf<<20) /* years tens position */
+#define YEARS_HUNDREDS (0xf<<24) /* years hundereds position */
+#define YEARS_MILLENIUMS (0xf<<28) /* years millenium position */
+
+/* MASK SHIFT TIME_REG & ALARM_TIME_REG*/
+#define SECOND_SHIFT 0x00 /* seconds units */
+#define MINUTE_SHIFT 0x08 /* minutes units position */
+#define HOUR_SHIFT 0x10 /* hours units position */
+#define MDAY_SHIFT 0x00 /* Month day shift */
+#define MONTH_SHIFT 0x08 /* Month shift */
+#define YEAR_SHIFT 0x10 /* Year shift */
+
+#define SECOND_MASK 0x7F
+#define MIN_MASK 0x7F
+#define HOUR_MASK 0x3F
+#define DAY_MASK 0x3F
+#define MONTH_MASK 0x7F
+#define YEAR_MASK 0xFFFF
+
+/* date reg equal to time reg, for debug only */
+#define TIME_BYP (1<<9)
+#define INT_ENABLE (1<<31) /* interrupt enable */
+
+/* STATUS_REG */
+#define CLK_UNCONNECTED (1<<0)
+#define PEND_WR_TIME (1<<2)
+#define PEND_WR_DATE (1<<3)
+#define LOST_WR_TIME (1<<4)
+#define LOST_WR_DATE (1<<5)
+#define RTC_INT_MASK (1<<31)
+#define STATUS_BUSY (PEND_WR_TIME | PEND_WR_DATE)
+#define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE)
+
+struct spear_rtc_config {
+ struct rtc_device *rtc;
+ struct clk *clk;
+ spinlock_t lock;
+ void __iomem *ioaddr;
+ unsigned int irq_wake;
+};
+
+static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&config->lock, flags);
+ val = readl(config->ioaddr + STATUS_REG);
+ val |= RTC_INT_MASK;
+ writel(val, config->ioaddr + STATUS_REG);
+ spin_unlock_irqrestore(&config->lock, flags);
+}
+
+static inline void spear_rtc_enable_interrupt(struct spear_rtc_config *config)
+{
+ unsigned int val;
+
+ val = readl(config->ioaddr + CTRL_REG);
+ if (!(val & INT_ENABLE)) {
+ spear_rtc_clear_interrupt(config);
+ val |= INT_ENABLE;
+ writel(val, config->ioaddr + CTRL_REG);
+ }
+}
+
+static inline void spear_rtc_disable_interrupt(struct spear_rtc_config *config)
+{
+ unsigned int val;
+
+ val = readl(config->ioaddr + CTRL_REG);
+ if (val & INT_ENABLE) {
+ val &= ~INT_ENABLE;
+ writel(val, config->ioaddr + CTRL_REG);
+ }
+}
+
+static inline int is_write_complete(struct spear_rtc_config *config)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&config->lock, flags);
+ if ((readl(config->ioaddr + STATUS_REG)) & STATUS_FAIL)
+ ret = -EIO;
+ spin_unlock_irqrestore(&config->lock, flags);
+
+ return ret;
+}
+
+static void rtc_wait_not_busy(struct spear_rtc_config *config)
+{
+ int status, count = 0;
+ unsigned long flags;
+
+ /* Assuming BUSY may stay active for 80 msec) */
+ for (count = 0; count < 80; count++) {
+ spin_lock_irqsave(&config->lock, flags);
+ status = readl(config->ioaddr + STATUS_REG);
+ spin_unlock_irqrestore(&config->lock, flags);
+ if ((status & STATUS_BUSY) == 0)
+ break;
+ /* check status busy, after each msec */
+ msleep(1);
+ }
+}
+
+static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
+{
+ struct spear_rtc_config *config = dev_id;
+ unsigned long flags, events = 0;
+ unsigned int irq_data;
+
+ spin_lock_irqsave(&config->lock, flags);
+ irq_data = readl(config->ioaddr + STATUS_REG);
+ spin_unlock_irqrestore(&config->lock, flags);
+
+ if ((irq_data & RTC_INT_MASK)) {
+ spear_rtc_clear_interrupt(config);
+ events = RTC_IRQF | RTC_AF;
+ rtc_update_irq(config->rtc, 1, events);
+ return IRQ_HANDLED;
+ } else
+ return IRQ_NONE;
+
+}
+
+static int tm2bcd(struct rtc_time *tm)
+{
+ if (rtc_valid_tm(tm) != 0)
+ return -EINVAL;
+ tm->tm_sec = bin2bcd(tm->tm_sec);
+ tm->tm_min = bin2bcd(tm->tm_min);
+ tm->tm_hour = bin2bcd(tm->tm_hour);
+ tm->tm_mday = bin2bcd(tm->tm_mday);
+ tm->tm_mon = bin2bcd(tm->tm_mon + 1);
+ tm->tm_year = bin2bcd(tm->tm_year);
+
+ return 0;
+}
+
+static void bcd2tm(struct rtc_time *tm)
+{
+ tm->tm_sec = bcd2bin(tm->tm_sec);
+ tm->tm_min = bcd2bin(tm->tm_min);
+ tm->tm_hour = bcd2bin(tm->tm_hour);
+ tm->tm_mday = bcd2bin(tm->tm_mday);
+ tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
+ /* epoch == 1900 */
+ tm->tm_year = bcd2bin(tm->tm_year);
+}
+
+/*
+ * spear_rtc_read_time - set the time
+ * @dev: rtc device in use
+ * @tm: holds date and time
+ *
+ * This function read time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
+ unsigned int time, date;
+
+ /* we don't report wday/yday/isdst ... */
+ rtc_wait_not_busy(config);
+
+ time = readl(config->ioaddr + TIME_REG);
+ date = readl(config->ioaddr + DATE_REG);
+ tm->tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
+ tm->tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
+ tm->tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
+ tm->tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
+ tm->tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
+ tm->tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
+
+ bcd2tm(tm);
+ return 0;
+}
+
+/*
+ * spear_rtc_set_time - set the time
+ * @dev: rtc device in use
+ * @tm: holds date and time
+ *
+ * This function set time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
+ unsigned int time, date;
+
+ if (tm2bcd(tm) < 0)
+ return -EINVAL;
+
+ rtc_wait_not_busy(config);
+ time = (tm->tm_sec << SECOND_SHIFT) | (tm->tm_min << MINUTE_SHIFT) |
+ (tm->tm_hour << HOUR_SHIFT);
+ date = (tm->tm_mday << MDAY_SHIFT) | (tm->tm_mon << MONTH_SHIFT) |
+ (tm->tm_year << YEAR_SHIFT);
+ writel(time, config->ioaddr + TIME_REG);
+ writel(date, config->ioaddr + DATE_REG);
+
+ return is_write_complete(config);
+}
+
+/*
+ * spear_rtc_read_alarm - read the alarm time
+ * @dev: rtc device in use
+ * @alm: holds alarm date and time
+ *
+ * This function read alarm time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
+ unsigned int time, date;
+
+ rtc_wait_not_busy(config);
+
+ time = readl(config->ioaddr + ALARM_TIME_REG);
+ date = readl(config->ioaddr + ALARM_DATE_REG);
+ alm->time.tm_sec = (time >> SECOND_SHIFT) & SECOND_MASK;
+ alm->time.tm_min = (time >> MINUTE_SHIFT) & MIN_MASK;
+ alm->time.tm_hour = (time >> HOUR_SHIFT) & HOUR_MASK;
+ alm->time.tm_mday = (date >> MDAY_SHIFT) & DAY_MASK;
+ alm->time.tm_mon = (date >> MONTH_SHIFT) & MONTH_MASK;
+ alm->time.tm_year = (date >> YEAR_SHIFT) & YEAR_MASK;
+
+ bcd2tm(&alm->time);
+ alm->enabled = readl(config->ioaddr + CTRL_REG) & INT_ENABLE;
+
+ return 0;
+}
+
+/*
+ * spear_rtc_set_alarm - set the alarm time
+ * @dev: rtc device in use
+ * @alm: holds alarm date and time
+ *
+ * This function set alarm time and date. On success it will return 0
+ * otherwise -ve error is returned.
+ */
+static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
+ unsigned int time, date;
+ int err;
+
+ if (tm2bcd(&alm->time) < 0)
+ return -EINVAL;
+
+ rtc_wait_not_busy(config);
+
+ time = (alm->time.tm_sec << SECOND_SHIFT) | (alm->time.tm_min <<
+ MINUTE_SHIFT) | (alm->time.tm_hour << HOUR_SHIFT);
+ date = (alm->time.tm_mday << MDAY_SHIFT) | (alm->time.tm_mon <<
+ MONTH_SHIFT) | (alm->time.tm_year << YEAR_SHIFT);
+
+ writel(time, config->ioaddr + ALARM_TIME_REG);
+ writel(date, config->ioaddr + ALARM_DATE_REG);
+ err = is_write_complete(config);
+ if (err < 0)
+ return err;
+
+ if (alm->enabled)
+ spear_rtc_enable_interrupt(config);
+ else
+ spear_rtc_disable_interrupt(config);
+
+ return 0;
+}
+
+static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
+ int ret = 0;
+
+ spear_rtc_clear_interrupt(config);
+
+ switch (enabled) {
+ case 0:
+ /* alarm off */
+ spear_rtc_disable_interrupt(config);
+ break;
+ case 1:
+ /* alarm on */
+ spear_rtc_enable_interrupt(config);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static struct rtc_class_ops spear_rtc_ops = {
+ .read_time = spear_rtc_read_time,
+ .set_time = spear_rtc_set_time,
+ .read_alarm = spear_rtc_read_alarm,
+ .set_alarm = spear_rtc_set_alarm,
+ .alarm_irq_enable = spear_alarm_irq_enable,
+};
+
+static int spear_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct spear_rtc_config *config;
+ int status = 0;
+ int irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no resource defined\n");
+ return -EBUSY;
+ }
+
+ config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
+ if (!config)
+ return -ENOMEM;
+
+ /* alarm irqs */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no update irq?\n");
+ return irq;
+ }
+
+ status = devm_request_irq(&pdev->dev, irq, spear_rtc_irq, 0, pdev->name,
+ config);
+ if (status) {
+ dev_err(&pdev->dev, "Alarm interrupt IRQ%d already claimed\n",
+ irq);
+ return status;
+ }
+
+ config->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(config->ioaddr))
+ return PTR_ERR(config->ioaddr);
+
+ config->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(config->clk))
+ return PTR_ERR(config->clk);
+
+ status = clk_prepare_enable(config->clk);
+ if (status < 0)
+ return status;
+
+ spin_lock_init(&config->lock);
+ platform_set_drvdata(pdev, config);
+
+ config->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &spear_rtc_ops, THIS_MODULE);
+ if (IS_ERR(config->rtc)) {
+ dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+ PTR_ERR(config->rtc));
+ status = PTR_ERR(config->rtc);
+ goto err_disable_clock;
+ }
+
+ config->rtc->uie_unsupported = 1;
+
+ if (!device_can_wakeup(&pdev->dev))
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+err_disable_clock:
+ clk_disable_unprepare(config->clk);
+
+ return status;
+}
+
+static int spear_rtc_remove(struct platform_device *pdev)
+{
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
+
+ spear_rtc_disable_interrupt(config);
+ clk_disable_unprepare(config->clk);
+ device_init_wakeup(&pdev->dev, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int spear_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (device_may_wakeup(&pdev->dev)) {
+ if (!enable_irq_wake(irq))
+ config->irq_wake = 1;
+ } else {
+ spear_rtc_disable_interrupt(config);
+ clk_disable(config->clk);
+ }
+
+ return 0;
+}
+
+static int spear_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+
+ if (device_may_wakeup(&pdev->dev)) {
+ if (config->irq_wake) {
+ disable_irq_wake(irq);
+ config->irq_wake = 0;
+ }
+ } else {
+ clk_enable(config->clk);
+ spear_rtc_enable_interrupt(config);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(spear_rtc_pm_ops, spear_rtc_suspend, spear_rtc_resume);
+
+static void spear_rtc_shutdown(struct platform_device *pdev)
+{
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
+
+ spear_rtc_disable_interrupt(config);
+ clk_disable(config->clk);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spear_rtc_id_table[] = {
+ { .compatible = "st,spear600-rtc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, spear_rtc_id_table);
+#endif
+
+static struct platform_driver spear_rtc_driver = {
+ .probe = spear_rtc_probe,
+ .remove = spear_rtc_remove,
+ .shutdown = spear_rtc_shutdown,
+ .driver = {
+ .name = "rtc-spear",
+ .pm = &spear_rtc_pm_ops,
+ .of_match_table = of_match_ptr(spear_rtc_id_table),
+ },
+};
+
+module_platform_driver(spear_rtc_driver);
+
+MODULE_ALIAS("platform:rtc-spear");
+MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
+MODULE_DESCRIPTION("ST SPEAr Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c
new file mode 100644
index 000000000..83a057a03
--- /dev/null
+++ b/drivers/rtc/rtc-starfire.c
@@ -0,0 +1,60 @@
+/* rtc-starfire.c: Starfire platform RTC driver.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <asm/oplib.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Starfire RTC driver");
+MODULE_LICENSE("GPL");
+
+static u32 starfire_get_time(void)
+{
+ static char obp_gettod[32];
+ static u32 unix_tod;
+
+ sprintf(obp_gettod, "h# %08x unix-gettod",
+ (unsigned int) (long) &unix_tod);
+ prom_feval(obp_gettod);
+
+ return unix_tod;
+}
+
+static int starfire_read_time(struct device *dev, struct rtc_time *tm)
+{
+ rtc_time_to_tm(starfire_get_time(), tm);
+ return rtc_valid_tm(tm);
+}
+
+static const struct rtc_class_ops starfire_rtc_ops = {
+ .read_time = starfire_read_time,
+};
+
+static int __init starfire_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&pdev->dev, "starfire",
+ &starfire_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver starfire_rtc_driver = {
+ .driver = {
+ .name = "rtc-starfire",
+ },
+};
+
+module_platform_driver_probe(starfire_rtc_driver, starfire_rtc_probe);
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
new file mode 100644
index 000000000..0e93b714e
--- /dev/null
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -0,0 +1,369 @@
+/*
+ * A RTC driver for the Simtek STK17TA8
+ *
+ * By Thomas Hommel <thomas.hommel@ge.com>
+ *
+ * Based on the DS1553 driver from
+ * Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/delay.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+
+#define DRV_VERSION "0.1"
+
+#define RTC_REG_SIZE 0x20000
+#define RTC_OFFSET 0x1fff0
+
+#define RTC_FLAGS (RTC_OFFSET + 0)
+#define RTC_CENTURY (RTC_OFFSET + 1)
+#define RTC_SECONDS_ALARM (RTC_OFFSET + 2)
+#define RTC_MINUTES_ALARM (RTC_OFFSET + 3)
+#define RTC_HOURS_ALARM (RTC_OFFSET + 4)
+#define RTC_DATE_ALARM (RTC_OFFSET + 5)
+#define RTC_INTERRUPTS (RTC_OFFSET + 6)
+#define RTC_WATCHDOG (RTC_OFFSET + 7)
+#define RTC_CALIBRATION (RTC_OFFSET + 8)
+#define RTC_SECONDS (RTC_OFFSET + 9)
+#define RTC_MINUTES (RTC_OFFSET + 10)
+#define RTC_HOURS (RTC_OFFSET + 11)
+#define RTC_DAY (RTC_OFFSET + 12)
+#define RTC_DATE (RTC_OFFSET + 13)
+#define RTC_MONTH (RTC_OFFSET + 14)
+#define RTC_YEAR (RTC_OFFSET + 15)
+
+#define RTC_SECONDS_MASK 0x7f
+#define RTC_DAY_MASK 0x07
+#define RTC_CAL_MASK 0x3f
+
+/* Bits in the Calibration register */
+#define RTC_STOP 0x80
+
+/* Bits in the Flags register */
+#define RTC_FLAGS_AF 0x40
+#define RTC_FLAGS_PF 0x20
+#define RTC_WRITE 0x02
+#define RTC_READ 0x01
+
+/* Bits in the Interrupts register */
+#define RTC_INTS_AIE 0x40
+
+struct rtc_plat_data {
+ struct rtc_device *rtc;
+ void __iomem *ioaddr;
+ unsigned long last_jiffies;
+ int irq;
+ unsigned int irqen;
+ int alrm_sec;
+ int alrm_min;
+ int alrm_hour;
+ int alrm_mday;
+ spinlock_t lock;
+};
+
+static int stk17ta8_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ u8 flags;
+
+ flags = readb(pdata->ioaddr + RTC_FLAGS);
+ writeb(flags | RTC_WRITE, pdata->ioaddr + RTC_FLAGS);
+
+ writeb(bin2bcd(tm->tm_year % 100), ioaddr + RTC_YEAR);
+ writeb(bin2bcd(tm->tm_mon + 1), ioaddr + RTC_MONTH);
+ writeb(bin2bcd(tm->tm_wday) & RTC_DAY_MASK, ioaddr + RTC_DAY);
+ writeb(bin2bcd(tm->tm_mday), ioaddr + RTC_DATE);
+ writeb(bin2bcd(tm->tm_hour), ioaddr + RTC_HOURS);
+ writeb(bin2bcd(tm->tm_min), ioaddr + RTC_MINUTES);
+ writeb(bin2bcd(tm->tm_sec) & RTC_SECONDS_MASK, ioaddr + RTC_SECONDS);
+ writeb(bin2bcd((tm->tm_year + 1900) / 100), ioaddr + RTC_CENTURY);
+
+ writeb(flags & ~RTC_WRITE, pdata->ioaddr + RTC_FLAGS);
+ return 0;
+}
+
+static int stk17ta8_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned int year, month, day, hour, minute, second, week;
+ unsigned int century;
+ u8 flags;
+
+ /* give enough time to update RTC in case of continuous read */
+ if (pdata->last_jiffies == jiffies)
+ msleep(1);
+ pdata->last_jiffies = jiffies;
+
+ flags = readb(pdata->ioaddr + RTC_FLAGS);
+ writeb(flags | RTC_READ, ioaddr + RTC_FLAGS);
+ second = readb(ioaddr + RTC_SECONDS) & RTC_SECONDS_MASK;
+ minute = readb(ioaddr + RTC_MINUTES);
+ hour = readb(ioaddr + RTC_HOURS);
+ day = readb(ioaddr + RTC_DATE);
+ week = readb(ioaddr + RTC_DAY) & RTC_DAY_MASK;
+ month = readb(ioaddr + RTC_MONTH);
+ year = readb(ioaddr + RTC_YEAR);
+ century = readb(ioaddr + RTC_CENTURY);
+ writeb(flags & ~RTC_READ, ioaddr + RTC_FLAGS);
+ tm->tm_sec = bcd2bin(second);
+ tm->tm_min = bcd2bin(minute);
+ tm->tm_hour = bcd2bin(hour);
+ tm->tm_mday = bcd2bin(day);
+ tm->tm_wday = bcd2bin(week);
+ tm->tm_mon = bcd2bin(month) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = bcd2bin(year) + bcd2bin(century) * 100 - 1900;
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+ return 0;
+}
+
+static void stk17ta8_rtc_update_alarm(struct rtc_plat_data *pdata)
+{
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long irqflags;
+ u8 flags;
+
+ spin_lock_irqsave(&pdata->lock, irqflags);
+
+ flags = readb(ioaddr + RTC_FLAGS);
+ writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
+
+ writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_mday),
+ ioaddr + RTC_DATE_ALARM);
+ writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_hour),
+ ioaddr + RTC_HOURS_ALARM);
+ writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_min),
+ ioaddr + RTC_MINUTES_ALARM);
+ writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
+ 0x80 : bin2bcd(pdata->alrm_sec),
+ ioaddr + RTC_SECONDS_ALARM);
+ writeb(pdata->irqen ? RTC_INTS_AIE : 0, ioaddr + RTC_INTERRUPTS);
+ readb(ioaddr + RTC_FLAGS); /* clear interrupts */
+ writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
+ spin_unlock_irqrestore(&pdata->lock, irqflags);
+}
+
+static int stk17ta8_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ pdata->alrm_mday = alrm->time.tm_mday;
+ pdata->alrm_hour = alrm->time.tm_hour;
+ pdata->alrm_min = alrm->time.tm_min;
+ pdata->alrm_sec = alrm->time.tm_sec;
+ if (alrm->enabled)
+ pdata->irqen |= RTC_AF;
+ stk17ta8_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static int stk17ta8_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ alrm->time.tm_mday = pdata->alrm_mday < 0 ? 0 : pdata->alrm_mday;
+ alrm->time.tm_hour = pdata->alrm_hour < 0 ? 0 : pdata->alrm_hour;
+ alrm->time.tm_min = pdata->alrm_min < 0 ? 0 : pdata->alrm_min;
+ alrm->time.tm_sec = pdata->alrm_sec < 0 ? 0 : pdata->alrm_sec;
+ alrm->enabled = (pdata->irqen & RTC_AF) ? 1 : 0;
+ return 0;
+}
+
+static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ unsigned long events = 0;
+
+ spin_lock(&pdata->lock);
+ /* read and clear interrupt */
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF) {
+ events = RTC_IRQF;
+ if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
+ events |= RTC_UF;
+ else
+ events |= RTC_AF;
+ rtc_update_irq(pdata->rtc, 1, events);
+ }
+ spin_unlock(&pdata->lock);
+ return events ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static int stk17ta8_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->irq <= 0)
+ return -EINVAL;
+ if (enabled)
+ pdata->irqen |= RTC_AF;
+ else
+ pdata->irqen &= ~RTC_AF;
+ stk17ta8_rtc_update_alarm(pdata);
+ return 0;
+}
+
+static const struct rtc_class_ops stk17ta8_rtc_ops = {
+ .read_time = stk17ta8_rtc_read_time,
+ .set_time = stk17ta8_rtc_set_time,
+ .read_alarm = stk17ta8_rtc_read_alarm,
+ .set_alarm = stk17ta8_rtc_set_alarm,
+ .alarm_irq_enable = stk17ta8_rtc_alarm_irq_enable,
+};
+
+static ssize_t stk17ta8_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ *buf++ = readb(ioaddr + pos++);
+ return count;
+}
+
+static ssize_t stk17ta8_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+ void __iomem *ioaddr = pdata->ioaddr;
+ ssize_t count;
+
+ for (count = 0; size > 0 && pos < RTC_OFFSET; count++, size--)
+ writeb(*buf++, ioaddr + pos++);
+ return count;
+}
+
+static struct bin_attribute stk17ta8_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = RTC_OFFSET,
+ .read = stk17ta8_nvram_read,
+ .write = stk17ta8_nvram_write,
+};
+
+static int stk17ta8_rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ unsigned int cal;
+ unsigned int flags;
+ struct rtc_plat_data *pdata;
+ void __iomem *ioaddr;
+ int ret = 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ioaddr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(ioaddr))
+ return PTR_ERR(ioaddr);
+ pdata->ioaddr = ioaddr;
+ pdata->irq = platform_get_irq(pdev, 0);
+
+ /* turn RTC on if it was not on */
+ cal = readb(ioaddr + RTC_CALIBRATION);
+ if (cal & RTC_STOP) {
+ cal &= RTC_CAL_MASK;
+ flags = readb(ioaddr + RTC_FLAGS);
+ writeb(flags | RTC_WRITE, ioaddr + RTC_FLAGS);
+ writeb(cal, ioaddr + RTC_CALIBRATION);
+ writeb(flags & ~RTC_WRITE, ioaddr + RTC_FLAGS);
+ }
+ if (readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_PF)
+ dev_warn(&pdev->dev, "voltage-low detected.\n");
+
+ spin_lock_init(&pdata->lock);
+ pdata->last_jiffies = jiffies;
+ platform_set_drvdata(pdev, pdata);
+ if (pdata->irq > 0) {
+ writeb(0, ioaddr + RTC_INTERRUPTS);
+ if (devm_request_irq(&pdev->dev, pdata->irq,
+ stk17ta8_rtc_interrupt,
+ IRQF_SHARED,
+ pdev->name, pdev) < 0) {
+ dev_warn(&pdev->dev, "interrupt not available.\n");
+ pdata->irq = 0;
+ }
+ }
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &stk17ta8_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc))
+ return PTR_ERR(pdata->rtc);
+
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
+
+ return ret;
+}
+
+static int stk17ta8_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &stk17ta8_nvram_attr);
+ if (pdata->irq > 0)
+ writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:stk17ta8");
+
+static struct platform_driver stk17ta8_rtc_driver = {
+ .probe = stk17ta8_rtc_probe,
+ .remove = stk17ta8_rtc_remove,
+ .driver = {
+ .name = "stk17ta8",
+ },
+};
+
+module_platform_driver(stk17ta8_rtc_driver);
+
+MODULE_AUTHOR("Thomas Hommel <thomas.hommel@ge.com>");
+MODULE_DESCRIPTION("Simtek STK17TA8 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c
new file mode 100644
index 000000000..eb09eddf3
--- /dev/null
+++ b/drivers/rtc/rtc-stmp3xxx.c
@@ -0,0 +1,408 @@
+/*
+ * Freescale STMP37XX/STMP378X Real Time Clock driver
+ *
+ * Copyright (c) 2007 Sigmatel, Inc.
+ * Peter Hartley, <peter.hartley@sigmatel.com>
+ *
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ * Copyright 2011 Wolfram Sang, Pengutronix e.K.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/stmp_device.h>
+#include <linux/stmp3xxx_rtc_wdt.h>
+
+#define STMP3XXX_RTC_CTRL 0x0
+#define STMP3XXX_RTC_CTRL_SET 0x4
+#define STMP3XXX_RTC_CTRL_CLR 0x8
+#define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001
+#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002
+#define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004
+#define STMP3XXX_RTC_CTRL_WATCHDOGEN 0x00000010
+
+#define STMP3XXX_RTC_STAT 0x10
+#define STMP3XXX_RTC_STAT_STALE_SHIFT 16
+#define STMP3XXX_RTC_STAT_RTC_PRESENT 0x80000000
+#define STMP3XXX_RTC_STAT_XTAL32000_PRESENT 0x10000000
+#define STMP3XXX_RTC_STAT_XTAL32768_PRESENT 0x08000000
+
+#define STMP3XXX_RTC_SECONDS 0x30
+
+#define STMP3XXX_RTC_ALARM 0x40
+
+#define STMP3XXX_RTC_WATCHDOG 0x50
+
+#define STMP3XXX_RTC_PERSISTENT0 0x60
+#define STMP3XXX_RTC_PERSISTENT0_SET 0x64
+#define STMP3XXX_RTC_PERSISTENT0_CLR 0x68
+#define STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE (1 << 0)
+#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1)
+#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN (1 << 2)
+#define STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP (1 << 4)
+#define STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP (1 << 5)
+#define STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ (1 << 6)
+#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE (1 << 7)
+
+#define STMP3XXX_RTC_PERSISTENT1 0x70
+/* missing bitmask in headers */
+#define STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER 0x80000000
+
+struct stmp3xxx_rtc_data {
+ struct rtc_device *rtc;
+ void __iomem *io;
+ int irq_alarm;
+};
+
+#if IS_ENABLED(CONFIG_STMP3XXX_RTC_WATCHDOG)
+/**
+ * stmp3xxx_wdt_set_timeout - configure the watchdog inside the STMP3xxx RTC
+ * @dev: the parent device of the watchdog (= the RTC)
+ * @timeout: the desired value for the timeout register of the watchdog.
+ * 0 disables the watchdog
+ *
+ * The watchdog needs one register and two bits which are in the RTC domain.
+ * To handle the resource conflict, the RTC driver will create another
+ * platform_device for the watchdog driver as a child of the RTC device.
+ * The watchdog driver is passed the below accessor function via platform_data
+ * to configure the watchdog. Locking is not needed because accessing SET/CLR
+ * registers is atomic.
+ */
+
+static void stmp3xxx_wdt_set_timeout(struct device *dev, u32 timeout)
+{
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ if (timeout) {
+ writel(timeout, rtc_data->io + STMP3XXX_RTC_WATCHDOG);
+ writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
+ rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET);
+ writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
+ rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_SET);
+ } else {
+ writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
+ rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR);
+ writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
+ rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR);
+ }
+}
+
+static struct stmp3xxx_wdt_pdata wdt_pdata = {
+ .wdt_set_timeout = stmp3xxx_wdt_set_timeout,
+};
+
+static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
+{
+ struct platform_device *wdt_pdev =
+ platform_device_alloc("stmp3xxx_rtc_wdt", rtc_pdev->id);
+
+ if (wdt_pdev) {
+ wdt_pdev->dev.parent = &rtc_pdev->dev;
+ wdt_pdev->dev.platform_data = &wdt_pdata;
+ platform_device_add(wdt_pdev);
+ }
+}
+#else
+static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
+{
+}
+#endif /* CONFIG_STMP3XXX_RTC_WATCHDOG */
+
+static int stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data)
+{
+ int timeout = 5000; /* 3ms according to i.MX28 Ref Manual */
+ /*
+ * The i.MX28 Applications Processor Reference Manual, Rev. 1, 2010
+ * states:
+ * | The order in which registers are updated is
+ * | Persistent 0, 1, 2, 3, 4, 5, Alarm, Seconds.
+ * | (This list is in bitfield order, from LSB to MSB, as they would
+ * | appear in the STALE_REGS and NEW_REGS bitfields of the HW_RTC_STAT
+ * | register. For example, the Seconds register corresponds to
+ * | STALE_REGS or NEW_REGS containing 0x80.)
+ */
+ do {
+ if (!(readl(rtc_data->io + STMP3XXX_RTC_STAT) &
+ (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)))
+ return 0;
+ udelay(1);
+ } while (--timeout > 0);
+ return (readl(rtc_data->io + STMP3XXX_RTC_STAT) &
+ (0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT)) ? -ETIME : 0;
+}
+
+/* Time read/write */
+static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ int ret;
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ ret = stmp3xxx_wait_time(rtc_data);
+ if (ret)
+ return ret;
+
+ rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm);
+ return 0;
+}
+
+static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t)
+{
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS);
+ return stmp3xxx_wait_time(rtc_data);
+}
+
+/* interrupt(s) handler */
+static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id)
+{
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id);
+ u32 status = readl(rtc_data->io + STMP3XXX_RTC_CTRL);
+
+ if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) {
+ writel(STMP3XXX_RTC_CTRL_ALARM_IRQ,
+ rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
+ rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ if (enabled) {
+ writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
+ STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN,
+ rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET);
+ writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
+ rtc_data->io + STMP3XXX_RTC_CTRL_SET);
+ } else {
+ writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
+ STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN,
+ rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
+ writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
+ rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
+ }
+ return 0;
+}
+
+static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time);
+ return 0;
+}
+
+static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned long t;
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ rtc_tm_to_time(&alm->time, &t);
+ writel(t, rtc_data->io + STMP3XXX_RTC_ALARM);
+
+ stmp3xxx_alarm_irq_enable(dev, alm->enabled);
+
+ return 0;
+}
+
+static struct rtc_class_ops stmp3xxx_rtc_ops = {
+ .alarm_irq_enable =
+ stmp3xxx_alarm_irq_enable,
+ .read_time = stmp3xxx_rtc_gettime,
+ .set_mmss = stmp3xxx_rtc_set_mmss,
+ .read_alarm = stmp3xxx_rtc_read_alarm,
+ .set_alarm = stmp3xxx_rtc_set_alarm,
+};
+
+static int stmp3xxx_rtc_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev);
+
+ if (!rtc_data)
+ return 0;
+
+ writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
+ rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
+
+ return 0;
+}
+
+static int stmp3xxx_rtc_probe(struct platform_device *pdev)
+{
+ struct stmp3xxx_rtc_data *rtc_data;
+ struct resource *r;
+ u32 rtc_stat;
+ u32 pers0_set, pers0_clr;
+ u32 crystalfreq = 0;
+ int err;
+
+ rtc_data = devm_kzalloc(&pdev->dev, sizeof(*rtc_data), GFP_KERNEL);
+ if (!rtc_data)
+ return -ENOMEM;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ dev_err(&pdev->dev, "failed to get resource\n");
+ return -ENXIO;
+ }
+
+ rtc_data->io = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+ if (!rtc_data->io) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ return -EIO;
+ }
+
+ rtc_data->irq_alarm = platform_get_irq(pdev, 0);
+
+ rtc_stat = readl(rtc_data->io + STMP3XXX_RTC_STAT);
+ if (!(rtc_stat & STMP3XXX_RTC_STAT_RTC_PRESENT)) {
+ dev_err(&pdev->dev, "no device onboard\n");
+ return -ENODEV;
+ }
+
+ platform_set_drvdata(pdev, rtc_data);
+
+ err = stmp_reset_block(rtc_data->io);
+ if (err) {
+ dev_err(&pdev->dev, "stmp_reset_block failed: %d\n", err);
+ return err;
+ }
+
+ /*
+ * Obviously the rtc needs a clock input to be able to run.
+ * This clock can be provided by an external 32k crystal. If that one is
+ * missing XTAL must not be disabled in suspend which consumes a
+ * lot of power. Normally the presence and exact frequency (supported
+ * are 32000 Hz and 32768 Hz) is detectable from fuses, but as reality
+ * proves these fuses are not blown correctly on all machines, so the
+ * frequency can be overridden in the device tree.
+ */
+ if (rtc_stat & STMP3XXX_RTC_STAT_XTAL32000_PRESENT)
+ crystalfreq = 32000;
+ else if (rtc_stat & STMP3XXX_RTC_STAT_XTAL32768_PRESENT)
+ crystalfreq = 32768;
+
+ of_property_read_u32(pdev->dev.of_node, "stmp,crystal-freq",
+ &crystalfreq);
+
+ switch (crystalfreq) {
+ case 32000:
+ /* keep 32kHz crystal running in low-power mode */
+ pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ |
+ STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+ STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE;
+ pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP;
+ break;
+ case 32768:
+ /* keep 32.768kHz crystal running in low-power mode */
+ pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+ STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE;
+ pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP |
+ STMP3XXX_RTC_PERSISTENT0_XTAL32_FREQ;
+ break;
+ default:
+ dev_warn(&pdev->dev,
+ "invalid crystal-freq specified in device-tree. Assuming no crystal\n");
+ /* fall-through */
+ case 0:
+ /* keep XTAL on in low-power mode */
+ pers0_set = STMP3XXX_RTC_PERSISTENT0_XTAL24MHZ_PWRUP;
+ pers0_clr = STMP3XXX_RTC_PERSISTENT0_XTAL32KHZ_PWRUP |
+ STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE;
+ }
+
+ writel(pers0_set, rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET);
+
+ writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
+ STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
+ STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE | pers0_clr,
+ rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
+
+ writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN |
+ STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
+ rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
+
+ rtc_data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &stmp3xxx_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc_data->rtc))
+ return PTR_ERR(rtc_data->rtc);
+
+ err = devm_request_irq(&pdev->dev, rtc_data->irq_alarm,
+ stmp3xxx_rtc_interrupt, 0, "RTC alarm", &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot claim IRQ%d\n",
+ rtc_data->irq_alarm);
+ return err;
+ }
+
+ stmp3xxx_wdt_register(pdev);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stmp3xxx_rtc_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int stmp3xxx_rtc_resume(struct device *dev)
+{
+ struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+ stmp_reset_block(rtc_data->io);
+ writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
+ STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
+ STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
+ rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(stmp3xxx_rtc_pm_ops, stmp3xxx_rtc_suspend,
+ stmp3xxx_rtc_resume);
+
+static const struct of_device_id rtc_dt_ids[] = {
+ { .compatible = "fsl,stmp3xxx-rtc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rtc_dt_ids);
+
+static struct platform_driver stmp3xxx_rtcdrv = {
+ .probe = stmp3xxx_rtc_probe,
+ .remove = stmp3xxx_rtc_remove,
+ .driver = {
+ .name = "stmp3xxx-rtc",
+ .pm = &stmp3xxx_rtc_pm_ops,
+ .of_match_table = rtc_dt_ids,
+ },
+};
+
+module_platform_driver(stmp3xxx_rtcdrv);
+
+MODULE_DESCRIPTION("STMP3xxx RTC Driver");
+MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com> and "
+ "Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sun4v.c b/drivers/rtc/rtc-sun4v.c
new file mode 100644
index 000000000..7c696c12f
--- /dev/null
+++ b/drivers/rtc/rtc-sun4v.c
@@ -0,0 +1,105 @@
+/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems.
+ *
+ * Copyright (C) 2008 David S. Miller <davem@davemloft.net>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <asm/hypervisor.h>
+
+static unsigned long hypervisor_get_time(void)
+{
+ unsigned long ret, time;
+ int retries = 10000;
+
+retry:
+ ret = sun4v_tod_get(&time);
+ if (ret == HV_EOK)
+ return time;
+ if (ret == HV_EWOULDBLOCK) {
+ if (--retries > 0) {
+ udelay(100);
+ goto retry;
+ }
+ pr_warn("tod_get() timed out.\n");
+ return 0;
+ }
+ pr_warn("tod_get() not supported.\n");
+ return 0;
+}
+
+static int sun4v_read_time(struct device *dev, struct rtc_time *tm)
+{
+ rtc_time_to_tm(hypervisor_get_time(), tm);
+ return 0;
+}
+
+static int hypervisor_set_time(unsigned long secs)
+{
+ unsigned long ret;
+ int retries = 10000;
+
+retry:
+ ret = sun4v_tod_set(secs);
+ if (ret == HV_EOK)
+ return 0;
+ if (ret == HV_EWOULDBLOCK) {
+ if (--retries > 0) {
+ udelay(100);
+ goto retry;
+ }
+ pr_warn("tod_set() timed out.\n");
+ return -EAGAIN;
+ }
+ pr_warn("tod_set() not supported.\n");
+ return -EOPNOTSUPP;
+}
+
+static int sun4v_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long secs;
+ int err;
+
+ err = rtc_tm_to_time(tm, &secs);
+ if (err)
+ return err;
+
+ return hypervisor_set_time(secs);
+}
+
+static const struct rtc_class_ops sun4v_rtc_ops = {
+ .read_time = sun4v_read_time,
+ .set_time = sun4v_set_time,
+};
+
+static int __init sun4v_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&pdev->dev, "sun4v",
+ &sun4v_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
+}
+
+static struct platform_driver sun4v_rtc_driver = {
+ .driver = {
+ .name = "rtc-sun4v",
+ },
+};
+
+module_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe);
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("SUN4V RTC driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
new file mode 100644
index 000000000..c169a2cd4
--- /dev/null
+++ b/drivers/rtc/rtc-sun6i.c
@@ -0,0 +1,447 @@
+/*
+ * An RTC driver for Allwinner A31/A23
+ *
+ * Copyright (c) 2014, Chen-Yu Tsai <wens@csie.org>
+ *
+ * based on rtc-sunxi.c
+ *
+ * An RTC driver for Allwinner A10/A20
+ *
+ * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+
+/* Control register */
+#define SUN6I_LOSC_CTRL 0x0000
+#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
+#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
+#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
+
+/* RTC */
+#define SUN6I_RTC_YMD 0x0010
+#define SUN6I_RTC_HMS 0x0014
+
+/* Alarm 0 (counter) */
+#define SUN6I_ALRM_COUNTER 0x0020
+#define SUN6I_ALRM_CUR_VAL 0x0024
+#define SUN6I_ALRM_EN 0x0028
+#define SUN6I_ALRM_EN_CNT_EN BIT(0)
+#define SUN6I_ALRM_IRQ_EN 0x002c
+#define SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0)
+#define SUN6I_ALRM_IRQ_STA 0x0030
+#define SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0)
+
+/* Alarm 1 (wall clock) */
+#define SUN6I_ALRM1_EN 0x0044
+#define SUN6I_ALRM1_IRQ_EN 0x0048
+#define SUN6I_ALRM1_IRQ_STA 0x004c
+#define SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND BIT(0)
+
+/* Alarm config */
+#define SUN6I_ALARM_CONFIG 0x0050
+#define SUN6I_ALARM_CONFIG_WAKEUP BIT(0)
+
+/*
+ * Get date values
+ */
+#define SUN6I_DATE_GET_DAY_VALUE(x) ((x) & 0x0000001f)
+#define SUN6I_DATE_GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8)
+#define SUN6I_DATE_GET_YEAR_VALUE(x) (((x) & 0x003f0000) >> 16)
+#define SUN6I_LEAP_GET_VALUE(x) (((x) & 0x00400000) >> 22)
+
+/*
+ * Get time values
+ */
+#define SUN6I_TIME_GET_SEC_VALUE(x) ((x) & 0x0000003f)
+#define SUN6I_TIME_GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8)
+#define SUN6I_TIME_GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16)
+
+/*
+ * Set date values
+ */
+#define SUN6I_DATE_SET_DAY_VALUE(x) ((x) & 0x0000001f)
+#define SUN6I_DATE_SET_MON_VALUE(x) ((x) << 8 & 0x00000f00)
+#define SUN6I_DATE_SET_YEAR_VALUE(x) ((x) << 16 & 0x003f0000)
+#define SUN6I_LEAP_SET_VALUE(x) ((x) << 22 & 0x00400000)
+
+/*
+ * Set time values
+ */
+#define SUN6I_TIME_SET_SEC_VALUE(x) ((x) & 0x0000003f)
+#define SUN6I_TIME_SET_MIN_VALUE(x) ((x) << 8 & 0x00003f00)
+#define SUN6I_TIME_SET_HOUR_VALUE(x) ((x) << 16 & 0x001f0000)
+
+/*
+ * The year parameter passed to the driver is usually an offset relative to
+ * the year 1900. This macro is used to convert this offset to another one
+ * relative to the minimum year allowed by the hardware.
+ *
+ * The year range is 1970 - 2033. This range is selected to match Allwinner's
+ * driver, even though it is somewhat limited.
+ */
+#define SUN6I_YEAR_MIN 1970
+#define SUN6I_YEAR_MAX 2033
+#define SUN6I_YEAR_OFF (SUN6I_YEAR_MIN - 1900)
+
+struct sun6i_rtc_dev {
+ struct rtc_device *rtc;
+ struct device *dev;
+ void __iomem *base;
+ int irq;
+ unsigned long alarm;
+};
+
+static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id)
+{
+ struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id;
+ u32 val;
+
+ val = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+
+ if (val & SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND) {
+ val |= SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND;
+ writel(val, chip->base + SUN6I_ALRM_IRQ_STA);
+
+ rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void sun6i_rtc_setaie(int to, struct sun6i_rtc_dev *chip)
+{
+ u32 alrm_val = 0;
+ u32 alrm_irq_val = 0;
+ u32 alrm_wake_val = 0;
+
+ if (to) {
+ alrm_val = SUN6I_ALRM_EN_CNT_EN;
+ alrm_irq_val = SUN6I_ALRM_IRQ_EN_CNT_IRQ_EN;
+ alrm_wake_val = SUN6I_ALARM_CONFIG_WAKEUP;
+ } else {
+ writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
+ chip->base + SUN6I_ALRM_IRQ_STA);
+ }
+
+ writel(alrm_val, chip->base + SUN6I_ALRM_EN);
+ writel(alrm_irq_val, chip->base + SUN6I_ALRM_IRQ_EN);
+ writel(alrm_wake_val, chip->base + SUN6I_ALARM_CONFIG);
+}
+
+static int sun6i_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 date, time;
+
+ /*
+ * read again in case it changes
+ */
+ do {
+ date = readl(chip->base + SUN6I_RTC_YMD);
+ time = readl(chip->base + SUN6I_RTC_HMS);
+ } while ((date != readl(chip->base + SUN6I_RTC_YMD)) ||
+ (time != readl(chip->base + SUN6I_RTC_HMS)));
+
+ rtc_tm->tm_sec = SUN6I_TIME_GET_SEC_VALUE(time);
+ rtc_tm->tm_min = SUN6I_TIME_GET_MIN_VALUE(time);
+ rtc_tm->tm_hour = SUN6I_TIME_GET_HOUR_VALUE(time);
+
+ rtc_tm->tm_mday = SUN6I_DATE_GET_DAY_VALUE(date);
+ rtc_tm->tm_mon = SUN6I_DATE_GET_MON_VALUE(date);
+ rtc_tm->tm_year = SUN6I_DATE_GET_YEAR_VALUE(date);
+
+ rtc_tm->tm_mon -= 1;
+
+ /*
+ * switch from (data_year->min)-relative offset to
+ * a (1900)-relative one
+ */
+ rtc_tm->tm_year += SUN6I_YEAR_OFF;
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 alrm_st;
+ u32 alrm_en;
+
+ alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN);
+ alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA);
+ wkalrm->enabled = !!(alrm_en & SUN6I_ALRM_EN_CNT_EN);
+ wkalrm->pending = !!(alrm_st & SUN6I_ALRM_EN_CNT_EN);
+ rtc_time_to_tm(chip->alarm, &wkalrm->time);
+
+ return 0;
+}
+
+static int sun6i_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &wkalrm->time;
+ struct rtc_time tm_now;
+ unsigned long time_now = 0;
+ unsigned long time_set = 0;
+ unsigned long time_gap = 0;
+ int ret = 0;
+
+ ret = sun6i_rtc_gettime(dev, &tm_now);
+ if (ret < 0) {
+ dev_err(dev, "Error in getting time\n");
+ return -EINVAL;
+ }
+
+ rtc_tm_to_time(alrm_tm, &time_set);
+ rtc_tm_to_time(&tm_now, &time_now);
+ if (time_set <= time_now) {
+ dev_err(dev, "Date to set in the past\n");
+ return -EINVAL;
+ }
+
+ time_gap = time_set - time_now;
+
+ if (time_gap > U32_MAX) {
+ dev_err(dev, "Date too far in the future\n");
+ return -EINVAL;
+ }
+
+ sun6i_rtc_setaie(0, chip);
+ writel(0, chip->base + SUN6I_ALRM_COUNTER);
+ usleep_range(100, 300);
+
+ writel(time_gap, chip->base + SUN6I_ALRM_COUNTER);
+ chip->alarm = time_set;
+
+ sun6i_rtc_setaie(wkalrm->enabled, chip);
+
+ return 0;
+}
+
+static int sun6i_rtc_wait(struct sun6i_rtc_dev *chip, int offset,
+ unsigned int mask, unsigned int ms_timeout)
+{
+ const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
+ u32 reg;
+
+ do {
+ reg = readl(chip->base + offset);
+ reg &= mask;
+
+ if (!reg)
+ return 0;
+
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+static int sun6i_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 date = 0;
+ u32 time = 0;
+ int year;
+
+ year = rtc_tm->tm_year + 1900;
+ if (year < SUN6I_YEAR_MIN || year > SUN6I_YEAR_MAX) {
+ dev_err(dev, "rtc only supports year in range %d - %d\n",
+ SUN6I_YEAR_MIN, SUN6I_YEAR_MAX);
+ return -EINVAL;
+ }
+
+ rtc_tm->tm_year -= SUN6I_YEAR_OFF;
+ rtc_tm->tm_mon += 1;
+
+ date = SUN6I_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
+ SUN6I_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
+ SUN6I_DATE_SET_YEAR_VALUE(rtc_tm->tm_year);
+
+ if (is_leap_year(year))
+ date |= SUN6I_LEAP_SET_VALUE(1);
+
+ time = SUN6I_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) |
+ SUN6I_TIME_SET_MIN_VALUE(rtc_tm->tm_min) |
+ SUN6I_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
+
+ /* Check whether registers are writable */
+ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+ SUN6I_LOSC_CTRL_ACC_MASK, 50)) {
+ dev_err(dev, "rtc is still busy.\n");
+ return -EBUSY;
+ }
+
+ writel(time, chip->base + SUN6I_RTC_HMS);
+
+ /*
+ * After writing the RTC HH-MM-SS register, the
+ * SUN6I_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
+ * be cleared until the real writing operation is finished
+ */
+
+ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+ SUN6I_LOSC_CTRL_RTC_HMS_ACC, 50)) {
+ dev_err(dev, "Failed to set rtc time.\n");
+ return -ETIMEDOUT;
+ }
+
+ writel(date, chip->base + SUN6I_RTC_YMD);
+
+ /*
+ * After writing the RTC YY-MM-DD register, the
+ * SUN6I_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
+ * be cleared until the real writing operation is finished
+ */
+
+ if (sun6i_rtc_wait(chip, SUN6I_LOSC_CTRL,
+ SUN6I_LOSC_CTRL_RTC_YMD_ACC, 50)) {
+ dev_err(dev, "Failed to set rtc time.\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int sun6i_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (!enabled)
+ sun6i_rtc_setaie(enabled, chip);
+
+ return 0;
+}
+
+static const struct rtc_class_ops sun6i_rtc_ops = {
+ .read_time = sun6i_rtc_gettime,
+ .set_time = sun6i_rtc_settime,
+ .read_alarm = sun6i_rtc_getalarm,
+ .set_alarm = sun6i_rtc_setalarm,
+ .alarm_irq_enable = sun6i_rtc_alarm_irq_enable
+};
+
+static int sun6i_rtc_probe(struct platform_device *pdev)
+{
+ struct sun6i_rtc_dev *chip;
+ struct resource *res;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, chip);
+ chip->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return chip->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
+ 0, dev_name(&pdev->dev), chip);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return ret;
+ }
+
+ /* clear the alarm counter value */
+ writel(0, chip->base + SUN6I_ALRM_COUNTER);
+
+ /* disable counter alarm */
+ writel(0, chip->base + SUN6I_ALRM_EN);
+
+ /* disable counter alarm interrupt */
+ writel(0, chip->base + SUN6I_ALRM_IRQ_EN);
+
+ /* disable week alarm */
+ writel(0, chip->base + SUN6I_ALRM1_EN);
+
+ /* disable week alarm interrupt */
+ writel(0, chip->base + SUN6I_ALRM1_IRQ_EN);
+
+ /* clear counter alarm pending interrupts */
+ writel(SUN6I_ALRM_IRQ_STA_CNT_IRQ_PEND,
+ chip->base + SUN6I_ALRM_IRQ_STA);
+
+ /* clear week alarm pending interrupts */
+ writel(SUN6I_ALRM1_IRQ_STA_WEEK_IRQ_PEND,
+ chip->base + SUN6I_ALRM1_IRQ_STA);
+
+ /* disable alarm wakeup */
+ writel(0, chip->base + SUN6I_ALARM_CONFIG);
+
+ chip->rtc = rtc_device_register("rtc-sun6i", &pdev->dev,
+ &sun6i_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ return PTR_ERR(chip->rtc);
+ }
+
+ dev_info(&pdev->dev, "RTC enabled\n");
+
+ return 0;
+}
+
+static int sun6i_rtc_remove(struct platform_device *pdev)
+{
+ struct sun6i_rtc_dev *chip = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(chip->rtc);
+
+ return 0;
+}
+
+static const struct of_device_id sun6i_rtc_dt_ids[] = {
+ { .compatible = "allwinner,sun6i-a31-rtc" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
+
+static struct platform_driver sun6i_rtc_driver = {
+ .probe = sun6i_rtc_probe,
+ .remove = sun6i_rtc_remove,
+ .driver = {
+ .name = "sun6i-rtc",
+ .of_match_table = sun6i_rtc_dt_ids,
+ },
+};
+
+module_platform_driver(sun6i_rtc_driver);
+
+MODULE_DESCRIPTION("sun6i RTC driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c
new file mode 100644
index 000000000..6e678fa4d
--- /dev/null
+++ b/drivers/rtc/rtc-sunxi.c
@@ -0,0 +1,522 @@
+/*
+ * An RTC driver for Allwinner A10/A20
+ *
+ * Copyright (c) 2013, Carlo Caione <carlo.caione@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+
+#define SUNXI_LOSC_CTRL 0x0000
+#define SUNXI_LOSC_CTRL_RTC_HMS_ACC BIT(8)
+#define SUNXI_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+
+#define SUNXI_RTC_YMD 0x0004
+
+#define SUNXI_RTC_HMS 0x0008
+
+#define SUNXI_ALRM_DHMS 0x000c
+
+#define SUNXI_ALRM_EN 0x0014
+#define SUNXI_ALRM_EN_CNT_EN BIT(8)
+
+#define SUNXI_ALRM_IRQ_EN 0x0018
+#define SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN BIT(0)
+
+#define SUNXI_ALRM_IRQ_STA 0x001c
+#define SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND BIT(0)
+
+#define SUNXI_MASK_DH 0x0000001f
+#define SUNXI_MASK_SM 0x0000003f
+#define SUNXI_MASK_M 0x0000000f
+#define SUNXI_MASK_LY 0x00000001
+#define SUNXI_MASK_D 0x00000ffe
+#define SUNXI_MASK_M 0x0000000f
+
+#define SUNXI_GET(x, mask, shift) (((x) & ((mask) << (shift))) \
+ >> (shift))
+
+#define SUNXI_SET(x, mask, shift) (((x) & (mask)) << (shift))
+
+/*
+ * Get date values
+ */
+#define SUNXI_DATE_GET_DAY_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 0)
+#define SUNXI_DATE_GET_MON_VALUE(x) SUNXI_GET(x, SUNXI_MASK_M, 8)
+#define SUNXI_DATE_GET_YEAR_VALUE(x, mask) SUNXI_GET(x, mask, 16)
+
+/*
+ * Get time values
+ */
+#define SUNXI_TIME_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0)
+#define SUNXI_TIME_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8)
+#define SUNXI_TIME_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16)
+
+/*
+ * Get alarm values
+ */
+#define SUNXI_ALRM_GET_SEC_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 0)
+#define SUNXI_ALRM_GET_MIN_VALUE(x) SUNXI_GET(x, SUNXI_MASK_SM, 8)
+#define SUNXI_ALRM_GET_HOUR_VALUE(x) SUNXI_GET(x, SUNXI_MASK_DH, 16)
+
+/*
+ * Set date values
+ */
+#define SUNXI_DATE_SET_DAY_VALUE(x) SUNXI_DATE_GET_DAY_VALUE(x)
+#define SUNXI_DATE_SET_MON_VALUE(x) SUNXI_SET(x, SUNXI_MASK_M, 8)
+#define SUNXI_DATE_SET_YEAR_VALUE(x, mask) SUNXI_SET(x, mask, 16)
+#define SUNXI_LEAP_SET_VALUE(x, shift) SUNXI_SET(x, SUNXI_MASK_LY, shift)
+
+/*
+ * Set time values
+ */
+#define SUNXI_TIME_SET_SEC_VALUE(x) SUNXI_TIME_GET_SEC_VALUE(x)
+#define SUNXI_TIME_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8)
+#define SUNXI_TIME_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16)
+
+/*
+ * Set alarm values
+ */
+#define SUNXI_ALRM_SET_SEC_VALUE(x) SUNXI_ALRM_GET_SEC_VALUE(x)
+#define SUNXI_ALRM_SET_MIN_VALUE(x) SUNXI_SET(x, SUNXI_MASK_SM, 8)
+#define SUNXI_ALRM_SET_HOUR_VALUE(x) SUNXI_SET(x, SUNXI_MASK_DH, 16)
+#define SUNXI_ALRM_SET_DAY_VALUE(x) SUNXI_SET(x, SUNXI_MASK_D, 21)
+
+/*
+ * Time unit conversions
+ */
+#define SEC_IN_MIN 60
+#define SEC_IN_HOUR (60 * SEC_IN_MIN)
+#define SEC_IN_DAY (24 * SEC_IN_HOUR)
+
+/*
+ * The year parameter passed to the driver is usually an offset relative to
+ * the year 1900. This macro is used to convert this offset to another one
+ * relative to the minimum year allowed by the hardware.
+ */
+#define SUNXI_YEAR_OFF(x) ((x)->min - 1900)
+
+/*
+ * min and max year are arbitrary set considering the limited range of the
+ * hardware register field
+ */
+struct sunxi_rtc_data_year {
+ unsigned int min; /* min year allowed */
+ unsigned int max; /* max year allowed */
+ unsigned int mask; /* mask for the year field */
+ unsigned char leap_shift; /* bit shift to get the leap year */
+};
+
+static struct sunxi_rtc_data_year data_year_param[] = {
+ [0] = {
+ .min = 2010,
+ .max = 2073,
+ .mask = 0x3f,
+ .leap_shift = 22,
+ },
+ [1] = {
+ .min = 1970,
+ .max = 2225,
+ .mask = 0xff,
+ .leap_shift = 24,
+ },
+};
+
+struct sunxi_rtc_dev {
+ struct rtc_device *rtc;
+ struct device *dev;
+ struct sunxi_rtc_data_year *data_year;
+ void __iomem *base;
+ int irq;
+};
+
+static irqreturn_t sunxi_rtc_alarmirq(int irq, void *id)
+{
+ struct sunxi_rtc_dev *chip = (struct sunxi_rtc_dev *) id;
+ u32 val;
+
+ val = readl(chip->base + SUNXI_ALRM_IRQ_STA);
+
+ if (val & SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND) {
+ val |= SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND;
+ writel(val, chip->base + SUNXI_ALRM_IRQ_STA);
+
+ rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF);
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void sunxi_rtc_setaie(int to, struct sunxi_rtc_dev *chip)
+{
+ u32 alrm_val = 0;
+ u32 alrm_irq_val = 0;
+
+ if (to) {
+ alrm_val = readl(chip->base + SUNXI_ALRM_EN);
+ alrm_val |= SUNXI_ALRM_EN_CNT_EN;
+
+ alrm_irq_val = readl(chip->base + SUNXI_ALRM_IRQ_EN);
+ alrm_irq_val |= SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN;
+ } else {
+ writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND,
+ chip->base + SUNXI_ALRM_IRQ_STA);
+ }
+
+ writel(alrm_val, chip->base + SUNXI_ALRM_EN);
+ writel(alrm_irq_val, chip->base + SUNXI_ALRM_IRQ_EN);
+}
+
+static int sunxi_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &wkalrm->time;
+ u32 alrm;
+ u32 alrm_en;
+ u32 date;
+
+ alrm = readl(chip->base + SUNXI_ALRM_DHMS);
+ date = readl(chip->base + SUNXI_RTC_YMD);
+
+ alrm_tm->tm_sec = SUNXI_ALRM_GET_SEC_VALUE(alrm);
+ alrm_tm->tm_min = SUNXI_ALRM_GET_MIN_VALUE(alrm);
+ alrm_tm->tm_hour = SUNXI_ALRM_GET_HOUR_VALUE(alrm);
+
+ alrm_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date);
+ alrm_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date);
+ alrm_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date,
+ chip->data_year->mask);
+
+ alrm_tm->tm_mon -= 1;
+
+ /*
+ * switch from (data_year->min)-relative offset to
+ * a (1900)-relative one
+ */
+ alrm_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year);
+
+ alrm_en = readl(chip->base + SUNXI_ALRM_IRQ_EN);
+ if (alrm_en & SUNXI_ALRM_EN_CNT_EN)
+ wkalrm->enabled = 1;
+
+ return 0;
+}
+
+static int sunxi_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 date, time;
+
+ /*
+ * read again in case it changes
+ */
+ do {
+ date = readl(chip->base + SUNXI_RTC_YMD);
+ time = readl(chip->base + SUNXI_RTC_HMS);
+ } while ((date != readl(chip->base + SUNXI_RTC_YMD)) ||
+ (time != readl(chip->base + SUNXI_RTC_HMS)));
+
+ rtc_tm->tm_sec = SUNXI_TIME_GET_SEC_VALUE(time);
+ rtc_tm->tm_min = SUNXI_TIME_GET_MIN_VALUE(time);
+ rtc_tm->tm_hour = SUNXI_TIME_GET_HOUR_VALUE(time);
+
+ rtc_tm->tm_mday = SUNXI_DATE_GET_DAY_VALUE(date);
+ rtc_tm->tm_mon = SUNXI_DATE_GET_MON_VALUE(date);
+ rtc_tm->tm_year = SUNXI_DATE_GET_YEAR_VALUE(date,
+ chip->data_year->mask);
+
+ rtc_tm->tm_mon -= 1;
+
+ /*
+ * switch from (data_year->min)-relative offset to
+ * a (1900)-relative one
+ */
+ rtc_tm->tm_year += SUNXI_YEAR_OFF(chip->data_year);
+
+ return rtc_valid_tm(rtc_tm);
+}
+
+static int sunxi_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);
+ struct rtc_time *alrm_tm = &wkalrm->time;
+ struct rtc_time tm_now;
+ u32 alrm = 0;
+ unsigned long time_now = 0;
+ unsigned long time_set = 0;
+ unsigned long time_gap = 0;
+ unsigned long time_gap_day = 0;
+ unsigned long time_gap_hour = 0;
+ unsigned long time_gap_min = 0;
+ int ret = 0;
+
+ ret = sunxi_rtc_gettime(dev, &tm_now);
+ if (ret < 0) {
+ dev_err(dev, "Error in getting time\n");
+ return -EINVAL;
+ }
+
+ rtc_tm_to_time(alrm_tm, &time_set);
+ rtc_tm_to_time(&tm_now, &time_now);
+ if (time_set <= time_now) {
+ dev_err(dev, "Date to set in the past\n");
+ return -EINVAL;
+ }
+
+ time_gap = time_set - time_now;
+ time_gap_day = time_gap / SEC_IN_DAY;
+ time_gap -= time_gap_day * SEC_IN_DAY;
+ time_gap_hour = time_gap / SEC_IN_HOUR;
+ time_gap -= time_gap_hour * SEC_IN_HOUR;
+ time_gap_min = time_gap / SEC_IN_MIN;
+ time_gap -= time_gap_min * SEC_IN_MIN;
+
+ if (time_gap_day > 255) {
+ dev_err(dev, "Day must be in the range 0 - 255\n");
+ return -EINVAL;
+ }
+
+ sunxi_rtc_setaie(0, chip);
+ writel(0, chip->base + SUNXI_ALRM_DHMS);
+ usleep_range(100, 300);
+
+ alrm = SUNXI_ALRM_SET_SEC_VALUE(time_gap) |
+ SUNXI_ALRM_SET_MIN_VALUE(time_gap_min) |
+ SUNXI_ALRM_SET_HOUR_VALUE(time_gap_hour) |
+ SUNXI_ALRM_SET_DAY_VALUE(time_gap_day);
+ writel(alrm, chip->base + SUNXI_ALRM_DHMS);
+
+ writel(0, chip->base + SUNXI_ALRM_IRQ_EN);
+ writel(SUNXI_ALRM_IRQ_EN_CNT_IRQ_EN, chip->base + SUNXI_ALRM_IRQ_EN);
+
+ sunxi_rtc_setaie(wkalrm->enabled, chip);
+
+ return 0;
+}
+
+static int sunxi_rtc_wait(struct sunxi_rtc_dev *chip, int offset,
+ unsigned int mask, unsigned int ms_timeout)
+{
+ const unsigned long timeout = jiffies + msecs_to_jiffies(ms_timeout);
+ u32 reg;
+
+ do {
+ reg = readl(chip->base + offset);
+ reg &= mask;
+
+ if (reg == mask)
+ return 0;
+
+ } while (time_before(jiffies, timeout));
+
+ return -ETIMEDOUT;
+}
+
+static int sunxi_rtc_settime(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);
+ u32 date = 0;
+ u32 time = 0;
+ int year;
+
+ /*
+ * the input rtc_tm->tm_year is the offset relative to 1900. We use
+ * the SUNXI_YEAR_OFF macro to rebase it with respect to the min year
+ * allowed by the hardware
+ */
+
+ year = rtc_tm->tm_year + 1900;
+ if (year < chip->data_year->min || year > chip->data_year->max) {
+ dev_err(dev, "rtc only supports year in range %d - %d\n",
+ chip->data_year->min, chip->data_year->max);
+ return -EINVAL;
+ }
+
+ rtc_tm->tm_year -= SUNXI_YEAR_OFF(chip->data_year);
+ rtc_tm->tm_mon += 1;
+
+ date = SUNXI_DATE_SET_DAY_VALUE(rtc_tm->tm_mday) |
+ SUNXI_DATE_SET_MON_VALUE(rtc_tm->tm_mon) |
+ SUNXI_DATE_SET_YEAR_VALUE(rtc_tm->tm_year,
+ chip->data_year->mask);
+
+ if (is_leap_year(year))
+ date |= SUNXI_LEAP_SET_VALUE(1, chip->data_year->leap_shift);
+
+ time = SUNXI_TIME_SET_SEC_VALUE(rtc_tm->tm_sec) |
+ SUNXI_TIME_SET_MIN_VALUE(rtc_tm->tm_min) |
+ SUNXI_TIME_SET_HOUR_VALUE(rtc_tm->tm_hour);
+
+ writel(0, chip->base + SUNXI_RTC_HMS);
+ writel(0, chip->base + SUNXI_RTC_YMD);
+
+ writel(time, chip->base + SUNXI_RTC_HMS);
+
+ /*
+ * After writing the RTC HH-MM-SS register, the
+ * SUNXI_LOSC_CTRL_RTC_HMS_ACC bit is set and it will not
+ * be cleared until the real writing operation is finished
+ */
+
+ if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL,
+ SUNXI_LOSC_CTRL_RTC_HMS_ACC, 50)) {
+ dev_err(dev, "Failed to set rtc time.\n");
+ return -1;
+ }
+
+ writel(date, chip->base + SUNXI_RTC_YMD);
+
+ /*
+ * After writing the RTC YY-MM-DD register, the
+ * SUNXI_LOSC_CTRL_RTC_YMD_ACC bit is set and it will not
+ * be cleared until the real writing operation is finished
+ */
+
+ if (sunxi_rtc_wait(chip, SUNXI_LOSC_CTRL,
+ SUNXI_LOSC_CTRL_RTC_YMD_ACC, 50)) {
+ dev_err(dev, "Failed to set rtc time.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sunxi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct sunxi_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (!enabled)
+ sunxi_rtc_setaie(enabled, chip);
+
+ return 0;
+}
+
+static const struct rtc_class_ops sunxi_rtc_ops = {
+ .read_time = sunxi_rtc_gettime,
+ .set_time = sunxi_rtc_settime,
+ .read_alarm = sunxi_rtc_getalarm,
+ .set_alarm = sunxi_rtc_setalarm,
+ .alarm_irq_enable = sunxi_rtc_alarm_irq_enable
+};
+
+static const struct of_device_id sunxi_rtc_dt_ids[] = {
+ { .compatible = "allwinner,sun4i-a10-rtc", .data = &data_year_param[0] },
+ { .compatible = "allwinner,sun7i-a20-rtc", .data = &data_year_param[1] },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, sunxi_rtc_dt_ids);
+
+static int sunxi_rtc_probe(struct platform_device *pdev)
+{
+ struct sunxi_rtc_dev *chip;
+ struct resource *res;
+ const struct of_device_id *of_id;
+ int ret;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, chip);
+ chip->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ chip->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(chip->base))
+ return PTR_ERR(chip->base);
+
+ chip->irq = platform_get_irq(pdev, 0);
+ if (chip->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return chip->irq;
+ }
+ ret = devm_request_irq(&pdev->dev, chip->irq, sunxi_rtc_alarmirq,
+ 0, dev_name(&pdev->dev), chip);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return ret;
+ }
+
+ of_id = of_match_device(sunxi_rtc_dt_ids, &pdev->dev);
+ if (!of_id) {
+ dev_err(&pdev->dev, "Unable to setup RTC data\n");
+ return -ENODEV;
+ }
+ chip->data_year = (struct sunxi_rtc_data_year *) of_id->data;
+
+ /* clear the alarm count value */
+ writel(0, chip->base + SUNXI_ALRM_DHMS);
+
+ /* disable alarm, not generate irq pending */
+ writel(0, chip->base + SUNXI_ALRM_EN);
+
+ /* disable alarm week/cnt irq, unset to cpu */
+ writel(0, chip->base + SUNXI_ALRM_IRQ_EN);
+
+ /* clear alarm week/cnt irq pending */
+ writel(SUNXI_ALRM_IRQ_STA_CNT_IRQ_PEND, chip->base +
+ SUNXI_ALRM_IRQ_STA);
+
+ chip->rtc = rtc_device_register("rtc-sunxi", &pdev->dev,
+ &sunxi_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ dev_err(&pdev->dev, "unable to register device\n");
+ return PTR_ERR(chip->rtc);
+ }
+
+ dev_info(&pdev->dev, "RTC enabled\n");
+
+ return 0;
+}
+
+static int sunxi_rtc_remove(struct platform_device *pdev)
+{
+ struct sunxi_rtc_dev *chip = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(chip->rtc);
+
+ return 0;
+}
+
+static struct platform_driver sunxi_rtc_driver = {
+ .probe = sunxi_rtc_probe,
+ .remove = sunxi_rtc_remove,
+ .driver = {
+ .name = "sunxi-rtc",
+ .of_match_table = sunxi_rtc_dt_ids,
+ },
+};
+
+module_platform_driver(sunxi_rtc_driver);
+
+MODULE_DESCRIPTION("sunxi RTC driver");
+MODULE_AUTHOR("Carlo Caione <carlo.caione@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
new file mode 100644
index 000000000..babd43bf3
--- /dev/null
+++ b/drivers/rtc/rtc-sysfs.c
@@ -0,0 +1,265 @@
+/*
+ * RTC subsystem, sysfs interface
+ *
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+
+#include "rtc-core.h"
+
+
+/* device attributes */
+
+/*
+ * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's
+ * ideally UTC. However, PCs that also boot to MS-Windows normally use
+ * the local time and change to match daylight savings time. That affects
+ * attributes including date, time, since_epoch, and wakealarm.
+ */
+
+static ssize_t
+name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t
+date_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t retval;
+ struct rtc_time tm;
+
+ retval = rtc_read_time(to_rtc_device(dev), &tm);
+ if (retval == 0) {
+ retval = sprintf(buf, "%04d-%02d-%02d\n",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
+ }
+
+ return retval;
+}
+static DEVICE_ATTR_RO(date);
+
+static ssize_t
+time_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t retval;
+ struct rtc_time tm;
+
+ retval = rtc_read_time(to_rtc_device(dev), &tm);
+ if (retval == 0) {
+ retval = sprintf(buf, "%02d:%02d:%02d\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ }
+
+ return retval;
+}
+static DEVICE_ATTR_RO(time);
+
+static ssize_t
+since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t retval;
+ struct rtc_time tm;
+
+ retval = rtc_read_time(to_rtc_device(dev), &tm);
+ if (retval == 0) {
+ unsigned long time;
+ rtc_tm_to_time(&tm, &time);
+ retval = sprintf(buf, "%lu\n", time);
+ }
+
+ return retval;
+}
+static DEVICE_ATTR_RO(since_epoch);
+
+static ssize_t
+max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
+}
+
+static ssize_t
+max_user_freq_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ struct rtc_device *rtc = to_rtc_device(dev);
+ unsigned long val = simple_strtoul(buf, NULL, 0);
+
+ if (val >= 4096 || val == 0)
+ return -EINVAL;
+
+ rtc->max_user_freq = (int)val;
+
+ return n;
+}
+static DEVICE_ATTR_RW(max_user_freq);
+
+/**
+ * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time
+ *
+ * Returns 1 if the system clock was set by this RTC at the last
+ * boot or resume event.
+ */
+static ssize_t
+hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+#ifdef CONFIG_RTC_HCTOSYS_DEVICE
+ if (rtc_hctosys_ret == 0 &&
+ strcmp(dev_name(&to_rtc_device(dev)->dev),
+ CONFIG_RTC_HCTOSYS_DEVICE) == 0)
+ return sprintf(buf, "1\n");
+ else
+#endif
+ return sprintf(buf, "0\n");
+}
+static DEVICE_ATTR_RO(hctosys);
+
+static struct attribute *rtc_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_date.attr,
+ &dev_attr_time.attr,
+ &dev_attr_since_epoch.attr,
+ &dev_attr_max_user_freq.attr,
+ &dev_attr_hctosys.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(rtc);
+
+static ssize_t
+rtc_sysfs_show_wakealarm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t retval;
+ unsigned long alarm;
+ struct rtc_wkalrm alm;
+
+ /* Don't show disabled alarms. For uniformity, RTC alarms are
+ * conceptually one-shot, even though some common RTCs (on PCs)
+ * don't actually work that way.
+ *
+ * NOTE: RTC implementations where the alarm doesn't match an
+ * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC
+ * alarms after they trigger, to ensure one-shot semantics.
+ */
+ retval = rtc_read_alarm(to_rtc_device(dev), &alm);
+ if (retval == 0 && alm.enabled) {
+ rtc_tm_to_time(&alm.time, &alarm);
+ retval = sprintf(buf, "%lu\n", alarm);
+ }
+
+ return retval;
+}
+
+static ssize_t
+rtc_sysfs_set_wakealarm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ ssize_t retval;
+ unsigned long now, alarm;
+ unsigned long push = 0;
+ struct rtc_wkalrm alm;
+ struct rtc_device *rtc = to_rtc_device(dev);
+ char *buf_ptr;
+ int adjust = 0;
+
+ /* Only request alarms that trigger in the future. Disable them
+ * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
+ */
+ retval = rtc_read_time(rtc, &alm.time);
+ if (retval < 0)
+ return retval;
+ rtc_tm_to_time(&alm.time, &now);
+
+ buf_ptr = (char *)buf;
+ if (*buf_ptr == '+') {
+ buf_ptr++;
+ if (*buf_ptr == '=') {
+ buf_ptr++;
+ push = 1;
+ } else
+ adjust = 1;
+ }
+ alarm = simple_strtoul(buf_ptr, NULL, 0);
+ if (adjust) {
+ alarm += now;
+ }
+ if (alarm > now || push) {
+ /* Avoid accidentally clobbering active alarms; we can't
+ * entirely prevent that here, without even the minimal
+ * locking from the /dev/rtcN api.
+ */
+ retval = rtc_read_alarm(rtc, &alm);
+ if (retval < 0)
+ return retval;
+ if (alm.enabled) {
+ if (push) {
+ rtc_tm_to_time(&alm.time, &push);
+ alarm += push;
+ } else
+ return -EBUSY;
+ } else if (push)
+ return -EINVAL;
+ alm.enabled = 1;
+ } else {
+ alm.enabled = 0;
+
+ /* Provide a valid future alarm time. Linux isn't EFI,
+ * this time won't be ignored when disabling the alarm.
+ */
+ alarm = now + 300;
+ }
+ rtc_time_to_tm(alarm, &alm.time);
+
+ retval = rtc_set_alarm(rtc, &alm);
+ return (retval < 0) ? retval : n;
+}
+static DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
+ rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);
+
+
+/* The reason to trigger an alarm with no process watching it (via sysfs)
+ * is its side effect: waking from a system state like suspend-to-RAM or
+ * suspend-to-disk. So: no attribute unless that side effect is possible.
+ * (Userspace may disable that mechanism later.)
+ */
+static inline int rtc_does_wakealarm(struct rtc_device *rtc)
+{
+ if (!device_can_wakeup(rtc->dev.parent))
+ return 0;
+ return rtc->ops->set_alarm != NULL;
+}
+
+
+void rtc_sysfs_add_device(struct rtc_device *rtc)
+{
+ int err;
+
+ /* not all RTCs support both alarms and wakeup */
+ if (!rtc_does_wakealarm(rtc))
+ return;
+
+ err = device_create_file(&rtc->dev, &dev_attr_wakealarm);
+ if (err)
+ dev_err(rtc->dev.parent,
+ "failed to create alarm attribute, %d\n", err);
+}
+
+void rtc_sysfs_del_device(struct rtc_device *rtc)
+{
+ /* REVISIT did we add it successfully? */
+ if (rtc_does_wakealarm(rtc))
+ device_remove_file(&rtc->dev, &dev_attr_wakealarm);
+}
+
+void __init rtc_sysfs_init(struct class *rtc_class)
+{
+ rtc_class->dev_groups = rtc_groups;
+}
diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c
new file mode 100644
index 000000000..60232bd36
--- /dev/null
+++ b/drivers/rtc/rtc-tegra.c
@@ -0,0 +1,434 @@
+/*
+ * An RTC driver for the NVIDIA Tegra 200 series internal RTC.
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+
+/* set to 1 = busy every eight 32kHz clocks during copy of sec+msec to AHB */
+#define TEGRA_RTC_REG_BUSY 0x004
+#define TEGRA_RTC_REG_SECONDS 0x008
+/* when msec is read, the seconds are buffered into shadow seconds. */
+#define TEGRA_RTC_REG_SHADOW_SECONDS 0x00c
+#define TEGRA_RTC_REG_MILLI_SECONDS 0x010
+#define TEGRA_RTC_REG_SECONDS_ALARM0 0x014
+#define TEGRA_RTC_REG_SECONDS_ALARM1 0x018
+#define TEGRA_RTC_REG_MILLI_SECONDS_ALARM0 0x01c
+#define TEGRA_RTC_REG_INTR_MASK 0x028
+/* write 1 bits to clear status bits */
+#define TEGRA_RTC_REG_INTR_STATUS 0x02c
+
+/* bits in INTR_MASK */
+#define TEGRA_RTC_INTR_MASK_MSEC_CDN_ALARM (1<<4)
+#define TEGRA_RTC_INTR_MASK_SEC_CDN_ALARM (1<<3)
+#define TEGRA_RTC_INTR_MASK_MSEC_ALARM (1<<2)
+#define TEGRA_RTC_INTR_MASK_SEC_ALARM1 (1<<1)
+#define TEGRA_RTC_INTR_MASK_SEC_ALARM0 (1<<0)
+
+/* bits in INTR_STATUS */
+#define TEGRA_RTC_INTR_STATUS_MSEC_CDN_ALARM (1<<4)
+#define TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM (1<<3)
+#define TEGRA_RTC_INTR_STATUS_MSEC_ALARM (1<<2)
+#define TEGRA_RTC_INTR_STATUS_SEC_ALARM1 (1<<1)
+#define TEGRA_RTC_INTR_STATUS_SEC_ALARM0 (1<<0)
+
+struct tegra_rtc_info {
+ struct platform_device *pdev;
+ struct rtc_device *rtc_dev;
+ void __iomem *rtc_base; /* NULL if not initialized. */
+ int tegra_rtc_irq; /* alarm and periodic irq */
+ spinlock_t tegra_rtc_lock;
+};
+
+/* RTC hardware is busy when it is updating its values over AHB once
+ * every eight 32kHz clocks (~250uS).
+ * outside of these updates the CPU is free to write.
+ * CPU is always free to read.
+ */
+static inline u32 tegra_rtc_check_busy(struct tegra_rtc_info *info)
+{
+ return readl(info->rtc_base + TEGRA_RTC_REG_BUSY) & 1;
+}
+
+/* Wait for hardware to be ready for writing.
+ * This function tries to maximize the amount of time before the next update.
+ * It does this by waiting for the RTC to become busy with its periodic update,
+ * then returning once the RTC first becomes not busy.
+ * This periodic update (where the seconds and milliseconds are copied to the
+ * AHB side) occurs every eight 32kHz clocks (~250uS).
+ * The behavior of this function allows us to make some assumptions without
+ * introducing a race, because 250uS is plenty of time to read/write a value.
+ */
+static int tegra_rtc_wait_while_busy(struct device *dev)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+
+ int retries = 500; /* ~490 us is the worst case, ~250 us is best. */
+
+ /* first wait for the RTC to become busy. this is when it
+ * posts its updated seconds+msec registers to AHB side. */
+ while (tegra_rtc_check_busy(info)) {
+ if (!retries--)
+ goto retry_failed;
+ udelay(1);
+ }
+
+ /* now we have about 250 us to manipulate registers */
+ return 0;
+
+retry_failed:
+ dev_err(dev, "write failed:retry count exceeded.\n");
+ return -ETIMEDOUT;
+}
+
+static int tegra_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long sec, msec;
+ unsigned long sl_irq_flags;
+
+ /* RTC hardware copies seconds to shadow seconds when a read
+ * of milliseconds occurs. use a lock to keep other threads out. */
+ spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
+
+ msec = readl(info->rtc_base + TEGRA_RTC_REG_MILLI_SECONDS);
+ sec = readl(info->rtc_base + TEGRA_RTC_REG_SHADOW_SECONDS);
+
+ spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
+
+ rtc_time_to_tm(sec, tm);
+
+ dev_vdbg(dev, "time read as %lu. %d/%d/%d %d:%02u:%02u\n",
+ sec,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_year + 1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec
+ );
+
+ return 0;
+}
+
+static int tegra_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long sec;
+ int ret;
+
+ /* convert tm to seconds. */
+ ret = rtc_valid_tm(tm);
+ if (ret)
+ return ret;
+
+ rtc_tm_to_time(tm, &sec);
+
+ dev_vdbg(dev, "time set to %lu. %d/%d/%d %d:%02u:%02u\n",
+ sec,
+ tm->tm_mon+1,
+ tm->tm_mday,
+ tm->tm_year+1900,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec
+ );
+
+ /* seconds only written if wait succeeded. */
+ ret = tegra_rtc_wait_while_busy(dev);
+ if (!ret)
+ writel(sec, info->rtc_base + TEGRA_RTC_REG_SECONDS);
+
+ dev_vdbg(dev, "time read back as %d\n",
+ readl(info->rtc_base + TEGRA_RTC_REG_SECONDS));
+
+ return ret;
+}
+
+static int tegra_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long sec;
+ unsigned tmp;
+
+ sec = readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
+
+ if (sec == 0) {
+ /* alarm is disabled. */
+ alarm->enabled = 0;
+ alarm->time.tm_mon = -1;
+ alarm->time.tm_mday = -1;
+ alarm->time.tm_year = -1;
+ alarm->time.tm_hour = -1;
+ alarm->time.tm_min = -1;
+ alarm->time.tm_sec = -1;
+ } else {
+ /* alarm is enabled. */
+ alarm->enabled = 1;
+ rtc_time_to_tm(sec, &alarm->time);
+ }
+
+ tmp = readl(info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ alarm->pending = (tmp & TEGRA_RTC_INTR_STATUS_SEC_ALARM0) != 0;
+
+ return 0;
+}
+
+static int tegra_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ unsigned status;
+ unsigned long sl_irq_flags;
+
+ tegra_rtc_wait_while_busy(dev);
+ spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
+
+ /* read the original value, and OR in the flag. */
+ status = readl(info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+ if (enabled)
+ status |= TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* set it */
+ else
+ status &= ~TEGRA_RTC_INTR_MASK_SEC_ALARM0; /* clear it */
+
+ writel(status, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+
+ spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
+
+ return 0;
+}
+
+static int tegra_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long sec;
+
+ if (alarm->enabled)
+ rtc_tm_to_time(&alarm->time, &sec);
+ else
+ sec = 0;
+
+ tegra_rtc_wait_while_busy(dev);
+ writel(sec, info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
+ dev_vdbg(dev, "alarm read back as %d\n",
+ readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0));
+
+ /* if successfully written and alarm is enabled ... */
+ if (sec) {
+ tegra_rtc_alarm_irq_enable(dev, 1);
+
+ dev_vdbg(dev, "alarm set as %lu. %d/%d/%d %d:%02u:%02u\n",
+ sec,
+ alarm->time.tm_mon+1,
+ alarm->time.tm_mday,
+ alarm->time.tm_year+1900,
+ alarm->time.tm_hour,
+ alarm->time.tm_min,
+ alarm->time.tm_sec);
+ } else {
+ /* disable alarm if 0 or write error. */
+ dev_vdbg(dev, "alarm disabled\n");
+ tegra_rtc_alarm_irq_enable(dev, 0);
+ }
+
+ return 0;
+}
+
+static int tegra_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ if (!dev || !dev->driver)
+ return 0;
+
+ seq_printf(seq, "name\t\t: %s\n", dev_name(dev));
+
+ return 0;
+}
+
+static irqreturn_t tegra_rtc_irq_handler(int irq, void *data)
+{
+ struct device *dev = data;
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+ unsigned long events = 0;
+ unsigned status;
+ unsigned long sl_irq_flags;
+
+ status = readl(info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ if (status) {
+ /* clear the interrupt masks and status on any irq. */
+ tegra_rtc_wait_while_busy(dev);
+ spin_lock_irqsave(&info->tegra_rtc_lock, sl_irq_flags);
+ writel(0, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+ writel(status, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ spin_unlock_irqrestore(&info->tegra_rtc_lock, sl_irq_flags);
+ }
+
+ /* check if Alarm */
+ if ((status & TEGRA_RTC_INTR_STATUS_SEC_ALARM0))
+ events |= RTC_IRQF | RTC_AF;
+
+ /* check if Periodic */
+ if ((status & TEGRA_RTC_INTR_STATUS_SEC_CDN_ALARM))
+ events |= RTC_IRQF | RTC_PF;
+
+ rtc_update_irq(info->rtc_dev, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static struct rtc_class_ops tegra_rtc_ops = {
+ .read_time = tegra_rtc_read_time,
+ .set_time = tegra_rtc_set_time,
+ .read_alarm = tegra_rtc_read_alarm,
+ .set_alarm = tegra_rtc_set_alarm,
+ .proc = tegra_rtc_proc,
+ .alarm_irq_enable = tegra_rtc_alarm_irq_enable,
+};
+
+static const struct of_device_id tegra_rtc_dt_match[] = {
+ { .compatible = "nvidia,tegra20-rtc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, tegra_rtc_dt_match);
+
+static int __init tegra_rtc_probe(struct platform_device *pdev)
+{
+ struct tegra_rtc_info *info;
+ struct resource *res;
+ int ret;
+
+ info = devm_kzalloc(&pdev->dev, sizeof(struct tegra_rtc_info),
+ GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->rtc_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->rtc_base))
+ return PTR_ERR(info->rtc_base);
+
+ info->tegra_rtc_irq = platform_get_irq(pdev, 0);
+ if (info->tegra_rtc_irq <= 0)
+ return -EBUSY;
+
+ /* set context info. */
+ info->pdev = pdev;
+ spin_lock_init(&info->tegra_rtc_lock);
+
+ platform_set_drvdata(pdev, info);
+
+ /* clear out the hardware. */
+ writel(0, info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0);
+ writel(0xffffffff, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ writel(0, info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev,
+ dev_name(&pdev->dev), &tegra_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(info->rtc_dev)) {
+ ret = PTR_ERR(info->rtc_dev);
+ dev_err(&pdev->dev, "Unable to register device (err=%d).\n",
+ ret);
+ return ret;
+ }
+
+ ret = devm_request_irq(&pdev->dev, info->tegra_rtc_irq,
+ tegra_rtc_irq_handler, IRQF_TRIGGER_HIGH,
+ dev_name(&pdev->dev), &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Unable to request interrupt for device (err=%d).\n",
+ ret);
+ return ret;
+ }
+
+ dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_rtc_suspend(struct device *dev)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+
+ tegra_rtc_wait_while_busy(dev);
+
+ /* only use ALARM0 as a wake source. */
+ writel(0xffffffff, info->rtc_base + TEGRA_RTC_REG_INTR_STATUS);
+ writel(TEGRA_RTC_INTR_STATUS_SEC_ALARM0,
+ info->rtc_base + TEGRA_RTC_REG_INTR_MASK);
+
+ dev_vdbg(dev, "alarm sec = %d\n",
+ readl(info->rtc_base + TEGRA_RTC_REG_SECONDS_ALARM0));
+
+ dev_vdbg(dev, "Suspend (device_may_wakeup=%d) irq:%d\n",
+ device_may_wakeup(dev), info->tegra_rtc_irq);
+
+ /* leave the alarms on as a wake source. */
+ if (device_may_wakeup(dev))
+ enable_irq_wake(info->tegra_rtc_irq);
+
+ return 0;
+}
+
+static int tegra_rtc_resume(struct device *dev)
+{
+ struct tegra_rtc_info *info = dev_get_drvdata(dev);
+
+ dev_vdbg(dev, "Resume (device_may_wakeup=%d)\n",
+ device_may_wakeup(dev));
+ /* alarms were left on as a wake source, turn them off. */
+ if (device_may_wakeup(dev))
+ disable_irq_wake(info->tegra_rtc_irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tegra_rtc_pm_ops, tegra_rtc_suspend, tegra_rtc_resume);
+
+static void tegra_rtc_shutdown(struct platform_device *pdev)
+{
+ dev_vdbg(&pdev->dev, "disabling interrupts.\n");
+ tegra_rtc_alarm_irq_enable(&pdev->dev, 0);
+}
+
+MODULE_ALIAS("platform:tegra_rtc");
+static struct platform_driver tegra_rtc_driver = {
+ .shutdown = tegra_rtc_shutdown,
+ .driver = {
+ .name = "tegra_rtc",
+ .of_match_table = tegra_rtc_dt_match,
+ .pm = &tegra_rtc_pm_ops,
+ },
+};
+
+module_platform_driver_probe(tegra_rtc_driver, tegra_rtc_probe);
+
+MODULE_AUTHOR("Jon Mayo <jmayo@nvidia.com>");
+MODULE_DESCRIPTION("driver for Tegra internal RTC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
new file mode 100644
index 000000000..3a2da4c89
--- /dev/null
+++ b/drivers/rtc/rtc-test.c
@@ -0,0 +1,201 @@
+/*
+ * An RTC test device/driver
+ * Copyright (C) 2005 Tower Technologies
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+static int test_mmss64;
+module_param(test_mmss64, int, 0644);
+MODULE_PARM_DESC(test_mmss64, "Test struct rtc_class_ops.set_mmss64().");
+
+static struct platform_device *test0 = NULL, *test1 = NULL;
+
+static int test_rtc_read_alarm(struct device *dev,
+ struct rtc_wkalrm *alrm)
+{
+ return 0;
+}
+
+static int test_rtc_set_alarm(struct device *dev,
+ struct rtc_wkalrm *alrm)
+{
+ return 0;
+}
+
+static int test_rtc_read_time(struct device *dev,
+ struct rtc_time *tm)
+{
+ rtc_time64_to_tm(ktime_get_real_seconds(), tm);
+ return 0;
+}
+
+static int test_rtc_set_mmss64(struct device *dev, time64_t secs)
+{
+ dev_info(dev, "%s, secs = %lld\n", __func__, (long long)secs);
+ return 0;
+}
+
+static int test_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ dev_info(dev, "%s, secs = %lu\n", __func__, secs);
+ return 0;
+}
+
+static int test_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct platform_device *plat_dev = to_platform_device(dev);
+
+ seq_printf(seq, "test\t\t: yes\n");
+ seq_printf(seq, "id\t\t: %d\n", plat_dev->id);
+
+ return 0;
+}
+
+static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+ return 0;
+}
+
+static struct rtc_class_ops test_rtc_ops = {
+ .proc = test_rtc_proc,
+ .read_time = test_rtc_read_time,
+ .read_alarm = test_rtc_read_alarm,
+ .set_alarm = test_rtc_set_alarm,
+ .set_mmss = test_rtc_set_mmss,
+ .alarm_irq_enable = test_rtc_alarm_irq_enable,
+};
+
+static ssize_t test_irq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", 42);
+}
+static ssize_t test_irq_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ struct platform_device *plat_dev = to_platform_device(dev);
+ struct rtc_device *rtc = platform_get_drvdata(plat_dev);
+
+ retval = count;
+ if (strncmp(buf, "tick", 4) == 0 && rtc->pie_enabled)
+ rtc_update_irq(rtc, 1, RTC_PF | RTC_IRQF);
+ else if (strncmp(buf, "alarm", 5) == 0) {
+ struct rtc_wkalrm alrm;
+ int err = rtc_read_alarm(rtc, &alrm);
+
+ if (!err && alrm.enabled)
+ rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
+
+ } else if (strncmp(buf, "update", 6) == 0 && rtc->uie_rtctimer.enabled)
+ rtc_update_irq(rtc, 1, RTC_UF | RTC_IRQF);
+ else
+ retval = -EINVAL;
+
+ return retval;
+}
+static DEVICE_ATTR(irq, S_IRUGO | S_IWUSR, test_irq_show, test_irq_store);
+
+static int test_probe(struct platform_device *plat_dev)
+{
+ int err;
+ struct rtc_device *rtc;
+
+ if (test_mmss64) {
+ test_rtc_ops.set_mmss64 = test_rtc_set_mmss64;
+ test_rtc_ops.set_mmss = NULL;
+ }
+
+ rtc = devm_rtc_device_register(&plat_dev->dev, "test",
+ &test_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ return PTR_ERR(rtc);
+ }
+
+ err = device_create_file(&plat_dev->dev, &dev_attr_irq);
+ if (err)
+ dev_err(&plat_dev->dev, "Unable to create sysfs entry: %s\n",
+ dev_attr_irq.attr.name);
+
+ platform_set_drvdata(plat_dev, rtc);
+
+ return 0;
+}
+
+static int test_remove(struct platform_device *plat_dev)
+{
+ device_remove_file(&plat_dev->dev, &dev_attr_irq);
+
+ return 0;
+}
+
+static struct platform_driver test_driver = {
+ .probe = test_probe,
+ .remove = test_remove,
+ .driver = {
+ .name = "rtc-test",
+ },
+};
+
+static int __init test_init(void)
+{
+ int err;
+
+ if ((err = platform_driver_register(&test_driver)))
+ return err;
+
+ if ((test0 = platform_device_alloc("rtc-test", 0)) == NULL) {
+ err = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ if ((test1 = platform_device_alloc("rtc-test", 1)) == NULL) {
+ err = -ENOMEM;
+ goto exit_put_test0;
+ }
+
+ if ((err = platform_device_add(test0)))
+ goto exit_put_test1;
+
+ if ((err = platform_device_add(test1)))
+ goto exit_del_test0;
+
+ return 0;
+
+exit_del_test0:
+ platform_device_del(test0);
+
+exit_put_test1:
+ platform_device_put(test1);
+
+exit_put_test0:
+ platform_device_put(test0);
+
+exit_driver_unregister:
+ platform_driver_unregister(&test_driver);
+ return err;
+}
+
+static void __exit test_exit(void)
+{
+ platform_device_unregister(test0);
+ platform_device_unregister(test1);
+ platform_driver_unregister(&test_driver);
+}
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("RTC test driver/device");
+MODULE_LICENSE("GPL");
+
+module_init(test_init);
+module_exit(test_exit);
diff --git a/drivers/rtc/rtc-tile.c b/drivers/rtc/rtc-tile.c
new file mode 100644
index 000000000..0b60867d8
--- /dev/null
+++ b/drivers/rtc/rtc-tile.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2011 Tilera Corporation. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, version 2.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for
+ * more details.
+ *
+ * Tilera-specific RTC driver.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+/* Platform device pointer. */
+static struct platform_device *tile_rtc_platform_device;
+
+/*
+ * RTC read routine. Gets time info from RTC chip via hypervisor syscall.
+ */
+static int read_rtc_time(struct device *dev, struct rtc_time *tm)
+{
+ HV_RTCTime hvtm = hv_get_rtc();
+
+ tm->tm_sec = hvtm.tm_sec;
+ tm->tm_min = hvtm.tm_min;
+ tm->tm_hour = hvtm.tm_hour;
+ tm->tm_mday = hvtm.tm_mday;
+ tm->tm_mon = hvtm.tm_mon;
+ tm->tm_year = hvtm.tm_year;
+ tm->tm_wday = 0;
+ tm->tm_yday = 0;
+ tm->tm_isdst = 0;
+
+ if (rtc_valid_tm(tm) < 0)
+ dev_warn(dev, "Read invalid date/time from RTC\n");
+
+ return 0;
+}
+
+/*
+ * RTC write routine. Sends time info to hypervisor via syscall, to be
+ * written to RTC chip.
+ */
+static int set_rtc_time(struct device *dev, struct rtc_time *tm)
+{
+ HV_RTCTime hvtm;
+
+ hvtm.tm_sec = tm->tm_sec;
+ hvtm.tm_min = tm->tm_min;
+ hvtm.tm_hour = tm->tm_hour;
+ hvtm.tm_mday = tm->tm_mday;
+ hvtm.tm_mon = tm->tm_mon;
+ hvtm.tm_year = tm->tm_year;
+
+ hv_set_rtc(hvtm);
+
+ return 0;
+}
+
+/*
+ * RTC read/write ops.
+ */
+static const struct rtc_class_ops tile_rtc_ops = {
+ .read_time = read_rtc_time,
+ .set_time = set_rtc_time,
+};
+
+/*
+ * Device probe routine.
+ */
+static int tile_rtc_probe(struct platform_device *dev)
+{
+ struct rtc_device *rtc;
+
+ rtc = devm_rtc_device_register(&dev->dev, "tile",
+ &tile_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(dev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver tile_rtc_platform_driver = {
+ .driver = {
+ .name = "rtc-tile",
+ },
+ .probe = tile_rtc_probe,
+};
+
+/*
+ * Driver init routine.
+ */
+static int __init tile_rtc_driver_init(void)
+{
+ int err;
+
+ err = platform_driver_register(&tile_rtc_platform_driver);
+ if (err)
+ return err;
+
+ tile_rtc_platform_device = platform_device_alloc("rtc-tile", 0);
+ if (tile_rtc_platform_device == NULL) {
+ err = -ENOMEM;
+ goto exit_driver_unregister;
+ }
+
+ err = platform_device_add(tile_rtc_platform_device);
+ if (err)
+ goto exit_device_put;
+
+ return 0;
+
+exit_device_put:
+ platform_device_put(tile_rtc_platform_device);
+
+exit_driver_unregister:
+ platform_driver_unregister(&tile_rtc_platform_driver);
+ return err;
+}
+
+/*
+ * Driver cleanup routine.
+ */
+static void __exit tile_rtc_driver_exit(void)
+{
+ platform_device_unregister(tile_rtc_platform_device);
+ platform_driver_unregister(&tile_rtc_platform_driver);
+}
+
+module_init(tile_rtc_driver_init);
+module_exit(tile_rtc_driver_exit);
+
+MODULE_DESCRIPTION("Tilera-specific Real Time Clock Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:rtc-tile");
diff --git a/drivers/rtc/rtc-tps6586x.c b/drivers/rtc/rtc-tps6586x.c
new file mode 100644
index 000000000..3b6ce80a7
--- /dev/null
+++ b/drivers/rtc/rtc-tps6586x.c
@@ -0,0 +1,350 @@
+/*
+ * rtc-tps6586x.c: RTC driver for TI PMIC TPS6586X
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mfd/tps6586x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define RTC_CTRL 0xc0
+#define POR_RESET_N BIT(7)
+#define OSC_SRC_SEL BIT(6)
+#define RTC_ENABLE BIT(5) /* enables alarm */
+#define RTC_BUF_ENABLE BIT(4) /* 32 KHz buffer enable */
+#define PRE_BYPASS BIT(3) /* 0=1KHz or 1=32KHz updates */
+#define CL_SEL_MASK (BIT(2)|BIT(1))
+#define CL_SEL_POS 1
+#define RTC_ALARM1_HI 0xc1
+#define RTC_COUNT4 0xc6
+
+/* start a PMU RTC access by reading the register prior to the RTC_COUNT4 */
+#define RTC_COUNT4_DUMMYREAD 0xc5
+
+/*only 14-bits width in second*/
+#define ALM1_VALID_RANGE_IN_SEC 0x3FFF
+
+#define TPS6586X_RTC_CL_SEL_1_5PF 0x0
+#define TPS6586X_RTC_CL_SEL_6_5PF 0x1
+#define TPS6586X_RTC_CL_SEL_7_5PF 0x2
+#define TPS6586X_RTC_CL_SEL_12_5PF 0x3
+
+struct tps6586x_rtc {
+ struct device *dev;
+ struct rtc_device *rtc;
+ int irq;
+ bool irq_en;
+ unsigned long long epoch_start;
+};
+
+static inline struct device *to_tps6586x_dev(struct device *dev)
+{
+ return dev->parent;
+}
+
+static int tps6586x_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+ struct device *tps_dev = to_tps6586x_dev(dev);
+ unsigned long long ticks = 0;
+ unsigned long seconds;
+ u8 buff[6];
+ int ret;
+ int i;
+
+ ret = tps6586x_reads(tps_dev, RTC_COUNT4_DUMMYREAD, sizeof(buff), buff);
+ if (ret < 0) {
+ dev_err(dev, "read counter failed with err %d\n", ret);
+ return ret;
+ }
+
+ for (i = 1; i < sizeof(buff); i++) {
+ ticks <<= 8;
+ ticks |= buff[i];
+ }
+
+ seconds = ticks >> 10;
+ seconds += rtc->epoch_start;
+ rtc_time_to_tm(seconds, tm);
+ return rtc_valid_tm(tm);
+}
+
+static int tps6586x_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+ struct device *tps_dev = to_tps6586x_dev(dev);
+ unsigned long long ticks;
+ unsigned long seconds;
+ u8 buff[5];
+ int ret;
+
+ rtc_tm_to_time(tm, &seconds);
+ if (seconds < rtc->epoch_start) {
+ dev_err(dev, "requested time unsupported\n");
+ return -EINVAL;
+ }
+ seconds -= rtc->epoch_start;
+
+ ticks = (unsigned long long)seconds << 10;
+ buff[0] = (ticks >> 32) & 0xff;
+ buff[1] = (ticks >> 24) & 0xff;
+ buff[2] = (ticks >> 16) & 0xff;
+ buff[3] = (ticks >> 8) & 0xff;
+ buff[4] = ticks & 0xff;
+
+ /* Disable RTC before changing time */
+ ret = tps6586x_clr_bits(tps_dev, RTC_CTRL, RTC_ENABLE);
+ if (ret < 0) {
+ dev_err(dev, "failed to clear RTC_ENABLE\n");
+ return ret;
+ }
+
+ ret = tps6586x_writes(tps_dev, RTC_COUNT4, sizeof(buff), buff);
+ if (ret < 0) {
+ dev_err(dev, "failed to program new time\n");
+ return ret;
+ }
+
+ /* Enable RTC */
+ ret = tps6586x_set_bits(tps_dev, RTC_CTRL, RTC_ENABLE);
+ if (ret < 0) {
+ dev_err(dev, "failed to set RTC_ENABLE\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int tps6586x_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+
+ if (enabled && !rtc->irq_en) {
+ enable_irq(rtc->irq);
+ rtc->irq_en = true;
+ } else if (!enabled && rtc->irq_en) {
+ disable_irq(rtc->irq);
+ rtc->irq_en = false;
+ }
+ return 0;
+}
+
+static int tps6586x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+ struct device *tps_dev = to_tps6586x_dev(dev);
+ unsigned long seconds;
+ unsigned long ticks;
+ unsigned long rtc_current_time;
+ unsigned long long rticks = 0;
+ u8 buff[3];
+ u8 rbuff[6];
+ int ret;
+ int i;
+
+ rtc_tm_to_time(&alrm->time, &seconds);
+
+ if (alrm->enabled && (seconds < rtc->epoch_start)) {
+ dev_err(dev, "can't set alarm to requested time\n");
+ return -EINVAL;
+ }
+
+ ret = tps6586x_rtc_alarm_irq_enable(dev, alrm->enabled);
+ if (ret < 0) {
+ dev_err(dev, "can't set alarm irq, err %d\n", ret);
+ return ret;
+ }
+
+ seconds -= rtc->epoch_start;
+ ret = tps6586x_reads(tps_dev, RTC_COUNT4_DUMMYREAD,
+ sizeof(rbuff), rbuff);
+ if (ret < 0) {
+ dev_err(dev, "read counter failed with err %d\n", ret);
+ return ret;
+ }
+
+ for (i = 1; i < sizeof(rbuff); i++) {
+ rticks <<= 8;
+ rticks |= rbuff[i];
+ }
+
+ rtc_current_time = rticks >> 10;
+ if ((seconds - rtc_current_time) > ALM1_VALID_RANGE_IN_SEC)
+ seconds = rtc_current_time - 1;
+
+ ticks = (unsigned long long)seconds << 10;
+ buff[0] = (ticks >> 16) & 0xff;
+ buff[1] = (ticks >> 8) & 0xff;
+ buff[2] = ticks & 0xff;
+
+ ret = tps6586x_writes(tps_dev, RTC_ALARM1_HI, sizeof(buff), buff);
+ if (ret)
+ dev_err(dev, "programming alarm failed with err %d\n", ret);
+
+ return ret;
+}
+
+static int tps6586x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+ struct device *tps_dev = to_tps6586x_dev(dev);
+ unsigned long ticks;
+ unsigned long seconds;
+ u8 buff[3];
+ int ret;
+
+ ret = tps6586x_reads(tps_dev, RTC_ALARM1_HI, sizeof(buff), buff);
+ if (ret) {
+ dev_err(dev, "read RTC_ALARM1_HI failed with err %d\n", ret);
+ return ret;
+ }
+
+ ticks = (buff[0] << 16) | (buff[1] << 8) | buff[2];
+ seconds = ticks >> 10;
+ seconds += rtc->epoch_start;
+
+ rtc_time_to_tm(seconds, &alrm->time);
+ return 0;
+}
+
+static const struct rtc_class_ops tps6586x_rtc_ops = {
+ .read_time = tps6586x_rtc_read_time,
+ .set_time = tps6586x_rtc_set_time,
+ .set_alarm = tps6586x_rtc_set_alarm,
+ .read_alarm = tps6586x_rtc_read_alarm,
+ .alarm_irq_enable = tps6586x_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t tps6586x_rtc_irq(int irq, void *data)
+{
+ struct tps6586x_rtc *rtc = data;
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static int tps6586x_rtc_probe(struct platform_device *pdev)
+{
+ struct device *tps_dev = to_tps6586x_dev(&pdev->dev);
+ struct tps6586x_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->dev = &pdev->dev;
+ rtc->irq = platform_get_irq(pdev, 0);
+
+ /* Set epoch start as 00:00:00:01:01:2009 */
+ rtc->epoch_start = mktime(2009, 1, 1, 0, 0, 0);
+
+ /* 1 kHz tick mode, enable tick counting */
+ ret = tps6586x_update(tps_dev, RTC_CTRL,
+ RTC_ENABLE | OSC_SRC_SEL |
+ ((TPS6586X_RTC_CL_SEL_1_5PF << CL_SEL_POS) & CL_SEL_MASK),
+ RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to start counter\n");
+ return ret;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ platform_set_drvdata(pdev, rtc);
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, dev_name(&pdev->dev),
+ &tps6586x_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "RTC device register: ret %d\n", ret);
+ goto fail_rtc_register;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+ tps6586x_rtc_irq,
+ IRQF_ONESHOT | IRQF_EARLY_RESUME,
+ dev_name(&pdev->dev), rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "request IRQ(%d) failed with ret %d\n",
+ rtc->irq, ret);
+ goto fail_rtc_register;
+ }
+ disable_irq(rtc->irq);
+ return 0;
+
+fail_rtc_register:
+ tps6586x_update(tps_dev, RTC_CTRL, 0,
+ RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK);
+ return ret;
+};
+
+static int tps6586x_rtc_remove(struct platform_device *pdev)
+{
+ struct device *tps_dev = to_tps6586x_dev(&pdev->dev);
+
+ tps6586x_update(tps_dev, RTC_CTRL, 0,
+ RTC_ENABLE | OSC_SRC_SEL | PRE_BYPASS | CL_SEL_MASK);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tps6586x_rtc_suspend(struct device *dev)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->irq);
+ return 0;
+}
+
+static int tps6586x_rtc_resume(struct device *dev)
+{
+ struct tps6586x_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tps6586x_pm_ops, tps6586x_rtc_suspend,
+ tps6586x_rtc_resume);
+
+static struct platform_driver tps6586x_rtc_driver = {
+ .driver = {
+ .name = "tps6586x-rtc",
+ .pm = &tps6586x_pm_ops,
+ },
+ .probe = tps6586x_rtc_probe,
+ .remove = tps6586x_rtc_remove,
+};
+module_platform_driver(tps6586x_rtc_driver);
+
+MODULE_ALIAS("platform:rtc-tps6586x");
+MODULE_DESCRIPTION("TI TPS6586x RTC driver");
+MODULE_AUTHOR("Laxman dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c
new file mode 100644
index 000000000..f42aa2b2d
--- /dev/null
+++ b/drivers/rtc/rtc-tps65910.c
@@ -0,0 +1,337 @@
+/*
+ * rtc-tps65910.c -- TPS65910 Real Time Clock interface
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+ * Author: Venu Byravarasu <vbyravarasu@nvidia.com>
+ *
+ * Based on original TI driver rtc-twl.c
+ * Copyright (C) 2007 MontaVista Software, Inc
+ * Author: Alexandre Rusev <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/tps65910.h>
+
+struct tps65910_rtc {
+ struct rtc_device *rtc;
+ int irq;
+};
+
+/* Total number of RTC registers needed to set time*/
+#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1)
+
+static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+ struct tps65910 *tps = dev_get_drvdata(dev->parent);
+ u8 val = 0;
+
+ if (enabled)
+ val = TPS65910_RTC_INTERRUPTS_IT_ALARM;
+
+ return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, val);
+}
+
+/*
+ * Gets current tps65910 RTC time and date parameters.
+ *
+ * The RTC's time/alarm representation is not what gmtime(3) requires
+ * Linux to use:
+ *
+ * - Months are 1..12 vs Linux 0-11
+ * - Years are 0..99 vs Linux 1900..N (we assume 21st century)
+ */
+static int tps65910_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[NUM_TIME_REGS];
+ struct tps65910 *tps = dev_get_drvdata(dev->parent);
+ int ret;
+
+ /* Copy RTC counting registers to static registers or latches */
+ ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
+ TPS65910_RTC_CTRL_GET_TIME, TPS65910_RTC_CTRL_GET_TIME);
+ if (ret < 0) {
+ dev_err(dev, "RTC CTRL reg update failed with err:%d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, rtc_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "reading from RTC failed with err:%d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_mday = bcd2bin(rtc_data[3]);
+ tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ return ret;
+}
+
+static int tps65910_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[NUM_TIME_REGS];
+ struct tps65910 *tps = dev_get_drvdata(dev->parent);
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_mday);
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[5] = bin2bcd(tm->tm_year - 100);
+
+ /* Stop RTC while updating the RTC time registers */
+ ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
+ TPS65910_RTC_CTRL_STOP_RTC, 0);
+ if (ret < 0) {
+ dev_err(dev, "RTC stop failed with err:%d\n", ret);
+ return ret;
+ }
+
+ /* update all the time registers in one shot */
+ ret = regmap_bulk_write(tps->regmap, TPS65910_SECONDS, rtc_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_set_time error %d\n", ret);
+ return ret;
+ }
+
+ /* Start back RTC */
+ ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
+ TPS65910_RTC_CTRL_STOP_RTC, 1);
+ if (ret < 0)
+ dev_err(dev, "RTC start failed with err:%d\n", ret);
+
+ return ret;
+}
+
+/*
+ * Gets current tps65910 RTC alarm time.
+ */
+static int tps65910_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[NUM_TIME_REGS];
+ u32 int_val;
+ struct tps65910 *tps = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, alarm_data,
+ NUM_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_alarm error %d\n", ret);
+ return ret;
+ }
+
+ alm->time.tm_sec = bcd2bin(alarm_data[0]);
+ alm->time.tm_min = bcd2bin(alarm_data[1]);
+ alm->time.tm_hour = bcd2bin(alarm_data[2]);
+ alm->time.tm_mday = bcd2bin(alarm_data[3]);
+ alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
+ alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
+
+ ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, &int_val);
+ if (ret < 0)
+ return ret;
+
+ if (int_val & TPS65910_RTC_INTERRUPTS_IT_ALARM)
+ alm->enabled = 1;
+
+ return ret;
+}
+
+static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[NUM_TIME_REGS];
+ struct tps65910 *tps = dev_get_drvdata(dev->parent);
+ int ret;
+
+ ret = tps65910_rtc_alarm_irq_enable(dev, 0);
+ if (ret)
+ return ret;
+
+ alarm_data[0] = bin2bcd(alm->time.tm_sec);
+ alarm_data[1] = bin2bcd(alm->time.tm_min);
+ alarm_data[2] = bin2bcd(alm->time.tm_hour);
+ alarm_data[3] = bin2bcd(alm->time.tm_mday);
+ alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
+ alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
+
+ /* update all the alarm registers in one shot */
+ ret = regmap_bulk_write(tps->regmap, TPS65910_ALARM_SECONDS,
+ alarm_data, NUM_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "rtc_set_alarm error %d\n", ret);
+ return ret;
+ }
+
+ if (alm->enabled)
+ ret = tps65910_rtc_alarm_irq_enable(dev, 1);
+
+ return ret;
+}
+
+static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc)
+{
+ struct device *dev = rtc;
+ unsigned long events = 0;
+ struct tps65910 *tps = dev_get_drvdata(dev->parent);
+ struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev);
+ int ret;
+ u32 rtc_reg;
+
+ ret = regmap_read(tps->regmap, TPS65910_RTC_STATUS, &rtc_reg);
+ if (ret)
+ return IRQ_NONE;
+
+ if (rtc_reg & TPS65910_RTC_STATUS_ALARM)
+ events = RTC_IRQF | RTC_AF;
+
+ ret = regmap_write(tps->regmap, TPS65910_RTC_STATUS, rtc_reg);
+ if (ret)
+ return IRQ_NONE;
+
+ /* Notify RTC core on event */
+ rtc_update_irq(tps_rtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops tps65910_rtc_ops = {
+ .read_time = tps65910_rtc_read_time,
+ .set_time = tps65910_rtc_set_time,
+ .read_alarm = tps65910_rtc_read_alarm,
+ .set_alarm = tps65910_rtc_set_alarm,
+ .alarm_irq_enable = tps65910_rtc_alarm_irq_enable,
+};
+
+static int tps65910_rtc_probe(struct platform_device *pdev)
+{
+ struct tps65910 *tps65910 = NULL;
+ struct tps65910_rtc *tps_rtc = NULL;
+ int ret;
+ int irq;
+ u32 rtc_reg;
+
+ tps65910 = dev_get_drvdata(pdev->dev.parent);
+
+ tps_rtc = devm_kzalloc(&pdev->dev, sizeof(struct tps65910_rtc),
+ GFP_KERNEL);
+ if (!tps_rtc)
+ return -ENOMEM;
+
+ /* Clear pending interrupts */
+ ret = regmap_read(tps65910->regmap, TPS65910_RTC_STATUS, &rtc_reg);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(tps65910->regmap, TPS65910_RTC_STATUS, rtc_reg);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(&pdev->dev, "Enabling rtc-tps65910.\n");
+
+ /* Enable RTC digital power domain */
+ ret = regmap_update_bits(tps65910->regmap, TPS65910_DEVCTRL,
+ DEVCTRL_RTC_PWDN_MASK, 0 << DEVCTRL_RTC_PWDN_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ rtc_reg = TPS65910_RTC_CTRL_STOP_RTC;
+ ret = regmap_write(tps65910->regmap, TPS65910_RTC_CTRL, rtc_reg);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, tps_rtc);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
+ irq);
+ return -ENXIO;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ tps65910_rtc_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME,
+ dev_name(&pdev->dev), &pdev->dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ is not free.\n");
+ return ret;
+ }
+ tps_rtc->irq = irq;
+ device_set_wakeup_capable(&pdev->dev, 1);
+
+ tps_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &tps65910_rtc_ops, THIS_MODULE);
+ if (IS_ERR(tps_rtc->rtc)) {
+ ret = PTR_ERR(tps_rtc->rtc);
+ dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Disable tps65910 RTC interrupts.
+ * Sets status flag to free.
+ */
+static int tps65910_rtc_remove(struct platform_device *pdev)
+{
+ tps65910_rtc_alarm_irq_enable(&pdev->dev, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tps65910_rtc_suspend(struct device *dev)
+{
+ struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(tps_rtc->irq);
+ return 0;
+}
+
+static int tps65910_rtc_resume(struct device *dev)
+{
+ struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(tps_rtc->irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tps65910_rtc_pm_ops, tps65910_rtc_suspend,
+ tps65910_rtc_resume);
+
+static struct platform_driver tps65910_rtc_driver = {
+ .probe = tps65910_rtc_probe,
+ .remove = tps65910_rtc_remove,
+ .driver = {
+ .name = "tps65910-rtc",
+ .pm = &tps65910_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(tps65910_rtc_driver);
+MODULE_ALIAS("platform:rtc-tps65910");
+MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-tps80031.c b/drivers/rtc/rtc-tps80031.c
new file mode 100644
index 000000000..27e254cde
--- /dev/null
+++ b/drivers/rtc/rtc-tps80031.c
@@ -0,0 +1,337 @@
+/*
+ * rtc-tps80031.c -- TI TPS80031/TPS80032 RTC driver
+ *
+ * RTC driver for TI TPS80031/TPS80032 Fully Integrated
+ * Power Management with Power Path and Battery Charger
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/bcd.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/tps80031.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+
+#define ENABLE_ALARM_INT 0x08
+#define ALARM_INT_STATUS 0x40
+
+/**
+ * Setting bit to 1 in STOP_RTC will run the RTC and
+ * setting this bit to 0 will freeze RTC.
+ */
+#define STOP_RTC 0x1
+
+/* Power on reset Values of RTC registers */
+#define TPS80031_RTC_POR_YEAR 0
+#define TPS80031_RTC_POR_MONTH 1
+#define TPS80031_RTC_POR_DAY 1
+
+/* Numbers of registers for time and alarms */
+#define TPS80031_RTC_TIME_NUM_REGS 7
+#define TPS80031_RTC_ALARM_NUM_REGS 6
+
+/**
+ * PMU RTC have only 2 nibbles to store year information, so using an
+ * offset of 100 to set the base year as 2000 for our driver.
+ */
+#define RTC_YEAR_OFFSET 100
+
+struct tps80031_rtc {
+ struct rtc_device *rtc;
+ int irq;
+};
+
+static int tps80031_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ u8 buff[TPS80031_RTC_TIME_NUM_REGS];
+ int ret;
+
+ ret = tps80031_reads(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_SECONDS_REG, TPS80031_RTC_TIME_NUM_REGS, buff);
+ if (ret < 0) {
+ dev_err(dev, "reading RTC_SECONDS_REG failed, err = %d\n", ret);
+ return ret;
+ }
+
+ tm->tm_sec = bcd2bin(buff[0]);
+ tm->tm_min = bcd2bin(buff[1]);
+ tm->tm_hour = bcd2bin(buff[2]);
+ tm->tm_mday = bcd2bin(buff[3]);
+ tm->tm_mon = bcd2bin(buff[4]) - 1;
+ tm->tm_year = bcd2bin(buff[5]) + RTC_YEAR_OFFSET;
+ tm->tm_wday = bcd2bin(buff[6]);
+ return 0;
+}
+
+static int tps80031_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ u8 buff[7];
+ int ret;
+
+ buff[0] = bin2bcd(tm->tm_sec);
+ buff[1] = bin2bcd(tm->tm_min);
+ buff[2] = bin2bcd(tm->tm_hour);
+ buff[3] = bin2bcd(tm->tm_mday);
+ buff[4] = bin2bcd(tm->tm_mon + 1);
+ buff[5] = bin2bcd(tm->tm_year % RTC_YEAR_OFFSET);
+ buff[6] = bin2bcd(tm->tm_wday);
+
+ /* Stop RTC while updating the RTC time registers */
+ ret = tps80031_clr_bits(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_CTRL_REG, STOP_RTC);
+ if (ret < 0) {
+ dev_err(dev->parent, "Stop RTC failed, err = %d\n", ret);
+ return ret;
+ }
+
+ ret = tps80031_writes(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_SECONDS_REG,
+ TPS80031_RTC_TIME_NUM_REGS, buff);
+ if (ret < 0) {
+ dev_err(dev, "writing RTC_SECONDS_REG failed, err %d\n", ret);
+ return ret;
+ }
+
+ ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_CTRL_REG, STOP_RTC);
+ if (ret < 0)
+ dev_err(dev->parent, "Start RTC failed, err = %d\n", ret);
+ return ret;
+}
+
+static int tps80031_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enable)
+{
+ int ret;
+
+ if (enable)
+ ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_INTERRUPTS_REG, ENABLE_ALARM_INT);
+ else
+ ret = tps80031_clr_bits(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_INTERRUPTS_REG, ENABLE_ALARM_INT);
+ if (ret < 0) {
+ dev_err(dev, "Update on RTC_INT failed, err = %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int tps80031_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ u8 buff[TPS80031_RTC_ALARM_NUM_REGS];
+ int ret;
+
+ buff[0] = bin2bcd(alrm->time.tm_sec);
+ buff[1] = bin2bcd(alrm->time.tm_min);
+ buff[2] = bin2bcd(alrm->time.tm_hour);
+ buff[3] = bin2bcd(alrm->time.tm_mday);
+ buff[4] = bin2bcd(alrm->time.tm_mon + 1);
+ buff[5] = bin2bcd(alrm->time.tm_year % RTC_YEAR_OFFSET);
+ ret = tps80031_writes(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_ALARM_SECONDS_REG,
+ TPS80031_RTC_ALARM_NUM_REGS, buff);
+ if (ret < 0) {
+ dev_err(dev, "Writing RTC_ALARM failed, err %d\n", ret);
+ return ret;
+ }
+ return tps80031_rtc_alarm_irq_enable(dev, alrm->enabled);
+}
+
+static int tps80031_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ u8 buff[6];
+ int ret;
+
+ ret = tps80031_reads(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_ALARM_SECONDS_REG,
+ TPS80031_RTC_ALARM_NUM_REGS, buff);
+ if (ret < 0) {
+ dev_err(dev->parent,
+ "reading RTC_ALARM failed, err = %d\n", ret);
+ return ret;
+ }
+
+ alrm->time.tm_sec = bcd2bin(buff[0]);
+ alrm->time.tm_min = bcd2bin(buff[1]);
+ alrm->time.tm_hour = bcd2bin(buff[2]);
+ alrm->time.tm_mday = bcd2bin(buff[3]);
+ alrm->time.tm_mon = bcd2bin(buff[4]) - 1;
+ alrm->time.tm_year = bcd2bin(buff[5]) + RTC_YEAR_OFFSET;
+ return 0;
+}
+
+static int clear_alarm_int_status(struct device *dev, struct tps80031_rtc *rtc)
+{
+ int ret;
+ u8 buf;
+
+ /**
+ * As per datasheet, A dummy read of this RTC_STATUS_REG register
+ * is necessary before each I2C read in order to update the status
+ * register value.
+ */
+ ret = tps80031_read(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_STATUS_REG, &buf);
+ if (ret < 0) {
+ dev_err(dev, "reading RTC_STATUS failed. err = %d\n", ret);
+ return ret;
+ }
+
+ /* clear Alarm status bits.*/
+ ret = tps80031_set_bits(dev->parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_STATUS_REG, ALARM_INT_STATUS);
+ if (ret < 0) {
+ dev_err(dev, "clear Alarm INT failed, err = %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static irqreturn_t tps80031_rtc_irq(int irq, void *data)
+{
+ struct device *dev = data;
+ struct tps80031_rtc *rtc = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clear_alarm_int_status(dev, rtc);
+ if (ret < 0)
+ return ret;
+
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops tps80031_rtc_ops = {
+ .read_time = tps80031_rtc_read_time,
+ .set_time = tps80031_rtc_set_time,
+ .set_alarm = tps80031_rtc_set_alarm,
+ .read_alarm = tps80031_rtc_read_alarm,
+ .alarm_irq_enable = tps80031_rtc_alarm_irq_enable,
+};
+
+static int tps80031_rtc_probe(struct platform_device *pdev)
+{
+ struct tps80031_rtc *rtc;
+ struct rtc_time tm;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->irq = platform_get_irq(pdev, 0);
+ platform_set_drvdata(pdev, rtc);
+
+ /* Start RTC */
+ ret = tps80031_set_bits(pdev->dev.parent, TPS80031_SLAVE_ID1,
+ TPS80031_RTC_CTRL_REG, STOP_RTC);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to start RTC. err = %d\n", ret);
+ return ret;
+ }
+
+ /* If RTC have POR values, set time 01:01:2000 */
+ tps80031_rtc_read_time(&pdev->dev, &tm);
+ if ((tm.tm_year == RTC_YEAR_OFFSET + TPS80031_RTC_POR_YEAR) &&
+ (tm.tm_mon == (TPS80031_RTC_POR_MONTH - 1)) &&
+ (tm.tm_mday == TPS80031_RTC_POR_DAY)) {
+ tm.tm_year = 2000;
+ tm.tm_mday = 1;
+ tm.tm_mon = 1;
+ ret = tps80031_rtc_set_time(&pdev->dev, &tm);
+ if (ret < 0) {
+ dev_err(&pdev->dev,
+ "RTC set time failed, err = %d\n", ret);
+ return ret;
+ }
+ }
+
+ /* Clear alarm intretupt status if it is there */
+ ret = clear_alarm_int_status(&pdev->dev, rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Clear alarm int failed, err = %d\n", ret);
+ return ret;
+ }
+
+ rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &tps80031_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ dev_err(&pdev->dev, "RTC registration failed, err %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
+ tps80031_rtc_irq,
+ IRQF_ONESHOT | IRQF_EARLY_RESUME,
+ dev_name(&pdev->dev), rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "request IRQ:%d failed, err = %d\n",
+ rtc->irq, ret);
+ return ret;
+ }
+ device_set_wakeup_capable(&pdev->dev, 1);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tps80031_rtc_suspend(struct device *dev)
+{
+ struct tps80031_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(rtc->irq);
+ return 0;
+}
+
+static int tps80031_rtc_resume(struct device *dev)
+{
+ struct tps80031_rtc *rtc = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(rtc->irq);
+ return 0;
+};
+#endif
+
+static SIMPLE_DEV_PM_OPS(tps80031_pm_ops, tps80031_rtc_suspend,
+ tps80031_rtc_resume);
+
+static struct platform_driver tps80031_rtc_driver = {
+ .driver = {
+ .name = "tps80031-rtc",
+ .pm = &tps80031_pm_ops,
+ },
+ .probe = tps80031_rtc_probe,
+};
+
+module_platform_driver(tps80031_rtc_driver);
+
+MODULE_ALIAS("platform:tps80031-rtc");
+MODULE_DESCRIPTION("TI TPS80031/TPS80032 RTC driver");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
new file mode 100644
index 000000000..2dc787dc0
--- /dev/null
+++ b/drivers/rtc/rtc-twl.c
@@ -0,0 +1,621 @@
+/*
+ * rtc-twl.c -- TWL Real Time Clock interface
+ *
+ * Copyright (C) 2007 MontaVista Software, Inc
+ * Author: Alexandre Rusev <source@mvista.com>
+ *
+ * Based on original TI driver twl4030-rtc.c
+ * Copyright (C) 2006 Texas Instruments, Inc.
+ *
+ * Based on rtc-omap.c
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
+ * Copyright (C) 2006 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+
+#include <linux/i2c/twl.h>
+
+
+/*
+ * RTC block register offsets (use TWL_MODULE_RTC)
+ */
+enum {
+ REG_SECONDS_REG = 0,
+ REG_MINUTES_REG,
+ REG_HOURS_REG,
+ REG_DAYS_REG,
+ REG_MONTHS_REG,
+ REG_YEARS_REG,
+ REG_WEEKS_REG,
+
+ REG_ALARM_SECONDS_REG,
+ REG_ALARM_MINUTES_REG,
+ REG_ALARM_HOURS_REG,
+ REG_ALARM_DAYS_REG,
+ REG_ALARM_MONTHS_REG,
+ REG_ALARM_YEARS_REG,
+
+ REG_RTC_CTRL_REG,
+ REG_RTC_STATUS_REG,
+ REG_RTC_INTERRUPTS_REG,
+
+ REG_RTC_COMP_LSB_REG,
+ REG_RTC_COMP_MSB_REG,
+};
+static const u8 twl4030_rtc_reg_map[] = {
+ [REG_SECONDS_REG] = 0x00,
+ [REG_MINUTES_REG] = 0x01,
+ [REG_HOURS_REG] = 0x02,
+ [REG_DAYS_REG] = 0x03,
+ [REG_MONTHS_REG] = 0x04,
+ [REG_YEARS_REG] = 0x05,
+ [REG_WEEKS_REG] = 0x06,
+
+ [REG_ALARM_SECONDS_REG] = 0x07,
+ [REG_ALARM_MINUTES_REG] = 0x08,
+ [REG_ALARM_HOURS_REG] = 0x09,
+ [REG_ALARM_DAYS_REG] = 0x0A,
+ [REG_ALARM_MONTHS_REG] = 0x0B,
+ [REG_ALARM_YEARS_REG] = 0x0C,
+
+ [REG_RTC_CTRL_REG] = 0x0D,
+ [REG_RTC_STATUS_REG] = 0x0E,
+ [REG_RTC_INTERRUPTS_REG] = 0x0F,
+
+ [REG_RTC_COMP_LSB_REG] = 0x10,
+ [REG_RTC_COMP_MSB_REG] = 0x11,
+};
+static const u8 twl6030_rtc_reg_map[] = {
+ [REG_SECONDS_REG] = 0x00,
+ [REG_MINUTES_REG] = 0x01,
+ [REG_HOURS_REG] = 0x02,
+ [REG_DAYS_REG] = 0x03,
+ [REG_MONTHS_REG] = 0x04,
+ [REG_YEARS_REG] = 0x05,
+ [REG_WEEKS_REG] = 0x06,
+
+ [REG_ALARM_SECONDS_REG] = 0x08,
+ [REG_ALARM_MINUTES_REG] = 0x09,
+ [REG_ALARM_HOURS_REG] = 0x0A,
+ [REG_ALARM_DAYS_REG] = 0x0B,
+ [REG_ALARM_MONTHS_REG] = 0x0C,
+ [REG_ALARM_YEARS_REG] = 0x0D,
+
+ [REG_RTC_CTRL_REG] = 0x10,
+ [REG_RTC_STATUS_REG] = 0x11,
+ [REG_RTC_INTERRUPTS_REG] = 0x12,
+
+ [REG_RTC_COMP_LSB_REG] = 0x13,
+ [REG_RTC_COMP_MSB_REG] = 0x14,
+};
+
+/* RTC_CTRL_REG bitfields */
+#define BIT_RTC_CTRL_REG_STOP_RTC_M 0x01
+#define BIT_RTC_CTRL_REG_ROUND_30S_M 0x02
+#define BIT_RTC_CTRL_REG_AUTO_COMP_M 0x04
+#define BIT_RTC_CTRL_REG_MODE_12_24_M 0x08
+#define BIT_RTC_CTRL_REG_TEST_MODE_M 0x10
+#define BIT_RTC_CTRL_REG_SET_32_COUNTER_M 0x20
+#define BIT_RTC_CTRL_REG_GET_TIME_M 0x40
+#define BIT_RTC_CTRL_REG_RTC_V_OPT 0x80
+
+/* RTC_STATUS_REG bitfields */
+#define BIT_RTC_STATUS_REG_RUN_M 0x02
+#define BIT_RTC_STATUS_REG_1S_EVENT_M 0x04
+#define BIT_RTC_STATUS_REG_1M_EVENT_M 0x08
+#define BIT_RTC_STATUS_REG_1H_EVENT_M 0x10
+#define BIT_RTC_STATUS_REG_1D_EVENT_M 0x20
+#define BIT_RTC_STATUS_REG_ALARM_M 0x40
+#define BIT_RTC_STATUS_REG_POWER_UP_M 0x80
+
+/* RTC_INTERRUPTS_REG bitfields */
+#define BIT_RTC_INTERRUPTS_REG_EVERY_M 0x03
+#define BIT_RTC_INTERRUPTS_REG_IT_TIMER_M 0x04
+#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M 0x08
+
+
+/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */
+#define ALL_TIME_REGS 6
+
+/*----------------------------------------------------------------------*/
+static u8 *rtc_reg_map;
+
+/*
+ * Supports 1 byte read from TWL RTC register.
+ */
+static int twl_rtc_read_u8(u8 *data, u8 reg)
+{
+ int ret;
+
+ ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
+ if (ret < 0)
+ pr_err("Could not read TWL register %X - error %d\n", reg, ret);
+ return ret;
+}
+
+/*
+ * Supports 1 byte write to TWL RTC registers.
+ */
+static int twl_rtc_write_u8(u8 data, u8 reg)
+{
+ int ret;
+
+ ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (rtc_reg_map[reg]));
+ if (ret < 0)
+ pr_err("Could not write TWL register %X - error %d\n",
+ reg, ret);
+ return ret;
+}
+
+/*
+ * Cache the value for timer/alarm interrupts register; this is
+ * only changed by callers holding rtc ops lock (or resume).
+ */
+static unsigned char rtc_irq_bits;
+
+/*
+ * Enable 1/second update and/or alarm interrupts.
+ */
+static int set_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ int ret;
+
+ /* if the bit is set, return from here */
+ if (rtc_irq_bits & bit)
+ return 0;
+
+ val = rtc_irq_bits | bit;
+ val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
+ ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+ if (ret == 0)
+ rtc_irq_bits = val;
+
+ return ret;
+}
+
+/*
+ * Disable update and/or alarm interrupts.
+ */
+static int mask_rtc_irq_bit(unsigned char bit)
+{
+ unsigned char val;
+ int ret;
+
+ /* if the bit is clear, return from here */
+ if (!(rtc_irq_bits & bit))
+ return 0;
+
+ val = rtc_irq_bits & ~bit;
+ ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
+ if (ret == 0)
+ rtc_irq_bits = val;
+
+ return ret;
+}
+
+static int twl_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ int irq = platform_get_irq(pdev, 0);
+ static bool twl_rtc_wake_enabled;
+ int ret;
+
+ if (enabled) {
+ ret = set_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ if (device_can_wakeup(dev) && !twl_rtc_wake_enabled) {
+ enable_irq_wake(irq);
+ twl_rtc_wake_enabled = true;
+ }
+ } else {
+ ret = mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ if (twl_rtc_wake_enabled) {
+ disable_irq_wake(irq);
+ twl_rtc_wake_enabled = false;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Gets current TWL RTC time and date parameters.
+ *
+ * The RTC's time/alarm representation is not what gmtime(3) requires
+ * Linux to use:
+ *
+ * - Months are 1..12 vs Linux 0-11
+ * - Years are 0..99 vs Linux 1900..N (we assume 21st century)
+ */
+static int twl_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char rtc_data[ALL_TIME_REGS];
+ int ret;
+ u8 save_control;
+ u8 rtc_control;
+
+ ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s: reading CTRL_REG, error %d\n", __func__, ret);
+ return ret;
+ }
+ /* for twl6030/32 make sure BIT_RTC_CTRL_REG_GET_TIME_M is clear */
+ if (twl_class_is_6030()) {
+ if (save_control & BIT_RTC_CTRL_REG_GET_TIME_M) {
+ save_control &= ~BIT_RTC_CTRL_REG_GET_TIME_M;
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s clr GET_TIME, error %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+ }
+
+ /* Copy RTC counting registers to static registers or latches */
+ rtc_control = save_control | BIT_RTC_CTRL_REG_GET_TIME_M;
+
+ /* for twl6030/32 enable read access to static shadowed registers */
+ if (twl_class_is_6030())
+ rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT;
+
+ ret = twl_rtc_write_u8(rtc_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s: writing CTRL_REG, error %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
+ (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: reading data, error %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* for twl6030 restore original state of rtc control register */
+ if (twl_class_is_6030()) {
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0) {
+ dev_err(dev, "%s: restore CTRL_REG, error %d\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_mday = bcd2bin(rtc_data[3]);
+ tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ return ret;
+}
+
+static int twl_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned char save_control;
+ unsigned char rtc_data[ALL_TIME_REGS];
+ int ret;
+
+ rtc_data[0] = bin2bcd(tm->tm_sec);
+ rtc_data[1] = bin2bcd(tm->tm_min);
+ rtc_data[2] = bin2bcd(tm->tm_hour);
+ rtc_data[3] = bin2bcd(tm->tm_mday);
+ rtc_data[4] = bin2bcd(tm->tm_mon + 1);
+ rtc_data[5] = bin2bcd(tm->tm_year - 100);
+
+ /* Stop RTC while updating the TC registers */
+ ret = twl_rtc_read_u8(&save_control, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ goto out;
+
+ save_control &= ~BIT_RTC_CTRL_REG_STOP_RTC_M;
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ goto out;
+
+ /* update all the time registers in one shot */
+ ret = twl_i2c_write(TWL_MODULE_RTC, rtc_data,
+ (rtc_reg_map[REG_SECONDS_REG]), ALL_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_set_time error %d\n", ret);
+ goto out;
+ }
+
+ /* Start back RTC */
+ save_control |= BIT_RTC_CTRL_REG_STOP_RTC_M;
+ ret = twl_rtc_write_u8(save_control, REG_RTC_CTRL_REG);
+
+out:
+ return ret;
+}
+
+/*
+ * Gets current TWL RTC alarm time.
+ */
+static int twl_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char rtc_data[ALL_TIME_REGS];
+ int ret;
+
+ ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data,
+ (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
+ if (ret < 0) {
+ dev_err(dev, "rtc_read_alarm error %d\n", ret);
+ return ret;
+ }
+
+ /* some of these fields may be wildcard/"match all" */
+ alm->time.tm_sec = bcd2bin(rtc_data[0]);
+ alm->time.tm_min = bcd2bin(rtc_data[1]);
+ alm->time.tm_hour = bcd2bin(rtc_data[2]);
+ alm->time.tm_mday = bcd2bin(rtc_data[3]);
+ alm->time.tm_mon = bcd2bin(rtc_data[4]) - 1;
+ alm->time.tm_year = bcd2bin(rtc_data[5]) + 100;
+
+ /* report cached alarm enable state */
+ if (rtc_irq_bits & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M)
+ alm->enabled = 1;
+
+ return ret;
+}
+
+static int twl_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
+{
+ unsigned char alarm_data[ALL_TIME_REGS];
+ int ret;
+
+ ret = twl_rtc_alarm_irq_enable(dev, 0);
+ if (ret)
+ goto out;
+
+ alarm_data[0] = bin2bcd(alm->time.tm_sec);
+ alarm_data[1] = bin2bcd(alm->time.tm_min);
+ alarm_data[2] = bin2bcd(alm->time.tm_hour);
+ alarm_data[3] = bin2bcd(alm->time.tm_mday);
+ alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
+ alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
+
+ /* update all the alarm registers in one shot */
+ ret = twl_i2c_write(TWL_MODULE_RTC, alarm_data,
+ (rtc_reg_map[REG_ALARM_SECONDS_REG]), ALL_TIME_REGS);
+ if (ret) {
+ dev_err(dev, "rtc_set_alarm error %d\n", ret);
+ goto out;
+ }
+
+ if (alm->enabled)
+ ret = twl_rtc_alarm_irq_enable(dev, 1);
+out:
+ return ret;
+}
+
+static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
+{
+ unsigned long events;
+ int ret = IRQ_NONE;
+ int res;
+ u8 rd_reg;
+
+ res = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ if (res)
+ goto out;
+ /*
+ * Figure out source of interrupt: ALARM or TIMER in RTC_STATUS_REG.
+ * only one (ALARM or RTC) interrupt source may be enabled
+ * at time, we also could check our results
+ * by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
+ */
+ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+ events = RTC_IRQF | RTC_AF;
+ else
+ events = RTC_IRQF | RTC_PF;
+
+ res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M,
+ REG_RTC_STATUS_REG);
+ if (res)
+ goto out;
+
+ if (twl_class_is_4030()) {
+ /* Clear on Read enabled. RTC_IT bit of TWL4030_INT_PWR_ISR1
+ * needs 2 reads to clear the interrupt. One read is done in
+ * do_twl_pwrirq(). Doing the second read, to clear
+ * the bit.
+ *
+ * FIXME the reason PWR_ISR1 needs an extra read is that
+ * RTC_IF retriggered until we cleared REG_ALARM_M above.
+ * But re-reading like this is a bad hack; by doing so we
+ * risk wrongly clearing status for some other IRQ (losing
+ * the interrupt). Be smarter about handling RTC_UF ...
+ */
+ res = twl_i2c_read_u8(TWL4030_MODULE_INT,
+ &rd_reg, TWL4030_INT_PWR_ISR1);
+ if (res)
+ goto out;
+ }
+
+ /* Notify RTC core on event */
+ rtc_update_irq(rtc, 1, events);
+
+ ret = IRQ_HANDLED;
+out:
+ return ret;
+}
+
+static struct rtc_class_ops twl_rtc_ops = {
+ .read_time = twl_rtc_read_time,
+ .set_time = twl_rtc_set_time,
+ .read_alarm = twl_rtc_read_alarm,
+ .set_alarm = twl_rtc_set_alarm,
+ .alarm_irq_enable = twl_rtc_alarm_irq_enable,
+};
+
+/*----------------------------------------------------------------------*/
+
+static int twl_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ int ret = -EINVAL;
+ int irq = platform_get_irq(pdev, 0);
+ u8 rd_reg;
+
+ if (irq <= 0)
+ return ret;
+
+ /* Initialize the register map */
+ if (twl_class_is_4030())
+ rtc_reg_map = (u8 *)twl4030_rtc_reg_map;
+ else
+ rtc_reg_map = (u8 *)twl6030_rtc_reg_map;
+
+ ret = twl_rtc_read_u8(&rd_reg, REG_RTC_STATUS_REG);
+ if (ret < 0)
+ return ret;
+
+ if (rd_reg & BIT_RTC_STATUS_REG_POWER_UP_M)
+ dev_warn(&pdev->dev, "Power up reset detected.\n");
+
+ if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
+ dev_warn(&pdev->dev, "Pending Alarm interrupt detected.\n");
+
+ /* Clear RTC Power up reset and pending alarm interrupts */
+ ret = twl_rtc_write_u8(rd_reg, REG_RTC_STATUS_REG);
+ if (ret < 0)
+ return ret;
+
+ if (twl_class_is_6030()) {
+ twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_LINE_A);
+ twl6030_interrupt_unmask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_STS_A);
+ }
+
+ dev_info(&pdev->dev, "Enabling TWL-RTC\n");
+ ret = twl_rtc_write_u8(BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG);
+ if (ret < 0)
+ return ret;
+
+ /* ensure interrupts are disabled, bootloaders can be strange */
+ ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "unable to disable interrupt\n");
+
+ /* init cached IRQ enable bits */
+ ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ return ret;
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &twl_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
+ PTR_ERR(rtc));
+ return PTR_ERR(rtc);
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ twl_rtc_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&rtc->dev), rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "IRQ is not free.\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
+}
+
+/*
+ * Disable all TWL RTC module interrupts.
+ * Sets status flag to free.
+ */
+static int twl_rtc_remove(struct platform_device *pdev)
+{
+ /* leave rtc running, but disable irqs */
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ if (twl_class_is_6030()) {
+ twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_LINE_A);
+ twl6030_interrupt_mask(TWL6030_RTC_INT_MASK,
+ REG_INT_MSK_STS_A);
+ }
+
+ return 0;
+}
+
+static void twl_rtc_shutdown(struct platform_device *pdev)
+{
+ /* mask timer interrupts, but leave alarm interrupts on to enable
+ power-on when alarm is triggered */
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static unsigned char irqstat;
+
+static int twl_rtc_suspend(struct device *dev)
+{
+ irqstat = rtc_irq_bits;
+
+ mask_rtc_irq_bit(BIT_RTC_INTERRUPTS_REG_IT_TIMER_M);
+ return 0;
+}
+
+static int twl_rtc_resume(struct device *dev)
+{
+ set_rtc_irq_bit(irqstat);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl_rtc_of_match[] = {
+ {.compatible = "ti,twl4030-rtc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
+#endif
+
+MODULE_ALIAS("platform:twl_rtc");
+
+static struct platform_driver twl4030rtc_driver = {
+ .probe = twl_rtc_probe,
+ .remove = twl_rtc_remove,
+ .shutdown = twl_rtc_shutdown,
+ .driver = {
+ .name = "twl_rtc",
+ .pm = &twl_rtc_pm_ops,
+ .of_match_table = of_match_ptr(twl_rtc_of_match),
+ },
+};
+
+module_platform_driver(twl4030rtc_driver);
+
+MODULE_AUTHOR("Texas Instruments, MontaVista Software");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
new file mode 100644
index 000000000..cb7f94ede
--- /dev/null
+++ b/drivers/rtc/rtc-tx4939.c
@@ -0,0 +1,298 @@
+/*
+ * TX4939 internal RTC driver
+ * Based on RBTX49xx patch from CELF patch archive.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * (C) Copyright TOSHIBA CORPORATION 2005-2007
+ */
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/gfp.h>
+#include <asm/txx9/tx4939.h>
+
+struct tx4939rtc_plat_data {
+ struct rtc_device *rtc;
+ struct tx4939_rtc_reg __iomem *rtcreg;
+ spinlock_t lock;
+};
+
+static struct tx4939rtc_plat_data *get_tx4939rtc_plat_data(struct device *dev)
+{
+ return platform_get_drvdata(to_platform_device(dev));
+}
+
+static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd)
+{
+ int i = 0;
+
+ __raw_writel(cmd, &rtcreg->ctl);
+ /* This might take 30us (next 32.768KHz clock) */
+ while (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_BUSY) {
+ /* timeout on approx. 100us (@ GBUS200MHz) */
+ if (i++ > 200 * 100)
+ return -EBUSY;
+ cpu_relax();
+ }
+ return 0;
+}
+
+static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ int i, ret;
+ unsigned char buf[6];
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = secs;
+ buf[3] = secs >> 8;
+ buf[4] = secs >> 16;
+ buf[5] = secs >> 24;
+ spin_lock_irq(&pdata->lock);
+ __raw_writel(0, &rtcreg->adr);
+ for (i = 0; i < 6; i++)
+ __raw_writel(buf[i], &rtcreg->dat);
+ ret = tx4939_rtc_cmd(rtcreg,
+ TX4939_RTCCTL_COMMAND_SETTIME |
+ (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
+ spin_unlock_irq(&pdata->lock);
+ return ret;
+}
+
+static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ int i, ret;
+ unsigned long sec;
+ unsigned char buf[6];
+
+ spin_lock_irq(&pdata->lock);
+ ret = tx4939_rtc_cmd(rtcreg,
+ TX4939_RTCCTL_COMMAND_GETTIME |
+ (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
+ if (ret) {
+ spin_unlock_irq(&pdata->lock);
+ return ret;
+ }
+ __raw_writel(2, &rtcreg->adr);
+ for (i = 2; i < 6; i++)
+ buf[i] = __raw_readl(&rtcreg->dat);
+ spin_unlock_irq(&pdata->lock);
+ sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ rtc_time_to_tm(sec, tm);
+ return rtc_valid_tm(tm);
+}
+
+static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ int i, ret;
+ unsigned long sec;
+ unsigned char buf[6];
+
+ if (alrm->time.tm_sec < 0 ||
+ alrm->time.tm_min < 0 ||
+ alrm->time.tm_hour < 0 ||
+ alrm->time.tm_mday < 0 ||
+ alrm->time.tm_mon < 0 ||
+ alrm->time.tm_year < 0)
+ return -EINVAL;
+ rtc_tm_to_time(&alrm->time, &sec);
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = sec;
+ buf[3] = sec >> 8;
+ buf[4] = sec >> 16;
+ buf[5] = sec >> 24;
+ spin_lock_irq(&pdata->lock);
+ __raw_writel(0, &rtcreg->adr);
+ for (i = 0; i < 6; i++)
+ __raw_writel(buf[i], &rtcreg->dat);
+ ret = tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_SETALARM |
+ (alrm->enabled ? TX4939_RTCCTL_ALME : 0));
+ spin_unlock_irq(&pdata->lock);
+ return ret;
+}
+
+static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ int i, ret;
+ unsigned long sec;
+ unsigned char buf[6];
+ u32 ctl;
+
+ spin_lock_irq(&pdata->lock);
+ ret = tx4939_rtc_cmd(rtcreg,
+ TX4939_RTCCTL_COMMAND_GETALARM |
+ (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALME));
+ if (ret) {
+ spin_unlock_irq(&pdata->lock);
+ return ret;
+ }
+ __raw_writel(2, &rtcreg->adr);
+ for (i = 2; i < 6; i++)
+ buf[i] = __raw_readl(&rtcreg->dat);
+ ctl = __raw_readl(&rtcreg->ctl);
+ alrm->enabled = (ctl & TX4939_RTCCTL_ALME) ? 1 : 0;
+ alrm->pending = (ctl & TX4939_RTCCTL_ALMD) ? 1 : 0;
+ spin_unlock_irq(&pdata->lock);
+ sec = (buf[5] << 24) | (buf[4] << 16) | (buf[3] << 8) | buf[2];
+ rtc_time_to_tm(sec, &alrm->time);
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int tx4939_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+
+ spin_lock_irq(&pdata->lock);
+ tx4939_rtc_cmd(pdata->rtcreg,
+ TX4939_RTCCTL_COMMAND_NOP |
+ (enabled ? TX4939_RTCCTL_ALME : 0));
+ spin_unlock_irq(&pdata->lock);
+ return 0;
+}
+
+static irqreturn_t tx4939_rtc_interrupt(int irq, void *dev_id)
+{
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev_id);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ unsigned long events = RTC_IRQF;
+
+ spin_lock(&pdata->lock);
+ if (__raw_readl(&rtcreg->ctl) & TX4939_RTCCTL_ALMD) {
+ events |= RTC_AF;
+ tx4939_rtc_cmd(rtcreg, TX4939_RTCCTL_COMMAND_NOP);
+ }
+ spin_unlock(&pdata->lock);
+ rtc_update_irq(pdata->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops tx4939_rtc_ops = {
+ .read_time = tx4939_rtc_read_time,
+ .read_alarm = tx4939_rtc_read_alarm,
+ .set_alarm = tx4939_rtc_set_alarm,
+ .set_mmss = tx4939_rtc_set_mmss,
+ .alarm_irq_enable = tx4939_rtc_alarm_irq_enable,
+};
+
+static ssize_t tx4939_rtc_nvram_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ ssize_t count;
+
+ spin_lock_irq(&pdata->lock);
+ for (count = 0; size > 0 && pos < TX4939_RTC_REG_RAMSIZE;
+ count++, size--) {
+ __raw_writel(pos++, &rtcreg->adr);
+ *buf++ = __raw_readl(&rtcreg->dat);
+ }
+ spin_unlock_irq(&pdata->lock);
+ return count;
+}
+
+static ssize_t tx4939_rtc_nvram_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t pos, size_t size)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct tx4939rtc_plat_data *pdata = get_tx4939rtc_plat_data(dev);
+ struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg;
+ ssize_t count;
+
+ spin_lock_irq(&pdata->lock);
+ for (count = 0; size > 0 && pos < TX4939_RTC_REG_RAMSIZE;
+ count++, size--) {
+ __raw_writel(pos++, &rtcreg->adr);
+ __raw_writel(*buf++, &rtcreg->dat);
+ }
+ spin_unlock_irq(&pdata->lock);
+ return count;
+}
+
+static struct bin_attribute tx4939_rtc_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = TX4939_RTC_REG_RAMSIZE,
+ .read = tx4939_rtc_nvram_read,
+ .write = tx4939_rtc_nvram_write,
+};
+
+static int __init tx4939_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtc;
+ struct tx4939rtc_plat_data *pdata;
+ struct resource *res;
+ int irq, ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, pdata);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata->rtcreg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->rtcreg))
+ return PTR_ERR(pdata->rtcreg);
+
+ spin_lock_init(&pdata->lock);
+ tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
+ if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt,
+ 0, pdev->name, &pdev->dev) < 0)
+ return -EBUSY;
+ rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &tx4939_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+ pdata->rtc = rtc;
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
+
+ return ret;
+}
+
+static int __exit tx4939_rtc_remove(struct platform_device *pdev)
+{
+ struct tx4939rtc_plat_data *pdata = platform_get_drvdata(pdev);
+
+ sysfs_remove_bin_file(&pdev->dev.kobj, &tx4939_rtc_nvram_attr);
+ spin_lock_irq(&pdata->lock);
+ tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
+ spin_unlock_irq(&pdata->lock);
+ return 0;
+}
+
+static struct platform_driver tx4939_rtc_driver = {
+ .remove = __exit_p(tx4939_rtc_remove),
+ .driver = {
+ .name = "tx4939rtc",
+ },
+};
+
+module_platform_driver_probe(tx4939_rtc_driver, tx4939_rtc_probe);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TX4939 internal RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tx4939rtc");
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
new file mode 100644
index 000000000..bfbfa7ed7
--- /dev/null
+++ b/drivers/rtc/rtc-v3020.c
@@ -0,0 +1,393 @@
+/* drivers/rtc/rtc-v3020.c
+ *
+ * Copyright (C) 2006 8D Technologies inc.
+ * Copyright (C) 2004 Compulab Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Driver for the V3020 RTC
+ *
+ * Changelog:
+ *
+ * 10-May-2006: Raphael Assenat <raph@8d.com>
+ * - Converted to platform driver
+ * - Use the generic rtc class
+ *
+ * ??-???-2004: Someone at Compulab
+ * - Initial driver creation.
+ *
+ */
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/types.h>
+#include <linux/bcd.h>
+#include <linux/rtc-v3020.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/io.h>
+
+#undef DEBUG
+
+struct v3020;
+
+struct v3020_chip_ops {
+ int (*map_io)(struct v3020 *chip, struct platform_device *pdev,
+ struct v3020_platform_data *pdata);
+ void (*unmap_io)(struct v3020 *chip);
+ unsigned char (*read_bit)(struct v3020 *chip);
+ void (*write_bit)(struct v3020 *chip, unsigned char bit);
+};
+
+#define V3020_CS 0
+#define V3020_WR 1
+#define V3020_RD 2
+#define V3020_IO 3
+
+struct v3020_gpio {
+ const char *name;
+ unsigned int gpio;
+};
+
+struct v3020 {
+ /* MMIO access */
+ void __iomem *ioaddress;
+ int leftshift;
+
+ /* GPIO access */
+ struct v3020_gpio *gpio;
+
+ struct v3020_chip_ops *ops;
+
+ struct rtc_device *rtc;
+};
+
+
+static int v3020_mmio_map(struct v3020 *chip, struct platform_device *pdev,
+ struct v3020_platform_data *pdata)
+{
+ if (pdev->num_resources != 1)
+ return -EBUSY;
+
+ if (pdev->resource[0].flags != IORESOURCE_MEM)
+ return -EBUSY;
+
+ chip->leftshift = pdata->leftshift;
+ chip->ioaddress = ioremap(pdev->resource[0].start, 1);
+ if (chip->ioaddress == NULL)
+ return -EBUSY;
+
+ return 0;
+}
+
+static void v3020_mmio_unmap(struct v3020 *chip)
+{
+ iounmap(chip->ioaddress);
+}
+
+static void v3020_mmio_write_bit(struct v3020 *chip, unsigned char bit)
+{
+ writel(bit << chip->leftshift, chip->ioaddress);
+}
+
+static unsigned char v3020_mmio_read_bit(struct v3020 *chip)
+{
+ return !!(readl(chip->ioaddress) & (1 << chip->leftshift));
+}
+
+static struct v3020_chip_ops v3020_mmio_ops = {
+ .map_io = v3020_mmio_map,
+ .unmap_io = v3020_mmio_unmap,
+ .read_bit = v3020_mmio_read_bit,
+ .write_bit = v3020_mmio_write_bit,
+};
+
+static struct v3020_gpio v3020_gpio[] = {
+ { "RTC CS", 0 },
+ { "RTC WR", 0 },
+ { "RTC RD", 0 },
+ { "RTC IO", 0 },
+};
+
+static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev,
+ struct v3020_platform_data *pdata)
+{
+ int i, err;
+
+ v3020_gpio[V3020_CS].gpio = pdata->gpio_cs;
+ v3020_gpio[V3020_WR].gpio = pdata->gpio_wr;
+ v3020_gpio[V3020_RD].gpio = pdata->gpio_rd;
+ v3020_gpio[V3020_IO].gpio = pdata->gpio_io;
+
+ for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++) {
+ err = gpio_request(v3020_gpio[i].gpio, v3020_gpio[i].name);
+ if (err)
+ goto err_request;
+
+ gpio_direction_output(v3020_gpio[i].gpio, 1);
+ }
+
+ chip->gpio = v3020_gpio;
+
+ return 0;
+
+err_request:
+ while (--i >= 0)
+ gpio_free(v3020_gpio[i].gpio);
+
+ return err;
+}
+
+static void v3020_gpio_unmap(struct v3020 *chip)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(v3020_gpio); i++)
+ gpio_free(v3020_gpio[i].gpio);
+}
+
+static void v3020_gpio_write_bit(struct v3020 *chip, unsigned char bit)
+{
+ gpio_direction_output(chip->gpio[V3020_IO].gpio, bit);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 0);
+ gpio_set_value(chip->gpio[V3020_WR].gpio, 0);
+ udelay(1);
+ gpio_set_value(chip->gpio[V3020_WR].gpio, 1);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 1);
+}
+
+static unsigned char v3020_gpio_read_bit(struct v3020 *chip)
+{
+ int bit;
+
+ gpio_direction_input(chip->gpio[V3020_IO].gpio);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 0);
+ gpio_set_value(chip->gpio[V3020_RD].gpio, 0);
+ udelay(1);
+ bit = !!gpio_get_value(chip->gpio[V3020_IO].gpio);
+ udelay(1);
+ gpio_set_value(chip->gpio[V3020_RD].gpio, 1);
+ gpio_set_value(chip->gpio[V3020_CS].gpio, 1);
+
+ return bit;
+}
+
+static struct v3020_chip_ops v3020_gpio_ops = {
+ .map_io = v3020_gpio_map,
+ .unmap_io = v3020_gpio_unmap,
+ .read_bit = v3020_gpio_read_bit,
+ .write_bit = v3020_gpio_write_bit,
+};
+
+static void v3020_set_reg(struct v3020 *chip, unsigned char address,
+ unsigned char data)
+{
+ int i;
+ unsigned char tmp;
+
+ tmp = address;
+ for (i = 0; i < 4; i++) {
+ chip->ops->write_bit(chip, (tmp & 1));
+ tmp >>= 1;
+ udelay(1);
+ }
+
+ /* Commands dont have data */
+ if (!V3020_IS_COMMAND(address)) {
+ for (i = 0; i < 8; i++) {
+ chip->ops->write_bit(chip, (data & 1));
+ data >>= 1;
+ udelay(1);
+ }
+ }
+}
+
+static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address)
+{
+ unsigned int data = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ chip->ops->write_bit(chip, (address & 1));
+ address >>= 1;
+ udelay(1);
+ }
+
+ for (i = 0; i < 8; i++) {
+ data >>= 1;
+ if (chip->ops->read_bit(chip))
+ data |= 0x80;
+ udelay(1);
+ }
+
+ return data;
+}
+
+static int v3020_read_time(struct device *dev, struct rtc_time *dt)
+{
+ struct v3020 *chip = dev_get_drvdata(dev);
+ int tmp;
+
+ /* Copy the current time to ram... */
+ v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0);
+
+ /* ...and then read constant values. */
+ tmp = v3020_get_reg(chip, V3020_SECONDS);
+ dt->tm_sec = bcd2bin(tmp);
+ tmp = v3020_get_reg(chip, V3020_MINUTES);
+ dt->tm_min = bcd2bin(tmp);
+ tmp = v3020_get_reg(chip, V3020_HOURS);
+ dt->tm_hour = bcd2bin(tmp);
+ tmp = v3020_get_reg(chip, V3020_MONTH_DAY);
+ dt->tm_mday = bcd2bin(tmp);
+ tmp = v3020_get_reg(chip, V3020_MONTH);
+ dt->tm_mon = bcd2bin(tmp) - 1;
+ tmp = v3020_get_reg(chip, V3020_WEEK_DAY);
+ dt->tm_wday = bcd2bin(tmp);
+ tmp = v3020_get_reg(chip, V3020_YEAR);
+ dt->tm_year = bcd2bin(tmp)+100;
+
+ dev_dbg(dev, "\n%s : Read RTC values\n", __func__);
+ dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour);
+ dev_dbg(dev, "tm_min : %i\n", dt->tm_min);
+ dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec);
+ dev_dbg(dev, "tm_year: %i\n", dt->tm_year);
+ dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon);
+ dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday);
+ dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday);
+
+ return 0;
+}
+
+
+static int v3020_set_time(struct device *dev, struct rtc_time *dt)
+{
+ struct v3020 *chip = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "\n%s : Setting RTC values\n", __func__);
+ dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec);
+ dev_dbg(dev, "tm_min : %i\n", dt->tm_min);
+ dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour);
+ dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday);
+ dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday);
+ dev_dbg(dev, "tm_year: %i\n", dt->tm_year);
+
+ /* Write all the values to ram... */
+ v3020_set_reg(chip, V3020_SECONDS, bin2bcd(dt->tm_sec));
+ v3020_set_reg(chip, V3020_MINUTES, bin2bcd(dt->tm_min));
+ v3020_set_reg(chip, V3020_HOURS, bin2bcd(dt->tm_hour));
+ v3020_set_reg(chip, V3020_MONTH_DAY, bin2bcd(dt->tm_mday));
+ v3020_set_reg(chip, V3020_MONTH, bin2bcd(dt->tm_mon + 1));
+ v3020_set_reg(chip, V3020_WEEK_DAY, bin2bcd(dt->tm_wday));
+ v3020_set_reg(chip, V3020_YEAR, bin2bcd(dt->tm_year % 100));
+
+ /* ...and set the clock. */
+ v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0);
+
+ /* Compulab used this delay here. I dont know why,
+ * the datasheet does not specify a delay. */
+ /*mdelay(5);*/
+
+ return 0;
+}
+
+static const struct rtc_class_ops v3020_rtc_ops = {
+ .read_time = v3020_read_time,
+ .set_time = v3020_set_time,
+};
+
+static int rtc_probe(struct platform_device *pdev)
+{
+ struct v3020_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct v3020 *chip;
+ int retval = -EBUSY;
+ int i;
+ int temp;
+
+ chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return -ENOMEM;
+
+ if (pdata->use_gpio)
+ chip->ops = &v3020_gpio_ops;
+ else
+ chip->ops = &v3020_mmio_ops;
+
+ retval = chip->ops->map_io(chip, pdev, pdata);
+ if (retval)
+ return retval;
+
+ /* Make sure the v3020 expects a communication cycle
+ * by reading 8 times */
+ for (i = 0; i < 8; i++)
+ temp = chip->ops->read_bit(chip);
+
+ /* Test chip by doing a write/read sequence
+ * to the chip ram */
+ v3020_set_reg(chip, V3020_SECONDS, 0x33);
+ if (v3020_get_reg(chip, V3020_SECONDS) != 0x33) {
+ retval = -ENODEV;
+ goto err_io;
+ }
+
+ /* Make sure frequency measurement mode, test modes, and lock
+ * are all disabled */
+ v3020_set_reg(chip, V3020_STATUS_0, 0x0);
+
+ if (pdata->use_gpio)
+ dev_info(&pdev->dev, "Chip available at GPIOs "
+ "%d, %d, %d, %d\n",
+ chip->gpio[V3020_CS].gpio, chip->gpio[V3020_WR].gpio,
+ chip->gpio[V3020_RD].gpio, chip->gpio[V3020_IO].gpio);
+ else
+ dev_info(&pdev->dev, "Chip available at "
+ "physical address 0x%llx,"
+ "data connected to D%d\n",
+ (unsigned long long)pdev->resource[0].start,
+ chip->leftshift);
+
+ platform_set_drvdata(pdev, chip);
+
+ chip->rtc = devm_rtc_device_register(&pdev->dev, "v3020",
+ &v3020_rtc_ops, THIS_MODULE);
+ if (IS_ERR(chip->rtc)) {
+ retval = PTR_ERR(chip->rtc);
+ goto err_io;
+ }
+
+ return 0;
+
+err_io:
+ chip->ops->unmap_io(chip);
+
+ return retval;
+}
+
+static int rtc_remove(struct platform_device *dev)
+{
+ struct v3020 *chip = platform_get_drvdata(dev);
+
+ chip->ops->unmap_io(chip);
+
+ return 0;
+}
+
+static struct platform_driver rtc_device_driver = {
+ .probe = rtc_probe,
+ .remove = rtc_remove,
+ .driver = {
+ .name = "v3020",
+ },
+};
+
+module_platform_driver(rtc_device_driver);
+
+MODULE_DESCRIPTION("V3020 RTC");
+MODULE_AUTHOR("Raphael Assenat");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:v3020");
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
new file mode 100644
index 000000000..f64c28227
--- /dev/null
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -0,0 +1,381 @@
+/*
+ * Driver for NEC VR4100 series Real Time Clock unit.
+ *
+ * Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/err.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/log2.h>
+
+#include <asm/div64.h>
+
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
+MODULE_DESCRIPTION("NEC VR4100 series RTC driver");
+MODULE_LICENSE("GPL v2");
+
+/* RTC 1 registers */
+#define ETIMELREG 0x00
+#define ETIMEMREG 0x02
+#define ETIMEHREG 0x04
+/* RFU */
+#define ECMPLREG 0x08
+#define ECMPMREG 0x0a
+#define ECMPHREG 0x0c
+/* RFU */
+#define RTCL1LREG 0x10
+#define RTCL1HREG 0x12
+#define RTCL1CNTLREG 0x14
+#define RTCL1CNTHREG 0x16
+#define RTCL2LREG 0x18
+#define RTCL2HREG 0x1a
+#define RTCL2CNTLREG 0x1c
+#define RTCL2CNTHREG 0x1e
+
+/* RTC 2 registers */
+#define TCLKLREG 0x00
+#define TCLKHREG 0x02
+#define TCLKCNTLREG 0x04
+#define TCLKCNTHREG 0x06
+/* RFU */
+#define RTCINTREG 0x1e
+ #define TCLOCK_INT 0x08
+ #define RTCLONG2_INT 0x04
+ #define RTCLONG1_INT 0x02
+ #define ELAPSEDTIME_INT 0x01
+
+#define RTC_FREQUENCY 32768
+#define MAX_PERIODIC_RATE 6553
+
+static void __iomem *rtc1_base;
+static void __iomem *rtc2_base;
+
+#define rtc1_read(offset) readw(rtc1_base + (offset))
+#define rtc1_write(offset, value) writew((value), rtc1_base + (offset))
+
+#define rtc2_read(offset) readw(rtc2_base + (offset))
+#define rtc2_write(offset, value) writew((value), rtc2_base + (offset))
+
+static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */
+
+static DEFINE_SPINLOCK(rtc_lock);
+static char rtc_name[] = "RTC";
+static unsigned long periodic_count;
+static unsigned int alarm_enabled;
+static int aie_irq;
+static int pie_irq;
+
+static inline unsigned long read_elapsed_second(void)
+{
+
+ unsigned long first_low, first_mid, first_high;
+
+ unsigned long second_low, second_mid, second_high;
+
+ do {
+ first_low = rtc1_read(ETIMELREG);
+ first_mid = rtc1_read(ETIMEMREG);
+ first_high = rtc1_read(ETIMEHREG);
+ second_low = rtc1_read(ETIMELREG);
+ second_mid = rtc1_read(ETIMEMREG);
+ second_high = rtc1_read(ETIMEHREG);
+ } while (first_low != second_low || first_mid != second_mid ||
+ first_high != second_high);
+
+ return (first_high << 17) | (first_mid << 1) | (first_low >> 15);
+}
+
+static inline void write_elapsed_second(unsigned long sec)
+{
+ spin_lock_irq(&rtc_lock);
+
+ rtc1_write(ETIMELREG, (uint16_t)(sec << 15));
+ rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1));
+ rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17));
+
+ spin_unlock_irq(&rtc_lock);
+}
+
+static void vr41xx_rtc_release(struct device *dev)
+{
+
+ spin_lock_irq(&rtc_lock);
+
+ rtc1_write(ECMPLREG, 0);
+ rtc1_write(ECMPMREG, 0);
+ rtc1_write(ECMPHREG, 0);
+ rtc1_write(RTCL1LREG, 0);
+ rtc1_write(RTCL1HREG, 0);
+
+ spin_unlock_irq(&rtc_lock);
+
+ disable_irq(aie_irq);
+ disable_irq(pie_irq);
+}
+
+static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long epoch_sec, elapsed_sec;
+
+ epoch_sec = mktime(epoch, 1, 1, 0, 0, 0);
+ elapsed_sec = read_elapsed_second();
+
+ rtc_time_to_tm(epoch_sec + elapsed_sec, time);
+
+ return 0;
+}
+
+static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time)
+{
+ unsigned long epoch_sec, current_sec;
+
+ epoch_sec = mktime(epoch, 1, 1, 0, 0, 0);
+ current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
+ time->tm_hour, time->tm_min, time->tm_sec);
+
+ write_elapsed_second(current_sec - epoch_sec);
+
+ return 0;
+}
+
+static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ unsigned long low, mid, high;
+ struct rtc_time *time = &wkalrm->time;
+
+ spin_lock_irq(&rtc_lock);
+
+ low = rtc1_read(ECMPLREG);
+ mid = rtc1_read(ECMPMREG);
+ high = rtc1_read(ECMPHREG);
+ wkalrm->enabled = alarm_enabled;
+
+ spin_unlock_irq(&rtc_lock);
+
+ rtc_time_to_tm((high << 17) | (mid << 1) | (low >> 15), time);
+
+ return 0;
+}
+
+static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+ unsigned long alarm_sec;
+ struct rtc_time *time = &wkalrm->time;
+
+ alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
+ time->tm_hour, time->tm_min, time->tm_sec);
+
+ spin_lock_irq(&rtc_lock);
+
+ if (alarm_enabled)
+ disable_irq(aie_irq);
+
+ rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15));
+ rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1));
+ rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17));
+
+ if (wkalrm->enabled)
+ enable_irq(aie_irq);
+
+ alarm_enabled = wkalrm->enabled;
+
+ spin_unlock_irq(&rtc_lock);
+
+ return 0;
+}
+
+static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case RTC_EPOCH_READ:
+ return put_user(epoch, (unsigned long __user *)arg);
+ case RTC_EPOCH_SET:
+ /* Doesn't support before 1900 */
+ if (arg < 1900)
+ return -EINVAL;
+ epoch = arg;
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+static int vr41xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ spin_lock_irq(&rtc_lock);
+ if (enabled) {
+ if (!alarm_enabled) {
+ enable_irq(aie_irq);
+ alarm_enabled = 1;
+ }
+ } else {
+ if (alarm_enabled) {
+ disable_irq(aie_irq);
+ alarm_enabled = 0;
+ }
+ }
+ spin_unlock_irq(&rtc_lock);
+ return 0;
+}
+
+static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = (struct platform_device *)dev_id;
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+
+ rtc2_write(RTCINTREG, ELAPSEDTIME_INT);
+
+ rtc_update_irq(rtc, 1, RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = (struct platform_device *)dev_id;
+ struct rtc_device *rtc = platform_get_drvdata(pdev);
+ unsigned long count = periodic_count;
+
+ rtc2_write(RTCINTREG, RTCLONG1_INT);
+
+ rtc1_write(RTCL1LREG, count);
+ rtc1_write(RTCL1HREG, count >> 16);
+
+ rtc_update_irq(rtc, 1, RTC_PF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops vr41xx_rtc_ops = {
+ .release = vr41xx_rtc_release,
+ .ioctl = vr41xx_rtc_ioctl,
+ .read_time = vr41xx_rtc_read_time,
+ .set_time = vr41xx_rtc_set_time,
+ .read_alarm = vr41xx_rtc_read_alarm,
+ .set_alarm = vr41xx_rtc_set_alarm,
+};
+
+static int rtc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct rtc_device *rtc;
+ int retval;
+
+ if (pdev->num_resources != 4)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EBUSY;
+
+ rtc1_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!rtc1_base)
+ return -EBUSY;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ retval = -EBUSY;
+ goto err_rtc1_iounmap;
+ }
+
+ rtc2_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!rtc2_base) {
+ retval = -EBUSY;
+ goto err_rtc1_iounmap;
+ }
+
+ rtc = devm_rtc_device_register(&pdev->dev, rtc_name, &vr41xx_rtc_ops,
+ THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ retval = PTR_ERR(rtc);
+ goto err_iounmap_all;
+ }
+
+ rtc->max_user_freq = MAX_PERIODIC_RATE;
+
+ spin_lock_irq(&rtc_lock);
+
+ rtc1_write(ECMPLREG, 0);
+ rtc1_write(ECMPMREG, 0);
+ rtc1_write(ECMPHREG, 0);
+ rtc1_write(RTCL1LREG, 0);
+ rtc1_write(RTCL1HREG, 0);
+
+ spin_unlock_irq(&rtc_lock);
+
+ aie_irq = platform_get_irq(pdev, 0);
+ if (aie_irq <= 0) {
+ retval = -EBUSY;
+ goto err_iounmap_all;
+ }
+
+ retval = devm_request_irq(&pdev->dev, aie_irq, elapsedtime_interrupt, 0,
+ "elapsed_time", pdev);
+ if (retval < 0)
+ goto err_iounmap_all;
+
+ pie_irq = platform_get_irq(pdev, 1);
+ if (pie_irq <= 0) {
+ retval = -EBUSY;
+ goto err_iounmap_all;
+ }
+
+ retval = devm_request_irq(&pdev->dev, pie_irq, rtclong1_interrupt, 0,
+ "rtclong1", pdev);
+ if (retval < 0)
+ goto err_iounmap_all;
+
+ platform_set_drvdata(pdev, rtc);
+
+ disable_irq(aie_irq);
+ disable_irq(pie_irq);
+
+ dev_info(&pdev->dev, "Real Time Clock of NEC VR4100 series\n");
+
+ return 0;
+
+err_iounmap_all:
+ rtc2_base = NULL;
+
+err_rtc1_iounmap:
+ rtc1_base = NULL;
+
+ return retval;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:RTC");
+
+static struct platform_driver rtc_platform_driver = {
+ .probe = rtc_probe,
+ .driver = {
+ .name = rtc_name,
+ },
+};
+
+module_platform_driver(rtc_platform_driver);
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
new file mode 100644
index 000000000..a58b6d17e
--- /dev/null
+++ b/drivers/rtc/rtc-vt8500.c
@@ -0,0 +1,289 @@
+/*
+ * drivers/rtc/rtc-vt8500.c
+ *
+ * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * Based on rtc-pxa.c
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/bcd.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+
+/*
+ * Register definitions
+ */
+#define VT8500_RTC_TS 0x00 /* Time set */
+#define VT8500_RTC_DS 0x04 /* Date set */
+#define VT8500_RTC_AS 0x08 /* Alarm set */
+#define VT8500_RTC_CR 0x0c /* Control */
+#define VT8500_RTC_TR 0x10 /* Time read */
+#define VT8500_RTC_DR 0x14 /* Date read */
+#define VT8500_RTC_WS 0x18 /* Write status */
+#define VT8500_RTC_CL 0x20 /* Calibration */
+#define VT8500_RTC_IS 0x24 /* Interrupt status */
+#define VT8500_RTC_ST 0x28 /* Status */
+
+#define INVALID_TIME_BIT (1 << 31)
+
+#define DATE_CENTURY_S 19
+#define DATE_YEAR_S 11
+#define DATE_YEAR_MASK (0xff << DATE_YEAR_S)
+#define DATE_MONTH_S 6
+#define DATE_MONTH_MASK (0x1f << DATE_MONTH_S)
+#define DATE_DAY_MASK 0x3f
+
+#define TIME_DOW_S 20
+#define TIME_DOW_MASK (0x07 << TIME_DOW_S)
+#define TIME_HOUR_S 14
+#define TIME_HOUR_MASK (0x3f << TIME_HOUR_S)
+#define TIME_MIN_S 7
+#define TIME_MIN_MASK (0x7f << TIME_MIN_S)
+#define TIME_SEC_MASK 0x7f
+
+#define ALARM_DAY_S 20
+#define ALARM_DAY_MASK (0x3f << ALARM_DAY_S)
+
+#define ALARM_DAY_BIT (1 << 29)
+#define ALARM_HOUR_BIT (1 << 28)
+#define ALARM_MIN_BIT (1 << 27)
+#define ALARM_SEC_BIT (1 << 26)
+
+#define ALARM_ENABLE_MASK (ALARM_DAY_BIT \
+ | ALARM_HOUR_BIT \
+ | ALARM_MIN_BIT \
+ | ALARM_SEC_BIT)
+
+#define VT8500_RTC_CR_ENABLE (1 << 0) /* Enable RTC */
+#define VT8500_RTC_CR_12H (1 << 1) /* 12h time format */
+#define VT8500_RTC_CR_SM_ENABLE (1 << 2) /* Enable periodic irqs */
+#define VT8500_RTC_CR_SM_SEC (1 << 3) /* 0: 1Hz/60, 1: 1Hz */
+#define VT8500_RTC_CR_CALIB (1 << 4) /* Enable calibration */
+
+#define VT8500_RTC_IS_ALARM (1 << 0) /* Alarm interrupt status */
+
+struct vt8500_rtc {
+ void __iomem *regbase;
+ int irq_alarm;
+ struct rtc_device *rtc;
+ spinlock_t lock; /* Protects this structure */
+};
+
+static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_id;
+ u32 isr;
+ unsigned long events = 0;
+
+ spin_lock(&vt8500_rtc->lock);
+
+ /* clear interrupt sources */
+ isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+ writel(isr, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+ spin_unlock(&vt8500_rtc->lock);
+
+ if (isr & VT8500_RTC_IS_ALARM)
+ events |= RTC_AF | RTC_IRQF;
+
+ rtc_update_irq(vt8500_rtc->rtc, 1, events);
+
+ return IRQ_HANDLED;
+}
+
+static int vt8500_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ u32 date, time;
+
+ date = readl(vt8500_rtc->regbase + VT8500_RTC_DR);
+ time = readl(vt8500_rtc->regbase + VT8500_RTC_TR);
+
+ tm->tm_sec = bcd2bin(time & TIME_SEC_MASK);
+ tm->tm_min = bcd2bin((time & TIME_MIN_MASK) >> TIME_MIN_S);
+ tm->tm_hour = bcd2bin((time & TIME_HOUR_MASK) >> TIME_HOUR_S);
+ tm->tm_mday = bcd2bin(date & DATE_DAY_MASK);
+ tm->tm_mon = bcd2bin((date & DATE_MONTH_MASK) >> DATE_MONTH_S) - 1;
+ tm->tm_year = bcd2bin((date & DATE_YEAR_MASK) >> DATE_YEAR_S)
+ + ((date >> DATE_CENTURY_S) & 1 ? 200 : 100);
+ tm->tm_wday = (time & TIME_DOW_MASK) >> TIME_DOW_S;
+
+ return 0;
+}
+
+static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+ if (tm->tm_year < 100) {
+ dev_warn(dev, "Only years 2000-2199 are supported by the "
+ "hardware!\n");
+ return -EINVAL;
+ }
+
+ writel((bin2bcd(tm->tm_year % 100) << DATE_YEAR_S)
+ | (bin2bcd(tm->tm_mon + 1) << DATE_MONTH_S)
+ | (bin2bcd(tm->tm_mday))
+ | ((tm->tm_year >= 200) << DATE_CENTURY_S),
+ vt8500_rtc->regbase + VT8500_RTC_DS);
+ writel((bin2bcd(tm->tm_wday) << TIME_DOW_S)
+ | (bin2bcd(tm->tm_hour) << TIME_HOUR_S)
+ | (bin2bcd(tm->tm_min) << TIME_MIN_S)
+ | (bin2bcd(tm->tm_sec)),
+ vt8500_rtc->regbase + VT8500_RTC_TS);
+
+ return 0;
+}
+
+static int vt8500_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ u32 isr, alarm;
+
+ alarm = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+ isr = readl(vt8500_rtc->regbase + VT8500_RTC_IS);
+
+ alrm->time.tm_mday = bcd2bin((alarm & ALARM_DAY_MASK) >> ALARM_DAY_S);
+ alrm->time.tm_hour = bcd2bin((alarm & TIME_HOUR_MASK) >> TIME_HOUR_S);
+ alrm->time.tm_min = bcd2bin((alarm & TIME_MIN_MASK) >> TIME_MIN_S);
+ alrm->time.tm_sec = bcd2bin((alarm & TIME_SEC_MASK));
+
+ alrm->enabled = (alarm & ALARM_ENABLE_MASK) ? 1 : 0;
+ alrm->pending = (isr & VT8500_RTC_IS_ALARM) ? 1 : 0;
+
+ return rtc_valid_tm(&alrm->time);
+}
+
+static int vt8500_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+
+ writel((alrm->enabled ? ALARM_ENABLE_MASK : 0)
+ | (bin2bcd(alrm->time.tm_mday) << ALARM_DAY_S)
+ | (bin2bcd(alrm->time.tm_hour) << TIME_HOUR_S)
+ | (bin2bcd(alrm->time.tm_min) << TIME_MIN_S)
+ | (bin2bcd(alrm->time.tm_sec)),
+ vt8500_rtc->regbase + VT8500_RTC_AS);
+
+ return 0;
+}
+
+static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev);
+ unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_AS);
+
+ if (enabled)
+ tmp |= ALARM_ENABLE_MASK;
+ else
+ tmp &= ~ALARM_ENABLE_MASK;
+
+ writel(tmp, vt8500_rtc->regbase + VT8500_RTC_AS);
+ return 0;
+}
+
+static const struct rtc_class_ops vt8500_rtc_ops = {
+ .read_time = vt8500_rtc_read_time,
+ .set_time = vt8500_rtc_set_time,
+ .read_alarm = vt8500_rtc_read_alarm,
+ .set_alarm = vt8500_rtc_set_alarm,
+ .alarm_irq_enable = vt8500_alarm_irq_enable,
+};
+
+static int vt8500_rtc_probe(struct platform_device *pdev)
+{
+ struct vt8500_rtc *vt8500_rtc;
+ struct resource *res;
+ int ret;
+
+ vt8500_rtc = devm_kzalloc(&pdev->dev,
+ sizeof(struct vt8500_rtc), GFP_KERNEL);
+ if (!vt8500_rtc)
+ return -ENOMEM;
+
+ spin_lock_init(&vt8500_rtc->lock);
+ platform_set_drvdata(pdev, vt8500_rtc);
+
+ vt8500_rtc->irq_alarm = platform_get_irq(pdev, 0);
+ if (vt8500_rtc->irq_alarm < 0) {
+ dev_err(&pdev->dev, "No alarm IRQ resource defined\n");
+ return vt8500_rtc->irq_alarm;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ vt8500_rtc->regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(vt8500_rtc->regbase))
+ return PTR_ERR(vt8500_rtc->regbase);
+
+ /* Enable RTC and set it to 24-hour mode */
+ writel(VT8500_RTC_CR_ENABLE,
+ vt8500_rtc->regbase + VT8500_RTC_CR);
+
+ vt8500_rtc->rtc = devm_rtc_device_register(&pdev->dev, "vt8500-rtc",
+ &vt8500_rtc_ops, THIS_MODULE);
+ if (IS_ERR(vt8500_rtc->rtc)) {
+ ret = PTR_ERR(vt8500_rtc->rtc);
+ dev_err(&pdev->dev,
+ "Failed to register RTC device -> %d\n", ret);
+ goto err_return;
+ }
+
+ ret = devm_request_irq(&pdev->dev, vt8500_rtc->irq_alarm,
+ vt8500_rtc_irq, 0, "rtc alarm", vt8500_rtc);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "can't get irq %i, err %d\n",
+ vt8500_rtc->irq_alarm, ret);
+ goto err_return;
+ }
+
+ return 0;
+
+err_return:
+ return ret;
+}
+
+static int vt8500_rtc_remove(struct platform_device *pdev)
+{
+ struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);
+
+ /* Disable alarm matching */
+ writel(0, vt8500_rtc->regbase + VT8500_RTC_IS);
+
+ return 0;
+}
+
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-rtc", },
+ {}
+};
+
+static struct platform_driver vt8500_rtc_driver = {
+ .probe = vt8500_rtc_probe,
+ .remove = vt8500_rtc_remove,
+ .driver = {
+ .name = "vt8500-rtc",
+ .of_match_table = wmt_dt_ids,
+ },
+};
+
+module_platform_driver(vt8500_rtc_driver);
+
+MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
+MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:vt8500-rtc");
diff --git a/drivers/rtc/rtc-wm831x.c b/drivers/rtc/rtc-wm831x.c
new file mode 100644
index 000000000..75aea4c4d
--- /dev/null
+++ b/drivers/rtc/rtc-wm831x.c
@@ -0,0 +1,487 @@
+/*
+ * Real Time Clock driver for Wolfson Microelectronics WM831x
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/mfd/wm831x/core.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+
+/*
+ * R16416 (0x4020) - RTC Write Counter
+ */
+#define WM831X_RTC_WR_CNT_MASK 0xFFFF /* RTC_WR_CNT - [15:0] */
+#define WM831X_RTC_WR_CNT_SHIFT 0 /* RTC_WR_CNT - [15:0] */
+#define WM831X_RTC_WR_CNT_WIDTH 16 /* RTC_WR_CNT - [15:0] */
+
+/*
+ * R16417 (0x4021) - RTC Time 1
+ */
+#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */
+
+/*
+ * R16418 (0x4022) - RTC Time 2
+ */
+#define WM831X_RTC_TIME_MASK 0xFFFF /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_SHIFT 0 /* RTC_TIME - [15:0] */
+#define WM831X_RTC_TIME_WIDTH 16 /* RTC_TIME - [15:0] */
+
+/*
+ * R16419 (0x4023) - RTC Alarm 1
+ */
+#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */
+
+/*
+ * R16420 (0x4024) - RTC Alarm 2
+ */
+#define WM831X_RTC_ALM_MASK 0xFFFF /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_SHIFT 0 /* RTC_ALM - [15:0] */
+#define WM831X_RTC_ALM_WIDTH 16 /* RTC_ALM - [15:0] */
+
+/*
+ * R16421 (0x4025) - RTC Control
+ */
+#define WM831X_RTC_VALID 0x8000 /* RTC_VALID */
+#define WM831X_RTC_VALID_MASK 0x8000 /* RTC_VALID */
+#define WM831X_RTC_VALID_SHIFT 15 /* RTC_VALID */
+#define WM831X_RTC_VALID_WIDTH 1 /* RTC_VALID */
+#define WM831X_RTC_SYNC_BUSY 0x4000 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_SYNC_BUSY_MASK 0x4000 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_SYNC_BUSY_SHIFT 14 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_SYNC_BUSY_WIDTH 1 /* RTC_SYNC_BUSY */
+#define WM831X_RTC_ALM_ENA 0x0400 /* RTC_ALM_ENA */
+#define WM831X_RTC_ALM_ENA_MASK 0x0400 /* RTC_ALM_ENA */
+#define WM831X_RTC_ALM_ENA_SHIFT 10 /* RTC_ALM_ENA */
+#define WM831X_RTC_ALM_ENA_WIDTH 1 /* RTC_ALM_ENA */
+#define WM831X_RTC_PINT_FREQ_MASK 0x0070 /* RTC_PINT_FREQ - [6:4] */
+#define WM831X_RTC_PINT_FREQ_SHIFT 4 /* RTC_PINT_FREQ - [6:4] */
+#define WM831X_RTC_PINT_FREQ_WIDTH 3 /* RTC_PINT_FREQ - [6:4] */
+
+/*
+ * R16422 (0x4026) - RTC Trim
+ */
+#define WM831X_RTC_TRIM_MASK 0x03FF /* RTC_TRIM - [9:0] */
+#define WM831X_RTC_TRIM_SHIFT 0 /* RTC_TRIM - [9:0] */
+#define WM831X_RTC_TRIM_WIDTH 10 /* RTC_TRIM - [9:0] */
+
+#define WM831X_SET_TIME_RETRIES 5
+#define WM831X_GET_TIME_RETRIES 5
+
+struct wm831x_rtc {
+ struct wm831x *wm831x;
+ struct rtc_device *rtc;
+ unsigned int alarm_enabled:1;
+};
+
+static void wm831x_rtc_add_randomness(struct wm831x *wm831x)
+{
+ int ret;
+ u16 reg;
+
+ /*
+ * The write counter contains a pseudo-random number which is
+ * regenerated every time we set the RTC so it should be a
+ * useful per-system source of entropy.
+ */
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_WRITE_COUNTER);
+ if (ret >= 0) {
+ reg = ret;
+ add_device_randomness(&reg, sizeof(reg));
+ } else {
+ dev_warn(wm831x->dev, "Failed to read RTC write counter: %d\n",
+ ret);
+ }
+}
+
+/*
+ * Read current time and date in RTC
+ */
+static int wm831x_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ struct wm831x *wm831x = wm831x_rtc->wm831x;
+ u16 time1[2], time2[2];
+ int ret;
+ int count = 0;
+
+ /* Has the RTC been programmed? */
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read RTC control: %d\n", ret);
+ return ret;
+ }
+ if (!(ret & WM831X_RTC_VALID)) {
+ dev_dbg(dev, "RTC not yet configured\n");
+ return -EINVAL;
+ }
+
+ /* Read twice to make sure we don't read a corrupt, partially
+ * incremented, value.
+ */
+ do {
+ ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
+ 2, time1);
+ if (ret != 0)
+ continue;
+
+ ret = wm831x_bulk_read(wm831x, WM831X_RTC_TIME_1,
+ 2, time2);
+ if (ret != 0)
+ continue;
+
+ if (memcmp(time1, time2, sizeof(time1)) == 0) {
+ u32 time = (time1[0] << 16) | time1[1];
+
+ rtc_time_to_tm(time, tm);
+ return rtc_valid_tm(tm);
+ }
+
+ } while (++count < WM831X_GET_TIME_RETRIES);
+
+ dev_err(dev, "Timed out reading current time\n");
+
+ return -EIO;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int wm831x_rtc_set_mmss(struct device *dev, unsigned long time)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ struct wm831x *wm831x = wm831x_rtc->wm831x;
+ struct rtc_time new_tm;
+ unsigned long new_time;
+ int ret;
+ int count = 0;
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_1,
+ (time >> 16) & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write TIME_1: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_TIME_2, time & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write TIME_2: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for the update to complete - should happen first time
+ * round but be conservative.
+ */
+ do {
+ msleep(1);
+
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0)
+ ret = WM831X_RTC_SYNC_BUSY;
+ } while (!(ret & WM831X_RTC_SYNC_BUSY) &&
+ ++count < WM831X_SET_TIME_RETRIES);
+
+ if (ret & WM831X_RTC_SYNC_BUSY) {
+ dev_err(dev, "Timed out writing RTC update\n");
+ return -EIO;
+ }
+
+ /* Check that the update was accepted; security features may
+ * have caused the update to be ignored.
+ */
+ ret = wm831x_rtc_readtime(dev, &new_tm);
+ if (ret < 0)
+ return ret;
+
+ ret = rtc_tm_to_time(&new_tm, &new_time);
+ if (ret < 0) {
+ dev_err(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ /* Allow a second of change in case of tick */
+ if (new_time - time > 1) {
+ dev_err(dev, "RTC update not permitted by hardware\n");
+ return -EPERM;
+ }
+
+ return 0;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int wm831x_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ int ret;
+ u16 data[2];
+ u32 time;
+
+ ret = wm831x_bulk_read(wm831x_rtc->wm831x, WM831X_RTC_ALARM_1,
+ 2, data);
+ if (ret != 0) {
+ dev_err(dev, "Failed to read alarm time: %d\n", ret);
+ return ret;
+ }
+
+ time = (data[0] << 16) | data[1];
+
+ rtc_time_to_tm(time, &alrm->time);
+
+ ret = wm831x_reg_read(wm831x_rtc->wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read RTC control: %d\n", ret);
+ return ret;
+ }
+
+ if (ret & WM831X_RTC_ALM_ENA)
+ alrm->enabled = 1;
+ else
+ alrm->enabled = 0;
+
+ return 0;
+}
+
+static int wm831x_rtc_stop_alarm(struct wm831x_rtc *wm831x_rtc)
+{
+ wm831x_rtc->alarm_enabled = 0;
+
+ return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, 0);
+}
+
+static int wm831x_rtc_start_alarm(struct wm831x_rtc *wm831x_rtc)
+{
+ wm831x_rtc->alarm_enabled = 1;
+
+ return wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, WM831X_RTC_ALM_ENA);
+}
+
+static int wm831x_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+ struct wm831x *wm831x = wm831x_rtc->wm831x;
+ int ret;
+ unsigned long time;
+
+ ret = rtc_tm_to_time(&alrm->time, &time);
+ if (ret < 0) {
+ dev_err(dev, "Failed to convert time: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_rtc_stop_alarm(wm831x_rtc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to stop alarm: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_1,
+ (time >> 16) & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write ALARM_1: %d\n", ret);
+ return ret;
+ }
+
+ ret = wm831x_reg_write(wm831x, WM831X_RTC_ALARM_2, time & 0xffff);
+ if (ret < 0) {
+ dev_err(dev, "Failed to write ALARM_2: %d\n", ret);
+ return ret;
+ }
+
+ if (alrm->enabled) {
+ ret = wm831x_rtc_start_alarm(wm831x_rtc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to start alarm: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int wm831x_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(dev);
+
+ if (enabled)
+ return wm831x_rtc_start_alarm(wm831x_rtc);
+ else
+ return wm831x_rtc_stop_alarm(wm831x_rtc);
+}
+
+static irqreturn_t wm831x_alm_irq(int irq, void *data)
+{
+ struct wm831x_rtc *wm831x_rtc = data;
+
+ rtc_update_irq(wm831x_rtc->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops wm831x_rtc_ops = {
+ .read_time = wm831x_rtc_readtime,
+ .set_mmss = wm831x_rtc_set_mmss,
+ .read_alarm = wm831x_rtc_readalarm,
+ .set_alarm = wm831x_rtc_setalarm,
+ .alarm_irq_enable = wm831x_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM
+/* Turn off the alarm if it should not be a wake source. */
+static int wm831x_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ int ret, enable;
+
+ if (wm831x_rtc->alarm_enabled && device_may_wakeup(&pdev->dev))
+ enable = WM831X_RTC_ALM_ENA;
+ else
+ enable = 0;
+
+ ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, enable);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Failed to update RTC alarm: %d\n", ret);
+
+ return 0;
+}
+
+/* Enable the alarm if it should be enabled (in case it was disabled to
+ * prevent use as a wake source).
+ */
+static int wm831x_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (wm831x_rtc->alarm_enabled) {
+ ret = wm831x_rtc_start_alarm(wm831x_rtc);
+ if (ret != 0)
+ dev_err(&pdev->dev,
+ "Failed to restart RTC alarm: %d\n", ret);
+ }
+
+ return 0;
+}
+
+/* Unconditionally disable the alarm */
+static int wm831x_rtc_freeze(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm831x_rtc *wm831x_rtc = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ ret = wm831x_set_bits(wm831x_rtc->wm831x, WM831X_RTC_CONTROL,
+ WM831X_RTC_ALM_ENA, 0);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", ret);
+
+ return 0;
+}
+#else
+#define wm831x_rtc_suspend NULL
+#define wm831x_rtc_resume NULL
+#define wm831x_rtc_freeze NULL
+#endif
+
+static int wm831x_rtc_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_rtc *wm831x_rtc;
+ int alm_irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "ALM"));
+ int ret = 0;
+
+ wm831x_rtc = devm_kzalloc(&pdev->dev, sizeof(*wm831x_rtc), GFP_KERNEL);
+ if (wm831x_rtc == NULL)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, wm831x_rtc);
+ wm831x_rtc->wm831x = wm831x;
+
+ ret = wm831x_reg_read(wm831x, WM831X_RTC_CONTROL);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to read RTC control: %d\n", ret);
+ goto err;
+ }
+ if (ret & WM831X_RTC_ALM_ENA)
+ wm831x_rtc->alarm_enabled = 1;
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ wm831x_rtc->rtc = devm_rtc_device_register(&pdev->dev, "wm831x",
+ &wm831x_rtc_ops, THIS_MODULE);
+ if (IS_ERR(wm831x_rtc->rtc)) {
+ ret = PTR_ERR(wm831x_rtc->rtc);
+ goto err;
+ }
+
+ ret = devm_request_threaded_irq(&pdev->dev, alm_irq, NULL,
+ wm831x_alm_irq,
+ IRQF_TRIGGER_RISING, "RTC alarm",
+ wm831x_rtc);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",
+ alm_irq, ret);
+ }
+
+ wm831x_rtc_add_randomness(wm831x);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static const struct dev_pm_ops wm831x_rtc_pm_ops = {
+ .suspend = wm831x_rtc_suspend,
+ .resume = wm831x_rtc_resume,
+
+ .freeze = wm831x_rtc_freeze,
+ .thaw = wm831x_rtc_resume,
+ .restore = wm831x_rtc_resume,
+
+ .poweroff = wm831x_rtc_suspend,
+};
+
+static struct platform_driver wm831x_rtc_driver = {
+ .probe = wm831x_rtc_probe,
+ .driver = {
+ .name = "wm831x-rtc",
+ .pm = &wm831x_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(wm831x_rtc_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("RTC driver for the WM831x series PMICs");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm831x-rtc");
diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c
new file mode 100644
index 000000000..fa247deb9
--- /dev/null
+++ b/drivers/rtc/rtc-wm8350.c
@@ -0,0 +1,485 @@
+/*
+ * Real Time Clock driver for Wolfson Microelectronics WM8350
+ *
+ * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood
+ * linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/completion.h>
+#include <linux/mfd/wm8350/rtc.h>
+#include <linux/mfd/wm8350/core.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#define WM8350_SET_ALM_RETRIES 5
+#define WM8350_SET_TIME_RETRIES 5
+#define WM8350_GET_TIME_RETRIES 5
+
+#define to_wm8350_from_rtc_dev(d) container_of(d, struct wm8350, rtc.pdev.dev)
+
+/*
+ * Read current time and date in RTC
+ */
+static int wm8350_rtc_readtime(struct device *dev, struct rtc_time *tm)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ u16 time1[4], time2[4];
+ int retries = WM8350_GET_TIME_RETRIES, ret;
+
+ /*
+ * Read the time twice and compare.
+ * If time1 == time2, then time is valid else retry.
+ */
+ do {
+ ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES,
+ 4, time1);
+ if (ret < 0)
+ return ret;
+ ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES,
+ 4, time2);
+ if (ret < 0)
+ return ret;
+
+ if (memcmp(time1, time2, sizeof(time1)) == 0) {
+ tm->tm_sec = time1[0] & WM8350_RTC_SECS_MASK;
+
+ tm->tm_min = (time1[0] & WM8350_RTC_MINS_MASK)
+ >> WM8350_RTC_MINS_SHIFT;
+
+ tm->tm_hour = time1[1] & WM8350_RTC_HRS_MASK;
+
+ tm->tm_wday = ((time1[1] >> WM8350_RTC_DAY_SHIFT)
+ & 0x7) - 1;
+
+ tm->tm_mon = ((time1[2] & WM8350_RTC_MTH_MASK)
+ >> WM8350_RTC_MTH_SHIFT) - 1;
+
+ tm->tm_mday = (time1[2] & WM8350_RTC_DATE_MASK);
+
+ tm->tm_year = ((time1[3] & WM8350_RTC_YHUNDREDS_MASK)
+ >> WM8350_RTC_YHUNDREDS_SHIFT) * 100;
+ tm->tm_year += time1[3] & WM8350_RTC_YUNITS_MASK;
+
+ tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon,
+ tm->tm_year);
+ tm->tm_year -= 1900;
+
+ dev_dbg(dev, "Read (%d left): %04x %04x %04x %04x\n",
+ retries,
+ time1[0], time1[1], time1[2], time1[3]);
+
+ return 0;
+ }
+ } while (retries--);
+
+ dev_err(dev, "timed out reading RTC time\n");
+ return -EIO;
+}
+
+/*
+ * Set current time and date in RTC
+ */
+static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ u16 time[4];
+ u16 rtc_ctrl;
+ int ret, retries = WM8350_SET_TIME_RETRIES;
+
+ time[0] = tm->tm_sec;
+ time[0] |= tm->tm_min << WM8350_RTC_MINS_SHIFT;
+ time[1] = tm->tm_hour;
+ time[1] |= (tm->tm_wday + 1) << WM8350_RTC_DAY_SHIFT;
+ time[2] = tm->tm_mday;
+ time[2] |= (tm->tm_mon + 1) << WM8350_RTC_MTH_SHIFT;
+ time[3] = ((tm->tm_year + 1900) / 100) << WM8350_RTC_YHUNDREDS_SHIFT;
+ time[3] |= (tm->tm_year + 1900) % 100;
+
+ dev_dbg(dev, "Setting: %04x %04x %04x %04x\n",
+ time[0], time[1], time[2], time[3]);
+
+ /* Set RTC_SET to stop the clock */
+ ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, WM8350_RTC_SET);
+ if (ret < 0)
+ return ret;
+
+ /* Wait until confirmation of stopping */
+ do {
+ rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(1));
+ } while (--retries && !(rtc_ctrl & WM8350_RTC_STS));
+
+ if (!retries) {
+ dev_err(dev, "timed out on set confirmation\n");
+ return -EIO;
+ }
+
+ /* Write time to RTC */
+ ret = wm8350_block_write(wm8350, WM8350_RTC_SECONDS_MINUTES, 4, time);
+ if (ret < 0)
+ return ret;
+
+ /* Clear RTC_SET to start the clock */
+ ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+ WM8350_RTC_SET);
+ return ret;
+}
+
+/*
+ * Read alarm time and date in RTC
+ */
+static int wm8350_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ struct rtc_time *tm = &alrm->time;
+ u16 time[4];
+ int ret;
+
+ ret = wm8350_block_read(wm8350, WM8350_ALARM_SECONDS_MINUTES, 4, time);
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = time[0] & WM8350_RTC_ALMSECS_MASK;
+ if (tm->tm_sec == WM8350_RTC_ALMSECS_MASK)
+ tm->tm_sec = -1;
+
+ tm->tm_min = time[0] & WM8350_RTC_ALMMINS_MASK;
+ if (tm->tm_min == WM8350_RTC_ALMMINS_MASK)
+ tm->tm_min = -1;
+ else
+ tm->tm_min >>= WM8350_RTC_ALMMINS_SHIFT;
+
+ tm->tm_hour = time[1] & WM8350_RTC_ALMHRS_MASK;
+ if (tm->tm_hour == WM8350_RTC_ALMHRS_MASK)
+ tm->tm_hour = -1;
+
+ tm->tm_wday = ((time[1] >> WM8350_RTC_ALMDAY_SHIFT) & 0x7) - 1;
+ if (tm->tm_wday > 7)
+ tm->tm_wday = -1;
+
+ tm->tm_mon = time[2] & WM8350_RTC_ALMMTH_MASK;
+ if (tm->tm_mon == WM8350_RTC_ALMMTH_MASK)
+ tm->tm_mon = -1;
+ else
+ tm->tm_mon = (tm->tm_mon >> WM8350_RTC_ALMMTH_SHIFT) - 1;
+
+ tm->tm_mday = (time[2] & WM8350_RTC_ALMDATE_MASK);
+ if (tm->tm_mday == WM8350_RTC_ALMDATE_MASK)
+ tm->tm_mday = -1;
+
+ tm->tm_year = -1;
+
+ alrm->enabled = !(time[3] & WM8350_RTC_ALMSTS);
+
+ return 0;
+}
+
+static int wm8350_rtc_stop_alarm(struct wm8350 *wm8350)
+{
+ int retries = WM8350_SET_ALM_RETRIES;
+ u16 rtc_ctrl;
+ int ret;
+
+ /* Set RTC_SET to stop the clock */
+ ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+ WM8350_RTC_ALMSET);
+ if (ret < 0)
+ return ret;
+
+ /* Wait until confirmation of stopping */
+ do {
+ rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(1));
+ } while (retries-- && !(rtc_ctrl & WM8350_RTC_ALMSTS));
+
+ if (!(rtc_ctrl & WM8350_RTC_ALMSTS))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int wm8350_rtc_start_alarm(struct wm8350 *wm8350)
+{
+ int ret;
+ int retries = WM8350_SET_ALM_RETRIES;
+ u16 rtc_ctrl;
+
+ ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+ WM8350_RTC_ALMSET);
+ if (ret < 0)
+ return ret;
+
+ /* Wait until confirmation */
+ do {
+ rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
+ schedule_timeout_uninterruptible(msecs_to_jiffies(1));
+ } while (retries-- && rtc_ctrl & WM8350_RTC_ALMSTS);
+
+ if (rtc_ctrl & WM8350_RTC_ALMSTS)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int wm8350_rtc_alarm_irq_enable(struct device *dev,
+ unsigned int enabled)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+
+ if (enabled)
+ return wm8350_rtc_start_alarm(wm8350);
+ else
+ return wm8350_rtc_stop_alarm(wm8350);
+}
+
+static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct wm8350 *wm8350 = dev_get_drvdata(dev);
+ struct rtc_time *tm = &alrm->time;
+ u16 time[3];
+ int ret;
+
+ memset(time, 0, sizeof(time));
+
+ if (tm->tm_sec != -1)
+ time[0] |= tm->tm_sec;
+ else
+ time[0] |= WM8350_RTC_ALMSECS_MASK;
+
+ if (tm->tm_min != -1)
+ time[0] |= tm->tm_min << WM8350_RTC_ALMMINS_SHIFT;
+ else
+ time[0] |= WM8350_RTC_ALMMINS_MASK;
+
+ if (tm->tm_hour != -1)
+ time[1] |= tm->tm_hour;
+ else
+ time[1] |= WM8350_RTC_ALMHRS_MASK;
+
+ if (tm->tm_wday != -1)
+ time[1] |= (tm->tm_wday + 1) << WM8350_RTC_ALMDAY_SHIFT;
+ else
+ time[1] |= WM8350_RTC_ALMDAY_MASK;
+
+ if (tm->tm_mday != -1)
+ time[2] |= tm->tm_mday;
+ else
+ time[2] |= WM8350_RTC_ALMDATE_MASK;
+
+ if (tm->tm_mon != -1)
+ time[2] |= (tm->tm_mon + 1) << WM8350_RTC_ALMMTH_SHIFT;
+ else
+ time[2] |= WM8350_RTC_ALMMTH_MASK;
+
+ ret = wm8350_rtc_stop_alarm(wm8350);
+ if (ret < 0)
+ return ret;
+
+ /* Write time to RTC */
+ ret = wm8350_block_write(wm8350, WM8350_ALARM_SECONDS_MINUTES,
+ 3, time);
+ if (ret < 0)
+ return ret;
+
+ if (alrm->enabled)
+ ret = wm8350_rtc_start_alarm(wm8350);
+
+ return ret;
+}
+
+static irqreturn_t wm8350_rtc_alarm_handler(int irq, void *data)
+{
+ struct wm8350 *wm8350 = data;
+ struct rtc_device *rtc = wm8350->rtc.rtc;
+ int ret;
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
+
+ /* Make it one shot */
+ ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+ WM8350_RTC_ALMSET);
+ if (ret != 0) {
+ dev_err(&(wm8350->rtc.pdev->dev),
+ "Failed to disable alarm: %d\n", ret);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8350_rtc_update_handler(int irq, void *data)
+{
+ struct wm8350 *wm8350 = data;
+ struct rtc_device *rtc = wm8350->rtc.rtc;
+
+ rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF);
+
+ return IRQ_HANDLED;
+}
+
+static const struct rtc_class_ops wm8350_rtc_ops = {
+ .read_time = wm8350_rtc_readtime,
+ .set_time = wm8350_rtc_settime,
+ .read_alarm = wm8350_rtc_readalarm,
+ .set_alarm = wm8350_rtc_setalarm,
+ .alarm_irq_enable = wm8350_rtc_alarm_irq_enable,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int wm8350_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+ int ret = 0;
+ u16 reg;
+
+ reg = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
+
+ if (device_may_wakeup(&wm8350->rtc.pdev->dev) &&
+ reg & WM8350_RTC_ALMSTS) {
+ ret = wm8350_rtc_stop_alarm(wm8350);
+ if (ret != 0)
+ dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n",
+ ret);
+ }
+
+ return ret;
+}
+
+static int wm8350_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev);
+ int ret;
+
+ if (wm8350->rtc.alarm_enabled) {
+ ret = wm8350_rtc_start_alarm(wm8350);
+ if (ret != 0)
+ dev_err(&pdev->dev,
+ "Failed to restart RTC alarm: %d\n", ret);
+ }
+
+ return 0;
+}
+#endif
+
+static int wm8350_rtc_probe(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+ struct wm8350_rtc *wm_rtc = &wm8350->rtc;
+ int ret = 0;
+ u16 timectl, power5;
+
+ timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL);
+ if (timectl & WM8350_RTC_BCD) {
+ dev_err(&pdev->dev, "RTC BCD mode not supported\n");
+ return -EINVAL;
+ }
+ if (timectl & WM8350_RTC_12HR) {
+ dev_err(&pdev->dev, "RTC 12 hour mode not supported\n");
+ return -EINVAL;
+ }
+
+ /* enable the RTC if it's not already enabled */
+ power5 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5);
+ if (!(power5 & WM8350_RTC_TICK_ENA)) {
+ dev_info(wm8350->dev, "Starting RTC\n");
+
+ wm8350_reg_unlock(wm8350);
+
+ ret = wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5,
+ WM8350_RTC_TICK_ENA);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to enable RTC: %d\n", ret);
+ return ret;
+ }
+
+ wm8350_reg_lock(wm8350);
+ }
+
+ if (timectl & WM8350_RTC_STS) {
+ int retries;
+
+ ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL,
+ WM8350_RTC_SET);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to start: %d\n", ret);
+ return ret;
+ }
+
+ retries = WM8350_SET_TIME_RETRIES;
+ do {
+ timectl = wm8350_reg_read(wm8350,
+ WM8350_RTC_TIME_CONTROL);
+ } while (timectl & WM8350_RTC_STS && --retries);
+
+ if (retries == 0) {
+ dev_err(&pdev->dev, "failed to start: timeout\n");
+ return -ENODEV;
+ }
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ wm_rtc->rtc = devm_rtc_device_register(&pdev->dev, "wm8350",
+ &wm8350_rtc_ops, THIS_MODULE);
+ if (IS_ERR(wm_rtc->rtc)) {
+ ret = PTR_ERR(wm_rtc->rtc);
+ dev_err(&pdev->dev, "failed to register RTC: %d\n", ret);
+ return ret;
+ }
+
+ wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC,
+ wm8350_rtc_update_handler, 0,
+ "RTC Seconds", wm8350);
+ wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC);
+
+ wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM,
+ wm8350_rtc_alarm_handler, 0,
+ "RTC Alarm", wm8350);
+
+ return 0;
+}
+
+static int wm8350_rtc_remove(struct platform_device *pdev)
+{
+ struct wm8350 *wm8350 = platform_get_drvdata(pdev);
+
+ wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350);
+ wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM, wm8350);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(wm8350_rtc_pm_ops, wm8350_rtc_suspend,
+ wm8350_rtc_resume);
+
+static struct platform_driver wm8350_rtc_driver = {
+ .probe = wm8350_rtc_probe,
+ .remove = wm8350_rtc_remove,
+ .driver = {
+ .name = "wm8350-rtc",
+ .pm = &wm8350_rtc_pm_ops,
+ },
+};
+
+module_platform_driver(wm8350_rtc_driver);
+
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("RTC driver for the WM8350");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8350-rtc");
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
new file mode 100644
index 000000000..5638b7ba8
--- /dev/null
+++ b/drivers/rtc/rtc-x1205.c
@@ -0,0 +1,696 @@
+/*
+ * An i2c driver for the Xicor/Intersil X1205 RTC
+ * Copyright 2004 Karen Spearel
+ * Copyright 2005 Alessandro Zummo
+ *
+ * please send all reports to:
+ * Karen Spearel <kas111 at gmail dot com>
+ * Alessandro Zummo <a.zummo@towertech.it>
+ *
+ * based on a lot of other RTC drivers.
+ *
+ * Information and datasheet:
+ * http://www.intersil.com/cda/deviceinfo/0,1477,X1205,00.html
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+
+#define DRV_VERSION "1.0.8"
+
+/* offsets into CCR area */
+
+#define CCR_SEC 0
+#define CCR_MIN 1
+#define CCR_HOUR 2
+#define CCR_MDAY 3
+#define CCR_MONTH 4
+#define CCR_YEAR 5
+#define CCR_WDAY 6
+#define CCR_Y2K 7
+
+#define X1205_REG_SR 0x3F /* status register */
+#define X1205_REG_Y2K 0x37
+#define X1205_REG_DW 0x36
+#define X1205_REG_YR 0x35
+#define X1205_REG_MO 0x34
+#define X1205_REG_DT 0x33
+#define X1205_REG_HR 0x32
+#define X1205_REG_MN 0x31
+#define X1205_REG_SC 0x30
+#define X1205_REG_DTR 0x13
+#define X1205_REG_ATR 0x12
+#define X1205_REG_INT 0x11
+#define X1205_REG_0 0x10
+#define X1205_REG_Y2K1 0x0F
+#define X1205_REG_DWA1 0x0E
+#define X1205_REG_YRA1 0x0D
+#define X1205_REG_MOA1 0x0C
+#define X1205_REG_DTA1 0x0B
+#define X1205_REG_HRA1 0x0A
+#define X1205_REG_MNA1 0x09
+#define X1205_REG_SCA1 0x08
+#define X1205_REG_Y2K0 0x07
+#define X1205_REG_DWA0 0x06
+#define X1205_REG_YRA0 0x05
+#define X1205_REG_MOA0 0x04
+#define X1205_REG_DTA0 0x03
+#define X1205_REG_HRA0 0x02
+#define X1205_REG_MNA0 0x01
+#define X1205_REG_SCA0 0x00
+
+#define X1205_CCR_BASE 0x30 /* Base address of CCR */
+#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */
+
+#define X1205_SR_RTCF 0x01 /* Clock failure */
+#define X1205_SR_WEL 0x02 /* Write Enable Latch */
+#define X1205_SR_RWEL 0x04 /* Register Write Enable */
+#define X1205_SR_AL0 0x20 /* Alarm 0 match */
+
+#define X1205_DTR_DTR0 0x01
+#define X1205_DTR_DTR1 0x02
+#define X1205_DTR_DTR2 0x04
+
+#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */
+
+#define X1205_INT_AL0E 0x20 /* Alarm 0 enable */
+
+static struct i2c_driver x1205_driver;
+
+/*
+ * In the routines that deal directly with the x1205 hardware, we use
+ * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
+ * Epoch is initialized as 2000. Time is set to UTC.
+ */
+static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
+ unsigned char reg_base)
+{
+ unsigned char dt_addr[2] = { 0, reg_base };
+ unsigned char buf[8];
+ int i;
+
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 2,
+ .buf = dt_addr
+ },
+ {/* read date */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 8,
+ .buf = buf
+ },
+ };
+
+ /* read date registers */
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(&client->dev,
+ "%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
+ "mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
+ __func__,
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7]);
+
+ /* Mask out the enable bits if these are alarm registers */
+ if (reg_base < X1205_CCR_BASE)
+ for (i = 0; i <= 4; i++)
+ buf[i] &= 0x7F;
+
+ tm->tm_sec = bcd2bin(buf[CCR_SEC]);
+ tm->tm_min = bcd2bin(buf[CCR_MIN]);
+ tm->tm_hour = bcd2bin(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
+ tm->tm_mday = bcd2bin(buf[CCR_MDAY]);
+ tm->tm_mon = bcd2bin(buf[CCR_MONTH]) - 1; /* mon is 0-11 */
+ tm->tm_year = bcd2bin(buf[CCR_YEAR])
+ + (bcd2bin(buf[CCR_Y2K]) * 100) - 1900;
+ tm->tm_wday = buf[CCR_WDAY];
+
+ dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __func__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ return 0;
+}
+
+static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
+{
+ static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
+
+ struct i2c_msg msgs[] = {
+ { /* setup read ptr */
+ .addr = client->addr,
+ .len = 2,
+ .buf = sr_addr
+ },
+ { /* read status */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = sr
+ },
+ };
+
+ /* read status register */
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
+ u8 reg_base, unsigned char alm_enable)
+{
+ int i, xfer;
+ unsigned char rdata[10] = { 0, reg_base };
+ unsigned char *buf = rdata + 2;
+
+ static const unsigned char wel[3] = { 0, X1205_REG_SR,
+ X1205_SR_WEL };
+
+ static const unsigned char rwel[3] = { 0, X1205_REG_SR,
+ X1205_SR_WEL | X1205_SR_RWEL };
+
+ static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
+
+ dev_dbg(&client->dev,
+ "%s: sec=%d min=%d hour=%d mday=%d mon=%d year=%d wday=%d\n",
+ __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday,
+ tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ buf[CCR_SEC] = bin2bcd(tm->tm_sec);
+ buf[CCR_MIN] = bin2bcd(tm->tm_min);
+
+ /* set hour and 24hr bit */
+ buf[CCR_HOUR] = bin2bcd(tm->tm_hour) | X1205_HR_MIL;
+
+ buf[CCR_MDAY] = bin2bcd(tm->tm_mday);
+
+ /* month, 1 - 12 */
+ buf[CCR_MONTH] = bin2bcd(tm->tm_mon + 1);
+
+ /* year, since the rtc epoch*/
+ buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100);
+ buf[CCR_WDAY] = tm->tm_wday & 0x07;
+ buf[CCR_Y2K] = bin2bcd((tm->tm_year + 1900) / 100);
+
+ /* If writing alarm registers, set compare bits on registers 0-4 */
+ if (reg_base < X1205_CCR_BASE)
+ for (i = 0; i <= 4; i++)
+ buf[i] |= 0x80;
+
+ /* this sequence is required to unlock the chip */
+ xfer = i2c_master_send(client, wel, 3);
+ if (xfer != 3) {
+ dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer);
+ return -EIO;
+ }
+
+ xfer = i2c_master_send(client, rwel, 3);
+ if (xfer != 3) {
+ dev_err(&client->dev, "%s: rwel - %d\n", __func__, xfer);
+ return -EIO;
+ }
+
+ xfer = i2c_master_send(client, rdata, sizeof(rdata));
+ if (xfer != sizeof(rdata)) {
+ dev_err(&client->dev,
+ "%s: result=%d addr=%02x, data=%02x\n",
+ __func__,
+ xfer, rdata[1], rdata[2]);
+ return -EIO;
+ }
+
+ /* If we wrote to the nonvolatile region, wait 10msec for write cycle*/
+ if (reg_base < X1205_CCR_BASE) {
+ unsigned char al0e[3] = { 0, X1205_REG_INT, 0 };
+
+ msleep(10);
+
+ /* ...and set or clear the AL0E bit in the INT register */
+
+ /* Need to set RWEL again as the write has cleared it */
+ xfer = i2c_master_send(client, rwel, 3);
+ if (xfer != 3) {
+ dev_err(&client->dev,
+ "%s: aloe rwel - %d\n",
+ __func__,
+ xfer);
+ return -EIO;
+ }
+
+ if (alm_enable)
+ al0e[2] = X1205_INT_AL0E;
+
+ xfer = i2c_master_send(client, al0e, 3);
+ if (xfer != 3) {
+ dev_err(&client->dev,
+ "%s: al0e - %d\n",
+ __func__,
+ xfer);
+ return -EIO;
+ }
+
+ /* and wait 10msec again for this write to complete */
+ msleep(10);
+ }
+
+ /* disable further writes */
+ xfer = i2c_master_send(client, diswe, 3);
+ if (xfer != 3) {
+ dev_err(&client->dev, "%s: diswe - %d\n", __func__, xfer);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int x1205_fix_osc(struct i2c_client *client)
+{
+ int err;
+ struct rtc_time tm;
+
+ memset(&tm, 0, sizeof(tm));
+
+ err = x1205_set_datetime(client, &tm, X1205_CCR_BASE, 0);
+ if (err < 0)
+ dev_err(&client->dev, "unable to restart the oscillator\n");
+
+ return err;
+}
+
+static int x1205_get_dtrim(struct i2c_client *client, int *trim)
+{
+ unsigned char dtr;
+ static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR };
+
+ struct i2c_msg msgs[] = {
+ { /* setup read ptr */
+ .addr = client->addr,
+ .len = 2,
+ .buf = dtr_addr
+ },
+ { /* read dtr */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &dtr
+ },
+ };
+
+ /* read dtr register */
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(&client->dev, "%s: raw dtr=%x\n", __func__, dtr);
+
+ *trim = 0;
+
+ if (dtr & X1205_DTR_DTR0)
+ *trim += 20;
+
+ if (dtr & X1205_DTR_DTR1)
+ *trim += 10;
+
+ if (dtr & X1205_DTR_DTR2)
+ *trim = -*trim;
+
+ return 0;
+}
+
+static int x1205_get_atrim(struct i2c_client *client, int *trim)
+{
+ s8 atr;
+ static unsigned char atr_addr[2] = { 0, X1205_REG_ATR };
+
+ struct i2c_msg msgs[] = {
+ {/* setup read ptr */
+ .addr = client->addr,
+ .len = 2,
+ .buf = atr_addr
+ },
+ {/* read atr */
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &atr
+ },
+ };
+
+ /* read atr register */
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(&client->dev, "%s: raw atr=%x\n", __func__, atr);
+
+ /* atr is a two's complement value on 6 bits,
+ * perform sign extension. The formula is
+ * Catr = (atr * 0.25pF) + 11.00pF.
+ */
+ atr = sign_extend32(atr, 5);
+
+ dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __func__, atr, atr);
+
+ *trim = (atr * 250) + 11000;
+
+ dev_dbg(&client->dev, "%s: real=%d\n", __func__, *trim);
+
+ return 0;
+}
+
+struct x1205_limit {
+ unsigned char reg, mask, min, max;
+};
+
+static int x1205_validate_client(struct i2c_client *client)
+{
+ int i, xfer;
+
+ /* Probe array. We will read the register at the specified
+ * address and check if the given bits are zero.
+ */
+ static const unsigned char probe_zero_pattern[] = {
+ /* register, mask */
+ X1205_REG_SR, 0x18,
+ X1205_REG_DTR, 0xF8,
+ X1205_REG_ATR, 0xC0,
+ X1205_REG_INT, 0x18,
+ X1205_REG_0, 0xFF,
+ };
+
+ static const struct x1205_limit probe_limits_pattern[] = {
+ /* register, mask, min, max */
+ { X1205_REG_Y2K, 0xFF, 19, 20 },
+ { X1205_REG_DW, 0xFF, 0, 6 },
+ { X1205_REG_YR, 0xFF, 0, 99 },
+ { X1205_REG_MO, 0xFF, 0, 12 },
+ { X1205_REG_DT, 0xFF, 0, 31 },
+ { X1205_REG_HR, 0x7F, 0, 23 },
+ { X1205_REG_MN, 0xFF, 0, 59 },
+ { X1205_REG_SC, 0xFF, 0, 59 },
+ { X1205_REG_Y2K1, 0xFF, 19, 20 },
+ { X1205_REG_Y2K0, 0xFF, 19, 20 },
+ };
+
+ /* check that registers have bits a 0 where expected */
+ for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) {
+ unsigned char buf;
+
+ unsigned char addr[2] = { 0, probe_zero_pattern[i] };
+
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .len = 2,
+ .buf = addr
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &buf
+ },
+ };
+
+ xfer = i2c_transfer(client->adapter, msgs, 2);
+ if (xfer != 2) {
+ dev_err(&client->dev,
+ "%s: could not read register %x\n",
+ __func__, probe_zero_pattern[i]);
+
+ return -EIO;
+ }
+
+ if ((buf & probe_zero_pattern[i+1]) != 0) {
+ dev_err(&client->dev,
+ "%s: register=%02x, zero pattern=%d, value=%x\n",
+ __func__, probe_zero_pattern[i], i, buf);
+
+ return -ENODEV;
+ }
+ }
+
+ /* check limits (only registers with bcd values) */
+ for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
+ unsigned char reg, value;
+
+ unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };
+
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = client->addr,
+ .len = 2,
+ .buf = addr
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &reg
+ },
+ };
+
+ xfer = i2c_transfer(client->adapter, msgs, 2);
+ if (xfer != 2) {
+ dev_err(&client->dev,
+ "%s: could not read register %x\n",
+ __func__, probe_limits_pattern[i].reg);
+
+ return -EIO;
+ }
+
+ value = bcd2bin(reg & probe_limits_pattern[i].mask);
+
+ if (value > probe_limits_pattern[i].max ||
+ value < probe_limits_pattern[i].min) {
+ dev_dbg(&client->dev,
+ "%s: register=%x, lim pattern=%d, value=%d\n",
+ __func__, probe_limits_pattern[i].reg,
+ i, value);
+
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+}
+
+static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int err;
+ unsigned char intreg, status;
+ static unsigned char int_addr[2] = { 0, X1205_REG_INT };
+ struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_msg msgs[] = {
+ { /* setup read ptr */
+ .addr = client->addr,
+ .len = 2,
+ .buf = int_addr
+ },
+ {/* read INT register */
+
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = &intreg
+ },
+ };
+
+ /* read interrupt register and status register */
+ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) {
+ dev_err(&client->dev, "%s: read error\n", __func__);
+ return -EIO;
+ }
+ err = x1205_get_status(client, &status);
+ if (err == 0) {
+ alrm->pending = (status & X1205_SR_AL0) ? 1 : 0;
+ alrm->enabled = (intreg & X1205_INT_AL0E) ? 1 : 0;
+ err = x1205_get_datetime(client, &alrm->time, X1205_ALM0_BASE);
+ }
+ return err;
+}
+
+static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ return x1205_set_datetime(to_i2c_client(dev),
+ &alrm->time, X1205_ALM0_BASE, alrm->enabled);
+}
+
+static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ return x1205_get_datetime(to_i2c_client(dev),
+ tm, X1205_CCR_BASE);
+}
+
+static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return x1205_set_datetime(to_i2c_client(dev),
+ tm, X1205_CCR_BASE, 0);
+}
+
+static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ int err, dtrim, atrim;
+
+ err = x1205_get_dtrim(to_i2c_client(dev), &dtrim);
+ if (!err)
+ seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim);
+
+ err = x1205_get_atrim(to_i2c_client(dev), &atrim);
+ if (!err)
+ seq_printf(seq, "analog_trim\t: %d.%02d pF\n",
+ atrim / 1000, atrim % 1000);
+ return 0;
+}
+
+static const struct rtc_class_ops x1205_rtc_ops = {
+ .proc = x1205_rtc_proc,
+ .read_time = x1205_rtc_read_time,
+ .set_time = x1205_rtc_set_time,
+ .read_alarm = x1205_rtc_read_alarm,
+ .set_alarm = x1205_rtc_set_alarm,
+};
+
+static ssize_t x1205_sysfs_show_atrim(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err, atrim;
+
+ err = x1205_get_atrim(to_i2c_client(dev), &atrim);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000);
+}
+static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL);
+
+static ssize_t x1205_sysfs_show_dtrim(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err, dtrim;
+
+ err = x1205_get_dtrim(to_i2c_client(dev), &dtrim);
+ if (err)
+ return err;
+
+ return sprintf(buf, "%d ppm\n", dtrim);
+}
+static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL);
+
+static int x1205_sysfs_register(struct device *dev)
+{
+ int err;
+
+ err = device_create_file(dev, &dev_attr_atrim);
+ if (err)
+ return err;
+
+ err = device_create_file(dev, &dev_attr_dtrim);
+ if (err)
+ device_remove_file(dev, &dev_attr_atrim);
+
+ return err;
+}
+
+static void x1205_sysfs_unregister(struct device *dev)
+{
+ device_remove_file(dev, &dev_attr_atrim);
+ device_remove_file(dev, &dev_attr_dtrim);
+}
+
+
+static int x1205_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ unsigned char sr;
+ struct rtc_device *rtc;
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ if (x1205_validate_client(client) < 0)
+ return -ENODEV;
+
+ dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
+
+ rtc = devm_rtc_device_register(&client->dev, x1205_driver.driver.name,
+ &x1205_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ i2c_set_clientdata(client, rtc);
+
+ /* Check for power failures and eventually enable the osc */
+ err = x1205_get_status(client, &sr);
+ if (!err) {
+ if (sr & X1205_SR_RTCF) {
+ dev_err(&client->dev,
+ "power failure detected, "
+ "please set the clock\n");
+ udelay(50);
+ x1205_fix_osc(client);
+ }
+ } else {
+ dev_err(&client->dev, "couldn't read status\n");
+ }
+
+ err = x1205_sysfs_register(&client->dev);
+ if (err)
+ dev_err(&client->dev, "Unable to create sysfs entries\n");
+
+ return 0;
+}
+
+static int x1205_remove(struct i2c_client *client)
+{
+ x1205_sysfs_unregister(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id x1205_id[] = {
+ { "x1205", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, x1205_id);
+
+static struct i2c_driver x1205_driver = {
+ .driver = {
+ .name = "rtc-x1205",
+ },
+ .probe = x1205_probe,
+ .remove = x1205_remove,
+ .id_table = x1205_id,
+};
+
+module_i2c_driver(x1205_driver);
+
+MODULE_AUTHOR(
+ "Karen Spearel <kas111 at gmail dot com>, "
+ "Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c
new file mode 100644
index 000000000..65b432a09
--- /dev/null
+++ b/drivers/rtc/rtc-xgene.c
@@ -0,0 +1,277 @@
+/*
+ * APM X-Gene SoC Real Time Clock Driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Rameshwar Prasad Sahu <rsahu@apm.com>
+ * Loc Ho <lho@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/rtc.h>
+
+/* RTC CSR Registers */
+#define RTC_CCVR 0x00
+#define RTC_CMR 0x04
+#define RTC_CLR 0x08
+#define RTC_CCR 0x0C
+#define RTC_CCR_IE BIT(0)
+#define RTC_CCR_MASK BIT(1)
+#define RTC_CCR_EN BIT(2)
+#define RTC_CCR_WEN BIT(3)
+#define RTC_STAT 0x10
+#define RTC_STAT_BIT BIT(0)
+#define RTC_RSTAT 0x14
+#define RTC_EOI 0x18
+#define RTC_VER 0x1C
+
+struct xgene_rtc_dev {
+ struct rtc_device *rtc;
+ struct device *dev;
+ unsigned long alarm_time;
+ void __iomem *csr_base;
+ struct clk *clk;
+ unsigned int irq_wake;
+};
+
+static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(readl(pdata->csr_base + RTC_CCVR), tm);
+ return rtc_valid_tm(tm);
+}
+
+static int xgene_rtc_set_mmss(struct device *dev, unsigned long secs)
+{
+ struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
+
+ /*
+ * NOTE: After the following write, the RTC_CCVR is only reflected
+ * after the update cycle of 1 seconds.
+ */
+ writel((u32) secs, pdata->csr_base + RTC_CLR);
+ readl(pdata->csr_base + RTC_CLR); /* Force a barrier */
+
+ return 0;
+}
+
+static int xgene_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
+
+ rtc_time_to_tm(pdata->alarm_time, &alrm->time);
+ alrm->enabled = readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE;
+
+ return 0;
+}
+
+static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
+{
+ struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
+ u32 ccr;
+
+ ccr = readl(pdata->csr_base + RTC_CCR);
+ if (enabled) {
+ ccr &= ~RTC_CCR_MASK;
+ ccr |= RTC_CCR_IE;
+ } else {
+ ccr &= ~RTC_CCR_IE;
+ ccr |= RTC_CCR_MASK;
+ }
+ writel(ccr, pdata->csr_base + RTC_CCR);
+
+ return 0;
+}
+
+static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
+ unsigned long rtc_time;
+ unsigned long alarm_time;
+
+ rtc_time = readl(pdata->csr_base + RTC_CCVR);
+ rtc_tm_to_time(&alrm->time, &alarm_time);
+
+ pdata->alarm_time = alarm_time;
+ writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR);
+
+ xgene_rtc_alarm_irq_enable(dev, alrm->enabled);
+
+ return 0;
+}
+
+static const struct rtc_class_ops xgene_rtc_ops = {
+ .read_time = xgene_rtc_read_time,
+ .set_mmss = xgene_rtc_set_mmss,
+ .read_alarm = xgene_rtc_read_alarm,
+ .set_alarm = xgene_rtc_set_alarm,
+ .alarm_irq_enable = xgene_rtc_alarm_irq_enable,
+};
+
+static irqreturn_t xgene_rtc_interrupt(int irq, void *id)
+{
+ struct xgene_rtc_dev *pdata = (struct xgene_rtc_dev *) id;
+
+ /* Check if interrupt asserted */
+ if (!(readl(pdata->csr_base + RTC_STAT) & RTC_STAT_BIT))
+ return IRQ_NONE;
+
+ /* Clear interrupt */
+ readl(pdata->csr_base + RTC_EOI);
+
+ rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
+
+ return IRQ_HANDLED;
+}
+
+static int xgene_rtc_probe(struct platform_device *pdev)
+{
+ struct xgene_rtc_dev *pdata;
+ struct resource *res;
+ int ret;
+ int irq;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, pdata);
+ pdata->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pdata->csr_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pdata->csr_base))
+ return PTR_ERR(pdata->csr_base);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource\n");
+ return irq;
+ }
+ ret = devm_request_irq(&pdev->dev, irq, xgene_rtc_interrupt, 0,
+ dev_name(&pdev->dev), pdata);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not request IRQ\n");
+ return ret;
+ }
+
+ pdata->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pdata->clk)) {
+ dev_err(&pdev->dev, "Couldn't get the clock for RTC\n");
+ return -ENODEV;
+ }
+ clk_prepare_enable(pdata->clk);
+
+ /* Turn on the clock and the crystal */
+ writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
+ &xgene_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pdata->rtc)) {
+ clk_disable_unprepare(pdata->clk);
+ return PTR_ERR(pdata->rtc);
+ }
+
+ /* HW does not support update faster than 1 seconds */
+ pdata->rtc->uie_unsupported = 1;
+
+ return 0;
+}
+
+static int xgene_rtc_remove(struct platform_device *pdev)
+{
+ struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
+
+ xgene_rtc_alarm_irq_enable(&pdev->dev, 0);
+ device_init_wakeup(&pdev->dev, 0);
+ clk_disable_unprepare(pdata->clk);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int xgene_rtc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (device_may_wakeup(&pdev->dev)) {
+ if (!enable_irq_wake(irq))
+ pdata->irq_wake = 1;
+ } else {
+ xgene_rtc_alarm_irq_enable(dev, 0);
+ clk_disable(pdata->clk);
+ }
+
+ return 0;
+}
+
+static int xgene_rtc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (device_may_wakeup(&pdev->dev)) {
+ if (pdata->irq_wake) {
+ disable_irq_wake(irq);
+ pdata->irq_wake = 0;
+ }
+ } else {
+ clk_enable(pdata->clk);
+ xgene_rtc_alarm_irq_enable(dev, 1);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id xgene_rtc_of_match[] = {
+ {.compatible = "apm,xgene-rtc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, xgene_rtc_of_match);
+#endif
+
+static struct platform_driver xgene_rtc_driver = {
+ .probe = xgene_rtc_probe,
+ .remove = xgene_rtc_remove,
+ .driver = {
+ .name = "xgene-rtc",
+ .pm = &xgene_rtc_pm_ops,
+ .of_match_table = of_match_ptr(xgene_rtc_of_match),
+ },
+};
+
+module_platform_driver(xgene_rtc_driver);
+
+MODULE_DESCRIPTION("APM X-Gene SoC RTC driver");
+MODULE_AUTHOR("Rameshwar Sahu <rsahu@apm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c
new file mode 100644
index 000000000..7728d5e32
--- /dev/null
+++ b/drivers/rtc/systohc.c
@@ -0,0 +1,47 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ */
+#include <linux/rtc.h>
+#include <linux/time.h>
+
+/**
+ * rtc_set_ntp_time - Save NTP synchronized time to the RTC
+ * @now: Current time of day
+ *
+ * Replacement for the NTP platform function update_persistent_clock64
+ * that stores time for later retrieval by rtc_hctosys.
+ *
+ * Returns 0 on successful RTC update, -ENODEV if a RTC update is not
+ * possible at all, and various other -errno for specific temporary failure
+ * cases.
+ *
+ * If temporary failure is indicated the caller should try again 'soon'
+ */
+int rtc_set_ntp_time(struct timespec64 now)
+{
+ struct rtc_device *rtc;
+ struct rtc_time tm;
+ int err = -ENODEV;
+
+ if (now.tv_nsec < (NSEC_PER_SEC >> 1))
+ rtc_time64_to_tm(now.tv_sec, &tm);
+ else
+ rtc_time64_to_tm(now.tv_sec + 1, &tm);
+
+ rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
+ if (rtc) {
+ /* rtc_hctosys exclusively uses UTC, so we call set_time here,
+ * not set_mmss. */
+ if (rtc->ops &&
+ (rtc->ops->set_time ||
+ rtc->ops->set_mmss64 ||
+ rtc->ops->set_mmss))
+ err = rtc_set_time(rtc, &tm);
+ rtc_class_close(rtc);
+ }
+
+ return err;
+}