RTC for 6.0
New drivers: - Microchip PolarFire - Nuvoton NCT3018Y - TI K3 RTC Subsystem: - Replace flush_scheduled_work() with flush_work() - Remove deprecated ida_simple_get()/ida_simple_remove() calls Drivers: - use simple i2c probe where possible - sun6i: add R329 support - zynqmp: add calibration support - vr41xx: remove unused driver -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmL2dLsACgkQY6TcMGxw OjK+ig//YDSXeTC5X1osHyeeWSVcbKieWPhd8QAMLG23OGQdFt0k/GZZb3Rb7vG2 3HZukr/LosicaZ67bKWtOPCnvfSGUtq7CMcj077eIYa2s1CpTgXtosWLJZKqAXgi Rjk4A4CK2W+iCeUCnwtlotbdWyi4MP767CysoPG/h49uENICmlCT8s9wk7lUwaDp 1uKANvZDWXumVz6wrGd6t9VQ1ToB+q5+bOqjEXExUH7KynqVx8FD/y8RblM5UsNq UnBhJq6eKN5uoKjyjb4I4zE5Wh96ciYiy8+b2OcteWXNe/y0lxXFOM5cjcqTPCUl P6xTi76JDbus3Fc5kNem1Cx9qiPM8hto0ll2P7ydY6FW4WOZtaxlCx1KmmddFzmy oeYDwdjmuKurAMVqme/jLciYtFrZtIhl/qzCJLwWb2qRqYZrlgh4Hd2v4Bq7ZP7Z 18L2g6zSEFv1ptooLiIqmUfqoGIiVYSvDEWotSGTHZ2mr9FmnFiqhG8PdQNVlB58 SuObtoewahp8V+b2N2+rU0WBqDBMJdur3dax9HyFdRZSQiIhatC9gu9PnB7pyimB pZ4z9QStwXM49MFRMwHS7yT0zexQbqOqJvVpz4hi3hCZKWrzWPysjvkaxsBolDKn kZNhwmx+NWG5o9KB5ct2E0FbXbyvN7zNbdQKFdp67u/ug0vb4iI= =hoJf -----END PGP SIGNATURE----- Merge tag 'rtc-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "New drivers: - Microchip PolarFire - Nuvoton NCT3018Y - TI K3 RTC Subsystem: - Replace flush_scheduled_work() with flush_work() - Remove deprecated ida_simple_get()/ida_simple_remove() calls Drivers: - use simple i2c probe where possible - sun6i: add R329 support - zynqmp: add calibration support - vr41xx: remove unused driver" * tag 'rtc-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (31 commits) rtc: spear: set range max rtc: rtc-cmos: Do not check ACPI_FADT_LOW_POWER_S0 rtc: zynqmp: initialize fract_tick rtc: Add NCT3018Y real time clock driver dt-bindings: rtc: nuvoton: add NCT3018Y Real Time Clock dt-bindings: rtc: nxp,pcf85063: Convert to DT schema dt-bindings: rtc: microcrystal,rv3032: Add missing type to 'trickle-voltage-millivolt' rtc: rx8025: fix 12/24 hour mode detection on RX-8035 rtc: cros-ec: Only warn once in .remove() about notifier_chain problems rtc: vr41xx: remove driver rtc: mpfs: remove 'pending' variable from mpfs_rtc_wakeup_irq_handler() rtc: rv8803: fix missing unlock on error in rv8803_set_time() rtc: zynqmp: Add calibration set and get support rtc: zynqmp: Updated calibration value dt-bindings: rtc: zynqmp: Add clock information rtc: sun6i: add support for R329 RTC rtc: Directly use ida_alloc()/free() rtc: Introduce ti-k3-rtc dt-bindings: rtc: Add TI K3 RTC description dt-bindings: rtc: qcom-pm8xxx-rtc: Update the maintainers section ...
This commit is contained in:
commit
3d076fec5a
|
@ -32,6 +32,7 @@ properties:
|
|||
- 11000
|
||||
|
||||
trickle-voltage-millivolt:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum:
|
||||
- 1750
|
||||
- 3000
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/nuvoton,nct3018y.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NUVOTON NCT3018Y Real Time Clock
|
||||
|
||||
allOf:
|
||||
- $ref: "rtc.yaml#"
|
||||
|
||||
maintainers:
|
||||
- Medad CChien <ctcchien@nuvoton.com>
|
||||
- Mia Lin <mimi05633@gmail.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: nuvoton,nct3018y
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
start-year: true
|
||||
|
||||
reset-source: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@6f {
|
||||
compatible = "nuvoton,nct3018y";
|
||||
reg = <0x6f>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -1,32 +0,0 @@
|
|||
* NXP PCF85063 Real Time Clock
|
||||
|
||||
Required properties:
|
||||
- compatible: Should one of contain:
|
||||
"nxp,pca85073a",
|
||||
"nxp,pcf85063",
|
||||
"nxp,pcf85063a",
|
||||
"nxp,pcf85063tp",
|
||||
"microcrystal,rv8263"
|
||||
- reg: I2C address for chip.
|
||||
|
||||
Optional property:
|
||||
- quartz-load-femtofarads: The capacitive load of the quartz(x-tal),
|
||||
expressed in femto Farad (fF). Valid values are 7000 and 12500.
|
||||
Default value (if no value is specified) is 7000fF.
|
||||
|
||||
Optional child node:
|
||||
- clock: Provide this if the square wave pin is used as boot-enabled fixed clock.
|
||||
|
||||
Example:
|
||||
|
||||
pcf85063: rtc@51 {
|
||||
compatible = "nxp,pcf85063";
|
||||
reg = <0x51>;
|
||||
quartz-load-femtofarads = <12500>;
|
||||
|
||||
clock {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <32768>;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/nxp,pcf85063.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: NXP PCF85063 Real Time Clock
|
||||
|
||||
maintainers:
|
||||
- Alexander Stein <alexander.stein@ew.tq-group.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- microcrystal,rv8263
|
||||
- nxp,pcf85063
|
||||
- nxp,pcf85063a
|
||||
- nxp,pcf85063tp
|
||||
- nxp,pca85073a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
clock-output-names:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
quartz-load-femtofarads:
|
||||
description:
|
||||
The capacitive load of the quartz(x-tal).
|
||||
enum: [7000, 12500]
|
||||
default: 7000
|
||||
|
||||
clock:
|
||||
$ref: /schemas/clock/fixed-clock.yaml
|
||||
description:
|
||||
Provide this if the square wave pin is used as boot-enabled
|
||||
fixed clock.
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- microcrystal,rv8263
|
||||
then:
|
||||
properties:
|
||||
quartz-load-femtofarads: false
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- nxp,pcf85063
|
||||
then:
|
||||
properties:
|
||||
quartz-load-femtofarads:
|
||||
const: 7000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@51 {
|
||||
compatible = "nxp,pcf85063a";
|
||||
reg = <0x51>;
|
||||
quartz-load-femtofarads = <12500>;
|
||||
|
||||
clock {
|
||||
compatible = "fixed-clock";
|
||||
#clock-cells = <0>;
|
||||
clock-frequency = <32768>;
|
||||
};
|
||||
};
|
||||
};
|
|
@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml#
|
|||
title: Qualcomm PM8xxx PMIC RTC device
|
||||
|
||||
maintainers:
|
||||
- Satya Priya <skakit@codeaurora.org>
|
||||
- Satya Priya <quic_c_skakit@quicinc.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
|
|
@ -14,6 +14,8 @@ For MediaTek PMIC wrapper bus bindings, see:
|
|||
Required properties:
|
||||
- compatible: Should be one of follows
|
||||
"mediatek,mt6323-rtc": for MT6323 PMIC
|
||||
"mediatek,mt6358-rtc": for MT6358 PMIC
|
||||
"mediatek,mt6366-rtc", "mediatek,mt6358-rtc": for MT6366 PMIC
|
||||
"mediatek,mt6397-rtc": for MT6397 PMIC
|
||||
|
||||
Example:
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/ti,k3-rtc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments K3 Real Time Clock
|
||||
|
||||
maintainers:
|
||||
- Nishanth Menon <nm@ti.com>
|
||||
|
||||
description: |
|
||||
This RTC appears in the AM62x family of SoCs.
|
||||
|
||||
allOf:
|
||||
- $ref: "rtc.yaml#"
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,am62-rtc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: VBUS Interface clock
|
||||
- description: 32k Clock source (external or internal).
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: vbus
|
||||
- const: osc32k
|
||||
|
||||
power-domains:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- clock-names
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
rtc@2b1f0000 {
|
||||
compatible = "ti,am62-rtc";
|
||||
reg = <0x2b1f0000 0x100>;
|
||||
interrupts = <GIC_SPI 100 IRQ_TYPE_LEVEL_HIGH>;
|
||||
power-domains = <&bar 0>;
|
||||
clocks = <&foo 0>, <&foo 1>;
|
||||
clock-names = "vbus", "osc32k";
|
||||
wakeup-source;
|
||||
};
|
|
@ -23,8 +23,15 @@ properties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: rtc
|
||||
|
||||
interrupts:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
|
@ -39,6 +46,7 @@ properties:
|
|||
minimum: 0x1
|
||||
maximum: 0x1FFFFF
|
||||
default: 0x198233
|
||||
deprecated: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
@ -61,5 +69,7 @@ examples:
|
|||
interrupts = <0 26 4>, <0 27 4>;
|
||||
interrupt-names = "alarm", "sec";
|
||||
calibration = <0x198233>;
|
||||
clock-names = "rtc";
|
||||
clocks = <&rtc_clk>;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -2486,11 +2486,13 @@ S: Supported
|
|||
F: Documentation/devicetree/bindings/*/*/*npcm*
|
||||
F: Documentation/devicetree/bindings/*/*npcm*
|
||||
F: Documentation/devicetree/bindings/arm/npcm/*
|
||||
F: Documentation/devicetree/bindings/rtc/nuvoton,nct3018y.yaml
|
||||
F: arch/arm/boot/dts/nuvoton-npcm*
|
||||
F: arch/arm/mach-npcm/
|
||||
F: arch/arm64/boot/dts/nuvoton/
|
||||
F: drivers/*/*npcm*
|
||||
F: drivers/*/*/*npcm*
|
||||
F: drivers/rtc/rtc-nct3018y.c
|
||||
F: include/dt-bindings/clock/nuvoton,npcm7xx-clock.h
|
||||
F: include/dt-bindings/clock/nuvoton,npcm845-clk.h
|
||||
|
||||
|
@ -17529,6 +17531,7 @@ F: drivers/char/hw_random/mpfs-rng.c
|
|||
F: drivers/clk/microchip/clk-mpfs.c
|
||||
F: drivers/mailbox/mailbox-mpfs.c
|
||||
F: drivers/pci/controller/pcie-microchip-host.c
|
||||
F: drivers/rtc/rtc-mpfs.c
|
||||
F: drivers/soc/microchip/
|
||||
F: drivers/spi/spi-microchip-core.c
|
||||
F: drivers/usb/musb/mpfs.c
|
||||
|
|
|
@ -383,6 +383,16 @@ config RTC_DRV_MAX77686
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max77686.
|
||||
|
||||
config RTC_DRV_NCT3018Y
|
||||
tristate "Nuvoton NCT3018Y"
|
||||
depends on OF
|
||||
help
|
||||
If you say yes here you get support for the Nuvoton NCT3018Y I2C RTC
|
||||
chip.
|
||||
|
||||
This driver can also be built as a module, if so, the module will be
|
||||
called "rtc-nct3018y".
|
||||
|
||||
config RTC_DRV_RK808
|
||||
tristate "Rockchip RK805/RK808/RK809/RK817/RK818 RTC"
|
||||
depends on MFD_RK808
|
||||
|
@ -1478,16 +1488,6 @@ config RTC_DRV_SUNPLUS
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-sunplus.
|
||||
|
||||
config RTC_DRV_VR41XX
|
||||
tristate "NEC VR41XX"
|
||||
depends on CPU_VR41XX || COMPILE_TEST
|
||||
help
|
||||
If you say Y here you will get access to the real time clock
|
||||
built into your NEC VR41XX CPU.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rtc-vr41xx.
|
||||
|
||||
config RTC_DRV_PL030
|
||||
tristate "ARM AMBA PL030 RTC"
|
||||
depends on ARM_AMBA
|
||||
|
@ -1929,6 +1929,17 @@ config RTC_DRV_ASPEED
|
|||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-aspeed".
|
||||
|
||||
config RTC_DRV_TI_K3
|
||||
tristate "TI K3 RTC"
|
||||
depends on ARCH_K3 || COMPILE_TEST
|
||||
select REGMAP_MMIO
|
||||
help
|
||||
If you say yes here you get support for the Texas Instruments's
|
||||
Real Time Clock for K3 architecture.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-ti-k3".
|
||||
|
||||
comment "HID Sensor RTC drivers"
|
||||
|
||||
config RTC_DRV_HID_SENSOR_TIME
|
||||
|
@ -1973,4 +1984,14 @@ config RTC_DRV_MSC313
|
|||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-msc313".
|
||||
|
||||
config RTC_DRV_POLARFIRE_SOC
|
||||
tristate "Microchip PolarFire SoC built-in RTC"
|
||||
depends on SOC_MICROCHIP_POLARFIRE
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
built-in RTC on Polarfire SoC.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-mpfs".
|
||||
|
||||
endif # RTC_CLASS
|
||||
|
|
|
@ -112,6 +112,7 @@ obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
|
|||
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
|
||||
obj-$(CONFIG_RTC_DRV_MXC_V2) += rtc-mxc_v2.o
|
||||
obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
|
||||
obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
|
||||
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
|
||||
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
|
||||
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
|
||||
|
@ -130,6 +131,7 @@ obj-$(CONFIG_RTC_DRV_PIC32) += rtc-pic32.o
|
|||
obj-$(CONFIG_RTC_DRV_PL030) += rtc-pl030.o
|
||||
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
|
||||
obj-$(CONFIG_RTC_DRV_PM8XXX) += rtc-pm8xxx.o
|
||||
obj-$(CONFIG_RTC_DRV_POLARFIRE_SOC) += rtc-mpfs.o
|
||||
obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
||||
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
||||
obj-$(CONFIG_RTC_DRV_R7301) += rtc-r7301.o
|
||||
|
@ -172,11 +174,11 @@ obj-$(CONFIG_RTC_DRV_SUNPLUS) += rtc-sunplus.o
|
|||
obj-$(CONFIG_RTC_DRV_SUNXI) += rtc-sunxi.o
|
||||
obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
|
||||
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
|
||||
obj-$(CONFIG_RTC_DRV_TI_K3) += rtc-ti-k3.o
|
||||
obj-$(CONFIG_RTC_DRV_TPS6586X) += rtc-tps6586x.o
|
||||
obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
|
||||
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
|
||||
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
|
||||
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
|
||||
obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
|
||||
obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o
|
||||
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
|
||||
|
|
|
@ -36,7 +36,7 @@ static void rtc_device_release(struct device *dev)
|
|||
|
||||
cancel_work_sync(&rtc->irqwork);
|
||||
|
||||
ida_simple_remove(&rtc_ida, rtc->id);
|
||||
ida_free(&rtc_ida, rtc->id);
|
||||
mutex_destroy(&rtc->ops_lock);
|
||||
kfree(rtc);
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ static int rtc_device_get_id(struct device *dev)
|
|||
}
|
||||
|
||||
if (id < 0)
|
||||
id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
|
||||
id = ida_alloc(&rtc_ida, GFP_KERNEL);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ struct rtc_device *devm_rtc_allocate_device(struct device *dev)
|
|||
|
||||
rtc = rtc_allocate_device();
|
||||
if (!rtc) {
|
||||
ida_simple_remove(&rtc_ida, id);
|
||||
ida_free(&rtc_ida, id);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
|
|
|
@ -96,7 +96,7 @@ static int clear_uie(struct rtc_device *rtc)
|
|||
}
|
||||
if (rtc->uie_task_active) {
|
||||
spin_unlock_irq(&rtc->irq_lock);
|
||||
flush_scheduled_work();
|
||||
flush_work(&rtc->uie_task);
|
||||
spin_lock_irq(&rtc->irq_lock);
|
||||
}
|
||||
rtc->uie_irq_active = 0;
|
||||
|
@ -566,9 +566,3 @@ void __init rtc_dev_init(void)
|
|||
if (err < 0)
|
||||
pr_err("failed to allocate char dev region\n");
|
||||
}
|
||||
|
||||
void __exit rtc_dev_exit(void)
|
||||
{
|
||||
if (rtc_devt)
|
||||
unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
|
||||
}
|
||||
|
|
|
@ -817,8 +817,7 @@ static const struct regmap_config abb5zes3_rtc_regmap_config = {
|
|||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int abb5zes3_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int abb5zes3_probe(struct i2c_client *client)
|
||||
{
|
||||
struct abb5zes3_rtc_data *data = NULL;
|
||||
struct device *dev = &client->dev;
|
||||
|
@ -945,7 +944,7 @@ static struct i2c_driver abb5zes3_driver = {
|
|||
.pm = &abb5zes3_rtc_pm_ops,
|
||||
.of_match_table = of_match_ptr(abb5zes3_dt_match),
|
||||
},
|
||||
.probe = abb5zes3_probe,
|
||||
.probe_new = abb5zes3_probe,
|
||||
.id_table = abb5zes3_id,
|
||||
};
|
||||
module_i2c_driver(abb5zes3_driver);
|
||||
|
|
|
@ -495,8 +495,7 @@ static void abeoz9_hwmon_register(struct device *dev,
|
|||
|
||||
#endif
|
||||
|
||||
static int abeoz9_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int abeoz9_probe(struct i2c_client *client)
|
||||
{
|
||||
struct abeoz9_rtc_data *data = NULL;
|
||||
struct device *dev = &client->dev;
|
||||
|
@ -580,7 +579,7 @@ static struct i2c_driver abeoz9_driver = {
|
|||
.name = "rtc-ab-eoz9",
|
||||
.of_match_table = of_match_ptr(abeoz9_dt_match),
|
||||
},
|
||||
.probe = abeoz9_probe,
|
||||
.probe_new = abeoz9_probe,
|
||||
.id_table = abeoz9_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -249,8 +249,7 @@ static void bq32k_sysfs_unregister(struct device *dev)
|
|||
device_remove_file(dev, &dev_attr_trickle_charge_bypass);
|
||||
}
|
||||
|
||||
static int bq32k_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int bq32k_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct rtc_device *rtc;
|
||||
|
@ -322,7 +321,7 @@ static struct i2c_driver bq32k_driver = {
|
|||
.name = "bq32k",
|
||||
.of_match_table = of_match_ptr(bq32k_of_match),
|
||||
},
|
||||
.probe = bq32k_probe,
|
||||
.probe_new = bq32k_probe,
|
||||
.remove = bq32k_remove,
|
||||
.id_table = bq32k_id,
|
||||
};
|
||||
|
|
|
@ -1260,9 +1260,6 @@ static void use_acpi_alarm_quirks(void)
|
|||
if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL)
|
||||
return;
|
||||
|
||||
if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
|
||||
return;
|
||||
|
||||
if (!is_hpet_enabled())
|
||||
return;
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#ifdef CONFIG_RTC_INTF_DEV
|
||||
|
||||
extern void __init rtc_dev_init(void);
|
||||
extern void __exit rtc_dev_exit(void);
|
||||
extern void rtc_dev_prepare(struct rtc_device *rtc);
|
||||
|
||||
#else
|
||||
|
@ -11,10 +10,6 @@ static inline void rtc_dev_init(void)
|
|||
{
|
||||
}
|
||||
|
||||
static inline void rtc_dev_exit(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void rtc_dev_prepare(struct rtc_device *rtc)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -375,10 +375,8 @@ static int cros_ec_rtc_remove(struct platform_device *pdev)
|
|||
ret = blocking_notifier_chain_unregister(
|
||||
&cros_ec_rtc->cros_ec->event_notifier,
|
||||
&cros_ec_rtc->notifier);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
dev_err(dev, "failed to unregister notifier\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -467,8 +467,7 @@ static const struct watchdog_ops ds1374_wdt_ops = {
|
|||
*
|
||||
*****************************************************************************
|
||||
*/
|
||||
static int ds1374_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ds1374_probe(struct i2c_client *client)
|
||||
{
|
||||
struct ds1374 *ds1374;
|
||||
int ret;
|
||||
|
@ -575,7 +574,7 @@ static struct i2c_driver ds1374_driver = {
|
|||
.of_match_table = of_match_ptr(ds1374_of_match),
|
||||
.pm = &ds1374_pm,
|
||||
},
|
||||
.probe = ds1374_probe,
|
||||
.probe_new = ds1374_probe,
|
||||
.remove = ds1374_remove,
|
||||
.id_table = ds1374_id,
|
||||
};
|
||||
|
|
|
@ -106,8 +106,7 @@ static const struct rtc_class_ops ds1672_rtc_ops = {
|
|||
.set_time = ds1672_set_time,
|
||||
};
|
||||
|
||||
static int ds1672_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ds1672_probe(struct i2c_client *client)
|
||||
{
|
||||
int err = 0;
|
||||
struct rtc_device *rtc;
|
||||
|
@ -150,7 +149,7 @@ static struct i2c_driver ds1672_driver = {
|
|||
.name = "rtc-ds1672",
|
||||
.of_match_table = of_match_ptr(ds1672_of_match),
|
||||
},
|
||||
.probe = &ds1672_probe,
|
||||
.probe_new = ds1672_probe,
|
||||
.id_table = ds1672_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -566,8 +566,7 @@ static const struct dev_pm_ops ds3232_pm_ops = {
|
|||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int ds3232_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ds3232_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
static const struct regmap_config config = {
|
||||
|
@ -604,7 +603,7 @@ static struct i2c_driver ds3232_driver = {
|
|||
.of_match_table = of_match_ptr(ds3232_of_match),
|
||||
.pm = &ds3232_pm_ops,
|
||||
},
|
||||
.probe = ds3232_i2c_probe,
|
||||
.probe_new = ds3232_i2c_probe,
|
||||
.id_table = ds3232_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -111,8 +111,7 @@ static const struct rtc_class_ops em3027_rtc_ops = {
|
|||
.set_time = em3027_set_time,
|
||||
};
|
||||
|
||||
static int em3027_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int em3027_probe(struct i2c_client *client)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
|
||||
|
@ -148,7 +147,7 @@ static struct i2c_driver em3027_driver = {
|
|||
.name = "rtc-em3027",
|
||||
.of_match_table = of_match_ptr(em3027_of_match),
|
||||
},
|
||||
.probe = &em3027_probe,
|
||||
.probe_new = em3027_probe,
|
||||
.id_table = em3027_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -340,8 +340,7 @@ static const struct rtc_class_ops fm3130_rtc_ops = {
|
|||
|
||||
static struct i2c_driver fm3130_driver;
|
||||
|
||||
static int fm3130_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int fm3130_probe(struct i2c_client *client)
|
||||
{
|
||||
struct fm3130 *fm3130;
|
||||
int err = -ENODEV;
|
||||
|
@ -518,7 +517,7 @@ static struct i2c_driver fm3130_driver = {
|
|||
.driver = {
|
||||
.name = "rtc-fm3130",
|
||||
},
|
||||
.probe = fm3130_probe,
|
||||
.probe_new = fm3130_probe,
|
||||
.id_table = fm3130_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -495,8 +495,7 @@ static int hym8563_resume(struct device *dev)
|
|||
|
||||
static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume);
|
||||
|
||||
static int hym8563_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int hym8563_probe(struct i2c_client *client)
|
||||
{
|
||||
struct hym8563 *hym8563;
|
||||
int ret;
|
||||
|
@ -572,7 +571,7 @@ static struct i2c_driver hym8563_driver = {
|
|||
.pm = &hym8563_pm_ops,
|
||||
.of_match_table = hym8563_dt_idtable,
|
||||
},
|
||||
.probe = hym8563_probe,
|
||||
.probe_new = hym8563_probe,
|
||||
.id_table = hym8563_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -232,8 +232,7 @@ static const struct rtc_class_ops isl12022_rtc_ops = {
|
|||
.set_time = isl12022_rtc_set_time,
|
||||
};
|
||||
|
||||
static int isl12022_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int isl12022_probe(struct i2c_client *client)
|
||||
{
|
||||
struct isl12022 *isl12022;
|
||||
|
||||
|
@ -275,7 +274,7 @@ static struct i2c_driver isl12022_driver = {
|
|||
.of_match_table = of_match_ptr(isl12022_dt_match),
|
||||
#endif
|
||||
},
|
||||
.probe = isl12022_probe,
|
||||
.probe_new = isl12022_probe,
|
||||
.id_table = isl12022_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -880,10 +880,14 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (client->irq > 0)
|
||||
if (client->irq > 0) {
|
||||
rc = isl1208_setup_irq(client, client->irq);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
} else {
|
||||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features);
|
||||
}
|
||||
|
||||
if (evdet_irq > 0 && evdet_irq != client->irq)
|
||||
rc = isl1208_setup_irq(client, evdet_irq);
|
||||
|
|
|
@ -197,8 +197,7 @@ static const struct rtc_class_ops max6900_rtc_ops = {
|
|||
.set_time = max6900_rtc_set_time,
|
||||
};
|
||||
|
||||
static int
|
||||
max6900_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
static int max6900_probe(struct i2c_client *client)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
|
||||
|
@ -225,7 +224,7 @@ static struct i2c_driver max6900_driver = {
|
|||
.driver = {
|
||||
.name = "rtc-max6900",
|
||||
},
|
||||
.probe = max6900_probe,
|
||||
.probe_new = max6900_probe,
|
||||
.id_table = max6900_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -21,13 +21,13 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
|
|||
unsigned long flags;
|
||||
unsigned char seconds;
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (i = 0; i < 100; i++) {
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
|
||||
/*
|
||||
* Check whether there is an update in progress during which the
|
||||
* readout is unspecified. The maximum update time is ~2ms. Poll
|
||||
* every msec for completion.
|
||||
* every 100 usec for completion.
|
||||
*
|
||||
* Store the second value before checking UIP so a long lasting
|
||||
* NMI which happens to hit after the UIP check cannot make
|
||||
|
@ -37,7 +37,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
|
|||
|
||||
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
mdelay(1);
|
||||
udelay(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ bool mc146818_avoid_UIP(void (*callback)(unsigned char seconds, void *param),
|
|||
*/
|
||||
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) {
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
mdelay(1);
|
||||
udelay(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Microchip MPFS RTC driver
|
||||
*
|
||||
* Copyright (c) 2021-2022 Microchip Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Daire McNamara <daire.mcnamara@microchip.com>
|
||||
* & Conor Dooley <conor.dooley@microchip.com>
|
||||
*/
|
||||
#include "linux/bits.h"
|
||||
#include "linux/iopoll.h"
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define CONTROL_REG 0x00
|
||||
#define MODE_REG 0x04
|
||||
#define PRESCALER_REG 0x08
|
||||
#define ALARM_LOWER_REG 0x0c
|
||||
#define ALARM_UPPER_REG 0x10
|
||||
#define COMPARE_LOWER_REG 0x14
|
||||
#define COMPARE_UPPER_REG 0x18
|
||||
#define DATETIME_LOWER_REG 0x20
|
||||
#define DATETIME_UPPER_REG 0x24
|
||||
|
||||
#define CONTROL_RUNNING_BIT BIT(0)
|
||||
#define CONTROL_START_BIT BIT(0)
|
||||
#define CONTROL_STOP_BIT BIT(1)
|
||||
#define CONTROL_ALARM_ON_BIT BIT(2)
|
||||
#define CONTROL_ALARM_OFF_BIT BIT(3)
|
||||
#define CONTROL_RESET_BIT BIT(4)
|
||||
#define CONTROL_UPLOAD_BIT BIT(5)
|
||||
#define CONTROL_DOWNLOAD_BIT BIT(6)
|
||||
#define CONTROL_MATCH_BIT BIT(7)
|
||||
#define CONTROL_WAKEUP_CLR_BIT BIT(8)
|
||||
#define CONTROL_WAKEUP_SET_BIT BIT(9)
|
||||
#define CONTROL_UPDATED_BIT BIT(10)
|
||||
|
||||
#define MODE_CLOCK_CALENDAR BIT(0)
|
||||
#define MODE_WAKE_EN BIT(1)
|
||||
#define MODE_WAKE_RESET BIT(2)
|
||||
#define MODE_WAKE_CONTINUE BIT(3)
|
||||
|
||||
#define MAX_PRESCALER_COUNT GENMASK(25, 0)
|
||||
#define DATETIME_UPPER_MASK GENMASK(29, 0)
|
||||
#define ALARM_UPPER_MASK GENMASK(10, 0)
|
||||
|
||||
#define UPLOAD_TIMEOUT_US 50
|
||||
|
||||
struct mpfs_rtc_dev {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static void mpfs_rtc_start(struct mpfs_rtc_dev *rtcdev)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(rtcdev->base + CONTROL_REG);
|
||||
ctrl &= ~CONTROL_STOP_BIT;
|
||||
ctrl |= CONTROL_START_BIT;
|
||||
writel(ctrl, rtcdev->base + CONTROL_REG);
|
||||
}
|
||||
|
||||
static void mpfs_rtc_clear_irq(struct mpfs_rtc_dev *rtcdev)
|
||||
{
|
||||
u32 val = readl(rtcdev->base + CONTROL_REG);
|
||||
|
||||
val &= ~(CONTROL_ALARM_ON_BIT | CONTROL_STOP_BIT);
|
||||
val |= CONTROL_ALARM_OFF_BIT;
|
||||
writel(val, rtcdev->base + CONTROL_REG);
|
||||
/*
|
||||
* Ensure that the posted write to the CONTROL_REG register completed before
|
||||
* returning from this function. Not doing this may result in the interrupt
|
||||
* only being cleared some time after this function returns.
|
||||
*/
|
||||
(void)readl(rtcdev->base + CONTROL_REG);
|
||||
}
|
||||
|
||||
static int mpfs_rtc_readtime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev);
|
||||
u64 time;
|
||||
|
||||
time = readl(rtcdev->base + DATETIME_LOWER_REG);
|
||||
time |= ((u64)readl(rtcdev->base + DATETIME_UPPER_REG) & DATETIME_UPPER_MASK) << 32;
|
||||
rtc_time64_to_tm(time, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_rtc_settime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev);
|
||||
u32 ctrl, prog;
|
||||
u64 time;
|
||||
int ret;
|
||||
|
||||
time = rtc_tm_to_time64(tm);
|
||||
|
||||
writel((u32)time, rtcdev->base + DATETIME_LOWER_REG);
|
||||
writel((u32)(time >> 32) & DATETIME_UPPER_MASK, rtcdev->base + DATETIME_UPPER_REG);
|
||||
|
||||
ctrl = readl(rtcdev->base + CONTROL_REG);
|
||||
ctrl &= ~CONTROL_STOP_BIT;
|
||||
ctrl |= CONTROL_UPLOAD_BIT;
|
||||
writel(ctrl, rtcdev->base + CONTROL_REG);
|
||||
|
||||
ret = read_poll_timeout(readl, prog, prog & CONTROL_UPLOAD_BIT, 0, UPLOAD_TIMEOUT_US,
|
||||
false, rtcdev->base + CONTROL_REG);
|
||||
if (ret) {
|
||||
dev_err(dev, "timed out uploading time to rtc");
|
||||
return ret;
|
||||
}
|
||||
mpfs_rtc_start(rtcdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev);
|
||||
u32 mode = readl(rtcdev->base + MODE_REG);
|
||||
u64 time;
|
||||
|
||||
alrm->enabled = mode & MODE_WAKE_EN;
|
||||
|
||||
time = (u64)readl(rtcdev->base + ALARM_LOWER_REG) << 32;
|
||||
time |= (readl(rtcdev->base + ALARM_UPPER_REG) & ALARM_UPPER_MASK);
|
||||
rtc_time64_to_tm(time, &alrm->time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev);
|
||||
u32 mode, ctrl;
|
||||
u64 time;
|
||||
|
||||
/* Disable the alarm before updating */
|
||||
ctrl = readl(rtcdev->base + CONTROL_REG);
|
||||
ctrl |= CONTROL_ALARM_OFF_BIT;
|
||||
writel(ctrl, rtcdev->base + CONTROL_REG);
|
||||
|
||||
time = rtc_tm_to_time64(&alrm->time);
|
||||
|
||||
writel((u32)time, rtcdev->base + ALARM_LOWER_REG);
|
||||
writel((u32)(time >> 32) & ALARM_UPPER_MASK, rtcdev->base + ALARM_UPPER_REG);
|
||||
|
||||
/* Bypass compare register in alarm mode */
|
||||
writel(GENMASK(31, 0), rtcdev->base + COMPARE_LOWER_REG);
|
||||
writel(GENMASK(29, 0), rtcdev->base + COMPARE_UPPER_REG);
|
||||
|
||||
/* Configure the RTC to enable the alarm. */
|
||||
ctrl = readl(rtcdev->base + CONTROL_REG);
|
||||
mode = readl(rtcdev->base + MODE_REG);
|
||||
if (alrm->enabled) {
|
||||
mode = MODE_WAKE_EN | MODE_WAKE_CONTINUE;
|
||||
/* Enable the alarm */
|
||||
ctrl &= ~CONTROL_ALARM_OFF_BIT;
|
||||
ctrl |= CONTROL_ALARM_ON_BIT;
|
||||
}
|
||||
ctrl &= ~CONTROL_STOP_BIT;
|
||||
ctrl |= CONTROL_START_BIT;
|
||||
writel(ctrl, rtcdev->base + CONTROL_REG);
|
||||
writel(mode, rtcdev->base + MODE_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpfs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev = dev_get_drvdata(dev);
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl(rtcdev->base + CONTROL_REG);
|
||||
ctrl &= ~(CONTROL_ALARM_ON_BIT | CONTROL_ALARM_OFF_BIT | CONTROL_STOP_BIT);
|
||||
|
||||
if (enabled)
|
||||
ctrl |= CONTROL_ALARM_ON_BIT;
|
||||
else
|
||||
ctrl |= CONTROL_ALARM_OFF_BIT;
|
||||
|
||||
writel(ctrl, rtcdev->base + CONTROL_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct clk *mpfs_rtc_init_clk(struct device *dev)
|
||||
{
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clk = devm_clk_get(dev, "rtc");
|
||||
if (IS_ERR(clk))
|
||||
return clk;
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
devm_add_action_or_reset(dev, (void (*) (void *))clk_disable_unprepare, clk);
|
||||
return clk;
|
||||
}
|
||||
|
||||
static irqreturn_t mpfs_rtc_wakeup_irq_handler(int irq, void *dev)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev = dev;
|
||||
|
||||
mpfs_rtc_clear_irq(rtcdev);
|
||||
|
||||
rtc_update_irq(rtcdev->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops mpfs_rtc_ops = {
|
||||
.read_time = mpfs_rtc_readtime,
|
||||
.set_time = mpfs_rtc_settime,
|
||||
.read_alarm = mpfs_rtc_readalarm,
|
||||
.set_alarm = mpfs_rtc_setalarm,
|
||||
.alarm_irq_enable = mpfs_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int mpfs_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mpfs_rtc_dev *rtcdev;
|
||||
struct clk *clk;
|
||||
u32 prescaler;
|
||||
int wakeup_irq, ret;
|
||||
|
||||
rtcdev = devm_kzalloc(&pdev->dev, sizeof(struct mpfs_rtc_dev), GFP_KERNEL);
|
||||
if (!rtcdev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, rtcdev);
|
||||
|
||||
rtcdev->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(rtcdev->rtc))
|
||||
return PTR_ERR(rtcdev->rtc);
|
||||
|
||||
rtcdev->rtc->ops = &mpfs_rtc_ops;
|
||||
|
||||
/* range is capped by alarm max, lower reg is 31:0 & upper is 10:0 */
|
||||
rtcdev->rtc->range_max = GENMASK_ULL(42, 0);
|
||||
|
||||
clk = mpfs_rtc_init_clk(&pdev->dev);
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
rtcdev->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(rtcdev->base)) {
|
||||
dev_dbg(&pdev->dev, "invalid ioremap resources\n");
|
||||
return PTR_ERR(rtcdev->base);
|
||||
}
|
||||
|
||||
wakeup_irq = platform_get_irq(pdev, 0);
|
||||
if (wakeup_irq <= 0) {
|
||||
dev_dbg(&pdev->dev, "could not get wakeup irq\n");
|
||||
return wakeup_irq;
|
||||
}
|
||||
ret = devm_request_irq(&pdev->dev, wakeup_irq, mpfs_rtc_wakeup_irq_handler, 0,
|
||||
dev_name(&pdev->dev), rtcdev);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "could not request wakeup irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* prescaler hardware adds 1 to reg value */
|
||||
prescaler = clk_get_rate(devm_clk_get(&pdev->dev, "rtcref")) - 1;
|
||||
|
||||
if (prescaler > MAX_PRESCALER_COUNT) {
|
||||
dev_dbg(&pdev->dev, "invalid prescaler %d\n", prescaler);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
writel(prescaler, rtcdev->base + PRESCALER_REG);
|
||||
dev_info(&pdev->dev, "prescaler set to: 0x%X \r\n", prescaler);
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
ret = dev_pm_set_wake_irq(&pdev->dev, wakeup_irq);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to enable irq wake\n");
|
||||
|
||||
return devm_rtc_register_device(rtcdev->rtc);
|
||||
}
|
||||
|
||||
static int mpfs_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id mpfs_rtc_of_match[] = {
|
||||
{ .compatible = "microchip,mpfs-rtc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, mpfs_rtc_of_match);
|
||||
|
||||
static struct platform_driver mpfs_rtc_driver = {
|
||||
.probe = mpfs_rtc_probe,
|
||||
.remove = mpfs_rtc_remove,
|
||||
.driver = {
|
||||
.name = "mpfs_rtc",
|
||||
.of_match_table = mpfs_rtc_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mpfs_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Real time clock for Microchip Polarfire SoC");
|
||||
MODULE_AUTHOR("Daire McNamara <daire.mcnamara@microchip.com>");
|
||||
MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,553 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2022 Nuvoton Technology Corporation
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define NCT3018Y_REG_SC 0x00 /* seconds */
|
||||
#define NCT3018Y_REG_SCA 0x01 /* alarm */
|
||||
#define NCT3018Y_REG_MN 0x02
|
||||
#define NCT3018Y_REG_MNA 0x03 /* alarm */
|
||||
#define NCT3018Y_REG_HR 0x04
|
||||
#define NCT3018Y_REG_HRA 0x05 /* alarm */
|
||||
#define NCT3018Y_REG_DW 0x06
|
||||
#define NCT3018Y_REG_DM 0x07
|
||||
#define NCT3018Y_REG_MO 0x08
|
||||
#define NCT3018Y_REG_YR 0x09
|
||||
#define NCT3018Y_REG_CTRL 0x0A /* timer control */
|
||||
#define NCT3018Y_REG_ST 0x0B /* status */
|
||||
#define NCT3018Y_REG_CLKO 0x0C /* clock out */
|
||||
|
||||
#define NCT3018Y_BIT_AF BIT(7)
|
||||
#define NCT3018Y_BIT_ST BIT(7)
|
||||
#define NCT3018Y_BIT_DM BIT(6)
|
||||
#define NCT3018Y_BIT_HF BIT(5)
|
||||
#define NCT3018Y_BIT_DSM BIT(4)
|
||||
#define NCT3018Y_BIT_AIE BIT(3)
|
||||
#define NCT3018Y_BIT_OFIE BIT(2)
|
||||
#define NCT3018Y_BIT_CIE BIT(1)
|
||||
#define NCT3018Y_BIT_TWO BIT(0)
|
||||
|
||||
#define NCT3018Y_REG_BAT_MASK 0x07
|
||||
#define NCT3018Y_REG_CLKO_F_MASK 0x03 /* frequenc mask */
|
||||
#define NCT3018Y_REG_CLKO_CKE 0x80 /* clock out enabled */
|
||||
|
||||
struct nct3018y {
|
||||
struct rtc_device *rtc;
|
||||
struct i2c_client *client;
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw clkout_hw;
|
||||
#endif
|
||||
};
|
||||
|
||||
static int nct3018y_set_alarm_mode(struct i2c_client *client, bool on)
|
||||
{
|
||||
int err, flags;
|
||||
|
||||
dev_dbg(&client->dev, "%s:on:%d\n", __func__, on);
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL);
|
||||
if (flags < 0) {
|
||||
dev_dbg(&client->dev,
|
||||
"Failed to read NCT3018Y_REG_CTRL\n");
|
||||
return flags;
|
||||
}
|
||||
|
||||
if (on)
|
||||
flags |= NCT3018Y_BIT_AIE;
|
||||
else
|
||||
flags &= ~NCT3018Y_BIT_AIE;
|
||||
|
||||
flags |= NCT3018Y_BIT_CIE;
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST);
|
||||
if (flags < 0) {
|
||||
dev_dbg(&client->dev,
|
||||
"Failed to read NCT3018Y_REG_ST\n");
|
||||
return flags;
|
||||
}
|
||||
|
||||
flags &= ~(NCT3018Y_BIT_AF);
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_ST\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nct3018y_get_alarm_mode(struct i2c_client *client, unsigned char *alarm_enable,
|
||||
unsigned char *alarm_flag)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if (alarm_enable) {
|
||||
dev_dbg(&client->dev, "%s:NCT3018Y_REG_CTRL\n", __func__);
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
*alarm_enable = flags & NCT3018Y_BIT_AIE;
|
||||
}
|
||||
|
||||
if (alarm_flag) {
|
||||
dev_dbg(&client->dev, "%s:NCT3018Y_REG_ST\n", __func__);
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
*alarm_flag = flags & NCT3018Y_BIT_AF;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "%s:alarm_enable:%x alarm_flag:%x\n",
|
||||
__func__, *alarm_enable, *alarm_flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t nct3018y_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct nct3018y *nct3018y = i2c_get_clientdata(dev_id);
|
||||
struct i2c_client *client = nct3018y->client;
|
||||
int err;
|
||||
unsigned char alarm_flag;
|
||||
unsigned char alarm_enable;
|
||||
|
||||
dev_dbg(&client->dev, "%s:irq:%d\n", __func__, irq);
|
||||
err = nct3018y_get_alarm_mode(nct3018y->client, &alarm_enable, &alarm_flag);
|
||||
if (err)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (alarm_flag) {
|
||||
dev_dbg(&client->dev, "%s:alarm flag:%x\n",
|
||||
__func__, alarm_flag);
|
||||
rtc_update_irq(nct3018y->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
nct3018y_set_alarm_mode(nct3018y->client, 0);
|
||||
dev_dbg(&client->dev, "%s:IRQ_HANDLED\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* In the routines that deal directly with the nct3018y hardware, we use
|
||||
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
|
||||
*/
|
||||
static int nct3018y_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
unsigned char buf[10];
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_ST, 1, buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (!buf[0]) {
|
||||
dev_dbg(&client->dev, " voltage <=1.7, date/time is not reliable.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SC, sizeof(buf), buf);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
tm->tm_sec = bcd2bin(buf[0] & 0x7F);
|
||||
tm->tm_min = bcd2bin(buf[2] & 0x7F);
|
||||
tm->tm_hour = bcd2bin(buf[4] & 0x3F);
|
||||
tm->tm_wday = buf[6] & 0x07;
|
||||
tm->tm_mday = bcd2bin(buf[7] & 0x3F);
|
||||
tm->tm_mon = bcd2bin(buf[8] & 0x1F) - 1;
|
||||
tm->tm_year = bcd2bin(buf[9]) + 100;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nct3018y_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
unsigned char buf[4] = {0};
|
||||
int err;
|
||||
|
||||
buf[0] = bin2bcd(tm->tm_sec);
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SC, buf[0]);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SC\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
buf[0] = bin2bcd(tm->tm_min);
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MN, buf[0]);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MN\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
buf[0] = bin2bcd(tm->tm_hour);
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HR, buf[0]);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HR\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
buf[0] = tm->tm_wday & 0x07;
|
||||
buf[1] = bin2bcd(tm->tm_mday);
|
||||
buf[2] = bin2bcd(tm->tm_mon + 1);
|
||||
buf[3] = bin2bcd(tm->tm_year - 100);
|
||||
err = i2c_smbus_write_i2c_block_data(client, NCT3018Y_REG_DW,
|
||||
sizeof(buf), buf);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write for day and mon and year\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int nct3018y_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
unsigned char buf[5];
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(client, NCT3018Y_REG_SCA,
|
||||
sizeof(buf), buf);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to read date\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev, "%s: raw data is sec=%02x, min=%02x hr=%02x\n",
|
||||
__func__, buf[0], buf[2], buf[4]);
|
||||
|
||||
tm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
|
||||
tm->time.tm_min = bcd2bin(buf[2] & 0x7F);
|
||||
tm->time.tm_hour = bcd2bin(buf[4] & 0x3F);
|
||||
|
||||
err = nct3018y_get_alarm_mode(client, &tm->enabled, &tm->pending);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
dev_dbg(&client->dev, "%s:s=%d m=%d, hr=%d, enabled=%d, pending=%d\n",
|
||||
__func__, tm->time.tm_sec, tm->time.tm_min,
|
||||
tm->time.tm_hour, tm->enabled, tm->pending);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nct3018y_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "%s, sec=%d, min=%d hour=%d tm->enabled:%d\n",
|
||||
__func__, tm->time.tm_sec, tm->time.tm_min, tm->time.tm_hour,
|
||||
tm->enabled);
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_SCA, bin2bcd(tm->time.tm_sec));
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_SCA\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_MNA, bin2bcd(tm->time.tm_min));
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_MNA\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_HRA, bin2bcd(tm->time.tm_hour));
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_HRA\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return nct3018y_set_alarm_mode(client, tm->enabled);
|
||||
}
|
||||
|
||||
static int nct3018y_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
dev_dbg(dev, "%s: alarm enable=%d\n", __func__, enabled);
|
||||
|
||||
return nct3018y_set_alarm_mode(to_i2c_client(dev), enabled);
|
||||
}
|
||||
|
||||
static int nct3018y_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int status, flags = 0;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_VL_READ:
|
||||
status = i2c_smbus_read_byte_data(client, NCT3018Y_REG_ST);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (!(status & NCT3018Y_REG_BAT_MASK))
|
||||
flags |= RTC_VL_DATA_INVALID;
|
||||
|
||||
return put_user(flags, (unsigned int __user *)arg);
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
/*
|
||||
* Handling of the clkout
|
||||
*/
|
||||
|
||||
#define clkout_hw_to_nct3018y(_hw) container_of(_hw, struct nct3018y, clkout_hw)
|
||||
|
||||
static const int clkout_rates[] = {
|
||||
32768,
|
||||
1024,
|
||||
32,
|
||||
1,
|
||||
};
|
||||
|
||||
static unsigned long nct3018y_clkout_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw);
|
||||
struct i2c_client *client = nct3018y->client;
|
||||
int flags;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO);
|
||||
if (flags < 0)
|
||||
return 0;
|
||||
|
||||
flags &= NCT3018Y_REG_CLKO_F_MASK;
|
||||
return clkout_rates[flags];
|
||||
}
|
||||
|
||||
static long nct3018y_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
||||
if (clkout_rates[i] <= rate)
|
||||
return clkout_rates[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nct3018y_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw);
|
||||
struct i2c_client *client = nct3018y->client;
|
||||
int i, flags;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
|
||||
if (clkout_rates[i] == rate) {
|
||||
flags &= ~NCT3018Y_REG_CLKO_F_MASK;
|
||||
flags |= i;
|
||||
return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nct3018y_clkout_control(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw);
|
||||
struct i2c_client *client = nct3018y->client;
|
||||
int flags;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
if (enable)
|
||||
flags |= NCT3018Y_REG_CLKO_CKE;
|
||||
else
|
||||
flags &= ~NCT3018Y_REG_CLKO_CKE;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, NCT3018Y_REG_CLKO, flags);
|
||||
}
|
||||
|
||||
static int nct3018y_clkout_prepare(struct clk_hw *hw)
|
||||
{
|
||||
return nct3018y_clkout_control(hw, 1);
|
||||
}
|
||||
|
||||
static void nct3018y_clkout_unprepare(struct clk_hw *hw)
|
||||
{
|
||||
nct3018y_clkout_control(hw, 0);
|
||||
}
|
||||
|
||||
static int nct3018y_clkout_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct nct3018y *nct3018y = clkout_hw_to_nct3018y(hw);
|
||||
struct i2c_client *client = nct3018y->client;
|
||||
int flags;
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CLKO);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
return flags & NCT3018Y_REG_CLKO_CKE;
|
||||
}
|
||||
|
||||
static const struct clk_ops nct3018y_clkout_ops = {
|
||||
.prepare = nct3018y_clkout_prepare,
|
||||
.unprepare = nct3018y_clkout_unprepare,
|
||||
.is_prepared = nct3018y_clkout_is_prepared,
|
||||
.recalc_rate = nct3018y_clkout_recalc_rate,
|
||||
.round_rate = nct3018y_clkout_round_rate,
|
||||
.set_rate = nct3018y_clkout_set_rate,
|
||||
};
|
||||
|
||||
static struct clk *nct3018y_clkout_register_clk(struct nct3018y *nct3018y)
|
||||
{
|
||||
struct i2c_client *client = nct3018y->client;
|
||||
struct device_node *node = client->dev.of_node;
|
||||
struct clk *clk;
|
||||
struct clk_init_data init;
|
||||
|
||||
init.name = "nct3018y-clkout";
|
||||
init.ops = &nct3018y_clkout_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
nct3018y->clkout_hw.init = &init;
|
||||
|
||||
/* optional override of the clockname */
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
||||
/* register the clock */
|
||||
clk = devm_clk_register(&client->dev, &nct3018y->clkout_hw);
|
||||
|
||||
if (!IS_ERR(clk))
|
||||
of_clk_add_provider(node, of_clk_src_simple_get, clk);
|
||||
|
||||
return clk;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct rtc_class_ops nct3018y_rtc_ops = {
|
||||
.read_time = nct3018y_rtc_read_time,
|
||||
.set_time = nct3018y_rtc_set_time,
|
||||
.read_alarm = nct3018y_rtc_read_alarm,
|
||||
.set_alarm = nct3018y_rtc_set_alarm,
|
||||
.alarm_irq_enable = nct3018y_irq_enable,
|
||||
.ioctl = nct3018y_ioctl,
|
||||
};
|
||||
|
||||
static int nct3018y_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct nct3018y *nct3018y;
|
||||
int err, flags;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_BYTE |
|
||||
I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
nct3018y = devm_kzalloc(&client->dev, sizeof(struct nct3018y),
|
||||
GFP_KERNEL);
|
||||
if (!nct3018y)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, nct3018y);
|
||||
nct3018y->client = client;
|
||||
device_set_wakeup_capable(&client->dev, 1);
|
||||
|
||||
flags = i2c_smbus_read_byte_data(client, NCT3018Y_REG_CTRL);
|
||||
if (flags < 0) {
|
||||
dev_dbg(&client->dev, "%s: read error\n", __func__);
|
||||
return flags;
|
||||
} else if (flags & NCT3018Y_BIT_TWO) {
|
||||
dev_dbg(&client->dev, "%s: NCT3018Y_BIT_TWO is set\n", __func__);
|
||||
}
|
||||
|
||||
flags = NCT3018Y_BIT_TWO;
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_CTRL, flags);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "Unable to write NCT3018Y_REG_CTRL\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
flags = 0;
|
||||
err = i2c_smbus_write_byte_data(client, NCT3018Y_REG_ST, flags);
|
||||
if (err < 0) {
|
||||
dev_dbg(&client->dev, "%s: write error\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
nct3018y->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(nct3018y->rtc))
|
||||
return PTR_ERR(nct3018y->rtc);
|
||||
|
||||
nct3018y->rtc->ops = &nct3018y_rtc_ops;
|
||||
nct3018y->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
nct3018y->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
if (client->irq > 0) {
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, nct3018y_irq,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
|
||||
"nct3018y", client);
|
||||
if (err) {
|
||||
dev_dbg(&client->dev, "unable to request IRQ %d\n", client->irq);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, nct3018y->rtc->features);
|
||||
clear_bit(RTC_FEATURE_ALARM, nct3018y->rtc->features);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
/* register clk in common clk framework */
|
||||
nct3018y_clkout_register_clk(nct3018y);
|
||||
#endif
|
||||
|
||||
return devm_rtc_register_device(nct3018y->rtc);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id nct3018y_id[] = {
|
||||
{ "nct3018y", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, nct3018y_id);
|
||||
|
||||
static const struct of_device_id nct3018y_of_match[] = {
|
||||
{ .compatible = "nuvoton,nct3018y" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nct3018y_of_match);
|
||||
|
||||
static struct i2c_driver nct3018y_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-nct3018y",
|
||||
.of_match_table = of_match_ptr(nct3018y_of_match),
|
||||
},
|
||||
.probe = nct3018y_probe,
|
||||
.id_table = nct3018y_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(nct3018y_driver);
|
||||
|
||||
MODULE_AUTHOR("Medad CChien <ctcchien@nuvoton.com>");
|
||||
MODULE_AUTHOR("Mia Lin <mimi05633@gmail.com>");
|
||||
MODULE_DESCRIPTION("Nuvoton NCT3018Y RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -390,8 +390,7 @@ static const struct regmap_config regmap_config = {
|
|||
.max_register = 0x13,
|
||||
};
|
||||
|
||||
static int pcf8523_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pcf8523_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pcf8523 *pcf8523;
|
||||
struct rtc_device *rtc;
|
||||
|
@ -485,7 +484,7 @@ static struct i2c_driver pcf8523_driver = {
|
|||
.name = "rtc-pcf8523",
|
||||
.of_match_table = pcf8523_of_match,
|
||||
},
|
||||
.probe = pcf8523_probe,
|
||||
.probe_new = pcf8523_probe,
|
||||
.id_table = pcf8523_id,
|
||||
};
|
||||
module_i2c_driver(pcf8523_driver);
|
||||
|
|
|
@ -350,8 +350,7 @@ static const struct pcf85x63_config pcf_85363_config = {
|
|||
.num_nvram = 2
|
||||
};
|
||||
|
||||
static int pcf85363_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pcf85363_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pcf85363 *pcf85363;
|
||||
const struct pcf85x63_config *config = &pcf_85363_config;
|
||||
|
@ -436,7 +435,7 @@ static struct i2c_driver pcf85363_driver = {
|
|||
.name = "pcf85363",
|
||||
.of_match_table = of_match_ptr(dev_ids),
|
||||
},
|
||||
.probe = pcf85363_probe,
|
||||
.probe_new = pcf85363_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(pcf85363_driver);
|
||||
|
|
|
@ -509,8 +509,7 @@ static const struct rtc_class_ops pcf8563_rtc_ops = {
|
|||
.alarm_irq_enable = pcf8563_irq_enable,
|
||||
};
|
||||
|
||||
static int pcf8563_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pcf8563_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pcf8563 *pcf8563;
|
||||
int err;
|
||||
|
@ -606,7 +605,7 @@ static struct i2c_driver pcf8563_driver = {
|
|||
.name = "rtc-pcf8563",
|
||||
.of_match_table = of_match_ptr(pcf8563_of_match),
|
||||
},
|
||||
.probe = pcf8563_probe,
|
||||
.probe_new = pcf8563_probe,
|
||||
.id_table = pcf8563_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -275,8 +275,7 @@ static const struct rtc_class_ops pcf8583_rtc_ops = {
|
|||
.set_time = pcf8583_rtc_set_time,
|
||||
};
|
||||
|
||||
static int pcf8583_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int pcf8583_probe(struct i2c_client *client)
|
||||
{
|
||||
struct pcf8583 *pcf8583;
|
||||
|
||||
|
@ -307,7 +306,7 @@ static struct i2c_driver pcf8583_driver = {
|
|||
.driver = {
|
||||
.name = "pcf8583",
|
||||
},
|
||||
.probe = pcf8583_probe,
|
||||
.probe_new = pcf8583_probe,
|
||||
.id_table = pcf8583_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -784,8 +784,7 @@ static const struct regmap_config config = {
|
|||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int rv3029_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int rv3029_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK |
|
||||
|
@ -819,7 +818,7 @@ static struct i2c_driver rv3029_driver = {
|
|||
.name = "rv3029",
|
||||
.of_match_table = of_match_ptr(rv3029_of_match),
|
||||
},
|
||||
.probe = rv3029_i2c_probe,
|
||||
.probe_new = rv3029_i2c_probe,
|
||||
.id_table = rv3029_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -33,6 +34,7 @@
|
|||
#define RV8803_EXT 0x0D
|
||||
#define RV8803_FLAG 0x0E
|
||||
#define RV8803_CTRL 0x0F
|
||||
#define RV8803_OSC_OFFSET 0x2C
|
||||
|
||||
#define RV8803_EXT_WADA BIT(6)
|
||||
|
||||
|
@ -49,12 +51,15 @@
|
|||
#define RV8803_CTRL_TIE BIT(4)
|
||||
#define RV8803_CTRL_UIE BIT(5)
|
||||
|
||||
#define RX8803_CTRL_CSEL GENMASK(7, 6)
|
||||
|
||||
#define RX8900_BACKUP_CTRL 0x18
|
||||
#define RX8900_FLAG_SWOFF BIT(2)
|
||||
#define RX8900_FLAG_VDETOFF BIT(3)
|
||||
|
||||
enum rv8803_type {
|
||||
rv_8803,
|
||||
rx_8803,
|
||||
rx_8804,
|
||||
rx_8900
|
||||
};
|
||||
|
@ -64,6 +69,7 @@ struct rv8803_data {
|
|||
struct rtc_device *rtc;
|
||||
struct mutex flags_lock;
|
||||
u8 ctrl;
|
||||
u8 backup;
|
||||
enum rv8803_type type;
|
||||
};
|
||||
|
||||
|
@ -136,6 +142,44 @@ static int rv8803_write_regs(const struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int rv8803_regs_init(struct rv8803_data *rv8803)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = rv8803_write_reg(rv8803->client, RV8803_OSC_OFFSET, 0x00);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rv8803_write_reg(rv8803->client, RV8803_CTRL,
|
||||
FIELD_PREP(RX8803_CTRL_CSEL, 1)); /* 2s */
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = rv8803_write_regs(rv8803->client, RV8803_ALARM_MIN, 3,
|
||||
(u8[]){ 0, 0, 0 });
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return rv8803_write_reg(rv8803->client, RV8803_RAM, 0x00);
|
||||
}
|
||||
|
||||
static int rv8803_regs_configure(struct rv8803_data *rv8803);
|
||||
|
||||
static int rv8803_regs_reset(struct rv8803_data *rv8803)
|
||||
{
|
||||
/*
|
||||
* The RV-8803 resets all registers to POR defaults after voltage-loss,
|
||||
* the Epson RTCs don't, so we manually reset the remainder here.
|
||||
*/
|
||||
if (rv8803->type == rx_8803 || rv8803->type == rx_8900) {
|
||||
int ret = rv8803_regs_init(rv8803);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return rv8803_regs_configure(rv8803);
|
||||
}
|
||||
|
||||
static irqreturn_t rv8803_handle_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *client = dev_id;
|
||||
|
@ -269,6 +313,14 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
|
|||
return flags;
|
||||
}
|
||||
|
||||
if (flags & RV8803_FLAG_V2F) {
|
||||
ret = rv8803_regs_reset(rv8803);
|
||||
if (ret) {
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = rv8803_write_reg(rv8803->client, RV8803_FLAG,
|
||||
flags & ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F));
|
||||
|
||||
|
@ -498,18 +550,32 @@ static int rx8900_trickle_charger_init(struct rv8803_data *rv8803)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
flags = ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF) & (u8)err;
|
||||
|
||||
if (of_property_read_bool(node, "epson,vdet-disable"))
|
||||
flags |= RX8900_FLAG_VDETOFF;
|
||||
|
||||
if (of_property_read_bool(node, "trickle-diode-disable"))
|
||||
flags |= RX8900_FLAG_SWOFF;
|
||||
flags = (u8)err;
|
||||
flags &= ~(RX8900_FLAG_VDETOFF | RX8900_FLAG_SWOFF);
|
||||
flags |= rv8803->backup;
|
||||
|
||||
return i2c_smbus_write_byte_data(rv8803->client, RX8900_BACKUP_CTRL,
|
||||
flags);
|
||||
}
|
||||
|
||||
/* configure registers with values different than the Power-On reset defaults */
|
||||
static int rv8803_regs_configure(struct rv8803_data *rv8803)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rx8900_trickle_charger_init(rv8803);
|
||||
if (err) {
|
||||
dev_err(&rv8803->client->dev, "failed to init charger\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv8803_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
|
@ -576,16 +642,16 @@ static int rv8803_probe(struct i2c_client *client,
|
|||
if (!client->irq)
|
||||
clear_bit(RTC_FEATURE_ALARM, rv8803->rtc->features);
|
||||
|
||||
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
|
||||
if (of_property_read_bool(client->dev.of_node, "epson,vdet-disable"))
|
||||
rv8803->backup |= RX8900_FLAG_VDETOFF;
|
||||
|
||||
if (of_property_read_bool(client->dev.of_node, "trickle-diode-disable"))
|
||||
rv8803->backup |= RX8900_FLAG_SWOFF;
|
||||
|
||||
err = rv8803_regs_configure(rv8803);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = rx8900_trickle_charger_init(rv8803);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "failed to init charger\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
rv8803->rtc->ops = &rv8803_rtc_ops;
|
||||
rv8803->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rv8803->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
@ -603,7 +669,7 @@ static int rv8803_probe(struct i2c_client *client,
|
|||
static const struct i2c_device_id rv8803_id[] = {
|
||||
{ "rv8803", rv_8803 },
|
||||
{ "rv8804", rx_8804 },
|
||||
{ "rx8803", rv_8803 },
|
||||
{ "rx8803", rx_8803 },
|
||||
{ "rx8900", rx_8900 },
|
||||
{ }
|
||||
};
|
||||
|
@ -616,7 +682,7 @@ static const __maybe_unused struct of_device_id rv8803_of_match[] = {
|
|||
},
|
||||
{
|
||||
.compatible = "epson,rx8803",
|
||||
.data = (void *)rv_8803
|
||||
.data = (void *)rx_8803
|
||||
},
|
||||
{
|
||||
.compatible = "epson,rx8804",
|
||||
|
|
|
@ -419,8 +419,7 @@ static struct regmap_config regmap_i2c_config = {
|
|||
.read_flag_mask = 0x80,
|
||||
};
|
||||
|
||||
static int rx6110_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int rx6110_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
struct rx6110_data *rx6110;
|
||||
|
@ -464,7 +463,7 @@ static struct i2c_driver rx6110_i2c_driver = {
|
|||
.name = RX6110_DRIVER_NAME,
|
||||
.acpi_match_table = rx6110_i2c_acpi_match,
|
||||
},
|
||||
.probe = rx6110_i2c_probe,
|
||||
.probe_new = rx6110_i2c_probe,
|
||||
.id_table = rx6110_i2c_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
#define RX8025_BIT_CTRL2_XST BIT(5)
|
||||
#define RX8025_BIT_CTRL2_VDET BIT(6)
|
||||
|
||||
#define RX8035_BIT_HOUR_1224 BIT(7)
|
||||
|
||||
/* Clock precision adjustment */
|
||||
#define RX8025_ADJ_RESOLUTION 3050 /* in ppb */
|
||||
#define RX8025_ADJ_DATA_MAX 62
|
||||
|
@ -78,6 +80,7 @@ struct rx8025_data {
|
|||
struct rtc_device *rtc;
|
||||
enum rx_model model;
|
||||
u8 ctrl1;
|
||||
int is_24;
|
||||
};
|
||||
|
||||
static s32 rx8025_read_reg(const struct i2c_client *client, u8 number)
|
||||
|
@ -226,7 +229,7 @@ static int rx8025_get_time(struct device *dev, struct rtc_time *dt)
|
|||
|
||||
dt->tm_sec = bcd2bin(date[RX8025_REG_SEC] & 0x7f);
|
||||
dt->tm_min = bcd2bin(date[RX8025_REG_MIN] & 0x7f);
|
||||
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
|
||||
if (rx8025->is_24)
|
||||
dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x3f);
|
||||
else
|
||||
dt->tm_hour = bcd2bin(date[RX8025_REG_HOUR] & 0x1f) % 12
|
||||
|
@ -254,7 +257,7 @@ static int rx8025_set_time(struct device *dev, struct rtc_time *dt)
|
|||
*/
|
||||
date[RX8025_REG_SEC] = bin2bcd(dt->tm_sec);
|
||||
date[RX8025_REG_MIN] = bin2bcd(dt->tm_min);
|
||||
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
|
||||
if (rx8025->is_24)
|
||||
date[RX8025_REG_HOUR] = bin2bcd(dt->tm_hour);
|
||||
else
|
||||
date[RX8025_REG_HOUR] = (dt->tm_hour >= 12 ? 0x20 : 0)
|
||||
|
@ -279,6 +282,7 @@ static int rx8025_init_client(struct i2c_client *client)
|
|||
struct rx8025_data *rx8025 = i2c_get_clientdata(client);
|
||||
u8 ctrl[2], ctrl2;
|
||||
int need_clear = 0;
|
||||
int hour_reg;
|
||||
int err;
|
||||
|
||||
err = rx8025_read_regs(client, RX8025_REG_CTRL1, 2, ctrl);
|
||||
|
@ -303,6 +307,16 @@ static int rx8025_init_client(struct i2c_client *client)
|
|||
|
||||
err = rx8025_write_reg(client, RX8025_REG_CTRL2, ctrl2);
|
||||
}
|
||||
|
||||
if (rx8025->model == model_rx_8035) {
|
||||
/* In RX-8035, 12/24 flag is in the hour register */
|
||||
hour_reg = rx8025_read_reg(client, RX8025_REG_HOUR);
|
||||
if (hour_reg < 0)
|
||||
return hour_reg;
|
||||
rx8025->is_24 = (hour_reg & RX8035_BIT_HOUR_1224);
|
||||
} else {
|
||||
rx8025->is_24 = (ctrl[1] & RX8025_BIT_CTRL1_1224);
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
@ -329,7 +343,7 @@ static int rx8025_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||
/* Hardware alarms precision is 1 minute! */
|
||||
t->time.tm_sec = 0;
|
||||
t->time.tm_min = bcd2bin(ald[0] & 0x7f);
|
||||
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
|
||||
if (rx8025->is_24)
|
||||
t->time.tm_hour = bcd2bin(ald[1] & 0x3f);
|
||||
else
|
||||
t->time.tm_hour = bcd2bin(ald[1] & 0x1f) % 12
|
||||
|
@ -350,7 +364,7 @@ static int rx8025_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||
int err;
|
||||
|
||||
ald[0] = bin2bcd(t->time.tm_min);
|
||||
if (rx8025->ctrl1 & RX8025_BIT_CTRL1_1224)
|
||||
if (rx8025->is_24)
|
||||
ald[1] = bin2bcd(t->time.tm_hour);
|
||||
else
|
||||
ald[1] = (t->time.tm_hour >= 12 ? 0x20 : 0)
|
||||
|
|
|
@ -248,8 +248,7 @@ static const struct rx85x1_config rx8571_config = {
|
|||
.num_nvram = 2
|
||||
};
|
||||
|
||||
static int rx8581_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int rx8581_probe(struct i2c_client *client)
|
||||
{
|
||||
struct rx8581 *rx8581;
|
||||
const struct rx85x1_config *config = &rx8581_config;
|
||||
|
@ -326,7 +325,7 @@ static struct i2c_driver rx8581_driver = {
|
|||
.name = "rtc-rx8581",
|
||||
.of_match_table = of_match_ptr(rx8581_of_match),
|
||||
},
|
||||
.probe = rx8581_probe,
|
||||
.probe_new = rx8581_probe,
|
||||
.id_table = rx8581_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -420,8 +420,7 @@ static const struct rtc_class_ops s35390a_rtc_ops = {
|
|||
.ioctl = s35390a_rtc_ioctl,
|
||||
};
|
||||
|
||||
static int s35390a_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int s35390a_probe(struct i2c_client *client)
|
||||
{
|
||||
int err, err_read;
|
||||
unsigned int i;
|
||||
|
@ -502,7 +501,7 @@ static struct i2c_driver s35390a_driver = {
|
|||
.name = "rtc-s35390a",
|
||||
.of_match_table = of_match_ptr(s35390a_of_match),
|
||||
},
|
||||
.probe = s35390a_probe,
|
||||
.probe_new = s35390a_probe,
|
||||
.id_table = s35390a_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -163,8 +163,7 @@ static const struct regmap_config regmap_config = {
|
|||
.max_register = 0x11,
|
||||
};
|
||||
|
||||
static int sd3078_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int sd3078_probe(struct i2c_client *client)
|
||||
{
|
||||
int ret;
|
||||
struct sd3078 *sd3078;
|
||||
|
@ -218,7 +217,7 @@ static struct i2c_driver sd3078_driver = {
|
|||
.name = "sd3078",
|
||||
.of_match_table = of_match_ptr(rtc_dt_match),
|
||||
},
|
||||
.probe = sd3078_probe,
|
||||
.probe_new = sd3078_probe,
|
||||
.id_table = sd3078_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -388,7 +388,7 @@ static int spear_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
config->rtc->ops = &spear_rtc_ops;
|
||||
config->rtc->range_min = RTC_TIMESTAMP_BEGIN_0000;
|
||||
config->rtc->range_min = RTC_TIMESTAMP_END_9999;
|
||||
config->rtc->range_max = RTC_TIMESTAMP_END_9999;
|
||||
|
||||
status = devm_rtc_register_device(config->rtc);
|
||||
if (status)
|
||||
|
|
|
@ -875,6 +875,8 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
|
|||
{ .compatible = "allwinner,sun50i-h6-rtc" },
|
||||
{ .compatible = "allwinner,sun50i-h616-rtc",
|
||||
.data = (void *)RTC_LINEAR_DAY },
|
||||
{ .compatible = "allwinner,sun50i-r329-rtc",
|
||||
.data = (void *)RTC_LINEAR_DAY },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
|
||||
|
|
|
@ -0,0 +1,680 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Texas Instruments K3 RTC driver
|
||||
*
|
||||
* Copyright (C) 2021-2022 Texas Instruments Incorporated - https://www.ti.com/
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
/* Registers */
|
||||
#define REG_K3RTC_S_CNT_LSW 0x08
|
||||
#define REG_K3RTC_S_CNT_MSW 0x0c
|
||||
#define REG_K3RTC_COMP 0x10
|
||||
#define REG_K3RTC_ON_OFF_S_CNT_LSW 0x20
|
||||
#define REG_K3RTC_ON_OFF_S_CNT_MSW 0x24
|
||||
#define REG_K3RTC_SCRATCH0 0x30
|
||||
#define REG_K3RTC_SCRATCH7 0x4c
|
||||
#define REG_K3RTC_GENERAL_CTL 0x50
|
||||
#define REG_K3RTC_IRQSTATUS_RAW_SYS 0x54
|
||||
#define REG_K3RTC_IRQSTATUS_SYS 0x58
|
||||
#define REG_K3RTC_IRQENABLE_SET_SYS 0x5c
|
||||
#define REG_K3RTC_IRQENABLE_CLR_SYS 0x60
|
||||
#define REG_K3RTC_SYNCPEND 0x68
|
||||
#define REG_K3RTC_KICK0 0x70
|
||||
#define REG_K3RTC_KICK1 0x74
|
||||
|
||||
/* Freeze when lsw is read and unfreeze when msw is read */
|
||||
#define K3RTC_CNT_FMODE_S_CNT_VALUE (0x2 << 24)
|
||||
|
||||
/* Magic values for lock/unlock */
|
||||
#define K3RTC_KICK0_UNLOCK_VALUE 0x83e70b13
|
||||
#define K3RTC_KICK1_UNLOCK_VALUE 0x95a4f1e0
|
||||
|
||||
/* Multiplier for ppb conversions */
|
||||
#define K3RTC_PPB_MULT (1000000000LL)
|
||||
/* Min and max values supported with 'offset' interface (swapped sign) */
|
||||
#define K3RTC_MIN_OFFSET (-277761)
|
||||
#define K3RTC_MAX_OFFSET (277778)
|
||||
|
||||
/**
|
||||
* struct ti_k3_rtc_soc_data - Private of compatible data for ti-k3-rtc
|
||||
* @unlock_irq_erratum: Has erratum for unlock infinite IRQs (erratum i2327)
|
||||
*/
|
||||
struct ti_k3_rtc_soc_data {
|
||||
const bool unlock_irq_erratum;
|
||||
};
|
||||
|
||||
static const struct regmap_config ti_k3_rtc_regmap_config = {
|
||||
.name = "peripheral-registers",
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.max_register = REG_K3RTC_KICK1,
|
||||
};
|
||||
|
||||
enum ti_k3_rtc_fields {
|
||||
K3RTC_KICK0,
|
||||
K3RTC_KICK1,
|
||||
K3RTC_S_CNT_LSW,
|
||||
K3RTC_S_CNT_MSW,
|
||||
K3RTC_O32K_OSC_DEP_EN,
|
||||
K3RTC_UNLOCK,
|
||||
K3RTC_CNT_FMODE,
|
||||
K3RTC_PEND,
|
||||
K3RTC_RELOAD_FROM_BBD,
|
||||
K3RTC_COMP,
|
||||
|
||||
K3RTC_ALM_S_CNT_LSW,
|
||||
K3RTC_ALM_S_CNT_MSW,
|
||||
K3RTC_IRQ_STATUS_RAW,
|
||||
K3RTC_IRQ_STATUS,
|
||||
K3RTC_IRQ_ENABLE_SET,
|
||||
K3RTC_IRQ_ENABLE_CLR,
|
||||
|
||||
K3RTC_IRQ_STATUS_ALT,
|
||||
K3RTC_IRQ_ENABLE_CLR_ALT,
|
||||
|
||||
K3_RTC_MAX_FIELDS
|
||||
};
|
||||
|
||||
static const struct reg_field ti_rtc_reg_fields[] = {
|
||||
[K3RTC_KICK0] = REG_FIELD(REG_K3RTC_KICK0, 0, 31),
|
||||
[K3RTC_KICK1] = REG_FIELD(REG_K3RTC_KICK1, 0, 31),
|
||||
[K3RTC_S_CNT_LSW] = REG_FIELD(REG_K3RTC_S_CNT_LSW, 0, 31),
|
||||
[K3RTC_S_CNT_MSW] = REG_FIELD(REG_K3RTC_S_CNT_MSW, 0, 15),
|
||||
[K3RTC_O32K_OSC_DEP_EN] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 21, 21),
|
||||
[K3RTC_UNLOCK] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 23, 23),
|
||||
[K3RTC_CNT_FMODE] = REG_FIELD(REG_K3RTC_GENERAL_CTL, 24, 25),
|
||||
[K3RTC_PEND] = REG_FIELD(REG_K3RTC_SYNCPEND, 0, 1),
|
||||
[K3RTC_RELOAD_FROM_BBD] = REG_FIELD(REG_K3RTC_SYNCPEND, 31, 31),
|
||||
[K3RTC_COMP] = REG_FIELD(REG_K3RTC_COMP, 0, 31),
|
||||
|
||||
/* We use on to off as alarm trigger */
|
||||
[K3RTC_ALM_S_CNT_LSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_LSW, 0, 31),
|
||||
[K3RTC_ALM_S_CNT_MSW] = REG_FIELD(REG_K3RTC_ON_OFF_S_CNT_MSW, 0, 15),
|
||||
[K3RTC_IRQ_STATUS_RAW] = REG_FIELD(REG_K3RTC_IRQSTATUS_RAW_SYS, 0, 0),
|
||||
[K3RTC_IRQ_STATUS] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 0, 0),
|
||||
[K3RTC_IRQ_ENABLE_SET] = REG_FIELD(REG_K3RTC_IRQENABLE_SET_SYS, 0, 0),
|
||||
[K3RTC_IRQ_ENABLE_CLR] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 0, 0),
|
||||
/* Off to on is alternate */
|
||||
[K3RTC_IRQ_STATUS_ALT] = REG_FIELD(REG_K3RTC_IRQSTATUS_SYS, 1, 1),
|
||||
[K3RTC_IRQ_ENABLE_CLR_ALT] = REG_FIELD(REG_K3RTC_IRQENABLE_CLR_SYS, 1, 1),
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ti_k3_rtc - Private data for ti-k3-rtc
|
||||
* @irq: IRQ
|
||||
* @sync_timeout_us: data sync timeout period in uSec
|
||||
* @rate_32k: 32k clock rate in Hz
|
||||
* @rtc_dev: rtc device
|
||||
* @regmap: rtc mmio regmap
|
||||
* @r_fields: rtc register fields
|
||||
* @soc: SoC compatible match data
|
||||
*/
|
||||
struct ti_k3_rtc {
|
||||
unsigned int irq;
|
||||
u32 sync_timeout_us;
|
||||
unsigned long rate_32k;
|
||||
struct rtc_device *rtc_dev;
|
||||
struct regmap *regmap;
|
||||
struct regmap_field *r_fields[K3_RTC_MAX_FIELDS];
|
||||
const struct ti_k3_rtc_soc_data *soc;
|
||||
};
|
||||
|
||||
static int k3rtc_field_read(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f)
|
||||
{
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
ret = regmap_field_read(priv->r_fields[f], &val);
|
||||
/*
|
||||
* We shouldn't be seeing regmap fail on us for mmio reads
|
||||
* This is possible if clock context fails, but that isn't the case for us
|
||||
*/
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return ret;
|
||||
return val;
|
||||
}
|
||||
|
||||
static void k3rtc_field_write(struct ti_k3_rtc *priv, enum ti_k3_rtc_fields f, u32 val)
|
||||
{
|
||||
regmap_field_write(priv->r_fields[f], val);
|
||||
}
|
||||
|
||||
/**
|
||||
* k3rtc_fence - Ensure a register sync took place between the two domains
|
||||
* @priv: pointer to priv data
|
||||
*
|
||||
* Return: 0 if the sync took place, else returns -ETIMEDOUT
|
||||
*/
|
||||
static int k3rtc_fence(struct ti_k3_rtc *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_PEND], ret,
|
||||
!ret, 2, priv->sync_timeout_us);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int k3rtc_check_unlocked(struct ti_k3_rtc *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = k3rtc_field_read(priv, K3RTC_UNLOCK);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return (ret) ? 0 : 1;
|
||||
}
|
||||
|
||||
static int k3rtc_unlock_rtc(struct ti_k3_rtc *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = k3rtc_check_unlocked(priv);
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
k3rtc_field_write(priv, K3RTC_KICK0, K3RTC_KICK0_UNLOCK_VALUE);
|
||||
k3rtc_field_write(priv, K3RTC_KICK1, K3RTC_KICK1_UNLOCK_VALUE);
|
||||
|
||||
/* Skip fence since we are going to check the unlock bit as fence */
|
||||
ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_UNLOCK], ret,
|
||||
!ret, 2, priv->sync_timeout_us);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int k3rtc_configure(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
|
||||
/*
|
||||
* HWBUG: The compare state machine is broken if the RTC module
|
||||
* is NOT unlocked in under one second of boot - which is pretty long
|
||||
* time from the perspective of Linux driver (module load, u-boot
|
||||
* shell all can take much longer than this.
|
||||
*
|
||||
* In such occurrence, it is assumed that the RTC module is unusable
|
||||
*/
|
||||
if (priv->soc->unlock_irq_erratum) {
|
||||
ret = k3rtc_check_unlocked(priv);
|
||||
/* If there is an error OR if we are locked, return error */
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
HW_ERR "Erratum i2327 unlock QUIRK! Cannot operate!!\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
} else {
|
||||
/* May need to explicitly unlock first time */
|
||||
ret = k3rtc_unlock_rtc(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to unlock(%d)!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable Shadow register sync on 32k clock boundary */
|
||||
k3rtc_field_write(priv, K3RTC_O32K_OSC_DEP_EN, 0x1);
|
||||
|
||||
/*
|
||||
* Wait at least clock sync time before proceeding further programming.
|
||||
* This ensures that the 32k based sync is active.
|
||||
*/
|
||||
usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 5);
|
||||
|
||||
/* We need to ensure fence here to make sure sync here */
|
||||
ret = k3rtc_fence(priv);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed fence osc_dep enable(%d) - is 32k clk working?!\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* FMODE setting: Reading lower seconds will freeze value on higher
|
||||
* seconds. This also implies that we must *ALWAYS* read lower seconds
|
||||
* prior to reading higher seconds
|
||||
*/
|
||||
k3rtc_field_write(priv, K3RTC_CNT_FMODE, K3RTC_CNT_FMODE_S_CNT_VALUE);
|
||||
|
||||
/* Clear any spurious IRQ sources if any */
|
||||
k3rtc_field_write(priv, K3RTC_IRQ_STATUS_ALT, 0x1);
|
||||
k3rtc_field_write(priv, K3RTC_IRQ_STATUS, 0x1);
|
||||
/* Disable all IRQs */
|
||||
k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR_ALT, 0x1);
|
||||
k3rtc_field_write(priv, K3RTC_IRQ_ENABLE_CLR, 0x1);
|
||||
|
||||
/* And.. Let us Sync the writes in */
|
||||
return k3rtc_fence(priv);
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
u32 seconds_lo, seconds_hi;
|
||||
|
||||
seconds_lo = k3rtc_field_read(priv, K3RTC_S_CNT_LSW);
|
||||
seconds_hi = k3rtc_field_read(priv, K3RTC_S_CNT_MSW);
|
||||
|
||||
rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
time64_t seconds;
|
||||
|
||||
seconds = rtc_tm_to_time64(tm);
|
||||
|
||||
/*
|
||||
* Read operation on LSW will freeze the RTC, so to update
|
||||
* the time, we cannot use field operations. Just write since the
|
||||
* reserved bits are ignored.
|
||||
*/
|
||||
regmap_write(priv->regmap, REG_K3RTC_S_CNT_LSW, seconds);
|
||||
regmap_write(priv->regmap, REG_K3RTC_S_CNT_MSW, seconds >> 32);
|
||||
|
||||
return k3rtc_fence(priv);
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
u32 offset = enabled ? K3RTC_IRQ_ENABLE_SET : K3RTC_IRQ_ENABLE_CLR;
|
||||
|
||||
reg = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET);
|
||||
if ((enabled && reg) || (!enabled && !reg))
|
||||
return 0;
|
||||
|
||||
k3rtc_field_write(priv, offset, 0x1);
|
||||
|
||||
/*
|
||||
* Ensure the write sync is through - NOTE: it should be OK to have
|
||||
* ISR to fire as we are checking sync (which should be done in a 32k
|
||||
* cycle or so).
|
||||
*/
|
||||
return k3rtc_fence(priv);
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
u32 seconds_lo, seconds_hi;
|
||||
|
||||
seconds_lo = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_LSW);
|
||||
seconds_hi = k3rtc_field_read(priv, K3RTC_ALM_S_CNT_MSW);
|
||||
|
||||
rtc_time64_to_tm((((time64_t)seconds_hi) << 32) | (time64_t)seconds_lo, &alarm->time);
|
||||
|
||||
alarm->enabled = k3rtc_field_read(priv, K3RTC_IRQ_ENABLE_SET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
time64_t seconds;
|
||||
int ret;
|
||||
|
||||
seconds = rtc_tm_to_time64(&alarm->time);
|
||||
|
||||
k3rtc_field_write(priv, K3RTC_ALM_S_CNT_LSW, seconds);
|
||||
k3rtc_field_write(priv, K3RTC_ALM_S_CNT_MSW, (seconds >> 32));
|
||||
|
||||
/* Make sure the alarm time is synced in */
|
||||
ret = k3rtc_fence(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to fence(%d)! Potential config issue?\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Alarm IRQ enable will do a sync */
|
||||
return ti_k3_rtc_alarm_irq_enable(dev, alarm->enabled);
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
u32 ticks_per_hr = priv->rate_32k * 3600;
|
||||
int comp;
|
||||
s64 tmp;
|
||||
|
||||
comp = k3rtc_field_read(priv, K3RTC_COMP);
|
||||
|
||||
/* Convert from RTC calibration register format to ppb format */
|
||||
tmp = comp * (s64)K3RTC_PPB_MULT;
|
||||
if (tmp < 0)
|
||||
tmp -= ticks_per_hr / 2LL;
|
||||
else
|
||||
tmp += ticks_per_hr / 2LL;
|
||||
tmp = div_s64(tmp, ticks_per_hr);
|
||||
|
||||
/* Offset value operates in negative way, so swap sign */
|
||||
*offset = (long)-tmp;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
u32 ticks_per_hr = priv->rate_32k * 3600;
|
||||
int comp;
|
||||
s64 tmp;
|
||||
|
||||
/* Make sure offset value is within supported range */
|
||||
if (offset < K3RTC_MIN_OFFSET || offset > K3RTC_MAX_OFFSET)
|
||||
return -ERANGE;
|
||||
|
||||
/* Convert from ppb format to RTC calibration register format */
|
||||
tmp = offset * (s64)ticks_per_hr;
|
||||
if (tmp < 0)
|
||||
tmp -= K3RTC_PPB_MULT / 2LL;
|
||||
else
|
||||
tmp += K3RTC_PPB_MULT / 2LL;
|
||||
tmp = div_s64(tmp, K3RTC_PPB_MULT);
|
||||
|
||||
/* Offset value operates in negative way, so swap sign */
|
||||
comp = (int)-tmp;
|
||||
|
||||
k3rtc_field_write(priv, K3RTC_COMP, comp);
|
||||
|
||||
return k3rtc_fence(priv);
|
||||
}
|
||||
|
||||
static irqreturn_t ti_k3_rtc_interrupt(s32 irq, void *dev_id)
|
||||
{
|
||||
struct device *dev = dev_id;
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* IRQ assertion can be very fast, however, the IRQ Status clear
|
||||
* de-assert depends on 32k clock edge in the 32k domain
|
||||
* If we clear the status prior to the first 32k clock edge,
|
||||
* the status bit is cleared, but the IRQ stays re-asserted.
|
||||
*
|
||||
* To prevent this condition, we need to wait for clock sync time.
|
||||
* We can either do that by polling the 32k observability signal for
|
||||
* a toggle OR we could just sleep and let the processor do other
|
||||
* stuff.
|
||||
*/
|
||||
usleep_range(priv->sync_timeout_us, priv->sync_timeout_us + 2);
|
||||
|
||||
/* Lets make sure that this is a valid interrupt */
|
||||
reg = k3rtc_field_read(priv, K3RTC_IRQ_STATUS);
|
||||
|
||||
if (!reg) {
|
||||
u32 raw = k3rtc_field_read(priv, K3RTC_IRQ_STATUS_RAW);
|
||||
|
||||
dev_err(dev,
|
||||
HW_ERR
|
||||
"Erratum i2327/IRQ trig: status: 0x%08x / 0x%08x\n", reg, raw);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write 1 to clear status reg
|
||||
* We cannot use a field operation here due to a potential race between
|
||||
* 32k domain and vbus domain.
|
||||
*/
|
||||
regmap_write(priv->regmap, REG_K3RTC_IRQSTATUS_SYS, 0x1);
|
||||
|
||||
/* Sync the write in */
|
||||
ret = k3rtc_fence(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to fence irq status clr(%d)!\n", ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Force the 32k status to be reloaded back in to ensure status is
|
||||
* reflected back correctly.
|
||||
*/
|
||||
k3rtc_field_write(priv, K3RTC_RELOAD_FROM_BBD, 0x1);
|
||||
|
||||
/* Ensure the write sync is through */
|
||||
ret = k3rtc_fence(priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to fence reload from bbd(%d)!\n", ret);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Now we ensure that the status bit is cleared */
|
||||
ret = regmap_field_read_poll_timeout(priv->r_fields[K3RTC_IRQ_STATUS],
|
||||
ret, !ret, 2, priv->sync_timeout_us);
|
||||
if (ret) {
|
||||
dev_err(dev, "Time out waiting for status clear\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* Notify RTC core on event */
|
||||
rtc_update_irq(priv->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ti_k3_rtc_ops = {
|
||||
.read_time = ti_k3_rtc_read_time,
|
||||
.set_time = ti_k3_rtc_set_time,
|
||||
.read_alarm = ti_k3_rtc_read_alarm,
|
||||
.set_alarm = ti_k3_rtc_set_alarm,
|
||||
.read_offset = ti_k3_rtc_read_offset,
|
||||
.set_offset = ti_k3_rtc_set_offset,
|
||||
.alarm_irq_enable = ti_k3_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int ti_k3_rtc_scratch_read(void *priv_data, unsigned int offset,
|
||||
void *val, size_t bytes)
|
||||
{
|
||||
struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data;
|
||||
|
||||
return regmap_bulk_read(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4);
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_scratch_write(void *priv_data, unsigned int offset,
|
||||
void *val, size_t bytes)
|
||||
{
|
||||
struct ti_k3_rtc *priv = (struct ti_k3_rtc *)priv_data;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_write(priv->regmap, REG_K3RTC_SCRATCH0 + offset, val, bytes / 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return k3rtc_fence(priv);
|
||||
}
|
||||
|
||||
static struct nvmem_config ti_k3_rtc_nvmem_config = {
|
||||
.name = "ti_k3_rtc_scratch",
|
||||
.word_size = 4,
|
||||
.stride = 4,
|
||||
.size = REG_K3RTC_SCRATCH7 - REG_K3RTC_SCRATCH0 + 4,
|
||||
.reg_read = ti_k3_rtc_scratch_read,
|
||||
.reg_write = ti_k3_rtc_scratch_write,
|
||||
};
|
||||
|
||||
static int k3rtc_get_32kclk(struct device *dev, struct ti_k3_rtc *priv)
|
||||
{
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
|
||||
clk = devm_clk_get(dev, "osc32k");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = devm_add_action_or_reset(dev, (void (*)(void *))clk_disable_unprepare, clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->rate_32k = clk_get_rate(clk);
|
||||
|
||||
/* Make sure we are exact 32k clock. Else, try to compensate delay */
|
||||
if (priv->rate_32k != 32768)
|
||||
dev_warn(dev, "Clock rate %ld is not 32768! Could misbehave!\n",
|
||||
priv->rate_32k);
|
||||
|
||||
/*
|
||||
* Sync timeout should be two 32k clk sync cycles = ~61uS. We double
|
||||
* it to comprehend intermediate bus segment and cpu frequency
|
||||
* deltas
|
||||
*/
|
||||
priv->sync_timeout_us = (u32)(DIV_ROUND_UP_ULL(1000000, priv->rate_32k) * 4);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int k3rtc_get_vbusclk(struct device *dev, struct ti_k3_rtc *priv)
|
||||
{
|
||||
int ret;
|
||||
struct clk *clk;
|
||||
|
||||
/* Note: VBUS isn't a context clock, it is needed for hardware operation */
|
||||
clk = devm_clk_get(dev, "vbus");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_add_action_or_reset(dev, (void (*)(void *))clk_disable_unprepare, clk);
|
||||
}
|
||||
|
||||
static int ti_k3_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ti_k3_rtc *priv;
|
||||
void __iomem *rtc_base;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(struct ti_k3_rtc), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
rtc_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(rtc_base))
|
||||
return PTR_ERR(rtc_base);
|
||||
|
||||
priv->regmap = devm_regmap_init_mmio(dev, rtc_base, &ti_k3_rtc_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
ret = devm_regmap_field_bulk_alloc(dev, priv->regmap, priv->r_fields,
|
||||
ti_rtc_reg_fields, K3_RTC_MAX_FIELDS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = k3rtc_get_32kclk(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = k3rtc_get_vbusclk(dev, priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
priv->irq = (unsigned int)ret;
|
||||
|
||||
priv->rtc_dev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(priv->rtc_dev))
|
||||
return PTR_ERR(priv->rtc_dev);
|
||||
|
||||
priv->soc = of_device_get_match_data(dev);
|
||||
|
||||
priv->rtc_dev->ops = &ti_k3_rtc_ops;
|
||||
priv->rtc_dev->range_max = (1ULL << 48) - 1; /* 48Bit seconds */
|
||||
ti_k3_rtc_nvmem_config.priv = priv;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, priv->irq, NULL,
|
||||
ti_k3_rtc_interrupt,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
dev_name(dev), dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Could not request IRQ: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
ret = k3rtc_configure(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (device_property_present(dev, "wakeup-source"))
|
||||
device_init_wakeup(dev, true);
|
||||
else
|
||||
device_set_wakeup_capable(dev, true);
|
||||
|
||||
ret = devm_rtc_register_device(priv->rtc_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_rtc_nvmem_register(priv->rtc_dev, &ti_k3_rtc_nvmem_config);
|
||||
}
|
||||
|
||||
static const struct ti_k3_rtc_soc_data ti_k3_am62_data = {
|
||||
.unlock_irq_erratum = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id ti_k3_rtc_of_match_table[] = {
|
||||
{.compatible = "ti,am62-rtc", .data = &ti_k3_am62_data},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_k3_rtc_of_match_table);
|
||||
|
||||
static int __maybe_unused ti_k3_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(priv->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ti_k3_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct ti_k3_rtc *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(priv->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ti_k3_rtc_pm_ops, ti_k3_rtc_suspend, ti_k3_rtc_resume);
|
||||
|
||||
static struct platform_driver ti_k3_rtc_driver = {
|
||||
.probe = ti_k3_rtc_probe,
|
||||
.driver = {
|
||||
.name = "rtc-ti-k3",
|
||||
.of_match_table = ti_k3_rtc_of_match_table,
|
||||
.pm = &ti_k3_rtc_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ti_k3_rtc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("TI K3 RTC driver");
|
||||
MODULE_AUTHOR("Nishanth Menon");
|
|
@ -1,363 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Driver for NEC VR4100 series Real Time Clock unit.
|
||||
*
|
||||
* Copyright (C) 2003-2008 Yoichi Yuasa <yuasa@linux-mips.org>
|
||||
*/
|
||||
#include <linux/compat.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
#include <asm/div64.h>
|
||||
|
||||
MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
|
||||
MODULE_DESCRIPTION("NEC VR4100 series RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
/* RTC 1 registers */
|
||||
#define ETIMELREG 0x00
|
||||
#define ETIMEMREG 0x02
|
||||
#define ETIMEHREG 0x04
|
||||
/* RFU */
|
||||
#define ECMPLREG 0x08
|
||||
#define ECMPMREG 0x0a
|
||||
#define ECMPHREG 0x0c
|
||||
/* RFU */
|
||||
#define RTCL1LREG 0x10
|
||||
#define RTCL1HREG 0x12
|
||||
#define RTCL1CNTLREG 0x14
|
||||
#define RTCL1CNTHREG 0x16
|
||||
#define RTCL2LREG 0x18
|
||||
#define RTCL2HREG 0x1a
|
||||
#define RTCL2CNTLREG 0x1c
|
||||
#define RTCL2CNTHREG 0x1e
|
||||
|
||||
/* RTC 2 registers */
|
||||
#define TCLKLREG 0x00
|
||||
#define TCLKHREG 0x02
|
||||
#define TCLKCNTLREG 0x04
|
||||
#define TCLKCNTHREG 0x06
|
||||
/* RFU */
|
||||
#define RTCINTREG 0x1e
|
||||
#define TCLOCK_INT 0x08
|
||||
#define RTCLONG2_INT 0x04
|
||||
#define RTCLONG1_INT 0x02
|
||||
#define ELAPSEDTIME_INT 0x01
|
||||
|
||||
#define RTC_FREQUENCY 32768
|
||||
#define MAX_PERIODIC_RATE 6553
|
||||
|
||||
static void __iomem *rtc1_base;
|
||||
static void __iomem *rtc2_base;
|
||||
|
||||
#define rtc1_read(offset) readw(rtc1_base + (offset))
|
||||
#define rtc1_write(offset, value) writew((value), rtc1_base + (offset))
|
||||
|
||||
#define rtc2_read(offset) readw(rtc2_base + (offset))
|
||||
#define rtc2_write(offset, value) writew((value), rtc2_base + (offset))
|
||||
|
||||
/* 32-bit compat for ioctls that nobody else uses */
|
||||
#define RTC_EPOCH_READ32 _IOR('p', 0x0d, __u32)
|
||||
|
||||
static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */
|
||||
|
||||
static DEFINE_SPINLOCK(rtc_lock);
|
||||
static char rtc_name[] = "RTC";
|
||||
static unsigned long periodic_count;
|
||||
static unsigned int alarm_enabled;
|
||||
static int aie_irq;
|
||||
static int pie_irq;
|
||||
|
||||
static inline time64_t read_elapsed_second(void)
|
||||
{
|
||||
|
||||
unsigned long first_low, first_mid, first_high;
|
||||
|
||||
unsigned long second_low, second_mid, second_high;
|
||||
|
||||
do {
|
||||
first_low = rtc1_read(ETIMELREG);
|
||||
first_mid = rtc1_read(ETIMEMREG);
|
||||
first_high = rtc1_read(ETIMEHREG);
|
||||
second_low = rtc1_read(ETIMELREG);
|
||||
second_mid = rtc1_read(ETIMEMREG);
|
||||
second_high = rtc1_read(ETIMEHREG);
|
||||
} while (first_low != second_low || first_mid != second_mid ||
|
||||
first_high != second_high);
|
||||
|
||||
return ((u64)first_high << 17) | (first_mid << 1) | (first_low >> 15);
|
||||
}
|
||||
|
||||
static inline void write_elapsed_second(time64_t sec)
|
||||
{
|
||||
spin_lock_irq(&rtc_lock);
|
||||
|
||||
rtc1_write(ETIMELREG, (uint16_t)(sec << 15));
|
||||
rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1));
|
||||
rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17));
|
||||
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
}
|
||||
|
||||
static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time)
|
||||
{
|
||||
time64_t epoch_sec, elapsed_sec;
|
||||
|
||||
epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0);
|
||||
elapsed_sec = read_elapsed_second();
|
||||
|
||||
rtc_time64_to_tm(epoch_sec + elapsed_sec, time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time)
|
||||
{
|
||||
time64_t epoch_sec, current_sec;
|
||||
|
||||
epoch_sec = mktime64(epoch, 1, 1, 0, 0, 0);
|
||||
current_sec = rtc_tm_to_time64(time);
|
||||
|
||||
write_elapsed_second(current_sec - epoch_sec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
unsigned long low, mid, high;
|
||||
struct rtc_time *time = &wkalrm->time;
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
|
||||
low = rtc1_read(ECMPLREG);
|
||||
mid = rtc1_read(ECMPMREG);
|
||||
high = rtc1_read(ECMPHREG);
|
||||
wkalrm->enabled = alarm_enabled;
|
||||
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
rtc_time64_to_tm((high << 17) | (mid << 1) | (low >> 15), time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
time64_t alarm_sec;
|
||||
|
||||
alarm_sec = rtc_tm_to_time64(&wkalrm->time);
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
|
||||
if (alarm_enabled)
|
||||
disable_irq(aie_irq);
|
||||
|
||||
rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15));
|
||||
rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1));
|
||||
rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17));
|
||||
|
||||
if (wkalrm->enabled)
|
||||
enable_irq(aie_irq);
|
||||
|
||||
alarm_enabled = wkalrm->enabled;
|
||||
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case RTC_EPOCH_READ:
|
||||
return put_user(epoch, (unsigned long __user *)arg);
|
||||
#ifdef CONFIG_64BIT
|
||||
case RTC_EPOCH_READ32:
|
||||
return put_user(epoch, (unsigned int __user *)arg);
|
||||
#endif
|
||||
case RTC_EPOCH_SET:
|
||||
/* Doesn't support before 1900 */
|
||||
if (arg < 1900)
|
||||
return -EINVAL;
|
||||
epoch = arg;
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vr41xx_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
spin_lock_irq(&rtc_lock);
|
||||
if (enabled) {
|
||||
if (!alarm_enabled) {
|
||||
enable_irq(aie_irq);
|
||||
alarm_enabled = 1;
|
||||
}
|
||||
} else {
|
||||
if (alarm_enabled) {
|
||||
disable_irq(aie_irq);
|
||||
alarm_enabled = 0;
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device *)dev_id;
|
||||
struct rtc_device *rtc = platform_get_drvdata(pdev);
|
||||
|
||||
rtc2_write(RTCINTREG, ELAPSEDTIME_INT);
|
||||
|
||||
rtc_update_irq(rtc, 1, RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct platform_device *pdev = (struct platform_device *)dev_id;
|
||||
struct rtc_device *rtc = platform_get_drvdata(pdev);
|
||||
unsigned long count = periodic_count;
|
||||
|
||||
rtc2_write(RTCINTREG, RTCLONG1_INT);
|
||||
|
||||
rtc1_write(RTCL1LREG, count);
|
||||
rtc1_write(RTCL1HREG, count >> 16);
|
||||
|
||||
rtc_update_irq(rtc, 1, RTC_PF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops vr41xx_rtc_ops = {
|
||||
.ioctl = vr41xx_rtc_ioctl,
|
||||
.read_time = vr41xx_rtc_read_time,
|
||||
.set_time = vr41xx_rtc_set_time,
|
||||
.read_alarm = vr41xx_rtc_read_alarm,
|
||||
.set_alarm = vr41xx_rtc_set_alarm,
|
||||
.alarm_irq_enable = vr41xx_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct rtc_device *rtc;
|
||||
int retval;
|
||||
|
||||
if (pdev->num_resources != 4)
|
||||
return -EBUSY;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
|
||||
rtc1_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!rtc1_base)
|
||||
return -EBUSY;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
if (!res) {
|
||||
retval = -EBUSY;
|
||||
goto err_rtc1_iounmap;
|
||||
}
|
||||
|
||||
rtc2_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
|
||||
if (!rtc2_base) {
|
||||
retval = -EBUSY;
|
||||
goto err_rtc1_iounmap;
|
||||
}
|
||||
|
||||
rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(rtc)) {
|
||||
retval = PTR_ERR(rtc);
|
||||
goto err_iounmap_all;
|
||||
}
|
||||
|
||||
rtc->ops = &vr41xx_rtc_ops;
|
||||
|
||||
/* 48-bit counter at 32.768 kHz */
|
||||
rtc->range_max = (1ULL << 33) - 1;
|
||||
rtc->max_user_freq = MAX_PERIODIC_RATE;
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
|
||||
rtc1_write(ECMPLREG, 0);
|
||||
rtc1_write(ECMPMREG, 0);
|
||||
rtc1_write(ECMPHREG, 0);
|
||||
rtc1_write(RTCL1LREG, 0);
|
||||
rtc1_write(RTCL1HREG, 0);
|
||||
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
aie_irq = platform_get_irq(pdev, 0);
|
||||
if (aie_irq <= 0) {
|
||||
retval = -EBUSY;
|
||||
goto err_iounmap_all;
|
||||
}
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, aie_irq, elapsedtime_interrupt, 0,
|
||||
"elapsed_time", pdev);
|
||||
if (retval < 0)
|
||||
goto err_iounmap_all;
|
||||
|
||||
pie_irq = platform_get_irq(pdev, 1);
|
||||
if (pie_irq <= 0) {
|
||||
retval = -EBUSY;
|
||||
goto err_iounmap_all;
|
||||
}
|
||||
|
||||
retval = devm_request_irq(&pdev->dev, pie_irq, rtclong1_interrupt, 0,
|
||||
"rtclong1", pdev);
|
||||
if (retval < 0)
|
||||
goto err_iounmap_all;
|
||||
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
disable_irq(aie_irq);
|
||||
disable_irq(pie_irq);
|
||||
|
||||
dev_info(&pdev->dev, "Real Time Clock of NEC VR4100 series\n");
|
||||
|
||||
retval = devm_rtc_register_device(rtc);
|
||||
if (retval)
|
||||
goto err_iounmap_all;
|
||||
|
||||
return 0;
|
||||
|
||||
err_iounmap_all:
|
||||
rtc2_base = NULL;
|
||||
|
||||
err_rtc1_iounmap:
|
||||
rtc1_base = NULL;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:RTC");
|
||||
|
||||
static struct platform_driver rtc_platform_driver = {
|
||||
.probe = rtc_probe,
|
||||
.driver = {
|
||||
.name = rtc_name,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rtc_platform_driver);
|
|
@ -614,8 +614,7 @@ static void x1205_sysfs_unregister(struct device *dev)
|
|||
}
|
||||
|
||||
|
||||
static int x1205_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int x1205_probe(struct i2c_client *client)
|
||||
{
|
||||
int err = 0;
|
||||
unsigned char sr;
|
||||
|
@ -681,7 +680,7 @@ static struct i2c_driver x1205_driver = {
|
|||
.name = "rtc-x1205",
|
||||
.of_match_table = x1205_dt_ids,
|
||||
},
|
||||
.probe = x1205_probe,
|
||||
.probe_new = x1205_probe,
|
||||
.remove = x1205_remove,
|
||||
.id_table = x1205_id,
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
|
@ -36,17 +37,23 @@
|
|||
#define RTC_OSC_EN BIT(24)
|
||||
#define RTC_BATT_EN BIT(31)
|
||||
|
||||
#define RTC_CALIB_DEF 0x198233
|
||||
#define RTC_CALIB_DEF 0x7FFF
|
||||
#define RTC_CALIB_MASK 0x1FFFFF
|
||||
#define RTC_ALRM_MASK BIT(1)
|
||||
#define RTC_MSEC 1000
|
||||
#define RTC_FR_MASK 0xF0000
|
||||
#define RTC_FR_MAX_TICKS 16
|
||||
#define RTC_PPB 1000000000LL
|
||||
#define RTC_MIN_OFFSET -32768000
|
||||
#define RTC_MAX_OFFSET 32767000
|
||||
|
||||
struct xlnx_rtc_dev {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *reg_base;
|
||||
int alarm_irq;
|
||||
int sec_irq;
|
||||
unsigned int calibval;
|
||||
struct clk *rtc_clk;
|
||||
unsigned int freq;
|
||||
};
|
||||
|
||||
static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
|
@ -61,13 +68,6 @@ static int xlnx_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
*/
|
||||
new_time = rtc_tm_to_time64(tm) + 1;
|
||||
|
||||
/*
|
||||
* Writing into calibration register will clear the Tick Counter and
|
||||
* force the next second to be signaled exactly in 1 second period
|
||||
*/
|
||||
xrtcdev->calibval &= RTC_CALIB_MASK;
|
||||
writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
|
||||
|
||||
writel(new_time, xrtcdev->reg_base + RTC_SET_TM_WR);
|
||||
|
||||
/*
|
||||
|
@ -173,15 +173,76 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev)
|
|||
rtc_ctrl = readl(xrtcdev->reg_base + RTC_CTRL);
|
||||
rtc_ctrl |= RTC_BATT_EN;
|
||||
writel(rtc_ctrl, xrtcdev->reg_base + RTC_CTRL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Based on crystal freq of 33.330 KHz
|
||||
* set the seconds counter and enable, set fractions counter
|
||||
* to default value suggested as per design spec
|
||||
* to correct RTC delay in frequency over period of time.
|
||||
static int xlnx_rtc_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
|
||||
unsigned long long rtc_ppb = RTC_PPB;
|
||||
unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq);
|
||||
unsigned int calibval;
|
||||
long offset_val;
|
||||
|
||||
calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD);
|
||||
/* Offset with seconds ticks */
|
||||
offset_val = calibval & RTC_TICK_MASK;
|
||||
offset_val = offset_val - RTC_CALIB_DEF;
|
||||
offset_val = offset_val * tick_mult;
|
||||
|
||||
/* Offset with fractional ticks */
|
||||
if (calibval & RTC_FR_EN)
|
||||
offset_val += ((calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT)
|
||||
* (tick_mult / RTC_FR_MAX_TICKS);
|
||||
*offset = offset_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xlnx_rtc_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
|
||||
unsigned long long rtc_ppb = RTC_PPB;
|
||||
unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq);
|
||||
unsigned char fract_tick = 0;
|
||||
unsigned int calibval;
|
||||
short int max_tick;
|
||||
int fract_offset;
|
||||
|
||||
if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET)
|
||||
return -ERANGE;
|
||||
|
||||
/* Number ticks for given offset */
|
||||
max_tick = div_s64_rem(offset, tick_mult, &fract_offset);
|
||||
|
||||
/* Number fractional ticks for given offset */
|
||||
if (fract_offset) {
|
||||
if (fract_offset < 0) {
|
||||
fract_offset = fract_offset + tick_mult;
|
||||
max_tick--;
|
||||
}
|
||||
if (fract_offset > (tick_mult / RTC_FR_MAX_TICKS)) {
|
||||
for (fract_tick = 1; fract_tick < 16; fract_tick++) {
|
||||
if (fract_offset <=
|
||||
(fract_tick *
|
||||
(tick_mult / RTC_FR_MAX_TICKS)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Zynqmp RTC uses second and fractional tick
|
||||
* counters for compensation
|
||||
*/
|
||||
xrtcdev->calibval &= RTC_CALIB_MASK;
|
||||
writel(xrtcdev->calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
|
||||
calibval = max_tick + RTC_CALIB_DEF;
|
||||
|
||||
if (fract_tick)
|
||||
calibval |= RTC_FR_EN;
|
||||
|
||||
calibval |= (fract_tick << RTC_FR_DATSHIFT);
|
||||
|
||||
writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops xlnx_rtc_ops = {
|
||||
|
@ -190,6 +251,8 @@ static const struct rtc_class_ops xlnx_rtc_ops = {
|
|||
.read_alarm = xlnx_rtc_read_alarm,
|
||||
.set_alarm = xlnx_rtc_set_alarm,
|
||||
.alarm_irq_enable = xlnx_rtc_alarm_irq_enable,
|
||||
.read_offset = xlnx_rtc_read_offset,
|
||||
.set_offset = xlnx_rtc_set_offset,
|
||||
};
|
||||
|
||||
static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
|
||||
|
@ -255,10 +318,22 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "calibration",
|
||||
&xrtcdev->calibval);
|
||||
if (ret)
|
||||
xrtcdev->calibval = RTC_CALIB_DEF;
|
||||
/* Getting the rtc_clk info */
|
||||
xrtcdev->rtc_clk = devm_clk_get_optional(&pdev->dev, "rtc_clk");
|
||||
if (IS_ERR(xrtcdev->rtc_clk)) {
|
||||
if (PTR_ERR(xrtcdev->rtc_clk) != -EPROBE_DEFER)
|
||||
dev_warn(&pdev->dev, "Device clock not found.\n");
|
||||
}
|
||||
xrtcdev->freq = clk_get_rate(xrtcdev->rtc_clk);
|
||||
if (!xrtcdev->freq) {
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "calibration",
|
||||
&xrtcdev->freq);
|
||||
if (ret)
|
||||
xrtcdev->freq = RTC_CALIB_DEF;
|
||||
}
|
||||
ret = readl(xrtcdev->reg_base + RTC_CALIB_RD);
|
||||
if (!ret)
|
||||
writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR));
|
||||
|
||||
xlnx_init_rtc(xrtcdev);
|
||||
|
||||
|
|
Loading…
Reference in New Issue