mirror of
https://github.com/torvalds/linux.git
synced 2024-12-04 01:51:34 +00:00
RTC for 6.13
New drivers: - Amlogic A4 and A5 RTC - Marvell 88PM886 PMIC RTC - Renesas RTCA-3 for Renesas RZ/G3S Drivers: - ab-eoz9: fix temperature and alarm support - cmos: improve locking behaviour - isl12022: add alarm support - m48t59: improve epoch handling - mt6359: add range - rzn1: fix BCD conversions and simplify driver -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmdKJY4ACgkQY6TcMGxw OjJvKhAAmxWzgm5wXTtzNdO/GvmlXehTQ+vdUbhldIGTDKekT2Dr4ijwAI/JOKJt CDasXOQw2jEQg1NyxMmcBHtEol6866pUW9+tMFwYi2SxphOfhMbuw7bQNpOPCWUS rK3y/sbz0CrHPaGv9RcAxSpzCboXNWCaIGE/JPC/3nhnlSYwW2XZMLxCVTJ1ixAV S0Z4CtwCo3E7t6+i1ZY0Lm1AXze9xOuEWF+r9x5TonpetY8z94EcB8xniHfLbrpZ 1hpwewihyG7pJzvJr7+Q8Ze8P6m/LbohBk7TbzG035ILBSbUu6UHysQCocMgsz8j M4yOTTKPwprdc9DrhtykEK4sx+fF1V5tJV+Nl1sljpQco6PNWu+JP6BFrRR8OH0w OwmNytgLQux0+rtWjMMuyXYkSKJssTTrpZqpVRud/jf/IVyowQxbCQnTUK8WSm6v 1cqI4KnVcUFx+kp0BeLJz4a31O8VeMhj+YYqruFhaGdhwMYimXp8X9ZMxZkCVpMa OlmtswPvBkN1b/+w72aOXBmrWFYafICZZY7MAViEzVHM0yoIVwEzSfBToT7hvV7h w2LFSvHqLIChnUYYEQUlCh7xXhUks7fwbyRG/2n/0G1We7ZsEKIlPXCmhRl0e2R+ TGgkfUjczJ5z48m0oSy62yj1bmKVvtVdsJ/Ua5192eSxqBCTv78= =Q4/G -----END PGP SIGNATURE----- Merge tag 'rtc-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "New drivers: - Amlogic A4 and A5 RTC - Marvell 88PM886 PMIC RTC - Renesas RTCA-3 for Renesas RZ/G3S Driver updates: - ab-eoz9: fix temperature and alarm support - cmos: improve locking behaviour - isl12022: add alarm support - m48t59: improve epoch handling - mt6359: add range - rzn1: fix BCD conversions and simplify driver" * tag 'rtc-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (38 commits) rtc: ab-eoz9: don't fail temperature reads on undervoltage notification rtc: rzn1: reduce register access rtc: rzn1: drop superfluous wday calculation m68k: mvme147, mvme16x: Adopt rtc-m48t59 platform driver rtc: brcmstb-waketimer: don't include 'pm_wakeup.h' directly rtc: m48t59: Use platform_data struct for year offset value rtc: ab-eoz9: fix abeoz9_rtc_read_alarm rtc: rv3028: fix RV3028_TS_COUNT type rtc: rzn1: update Michel's email rtc: rzn1: fix BCD to rtc_time conversion errors rtc: amlogic-a4: fix compile error rtc: amlogic-a4: drop error messages MAINTAINERS: Add an entry for Amlogic RTC driver rtc: support for the Amlogic on-chip RTC dt-bindings: rtc: Add Amlogic A4 and A5 RTC rtc: add driver for Marvell 88PM886 PMIC RTC rtc: check if __rtc_read_time was successful in rtc_timer_do_work() rtc: pcf8563: Switch to regmap rtc: pcf8563: Sort headers alphabetically rtc: abx80x: Fix WDT bit position of the status register ...
This commit is contained in:
commit
0e287d31b6
@ -30,7 +30,9 @@ properties:
|
||||
- const: allwinner,sun50i-a64-rtc
|
||||
- const: allwinner,sun8i-h3-rtc
|
||||
- items:
|
||||
- const: allwinner,sun20i-d1-rtc
|
||||
- enum:
|
||||
- allwinner,sun20i-d1-rtc
|
||||
- allwinner,sun55i-a523-rtc
|
||||
- const: allwinner,sun50i-r329-rtc
|
||||
|
||||
reg:
|
||||
|
63
Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml
Normal file
63
Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml
Normal file
@ -0,0 +1,63 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2024 Amlogic, Inc. All rights reserved
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/amlogic,a4-rtc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Amlogic A4 and A5 RTC
|
||||
|
||||
maintainers:
|
||||
- Yiting Deng <yiting.deng@amlogic.com>
|
||||
- Xianwei Zhao <xianwei.zhao@amlogic.com>
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,a4-rtc
|
||||
- amlogic,a5-rtc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: RTC clock source, available 24M or 32K crystal
|
||||
oscillator source. when using 24M, need to divide 24M into 32K.
|
||||
- description: RTC module accesses the clock of the apb bus.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: osc
|
||||
- const: sys
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
- clock-names
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
apb {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
rtc@8e600 {
|
||||
compatible = "amlogic,a4-rtc";
|
||||
reg = <0x0 0x8e600 0x0 0x38>;
|
||||
clocks = <&xtal_32k>, <&clkc_periphs 1>;
|
||||
clock-names = "osc", "sys";
|
||||
interrupts = <GIC_SPI 131 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/microchip,mfps-rtc.yaml#
|
||||
$id: http://devicetree.org/schemas/rtc/microchip,mpfs-rtc.yaml#
|
||||
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
@ -12,12 +12,14 @@ allOf:
|
||||
|
||||
maintainers:
|
||||
- Daire McNamara <daire.mcnamara@microchip.com>
|
||||
- Lewis Hanly <lewis.hanly@microchip.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- microchip,mpfs-rtc
|
||||
oneOf:
|
||||
- items:
|
||||
- const: microchip,pic64gx-rtc
|
||||
- const: microchip,mpfs-rtc
|
||||
- const: microchip,mpfs-rtc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
84
Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml
Normal file
84
Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml
Normal file
@ -0,0 +1,84 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/renesas,rz-rtca3.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Renesas RTCA-3 Real Time Clock
|
||||
|
||||
maintainers:
|
||||
- Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- renesas,r9a08g045-rtca3 # RZ/G3S
|
||||
- const: renesas,rz-rtca3
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: Alarm interrupt
|
||||
- description: Periodic interrupt
|
||||
- description: Carry interrupt
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: alarm
|
||||
- const: period
|
||||
- const: carry
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: RTC bus clock
|
||||
- description: RTC counter clock
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus
|
||||
- const: counter
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
items:
|
||||
- description: VBATTB module reset
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- interrupt-names
|
||||
- clocks
|
||||
- clock-names
|
||||
- power-domains
|
||||
- resets
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/r9a08g045-cpg.h>
|
||||
#include <dt-bindings/clock/renesas,r9a08g045-vbattb.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
rtc@1004ec00 {
|
||||
compatible = "renesas,r9a08g045-rtca3", "renesas,rz-rtca3";
|
||||
reg = <0x1004ec00 0x400>;
|
||||
interrupts = <GIC_SPI 315 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 316 IRQ_TYPE_LEVEL_HIGH>,
|
||||
<GIC_SPI 317 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-names = "alarm", "period", "carry";
|
||||
clocks = <&cpg CPG_MOD R9A08G045_VBAT_BCLK>, <&vbattclk VBATTB_VBATTCLK>;
|
||||
clock-names = "bus", "counter";
|
||||
power-domains = <&cpg>;
|
||||
resets = <&cpg R9A08G045_VBAT_BRESETN>;
|
||||
};
|
17
MAINTAINERS
17
MAINTAINERS
@ -1218,6 +1218,14 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml
|
||||
F: drivers/perf/amlogic/
|
||||
F: include/soc/amlogic/
|
||||
|
||||
AMLOGIC RTC DRIVER
|
||||
M: Yiting Deng <yiting.deng@amlogic.com>
|
||||
M: Xianwei Zhao <xianwei.zhao@amlogic.com>
|
||||
L: linux-amlogic@lists.infradead.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/rtc/amlogic,a4-rtc.yaml
|
||||
F: drivers/rtc/rtc-amlogic-a4.c
|
||||
|
||||
AMPHENOL CHIPCAP 2 HUMIDITY-TEMPERATURE IIO DRIVER
|
||||
M: Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -13794,6 +13802,7 @@ F: Documentation/devicetree/bindings/mfd/marvell,88pm886-a1.yaml
|
||||
F: drivers/input/misc/88pm886-onkey.c
|
||||
F: drivers/mfd/88pm886.c
|
||||
F: drivers/regulator/88pm886-regulator.c
|
||||
F: drivers/rtc/rtc-88pm886.c
|
||||
F: include/linux/mfd/88pm886.h
|
||||
|
||||
MARVELL ARMADA 3700 PHY DRIVERS
|
||||
@ -19915,6 +19924,14 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/timer/renesas,rz-mtu3.yaml
|
||||
F: drivers/counter/rz-mtu3-cnt.c
|
||||
|
||||
RENESAS RTCA-3 RTC DRIVER
|
||||
M: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
|
||||
L: linux-rtc@vger.kernel.org
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/rtc/renesas,rz-rtca3.yaml
|
||||
F: drivers/rtc/rtc-renesas-rtca3.c
|
||||
|
||||
RENESAS RZ/N1 A5PSW SWITCH DRIVER
|
||||
M: Clément Léger <clement.leger@bootlin.com>
|
||||
L: linux-renesas-soc@vger.kernel.org
|
||||
|
@ -503,6 +503,7 @@ CONFIG_UHID=m
|
||||
# CONFIG_USB_SUPPORT is not set
|
||||
CONFIG_RTC_CLASS=y
|
||||
# CONFIG_RTC_NVMEM is not set
|
||||
CONFIG_RTC_DRV_M48T59=m
|
||||
CONFIG_RTC_DRV_MSM6242=m
|
||||
CONFIG_RTC_DRV_RP5C01=m
|
||||
CONFIG_RTC_DRV_GENERIC=m
|
||||
|
@ -391,6 +391,7 @@ CONFIG_UHID=m
|
||||
# CONFIG_USB_SUPPORT is not set
|
||||
CONFIG_RTC_CLASS=y
|
||||
# CONFIG_RTC_NVMEM is not set
|
||||
CONFIG_RTC_DRV_M48T59=y
|
||||
CONFIG_RTC_DRV_GENERIC=m
|
||||
# CONFIG_VIRTIO_MENU is not set
|
||||
# CONFIG_VHOST_MENU is not set
|
||||
|
@ -392,6 +392,7 @@ CONFIG_UHID=m
|
||||
# CONFIG_USB_SUPPORT is not set
|
||||
CONFIG_RTC_CLASS=y
|
||||
# CONFIG_RTC_NVMEM is not set
|
||||
CONFIG_RTC_DRV_M48T59=y
|
||||
CONFIG_RTC_DRV_GENERIC=m
|
||||
# CONFIG_VIRTIO_MENU is not set
|
||||
# CONFIG_VHOST_MENU is not set
|
||||
|
@ -4,24 +4,7 @@
|
||||
|
||||
#include <asm/irq.h>
|
||||
|
||||
typedef struct {
|
||||
unsigned char
|
||||
ctrl,
|
||||
bcd_sec,
|
||||
bcd_min,
|
||||
bcd_hr,
|
||||
bcd_dow,
|
||||
bcd_dom,
|
||||
bcd_mth,
|
||||
bcd_year;
|
||||
} MK48T02;
|
||||
|
||||
#define RTC_WRITE 0x80
|
||||
#define RTC_READ 0x40
|
||||
#define RTC_STOP 0x20
|
||||
|
||||
#define m147_rtc ((MK48T02 * volatile)0xfffe07f8)
|
||||
|
||||
#define MVME147_RTC_BASE 0xfffe0000
|
||||
|
||||
struct pcc_regs {
|
||||
volatile u_long dma_tadr;
|
||||
|
@ -24,23 +24,7 @@ typedef struct {
|
||||
|
||||
#define mvmelp ((*(volatile MVMElpPtr)(MVME_LPR_BASE)))
|
||||
|
||||
typedef struct {
|
||||
unsigned char
|
||||
ctrl,
|
||||
bcd_sec,
|
||||
bcd_min,
|
||||
bcd_hr,
|
||||
bcd_dow,
|
||||
bcd_dom,
|
||||
bcd_mth,
|
||||
bcd_year;
|
||||
} MK48T08_t, *MK48T08ptr_t;
|
||||
|
||||
#define RTC_WRITE 0x80
|
||||
#define RTC_READ 0x40
|
||||
#define RTC_STOP 0x20
|
||||
|
||||
#define MVME_RTC_BASE 0xfffc1ff8
|
||||
#define MVME_RTC_BASE 0xfffc0000
|
||||
|
||||
#define MVME_I596_BASE 0xfff46000
|
||||
|
||||
|
@ -19,8 +19,9 @@
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc/m48t59.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/bootinfo-vme.h>
|
||||
@ -36,13 +37,9 @@
|
||||
|
||||
static void mvme147_get_model(char *model);
|
||||
static void __init mvme147_sched_init(void);
|
||||
extern int mvme147_hwclk (int, struct rtc_time *);
|
||||
extern void mvme147_reset (void);
|
||||
|
||||
|
||||
static int bcd2int (unsigned char b);
|
||||
|
||||
|
||||
int __init mvme147_parse_bootinfo(const struct bi_record *bi)
|
||||
{
|
||||
uint16_t tag = be16_to_cpu(bi->tag);
|
||||
@ -80,7 +77,6 @@ void __init config_mvme147(void)
|
||||
{
|
||||
mach_sched_init = mvme147_sched_init;
|
||||
mach_init_IRQ = mvme147_init_IRQ;
|
||||
mach_hwclk = mvme147_hwclk;
|
||||
mach_reset = mvme147_reset;
|
||||
mach_get_model = mvme147_get_model;
|
||||
|
||||
@ -89,6 +85,28 @@ void __init config_mvme147(void)
|
||||
vme_brdtype = VME_TYPE_MVME147;
|
||||
}
|
||||
|
||||
static struct resource m48t59_rsrc[] = {
|
||||
DEFINE_RES_MEM(MVME147_RTC_BASE, 0x800),
|
||||
};
|
||||
|
||||
static struct m48t59_plat_data m48t59_data = {
|
||||
.type = M48T59RTC_TYPE_M48T02,
|
||||
.yy_offset = 70,
|
||||
};
|
||||
|
||||
static int __init mvme147_platform_init(void)
|
||||
{
|
||||
if (!MACH_IS_MVME147)
|
||||
return 0;
|
||||
|
||||
platform_device_register_resndata(NULL, "rtc-m48t59", -1,
|
||||
m48t59_rsrc, ARRAY_SIZE(m48t59_rsrc),
|
||||
&m48t59_data, sizeof(m48t59_data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_initcall(mvme147_platform_init);
|
||||
|
||||
static u64 mvme147_read_clk(struct clocksource *cs);
|
||||
|
||||
static struct clocksource mvme147_clk = {
|
||||
@ -162,31 +180,6 @@ static u64 mvme147_read_clk(struct clocksource *cs)
|
||||
return ticks;
|
||||
}
|
||||
|
||||
static int bcd2int (unsigned char b)
|
||||
{
|
||||
return ((b>>4)*10 + (b&15));
|
||||
}
|
||||
|
||||
int mvme147_hwclk(int op, struct rtc_time *t)
|
||||
{
|
||||
if (!op) {
|
||||
m147_rtc->ctrl = RTC_READ;
|
||||
t->tm_year = bcd2int (m147_rtc->bcd_year);
|
||||
t->tm_mon = bcd2int(m147_rtc->bcd_mth) - 1;
|
||||
t->tm_mday = bcd2int (m147_rtc->bcd_dom);
|
||||
t->tm_hour = bcd2int (m147_rtc->bcd_hr);
|
||||
t->tm_min = bcd2int (m147_rtc->bcd_min);
|
||||
t->tm_sec = bcd2int (m147_rtc->bcd_sec);
|
||||
m147_rtc->ctrl = 0;
|
||||
if (t->tm_year < 70)
|
||||
t->tm_year += 100;
|
||||
} else {
|
||||
/* FIXME Setting the time is not yet supported */
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scc_delay(void)
|
||||
{
|
||||
__asm__ __volatile__ ("nop; nop;");
|
||||
|
@ -3,4 +3,4 @@
|
||||
# Makefile for Linux arch/m68k/mvme16x source directory
|
||||
#
|
||||
|
||||
obj-y := config.o rtc.o
|
||||
obj-y := config.o
|
||||
|
@ -21,9 +21,10 @@
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc/m48t59.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/bootinfo-vme.h>
|
||||
@ -39,16 +40,10 @@
|
||||
|
||||
extern t_bdid mvme_bdid;
|
||||
|
||||
static MK48T08ptr_t volatile rtc = (MK48T08ptr_t)MVME_RTC_BASE;
|
||||
|
||||
static void mvme16x_get_model(char *model);
|
||||
extern void mvme16x_sched_init(void);
|
||||
extern int mvme16x_hwclk (int, struct rtc_time *);
|
||||
extern void mvme16x_reset (void);
|
||||
|
||||
int bcd2int (unsigned char b);
|
||||
|
||||
|
||||
unsigned short mvme16x_config;
|
||||
EXPORT_SYMBOL(mvme16x_config);
|
||||
|
||||
@ -268,7 +263,6 @@ void __init config_mvme16x(void)
|
||||
|
||||
mach_sched_init = mvme16x_sched_init;
|
||||
mach_init_IRQ = mvme16x_init_IRQ;
|
||||
mach_hwclk = mvme16x_hwclk;
|
||||
mach_reset = mvme16x_reset;
|
||||
mach_get_model = mvme16x_get_model;
|
||||
mach_get_hardware_list = mvme16x_get_hardware_list;
|
||||
@ -312,6 +306,28 @@ void __init config_mvme16x(void)
|
||||
}
|
||||
}
|
||||
|
||||
static struct resource m48t59_rsrc[] = {
|
||||
DEFINE_RES_MEM(MVME_RTC_BASE, 0x2000),
|
||||
};
|
||||
|
||||
static struct m48t59_plat_data m48t59_data = {
|
||||
.type = M48T59RTC_TYPE_M48T08,
|
||||
.yy_offset = 70,
|
||||
};
|
||||
|
||||
static int __init mvme16x_platform_init(void)
|
||||
{
|
||||
if (!MACH_IS_MVME16x)
|
||||
return 0;
|
||||
|
||||
platform_device_register_resndata(NULL, "rtc-m48t59", -1,
|
||||
m48t59_rsrc, ARRAY_SIZE(m48t59_rsrc),
|
||||
&m48t59_data, sizeof(m48t59_data));
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_initcall(mvme16x_platform_init);
|
||||
|
||||
static irqreturn_t mvme16x_abort_int (int irq, void *dev_id)
|
||||
{
|
||||
unsigned long *new = (unsigned long *)vectors;
|
||||
@ -426,28 +442,3 @@ static u64 mvme16x_read_clk(struct clocksource *cs)
|
||||
|
||||
return ticks;
|
||||
}
|
||||
|
||||
int bcd2int (unsigned char b)
|
||||
{
|
||||
return ((b>>4)*10 + (b&15));
|
||||
}
|
||||
|
||||
int mvme16x_hwclk(int op, struct rtc_time *t)
|
||||
{
|
||||
if (!op) {
|
||||
rtc->ctrl = RTC_READ;
|
||||
t->tm_year = bcd2int (rtc->bcd_year);
|
||||
t->tm_mon = bcd2int(rtc->bcd_mth) - 1;
|
||||
t->tm_mday = bcd2int (rtc->bcd_dom);
|
||||
t->tm_hour = bcd2int (rtc->bcd_hr);
|
||||
t->tm_min = bcd2int (rtc->bcd_min);
|
||||
t->tm_sec = bcd2int (rtc->bcd_sec);
|
||||
rtc->ctrl = 0;
|
||||
if (t->tm_year < 70)
|
||||
t->tm_year += 100;
|
||||
} else {
|
||||
/* FIXME Setting the time is not yet supported */
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,165 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Real Time Clock interface for Linux on the MVME16x
|
||||
*
|
||||
* Based on the PC driver by Paul Gortmaker.
|
||||
*/
|
||||
|
||||
#define RTC_VERSION "1.00"
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/rtc.h> /* For struct rtc_time and ioctls, etc */
|
||||
#include <linux/bcd.h>
|
||||
#include <asm/mvme16xhw.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
/*
|
||||
* We sponge a minor off of the misc major. No need slurping
|
||||
* up another valuable major dev number for this. If you add
|
||||
* an ioctl, make sure you don't conflict with SPARC's RTC
|
||||
* ioctls.
|
||||
*/
|
||||
|
||||
static const unsigned char days_in_mo[] =
|
||||
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
static atomic_t rtc_ready = ATOMIC_INIT(1);
|
||||
|
||||
static long rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
volatile MK48T08ptr_t rtc = (MK48T08ptr_t)MVME_RTC_BASE;
|
||||
unsigned long flags;
|
||||
struct rtc_time wtime;
|
||||
void __user *argp = (void __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_RD_TIME: /* Read the time/date from RTC */
|
||||
{
|
||||
local_irq_save(flags);
|
||||
/* Ensure clock and real-time-mode-register are accessible */
|
||||
rtc->ctrl = RTC_READ;
|
||||
memset(&wtime, 0, sizeof(struct rtc_time));
|
||||
wtime.tm_sec = bcd2bin(rtc->bcd_sec);
|
||||
wtime.tm_min = bcd2bin(rtc->bcd_min);
|
||||
wtime.tm_hour = bcd2bin(rtc->bcd_hr);
|
||||
wtime.tm_mday = bcd2bin(rtc->bcd_dom);
|
||||
wtime.tm_mon = bcd2bin(rtc->bcd_mth)-1;
|
||||
wtime.tm_year = bcd2bin(rtc->bcd_year);
|
||||
if (wtime.tm_year < 70)
|
||||
wtime.tm_year += 100;
|
||||
wtime.tm_wday = bcd2bin(rtc->bcd_dow)-1;
|
||||
rtc->ctrl = 0;
|
||||
local_irq_restore(flags);
|
||||
return copy_to_user(argp, &wtime, sizeof wtime) ?
|
||||
-EFAULT : 0;
|
||||
}
|
||||
case RTC_SET_TIME: /* Set the RTC */
|
||||
{
|
||||
struct rtc_time rtc_tm;
|
||||
unsigned char mon, day, hrs, min, sec, leap_yr;
|
||||
unsigned int yrs;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
if (copy_from_user(&rtc_tm, argp, sizeof(struct rtc_time)))
|
||||
return -EFAULT;
|
||||
|
||||
yrs = rtc_tm.tm_year;
|
||||
if (yrs < 1900)
|
||||
yrs += 1900;
|
||||
mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
|
||||
day = rtc_tm.tm_mday;
|
||||
hrs = rtc_tm.tm_hour;
|
||||
min = rtc_tm.tm_min;
|
||||
sec = rtc_tm.tm_sec;
|
||||
|
||||
leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
|
||||
|
||||
if ((mon > 12) || (day == 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
|
||||
return -EINVAL;
|
||||
|
||||
if ((hrs >= 24) || (min >= 60) || (sec >= 60))
|
||||
return -EINVAL;
|
||||
|
||||
if (yrs >= 2070)
|
||||
return -EINVAL;
|
||||
|
||||
local_irq_save(flags);
|
||||
rtc->ctrl = RTC_WRITE;
|
||||
|
||||
rtc->bcd_sec = bin2bcd(sec);
|
||||
rtc->bcd_min = bin2bcd(min);
|
||||
rtc->bcd_hr = bin2bcd(hrs);
|
||||
rtc->bcd_dom = bin2bcd(day);
|
||||
rtc->bcd_mth = bin2bcd(mon);
|
||||
rtc->bcd_year = bin2bcd(yrs%100);
|
||||
|
||||
rtc->ctrl = 0;
|
||||
local_irq_restore(flags);
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We enforce only one user at a time here with the open/close.
|
||||
*/
|
||||
static int rtc_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if( !atomic_dec_and_test(&rtc_ready) )
|
||||
{
|
||||
atomic_inc( &rtc_ready );
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
atomic_inc( &rtc_ready );
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The various file operations we support.
|
||||
*/
|
||||
|
||||
static const struct file_operations rtc_fops = {
|
||||
.unlocked_ioctl = rtc_ioctl,
|
||||
.open = rtc_open,
|
||||
.release = rtc_release,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice rtc_dev=
|
||||
{
|
||||
.minor = RTC_MINOR,
|
||||
.name = "rtc",
|
||||
.fops = &rtc_fops
|
||||
};
|
||||
|
||||
static int __init rtc_MK48T08_init(void)
|
||||
{
|
||||
if (!MACH_IS_MVME16x)
|
||||
return -ENODEV;
|
||||
|
||||
pr_info("MK48T08 Real Time Clock Driver v%s\n", RTC_VERSION);
|
||||
return misc_register(&rtc_dev);
|
||||
}
|
||||
device_initcall(rtc_MK48T08_init);
|
@ -255,6 +255,7 @@ static void mostek_write_byte(struct device *dev, u32 ofs, u8 val)
|
||||
static struct m48t59_plat_data m48t59_data = {
|
||||
.read_byte = mostek_read_byte,
|
||||
.write_byte = mostek_write_byte,
|
||||
.yy_offset = 68,
|
||||
};
|
||||
|
||||
/* resource is set at runtime */
|
||||
|
@ -544,6 +544,7 @@ static void mostek_write_byte(struct device *dev, u32 ofs, u8 val)
|
||||
static struct m48t59_plat_data m48t59_data = {
|
||||
.read_byte = mostek_read_byte,
|
||||
.write_byte = mostek_write_byte,
|
||||
.yy_offset = 68,
|
||||
};
|
||||
|
||||
static struct platform_device m48t59_rtc = {
|
||||
|
@ -182,6 +182,16 @@ config RTC_DRV_88PM80X
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-88pm80x.
|
||||
|
||||
config RTC_DRV_88PM886
|
||||
tristate "Marvell 88PM886 RTC driver"
|
||||
depends on MFD_88PM886_PMIC
|
||||
help
|
||||
If you say yes here you will get support for the RTC function in the
|
||||
Marvell 88PM886 chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-88pm886.
|
||||
|
||||
config RTC_DRV_ABB5ZES3
|
||||
select REGMAP_I2C
|
||||
tristate "Abracon AB-RTCMC-32.768kHz-B5ZE-S3"
|
||||
@ -496,6 +506,7 @@ config RTC_DRV_PCF85363
|
||||
|
||||
config RTC_DRV_PCF8563
|
||||
tristate "Philips PCF8563/Epson RTC8564"
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Philips PCF8563 RTC chip. The Epson RTC8564
|
||||
@ -2005,6 +2016,16 @@ config RTC_DRV_MA35D1
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-ma35d1".
|
||||
|
||||
config RTC_DRV_RENESAS_RTCA3
|
||||
tristate "Renesas RTCA-3 RTC"
|
||||
depends on ARCH_RENESAS
|
||||
help
|
||||
If you say yes here you get support for the Renesas RTCA-3 RTC
|
||||
available on the Renesas RZ/G3S SoC.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-rtca3".
|
||||
|
||||
comment "HID Sensor RTC drivers"
|
||||
|
||||
config RTC_DRV_HID_SENSOR_TIME
|
||||
@ -2070,4 +2091,16 @@ config RTC_DRV_SSD202D
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-ssd20xd".
|
||||
|
||||
config RTC_DRV_AMLOGIC_A4
|
||||
tristate "Amlogic RTC"
|
||||
depends on ARCH_MESON || COMPILE_TEST
|
||||
select REGMAP_MMIO
|
||||
default y
|
||||
help
|
||||
If you say yes here you get support for the RTC block on the
|
||||
Amlogic A113L2(A4) and A113X2(A5) SoCs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called "rtc-amlogic-a4".
|
||||
|
||||
endif # RTC_CLASS
|
||||
|
@ -21,11 +21,13 @@ obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += lib_test.o
|
||||
|
||||
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
|
||||
obj-$(CONFIG_RTC_DRV_88PM860X) += rtc-88pm860x.o
|
||||
obj-$(CONFIG_RTC_DRV_88PM886) += rtc-88pm886.o
|
||||
obj-$(CONFIG_RTC_DRV_AB8500) += rtc-ab8500.o
|
||||
obj-$(CONFIG_RTC_DRV_ABB5ZES3) += rtc-ab-b5ze-s3.o
|
||||
obj-$(CONFIG_RTC_DRV_ABEOZ9) += rtc-ab-eoz9.o
|
||||
obj-$(CONFIG_RTC_DRV_ABX80X) += rtc-abx80x.o
|
||||
obj-$(CONFIG_RTC_DRV_AC100) += rtc-ac100.o
|
||||
obj-$(CONFIG_RTC_DRV_AMLOGIC_A4) += rtc-amlogic-a4.o
|
||||
obj-$(CONFIG_RTC_DRV_ARMADA38X) += rtc-armada38x.o
|
||||
obj-$(CONFIG_RTC_DRV_AS3722) += rtc-as3722.o
|
||||
obj-$(CONFIG_RTC_DRV_ASM9260) += rtc-asm9260.o
|
||||
@ -158,13 +160,14 @@ obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o
|
||||
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
|
||||
obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o
|
||||
obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o
|
||||
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
|
||||
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
|
||||
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
|
||||
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
|
||||
obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o
|
||||
obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o
|
||||
obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o
|
||||
obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o
|
||||
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
|
||||
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
|
||||
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
|
||||
|
@ -904,13 +904,18 @@ void rtc_timer_do_work(struct work_struct *work)
|
||||
struct timerqueue_node *next;
|
||||
ktime_t now;
|
||||
struct rtc_time tm;
|
||||
int err;
|
||||
|
||||
struct rtc_device *rtc =
|
||||
container_of(work, struct rtc_device, irqwork);
|
||||
|
||||
mutex_lock(&rtc->ops_lock);
|
||||
again:
|
||||
__rtc_read_time(rtc, &tm);
|
||||
err = __rtc_read_time(rtc, &tm);
|
||||
if (err) {
|
||||
mutex_unlock(&rtc->ops_lock);
|
||||
return;
|
||||
}
|
||||
now = rtc_tm_to_ktime(tm);
|
||||
while ((next = timerqueue_getnext(&rtc->timerqueue))) {
|
||||
if (next->expires > now)
|
||||
|
@ -329,7 +329,7 @@ static struct platform_driver pm80x_rtc_driver = {
|
||||
.pm = &pm80x_rtc_pm_ops,
|
||||
},
|
||||
.probe = pm80x_rtc_probe,
|
||||
.remove_new = pm80x_rtc_remove,
|
||||
.remove = pm80x_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pm80x_rtc_driver);
|
||||
|
@ -371,7 +371,7 @@ static struct platform_driver pm860x_rtc_driver = {
|
||||
.pm = &pm860x_rtc_pm_ops,
|
||||
},
|
||||
.probe = pm860x_rtc_probe,
|
||||
.remove_new = pm860x_rtc_remove,
|
||||
.remove = pm860x_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pm860x_rtc_driver);
|
||||
|
97
drivers/rtc/rtc-88pm886.c
Normal file
97
drivers/rtc/rtc-88pm886.c
Normal file
@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <linux/limits.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#include <linux/mfd/88pm886.h>
|
||||
|
||||
/*
|
||||
* Time is calculated as the sum of a 32-bit read-only advancing counter and a
|
||||
* writeable constant offset stored in the chip's spare registers.
|
||||
*/
|
||||
|
||||
static int pm886_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
u32 time;
|
||||
u32 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, PM886_REG_RTC_SPARE1, &buf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
time = buf;
|
||||
|
||||
ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
time += buf;
|
||||
|
||||
rtc_time64_to_tm(time, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm886_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
u32 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, PM886_REG_RTC_CNT1, &buf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf = rtc_tm_to_time64(tm) - buf;
|
||||
|
||||
return regmap_bulk_write(regmap, PM886_REG_RTC_SPARE1, &buf, 4);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pm886_rtc_ops = {
|
||||
.read_time = pm886_rtc_read_time,
|
||||
.set_time = pm886_rtc_set_time,
|
||||
};
|
||||
|
||||
static int pm886_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct pm886_chip *chip = dev_get_drvdata(pdev->dev.parent);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rtc_device *rtc;
|
||||
int ret;
|
||||
|
||||
platform_set_drvdata(pdev, chip->regmap);
|
||||
|
||||
rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc))
|
||||
return dev_err_probe(dev, PTR_ERR(rtc),
|
||||
"Failed to allocate RTC device\n");
|
||||
|
||||
rtc->ops = &pm886_rtc_ops;
|
||||
rtc->range_max = U32_MAX;
|
||||
|
||||
ret = devm_rtc_register_device(rtc);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to register RTC device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id pm886_rtc_id_table[] = {
|
||||
{ "88pm886-rtc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, pm886_rtc_id_table);
|
||||
|
||||
static struct platform_driver pm886_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "88pm886-rtc",
|
||||
},
|
||||
.probe = pm886_rtc_probe,
|
||||
.id_table = pm886_rtc_id_table,
|
||||
};
|
||||
module_platform_driver(pm886_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Marvell 88PM886 RTC driver");
|
||||
MODULE_AUTHOR("Karel Balej <balejk@matfyz.cz>");
|
||||
MODULE_LICENSE("GPL");
|
@ -64,7 +64,7 @@
|
||||
#define ABEOZ9_BIT_ALARM_MIN GENMASK(6, 0)
|
||||
#define ABEOZ9_REG_ALARM_HOURS 0x12
|
||||
#define ABEOZ9_BIT_ALARM_HOURS_PM BIT(5)
|
||||
#define ABEOZ9_BIT_ALARM_HOURS GENMASK(4, 0)
|
||||
#define ABEOZ9_BIT_ALARM_HOURS GENMASK(5, 0)
|
||||
#define ABEOZ9_REG_ALARM_DAYS 0x13
|
||||
#define ABEOZ9_BIT_ALARM_DAYS GENMASK(5, 0)
|
||||
#define ABEOZ9_REG_ALARM_WEEKDAYS 0x14
|
||||
@ -231,8 +231,6 @@ static int abeoz9_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
alarm->time.tm_sec = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_SEC, regs[0]));
|
||||
alarm->time.tm_min = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_MIN, regs[1]));
|
||||
alarm->time.tm_hour = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_HOURS, regs[2]));
|
||||
if (FIELD_GET(ABEOZ9_BIT_ALARM_HOURS_PM, regs[2]))
|
||||
alarm->time.tm_hour += 12;
|
||||
|
||||
alarm->time.tm_mday = bcd2bin(FIELD_GET(ABEOZ9_BIT_ALARM_DAYS, regs[3]));
|
||||
|
||||
@ -396,13 +394,6 @@ static int abeoz9z3_temp_read(struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if ((val & ABEOZ9_REG_CTRL_STATUS_V1F) ||
|
||||
(val & ABEOZ9_REG_CTRL_STATUS_V2F)) {
|
||||
dev_err(dev,
|
||||
"thermometer might be disabled due to low voltage\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
ret = regmap_read(regmap, ABEOZ9_REG_REG_TEMP, &val);
|
||||
|
@ -403,7 +403,7 @@ static struct platform_driver ab8500_rtc_driver = {
|
||||
.name = "ab8500-rtc",
|
||||
},
|
||||
.probe = ab8500_rtc_probe,
|
||||
.remove_new = ab8500_rtc_remove,
|
||||
.remove = ab8500_rtc_remove,
|
||||
.id_table = ab85xx_rtc_ids,
|
||||
};
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
#define ABX8XX_REG_STATUS 0x0f
|
||||
#define ABX8XX_STATUS_AF BIT(2)
|
||||
#define ABX8XX_STATUS_BLF BIT(4)
|
||||
#define ABX8XX_STATUS_WDT BIT(6)
|
||||
#define ABX8XX_STATUS_WDT BIT(5)
|
||||
|
||||
#define ABX8XX_REG_CTRL1 0x10
|
||||
#define ABX8XX_CTRL_WRITE BIT(0)
|
||||
|
@ -628,7 +628,7 @@ MODULE_DEVICE_TABLE(of, ac100_rtc_match);
|
||||
|
||||
static struct platform_driver ac100_rtc_driver = {
|
||||
.probe = ac100_rtc_probe,
|
||||
.remove_new = ac100_rtc_remove,
|
||||
.remove = ac100_rtc_remove,
|
||||
.driver = {
|
||||
.name = "ac100-rtc",
|
||||
.of_match_table = of_match_ptr(ac100_rtc_match),
|
||||
|
465
drivers/rtc/rtc-amlogic-a4.c
Normal file
465
drivers/rtc/rtc-amlogic-a4.c
Normal file
@ -0,0 +1,465 @@
|
||||
// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
|
||||
/*
|
||||
* Copyright (C) 2024 Amlogic, Inc. All rights reserved
|
||||
* Author: Yiting Deng <yiting.deng@amlogic.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/time64.h>
|
||||
|
||||
/* rtc oscillator rate */
|
||||
#define OSC_32K 32768
|
||||
#define OSC_24M 24000000
|
||||
|
||||
#define RTC_CTRL (0x0 << 2) /* Control RTC */
|
||||
#define RTC_ALRM0_EN BIT(0)
|
||||
#define RTC_OSC_SEL BIT(8)
|
||||
#define RTC_ENABLE BIT(12)
|
||||
|
||||
#define RTC_COUNTER_REG (0x1 << 2) /* Program RTC counter initial value */
|
||||
|
||||
#define RTC_ALARM0_REG (0x2 << 2) /* Program RTC alarm0 value */
|
||||
|
||||
#define RTC_SEC_ADJUST_REG (0x6 << 2) /* Control second-based timing adjustment */
|
||||
#define RTC_MATCH_COUNTER GENMASK(18, 0)
|
||||
#define RTC_SEC_ADJUST_CTRL GENMASK(20, 19)
|
||||
#define RTC_ADJ_VALID BIT(23)
|
||||
|
||||
#define RTC_INT_MASK (0x8 << 2) /* RTC interrupt mask */
|
||||
#define RTC_ALRM0_IRQ_MSK BIT(0)
|
||||
|
||||
#define RTC_INT_CLR (0x9 << 2) /* Clear RTC interrupt */
|
||||
#define RTC_ALRM0_IRQ_CLR BIT(0)
|
||||
|
||||
#define RTC_OSCIN_CTRL0 (0xa << 2) /* Control RTC clk from 24M */
|
||||
#define RTC_OSCIN_CTRL1 (0xb << 2) /* Control RTC clk from 24M */
|
||||
#define RTC_OSCIN_IN_EN BIT(31)
|
||||
#define RTC_OSCIN_OUT_CFG GENMASK(29, 28)
|
||||
#define RTC_OSCIN_OUT_N0M0 GENMASK(11, 0)
|
||||
#define RTC_OSCIN_OUT_N1M1 GENMASK(23, 12)
|
||||
|
||||
#define RTC_INT_STATUS (0xc << 2) /* RTC interrupt status */
|
||||
#define RTC_ALRM0_IRQ_STATUS BIT(0)
|
||||
|
||||
#define RTC_REAL_TIME (0xd << 2) /* RTC time value */
|
||||
|
||||
#define RTC_OSCIN_OUT_32K_N0 0x2dc
|
||||
#define RTC_OSCIN_OUT_32K_N1 0x2db
|
||||
#define RTC_OSCIN_OUT_32K_M0 0x1
|
||||
#define RTC_OSCIN_OUT_32K_M1 0x2
|
||||
|
||||
#define RTC_SWALLOW_SECOND 0x2
|
||||
#define RTC_INSERT_SECOND 0x3
|
||||
|
||||
struct aml_rtc_config {
|
||||
bool gray_stored;
|
||||
};
|
||||
|
||||
struct aml_rtc_data {
|
||||
struct regmap *map;
|
||||
struct rtc_device *rtc_dev;
|
||||
int irq;
|
||||
struct clk *rtc_clk;
|
||||
struct clk *sys_clk;
|
||||
int rtc_enabled;
|
||||
const struct aml_rtc_config *config;
|
||||
};
|
||||
|
||||
static const struct regmap_config aml_rtc_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = RTC_REAL_TIME,
|
||||
};
|
||||
|
||||
static inline u32 gray_to_binary(u32 gray)
|
||||
{
|
||||
u32 bcd = gray;
|
||||
int size = sizeof(bcd) * 8;
|
||||
int i;
|
||||
|
||||
for (i = 0; (1 << i) < size; i++)
|
||||
bcd ^= bcd >> (1 << i);
|
||||
|
||||
return bcd;
|
||||
}
|
||||
|
||||
static inline u32 binary_to_gray(u32 bcd)
|
||||
{
|
||||
return bcd ^ (bcd >> 1);
|
||||
}
|
||||
|
||||
static int aml_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
u32 time_sec;
|
||||
|
||||
/* if RTC disabled, read time failed */
|
||||
if (!rtc->rtc_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_read(rtc->map, RTC_REAL_TIME, &time_sec);
|
||||
if (rtc->config->gray_stored)
|
||||
time_sec = gray_to_binary(time_sec);
|
||||
rtc_time64_to_tm(time_sec, tm);
|
||||
dev_dbg(dev, "%s: read time = %us\n", __func__, time_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
u32 time_sec;
|
||||
|
||||
/* if RTC disabled, first enable it */
|
||||
if (!rtc->rtc_enabled) {
|
||||
regmap_write_bits(rtc->map, RTC_CTRL, RTC_ENABLE, RTC_ENABLE);
|
||||
usleep_range(100, 200);
|
||||
rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE);
|
||||
if (!rtc->rtc_enabled)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
time_sec = rtc_tm_to_time64(tm);
|
||||
if (rtc->config->gray_stored)
|
||||
time_sec = binary_to_gray(time_sec);
|
||||
regmap_write(rtc->map, RTC_COUNTER_REG, time_sec);
|
||||
dev_dbg(dev, "%s: set time = %us\n", __func__, time_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
time64_t alarm_sec;
|
||||
|
||||
/* if RTC disabled, set alarm failed */
|
||||
if (!rtc->rtc_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_update_bits(rtc->map, RTC_CTRL,
|
||||
RTC_ALRM0_EN, RTC_ALRM0_EN);
|
||||
regmap_update_bits(rtc->map, RTC_INT_MASK,
|
||||
RTC_ALRM0_IRQ_MSK, 0);
|
||||
|
||||
alarm_sec = rtc_tm_to_time64(&alarm->time);
|
||||
if (rtc->config->gray_stored)
|
||||
alarm_sec = binary_to_gray(alarm_sec);
|
||||
regmap_write(rtc->map, RTC_ALARM0_REG, alarm_sec);
|
||||
|
||||
dev_dbg(dev, "%s: alarm->enabled=%d alarm_set=%llds\n", __func__,
|
||||
alarm->enabled, alarm_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
u32 alarm_sec;
|
||||
int alarm_enable;
|
||||
int alarm_mask;
|
||||
|
||||
/* if RTC disabled, read alarm failed */
|
||||
if (!rtc->rtc_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_read(rtc->map, RTC_ALARM0_REG, &alarm_sec);
|
||||
if (rtc->config->gray_stored)
|
||||
alarm_sec = gray_to_binary(alarm_sec);
|
||||
rtc_time64_to_tm(alarm_sec, &alarm->time);
|
||||
|
||||
alarm_enable = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN);
|
||||
alarm_mask = regmap_test_bits(rtc->map, RTC_INT_MASK, RTC_ALRM0_IRQ_MSK);
|
||||
alarm->enabled = (alarm_enable && !alarm_mask) ? 1 : 0;
|
||||
dev_dbg(dev, "%s: alarm->enabled=%d alarm=%us\n", __func__,
|
||||
alarm->enabled, alarm_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
u32 reg_val;
|
||||
long val;
|
||||
int sign, match_counter, enable;
|
||||
|
||||
/* if RTC disabled, read offset failed */
|
||||
if (!rtc->rtc_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
regmap_read(rtc->map, RTC_SEC_ADJUST_REG, ®_val);
|
||||
enable = FIELD_GET(RTC_ADJ_VALID, reg_val);
|
||||
if (!enable) {
|
||||
val = 0;
|
||||
} else {
|
||||
sign = FIELD_GET(RTC_SEC_ADJUST_CTRL, reg_val);
|
||||
match_counter = FIELD_GET(RTC_MATCH_COUNTER, reg_val);
|
||||
val = 1000000000 / (match_counter + 1);
|
||||
if (sign == RTC_SWALLOW_SECOND)
|
||||
val = -val;
|
||||
}
|
||||
*offset = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
int sign = 0;
|
||||
int match_counter = 0;
|
||||
int enable = 0;
|
||||
u32 reg_val;
|
||||
|
||||
/* if RTC disabled, set offset failed */
|
||||
if (!rtc->rtc_enabled)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset) {
|
||||
enable = 1;
|
||||
sign = offset < 0 ? RTC_SWALLOW_SECOND : RTC_INSERT_SECOND;
|
||||
match_counter = 1000000000 / abs(offset) - 1;
|
||||
if (match_counter < 0 || match_counter > RTC_MATCH_COUNTER)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg_val = FIELD_PREP(RTC_ADJ_VALID, enable) |
|
||||
FIELD_PREP(RTC_SEC_ADJUST_CTRL, sign) |
|
||||
FIELD_PREP(RTC_MATCH_COUNTER, match_counter);
|
||||
regmap_write(rtc->map, RTC_SEC_ADJUST_REG, reg_val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_alarm_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (enabled) {
|
||||
regmap_update_bits(rtc->map, RTC_CTRL,
|
||||
RTC_ALRM0_EN, RTC_ALRM0_EN);
|
||||
regmap_update_bits(rtc->map, RTC_INT_MASK,
|
||||
RTC_ALRM0_IRQ_MSK, 0);
|
||||
} else {
|
||||
regmap_update_bits(rtc->map, RTC_INT_MASK,
|
||||
RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK);
|
||||
regmap_update_bits(rtc->map, RTC_CTRL,
|
||||
RTC_ALRM0_EN, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops aml_rtc_ops = {
|
||||
.read_time = aml_rtc_read_time,
|
||||
.set_time = aml_rtc_set_time,
|
||||
.read_alarm = aml_rtc_read_alarm,
|
||||
.set_alarm = aml_rtc_set_alarm,
|
||||
.alarm_irq_enable = aml_rtc_alarm_enable,
|
||||
.read_offset = aml_rtc_read_offset,
|
||||
.set_offset = aml_rtc_set_offset,
|
||||
};
|
||||
|
||||
static irqreturn_t aml_rtc_handler(int irq, void *data)
|
||||
{
|
||||
struct aml_rtc_data *rtc = (struct aml_rtc_data *)data;
|
||||
|
||||
regmap_write(rtc->map, RTC_ALARM0_REG, 0);
|
||||
regmap_write(rtc->map, RTC_INT_CLR, RTC_ALRM0_IRQ_STATUS);
|
||||
|
||||
rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void aml_rtc_init(struct aml_rtc_data *rtc)
|
||||
{
|
||||
u32 reg_val = 0;
|
||||
|
||||
rtc->rtc_enabled = regmap_test_bits(rtc->map, RTC_CTRL, RTC_ENABLE);
|
||||
if (!rtc->rtc_enabled) {
|
||||
if (clk_get_rate(rtc->rtc_clk) == OSC_24M) {
|
||||
/* select 24M oscillator */
|
||||
regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, RTC_OSC_SEL);
|
||||
|
||||
/*
|
||||
* Set RTC oscillator to freq_out to freq_in/((N0*M0+N1*M1)/(M0+M1))
|
||||
* Enable clock_in gate of oscillator 24MHz
|
||||
* Set N0 to 733, N1 to 732
|
||||
*/
|
||||
reg_val = FIELD_PREP(RTC_OSCIN_IN_EN, 1)
|
||||
| FIELD_PREP(RTC_OSCIN_OUT_CFG, 1)
|
||||
| FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_N0)
|
||||
| FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_N1);
|
||||
regmap_write_bits(rtc->map, RTC_OSCIN_CTRL0, RTC_OSCIN_IN_EN
|
||||
| RTC_OSCIN_OUT_CFG | RTC_OSCIN_OUT_N0M0
|
||||
| RTC_OSCIN_OUT_N1M1, reg_val);
|
||||
|
||||
/* Set M0 to 2, M1 to 3, so freq_out = 32768 Hz*/
|
||||
reg_val = FIELD_PREP(RTC_OSCIN_OUT_N0M0, RTC_OSCIN_OUT_32K_M0)
|
||||
| FIELD_PREP(RTC_OSCIN_OUT_N1M1, RTC_OSCIN_OUT_32K_M1);
|
||||
regmap_write_bits(rtc->map, RTC_OSCIN_CTRL1, RTC_OSCIN_OUT_N0M0
|
||||
| RTC_OSCIN_OUT_N1M1, reg_val);
|
||||
} else {
|
||||
/* select 32K oscillator */
|
||||
regmap_write_bits(rtc->map, RTC_CTRL, RTC_OSC_SEL, 0);
|
||||
}
|
||||
}
|
||||
regmap_write_bits(rtc->map, RTC_INT_MASK,
|
||||
RTC_ALRM0_IRQ_MSK, RTC_ALRM0_IRQ_MSK);
|
||||
regmap_write_bits(rtc->map, RTC_CTRL, RTC_ALRM0_EN, 0);
|
||||
}
|
||||
|
||||
static int aml_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct aml_rtc_data *rtc;
|
||||
void __iomem *base;
|
||||
int ret = 0;
|
||||
|
||||
rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
rtc->config = of_device_get_match_data(dev);
|
||||
if (!rtc->config)
|
||||
return -ENODEV;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return dev_err_probe(dev, PTR_ERR(base), "resource ioremap failed\n");
|
||||
|
||||
rtc->map = devm_regmap_init_mmio(dev, base, &aml_rtc_regmap_config);
|
||||
if (IS_ERR(rtc->map))
|
||||
return dev_err_probe(dev, PTR_ERR(rtc->map), "regmap init failed\n");
|
||||
|
||||
rtc->irq = platform_get_irq(pdev, 0);
|
||||
if (rtc->irq < 0)
|
||||
return rtc->irq;
|
||||
|
||||
rtc->rtc_clk = devm_clk_get(dev, "osc");
|
||||
if (IS_ERR(rtc->rtc_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(rtc->rtc_clk),
|
||||
"failed to find rtc clock\n");
|
||||
if (clk_get_rate(rtc->rtc_clk) != OSC_32K && clk_get_rate(rtc->rtc_clk) != OSC_24M)
|
||||
return dev_err_probe(dev, -EINVAL, "Invalid clock configuration\n");
|
||||
|
||||
rtc->sys_clk = devm_clk_get_enabled(dev, "sys");
|
||||
if (IS_ERR(rtc->sys_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(rtc->sys_clk),
|
||||
"failed to get_enable rtc sys clk\n");
|
||||
aml_rtc_init(rtc);
|
||||
|
||||
device_init_wakeup(dev, 1);
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->rtc_dev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtc_dev)) {
|
||||
ret = PTR_ERR(rtc->rtc_dev);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, rtc->irq, aml_rtc_handler,
|
||||
IRQF_ONESHOT, "aml-rtc alarm", rtc);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "IRQ%d request failed, ret = %d\n",
|
||||
rtc->irq, ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
rtc->rtc_dev->ops = &aml_rtc_ops;
|
||||
rtc->rtc_dev->range_min = 0;
|
||||
rtc->rtc_dev->range_max = U32_MAX;
|
||||
|
||||
ret = devm_rtc_register_device(rtc->rtc_dev);
|
||||
if (ret) {
|
||||
dev_err_probe(&pdev->dev, ret, "Failed to register RTC device: %d\n", ret);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_clk:
|
||||
clk_disable_unprepare(rtc->sys_clk);
|
||||
device_init_wakeup(dev, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int aml_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(rtc->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aml_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(rtc->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(aml_rtc_pm_ops,
|
||||
aml_rtc_suspend, aml_rtc_resume);
|
||||
|
||||
static void aml_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aml_rtc_data *rtc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
clk_disable_unprepare(rtc->sys_clk);
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
}
|
||||
|
||||
static const struct aml_rtc_config a5_rtc_config = {
|
||||
};
|
||||
|
||||
static const struct aml_rtc_config a4_rtc_config = {
|
||||
.gray_stored = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id aml_rtc_device_id[] = {
|
||||
{
|
||||
.compatible = "amlogic,a4-rtc",
|
||||
.data = &a4_rtc_config,
|
||||
},
|
||||
{
|
||||
.compatible = "amlogic,a5-rtc",
|
||||
.data = &a5_rtc_config,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aml_rtc_device_id);
|
||||
|
||||
static struct platform_driver aml_rtc_driver = {
|
||||
.probe = aml_rtc_probe,
|
||||
.remove = aml_rtc_remove,
|
||||
.driver = {
|
||||
.name = "aml-rtc",
|
||||
.pm = &aml_rtc_pm_ops,
|
||||
.of_match_table = aml_rtc_device_id,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(aml_rtc_driver);
|
||||
MODULE_DESCRIPTION("Amlogic RTC driver");
|
||||
MODULE_AUTHOR("Yiting Deng <yiting.deng@amlogic.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -325,7 +325,7 @@ MODULE_DEVICE_TABLE(of, asm9260_dt_ids);
|
||||
|
||||
static struct platform_driver asm9260_rtc_driver = {
|
||||
.probe = asm9260_rtc_probe,
|
||||
.remove_new = asm9260_rtc_remove,
|
||||
.remove = asm9260_rtc_remove,
|
||||
.driver = {
|
||||
.name = "asm9260-rtc",
|
||||
.of_match_table = asm9260_dt_ids,
|
||||
|
@ -640,7 +640,7 @@ static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
|
||||
* triggering a section mismatch warning.
|
||||
*/
|
||||
static struct platform_driver at91_rtc_driver __refdata = {
|
||||
.remove_new = __exit_p(at91_rtc_remove),
|
||||
.remove = __exit_p(at91_rtc_remove),
|
||||
.shutdown = at91_rtc_shutdown,
|
||||
.driver = {
|
||||
.name = "at91_rtc",
|
||||
|
@ -530,7 +530,7 @@ MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
|
||||
|
||||
static struct platform_driver at91_rtc_driver = {
|
||||
.probe = at91_rtc_probe,
|
||||
.remove_new = at91_rtc_remove,
|
||||
.remove = at91_rtc_remove,
|
||||
.shutdown = at91_rtc_shutdown,
|
||||
.driver = {
|
||||
.name = "rtc-at91sam9",
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/stat.h>
|
||||
@ -417,7 +416,7 @@ static const __maybe_unused struct of_device_id brcmstb_waketmr_of_match[] = {
|
||||
|
||||
static struct platform_driver brcmstb_waketmr_driver = {
|
||||
.probe = brcmstb_waketmr_probe,
|
||||
.remove_new = brcmstb_waketmr_remove,
|
||||
.remove = brcmstb_waketmr_remove,
|
||||
.driver = {
|
||||
.name = "brcmstb-waketimer",
|
||||
.pm = &brcmstb_waketmr_pm_ops,
|
||||
|
@ -402,7 +402,7 @@ static struct platform_driver cdns_rtc_driver = {
|
||||
.pm = &cdns_rtc_pm_ops,
|
||||
},
|
||||
.probe = cdns_rtc_probe,
|
||||
.remove_new = cdns_rtc_remove,
|
||||
.remove = cdns_rtc_remove,
|
||||
};
|
||||
module_platform_driver(cdns_rtc_driver);
|
||||
|
||||
|
@ -645,18 +645,17 @@ static int cmos_nvram_read(void *priv, unsigned int off, void *val,
|
||||
unsigned char *buf = val;
|
||||
|
||||
off += NVRAM_OFFSET;
|
||||
spin_lock_irq(&rtc_lock);
|
||||
for (; count; count--, off++) {
|
||||
for (; count; count--, off++, buf++) {
|
||||
guard(spinlock_irq)(&rtc_lock);
|
||||
if (off < 128)
|
||||
*buf++ = CMOS_READ(off);
|
||||
*buf = CMOS_READ(off);
|
||||
else if (can_bank2)
|
||||
*buf++ = cmos_read_bank2(off);
|
||||
*buf = cmos_read_bank2(off);
|
||||
else
|
||||
break;
|
||||
return -EIO;
|
||||
}
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
return count ? -EIO : 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmos_nvram_write(void *priv, unsigned int off, void *val,
|
||||
@ -671,23 +670,23 @@ static int cmos_nvram_write(void *priv, unsigned int off, void *val,
|
||||
* NVRAM to update, updating checksums is also part of its job.
|
||||
*/
|
||||
off += NVRAM_OFFSET;
|
||||
spin_lock_irq(&rtc_lock);
|
||||
for (; count; count--, off++) {
|
||||
for (; count; count--, off++, buf++) {
|
||||
/* don't trash RTC registers */
|
||||
if (off == cmos->day_alrm
|
||||
|| off == cmos->mon_alrm
|
||||
|| off == cmos->century)
|
||||
buf++;
|
||||
else if (off < 128)
|
||||
CMOS_WRITE(*buf++, off);
|
||||
else if (can_bank2)
|
||||
cmos_write_bank2(*buf++, off);
|
||||
else
|
||||
break;
|
||||
}
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
continue;
|
||||
|
||||
return count ? -EIO : 0;
|
||||
guard(spinlock_irq)(&rtc_lock);
|
||||
if (off < 128)
|
||||
CMOS_WRITE(*buf, off);
|
||||
else if (can_bank2)
|
||||
cmos_write_bank2(*buf, off);
|
||||
else
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
@ -1528,7 +1527,7 @@ static void cmos_platform_shutdown(struct platform_device *pdev)
|
||||
MODULE_ALIAS("platform:rtc_cmos");
|
||||
|
||||
static struct platform_driver cmos_platform_driver = {
|
||||
.remove_new = cmos_platform_remove,
|
||||
.remove = cmos_platform_remove,
|
||||
.shutdown = cmos_platform_shutdown,
|
||||
.driver = {
|
||||
.name = driver_name,
|
||||
|
@ -401,7 +401,7 @@ MODULE_DEVICE_TABLE(platform, cros_ec_rtc_id);
|
||||
|
||||
static struct platform_driver cros_ec_rtc_driver = {
|
||||
.probe = cros_ec_rtc_probe,
|
||||
.remove_new = cros_ec_rtc_remove,
|
||||
.remove = cros_ec_rtc_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = &cros_ec_rtc_pm_ops,
|
||||
|
@ -1354,7 +1354,7 @@ static struct platform_driver ds1685_rtc_driver = {
|
||||
.name = "rtc-ds1685",
|
||||
},
|
||||
.probe = ds1685_rtc_probe,
|
||||
.remove_new = ds1685_rtc_remove,
|
||||
.remove = ds1685_rtc_remove,
|
||||
};
|
||||
module_platform_driver(ds1685_rtc_driver);
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
@ -214,7 +214,7 @@ static struct platform_driver ftrtc010_rtc_driver = {
|
||||
.of_match_table = ftrtc010_rtc_dt_match,
|
||||
},
|
||||
.probe = ftrtc010_rtc_probe,
|
||||
.remove_new = ftrtc010_rtc_remove,
|
||||
.remove = ftrtc010_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe);
|
||||
|
@ -319,7 +319,7 @@ static struct platform_driver hid_time_platform_driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = hid_time_probe,
|
||||
.remove_new = hid_time_remove,
|
||||
.remove = hid_time_remove,
|
||||
};
|
||||
module_platform_driver(hid_time_platform_driver);
|
||||
|
||||
|
@ -860,7 +860,7 @@ static struct platform_driver dryice_rtc_driver __refdata = {
|
||||
.name = "imxdi_rtc",
|
||||
.of_match_table = dryice_dt_ids,
|
||||
},
|
||||
.remove_new = __exit_p(dryice_rtc_remove),
|
||||
.remove = __exit_p(dryice_rtc_remove),
|
||||
};
|
||||
|
||||
module_platform_driver_probe(dryice_rtc_driver, dryice_rtc_probe);
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* ISL register offsets */
|
||||
/* RTC - Real time clock registers */
|
||||
#define ISL12022_REG_SC 0x00
|
||||
#define ISL12022_REG_MN 0x01
|
||||
#define ISL12022_REG_HR 0x02
|
||||
@ -30,21 +30,36 @@
|
||||
#define ISL12022_REG_YR 0x05
|
||||
#define ISL12022_REG_DW 0x06
|
||||
|
||||
/* CSR - Control and status registers */
|
||||
#define ISL12022_REG_SR 0x07
|
||||
#define ISL12022_REG_INT 0x08
|
||||
|
||||
#define ISL12022_REG_PWR_VBAT 0x0a
|
||||
|
||||
#define ISL12022_REG_BETA 0x0d
|
||||
|
||||
/* ALARM - Alarm registers */
|
||||
#define ISL12022_REG_SCA0 0x10
|
||||
#define ISL12022_REG_MNA0 0x11
|
||||
#define ISL12022_REG_HRA0 0x12
|
||||
#define ISL12022_REG_DTA0 0x13
|
||||
#define ISL12022_REG_MOA0 0x14
|
||||
#define ISL12022_REG_DWA0 0x15
|
||||
#define ISL12022_ALARM ISL12022_REG_SCA0
|
||||
#define ISL12022_ALARM_LEN (ISL12022_REG_DWA0 - ISL12022_REG_SCA0 + 1)
|
||||
|
||||
/* TEMP - Temperature sensor registers */
|
||||
#define ISL12022_REG_TEMP_L 0x28
|
||||
|
||||
/* ISL register bits */
|
||||
#define ISL12022_HR_MIL (1 << 7) /* military or 24 hour time */
|
||||
|
||||
#define ISL12022_SR_ALM (1 << 4)
|
||||
#define ISL12022_SR_LBAT85 (1 << 2)
|
||||
#define ISL12022_SR_LBAT75 (1 << 1)
|
||||
|
||||
#define ISL12022_INT_ARST (1 << 7)
|
||||
#define ISL12022_INT_WRTC (1 << 6)
|
||||
#define ISL12022_INT_IM (1 << 5)
|
||||
#define ISL12022_INT_FOBATB (1 << 4)
|
||||
#define ISL12022_INT_FO_MASK GENMASK(3, 0)
|
||||
#define ISL12022_INT_FO_OFF 0x0
|
||||
#define ISL12022_INT_FO_32K 0x1
|
||||
@ -52,8 +67,19 @@
|
||||
#define ISL12022_REG_VB85_MASK GENMASK(5, 3)
|
||||
#define ISL12022_REG_VB75_MASK GENMASK(2, 0)
|
||||
|
||||
#define ISL12022_ALARM_ENABLE (1 << 7) /* for all ALARM registers */
|
||||
|
||||
#define ISL12022_BETA_TSE (1 << 7)
|
||||
|
||||
static struct i2c_driver isl12022_driver;
|
||||
|
||||
struct isl12022 {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
int irq;
|
||||
bool irq_enabled;
|
||||
};
|
||||
|
||||
static umode_t isl12022_hwmon_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
@ -116,7 +142,8 @@ static const struct hwmon_chip_info isl12022_hwmon_chip_info = {
|
||||
|
||||
static void isl12022_hwmon_register(struct device *dev)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
struct device *hwmon;
|
||||
int ret;
|
||||
|
||||
@ -143,8 +170,9 @@ static void isl12022_hwmon_register(struct device *dev)
|
||||
*/
|
||||
static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
uint8_t buf[ISL12022_REG_INT + 1];
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u8 buf[ISL12022_REG_INT + 1];
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, ISL12022_REG_SC, buf, sizeof(buf));
|
||||
@ -178,9 +206,10 @@ static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
int ret;
|
||||
uint8_t buf[ISL12022_REG_DW + 1];
|
||||
u8 buf[ISL12022_REG_DW + 1];
|
||||
|
||||
dev_dbg(dev, "%s: %ptR\n", __func__, tm);
|
||||
|
||||
@ -208,9 +237,198 @@ static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
return regmap_bulk_write(regmap, ISL12022_REG_SC, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static int isl12022_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct rtc_time *tm = &alarm->time;
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u8 buf[ISL12022_ALARM_LEN];
|
||||
unsigned int i, yr;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(regmap, ISL12022_ALARM, buf, sizeof(buf));
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: reading ALARM registers failed\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The alarm doesn't store the year so get it from the rtc section */
|
||||
ret = regmap_read(regmap, ISL12022_REG_YR, &yr);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: reading YR register failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"%s: sc=%02x, mn=%02x, hr=%02x, dt=%02x, mo=%02x, dw=%02x yr=%u\n",
|
||||
__func__, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], yr);
|
||||
|
||||
tm->tm_sec = bcd2bin(buf[ISL12022_REG_SCA0 - ISL12022_ALARM] & 0x7F);
|
||||
tm->tm_min = bcd2bin(buf[ISL12022_REG_MNA0 - ISL12022_ALARM] & 0x7F);
|
||||
tm->tm_hour = bcd2bin(buf[ISL12022_REG_HRA0 - ISL12022_ALARM] & 0x3F);
|
||||
tm->tm_mday = bcd2bin(buf[ISL12022_REG_DTA0 - ISL12022_ALARM] & 0x3F);
|
||||
tm->tm_mon = bcd2bin(buf[ISL12022_REG_MOA0 - ISL12022_ALARM] & 0x1F) - 1;
|
||||
tm->tm_wday = buf[ISL12022_REG_DWA0 - ISL12022_ALARM] & 0x07;
|
||||
tm->tm_year = bcd2bin(yr) + 100;
|
||||
|
||||
for (i = 0; i < ISL12022_ALARM_LEN; i++) {
|
||||
if (buf[i] & ISL12022_ALARM_ENABLE) {
|
||||
alarm->enabled = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: %ptR\n", __func__, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl12022_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct rtc_time *alarm_tm = &alarm->time;
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u8 regs[ISL12022_ALARM_LEN] = { 0, };
|
||||
struct rtc_time rtc_tm;
|
||||
int ret, enable, dw;
|
||||
|
||||
ret = isl12022_rtc_read_time(dev, &rtc_tm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If the alarm time is before the current time disable the alarm */
|
||||
if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0)
|
||||
enable = 0;
|
||||
else
|
||||
enable = ISL12022_ALARM_ENABLE;
|
||||
|
||||
/*
|
||||
* Set non-matching day of the week to safeguard against early false
|
||||
* matching while setting all the alarm registers (this rtc lacks a
|
||||
* general alarm/irq enable/disable bit).
|
||||
*/
|
||||
ret = regmap_read(regmap, ISL12022_REG_DW, &dw);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: reading DW failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
/* ~4 days into the future should be enough to avoid match */
|
||||
dw = ((dw + 4) % 7) | ISL12022_ALARM_ENABLE;
|
||||
ret = regmap_write(regmap, ISL12022_REG_DWA0, dw);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: writing DWA0 failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Program the alarm and enable it for each setting */
|
||||
regs[ISL12022_REG_SCA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_sec) | enable;
|
||||
regs[ISL12022_REG_MNA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_min) | enable;
|
||||
regs[ISL12022_REG_HRA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_hour) | enable;
|
||||
regs[ISL12022_REG_DTA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mday) | enable;
|
||||
regs[ISL12022_REG_MOA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_mon + 1) | enable;
|
||||
regs[ISL12022_REG_DWA0 - ISL12022_ALARM] = bin2bcd(alarm_tm->tm_wday & 7) | enable;
|
||||
|
||||
/* write ALARM registers */
|
||||
ret = regmap_bulk_write(regmap, ISL12022_ALARM, ®s, sizeof(regs));
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: writing ALARM registers failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t isl12022_rtc_interrupt(int irq, void *data)
|
||||
{
|
||||
struct isl12022 *isl12022 = data;
|
||||
struct rtc_device *rtc = isl12022->rtc;
|
||||
struct device *dev = &rtc->dev;
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u32 val = 0;
|
||||
unsigned long events = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(regmap, ISL12022_REG_SR, &val);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "%s: reading SR failed\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (val & ISL12022_SR_ALM)
|
||||
events |= RTC_IRQF | RTC_AF;
|
||||
|
||||
if (events & RTC_AF)
|
||||
dev_dbg(dev, "alarm!\n");
|
||||
|
||||
if (!events)
|
||||
return IRQ_NONE;
|
||||
|
||||
rtc_update_irq(rtc, 1, events);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int isl12022_rtc_alarm_irq_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
|
||||
/* Make sure enabled is 0 or 1 */
|
||||
enabled = !!enabled;
|
||||
|
||||
if (isl12022->irq_enabled == enabled)
|
||||
return 0;
|
||||
|
||||
if (enabled)
|
||||
enable_irq(isl12022->irq);
|
||||
else
|
||||
disable_irq(isl12022->irq);
|
||||
|
||||
isl12022->irq_enabled = enabled;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl12022_setup_irq(struct device *dev, int irq)
|
||||
{
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
unsigned int reg_mask, reg_val;
|
||||
u8 buf[ISL12022_ALARM_LEN] = { 0, };
|
||||
int ret;
|
||||
|
||||
/* Clear and disable all alarm registers */
|
||||
ret = regmap_bulk_write(regmap, ISL12022_ALARM, buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable automatic reset of ALM bit and enable single event interrupt
|
||||
* mode.
|
||||
*/
|
||||
reg_mask = ISL12022_INT_ARST | ISL12022_INT_IM | ISL12022_INT_FO_MASK;
|
||||
reg_val = ISL12022_INT_ARST | ISL12022_INT_FO_OFF;
|
||||
ret = regmap_write_bits(regmap, ISL12022_REG_INT,
|
||||
reg_mask, reg_val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
isl12022_rtc_interrupt,
|
||||
IRQF_SHARED | IRQF_ONESHOT,
|
||||
isl12022_driver.driver.name,
|
||||
isl12022);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Unable to request irq %d\n", irq);
|
||||
|
||||
isl12022->irq = irq;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isl12022_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u32 user, val;
|
||||
int ret;
|
||||
|
||||
@ -238,6 +456,9 @@ static const struct rtc_class_ops isl12022_rtc_ops = {
|
||||
.ioctl = isl12022_rtc_ioctl,
|
||||
.read_time = isl12022_rtc_read_time,
|
||||
.set_time = isl12022_rtc_set_time,
|
||||
.read_alarm = isl12022_rtc_read_alarm,
|
||||
.set_alarm = isl12022_rtc_set_alarm,
|
||||
.alarm_irq_enable = isl12022_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
@ -248,7 +469,8 @@ static const struct regmap_config regmap_config = {
|
||||
|
||||
static int isl12022_register_clock(struct device *dev)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
struct clk_hw *hw;
|
||||
int ret;
|
||||
|
||||
@ -288,7 +510,8 @@ static const u32 trip_levels[2][7] = {
|
||||
|
||||
static void isl12022_set_trip_levels(struct device *dev)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u32 levels[2] = {0, 0};
|
||||
int ret, i, j, x[2];
|
||||
u8 val, mask;
|
||||
@ -325,6 +548,7 @@ static void isl12022_set_trip_levels(struct device *dev)
|
||||
|
||||
static int isl12022_probe(struct i2c_client *client)
|
||||
{
|
||||
struct isl12022 *isl12022;
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
int ret;
|
||||
@ -332,13 +556,17 @@ static int isl12022_probe(struct i2c_client *client)
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "regmap allocation failed\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
/* Allocate driver state */
|
||||
isl12022 = devm_kzalloc(&client->dev, sizeof(*isl12022), GFP_KERNEL);
|
||||
if (!isl12022)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&client->dev, regmap);
|
||||
regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(regmap))
|
||||
return dev_err_probe(&client->dev, PTR_ERR(regmap), "regmap allocation failed\n");
|
||||
isl12022->regmap = regmap;
|
||||
|
||||
dev_set_drvdata(&client->dev, isl12022);
|
||||
|
||||
ret = isl12022_register_clock(&client->dev);
|
||||
if (ret)
|
||||
@ -350,11 +578,20 @@ static int isl12022_probe(struct i2c_client *client)
|
||||
rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
isl12022->rtc = rtc;
|
||||
|
||||
rtc->ops = &isl12022_rtc_ops;
|
||||
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = isl12022_setup_irq(&client->dev, client->irq);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
clear_bit(RTC_FEATURE_ALARM, rtc->features);
|
||||
}
|
||||
|
||||
return devm_rtc_register_device(rtc);
|
||||
}
|
||||
|
||||
|
@ -381,7 +381,7 @@ MODULE_DEVICE_TABLE(acpi, loongson_rtc_acpi_match);
|
||||
|
||||
static struct platform_driver loongson_rtc_driver = {
|
||||
.probe = loongson_rtc_probe,
|
||||
.remove_new = loongson_rtc_remove,
|
||||
.remove = loongson_rtc_remove,
|
||||
.driver = {
|
||||
.name = "loongson-rtc",
|
||||
.of_match_table = loongson_rtc_of_match,
|
||||
|
@ -285,7 +285,7 @@ MODULE_DEVICE_TABLE(of, lpc24xx_rtc_match);
|
||||
|
||||
static struct platform_driver lpc24xx_rtc_driver = {
|
||||
.probe = lpc24xx_rtc_probe,
|
||||
.remove_new = lpc24xx_rtc_remove,
|
||||
.remove = lpc24xx_rtc_remove,
|
||||
.driver = {
|
||||
.name = "lpc24xx-rtc",
|
||||
.of_match_table = lpc24xx_rtc_match,
|
||||
|
@ -71,7 +71,7 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
/* Issue the READ command */
|
||||
M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL);
|
||||
|
||||
tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR));
|
||||
tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)) + pdata->yy_offset;
|
||||
/* tm_mon is 0-11 */
|
||||
tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
|
||||
tm->tm_mday = bcd2bin(M48T59_READ(M48T59_MDAY));
|
||||
@ -82,10 +82,6 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
dev_dbg(dev, "Century bit is enabled\n");
|
||||
tm->tm_year += 100; /* one century */
|
||||
}
|
||||
#ifdef CONFIG_SPARC
|
||||
/* Sun SPARC machines count years since 1968 */
|
||||
tm->tm_year += 68;
|
||||
#endif
|
||||
|
||||
tm->tm_wday = bcd2bin(val & 0x07);
|
||||
tm->tm_hour = bcd2bin(M48T59_READ(M48T59_HOUR) & 0x3F);
|
||||
@ -106,12 +102,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
struct m48t59_private *m48t59 = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
u8 val = 0;
|
||||
int year = tm->tm_year;
|
||||
|
||||
#ifdef CONFIG_SPARC
|
||||
/* Sun SPARC machines count years since 1968 */
|
||||
year -= 68;
|
||||
#endif
|
||||
int year = tm->tm_year - pdata->yy_offset;
|
||||
|
||||
dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n",
|
||||
year + 1900, tm->tm_mon, tm->tm_mday,
|
||||
@ -162,11 +153,7 @@ static int m48t59_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
/* Issue the READ command */
|
||||
M48T59_SET_BITS(M48T59_CNTL_READ, M48T59_CNTL);
|
||||
|
||||
tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR));
|
||||
#ifdef CONFIG_SPARC
|
||||
/* Sun SPARC machines count years since 1968 */
|
||||
tm->tm_year += 68;
|
||||
#endif
|
||||
tm->tm_year = bcd2bin(M48T59_READ(M48T59_YEAR)) + pdata->yy_offset;
|
||||
/* tm_mon is 0-11 */
|
||||
tm->tm_mon = bcd2bin(M48T59_READ(M48T59_MONTH)) - 1;
|
||||
|
||||
@ -197,12 +184,7 @@ static int m48t59_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
struct rtc_time *tm = &alrm->time;
|
||||
u8 mday, hour, min, sec;
|
||||
unsigned long flags;
|
||||
int year = tm->tm_year;
|
||||
|
||||
#ifdef CONFIG_SPARC
|
||||
/* Sun SPARC machines count years since 1968 */
|
||||
year -= 68;
|
||||
#endif
|
||||
int year = tm->tm_year - pdata->yy_offset;
|
||||
|
||||
/* If no irq, we don't support ALARM */
|
||||
if (m48t59->irq == NO_IRQ)
|
||||
|
@ -875,7 +875,7 @@ static struct platform_driver max77686_rtc_driver = {
|
||||
.pm = &max77686_rtc_pm_ops,
|
||||
},
|
||||
.probe = max77686_rtc_probe,
|
||||
.remove_new = max77686_rtc_remove,
|
||||
.remove = max77686_rtc_remove,
|
||||
.id_table = rtc_id,
|
||||
};
|
||||
|
||||
|
@ -350,7 +350,7 @@ MODULE_DEVICE_TABLE(platform, mc13xxx_rtc_idtable);
|
||||
|
||||
static struct platform_driver mc13xxx_rtc_driver = {
|
||||
.id_table = mc13xxx_rtc_idtable,
|
||||
.remove_new = mc13xxx_rtc_remove,
|
||||
.remove = mc13xxx_rtc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
|
@ -216,7 +216,7 @@ int mc146818_set_time(struct rtc_time *time)
|
||||
unsigned char save_control, save_freq_select;
|
||||
unsigned int yrs;
|
||||
#ifdef CONFIG_MACH_DECSTATION
|
||||
unsigned int real_yrs, leap_yr;
|
||||
unsigned int real_yrs;
|
||||
#endif
|
||||
unsigned char century = 0;
|
||||
|
||||
@ -232,8 +232,6 @@ int mc146818_set_time(struct rtc_time *time)
|
||||
|
||||
#ifdef CONFIG_MACH_DECSTATION
|
||||
real_yrs = yrs;
|
||||
leap_yr = ((!((yrs + 1900) % 4) && ((yrs + 1900) % 100)) ||
|
||||
!((yrs + 1900) % 400));
|
||||
yrs = 72;
|
||||
|
||||
/*
|
||||
@ -241,7 +239,7 @@ int mc146818_set_time(struct rtc_time *time)
|
||||
* for non-leap years, so that Feb, 29th is handled
|
||||
* correctly.
|
||||
*/
|
||||
if (!leap_yr && mon < 3) {
|
||||
if (!is_leap_year(real_yrs + 1900) && mon < 3) {
|
||||
real_yrs--;
|
||||
yrs = 73;
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ static struct platform_driver mpc5121_rtc_driver = {
|
||||
.of_match_table = of_match_ptr(mpc5121_rtc_match),
|
||||
},
|
||||
.probe = mpc5121_rtc_probe,
|
||||
.remove_new = mpc5121_rtc_remove,
|
||||
.remove = mpc5121_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mpc5121_rtc_driver);
|
||||
|
@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE(of, mpfs_rtc_of_match);
|
||||
|
||||
static struct platform_driver mpfs_rtc_driver = {
|
||||
.probe = mpfs_rtc_probe,
|
||||
.remove_new = mpfs_rtc_remove,
|
||||
.remove = mpfs_rtc_remove,
|
||||
.driver = {
|
||||
.name = "mpfs_rtc",
|
||||
.of_match_table = mpfs_rtc_of_match,
|
||||
|
@ -75,6 +75,7 @@ static int __mtk_rtc_read_time(struct mt6397_rtc *rtc,
|
||||
tm->tm_min = data[RTC_OFFSET_MIN];
|
||||
tm->tm_hour = data[RTC_OFFSET_HOUR];
|
||||
tm->tm_mday = data[RTC_OFFSET_DOM];
|
||||
tm->tm_wday = data[RTC_OFFSET_DOW];
|
||||
tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_TC_MTH_MASK;
|
||||
tm->tm_year = data[RTC_OFFSET_YEAR];
|
||||
|
||||
@ -86,9 +87,8 @@ exit:
|
||||
|
||||
static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
time64_t time;
|
||||
struct mt6397_rtc *rtc = dev_get_drvdata(dev);
|
||||
int days, sec, ret;
|
||||
int sec, ret;
|
||||
|
||||
do {
|
||||
ret = __mtk_rtc_read_time(rtc, tm, &sec);
|
||||
@ -96,21 +96,9 @@ static int mtk_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
goto exit;
|
||||
} while (sec < tm->tm_sec);
|
||||
|
||||
/* HW register use 7 bits to store year data, minus
|
||||
* RTC_MIN_YEAR_OFFSET before write year data to register, and plus
|
||||
* RTC_MIN_YEAR_OFFSET back after read year from register
|
||||
*/
|
||||
tm->tm_year += RTC_MIN_YEAR_OFFSET;
|
||||
|
||||
/* HW register start mon from one, but tm_mon start from zero. */
|
||||
/* HW register start mon/wday from one, but tm_mon/tm_wday start from zero. */
|
||||
tm->tm_mon--;
|
||||
time = rtc_tm_to_time64(tm);
|
||||
|
||||
/* rtc_tm_to_time64 covert Gregorian date to seconds since
|
||||
* 01-01-1970 00:00:00, and this date is Thursday.
|
||||
*/
|
||||
days = div_s64(time, 86400);
|
||||
tm->tm_wday = (days + 4) % 7;
|
||||
tm->tm_wday--;
|
||||
|
||||
exit:
|
||||
return ret;
|
||||
@ -122,13 +110,14 @@ static int mtk_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
int ret;
|
||||
u16 data[RTC_OFFSET_COUNT];
|
||||
|
||||
tm->tm_year -= RTC_MIN_YEAR_OFFSET;
|
||||
tm->tm_mon++;
|
||||
tm->tm_wday++;
|
||||
|
||||
data[RTC_OFFSET_SEC] = tm->tm_sec;
|
||||
data[RTC_OFFSET_MIN] = tm->tm_min;
|
||||
data[RTC_OFFSET_HOUR] = tm->tm_hour;
|
||||
data[RTC_OFFSET_DOM] = tm->tm_mday;
|
||||
data[RTC_OFFSET_DOW] = tm->tm_wday;
|
||||
data[RTC_OFFSET_MTH] = tm->tm_mon;
|
||||
data[RTC_OFFSET_YEAR] = tm->tm_year;
|
||||
|
||||
@ -178,7 +167,6 @@ static int mtk_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
tm->tm_mon = data[RTC_OFFSET_MTH] & RTC_AL_MTH_MASK;
|
||||
tm->tm_year = data[RTC_OFFSET_YEAR] & RTC_AL_YEA_MASK;
|
||||
|
||||
tm->tm_year += RTC_MIN_YEAR_OFFSET;
|
||||
tm->tm_mon--;
|
||||
|
||||
return 0;
|
||||
@ -194,7 +182,6 @@ static int mtk_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
int ret;
|
||||
u16 data[RTC_OFFSET_COUNT];
|
||||
|
||||
tm->tm_year -= RTC_MIN_YEAR_OFFSET;
|
||||
tm->tm_mon++;
|
||||
|
||||
mutex_lock(&rtc->lock);
|
||||
@ -302,6 +289,10 @@ static int mtk_rtc_probe(struct platform_device *pdev)
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
rtc->rtc_dev->ops = &mtk_rtc_ops;
|
||||
rtc->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_1900;
|
||||
rtc->rtc_dev->range_max = mktime64(2027, 12, 31, 23, 59, 59);
|
||||
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);
|
||||
}
|
||||
|
@ -394,7 +394,7 @@ static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume);
|
||||
|
||||
static struct platform_driver mtk_rtc_driver = {
|
||||
.probe = mtk_rtc_probe,
|
||||
.remove_new = mtk_rtc_remove,
|
||||
.remove = mtk_rtc_remove,
|
||||
.driver = {
|
||||
.name = MTK_RTC_DEV,
|
||||
.of_match_table = mtk_rtc_match,
|
||||
|
@ -308,7 +308,7 @@ MODULE_DEVICE_TABLE(of, rtc_mv_of_match_table);
|
||||
* triggering a section mismatch warning.
|
||||
*/
|
||||
static struct platform_driver mv_rtc_driver __refdata = {
|
||||
.remove_new = __exit_p(mv_rtc_remove),
|
||||
.remove = __exit_p(mv_rtc_remove),
|
||||
.driver = {
|
||||
.name = "rtc-mv",
|
||||
.of_match_table = of_match_ptr(rtc_mv_of_match_table),
|
||||
|
@ -381,7 +381,7 @@ static struct platform_driver mxc_rtc_driver = {
|
||||
.of_match_table = mxc_ids,
|
||||
},
|
||||
.probe = mxc_rtc_probe,
|
||||
.remove_new = mxc_rtc_remove,
|
||||
.remove = mxc_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mxc_rtc_driver);
|
||||
|
@ -197,13 +197,28 @@ static int bbnsm_rtc_probe(struct platform_device *pdev)
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %d: %d\n",
|
||||
bbnsm->irq, ret);
|
||||
return ret;
|
||||
goto err;
|
||||
}
|
||||
|
||||
bbnsm->rtc->ops = &bbnsm_rtc_ops;
|
||||
bbnsm->rtc->range_max = U32_MAX;
|
||||
|
||||
return devm_rtc_register_device(bbnsm->rtc);
|
||||
ret = devm_rtc_register_device(bbnsm->rtc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bbnsm_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
}
|
||||
|
||||
static const struct of_device_id bbnsm_dt_ids[] = {
|
||||
@ -218,6 +233,7 @@ static struct platform_driver bbnsm_rtc_driver = {
|
||||
.of_match_table = bbnsm_dt_ids,
|
||||
},
|
||||
.probe = bbnsm_rtc_probe,
|
||||
.remove = bbnsm_rtc_remove,
|
||||
};
|
||||
module_platform_driver(bbnsm_rtc_driver);
|
||||
|
||||
|
@ -1014,7 +1014,7 @@ static void omap_rtc_shutdown(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver omap_rtc_driver = {
|
||||
.probe = omap_rtc_probe,
|
||||
.remove_new = omap_rtc_remove,
|
||||
.remove = omap_rtc_remove,
|
||||
.shutdown = omap_rtc_shutdown,
|
||||
.driver = {
|
||||
.name = "omap_rtc",
|
||||
|
@ -346,7 +346,7 @@ MODULE_DEVICE_TABLE(of, of_palmas_rtc_match);
|
||||
|
||||
static struct platform_driver palmas_rtc_driver = {
|
||||
.probe = palmas_rtc_probe,
|
||||
.remove_new = palmas_rtc_remove,
|
||||
.remove = palmas_rtc_remove,
|
||||
.driver = {
|
||||
.name = "palmas-rtc",
|
||||
.pm = &palmas_rtc_pm_ops,
|
||||
|
@ -273,7 +273,7 @@ static struct platform_driver pcf50633_rtc_driver = {
|
||||
.name = "pcf50633-rtc",
|
||||
},
|
||||
.probe = pcf50633_rtc_probe,
|
||||
.remove_new = pcf50633_rtc_remove,
|
||||
.remove = pcf50633_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(pcf50633_rtc_driver);
|
||||
|
@ -11,14 +11,15 @@
|
||||
* https://www.nxp.com/docs/en/data-sheet/PCF8563.pdf
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PCF8563_REG_ST1 0x00 /* status */
|
||||
#define PCF8563_REG_ST2 0x01
|
||||
@ -77,64 +78,18 @@ struct pcf8563 {
|
||||
*/
|
||||
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
|
||||
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw clkout_hw;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
|
||||
unsigned char length, unsigned char *buf)
|
||||
static int pcf8563_set_alarm_mode(struct pcf8563 *pcf8563, bool on)
|
||||
{
|
||||
struct i2c_msg msgs[] = {
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = ®,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = length,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
|
||||
dev_err(&client->dev, "%s: read error\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf8563_write_block_data(struct i2c_client *client,
|
||||
unsigned char reg, unsigned char length,
|
||||
unsigned char *buf)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
unsigned char data[2] = { reg + i, buf[i] };
|
||||
|
||||
err = i2c_master_send(client, data, sizeof(data));
|
||||
if (err != sizeof(data)) {
|
||||
dev_err(&client->dev,
|
||||
"%s: err=%d addr=%02x, data=%02x\n",
|
||||
__func__, err, data[0], data[1]);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on)
|
||||
{
|
||||
unsigned char buf;
|
||||
u32 buf;
|
||||
int err;
|
||||
|
||||
err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
|
||||
err = regmap_read(pcf8563->regmap, PCF8563_REG_ST2, &buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@ -145,23 +100,17 @@ static int pcf8563_set_alarm_mode(struct i2c_client *client, bool on)
|
||||
|
||||
buf &= ~(PCF8563_BIT_AF | PCF8563_BITS_ST2_N);
|
||||
|
||||
err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "%s: write error\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return regmap_write(pcf8563->regmap, PCF8563_REG_ST2, buf);
|
||||
}
|
||||
|
||||
static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en,
|
||||
static int pcf8563_get_alarm_mode(struct pcf8563 *pcf8563, unsigned char *en,
|
||||
unsigned char *pen)
|
||||
{
|
||||
unsigned char buf;
|
||||
u32 buf;
|
||||
int err;
|
||||
|
||||
err = pcf8563_read_block_data(client, PCF8563_REG_ST2, 1, &buf);
|
||||
if (err)
|
||||
err = regmap_read(pcf8563->regmap, PCF8563_REG_ST2, &buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (en)
|
||||
@ -174,17 +123,17 @@ static int pcf8563_get_alarm_mode(struct i2c_client *client, unsigned char *en,
|
||||
|
||||
static irqreturn_t pcf8563_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct pcf8563 *pcf8563 = i2c_get_clientdata(dev_id);
|
||||
int err;
|
||||
struct pcf8563 *pcf8563 = dev_id;
|
||||
char pending;
|
||||
int err;
|
||||
|
||||
err = pcf8563_get_alarm_mode(pcf8563->client, NULL, &pending);
|
||||
err = pcf8563_get_alarm_mode(pcf8563, NULL, &pending);
|
||||
if (err)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (pending) {
|
||||
rtc_update_irq(pcf8563->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
pcf8563_set_alarm_mode(pcf8563->client, 1);
|
||||
pcf8563_set_alarm_mode(pcf8563, 1);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -197,22 +146,22 @@ static irqreturn_t pcf8563_irq(int irq, void *dev_id)
|
||||
*/
|
||||
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
|
||||
struct pcf8563 *pcf8563 = dev_get_drvdata(dev);
|
||||
unsigned char buf[9];
|
||||
int err;
|
||||
|
||||
err = pcf8563_read_block_data(client, PCF8563_REG_ST1, 9, buf);
|
||||
if (err)
|
||||
err = regmap_bulk_read(pcf8563->regmap, PCF8563_REG_ST1, buf,
|
||||
sizeof(buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) {
|
||||
dev_err(&client->dev,
|
||||
dev_err(dev,
|
||||
"low voltage detected, date/time is not reliable.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
dev_dbg(dev,
|
||||
"%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
|
||||
"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
|
||||
__func__,
|
||||
@ -220,7 +169,6 @@ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
buf[4], buf[5], buf[6], buf[7],
|
||||
buf[8]);
|
||||
|
||||
|
||||
tm->tm_sec = bcd2bin(buf[PCF8563_REG_SC] & 0x7F);
|
||||
tm->tm_min = bcd2bin(buf[PCF8563_REG_MN] & 0x7F);
|
||||
tm->tm_hour = bcd2bin(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
|
||||
@ -232,7 +180,7 @@ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
|
||||
(tm->tm_year >= 100) : (tm->tm_year < 100);
|
||||
|
||||
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
|
||||
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
|
||||
"mday=%d, mon=%d, year=%d, wday=%d\n",
|
||||
__func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
@ -243,11 +191,10 @@ static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pcf8563 *pcf8563 = i2c_get_clientdata(client);
|
||||
struct pcf8563 *pcf8563 = dev_get_drvdata(dev);
|
||||
unsigned char buf[9];
|
||||
|
||||
dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
|
||||
dev_dbg(dev, "%s: secs=%d, mins=%d, hours=%d, "
|
||||
"mday=%d, mon=%d, year=%d, wday=%d\n",
|
||||
__func__,
|
||||
tm->tm_sec, tm->tm_min, tm->tm_hour,
|
||||
@ -270,22 +217,24 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
|
||||
|
||||
return pcf8563_write_block_data(client, PCF8563_REG_SC,
|
||||
9 - PCF8563_REG_SC, buf + PCF8563_REG_SC);
|
||||
return regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC,
|
||||
buf + PCF8563_REG_SC,
|
||||
sizeof(buf) - PCF8563_REG_SC);
|
||||
}
|
||||
|
||||
static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pcf8563 *pcf8563 = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
ret = i2c_smbus_read_byte_data(client, PCF8563_REG_SC);
|
||||
ret = regmap_test_bits(pcf8563->regmap, PCF8563_REG_SC,
|
||||
PCF8563_SC_LV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return put_user(ret & PCF8563_SC_LV ? RTC_VL_DATA_INVALID : 0,
|
||||
return put_user(ret ? RTC_VL_DATA_INVALID : 0,
|
||||
(unsigned int __user *)arg);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
@ -294,15 +243,16 @@ static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long
|
||||
|
||||
static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pcf8563 *pcf8563 = dev_get_drvdata(dev);
|
||||
unsigned char buf[4];
|
||||
int err;
|
||||
|
||||
err = pcf8563_read_block_data(client, PCF8563_REG_AMN, 4, buf);
|
||||
if (err)
|
||||
err = regmap_bulk_read(pcf8563->regmap, PCF8563_REG_AMN, buf,
|
||||
sizeof(buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
dev_dbg(dev,
|
||||
"%s: raw data is min=%02x, hr=%02x, mday=%02x, wday=%02x\n",
|
||||
__func__, buf[0], buf[1], buf[2], buf[3]);
|
||||
|
||||
@ -312,11 +262,11 @@ static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
tm->time.tm_mday = bcd2bin(buf[2] & 0x3F);
|
||||
tm->time.tm_wday = bcd2bin(buf[3] & 0x7);
|
||||
|
||||
err = pcf8563_get_alarm_mode(client, &tm->enabled, &tm->pending);
|
||||
err = pcf8563_get_alarm_mode(pcf8563, &tm->enabled, &tm->pending);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dev_dbg(&client->dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d,"
|
||||
dev_dbg(dev, "%s: tm is mins=%d, hours=%d, mday=%d, wday=%d,"
|
||||
" enabled=%d, pending=%d\n", __func__, tm->time.tm_min,
|
||||
tm->time.tm_hour, tm->time.tm_mday, tm->time.tm_wday,
|
||||
tm->enabled, tm->pending);
|
||||
@ -326,7 +276,7 @@ static int pcf8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
|
||||
static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct pcf8563 *pcf8563 = dev_get_drvdata(dev);
|
||||
unsigned char buf[4];
|
||||
int err;
|
||||
|
||||
@ -335,17 +285,20 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
buf[2] = bin2bcd(tm->time.tm_mday);
|
||||
buf[3] = tm->time.tm_wday & 0x07;
|
||||
|
||||
err = pcf8563_write_block_data(client, PCF8563_REG_AMN, 4, buf);
|
||||
err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, buf,
|
||||
sizeof(buf));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return pcf8563_set_alarm_mode(client, !!tm->enabled);
|
||||
return pcf8563_set_alarm_mode(pcf8563, !!tm->enabled);
|
||||
}
|
||||
|
||||
static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct pcf8563 *pcf8563 = dev_get_drvdata(dev);
|
||||
|
||||
dev_dbg(dev, "%s: en=%d\n", __func__, enabled);
|
||||
return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
|
||||
return pcf8563_set_alarm_mode(pcf8563, !!enabled);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
@ -366,10 +319,10 @@ static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
|
||||
struct i2c_client *client = pcf8563->client;
|
||||
unsigned char buf;
|
||||
int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
|
||||
u32 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
@ -393,11 +346,10 @@ static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
|
||||
struct i2c_client *client = pcf8563->client;
|
||||
unsigned char buf;
|
||||
int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
|
||||
int i;
|
||||
int i, ret;
|
||||
u32 buf;
|
||||
|
||||
ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -405,10 +357,10 @@ static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
if (clkout_rates[i] == rate) {
|
||||
buf &= ~PCF8563_REG_CLKO_F_MASK;
|
||||
buf |= i;
|
||||
ret = pcf8563_write_block_data(client,
|
||||
PCF8563_REG_CLKO, 1,
|
||||
&buf);
|
||||
return ret;
|
||||
return regmap_update_bits(pcf8563->regmap,
|
||||
PCF8563_REG_CLKO,
|
||||
PCF8563_REG_CLKO_F_MASK,
|
||||
buf);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
@ -417,10 +369,10 @@ static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
static int pcf8563_clkout_control(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
|
||||
struct i2c_client *client = pcf8563->client;
|
||||
unsigned char buf;
|
||||
int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
|
||||
u32 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -429,8 +381,8 @@ static int pcf8563_clkout_control(struct clk_hw *hw, bool enable)
|
||||
else
|
||||
buf &= ~PCF8563_REG_CLKO_FE;
|
||||
|
||||
ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
|
||||
return ret;
|
||||
return regmap_update_bits(pcf8563->regmap, PCF8563_REG_CLKO,
|
||||
PCF8563_REG_CLKO_FE, buf);
|
||||
}
|
||||
|
||||
static int pcf8563_clkout_prepare(struct clk_hw *hw)
|
||||
@ -446,10 +398,10 @@ static void pcf8563_clkout_unprepare(struct clk_hw *hw)
|
||||
static int pcf8563_clkout_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
|
||||
struct i2c_client *client = pcf8563->client;
|
||||
unsigned char buf;
|
||||
int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
|
||||
u32 buf;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pcf8563->regmap, PCF8563_REG_CLKO, &buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -467,16 +419,14 @@ static const struct clk_ops pcf8563_clkout_ops = {
|
||||
|
||||
static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
|
||||
{
|
||||
struct i2c_client *client = pcf8563->client;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
struct clk *clk;
|
||||
struct device_node *node = pcf8563->rtc->dev.of_node;
|
||||
struct clk_init_data init;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
unsigned char buf;
|
||||
|
||||
/* disable the clkout output */
|
||||
buf = 0;
|
||||
ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
|
||||
ret = regmap_clear_bits(pcf8563->regmap, PCF8563_REG_CLKO,
|
||||
PCF8563_REG_CLKO_FE);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
@ -491,7 +441,7 @@ static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
||||
/* register the clock */
|
||||
clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw);
|
||||
clk = devm_clk_register(&pcf8563->rtc->dev, &pcf8563->clkout_hw);
|
||||
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
@ -509,11 +459,16 @@ static const struct rtc_class_ops pcf8563_rtc_ops = {
|
||||
.alarm_irq_enable = pcf8563_irq_enable,
|
||||
};
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0xF,
|
||||
};
|
||||
|
||||
static int pcf8563_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pcf8563 *pcf8563;
|
||||
int err;
|
||||
unsigned char buf;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
@ -525,20 +480,23 @@ static int pcf8563_probe(struct i2c_client *client)
|
||||
if (!pcf8563)
|
||||
return -ENOMEM;
|
||||
|
||||
pcf8563->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(pcf8563->regmap))
|
||||
return PTR_ERR(pcf8563->regmap);
|
||||
|
||||
i2c_set_clientdata(client, pcf8563);
|
||||
pcf8563->client = client;
|
||||
device_set_wakeup_capable(&client->dev, 1);
|
||||
|
||||
/* Set timer to lowest frequency to save power (ref Haoyu datasheet) */
|
||||
buf = PCF8563_TMRC_1_60;
|
||||
err = pcf8563_write_block_data(client, PCF8563_REG_TMRC, 1, &buf);
|
||||
err = regmap_set_bits(pcf8563->regmap, PCF8563_REG_TMRC,
|
||||
PCF8563_TMRC_1_60);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "%s: write error\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Clear flags and disable interrupts */
|
||||
buf = 0;
|
||||
err = pcf8563_write_block_data(client, PCF8563_REG_ST2, 1, &buf);
|
||||
err = regmap_write(pcf8563->regmap, PCF8563_REG_ST2, 0);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "%s: write error\n", __func__);
|
||||
return err;
|
||||
|
@ -371,7 +371,7 @@ MODULE_DEVICE_TABLE(of, pic32_rtc_dt_ids);
|
||||
|
||||
static struct platform_driver pic32_rtc_driver = {
|
||||
.probe = pic32_rtc_probe,
|
||||
.remove_new = pic32_rtc_remove,
|
||||
.remove = pic32_rtc_remove,
|
||||
.driver = {
|
||||
.name = "pic32-rtc",
|
||||
.of_match_table = of_match_ptr(pic32_rtc_dt_ids),
|
||||
|
@ -537,7 +537,7 @@ static void pm8xxx_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver pm8xxx_rtc_driver = {
|
||||
.probe = pm8xxx_rtc_probe,
|
||||
.remove_new = pm8xxx_remove,
|
||||
.remove = pm8xxx_remove,
|
||||
.driver = {
|
||||
.name = "rtc-pm8xxx",
|
||||
.of_match_table = pm8xxx_id_table,
|
||||
|
@ -409,7 +409,7 @@ static SIMPLE_DEV_PM_OPS(pxa_rtc_pm_ops, pxa_rtc_suspend, pxa_rtc_resume);
|
||||
* triggering a section mismatch warning.
|
||||
*/
|
||||
static struct platform_driver pxa_rtc_driver __refdata = {
|
||||
.remove_new = __exit_p(pxa_rtc_remove),
|
||||
.remove = __exit_p(pxa_rtc_remove),
|
||||
.driver = {
|
||||
.name = "pxa-rtc",
|
||||
.of_match_table = of_match_ptr(pxa_rtc_dt_ids),
|
||||
|
@ -298,7 +298,7 @@ static SIMPLE_DEV_PM_OPS(rc5t583_rtc_pm_ops, rc5t583_rtc_suspend,
|
||||
|
||||
static struct platform_driver rc5t583_rtc_driver = {
|
||||
.probe = rc5t583_rtc_probe,
|
||||
.remove_new = rc5t583_rtc_remove,
|
||||
.remove = rc5t583_rtc_remove,
|
||||
.driver = {
|
||||
.name = "rtc-rc5t583",
|
||||
.pm = &rc5t583_rtc_pm_ops,
|
||||
|
900
drivers/rtc/rtc-renesas-rtca3.c
Normal file
900
drivers/rtc/rtc-renesas-rtca3.c
Normal file
@ -0,0 +1,900 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* On-Chip RTC Support available on RZ/G3S SoC
|
||||
*
|
||||
* Copyright (C) 2024 Renesas Electronics Corp.
|
||||
*/
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
/* Counter registers. */
|
||||
#define RTCA3_RSECCNT 0x2
|
||||
#define RTCA3_RSECCNT_SEC GENMASK(6, 0)
|
||||
#define RTCA3_RMINCNT 0x4
|
||||
#define RTCA3_RMINCNT_MIN GENMASK(6, 0)
|
||||
#define RTCA3_RHRCNT 0x6
|
||||
#define RTCA3_RHRCNT_HR GENMASK(5, 0)
|
||||
#define RTCA3_RHRCNT_PM BIT(6)
|
||||
#define RTCA3_RWKCNT 0x8
|
||||
#define RTCA3_RWKCNT_WK GENMASK(2, 0)
|
||||
#define RTCA3_RDAYCNT 0xa
|
||||
#define RTCA3_RDAYCNT_DAY GENMASK(5, 0)
|
||||
#define RTCA3_RMONCNT 0xc
|
||||
#define RTCA3_RMONCNT_MONTH GENMASK(4, 0)
|
||||
#define RTCA3_RYRCNT 0xe
|
||||
#define RTCA3_RYRCNT_YEAR GENMASK(7, 0)
|
||||
|
||||
/* Alarm registers. */
|
||||
#define RTCA3_RSECAR 0x10
|
||||
#define RTCA3_RSECAR_SEC GENMASK(6, 0)
|
||||
#define RTCA3_RMINAR 0x12
|
||||
#define RTCA3_RMINAR_MIN GENMASK(6, 0)
|
||||
#define RTCA3_RHRAR 0x14
|
||||
#define RTCA3_RHRAR_HR GENMASK(5, 0)
|
||||
#define RTCA3_RHRAR_PM BIT(6)
|
||||
#define RTCA3_RWKAR 0x16
|
||||
#define RTCA3_RWKAR_DAYW GENMASK(2, 0)
|
||||
#define RTCA3_RDAYAR 0x18
|
||||
#define RTCA3_RDAYAR_DATE GENMASK(5, 0)
|
||||
#define RTCA3_RMONAR 0x1a
|
||||
#define RTCA3_RMONAR_MON GENMASK(4, 0)
|
||||
#define RTCA3_RYRAR 0x1c
|
||||
#define RTCA3_RYRAR_YR GENMASK(7, 0)
|
||||
#define RTCA3_RYRAREN 0x1e
|
||||
|
||||
/* Alarm enable bit (for all alarm registers). */
|
||||
#define RTCA3_AR_ENB BIT(7)
|
||||
|
||||
/* Control registers. */
|
||||
#define RTCA3_RCR1 0x22
|
||||
#define RTCA3_RCR1_AIE BIT(0)
|
||||
#define RTCA3_RCR1_CIE BIT(1)
|
||||
#define RTCA3_RCR1_PIE BIT(2)
|
||||
#define RTCA3_RCR1_PES GENMASK(7, 4)
|
||||
#define RTCA3_RCR1_PES_1_64_SEC 0x8
|
||||
#define RTCA3_RCR2 0x24
|
||||
#define RTCA3_RCR2_START BIT(0)
|
||||
#define RTCA3_RCR2_RESET BIT(1)
|
||||
#define RTCA3_RCR2_AADJE BIT(4)
|
||||
#define RTCA3_RCR2_ADJP BIT(5)
|
||||
#define RTCA3_RCR2_HR24 BIT(6)
|
||||
#define RTCA3_RCR2_CNTMD BIT(7)
|
||||
#define RTCA3_RSR 0x20
|
||||
#define RTCA3_RSR_AF BIT(0)
|
||||
#define RTCA3_RSR_CF BIT(1)
|
||||
#define RTCA3_RSR_PF BIT(2)
|
||||
#define RTCA3_RADJ 0x2e
|
||||
#define RTCA3_RADJ_ADJ GENMASK(5, 0)
|
||||
#define RTCA3_RADJ_ADJ_MAX 0x3f
|
||||
#define RTCA3_RADJ_PMADJ GENMASK(7, 6)
|
||||
#define RTCA3_RADJ_PMADJ_NONE 0
|
||||
#define RTCA3_RADJ_PMADJ_ADD 1
|
||||
#define RTCA3_RADJ_PMADJ_SUB 2
|
||||
|
||||
/* Polling operation timeouts. */
|
||||
#define RTCA3_DEFAULT_TIMEOUT_US 150
|
||||
#define RTCA3_IRQSET_TIMEOUT_US 5000
|
||||
#define RTCA3_START_TIMEOUT_US 150000
|
||||
#define RTCA3_RESET_TIMEOUT_US 200000
|
||||
|
||||
/**
|
||||
* enum rtca3_alrm_set_step - RTCA3 alarm set steps
|
||||
* @RTCA3_ALRM_SSTEP_DONE: alarm setup done step
|
||||
* @RTCA3_ALRM_SSTEP_IRQ: two 1/64 periodic IRQs were generated step
|
||||
* @RTCA3_ALRM_SSTEP_INIT: alarm setup initialization step
|
||||
*/
|
||||
enum rtca3_alrm_set_step {
|
||||
RTCA3_ALRM_SSTEP_DONE = 0,
|
||||
RTCA3_ALRM_SSTEP_IRQ = 1,
|
||||
RTCA3_ALRM_SSTEP_INIT = 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rtca3_ppb_per_cycle - PPB per cycle
|
||||
* @ten_sec: PPB per cycle in 10 seconds adjutment mode
|
||||
* @sixty_sec: PPB per cycle in 60 seconds adjustment mode
|
||||
*/
|
||||
struct rtca3_ppb_per_cycle {
|
||||
int ten_sec;
|
||||
int sixty_sec;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct rtca3_priv - RTCA3 private data structure
|
||||
* @base: base address
|
||||
* @rtc_dev: RTC device
|
||||
* @rstc: reset control
|
||||
* @set_alarm_completion: alarm setup completion
|
||||
* @alrm_sstep: alarm setup step (see enum rtca3_alrm_set_step)
|
||||
* @lock: device lock
|
||||
* @ppb: ppb per cycle for each the available adjustment modes
|
||||
* @wakeup_irq: wakeup IRQ
|
||||
*/
|
||||
struct rtca3_priv {
|
||||
void __iomem *base;
|
||||
struct rtc_device *rtc_dev;
|
||||
struct reset_control *rstc;
|
||||
struct completion set_alarm_completion;
|
||||
atomic_t alrm_sstep;
|
||||
spinlock_t lock;
|
||||
struct rtca3_ppb_per_cycle ppb;
|
||||
int wakeup_irq;
|
||||
};
|
||||
|
||||
static void rtca3_byte_update_bits(struct rtca3_priv *priv, u8 off, u8 mask, u8 val)
|
||||
{
|
||||
u8 tmp;
|
||||
|
||||
tmp = readb(priv->base + off);
|
||||
tmp &= ~mask;
|
||||
tmp |= (val & mask);
|
||||
writeb(tmp, priv->base + off);
|
||||
}
|
||||
|
||||
static u8 rtca3_alarm_handler_helper(struct rtca3_priv *priv)
|
||||
{
|
||||
u8 val, pending;
|
||||
|
||||
val = readb(priv->base + RTCA3_RSR);
|
||||
pending = val & RTCA3_RSR_AF;
|
||||
writeb(val & ~pending, priv->base + RTCA3_RSR);
|
||||
|
||||
if (pending)
|
||||
rtc_update_irq(priv->rtc_dev, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
return pending;
|
||||
}
|
||||
|
||||
static irqreturn_t rtca3_alarm_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_id;
|
||||
u8 pending;
|
||||
|
||||
guard(spinlock)(&priv->lock);
|
||||
|
||||
pending = rtca3_alarm_handler_helper(priv);
|
||||
|
||||
return IRQ_RETVAL(pending);
|
||||
}
|
||||
|
||||
static irqreturn_t rtca3_periodic_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_id;
|
||||
u8 val, pending;
|
||||
|
||||
guard(spinlock)(&priv->lock);
|
||||
|
||||
val = readb(priv->base + RTCA3_RSR);
|
||||
pending = val & RTCA3_RSR_PF;
|
||||
|
||||
if (pending) {
|
||||
writeb(val & ~pending, priv->base + RTCA3_RSR);
|
||||
|
||||
if (atomic_read(&priv->alrm_sstep) > RTCA3_ALRM_SSTEP_IRQ) {
|
||||
/* Alarm setup in progress. */
|
||||
atomic_dec(&priv->alrm_sstep);
|
||||
|
||||
if (atomic_read(&priv->alrm_sstep) == RTCA3_ALRM_SSTEP_IRQ) {
|
||||
/*
|
||||
* We got 2 * 1/64 periodic interrupts. Disable
|
||||
* interrupt and let alarm setup continue.
|
||||
*/
|
||||
rtca3_byte_update_bits(priv, RTCA3_RCR1,
|
||||
RTCA3_RCR1_PIE, 0);
|
||||
readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, val,
|
||||
!(val & RTCA3_RCR1_PIE),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
complete(&priv->set_alarm_completion);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_RETVAL(pending);
|
||||
}
|
||||
|
||||
static void rtca3_prepare_cntalrm_regs_for_read(struct rtca3_priv *priv, bool cnt)
|
||||
{
|
||||
/* Offset b/w time and alarm registers. */
|
||||
u8 offset = cnt ? 0 : 0xe;
|
||||
|
||||
/*
|
||||
* According to HW manual (section 22.6.4. Notes on writing to and
|
||||
* reading from registers) after writing to count registers, alarm
|
||||
* registers, year alarm enable register, bits RCR2.AADJE, AADJP,
|
||||
* and HR24 register, we need to do 3 empty reads before being
|
||||
* able to fetch the registers content.
|
||||
*/
|
||||
for (u8 i = 0; i < 3; i++) {
|
||||
readb(priv->base + RTCA3_RSECCNT + offset);
|
||||
readb(priv->base + RTCA3_RMINCNT + offset);
|
||||
readb(priv->base + RTCA3_RHRCNT + offset);
|
||||
readb(priv->base + RTCA3_RWKCNT + offset);
|
||||
readb(priv->base + RTCA3_RDAYCNT + offset);
|
||||
readw(priv->base + RTCA3_RYRCNT + offset);
|
||||
if (!cnt)
|
||||
readb(priv->base + RTCA3_RYRAREN);
|
||||
}
|
||||
}
|
||||
|
||||
static int rtca3_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
u8 sec, min, hour, wday, mday, month, tmp;
|
||||
u8 trials = 0;
|
||||
u32 year100;
|
||||
u16 year;
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
tmp = readb(priv->base + RTCA3_RCR2);
|
||||
if (!(tmp & RTCA3_RCR2_START))
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
/* Clear carry interrupt. */
|
||||
rtca3_byte_update_bits(priv, RTCA3_RSR, RTCA3_RSR_CF, 0);
|
||||
|
||||
/* Read counters. */
|
||||
sec = readb(priv->base + RTCA3_RSECCNT);
|
||||
min = readb(priv->base + RTCA3_RMINCNT);
|
||||
hour = readb(priv->base + RTCA3_RHRCNT);
|
||||
wday = readb(priv->base + RTCA3_RWKCNT);
|
||||
mday = readb(priv->base + RTCA3_RDAYCNT);
|
||||
month = readb(priv->base + RTCA3_RMONCNT);
|
||||
year = readw(priv->base + RTCA3_RYRCNT);
|
||||
|
||||
tmp = readb(priv->base + RTCA3_RSR);
|
||||
|
||||
/*
|
||||
* We cannot generate carries due to reading 64Hz counter as
|
||||
* the driver doesn't implement carry, thus, carries will be
|
||||
* generated once per seconds. Add a timeout of 5 trials here
|
||||
* to avoid infinite loop, if any.
|
||||
*/
|
||||
} while ((tmp & RTCA3_RSR_CF) && ++trials < 5);
|
||||
|
||||
if (trials >= 5)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
tm->tm_sec = bcd2bin(FIELD_GET(RTCA3_RSECCNT_SEC, sec));
|
||||
tm->tm_min = bcd2bin(FIELD_GET(RTCA3_RMINCNT_MIN, min));
|
||||
tm->tm_hour = bcd2bin(FIELD_GET(RTCA3_RHRCNT_HR, hour));
|
||||
tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKCNT_WK, wday));
|
||||
tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYCNT_DAY, mday));
|
||||
tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONCNT_MONTH, month)) - 1;
|
||||
year = FIELD_GET(RTCA3_RYRCNT_YEAR, year);
|
||||
year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20);
|
||||
tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtca3_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
u8 rcr2, tmp;
|
||||
int ret;
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
/* Stop the RTC. */
|
||||
rcr2 = readb(priv->base + RTCA3_RCR2);
|
||||
writeb(rcr2 & ~RTCA3_RCR2_START, priv->base + RTCA3_RCR2);
|
||||
ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp,
|
||||
!(tmp & RTCA3_RCR2_START),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Update time. */
|
||||
writeb(bin2bcd(tm->tm_sec), priv->base + RTCA3_RSECCNT);
|
||||
writeb(bin2bcd(tm->tm_min), priv->base + RTCA3_RMINCNT);
|
||||
writeb(bin2bcd(tm->tm_hour), priv->base + RTCA3_RHRCNT);
|
||||
writeb(bin2bcd(tm->tm_wday), priv->base + RTCA3_RWKCNT);
|
||||
writeb(bin2bcd(tm->tm_mday), priv->base + RTCA3_RDAYCNT);
|
||||
writeb(bin2bcd(tm->tm_mon + 1), priv->base + RTCA3_RMONCNT);
|
||||
writew(bin2bcd(tm->tm_year % 100), priv->base + RTCA3_RYRCNT);
|
||||
|
||||
/* Make sure we can read back the counters. */
|
||||
rtca3_prepare_cntalrm_regs_for_read(priv, true);
|
||||
|
||||
/* Start RTC. */
|
||||
writeb(rcr2 | RTCA3_RCR2_START, priv->base + RTCA3_RCR2);
|
||||
return readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp,
|
||||
(tmp & RTCA3_RCR2_START),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int rtca3_alarm_irq_set_helper(struct rtca3_priv *priv,
|
||||
u8 interrupts,
|
||||
unsigned int enabled)
|
||||
{
|
||||
u8 tmp, val;
|
||||
|
||||
if (enabled) {
|
||||
/*
|
||||
* AIE, CIE, PIE bit indexes in RSR corresponds with
|
||||
* those on RCR1. Same interrupts mask can be used.
|
||||
*/
|
||||
rtca3_byte_update_bits(priv, RTCA3_RSR, interrupts, 0);
|
||||
val = interrupts;
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
|
||||
rtca3_byte_update_bits(priv, RTCA3_RCR1, interrupts, val);
|
||||
return readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp,
|
||||
((tmp & interrupts) == val),
|
||||
10, RTCA3_IRQSET_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int rtca3_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
return rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE, enabled);
|
||||
}
|
||||
|
||||
static int rtca3_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
u8 sec, min, hour, wday, mday, month;
|
||||
struct rtc_time *tm = &wkalrm->time;
|
||||
u32 year100;
|
||||
u16 year;
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
sec = readb(priv->base + RTCA3_RSECAR);
|
||||
min = readb(priv->base + RTCA3_RMINAR);
|
||||
hour = readb(priv->base + RTCA3_RHRAR);
|
||||
wday = readb(priv->base + RTCA3_RWKAR);
|
||||
mday = readb(priv->base + RTCA3_RDAYAR);
|
||||
month = readb(priv->base + RTCA3_RMONAR);
|
||||
year = readw(priv->base + RTCA3_RYRAR);
|
||||
|
||||
tm->tm_sec = bcd2bin(FIELD_GET(RTCA3_RSECAR_SEC, sec));
|
||||
tm->tm_min = bcd2bin(FIELD_GET(RTCA3_RMINAR_MIN, min));
|
||||
tm->tm_hour = bcd2bin(FIELD_GET(RTCA3_RHRAR_HR, hour));
|
||||
tm->tm_wday = bcd2bin(FIELD_GET(RTCA3_RWKAR_DAYW, wday));
|
||||
tm->tm_mday = bcd2bin(FIELD_GET(RTCA3_RDAYAR_DATE, mday));
|
||||
tm->tm_mon = bcd2bin(FIELD_GET(RTCA3_RMONAR_MON, month)) - 1;
|
||||
year = FIELD_GET(RTCA3_RYRAR_YR, year);
|
||||
year100 = bcd2bin((year == 0x99) ? 0x19 : 0x20);
|
||||
tm->tm_year = (year100 * 100 + bcd2bin(year)) - 1900;
|
||||
|
||||
wkalrm->enabled = !!(readb(priv->base + RTCA3_RCR1) & RTCA3_RCR1_AIE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtca3_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
struct rtc_time *tm = &wkalrm->time;
|
||||
u8 rcr1, tmp;
|
||||
int ret;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &priv->lock) {
|
||||
tmp = readb(priv->base + RTCA3_RCR2);
|
||||
if (!(tmp & RTCA3_RCR2_START))
|
||||
return -EPERM;
|
||||
|
||||
/* Disable AIE to prevent false interrupts. */
|
||||
rcr1 = readb(priv->base + RTCA3_RCR1);
|
||||
rcr1 &= ~RTCA3_RCR1_AIE;
|
||||
writeb(rcr1, priv->base + RTCA3_RCR1);
|
||||
ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp,
|
||||
!(tmp & RTCA3_RCR1_AIE),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set the time and enable the alarm. */
|
||||
writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_sec), priv->base + RTCA3_RSECAR);
|
||||
writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_min), priv->base + RTCA3_RMINAR);
|
||||
writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_hour), priv->base + RTCA3_RHRAR);
|
||||
writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_wday), priv->base + RTCA3_RWKAR);
|
||||
writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_mday), priv->base + RTCA3_RDAYAR);
|
||||
writeb(RTCA3_AR_ENB | bin2bcd(tm->tm_mon + 1), priv->base + RTCA3_RMONAR);
|
||||
|
||||
writew(bin2bcd(tm->tm_year % 100), priv->base + RTCA3_RYRAR);
|
||||
writeb(RTCA3_AR_ENB, priv->base + RTCA3_RYRAREN);
|
||||
|
||||
/* Make sure we can read back the counters. */
|
||||
rtca3_prepare_cntalrm_regs_for_read(priv, false);
|
||||
|
||||
/* Need to wait for 2 * 1/64 periodic interrupts to be generated. */
|
||||
atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_INIT);
|
||||
reinit_completion(&priv->set_alarm_completion);
|
||||
|
||||
/* Enable periodic interrupt. */
|
||||
rcr1 |= RTCA3_RCR1_PIE;
|
||||
writeb(rcr1, priv->base + RTCA3_RCR1);
|
||||
ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp,
|
||||
(tmp & RTCA3_RCR1_PIE),
|
||||
10, RTCA3_IRQSET_TIMEOUT_US);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto setup_failed;
|
||||
|
||||
/* Wait for the 2 * 1/64 periodic interrupts. */
|
||||
ret = wait_for_completion_interruptible_timeout(&priv->set_alarm_completion,
|
||||
msecs_to_jiffies(500));
|
||||
if (ret <= 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto setup_failed;
|
||||
}
|
||||
|
||||
scoped_guard(spinlock_irqsave, &priv->lock) {
|
||||
ret = rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE, wkalrm->enabled);
|
||||
atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
setup_failed:
|
||||
scoped_guard(spinlock_irqsave, &priv->lock) {
|
||||
/*
|
||||
* Disable PIE to avoid interrupt storm in case HW needed more than
|
||||
* specified timeout for setup.
|
||||
*/
|
||||
writeb(rcr1 & ~RTCA3_RCR1_PIE, priv->base + RTCA3_RCR1);
|
||||
readb_poll_timeout_atomic(priv->base + RTCA3_RCR1, tmp, !(tmp & ~RTCA3_RCR1_PIE),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rtca3_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
u8 val, radj, cycles;
|
||||
u32 ppb_per_cycle;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &priv->lock) {
|
||||
radj = readb(priv->base + RTCA3_RADJ);
|
||||
val = readb(priv->base + RTCA3_RCR2);
|
||||
}
|
||||
|
||||
cycles = FIELD_GET(RTCA3_RADJ_ADJ, radj);
|
||||
|
||||
if (!cycles) {
|
||||
*offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val & RTCA3_RCR2_ADJP)
|
||||
ppb_per_cycle = priv->ppb.ten_sec;
|
||||
else
|
||||
ppb_per_cycle = priv->ppb.sixty_sec;
|
||||
|
||||
*offset = cycles * ppb_per_cycle;
|
||||
val = FIELD_GET(RTCA3_RADJ_PMADJ, radj);
|
||||
if (val == RTCA3_RADJ_PMADJ_SUB)
|
||||
*offset = -(*offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtca3_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
int cycles, cycles10, cycles60;
|
||||
u8 radj, adjp, tmp;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Automatic time error adjustment could be set at intervals of 10
|
||||
* or 60 seconds.
|
||||
*/
|
||||
cycles10 = DIV_ROUND_CLOSEST(offset, priv->ppb.ten_sec);
|
||||
cycles60 = DIV_ROUND_CLOSEST(offset, priv->ppb.sixty_sec);
|
||||
|
||||
/* We can set b/w 1 and 63 clock cycles. */
|
||||
if (cycles60 >= -RTCA3_RADJ_ADJ_MAX &&
|
||||
cycles60 <= RTCA3_RADJ_ADJ_MAX) {
|
||||
cycles = cycles60;
|
||||
adjp = 0;
|
||||
} else if (cycles10 >= -RTCA3_RADJ_ADJ_MAX &&
|
||||
cycles10 <= RTCA3_RADJ_ADJ_MAX) {
|
||||
cycles = cycles10;
|
||||
adjp = RTCA3_RCR2_ADJP;
|
||||
} else {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
radj = FIELD_PREP(RTCA3_RADJ_ADJ, abs(cycles));
|
||||
if (!cycles)
|
||||
radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_NONE);
|
||||
else if (cycles > 0)
|
||||
radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_ADD);
|
||||
else
|
||||
radj |= FIELD_PREP(RTCA3_RADJ_PMADJ, RTCA3_RADJ_PMADJ_SUB);
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
tmp = readb(priv->base + RTCA3_RCR2);
|
||||
|
||||
if ((tmp & RTCA3_RCR2_ADJP) != adjp) {
|
||||
/* RADJ.PMADJ need to be set to zero before setting RCR2.ADJP. */
|
||||
writeb(0, priv->base + RTCA3_RADJ);
|
||||
ret = readb_poll_timeout_atomic(priv->base + RTCA3_RADJ, tmp, !tmp,
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtca3_byte_update_bits(priv, RTCA3_RCR2, RTCA3_RCR2_ADJP, adjp);
|
||||
ret = readb_poll_timeout_atomic(priv->base + RTCA3_RCR2, tmp,
|
||||
((tmp & RTCA3_RCR2_ADJP) == adjp),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
writeb(radj, priv->base + RTCA3_RADJ);
|
||||
return readb_poll_timeout_atomic(priv->base + RTCA3_RADJ, tmp, (tmp == radj),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rtca3_ops = {
|
||||
.read_time = rtca3_read_time,
|
||||
.set_time = rtca3_set_time,
|
||||
.read_alarm = rtca3_read_alarm,
|
||||
.set_alarm = rtca3_set_alarm,
|
||||
.alarm_irq_enable = rtca3_alarm_irq_enable,
|
||||
.set_offset = rtca3_set_offset,
|
||||
.read_offset = rtca3_read_offset,
|
||||
};
|
||||
|
||||
static int rtca3_initial_setup(struct clk *clk, struct rtca3_priv *priv)
|
||||
{
|
||||
unsigned long osc32k_rate;
|
||||
u8 val, tmp, mask;
|
||||
u32 sleep_us;
|
||||
int ret;
|
||||
|
||||
osc32k_rate = clk_get_rate(clk);
|
||||
if (!osc32k_rate)
|
||||
return -EINVAL;
|
||||
|
||||
sleep_us = DIV_ROUND_UP_ULL(1000000ULL, osc32k_rate) * 6;
|
||||
|
||||
priv->ppb.ten_sec = DIV_ROUND_CLOSEST_ULL(1000000000ULL, (osc32k_rate * 10));
|
||||
priv->ppb.sixty_sec = DIV_ROUND_CLOSEST_ULL(1000000000ULL, (osc32k_rate * 60));
|
||||
|
||||
/*
|
||||
* According to HW manual (section 22.4.2. Clock and count mode setting procedure)
|
||||
* we need to wait at least 6 cycles of the 32KHz clock after clock was enabled.
|
||||
*/
|
||||
usleep_range(sleep_us, sleep_us + 10);
|
||||
|
||||
/* Disable all interrupts. */
|
||||
mask = RTCA3_RCR1_AIE | RTCA3_RCR1_CIE | RTCA3_RCR1_PIE;
|
||||
ret = rtca3_alarm_irq_set_helper(priv, mask, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mask = RTCA3_RCR2_START | RTCA3_RCR2_HR24;
|
||||
val = readb(priv->base + RTCA3_RCR2);
|
||||
/* Nothing to do if already started in 24 hours and calendar count mode. */
|
||||
if ((val & mask) == mask)
|
||||
return 0;
|
||||
|
||||
/* Reconfigure the RTC in 24 hours and calendar count mode. */
|
||||
mask = RTCA3_RCR2_START | RTCA3_RCR2_CNTMD;
|
||||
writeb(0, priv->base + RTCA3_RCR2);
|
||||
ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, !(tmp & mask),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Set 24 hours mode. According to HW manual (section 22.3.19. RTC Control
|
||||
* Register 2) this needs to be done separate from stop operation.
|
||||
*/
|
||||
mask = RTCA3_RCR2_HR24;
|
||||
val = RTCA3_RCR2_HR24;
|
||||
writeb(val, priv->base + RTCA3_RCR2);
|
||||
ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, (tmp & mask),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Execute reset. */
|
||||
mask = RTCA3_RCR2_RESET;
|
||||
writeb(val | RTCA3_RCR2_RESET, priv->base + RTCA3_RCR2);
|
||||
ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, !(tmp & mask),
|
||||
10, RTCA3_RESET_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* According to HW manual (section 22.6.3. Notes on writing to and reading
|
||||
* from registers) after reset we need to wait 6 clock cycles before
|
||||
* writing to RTC registers.
|
||||
*/
|
||||
usleep_range(sleep_us, sleep_us + 10);
|
||||
|
||||
/* Set no adjustment. */
|
||||
writeb(0, priv->base + RTCA3_RADJ);
|
||||
ret = readb_poll_timeout(priv->base + RTCA3_RADJ, tmp, !tmp, 10,
|
||||
RTCA3_DEFAULT_TIMEOUT_US);
|
||||
|
||||
/* Start the RTC and enable automatic time error adjustment. */
|
||||
mask = RTCA3_RCR2_START | RTCA3_RCR2_AADJE;
|
||||
val |= RTCA3_RCR2_START | RTCA3_RCR2_AADJE;
|
||||
writeb(val, priv->base + RTCA3_RCR2);
|
||||
ret = readb_poll_timeout(priv->base + RTCA3_RCR2, tmp, ((tmp & mask) == mask),
|
||||
10, RTCA3_START_TIMEOUT_US);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* According to HW manual (section 22.6.4. Notes on writing to and reading
|
||||
* from registers) we need to wait 1/128 seconds while the clock is operating
|
||||
* (RCR2.START bit = 1) to be able to read the counters after a return from
|
||||
* reset.
|
||||
*/
|
||||
usleep_range(8000, 9000);
|
||||
|
||||
/* Set period interrupt to 1/64 seconds. It is necessary for alarm setup. */
|
||||
val = FIELD_PREP(RTCA3_RCR1_PES, RTCA3_RCR1_PES_1_64_SEC);
|
||||
rtca3_byte_update_bits(priv, RTCA3_RCR1, RTCA3_RCR1_PES, val);
|
||||
return readb_poll_timeout(priv->base + RTCA3_RCR1, tmp, ((tmp & RTCA3_RCR1_PES) == val),
|
||||
10, RTCA3_DEFAULT_TIMEOUT_US);
|
||||
}
|
||||
|
||||
static int rtca3_request_irqs(struct platform_device *pdev, struct rtca3_priv *priv)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret, irq;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "alarm");
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "Failed to get alarm IRQ!\n");
|
||||
|
||||
ret = devm_request_irq(dev, irq, rtca3_alarm_handler, 0, "rtca3-alarm", priv);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request alarm IRQ!\n");
|
||||
priv->wakeup_irq = irq;
|
||||
|
||||
irq = platform_get_irq_byname(pdev, "period");
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "Failed to get period IRQ!\n");
|
||||
|
||||
ret = devm_request_irq(dev, irq, rtca3_periodic_handler, 0, "rtca3-period", priv);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request period IRQ!\n");
|
||||
|
||||
/*
|
||||
* Driver doesn't implement carry handler. Just get the IRQ here
|
||||
* for backward compatibility, in case carry support will be added later.
|
||||
*/
|
||||
irq = platform_get_irq_byname(pdev, "carry");
|
||||
if (irq < 0)
|
||||
return dev_err_probe(dev, irq, "Failed to get carry IRQ!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtca3_action(void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = reset_control_assert(priv->rstc);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to de-assert reset!");
|
||||
|
||||
ret = pm_runtime_put_sync(dev);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "Failed to runtime suspend!");
|
||||
}
|
||||
|
||||
static int rtca3_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rtca3_priv *priv;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
ret = devm_pm_runtime_enable(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->rstc = devm_reset_control_get_shared(dev, NULL);
|
||||
if (IS_ERR(priv->rstc))
|
||||
return PTR_ERR(priv->rstc);
|
||||
|
||||
ret = pm_runtime_resume_and_get(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = reset_control_deassert(priv->rstc);
|
||||
if (ret) {
|
||||
pm_runtime_put_sync(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, priv);
|
||||
ret = devm_add_action_or_reset(dev, rtca3_action, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* This must be an always-on clock to keep the RTC running even after
|
||||
* driver is unbinded.
|
||||
*/
|
||||
clk = devm_clk_get_enabled(dev, "counter");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
atomic_set(&priv->alrm_sstep, RTCA3_ALRM_SSTEP_DONE);
|
||||
init_completion(&priv->set_alarm_completion);
|
||||
|
||||
ret = rtca3_initial_setup(clk, priv);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to setup the RTC!\n");
|
||||
|
||||
ret = rtca3_request_irqs(pdev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
priv->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(priv->rtc_dev))
|
||||
return PTR_ERR(priv->rtc_dev);
|
||||
|
||||
priv->rtc_dev->ops = &rtca3_ops;
|
||||
priv->rtc_dev->max_user_freq = 256;
|
||||
priv->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
priv->rtc_dev->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
return devm_rtc_register_device(priv->rtc_dev);
|
||||
}
|
||||
|
||||
static void rtca3_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rtca3_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
/*
|
||||
* Disable alarm, periodic interrupts. The RTC device cannot
|
||||
* power up the system.
|
||||
*/
|
||||
rtca3_alarm_irq_set_helper(priv, RTCA3_RCR1_AIE | RTCA3_RCR1_PIE, 0);
|
||||
}
|
||||
|
||||
static int rtca3_suspend(struct device *dev)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (!device_may_wakeup(dev))
|
||||
return 0;
|
||||
|
||||
/* Alarm setup in progress. */
|
||||
if (atomic_read(&priv->alrm_sstep) != RTCA3_ALRM_SSTEP_DONE)
|
||||
return -EBUSY;
|
||||
|
||||
enable_irq_wake(priv->wakeup_irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtca3_clean_alarm(struct rtca3_priv *priv)
|
||||
{
|
||||
struct rtc_device *rtc_dev = priv->rtc_dev;
|
||||
time64_t alarm_time, now;
|
||||
struct rtc_wkalrm alarm;
|
||||
struct rtc_time tm;
|
||||
u8 pending;
|
||||
int ret;
|
||||
|
||||
ret = rtc_read_alarm(rtc_dev, &alarm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!alarm.enabled)
|
||||
return 0;
|
||||
|
||||
ret = rtc_read_time(rtc_dev, &tm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarm_time = rtc_tm_to_time64(&alarm.time);
|
||||
now = rtc_tm_to_time64(&tm);
|
||||
if (alarm_time >= now)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Heuristically, it has been determined that when returning from deep
|
||||
* sleep state the RTCA3_RSR.AF is zero even though the alarm expired.
|
||||
* Call again the rtc_update_irq() if alarm helper detects this.
|
||||
*/
|
||||
|
||||
guard(spinlock_irqsave)(&priv->lock);
|
||||
|
||||
pending = rtca3_alarm_handler_helper(priv);
|
||||
if (!pending)
|
||||
rtc_update_irq(priv->rtc_dev, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtca3_resume(struct device *dev)
|
||||
{
|
||||
struct rtca3_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (!device_may_wakeup(dev))
|
||||
return 0;
|
||||
|
||||
disable_irq_wake(priv->wakeup_irq);
|
||||
|
||||
/*
|
||||
* According to the HW manual (section 22.6.4 Notes on writing to
|
||||
* and reading from registers) we need to wait 1/128 seconds while
|
||||
* RCR2.START = 1 to be able to read the counters after a return from low
|
||||
* power consumption state.
|
||||
*/
|
||||
mdelay(8);
|
||||
|
||||
/*
|
||||
* The alarm cannot wake the system from deep sleep states. In case
|
||||
* we return from deep sleep states and the alarm expired we need
|
||||
* to disable it to avoid failures when setting another alarm.
|
||||
*/
|
||||
return rtca3_clean_alarm(priv);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(rtca3_pm_ops, rtca3_suspend, rtca3_resume);
|
||||
|
||||
static const struct of_device_id rtca3_of_match[] = {
|
||||
{ .compatible = "renesas,rz-rtca3", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rtca3_of_match);
|
||||
|
||||
static struct platform_driver rtca3_platform_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-rtca3",
|
||||
.pm = pm_ptr(&rtca3_pm_ops),
|
||||
.of_match_table = rtca3_of_match,
|
||||
},
|
||||
.probe = rtca3_probe,
|
||||
.remove = rtca3_remove,
|
||||
};
|
||||
module_platform_driver(rtca3_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Renesas RTCA-3 RTC driver");
|
||||
MODULE_AUTHOR("Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -228,7 +228,7 @@ static void rtd119x_rtc_remove(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver rtd119x_rtc_driver = {
|
||||
.probe = rtd119x_rtc_probe,
|
||||
.remove_new = rtd119x_rtc_remove,
|
||||
.remove = rtd119x_rtc_remove,
|
||||
.driver = {
|
||||
.name = "rtd1295-rtc",
|
||||
.of_match_table = rtd119x_rtc_dt_ids,
|
||||
|
@ -120,8 +120,9 @@ static ssize_t timestamp0_show(struct device *dev,
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
|
||||
struct rtc_time tm;
|
||||
int ret, count;
|
||||
unsigned int count;
|
||||
u8 date[6];
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
|
||||
if (ret)
|
||||
@ -156,7 +157,8 @@ static ssize_t timestamp0_count_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent);
|
||||
int ret, count;
|
||||
unsigned int count;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count);
|
||||
if (ret)
|
||||
|
@ -7,7 +7,7 @@
|
||||
* - 2022 Schneider Electric
|
||||
*
|
||||
* Authors:
|
||||
* - Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com>
|
||||
* - Michel Pollet <buserror@gmail.com>
|
||||
* - Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
*/
|
||||
|
||||
@ -35,13 +35,13 @@
|
||||
#define RZN1_RTC_CTL2_WUST BIT(5)
|
||||
#define RZN1_RTC_CTL2_STOPPED (RZN1_RTC_CTL2_WAIT | RZN1_RTC_CTL2_WST)
|
||||
|
||||
#define RZN1_RTC_SEC 0x14
|
||||
#define RZN1_RTC_MIN 0x18
|
||||
#define RZN1_RTC_HOUR 0x1c
|
||||
#define RZN1_RTC_WEEK 0x20
|
||||
#define RZN1_RTC_DAY 0x24
|
||||
#define RZN1_RTC_MONTH 0x28
|
||||
#define RZN1_RTC_YEAR 0x2c
|
||||
#define RZN1_RTC_TIME 0x30
|
||||
#define RZN1_RTC_TIME_MIN_SHIFT 8
|
||||
#define RZN1_RTC_TIME_HOUR_SHIFT 16
|
||||
#define RZN1_RTC_CAL 0x34
|
||||
#define RZN1_RTC_CAL_DAY_SHIFT 8
|
||||
#define RZN1_RTC_CAL_MON_SHIFT 16
|
||||
#define RZN1_RTC_CAL_YEAR_SHIFT 24
|
||||
|
||||
#define RZN1_RTC_SUBU 0x38
|
||||
#define RZN1_RTC_SUBU_DEV BIT(7)
|
||||
@ -52,12 +52,8 @@
|
||||
#define RZN1_RTC_ALW 0x48
|
||||
|
||||
#define RZN1_RTC_SECC 0x4c
|
||||
#define RZN1_RTC_MINC 0x50
|
||||
#define RZN1_RTC_HOURC 0x54
|
||||
#define RZN1_RTC_WEEKC 0x58
|
||||
#define RZN1_RTC_DAYC 0x5c
|
||||
#define RZN1_RTC_MONTHC 0x60
|
||||
#define RZN1_RTC_YEARC 0x64
|
||||
#define RZN1_RTC_TIMEC 0x68
|
||||
#define RZN1_RTC_CALC 0x6c
|
||||
|
||||
struct rzn1_rtc {
|
||||
struct rtc_device *rtcdev;
|
||||
@ -66,26 +62,18 @@ struct rzn1_rtc {
|
||||
|
||||
static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_sec = readl(rtc->base + RZN1_RTC_SECC);
|
||||
tm->tm_min = readl(rtc->base + RZN1_RTC_MINC);
|
||||
tm->tm_hour = readl(rtc->base + RZN1_RTC_HOURC);
|
||||
tm->tm_wday = readl(rtc->base + RZN1_RTC_WEEKC);
|
||||
tm->tm_mday = readl(rtc->base + RZN1_RTC_DAYC);
|
||||
tm->tm_mon = readl(rtc->base + RZN1_RTC_MONTHC);
|
||||
tm->tm_year = readl(rtc->base + RZN1_RTC_YEARC);
|
||||
}
|
||||
u32 val;
|
||||
|
||||
static unsigned int rzn1_rtc_tm_to_wday(struct rtc_time *tm)
|
||||
{
|
||||
time64_t time;
|
||||
unsigned int days;
|
||||
u32 secs;
|
||||
val = readl(rtc->base + RZN1_RTC_TIMEC);
|
||||
tm->tm_sec = bcd2bin(val);
|
||||
tm->tm_min = bcd2bin(val >> RZN1_RTC_TIME_MIN_SHIFT);
|
||||
tm->tm_hour = bcd2bin(val >> RZN1_RTC_TIME_HOUR_SHIFT);
|
||||
|
||||
time = rtc_tm_to_time64(tm);
|
||||
days = div_s64_rem(time, 86400, &secs);
|
||||
|
||||
/* day of the week, 1970-01-01 was a Thursday */
|
||||
return (days + 4) % 7;
|
||||
val = readl(rtc->base + RZN1_RTC_CALC);
|
||||
tm->tm_wday = val & 0x0f;
|
||||
tm->tm_mday = bcd2bin(val >> RZN1_RTC_CAL_DAY_SHIFT);
|
||||
tm->tm_mon = bcd2bin(val >> RZN1_RTC_CAL_MON_SHIFT) - 1;
|
||||
tm->tm_year = bcd2bin(val >> RZN1_RTC_CAL_YEAR_SHIFT) + 100;
|
||||
}
|
||||
|
||||
static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
@ -103,17 +91,9 @@ static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
||||
rzn1_rtc_get_time_snapshot(rtc, tm);
|
||||
secs = readl(rtc->base + RZN1_RTC_SECC);
|
||||
if (tm->tm_sec != secs)
|
||||
if (tm->tm_sec != bcd2bin(secs))
|
||||
rzn1_rtc_get_time_snapshot(rtc, tm);
|
||||
|
||||
tm->tm_sec = bcd2bin(tm->tm_sec);
|
||||
tm->tm_min = bcd2bin(tm->tm_min);
|
||||
tm->tm_hour = bcd2bin(tm->tm_hour);
|
||||
tm->tm_wday = bcd2bin(tm->tm_wday);
|
||||
tm->tm_mday = bcd2bin(tm->tm_mday);
|
||||
tm->tm_mon = bcd2bin(tm->tm_mon);
|
||||
tm->tm_year = bcd2bin(tm->tm_year);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -123,14 +103,6 @@ static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
tm->tm_sec = bin2bcd(tm->tm_sec);
|
||||
tm->tm_min = bin2bcd(tm->tm_min);
|
||||
tm->tm_hour = bin2bcd(tm->tm_hour);
|
||||
tm->tm_wday = bin2bcd(rzn1_rtc_tm_to_wday(tm));
|
||||
tm->tm_mday = bin2bcd(tm->tm_mday);
|
||||
tm->tm_mon = bin2bcd(tm->tm_mon);
|
||||
tm->tm_year = bin2bcd(tm->tm_year);
|
||||
|
||||
val = readl(rtc->base + RZN1_RTC_CTL2);
|
||||
if (!(val & RZN1_RTC_CTL2_STOPPED)) {
|
||||
/* Hold the counter if it was counting up */
|
||||
@ -144,13 +116,17 @@ static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
return ret;
|
||||
}
|
||||
|
||||
writel(tm->tm_sec, rtc->base + RZN1_RTC_SEC);
|
||||
writel(tm->tm_min, rtc->base + RZN1_RTC_MIN);
|
||||
writel(tm->tm_hour, rtc->base + RZN1_RTC_HOUR);
|
||||
writel(tm->tm_wday, rtc->base + RZN1_RTC_WEEK);
|
||||
writel(tm->tm_mday, rtc->base + RZN1_RTC_DAY);
|
||||
writel(tm->tm_mon, rtc->base + RZN1_RTC_MONTH);
|
||||
writel(tm->tm_year, rtc->base + RZN1_RTC_YEAR);
|
||||
val = bin2bcd(tm->tm_sec);
|
||||
val |= bin2bcd(tm->tm_min) << RZN1_RTC_TIME_MIN_SHIFT;
|
||||
val |= bin2bcd(tm->tm_hour) << RZN1_RTC_TIME_HOUR_SHIFT;
|
||||
writel(val, rtc->base + RZN1_RTC_TIME);
|
||||
|
||||
val = tm->tm_wday;
|
||||
val |= bin2bcd(tm->tm_mday) << RZN1_RTC_CAL_DAY_SHIFT;
|
||||
val |= bin2bcd(tm->tm_mon + 1) << RZN1_RTC_CAL_MON_SHIFT;
|
||||
val |= bin2bcd(tm->tm_year - 100) << RZN1_RTC_CAL_YEAR_SHIFT;
|
||||
writel(val, rtc->base + RZN1_RTC_CAL);
|
||||
|
||||
writel(0, rtc->base + RZN1_RTC_CTL2);
|
||||
|
||||
return 0;
|
||||
@ -405,7 +381,7 @@ MODULE_DEVICE_TABLE(of, rzn1_rtc_of_match);
|
||||
|
||||
static struct platform_driver rzn1_rtc_driver = {
|
||||
.probe = rzn1_rtc_probe,
|
||||
.remove_new = rzn1_rtc_remove,
|
||||
.remove = rzn1_rtc_remove,
|
||||
.driver = {
|
||||
.name = "rzn1-rtc",
|
||||
.of_match_table = rzn1_rtc_of_match,
|
||||
@ -413,7 +389,7 @@ static struct platform_driver rzn1_rtc_driver = {
|
||||
};
|
||||
module_platform_driver(rzn1_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Michel Pollet <Michel.Pollet@bp.renesas.com");
|
||||
MODULE_AUTHOR("Michel Pollet <buserror@gmail.com>");
|
||||
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com");
|
||||
MODULE_DESCRIPTION("RZ/N1 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -597,7 +597,7 @@ MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
|
||||
|
||||
static struct platform_driver s3c_rtc_driver = {
|
||||
.probe = s3c_rtc_probe,
|
||||
.remove_new = s3c_rtc_remove,
|
||||
.remove = s3c_rtc_remove,
|
||||
.driver = {
|
||||
.name = "s3c-rtc",
|
||||
.pm = &s3c_rtc_pm_ops,
|
||||
|
@ -341,7 +341,7 @@ MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids);
|
||||
|
||||
static struct platform_driver sa1100_rtc_driver = {
|
||||
.probe = sa1100_rtc_probe,
|
||||
.remove_new = sa1100_rtc_remove,
|
||||
.remove = sa1100_rtc_remove,
|
||||
.driver = {
|
||||
.name = "sa1100-rtc",
|
||||
.pm = &sa1100_rtc_pm_ops,
|
||||
|
@ -678,7 +678,7 @@ static struct platform_driver sh_rtc_platform_driver __refdata = {
|
||||
.pm = &sh_rtc_pm_ops,
|
||||
.of_match_table = sh_rtc_of_match,
|
||||
},
|
||||
.remove_new = __exit_p(sh_rtc_remove),
|
||||
.remove = __exit_p(sh_rtc_remove),
|
||||
};
|
||||
|
||||
module_platform_driver_probe(sh_rtc_platform_driver, sh_rtc_probe);
|
||||
|
@ -475,7 +475,7 @@ MODULE_DEVICE_TABLE(of, spear_rtc_id_table);
|
||||
|
||||
static struct platform_driver spear_rtc_driver = {
|
||||
.probe = spear_rtc_probe,
|
||||
.remove_new = spear_rtc_remove,
|
||||
.remove = spear_rtc_remove,
|
||||
.shutdown = spear_rtc_shutdown,
|
||||
.driver = {
|
||||
.name = "rtc-spear",
|
||||
|
@ -218,15 +218,14 @@ static int st_rtc_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
|
||||
pdev->name, rtc);
|
||||
ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler,
|
||||
IRQF_NO_AUTOEN, pdev->name, rtc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_irq_wake(rtc->irq);
|
||||
disable_irq(rtc->irq);
|
||||
|
||||
rtc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(rtc->clk))
|
||||
|
@ -1287,7 +1287,7 @@ static const struct dev_pm_ops stm32_rtc_pm_ops = {
|
||||
|
||||
static struct platform_driver stm32_rtc_driver = {
|
||||
.probe = stm32_rtc_probe,
|
||||
.remove_new = stm32_rtc_remove,
|
||||
.remove = stm32_rtc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &stm32_rtc_pm_ops,
|
||||
|
@ -403,7 +403,7 @@ MODULE_DEVICE_TABLE(of, rtc_dt_ids);
|
||||
|
||||
static struct platform_driver stmp3xxx_rtcdrv = {
|
||||
.probe = stmp3xxx_rtc_probe,
|
||||
.remove_new = stmp3xxx_rtc_remove,
|
||||
.remove = stmp3xxx_rtc_remove,
|
||||
.driver = {
|
||||
.name = "stmp3xxx-rtc",
|
||||
.pm = &stmp3xxx_rtc_pm_ops,
|
||||
|
@ -344,7 +344,7 @@ static SIMPLE_DEV_PM_OPS(sp_rtc_pm_ops, sp_rtc_suspend, sp_rtc_resume);
|
||||
|
||||
static struct platform_driver sp_rtc_driver = {
|
||||
.probe = sp_rtc_probe,
|
||||
.remove_new = sp_rtc_remove,
|
||||
.remove = sp_rtc_remove,
|
||||
.driver = {
|
||||
.name = "sp7021-rtc",
|
||||
.of_match_table = sp_rtc_of_match,
|
||||
|
@ -399,7 +399,7 @@ static void tegra_rtc_shutdown(struct platform_device *pdev)
|
||||
|
||||
static struct platform_driver tegra_rtc_driver = {
|
||||
.probe = tegra_rtc_probe,
|
||||
.remove_new = tegra_rtc_remove,
|
||||
.remove = tegra_rtc_remove,
|
||||
.shutdown = tegra_rtc_shutdown,
|
||||
.driver = {
|
||||
.name = "tegra_rtc",
|
||||
|
@ -317,7 +317,7 @@ static struct platform_driver tps6586x_rtc_driver = {
|
||||
.pm = &tps6586x_pm_ops,
|
||||
},
|
||||
.probe = tps6586x_rtc_probe,
|
||||
.remove_new = tps6586x_rtc_remove,
|
||||
.remove = tps6586x_rtc_remove,
|
||||
};
|
||||
module_platform_driver(tps6586x_rtc_driver);
|
||||
|
||||
|
@ -673,7 +673,7 @@ MODULE_DEVICE_TABLE(of, twl_rtc_of_match);
|
||||
|
||||
static struct platform_driver twl4030rtc_driver = {
|
||||
.probe = twl_rtc_probe,
|
||||
.remove_new = twl_rtc_remove,
|
||||
.remove = twl_rtc_remove,
|
||||
.shutdown = twl_rtc_shutdown,
|
||||
.driver = {
|
||||
.name = "twl_rtc",
|
||||
|
@ -251,7 +251,7 @@ MODULE_DEVICE_TABLE(of, wmt_dt_ids);
|
||||
|
||||
static struct platform_driver vt8500_rtc_driver = {
|
||||
.probe = vt8500_rtc_probe,
|
||||
.remove_new = vt8500_rtc_remove,
|
||||
.remove = vt8500_rtc_remove,
|
||||
.driver = {
|
||||
.name = "vt8500-rtc",
|
||||
.of_match_table = wmt_dt_ids,
|
||||
|
@ -459,7 +459,7 @@ static SIMPLE_DEV_PM_OPS(wm8350_rtc_pm_ops, wm8350_rtc_suspend,
|
||||
|
||||
static struct platform_driver wm8350_rtc_driver = {
|
||||
.probe = wm8350_rtc_probe,
|
||||
.remove_new = wm8350_rtc_remove,
|
||||
.remove = wm8350_rtc_remove,
|
||||
.driver = {
|
||||
.name = "wm8350-rtc",
|
||||
.pm = &wm8350_rtc_pm_ops,
|
||||
|
@ -263,7 +263,7 @@ MODULE_DEVICE_TABLE(of, xgene_rtc_of_match);
|
||||
|
||||
static struct platform_driver xgene_rtc_driver = {
|
||||
.probe = xgene_rtc_probe,
|
||||
.remove_new = xgene_rtc_remove,
|
||||
.remove = xgene_rtc_remove,
|
||||
.driver = {
|
||||
.name = "xgene-rtc",
|
||||
.pm = &xgene_rtc_pm_ops,
|
||||
|
@ -382,7 +382,7 @@ MODULE_DEVICE_TABLE(of, xlnx_rtc_of_match);
|
||||
|
||||
static struct platform_driver xlnx_rtc_driver = {
|
||||
.probe = xlnx_rtc_probe,
|
||||
.remove_new = xlnx_rtc_remove,
|
||||
.remove = xlnx_rtc_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.pm = &xlnx_rtc_pm_ops,
|
||||
|
@ -31,6 +31,15 @@
|
||||
#define PM886_INT_WC BIT(1)
|
||||
#define PM886_INT_MASK_MODE BIT(2)
|
||||
|
||||
#define PM886_REG_RTC_CNT1 0xd1
|
||||
#define PM886_REG_RTC_CNT2 0xd2
|
||||
#define PM886_REG_RTC_CNT3 0xd3
|
||||
#define PM886_REG_RTC_CNT4 0xd4
|
||||
#define PM886_REG_RTC_SPARE1 0xea
|
||||
#define PM886_REG_RTC_SPARE2 0xeb
|
||||
#define PM886_REG_RTC_SPARE3 0xec
|
||||
#define PM886_REG_RTC_SPARE4 0xed
|
||||
#define PM886_REG_RTC_SPARE5 0xee
|
||||
#define PM886_REG_RTC_SPARE6 0xef
|
||||
|
||||
#define PM886_REG_BUCK_EN 0x08
|
||||
|
@ -56,6 +56,9 @@ struct m48t59_plat_data {
|
||||
void __iomem *ioaddr;
|
||||
/* offset to RTC registers, automatically set according to the type */
|
||||
unsigned int offset;
|
||||
|
||||
/* YY digits (in RTC) are offset, i.e. year is 1900 + yy_offset + YY */
|
||||
int yy_offset;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_RTC_M48T59_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user