mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 14:42:24 +00:00
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:
|
||||
- compatible: Should one of contain:
|
||||
"nxp,pca85073a",
|
||||
"nxp,pcf85063",
|
||||
"nxp,pcf85063a",
|
||||
"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: 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
|
||||
M: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
L: linux-mtd@lists.infradead.org
|
||||
|
@ -1548,6 +1548,13 @@ config RTC_DRV_RS5C313
|
||||
help
|
||||
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
|
||||
tristate "Generic RTC support"
|
||||
# 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_RX8025) += rtc-rx8025.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_S3C) += rtc-s3c.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);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable EXTCLK\n");
|
||||
return ret;
|
||||
goto err_disable_pclk;
|
||||
}
|
||||
}
|
||||
|
||||
rtc->rtc_irq = platform_get_irq(pdev, 0);
|
||||
if (rtc->rtc_irq < 0)
|
||||
return rtc->rtc_irq;
|
||||
if (rtc->rtc_irq < 0) {
|
||||
ret = rtc->rtc_irq;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
if (!res) {
|
||||
ret = -ENODEV;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
rtc->rtc_base = devm_ioremap(dev, res->start,
|
||||
resource_size(res));
|
||||
if (!rtc->rtc_base)
|
||||
return -ENOMEM;
|
||||
if (!rtc->rtc_base) {
|
||||
ret = -ENOMEM;
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
rtc->rtc_dev = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtc_dev))
|
||||
return PTR_ERR(rtc->rtc_dev);
|
||||
if (IS_ERR(rtc->rtc_dev)) {
|
||||
ret = PTR_ERR(rtc->rtc_dev);
|
||||
goto err_disable_extclk;
|
||||
}
|
||||
|
||||
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,
|
||||
IRQF_SHARED, pdev->name, dev);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
goto err_disable_extclk;
|
||||
|
||||
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)
|
||||
|
@ -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);
|
||||
if (ret) {
|
||||
pr_err("failed to get the RTC bias\n");
|
||||
iounmap(hw_srnprot);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -399,7 +399,7 @@ static struct platform_driver meson_rtc_driver = {
|
||||
module_platform_driver(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_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:meson-rtc");
|
||||
|
@ -269,6 +269,8 @@ static int mtk_rtc_probe(struct platform_device *pdev)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
rtc->addr_base = res->start;
|
||||
|
||||
rtc->data = of_device_get_match_data(&pdev->dev);
|
||||
|
@ -311,7 +311,7 @@ static int mxc_rtc_probe(struct platform_device *pdev)
|
||||
if (!pdata)
|
||||
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);
|
||||
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[] = {
|
||||
{ "pca85073a", PCF85063A },
|
||||
{ "pcf85063", PCF85063 },
|
||||
{ "pcf85063tp", PCF85063TP },
|
||||
{ "pcf85063a", PCF85063A },
|
||||
@ -660,6 +661,7 @@ MODULE_DEVICE_TABLE(i2c, pcf85063_ids);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
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,pcf85063tp", .data = &pcf85063_cfg[PCF85063TP] },
|
||||
{ .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);
|
||||
u8 digoff;
|
||||
int err;
|
||||
|
||||
offset /= RX8025_ADJ_RESOLUTION;
|
||||
if (offset > RX8025_ADJ_DATA_MAX)
|
||||
@ -449,11 +448,7 @@ static int rx8025_set_offset(struct device *dev, long offset)
|
||||
offset += 128;
|
||||
digoff = offset;
|
||||
|
||||
err = rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return rx8025_write_reg(client, RX8025_REG_DIGOFF, digoff);
|
||||
}
|
||||
|
||||
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_EN_OFFSET 0
|
||||
|
||||
/* General-purpose data */
|
||||
#define SUN6I_GP_DATA 0x0100
|
||||
#define SUN6I_GP_DATA_SIZE 0x20
|
||||
|
||||
/*
|
||||
* Get date values
|
||||
*/
|
||||
@ -679,6 +683,39 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
|
||||
.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
|
||||
/* Enable IRQ wake on suspend, to wake up from RTC. */
|
||||
static int sun6i_rtc_suspend(struct device *dev)
|
||||
@ -812,6 +849,11 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
|
||||
if (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");
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user