mirror of https://gitee.com/openkylin/linux.git
RTC for 5.0
Subsystem: - new quartz-load-femtofarads DT property for quartz load capacitance - remove rtc_class_ops.read_callback New drivers: - Abracon AB-RTCMC-32.768kHz-EOZ9 - Amlogic Meson RTC - Cadence RTC IP - Microcrystal RV3028 - Whwave sd3078 Drivers: - cmos: ignore bogus century byte - ds1307: rework rx8130 support - isl1208: add isl1209 support, nvmem support - rs5C372: report invalid time when the oscillator stopped - rx8581: add rx8571 support -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEXx9Viay1+e7J/aM4AyWl4gNJNJIFAlyATtkACgkQAyWl4gNJ NJIEOA//eZgauZj9tft1lTCfHDRQoxpcGzmTrLOX3W/s3n6SQgd31YQmTrsoBRbB kjP/qOuSLiZi6bmwBWkLN7fU/eUa433iqxbNpA8tAo0TzrCkm/YBln5ozVmonQ+F Vm4n5NKlTCJSb55Gzil+oEj1/Ly8d10IBLslhuZl23BEPmGCRm4E5SoiwdyAGzX2 APth4X88BtwzmNNjvP6BgoW19aDQDyugHK6xP8vTa2rv34pPrhuBARbBP8WQDkM+ NVqimUH/+i+uj7U5HEKClL0acG20KKNorz3BQeKUFUrwWHFq3IfdkRMBXRAH8OKl gmyktZJmgROdCywD27mW5dmm+kCqVyB8NoxRidADZWPuuITzlZSM6zLOt5mrmKhX WMCWBk1Ol3gJNbIcX4GXrEytiZLeFGonk5FMRTX2U7wWGCA1bZxJPkTgwLUIzUZE PX5G8SIs/3YkL2/0FeRAS16oK1EI1XXUOenVXfdwJqeMeQ8wSGIh9Sp3XDe0v6EZ mtcCTQFi1EJWcVBERUOo7iRoSGbohQWmQeO01J8aUkj7SpTG429csESOeBGYRS24 FEtiPG2DPz1sj0gLQ2yp1H8h4mdtn4uODbewN/TRXIqB9Th6UvR8UbfB9v/qJozY KyCpONuHctGd0CLXa0Gb09t8qxht9wT1ixajTqirsVce5l2WGDU= =/Noj -----END PGP SIGNATURE----- Merge tag 'rtc-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "There is an unusual amount of new drivers this cycle, and this explains the number of insertions. Other than that, the changes are the usual fixes and feature addition. Subsystem updates: - new quartz-load-femtofarads DT property for quartz load capacitance - remove rtc_class_ops.read_callback New drivers: - Abracon AB-RTCMC-32.768kHz-EOZ9 - Amlogic Meson RTC - Cadence RTC IP - Microcrystal RV3028 - Whwave sd3078 Driver updates: - cmos: ignore bogus century byte - ds1307: rework rx8130 support - isl1208: add isl1209 support, nvmem support - rs5C372: report invalid time when the oscillator stopped - rx8581: add rx8571 support" * tag 'rtc-5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (66 commits) rtc: pic32: convert to SPDX identifier rtc: pic32: let the core handle range rtc: pic32: convert to devm_rtc_allocate_device rtc: update my email address rtc: rv8803: convert to SPDX identifier rtc: rv8803: let the core handle range rtc: tx4939: convert to SPDX identifier rtc: tx4939: use .set_time rtc: tx4939: switch to rtc_time64_to_tm/rtc_tm_to_time64 rtc: tx4939: set range rtc: tx4939: remove useless test rtc: zynqmp: let the core handle range rtc: zynqmp: fix possible race condition rtc: imx-sc: use rtc_time64_to_tm rtc: rx8581: Add support for Epson rx8571 RTC dt-bindings: rtc: add rx8571 compatible rtc: pcf85063: remove dead code rtc: remove rtc_class_ops.read_callback rtc: add AB-RTCMC-32.768kHz-EOZ9 RTC support dt-bindings: rtc: add ABEOZ9 ...
This commit is contained in:
commit
f8d35403eb
|
@ -31,6 +31,7 @@ Electricity
|
|||
-microwatt-hours: micro Watt-hours
|
||||
-microvolt : micro volts
|
||||
-picofarads : picofarads
|
||||
-femtofarads : femtofarads
|
||||
|
||||
Temperature
|
||||
----------------------------------------
|
||||
|
|
|
@ -16,6 +16,7 @@ Required properties:
|
|||
"abracon,ab1803"
|
||||
"abracon,ab1804"
|
||||
"abracon,ab1805"
|
||||
"microcrystal,rv1805"
|
||||
Using "abracon,abx80x" will enable chip autodetection.
|
||||
- "reg": I2C bus address of the device
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
Cadence Real Time Clock
|
||||
|
||||
The Cadence RTC controller with date, time and alarm capabilities.
|
||||
The alarm may wake the system from low-power state.
|
||||
|
||||
Required properties:
|
||||
- compatible: Should be "cdns,rtc-r109v3"
|
||||
- reg: Specifies base physical address and size of the register area.
|
||||
- interrupts: A single interrupt specifier.
|
||||
- clocks: Must contain two entries:
|
||||
- pclk: APB registers clock
|
||||
- ref_clk: reference 1Hz or 100Hz clock, depending on IP configuration
|
||||
See ../clocks/clock-bindings.txt for details.
|
||||
|
||||
Example:
|
||||
rtc0: rtc@fd080000 {
|
||||
compatible = "cdns,rtc-r109v3";
|
||||
reg = <0xfd080000 0x1000>;
|
||||
|
||||
clock-names = "pclk", "ref_clk";
|
||||
clocks = <&sysclock>, <&refclock>;
|
||||
|
||||
interrupt-parent = <&gic>;
|
||||
interrupts = <0 6 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
Intersil ISL1209/19 I2C RTC/Alarm chip with event in
|
||||
|
||||
ISL12X9 have additional pins EVIN and #EVDET for tamper detection, while the
|
||||
ISL1208 and ISL1218 do not. They are all use the same driver with the bindings
|
||||
described here, with chip specific properties as noted.
|
||||
|
||||
Required properties supported by the device:
|
||||
- "compatible": Should be one of the following:
|
||||
- "isil,isl1208"
|
||||
- "isil,isl1209"
|
||||
- "isil,isl1218"
|
||||
- "isil,isl1219"
|
||||
- "reg": I2C bus address of the device
|
||||
|
||||
Optional properties:
|
||||
- "interrupt-names": list which may contains "irq" and "evdet"
|
||||
evdet applies to isl1209 and isl1219 only
|
||||
- "interrupts": list of interrupts for "irq" and "evdet"
|
||||
evdet applies to isl1209 and isl1219 only
|
||||
- "isil,ev-evienb": Enable or disable internal pull on EVIN pin
|
||||
Applies to isl1209 and isl1219 only
|
||||
Possible values are 0 and 1
|
||||
Value 0 enables internal pull-up on evin pin, 1 disables it.
|
||||
Default will leave the non-volatile configuration of the pullup
|
||||
as is.
|
||||
|
||||
Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12 and #EVDET pin
|
||||
connected to SoC gpio2 pin 24 and internal pull-up enabled in EVIN pin.
|
||||
|
||||
isl1219: rtc@68 {
|
||||
compatible = "isil,isl1219";
|
||||
reg = <0x68>;
|
||||
interrupt-names = "irq", "evdet";
|
||||
interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>,
|
||||
<&gpio2 24 IRQ_TYPE_EDGE_FALLING>;
|
||||
isil,ev-evienb = <1>;
|
||||
};
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
Intersil ISL1219 I2C RTC/Alarm chip with event in
|
||||
|
||||
ISL1219 has additional pins EVIN and #EVDET for tamper detection.
|
||||
|
||||
Required properties supported by the device:
|
||||
|
||||
- "compatible": must be "isil,isl1219"
|
||||
- "reg": I2C bus address of the device
|
||||
|
||||
Optional properties:
|
||||
|
||||
- "interrupt-names": list which may contains "irq" and "evdet"
|
||||
- "interrupts": list of interrupts for "irq" and "evdet"
|
||||
- "isil,ev-evienb": if present EV.EVIENB bit is set to the specified
|
||||
value for proper operation.
|
||||
|
||||
|
||||
Example isl1219 node with #IRQ pin connected to SoC gpio1 pin12
|
||||
and #EVDET pin connected to SoC gpio2 pin 24:
|
||||
|
||||
isl1219: rtc@68 {
|
||||
compatible = "isil,isl1219";
|
||||
reg = <0x68>;
|
||||
interrupt-names = "irq", "evdet";
|
||||
interrupts-extended = <&gpio1 12 IRQ_TYPE_EDGE_FALLING>,
|
||||
<&gpio2 24 IRQ_TYPE_EDGE_FALLING>;
|
||||
isil,ev-evienb = <1>;
|
||||
};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
* NXP PCF85063 Real Time Clock
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "nxp,pcf85063".
|
||||
- reg: I2C address for chip.
|
||||
|
||||
Optional property:
|
||||
- quartz-load-femtofarads: The capacitive load of the quartz(x-tal),
|
||||
expressed in femto Farad (fF). Valid values are 7000 and 12500.
|
||||
Default value (if no value is specified) is 7000fF.
|
||||
|
||||
Example:
|
||||
|
||||
pcf85063: rtc@51 {
|
||||
compatible = "nxp,pcf85063";
|
||||
reg = <0x51>;
|
||||
quartz-load-femtofarads = <12500>;
|
||||
};
|
|
@ -0,0 +1,18 @@
|
|||
* NXP PCF8523 Real Time Clock
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "nxp,pcf8523".
|
||||
- reg: I2C address for chip.
|
||||
|
||||
Optional property:
|
||||
- quartz-load-femtofarads: The capacitive load of the quartz(x-tal),
|
||||
expressed in femto Farad (fF). Valid values are 7000 and 12500.
|
||||
Default value (if no value is specified) is 12500fF.
|
||||
|
||||
Example:
|
||||
|
||||
pcf8523: rtc@68 {
|
||||
compatible = "nxp,pcf8523";
|
||||
reg = <0x68>;
|
||||
quartz-load-femtofarads = <7000>;
|
||||
};
|
|
@ -0,0 +1,35 @@
|
|||
* Amlogic Meson6, Meson8, Meson8b and Meson8m2 RTC
|
||||
|
||||
Required properties:
|
||||
- compatible: should be one of the following describing the hardware:
|
||||
* "amlogic,meson6-rtc"
|
||||
* "amlogic,meson8-rtc"
|
||||
* "amlogic,meson8b-rtc"
|
||||
* "amlogic,meson8m2-rtc"
|
||||
|
||||
- reg: physical register space for the controller's memory mapped registers.
|
||||
- interrupts: the interrupt line of the RTC block.
|
||||
- clocks: reference to the external 32.768kHz crystal oscillator.
|
||||
- vdd-supply: reference to the power supply of the RTC block.
|
||||
- resets: reset controller reference to allow reset of the controller
|
||||
|
||||
Optional properties for the battery-backed non-volatile memory:
|
||||
- #address-cells: should be 1 to address the battery-backed non-volatile memory
|
||||
- #size-cells: should be 1 to reference the battery-backed non-volatile memory
|
||||
|
||||
Optional child nodes:
|
||||
- see ../nvmem/nvmem.txt
|
||||
|
||||
Example:
|
||||
|
||||
rtc: rtc@740 {
|
||||
compatible = "amlogic,meson6-rtc";
|
||||
reg = <0x740 0x14>;
|
||||
interrupts = <GIC_SPI 72 IRQ_TYPE_EDGE_RISING>;
|
||||
clocks = <&rtc32k_xtal>;
|
||||
vdd-supply = <&rtc_vdd>;
|
||||
resets = <&reset RESET_RTC>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
};
|
|
@ -21,12 +21,16 @@ Optional properties
|
|||
The following properties may not be supported by all drivers. However, if a
|
||||
driver wants to support one of the below features, it should adapt the bindings
|
||||
below.
|
||||
- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given
|
||||
if trickle charger should be enabled
|
||||
- trickle-diode-disable : Do not use internal trickle charger diode Should be
|
||||
given if internal trickle charger diode should be
|
||||
disabled
|
||||
- wakeup-source : Enables wake up of host system on alarm
|
||||
- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given
|
||||
if trickle charger should be enabled
|
||||
- trickle-diode-disable : Do not use internal trickle charger diode Should be
|
||||
given if internal trickle charger diode should be
|
||||
disabled
|
||||
- wakeup-source : Enables wake up of host system on alarm
|
||||
- quartz-load-femtofarads : The capacitive load of the quartz(x-tal),
|
||||
expressed in femto Farad (fF).
|
||||
The default value shall be listed (if optional),
|
||||
and likewise all valid values.
|
||||
|
||||
Trivial RTCs
|
||||
------------
|
||||
|
@ -39,21 +43,23 @@ possibly an interrupt line.
|
|||
Compatible Vendor / Chip
|
||||
========== =============
|
||||
abracon,abb5zes3 AB-RTCMC-32.768kHz-B5ZE-S3: Real Time Clock/Calendar Module with I2C Interface
|
||||
abracon,abeoz9 AB-RTCMC-32.768kHz-EOZ9: Real Time Clock/Calendar Module with I2C Interface
|
||||
dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output
|
||||
dallas,ds1672 Dallas DS1672 Real-time Clock
|
||||
dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM
|
||||
epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
epson,rx8571 I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM
|
||||
epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE
|
||||
emmicro,em3027 EM Microelectronic EM3027 Real-time Clock
|
||||
isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM
|
||||
isil,isl1218 Intersil ISL1218 Low Power RTC with Battery Backed SRAM
|
||||
isil,isl12022 Intersil ISL12022 Real-time Clock
|
||||
microcrystal,rv3028 Real Time Clock Module with I2C-Bus
|
||||
microcrystal,rv3029 Real Time Clock Module with I2C-Bus
|
||||
microcrystal,rv8523 Real Time Clock
|
||||
nxp,pcf2127 Real-time clock
|
||||
nxp,pcf2129 Real-time clock
|
||||
nxp,pcf8523 Real-time Clock
|
||||
nxp,pcf8563 Real-time clock/calendar
|
||||
nxp,pcf85063 Tiny Real-Time Clock
|
||||
pericom,pt7c4338 Real-time Clock Module
|
||||
ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
|
@ -62,3 +68,4 @@ ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
|||
ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
sii,s35390a 2-wire CMOS real-time clock
|
||||
whwave,sd3078 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
|
|
|
@ -439,6 +439,7 @@ vot Vision Optical Technology Co., Ltd.
|
|||
wd Western Digital Corp.
|
||||
wetek WeTek Electronics, limited.
|
||||
wexler Wexler
|
||||
whwave Shenzhen whwave Electronics, Inc.
|
||||
wi2wi Wi2Wi, Inc.
|
||||
winbond Winbond Electronics corp.
|
||||
winstar Winstar Display Corp.
|
||||
|
|
|
@ -16697,6 +16697,12 @@ L: linux-gpio@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/gpio/gpio-wcove.c
|
||||
|
||||
WHWAVE RTC DRIVER
|
||||
M: Dianlong Li <long17.cool@163.com>
|
||||
L: linux-rtc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/rtc/rtc-sd3078.c
|
||||
|
||||
WIIMOTE HID DRIVER
|
||||
M: David Herrmann <dh.herrmann@googlemail.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
|
|
|
@ -185,6 +185,16 @@ config RTC_DRV_ABB5ZES3
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ab-b5ze-s3.
|
||||
|
||||
config RTC_DRV_ABEOZ9
|
||||
select REGMAP_I2C
|
||||
tristate "Abracon AB-RTCMC-32.768kHz-EOZ9"
|
||||
help
|
||||
If you say yes here you get support for the Abracon
|
||||
AB-RTCMC-32.768kHz-EOA9 I2C RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ab-e0z9.
|
||||
|
||||
config RTC_DRV_ABX80X
|
||||
tristate "Abracon ABx80x"
|
||||
select WATCHDOG_CORE if WATCHDOG
|
||||
|
@ -601,9 +611,10 @@ config RTC_DRV_RX8010
|
|||
will be called rtc-rx8010.
|
||||
|
||||
config RTC_DRV_RX8581
|
||||
tristate "Epson RX-8581"
|
||||
tristate "Epson RX-8571/RX-8581"
|
||||
help
|
||||
If you say yes here you will get support for the Epson RX-8581.
|
||||
If you say yes here you will get support for the Epson RX-8571/
|
||||
RX-8581.
|
||||
|
||||
This driver can also be built as a module. If so the module
|
||||
will be called rtc-rx8581.
|
||||
|
@ -626,6 +637,15 @@ config RTC_DRV_EM3027
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-em3027.
|
||||
|
||||
config RTC_DRV_RV3028
|
||||
tristate "Micro Crystal RV3028"
|
||||
help
|
||||
If you say yes here you get support for the Micro Crystal
|
||||
RV3028.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rv3028.
|
||||
|
||||
config RTC_DRV_RV8803
|
||||
tristate "Micro Crystal RV8803, Epson RX8900"
|
||||
help
|
||||
|
@ -646,6 +666,15 @@ config RTC_DRV_S5M
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-s5m.
|
||||
|
||||
config RTC_DRV_SD3078
|
||||
tristate "ZXW Crystal SD3078"
|
||||
help
|
||||
If you say yes here you get support for the ZXW Crystal
|
||||
SD3078 RTC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-sd3078
|
||||
|
||||
endif # I2C
|
||||
|
||||
comment "SPI RTC drivers"
|
||||
|
@ -1285,6 +1314,17 @@ config RTC_DRV_IMXDI
|
|||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-imxdi".
|
||||
|
||||
config RTC_DRV_MESON
|
||||
tristate "Amlogic Meson RTC"
|
||||
depends on (ARM && ARCH_MESON) || COMPILE_TEST
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
Support for the RTC block on the Amlogic Meson6, Meson8, Meson8b
|
||||
and Meson8m2 SoCs.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-meson".
|
||||
|
||||
config RTC_DRV_OMAP
|
||||
tristate "TI OMAP Real Time Clock"
|
||||
depends on ARCH_OMAP || ARCH_DAVINCI || COMPILE_TEST
|
||||
|
@ -1508,6 +1548,16 @@ config RTC_DRV_ARMADA38X
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called armada38x-rtc.
|
||||
|
||||
config RTC_DRV_CADENCE
|
||||
tristate "Cadence RTC driver"
|
||||
depends on OF && HAS_IOMEM
|
||||
help
|
||||
If you say Y here you will get access to Cadence RTC IP
|
||||
found on certain SOCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rtc-cadence.
|
||||
|
||||
config RTC_DRV_FTRTC010
|
||||
tristate "Faraday Technology FTRTC010 RTC"
|
||||
depends on HAS_IOMEM
|
||||
|
@ -1679,6 +1729,7 @@ config RTC_DRV_SNVS
|
|||
|
||||
config RTC_DRV_IMX_SC
|
||||
depends on IMX_SCU
|
||||
depends on HAVE_ARM_SMCCC
|
||||
tristate "NXP i.MX System Controller RTC support"
|
||||
help
|
||||
If you say yes here you get support for the NXP i.MX System
|
||||
|
@ -1795,8 +1846,7 @@ 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
|
||||
depends on HID_SENSOR_HUB && IIO
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
help
|
||||
Say yes here to build support for the HID Sensors of type Time.
|
||||
|
|
|
@ -28,6 +28,7 @@ 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_ABEOZ9) += rtc-ab-eoz9.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
|
||||
|
@ -39,6 +40,7 @@ obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
|||
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
||||
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
|
||||
obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.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
|
||||
|
@ -100,6 +102,7 @@ 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_MESON) += rtc-meson.o
|
||||
obj-$(CONFIG_RTC_DRV_MOXART) += rtc-moxart.o
|
||||
obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
|
||||
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
|
||||
|
@ -137,6 +140,7 @@ 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_RV3028) += rtc-rv3028.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
|
||||
|
@ -149,6 +153,7 @@ 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_SD3078) += rtc-sd3078.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
|
||||
|
|
|
@ -178,11 +178,6 @@ rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|||
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) ?:
|
||||
|
|
|
@ -100,7 +100,7 @@ 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)
|
||||
|| tm->tm_mday > rtc_month_days(tm->tm_mon, ((unsigned)tm->tm_year + 1900))
|
||||
|| ((unsigned)tm->tm_hour) >= 24
|
||||
|| ((unsigned)tm->tm_min) >= 60
|
||||
|| ((unsigned)tm->tm_sec) >= 60)
|
||||
|
@ -116,8 +116,8 @@ EXPORT_SYMBOL(rtc_valid_tm);
|
|||
*/
|
||||
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);
|
||||
return mktime64(((unsigned)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);
|
||||
|
||||
|
|
|
@ -114,12 +114,14 @@ static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
unsigned char buf[4];
|
||||
unsigned long ticks, base, data;
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
@ -137,7 +139,8 @@ static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
/* load 32-bit read-only counter */
|
||||
regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
|
||||
data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
@ -158,11 +161,13 @@ static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
int ret;
|
||||
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
@ -185,12 +190,14 @@ static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
|
||||
|
||||
regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
|
||||
base = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
|
|
@ -115,11 +115,13 @@ static int pm860x_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
|
||||
dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
|
||||
buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
|
||||
base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
|
||||
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 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
@ -145,7 +147,8 @@ static int pm860x_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
/* load 32-bit read-only counter */
|
||||
pm860x_bulk_read(info->i2c, PM8607_RTC_COUNTER1, 4, buf);
|
||||
data = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
@ -170,10 +173,12 @@ static int pm860x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
|
||||
dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
|
||||
buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
|
||||
base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
|
||||
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 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
@ -198,11 +203,13 @@ static int pm860x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
pm860x_page_bulk_read(info->i2c, REG0_ADDR, 8, buf);
|
||||
dev_dbg(info->dev, "%x-%x-%x-%x-%x-%x-%x-%x\n", buf[0], buf[1],
|
||||
buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
|
||||
base = (buf[1] << 24) | (buf[3] << 16) | (buf[5] << 8) | buf[7];
|
||||
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 = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
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);
|
||||
|
|
|
@ -0,0 +1,465 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Real Time Clock driver for AB-RTCMC-32.768kHz-EOZ9 chip.
|
||||
* Copyright (C) 2019 Orolia
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
|
||||
#define ABEOZ9_REG_CTRL1 0x00
|
||||
#define ABEOZ9_REG_CTRL1_MASK GENMASK(7, 0)
|
||||
#define ABEOZ9_REG_CTRL1_WE BIT(0)
|
||||
#define ABEOZ9_REG_CTRL1_TE BIT(1)
|
||||
#define ABEOZ9_REG_CTRL1_TAR BIT(2)
|
||||
#define ABEOZ9_REG_CTRL1_EERE BIT(3)
|
||||
#define ABEOZ9_REG_CTRL1_SRON BIT(4)
|
||||
#define ABEOZ9_REG_CTRL1_TD0 BIT(5)
|
||||
#define ABEOZ9_REG_CTRL1_TD1 BIT(6)
|
||||
#define ABEOZ9_REG_CTRL1_CLKINT BIT(7)
|
||||
|
||||
#define ABEOZ9_REG_CTRL_INT 0x01
|
||||
#define ABEOZ9_REG_CTRL_INT_AIE BIT(0)
|
||||
#define ABEOZ9_REG_CTRL_INT_TIE BIT(1)
|
||||
#define ABEOZ9_REG_CTRL_INT_V1IE BIT(2)
|
||||
#define ABEOZ9_REG_CTRL_INT_V2IE BIT(3)
|
||||
#define ABEOZ9_REG_CTRL_INT_SRIE BIT(4)
|
||||
|
||||
#define ABEOZ9_REG_CTRL_INT_FLAG 0x02
|
||||
#define ABEOZ9_REG_CTRL_INT_FLAG_AF BIT(0)
|
||||
#define ABEOZ9_REG_CTRL_INT_FLAG_TF BIT(1)
|
||||
#define ABEOZ9_REG_CTRL_INT_FLAG_V1IF BIT(2)
|
||||
#define ABEOZ9_REG_CTRL_INT_FLAG_V2IF BIT(3)
|
||||
#define ABEOZ9_REG_CTRL_INT_FLAG_SRF BIT(4)
|
||||
|
||||
#define ABEOZ9_REG_CTRL_STATUS 0x03
|
||||
#define ABEOZ9_REG_CTRL_STATUS_V1F BIT(2)
|
||||
#define ABEOZ9_REG_CTRL_STATUS_V2F BIT(3)
|
||||
#define ABEOZ9_REG_CTRL_STATUS_SR BIT(4)
|
||||
#define ABEOZ9_REG_CTRL_STATUS_PON BIT(5)
|
||||
#define ABEOZ9_REG_CTRL_STATUS_EEBUSY BIT(7)
|
||||
|
||||
#define ABEOZ9_REG_SEC 0x08
|
||||
#define ABEOZ9_REG_MIN 0x09
|
||||
#define ABEOZ9_REG_HOURS 0x0A
|
||||
#define ABEOZ9_HOURS_PM BIT(6)
|
||||
#define ABEOZ9_REG_DAYS 0x0B
|
||||
#define ABEOZ9_REG_WEEKDAYS 0x0C
|
||||
#define ABEOZ9_REG_MONTHS 0x0D
|
||||
#define ABEOZ9_REG_YEARS 0x0E
|
||||
|
||||
#define ABEOZ9_SEC_LEN 7
|
||||
|
||||
#define ABEOZ9_REG_REG_TEMP 0x20
|
||||
#define ABEOZ953_TEMP_MAX 120
|
||||
#define ABEOZ953_TEMP_MIN -60
|
||||
|
||||
#define ABEOZ9_REG_EEPROM 0x30
|
||||
#define ABEOZ9_REG_EEPROM_MASK GENMASK(8, 0)
|
||||
#define ABEOZ9_REG_EEPROM_THP BIT(0)
|
||||
#define ABEOZ9_REG_EEPROM_THE BIT(1)
|
||||
#define ABEOZ9_REG_EEPROM_FD0 BIT(2)
|
||||
#define ABEOZ9_REG_EEPROM_FD1 BIT(3)
|
||||
#define ABEOZ9_REG_EEPROM_R1K BIT(4)
|
||||
#define ABEOZ9_REG_EEPROM_R5K BIT(5)
|
||||
#define ABEOZ9_REG_EEPROM_R20K BIT(6)
|
||||
#define ABEOZ9_REG_EEPROM_R80K BIT(7)
|
||||
|
||||
struct abeoz9_rtc_data {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
struct device *hwmon_dev;
|
||||
};
|
||||
|
||||
static int abeoz9_check_validity(struct device *dev)
|
||||
{
|
||||
struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"unable to get CTRL_STATUS register (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (val & ABEOZ9_REG_CTRL_STATUS_PON) {
|
||||
dev_warn(dev, "power-on reset detected, date is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (val & ABEOZ9_REG_CTRL_STATUS_V1F) {
|
||||
dev_warn(dev,
|
||||
"voltage drops below VLOW1 threshold, date is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
|
||||
dev_warn(dev,
|
||||
"voltage drops below VLOW2 threshold, date is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int abeoz9_reset_validity(struct regmap *regmap)
|
||||
{
|
||||
return regmap_update_bits(regmap, ABEOZ9_REG_CTRL_STATUS,
|
||||
ABEOZ9_REG_CTRL_STATUS_V1F |
|
||||
ABEOZ9_REG_CTRL_STATUS_V2F |
|
||||
ABEOZ9_REG_CTRL_STATUS_PON,
|
||||
0);
|
||||
}
|
||||
|
||||
static int abeoz9_rtc_get_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
|
||||
u8 regs[ABEOZ9_SEC_LEN];
|
||||
int ret;
|
||||
|
||||
ret = abeoz9_check_validity(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, ABEOZ9_REG_SEC,
|
||||
regs,
|
||||
sizeof(regs));
|
||||
if (ret) {
|
||||
dev_err(dev, "reading RTC time failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] & 0x7F);
|
||||
tm->tm_min = bcd2bin(regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] & 0x7F);
|
||||
|
||||
if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM) {
|
||||
tm->tm_hour =
|
||||
bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & 0x1f);
|
||||
if (regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] & ABEOZ9_HOURS_PM)
|
||||
tm->tm_hour += 12;
|
||||
} else {
|
||||
tm->tm_hour = bcd2bin(regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC]);
|
||||
}
|
||||
|
||||
tm->tm_mday = bcd2bin(regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC]);
|
||||
tm->tm_wday = bcd2bin(regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC]);
|
||||
tm->tm_mon = bcd2bin(regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC]) - 1;
|
||||
tm->tm_year = bcd2bin(regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC]) + 100;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int abeoz9_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
u8 regs[ABEOZ9_SEC_LEN];
|
||||
int ret;
|
||||
|
||||
regs[ABEOZ9_REG_SEC - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_sec);
|
||||
regs[ABEOZ9_REG_MIN - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_min);
|
||||
regs[ABEOZ9_REG_HOURS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_hour);
|
||||
regs[ABEOZ9_REG_DAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mday);
|
||||
regs[ABEOZ9_REG_WEEKDAYS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_wday);
|
||||
regs[ABEOZ9_REG_MONTHS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_mon + 1);
|
||||
regs[ABEOZ9_REG_YEARS - ABEOZ9_REG_SEC] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
ret = regmap_bulk_write(data->regmap, ABEOZ9_REG_SEC,
|
||||
regs,
|
||||
sizeof(regs));
|
||||
|
||||
if (ret) {
|
||||
dev_err(dev, "set RTC time failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return abeoz9_reset_validity(regmap);
|
||||
}
|
||||
|
||||
static int abeoz9_trickle_parse_dt(struct device_node *node)
|
||||
{
|
||||
u32 ohms = 0;
|
||||
|
||||
if (of_property_read_u32(node, "trickle-resistor-ohms", &ohms))
|
||||
return 0;
|
||||
|
||||
switch (ohms) {
|
||||
case 1000:
|
||||
return ABEOZ9_REG_EEPROM_R1K;
|
||||
case 5000:
|
||||
return ABEOZ9_REG_EEPROM_R5K;
|
||||
case 20000:
|
||||
return ABEOZ9_REG_EEPROM_R20K;
|
||||
case 80000:
|
||||
return ABEOZ9_REG_EEPROM_R80K;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int abeoz9_rtc_setup(struct device *dev, struct device_node *node)
|
||||
{
|
||||
struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
int ret;
|
||||
|
||||
/* Enable Self Recovery, Clock for Watch and EEPROM refresh functions */
|
||||
ret = regmap_update_bits(regmap, ABEOZ9_REG_CTRL1,
|
||||
ABEOZ9_REG_CTRL1_MASK,
|
||||
ABEOZ9_REG_CTRL1_WE |
|
||||
ABEOZ9_REG_CTRL1_EERE |
|
||||
ABEOZ9_REG_CTRL1_SRON);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "unable to set CTRL_1 register (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"unable to set control CTRL_INT register (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_write(regmap, ABEOZ9_REG_CTRL_INT_FLAG, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev,
|
||||
"unable to set control CTRL_INT_FLAG register (%d)\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = abeoz9_trickle_parse_dt(node);
|
||||
|
||||
/* Enable built-in termometer */
|
||||
ret |= ABEOZ9_REG_EEPROM_THE;
|
||||
|
||||
ret = regmap_update_bits(regmap, ABEOZ9_REG_EEPROM,
|
||||
ABEOZ9_REG_EEPROM_MASK,
|
||||
ret);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "unable to set EEPROM register (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rtc_ops = {
|
||||
.read_time = abeoz9_rtc_get_time,
|
||||
.set_time = abeoz9_rtc_set_time,
|
||||
};
|
||||
|
||||
static const struct regmap_config abeoz9_rtc_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
|
||||
static int abeoz9z3_temp_read(struct device *dev,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *temp)
|
||||
{
|
||||
struct abeoz9_rtc_data *data = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = data->regmap;
|
||||
int ret;
|
||||
unsigned int val;
|
||||
|
||||
ret = regmap_read(regmap, ABEOZ9_REG_CTRL_STATUS, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((val & ABEOZ9_REG_CTRL_STATUS_V1F) ||
|
||||
(val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
|
||||
dev_err(dev,
|
||||
"thermometer might be disabled due to low voltage\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*temp = 1000 * (val + ABEOZ953_TEMP_MIN);
|
||||
return 0;
|
||||
case hwmon_temp_max:
|
||||
*temp = 1000 * ABEOZ953_TEMP_MAX;
|
||||
return 0;
|
||||
case hwmon_temp_min:
|
||||
*temp = 1000 * ABEOZ953_TEMP_MIN;
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static umode_t abeoz9_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
case hwmon_temp_max:
|
||||
case hwmon_temp_min:
|
||||
return 0444;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const u32 abeoz9_chip_config[] = {
|
||||
HWMON_C_REGISTER_TZ,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info abeoz9_chip = {
|
||||
.type = hwmon_chip,
|
||||
.config = abeoz9_chip_config,
|
||||
};
|
||||
|
||||
static const u32 abeoz9_temp_config[] = {
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info abeoz9_temp = {
|
||||
.type = hwmon_temp,
|
||||
.config = abeoz9_temp_config,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *abeoz9_info[] = {
|
||||
&abeoz9_chip,
|
||||
&abeoz9_temp,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops abeoz9_hwmon_ops = {
|
||||
.is_visible = abeoz9_is_visible,
|
||||
.read = abeoz9z3_temp_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info abeoz9_chip_info = {
|
||||
.ops = &abeoz9_hwmon_ops,
|
||||
.info = abeoz9_info,
|
||||
};
|
||||
|
||||
static void abeoz9_hwmon_register(struct device *dev,
|
||||
struct abeoz9_rtc_data *data)
|
||||
{
|
||||
data->hwmon_dev =
|
||||
devm_hwmon_device_register_with_info(dev,
|
||||
"abeoz9",
|
||||
data,
|
||||
&abeoz9_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
dev_warn(dev, "unable to register hwmon device %ld\n",
|
||||
PTR_ERR(data->hwmon_dev));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void abeoz9_hwmon_register(struct device *dev,
|
||||
struct abeoz9_rtc_data *data)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int abeoz9_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct abeoz9_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, &abeoz9_rtc_regmap_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
ret = PTR_ERR(regmap);
|
||||
dev_err(dev, "regmap allocation failed: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
data->regmap = regmap;
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
ret = abeoz9_rtc_setup(dev, client->dev.of_node);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
data->rtc = devm_rtc_allocate_device(dev);
|
||||
ret = PTR_ERR_OR_ZERO(data->rtc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
data->rtc->ops = &rtc_ops;
|
||||
data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
data->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
ret = rtc_register_device(data->rtc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
abeoz9_hwmon_register(dev, data);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(dev, "unable to register RTC device (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id abeoz9_dt_match[] = {
|
||||
{ .compatible = "abracon,abeoz9" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, abeoz9_dt_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id abeoz9_id[] = {
|
||||
{ "abeoz9", 0 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver abeoz9_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-ab-eoz9",
|
||||
.of_match_table = of_match_ptr(abeoz9_dt_match),
|
||||
},
|
||||
.probe = abeoz9_probe,
|
||||
.id_table = abeoz9_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(abeoz9_driver);
|
||||
|
||||
MODULE_AUTHOR("Artem Panfilov <panfilov.artyom@gmail.com>");
|
||||
MODULE_DESCRIPTION("Abracon AB-RTCMC-32.768kHz-EOZ9 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -5,7 +5,7 @@
|
|||
* Copyright 2014-2015 Macq S.A.
|
||||
*
|
||||
* Author: Philippe De Muyter <phdm@macqel.be>
|
||||
* Author: Alexandre Belloni <alexandre.belloni@free-electrons.com>
|
||||
* Author: Alexandre Belloni <alexandre.belloni@bootlin.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
|
||||
|
@ -46,6 +46,9 @@
|
|||
#define ABX8XX_CTRL_ARST BIT(2)
|
||||
#define ABX8XX_CTRL_12_24 BIT(6)
|
||||
|
||||
#define ABX8XX_REG_CTRL2 0x11
|
||||
#define ABX8XX_CTRL2_RSVD BIT(5)
|
||||
|
||||
#define ABX8XX_REG_IRQ 0x12
|
||||
#define ABX8XX_IRQ_AIE BIT(2)
|
||||
#define ABX8XX_IRQ_IM_1_4 (0x3 << 5)
|
||||
|
@ -78,6 +81,9 @@
|
|||
|
||||
#define ABX8XX_REG_ID0 0x28
|
||||
|
||||
#define ABX8XX_REG_OUT_CTRL 0x30
|
||||
#define ABX8XX_OUT_CTRL_EXDS BIT(4)
|
||||
|
||||
#define ABX8XX_REG_TRICKLE 0x20
|
||||
#define ABX8XX_TRICKLE_CHARGE_ENABLE 0xa0
|
||||
#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
|
||||
|
@ -86,7 +92,7 @@
|
|||
static u8 trickle_resistors[] = {0, 3, 6, 11};
|
||||
|
||||
enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
|
||||
AB1801, AB1803, AB1804, AB1805, ABX80X};
|
||||
AB1801, AB1803, AB1804, AB1805, RV1805, ABX80X};
|
||||
|
||||
struct abx80x_cap {
|
||||
u16 pn;
|
||||
|
@ -103,6 +109,7 @@ static struct abx80x_cap abx80x_caps[] = {
|
|||
[AB1803] = {.pn = 0x1803},
|
||||
[AB1804] = {.pn = 0x1804, .has_tc = true, .has_wdog = true},
|
||||
[AB1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
|
||||
[RV1805] = {.pn = 0x1805, .has_tc = true, .has_wdog = true},
|
||||
[ABX80X] = {.pn = 0}
|
||||
};
|
||||
|
||||
|
@ -723,6 +730,62 @@ static int abx80x_probe(struct i2c_client *client,
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
/* Configure RV1805 specifics */
|
||||
if (part == RV1805) {
|
||||
/*
|
||||
* Avoid accidentally entering test mode. This can happen
|
||||
* on the RV1805 in case the reserved bit 5 in control2
|
||||
* register is set. RV-1805-C3 datasheet indicates that
|
||||
* the bit should be cleared in section 11h - Control2.
|
||||
*/
|
||||
data = i2c_smbus_read_byte_data(client, ABX8XX_REG_CTRL2);
|
||||
if (data < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to read control2 register\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, ABX8XX_REG_CTRL2,
|
||||
data & ~ABX8XX_CTRL2_RSVD);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to write control2 register\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Avoid extra power leakage. The RV1805 uses smaller
|
||||
* 10pin package and the EXTI input is not present.
|
||||
* Disable it to avoid leakage.
|
||||
*/
|
||||
data = i2c_smbus_read_byte_data(client, ABX8XX_REG_OUT_CTRL);
|
||||
if (data < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to read output control register\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the configuration key register to enable access to
|
||||
* the config2 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_OUT_CTRL,
|
||||
data | ABX8XX_OUT_CTRL_EXDS);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to write output control register\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* part autodetection */
|
||||
if (part == ABX80X) {
|
||||
for (i = 0; abx80x_caps[i].pn; i++)
|
||||
|
@ -826,7 +889,7 @@ static const struct i2c_device_id abx80x_id[] = {
|
|||
{ "ab1803", AB1803 },
|
||||
{ "ab1804", AB1804 },
|
||||
{ "ab1805", AB1805 },
|
||||
{ "rv1805", AB1805 },
|
||||
{ "rv1805", RV1805 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, abx80x_id);
|
||||
|
@ -843,6 +906,6 @@ static struct i2c_driver abx80x_driver = {
|
|||
module_i2c_driver(abx80x_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe De Muyter <phdm@macqel.be>");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Abracon ABX80X RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -0,0 +1,423 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Copyright 2019 Cadence
|
||||
*
|
||||
* Authors:
|
||||
* Jan Kotas <jank@cadence.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
|
||||
/* Registers */
|
||||
#define CDNS_RTC_CTLR 0x00
|
||||
#define CDNS_RTC_HMR 0x04
|
||||
#define CDNS_RTC_TIMR 0x08
|
||||
#define CDNS_RTC_CALR 0x0C
|
||||
#define CDNS_RTC_TIMAR 0x10
|
||||
#define CDNS_RTC_CALAR 0x14
|
||||
#define CDNS_RTC_AENR 0x18
|
||||
#define CDNS_RTC_EFLR 0x1C
|
||||
#define CDNS_RTC_IENR 0x20
|
||||
#define CDNS_RTC_IDISR 0x24
|
||||
#define CDNS_RTC_IMSKR 0x28
|
||||
#define CDNS_RTC_STSR 0x2C
|
||||
#define CDNS_RTC_KRTCR 0x30
|
||||
|
||||
/* Control */
|
||||
#define CDNS_RTC_CTLR_TIME BIT(0)
|
||||
#define CDNS_RTC_CTLR_CAL BIT(1)
|
||||
#define CDNS_RTC_CTLR_TIME_CAL (CDNS_RTC_CTLR_TIME | CDNS_RTC_CTLR_CAL)
|
||||
|
||||
/* Status */
|
||||
#define CDNS_RTC_STSR_VT BIT(0)
|
||||
#define CDNS_RTC_STSR_VC BIT(1)
|
||||
#define CDNS_RTC_STSR_VTA BIT(2)
|
||||
#define CDNS_RTC_STSR_VCA BIT(3)
|
||||
#define CDNS_RTC_STSR_VT_VC (CDNS_RTC_STSR_VT | CDNS_RTC_STSR_VC)
|
||||
#define CDNS_RTC_STSR_VTA_VCA (CDNS_RTC_STSR_VTA | CDNS_RTC_STSR_VCA)
|
||||
|
||||
/* Keep RTC */
|
||||
#define CDNS_RTC_KRTCR_KRTC BIT(0)
|
||||
|
||||
/* Alarm, Event, Interrupt */
|
||||
#define CDNS_RTC_AEI_HOS BIT(0)
|
||||
#define CDNS_RTC_AEI_SEC BIT(1)
|
||||
#define CDNS_RTC_AEI_MIN BIT(2)
|
||||
#define CDNS_RTC_AEI_HOUR BIT(3)
|
||||
#define CDNS_RTC_AEI_DATE BIT(4)
|
||||
#define CDNS_RTC_AEI_MNTH BIT(5)
|
||||
#define CDNS_RTC_AEI_ALRM BIT(6)
|
||||
|
||||
/* Time */
|
||||
#define CDNS_RTC_TIME_H GENMASK(7, 0)
|
||||
#define CDNS_RTC_TIME_S GENMASK(14, 8)
|
||||
#define CDNS_RTC_TIME_M GENMASK(22, 16)
|
||||
#define CDNS_RTC_TIME_HR GENMASK(29, 24)
|
||||
#define CDNS_RTC_TIME_PM BIT(30)
|
||||
#define CDNS_RTC_TIME_CH BIT(31)
|
||||
|
||||
/* Calendar */
|
||||
#define CDNS_RTC_CAL_DAY GENMASK(2, 0)
|
||||
#define CDNS_RTC_CAL_M GENMASK(7, 3)
|
||||
#define CDNS_RTC_CAL_D GENMASK(13, 8)
|
||||
#define CDNS_RTC_CAL_Y GENMASK(23, 16)
|
||||
#define CDNS_RTC_CAL_C GENMASK(29, 24)
|
||||
#define CDNS_RTC_CAL_CH BIT(31)
|
||||
|
||||
#define CDNS_RTC_MAX_REGS_TRIES 3
|
||||
|
||||
struct cdns_rtc {
|
||||
struct rtc_device *rtc_dev;
|
||||
struct clk *pclk;
|
||||
struct clk *ref_clk;
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static void cdns_rtc_set_enabled(struct cdns_rtc *crtc, bool enabled)
|
||||
{
|
||||
u32 reg = enabled ? 0x0 : CDNS_RTC_CTLR_TIME_CAL;
|
||||
|
||||
writel(reg, crtc->regs + CDNS_RTC_CTLR);
|
||||
}
|
||||
|
||||
static bool cdns_rtc_get_enabled(struct cdns_rtc *crtc)
|
||||
{
|
||||
return !(readl(crtc->regs + CDNS_RTC_CTLR) & CDNS_RTC_CTLR_TIME_CAL);
|
||||
}
|
||||
|
||||
static irqreturn_t cdns_rtc_irq_handler(int irq, void *id)
|
||||
{
|
||||
struct device *dev = id;
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
|
||||
/* Reading the register clears it */
|
||||
if (!(readl(crtc->regs + CDNS_RTC_EFLR) & CDNS_RTC_AEI_ALRM))
|
||||
return IRQ_NONE;
|
||||
|
||||
rtc_update_irq(crtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static u32 cdns_rtc_time2reg(struct rtc_time *tm)
|
||||
{
|
||||
return FIELD_PREP(CDNS_RTC_TIME_S, bin2bcd(tm->tm_sec))
|
||||
| FIELD_PREP(CDNS_RTC_TIME_M, bin2bcd(tm->tm_min))
|
||||
| FIELD_PREP(CDNS_RTC_TIME_HR, bin2bcd(tm->tm_hour));
|
||||
}
|
||||
|
||||
static void cdns_rtc_reg2time(u32 reg, struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_sec = bcd2bin(FIELD_GET(CDNS_RTC_TIME_S, reg));
|
||||
tm->tm_min = bcd2bin(FIELD_GET(CDNS_RTC_TIME_M, reg));
|
||||
tm->tm_hour = bcd2bin(FIELD_GET(CDNS_RTC_TIME_HR, reg));
|
||||
}
|
||||
|
||||
static int cdns_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
|
||||
/* If the RTC is disabled, assume the values are invalid */
|
||||
if (!cdns_rtc_get_enabled(crtc))
|
||||
return -EINVAL;
|
||||
|
||||
cdns_rtc_set_enabled(crtc, false);
|
||||
|
||||
reg = readl(crtc->regs + CDNS_RTC_TIMR);
|
||||
cdns_rtc_reg2time(reg, tm);
|
||||
|
||||
reg = readl(crtc->regs + CDNS_RTC_CALR);
|
||||
tm->tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg));
|
||||
tm->tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1;
|
||||
tm->tm_year = bcd2bin(FIELD_GET(CDNS_RTC_CAL_Y, reg))
|
||||
+ bcd2bin(FIELD_GET(CDNS_RTC_CAL_C, reg)) * 100 - 1900;
|
||||
tm->tm_wday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_DAY, reg)) - 1;
|
||||
|
||||
cdns_rtc_set_enabled(crtc, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
u32 timr, calr, stsr;
|
||||
int ret = -EIO;
|
||||
int year = tm->tm_year + 1900;
|
||||
int tries;
|
||||
|
||||
cdns_rtc_set_enabled(crtc, false);
|
||||
|
||||
timr = cdns_rtc_time2reg(tm);
|
||||
|
||||
calr = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(tm->tm_mday))
|
||||
| FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(tm->tm_mon + 1))
|
||||
| FIELD_PREP(CDNS_RTC_CAL_Y, bin2bcd(year % 100))
|
||||
| FIELD_PREP(CDNS_RTC_CAL_C, bin2bcd(year / 100))
|
||||
| FIELD_PREP(CDNS_RTC_CAL_DAY, tm->tm_wday + 1);
|
||||
|
||||
/* Update registers, check valid flags */
|
||||
for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) {
|
||||
writel(timr, crtc->regs + CDNS_RTC_TIMR);
|
||||
writel(calr, crtc->regs + CDNS_RTC_CALR);
|
||||
stsr = readl(crtc->regs + CDNS_RTC_STSR);
|
||||
|
||||
if ((stsr & CDNS_RTC_STSR_VT_VC) == CDNS_RTC_STSR_VT_VC) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cdns_rtc_set_enabled(crtc, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
|
||||
if (enabled) {
|
||||
writel((CDNS_RTC_AEI_SEC | CDNS_RTC_AEI_MIN | CDNS_RTC_AEI_HOUR
|
||||
| CDNS_RTC_AEI_DATE | CDNS_RTC_AEI_MNTH),
|
||||
crtc->regs + CDNS_RTC_AENR);
|
||||
writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IENR);
|
||||
} else {
|
||||
writel(0, crtc->regs + CDNS_RTC_AENR);
|
||||
writel(CDNS_RTC_AEI_ALRM, crtc->regs + CDNS_RTC_IDISR);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
|
||||
reg = readl(crtc->regs + CDNS_RTC_TIMAR);
|
||||
cdns_rtc_reg2time(reg, &alarm->time);
|
||||
|
||||
reg = readl(crtc->regs + CDNS_RTC_CALAR);
|
||||
alarm->time.tm_mday = bcd2bin(FIELD_GET(CDNS_RTC_CAL_D, reg));
|
||||
alarm->time.tm_mon = bcd2bin(FIELD_GET(CDNS_RTC_CAL_M, reg)) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
int ret = -EIO;
|
||||
int tries;
|
||||
u32 timar, calar, stsr;
|
||||
|
||||
cdns_rtc_alarm_irq_enable(dev, 0);
|
||||
|
||||
timar = cdns_rtc_time2reg(&alarm->time);
|
||||
calar = FIELD_PREP(CDNS_RTC_CAL_D, bin2bcd(alarm->time.tm_mday))
|
||||
| FIELD_PREP(CDNS_RTC_CAL_M, bin2bcd(alarm->time.tm_mon + 1));
|
||||
|
||||
/* Update registers, check valid alarm flags */
|
||||
for (tries = 0; tries < CDNS_RTC_MAX_REGS_TRIES; tries++) {
|
||||
writel(timar, crtc->regs + CDNS_RTC_TIMAR);
|
||||
writel(calar, crtc->regs + CDNS_RTC_CALAR);
|
||||
stsr = readl(crtc->regs + CDNS_RTC_STSR);
|
||||
|
||||
if ((stsr & CDNS_RTC_STSR_VTA_VCA) == CDNS_RTC_STSR_VTA_VCA) {
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
cdns_rtc_alarm_irq_enable(dev, alarm->enabled);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops cdns_rtc_ops = {
|
||||
.read_time = cdns_rtc_read_time,
|
||||
.set_time = cdns_rtc_set_time,
|
||||
.read_alarm = cdns_rtc_read_alarm,
|
||||
.set_alarm = cdns_rtc_set_alarm,
|
||||
.alarm_irq_enable = cdns_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int cdns_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_rtc *crtc;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
unsigned long ref_clk_freq;
|
||||
|
||||
crtc = devm_kzalloc(&pdev->dev, sizeof(*crtc), GFP_KERNEL);
|
||||
if (!crtc)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
crtc->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(crtc->regs))
|
||||
return PTR_ERR(crtc->regs);
|
||||
|
||||
crtc->irq = platform_get_irq(pdev, 0);
|
||||
if (crtc->irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
crtc->pclk = devm_clk_get(&pdev->dev, "pclk");
|
||||
if (IS_ERR(crtc->pclk)) {
|
||||
ret = PTR_ERR(crtc->pclk);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to retrieve the peripheral clock, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
crtc->ref_clk = devm_clk_get(&pdev->dev, "ref_clk");
|
||||
if (IS_ERR(crtc->ref_clk)) {
|
||||
ret = PTR_ERR(crtc->ref_clk);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to retrieve the reference clock, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
crtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(crtc->rtc_dev)) {
|
||||
ret = PTR_ERR(crtc->rtc_dev);
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to allocate the RTC device, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, crtc);
|
||||
|
||||
ret = clk_prepare_enable(crtc->pclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to enable the peripheral clock, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(crtc->ref_clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to enable the reference clock, %d\n", ret);
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
|
||||
ref_clk_freq = clk_get_rate(crtc->ref_clk);
|
||||
if ((ref_clk_freq != 1) && (ref_clk_freq != 100)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Invalid reference clock frequency %lu Hz.\n",
|
||||
ref_clk_freq);
|
||||
ret = -EINVAL;
|
||||
goto err_disable_ref_clk;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, crtc->irq,
|
||||
cdns_rtc_irq_handler, 0,
|
||||
dev_name(&pdev->dev), &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to request interrupt for the device, %d\n",
|
||||
ret);
|
||||
goto err_disable_ref_clk;
|
||||
}
|
||||
|
||||
/* The RTC supports 01.01.1900 - 31.12.2999 */
|
||||
crtc->rtc_dev->range_min = mktime64(1900, 1, 1, 0, 0, 0);
|
||||
crtc->rtc_dev->range_max = mktime64(2999, 12, 31, 23, 59, 59);
|
||||
|
||||
crtc->rtc_dev->ops = &cdns_rtc_ops;
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
/* Always use 24-hour mode and keep the RTC values */
|
||||
writel(0, crtc->regs + CDNS_RTC_HMR);
|
||||
writel(CDNS_RTC_KRTCR_KRTC, crtc->regs + CDNS_RTC_KRTCR);
|
||||
|
||||
ret = rtc_register_device(crtc->rtc_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Failed to register the RTC device, %d\n", ret);
|
||||
goto err_disable_wakeup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_wakeup:
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
|
||||
err_disable_ref_clk:
|
||||
clk_disable_unprepare(crtc->ref_clk);
|
||||
|
||||
err_disable_pclk:
|
||||
clk_disable_unprepare(crtc->pclk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdns_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cdns_rtc *crtc = platform_get_drvdata(pdev);
|
||||
|
||||
cdns_rtc_alarm_irq_enable(&pdev->dev, 0);
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
|
||||
clk_disable_unprepare(crtc->pclk);
|
||||
clk_disable_unprepare(crtc->ref_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cdns_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(crtc->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdns_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct cdns_rtc *crtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(crtc->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cdns_rtc_pm_ops, cdns_rtc_suspend, cdns_rtc_resume);
|
||||
|
||||
static const struct of_device_id cdns_rtc_of_match[] = {
|
||||
{ .compatible = "cdns,rtc-r109v3" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cdns_rtc_of_match);
|
||||
|
||||
static struct platform_driver cdns_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "cdns-rtc",
|
||||
.of_match_table = cdns_rtc_of_match,
|
||||
.pm = &cdns_rtc_pm_ops,
|
||||
},
|
||||
.probe = cdns_rtc_probe,
|
||||
.remove = cdns_rtc_remove,
|
||||
};
|
||||
module_platform_driver(cdns_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Jan Kotas <jank@cadence.com>");
|
||||
MODULE_DESCRIPTION("Cadence RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:cdns-rtc");
|
|
@ -235,9 +235,13 @@ static int coh901331_suspend(struct device *dev)
|
|||
|
||||
static int coh901331_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct coh901331_port *rtap = dev_get_drvdata(dev);
|
||||
|
||||
clk_prepare(rtap->clk);
|
||||
ret = clk_prepare(rtap->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
disable_irq_wake(rtap->irq);
|
||||
} else {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -58,7 +58,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
|||
"%s: raw read data - counters=%02x,%02x,%02x,%02x\n",
|
||||
__func__, buf[0], buf[1], buf[2], buf[3]);
|
||||
|
||||
time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
|
||||
time = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
|
||||
(buf[1] << 8) | buf[0];
|
||||
|
||||
rtc_time_to_tm(time, tm);
|
||||
|
||||
|
|
|
@ -109,6 +109,8 @@ static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
}
|
||||
|
||||
ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK);
|
||||
tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK);
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* Copyright 2018 NXP.
|
||||
*/
|
||||
|
||||
#include <linux/arm-smccc.h>
|
||||
#include <linux/firmware/imx/sci.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
@ -12,6 +13,9 @@
|
|||
#define IMX_SC_TIMER_FUNC_GET_RTC_SEC1970 9
|
||||
#define IMX_SC_TIMER_FUNC_SET_RTC_TIME 6
|
||||
|
||||
#define IMX_SIP_SRTC 0xC2000002
|
||||
#define IMX_SIP_SRTC_SET_TIME 0x0
|
||||
|
||||
static struct imx_sc_ipc *rtc_ipc_handle;
|
||||
static struct rtc_device *imx_sc_rtc;
|
||||
|
||||
|
@ -37,13 +41,28 @@ static int imx_sc_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
return ret;
|
||||
}
|
||||
|
||||
rtc_time_to_tm(msg.time, tm);
|
||||
rtc_time64_to_tm(msg.time, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_sc_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct arm_smccc_res res;
|
||||
|
||||
/* pack 2 time parameters into 1 register, 16 bits for each */
|
||||
arm_smccc_smc(IMX_SIP_SRTC, IMX_SIP_SRTC_SET_TIME,
|
||||
((tm->tm_year + 1900) << 16) | (tm->tm_mon + 1),
|
||||
(tm->tm_mday << 16) | tm->tm_hour,
|
||||
(tm->tm_min << 16) | tm->tm_sec,
|
||||
0, 0, 0, &res);
|
||||
|
||||
return res.a0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops imx_sc_rtc_ops = {
|
||||
.read_time = imx_sc_rtc_read_time,
|
||||
.set_time = imx_sc_rtc_set_time,
|
||||
};
|
||||
|
||||
static int imx_sc_rtc_probe(struct platform_device *pdev)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/bcd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
|
@ -73,10 +74,50 @@
|
|||
static struct i2c_driver isl1208_driver;
|
||||
|
||||
/* ISL1208 various variants */
|
||||
enum {
|
||||
enum isl1208_id {
|
||||
TYPE_ISL1208 = 0,
|
||||
TYPE_ISL1209,
|
||||
TYPE_ISL1218,
|
||||
TYPE_ISL1219,
|
||||
ISL_LAST_ID
|
||||
};
|
||||
|
||||
/* Chip capabilities table */
|
||||
static const struct isl1208_config {
|
||||
const char name[8];
|
||||
unsigned int nvmem_length;
|
||||
unsigned has_tamper:1;
|
||||
unsigned has_timestamp:1;
|
||||
} isl1208_configs[] = {
|
||||
[TYPE_ISL1208] = { "isl1208", 2, false, false },
|
||||
[TYPE_ISL1209] = { "isl1209", 2, true, false },
|
||||
[TYPE_ISL1218] = { "isl1218", 8, false, false },
|
||||
[TYPE_ISL1219] = { "isl1219", 2, true, true },
|
||||
};
|
||||
|
||||
static const struct i2c_device_id isl1208_id[] = {
|
||||
{ "isl1208", TYPE_ISL1208 },
|
||||
{ "isl1209", TYPE_ISL1209 },
|
||||
{ "isl1218", TYPE_ISL1218 },
|
||||
{ "isl1219", TYPE_ISL1219 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isl1208_id);
|
||||
|
||||
static const struct of_device_id isl1208_of_match[] = {
|
||||
{ .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] },
|
||||
{ .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] },
|
||||
{ .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] },
|
||||
{ .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, isl1208_of_match);
|
||||
|
||||
/* Device state */
|
||||
struct isl1208_state {
|
||||
struct nvmem_config nvmem_config;
|
||||
struct rtc_device *rtc;
|
||||
const struct isl1208_config *config;
|
||||
};
|
||||
|
||||
/* block read */
|
||||
|
@ -161,6 +202,7 @@ isl1208_i2c_get_atr(struct i2c_client *client)
|
|||
return atr;
|
||||
}
|
||||
|
||||
/* returns adjustment value + 100 */
|
||||
static int
|
||||
isl1208_i2c_get_dtr(struct i2c_client *client)
|
||||
{
|
||||
|
@ -171,7 +213,7 @@ isl1208_i2c_get_dtr(struct i2c_client *client)
|
|||
/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
|
||||
dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1);
|
||||
|
||||
return dtr;
|
||||
return dtr + 100;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -248,8 +290,8 @@ isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
|
|||
(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);
|
||||
if (dtr >= 0)
|
||||
seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100);
|
||||
|
||||
atr = isl1208_i2c_get_atr(client);
|
||||
if (atr >= 0)
|
||||
|
@ -556,7 +598,7 @@ 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);
|
||||
struct isl1208_state *isl1208 = i2c_get_clientdata(client);
|
||||
int handled = 0, sr, err;
|
||||
|
||||
/*
|
||||
|
@ -579,7 +621,7 @@ isl1208_rtc_interrupt(int irq, void *data)
|
|||
if (sr & ISL1208_REG_SR_ALM) {
|
||||
dev_dbg(&client->dev, "alarm!\n");
|
||||
|
||||
rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF);
|
||||
rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
/* Clear the alarm */
|
||||
sr &= ~ISL1208_REG_SR_ALM;
|
||||
|
@ -596,11 +638,12 @@ isl1208_rtc_interrupt(int irq, void *data)
|
|||
return err;
|
||||
}
|
||||
|
||||
if (sr & ISL1208_REG_SR_EVT) {
|
||||
sysfs_notify(&rtc->dev.kobj, NULL,
|
||||
dev_attr_timestamp0.attr.name);
|
||||
if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) {
|
||||
dev_warn(&client->dev, "event detected");
|
||||
handled = 1;
|
||||
if (isl1208->config->has_timestamp)
|
||||
sysfs_notify(&isl1208->rtc->dev.kobj, NULL,
|
||||
dev_attr_timestamp0.attr.name);
|
||||
}
|
||||
|
||||
return handled ? IRQ_HANDLED : IRQ_NONE;
|
||||
|
@ -637,7 +680,7 @@ isl1208_sysfs_show_dtrim(struct device *dev,
|
|||
if (dtr < 0)
|
||||
return dtr;
|
||||
|
||||
return sprintf(buf, "%d ppm\n", dtr);
|
||||
return sprintf(buf, "%d ppm\n", dtr - 100);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
|
||||
|
@ -700,6 +743,46 @@ static const struct attribute_group isl1219_rtc_sysfs_files = {
|
|||
.attrs = isl1219_rtc_attrs,
|
||||
};
|
||||
|
||||
static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct isl1208_state *isl1208 = priv;
|
||||
struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
|
||||
int ret;
|
||||
|
||||
/* nvmem sanitizes offset/count for us, but count==0 is possible */
|
||||
if (!count)
|
||||
return count;
|
||||
ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf,
|
||||
count);
|
||||
return ret == 0 ? count : ret;
|
||||
}
|
||||
|
||||
static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct isl1208_state *isl1208 = priv;
|
||||
struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
|
||||
int ret;
|
||||
|
||||
/* nvmem sanitizes off/count for us, but count==0 is possible */
|
||||
if (!count)
|
||||
return count;
|
||||
ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf,
|
||||
count);
|
||||
|
||||
return ret == 0 ? count : ret;
|
||||
}
|
||||
|
||||
static const struct nvmem_config isl1208_nvmem_config = {
|
||||
.name = "isl1208_nvram",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
/* .size from chip specific config */
|
||||
.reg_read = isl1208_nvmem_read,
|
||||
.reg_write = isl1208_nvmem_write,
|
||||
};
|
||||
|
||||
static int isl1208_setup_irq(struct i2c_client *client, int irq)
|
||||
{
|
||||
int rc = devm_request_threaded_irq(&client->dev, irq, NULL,
|
||||
|
@ -722,7 +805,7 @@ static int
|
|||
isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int rc = 0;
|
||||
struct rtc_device *rtc;
|
||||
struct isl1208_state *isl1208;
|
||||
int evdet_irq = -1;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
|
@ -731,13 +814,33 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
if (isl1208_i2c_validate_client(client) < 0)
|
||||
return -ENODEV;
|
||||
|
||||
rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
/* Allocate driver state, point i2c client data to it */
|
||||
isl1208 = devm_kzalloc(&client->dev, sizeof(*isl1208), GFP_KERNEL);
|
||||
if (!isl1208)
|
||||
return -ENOMEM;
|
||||
i2c_set_clientdata(client, isl1208);
|
||||
|
||||
rtc->ops = &isl1208_rtc_ops;
|
||||
/* Determine which chip we have */
|
||||
if (client->dev.of_node) {
|
||||
isl1208->config = of_device_get_match_data(&client->dev);
|
||||
if (!isl1208->config)
|
||||
return -ENODEV;
|
||||
} else {
|
||||
if (id->driver_data >= ISL_LAST_ID)
|
||||
return -ENODEV;
|
||||
isl1208->config = &isl1208_configs[id->driver_data];
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, rtc);
|
||||
isl1208->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(isl1208->rtc))
|
||||
return PTR_ERR(isl1208->rtc);
|
||||
|
||||
isl1208->rtc->ops = &isl1208_rtc_ops;
|
||||
|
||||
/* Setup nvmem configuration in driver state struct */
|
||||
isl1208->nvmem_config = isl1208_nvmem_config;
|
||||
isl1208->nvmem_config.size = isl1208->config->nvmem_length;
|
||||
isl1208->nvmem_config.priv = isl1208;
|
||||
|
||||
rc = isl1208_i2c_get_sr(client);
|
||||
if (rc < 0) {
|
||||
|
@ -749,7 +852,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
dev_warn(&client->dev, "rtc power failure detected, "
|
||||
"please set clock.\n");
|
||||
|
||||
if (id->driver_data == TYPE_ISL1219) {
|
||||
if (isl1208->config->has_tamper) {
|
||||
struct device_node *np = client->dev.of_node;
|
||||
u32 evienb;
|
||||
|
||||
|
@ -770,13 +873,15 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
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");
|
||||
}
|
||||
if (isl1208->config->has_timestamp) {
|
||||
rc = rtc_add_group(isl1208->rtc, &isl1219_rtc_sysfs_files);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = rtc_add_group(rtc, &isl1208_rtc_sysfs_files);
|
||||
rc = rtc_add_group(isl1208->rtc, &isl1208_rtc_sysfs_files);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -790,25 +895,13 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
return rtc_register_device(rtc);
|
||||
rc = rtc_nvmem_register(isl1208->rtc, &isl1208->nvmem_config);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return rtc_register_device(isl1208->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",
|
||||
|
|
|
@ -82,7 +82,7 @@ unsigned int mc146818_get_time(struct rtc_time *time)
|
|||
time->tm_year += real_year - 72;
|
||||
#endif
|
||||
|
||||
if (century)
|
||||
if (century > 20)
|
||||
time->tm_year += (century - 19) * 100;
|
||||
|
||||
/*
|
||||
|
|
|
@ -0,0 +1,407 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* RTC driver for the interal RTC block in the Amlogic Meson6, Meson8,
|
||||
* Meson8b and Meson8m2 SoCs.
|
||||
*
|
||||
* The RTC is split in to two parts, the AHB front end and a simple serial
|
||||
* connection to the actual registers. This driver manages both parts.
|
||||
*
|
||||
* Copyright (c) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
|
||||
* Copyright (c) 2015 Ben Dooks <ben.dooks@codethink.co.uk> for Codethink Ltd
|
||||
* Based on origin by Carlo Caione <carlo@endlessm.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
/* registers accessed from cpu bus */
|
||||
#define RTC_ADDR0 0x00
|
||||
#define RTC_ADDR0_LINE_SCLK BIT(0)
|
||||
#define RTC_ADDR0_LINE_SEN BIT(1)
|
||||
#define RTC_ADDR0_LINE_SDI BIT(2)
|
||||
#define RTC_ADDR0_START_SER BIT(17)
|
||||
#define RTC_ADDR0_WAIT_SER BIT(22)
|
||||
#define RTC_ADDR0_DATA GENMASK(31, 24)
|
||||
|
||||
#define RTC_ADDR1 0x04
|
||||
#define RTC_ADDR1_SDO BIT(0)
|
||||
#define RTC_ADDR1_S_READY BIT(1)
|
||||
|
||||
#define RTC_ADDR2 0x08
|
||||
#define RTC_ADDR3 0x0c
|
||||
|
||||
#define RTC_REG4 0x10
|
||||
#define RTC_REG4_STATIC_VALUE GENMASK(7, 0)
|
||||
|
||||
/* rtc registers accessed via rtc-serial interface */
|
||||
#define RTC_COUNTER (0)
|
||||
#define RTC_SEC_ADJ (2)
|
||||
#define RTC_REGMEM_0 (4)
|
||||
#define RTC_REGMEM_1 (5)
|
||||
#define RTC_REGMEM_2 (6)
|
||||
#define RTC_REGMEM_3 (7)
|
||||
|
||||
#define RTC_ADDR_BITS (3) /* number of address bits to send */
|
||||
#define RTC_DATA_BITS (32) /* number of data bits to tx/rx */
|
||||
|
||||
#define MESON_STATIC_BIAS_CUR (0x5 << 1)
|
||||
#define MESON_STATIC_VOLTAGE (0x3 << 11)
|
||||
#define MESON_STATIC_DEFAULT (MESON_STATIC_BIAS_CUR | MESON_STATIC_VOLTAGE)
|
||||
|
||||
struct meson_rtc {
|
||||
struct rtc_device *rtc; /* rtc device we created */
|
||||
struct device *dev; /* device we bound from */
|
||||
struct reset_control *reset; /* reset source */
|
||||
struct regulator *vdd; /* voltage input */
|
||||
struct regmap *peripheral; /* peripheral registers */
|
||||
struct regmap *serial; /* serial registers */
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_rtc_peripheral_regmap_config = {
|
||||
.name = "peripheral-registers",
|
||||
.reg_bits = 8,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = RTC_REG4,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
/* RTC front-end serialiser controls */
|
||||
|
||||
static void meson_rtc_sclk_pulse(struct meson_rtc *rtc)
|
||||
{
|
||||
udelay(5);
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK, 0);
|
||||
udelay(5);
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SCLK,
|
||||
RTC_ADDR0_LINE_SCLK);
|
||||
}
|
||||
|
||||
static void meson_rtc_send_bit(struct meson_rtc *rtc, unsigned int bit)
|
||||
{
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI,
|
||||
bit ? RTC_ADDR0_LINE_SDI : 0);
|
||||
meson_rtc_sclk_pulse(rtc);
|
||||
}
|
||||
|
||||
static void meson_rtc_send_bits(struct meson_rtc *rtc, u32 data,
|
||||
unsigned int nr)
|
||||
{
|
||||
u32 bit = 1 << (nr - 1);
|
||||
|
||||
while (bit) {
|
||||
meson_rtc_send_bit(rtc, data & bit);
|
||||
bit >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void meson_rtc_set_dir(struct meson_rtc *rtc, u32 mode)
|
||||
{
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN, 0);
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0);
|
||||
meson_rtc_send_bit(rtc, mode);
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SDI, 0);
|
||||
}
|
||||
|
||||
static u32 meson_rtc_get_data(struct meson_rtc *rtc)
|
||||
{
|
||||
u32 tmp, val = 0;
|
||||
int bit;
|
||||
|
||||
for (bit = 0; bit < RTC_DATA_BITS; bit++) {
|
||||
meson_rtc_sclk_pulse(rtc);
|
||||
val <<= 1;
|
||||
|
||||
regmap_read(rtc->peripheral, RTC_ADDR1, &tmp);
|
||||
val |= tmp & RTC_ADDR1_SDO;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int meson_rtc_get_bus(struct meson_rtc *rtc)
|
||||
{
|
||||
int ret, retries = 3;
|
||||
u32 val;
|
||||
|
||||
/* prepare bus for transfers, set all lines low */
|
||||
val = RTC_ADDR0_LINE_SDI | RTC_ADDR0_LINE_SEN | RTC_ADDR0_LINE_SCLK;
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, val, 0);
|
||||
|
||||
for (retries = 0; retries < 3; retries++) {
|
||||
/* wait for the bus to be ready */
|
||||
if (!regmap_read_poll_timeout(rtc->peripheral, RTC_ADDR1, val,
|
||||
val & RTC_ADDR1_S_READY, 10,
|
||||
10000))
|
||||
return 0;
|
||||
|
||||
dev_warn(rtc->dev, "failed to get bus, resetting RTC\n");
|
||||
|
||||
ret = reset_control_reset(rtc->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_err(rtc->dev, "bus is not ready\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int meson_rtc_serial_bus_reg_read(void *context, unsigned int reg,
|
||||
unsigned int *data)
|
||||
{
|
||||
struct meson_rtc *rtc = context;
|
||||
int ret;
|
||||
|
||||
ret = meson_rtc_get_bus(rtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN,
|
||||
RTC_ADDR0_LINE_SEN);
|
||||
meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS);
|
||||
meson_rtc_set_dir(rtc, 0);
|
||||
*data = meson_rtc_get_data(rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_rtc_serial_bus_reg_write(void *context, unsigned int reg,
|
||||
unsigned int data)
|
||||
{
|
||||
struct meson_rtc *rtc = context;
|
||||
int ret;
|
||||
|
||||
ret = meson_rtc_get_bus(rtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0, RTC_ADDR0_LINE_SEN,
|
||||
RTC_ADDR0_LINE_SEN);
|
||||
meson_rtc_send_bits(rtc, data, RTC_DATA_BITS);
|
||||
meson_rtc_send_bits(rtc, reg, RTC_ADDR_BITS);
|
||||
meson_rtc_set_dir(rtc, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct regmap_bus meson_rtc_serial_bus = {
|
||||
.reg_read = meson_rtc_serial_bus_reg_read,
|
||||
.reg_write = meson_rtc_serial_bus_reg_write,
|
||||
};
|
||||
|
||||
static const struct regmap_config meson_rtc_serial_regmap_config = {
|
||||
.name = "serial-registers",
|
||||
.reg_bits = 4,
|
||||
.reg_stride = 1,
|
||||
.val_bits = 32,
|
||||
.max_register = RTC_REGMEM_3,
|
||||
.fast_io = false,
|
||||
};
|
||||
|
||||
static int meson_rtc_write_static(struct meson_rtc *rtc, u32 data)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
regmap_write(rtc->peripheral, RTC_REG4,
|
||||
FIELD_PREP(RTC_REG4_STATIC_VALUE, (data >> 8)));
|
||||
|
||||
/* write the static value and start the auto serializer */
|
||||
tmp = FIELD_PREP(RTC_ADDR0_DATA, (data & 0xff)) | RTC_ADDR0_START_SER;
|
||||
regmap_update_bits(rtc->peripheral, RTC_ADDR0,
|
||||
RTC_ADDR0_DATA | RTC_ADDR0_START_SER, tmp);
|
||||
|
||||
/* wait for the auto serializer to complete */
|
||||
return regmap_read_poll_timeout(rtc->peripheral, RTC_REG4, tmp,
|
||||
!(tmp & RTC_ADDR0_WAIT_SER), 10,
|
||||
10000);
|
||||
}
|
||||
|
||||
/* RTC interface layer functions */
|
||||
|
||||
static int meson_rtc_gettime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct meson_rtc *rtc = dev_get_drvdata(dev);
|
||||
u32 time;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rtc->serial, RTC_COUNTER, &time);
|
||||
if (!ret)
|
||||
rtc_time64_to_tm(time, tm);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int meson_rtc_settime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct meson_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
||||
return regmap_write(rtc->serial, RTC_COUNTER, rtc_tm_to_time64(tm));
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops meson_rtc_ops = {
|
||||
.read_time = meson_rtc_gettime,
|
||||
.set_time = meson_rtc_settime,
|
||||
};
|
||||
|
||||
/* NVMEM interface layer functions */
|
||||
|
||||
static int meson_rtc_regmem_read(void *context, unsigned int offset,
|
||||
void *buf, size_t bytes)
|
||||
{
|
||||
struct meson_rtc *rtc = context;
|
||||
unsigned int read_offset, read_size;
|
||||
|
||||
read_offset = RTC_REGMEM_0 + (offset / 4);
|
||||
read_size = bytes / 4;
|
||||
|
||||
return regmap_bulk_read(rtc->serial, read_offset, buf, read_size);
|
||||
}
|
||||
|
||||
static int meson_rtc_regmem_write(void *context, unsigned int offset,
|
||||
void *buf, size_t bytes)
|
||||
{
|
||||
struct meson_rtc *rtc = context;
|
||||
unsigned int write_offset, write_size;
|
||||
|
||||
write_offset = RTC_REGMEM_0 + (offset / 4);
|
||||
write_size = bytes / 4;
|
||||
|
||||
return regmap_bulk_write(rtc->serial, write_offset, buf, write_size);
|
||||
}
|
||||
|
||||
static int meson_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct nvmem_config meson_rtc_nvmem_config = {
|
||||
.name = "meson-rtc-regmem",
|
||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||
.word_size = 4,
|
||||
.stride = 4,
|
||||
.size = 4 * 4,
|
||||
.reg_read = meson_rtc_regmem_read,
|
||||
.reg_write = meson_rtc_regmem_write,
|
||||
};
|
||||
struct device *dev = &pdev->dev;
|
||||
struct meson_rtc *rtc;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
u32 tm;
|
||||
|
||||
rtc = devm_kzalloc(dev, sizeof(struct meson_rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
rtc->rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtc))
|
||||
return PTR_ERR(rtc->rtc);
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->dev = dev;
|
||||
|
||||
rtc->rtc->ops = &meson_rtc_ops;
|
||||
rtc->rtc->range_max = U32_MAX;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
rtc->peripheral = devm_regmap_init_mmio(dev, base,
|
||||
&meson_rtc_peripheral_regmap_config);
|
||||
if (IS_ERR(rtc->peripheral)) {
|
||||
dev_err(dev, "failed to create peripheral regmap\n");
|
||||
return PTR_ERR(rtc->peripheral);
|
||||
}
|
||||
|
||||
rtc->reset = devm_reset_control_get(dev, NULL);
|
||||
if (IS_ERR(rtc->reset)) {
|
||||
dev_err(dev, "missing reset line\n");
|
||||
return PTR_ERR(rtc->reset);
|
||||
}
|
||||
|
||||
rtc->vdd = devm_regulator_get(dev, "vdd");
|
||||
if (IS_ERR(rtc->vdd)) {
|
||||
dev_err(dev, "failed to get the vdd-supply\n");
|
||||
return PTR_ERR(rtc->vdd);
|
||||
}
|
||||
|
||||
ret = regulator_enable(rtc->vdd);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable vdd-supply\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = meson_rtc_write_static(rtc, MESON_STATIC_DEFAULT);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to set static values\n");
|
||||
goto out_disable_vdd;
|
||||
}
|
||||
|
||||
rtc->serial = devm_regmap_init(dev, &meson_rtc_serial_bus, rtc,
|
||||
&meson_rtc_serial_regmap_config);
|
||||
if (IS_ERR(rtc->serial)) {
|
||||
dev_err(dev, "failed to create serial regmap\n");
|
||||
ret = PTR_ERR(rtc->serial);
|
||||
goto out_disable_vdd;
|
||||
}
|
||||
|
||||
/*
|
||||
* check if we can read RTC counter, if not then the RTC is probably
|
||||
* not functional. If it isn't probably best to not bind.
|
||||
*/
|
||||
ret = regmap_read(rtc->serial, RTC_COUNTER, &tm);
|
||||
if (ret) {
|
||||
dev_err(dev, "cannot read RTC counter, RTC not functional\n");
|
||||
goto out_disable_vdd;
|
||||
}
|
||||
|
||||
meson_rtc_nvmem_config.priv = rtc;
|
||||
ret = rtc_nvmem_register(rtc->rtc, &meson_rtc_nvmem_config);
|
||||
if (ret)
|
||||
goto out_disable_vdd;
|
||||
|
||||
ret = rtc_register_device(rtc->rtc);
|
||||
if (ret)
|
||||
goto out_disable_vdd;
|
||||
|
||||
return 0;
|
||||
|
||||
out_disable_vdd:
|
||||
regulator_disable(rtc->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id meson_rtc_dt_match[] = {
|
||||
{ .compatible = "amlogic,meson6-rtc", },
|
||||
{ .compatible = "amlogic,meson8-rtc", },
|
||||
{ .compatible = "amlogic,meson8b-rtc", },
|
||||
{ .compatible = "amlogic,meson8m2-rtc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, meson_rtc_dt_match);
|
||||
|
||||
static struct platform_driver meson_rtc_driver = {
|
||||
.probe = meson_rtc_probe,
|
||||
.driver = {
|
||||
.name = "meson-rtc",
|
||||
.of_match_table = of_match_ptr(meson_rtc_dt_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(meson_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Amlogic Meson RTC Driver");
|
||||
MODULE_AUTHOR("Ben Dooks <ben.doosk@codethink.co.uk>");
|
||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:meson-rtc");
|
|
@ -27,17 +27,11 @@
|
|||
*/
|
||||
|
||||
#define PCF85063_REG_CTRL1 0x00 /* status */
|
||||
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
|
||||
#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;
|
||||
|
||||
|
@ -180,6 +174,39 @@ static const struct rtc_class_ops pcf85063_rtc_ops = {
|
|||
.set_time = pcf85063_rtc_set_time
|
||||
};
|
||||
|
||||
static int pcf85063_load_capacitance(struct i2c_client *client)
|
||||
{
|
||||
u32 load;
|
||||
int rc;
|
||||
u8 reg;
|
||||
|
||||
rc = i2c_smbus_read_byte_data(client, PCF85063_REG_CTRL1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
reg = rc;
|
||||
load = 7000;
|
||||
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 7000",
|
||||
load);
|
||||
/* fall through */
|
||||
case 7000:
|
||||
reg &= ~PCF85063_REG_CTRL1_CAP_SEL;
|
||||
break;
|
||||
case 12500:
|
||||
reg |= PCF85063_REG_CTRL1_CAP_SEL;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = i2c_smbus_write_byte_data(client, PCF85063_REG_CTRL1, reg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pcf85063_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -197,6 +224,11 @@ static int pcf85063_probe(struct i2c_client *client,
|
|||
return err;
|
||||
}
|
||||
|
||||
err = pcf85063_load_capacitance(client);
|
||||
if (err < 0)
|
||||
dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
|
||||
err);
|
||||
|
||||
rtc = devm_rtc_device_register(&client->dev,
|
||||
pcf85063_driver.driver.name,
|
||||
&pcf85063_rtc_ops, THIS_MODULE);
|
||||
|
|
|
@ -97,8 +97,9 @@ static int pcf8523_voltage_low(struct i2c_client *client)
|
|||
return !!(value & REG_CONTROL3_BLF);
|
||||
}
|
||||
|
||||
static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
|
||||
static int pcf8523_load_capacitance(struct i2c_client *client)
|
||||
{
|
||||
u32 load;
|
||||
u8 value;
|
||||
int err;
|
||||
|
||||
|
@ -106,14 +107,24 @@ static int pcf8523_select_capacitance(struct i2c_client *client, bool high)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!high)
|
||||
value &= ~REG_CONTROL1_CAP_SEL;
|
||||
else
|
||||
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);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -347,9 +358,10 @@ static int pcf8523_probe(struct i2c_client *client,
|
|||
if (!pcf)
|
||||
return -ENOMEM;
|
||||
|
||||
err = pcf8523_select_capacitance(client, true);
|
||||
err = pcf8523_load_capacitance(client);
|
||||
if (err < 0)
|
||||
return err;
|
||||
dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
|
||||
err);
|
||||
|
||||
err = pcf8523_set_pm(client, 0);
|
||||
if (err < 0)
|
||||
|
@ -374,6 +386,7 @@ MODULE_DEVICE_TABLE(i2c, pcf8523_id);
|
|||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pcf8523_of_match[] = {
|
||||
{ .compatible = "nxp,pcf8523" },
|
||||
{ .compatible = "microcrystal,rv8523" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pcf8523_of_match);
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* 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>
|
||||
|
@ -180,22 +172,16 @@ 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 %ptR\n", tm);
|
||||
|
||||
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);
|
||||
writeb(bin2bcd(tm->tm_year - 100), base + PIC32_RTCYEAR);
|
||||
clk_disable(pdata->clk);
|
||||
|
||||
return 0;
|
||||
|
@ -348,13 +334,17 @@ static int pic32_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
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);
|
||||
pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(pdata->rtc))
|
||||
return PTR_ERR(pdata->rtc);
|
||||
|
||||
pdata->rtc->ops = &pic32_rtcops;
|
||||
pdata->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
pdata->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
ret = rtc_register_device(pdata->rtc);
|
||||
if (ret)
|
||||
goto err_nortc;
|
||||
}
|
||||
|
||||
pdata->rtc->max_user_freq = 128;
|
||||
|
||||
|
|
|
@ -213,7 +213,8 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
}
|
||||
}
|
||||
|
||||
secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
|
||||
secs = value[0] | (value[1] << 8) | (value[2] << 16) |
|
||||
((unsigned long)value[3] << 24);
|
||||
|
||||
rtc_time_to_tm(secs, tm);
|
||||
|
||||
|
@ -284,7 +285,8 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
|||
return rc;
|
||||
}
|
||||
|
||||
secs = value[0] | (value[1] << 8) | (value[2] << 16) | (value[3] << 24);
|
||||
secs = value[0] | (value[1] << 8) | (value[2] << 16) |
|
||||
((unsigned long)value[3] << 24);
|
||||
|
||||
rtc_time_to_tm(secs, &alarm->time);
|
||||
|
||||
|
|
|
@ -52,8 +52,10 @@
|
|||
# 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_XSTP (1 << 4) /* only if !R2x2x */
|
||||
# define R2x2x_CTRL2_VDET (1 << 6) /* only if R2x2x */
|
||||
# define R2x2x_CTRL2_XSTP (1 << 5) /* only if R2x2x */
|
||||
# define R2x2x_CTRL2_PON (1 << 4) /* only if R2x2x */
|
||||
# define RS5C_CTRL2_CTFG (1 << 2)
|
||||
# define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */
|
||||
# define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */
|
||||
|
@ -212,10 +214,27 @@ 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);
|
||||
unsigned char ctrl2 = rs5c->regs[RS5C_REG_CTRL2];
|
||||
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
switch (rs5c->type) {
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
|
||||
(rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) {
|
||||
dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (ctrl2 & RS5C_CTRL2_XSTP) {
|
||||
dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
|
@ -243,6 +262,7 @@ 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];
|
||||
unsigned char ctrl2;
|
||||
int addr;
|
||||
|
||||
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
|
||||
|
@ -261,7 +281,32 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
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__);
|
||||
dev_dbg(&client->dev, "%s: write error in line %i\n",
|
||||
__func__, __LINE__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
addr = RS5C_ADDR(RS5C_REG_CTRL2);
|
||||
ctrl2 = i2c_smbus_read_byte_data(client, addr);
|
||||
|
||||
/* clear rtc warning bits */
|
||||
switch (rs5c->type) {
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON);
|
||||
if (rs5c->type == rtc_r2025sd)
|
||||
ctrl2 |= R2x2x_CTRL2_XSTP;
|
||||
else
|
||||
ctrl2 &= ~R2x2x_CTRL2_XSTP;
|
||||
break;
|
||||
default:
|
||||
ctrl2 &= ~RS5C_CTRL2_XSTP;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i2c_smbus_write_byte_data(client, addr, ctrl2) < 0) {
|
||||
dev_dbg(&client->dev, "%s: write error in line %i\n",
|
||||
__func__, __LINE__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -519,20 +564,25 @@ 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];
|
||||
|
||||
switch (rs5c372->type) {
|
||||
case rtc_r2025sd:
|
||||
if (buf[1] & R2x2x_CTRL2_XSTP)
|
||||
return ret;
|
||||
break;
|
||||
case rtc_r2221tl:
|
||||
if (!(buf[1] & R2x2x_CTRL2_XSTP))
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
if (!(buf[1] & RS5C_CTRL2_XSTP))
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
|
||||
/* use 24hr mode */
|
||||
switch (rs5c372->type) {
|
||||
case rtc_rs5c372a:
|
||||
|
|
|
@ -0,0 +1,732 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* RTC driver for the Micro Crystal RV3028
|
||||
*
|
||||
* Copyright (C) 2019 Micro Crystal SA
|
||||
*
|
||||
* Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define RV3028_SEC 0x00
|
||||
#define RV3028_MIN 0x01
|
||||
#define RV3028_HOUR 0x02
|
||||
#define RV3028_WDAY 0x03
|
||||
#define RV3028_DAY 0x04
|
||||
#define RV3028_MONTH 0x05
|
||||
#define RV3028_YEAR 0x06
|
||||
#define RV3028_ALARM_MIN 0x07
|
||||
#define RV3028_ALARM_HOUR 0x08
|
||||
#define RV3028_ALARM_DAY 0x09
|
||||
#define RV3028_STATUS 0x0E
|
||||
#define RV3028_CTRL1 0x0F
|
||||
#define RV3028_CTRL2 0x10
|
||||
#define RV3028_EVT_CTRL 0x13
|
||||
#define RV3028_TS_COUNT 0x14
|
||||
#define RV3028_TS_SEC 0x15
|
||||
#define RV3028_RAM1 0x1F
|
||||
#define RV3028_EEPROM_ADDR 0x25
|
||||
#define RV3028_EEPROM_DATA 0x26
|
||||
#define RV3028_EEPROM_CMD 0x27
|
||||
#define RV3028_CLKOUT 0x35
|
||||
#define RV3028_OFFSET 0x36
|
||||
#define RV3028_BACKUP 0x37
|
||||
|
||||
#define RV3028_STATUS_PORF BIT(0)
|
||||
#define RV3028_STATUS_EVF BIT(1)
|
||||
#define RV3028_STATUS_AF BIT(2)
|
||||
#define RV3028_STATUS_TF BIT(3)
|
||||
#define RV3028_STATUS_UF BIT(4)
|
||||
#define RV3028_STATUS_BSF BIT(5)
|
||||
#define RV3028_STATUS_CLKF BIT(6)
|
||||
#define RV3028_STATUS_EEBUSY BIT(7)
|
||||
|
||||
#define RV3028_CTRL1_EERD BIT(3)
|
||||
#define RV3028_CTRL1_WADA BIT(5)
|
||||
|
||||
#define RV3028_CTRL2_RESET BIT(0)
|
||||
#define RV3028_CTRL2_12_24 BIT(1)
|
||||
#define RV3028_CTRL2_EIE BIT(2)
|
||||
#define RV3028_CTRL2_AIE BIT(3)
|
||||
#define RV3028_CTRL2_TIE BIT(4)
|
||||
#define RV3028_CTRL2_UIE BIT(5)
|
||||
#define RV3028_CTRL2_TSE BIT(7)
|
||||
|
||||
#define RV3028_EVT_CTRL_TSR BIT(2)
|
||||
|
||||
#define RV3028_EEPROM_CMD_WRITE 0x21
|
||||
#define RV3028_EEPROM_CMD_READ 0x22
|
||||
|
||||
#define RV3028_EEBUSY_POLL 10000
|
||||
#define RV3028_EEBUSY_TIMEOUT 100000
|
||||
|
||||
#define RV3028_BACKUP_TCE BIT(5)
|
||||
#define RV3028_BACKUP_TCR_MASK GENMASK(1,0)
|
||||
|
||||
#define OFFSET_STEP_PPT 953674
|
||||
|
||||
enum rv3028_type {
|
||||
rv_3028,
|
||||
};
|
||||
|
||||
struct rv3028_data {
|
||||
struct regmap *regmap;
|
||||
struct rtc_device *rtc;
|
||||
enum rv3028_type type;
|
||||
};
|
||||
|
||||
static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000};
|
||||
|
||||
static ssize_t timestamp0_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
|
||||
|
||||
regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR,
|
||||
RV3028_EVT_CTRL_TSR);
|
||||
|
||||
return count;
|
||||
};
|
||||
|
||||
static ssize_t timestamp0_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
|
||||
struct rtc_time tm;
|
||||
int ret, count;
|
||||
u8 date[6];
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date,
|
||||
sizeof(date));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tm.tm_sec = bcd2bin(date[0]);
|
||||
tm.tm_min = bcd2bin(date[1]);
|
||||
tm.tm_hour = bcd2bin(date[2]);
|
||||
tm.tm_mday = bcd2bin(date[3]);
|
||||
tm.tm_mon = bcd2bin(date[4]) - 1;
|
||||
tm.tm_year = bcd2bin(date[5]) + 100;
|
||||
|
||||
ret = rtc_valid_tm(&tm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%llu\n",
|
||||
(unsigned long long)rtc_tm_to_time64(&tm));
|
||||
};
|
||||
|
||||
static DEVICE_ATTR_RW(timestamp0);
|
||||
|
||||
static ssize_t timestamp0_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
|
||||
int ret, count;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", count);
|
||||
};
|
||||
|
||||
static DEVICE_ATTR_RO(timestamp0_count);
|
||||
|
||||
static struct attribute *rv3028_attrs[] = {
|
||||
&dev_attr_timestamp0.attr,
|
||||
&dev_attr_timestamp0_count.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group rv3028_attr_group = {
|
||||
.attrs = rv3028_attrs,
|
||||
};
|
||||
|
||||
static irqreturn_t rv3028_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_id;
|
||||
unsigned long events = 0;
|
||||
u32 status = 0, ctrl = 0;
|
||||
|
||||
if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 ||
|
||||
status == 0) {
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (status & RV3028_STATUS_PORF)
|
||||
dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n");
|
||||
|
||||
if (status & RV3028_STATUS_TF) {
|
||||
status |= RV3028_STATUS_TF;
|
||||
ctrl |= RV3028_CTRL2_TIE;
|
||||
events |= RTC_PF;
|
||||
}
|
||||
|
||||
if (status & RV3028_STATUS_AF) {
|
||||
status |= RV3028_STATUS_AF;
|
||||
ctrl |= RV3028_CTRL2_AIE;
|
||||
events |= RTC_AF;
|
||||
}
|
||||
|
||||
if (status & RV3028_STATUS_UF) {
|
||||
status |= RV3028_STATUS_UF;
|
||||
ctrl |= RV3028_CTRL2_UIE;
|
||||
events |= RTC_UF;
|
||||
}
|
||||
|
||||
if (events) {
|
||||
rtc_update_irq(rv3028->rtc, 1, events);
|
||||
regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0);
|
||||
regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0);
|
||||
}
|
||||
|
||||
if (status & RV3028_STATUS_EVF) {
|
||||
sysfs_notify(&rv3028->rtc->dev.kobj, NULL,
|
||||
dev_attr_timestamp0.attr.name);
|
||||
dev_warn(&rv3028->rtc->dev, "event detected");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rv3028_get_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int ret, status;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (status & RV3028_STATUS_PORF) {
|
||||
dev_warn(dev, "Voltage low, data is invalid.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tm->tm_sec = bcd2bin(date[RV3028_SEC] & 0x7f);
|
||||
tm->tm_min = bcd2bin(date[RV3028_MIN] & 0x7f);
|
||||
tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f);
|
||||
tm->tm_wday = ilog2(date[RV3028_WDAY] & 0x7f);
|
||||
tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f);
|
||||
tm->tm_mon = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1;
|
||||
tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3028_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
u8 date[7];
|
||||
int ret;
|
||||
|
||||
date[RV3028_SEC] = bin2bcd(tm->tm_sec);
|
||||
date[RV3028_MIN] = bin2bcd(tm->tm_min);
|
||||
date[RV3028_HOUR] = bin2bcd(tm->tm_hour);
|
||||
date[RV3028_WDAY] = 1 << (tm->tm_wday);
|
||||
date[RV3028_DAY] = bin2bcd(tm->tm_mday);
|
||||
date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1);
|
||||
date[RV3028_YEAR] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
/*
|
||||
* Writing to the Seconds register has the same effect as setting RESET
|
||||
* bit to 1
|
||||
*/
|
||||
ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date,
|
||||
sizeof(date));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
|
||||
RV3028_STATUS_PORF, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
u8 alarmvals[3];
|
||||
int status, ctrl, ret;
|
||||
|
||||
ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals,
|
||||
sizeof(alarmvals));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
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 = !!(ctrl & RV3028_CTRL2_AIE);
|
||||
alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
u8 alarmvals[3];
|
||||
u8 ctrl = 0;
|
||||
int ret;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
|
||||
RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarmvals[0] = bin2bcd(alrm->time.tm_min);
|
||||
alarmvals[1] = bin2bcd(alrm->time.tm_hour);
|
||||
alarmvals[2] = bin2bcd(alrm->time.tm_mday);
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
|
||||
RV3028_STATUS_AF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals,
|
||||
sizeof(alarmvals));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alrm->enabled) {
|
||||
if (rv3028->rtc->uie_rtctimer.enabled)
|
||||
ctrl |= RV3028_CTRL2_UIE;
|
||||
if (rv3028->rtc->aie_timer.enabled)
|
||||
ctrl |= RV3028_CTRL2_AIE;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
|
||||
RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
int ctrl = 0, ret;
|
||||
|
||||
if (enabled) {
|
||||
if (rv3028->rtc->uie_rtctimer.enabled)
|
||||
ctrl |= RV3028_CTRL2_UIE;
|
||||
if (rv3028->rtc->aie_timer.enabled)
|
||||
ctrl |= RV3028_CTRL2_AIE;
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
|
||||
RV3028_STATUS_AF | RV3028_STATUS_UF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
|
||||
RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3028_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
int ret, value, steps;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
steps = sign_extend32(value << 1, 8);
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
steps += value >> 7;
|
||||
|
||||
*offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3028_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
offset = clamp(offset, -244141L, 243187L) * 1000;
|
||||
offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT);
|
||||
|
||||
ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7),
|
||||
offset << 7);
|
||||
}
|
||||
|
||||
static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev);
|
||||
int status, ret = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (status & RV3028_STATUS_PORF)
|
||||
dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n");
|
||||
|
||||
status &= RV3028_STATUS_PORF;
|
||||
|
||||
if (copy_to_user((void __user *)arg, &status, sizeof(int)))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case RTC_VL_CLR:
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS,
|
||||
RV3028_STATUS_PORF, 0);
|
||||
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
static int rv3028_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes);
|
||||
}
|
||||
|
||||
static int rv3028_nvram_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes);
|
||||
}
|
||||
|
||||
static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
u32 status, ctrl1;
|
||||
int i, ret, err;
|
||||
u8 *buf = val;
|
||||
|
||||
ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD)) {
|
||||
ret = regmap_update_bits(priv, RV3028_CTRL1,
|
||||
RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD,
|
||||
RV3028_EEPROM_CMD_WRITE);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT);
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
}
|
||||
|
||||
restore_eerd:
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD))
|
||||
{
|
||||
err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
|
||||
0);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
u32 status, ctrl1, data;
|
||||
int i, ret, err;
|
||||
u8 *buf = val;
|
||||
|
||||
ret = regmap_read(priv, RV3028_CTRL1, &ctrl1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD)) {
|
||||
ret = regmap_update_bits(priv, RV3028_CTRL1,
|
||||
RV3028_CTRL1_EERD, RV3028_CTRL1_EERD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
}
|
||||
|
||||
for (i = 0; i < bytes; i++) {
|
||||
ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_write(priv, RV3028_EEPROM_CMD,
|
||||
RV3028_EEPROM_CMD_READ);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status,
|
||||
!(status & RV3028_STATUS_EEBUSY),
|
||||
RV3028_EEBUSY_POLL,
|
||||
RV3028_EEBUSY_TIMEOUT);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
|
||||
ret = regmap_read(priv, RV3028_EEPROM_DATA, &data);
|
||||
if (ret)
|
||||
goto restore_eerd;
|
||||
buf[i] = data;
|
||||
}
|
||||
|
||||
restore_eerd:
|
||||
if (!(ctrl1 & RV3028_CTRL1_EERD))
|
||||
{
|
||||
err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD,
|
||||
0);
|
||||
if (err && !ret)
|
||||
ret = err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct rtc_class_ops rv3028_rtc_ops = {
|
||||
.read_time = rv3028_get_time,
|
||||
.set_time = rv3028_set_time,
|
||||
.read_offset = rv3028_read_offset,
|
||||
.set_offset = rv3028_set_offset,
|
||||
.ioctl = rv3028_ioctl,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x37,
|
||||
};
|
||||
|
||||
static int rv3028_probe(struct i2c_client *client)
|
||||
{
|
||||
struct rv3028_data *rv3028;
|
||||
int ret, status;
|
||||
u32 ohms;
|
||||
struct nvmem_config nvmem_cfg = {
|
||||
.name = "rv3028_nvram",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = 2,
|
||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||
.reg_read = rv3028_nvram_read,
|
||||
.reg_write = rv3028_nvram_write,
|
||||
};
|
||||
struct nvmem_config eeprom_cfg = {
|
||||
.name = "rv3028_eeprom",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = 43,
|
||||
.type = NVMEM_TYPE_EEPROM,
|
||||
.reg_read = rv3028_eeprom_read,
|
||||
.reg_write = rv3028_eeprom_write,
|
||||
};
|
||||
|
||||
rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data),
|
||||
GFP_KERNEL);
|
||||
if (!rv3028)
|
||||
return -ENOMEM;
|
||||
|
||||
rv3028->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
|
||||
i2c_set_clientdata(client, rv3028);
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (status & RV3028_STATUS_PORF)
|
||||
dev_warn(&client->dev, "Voltage low, data loss detected.\n");
|
||||
|
||||
if (status & RV3028_STATUS_AF)
|
||||
dev_warn(&client->dev, "An alarm may have been missed.\n");
|
||||
|
||||
rv3028->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(rv3028->rtc)) {
|
||||
return PTR_ERR(rv3028->rtc);
|
||||
}
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, rv3028_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"rv3028", rv3028);
|
||||
if (ret) {
|
||||
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
client->irq = 0;
|
||||
} else {
|
||||
rv3028_rtc_ops.read_alarm = rv3028_get_alarm;
|
||||
rv3028_rtc_ops.set_alarm = rv3028_set_alarm;
|
||||
rv3028_rtc_ops.alarm_irq_enable = rv3028_alarm_irq_enable;
|
||||
}
|
||||
}
|
||||
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1,
|
||||
RV3028_CTRL1_WADA, RV3028_CTRL1_WADA);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* setup timestamping */
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2,
|
||||
RV3028_CTRL2_EIE | RV3028_CTRL2_TSE,
|
||||
RV3028_CTRL2_EIE | RV3028_CTRL2_TSE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* setup trickle charger */
|
||||
if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms",
|
||||
&ohms)) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++)
|
||||
if (ohms == rv3028_trickle_resistors[i])
|
||||
break;
|
||||
|
||||
if (i < ARRAY_SIZE(rv3028_trickle_resistors)) {
|
||||
ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
|
||||
RV3028_BACKUP_TCE |
|
||||
RV3028_BACKUP_TCR_MASK,
|
||||
RV3028_BACKUP_TCE | i);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
dev_warn(&client->dev, "invalid trickle resistor value\n");
|
||||
}
|
||||
}
|
||||
|
||||
ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
rv3028->rtc->ops = &rv3028_rtc_ops;
|
||||
ret = rtc_register_device(rv3028->rtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvmem_cfg.priv = rv3028->regmap;
|
||||
rtc_nvmem_register(rv3028->rtc, &nvmem_cfg);
|
||||
eeprom_cfg.priv = rv3028->regmap;
|
||||
rtc_nvmem_register(rv3028->rtc, &eeprom_cfg);
|
||||
|
||||
rv3028->rtc->max_user_freq = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id rv3028_of_match[] = {
|
||||
{ .compatible = "microcrystal,rv3028", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rv3028_of_match);
|
||||
|
||||
static struct i2c_driver rv3028_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-rv3028",
|
||||
.of_match_table = of_match_ptr(rv3028_of_match),
|
||||
},
|
||||
.probe_new = rv3028_probe,
|
||||
};
|
||||
module_i2c_driver(rv3028_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -1,13 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* 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.
|
||||
* Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -236,9 +232,6 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
|
|||
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;
|
||||
|
@ -602,6 +595,8 @@ static int rv8803_probe(struct i2c_client *client,
|
|||
|
||||
rv8803->rtc->ops = &rv8803_rtc_ops;
|
||||
rv8803->rtc->nvram_old_abi = true;
|
||||
rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rv8803->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
err = rtc_register_device(rv8803->rtc);
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -648,6 +643,6 @@ static struct i2c_driver rv8803_driver = {
|
|||
};
|
||||
module_i2c_driver(rv8803_driver);
|
||||
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/log2.h>
|
||||
|
@ -51,11 +53,19 @@
|
|||
#define RX8581_CTRL_STOP 0x02 /* STOP bit */
|
||||
#define RX8581_CTRL_RESET 0x01 /* RESET bit */
|
||||
|
||||
#define RX8571_USER_RAM 0x10
|
||||
#define RX8571_NVRAM_SIZE 0x10
|
||||
|
||||
struct rx8581 {
|
||||
struct regmap *regmap;
|
||||
struct rtc_device *rtc;
|
||||
};
|
||||
|
||||
struct rx85x1_config {
|
||||
struct regmap_config regmap;
|
||||
unsigned int num_nvram;
|
||||
};
|
||||
|
||||
/*
|
||||
* In the routines that deal directly with the rx8581 hardware, we use
|
||||
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
|
||||
|
@ -181,25 +191,103 @@ static const struct rtc_class_ops rx8581_rtc_ops = {
|
|||
.set_time = rx8581_rtc_set_time,
|
||||
};
|
||||
|
||||
static int rx8581_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int rx8571_nvram_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct rx8581 *rx8581;
|
||||
static const struct regmap_config config = {
|
||||
struct rx8581 *rx8581 = priv;
|
||||
|
||||
return regmap_bulk_read(rx8581->regmap, RX8571_USER_RAM + offset,
|
||||
val, bytes);
|
||||
}
|
||||
|
||||
static int rx8571_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct rx8581 *rx8581 = priv;
|
||||
|
||||
return regmap_bulk_write(rx8581->regmap, RX8571_USER_RAM + offset,
|
||||
val, bytes);
|
||||
}
|
||||
|
||||
static int rx85x1_nvram_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct rx8581 *rx8581 = priv;
|
||||
unsigned int tmp_val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rx8581->regmap, RX8581_REG_RAM, &tmp_val);
|
||||
(*(unsigned char *)val) = (unsigned char) tmp_val;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rx85x1_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct rx8581 *rx8581 = priv;
|
||||
unsigned char tmp_val;
|
||||
|
||||
tmp_val = *((unsigned char *)val);
|
||||
return regmap_write(rx8581->regmap, RX8581_REG_RAM,
|
||||
(unsigned int)tmp_val);
|
||||
}
|
||||
|
||||
static const struct rx85x1_config rx8581_config = {
|
||||
.regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xf,
|
||||
},
|
||||
.num_nvram = 1
|
||||
};
|
||||
|
||||
static const struct rx85x1_config rx8571_config = {
|
||||
.regmap = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x1f,
|
||||
},
|
||||
.num_nvram = 2
|
||||
};
|
||||
|
||||
static int rx8581_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct rx8581 *rx8581;
|
||||
const struct rx85x1_config *config = &rx8581_config;
|
||||
const void *data = of_device_get_match_data(&client->dev);
|
||||
static struct nvmem_config nvmem_cfg[] = {
|
||||
{
|
||||
.name = "rx85x1-",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = 1,
|
||||
.reg_read = rx85x1_nvram_read,
|
||||
.reg_write = rx85x1_nvram_write,
|
||||
}, {
|
||||
.name = "rx8571-",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = RX8571_NVRAM_SIZE,
|
||||
.reg_read = rx8571_nvram_read,
|
||||
.reg_write = rx8571_nvram_write,
|
||||
},
|
||||
};
|
||||
int ret, i;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
if (data)
|
||||
config = data;
|
||||
|
||||
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);
|
||||
rx8581->regmap = devm_regmap_init_i2c(client, &config->regmap);
|
||||
if (IS_ERR(rx8581->regmap))
|
||||
return PTR_ERR(rx8581->regmap);
|
||||
|
||||
|
@ -213,7 +301,14 @@ static int rx8581_probe(struct i2c_client *client,
|
|||
rx8581->rtc->start_secs = 0;
|
||||
rx8581->rtc->set_start_time = true;
|
||||
|
||||
return rtc_register_device(rx8581->rtc);
|
||||
ret = rtc_register_device(rx8581->rtc);
|
||||
|
||||
for (i = 0; i < config->num_nvram; i++) {
|
||||
nvmem_cfg[i].priv = rx8581;
|
||||
rtc_nvmem_register(rx8581->rtc, &nvmem_cfg[i]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id rx8581_id[] = {
|
||||
|
@ -223,8 +318,9 @@ static const struct i2c_device_id rx8581_id[] = {
|
|||
MODULE_DEVICE_TABLE(i2c, rx8581_id);
|
||||
|
||||
static const struct of_device_id rx8581_of_match[] = {
|
||||
{ .compatible = "epson,rx8581" },
|
||||
{ }
|
||||
{ .compatible = "epson,rx8571", .data = &rx8571_config },
|
||||
{ .compatible = "epson,rx8581", .data = &rx8581_config },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rx8581_of_match);
|
||||
|
||||
|
@ -240,5 +336,5 @@ static struct i2c_driver rx8581_driver = {
|
|||
module_i2c_driver(rx8581_driver);
|
||||
|
||||
MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
|
||||
MODULE_DESCRIPTION("Epson RX-8581 RTC driver");
|
||||
MODULE_DESCRIPTION("Epson RX-8571/RX-8581 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <linux/log2.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
|
@ -39,7 +40,7 @@ struct s3c_rtc {
|
|||
void __iomem *base;
|
||||
struct clk *rtc_clk;
|
||||
struct clk *rtc_src_clk;
|
||||
bool clk_disabled;
|
||||
bool alarm_enabled;
|
||||
|
||||
const struct s3c_rtc_data *data;
|
||||
|
||||
|
@ -47,7 +48,7 @@ struct s3c_rtc {
|
|||
int irq_tick;
|
||||
|
||||
spinlock_t pie_lock;
|
||||
spinlock_t alarm_clk_lock;
|
||||
spinlock_t alarm_lock;
|
||||
|
||||
int ticnt_save;
|
||||
int ticnt_en_save;
|
||||
|
@ -70,44 +71,27 @@ struct s3c_rtc_data {
|
|||
|
||||
static int s3c_rtc_enable_clk(struct s3c_rtc *info)
|
||||
{
|
||||
unsigned long irq_flags;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
|
||||
ret = clk_enable(info->rtc_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
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;
|
||||
}
|
||||
if (info->data->needs_src_clk) {
|
||||
ret = clk_enable(info->rtc_src_clk);
|
||||
if (ret) {
|
||||
clk_disable(info->rtc_clk);
|
||||
return ret;
|
||||
}
|
||||
info->clk_disabled = false;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
if (info->data->needs_src_clk)
|
||||
clk_disable(info->rtc_src_clk);
|
||||
clk_disable(info->rtc_clk);
|
||||
}
|
||||
|
||||
/* IRQ Handlers */
|
||||
|
@ -135,6 +119,7 @@ static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
|
|||
static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct s3c_rtc *info = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
|
@ -151,17 +136,19 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
|
|||
|
||||
writeb(tmp, info->base + S3C2410_RTCALM);
|
||||
|
||||
spin_lock_irqsave(&info->alarm_lock, flags);
|
||||
|
||||
if (info->alarm_enabled && !enabled)
|
||||
s3c_rtc_disable_clk(info);
|
||||
else if (!info->alarm_enabled && enabled)
|
||||
ret = s3c_rtc_enable_clk(info);
|
||||
|
||||
info->alarm_enabled = enabled;
|
||||
spin_unlock_irqrestore(&info->alarm_lock, flags);
|
||||
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set RTC frequency */
|
||||
|
@ -357,10 +344,10 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
|
||||
writeb(alrm_en, info->base + S3C2410_RTCALM);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
s3c_rtc_setaie(dev, alrm->enabled);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -456,16 +443,6 @@ static int s3c_rtc_remove(struct platform_device *pdev)
|
|||
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;
|
||||
|
@ -485,13 +462,13 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
info->data = s3c_rtc_get_data(pdev);
|
||||
info->data = of_device_get_match_data(&pdev->dev);
|
||||
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);
|
||||
spin_lock_init(&info->alarm_lock);
|
||||
|
||||
platform_set_drvdata(pdev, info);
|
||||
|
||||
|
@ -591,6 +568,8 @@ static int s3c_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
s3c_rtc_setfreq(info, 1);
|
||||
|
||||
s3c_rtc_disable_clk(info);
|
||||
|
||||
return 0;
|
||||
|
||||
err_nortc:
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Real Time Clock (RTC) Driver for sd3078
|
||||
* Copyright (C) 2018 Zoro Li
|
||||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define SD3078_REG_SC 0x00
|
||||
#define SD3078_REG_MN 0x01
|
||||
#define SD3078_REG_HR 0x02
|
||||
#define SD3078_REG_DW 0x03
|
||||
#define SD3078_REG_DM 0x04
|
||||
#define SD3078_REG_MO 0x05
|
||||
#define SD3078_REG_YR 0x06
|
||||
|
||||
#define SD3078_REG_CTRL1 0x0f
|
||||
#define SD3078_REG_CTRL2 0x10
|
||||
#define SD3078_REG_CTRL3 0x11
|
||||
|
||||
#define KEY_WRITE1 0x80
|
||||
#define KEY_WRITE2 0x04
|
||||
#define KEY_WRITE3 0x80
|
||||
|
||||
#define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1)
|
||||
|
||||
/*
|
||||
* The sd3078 has write protection
|
||||
* and we can choose whether or not to use it.
|
||||
* Write protection is turned off by default.
|
||||
*/
|
||||
#define WRITE_PROTECT_EN 0
|
||||
|
||||
struct sd3078 {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
/*
|
||||
* In order to prevent arbitrary modification of the time register,
|
||||
* when modification of the register,
|
||||
* the "write" bit needs to be written in a certain order.
|
||||
* 1. set WRITE1 bit
|
||||
* 2. set WRITE2 bit
|
||||
* 3. set WRITE3 bit
|
||||
*/
|
||||
static void sd3078_enable_reg_write(struct sd3078 *sd3078)
|
||||
{
|
||||
regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
|
||||
KEY_WRITE1, KEY_WRITE1);
|
||||
regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
|
||||
KEY_WRITE2, KEY_WRITE2);
|
||||
regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
|
||||
KEY_WRITE3, KEY_WRITE3);
|
||||
}
|
||||
|
||||
#if WRITE_PROTECT_EN
|
||||
/*
|
||||
* In order to prevent arbitrary modification of the time register,
|
||||
* we should disable the write function.
|
||||
* when disable write,
|
||||
* the "write" bit needs to be clear in a certain order.
|
||||
* 1. clear WRITE2 bit
|
||||
* 2. clear WRITE3 bit
|
||||
* 3. clear WRITE1 bit
|
||||
*/
|
||||
static void sd3078_disable_reg_write(struct sd3078 *sd3078)
|
||||
{
|
||||
regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
|
||||
KEY_WRITE2, 0);
|
||||
regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1,
|
||||
KEY_WRITE3, 0);
|
||||
regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2,
|
||||
KEY_WRITE1, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned char hour;
|
||||
unsigned char rtc_data[NUM_TIME_REGS] = {0};
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sd3078 *sd3078 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, 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[SD3078_REG_SC] & 0x7F);
|
||||
tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F);
|
||||
|
||||
/*
|
||||
* The sd3078 supports 12/24 hour mode.
|
||||
* When getting time,
|
||||
* we need to convert the 12 hour mode to the 24 hour mode.
|
||||
*/
|
||||
hour = rtc_data[SD3078_REG_HR];
|
||||
if (hour & 0x80) /* 24H MODE */
|
||||
tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F);
|
||||
else if (hour & 0x20) /* 12H MODE PM */
|
||||
tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12;
|
||||
else /* 12H MODE AM */
|
||||
tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F);
|
||||
|
||||
tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F);
|
||||
tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07;
|
||||
tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1;
|
||||
tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned char rtc_data[NUM_TIME_REGS];
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct sd3078 *sd3078 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec);
|
||||
rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min);
|
||||
rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80;
|
||||
rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday);
|
||||
rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07;
|
||||
rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1;
|
||||
rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
#if WRITE_PROTECT_EN
|
||||
sd3078_enable_reg_write(sd3078);
|
||||
#endif
|
||||
|
||||
ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data,
|
||||
NUM_TIME_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "writing to RTC failed with err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if WRITE_PROTECT_EN
|
||||
sd3078_disable_reg_write(sd3078);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops sd3078_rtc_ops = {
|
||||
.read_time = sd3078_rtc_read_time,
|
||||
.set_time = sd3078_rtc_set_time,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x11,
|
||||
};
|
||||
|
||||
static int sd3078_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct sd3078 *sd3078;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL);
|
||||
if (!sd3078)
|
||||
return -ENOMEM;
|
||||
|
||||
sd3078->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(sd3078->regmap)) {
|
||||
dev_err(&client->dev, "regmap allocation failed\n");
|
||||
return PTR_ERR(sd3078->regmap);
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, sd3078);
|
||||
|
||||
sd3078->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(sd3078->rtc))
|
||||
return PTR_ERR(sd3078->rtc);
|
||||
|
||||
sd3078->rtc->ops = &sd3078_rtc_ops;
|
||||
sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
ret = rtc_register_device(sd3078->rtc);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register rtc device\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
sd3078_enable_reg_write(sd3078);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sd3078_id[] = {
|
||||
{"sd3078", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sd3078_id);
|
||||
|
||||
static const struct of_device_id rtc_dt_match[] = {
|
||||
{ .compatible = "whwave,sd3078" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rtc_dt_match);
|
||||
|
||||
static struct i2c_driver sd3078_driver = {
|
||||
.driver = {
|
||||
.name = "sd3078",
|
||||
.of_match_table = of_match_ptr(rtc_dt_match),
|
||||
},
|
||||
.probe = sd3078_probe,
|
||||
.id_table = sd3078_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(sd3078_driver);
|
||||
|
||||
MODULE_AUTHOR("Dianlong Li <long17.cool@163.com>");
|
||||
MODULE_DESCRIPTION("SD3078 RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -239,6 +239,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
|
|||
u32 lpsr;
|
||||
u32 events = 0;
|
||||
|
||||
if (data->clk)
|
||||
clk_enable(data->clk);
|
||||
|
||||
regmap_read(data->regmap, data->offset + SNVS_LPSR, &lpsr);
|
||||
|
||||
if (lpsr & SNVS_LPSR_LPTA) {
|
||||
|
@ -253,6 +256,9 @@ static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
|
|||
/* clear interrupt status */
|
||||
regmap_write(data->regmap, data->offset + SNVS_LPSR, lpsr);
|
||||
|
||||
if (data->clk)
|
||||
clk_disable(data->clk);
|
||||
|
||||
return events ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* 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>
|
||||
|
@ -65,10 +62,11 @@ static int tx4939_rtc_cmd(struct tx4939_rtc_reg __iomem *rtcreg, int cmd)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tx4939_rtc_set_mmss(struct device *dev, unsigned long secs)
|
||||
static int tx4939_rtc_set_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;
|
||||
unsigned long secs = rtc_tm_to_time64(tm);
|
||||
int i, ret;
|
||||
unsigned char buf[6];
|
||||
|
||||
|
@ -111,7 +109,7 @@ static int tx4939_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
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);
|
||||
rtc_time64_to_tm(sec, tm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -123,14 +121,7 @@ static int tx4939_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
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);
|
||||
sec = rtc_tm_to_time64(&alrm->time);
|
||||
buf[0] = 0;
|
||||
buf[1] = 0;
|
||||
buf[2] = sec;
|
||||
|
@ -173,7 +164,7 @@ static int tx4939_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
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);
|
||||
rtc_time64_to_tm(sec, &alrm->time);
|
||||
return rtc_valid_tm(&alrm->time);
|
||||
}
|
||||
|
||||
|
@ -210,7 +201,7 @@ 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,
|
||||
.set_time = tx4939_rtc_set_time,
|
||||
.alarm_irq_enable = tx4939_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
|
@ -283,6 +274,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
rtc->ops = &tx4939_rtc_ops;
|
||||
rtc->nvram_old_abi = true;
|
||||
rtc->range_max = U32_MAX;
|
||||
|
||||
pdata->rtc = rtc;
|
||||
|
||||
|
@ -315,5 +307,5 @@ 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_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:tx4939rtc");
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
#define RTC_CALIB_DEF 0x198233
|
||||
#define RTC_CALIB_MASK 0x1FFFFF
|
||||
#define RTC_SEC_MAX_VAL 0xFFFFFFFF
|
||||
|
||||
struct xlnx_rtc_dev {
|
||||
struct rtc_device *rtc;
|
||||
|
@ -71,9 +70,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
*/
|
||||
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
|
||||
|
@ -154,9 +150,6 @@ static int xlnx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
|
||||
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);
|
||||
|
@ -222,6 +215,13 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, xrtcdev);
|
||||
|
||||
xrtcdev->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(xrtcdev->rtc))
|
||||
return PTR_ERR(xrtcdev->rtc);
|
||||
|
||||
xrtcdev->rtc->ops = &xlnx_rtc_ops;
|
||||
xrtcdev->rtc->range_max = U32_MAX;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
xrtcdev->reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
|
@ -263,9 +263,7 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
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);
|
||||
return rtc_register_device(xrtcdev->rtc);
|
||||
}
|
||||
|
||||
static int xlnx_rtc_remove(struct platform_device *pdev)
|
||||
|
|
|
@ -67,7 +67,7 @@ extern struct class *rtc_class;
|
|||
*
|
||||
* The (current) exceptions are mostly filesystem hooks:
|
||||
* - the proc() hook for procfs
|
||||
* - non-ioctl() chardev hooks: open(), release(), read_callback()
|
||||
* - non-ioctl() chardev hooks: open(), release()
|
||||
*
|
||||
* REVISIT those periodic irq calls *do* have ops_lock when they're
|
||||
* issued through ioctl() ...
|
||||
|
@ -81,7 +81,6 @@ struct rtc_class_ops {
|
|||
int (*proc)(struct device *, struct seq_file *);
|
||||
int (*set_mmss64)(struct device *, time64_t secs);
|
||||
int (*set_mmss)(struct device *, unsigned long secs);
|
||||
int (*read_callback)(struct device *, int data);
|
||||
int (*alarm_irq_enable)(struct device *, unsigned int enabled);
|
||||
int (*read_offset)(struct device *, long *offset);
|
||||
int (*set_offset)(struct device *, long offset);
|
||||
|
|
Loading…
Reference in New Issue