diff options
Diffstat (limited to 'drivers/rtc')
170 files changed, 72729 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig new file mode 100644 index 000000000..b5845f16a --- /dev/null +++ b/drivers/rtc/Kconfig @@ -0,0 +1,1822 @@ +# +# RTC class/drivers configuration +# + +config RTC_LIB + bool + +config RTC_MC146818_LIB + bool + select RTC_LIB + +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_HCTOSYS_DEVICE + string "RTC used to set the system time" + depends on RTC_HCTOSYS + 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_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_SYSTOHC_DEVICE + string "RTC used to synchronize NTP adjustment" + depends on RTC_SYSTOHC + default RTC_HCTOSYS_DEVICE if RTC_HCTOSYS + default "rtc0" + help + The RTC device used for NTP synchronization. The main difference + between RTC_HCTOSYS_DEVICE and RTC_SYSTOHC_DEVICE is that this + one can sleep when setting time, because it runs in the workqueue + context. + +config RTC_DEBUG + bool "RTC debug support" + help + Say yes here to enable debugging support in the RTC framework + and individual RTC drivers. + +config RTC_NVMEM + bool "RTC non volatile storage support" + select NVMEM + default RTC_CLASS + help + Say yes here to add support for the non volatile (often battery + backed) storage present on RTCs. + +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" + +if I2C + +config RTC_DRV_88PM860X + tristate "Marvell 88PM860x" + depends on 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 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 + 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_AC100 + tristate "X-Powers AC100" + depends on MFD_AC100 + help + If you say yes here you get support for the real-time clock found + in X-Powers AC100 family peripheral ICs. + + This driver can also be built as a module. If so, the module + will be called rtc-ac100. + +config RTC_DRV_BRCMSTB + tristate "Broadcom STB wake-timer" + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST + default ARCH_BRCMSTB || BMIPS_GENERIC + help + If you say yes here you get support for the wake-timer found on + Broadcom STB SoCs (BCM7xxx). + + This driver can also be built as a module. If so, the module will + be called rtc-brcmstb-waketimer. + +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/41, ST M41T00, EPSON RX-8025, ISL12057" + 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, DS1341, + ST M41T00, EPSON RX-8025, Intersil ISL12057 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_DS1307_CENTURY + bool "Century bit support for rtc-ds1307" + depends on RTC_DRV_DS1307 + default n + help + The DS1307 driver suffered from a bug where it was enabling the + century bit inconditionnally but never used it when reading the time. + It made the driver unable to support dates beyond 2099. + Setting this option will add proper support for the century bit but if + the time was previously set using a kernel predating this option, + reading the date will return a date in the next century. + To solve that, you could boot a kernel without this option set, set + the RTC date and then boot a kernel with this option set. + +config RTC_DRV_DS1374 + tristate "Dallas/Maxim DS1374" + 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_HYM8563 + tristate "Haoyu Microelectronics HYM8563" + depends on 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 || COMPILE_TEST + select REGMAP_IRQ + 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 || MFD_MAX77620 || COMPILE_TEST + help + If you say yes here you will get support for the + RTC of Maxim MAX77686/MAX77620/MAX77802 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 RK805/RK808/RK818 RTC" + depends on MFD_RK808 + help + If you say yes here you will get support for the + RTC of RK805, RK808 and RK818 PMIC. + + This driver can also be built as a module. If so, the module + will be called rk808-rtc. + +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_ISL12026 + tristate "Intersil ISL12026" + depends on OF || COMPILE_TEST + help + If you say yes here you get support for the + Intersil ISL12026 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-isl12026. + +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_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_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_PCF85363 + tristate "NXP PCF85363" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the PCF85363 RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-pcf85363. + + The nvmem interface will be named pcf85363-#, where # is the + zero-based instance number. + +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_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 + depends on OF + 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_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_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_RX8010 + tristate "Epson RX8010SJ" + depends on I2C + help + If you say yes here you get support for the Epson RX8010SJ RTC + chip. + + This driver can also be built as a module. If so, the module + will be called rtc-rx8010. + +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_RV8803 + tristate "Micro Crystal RV8803, Epson RX8900" + help + If you say yes here you get support for the Micro Crystal RV8803 and + Epson RX8900 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rv8803. + +config RTC_DRV_S5M + tristate "Samsung S2M/S5M series" + depends on MFD_SEC_CORE || COMPILE_TEST + select REGMAP_IRQ + select REGMAP_I2C + 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_DS1302 + tristate "Dallas/Maxim DS1302" + depends on SPI + help + If you say yes here you get support for the Dallas DS1302 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1302. + +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 + select REGMAP_SPI + 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 supports the RTC feature and trickle charging but not + other chip features such as alarms. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1390. + +config RTC_DRV_MAX6916 + tristate "Maxim MAX6916" + help + If you say yes here you will get support for the + Maxim MAX6916 SPI RTC chip. + + 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-max6916. + +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_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_RX6110 + tristate "Epson RX-6110" + select REGMAP_SPI + help + If you say yes here you will get support for the Epson RX-6610. + + This driver can also be built as a module. If so the module + will be called rtc-rx6110. + +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_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_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_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 + +# +# Helper to resolve issues with configs that have SPI enabled but I2C +# modular. See SND_SOC_I2C_AND_SPI for more information +# +config RTC_I2C_AND_SPI + tristate + default m if I2C=m + default y if I2C=y + default y if SPI_MASTER=y + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + +comment "SPI and I2C RTC drivers" + +config RTC_DRV_DS3232 + tristate "Dallas/Maxim DS3232/DS3234" + depends on RTC_I2C_AND_SPI + help + If you say yes here you get support for Dallas Semiconductor + DS3232 and DS3234 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_DS3232_HWMON + bool "HWMON support for Dallas/Maxim DS3232/DS3234" + depends on RTC_DRV_DS3232 && HWMON && !(RTC_DRV_DS3232=y && HWMON=m) + default y + help + Say Y here if you want to expose temperature sensor data on + rtc-ds3232 + +config RTC_DRV_PCF2127 + tristate "NXP PCF2127" + depends on RTC_I2C_AND_SPI + 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_RV3029C2 + tristate "Micro Crystal RV3029/3049" + depends on RTC_I2C_AND_SPI + help + If you say yes here you get support for the Micro Crystal + RV3029 and RV3049 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rv3029c2. + +config RTC_DRV_RV3029_HWMON + bool "HWMON support for RV3029/3049" + depends on RTC_DRV_RV3029C2 && HWMON + depends on !(RTC_DRV_RV3029C2=y && HWMON=m) + default y + help + Say Y here if you want to expose temperature sensor data on + rtc-rv3029. + +comment "Platform RTC drivers" + +# this 'CMOS' RTC driver is arch dependent because it 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 || PPC || MIPS || SPARC64 + default y if X86 + select RTC_MC146818_LIB + 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 + select RTC_MC146818_LIB + 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_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_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/DA9062 RTC" + depends on MFD_DA9063 || MFD_DA9062 + help + If you say yes here you will get support for the RTC subsystem + for the Dialog Semiconductor PMIC chips DA9063 and DA9062. + + 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_SC27XX + tristate "Spreadtrum SC27xx RTC" + depends on MFD_SC27XX_PMIC || COMPILE_TEST + help + If you say Y here you will get support for the RTC subsystem + of the Spreadtrum SC27xx series PMICs. The SC27xx series PMICs + includes the SC2720, SC2721, SC2723, SC2730 and SC2731 chips. + + This driver can also be built as a module. If so, the module + will be called rtc-sc27xx. + +config RTC_DRV_SPEAR + tristate "SPEAR ST RTC" + depends on PLAT_SPEAR || COMPILE_TEST + 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 || COMPILE_TEST + 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. + +config RTC_DRV_ZYNQMP + tristate "Xilinx Zynq Ultrascale+ MPSoC RTC" + depends on OF + help + If you say yes here you get support for the RTC controller found on + Xilinx Zynq Ultrascale+ MPSoC. + +config RTC_DRV_CROS_EC + tristate "Chrome OS EC RTC driver" + depends on MFD_CROS_EC + help + If you say yes here you will get support for the + Chrome OS Embedded Controller's RTC. + + This driver can also be built as a module. If so, the module + will be called rtc-cros-ec. + +comment "on-CPU RTC drivers" + +config RTC_DRV_ASM9260 + tristate "Alphascale asm9260 RTC" + depends on MACH_ASM9260 || COMPILE_TEST + help + If you say yes here you get support for the RTC on the + Alphascale asm9260 SoC. + + This driver can also be built as a module. If so, the module + will be called rtc-asm9260. + +config RTC_DRV_DAVINCI + tristate "TI DaVinci RTC" + depends on ARCH_DAVINCI_DM365 || COMPILE_TEST + 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 || COMPILE_TEST + 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 || COMPILE_TEST + depends on OF + depends on PINCTRL + select GENERIC_PINCONF + 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 || COMPILE_TEST + 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 || COMPILE_TEST + 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 || ARCH_RENESAS + help + Say Y here to enable support for the on-chip RTC found in + most SuperH processors. This RTC is also found in RZ/A SoCs. + + 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 || COMPILE_TEST + 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_AT91RM9200 + tristate "AT91RM9200 or some AT91SAM9 RTC" + depends on ARCH_AT91 || COMPILE_TEST + 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 || COMPILE_TEST + depends on HAS_IOMEM + 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_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 || COMPILE_TEST + 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 + select RTC_DRV_SA1100 + help + If you say Y here you will get access to the real time clock + built into your PXA27x or PXA3xx CPU. This RTC is actually 2 RTCs + consisting of an SA1100 compatible RTC and the extended PXA RTC. + + 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 || COMPILE_TEST + 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 + bool "Allwinner A31 RTC" + default MACH_SUN6I || MACH_SUN8I + depends on COMMON_CLK + depends on ARCH_SUNXI || COMPILE_TEST + help + If you say Y here you will get support for the RTC found in + some Allwinner SoCs like the A31 or the A64. + +config RTC_DRV_SUNXI + tristate "Allwinner sun4i/sun7i RTC" + depends on MACH_SUN4I || MACH_SUN7I || COMPILE_TEST + 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 || COMPILE_TEST + 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 || COMPILE_TEST + 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 || COMPILE_TEST + 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_FTRTC010 + tristate "Faraday Technology FTRTC010 RTC" + depends on HAS_IOMEM + default ARCH_GEMINI + help + If you say Y here you will get support for the + Faraday Technolog FTRTC010 found on e.g. Gemini SoC's. + + This driver can also be built as a module. If so, the module + will be called rtc-ftrtc010. + +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 || COMPILE_TEST + 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 || COMPILE_TEST + select STMP_DEVICE + 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_INGENIC || COMPILE_TEST + help + If you say yes here you get support for the Ingenic JZ47xx SoCs RTC + controllers. + + This driver can also be built as a module. If so, the module + will be called rtc-jz4740. + +config RTC_DRV_LPC24XX + tristate "NXP RTC for LPC178x/18xx/408x/43xx" + depends on ARCH_LPC18XX || COMPILE_TEST + depends on OF && HAS_IOMEM + help + This enables support for the NXP RTC found which can be found on + NXP LPC178x/18xx/408x/43xx devices. + + If you have one of the devices above enable this driver to use + the hardware RTC. This driver can also be built as a module. If + so, the module will be called rtc-lpc24xx. + +config RTC_DRV_LPC32XX + depends on ARCH_LPC32XX || COMPILE_TEST + tristate "NXP LPC32XX RTC" + help + This enables support for the NXP RTC in the LPC32XX + + This driver can also be built 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 || COMPILE_TEST + 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 || COMPILE_TEST + 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_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_LOONGSON32 + 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_MXC_V2 + tristate "Freescale MXC Real Time Clock for i.MX53" + depends on ARCH_MXC + help + If you say yes here you get support for the Freescale MXC + SRTC module in i.MX53 processor. + + This driver can also be built as a module, if so, the module + will be called "rtc-mxc_v2". + +config RTC_DRV_SNVS + tristate "Freescale SNVS RTC support" + select REGMAP_MMIO + 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_ST_LPC + tristate "STMicroelectronics LPC RTC" + depends on ARCH_STI + depends on OF + help + Say Y here to include STMicroelectronics Low Power Controller + (LPC) based RTC support. + + To compile this driver as a module, choose M here: the + module will be called rtc-st-lpc. + +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_MT6397 + tristate "MediaTek PMIC based RTC" + depends on MFD_MT6397 || (COMPILE_TEST && IRQ_DOMAIN) + help + This selects the MediaTek(R) RTC driver. RTC is part of MediaTek + MT6397 PMIC. You should enable MT6397 PMIC MFD before select + MediaTek(R) RTC driver. + + If you want to use MediaTek(R) RTC interface, select Y or M here. + +config RTC_DRV_MT7622 + tristate "MediaTek SoC based RTC" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + This enables support for the real time clock built in the MediaTek + SoCs. + + This drive can also be built as a module. If so, the module + will be called rtc-mt7622. + +config RTC_DRV_XGENE + tristate "APM X-Gene RTC" + depends on HAS_IOMEM + depends on ARCH_XGENE || COMPILE_TEST + 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". + +config RTC_DRV_PIC32 + tristate "Microchip PIC32 RTC" + depends on MACH_PIC32 + default y + help + If you say yes here you get support for the PIC32 RTC module. + + This driver can also be built as a module. If so, the module + will be called rtc-pic32 + +config RTC_DRV_R7301 + tristate "EPSON TOYOCOM RTC-7301SF/DG" + select REGMAP_MMIO + depends on OF && HAS_IOMEM + help + If you say yes here you get support for the EPSON TOYOCOM + RTC-7301SF/DG chips. + + This driver can also be built as a module. If so, the module + will be called rtc-r7301. + +config RTC_DRV_STM32 + tristate "STM32 RTC" + select REGMAP_MMIO + depends on ARCH_STM32 || COMPILE_TEST + help + If you say yes here you get support for the STM32 On-Chip + Real Time Clock. + + This driver can also be built as a module, if so, the module + will be called "rtc-stm32". + +config RTC_DRV_CPCAP + depends on MFD_CPCAP + tristate "Motorola CPCAP RTC" + help + Say y here for CPCAP rtc found on some Motorola phones + and tablets such as Droid 4. + +config RTC_DRV_RTD119X + bool "Realtek RTD129x RTC" + depends on ARCH_REALTEK || COMPILE_TEST + default ARCH_REALTEK + help + If you say yes here, you get support for the RTD1295 SoC + Real Time Clock. + +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. + +config RTC_DRV_GOLDFISH + tristate "Goldfish Real Time Clock" + depends on MIPS && (GOLDFISH || COMPILE_TEST) + help + Say yes to enable RTC driver for the Goldfish based virtual platform. + + Goldfish is a code name for the virtual platform developed by Google + for Android emulation. + +endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile new file mode 100644 index 000000000..5ff2fc0c3 --- /dev/null +++ b/drivers/rtc/Makefile @@ -0,0 +1,178 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# 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 +obj-$(CONFIG_RTC_MC146818_LIB) += rtc-mc146818-lib.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_NVMEM) += nvmem.o +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_88PM80X) += rtc-88pm80x.o +obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.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_AC100) += rtc-ac100.o +obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o +obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o +obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.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_BRCMSTB) += rtc-brcmstb-waketimer.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_CPCAP) += rtc-cpcap.o +obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.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_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_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_FTRTC010) += rtc-ftrtc010.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_ISL12022) += rtc-isl12022.o +obj-$(CONFIG_RTC_DRV_ISL12026) += rtc-isl12026.o +obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o +obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o +obj-$(CONFIG_RTC_DRV_LP8788) += rtc-lp8788.o +obj-$(CONFIG_RTC_DRV_LPC24XX) += rtc-lpc24xx.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_MAX6900) += rtc-max6900.o +obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o +obj-$(CONFIG_RTC_DRV_MAX6916) += rtc-max6916.o +obj-$(CONFIG_RTC_DRV_MAX77686) += rtc-max77686.o +obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o +obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o +obj-$(CONFIG_RTC_DRV_MAX8997) += rtc-max8997.o +obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o +obj-$(CONFIG_RTC_DRV_MC13XXX) += rtc-mc13xxx.o +obj-$(CONFIG_RTC_DRV_MCP795) += rtc-mcp795.o +obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o +obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o +obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o +obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o +obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o +obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o +obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o +obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o +obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o +obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o +obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o +obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o +obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o +obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o +obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o +obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o +obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o +obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o +obj-$(CONFIG_RTC_DRV_PCF85363) += rtc-pcf85363.o +obj-$(CONFIG_RTC_DRV_PCF8523) += rtc-pcf8523.o +obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o +obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o +obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.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_R7301) += rtc-r7301.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_RTD119X) += rtc-rtd119x.o +obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o +obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o +obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o +obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o +obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.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_SC27XX) += rtc-sc27xx.o +obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o +obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.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_STM32) += rtc-stm32.o +obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o +obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.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_TPS6586X) += rtc-tps6586x.o +obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o +obj-$(CONFIG_RTC_DRV_TPS80031) += rtc-tps80031.o +obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.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_ZYNQMP) += rtc-zynqmp.o +obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c new file mode 100644 index 000000000..0fca4d74c --- /dev/null +++ b/drivers/rtc/class.c @@ -0,0 +1,545 @@ +/* + * 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; + } + + ktime_get_real_ts64(&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 */ + ktime_get_real_ts64(&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 + +/* Ensure the caller will set the id before releasing the device */ +static struct rtc_device *rtc_allocate_device(void) +{ + struct rtc_device *rtc; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return NULL; + + device_initialize(&rtc->dev); + + /* Drivers can revise this default after allocating the device. */ + rtc->set_offset_nsec = NSEC_PER_SEC / 2; + + rtc->irq_freq = 1; + rtc->max_user_freq = 64; + rtc->dev.class = rtc_class; + rtc->dev.groups = rtc_get_dev_attribute_groups(); + rtc->dev.release = rtc_device_release; + + mutex_init(&rtc->ops_lock); + spin_lock_init(&rtc->irq_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; + + return rtc; +} + +static int rtc_device_get_id(struct device *dev) +{ + int of_id = -1, id = -1; + + 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); + + return id; +} + +static void rtc_device_get_offset(struct rtc_device *rtc) +{ + time64_t range_secs; + u32 start_year; + int ret; + + /* + * If RTC driver did not implement the range of RTC hardware device, + * then we can not expand the RTC range by adding or subtracting one + * offset. + */ + if (rtc->range_min == rtc->range_max) + return; + + ret = device_property_read_u32(rtc->dev.parent, "start-year", + &start_year); + if (!ret) { + rtc->start_secs = mktime64(start_year, 1, 1, 0, 0, 0); + rtc->set_start_time = true; + } + + /* + * If user did not implement the start time for RTC driver, then no + * need to expand the RTC range. + */ + if (!rtc->set_start_time) + return; + + range_secs = rtc->range_max - rtc->range_min + 1; + + /* + * If the start_secs is larger than the maximum seconds (rtc->range_max) + * supported by RTC hardware or the maximum seconds of new expanded + * range (start_secs + rtc->range_max - rtc->range_min) is less than + * rtc->range_min, which means the minimum seconds (rtc->range_min) of + * RTC hardware will be mapped to start_secs by adding one offset, so + * the offset seconds calculation formula should be: + * rtc->offset_secs = rtc->start_secs - rtc->range_min; + * + * If the start_secs is larger than the minimum seconds (rtc->range_min) + * supported by RTC hardware, then there is one region is overlapped + * between the original RTC hardware range and the new expanded range, + * and this overlapped region do not need to be mapped into the new + * expanded range due to it is valid for RTC device. So the minimum + * seconds of RTC hardware (rtc->range_min) should be mapped to + * rtc->range_max + 1, then the offset seconds formula should be: + * rtc->offset_secs = rtc->range_max - rtc->range_min + 1; + * + * If the start_secs is less than the minimum seconds (rtc->range_min), + * which is similar to case 2. So the start_secs should be mapped to + * start_secs + rtc->range_max - rtc->range_min + 1, then the + * offset seconds formula should be: + * rtc->offset_secs = -(rtc->range_max - rtc->range_min + 1); + * + * Otherwise the offset seconds should be 0. + */ + if (rtc->start_secs > rtc->range_max || + rtc->start_secs + range_secs - 1 < rtc->range_min) + rtc->offset_secs = rtc->start_secs - rtc->range_min; + else if (rtc->start_secs > rtc->range_min) + rtc->offset_secs = range_secs; + else if (rtc->start_secs < rtc->range_min) + rtc->offset_secs = -range_secs; + else + rtc->offset_secs = 0; +} + +/** + * 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 id, err; + + id = rtc_device_get_id(dev); + if (id < 0) { + err = id; + goto exit; + } + + rtc = rtc_allocate_device(); + if (!rtc) { + err = -ENOMEM; + goto exit_ida; + } + + rtc->id = id; + rtc->ops = ops; + rtc->owner = owner; + rtc->dev.parent = dev; + + dev_set_name(&rtc->dev, "rtc%d", id); + + rtc_device_get_offset(rtc); + + /* 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 = cdev_device_add(&rtc->char_dev, &rtc->dev); + if (err) { + dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n", + name, MAJOR(rtc->dev.devt), rtc->id); + + /* This will free both memory and the ID */ + put_device(&rtc->dev); + goto exit; + } else { + dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", name, + MAJOR(rtc->dev.devt), rtc->id); + } + + rtc_proc_add_device(rtc); + + dev_info(dev, "rtc core: registered %s as %s\n", + name, dev_name(&rtc->dev)); + + return 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) +{ + mutex_lock(&rtc->ops_lock); + /* + * Remove innards of this RTC, then disable it, before + * letting any rtc_class_open() users access it again + */ + rtc_proc_del_device(rtc); + cdev_device_del(&rtc->char_dev, &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_nvmem_unregister(rtc); + 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 void devm_rtc_release_device(struct device *dev, void *res) +{ + struct rtc_device *rtc = *(struct rtc_device **)res; + + rtc_nvmem_unregister(rtc); + + if (rtc->registered) + rtc_device_unregister(rtc); + else + put_device(&rtc->dev); +} + +struct rtc_device *devm_rtc_allocate_device(struct device *dev) +{ + struct rtc_device **ptr, *rtc; + int id, err; + + id = rtc_device_get_id(dev); + if (id < 0) + return ERR_PTR(id); + + ptr = devres_alloc(devm_rtc_release_device, sizeof(*ptr), GFP_KERNEL); + if (!ptr) { + err = -ENOMEM; + goto exit_ida; + } + + rtc = rtc_allocate_device(); + if (!rtc) { + err = -ENOMEM; + goto exit_devres; + } + + *ptr = rtc; + devres_add(dev, ptr); + + rtc->id = id; + rtc->dev.parent = dev; + dev_set_name(&rtc->dev, "rtc%d", id); + + return rtc; + +exit_devres: + devres_free(ptr); +exit_ida: + ida_simple_remove(&rtc_ida, id); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(devm_rtc_allocate_device); + +int __rtc_register_device(struct module *owner, struct rtc_device *rtc) +{ + struct rtc_wkalrm alrm; + int err; + + if (!rtc->ops) + return -EINVAL; + + rtc->owner = owner; + rtc_device_get_offset(rtc); + + /* 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 = cdev_device_add(&rtc->char_dev, &rtc->dev); + if (err) + dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n", + MAJOR(rtc->dev.devt), rtc->id); + else + dev_dbg(rtc->dev.parent, "char device (%d:%d)\n", + MAJOR(rtc->dev.devt), rtc->id); + + rtc_proc_add_device(rtc); + + rtc->registered = true; + dev_info(rtc->dev.parent, "registered as %s\n", + dev_name(&rtc->dev)); + + return 0; +} +EXPORT_SYMBOL_GPL(__rtc_register_device); + +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(); + return 0; +} +subsys_initcall(rtc_init); diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c new file mode 100644 index 000000000..b9ec4a16d --- /dev/null +++ b/drivers/rtc/hctosys.c @@ -0,0 +1,77 @@ +/* + * 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); + +#if BITS_PER_LONG == 32 + if (tv64.tv_sec > INT_MAX) { + err = -ERANGE; + goto err_read; + } +#endif + + 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..ce051f918 --- /dev/null +++ b/drivers/rtc/interface.c @@ -0,0 +1,1095 @@ +/* + * 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> + +#define CREATE_TRACE_POINTS +#include <trace/events/rtc.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 void rtc_add_offset(struct rtc_device *rtc, struct rtc_time *tm) +{ + time64_t secs; + + if (!rtc->offset_secs) + return; + + secs = rtc_tm_to_time64(tm); + + /* + * Since the reading time values from RTC device are always in the RTC + * original valid range, but we need to skip the overlapped region + * between expanded range and original range, which is no need to add + * the offset. + */ + if ((rtc->start_secs > rtc->range_min && secs >= rtc->start_secs) || + (rtc->start_secs < rtc->range_min && + secs <= (rtc->start_secs + rtc->range_max - rtc->range_min))) + return; + + rtc_time64_to_tm(secs + rtc->offset_secs, tm); +} + +static void rtc_subtract_offset(struct rtc_device *rtc, struct rtc_time *tm) +{ + time64_t secs; + + if (!rtc->offset_secs) + return; + + secs = rtc_tm_to_time64(tm); + + /* + * If the setting time values are in the valid range of RTC hardware + * device, then no need to subtract the offset when setting time to RTC + * device. Otherwise we need to subtract the offset to make the time + * values are valid for RTC hardware device. + */ + if (secs >= rtc->range_min && secs <= rtc->range_max) + return; + + rtc_time64_to_tm(secs - rtc->offset_secs, tm); +} + +static int rtc_valid_range(struct rtc_device *rtc, struct rtc_time *tm) +{ + if (rtc->range_min != rtc->range_max) { + time64_t time = rtc_tm_to_time64(tm); + time64_t range_min = rtc->set_start_time ? rtc->start_secs : + rtc->range_min; + time64_t range_max = rtc->set_start_time ? + (rtc->start_secs + rtc->range_max - rtc->range_min) : + rtc->range_max; + + if (time < range_min || time > range_max) + return -ERANGE; + } + + return 0; +} + +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; + } + + rtc_add_offset(rtc, tm); + + 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); + + trace_rtc_read_time(rtc_tm_to_time64(tm), err); + return err; +} +EXPORT_SYMBOL_GPL(rtc_read_time); + +int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) +{ + int err, uie; + + err = rtc_valid_tm(tm); + if (err != 0) + return err; + + err = rtc_valid_range(rtc, tm); + if (err) + return err; + + rtc_subtract_offset(rtc, tm); + +#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL + uie = rtc->uie_rtctimer.enabled || rtc->uie_irq_active; +#else + uie = rtc->uie_rtctimer.enabled; +#endif + if (uie) { + err = rtc_update_irq_enable(rtc, 0); + if (err) + 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); + + if (uie) { + err = rtc_update_irq_enable(rtc, 1); + if (err) + return err; + } + + trace_rtc_set_time(rtc_tm_to_time64(tm), err); + return err; +} +EXPORT_SYMBOL_GPL(rtc_set_time); + +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 { + alarm->enabled = 0; + alarm->pending = 0; + alarm->time.tm_sec = -1; + alarm->time.tm_min = -1; + alarm->time.tm_hour = -1; + alarm->time.tm_mday = -1; + 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; + err = rtc->ops->read_alarm(rtc->dev.parent, alarm); + } + + mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); + 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) { + rtc_add_offset(rtc, &alarm->time); + 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; + } + + /* Can't proceed if alarm is still invalid after replacing + * missing fields. + */ + err = rtc_valid_tm(&alarm->time); + if (err) + goto done; + + /* 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"); + } + + err = rtc_valid_tm(&alarm->time); + +done: + 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); + + trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err); + 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. + */ + + rtc_subtract_offset(rtc, &alarm->time); + + if (!rtc->ops) + err = -ENODEV; + else if (!rtc->ops->set_alarm) + err = -EINVAL; + else + err = rtc->ops->set_alarm(rtc->dev.parent, alarm); + + trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err); + return err; +} + +int rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +{ + int err; + + if (!rtc->ops) + return -ENODEV; + else if (!rtc->ops->set_alarm) + return -EINVAL; + + err = rtc_valid_tm(&alarm->time); + if (err != 0) + return err; + + err = rtc_valid_range(rtc, &alarm->time); + if (err) + 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 = 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 = 0; + + /* Alarm has to be enabled & in the future for us to enqueue it */ + if (alarm->enabled && (rtc_tm_to_ktime(now) < + rtc->aie_timer.node.expires)) { + + rtc->aie_timer.enabled = 1; + timerqueue_add(&rtc->timerqueue, &rtc->aie_timer.node); + trace_rtc_timer_enqueue(&rtc->aie_timer); + } + 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); + + trace_rtc_alarm_irq_enable(enabled, err); + 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); + + 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 = 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 (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); + +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 = 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. + */ +int rtc_irq_set_state(struct rtc_device *rtc, int enabled) +{ + int err = 0; + + while (rtc_update_hrtimer(rtc, enabled) < 0) + cpu_relax(); + + rtc->pie_enabled = enabled; + + trace_rtc_irq_set_state(enabled, err); + return err; +} + +/** + * 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 + * 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, int freq) +{ + int err = 0; + + if (freq <= 0 || freq > RTC_MAX_FREQ) + return -EINVAL; + + rtc->irq_freq = freq; + while (rtc->pie_enabled && rtc_update_hrtimer(rtc, 1) < 0) + cpu_relax(); + + trace_rtc_irq_set_freq(freq, err); + return err; +} + +/** + * 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) +{ + struct timerqueue_node *next = timerqueue_getnext(&rtc->timerqueue); + struct rtc_time tm; + ktime_t now; + + timer->enabled = 1; + __rtc_read_time(rtc, &tm); + now = rtc_tm_to_ktime(tm); + + /* Skip over expired timers */ + while (next) { + if (next->expires >= now) + break; + next = timerqueue_iterate_next(next); + } + + timerqueue_add(&rtc->timerqueue, &timer->node); + trace_rtc_timer_enqueue(timer); + if (!next || ktime_before(timer->node.expires, next->expires)) { + 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); + trace_rtc_timer_dequeue(timer); + 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); + trace_rtc_alarm_irq_enable(0, 0); +} + +/** + * 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); + trace_rtc_timer_dequeue(timer); + 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 > now) + break; + + /* expire timer */ + timer = container_of(next, struct rtc_timer, node); + timerqueue_del(&rtc->timerqueue, &timer->node); + trace_rtc_timer_dequeue(timer); + timer->enabled = 0; + if (timer->func) + timer->func(timer->private_data); + + trace_rtc_timer_fired(timer); + /* 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); + trace_rtc_timer_enqueue(timer); + } + } + + /* 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); + trace_rtc_timer_dequeue(timer); + 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->func = f; + timer->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 + */ +void rtc_timer_cancel(struct rtc_device *rtc, struct rtc_timer *timer) +{ + mutex_lock(&rtc->ops_lock); + if (timer->enabled) + rtc_timer_remove(rtc, timer); + mutex_unlock(&rtc->ops_lock); +} + +/** + * rtc_read_offset - Read the amount of rtc offset in parts per billion + * @ rtc: rtc device to be used + * @ offset: the offset in parts per billion + * + * see below for details. + * + * Kernel interface to read rtc clock offset + * Returns 0 on success, or a negative number on error. + * If read_offset() is not implemented for the rtc, return -EINVAL + */ +int rtc_read_offset(struct rtc_device *rtc, long *offset) +{ + int ret; + + if (!rtc->ops) + return -ENODEV; + + if (!rtc->ops->read_offset) + return -EINVAL; + + mutex_lock(&rtc->ops_lock); + ret = rtc->ops->read_offset(rtc->dev.parent, offset); + mutex_unlock(&rtc->ops_lock); + + trace_rtc_read_offset(*offset, ret); + return ret; +} + +/** + * rtc_set_offset - Adjusts the duration of the average second + * @ rtc: rtc device to be used + * @ offset: the offset in parts per billion + * + * Some rtc's allow an adjustment to the average duration of a second + * to compensate for differences in the actual clock rate due to temperature, + * the crystal, capacitor, etc. + * + * The adjustment applied is as follows: + * t = t0 * (1 + offset * 1e-9) + * where t0 is the measured length of 1 RTC second with offset = 0 + * + * Kernel interface to adjust an rtc clock offset. + * Return 0 on success, or a negative number on error. + * If the rtc offset is not setable (or not implemented), return -EINVAL + */ +int rtc_set_offset(struct rtc_device *rtc, long offset) +{ + int ret; + + if (!rtc->ops) + return -ENODEV; + + if (!rtc->ops->set_offset) + return -EINVAL; + + mutex_lock(&rtc->ops_lock); + ret = rtc->ops->set_offset(rtc->dev.parent, offset); + mutex_unlock(&rtc->ops_lock); + + trace_rtc_set_offset(offset, ret); + return ret; +} diff --git a/drivers/rtc/nvmem.c b/drivers/rtc/nvmem.c new file mode 100644 index 000000000..36ab183c4 --- /dev/null +++ b/drivers/rtc/nvmem.c @@ -0,0 +1,118 @@ +/* + * RTC subsystem, nvmem interface + * + * Copyright (C) 2017 Alexandre Belloni + * + * 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/types.h> +#include <linux/nvmem-consumer.h> +#include <linux/rtc.h> +#include <linux/sysfs.h> + +/* + * Deprecated ABI compatibility, this should be removed at some point + */ + +static const char nvram_warning[] = "Deprecated ABI, please use nvmem"; + +static ssize_t +rtc_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct rtc_device *rtc = attr->private; + + dev_warn_once(kobj_to_dev(kobj), nvram_warning); + + return nvmem_device_read(rtc->nvmem, off, count, buf); +} + +static ssize_t +rtc_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct rtc_device *rtc = attr->private; + + dev_warn_once(kobj_to_dev(kobj), nvram_warning); + + return nvmem_device_write(rtc->nvmem, off, count, buf); +} + +static int rtc_nvram_register(struct rtc_device *rtc, size_t size) +{ + int err; + + rtc->nvram = devm_kzalloc(rtc->dev.parent, + sizeof(struct bin_attribute), + GFP_KERNEL); + if (!rtc->nvram) + return -ENOMEM; + + rtc->nvram->attr.name = "nvram"; + rtc->nvram->attr.mode = 0644; + rtc->nvram->private = rtc; + + sysfs_bin_attr_init(rtc->nvram); + + rtc->nvram->read = rtc_nvram_read; + rtc->nvram->write = rtc_nvram_write; + rtc->nvram->size = size; + + err = sysfs_create_bin_file(&rtc->dev.parent->kobj, + rtc->nvram); + if (err) { + devm_kfree(rtc->dev.parent, rtc->nvram); + rtc->nvram = NULL; + } + + return err; +} + +static void rtc_nvram_unregister(struct rtc_device *rtc) +{ + sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram); +} + +/* + * New ABI, uses nvmem + */ +int rtc_nvmem_register(struct rtc_device *rtc, + struct nvmem_config *nvmem_config) +{ + if (!IS_ERR_OR_NULL(rtc->nvmem)) + return -EBUSY; + + if (!nvmem_config) + return -ENODEV; + + nvmem_config->dev = rtc->dev.parent; + nvmem_config->owner = rtc->owner; + rtc->nvmem = nvmem_register(nvmem_config); + if (IS_ERR(rtc->nvmem)) + return PTR_ERR(rtc->nvmem); + + /* Register the old ABI */ + if (rtc->nvram_old_abi) + rtc_nvram_register(rtc, nvmem_config->size); + + return 0; +} +EXPORT_SYMBOL_GPL(rtc_nvmem_register); + +void rtc_nvmem_unregister(struct rtc_device *rtc) +{ + if (IS_ERR_OR_NULL(rtc->nvmem)) + return; + + /* unregister the old ABI */ + if (rtc->nvram) + rtc_nvram_unregister(rtc); + + nvmem_unregister(rtc->nvmem); +} diff --git a/drivers/rtc/rtc-88pm80x.c b/drivers/rtc/rtc-88pm80x.c new file mode 100644 index 000000000..1fc48ebd3 --- /dev/null +++ b/drivers/rtc/rtc-88pm80x.c @@ -0,0 +1,355 @@ +/* + * 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; + + int irq; +}; + +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; + + now_time = rtc_tm_to_time64(now); + next_time = rtc_tm_to_time64(next); + + if (next_time < now_time) { + /* Advance one day */ + next_time += 60 * 60 * 24; + rtc_time64_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 = ((unsigned long)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 = ((unsigned long)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_time64_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; + + ticks = rtc_tm_to_time64(tm); + + /* load 32-bit read-only counter */ + regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4); + data = ((unsigned long)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 = ((unsigned long)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 = ((unsigned long)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_time64_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 = ((unsigned long)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 = ((unsigned long)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_time64_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 */ + ticks = rtc_tm_to_time64(&alarm_tm); + 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_rtc_pdata *pdata = dev_get_platdata(&pdev->dev); + struct pm80x_rtc_info *info; + struct device_node *node = pdev->dev.of_node; + int ret; + + if (!pdata && !node) { + dev_err(&pdev->dev, + "pm80x-rtc requires platform data or of_node\n"); + return -EINVAL; + } + + if (!pdata) { + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + } + + 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); + + info->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc_dev)) + return PTR_ERR(info->rtc_dev); + + 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; + } + + info->rtc_dev->ops = &pm80x_rtc_ops; + info->rtc_dev->range_max = U32_MAX; + + ret = rtc_register_device(info->rtc_dev); + if (ret) { + 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); + + /* remember whether this power up is caused by PMIC RTC or not */ + 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..9d4a59aa2 --- /dev/null +++ b/drivers/rtc/rtc-88pm860x.c @@ -0,0 +1,470 @@ +/* + * 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 = ((unsigned long)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 = ((unsigned long)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 > 206) { + dev_dbg(info->dev, "Set time %d out of range. " + "Please set time between 1970 to 2106.\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 = ((unsigned long)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 = ((unsigned long)buf[1] << 24) | (buf[3] << 16) | + (buf[5] << 8) | buf[7]; + + pm860x_bulk_read(info->i2c, PM8607_RTC_EXPIRE1, 4, buf); + data = ((unsigned long)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 = ((unsigned long)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 = ((unsigned long)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); + + info->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc_dev)) + return PTR_ERR(info->rtc_dev); + + 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->ops = &pm860x_rtc_ops; + + ret = rtc_register_device(info->rtc_dev); + if (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 + cancel_delayed_work_sync(&info->calib_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..223360176 --- /dev/null +++ b/drivers/rtc/rtc-ab-b5ze-s3.c @@ -0,0 +1,1029 @@ +/* + * 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 = 0; + + /* + * 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; + +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; + + 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, ®); + 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, ®); + 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, ®s[0], ®s[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, ®); + 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, ®); + 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; + + data->rtc = devm_rtc_allocate_device(dev); + ret = PTR_ERR_OR_ZERO(data->rtc); + if (ret) { + dev_err(dev, "%s: unable to allocate RTC device (%d)\n", + __func__, 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->ops = &rtc_ops; + data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + data->rtc->range_max = RTC_TIMESTAMP_END_2099; + + /* 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; + } + } + + ret = rtc_register_device(data->rtc); + +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" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abb5zes3_dt_match); +#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, + .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..821ff52a2 --- /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 0; +} + +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, ®val); + 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..e28f4401f --- /dev/null +++ b/drivers/rtc/rtc-ab8500.c @@ -0,0 +1,483 @@ +/* + * 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> +#include <linux/pm_wakeirq.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 + +/* 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 +}; + +/* 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 0; +} + +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 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 platform_device_id ab85xx_rtc_ids[] = { + { "ab8500-rtc", (kernel_ulong_t)&ab8500_rtc_ops, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, ab85xx_rtc_ids); + +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_ONESHOT, + "ab8500-rtc", rtc); + if (err < 0) + return err; + + dev_pm_set_wake_irq(&pdev->dev, irq); + 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) +{ + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + 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..2cefa67a1 --- /dev/null +++ b/drivers/rtc/rtc-abx80x.c @@ -0,0 +1,691 @@ +/* + * 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_AHTH 0x08 +#define ABX8XX_REG_ASC 0x09 +#define ABX8XX_REG_AMN 0x0a +#define ABX8XX_REG_AHR 0x0b +#define ABX8XX_REG_ADA 0x0c +#define ABX8XX_REG_AMO 0x0d +#define ABX8XX_REG_AWD 0x0e + +#define ABX8XX_REG_STATUS 0x0f +#define ABX8XX_STATUS_AF BIT(2) + +#define ABX8XX_REG_CTRL1 0x10 +#define ABX8XX_CTRL_WRITE BIT(0) +#define ABX8XX_CTRL_ARST BIT(2) +#define ABX8XX_CTRL_12_24 BIT(6) + +#define ABX8XX_REG_IRQ 0x12 +#define ABX8XX_IRQ_AIE BIT(2) +#define ABX8XX_IRQ_IM_1_4 (0x3 << 5) + +#define ABX8XX_REG_CD_TIMER_CTL 0x18 + +#define ABX8XX_REG_OSC 0x1c +#define ABX8XX_OSC_FOS BIT(3) +#define ABX8XX_OSC_BOS BIT(4) +#define ABX8XX_OSC_ACAL_512 BIT(5) +#define ABX8XX_OSC_ACAL_1024 BIT(6) + +#define ABX8XX_OSC_OSEL BIT(7) + +#define ABX8XX_REG_OSS 0x1d +#define ABX8XX_OSS_OF BIT(1) +#define ABX8XX_OSS_OMODE BIT(4) + +#define ABX8XX_REG_CFG_KEY 0x1f +#define ABX8XX_CFG_KEY_OSC 0xa1 +#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 int abx80x_is_rc_mode(struct i2c_client *client) +{ + int flags = 0; + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); + if (flags < 0) { + dev_err(&client->dev, + "Failed to read autocalibration attribute\n"); + return flags; + } + + return (flags & ABX8XX_OSS_OMODE) ? 1 : 0; +} + +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, flags, rc_mode = 0; + + /* Read the Oscillator Failure only in XT mode */ + rc_mode = abx80x_is_rc_mode(client); + if (rc_mode < 0) + return rc_mode; + + if (!rc_mode) { + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); + if (flags < 0) + return flags; + + if (flags & ABX8XX_OSS_OF) { + dev_err(dev, "Oscillator failure, data is invalid.\n"); + return -EINVAL; + } + } + + 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; + + return 0; +} + +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, flags; + + 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; + } + + /* Clear the OF bit of Oscillator Status Register */ + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); + if (flags < 0) + return flags; + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSS, + flags & ~ABX8XX_OSS_OF); + if (err < 0) { + dev_err(&client->dev, "Unable to write oscillator status register\n"); + return err; + } + + return 0; +} + +static irqreturn_t abx80x_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rtc_device *rtc = i2c_get_clientdata(client); + int status; + + status = i2c_smbus_read_byte_data(client, ABX8XX_REG_STATUS); + if (status < 0) + return IRQ_NONE; + + if (status & ABX8XX_STATUS_AF) + rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF); + + i2c_smbus_write_byte_data(client, ABX8XX_REG_STATUS, 0); + + return IRQ_HANDLED; +} + +static int abx80x_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[7]; + + int irq_mask, err; + + if (client->irq <= 0) + return -EINVAL; + + err = i2c_smbus_read_i2c_block_data(client, ABX8XX_REG_ASC, + sizeof(buf), buf); + if (err) + return err; + + irq_mask = i2c_smbus_read_byte_data(client, ABX8XX_REG_IRQ); + if (irq_mask < 0) + return irq_mask; + + t->time.tm_sec = bcd2bin(buf[0] & 0x7F); + t->time.tm_min = bcd2bin(buf[1] & 0x7F); + t->time.tm_hour = bcd2bin(buf[2] & 0x3F); + t->time.tm_mday = bcd2bin(buf[3] & 0x3F); + t->time.tm_mon = bcd2bin(buf[4] & 0x1F) - 1; + t->time.tm_wday = buf[5] & 0x7; + + t->enabled = !!(irq_mask & ABX8XX_IRQ_AIE); + t->pending = (buf[6] & ABX8XX_STATUS_AF) && t->enabled; + + return err; +} + +static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 alarm[6]; + int err; + + if (client->irq <= 0) + return -EINVAL; + + alarm[0] = 0x0; + alarm[1] = bin2bcd(t->time.tm_sec); + alarm[2] = bin2bcd(t->time.tm_min); + alarm[3] = bin2bcd(t->time.tm_hour); + alarm[4] = bin2bcd(t->time.tm_mday); + alarm[5] = bin2bcd(t->time.tm_mon + 1); + + err = i2c_smbus_write_i2c_block_data(client, ABX8XX_REG_AHTH, + sizeof(alarm), alarm); + if (err < 0) { + dev_err(&client->dev, "Unable to write alarm registers\n"); + return -EIO; + } + + if (t->enabled) { + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ, + (ABX8XX_IRQ_IM_1_4 | + ABX8XX_IRQ_AIE)); + if (err) + return err; + } + + return 0; +} + +static int abx80x_rtc_set_autocalibration(struct device *dev, + int autocalibration) +{ + struct i2c_client *client = to_i2c_client(dev); + int retval, flags = 0; + + if ((autocalibration != 0) && (autocalibration != 1024) && + (autocalibration != 512)) { + dev_err(dev, "autocalibration value outside permitted range\n"); + return -EINVAL; + } + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (autocalibration == 0) { + flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024); + } else if (autocalibration == 1024) { + /* 1024 autocalibration is 0x10 */ + flags |= ABX8XX_OSC_ACAL_1024; + flags &= ~(ABX8XX_OSC_ACAL_512); + } else { + /* 512 autocalibration is 0x11 */ + flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512); + } + + /* Unlock write access to Oscillator Control Register */ + retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, + ABX8XX_CFG_KEY_OSC); + if (retval < 0) { + dev_err(dev, "Failed to write CONFIG_KEY register\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags); + + return retval; +} + +static int abx80x_rtc_get_autocalibration(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int flags = 0, autocalibration; + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (flags & ABX8XX_OSC_ACAL_512) + autocalibration = 512; + else if (flags & ABX8XX_OSC_ACAL_1024) + autocalibration = 1024; + else + autocalibration = 0; + + return autocalibration; +} + +static ssize_t autocalibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int retval; + unsigned long autocalibration = 0; + + retval = kstrtoul(buf, 10, &autocalibration); + if (retval < 0) { + dev_err(dev, "Failed to store RTC autocalibration attribute\n"); + return -EINVAL; + } + + retval = abx80x_rtc_set_autocalibration(dev, autocalibration); + + return retval ? retval : count; +} + +static ssize_t autocalibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int autocalibration = 0; + + autocalibration = abx80x_rtc_get_autocalibration(dev); + if (autocalibration < 0) { + dev_err(dev, "Failed to read RTC autocalibration\n"); + sprintf(buf, "0\n"); + return autocalibration; + } + + return sprintf(buf, "%d\n", autocalibration); +} + +static DEVICE_ATTR_RW(autocalibration); + +static ssize_t oscillator_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + int retval, flags, rc_mode = 0; + + if (strncmp(buf, "rc", 2) == 0) { + rc_mode = 1; + } else if (strncmp(buf, "xtal", 4) == 0) { + rc_mode = 0; + } else { + dev_err(dev, "Oscillator selection value outside permitted ones\n"); + return -EINVAL; + } + + flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); + if (flags < 0) + return flags; + + if (rc_mode == 0) + flags &= ~(ABX8XX_OSC_OSEL); + else + flags |= (ABX8XX_OSC_OSEL); + + /* Unlock write access on Oscillator Control register */ + retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, + ABX8XX_CFG_KEY_OSC); + if (retval < 0) { + dev_err(dev, "Failed to write CONFIG_KEY register\n"); + return retval; + } + + retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags); + if (retval < 0) { + dev_err(dev, "Failed to write Oscillator Control register\n"); + return retval; + } + + return retval ? retval : count; +} + +static ssize_t oscillator_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc_mode = 0; + struct i2c_client *client = to_i2c_client(dev); + + rc_mode = abx80x_is_rc_mode(client); + + if (rc_mode < 0) { + dev_err(dev, "Failed to read RTC oscillator selection\n"); + sprintf(buf, "\n"); + return rc_mode; + } + + if (rc_mode) + return sprintf(buf, "rc\n"); + else + return sprintf(buf, "xtal\n"); +} + +static DEVICE_ATTR_RW(oscillator); + +static struct attribute *rtc_calib_attrs[] = { + &dev_attr_autocalibration.attr, + &dev_attr_oscillator.attr, + NULL, +}; + +static const struct attribute_group rtc_calib_attr_group = { + .attrs = rtc_calib_attrs, +}; + +static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + + if (enabled) + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ, + (ABX8XX_IRQ_IM_1_4 | + ABX8XX_IRQ_AIE)); + else + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_IRQ, + ABX8XX_IRQ_IM_1_4); + return err; +} + +static const struct rtc_class_ops abx80x_rtc_ops = { + .read_time = abx80x_rtc_read_time, + .set_time = abx80x_rtc_set_time, + .read_alarm = abx80x_read_alarm, + .set_alarm = abx80x_set_alarm, + .alarm_irq_enable = abx80x_alarm_irq_enable, +}; + +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 void rtc_calib_remove_sysfs_group(void *_dev) +{ + struct device *dev = _dev; + + sysfs_remove_group(&dev->kobj, &rtc_calib_attr_group); +} + +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_ARST)) | + 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); + } + + err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CD_TIMER_CTL, + BIT(2)); + if (err) + return err; + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &abx80x_rtc_ops; + + i2c_set_clientdata(client, rtc); + + if (client->irq > 0) { + dev_info(&client->dev, "IRQ %d supplied\n", client->irq); + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + abx80x_handle_irq, + IRQF_SHARED | IRQF_ONESHOT, + "abx8xx", + client); + if (err) { + dev_err(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + + /* Export sysfs entries */ + err = sysfs_create_group(&(&client->dev)->kobj, &rtc_calib_attr_group); + if (err) { + dev_err(&client->dev, "Failed to create sysfs group: %d\n", + err); + return err; + } + + err = devm_add_action_or_reset(&client->dev, + rtc_calib_remove_sysfs_group, + &client->dev); + if (err) { + dev_err(&client->dev, + "Failed to add sysfs cleanup action: %d\n", + err); + return err; + } + + err = rtc_register_device(rtc); + + return err; +} + +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 }, + { "rv1805", 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-ac100.c b/drivers/rtc/rtc-ac100.c new file mode 100644 index 000000000..784b67628 --- /dev/null +++ b/drivers/rtc/rtc-ac100.c @@ -0,0 +1,661 @@ +/* + * RTC Driver for X-Powers AC100 + * + * Copyright (c) 2016 Chen-Yu Tsai + * + * Chen-Yu Tsai <wens@csie.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. + * + * 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/clk-provider.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/ac100.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/types.h> + +/* Control register */ +#define AC100_RTC_CTRL_24HOUR BIT(0) + +/* Clock output register bits */ +#define AC100_CLKOUT_PRE_DIV_SHIFT 5 +#define AC100_CLKOUT_PRE_DIV_WIDTH 3 +#define AC100_CLKOUT_MUX_SHIFT 4 +#define AC100_CLKOUT_MUX_WIDTH 1 +#define AC100_CLKOUT_DIV_SHIFT 1 +#define AC100_CLKOUT_DIV_WIDTH 3 +#define AC100_CLKOUT_EN BIT(0) + +/* RTC */ +#define AC100_RTC_SEC_MASK GENMASK(6, 0) +#define AC100_RTC_MIN_MASK GENMASK(6, 0) +#define AC100_RTC_HOU_MASK GENMASK(5, 0) +#define AC100_RTC_WEE_MASK GENMASK(2, 0) +#define AC100_RTC_DAY_MASK GENMASK(5, 0) +#define AC100_RTC_MON_MASK GENMASK(4, 0) +#define AC100_RTC_YEA_MASK GENMASK(7, 0) +#define AC100_RTC_YEA_LEAP BIT(15) +#define AC100_RTC_UPD_TRIGGER BIT(15) + +/* Alarm (wall clock) */ +#define AC100_ALM_INT_ENABLE BIT(0) + +#define AC100_ALM_SEC_MASK GENMASK(6, 0) +#define AC100_ALM_MIN_MASK GENMASK(6, 0) +#define AC100_ALM_HOU_MASK GENMASK(5, 0) +#define AC100_ALM_WEE_MASK GENMASK(2, 0) +#define AC100_ALM_DAY_MASK GENMASK(5, 0) +#define AC100_ALM_MON_MASK GENMASK(4, 0) +#define AC100_ALM_YEA_MASK GENMASK(7, 0) +#define AC100_ALM_ENABLE_FLAG BIT(15) +#define AC100_ALM_UPD_TRIGGER BIT(15) + +/* + * 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 - 2069. This range is selected to match Allwinner's + * driver. + */ +#define AC100_YEAR_MIN 1970 +#define AC100_YEAR_MAX 2069 +#define AC100_YEAR_OFF (AC100_YEAR_MIN - 1900) + +struct ac100_clkout { + struct clk_hw hw; + struct regmap *regmap; + u8 offset; +}; + +#define to_ac100_clkout(_hw) container_of(_hw, struct ac100_clkout, hw) + +#define AC100_RTC_32K_NAME "ac100-rtc-32k" +#define AC100_RTC_32K_RATE 32768 +#define AC100_CLKOUT_NUM 3 + +static const char * const ac100_clkout_names[AC100_CLKOUT_NUM] = { + "ac100-cko1-rtc", + "ac100-cko2-rtc", + "ac100-cko3-rtc", +}; + +struct ac100_rtc_dev { + struct rtc_device *rtc; + struct device *dev; + struct regmap *regmap; + int irq; + unsigned long alarm; + + struct clk_hw *rtc_32k_clk; + struct ac100_clkout clks[AC100_CLKOUT_NUM]; + struct clk_hw_onecell_data *clk_data; +}; + +/** + * Clock controls for 3 clock output pins + */ + +static const struct clk_div_table ac100_clkout_prediv[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 16 }, + { .val = 5, .div = 32 }, + { .val = 6, .div = 64 }, + { .val = 7, .div = 122 }, + { }, +}; + +/* Abuse the fact that one parent is 32768 Hz, and the other is 4 MHz */ +static unsigned long ac100_clkout_recalc_rate(struct clk_hw *hw, + unsigned long prate) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + unsigned int reg, div; + + regmap_read(clk->regmap, clk->offset, ®); + + /* Handle pre-divider first */ + if (prate != AC100_RTC_32K_RATE) { + div = (reg >> AC100_CLKOUT_PRE_DIV_SHIFT) & + ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1); + prate = divider_recalc_rate(hw, prate, div, + ac100_clkout_prediv, 0, + AC100_CLKOUT_PRE_DIV_WIDTH); + } + + div = (reg >> AC100_CLKOUT_DIV_SHIFT) & + (BIT(AC100_CLKOUT_DIV_WIDTH) - 1); + return divider_recalc_rate(hw, prate, div, NULL, + CLK_DIVIDER_POWER_OF_TWO, + AC100_CLKOUT_DIV_WIDTH); +} + +static long ac100_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + unsigned long best_rate = 0, tmp_rate, tmp_prate; + int i; + + if (prate == AC100_RTC_32K_RATE) + return divider_round_rate(hw, rate, &prate, NULL, + AC100_CLKOUT_DIV_WIDTH, + CLK_DIVIDER_POWER_OF_TWO); + + for (i = 0; ac100_clkout_prediv[i].div; i++) { + tmp_prate = DIV_ROUND_UP(prate, ac100_clkout_prediv[i].val); + tmp_rate = divider_round_rate(hw, rate, &tmp_prate, NULL, + AC100_CLKOUT_DIV_WIDTH, + CLK_DIVIDER_POWER_OF_TWO); + + if (tmp_rate > rate) + continue; + if (rate - tmp_rate < best_rate - tmp_rate) + best_rate = tmp_rate; + } + + return best_rate; +} + +static int ac100_clkout_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_hw *best_parent; + unsigned long best = 0; + int i, num_parents = clk_hw_get_num_parents(hw); + + for (i = 0; i < num_parents; i++) { + struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i); + unsigned long tmp, prate; + + /* + * The clock has two parents, one is a fixed clock which is + * internally registered by the ac100 driver. The other parent + * is a clock from the codec side of the chip, which we + * properly declare and reference in the devicetree and is + * not implemented in any driver right now. + * If the clock core looks for the parent of that second + * missing clock, it can't find one that is registered and + * returns NULL. + * So we end up in a situation where clk_hw_get_num_parents + * returns the amount of clocks we can be parented to, but + * clk_hw_get_parent_by_index will not return the orphan + * clocks. + * Thus we need to check if the parent exists before + * we get the parent rate, so we could use the RTC + * without waiting for the codec to be supported. + */ + if (!parent) + continue; + + prate = clk_hw_get_rate(parent); + + tmp = ac100_clkout_round_rate(hw, req->rate, prate); + + if (tmp > req->rate) + continue; + if (req->rate - tmp < req->rate - best) { + best = tmp; + best_parent = parent; + } + } + + if (!best) + return -EINVAL; + + req->best_parent_hw = best_parent; + req->best_parent_rate = best; + req->rate = best; + + return 0; +} + +static int ac100_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + int div = 0, pre_div = 0; + + do { + div = divider_get_val(rate * ac100_clkout_prediv[pre_div].div, + prate, NULL, AC100_CLKOUT_DIV_WIDTH, + CLK_DIVIDER_POWER_OF_TWO); + if (div >= 0) + break; + } while (prate != AC100_RTC_32K_RATE && + ac100_clkout_prediv[++pre_div].div); + + if (div < 0) + return div; + + pre_div = ac100_clkout_prediv[pre_div].val; + + regmap_update_bits(clk->regmap, clk->offset, + ((1 << AC100_CLKOUT_DIV_WIDTH) - 1) << AC100_CLKOUT_DIV_SHIFT | + ((1 << AC100_CLKOUT_PRE_DIV_WIDTH) - 1) << AC100_CLKOUT_PRE_DIV_SHIFT, + (div - 1) << AC100_CLKOUT_DIV_SHIFT | + (pre_div - 1) << AC100_CLKOUT_PRE_DIV_SHIFT); + + return 0; +} + +static int ac100_clkout_prepare(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + + return regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN, + AC100_CLKOUT_EN); +} + +static void ac100_clkout_unprepare(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + + regmap_update_bits(clk->regmap, clk->offset, AC100_CLKOUT_EN, 0); +} + +static int ac100_clkout_is_prepared(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + unsigned int reg; + + regmap_read(clk->regmap, clk->offset, ®); + + return reg & AC100_CLKOUT_EN; +} + +static u8 ac100_clkout_get_parent(struct clk_hw *hw) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + unsigned int reg; + + regmap_read(clk->regmap, clk->offset, ®); + + return (reg >> AC100_CLKOUT_MUX_SHIFT) & 0x1; +} + +static int ac100_clkout_set_parent(struct clk_hw *hw, u8 index) +{ + struct ac100_clkout *clk = to_ac100_clkout(hw); + + return regmap_update_bits(clk->regmap, clk->offset, + BIT(AC100_CLKOUT_MUX_SHIFT), + index ? BIT(AC100_CLKOUT_MUX_SHIFT) : 0); +} + +static const struct clk_ops ac100_clkout_ops = { + .prepare = ac100_clkout_prepare, + .unprepare = ac100_clkout_unprepare, + .is_prepared = ac100_clkout_is_prepared, + .recalc_rate = ac100_clkout_recalc_rate, + .determine_rate = ac100_clkout_determine_rate, + .get_parent = ac100_clkout_get_parent, + .set_parent = ac100_clkout_set_parent, + .set_rate = ac100_clkout_set_rate, +}; + +static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) +{ + struct device_node *np = chip->dev->of_node; + const char *parents[2] = {AC100_RTC_32K_NAME}; + int i, ret; + + chip->clk_data = devm_kzalloc(chip->dev, + struct_size(chip->clk_data, hws, + AC100_CLKOUT_NUM), + GFP_KERNEL); + if (!chip->clk_data) + return -ENOMEM; + + chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev, + AC100_RTC_32K_NAME, + NULL, 0, + AC100_RTC_32K_RATE); + if (IS_ERR(chip->rtc_32k_clk)) { + ret = PTR_ERR(chip->rtc_32k_clk); + dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n", + ret); + return ret; + } + + parents[1] = of_clk_get_parent_name(np, 0); + if (!parents[1]) { + dev_err(chip->dev, "Failed to get ADDA 4M clock\n"); + return -EINVAL; + } + + for (i = 0; i < AC100_CLKOUT_NUM; i++) { + struct ac100_clkout *clk = &chip->clks[i]; + struct clk_init_data init = { + .name = ac100_clkout_names[i], + .ops = &ac100_clkout_ops, + .parent_names = parents, + .num_parents = ARRAY_SIZE(parents), + .flags = 0, + }; + + of_property_read_string_index(np, "clock-output-names", + i, &init.name); + clk->regmap = chip->regmap; + clk->offset = AC100_CLKOUT_CTRL1 + i; + clk->hw.init = &init; + + ret = devm_clk_hw_register(chip->dev, &clk->hw); + if (ret) { + dev_err(chip->dev, "Failed to register clk '%s': %d\n", + init.name, ret); + goto err_unregister_rtc_32k; + } + + chip->clk_data->hws[i] = &clk->hw; + } + + chip->clk_data->num = i; + ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data); + if (ret) + goto err_unregister_rtc_32k; + + return 0; + +err_unregister_rtc_32k: + clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); + + return ret; +} + +static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) +{ + of_clk_del_provider(chip->dev->of_node); + clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); +} + +/** + * RTC related bits + */ +static int ac100_rtc_get_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + u16 reg[7]; + int ret; + + ret = regmap_bulk_read(regmap, AC100_RTC_SEC, reg, 7); + if (ret) + return ret; + + rtc_tm->tm_sec = bcd2bin(reg[0] & AC100_RTC_SEC_MASK); + rtc_tm->tm_min = bcd2bin(reg[1] & AC100_RTC_MIN_MASK); + rtc_tm->tm_hour = bcd2bin(reg[2] & AC100_RTC_HOU_MASK); + rtc_tm->tm_wday = bcd2bin(reg[3] & AC100_RTC_WEE_MASK); + rtc_tm->tm_mday = bcd2bin(reg[4] & AC100_RTC_DAY_MASK); + rtc_tm->tm_mon = bcd2bin(reg[5] & AC100_RTC_MON_MASK) - 1; + rtc_tm->tm_year = bcd2bin(reg[6] & AC100_RTC_YEA_MASK) + + AC100_YEAR_OFF; + + return 0; +} + +static int ac100_rtc_set_time(struct device *dev, struct rtc_time *rtc_tm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + int year; + u16 reg[8]; + + /* our RTC has a limited year range... */ + year = rtc_tm->tm_year - AC100_YEAR_OFF; + if (year < 0 || year > (AC100_YEAR_MAX - 1900)) { + dev_err(dev, "rtc only supports year in range %d - %d\n", + AC100_YEAR_MIN, AC100_YEAR_MAX); + return -EINVAL; + } + + /* convert to BCD */ + reg[0] = bin2bcd(rtc_tm->tm_sec) & AC100_RTC_SEC_MASK; + reg[1] = bin2bcd(rtc_tm->tm_min) & AC100_RTC_MIN_MASK; + reg[2] = bin2bcd(rtc_tm->tm_hour) & AC100_RTC_HOU_MASK; + reg[3] = bin2bcd(rtc_tm->tm_wday) & AC100_RTC_WEE_MASK; + reg[4] = bin2bcd(rtc_tm->tm_mday) & AC100_RTC_DAY_MASK; + reg[5] = bin2bcd(rtc_tm->tm_mon + 1) & AC100_RTC_MON_MASK; + reg[6] = bin2bcd(year) & AC100_RTC_YEA_MASK; + /* trigger write */ + reg[7] = AC100_RTC_UPD_TRIGGER; + + /* Is it a leap year? */ + if (is_leap_year(year + AC100_YEAR_OFF + 1900)) + reg[6] |= AC100_RTC_YEA_LEAP; + + return regmap_bulk_write(regmap, AC100_RTC_SEC, reg, 8); +} + +static int ac100_rtc_alarm_irq_enable(struct device *dev, unsigned int en) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + unsigned int val; + + val = en ? AC100_ALM_INT_ENABLE : 0; + + return regmap_write(regmap, AC100_ALM_INT_ENA, val); +} + +static int ac100_rtc_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + struct rtc_time *alrm_tm = &alrm->time; + u16 reg[7]; + unsigned int val; + int ret; + + ret = regmap_read(regmap, AC100_ALM_INT_ENA, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & AC100_ALM_INT_ENABLE); + + ret = regmap_bulk_read(regmap, AC100_ALM_SEC, reg, 7); + if (ret) + return ret; + + alrm_tm->tm_sec = bcd2bin(reg[0] & AC100_ALM_SEC_MASK); + alrm_tm->tm_min = bcd2bin(reg[1] & AC100_ALM_MIN_MASK); + alrm_tm->tm_hour = bcd2bin(reg[2] & AC100_ALM_HOU_MASK); + alrm_tm->tm_wday = bcd2bin(reg[3] & AC100_ALM_WEE_MASK); + alrm_tm->tm_mday = bcd2bin(reg[4] & AC100_ALM_DAY_MASK); + alrm_tm->tm_mon = bcd2bin(reg[5] & AC100_ALM_MON_MASK) - 1; + alrm_tm->tm_year = bcd2bin(reg[6] & AC100_ALM_YEA_MASK) + + AC100_YEAR_OFF; + + return 0; +} + +static int ac100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct ac100_rtc_dev *chip = dev_get_drvdata(dev); + struct regmap *regmap = chip->regmap; + struct rtc_time *alrm_tm = &alrm->time; + u16 reg[8]; + int year; + int ret; + + /* our alarm has a limited year range... */ + year = alrm_tm->tm_year - AC100_YEAR_OFF; + if (year < 0 || year > (AC100_YEAR_MAX - 1900)) { + dev_err(dev, "alarm only supports year in range %d - %d\n", + AC100_YEAR_MIN, AC100_YEAR_MAX); + return -EINVAL; + } + + /* convert to BCD */ + reg[0] = (bin2bcd(alrm_tm->tm_sec) & AC100_ALM_SEC_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[1] = (bin2bcd(alrm_tm->tm_min) & AC100_ALM_MIN_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[2] = (bin2bcd(alrm_tm->tm_hour) & AC100_ALM_HOU_MASK) | + AC100_ALM_ENABLE_FLAG; + /* Do not enable weekday alarm */ + reg[3] = bin2bcd(alrm_tm->tm_wday) & AC100_ALM_WEE_MASK; + reg[4] = (bin2bcd(alrm_tm->tm_mday) & AC100_ALM_DAY_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[5] = (bin2bcd(alrm_tm->tm_mon + 1) & AC100_ALM_MON_MASK) | + AC100_ALM_ENABLE_FLAG; + reg[6] = (bin2bcd(year) & AC100_ALM_YEA_MASK) | + AC100_ALM_ENABLE_FLAG; + /* trigger write */ + reg[7] = AC100_ALM_UPD_TRIGGER; + + ret = regmap_bulk_write(regmap, AC100_ALM_SEC, reg, 8); + if (ret) + return ret; + + return ac100_rtc_alarm_irq_enable(dev, alrm->enabled); +} + +static irqreturn_t ac100_rtc_irq(int irq, void *data) +{ + struct ac100_rtc_dev *chip = data; + struct regmap *regmap = chip->regmap; + unsigned int val = 0; + int ret; + + mutex_lock(&chip->rtc->ops_lock); + + /* read status */ + ret = regmap_read(regmap, AC100_ALM_INT_STA, &val); + if (ret) + goto out; + + if (val & AC100_ALM_INT_ENABLE) { + /* signal rtc framework */ + rtc_update_irq(chip->rtc, 1, RTC_AF | RTC_IRQF); + + /* clear status */ + ret = regmap_write(regmap, AC100_ALM_INT_STA, val); + if (ret) + goto out; + + /* disable interrupt */ + ret = ac100_rtc_alarm_irq_enable(chip->dev, 0); + if (ret) + goto out; + } + +out: + mutex_unlock(&chip->rtc->ops_lock); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops ac100_rtc_ops = { + .read_time = ac100_rtc_get_time, + .set_time = ac100_rtc_set_time, + .read_alarm = ac100_rtc_get_alarm, + .set_alarm = ac100_rtc_set_alarm, + .alarm_irq_enable = ac100_rtc_alarm_irq_enable, +}; + +static int ac100_rtc_probe(struct platform_device *pdev) +{ + struct ac100_dev *ac100 = dev_get_drvdata(pdev->dev.parent); + struct ac100_rtc_dev *chip; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + chip->regmap = ac100->regmap; + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return chip->irq; + } + + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + chip->rtc->ops = &ac100_rtc_ops; + + ret = devm_request_threaded_irq(&pdev->dev, chip->irq, NULL, + ac100_rtc_irq, + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&pdev->dev), chip); + if (ret) { + dev_err(&pdev->dev, "Could not request IRQ\n"); + return ret; + } + + /* always use 24 hour mode */ + regmap_write_bits(chip->regmap, AC100_RTC_CTRL, AC100_RTC_CTRL_24HOUR, + AC100_RTC_CTRL_24HOUR); + + /* disable counter alarm interrupt */ + regmap_write(chip->regmap, AC100_ALM_INT_ENA, 0); + + /* clear counter alarm pending interrupts */ + regmap_write(chip->regmap, AC100_ALM_INT_STA, AC100_ALM_INT_ENABLE); + + ret = ac100_rtc_register_clks(chip); + if (ret) + return ret; + + ret = rtc_register_device(chip->rtc); + if (ret) { + dev_err(&pdev->dev, "unable to register device\n"); + return ret; + } + + dev_info(&pdev->dev, "RTC enabled\n"); + + return 0; +} + +static int ac100_rtc_remove(struct platform_device *pdev) +{ + struct ac100_rtc_dev *chip = platform_get_drvdata(pdev); + + ac100_rtc_unregister_clks(chip); + + return 0; +} + +static const struct of_device_id ac100_rtc_match[] = { + { .compatible = "x-powers,ac100-rtc" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ac100_rtc_match); + +static struct platform_driver ac100_rtc_driver = { + .probe = ac100_rtc_probe, + .remove = ac100_rtc_remove, + .driver = { + .name = "ac100-rtc", + .of_match_table = of_match_ptr(ac100_rtc_match), + }, +}; +module_platform_driver(ac100_rtc_driver); + +MODULE_DESCRIPTION("X-Powers AC100 RTC driver"); +MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-armada38x.c b/drivers/rtc/rtc-armada38x.c new file mode 100644 index 000000000..b74338d6d --- /dev/null +++ b/drivers/rtc/rtc-armada38x.c @@ -0,0 +1,629 @@ +/* + * 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/of_device.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_IRQ2_CONF 0x8 +#define RTC_IRQ_AL_EN BIT(0) +#define RTC_IRQ_FREQ_EN BIT(1) +#define RTC_IRQ_FREQ_1HZ BIT(2) +#define RTC_CCR 0x18 +#define RTC_CCR_MODE BIT(15) +#define RTC_CONF_TEST 0x1C +#define RTC_NOMINAL_TIMING BIT(13) + +#define RTC_TIME 0xC +#define RTC_ALARM1 0x10 +#define RTC_ALARM2 0x14 + +/* Armada38x SoC registers */ +#define RTC_38X_BRIDGE_TIMING_CTL 0x0 +#define RTC_38X_PERIOD_OFFS 0 +#define RTC_38X_PERIOD_MASK (0x3FF << RTC_38X_PERIOD_OFFS) +#define RTC_38X_READ_DELAY_OFFS 26 +#define RTC_38X_READ_DELAY_MASK (0x1F << RTC_38X_READ_DELAY_OFFS) + +/* Armada 7K/8K registers */ +#define RTC_8K_BRIDGE_TIMING_CTL0 0x0 +#define RTC_8K_WRCLK_PERIOD_OFFS 0 +#define RTC_8K_WRCLK_PERIOD_MASK (0xFFFF << RTC_8K_WRCLK_PERIOD_OFFS) +#define RTC_8K_WRCLK_SETUP_OFFS 16 +#define RTC_8K_WRCLK_SETUP_MASK (0xFFFF << RTC_8K_WRCLK_SETUP_OFFS) +#define RTC_8K_BRIDGE_TIMING_CTL1 0x4 +#define RTC_8K_READ_DELAY_OFFS 0 +#define RTC_8K_READ_DELAY_MASK (0xFFFF << RTC_8K_READ_DELAY_OFFS) + +#define RTC_8K_ISR 0x10 +#define RTC_8K_IMR 0x14 +#define RTC_8K_ALARM2 BIT(0) + +#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) + +#define SAMPLE_NR 100 + +struct value_to_freq { + u32 value; + u8 freq; +}; + +struct armada38x_rtc { + struct rtc_device *rtc_dev; + void __iomem *regs; + void __iomem *regs_soc; + spinlock_t lock; + int irq; + bool initialized; + struct value_to_freq *val_to_freq; + struct armada38x_rtc_data *data; +}; + +#define ALARM1 0 +#define ALARM2 1 + +#define ALARM_REG(base, alarm) ((base) + (alarm) * sizeof(u32)) + +struct armada38x_rtc_data { + /* Initialize the RTC-MBUS bridge timing */ + void (*update_mbus_timing)(struct armada38x_rtc *rtc); + u32 (*read_rtc_reg)(struct armada38x_rtc *rtc, u8 rtc_reg); + void (*clear_isr)(struct armada38x_rtc *rtc); + void (*unmask_interrupt)(struct armada38x_rtc *rtc); + u32 alarm; +}; + +/* + * 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 + * According to errata RES-3124064, Write to any RTC register + * may fail. As a workaround, before writing to RTC + * register, issue a dummy write of 0x0 twice to RTC Status + * register. + */ + +static void rtc_delayed_write(u32 val, struct armada38x_rtc *rtc, int offset) +{ + writel(0, rtc->regs + RTC_STATUS); + writel(0, rtc->regs + RTC_STATUS); + writel(val, rtc->regs + offset); + udelay(5); +} + +/* Update RTC-MBUS bridge timing parameters */ +static void rtc_update_38x_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); + reg &= ~RTC_38X_PERIOD_MASK; + reg |= 0x3FF << RTC_38X_PERIOD_OFFS; /* Maximum value */ + reg &= ~RTC_38X_READ_DELAY_MASK; + reg |= 0x1F << RTC_38X_READ_DELAY_OFFS; /* Maximum value */ + writel(reg, rtc->regs_soc + RTC_38X_BRIDGE_TIMING_CTL); +} + +static void rtc_update_8k_mbus_timing_params(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); + reg &= ~RTC_8K_WRCLK_PERIOD_MASK; + reg |= 0x3FF << RTC_8K_WRCLK_PERIOD_OFFS; + reg &= ~RTC_8K_WRCLK_SETUP_MASK; + reg |= 0x29 << RTC_8K_WRCLK_SETUP_OFFS; + writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL0); + + reg = readl(rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); + reg &= ~RTC_8K_READ_DELAY_MASK; + reg |= 0x3F << RTC_8K_READ_DELAY_OFFS; + writel(reg, rtc->regs_soc + RTC_8K_BRIDGE_TIMING_CTL1); +} + +static u32 read_rtc_register(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + return readl(rtc->regs + rtc_reg); +} + +static u32 read_rtc_register_38x_wa(struct armada38x_rtc *rtc, u8 rtc_reg) +{ + int i, index_max = 0, max = 0; + + for (i = 0; i < SAMPLE_NR; i++) { + rtc->val_to_freq[i].value = readl(rtc->regs + rtc_reg); + rtc->val_to_freq[i].freq = 0; + } + + for (i = 0; i < SAMPLE_NR; i++) { + int j = 0; + u32 value = rtc->val_to_freq[i].value; + + while (rtc->val_to_freq[j].freq) { + if (rtc->val_to_freq[j].value == value) { + rtc->val_to_freq[j].freq++; + break; + } + j++; + } + + if (!rtc->val_to_freq[j].freq) { + rtc->val_to_freq[j].value = value; + rtc->val_to_freq[j].freq = 1; + } + + if (rtc->val_to_freq[j].freq > max) { + index_max = j; + max = rtc->val_to_freq[j].freq; + } + + /* + * If a value already has half of the sample this is the most + * frequent one and we can stop the research right now + */ + if (max > SAMPLE_NR / 2) + break; + } + + return rtc->val_to_freq[index_max].value; +} + +static void armada38x_clear_isr(struct armada38x_rtc *rtc) +{ + u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); + + writel(val & ~SOC_RTC_ALARM1, rtc->regs_soc + SOC_RTC_INTERRUPT); +} + +static void armada38x_unmask_interrupt(struct armada38x_rtc *rtc) +{ + u32 val = readl(rtc->regs_soc + SOC_RTC_INTERRUPT); + + writel(val | SOC_RTC_ALARM1_MASK, rtc->regs_soc + SOC_RTC_INTERRUPT); +} + +static void armada8k_clear_isr(struct armada38x_rtc *rtc) +{ + writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_ISR); +} + +static void armada8k_unmask_interrupt(struct armada38x_rtc *rtc) +{ + writel(RTC_8K_ALARM2, rtc->regs_soc + RTC_8K_IMR); +} + +static int armada38x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long time, flags; + + spin_lock_irqsave(&rtc->lock, flags); + time = rtc->data->read_rtc_reg(rtc, RTC_TIME); + spin_unlock_irqrestore(&rtc->lock, flags); + + rtc_time_to_tm(time, tm); + + return 0; +} + +static void armada38x_rtc_reset(struct armada38x_rtc *rtc) +{ + u32 reg; + + reg = rtc->data->read_rtc_reg(rtc, RTC_CONF_TEST); + /* If bits [7:0] are non-zero, assume RTC was uninitialized */ + if (reg & 0xff) { + rtc_delayed_write(0, rtc, RTC_CONF_TEST); + msleep(500); /* Oscillator startup time */ + rtc_delayed_write(0, rtc, RTC_TIME); + rtc_delayed_write(SOC_RTC_ALARM1 | SOC_RTC_ALARM2, rtc, + RTC_STATUS); + rtc_delayed_write(RTC_NOMINAL_TIMING, rtc, RTC_CCR); + } + rtc->initialized = true; +} + +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; + + if (!rtc->initialized) + armada38x_rtc_reset(rtc); + + spin_lock_irqsave(&rtc->lock, flags); + rtc_delayed_write(time, rtc, RTC_TIME); + spin_unlock_irqrestore(&rtc->lock, flags); + +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 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + u32 val; + + spin_lock_irqsave(&rtc->lock, flags); + + time = rtc->data->read_rtc_reg(rtc, reg); + val = rtc->data->read_rtc_reg(rtc, reg_irq) & RTC_IRQ_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); + u32 reg = ALARM_REG(RTC_ALARM1, rtc->data->alarm); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + unsigned long time, flags; + int ret = 0; + + ret = rtc_tm_to_time(&alrm->time, &time); + + if (ret) + goto out; + + spin_lock_irqsave(&rtc->lock, flags); + + rtc_delayed_write(time, rtc, reg); + + if (alrm->enabled) { + rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); + rtc->data->unmask_interrupt(rtc); + } + + 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); + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + if (enabled) + rtc_delayed_write(RTC_IRQ_AL_EN, rtc, reg_irq); + else + rtc_delayed_write(0, rtc, reg_irq); + + 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; + u32 reg_irq = ALARM_REG(RTC_IRQ1_CONF, rtc->data->alarm); + + dev_dbg(&rtc->rtc_dev->dev, "%s:irq(%d)\n", __func__, irq); + + spin_lock(&rtc->lock); + + rtc->data->clear_isr(rtc); + val = rtc->data->read_rtc_reg(rtc, reg_irq); + /* disable all the interrupts for alarm*/ + rtc_delayed_write(0, rtc, reg_irq); + /* Ack the event */ + rtc_delayed_write(1 << rtc->data->alarm, rtc, RTC_STATUS); + + spin_unlock(&rtc->lock); + + if (val & RTC_IRQ_FREQ_EN) { + if (val & RTC_IRQ_FREQ_1HZ) + event |= RTC_UF; + else + event |= RTC_PF; + } + + rtc_update_irq(rtc->rtc_dev, 1, event); + + return IRQ_HANDLED; +} + +/* + * The information given in the Armada 388 functional spec is complex. + * They give two different formulas for calculating the offset value, + * but when considering "Offset" as an 8-bit signed integer, they both + * reduce down to (we shall rename "Offset" as "val" here): + * + * val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768 + * + * Converting to time, f = 1/t: + * val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768 + * + * => t_measured / t_ideal = val * resolution + 1 + * + * "offset" in the RTC interface is defined as: + * t = t0 * (1 + offset * 1e-9) + * where t is the desired period, t0 is the measured period with a zero + * offset, which is t_measured above. With t0 = t_measured and t = t_ideal, + * offset = (t_ideal / t_measured - 1) / 1e-9 + * + * => t_ideal / t_measured = offset * 1e-9 + 1 + * + * so: + * + * offset * 1e-9 + 1 = 1 / (val * resolution + 1) + * + * We want "resolution" to be an integer, so resolution = R * 1e-9, giving + * offset = 1e18 / (val * R + 1e9) - 1e9 + * val = (1e18 / (offset + 1e9) - 1e9) / R + * with a common transformation: + * f(x) = 1e18 / (x + 1e9) - 1e9 + * offset = f(val * R) + * val = f(offset) / R + * + * Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb). + */ +static long armada38x_ppb_convert(long ppb) +{ + long div = ppb + 1000000000L; + + return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L; +} + +static int armada38x_rtc_read_offset(struct device *dev, long *offset) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long ccr, flags; + long ppb_cor; + + spin_lock_irqsave(&rtc->lock, flags); + ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR); + spin_unlock_irqrestore(&rtc->lock, flags); + + ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr; + /* ppb_cor + 1000000000L can never be zero */ + *offset = armada38x_ppb_convert(ppb_cor); + + return 0; +} + +static int armada38x_rtc_set_offset(struct device *dev, long offset) +{ + struct armada38x_rtc *rtc = dev_get_drvdata(dev); + unsigned long ccr = 0; + long ppb_cor, off; + + /* + * The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we + * need to clamp the input. This equates to -484270 .. 488558. + * Not only is this to stop out of range "off" but also to + * avoid the division by zero in armada38x_ppb_convert(). + */ + offset = clamp(offset, -484270L, 488558L); + + ppb_cor = armada38x_ppb_convert(offset); + + /* + * Use low update mode where possible, which gives a better + * resolution of correction. + */ + off = DIV_ROUND_CLOSEST(ppb_cor, 954); + if (off > 127 || off < -128) { + ccr = RTC_CCR_MODE; + off = DIV_ROUND_CLOSEST(ppb_cor, 3815); + } + + /* + * Armada 388 requires a bit pattern in bits 14..8 depending on + * the sign bit: { 0, ~S, S, S, S, S, S } + */ + ccr |= (off & 0x3fff) ^ 0x2000; + rtc_delayed_write(ccr, rtc, RTC_CCR); + + return 0; +} + +static const 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, + .read_offset = armada38x_rtc_read_offset, + .set_offset = armada38x_rtc_set_offset, +}; + +static const struct rtc_class_ops armada38x_rtc_ops_noirq = { + .read_time = armada38x_rtc_read_time, + .set_time = armada38x_rtc_set_time, + .read_alarm = armada38x_rtc_read_alarm, + .read_offset = armada38x_rtc_read_offset, + .set_offset = armada38x_rtc_set_offset, +}; + +static const struct armada38x_rtc_data armada38x_data = { + .update_mbus_timing = rtc_update_38x_mbus_timing_params, + .read_rtc_reg = read_rtc_register_38x_wa, + .clear_isr = armada38x_clear_isr, + .unmask_interrupt = armada38x_unmask_interrupt, + .alarm = ALARM1, +}; + +static const struct armada38x_rtc_data armada8k_data = { + .update_mbus_timing = rtc_update_8k_mbus_timing_params, + .read_rtc_reg = read_rtc_register, + .clear_isr = armada8k_clear_isr, + .unmask_interrupt = armada8k_unmask_interrupt, + .alarm = ALARM2, +}; + +#ifdef CONFIG_OF +static const struct of_device_id armada38x_rtc_of_match_table[] = { + { + .compatible = "marvell,armada-380-rtc", + .data = &armada38x_data, + }, + { + .compatible = "marvell,armada-8k-rtc", + .data = &armada8k_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, armada38x_rtc_of_match_table); +#endif + +static __init int armada38x_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct armada38x_rtc *rtc; + const struct of_device_id *match; + int ret; + + match = of_match_device(armada38x_rtc_of_match_table, &pdev->dev); + if (!match) + return -ENODEV; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct armada38x_rtc), + GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->val_to_freq = devm_kcalloc(&pdev->dev, SAMPLE_NR, + sizeof(struct value_to_freq), GFP_KERNEL); + if (!rtc->val_to_freq) + return -ENOMEM; + + spin_lock_init(&rtc->lock); + + 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; + } + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + 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; + } + platform_set_drvdata(pdev, rtc); + + if (rtc->irq != -1) { + device_init_wakeup(&pdev->dev, 1); + rtc->rtc_dev->ops = &armada38x_rtc_ops; + } else { + /* + * If there is no interrupt available then we can't + * use the alarm + */ + rtc->rtc_dev->ops = &armada38x_rtc_ops_noirq; + } + rtc->data = (struct armada38x_rtc_data *)match->data; + + /* Update RTC-MBUS bridge timing parameters */ + rtc->data->update_mbus_timing(rtc); + + ret = rtc_register_device(rtc->rtc_dev); + if (ret) + dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret); + + return ret; +} + +#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); + + /* Update RTC-MBUS bridge timing parameters */ + rtc->data->update_mbus_timing(rtc); + + return disable_irq_wake(rtc->irq); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(armada38x_rtc_pm_ops, + armada38x_rtc_suspend, armada38x_rtc_resume); + +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..6ef0c887e --- /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 + 1); + 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) - 1; + 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, + "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-asm9260.c b/drivers/rtc/rtc-asm9260.c new file mode 100644 index 000000000..d36534965 --- /dev/null +++ b/drivers/rtc/rtc-asm9260.c @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2016 Oleksij Rempel <linux@rempel-privat.de> + * + * 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/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +/* Miscellaneous registers */ +/* Interrupt Location Register */ +#define HW_ILR 0x00 +#define BM_RTCALF BIT(1) +#define BM_RTCCIF BIT(0) + +/* Clock Control Register */ +#define HW_CCR 0x08 +/* Calibration counter disable */ +#define BM_CCALOFF BIT(4) +/* Reset internal oscillator divider */ +#define BM_CTCRST BIT(1) +/* Clock Enable */ +#define BM_CLKEN BIT(0) + +/* Counter Increment Interrupt Register */ +#define HW_CIIR 0x0C +#define BM_CIIR_IMYEAR BIT(7) +#define BM_CIIR_IMMON BIT(6) +#define BM_CIIR_IMDOY BIT(5) +#define BM_CIIR_IMDOW BIT(4) +#define BM_CIIR_IMDOM BIT(3) +#define BM_CIIR_IMHOUR BIT(2) +#define BM_CIIR_IMMIN BIT(1) +#define BM_CIIR_IMSEC BIT(0) + +/* Alarm Mask Register */ +#define HW_AMR 0x10 +#define BM_AMR_IMYEAR BIT(7) +#define BM_AMR_IMMON BIT(6) +#define BM_AMR_IMDOY BIT(5) +#define BM_AMR_IMDOW BIT(4) +#define BM_AMR_IMDOM BIT(3) +#define BM_AMR_IMHOUR BIT(2) +#define BM_AMR_IMMIN BIT(1) +#define BM_AMR_IMSEC BIT(0) +#define BM_AMR_OFF 0xff + +/* Consolidated time registers */ +#define HW_CTIME0 0x14 +#define BM_CTIME0_DOW_S 24 +#define BM_CTIME0_DOW_M 0x7 +#define BM_CTIME0_HOUR_S 16 +#define BM_CTIME0_HOUR_M 0x1f +#define BM_CTIME0_MIN_S 8 +#define BM_CTIME0_MIN_M 0x3f +#define BM_CTIME0_SEC_S 0 +#define BM_CTIME0_SEC_M 0x3f + +#define HW_CTIME1 0x18 +#define BM_CTIME1_YEAR_S 16 +#define BM_CTIME1_YEAR_M 0xfff +#define BM_CTIME1_MON_S 8 +#define BM_CTIME1_MON_M 0xf +#define BM_CTIME1_DOM_S 0 +#define BM_CTIME1_DOM_M 0x1f + +#define HW_CTIME2 0x1C +#define BM_CTIME2_DOY_S 0 +#define BM_CTIME2_DOY_M 0xfff + +/* Time counter registers */ +#define HW_SEC 0x20 +#define HW_MIN 0x24 +#define HW_HOUR 0x28 +#define HW_DOM 0x2C +#define HW_DOW 0x30 +#define HW_DOY 0x34 +#define HW_MONTH 0x38 +#define HW_YEAR 0x3C + +#define HW_CALIBRATION 0x40 +#define BM_CALDIR_BACK BIT(17) +#define BM_CALVAL_M 0x1ffff + +/* General purpose registers */ +#define HW_GPREG0 0x44 +#define HW_GPREG1 0x48 +#define HW_GPREG2 0x4C +#define HW_GPREG3 0x50 +#define HW_GPREG4 0x54 + +/* Alarm register group */ +#define HW_ALSEC 0x60 +#define HW_ALMIN 0x64 +#define HW_ALHOUR 0x68 +#define HW_ALDOM 0x6C +#define HW_ALDOW 0x70 +#define HW_ALDOY 0x74 +#define HW_ALMON 0x78 +#define HW_ALYEAR 0x7C + +struct asm9260_rtc_priv { + struct device *dev; + void __iomem *iobase; + struct rtc_device *rtc; + struct clk *clk; +}; + +static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id) +{ + struct asm9260_rtc_priv *priv = dev_id; + u32 isr; + unsigned long events = 0; + + mutex_lock(&priv->rtc->ops_lock); + isr = ioread32(priv->iobase + HW_CIIR); + if (!isr) { + mutex_unlock(&priv->rtc->ops_lock); + return IRQ_NONE; + } + + iowrite32(0, priv->iobase + HW_CIIR); + mutex_unlock(&priv->rtc->ops_lock); + + events |= RTC_AF | RTC_IRQF; + + rtc_update_irq(priv->rtc, 1, events); + + return IRQ_HANDLED; +} + +static int asm9260_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + u32 ctime0, ctime1, ctime2; + + ctime0 = ioread32(priv->iobase + HW_CTIME0); + ctime1 = ioread32(priv->iobase + HW_CTIME1); + ctime2 = ioread32(priv->iobase + HW_CTIME2); + + if (ctime1 != ioread32(priv->iobase + HW_CTIME1)) { + /* + * woops, counter flipped right now. Now we are safe + * to reread. + */ + ctime0 = ioread32(priv->iobase + HW_CTIME0); + ctime1 = ioread32(priv->iobase + HW_CTIME1); + ctime2 = ioread32(priv->iobase + HW_CTIME2); + } + + tm->tm_sec = (ctime0 >> BM_CTIME0_SEC_S) & BM_CTIME0_SEC_M; + tm->tm_min = (ctime0 >> BM_CTIME0_MIN_S) & BM_CTIME0_MIN_M; + tm->tm_hour = (ctime0 >> BM_CTIME0_HOUR_S) & BM_CTIME0_HOUR_M; + tm->tm_wday = (ctime0 >> BM_CTIME0_DOW_S) & BM_CTIME0_DOW_M; + + tm->tm_mday = (ctime1 >> BM_CTIME1_DOM_S) & BM_CTIME1_DOM_M; + tm->tm_mon = (ctime1 >> BM_CTIME1_MON_S) & BM_CTIME1_MON_M; + tm->tm_year = (ctime1 >> BM_CTIME1_YEAR_S) & BM_CTIME1_YEAR_M; + + tm->tm_yday = (ctime2 >> BM_CTIME2_DOY_S) & BM_CTIME2_DOY_M; + + return 0; +} + +static int asm9260_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + /* + * make sure SEC counter will not flip other counter on write time, + * real value will be written at the enf of sequence. + */ + iowrite32(0, priv->iobase + HW_SEC); + + iowrite32(tm->tm_year, priv->iobase + HW_YEAR); + iowrite32(tm->tm_mon, priv->iobase + HW_MONTH); + iowrite32(tm->tm_mday, priv->iobase + HW_DOM); + iowrite32(tm->tm_wday, priv->iobase + HW_DOW); + iowrite32(tm->tm_yday, priv->iobase + HW_DOY); + iowrite32(tm->tm_hour, priv->iobase + HW_HOUR); + iowrite32(tm->tm_min, priv->iobase + HW_MIN); + iowrite32(tm->tm_sec, priv->iobase + HW_SEC); + + return 0; +} + +static int asm9260_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + alrm->time.tm_year = ioread32(priv->iobase + HW_ALYEAR); + alrm->time.tm_mon = ioread32(priv->iobase + HW_ALMON); + alrm->time.tm_mday = ioread32(priv->iobase + HW_ALDOM); + alrm->time.tm_wday = ioread32(priv->iobase + HW_ALDOW); + alrm->time.tm_yday = ioread32(priv->iobase + HW_ALDOY); + alrm->time.tm_hour = ioread32(priv->iobase + HW_ALHOUR); + alrm->time.tm_min = ioread32(priv->iobase + HW_ALMIN); + alrm->time.tm_sec = ioread32(priv->iobase + HW_ALSEC); + + alrm->enabled = ioread32(priv->iobase + HW_AMR) ? 1 : 0; + alrm->pending = ioread32(priv->iobase + HW_CIIR) ? 1 : 0; + + return rtc_valid_tm(&alrm->time); +} + +static int asm9260_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + iowrite32(alrm->time.tm_year, priv->iobase + HW_ALYEAR); + iowrite32(alrm->time.tm_mon, priv->iobase + HW_ALMON); + iowrite32(alrm->time.tm_mday, priv->iobase + HW_ALDOM); + iowrite32(alrm->time.tm_wday, priv->iobase + HW_ALDOW); + iowrite32(alrm->time.tm_yday, priv->iobase + HW_ALDOY); + iowrite32(alrm->time.tm_hour, priv->iobase + HW_ALHOUR); + iowrite32(alrm->time.tm_min, priv->iobase + HW_ALMIN); + iowrite32(alrm->time.tm_sec, priv->iobase + HW_ALSEC); + + iowrite32(alrm->enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR); + + return 0; +} + +static int asm9260_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct asm9260_rtc_priv *priv = dev_get_drvdata(dev); + + iowrite32(enabled ? 0 : BM_AMR_OFF, priv->iobase + HW_AMR); + return 0; +} + +static const struct rtc_class_ops asm9260_rtc_ops = { + .read_time = asm9260_rtc_read_time, + .set_time = asm9260_rtc_set_time, + .read_alarm = asm9260_rtc_read_alarm, + .set_alarm = asm9260_rtc_set_alarm, + .alarm_irq_enable = asm9260_alarm_irq_enable, +}; + +static int asm9260_rtc_probe(struct platform_device *pdev) +{ + struct asm9260_rtc_priv *priv; + struct device *dev = &pdev->dev; + struct resource *res; + int irq_alarm, ret; + u32 ccr; + + priv = devm_kzalloc(dev, sizeof(struct asm9260_rtc_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + platform_set_drvdata(pdev, priv); + + irq_alarm = platform_get_irq(pdev, 0); + if (irq_alarm < 0) { + dev_err(dev, "No alarm IRQ resource defined\n"); + return irq_alarm; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->iobase = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->iobase)) + return PTR_ERR(priv->iobase); + + priv->clk = devm_clk_get(dev, "ahb"); + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "Failed to enable clk!\n"); + return ret; + } + + ccr = ioread32(priv->iobase + HW_CCR); + /* if dev is not enabled, reset it */ + if ((ccr & (BM_CLKEN | BM_CTCRST)) != BM_CLKEN) { + iowrite32(BM_CTCRST, priv->iobase + HW_CCR); + ccr = 0; + } + + iowrite32(BM_CLKEN | ccr, priv->iobase + HW_CCR); + iowrite32(0, priv->iobase + HW_CIIR); + iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR); + + priv->rtc = devm_rtc_device_register(dev, dev_name(dev), + &asm9260_rtc_ops, THIS_MODULE); + if (IS_ERR(priv->rtc)) { + ret = PTR_ERR(priv->rtc); + dev_err(dev, "Failed to register RTC device: %d\n", ret); + goto err_return; + } + + ret = devm_request_threaded_irq(dev, irq_alarm, NULL, + asm9260_rtc_irq, IRQF_ONESHOT, + dev_name(dev), priv); + if (ret < 0) { + dev_err(dev, "can't get irq %i, err %d\n", + irq_alarm, ret); + goto err_return; + } + + return 0; + +err_return: + clk_disable_unprepare(priv->clk); + return ret; +} + +static int asm9260_rtc_remove(struct platform_device *pdev) +{ + struct asm9260_rtc_priv *priv = platform_get_drvdata(pdev); + + /* Disable alarm matching */ + iowrite32(BM_AMR_OFF, priv->iobase + HW_AMR); + clk_disable_unprepare(priv->clk); + return 0; +} + +static const struct of_device_id asm9260_dt_ids[] = { + { .compatible = "alphascale,asm9260-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, asm9260_dt_ids); + +static struct platform_driver asm9260_rtc_driver = { + .probe = asm9260_rtc_probe, + .remove = asm9260_rtc_remove, + .driver = { + .name = "asm9260-rtc", + .of_match_table = asm9260_dt_ids, + }, +}; + +module_platform_driver(asm9260_rtc_driver); + +MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>"); +MODULE_DESCRIPTION("Alphascale asm9260 SoC Realtime Clock Driver (RTC)"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c new file mode 100644 index 000000000..caa71d04e --- /dev/null +++ b/drivers/rtc/rtc-at91rm9200.c @@ -0,0 +1,558 @@ +/* + * 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/bcd.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spinlock.h> +#include <linux/suspend.h> +#include <linux/time.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) + +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 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 struct clk *sclk; + +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. + */ + 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_year = -1; + + alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM) + ? 1 : 0; + + dev_dbg(dev, "%s(): %02d-%02d %02d:%02d:%02d %sabled\n", __func__, + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + alrm->enabled ? "en" : "dis"); + + 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); + + 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__, + 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) +{ + 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; + } + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + platform_set_drvdata(pdev, rtc); + + sclk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(sclk)) + return PTR_ERR(sclk); + + ret = clk_prepare_enable(sclk); + if (ret) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return ret; + } + + 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); + goto err_clk; + } + + /* 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->ops = &at91_rtc_ops; + rtc->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtc->range_max = RTC_TIMESTAMP_END_2099; + ret = rtc_register_device(rtc); + if (ret) + goto err_clk; + + /* 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; + +err_clk: + clk_disable_unprepare(sclk); + + return ret; +} + +/* + * 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); + + clk_disable_unprepare(sclk); + + 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_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + + 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..ee71e647f --- /dev/null +++ b/drivers/rtc/rtc-at91sam9.c @@ -0,0 +1,595 @@ +/* + * "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/clk.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/suspend.h> +#include <linux/time.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 = 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 = { + .name = "gpbr", + .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; + + spin_lock_init(&rtc->lock); + 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); + + ret = clk_prepare_enable(rtc->sclk); + if (ret) { + dev_err(&pdev->dev, "Could not enable slow clock\n"); + return ret; + } + + sclk_rate = clk_get_rate(rtc->sclk); + if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) { + dev_err(&pdev->dev, "Invalid slow clock rate\n"); + ret = -EINVAL; + goto err_clk; + } + + 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)) { + ret = PTR_ERR(rtc->rtcdev); + goto err_clk; + } + + /* 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); + goto err_clk; + } + + /* 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; + +err_clk: + clk_disable_unprepare(rtc->sclk); + + return ret; +} + +/* + * 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)); + + 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..7c5530c71 --- /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 0; +} + +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 const 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-bq32k.c b/drivers/rtc/rtc-bq32k.c new file mode 100644 index 000000000..ef5274100 --- /dev/null +++ b/drivers/rtc/rtc-bq32k.c @@ -0,0 +1,337 @@ +/* + * 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 */ +#define BQ32K_TCFE BIT(6) /* Trickle charge FET bypass */ + +#define MAX_LEN 10 /* Maximum number of consecutive + * register for this particular RTC. + */ + +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[MAX_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, ®s, 0, sizeof(regs)); + if (error) + return error; + + /* + * In case of oscillator failure, the register contents should be + * considered invalid. The flag is cleared the next time the RTC is set. + */ + if (regs.minutes & BQ32K_OF) + return -EINVAL; + + tm->tm_sec = bcd2bin(regs.seconds & BQ32K_SECONDS_MASK); + tm->tm_min = bcd2bin(regs.minutes & BQ32K_MINUTES_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 0; +} + +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, ®s, 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, ®, BQ32K_CFG2, 1); + if (error) + return error; + + reg = 0x20; + error = bq32k_write(dev, ®, BQ32K_TCH2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle RTC battery charge.\n"); + return 0; +} + +static ssize_t bq32k_sysfs_show_tricklecharge_bypass(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int reg, error; + + error = bq32k_read(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + return sprintf(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0); +} + +static ssize_t bq32k_sysfs_store_tricklecharge_bypass(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int reg, enable, error; + + if (kstrtoint(buf, 0, &enable)) + return -EINVAL; + + error = bq32k_read(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + if (enable) { + reg |= BQ32K_TCFE; + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + dev_info(dev, "Enabled trickle charge FET bypass.\n"); + } else { + reg &= ~BQ32K_TCFE; + error = bq32k_write(dev, ®, BQ32K_CFG2, 1); + if (error) + return error; + + dev_info(dev, "Disabled trickle charge FET bypass.\n"); + } + + return count; +} + +static DEVICE_ATTR(trickle_charge_bypass, 0644, + bq32k_sysfs_show_tricklecharge_bypass, + bq32k_sysfs_store_tricklecharge_bypass); + +static int bq32k_sysfs_register(struct device *dev) +{ + return device_create_file(dev, &dev_attr_trickle_charge_bypass); +} + +static void bq32k_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_trickle_charge_bypass); +} + +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, ®, BQ32K_SECONDS, 1); + if (!error && (reg & BQ32K_STOP)) { + dev_warn(dev, "Oscillator was halted. Restarting...\n"); + reg &= ~BQ32K_STOP; + error = bq32k_write(dev, ®, BQ32K_SECONDS, 1); + } + if (error) + return error; + + /* Check Oscillator Failure flag */ + error = bq32k_read(dev, ®, BQ32K_MINUTES, 1); + if (error) + return error; + if (reg & BQ32K_OF) + dev_warn(dev, "Oscillator Failure. Check RTC battery.\n"); + + if (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); + + error = bq32k_sysfs_register(&client->dev); + if (error) { + dev_err(&client->dev, + "Unable to create sysfs entries for rtc bq32000\n"); + return error; + } + + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static int bq32k_remove(struct i2c_client *client) +{ + bq32k_sysfs_unregister(&client->dev); + + return 0; +} + +static const struct i2c_device_id bq32k_id[] = { + { "bq32000", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, bq32k_id); + +static const struct of_device_id bq32k_of_match[] = { + { .compatible = "ti,bq32000" }, + { } +}; +MODULE_DEVICE_TABLE(of, bq32k_of_match); + +static struct i2c_driver bq32k_driver = { + .driver = { + .name = "bq32k", + .of_match_table = of_match_ptr(bq32k_of_match), + }, + .probe = bq32k_probe, + .remove = bq32k_remove, + .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..113493b52 --- /dev/null +++ b/drivers/rtc/rtc-bq4802.c @@ -0,0 +1,201 @@ +/* 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 bq4802 *p = dev_get_drvdata(dev); + 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 bq4802 *p = dev_get_drvdata(dev); + 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)); + if (!p->regs){ + err = -ENOMEM; + goto out; + } + 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-brcmstb-waketimer.c b/drivers/rtc/rtc-brcmstb-waketimer.c new file mode 100644 index 000000000..1abc83978 --- /dev/null +++ b/drivers/rtc/rtc-brcmstb-waketimer.c @@ -0,0 +1,341 @@ +/* + * Copyright © 2014-2017 Broadcom + * + * 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 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/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irqreturn.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_wakeup.h> +#include <linux/reboot.h> +#include <linux/rtc.h> +#include <linux/stat.h> +#include <linux/suspend.h> + +struct brcmstb_waketmr { + struct rtc_device *rtc; + struct device *dev; + void __iomem *base; + int irq; + struct notifier_block reboot_notifier; + struct clk *clk; + u32 rate; +}; + +#define BRCMSTB_WKTMR_EVENT 0x00 +#define BRCMSTB_WKTMR_COUNTER 0x04 +#define BRCMSTB_WKTMR_ALARM 0x08 +#define BRCMSTB_WKTMR_PRESCALER 0x0C +#define BRCMSTB_WKTMR_PRESCALER_VAL 0x10 + +#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000 + +static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer) +{ + writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT); + (void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); +} + +static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer, + unsigned int secs) +{ + brcmstb_waketmr_clear_alarm(timer); + + /* Make sure we are actually counting in seconds */ + writel_relaxed(timer->rate, timer->base + BRCMSTB_WKTMR_PRESCALER); + + writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM); +} + +static irqreturn_t brcmstb_waketmr_irq(int irq, void *data) +{ + struct brcmstb_waketmr *timer = data; + + pm_wakeup_event(timer->dev, 0); + + return IRQ_HANDLED; +} + +struct wktmr_time { + u32 sec; + u32 pre; +}; + +static void wktmr_read(struct brcmstb_waketmr *timer, + struct wktmr_time *t) +{ + u32 tmp; + + do { + t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER); + tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL); + } while (tmp >= timer->rate); + + t->pre = timer->rate - tmp; +} + +static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer) +{ + struct device *dev = timer->dev; + int ret = 0; + + if (device_may_wakeup(dev)) { + ret = enable_irq_wake(timer->irq); + if (ret) { + dev_err(dev, "failed to enable wake-up interrupt\n"); + return ret; + } + } + + return ret; +} + +/* If enabled as a wakeup-source, arm the timer when powering off */ +static int brcmstb_waketmr_reboot(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct brcmstb_waketmr *timer; + + timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier); + + /* Set timer for cold boot */ + if (action == SYS_POWER_OFF) + brcmstb_waketmr_prepare_suspend(timer); + + return NOTIFY_DONE; +} + +static int brcmstb_waketmr_gettime(struct device *dev, + struct rtc_time *tm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + struct wktmr_time now; + + wktmr_read(timer, &now); + + rtc_time_to_tm(now.sec, tm); + + return 0; +} + +static int brcmstb_waketmr_settime(struct device *dev, + struct rtc_time *tm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + + sec = rtc_tm_to_time64(tm); + + writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER); + + return 0; +} + +static int brcmstb_waketmr_getalarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + u32 reg; + + sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM); + if (sec != 0) { + /* Alarm is enabled */ + alarm->enabled = 1; + rtc_time64_to_tm(sec, &alarm->time); + } + + reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT); + alarm->pending = !!(reg & 1); + + return 0; +} + +static int brcmstb_waketmr_setalarm(struct device *dev, + struct rtc_wkalrm *alarm) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + time64_t sec; + + if (alarm->enabled) + sec = rtc_tm_to_time64(&alarm->time); + else + sec = 0; + + brcmstb_waketmr_set_alarm(timer, sec); + + return 0; +} + +/* + * Does not do much but keep the RTC class happy. We always support + * alarms. + */ +static int brcmstb_waketmr_alarm_enable(struct device *dev, + unsigned int enabled) +{ + return 0; +} + +static const struct rtc_class_ops brcmstb_waketmr_ops = { + .read_time = brcmstb_waketmr_gettime, + .set_time = brcmstb_waketmr_settime, + .read_alarm = brcmstb_waketmr_getalarm, + .set_alarm = brcmstb_waketmr_setalarm, + .alarm_irq_enable = brcmstb_waketmr_alarm_enable, +}; + +static int brcmstb_waketmr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct brcmstb_waketmr *timer; + struct resource *res; + int ret; + + timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL); + if (!timer) + return -ENOMEM; + + platform_set_drvdata(pdev, timer); + timer->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + timer->base = devm_ioremap_resource(dev, res); + if (IS_ERR(timer->base)) + return PTR_ERR(timer->base); + + timer->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(timer->rtc)) + return PTR_ERR(timer->rtc); + + /* + * Set wakeup capability before requesting wakeup interrupt, so we can + * process boot-time "wakeups" (e.g., from S5 soft-off) + */ + device_set_wakeup_capable(dev, true); + device_wakeup_enable(dev); + + timer->irq = platform_get_irq(pdev, 0); + if (timer->irq < 0) + return -ENODEV; + + timer->clk = devm_clk_get(dev, NULL); + if (!IS_ERR(timer->clk)) { + ret = clk_prepare_enable(timer->clk); + if (ret) + return ret; + timer->rate = clk_get_rate(timer->clk); + if (!timer->rate) + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; + } else { + timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ; + timer->clk = NULL; + } + + ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0, + "brcmstb-waketimer", timer); + if (ret < 0) + goto err_clk; + + timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot; + register_reboot_notifier(&timer->reboot_notifier); + + timer->rtc->ops = &brcmstb_waketmr_ops; + timer->rtc->range_max = U32_MAX; + + ret = rtc_register_device(timer->rtc); + if (ret) { + dev_err(dev, "unable to register device\n"); + goto err_notifier; + } + + dev_info(dev, "registered, with irq %d\n", timer->irq); + + return 0; + +err_notifier: + unregister_reboot_notifier(&timer->reboot_notifier); + +err_clk: + if (timer->clk) + clk_disable_unprepare(timer->clk); + + return ret; +} + +static int brcmstb_waketmr_remove(struct platform_device *pdev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev); + + unregister_reboot_notifier(&timer->reboot_notifier); + clk_disable_unprepare(timer->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int brcmstb_waketmr_suspend(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + + return brcmstb_waketmr_prepare_suspend(timer); +} + +static int brcmstb_waketmr_resume(struct device *dev) +{ + struct brcmstb_waketmr *timer = dev_get_drvdata(dev); + int ret; + + if (!device_may_wakeup(dev)) + return 0; + + ret = disable_irq_wake(timer->irq); + + brcmstb_waketmr_clear_alarm(timer); + + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops, + brcmstb_waketmr_suspend, brcmstb_waketmr_resume); + +static const struct of_device_id brcmstb_waketmr_of_match[] = { + { .compatible = "brcm,brcmstb-waketimer" }, + { /* sentinel */ }, +}; + +static struct platform_driver brcmstb_waketmr_driver = { + .probe = brcmstb_waketmr_probe, + .remove = brcmstb_waketmr_remove, + .driver = { + .name = "brcmstb-waketimer", + .pm = &brcmstb_waketmr_pm_ops, + .of_match_table = of_match_ptr(brcmstb_waketmr_of_match), + } +}; +module_platform_driver(brcmstb_waketmr_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Brian Norris"); +MODULE_AUTHOR("Markus Mayer"); +MODULE_DESCRIPTION("Wake-up timer driver for STB chips"); diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c new file mode 100644 index 000000000..8545f0da5 --- /dev/null +++ b/drivers/rtc/rtc-cmos.c @@ -0,0 +1,1504 @@ +/* + * 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> +#ifdef CONFIG_X86 +#include <asm/i8259.h> +#include <asm/processor.h> +#include <linux/dmi.h> +#endif + +/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */ +#include <linux/mc146818rtc.h> + +#ifdef CONFIG_ACPI +/* + * Use ACPI SCI to replace HPET interrupt for RTC Alarm event + * + * If cleared, ACPI SCI is only used to wake up the system from suspend + * + * If set, ACPI SCI is used to handle UIE/AIE and system wakeup + */ + +static bool use_acpi_alarm; +module_param(use_acpi_alarm, bool, 0444); + +static inline int cmos_use_acpi_alarm(void) +{ + return use_acpi_alarm; +} +#else /* !CONFIG_ACPI */ + +static inline int cmos_use_acpi_alarm(void) +{ + return 0; +} +#endif + +struct cmos_rtc { + struct rtc_device *rtc; + struct device *dev; + int irq; + struct resource *iomem; + time64_t alarm_expires; + + 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; + + struct rtc_wkalrm saved_wkalrm; +}; + +/* 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 + +/* Don't use HPET for RTC Alarm event if ACPI Fixed event is used */ +static inline int use_hpet_alarm(void) +{ + return is_hpet_enabled() && !cmos_use_acpi_alarm(); +} + +/*----------------------------------------------------------------*/ + +#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) +{ + /* + * If pm_trace abused the RTC for storage, set the timespec to 0, + * which tells the caller that this RTC value is unusable. + */ + if (!pm_trace_rtc_valid()) + return -EIO; + + /* REVISIT: if the clock has a "century" register, use + * that instead of the heuristic in mc146818_get_time(). + * That'll make Y3K compatility (year > 2070) easy! + */ + mc146818_get_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 mc146818_set_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; + + /* This not only a rtc_op, but also called directly */ + 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. + */ + + 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->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 (use_hpet_alarm()) + 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); + if (use_hpet_alarm()) + hpet_set_rtc_irq_bit(mask); + + if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) { + if (cmos->wake_on) + cmos->wake_on(cmos->dev); + } + + 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); + if (use_hpet_alarm()) + hpet_mask_rtc_irq_bit(mask); + + if ((mask & RTC_AIE) && cmos_use_acpi_alarm()) { + if (cmos->wake_off) + cmos->wake_off(cmos->dev); + } + + cmos_checkintr(cmos, rtc_control); +} + +static int cmos_validate_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_time now; + + cmos_read_time(dev, &now); + + if (!cmos->day_alrm) { + time64_t t_max_date; + time64_t t_alrm; + + t_max_date = rtc_tm_to_time64(&now); + t_max_date += 24 * 60 * 60 - 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one day in the future\n"); + return -EINVAL; + } + } else if (!cmos->mon_alrm) { + struct rtc_time max_date = now; + time64_t t_max_date; + time64_t t_alrm; + int max_mday; + + if (max_date.tm_mon == 11) { + max_date.tm_mon = 0; + max_date.tm_year += 1; + } else { + max_date.tm_mon += 1; + } + max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year); + if (max_date.tm_mday > max_mday) + max_date.tm_mday = max_mday; + + t_max_date = rtc_tm_to_time64(&max_date); + t_max_date -= 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one month in the future\n"); + return -EINVAL; + } + } else { + struct rtc_time max_date = now; + time64_t t_max_date; + time64_t t_alrm; + int max_mday; + + max_date.tm_year += 1; + max_mday = rtc_month_days(max_date.tm_mon, max_date.tm_year); + if (max_date.tm_mday > max_mday) + max_date.tm_mday = max_mday; + + t_max_date = rtc_tm_to_time64(&max_date); + t_max_date -= 1; + t_alrm = rtc_tm_to_time64(&t->time); + if (t_alrm > t_max_date) { + dev_err(dev, + "Alarms can be up to one year in the future\n"); + return -EINVAL; + } + } + + return 0; +} + +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; + int ret; + + /* This not only a rtc_op, but also called directly */ + if (!is_valid_irq(cmos->irq)) + return -EIO; + + ret = cmos_validate_alarm(dev, t); + if (ret < 0) + return ret; + + 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; + + spin_lock_irq(&rtc_lock); + rtc_control = CMOS_READ(RTC_CONTROL); + spin_unlock_irq(&rtc_lock); + + 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); + } + + if (use_hpet_alarm()) { + /* + * 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); + + cmos->alarm_expires = rtc_tm_to_time64(&t->time); + + return 0; +} + +static int cmos_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned long flags; + + 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 IS_ENABLED(CONFIG_RTC_INTF_PROC) + +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", + use_hpet_alarm() ? "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, +}; + +static const struct rtc_class_ops cmos_rtc_ops_no_alarm = { + .read_time = cmos_read_time, + .set_time = cmos_set_time, + .proc = cmos_procfs, +}; + +/*----------------------------------------------------------------*/ + +/* + * 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 int cmos_nvram_read(void *priv, unsigned int off, void *val, + size_t count) +{ + unsigned char *buf = val; + int retval; + + 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 int cmos_nvram_write(void *priv, unsigned int off, void *val, + size_t count) +{ + struct cmos_rtc *cmos = priv; + unsigned char *buf = val; + int retval; + + /* 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 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 (use_hpet_alarm()) + 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); + if (use_hpet_alarm()) + 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; + struct nvmem_config nvmem_cfg = { + .name = "cmos_nvram", + .word_size = 1, + .stride = 1, + .reg_read = cmos_nvram_read, + .reg_write = cmos_nvram_write, + .priv = &cmos_rtc, + }; + + /* 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 = devm_rtc_allocate_device(dev); + 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; + if (use_hpet_alarm()) + 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); + + if (is_valid_irq(rtc_irq) && !(rtc_control & RTC_24H)) { + dev_warn(dev, "only 24-hr supported\n"); + retval = -ENXIO; + goto cleanup1; + } + + if (use_hpet_alarm()) + hpet_rtc_timer_init(); + + if (is_valid_irq(rtc_irq)) { + irq_handler_t rtc_cmos_int_handler; + + if (use_hpet_alarm()) { + rtc_cmos_int_handler = hpet_rtc_interrupt; + retval = hpet_register_irq_handler(cmos_interrupt); + if (retval) { + hpet_mask_rtc_irq_bit(RTC_IRQMASK); + 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; + } + + cmos_rtc.rtc->ops = &cmos_rtc_ops; + } else { + cmos_rtc.rtc->ops = &cmos_rtc_ops_no_alarm; + } + + cmos_rtc.rtc->nvram_old_abi = true; + retval = rtc_register_device(cmos_rtc.rtc); + if (retval) + goto cleanup2; + + /* export at least the first block of NVRAM */ + nvmem_cfg.size = address_space - NVRAM_OFFSET; + if (rtc_nvmem_register(cmos_rtc.rtc, &nvmem_cfg)) + dev_err(dev, "nvmem registration failed\n"); + + dev_info(dev, "%s%s, %d 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" : "", + nvmem_cfg.size, + use_hpet_alarm() ? ", hpet irqs" : ""); + + return 0; + +cleanup2: + if (is_valid_irq(rtc_irq)) + free_irq(rtc_irq, cmos_rtc.rtc); +cleanup1: + cmos_rtc.dev = NULL; +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 cmos_do_remove(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct resource *ports; + + cmos_do_shutdown(cmos->irq); + + if (is_valid_irq(cmos->irq)) { + free_irq(cmos->irq, cmos->rtc); + if (use_hpet_alarm()) + hpet_unregister_irq_handler(cmos_interrupt); + } + + 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; +} + +static int cmos_aie_poweroff(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_time now; + time64_t t_now; + int retval = 0; + unsigned char rtc_control; + + if (!cmos->alarm_expires) + return -EINVAL; + + spin_lock_irq(&rtc_lock); + rtc_control = CMOS_READ(RTC_CONTROL); + spin_unlock_irq(&rtc_lock); + + /* We only care about the situation where AIE is disabled. */ + if (rtc_control & RTC_AIE) + return -EBUSY; + + cmos_read_time(dev, &now); + t_now = rtc_tm_to_time64(&now); + + /* + * When enabling "RTC wake-up" in BIOS setup, the machine reboots + * automatically right after shutdown on some buggy boxes. + * This automatic rebooting issue won't happen when the alarm + * time is larger than now+1 seconds. + * + * If the alarm time is equal to now+1 seconds, the issue can be + * prevented by cancelling the alarm. + */ + if (cmos->alarm_expires == t_now + 1) { + struct rtc_wkalrm alarm; + + /* Cancel the AIE timer by configuring the past time. */ + rtc_time64_to_tm(t_now - 1, &alarm.time); + alarm.enabled = 0; + retval = cmos_set_alarm(dev, &alarm); + } else if (cmos->alarm_expires > t_now + 1) { + retval = -EBUSY; + } + + return retval; +} + +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); + if (use_hpet_alarm()) + hpet_mask_rtc_irq_bit(mask); + cmos_checkintr(cmos, tmp); + } + spin_unlock_irq(&rtc_lock); + + if ((tmp & RTC_AIE) && !cmos_use_acpi_alarm()) { + cmos->enabled_wake = 1; + if (cmos->wake_on) + cmos->wake_on(dev); + else + enable_irq_wake(cmos->irq); + } + + cmos_read_alarm(dev, &cmos->saved_wkalrm); + + 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) +{ + if (!IS_ENABLED(CONFIG_PM)) + return -ENOSYS; + + return cmos_suspend(dev); +} + +static void cmos_check_wkalrm(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + struct rtc_wkalrm current_alarm; + time64_t t_now; + time64_t t_current_expires; + time64_t t_saved_expires; + struct rtc_time now; + + /* Check if we have RTC Alarm armed */ + if (!(cmos->suspend_ctrl & RTC_AIE)) + return; + + cmos_read_time(dev, &now); + t_now = rtc_tm_to_time64(&now); + + /* + * ACPI RTC wake event is cleared after resume from STR, + * ACK the rtc irq here + */ + if (t_now >= cmos->alarm_expires && cmos_use_acpi_alarm()) { + cmos_interrupt(0, (void *)cmos->rtc); + return; + } + + cmos_read_alarm(dev, ¤t_alarm); + t_current_expires = rtc_tm_to_time64(¤t_alarm.time); + t_saved_expires = rtc_tm_to_time64(&cmos->saved_wkalrm.time); + if (t_current_expires != t_saved_expires || + cmos->saved_wkalrm.enabled != current_alarm.enabled) { + cmos_set_alarm(dev, &cmos->saved_wkalrm); + } +} + +static void cmos_check_acpi_rtc_status(struct device *dev, + unsigned char *rtc_control); + +static int __maybe_unused cmos_resume(struct device *dev) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned char tmp; + + if (cmos->enabled_wake && !cmos_use_acpi_alarm()) { + if (cmos->wake_off) + cmos->wake_off(dev); + else + disable_irq_wake(cmos->irq); + cmos->enabled_wake = 0; + } + + /* The BIOS might have changed the alarm, restore it */ + cmos_check_wkalrm(dev); + + 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) && use_hpet_alarm()) + hpet_rtc_timer_init(); + + do { + CMOS_WRITE(tmp, RTC_CONTROL); + if (use_hpet_alarm()) + hpet_set_rtc_irq_bit(tmp & RTC_IRQMASK); + + mask = CMOS_READ(RTC_INTR_FLAGS); + mask &= (tmp & RTC_IRQMASK) | RTC_IRQF; + if (!use_hpet_alarm() || !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); + + if (tmp & RTC_AIE) + cmos_check_acpi_rtc_status(dev, &tmp); + } + spin_unlock_irq(&rtc_lock); + + dev_dbg(dev, "resume, ctrl %02x\n", tmp); + + return 0; +} + +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; + struct cmos_rtc *cmos = dev_get_drvdata(dev); + unsigned char rtc_control = 0; + unsigned char rtc_intr; + unsigned long flags; + + + /* + * Always update rtc irq when ACPI is used as RTC Alarm. + * Or else, ACPI SCI is enabled during suspend/resume only, + * update rtc irq in that case. + */ + if (cmos_use_acpi_alarm()) + cmos_interrupt(0, (void *)cmos->rtc); + else { + /* Fix me: can we use cmos_interrupt() here as well? */ + spin_lock_irqsave(&rtc_lock, flags); + if (cmos_rtc.suspend_ctrl) + rtc_control = CMOS_READ(RTC_CONTROL); + if (rtc_control & RTC_AIE) { + cmos_rtc.suspend_ctrl &= ~RTC_AIE; + CMOS_WRITE(rtc_control, RTC_CONTROL); + rtc_intr = CMOS_READ(RTC_INTR_FLAGS); + rtc_update_irq(cmos->rtc, 1, rtc_intr); + } + spin_unlock_irqrestore(&rtc_lock, flags); + } + + pm_wakeup_hard_event(dev); + 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); +} + +#ifdef CONFIG_X86 +/* Enable use_acpi_alarm mode for Intel platforms no earlier than 2015 */ +static void use_acpi_alarm_quirks(void) +{ + int year; + + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL) + return; + + if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0)) + return; + + if (!is_hpet_enabled()) + return; + + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && year >= 2015) + use_acpi_alarm = true; +} +#else +static inline void use_acpi_alarm_quirks(void) { } +#endif + +/* 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; + + use_acpi_alarm_quirks(); + + 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); +} + +static void cmos_check_acpi_rtc_status(struct device *dev, + unsigned char *rtc_control) +{ + struct cmos_rtc *cmos = dev_get_drvdata(dev); + acpi_event_status rtc_status; + acpi_status status; + + if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC) + return; + + status = acpi_get_event_status(ACPI_EVENT_RTC, &rtc_status); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Could not get RTC status\n"); + } else if (rtc_status & ACPI_EVENT_FLAG_SET) { + unsigned char mask; + *rtc_control &= ~RTC_AIE; + CMOS_WRITE(*rtc_control, RTC_CONTROL); + mask = CMOS_READ(RTC_INTR_FLAGS); + rtc_update_irq(cmos->rtc, 1, mask); + } +} + +#else + +static void cmos_wake_setup(struct device *dev) +{ +} + +static void cmos_check_acpi_rtc_status(struct device *dev, + unsigned char *rtc_control) +{ +} + +#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)) { + unsigned int irq = 0; +#ifdef CONFIG_X86 + /* Some machines contain a PNP entry for the RTC, but + * don't define the IRQ. It should always be safe to + * hardcode it on systems with a legacy PIC. + */ + if (nr_legacy_irqs()) + irq = 8; +#endif + return cmos_do_probe(&pnp->dev, + pnp_get_resource(pnp, IORESOURCE_IO, 0), irq); + } else { + return cmos_do_probe(&pnp->dev, + pnp_get_resource(pnp, IORESOURCE_IO, 0), + pnp_irq(pnp, 0)); + } +} + +static void 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) { + int retval = cmos_poweroff(dev); + + if (cmos_aie_poweroff(dev) < 0 && !retval) + 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 = 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; + 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); +} +#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 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) { + int retval = cmos_poweroff(dev); + + if (cmos_aie_poweroff(dev) < 0 && !retval) + return; + } + + cmos_do_shutdown(cmos->irq); +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc_cmos"); + +static struct platform_driver cmos_platform_driver = { + .remove = cmos_platform_remove, + .shutdown = cmos_platform_shutdown, + .driver = { + .name = driver_name, + .pm = &cmos_pm_ops, + .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; + } + + 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..fc5cf5c44 --- /dev/null +++ b/drivers/rtc/rtc-coh901331.c @@ -0,0 +1,283 @@ +/* + * 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/mod_devicetable.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 0; + } + 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 const 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" }, + {}, +}; +MODULE_DEVICE_TABLE(of, coh901331_dt_match); + +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..ccc17a2e2 --- /dev/null +++ b/drivers/rtc/rtc-core.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#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); + +#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) +{ +} + +#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 +const struct attribute_group **rtc_get_dev_attribute_groups(void); +int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp); +int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps); +#else +static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) +{ + return NULL; +} + +static inline +int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp) +{ + return 0; +} + +static inline +int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps) +{ + return 0; +} +#endif diff --git a/drivers/rtc/rtc-cpcap.c b/drivers/rtc/rtc-cpcap.c new file mode 100644 index 000000000..6b477174a --- /dev/null +++ b/drivers/rtc/rtc-cpcap.c @@ -0,0 +1,331 @@ +/* + * Motorola CPCAP PMIC RTC driver + * + * Based on cpcap-regulator.c from Motorola Linux kernel tree + * Copyright (C) 2009 Motorola, Inc. + * + * Rewritten for mainline kernel + * - use DT + * - use regmap + * - use standard interrupt framework + * - use managed device resources + * - remove custom "secure clock daemon" helpers + * + * Copyright (C) 2017 Sebastian Reichel <sre@kernel.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. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/err.h> +#include <linux/regmap.h> +#include <linux/mfd/motorola-cpcap.h> +#include <linux/slab.h> +#include <linux/sched.h> + +#define SECS_PER_DAY 86400 +#define DAY_MASK 0x7FFF +#define TOD1_MASK 0x00FF +#define TOD2_MASK 0x01FF + +struct cpcap_time { + int day; + int tod1; + int tod2; +}; + +struct cpcap_rtc { + struct regmap *regmap; + struct rtc_device *rtc_dev; + u16 vendor; + int alarm_irq; + bool alarm_enabled; + int update_irq; + bool update_enabled; +}; + +static void cpcap2rtc_time(struct rtc_time *rtc, struct cpcap_time *cpcap) +{ + unsigned long int tod; + unsigned long int time; + + tod = (cpcap->tod1 & TOD1_MASK) | ((cpcap->tod2 & TOD2_MASK) << 8); + time = tod + ((cpcap->day & DAY_MASK) * SECS_PER_DAY); + + rtc_time_to_tm(time, rtc); +} + +static void rtc2cpcap_time(struct cpcap_time *cpcap, struct rtc_time *rtc) +{ + unsigned long time; + + rtc_tm_to_time(rtc, &time); + + cpcap->day = time / SECS_PER_DAY; + time %= SECS_PER_DAY; + cpcap->tod2 = (time >> 8) & TOD2_MASK; + cpcap->tod1 = time & TOD1_MASK; +} + +static int cpcap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct cpcap_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->alarm_enabled == enabled) + return 0; + + if (enabled) + enable_irq(rtc->alarm_irq); + else + disable_irq(rtc->alarm_irq); + + rtc->alarm_enabled = !!enabled; + + return 0; +} + +static int cpcap_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int temp_tod2; + int ret; + + rtc = dev_get_drvdata(dev); + + ret = regmap_read(rtc->regmap, CPCAP_REG_TOD2, &temp_tod2); + ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD1, &cpcap_tm.tod1); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TOD2, &cpcap_tm.tod2); + + if (temp_tod2 > cpcap_tm.tod2) + ret |= regmap_read(rtc->regmap, CPCAP_REG_DAY, &cpcap_tm.day); + + if (ret) { + dev_err(dev, "Failed to read time\n"); + return -EIO; + } + + cpcap2rtc_time(tm, &cpcap_tm); + + return 0; +} + +static int cpcap_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int ret = 0; + + rtc = dev_get_drvdata(dev); + + rtc2cpcap_time(&cpcap_tm, tm); + + if (rtc->alarm_enabled) + disable_irq(rtc->alarm_irq); + if (rtc->update_enabled) + disable_irq(rtc->update_irq); + + if (rtc->vendor == CPCAP_VENDOR_ST) { + /* The TOD1 and TOD2 registers MUST be written in this order + * for the change to properly set. + */ + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1, + TOD1_MASK, cpcap_tm.tod1); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2, + TOD2_MASK, cpcap_tm.tod2); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY, + DAY_MASK, cpcap_tm.day); + } else { + /* Clearing the upper lower 8 bits of the TOD guarantees that + * the upper half of TOD (TOD2) will not increment for 0xFF RTC + * ticks (255 seconds). During this time we can safely write + * to DAY, TOD2, then TOD1 (in that order) and expect RTC to be + * synchronized to the exact time requested upon the final write + * to TOD1. + */ + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1, + TOD1_MASK, 0); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_DAY, + DAY_MASK, cpcap_tm.day); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD2, + TOD2_MASK, cpcap_tm.tod2); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TOD1, + TOD1_MASK, cpcap_tm.tod1); + } + + if (rtc->update_enabled) + enable_irq(rtc->update_irq); + if (rtc->alarm_enabled) + enable_irq(rtc->alarm_irq); + + return ret; +} + +static int cpcap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int ret; + + rtc = dev_get_drvdata(dev); + + alrm->enabled = rtc->alarm_enabled; + + ret = regmap_read(rtc->regmap, CPCAP_REG_DAYA, &cpcap_tm.day); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA2, &cpcap_tm.tod2); + ret |= regmap_read(rtc->regmap, CPCAP_REG_TODA1, &cpcap_tm.tod1); + + if (ret) { + dev_err(dev, "Failed to read time\n"); + return -EIO; + } + + cpcap2rtc_time(&alrm->time, &cpcap_tm); + return rtc_valid_tm(&alrm->time); +} + +static int cpcap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cpcap_rtc *rtc; + struct cpcap_time cpcap_tm; + int ret; + + rtc = dev_get_drvdata(dev); + + rtc2cpcap_time(&cpcap_tm, &alrm->time); + + if (rtc->alarm_enabled) + disable_irq(rtc->alarm_irq); + + ret = regmap_update_bits(rtc->regmap, CPCAP_REG_DAYA, DAY_MASK, + cpcap_tm.day); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA2, TOD2_MASK, + cpcap_tm.tod2); + ret |= regmap_update_bits(rtc->regmap, CPCAP_REG_TODA1, TOD1_MASK, + cpcap_tm.tod1); + + if (!ret) { + enable_irq(rtc->alarm_irq); + rtc->alarm_enabled = true; + } + + return ret; +} + +static const struct rtc_class_ops cpcap_rtc_ops = { + .read_time = cpcap_rtc_read_time, + .set_time = cpcap_rtc_set_time, + .read_alarm = cpcap_rtc_read_alarm, + .set_alarm = cpcap_rtc_set_alarm, + .alarm_irq_enable = cpcap_rtc_alarm_irq_enable, +}; + +static irqreturn_t cpcap_rtc_alarm_irq(int irq, void *data) +{ + struct cpcap_rtc *rtc = data; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static irqreturn_t cpcap_rtc_update_irq(int irq, void *data) +{ + struct cpcap_rtc *rtc = data; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); + return IRQ_HANDLED; +} + +static int cpcap_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cpcap_rtc *rtc; + int err; + + rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regmap = dev_get_regmap(dev->parent, NULL); + if (!rtc->regmap) + return -ENODEV; + + platform_set_drvdata(pdev, rtc); + rtc->rtc_dev = devm_rtc_device_register(dev, "cpcap_rtc", + &cpcap_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + err = cpcap_get_vendor(dev, rtc->regmap, &rtc->vendor); + if (err) + return err; + + rtc->alarm_irq = platform_get_irq(pdev, 0); + err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL, + cpcap_rtc_alarm_irq, IRQF_TRIGGER_NONE, + "rtc_alarm", rtc); + if (err) { + dev_err(dev, "Could not request alarm irq: %d\n", err); + return err; + } + disable_irq(rtc->alarm_irq); + + /* Stock Android uses the 1 Hz interrupt for "secure clock daemon", + * which is not supported by the mainline kernel. The mainline kernel + * does not use the irq at the moment, but we explicitly request and + * disable it, so that its masked and does not wake up the processor + * every second. + */ + rtc->update_irq = platform_get_irq(pdev, 1); + err = devm_request_threaded_irq(dev, rtc->update_irq, NULL, + cpcap_rtc_update_irq, IRQF_TRIGGER_NONE, + "rtc_1hz", rtc); + if (err) { + dev_err(dev, "Could not request update irq: %d\n", err); + return err; + } + disable_irq(rtc->update_irq); + + err = device_init_wakeup(dev, 1); + if (err) { + dev_err(dev, "wakeup initialization failed (%d)\n", err); + /* ignore error and continue without wakeup support */ + } + + return 0; +} + +static const struct of_device_id cpcap_rtc_of_match[] = { + { .compatible = "motorola,cpcap-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, cpcap_rtc_of_match); + +static struct platform_driver cpcap_rtc_driver = { + .probe = cpcap_rtc_probe, + .driver = { + .name = "cpcap-rtc", + .of_match_table = cpcap_rtc_of_match, + }, +}; + +module_platform_driver(cpcap_rtc_driver); + +MODULE_ALIAS("platform:cpcap-rtc"); +MODULE_DESCRIPTION("CPCAP RTC driver"); +MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-cros-ec.c b/drivers/rtc/rtc-cros-ec.c new file mode 100644 index 000000000..4d6bf9304 --- /dev/null +++ b/drivers/rtc/rtc-cros-ec.c @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: GPL-2.0 +// RTC driver for ChromeOS Embedded Controller. +// +// Copyright (C) 2017 Google, Inc. +// Author: Stephen Barber <smbarber@chromium.org> + +#include <linux/kernel.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#define DRV_NAME "cros-ec-rtc" + +/** + * struct cros_ec_rtc - Driver data for EC RTC + * + * @cros_ec: Pointer to EC device + * @rtc: Pointer to RTC device + * @notifier: Notifier info for responding to EC events + * @saved_alarm: Alarm to restore when interrupts are reenabled + */ +struct cros_ec_rtc { + struct cros_ec_device *cros_ec; + struct rtc_device *rtc; + struct notifier_block notifier; + u32 saved_alarm; +}; + +static int cros_ec_rtc_get(struct cros_ec_device *cros_ec, u32 command, + u32 *response) +{ + int ret; + struct { + struct cros_ec_command msg; + struct ec_response_rtc data; + } __packed msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg.command = command; + msg.msg.insize = sizeof(msg.data); + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, + "error getting %s from EC: %d\n", + command == EC_CMD_RTC_GET_VALUE ? "time" : "alarm", + ret); + return ret; + } + + *response = msg.data.time; + + return 0; +} + +static int cros_ec_rtc_set(struct cros_ec_device *cros_ec, u32 command, + u32 param) +{ + int ret = 0; + struct { + struct cros_ec_command msg; + struct ec_response_rtc data; + } __packed msg; + + memset(&msg, 0, sizeof(msg)); + msg.msg.command = command; + msg.msg.outsize = sizeof(msg.data); + msg.data.time = param; + + ret = cros_ec_cmd_xfer_status(cros_ec, &msg.msg); + if (ret < 0) { + dev_err(cros_ec->dev, "error setting %s on EC: %d\n", + command == EC_CMD_RTC_SET_VALUE ? "time" : "alarm", + ret); + return ret; + } + + return 0; +} + +/* Read the current time from the EC. */ +static int cros_ec_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 time; + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, &time); + if (ret) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + rtc_time64_to_tm(time, tm); + + return 0; +} + +/* Set the current EC time. */ +static int cros_ec_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + time64_t time; + + time = rtc_tm_to_time64(tm); + if (time < 0 || time > U32_MAX) + return -EINVAL; + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_VALUE, (u32)time); + if (ret < 0) { + dev_err(dev, "error setting time: %d\n", ret); + return ret; + } + + return 0; +} + +/* Read alarm time from RTC. */ +static int cros_ec_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 current_time, alarm_offset; + + /* + * The EC host command for getting the alarm is relative (i.e. 5 + * seconds from now) whereas rtc_wkalrm is absolute. Get the current + * RTC time first so we can calculate the relative time. + */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, &alarm_offset); + if (ret < 0) { + dev_err(dev, "error getting alarm: %d\n", ret); + return ret; + } + + rtc_time64_to_tm(current_time + alarm_offset, &alrm->time); + + return 0; +} + +/* Set the EC's RTC alarm. */ +static int cros_ec_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + time64_t alarm_time; + u32 current_time, alarm_offset; + + /* + * The EC host command for setting the alarm is relative + * (i.e. 5 seconds from now) whereas rtc_wkalrm is absolute. + * Get the current RTC time first so we can calculate the + * relative time. + */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + alarm_time = rtc_tm_to_time64(&alrm->time); + + if (alarm_time < 0 || alarm_time > U32_MAX) + return -EINVAL; + + if (!alrm->enabled) { + /* + * If the alarm is being disabled, send an alarm + * clear command. + */ + alarm_offset = EC_RTC_ALARM_CLEAR; + cros_ec_rtc->saved_alarm = (u32)alarm_time; + } else { + /* Don't set an alarm in the past. */ + if ((u32)alarm_time <= current_time) + return -ETIME; + + alarm_offset = (u32)alarm_time - current_time; + } + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, alarm_offset); + if (ret < 0) { + dev_err(dev, "error setting alarm: %d\n", ret); + return ret; + } + + return 0; +} + +static int cros_ec_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(dev); + struct cros_ec_device *cros_ec = cros_ec_rtc->cros_ec; + int ret; + u32 current_time, alarm_offset, alarm_value; + + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_VALUE, ¤t_time); + if (ret < 0) { + dev_err(dev, "error getting time: %d\n", ret); + return ret; + } + + if (enabled) { + /* Restore saved alarm if it's still in the future. */ + if (cros_ec_rtc->saved_alarm < current_time) + alarm_offset = EC_RTC_ALARM_CLEAR; + else + alarm_offset = cros_ec_rtc->saved_alarm - current_time; + + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + alarm_offset); + if (ret < 0) { + dev_err(dev, "error restoring alarm: %d\n", ret); + return ret; + } + } else { + /* Disable alarm, saving the old alarm value. */ + ret = cros_ec_rtc_get(cros_ec, EC_CMD_RTC_GET_ALARM, + &alarm_offset); + if (ret < 0) { + dev_err(dev, "error saving alarm: %d\n", ret); + return ret; + } + + alarm_value = current_time + alarm_offset; + + /* + * If the current EC alarm is already past, we don't want + * to set an alarm when we go through the alarm irq enable + * path. + */ + if (alarm_value < current_time) + cros_ec_rtc->saved_alarm = EC_RTC_ALARM_CLEAR; + else + cros_ec_rtc->saved_alarm = alarm_value; + + alarm_offset = EC_RTC_ALARM_CLEAR; + ret = cros_ec_rtc_set(cros_ec, EC_CMD_RTC_SET_ALARM, + alarm_offset); + if (ret < 0) { + dev_err(dev, "error disabling alarm: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int cros_ec_rtc_event(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_notify) +{ + struct cros_ec_rtc *cros_ec_rtc; + struct rtc_device *rtc; + struct cros_ec_device *cros_ec; + u32 host_event; + + cros_ec_rtc = container_of(nb, struct cros_ec_rtc, notifier); + rtc = cros_ec_rtc->rtc; + cros_ec = cros_ec_rtc->cros_ec; + + host_event = cros_ec_get_host_event(cros_ec); + if (host_event & EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)) { + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + return NOTIFY_OK; + } else { + return NOTIFY_DONE; + } +} + +static const struct rtc_class_ops cros_ec_rtc_ops = { + .read_time = cros_ec_rtc_read_time, + .set_time = cros_ec_rtc_set_time, + .read_alarm = cros_ec_rtc_read_alarm, + .set_alarm = cros_ec_rtc_set_alarm, + .alarm_irq_enable = cros_ec_rtc_alarm_irq_enable, +}; + +#ifdef CONFIG_PM_SLEEP +static int cros_ec_rtc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + return enable_irq_wake(cros_ec_rtc->cros_ec->irq); + + return 0; +} + +static int cros_ec_rtc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct cros_ec_rtc *cros_ec_rtc = dev_get_drvdata(&pdev->dev); + + if (device_may_wakeup(dev)) + return disable_irq_wake(cros_ec_rtc->cros_ec->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(cros_ec_rtc_pm_ops, cros_ec_rtc_suspend, + cros_ec_rtc_resume); + +static int cros_ec_rtc_probe(struct platform_device *pdev) +{ + struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct cros_ec_rtc *cros_ec_rtc; + struct rtc_time tm; + int ret; + + cros_ec_rtc = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_rtc), + GFP_KERNEL); + if (!cros_ec_rtc) + return -ENOMEM; + + platform_set_drvdata(pdev, cros_ec_rtc); + cros_ec_rtc->cros_ec = cros_ec; + + /* Get initial time */ + ret = cros_ec_rtc_read_time(&pdev->dev, &tm); + if (ret) { + dev_err(&pdev->dev, "failed to read RTC time\n"); + return ret; + } + + ret = device_init_wakeup(&pdev->dev, 1); + if (ret) { + dev_err(&pdev->dev, "failed to initialize wakeup\n"); + return ret; + } + + cros_ec_rtc->rtc = devm_rtc_device_register(&pdev->dev, DRV_NAME, + &cros_ec_rtc_ops, + THIS_MODULE); + if (IS_ERR(cros_ec_rtc->rtc)) { + ret = PTR_ERR(cros_ec_rtc->rtc); + dev_err(&pdev->dev, "failed to register rtc device\n"); + return ret; + } + + /* Get RTC events from the EC. */ + cros_ec_rtc->notifier.notifier_call = cros_ec_rtc_event; + ret = blocking_notifier_chain_register(&cros_ec->event_notifier, + &cros_ec_rtc->notifier); + if (ret) { + dev_err(&pdev->dev, "failed to register notifier\n"); + return ret; + } + + return 0; +} + +static int cros_ec_rtc_remove(struct platform_device *pdev) +{ + struct cros_ec_rtc *cros_ec_rtc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + ret = blocking_notifier_chain_unregister( + &cros_ec_rtc->cros_ec->event_notifier, + &cros_ec_rtc->notifier); + if (ret) { + dev_err(dev, "failed to unregister notifier\n"); + return ret; + } + + return 0; +} + +static struct platform_driver cros_ec_rtc_driver = { + .probe = cros_ec_rtc_probe, + .remove = cros_ec_rtc_remove, + .driver = { + .name = DRV_NAME, + .pm = &cros_ec_rtc_pm_ops, + }, +}; + +module_platform_driver(cros_ec_rtc_driver); + +MODULE_DESCRIPTION("RTC driver for Chrome OS ECs"); +MODULE_AUTHOR("Stephen Barber <smbarber@chromium.org>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c new file mode 100644 index 000000000..03044e1bc --- /dev/null +++ b/drivers/rtc/rtc-da9052.c @@ -0,0 +1,334 @@ +/* + * 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; + rtc_tm->tm_sec = 0; + + 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; + + return 0; + } + + 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); + + device_init_wakeup(&pdev->dev, true); + rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &da9052_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + 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; + } + + return 0; +} + +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..e08cd8130 --- /dev/null +++ b/drivers/rtc/rtc-da9055.c @@ -0,0 +1,404 @@ +/* + * 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; + rtc_tm->tm_sec = 0; + + 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 0; +} + +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..69b54e555 --- /dev/null +++ b/drivers/rtc/rtc-da9063.c @@ -0,0 +1,515 @@ +/* rtc-da9063.c - Real time clock device driver for DA9063 + * Copyright (C) 2013-2015 Dialog Semiconductor Ltd. + * + * 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/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +#include <linux/mfd/da9062/registers.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) + +enum { + RTC_SEC = 0, + RTC_MIN = 1, + RTC_HOUR = 2, + RTC_DAY = 3, + RTC_MONTH = 4, + RTC_YEAR = 5, + RTC_DATA_LEN +}; + +struct da9063_compatible_rtc_regmap { + /* REGS */ + int rtc_enable_reg; + int rtc_enable_32k_crystal_reg; + int rtc_alarm_secs_reg; + int rtc_alarm_year_reg; + int rtc_count_secs_reg; + int rtc_count_year_reg; + int rtc_event_reg; + /* MASKS */ + int rtc_enable_mask; + int rtc_crystal_mask; + int rtc_event_alarm_mask; + int rtc_alarm_on_mask; + int rtc_alarm_status_mask; + int rtc_tick_on_mask; + int rtc_ready_to_read_mask; + int rtc_count_sec_mask; + int rtc_count_min_mask; + int rtc_count_hour_mask; + int rtc_count_day_mask; + int rtc_count_month_mask; + int rtc_count_year_mask; + /* ALARM CONFIG */ + int rtc_data_start; + int rtc_alarm_len; +}; + +struct da9063_compatible_rtc { + struct rtc_device *rtc_dev; + struct rtc_time alarm_time; + struct regmap *regmap; + const struct da9063_compatible_rtc_regmap *config; + bool rtc_sync; +}; + +static const struct da9063_compatible_rtc_regmap da9063_ad_regs = { + /* REGS */ + .rtc_enable_reg = DA9063_REG_CONTROL_E, + .rtc_alarm_secs_reg = DA9063_AD_REG_ALARM_MI, + .rtc_alarm_year_reg = DA9063_AD_REG_ALARM_Y, + .rtc_count_secs_reg = DA9063_REG_COUNT_S, + .rtc_count_year_reg = DA9063_REG_COUNT_Y, + .rtc_event_reg = DA9063_REG_EVENT_A, + /* MASKS */ + .rtc_enable_mask = DA9063_RTC_EN, + .rtc_crystal_mask = DA9063_CRYSTAL, + .rtc_enable_32k_crystal_reg = DA9063_REG_EN_32K, + .rtc_event_alarm_mask = DA9063_E_ALARM, + .rtc_alarm_on_mask = DA9063_ALARM_ON, + .rtc_alarm_status_mask = DA9063_ALARM_STATUS_ALARM | + DA9063_ALARM_STATUS_TICK, + .rtc_tick_on_mask = DA9063_TICK_ON, + .rtc_ready_to_read_mask = DA9063_RTC_READ, + .rtc_count_sec_mask = DA9063_COUNT_SEC_MASK, + .rtc_count_min_mask = DA9063_COUNT_MIN_MASK, + .rtc_count_hour_mask = DA9063_COUNT_HOUR_MASK, + .rtc_count_day_mask = DA9063_COUNT_DAY_MASK, + .rtc_count_month_mask = DA9063_COUNT_MONTH_MASK, + .rtc_count_year_mask = DA9063_COUNT_YEAR_MASK, + /* ALARM CONFIG */ + .rtc_data_start = RTC_MIN, + .rtc_alarm_len = RTC_DATA_LEN - 1, +}; + +static const struct da9063_compatible_rtc_regmap da9063_bb_regs = { + /* REGS */ + .rtc_enable_reg = DA9063_REG_CONTROL_E, + .rtc_alarm_secs_reg = DA9063_BB_REG_ALARM_S, + .rtc_alarm_year_reg = DA9063_BB_REG_ALARM_Y, + .rtc_count_secs_reg = DA9063_REG_COUNT_S, + .rtc_count_year_reg = DA9063_REG_COUNT_Y, + .rtc_event_reg = DA9063_REG_EVENT_A, + /* MASKS */ + .rtc_enable_mask = DA9063_RTC_EN, + .rtc_crystal_mask = DA9063_CRYSTAL, + .rtc_enable_32k_crystal_reg = DA9063_REG_EN_32K, + .rtc_event_alarm_mask = DA9063_E_ALARM, + .rtc_alarm_on_mask = DA9063_ALARM_ON, + .rtc_alarm_status_mask = DA9063_ALARM_STATUS_ALARM | + DA9063_ALARM_STATUS_TICK, + .rtc_tick_on_mask = DA9063_TICK_ON, + .rtc_ready_to_read_mask = DA9063_RTC_READ, + .rtc_count_sec_mask = DA9063_COUNT_SEC_MASK, + .rtc_count_min_mask = DA9063_COUNT_MIN_MASK, + .rtc_count_hour_mask = DA9063_COUNT_HOUR_MASK, + .rtc_count_day_mask = DA9063_COUNT_DAY_MASK, + .rtc_count_month_mask = DA9063_COUNT_MONTH_MASK, + .rtc_count_year_mask = DA9063_COUNT_YEAR_MASK, + /* ALARM CONFIG */ + .rtc_data_start = RTC_SEC, + .rtc_alarm_len = RTC_DATA_LEN, +}; + +static const struct da9063_compatible_rtc_regmap da9062_aa_regs = { + /* REGS */ + .rtc_enable_reg = DA9062AA_CONTROL_E, + .rtc_alarm_secs_reg = DA9062AA_ALARM_S, + .rtc_alarm_year_reg = DA9062AA_ALARM_Y, + .rtc_count_secs_reg = DA9062AA_COUNT_S, + .rtc_count_year_reg = DA9062AA_COUNT_Y, + .rtc_event_reg = DA9062AA_EVENT_A, + /* MASKS */ + .rtc_enable_mask = DA9062AA_RTC_EN_MASK, + .rtc_crystal_mask = DA9062AA_CRYSTAL_MASK, + .rtc_enable_32k_crystal_reg = DA9062AA_EN_32K, + .rtc_event_alarm_mask = DA9062AA_M_ALARM_MASK, + .rtc_alarm_on_mask = DA9062AA_ALARM_ON_MASK, + .rtc_alarm_status_mask = (0x02 << 6), + .rtc_tick_on_mask = DA9062AA_TICK_ON_MASK, + .rtc_ready_to_read_mask = DA9062AA_RTC_READ_MASK, + .rtc_count_sec_mask = DA9062AA_COUNT_SEC_MASK, + .rtc_count_min_mask = DA9062AA_COUNT_MIN_MASK, + .rtc_count_hour_mask = DA9062AA_COUNT_HOUR_MASK, + .rtc_count_day_mask = DA9062AA_COUNT_DAY_MASK, + .rtc_count_month_mask = DA9062AA_COUNT_MONTH_MASK, + .rtc_count_year_mask = DA9062AA_COUNT_YEAR_MASK, + /* ALARM CONFIG */ + .rtc_data_start = RTC_SEC, + .rtc_alarm_len = RTC_DATA_LEN, +}; + +static const struct of_device_id da9063_compatible_reg_id_table[] = { + { .compatible = "dlg,da9063-rtc", .data = &da9063_bb_regs }, + { .compatible = "dlg,da9062-rtc", .data = &da9062_aa_regs }, + { }, +}; +MODULE_DEVICE_TABLE(of, da9063_compatible_reg_id_table); + +static void da9063_data_to_tm(u8 *data, struct rtc_time *tm, + struct da9063_compatible_rtc *rtc) +{ + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + tm->tm_sec = data[RTC_SEC] & config->rtc_count_sec_mask; + tm->tm_min = data[RTC_MIN] & config->rtc_count_min_mask; + tm->tm_hour = data[RTC_HOUR] & config->rtc_count_hour_mask; + tm->tm_mday = data[RTC_DAY] & config->rtc_count_day_mask; + tm->tm_mon = MONTHS_FROM_DA9063(data[RTC_MONTH] & + config->rtc_count_month_mask); + tm->tm_year = YEARS_FROM_DA9063(data[RTC_YEAR] & + config->rtc_count_year_mask); +} + +static void da9063_tm_to_data(struct rtc_time *tm, u8 *data, + struct da9063_compatible_rtc *rtc) +{ + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + data[RTC_SEC] = tm->tm_sec & config->rtc_count_sec_mask; + data[RTC_MIN] = tm->tm_min & config->rtc_count_min_mask; + data[RTC_HOUR] = tm->tm_hour & config->rtc_count_hour_mask; + data[RTC_DAY] = tm->tm_mday & config->rtc_count_day_mask; + data[RTC_MONTH] = MONTHS_TO_DA9063(tm->tm_mon) & + config->rtc_count_month_mask; + data[RTC_YEAR] = YEARS_TO_DA9063(tm->tm_year) & + config->rtc_count_year_mask; +} + +static int da9063_rtc_stop_alarm(struct device *dev) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + return regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_alarm_on_mask, + 0); +} + +static int da9063_rtc_start_alarm(struct device *dev) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + return regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_alarm_on_mask, + config->rtc_alarm_on_mask); +} + +static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + unsigned long tm_secs; + unsigned long al_secs; + u8 data[RTC_DATA_LEN]; + int ret; + + ret = regmap_bulk_read(rtc->regmap, + config->rtc_count_secs_reg, + 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] & config->rtc_ready_to_read_mask)) { + dev_dbg(dev, "RTC not yet ready to be read by the host\n"); + return -EINVAL; + } + + da9063_data_to_tm(data, tm, rtc); + + 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 0; +} + +static int da9063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 data[RTC_DATA_LEN]; + int ret; + + da9063_tm_to_data(tm, data, rtc); + ret = regmap_bulk_write(rtc->regmap, + config->rtc_count_secs_reg, + 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_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 data[RTC_DATA_LEN]; + int ret; + unsigned int val; + + data[RTC_SEC] = 0; + ret = regmap_bulk_read(rtc->regmap, + config->rtc_alarm_secs_reg, + &data[config->rtc_data_start], + config->rtc_alarm_len); + if (ret < 0) + return ret; + + da9063_data_to_tm(data, &alrm->time, rtc); + + alrm->enabled = !!(data[RTC_YEAR] & config->rtc_alarm_on_mask); + + ret = regmap_read(rtc->regmap, + config->rtc_event_reg, + &val); + if (ret < 0) + return ret; + + if (val & config->rtc_event_alarm_mask) + alrm->pending = 1; + else + alrm->pending = 0; + + return 0; +} + +static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev); + const struct da9063_compatible_rtc_regmap *config = rtc->config; + u8 data[RTC_DATA_LEN]; + int ret; + + da9063_tm_to_data(&alrm->time, data, rtc); + + 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->regmap, + config->rtc_alarm_secs_reg, + &data[config->rtc_data_start], + config->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, rtc); + + 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_compatible_rtc *rtc = data; + const struct da9063_compatible_rtc_regmap *config = rtc->config; + + regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_alarm_on_mask, + 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_compatible_rtc *rtc; + const struct da9063_compatible_rtc_regmap *config; + const struct of_device_id *match; + int irq_alarm; + u8 data[RTC_DATA_LEN]; + int ret; + + if (!pdev->dev.of_node) + return -ENXIO; + + match = of_match_node(da9063_compatible_reg_id_table, + pdev->dev.of_node); + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->config = match->data; + if (of_device_is_compatible(pdev->dev.of_node, "dlg,da9063-rtc")) { + struct da9063 *chip = dev_get_drvdata(pdev->dev.parent); + + if (chip->variant_code == PMIC_DA9063_AD) + rtc->config = &da9063_ad_regs; + } + + rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc->regmap) { + dev_warn(&pdev->dev, "Parent regmap unavailable.\n"); + return -ENXIO; + } + + config = rtc->config; + ret = regmap_update_bits(rtc->regmap, + config->rtc_enable_reg, + config->rtc_enable_mask, + config->rtc_enable_mask); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable RTC\n"); + return ret; + } + + ret = regmap_update_bits(rtc->regmap, + config->rtc_enable_32k_crystal_reg, + config->rtc_crystal_mask, + config->rtc_crystal_mask); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to run 32kHz oscillator\n"); + return ret; + } + + ret = regmap_update_bits(rtc->regmap, + config->rtc_alarm_secs_reg, + config->rtc_alarm_status_mask, + 0); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to access RTC alarm register\n"); + return ret; + } + + ret = regmap_update_bits(rtc->regmap, + config->rtc_alarm_secs_reg, + DA9063_ALARM_STATUS_ALARM, + DA9063_ALARM_STATUS_ALARM); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to access RTC alarm register\n"); + return ret; + } + + ret = regmap_update_bits(rtc->regmap, + config->rtc_alarm_year_reg, + config->rtc_tick_on_mask, + 0); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to disable TICKs\n"); + return ret; + } + + data[RTC_SEC] = 0; + ret = regmap_bulk_read(rtc->regmap, + config->rtc_alarm_secs_reg, + &data[config->rtc_data_start], + config->rtc_alarm_len); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read initial alarm data: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, rtc); + + 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->rtc_sync = false; + + /* + * TODO: some models have alarms on a minute boundary but still support + * real hardware interrupts. Add this once the core supports it. + */ + if (config->rtc_data_start != RTC_SEC) + rtc->rtc_dev->uie_unsupported = 1; + + 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); + + return ret; +} + +static struct platform_driver da9063_rtc_driver = { + .probe = da9063_rtc_probe, + .driver = { + .name = DA9063_DRVNAME_RTC, + .of_match_table = da9063_compatible_reg_id_table, + }, +}; + +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"); +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..caf35567e --- /dev/null +++ b/drivers/rtc/rtc-davinci.c @@ -0,0 +1,561 @@ +/* + * 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; + + alm->time.tm_sec = 0; + + 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 const 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 = { + .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..43d962a9c --- /dev/null +++ b/drivers/rtc/rtc-dev.c @@ -0,0 +1,483 @@ +/* + * 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/signal.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) +{ + struct rtc_device *rtc = container_of(inode->i_cdev, + struct rtc_device, char_dev); + + if (test_and_set_bit_lock(RTC_DEV_BUSY, &rtc->flags)) + return -EBUSY; + + file->private_data = rtc; + + spin_lock_irq(&rtc->irq_lock); + rtc->irq_data = 0; + spin_unlock_irq(&rtc->irq_lock); + + return 0; +} + +#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(struct timer_list *t) +{ + struct rtc_device *rtc = from_timer(rtc, t, uie_timer); + 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 __poll_t 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) ? (EPOLLIN | EPOLLRDNORM) : 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, 1); + break; + + case RTC_PIE_OFF: + err = rtc_irq_set_state(rtc, 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, 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, 0); + + 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, "too many RTC devices\n"); + 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); + timer_setup(&rtc->uie_timer, rtc_uie_timer, 0); +#endif + + cdev_init(&rtc->char_dev, &rtc_dev_fops); + rtc->char_dev.owner = rtc->owner; +} + +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..b253bf1b3 --- /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 const 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..97d8259b9 --- /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/mfd/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 const 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..50fabe1cd --- /dev/null +++ b/drivers/rtc/rtc-ds1216.c @@ -0,0 +1,175 @@ +/* + * 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> + +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 ds1216_priv *priv = dev_get_drvdata(dev); + struct ds1216_regs regs; + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_read(priv->ioaddr, (u8 *)®s); + + 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 0; +} + +static int ds1216_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ds1216_priv *priv = dev_get_drvdata(dev); + struct ds1216_regs regs; + + ds1216_switch_ds_to_clock(priv->ioaddr); + ds1216_read(priv->ioaddr, (u8 *)®s); + + 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 *)®s); + 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]; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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_ALIAS("platform:rtc-ds1216"); diff --git a/drivers/rtc/rtc-ds1286.c b/drivers/rtc/rtc-ds1286.c new file mode 100644 index 000000000..0744916b7 --- /dev/null +++ b/drivers/rtc/rtc-ds1286.c @@ -0,0 +1,364 @@ +/* + * 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/rtc/ds1286.h> +#include <linux/io.h> +#include <linux/slab.h> + +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 0; +} + +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; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct ds1286_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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_ALIAS("platform:rtc-ds1286"); diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c new file mode 100644 index 000000000..2a881150d --- /dev/null +++ b/drivers/rtc/rtc-ds1302.c @@ -0,0 +1,218 @@ +/* + * 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/bcd.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> + +#define DRV_NAME "rtc-ds1302" + +#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_CLCK_BURST 0x1F /* Address of clock burst */ +#define RTC_CLCK_LEN 0x08 /* Size of clock burst */ +#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 */ + +static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *time) +{ + struct spi_device *spi = dev_get_drvdata(dev); + u8 buf[1 + RTC_CLCK_LEN]; + u8 *bp; + int status; + + /* Enable writing */ + bp = buf; + *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; + *bp++ = RTC_CMD_WRITE_ENABLE; + + status = spi_write_then_read(spi, buf, 2, + NULL, 0); + if (status) + return status; + + /* Write registers starting at the first time/date address. */ + bp = buf; + *bp++ = RTC_CLCK_BURST << 1 | RTC_CMD_WRITE; + + *bp++ = bin2bcd(time->tm_sec); + *bp++ = bin2bcd(time->tm_min); + *bp++ = bin2bcd(time->tm_hour); + *bp++ = bin2bcd(time->tm_mday); + *bp++ = bin2bcd(time->tm_mon + 1); + *bp++ = time->tm_wday + 1; + *bp++ = bin2bcd(time->tm_year % 100); + *bp++ = RTC_CMD_WRITE_DISABLE; + + /* use write-then-read since dma from stack is nonportable */ + return spi_write_then_read(spi, buf, sizeof(buf), + NULL, 0); +} + +static int ds1302_rtc_get_time(struct device *dev, struct rtc_time *time) +{ + struct spi_device *spi = dev_get_drvdata(dev); + u8 addr = RTC_CLCK_BURST << 1 | RTC_CMD_READ; + u8 buf[RTC_CLCK_LEN - 1]; + int status; + + /* Use write-then-read to get all the date/time registers + * since dma from stack is nonportable + */ + status = spi_write_then_read(spi, &addr, sizeof(addr), + buf, sizeof(buf)); + if (status < 0) + return status; + + /* Decode the registers */ + time->tm_sec = bcd2bin(buf[RTC_ADDR_SEC]); + time->tm_min = bcd2bin(buf[RTC_ADDR_MIN]); + time->tm_hour = bcd2bin(buf[RTC_ADDR_HOUR]); + time->tm_wday = buf[RTC_ADDR_DAY] - 1; + time->tm_mday = bcd2bin(buf[RTC_ADDR_DATE]); + time->tm_mon = bcd2bin(buf[RTC_ADDR_MON]) - 1; + time->tm_year = bcd2bin(buf[RTC_ADDR_YEAR]) + 100; + + return 0; +} + +static const struct rtc_class_ops ds1302_rtc_ops = { + .read_time = ds1302_rtc_get_time, + .set_time = ds1302_rtc_set_time, +}; + +static int ds1302_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + u8 addr; + u8 buf[4]; + u8 *bp; + int status; + + /* 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)) { + dev_err(&spi->dev, "bad word length\n"); + return -EINVAL; + } else if (spi->max_speed_hz > 2000000) { + dev_err(&spi->dev, "speed is too high\n"); + return -EINVAL; + } else if (spi->mode & SPI_CPHA) { + dev_err(&spi->dev, "bad mode\n"); + return -EINVAL; + } + + addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; + status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status < 0) { + dev_err(&spi->dev, "control register read error %d\n", + status); + return status; + } + + if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) { + status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status < 0) { + dev_err(&spi->dev, "control register read error %d\n", + status); + return status; + } + + if ((buf[0] & ~RTC_CMD_WRITE_DISABLE) != 0) { + dev_err(&spi->dev, "junk in control register\n"); + return -ENODEV; + } + } + if (buf[0] == 0) { + bp = buf; + *bp++ = RTC_ADDR_CTRL << 1 | RTC_CMD_WRITE; + *bp++ = RTC_CMD_WRITE_DISABLE; + + status = spi_write_then_read(spi, buf, 2, NULL, 0); + if (status < 0) { + dev_err(&spi->dev, "control register write error %d\n", + status); + return status; + } + + addr = RTC_ADDR_CTRL << 1 | RTC_CMD_READ; + status = spi_write_then_read(spi, &addr, sizeof(addr), buf, 1); + if (status < 0) { + dev_err(&spi->dev, + "error %d reading control register\n", + status); + return status; + } + + if (buf[0] != RTC_CMD_WRITE_DISABLE) { + dev_err(&spi->dev, "failed to detect chip\n"); + return -ENODEV; + } + } + + spi_set_drvdata(spi, spi); + + rtc = devm_rtc_device_register(&spi->dev, "ds1302", + &ds1302_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + status = PTR_ERR(rtc); + dev_err(&spi->dev, "error %d registering rtc\n", status); + return status; + } + + return 0; +} + +static int ds1302_remove(struct spi_device *spi) +{ + spi_set_drvdata(spi, NULL); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ds1302_dt_ids[] = { + { .compatible = "maxim,ds1302", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ds1302_dt_ids); +#endif + +static struct spi_driver ds1302_driver = { + .driver.name = "rtc-ds1302", + .driver.of_match_table = of_match_ptr(ds1302_dt_ids), + .probe = ds1302_probe, + .remove = ds1302_remove, +}; + +module_spi_driver(ds1302_driver); + +MODULE_DESCRIPTION("Dallas DS1302 RTC driver"); +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..2d502fc85 --- /dev/null +++ b/drivers/rtc/rtc-ds1305.c @@ -0,0 +1,759 @@ +/* + * 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: %3ph, %4ph\n", "read", &buf[0], &buf[3]); + + /* 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); + + return 0; +} + +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: %3ph, %4ph\n", "write", &buf[1], &buf[4]); + + /* 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]); + + 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 int ds1305_nvram_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct ds1305 *ds1305 = priv; + struct spi_device *spi = ds1305->spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + + addr = DS1305_NVRAM + off; + msg_init(&m, x, &addr, count, NULL, buf); + + return spi_sync(spi, &m); +} + +static int ds1305_nvram_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct ds1305 *ds1305 = priv; + struct spi_device *spi = ds1305->spi; + u8 addr; + struct spi_message m; + struct spi_transfer x[2]; + + addr = (DS1305_WRITE | DS1305_NVRAM) + off; + msg_init(&m, x, &addr, count, buf, NULL); + + return spi_sync(spi, &m); +} + +/*----------------------------------------------------------------------*/ + +/* + * 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; + struct nvmem_config ds1305_nvmem_cfg = { + .name = "ds1305_nvram", + .word_size = 1, + .stride = 1, + .size = DS1305_NVRAM_LEN, + .reg_read = ds1305_nvram_read, + .reg_write = ds1305_nvram_write, + }; + + /* 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_allocate_device(&spi->dev); + if (IS_ERR(ds1305->rtc)) { + return PTR_ERR(ds1305->rtc); + } + + ds1305->rtc->ops = &ds1305_ops; + + ds1305_nvmem_cfg.priv = ds1305; + ds1305->rtc->nvram_old_abi = true; + status = rtc_register_device(ds1305->rtc); + if (status) { + dev_dbg(&spi->dev, "register rtc --> %d\n", status); + return status; + } + + rtc_nvmem_register(ds1305->rtc, &ds1305_nvmem_cfg); + + /* 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); + } + } + + return 0; +} + +static int ds1305_remove(struct spi_device *spi) +{ + struct ds1305 *ds1305 = spi_get_drvdata(spi); + + /* 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", + .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..94d317799 --- /dev/null +++ b/drivers/rtc/rtc-ds1307.c @@ -0,0 +1,1761 @@ +/* + * 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/acpi.h> +#include <linux/bcd.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/rtc/ds1307.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/clk-provider.h> +#include <linux/regmap.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_1308, + ds_1337, + ds_1338, + ds_1339, + ds_1340, + ds_1341, + ds_1388, + ds_3231, + m41t0, + m41t00, + m41t11, + mcp794xx, + rx_8025, + rx_8130, + 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 M41T0_BIT_OF 0x80 +#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 DS3231_BIT_EN32KHZ 0x08 +# 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 { + 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 device *dev; + struct regmap *regmap; + const char *name; + struct rtc_device *rtc; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clks[2]; +#endif +}; + +struct chip_desc { + unsigned alarm:1; + u16 nvram_offset; + u16 nvram_size; + u8 offset; /* register's offset */ + u8 century_reg; + u8 century_enable_bit; + u8 century_bit; + u8 bbsqi_bit; + irq_handler_t irq_handler; + const struct rtc_class_ops *rtc_ops; + u16 trickle_charger_reg; + u8 (*do_trickle_setup)(struct ds1307 *, u32, + bool); +}; + +static int ds1307_get_time(struct device *dev, struct rtc_time *t); +static int ds1307_set_time(struct device *dev, struct rtc_time *t); +static u8 do_trickle_setup_ds1339(struct ds1307 *, u32 ohms, bool diode); +static irqreturn_t rx8130_irq(int irq, void *dev_id); +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t); +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t); +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled); +static irqreturn_t mcp794xx_irq(int irq, void *dev_id); +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t); +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t); +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled); + +static const struct rtc_class_ops rx8130_rtc_ops = { + .read_time = ds1307_get_time, + .set_time = ds1307_set_time, + .read_alarm = rx8130_read_alarm, + .set_alarm = rx8130_set_alarm, + .alarm_irq_enable = rx8130_alarm_irq_enable, +}; + +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 const struct chip_desc chips[last_ds_type] = { + [ds_1307] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1308] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1337] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, + [ds_1338] = { + .nvram_offset = 8, + .nvram_size = 56, + }, + [ds_1339] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS1339_BIT_BBSQI, + .trickle_charger_reg = 0x10, + .do_trickle_setup = &do_trickle_setup_ds1339, + }, + [ds_1340] = { + .century_reg = DS1307_REG_HOUR, + .century_enable_bit = DS1340_BIT_CENTURY_EN, + .century_bit = DS1340_BIT_CENTURY, + .do_trickle_setup = &do_trickle_setup_ds1339, + .trickle_charger_reg = 0x08, + }, + [ds_1341] = { + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + }, + [ds_1388] = { + .offset = 1, + .trickle_charger_reg = 0x0a, + }, + [ds_3231] = { + .alarm = 1, + .century_reg = DS1307_REG_MONTH, + .century_bit = DS1337_BIT_CENTURY, + .bbsqi_bit = DS3231_BIT_BBSQW, + }, + [rx_8130] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 4, /* 32bit (4 word x 8 bit) */ + .offset = 0x10, + .irq_handler = rx8130_irq, + .rtc_ops = &rx8130_rtc_ops, + }, + [m41t11] = { + /* this is battery backed SRAM */ + .nvram_offset = 8, + .nvram_size = 56, + }, + [mcp794xx] = { + .alarm = 1, + /* this is battery backed SRAM */ + .nvram_offset = 0x20, + .nvram_size = 0x40, + .irq_handler = mcp794xx_irq, + .rtc_ops = &mcp794xx_rtc_ops, + }, +}; + +static const struct i2c_device_id ds1307_id[] = { + { "ds1307", ds_1307 }, + { "ds1308", ds_1308 }, + { "ds1337", ds_1337 }, + { "ds1338", ds_1338 }, + { "ds1339", ds_1339 }, + { "ds1388", ds_1388 }, + { "ds1340", ds_1340 }, + { "ds1341", ds_1341 }, + { "ds3231", ds_3231 }, + { "m41t0", m41t0 }, + { "m41t00", m41t00 }, + { "m41t11", m41t11 }, + { "mcp7940x", mcp794xx }, + { "mcp7941x", mcp794xx }, + { "pt7c4338", ds_1307 }, + { "rx8025", rx_8025 }, + { "isl12057", ds_1337 }, + { "rx8130", rx_8130 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1307_id); + +#ifdef CONFIG_OF +static const struct of_device_id ds1307_of_match[] = { + { + .compatible = "dallas,ds1307", + .data = (void *)ds_1307 + }, + { + .compatible = "dallas,ds1308", + .data = (void *)ds_1308 + }, + { + .compatible = "dallas,ds1337", + .data = (void *)ds_1337 + }, + { + .compatible = "dallas,ds1338", + .data = (void *)ds_1338 + }, + { + .compatible = "dallas,ds1339", + .data = (void *)ds_1339 + }, + { + .compatible = "dallas,ds1388", + .data = (void *)ds_1388 + }, + { + .compatible = "dallas,ds1340", + .data = (void *)ds_1340 + }, + { + .compatible = "dallas,ds1341", + .data = (void *)ds_1341 + }, + { + .compatible = "maxim,ds3231", + .data = (void *)ds_3231 + }, + { + .compatible = "st,m41t0", + .data = (void *)m41t0 + }, + { + .compatible = "st,m41t00", + .data = (void *)m41t00 + }, + { + .compatible = "st,m41t11", + .data = (void *)m41t11 + }, + { + .compatible = "microchip,mcp7940x", + .data = (void *)mcp794xx + }, + { + .compatible = "microchip,mcp7941x", + .data = (void *)mcp794xx + }, + { + .compatible = "pericom,pt7c4338", + .data = (void *)ds_1307 + }, + { + .compatible = "epson,rx8025", + .data = (void *)rx_8025 + }, + { + .compatible = "isil,isl12057", + .data = (void *)ds_1337 + }, + { + .compatible = "epson,rx8130", + .data = (void *)rx_8130 + }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1307_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id ds1307_acpi_ids[] = { + { .id = "DS1307", .driver_data = ds_1307 }, + { .id = "DS1308", .driver_data = ds_1308 }, + { .id = "DS1337", .driver_data = ds_1337 }, + { .id = "DS1338", .driver_data = ds_1338 }, + { .id = "DS1339", .driver_data = ds_1339 }, + { .id = "DS1388", .driver_data = ds_1388 }, + { .id = "DS1340", .driver_data = ds_1340 }, + { .id = "DS1341", .driver_data = ds_1341 }, + { .id = "DS3231", .driver_data = ds_3231 }, + { .id = "M41T0", .driver_data = m41t0 }, + { .id = "M41T00", .driver_data = m41t00 }, + { .id = "M41T11", .driver_data = m41t11 }, + { .id = "MCP7940X", .driver_data = mcp794xx }, + { .id = "MCP7941X", .driver_data = mcp794xx }, + { .id = "PT7C4338", .driver_data = ds_1307 }, + { .id = "RX8025", .driver_data = rx_8025 }, + { .id = "ISL12057", .driver_data = ds_1337 }, + { .id = "RX8130", .driver_data = rx_8130 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids); +#endif + +/* + * 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 irqreturn_t ds1307_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int stat, ret; + + mutex_lock(lock); + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &stat); + if (ret) + goto out; + + if (stat & DS1337_BIT_A1I) { + stat &= ~DS1337_BIT_A1I; + regmap_write(ds1307->regmap, DS1337_REG_STATUS, stat); + + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +/*----------------------------------------------------------------------*/ + +static int ds1307_get_time(struct device *dev, struct rtc_time *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int tmp, ret; + const struct chip_desc *chip = &chips[ds1307->type]; + u8 regs[7]; + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "read", ret); + return ret; + } + + dev_dbg(dev, "%s: %7ph\n", "read", regs); + + /* if oscillator fail bit is set, no data can be trusted */ + if (ds1307->type == m41t0 && + regs[DS1307_REG_MIN] & M41T0_BIT_OF) { + dev_warn_once(dev, "oscillator failed, set time!\n"); + return -EINVAL; + } + + t->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f); + t->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f); + tmp = regs[DS1307_REG_HOUR] & 0x3f; + t->tm_hour = bcd2bin(tmp); + /* rx8130 is bit position, not BCD */ + if (ds1307->type == rx_8130) + t->tm_wday = fls(regs[DS1307_REG_WDAY] & 0x7f); + else + t->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1; + t->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f); + tmp = regs[DS1307_REG_MONTH] & 0x1f; + t->tm_mon = bcd2bin(tmp) - 1; + t->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100; + + if (regs[chip->century_reg] & chip->century_bit && + IS_ENABLED(CONFIG_RTC_DRV_DS1307_CENTURY)) + t->tm_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); + + return 0; +} + +static int ds1307_set_time(struct device *dev, struct rtc_time *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + const struct chip_desc *chip = &chips[ds1307->type]; + int result; + int tmp; + u8 regs[7]; + + 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); + + if (t->tm_year < 100) + return -EINVAL; + +#ifdef CONFIG_RTC_DRV_DS1307_CENTURY + if (t->tm_year > (chip->century_bit ? 299 : 199)) + return -EINVAL; +#else + if (t->tm_year > 199) + return -EINVAL; +#endif + + regs[DS1307_REG_SECS] = bin2bcd(t->tm_sec); + regs[DS1307_REG_MIN] = bin2bcd(t->tm_min); + regs[DS1307_REG_HOUR] = bin2bcd(t->tm_hour); + /* rx8130 is bit position, not BCD */ + if (ds1307->type == rx_8130) + regs[DS1307_REG_WDAY] = 1 << t->tm_wday; + else + regs[DS1307_REG_WDAY] = bin2bcd(t->tm_wday + 1); + regs[DS1307_REG_MDAY] = bin2bcd(t->tm_mday); + regs[DS1307_REG_MONTH] = bin2bcd(t->tm_mon + 1); + + /* assume 20YY not 19YY */ + tmp = t->tm_year - 100; + regs[DS1307_REG_YEAR] = bin2bcd(tmp); + + if (chip->century_enable_bit) + regs[chip->century_reg] |= chip->century_enable_bit; + if (t->tm_year > 199 && chip->century_bit) + regs[chip->century_reg] |= chip->century_bit; + + if (ds1307->type == mcp794xx) { + /* + * these bits were cleared when preparing the date/time + * values and need to be set again before writing the + * regsfer out to the device. + */ + regs[DS1307_REG_SECS] |= MCP794XX_BIT_ST; + regs[DS1307_REG_WDAY] |= MCP794XX_BIT_VBATEN; + } + + dev_dbg(dev, "%s: %7ph\n", "write", regs); + + result = regmap_bulk_write(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (result) { + 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 ds1307 *ds1307 = dev_get_drvdata(dev); + int ret; + u8 regs[9]; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* read all ALARM1, ALARM2, and status registers at once */ + ret = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, + regs, sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "alarm read", ret); + return ret; + } + + dev_dbg(dev, "%s: %4ph, %3ph, %2ph\n", "alarm read", + ®s[0], ®s[4], ®s[7]); + + /* + * 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(regs[0] & 0x7f); + t->time.tm_min = bcd2bin(regs[1] & 0x7f); + t->time.tm_hour = bcd2bin(regs[2] & 0x3f); + t->time.tm_mday = bcd2bin(regs[3] & 0x3f); + + /* ... and status */ + t->enabled = !!(regs[7] & DS1337_BIT_A1IE); + t->pending = !!(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 ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[9]; + 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 = regmap_bulk_read(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "%s error %d\n", "alarm write", ret); + return ret; + } + control = regs[7]; + status = regs[8]; + + dev_dbg(dev, "%s: %4ph, %3ph, %02x %02x\n", "alarm set (old status)", + ®s[0], ®s[4], control, status); + + /* set ALARM1, using 24 hour and day-of-month modes */ + regs[0] = bin2bcd(t->time.tm_sec); + regs[1] = bin2bcd(t->time.tm_min); + regs[2] = bin2bcd(t->time.tm_hour); + regs[3] = bin2bcd(t->time.tm_mday); + + /* set ALARM2 to non-garbage */ + regs[4] = 0; + regs[5] = 0; + regs[6] = 0; + + /* disable alarms */ + regs[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE); + regs[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I); + + ret = regmap_bulk_write(ds1307->regmap, DS1339_REG_ALARM1_SECS, regs, + sizeof(regs)); + if (ret) { + dev_err(dev, "can't set alarm time\n"); + return ret; + } + + /* optionally enable ALARM1 */ + if (t->enabled) { + dev_dbg(dev, "alarm IRQ armed\n"); + regs[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */ + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, regs[7]); + } + + return 0; +} + +static int ds1307_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -ENOTTY; + + return regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + DS1337_BIT_A1IE, + enabled ? DS1337_BIT_A1IE : 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 rx8130 devices. + */ + +#define RX8130_REG_ALARM_MIN 0x07 +#define RX8130_REG_ALARM_HOUR 0x08 +#define RX8130_REG_ALARM_WEEK_OR_DAY 0x09 +#define RX8130_REG_EXTENSION 0x0c +#define RX8130_REG_EXTENSION_WADA BIT(3) +#define RX8130_REG_FLAG 0x0d +#define RX8130_REG_FLAG_AF BIT(3) +#define RX8130_REG_CONTROL0 0x0e +#define RX8130_REG_CONTROL0_AIE BIT(3) + +static irqreturn_t rx8130_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + u8 ctl[3]; + int ret; + + mutex_lock(lock); + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + goto out; + if (!(ctl[1] & RX8130_REG_FLAG_AF)) + goto out; + ctl[1] &= ~RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int rx8130_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read alarm registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); + if (ret < 0) + return ret; + + /* Read control registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + t->enabled = !!(ctl[2] & RX8130_REG_CONTROL0_AIE); + t->pending = !!(ctl[1] & RX8130_REG_FLAG_AF); + + /* Report alarm 0 time assuming 24-hour and day-of-month modes. */ + t->time.tm_sec = -1; + t->time.tm_min = bcd2bin(ald[0] & 0x7f); + t->time.tm_hour = bcd2bin(ald[1] & 0x7f); + t->time.tm_wday = -1; + t->time.tm_mday = bcd2bin(ald[2] & 0x7f); + t->time.tm_mon = -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\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); + + return 0; +} + +static int rx8130_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 ald[3], ctl[3]; + 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 registers. */ + ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + ctl[0] &= RX8130_REG_EXTENSION_WADA; + ctl[1] &= ~RX8130_REG_FLAG_AF; + ctl[2] &= ~RX8130_REG_CONTROL0_AIE; + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl, + sizeof(ctl)); + if (ret < 0) + return ret; + + /* Hardware alarm precision is 1 minute! */ + ald[0] = bin2bcd(t->time.tm_min); + ald[1] = bin2bcd(t->time.tm_hour); + ald[2] = bin2bcd(t->time.tm_mday); + + ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_ALARM_MIN, ald, + sizeof(ald)); + if (ret < 0) + return ret; + + if (!t->enabled) + return 0; + + ctl[2] |= RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, ctl[2]); +} + +static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + int ret, reg; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + ret = regmap_read(ds1307->regmap, RX8130_REG_CONTROL0, ®); + if (ret < 0) + return ret; + + if (enabled) + reg |= RX8130_REG_CONTROL0_AIE; + else + reg &= ~RX8130_REG_CONTROL0_AIE; + + return regmap_write(ds1307->regmap, RX8130_REG_CONTROL0, reg); +} + +/*----------------------------------------------------------------------*/ + +/* + * 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 BIT(3) +# define MCP794XX_BIT_ALMX_C0 BIT(4) +# define MCP794XX_BIT_ALMX_C1 BIT(5) +# define MCP794XX_BIT_ALMX_C2 BIT(6) +# define MCP794XX_BIT_ALMX_POL BIT(7) +# define MCP794XX_MSK_ALMX_MATCH (MCP794XX_BIT_ALMX_C0 | \ + MCP794XX_BIT_ALMX_C1 | \ + MCP794XX_BIT_ALMX_C2) + +static irqreturn_t mcp794xx_irq(int irq, void *dev_id) +{ + struct ds1307 *ds1307 = dev_id; + struct mutex *lock = &ds1307->rtc->ops_lock; + int reg, ret; + + mutex_lock(lock); + + /* Check and clear alarm 0 interrupt flag. */ + ret = regmap_read(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, ®); + if (ret) + goto out; + if (!(reg & MCP794XX_BIT_ALMX_IF)) + goto out; + reg &= ~MCP794XX_BIT_ALMX_IF; + ret = regmap_write(ds1307->regmap, MCP794XX_REG_ALARM0_CTRL, reg); + if (ret) + goto out; + + /* Disable alarm 0. */ + ret = regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, 0); + if (ret) + goto out; + + rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF); + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 regs[10]; + int ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + /* Read control and alarm 0 registers. */ + ret = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + 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(regs[3] & 0x7f); + t->time.tm_min = bcd2bin(regs[4] & 0x7f); + t->time.tm_hour = bcd2bin(regs[5] & 0x3f); + t->time.tm_wday = bcd2bin(regs[6] & 0x7) - 1; + t->time.tm_mday = bcd2bin(regs[7] & 0x3f); + t->time.tm_mon = bcd2bin(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=%lu\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, + !!(regs[6] & MCP794XX_BIT_ALMX_POL), + !!(regs[6] & MCP794XX_BIT_ALMX_IF), + (regs[6] & MCP794XX_MSK_ALMX_MATCH) >> 4); + + return 0; +} + +/* + * We may have a random RTC weekday, therefore calculate alarm weekday based + * on current weekday we read from the RTC timekeeping regs + */ +static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm) +{ + struct rtc_time tm_now; + int days_now, days_alarm, ret; + + ret = ds1307_get_time(dev, &tm_now); + if (ret) + return ret; + + days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60); + days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60); + + return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1; +} + +static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + unsigned char regs[10]; + int wday, ret; + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + wday = mcp794xx_alm_weekday(dev, &t->time); + if (wday < 0) + return wday; + + 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 = regmap_bulk_read(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + 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] = wday; + 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; + /* Disable interrupt. We will not enable until completely programmed */ + regs[0] &= ~MCP794XX_BIT_ALM0_EN; + + ret = regmap_bulk_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs, + sizeof(regs)); + if (ret) + return ret; + + if (!t->enabled) + return 0; + regs[0] |= MCP794XX_BIT_ALM0_EN; + return regmap_write(ds1307->regmap, MCP794XX_REG_CONTROL, regs[0]); +} + +static int mcp794xx_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + + if (!test_bit(HAS_ALARM, &ds1307->flags)) + return -EINVAL; + + return regmap_update_bits(ds1307->regmap, MCP794XX_REG_CONTROL, + MCP794XX_BIT_ALM0_EN, + enabled ? MCP794XX_BIT_ALM0_EN : 0); +} + +/*----------------------------------------------------------------------*/ + +static int ds1307_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; + + return regmap_bulk_read(ds1307->regmap, chip->nvram_offset + offset, + val, bytes); +} + +static int ds1307_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct ds1307 *ds1307 = priv; + const struct chip_desc *chip = &chips[ds1307->type]; + + return regmap_bulk_write(ds1307->regmap, chip->nvram_offset + offset, + val, bytes); +} + +/*----------------------------------------------------------------------*/ + +static u8 do_trickle_setup_ds1339(struct ds1307 *ds1307, + u32 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(ds1307->dev, + "Unsupported ohm value %u in dt\n", ohms); + return 0; + } + return setup; +} + +static u8 ds1307_trickle_init(struct ds1307 *ds1307, + const struct chip_desc *chip) +{ + u32 ohms; + bool diode = true; + + if (!chip->do_trickle_setup) + return 0; + + if (device_property_read_u32(ds1307->dev, "trickle-resistor-ohms", + &ohms)) + return 0; + + if (device_property_read_bool(ds1307->dev, "trickle-diode-disable")) + diode = false; + + return chip->do_trickle_setup(ds1307, ohms, diode); +} + +/*----------------------------------------------------------------------*/ + +#if IS_REACHABLE(CONFIG_HWMON) + +/* + * Temperature sensor support for ds3231 devices. + */ + +#define DS3231_REG_TEMPERATURE 0x11 + +/* + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every 64 seconds. + */ +static int ds3231_hwmon_read_temp(struct device *dev, s32 *mC) +{ + struct ds1307 *ds1307 = dev_get_drvdata(dev); + u8 temp_buf[2]; + s16 temp; + int ret; + + ret = regmap_bulk_read(ds1307->regmap, DS3231_REG_TEMPERATURE, + temp_buf, sizeof(temp_buf)); + if (ret) + return ret; + /* + * Temperature is represented as a 10-bit code with a resolution of + * 0.25 degree celsius and encoded in two's complement format. + */ + temp = (temp_buf[0] << 8) | temp_buf[1]; + temp >>= 6; + *mC = temp * 250; + + return 0; +} + +static ssize_t ds3231_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + s32 temp; + + ret = ds3231_hwmon_read_temp(dev, &temp); + if (ret) + return ret; + + return sprintf(buf, "%d\n", temp); +} +static SENSOR_DEVICE_ATTR(temp1_input, 0444, ds3231_hwmon_show_temp, + NULL, 0); + +static struct attribute *ds3231_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ds3231_hwmon); + +static void ds1307_hwmon_register(struct ds1307 *ds1307) +{ + struct device *dev; + + if (ds1307->type != ds_3231) + return; + + dev = devm_hwmon_device_register_with_groups(ds1307->dev, ds1307->name, + ds1307, + ds3231_hwmon_groups); + if (IS_ERR(dev)) { + dev_warn(ds1307->dev, "unable to register hwmon device %ld\n", + PTR_ERR(dev)); + } +} + +#else + +static void ds1307_hwmon_register(struct ds1307 *ds1307) +{ +} + +#endif /* CONFIG_RTC_DRV_DS1307_HWMON */ + +/*----------------------------------------------------------------------*/ + +/* + * Square-wave output support for DS3231 + * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3231.pdf + */ +#ifdef CONFIG_COMMON_CLK + +enum { + DS3231_CLK_SQW = 0, + DS3231_CLK_32KHZ, +}; + +#define clk_sqw_to_ds1307(clk) \ + container_of(clk, struct ds1307, clks[DS3231_CLK_SQW]) +#define clk_32khz_to_ds1307(clk) \ + container_of(clk, struct ds1307, clks[DS3231_CLK_32KHZ]) + +static int ds3231_clk_sqw_rates[] = { + 1, + 1024, + 4096, + 8192, +}; + +static int ds1337_write_control(struct ds1307 *ds1307, u8 mask, u8 value) +{ + struct mutex *lock = &ds1307->rtc->ops_lock; + int ret; + + mutex_lock(lock); + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_CONTROL, + mask, value); + mutex_unlock(lock); + + return ret; +} + +static unsigned long ds3231_clk_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control, ret; + int rate_sel = 0; + + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) + return ret; + if (control & DS1337_BIT_RS1) + rate_sel += 1; + if (control & DS1337_BIT_RS2) + rate_sel += 2; + + return ds3231_clk_sqw_rates[rate_sel]; +} + +static long ds3231_clk_sqw_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + + for (i = ARRAY_SIZE(ds3231_clk_sqw_rates) - 1; i >= 0; i--) { + if (ds3231_clk_sqw_rates[i] <= rate) + return ds3231_clk_sqw_rates[i]; + } + + return 0; +} + +static int ds3231_clk_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control = 0; + int rate_sel; + + for (rate_sel = 0; rate_sel < ARRAY_SIZE(ds3231_clk_sqw_rates); + rate_sel++) { + if (ds3231_clk_sqw_rates[rate_sel] == rate) + break; + } + + if (rate_sel == ARRAY_SIZE(ds3231_clk_sqw_rates)) + return -EINVAL; + + if (rate_sel & 1) + control |= DS1337_BIT_RS1; + if (rate_sel & 2) + control |= DS1337_BIT_RS2; + + return ds1337_write_control(ds1307, DS1337_BIT_RS1 | DS1337_BIT_RS2, + control); +} + +static int ds3231_clk_sqw_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + return ds1337_write_control(ds1307, DS1337_BIT_INTCN, 0); +} + +static void ds3231_clk_sqw_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + + ds1337_write_control(ds1307, DS1337_BIT_INTCN, DS1337_BIT_INTCN); +} + +static int ds3231_clk_sqw_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_sqw_to_ds1307(hw); + int control, ret; + + ret = regmap_read(ds1307->regmap, DS1337_REG_CONTROL, &control); + if (ret) + return ret; + + return !(control & DS1337_BIT_INTCN); +} + +static const struct clk_ops ds3231_clk_sqw_ops = { + .prepare = ds3231_clk_sqw_prepare, + .unprepare = ds3231_clk_sqw_unprepare, + .is_prepared = ds3231_clk_sqw_is_prepared, + .recalc_rate = ds3231_clk_sqw_recalc_rate, + .round_rate = ds3231_clk_sqw_round_rate, + .set_rate = ds3231_clk_sqw_set_rate, +}; + +static unsigned long ds3231_clk_32khz_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 32768; +} + +static int ds3231_clk_32khz_control(struct ds1307 *ds1307, bool enable) +{ + struct mutex *lock = &ds1307->rtc->ops_lock; + int ret; + + mutex_lock(lock); + ret = regmap_update_bits(ds1307->regmap, DS1337_REG_STATUS, + DS3231_BIT_EN32KHZ, + enable ? DS3231_BIT_EN32KHZ : 0); + mutex_unlock(lock); + + return ret; +} + +static int ds3231_clk_32khz_prepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + + return ds3231_clk_32khz_control(ds1307, true); +} + +static void ds3231_clk_32khz_unprepare(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + + ds3231_clk_32khz_control(ds1307, false); +} + +static int ds3231_clk_32khz_is_prepared(struct clk_hw *hw) +{ + struct ds1307 *ds1307 = clk_32khz_to_ds1307(hw); + int status, ret; + + ret = regmap_read(ds1307->regmap, DS1337_REG_STATUS, &status); + if (ret) + return ret; + + return !!(status & DS3231_BIT_EN32KHZ); +} + +static const struct clk_ops ds3231_clk_32khz_ops = { + .prepare = ds3231_clk_32khz_prepare, + .unprepare = ds3231_clk_32khz_unprepare, + .is_prepared = ds3231_clk_32khz_is_prepared, + .recalc_rate = ds3231_clk_32khz_recalc_rate, +}; + +static struct clk_init_data ds3231_clks_init[] = { + [DS3231_CLK_SQW] = { + .name = "ds3231_clk_sqw", + .ops = &ds3231_clk_sqw_ops, + }, + [DS3231_CLK_32KHZ] = { + .name = "ds3231_clk_32khz", + .ops = &ds3231_clk_32khz_ops, + }, +}; + +static int ds3231_clks_register(struct ds1307 *ds1307) +{ + struct device_node *node = ds1307->dev->of_node; + struct clk_onecell_data *onecell; + int i; + + onecell = devm_kzalloc(ds1307->dev, sizeof(*onecell), GFP_KERNEL); + if (!onecell) + return -ENOMEM; + + onecell->clk_num = ARRAY_SIZE(ds3231_clks_init); + onecell->clks = devm_kcalloc(ds1307->dev, onecell->clk_num, + sizeof(onecell->clks[0]), GFP_KERNEL); + if (!onecell->clks) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(ds3231_clks_init); i++) { + struct clk_init_data init = ds3231_clks_init[i]; + + /* + * Interrupt signal due to alarm conditions and square-wave + * output share same pin, so don't initialize both. + */ + if (i == DS3231_CLK_SQW && test_bit(HAS_ALARM, &ds1307->flags)) + continue; + + /* optional override of the clockname */ + of_property_read_string_index(node, "clock-output-names", i, + &init.name); + ds1307->clks[i].init = &init; + + onecell->clks[i] = devm_clk_register(ds1307->dev, + &ds1307->clks[i]); + if (IS_ERR(onecell->clks[i])) + return PTR_ERR(onecell->clks[i]); + } + + if (!node) + return 0; + + of_clk_add_provider(node, of_clk_src_onecell_get, onecell); + + return 0; +} + +static void ds1307_clks_register(struct ds1307 *ds1307) +{ + int ret; + + if (ds1307->type != ds_3231) + return; + + ret = ds3231_clks_register(ds1307); + if (ret) { + dev_warn(ds1307->dev, "unable to register clock device %d\n", + ret); + } +} + +#else + +static void ds1307_clks_register(struct ds1307 *ds1307) +{ +} + +#endif /* CONFIG_COMMON_CLK */ + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ds1307_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ds1307 *ds1307; + int err = -ENODEV; + int tmp; + const struct chip_desc *chip; + bool want_irq; + bool ds1307_can_wakeup_device = false; + unsigned char regs[8]; + struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev); + u8 trickle_charger_setup = 0; + + ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL); + if (!ds1307) + return -ENOMEM; + + dev_set_drvdata(&client->dev, ds1307); + ds1307->dev = &client->dev; + ds1307->name = client->name; + + ds1307->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(ds1307->regmap)) { + dev_err(ds1307->dev, "regmap allocation failed\n"); + return PTR_ERR(ds1307->regmap); + } + + i2c_set_clientdata(client, ds1307); + + if (client->dev.of_node) { + ds1307->type = (enum ds_type) + of_device_get_match_data(&client->dev); + chip = &chips[ds1307->type]; + } else if (id) { + chip = &chips[id->driver_data]; + ds1307->type = id->driver_data; + } else { + const struct acpi_device_id *acpi_id; + + acpi_id = acpi_match_device(ACPI_PTR(ds1307_acpi_ids), + ds1307->dev); + if (!acpi_id) + return -ENODEV; + chip = &chips[acpi_id->driver_data]; + ds1307->type = acpi_id->driver_data; + } + + want_irq = client->irq > 0 && chip->alarm; + + if (!pdata) + trickle_charger_setup = ds1307_trickle_init(ds1307, chip); + else if (pdata->trickle_charger_setup) + trickle_charger_setup = pdata->trickle_charger_setup; + + if (trickle_charger_setup && chip->trickle_charger_reg) { + trickle_charger_setup |= DS13XX_TRICKLE_CHARGER_MAGIC; + dev_dbg(ds1307->dev, + "writing trickle charger info 0x%x to 0x%x\n", + trickle_charger_setup, chip->trickle_charger_reg); + regmap_write(ds1307->regmap, chip->trickle_charger_reg, + trickle_charger_setup); + } + +#ifdef CONFIG_OF +/* + * For devices with no IRQ directly 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 "wakeup-source" boolean property. + * If the "wakeup-source" property is set, don't request an IRQ. + * This will guarantee the 'wakealarm' sysfs entry is available on the device, + * if supported by the RTC. + */ + if (chip->alarm && of_property_read_bool(client->dev.of_node, + "wakeup-source")) + ds1307_can_wakeup_device = true; +#endif + + switch (ds1307->type) { + case ds_1337: + case ds_1339: + case ds_1341: + case ds_3231: + /* get registers that the "rtc" read below won't read... */ + err = regmap_bulk_read(ds1307->regmap, DS1337_REG_CONTROL, + regs, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (regs[0] & DS1337_BIT_nEOSC) + regs[0] &= ~DS1337_BIT_nEOSC; + + /* + * Using IRQ or defined as wakeup-source? + * Disable the square wave and both alarms. + * For some variants, be sure alarms can trigger when we're + * running on Vbackup (BBSQI/BBSQW) + */ + if (want_irq || ds1307_can_wakeup_device) { + regs[0] |= DS1337_BIT_INTCN | chip->bbsqi_bit; + regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE); + } + + regmap_write(ds1307->regmap, DS1337_REG_CONTROL, + regs[0]); + + /* oscillator fault? clear flag, and warn */ + if (regs[1] & DS1337_BIT_OSF) { + regmap_write(ds1307->regmap, DS1337_REG_STATUS, + regs[1] & ~DS1337_BIT_OSF); + dev_warn(ds1307->dev, "SET TIME!\n"); + } + break; + + case rx_8025: + err = regmap_bulk_read(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, regs, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* oscillator off? turn it on, so clock can tick. */ + if (!(regs[1] & RX8025_BIT_XST)) { + regs[1] |= RX8025_BIT_XST; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + regs[1]); + dev_warn(ds1307->dev, + "oscillator stop detected - SET TIME!\n"); + } + + if (regs[1] & RX8025_BIT_PON) { + regs[1] &= ~RX8025_BIT_PON; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + regs[1]); + dev_warn(ds1307->dev, "power-on detected\n"); + } + + if (regs[1] & RX8025_BIT_VDET) { + regs[1] &= ~RX8025_BIT_VDET; + regmap_write(ds1307->regmap, + RX8025_REG_CTRL2 << 4 | 0x08, + regs[1]); + dev_warn(ds1307->dev, "voltage drop detected\n"); + } + + /* make sure we are running in 24hour mode */ + if (!(regs[0] & RX8025_BIT_2412)) { + u8 hour; + + /* switch to 24 hour mode */ + regmap_write(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, + regs[0] | RX8025_BIT_2412); + + err = regmap_bulk_read(ds1307->regmap, + RX8025_REG_CTRL1 << 4 | 0x08, + regs, 2); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* correct hour */ + hour = bcd2bin(regs[DS1307_REG_HOUR]); + if (hour == 12) + hour = 0; + if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + hour += 12; + + regmap_write(ds1307->regmap, + DS1307_REG_HOUR << 4 | 0x08, hour); + } + break; + default: + break; + } + +read_rtc: + /* read RTC registers */ + err = regmap_bulk_read(ds1307->regmap, chip->offset, regs, + sizeof(regs)); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + 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 = regs[DS1307_REG_SECS]; + switch (ds1307->type) { + case ds_1307: + case m41t0: + case m41t00: + case m41t11: + /* clock halted? turn it on, so clock can tick. */ + if (tmp & DS1307_BIT_CH) { + regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); + dev_warn(ds1307->dev, "SET TIME!\n"); + goto read_rtc; + } + break; + case ds_1308: + case ds_1338: + /* clock halted? turn it on, so clock can tick. */ + if (tmp & DS1307_BIT_CH) + regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); + + /* oscillator fault? clear flag, and warn */ + if (regs[DS1307_REG_CONTROL] & DS1338_BIT_OSF) { + regmap_write(ds1307->regmap, DS1307_REG_CONTROL, + regs[DS1307_REG_CONTROL] & + ~DS1338_BIT_OSF); + dev_warn(ds1307->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) + regmap_write(ds1307->regmap, DS1307_REG_SECS, 0); + + err = regmap_read(ds1307->regmap, DS1340_REG_FLAG, &tmp); + if (err) { + dev_dbg(ds1307->dev, "read error %d\n", err); + goto exit; + } + + /* oscillator fault? clear flag, and warn */ + if (tmp & DS1340_BIT_OSF) { + regmap_write(ds1307->regmap, DS1340_REG_FLAG, 0); + dev_warn(ds1307->dev, "SET TIME!\n"); + } + break; + case mcp794xx: + /* make sure that the backup battery is enabled */ + if (!(regs[DS1307_REG_WDAY] & MCP794XX_BIT_VBATEN)) { + regmap_write(ds1307->regmap, DS1307_REG_WDAY, + regs[DS1307_REG_WDAY] | + MCP794XX_BIT_VBATEN); + } + + /* clock halted? turn it on, so clock can tick. */ + if (!(tmp & MCP794XX_BIT_ST)) { + regmap_write(ds1307->regmap, DS1307_REG_SECS, + MCP794XX_BIT_ST); + dev_warn(ds1307->dev, "SET TIME!\n"); + goto read_rtc; + } + + break; + default: + break; + } + + tmp = regs[DS1307_REG_HOUR]; + switch (ds1307->type) { + case ds_1340: + case m41t0: + case m41t00: + case m41t11: + /* + * 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 (regs[DS1307_REG_HOUR] & DS1307_BIT_PM) + tmp += 12; + regmap_write(ds1307->regmap, chip->offset + DS1307_REG_HOUR, + bin2bcd(tmp)); + } + + if (want_irq || ds1307_can_wakeup_device) { + device_set_wakeup_capable(ds1307->dev, true); + set_bit(HAS_ALARM, &ds1307->flags); + } + + ds1307->rtc = devm_rtc_allocate_device(ds1307->dev); + if (IS_ERR(ds1307->rtc)) + return PTR_ERR(ds1307->rtc); + + if (ds1307_can_wakeup_device && !want_irq) { + dev_info(ds1307->dev, + "'wakeup-source' is set, request for an IRQ is disabled!\n"); + /* We cannot support UIE mode if we do not have an IRQ line */ + ds1307->rtc->uie_unsupported = 1; + } + + if (want_irq) { + err = devm_request_threaded_irq(ds1307->dev, client->irq, NULL, + chip->irq_handler ?: ds1307_irq, + IRQF_SHARED | IRQF_ONESHOT, + ds1307->name, ds1307); + if (err) { + client->irq = 0; + device_set_wakeup_capable(ds1307->dev, false); + clear_bit(HAS_ALARM, &ds1307->flags); + dev_err(ds1307->dev, "unable to request IRQ!\n"); + } else { + dev_dbg(ds1307->dev, "got IRQ %d\n", client->irq); + } + } + + ds1307->rtc->ops = chip->rtc_ops ?: &ds13xx_rtc_ops; + err = rtc_register_device(ds1307->rtc); + if (err) + return err; + + if (chip->nvram_size) { + struct nvmem_config nvmem_cfg = { + .name = "ds1307_nvram", + .word_size = 1, + .stride = 1, + .size = chip->nvram_size, + .reg_read = ds1307_nvram_read, + .reg_write = ds1307_nvram_write, + .priv = ds1307, + }; + + ds1307->rtc->nvram_old_abi = true; + rtc_nvmem_register(ds1307->rtc, &nvmem_cfg); + } + + ds1307_hwmon_register(ds1307); + ds1307_clks_register(ds1307); + + return 0; + +exit: + return err; +} + +static struct i2c_driver ds1307_driver = { + .driver = { + .name = "rtc-ds1307", + .of_match_table = of_match_ptr(ds1307_of_match), + .acpi_match_table = ACPI_PTR(ds1307_acpi_ids), + }, + .probe = ds1307_probe, + .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..5208da4cf --- /dev/null +++ b/drivers/rtc/rtc-ds1343.c @@ -0,0 +1,621 @@ +/* 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/pm_wakeirq.h> +#include <linux/slab.h> + +#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 int ds1343_nvram_write(void *priv, unsigned int off, void *val, + size_t bytes) +{ + struct ds1343_priv *ds1343 = priv; + + return regmap_bulk_write(ds1343->map, DS1343_NVRAM + off, val, bytes); +} + +static int ds1343_nvram_read(void *priv, unsigned int off, void *val, + size_t bytes) +{ + struct ds1343_priv *ds1343 = priv; + + return regmap_bulk_read(ds1343->map, DS1343_NVRAM + off, val, bytes); +} + +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) +{ + 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) + return 0; + + device_remove_file(dev, &dev_attr_glitch_filter); + + return err; +} + +static void ds1343_sysfs_unregister(struct device *dev) +{ + device_remove_file(dev, &dev_attr_glitch_filter); + device_remove_file(dev, &dev_attr_trickle_charger); +} + +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 0; +} + +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; + +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 = { .reg_bits = 8, .val_bits = 8, + .write_flag_mask = 0x80, }; + unsigned int data; + int res; + struct nvmem_config nvmem_cfg = { + .name = "ds1343-", + .word_size = 1, + .stride = 1, + .size = DS1343_NVRAM_LEN, + .reg_read = ds1343_nvram_read, + .reg_write = ds1343_nvram_write, + }; + + 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_allocate_device(&spi->dev); + if (IS_ERR(priv->rtc)) + return PTR_ERR(priv->rtc); + + priv->rtc->nvram_old_abi = true; + priv->rtc->ops = &ds1343_rtc_ops; + + res = rtc_register_device(priv->rtc); + if (res) + return res; + + nvmem_cfg.priv = priv; + rtc_nvmem_register(priv->rtc, &nvmem_cfg); + + priv->irq = spi->irq; + + if (priv->irq >= 0) { + res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + ds1343_thread, IRQF_ONESHOT, + "ds1343", priv); + if (res) { + priv->irq = -1; + dev_err(&spi->dev, + "unable to request irq for rtc ds1343\n"); + } else { + device_init_wakeup(&spi->dev, true); + dev_pm_set_wake_irq(&spi->dev, spi->irq); + } + } + + 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); + + dev_pm_clear_wake_irq(&spi->dev); + device_init_wakeup(&spi->dev, false); + 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", + .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"); diff --git a/drivers/rtc/rtc-ds1347.c b/drivers/rtc/rtc-ds1347.c new file mode 100644 index 000000000..938512c67 --- /dev/null +++ b/drivers/rtc/rtc-ds1347.c @@ -0,0 +1,175 @@ +/* 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> +#include <linux/regmap.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 const struct regmap_range ds1347_ranges[] = { + { + .range_min = DS1347_SECONDS_REG, + .range_max = DS1347_STATUS_REG, + }, +}; + +static const struct regmap_access_table ds1347_access_table = { + .yes_ranges = ds1347_ranges, + .n_yes_ranges = ARRAY_SIZE(ds1347_ranges), +}; + +static int ds1347_read_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct regmap *map; + int err; + unsigned char buf[8]; + + map = spi_get_drvdata(spi); + + err = regmap_bulk_read(map, DS1347_CLOCK_BURST, 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 0; +} + +static int ds1347_set_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct regmap *map; + unsigned char buf[8]; + + map = spi_get_drvdata(spi); + + buf[0] = bin2bcd(dt->tm_sec); + buf[1] = bin2bcd(dt->tm_min); + buf[2] = (bin2bcd(dt->tm_hour) & 0x3F); + buf[3] = bin2bcd(dt->tm_mday); + buf[4] = bin2bcd(dt->tm_mon + 1); + buf[5] = 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[6] = bin2bcd(dt->tm_year); + buf[7] = bin2bcd(0x00); + + /* write the rtc settings */ + return regmap_bulk_write(map, DS1347_CLOCK_BURST, buf, 8); +} + +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; + struct regmap_config config; + struct regmap *map; + unsigned int data; + int res; + + memset(&config, 0, sizeof(config)); + config.reg_bits = 8; + config.val_bits = 8; + config.read_flag_mask = 0x80; + config.max_register = 0x3F; + config.wr_table = &ds1347_access_table; + + /* 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); + + map = devm_regmap_init_spi(spi, &config); + + if (IS_ERR(map)) { + dev_err(&spi->dev, "ds1347 regmap init spi failed\n"); + return PTR_ERR(map); + } + + spi_set_drvdata(spi, map); + + /* RTC Settings */ + res = regmap_read(map, DS1347_SECONDS_REG, &data); + if (res) + return res; + + /* Disable the write protect of rtc */ + regmap_read(map, DS1347_CONTROL_REG, &data); + data = data & ~(1<<7); + regmap_write(map, DS1347_CONTROL_REG, data); + + /* Enable the oscillator , disable the oscillator stop flag, + and glitch filter to reduce current consumption */ + regmap_read(map, DS1347_STATUS_REG, &data); + data = data & 0x1B; + regmap_write(map, DS1347_STATUS_REG, data); + + /* display the settings */ + regmap_read(map, DS1347_CONTROL_REG, &data); + dev_info(&spi->dev, "DS1347 RTC CTRL Reg = 0x%02x\n", data); + + regmap_read(map, 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); + + return 0; +} + +static struct spi_driver ds1347_driver = { + .driver = { + .name = "ds1347", + }, + .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..77a106e90 --- /dev/null +++ b/drivers/rtc/rtc-ds1374.c @@ -0,0 +1,728 @@ +/* + * 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 (WARN_ON(nbytes > 4)) + 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; + + /* the hardware's tick rate is 4096 Hz, so + * the counter value needs to be scaled accordingly + */ + new_margin <<= 12; + 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: + /* when returning ... inverse is true */ + return put_user((wdt_margin >> 12), (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(); + return 0; + } + + if (options & WDIOS_ENABLECARD) { + pr_info("enable watchdog\n"); + ds1374_wdt_settimeout(wdt_margin); + ds1374_wdt_ping(); + return 0; + } + 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->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(ds1374->rtc)) + return PTR_ERR(ds1374->rtc); + + 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->ops = &ds1374_rtc_ops; + + ret = rtc_register_device(ds1374->rtc); + if (ret) + return ret; + +#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 + misc_deregister(&ds1374_miscdev); + 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", + .of_match_table = of_match_ptr(ds1374_of_match), + .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..3b095401f --- /dev/null +++ b/drivers/rtc/rtc-ds1390.c @@ -0,0 +1,238 @@ +/* + * 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> +#include <linux/of.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 + +#define DS1390_TRICKLE_CHARGER_ENABLE 0xA0 +#define DS1390_TRICKLE_CHARGER_250_OHM 0x01 +#define DS1390_TRICKLE_CHARGER_2K_OHM 0x02 +#define DS1390_TRICKLE_CHARGER_4K_OHM 0x03 +#define DS1390_TRICKLE_CHARGER_NO_DIODE 0x04 +#define DS1390_TRICKLE_CHARGER_DIODE 0x08 + +struct ds1390 { + struct rtc_device *rtc; + u8 txrx_buf[9]; /* cmd + 8 registers */ +}; + +static void ds1390_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 write */ + buf[0] = address | 0x80; + buf[1] = data; + + spi_write(spi, buf, 2); +} + +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[0]; + + return 0; +} + +static void ds1390_trickle_of_init(struct spi_device *spi) +{ + u32 ohms = 0; + u8 value; + + if (of_property_read_u32(spi->dev.of_node, "trickle-resistor-ohms", + &ohms)) + goto out; + + /* Enable charger */ + value = DS1390_TRICKLE_CHARGER_ENABLE; + if (of_property_read_bool(spi->dev.of_node, "trickle-diode-disable")) + value |= DS1390_TRICKLE_CHARGER_NO_DIODE; + else + value |= DS1390_TRICKLE_CHARGER_DIODE; + + /* Resistor select */ + switch (ohms) { + case 250: + value |= DS1390_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + value |= DS1390_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + value |= DS1390_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(&spi->dev, + "Unsupported ohm value %02ux in dt\n", ohms); + return; + } + + ds1390_set_reg(&spi->dev, DS1390_REG_TRICKLE, value); + +out: + return; +} + +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 0; +} + +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; + } + + if (spi->dev.of_node) + ds1390_trickle_of_init(spi); + + 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 const struct of_device_id ds1390_of_match[] = { + { .compatible = "dallas,ds1390" }, + {} +}; +MODULE_DEVICE_TABLE(of, ds1390_of_match); + +static struct spi_driver ds1390_driver = { + .driver = { + .name = "rtc-ds1390", + .of_match_table = of_match_ptr(ds1390_of_match), + }, + .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..b8b6e51c0 --- /dev/null +++ b/drivers/rtc/rtc-ds1511.c @@ -0,0 +1,513 @@ +/* + * 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> + +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 0x100 + +#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(rtc_read(RTC_CMD) | 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--; + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 int ds1511_nvram_read(void *priv, unsigned int pos, void *buf, + size_t size) +{ + int i; + + rtc_write(pos, DS1511_RAMADDR_LSB); + for (i = 0; i < size; i++) + *(char *)buf++ = rtc_read(DS1511_RAMDATA); + + return 0; +} + +static int ds1511_nvram_write(void *priv, unsigned int pos, void *buf, + size_t size) +{ + int i; + + rtc_write(pos, DS1511_RAMADDR_LSB); + for (i = 0; i < size; i++) + rtc_write(*(char *)buf++, DS1511_RAMDATA); + + return 0; +} + +static int ds1511_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct rtc_plat_data *pdata; + int ret = 0; + struct nvmem_config ds1511_nvmem_cfg = { + .name = "ds1511_nvram", + .word_size = 1, + .stride = 1, + .size = DS1511_RAM_MAX, + .reg_read = ds1511_nvram_read, + .reg_write = ds1511_nvram_write, + .priv = &pdev->dev, + }; + + 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(DS1511_BME, 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_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &ds1511_rtc_ops; + + pdata->rtc->nvram_old_abi = true; + + ret = rtc_register_device(pdata->rtc); + if (ret) + return ret; + + rtc_nvmem_register(pdata->rtc, &ds1511_nvmem_cfg); + + /* + * 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; + } + } + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:ds1511"); + +static struct platform_driver ds1511_rtc_driver = { + .probe = ds1511_rtc_probe, + .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"); diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c new file mode 100644 index 000000000..34af7a802 --- /dev/null +++ b/drivers/rtc/rtc-ds1553.c @@ -0,0 +1,338 @@ +/* + * 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 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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; + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 int ds1553_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct platform_device *pdev = priv; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + *buf++ = readb(ioaddr + pos++); + return 0; +} + +static int ds1553_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct platform_device *pdev = priv; + struct rtc_plat_data *pdata = platform_get_drvdata(pdev); + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + writeb(*buf++, ioaddr + pos++); + return 0; +} + +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; + struct nvmem_config nvmem_cfg = { + .name = "ds1553_nvram", + .word_size = 1, + .stride = 1, + .size = RTC_OFFSET, + .reg_read = ds1553_nvram_read, + .reg_write = ds1553_nvram_write, + .priv = pdev, + }; + + 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_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &ds1553_rtc_ops; + pdata->rtc->nvram_old_abi = true; + + ret = rtc_register_device(pdata->rtc); + if (ret) + return ret; + + 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; + } + } + + if (rtc_nvmem_register(pdata->rtc, &nvmem_cfg)) + dev_err(&pdev->dev, "unable to register nvmem\n"); + + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:rtc-ds1553"); + +static struct platform_driver ds1553_rtc_driver = { + .probe = ds1553_rtc_probe, + .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"); diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c new file mode 100644 index 000000000..b1ebca099 --- /dev/null +++ b/drivers/rtc/rtc-ds1672.c @@ -0,0 +1,219 @@ +/* + * 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> + +/* 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 = ((unsigned long)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; + + 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 const struct i2c_device_id ds1672_id[] = { + { "ds1672", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds1672_id); + +static const struct of_device_id ds1672_of_match[] = { + { .compatible = "dallas,ds1672" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds1672_of_match); + +static struct i2c_driver ds1672_driver = { + .driver = { + .name = "rtc-ds1672", + .of_match_table = of_match_ptr(ds1672_of_match), + }, + .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"); diff --git a/drivers/rtc/rtc-ds1685.c b/drivers/rtc/rtc-ds1685.c new file mode 100644 index 000000000..6f39f683a --- /dev/null +++ b/drivers/rtc/rtc-ds1685.c @@ -0,0 +1,1641 @@ +/* + * 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 + + +/* ----------------------------------------------------------------------- */ +/* 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); +} + +/** + * s1685_rtc_check_mday - check validity of the day of month. + * @rtc: pointer to the ds1685 rtc structure. + * @mday: day of month. + * + * Returns -EDOM if the day of month is not within 1..31 range. + */ +static inline int +ds1685_rtc_check_mday(struct ds1685_priv *rtc, u8 mday) +{ + if (rtc->bcd_mode) { + if (mday < 0x01 || mday > 0x31 || (mday & 0x0f) > 0x09) + return -EDOM; + } else { + if (mday < 1 || mday > 31) + return -EDOM; + } + return 0; +} + +/** + * 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 ds1685_priv *rtc = dev_get_drvdata(dev); + 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 0; +} + +/** + * 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 ds1685_priv *rtc = dev_get_drvdata(dev); + 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 ds1685_priv *rtc = dev_get_drvdata(dev); + u8 seconds, minutes, hours, mday, ctrlb, ctrlc; + int ret; + + /* 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 the month date for validity. */ + ret = ds1685_rtc_check_mday(rtc, mday); + if (ret) + return ret; + + /* + * 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 and only assign when we care. + */ + if (likely(seconds < 0xc0)) + alrm->time.tm_sec = ds1685_rtc_bcd2bin(rtc, seconds, + RTC_SECS_BCD_MASK, + RTC_SECS_BIN_MASK); + + if (likely(minutes < 0xc0)) + alrm->time.tm_min = ds1685_rtc_bcd2bin(rtc, minutes, + RTC_MINS_BCD_MASK, + RTC_MINS_BIN_MASK); + + if (likely(hours < 0xc0)) + 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->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 ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ctrlb, seconds, minutes, hours, mday; + int ret; + + /* 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. */ + ret = ds1685_rtc_check_mday(rtc, mday); + if (ret) + return ret; + + /* + * 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: %8phC\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: %8phC\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, + 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); +#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 ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ctrld; + + ctrld = rtc->read(rtc, RTC_CTRL_D); + + return sprintf(buf, "%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 ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ctrl4a; + + ds1685_rtc_switch_to_bank1(rtc); + ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A); + ds1685_rtc_switch_to_bank0(rtc); + + return sprintf(buf, "%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 ds1685_priv *rtc = dev_get_drvdata(dev); + u8 ssn[8]; + + ds1685_rtc_switch_to_bank1(rtc); + ds1685_rtc_get_ssn(rtc, ssn); + ds1685_rtc_switch_to_bank0(rtc); + + return sprintf(buf, "%8phC\n", ssn); +} +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, +}; + +/** + * 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; + + 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); + + 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)); + + rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc_dev)) + return PTR_ERR(rtc_dev); + + rtc_dev->ops = &ds1685_rtc_ops; + + /* Century bit is useless because leap year fails in 1900 and 2100 */ + rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000; + rtc_dev->range_max = RTC_TIMESTAMP_END_2099; + + /* 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; + + /* + * 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); + +#ifdef CONFIG_SYSFS + ret = ds1685_rtc_sysfs_register(&pdev->dev); + if (ret) + return ret; +#endif + + return rtc_register_device(rtc_dev); +} + +/** + * 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 + + /* 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, +}; +module_platform_driver(ds1685_rtc_driver); +/* ----------------------------------------------------------------------- */ + + +/* ----------------------------------------------------------------------- */ +/* 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"); + while(1); + 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. */ + while(1); + 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_ALIAS("platform:rtc-ds1685"); diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c new file mode 100644 index 000000000..5a4c2c5e8 --- /dev/null +++ b/drivers/rtc/rtc-ds1742.c @@ -0,0 +1,227 @@ +/* + * 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 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; + unsigned long last_jiffies; +}; + +static int ds1742_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 0; +} + +static const struct rtc_class_ops ds1742_rtc_ops = { + .read_time = ds1742_rtc_read_time, + .set_time = ds1742_rtc_set_time, +}; + +static int ds1742_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr_nvram; + u8 *buf = val; + + for (; bytes; bytes--) + *buf++ = readb(ioaddr + pos++); + return 0; +} + +static int ds1742_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr_nvram; + u8 *buf = val; + + for (; bytes; bytes--) + writeb(*buf++, ioaddr + pos++); + return 0; +} + +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; + struct nvmem_config nvmem_cfg = { + .name = "ds1742_nvram", + .reg_read = ds1742_nvram_read, + .reg_write = ds1742_nvram_write, + }; + + + 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->ioaddr_rtc = ioaddr + resource_size(res) - RTC_SIZE; + + nvmem_cfg.size = resource_size(res) - RTC_SIZE; + nvmem_cfg.priv = pdata; + + /* 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_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &ds1742_rtc_ops; + rtc->nvram_old_abi = true; + + ret = rtc_register_device(rtc); + if (ret) + return ret; + + if (rtc_nvmem_register(rtc, &nvmem_cfg)) + dev_err(&pdev->dev, "Unable to register nvmem\n"); + + 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, + .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_ALIAS("platform:rtc-ds1742"); diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c new file mode 100644 index 000000000..b886b6a5c --- /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/platform_data/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; + const 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 const 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 0; +} + +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..7184e5145 --- /dev/null +++ b/drivers/rtc/rtc-ds3232.c @@ -0,0 +1,735 @@ +/* + * RTC client/driver for the Maxim/Dallas DS3232/DS3234 Real-Time Clock + * + * Copyright (C) 2009-2011 Freescale Semiconductor. + * Author: Jack Lan <jack.lan@freescale.com> + * 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 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/interrupt.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/hwmon.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 + +#define DS3232_REG_TEMPERATURE 0x11 + +struct ds3232 { + struct device *dev; + struct regmap *regmap; + int irq; + struct rtc_device *rtc; + + bool suspended; +}; + +static int ds3232_check_rtc_status(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int ret = 0; + int control, stat; + + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + return ret; + + if (stat & DS3232_REG_SR_OSF) + dev_warn(dev, + "oscillator discontinuity flagged, " + "time unreliable\n"); + + stat &= ~(DS3232_REG_SR_OSF | DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + + ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat); + if (ret) + return ret; + + /* If the alarm is pending, clear it before requesting + * the interrupt, so an interrupt event isn't reported + * before everything is initialized. + */ + + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + return ret; + + control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); + control |= DS3232_REG_CR_INTCN; + + return regmap_write(ds3232->regmap, DS3232_REG_CR, control); +} + +static int ds3232_read_time(struct device *dev, struct rtc_time *time) +{ + struct ds3232 *ds3232 = dev_get_drvdata(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 = regmap_bulk_read(ds3232->regmap, DS3232_REG_SECONDS, buf, 7); + if (ret) + return ret; + + 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 0; +} + +static int ds3232_set_time(struct device *dev, struct rtc_time *time) +{ + struct ds3232 *ds3232 = dev_get_drvdata(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 regmap_bulk_write(ds3232->regmap, DS3232_REG_SECONDS, buf, 7); +} + +/* + * 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 ds3232 *ds3232 = dev_get_drvdata(dev); + int control, stat; + int ret; + u8 buf[4]; + + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + goto out; + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + goto out; + ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_ALARM1, buf, 4); + if (ret) + 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->enabled = !!(control & DS3232_REG_CR_A1IE); + alarm->pending = !!(stat & DS3232_REG_SR_A1F); + + ret = 0; +out: + 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 ds3232 *ds3232 = dev_get_drvdata(dev); + int control, stat; + int ret; + u8 buf[4]; + + if (ds3232->irq <= 0) + return -EINVAL; + + 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 = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + goto out; + control &= ~(DS3232_REG_CR_A1IE | DS3232_REG_CR_A2IE); + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control); + if (ret) + goto out; + + /* clear any pending alarm flag */ + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + goto out; + stat &= ~(DS3232_REG_SR_A1F | DS3232_REG_SR_A2F); + ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat); + if (ret) + goto out; + + ret = regmap_bulk_write(ds3232->regmap, DS3232_REG_ALARM1, buf, 4); + if (ret) + goto out; + + if (alarm->enabled) { + control |= DS3232_REG_CR_A1IE; + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control); + } +out: + return ret; +} + +static int ds3232_update_alarm(struct device *dev, unsigned int enabled) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + int control; + int ret; + + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) + return ret; + + if (enabled) + /* enable alarm1 interrupt */ + control |= DS3232_REG_CR_A1IE; + else + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control); + + return ret; +} + +/* + * Temperature sensor support for ds3232/ds3234 devices. + * A user-initiated temperature conversion is not started by this function, + * so the temperature is updated once every 64 seconds. + */ +static int ds3232_hwmon_read_temp(struct device *dev, long int *mC) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + u8 temp_buf[2]; + s16 temp; + int ret; + + ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_TEMPERATURE, temp_buf, + sizeof(temp_buf)); + if (ret < 0) + return ret; + + /* + * Temperature is represented as a 10-bit code with a resolution of + * 0.25 degree celsius and encoded in two's complement format. + */ + temp = (temp_buf[0] << 8) | temp_buf[1]; + temp >>= 6; + *mC = temp * 250; + + return 0; +} + +static umode_t ds3232_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static int ds3232_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int err; + + switch (attr) { + case hwmon_temp_input: + err = ds3232_hwmon_read_temp(dev, temp); + break; + default: + err = -EOPNOTSUPP; + break; + } + + return err; +} + +static u32 ds3232_hwmon_chip_config[] = { + HWMON_C_REGISTER_TZ, + 0 +}; + +static const struct hwmon_channel_info ds3232_hwmon_chip = { + .type = hwmon_chip, + .config = ds3232_hwmon_chip_config, +}; + +static u32 ds3232_hwmon_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info ds3232_hwmon_temp = { + .type = hwmon_temp, + .config = ds3232_hwmon_temp_config, +}; + +static const struct hwmon_channel_info *ds3232_hwmon_info[] = { + &ds3232_hwmon_chip, + &ds3232_hwmon_temp, + NULL +}; + +static const struct hwmon_ops ds3232_hwmon_hwmon_ops = { + .is_visible = ds3232_hwmon_is_visible, + .read = ds3232_hwmon_read, +}; + +static const struct hwmon_chip_info ds3232_hwmon_chip_info = { + .ops = &ds3232_hwmon_hwmon_ops, + .info = ds3232_hwmon_info, +}; + +static void ds3232_hwmon_register(struct device *dev, const char *name) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct device *hwmon_dev; + + if (!IS_ENABLED(CONFIG_RTC_DRV_DS3232_HWMON)) + return; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, name, ds3232, + &ds3232_hwmon_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) { + dev_err(dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } +} + +static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + + if (ds3232->irq <= 0) + return -EINVAL; + + return ds3232_update_alarm(dev, enabled); +} + +static irqreturn_t ds3232_irq(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct ds3232 *ds3232 = dev_get_drvdata(dev); + struct mutex *lock = &ds3232->rtc->ops_lock; + int ret; + int stat, control; + + mutex_lock(lock); + + ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat); + if (ret) + goto unlock; + + if (stat & DS3232_REG_SR_A1F) { + ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control); + if (ret) { + dev_warn(ds3232->dev, + "Read Control Register error %d\n", ret); + } else { + /* disable alarm1 interrupt */ + control &= ~(DS3232_REG_CR_A1IE); + ret = regmap_write(ds3232->regmap, DS3232_REG_CR, + control); + if (ret) { + dev_warn(ds3232->dev, + "Write Control Register error %d\n", + ret); + goto unlock; + } + + /* clear the alarm pend flag */ + stat &= ~DS3232_REG_SR_A1F; + ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat); + if (ret) { + dev_warn(ds3232->dev, + "Write Status Register error %d\n", + ret); + goto unlock; + } + + rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF); + } + } + +unlock: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +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 device *dev, struct regmap *regmap, int irq, + const char *name) +{ + struct ds3232 *ds3232; + int ret; + + ds3232 = devm_kzalloc(dev, sizeof(*ds3232), GFP_KERNEL); + if (!ds3232) + return -ENOMEM; + + ds3232->regmap = regmap; + ds3232->irq = irq; + ds3232->dev = dev; + dev_set_drvdata(dev, ds3232); + + ret = ds3232_check_rtc_status(dev); + if (ret) + return ret; + + if (ds3232->irq > 0) + device_init_wakeup(dev, 1); + + ds3232_hwmon_register(dev, name); + + ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops, + THIS_MODULE); + if (IS_ERR(ds3232->rtc)) + return PTR_ERR(ds3232->rtc); + + if (ds3232->irq > 0) { + ret = devm_request_threaded_irq(dev, ds3232->irq, NULL, + ds3232_irq, + IRQF_SHARED | IRQF_ONESHOT, + name, dev); + if (ret) { + device_set_wakeup_capable(dev, 0); + ds3232->irq = 0; + dev_err(dev, "unable to request IRQ\n"); + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ds3232_suspend(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) { + if (enable_irq_wake(ds3232->irq)) + dev_warn_once(dev, "Cannot set wakeup source\n"); + } + + return 0; +} + +static int ds3232_resume(struct device *dev) +{ + struct ds3232 *ds3232 = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(ds3232->irq); + + return 0; +} +#endif + +static const struct dev_pm_ops ds3232_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ds3232_suspend, ds3232_resume) +}; + +#if IS_ENABLED(CONFIG_I2C) + +static int ds3232_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x13, + }; + + regmap = devm_regmap_init_i2c(client, &config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return ds3232_probe(&client->dev, regmap, client->irq, client->name); +} + +static const struct i2c_device_id ds3232_id[] = { + { "ds3232", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ds3232_id); + +static const struct of_device_id ds3232_of_match[] = { + { .compatible = "dallas,ds3232" }, + { } +}; +MODULE_DEVICE_TABLE(of, ds3232_of_match); + +static struct i2c_driver ds3232_driver = { + .driver = { + .name = "rtc-ds3232", + .of_match_table = of_match_ptr(ds3232_of_match), + .pm = &ds3232_pm_ops, + }, + .probe = ds3232_i2c_probe, + .id_table = ds3232_id, +}; + +static int ds3232_register_driver(void) +{ + return i2c_add_driver(&ds3232_driver); +} + +static void ds3232_unregister_driver(void) +{ + i2c_del_driver(&ds3232_driver); +} + +#else + +static int ds3232_register_driver(void) +{ + return 0; +} + +static void ds3232_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int ds3234_probe(struct spi_device *spi) +{ + int res; + unsigned int tmp; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x13, + .write_flag_mask = 0x80, + }; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + res = regmap_read(regmap, DS3232_REG_SECONDS, &tmp); + if (res) + 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 + */ + res = regmap_read(regmap, DS3232_REG_CR, &tmp); + if (res) + return res; + res = regmap_write(regmap, DS3232_REG_CR, tmp & 0x1c); + if (res) + return res; + + res = regmap_read(regmap, DS3232_REG_SR, &tmp); + if (res) + return res; + res = regmap_write(regmap, DS3232_REG_SR, tmp & 0x88); + if (res) + return res; + + /* Print our settings */ + res = regmap_read(regmap, DS3232_REG_CR, &tmp); + if (res) + return res; + dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp); + + res = regmap_read(regmap, DS3232_REG_SR, &tmp); + if (res) + return res; + dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp); + + return ds3232_probe(&spi->dev, regmap, spi->irq, "ds3234"); +} + +static struct spi_driver ds3234_driver = { + .driver = { + .name = "ds3234", + }, + .probe = ds3234_probe, +}; + +static int ds3234_register_driver(void) +{ + return spi_register_driver(&ds3234_driver); +} + +static void ds3234_unregister_driver(void) +{ + spi_unregister_driver(&ds3234_driver); +} + +#else + +static int ds3234_register_driver(void) +{ + return 0; +} + +static void ds3234_unregister_driver(void) +{ +} + +#endif + +static int __init ds323x_init(void) +{ + int ret; + + ret = ds3232_register_driver(); + if (ret) { + pr_err("Failed to register ds3232 driver: %d\n", ret); + return ret; + } + + ret = ds3234_register_driver(); + if (ret) { + pr_err("Failed to register ds3234 driver: %d\n", ret); + ds3232_unregister_driver(); + } + + return ret; +} +module_init(ds323x_init) + +static void __exit ds323x_exit(void) +{ + ds3234_unregister_driver(); + ds3232_unregister_driver(); +} +module_exit(ds323x_exit) + +MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>"); +MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>"); +MODULE_DESCRIPTION("Maxim/Dallas DS3232/DS3234 RTC Driver"); +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..6c037dc4e --- /dev/null +++ b/drivers/rtc/rtc-efi-platform.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 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..3454e7814 --- /dev/null +++ b/drivers/rtc/rtc-efi.c @@ -0,0 +1,292 @@ +/* + * rtc-efi: RTC Class Driver for EFI-based systems + * + * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. + * + * Author: dann frazier <dannf@dannf.org> + * 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) + +/* + * 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 + */ +static int +compute_wday(efi_time_t *eft, int yday) +{ + int ndays = eft->year * (365 % 7) + + (eft->year - 1) / 4 + - (eft->year - 1) / 100 + + (eft->year - 1) / 400 + + yday; + + /* + * 1/1/0000 may or may not have been a Sunday (if it ever existed at + * all) but assuming it was makes this calculation work correctly. + */ + return ndays % 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; + + if (eft->year < 1900 || eft->year > 9999) + return false; + wtime->tm_year = eft->year - 1900; + + /* day in the year [1-365]*/ + wtime->tm_yday = compute_yday(eft); + + /* day of the week [0-6], Sunday=0 */ + wtime->tm_wday = compute_wday(eft, wtime->tm_yday); + + 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 0; +} + +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 int efi_procfs(struct device *dev, struct seq_file *seq) +{ + efi_time_t eft, alm; + efi_time_cap_t cap; + efi_bool_t enabled, pending; + + memset(&eft, 0, sizeof(eft)); + memset(&alm, 0, sizeof(alm)); + memset(&cap, 0, sizeof(cap)); + + efi.get_time(&eft, &cap); + efi.get_wakeup_time(&enabled, &pending, &alm); + + seq_printf(seq, + "Time\t\t: %u:%u:%u.%09u\n" + "Date\t\t: %u-%u-%u\n" + "Daylight\t: %u\n", + eft.hour, eft.minute, eft.second, eft.nanosecond, + eft.year, eft.month, eft.day, + eft.daylight); + + if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) + seq_puts(seq, "Timezone\t: unspecified\n"); + else + /* XXX fixme: convert to string? */ + seq_printf(seq, "Timezone\t: %u\n", eft.timezone); + + seq_printf(seq, + "Alarm Time\t: %u:%u:%u.%09u\n" + "Alarm Date\t: %u-%u-%u\n" + "Alarm Daylight\t: %u\n" + "Enabled\t\t: %s\n" + "Pending\t\t: %s\n", + alm.hour, alm.minute, alm.second, alm.nanosecond, + alm.year, alm.month, alm.day, + alm.daylight, + enabled == 1 ? "yes" : "no", + pending == 1 ? "yes" : "no"); + + if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE) + seq_puts(seq, "Timezone\t: unspecified\n"); + else + /* XXX fixme: convert to string? */ + seq_printf(seq, "Timezone\t: %u\n", alm.timezone); + + /* + * now prints the capabilities + */ + seq_printf(seq, + "Resolution\t: %u\n" + "Accuracy\t: %u\n" + "SetstoZero\t: %u\n", + cap.resolution, cap.accuracy, cap.sets_to_zero); + + return 0; +} + +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, + .proc = efi_procfs, +}; + +static int __init efi_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + efi_time_t eft; + efi_time_cap_t cap; + + /* First check if the RTC is usable */ + if (efi.get_time(&eft, &cap) != EFI_SUCCESS) + return -ENODEV; + + 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@dannf.org>"); +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..b0ef8cfe7 --- /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 const 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..694038208 --- /dev/null +++ b/drivers/rtc/rtc-ep93xx.c @@ -0,0 +1,185 @@ +/* + * 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 + +/* + * 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 = 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 = 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); + + 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_ALIAS("platform:ep93xx-rtc"); diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c new file mode 100644 index 000000000..e1137670d --- /dev/null +++ b/drivers/rtc/rtc-fm3130.c @@ -0,0 +1,535 @@ +/* + * 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); + + return 0; +} + + +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", + }, + .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-ftrtc010.c b/drivers/rtc/rtc-ftrtc010.c new file mode 100644 index 000000000..8f1dd88fa --- /dev/null +++ b/drivers/rtc/rtc-ftrtc010.c @@ -0,0 +1,219 @@ +/* + * Faraday Technology FTRTC010 driver + * + * Copyright (C) 2009 Janos Laube <janos.dev@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. + * + * Original code for older kernel 2.6.15 are from Stormlinksemi + * first update from Janos Laube for > 2.6.29 kernels + * + * checkpatch fixes and usage of rtc-lib code + * Hans Ulli Kroll <ulli.kroll@googlemail.com> + */ + +#include <linux/rtc.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/clk.h> + +#define DRV_NAME "rtc-ftrtc010" + +MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); +MODULE_DESCRIPTION("RTC driver for Gemini SoC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); + +struct ftrtc010_rtc { + struct rtc_device *rtc_dev; + void __iomem *rtc_base; + int rtc_irq; + struct clk *pclk; + struct clk *extclk; +}; + +enum ftrtc010_rtc_offsets { + FTRTC010_RTC_SECOND = 0x00, + FTRTC010_RTC_MINUTE = 0x04, + FTRTC010_RTC_HOUR = 0x08, + FTRTC010_RTC_DAYS = 0x0C, + FTRTC010_RTC_ALARM_SECOND = 0x10, + FTRTC010_RTC_ALARM_MINUTE = 0x14, + FTRTC010_RTC_ALARM_HOUR = 0x18, + FTRTC010_RTC_RECORD = 0x1C, + FTRTC010_RTC_CR = 0x20, +}; + +static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev) +{ + return IRQ_HANDLED; +} + +/* + * Looks like the RTC in the Gemini SoC is (totaly) broken + * We can't read/write directly the time from RTC registers. + * We must do some "offset" calculation to get the real time + * + * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does + * the same thing, without the rtc-lib.c calls. + */ + +static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); + + u32 days, hour, min, sec, offset; + timeu64_t time; + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD); + + time = offset + days * 86400 + hour * 3600 + min * 60 + sec; + + rtc_time64_to_tm(time, tm); + + return 0; +} + +static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); + u32 sec, min, hour, day, offset; + timeu64_t time; + + time = rtc_tm_to_time64(tm); + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + + offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); + + writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD); + writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR); + + return 0; +} + +static const struct rtc_class_ops ftrtc010_rtc_ops = { + .read_time = ftrtc010_rtc_read_time, + .set_time = ftrtc010_rtc_set_time, +}; + +static int ftrtc010_rtc_probe(struct platform_device *pdev) +{ + u32 days, hour, min, sec; + struct ftrtc010_rtc *rtc; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (unlikely(!rtc)) + return -ENOMEM; + platform_set_drvdata(pdev, rtc); + + rtc->pclk = devm_clk_get(dev, "PCLK"); + if (IS_ERR(rtc->pclk)) { + dev_err(dev, "could not get PCLK\n"); + } else { + ret = clk_prepare_enable(rtc->pclk); + if (ret) { + dev_err(dev, "failed to enable PCLK\n"); + return ret; + } + } + rtc->extclk = devm_clk_get(dev, "EXTCLK"); + if (IS_ERR(rtc->extclk)) { + dev_err(dev, "could not get EXTCLK\n"); + } else { + ret = clk_prepare_enable(rtc->extclk); + if (ret) { + dev_err(dev, "failed to enable EXTCLK\n"); + return ret; + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) + return -ENODEV; + + rtc->rtc_irq = res->start; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + rtc->rtc_base = devm_ioremap(dev, res->start, + resource_size(res)); + if (!rtc->rtc_base) + return -ENOMEM; + + rtc->rtc_dev = devm_rtc_allocate_device(dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + rtc->rtc_dev->ops = &ftrtc010_rtc_ops; + + sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); + min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); + hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); + days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); + + rtc->rtc_dev->range_min = (u64)days * 86400 + hour * 3600 + + min * 60 + sec; + rtc->rtc_dev->range_max = U32_MAX + rtc->rtc_dev->range_min; + + ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt, + IRQF_SHARED, pdev->name, dev); + if (unlikely(ret)) + return ret; + + return rtc_register_device(rtc->rtc_dev); +} + +static int ftrtc010_rtc_remove(struct platform_device *pdev) +{ + struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev); + + if (!IS_ERR(rtc->extclk)) + clk_disable_unprepare(rtc->extclk); + if (!IS_ERR(rtc->pclk)) + clk_disable_unprepare(rtc->pclk); + + return 0; +} + +static const struct of_device_id ftrtc010_rtc_dt_match[] = { + { .compatible = "cortina,gemini-rtc" }, + { .compatible = "faraday,ftrtc010" }, + { } +}; +MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match); + +static struct platform_driver ftrtc010_rtc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = ftrtc010_rtc_dt_match, + }, + .probe = ftrtc010_rtc_probe, + .remove = ftrtc010_rtc_remove, +}; + +module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe); diff --git a/drivers/rtc/rtc-generic.c b/drivers/rtc/rtc-generic.c new file mode 100644 index 000000000..1bf5d2347 --- /dev/null +++ b/drivers/rtc/rtc-generic.c @@ -0,0 +1,38 @@ +/* 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> + +static int __init generic_rtc_probe(struct platform_device *dev) +{ + struct rtc_device *rtc; + const struct rtc_class_ops *ops = dev_get_platdata(&dev->dev); + + rtc = devm_rtc_device_register(&dev->dev, "rtc-generic", + 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-goldfish.c b/drivers/rtc/rtc-goldfish.c new file mode 100644 index 000000000..30cbe22c5 --- /dev/null +++ b/drivers/rtc/rtc-goldfish.c @@ -0,0 +1,240 @@ +/* drivers/rtc/rtc-goldfish.c + * + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2017 Imagination Technologies Ltd. + * + * 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/platform_device.h> +#include <linux/rtc.h> +#include <linux/io.h> + +#define TIMER_TIME_LOW 0x00 /* get low bits of current time */ + /* and update TIMER_TIME_HIGH */ +#define TIMER_TIME_HIGH 0x04 /* get high bits of time at last */ + /* TIMER_TIME_LOW read */ +#define TIMER_ALARM_LOW 0x08 /* set low bits of alarm and */ + /* activate it */ +#define TIMER_ALARM_HIGH 0x0c /* set high bits of next alarm */ +#define TIMER_IRQ_ENABLED 0x10 +#define TIMER_CLEAR_ALARM 0x14 +#define TIMER_ALARM_STATUS 0x18 +#define TIMER_CLEAR_INTERRUPT 0x1c + +struct goldfish_rtc { + void __iomem *base; + int irq; + struct rtc_device *rtc; +}; + +static int goldfish_rtc_read_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + u64 rtc_alarm; + u64 rtc_alarm_low; + u64 rtc_alarm_high; + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + rtc_alarm_low = readl(base + TIMER_ALARM_LOW); + rtc_alarm_high = readl(base + TIMER_ALARM_HIGH); + rtc_alarm = (rtc_alarm_high << 32) | rtc_alarm_low; + + do_div(rtc_alarm, NSEC_PER_SEC); + memset(alrm, 0, sizeof(struct rtc_wkalrm)); + + rtc_time_to_tm(rtc_alarm, &alrm->time); + + if (readl(base + TIMER_ALARM_STATUS)) + alrm->enabled = 1; + else + alrm->enabled = 0; + + return 0; +} + +static int goldfish_rtc_set_alarm(struct device *dev, + struct rtc_wkalrm *alrm) +{ + struct goldfish_rtc *rtcdrv; + unsigned long rtc_alarm; + u64 rtc_alarm64; + u64 rtc_status_reg; + void __iomem *base; + int ret = 0; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (alrm->enabled) { + ret = rtc_tm_to_time(&alrm->time, &rtc_alarm); + if (ret != 0) + return ret; + + rtc_alarm64 = rtc_alarm * NSEC_PER_SEC; + writel((rtc_alarm64 >> 32), base + TIMER_ALARM_HIGH); + writel(rtc_alarm64, base + TIMER_ALARM_LOW); + writel(1, base + TIMER_IRQ_ENABLED); + } else { + /* + * if this function was called with enabled=0 + * then it could mean that the application is + * trying to cancel an ongoing alarm + */ + rtc_status_reg = readl(base + TIMER_ALARM_STATUS); + if (rtc_status_reg) + writel(1, base + TIMER_CLEAR_ALARM); + } + + return ret; +} + +static int goldfish_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + void __iomem *base; + struct goldfish_rtc *rtcdrv; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + if (enabled) + writel(1, base + TIMER_IRQ_ENABLED); + else + writel(0, base + TIMER_IRQ_ENABLED); + + return 0; +} + +static irqreturn_t goldfish_rtc_interrupt(int irq, void *dev_id) +{ + struct goldfish_rtc *rtcdrv = dev_id; + void __iomem *base = rtcdrv->base; + + writel(1, base + TIMER_CLEAR_INTERRUPT); + + rtc_update_irq(rtcdrv->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int goldfish_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + u64 time_high; + u64 time_low; + u64 time; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + time_low = readl(base + TIMER_TIME_LOW); + time_high = readl(base + TIMER_TIME_HIGH); + time = (time_high << 32) | time_low; + + do_div(time, NSEC_PER_SEC); + + rtc_time_to_tm(time, tm); + + return 0; +} + +static int goldfish_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct goldfish_rtc *rtcdrv; + void __iomem *base; + unsigned long now; + u64 now64; + int ret; + + rtcdrv = dev_get_drvdata(dev); + base = rtcdrv->base; + + ret = rtc_tm_to_time(tm, &now); + if (ret == 0) { + now64 = now * NSEC_PER_SEC; + writel((now64 >> 32), base + TIMER_TIME_HIGH); + writel(now64, base + TIMER_TIME_LOW); + } + + return ret; +} + +static const struct rtc_class_ops goldfish_rtc_ops = { + .read_time = goldfish_rtc_read_time, + .set_time = goldfish_rtc_set_time, + .read_alarm = goldfish_rtc_read_alarm, + .set_alarm = goldfish_rtc_set_alarm, + .alarm_irq_enable = goldfish_rtc_alarm_irq_enable +}; + +static int goldfish_rtc_probe(struct platform_device *pdev) +{ + struct goldfish_rtc *rtcdrv; + struct resource *r; + int err; + + rtcdrv = devm_kzalloc(&pdev->dev, sizeof(*rtcdrv), GFP_KERNEL); + if (!rtcdrv) + return -ENOMEM; + + platform_set_drvdata(pdev, rtcdrv); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) + return -ENODEV; + + rtcdrv->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(rtcdrv->base)) + return -ENODEV; + + rtcdrv->irq = platform_get_irq(pdev, 0); + if (rtcdrv->irq < 0) + return -ENODEV; + + rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &goldfish_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtcdrv->rtc)) + return PTR_ERR(rtcdrv->rtc); + + err = devm_request_irq(&pdev->dev, rtcdrv->irq, + goldfish_rtc_interrupt, + 0, pdev->name, rtcdrv); + if (err) + return err; + + return 0; +} + +static const struct of_device_id goldfish_rtc_of_match[] = { + { .compatible = "google,goldfish-rtc", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_rtc_of_match); + +static struct platform_driver goldfish_rtc = { + .probe = goldfish_rtc_probe, + .driver = { + .name = "goldfish_rtc", + .of_match_table = goldfish_rtc_of_match, + } +}; + +module_platform_driver(goldfish_rtc); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-hid-sensor-time.c b/drivers/rtc/rtc-hid-sensor-time.c new file mode 100644 index 000000000..3e1abb455 --- /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, false); + /* 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(time_state->rtc)) { + hid_device_io_stop(hsdev->hdev); + ret = PTR_ERR(time_state->rtc); + 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 const 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..a8c2d38b2 --- /dev/null +++ b/drivers/rtc/rtc-hym8563.c @@ -0,0 +1,610 @@ +/* + * 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 -EINVAL; + } + + 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] = bin2bcd(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 = 0; + + 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); + + 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 = 0; + 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; + } + + if (client->irq > 0) { + 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", + .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..80931114c --- /dev/null +++ b/drivers/rtc/rtc-imxdi.c @@ -0,0 +1,883 @@ +/* + * 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) /* Temperature 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 + * @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; + struct clk *clk; + u32 dsr; + spinlock_t irq_lock; + wait_queue_head_t write_wait; + struct mutex write_mutex; + struct work_struct work; +}; + +/* Some background: + * + * The DryIce unit is a complex security/tamper monitor device. To be able do + * its job in a useful manner it runs a bigger statemachine to bring it into + * security/tamper failure state and once again to bring it out of this state. + * + * This unit can be in one of three states: + * + * - "NON-VALID STATE" + * always after the battery power was removed + * - "FAILURE STATE" + * if one of the enabled security events has happened + * - "VALID STATE" + * if the unit works as expected + * + * Everything stops when the unit enters the failure state including the RTC + * counter (to be able to detect the time the security event happened). + * + * The following events (when enabled) let the DryIce unit enter the failure + * state: + * + * - wire-mesh-tamper detect + * - external tamper B detect + * - external tamper A detect + * - temperature tamper detect + * - clock tamper detect + * - voltage tamper detect + * - RTC counter overflow + * - monotonic counter overflow + * - external boot + * + * If we find the DryIce unit in "FAILURE STATE" and the TDCHL cleared, we + * can only detect this state. In this case the unit is completely locked and + * must force a second "SYSTEM POR" to bring the DryIce into the + * "NON-VALID STATE" + "FAILURE STATE" where a recovery is possible. + * If the TDCHL is set in the "FAILURE STATE" we are out of luck. In this case + * a battery power cycle is required. + * + * In the "NON-VALID STATE" + "FAILURE STATE" we can clear the "FAILURE STATE" + * and recover the DryIce unit. By clearing the "NON-VALID STATE" as the last + * task, we bring back this unit into life. + */ + +/* + * Do a write into the unit without interrupt support. + * We do not need to check the WEF here, because the only reason this kind of + * write error can happen is if we write to the unit twice within the 122 us + * interval. This cannot happen, since we are using this function only while + * setting up the unit. + */ +static void di_write_busy_wait(const struct imxdi_dev *imxdi, u32 val, + unsigned reg) +{ + /* do the register write */ + writel(val, imxdi->ioaddr + reg); + + /* + * now it takes four 32,768 kHz clock cycles to take + * the change into effect = 122 us + */ + usleep_range(130, 200); +} + +static void di_report_tamper_info(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dtcr; + + dtcr = readl(imxdi->ioaddr + DTCR); + + dev_emerg(&imxdi->pdev->dev, "DryIce tamper event detected\n"); + /* the following flags force a transition into the "FAILURE STATE" */ + if (dsr & DSR_VTD) + dev_emerg(&imxdi->pdev->dev, "%sVoltage Tamper Event\n", + dtcr & DTCR_VTE ? "" : "Spurious "); + + if (dsr & DSR_CTD) + dev_emerg(&imxdi->pdev->dev, "%s32768 Hz Clock Tamper Event\n", + dtcr & DTCR_CTE ? "" : "Spurious "); + + if (dsr & DSR_TTD) + dev_emerg(&imxdi->pdev->dev, "%sTemperature Tamper Event\n", + dtcr & DTCR_TTE ? "" : "Spurious "); + + if (dsr & DSR_SAD) + dev_emerg(&imxdi->pdev->dev, + "%sSecure Controller Alarm Event\n", + dtcr & DTCR_SAIE ? "" : "Spurious "); + + if (dsr & DSR_EBD) + dev_emerg(&imxdi->pdev->dev, "%sExternal Boot Tamper Event\n", + dtcr & DTCR_EBE ? "" : "Spurious "); + + if (dsr & DSR_ETAD) + dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper A Event\n", + dtcr & DTCR_ETAE ? "" : "Spurious "); + + if (dsr & DSR_ETBD) + dev_emerg(&imxdi->pdev->dev, "%sExternal Tamper B Event\n", + dtcr & DTCR_ETBE ? "" : "Spurious "); + + if (dsr & DSR_WTD) + dev_emerg(&imxdi->pdev->dev, "%sWire-mesh Tamper Event\n", + dtcr & DTCR_WTE ? "" : "Spurious "); + + if (dsr & DSR_MCO) + dev_emerg(&imxdi->pdev->dev, + "%sMonotonic-counter Overflow Event\n", + dtcr & DTCR_MOE ? "" : "Spurious "); + + if (dsr & DSR_TCO) + dev_emerg(&imxdi->pdev->dev, "%sTimer-counter Overflow Event\n", + dtcr & DTCR_TOE ? "" : "Spurious "); +} + +static void di_what_is_to_be_done(struct imxdi_dev *imxdi, + const char *power_supply) +{ + dev_emerg(&imxdi->pdev->dev, "Please cycle the %s power supply in order to get the DryIce/RTC unit working again\n", + power_supply); +} + +static int di_handle_failure_state(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dcr; + + dev_dbg(&imxdi->pdev->dev, "DSR register reports: %08X\n", dsr); + + /* report the cause */ + di_report_tamper_info(imxdi, dsr); + + dcr = readl(imxdi->ioaddr + DCR); + + if (dcr & DCR_FSHL) { + /* we are out of luck */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + /* + * with the next SYSTEM POR we will transit from the "FAILURE STATE" + * into the "NON-VALID STATE" + "FAILURE STATE" + */ + di_what_is_to_be_done(imxdi, "main"); + + return -ENODEV; +} + +static int di_handle_valid_state(struct imxdi_dev *imxdi, u32 dsr) +{ + /* initialize alarm */ + di_write_busy_wait(imxdi, DCAMR_UNSET, DCAMR); + di_write_busy_wait(imxdi, 0, DCALR); + + /* clear alarm flag */ + if (dsr & DSR_CAF) + di_write_busy_wait(imxdi, DSR_CAF, DSR); + + return 0; +} + +static int di_handle_invalid_state(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dcr, sec; + + /* + * lets disable all sources which can force the DryIce unit into + * the "FAILURE STATE" for now + */ + di_write_busy_wait(imxdi, 0x00000000, DTCR); + /* and lets protect them at runtime from any change */ + di_write_busy_wait(imxdi, DCR_TDCSL, DCR); + + sec = readl(imxdi->ioaddr + DTCMR); + if (sec != 0) + dev_warn(&imxdi->pdev->dev, + "The security violation has happened at %u seconds\n", + sec); + /* + * the timer cannot be set/modified if + * - the TCHL or TCSL bit is set in DCR + */ + dcr = readl(imxdi->ioaddr + DCR); + if (!(dcr & DCR_TCE)) { + if (dcr & DCR_TCHL) { + /* we are out of luck */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + if (dcr & DCR_TCSL) { + di_what_is_to_be_done(imxdi, "main"); + return -ENODEV; + } + } + /* + * - the timer counter stops/is stopped if + * - its overflow flag is set (TCO in DSR) + * -> clear overflow bit to make it count again + * - NVF is set in DSR + * -> clear non-valid bit to make it count again + * - its TCE (DCR) is cleared + * -> set TCE to make it count + * - it was never set before + * -> write a time into it (required again if the NVF was set) + */ + /* state handled */ + di_write_busy_wait(imxdi, DSR_NVF, DSR); + /* clear overflow flag */ + di_write_busy_wait(imxdi, DSR_TCO, DSR); + /* enable the counter */ + di_write_busy_wait(imxdi, dcr | DCR_TCE, DCR); + /* set and trigger it to make it count */ + di_write_busy_wait(imxdi, sec, DTCMR); + + /* now prepare for the valid state */ + return di_handle_valid_state(imxdi, __raw_readl(imxdi->ioaddr + DSR)); +} + +static int di_handle_invalid_and_failure_state(struct imxdi_dev *imxdi, u32 dsr) +{ + u32 dcr; + + /* + * now we must first remove the tamper sources in order to get the + * device out of the "FAILURE STATE" + * To disable any of the following sources we need to modify the DTCR + */ + if (dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | DSR_EBD | DSR_SAD | + DSR_TTD | DSR_CTD | DSR_VTD | DSR_MCO | DSR_TCO)) { + dcr = __raw_readl(imxdi->ioaddr + DCR); + if (dcr & DCR_TDCHL) { + /* + * the tamper register is locked. We cannot disable the + * tamper detection. The TDCHL can only be reset by a + * DRYICE POR, but we cannot force a DRYICE POR in + * softwere because we are still in "FAILURE STATE". + * We need a DRYICE POR via battery power cycling.... + */ + /* + * out of luck! + * we cannot disable them without a DRYICE POR + */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + if (dcr & DCR_TDCSL) { + /* a soft lock can be removed by a SYSTEM POR */ + di_what_is_to_be_done(imxdi, "main"); + return -ENODEV; + } + } + + /* disable all sources */ + di_write_busy_wait(imxdi, 0x00000000, DTCR); + + /* clear the status bits now */ + di_write_busy_wait(imxdi, dsr & (DSR_WTD | DSR_ETBD | DSR_ETAD | + DSR_EBD | DSR_SAD | DSR_TTD | DSR_CTD | DSR_VTD | + DSR_MCO | DSR_TCO), DSR); + + dsr = readl(imxdi->ioaddr + DSR); + if ((dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF | + DSR_WCF | DSR_WEF)) != 0) + dev_warn(&imxdi->pdev->dev, + "There are still some sources of pain in DSR: %08x!\n", + dsr & ~(DSR_NVF | DSR_SVF | DSR_WBF | DSR_WNF | + DSR_WCF | DSR_WEF)); + + /* + * now we are trying to clear the "Security-violation flag" to + * get the DryIce out of this state + */ + di_write_busy_wait(imxdi, DSR_SVF, DSR); + + /* success? */ + dsr = readl(imxdi->ioaddr + DSR); + if (dsr & DSR_SVF) { + dev_crit(&imxdi->pdev->dev, + "Cannot clear the security violation flag. We are ending up in an endless loop!\n"); + /* last resort */ + di_what_is_to_be_done(imxdi, "battery"); + return -ENODEV; + } + + /* + * now we have left the "FAILURE STATE" and ending up in the + * "NON-VALID STATE" time to recover everything + */ + return di_handle_invalid_state(imxdi, dsr); +} + +static int di_handle_state(struct imxdi_dev *imxdi) +{ + int rc; + u32 dsr; + + dsr = readl(imxdi->ioaddr + DSR); + + switch (dsr & (DSR_NVF | DSR_SVF)) { + case DSR_NVF: + dev_warn(&imxdi->pdev->dev, "Invalid stated unit detected\n"); + rc = di_handle_invalid_state(imxdi, dsr); + break; + case DSR_SVF: + dev_warn(&imxdi->pdev->dev, "Failure stated unit detected\n"); + rc = di_handle_failure_state(imxdi, dsr); + break; + case DSR_NVF | DSR_SVF: + dev_warn(&imxdi->pdev->dev, + "Failure+Invalid stated unit detected\n"); + rc = di_handle_invalid_and_failure_state(imxdi, dsr); + break; + default: + dev_notice(&imxdi->pdev->dev, "Unlocked unit detected\n"); + rc = di_handle_valid_state(imxdi, dsr); + } + + return rc; +} + +/* + * 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); + writel(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); + writel(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 */ + writel(DSR_WEF, imxdi->ioaddr + DSR); + + /* wait for it to take effect */ + for (cnt = 0; cnt < 1000; cnt++) { + if ((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 */ + 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 = 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); + u32 dcr, dsr; + int rc; + + dcr = readl(imxdi->ioaddr + DCR); + dsr = readl(imxdi->ioaddr + DSR); + + if (!(dcr & DCR_TCE) || (dsr & DSR_SVF)) { + if (dcr & DCR_TCHL) { + /* we are even more out of luck */ + di_what_is_to_be_done(imxdi, "battery"); + return -EPERM; + } + if ((dcr & DCR_TCSL) || (dsr & DSR_SVF)) { + /* we are out of luck for now */ + di_what_is_to_be_done(imxdi, "main"); + return -EPERM; + } + } + + /* zero the fractional part first */ + rc = di_write_wait(imxdi, 0, DTCLR); + if (rc != 0) + return rc; + + rc = di_write_wait(imxdi, secs, DTCMR); + if (rc != 0) + return rc; + + return di_write_wait(imxdi, readl(imxdi->ioaddr + DCR) | DCR_TCE, DCR); +} + +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 = readl(imxdi->ioaddr + DCAMR); + rtc_time_to_tm(dcamr, &alarm->time); + + /* alarm is enabled if the interrupt is enabled */ + alarm->enabled = (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 = (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 = 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 const 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, +}; + +/* + * interrupt handler for dryice "normal" and security violation interrupt + */ +static irqreturn_t dryice_irq(int irq, void *dev_id) +{ + struct imxdi_dev *imxdi = dev_id; + u32 dsr, dier; + irqreturn_t rc = IRQ_NONE; + + dier = readl(imxdi->ioaddr + DIER); + dsr = readl(imxdi->ioaddr + DSR); + + /* handle the security violation event */ + if (dier & DIER_SVIE) { + if (dsr & DSR_SVF) { + /* + * Disable the interrupt when this kind of event has + * happened. + * There cannot be more than one event of this type, + * because it needs a complex state change + * including a main power cycle to get again out of + * this state. + */ + di_int_disable(imxdi, DIER_SVIE); + /* report the violation */ + di_report_tamper_info(imxdi, dsr); + rc = IRQ_HANDLED; + } + } + + /* 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.head)) + return rc; + + /* DSR_WCF clears itself on DSR read */ + 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 */ + 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 norm_irq, sec_irq; + 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); + + norm_irq = platform_get_irq(pdev, 0); + if (norm_irq < 0) + return norm_irq; + + /* the 2nd irq is the security violation irq + * make this optional, don't break the device tree ABI + */ + sec_irq = platform_get_irq(pdev, 1); + if (sec_irq <= 0) + sec_irq = IRQ_NOTCONNECTED; + + 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 */ + writel(0, imxdi->ioaddr + DIER); + + rc = di_handle_state(imxdi); + if (rc != 0) + goto err; + + rc = devm_request_irq(&pdev->dev, norm_irq, dryice_irq, + IRQF_SHARED, pdev->name, imxdi); + if (rc) { + dev_warn(&pdev->dev, "interrupt not available.\n"); + goto err; + } + + rc = devm_request_irq(&pdev->dev, sec_irq, dryice_irq, + IRQF_SHARED, pdev->name, imxdi); + if (rc) { + dev_warn(&pdev->dev, "security violation interrupt not available.\n"); + /* this is not an error, see above */ + } + + 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 */ + 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..890ccfc9e --- /dev/null +++ b/drivers/rtc/rtc-isl12022.c @@ -0,0 +1,289 @@ +/* + * 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> + +/* 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_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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); + + return 0; +} + +static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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 = true; + } + + /* 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 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; + + 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" }, + { }, +}; +MODULE_DEVICE_TABLE(of, isl12022_dt_match); +#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"); diff --git a/drivers/rtc/rtc-isl12026.c b/drivers/rtc/rtc-isl12026.c new file mode 100644 index 000000000..97f594f96 --- /dev/null +++ b/drivers/rtc/rtc-isl12026.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * An I2C driver for the Intersil ISL 12026 + * + * Copyright (c) 2018 Cavium, Inc. + */ +#include <linux/bcd.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/nvmem-provider.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/rtc.h> +#include <linux/slab.h> + +/* register offsets */ +#define ISL12026_REG_PWR 0x14 +# define ISL12026_REG_PWR_BSW BIT(6) +# define ISL12026_REG_PWR_SBIB BIT(7) +#define ISL12026_REG_SC 0x30 +#define ISL12026_REG_HR 0x32 +# define ISL12026_REG_HR_MIL BIT(7) /* military or 24 hour time */ +#define ISL12026_REG_SR 0x3f +# define ISL12026_REG_SR_RTCF BIT(0) +# define ISL12026_REG_SR_WEL BIT(1) +# define ISL12026_REG_SR_RWEL BIT(2) +# define ISL12026_REG_SR_MBZ BIT(3) +# define ISL12026_REG_SR_OSCF BIT(4) + +/* The EEPROM array responds at i2c address 0x57 */ +#define ISL12026_EEPROM_ADDR 0x57 + +#define ISL12026_PAGESIZE 16 +#define ISL12026_NVMEM_WRITE_TIME 20 + +struct isl12026 { + struct rtc_device *rtc; + struct i2c_client *nvm_client; +}; + +static int isl12026_read_reg(struct i2c_client *client, int reg) +{ + u8 addr[] = {0, reg}; + u8 val; + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(addr), + .buf = addr + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &val + } + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read reg error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + } else { + ret = val; + } + + return ret; +} + +static int isl12026_arm_write(struct i2c_client *client) +{ + int ret; + u8 op[3]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = op + }; + + /* Set SR.WEL */ + op[0] = 0; + op[1] = ISL12026_REG_SR; + op[2] = ISL12026_REG_SR_WEL; + msg.len = 3; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "write error SR.WEL, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + /* Set SR.WEL and SR.RWEL */ + op[2] = ISL12026_REG_SR_WEL | ISL12026_REG_SR_RWEL; + msg.len = 3; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, + "write error SR.WEL|SR.RWEL, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } else { + ret = 0; + } +out: + return ret; +} + +static int isl12026_disarm_write(struct i2c_client *client) +{ + int ret; + u8 op[3] = {0, ISL12026_REG_SR, 0}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(op), + .buf = op + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, + "write error SR, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + } else { + ret = 0; + } + + return ret; +} + +static int isl12026_write_reg(struct i2c_client *client, int reg, u8 val) +{ + int ret; + u8 op[3] = {0, reg, val}; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(op), + .buf = op + }; + + ret = isl12026_arm_write(client); + if (ret) + return ret; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "write error CCR, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + msleep(ISL12026_NVMEM_WRITE_TIME); + + ret = isl12026_disarm_write(client); +out: + return ret; +} + +static int isl12026_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + u8 op[10]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = sizeof(op), + .buf = op + }; + + ret = isl12026_arm_write(client); + if (ret) + return ret; + + /* Set the CCR registers */ + op[0] = 0; + op[1] = ISL12026_REG_SC; + op[2] = bin2bcd(tm->tm_sec); /* SC */ + op[3] = bin2bcd(tm->tm_min); /* MN */ + op[4] = bin2bcd(tm->tm_hour) | ISL12026_REG_HR_MIL; /* HR */ + op[5] = bin2bcd(tm->tm_mday); /* DT */ + op[6] = bin2bcd(tm->tm_mon + 1); /* MO */ + op[7] = bin2bcd(tm->tm_year % 100); /* YR */ + op[8] = bin2bcd(tm->tm_wday & 7); /* DW */ + op[9] = bin2bcd(tm->tm_year >= 100 ? 20 : 19); /* Y2K */ + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret != 1) { + dev_err(&client->dev, "write error CCR, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + ret = isl12026_disarm_write(client); +out: + return ret; +} + +static int isl12026_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 ccr[8]; + u8 addr[2]; + u8 sr; + int ret; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(addr), + .buf = addr + }, { + .addr = client->addr, + .flags = I2C_M_RD, + } + }; + + /* First, read SR */ + addr[0] = 0; + addr[1] = ISL12026_REG_SR; + msgs[1].len = 1; + msgs[1].buf = &sr; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + if (sr & ISL12026_REG_SR_RTCF) + dev_warn(&client->dev, "Real-Time Clock Failure on read\n"); + if (sr & ISL12026_REG_SR_OSCF) + dev_warn(&client->dev, "Oscillator Failure on read\n"); + + /* Second, CCR regs */ + addr[0] = 0; + addr[1] = ISL12026_REG_SC; + msgs[1].len = sizeof(ccr); + msgs[1].buf = ccr; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&client->dev, "read error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + goto out; + } + + tm->tm_sec = bcd2bin(ccr[0] & 0x7F); + tm->tm_min = bcd2bin(ccr[1] & 0x7F); + if (ccr[2] & ISL12026_REG_HR_MIL) + tm->tm_hour = bcd2bin(ccr[2] & 0x3F); + else + tm->tm_hour = bcd2bin(ccr[2] & 0x1F) + + ((ccr[2] & 0x20) ? 12 : 0); + tm->tm_mday = bcd2bin(ccr[3] & 0x3F); + tm->tm_mon = bcd2bin(ccr[4] & 0x1F) - 1; + tm->tm_year = bcd2bin(ccr[5]); + if (bcd2bin(ccr[7]) == 20) + tm->tm_year += 100; + tm->tm_wday = ccr[6] & 0x07; + + ret = 0; +out: + return ret; +} + +static const struct rtc_class_ops isl12026_rtc_ops = { + .read_time = isl12026_rtc_read_time, + .set_time = isl12026_rtc_set_time, +}; + +static int isl12026_nvm_read(void *p, unsigned int offset, + void *val, size_t bytes) +{ + struct isl12026 *priv = p; + int ret; + u8 addr[2]; + struct i2c_msg msgs[] = { + { + .addr = priv->nvm_client->addr, + .flags = 0, + .len = sizeof(addr), + .buf = addr + }, { + .addr = priv->nvm_client->addr, + .flags = I2C_M_RD, + .buf = val + } + }; + + /* + * offset and bytes checked and limited by nvmem core, so + * proceed without further checks. + */ + ret = mutex_lock_interruptible(&priv->rtc->ops_lock); + if (ret) + return ret; + + /* 2 bytes of address, most significant first */ + addr[0] = offset >> 8; + addr[1] = offset; + msgs[1].len = bytes; + ret = i2c_transfer(priv->nvm_client->adapter, msgs, ARRAY_SIZE(msgs)); + + mutex_unlock(&priv->rtc->ops_lock); + + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&priv->nvm_client->dev, + "nvmem read error, ret=%d\n", ret); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int isl12026_nvm_write(void *p, unsigned int offset, + void *val, size_t bytes) +{ + struct isl12026 *priv = p; + int ret; + u8 *v = val; + size_t chunk_size, num_written; + u8 payload[ISL12026_PAGESIZE + 2]; /* page + 2 address bytes */ + struct i2c_msg msgs[] = { + { + .addr = priv->nvm_client->addr, + .flags = 0, + .buf = payload + } + }; + + /* + * offset and bytes checked and limited by nvmem core, so + * proceed without further checks. + */ + ret = mutex_lock_interruptible(&priv->rtc->ops_lock); + if (ret) + return ret; + + num_written = 0; + while (bytes) { + chunk_size = round_down(offset, ISL12026_PAGESIZE) + + ISL12026_PAGESIZE - offset; + chunk_size = min(bytes, chunk_size); + /* + * 2 bytes of address, most significant first, followed + * by page data bytes + */ + memcpy(payload + 2, v + num_written, chunk_size); + payload[0] = offset >> 8; + payload[1] = offset; + msgs[0].len = chunk_size + 2; + ret = i2c_transfer(priv->nvm_client->adapter, + msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + dev_err(&priv->nvm_client->dev, + "nvmem write error, ret=%d\n", ret); + ret = ret < 0 ? ret : -EIO; + break; + } + ret = 0; + bytes -= chunk_size; + offset += chunk_size; + num_written += chunk_size; + msleep(ISL12026_NVMEM_WRITE_TIME); + } + + mutex_unlock(&priv->rtc->ops_lock); + + return ret; +} + +static void isl12026_force_power_modes(struct i2c_client *client) +{ + int ret; + int pwr, requested_pwr; + u32 bsw_val, sbib_val; + bool set_bsw, set_sbib; + + /* + * If we can read the of_property, set the specified value. + * If there is an error reading the of_property (likely + * because it does not exist), keep the current value. + */ + ret = of_property_read_u32(client->dev.of_node, + "isil,pwr-bsw", &bsw_val); + set_bsw = (ret == 0); + + ret = of_property_read_u32(client->dev.of_node, + "isil,pwr-sbib", &sbib_val); + set_sbib = (ret == 0); + + /* Check if PWR.BSW and/or PWR.SBIB need specified values */ + if (!set_bsw && !set_sbib) + return; + + pwr = isl12026_read_reg(client, ISL12026_REG_PWR); + if (pwr < 0) { + dev_warn(&client->dev, "Error: Failed to read PWR %d\n", pwr); + return; + } + + requested_pwr = pwr; + + if (set_bsw) { + if (bsw_val) + requested_pwr |= ISL12026_REG_PWR_BSW; + else + requested_pwr &= ~ISL12026_REG_PWR_BSW; + } /* else keep current BSW */ + + if (set_sbib) { + if (sbib_val) + requested_pwr |= ISL12026_REG_PWR_SBIB; + else + requested_pwr &= ~ISL12026_REG_PWR_SBIB; + } /* else keep current SBIB */ + + if (pwr >= 0 && pwr != requested_pwr) { + dev_dbg(&client->dev, "PWR: %02x\n", pwr); + dev_dbg(&client->dev, "Updating PWR to: %02x\n", requested_pwr); + isl12026_write_reg(client, ISL12026_REG_PWR, requested_pwr); + } +} + +static int isl12026_probe_new(struct i2c_client *client) +{ + struct isl12026 *priv; + int ret; + struct nvmem_config nvm_cfg = { + .name = "isl12026-", + .base_dev = &client->dev, + .stride = 1, + .word_size = 1, + .size = 512, + .reg_read = isl12026_nvm_read, + .reg_write = isl12026_nvm_write, + }; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(client, priv); + + isl12026_force_power_modes(client); + + priv->nvm_client = i2c_new_dummy(client->adapter, ISL12026_EEPROM_ADDR); + if (!priv->nvm_client) + return -ENOMEM; + + priv->rtc = devm_rtc_allocate_device(&client->dev); + ret = PTR_ERR_OR_ZERO(priv->rtc); + if (ret) + return ret; + + priv->rtc->ops = &isl12026_rtc_ops; + nvm_cfg.priv = priv; + ret = rtc_nvmem_register(priv->rtc, &nvm_cfg); + if (ret) + return ret; + + return rtc_register_device(priv->rtc); +} + +static int isl12026_remove(struct i2c_client *client) +{ + struct isl12026 *priv = i2c_get_clientdata(client); + + i2c_unregister_device(priv->nvm_client); + return 0; +} + +static const struct of_device_id isl12026_dt_match[] = { + { .compatible = "isil,isl12026" }, + { } +}; +MODULE_DEVICE_TABLE(of, isl12026_dt_match); + +static struct i2c_driver isl12026_driver = { + .driver = { + .name = "rtc-isl12026", + .of_match_table = isl12026_dt_match, + }, + .probe_new = isl12026_probe_new, + .remove = isl12026_remove, +}; + +module_i2c_driver(isl12026_driver); + +MODULE_DESCRIPTION("ISL 12026 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..033f65aef --- /dev/null +++ b/drivers/rtc/rtc-isl1208.c @@ -0,0 +1,855 @@ +/* + * 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> +#include "rtc-core.h" +#include <linux/of_irq.h> + +/* 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_EVT (1<<3) /* event */ +#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 ISL1219_REG_EV 0x09 +#define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */ +#define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */ +#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 + +/* event section */ +#define ISL1219_REG_SCT 0x14 +#define ISL1219_REG_MNT 0x15 +#define ISL1219_REG_HRT 0x16 +#define ISL1219_REG_DTT 0x17 +#define ISL1219_REG_MOT 0x18 +#define ISL1219_REG_YRT 0x19 +#define ISL1219_EVT_SECTION_LEN 6 + +static struct i2c_driver isl1208_driver; + +/* ISL1208 various variants */ +enum { + TYPE_ISL1208 = 0, + TYPE_ISL1218, + TYPE_ISL1219, +}; + +/* 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; + + WARN_ON(reg > ISL1219_REG_YRT); + WARN_ON(reg + len > ISL1219_REG_YRT + 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; + + WARN_ON(reg > ISL1219_REG_YRT); + WARN_ON(reg + len > ISL1219_REG_YRT + 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; + struct rtc_time rtc_tm; + int err, enable; + + err = isl1208_i2c_read_time(client, &rtc_tm); + if (err) + return err; + + /* If the alarm time is before the current time disable the alarm */ + if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0) + 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 = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(&client->dev, "%s: reading SR failed\n", __func__); + return sr; + } + 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 ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return sr; + } + + sr &= ~ISL1208_REG_SR_EVT; + + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); + if (sr < 0) + dev_err(dev, "%s: writing SR failed\n", + __func__); + + return count; +}; + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev->parent); + u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, }; + struct rtc_time tm; + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return sr; + } + + if (!(sr & ISL1208_REG_SR_EVT)) + return 0; + + sr = isl1208_i2c_read_regs(client, ISL1219_REG_SCT, regs, + ISL1219_EVT_SECTION_LEN); + if (sr < 0) { + dev_err(dev, "%s: reading event section failed\n", + __func__); + return 0; + } + + /* MSB of each alarm register is an enable bit */ + tm.tm_sec = bcd2bin(regs[ISL1219_REG_SCT - ISL1219_REG_SCT] & 0x7f); + tm.tm_min = bcd2bin(regs[ISL1219_REG_MNT - ISL1219_REG_SCT] & 0x7f); + tm.tm_hour = bcd2bin(regs[ISL1219_REG_HRT - ISL1219_REG_SCT] & 0x3f); + tm.tm_mday = bcd2bin(regs[ISL1219_REG_DTT - ISL1219_REG_SCT] & 0x3f); + tm.tm_mon = + bcd2bin(regs[ISL1219_REG_MOT - ISL1219_REG_SCT] & 0x1f) - 1; + tm.tm_year = bcd2bin(regs[ISL1219_REG_YRT - ISL1219_REG_SCT]) + 100; + + sr = rtc_valid_tm(&tm); + if (sr) + return sr; + + return sprintf(buf, "%llu\n", + (unsigned long long)rtc_tm_to_time64(&tm)); +}; + +static DEVICE_ATTR_RW(timestamp0); + +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; + } + + if (sr & ISL1208_REG_SR_EVT) { + sysfs_notify(&rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); + dev_warn(&client->dev, "event detected"); + handled = 1; + } + + 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->parent)); + 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->parent)); + 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->parent)); + 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; + + if (isl1208_i2c_set_usr(to_i2c_client(dev->parent), usr)) + return -EIO; + + return 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 struct attribute *isl1219_rtc_attrs[] = { + &dev_attr_timestamp0.attr, + NULL +}; + +static const struct attribute_group isl1219_rtc_sysfs_files = { + .attrs = isl1219_rtc_attrs, +}; + +static int isl1208_setup_irq(struct i2c_client *client, int irq) +{ + int rc = devm_request_threaded_irq(&client->dev, irq, NULL, + isl1208_rtc_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + isl1208_driver.driver.name, + client); + if (!rc) { + device_init_wakeup(&client->dev, 1); + enable_irq_wake(irq); + } else { + dev_err(&client->dev, + "Unable to request irq %d, no alarm support\n", + irq); + } + return rc; +} + +static int +isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int rc = 0; + struct rtc_device *rtc; + int evdet_irq = -1; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + if (isl1208_i2c_validate_client(client) < 0) + return -ENODEV; + + rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &isl1208_rtc_ops; + + 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"); + + if (id->driver_data == TYPE_ISL1219) { + struct device_node *np = client->dev.of_node; + u32 evienb; + + rc = i2c_smbus_read_byte_data(client, ISL1219_REG_EV); + if (rc < 0) { + dev_err(&client->dev, "failed to read EV reg\n"); + return rc; + } + rc |= ISL1219_REG_EV_EVEN; + if (!of_property_read_u32(np, "isil,ev-evienb", &evienb)) { + if (evienb) + rc |= ISL1219_REG_EV_EVIENB; + else + rc &= ~ISL1219_REG_EV_EVIENB; + } + rc = i2c_smbus_write_byte_data(client, ISL1219_REG_EV, rc); + if (rc < 0) { + dev_err(&client->dev, "could not enable tamper detection\n"); + return rc; + } + rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files); + if (rc) + return rc; + evdet_irq = of_irq_get_byname(np, "evdet"); + } + + rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files); + if (rc) + return rc; + + if (client->irq > 0) + rc = isl1208_setup_irq(client, client->irq); + if (rc) + return rc; + + if (evdet_irq > 0 && evdet_irq != client->irq) + rc = isl1208_setup_irq(client, evdet_irq); + if (rc) + return rc; + + return rtc_register_device(rtc); +} + +static const struct i2c_device_id isl1208_id[] = { + { "isl1208", TYPE_ISL1208 }, + { "isl1218", TYPE_ISL1218 }, + { "isl1219", TYPE_ISL1219 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, isl1208_id); + +static const struct of_device_id isl1208_of_match[] = { + { .compatible = "isil,isl1208" }, + { .compatible = "isil,isl1218" }, + { .compatible = "isil,isl1219" }, + { } +}; +MODULE_DEVICE_TABLE(of, isl1208_of_match); + +static struct i2c_driver isl1208_driver = { + .driver = { + .name = "rtc-isl1208", + .of_match_table = of_match_ptr(isl1208_of_match), + }, + .probe = isl1208_probe, + .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"); diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c new file mode 100644 index 000000000..d0a891777 --- /dev/null +++ b/drivers/rtc/rtc-jz4740.c @@ -0,0 +1,451 @@ +/* + * 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/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/reboot.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_WAKEUP_FILTER 0x24 +#define JZ_REG_RTC_RESET_COUNTER 0x28 +#define JZ_REG_RTC_SCRATCHPAD 0x34 + +/* The following are present on the jz4780 */ +#define JZ_REG_RTC_WENR 0x3C +#define JZ_RTC_WENR_WEN BIT(31) + +#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) + +/* Magic value to enable writes on jz4780 */ +#define JZ_RTC_WENR_MAGIC 0xA55A + +#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 +#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 + +enum jz4740_rtc_type { + ID_JZ4740, + ID_JZ4780, +}; + +struct jz4740_rtc { + void __iomem *base; + enum jz4740_rtc_type type; + + struct rtc_device *rtc; + struct clk *clk; + + int irq; + + spinlock_t lock; + + unsigned int min_wakeup_pin_assert_time; + unsigned int reset_pin_assert_time; +}; + +static struct device *dev_for_power_off; + +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 = 10000; + + 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 jz4780_rtc_enable_write(struct jz4740_rtc *rtc) +{ + uint32_t ctrl; + int ret, timeout = 10000; + + ret = jz4740_rtc_wait_write_ready(rtc); + if (ret != 0) + return ret; + + writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR); + + do { + ctrl = readl(rtc->base + JZ_REG_RTC_WENR); + } while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout); + + return timeout ? 0 : -EIO; +} + +static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, + uint32_t val) +{ + int ret = 0; + + if (rtc->type >= ID_JZ4780) + ret = jz4780_rtc_enable_write(rtc); + if (ret == 0) + 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 0; +} + +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 const 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; +} + +static 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); +} + +static void jz4740_rtc_power_off(void) +{ + struct jz4740_rtc *rtc = dev_get_drvdata(dev_for_power_off); + unsigned long rtc_rate; + unsigned long wakeup_filter_ticks; + unsigned long reset_counter_ticks; + + clk_prepare_enable(rtc->clk); + + rtc_rate = clk_get_rate(rtc->clk); + + /* + * Set minimum wakeup pin assertion time: 100 ms. + * Range is 0 to 2 sec if RTC is clocked at 32 kHz. + */ + wakeup_filter_ticks = + (rtc->min_wakeup_pin_assert_time * rtc_rate) / 1000; + if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK) + wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; + else + wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK; + jz4740_rtc_reg_write(rtc, + JZ_REG_RTC_WAKEUP_FILTER, wakeup_filter_ticks); + + /* + * Set reset pin low-level assertion time after wakeup: 60 ms. + * Range is 0 to 125 ms if RTC is clocked at 32 kHz. + */ + reset_counter_ticks = (rtc->reset_pin_assert_time * rtc_rate) / 1000; + if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK) + reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK; + else + reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK; + jz4740_rtc_reg_write(rtc, + JZ_REG_RTC_RESET_COUNTER, reset_counter_ticks); + + jz4740_rtc_poweroff(dev_for_power_off); + kernel_halt(); +} + +static const struct of_device_id jz4740_rtc_of_match[] = { + { .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 }, + { .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); + +static int jz4740_rtc_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_rtc *rtc; + uint32_t scratchpad; + struct resource *mem; + const struct platform_device_id *id = platform_get_device_id(pdev); + const struct of_device_id *of_id = of_match_device( + jz4740_rtc_of_match, &pdev->dev); + struct device_node *np = pdev->dev.of_node; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + if (of_id) + rtc->type = (enum jz4740_rtc_type)of_id->data; + else + rtc->type = id->driver_data; + + 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); + + rtc->clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(rtc->clk)) { + dev_err(&pdev->dev, "Failed to get RTC clock\n"); + return PTR_ERR(rtc->clk); + } + + 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 to RTC registers\n"); + return ret; + } + } + + if (np && of_device_is_system_power_controller(np)) { + if (!pm_power_off) { + /* Default: 60ms */ + rtc->reset_pin_assert_time = 60; + of_property_read_u32(np, "reset-pin-assert-time-ms", + &rtc->reset_pin_assert_time); + + /* Default: 100ms */ + rtc->min_wakeup_pin_assert_time = 100; + of_property_read_u32(np, + "min-wakeup-pin-assert-time-ms", + &rtc->min_wakeup_pin_assert_time); + + dev_for_power_off = &pdev->dev; + pm_power_off = jz4740_rtc_power_off; + } else { + dev_warn(&pdev->dev, + "Poweroff handler already present!\n"); + } + } + + 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 const struct platform_device_id jz4740_rtc_ids[] = { + { "jz4740-rtc", ID_JZ4740 }, + { "jz4780-rtc", ID_JZ4780 }, + {} +}; +MODULE_DEVICE_TABLE(platform, jz4740_rtc_ids); + +static struct platform_driver jz4740_rtc_driver = { + .probe = jz4740_rtc_probe, + .driver = { + .name = "jz4740-rtc", + .pm = JZ4740_RTC_PM_OPS, + .of_match_table = of_match_ptr(jz4740_rtc_of_match), + }, + .id_table = jz4740_rtc_ids, +}; + +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..4a3c0f3aa --- /dev/null +++ b/drivers/rtc/rtc-lib.c @@ -0,0 +1,148 @@ +/* + * 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/export.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, secs; + int days; + + /* time must be positive */ + days = div_s64_rem(time, 86400, &secs); + + /* 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); + while (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); 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-lpc24xx.c b/drivers/rtc/rtc-lpc24xx.c new file mode 100644 index 000000000..14dc7b04f --- /dev/null +++ b/drivers/rtc/rtc-lpc24xx.c @@ -0,0 +1,310 @@ +/* + * RTC driver for NXP LPC178x/18xx/43xx Real-Time Clock (RTC) + * + * Copyright (C) 2011 NXP Semiconductors + * Copyright (C) 2015 Joachim Eastwood <manabian@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. + * + */ + +#include <linux/clk.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> + +/* LPC24xx RTC register offsets and bits */ +#define LPC24XX_ILR 0x00 +#define LPC24XX_RTCCIF BIT(0) +#define LPC24XX_RTCALF BIT(1) +#define LPC24XX_CTC 0x04 +#define LPC24XX_CCR 0x08 +#define LPC24XX_CLKEN BIT(0) +#define LPC178X_CCALEN BIT(4) +#define LPC24XX_CIIR 0x0c +#define LPC24XX_AMR 0x10 +#define LPC24XX_ALARM_DISABLE 0xff +#define LPC24XX_CTIME0 0x14 +#define LPC24XX_CTIME1 0x18 +#define LPC24XX_CTIME2 0x1c +#define LPC24XX_SEC 0x20 +#define LPC24XX_MIN 0x24 +#define LPC24XX_HOUR 0x28 +#define LPC24XX_DOM 0x2c +#define LPC24XX_DOW 0x30 +#define LPC24XX_DOY 0x34 +#define LPC24XX_MONTH 0x38 +#define LPC24XX_YEAR 0x3c +#define LPC24XX_ALSEC 0x60 +#define LPC24XX_ALMIN 0x64 +#define LPC24XX_ALHOUR 0x68 +#define LPC24XX_ALDOM 0x6c +#define LPC24XX_ALDOW 0x70 +#define LPC24XX_ALDOY 0x74 +#define LPC24XX_ALMON 0x78 +#define LPC24XX_ALYEAR 0x7c + +/* Macros to read fields in consolidated time (CT) registers */ +#define CT0_SECS(x) (((x) >> 0) & 0x3f) +#define CT0_MINS(x) (((x) >> 8) & 0x3f) +#define CT0_HOURS(x) (((x) >> 16) & 0x1f) +#define CT0_DOW(x) (((x) >> 24) & 0x07) +#define CT1_DOM(x) (((x) >> 0) & 0x1f) +#define CT1_MONTH(x) (((x) >> 8) & 0x0f) +#define CT1_YEAR(x) (((x) >> 16) & 0xfff) +#define CT2_DOY(x) (((x) >> 0) & 0xfff) + +#define rtc_readl(dev, reg) readl((dev)->rtc_base + (reg)) +#define rtc_writel(dev, reg, val) writel((val), (dev)->rtc_base + (reg)) + +struct lpc24xx_rtc { + void __iomem *rtc_base; + struct rtc_device *rtc; + struct clk *clk_rtc; + struct clk *clk_reg; +}; + +static int lpc24xx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + + /* Disable RTC during update */ + rtc_writel(rtc, LPC24XX_CCR, LPC178X_CCALEN); + + rtc_writel(rtc, LPC24XX_SEC, tm->tm_sec); + rtc_writel(rtc, LPC24XX_MIN, tm->tm_min); + rtc_writel(rtc, LPC24XX_HOUR, tm->tm_hour); + rtc_writel(rtc, LPC24XX_DOW, tm->tm_wday); + rtc_writel(rtc, LPC24XX_DOM, tm->tm_mday); + rtc_writel(rtc, LPC24XX_DOY, tm->tm_yday); + rtc_writel(rtc, LPC24XX_MONTH, tm->tm_mon); + rtc_writel(rtc, LPC24XX_YEAR, tm->tm_year); + + rtc_writel(rtc, LPC24XX_CCR, LPC24XX_CLKEN | LPC178X_CCALEN); + + return 0; +} + +static int lpc24xx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + u32 ct0, ct1, ct2; + + ct0 = rtc_readl(rtc, LPC24XX_CTIME0); + ct1 = rtc_readl(rtc, LPC24XX_CTIME1); + ct2 = rtc_readl(rtc, LPC24XX_CTIME2); + + tm->tm_sec = CT0_SECS(ct0); + tm->tm_min = CT0_MINS(ct0); + tm->tm_hour = CT0_HOURS(ct0); + tm->tm_wday = CT0_DOW(ct0); + tm->tm_mon = CT1_MONTH(ct1); + tm->tm_mday = CT1_DOM(ct1); + tm->tm_year = CT1_YEAR(ct1); + tm->tm_yday = CT2_DOY(ct2); + + return 0; +} + +static int lpc24xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + + tm->tm_sec = rtc_readl(rtc, LPC24XX_ALSEC); + tm->tm_min = rtc_readl(rtc, LPC24XX_ALMIN); + tm->tm_hour = rtc_readl(rtc, LPC24XX_ALHOUR); + tm->tm_mday = rtc_readl(rtc, LPC24XX_ALDOM); + tm->tm_wday = rtc_readl(rtc, LPC24XX_ALDOW); + tm->tm_yday = rtc_readl(rtc, LPC24XX_ALDOY); + tm->tm_mon = rtc_readl(rtc, LPC24XX_ALMON); + tm->tm_year = rtc_readl(rtc, LPC24XX_ALYEAR); + + wkalrm->enabled = rtc_readl(rtc, LPC24XX_AMR) == 0; + wkalrm->pending = !!(rtc_readl(rtc, LPC24XX_ILR) & LPC24XX_RTCCIF); + + return rtc_valid_tm(&wkalrm->time); +} + +static int lpc24xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time *tm = &wkalrm->time; + + /* Disable alarm irq during update */ + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + + rtc_writel(rtc, LPC24XX_ALSEC, tm->tm_sec); + rtc_writel(rtc, LPC24XX_ALMIN, tm->tm_min); + rtc_writel(rtc, LPC24XX_ALHOUR, tm->tm_hour); + rtc_writel(rtc, LPC24XX_ALDOM, tm->tm_mday); + rtc_writel(rtc, LPC24XX_ALDOW, tm->tm_wday); + rtc_writel(rtc, LPC24XX_ALDOY, tm->tm_yday); + rtc_writel(rtc, LPC24XX_ALMON, tm->tm_mon); + rtc_writel(rtc, LPC24XX_ALYEAR, tm->tm_year); + + if (wkalrm->enabled) + rtc_writel(rtc, LPC24XX_AMR, 0); + + return 0; +} + +static int lpc24xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct lpc24xx_rtc *rtc = dev_get_drvdata(dev); + + if (enable) + rtc_writel(rtc, LPC24XX_AMR, 0); + else + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + + return 0; +} + +static irqreturn_t lpc24xx_rtc_interrupt(int irq, void *data) +{ + unsigned long events = RTC_IRQF; + struct lpc24xx_rtc *rtc = data; + u32 rtc_iir; + + /* Check interrupt cause */ + rtc_iir = rtc_readl(rtc, LPC24XX_ILR); + if (rtc_iir & LPC24XX_RTCALF) { + events |= RTC_AF; + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + } + + /* Clear interrupt status and report event */ + rtc_writel(rtc, LPC24XX_ILR, rtc_iir); + rtc_update_irq(rtc->rtc, 1, events); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops lpc24xx_rtc_ops = { + .read_time = lpc24xx_rtc_read_time, + .set_time = lpc24xx_rtc_set_time, + .read_alarm = lpc24xx_rtc_read_alarm, + .set_alarm = lpc24xx_rtc_set_alarm, + .alarm_irq_enable = lpc24xx_rtc_alarm_irq_enable, +}; + +static int lpc24xx_rtc_probe(struct platform_device *pdev) +{ + struct lpc24xx_rtc *rtc; + struct resource *res; + 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->rtc_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtc->rtc_base)) + return PTR_ERR(rtc->rtc_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_warn(&pdev->dev, "can't get interrupt resource\n"); + return irq; + } + + rtc->clk_rtc = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(rtc->clk_rtc)) { + dev_err(&pdev->dev, "error getting rtc clock\n"); + return PTR_ERR(rtc->clk_rtc); + } + + rtc->clk_reg = devm_clk_get(&pdev->dev, "reg"); + if (IS_ERR(rtc->clk_reg)) { + dev_err(&pdev->dev, "error getting reg clock\n"); + return PTR_ERR(rtc->clk_reg); + } + + ret = clk_prepare_enable(rtc->clk_rtc); + if (ret) { + dev_err(&pdev->dev, "unable to enable rtc clock\n"); + return ret; + } + + ret = clk_prepare_enable(rtc->clk_reg); + if (ret) { + dev_err(&pdev->dev, "unable to enable reg clock\n"); + goto disable_rtc_clk; + } + + platform_set_drvdata(pdev, rtc); + + /* Clear any pending interrupts */ + rtc_writel(rtc, LPC24XX_ILR, LPC24XX_RTCCIF | LPC24XX_RTCALF); + + /* Enable RTC count */ + rtc_writel(rtc, LPC24XX_CCR, LPC24XX_CLKEN | LPC178X_CCALEN); + + ret = devm_request_irq(&pdev->dev, irq, lpc24xx_rtc_interrupt, 0, + pdev->name, rtc); + if (ret < 0) { + dev_warn(&pdev->dev, "can't request interrupt\n"); + goto disable_clks; + } + + rtc->rtc = devm_rtc_device_register(&pdev->dev, "lpc24xx-rtc", + &lpc24xx_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc)) { + dev_err(&pdev->dev, "can't register rtc device\n"); + ret = PTR_ERR(rtc->rtc); + goto disable_clks; + } + + return 0; + +disable_clks: + clk_disable_unprepare(rtc->clk_reg); +disable_rtc_clk: + clk_disable_unprepare(rtc->clk_rtc); + return ret; +} + +static int lpc24xx_rtc_remove(struct platform_device *pdev) +{ + struct lpc24xx_rtc *rtc = platform_get_drvdata(pdev); + + /* Ensure all interrupt sources are masked */ + rtc_writel(rtc, LPC24XX_AMR, LPC24XX_ALARM_DISABLE); + rtc_writel(rtc, LPC24XX_CIIR, 0); + + rtc_writel(rtc, LPC24XX_CCR, LPC178X_CCALEN); + + clk_disable_unprepare(rtc->clk_rtc); + clk_disable_unprepare(rtc->clk_reg); + + return 0; +} + +static const struct of_device_id lpc24xx_rtc_match[] = { + { .compatible = "nxp,lpc1788-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, lpc24xx_rtc_match); + +static struct platform_driver lpc24xx_rtc_driver = { + .probe = lpc24xx_rtc_probe, + .remove = lpc24xx_rtc_remove, + .driver = { + .name = "lpc24xx-rtc", + .of_match_table = lpc24xx_rtc_match, + }, +}; +module_platform_driver(lpc24xx_rtc_driver); + +MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com>"); +MODULE_DESCRIPTION("RTC driver for the LPC178x/18xx/408x/43xx SoCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c new file mode 100644 index 000000000..910e60027 --- /dev/null +++ b/drivers/rtc/rtc-lpc32xx.c @@ -0,0 +1,388 @@ +/* + * 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 0; +} + +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) { + 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 lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->irq >= 0) { + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + else + disable_irq_wake(rtc->irq); + } + + return 0; +} + +static int lpc32xx_rtc_resume(struct device *dev) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->irq >= 0 && device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + + return 0; +} + +/* Unconditionally disable the alarm */ +static int lpc32xx_rtc_freeze(struct device *dev) +{ + struct lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + 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 lpc32xx_rtc *rtc = dev_get_drvdata(dev); + + 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..f4c248655 --- /dev/null +++ b/drivers/rtc/rtc-ls1x.c @@ -0,0 +1,196 @@ +/* + * 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 <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; + time64_t t; + + v = readl(SYS_TOYREAD0); + t = readl(SYS_TOYREAD1); + + memset(rtm, 0, sizeof(struct rtc_time)); + t = mktime64((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_time64_to_tm(t, rtm); + + return 0; +} + +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 const 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; + + v = readl(SYS_COUNTER_CNTRL); + if (!(v & RTC_CNTR_OK)) { + dev_err(&pdev->dev, "rtc counters not working\n"); + return -ENODEV; + } + + /* 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"); + return -ETIMEDOUT; + } + 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_allocate_device(&pdev->dev); + if (IS_ERR(rtcdev)) + return PTR_ERR(rtcdev); + + platform_set_drvdata(pdev, rtcdev); + rtcdev->ops = &ls1x_rtc_ops; + rtcdev->range_min = RTC_TIMESTAMP_BEGIN_1900; + rtcdev->range_max = RTC_TIMESTAMP_END_2099; + + return rtc_register_device(rtcdev); +} + +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..5808a1e4c --- /dev/null +++ b/drivers/rtc/rtc-m41t80.c @@ -0,0 +1,1022 @@ +/* + * 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/clk-provider.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.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 0x00 +#define M41T80_REG_SEC 0x01 +#define M41T80_REG_MIN 0x02 +#define M41T80_REG_HOUR 0x03 +#define M41T80_REG_WDAY 0x04 +#define M41T80_REG_DAY 0x05 +#define M41T80_REG_MON 0x06 +#define M41T80_REG_YEAR 0x07 +#define M41T80_REG_ALARM_MON 0x0a +#define M41T80_REG_ALARM_DAY 0x0b +#define M41T80_REG_ALARM_HOUR 0x0c +#define M41T80_REG_ALARM_MIN 0x0d +#define M41T80_REG_ALARM_SEC 0x0e +#define M41T80_REG_FLAGS 0x0f +#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_SQW_MAX_FREQ 32768 + +#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */ +#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */ +#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */ +#define M41T80_ALHOUR_HT BIT(6) /* HT: Halt Update Bit */ +#define M41T80_FLAGS_OF BIT(2) /* OF: Oscillator Failure Bit */ +#define M41T80_FLAGS_AF BIT(6) /* AF: Alarm Flag Bit */ +#define M41T80_FLAGS_BATT_LOW BIT(4) /* BL: Battery Low Bit */ +#define M41T80_WATCHDOG_RB2 BIT(7) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB1 BIT(1) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB0 BIT(0) /* RB: Watchdog resolution */ + +#define M41T80_FEATURE_HT BIT(0) /* Halt feature */ +#define M41T80_FEATURE_BL BIT(1) /* Battery low indicator */ +#define M41T80_FEATURE_SQ BIT(2) /* Squarewave feature */ +#define M41T80_FEATURE_WD BIT(3) /* Extra watchdog resolution */ +#define M41T80_FEATURE_SQ_ALT BIT(4) /* RSx bits are in reg 4 */ + +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); + +static const struct of_device_id m41t80_of_match[] = { + { + .compatible = "st,m41t62", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT) + }, + { + .compatible = "st,m41t65", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_WD) + }, + { + .compatible = "st,m41t80", + .data = (void *)(M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t81", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t81s", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t82", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t83", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t84", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t85", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "st,m41t87", + .data = (void *)(M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ) + }, + { + .compatible = "microcrystal,rv4162", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT) + }, + /* DT compatibility only, do not use compatibles below: */ + { + .compatible = "st,rv4162", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT) + }, + { + .compatible = "rv4162", + .data = (void *)(M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT) + }, + { } +}; +MODULE_DEVICE_TABLE(of, m41t80_of_match); + +struct m41t80_data { + unsigned long features; + struct i2c_client *client; + struct rtc_device *rtc; +#ifdef CONFIG_COMMON_CLK + struct clk_hw sqw; + unsigned long freq; + unsigned int sqwe; +#endif +}; + +static irqreturn_t m41t80_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct m41t80_data *m41t80 = i2c_get_clientdata(client); + struct mutex *lock = &m41t80->rtc->ops_lock; + unsigned long events = 0; + int flags, flags_afe; + + mutex_lock(lock); + + flags_afe = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (flags_afe < 0) { + mutex_unlock(lock); + return IRQ_NONE; + } + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags <= 0) { + mutex_unlock(lock); + return IRQ_NONE; + } + + if (flags & M41T80_FLAGS_AF) { + flags &= ~M41T80_FLAGS_AF; + flags_afe &= ~M41T80_ALMON_AFE; + events |= RTC_AF; + } + + if (events) { + rtc_update_irq(m41t80->rtc, 1, events); + i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags); + i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + flags_afe); + } + + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf[8]; + int err, flags; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + + if (flags & M41T80_FLAGS_OF) { + dev_err(&client->dev, "Oscillator failure, data is invalid.\n"); + return -EINVAL; + } + + err = i2c_smbus_read_i2c_block_data(client, M41T80_REG_SSEC, + sizeof(buf), buf); + if (err < 0) { + dev_err(&client->dev, "Unable to read date\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 0; +} + +static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct m41t80_data *clientdata = i2c_get_clientdata(client); + unsigned char buf[8]; + int err, flags; + + if (tm->tm_year < 100 || tm->tm_year > 199) + return -EINVAL; + + buf[M41T80_REG_SSEC] = 0; + buf[M41T80_REG_SEC] = bin2bcd(tm->tm_sec); + buf[M41T80_REG_MIN] = bin2bcd(tm->tm_min); + buf[M41T80_REG_HOUR] = bin2bcd(tm->tm_hour); + buf[M41T80_REG_DAY] = bin2bcd(tm->tm_mday); + buf[M41T80_REG_MON] = bin2bcd(tm->tm_mon + 1); + buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100); + buf[M41T80_REG_WDAY] = tm->tm_wday; + + /* If the square wave output is controlled in the weekday register */ + if (clientdata->features & M41T80_FEATURE_SQ_ALT) { + int val; + + val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY); + if (val < 0) + return val; + + buf[M41T80_REG_WDAY] |= (val & 0xf0); + } + + err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC, + sizeof(buf), buf); + if (err < 0) { + dev_err(&client->dev, "Unable to write to date registers\n"); + return err; + } + + /* Clear the OF bit of Flags Register */ + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + + if (i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, + flags & ~M41T80_FLAGS_OF)) { + dev_err(&client->dev, "Unable to write flags register\n"); + return -EIO; + } + + return err; +} + +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; +} + +static int m41t80_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + int flags, retval; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (flags < 0) + return flags; + + if (enabled) + flags |= M41T80_ALMON_AFE; + else + flags &= ~M41T80_ALMON_AFE; + + retval = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, flags); + if (retval < 0) { + dev_err(dev, "Unable to enable alarm IRQ %d\n", retval); + return retval; + } + return 0; +} + +static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 alarmvals[5]; + int ret, err; + + alarmvals[0] = bin2bcd(alrm->time.tm_mon + 1); + alarmvals[1] = bin2bcd(alrm->time.tm_mday); + alarmvals[2] = bin2bcd(alrm->time.tm_hour); + alarmvals[3] = bin2bcd(alrm->time.tm_min); + alarmvals[4] = bin2bcd(alrm->time.tm_sec); + + /* Clear AF and AFE flags */ + ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (ret < 0) + return ret; + err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + ret & ~(M41T80_ALMON_AFE)); + if (err < 0) { + dev_err(dev, "Unable to clear AFE bit\n"); + return err; + } + + /* Keep SQWE bit value */ + alarmvals[0] |= (ret & M41T80_ALMON_SQWE); + + ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (ret < 0) + return ret; + + err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, + ret & ~(M41T80_FLAGS_AF)); + if (err < 0) { + dev_err(dev, "Unable to clear AF bit\n"); + return err; + } + + /* Write the alarm */ + err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_ALARM_MON, + 5, alarmvals); + if (err) + return err; + + /* Enable the alarm interrupt */ + if (alrm->enabled) { + alarmvals[0] |= M41T80_ALMON_AFE; + err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + alarmvals[0]); + if (err) + return err; + } + + return 0; +} + +static int m41t80_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 alarmvals[5]; + int flags, ret; + + ret = i2c_smbus_read_i2c_block_data(client, M41T80_REG_ALARM_MON, + 5, alarmvals); + if (ret != 5) + return ret < 0 ? ret : -EIO; + + flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); + if (flags < 0) + return flags; + + alrm->time.tm_sec = bcd2bin(alarmvals[4] & 0x7f); + alrm->time.tm_min = bcd2bin(alarmvals[3] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[2] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mon = bcd2bin(alarmvals[0] & 0x3f) - 1; + + alrm->enabled = !!(alarmvals[0] & M41T80_ALMON_AFE); + alrm->pending = (flags & M41T80_FLAGS_AF) && alrm->enabled; + + return 0; +} + +static struct rtc_class_ops m41t80_rtc_ops = { + .read_time = m41t80_rtc_read_time, + .set_time = m41t80_rtc_set_time, + .proc = m41t80_rtc_proc, +}; + +#ifdef CONFIG_PM_SLEEP +static int m41t80_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq >= 0 && device_may_wakeup(dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int m41t80_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client->irq >= 0 && device_may_wakeup(dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume); + +#ifdef CONFIG_COMMON_CLK +#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw) + +static unsigned long m41t80_decode_freq(int setting) +{ + return (setting == 0) ? 0 : (setting == 1) ? M41T80_SQW_MAX_FREQ : + M41T80_SQW_MAX_FREQ >> setting; +} + +static unsigned long m41t80_get_freq(struct m41t80_data *m41t80) +{ + struct i2c_client *client = m41t80->client; + int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? + M41T80_REG_WDAY : M41T80_REG_SQW; + int ret = i2c_smbus_read_byte_data(client, reg_sqw); + + if (ret < 0) + return 0; + return m41t80_decode_freq(ret >> 4); +} + +static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return sqw_to_m41t80_data(hw)->freq; +} + +static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + if (rate >= M41T80_SQW_MAX_FREQ) + return M41T80_SQW_MAX_FREQ; + if (rate >= M41T80_SQW_MAX_FREQ / 4) + return M41T80_SQW_MAX_FREQ / 4; + if (!rate) + return 0; + return 1 << ilog2(rate); +} + +static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ? + M41T80_REG_WDAY : M41T80_REG_SQW; + int reg, ret, val = 0; + + if (rate >= M41T80_SQW_MAX_FREQ) + val = 1; + else if (rate >= M41T80_SQW_MAX_FREQ / 4) + val = 2; + else if (rate) + val = 15 - ilog2(rate); + + reg = i2c_smbus_read_byte_data(client, reg_sqw); + if (reg < 0) + return reg; + + reg = (reg & 0x0f) | (val << 4); + + ret = i2c_smbus_write_byte_data(client, reg_sqw, reg); + if (!ret) + m41t80->freq = m41t80_decode_freq(val); + return ret; +} + +static int m41t80_sqw_control(struct clk_hw *hw, bool enable) +{ + struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw); + struct i2c_client *client = m41t80->client; + int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + + if (ret < 0) + return ret; + + if (enable) + ret |= M41T80_ALMON_SQWE; + else + ret &= ~M41T80_ALMON_SQWE; + + ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret); + if (!ret) + m41t80->sqwe = enable; + return ret; +} + +static int m41t80_sqw_prepare(struct clk_hw *hw) +{ + return m41t80_sqw_control(hw, 1); +} + +static void m41t80_sqw_unprepare(struct clk_hw *hw) +{ + m41t80_sqw_control(hw, 0); +} + +static int m41t80_sqw_is_prepared(struct clk_hw *hw) +{ + return sqw_to_m41t80_data(hw)->sqwe; +} + +static const struct clk_ops m41t80_sqw_ops = { + .prepare = m41t80_sqw_prepare, + .unprepare = m41t80_sqw_unprepare, + .is_prepared = m41t80_sqw_is_prepared, + .recalc_rate = m41t80_sqw_recalc_rate, + .round_rate = m41t80_sqw_round_rate, + .set_rate = m41t80_sqw_set_rate, +}; + +static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80) +{ + struct i2c_client *client = m41t80->client; + struct device_node *node = client->dev.of_node; + struct clk *clk; + struct clk_init_data init; + int ret; + + /* First disable the clock */ + ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON); + if (ret < 0) + return ERR_PTR(ret); + ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, + ret & ~(M41T80_ALMON_SQWE)); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "m41t80-sqw"; + init.ops = &m41t80_sqw_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + m41t80->sqw.init = &init; + m41t80->freq = m41t80_get_freq(m41t80); + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = clk_register(&client->dev, &m41t80->sqw); + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +#ifdef CONFIG_RTC_DRV_M41T80_WDT +/* + ***************************************************************************** + * + * Watchdog Driver + * + ***************************************************************************** + */ +static DEFINE_MUTEX(m41t80_rtc_mutex); +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) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int rc = 0; + struct rtc_time tm; + struct m41t80_data *m41t80_data = NULL; + bool wakeup_source = false; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&adapter->dev, "doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n"); + return -ENODEV; + } + + m41t80_data = devm_kzalloc(&client->dev, sizeof(*m41t80_data), + GFP_KERNEL); + if (!m41t80_data) + return -ENOMEM; + + m41t80_data->client = client; + if (client->dev.of_node) + m41t80_data->features = (unsigned long) + of_device_get_match_data(&client->dev); + else + m41t80_data->features = id->driver_data; + i2c_set_clientdata(client, m41t80_data); + + m41t80_data->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(m41t80_data->rtc)) + return PTR_ERR(m41t80_data->rtc); + +#ifdef CONFIG_OF + wakeup_source = of_property_read_bool(client->dev.of_node, + "wakeup-source"); +#endif + if (client->irq > 0) { + rc = devm_request_threaded_irq(&client->dev, client->irq, + NULL, m41t80_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "m41t80", client); + if (rc) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + wakeup_source = false; + } + } + if (client->irq > 0 || wakeup_source) { + m41t80_rtc_ops.read_alarm = m41t80_read_alarm; + m41t80_rtc_ops.set_alarm = m41t80_set_alarm; + m41t80_rtc_ops.alarm_irq_enable = m41t80_alarm_irq_enable; + /* Enable the wakealarm */ + device_init_wakeup(&client->dev, true); + } + + m41t80_data->rtc->ops = &m41t80_rtc_ops; + + if (client->irq <= 0) { + /* We cannot support UIE mode if we do not have an IRQ line */ + m41t80_data->rtc->uie_unsupported = 1; + } + + /* 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 (m41t80_data->features & M41T80_FEATURE_HT) { + m41t80_rtc_read_time(&client->dev, &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; + } + +#ifdef CONFIG_RTC_DRV_M41T80_WDT + if (m41t80_data->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 +#ifdef CONFIG_COMMON_CLK + if (m41t80_data->features & M41T80_FEATURE_SQ) + m41t80_sqw_register_clk(m41t80_data); +#endif + + rc = rtc_register_device(m41t80_data->rtc); + if (rc) + return rc; + + 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", + .of_match_table = of_match_ptr(m41t80_of_match), + .pm = &m41t80_pm, + }, + .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..4a08a9dab --- /dev/null +++ b/drivers/rtc/rtc-m41t93.c @@ -0,0 +1,209 @@ +/* + * + * 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; +} + + +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", + }, + .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..bab82b4be --- /dev/null +++ b/drivers/rtc/rtc-m41t94.c @@ -0,0 +1,148 @@ +/* + * 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); + + return 0; +} + +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", + }, + .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..0cf6507de --- /dev/null +++ b/drivers/rtc/rtc-m48t35.c @@ -0,0 +1,191 @@ +/* + * 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> + +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 0; +} + +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_ALIAS("platform:rtc-m48t35"); diff --git a/drivers/rtc/rtc-m48t59.c b/drivers/rtc/rtc-m48t59.c new file mode 100644 index 000000000..ac9ca1042 --- /dev/null +++ b/drivers/rtc/rtc-m48t59.c @@ -0,0 +1,502 @@ +/* + * 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 m48t59_private *m48t59 = dev_get_drvdata(dev); + + writeb(val, m48t59->ioaddr+ofs); +} + +static u8 +m48t59_mem_readb(struct device *dev, u32 ofs) +{ + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + + 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 m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 0; +} + +static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 m48t59_plat_data *pdata = dev_get_platdata(dev); + struct m48t59_private *m48t59 = dev_get_drvdata(dev); + 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 int m48t59_nvram_read(void *priv, unsigned int offset, void *val, + size_t size) +{ + struct platform_device *pdev = priv; + struct device *dev = &pdev->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; + u8 *buf = val; + + spin_lock_irqsave(&m48t59->lock, flags); + + for (; cnt < size; cnt++) + *buf++ = M48T59_READ(cnt); + + spin_unlock_irqrestore(&m48t59->lock, flags); + + return 0; +} + +static int m48t59_nvram_write(void *priv, unsigned int offset, void *val, + size_t size) +{ + struct platform_device *pdev = priv; + struct device *dev = &pdev->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; + u8 *buf = val; + + spin_lock_irqsave(&m48t59->lock, flags); + + for (; cnt < size; cnt++) + M48T59_WRITE(*buf++, cnt); + + spin_unlock_irqrestore(&m48t59->lock, flags); + + return 0; +} + +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; + const struct rtc_class_ops *ops; + struct nvmem_config nvmem_cfg = { + .name = "m48t59-", + .word_size = 1, + .stride = 1, + .reg_read = m48t59_nvram_read, + .reg_write = m48t59_nvram_write, + .priv = pdev, + }; + + /* 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: + ops = &m48t59_rtc_ops; + pdata->offset = 0x1ff0; + break; + case M48T59RTC_TYPE_M48T02: + ops = &m48t02_rtc_ops; + pdata->offset = 0x7f0; + break; + case M48T59RTC_TYPE_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_allocate_device(&pdev->dev); + if (IS_ERR(m48t59->rtc)) + return PTR_ERR(m48t59->rtc); + + m48t59->rtc->nvram_old_abi = true; + m48t59->rtc->ops = ops; + + nvmem_cfg.size = pdata->offset; + ret = rtc_nvmem_register(m48t59->rtc, &nvmem_cfg); + if (ret) + return ret; + + ret = rtc_register_device(m48t59->rtc); + if (ret) + return ret; + + 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, +}; + +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..a9533535c --- /dev/null +++ b/drivers/rtc/rtc-m48t86.c @@ -0,0 +1,295 @@ +/* + * 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/bcd.h> +#include <linux/io.h> + +#define M48T86_SEC 0x00 +#define M48T86_SECALRM 0x01 +#define M48T86_MIN 0x02 +#define M48T86_MINALRM 0x03 +#define M48T86_HOUR 0x04 +#define M48T86_HOURALRM 0x05 +#define M48T86_DOW 0x06 /* 1 = sunday */ +#define M48T86_DOM 0x07 +#define M48T86_MONTH 0x08 /* 1 - 12 */ +#define M48T86_YEAR 0x09 /* 0 - 99 */ +#define M48T86_A 0x0a +#define M48T86_B 0x0b +#define M48T86_B_SET BIT(7) +#define M48T86_B_DM BIT(2) +#define M48T86_B_H24 BIT(1) +#define M48T86_C 0x0c +#define M48T86_D 0x0d +#define M48T86_D_VRT BIT(7) +#define M48T86_NVRAM(x) (0x0e + (x)) +#define M48T86_NVRAM_LEN 114 + +struct m48t86_rtc_info { + void __iomem *index_reg; + void __iomem *data_reg; + struct rtc_device *rtc; +}; + +static unsigned char m48t86_readb(struct device *dev, unsigned long addr) +{ + struct m48t86_rtc_info *info = dev_get_drvdata(dev); + unsigned char value; + + writeb(addr, info->index_reg); + value = readb(info->data_reg); + + return value; +} + +static void m48t86_writeb(struct device *dev, + unsigned char value, unsigned long addr) +{ + struct m48t86_rtc_info *info = dev_get_drvdata(dev); + + writeb(addr, info->index_reg); + writeb(value, info->data_reg); +} + +static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char reg; + + reg = m48t86_readb(dev, M48T86_B); + + if (reg & M48T86_B_DM) { + /* data (binary) mode */ + tm->tm_sec = m48t86_readb(dev, M48T86_SEC); + tm->tm_min = m48t86_readb(dev, M48T86_MIN); + tm->tm_hour = m48t86_readb(dev, M48T86_HOUR) & 0x3f; + tm->tm_mday = m48t86_readb(dev, M48T86_DOM); + /* tm_mon is 0-11 */ + tm->tm_mon = m48t86_readb(dev, M48T86_MONTH) - 1; + tm->tm_year = m48t86_readb(dev, M48T86_YEAR) + 100; + tm->tm_wday = m48t86_readb(dev, M48T86_DOW); + } else { + /* bcd mode */ + tm->tm_sec = bcd2bin(m48t86_readb(dev, M48T86_SEC)); + tm->tm_min = bcd2bin(m48t86_readb(dev, M48T86_MIN)); + tm->tm_hour = bcd2bin(m48t86_readb(dev, M48T86_HOUR) & + 0x3f); + tm->tm_mday = bcd2bin(m48t86_readb(dev, M48T86_DOM)); + /* tm_mon is 0-11 */ + tm->tm_mon = bcd2bin(m48t86_readb(dev, M48T86_MONTH)) - 1; + tm->tm_year = bcd2bin(m48t86_readb(dev, M48T86_YEAR)) + 100; + tm->tm_wday = bcd2bin(m48t86_readb(dev, M48T86_DOW)); + } + + /* correct the hour if the clock is in 12h mode */ + if (!(reg & M48T86_B_H24)) + if (m48t86_readb(dev, M48T86_HOUR) & 0x80) + tm->tm_hour += 12; + + return 0; +} + +static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + unsigned char reg; + + reg = m48t86_readb(dev, M48T86_B); + + /* update flag and 24h mode */ + reg |= M48T86_B_SET | M48T86_B_H24; + m48t86_writeb(dev, reg, M48T86_B); + + if (reg & M48T86_B_DM) { + /* data (binary) mode */ + m48t86_writeb(dev, tm->tm_sec, M48T86_SEC); + m48t86_writeb(dev, tm->tm_min, M48T86_MIN); + m48t86_writeb(dev, tm->tm_hour, M48T86_HOUR); + m48t86_writeb(dev, tm->tm_mday, M48T86_DOM); + m48t86_writeb(dev, tm->tm_mon + 1, M48T86_MONTH); + m48t86_writeb(dev, tm->tm_year % 100, M48T86_YEAR); + m48t86_writeb(dev, tm->tm_wday, M48T86_DOW); + } else { + /* bcd mode */ + m48t86_writeb(dev, bin2bcd(tm->tm_sec), M48T86_SEC); + m48t86_writeb(dev, bin2bcd(tm->tm_min), M48T86_MIN); + m48t86_writeb(dev, bin2bcd(tm->tm_hour), M48T86_HOUR); + m48t86_writeb(dev, bin2bcd(tm->tm_mday), M48T86_DOM); + m48t86_writeb(dev, bin2bcd(tm->tm_mon + 1), M48T86_MONTH); + m48t86_writeb(dev, bin2bcd(tm->tm_year % 100), M48T86_YEAR); + m48t86_writeb(dev, bin2bcd(tm->tm_wday), M48T86_DOW); + } + + /* update ended */ + reg &= ~M48T86_B_SET; + m48t86_writeb(dev, reg, M48T86_B); + + return 0; +} + +static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq) +{ + unsigned char reg; + + reg = m48t86_readb(dev, M48T86_B); + + seq_printf(seq, "mode\t\t: %s\n", + (reg & M48T86_B_DM) ? "binary" : "bcd"); + + reg = m48t86_readb(dev, M48T86_D); + + seq_printf(seq, "battery\t\t: %s\n", + (reg & M48T86_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_nvram_read(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct device *dev = priv; + unsigned int i; + + for (i = 0; i < count; i++) + ((u8 *)buf)[i] = m48t86_readb(dev, M48T86_NVRAM(off + i)); + + return 0; +} + +static int m48t86_nvram_write(void *priv, unsigned int off, void *buf, + size_t count) +{ + struct device *dev = priv; + unsigned int i; + + for (i = 0; i < count; i++) + m48t86_writeb(dev, ((u8 *)buf)[i], M48T86_NVRAM(off + i)); + + return 0; +} + +/* + * The RTC is an optional feature at purchase time on some Technologic Systems + * boards. Verify that it actually exists by checking if the last two bytes + * of the NVRAM can be changed. + * + * This is based on the method used in their rtc7800.c example. + */ +static bool m48t86_verify_chip(struct platform_device *pdev) +{ + unsigned int offset0 = M48T86_NVRAM(M48T86_NVRAM_LEN - 2); + unsigned int offset1 = M48T86_NVRAM(M48T86_NVRAM_LEN - 1); + unsigned char tmp0, tmp1; + + tmp0 = m48t86_readb(&pdev->dev, offset0); + tmp1 = m48t86_readb(&pdev->dev, offset1); + + m48t86_writeb(&pdev->dev, 0x00, offset0); + m48t86_writeb(&pdev->dev, 0x55, offset1); + if (m48t86_readb(&pdev->dev, offset1) == 0x55) { + m48t86_writeb(&pdev->dev, 0xaa, offset1); + if (m48t86_readb(&pdev->dev, offset1) == 0xaa && + m48t86_readb(&pdev->dev, offset0) == 0x00) { + m48t86_writeb(&pdev->dev, tmp0, offset0); + m48t86_writeb(&pdev->dev, tmp1, offset1); + + return true; + } + } + return false; +} + +static int m48t86_rtc_probe(struct platform_device *pdev) +{ + struct m48t86_rtc_info *info; + struct resource *res; + unsigned char reg; + int err; + struct nvmem_config m48t86_nvmem_cfg = { + .name = "m48t86_nvram", + .word_size = 1, + .stride = 1, + .size = M48T86_NVRAM_LEN, + .reg_read = m48t86_nvram_read, + .reg_write = m48t86_nvram_write, + .priv = &pdev->dev, + }; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + info->index_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->index_reg)) + return PTR_ERR(info->index_reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + info->data_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(info->data_reg)) + return PTR_ERR(info->data_reg); + + dev_set_drvdata(&pdev->dev, info); + + if (!m48t86_verify_chip(pdev)) { + dev_info(&pdev->dev, "RTC not present\n"); + return -ENODEV; + } + + info->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); + + info->rtc->ops = &m48t86_rtc_ops; + info->rtc->nvram_old_abi = true; + + err = rtc_register_device(info->rtc); + if (err) + return err; + + rtc_nvmem_register(info->rtc, &m48t86_nvmem_cfg); + + /* read battery status */ + reg = m48t86_readb(&pdev->dev, M48T86_D); + dev_info(&pdev->dev, "battery %s\n", + (reg & M48T86_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_ALIAS("platform:rtc-m48t86"); diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c new file mode 100644 index 000000000..ab60f13fa --- /dev/null +++ b/drivers/rtc/rtc-max6900.c @@ -0,0 +1,238 @@ +/* + * 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> + +/* + * 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_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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 0; +} + +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_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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 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; + + 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 const struct i2c_device_id max6900_id[] = { + { "max6900", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max6900_id); + +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"); diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c new file mode 100644 index 000000000..745827463 --- /dev/null +++ b/drivers/rtc/rtc-max6902.c @@ -0,0 +1,158 @@ +/* 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 0; +} + +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", + }, + .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-max6916.c b/drivers/rtc/rtc-max6916.c new file mode 100644 index 000000000..7e908a490 --- /dev/null +++ b/drivers/rtc/rtc-max6916.c @@ -0,0 +1,164 @@ +/* rtc-max6916.c + * + * Driver for MAXIM max6916 Low Current, SPI Compatible + * Real Time Clock + * + * Author : Venkat Prashanth B U <venkat.prashanth2498@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 max6916 rtc */ + +#define MAX6916_SECONDS_REG 0x01 +#define MAX6916_MINUTES_REG 0x02 +#define MAX6916_HOURS_REG 0x03 +#define MAX6916_DATE_REG 0x04 +#define MAX6916_MONTH_REG 0x05 +#define MAX6916_DAY_REG 0x06 +#define MAX6916_YEAR_REG 0x07 +#define MAX6916_CONTROL_REG 0x08 +#define MAX6916_STATUS_REG 0x0C +#define MAX6916_CLOCK_BURST 0x3F + +static int max6916_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 max6916_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 max6916_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] = MAX6916_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 0; +} + +static int max6916_set_time(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[9]; + + if (dt->tm_year < 100 || dt->tm_year > 199) { + dev_err(&spi->dev, "Year must be between 2000 and 2099. It's %d.\n", + dt->tm_year + 1900); + return -EINVAL; + } + + buf[0] = MAX6916_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); + buf[7] = bin2bcd(dt->tm_year % 100); + buf[8] = bin2bcd(0x00); + + /* write the rtc settings */ + return spi_write_then_read(spi, buf, 9, NULL, 0); +} + +static const struct rtc_class_ops max6916_rtc_ops = { + .read_time = max6916_read_time, + .set_time = max6916_set_time, +}; + +static int max6916_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char data; + int res; + + /* spi setup with max6916 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 = max6916_read_reg(&spi->dev, MAX6916_SECONDS_REG, &data); + if (res) + return res; + + /* Disable the write protect of rtc */ + max6916_read_reg(&spi->dev, MAX6916_CONTROL_REG, &data); + data = data & ~(1 << 7); + max6916_write_reg(&spi->dev, MAX6916_CONTROL_REG, data); + + /*Enable oscillator,disable oscillator stop flag, glitch filter*/ + max6916_read_reg(&spi->dev, MAX6916_STATUS_REG, &data); + data = data & 0x1B; + max6916_write_reg(&spi->dev, MAX6916_STATUS_REG, data); + + /* display the settings */ + max6916_read_reg(&spi->dev, MAX6916_CONTROL_REG, &data); + dev_info(&spi->dev, "MAX6916 RTC CTRL Reg = 0x%02x\n", data); + + max6916_read_reg(&spi->dev, MAX6916_STATUS_REG, &data); + dev_info(&spi->dev, "MAX6916 RTC Status Reg = 0x%02x\n", data); + + rtc = devm_rtc_device_register(&spi->dev, "max6916", + &max6916_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + spi_set_drvdata(spi, rtc); + + return 0; +} + +static struct spi_driver max6916_driver = { + .driver = { + .name = "max6916", + }, + .probe = max6916_probe, +}; +module_spi_driver(max6916_driver); + +MODULE_DESCRIPTION("MAX6916 SPI RTC DRIVER"); +MODULE_AUTHOR("Venkat Prashanth B U <venkat.prashanth2498@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c new file mode 100644 index 000000000..8e09450d1 --- /dev/null +++ b/drivers/rtc/rtc-max77686.c @@ -0,0 +1,856 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// RTC driver for Maxim MAX77686 and MAX77802 +// +// Copyright (C) 2012 Samsung Electronics Co.Ltd +// +// based on rtc-max8997.c + +#include <linux/i2c.h> +#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> + +#define MAX77686_I2C_ADDR_RTC (0x0C >> 1) +#define MAX77620_I2C_ADDR_RTC 0x68 +#define MAX77686_INVALID_I2C_ADDR (-1) + +/* Define non existing register */ +#define MAX77686_INVALID_REG (-1) + +/* RTC Control Register */ +#define BCD_EN_SHIFT 0 +#define BCD_EN_MASK BIT(BCD_EN_SHIFT) +#define MODEL24_SHIFT 1 +#define MODEL24_MASK BIT(MODEL24_SHIFT) +/* RTC Update Register1 */ +#define RTC_UDR_SHIFT 0 +#define RTC_UDR_MASK BIT(RTC_UDR_SHIFT) +#define RTC_RBUDR_SHIFT 4 +#define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT) +/* RTC Hour register */ +#define HOUR_PM_SHIFT 6 +#define HOUR_PM_MASK BIT(HOUR_PM_SHIFT) +/* RTC Alarm Enable */ +#define ALARM_ENABLE_SHIFT 7 +#define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT) + +#define REG_RTC_NONE 0xdeadbeef + +/* + * MAX77802 has separate register (RTCAE1) for alarm enable instead + * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE} + * as in done in MAX77686. + */ +#define MAX77802_ALARM_ENABLE_VALUE 0x77 + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_MONTH, + RTC_YEAR, + RTC_DATE, + RTC_NR_TIME +}; + +struct max77686_rtc_driver_data { + /* Minimum usecs needed for a RTC update */ + unsigned long delay; + /* Mask used to read RTC registers value */ + u8 mask; + /* Registers offset to I2C addresses map */ + const unsigned int *map; + /* Has a separate alarm enable register? */ + bool alarm_enable_reg; + /* I2C address for RTC block */ + int rtc_i2c_addr; + /* RTC interrupt via platform resource */ + bool rtc_irq_from_platform; + /* Pending alarm status register */ + int alarm_pending_status_reg; + /* RTC IRQ CHIP for regmap */ + const struct regmap_irq_chip *rtc_irq_chip; +}; + +struct max77686_rtc_info { + struct device *dev; + struct i2c_client *rtc; + struct rtc_device *rtc_dev; + struct mutex lock; + + struct regmap *regmap; + struct regmap *rtc_regmap; + + const struct max77686_rtc_driver_data *drv_data; + struct regmap_irq_chip_data *rtc_irq_data; + + int rtc_irq; + int virq; + int rtc_24hr_mode; +}; + +enum MAX77686_RTC_OP { + MAX77686_RTC_WRITE, + MAX77686_RTC_READ, +}; + +/* These are not registers but just offsets that are mapped to addresses */ +enum max77686_rtc_reg_offset { + REG_RTC_CONTROLM = 0, + REG_RTC_CONTROL, + REG_RTC_UPDATE0, + REG_WTSR_SMPL_CNTL, + REG_RTC_SEC, + REG_RTC_MIN, + REG_RTC_HOUR, + REG_RTC_WEEKDAY, + REG_RTC_MONTH, + REG_RTC_YEAR, + REG_RTC_DATE, + REG_ALARM1_SEC, + REG_ALARM1_MIN, + REG_ALARM1_HOUR, + REG_ALARM1_WEEKDAY, + REG_ALARM1_MONTH, + REG_ALARM1_YEAR, + REG_ALARM1_DATE, + REG_ALARM2_SEC, + REG_ALARM2_MIN, + REG_ALARM2_HOUR, + REG_ALARM2_WEEKDAY, + REG_ALARM2_MONTH, + REG_ALARM2_YEAR, + REG_ALARM2_DATE, + REG_RTC_AE1, + REG_RTC_END, +}; + +/* Maps RTC registers offset to the MAX77686 register addresses */ +static const unsigned int max77686_map[REG_RTC_END] = { + [REG_RTC_CONTROLM] = MAX77686_RTC_CONTROLM, + [REG_RTC_CONTROL] = MAX77686_RTC_CONTROL, + [REG_RTC_UPDATE0] = MAX77686_RTC_UPDATE0, + [REG_WTSR_SMPL_CNTL] = MAX77686_WTSR_SMPL_CNTL, + [REG_RTC_SEC] = MAX77686_RTC_SEC, + [REG_RTC_MIN] = MAX77686_RTC_MIN, + [REG_RTC_HOUR] = MAX77686_RTC_HOUR, + [REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY, + [REG_RTC_MONTH] = MAX77686_RTC_MONTH, + [REG_RTC_YEAR] = MAX77686_RTC_YEAR, + [REG_RTC_DATE] = MAX77686_RTC_DATE, + [REG_ALARM1_SEC] = MAX77686_ALARM1_SEC, + [REG_ALARM1_MIN] = MAX77686_ALARM1_MIN, + [REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR, + [REG_ALARM1_WEEKDAY] = MAX77686_ALARM1_WEEKDAY, + [REG_ALARM1_MONTH] = MAX77686_ALARM1_MONTH, + [REG_ALARM1_YEAR] = MAX77686_ALARM1_YEAR, + [REG_ALARM1_DATE] = MAX77686_ALARM1_DATE, + [REG_ALARM2_SEC] = MAX77686_ALARM2_SEC, + [REG_ALARM2_MIN] = MAX77686_ALARM2_MIN, + [REG_ALARM2_HOUR] = MAX77686_ALARM2_HOUR, + [REG_ALARM2_WEEKDAY] = MAX77686_ALARM2_WEEKDAY, + [REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH, + [REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR, + [REG_ALARM2_DATE] = MAX77686_ALARM2_DATE, + [REG_RTC_AE1] = REG_RTC_NONE, +}; + +static const struct regmap_irq max77686_rtc_irqs[] = { + /* RTC interrupts */ + REGMAP_IRQ_REG(0, 0, MAX77686_RTCINT_RTC60S_MSK), + REGMAP_IRQ_REG(1, 0, MAX77686_RTCINT_RTCA1_MSK), + REGMAP_IRQ_REG(2, 0, MAX77686_RTCINT_RTCA2_MSK), + REGMAP_IRQ_REG(3, 0, MAX77686_RTCINT_SMPL_MSK), + REGMAP_IRQ_REG(4, 0, MAX77686_RTCINT_RTC1S_MSK), + REGMAP_IRQ_REG(5, 0, MAX77686_RTCINT_WTSR_MSK), +}; + +static const struct regmap_irq_chip max77686_rtc_irq_chip = { + .name = "max77686-rtc", + .status_base = MAX77686_RTC_INT, + .mask_base = MAX77686_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), +}; + +static const struct max77686_rtc_driver_data max77686_drv_data = { + .delay = 16000, + .mask = 0x7f, + .map = max77686_map, + .alarm_enable_reg = false, + .rtc_irq_from_platform = false, + .alarm_pending_status_reg = MAX77686_REG_STATUS2, + .rtc_i2c_addr = MAX77686_I2C_ADDR_RTC, + .rtc_irq_chip = &max77686_rtc_irq_chip, +}; + +static const struct max77686_rtc_driver_data max77620_drv_data = { + .delay = 16000, + .mask = 0x7f, + .map = max77686_map, + .alarm_enable_reg = false, + .rtc_irq_from_platform = true, + .alarm_pending_status_reg = MAX77686_INVALID_REG, + .rtc_i2c_addr = MAX77620_I2C_ADDR_RTC, + .rtc_irq_chip = &max77686_rtc_irq_chip, +}; + +static const unsigned int max77802_map[REG_RTC_END] = { + [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM, + [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL, + [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0, + [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL, + [REG_RTC_SEC] = MAX77802_RTC_SEC, + [REG_RTC_MIN] = MAX77802_RTC_MIN, + [REG_RTC_HOUR] = MAX77802_RTC_HOUR, + [REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY, + [REG_RTC_MONTH] = MAX77802_RTC_MONTH, + [REG_RTC_YEAR] = MAX77802_RTC_YEAR, + [REG_RTC_DATE] = MAX77802_RTC_DATE, + [REG_ALARM1_SEC] = MAX77802_ALARM1_SEC, + [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN, + [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR, + [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY, + [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH, + [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR, + [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE, + [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC, + [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN, + [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR, + [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY, + [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH, + [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR, + [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE, + [REG_RTC_AE1] = MAX77802_RTC_AE1, +}; + +static const struct regmap_irq_chip max77802_rtc_irq_chip = { + .name = "max77802-rtc", + .status_base = MAX77802_RTC_INT, + .mask_base = MAX77802_RTC_INTM, + .num_regs = 1, + .irqs = max77686_rtc_irqs, /* same masks as 77686 */ + .num_irqs = ARRAY_SIZE(max77686_rtc_irqs), +}; + +static const struct max77686_rtc_driver_data max77802_drv_data = { + .delay = 200, + .mask = 0xff, + .map = max77802_map, + .alarm_enable_reg = true, + .rtc_irq_from_platform = false, + .alarm_pending_status_reg = MAX77686_REG_STATUS2, + .rtc_i2c_addr = MAX77686_INVALID_I2C_ADDR, + .rtc_irq_chip = &max77802_rtc_irq_chip, +}; + +static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm, + struct max77686_rtc_info *info) +{ + u8 mask = info->drv_data->mask; + + tm->tm_sec = data[RTC_SEC] & mask; + tm->tm_min = data[RTC_MIN] & mask; + if (info->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] & mask) - 1; + tm->tm_mday = data[RTC_DATE] & 0x1f; + tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1; + tm->tm_year = data[RTC_YEAR] & mask; + tm->tm_yday = 0; + tm->tm_isdst = 0; + + /* + * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the + * year values are just 0..99 so add 100 to support up to 2099. + */ + if (!info->drv_data->alarm_enable_reg) + tm->tm_year += 100; +} + +static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data, + struct max77686_rtc_info *info) +{ + 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; + + if (info->drv_data->alarm_enable_reg) { + data[RTC_YEAR] = tm->tm_year; + return 0; + } + + data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; + + if (tm->tm_year < 100) { + dev_err(info->dev, "RTC cannot handle the year %d.\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; + unsigned long delay = info->drv_data->delay; + + if (op == MAX77686_RTC_WRITE) + data = 1 << RTC_UDR_SHIFT; + else + data = 1 << RTC_RBUDR_SHIFT; + + ret = regmap_update_bits(info->rtc_regmap, + info->drv_data->map[REG_RTC_UPDATE0], + data, data); + if (ret < 0) + dev_err(info->dev, "Fail to write update reg(ret=%d, data=0x%x)\n", + ret, data); + else { + /* Minimum delay required before RTC update. */ + usleep_range(delay, delay * 2); + } + + 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->rtc_regmap, + info->drv_data->map[REG_RTC_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read time reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, tm, info); + +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, info); + if (ret < 0) + return ret; + + mutex_lock(&info->lock); + + ret = regmap_bulk_write(info->rtc_regmap, + info->drv_data->map[REG_RTC_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to write time reg(%d)\n", 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; + const unsigned int *map = info->drv_data->map; + 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->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &alrm->time, info); + + alrm->enabled = 0; + + if (info->drv_data->alarm_enable_reg) { + if (map[REG_RTC_AE1] == REG_RTC_NONE) { + ret = -EINVAL; + dev_err(info->dev, + "alarm enable register not set(%d)\n", ret); + goto out; + } + + ret = regmap_read(info->rtc_regmap, map[REG_RTC_AE1], &val); + if (ret < 0) { + dev_err(info->dev, + "fail to read alarm enable(%d)\n", ret); + goto out; + } + + if (val) + alrm->enabled = 1; + } else { + for (i = 0; i < ARRAY_SIZE(data); i++) { + if (data[i] & ALARM_ENABLE_MASK) { + alrm->enabled = 1; + break; + } + } + } + + alrm->pending = 0; + + if (info->drv_data->alarm_pending_status_reg == MAX77686_INVALID_REG) + goto out; + + ret = regmap_read(info->regmap, + info->drv_data->alarm_pending_status_reg, &val); + if (ret < 0) { + dev_err(info->dev, + "Fail to read alarm pending status reg(%d)\n", ret); + goto out; + } + + if (val & (1 << 4)) /* RTCA1 */ + alrm->pending = 1; + +out: + mutex_unlock(&info->lock); + return ret; +} + +static int max77686_rtc_stop_alarm(struct max77686_rtc_info *info) +{ + u8 data[RTC_NR_TIME]; + int ret, i; + struct rtc_time tm; + const unsigned int *map = info->drv_data->map; + + 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; + + if (info->drv_data->alarm_enable_reg) { + if (map[REG_RTC_AE1] == REG_RTC_NONE) { + ret = -EINVAL; + dev_err(info->dev, + "alarm enable register not set(%d)\n", ret); + goto out; + } + + ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], 0); + } else { + ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &tm, info); + + for (i = 0; i < ARRAY_SIZE(data); i++) + data[i] &= ~ALARM_ENABLE_MASK; + + ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + } + + if (ret < 0) { + dev_err(info->dev, "Fail to write alarm reg(%d)\n", 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; + const unsigned int *map = info->drv_data->map; + + 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; + + if (info->drv_data->alarm_enable_reg) { + ret = regmap_write(info->rtc_regmap, map[REG_RTC_AE1], + MAX77802_ALARM_ENABLE_VALUE); + } else { + ret = regmap_bulk_read(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to read alarm reg(%d)\n", ret); + goto out; + } + + max77686_rtc_data_to_tm(data, &tm, info); + + 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] & info->drv_data->mask) + data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT); + if (data[RTC_DATE] & 0x1f) + data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT); + + ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + } + + if (ret < 0) { + dev_err(info->dev, "Fail to write alarm reg(%d)\n", 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, info); + 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->rtc_regmap, + info->drv_data->map[REG_ALARM1_SEC], + data, ARRAY_SIZE(data)); + + if (ret < 0) { + dev_err(info->dev, "Fail to write alarm reg(%d)\n", 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_dbg(info->dev, "RTC alarm IRQ: %d\n", 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->rtc_regmap, + info->drv_data->map[REG_RTC_CONTROLM], + data, ARRAY_SIZE(data)); + if (ret < 0) { + dev_err(info->dev, "Fail to write controlm reg(%d)\n", ret); + return ret; + } + + ret = max77686_rtc_update(info, MAX77686_RTC_WRITE); + return ret; +} + +static const struct regmap_config max77686_rtc_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int max77686_init_rtc_regmap(struct max77686_rtc_info *info) +{ + struct device *parent = info->dev->parent; + struct i2c_client *parent_i2c = to_i2c_client(parent); + int ret; + + if (info->drv_data->rtc_irq_from_platform) { + struct platform_device *pdev = to_platform_device(info->dev); + + info->rtc_irq = platform_get_irq(pdev, 0); + if (info->rtc_irq < 0) { + dev_err(info->dev, "Failed to get rtc interrupts: %d\n", + info->rtc_irq); + return info->rtc_irq; + } + } else { + info->rtc_irq = parent_i2c->irq; + } + + info->regmap = dev_get_regmap(parent, NULL); + if (!info->regmap) { + dev_err(info->dev, "Failed to get rtc regmap\n"); + return -ENODEV; + } + + if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) { + info->rtc_regmap = info->regmap; + goto add_rtc_irq; + } + + info->rtc = i2c_new_dummy(parent_i2c->adapter, + info->drv_data->rtc_i2c_addr); + if (!info->rtc) { + dev_err(info->dev, "Failed to allocate I2C device for RTC\n"); + return -ENODEV; + } + + info->rtc_regmap = devm_regmap_init_i2c(info->rtc, + &max77686_rtc_regmap_config); + if (IS_ERR(info->rtc_regmap)) { + ret = PTR_ERR(info->rtc_regmap); + dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret); + goto err_unregister_i2c; + } + +add_rtc_irq: + ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq, + IRQF_ONESHOT | IRQF_SHARED, + 0, info->drv_data->rtc_irq_chip, + &info->rtc_irq_data); + if (ret < 0) { + dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret); + goto err_unregister_i2c; + } + + return 0; + +err_unregister_i2c: + if (info->rtc) + i2c_unregister_device(info->rtc); + return ret; +} + +static int max77686_rtc_probe(struct platform_device *pdev) +{ + struct max77686_rtc_info *info; + const struct platform_device_id *id = platform_get_device_id(pdev); + int ret; + + 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->drv_data = (const struct max77686_rtc_driver_data *) + id->driver_data; + + ret = max77686_init_rtc_regmap(info); + if (ret < 0) + return ret; + + 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, id->name, + &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; + } + + info->virq = regmap_irq_get_virq(info->rtc_irq_data, + MAX77686_RTCIRQ_RTCA1); + if (info->virq <= 0) { + ret = -ENXIO; + goto err_rtc; + } + + ret = request_threaded_irq(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); + goto err_rtc; + } + + return 0; + +err_rtc: + regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data); + if (info->rtc) + i2c_unregister_device(info->rtc); + + return ret; +} + +static int max77686_rtc_remove(struct platform_device *pdev) +{ + struct max77686_rtc_info *info = platform_get_drvdata(pdev); + + free_irq(info->virq, info); + regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data); + if (info->rtc) + i2c_unregister_device(info->rtc); + + return 0; +} + +#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", .driver_data = (kernel_ulong_t)&max77686_drv_data, }, + { "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, }, + { "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rtc_id); + +static struct platform_driver max77686_rtc_driver = { + .driver = { + .name = "max77686-rtc", + .pm = &max77686_rtc_pm_ops, + }, + .probe = max77686_rtc_probe, + .remove = max77686_rtc_remove, + .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-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..67d6fc2d2 --- /dev/null +++ b/drivers/rtc/rtc-max8925.c @@ -0,0 +1,325 @@ +/* + * 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); +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..20e50d9fd --- /dev/null +++ b/drivers/rtc/rtc-max8997.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// RTC driver for Maxim MAX8997 +// +// Copyright (C) 2013 Samsung Electronics Co.Ltd +// +// based on rtc-max8998.c + +#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 0; +} + +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 ret; +} + +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 }, + {}, +}; +MODULE_DEVICE_TABLE(platform, rtc_id); + +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..c873b4509 --- /dev/null +++ b/drivers/rtc/rtc-max8998.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// 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> + +#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 0; +} + +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 }, + { } +}; +MODULE_DEVICE_TABLE(platform, max8998_rtc_id); + +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..0fa33708f --- /dev/null +++ b/drivers/rtc/rtc-mc13xxx.c @@ -0,0 +1,358 @@ +/* + * 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/mod_devicetable.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 0; +} + +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: %s %lld\n", __func__, alarm->enabled ? "on" : "off", + (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 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_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_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_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-mc146818-lib.c b/drivers/rtc/rtc-mc146818-lib.c new file mode 100644 index 000000000..86b885891 --- /dev/null +++ b/drivers/rtc/rtc-mc146818-lib.c @@ -0,0 +1,198 @@ +#include <linux/bcd.h> +#include <linux/delay.h> +#include <linux/export.h> +#include <linux/mc146818rtc.h> + +#ifdef CONFIG_ACPI +#include <linux/acpi.h> +#endif + +/* + * Returns true if a clock update is in progress + */ +static inline unsigned char mc146818_is_updating(void) +{ + unsigned char uip; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); + uip = (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP); + spin_unlock_irqrestore(&rtc_lock, flags); + return uip; +} + +unsigned int mc146818_get_time(struct rtc_time *time) +{ + unsigned char ctrl; + unsigned long flags; + unsigned char century = 0; + +#ifdef CONFIG_MACH_DECSTATION + unsigned int real_year; +#endif + + /* + * read RTC once any update in progress is done. The update + * can take just over 2ms. We wait 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 (mc146818_is_updating()) + mdelay(20); + + /* + * 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(&rtc_lock, flags); + time->tm_sec = CMOS_READ(RTC_SECONDS); + time->tm_min = CMOS_READ(RTC_MINUTES); + time->tm_hour = CMOS_READ(RTC_HOURS); + time->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); + time->tm_mon = CMOS_READ(RTC_MONTH); + time->tm_year = CMOS_READ(RTC_YEAR); +#ifdef CONFIG_MACH_DECSTATION + real_year = CMOS_READ(RTC_DEC_YEAR); +#endif +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && + acpi_gbl_FADT.century) + century = CMOS_READ(acpi_gbl_FADT.century); +#endif + ctrl = CMOS_READ(RTC_CONTROL); + spin_unlock_irqrestore(&rtc_lock, flags); + + if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD) + { + time->tm_sec = bcd2bin(time->tm_sec); + time->tm_min = bcd2bin(time->tm_min); + time->tm_hour = bcd2bin(time->tm_hour); + time->tm_mday = bcd2bin(time->tm_mday); + time->tm_mon = bcd2bin(time->tm_mon); + time->tm_year = bcd2bin(time->tm_year); + century = bcd2bin(century); + } + +#ifdef CONFIG_MACH_DECSTATION + time->tm_year += real_year - 72; +#endif + + if (century > 19) + time->tm_year += (century - 19) * 100; + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + if (time->tm_year <= 69) + time->tm_year += 100; + + time->tm_mon--; + + return RTC_24H; +} +EXPORT_SYMBOL_GPL(mc146818_get_time); + +/* Set the current date and time in the real time clock. */ +int mc146818_set_time(struct rtc_time *time) +{ + unsigned long flags; + unsigned char mon, day, hrs, min, sec; + unsigned char save_control, save_freq_select; + unsigned int yrs; +#ifdef CONFIG_MACH_DECSTATION + unsigned int real_yrs, leap_yr; +#endif + unsigned char century = 0; + + 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 > 255) /* They are unsigned */ + return -EINVAL; + + spin_lock_irqsave(&rtc_lock, flags); +#ifdef CONFIG_MACH_DECSTATION + real_yrs = yrs; + leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) || + !((yrs + 1900) % 400)); + yrs = 72; + + /* + * We want to keep the year set to 73 until March + * for non-leap years, so that Feb, 29th is handled + * correctly. + */ + if (!leap_yr && mon < 3) { + real_yrs--; + yrs = 73; + } +#endif + +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && + acpi_gbl_FADT.century) { + century = (yrs + 1900) / 100; + yrs %= 100; + } +#endif + + /* These limits and adjustments are independent of + * whether the chip is in binary mode or not. + */ + if (yrs > 169) { + spin_unlock_irqrestore(&rtc_lock, flags); + return -EINVAL; + } + + if (yrs >= 100) + yrs -= 100; + + if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) + || RTC_ALWAYS_BCD) { + sec = bin2bcd(sec); + min = bin2bcd(min); + hrs = bin2bcd(hrs); + day = bin2bcd(day); + mon = bin2bcd(mon); + yrs = bin2bcd(yrs); + century = bin2bcd(century); + } + + save_control = CMOS_READ(RTC_CONTROL); + CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); + save_freq_select = CMOS_READ(RTC_FREQ_SELECT); + CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); + +#ifdef CONFIG_MACH_DECSTATION + CMOS_WRITE(real_yrs, RTC_DEC_YEAR); +#endif + CMOS_WRITE(yrs, RTC_YEAR); + CMOS_WRITE(mon, RTC_MONTH); + CMOS_WRITE(day, RTC_DAY_OF_MONTH); + CMOS_WRITE(hrs, RTC_HOURS); + CMOS_WRITE(min, RTC_MINUTES); + CMOS_WRITE(sec, RTC_SECONDS); +#ifdef CONFIG_ACPI + if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID && + acpi_gbl_FADT.century) + CMOS_WRITE(century, acpi_gbl_FADT.century); +#endif + + CMOS_WRITE(save_control, RTC_CONTROL); + CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + + spin_unlock_irqrestore(&rtc_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(mc146818_set_time); diff --git a/drivers/rtc/rtc-mcp795.c b/drivers/rtc/rtc-mcp795.c new file mode 100644 index 000000000..00e11c1b2 --- /dev/null +++ b/drivers/rtc/rtc-mcp795.c @@ -0,0 +1,457 @@ +/* + * 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> +#include <linux/of.h> +#include <linux/bcd.h> +#include <linux/delay.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 + +/* MCP795 RTCC registers, see datasheet table 4-1 */ +#define MCP795_REG_SECONDS 0x01 +#define MCP795_REG_DAY 0x04 +#define MCP795_REG_MONTH 0x06 +#define MCP795_REG_CONTROL 0x08 +#define MCP795_REG_ALM0_SECONDS 0x0C +#define MCP795_REG_ALM0_DAY 0x0F + +#define MCP795_ST_BIT BIT(7) +#define MCP795_24_BIT BIT(6) +#define MCP795_LP_BIT BIT(5) +#define MCP795_EXTOSC_BIT BIT(3) +#define MCP795_OSCON_BIT BIT(5) +#define MCP795_ALM0_BIT BIT(4) +#define MCP795_ALM1_BIT BIT(5) +#define MCP795_ALM0IF_BIT BIT(3) +#define MCP795_ALM0C0_BIT BIT(4) +#define MCP795_ALM0C1_BIT BIT(5) +#define MCP795_ALM0C2_BIT BIT(6) + +#define SEC_PER_DAY (24 * 60 * 60) + +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[257]; + + 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_stop_oscillator(struct device *dev, bool *extosc) +{ + int retries = 5; + int ret; + u8 data; + + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_SECONDS, MCP795_ST_BIT, 0); + if (ret) + return ret; + ret = mcp795_rtcc_read(dev, MCP795_REG_CONTROL, &data, 1); + if (ret) + return ret; + *extosc = !!(data & MCP795_EXTOSC_BIT); + ret = mcp795_rtcc_set_bits( + dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, 0); + if (ret) + return ret; + /* wait for the OSCON bit to clear */ + do { + usleep_range(700, 800); + ret = mcp795_rtcc_read(dev, MCP795_REG_DAY, &data, 1); + if (ret) + break; + if (!(data & MCP795_OSCON_BIT)) + break; + + } while (--retries); + + return !retries ? -EIO : ret; +} + +static int mcp795_start_oscillator(struct device *dev, bool *extosc) +{ + if (extosc) { + u8 data = *extosc ? MCP795_EXTOSC_BIT : 0; + int ret; + + ret = mcp795_rtcc_set_bits( + dev, MCP795_REG_CONTROL, MCP795_EXTOSC_BIT, data); + if (ret) + return ret; + } + return mcp795_rtcc_set_bits( + dev, MCP795_REG_SECONDS, MCP795_ST_BIT, MCP795_ST_BIT); +} + +/* Enable or disable Alarm 0 in RTC */ +static int mcp795_update_alarm(struct device *dev, bool enable) +{ + int ret; + + dev_dbg(dev, "%s alarm\n", enable ? "Enable" : "Disable"); + + if (enable) { + /* clear ALM0IF (Alarm 0 Interrupt Flag) bit */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_ALM0_DAY, + MCP795_ALM0IF_BIT, 0); + if (ret) + return ret; + /* enable alarm 0 */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL, + MCP795_ALM0_BIT, MCP795_ALM0_BIT); + } else { + /* disable alarm 0 and alarm 1 */ + ret = mcp795_rtcc_set_bits(dev, MCP795_REG_CONTROL, + MCP795_ALM0_BIT | MCP795_ALM1_BIT, 0); + } + return ret; +} + +static int mcp795_set_time(struct device *dev, struct rtc_time *tim) +{ + int ret; + u8 data[7]; + bool extosc; + + /* Stop RTC and store current value of EXTOSC bit */ + ret = mcp795_stop_oscillator(dev, &extosc); + if (ret) + return ret; + + /* Read first, so we can leave config bits untouched */ + ret = mcp795_rtcc_read(dev, MCP795_REG_SECONDS, data, sizeof(data)); + + if (ret) + return ret; + + data[0] = (data[0] & 0x80) | bin2bcd(tim->tm_sec); + data[1] = (data[1] & 0x80) | bin2bcd(tim->tm_min); + data[2] = bin2bcd(tim->tm_hour); + data[3] = (data[3] & 0xF8) | bin2bcd(tim->tm_wday + 1); + data[4] = bin2bcd(tim->tm_mday); + data[5] = (data[5] & MCP795_LP_BIT) | bin2bcd(tim->tm_mon + 1); + + if (tim->tm_year > 100) + tim->tm_year -= 100; + + data[6] = bin2bcd(tim->tm_year); + + /* Always write the date and month using a separate Write command. + * This is a workaround for a know silicon issue that some combinations + * of date and month values may result in the date being reset to 1. + */ + ret = mcp795_rtcc_write(dev, MCP795_REG_SECONDS, data, 5); + if (ret) + return ret; + + ret = mcp795_rtcc_write(dev, MCP795_REG_MONTH, &data[5], 2); + if (ret) + return ret; + + /* Start back RTC and restore previous value of EXTOSC bit. + * There is no need to clear EXTOSC bit when the previous value was 0 + * because it was already cleared when stopping the RTC oscillator. + */ + ret = mcp795_start_oscillator(dev, extosc ? &extosc : NULL); + if (ret) + return ret; + + dev_dbg(dev, "Set mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n", + tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, + tim->tm_wday, 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, MCP795_REG_SECONDS, data, sizeof(data)); + + if (ret) + return ret; + + tim->tm_sec = bcd2bin(data[0] & 0x7F); + tim->tm_min = bcd2bin(data[1] & 0x7F); + tim->tm_hour = bcd2bin(data[2] & 0x3F); + tim->tm_wday = bcd2bin(data[3] & 0x07) - 1; + tim->tm_mday = bcd2bin(data[4] & 0x3F); + tim->tm_mon = bcd2bin(data[5] & 0x1F) - 1; + tim->tm_year = bcd2bin(data[6]) + 100; /* Assume we are in 20xx */ + + dev_dbg(dev, "Read from mcp795: %04d-%02d-%02d(%d) %02d:%02d:%02d\n", + tim->tm_year + 1900, tim->tm_mon, tim->tm_mday, + tim->tm_wday, tim->tm_hour, tim->tm_min, tim->tm_sec); + + return 0; +} + +static int mcp795_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time now_tm; + time64_t now; + time64_t later; + u8 tmp[6]; + int ret; + + /* Read current time from RTC hardware */ + ret = mcp795_read_time(dev, &now_tm); + if (ret) + return ret; + /* Get the number of seconds since 1970 */ + now = rtc_tm_to_time64(&now_tm); + later = rtc_tm_to_time64(&alm->time); + if (later <= now) + return -EINVAL; + /* make sure alarm fires within the next one year */ + if ((later - now) >= + (SEC_PER_DAY * (365 + is_leap_year(alm->time.tm_year)))) + return -EDOM; + /* disable alarm */ + ret = mcp795_update_alarm(dev, false); + if (ret) + return ret; + /* Read registers, so we can leave configuration bits untouched */ + ret = mcp795_rtcc_read(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp)); + if (ret) + return ret; + + alm->time.tm_year = -1; + alm->time.tm_isdst = -1; + alm->time.tm_yday = -1; + + tmp[0] = (tmp[0] & 0x80) | bin2bcd(alm->time.tm_sec); + tmp[1] = (tmp[1] & 0x80) | bin2bcd(alm->time.tm_min); + tmp[2] = (tmp[2] & 0xE0) | bin2bcd(alm->time.tm_hour); + tmp[3] = (tmp[3] & 0x80) | bin2bcd(alm->time.tm_wday + 1); + /* set alarm match: seconds, minutes, hour, day, date and month */ + tmp[3] |= (MCP795_ALM0C2_BIT | MCP795_ALM0C1_BIT | MCP795_ALM0C0_BIT); + tmp[4] = (tmp[4] & 0xC0) | bin2bcd(alm->time.tm_mday); + tmp[5] = (tmp[5] & 0xE0) | bin2bcd(alm->time.tm_mon + 1); + + ret = mcp795_rtcc_write(dev, MCP795_REG_ALM0_SECONDS, tmp, sizeof(tmp)); + if (ret) + return ret; + + /* enable alarm if requested */ + if (alm->enabled) { + ret = mcp795_update_alarm(dev, true); + if (ret) + return ret; + dev_dbg(dev, "Alarm IRQ armed\n"); + } + dev_dbg(dev, "Set alarm: %02d-%02d(%d) %02d:%02d:%02d\n", + alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday, + alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec); + return 0; +} + +static int mcp795_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + u8 data[6]; + int ret; + + ret = mcp795_rtcc_read( + dev, MCP795_REG_ALM0_SECONDS, data, sizeof(data)); + if (ret) + return ret; + + alm->time.tm_sec = bcd2bin(data[0] & 0x7F); + alm->time.tm_min = bcd2bin(data[1] & 0x7F); + alm->time.tm_hour = bcd2bin(data[2] & 0x1F); + alm->time.tm_wday = bcd2bin(data[3] & 0x07) - 1; + alm->time.tm_mday = bcd2bin(data[4] & 0x3F); + alm->time.tm_mon = bcd2bin(data[5] & 0x1F) - 1; + alm->time.tm_year = -1; + alm->time.tm_isdst = -1; + alm->time.tm_yday = -1; + + dev_dbg(dev, "Read alarm: %02d-%02d(%d) %02d:%02d:%02d\n", + alm->time.tm_mon, alm->time.tm_mday, alm->time.tm_wday, + alm->time.tm_hour, alm->time.tm_min, alm->time.tm_sec); + return 0; +} + +static int mcp795_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + return mcp795_update_alarm(dev, !!enabled); +} + +static irqreturn_t mcp795_irq(int irq, void *data) +{ + struct spi_device *spi = data; + struct rtc_device *rtc = spi_get_drvdata(spi); + struct mutex *lock = &rtc->ops_lock; + int ret; + + mutex_lock(lock); + + /* Disable alarm. + * There is no need to clear ALM0IF (Alarm 0 Interrupt Flag) bit, + * because it is done every time when alarm is enabled. + */ + ret = mcp795_update_alarm(&spi->dev, false); + if (ret) + dev_err(&spi->dev, + "Failed to disable alarm in IRQ (ret=%d)\n", ret); + rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF); + + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static const struct rtc_class_ops mcp795_rtc_ops = { + .read_time = mcp795_read_time, + .set_time = mcp795_set_time, + .read_alarm = mcp795_read_alarm, + .set_alarm = mcp795_set_alarm, + .alarm_irq_enable = mcp795_alarm_irq_enable +}; + +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 but don't set the value of EXTOSC bit */ + mcp795_start_oscillator(&spi->dev, NULL); + /* 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); + + if (spi->irq > 0) { + dev_dbg(&spi->dev, "Alarm support enabled\n"); + + /* Clear any pending alarm (ALM0IF bit) before requesting + * the interrupt. + */ + mcp795_rtcc_set_bits(&spi->dev, MCP795_REG_ALM0_DAY, + MCP795_ALM0IF_BIT, 0); + ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, + mcp795_irq, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&rtc->dev), spi); + if (ret) + dev_err(&spi->dev, "Failed to request IRQ: %d: %d\n", + spi->irq, ret); + else + device_init_wakeup(&spi->dev, true); + } + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mcp795_of_match[] = { + { .compatible = "maxim,mcp795" }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp795_of_match); +#endif + +static struct spi_driver mcp795_driver = { + .driver = { + .name = "rtc-mcp795", + .of_match_table = of_match_ptr(mcp795_of_match), + }, + .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..07b30a373 --- /dev/null +++ b/drivers/rtc/rtc-moxart.c @@ -0,0 +1,328 @@ +/* + * 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" }, + { }, +}; +MODULE_DEVICE_TABLE(of, moxart_rtc_match); + +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..dd0364293 --- /dev/null +++ b/drivers/rtc/rtc-mpc5121.c @@ -0,0 +1,424 @@ +/* + * 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(®s->second_set, tm->tm_sec); + out_8(®s->minute_set, tm->tm_min); + out_8(®s->hour_set, tm->tm_hour); + + /* set time sequence */ + out_8(®s->set_time, 0x1); + out_8(®s->set_time, 0x3); + out_8(®s->set_time, 0x1); + out_8(®s->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(®s->actual_time) + in_be32(®s->target_time); + + rtc_time_to_tm(now, tm); + + /* + * update second minute hour registers + * so alarms will work + */ + mpc5121_rtc_update_smh(regs, tm); + + return 0; +} + +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(®s->target_time, now - in_be32(®s->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(®s->second); + tm->tm_min = in_8(®s->minute); + + /* 12 hour format? */ + if (in_8(®s->hour) & 0x20) + tm->tm_hour = (in_8(®s->hour) >> 1) + + (in_8(®s->hour) & 1 ? 12 : 0); + else + tm->tm_hour = in_8(®s->hour); + + tmp = in_8(®s->wday_mday); + tm->tm_mday = tmp & 0x1f; + tm->tm_mon = in_8(®s->month) - 1; + tm->tm_year = in_be16(®s->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(®s->month_set, tm->tm_mon + 1); + out_8(®s->weekday_set, tm->tm_wday ? tm->tm_wday : 7); + out_8(®s->date_set, tm->tm_mday); + out_be16(®s->year_set, tm->tm_year + 1900); + + /* set date sequence */ + out_8(®s->set_date, 0x1); + out_8(®s->set_date, 0x3); + out_8(®s->set_date, 0x1); + out_8(®s->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(®s->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(®s->alm_min_set, alarm->time.tm_min); + out_8(®s->alm_hour_set, alarm->time.tm_hour); + + out_8(®s->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(®s->int_alm)) { + /* acknowledge and clear status */ + out_8(®s->int_alm, 1); + out_8(®s->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(®s->int_sec) && (in_8(®s->int_enable) & 0x1)) { + /* acknowledge */ + out_8(®s->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(®s->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(®s->alm_enable, 0); + out_8(®s->int_enable, in_8(®s->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", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mpc5121_rtc_match); +#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..1925aaf09 --- /dev/null +++ b/drivers/rtc/rtc-mrst.c @@ -0,0 +1,526 @@ +/* + * 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/mc146818rtc.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sfi.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; + + 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 0; +} + +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 > 172) + 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; + + /* 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 IS_ENABLED(CONFIG_RTC_INTF_PROC) + +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 = devm_request_mem_region(dev, 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.dev = dev; + dev_set_drvdata(dev, &mrst_rtc); + + mrst_rtc.rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(mrst_rtc.rtc)) + return PTR_ERR(mrst_rtc.rtc); + + mrst_rtc.rtc->ops = &mrst_rtc_ops; + + 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 = devm_request_irq(dev, 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 cleanup0; + } + } + + retval = rtc_register_device(mrst_rtc.rtc); + if (retval) + goto cleanup0; + + dev_dbg(dev, "initialised\n"); + return 0; + +cleanup0: + mrst_rtc.dev = NULL; + 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); + + rtc_mrst_do_shutdown(); + + mrst->rtc = 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..6aace9319 --- /dev/null +++ b/drivers/rtc/rtc-msm6242.c @@ -0,0 +1,239 @@ +/* + * 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) & + MSM6242_HOUR10_HR_MASK) * 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 0; +} + +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-mt6397.c b/drivers/rtc/rtc-mt6397.c new file mode 100644 index 000000000..671b6d275 --- /dev/null +++ b/drivers/rtc/rtc-mt6397.c @@ -0,0 +1,441 @@ +/* +* Copyright (c) 2014-2015 MediaTek Inc. +* Author: Tianping.Fang <tianping.fang@mediatek.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. +* +* 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/init.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/irqdomain.h> +#include <linux/platform_device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/io.h> +#include <linux/mfd/mt6397/core.h> + +#define RTC_BBPU 0x0000 +#define RTC_BBPU_CBUSY BIT(6) + +#define RTC_WRTGR 0x003c + +#define RTC_IRQ_STA 0x0002 +#define RTC_IRQ_STA_AL BIT(0) +#define RTC_IRQ_STA_LP BIT(3) + +#define RTC_IRQ_EN 0x0004 +#define RTC_IRQ_EN_AL BIT(0) +#define RTC_IRQ_EN_ONESHOT BIT(2) +#define RTC_IRQ_EN_LP BIT(3) +#define RTC_IRQ_EN_ONESHOT_AL (RTC_IRQ_EN_ONESHOT | RTC_IRQ_EN_AL) + +#define RTC_AL_MASK 0x0008 +#define RTC_AL_MASK_DOW BIT(4) + +#define RTC_TC_SEC 0x000a +/* Min, Hour, Dom... register offset to RTC_TC_SEC */ +#define RTC_OFFSET_SEC 0 +#define RTC_OFFSET_MIN 1 +#define RTC_OFFSET_HOUR 2 +#define RTC_OFFSET_DOM 3 +#define RTC_OFFSET_DOW 4 +#define RTC_OFFSET_MTH 5 +#define RTC_OFFSET_YEAR 6 +#define RTC_OFFSET_COUNT 7 + +#define RTC_AL_SEC 0x0018 + +#define RTC_AL_SEC_MASK 0x003f +#define RTC_AL_MIN_MASK 0x003f +#define RTC_AL_HOU_MASK 0x001f +#define RTC_AL_DOM_MASK 0x001f +#define RTC_AL_DOW_MASK 0x0007 +#define RTC_AL_MTH_MASK 0x000f +#define RTC_AL_YEA_MASK 0x007f + +#define RTC_PDN2 0x002e +#define RTC_PDN2_PWRON_ALARM BIT(4) + +#define RTC_MIN_YEAR 1968 +#define RTC_BASE_YEAR 1900 +#define RTC_NUM_YEARS 128 +#define RTC_MIN_YEAR_OFFSET (RTC_MIN_YEAR - RTC_BASE_YEAR) + +struct mt6397_rtc { + struct device *dev; + struct rtc_device *rtc_dev; + struct mutex lock; + struct regmap *regmap; + int irq; + u32 addr_base; +}; + +static int mtk_rtc_write_trigger(struct mt6397_rtc *rtc) +{ + unsigned long timeout = jiffies + HZ; + int ret; + u32 data; + + ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_WRTGR, 1); + if (ret < 0) + return ret; + + while (1) { + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_BBPU, + &data); + if (ret < 0) + break; + if (!(data & RTC_BBPU_CBUSY)) + break; + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + break; + } + cpu_relax(); + } + + return ret; +} + +static irqreturn_t mtk_rtc_irq_handler_thread(int irq, void *data) +{ + struct mt6397_rtc *rtc = data; + u32 irqsta, irqen; + int ret; + + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_STA, &irqsta); + if ((ret >= 0) && (irqsta & RTC_IRQ_STA_AL)) { + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + irqen = irqsta & ~RTC_IRQ_EN_AL; + mutex_lock(&rtc->lock); + if (regmap_write(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, + irqen) == 0) + mtk_rtc_write_trigger(rtc); + mutex_unlock(&rtc->lock); + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int __mtk_rtc_read_time(struct mt6397_rtc *rtc, + struct rtc_time *tm, int *sec) +{ + int ret; + u16 data[RTC_OFFSET_COUNT]; + + mutex_lock(&rtc->lock); + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + tm->tm_sec = data[RTC_OFFSET_SEC]; + tm->tm_min = data[RTC_OFFSET_MIN]; + tm->tm_hour = data[RTC_OFFSET_HOUR]; + tm->tm_mday = data[RTC_OFFSET_DOM]; + tm->tm_mon = data[RTC_OFFSET_MTH]; + tm->tm_year = data[RTC_OFFSET_YEAR]; + + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_TC_SEC, sec); +exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + time64_t time; + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + int days, sec, ret; + + do { + ret = __mtk_rtc_read_time(rtc, tm, &sec); + if (ret < 0) + goto exit; + } while (sec < tm->tm_sec); + + /* HW register use 7 bits to store year data, minus + * RTC_MIN_YEAR_OFFSET before write year data to register, and plus + * RTC_MIN_YEAR_OFFSET back after read year from register + */ + tm->tm_year += RTC_MIN_YEAR_OFFSET; + + /* HW register start mon from one, but tm_mon start from zero. */ + tm->tm_mon--; + time = rtc_tm_to_time64(tm); + + /* rtc_tm_to_time64 covert Gregorian date to seconds since + * 01-01-1970 00:00:00, and this date is Thursday. + */ + days = div_s64(time, 86400); + tm->tm_wday = (days + 4) % 7; + +exit: + return ret; +} + +static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + int ret; + u16 data[RTC_OFFSET_COUNT]; + + tm->tm_year -= RTC_MIN_YEAR_OFFSET; + tm->tm_mon++; + + data[RTC_OFFSET_SEC] = tm->tm_sec; + data[RTC_OFFSET_MIN] = tm->tm_min; + data[RTC_OFFSET_HOUR] = tm->tm_hour; + data[RTC_OFFSET_DOM] = tm->tm_mday; + data[RTC_OFFSET_MTH] = tm->tm_mon; + data[RTC_OFFSET_YEAR] = tm->tm_year; + + mutex_lock(&rtc->lock); + ret = regmap_bulk_write(rtc->regmap, rtc->addr_base + RTC_TC_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + /* Time register write to hardware after call trigger function */ + ret = mtk_rtc_write_trigger(rtc); + +exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time *tm = &alm->time; + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + u32 irqen, pdn2; + int ret; + u16 data[RTC_OFFSET_COUNT]; + + mutex_lock(&rtc->lock); + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_IRQ_EN, &irqen); + if (ret < 0) + goto err_exit; + ret = regmap_read(rtc->regmap, rtc->addr_base + RTC_PDN2, &pdn2); + if (ret < 0) + goto err_exit; + + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto err_exit; + + alm->enabled = !!(irqen & RTC_IRQ_EN_AL); + alm->pending = !!(pdn2 & RTC_PDN2_PWRON_ALARM); + mutex_unlock(&rtc->lock); + + tm->tm_sec = data[RTC_OFFSET_SEC] & RTC_AL_SEC_MASK; + tm->tm_min = data[RTC_OFFSET_MIN] & RTC_AL_MIN_MASK; + tm->tm_hour = data[RTC_OFFSET_HOUR] & RTC_AL_HOU_MASK; + tm->tm_mday = data[RTC_OFFSET_DOM] & RTC_AL_DOM_MASK; + tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK; + tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK; + + tm->tm_year += RTC_MIN_YEAR_OFFSET; + tm->tm_mon--; + + return 0; +err_exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct rtc_time *tm = &alm->time; + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + int ret; + u16 data[RTC_OFFSET_COUNT]; + + tm->tm_year -= RTC_MIN_YEAR_OFFSET; + tm->tm_mon++; + + mutex_lock(&rtc->lock); + ret = regmap_bulk_read(rtc->regmap, rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + + data[RTC_OFFSET_SEC] = ((data[RTC_OFFSET_SEC] & ~(RTC_AL_SEC_MASK)) | + (tm->tm_sec & RTC_AL_SEC_MASK)); + data[RTC_OFFSET_MIN] = ((data[RTC_OFFSET_MIN] & ~(RTC_AL_MIN_MASK)) | + (tm->tm_min & RTC_AL_MIN_MASK)); + data[RTC_OFFSET_HOUR] = ((data[RTC_OFFSET_HOUR] & ~(RTC_AL_HOU_MASK)) | + (tm->tm_hour & RTC_AL_HOU_MASK)); + data[RTC_OFFSET_DOM] = ((data[RTC_OFFSET_DOM] & ~(RTC_AL_DOM_MASK)) | + (tm->tm_mday & RTC_AL_DOM_MASK)); + data[RTC_OFFSET_MTH] = ((data[RTC_OFFSET_MTH] & ~(RTC_AL_MTH_MASK)) | + (tm->tm_mon & RTC_AL_MTH_MASK)); + data[RTC_OFFSET_YEAR] = ((data[RTC_OFFSET_YEAR] & ~(RTC_AL_YEA_MASK)) | + (tm->tm_year & RTC_AL_YEA_MASK)); + + if (alm->enabled) { + ret = regmap_bulk_write(rtc->regmap, + rtc->addr_base + RTC_AL_SEC, + data, RTC_OFFSET_COUNT); + if (ret < 0) + goto exit; + ret = regmap_write(rtc->regmap, rtc->addr_base + RTC_AL_MASK, + RTC_AL_MASK_DOW); + if (ret < 0) + goto exit; + ret = regmap_update_bits(rtc->regmap, + rtc->addr_base + RTC_IRQ_EN, + RTC_IRQ_EN_ONESHOT_AL, + RTC_IRQ_EN_ONESHOT_AL); + if (ret < 0) + goto exit; + } else { + ret = regmap_update_bits(rtc->regmap, + rtc->addr_base + RTC_IRQ_EN, + RTC_IRQ_EN_ONESHOT_AL, 0); + if (ret < 0) + goto exit; + } + + /* All alarm time register write to hardware after calling + * mtk_rtc_write_trigger. This can avoid race condition if alarm + * occur happen during writing alarm time register. + */ + ret = mtk_rtc_write_trigger(rtc); +exit: + mutex_unlock(&rtc->lock); + return ret; +} + +static const struct rtc_class_ops mtk_rtc_ops = { + .read_time = mtk_rtc_read_time, + .set_time = mtk_rtc_set_time, + .read_alarm = mtk_rtc_read_alarm, + .set_alarm = mtk_rtc_set_alarm, +}; + +static int mtk_rtc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); + struct mt6397_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct mt6397_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + rtc->addr_base = res->start; + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) + return rtc->irq; + + rtc->regmap = mt6397_chip->regmap; + rtc->dev = &pdev->dev; + mutex_init(&rtc->lock); + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_allocate_device(rtc->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + ret = request_threaded_irq(rtc->irq, NULL, + mtk_rtc_irq_handler_thread, + IRQF_ONESHOT | IRQF_TRIGGER_HIGH, + "mt6397-rtc", rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", + rtc->irq, ret); + return ret; + } + + device_init_wakeup(&pdev->dev, 1); + + rtc->rtc_dev->ops = &mtk_rtc_ops; + + ret = rtc_register_device(rtc->rtc_dev); + if (ret) { + dev_err(&pdev->dev, "register rtc device failed\n"); + goto out_free_irq; + } + + return 0; + +out_free_irq: + free_irq(rtc->irq, rtc); + return ret; +} + +static int mtk_rtc_remove(struct platform_device *pdev) +{ + struct mt6397_rtc *rtc = platform_get_drvdata(pdev); + + free_irq(rtc->irq, rtc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mt6397_rtc_suspend(struct device *dev) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(rtc->irq); + + return 0; +} + +static int mt6397_rtc_resume(struct device *dev) +{ + struct mt6397_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(rtc->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mt6397_pm_ops, mt6397_rtc_suspend, + mt6397_rtc_resume); + +static const struct of_device_id mt6397_rtc_of_match[] = { + { .compatible = "mediatek,mt6397-rtc", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6397_rtc_of_match); + +static struct platform_driver mtk_rtc_driver = { + .driver = { + .name = "mt6397-rtc", + .of_match_table = mt6397_rtc_of_match, + .pm = &mt6397_pm_ops, + }, + .probe = mtk_rtc_probe, + .remove = mtk_rtc_remove, +}; + +module_platform_driver(mtk_rtc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Tianping Fang <tianping.fang@mediatek.com>"); +MODULE_DESCRIPTION("RTC Driver for MediaTek MT6397 PMIC"); diff --git a/drivers/rtc/rtc-mt7622.c b/drivers/rtc/rtc-mt7622.c new file mode 100644 index 000000000..fd0cea722 --- /dev/null +++ b/drivers/rtc/rtc-mt7622.c @@ -0,0 +1,423 @@ +/* + * Driver for MediaTek SoC based RTC + * + * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.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/clk.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#define MTK_RTC_DEV KBUILD_MODNAME + +#define MTK_RTC_PWRCHK1 0x4 +#define RTC_PWRCHK1_MAGIC 0xc6 + +#define MTK_RTC_PWRCHK2 0x8 +#define RTC_PWRCHK2_MAGIC 0x9a + +#define MTK_RTC_KEY 0xc +#define RTC_KEY_MAGIC 0x59 + +#define MTK_RTC_PROT1 0x10 +#define RTC_PROT1_MAGIC 0xa3 + +#define MTK_RTC_PROT2 0x14 +#define RTC_PROT2_MAGIC 0x57 + +#define MTK_RTC_PROT3 0x18 +#define RTC_PROT3_MAGIC 0x67 + +#define MTK_RTC_PROT4 0x1c +#define RTC_PROT4_MAGIC 0xd2 + +#define MTK_RTC_CTL 0x20 +#define RTC_RC_STOP BIT(0) + +#define MTK_RTC_DEBNCE 0x2c +#define RTC_DEBNCE_MASK GENMASK(2, 0) + +#define MTK_RTC_INT 0x30 +#define RTC_INT_AL_STA BIT(4) + +/* + * Ranges from 0x40 to 0x78 provide RTC time setup for year, month, + * day of month, day of week, hour, minute and second. + */ +#define MTK_RTC_TREG(_t, _f) (0x40 + (0x4 * (_f)) + ((_t) * 0x20)) + +#define MTK_RTC_AL_CTL 0x7c +#define RTC_AL_EN BIT(0) +#define RTC_AL_ALL GENMASK(7, 0) + +/* + * The offset is used in the translation for the year between in struct + * rtc_time and in hardware register MTK_RTC_TREG(x,MTK_YEA) + */ +#define MTK_RTC_TM_YR_OFFSET 100 + +/* + * The lowest value for the valid tm_year. RTC hardware would take incorrectly + * tm_year 100 as not a leap year and thus it is also required being excluded + * from the valid options. + */ +#define MTK_RTC_TM_YR_L (MTK_RTC_TM_YR_OFFSET + 1) + +/* + * The most year the RTC can hold is 99 and the next to 99 in year register + * would be wraparound to 0, for MT7622. + */ +#define MTK_RTC_HW_YR_LIMIT 99 + +/* The highest value for the valid tm_year */ +#define MTK_RTC_TM_YR_H (MTK_RTC_TM_YR_OFFSET + MTK_RTC_HW_YR_LIMIT) + +/* Simple macro helps to check whether the hardware supports the tm_year */ +#define MTK_RTC_TM_YR_VALID(_y) ((_y) >= MTK_RTC_TM_YR_L && \ + (_y) <= MTK_RTC_TM_YR_H) + +/* Types of the function the RTC provides are time counter and alarm. */ +enum { + MTK_TC, + MTK_AL, +}; + +/* Indexes are used for the pointer to relevant registers in MTK_RTC_TREG */ +enum { + MTK_YEA, + MTK_MON, + MTK_DOM, + MTK_DOW, + MTK_HOU, + MTK_MIN, + MTK_SEC +}; + +struct mtk_rtc { + struct rtc_device *rtc; + void __iomem *base; + int irq; + struct clk *clk; +}; + +static void mtk_w32(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + writel_relaxed(val, rtc->base + reg); +} + +static u32 mtk_r32(struct mtk_rtc *rtc, u32 reg) +{ + return readl_relaxed(rtc->base + reg); +} + +static void mtk_rmw(struct mtk_rtc *rtc, u32 reg, u32 mask, u32 set) +{ + u32 val; + + val = mtk_r32(rtc, reg); + val &= ~mask; + val |= set; + mtk_w32(rtc, reg, val); +} + +static void mtk_set(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + mtk_rmw(rtc, reg, 0, val); +} + +static void mtk_clr(struct mtk_rtc *rtc, u32 reg, u32 val) +{ + mtk_rmw(rtc, reg, val, 0); +} + +static void mtk_rtc_hw_init(struct mtk_rtc *hw) +{ + /* The setup of the init sequence is for allowing RTC got to work */ + mtk_w32(hw, MTK_RTC_PWRCHK1, RTC_PWRCHK1_MAGIC); + mtk_w32(hw, MTK_RTC_PWRCHK2, RTC_PWRCHK2_MAGIC); + mtk_w32(hw, MTK_RTC_KEY, RTC_KEY_MAGIC); + mtk_w32(hw, MTK_RTC_PROT1, RTC_PROT1_MAGIC); + mtk_w32(hw, MTK_RTC_PROT2, RTC_PROT2_MAGIC); + mtk_w32(hw, MTK_RTC_PROT3, RTC_PROT3_MAGIC); + mtk_w32(hw, MTK_RTC_PROT4, RTC_PROT4_MAGIC); + mtk_rmw(hw, MTK_RTC_DEBNCE, RTC_DEBNCE_MASK, 0); + mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); +} + +static void mtk_rtc_get_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, + int time_alarm) +{ + u32 year, mon, mday, wday, hour, min, sec; + + /* + * Read again until the field of the second is not changed which + * ensures all fields in the consistent state. Note that MTK_SEC must + * be read first. In this way, it guarantees the others remain not + * changed when the results for two MTK_SEC consecutive reads are same. + */ + do { + sec = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC)); + min = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN)); + hour = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU)); + wday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW)); + mday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM)); + mon = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MON)); + year = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA)); + } while (sec != mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC))); + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour = hour; + tm->tm_wday = wday; + tm->tm_mday = mday; + tm->tm_mon = mon - 1; + + /* Rebase to the absolute year which userspace queries */ + tm->tm_year = year + MTK_RTC_TM_YR_OFFSET; +} + +static void mtk_rtc_set_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, + int time_alarm) +{ + u32 year; + + /* Rebase to the relative year which RTC hardware requires */ + year = tm->tm_year - MTK_RTC_TM_YR_OFFSET; + + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA), year); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MON), tm->tm_mon + 1); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW), tm->tm_wday); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM), tm->tm_mday); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU), tm->tm_hour); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN), tm->tm_min); + mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC), tm->tm_sec); +} + +static irqreturn_t mtk_rtc_alarmirq(int irq, void *id) +{ + struct mtk_rtc *hw = (struct mtk_rtc *)id; + u32 irq_sta; + + irq_sta = mtk_r32(hw, MTK_RTC_INT); + if (irq_sta & RTC_INT_AL_STA) { + /* Stop alarm also implicitly disables the alarm interrupt */ + mtk_w32(hw, MTK_RTC_AL_CTL, 0); + rtc_update_irq(hw->rtc, 1, RTC_IRQF | RTC_AF); + + /* Ack alarm interrupt status */ + mtk_w32(hw, MTK_RTC_INT, RTC_INT_AL_STA); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC); + + return 0; +} + +static int mtk_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (!MTK_RTC_TM_YR_VALID(tm->tm_year)) + return -EINVAL; + + /* Stop time counter before setting a new one*/ + mtk_set(hw, MTK_RTC_CTL, RTC_RC_STOP); + + mtk_rtc_set_alarm_or_time(hw, tm, MTK_TC); + + /* Restart the time counter */ + mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); + + return 0; +} + +static int mtk_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + + mtk_rtc_get_alarm_or_time(hw, alrm_tm, MTK_AL); + + wkalrm->enabled = !!(mtk_r32(hw, MTK_RTC_AL_CTL) & RTC_AL_EN); + wkalrm->pending = !!(mtk_r32(hw, MTK_RTC_INT) & RTC_INT_AL_STA); + + return 0; +} + +static int mtk_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + struct rtc_time *alrm_tm = &wkalrm->time; + + if (!MTK_RTC_TM_YR_VALID(alrm_tm->tm_year)) + return -EINVAL; + + /* + * Stop the alarm also implicitly including disables interrupt before + * setting a new one. + */ + mtk_clr(hw, MTK_RTC_AL_CTL, RTC_AL_EN); + + /* + * Avoid contention between mtk_rtc_setalarm and IRQ handler so that + * disabling the interrupt and awaiting for pending IRQ handler to + * complete. + */ + synchronize_irq(hw->irq); + + mtk_rtc_set_alarm_or_time(hw, alrm_tm, MTK_AL); + + /* Restart the alarm with the new setup */ + mtk_w32(hw, MTK_RTC_AL_CTL, RTC_AL_ALL); + + return 0; +} + +static const struct rtc_class_ops mtk_rtc_ops = { + .read_time = mtk_rtc_gettime, + .set_time = mtk_rtc_settime, + .read_alarm = mtk_rtc_getalarm, + .set_alarm = mtk_rtc_setalarm, +}; + +static const struct of_device_id mtk_rtc_match[] = { + { .compatible = "mediatek,mt7622-rtc" }, + { .compatible = "mediatek,soc-rtc" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_rtc_match); + +static int mtk_rtc_probe(struct platform_device *pdev) +{ + struct mtk_rtc *hw; + struct resource *res; + int ret; + + hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + platform_set_drvdata(pdev, hw); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hw->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hw->base)) + return PTR_ERR(hw->base); + + hw->clk = devm_clk_get(&pdev->dev, "rtc"); + if (IS_ERR(hw->clk)) { + dev_err(&pdev->dev, "No clock\n"); + return PTR_ERR(hw->clk); + } + + ret = clk_prepare_enable(hw->clk); + if (ret) + return ret; + + hw->irq = platform_get_irq(pdev, 0); + if (hw->irq < 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + ret = hw->irq; + goto err; + } + + ret = devm_request_irq(&pdev->dev, hw->irq, mtk_rtc_alarmirq, + 0, dev_name(&pdev->dev), hw); + if (ret) { + dev_err(&pdev->dev, "Can't request IRQ\n"); + goto err; + } + + mtk_rtc_hw_init(hw); + + device_init_wakeup(&pdev->dev, true); + + hw->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &mtk_rtc_ops, THIS_MODULE); + if (IS_ERR(hw->rtc)) { + ret = PTR_ERR(hw->rtc); + dev_err(&pdev->dev, "Unable to register device\n"); + goto err; + } + + return 0; +err: + clk_disable_unprepare(hw->clk); + + return ret; +} + +static int mtk_rtc_remove(struct platform_device *pdev) +{ + struct mtk_rtc *hw = platform_get_drvdata(pdev); + + clk_disable_unprepare(hw->clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mtk_rtc_suspend(struct device *dev) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(hw->irq); + + return 0; +} + +static int mtk_rtc_resume(struct device *dev) +{ + struct mtk_rtc *hw = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(hw->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume); + +#define MTK_RTC_PM_OPS (&mtk_rtc_pm_ops) +#else /* CONFIG_PM */ +#define MTK_RTC_PM_OPS NULL +#endif /* CONFIG_PM */ + +static struct platform_driver mtk_rtc_driver = { + .probe = mtk_rtc_probe, + .remove = mtk_rtc_remove, + .driver = { + .name = MTK_RTC_DEV, + .of_match_table = mtk_rtc_match, + .pm = MTK_RTC_PM_OPS, + }, +}; + +module_platform_driver(mtk_rtc_driver); + +MODULE_DESCRIPTION("MediaTek SoC based RTC Driver"); +MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c new file mode 100644 index 000000000..4b198b377 --- /dev/null +++ b/drivers/rtc/rtc-mv.c @@ -0,0 +1,330 @@ +/* + * 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/bitops.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 BIT(22) /* 12 hour 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 BIT(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 hour 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 0; +} + +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 hour 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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; + 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 hour mode is enabled */ + rtc_time = readl(pdata->ioaddr + RTC_TIME_REG_OFFS); + if (rtc_time & RTC_HOURS_12H_MODE) { + dev_err(&pdev->dev, "12 Hour mode is enabled but 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; + } + } + + 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", }, + {} +}; +MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table); +#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..878c6ee82 --- /dev/null +++ b/drivers/rtc/rtc-mxc.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved. + +#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> +#include <linux/of.h> +#include <linux/of_device.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 + +#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_ref; + struct clk *clk_ipg; + struct rtc_time g_rtc_alarm; + enum imx_rtc_type devtype; +}; + +static const 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); + +#ifdef CONFIG_OF +static const struct of_device_id imx_rtc_dt_ids[] = { + { .compatible = "fsl,imx1-rtc", .data = (const void *)IMX1_RTC }, + { .compatible = "fsl,imx21-rtc", .data = (const void *)IMX21_RTC }, + {} +}; +MODULE_DEVICE_TABLE(of, imx_rtc_dt_ids); +#endif + +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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 & 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; +} + +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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + /* + * 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 const struct rtc_class_ops mxc_rtc_ops = { + .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; + const struct of_device_id *of_id; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + of_id = of_match_device(imx_rtc_dt_ids, &pdev->dev); + if (of_id) + pdata->devtype = (enum imx_rtc_type)of_id->data; + else + 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_ipg = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(pdata->clk_ipg)) { + dev_err(&pdev->dev, "unable to get ipg clock!\n"); + return PTR_ERR(pdata->clk_ipg); + } + + ret = clk_prepare_enable(pdata->clk_ipg); + if (ret) + return ret; + + pdata->clk_ref = devm_clk_get(&pdev->dev, "ref"); + if (IS_ERR(pdata->clk_ref)) { + dev_err(&pdev->dev, "unable to get ref clock!\n"); + ret = PTR_ERR(pdata->clk_ref); + goto exit_put_clk_ipg; + } + + ret = clk_prepare_enable(pdata->clk_ref); + if (ret) + goto exit_put_clk_ipg; + + rate = clk_get_rate(pdata->clk_ref); + + 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_ref; + } + + 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_ref; + } + + 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_ref; + } + + pdata->rtc = rtc; + + return 0; + +exit_put_clk_ref: + clk_disable_unprepare(pdata->clk_ref); +exit_put_clk_ipg: + clk_disable_unprepare(pdata->clk_ipg); + + 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_ref); + clk_disable_unprepare(pdata->clk_ipg); + + 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", + .of_match_table = of_match_ptr(imx_rtc_dt_ids), + .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-mxc_v2.c b/drivers/rtc/rtc-mxc_v2.c new file mode 100644 index 000000000..45c7366b7 --- /dev/null +++ b/drivers/rtc/rtc-mxc_v2.c @@ -0,0 +1,414 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Real Time Clock (RTC) Driver for i.MX53 + * Copyright (c) 2004-2011 Freescale Semiconductor, Inc. + * Copyright (c) 2017 Beckhoff Automation GmbH & Co. KG + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#define SRTC_LPPDR_INIT 0x41736166 /* init for glitch detect */ + +#define SRTC_LPCR_EN_LP BIT(3) /* lp enable */ +#define SRTC_LPCR_WAE BIT(4) /* lp wakeup alarm enable */ +#define SRTC_LPCR_ALP BIT(7) /* lp alarm flag */ +#define SRTC_LPCR_NSA BIT(11) /* lp non secure access */ +#define SRTC_LPCR_NVE BIT(14) /* lp non valid state exit bit */ +#define SRTC_LPCR_IE BIT(15) /* lp init state exit bit */ + +#define SRTC_LPSR_ALP BIT(3) /* lp alarm flag */ +#define SRTC_LPSR_NVES BIT(14) /* lp non-valid state exit status */ +#define SRTC_LPSR_IES BIT(15) /* lp init state exit status */ + +#define SRTC_LPSCMR 0x00 /* LP Secure Counter MSB Reg */ +#define SRTC_LPSCLR 0x04 /* LP Secure Counter LSB Reg */ +#define SRTC_LPSAR 0x08 /* LP Secure Alarm Reg */ +#define SRTC_LPCR 0x10 /* LP Control Reg */ +#define SRTC_LPSR 0x14 /* LP Status Reg */ +#define SRTC_LPPDR 0x18 /* LP Power Supply Glitch Detector Reg */ + +/* max. number of retries to read registers, 120 was max during test */ +#define REG_READ_TIMEOUT 2000 + +struct mxc_rtc_data { + struct rtc_device *rtc; + void __iomem *ioaddr; + struct clk *clk; + spinlock_t lock; /* protects register access */ + int irq; +}; + +/* + * This function does write synchronization for writes to the lp srtc block. + * To take care of the asynchronous CKIL clock, all writes from the IP domain + * will be synchronized to the CKIL domain. + * The caller should hold the pdata->lock + */ +static void mxc_rtc_sync_lp_locked(struct device *dev, void __iomem *ioaddr) +{ + unsigned int i; + + /* Wait for 3 CKIL cycles */ + for (i = 0; i < 3; i++) { + const u32 count = readl(ioaddr + SRTC_LPSCLR); + unsigned int timeout = REG_READ_TIMEOUT; + + while ((readl(ioaddr + SRTC_LPSCLR)) == count) { + if (!--timeout) { + dev_err_once(dev, "SRTC_LPSCLR stuck! Check your hw.\n"); + return; + } + } + } +} + +/* This function is the RTC interrupt service routine. */ +static irqreturn_t mxc_rtc_interrupt(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + unsigned long flags; + u32 lp_status; + u32 lp_cr; + + spin_lock_irqsave(&pdata->lock, flags); + if (clk_enable(pdata->clk)) { + spin_unlock_irqrestore(&pdata->lock, flags); + return IRQ_NONE; + } + + lp_status = readl(ioaddr + SRTC_LPSR); + lp_cr = readl(ioaddr + SRTC_LPCR); + + /* update irq data & counter */ + if (lp_status & SRTC_LPSR_ALP) { + if (lp_cr & SRTC_LPCR_ALP) + rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); + + /* disable further lp alarm interrupts */ + lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE); + } + + /* Update interrupt enables */ + writel(lp_cr, ioaddr + SRTC_LPCR); + + /* clear interrupt status */ + writel(lp_status, ioaddr + SRTC_LPSR); + + mxc_rtc_sync_lp_locked(dev, ioaddr); + clk_disable(pdata->clk); + spin_unlock_irqrestore(&pdata->lock, flags); + return IRQ_HANDLED; +} + +/* + * Enable clk and aquire spinlock + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_lock(struct mxc_rtc_data *const pdata) +{ + int ret; + + spin_lock_irq(&pdata->lock); + ret = clk_enable(pdata->clk); + if (ret) { + spin_unlock_irq(&pdata->lock); + return ret; + } + return 0; +} + +static int mxc_rtc_unlock(struct mxc_rtc_data *const pdata) +{ + clk_disable(pdata->clk); + spin_unlock_irq(&pdata->lock); + return 0; +} + +/* + * This function reads the current RTC time into tm in Gregorian date. + * + * @param tm contains the RTC time value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + const int clk_failed = clk_enable(pdata->clk); + + if (!clk_failed) { + const time64_t now = readl(pdata->ioaddr + SRTC_LPSCMR); + + rtc_time64_to_tm(now, tm); + clk_disable(pdata->clk); + return 0; + } + return clk_failed; +} + +/* + * This function sets the internal RTC time based on tm in Gregorian date. + * + * @param tm the time value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + time64_t time = rtc_tm_to_time64(tm); + int ret; + + ret = mxc_rtc_lock(pdata); + if (ret) + return ret; + + writel(time, pdata->ioaddr + SRTC_LPSCMR); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + return mxc_rtc_unlock(pdata); +} + +/* + * This function reads the current alarm value into the passed in \b alrm + * argument. It updates the \b alrm's pending field value based on the whether + * an alarm interrupt occurs or not. + * + * @param alrm contains the RTC alarm value upon return + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + void __iomem *ioaddr = pdata->ioaddr; + int ret; + + ret = mxc_rtc_lock(pdata); + if (ret) + return ret; + + rtc_time64_to_tm(readl(ioaddr + SRTC_LPSAR), &alrm->time); + alrm->pending = !!(readl(ioaddr + SRTC_LPSR) & SRTC_LPSR_ALP); + return mxc_rtc_unlock(pdata); +} + +/* + * Enable/Disable alarm interrupt + * The caller should hold the pdata->lock + */ +static void mxc_rtc_alarm_irq_enable_locked(struct mxc_rtc_data *pdata, + unsigned int enable) +{ + u32 lp_cr = readl(pdata->ioaddr + SRTC_LPCR); + + if (enable) + lp_cr |= (SRTC_LPCR_ALP | SRTC_LPCR_WAE); + else + lp_cr &= ~(SRTC_LPCR_ALP | SRTC_LPCR_WAE); + + writel(lp_cr, pdata->ioaddr + SRTC_LPCR); +} + +static int mxc_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + int ret = mxc_rtc_lock(pdata); + + if (ret) + return ret; + + mxc_rtc_alarm_irq_enable_locked(pdata, enable); + return mxc_rtc_unlock(pdata); +} + +/* + * This function sets the RTC alarm based on passed in alrm. + * + * @param alrm the alarm value to be set in the RTC + * + * @return 0 if successful; non-zero otherwise. + */ +static int mxc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + const time64_t time = rtc_tm_to_time64(&alrm->time); + struct mxc_rtc_data *pdata = dev_get_drvdata(dev); + int ret = mxc_rtc_lock(pdata); + + if (ret) + return ret; + + writel((u32)time, pdata->ioaddr + SRTC_LPSAR); + + /* clear alarm interrupt status bit */ + writel(SRTC_LPSR_ALP, pdata->ioaddr + SRTC_LPSR); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + + mxc_rtc_alarm_irq_enable_locked(pdata, alrm->enabled); + mxc_rtc_sync_lp_locked(dev, pdata->ioaddr); + mxc_rtc_unlock(pdata); + return ret; +} + +static const struct rtc_class_ops mxc_rtc_ops = { + .read_time = mxc_rtc_read_time, + .set_time = mxc_rtc_set_time, + .read_alarm = mxc_rtc_read_alarm, + .set_alarm = mxc_rtc_set_alarm, + .alarm_irq_enable = mxc_rtc_alarm_irq_enable, +}; + +static int mxc_rtc_wait_for_flag(void __iomem *ioaddr, int flag) +{ + unsigned int timeout = REG_READ_TIMEOUT; + + while (!(readl(ioaddr) & flag)) { + if (!--timeout) + return -EBUSY; + } + return 0; +} + +static int mxc_rtc_probe(struct platform_device *pdev) +{ + struct mxc_rtc_data *pdata; + struct resource *res; + 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); + pdata->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->ioaddr)) + return PTR_ERR(pdata->ioaddr); + + ioaddr = pdata->ioaddr; + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "unable to get rtc clock!\n"); + return PTR_ERR(pdata->clk); + } + + spin_lock_init(&pdata->lock); + pdata->irq = platform_get_irq(pdev, 0); + if (pdata->irq < 0) + return pdata->irq; + + device_init_wakeup(&pdev->dev, 1); + + ret = clk_prepare_enable(pdata->clk); + if (ret) + return ret; + /* initialize glitch detect */ + writel(SRTC_LPPDR_INIT, ioaddr + SRTC_LPPDR); + + /* clear lp interrupt status */ + writel(0xFFFFFFFF, ioaddr + SRTC_LPSR); + + /* move out of init state */ + writel((SRTC_LPCR_IE | SRTC_LPCR_NSA), ioaddr + SRTC_LPCR); + ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_IES); + if (ret) { + dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_IES\n"); + clk_disable_unprepare(pdata->clk); + return ret; + } + + /* move out of non-valid state */ + writel((SRTC_LPCR_IE | SRTC_LPCR_NVE | SRTC_LPCR_NSA | + SRTC_LPCR_EN_LP), ioaddr + SRTC_LPCR); + ret = mxc_rtc_wait_for_flag(ioaddr + SRTC_LPSR, SRTC_LPSR_NVES); + if (ret) { + dev_err(&pdev->dev, "Timeout waiting for SRTC_LPSR_NVES\n"); + clk_disable_unprepare(pdata->clk); + return ret; + } + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &mxc_rtc_ops; + pdata->rtc->range_max = U32_MAX; + + clk_disable(pdata->clk); + platform_set_drvdata(pdev, pdata); + ret = + devm_request_irq(&pdev->dev, pdata->irq, mxc_rtc_interrupt, 0, + pdev->name, &pdev->dev); + if (ret < 0) { + dev_err(&pdev->dev, "interrupt not available.\n"); + clk_unprepare(pdata->clk); + return ret; + } + + ret = rtc_register_device(pdata->rtc); + if (ret < 0) + clk_unprepare(pdata->clk); + + return ret; +} + +static int mxc_rtc_remove(struct platform_device *pdev) +{ + struct mxc_rtc_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 mxc_rtc_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 mxc_rtc_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 const struct of_device_id mxc_ids[] = { + { .compatible = "fsl,imx53-rtc", }, + {} +}; +MODULE_DEVICE_TABLE(of, mxc_ids); + +static struct platform_driver mxc_rtc_driver = { + .driver = { + .name = "mxc_rtc_v2", + .of_match_table = mxc_ids, + .pm = &mxc_rtc_pm_ops, + }, + .probe = mxc_rtc_probe, + .remove = mxc_rtc_remove, +}; + +module_platform_driver(mxc_rtc_driver); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Real Time Clock (RTC) Driver for i.MX53"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-nuc900.c b/drivers/rtc/rtc-nuc900.c new file mode 100644 index 000000000..7da664a77 --- /dev/null +++ b/drivers/rtc/rtc-nuc900.c @@ -0,0 +1,275 @@ +/* + * 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 void 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); +} + +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); + + nuc900_rtc_bcd2bin(timeval, clrval, tm); + + return 0; +} + +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); + + nuc900_rtc_bcd2bin(timeval, carval, &alrm->time); + + return rtc_valid_tm(&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 const 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..080211dd9 --- /dev/null +++ b/drivers/rtc/rtc-omap.c @@ -0,0 +1,1020 @@ +/* + * 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 <dt-bindings/gpio/gpio.h> +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinconf-generic.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/rtc.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_SCRATCH0_REG 0x60 +#define OMAP_RTC_SCRATCH1_REG 0x64 +#define OMAP_RTC_SCRATCH2_REG 0x68 + +#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) +#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) +#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) + +/* 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) +#define OMAP_RTC_PMIC_EXT_WKUP_EN(x) BIT(x) +#define OMAP_RTC_PMIC_EXT_WKUP_POL(x) BIT(4 + x) + +/* 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; + struct clk *clk; + int irq_alarm; + int irq_timer; + u8 interrupts_reg; + bool is_pmic_controller; + bool has_ext_clk; + bool is_suspending; + const struct omap_rtc_device_type *type; + struct pinctrl_dev *pctldev; +}; + +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) +{ + 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"); + rtc->type->lock(rtc); + 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 const 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 const struct pinctrl_pin_desc rtc_pins_desc[] = { + PINCTRL_PIN(0, "ext_wakeup0"), + PINCTRL_PIN(1, "ext_wakeup1"), + PINCTRL_PIN(2, "ext_wakeup2"), + PINCTRL_PIN(3, "ext_wakeup3"), +}; + +static int rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + return 0; +} + +static const char *rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group) +{ + return NULL; +} + +static const struct pinctrl_ops rtc_pinctrl_ops = { + .get_groups_count = rtc_pinctrl_get_groups_count, + .get_group_name = rtc_pinctrl_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +#define PIN_CONFIG_ACTIVE_HIGH (PIN_CONFIG_END + 1) + +static const struct pinconf_generic_params rtc_params[] = { + {"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0}, +}; + +#ifdef CONFIG_DEBUG_FS +static const struct pin_config_item rtc_conf_items[ARRAY_SIZE(rtc_params)] = { + PCONFDUMP(PIN_CONFIG_ACTIVE_HIGH, "input active high", NULL, false), +}; +#endif + +static int rtc_pinconf_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + unsigned int param = pinconf_to_config_param(*config); + u32 val; + u16 arg = 0; + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + if (!(val & OMAP_RTC_PMIC_EXT_WKUP_EN(pin))) + return -EINVAL; + break; + case PIN_CONFIG_ACTIVE_HIGH: + if (val & OMAP_RTC_PMIC_EXT_WKUP_POL(pin)) + return -EINVAL; + break; + default: + return -ENOTSUPP; + }; + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static int rtc_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); + u32 val; + unsigned int param; + u32 param_val; + int i; + + val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); + + /* active low by default */ + val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin); + + for (i = 0; i < num_configs; i++) { + param = pinconf_to_config_param(configs[i]); + param_val = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_INPUT_ENABLE: + if (param_val) + val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin); + else + val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin); + break; + case PIN_CONFIG_ACTIVE_HIGH: + val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin); + break; + default: + dev_err(&rtc->rtc->dev, "Property %u not supported\n", + param); + return -ENOTSUPP; + } + } + + rtc->type->unlock(rtc); + rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); + rtc->type->lock(rtc); + + return 0; +} + +static const struct pinconf_ops rtc_pinconf_ops = { + .is_generic = true, + .pin_config_get = rtc_pinconf_get, + .pin_config_set = rtc_pinconf_set, +}; + +static struct pinctrl_desc rtc_pinctrl_desc = { + .pins = rtc_pins_desc, + .npins = ARRAY_SIZE(rtc_pins_desc), + .pctlops = &rtc_pinctrl_ops, + .confops = &rtc_pinconf_ops, + .custom_params = rtc_params, + .num_custom_params = ARRAY_SIZE(rtc_params), +#ifdef CONFIG_DEBUG_FS + .custom_conf_items = rtc_conf_items, +#endif + .owner = THIS_MODULE, +}; + +static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val, + size_t bytes) +{ + struct omap_rtc *rtc = priv; + u32 *val = _val; + int i; + + for (i = 0; i < bytes / 4; i++) + val[i] = rtc_readl(rtc, + OMAP_RTC_SCRATCH0_REG + offset + (i * 4)); + + return 0; +} + +static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val, + size_t bytes) +{ + struct omap_rtc *rtc = priv; + u32 *val = _val; + int i; + + rtc->type->unlock(rtc); + for (i = 0; i < bytes / 4; i++) + rtc_writel(rtc, + OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]); + rtc->type->lock(rtc); + + return 0; +} + +static struct nvmem_config omap_rtc_nvmem_config = { + .name = "omap_rtc_scratch", + .word_size = 4, + .stride = 4, + .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG, + .reg_read = omap_rtc_scratch_read, + .reg_write = omap_rtc_scratch_write, +}; + +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; + + rtc->clk = devm_clk_get(&pdev->dev, "ext-clk"); + if (!IS_ERR(rtc->clk)) + rtc->has_ext_clk = true; + else + rtc->clk = devm_clk_get(&pdev->dev, "int-clk"); + + if (!IS_ERR(rtc->clk)) + clk_prepare_enable(rtc->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtc->base)) { + clk_disable_unprepare(rtc->clk); + 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); + + /* + * If we have the external clock then switch to it so we can keep + * ticking across suspend. + */ + if (rtc->has_ext_clk) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE; + reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC; + rtc_writel(rtc, OMAP_RTC_OSC_REG, reg); + } + + rtc->type->lock(rtc); + + device_init_wakeup(&pdev->dev, true); + + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err; + } + + rtc->rtc->ops = &omap_rtc_ops; + omap_rtc_nvmem_config.priv = rtc; + + /* 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; + } + + /* Support ext_wakeup pinconf */ + rtc_pinctrl_desc.name = dev_name(&pdev->dev); + + rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc); + if (IS_ERR(rtc->pctldev)) { + dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); + ret = PTR_ERR(rtc->pctldev); + goto err; + } + + ret = rtc_register_device(rtc->rtc); + if (ret) + goto err_deregister_pinctrl; + + rtc_nvmem_register(rtc->rtc, &omap_rtc_nvmem_config); + + 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_deregister_pinctrl: + pinctrl_unregister(rtc->pctldev); +err: + clk_disable_unprepare(rtc->clk); + 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 omap_rtc_remove(struct platform_device *pdev) +{ + struct omap_rtc *rtc = platform_get_drvdata(pdev); + u8 reg; + + 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); + + if (!IS_ERR(rtc->clk)) + clk_disable_unprepare(rtc->clk); + + rtc->type->unlock(rtc); + /* leave rtc running, but disable irqs */ + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); + + if (rtc->has_ext_clk) { + reg = rtc_read(rtc, OMAP_RTC_OSC_REG); + reg &= ~OMAP_RTC_OSC_SEL_32KCLK_SRC; + rtc_write(rtc, OMAP_RTC_OSC_REG, reg); + } + + rtc->type->lock(rtc); + + /* Disable the clock/module */ + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + /* Remove ext_wakeup pinconf */ + pinctrl_unregister(rtc->pctldev); + + return 0; +} + +static int __maybe_unused 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); + + rtc->is_suspending = true; + + return 0; +} + +static int __maybe_unused omap_rtc_resume(struct device *dev) +{ + struct omap_rtc *rtc = dev_get_drvdata(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); + + rtc->is_suspending = false; + + return 0; +} + +static int __maybe_unused omap_rtc_runtime_suspend(struct device *dev) +{ + struct omap_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->is_suspending && !rtc->has_ext_clk) + return -EBUSY; + + return 0; +} + +static const struct dev_pm_ops omap_rtc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(omap_rtc_suspend, omap_rtc_resume) + SET_RUNTIME_PM_OPS(omap_rtc_runtime_suspend, NULL, NULL) +}; + +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 = 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..60f2250fd --- /dev/null +++ b/drivers/rtc/rtc-opal.c @@ -0,0 +1,310 @@ +/* + * 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); + + tm->tm_wday = -1; +} + +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) +{ + s64 rc = OPAL_BUSY; + int retries = 10; + 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) { + msleep(OPAL_BUSY_DELAY_MS); + opal_poll_events(NULL); + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) { + if (retries--) { + msleep(10); /* Wait 10ms before retry */ + rc = OPAL_BUSY; /* go around again */ + } + } + } + + 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) +{ + s64 rc = OPAL_BUSY; + int retries = 10; + 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) { + msleep(OPAL_BUSY_DELAY_MS); + opal_poll_events(NULL); + } else if (rc == OPAL_BUSY) { + msleep(OPAL_BUSY_DELAY_MS); + } else if (rc == OPAL_HARDWARE || rc == OPAL_INTERNAL_ERROR) { + if (retries--) { + msleep(10); /* Wait 10ms before retry */ + rc = OPAL_BUSY; /* go around again */ + } + } + } + + 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 = opal_get_async_rc(msg); + 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); + + /* check if no alarm is set */ + if (y_m_d == 0 && h_m_s_ms == 0) { + pr_debug("No alarm is set\n"); + rc = -ENOENT; + goto exit; + } else { + pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms); + } + + 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; + struct opal_msg msg; + u32 y_m_d = 0; + int token, rc; + + /* if alarm is enabled */ + if (alarm->enabled) { + tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms); + pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms); + + } else { + pr_debug("Alarm getting disabled\n"); + } + + 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 = opal_get_async_rc(msg); + if (rc != OPAL_SUCCESS) + rc = -EIO; + +exit: + opal_async_release_token(token); + return rc; +} + +int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc_wkalrm alarm = { .enabled = 0 }; + + /* + * TPO is automatically enabled when opal_set_tpo_time() is called with + * non-zero rtc-time. We only handle disable case which needs to be + * explicitly told to opal. + */ + return enabled ? 0 : opal_set_tpo_time(dev, &alarm); +} + +static struct rtc_class_ops opal_rtc_ops = { + .read_time = opal_get_rtc_time, + .set_time = opal_set_rtc_time, +}; + +static int opal_rtc_probe(struct platform_device *pdev) +{ + struct rtc_device *rtc; + + if (pdev->dev.of_node && + (of_property_read_bool(pdev->dev.of_node, "wakeup-source") || + of_property_read_bool(pdev->dev.of_node, "has-tpo")/* legacy */)) { + device_set_wakeup_capable(&pdev->dev, true); + opal_rtc_ops.read_alarm = opal_get_tpo_time; + opal_rtc_ops.set_alarm = opal_set_tpo_time; + opal_rtc_ops.alarm_irq_enable = opal_tpo_alarm_irq_enable; + } + + 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, + .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..4bcfb8867 --- /dev/null +++ b/drivers/rtc/rtc-palmas.c @@ -0,0 +1,376 @@ +/* + * 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 const 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 = false; + + 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, + 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..f176cb9d0 --- /dev/null +++ b/drivers/rtc/rtc-pcap.c @@ -0,0 +1,189 @@ +/* + * 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 pcap_rtc *pcap_rtc = dev_get_drvdata(dev); + 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 pcap_rtc *pcap_rtc = dev_get_drvdata(dev); + 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 pcap_rtc *pcap_rtc = dev_get_drvdata(dev); + 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 0; +} + +static int pcap_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct pcap_rtc *pcap_rtc = dev_get_drvdata(dev); + 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 pcap_rtc *pcap_rtc = dev_get_drvdata(dev); + + 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..e5222c5d8 --- /dev/null +++ b/drivers/rtc/rtc-pcf2123.c @@ -0,0 +1,474 @@ +/* + * 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> + +/* REGISTERS */ +#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_REG_ALRM_MN (0x09) /* Alarm Registers */ +#define PCF2123_REG_ALRM_HR (0x0a) +#define PCF2123_REG_ALRM_DM (0x0b) +#define PCF2123_REG_ALRM_DW (0x0c) +#define PCF2123_REG_OFFSET (0x0d) /* Clock Rate Offset Register */ +#define PCF2123_REG_TMR_CLKOUT (0x0e) /* Timer Registers */ +#define PCF2123_REG_CTDWN_TMR (0x0f) + +/* PCF2123_REG_CTRL1 BITS */ +#define CTRL1_CLEAR (0) /* Clear */ +#define CTRL1_CORR_INT BIT(1) /* Correction irq enable */ +#define CTRL1_12_HOUR BIT(2) /* 12 hour time */ +#define CTRL1_SW_RESET (BIT(3) | BIT(4) | BIT(6)) /* Software reset */ +#define CTRL1_STOP BIT(5) /* Stop the clock */ +#define CTRL1_EXT_TEST BIT(7) /* External clock test mode */ + +/* PCF2123_REG_CTRL2 BITS */ +#define CTRL2_TIE BIT(0) /* Countdown timer irq enable */ +#define CTRL2_AIE BIT(1) /* Alarm irq enable */ +#define CTRL2_TF BIT(2) /* Countdown timer flag */ +#define CTRL2_AF BIT(3) /* Alarm flag */ +#define CTRL2_TI_TP BIT(4) /* Irq pin generates pulse */ +#define CTRL2_MSF BIT(5) /* Minute or second irq flag */ +#define CTRL2_SI BIT(6) /* Second irq enable */ +#define CTRL2_MI BIT(7) /* Minute irq enable */ + +/* PCF2123_REG_SC BITS */ +#define OSC_HAS_STOPPED BIT(7) /* Clock has been stopped */ + +/* PCF2123_REG_ALRM_XX BITS */ +#define ALRM_ENABLE BIT(7) /* MN, HR, DM, or DW alarm enable */ + +/* PCF2123_REG_TMR_CLKOUT BITS */ +#define CD_TMR_4096KHZ (0) /* 4096 KHz countdown timer */ +#define CD_TMR_64HZ (1) /* 64 Hz countdown timer */ +#define CD_TMR_1HZ (2) /* 1 Hz countdown timer */ +#define CD_TMR_60th_HZ (3) /* 60th Hz countdown timer */ +#define CD_TMR_TE BIT(3) /* Countdown timer enable */ + +/* PCF2123_REG_OFFSET BITS */ +#define OFFSET_SIGN_BIT 6 /* 2's complement sign bit */ +#define OFFSET_COARSE BIT(7) /* Coarse mode offset */ +#define OFFSET_STEP (2170) /* Offset step in parts per billion */ + +/* READ/WRITE ADDRESS BITS */ +#define PCF2123_WRITE BIT(4) +#define PCF2123_READ (BIT(4) | BIT(7)) + + +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 int pcf2123_read(struct device *dev, u8 reg, u8 *rxbuf, size_t size) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + reg |= PCF2123_READ; + ret = spi_write_then_read(spi, ®, 1, rxbuf, size); + pcf2123_delay_trec(); + + return ret; +} + +static int pcf2123_write(struct device *dev, u8 *txbuf, size_t size) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + txbuf[0] |= PCF2123_WRITE; + ret = spi_write(spi, txbuf, size); + pcf2123_delay_trec(); + + return ret; +} + +static int pcf2123_write_reg(struct device *dev, u8 reg, u8 val) +{ + u8 txbuf[2]; + + txbuf[0] = reg; + txbuf[1] = val; + return pcf2123_write(dev, txbuf, sizeof(txbuf)); +} + +static ssize_t pcf2123_show(struct device *dev, struct device_attribute *attr, + char *buffer) +{ + struct pcf2123_sysfs_reg *r; + u8 rxbuf[1]; + unsigned long reg; + int ret; + + r = container_of(attr, struct pcf2123_sysfs_reg, attr); + + ret = kstrtoul(r->name, 16, ®); + if (ret) + return ret; + + ret = pcf2123_read(dev, reg, rxbuf, 1); + if (ret < 0) + return -EIO; + + 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 pcf2123_sysfs_reg *r; + unsigned long reg; + unsigned long val; + + int ret; + + r = container_of(attr, struct pcf2123_sysfs_reg, attr); + + ret = kstrtoul(r->name, 16, ®); + if (ret) + return ret; + + ret = kstrtoul(buffer, 10, &val); + if (ret) + return ret; + + ret = pcf2123_write_reg(dev, reg, val); + if (ret < 0) + return -EIO; + return count; +} + +static int pcf2123_read_offset(struct device *dev, long *offset) +{ + int ret; + s8 reg; + + ret = pcf2123_read(dev, PCF2123_REG_OFFSET, ®, 1); + if (ret < 0) + return ret; + + if (reg & OFFSET_COARSE) + reg <<= 1; /* multiply by 2 and sign extend */ + else + reg = sign_extend32(reg, OFFSET_SIGN_BIT); + + *offset = ((long)reg) * OFFSET_STEP; + + return 0; +} + +/* + * The offset register is a 7 bit signed value with a coarse bit in bit 7. + * The main difference between the two is normal offset adjusts the first + * second of n minutes every other hour, with 61, 62 and 63 being shoved + * into the 60th minute. + * The coarse adjustment does the same, but every hour. + * the two overlap, with every even normal offset value corresponding + * to a coarse offset. Based on this algorithm, it seems that despite the + * name, coarse offset is a better fit for overlapping values. + */ +static int pcf2123_set_offset(struct device *dev, long offset) +{ + s8 reg; + + if (offset > OFFSET_STEP * 127) + reg = 127; + else if (offset < OFFSET_STEP * -128) + reg = -128; + else + reg = (s8)((offset + (OFFSET_STEP >> 1)) / OFFSET_STEP); + + /* choose fine offset only for odd values in the normal range */ + if (reg & 1 && reg <= 63 && reg >= -64) { + /* Normal offset. Clear the coarse bit */ + reg &= ~OFFSET_COARSE; + } else { + /* Coarse offset. Divide by 2 and set the coarse bit */ + reg >>= 1; + reg |= OFFSET_COARSE; + } + + return pcf2123_write_reg(dev, PCF2123_REG_OFFSET, reg); +} + +static int pcf2123_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 rxbuf[7]; + int ret; + + ret = pcf2123_read(dev, PCF2123_REG_SC, rxbuf, sizeof(rxbuf)); + if (ret < 0) + return ret; + + if (rxbuf[0] & OSC_HAS_STOPPED) { + dev_info(dev, "clock was stopped. Time is not valid\n"); + return -EINVAL; + } + + 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); + + return 0; +} + +static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + 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 */ + ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_STOP); + if (ret < 0) + return ret; + + /* Set the new time */ + txbuf[0] = 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 = pcf2123_write(dev, txbuf, sizeof(txbuf)); + if (ret < 0) + return ret; + + /* Start the counter */ + ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_CLEAR); + if (ret < 0) + return ret; + + return 0; +} + +static int pcf2123_reset(struct device *dev) +{ + int ret; + u8 rxbuf[2]; + + ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_SW_RESET); + if (ret < 0) + return ret; + + /* Stop the counter */ + dev_dbg(dev, "stopping RTC\n"); + ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_STOP); + if (ret < 0) + return ret; + + /* See if the counter was actually stopped */ + dev_dbg(dev, "checking for presence of RTC\n"); + ret = pcf2123_read(dev, PCF2123_REG_CTRL1, rxbuf, sizeof(rxbuf)); + if (ret < 0) + return ret; + + dev_dbg(dev, "received data from RTC (0x%02X 0x%02X)\n", + rxbuf[0], rxbuf[1]); + if (!(rxbuf[0] & CTRL1_STOP)) + return -ENODEV; + + /* Start the counter */ + ret = pcf2123_write_reg(dev, PCF2123_REG_CTRL1, CTRL1_CLEAR); + if (ret < 0) + return ret; + + return 0; +} + +static const struct rtc_class_ops pcf2123_rtc_ops = { + .read_time = pcf2123_rtc_read_time, + .set_time = pcf2123_rtc_set_time, + .read_offset = pcf2123_read_offset, + .set_offset = pcf2123_set_offset, + +}; + +static int pcf2123_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + struct rtc_time tm; + struct pcf2123_plat_data *pdata; + int ret, i; + + pdata = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_plat_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + spi->dev.platform_data = pdata; + + ret = pcf2123_rtc_read_time(&spi->dev, &tm); + if (ret < 0) { + ret = pcf2123_reset(&spi->dev); + if (ret < 0) { + dev_err(&spi->dev, "chip not found\n"); + goto kfree_exit; + } + } + + dev_info(&spi->dev, "spiclk %u KHz.\n", + (spi->max_speed_hz + 500) / 1000); + + /* 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", + .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"); diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c new file mode 100644 index 000000000..8c62406f9 --- /dev/null +++ b/drivers/rtc/rtc-pcf2127.c @@ -0,0 +1,497 @@ +/* + * An I2C and SPI driver for the NXP PCF2127/29 RTC + * Copyright 2013 Til-Technologies + * + * Author: Renaud Cerrato <r.cerrato@til-technologies.fr> + * + * based on the other drivers in this same directory. + * + * Datasheet: http://cache.nxp.com/documents/data_sheet/PCF2127.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/spi/spi.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#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_CTRL3_BLF BIT(2) + +#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) + +/* the pcf2127 has 512 bytes nvmem, pcf2129 doesn't */ +#define PCF2127_REG_RAM_addr_MSB 0x1a +#define PCF2127_REG_RAM_wrt_cmd 0x1c +#define PCF2127_REG_RAM_rd_cmd 0x1d + +#define PCF2127_OSF BIT(7) /* Oscillator Fail flag */ + +struct pcf2127 { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +/* + * 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_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + unsigned char buf[10]; + int ret; + + /* + * Avoid reading CTRL2 register as it causes WD_VAL register + * value to reset to 0 which means watchdog is stopped. + */ + ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_CTRL3, + (buf + PCF2127_REG_CTRL3), + ARRAY_SIZE(buf) - PCF2127_REG_CTRL3); + if (ret) { + dev_err(dev, "%s: read error\n", __func__); + return ret; + } + + if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF) + dev_info(dev, + "low voltage detected, check/replace RTC battery.\n"); + + if (buf[PCF2127_REG_SC] & PCF2127_OSF) { + /* + * no need clear the flag here, + * it will be cleared once the new date is saved + */ + dev_warn(dev, + "oscillator stop detected, date/time is not reliable\n"); + return -EINVAL; + } + + dev_dbg(dev, + "%s: raw data is cr3=%02x, sec=%02x, min=%02x, hr=%02x, " + "mday=%02x, wday=%02x, mon=%02x, year=%02x\n", + __func__, buf[PCF2127_REG_CTRL3], buf[PCF2127_REG_SC], + buf[PCF2127_REG_MN], buf[PCF2127_REG_HR], + buf[PCF2127_REG_DM], buf[PCF2127_REG_DW], + buf[PCF2127_REG_MO], buf[PCF2127_REG_YR]); + + 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(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 pcf2127_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + unsigned char buf[7]; + int i = 0, err; + + 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); + + /* hours, minutes and seconds */ + buf[i++] = bin2bcd(tm->tm_sec); /* this will also clear OSF flag */ + 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 = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_SC, buf, i); + if (err) { + dev_err(dev, + "%s: err=%d", __func__, err); + return err; + } + + return 0; +} + +#ifdef CONFIG_RTC_INTF_DEV +static int pcf2127_rtc_ioctl(struct device *dev, + unsigned int cmd, unsigned long arg) +{ + struct pcf2127 *pcf2127 = dev_get_drvdata(dev); + int touser; + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = regmap_read(pcf2127->regmap, PCF2127_REG_CTRL3, &touser); + if (ret) + return ret; + + touser = touser & PCF2127_REG_CTRL3_BLF ? 1 : 0; + + if (copy_to_user((void __user *)arg, &touser, sizeof(int))) + return -EFAULT; + return 0; + default: + return -ENOIOCTLCMD; + } +} +#else +#define pcf2127_rtc_ioctl NULL +#endif + +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_nvmem_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct pcf2127 *pcf2127 = priv; + int ret; + unsigned char offsetbuf[] = { offset >> 8, offset }; + + ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_addr_MSB, + offsetbuf, 2); + if (ret) + return ret; + + ret = regmap_bulk_read(pcf2127->regmap, PCF2127_REG_RAM_rd_cmd, + val, bytes); + + return ret ?: bytes; +} + +static int pcf2127_nvmem_write(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + struct pcf2127 *pcf2127 = priv; + int ret; + unsigned char offsetbuf[] = { offset >> 8, offset }; + + ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_addr_MSB, + offsetbuf, 2); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf2127->regmap, PCF2127_REG_RAM_wrt_cmd, + val, bytes); + + return ret ?: bytes; +} + +static int pcf2127_probe(struct device *dev, struct regmap *regmap, + const char *name, bool has_nvmem) +{ + struct pcf2127 *pcf2127; + int ret = 0; + + dev_dbg(dev, "%s\n", __func__); + + pcf2127 = devm_kzalloc(dev, sizeof(*pcf2127), GFP_KERNEL); + if (!pcf2127) + return -ENOMEM; + + pcf2127->regmap = regmap; + + dev_set_drvdata(dev, pcf2127); + + pcf2127->rtc = devm_rtc_device_register(dev, name, &pcf2127_rtc_ops, + THIS_MODULE); + if (IS_ERR(pcf2127->rtc)) + return PTR_ERR(pcf2127->rtc); + + if (has_nvmem) { + struct nvmem_config nvmem_cfg = { + .priv = pcf2127, + .reg_read = pcf2127_nvmem_read, + .reg_write = pcf2127_nvmem_write, + .size = 512, + }; + + ret = rtc_nvmem_register(pcf2127->rtc, &nvmem_cfg); + } + + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id pcf2127_of_match[] = { + { .compatible = "nxp,pcf2127" }, + { .compatible = "nxp,pcf2129" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf2127_of_match); +#endif + +#if IS_ENABLED(CONFIG_I2C) + +static int pcf2127_i2c_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(client, data, count); + if (ret != count) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int pcf2127_i2c_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + int ret; + void *buf; + + if (WARN_ON(reg_size != 1)) + return -EINVAL; + + buf = kmalloc(val_size + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, reg, 1); + memcpy(buf + 1, val, val_size); + + ret = i2c_master_send(client, buf, val_size + 1); + + kfree(buf); + + if (ret != val_size + 1) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static int pcf2127_i2c_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + int ret; + + if (WARN_ON(reg_size != 1)) + return -EINVAL; + + ret = i2c_master_send(client, reg, 1); + if (ret != 1) + return ret < 0 ? ret : -EIO; + + ret = i2c_master_recv(client, val, val_size); + if (ret != val_size) + return ret < 0 ? ret : -EIO; + + return 0; +} + +/* + * The reason we need this custom regmap_bus instead of using regmap_init_i2c() + * is that the STOP condition is required between set register address and + * read register data when reading from registers. + */ +static const struct regmap_bus pcf2127_i2c_regmap = { + .write = pcf2127_i2c_write, + .gather_write = pcf2127_i2c_gather_write, + .read = pcf2127_i2c_read, +}; + +static struct i2c_driver pcf2127_i2c_driver; + +static int pcf2127_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + }; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + regmap = devm_regmap_init(&client->dev, &pcf2127_i2c_regmap, + &client->dev, &config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return pcf2127_probe(&client->dev, regmap, + pcf2127_i2c_driver.driver.name, id->driver_data); +} + +static const struct i2c_device_id pcf2127_i2c_id[] = { + { "pcf2127", 1 }, + { "pcf2129", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, pcf2127_i2c_id); + +static struct i2c_driver pcf2127_i2c_driver = { + .driver = { + .name = "rtc-pcf2127-i2c", + .of_match_table = of_match_ptr(pcf2127_of_match), + }, + .probe = pcf2127_i2c_probe, + .id_table = pcf2127_i2c_id, +}; + +static int pcf2127_i2c_register_driver(void) +{ + return i2c_add_driver(&pcf2127_i2c_driver); +} + +static void pcf2127_i2c_unregister_driver(void) +{ + i2c_del_driver(&pcf2127_i2c_driver); +} + +#else + +static int pcf2127_i2c_register_driver(void) +{ + return 0; +} + +static void pcf2127_i2c_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static struct spi_driver pcf2127_spi_driver; + +static int pcf2127_spi_probe(struct spi_device *spi) +{ + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0xa0, + .write_flag_mask = 0x20, + }; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return pcf2127_probe(&spi->dev, regmap, pcf2127_spi_driver.driver.name, + spi_get_device_id(spi)->driver_data); +} + +static const struct spi_device_id pcf2127_spi_id[] = { + { "pcf2127", 1 }, + { "pcf2129", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, pcf2127_spi_id); + +static struct spi_driver pcf2127_spi_driver = { + .driver = { + .name = "rtc-pcf2127-spi", + .of_match_table = of_match_ptr(pcf2127_of_match), + }, + .probe = pcf2127_spi_probe, + .id_table = pcf2127_spi_id, +}; + +static int pcf2127_spi_register_driver(void) +{ + return spi_register_driver(&pcf2127_spi_driver); +} + +static void pcf2127_spi_unregister_driver(void) +{ + spi_unregister_driver(&pcf2127_spi_driver); +} + +#else + +static int pcf2127_spi_register_driver(void) +{ + return 0; +} + +static void pcf2127_spi_unregister_driver(void) +{ +} + +#endif + +static int __init pcf2127_init(void) +{ + int ret; + + ret = pcf2127_i2c_register_driver(); + if (ret) { + pr_err("Failed to register pcf2127 i2c driver: %d\n", ret); + return ret; + } + + ret = pcf2127_spi_register_driver(); + if (ret) { + pr_err("Failed to register pcf2127 spi driver: %d\n", ret); + pcf2127_i2c_unregister_driver(); + } + + return ret; +} +module_init(pcf2127_init) + +static void __exit pcf2127_exit(void) +{ + pcf2127_spi_unregister_driver(); + pcf2127_i2c_unregister_driver(); +} +module_exit(pcf2127_exit) + +MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>"); +MODULE_DESCRIPTION("NXP PCF2127/29 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pcf50633.c b/drivers/rtc/rtc-pcf50633.c new file mode 100644 index 000000000..ef72b0c38 --- /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 0; +} + +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 const 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..283c2335b --- /dev/null +++ b/drivers/rtc/rtc-pcf85063.c @@ -0,0 +1,234 @@ +/* + * 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> + +/* + * Information for this driver was pulled from the following datasheets. + * + * http://www.nxp.com/documents/data_sheet/PCF85063A.pdf + * http://www.nxp.com/documents/data_sheet/PCF85063TP.pdf + * + * PCF85063A -- Rev. 6 — 18 November 2015 + * PCF85063TP -- Rev. 4 — 6 May 2015 +*/ + +#define PCF85063_REG_CTRL1 0x00 /* status */ +#define PCF85063_REG_CTRL1_STOP BIT(5) +#define PCF85063_REG_CTRL2 0x01 + +#define PCF85063_REG_SC 0x04 /* datetime */ +#define PCF85063_REG_SC_OS 0x80 +#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 + +static struct i2c_driver pcf85063_driver; + +static int pcf85063_stop_clock(struct i2c_client *client, u8 *ctrl1) +{ + int rc; + u8 reg; + + rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1); + if (rc < 0) { + dev_err(&client->dev, "Failing to stop the clock\n"); + return -EIO; + } + + /* stop the clock */ + reg = rc | PCF85063_REG_CTRL1_STOP; + + rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg); + if (rc < 0) { + dev_err(&client->dev, "Failing to stop the clock\n"); + return -EIO; + } + + *ctrl1 = reg; + + return 0; +} + +static int pcf85063_start_clock(struct i2c_client *client, u8 ctrl1) +{ + int rc; + + /* start the clock */ + ctrl1 &= ~PCF85063_REG_CTRL1_STOP; + + rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, ctrl1); + if (rc < 0) { + dev_err(&client->dev, "Failing to start the clock\n"); + return -EIO; + } + + return 0; +} + +static int pcf85063_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + u8 regs[7]; + + /* + * while reading, the time/date registers are blocked and not updated + * anymore until the access is finished. To not lose a second + * event, the access must be finished within one second. So, read all + * time/date registers in one turn. + */ + rc = i2c_smbus_read_i2c_block_data(client, PCF85063_REG_SC, + sizeof(regs), regs); + if (rc != sizeof(regs)) { + dev_err(&client->dev, "date/time register read error\n"); + return -EIO; + } + + /* if the clock has lost its power it makes no sense to use its time */ + if (regs[0] & PCF85063_REG_SC_OS) { + dev_warn(&client->dev, "Power loss detected, invalid time\n"); + return -EINVAL; + } + + tm->tm_sec = bcd2bin(regs[0] & 0x7F); + tm->tm_min = bcd2bin(regs[1] & 0x7F); + tm->tm_hour = bcd2bin(regs[2] & 0x3F); /* rtc hr 0-23 */ + tm->tm_mday = bcd2bin(regs[3] & 0x3F); + tm->tm_wday = regs[4] & 0x07; + tm->tm_mon = bcd2bin(regs[5] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(regs[6]); + tm->tm_year += 100; + + return 0; +} + +static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + u8 regs[7]; + u8 ctrl1; + + if ((tm->tm_year < 100) || (tm->tm_year > 199)) + return -EINVAL; + + /* + * to accurately set the time, reset the divider chain and keep it in + * reset state until all time/date registers are written + */ + rc = pcf85063_stop_clock(client, &ctrl1); + if (rc != 0) + return rc; + + /* hours, minutes and seconds */ + regs[0] = bin2bcd(tm->tm_sec) & 0x7F; /* clear OS flag */ + + regs[1] = bin2bcd(tm->tm_min); + regs[2] = bin2bcd(tm->tm_hour); + + /* Day of month, 1 - 31 */ + regs[3] = bin2bcd(tm->tm_mday); + + /* Day, 0 - 6 */ + regs[4] = tm->tm_wday & 0x07; + + /* month, 1 - 12 */ + regs[5] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + regs[6] = bin2bcd(tm->tm_year - 100); + + /* write all registers at once */ + rc = i2c_smbus_write_i2c_block_data(client, PCF85063_REG_SC, + sizeof(regs), regs); + if (rc < 0) { + dev_err(&client->dev, "date/time register write error\n"); + return rc; + } + + /* + * Write the control register as a separate action since the size of + * the register space is different between the PCF85063TP and + * PCF85063A devices. The rollover point can not be used. + */ + rc = pcf85063_start_clock(client, ctrl1); + if (rc != 0) + return rc; + + return 0; +} + +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 rtc_device *rtc; + int err; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + err = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1); + if (err < 0) { + dev_err(&client->dev, "RTC chip is not present\n"); + return err; + } + + rtc = devm_rtc_device_register(&client->dev, + pcf85063_driver.driver.name, + &pcf85063_rtc_ops, THIS_MODULE); + + return PTR_ERR_OR_ZERO(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", + .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"); diff --git a/drivers/rtc/rtc-pcf8523.c b/drivers/rtc/rtc-pcf8523.c new file mode 100644 index 000000000..2e03021f1 --- /dev/null +++ b/drivers/rtc/rtc-pcf8523.c @@ -0,0 +1,406 @@ +/* + * 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 + +#define REG_OFFSET 0x0e +#define REG_OFFSET_MODE BIT(7) + +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 = ® + + 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_voltage_low(struct i2c_client *client) +{ + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL3, &value); + if (err < 0) + return err; + + return !!(value & REG_CONTROL3_BLF); +} + +static int pcf8523_load_capacitance(struct i2c_client *client) +{ + u32 load; + u8 value; + int err; + + err = pcf8523_read(client, REG_CONTROL1, &value); + if (err < 0) + return err; + + load = 12500; + of_property_read_u32(client->dev.of_node, "quartz-load-femtofarads", + &load); + + switch (load) { + default: + dev_warn(&client->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 12500", + load); + /* fall through */ + case 12500: + value |= REG_CONTROL1_CAP_SEL; + break; + case 7000: + value &= ~REG_CONTROL1_CAP_SEL; + break; + } + + err = pcf8523_write(client, REG_CONTROL1, value); + + 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; + + err = pcf8523_voltage_low(client); + if (err < 0) { + return err; + } else if (err > 0) { + dev_err(dev, "low voltage detected, time is unreliable\n"); + return -EINVAL; + } + + 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) + return -EINVAL; + + 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 0; +} + +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; + + /* + * The hardware can only store values between 0 and 99 in it's YEAR + * register (with 99 overflowing to 0 on increment). + * After 2100-02-28 we could start interpreting the year to be in the + * interval [2100, 2199], but there is no path to switch in a smooth way + * because the chip handles YEAR=0x00 (and the out-of-spec + * YEAR=0xa0) as a leap year, but 2100 isn't. + */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + return -EINVAL; + + err = pcf8523_stop_rtc(client); + if (err < 0) + return err; + + regs[0] = REG_SECONDS; + /* This will purposely overwrite REG_SECONDS_OS */ + 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); + int ret; + + switch (cmd) { + case RTC_VL_READ: + ret = pcf8523_voltage_low(client); + if (ret < 0) + return ret; + + 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 int pcf8523_rtc_read_offset(struct device *dev, long *offset) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + u8 value; + s8 val; + + err = pcf8523_read(client, REG_OFFSET, &value); + if (err < 0) + return err; + + /* sign extend the 7-bit offset value */ + val = value << 1; + *offset = (value & REG_OFFSET_MODE ? 4069 : 4340) * (val >> 1); + + return 0; +} + +static int pcf8523_rtc_set_offset(struct device *dev, long offset) +{ + struct i2c_client *client = to_i2c_client(dev); + long reg_m0, reg_m1; + u8 value; + + reg_m0 = clamp(DIV_ROUND_CLOSEST(offset, 4340), -64L, 63L); + reg_m1 = clamp(DIV_ROUND_CLOSEST(offset, 4069), -64L, 63L); + + if (abs(reg_m0 * 4340 - offset) < abs(reg_m1 * 4069 - offset)) + value = reg_m0 & 0x7f; + else + value = (reg_m1 & 0x7f) | REG_OFFSET_MODE; + + return pcf8523_write(client, REG_OFFSET, value); +} + +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, + .read_offset = pcf8523_rtc_read_offset, + .set_offset = pcf8523_rtc_set_offset, +}; + +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_load_capacitance(client); + if (err < 0) + dev_warn(&client->dev, "failed to set xtal load capacitance: %d", + 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, + .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-pcf85363.c b/drivers/rtc/rtc-pcf85363.c new file mode 100644 index 000000000..c3702684b --- /dev/null +++ b/drivers/rtc/rtc-pcf85363.c @@ -0,0 +1,402 @@ +/* + * drivers/rtc/rtc-pcf85363.c + * + * Driver for NXP PCF85363 real-time clock. + * + * Copyright (C) 2017 Eric Nelson + * + * 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 loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert + */ +#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> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +/* + * Date/Time registers + */ +#define DT_100THS 0x00 +#define DT_SECS 0x01 +#define DT_MINUTES 0x02 +#define DT_HOURS 0x03 +#define DT_DAYS 0x04 +#define DT_WEEKDAYS 0x05 +#define DT_MONTHS 0x06 +#define DT_YEARS 0x07 + +/* + * Alarm registers + */ +#define DT_SECOND_ALM1 0x08 +#define DT_MINUTE_ALM1 0x09 +#define DT_HOUR_ALM1 0x0a +#define DT_DAY_ALM1 0x0b +#define DT_MONTH_ALM1 0x0c +#define DT_MINUTE_ALM2 0x0d +#define DT_HOUR_ALM2 0x0e +#define DT_WEEKDAY_ALM2 0x0f +#define DT_ALARM_EN 0x10 + +/* + * Time stamp registers + */ +#define DT_TIMESTAMP1 0x11 +#define DT_TIMESTAMP2 0x17 +#define DT_TIMESTAMP3 0x1d +#define DT_TS_MODE 0x23 + +/* + * control registers + */ +#define CTRL_OFFSET 0x24 +#define CTRL_OSCILLATOR 0x25 +#define CTRL_BATTERY 0x26 +#define CTRL_PIN_IO 0x27 +#define CTRL_FUNCTION 0x28 +#define CTRL_INTA_EN 0x29 +#define CTRL_INTB_EN 0x2a +#define CTRL_FLAGS 0x2b +#define CTRL_RAMBYTE 0x2c +#define CTRL_WDOG 0x2d +#define CTRL_STOP_EN 0x2e +#define CTRL_RESETS 0x2f +#define CTRL_RAM 0x40 + +#define ALRM_SEC_A1E BIT(0) +#define ALRM_MIN_A1E BIT(1) +#define ALRM_HR_A1E BIT(2) +#define ALRM_DAY_A1E BIT(3) +#define ALRM_MON_A1E BIT(4) +#define ALRM_MIN_A2E BIT(5) +#define ALRM_HR_A2E BIT(6) +#define ALRM_DAY_A2E BIT(7) + +#define INT_WDIE BIT(0) +#define INT_BSIE BIT(1) +#define INT_TSRIE BIT(2) +#define INT_A2IE BIT(3) +#define INT_A1IE BIT(4) +#define INT_OIE BIT(5) +#define INT_PIE BIT(6) +#define INT_ILP BIT(7) + +#define FLAGS_TSR1F BIT(0) +#define FLAGS_TSR2F BIT(1) +#define FLAGS_TSR3F BIT(2) +#define FLAGS_BSF BIT(3) +#define FLAGS_WDF BIT(4) +#define FLAGS_A1F BIT(5) +#define FLAGS_A2F BIT(6) +#define FLAGS_PIF BIT(7) + +#define PIN_IO_INTAPM GENMASK(1, 0) +#define PIN_IO_INTA_CLK 0 +#define PIN_IO_INTA_BAT 1 +#define PIN_IO_INTA_OUT 2 +#define PIN_IO_INTA_HIZ 3 + +#define STOP_EN_STOP BIT(0) + +#define RESET_CPR 0xa4 + +#define NVRAM_SIZE 0x40 + +static struct i2c_driver pcf85363_driver; + +struct pcf85363 { + struct device *dev; + struct rtc_device *rtc; + struct regmap *regmap; +}; + +static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_YEARS + 1]; + int ret, len = sizeof(buf); + + /* read the RTC date and time registers all at once */ + ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len); + if (ret) { + dev_err(dev, "%s: error %d\n", __func__, ret); + return ret; + } + + tm->tm_year = bcd2bin(buf[DT_YEARS]); + /* adjust for 1900 base of rtc_time */ + tm->tm_year += 100; + + tm->tm_wday = buf[DT_WEEKDAYS] & 7; + buf[DT_SECS] &= 0x7F; + tm->tm_sec = bcd2bin(buf[DT_SECS]); + buf[DT_MINUTES] &= 0x7F; + tm->tm_min = bcd2bin(buf[DT_MINUTES]); + tm->tm_hour = bcd2bin(buf[DT_HOURS]); + tm->tm_mday = bcd2bin(buf[DT_DAYS]); + tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1; + + return 0; +} + +static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char tmp[11]; + unsigned char *buf = &tmp[2]; + int ret; + + tmp[0] = STOP_EN_STOP; + tmp[1] = RESET_CPR; + + buf[DT_100THS] = 0; + buf[DT_SECS] = bin2bcd(tm->tm_sec); + buf[DT_MINUTES] = bin2bcd(tm->tm_min); + buf[DT_HOURS] = bin2bcd(tm->tm_hour); + buf[DT_DAYS] = bin2bcd(tm->tm_mday); + buf[DT_WEEKDAYS] = tm->tm_wday; + buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); + buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); + + ret = regmap_bulk_write(pcf85363->regmap, CTRL_STOP_EN, + tmp, 2); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85363->regmap, DT_100THS, + buf, sizeof(tmp) - 2); + if (ret) + return ret; + + return regmap_write(pcf85363->regmap, CTRL_STOP_EN, 0); +} + +static int pcf85363_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1]; + unsigned int val; + int ret; + + ret = regmap_bulk_read(pcf85363->regmap, DT_SECOND_ALM1, buf, + sizeof(buf)); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(buf[0]); + alrm->time.tm_min = bcd2bin(buf[1]); + alrm->time.tm_hour = bcd2bin(buf[2]); + alrm->time.tm_mday = bcd2bin(buf[3]); + alrm->time.tm_mon = bcd2bin(buf[4]) - 1; + + ret = regmap_read(pcf85363->regmap, CTRL_INTA_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & INT_A1IE); + + return 0; +} + +static int _pcf85363_rtc_alarm_irq_enable(struct pcf85363 *pcf85363, unsigned + int enabled) +{ + unsigned int alarm_flags = ALRM_SEC_A1E | ALRM_MIN_A1E | ALRM_HR_A1E | + ALRM_DAY_A1E | ALRM_MON_A1E; + int ret; + + ret = regmap_update_bits(pcf85363->regmap, DT_ALARM_EN, alarm_flags, + enabled ? alarm_flags : 0); + if (ret) + return ret; + + ret = regmap_update_bits(pcf85363->regmap, CTRL_INTA_EN, + INT_A1IE, enabled ? INT_A1IE : 0); + + if (ret || enabled) + return ret; + + /* clear current flags */ + return regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0); +} + +static int pcf85363_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + + return _pcf85363_rtc_alarm_irq_enable(pcf85363, enabled); +} + +static int pcf85363_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1]; + int ret; + + buf[0] = bin2bcd(alrm->time.tm_sec); + buf[1] = bin2bcd(alrm->time.tm_min); + buf[2] = bin2bcd(alrm->time.tm_hour); + buf[3] = bin2bcd(alrm->time.tm_mday); + buf[4] = bin2bcd(alrm->time.tm_mon + 1); + + /* + * Disable the alarm interrupt before changing the value to avoid + * spurious interrupts + */ + ret = _pcf85363_rtc_alarm_irq_enable(pcf85363, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85363->regmap, DT_SECOND_ALM1, buf, + sizeof(buf)); + if (ret) + return ret; + + return _pcf85363_rtc_alarm_irq_enable(pcf85363, alrm->enabled); +} + +static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id) +{ + struct pcf85363 *pcf85363 = i2c_get_clientdata(dev_id); + unsigned int flags; + int err; + + err = regmap_read(pcf85363->regmap, CTRL_FLAGS, &flags); + if (err) + return IRQ_NONE; + + if (flags & FLAGS_A1F) { + rtc_update_irq(pcf85363->rtc, 1, RTC_IRQF | RTC_AF); + regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static const struct rtc_class_ops rtc_ops = { + .read_time = pcf85363_rtc_read_time, + .set_time = pcf85363_rtc_set_time, +}; + +static const struct rtc_class_ops rtc_ops_alarm = { + .read_time = pcf85363_rtc_read_time, + .set_time = pcf85363_rtc_set_time, + .read_alarm = pcf85363_rtc_read_alarm, + .set_alarm = pcf85363_rtc_set_alarm, + .alarm_irq_enable = pcf85363_rtc_alarm_irq_enable, +}; + +static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + + return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset, + val, bytes); +} + +static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + struct pcf85363 *pcf85363 = priv; + + return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset, + val, bytes); +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7f, +}; + +static int pcf85363_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pcf85363 *pcf85363; + struct nvmem_config nvmem_cfg = { + .name = "pcf85363-", + .word_size = 1, + .stride = 1, + .size = NVRAM_SIZE, + .reg_read = pcf85363_nvram_read, + .reg_write = pcf85363_nvram_write, + }; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363), + GFP_KERNEL); + if (!pcf85363) + return -ENOMEM; + + pcf85363->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(pcf85363->regmap)) { + dev_err(&client->dev, "regmap allocation failed\n"); + return PTR_ERR(pcf85363->regmap); + } + + pcf85363->dev = &client->dev; + i2c_set_clientdata(client, pcf85363); + + pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev); + if (IS_ERR(pcf85363->rtc)) + return PTR_ERR(pcf85363->rtc); + + pcf85363->rtc->ops = &rtc_ops; + + if (client->irq > 0) { + regmap_write(pcf85363->regmap, CTRL_FLAGS, 0); + regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO, + PIN_IO_INTA_OUT, PIN_IO_INTAPM); + ret = devm_request_threaded_irq(pcf85363->dev, client->irq, + NULL, pcf85363_rtc_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "pcf85363", client); + if (ret) + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + else + pcf85363->rtc->ops = &rtc_ops_alarm; + } + + ret = rtc_register_device(pcf85363->rtc); + + nvmem_cfg.priv = pcf85363; + rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg); + + return ret; +} + +static const struct of_device_id dev_ids[] = { + { .compatible = "nxp,pcf85363" }, + {} +}; +MODULE_DEVICE_TABLE(of, dev_ids); + +static struct i2c_driver pcf85363_driver = { + .driver = { + .name = "pcf85363", + .of_match_table = of_match_ptr(dev_ids), + }, + .probe = pcf85363_probe, +}; + +module_i2c_driver(pcf85363_driver); + +MODULE_AUTHOR("Eric Nelson"); +MODULE_DESCRIPTION("pcf85363 I2C RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c new file mode 100644 index 000000000..d8adf69b6 --- /dev/null +++ b/drivers/rtc/rtc-pcf8563.c @@ -0,0 +1,656 @@ +/* + * 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/clk-provider.h> +#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 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_CLKO_FE 0x80 /* clock out enabled */ +#define PCF8563_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define PCF8563_REG_CLKO_F_32768HZ 0x00 +#define PCF8563_REG_CLKO_F_1024HZ 0x01 +#define PCF8563_REG_CLKO_F_32HZ 0x02 +#define PCF8563_REG_CLKO_F_1HZ 0x03 + +#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; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif +}; + +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 = ®, + }, + { + .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_err(&client->dev, + "low voltage detected, date/time is not reliable.\n"); + return -EINVAL; + } + + 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); + + 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_sec = 0; + 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); + + 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; + + /* The alarm has no seconds, round up to nearest minute */ + if (tm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&tm->time); + + alarm_time += 60 - tm->time.tm_sec; + rtc_time64_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, !!tm->enabled); +} + +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); +} + +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw) + +static int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + + if (ret < 0) + return 0; + + buf &= PCF8563_REG_CLKO_F_MASK; + return clkout_rates[buf]; +} + +static long pcf8563_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 pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + int i; + + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + buf &= ~PCF8563_REG_CLKO_F_MASK; + buf |= i; + ret = pcf8563_write_block_data(client, + PCF8563_REG_CLKO, 1, + &buf); + return ret; + } + + return -EINVAL; +} + +static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + + if (ret < 0) + return ret; + + if (enable) + buf |= PCF8563_REG_CLKO_FE; + else + buf &= ~PCF8563_REG_CLKO_FE; + + ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); + return ret; +} + +static int pcf8563_clkout_prepare(struct clk_hw *hw) +{ + return pcf8563_clkout_control(hw, 1); +} + +static void pcf8563_clkout_unprepare(struct clk_hw *hw) +{ + pcf8563_clkout_control(hw, 0); +} + +static int pcf8563_clkout_is_prepared(struct clk_hw *hw) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + + if (ret < 0) + return ret; + + return !!(buf & PCF8563_REG_CLKO_FE); +} + +static const struct clk_ops pcf8563_clkout_ops = { + .prepare = pcf8563_clkout_prepare, + .unprepare = pcf8563_clkout_unprepare, + .is_prepared = pcf8563_clkout_is_prepared, + .recalc_rate = pcf8563_clkout_recalc_rate, + .round_rate = pcf8563_clkout_round_rate, + .set_rate = pcf8563_clkout_set_rate, +}; + +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) +{ + struct i2c_client *client = pcf8563->client; + struct device_node *node = client->dev.of_node; + struct clk *clk; + struct clk_init_data init; + int ret; + unsigned char buf; + + /* disable the clkout output */ + buf = 0; + ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "pcf8563-clkout"; + init.ops = &pcf8563_clkout_ops; + init.flags = 0; + init.parent_names = NULL; + init.num_parents = 0; + pcf8563->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + +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; + + 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; + + 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; + } + + /* Clear flags and disable interrupts */ + buf = 0; + err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf); + if (err < 0) { + dev_err(&client->dev, "%s: write error\n", __func__); + return err; + } + + 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_LOW, + pcf8563_driver.driver.name, client); + if (err) { + dev_err(&client->dev, "unable to request IRQ %d\n", + client->irq); + return err; + } + + } + +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + pcf8563_clkout_register_clk(pcf8563); +#endif + + /* 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", + .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"); diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c new file mode 100644 index 000000000..7ca9e8871 --- /dev/null +++ b/drivers/rtc/rtc-pcf8583.c @@ -0,0 +1,321 @@ +/* + * 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", + }, + .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-pic32.c b/drivers/rtc/rtc-pic32.c new file mode 100644 index 000000000..3c08eab4f --- /dev/null +++ b/drivers/rtc/rtc-pic32.c @@ -0,0 +1,410 @@ +/* + * PIC32 RTC driver + * + * Joshua Henderson <joshua.henderson@microchip.com> + * Copyright (C) 2016 Microchip Technology Inc. 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; 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/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/rtc.h> +#include <linux/bcd.h> + +#include <asm/mach-pic32/pic32.h> + +#define PIC32_RTCCON 0x00 +#define PIC32_RTCCON_ON BIT(15) +#define PIC32_RTCCON_SIDL BIT(13) +#define PIC32_RTCCON_RTCCLKSEL (3 << 9) +#define PIC32_RTCCON_RTCCLKON BIT(6) +#define PIC32_RTCCON_RTCWREN BIT(3) +#define PIC32_RTCCON_RTCSYNC BIT(2) +#define PIC32_RTCCON_HALFSEC BIT(1) +#define PIC32_RTCCON_RTCOE BIT(0) + +#define PIC32_RTCALRM 0x10 +#define PIC32_RTCALRM_ALRMEN BIT(15) +#define PIC32_RTCALRM_CHIME BIT(14) +#define PIC32_RTCALRM_PIV BIT(13) +#define PIC32_RTCALRM_ALARMSYNC BIT(12) +#define PIC32_RTCALRM_AMASK 0x0F00 +#define PIC32_RTCALRM_ARPT 0xFF + +#define PIC32_RTCHOUR 0x23 +#define PIC32_RTCMIN 0x22 +#define PIC32_RTCSEC 0x21 +#define PIC32_RTCYEAR 0x33 +#define PIC32_RTCMON 0x32 +#define PIC32_RTCDAY 0x31 + +#define PIC32_ALRMTIME 0x40 +#define PIC32_ALRMDATE 0x50 + +#define PIC32_ALRMHOUR 0x43 +#define PIC32_ALRMMIN 0x42 +#define PIC32_ALRMSEC 0x41 +#define PIC32_ALRMYEAR 0x53 +#define PIC32_ALRMMON 0x52 +#define PIC32_ALRMDAY 0x51 + +struct pic32_rtc_dev { + struct rtc_device *rtc; + void __iomem *reg_base; + struct clk *clk; + spinlock_t alarm_lock; + int alarm_irq; + bool alarm_clk_enabled; +}; + +static void pic32_rtc_alarm_clk_enable(struct pic32_rtc_dev *pdata, + bool enable) +{ + unsigned long flags; + + spin_lock_irqsave(&pdata->alarm_lock, flags); + if (enable) { + if (!pdata->alarm_clk_enabled) { + clk_enable(pdata->clk); + pdata->alarm_clk_enabled = true; + } + } else { + if (pdata->alarm_clk_enabled) { + clk_disable(pdata->clk); + pdata->alarm_clk_enabled = false; + } + } + spin_unlock_irqrestore(&pdata->alarm_lock, flags); +} + +static irqreturn_t pic32_rtc_alarmirq(int irq, void *id) +{ + struct pic32_rtc_dev *pdata = (struct pic32_rtc_dev *)id; + + clk_enable(pdata->clk); + rtc_update_irq(pdata->rtc, 1, RTC_AF | RTC_IRQF); + clk_disable(pdata->clk); + + pic32_rtc_alarm_clk_enable(pdata, false); + + return IRQ_HANDLED; +} + +static int pic32_rtc_setaie(struct device *dev, unsigned int enabled) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + + clk_enable(pdata->clk); + + writel(PIC32_RTCALRM_ALRMEN, + base + (enabled ? PIC32_SET(PIC32_RTCALRM) : + PIC32_CLR(PIC32_RTCALRM))); + + clk_disable(pdata->clk); + + pic32_rtc_alarm_clk_enable(pdata, enabled); + + return 0; +} + +static int pic32_rtc_setfreq(struct device *dev, int freq) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + + clk_enable(pdata->clk); + + writel(PIC32_RTCALRM_AMASK, base + PIC32_CLR(PIC32_RTCALRM)); + writel(freq << 8, base + PIC32_SET(PIC32_RTCALRM)); + writel(PIC32_RTCALRM_CHIME, base + PIC32_SET(PIC32_RTCALRM)); + + clk_disable(pdata->clk); + + return 0; +} + +static int pic32_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + unsigned int tries = 0; + + clk_enable(pdata->clk); + + do { + rtc_tm->tm_hour = readb(base + PIC32_RTCHOUR); + rtc_tm->tm_min = readb(base + PIC32_RTCMIN); + rtc_tm->tm_mon = readb(base + PIC32_RTCMON); + rtc_tm->tm_mday = readb(base + PIC32_RTCDAY); + rtc_tm->tm_year = readb(base + PIC32_RTCYEAR); + rtc_tm->tm_sec = readb(base + PIC32_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. + */ + tries += 1; + } while (rtc_tm->tm_sec == 0 && tries < 2); + + 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) - 1; + rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); + + 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); + + clk_disable(pdata->clk); + return 0; +} + +static int pic32_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + 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); + + if (year < 0 || year >= 100) { + dev_err(dev, "rtc only supports 100 years\n"); + return -EINVAL; + } + + clk_enable(pdata->clk); + writeb(bin2bcd(tm->tm_sec), base + PIC32_RTCSEC); + writeb(bin2bcd(tm->tm_min), base + PIC32_RTCMIN); + writeb(bin2bcd(tm->tm_hour), base + PIC32_RTCHOUR); + writeb(bin2bcd(tm->tm_mday), base + PIC32_RTCDAY); + writeb(bin2bcd(tm->tm_mon + 1), base + PIC32_RTCMON); + writeb(bin2bcd(year), base + PIC32_RTCYEAR); + clk_disable(pdata->clk); + + return 0; +} + +static int pic32_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + struct rtc_time *alm_tm = &alrm->time; + void __iomem *base = pdata->reg_base; + unsigned int alm_en; + + clk_enable(pdata->clk); + alm_tm->tm_sec = readb(base + PIC32_ALRMSEC); + alm_tm->tm_min = readb(base + PIC32_ALRMMIN); + alm_tm->tm_hour = readb(base + PIC32_ALRMHOUR); + alm_tm->tm_mon = readb(base + PIC32_ALRMMON); + alm_tm->tm_mday = readb(base + PIC32_ALRMDAY); + alm_tm->tm_year = readb(base + PIC32_ALRMYEAR); + + alm_en = readb(base + PIC32_RTCALRM); + + alrm->enabled = (alm_en & PIC32_RTCALRM_ALRMEN) ? 1 : 0; + + dev_dbg(dev, "getalarm: %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); + + alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); + alm_tm->tm_min = bcd2bin(alm_tm->tm_min); + alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); + alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); + alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon) - 1; + alm_tm->tm_year = bcd2bin(alm_tm->tm_year); + + clk_disable(pdata->clk); + return 0; +} + +static int pic32_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + void __iomem *base = pdata->reg_base; + + clk_enable(pdata->clk); + dev_dbg(dev, "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); + + writel(0x00, base + PIC32_ALRMTIME); + writel(0x00, base + PIC32_ALRMDATE); + + pic32_rtc_setaie(dev, alrm->enabled); + + clk_disable(pdata->clk); + return 0; +} + +static int pic32_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct pic32_rtc_dev *pdata = dev_get_drvdata(dev); + void __iomem *base = pdata->reg_base; + unsigned int repeat; + + clk_enable(pdata->clk); + + repeat = readw(base + PIC32_RTCALRM); + repeat &= PIC32_RTCALRM_ARPT; + seq_printf(seq, "periodic_IRQ\t: %s\n", repeat ? "yes" : "no"); + + clk_disable(pdata->clk); + return 0; +} + +static const struct rtc_class_ops pic32_rtcops = { + .read_time = pic32_rtc_gettime, + .set_time = pic32_rtc_settime, + .read_alarm = pic32_rtc_getalarm, + .set_alarm = pic32_rtc_setalarm, + .proc = pic32_rtc_proc, + .alarm_irq_enable = pic32_rtc_setaie, +}; + +static void pic32_rtc_enable(struct pic32_rtc_dev *pdata, int en) +{ + void __iomem *base = pdata->reg_base; + + if (!base) + return; + + clk_enable(pdata->clk); + if (!en) { + writel(PIC32_RTCCON_ON, base + PIC32_CLR(PIC32_RTCCON)); + } else { + pic32_syskey_unlock(); + + writel(PIC32_RTCCON_RTCWREN, base + PIC32_SET(PIC32_RTCCON)); + writel(3 << 9, base + PIC32_CLR(PIC32_RTCCON)); + + if (!(readl(base + PIC32_RTCCON) & PIC32_RTCCON_ON)) + writel(PIC32_RTCCON_ON, base + PIC32_SET(PIC32_RTCCON)); + } + clk_disable(pdata->clk); +} + +static int pic32_rtc_remove(struct platform_device *pdev) +{ + struct pic32_rtc_dev *pdata = platform_get_drvdata(pdev); + + pic32_rtc_setaie(&pdev->dev, 0); + clk_unprepare(pdata->clk); + pdata->clk = NULL; + + return 0; +} + +static int pic32_rtc_probe(struct platform_device *pdev) +{ + struct pic32_rtc_dev *pdata; + struct resource *res; + int ret; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + platform_set_drvdata(pdev, pdata); + + pdata->alarm_irq = platform_get_irq(pdev, 0); + if (pdata->alarm_irq < 0) { + dev_err(&pdev->dev, "no irq for alarm\n"); + return pdata->alarm_irq; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pdata->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pdata->reg_base)) + return PTR_ERR(pdata->reg_base); + + pdata->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pdata->clk)) { + dev_err(&pdev->dev, "failed to find rtc clock source\n"); + ret = PTR_ERR(pdata->clk); + pdata->clk = NULL; + return ret; + } + + spin_lock_init(&pdata->alarm_lock); + + clk_prepare_enable(pdata->clk); + + pic32_rtc_enable(pdata, 1); + + device_init_wakeup(&pdev->dev, 1); + + pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &pic32_rtcops, + THIS_MODULE); + if (IS_ERR(pdata->rtc)) { + ret = PTR_ERR(pdata->rtc); + goto err_nortc; + } + + pdata->rtc->max_user_freq = 128; + + pic32_rtc_setfreq(&pdev->dev, 1); + ret = devm_request_irq(&pdev->dev, pdata->alarm_irq, + pic32_rtc_alarmirq, 0, + dev_name(&pdev->dev), pdata); + if (ret) { + dev_err(&pdev->dev, + "IRQ %d error %d\n", pdata->alarm_irq, ret); + goto err_nortc; + } + + clk_disable(pdata->clk); + + return 0; + +err_nortc: + pic32_rtc_enable(pdata, 0); + clk_disable_unprepare(pdata->clk); + + return ret; +} + +static const struct of_device_id pic32_rtc_dt_ids[] = { + { .compatible = "microchip,pic32mzda-rtc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids); + +static struct platform_driver pic32_rtc_driver = { + .probe = pic32_rtc_probe, + .remove = pic32_rtc_remove, + .driver = { + .name = "pic32-rtc", + .of_match_table = of_match_ptr(pic32_rtc_dt_ids), + }, +}; +module_platform_driver(pic32_rtc_driver); + +MODULE_DESCRIPTION("Microchip PIC32 RTC Driver"); +MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c new file mode 100644 index 000000000..343bb6ed1 --- /dev/null +++ b/drivers/rtc/rtc-pl030.c @@ -0,0 +1,190 @@ +/* + * 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->rtc = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + goto err_rtc; + } + + rtc->rtc->ops = &pl030_ops; + 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; + + ret = rtc_register_device(rtc->rtc); + if (ret) + 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); + 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..82eb7da2c --- /dev/null +++ b/drivers/rtc/rtc-pl031.c @@ -0,0 +1,490 @@ +/* + * 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/pm_wakeirq.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); + + dev_pm_clear_wake_irq(&adev->dev); + device_init_wakeup(&adev->dev, false); + if (adev->irq[0]) + free_irq(adev->irq[0], ldata); + rtc_device_unregister(ldata->rtc); + 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; + unsigned long time, data; + + ret = amba_request_regions(adev, NULL); + if (ret) + goto err_req; + + ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local), + GFP_KERNEL); + ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops), + GFP_KERNEL); + if (!ldata || !ops) { + ret = -ENOMEM; + goto out; + } + + ldata->vendor = vendor; + ldata->base = devm_ioremap(&adev->dev, adev->res.start, + resource_size(&adev->res)); + if (!ldata->base) { + ret = -ENOMEM; + goto out; + } + + 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); + } + } + } + + if (!adev->irq[0]) { + /* When there's no interrupt, no point in exposing the alarm */ + ops->read_alarm = NULL; + ops->set_alarm = NULL; + ops->alarm_irq_enable = NULL; + } + + device_init_wakeup(&adev->dev, true); + ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, + THIS_MODULE); + if (IS_ERR(ldata->rtc)) { + ret = PTR_ERR(ldata->rtc); + goto out; + } + + if (adev->irq[0]) { + ret = request_irq(adev->irq[0], pl031_interrupt, + vendor->irqflags, "rtc-pl031", ldata); + if (ret) + goto out_no_irq; + dev_pm_set_wake_irq(&adev->dev, adev->irq[0]); + } + return 0; + +out_no_irq: + rtc_device_unregister(ldata->rtc); +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, + }, +}; + +/* 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, +}; + +/* 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. + * TODO check if it shares with IRQF_NO_SUSPEND user, else we can + * remove IRQF_COND_SUSPEND + */ + .irqflags = IRQF_SHARED | IRQF_COND_SUSPEND, +}; + +static const 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..e03104b73 --- /dev/null +++ b/drivers/rtc/rtc-pm8xxx.c @@ -0,0 +1,568 @@ +/* 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. Disable rtc if enabled. + * 3. Write 0x00 to LSB. + * 4. Write Byte[1], Byte[2], Byte[3] then Byte[0]. + * 5. Enable rtc if disabled in step 2. + * 6. 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, rtc_disabled = 0; + unsigned int ctrl_reg, rtc_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); + + dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", 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_read(rtc_dd->regmap, regs->alarm_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->alarm_ctrl, ctrl_reg); + if (rc) { + dev_err(dev, "Write to RTC Alarm control register failed\n"); + goto rtc_rw_fail; + } + } + + /* Disable RTC H/w before writing on RTC register */ + rc = regmap_read(rtc_dd->regmap, regs->ctrl, &rtc_ctrl_reg); + if (rc) + goto rtc_rw_fail; + + if (rtc_ctrl_reg & PM8xxx_RTC_ENABLE) { + rtc_disabled = 1; + rtc_ctrl_reg &= ~PM8xxx_RTC_ENABLE; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_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; + } + + /* Enable RTC H/w after writing on RTC register */ + if (rtc_disabled) { + rtc_ctrl_reg |= PM8xxx_RTC_ENABLE; + rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg); + if (rc) { + dev_err(dev, "Write to RTC control register failed\n"); + goto rtc_rw_fail; + } + } + + if (alarm_enabled) { + 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; + } + } + +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, ®); + 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) | + ((unsigned long)value[3] << 24); + + rtc_time_to_tm(secs, tm); + + 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) | + ((unsigned long)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,pm8018-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..b8c5b9310 --- /dev/null +++ b/drivers/rtc/rtc-proc.c @@ -0,0 +1,121 @@ +/* + * 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 = snprintf(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; +} + +void rtc_proc_add_device(struct rtc_device *rtc) +{ + if (is_rtc_hctosys(rtc)) + proc_create_single_data("driver/rtc", 0, NULL, rtc_proc_show, + 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..347288bff --- /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 0; +} + +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..9e83be32f --- /dev/null +++ b/drivers/rtc/rtc-puv3.c @@ -0,0 +1,309 @@ +/* + * 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 const struct rtc_class_ops puv3_rtcops = { + .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) +{ + 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); + + rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + ret = devm_request_irq(&pdev->dev, puv3_rtc_alarmno, puv3_rtc_alarmirq, + 0, "pkunity-rtc alarm", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); + return ret; + } + + ret = devm_request_irq(&pdev->dev, puv3_rtc_tickno, puv3_rtc_tickirq, + 0, "pkunity-rtc tick", rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); + return ret; + } + + /* 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->ops = &puv3_rtcops; + ret = rtc_register_device(rtc); + if (ret) { + dev_err(&pdev->dev, "cannot attach rtc\n"); + 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..140c58b16 --- /dev/null +++ b/drivers/rtc/rtc-pxa.c @@ -0,0 +1,442 @@ +/* + * 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> + +#include "rtc-sa1100.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 sa1100_rtc sa1100_rtc; + struct resource *ress; + void __iomem *base; + 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->sa1100_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->sa1100_rtc.irq_1hz, ret); + goto err_irq_1Hz; + } + ret = request_irq(pxa_rtc->sa1100_rtc.irq_alarm, pxa_rtc_irq, 0, + "rtc Alrm", dev); + if (ret < 0) { + dev_err(dev, "can't get irq %i, err %d\n", + pxa_rtc->sa1100_rtc.irq_alarm, ret); + goto err_irq_Alrm; + } + + return 0; + +err_irq_Alrm: + free_irq(pxa_rtc->sa1100_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->sa1100_rtc.irq_1hz, dev); + free_irq(pxa_rtc->sa1100_rtc.irq_alarm, 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; + struct sa1100_rtc *sa1100_rtc; + int ret; + + pxa_rtc = devm_kzalloc(dev, sizeof(*pxa_rtc), GFP_KERNEL); + if (!pxa_rtc) + return -ENOMEM; + sa1100_rtc = &pxa_rtc->sa1100_rtc; + + 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; + } + + sa1100_rtc->irq_1hz = platform_get_irq(pdev, 0); + if (sa1100_rtc->irq_1hz < 0) { + dev_err(dev, "No 1Hz IRQ resource defined\n"); + return -ENXIO; + } + sa1100_rtc->irq_alarm = platform_get_irq(pdev, 1); + if (sa1100_rtc->irq_alarm < 0) { + dev_err(dev, "No alarm IRQ resource defined\n"); + return -ENXIO; + } + + sa1100_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(sa1100_rtc->rtc)) + return PTR_ERR(sa1100_rtc->rtc); + + 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; + } + + pxa_rtc_open(dev); + + sa1100_rtc->rcnr = pxa_rtc->base + 0x0; + sa1100_rtc->rtsr = pxa_rtc->base + 0x8; + sa1100_rtc->rtar = pxa_rtc->base + 0x4; + sa1100_rtc->rttr = pxa_rtc->base + 0xc; + ret = sa1100_rtc_init(pdev, sa1100_rtc); + if (ret) { + dev_err(dev, "Unable to init SA1100 RTC sub-device\n"); + return ret; + } + + 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->sa1100_rtc.irq_alarm); + 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->sa1100_rtc.irq_alarm); + 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-r7301.c b/drivers/rtc/rtc-r7301.c new file mode 100644 index 000000000..1943c8151 --- /dev/null +++ b/drivers/rtc/rtc-r7301.c @@ -0,0 +1,454 @@ +/* + * EPSON TOYOCOM RTC-7301SF/DG Driver + * + * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com> + * + * Based on rtc-rp5c01.c + * + * Datasheet: http://www5.epsondevice.com/en/products/parallel/rtc7301sf.html + */ + +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/delay.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#define DRV_NAME "rtc-r7301" + +#define RTC7301_1_SEC 0x0 /* Bank 0 and Band 1 */ +#define RTC7301_10_SEC 0x1 /* Bank 0 and Band 1 */ +#define RTC7301_AE BIT(3) +#define RTC7301_1_MIN 0x2 /* Bank 0 and Band 1 */ +#define RTC7301_10_MIN 0x3 /* Bank 0 and Band 1 */ +#define RTC7301_1_HOUR 0x4 /* Bank 0 and Band 1 */ +#define RTC7301_10_HOUR 0x5 /* Bank 0 and Band 1 */ +#define RTC7301_DAY_OF_WEEK 0x6 /* Bank 0 and Band 1 */ +#define RTC7301_1_DAY 0x7 /* Bank 0 and Band 1 */ +#define RTC7301_10_DAY 0x8 /* Bank 0 and Band 1 */ +#define RTC7301_1_MONTH 0x9 /* Bank 0 */ +#define RTC7301_10_MONTH 0xa /* Bank 0 */ +#define RTC7301_1_YEAR 0xb /* Bank 0 */ +#define RTC7301_10_YEAR 0xc /* Bank 0 */ +#define RTC7301_100_YEAR 0xd /* Bank 0 */ +#define RTC7301_1000_YEAR 0xe /* Bank 0 */ +#define RTC7301_ALARM_CONTROL 0xe /* Bank 1 */ +#define RTC7301_ALARM_CONTROL_AIE BIT(0) +#define RTC7301_ALARM_CONTROL_AF BIT(1) +#define RTC7301_TIMER_CONTROL 0xe /* Bank 2 */ +#define RTC7301_TIMER_CONTROL_TIE BIT(0) +#define RTC7301_TIMER_CONTROL_TF BIT(1) +#define RTC7301_CONTROL 0xf /* All banks */ +#define RTC7301_CONTROL_BUSY BIT(0) +#define RTC7301_CONTROL_STOP BIT(1) +#define RTC7301_CONTROL_BANK_SEL_0 BIT(2) +#define RTC7301_CONTROL_BANK_SEL_1 BIT(3) + +struct rtc7301_priv { + struct regmap *regmap; + int irq; + spinlock_t lock; + u8 bank; +}; + +static const struct regmap_config rtc7301_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 4, +}; + +static u8 rtc7301_read(struct rtc7301_priv *priv, unsigned int reg) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + unsigned int val; + + regmap_read(priv->regmap, reg_stride * reg, &val); + + return val & 0xf; +} + +static void rtc7301_write(struct rtc7301_priv *priv, u8 val, unsigned int reg) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + + regmap_write(priv->regmap, reg_stride * reg, val); +} + +static void rtc7301_update_bits(struct rtc7301_priv *priv, unsigned int reg, + u8 mask, u8 val) +{ + int reg_stride = regmap_get_reg_stride(priv->regmap); + + regmap_update_bits(priv->regmap, reg_stride * reg, mask, val); +} + +static int rtc7301_wait_while_busy(struct rtc7301_priv *priv) +{ + int retries = 100; + + while (retries-- > 0) { + u8 val; + + val = rtc7301_read(priv, RTC7301_CONTROL); + if (!(val & RTC7301_CONTROL_BUSY)) + return 0; + + udelay(300); + } + + return -ETIMEDOUT; +} + +static void rtc7301_stop(struct rtc7301_priv *priv) +{ + rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, + RTC7301_CONTROL_STOP); +} + +static void rtc7301_start(struct rtc7301_priv *priv) +{ + rtc7301_update_bits(priv, RTC7301_CONTROL, RTC7301_CONTROL_STOP, 0); +} + +static void rtc7301_select_bank(struct rtc7301_priv *priv, u8 bank) +{ + u8 val = 0; + + if (bank == priv->bank) + return; + + if (bank & BIT(0)) + val |= RTC7301_CONTROL_BANK_SEL_0; + if (bank & BIT(1)) + val |= RTC7301_CONTROL_BANK_SEL_1; + + rtc7301_update_bits(priv, RTC7301_CONTROL, + RTC7301_CONTROL_BANK_SEL_0 | + RTC7301_CONTROL_BANK_SEL_1, val); + + priv->bank = bank; +} + +static void rtc7301_get_time(struct rtc7301_priv *priv, struct rtc_time *tm, + bool alarm) +{ + int year; + + tm->tm_sec = rtc7301_read(priv, RTC7301_1_SEC); + tm->tm_sec += (rtc7301_read(priv, RTC7301_10_SEC) & ~RTC7301_AE) * 10; + tm->tm_min = rtc7301_read(priv, RTC7301_1_MIN); + tm->tm_min += (rtc7301_read(priv, RTC7301_10_MIN) & ~RTC7301_AE) * 10; + tm->tm_hour = rtc7301_read(priv, RTC7301_1_HOUR); + tm->tm_hour += (rtc7301_read(priv, RTC7301_10_HOUR) & ~RTC7301_AE) * 10; + tm->tm_mday = rtc7301_read(priv, RTC7301_1_DAY); + tm->tm_mday += (rtc7301_read(priv, RTC7301_10_DAY) & ~RTC7301_AE) * 10; + + if (alarm) { + tm->tm_wday = -1; + tm->tm_mon = -1; + tm->tm_year = -1; + tm->tm_yday = -1; + tm->tm_isdst = -1; + return; + } + + tm->tm_wday = (rtc7301_read(priv, RTC7301_DAY_OF_WEEK) & ~RTC7301_AE); + tm->tm_mon = rtc7301_read(priv, RTC7301_10_MONTH) * 10 + + rtc7301_read(priv, RTC7301_1_MONTH) - 1; + year = rtc7301_read(priv, RTC7301_1000_YEAR) * 1000 + + rtc7301_read(priv, RTC7301_100_YEAR) * 100 + + rtc7301_read(priv, RTC7301_10_YEAR) * 10 + + rtc7301_read(priv, RTC7301_1_YEAR); + + tm->tm_year = year - 1900; +} + +static void rtc7301_write_time(struct rtc7301_priv *priv, struct rtc_time *tm, + bool alarm) +{ + int year; + + rtc7301_write(priv, tm->tm_sec % 10, RTC7301_1_SEC); + rtc7301_write(priv, tm->tm_sec / 10, RTC7301_10_SEC); + + rtc7301_write(priv, tm->tm_min % 10, RTC7301_1_MIN); + rtc7301_write(priv, tm->tm_min / 10, RTC7301_10_MIN); + + rtc7301_write(priv, tm->tm_hour % 10, RTC7301_1_HOUR); + rtc7301_write(priv, tm->tm_hour / 10, RTC7301_10_HOUR); + + rtc7301_write(priv, tm->tm_mday % 10, RTC7301_1_DAY); + rtc7301_write(priv, tm->tm_mday / 10, RTC7301_10_DAY); + + /* Don't care for alarm register */ + rtc7301_write(priv, alarm ? RTC7301_AE : tm->tm_wday, + RTC7301_DAY_OF_WEEK); + + if (alarm) + return; + + rtc7301_write(priv, (tm->tm_mon + 1) % 10, RTC7301_1_MONTH); + rtc7301_write(priv, (tm->tm_mon + 1) / 10, RTC7301_10_MONTH); + + year = tm->tm_year + 1900; + + rtc7301_write(priv, year % 10, RTC7301_1_YEAR); + rtc7301_write(priv, (year / 10) % 10, RTC7301_10_YEAR); + rtc7301_write(priv, (year / 100) % 10, RTC7301_100_YEAR); + rtc7301_write(priv, year / 1000, RTC7301_1000_YEAR); +} + +static void rtc7301_alarm_irq(struct rtc7301_priv *priv, unsigned int enabled) +{ + rtc7301_update_bits(priv, RTC7301_ALARM_CONTROL, + RTC7301_ALARM_CONTROL_AF | + RTC7301_ALARM_CONTROL_AIE, + enabled ? RTC7301_ALARM_CONTROL_AIE : 0); +} + +static int rtc7301_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + int err; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 0); + + err = rtc7301_wait_while_busy(priv); + if (!err) + rtc7301_get_time(priv, tm, false); + + spin_unlock_irqrestore(&priv->lock, flags); + + return err; +} + +static int rtc7301_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_stop(priv); + udelay(300); + rtc7301_select_bank(priv, 0); + rtc7301_write_time(priv, tm, false); + rtc7301_start(priv); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + u8 alrm_ctrl; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_get_time(priv, &alarm->time, true); + + alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); + + alarm->enabled = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AIE); + alarm->pending = !!(alrm_ctrl & RTC7301_ALARM_CONTROL_AF); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_write_time(priv, &alarm->time, true); + rtc7301_alarm_irq(priv, alarm->enabled); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static int rtc7301_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + unsigned long flags; + + if (priv->irq <= 0) + return -EINVAL; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + rtc7301_alarm_irq(priv, enabled); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +static const struct rtc_class_ops rtc7301_rtc_ops = { + .read_time = rtc7301_read_time, + .set_time = rtc7301_set_time, + .read_alarm = rtc7301_read_alarm, + .set_alarm = rtc7301_set_alarm, + .alarm_irq_enable = rtc7301_alarm_irq_enable, +}; + +static irqreturn_t rtc7301_irq_handler(int irq, void *dev_id) +{ + struct rtc_device *rtc = dev_id; + struct rtc7301_priv *priv = dev_get_drvdata(rtc->dev.parent); + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + u8 alrm_ctrl; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 1); + + alrm_ctrl = rtc7301_read(priv, RTC7301_ALARM_CONTROL); + if (alrm_ctrl & RTC7301_ALARM_CONTROL_AF) { + ret = IRQ_HANDLED; + rtc7301_alarm_irq(priv, false); + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static void rtc7301_init(struct rtc7301_priv *priv) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + rtc7301_select_bank(priv, 2); + rtc7301_write(priv, 0, RTC7301_TIMER_CONTROL); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int __init rtc7301_rtc_probe(struct platform_device *dev) +{ + struct resource *res; + void __iomem *regs; + struct rtc7301_priv *priv; + struct rtc_device *rtc; + int ret; + + 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; + + regs = devm_ioremap_resource(&dev->dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->regmap = devm_regmap_init_mmio(&dev->dev, regs, + &rtc7301_regmap_config); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + priv->irq = platform_get_irq(dev, 0); + + spin_lock_init(&priv->lock); + priv->bank = -1; + + rtc7301_init(priv); + + platform_set_drvdata(dev, priv); + + rtc = devm_rtc_device_register(&dev->dev, DRV_NAME, &rtc7301_rtc_ops, + THIS_MODULE); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + if (priv->irq > 0) { + ret = devm_request_irq(&dev->dev, priv->irq, + rtc7301_irq_handler, IRQF_SHARED, + dev_name(&dev->dev), rtc); + if (ret) { + priv->irq = 0; + dev_err(&dev->dev, "unable to request IRQ\n"); + } else { + device_set_wakeup_capable(&dev->dev, true); + } + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int rtc7301_suspend(struct device *dev) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(priv->irq); + + return 0; +} + +static int rtc7301_resume(struct device *dev) +{ + struct rtc7301_priv *priv = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(priv->irq); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(rtc7301_pm_ops, rtc7301_suspend, rtc7301_resume); + +static const struct of_device_id rtc7301_dt_match[] = { + { .compatible = "epson,rtc7301sf" }, + { .compatible = "epson,rtc7301dg" }, + {} +}; +MODULE_DEVICE_TABLE(of, rtc7301_dt_match); + +static struct platform_driver rtc7301_rtc_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = rtc7301_dt_match, + .pm = &rtc7301_pm_ops, + }, +}; + +module_platform_driver_probe(rtc7301_rtc_driver, rtc7301_rtc_probe); + +MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("EPSON TOYOCOM RTC-7301SF/DG Driver"); +MODULE_ALIAS("platform:rtc-r7301"); diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c new file mode 100644 index 000000000..a39ccd1cf --- /dev/null +++ b/drivers/rtc/rtc-r9701.c @@ -0,0 +1,179 @@ +/* + * 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 0; +} + +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 struct spi_driver r9701_driver = { + .driver = { + .name = "rtc-r9701", + }, + .probe = r9701_probe, +}; + +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..68ce77414 --- /dev/null +++ b/drivers/rtc/rtc-rc5t583.c @@ -0,0 +1,322 @@ +/* + * 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_sec = 0; + 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..739c0d42e --- /dev/null +++ b/drivers/rtc/rtc-rk808.c @@ -0,0 +1,449 @@ +/* + * 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; +}; + +/* + * The Rockchip calendar used by the RK808 counts November with 31 days. We use + * these translation functions to convert its dates to/from the Gregorian + * calendar used by the rest of the world. We arbitrarily define Jan 1st, 2016 + * as the day when both calendars were in sync, and treat all other dates + * relative to that. + * NOTE: Other system software (e.g. firmware) that reads the same hardware must + * implement this exact same conversion algorithm, with the same anchor date. + */ +static time64_t nov2dec_transitions(struct rtc_time *tm) +{ + return (tm->tm_year + 1900) - 2016 + (tm->tm_mon + 1 > 11 ? 1 : 0); +} + +static void rockchip_to_gregorian(struct rtc_time *tm) +{ + /* If it's Nov 31st, rtc_tm_to_time64() will count that like Dec 1st */ + time64_t time = rtc_tm_to_time64(tm); + rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm); +} + +static void gregorian_to_rockchip(struct rtc_time *tm) +{ + time64_t extra_days = nov2dec_transitions(tm); + time64_t time = rtc_tm_to_time64(tm); + rtc_time64_to_tm(time - extra_days * 86400, tm); + + /* Compensate if we went back over Nov 31st (will work up to 2381) */ + if (nov2dec_transitions(tm) < extra_days) { + if (tm->tm_mon + 1 == 11) + tm->tm_mday++; /* This may result in 31! */ + else + rtc_time64_to_tm(time - (extra_days - 1) * 86400, tm); + } +} + +/* 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); + rockchip_to_gregorian(tm); + 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; + + 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); + gregorian_to_rockchip(tm); + 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); + + /* 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; + rockchip_to_gregorian(&alrm->time); + + 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); + + gregorian_to_rockchip(&alrm->time); + 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; + 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; + } + + device_init_wakeup(&pdev->dev, 1); + + rk808_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rk808_rtc->rtc)) + return PTR_ERR(rk808_rtc->rtc); + + rk808_rtc->rtc->ops = &rk808_rtc_ops; + + 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; + } + + return rtc_register_device(rk808_rtc->rtc); +} + +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..f1c160fe7 --- /dev/null +++ b/drivers/rtc/rtc-rp5c01.c @@ -0,0 +1,276 @@ +/* + * 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 */ +}; + +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 0; +} + +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 int rp5c01_nvram_read(void *_priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rp5c01_priv *priv = _priv; + u8 *buf = val; + + spin_lock_irq(&priv->lock); + + for (; bytes; bytes--) { + 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 0; +} + +static int rp5c01_nvram_write(void *_priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rp5c01_priv *priv = _priv; + u8 *buf = val; + + spin_lock_irq(&priv->lock); + + for (; bytes; bytes--) { + 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 0; +} + +static int __init rp5c01_rtc_probe(struct platform_device *dev) +{ + struct resource *res; + struct rp5c01_priv *priv; + struct rtc_device *rtc; + int error; + struct nvmem_config nvmem_cfg = { + .name = "rp5c01_nvram", + .word_size = 1, + .stride = 1, + .size = RP5C01_MODE, + .reg_read = rp5c01_nvram_read, + .reg_write = rp5c01_nvram_write, + }; + + 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; + + spin_lock_init(&priv->lock); + + platform_set_drvdata(dev, priv); + + rtc = devm_rtc_allocate_device(&dev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &rp5c01_rtc_ops; + rtc->nvram_old_abi = true; + + priv->rtc = rtc; + + nvmem_cfg.priv = priv; + error = rtc_nvmem_register(rtc, &nvmem_cfg); + if (error) + return error; + + return rtc_register_device(rtc); +} + +static struct platform_driver rp5c01_rtc_driver = { + .driver = { + .name = "rtc-rp5c01", + }, +}; + +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..89f38e3e9 --- /dev/null +++ b/drivers/rtc/rtc-rs5c313.c @@ -0,0 +1,412 @@ +/* + * 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" + +#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_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..f2de8b17e --- /dev/null +++ b/drivers/rtc/rtc-rs5c348.c @@ -0,0 +1,225 @@ +/* + * 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 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); + + 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, "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", + }, + .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_ALIAS("spi:rtc-rs5c348"); diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c new file mode 100644 index 000000000..c50383290 --- /dev/null +++ b/drivers/rtc/rtc-rs5c372.c @@ -0,0 +1,710 @@ +/* + * 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> +#include <linux/of_device.h> + +/* + * 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); + +static const struct of_device_id rs5c372_of_match[] = { + { + .compatible = "ricoh,r2025sd", + .data = (void *)rtc_r2025sd + }, + { + .compatible = "ricoh,r2221tl", + .data = (void *)rtc_r2221tl + }, + { + .compatible = "ricoh,rs5c372a", + .data = (void *)rtc_rs5c372a + }, + { + .compatible = "ricoh,rs5c372b", + .data = (void *)rtc_rs5c372b + }, + { + .compatible = "ricoh,rv5c386", + .data = (void *)rtc_rv5c386 + }, + { + .compatible = "ricoh,rv5c387a", + .data = (void *)rtc_rv5c387a + }, + { } +}; +MODULE_DEVICE_TABLE(of, rs5c372_of_match); + +/* 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_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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); + + return 0; +} + +static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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 IS_ENABLED(CONFIG_RTC_INTF_PROC) +#define NEED_TRIM +#endif + +#if IS_ENABLED(CONFIG_RTC_INTF_SYSFS) +#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 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]); + + /* ... 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 IS_ENABLED(CONFIG_RTC_INTF_PROC) + +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 IS_ENABLED(CONFIG_RTC_INTF_SYSFS) + +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; + + 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); + if (client->dev.of_node) + rs5c372->type = (enum rtc_type) + of_device_get_match_data(&client->dev); + else + 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; + } + + dev_info(&client->dev, "%s found, %s\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", + .of_match_table = of_match_ptr(rs5c372_of_match), + }, + .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"); diff --git a/drivers/rtc/rtc-rtd119x.c b/drivers/rtc/rtc-rtd119x.c new file mode 100644 index 000000000..b233559d9 --- /dev/null +++ b/drivers/rtc/rtc-rtd119x.c @@ -0,0 +1,242 @@ +/* + * Realtek RTD129x RTC + * + * Copyright (c) 2017 Andreas Färber + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spinlock.h> + +#define RTD_RTCSEC 0x00 +#define RTD_RTCMIN 0x04 +#define RTD_RTCHR 0x08 +#define RTD_RTCDATE1 0x0c +#define RTD_RTCDATE2 0x10 +#define RTD_RTCACR 0x28 +#define RTD_RTCEN 0x2c +#define RTD_RTCCR 0x30 + +#define RTD_RTCSEC_RTCSEC_MASK 0x7f + +#define RTD_RTCMIN_RTCMIN_MASK 0x3f + +#define RTD_RTCHR_RTCHR_MASK 0x1f + +#define RTD_RTCDATE1_RTCDATE1_MASK 0xff + +#define RTD_RTCDATE2_RTCDATE2_MASK 0x7f + +#define RTD_RTCACR_RTCPWR BIT(7) + +#define RTD_RTCEN_RTCEN_MASK 0xff + +#define RTD_RTCCR_RTCRST BIT(6) + +struct rtd119x_rtc { + void __iomem *base; + struct clk *clk; + struct rtc_device *rtcdev; + unsigned int base_year; +}; + +static inline int rtd119x_rtc_days_in_year(int year) +{ + return 365 + (is_leap_year(year) ? 1 : 0); +} + +static void rtd119x_rtc_reset(struct device *dev) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCCR); + val |= RTD_RTCCR_RTCRST; + writel_relaxed(val, data->base + RTD_RTCCR); + + val &= ~RTD_RTCCR_RTCRST; + writel(val, data->base + RTD_RTCCR); +} + +static void rtd119x_rtc_set_enabled(struct device *dev, bool enable) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + u32 val; + + val = readl_relaxed(data->base + RTD_RTCEN); + if (enable) { + if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a) + return; + writel_relaxed(0x5a, data->base + RTD_RTCEN); + } else { + writel_relaxed(0, data->base + RTD_RTCEN); + } +} + +static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + s32 day; + u32 sec; + unsigned int year; + int tries = 0; + + while (true) { + tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tm->tm_min = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK; + tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK; + day = readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK; + day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8; + sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; + tries++; + + if (sec == tm->tm_sec) + break; + + if (tries >= 3) + return -EINVAL; + } + if (tries > 1) + dev_dbg(dev, "%s: needed %i tries\n", __func__, tries); + + year = data->base_year; + while (day >= rtd119x_rtc_days_in_year(year)) { + day -= rtd119x_rtc_days_in_year(year); + year++; + } + tm->tm_year = year - 1900; + tm->tm_yday = day; + + tm->tm_mon = 0; + while (day >= rtc_month_days(tm->tm_mon, year)) { + day -= rtc_month_days(tm->tm_mon, year); + tm->tm_mon++; + } + tm->tm_mday = day + 1; + + return 0; +} + +static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rtd119x_rtc *data = dev_get_drvdata(dev); + unsigned int day; + int i; + + if (1900 + tm->tm_year < data->base_year) + return -EINVAL; + + day = 0; + for (i = data->base_year; i < 1900 + tm->tm_year; i++) + day += rtd119x_rtc_days_in_year(i); + + day += tm->tm_yday; + if (day > 0x7fff) + return -EINVAL; + + rtd119x_rtc_set_enabled(dev, false); + + writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC); + writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN); + writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR); + writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1); + writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2); + + rtd119x_rtc_set_enabled(dev, true); + + return 0; +} + +static const struct rtc_class_ops rtd119x_rtc_ops = { + .read_time = rtd119x_rtc_read_time, + .set_time = rtd119x_rtc_set_time, +}; + +static const struct of_device_id rtd119x_rtc_dt_ids[] = { + { .compatible = "realtek,rtd1295-rtc" }, + { } +}; + +static int rtd119x_rtc_probe(struct platform_device *pdev) +{ + struct rtd119x_rtc *data; + struct resource *res; + u32 val; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + platform_set_drvdata(pdev, data); + data->base_year = 2014; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(data->base)) + return PTR_ERR(data->base); + + data->clk = of_clk_get(pdev->dev.of_node, 0); + if (IS_ERR(data->clk)) + return PTR_ERR(data->clk); + + ret = clk_prepare_enable(data->clk); + if (ret) { + clk_put(data->clk); + return ret; + } + + val = readl_relaxed(data->base + RTD_RTCACR); + if (!(val & RTD_RTCACR_RTCPWR)) { + writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR); + + rtd119x_rtc_reset(&pdev->dev); + + writel_relaxed(0, data->base + RTD_RTCMIN); + writel_relaxed(0, data->base + RTD_RTCHR); + writel_relaxed(0, data->base + RTD_RTCDATE1); + writel_relaxed(0, data->base + RTD_RTCDATE2); + } + + rtd119x_rtc_set_enabled(&pdev->dev, true); + + data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc", + &rtd119x_rtc_ops, THIS_MODULE); + if (IS_ERR(data->rtcdev)) { + dev_err(&pdev->dev, "failed to register rtc device"); + clk_disable_unprepare(data->clk); + clk_put(data->clk); + return PTR_ERR(data->rtcdev); + } + + return 0; +} + +static int rtd119x_rtc_remove(struct platform_device *pdev) +{ + struct rtd119x_rtc *data = platform_get_drvdata(pdev); + + rtd119x_rtc_set_enabled(&pdev->dev, false); + + clk_disable_unprepare(data->clk); + clk_put(data->clk); + + return 0; +} + +static struct platform_driver rtd119x_rtc_driver = { + .probe = rtd119x_rtc_probe, + .remove = rtd119x_rtc_remove, + .driver = { + .name = "rtd1295-rtc", + .of_match_table = rtd119x_rtc_dt_ids, + }, +}; +builtin_platform_driver(rtd119x_rtc_driver); diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c new file mode 100644 index 000000000..cfe3aece5 --- /dev/null +++ b/drivers/rtc/rtc-rv3029c2.c @@ -0,0 +1,1001 @@ +/* + * Micro Crystal RV-3029 / RV-3049 rtc class driver + * + * Author: Gregory Hermant <gregory.hermant@calao-systems.com> + * Michael Buesch <m@bues.ch> + * + * 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. + * + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/regmap.h> + +/* Register map */ +/* control section */ +#define RV3029_ONOFF_CTRL 0x00 +#define RV3029_ONOFF_CTRL_WE BIT(0) +#define RV3029_ONOFF_CTRL_TE BIT(1) +#define RV3029_ONOFF_CTRL_TAR BIT(2) +#define RV3029_ONOFF_CTRL_EERE BIT(3) +#define RV3029_ONOFF_CTRL_SRON BIT(4) +#define RV3029_ONOFF_CTRL_TD0 BIT(5) +#define RV3029_ONOFF_CTRL_TD1 BIT(6) +#define RV3029_ONOFF_CTRL_CLKINT BIT(7) +#define RV3029_IRQ_CTRL 0x01 +#define RV3029_IRQ_CTRL_AIE BIT(0) +#define RV3029_IRQ_CTRL_TIE BIT(1) +#define RV3029_IRQ_CTRL_V1IE BIT(2) +#define RV3029_IRQ_CTRL_V2IE BIT(3) +#define RV3029_IRQ_CTRL_SRIE BIT(4) +#define RV3029_IRQ_FLAGS 0x02 +#define RV3029_IRQ_FLAGS_AF BIT(0) +#define RV3029_IRQ_FLAGS_TF BIT(1) +#define RV3029_IRQ_FLAGS_V1IF BIT(2) +#define RV3029_IRQ_FLAGS_V2IF BIT(3) +#define RV3029_IRQ_FLAGS_SRF BIT(4) +#define RV3029_STATUS 0x03 +#define RV3029_STATUS_VLOW1 BIT(2) +#define RV3029_STATUS_VLOW2 BIT(3) +#define RV3029_STATUS_SR BIT(4) +#define RV3029_STATUS_PON BIT(5) +#define RV3029_STATUS_EEBUSY BIT(7) +#define RV3029_RST_CTRL 0x04 +#define RV3029_RST_CTRL_SYSR BIT(4) +#define RV3029_CONTROL_SECTION_LEN 0x05 + +/* watch section */ +#define RV3029_W_SEC 0x08 +#define RV3029_W_MINUTES 0x09 +#define RV3029_W_HOURS 0x0A +#define RV3029_REG_HR_12_24 BIT(6) /* 24h/12h mode */ +#define RV3029_REG_HR_PM BIT(5) /* PM/AM bit in 12h mode */ +#define RV3029_W_DATE 0x0B +#define RV3029_W_DAYS 0x0C +#define RV3029_W_MONTHS 0x0D +#define RV3029_W_YEARS 0x0E +#define RV3029_WATCH_SECTION_LEN 0x07 + +/* alarm section */ +#define RV3029_A_SC 0x10 +#define RV3029_A_MN 0x11 +#define RV3029_A_HR 0x12 +#define RV3029_A_DT 0x13 +#define RV3029_A_DW 0x14 +#define RV3029_A_MO 0x15 +#define RV3029_A_YR 0x16 +#define RV3029_A_AE_X BIT(7) +#define RV3029_ALARM_SECTION_LEN 0x07 + +/* timer section */ +#define RV3029_TIMER_LOW 0x18 +#define RV3029_TIMER_HIGH 0x19 + +/* temperature section */ +#define RV3029_TEMP_PAGE 0x20 + +/* eeprom data section */ +#define RV3029_E2P_EEDATA1 0x28 +#define RV3029_E2P_EEDATA2 0x29 +#define RV3029_E2PDATA_SECTION_LEN 0x02 + +/* eeprom control section */ +#define RV3029_CONTROL_E2P_EECTRL 0x30 +#define RV3029_EECTRL_THP BIT(0) /* temp scan interval */ +#define RV3029_EECTRL_THE BIT(1) /* thermometer enable */ +#define RV3029_EECTRL_FD0 BIT(2) /* CLKOUT */ +#define RV3029_EECTRL_FD1 BIT(3) /* CLKOUT */ +#define RV3029_TRICKLE_1K BIT(4) /* 1.5K resistance */ +#define RV3029_TRICKLE_5K BIT(5) /* 5K resistance */ +#define RV3029_TRICKLE_20K BIT(6) /* 20K resistance */ +#define RV3029_TRICKLE_80K BIT(7) /* 80K resistance */ +#define RV3029_TRICKLE_MASK (RV3029_TRICKLE_1K |\ + RV3029_TRICKLE_5K |\ + RV3029_TRICKLE_20K |\ + RV3029_TRICKLE_80K) +#define RV3029_TRICKLE_SHIFT 4 +#define RV3029_CONTROL_E2P_XOFFS 0x31 /* XTAL offset */ +#define RV3029_CONTROL_E2P_XOFFS_SIGN BIT(7) /* Sign: 1->pos, 0->neg */ +#define RV3029_CONTROL_E2P_QCOEF 0x32 /* XTAL temp drift coef */ +#define RV3029_CONTROL_E2P_TURNOVER 0x33 /* XTAL turnover temp (in *C) */ +#define RV3029_CONTROL_E2P_TOV_MASK 0x3F /* XTAL turnover temp mask */ + +/* user ram section */ +#define RV3029_USR1_RAM_PAGE 0x38 +#define RV3029_USR1_SECTION_LEN 0x04 +#define RV3029_USR2_RAM_PAGE 0x3C +#define RV3029_USR2_SECTION_LEN 0x04 + +struct rv3029_data { + struct device *dev; + struct rtc_device *rtc; + struct regmap *regmap; + int irq; +}; + +static int rv3029_read_regs(struct device *dev, u8 reg, u8 *buf, + unsigned int len) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + + if ((reg > RV3029_USR1_RAM_PAGE + 7) || + (reg + len > RV3029_USR1_RAM_PAGE + 8)) + return -EINVAL; + + return regmap_bulk_read(rv3029->regmap, reg, buf, len); +} + +static int rv3029_write_regs(struct device *dev, u8 reg, u8 const buf[], + unsigned int len) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + + if ((reg > RV3029_USR1_RAM_PAGE + 7) || + (reg + len > RV3029_USR1_RAM_PAGE + 8)) + return -EINVAL; + + return regmap_bulk_write(rv3029->regmap, reg, buf, len); +} + +static int rv3029_update_bits(struct device *dev, u8 reg, u8 mask, u8 set) +{ + u8 buf; + int ret; + + ret = rv3029_read_regs(dev, reg, &buf, 1); + if (ret < 0) + return ret; + buf &= ~mask; + buf |= set & mask; + ret = rv3029_write_regs(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static int rv3029_get_sr(struct device *dev, u8 *buf) +{ + int ret = rv3029_read_regs(dev, RV3029_STATUS, buf, 1); + + if (ret < 0) + return -EIO; + dev_dbg(dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]); + return 0; +} + +static int rv3029_set_sr(struct device *dev, u8 val) +{ + u8 buf[1]; + int sr; + + buf[0] = val; + sr = rv3029_write_regs(dev, RV3029_STATUS, buf, 1); + dev_dbg(dev, "status = 0x%.2x (%d)\n", buf[0], buf[0]); + if (sr < 0) + return -EIO; + return 0; +} + +static int rv3029_eeprom_busywait(struct device *dev) +{ + int i, ret; + u8 sr; + + for (i = 100; i > 0; i--) { + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + break; + if (!(sr & RV3029_STATUS_EEBUSY)) + break; + usleep_range(1000, 10000); + } + if (i <= 0) { + dev_err(dev, "EEPROM busy wait timeout.\n"); + return -ETIMEDOUT; + } + + return ret; +} + +static int rv3029_eeprom_exit(struct device *dev) +{ + /* Re-enable eeprom refresh */ + return rv3029_update_bits(dev, RV3029_ONOFF_CTRL, + RV3029_ONOFF_CTRL_EERE, + RV3029_ONOFF_CTRL_EERE); +} + +static int rv3029_eeprom_enter(struct device *dev) +{ + int ret; + u8 sr; + + /* Check whether we are in the allowed voltage range. */ + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + return ret; + if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) { + /* We clear the bits and retry once just in case + * we had a brown out in early startup. + */ + sr &= ~RV3029_STATUS_VLOW1; + sr &= ~RV3029_STATUS_VLOW2; + ret = rv3029_set_sr(dev, sr); + if (ret < 0) + return ret; + usleep_range(1000, 10000); + ret = rv3029_get_sr(dev, &sr); + if (ret < 0) + return ret; + if (sr & (RV3029_STATUS_VLOW1 | RV3029_STATUS_VLOW2)) { + dev_err(dev, + "Supply voltage is too low to safely access the EEPROM.\n"); + return -ENODEV; + } + } + + /* Disable eeprom refresh. */ + ret = rv3029_update_bits(dev, RV3029_ONOFF_CTRL, RV3029_ONOFF_CTRL_EERE, + 0); + if (ret < 0) + return ret; + + /* Wait for any previous eeprom accesses to finish. */ + ret = rv3029_eeprom_busywait(dev); + if (ret < 0) + rv3029_eeprom_exit(dev); + + return ret; +} + +static int rv3029_eeprom_read(struct device *dev, u8 reg, + u8 buf[], size_t len) +{ + int ret, err; + + err = rv3029_eeprom_enter(dev); + if (err < 0) + return err; + + ret = rv3029_read_regs(dev, reg, buf, len); + + err = rv3029_eeprom_exit(dev); + if (err < 0) + return err; + + return ret; +} + +static int rv3029_eeprom_write(struct device *dev, u8 reg, + u8 const buf[], size_t len) +{ + int ret, err; + size_t i; + u8 tmp; + + err = rv3029_eeprom_enter(dev); + if (err < 0) + return err; + + for (i = 0; i < len; i++, reg++) { + ret = rv3029_read_regs(dev, reg, &tmp, 1); + if (ret < 0) + break; + if (tmp != buf[i]) { + ret = rv3029_write_regs(dev, reg, &buf[i], 1); + if (ret < 0) + break; + } + ret = rv3029_eeprom_busywait(dev); + if (ret < 0) + break; + } + + err = rv3029_eeprom_exit(dev); + if (err < 0) + return err; + + return ret; +} + +static int rv3029_eeprom_update_bits(struct device *dev, + u8 reg, u8 mask, u8 set) +{ + u8 buf; + int ret; + + ret = rv3029_eeprom_read(dev, reg, &buf, 1); + if (ret < 0) + return ret; + buf &= ~mask; + buf |= set & mask; + ret = rv3029_eeprom_write(dev, reg, &buf, 1); + if (ret < 0) + return ret; + + return 0; +} + +static irqreturn_t rv3029_handle_irq(int irq, void *dev_id) +{ + struct device *dev = dev_id; + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + struct mutex *lock = &rv3029->rtc->ops_lock; + unsigned long events = 0; + u8 flags, controls; + int ret; + + mutex_lock(lock); + + ret = rv3029_read_regs(dev, RV3029_IRQ_CTRL, &controls, 1); + if (ret) { + dev_warn(dev, "Read IRQ Control Register error %d\n", ret); + mutex_unlock(lock); + return IRQ_NONE; + } + + ret = rv3029_read_regs(dev, RV3029_IRQ_FLAGS, &flags, 1); + if (ret) { + dev_warn(dev, "Read IRQ Flags Register error %d\n", ret); + mutex_unlock(lock); + return IRQ_NONE; + } + + if (flags & RV3029_IRQ_FLAGS_AF) { + flags &= ~RV3029_IRQ_FLAGS_AF; + controls &= ~RV3029_IRQ_CTRL_AIE; + events |= RTC_AF; + } + + if (events) { + rtc_update_irq(rv3029->rtc, 1, events); + rv3029_write_regs(dev, RV3029_IRQ_FLAGS, &flags, 1); + rv3029_write_regs(dev, RV3029_IRQ_CTRL, &controls, 1); + } + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +static int rv3029_read_time(struct device *dev, struct rtc_time *tm) +{ + u8 buf[1]; + int ret; + u8 regs[RV3029_WATCH_SECTION_LEN] = { 0, }; + + ret = rv3029_get_sr(dev, buf); + if (ret < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + ret = rv3029_read_regs(dev, RV3029_W_SEC, regs, + RV3029_WATCH_SECTION_LEN); + if (ret < 0) { + dev_err(dev, "%s: reading RTC section failed\n", __func__); + return ret; + } + + tm->tm_sec = bcd2bin(regs[RV3029_W_SEC - RV3029_W_SEC]); + tm->tm_min = bcd2bin(regs[RV3029_W_MINUTES - RV3029_W_SEC]); + + /* HR field has a more complex interpretation */ + { + const u8 _hr = regs[RV3029_W_HOURS - RV3029_W_SEC]; + + if (_hr & RV3029_REG_HR_12_24) { + /* 12h format */ + tm->tm_hour = bcd2bin(_hr & 0x1f); + if (_hr & RV3029_REG_HR_PM) /* PM flag set */ + tm->tm_hour += 12; + } else /* 24h format */ + tm->tm_hour = bcd2bin(_hr & 0x3f); + } + + tm->tm_mday = bcd2bin(regs[RV3029_W_DATE - RV3029_W_SEC]); + tm->tm_mon = bcd2bin(regs[RV3029_W_MONTHS - RV3029_W_SEC]) - 1; + tm->tm_year = bcd2bin(regs[RV3029_W_YEARS - RV3029_W_SEC]) + 100; + tm->tm_wday = bcd2bin(regs[RV3029_W_DAYS - RV3029_W_SEC]) - 1; + + return 0; +} + +static int rv3029_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) +{ + struct rtc_time *const tm = &alarm->time; + int ret; + u8 regs[8], controls, flags; + + ret = rv3029_get_sr(dev, regs); + if (ret < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + ret = rv3029_read_regs(dev, RV3029_A_SC, regs, + RV3029_ALARM_SECTION_LEN); + + if (ret < 0) { + dev_err(dev, "%s: reading alarm section failed\n", __func__); + return ret; + } + + ret = rv3029_read_regs(dev, RV3029_IRQ_CTRL, &controls, 1); + if (ret) { + dev_err(dev, "Read IRQ Control Register error %d\n", ret); + return ret; + } + ret = rv3029_read_regs(dev, RV3029_IRQ_FLAGS, &flags, 1); + if (ret < 0) { + dev_err(dev, "Read IRQ Flags Register error %d\n", ret); + return ret; + } + + tm->tm_sec = bcd2bin(regs[RV3029_A_SC - RV3029_A_SC] & 0x7f); + tm->tm_min = bcd2bin(regs[RV3029_A_MN - RV3029_A_SC] & 0x7f); + tm->tm_hour = bcd2bin(regs[RV3029_A_HR - RV3029_A_SC] & 0x3f); + tm->tm_mday = bcd2bin(regs[RV3029_A_DT - RV3029_A_SC] & 0x3f); + tm->tm_mon = bcd2bin(regs[RV3029_A_MO - RV3029_A_SC] & 0x1f) - 1; + tm->tm_year = bcd2bin(regs[RV3029_A_YR - RV3029_A_SC] & 0x7f) + 100; + tm->tm_wday = bcd2bin(regs[RV3029_A_DW - RV3029_A_SC] & 0x07) - 1; + + alarm->enabled = !!(controls & RV3029_IRQ_CTRL_AIE); + alarm->pending = (flags & RV3029_IRQ_FLAGS_AF) && alarm->enabled; + + return 0; +} + +static int rv3029_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + int ret; + u8 controls; + + ret = rv3029_read_regs(dev, RV3029_IRQ_CTRL, &controls, 1); + if (ret < 0) { + dev_warn(dev, "Read IRQ Control Register error %d\n", ret); + return ret; + } + + /* enable/disable AIE irq */ + if (enable) + controls |= RV3029_IRQ_CTRL_AIE; + else + controls &= ~RV3029_IRQ_CTRL_AIE; + + ret = rv3029_write_regs(dev, RV3029_IRQ_CTRL, &controls, 1); + if (ret < 0) { + dev_err(dev, "can't update INT reg\n"); + return ret; + } + + return 0; +} + +static int rv3029_set_alarm(struct device *dev, 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 = rv3029_get_sr(dev, regs); + if (ret < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return -EIO; + } + + /* Activate all the alarms with AE_x bit */ + regs[RV3029_A_SC - RV3029_A_SC] = bin2bcd(tm->tm_sec) | RV3029_A_AE_X; + regs[RV3029_A_MN - RV3029_A_SC] = bin2bcd(tm->tm_min) | RV3029_A_AE_X; + regs[RV3029_A_HR - RV3029_A_SC] = (bin2bcd(tm->tm_hour) & 0x3f) + | RV3029_A_AE_X; + regs[RV3029_A_DT - RV3029_A_SC] = (bin2bcd(tm->tm_mday) & 0x3f) + | RV3029_A_AE_X; + regs[RV3029_A_MO - RV3029_A_SC] = (bin2bcd(tm->tm_mon + 1) & 0x1f) + | RV3029_A_AE_X; + regs[RV3029_A_DW - RV3029_A_SC] = (bin2bcd(tm->tm_wday + 1) & 0x7) + | RV3029_A_AE_X; + regs[RV3029_A_YR - RV3029_A_SC] = (bin2bcd(tm->tm_year - 100)) + | RV3029_A_AE_X; + + /* Write the alarm */ + ret = rv3029_write_regs(dev, RV3029_A_SC, regs, + RV3029_ALARM_SECTION_LEN); + if (ret < 0) + return ret; + + if (alarm->enabled) { + /* enable AIE irq */ + ret = rv3029_alarm_irq_enable(dev, 1); + if (ret) + return ret; + } else { + /* disable AIE irq */ + ret = rv3029_alarm_irq_enable(dev, 0); + if (ret) + return ret; + } + + return 0; +} + +static int rv3029_set_time(struct device *dev, struct rtc_time *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[RV3029_W_SEC - RV3029_W_SEC] = bin2bcd(tm->tm_sec); + regs[RV3029_W_MINUTES - RV3029_W_SEC] = bin2bcd(tm->tm_min); + regs[RV3029_W_HOURS - RV3029_W_SEC] = bin2bcd(tm->tm_hour); + regs[RV3029_W_DATE - RV3029_W_SEC] = bin2bcd(tm->tm_mday); + regs[RV3029_W_MONTHS - RV3029_W_SEC] = bin2bcd(tm->tm_mon + 1); + regs[RV3029_W_DAYS - RV3029_W_SEC] = bin2bcd(tm->tm_wday + 1) & 0x7; + regs[RV3029_W_YEARS - RV3029_W_SEC] = bin2bcd(tm->tm_year - 100); + + ret = rv3029_write_regs(dev, RV3029_W_SEC, regs, + RV3029_WATCH_SECTION_LEN); + if (ret < 0) + return ret; + + ret = rv3029_get_sr(dev, regs); + if (ret < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return ret; + } + /* clear PON bit */ + ret = rv3029_set_sr(dev, (regs[0] & ~RV3029_STATUS_PON)); + if (ret < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct rv3029_trickle_tab_elem { + u32 r; /* resistance in ohms */ + u8 conf; /* trickle config bits */ +} rv3029_trickle_tab[] = { + { + .r = 1076, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 1091, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_20K, + }, { + .r = 1137, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K | + RV3029_TRICKLE_80K, + }, { + .r = 1154, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_5K, + }, { + .r = 1371, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 1395, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_20K, + }, { + .r = 1472, + .conf = RV3029_TRICKLE_1K | RV3029_TRICKLE_80K, + }, { + .r = 1500, + .conf = RV3029_TRICKLE_1K, + }, { + .r = 3810, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K | + RV3029_TRICKLE_80K, + }, { + .r = 4000, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_20K, + }, { + .r = 4706, + .conf = RV3029_TRICKLE_5K | RV3029_TRICKLE_80K, + }, { + .r = 5000, + .conf = RV3029_TRICKLE_5K, + }, { + .r = 16000, + .conf = RV3029_TRICKLE_20K | RV3029_TRICKLE_80K, + }, { + .r = 20000, + .conf = RV3029_TRICKLE_20K, + }, { + .r = 80000, + .conf = RV3029_TRICKLE_80K, + }, +}; + +static void rv3029_trickle_config(struct device *dev) +{ + struct device_node *of_node = dev->of_node; + const struct rv3029_trickle_tab_elem *elem; + int i, err; + u32 ohms; + u8 trickle_set_bits; + + if (!of_node) + return; + + /* Configure the trickle charger. */ + err = of_property_read_u32(of_node, "trickle-resistor-ohms", &ohms); + if (err) { + /* Disable trickle charger. */ + trickle_set_bits = 0; + } else { + /* Enable trickle charger. */ + for (i = 0; i < ARRAY_SIZE(rv3029_trickle_tab); i++) { + elem = &rv3029_trickle_tab[i]; + if (elem->r >= ohms) + break; + } + trickle_set_bits = elem->conf; + dev_info(dev, + "Trickle charger enabled at %d ohms resistance.\n", + elem->r); + } + err = rv3029_eeprom_update_bits(dev, RV3029_CONTROL_E2P_EECTRL, + RV3029_TRICKLE_MASK, + trickle_set_bits); + if (err < 0) + dev_err(dev, "Failed to update trickle charger config\n"); +} + +#ifdef CONFIG_RTC_DRV_RV3029_HWMON + +static int rv3029_read_temp(struct device *dev, int *temp_mC) +{ + int ret; + u8 temp; + + ret = rv3029_read_regs(dev, RV3029_TEMP_PAGE, &temp, 1); + if (ret < 0) + return ret; + + *temp_mC = ((int)temp - 60) * 1000; + + return 0; +} + +static ssize_t rv3029_hwmon_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, temp_mC; + + ret = rv3029_read_temp(dev, &temp_mC); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", temp_mC); +} + +static ssize_t rv3029_hwmon_set_update_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + unsigned long interval_ms; + int ret; + u8 th_set_bits = 0; + + ret = kstrtoul(buf, 10, &interval_ms); + if (ret < 0) + return ret; + + if (interval_ms != 0) { + th_set_bits |= RV3029_EECTRL_THE; + if (interval_ms >= 16000) + th_set_bits |= RV3029_EECTRL_THP; + } + ret = rv3029_eeprom_update_bits(dev, RV3029_CONTROL_E2P_EECTRL, + RV3029_EECTRL_THE | RV3029_EECTRL_THP, + th_set_bits); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t rv3029_hwmon_show_update_interval(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret, interval_ms; + u8 eectrl; + + ret = rv3029_eeprom_read(dev, RV3029_CONTROL_E2P_EECTRL, + &eectrl, 1); + if (ret < 0) + return ret; + + if (eectrl & RV3029_EECTRL_THE) { + if (eectrl & RV3029_EECTRL_THP) + interval_ms = 16000; + else + interval_ms = 1000; + } else { + interval_ms = 0; + } + + return sprintf(buf, "%d\n", interval_ms); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, rv3029_hwmon_show_temp, + NULL, 0); +static SENSOR_DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, + rv3029_hwmon_show_update_interval, + rv3029_hwmon_set_update_interval, 0); + +static struct attribute *rv3029_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_update_interval.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(rv3029_hwmon); + +static void rv3029_hwmon_register(struct device *dev, const char *name) +{ + struct rv3029_data *rv3029 = dev_get_drvdata(dev); + struct device *hwmon_dev; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, name, rv3029, + rv3029_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + dev_warn(dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } +} + +#else /* CONFIG_RTC_DRV_RV3029_HWMON */ + +static void rv3029_hwmon_register(struct device *dev, const char *name) +{ +} + +#endif /* CONFIG_RTC_DRV_RV3029_HWMON */ + +static struct rtc_class_ops rv3029_rtc_ops = { + .read_time = rv3029_read_time, + .set_time = rv3029_set_time, +}; + +static int rv3029_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name) +{ + struct rv3029_data *rv3029; + int rc = 0; + u8 buf[1]; + + rv3029 = devm_kzalloc(dev, sizeof(*rv3029), GFP_KERNEL); + if (!rv3029) + return -ENOMEM; + + rv3029->regmap = regmap; + rv3029->irq = irq; + rv3029->dev = dev; + dev_set_drvdata(dev, rv3029); + + rc = rv3029_get_sr(dev, buf); + if (rc < 0) { + dev_err(dev, "reading status failed\n"); + return rc; + } + + rv3029_trickle_config(dev); + rv3029_hwmon_register(dev, name); + + rv3029->rtc = devm_rtc_device_register(dev, name, &rv3029_rtc_ops, + THIS_MODULE); + if (IS_ERR(rv3029->rtc)) { + dev_err(dev, "unable to register the class device\n"); + return PTR_ERR(rv3029->rtc); + } + + if (rv3029->irq > 0) { + rc = devm_request_threaded_irq(dev, rv3029->irq, + NULL, rv3029_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rv3029", dev); + if (rc) { + dev_warn(dev, "unable to request IRQ, alarms disabled\n"); + rv3029->irq = 0; + } else { + rv3029_rtc_ops.read_alarm = rv3029_read_alarm; + rv3029_rtc_ops.set_alarm = rv3029_set_alarm; + rv3029_rtc_ops.alarm_irq_enable = rv3029_alarm_irq_enable; + } + } + + return 0; +} + +#if IS_ENABLED(CONFIG_I2C) + +static int rv3029_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + }; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK | + I2C_FUNC_SMBUS_BYTE)) { + dev_err(&client->dev, "Adapter does not support SMBUS_I2C_BLOCK or SMBUS_I2C_BYTE\n"); + return -ENODEV; + } + + regmap = devm_regmap_init_i2c(client, &config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return rv3029_probe(&client->dev, regmap, client->irq, client->name); +} + +static const struct i2c_device_id rv3029_id[] = { + { "rv3029", 0 }, + { "rv3029c2", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv3029_id); + +static const struct of_device_id rv3029_of_match[] = { + { .compatible = "microcrystal,rv3029" }, + /* Backward compatibility only, do not use compatibles below: */ + { .compatible = "rv3029" }, + { .compatible = "rv3029c2" }, + { .compatible = "mc,rv3029c2" }, + { } +}; +MODULE_DEVICE_TABLE(of, rv3029_of_match); + +static struct i2c_driver rv3029_driver = { + .driver = { + .name = "rtc-rv3029c2", + .of_match_table = of_match_ptr(rv3029_of_match), + }, + .probe = rv3029_i2c_probe, + .id_table = rv3029_id, +}; + +static int rv3029_register_driver(void) +{ + return i2c_add_driver(&rv3029_driver); +} + +static void rv3029_unregister_driver(void) +{ + i2c_del_driver(&rv3029_driver); +} + +#else + +static int rv3029_register_driver(void) +{ + return 0; +} + +static void rv3029_unregister_driver(void) +{ +} + +#endif + +#if IS_ENABLED(CONFIG_SPI_MASTER) + +static int rv3049_probe(struct spi_device *spi) +{ + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + }; + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n", + __func__, PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + return rv3029_probe(&spi->dev, regmap, spi->irq, "rv3049"); +} + +static struct spi_driver rv3049_driver = { + .driver = { + .name = "rv3049", + }, + .probe = rv3049_probe, +}; + +static int rv3049_register_driver(void) +{ + return spi_register_driver(&rv3049_driver); +} + +static void rv3049_unregister_driver(void) +{ + spi_unregister_driver(&rv3049_driver); +} + +#else + +static int rv3049_register_driver(void) +{ + return 0; +} + +static void rv3049_unregister_driver(void) +{ +} + +#endif + +static int __init rv30x9_init(void) +{ + int ret; + + ret = rv3029_register_driver(); + if (ret) { + pr_err("Failed to register rv3029 driver: %d\n", ret); + return ret; + } + + ret = rv3049_register_driver(); + if (ret) { + pr_err("Failed to register rv3049 driver: %d\n", ret); + rv3029_unregister_driver(); + } + + return ret; +} +module_init(rv30x9_init) + +static void __exit rv30x9_exit(void) +{ + rv3049_unregister_driver(); + rv3029_unregister_driver(); +} +module_exit(rv30x9_exit) + +MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>"); +MODULE_AUTHOR("Michael Buesch <m@bues.ch>"); +MODULE_DESCRIPTION("Micro Crystal RV3029/RV3049 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:rv3049"); diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c new file mode 100644 index 000000000..17ccef5d5 --- /dev/null +++ b/drivers/rtc/rtc-rv8803.c @@ -0,0 +1,648 @@ +/* + * RTC driver for the Micro Crystal RV8803 + * + * Copyright (C) 2015 Micro Crystal SA + * + * 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/bitops.h> +#include <linux/log2.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/rtc.h> + +#define RV8803_I2C_TRY_COUNT 4 + +#define RV8803_SEC 0x00 +#define RV8803_MIN 0x01 +#define RV8803_HOUR 0x02 +#define RV8803_WEEK 0x03 +#define RV8803_DAY 0x04 +#define RV8803_MONTH 0x05 +#define RV8803_YEAR 0x06 +#define RV8803_RAM 0x07 +#define RV8803_ALARM_MIN 0x08 +#define RV8803_ALARM_HOUR 0x09 +#define RV8803_ALARM_WEEK_OR_DAY 0x0A +#define RV8803_EXT 0x0D +#define RV8803_FLAG 0x0E +#define RV8803_CTRL 0x0F + +#define RV8803_EXT_WADA BIT(6) + +#define RV8803_FLAG_V1F BIT(0) +#define RV8803_FLAG_V2F BIT(1) +#define RV8803_FLAG_AF BIT(3) +#define RV8803_FLAG_TF BIT(4) +#define RV8803_FLAG_UF BIT(5) + +#define RV8803_CTRL_RESET BIT(0) + +#define RV8803_CTRL_EIE BIT(2) +#define RV8803_CTRL_AIE BIT(3) +#define RV8803_CTRL_TIE BIT(4) +#define RV8803_CTRL_UIE BIT(5) + +#define RX8900_BACKUP_CTRL 0x18 +#define RX8900_FLAG_SWOFF BIT(2) +#define RX8900_FLAG_VDETOFF BIT(3) + +enum rv8803_type { + rv_8803, + rx_8900 +}; + +struct rv8803_data { + struct i2c_client *client; + struct rtc_device *rtc; + struct mutex flags_lock; + u8 ctrl; + enum rv8803_type type; +}; + +static int rv8803_read_reg(const struct i2c_client *client, u8 reg) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + /* + * There is a 61µs window during which the RTC does not acknowledge I2C + * transfers. In that case, ensure that there are multiple attempts. + */ + do + ret = i2c_smbus_read_byte_data(client, reg); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret < 0) + dev_err(&client->dev, "Unable to read register 0x%02x\n", reg); + + return ret; +} + +static int rv8803_read_regs(const struct i2c_client *client, + u8 reg, u8 count, u8 *values) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + do + ret = i2c_smbus_read_i2c_block_data(client, reg, count, values); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret != count) { + dev_err(&client->dev, + "Unable to read registers 0x%02x..0x%02x\n", + reg, reg + count - 1); + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int rv8803_write_reg(const struct i2c_client *client, u8 reg, u8 value) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + do + ret = i2c_smbus_write_byte_data(client, reg, value); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret) + dev_err(&client->dev, "Unable to write register 0x%02x\n", reg); + + return ret; +} + +static int rv8803_write_regs(const struct i2c_client *client, + u8 reg, u8 count, const u8 *values) +{ + int try = RV8803_I2C_TRY_COUNT; + s32 ret; + + do + ret = i2c_smbus_write_i2c_block_data(client, reg, count, + values); + while ((ret == -ENXIO || ret == -EIO) && --try); + if (ret) + dev_err(&client->dev, + "Unable to write registers 0x%02x..0x%02x\n", + reg, reg + count - 1); + + return ret; +} + +static irqreturn_t rv8803_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rv8803_data *rv8803 = i2c_get_clientdata(client); + unsigned long events = 0; + int flags; + + mutex_lock(&rv8803->flags_lock); + + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags <= 0) { + mutex_unlock(&rv8803->flags_lock); + return IRQ_NONE; + } + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (flags & RV8803_FLAG_TF) { + flags &= ~RV8803_FLAG_TF; + rv8803->ctrl &= ~RV8803_CTRL_TIE; + events |= RTC_PF; + } + + if (flags & RV8803_FLAG_AF) { + flags &= ~RV8803_FLAG_AF; + rv8803->ctrl &= ~RV8803_CTRL_AIE; + events |= RTC_AF; + } + + if (flags & RV8803_FLAG_UF) { + flags &= ~RV8803_FLAG_UF; + rv8803->ctrl &= ~RV8803_CTRL_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv8803->rtc, 1, events); + rv8803_write_reg(client, RV8803_FLAG, flags); + rv8803_write_reg(rv8803->client, RV8803_CTRL, rv8803->ctrl); + } + + mutex_unlock(&rv8803->flags_lock); + + return IRQ_HANDLED; +} + +static int rv8803_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 date1[7]; + u8 date2[7]; + u8 *date = date1; + int ret, flags; + + flags = rv8803_read_reg(rv8803->client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V2F) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = rv8803_read_regs(rv8803->client, RV8803_SEC, 7, date); + if (ret) + return ret; + + if ((date1[RV8803_SEC] & 0x7f) == bin2bcd(59)) { + ret = rv8803_read_regs(rv8803->client, RV8803_SEC, 7, date2); + if (ret) + return ret; + + if ((date2[RV8803_SEC] & 0x7f) != bin2bcd(59)) + date = date2; + } + + tm->tm_sec = bcd2bin(date[RV8803_SEC] & 0x7f); + tm->tm_min = bcd2bin(date[RV8803_MIN] & 0x7f); + tm->tm_hour = bcd2bin(date[RV8803_HOUR] & 0x3f); + tm->tm_wday = ilog2(date[RV8803_WEEK] & 0x7f); + tm->tm_mday = bcd2bin(date[RV8803_DAY] & 0x3f); + tm->tm_mon = bcd2bin(date[RV8803_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[RV8803_YEAR]) + 100; + + return 0; +} + +static int rv8803_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 date[7]; + int ctrl, flags, ret; + + if ((tm->tm_year < 100) || (tm->tm_year > 199)) + return -EINVAL; + + ctrl = rv8803_read_reg(rv8803->client, RV8803_CTRL); + if (ctrl < 0) + return ctrl; + + /* Stop the clock */ + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + ctrl | RV8803_CTRL_RESET); + if (ret) + return ret; + + date[RV8803_SEC] = bin2bcd(tm->tm_sec); + date[RV8803_MIN] = bin2bcd(tm->tm_min); + date[RV8803_HOUR] = bin2bcd(tm->tm_hour); + date[RV8803_WEEK] = 1 << (tm->tm_wday); + date[RV8803_DAY] = bin2bcd(tm->tm_mday); + date[RV8803_MONTH] = bin2bcd(tm->tm_mon + 1); + date[RV8803_YEAR] = bin2bcd(tm->tm_year - 100); + + ret = rv8803_write_regs(rv8803->client, RV8803_SEC, 7, date); + if (ret) + return ret; + + /* Restart the clock */ + ret = rv8803_write_reg(rv8803->client, RV8803_CTRL, + ctrl & ~RV8803_CTRL_RESET); + if (ret) + return ret; + + mutex_lock(&rv8803->flags_lock); + + flags = rv8803_read_reg(rv8803->client, RV8803_FLAG); + if (flags < 0) { + mutex_unlock(&rv8803->flags_lock); + return flags; + } + + ret = rv8803_write_reg(rv8803->client, RV8803_FLAG, + flags & ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F)); + + mutex_unlock(&rv8803->flags_lock); + + return ret; +} + +static int rv8803_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + struct i2c_client *client = rv8803->client; + u8 alarmvals[3]; + int flags, ret; + + ret = rv8803_read_regs(client, RV8803_ALARM_MIN, 3, alarmvals); + if (ret) + return ret; + + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) + return flags; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); + + alrm->enabled = !!(rv8803->ctrl & RV8803_CTRL_AIE); + alrm->pending = (flags & RV8803_FLAG_AF) && alrm->enabled; + + return 0; +} + +static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl[2]; + int ret, err; + + /* The alarm has no seconds, round up to nearest minute */ + if (alrm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&alrm->time); + + alarm_time += 60 - alrm->time.tm_sec; + rtc_time64_to_tm(alarm_time, &alrm->time); + } + + mutex_lock(&rv8803->flags_lock); + + ret = rv8803_read_regs(client, RV8803_FLAG, 2, ctrl); + if (ret) { + mutex_unlock(&rv8803->flags_lock); + return ret; + } + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + if (rv8803->ctrl & (RV8803_CTRL_AIE | RV8803_CTRL_UIE)) { + rv8803->ctrl &= ~(RV8803_CTRL_AIE | RV8803_CTRL_UIE); + err = rv8803_write_reg(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + if (err) { + mutex_unlock(&rv8803->flags_lock); + return err; + } + } + + ctrl[1] &= ~RV8803_FLAG_AF; + err = rv8803_write_reg(rv8803->client, RV8803_FLAG, ctrl[1]); + mutex_unlock(&rv8803->flags_lock); + if (err) + return err; + + err = rv8803_write_regs(rv8803->client, RV8803_ALARM_MIN, 3, alarmvals); + if (err) + return err; + + if (alrm->enabled) { + if (rv8803->rtc->uie_rtctimer.enabled) + rv8803->ctrl |= RV8803_CTRL_UIE; + if (rv8803->rtc->aie_timer.enabled) + rv8803->ctrl |= RV8803_CTRL_AIE; + + err = rv8803_write_reg(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + if (err) + return err; + } + + return 0; +} + +static int rv8803_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + int ctrl, flags, err; + + ctrl = rv8803->ctrl; + + if (enabled) { + if (rv8803->rtc->uie_rtctimer.enabled) + ctrl |= RV8803_CTRL_UIE; + if (rv8803->rtc->aie_timer.enabled) + ctrl |= RV8803_CTRL_AIE; + } else { + if (!rv8803->rtc->uie_rtctimer.enabled) + ctrl &= ~RV8803_CTRL_UIE; + if (!rv8803->rtc->aie_timer.enabled) + ctrl &= ~RV8803_CTRL_AIE; + } + + mutex_lock(&rv8803->flags_lock); + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) { + mutex_unlock(&rv8803->flags_lock); + return flags; + } + flags &= ~(RV8803_FLAG_AF | RV8803_FLAG_UF); + err = rv8803_write_reg(client, RV8803_FLAG, flags); + mutex_unlock(&rv8803->flags_lock); + if (err) + return err; + + if (ctrl != rv8803->ctrl) { + rv8803->ctrl = ctrl; + err = rv8803_write_reg(client, RV8803_CTRL, rv8803->ctrl); + if (err) + return err; + } + + return 0; +} + +static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + int flags, ret = 0; + + switch (cmd) { + case RTC_VL_READ: + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + flags &= RV8803_FLAG_V1F | RV8803_FLAG_V2F; + + if (copy_to_user((void __user *)arg, &flags, sizeof(int))) + return -EFAULT; + + return 0; + + case RTC_VL_CLR: + mutex_lock(&rv8803->flags_lock); + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) { + mutex_unlock(&rv8803->flags_lock); + return flags; + } + + flags &= ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F); + ret = rv8803_write_reg(client, RV8803_FLAG, flags); + mutex_unlock(&rv8803->flags_lock); + if (ret) + return ret; + + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static int rv8803_nvram_write(void *priv, unsigned int offset, void *val, + size_t bytes) +{ + int ret; + + ret = rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val); + if (ret) + return ret; + + return 0; +} + +static int rv8803_nvram_read(void *priv, unsigned int offset, + void *val, size_t bytes) +{ + int ret; + + ret = rv8803_read_reg(priv, RV8803_RAM); + if (ret < 0) + return ret; + + *(u8 *)val = ret; + + return 0; +} + +static struct rtc_class_ops rv8803_rtc_ops = { + .read_time = rv8803_get_time, + .set_time = rv8803_set_time, + .ioctl = rv8803_ioctl, +}; + +static int rx8900_trickle_charger_init(struct rv8803_data *rv8803) +{ + struct i2c_client *client = rv8803->client; + struct device_node *node = client->dev.of_node; + int err; + u8 flags; + + if (!node) + return 0; + + if (rv8803->type != rx_8900) + return 0; + + err = i2c_smbus_read_byte_data(rv8803->client, RX8900_BACKUP_CTRL); + if (err < 0) + return err; + + flags = ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF) & (u8)err; + + if (of_property_read_bool(node, "epson,vdet-disable")) + flags |= RX8900_FLAG_VDETOFF; + + if (of_property_read_bool(node, "trickle-diode-disable")) + flags |= RX8900_FLAG_SWOFF; + + return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL, + flags); +} + +static int rv8803_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct rv8803_data *rv8803; + int err, flags; + struct nvmem_config nvmem_cfg = { + .name = "rv8803_nvram", + .word_size = 1, + .stride = 1, + .size = 1, + .reg_read = rv8803_nvram_read, + .reg_write = rv8803_nvram_write, + .priv = client, + }; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&adapter->dev, "doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n"); + return -EIO; + } + + rv8803 = devm_kzalloc(&client->dev, sizeof(struct rv8803_data), + GFP_KERNEL); + if (!rv8803) + return -ENOMEM; + + mutex_init(&rv8803->flags_lock); + rv8803->client = client; + if (client->dev.of_node) + rv8803->type = (enum rv8803_type) + of_device_get_match_data(&client->dev); + else + rv8803->type = id->driver_data; + i2c_set_clientdata(client, rv8803); + + flags = rv8803_read_reg(client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (flags & RV8803_FLAG_AF) + dev_warn(&client->dev, "An alarm maybe have been missed.\n"); + + rv8803->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rv8803->rtc)) { + return PTR_ERR(rv8803->rtc); + } + + if (client->irq > 0) { + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv8803_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rv8803", client); + if (err) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } else { + rv8803_rtc_ops.read_alarm = rv8803_get_alarm; + rv8803_rtc_ops.set_alarm = rv8803_set_alarm; + rv8803_rtc_ops.alarm_irq_enable = rv8803_alarm_irq_enable; + } + } + + err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA); + if (err) + return err; + + err = rx8900_trickle_charger_init(rv8803); + if (err) { + dev_err(&client->dev, "failed to init charger\n"); + return err; + } + + rv8803->rtc->ops = &rv8803_rtc_ops; + rv8803->rtc->nvram_old_abi = true; + err = rtc_register_device(rv8803->rtc); + if (err) + return err; + + rtc_nvmem_register(rv8803->rtc, &nvmem_cfg); + + rv8803->rtc->max_user_freq = 1; + + return 0; +} + +static const struct i2c_device_id rv8803_id[] = { + { "rv8803", rv_8803 }, + { "rx8900", rx_8900 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv8803_id); + +static const struct of_device_id rv8803_of_match[] = { + { + .compatible = "microcrystal,rv8803", + .data = (void *)rv_8803 + }, + { + .compatible = "epson,rx8900", + .data = (void *)rx_8900 + }, + { } +}; +MODULE_DEVICE_TABLE(of, rv8803_of_match); + +static struct i2c_driver rv8803_driver = { + .driver = { + .name = "rtc-rv8803", + .of_match_table = of_match_ptr(rv8803_of_match), + }, + .probe = rv8803_probe, + .id_table = rv8803_id, +}; +module_i2c_driver(rv8803_driver); + +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); +MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rx4581.c b/drivers/rtc/rtc-rx4581.c new file mode 100644 index 000000000..c59a218bd --- /dev/null +++ b/drivers/rtc/rtc-rx4581.c @@ -0,0 +1,300 @@ +/* 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); + + return 0; +} + +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", + }, + .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-rx6110.c b/drivers/rtc/rtc-rx6110.c new file mode 100644 index 000000000..8e322d884 --- /dev/null +++ b/drivers/rtc/rtc-rx6110.c @@ -0,0 +1,401 @@ +/* + * Driver for the Epson RTC module RX-6110 SA + * + * Copyright(C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de> + * Copyright(C) SEIKO EPSON CORPORATION 2013. All rights reserved. + * + * This driver software is distributed as is, without any warranty of any kind, + * either express or implied as further specified in the GNU Public License. + * This software may be used and distributed according to the terms of the GNU + * Public License, version 2 as published by the Free Software Foundation. + * See the file COPYING in the main directory of this archive 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/bcd.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> + +/* RX-6110 Register definitions */ +#define RX6110_REG_SEC 0x10 +#define RX6110_REG_MIN 0x11 +#define RX6110_REG_HOUR 0x12 +#define RX6110_REG_WDAY 0x13 +#define RX6110_REG_MDAY 0x14 +#define RX6110_REG_MONTH 0x15 +#define RX6110_REG_YEAR 0x16 +#define RX6110_REG_RES1 0x17 +#define RX6110_REG_ALMIN 0x18 +#define RX6110_REG_ALHOUR 0x19 +#define RX6110_REG_ALWDAY 0x1A +#define RX6110_REG_TCOUNT0 0x1B +#define RX6110_REG_TCOUNT1 0x1C +#define RX6110_REG_EXT 0x1D +#define RX6110_REG_FLAG 0x1E +#define RX6110_REG_CTRL 0x1F +#define RX6110_REG_USER0 0x20 +#define RX6110_REG_USER1 0x21 +#define RX6110_REG_USER2 0x22 +#define RX6110_REG_USER3 0x23 +#define RX6110_REG_USER4 0x24 +#define RX6110_REG_USER5 0x25 +#define RX6110_REG_USER6 0x26 +#define RX6110_REG_USER7 0x27 +#define RX6110_REG_USER8 0x28 +#define RX6110_REG_USER9 0x29 +#define RX6110_REG_USERA 0x2A +#define RX6110_REG_USERB 0x2B +#define RX6110_REG_USERC 0x2C +#define RX6110_REG_USERD 0x2D +#define RX6110_REG_USERE 0x2E +#define RX6110_REG_USERF 0x2F +#define RX6110_REG_RES2 0x30 +#define RX6110_REG_RES3 0x31 +#define RX6110_REG_IRQ 0x32 + +#define RX6110_BIT_ALARM_EN BIT(7) + +/* Extension Register (1Dh) bit positions */ +#define RX6110_BIT_EXT_TSEL0 BIT(0) +#define RX6110_BIT_EXT_TSEL1 BIT(1) +#define RX6110_BIT_EXT_TSEL2 BIT(2) +#define RX6110_BIT_EXT_WADA BIT(3) +#define RX6110_BIT_EXT_TE BIT(4) +#define RX6110_BIT_EXT_USEL BIT(5) +#define RX6110_BIT_EXT_FSEL0 BIT(6) +#define RX6110_BIT_EXT_FSEL1 BIT(7) + +/* Flag Register (1Eh) bit positions */ +#define RX6110_BIT_FLAG_VLF BIT(1) +#define RX6110_BIT_FLAG_AF BIT(3) +#define RX6110_BIT_FLAG_TF BIT(4) +#define RX6110_BIT_FLAG_UF BIT(5) + +/* Control Register (1Fh) bit positions */ +#define RX6110_BIT_CTRL_TBKE BIT(0) +#define RX6110_BIT_CTRL_TBKON BIT(1) +#define RX6110_BIT_CTRL_TSTP BIT(2) +#define RX6110_BIT_CTRL_AIE BIT(3) +#define RX6110_BIT_CTRL_TIE BIT(4) +#define RX6110_BIT_CTRL_UIE BIT(5) +#define RX6110_BIT_CTRL_STOP BIT(6) +#define RX6110_BIT_CTRL_TEST BIT(7) + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WDAY, + RTC_MDAY, + RTC_MONTH, + RTC_YEAR, + RTC_NR_TIME +}; + +#define RX6110_DRIVER_NAME "rx6110" + +struct rx6110_data { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +/** + * rx6110_rtc_tm_to_data - convert rtc_time to native time encoding + * + * @tm: holds date and time + * @data: holds the encoding in rx6110 native form + */ +static int rx6110_rtc_tm_to_data(struct rtc_time *tm, u8 *data) +{ + pr_debug("%s: date %ds %dm %dh %dmd %dm %dy\n", __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year); + + /* + * The year in the RTC is a value between 0 and 99. + * Assume that this represents the current century + * and disregard all other values. + */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + return -EINVAL; + + data[RTC_SEC] = bin2bcd(tm->tm_sec); + data[RTC_MIN] = bin2bcd(tm->tm_min); + data[RTC_HOUR] = bin2bcd(tm->tm_hour); + data[RTC_WDAY] = BIT(bin2bcd(tm->tm_wday)); + data[RTC_MDAY] = bin2bcd(tm->tm_mday); + data[RTC_MONTH] = bin2bcd(tm->tm_mon + 1); + data[RTC_YEAR] = bin2bcd(tm->tm_year % 100); + + return 0; +} + +/** + * rx6110_data_to_rtc_tm - convert native time encoding to rtc_time + * + * @data: holds the encoding in rx6110 native form + * @tm: holds date and time + */ +static int rx6110_data_to_rtc_tm(u8 *data, struct rtc_time *tm) +{ + tm->tm_sec = bcd2bin(data[RTC_SEC] & 0x7f); + tm->tm_min = bcd2bin(data[RTC_MIN] & 0x7f); + /* only 24-hour clock */ + tm->tm_hour = bcd2bin(data[RTC_HOUR] & 0x3f); + tm->tm_wday = ffs(data[RTC_WDAY] & 0x7f); + tm->tm_mday = bcd2bin(data[RTC_MDAY] & 0x3f); + tm->tm_mon = bcd2bin(data[RTC_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(data[RTC_YEAR]) + 100; + + pr_debug("%s: date %ds %dm %dh %dmd %dm %dy\n", __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year); + + /* + * The year in the RTC is a value between 0 and 99. + * Assume that this represents the current century + * and disregard all other values. + */ + if (tm->tm_year < 100 || tm->tm_year >= 200) + return -EINVAL; + + return 0; +} + +/** + * rx6110_set_time - set the current time in the rx6110 registers + * + * @dev: the rtc device in use + * @tm: holds date and time + * + * 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 + * + * Note: If STOP is not set/cleared, the clock will start when the seconds + * register is written + * + */ +static int rx6110_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rx6110_data *rx6110 = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int ret; + + ret = rx6110_rtc_tm_to_data(tm, data); + if (ret < 0) + return ret; + + /* set STOP bit before changing clock/calendar */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_CTRL, + RX6110_BIT_CTRL_STOP, RX6110_BIT_CTRL_STOP); + if (ret) + return ret; + + ret = regmap_bulk_write(rx6110->regmap, RX6110_REG_SEC, data, + RTC_NR_TIME); + if (ret) + return ret; + + /* The time in the RTC is valid. Be sure to have VLF cleared. */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_FLAG, + RX6110_BIT_FLAG_VLF, 0); + if (ret) + return ret; + + /* clear STOP bit after changing clock/calendar */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_CTRL, + RX6110_BIT_CTRL_STOP, 0); + + return ret; +} + +/** + * rx6110_get_time - get the current time from the rx6110 registers + * @dev: the rtc device in use + * @tm: holds date and time + */ +static int rx6110_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rx6110_data *rx6110 = dev_get_drvdata(dev); + u8 data[RTC_NR_TIME]; + int flags; + int ret; + + ret = regmap_read(rx6110->regmap, RX6110_REG_FLAG, &flags); + if (ret) + return -EINVAL; + + /* check for VLF Flag (set at power-on) */ + if ((flags & RX6110_BIT_FLAG_VLF)) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + /* read registers to date */ + ret = regmap_bulk_read(rx6110->regmap, RX6110_REG_SEC, data, + RTC_NR_TIME); + if (ret) + return ret; + + ret = rx6110_data_to_rtc_tm(data, tm); + if (ret) + return ret; + + dev_dbg(dev, "%s: date %ds %dm %dh %dmd %dm %dy\n", __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year); + + return 0; +} + +static const struct reg_sequence rx6110_default_regs[] = { + { RX6110_REG_RES1, 0xB8 }, + { RX6110_REG_RES2, 0x00 }, + { RX6110_REG_RES3, 0x10 }, + { RX6110_REG_IRQ, 0x00 }, + { RX6110_REG_ALMIN, 0x00 }, + { RX6110_REG_ALHOUR, 0x00 }, + { RX6110_REG_ALWDAY, 0x00 }, +}; + +/** + * rx6110_init - initialize the rx6110 registers + * + * @rx6110: pointer to the rx6110 struct in use + * + */ +static int rx6110_init(struct rx6110_data *rx6110) +{ + struct rtc_device *rtc = rx6110->rtc; + int flags; + int ret; + + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_EXT, + RX6110_BIT_EXT_TE, 0); + if (ret) + return ret; + + ret = regmap_register_patch(rx6110->regmap, rx6110_default_regs, + ARRAY_SIZE(rx6110_default_regs)); + if (ret) + return ret; + + ret = regmap_read(rx6110->regmap, RX6110_REG_FLAG, &flags); + if (ret) + return ret; + + /* check for VLF Flag (set at power-on) */ + if ((flags & RX6110_BIT_FLAG_VLF)) + dev_warn(&rtc->dev, "Voltage low, data loss detected.\n"); + + /* check for Alarm Flag */ + if (flags & RX6110_BIT_FLAG_AF) + dev_warn(&rtc->dev, "An alarm may have been missed.\n"); + + /* check for Periodic Timer Flag */ + if (flags & RX6110_BIT_FLAG_TF) + dev_warn(&rtc->dev, "Periodic timer was detected\n"); + + /* check for Update Timer Flag */ + if (flags & RX6110_BIT_FLAG_UF) + dev_warn(&rtc->dev, "Update timer was detected\n"); + + /* clear all flags BUT VLF */ + ret = regmap_update_bits(rx6110->regmap, RX6110_REG_FLAG, + RX6110_BIT_FLAG_AF | + RX6110_BIT_FLAG_UF | + RX6110_BIT_FLAG_TF, + 0); + + return ret; +} + +static const struct rtc_class_ops rx6110_rtc_ops = { + .read_time = rx6110_get_time, + .set_time = rx6110_set_time, +}; + +static struct regmap_config regmap_spi_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RX6110_REG_IRQ, + .read_flag_mask = 0x80, +}; + +/** + * rx6110_probe - initialize rtc driver + * @spi: pointer to spi device + */ +static int rx6110_probe(struct spi_device *spi) +{ + struct rx6110_data *rx6110; + int err; + + if ((spi->bits_per_word && spi->bits_per_word != 8) || + (spi->max_speed_hz > 2000000) || + (spi->mode != (SPI_CS_HIGH | SPI_CPOL | SPI_CPHA))) { + dev_warn(&spi->dev, "SPI settings: bits_per_word: %d, max_speed_hz: %d, mode: %xh\n", + spi->bits_per_word, spi->max_speed_hz, spi->mode); + dev_warn(&spi->dev, "driving device in an unsupported mode"); + } + + rx6110 = devm_kzalloc(&spi->dev, sizeof(*rx6110), GFP_KERNEL); + if (!rx6110) + return -ENOMEM; + + rx6110->regmap = devm_regmap_init_spi(spi, ®map_spi_config); + if (IS_ERR(rx6110->regmap)) { + dev_err(&spi->dev, "regmap init failed for rtc rx6110\n"); + return PTR_ERR(rx6110->regmap); + } + + spi_set_drvdata(spi, rx6110); + + rx6110->rtc = devm_rtc_device_register(&spi->dev, + RX6110_DRIVER_NAME, + &rx6110_rtc_ops, THIS_MODULE); + + if (IS_ERR(rx6110->rtc)) + return PTR_ERR(rx6110->rtc); + + err = rx6110_init(rx6110); + if (err) + return err; + + rx6110->rtc->max_user_freq = 1; + + return 0; +} + +static int rx6110_remove(struct spi_device *spi) +{ + return 0; +} + +static const struct spi_device_id rx6110_id[] = { + { "rx6110", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rx6110_id); + +static struct spi_driver rx6110_driver = { + .driver = { + .name = RX6110_DRIVER_NAME, + }, + .probe = rx6110_probe, + .remove = rx6110_remove, + .id_table = rx6110_id, +}; + +module_spi_driver(rx6110_driver); + +MODULE_AUTHOR("Val Krutov <val.krutov@erd.epson.com>"); +MODULE_DESCRIPTION("RX-6110 SA RTC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c new file mode 100644 index 000000000..f4db80f9c --- /dev/null +++ b/drivers/rtc/rtc-rx8010.c @@ -0,0 +1,515 @@ +/* + * Driver for the Epson RTC module RX-8010 SJ + * + * Copyright(C) Timesys Corporation 2015 + * Copyright(C) General Electric Company 2015 + * + * 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/bitops.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtc.h> + +#define RX8010_SEC 0x10 +#define RX8010_MIN 0x11 +#define RX8010_HOUR 0x12 +#define RX8010_WDAY 0x13 +#define RX8010_MDAY 0x14 +#define RX8010_MONTH 0x15 +#define RX8010_YEAR 0x16 +#define RX8010_RESV17 0x17 +#define RX8010_ALMIN 0x18 +#define RX8010_ALHOUR 0x19 +#define RX8010_ALWDAY 0x1A +#define RX8010_TCOUNT0 0x1B +#define RX8010_TCOUNT1 0x1C +#define RX8010_EXT 0x1D +#define RX8010_FLAG 0x1E +#define RX8010_CTRL 0x1F +/* 0x20 to 0x2F are user registers */ +#define RX8010_RESV30 0x30 +#define RX8010_RESV31 0x31 +#define RX8010_IRQ 0x32 + +#define RX8010_EXT_WADA BIT(3) + +#define RX8010_FLAG_VLF BIT(1) +#define RX8010_FLAG_AF BIT(3) +#define RX8010_FLAG_TF BIT(4) +#define RX8010_FLAG_UF BIT(5) + +#define RX8010_CTRL_AIE BIT(3) +#define RX8010_CTRL_UIE BIT(5) +#define RX8010_CTRL_STOP BIT(6) +#define RX8010_CTRL_TEST BIT(7) + +#define RX8010_ALARM_AE BIT(7) + +static const struct i2c_device_id rx8010_id[] = { + { "rx8010", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8010_id); + +static const struct of_device_id rx8010_of_match[] = { + { .compatible = "epson,rx8010" }, + { } +}; +MODULE_DEVICE_TABLE(of, rx8010_of_match); + +struct rx8010_data { + struct i2c_client *client; + struct rtc_device *rtc; + u8 ctrlreg; +}; + +static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rx8010_data *rx8010 = i2c_get_clientdata(client); + int flagreg; + + mutex_lock(&rx8010->rtc->ops_lock); + + flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG); + + if (flagreg <= 0) { + mutex_unlock(&rx8010->rtc->ops_lock); + return IRQ_NONE; + } + + if (flagreg & RX8010_FLAG_VLF) + dev_warn(&client->dev, "Frequency stop detected\n"); + + if (flagreg & RX8010_FLAG_TF) { + flagreg &= ~RX8010_FLAG_TF; + rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF); + } + + if (flagreg & RX8010_FLAG_AF) { + flagreg &= ~RX8010_FLAG_AF; + rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF); + } + + if (flagreg & RX8010_FLAG_UF) { + flagreg &= ~RX8010_FLAG_UF; + rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF); + } + + i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg); + + mutex_unlock(&rx8010->rtc->ops_lock); + return IRQ_HANDLED; +} + +static int rx8010_get_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 date[7]; + int flagreg; + int err; + + flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG); + if (flagreg < 0) + return flagreg; + + if (flagreg & RX8010_FLAG_VLF) { + dev_warn(dev, "Frequency stop detected\n"); + return -EINVAL; + } + + err = i2c_smbus_read_i2c_block_data(rx8010->client, RX8010_SEC, + 7, date); + if (err != 7) + return err < 0 ? err : -EIO; + + dt->tm_sec = bcd2bin(date[RX8010_SEC - RX8010_SEC] & 0x7f); + dt->tm_min = bcd2bin(date[RX8010_MIN - RX8010_SEC] & 0x7f); + dt->tm_hour = bcd2bin(date[RX8010_HOUR - RX8010_SEC] & 0x3f); + dt->tm_mday = bcd2bin(date[RX8010_MDAY - RX8010_SEC] & 0x3f); + dt->tm_mon = bcd2bin(date[RX8010_MONTH - RX8010_SEC] & 0x1f) - 1; + dt->tm_year = bcd2bin(date[RX8010_YEAR - RX8010_SEC]) + 100; + dt->tm_wday = ffs(date[RX8010_WDAY - RX8010_SEC] & 0x7f); + + return 0; +} + +static int rx8010_set_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 date[7]; + int ctrl, flagreg; + int ret; + + if ((dt->tm_year < 100) || (dt->tm_year > 199)) + return -EINVAL; + + /* set STOP bit before changing clock/calendar */ + ctrl = i2c_smbus_read_byte_data(rx8010->client, RX8010_CTRL); + if (ctrl < 0) + return ctrl; + rx8010->ctrlreg = ctrl | RX8010_CTRL_STOP; + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL, + rx8010->ctrlreg); + if (ret < 0) + return ret; + + date[RX8010_SEC - RX8010_SEC] = bin2bcd(dt->tm_sec); + date[RX8010_MIN - RX8010_SEC] = bin2bcd(dt->tm_min); + date[RX8010_HOUR - RX8010_SEC] = bin2bcd(dt->tm_hour); + date[RX8010_MDAY - RX8010_SEC] = bin2bcd(dt->tm_mday); + date[RX8010_MONTH - RX8010_SEC] = bin2bcd(dt->tm_mon + 1); + date[RX8010_YEAR - RX8010_SEC] = bin2bcd(dt->tm_year - 100); + date[RX8010_WDAY - RX8010_SEC] = bin2bcd(1 << dt->tm_wday); + + ret = i2c_smbus_write_i2c_block_data(rx8010->client, + RX8010_SEC, 7, date); + if (ret < 0) + return ret; + + /* clear STOP bit after changing clock/calendar */ + ctrl = i2c_smbus_read_byte_data(rx8010->client, RX8010_CTRL); + if (ctrl < 0) + return ctrl; + rx8010->ctrlreg = ctrl & ~RX8010_CTRL_STOP; + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL, + rx8010->ctrlreg); + if (ret < 0) + return ret; + + flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG); + if (flagreg < 0) { + return flagreg; + } + + if (flagreg & RX8010_FLAG_VLF) + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, + flagreg & ~RX8010_FLAG_VLF); + + return 0; +} + +static int rx8010_init_client(struct i2c_client *client) +{ + struct rx8010_data *rx8010 = i2c_get_clientdata(client); + u8 ctrl[2]; + int need_clear = 0, err = 0; + + /* Initialize reserved registers as specified in datasheet */ + err = i2c_smbus_write_byte_data(client, RX8010_RESV17, 0xD8); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(client, RX8010_RESV30, 0x00); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(client, RX8010_RESV31, 0x08); + if (err < 0) + return err; + + err = i2c_smbus_write_byte_data(client, RX8010_IRQ, 0x00); + if (err < 0) + return err; + + err = i2c_smbus_read_i2c_block_data(rx8010->client, RX8010_FLAG, + 2, ctrl); + if (err != 2) + return err < 0 ? err : -EIO; + + if (ctrl[0] & RX8010_FLAG_VLF) + dev_warn(&client->dev, "Frequency stop was detected\n"); + + if (ctrl[0] & RX8010_FLAG_AF) { + dev_warn(&client->dev, "Alarm was detected\n"); + need_clear = 1; + } + + if (ctrl[0] & RX8010_FLAG_TF) + need_clear = 1; + + if (ctrl[0] & RX8010_FLAG_UF) + need_clear = 1; + + if (need_clear) { + ctrl[0] &= ~(RX8010_FLAG_AF | RX8010_FLAG_TF | RX8010_FLAG_UF); + err = i2c_smbus_write_byte_data(client, RX8010_FLAG, ctrl[0]); + if (err < 0) + return err; + } + + rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST); + + return 0; +} + +static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + struct i2c_client *client = rx8010->client; + u8 alarmvals[3]; + int flagreg; + int err; + + err = i2c_smbus_read_i2c_block_data(client, RX8010_ALMIN, 3, alarmvals); + if (err != 3) + return err < 0 ? err : -EIO; + + flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG); + if (flagreg < 0) + return flagreg; + + t->time.tm_sec = 0; + t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + + if (!(alarmvals[2] & RX8010_ALARM_AE)) + t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f); + + t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE); + t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled; + + return 0; +} + +static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + u8 alarmvals[3]; + int extreg, flagreg; + int err; + + flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG); + if (flagreg < 0) { + return flagreg; + } + + if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) { + rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE); + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL, + rx8010->ctrlreg); + if (err < 0) { + return err; + } + } + + flagreg &= ~RX8010_FLAG_AF; + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg); + if (err < 0) + return err; + + alarmvals[0] = bin2bcd(t->time.tm_min); + alarmvals[1] = bin2bcd(t->time.tm_hour); + alarmvals[2] = bin2bcd(t->time.tm_mday); + + err = i2c_smbus_write_i2c_block_data(rx8010->client, RX8010_ALMIN, + 2, alarmvals); + if (err < 0) + return err; + + extreg = i2c_smbus_read_byte_data(client, RX8010_EXT); + if (extreg < 0) + return extreg; + + extreg |= RX8010_EXT_WADA; + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_EXT, extreg); + if (err < 0) + return err; + + if (alarmvals[2] == 0) + alarmvals[2] |= RX8010_ALARM_AE; + + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_ALWDAY, + alarmvals[2]); + if (err < 0) + return err; + + if (t->enabled) { + if (rx8010->rtc->uie_rtctimer.enabled) + rx8010->ctrlreg |= RX8010_CTRL_UIE; + if (rx8010->rtc->aie_timer.enabled) + rx8010->ctrlreg |= + (RX8010_CTRL_AIE | RX8010_CTRL_UIE); + + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL, + rx8010->ctrlreg); + if (err < 0) + return err; + } + + return 0; +} + +static int rx8010_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + int flagreg; + u8 ctrl; + int err; + + ctrl = rx8010->ctrlreg; + + if (enabled) { + if (rx8010->rtc->uie_rtctimer.enabled) + ctrl |= RX8010_CTRL_UIE; + if (rx8010->rtc->aie_timer.enabled) + ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE); + } else { + if (!rx8010->rtc->uie_rtctimer.enabled) + ctrl &= ~RX8010_CTRL_UIE; + if (!rx8010->rtc->aie_timer.enabled) + ctrl &= ~RX8010_CTRL_AIE; + } + + flagreg = i2c_smbus_read_byte_data(client, RX8010_FLAG); + if (flagreg < 0) + return flagreg; + + flagreg &= ~RX8010_FLAG_AF; + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg); + if (err < 0) + return err; + + if (ctrl != rx8010->ctrlreg) { + rx8010->ctrlreg = ctrl; + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL, + rx8010->ctrlreg); + if (err < 0) + return err; + } + + return 0; +} + +static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rx8010_data *rx8010 = dev_get_drvdata(dev); + int ret, tmp; + int flagreg; + + switch (cmd) { + case RTC_VL_READ: + flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG); + if (flagreg < 0) + return flagreg; + + tmp = !!(flagreg & RX8010_FLAG_VLF); + if (copy_to_user((void __user *)arg, &tmp, sizeof(int))) + return -EFAULT; + + return 0; + + case RTC_VL_CLR: + flagreg = i2c_smbus_read_byte_data(rx8010->client, RX8010_FLAG); + if (flagreg < 0) { + return flagreg; + } + + flagreg &= ~RX8010_FLAG_VLF; + ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg); + if (ret < 0) + return ret; + + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static const struct rtc_class_ops rx8010_rtc_ops_default = { + .read_time = rx8010_get_time, + .set_time = rx8010_set_time, + .ioctl = rx8010_ioctl, +}; + +static const struct rtc_class_ops rx8010_rtc_ops_alarm = { + .read_time = rx8010_get_time, + .set_time = rx8010_set_time, + .ioctl = rx8010_ioctl, + .read_alarm = rx8010_read_alarm, + .set_alarm = rx8010_set_alarm, + .alarm_irq_enable = rx8010_alarm_irq_enable, +}; + +static int rx8010_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + const struct rtc_class_ops *rtc_ops; + struct rx8010_data *rx8010; + int err = 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"); + return -EIO; + } + + rx8010 = devm_kzalloc(&client->dev, sizeof(struct rx8010_data), + GFP_KERNEL); + if (!rx8010) + return -ENOMEM; + + rx8010->client = client; + i2c_set_clientdata(client, rx8010); + + err = rx8010_init_client(client); + if (err) + return err; + + if (client->irq > 0) { + dev_info(&client->dev, "IRQ %d supplied\n", client->irq); + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + rx8010_irq_1_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rx8010", client); + + if (err) { + dev_err(&client->dev, "unable to request IRQ\n"); + return err; + } + + rtc_ops = &rx8010_rtc_ops_alarm; + } else { + rtc_ops = &rx8010_rtc_ops_default; + } + + rx8010->rtc = devm_rtc_device_register(&client->dev, client->name, + rtc_ops, THIS_MODULE); + + if (IS_ERR(rx8010->rtc)) { + dev_err(&client->dev, "unable to register the class device\n"); + return PTR_ERR(rx8010->rtc); + } + + rx8010->rtc->max_user_freq = 1; + + return err; +} + +static struct i2c_driver rx8010_driver = { + .driver = { + .name = "rtc-rx8010", + .of_match_table = of_match_ptr(rx8010_of_match), + }, + .probe = rx8010_probe, + .id_table = rx8010_id, +}; + +module_i2c_driver(rx8010_driver); + +MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>"); +MODULE_DESCRIPTION("Epson RX8010SJ RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c new file mode 100644 index 000000000..41127adf5 --- /dev/null +++ b/drivers/rtc/rtc-rx8025.c @@ -0,0 +1,587 @@ +/* + * 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 + * <lm-sensors@lm-sensors.org> + * 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/bcd.h> +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.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 BIT(3) +#define RX8025_BIT_CTRL1_1224 BIT(5) +#define RX8025_BIT_CTRL1_DALE BIT(6) +#define RX8025_BIT_CTRL1_WALE BIT(7) + +#define RX8025_BIT_CTRL2_DAFG BIT(0) +#define RX8025_BIT_CTRL2_WAFG BIT(1) +#define RX8025_BIT_CTRL2_CTFG BIT(2) +#define RX8025_BIT_CTRL2_PON BIT(4) +#define RX8025_BIT_CTRL2_XST BIT(5) +#define RX8025_BIT_CTRL2_VDET BIT(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; + u8 ctrl1; +}; + +static s32 rx8025_read_reg(const struct i2c_client *client, u8 number) +{ + return i2c_smbus_read_byte_data(client, number << 4); +} + +static int rx8025_read_regs(const struct i2c_client *client, + u8 number, u8 length, u8 *values) +{ + int ret = i2c_smbus_read_i2c_block_data(client, number << 4, length, + values); + if (ret != length) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static s32 rx8025_write_reg(const struct i2c_client *client, u8 number, + u8 value) +{ + return i2c_smbus_write_byte_data(client, number << 4, value); +} + +static s32 rx8025_write_regs(const struct i2c_client *client, + u8 number, u8 length, const u8 *values) +{ + return i2c_smbus_write_i2c_block_data(client, number << 4, + length, values); +} + +static int rx8025_check_validity(struct device *dev) +{ + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + int ctrl2; + + ctrl2 = rx8025_read_reg(rx8025->client, RX8025_REG_CTRL2); + if (ctrl2 < 0) + return ctrl2; + + if (ctrl2 & RX8025_BIT_CTRL2_VDET) + dev_warn(dev, "power voltage drop detected\n"); + + if (ctrl2 & RX8025_BIT_CTRL2_PON) { + dev_warn(dev, "power-on reset detected, date is invalid\n"); + return -EINVAL; + } + + if (!(ctrl2 & RX8025_BIT_CTRL2_XST)) { + dev_warn(dev, "crystal stopped, date is invalid\n"); + return -EINVAL; + } + + return 0; +} + +static int rx8025_reset_validity(struct i2c_client *client) +{ + int ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); + + if (ctrl2 < 0) + return ctrl2; + + ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET); + + return rx8025_write_reg(client, RX8025_REG_CTRL2, + ctrl2 | RX8025_BIT_CTRL2_XST); +} + +static irqreturn_t rx8025_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rx8025_data *rx8025 = i2c_get_clientdata(client); + struct mutex *lock = &rx8025->rtc->ops_lock; + int status; + + mutex_lock(lock); + status = rx8025_read_reg(client, RX8025_REG_CTRL2); + if (status < 0) + 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; + rtc_update_irq(rx8025->rtc, 1, RTC_PF | RTC_IRQF); + } + + 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; + rtc_update_irq(rx8025->rtc, 1, RTC_AF | RTC_IRQF); + } + +out: + mutex_unlock(lock); + + return IRQ_HANDLED; +} + +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_check_validity(dev); + if (err) + return 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]) + 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 0; +} + +static int rx8025_set_time(struct device *dev, struct rtc_time *dt) +{ + struct rx8025_data *rx8025 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + if ((dt->tm_year < 100) || (dt->tm_year > 199)) + return -EINVAL; + + /* + * 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]); + + ret = rx8025_write_regs(rx8025->client, RX8025_REG_SEC, 7, date); + if (ret < 0) + return ret; + + return rx8025_reset_validity(rx8025->client); +} + +static int rx8025_init_client(struct i2c_client *client) +{ + 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_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_clear) { + ctrl2 = ctrl[1]; + ctrl2 &= ~(RX8025_BIT_CTRL2_CTFG | RX8025_BIT_CTRL2_WAFG | + RX8025_BIT_CTRL2_DAFG); + + 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 ald[2]; + int ctrl2, err; + + if (client->irq <= 0) + return -EINVAL; + + err = rx8025_read_regs(client, RX8025_REG_ALDMIN, 2, ald); + if (err) + return err; + + ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); + if (ctrl2 < 0) + return ctrl2; + + 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); + + 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! + * round up to nearest minute + */ + if (t->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&t->time); + + alarm_time += 60 - t->time.tm_sec; + rtc_time64_to_tm(alarm_time, &t->time); + } + + 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 const 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); + int digoff; + + digoff = rx8025_read_reg(client, RX8025_REG_DIGOFF); + if (digoff < 0) + return digoff; + + *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 = 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"); + return -EIO; + } + + rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL); + if (!rx8025) + return -ENOMEM; + + rx8025->client = client; + i2c_set_clientdata(client, rx8025); + + err = rx8025_init_client(client); + if (err) + return err; + + rx8025->rtc = devm_rtc_device_register(&client->dev, client->name, + &rx8025_rtc_ops, THIS_MODULE); + if (IS_ERR(rx8025->rtc)) { + dev_err(&client->dev, "unable to register the class device\n"); + return PTR_ERR(rx8025->rtc); + } + + if (client->irq > 0) { + dev_info(&client->dev, "IRQ %d supplied\n", client->irq); + err = devm_request_threaded_irq(&client->dev, client->irq, NULL, + rx8025_handle_irq, + IRQF_ONESHOT, + "rx8025", client); + if (err) { + dev_err(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } + } + + rx8025->rtc->max_user_freq = 1; + + /* the rx8025 alarm only supports a minute accuracy */ + rx8025->rtc->uie_unsupported = 1; + + err = rx8025_sysfs_register(&client->dev); + return err; +} + +static int rx8025_remove(struct i2c_client *client) +{ + rx8025_sysfs_unregister(&client->dev); + return 0; +} + +static struct i2c_driver rx8025_driver = { + .driver = { + .name = "rtc-rx8025", + }, + .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..eac882169 --- /dev/null +++ b/drivers/rtc/rtc-rx8581.c @@ -0,0 +1,244 @@ +/* + * 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/regmap.h> +#include <linux/rtc.h> +#include <linux/log2.h> + +#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 regmap *regmap; + struct rtc_device *rtc; +}; + +/* + * 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_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned char date[7]; + unsigned int data; + int 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. + */ + err = regmap_read(rx8581->regmap, RX8581_REG_FLAG, &data); + if (err < 0) + return err; + + if (data & RX8581_FLAG_VLF) { + dev_warn(dev, + "low voltage detected, date/time is not reliable.\n"); + return -EINVAL; + } + + do { + /* If update flag set, clear it */ + if (data & RX8581_FLAG_UF) { + err = regmap_write(rx8581->regmap, RX8581_REG_FLAG, + data & ~RX8581_FLAG_UF); + if (err < 0) + return err; + } + + /* Now read time and date */ + err = regmap_bulk_read(rx8581->regmap, RX8581_REG_SC, date, + sizeof(date)); + if (err < 0) + return err; + + /* Check flag register */ + err = regmap_read(rx8581->regmap, RX8581_REG_FLAG, &data); + if (err < 0) + return err; + } while (data & RX8581_FLAG_UF); + + 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[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]) + 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, tm->tm_year, tm->tm_wday); + + return 0; +} + +static int rx8581_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + int err; + unsigned char buf[7]; + struct rx8581 *rx8581 = i2c_get_clientdata(client); + + 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); + + /* 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 */ + err = regmap_update_bits(rx8581->regmap, RX8581_REG_CTRL, + RX8581_CTRL_STOP, RX8581_CTRL_STOP); + if (err < 0) + return err; + + /* write register's data */ + err = regmap_bulk_write(rx8581->regmap, RX8581_REG_SC, + buf, sizeof(buf)); + if (err < 0) + return err; + + /* get VLF and clear it */ + err = regmap_update_bits(rx8581->regmap, RX8581_REG_FLAG, + RX8581_FLAG_VLF, 0); + if (err < 0) + return err; + + /* Restart the clock */ + return regmap_update_bits(rx8581->regmap, RX8581_REG_CTRL, + RX8581_CTRL_STOP, 0); +} + +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; + static const struct regmap_config config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xf, + }; + + dev_dbg(&client->dev, "%s\n", __func__); + + rx8581 = devm_kzalloc(&client->dev, sizeof(struct rx8581), GFP_KERNEL); + if (!rx8581) + return -ENOMEM; + + i2c_set_clientdata(client, rx8581); + + rx8581->regmap = devm_regmap_init_i2c(client, &config); + if (IS_ERR(rx8581->regmap)) + return PTR_ERR(rx8581->regmap); + + rx8581->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(rx8581->rtc)) + return PTR_ERR(rx8581->rtc); + + rx8581->rtc->ops = &rx8581_rtc_ops; + rx8581->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + rx8581->rtc->range_max = RTC_TIMESTAMP_END_2099; + rx8581->rtc->start_secs = 0; + rx8581->rtc->set_start_time = true; + + return rtc_register_device(rx8581->rtc); +} + +static const struct i2c_device_id rx8581_id[] = { + { "rx8581", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8581_id); + +static const struct of_device_id rx8581_of_match[] = { + { .compatible = "epson,rx8581" }, + { } +}; +MODULE_DEVICE_TABLE(of, rx8581_of_match); + +static struct i2c_driver rx8581_driver = { + .driver = { + .name = "rtc-rx8581", + .of_match_table = of_match_ptr(rx8581_of_match), + }, + .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"); diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c new file mode 100644 index 000000000..3c64dbb08 --- /dev/null +++ b/drivers/rtc/rtc-s35390a.c @@ -0,0 +1,545 @@ +/* + * 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> +#include <linux/delay.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 + +/* flags for STATUS1 */ +#define S35390A_FLAG_POC 0x01 +#define S35390A_FLAG_BLD 0x02 +#define S35390A_FLAG_INT2 0x04 +#define S35390A_FLAG_24H 0x40 +#define S35390A_FLAG_RESET 0x80 + +/* flag for STATUS2 */ +#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); + +static const struct of_device_id s35390a_of_match[] = { + { .compatible = "s35390a" }, + { .compatible = "sii,s35390a" }, + { } +}; +MODULE_DEVICE_TABLE(of, s35390a_of_match); + +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_init(struct s35390a *s35390a) +{ + u8 buf; + int ret; + unsigned initcount = 0; + + /* + * At least one of POC and BLD are set, so reinitialise chip. Keeping + * this information in the hardware to know later that the time isn't + * valid is unfortunately not possible because POC and BLD are cleared + * on read. So the reset is best done now. + * + * The 24H bit is kept over reset, so set it already here. + */ +initialize: + buf = S35390A_FLAG_RESET | S35390A_FLAG_24H; + ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); + + if (ret < 0) + return ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1); + if (ret < 0) + return ret; + + if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) { + /* Try up to five times to reset the chip */ + if (initcount < 5) { + ++initcount; + goto initialize; + } else + return -EIO; + } + + return 1; +} + +/* + * Returns <0 on error, 0 if rtc is setup fine and 1 if the chip was reset. + * To keep the information if an irq is pending, pass the value read from + * STATUS1 to the caller. + */ +static int s35390a_read_status(struct s35390a *s35390a, char *status1) +{ + int ret; + + ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, status1, 1); + if (ret < 0) + return ret; + + if (*status1 & S35390A_FLAG_POC) { + /* + * Do not communicate for 0.5 seconds since the power-on + * detection circuit is in operation. + */ + msleep(500); + return 1; + } else if (*status1 & S35390A_FLAG_BLD) + return 1; + /* + * If both POC and BLD are unset everything is fine. + */ + return 0; +} + +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_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + int i, err; + char buf[7], status; + + 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); + + if (s35390a_read_status(s35390a, &status) == 1) + s35390a_init(s35390a); + + 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_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + char buf[7], status; + int i, err; + + if (s35390a_read_status(s35390a, &status) == 1) + return -EINVAL; + + 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 0; +} + +static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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 (which deasserts the irq line) */ + err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); + if (err < 0) + return err; + + /* clear pending interrupt (in STATUS1 only), 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; + else + buf[S35390A_ALRM_BYTE_WDAY] = 0; + + 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_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ + struct i2c_client *client = to_i2c_client(dev); + 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_MASK) != S35390A_INT2_MODE_ALARM) { + /* + * When the alarm isn't enabled, the register to configure + * the alarm time isn't accessible. + */ + alm->enabled = 0; + return 0; + } else { + alm->enabled = 1; + } + + 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]); + + /* + * B0 of the three matching registers is an enable flag. Iff it is set + * the configured value is used for matching. + */ + if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80) + alm->time.tm_wday = + bcd2bin(buf[S35390A_ALRM_BYTE_WDAY] & ~0x80); + + if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80) + alm->time.tm_hour = + s35390a_reg2hr(s35390a, + buf[S35390A_ALRM_BYTE_HOURS] & ~0x80); + + if (buf[S35390A_ALRM_BYTE_MINS] & 0x80) + alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80); + + /* alarm triggers always at s=0 */ + alm->time.tm_sec = 0; + + 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_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct s35390a *s35390a = i2c_get_clientdata(client); + char sts; + int err; + + switch (cmd) { + case RTC_VL_READ: + /* s35390a_reset set lowvoltage flag and init RTC if needed */ + err = s35390a_read_status(s35390a, &sts); + if (err < 0) + return err; + if (copy_to_user((void __user *)arg, &err, sizeof(int))) + return -EFAULT; + break; + case RTC_VL_CLR: + /* update flag and clear register */ + err = s35390a_init(s35390a); + if (err < 0) + return err; + break; + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +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, + .ioctl = s35390a_rtc_ioctl, +}; + +static struct i2c_driver s35390a_driver; + +static int s35390a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int err, err_read; + unsigned int i; + struct s35390a *s35390a; + char buf, status1; + + 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_read = s35390a_read_status(s35390a, &status1); + if (err_read < 0) { + err = err_read; + dev_err(&client->dev, "error resetting chip\n"); + goto exit_dummy; + } + + if (status1 & S35390A_FLAG_24H) + s35390a->twentyfourhour = 1; + else + s35390a->twentyfourhour = 0; + + if (status1 & S35390A_FLAG_INT2) { + /* disable alarm (and maybe test mode) */ + buf = 0; + err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1); + if (err < 0) { + dev_err(&client->dev, "error disabling alarm"); + goto exit_dummy; + } + } else { + err = s35390a_disable_test_mode(s35390a); + if (err < 0) { + dev_err(&client->dev, "error disabling test mode\n"); + goto exit_dummy; + } + } + + 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; + } + + if (status1 & S35390A_FLAG_INT2) + rtc_update_irq(s35390a->rtc, 1, RTC_AF); + + 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", + .of_match_table = of_match_ptr(s35390a_of_match), + }, + .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..58e03ac35 --- /dev/null +++ b/drivers/rtc/rtc-s3c.c @@ -0,0 +1,876 @@ +/* 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; + bool clk_disabled; + + const struct s3c_rtc_data *data; + + int irq_alarm; + int irq_tick; + + spinlock_t pie_lock; + spinlock_t alarm_clk_lock; + + int ticnt_save; + int 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 int s3c_rtc_enable_clk(struct s3c_rtc *info) +{ + unsigned long irq_flags; + int ret = 0; + + spin_lock_irqsave(&info->alarm_clk_lock, irq_flags); + + if (info->clk_disabled) { + ret = clk_enable(info->rtc_clk); + if (ret) + goto out; + + if (info->data->needs_src_clk) { + ret = clk_enable(info->rtc_src_clk); + if (ret) { + clk_disable(info->rtc_clk); + goto out; + } + } + info->clk_disabled = false; + } + +out: + spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags); + + return ret; +} + +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->clk_disabled) { + if (info->data->needs_src_clk) + clk_disable(info->rtc_src_clk); + clk_disable(info->rtc_clk); + info->clk_disabled = true; + } + 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; + int ret; + + dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled); + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + 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); + + if (enabled) { + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + } else { + s3c_rtc_disable_clk(info); + } + + return 0; +} + +/* Set RTC frequency */ +static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) +{ + int ret; + + if (!is_power_of_2(freq)) + return -EINVAL; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + spin_lock_irq(&info->pie_lock); + + if (info->data->set_freq) + info->data->set_freq(info, freq); + + spin_unlock_irq(&info->pie_lock); + s3c_rtc_disable_clk(info); + + 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; + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + +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 0; +} + +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; + int ret; + + 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; + } + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + 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; + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + 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); + + if (alm_en & S3C2410_RTCALM_MINEN) + alm_tm->tm_min = bcd2bin(alm_tm->tm_min); + + if (alm_en & S3C2410_RTCALM_HOUREN) + alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); + + if (alm_en & S3C2410_RTCALM_DAYEN) + alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); + + if (alm_en & S3C2410_RTCALM_MONEN) { + alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); + alm_tm->tm_mon -= 1; + } + + if (alm_en & S3C2410_RTCALM_YEAREN) + alm_tm->tm_year = bcd2bin(alm_tm->tm_year); + + 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; + int ret; + + 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); + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + 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); + } + + if (tm->tm_mon < 12 && tm->tm_mon >= 0) { + alrm_en |= S3C2410_RTCALM_MONEN; + writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON); + } + + if (tm->tm_mday <= 31 && tm->tm_mday >= 1) { + alrm_en |= S3C2410_RTCALM_DAYEN; + writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE); + } + + 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); + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + 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); + + if (info->data->needs_src_clk) + clk_unprepare(info->rtc_src_clk); + clk_unprepare(info->rtc_clk); + + return 0; +} + +static const struct of_device_id s3c_rtc_dt_match[]; + +static const 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 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)) { + ret = PTR_ERR(info->rtc_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to find rtc clock\n"); + else + dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n"); + return ret; + } + ret = clk_prepare_enable(info->rtc_clk); + if (ret) + return ret; + + if (info->data->needs_src_clk) { + info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src"); + if (IS_ERR(info->rtc_src_clk)) { + ret = PTR_ERR(info->rtc_src_clk); + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "failed to find rtc source clock\n"); + else + dev_dbg(&pdev->dev, + "probe deferred due to missing rtc src clk\n"); + goto err_src_clk; + } + ret = clk_prepare_enable(info->rtc_src_clk); + if (ret) + goto err_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); + + 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); +err_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); + int ret; + + ret = s3c_rtc_enable_clk(info); + if (ret) + return ret; + + /* 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 const struct of_device_id s3c_rtc_dt_match[] = { + { + .compatible = "samsung,s3c2410-rtc", + .data = &s3c2410_rtc_data, + }, { + .compatible = "samsung,s3c2416-rtc", + .data = &s3c2416_rtc_data, + }, { + .compatible = "samsung,s3c2443-rtc", + .data = &s3c2443_rtc_data, + }, { + .compatible = "samsung,s3c6410-rtc", + .data = &s3c6410_rtc_data, + }, { + .compatible = "samsung,exynos3250-rtc", + .data = &s3c6410_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..6495f84f7 --- /dev/null +++ b/drivers/rtc/rtc-s5m.c @@ -0,0 +1,902 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (c) 2013-2014 Samsung Electronics Co., Ltd +// http://www.samsung.com +// +// Copyright (C) 2013 Google, Inc + +#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 + +enum { + RTC_SEC = 0, + RTC_MIN, + RTC_HOUR, + RTC_WEEKDAY, + RTC_DATE, + RTC_MONTH, + RTC_YEAR1, + RTC_YEAR2, + /* Make sure this is always the last enum name. */ + RTC_MAX_NUM_TIME_REGS +}; + +/* + * Registers used by the driver which are different between chipsets. + * + * Operations like read time and write alarm/time require updating + * specific fields in UDR register. These fields usually are auto-cleared + * (with some exceptions). + * + * Table of operations per device: + * + * Device | Write time | Read time | Write alarm + * ================================================= + * S5M8767 | UDR + TIME | | UDR + * S2MPS11/14 | WUDR | RUDR | WUDR + RUDR + * S2MPS13 | WUDR | RUDR | WUDR + AUDR + * S2MPS15 | WUDR | RUDR | AUDR + */ +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 udr_update; + /* Auto-cleared mask in UDR field for writing time and alarm */ + unsigned int autoclear_udr_mask; + /* + * Masks in UDR field for time and alarm operations. + * The read time mask can be 0. Rest should not. + */ + unsigned int read_time_udr_mask; + unsigned int write_time_udr_mask; + unsigned int write_alarm_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, + .udr_update = S5M_RTC_UDR_CON, + .autoclear_udr_mask = S5M_RTC_UDR_MASK, + .read_time_udr_mask = 0, /* Not needed */ + .write_time_udr_mask = S5M_RTC_UDR_MASK | S5M_RTC_TIME_EN_MASK, + .write_alarm_udr_mask = S5M_RTC_UDR_MASK, +}; + +/* Register map for S2MPS13 */ +static const struct s5m_rtc_reg_config s2mps13_rtc_regs = { + .regs_count = 7, + .time = S2MPS_RTC_SEC, + .ctrl = S2MPS_RTC_CTRL, + .alarm0 = S2MPS_ALARM0_SEC, + .alarm1 = S2MPS_ALARM1_SEC, + .udr_update = S2MPS_RTC_UDR_CON, + .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS_RTC_WUDR_MASK | S2MPS13_RTC_AUDR_MASK, +}; + +/* Register map for S2MPS11/14 */ +static const struct s5m_rtc_reg_config s2mps14_rtc_regs = { + .regs_count = 7, + .time = S2MPS_RTC_SEC, + .ctrl = S2MPS_RTC_CTRL, + .alarm0 = S2MPS_ALARM0_SEC, + .alarm1 = S2MPS_ALARM1_SEC, + .udr_update = S2MPS_RTC_UDR_CON, + .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS_RTC_WUDR_MASK | S2MPS_RTC_RUDR_MASK, +}; + +/* + * Register map for S2MPS15 - in comparison to S2MPS14 the WUDR and AUDR bits + * are swapped. + */ +static const struct s5m_rtc_reg_config s2mps15_rtc_regs = { + .regs_count = 7, + .time = S2MPS_RTC_SEC, + .ctrl = S2MPS_RTC_CTRL, + .alarm0 = S2MPS_ALARM0_SEC, + .alarm1 = S2MPS_ALARM1_SEC, + .udr_update = S2MPS_RTC_UDR_CON, + .autoclear_udr_mask = S2MPS_RTC_WUDR_MASK, + .read_time_udr_mask = S2MPS_RTC_RUDR_MASK, + .write_time_udr_mask = S2MPS15_RTC_WUDR_MASK, + .write_alarm_udr_mask = S2MPS15_RTC_AUDR_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 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->udr_update, &data); + } while (--retry && (data & info->regs->autoclear_udr_mask) && !ret); + + if (!retry) + dev_err(info->dev, "waiting for UDR update, reached max number of retries\n"); + + return ret; +} + +static 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 S2MPS15X: + 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 int s5m8767_rtc_set_time_reg(struct s5m_rtc_info *info) +{ + int ret; + unsigned int data; + + ret = regmap_read(info->regmap, info->regs->udr_update, &data); + if (ret < 0) { + dev_err(info->dev, "failed to read update reg(%d)\n", ret); + return ret; + } + + data |= info->regs->write_time_udr_mask; + + ret = regmap_write(info->regmap, info->regs->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 int s5m8767_rtc_set_alarm_reg(struct s5m_rtc_info *info) +{ + int ret; + unsigned int data; + + ret = regmap_read(info->regmap, info->regs->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->write_alarm_udr_mask; + switch (info->device_type) { + case S5M8763X: + case S5M8767X: + data &= ~S5M_RTC_TIME_EN_MASK; + break; + case S2MPS15X: + case S2MPS14X: + case S2MPS13X: + /* No exceptions needed */ + break; + default: + return -EINVAL; + } + + ret = regmap_write(info->regmap, info->regs->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->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[RTC_MAX_NUM_TIME_REGS]; + int ret; + + if (info->regs->read_time_udr_mask) { + ret = regmap_update_bits(info->regmap, + info->regs->udr_update, + info->regs->read_time_udr_mask, + info->regs->read_time_udr_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 S2MPS15X: + 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 0; +} + +static int s5m_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct s5m_rtc_info *info = dev_get_drvdata(dev); + u8 data[RTC_MAX_NUM_TIME_REGS]; + int ret = 0; + + switch (info->device_type) { + case S5M8763X: + s5m8763_tm_to_data(tm, data); + break; + case S5M8767X: + case S2MPS15X: + 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[RTC_MAX_NUM_TIME_REGS]; + 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 S2MPS15X: + 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[RTC_MAX_NUM_TIME_REGS]; + 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 S2MPS15X: + 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[RTC_MAX_NUM_TIME_REGS]; + 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 S2MPS15X: + 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[RTC_MAX_NUM_TIME_REGS]; + int ret; + + switch (info->device_type) { + case S5M8763X: + s5m8763_tm_to_data(&alrm->time, data); + break; + + case S5M8767X: + case S2MPS15X: + 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 S2MPS15X: + case S2MPS14X: + case S2MPS13X: + data[0] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT); + ret = regmap_write(info->regmap, info->regs->ctrl, data[0]); + if (ret < 0) + break; + + /* + * Should set WUDR & (RUDR or AUDR) bits to high after writing + * RTC_CTRL register like writing Alarm registers. We can't find + * the description from datasheet but vendor code does that + * really. + */ + ret = s5m8767_rtc_set_alarm_reg(info); + 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 S2MPS15X: + regmap_cfg = &s2mps14_rtc_regmap_config; + info->regs = &s2mps15_rtc_regs; + alarm_irq = S2MPS14_IRQ_RTCA0; + break; + case S2MPS14X: + regmap_cfg = &s2mps14_rtc_regmap_config; + info->regs = &s2mps14_rtc_regs; + alarm_irq = S2MPS14_IRQ_RTCA0; + break; + case S2MPS13X: + regmap_cfg = &s2mps14_rtc_regmap_config; + info->regs = &s2mps13_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 }, + { "s2mps15-rtc", S2MPS15X }, + { }, +}; +MODULE_DEVICE_TABLE(platform, s5m_rtc_id); + +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..56f625371 --- /dev/null +++ b/drivers/rtc/rtc-sa1100.c @@ -0,0 +1,373 @@ +/* + * 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> + +#define RTSR_HZE BIT(3) /* HZ interrupt enable */ +#define RTSR_ALE BIT(2) /* RTC alarm interrupt enable */ +#define RTSR_HZ BIT(1) /* HZ rising-edge detected */ +#define RTSR_AL BIT(0) /* RTC alarm detected */ + +#include "rtc-sa1100.h" + +#define RTC_DEF_DIVIDER (32768 - 1) +#define RTC_DEF_TRIM 0 +#define RTC_FREQ 1024 + + +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 = readl_relaxed(info->rtsr); + /* clear interrupt sources */ + writel_relaxed(0, info->rtsr); + /* 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. */ + writel_relaxed((RTSR_AL | RTSR_HZ) & (rtsr >> 2), info->rtsr); + } 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(). */ + writel_relaxed(RTSR_AL | RTSR_HZ, info->rtsr); + } + + /* clear alarm interrupt if it has occurred */ + if (rtsr & RTSR_AL) + rtsr &= ~RTSR_ALE; + writel_relaxed(rtsr & (RTSR_ALE | RTSR_HZE), info->rtsr); + + /* 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_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + u32 rtsr; + struct sa1100_rtc *info = dev_get_drvdata(dev); + + spin_lock_irq(&info->lock); + rtsr = readl_relaxed(info->rtsr); + if (enabled) + rtsr |= RTSR_ALE; + else + rtsr &= ~RTSR_ALE; + writel_relaxed(rtsr, info->rtsr); + spin_unlock_irq(&info->lock); + return 0; +} + +static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + + rtc_time_to_tm(readl_relaxed(info->rcnr), tm); + return 0; +} + +static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + unsigned long time; + int ret; + + ret = rtc_tm_to_time(tm, &time); + if (ret == 0) + writel_relaxed(time, info->rcnr); + return ret; +} + +static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + u32 rtsr; + struct sa1100_rtc *info = dev_get_drvdata(dev); + + rtsr = readl_relaxed(info->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; + writel_relaxed(readl_relaxed(info->rtsr) & + (RTSR_HZE | RTSR_ALE | RTSR_AL), info->rtsr); + writel_relaxed(time, info->rtar); + if (alrm->enabled) + writel_relaxed(readl_relaxed(info->rtsr) | RTSR_ALE, info->rtsr); + else + writel_relaxed(readl_relaxed(info->rtsr) & ~RTSR_ALE, info->rtsr); +out: + spin_unlock_irq(&info->lock); + + return ret; +} + +static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq) +{ + struct sa1100_rtc *info = dev_get_drvdata(dev); + + seq_printf(seq, "trim/divider\t\t: 0x%08x\n", readl_relaxed(info->rttr)); + seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", readl_relaxed(info->rtsr)); + + return 0; +} + +static const struct rtc_class_ops sa1100_rtc_ops = { + .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, +}; + +int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info) +{ + int ret; + + spin_lock_init(&info->lock); + + 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); + } + + 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 (readl_relaxed(info->rttr) == 0) { + writel_relaxed(RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16), info->rttr); + dev_warn(&pdev->dev, "warning: " + "initializing default clock divider/trim value\n"); + /* The current RTC value probably doesn't make sense either */ + writel_relaxed(0, info->rcnr); + } + + info->rtc->ops = &sa1100_rtc_ops; + info->rtc->max_user_freq = RTC_FREQ; + + ret = rtc_register_device(info->rtc); + if (ret) { + clk_disable_unprepare(info->clk); + return ret; + } + + /* 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. */ + writel_relaxed(RTSR_AL | RTSR_HZ, info->rtsr); + + return 0; +} +EXPORT_SYMBOL_GPL(sa1100_rtc_init); + +static int sa1100_rtc_probe(struct platform_device *pdev) +{ + struct sa1100_rtc *info; + struct resource *iores; + void __iomem *base; + int irq_1hz, irq_alarm; + int ret; + + 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->irq_1hz = irq_1hz; + info->irq_alarm = irq_alarm; + + info->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(info->rtc)) + return PTR_ERR(info->rtc); + + ret = devm_request_irq(&pdev->dev, irq_1hz, sa1100_rtc_interrupt, 0, + "rtc 1Hz", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_1hz); + return ret; + } + ret = devm_request_irq(&pdev->dev, irq_alarm, sa1100_rtc_interrupt, 0, + "rtc Alrm", &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "IRQ %d already in use.\n", irq_alarm); + return ret; + } + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(base)) + return PTR_ERR(base); + + if (IS_ENABLED(CONFIG_ARCH_SA1100) || + of_device_is_compatible(pdev->dev.of_node, "mrvl,sa1100-rtc")) { + info->rcnr = base + 0x04; + info->rtsr = base + 0x10; + info->rtar = base + 0x00; + info->rttr = base + 0x08; + } else { + info->rcnr = base + 0x0; + info->rtsr = base + 0x8; + info->rtar = base + 0x4; + info->rttr = base + 0xc; + } + + platform_set_drvdata(pdev, info); + device_init_wakeup(&pdev->dev, 1); + + return sa1100_rtc_init(pdev, info); +} + +static int sa1100_rtc_remove(struct platform_device *pdev) +{ + struct sa1100_rtc *info = platform_get_drvdata(pdev); + + if (info) { + spin_lock_irq(&info->lock); + writel_relaxed(0, info->rtsr); + spin_unlock_irq(&info->lock); + 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-sa1100.h b/drivers/rtc/rtc-sa1100.h new file mode 100644 index 000000000..cc724f5b0 --- /dev/null +++ b/drivers/rtc/rtc-sa1100.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __RTC_SA1100_H__ +#define __RTC_SA1100_H__ + +#include <linux/kernel.h> + +struct clk; +struct platform_device; + +struct sa1100_rtc { + spinlock_t lock; + void __iomem *rcnr; + void __iomem *rtar; + void __iomem *rtsr; + void __iomem *rttr; + int irq_1hz; + int irq_alarm; + struct rtc_device *rtc; + struct clk *clk; +}; + +int sa1100_rtc_init(struct platform_device *pdev, struct sa1100_rtc *info); + +#endif diff --git a/drivers/rtc/rtc-sc27xx.c b/drivers/rtc/rtc-sc27xx.c new file mode 100644 index 000000000..deea5c372 --- /dev/null +++ b/drivers/rtc/rtc-sc27xx.c @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2017 Spreadtrum Communications Inc. + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +#define SPRD_RTC_SEC_CNT_VALUE 0x0 +#define SPRD_RTC_MIN_CNT_VALUE 0x4 +#define SPRD_RTC_HOUR_CNT_VALUE 0x8 +#define SPRD_RTC_DAY_CNT_VALUE 0xc +#define SPRD_RTC_SEC_CNT_UPD 0x10 +#define SPRD_RTC_MIN_CNT_UPD 0x14 +#define SPRD_RTC_HOUR_CNT_UPD 0x18 +#define SPRD_RTC_DAY_CNT_UPD 0x1c +#define SPRD_RTC_SEC_ALM_UPD 0x20 +#define SPRD_RTC_MIN_ALM_UPD 0x24 +#define SPRD_RTC_HOUR_ALM_UPD 0x28 +#define SPRD_RTC_DAY_ALM_UPD 0x2c +#define SPRD_RTC_INT_EN 0x30 +#define SPRD_RTC_INT_RAW_STS 0x34 +#define SPRD_RTC_INT_CLR 0x38 +#define SPRD_RTC_INT_MASK_STS 0x3C +#define SPRD_RTC_SEC_ALM_VALUE 0x40 +#define SPRD_RTC_MIN_ALM_VALUE 0x44 +#define SPRD_RTC_HOUR_ALM_VALUE 0x48 +#define SPRD_RTC_DAY_ALM_VALUE 0x4c +#define SPRD_RTC_SPG_VALUE 0x50 +#define SPRD_RTC_SPG_UPD 0x54 +#define SPRD_RTC_PWR_CTRL 0x58 +#define SPRD_RTC_PWR_STS 0x5c +#define SPRD_RTC_SEC_AUXALM_UPD 0x60 +#define SPRD_RTC_MIN_AUXALM_UPD 0x64 +#define SPRD_RTC_HOUR_AUXALM_UPD 0x68 +#define SPRD_RTC_DAY_AUXALM_UPD 0x6c + +/* BIT & MASK definition for SPRD_RTC_INT_* registers */ +#define SPRD_RTC_SEC_EN BIT(0) +#define SPRD_RTC_MIN_EN BIT(1) +#define SPRD_RTC_HOUR_EN BIT(2) +#define SPRD_RTC_DAY_EN BIT(3) +#define SPRD_RTC_ALARM_EN BIT(4) +#define SPRD_RTC_HRS_FORMAT_EN BIT(5) +#define SPRD_RTC_AUXALM_EN BIT(6) +#define SPRD_RTC_SPG_UPD_EN BIT(7) +#define SPRD_RTC_SEC_UPD_EN BIT(8) +#define SPRD_RTC_MIN_UPD_EN BIT(9) +#define SPRD_RTC_HOUR_UPD_EN BIT(10) +#define SPRD_RTC_DAY_UPD_EN BIT(11) +#define SPRD_RTC_ALMSEC_UPD_EN BIT(12) +#define SPRD_RTC_ALMMIN_UPD_EN BIT(13) +#define SPRD_RTC_ALMHOUR_UPD_EN BIT(14) +#define SPRD_RTC_ALMDAY_UPD_EN BIT(15) +#define SPRD_RTC_INT_MASK GENMASK(15, 0) + +#define SPRD_RTC_TIME_INT_MASK \ + (SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN | \ + SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN) + +#define SPRD_RTC_ALMTIME_INT_MASK \ + (SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN | \ + SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN) + +#define SPRD_RTC_ALM_INT_MASK \ + (SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN | \ + SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN | \ + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN) + +/* second/minute/hour/day values mask definition */ +#define SPRD_RTC_SEC_MASK GENMASK(5, 0) +#define SPRD_RTC_MIN_MASK GENMASK(5, 0) +#define SPRD_RTC_HOUR_MASK GENMASK(4, 0) +#define SPRD_RTC_DAY_MASK GENMASK(15, 0) + +/* alarm lock definition for SPRD_RTC_SPG_UPD register */ +#define SPRD_RTC_ALMLOCK_MASK GENMASK(7, 0) +#define SPRD_RTC_ALM_UNLOCK 0xa5 +#define SPRD_RTC_ALM_LOCK (~SPRD_RTC_ALM_UNLOCK & \ + SPRD_RTC_ALMLOCK_MASK) + +/* SPG values definition for SPRD_RTC_SPG_UPD register */ +#define SPRD_RTC_POWEROFF_ALM_FLAG BIT(8) + +/* power control/status definition */ +#define SPRD_RTC_POWER_RESET_VALUE 0x96 +#define SPRD_RTC_POWER_STS_CLEAR GENMASK(7, 0) +#define SPRD_RTC_POWER_STS_SHIFT 8 +#define SPRD_RTC_POWER_STS_VALID \ + (~SPRD_RTC_POWER_RESET_VALUE << SPRD_RTC_POWER_STS_SHIFT) + +/* timeout of synchronizing time and alarm registers (us) */ +#define SPRD_RTC_POLL_TIMEOUT 200000 +#define SPRD_RTC_POLL_DELAY_US 20000 + +struct sprd_rtc { + struct rtc_device *rtc; + struct regmap *regmap; + struct device *dev; + u32 base; + int irq; + bool valid; +}; + +/* + * The Spreadtrum RTC controller has 3 groups registers, including time, normal + * alarm and auxiliary alarm. The time group registers are used to set RTC time, + * the normal alarm registers are used to set normal alarm, and the auxiliary + * alarm registers are used to set auxiliary alarm. Both alarm event and + * auxiliary alarm event can wake up system from deep sleep, but only alarm + * event can power up system from power down status. + */ +enum sprd_rtc_reg_types { + SPRD_RTC_TIME, + SPRD_RTC_ALARM, + SPRD_RTC_AUX_ALARM, +}; + +static int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc) +{ + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_ALM_INT_MASK); +} + +static int sprd_rtc_disable_ints(struct sprd_rtc *rtc) +{ + int ret; + + ret = regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_INT_MASK, 0); + if (ret) + return ret; + + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_INT_MASK); +} + +static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock) +{ + int ret; + u32 val; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val); + if (ret) + return ret; + + val &= ~(SPRD_RTC_ALMLOCK_MASK | SPRD_RTC_POWEROFF_ALM_FLAG); + if (lock) + val |= SPRD_RTC_ALM_LOCK; + else + val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG; + + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val); + if (ret) + return ret; + + /* wait until the SPG value is updated successfully */ + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, val, + (val & SPRD_RTC_SPG_UPD_EN), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret) { + dev_err(rtc->dev, "failed to update SPG value:%d\n", ret); + return ret; + } + + return 0; +} + +static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, + time64_t *secs) +{ + u32 sec_reg, min_reg, hour_reg, day_reg; + u32 val, sec, min, hour, day; + int ret; + + switch (type) { + case SPRD_RTC_TIME: + sec_reg = SPRD_RTC_SEC_CNT_VALUE; + min_reg = SPRD_RTC_MIN_CNT_VALUE; + hour_reg = SPRD_RTC_HOUR_CNT_VALUE; + day_reg = SPRD_RTC_DAY_CNT_VALUE; + break; + case SPRD_RTC_ALARM: + sec_reg = SPRD_RTC_SEC_ALM_VALUE; + min_reg = SPRD_RTC_MIN_ALM_VALUE; + hour_reg = SPRD_RTC_HOUR_ALM_VALUE; + day_reg = SPRD_RTC_DAY_ALM_VALUE; + break; + case SPRD_RTC_AUX_ALARM: + sec_reg = SPRD_RTC_SEC_AUXALM_UPD; + min_reg = SPRD_RTC_MIN_AUXALM_UPD; + hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; + day_reg = SPRD_RTC_DAY_AUXALM_UPD; + break; + default: + return -EINVAL; + } + + ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val); + if (ret) + return ret; + + sec = val & SPRD_RTC_SEC_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val); + if (ret) + return ret; + + min = val & SPRD_RTC_MIN_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val); + if (ret) + return ret; + + hour = val & SPRD_RTC_HOUR_MASK; + + ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val); + if (ret) + return ret; + + day = val & SPRD_RTC_DAY_MASK; + *secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec; + return 0; +} + +static int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type, + time64_t secs) +{ + u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask; + u32 sec, min, hour, day, val; + int ret, rem; + + /* convert seconds to RTC time format */ + day = div_s64_rem(secs, 86400, &rem); + hour = rem / 3600; + rem -= hour * 3600; + min = rem / 60; + sec = rem - min * 60; + + switch (type) { + case SPRD_RTC_TIME: + sec_reg = SPRD_RTC_SEC_CNT_UPD; + min_reg = SPRD_RTC_MIN_CNT_UPD; + hour_reg = SPRD_RTC_HOUR_CNT_UPD; + day_reg = SPRD_RTC_DAY_CNT_UPD; + sts_mask = SPRD_RTC_TIME_INT_MASK; + break; + case SPRD_RTC_ALARM: + sec_reg = SPRD_RTC_SEC_ALM_UPD; + min_reg = SPRD_RTC_MIN_ALM_UPD; + hour_reg = SPRD_RTC_HOUR_ALM_UPD; + day_reg = SPRD_RTC_DAY_ALM_UPD; + sts_mask = SPRD_RTC_ALMTIME_INT_MASK; + break; + case SPRD_RTC_AUX_ALARM: + sec_reg = SPRD_RTC_SEC_AUXALM_UPD; + min_reg = SPRD_RTC_MIN_AUXALM_UPD; + hour_reg = SPRD_RTC_HOUR_AUXALM_UPD; + day_reg = SPRD_RTC_DAY_AUXALM_UPD; + sts_mask = 0; + break; + default: + return -EINVAL; + } + + ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + min_reg, min); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour); + if (ret) + return ret; + + ret = regmap_write(rtc->regmap, rtc->base + day_reg, day); + if (ret) + return ret; + + if (type == SPRD_RTC_AUX_ALARM) + return 0; + + /* + * Since the time and normal alarm registers are put in always-power-on + * region supplied by VDDRTC, then these registers changing time will + * be very long, about 125ms. Thus here we should wait until all + * values are updated successfully. + */ + ret = regmap_read_poll_timeout(rtc->regmap, + rtc->base + SPRD_RTC_INT_RAW_STS, val, + ((val & sts_mask) == sts_mask), + SPRD_RTC_POLL_DELAY_US, + SPRD_RTC_POLL_TIMEOUT); + if (ret < 0) { + dev_err(rtc->dev, "set time/alarm values timeout\n"); + return ret; + } + + return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + sts_mask); +} + +static int sprd_rtc_read_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + u32 val; + int ret; + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_AUX_ALARM, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, &alrm->time); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & SPRD_RTC_AUXALM_EN); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); + if (ret) + return ret; + + alrm->pending = !!(val & SPRD_RTC_AUXALM_EN); + return 0; +} + +static int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(&alrm->time); + int ret; + + /* clear the auxiliary alarm interrupt status */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_AUXALM_EN); + if (ret) + return ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs); + if (ret) + return ret; + + if (alrm->enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_AUXALM_EN, + SPRD_RTC_AUXALM_EN); + } else { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_AUXALM_EN, 0); + } + + return ret; +} + +static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + int ret; + + if (!rtc->valid) { + dev_warn(dev, "RTC values are invalid\n"); + return -EINVAL; + } + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, tm); + return 0; +} + +static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(tm); + int ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs); + if (ret) + return ret; + + if (!rtc->valid) { + /* Clear RTC power status firstly */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_PWR_CTRL, + SPRD_RTC_POWER_STS_CLEAR); + if (ret) + return ret; + + /* + * Set RTC power status to indicate now RTC has valid time + * values. + */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_PWR_CTRL, + SPRD_RTC_POWER_STS_VALID); + if (ret) + return ret; + + rtc->valid = true; + } + + return 0; +} + +static int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs; + int ret; + u32 val; + + /* + * If aie_timer is enabled, we should get the normal alarm time. + * Otherwise we should get auxiliary alarm time. + */ + if (rtc->rtc && rtc->rtc->aie_timer.enabled == 0) + return sprd_rtc_read_aux_alarm(dev, alrm); + + ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs); + if (ret) + return ret; + + rtc_time64_to_tm(secs, &alrm->time); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & SPRD_RTC_ALARM_EN); + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val); + if (ret) + return ret; + + alrm->pending = !!(val & SPRD_RTC_ALARM_EN); + return 0; +} + +static int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + time64_t secs = rtc_tm_to_time64(&alrm->time); + struct rtc_time aie_time = + rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires); + int ret; + + /* + * We have 2 groups alarms: normal alarm and auxiliary alarm. Since + * both normal alarm event and auxiliary alarm event can wake up system + * from deep sleep, but only alarm event can power up system from power + * down status. Moreover we do not need to poll about 125ms when + * updating auxiliary alarm registers. Thus we usually set auxiliary + * alarm when wake up system from deep sleep, and for other scenarios, + * we should set normal alarm with polling status. + * + * So here we check if the alarm time is set by aie_timer, if yes, we + * should set normal alarm, if not, we should set auxiliary alarm which + * means it is just a wake event. + */ + if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time)) + return sprd_rtc_set_aux_alarm(dev, alrm); + + /* clear the alarm interrupt status firstly */ + ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR, + SPRD_RTC_ALARM_EN); + if (ret) + return ret; + + ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs); + if (ret) + return ret; + + if (alrm->enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, + SPRD_RTC_ALARM_EN); + if (ret) + return ret; + + /* unlock the alarm to enable the alarm function. */ + ret = sprd_rtc_lock_alarm(rtc, false); + } else { + regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN, 0); + + /* + * Lock the alarm function in case fake alarm event will power + * up systems. + */ + ret = sprd_rtc_lock_alarm(rtc, true); + } + + return ret; +} + +static int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct sprd_rtc *rtc = dev_get_drvdata(dev); + int ret; + + if (enabled) { + ret = regmap_update_bits(rtc->regmap, + rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN); + if (ret) + return ret; + + ret = sprd_rtc_lock_alarm(rtc, false); + } else { + regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, + SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0); + + ret = sprd_rtc_lock_alarm(rtc, true); + } + + return ret; +} + +static const struct rtc_class_ops sprd_rtc_ops = { + .read_time = sprd_rtc_read_time, + .set_time = sprd_rtc_set_time, + .read_alarm = sprd_rtc_read_alarm, + .set_alarm = sprd_rtc_set_alarm, + .alarm_irq_enable = sprd_rtc_alarm_irq_enable, +}; + +static irqreturn_t sprd_rtc_handler(int irq, void *dev_id) +{ + struct sprd_rtc *rtc = dev_id; + int ret; + + ret = sprd_rtc_clear_alarm_ints(rtc); + if (ret) + return IRQ_RETVAL(ret); + + rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static int sprd_rtc_check_power_down(struct sprd_rtc *rtc) +{ + u32 val; + int ret; + + ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_PWR_STS, &val); + if (ret) + return ret; + + /* + * If the RTC power status value is SPRD_RTC_POWER_RESET_VALUE, which + * means the RTC has been powered down, so the RTC time values are + * invalid. + */ + rtc->valid = val == SPRD_RTC_POWER_RESET_VALUE ? false : true; + return 0; +} + +static int sprd_rtc_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct sprd_rtc *rtc; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!rtc->regmap) + return -ENODEV; + + ret = of_property_read_u32(node, "reg", &rtc->base); + if (ret) { + dev_err(&pdev->dev, "failed to get RTC base address\n"); + return ret; + } + + rtc->irq = platform_get_irq(pdev, 0); + if (rtc->irq < 0) { + dev_err(&pdev->dev, "failed to get RTC irq number\n"); + return rtc->irq; + } + + rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) + return PTR_ERR(rtc->rtc); + + rtc->dev = &pdev->dev; + platform_set_drvdata(pdev, rtc); + + /* clear all RTC interrupts and disable all RTC interrupts */ + ret = sprd_rtc_disable_ints(rtc); + if (ret) { + dev_err(&pdev->dev, "failed to disable RTC interrupts\n"); + return ret; + } + + /* check if RTC time values are valid */ + ret = sprd_rtc_check_power_down(rtc); + if (ret) { + dev_err(&pdev->dev, "failed to check RTC time values\n"); + return ret; + } + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + sprd_rtc_handler, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + pdev->name, rtc); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request RTC irq\n"); + return ret; + } + + rtc->rtc->ops = &sprd_rtc_ops; + rtc->rtc->range_min = 0; + rtc->rtc->range_max = 5662310399LL; + ret = rtc_register_device(rtc->rtc); + if (ret) { + dev_err(&pdev->dev, "failed to register rtc device\n"); + return ret; + } + + device_init_wakeup(&pdev->dev, 1); + return 0; +} + +static int sprd_rtc_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + return 0; +} + +static const struct of_device_id sprd_rtc_of_match[] = { + { .compatible = "sprd,sc2731-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sprd_rtc_of_match); + +static struct platform_driver sprd_rtc_driver = { + .driver = { + .name = "sprd-rtc", + .of_match_table = sprd_rtc_of_match, + }, + .probe = sprd_rtc_probe, + .remove = sprd_rtc_remove, +}; +module_platform_driver(sprd_rtc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Spreadtrum RTC Device Driver"); +MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>"); diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c new file mode 100644 index 000000000..3d7414e5e --- /dev/null +++ b/drivers/rtc/rtc-sh.c @@ -0,0 +1,685 @@ +/* + * 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/mod_devicetable.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> +#ifdef CONFIG_SUPERH +#include <asm/rtc.h> +#else +/* Default values for RZ/A RTC */ +#define rtc_reg_size sizeof(u16) +#define RTC_BIT_INVERTED 0 /* no chip bugs */ +#define RTC_CAP_4_DIGIT_YEAR (1 << 0) +#define RTC_DEF_CAPABILITIES RTC_CAP_4_DIGIT_YEAR +#endif + +#define DRV_NAME "sh-rtc" + +#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) +{ + 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; + 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 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 sh_rtc *rtc = dev_get_drvdata(dev); + 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 0; +} + +static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + 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 = -1; /* return -1 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 sh_rtc *rtc = dev_get_drvdata(dev); + 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 */ + + 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_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sh_rtc *rtc = dev_get_drvdata(dev); + unsigned int rcr1; + struct rtc_time *tm = &wkalrm->time; + int mon; + + 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 const 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 (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 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; + + if (!pdev->dev.of_node) { + 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); + } else + snprintf(clk_name, sizeof(clk_name), "fck"); + + 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; + +#ifdef CONFIG_SUPERH + 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; + } +#endif + + 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_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_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 sh_rtc *rtc = dev_get_drvdata(dev); + + 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); + } +} + +static int __maybe_unused sh_rtc_suspend(struct device *dev) +{ + if (device_may_wakeup(dev)) + sh_rtc_set_irq_wake(dev, 1); + + return 0; +} + +static int __maybe_unused sh_rtc_resume(struct device *dev) +{ + if (device_may_wakeup(dev)) + sh_rtc_set_irq_wake(dev, 0); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(sh_rtc_pm_ops, sh_rtc_suspend, sh_rtc_resume); + +static const struct of_device_id sh_rtc_of_match[] = { + { .compatible = "renesas,sh-rtc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sh_rtc_of_match); + +static struct platform_driver sh_rtc_platform_driver = { + .driver = { + .name = DRV_NAME, + .pm = &sh_rtc_pm_ops, + .of_match_table = sh_rtc_of_match, + }, + .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_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..2a9e151ca --- /dev/null +++ b/drivers/rtc/rtc-sirfsoc.c @@ -0,0 +1,461 @@ +/* + * 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/regmap.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; + struct regmap *regmap; +#ifdef CONFIG_PM + u32 saved_counter; + u32 saved_overflow_rtc; +#endif +}; + +static u32 sirfsoc_rtc_readl(struct sirfsoc_rtc_drv *rtcdrv, u32 offset) +{ + u32 val; + + regmap_read(rtcdrv->regmap, rtcdrv->rtc_base + offset, &val); + return val; +} + +static void sirfsoc_rtc_writel(struct sirfsoc_rtc_drv *rtcdrv, + u32 offset, u32 val) +{ + regmap_write(rtcdrv->regmap, rtcdrv->rtc_base + offset, val); +} + +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_readl(rtcdrv, RTC_CN); + + rtc_alarm = sirfsoc_rtc_readl(rtcdrv, 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_readl(rtcdrv, 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_readl(rtcdrv, 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_writel(rtcdrv, RTC_ALARM0, rtc_alarm << RTC_SHIFT); + 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_writel(rtcdrv, RTC_STATUS, rtc_status_reg); + + 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_readl(rtcdrv, 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_writel(rtcdrv, RTC_STATUS, + rtc_status_reg); + } + + 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_readl(rtcdrv, RTC_CN); + cpu_relax(); + } while (tmp_rtc != sirfsoc_rtc_readl(rtcdrv, 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_writel(rtcdrv, RTC_SW_VALUE, rtcdrv->overflow_rtc); + sirfsoc_rtc_writel(rtcdrv, RTC_CN, rtc_time << RTC_SHIFT); + + return 0; +} + +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_readl(rtcdrv, RTC_STATUS); + if (enabled) + rtc_status_reg |= SIRFSOC_RTC_AL0E; + else + rtc_status_reg &= ~SIRFSOC_RTC_AL0E; + + sirfsoc_rtc_writel(rtcdrv, RTC_STATUS, rtc_status_reg); + + 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, + .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_readl(rtcdrv, 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_writel(rtcdrv, RTC_STATUS, rtc_status_reg); + + 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"}, + {}, +}; + +const struct regmap_config sysrtc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .fast_io = true, +}; + +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); + + rtcdrv->regmap = devm_regmap_init_iobg(&pdev->dev, + &sysrtc_regmap_config); + if (IS_ERR(rtcdrv->regmap)) { + err = PTR_ERR(rtcdrv->regmap); + dev_err(&pdev->dev, "Failed to allocate register map: %d\n", + err); + return err; + } + + /* + * 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_writel(rtcdrv, RTC_DIV, rtc_div); + + /* 0x3 -> RTC_CLK */ + sirfsoc_rtc_writel(rtcdrv, RTC_CLOCK_SWITCH, SIRFSOC_RTC_CLK); + + /* reset SYS RTC ALARM0 */ + sirfsoc_rtc_writel(rtcdrv, RTC_ALARM0, 0x0); + + /* reset SYS RTC ALARM1 */ + sirfsoc_rtc_writel(rtcdrv, RTC_ALARM1, 0x0); + + /* Restore RTC Overflow From Register After Command Reboot */ + rtcdrv->overflow_rtc = + sirfsoc_rtc_readl(rtcdrv, 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_readl(rtcdrv, RTC_SW_VALUE); + + rtcdrv->saved_counter = + sirfsoc_rtc_readl(rtcdrv, 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_readl(rtcdrv, RTC_CLOCK_SWITCH)) { + u32 rtc_div; + /* 0x3 -> RTC_CLK */ + sirfsoc_rtc_writel(rtcdrv, RTC_CLOCK_SWITCH, SIRFSOC_RTC_CLK); + /* + * 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_writel(rtcdrv, RTC_DIV, rtc_div); + + /* reset SYS RTC ALARM0 */ + sirfsoc_rtc_writel(rtcdrv, RTC_ALARM0, 0x0); + + /* reset SYS RTC ALARM1 */ + sirfsoc_rtc_writel(rtcdrv, RTC_ALARM1, 0x0); + } + rtcdrv->overflow_rtc = rtcdrv->saved_overflow_rtc; + + /* + * if current counter is small than previous, + * it means overflow in sleep + */ + tmp = sirfsoc_rtc_readl(rtcdrv, RTC_CN); + if (tmp <= rtcdrv->saved_counter) + rtcdrv->overflow_rtc++; + /* + *PWRC Value Be Changed When Suspend, Restore Overflow + * In Memory To Register + */ + sirfsoc_rtc_writel(rtcdrv, RTC_SW_VALUE, rtcdrv->overflow_rtc); + + 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..3cf011e12 --- /dev/null +++ b/drivers/rtc/rtc-snvs.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// Copyright (C) 2011-2012 Freescale Semiconductor, Inc. + +#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> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#define SNVS_LPREGISTER_OFFSET 0x34 + +/* 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; + struct regmap *regmap; + int offset; + int irq; + struct clk *clk; +}; + +/* Read 64 bit timer register, which could be in inconsistent state */ +static u64 rtc_read_lpsrt(struct snvs_rtc_data *data) +{ + u32 msb, lsb; + + regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &msb); + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &lsb); + return (u64)msb << 32 | lsb; +} + +/* Read the secure real time counter, taking care to deal with the cases of the + * counter updating while being read. + */ +static u32 rtc_read_lp_counter(struct snvs_rtc_data *data) +{ + u64 read1, read2; + unsigned int timeout = 100; + + /* As expected, the registers might update between the read of the LSB + * reg and the MSB reg. It's also possible that one register might be + * in partially modified state as well. + */ + read1 = rtc_read_lpsrt(data); + do { + read2 = read1; + read1 = rtc_read_lpsrt(data); + } while (read1 != read2 && --timeout); + if (!timeout) + dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); + + /* Convert 47-bit counter to 32-bit raw second count */ + return (u32) (read1 >> CNTR_TO_SECS_SH); +} + +/* Just read the lsb from the counter, dealing with inconsistent state */ +static int rtc_read_lp_counter_lsb(struct snvs_rtc_data *data, u32 *lsb) +{ + u32 count1, count2; + unsigned int timeout = 100; + + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + do { + count2 = count1; + regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &count1); + } while (count1 != count2 && --timeout); + if (!timeout) { + dev_err(&data->rtc->dev, "Timeout trying to get valid LPSRT Counter read\n"); + return -ETIMEDOUT; + } + + *lsb = count1; + return 0; +} + +static int rtc_write_sync_lp(struct snvs_rtc_data *data) +{ + u32 count1, count2; + u32 elapsed; + unsigned int timeout = 1000; + int ret; + + ret = rtc_read_lp_counter_lsb(data, &count1); + if (ret) + return ret; + + /* Wait for 3 CKIL cycles, about 61.0-91.5 µs */ + do { + ret = rtc_read_lp_counter_lsb(data, &count2); + if (ret) + return ret; + elapsed = count2 - count1; /* wrap around _is_ handled! */ + } while (elapsed < 3 && --timeout); + if (!timeout) { + dev_err(&data->rtc->dev, "Timeout waiting for LPSRT Counter to change\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable) +{ + int timeout = 1000; + u32 lpcr; + + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_SRTC_ENV, + enable ? SNVS_LPCR_SRTC_ENV : 0); + + while (--timeout) { + regmap_read(data->regmap, data->offset + SNVS_LPCR, &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); + + 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; + int ret; + + rtc_tm_to_time(tm, &time); + + /* Disable RTC first */ + ret = snvs_rtc_enable(data, false); + if (ret) + return ret; + + /* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */ + regmap_write(data->regmap, data->offset + SNVS_LPSRTCLR, time << CNTR_TO_SECS_SH); + regmap_write(data->regmap, data->offset + SNVS_LPSRTCMR, time >> (32 - CNTR_TO_SECS_SH)); + + /* Enable RTC again */ + ret = snvs_rtc_enable(data, true); + + return ret; +} + +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; + + regmap_read(data->regmap, data->offset + SNVS_LPTAR, &lptar); + rtc_time_to_tm(lptar, &alrm->time); + + regmap_read(data->regmap, data->offset + SNVS_LPSR, &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); + + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, + (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN), + enable ? (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN) : 0); + + return rtc_write_sync_lp(data); +} + +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; + int ret; + + rtc_tm_to_time(alrm_tm, &time); + + regmap_update_bits(data->regmap, data->offset + SNVS_LPCR, SNVS_LPCR_LPTA_EN, 0); + ret = rtc_write_sync_lp(data); + if (ret) + return ret; + regmap_write(data->regmap, data->offset + SNVS_LPTAR, time); + + /* Clear alarm interrupt status bit */ + regmap_write(data->regmap, data->offset + SNVS_LPSR, SNVS_LPSR_LPTA); + + 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; + + regmap_read(data->regmap, data->offset + SNVS_LPSR, &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 */ + regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr); + + return events ? IRQ_HANDLED : IRQ_NONE; +} + +static const struct regmap_config snvs_rtc_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static int snvs_rtc_probe(struct platform_device *pdev) +{ + struct snvs_rtc_data *data; + struct resource *res; + int ret; + void __iomem *mmio; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(data->rtc)) + return PTR_ERR(data->rtc); + + data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); + + if (IS_ERR(data->regmap)) { + dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mmio = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config); + } else { + data->offset = SNVS_LPREGISTER_OFFSET; + of_property_read_u32(pdev->dev.of_node, "offset", &data->offset); + } + + if (IS_ERR(data->regmap)) { + dev_err(&pdev->dev, "Can't find snvs syscon\n"); + return -ENODEV; + } + + 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); + + /* Initialize glitch detect */ + regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT); + + /* Clear interrupt status */ + regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff); + + /* Enable RTC */ + ret = snvs_rtc_enable(data, true); + if (ret) { + dev_err(&pdev->dev, "failed to enable rtc %d\n", ret); + goto error_rtc_device_register; + } + + 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->ops = &snvs_rtc_ops; + ret = rtc_register_device(data->rtc); + if (ret) { + 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)) + return 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..0567944fd --- /dev/null +++ b/drivers/rtc/rtc-spear.c @@ -0,0 +1,498 @@ +/* + * 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 void tm2bcd(struct rtc_time *tm) +{ + 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); +} + +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; + + tm2bcd(tm); + + 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; + + tm2bcd(&alm->time); + + 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 const 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; + + 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; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + 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-st-lpc.c b/drivers/rtc/rtc-st-lpc.c new file mode 100644 index 000000000..bee75ca7f --- /dev/null +++ b/drivers/rtc/rtc-st-lpc.c @@ -0,0 +1,331 @@ +/* + * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer + * + * Copyright (C) 2014 STMicroelectronics Limited + * + * Author: David Paris <david.paris@st.com> for STMicroelectronics + * Lee Jones <lee.jones@linaro.org> for STMicroelectronics + * + * Based on the original driver written by Stuart Menefy. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +#include <dt-bindings/mfd/st-lpc.h> + +/* Low Power Timer */ +#define LPC_LPT_LSB_OFF 0x400 +#define LPC_LPT_MSB_OFF 0x404 +#define LPC_LPT_START_OFF 0x408 + +/* Low Power Alarm */ +#define LPC_LPA_LSB_OFF 0x410 +#define LPC_LPA_MSB_OFF 0x414 +#define LPC_LPA_START_OFF 0x418 + +/* LPC as WDT */ +#define LPC_WDT_OFF 0x510 +#define LPC_WDT_FLAG_OFF 0x514 + +struct st_rtc { + struct rtc_device *rtc_dev; + struct rtc_wkalrm alarm; + struct resource *res; + struct clk *clk; + unsigned long clkrate; + void __iomem *ioaddr; + bool irq_enabled:1; + spinlock_t lock; + short irq; +}; + +static void st_rtc_set_hw_alarm(struct st_rtc *rtc, + unsigned long msb, unsigned long lsb) +{ + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF); + + writel_relaxed(msb, rtc->ioaddr + LPC_LPA_MSB_OFF); + writel_relaxed(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF); + + writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF); + + spin_unlock_irqrestore(&rtc->lock, flags); +} + +static irqreturn_t st_rtc_handler(int this_irq, void *data) +{ + struct st_rtc *rtc = (struct st_rtc *)data; + + rtc_update_irq(rtc->rtc_dev, 1, RTC_AF); + + return IRQ_HANDLED; +} + +static int st_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + unsigned long lpt_lsb, lpt_msb; + unsigned long long lpt; + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + do { + lpt_msb = readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF); + lpt_lsb = readl_relaxed(rtc->ioaddr + LPC_LPT_LSB_OFF); + } while (readl_relaxed(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb); + + spin_unlock_irqrestore(&rtc->lock, flags); + + lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb; + do_div(lpt, rtc->clkrate); + rtc_time64_to_tm(lpt, tm); + + return 0; +} + +static int st_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + unsigned long long lpt, secs; + unsigned long flags; + + secs = rtc_tm_to_time64(tm); + + lpt = (unsigned long long)secs * rtc->clkrate; + + spin_lock_irqsave(&rtc->lock, flags); + + writel_relaxed(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF); + writel_relaxed(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_LPT_START_OFF); + + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&rtc->lock, flags); + + memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm)); + + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + + if (enabled && !rtc->irq_enabled) { + enable_irq(rtc->irq); + rtc->irq_enabled = true; + } else if (!enabled && rtc->irq_enabled) { + disable_irq(rtc->irq); + rtc->irq_enabled = false; + } + + return 0; +} + +static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + struct rtc_time now; + unsigned long long now_secs; + unsigned long long alarm_secs; + unsigned long long lpa; + + st_rtc_read_time(dev, &now); + now_secs = rtc_tm_to_time64(&now); + alarm_secs = rtc_tm_to_time64(&t->time); + + /* Invalid alarm time */ + if (now_secs > alarm_secs) + return -EINVAL; + + memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm)); + + /* Now many secs to fire */ + alarm_secs -= now_secs; + lpa = (unsigned long long)alarm_secs * rtc->clkrate; + + st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa); + st_rtc_alarm_irq_enable(dev, t->enabled); + + return 0; +} + +static struct rtc_class_ops st_rtc_ops = { + .read_time = st_rtc_read_time, + .set_time = st_rtc_set_time, + .read_alarm = st_rtc_read_alarm, + .set_alarm = st_rtc_set_alarm, + .alarm_irq_enable = st_rtc_alarm_irq_enable, +}; + +static int st_rtc_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct st_rtc *rtc; + struct resource *res; + uint32_t mode; + int ret = 0; + + ret = of_property_read_u32(np, "st,lpc-mode", &mode); + if (ret) { + dev_err(&pdev->dev, "An LPC mode must be provided\n"); + return -EINVAL; + } + + /* LPC can either run as a Clocksource or in RTC or WDT mode */ + if (mode != ST_LPC_MODE_RTC) + return -ENODEV; + + rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc_dev)) + return PTR_ERR(rtc->rtc_dev); + + spin_lock_init(&rtc->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rtc->ioaddr)) + return PTR_ERR(rtc->ioaddr); + + rtc->irq = irq_of_parse_and_map(np, 0); + if (!rtc->irq) { + dev_err(&pdev->dev, "IRQ missing or invalid\n"); + return -EINVAL; + } + + ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0, + pdev->name, rtc); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq); + return ret; + } + + enable_irq_wake(rtc->irq); + disable_irq(rtc->irq); + + rtc->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(rtc->clk)) { + dev_err(&pdev->dev, "Unable to request clock\n"); + return PTR_ERR(rtc->clk); + } + + clk_prepare_enable(rtc->clk); + + rtc->clkrate = clk_get_rate(rtc->clk); + if (!rtc->clkrate) { + dev_err(&pdev->dev, "Unable to fetch clock rate\n"); + return -EINVAL; + } + + device_set_wakeup_capable(&pdev->dev, 1); + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev->ops = &st_rtc_ops; + rtc->rtc_dev->range_max = U64_MAX; + do_div(rtc->rtc_dev->range_max, rtc->clkrate); + + ret = rtc_register_device(rtc->rtc_dev); + if (ret) { + clk_disable_unprepare(rtc->clk); + return ret; + } + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int st_rtc_suspend(struct device *dev) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + return 0; + + writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_LPA_START_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF); + + return 0; +} + +static int st_rtc_resume(struct device *dev) +{ + struct st_rtc *rtc = dev_get_drvdata(dev); + + rtc_alarm_irq_enable(rtc->rtc_dev, 0); + + /* + * clean 'rtc->alarm' to allow a new + * .set_alarm to the upper RTC layer + */ + memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm)); + + writel_relaxed(0, rtc->ioaddr + LPC_LPA_MSB_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_LPA_LSB_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_WDT_OFF); + writel_relaxed(1, rtc->ioaddr + LPC_LPA_START_OFF); + writel_relaxed(0, rtc->ioaddr + LPC_WDT_OFF); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume); + +static const struct of_device_id st_rtc_match[] = { + { .compatible = "st,stih407-lpc" }, + {} +}; +MODULE_DEVICE_TABLE(of, st_rtc_match); + +static struct platform_driver st_rtc_platform_driver = { + .driver = { + .name = "st-lpc-rtc", + .pm = &st_rtc_pm_ops, + .of_match_table = st_rtc_match, + }, + .probe = st_rtc_probe, +}; + +module_platform_driver(st_rtc_platform_driver); + +MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver"); +MODULE_AUTHOR("David Paris <david.paris@st.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-starfire.c b/drivers/rtc/rtc-starfire.c new file mode 100644 index 000000000..a7d49329d --- /dev/null +++ b/drivers/rtc/rtc-starfire.c @@ -0,0 +1,58 @@ +/* rtc-starfire.c: Starfire platform RTC driver. + * + * Author: David S. Miller + * License: GPL + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/rtc.h> +#include <linux/platform_device.h> + +#include <asm/oplib.h> + +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 0; +} + +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", + }, +}; + +builtin_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..fccbecbb2 --- /dev/null +++ b/drivers/rtc/rtc-stk17ta8.c @@ -0,0 +1,343 @@ +/* + * 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 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + 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; + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 rtc_plat_data *pdata = dev_get_drvdata(dev); + + 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 int stk17ta8_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + *buf++ = readb(ioaddr + pos++); + return 0; +} + +static int stk17ta8_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct rtc_plat_data *pdata = priv; + void __iomem *ioaddr = pdata->ioaddr; + u8 *buf = val; + + for (; bytes; bytes--) + writeb(*buf++, ioaddr + pos++); + return 0; +} + +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; + struct nvmem_config nvmem_cfg = { + .name = "stk17ta8_nvram", + .word_size = 1, + .stride = 1, + .size = RTC_OFFSET, + .reg_read = stk17ta8_nvram_read, + .reg_write = stk17ta8_nvram_write, + }; + + 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_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + pdata->rtc->ops = &stk17ta8_rtc_ops; + pdata->rtc->nvram_old_abi = true; + + nvmem_cfg.priv = pdata; + ret = rtc_nvmem_register(pdata->rtc, &nvmem_cfg); + if (ret) + return ret; + + return rtc_register_device(pdata->rtc); +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:stk17ta8"); + +static struct platform_driver stk17ta8_rtc_driver = { + .probe = stk17ta8_rtc_probe, + .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"); diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c new file mode 100644 index 000000000..04bffc913 --- /dev/null +++ b/drivers/rtc/rtc-stm32.c @@ -0,0 +1,937 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2017 + * Author: Amelie Delaunay <amelie.delaunay@st.com> + */ + +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +#define DRIVER_NAME "stm32_rtc" + +/* STM32_RTC_TR bit fields */ +#define STM32_RTC_TR_SEC_SHIFT 0 +#define STM32_RTC_TR_SEC GENMASK(6, 0) +#define STM32_RTC_TR_MIN_SHIFT 8 +#define STM32_RTC_TR_MIN GENMASK(14, 8) +#define STM32_RTC_TR_HOUR_SHIFT 16 +#define STM32_RTC_TR_HOUR GENMASK(21, 16) + +/* STM32_RTC_DR bit fields */ +#define STM32_RTC_DR_DATE_SHIFT 0 +#define STM32_RTC_DR_DATE GENMASK(5, 0) +#define STM32_RTC_DR_MONTH_SHIFT 8 +#define STM32_RTC_DR_MONTH GENMASK(12, 8) +#define STM32_RTC_DR_WDAY_SHIFT 13 +#define STM32_RTC_DR_WDAY GENMASK(15, 13) +#define STM32_RTC_DR_YEAR_SHIFT 16 +#define STM32_RTC_DR_YEAR GENMASK(23, 16) + +/* STM32_RTC_CR bit fields */ +#define STM32_RTC_CR_FMT BIT(6) +#define STM32_RTC_CR_ALRAE BIT(8) +#define STM32_RTC_CR_ALRAIE BIT(12) + +/* STM32_RTC_ISR/STM32_RTC_ICSR bit fields */ +#define STM32_RTC_ISR_ALRAWF BIT(0) +#define STM32_RTC_ISR_INITS BIT(4) +#define STM32_RTC_ISR_RSF BIT(5) +#define STM32_RTC_ISR_INITF BIT(6) +#define STM32_RTC_ISR_INIT BIT(7) +#define STM32_RTC_ISR_ALRAF BIT(8) + +/* STM32_RTC_PRER bit fields */ +#define STM32_RTC_PRER_PRED_S_SHIFT 0 +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0) +#define STM32_RTC_PRER_PRED_A_SHIFT 16 +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16) + +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */ +#define STM32_RTC_ALRMXR_SEC_SHIFT 0 +#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0) +#define STM32_RTC_ALRMXR_SEC_MASK BIT(7) +#define STM32_RTC_ALRMXR_MIN_SHIFT 8 +#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8) +#define STM32_RTC_ALRMXR_MIN_MASK BIT(15) +#define STM32_RTC_ALRMXR_HOUR_SHIFT 16 +#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16) +#define STM32_RTC_ALRMXR_PM BIT(22) +#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23) +#define STM32_RTC_ALRMXR_DATE_SHIFT 24 +#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24) +#define STM32_RTC_ALRMXR_WDSEL BIT(30) +#define STM32_RTC_ALRMXR_WDAY_SHIFT 24 +#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24) +#define STM32_RTC_ALRMXR_DATE_MASK BIT(31) + +/* STM32_RTC_SR/_SCR bit fields */ +#define STM32_RTC_SR_ALRA BIT(0) + +/* STM32_RTC_VERR bit fields */ +#define STM32_RTC_VERR_MINREV_SHIFT 0 +#define STM32_RTC_VERR_MINREV GENMASK(3, 0) +#define STM32_RTC_VERR_MAJREV_SHIFT 4 +#define STM32_RTC_VERR_MAJREV GENMASK(7, 4) + +/* STM32_RTC_WPR key constants */ +#define RTC_WPR_1ST_KEY 0xCA +#define RTC_WPR_2ND_KEY 0x53 +#define RTC_WPR_WRONG_KEY 0xFF + +/* Max STM32 RTC register offset is 0x3FC */ +#define UNDEF_REG 0xFFFF + +struct stm32_rtc; + +struct stm32_rtc_registers { + u16 tr; + u16 dr; + u16 cr; + u16 isr; + u16 prer; + u16 alrmar; + u16 wpr; + u16 sr; + u16 scr; + u16 verr; +}; + +struct stm32_rtc_events { + u32 alra; +}; + +struct stm32_rtc_data { + const struct stm32_rtc_registers regs; + const struct stm32_rtc_events events; + void (*clear_events)(struct stm32_rtc *rtc, unsigned int flags); + bool has_pclk; + bool need_dbp; + bool has_wakeirq; +}; + +struct stm32_rtc { + struct rtc_device *rtc_dev; + void __iomem *base; + struct regmap *dbp; + unsigned int dbp_reg; + unsigned int dbp_mask; + struct clk *pclk; + struct clk *rtc_ck; + const struct stm32_rtc_data *data; + int irq_alarm; + int wakeirq_alarm; +}; + +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + + writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + regs->wpr); + writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + regs->wpr); +} + +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + + writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + regs->wpr); +} + +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int isr = readl_relaxed(rtc->base + regs->isr); + + if (!(isr & STM32_RTC_ISR_INITF)) { + isr |= STM32_RTC_ISR_INIT; + writel_relaxed(isr, rtc->base + regs->isr); + + /* + * It takes around 2 rtc_ck clock cycles to enter in + * initialization phase mode (and have INITF flag set). As + * slowest rtc_ck frequency may be 32kHz and highest should be + * 1MHz, we poll every 10 us with a timeout of 100ms. + */ + return readl_relaxed_poll_timeout_atomic( + rtc->base + regs->isr, + isr, (isr & STM32_RTC_ISR_INITF), + 10, 100000); + } + + return 0; +} + +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int isr = readl_relaxed(rtc->base + regs->isr); + + isr &= ~STM32_RTC_ISR_INIT; + writel_relaxed(isr, rtc->base + regs->isr); +} + +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int isr = readl_relaxed(rtc->base + regs->isr); + + isr &= ~STM32_RTC_ISR_RSF; + writel_relaxed(isr, rtc->base + regs->isr); + + /* + * Wait for RSF to be set to ensure the calendar registers are + * synchronised, it takes around 2 rtc_ck clock cycles + */ + return readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr, + isr, + (isr & STM32_RTC_ISR_RSF), + 10, 100000); +} + +static void stm32_rtc_clear_event_flags(struct stm32_rtc *rtc, + unsigned int flags) +{ + rtc->data->clear_events(rtc, flags); +} + +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id) +{ + struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id; + const struct stm32_rtc_registers *regs = &rtc->data->regs; + const struct stm32_rtc_events *evts = &rtc->data->events; + unsigned int status, cr; + + mutex_lock(&rtc->rtc_dev->ops_lock); + + status = readl_relaxed(rtc->base + regs->sr); + cr = readl_relaxed(rtc->base + regs->cr); + + if ((status & evts->alra) && + (cr & STM32_RTC_CR_ALRAIE)) { + /* Alarm A flag - Alarm interrupt */ + dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n"); + + /* Pass event to the kernel */ + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF); + + /* Clear event flags, otherwise new events won't be received */ + stm32_rtc_clear_event_flags(rtc, evts->alra); + } + + mutex_unlock(&rtc->rtc_dev->ops_lock); + + return IRQ_HANDLED; +} + +/* Convert rtc_time structure from bin to bcd format */ +static void tm2bcd(struct rtc_time *tm) +{ + 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 - 100); + /* + * Number of days since Sunday + * - on kernel side, 0=Sunday...6=Saturday + * - on rtc side, 0=invalid,1=Monday...7=Sunday + */ + tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday; +} + +/* Convert rtc_time structure from bcd to bin format */ +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; + tm->tm_year = bcd2bin(tm->tm_year) + 100; + /* + * Number of days since Sunday + * - on kernel side, 0=Sunday...6=Saturday + * - on rtc side, 0=invalid,1=Monday...7=Sunday + */ + tm->tm_wday %= 7; +} + +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int tr, dr; + + /* Time and Date in BCD format */ + tr = readl_relaxed(rtc->base + regs->tr); + dr = readl_relaxed(rtc->base + regs->dr); + + tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT; + tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT; + tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT; + + tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT; + tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT; + tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT; + tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT; + + /* We don't report tm_yday and tm_isdst */ + + bcd2tm(tm); + + return 0; +} + +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int tr, dr; + int ret = 0; + + tm2bcd(tm); + + /* Time in BCD format */ + tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) | + ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) | + ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR); + + /* Date in BCD format */ + dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) | + ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) | + ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) | + ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY); + + stm32_rtc_wpr_unlock(rtc); + + ret = stm32_rtc_enter_init_mode(rtc); + if (ret) { + dev_err(dev, "Can't enter in init mode. Set time aborted.\n"); + goto end; + } + + writel_relaxed(tr, rtc->base + regs->tr); + writel_relaxed(dr, rtc->base + regs->dr); + + stm32_rtc_exit_init_mode(rtc); + + ret = stm32_rtc_wait_sync(rtc); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + const struct stm32_rtc_events *evts = &rtc->data->events; + struct rtc_time *tm = &alrm->time; + unsigned int alrmar, cr, status; + + alrmar = readl_relaxed(rtc->base + regs->alrmar); + cr = readl_relaxed(rtc->base + regs->cr); + status = readl_relaxed(rtc->base + regs->sr); + + if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) { + /* + * Date/day doesn't matter in Alarm comparison so alarm + * triggers every day + */ + tm->tm_mday = -1; + tm->tm_wday = -1; + } else { + if (alrmar & STM32_RTC_ALRMXR_WDSEL) { + /* Alarm is set to a day of week */ + tm->tm_mday = -1; + tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >> + STM32_RTC_ALRMXR_WDAY_SHIFT; + tm->tm_wday %= 7; + } else { + /* Alarm is set to a day of month */ + tm->tm_wday = -1; + tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >> + STM32_RTC_ALRMXR_DATE_SHIFT; + } + } + + if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) { + /* Hours don't matter in Alarm comparison */ + tm->tm_hour = -1; + } else { + tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >> + STM32_RTC_ALRMXR_HOUR_SHIFT; + if (alrmar & STM32_RTC_ALRMXR_PM) + tm->tm_hour += 12; + } + + if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) { + /* Minutes don't matter in Alarm comparison */ + tm->tm_min = -1; + } else { + tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >> + STM32_RTC_ALRMXR_MIN_SHIFT; + } + + if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) { + /* Seconds don't matter in Alarm comparison */ + tm->tm_sec = -1; + } else { + tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >> + STM32_RTC_ALRMXR_SEC_SHIFT; + } + + bcd2tm(tm); + + alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0; + alrm->pending = (status & evts->alra) ? 1 : 0; + + return 0; +} + +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + const struct stm32_rtc_events *evts = &rtc->data->events; + unsigned int cr; + + cr = readl_relaxed(rtc->base + regs->cr); + + stm32_rtc_wpr_unlock(rtc); + + /* We expose Alarm A to the kernel */ + if (enabled) + cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE); + else + cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE); + writel_relaxed(cr, rtc->base + regs->cr); + + /* Clear event flags, otherwise new events won't be received */ + stm32_rtc_clear_event_flags(rtc, evts->alra); + + stm32_rtc_wpr_lock(rtc); + + return 0; +} + +static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec; + unsigned int dr = readl_relaxed(rtc->base + regs->dr); + unsigned int tr = readl_relaxed(rtc->base + regs->tr); + + cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT; + cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT; + cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT; + cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT; + cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT; + cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT; + + /* + * Assuming current date is M-D-Y H:M:S. + * RTC alarm can't be set on a specific month and year. + * So the valid alarm range is: + * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S + * with a specific case for December... + */ + if ((((tm->tm_year > cur_year) && + (tm->tm_mon == 0x1) && (cur_mon == 0x12)) || + ((tm->tm_year == cur_year) && + (tm->tm_mon <= cur_mon + 1))) && + ((tm->tm_mday > cur_day) || + ((tm->tm_mday == cur_day) && + ((tm->tm_hour > cur_hour) || + ((tm->tm_hour == cur_hour) && (tm->tm_min > cur_min)) || + ((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) && + (tm->tm_sec >= cur_sec)))))) + return 0; + + return -EINVAL; +} + +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + struct rtc_time *tm = &alrm->time; + unsigned int cr, isr, alrmar; + int ret = 0; + + tm2bcd(tm); + + /* + * RTC alarm can't be set on a specific date, unless this date is + * up to the same day of month next month. + */ + if (stm32_rtc_valid_alrm(rtc, tm) < 0) { + dev_err(dev, "Alarm can be set only on upcoming month.\n"); + return -EINVAL; + } + + alrmar = 0; + /* tm_year and tm_mon are not used because not supported by RTC */ + alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) & + STM32_RTC_ALRMXR_DATE; + /* 24-hour format */ + alrmar &= ~STM32_RTC_ALRMXR_PM; + alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) & + STM32_RTC_ALRMXR_HOUR; + alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) & + STM32_RTC_ALRMXR_MIN; + alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) & + STM32_RTC_ALRMXR_SEC; + + stm32_rtc_wpr_unlock(rtc); + + /* Disable Alarm */ + cr = readl_relaxed(rtc->base + regs->cr); + cr &= ~STM32_RTC_CR_ALRAE; + writel_relaxed(cr, rtc->base + regs->cr); + + /* + * Poll Alarm write flag to be sure that Alarm update is allowed: it + * takes around 2 rtc_ck clock cycles + */ + ret = readl_relaxed_poll_timeout_atomic(rtc->base + regs->isr, + isr, + (isr & STM32_RTC_ISR_ALRAWF), + 10, 100000); + + if (ret) { + dev_err(dev, "Alarm update not allowed\n"); + goto end; + } + + /* Write to Alarm register */ + writel_relaxed(alrmar, rtc->base + regs->alrmar); + + if (alrm->enabled) + stm32_rtc_alarm_irq_enable(dev, 1); + else + stm32_rtc_alarm_irq_enable(dev, 0); + +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static const struct rtc_class_ops stm32_rtc_ops = { + .read_time = stm32_rtc_read_time, + .set_time = stm32_rtc_set_time, + .read_alarm = stm32_rtc_read_alarm, + .set_alarm = stm32_rtc_set_alarm, + .alarm_irq_enable = stm32_rtc_alarm_irq_enable, +}; + +static void stm32_rtc_clear_events(struct stm32_rtc *rtc, + unsigned int flags) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + + /* Flags are cleared by writing 0 in RTC_ISR */ + writel_relaxed(readl_relaxed(rtc->base + regs->isr) & ~flags, + rtc->base + regs->isr); +} + +static const struct stm32_rtc_data stm32_rtc_data = { + .has_pclk = false, + .need_dbp = true, + .has_wakeirq = false, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x08, + .isr = 0x0C, + .prer = 0x10, + .alrmar = 0x1C, + .wpr = 0x24, + .sr = 0x0C, /* set to ISR offset to ease alarm management */ + .scr = UNDEF_REG, + .verr = UNDEF_REG, + }, + .events = { + .alra = STM32_RTC_ISR_ALRAF, + }, + .clear_events = stm32_rtc_clear_events, +}; + +static const struct stm32_rtc_data stm32h7_rtc_data = { + .has_pclk = true, + .need_dbp = true, + .has_wakeirq = false, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x08, + .isr = 0x0C, + .prer = 0x10, + .alrmar = 0x1C, + .wpr = 0x24, + .sr = 0x0C, /* set to ISR offset to ease alarm management */ + .scr = UNDEF_REG, + .verr = UNDEF_REG, + }, + .events = { + .alra = STM32_RTC_ISR_ALRAF, + }, + .clear_events = stm32_rtc_clear_events, +}; + +static void stm32mp1_rtc_clear_events(struct stm32_rtc *rtc, + unsigned int flags) +{ + struct stm32_rtc_registers regs = rtc->data->regs; + + /* Flags are cleared by writing 1 in RTC_SCR */ + writel_relaxed(flags, rtc->base + regs.scr); +} + +static const struct stm32_rtc_data stm32mp1_data = { + .has_pclk = true, + .need_dbp = false, + .has_wakeirq = true, + .regs = { + .tr = 0x00, + .dr = 0x04, + .cr = 0x18, + .isr = 0x0C, /* named RTC_ICSR on stm32mp1 */ + .prer = 0x10, + .alrmar = 0x40, + .wpr = 0x24, + .sr = 0x50, + .scr = 0x5C, + .verr = 0x3F4, + }, + .events = { + .alra = STM32_RTC_SR_ALRA, + }, + .clear_events = stm32mp1_rtc_clear_events, +}; + +static const struct of_device_id stm32_rtc_of_match[] = { + { .compatible = "st,stm32-rtc", .data = &stm32_rtc_data }, + { .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data }, + { .compatible = "st,stm32mp1-rtc", .data = &stm32mp1_data }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match); + +static int stm32_rtc_init(struct platform_device *pdev, + struct stm32_rtc *rtc) +{ + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr; + unsigned int rate; + int ret = 0; + + rate = clk_get_rate(rtc->rtc_ck); + + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */ + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT; + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT; + + for (pred_a = pred_a_max; pred_a + 1 > 0; pred_a--) { + pred_s = (rate / (pred_a + 1)) - 1; + + if (((pred_s + 1) * (pred_a + 1)) == rate) + break; + } + + /* + * Can't find a 1Hz, so give priority to RTC power consumption + * by choosing the higher possible value for prediv_a + */ + if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) { + pred_a = pred_a_max; + pred_s = (rate / (pred_a + 1)) - 1; + + dev_warn(&pdev->dev, "rtc_ck is %s\n", + (rate < ((pred_a + 1) * (pred_s + 1))) ? + "fast" : "slow"); + } + + stm32_rtc_wpr_unlock(rtc); + + ret = stm32_rtc_enter_init_mode(rtc); + if (ret) { + dev_err(&pdev->dev, + "Can't enter in init mode. Prescaler config failed.\n"); + goto end; + } + + prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S; + writel_relaxed(prer, rtc->base + regs->prer); + prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A; + writel_relaxed(prer, rtc->base + regs->prer); + + /* Force 24h time format */ + cr = readl_relaxed(rtc->base + regs->cr); + cr &= ~STM32_RTC_CR_FMT; + writel_relaxed(cr, rtc->base + regs->cr); + + stm32_rtc_exit_init_mode(rtc); + + ret = stm32_rtc_wait_sync(rtc); +end: + stm32_rtc_wpr_lock(rtc); + + return ret; +} + +static int stm32_rtc_probe(struct platform_device *pdev) +{ + struct stm32_rtc *rtc; + const struct stm32_rtc_registers *regs; + struct resource *res; + int ret; + + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return -ENOMEM; + + 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); + + rtc->data = (struct stm32_rtc_data *) + of_device_get_match_data(&pdev->dev); + regs = &rtc->data->regs; + + if (rtc->data->need_dbp) { + rtc->dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "st,syscfg"); + if (IS_ERR(rtc->dbp)) { + dev_err(&pdev->dev, "no st,syscfg\n"); + return PTR_ERR(rtc->dbp); + } + + ret = of_property_read_u32_index(pdev->dev.of_node, "st,syscfg", + 1, &rtc->dbp_reg); + if (ret) { + dev_err(&pdev->dev, "can't read DBP register offset\n"); + return ret; + } + + ret = of_property_read_u32_index(pdev->dev.of_node, "st,syscfg", + 2, &rtc->dbp_mask); + if (ret) { + dev_err(&pdev->dev, "can't read DBP register mask\n"); + return ret; + } + } + + if (!rtc->data->has_pclk) { + rtc->pclk = NULL; + rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL); + } else { + rtc->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(rtc->pclk)) { + dev_err(&pdev->dev, "no pclk clock"); + return PTR_ERR(rtc->pclk); + } + rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck"); + } + if (IS_ERR(rtc->rtc_ck)) { + dev_err(&pdev->dev, "no rtc_ck clock"); + return PTR_ERR(rtc->rtc_ck); + } + + if (rtc->data->has_pclk) { + ret = clk_prepare_enable(rtc->pclk); + if (ret) + return ret; + } + + ret = clk_prepare_enable(rtc->rtc_ck); + if (ret) + goto err_no_rtc_ck; + + if (rtc->data->need_dbp) + regmap_update_bits(rtc->dbp, rtc->dbp_reg, + rtc->dbp_mask, rtc->dbp_mask); + + /* + * After a system reset, RTC_ISR.INITS flag can be read to check if + * the calendar has been initialized or not. INITS flag is reset by a + * power-on reset (no vbat, no power-supply). It is not reset if + * rtc_ck parent clock has changed (so RTC prescalers need to be + * changed). That's why we cannot rely on this flag to know if RTC + * init has to be done. + */ + ret = stm32_rtc_init(pdev, rtc); + if (ret) + goto err; + + rtc->irq_alarm = platform_get_irq(pdev, 0); + if (rtc->irq_alarm <= 0) { + dev_err(&pdev->dev, "no alarm irq\n"); + ret = rtc->irq_alarm; + goto err; + } + + ret = device_init_wakeup(&pdev->dev, true); + if (rtc->data->has_wakeirq) { + rtc->wakeirq_alarm = platform_get_irq(pdev, 1); + if (rtc->wakeirq_alarm > 0) { + ret = dev_pm_set_dedicated_wake_irq(&pdev->dev, + rtc->wakeirq_alarm); + } else { + ret = rtc->wakeirq_alarm; + if (rtc->wakeirq_alarm == -EPROBE_DEFER) + goto err; + } + } + if (ret) + dev_warn(&pdev->dev, "alarm can't wake up the system: %d", ret); + + platform_set_drvdata(pdev, rtc); + + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name, + &stm32_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + dev_err(&pdev->dev, "rtc device registration failed, err=%d\n", + ret); + goto err; + } + + /* Handle RTC alarm interrupts */ + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL, + stm32_rtc_alarm_irq, IRQF_ONESHOT, + pdev->name, rtc); + if (ret) { + dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n", + rtc->irq_alarm); + goto err; + } + + /* + * If INITS flag is reset (calendar year field set to 0x00), calendar + * must be initialized + */ + if (!(readl_relaxed(rtc->base + regs->isr) & STM32_RTC_ISR_INITS)) + dev_warn(&pdev->dev, "Date/Time must be initialized\n"); + + if (regs->verr != UNDEF_REG) { + u32 ver = readl_relaxed(rtc->base + regs->verr); + + dev_info(&pdev->dev, "registered rev:%d.%d\n", + (ver >> STM32_RTC_VERR_MAJREV_SHIFT) & 0xF, + (ver >> STM32_RTC_VERR_MINREV_SHIFT) & 0xF); + } + + return 0; + +err: + clk_disable_unprepare(rtc->rtc_ck); +err_no_rtc_ck: + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + + if (rtc->data->need_dbp) + regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); + + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + + return ret; +} + +static int stm32_rtc_remove(struct platform_device *pdev) +{ + struct stm32_rtc *rtc = platform_get_drvdata(pdev); + const struct stm32_rtc_registers *regs = &rtc->data->regs; + unsigned int cr; + + /* Disable interrupts */ + stm32_rtc_wpr_unlock(rtc); + cr = readl_relaxed(rtc->base + regs->cr); + cr &= ~STM32_RTC_CR_ALRAIE; + writel_relaxed(cr, rtc->base + regs->cr); + stm32_rtc_wpr_lock(rtc); + + clk_disable_unprepare(rtc->rtc_ck); + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + + /* Enable backup domain write protection if needed */ + if (rtc->data->need_dbp) + regmap_update_bits(rtc->dbp, rtc->dbp_reg, rtc->dbp_mask, 0); + + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int stm32_rtc_suspend(struct device *dev) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + + if (rtc->data->has_pclk) + clk_disable_unprepare(rtc->pclk); + + if (device_may_wakeup(dev)) + return enable_irq_wake(rtc->irq_alarm); + + return 0; +} + +static int stm32_rtc_resume(struct device *dev) +{ + struct stm32_rtc *rtc = dev_get_drvdata(dev); + int ret = 0; + + if (rtc->data->has_pclk) { + ret = clk_prepare_enable(rtc->pclk); + if (ret) + return ret; + } + + ret = stm32_rtc_wait_sync(rtc); + if (ret < 0) + return ret; + + if (device_may_wakeup(dev)) + return disable_irq_wake(rtc->irq_alarm); + + return ret; +} +#endif + +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops, + stm32_rtc_suspend, stm32_rtc_resume); + +static struct platform_driver stm32_rtc_driver = { + .probe = stm32_rtc_probe, + .remove = stm32_rtc_remove, + .driver = { + .name = DRIVER_NAME, + .pm = &stm32_rtc_pm_ops, + .of_match_table = stm32_rtc_of_match, + }, +}; + +module_platform_driver(stm32_rtc_driver); + +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c new file mode 100644 index 000000000..b76318fd5 --- /dev/null +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -0,0 +1,424 @@ +/* + * 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_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_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) +{ + int rc = -1; + 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; + rc = platform_device_add(wdt_pdev); + } + + if (rc) + dev_err(&rtc_pdev->dev, + "failed to register stmp3xxx_rtc_wdt\n"); +} +#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 + STMP_OFFSET_REG_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 + + STMP_OFFSET_REG_SET); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET); + } else { + writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | + STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_CLR); + writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_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 const 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 + STMP_OFFSET_REG_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); + + /* + * Resetting the rtc stops the watchdog timer that is potentially + * running. So (assuming it is running on purpose) don't reset if the + * watchdog is enabled. + */ + if (readl(rtc_data->io + STMP3XXX_RTC_CTRL) & + STMP3XXX_RTC_CTRL_WATCHDOGEN) { + dev_info(&pdev->dev, + "Watchdog is running, skip resetting rtc\n"); + } else { + 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 + + STMP_OFFSET_REG_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 + STMP_OFFSET_REG_CLR); + + writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | + STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_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 + STMP_OFFSET_REG_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..11bc562eb --- /dev/null +++ b/drivers/rtc/rtc-sun4v.c @@ -0,0 +1,103 @@ +/* rtc-sun4v.c: Hypervisor based RTC for SUN4V systems. + * + * Author: David S. Miller + * License: GPL + * + * Copyright (C) 2008 David S. Miller <davem@davemloft.net> + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.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", + }, +}; + +builtin_platform_driver_probe(sun4v_rtc_driver, sun4v_rtc_probe); diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c new file mode 100644 index 000000000..e85abe805 --- /dev/null +++ b/drivers/rtc/rtc-sun6i.c @@ -0,0 +1,597 @@ +/* + * 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/clk.h> +#include <linux/clk-provider.h> +#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/slab.h> +#include <linux/types.h> + +/* Control register */ +#define SUN6I_LOSC_CTRL 0x0000 +#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16) +#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_EXT_OSC BIT(0) +#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7) + +#define SUN6I_LOSC_CLK_PRESCAL 0x0008 + +/* 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) + +#define SUN6I_LOSC_OUT_GATING 0x0060 +#define SUN6I_LOSC_OUT_GATING_EN_OFFSET 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; + + struct clk_hw hw; + struct clk_hw *int_osc; + struct clk *losc; + struct clk *ext_losc; + + spinlock_t lock; +}; + +static struct sun6i_rtc_dev *sun6i_rtc; + +static unsigned long sun6i_rtc_osc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + u32 val; + + val = readl(rtc->base + SUN6I_LOSC_CTRL); + if (val & SUN6I_LOSC_CTRL_EXT_OSC) + return parent_rate; + + val = readl(rtc->base + SUN6I_LOSC_CLK_PRESCAL); + val &= GENMASK(4, 0); + + return parent_rate / (val + 1); +} + +static u8 sun6i_rtc_osc_get_parent(struct clk_hw *hw) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + + return readl(rtc->base + SUN6I_LOSC_CTRL) & SUN6I_LOSC_CTRL_EXT_OSC; +} + +static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index) +{ + struct sun6i_rtc_dev *rtc = container_of(hw, struct sun6i_rtc_dev, hw); + unsigned long flags; + u32 val; + + if (index > 1) + return -EINVAL; + + spin_lock_irqsave(&rtc->lock, flags); + val = readl(rtc->base + SUN6I_LOSC_CTRL); + val &= ~SUN6I_LOSC_CTRL_EXT_OSC; + val |= SUN6I_LOSC_CTRL_KEY; + val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0; + writel(val, rtc->base + SUN6I_LOSC_CTRL); + spin_unlock_irqrestore(&rtc->lock, flags); + + return 0; +} + +static const struct clk_ops sun6i_rtc_osc_ops = { + .recalc_rate = sun6i_rtc_osc_recalc_rate, + + .get_parent = sun6i_rtc_osc_get_parent, + .set_parent = sun6i_rtc_osc_set_parent, +}; + +static void __init sun6i_rtc_clk_init(struct device_node *node) +{ + struct clk_hw_onecell_data *clk_data; + struct sun6i_rtc_dev *rtc; + struct clk_init_data init = { + .ops = &sun6i_rtc_osc_ops, + }; + const char *clkout_name = "osc32k-out"; + const char *parents[2]; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (!rtc) + return; + + clk_data = kzalloc(sizeof(*clk_data) + (sizeof(*clk_data->hws) * 2), + GFP_KERNEL); + if (!clk_data) { + kfree(rtc); + return; + } + + spin_lock_init(&rtc->lock); + + rtc->base = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(rtc->base)) { + pr_crit("Can't map RTC registers"); + goto err; + } + + /* Switch to the external, more precise, oscillator */ + writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC, + rtc->base + SUN6I_LOSC_CTRL); + + /* Yes, I know, this is ugly. */ + sun6i_rtc = rtc; + + /* Deal with old DTs */ + if (!of_get_property(node, "clocks", NULL)) + goto err; + + rtc->int_osc = clk_hw_register_fixed_rate_with_accuracy(NULL, + "rtc-int-osc", + NULL, 0, + 667000, + 300000000); + if (IS_ERR(rtc->int_osc)) { + pr_crit("Couldn't register the internal oscillator\n"); + goto err; + } + + parents[0] = clk_hw_get_name(rtc->int_osc); + parents[1] = of_clk_get_parent_name(node, 0); + + rtc->hw.init = &init; + + init.parent_names = parents; + init.num_parents = of_clk_get_parent_count(node) + 1; + of_property_read_string_index(node, "clock-output-names", 0, + &init.name); + + rtc->losc = clk_register(NULL, &rtc->hw); + if (IS_ERR(rtc->losc)) { + pr_crit("Couldn't register the LOSC clock\n"); + goto err_register; + } + + of_property_read_string_index(node, "clock-output-names", 1, + &clkout_name); + rtc->ext_losc = clk_register_gate(NULL, clkout_name, rtc->hw.init->name, + 0, rtc->base + SUN6I_LOSC_OUT_GATING, + SUN6I_LOSC_OUT_GATING_EN_OFFSET, 0, + &rtc->lock); + if (IS_ERR(rtc->ext_losc)) { + pr_crit("Couldn't register the LOSC external gate\n"); + goto err_register; + } + + clk_data->num = 2; + clk_data->hws[0] = &rtc->hw; + clk_data->hws[1] = __clk_get_hw(rtc->ext_losc); + of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); + return; + +err_register: + clk_hw_unregister_fixed_rate(rtc->int_osc); +err: + kfree(clk_data); +} +CLK_OF_DECLARE_DRIVER(sun6i_rtc_clk, "allwinner,sun6i-a31-rtc", + sun6i_rtc_clk_init); + +static irqreturn_t sun6i_rtc_alarmirq(int irq, void *id) +{ + struct sun6i_rtc_dev *chip = (struct sun6i_rtc_dev *) id; + irqreturn_t ret = IRQ_NONE; + u32 val; + + spin_lock(&chip->lock); + 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); + + ret = IRQ_HANDLED; + } + spin_unlock(&chip->lock); + + return ret; +} + +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; + unsigned long flags; + + 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); + } + + spin_lock_irqsave(&chip->lock, flags); + 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); + spin_unlock_irqrestore(&chip->lock, flags); +} + +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 0; +} + +static int sun6i_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct sun6i_rtc_dev *chip = dev_get_drvdata(dev); + unsigned long flags; + u32 alrm_st; + u32 alrm_en; + + spin_lock_irqsave(&chip->lock, flags); + alrm_en = readl(chip->base + SUN6I_ALRM_IRQ_EN); + alrm_st = readl(chip->base + SUN6I_ALRM_IRQ_STA); + spin_unlock_irqrestore(&chip->lock, flags); + + 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 = sun6i_rtc; + int ret; + + if (!chip) + return -ENODEV; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + + 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); + + clk_prepare_enable(chip->losc); + + chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-sun6i", + &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 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, + .driver = { + .name = "sun6i-rtc", + .of_match_table = sun6i_rtc_dt_ids, + }, +}; +builtin_platform_driver(sun6i_rtc_driver); diff --git a/drivers/rtc/rtc-sunxi.c b/drivers/rtc/rtc-sunxi.c new file mode 100644 index 000000000..21865d3d8 --- /dev/null +++ b/drivers/rtc/rtc-sunxi.c @@ -0,0 +1,513 @@ +/* + * 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 const 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; + const 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(unsigned 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 0; +} + +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; + time64_t diff; + unsigned long time_gap; + unsigned long time_gap_day; + unsigned long time_gap_hour; + unsigned long time_gap_min; + int ret; + + ret = sunxi_rtc_gettime(dev, &tm_now); + if (ret < 0) { + dev_err(dev, "Error in getting time\n"); + return -EINVAL; + } + + diff = rtc_tm_sub(alrm_tm, &tm_now); + if (diff <= 0) { + dev_err(dev, "Date to set in the past\n"); + return -EINVAL; + } + + if (diff > 255 * SEC_IN_DAY) { + dev_err(dev, "Day must be in the range 0 - 255\n"); + return -EINVAL; + } + + time_gap = diff; + 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; + + 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; + unsigned 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 %u - %u\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; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + platform_set_drvdata(pdev, chip); + chip->dev = &pdev->dev; + + chip->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(chip->rtc)) + return PTR_ERR(chip->rtc); + + 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; + } + + chip->data_year = of_device_get_match_data(&pdev->dev); + if (!chip->data_year) { + dev_err(&pdev->dev, "Unable to setup RTC data\n"); + return -ENODEV; + } + + /* 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->ops = &sunxi_rtc_ops; + + ret = rtc_register_device(chip->rtc); + if (ret) { + dev_err(&pdev->dev, "unable to register device\n"); + return ret; + } + + dev_info(&pdev->dev, "RTC enabled\n"); + + return 0; +} + +static struct platform_driver sunxi_rtc_driver = { + .probe = sunxi_rtc_probe, + .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..9746c32ee --- /dev/null +++ b/drivers/rtc/rtc-sysfs.c @@ -0,0 +1,362 @@ +/* + * 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 %s\n", dev_driver_string(dev->parent), + dev_name(dev->parent)); +} +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) { + time64_t time; + + time = rtc_tm_to_time64(&tm); + retval = sprintf(buf, "%lld\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; + int err; + + err = kstrtoul(buf, 0, &val); + if (err) + return err; + + 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 ssize_t +wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + time64_t 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) { + alarm = rtc_tm_to_time64(&alm.time); + retval = sprintf(buf, "%lld\n", alarm); + } + + return retval; +} + +static ssize_t +wakealarm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + time64_t now, alarm; + time64_t push = 0; + struct rtc_wkalrm alm; + struct rtc_device *rtc = to_rtc_device(dev); + const 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; + now = rtc_tm_to_time64(&alm.time); + + buf_ptr = buf; + if (*buf_ptr == '+') { + buf_ptr++; + if (*buf_ptr == '=') { + buf_ptr++; + push = 1; + } else + adjust = 1; + } + retval = kstrtos64(buf_ptr, 0, &alarm); + if (retval) + return retval; + 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) { + push = rtc_tm_to_time64(&alm.time); + 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_time64_to_tm(alarm, &alm.time); + + retval = rtc_set_alarm(rtc, &alm); + return (retval < 0) ? retval : n; +} +static DEVICE_ATTR_RW(wakealarm); + +static ssize_t +offset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t retval; + long offset; + + retval = rtc_read_offset(to_rtc_device(dev), &offset); + if (retval == 0) + retval = sprintf(buf, "%ld\n", offset); + + return retval; +} + +static ssize_t +offset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t n) +{ + ssize_t retval; + long offset; + + retval = kstrtol(buf, 10, &offset); + if (retval == 0) + retval = rtc_set_offset(to_rtc_device(dev), offset); + + return (retval < 0) ? retval : n; +} +static DEVICE_ATTR_RW(offset); + +static ssize_t +range_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min, + to_rtc_device(dev)->range_max); +} +static DEVICE_ATTR_RO(range); + +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, + &dev_attr_wakealarm.attr, + &dev_attr_offset.attr, + &dev_attr_range.attr, + NULL, +}; + +/* 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 bool rtc_does_wakealarm(struct rtc_device *rtc) +{ + if (!device_can_wakeup(rtc->dev.parent)) + return false; + + return rtc->ops->set_alarm != NULL; +} + +static umode_t rtc_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct rtc_device *rtc = to_rtc_device(dev); + umode_t mode = attr->mode; + + if (attr == &dev_attr_wakealarm.attr) { + if (!rtc_does_wakealarm(rtc)) + mode = 0; + } else if (attr == &dev_attr_offset.attr) { + if (!rtc->ops->set_offset) + mode = 0; + } else if (attr == &dev_attr_range.attr) { + if (!(rtc->range_max - rtc->range_min)) + mode = 0; + } + + return mode; +} + +static struct attribute_group rtc_attr_group = { + .is_visible = rtc_attr_is_visible, + .attrs = rtc_attrs, +}; + +static const struct attribute_group *rtc_attr_groups[] = { + &rtc_attr_group, + NULL +}; + +const struct attribute_group **rtc_get_dev_attribute_groups(void) +{ + return rtc_attr_groups; +} + +int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps) +{ + size_t old_cnt = 0, add_cnt = 0, new_cnt; + const struct attribute_group **groups, **old; + + if (rtc->registered) + return -EINVAL; + if (!grps) + return -EINVAL; + + groups = rtc->dev.groups; + if (groups) + for (; *groups; groups++) + old_cnt++; + + for (groups = grps; *groups; groups++) + add_cnt++; + + new_cnt = old_cnt + add_cnt + 1; + groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups)); + memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups)); + groups[old_cnt + add_cnt] = NULL; + + old = rtc->dev.groups; + rtc->dev.groups = groups; + if (old && old != rtc_attr_groups) + devm_kfree(&rtc->dev, old); + + return 0; +} +EXPORT_SYMBOL(rtc_add_groups); + +int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp) +{ + const struct attribute_group *groups[] = { grp, NULL }; + + return rtc_add_groups(rtc, groups); +} +EXPORT_SYMBOL(rtc_add_group); diff --git a/drivers/rtc/rtc-tegra.c b/drivers/rtc/rtc-tegra.c new file mode 100644 index 000000000..8dc48fe7f --- /dev/null +++ b/drivers/rtc/rtc-tegra.c @@ -0,0 +1,450 @@ +/* + * 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/clk.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/rtc.h> +#include <linux/slab.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. */ + struct clk *clk; + 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. */ + 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; + } 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 const 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; + + info->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(info->clk)) + return PTR_ERR(info->clk); + + ret = clk_prepare_enable(info->clk); + if (ret < 0) + return ret; + + /* 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); + goto disable_clk; + } + + 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); + goto disable_clk; + } + + dev_notice(&pdev->dev, "Tegra internal Real Time Clock\n"); + + return 0; + +disable_clk: + clk_disable_unprepare(info->clk); + return ret; +} + +static int tegra_rtc_remove(struct platform_device *pdev) +{ + struct tegra_rtc_info *info = platform_get_drvdata(pdev); + + clk_disable_unprepare(info->clk); + + 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 = { + .remove = tegra_rtc_remove, + .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..ade6a8270 --- /dev/null +++ b/drivers/rtc/rtc-test.c @@ -0,0 +1,203 @@ +/* + * 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> + +#define MAX_RTC_TEST 3 + +struct rtc_test_data { + struct rtc_device *rtc; + time64_t offset; + struct timer_list alarm; + bool alarm_en; +}; + +static struct platform_device *pdev[MAX_RTC_TEST]; + +static int test_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + time64_t alarm; + + alarm = (rtd->alarm.expires - jiffies) / HZ; + alarm += ktime_get_real_seconds() + rtd->offset; + + rtc_time64_to_tm(alarm, &alrm->time); + alrm->enabled = rtd->alarm_en; + + return 0; +} + +static int test_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + ktime_t timeout; + u64 expires; + + timeout = rtc_tm_to_time64(&alrm->time) - ktime_get_real_seconds(); + timeout -= rtd->offset; + + del_timer(&rtd->alarm); + + expires = jiffies + timeout * HZ; + if (expires > U32_MAX) + expires = U32_MAX; + + pr_err("ABE: %s +%d %s\n", __FILE__, __LINE__, __func__); + rtd->alarm.expires = expires; + + if (alrm->enabled) + add_timer(&rtd->alarm); + + rtd->alarm_en = alrm->enabled; + + return 0; +} + +static int test_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtc_time64_to_tm(ktime_get_real_seconds() + rtd->offset, tm); + + return 0; +} + +static int test_rtc_set_mmss64(struct device *dev, time64_t secs) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtd->offset = secs - ktime_get_real_seconds(); + + return 0; +} + +static int test_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) +{ + struct rtc_test_data *rtd = dev_get_drvdata(dev); + + rtd->alarm_en = enable; + if (enable) + add_timer(&rtd->alarm); + else + del_timer(&rtd->alarm); + + return 0; +} + +static const struct rtc_class_ops test_rtc_ops_noalm = { + .read_time = test_rtc_read_time, + .set_mmss64 = test_rtc_set_mmss64, + .alarm_irq_enable = test_rtc_alarm_irq_enable, +}; + +static const struct rtc_class_ops test_rtc_ops = { + .read_time = test_rtc_read_time, + .read_alarm = test_rtc_read_alarm, + .set_alarm = test_rtc_set_alarm, + .set_mmss64 = test_rtc_set_mmss64, + .alarm_irq_enable = test_rtc_alarm_irq_enable, +}; + +static void test_rtc_alarm_handler(struct timer_list *t) +{ + struct rtc_test_data *rtd = from_timer(rtd, t, alarm); + + rtc_update_irq(rtd->rtc, 1, RTC_AF | RTC_IRQF); +} + +static int test_probe(struct platform_device *plat_dev) +{ + struct rtc_test_data *rtd; + + rtd = devm_kzalloc(&plat_dev->dev, sizeof(*rtd), GFP_KERNEL); + if (!rtd) + return -ENOMEM; + + platform_set_drvdata(plat_dev, rtd); + + rtd->rtc = devm_rtc_allocate_device(&plat_dev->dev); + if (IS_ERR(rtd->rtc)) + return PTR_ERR(rtd->rtc); + + switch (plat_dev->id) { + case 0: + rtd->rtc->ops = &test_rtc_ops_noalm; + break; + default: + rtd->rtc->ops = &test_rtc_ops; + } + + timer_setup(&rtd->alarm, test_rtc_alarm_handler, 0); + rtd->alarm.expires = 0; + + return rtc_register_device(rtd->rtc); +} + +static struct platform_driver test_driver = { + .probe = test_probe, + .driver = { + .name = "rtc-test", + }, +}; + +static int __init test_init(void) +{ + int i, err; + + if ((err = platform_driver_register(&test_driver))) + return err; + + err = -ENOMEM; + for (i = 0; i < MAX_RTC_TEST; i++) { + pdev[i] = platform_device_alloc("rtc-test", i); + if (!pdev[i]) + goto exit_free_mem; + } + + for (i = 0; i < MAX_RTC_TEST; i++) { + err = platform_device_add(pdev[i]); + if (err) + goto exit_device_del; + } + + return 0; + +exit_device_del: + for (; i > 0; i--) + platform_device_del(pdev[i - 1]); + +exit_free_mem: + for (i = 0; i < MAX_RTC_TEST; i++) + platform_device_put(pdev[i]); + + platform_driver_unregister(&test_driver); + return err; +} + +static void __exit test_exit(void) +{ + int i; + + for (i = 0; i < MAX_RTC_TEST; i++) + platform_device_unregister(pdev[i]); + + 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-tps6586x.c b/drivers/rtc/rtc-tps6586x.c new file mode 100644 index 000000000..d6434e514 --- /dev/null +++ b/drivers/rtc/rtc-tps6586x.c @@ -0,0 +1,341 @@ +/* + * 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; +}; + +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 device *tps_dev = to_tps6586x_dev(dev); + unsigned long long ticks = 0; + time64_t 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; + rtc_time64_to_tm(seconds, tm); + + return 0; +} + +static int tps6586x_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct device *tps_dev = to_tps6586x_dev(dev); + unsigned long long ticks; + time64_t seconds; + u8 buff[5]; + int ret; + + seconds = rtc_tm_to_time64(tm); + + 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 device *tps_dev = to_tps6586x_dev(dev); + time64_t seconds; + unsigned long ticks; + unsigned long rtc_current_time; + unsigned long long rticks = 0; + u8 buff[3]; + u8 rbuff[6]; + int ret; + int i; + + seconds = rtc_tm_to_time64(&alrm->time); + + 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; + } + + 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 device *tps_dev = to_tps6586x_dev(dev); + unsigned long ticks; + time64_t 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; + + rtc_time64_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); + + /* 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_allocate_device(&pdev->dev); + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + dev_err(&pdev->dev, "RTC allocate device: ret %d\n", ret); + goto fail_rtc_register; + } + + rtc->rtc->ops = &tps6586x_rtc_ops; + rtc->rtc->range_max = (1ULL << 30) - 1; /* 30-bit seconds */ + rtc->rtc->start_secs = mktime64(2009, 1, 1, 0, 0, 0); + rtc->rtc->set_start_time = true; + + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL, + tps6586x_rtc_irq, + IRQF_ONESHOT, + 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); + + ret = rtc_register_device(rtc->rtc); + if (ret) { + dev_err(&pdev->dev, "RTC device register: ret %d\n", ret); + goto fail_rtc_register; + } + + 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:tps6586x-rtc"); +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..9f10af7ac --- /dev/null +++ b/drivers/rtc/rtc-tps65910.c @@ -0,0 +1,475 @@ +/* + * 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/math64.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) + +/* Total number of RTC registers needed to set compensation registers */ +#define NUM_COMP_REGS (TPS65910_RTC_COMP_MSB - TPS65910_RTC_COMP_LSB + 1) + +/* Min and max values supported with 'offset' interface (swapped sign) */ +#define MIN_OFFSET (-277761) +#define MAX_OFFSET (277778) + +/* Number of ticks per hour */ +#define TICKS_PER_HOUR (32768 * 3600) + +/* Multiplier for ppb conversions */ +#define PPB_MULT (1000000000LL) + +static int tps65910_rtc_alarm_irq_enable(struct device *dev, + unsigned int 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 int tps65910_rtc_set_calibration(struct device *dev, int calibration) +{ + unsigned char comp_data[NUM_COMP_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + s16 value; + int ret; + + /* + * TPS65910 uses two's complement 16 bit value for compensation for RTC + * crystal inaccuracies. One time every hour when seconds counter + * increments from 0 to 1 compensation value will be added to internal + * RTC counter value. + * + * Compensation value 0x7FFF is prohibited value. + * + * Valid range for compensation value: [-32768 .. 32766] + */ + if ((calibration < -32768) || (calibration > 32766)) { + dev_err(dev, "RTC calibration value out of range: %d\n", + calibration); + return -EINVAL; + } + + value = (s16)calibration; + + comp_data[0] = (u16)value & 0xFF; + comp_data[1] = ((u16)value >> 8) & 0xFF; + + /* Update all the compensation registers in one shot */ + ret = regmap_bulk_write(tps->regmap, TPS65910_RTC_COMP_LSB, + comp_data, NUM_COMP_REGS); + if (ret < 0) { + dev_err(dev, "rtc_set_calibration error: %d\n", ret); + return ret; + } + + /* Enable automatic compensation */ + ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL, + TPS65910_RTC_CTRL_AUTO_COMP, TPS65910_RTC_CTRL_AUTO_COMP); + if (ret < 0) + dev_err(dev, "auto_comp enable failed with error: %d\n", ret); + + return ret; +} + +static int tps65910_rtc_get_calibration(struct device *dev, int *calibration) +{ + unsigned char comp_data[NUM_COMP_REGS]; + struct tps65910 *tps = dev_get_drvdata(dev->parent); + unsigned int ctrl; + u16 value; + int ret; + + ret = regmap_read(tps->regmap, TPS65910_RTC_CTRL, &ctrl); + if (ret < 0) + return ret; + + /* If automatic compensation is not enabled report back zero */ + if (!(ctrl & TPS65910_RTC_CTRL_AUTO_COMP)) { + *calibration = 0; + return 0; + } + + ret = regmap_bulk_read(tps->regmap, TPS65910_RTC_COMP_LSB, comp_data, + NUM_COMP_REGS); + if (ret < 0) { + dev_err(dev, "rtc_get_calibration error: %d\n", ret); + return ret; + } + + value = (u16)comp_data[0] | ((u16)comp_data[1] << 8); + + *calibration = (s16)value; + + return 0; +} + +static int tps65910_read_offset(struct device *dev, long *offset) +{ + int calibration; + s64 tmp; + int ret; + + ret = tps65910_rtc_get_calibration(dev, &calibration); + if (ret < 0) + return ret; + + /* Convert from RTC calibration register format to ppb format */ + tmp = calibration * (s64)PPB_MULT; + if (tmp < 0) + tmp -= TICKS_PER_HOUR / 2LL; + else + tmp += TICKS_PER_HOUR / 2LL; + tmp = div_s64(tmp, TICKS_PER_HOUR); + + /* Offset value operates in negative way, so swap sign */ + *offset = (long)-tmp; + + return 0; +} + +static int tps65910_set_offset(struct device *dev, long offset) +{ + int calibration; + s64 tmp; + int ret; + + /* Make sure offset value is within supported range */ + if (offset < MIN_OFFSET || offset > MAX_OFFSET) + return -ERANGE; + + /* Convert from ppb format to RTC calibration register format */ + tmp = offset * (s64)TICKS_PER_HOUR; + if (tmp < 0) + tmp -= PPB_MULT / 2LL; + else + tmp += PPB_MULT / 2LL; + tmp = div_s64(tmp, PPB_MULT); + + /* Offset value operates in negative way, so swap sign */ + calibration = (int)-tmp; + + ret = tps65910_rtc_set_calibration(dev, calibration); + + 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, + .read_offset = tps65910_read_offset, + .set_offset = tps65910_set_offset, +}; + +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; + + tps_rtc->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(tps_rtc->rtc)) + return PTR_ERR(tps_rtc->rtc); + + /* 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, + 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->ops = &tps65910_rtc_ops; + tps_rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + tps_rtc->rtc->range_max = RTC_TIMESTAMP_END_2099; + + ret = rtc_register_device(tps_rtc->rtc); + if (ret) { + dev_err(&pdev->dev, "RTC device register: err %d\n", ret); + return ret; + } + + 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, + .driver = { + .name = "tps65910-rtc", + .pm = &tps65910_rtc_pm_ops, + }, +}; + +module_platform_driver(tps65910_rtc_driver); +MODULE_ALIAS("platform:tps65910-rtc"); +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..737f26eb2 --- /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, + 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..3472e79f2 --- /dev/null +++ b/drivers/rtc/rtc-twl.c @@ -0,0 +1,661 @@ +/* + * 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/mfd/twl.h> + +enum twl_class { + TWL_4030 = 0, + TWL_6030, +}; + +/* + * 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 + +/*----------------------------------------------------------------------*/ +struct twl_rtc { + struct device *dev; + struct rtc_device *rtc; + u8 *reg_map; + /* + * Cache the value for timer/alarm interrupts register; this is + * only changed by callers holding rtc ops lock (or resume). + */ + unsigned char rtc_irq_bits; + bool wake_enabled; +#ifdef CONFIG_PM_SLEEP + unsigned char irqstat; +#endif + enum twl_class class; +}; + +/* + * Supports 1 byte read from TWL RTC register. + */ +static int twl_rtc_read_u8(struct twl_rtc *twl_rtc, u8 *data, u8 reg) +{ + int ret; + + ret = twl_i2c_read_u8(TWL_MODULE_RTC, data, (twl_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(struct twl_rtc *twl_rtc, u8 data, u8 reg) +{ + int ret; + + ret = twl_i2c_write_u8(TWL_MODULE_RTC, data, (twl_rtc->reg_map[reg])); + if (ret < 0) + pr_err("Could not write TWL register %X - error %d\n", + reg, ret); + return ret; +} + +/* + * Enable 1/second update and/or alarm interrupts. + */ +static int set_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) +{ + unsigned char val; + int ret; + + /* if the bit is set, return from here */ + if (twl_rtc->rtc_irq_bits & bit) + return 0; + + val = twl_rtc->rtc_irq_bits | bit; + val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M; + ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); + if (ret == 0) + twl_rtc->rtc_irq_bits = val; + + return ret; +} + +/* + * Disable update and/or alarm interrupts. + */ +static int mask_rtc_irq_bit(struct twl_rtc *twl_rtc, unsigned char bit) +{ + unsigned char val; + int ret; + + /* if the bit is clear, return from here */ + if (!(twl_rtc->rtc_irq_bits & bit)) + return 0; + + val = twl_rtc->rtc_irq_bits & ~bit; + ret = twl_rtc_write_u8(twl_rtc, val, REG_RTC_INTERRUPTS_REG); + if (ret == 0) + twl_rtc->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); + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + int irq = platform_get_irq(pdev, 0); + int ret; + + if (enabled) { + ret = set_rtc_irq_bit(twl_rtc, + 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(twl_rtc, + 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) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + unsigned char rtc_data[ALL_TIME_REGS]; + int ret; + u8 save_control; + u8 rtc_control; + + ret = twl_rtc_read_u8(twl_rtc, &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_rtc->class == TWL_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(twl_rtc, 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_rtc->class == TWL_6030) + rtc_control |= BIT_RTC_CTRL_REG_RTC_V_OPT; + + ret = twl_rtc_write_u8(twl_rtc, 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, + (twl_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_rtc->class == TWL_6030) { + ret = twl_rtc_write_u8(twl_rtc, 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) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + 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(twl_rtc, &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(twl_rtc, 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, + (twl_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(twl_rtc, 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) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + unsigned char rtc_data[ALL_TIME_REGS]; + int ret; + + ret = twl_i2c_read(TWL_MODULE_RTC, rtc_data, + twl_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 (twl_rtc->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) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + 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, + twl_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 *data) +{ + struct twl_rtc *twl_rtc = data; + unsigned long events; + int ret = IRQ_NONE; + int res; + u8 rd_reg; + + res = twl_rtc_read_u8(twl_rtc, &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(twl_rtc, BIT_RTC_STATUS_REG_ALARM_M, + REG_RTC_STATUS_REG); + if (res) + goto out; + + if (twl_rtc->class == TWL_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(twl_rtc->rtc, 1, events); + + ret = IRQ_HANDLED; +out: + return ret; +} + +static const 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 twl_rtc *twl_rtc; + struct device_node *np = pdev->dev.of_node; + int ret = -EINVAL; + int irq = platform_get_irq(pdev, 0); + u8 rd_reg; + + if (!np) { + dev_err(&pdev->dev, "no DT info\n"); + return -EINVAL; + } + + if (irq <= 0) + return ret; + + twl_rtc = devm_kzalloc(&pdev->dev, sizeof(*twl_rtc), GFP_KERNEL); + if (!twl_rtc) + return -ENOMEM; + + if (twl_class_is_4030()) { + twl_rtc->class = TWL_4030; + twl_rtc->reg_map = (u8 *)twl4030_rtc_reg_map; + } else if (twl_class_is_6030()) { + twl_rtc->class = TWL_6030; + twl_rtc->reg_map = (u8 *)twl6030_rtc_reg_map; + } else { + dev_err(&pdev->dev, "TWL Class not supported.\n"); + return -EINVAL; + } + + ret = twl_rtc_read_u8(twl_rtc, &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(twl_rtc, rd_reg, REG_RTC_STATUS_REG); + if (ret < 0) + return ret; + + if (twl_rtc->class == TWL_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(twl_rtc, 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(twl_rtc, 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(twl_rtc, &twl_rtc->rtc_irq_bits, + REG_RTC_INTERRUPTS_REG); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, twl_rtc); + device_init_wakeup(&pdev->dev, 1); + + twl_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &twl_rtc_ops, THIS_MODULE); + if (IS_ERR(twl_rtc->rtc)) { + dev_err(&pdev->dev, "can't register RTC device, err %ld\n", + PTR_ERR(twl_rtc->rtc)); + return PTR_ERR(twl_rtc->rtc); + } + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + twl_rtc_interrupt, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + dev_name(&twl_rtc->rtc->dev), twl_rtc); + if (ret < 0) { + dev_err(&pdev->dev, "IRQ is not free.\n"); + return ret; + } + + return 0; +} + +/* + * Disable all TWL RTC module interrupts. + * Sets status flag to free. + */ +static int twl_rtc_remove(struct platform_device *pdev) +{ + struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); + + /* leave rtc running, but disable irqs */ + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_ALARM_M); + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + if (twl_rtc->class == TWL_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) +{ + struct twl_rtc *twl_rtc = platform_get_drvdata(pdev); + + /* mask timer interrupts, but leave alarm interrupts on to enable + power-on when alarm is triggered */ + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); +} + +#ifdef CONFIG_PM_SLEEP +static int twl_rtc_suspend(struct device *dev) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + twl_rtc->irqstat = twl_rtc->rtc_irq_bits; + + mask_rtc_irq_bit(twl_rtc, BIT_RTC_INTERRUPTS_REG_IT_TIMER_M); + return 0; +} + +static int twl_rtc_resume(struct device *dev) +{ + struct twl_rtc *twl_rtc = dev_get_drvdata(dev); + + set_rtc_irq_bit(twl_rtc, twl_rtc->irqstat); + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(twl_rtc_pm_ops, twl_rtc_suspend, twl_rtc_resume); + +static const struct of_device_id twl_rtc_of_match[] = { + {.compatible = "ti,twl4030-rtc", }, + { }, +}; +MODULE_DEVICE_TABLE(of, twl_rtc_of_match); + +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 = 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..61c110b20 --- /dev/null +++ b/drivers/rtc/rtc-tx4939.c @@ -0,0 +1,319 @@ +/* + * 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> + +#define TX4939_RTCCTL_ALME 0x00000080 +#define TX4939_RTCCTL_ALMD 0x00000040 +#define TX4939_RTCCTL_BUSY 0x00000020 + +#define TX4939_RTCCTL_COMMAND 0x00000007 +#define TX4939_RTCCTL_COMMAND_NOP 0x00000000 +#define TX4939_RTCCTL_COMMAND_GETTIME 0x00000001 +#define TX4939_RTCCTL_COMMAND_SETTIME 0x00000002 +#define TX4939_RTCCTL_COMMAND_GETALARM 0x00000003 +#define TX4939_RTCCTL_COMMAND_SETALARM 0x00000004 + +#define TX4939_RTCTBC_PM 0x00000080 +#define TX4939_RTCTBC_COMP 0x0000007f + +#define TX4939_RTC_REG_RAMSIZE 0x00000100 +#define TX4939_RTC_REG_RWBSIZE 0x00000006 + +struct tx4939_rtc_reg { + __u32 ctl; + __u32 adr; + __u32 dat; + __u32 tbc; +}; + +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 = ((unsigned long)buf[5] << 24) | (buf[4] << 16) | + (buf[3] << 8) | buf[2]; + rtc_time_to_tm(sec, tm); + return 0; +} + +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 = ((unsigned long)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 int tx4939_nvram_read(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct tx4939rtc_plat_data *pdata = priv; + struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg; + u8 *buf = val; + + spin_lock_irq(&pdata->lock); + for (; bytes; bytes--) { + __raw_writel(pos++, &rtcreg->adr); + *buf++ = __raw_readl(&rtcreg->dat); + } + spin_unlock_irq(&pdata->lock); + return 0; +} + +static int tx4939_nvram_write(void *priv, unsigned int pos, void *val, + size_t bytes) +{ + struct tx4939rtc_plat_data *pdata = priv; + struct tx4939_rtc_reg __iomem *rtcreg = pdata->rtcreg; + u8 *buf = val; + + spin_lock_irq(&pdata->lock); + for (; bytes; bytes--) { + __raw_writel(pos++, &rtcreg->adr); + __raw_writel(*buf++, &rtcreg->dat); + } + spin_unlock_irq(&pdata->lock); + return 0; +} + +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; + struct nvmem_config nvmem_cfg = { + .name = "tx4939_nvram", + .size = TX4939_RTC_REG_RAMSIZE, + .reg_read = tx4939_nvram_read, + .reg_write = tx4939_nvram_write, + }; + + 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_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + rtc->ops = &tx4939_rtc_ops; + rtc->nvram_old_abi = true; + + pdata->rtc = rtc; + + nvmem_cfg.priv = pdata; + ret = rtc_nvmem_register(rtc, &nvmem_cfg); + if (ret) + return ret; + + return rtc_register_device(rtc); +} + +static int __exit tx4939_rtc_remove(struct platform_device *pdev) +{ + struct tx4939rtc_plat_data *pdata = platform_get_drvdata(pdev); + + 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..1f3117b5a --- /dev/null +++ b/drivers/rtc/rtc-v3020.c @@ -0,0 +1,374 @@ +/* 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/platform_data/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 { + /* MMIO access */ + void __iomem *ioaddress; + int leftshift; + + /* GPIO access */ + struct gpio *gpio; + + const 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 const 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 gpio v3020_gpio[] = { + { 0, GPIOF_OUT_INIT_HIGH, "RTC CS"}, + { 0, GPIOF_OUT_INIT_HIGH, "RTC WR"}, + { 0, GPIOF_OUT_INIT_HIGH, "RTC RD"}, + { 0, GPIOF_OUT_INIT_HIGH, "RTC IO"}, +}; + +static int v3020_gpio_map(struct v3020 *chip, struct platform_device *pdev, + struct v3020_platform_data *pdata) +{ + int 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; + + err = gpio_request_array(v3020_gpio, ARRAY_SIZE(v3020_gpio)); + + if (!err) + chip->gpio = v3020_gpio; + + return err; +} + +static void v3020_gpio_unmap(struct v3020 *chip) +{ + gpio_free_array(v3020_gpio, ARRAY_SIZE(v3020_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 const 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..70f013e69 --- /dev/null +++ b/drivers/rtc/rtc-vr41xx.c @@ -0,0 +1,371 @@ +/* + * 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 time64_t 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 ((u64)first_high << 17) | (first_mid << 1) | (first_low >> 15); +} + +static inline void write_elapsed_second(time64_t 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 int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time) +{ + time64_t epoch_sec, elapsed_sec; + + epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0); + elapsed_sec = read_elapsed_second(); + + rtc_time64_to_tm(epoch_sec + elapsed_sec, time); + + return 0; +} + +static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time) +{ + time64_t epoch_sec, current_sec; + + epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0); + current_sec = mktime64(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) +{ + time64_t alarm_sec; + struct rtc_time *time = &wkalrm->time; + + alarm_sec = mktime64(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 = { + .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, + .alarm_irq_enable = vr41xx_rtc_alarm_irq_enable, +}; + +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_allocate_device(&pdev->dev); + if (IS_ERR(rtc)) { + retval = PTR_ERR(rtc); + goto err_iounmap_all; + } + + rtc->ops = &vr41xx_rtc_ops; + + /* 48-bit counter at 32.768 kHz */ + rtc->range_max = (1ULL << 33) - 1; + 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"); + + retval = rtc_register_device(rtc); + if (retval) + goto err_iounmap_all; + + 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..27e896995 --- /dev/null +++ b/drivers/rtc/rtc-vt8500.c @@ -0,0 +1,290 @@ +/* + * 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", }, + {} +}; +MODULE_DEVICE_TABLE(of, wmt_dt_ids); + +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..7b824dabf --- /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(®, 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 0; + } + + } 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..ed874e5c5 --- /dev/null +++ b/drivers/rtc/rtc-wm8350.c @@ -0,0 +1,490 @@ +/* + * 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 + +/* + * 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; + } + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, + wm8350_rtc_update_handler, 0, + "RTC Seconds", wm8350); + if (ret) + return ret; + + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + + ret = wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM, + wm8350_rtc_alarm_handler, 0, + "RTC Alarm", wm8350); + if (ret) { + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC, wm8350); + return ret; + } + + 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..f08f18e4f --- /dev/null +++ b/drivers/rtc/rtc-x1205.c @@ -0,0 +1,691 @@ +/* + * 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> + +/* 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 = ® + }, + }; + + 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; + + 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"); diff --git a/drivers/rtc/rtc-xgene.c b/drivers/rtc/rtc-xgene.c new file mode 100644 index 000000000..2f741f455 --- /dev/null +++ b/drivers/rtc/rtc-xgene.c @@ -0,0 +1,302 @@ +/* + * 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; + unsigned int irq_enabled; +}; + +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 0; +} + +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_alarm_irq_enabled(struct device *dev) +{ + struct xgene_rtc_dev *pdata = dev_get_drvdata(dev); + + return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 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 alarm_time; + + 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); + + pdata->rtc = devm_rtc_allocate_device(&pdev->dev); + if (IS_ERR(pdata->rtc)) + return PTR_ERR(pdata->rtc); + + 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; + } + ret = clk_prepare_enable(pdata->clk); + if (ret) + return ret; + + /* Turn on the clock and the crystal */ + writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR); + + ret = device_init_wakeup(&pdev->dev, 1); + if (ret) { + clk_disable_unprepare(pdata->clk); + return ret; + } + + /* HW does not support update faster than 1 seconds */ + pdata->rtc->uie_unsupported = 1; + pdata->rtc->ops = &xgene_rtc_ops; + + ret = rtc_register_device(pdata->rtc); + if (ret) { + clk_disable_unprepare(pdata->clk); + return ret; + } + + 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; +} + +static int __maybe_unused 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 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(&pdev->dev)) { + if (!enable_irq_wake(irq)) + pdata->irq_wake = 1; + } else { + pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev); + xgene_rtc_alarm_irq_enable(dev, 0); + clk_disable_unprepare(pdata->clk); + } + return 0; +} + +static int __maybe_unused 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; + int rc; + + 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 { + rc = clk_prepare_enable(pdata->clk); + if (rc) { + dev_err(dev, "Unable to enable clock error %d\n", rc); + return rc; + } + xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled); + } + + return 0; +} + +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/rtc-zynqmp.c b/drivers/rtc/rtc-zynqmp.c new file mode 100644 index 000000000..c532bd13f --- /dev/null +++ b/drivers/rtc/rtc-zynqmp.c @@ -0,0 +1,325 @@ +/* + * Xilinx Zynq Ultrascale+ MPSoC Real Time Clock Driver + * + * Copyright (C) 2015 Xilinx, Inc. + * + * 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/delay.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/rtc.h> + +/* RTC Registers */ +#define RTC_SET_TM_WR 0x00 +#define RTC_SET_TM_RD 0x04 +#define RTC_CALIB_WR 0x08 +#define RTC_CALIB_RD 0x0C +#define RTC_CUR_TM 0x10 +#define RTC_CUR_TICK 0x14 +#define RTC_ALRM 0x18 +#define RTC_INT_STS 0x20 +#define RTC_INT_MASK 0x24 +#define RTC_INT_EN 0x28 +#define RTC_INT_DIS 0x2C +#define RTC_CTRL 0x40 + +#define RTC_FR_EN BIT(20) +#define RTC_FR_DATSHIFT 16 +#define RTC_TICK_MASK 0xFFFF +#define RTC_INT_SEC BIT(0) +#define RTC_INT_ALRM BIT(1) +#define RTC_OSC_EN BIT(24) +#define RTC_BATT_EN BIT(31) + +#define RTC_CALIB_DEF 0x198233 +#define RTC_CALIB_MASK 0x1FFFFF +#define RTC_SEC_MAX_VAL 0xFFFFFFFF + +struct xlnx_rtc_dev { + struct rtc_device *rtc; + void __iomem *reg_base; + int alarm_irq; + int sec_irq; + int calibval; +}; + +static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long new_time; + + /* + * The value written will be updated after 1 sec into the + * seconds read register, so we need to program time +1 sec + * to get the correct time on read. + */ + new_time = rtc_tm_to_time64(tm) + 1; + + if (new_time > RTC_SEC_MAX_VAL) + return -EINVAL; + + /* + * Writing into calibration register will clear the Tick Counter and + * force the next second to be signaled exactly in 1 second period + */ + xrtcdev->calibval &= RTC_CALIB_MASK; + writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); + + writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR); + + /* + * Clear the rtc interrupt status register after setting the + * time. During a read_time function, the code should read the + * RTC_INT_STATUS register and if bit 0 is still 0, it means + * that one second has not elapsed yet since RTC was set and + * the current time should be read from SET_TIME_READ register; + * otherwise, CURRENT_TIME register is read to report the time + */ + writel(RTC_INT_SEC, xrtcdev->reg_base + RTC_INT_STS); + + return 0; +} + +static int xlnx_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + u32 status; + unsigned long read_time; + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + status = readl(xrtcdev->reg_base + RTC_INT_STS); + + if (status & RTC_INT_SEC) { + /* + * RTC has updated the CURRENT_TIME with the time written into + * SET_TIME_WRITE register. + */ + rtc_time64_to_tm(readl(xrtcdev->reg_base + RTC_CUR_TM), tm); + } else { + /* + * Time written in SET_TIME_WRITE has not yet updated into + * the seconds read register, so read the time from the + * SET_TIME_WRITE instead of CURRENT_TIME register. + * Since we add +1 sec while writing, we need to -1 sec while + * reading. + */ + read_time = readl(xrtcdev->reg_base + RTC_SET_TM_RD) - 1; + rtc_time64_to_tm(read_time, tm); + } + + return 0; +} + +static int xlnx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + rtc_time64_to_tm(readl(xrtcdev->reg_base + RTC_ALRM), &alrm->time); + alrm->enabled = readl(xrtcdev->reg_base + RTC_INT_MASK) & RTC_INT_ALRM; + + return 0; +} + +static int xlnx_rtc_alarm_irq_enable(struct device *dev, u32 enabled) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + if (enabled) + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN); + else + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_DIS); + + return 0; +} + +static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + unsigned long alarm_time; + + alarm_time = rtc_tm_to_time64(&alrm->time); + + if (alarm_time > RTC_SEC_MAX_VAL) + return -EINVAL; + + writel((u32)alarm_time, (xrtcdev->reg_base + RTC_ALRM)); + + xlnx_rtc_alarm_irq_enable(dev, alrm->enabled); + + return 0; +} + +static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev) +{ + u32 rtc_ctrl; + + /* Enable RTC switch to battery when VCC_PSAUX is not available */ + rtc_ctrl = readl(xrtcdev->reg_base + RTC_CTRL); + rtc_ctrl |= RTC_BATT_EN; + writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL); + + /* + * Based on crystal freq of 33.330 KHz + * set the seconds counter and enable, set fractions counter + * to default value suggested as per design spec + * to correct RTC delay in frequency over period of time. + */ + xrtcdev->calibval &= RTC_CALIB_MASK; + writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); +} + +static const struct rtc_class_ops xlnx_rtc_ops = { + .set_time = xlnx_rtc_set_time, + .read_time = xlnx_rtc_read_time, + .read_alarm = xlnx_rtc_read_alarm, + .set_alarm = xlnx_rtc_set_alarm, + .alarm_irq_enable = xlnx_rtc_alarm_irq_enable, +}; + +static irqreturn_t xlnx_rtc_interrupt(int irq, void *id) +{ + struct xlnx_rtc_dev *xrtcdev = (struct xlnx_rtc_dev *)id; + unsigned int status; + + status = readl(xrtcdev->reg_base + RTC_INT_STS); + /* Check if interrupt asserted */ + if (!(status & (RTC_INT_SEC | RTC_INT_ALRM))) + return IRQ_NONE; + + /* Clear RTC_INT_ALRM interrupt only */ + writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_STS); + + if (status & RTC_INT_ALRM) + rtc_update_irq(xrtcdev->rtc, 1, RTC_IRQF | RTC_AF); + + return IRQ_HANDLED; +} + +static int xlnx_rtc_probe(struct platform_device *pdev) +{ + struct xlnx_rtc_dev *xrtcdev; + struct resource *res; + int ret; + + xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev), GFP_KERNEL); + if (!xrtcdev) + return -ENOMEM; + + platform_set_drvdata(pdev, xrtcdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + xrtcdev->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(xrtcdev->reg_base)) + return PTR_ERR(xrtcdev->reg_base); + + xrtcdev->alarm_irq = platform_get_irq_byname(pdev, "alarm"); + if (xrtcdev->alarm_irq < 0) { + dev_err(&pdev->dev, "no irq resource\n"); + return xrtcdev->alarm_irq; + } + ret = devm_request_irq(&pdev->dev, xrtcdev->alarm_irq, + xlnx_rtc_interrupt, 0, + dev_name(&pdev->dev), xrtcdev); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + return ret; + } + + xrtcdev->sec_irq = platform_get_irq_byname(pdev, "sec"); + if (xrtcdev->sec_irq < 0) { + dev_err(&pdev->dev, "no irq resource\n"); + return xrtcdev->sec_irq; + } + ret = devm_request_irq(&pdev->dev, xrtcdev->sec_irq, + xlnx_rtc_interrupt, 0, + dev_name(&pdev->dev), xrtcdev); + if (ret) { + dev_err(&pdev->dev, "request irq failed\n"); + return ret; + } + + ret = of_property_read_u32(pdev->dev.of_node, "calibration", + &xrtcdev->calibval); + if (ret) + xrtcdev->calibval = RTC_CALIB_DEF; + + xlnx_init_rtc(xrtcdev); + + device_init_wakeup(&pdev->dev, 1); + + xrtcdev->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, + &xlnx_rtc_ops, THIS_MODULE); + return PTR_ERR_OR_ZERO(xrtcdev->rtc); +} + +static int xlnx_rtc_remove(struct platform_device *pdev) +{ + xlnx_rtc_alarm_irq_enable(&pdev->dev, 0); + device_init_wakeup(&pdev->dev, 0); + + return 0; +} + +static int __maybe_unused xlnx_rtc_suspend(struct device *dev) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(xrtcdev->alarm_irq); + else + xlnx_rtc_alarm_irq_enable(dev, 0); + + return 0; +} + +static int __maybe_unused xlnx_rtc_resume(struct device *dev) +{ + struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(xrtcdev->alarm_irq); + else + xlnx_rtc_alarm_irq_enable(dev, 1); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(xlnx_rtc_pm_ops, xlnx_rtc_suspend, xlnx_rtc_resume); + +static const struct of_device_id xlnx_rtc_of_match[] = { + {.compatible = "xlnx,zynqmp-rtc" }, + { } +}; +MODULE_DEVICE_TABLE(of, xlnx_rtc_of_match); + +static struct platform_driver xlnx_rtc_driver = { + .probe = xlnx_rtc_probe, + .remove = xlnx_rtc_remove, + .driver = { + .name = KBUILD_MODNAME, + .pm = &xlnx_rtc_pm_ops, + .of_match_table = xlnx_rtc_of_match, + }, +}; + +module_platform_driver(xlnx_rtc_driver); + +MODULE_DESCRIPTION("Xilinx Zynq MPSoC RTC driver"); +MODULE_AUTHOR("Xilinx Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c new file mode 100644 index 000000000..718293d72 --- /dev/null +++ b/drivers/rtc/systohc.c @@ -0,0 +1,70 @@ +/* + * 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 + * @target_nsec: pointer for desired now->tv_nsec value + * + * 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. + * + * -EPROTO is returned if now.tv_nsec is not close enough to *target_nsec. + * + * If temporary failure is indicated the caller should try again 'soon' + */ +int rtc_set_ntp_time(struct timespec64 now, unsigned long *target_nsec) +{ + struct rtc_device *rtc; + struct rtc_time tm; + struct timespec64 to_set; + int err = -ENODEV; + bool ok; + + rtc = rtc_class_open(CONFIG_RTC_SYSTOHC_DEVICE); + if (!rtc) + goto out_err; + + if (!rtc->ops || (!rtc->ops->set_time && !rtc->ops->set_mmss64 && + !rtc->ops->set_mmss)) + goto out_close; + + /* Compute the value of tv_nsec we require the caller to supply in + * now.tv_nsec. This is the value such that (now + + * set_offset_nsec).tv_nsec == 0. + */ + set_normalized_timespec64(&to_set, 0, -rtc->set_offset_nsec); + *target_nsec = to_set.tv_nsec; + + /* The ntp code must call this with the correct value in tv_nsec, if + * it does not we update target_nsec and return EPROTO to make the ntp + * code try again later. + */ + ok = rtc_tv_nsec_ok(rtc->set_offset_nsec, &to_set, &now); + if (!ok) { + err = -EPROTO; + goto out_close; + } + + rtc_time64_to_tm(to_set.tv_sec, &tm); + + /* rtc_hctosys exclusively uses UTC, so we call set_time here, not + * set_mmss. + */ + err = rtc_set_time(rtc, &tm); + +out_close: + rtc_class_close(rtc); +out_err: + return err; +} |