mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
RTC for 7.4
Subsystem:
- the hctosys limit for 32bit platforms is now configurable
- introduce lock guard for for rtc_device.ops_lock
- use sysfs_emit() instead of sprintf() were possible
- selftest fixes
New driver:
- Raspberry Pi 5 RTC
Drivers:
- abx80x: add support for ABX81X
- bd70528: properly enable wakeup
- cmos: Fix ACPI RTC alarm handling
- ds1685: power-off fixes
- mt6397: add nvmem support
- pcf85063: add HW UIE support
- rs5c372: Ricoh R2223x support
- rzn1: add R-Car X5H support
- s35390a: allow use of output pin for interrupt signal 1 for wakealarm to
support synology devices
- sun6i: add Allwinner B288 support
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmrmdmcACgkQY6TcMGxw
OjLB/g/9EG6jcs15Ci3pgKQ4g6h6UKHN86WIA64uPMjvuyAvmyccmrlW6AvwOhoH
kAp77s8hbujENJk4HPvT4kU0G7KlxpxfZwuvicHNBqGQ1L100Ahx22FdrgXxcejk
MKuN+30H6iJ01eDEmxJ530VlWCRBTusd8HexDh3Q6IY2mNLiphJXJ+moDheKBnAO
ECh8lc6Qnb5fFRYqUmAK0QLNKjU8kz6FCgj+vnuSaRftEAgniFmHTJHIiTdB0Bwp
REdpSMuhjM85oeCxcYu1r1ABnE08a3EbkoHelPlDiO/p1NNQ227IbciCG3rX++bJ
pa5IWkt2nfvC3RIyxuM5pcCSLpRHgji34LLiCkSzP99S3yujVYcZiBYltuYz5rx0
Jg4hIb1EmyV7AimQ3XZp1Y+/R2NxKVVHl7MEioq/EFYQFDo04EwcJAYIPn/ZPLDG
sVpbO/o3lmocP7f6n7jZFkuapQwZ68nijiuRby51gUK4uuraPQou4/igsBlJWn0s
z6MbS/1jtpRbAQ+8m8ev6LP5s7+4iALd4C8DGYEN53JOpx03LNLFpxPR14i8a5kt
FaXMjA6f12WvKaVcYJv++r/pxho/iSfXRQiSV3algm921Z5hpkCRi8xauI9sJJmG
IWjp+xZ+svPE+Ch7kn4jHbEGLbJy2sBSG30TKEE9WhUVOaHdhQo=
=NTHB
-----END PGP SIGNATURE-----
Merge tag 'rtc-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni:
"This introduces a way to lift the hctosys limit for 32-bit systems to
ensure systems relying on it can pass Y2038. Add a driver for the
Raspberry Pi 5 RTC and add support for the Abracon abx81x, Ricoh
R2223x, Renesas R-Car X5H and Allwinner B288 RTCs to existing drivers:
Subsystem:
- the hctosys limit for 32bit platforms is now configurable
- introduce lock guard for for rtc_device.ops_lock
- use sysfs_emit() instead of sprintf() were possible
- selftest fixes
New driver:
- Raspberry Pi 5 RTC
Drivers:
- abx80x: add support for ABX81X
- bd70528: properly enable wakeup
- cmos: Fix ACPI RTC alarm handling
- ds1685: power-off fixes
- mt6397: add nvmem support
- pcf85063: add HW UIE support
- rs5c372: Ricoh R2223x support
- rzn1: add R-Car X5H support
- s35390a: allow use of output pin for interrupt signal 1 for wakealarm to
support synology devices
- sun6i: add Allwinner B288 support"
* tag 'rtc-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (89 commits)
rtc: rs5c372: use sysfs_emit() instead of sprintf()
rtc: ds1307: use sysfs_emit() instead of sprintf()
rtc: isl1208: use sysfs_emit() instead of sprintf()
rtc: ds1685: use sysfs_emit() instead of sprintf()
rtc: ds1343: use sysfs_emit() instead of sprintf()
rtc: rx8025: use sysfs_emit() instead of sprintf()
rtc: x1205: use sysfs_emit() instead of sprintf()
rtc: bq32k: use sysfs_emit() instead of sprintf()
rtc: ep93xx: use sysfs_emit() instead of sprintf()
rtc: pcf2127: use sysfs_emit() instead of sprintf()
rtc: rv3029: use sysfs_emit() instead of sprintf()
rtc: ds1685: keep setting PAB until power is removed
rtc: ds1685: do not sleep in the power-off handler
rtc: ds1685: arm the wake-up alarm at power-off only if an alarm is set
rtc: rv3028: use sysfs_emit() instead of sprintf()
rtc: pcf85063: add 1 Hz update interrupt (UIE) support
rtc: pcf85063: preserve the alarm flag in clkout register updates
rtc: pcf85063: do not clear AIE in the interrupt handler
rtc: pcf85063: use devm_of_clk_add_hw_provider() for clkout
rtc: s35390a: Add synology quirk
...
This commit is contained in:
commit
9b6f2eabc8
|
|
@ -133,6 +133,11 @@ properties:
|
|||
required:
|
||||
- compatible
|
||||
|
||||
trickle-voltage-millivolt:
|
||||
$ref: /schemas/rtc/rtc.yaml#/properties/trickle-voltage-millivolt
|
||||
minimum: 1300
|
||||
maximum: 5000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- mboxes
|
||||
|
|
@ -144,6 +149,7 @@ examples:
|
|||
firmware {
|
||||
compatible = "raspberrypi,bcm2835-firmware", "simple-mfd";
|
||||
mboxes = <&mailbox>;
|
||||
trickle-voltage-millivolt = <3300>;
|
||||
|
||||
firmware_clocks: clocks {
|
||||
compatible = "raspberrypi,firmware-clocks";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
$id: http://devicetree.org/schemas/rtc/abracon,abx80x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Abracon ABX80X I2C ultra low power RTC/Alarm chip
|
||||
title: Abracon ABX80X (I2C) and ABX81X (SPI) ultra low power RTC/Alarm chips
|
||||
|
||||
maintainers:
|
||||
- linux-rtc@vger.kernel.org
|
||||
|
|
@ -21,10 +21,12 @@ properties:
|
|||
- abracon,ab0803
|
||||
- abracon,ab0804
|
||||
- abracon,ab0805
|
||||
- abracon,ab0815
|
||||
- abracon,ab1801
|
||||
- abracon,ab1803
|
||||
- abracon,ab1804
|
||||
- abracon,ab1805
|
||||
- abracon,ab1815
|
||||
- microcrystal,rv1805
|
||||
|
||||
reg:
|
||||
|
|
@ -61,6 +63,7 @@ required:
|
|||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/spi/spi-peripheral-props.yaml#
|
||||
- $ref: rtc.yaml#
|
||||
- if:
|
||||
properties:
|
||||
|
|
@ -73,6 +76,8 @@ allOf:
|
|||
- abracon,ab1804
|
||||
- abracon,ab0805
|
||||
- abracon,ab1805
|
||||
- abracon,ab0815
|
||||
- abracon,ab1815
|
||||
then:
|
||||
properties:
|
||||
abracon,tc-diode: false
|
||||
|
|
@ -96,3 +101,14 @@ examples:
|
|||
interrupts = <44 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
|
||||
- |
|
||||
spi {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@0 {
|
||||
compatible = "abracon,ab1815";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ properties:
|
|||
- enum:
|
||||
- allwinner,sun6i-a31-rtc
|
||||
- allwinner,sun8i-a23-rtc
|
||||
- allwinner,sun8i-b288-rtc
|
||||
- allwinner,sun8i-h3-rtc
|
||||
- allwinner,sun8i-r40-rtc
|
||||
- allwinner,sun8i-v3-rtc
|
||||
|
|
@ -82,6 +83,7 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- allwinner,sun8i-a23-rtc
|
||||
- allwinner,sun8i-b288-rtc
|
||||
- allwinner,sun8i-r40-rtc
|
||||
- allwinner,sun8i-v3-rtc
|
||||
|
||||
|
|
@ -162,6 +164,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- allwinner,sun8i-b288-rtc
|
||||
- allwinner,sun8i-r40-rtc
|
||||
- allwinner,sun50i-h616-rtc
|
||||
- allwinner,sun50i-r329-rtc
|
||||
|
|
|
|||
|
|
@ -14,14 +14,16 @@ properties:
|
|||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- renesas,r9a06g032-rtc
|
||||
- const: renesas,rzn1-rtc
|
||||
- renesas,r9a06g032-rtc # RZ/N1D
|
||||
- const: renesas,rzn1-rtc # RZ/N1
|
||||
|
||||
- const: renesas,r9a09g077-rtc
|
||||
- const: renesas,r9a09g077-rtc # RZ/T2H
|
||||
|
||||
- items:
|
||||
- const: renesas,r9a09g087-rtc
|
||||
- const: renesas,r9a09g077-rtc
|
||||
- const: renesas,r9a09g087-rtc # RZ/N2H
|
||||
- const: renesas,r9a09g077-rtc # RZ/T2H
|
||||
|
||||
- const: renesas,r8a78000-rtc # R-Car X5H
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
@ -49,11 +51,12 @@ properties:
|
|||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
|
|
@ -66,6 +69,7 @@ allOf:
|
|||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- renesas,r8a78000-rtc
|
||||
- renesas,r9a09g077-rtc
|
||||
- renesas,r9a09g087-rtc
|
||||
then:
|
||||
|
|
@ -75,6 +79,24 @@ allOf:
|
|||
clock-names:
|
||||
minItems: 2
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r8a78000-rtc
|
||||
then:
|
||||
properties:
|
||||
interrupts: false
|
||||
interrupt-names: false
|
||||
required:
|
||||
- resets
|
||||
else:
|
||||
properties:
|
||||
resets: false
|
||||
required:
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
|
|
@ -91,5 +113,4 @@ examples:
|
|||
clocks = <&sysctrl R9A06G032_HCLK_RTC>;
|
||||
clock-names = "hclk";
|
||||
power-domains = <&sysctrl>;
|
||||
start-year = <2000>;
|
||||
};
|
||||
|
|
|
|||
58
Documentation/devicetree/bindings/rtc/ricoh,r2223x.yaml
Normal file
58
Documentation/devicetree/bindings/rtc/ricoh,r2223x.yaml
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/ricoh,r2223x.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ricoh R2223x Real-Time Clock
|
||||
|
||||
maintainers:
|
||||
- Heiko Schocher <hs@nabladev.com>
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ricoh,r2223x
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
ricoh,eco-mode:
|
||||
type: boolean
|
||||
description:
|
||||
Put the RTC into eco mode, which lowers its current consumption while
|
||||
running from the backup supply. Set this on boards where the RTC is
|
||||
expected to keep time on a small backup cell.
|
||||
|
||||
start-year: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@32 {
|
||||
compatible = "ricoh,r2223x";
|
||||
reg = <0x32>;
|
||||
interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
|
||||
#clock-cells = <0>;
|
||||
ricoh,eco-mode;
|
||||
};
|
||||
};
|
||||
|
|
@ -51,6 +51,7 @@ properties:
|
|||
if trickle charger should be enabled.
|
||||
|
||||
trickle-voltage-millivolt:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
Selected voltage for trickle charger. Should be given
|
||||
if trickle charger should be enabled and the trickle voltage is different
|
||||
|
|
|
|||
118
Documentation/devicetree/bindings/rtc/sii,s35390a.yaml
Normal file
118
Documentation/devicetree/bindings/rtc/sii,s35390a.yaml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/sii,s35390a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: S-35390A 2-wire real-time clock
|
||||
|
||||
maintainers:
|
||||
- Markus Probst <markus.probst@posteo.de>
|
||||
|
||||
description:
|
||||
The S-35390A is a CMOS 2-wire real-time clock IC which operates with the
|
||||
very low current consumption in the wide range of operation voltage.
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: sii,s35390a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description: Supports up to 2 interrupt lines via the INT1 and INT2 pins.
|
||||
|
||||
interrupt-names:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
items:
|
||||
enum:
|
||||
- int1
|
||||
- int2
|
||||
|
||||
wakeup-parent: true
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
|
||||
patternProperties:
|
||||
"^pins":
|
||||
type: object
|
||||
additionalProperties: false
|
||||
|
||||
patternProperties:
|
||||
"-pins$":
|
||||
$ref: /schemas/pinctrl/pinmux-node.yaml#
|
||||
additionalProperties: false
|
||||
|
||||
properties:
|
||||
pins:
|
||||
minItems: 1
|
||||
items:
|
||||
enum:
|
||||
- int1
|
||||
- int2
|
||||
|
||||
function:
|
||||
description: |
|
||||
Pin function:
|
||||
- ignore: Preserve the previous state.
|
||||
- disable: Disable pin output.
|
||||
- wakeup: Output wakes up the system.
|
||||
- clock: Output clock pulse.
|
||||
- pmin1: Minute periodical output with 50% duty.
|
||||
- pmin2: Minute periodical output L for 7.81 ms.
|
||||
Can only be used with pin int1.
|
||||
enum:
|
||||
- ignore
|
||||
- disable
|
||||
- wakeup
|
||||
- clock
|
||||
- pmin1
|
||||
- pmin2
|
||||
|
||||
required:
|
||||
- pins
|
||||
- function
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@30 {
|
||||
compatible = "sii,s35390a";
|
||||
reg = <0x30>;
|
||||
pinctrl-0 = <&rtc_pins_default>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
interrupt-names = "int1";
|
||||
interrupts = <23>;
|
||||
|
||||
rtc_pins_default: pins {
|
||||
int1-pins {
|
||||
pins = "int1";
|
||||
function = "wakeup";
|
||||
};
|
||||
int2-pins {
|
||||
pins = "int2";
|
||||
function = "disable";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -81,8 +81,6 @@ properties:
|
|||
- ricoh,rv5c386
|
||||
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
- ricoh,rv5c387a
|
||||
# 2-wire CMOS real-time clock
|
||||
- sii,s35390a
|
||||
# ST SPEAr Real-time Clock
|
||||
- st,spear600-rtc
|
||||
# VIA/Wondermedia VT8500 Real-time Clock
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
static struct platform_device *rpi_hwmon;
|
||||
static struct platform_device *rpi_clk;
|
||||
static struct platform_device *rpi_rtc;
|
||||
|
||||
struct rpi_firmware {
|
||||
struct mbox_client cl;
|
||||
|
|
@ -231,6 +232,26 @@ static void rpi_register_clk_driver(struct device *dev)
|
|||
-1, NULL, 0);
|
||||
}
|
||||
|
||||
static void rpi_register_rtc_driver(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
|
||||
/*
|
||||
* Check if our model of Raspberry Pi board is from the 5th gen
|
||||
* as the RTC is only present on that model.
|
||||
*/
|
||||
if (!of_machine_is_compatible("brcm,bcm2712"))
|
||||
return;
|
||||
|
||||
pdev = platform_device_register_data(dev, "raspberrypi-rtc",
|
||||
-1, NULL, 0);
|
||||
|
||||
if (IS_ERR(pdev))
|
||||
return;
|
||||
|
||||
rpi_rtc = pdev;
|
||||
}
|
||||
|
||||
unsigned int rpi_firmware_clk_get_max_rate(struct rpi_firmware *fw, unsigned int id)
|
||||
{
|
||||
struct rpi_firmware_clk_rate_request msg =
|
||||
|
|
@ -305,6 +326,7 @@ static int rpi_firmware_probe(struct platform_device *pdev)
|
|||
rpi_firmware_print_firmware_revision(fw);
|
||||
rpi_register_hwmon_driver(dev, fw);
|
||||
rpi_register_clk_driver(dev);
|
||||
rpi_register_rtc_driver(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -327,6 +349,8 @@ static void rpi_firmware_remove(struct platform_device *pdev)
|
|||
rpi_hwmon = NULL;
|
||||
platform_device_unregister(rpi_clk);
|
||||
rpi_clk = NULL;
|
||||
platform_device_unregister(rpi_rtc);
|
||||
rpi_rtc = NULL;
|
||||
|
||||
rpi_firmware_put(fw);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dmi.h>
|
||||
|
|
@ -649,7 +650,6 @@ static int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev)
|
|||
|
||||
static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
|
||||
{
|
||||
struct rtc_device *rtc_device;
|
||||
time64_t then, now, duration;
|
||||
struct rtc_wkalrm alarm;
|
||||
struct rtc_time tm;
|
||||
|
|
@ -665,7 +665,8 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg)
|
|||
if (pdev->major < 64 || (pdev->major == 64 && pdev->minor < 53))
|
||||
return 0;
|
||||
|
||||
rtc_device = rtc_class_open("rtc0");
|
||||
struct rtc_device *rtc_device __free(rtc_class_close) =
|
||||
rtc_class_open("rtc0");
|
||||
if (!rtc_device)
|
||||
return 0;
|
||||
rc = rtc_read_next_alarm(rtc_device, &alarm);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,27 @@ config RTC_HCTOSYS
|
|||
the value read from a specified RTC device. This is useful to avoid
|
||||
unnecessary fsck runs at boot time, and to network better.
|
||||
|
||||
config RTC_HCTOSYS_TIME32_LIMIT
|
||||
bool "Reject RTC dates after the 2038 cutoff"
|
||||
depends on RTC_HCTOSYS && COMPAT_32BIT_TIME && !64BIT
|
||||
default y
|
||||
help
|
||||
Reject RTC dates after 03:14:07 UTC on 19 January 2038 when
|
||||
initializing the system clock in order to work around broken
|
||||
RTC implementations that produce nondeterministic timestamps
|
||||
without a working backup battery.
|
||||
|
||||
A rejected date leaves system time unchanged, normally near
|
||||
the Unix epoch, and the RTC's hctosys sysfs attribute reads
|
||||
0. Userspace must supply the time if no other source has
|
||||
initialized it.
|
||||
|
||||
Say Y here if the kernel may have to run old time32 userspace on
|
||||
hardware that can produce unpredictable RTC timestamps. If the
|
||||
kernel is intended to only run time64 compatible userspace and
|
||||
continue working past 2038, you must pick N here in order to
|
||||
have a working RTC_HCTOSYS functionality.
|
||||
|
||||
config RTC_HCTOSYS_DEVICE
|
||||
string "RTC used to set the system time"
|
||||
depends on RTC_HCTOSYS
|
||||
|
|
@ -212,17 +233,6 @@ config RTC_DRV_ABEOZ9
|
|||
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
|
||||
help
|
||||
If you say yes here you get support for Abracon AB080X and AB180X
|
||||
families of ultra-low-power battery- and capacitor-backed real-time
|
||||
clock chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-abx80x.
|
||||
|
||||
config RTC_DRV_AC100
|
||||
tristate "X-Powers AC100"
|
||||
depends on MFD_AC100
|
||||
|
|
@ -668,7 +678,9 @@ config RTC_DRV_RC5T619
|
|||
|
||||
config RTC_DRV_S35390A
|
||||
tristate "Seiko Instruments S-35390A"
|
||||
select PINCTRL
|
||||
select BITREVERSE
|
||||
select GENERIC_PINCONF
|
||||
help
|
||||
If you say yes here you will get support for the Seiko
|
||||
Instruments S-35390A.
|
||||
|
|
@ -954,6 +966,20 @@ config RTC_I2C_AND_SPI
|
|||
|
||||
comment "SPI and I2C RTC drivers"
|
||||
|
||||
config RTC_DRV_ABX80X
|
||||
tristate "Abracon ABx80x"
|
||||
depends on RTC_I2C_AND_SPI
|
||||
select WATCHDOG_CORE if WATCHDOG
|
||||
select REGMAP_I2C if I2C
|
||||
select REGMAP_SPI if SPI_MASTER
|
||||
help
|
||||
If you say yes here you get support for Abracon AB080X, AB180X,
|
||||
AB081X and AB181X families of ultra-low-power battery- and
|
||||
capacitor-backed real-time clock chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-abx80x.
|
||||
|
||||
config RTC_DRV_DS3232
|
||||
tristate "Dallas/Maxim DS3232/DS3234"
|
||||
depends on RTC_I2C_AND_SPI
|
||||
|
|
@ -1999,6 +2025,18 @@ config RTC_DRV_R7301
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-r7301.
|
||||
|
||||
config RTC_DRV_RPI
|
||||
tristate "Raspberry Pi RTC"
|
||||
depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
|
||||
depends on ARCH_BCM2835 || COMPILE_TEST
|
||||
default ARCH_BCM2835
|
||||
help
|
||||
If you say yes here you get support for the RTC found on
|
||||
Raspberry Pi 5th gen devices.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-raspberrypi.
|
||||
|
||||
config RTC_DRV_STM32
|
||||
tristate "STM32 RTC"
|
||||
select REGMAP_MMIO
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
|||
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
||||
obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o
|
||||
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
|
||||
obj-$(CONFIG_RTC_DRV_RPI) += rtc-raspberrypi.o
|
||||
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
|
||||
obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o
|
||||
obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
|
||||
|
|
|
|||
|
|
@ -72,12 +72,14 @@ static void rtc_hctosys(struct rtc_device *rtc)
|
|||
|
||||
tv64.tv_sec = rtc_tm_to_time64(&tm);
|
||||
|
||||
#if BITS_PER_LONG == 32
|
||||
if (tv64.tv_sec > INT_MAX) {
|
||||
if (IS_ENABLED(CONFIG_RTC_HCTOSYS_TIME32_LIMIT) &&
|
||||
tv64.tv_sec > INT_MAX) {
|
||||
dev_warn(rtc->dev.parent,
|
||||
"hctosys: rejecting %ptR UTC due to CONFIG_RTC_HCTOSYS_TIME32_LIMIT=y\n",
|
||||
&tm);
|
||||
err = -ERANGE;
|
||||
goto err_read;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = do_settimeofday64(&tv64);
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -516,12 +516,12 @@ static irqreturn_t ac100_rtc_irq(int irq, void *data)
|
|||
unsigned int val = 0;
|
||||
int ret;
|
||||
|
||||
rtc_lock(chip->rtc);
|
||||
guard(rtc_ops)(chip->rtc);
|
||||
|
||||
/* read status */
|
||||
ret = regmap_read(regmap, AC100_ALM_INT_STA, &val);
|
||||
if (ret)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (val & AC100_ALM_INT_ENABLE) {
|
||||
/* signal rtc framework */
|
||||
|
|
@ -530,16 +530,14 @@ static irqreturn_t ac100_rtc_irq(int irq, void *data)
|
|||
/* clear status */
|
||||
ret = regmap_write(regmap, AC100_ALM_INT_STA, val);
|
||||
if (ret)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/* disable interrupt */
|
||||
ret = ac100_rtc_alarm_irq_enable(chip->dev, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
out:
|
||||
rtc_unlock(chip->rtc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,15 +116,13 @@ static irqreturn_t asm9260_rtc_irq(int irq, void *dev_id)
|
|||
u32 isr;
|
||||
unsigned long events = 0;
|
||||
|
||||
rtc_lock(priv->rtc);
|
||||
guard(rtc_ops)(priv->rtc);
|
||||
isr = ioread32(priv->iobase + HW_CIIR);
|
||||
if (!isr) {
|
||||
rtc_unlock(priv->rtc);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
iowrite32(0, priv->iobase + HW_CIIR);
|
||||
rtc_unlock(priv->rtc);
|
||||
|
||||
events |= RTC_AF | RTC_IRQF;
|
||||
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ static void atcrtc_alarm_clear(struct work_struct *work)
|
|||
container_of(work, struct atcrtc_dev, rtc_work);
|
||||
int ret;
|
||||
|
||||
rtc_lock(rtc->rtc_dev);
|
||||
guard(rtc_ops)(rtc->rtc_dev);
|
||||
|
||||
if (!rtc->alarm_en) {
|
||||
ret = atcrtc_check_write_done(rtc);
|
||||
|
|
@ -159,7 +159,6 @@ static void atcrtc_alarm_clear(struct work_struct *work)
|
|||
regmap_update_bits(rtc->regmap, RTC_CR,
|
||||
ALARM_WAKEUP | ALARM_INT, 0);
|
||||
}
|
||||
rtc_unlock(rtc->rtc_dev);
|
||||
}
|
||||
|
||||
static int atcrtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
|
|
@ -328,6 +329,10 @@ static int bd70528_probe(struct platform_device *pdev)
|
|||
device_set_wakeup_capable(&pdev->dev, true);
|
||||
device_wakeup_enable(&pdev->dev);
|
||||
|
||||
ret = devm_pm_set_wake_irq(&pdev->dev, irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(rtc)) {
|
||||
dev_err(&pdev->dev, "RTC device creation failed\n");
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ static ssize_t bq32k_sysfs_show_tricklecharge_bypass(struct device *dev,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
return sprintf(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0);
|
||||
return sysfs_emit(buf, "%d\n", (reg & BQ32K_TCFE) ? 1 : 0);
|
||||
}
|
||||
|
||||
static ssize_t bq32k_sysfs_store_tricklecharge_bypass(struct device *dev,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,28 @@
|
|||
*/
|
||||
|
||||
static bool use_acpi_alarm;
|
||||
module_param(use_acpi_alarm, bool, 0444);
|
||||
static bool use_acpi_alarm_explicit;
|
||||
|
||||
static int set_use_acpi_alarm(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = param_set_bool(val, kp);
|
||||
if (!ret)
|
||||
use_acpi_alarm_explicit = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops use_acpi_alarm_ops = {
|
||||
.flags = KERNEL_PARAM_OPS_FL_NOARG,
|
||||
.set = set_use_acpi_alarm,
|
||||
.get = param_get_bool,
|
||||
};
|
||||
|
||||
param_check_bool(use_acpi_alarm, &use_acpi_alarm);
|
||||
module_param_cb(use_acpi_alarm, &use_acpi_alarm_ops, &use_acpi_alarm, 0444);
|
||||
__MODULE_PARM_TYPE(use_acpi_alarm, "bool");
|
||||
|
||||
static inline int cmos_use_acpi_alarm(void)
|
||||
{
|
||||
|
|
@ -758,7 +779,9 @@ static u32 rtc_handler(void *context)
|
|||
unsigned char rtc_intr;
|
||||
unsigned long flags;
|
||||
|
||||
|
||||
/* Quiesce this event before rtc->irqwork can arm the next one. */
|
||||
acpi_clear_event(ACPI_EVENT_RTC);
|
||||
acpi_disable_event(ACPI_EVENT_RTC, 0);
|
||||
/*
|
||||
* Always update rtc irq when ACPI is used as RTC Alarm.
|
||||
* Or else, ACPI SCI is enabled during suspend/resume only,
|
||||
|
|
@ -781,8 +804,6 @@ static u32 rtc_handler(void *context)
|
|||
}
|
||||
|
||||
pm_wakeup_hard_event(dev);
|
||||
acpi_clear_event(ACPI_EVENT_RTC);
|
||||
acpi_disable_event(ACPI_EVENT_RTC, 0);
|
||||
return ACPI_INTERRUPT_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -822,6 +843,9 @@ static void rtc_wake_off(struct device *dev)
|
|||
#ifdef CONFIG_X86
|
||||
static void use_acpi_alarm_quirks(void)
|
||||
{
|
||||
if (use_acpi_alarm_explicit)
|
||||
return;
|
||||
|
||||
if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -440,7 +440,7 @@ static void ds1305_work(struct work_struct *work)
|
|||
int status;
|
||||
|
||||
/* lock to protect ds1305->ctrl */
|
||||
rtc_lock(ds1305->rtc);
|
||||
guard(rtc_ops)(ds1305->rtc);
|
||||
|
||||
/* Disable the IRQ, and clear its status ... for now, we "know"
|
||||
* that if more than one alarm is active, they're in sync.
|
||||
|
|
@ -458,8 +458,6 @@ static void ds1305_work(struct work_struct *work)
|
|||
if (status < 0)
|
||||
dev_dbg(&spi->dev, "clear irq --> %d\n", status);
|
||||
|
||||
rtc_unlock(ds1305->rtc);
|
||||
|
||||
if (!test_bit(FLAG_EXITING, &ds1305->flags))
|
||||
enable_irq(spi->irq);
|
||||
|
||||
|
|
|
|||
|
|
@ -579,28 +579,27 @@ static irqreturn_t rx8130_irq(int irq, void *dev_id)
|
|||
u8 ctl[3];
|
||||
int ret;
|
||||
|
||||
rtc_lock(ds1307->rtc);
|
||||
guard(rtc_ops)(ds1307->rtc);
|
||||
|
||||
/* Read control registers. */
|
||||
ret = regmap_bulk_read(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
||||
sizeof(ctl));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (!(ctl[1] & RX8130_REG_FLAG_AF))
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
ctl[1] &= ~RX8130_REG_FLAG_AF;
|
||||
ctl[2] &= ~RX8130_REG_CONTROL0_AIE;
|
||||
|
||||
ret = regmap_bulk_write(ds1307->regmap, RX8130_REG_EXTENSION, ctl,
|
||||
sizeof(ctl));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
out:
|
||||
rtc_unlock(ds1307->rtc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -1381,7 +1380,7 @@ static ssize_t ds3231_hwmon_show_temp(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
return sysfs_emit(buf, "%d\n", temp);
|
||||
}
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, 0444, ds3231_hwmon_show_temp,
|
||||
NULL, 0);
|
||||
|
|
@ -1891,7 +1890,6 @@ static int ds1307_probe(struct i2c_client *client)
|
|||
if (!ds1307)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&client->dev, ds1307);
|
||||
ds1307->dev = &client->dev;
|
||||
ds1307->name = client->name;
|
||||
|
||||
|
|
|
|||
|
|
@ -94,9 +94,9 @@ static ssize_t ds1343_show_glitchfilter(struct device *dev,
|
|||
glitch_filt_status = !!(data & DS1343_EGFIL);
|
||||
|
||||
if (glitch_filt_status)
|
||||
return sprintf(buf, "enabled\n");
|
||||
return sysfs_emit(buf, "enabled\n");
|
||||
else
|
||||
return sprintf(buf, "disabled\n");
|
||||
return sysfs_emit(buf, "disabled\n");
|
||||
}
|
||||
|
||||
static ssize_t ds1343_store_glitchfilter(struct device *dev,
|
||||
|
|
@ -180,7 +180,7 @@ static ssize_t ds1343_show_tricklecharger(struct device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
return sprintf(buf, "%s %s\n", diodes, resistors);
|
||||
return sysfs_emit(buf, "%s %s\n", diodes, resistors);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(trickle_charger, 0444, ds1343_show_tricklecharger, NULL);
|
||||
|
|
@ -312,11 +312,11 @@ static irqreturn_t ds1343_thread(int irq, void *dev_id)
|
|||
unsigned int stat;
|
||||
int res = 0;
|
||||
|
||||
rtc_lock(priv->rtc);
|
||||
guard(rtc_ops)(priv->rtc);
|
||||
|
||||
res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
|
||||
if (res)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (stat & DS1343_IRQF0) {
|
||||
stat &= ~DS1343_IRQF0;
|
||||
|
|
@ -328,8 +328,6 @@ static irqreturn_t ds1343_thread(int irq, void *dev_id)
|
|||
DS1343_A0IE, 0);
|
||||
}
|
||||
|
||||
out:
|
||||
rtc_unlock(priv->rtc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -666,7 +666,7 @@ ds1685_rtc_irq_handler(int irq, void *dev_id)
|
|||
if (unlikely(!rtc))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
rtc_lock(rtc->dev);
|
||||
guard(rtc_ops)(rtc->dev);
|
||||
|
||||
/* Ctrlb holds the interrupt-enable bits and ctrlc the flag bits. */
|
||||
ctrlb = rtc->read(rtc, RTC_CTRL_B);
|
||||
|
|
@ -711,7 +711,6 @@ ds1685_rtc_irq_handler(int irq, void *dev_id)
|
|||
}
|
||||
}
|
||||
rtc_update_irq(rtc->dev, num_irqs, events);
|
||||
rtc_unlock(rtc->dev);
|
||||
|
||||
return events ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
|
@ -989,8 +988,8 @@ ds1685_rtc_sysfs_battery_show(struct device *dev,
|
|||
|
||||
ctrld = rtc->read(rtc, RTC_CTRL_D);
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
(ctrld & RTC_CTRL_D_VRT) ? "ok" : "not ok or N/A");
|
||||
return sysfs_emit(buf, "%s\n",
|
||||
(ctrld & RTC_CTRL_D_VRT) ? "ok" : "not ok or N/A");
|
||||
}
|
||||
static DEVICE_ATTR(battery, S_IRUGO, ds1685_rtc_sysfs_battery_show, NULL);
|
||||
|
||||
|
|
@ -1011,8 +1010,8 @@ ds1685_rtc_sysfs_auxbatt_show(struct device *dev,
|
|||
ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
|
||||
ds1685_rtc_switch_to_bank0(rtc);
|
||||
|
||||
return sprintf(buf, "%s\n",
|
||||
(ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "not ok or N/A");
|
||||
return sysfs_emit(buf, "%s\n",
|
||||
(ctrl4a & RTC_CTRL_4A_VRT2) ? "ok" : "not ok or N/A");
|
||||
}
|
||||
static DEVICE_ATTR(auxbatt, S_IRUGO, ds1685_rtc_sysfs_auxbatt_show, NULL);
|
||||
|
||||
|
|
@ -1033,7 +1032,7 @@ ds1685_rtc_sysfs_serial_show(struct device *dev,
|
|||
ds1685_rtc_get_ssn(rtc, ssn);
|
||||
ds1685_rtc_switch_to_bank0(rtc);
|
||||
|
||||
return sprintf(buf, "%8phC\n", ssn);
|
||||
return sysfs_emit(buf, "%8phC\n", ssn);
|
||||
}
|
||||
static DEVICE_ATTR(serial, S_IRUGO, ds1685_rtc_sysfs_serial_show, NULL);
|
||||
|
||||
|
|
@ -1367,7 +1366,7 @@ module_platform_driver(ds1685_rtc_driver);
|
|||
void __noreturn
|
||||
ds1685_rtc_poweroff(struct platform_device *pdev)
|
||||
{
|
||||
u8 ctrla, ctrl4a, ctrl4b;
|
||||
u8 ctrla, ctrlb, ctrl4a, ctrl4b;
|
||||
struct ds1685_priv *rtc;
|
||||
|
||||
/* Check for valid RTC data, else, spin forever. */
|
||||
|
|
@ -1394,6 +1393,8 @@ ds1685_rtc_poweroff(struct platform_device *pdev)
|
|||
ctrla &= ~(RTC_CTRL_A_DV2);
|
||||
rtc->write(rtc, RTC_CTRL_A, ctrla);
|
||||
|
||||
ctrlb = rtc->read(rtc, RTC_CTRL_B);
|
||||
|
||||
/*
|
||||
* Read Control 4A and check the status of the auxillary
|
||||
* battery. This must be present and working (VRT2 = 1)
|
||||
|
|
@ -1408,25 +1409,35 @@ ds1685_rtc_poweroff(struct platform_device *pdev)
|
|||
|
||||
/*
|
||||
* The auxillary battery is present and working.
|
||||
* Enable extended functions (ABE=1), enable
|
||||
* wake-up (WIE=1), and enable kickstart (KSE=1)
|
||||
* in Control 4B.
|
||||
* Enable extended functions (ABE=1) and kickstart
|
||||
* (KSE=1) in Control 4B. Enable wake-up (WIE=1) only
|
||||
* if an alarm is set, else stale alarm registers
|
||||
* can power the system back on.
|
||||
*/
|
||||
ctrl4b = rtc->read(rtc, RTC_EXT_CTRL_4B);
|
||||
ctrl4b |= (RTC_CTRL_4B_ABE | RTC_CTRL_4B_WIE |
|
||||
RTC_CTRL_4B_KSE);
|
||||
ctrl4b |= (RTC_CTRL_4B_ABE | RTC_CTRL_4B_KSE);
|
||||
if (ctrlb & RTC_CTRL_B_AIE)
|
||||
ctrl4b |= RTC_CTRL_4B_WIE;
|
||||
else
|
||||
ctrl4b &= ~(RTC_CTRL_4B_WIE);
|
||||
rtc->write(rtc, RTC_EXT_CTRL_4B, ctrl4b);
|
||||
}
|
||||
|
||||
/* Set PAB to 1 in Control 4A to power the system down. */
|
||||
/*
|
||||
* Set PAB to 1 in Control 4A to power the system down. PAB
|
||||
* is cleared again while KF and KSE, or WF and WIE, are set,
|
||||
* so keep clearing the flags and setting PAB until power is
|
||||
* gone. We do not switch back to bank0.
|
||||
*/
|
||||
dev_warn(&pdev->dev, "Powerdown.\n");
|
||||
msleep(20);
|
||||
rtc->write(rtc, RTC_EXT_CTRL_4A,
|
||||
(ctrl4a | RTC_CTRL_4A_PAB));
|
||||
|
||||
/* Spin ... we do not switch back to bank0. */
|
||||
while(1);
|
||||
unreachable();
|
||||
mdelay(20);
|
||||
while (1) {
|
||||
ctrl4a = rtc->read(rtc, RTC_EXT_CTRL_4A);
|
||||
rtc->write(rtc, RTC_EXT_CTRL_4A,
|
||||
(ctrl4a & ~RTC_CTRL_4A_RWK_MASK) |
|
||||
RTC_CTRL_4A_PAB);
|
||||
mdelay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ds1685_rtc_poweroff);
|
||||
|
|
|
|||
|
|
@ -389,45 +389,39 @@ static irqreturn_t ds3232_irq(int irq, void *dev_id)
|
|||
int ret;
|
||||
int stat, control;
|
||||
|
||||
rtc_lock(ds3232->rtc);
|
||||
guard(rtc_ops)(ds3232->rtc);
|
||||
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_SR, &stat);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (stat & DS3232_REG_SR_A1F) {
|
||||
ret = regmap_read(ds3232->regmap, DS3232_REG_CR, &control);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Read Control Register error %d\n", ret);
|
||||
} else {
|
||||
/* disable alarm1 interrupt */
|
||||
control &= ~(DS3232_REG_CR_A1IE);
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_CR,
|
||||
control);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Write Control Register error %d\n",
|
||||
ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* clear the alarm pend flag */
|
||||
stat &= ~DS3232_REG_SR_A1F;
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Write Status Register error %d\n",
|
||||
ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
/* disable alarm1 interrupt */
|
||||
control &= ~(DS3232_REG_CR_A1IE);
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_CR, control);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Write Control Register error %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
rtc_unlock(ds3232->rtc);
|
||||
/* clear the alarm pend flag */
|
||||
stat &= ~DS3232_REG_SR_A1F;
|
||||
ret = regmap_write(ds3232->regmap, DS3232_REG_SR, stat);
|
||||
if (ret) {
|
||||
dev_warn(ds3232->dev,
|
||||
"Write Status Register error %d\n", ret);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
rtc_update_irq(ds3232->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ static ssize_t comp_preload_show(struct device *dev,
|
|||
|
||||
ep93xx_rtc_get_swcomp(dev->parent, &preload, NULL);
|
||||
|
||||
return sprintf(buf, "%d\n", preload);
|
||||
return sysfs_emit(buf, "%d\n", preload);
|
||||
}
|
||||
static DEVICE_ATTR_RO(comp_preload);
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ static ssize_t comp_delete_show(struct device *dev,
|
|||
|
||||
ep93xx_rtc_get_swcomp(dev->parent, NULL, &delete);
|
||||
|
||||
return sprintf(buf, "%d\n", delete);
|
||||
return sysfs_emit(buf, "%d\n", delete);
|
||||
}
|
||||
static DEVICE_ATTR_RO(comp_delete);
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
|
||||
offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD);
|
||||
|
||||
time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
|
||||
time = offset + (timeu64_t)days * 86400 + hour * 3600 + min * 60 + sec;
|
||||
|
||||
rtc_time64_to_tm(time, tm);
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
|
||||
day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
|
||||
|
||||
offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
|
||||
offset = time - ((timeu64_t)day * 86400 + hour * 3600 + min * 60 + sec);
|
||||
|
||||
writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD);
|
||||
writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR);
|
||||
|
|
@ -119,51 +119,33 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->pclk = devm_clk_get(dev, "PCLK");
|
||||
if (IS_ERR(rtc->pclk)) {
|
||||
rtc->pclk = devm_clk_get_enabled(dev, "PCLK");
|
||||
if (IS_ERR_OR_NULL(rtc->pclk)) {
|
||||
dev_err(dev, "could not get PCLK\n");
|
||||
} else {
|
||||
ret = clk_prepare_enable(rtc->pclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable PCLK\n");
|
||||
return ret;
|
||||
}
|
||||
return PTR_ERR_OR_ZERO(rtc->pclk);
|
||||
}
|
||||
rtc->extclk = devm_clk_get(dev, "EXTCLK");
|
||||
if (IS_ERR(rtc->extclk)) {
|
||||
rtc->extclk = devm_clk_get_enabled(dev, "EXTCLK");
|
||||
if (IS_ERR_OR_NULL(rtc->extclk)) {
|
||||
dev_err(dev, "could not get EXTCLK\n");
|
||||
} else {
|
||||
ret = clk_prepare_enable(rtc->extclk);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable EXTCLK\n");
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
return PTR_ERR_OR_ZERO(rtc->extclk);
|
||||
}
|
||||
|
||||
rtc->rtc_irq = platform_get_irq(pdev, 0);
|
||||
if (rtc->rtc_irq < 0) {
|
||||
ret = rtc->rtc_irq;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
if (rtc->rtc_irq < 0)
|
||||
return rtc->rtc_irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
|
||||
rtc->rtc_base = devm_ioremap(dev, res->start,
|
||||
resource_size(res));
|
||||
if (!rtc->rtc_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
if (!rtc->rtc_base)
|
||||
return -ENOMEM;
|
||||
|
||||
rtc_dev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc_dev)) {
|
||||
ret = PTR_ERR(rtc_dev);
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
if (IS_ERR(rtc_dev))
|
||||
return PTR_ERR(rtc_dev);
|
||||
|
||||
rtc_dev->ops = &ftrtc010_rtc_ops;
|
||||
|
||||
|
|
@ -178,25 +160,13 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
|||
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
|
||||
IRQF_SHARED, pdev->name, dev);
|
||||
if (unlikely(ret))
|
||||
goto err_disable_extclk;
|
||||
return ret;
|
||||
|
||||
return devm_rtc_register_device(rtc_dev);
|
||||
ret = devm_rtc_register_device(rtc_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
err_disable_extclk:
|
||||
clk_disable_unprepare(rtc->extclk);
|
||||
err_disable_pclk:
|
||||
clk_disable_unprepare(rtc->pclk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ftrtc010_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev);
|
||||
|
||||
if (!IS_ERR(rtc->extclk))
|
||||
clk_disable_unprepare(rtc->extclk);
|
||||
if (!IS_ERR(rtc->pclk))
|
||||
clk_disable_unprepare(rtc->pclk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ftrtc010_rtc_dt_match[] = {
|
||||
|
|
@ -212,7 +182,6 @@ static struct platform_driver ftrtc010_rtc_driver = {
|
|||
.of_match_table = ftrtc010_rtc_dt_match,
|
||||
},
|
||||
.probe = ftrtc010_rtc_probe,
|
||||
.remove = ftrtc010_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe);
|
||||
|
|
|
|||
|
|
@ -417,7 +417,7 @@ static irqreturn_t hym8563_irq(int irq, void *dev_id)
|
|||
struct i2c_client *client = hym8563->client;
|
||||
int data, ret;
|
||||
|
||||
rtc_lock(hym8563->rtc);
|
||||
guard(rtc_ops)(hym8563->rtc);
|
||||
|
||||
/* Clear the alarm flag */
|
||||
|
||||
|
|
@ -425,7 +425,7 @@ static irqreturn_t hym8563_irq(int irq, void *dev_id)
|
|||
if (data < 0) {
|
||||
dev_err(&client->dev, "%s: error reading i2c data %d\n",
|
||||
__func__, data);
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
data &= ~HYM8563_CTL2_AF;
|
||||
|
|
@ -436,8 +436,6 @@ static irqreturn_t hym8563_irq(int irq, void *dev_id)
|
|||
__func__, ret);
|
||||
}
|
||||
|
||||
out:
|
||||
rtc_unlock(hym8563->rtc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -615,8 +615,8 @@ static ssize_t timestamp0_show(struct device *dev,
|
|||
if (sr)
|
||||
return sr;
|
||||
|
||||
return sprintf(buf, "%llu\n",
|
||||
(unsigned long long)rtc_tm_to_time64(&tm));
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
(unsigned long long)rtc_tm_to_time64(&tm));
|
||||
};
|
||||
|
||||
static DEVICE_ATTR_RW(timestamp0);
|
||||
|
|
@ -709,7 +709,7 @@ isl1208_sysfs_show_atrim(struct device *dev,
|
|||
if (atr < 0)
|
||||
return atr;
|
||||
|
||||
return sprintf(buf, "%d.%.2d pF\n", atr >> 2, (atr & 0x3) * 25);
|
||||
return sysfs_emit(buf, "%d.%.2d pF\n", atr >> 2, (atr & 0x3) * 25);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL);
|
||||
|
|
@ -722,7 +722,7 @@ isl1208_sysfs_show_dtrim(struct device *dev,
|
|||
if (dtr < 0)
|
||||
return dtr;
|
||||
|
||||
return sprintf(buf, "%d ppm\n", dtr - 100);
|
||||
return sysfs_emit(buf, "%d ppm\n", dtr - 100);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
|
||||
|
|
@ -735,7 +735,7 @@ isl1208_sysfs_show_usr(struct device *dev,
|
|||
if (usr < 0)
|
||||
return usr;
|
||||
|
||||
return sprintf(buf, "0x%.4x\n", usr);
|
||||
return sysfs_emit(buf, "0x%.4x\n", usr);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
|
|
|||
|
|
@ -162,19 +162,15 @@ static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
|
|||
unsigned long events = 0;
|
||||
int flags, flags_afe;
|
||||
|
||||
rtc_lock(m41t80->rtc);
|
||||
guard(rtc_ops)(m41t80->rtc);
|
||||
|
||||
flags_afe = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
||||
if (flags_afe < 0) {
|
||||
rtc_unlock(m41t80->rtc);
|
||||
if (flags_afe < 0)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
|
||||
if (flags <= 0) {
|
||||
rtc_unlock(m41t80->rtc);
|
||||
if (flags <= 0)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (flags & M41T80_FLAGS_AF) {
|
||||
flags &= ~M41T80_FLAGS_AF;
|
||||
|
|
@ -189,8 +185,6 @@ static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
|
|||
flags_afe);
|
||||
}
|
||||
|
||||
rtc_unlock(m41t80->rtc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ static irqreturn_t mcp795_irq(int irq, void *data)
|
|||
struct rtc_device *rtc = spi_get_drvdata(spi);
|
||||
int ret;
|
||||
|
||||
rtc_lock(rtc);
|
||||
guard(rtc_ops)(rtc);
|
||||
|
||||
/* Disable alarm.
|
||||
* There is no need to clear ALM0IF (Alarm 0 Interrupt Flag) bit,
|
||||
|
|
@ -364,7 +364,6 @@ static irqreturn_t mcp795_irq(int irq, void *data)
|
|||
"Failed to disable alarm in IRQ (ret=%d)\n", ret);
|
||||
rtc_update_irq(rtc, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
rtc_unlock(rtc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,8 +131,8 @@ static int mpfs_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
|
||||
alrm->enabled = mode & MODE_WAKE_EN;
|
||||
|
||||
time = (u64)readl(rtcdev->base + ALARM_LOWER_REG) << 32;
|
||||
time |= (readl(rtcdev->base + ALARM_UPPER_REG) & ALARM_UPPER_MASK);
|
||||
time = readl(rtcdev->base + ALARM_LOWER_REG);
|
||||
time |= ((u64)readl(rtcdev->base + ALARM_UPPER_REG) & ALARM_UPPER_MASK) << 32;
|
||||
rtc_time64_to_tm(time, &alrm->time);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
* Author: Tianping.Fang <tianping.fang@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/mt6397/core.h>
|
||||
|
|
@ -243,10 +245,91 @@ static const struct rtc_class_ops mtk_rtc_ops = {
|
|||
.set_alarm = mtk_rtc_set_alarm,
|
||||
};
|
||||
|
||||
/*
|
||||
* The spare byte of each of these registers, in the order a board addresses
|
||||
* them as RTC_NEW_SPARE0 to RTC_NEW_SPARE3.
|
||||
*/
|
||||
static const u32 mtk_rtc_spare_reg[] = {
|
||||
RTC_AL_HOU, RTC_AL_DOM, RTC_AL_DOW, RTC_AL_MTH,
|
||||
};
|
||||
|
||||
static int mtk_rtc_nvram_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct mt6397_rtc *rtc = priv;
|
||||
u8 *buf = val;
|
||||
u32 data;
|
||||
int ret = 0;
|
||||
|
||||
if (offset >= ARRAY_SIZE(mtk_rtc_spare_reg) ||
|
||||
bytes > ARRAY_SIZE(mtk_rtc_spare_reg) - offset)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&rtc->lock);
|
||||
|
||||
while (bytes--) {
|
||||
ret = regmap_read(rtc->regmap,
|
||||
rtc->addr_base + mtk_rtc_spare_reg[offset++],
|
||||
&data);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
*buf++ = FIELD_GET(RTC_SPARE_MASK, data);
|
||||
}
|
||||
|
||||
mutex_unlock(&rtc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_rtc_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct mt6397_rtc *rtc = priv;
|
||||
u8 *buf = val;
|
||||
int ret = 0;
|
||||
|
||||
if (offset >= ARRAY_SIZE(mtk_rtc_spare_reg) ||
|
||||
bytes > ARRAY_SIZE(mtk_rtc_spare_reg) - offset)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&rtc->lock);
|
||||
|
||||
while (bytes--) {
|
||||
ret = regmap_update_bits(rtc->regmap,
|
||||
rtc->addr_base + mtk_rtc_spare_reg[offset++],
|
||||
RTC_SPARE_MASK,
|
||||
FIELD_PREP(RTC_SPARE_MASK, *buf++));
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* None of it reaches the always-on domain until the write trigger,
|
||||
* which commits every pending alarm register at once -- so this runs
|
||||
* under the same lock the alarm paths take, rather than landing in
|
||||
* the middle of one of them.
|
||||
*/
|
||||
ret = mtk_rtc_write_trigger(rtc);
|
||||
out:
|
||||
mutex_unlock(&rtc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct nvmem_config nvmem_cfg = {
|
||||
.name = "mt6397_rtc_spare",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = ARRAY_SIZE(mtk_rtc_spare_reg),
|
||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||
.reg_read = mtk_rtc_nvram_read,
|
||||
.reg_write = mtk_rtc_nvram_write,
|
||||
};
|
||||
struct mt6397_rtc *rtc;
|
||||
int ret;
|
||||
|
||||
|
|
@ -293,7 +376,13 @@ static int mtk_rtc_probe(struct platform_device *pdev)
|
|||
rtc->rtc_dev->start_secs = mktime64(1968, 1, 2, 0, 0, 0);
|
||||
rtc->rtc_dev->set_start_time = true;
|
||||
|
||||
return devm_rtc_register_device(rtc->rtc_dev);
|
||||
ret = devm_rtc_register_device(rtc->rtc_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nvmem_cfg.priv = rtc;
|
||||
|
||||
return devm_rtc_nvmem_register(rtc->rtc_dev, &nvmem_cfg);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <linux/bcd.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
|
@ -23,6 +24,7 @@
|
|||
#define NCT3018Y_REG_CTRL 0x0A /* timer control */
|
||||
#define NCT3018Y_REG_ST 0x0B /* status */
|
||||
#define NCT3018Y_REG_CLKO 0x0C /* clock out */
|
||||
#define NCT3018Y_REG_INTR_CTRL 0x12 /* intrusion control */
|
||||
#define NCT3018Y_REG_PART 0x21 /* part info */
|
||||
|
||||
#define NCT3018Y_BIT_AF BIT(7)
|
||||
|
|
@ -34,6 +36,7 @@
|
|||
#define NCT3018Y_BIT_OFIE BIT(2)
|
||||
#define NCT3018Y_BIT_CIE BIT(1)
|
||||
#define NCT3018Y_BIT_TWO BIT(0)
|
||||
#define NCT3018Y_BIT_INTRUSION BIT(1) /* in NCT3018Y_REG_INTR_CTRL */
|
||||
|
||||
#define NCT3018Y_REG_BAT_MASK 0x07
|
||||
#define NCT3018Y_REG_CLKO_F_MASK 0x03 /* frequenc mask */
|
||||
|
|
@ -491,6 +494,95 @@ static const struct rtc_class_ops nct3018y_rtc_ops = {
|
|||
.ioctl = nct3018y_ioctl,
|
||||
};
|
||||
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
|
||||
static umode_t nct3018y_hwmon_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
if (type == hwmon_intrusion && attr == hwmon_intrusion_alarm)
|
||||
return 0644;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nct3018y_hwmon_read(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct nct3018y *nct3018y = dev_get_drvdata(dev);
|
||||
int flags;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(nct3018y->client,
|
||||
NCT3018Y_REG_INTR_CTRL);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
*val = !!(flags & NCT3018Y_BIT_INTRUSION);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nct3018y_hwmon_write(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct nct3018y *nct3018y = dev_get_drvdata(dev);
|
||||
int flags;
|
||||
|
||||
/* The alarm latches until it is cleared, and only clearing is a
|
||||
* meaningful thing to ask for.
|
||||
*/
|
||||
if (val)
|
||||
return -EINVAL;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(nct3018y->client,
|
||||
NCT3018Y_REG_INTR_CTRL);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
return i2c_smbus_write_byte_data(nct3018y->client,
|
||||
NCT3018Y_REG_INTR_CTRL,
|
||||
flags & ~NCT3018Y_BIT_INTRUSION);
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info * const nct3018y_hwmon_info[] = {
|
||||
HWMON_CHANNEL_INFO(intrusion, HWMON_INTRUSION_ALARM),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops nct3018y_hwmon_ops = {
|
||||
.is_visible = nct3018y_hwmon_is_visible,
|
||||
.read = nct3018y_hwmon_read,
|
||||
.write = nct3018y_hwmon_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info nct3018y_hwmon_chip_info = {
|
||||
.ops = &nct3018y_hwmon_ops,
|
||||
.info = nct3018y_hwmon_info,
|
||||
};
|
||||
|
||||
static void nct3018y_hwmon_register(struct nct3018y *nct3018y)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = devm_hwmon_device_register_with_info(&nct3018y->client->dev,
|
||||
"nct3018y", nct3018y,
|
||||
&nct3018y_hwmon_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(dev))
|
||||
dev_warn(&nct3018y->client->dev,
|
||||
"unable to register hwmon device %ld\n", PTR_ERR(dev));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void nct3018y_hwmon_register(struct nct3018y *nct3018y)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* IS_REACHABLE(CONFIG_HWMON) */
|
||||
|
||||
static int nct3018y_probe(struct i2c_client *client)
|
||||
{
|
||||
struct nct3018y *nct3018y;
|
||||
|
|
@ -568,6 +660,8 @@ static int nct3018y_probe(struct i2c_client *client)
|
|||
nct3018y_clkout_register_clk(nct3018y);
|
||||
#endif
|
||||
|
||||
nct3018y_hwmon_register(nct3018y);
|
||||
|
||||
return devm_rtc_register_device(nct3018y->rtc);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -210,20 +210,16 @@ static irqreturn_t nct6694_irq(int irq, void *dev_id)
|
|||
};
|
||||
int ret;
|
||||
|
||||
rtc_lock(data->rtc);
|
||||
guard(rtc_ops)(data->rtc);
|
||||
|
||||
sts->irq_en = NCT6694_RTC_IRQ_EN;
|
||||
sts->irq_pend = NCT6694_RTC_IRQ_STS;
|
||||
ret = nct6694_write_msg(data->nct6694, &cmd_hd, sts);
|
||||
if (ret) {
|
||||
rtc_unlock(data->rtc);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
rtc_unlock(data->rtc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -358,11 +358,8 @@ static irqreturn_t nvvrs_rtc_irq_handler(int irq, void *data)
|
|||
if (ret < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (ret & NVVRS_INT_SRC1_RTC_MASK) {
|
||||
rtc_lock(info->rtc);
|
||||
if (ret & NVVRS_INT_SRC1_RTC_MASK)
|
||||
rtc_update_irq(info->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
rtc_unlock(info->rtc);
|
||||
}
|
||||
|
||||
/* Clear all interrupts */
|
||||
if (nvvrs_pseq_irq_clear(info) < 0)
|
||||
|
|
|
|||
|
|
@ -408,7 +408,7 @@ static struct omap_rtc *omap_rtc_power_off_rtc;
|
|||
* generates pmic_pwr_enable control, which can be used to control an external
|
||||
* PMIC.
|
||||
*/
|
||||
int omap_rtc_power_off_program(struct device *dev)
|
||||
int omap_rtc_power_off_program(void)
|
||||
{
|
||||
struct omap_rtc *rtc = omap_rtc_power_off_rtc;
|
||||
struct rtc_time tm;
|
||||
|
|
@ -481,10 +481,9 @@ EXPORT_SYMBOL(omap_rtc_power_off_program);
|
|||
*/
|
||||
static void omap_rtc_power_off(void)
|
||||
{
|
||||
struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc;
|
||||
u32 val;
|
||||
|
||||
omap_rtc_power_off_program(rtc->dev.parent);
|
||||
omap_rtc_power_off_program();
|
||||
|
||||
/* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */
|
||||
omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc);
|
||||
|
|
|
|||
|
|
@ -310,7 +310,8 @@ static irqreturn_t pcf2123_rtc_irq(int irq, void *dev)
|
|||
unsigned int val = 0;
|
||||
int ret = IRQ_NONE;
|
||||
|
||||
rtc_lock(pcf2123->rtc);
|
||||
guard(rtc_ops)(pcf2123->rtc);
|
||||
|
||||
regmap_read(pcf2123->map, PCF2123_REG_CTRL2, &val);
|
||||
|
||||
/* Alarm? */
|
||||
|
|
@ -323,8 +324,6 @@ static irqreturn_t pcf2123_rtc_irq(int irq, void *dev)
|
|||
rtc_update_irq(pcf2123->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
}
|
||||
|
||||
rtc_unlock(pcf2123->rtc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -972,7 +972,7 @@ static ssize_t timestamp_show(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return sprintf(buf, "%llu\n", (unsigned long long)ts);
|
||||
return sysfs_emit(buf, "%llu\n", (unsigned long long)ts);
|
||||
}
|
||||
|
||||
static ssize_t timestamp0_show(struct device *dev,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
#define PCF85063_REG_CTRL2 0x01
|
||||
#define PCF85063_CTRL2_AF BIT(6)
|
||||
#define PCF85063_CTRL2_AIE BIT(7)
|
||||
#define PCF85063_CTRL2_TF BIT(3)
|
||||
|
||||
#define PCF85063_REG_OFFSET 0x02
|
||||
#define PCF85063_OFFSET_SIGN_BIT 6 /* 2's complement sign bit */
|
||||
|
|
@ -63,6 +64,14 @@
|
|||
#define PCF85063_REG_ALM_S 0x0b
|
||||
#define PCF85063_AEN BIT(7)
|
||||
|
||||
#define PCF85063_REG_TIMER_VALUE 0x10
|
||||
#define PCF85063_REG_TIMER_MODE 0x11
|
||||
#define PCF85063_TIMER_MODE_TI_TP BIT(0)
|
||||
#define PCF85063_TIMER_MODE_TIE BIT(1)
|
||||
#define PCF85063_TIMER_MODE_TE BIT(2)
|
||||
#define PCF85063_TIMER_MODE_TCF_MASK GENMASK(4, 3)
|
||||
#define PCF85063_TIMER_MODE_TCF_1HZ (2 << 3)
|
||||
|
||||
struct pcf85063_config {
|
||||
struct regmap_config regmap;
|
||||
unsigned has_alarms:1;
|
||||
|
|
@ -188,20 +197,75 @@ static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The chip has no dedicated 1 Hz update interrupt. Drive the countdown timer
|
||||
* at 1 Hz to emulate it, so the RTC core can offload UIE to the hardware
|
||||
* instead of polling. A free-running timer is left untouched to preserve its
|
||||
* phase across the per-second re-arming done by the core.
|
||||
*/
|
||||
static int pcf85063_set_timer_1hz(struct pcf85063 *pcf85063, bool enable)
|
||||
{
|
||||
unsigned int mask = PCF85063_TIMER_MODE_TCF_MASK |
|
||||
PCF85063_TIMER_MODE_TIE |
|
||||
PCF85063_TIMER_MODE_TI_TP |
|
||||
PCF85063_TIMER_MODE_TE;
|
||||
unsigned int mode = 0;
|
||||
unsigned int cur;
|
||||
int ret;
|
||||
|
||||
if (enable)
|
||||
mode = PCF85063_TIMER_MODE_TCF_1HZ | PCF85063_TIMER_MODE_TIE |
|
||||
PCF85063_TIMER_MODE_TI_TP | PCF85063_TIMER_MODE_TE;
|
||||
|
||||
ret = regmap_read(pcf85063->regmap, PCF85063_REG_TIMER_MODE, &cur);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((cur & mask) == mode)
|
||||
return 0;
|
||||
|
||||
/* Stop the counter before changing its reload value. */
|
||||
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_TIMER_MODE,
|
||||
PCF85063_TIMER_MODE_TE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (enable) {
|
||||
ret = regmap_write(pcf85063->regmap, PCF85063_REG_TIMER_VALUE, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clear TF but preserve AF */
|
||||
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_TF | PCF85063_CTRL2_AF, PCF85063_CTRL2_AF);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_TIMER_MODE,
|
||||
mask, mode);
|
||||
}
|
||||
|
||||
static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
u8 buf[5];
|
||||
int ret;
|
||||
|
||||
ret = pcf85063_set_timer_1hz(pcf85063, pcf85063->rtc->uie_rtctimer.enabled);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf[0] = bin2bcd(alrm->time.tm_sec);
|
||||
buf[1] = bin2bcd(alrm->time.tm_min);
|
||||
buf[2] = bin2bcd(alrm->time.tm_hour);
|
||||
buf[3] = bin2bcd(alrm->time.tm_mday);
|
||||
buf[4] = PCF85063_AEN; /* Do not match on week day */
|
||||
|
||||
/* Preserve TF */
|
||||
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0);
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF,
|
||||
PCF85063_CTRL2_TF);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -211,23 +275,33 @@ static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
return ret;
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
|
||||
alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF);
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF,
|
||||
pcf85063->rtc->aie_timer.enabled ?
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF :
|
||||
PCF85063_CTRL2_AF | PCF85063_CTRL2_TF);
|
||||
}
|
||||
|
||||
static int pcf85063_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = pcf85063_set_timer_1hz(pcf85063, pcf85063->rtc->uie_rtctimer.enabled);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE,
|
||||
enabled ? PCF85063_CTRL2_AIE : 0);
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_TF,
|
||||
pcf85063->rtc->aie_timer.enabled ?
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_TF : PCF85063_CTRL2_TF);
|
||||
}
|
||||
|
||||
static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct pcf85063 *pcf85063 = dev_id;
|
||||
unsigned long events = RTC_IRQF;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
|
|
@ -236,14 +310,33 @@ static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
|
|||
return IRQ_NONE;
|
||||
|
||||
if (val & PCF85063_CTRL2_AF) {
|
||||
rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
|
||||
0);
|
||||
return IRQ_HANDLED;
|
||||
events |= RTC_AF;
|
||||
val &= ~PCF85063_CTRL2_AF;
|
||||
ret = IRQ_HANDLED;
|
||||
} else {
|
||||
/* Preserve AF if it comes after the above regmap_read() */
|
||||
val |= PCF85063_CTRL2_AF;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
if (val & PCF85063_CTRL2_TF) {
|
||||
events |= RTC_UF;
|
||||
val &= ~PCF85063_CTRL2_TF;
|
||||
ret = IRQ_HANDLED;
|
||||
} else {
|
||||
/*
|
||||
* While not documented, TF demonstrates the same
|
||||
* write-1-to-preserve semantics as AF in real HW
|
||||
*/
|
||||
val |= PCF85063_CTRL2_TF;
|
||||
}
|
||||
|
||||
if (ret == IRQ_HANDLED) {
|
||||
regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_CTRL2_AF | PCF85063_CTRL2_TF, val);
|
||||
rtc_update_irq(pcf85063->rtc, 1, events);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcf85063_read_offset(struct device *dev, long *offset)
|
||||
|
|
@ -432,7 +525,8 @@ static int pcf85063_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
if (clkout_rates[i] == rate)
|
||||
return regmap_update_bits(pcf85063->regmap,
|
||||
PCF85063_REG_CTRL2,
|
||||
PCF85063_REG_CLKO_F_MASK, i);
|
||||
PCF85063_REG_CLKO_F_MASK | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF,
|
||||
i | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -461,7 +555,8 @@ static int pcf85063_clkout_control(struct clk_hw *hw, bool enable)
|
|||
}
|
||||
|
||||
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
|
||||
PCF85063_REG_CLKO_F_MASK, buf);
|
||||
PCF85063_REG_CLKO_F_MASK | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF,
|
||||
buf | PCF85063_CTRL2_AF | PCF85063_CTRL2_TF);
|
||||
}
|
||||
|
||||
static int pcf85063_clkout_prepare(struct clk_hw *hw)
|
||||
|
|
@ -495,12 +590,13 @@ static const struct clk_ops pcf85063_clkout_ops = {
|
|||
.set_rate = pcf85063_clkout_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
|
||||
static int pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
|
||||
{
|
||||
struct clk *clk;
|
||||
struct device *dev = pcf85063->rtc->dev.parent;
|
||||
struct clk_init_data init = {};
|
||||
struct device_node *node = pcf85063->rtc->dev.parent->of_node;
|
||||
struct device_node *node = dev->of_node;
|
||||
struct device_node *fixed_clock;
|
||||
int ret;
|
||||
|
||||
fixed_clock = of_get_child_by_name(node, "clock");
|
||||
if (fixed_clock) {
|
||||
|
|
@ -510,7 +606,7 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
|
|||
* registered automatically when being referenced.
|
||||
*/
|
||||
of_node_put(fixed_clock);
|
||||
return NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
init.name = "pcf85063-clkout";
|
||||
|
|
@ -524,12 +620,12 @@ static struct clk *pcf85063_clkout_register_clk(struct pcf85063 *pcf85063)
|
|||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
||||
/* register the clock */
|
||||
clk = devm_clk_register(&pcf85063->rtc->dev, &pcf85063->clkout_hw);
|
||||
ret = devm_clk_hw_register(dev, &pcf85063->clkout_hw);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
|
||||
return clk;
|
||||
return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
|
||||
&pcf85063->clkout_hw);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -607,6 +703,7 @@ static int pcf85063_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
dev_warn(&pcf85063->rtc->dev,
|
||||
"unable to request IRQ, alarms disabled\n");
|
||||
} else {
|
||||
set_bit(RTC_FEATURE_UPDATE_INTERRUPT, pcf85063->rtc->features);
|
||||
set_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
|
||||
device_init_wakeup(dev, true);
|
||||
err = dev_pm_set_wake_irq(dev, irq);
|
||||
|
|
|
|||
|
|
@ -479,7 +479,8 @@ static int pcf8563_probe(struct i2c_client *client)
|
|||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
|
||||
!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -ENODEV;
|
||||
|
||||
pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563),
|
||||
|
|
@ -531,7 +532,7 @@ static int pcf8563_probe(struct i2c_client *client)
|
|||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, pcf8563_irq,
|
||||
IRQF_SHARED | IRQF_ONESHOT | irqflags,
|
||||
pcf8563_driver.driver.name, client);
|
||||
pcf8563_driver.driver.name, pcf8563);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "unable to request IRQ %d\n",
|
||||
client->irq);
|
||||
|
|
|
|||
278
drivers/rtc/rtc-raspberrypi.c
Normal file
278
drivers/rtc/rtc-raspberrypi.c
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/**
|
||||
* rtc-raspberrypi.c
|
||||
*
|
||||
* RTC driver using firmware mailbox
|
||||
* Supports battery backed RTC and wake alarms
|
||||
*
|
||||
* Based on rtc-meson-vrtc by Neil Armstrong
|
||||
*
|
||||
* Copyright (c) 2023, Raspberry Pi Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/of.h>
|
||||
#include <soc/bcm2835/raspberrypi-firmware.h>
|
||||
|
||||
#define RTC_TIME 0
|
||||
#define RTC_ALARM 1
|
||||
#define RTC_ALARM_PENDING 2
|
||||
#define RTC_ALARM_ENABLE 3
|
||||
#define RTC_BBAT_CHG_VOLTS 4
|
||||
#define RTC_BBAT_CHG_VOLTS_MIN 5
|
||||
#define RTC_BBAT_CHG_VOLTS_MAX 6
|
||||
#define RTC_BBAT_VOLTS 7
|
||||
|
||||
struct rpi_rtc_data {
|
||||
struct rtc_device *rtc;
|
||||
struct rpi_firmware *fw;
|
||||
u32 bbat_vchg_millivolts;
|
||||
u32 bbat_vchg_min_millivolts;
|
||||
u32 bbat_vchg_max_millivolts;
|
||||
};
|
||||
|
||||
struct rpi_rtc_reg {
|
||||
__le32 reg;
|
||||
__le32 val;
|
||||
} __packed;
|
||||
|
||||
static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_TIME)
|
||||
};
|
||||
int err;
|
||||
|
||||
err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_GET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
rtc_time64_to_tm(le32_to_cpu(data.val), tm);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rpi_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_TIME),
|
||||
.val = cpu_to_le32(rtc_tm_to_time64(tm))
|
||||
};
|
||||
|
||||
return rpi_firmware_property(rtc->fw, RPI_FIRMWARE_SET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
}
|
||||
|
||||
static int rpi_rtc_alarm_irq_is_enabled(struct device *dev, unsigned char *enabled)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_ALARM_ENABLE)
|
||||
};
|
||||
int err;
|
||||
|
||||
err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_GET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
*enabled = le32_to_cpu(data.val) & 0x1;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rpi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_ALARM_ENABLE),
|
||||
.val = cpu_to_le32(enabled)
|
||||
};
|
||||
|
||||
return rpi_firmware_property(rtc->fw, RPI_FIRMWARE_SET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
}
|
||||
|
||||
static int rpi_rtc_alarm_clear_pending(struct device *dev)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_ALARM_PENDING),
|
||||
.val = cpu_to_le32(1)
|
||||
};
|
||||
|
||||
return rpi_firmware_property(rtc->fw, RPI_FIRMWARE_SET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
}
|
||||
|
||||
static int rpi_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_ALARM)
|
||||
};
|
||||
int err;
|
||||
|
||||
err = rpi_rtc_alarm_irq_is_enabled(dev, &alarm->enabled);
|
||||
if (!err)
|
||||
err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_GET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
rtc_time64_to_tm(le32_to_cpu(data.val), &alarm->time);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int rpi_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_ALARM),
|
||||
.val = cpu_to_le32(rtc_tm_to_time64(&alarm->time))
|
||||
};
|
||||
int err;
|
||||
|
||||
err = rpi_rtc_alarm_clear_pending(dev);
|
||||
|
||||
if (!err)
|
||||
err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_SET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
|
||||
if (!err)
|
||||
err = rpi_rtc_alarm_irq_enable(dev, alarm->enabled);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rpi_rtc_ops = {
|
||||
.read_time = rpi_rtc_read_time,
|
||||
.set_time = rpi_rtc_set_time,
|
||||
.read_alarm = rpi_rtc_read_alarm,
|
||||
.set_alarm = rpi_rtc_set_alarm,
|
||||
.alarm_irq_enable = rpi_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static void rpi_rtc_set_limits(struct device *dev)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_BBAT_CHG_VOLTS_MIN)
|
||||
};
|
||||
|
||||
int err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_GET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
if (!err)
|
||||
rtc->bbat_vchg_min_millivolts = le32_to_cpu(data.val) / 1000U;
|
||||
else
|
||||
dev_warn(dev, "failed to get minimum trickle charge voltage\n");
|
||||
|
||||
data.reg = cpu_to_le32(RTC_BBAT_CHG_VOLTS_MAX);
|
||||
data.val = cpu_to_le32(0);
|
||||
err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_GET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
if (!err)
|
||||
rtc->bbat_vchg_max_millivolts = le32_to_cpu(data.val) / 1000U;
|
||||
else
|
||||
dev_warn(dev, "failed to get maximum trickle charge voltage\n");
|
||||
}
|
||||
|
||||
static void rpi_rtc_set_charge_voltage(struct device *dev)
|
||||
{
|
||||
struct rpi_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
struct rpi_rtc_reg data = {
|
||||
.reg = cpu_to_le32(RTC_BBAT_CHG_VOLTS),
|
||||
.val = cpu_to_le32(rtc->bbat_vchg_millivolts * 1000U)
|
||||
};
|
||||
int err;
|
||||
|
||||
if (rtc->bbat_vchg_millivolts != 0 &&
|
||||
(rtc->bbat_vchg_millivolts < rtc->bbat_vchg_min_millivolts ||
|
||||
rtc->bbat_vchg_millivolts > rtc->bbat_vchg_max_millivolts)) {
|
||||
dev_warn(dev, "trickle charge voltage %umV is outside of the supported range (%umV - %umV)\n",
|
||||
rtc->bbat_vchg_millivolts,
|
||||
rtc->bbat_vchg_min_millivolts,
|
||||
rtc->bbat_vchg_max_millivolts);
|
||||
return;
|
||||
}
|
||||
|
||||
err = rpi_firmware_property(rtc->fw, RPI_FIRMWARE_SET_RTC_REG,
|
||||
&data, sizeof(data));
|
||||
|
||||
if (err)
|
||||
dev_err(dev, "failed to set trickle charge voltage to %umV: %d\n",
|
||||
rtc->bbat_vchg_millivolts, err);
|
||||
else if (rtc->bbat_vchg_millivolts)
|
||||
dev_info(dev, "trickle charging enabled at %umV\n",
|
||||
rtc->bbat_vchg_millivolts);
|
||||
}
|
||||
|
||||
static int rpi_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rpi_rtc_data *rtc;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rpi_firmware *firmware;
|
||||
int ret;
|
||||
|
||||
// Get the firmware device from the parent device
|
||||
firmware = devm_rpi_firmware_get(dev, dev->parent->of_node);
|
||||
|
||||
if (!firmware)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
rtc->fw = firmware;
|
||||
|
||||
ret = devm_device_init_wakeup(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtc))
|
||||
return PTR_ERR(rtc->rtc);
|
||||
|
||||
rtc->rtc->range_max = U32_MAX; /* 2106-02-07 */
|
||||
|
||||
set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->rtc->features);
|
||||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtc->features);
|
||||
|
||||
rtc->rtc->ops = &rpi_rtc_ops;
|
||||
|
||||
rpi_rtc_alarm_clear_pending(dev);
|
||||
|
||||
/*
|
||||
* The trickle-voltage property lives on the parent firmware node
|
||||
* because we deliberately do not create a DT child node just to
|
||||
* instantiate this driver (see DT maintainer guidance).
|
||||
* The firmware driver registers us as a platform device at runtime.
|
||||
*/
|
||||
rtc->bbat_vchg_millivolts = 0;
|
||||
of_property_read_u32(dev->parent->of_node, "trickle-voltage-millivolt",
|
||||
&rtc->bbat_vchg_millivolts);
|
||||
|
||||
rpi_rtc_set_limits(dev);
|
||||
rpi_rtc_set_charge_voltage(dev);
|
||||
|
||||
return devm_rtc_register_device(rtc->rtc);
|
||||
}
|
||||
|
||||
static const struct platform_device_id raspberrypi_rtc_id[] = {
|
||||
{ "raspberrypi-rtc" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, raspberrypi_rtc_id);
|
||||
|
||||
static struct platform_driver rpi_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "raspberrypi-rtc",
|
||||
},
|
||||
.id_table = raspberrypi_rtc_id,
|
||||
.probe = rpi_rtc_probe
|
||||
};
|
||||
|
||||
module_platform_driver(rpi_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Bell <jonathan@raspberrypi.com>");
|
||||
MODULE_AUTHOR("Sander Speetjens <sander.speetjens@gmail.com>");
|
||||
MODULE_DESCRIPTION("Raspberry Pi RTC driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
|
@ -52,6 +52,7 @@
|
|||
#define RS5C_REG_CTRL2 15
|
||||
# define RS5C372_CTRL2_24 (1 << 5)
|
||||
# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2x2x */
|
||||
# define R2223x_CTRL2_ECO (1 << 7) /* only if R2223x */
|
||||
# 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 */
|
||||
|
|
@ -68,6 +69,7 @@ enum rtc_type {
|
|||
rtc_undef = 0,
|
||||
rtc_r2025sd,
|
||||
rtc_r2221tl,
|
||||
rtc_r2223x,
|
||||
rtc_rs5c372a,
|
||||
rtc_rs5c372b,
|
||||
rtc_rv5c386,
|
||||
|
|
@ -77,6 +79,7 @@ enum rtc_type {
|
|||
static const struct i2c_device_id rs5c372_id[] = {
|
||||
{ .name = "r2025sd", .driver_data = rtc_r2025sd },
|
||||
{ .name = "r2221tl", .driver_data = rtc_r2221tl },
|
||||
{ .name = "r2223x", .driver_data = rtc_r2223x },
|
||||
{ .name = "rs5c372a", .driver_data = rtc_rs5c372a },
|
||||
{ .name = "rs5c372b", .driver_data = rtc_rs5c372b },
|
||||
{ .name = "rv5c386", .driver_data = rtc_rv5c386 },
|
||||
|
|
@ -94,6 +97,10 @@ static const __maybe_unused struct of_device_id rs5c372_of_match[] = {
|
|||
.compatible = "ricoh,r2221tl",
|
||||
.data = (void *)rtc_r2221tl
|
||||
},
|
||||
{
|
||||
.compatible = "ricoh,r2223x",
|
||||
.data = (void *)rtc_r2223x
|
||||
},
|
||||
{
|
||||
.compatible = "ricoh,rs5c372a",
|
||||
.data = (void *)rtc_rs5c372a
|
||||
|
|
@ -221,8 +228,10 @@ static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
switch (rs5c->type) {
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
|
||||
(rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) {
|
||||
((rs5c->type == rtc_r2221tl || rs5c->type == rtc_r2223x) &&
|
||||
(ctrl2 & R2x2x_CTRL2_XSTP))) {
|
||||
dev_warn(&client->dev, "rtc oscillator interruption detected. Please reset the rtc clock.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -292,6 +301,7 @@ static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
switch (rs5c->type) {
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
ctrl2 &= ~(R2x2x_CTRL2_VDET | R2x2x_CTRL2_PON);
|
||||
if (rs5c->type == rtc_r2025sd)
|
||||
ctrl2 |= R2x2x_CTRL2_XSTP;
|
||||
|
|
@ -511,8 +521,10 @@ static int rs5c372_ioctl(struct device *dev, unsigned int cmd, unsigned long arg
|
|||
switch (rs5c->type) {
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
if ((rs5c->type == rtc_r2025sd && !(ctrl2 & R2x2x_CTRL2_XSTP)) ||
|
||||
(rs5c->type == rtc_r2221tl && (ctrl2 & R2x2x_CTRL2_XSTP))) {
|
||||
((rs5c->type == rtc_r2221tl || rs5c->type == rtc_r2223x) &&
|
||||
(ctrl2 & R2x2x_CTRL2_XSTP))) {
|
||||
flags |= RTC_VL_DATA_INVALID;
|
||||
}
|
||||
if (ctrl2 & R2x2x_CTRL2_VDET)
|
||||
|
|
@ -527,7 +539,8 @@ static int rs5c372_ioctl(struct device *dev, unsigned int cmd, unsigned long arg
|
|||
return put_user(flags, (unsigned int __user *)arg);
|
||||
case RTC_VL_CLR:
|
||||
/* clear VDET bit */
|
||||
if (rs5c->type == rtc_r2025sd || rs5c->type == rtc_r2221tl) {
|
||||
if (rs5c->type == rtc_r2025sd || rs5c->type == rtc_r2221tl ||
|
||||
rs5c->type == rtc_r2223x) {
|
||||
ctrl2 &= ~R2x2x_CTRL2_VDET;
|
||||
if (i2c_smbus_write_byte_data(rs5c->client, addr, ctrl2) < 0) {
|
||||
dev_dbg(&rs5c->client->dev, "%s: write error in line %i\n",
|
||||
|
|
@ -554,6 +567,7 @@ static int rs5c372_read_offset(struct device *dev, long *offset)
|
|||
|
||||
switch (rs5c->type) {
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
ppb_per_step = val & R2221TL_TRIM_DEV ? 1017 : 3051;
|
||||
break;
|
||||
case rtc_rs5c372a:
|
||||
|
|
@ -600,6 +614,7 @@ static int rs5c372_set_offset(struct device *dev, long offset)
|
|||
}
|
||||
break;
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
/*
|
||||
* Check if it is possible to use high resolution mode (DEV=1).
|
||||
* In this mode, the minimum resolution is 2 / (32768 * 20 * 3),
|
||||
|
|
@ -684,7 +699,7 @@ static ssize_t rs5c372_sysfs_show_trim(struct device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%d\n", trim);
|
||||
return sysfs_emit(buf, "%d\n", trim);
|
||||
}
|
||||
static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL);
|
||||
|
||||
|
|
@ -697,7 +712,7 @@ static ssize_t rs5c372_sysfs_show_osc(struct device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000);
|
||||
return sysfs_emit(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000);
|
||||
}
|
||||
static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL);
|
||||
|
||||
|
|
@ -750,6 +765,7 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
|
|||
return ret;
|
||||
break;
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
if (!(buf[1] & R2x2x_CTRL2_XSTP))
|
||||
return ret;
|
||||
break;
|
||||
|
|
@ -768,6 +784,7 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
|
|||
break;
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
case rtc_rv5c386:
|
||||
case rtc_rv5c387a:
|
||||
buf[0] |= RV5C387_CTRL1_24;
|
||||
|
|
@ -791,6 +808,24 @@ static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int rs5c372_set_eco_mode(struct rs5c372 *rs5c372, bool eco)
|
||||
{
|
||||
struct i2c_client *client = rs5c372->client;
|
||||
int ctrl2;
|
||||
|
||||
ctrl2 = i2c_smbus_read_byte_data(client, RS5C_ADDR(RS5C_REG_CTRL2));
|
||||
if (ctrl2 < 0)
|
||||
return ctrl2;
|
||||
|
||||
if (eco)
|
||||
ctrl2 |= R2223x_CTRL2_ECO;
|
||||
else
|
||||
ctrl2 &= ~R2223x_CTRL2_ECO;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, RS5C_ADDR(RS5C_REG_CTRL2),
|
||||
ctrl2);
|
||||
}
|
||||
|
||||
static int rs5c372_probe(struct i2c_client *client)
|
||||
{
|
||||
int err = 0;
|
||||
|
|
@ -847,6 +882,7 @@ static int rs5c372_probe(struct i2c_client *client)
|
|||
break;
|
||||
case rtc_r2025sd:
|
||||
case rtc_r2221tl:
|
||||
case rtc_r2223x:
|
||||
case rtc_rv5c386:
|
||||
case rtc_rv5c387a:
|
||||
if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24)
|
||||
|
|
@ -876,6 +912,7 @@ static int rs5c372_probe(struct i2c_client *client)
|
|||
({ char *s; switch (rs5c372->type) {
|
||||
case rtc_r2025sd: s = "r2025sd"; break;
|
||||
case rtc_r2221tl: s = "r2221tl"; break;
|
||||
case rtc_r2223x: s = "r2223x"; break;
|
||||
case rtc_rs5c372a: s = "rs5c372a"; break;
|
||||
case rtc_rs5c372b: s = "rs5c372b"; break;
|
||||
case rtc_rv5c386: s = "rv5c386"; break;
|
||||
|
|
@ -885,6 +922,15 @@ static int rs5c372_probe(struct i2c_client *client)
|
|||
rs5c372->time24 ? "24hr" : "am/pm"
|
||||
);
|
||||
|
||||
if (rs5c372->type == rtc_r2223x) {
|
||||
bool eco = device_property_read_bool(&client->dev,
|
||||
"ricoh,eco-mode");
|
||||
|
||||
err = rs5c372_set_eco_mode(rs5c372, eco);
|
||||
if (err < 0)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* REVISIT use client->irq to register alarm irq ... */
|
||||
rs5c372->rtc = devm_rtc_device_register(&client->dev,
|
||||
rs5c372_driver.driver.name,
|
||||
|
|
|
|||
|
|
@ -147,8 +147,8 @@ static ssize_t timestamp0_show(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%llu\n",
|
||||
(unsigned long long)rtc_tm_to_time64(&tm));
|
||||
return sysfs_emit(buf, "%llu\n",
|
||||
(unsigned long long)rtc_tm_to_time64(&tm));
|
||||
};
|
||||
|
||||
static DEVICE_ATTR_RW(timestamp0);
|
||||
|
|
@ -164,7 +164,7 @@ static ssize_t timestamp0_count_show(struct device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", count);
|
||||
return sysfs_emit(buf, "%u\n", count);
|
||||
};
|
||||
|
||||
static DEVICE_ATTR_RO(timestamp0_count);
|
||||
|
|
|
|||
|
|
@ -270,19 +270,17 @@ static irqreturn_t rv3029_handle_irq(int irq, void *dev_id)
|
|||
unsigned long events = 0;
|
||||
int ret;
|
||||
|
||||
rtc_lock(rv3029->rtc);
|
||||
guard(rtc_ops)(rv3029->rtc);
|
||||
|
||||
ret = regmap_read(rv3029->regmap, RV3029_IRQ_CTRL, &controls);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Read IRQ Control Register error %d\n", ret);
|
||||
rtc_unlock(rv3029->rtc);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
ret = regmap_read(rv3029->regmap, RV3029_IRQ_FLAGS, &flags);
|
||||
if (ret) {
|
||||
dev_warn(dev, "Read IRQ Flags Register error %d\n", ret);
|
||||
rtc_unlock(rv3029->rtc);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -297,7 +295,6 @@ static irqreturn_t rv3029_handle_irq(int irq, void *dev_id)
|
|||
regmap_write(rv3029->regmap, RV3029_IRQ_FLAGS, flags);
|
||||
regmap_write(rv3029->regmap, RV3029_IRQ_CTRL, controls);
|
||||
}
|
||||
rtc_unlock(rv3029->rtc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
|
@ -604,7 +601,7 @@ static ssize_t rv3029_hwmon_show_temp(struct device *dev,
|
|||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", temp_mC);
|
||||
return sysfs_emit(buf, "%d\n", temp_mC);
|
||||
}
|
||||
|
||||
static ssize_t rv3029_hwmon_set_update_interval(struct device *dev,
|
||||
|
|
@ -657,7 +654,7 @@ static ssize_t rv3029_hwmon_show_update_interval(struct device *dev,
|
|||
interval_ms = 0;
|
||||
}
|
||||
|
||||
return sprintf(buf, "%d\n", interval_ms);
|
||||
return sysfs_emit(buf, "%d\n", interval_ms);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, rv3029_hwmon_show_temp,
|
||||
|
|
|
|||
|
|
@ -73,11 +73,10 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
|||
struct rx8010_data *rx8010 = i2c_get_clientdata(client);
|
||||
int flagreg, err;
|
||||
|
||||
rtc_lock(rx8010->rtc);
|
||||
guard(rtc_ops)(rx8010->rtc);
|
||||
|
||||
err = regmap_read(rx8010->regs, RX8010_FLAG, &flagreg);
|
||||
if (err) {
|
||||
rtc_unlock(rx8010->rtc);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
|
|
@ -100,7 +99,6 @@ static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
|
|||
}
|
||||
|
||||
err = regmap_write(rx8010->regs, RX8010_FLAG, flagreg);
|
||||
rtc_unlock(rx8010->rtc);
|
||||
return err ? IRQ_NONE : IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -180,10 +180,11 @@ static irqreturn_t rx8025_handle_irq(int irq, void *dev_id)
|
|||
struct rx8025_data *rx8025 = i2c_get_clientdata(client);
|
||||
int status, xstp;
|
||||
|
||||
rtc_lock(rx8025->rtc);
|
||||
guard(rtc_ops)(rx8025->rtc);
|
||||
|
||||
status = rx8025_read_reg(client, RX8025_REG_CTRL2);
|
||||
if (status < 0)
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
|
||||
xstp = rx8025_is_osc_stopped(rx8025->model, status);
|
||||
if (xstp)
|
||||
|
|
@ -201,13 +202,10 @@ static irqreturn_t rx8025_handle_irq(int irq, void *dev_id)
|
|||
status &= RX8025_BIT_CTRL2_DAFG;
|
||||
if (rx8025_write_reg(client, RX8025_REG_CTRL1,
|
||||
rx8025->ctrl1 & ~RX8025_BIT_CTRL1_DALE))
|
||||
goto out;
|
||||
return IRQ_HANDLED;
|
||||
rtc_update_irq(rx8025->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
}
|
||||
|
||||
out:
|
||||
rtc_unlock(rx8025->rtc);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
@ -488,7 +486,7 @@ static ssize_t rx8025_sysfs_show_clock_adjust(struct device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%ld\n", -adj);
|
||||
return sysfs_emit(buf, "%ld\n", -adj);
|
||||
}
|
||||
|
||||
static ssize_t rx8025_sysfs_store_clock_adjust(struct device *dev,
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
|
||||
struct rzn1_rtc_data {
|
||||
bool has_subu;
|
||||
bool broken_irqs;
|
||||
};
|
||||
|
||||
struct rzn1_rtc {
|
||||
|
|
@ -396,6 +397,11 @@ static const struct rtc_class_ops rzn1_rtc_ops_scmp = {
|
|||
.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static const struct rtc_class_ops rzn1_rtc_ops_scmp_no_alarm = {
|
||||
.read_time = rzn1_rtc_read_time,
|
||||
.set_time = rzn1_rtc_set_time,
|
||||
};
|
||||
|
||||
static void rzn1_rtc_disable_hardware(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
|
|
@ -431,10 +437,6 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(rtc->base))
|
||||
return dev_err_probe(dev, PTR_ERR(rtc->base), "Missing reg\n");
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "alarm");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
rtc->rtcdev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtcdev))
|
||||
return PTR_ERR(rtc->rtcdev);
|
||||
|
|
@ -503,25 +505,40 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
spin_lock_init(&rtc->ctl1_access_lock);
|
||||
|
||||
ret = devm_request_irq(dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "RTC alarm interrupt not available\n");
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "pps");
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return irq;
|
||||
if (irq >= 0)
|
||||
ret = devm_request_irq(dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc);
|
||||
|
||||
if (irq < 0 || ret) {
|
||||
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
|
||||
if (data->broken_irqs) {
|
||||
/* All known broken irqs are on SCMP only variants */
|
||||
rtc->rtcdev->ops = &rzn1_rtc_ops_scmp_no_alarm;
|
||||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
|
||||
dev_warn(dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n");
|
||||
} else {
|
||||
irq = platform_get_irq_byname(pdev, "alarm");
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
ret = devm_request_irq(dev, irq, rzn1_rtc_alarm_irq, 0, "RZN1 RTC Alarm", rtc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "RTC alarm interrupt not available\n");
|
||||
|
||||
irq = platform_get_irq_byname_optional(pdev, "pps");
|
||||
if (irq == -EPROBE_DEFER)
|
||||
return irq;
|
||||
if (irq >= 0)
|
||||
ret = devm_request_irq(dev, irq, rzn1_rtc_1s_irq, 0, "RZN1 RTC 1s", rtc);
|
||||
|
||||
if (irq < 0 || ret) {
|
||||
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
|
||||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
|
||||
dev_warn(dev, "RTC pps interrupt not available. Alarm has only minute accuracy\n");
|
||||
}
|
||||
}
|
||||
|
||||
return devm_rtc_register_device(rtc->rtcdev);
|
||||
}
|
||||
|
||||
static const struct rzn1_rtc_data rzn1_rtc_r8a78000_data = {
|
||||
.has_subu = false,
|
||||
.broken_irqs = true,
|
||||
};
|
||||
|
||||
static const struct rzn1_rtc_data rzn1_rtc_rzt2h_data = {
|
||||
.has_subu = false,
|
||||
};
|
||||
|
|
@ -531,6 +548,7 @@ static const struct rzn1_rtc_data rzn1_rtc_rzn1_data = {
|
|||
};
|
||||
|
||||
static const struct of_device_id rzn1_rtc_of_match[] = {
|
||||
{ .compatible = "renesas,r8a78000-rtc", .data = &rzn1_rtc_r8a78000_data },
|
||||
{ .compatible = "renesas,r9a09g077-rtc", .data = &rzn1_rtc_rzt2h_data },
|
||||
{ .compatible = "renesas,rzn1-rtc", .data = &rzn1_rtc_rzn1_data },
|
||||
{ /* sentinel */ }
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright (c) 2007 Byron Bradley
|
||||
*/
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/i2c.h>
|
||||
|
|
@ -12,11 +13,17 @@
|
|||
#include <linux/bcd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinmux.h>
|
||||
#include <linux/pinctrl/pinconf-generic.h>
|
||||
|
||||
#define DRIVER_NAME "rtc-s35390a"
|
||||
|
||||
#define S35390A_CMD_STATUS1 0
|
||||
#define S35390A_CMD_STATUS2 1
|
||||
#define S35390A_CMD_TIME1 2
|
||||
#define S35390A_CMD_TIME2 3
|
||||
#define S35390A_CMD_INT1_REG1 4
|
||||
#define S35390A_CMD_INT2_REG1 5
|
||||
#define S35390A_CMD_FREE_REG 7
|
||||
|
||||
|
|
@ -36,19 +43,38 @@
|
|||
#define S35390A_FLAG_POC BIT(0)
|
||||
#define S35390A_FLAG_BLD BIT(1)
|
||||
#define S35390A_FLAG_INT2 BIT(2)
|
||||
#define S35390A_FLAG_INT1 BIT(3)
|
||||
#define S35390A_FLAG_24H BIT(6)
|
||||
#define S35390A_FLAG_RESET BIT(7)
|
||||
|
||||
/* flag for STATUS2 */
|
||||
#define S35390A_FLAG_TEST BIT(0)
|
||||
|
||||
#define S35390A_INT_MODE_NOINTR 0x00
|
||||
|
||||
/* INT2 pin output mode */
|
||||
#define S35390A_INT2_MODE_MASK 0x0E
|
||||
#define S35390A_INT2_MODE_NOINTR 0x00
|
||||
#define S35390A_INT2_MODE_ALARM BIT(1) /* INT2AE */
|
||||
#define S35390A_INT2_MODE_PMIN_EDG BIT(2) /* INT2ME */
|
||||
#define S35390A_INT2_MODE_FREQ BIT(3) /* INT2FE */
|
||||
#define S35390A_INT2_MODE_PMIN (BIT(3) | BIT(2)) /* INT2FE | INT2ME */
|
||||
#define S35390A_INT2_MODE_PMIN1 (BIT(3) | BIT(2)) /* INT2FE | INT2ME */
|
||||
|
||||
/* INT1 pin output mode */
|
||||
#define S35390A_INT1_MODE_MASK 0xF0
|
||||
#define S35390A_INT1_MODE_ALARM BIT(5) /* INT1AE */
|
||||
#define S35390A_INT1_MODE_PMIN_EDG BIT(6) /* INT1ME */
|
||||
#define S35390A_INT1_MODE_FREQ BIT(7) /* INT1FE */
|
||||
#define S35390A_INT1_MODE_PMIN1 (BIT(7) | BIT(6)) /* INT1FE | INT1ME */
|
||||
#define S35390A_INT1_MODE_PMIN2 (BIT(7) | BIT(6) | BIT(5)) /* INT1FE | INT1ME | INT1AE */
|
||||
#define S35390A_INT1_MODE_32768KHZ BIT(4) /* 32kE */
|
||||
|
||||
#define S35390A_FUNC_IGNORE 0x00
|
||||
#define S35390A_FUNC_DISABLE 0x01
|
||||
#define S35390A_FUNC_WAKEUP 0x02
|
||||
#define S35390A_FUNC_CLOCK 0x03
|
||||
#define S35390A_FUNC_PMIN1 0x04
|
||||
#define S35390A_FUNC_PMIN2 0x05
|
||||
|
||||
|
||||
static const struct i2c_device_id s35390a_id[] = {
|
||||
{ .name = "s35390a" },
|
||||
|
|
@ -64,7 +90,10 @@ MODULE_DEVICE_TABLE(of, s35390a_of_match);
|
|||
|
||||
struct s35390a {
|
||||
struct i2c_client *client[8];
|
||||
int twentyfourhour;
|
||||
struct rtc_device *rtc;
|
||||
|
||||
struct mutex pinfunction_lock; /* lock preventing concurrent access of pin function */
|
||||
int pinfunction[2];
|
||||
};
|
||||
|
||||
static int s35390a_set_reg(struct s35390a *s35390a, int reg, u8 *buf, int len)
|
||||
|
|
@ -102,9 +131,8 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, u8 *buf, int len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_init(struct s35390a *s35390a)
|
||||
static int s35390a_init(struct s35390a *s35390a, u8 *sts)
|
||||
{
|
||||
u8 buf;
|
||||
int ret;
|
||||
unsigned initcount = 0;
|
||||
|
||||
|
|
@ -117,17 +145,17 @@ static int s35390a_init(struct s35390a *s35390a)
|
|||
* The 24H bit is kept over reset, so set it already here.
|
||||
*/
|
||||
initialize:
|
||||
buf = S35390A_FLAG_RESET | S35390A_FLAG_24H;
|
||||
ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
|
||||
*sts = S35390A_FLAG_RESET | S35390A_FLAG_24H;
|
||||
ret = s35390a_set_reg(s35390a, S35390A_CMD_STATUS1, sts, 1);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &buf, 1);
|
||||
ret = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, sts, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (buf & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) {
|
||||
if (*sts & (S35390A_FLAG_POC | S35390A_FLAG_BLD)) {
|
||||
/* Try up to five times to reset the chip */
|
||||
if (initcount < 5) {
|
||||
++initcount;
|
||||
|
|
@ -167,23 +195,9 @@ static int s35390a_read_status(struct s35390a *s35390a, char *status1)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_disable_test_mode(struct s35390a *s35390a)
|
||||
static char s35390a_hr2reg(int hour, bool twentyfourhour)
|
||||
{
|
||||
u8 buf[1];
|
||||
|
||||
if (s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf)) < 0)
|
||||
return -EIO;
|
||||
|
||||
if (!(buf[0] & S35390A_FLAG_TEST))
|
||||
return 0;
|
||||
|
||||
buf[0] &= ~S35390A_FLAG_TEST;
|
||||
return s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static char s35390a_hr2reg(struct s35390a *s35390a, int hour)
|
||||
{
|
||||
if (s35390a->twentyfourhour)
|
||||
if (twentyfourhour)
|
||||
return bin2bcd(hour);
|
||||
|
||||
if (hour < 12)
|
||||
|
|
@ -192,11 +206,11 @@ static char s35390a_hr2reg(struct s35390a *s35390a, int hour)
|
|||
return 0x40 | bin2bcd(hour - 12);
|
||||
}
|
||||
|
||||
static int s35390a_reg2hr(struct s35390a *s35390a, char reg)
|
||||
static int s35390a_reg2hr(char reg, bool twentyfourhour)
|
||||
{
|
||||
unsigned hour;
|
||||
|
||||
if (s35390a->twentyfourhour)
|
||||
if (twentyfourhour)
|
||||
return bcd2bin(reg & 0x3f);
|
||||
|
||||
hour = bcd2bin(reg & 0x3f);
|
||||
|
|
@ -210,7 +224,7 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct s35390a *s35390a = i2c_get_clientdata(client);
|
||||
int i;
|
||||
int i, err;
|
||||
u8 buf[7], status;
|
||||
|
||||
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d mday=%d, "
|
||||
|
|
@ -218,14 +232,18 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year,
|
||||
tm->tm_wday);
|
||||
|
||||
if (s35390a_read_status(s35390a, &status) == 1)
|
||||
s35390a_init(s35390a);
|
||||
err = s35390a_read_status(s35390a, &status);
|
||||
if (err == 1)
|
||||
err = s35390a_init(s35390a, &status);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
buf[S35390A_BYTE_YEAR] = bin2bcd(tm->tm_year - 100);
|
||||
buf[S35390A_BYTE_MONTH] = bin2bcd(tm->tm_mon + 1);
|
||||
buf[S35390A_BYTE_DAY] = bin2bcd(tm->tm_mday);
|
||||
buf[S35390A_BYTE_WDAY] = bin2bcd(tm->tm_wday);
|
||||
buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(s35390a, tm->tm_hour);
|
||||
buf[S35390A_BYTE_HOURS] = s35390a_hr2reg(tm->tm_hour, status & S35390A_FLAG_24H);
|
||||
buf[S35390A_BYTE_MINS] = bin2bcd(tm->tm_min);
|
||||
buf[S35390A_BYTE_SECS] = bin2bcd(tm->tm_sec);
|
||||
|
||||
|
|
@ -243,8 +261,11 @@ static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
u8 buf[7], status;
|
||||
int i, err;
|
||||
|
||||
if (s35390a_read_status(s35390a, &status) == 1)
|
||||
err = s35390a_read_status(s35390a, &status);
|
||||
if (err == 1)
|
||||
return -EINVAL;
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_TIME1, buf, sizeof(buf));
|
||||
if (err < 0)
|
||||
|
|
@ -256,7 +277,7 @@ static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
tm->tm_sec = bcd2bin(buf[S35390A_BYTE_SECS]);
|
||||
tm->tm_min = bcd2bin(buf[S35390A_BYTE_MINS]);
|
||||
tm->tm_hour = s35390a_reg2hr(s35390a, buf[S35390A_BYTE_HOURS]);
|
||||
tm->tm_hour = s35390a_reg2hr(buf[S35390A_BYTE_HOURS], status & S35390A_FLAG_24H);
|
||||
tm->tm_wday = bcd2bin(buf[S35390A_BYTE_WDAY]);
|
||||
tm->tm_mday = bcd2bin(buf[S35390A_BYTE_DAY]);
|
||||
tm->tm_mon = bcd2bin(buf[S35390A_BYTE_MONTH]) - 1;
|
||||
|
|
@ -270,11 +291,44 @@ static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct s35390a *s35390a = dev_get_drvdata(dev);
|
||||
u8 sts;
|
||||
int err;
|
||||
|
||||
guard(mutex)(&s35390a->pinfunction_lock);
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (enabled) {
|
||||
if (s35390a->pinfunction[0] == S35390A_FUNC_WAKEUP)
|
||||
sts = (sts & ~S35390A_INT1_MODE_MASK) | S35390A_INT1_MODE_ALARM;
|
||||
|
||||
if (s35390a->pinfunction[1] == S35390A_FUNC_WAKEUP)
|
||||
sts = (sts & ~S35390A_INT2_MODE_MASK) | S35390A_INT2_MODE_ALARM;
|
||||
} else {
|
||||
if (s35390a->pinfunction[0] == S35390A_FUNC_WAKEUP)
|
||||
sts = (sts & ~S35390A_INT1_MODE_MASK) | S35390A_INT_MODE_NOINTR;
|
||||
|
||||
if (s35390a->pinfunction[1] == S35390A_FUNC_WAKEUP)
|
||||
sts = (sts & ~S35390A_INT2_MODE_MASK) | S35390A_INT_MODE_NOINTR;
|
||||
}
|
||||
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct s35390a *s35390a = i2c_get_clientdata(client);
|
||||
u8 buf[3], sts = 0;
|
||||
u8 buf[3], status1, status2 = 0;
|
||||
int err, i;
|
||||
|
||||
dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\
|
||||
|
|
@ -282,23 +336,25 @@ static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
|||
alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday,
|
||||
alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday);
|
||||
|
||||
guard(mutex)(&s35390a->pinfunction_lock);
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &status2, sizeof(status2));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* disable interrupt (which deasserts the irq line) */
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
if (s35390a->pinfunction[0] == S35390A_FUNC_WAKEUP)
|
||||
status2 = (status2 & ~S35390A_INT1_MODE_MASK) | S35390A_INT_MODE_NOINTR;
|
||||
|
||||
if (s35390a->pinfunction[1] == S35390A_FUNC_WAKEUP)
|
||||
status2 = (status2 & ~S35390A_INT2_MODE_MASK) | S35390A_INT_MODE_NOINTR;
|
||||
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &status2, sizeof(status2));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* clear pending interrupt (in STATUS1 only), if any */
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (alm->enabled)
|
||||
sts = S35390A_INT2_MODE_ALARM;
|
||||
else
|
||||
sts = S35390A_INT2_MODE_NOINTR;
|
||||
|
||||
/* set interrupt mode*/
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &status1, sizeof(status1));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
@ -307,8 +363,8 @@ static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
|||
else
|
||||
buf[S35390A_ALRM_BYTE_WDAY] = 0;
|
||||
|
||||
buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
|
||||
alm->time.tm_hour) | 0x80;
|
||||
buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(alm->time.tm_hour,
|
||||
status1 & S35390A_FLAG_24H) | 0x80;
|
||||
buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80;
|
||||
|
||||
if (alm->time.tm_hour >= 12)
|
||||
|
|
@ -317,35 +373,71 @@ static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
|||
for (i = 0; i < 3; ++i)
|
||||
buf[i] = bitrev8(buf[i]);
|
||||
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf,
|
||||
sizeof(buf));
|
||||
if (alm->enabled) {
|
||||
/* set interrupt mode */
|
||||
if (s35390a->pinfunction[0] == S35390A_FUNC_WAKEUP)
|
||||
status2 = (status2 & ~S35390A_INT1_MODE_MASK) | S35390A_INT1_MODE_ALARM;
|
||||
|
||||
return err;
|
||||
if (s35390a->pinfunction[1] == S35390A_FUNC_WAKEUP)
|
||||
status2 = (status2 & ~S35390A_INT2_MODE_MASK) | S35390A_INT2_MODE_ALARM;
|
||||
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &status2, sizeof(status2));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (s35390a->pinfunction[0] == S35390A_FUNC_WAKEUP) {
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_INT1_REG1, buf, sizeof(buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (s35390a->pinfunction[1] == S35390A_FUNC_WAKEUP) {
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct s35390a *s35390a = i2c_get_clientdata(client);
|
||||
u8 buf[3], sts;
|
||||
int i, err;
|
||||
u8 buf[3], status1, status2;
|
||||
int i, err, reg;
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
guard(mutex)(&s35390a->pinfunction_lock);
|
||||
|
||||
err = s35390a_read_status(s35390a, &status1);
|
||||
if (err == 1)
|
||||
return -EINVAL;
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if ((sts & S35390A_INT2_MODE_MASK) != S35390A_INT2_MODE_ALARM) {
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &status2, sizeof(status2));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (s35390a->pinfunction[1] == S35390A_FUNC_WAKEUP &&
|
||||
(status2 & S35390A_INT2_MODE_MASK) == S35390A_INT2_MODE_ALARM) {
|
||||
reg = S35390A_CMD_INT2_REG1;
|
||||
} else if (s35390a->pinfunction[0] == S35390A_FUNC_WAKEUP &&
|
||||
(status2 & S35390A_INT1_MODE_MASK) == S35390A_INT1_MODE_ALARM) {
|
||||
reg = S35390A_CMD_INT1_REG1;
|
||||
} else {
|
||||
/*
|
||||
* When the alarm isn't enabled, the register to configure
|
||||
* the alarm time isn't accessible.
|
||||
*/
|
||||
alm->enabled = 0;
|
||||
return 0;
|
||||
} else {
|
||||
alm->enabled = 1;
|
||||
}
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
|
||||
alm->enabled = 1;
|
||||
|
||||
err = s35390a_get_reg(s35390a, reg, buf, sizeof(buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
|
|
@ -354,7 +446,7 @@ static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
|||
buf[i] = bitrev8(buf[i]);
|
||||
|
||||
/*
|
||||
* B0 of the three matching registers is an enable flag. Iff it is set
|
||||
* B0 of the three matching registers is an enable flag. If it is set
|
||||
* the configured value is used for matching.
|
||||
*/
|
||||
if (buf[S35390A_ALRM_BYTE_WDAY] & 0x80)
|
||||
|
|
@ -363,8 +455,8 @@ static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
|||
|
||||
if (buf[S35390A_ALRM_BYTE_HOURS] & 0x80)
|
||||
alm->time.tm_hour =
|
||||
s35390a_reg2hr(s35390a,
|
||||
buf[S35390A_ALRM_BYTE_HOURS] & ~0x80);
|
||||
s35390a_reg2hr(buf[S35390A_ALRM_BYTE_HOURS] & ~0x80,
|
||||
status1 & S35390A_FLAG_24H);
|
||||
|
||||
if (buf[S35390A_ALRM_BYTE_MINS] & 0x80)
|
||||
alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS] & ~0x80);
|
||||
|
|
@ -398,7 +490,7 @@ static int s35390a_rtc_ioctl(struct device *dev, unsigned int cmd,
|
|||
break;
|
||||
case RTC_VL_CLR:
|
||||
/* update flag and clear register */
|
||||
err = s35390a_init(s35390a);
|
||||
err = s35390a_init(s35390a, &sts);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
|
|
@ -410,11 +502,12 @@ static int s35390a_rtc_ioctl(struct device *dev, unsigned int cmd,
|
|||
}
|
||||
|
||||
static const struct rtc_class_ops s35390a_rtc_ops = {
|
||||
.read_time = s35390a_rtc_read_time,
|
||||
.set_time = s35390a_rtc_set_time,
|
||||
.set_alarm = s35390a_rtc_set_alarm,
|
||||
.read_alarm = s35390a_rtc_read_alarm,
|
||||
.ioctl = s35390a_rtc_ioctl,
|
||||
.read_time = s35390a_rtc_read_time,
|
||||
.set_time = s35390a_rtc_set_time,
|
||||
.set_alarm = s35390a_rtc_set_alarm,
|
||||
.read_alarm = s35390a_rtc_read_alarm,
|
||||
.alarm_irq_enable = s35390a_rtc_alarm_irq_enable,
|
||||
.ioctl = s35390a_rtc_ioctl,
|
||||
};
|
||||
|
||||
static int s35390a_nvmem_read(void *priv, unsigned int offset, void *val,
|
||||
|
|
@ -434,13 +527,168 @@ static int s35390a_nvmem_write(void *priv, unsigned int offset, void *val,
|
|||
return s35390a_set_reg(s35390a, S35390A_CMD_FREE_REG, val, bytes);
|
||||
}
|
||||
|
||||
static const struct pinctrl_pin_desc s35390a_pins_desc[] = {
|
||||
PINCTRL_PIN(0, "int1"),
|
||||
PINCTRL_PIN(1, "int2"),
|
||||
};
|
||||
|
||||
static const unsigned int int1_pins[] = { 0 };
|
||||
static const unsigned int int2_pins[] = { 1 };
|
||||
|
||||
static const struct pingroup s35390a_pin_groups[] = {
|
||||
PINCTRL_PINGROUP("int1_grp", int1_pins, ARRAY_SIZE(int1_pins)),
|
||||
PINCTRL_PINGROUP("int2_grp", int2_pins, ARRAY_SIZE(int2_pins)),
|
||||
};
|
||||
|
||||
static int s35390a_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
return ARRAY_SIZE(s35390a_pin_groups);
|
||||
}
|
||||
|
||||
static const char *s35390a_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
|
||||
unsigned int group)
|
||||
{
|
||||
return s35390a_pin_groups[group].name;
|
||||
}
|
||||
|
||||
static int s35390a_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector,
|
||||
const unsigned int **pins, unsigned int *npins)
|
||||
{
|
||||
*pins = s35390a_pin_groups[selector].pins;
|
||||
*npins = s35390a_pin_groups[selector].npins;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const all_groups[] = { "int1_grp", "int2_grp" };
|
||||
static const char * const int1_groups[] = { "int1_grp" };
|
||||
|
||||
static const struct pinfunction s35390a_functions[] = {
|
||||
[S35390A_FUNC_IGNORE] = PINCTRL_PINFUNCTION("ignore", all_groups, ARRAY_SIZE(all_groups)),
|
||||
[S35390A_FUNC_DISABLE] = PINCTRL_PINFUNCTION("disable", all_groups, ARRAY_SIZE(all_groups)),
|
||||
[S35390A_FUNC_WAKEUP] = PINCTRL_PINFUNCTION("wakeup", all_groups, ARRAY_SIZE(all_groups)),
|
||||
[S35390A_FUNC_CLOCK] = PINCTRL_PINFUNCTION("clock", all_groups, ARRAY_SIZE(all_groups)),
|
||||
[S35390A_FUNC_PMIN1] = PINCTRL_PINFUNCTION("pmin1", all_groups, ARRAY_SIZE(all_groups)),
|
||||
[S35390A_FUNC_PMIN2] = PINCTRL_PINFUNCTION("pmin2", int1_groups, ARRAY_SIZE(int1_groups)),
|
||||
};
|
||||
|
||||
static int s35390a_pinctrl_get_functions_count(struct pinctrl_dev *pctldev)
|
||||
{
|
||||
return ARRAY_SIZE(s35390a_functions);
|
||||
}
|
||||
|
||||
static const char *s35390a_pinctrl_get_function_name(struct pinctrl_dev *pctldev,
|
||||
unsigned int selector)
|
||||
{
|
||||
return s35390a_functions[selector].name;
|
||||
}
|
||||
|
||||
static int s35390a_pinctrl_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector,
|
||||
const char * const **groups,
|
||||
unsigned int * const ngroups)
|
||||
{
|
||||
*groups = s35390a_functions[selector].groups;
|
||||
*ngroups = s35390a_functions[selector].ngroups;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_pinctrl_set_mux(struct pinctrl_dev *pctldev, unsigned int function,
|
||||
unsigned int group)
|
||||
{
|
||||
int err;
|
||||
u8 status2, flag, mask;
|
||||
struct s35390a *s35390a = pinctrl_dev_get_drvdata(pctldev);
|
||||
|
||||
mask = group == 0 ? S35390A_INT1_MODE_MASK : S35390A_INT2_MODE_MASK;
|
||||
|
||||
guard(mutex)(&s35390a->pinfunction_lock);
|
||||
|
||||
dev_dbg(&s35390a->client[0]->dev, "%s: function=%d group=%d\n",
|
||||
__func__, function, group);
|
||||
|
||||
if (function == s35390a->pinfunction[group])
|
||||
return 0;
|
||||
|
||||
if (function != S35390A_FUNC_IGNORE) {
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &status2, 1);
|
||||
if (err < 0) {
|
||||
dev_err(&s35390a->client[0]->dev, "error reading status\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (function) {
|
||||
case S35390A_FUNC_DISABLE:
|
||||
case S35390A_FUNC_CLOCK: /* not implemented */
|
||||
status2 = (status2 & ~mask) | S35390A_INT_MODE_NOINTR;
|
||||
break;
|
||||
case S35390A_FUNC_WAKEUP:
|
||||
flag = group == 0 ? S35390A_INT1_MODE_ALARM : S35390A_INT2_MODE_ALARM;
|
||||
|
||||
if ((status2 & mask) != flag)
|
||||
status2 = (status2 & ~mask) | S35390A_INT_MODE_NOINTR;
|
||||
|
||||
break;
|
||||
case S35390A_FUNC_PMIN1:
|
||||
flag = group == 0 ? S35390A_INT1_MODE_PMIN1 : S35390A_INT2_MODE_PMIN1;
|
||||
status2 = (status2 & ~mask) | flag;
|
||||
break;
|
||||
|
||||
/* INT1 only modes */
|
||||
case S35390A_FUNC_PMIN2:
|
||||
if (group == 1)
|
||||
return -EINVAL;
|
||||
|
||||
status2 = (status2 & ~mask) | S35390A_INT1_MODE_PMIN2;
|
||||
break;
|
||||
}
|
||||
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &status2, 1);
|
||||
if (err < 0) {
|
||||
dev_err(&s35390a->client[0]->dev, "error setting interrupts\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
s35390a->pinfunction[group] = function;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinctrl_ops s35390a_pinctrl_ops = {
|
||||
.get_groups_count = s35390a_pinctrl_get_groups_count,
|
||||
.get_group_name = s35390a_pinctrl_get_group_name,
|
||||
.get_group_pins = s35390a_pinctrl_get_group_pins,
|
||||
#if IS_ENABLED(CONFIG_OF)
|
||||
.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
|
||||
.dt_free_map = pinconf_generic_dt_free_map
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct pinmux_ops s35390a_pinmux_ops = {
|
||||
.get_functions_count = s35390a_pinctrl_get_functions_count,
|
||||
.get_function_name = s35390a_pinctrl_get_function_name,
|
||||
.get_function_groups = s35390a_pinctrl_get_function_groups,
|
||||
.set_mux = s35390a_pinctrl_set_mux,
|
||||
.strict = true,
|
||||
};
|
||||
|
||||
static struct pinctrl_desc s35390a_pinctrl_desc = {
|
||||
.name = DRIVER_NAME,
|
||||
.pins = s35390a_pins_desc,
|
||||
.npins = ARRAY_SIZE(s35390a_pins_desc),
|
||||
.pctlops = &s35390a_pinctrl_ops,
|
||||
.pmxops = &s35390a_pinmux_ops,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int s35390a_probe(struct i2c_client *client)
|
||||
{
|
||||
int err, err_read;
|
||||
int err;
|
||||
unsigned int i;
|
||||
struct s35390a *s35390a;
|
||||
struct rtc_device *rtc;
|
||||
u8 buf, status1;
|
||||
struct pinctrl_dev *pctl;
|
||||
u8 status1, status2;
|
||||
bool irq = false;
|
||||
struct device *dev = &client->dev;
|
||||
struct nvmem_config nvmem_cfg = {
|
||||
.name = "s35390a_nvram",
|
||||
|
|
@ -451,6 +699,7 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
.reg_read = s35390a_nvmem_read,
|
||||
.reg_write = s35390a_nvmem_write,
|
||||
};
|
||||
int fallback[ARRAY_SIZE(s35390a_pin_groups)];
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
|
@ -459,7 +708,11 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
if (!s35390a)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&s35390a->pinfunction_lock);
|
||||
memset(s35390a->pinfunction, -1, sizeof(s35390a->pinfunction));
|
||||
|
||||
s35390a->client[0] = client;
|
||||
|
||||
i2c_set_clientdata(client, s35390a);
|
||||
|
||||
/* This chip uses multiple addresses, use dummy devices for them */
|
||||
|
|
@ -474,39 +727,35 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
}
|
||||
}
|
||||
|
||||
err = s35390a_read_status(s35390a, &status1);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "error resetting chip\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &status2, sizeof(status2));
|
||||
if (err < 0)
|
||||
return dev_err_probe(dev, err, "disabling alarm and test mode failed\n");
|
||||
|
||||
if (status1 & S35390A_FLAG_INT1) {
|
||||
status2 = (status2 & ~S35390A_INT1_MODE_MASK) | S35390A_INT_MODE_NOINTR;
|
||||
irq = true;
|
||||
}
|
||||
|
||||
if (status1 & S35390A_FLAG_INT2) {
|
||||
status2 = (status2 & ~S35390A_INT2_MODE_MASK) | S35390A_INT_MODE_NOINTR;
|
||||
irq = true;
|
||||
}
|
||||
|
||||
status2 &= ~S35390A_FLAG_TEST;
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &status2, sizeof(status2));
|
||||
if (err < 0)
|
||||
return dev_err_probe(dev, err, "disabling alarm and test mode failed\n");
|
||||
|
||||
rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
err_read = s35390a_read_status(s35390a, &status1);
|
||||
if (err_read < 0) {
|
||||
dev_err(dev, "error resetting chip\n");
|
||||
return err_read;
|
||||
}
|
||||
|
||||
if (status1 & S35390A_FLAG_24H)
|
||||
s35390a->twentyfourhour = 1;
|
||||
else
|
||||
s35390a->twentyfourhour = 0;
|
||||
|
||||
if (status1 & S35390A_FLAG_INT2) {
|
||||
/* disable alarm (and maybe test mode) */
|
||||
buf = 0;
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &buf, 1);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "error disabling alarm");
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
err = s35390a_disable_test_mode(s35390a);
|
||||
if (err < 0) {
|
||||
dev_err(dev, "error disabling test mode\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
device_set_wakeup_capable(dev, 1);
|
||||
|
||||
rtc->ops = &s35390a_rtc_ops;
|
||||
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
|
@ -514,9 +763,43 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->features);
|
||||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
|
||||
|
||||
if (status1 & S35390A_FLAG_INT2)
|
||||
s35390a->rtc = rtc;
|
||||
|
||||
device_set_wakeup_capable(dev, 1);
|
||||
|
||||
if (irq)
|
||||
rtc_update_irq(rtc, 1, RTC_AF);
|
||||
|
||||
err = devm_pinctrl_register_and_init(dev, &s35390a_pinctrl_desc, s35390a, &pctl);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "pinctrl register failed\n");
|
||||
|
||||
err = pinctrl_enable(pctl);
|
||||
if (err)
|
||||
return dev_err_probe(dev, err, "pinctrl enable failed\n");
|
||||
|
||||
/* If no pinmux function is defined in DT, fallback to previous behaviour */
|
||||
|
||||
if (dmi_match(DMI_SYS_VENDOR, "Synology Inc.")) {
|
||||
/*
|
||||
* Synology uses interrupt signal 1 for wakeup. Since pinctrl cannot be
|
||||
* configured on ACPI, we check against the dmi sys vendor.
|
||||
*/
|
||||
fallback[0] = S35390A_FUNC_WAKEUP;
|
||||
fallback[1] = S35390A_FUNC_DISABLE;
|
||||
} else {
|
||||
fallback[0] = S35390A_FUNC_IGNORE;
|
||||
fallback[1] = S35390A_FUNC_WAKEUP;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(s35390a_pin_groups); i++) {
|
||||
if (s35390a->pinfunction[i] == -1) {
|
||||
err = s35390a_pinctrl_set_mux(pctl, fallback[i], i);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
nvmem_cfg.priv = s35390a;
|
||||
err = devm_rtc_nvmem_register(rtc, &nvmem_cfg);
|
||||
if (err)
|
||||
|
|
@ -527,7 +810,7 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
|
||||
static struct i2c_driver s35390a_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-s35390a",
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = of_match_ptr(s35390a_of_match),
|
||||
},
|
||||
.probe = s35390a_probe,
|
||||
|
|
|
|||
|
|
@ -465,7 +465,7 @@ static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
|
|||
const struct stm32_rtc_events *evts = &rtc->data->events;
|
||||
unsigned int status, cr;
|
||||
|
||||
rtc_lock(rtc->rtc_dev);
|
||||
guard(rtc_ops)(rtc->rtc_dev);
|
||||
|
||||
status = readl_relaxed(rtc->base + regs->sr);
|
||||
cr = readl_relaxed(rtc->base + regs->cr);
|
||||
|
|
@ -482,8 +482,6 @@ static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
|
|||
stm32_rtc_clear_event_flags(rtc, evts->alra);
|
||||
}
|
||||
|
||||
rtc_unlock(rtc->rtc_dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -231,6 +231,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
const char *iosc_name = "rtc-int-osc";
|
||||
const char *clkout_name = "osc32k-out";
|
||||
const char *parents[2];
|
||||
struct resource res;
|
||||
u32 reg;
|
||||
|
||||
rtc = kzalloc_obj(*rtc);
|
||||
|
|
@ -267,9 +268,6 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
}
|
||||
writel(reg, rtc->base + SUN6I_LOSC_CTRL);
|
||||
|
||||
/* Yes, I know, this is ugly. */
|
||||
sun6i_rtc = rtc;
|
||||
|
||||
of_property_read_string_index(node, "clock-output-names", 2,
|
||||
&iosc_name);
|
||||
|
||||
|
|
@ -280,7 +278,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
300000000);
|
||||
if (IS_ERR(rtc->int_osc)) {
|
||||
pr_crit("Couldn't register the internal oscillator\n");
|
||||
goto err;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
parents[0] = clk_hw_get_name(rtc->int_osc);
|
||||
|
|
@ -309,7 +307,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
&rtc->lock);
|
||||
if (IS_ERR(rtc->ext_losc)) {
|
||||
pr_crit("Couldn't register the LOSC external gate\n");
|
||||
goto err_register;
|
||||
goto err_losc;
|
||||
}
|
||||
|
||||
clk_data->num = 3;
|
||||
|
|
@ -317,12 +315,22 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
clk_data->hws[1] = __clk_get_hw(rtc->ext_losc);
|
||||
clk_data->hws[2] = rtc->int_osc;
|
||||
of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
|
||||
|
||||
/* Yes, I know, this is ugly. */
|
||||
sun6i_rtc = rtc;
|
||||
return;
|
||||
|
||||
err_losc:
|
||||
clk_unregister(rtc->losc);
|
||||
err_register:
|
||||
clk_hw_unregister_fixed_rate(rtc->int_osc);
|
||||
err_unmap:
|
||||
iounmap(rtc->base);
|
||||
of_address_to_resource(node, 0, &res);
|
||||
release_mem_region(res.start, resource_size(&res));
|
||||
err:
|
||||
kfree(clk_data);
|
||||
kfree(rtc);
|
||||
}
|
||||
|
||||
static const struct sun6i_rtc_clk_data sun6i_a31_rtc_data = {
|
||||
|
|
@ -350,6 +358,10 @@ static void __init sun8i_a23_rtc_clk_init(struct device_node *node)
|
|||
CLK_OF_DECLARE_DRIVER(sun8i_a23_rtc_clk, "allwinner,sun8i-a23-rtc",
|
||||
sun8i_a23_rtc_clk_init);
|
||||
|
||||
/* The B288 RTC clocks are the same as the A23's */
|
||||
CLK_OF_DECLARE_DRIVER(sun8i_b288_rtc_clk, "allwinner,sun8i-b288-rtc",
|
||||
sun8i_a23_rtc_clk_init);
|
||||
|
||||
static const struct sun6i_rtc_clk_data sun8i_h3_rtc_data = {
|
||||
.rc_osc_rate = 16000000,
|
||||
.fixed_prescaler = 32,
|
||||
|
|
@ -859,6 +871,7 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
|
|||
static const struct of_device_id sun6i_rtc_dt_ids[] = {
|
||||
{ .compatible = "allwinner,sun6i-a31-rtc" },
|
||||
{ .compatible = "allwinner,sun8i-a23-rtc" },
|
||||
{ .compatible = "allwinner,sun8i-b288-rtc" },
|
||||
{ .compatible = "allwinner,sun8i-h3-rtc" },
|
||||
{ .compatible = "allwinner,sun8i-r40-rtc" },
|
||||
{ .compatible = "allwinner,sun8i-v3-rtc" },
|
||||
|
|
|
|||
|
|
@ -575,7 +575,7 @@ static ssize_t x1205_sysfs_show_atrim(struct device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000);
|
||||
return sysfs_emit(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000);
|
||||
}
|
||||
static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL);
|
||||
|
||||
|
|
@ -588,7 +588,7 @@ static ssize_t x1205_sysfs_show_dtrim(struct device *dev,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%d ppm\n", dtrim);
|
||||
return sysfs_emit(buf, "%d ppm\n", dtrim);
|
||||
}
|
||||
static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL);
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ static struct wkup_m3_wakeup_src rtc_wake_src(void)
|
|||
|
||||
static int am33xx_rtc_only_idle(unsigned long wfi_flags)
|
||||
{
|
||||
omap_rtc_power_off_program(&omap_rtc->dev);
|
||||
omap_rtc_power_off_program();
|
||||
am33xx_do_wfi_sram(wfi_flags);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,13 @@
|
|||
|
||||
#define RTC_AL_SEC 0x0018
|
||||
|
||||
/* The high byte of four of the alarms is spare, always-on storage */
|
||||
#define RTC_AL_HOU 0x001c
|
||||
#define RTC_AL_DOM 0x001e
|
||||
#define RTC_AL_DOW 0x0020
|
||||
#define RTC_AL_MTH 0x0022
|
||||
#define RTC_SPARE_MASK GENMASK(15, 8)
|
||||
|
||||
#define RTC_AL_SEC_MASK 0x003f
|
||||
#define RTC_AL_MIN_MASK 0x003f
|
||||
#define RTC_AL_HOU_MASK 0x001f
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#define _LINUX_RTC_H_
|
||||
|
||||
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
|
|
@ -167,6 +168,9 @@ struct rtc_device {
|
|||
#define rtc_lock(d) mutex_lock(&d->ops_lock)
|
||||
#define rtc_unlock(d) mutex_unlock(&d->ops_lock)
|
||||
|
||||
DEFINE_GUARD(rtc_ops, struct rtc_device *, mutex_lock(&_T->ops_lock),
|
||||
mutex_unlock(&_T->ops_lock))
|
||||
|
||||
/* useful timestamps */
|
||||
#define RTC_TIMESTAMP_BEGIN_0000 -62167219200ULL /* 0000-01-01 00:00:00 */
|
||||
#define RTC_TIMESTAMP_BEGIN_1900 -2208988800LL /* 1900-01-01 00:00:00 */
|
||||
|
|
@ -201,6 +205,7 @@ extern void rtc_update_irq(struct rtc_device *rtc,
|
|||
|
||||
extern struct rtc_device *rtc_class_open(const char *name);
|
||||
extern void rtc_class_close(struct rtc_device *rtc);
|
||||
DEFINE_FREE(rtc_class_close, struct rtc_device *, if (_T) rtc_class_close(_T));
|
||||
|
||||
extern int rtc_irq_set_state(struct rtc_device *rtc, int enabled);
|
||||
extern int rtc_irq_set_freq(struct rtc_device *rtc, int freq);
|
||||
|
|
|
|||
|
|
@ -3,5 +3,5 @@
|
|||
#ifndef _LINUX_RTCOMAP_H_
|
||||
#define _LINUX_RTCOMAP_H_
|
||||
|
||||
int omap_rtc_power_off_program(struct device *dev);
|
||||
int omap_rtc_power_off_program(void);
|
||||
#endif /* _LINUX_RTCOMAP_H_ */
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ enum rpi_firmware_property_tag {
|
|||
RPI_FIRMWARE_GET_THROTTLED = 0x00030046,
|
||||
RPI_FIRMWARE_GET_CLOCK_MEASURED = 0x00030047,
|
||||
RPI_FIRMWARE_NOTIFY_REBOOT = 0x00030048,
|
||||
RPI_FIRMWARE_GET_RTC_REG = 0x00030087,
|
||||
RPI_FIRMWARE_SET_CLOCK_STATE = 0x00038001,
|
||||
RPI_FIRMWARE_SET_CLOCK_RATE = 0x00038002,
|
||||
RPI_FIRMWARE_SET_VOLTAGE = 0x00038003,
|
||||
|
|
@ -92,6 +93,7 @@ enum rpi_firmware_property_tag {
|
|||
RPI_FIRMWARE_SET_POE_HAT_VAL = 0x00030050,
|
||||
RPI_FIRMWARE_NOTIFY_XHCI_RESET = 0x00030058,
|
||||
RPI_FIRMWARE_NOTIFY_DISPLAY_DONE = 0x00030066,
|
||||
RPI_FIRMWARE_SET_RTC_REG = 0x00038087,
|
||||
|
||||
/* Dispmanx TAGS */
|
||||
RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ TEST_F(rtc, date_read) {
|
|||
struct rtc_time rtc_tm;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
/* Read the RTC time/date */
|
||||
|
|
@ -116,9 +116,10 @@ TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
|
|||
long iter_count = 0;
|
||||
struct rtc_time rtc_tm;
|
||||
time_t start_rtc_read, prev_rtc_read;
|
||||
struct timespec prev_mono, cur_mono;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
TH_LOG("Continuously reading RTC time for %ds (with %dms breaks after every read).",
|
||||
|
|
@ -126,25 +127,31 @@ TEST_F_TIMEOUT(rtc, date_read_loop, READ_LOOP_DURATION_SEC + 2) {
|
|||
|
||||
rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
|
||||
ASSERT_NE(-1, rc);
|
||||
clock_gettime(CLOCK_MONOTONIC, &prev_mono);
|
||||
start_rtc_read = rtc_time_to_timestamp(&rtc_tm);
|
||||
prev_rtc_read = start_rtc_read;
|
||||
|
||||
do {
|
||||
time_t rtc_read;
|
||||
time_t delta_s = 0;
|
||||
|
||||
rc = ioctl(self->fd, RTC_RD_TIME, &rtc_tm);
|
||||
ASSERT_NE(-1, rc);
|
||||
clock_gettime(CLOCK_MONOTONIC, &cur_mono);
|
||||
|
||||
rtc_read = rtc_time_to_timestamp(&rtc_tm);
|
||||
delta_s = cur_mono.tv_sec - prev_mono.tv_sec;
|
||||
|
||||
/* Time should not go backwards */
|
||||
ASSERT_LE(prev_rtc_read, rtc_read);
|
||||
/* Time should not increase more then 1s at a time */
|
||||
ASSERT_GE(prev_rtc_read + 1, rtc_read);
|
||||
/* Time should not increase more then elapsed time + 1s */
|
||||
ASSERT_GE(prev_rtc_read + delta_s + 1, rtc_read);
|
||||
|
||||
/* Sleep 11ms to avoid killing / overheating the RTC */
|
||||
nanosleep_with_retries(READ_LOOP_SLEEP_MS * 1000000);
|
||||
|
||||
prev_rtc_read = rtc_read;
|
||||
prev_mono = cur_mono;
|
||||
iter_count++;
|
||||
} while (prev_rtc_read <= start_rtc_read + READ_LOOP_DURATION_SEC);
|
||||
|
||||
|
|
@ -156,15 +163,14 @@ TEST_F_TIMEOUT(rtc, uie_read, NUM_UIE + 2) {
|
|||
unsigned long data;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
/* Turn on update interrupts */
|
||||
rc = ioctl(self->fd, RTC_UIE_ON, 0);
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
TH_LOG("skip update IRQs not supported.");
|
||||
return;
|
||||
SKIP(return, "update IRQs are not supported.");
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_UIE; i++) {
|
||||
|
|
@ -185,15 +191,14 @@ TEST_F(rtc, uie_select) {
|
|||
unsigned long data;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
/* Turn on update interrupts */
|
||||
rc = ioctl(self->fd, RTC_UIE_ON, 0);
|
||||
if (rc == -1) {
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
TH_LOG("skip update IRQs not supported.");
|
||||
return;
|
||||
SKIP(return, "update IRQs are not supported.");
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_UIE; i++) {
|
||||
|
|
@ -229,14 +234,14 @@ TEST_F(rtc, alarm_alm_set) {
|
|||
enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
alarm_state = get_rtc_alarm_state(self->fd, 1);
|
||||
if (alarm_state == RTC_ALARM_DISABLED)
|
||||
SKIP(return, "Skipping test since alarms are not supported.");
|
||||
SKIP(return, "alarms are not supported.");
|
||||
if (alarm_state == RTC_ALARM_RES_MINUTE)
|
||||
SKIP(return, "Skipping test since alarms has only minute granularity.");
|
||||
SKIP(return, "alarms has only minute granularity.");
|
||||
|
||||
rc = ioctl(self->fd, RTC_RD_TIME, &tm);
|
||||
ASSERT_NE(-1, rc);
|
||||
|
|
@ -252,8 +257,7 @@ TEST_F(rtc, alarm_alm_set) {
|
|||
*/
|
||||
ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
TH_LOG("skip alarms are not supported.");
|
||||
return;
|
||||
SKIP(return, "alarms are not supported.");
|
||||
}
|
||||
|
||||
rc = ioctl(self->fd, RTC_ALM_READ, &tm);
|
||||
|
|
@ -299,14 +303,14 @@ TEST_F(rtc, alarm_wkalm_set) {
|
|||
enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
alarm_state = get_rtc_alarm_state(self->fd, 1);
|
||||
if (alarm_state == RTC_ALARM_DISABLED)
|
||||
SKIP(return, "Skipping test since alarms are not supported.");
|
||||
SKIP(return, "alarms are not supported.");
|
||||
if (alarm_state == RTC_ALARM_RES_MINUTE)
|
||||
SKIP(return, "Skipping test since alarms has only minute granularity.");
|
||||
SKIP(return, "alarms has only minute granularity.");
|
||||
|
||||
rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
|
||||
ASSERT_NE(-1, rc);
|
||||
|
|
@ -324,8 +328,7 @@ TEST_F(rtc, alarm_wkalm_set) {
|
|||
*/
|
||||
ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
TH_LOG("skip alarms are not supported.");
|
||||
return;
|
||||
SKIP(return, "alarms are not supported.");
|
||||
}
|
||||
|
||||
rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
|
||||
|
|
@ -363,12 +366,12 @@ TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
|
|||
enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
alarm_state = get_rtc_alarm_state(self->fd, 0);
|
||||
if (alarm_state == RTC_ALARM_DISABLED)
|
||||
SKIP(return, "Skipping test since alarms are not supported.");
|
||||
SKIP(return, "alarms are not supported.");
|
||||
|
||||
rc = ioctl(self->fd, RTC_RD_TIME, &tm);
|
||||
ASSERT_NE(-1, rc);
|
||||
|
|
@ -384,8 +387,7 @@ TEST_F_TIMEOUT(rtc, alarm_alm_set_minute, 65) {
|
|||
*/
|
||||
ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
TH_LOG("skip alarms are not supported.");
|
||||
return;
|
||||
SKIP(return, "alarms are not supported.");
|
||||
}
|
||||
|
||||
rc = ioctl(self->fd, RTC_ALM_READ, &tm);
|
||||
|
|
@ -431,12 +433,12 @@ TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
|
|||
enum rtc_alarm_state alarm_state = RTC_ALARM_UNKNOWN;
|
||||
|
||||
if (self->fd == -1 && errno == ENOENT)
|
||||
SKIP(return, "Skipping test since %s does not exist", rtc_file);
|
||||
SKIP(return, "%s does not exist", rtc_file);
|
||||
ASSERT_NE(-1, self->fd);
|
||||
|
||||
alarm_state = get_rtc_alarm_state(self->fd, 0);
|
||||
if (alarm_state == RTC_ALARM_DISABLED)
|
||||
SKIP(return, "Skipping test since alarms are not supported.");
|
||||
SKIP(return, "alarms are not supported.");
|
||||
|
||||
rc = ioctl(self->fd, RTC_RD_TIME, &alarm.time);
|
||||
ASSERT_NE(-1, rc);
|
||||
|
|
@ -454,8 +456,7 @@ TEST_F_TIMEOUT(rtc, alarm_wkalm_set_minute, 65) {
|
|||
*/
|
||||
ASSERT_EQ(RTC_ALARM_UNKNOWN, alarm_state);
|
||||
ASSERT_EQ(EINVAL, errno);
|
||||
TH_LOG("skip alarms are not supported.");
|
||||
return;
|
||||
SKIP(return, "alarms are not supported.");
|
||||
}
|
||||
|
||||
rc = ioctl(self->fd, RTC_WKALM_RD, &alarm);
|
||||
|
|
@ -502,8 +503,7 @@ int main(int argc, char **argv)
|
|||
if (access(rtc_file, R_OK) == 0)
|
||||
ret = test_harness_run(argc, argv);
|
||||
else
|
||||
ksft_exit_skip("[SKIP]: Cannot access rtc file %s - Exiting\n",
|
||||
rtc_file);
|
||||
ksft_exit_skip("Cannot access rtc file %s - Exiting\n", rtc_file);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user