RTC for 5.19
New driver: - Renesas RZN1 rtc Drivers: - sun6i: Add nvmem support -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmKX2rgACgkQY6TcMGxw OjJ0yw//bQY0gAQ4W1RghJ3NataBLVIiaxBpPYFSgSLA8xVj4vYNaQOn5ZAbn/yD a1Y+1QM3E71nNfSeuJWdF4UH52boyoBOMme0aowWjHXWIzbpRQnKo4hj15ViSrI5 W0wcA3BNU6KbS/LzKyanN2l+Xw7rAdc/Q6AiWkj8PUjZJoSzwH6R0PSlYu9OGi2J CNUDENeS7uysBNvtXMUQxJD/hEXDpKMdKhnoy3l2uYDfMk1LV4vR+WlXzdDNW8VB 0f9W5pD6TRNBJ/iDGXTLkTa7eDBwKuqJ+RFmzZ01KYQKcPr60VRBA6noAdaZbEiG veQWpURzp5J8QyzsWB8NfI/scJRUnIt0/oBtBLHeDtXBo3pcPpRGkHzjayQ0jnX7 aJj/HUR6Bn9LE0UPN1UYyHY+Vf1JOfXr3tuXV9dpSBHlj99wGqW28Qs/29jFgHfy xQN2cfBSYE+0WPBss/nFwkff/OGBax/JpQfBkEYd95oYMiCw6HjFg2efO9yYw3+m tW5IoJLybfKzoD/N0VYn0D9TO2Lvf+1wC2jgxcFH8urZKKwAjPOb2L2rU1R56Hx/ YHcQWOFaaHcZQLHDyT6/PSpMnhYiiFTbFswdYwq40Z9HkIjdclpKeYHOGDkpkiNA TB/aafw+NNXD47zOJxVR6Bxu2OBpupbXtvlXpHM0+qU2eszs2kw= =D9z8 -----END PGP SIGNATURE----- Merge tag 'rtc-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "A new driver represents the bulk of the changes and then we get the usual small fixes. New driver: - Renesas RZN1 rtc Drivers: - sun6i: Add nvmem support" * tag 'rtc-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: rtc: mxc: Silence a clang warning rtc: rzn1: Fix a variable type rtc: rzn1: Fix error code in probe rtc: rzn1: Avoid mixing variables rtc: ftrtc010: Fix error handling in ftrtc010_rtc_probe rtc: mt6397: check return value after calling platform_get_resource() rtc: rzn1: fix platform_no_drv_owner.cocci warning rtc: gamecube: Add missing iounmap in gamecube_rtc_read_offset_from_sram rtc: meson: Fix email address in MODULE_AUTHOR rtc: simplify the return expression of rx8025_set_offset() rtc: pcf85063: Add a compatible entry for pca85073a dt-binding: pcf85063: Add an entry for pca85073a MAINTAINERS: Add myself as maintainer of the RZN1 RTC driver rtc: rzn1: Add oscillator offset support rtc: rzn1: Add alarm support rtc: rzn1: Add new RTC driver dt-bindings: rtc: rzn1: Describe the RZN1 RTC rtc: sun6i: Add NVMEM provider
This commit is contained in:
commit
54eb8462f2
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible: Should one of contain:
|
- compatible: Should one of contain:
|
||||||
|
"nxp,pca85073a",
|
||||||
"nxp,pcf85063",
|
"nxp,pcf85063",
|
||||||
"nxp,pcf85063a",
|
"nxp,pcf85063a",
|
||||||
"nxp,pcf85063tp",
|
"nxp,pcf85063tp",
|
||||||
|
70
Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
Normal file
70
Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
$id: http://devicetree.org/schemas/rtc/renesas,rzn1-rtc.yaml#
|
||||||
|
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||||
|
|
||||||
|
title: Renesas RZ/N1 SoCs Real-Time Clock DT bindings
|
||||||
|
|
||||||
|
maintainers:
|
||||||
|
- Miquel Raynal <miquel.raynal@bootlin.com>
|
||||||
|
|
||||||
|
allOf:
|
||||||
|
- $ref: rtc.yaml#
|
||||||
|
|
||||||
|
properties:
|
||||||
|
compatible:
|
||||||
|
items:
|
||||||
|
- enum:
|
||||||
|
- renesas,r9a06g032-rtc
|
||||||
|
- const: renesas,rzn1-rtc
|
||||||
|
|
||||||
|
reg:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
interrupts:
|
||||||
|
minItems: 3
|
||||||
|
maxItems: 3
|
||||||
|
|
||||||
|
interrupt-names:
|
||||||
|
items:
|
||||||
|
- const: alarm
|
||||||
|
- const: timer
|
||||||
|
- const: pps
|
||||||
|
|
||||||
|
clocks:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
clock-names:
|
||||||
|
const: hclk
|
||||||
|
|
||||||
|
power-domains:
|
||||||
|
maxItems: 1
|
||||||
|
|
||||||
|
required:
|
||||||
|
- compatible
|
||||||
|
- reg
|
||||||
|
- interrupts
|
||||||
|
- interrupt-names
|
||||||
|
- clocks
|
||||||
|
- clock-names
|
||||||
|
- power-domains
|
||||||
|
|
||||||
|
unevaluatedProperties: false
|
||||||
|
|
||||||
|
examples:
|
||||||
|
- |
|
||||||
|
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||||
|
#include <dt-bindings/clock/r9a06g032-sysctrl.h>
|
||||||
|
rtc@40006000 {
|
||||||
|
compatible = "renesas,r9a06g032-rtc", "renesas,rzn1-rtc";
|
||||||
|
reg = <0x40006000 0x1000>;
|
||||||
|
interrupts = <GIC_SPI 66 IRQ_TYPE_EDGE_RISING>,
|
||||||
|
<GIC_SPI 67 IRQ_TYPE_EDGE_RISING>,
|
||||||
|
<GIC_SPI 68 IRQ_TYPE_EDGE_RISING>;
|
||||||
|
interrupt-names = "alarm", "timer", "pps";
|
||||||
|
clocks = <&sysctrl R9A06G032_HCLK_RTC>;
|
||||||
|
clock-names = "hclk";
|
||||||
|
power-domains = <&sysctrl>;
|
||||||
|
start-year = <2000>;
|
||||||
|
};
|
@ -16995,6 +16995,14 @@ S: Supported
|
|||||||
F: Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
|
F: Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml
|
||||||
F: drivers/iio/adc/rzg2l_adc.c
|
F: drivers/iio/adc/rzg2l_adc.c
|
||||||
|
|
||||||
|
RENESAS RZ/N1 RTC CONTROLLER DRIVER
|
||||||
|
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||||
|
L: linux-rtc@vger.kernel.org
|
||||||
|
L: linux-renesas-soc@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
|
||||||
|
F: drivers/rtc/rtc-rzn1.c
|
||||||
|
|
||||||
RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
|
RENESAS R-CAR GEN3 & RZ/N1 NAND CONTROLLER DRIVER
|
||||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||||
L: linux-mtd@lists.infradead.org
|
L: linux-mtd@lists.infradead.org
|
||||||
|
@ -1548,6 +1548,13 @@ config RTC_DRV_RS5C313
|
|||||||
help
|
help
|
||||||
If you say yes here you get support for the Ricoh RS5C313 RTC chips.
|
If you say yes here you get support for the Ricoh RS5C313 RTC chips.
|
||||||
|
|
||||||
|
config RTC_DRV_RZN1
|
||||||
|
tristate "Renesas RZ/N1 RTC"
|
||||||
|
depends on ARCH_RZN1 || COMPILE_TEST
|
||||||
|
depends on OF && HAS_IOMEM
|
||||||
|
help
|
||||||
|
If you say yes here you get support for the Renesas RZ/N1 RTC.
|
||||||
|
|
||||||
config RTC_DRV_GENERIC
|
config RTC_DRV_GENERIC
|
||||||
tristate "Generic RTC support"
|
tristate "Generic RTC support"
|
||||||
# Please consider writing a new RTC driver instead of using the generic
|
# Please consider writing a new RTC driver instead of using the generic
|
||||||
|
@ -151,6 +151,7 @@ obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o
|
|||||||
obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
|
obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
|
||||||
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
|
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
|
||||||
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
|
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
|
||||||
|
obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o
|
||||||
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
|
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
|
||||||
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
|
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
|
||||||
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
|
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
|
||||||
|
@ -137,26 +137,34 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
|||||||
ret = clk_prepare_enable(rtc->extclk);
|
ret = clk_prepare_enable(rtc->extclk);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "failed to enable EXTCLK\n");
|
dev_err(dev, "failed to enable EXTCLK\n");
|
||||||
return ret;
|
goto err_disable_pclk;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rtc->rtc_irq = platform_get_irq(pdev, 0);
|
rtc->rtc_irq = platform_get_irq(pdev, 0);
|
||||||
if (rtc->rtc_irq < 0)
|
if (rtc->rtc_irq < 0) {
|
||||||
return rtc->rtc_irq;
|
ret = rtc->rtc_irq;
|
||||||
|
goto err_disable_extclk;
|
||||||
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
if (!res)
|
if (!res) {
|
||||||
return -ENODEV;
|
ret = -ENODEV;
|
||||||
|
goto err_disable_extclk;
|
||||||
|
}
|
||||||
|
|
||||||
rtc->rtc_base = devm_ioremap(dev, res->start,
|
rtc->rtc_base = devm_ioremap(dev, res->start,
|
||||||
resource_size(res));
|
resource_size(res));
|
||||||
if (!rtc->rtc_base)
|
if (!rtc->rtc_base) {
|
||||||
return -ENOMEM;
|
ret = -ENOMEM;
|
||||||
|
goto err_disable_extclk;
|
||||||
|
}
|
||||||
|
|
||||||
rtc->rtc_dev = devm_rtc_allocate_device(dev);
|
rtc->rtc_dev = devm_rtc_allocate_device(dev);
|
||||||
if (IS_ERR(rtc->rtc_dev))
|
if (IS_ERR(rtc->rtc_dev)) {
|
||||||
return PTR_ERR(rtc->rtc_dev);
|
ret = PTR_ERR(rtc->rtc_dev);
|
||||||
|
goto err_disable_extclk;
|
||||||
|
}
|
||||||
|
|
||||||
rtc->rtc_dev->ops = &ftrtc010_rtc_ops;
|
rtc->rtc_dev->ops = &ftrtc010_rtc_ops;
|
||||||
|
|
||||||
@ -172,9 +180,15 @@ static int ftrtc010_rtc_probe(struct platform_device *pdev)
|
|||||||
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
|
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
|
||||||
IRQF_SHARED, pdev->name, dev);
|
IRQF_SHARED, pdev->name, dev);
|
||||||
if (unlikely(ret))
|
if (unlikely(ret))
|
||||||
return ret;
|
goto err_disable_extclk;
|
||||||
|
|
||||||
return devm_rtc_register_device(rtc->rtc_dev);
|
return devm_rtc_register_device(rtc->rtc_dev);
|
||||||
|
|
||||||
|
err_disable_extclk:
|
||||||
|
clk_disable_unprepare(rtc->extclk);
|
||||||
|
err_disable_pclk:
|
||||||
|
clk_disable_unprepare(rtc->pclk);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ftrtc010_rtc_remove(struct platform_device *pdev)
|
static int ftrtc010_rtc_remove(struct platform_device *pdev)
|
||||||
|
@ -267,6 +267,7 @@ static int gamecube_rtc_read_offset_from_sram(struct priv *d)
|
|||||||
ret = regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias);
|
ret = regmap_read(d->regmap, RTC_SRAM_BIAS, &d->rtc_bias);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_err("failed to get the RTC bias\n");
|
pr_err("failed to get the RTC bias\n");
|
||||||
|
iounmap(hw_srnprot);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ static struct platform_driver meson_rtc_driver = {
|
|||||||
module_platform_driver(meson_rtc_driver);
|
module_platform_driver(meson_rtc_driver);
|
||||||
|
|
||||||
MODULE_DESCRIPTION("Amlogic Meson RTC Driver");
|
MODULE_DESCRIPTION("Amlogic Meson RTC Driver");
|
||||||
MODULE_AUTHOR("Ben Dooks <ben.doosk@codethink.co.uk>");
|
MODULE_AUTHOR("Ben Dooks <ben.dooks@codethink.co.uk>");
|
||||||
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
MODULE_ALIAS("platform:meson-rtc");
|
MODULE_ALIAS("platform:meson-rtc");
|
||||||
|
@ -269,6 +269,8 @@ static int mtk_rtc_probe(struct platform_device *pdev)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res)
|
||||||
|
return -EINVAL;
|
||||||
rtc->addr_base = res->start;
|
rtc->addr_base = res->start;
|
||||||
|
|
||||||
rtc->data = of_device_get_match_data(&pdev->dev);
|
rtc->data = of_device_get_match_data(&pdev->dev);
|
||||||
|
@ -311,7 +311,7 @@ static int mxc_rtc_probe(struct platform_device *pdev)
|
|||||||
if (!pdata)
|
if (!pdata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
pdata->devtype = (enum imx_rtc_type)of_device_get_match_data(&pdev->dev);
|
pdata->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev);
|
||||||
|
|
||||||
pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0);
|
pdata->ioaddr = devm_platform_ioremap_resource(pdev, 0);
|
||||||
if (IS_ERR(pdata->ioaddr))
|
if (IS_ERR(pdata->ioaddr))
|
||||||
|
@ -650,6 +650,7 @@ static int pcf85063_probe(struct i2c_client *client)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id pcf85063_ids[] = {
|
static const struct i2c_device_id pcf85063_ids[] = {
|
||||||
|
{ "pca85073a", PCF85063A },
|
||||||
{ "pcf85063", PCF85063 },
|
{ "pcf85063", PCF85063 },
|
||||||
{ "pcf85063tp", PCF85063TP },
|
{ "pcf85063tp", PCF85063TP },
|
||||||
{ "pcf85063a", PCF85063A },
|
{ "pcf85063a", PCF85063A },
|
||||||
@ -660,6 +661,7 @@ MODULE_DEVICE_TABLE(i2c, pcf85063_ids);
|
|||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static const struct of_device_id pcf85063_of_match[] = {
|
static const struct of_device_id pcf85063_of_match[] = {
|
||||||
|
{ .compatible = "nxp,pca85073a", .data = &pcf85063_cfg[PCF85063A] },
|
||||||
{ .compatible = "nxp,pcf85063", .data = &pcf85063_cfg[PCF85063] },
|
{ .compatible = "nxp,pcf85063", .data = &pcf85063_cfg[PCF85063] },
|
||||||
{ .compatible = "nxp,pcf85063tp", .data = &pcf85063_cfg[PCF85063TP] },
|
{ .compatible = "nxp,pcf85063tp", .data = &pcf85063_cfg[PCF85063TP] },
|
||||||
{ .compatible = "nxp,pcf85063a", .data = &pcf85063_cfg[PCF85063A] },
|
{ .compatible = "nxp,pcf85063a", .data = &pcf85063_cfg[PCF85063A] },
|
||||||
|
@ -436,7 +436,6 @@ static int rx8025_set_offset(struct device *dev, long offset)
|
|||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
u8 digoff;
|
u8 digoff;
|
||||||
int err;
|
|
||||||
|
|
||||||
offset /= RX8025_ADJ_RESOLUTION;
|
offset /= RX8025_ADJ_RESOLUTION;
|
||||||
if (offset > RX8025_ADJ_DATA_MAX)
|
if (offset > RX8025_ADJ_DATA_MAX)
|
||||||
@ -449,11 +448,7 @@ static int rx8025_set_offset(struct device *dev, long offset)
|
|||||||
offset += 128;
|
offset += 128;
|
||||||
digoff = offset;
|
digoff = offset;
|
||||||
|
|
||||||
err = rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff);
|
return rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff);
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct rtc_class_ops rx8025_rtc_ops = {
|
static const struct rtc_class_ops rx8025_rtc_ops = {
|
||||||
|
418
drivers/rtc/rtc-rzn1.c
Normal file
418
drivers/rtc/rtc-rzn1.c
Normal file
@ -0,0 +1,418 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
|
/*
|
||||||
|
* Renesas RZ/N1 Real Time Clock interface for Linux
|
||||||
|
*
|
||||||
|
* Copyright:
|
||||||
|
* - 2014 Renesas Electronics Europe Limited
|
||||||
|
* - 2022 Schneider Electric
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* - Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com>
|
||||||
|
* - Miquel Raynal <miquel.raynal@bootlin.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bcd.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/iopoll.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/rtc.h>
|
||||||
|
|
||||||
|
#define RZN1_RTC_CTL0 0x00
|
||||||
|
#define RZN1_RTC_CTL0_SLSB_SUBU 0
|
||||||
|
#define RZN1_RTC_CTL0_SLSB_SCMP BIT(4)
|
||||||
|
#define RZN1_RTC_CTL0_AMPM BIT(5)
|
||||||
|
#define RZN1_RTC_CTL0_CE BIT(7)
|
||||||
|
|
||||||
|
#define RZN1_RTC_CTL1 0x04
|
||||||
|
#define RZN1_RTC_CTL1_ALME BIT(4)
|
||||||
|
|
||||||
|
#define RZN1_RTC_CTL2 0x08
|
||||||
|
#define RZN1_RTC_CTL2_WAIT BIT(0)
|
||||||
|
#define RZN1_RTC_CTL2_WST BIT(1)
|
||||||
|
#define RZN1_RTC_CTL2_WUST BIT(5)
|
||||||
|
#define RZN1_RTC_CTL2_STOPPED (RZN1_RTC_CTL2_WAIT | RZN1_RTC_CTL2_WST)
|
||||||
|
|
||||||
|
#define RZN1_RTC_SEC 0x14
|
||||||
|
#define RZN1_RTC_MIN 0x18
|
||||||
|
#define RZN1_RTC_HOUR 0x1c
|
||||||
|
#define RZN1_RTC_WEEK 0x20
|
||||||
|
#define RZN1_RTC_DAY 0x24
|
||||||
|
#define RZN1_RTC_MONTH 0x28
|
||||||
|
#define RZN1_RTC_YEAR 0x2c
|
||||||
|
|
||||||
|
#define RZN1_RTC_SUBU 0x38
|
||||||
|
#define RZN1_RTC_SUBU_DEV BIT(7)
|
||||||
|
#define RZN1_RTC_SUBU_DECR BIT(6)
|
||||||
|
|
||||||
|
#define RZN1_RTC_ALM 0x40
|
||||||
|
#define RZN1_RTC_ALH 0x44
|
||||||
|
#define RZN1_RTC_ALW 0x48
|
||||||
|
|
||||||
|
#define RZN1_RTC_SECC 0x4c
|
||||||
|
#define RZN1_RTC_MINC 0x50
|
||||||
|
#define RZN1_RTC_HOURC 0x54
|
||||||
|
#define RZN1_RTC_WEEKC 0x58
|
||||||
|
#define RZN1_RTC_DAYC 0x5c
|
||||||
|
#define RZN1_RTC_MONTHC 0x60
|
||||||
|
#define RZN1_RTC_YEARC 0x64
|
||||||
|
|
||||||
|
struct rzn1_rtc {
|
||||||
|
struct rtc_device *rtcdev;
|
||||||
|
void __iomem *base;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void rzn1_rtc_get_time_snapshot(struct rzn1_rtc *rtc, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
tm->tm_sec = readl(rtc->base + RZN1_RTC_SECC);
|
||||||
|
tm->tm_min = readl(rtc->base + RZN1_RTC_MINC);
|
||||||
|
tm->tm_hour = readl(rtc->base + RZN1_RTC_HOURC);
|
||||||
|
tm->tm_wday = readl(rtc->base + RZN1_RTC_WEEKC);
|
||||||
|
tm->tm_mday = readl(rtc->base + RZN1_RTC_DAYC);
|
||||||
|
tm->tm_mon = readl(rtc->base + RZN1_RTC_MONTHC);
|
||||||
|
tm->tm_year = readl(rtc->base + RZN1_RTC_YEARC);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int rzn1_rtc_tm_to_wday(struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
time64_t time;
|
||||||
|
unsigned int days;
|
||||||
|
u32 secs;
|
||||||
|
|
||||||
|
time = rtc_tm_to_time64(tm);
|
||||||
|
days = div_s64_rem(time, 86400, &secs);
|
||||||
|
|
||||||
|
/* day of the week, 1970-01-01 was a Thursday */
|
||||||
|
return (days + 4) % 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
u32 val, secs;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The RTC was not started or is stopped and thus does not carry the
|
||||||
|
* proper time/date.
|
||||||
|
*/
|
||||||
|
val = readl(rtc->base + RZN1_RTC_CTL2);
|
||||||
|
if (val & RZN1_RTC_CTL2_STOPPED)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
rzn1_rtc_get_time_snapshot(rtc, tm);
|
||||||
|
secs = readl(rtc->base + RZN1_RTC_SECC);
|
||||||
|
if (tm->tm_sec != secs)
|
||||||
|
rzn1_rtc_get_time_snapshot(rtc, tm);
|
||||||
|
|
||||||
|
tm->tm_sec = bcd2bin(tm->tm_sec);
|
||||||
|
tm->tm_min = bcd2bin(tm->tm_min);
|
||||||
|
tm->tm_hour = bcd2bin(tm->tm_hour);
|
||||||
|
tm->tm_wday = bcd2bin(tm->tm_wday);
|
||||||
|
tm->tm_mday = bcd2bin(tm->tm_mday);
|
||||||
|
tm->tm_mon = bcd2bin(tm->tm_mon);
|
||||||
|
tm->tm_year = bcd2bin(tm->tm_year);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
u32 val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
tm->tm_sec = bin2bcd(tm->tm_sec);
|
||||||
|
tm->tm_min = bin2bcd(tm->tm_min);
|
||||||
|
tm->tm_hour = bin2bcd(tm->tm_hour);
|
||||||
|
tm->tm_wday = bin2bcd(rzn1_rtc_tm_to_wday(tm));
|
||||||
|
tm->tm_mday = bin2bcd(tm->tm_mday);
|
||||||
|
tm->tm_mon = bin2bcd(tm->tm_mon);
|
||||||
|
tm->tm_year = bin2bcd(tm->tm_year);
|
||||||
|
|
||||||
|
val = readl(rtc->base + RZN1_RTC_CTL2);
|
||||||
|
if (!(val & RZN1_RTC_CTL2_STOPPED)) {
|
||||||
|
/* Hold the counter if it was counting up */
|
||||||
|
writel(RZN1_RTC_CTL2_WAIT, rtc->base + RZN1_RTC_CTL2);
|
||||||
|
|
||||||
|
/* Wait for the counter to stop: two 32k clock cycles */
|
||||||
|
usleep_range(61, 100);
|
||||||
|
ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, val,
|
||||||
|
val & RZN1_RTC_CTL2_WST, 0, 100);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(tm->tm_sec, rtc->base + RZN1_RTC_SEC);
|
||||||
|
writel(tm->tm_min, rtc->base + RZN1_RTC_MIN);
|
||||||
|
writel(tm->tm_hour, rtc->base + RZN1_RTC_HOUR);
|
||||||
|
writel(tm->tm_wday, rtc->base + RZN1_RTC_WEEK);
|
||||||
|
writel(tm->tm_mday, rtc->base + RZN1_RTC_DAY);
|
||||||
|
writel(tm->tm_mon, rtc->base + RZN1_RTC_MONTH);
|
||||||
|
writel(tm->tm_year, rtc->base + RZN1_RTC_YEAR);
|
||||||
|
writel(0, rtc->base + RZN1_RTC_CTL2);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t rzn1_rtc_alarm_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_id;
|
||||||
|
|
||||||
|
rtc_update_irq(rtc->rtcdev, 1, RTC_AF | RTC_IRQF);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
u32 ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
ctl1 |= RZN1_RTC_CTL1_ALME;
|
||||||
|
else
|
||||||
|
ctl1 &= ~RZN1_RTC_CTL1_ALME;
|
||||||
|
|
||||||
|
writel(ctl1, rtc->base + RZN1_RTC_CTL1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
struct rtc_time *tm = &alrm->time;
|
||||||
|
unsigned int min, hour, wday, delta_days;
|
||||||
|
time64_t alarm;
|
||||||
|
u32 ctl1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rzn1_rtc_read_time(dev, tm);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
min = readl(rtc->base + RZN1_RTC_ALM);
|
||||||
|
hour = readl(rtc->base + RZN1_RTC_ALH);
|
||||||
|
wday = readl(rtc->base + RZN1_RTC_ALW);
|
||||||
|
|
||||||
|
tm->tm_sec = 0;
|
||||||
|
tm->tm_min = bcd2bin(min);
|
||||||
|
tm->tm_hour = bcd2bin(hour);
|
||||||
|
delta_days = ((fls(wday) - 1) - tm->tm_wday + 7) % 7;
|
||||||
|
tm->tm_wday = fls(wday) - 1;
|
||||||
|
|
||||||
|
if (delta_days) {
|
||||||
|
alarm = rtc_tm_to_time64(tm) + (delta_days * 86400);
|
||||||
|
rtc_time64_to_tm(alarm, tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctl1 = readl(rtc->base + RZN1_RTC_CTL1);
|
||||||
|
alrm->enabled = !!(ctl1 & RZN1_RTC_CTL1_ALME);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
struct rtc_time *tm = &alrm->time, tm_now;
|
||||||
|
unsigned long alarm, farest;
|
||||||
|
unsigned int days_ahead, wday;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = rzn1_rtc_read_time(dev, &tm_now);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/* We cannot set alarms more than one week ahead */
|
||||||
|
farest = rtc_tm_to_time64(&tm_now) + (7 * 86400);
|
||||||
|
alarm = rtc_tm_to_time64(tm);
|
||||||
|
if (time_after(alarm, farest))
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
/* Convert alarm day into week day */
|
||||||
|
days_ahead = tm->tm_mday - tm_now.tm_mday;
|
||||||
|
wday = (tm_now.tm_wday + days_ahead) % 7;
|
||||||
|
|
||||||
|
writel(bin2bcd(tm->tm_min), rtc->base + RZN1_RTC_ALM);
|
||||||
|
writel(bin2bcd(tm->tm_hour), rtc->base + RZN1_RTC_ALH);
|
||||||
|
writel(BIT(wday), rtc->base + RZN1_RTC_ALW);
|
||||||
|
|
||||||
|
rzn1_rtc_alarm_irq_enable(dev, alrm->enabled);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_read_offset(struct device *dev, long *offset)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
unsigned int ppb_per_step;
|
||||||
|
bool subtract;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
val = readl(rtc->base + RZN1_RTC_SUBU);
|
||||||
|
ppb_per_step = val & RZN1_RTC_SUBU_DEV ? 1017 : 3051;
|
||||||
|
subtract = val & RZN1_RTC_SUBU_DECR;
|
||||||
|
val &= 0x3F;
|
||||||
|
|
||||||
|
if (!val)
|
||||||
|
*offset = 0;
|
||||||
|
else if (subtract)
|
||||||
|
*offset = -(((~val) & 0x3F) + 1) * ppb_per_step;
|
||||||
|
else
|
||||||
|
*offset = (val - 1) * ppb_per_step;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_set_offset(struct device *dev, long offset)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc = dev_get_drvdata(dev);
|
||||||
|
int stepsh, stepsl, steps;
|
||||||
|
u32 subu = 0, ctl2;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check which resolution mode (every 20 or 60s) can be used.
|
||||||
|
* Between 2 and 124 clock pulses can be added or substracted.
|
||||||
|
*
|
||||||
|
* In 20s mode, the minimum resolution is 2 / (32768 * 20) which is
|
||||||
|
* close to 3051 ppb. In 60s mode, the resolution is closer to 1017.
|
||||||
|
*/
|
||||||
|
stepsh = DIV_ROUND_CLOSEST(offset, 1017);
|
||||||
|
stepsl = DIV_ROUND_CLOSEST(offset, 3051);
|
||||||
|
|
||||||
|
if (stepsh >= -0x3E && stepsh <= 0x3E) {
|
||||||
|
/* 1017 ppb per step */
|
||||||
|
steps = stepsh;
|
||||||
|
subu |= RZN1_RTC_SUBU_DEV;
|
||||||
|
} else if (stepsl >= -0x3E && stepsl <= 0x3E) {
|
||||||
|
/* 3051 ppb per step */
|
||||||
|
steps = stepsl;
|
||||||
|
} else {
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!steps)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (steps > 0) {
|
||||||
|
subu |= steps + 1;
|
||||||
|
} else {
|
||||||
|
subu |= RZN1_RTC_SUBU_DECR;
|
||||||
|
subu |= (~(-steps - 1)) & 0x3F;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL2, ctl2,
|
||||||
|
!(ctl2 & RZN1_RTC_CTL2_WUST), 100, 2000000);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
writel(subu, rtc->base + RZN1_RTC_SUBU);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct rtc_class_ops rzn1_rtc_ops = {
|
||||||
|
.read_time = rzn1_rtc_read_time,
|
||||||
|
.set_time = rzn1_rtc_set_time,
|
||||||
|
.read_alarm = rzn1_rtc_read_alarm,
|
||||||
|
.set_alarm = rzn1_rtc_set_alarm,
|
||||||
|
.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
|
||||||
|
.read_offset = rzn1_rtc_read_offset,
|
||||||
|
.set_offset = rzn1_rtc_set_offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int rzn1_rtc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct rzn1_rtc *rtc;
|
||||||
|
int alarm_irq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
||||||
|
if (!rtc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, rtc);
|
||||||
|
|
||||||
|
rtc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||||
|
if (IS_ERR(rtc->base))
|
||||||
|
return dev_err_probe(&pdev->dev, PTR_ERR(rtc->base), "Missing reg\n");
|
||||||
|
|
||||||
|
alarm_irq = platform_get_irq(pdev, 0);
|
||||||
|
if (alarm_irq < 0)
|
||||||
|
return alarm_irq;
|
||||||
|
|
||||||
|
rtc->rtcdev = devm_rtc_allocate_device(&pdev->dev);
|
||||||
|
if (IS_ERR(rtc->rtcdev))
|
||||||
|
return PTR_ERR(rtc->rtcdev);
|
||||||
|
|
||||||
|
rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||||
|
rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;
|
||||||
|
rtc->rtcdev->ops = &rzn1_rtc_ops;
|
||||||
|
set_bit(RTC_FEATURE_ALARM_RES_MINUTE, rtc->rtcdev->features);
|
||||||
|
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->rtcdev->features);
|
||||||
|
|
||||||
|
devm_pm_runtime_enable(&pdev->dev);
|
||||||
|
ret = pm_runtime_resume_and_get(&pdev->dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure the clock counter is enabled.
|
||||||
|
* Set 24-hour mode and possible oscillator offset compensation in SUBU mode.
|
||||||
|
*/
|
||||||
|
writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU,
|
||||||
|
rtc->base + RZN1_RTC_CTL0);
|
||||||
|
|
||||||
|
/* Disable all interrupts */
|
||||||
|
writel(0, rtc->base + RZN1_RTC_CTL1);
|
||||||
|
|
||||||
|
ret = devm_request_irq(&pdev->dev, alarm_irq, rzn1_rtc_alarm_irq, 0,
|
||||||
|
dev_name(&pdev->dev), rtc);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "RTC timer interrupt not available\n");
|
||||||
|
goto dis_runtime_pm;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_rtc_register_device(rtc->rtcdev);
|
||||||
|
if (ret)
|
||||||
|
goto dis_runtime_pm;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
dis_runtime_pm:
|
||||||
|
pm_runtime_put(&pdev->dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int rzn1_rtc_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
pm_runtime_put(&pdev->dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id rzn1_rtc_of_match[] = {
|
||||||
|
{ .compatible = "renesas,rzn1-rtc" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, rzn1_rtc_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver rzn1_rtc_driver = {
|
||||||
|
.probe = rzn1_rtc_probe,
|
||||||
|
.remove = rzn1_rtc_remove,
|
||||||
|
.driver = {
|
||||||
|
.name = "rzn1-rtc",
|
||||||
|
.of_match_table = rzn1_rtc_of_match,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module_platform_driver(rzn1_rtc_driver);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Michel Pollet <Michel.Pollet@bp.renesas.com");
|
||||||
|
MODULE_AUTHOR("Miquel Raynal <miquel.raynal@bootlin.com");
|
||||||
|
MODULE_DESCRIPTION("RZ/N1 RTC driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
@ -71,6 +71,10 @@
|
|||||||
#define SUN6I_LOSC_OUT_GATING 0x0060
|
#define SUN6I_LOSC_OUT_GATING 0x0060
|
||||||
#define SUN6I_LOSC_OUT_GATING_EN_OFFSET 0
|
#define SUN6I_LOSC_OUT_GATING_EN_OFFSET 0
|
||||||
|
|
||||||
|
/* General-purpose data */
|
||||||
|
#define SUN6I_GP_DATA 0x0100
|
||||||
|
#define SUN6I_GP_DATA_SIZE 0x20
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get date values
|
* Get date values
|
||||||
*/
|
*/
|
||||||
@ -679,6 +683,39 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
|
|||||||
.alarm_irq_enable = sun6i_rtc_alarm_irq_enable
|
.alarm_irq_enable = sun6i_rtc_alarm_irq_enable
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int sun6i_rtc_nvmem_read(void *priv, unsigned int offset, void *_val, size_t bytes)
|
||||||
|
{
|
||||||
|
struct sun6i_rtc_dev *chip = priv;
|
||||||
|
u32 *val = _val;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bytes / 4; ++i)
|
||||||
|
val[i] = readl(chip->base + SUN6I_GP_DATA + offset + 4 * i);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sun6i_rtc_nvmem_write(void *priv, unsigned int offset, void *_val, size_t bytes)
|
||||||
|
{
|
||||||
|
struct sun6i_rtc_dev *chip = priv;
|
||||||
|
u32 *val = _val;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bytes / 4; ++i)
|
||||||
|
writel(val[i], chip->base + SUN6I_GP_DATA + offset + 4 * i);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nvmem_config sun6i_rtc_nvmem_cfg = {
|
||||||
|
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||||
|
.reg_read = sun6i_rtc_nvmem_read,
|
||||||
|
.reg_write = sun6i_rtc_nvmem_write,
|
||||||
|
.size = SUN6I_GP_DATA_SIZE,
|
||||||
|
.word_size = 4,
|
||||||
|
.stride = 4,
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_PM_SLEEP
|
#ifdef CONFIG_PM_SLEEP
|
||||||
/* Enable IRQ wake on suspend, to wake up from RTC. */
|
/* Enable IRQ wake on suspend, to wake up from RTC. */
|
||||||
static int sun6i_rtc_suspend(struct device *dev)
|
static int sun6i_rtc_suspend(struct device *dev)
|
||||||
@ -812,6 +849,11 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
sun6i_rtc_nvmem_cfg.priv = chip;
|
||||||
|
ret = devm_rtc_nvmem_register(chip->rtc, &sun6i_rtc_nvmem_cfg);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
dev_info(&pdev->dev, "RTC enabled\n");
|
dev_info(&pdev->dev, "RTC enabled\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user