RTC for 7.0

Drivers:
  - loongson: Loongson-2K0300 support
  - s35390a: nvmem support
  - zynqmp: rework calibration
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmmaW6UACgkQY6TcMGxw
 OjIEBA//T+wV/SxNKwTZc5+f0nA2ndhvSNQduy1Diguj0yt0pxweENopkRUMtyVh
 5cHWqTNj9R7OWQiUBfAMCia4OgVHGVgp4zRONohFpwa1aKLTJJtgancRcrrKie5A
 MTyzWaOl0odk6utU1MEXMdA7cF1Wr2moPbMEvEyE7LhgT0dF+CM2jD8yGuZawum2
 vH9aTDc6xMOofpjTlxOyb64fK3BJ9PUaSi++HJ22VP88+XS5GOPgpoBnYdA5lSju
 XWW3hAWYekg8F4Km9omEfjXWnJ5qvrJUBNnGz5m5ipp1rU/ZD3eg/kJ5YeNQZOfq
 YALhaNXfkHbrQXXfNHVaL77od2zUvpOWaN6DPbbKlYlqsZnWmD++8w3hVZT5jEP4
 XJ3oNpHjMt2WT5GnF96Ya636ZR36PnZSgb3TgoDgJ9Xx8Ko4ov/v62Mz5v6oxh4P
 NeB0XqnwBxa0YJ/73XVNsiJ54U4LY4k3A9SqZ1mjpHEXFlf+MpErNmgTi0Q/BP81
 RUcNI3GQuTAX/MaXSbceFn0K8XJxG+HKWqW3j0rmjN5BFSYqPGPGNWZaS8SUNwlt
 Xj6aeJRV9RTEc+fZqLss/ztxGXyCGcgM0+OOlXyQsVLHcQZAH36c67auM+OzmZZH
 uMhix3OcawZDlpDugHdJJtau0Clj7xtrrdSZqTuAuJXFIGNNtow=
 =YiJy
 -----END PGP SIGNATURE-----

Merge tag 'rtc-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux

Pull RTC updates from Alexandre Belloni:

 - loongson: Loongson-2K0300 support

 - s35390a: nvmem support

 - zynqmp: rework calibration

* tag 'rtc-7.0' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux:
  rtc: ds1390: fix number of bytes read from RTC
  rtc: class: Remove duplicate check for alarm
  rtc: optee: simplify OP-TEE context match
  rtc: interface: Alarm race handling should not discard preceding error
  rtc: s35390a: implement nvmem support
  rtc: loongson: Add Loongson-2K0300 support
  dt-bindings: rtc: loongson: Document Loongson-2K0300 compatible
  dt-bindings: rtc: loongson: Correct Loongson-1C interrupts property
  dt-bindings: rtc: renesas,rz-rtca3: Add RZ/V2N support
  dt-bindings: rtc: cpcap: convert to schema
  rtc: zynqmp: use dynamic max and min offset ranges
  rtc: zynqmp: rework set_offset
  rtc: zynqmp: rework read_offset
  rtc: zynqmp: check calibration max value
  rtc: zynqmp: correct frequency value
  rtc: amlogic-a4: Remove IRQF_ONESHOT
  rtc: pcf8563: use correct of_node for output clock
  rtc: max31335: use correct CONFIG symbol in IS_REACHABLE()
  rtc: nvvrs: Add ARCH_TEGRA to the NV VRS RTC driver
This commit is contained in:
Linus Torvalds 2026-02-22 09:43:11 -08:00
commit 5f2eac7767
14 changed files with 178 additions and 88 deletions

View file

@ -1,18 +0,0 @@
Motorola CPCAP PMIC RTC
-----------------------
This module is part of the CPCAP. For more details about the whole
chip see Documentation/devicetree/bindings/mfd/motorola-cpcap.txt.
Requires node properties:
- compatible: should contain "motorola,cpcap-rtc"
- interrupts: An interrupt specifier for alarm and 1 Hz irq
Example:
&cpcap {
cpcap_rtc: rtc {
compatible = "motorola,cpcap-rtc";
interrupts = <39 IRQ_TYPE_NONE>, <26 IRQ_TYPE_NONE>;
};
};

View file

@ -23,6 +23,7 @@ properties:
- loongson,ls1b-rtc - loongson,ls1b-rtc
- loongson,ls1c-rtc - loongson,ls1c-rtc
- loongson,ls7a-rtc - loongson,ls7a-rtc
- loongson,ls2k0300-rtc
- loongson,ls2k1000-rtc - loongson,ls2k1000-rtc
- items: - items:
- enum: - enum:
@ -42,6 +43,18 @@ required:
unevaluatedProperties: false unevaluatedProperties: false
if:
properties:
compatible:
contains:
enum:
- loongson,ls1c-rtc
- loongson,ls2k0300-rtc
then:
properties:
interrupts: false
examples: examples:
- | - |
#include <dt-bindings/interrupt-controller/irq.h> #include <dt-bindings/interrupt-controller/irq.h>

View file

@ -0,0 +1,32 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/motorola,cpcap-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Motorola CPCAP PMIC RTC
maintainers:
- Svyatoslav Ryhel <clamor95@gmail.com>
description:
This module is part of the Motorola CPCAP MFD device. For more details
see Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml. The
RTC is represented as a sub-node of the PMIC node on the device tree.
properties:
compatible:
const: motorola,cpcap-rtc
interrupts:
items:
- description: alarm interrupt
- description: 1 Hz interrupt
required:
- compatible
- interrupts
additionalProperties: false
...

View file

@ -14,6 +14,7 @@ properties:
items: items:
- enum: - enum:
- renesas,r9a08g045-rtca3 # RZ/G3S - renesas,r9a08g045-rtca3 # RZ/G3S
- renesas,r9a09g056-rtca3 # RZ/V2N
- renesas,r9a09g057-rtca3 # RZ/V2H - renesas,r9a09g057-rtca3 # RZ/V2H
- const: renesas,rz-rtca3 - const: renesas,rz-rtca3
@ -82,7 +83,9 @@ allOf:
properties: properties:
compatible: compatible:
contains: contains:
const: renesas,r9a09g057-rtca3 enum:
- renesas,r9a09g056-rtca3
- renesas,r9a09g057-rtca3
then: then:
properties: properties:
resets: resets:

View file

@ -418,6 +418,7 @@ config RTC_DRV_SPACEMIT_P1
config RTC_DRV_NVIDIA_VRS10 config RTC_DRV_NVIDIA_VRS10
tristate "NVIDIA VRS10 RTC device" tristate "NVIDIA VRS10 RTC device"
depends on ARCH_TEGRA || COMPILE_TEST
help help
If you say yes here you will get support for the battery backed RTC device If you say yes here you will get support for the battery backed RTC device
of NVIDIA VRS (Voltage Regulator Specification). The RTC is connected via of NVIDIA VRS (Voltage Regulator Specification). The RTC is connected via

View file

@ -410,7 +410,7 @@ int __devm_rtc_register_device(struct module *owner, struct rtc_device *rtc)
/* Check to see if there is an ALARM already set in hw */ /* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm); err = __rtc_read_alarm(rtc, &alrm);
if (!err && !rtc_valid_tm(&alrm.time)) if (!err)
rtc_initialize_alarm(rtc, &alrm); rtc_initialize_alarm(rtc, &alrm);
rtc_dev_prepare(rtc); rtc_dev_prepare(rtc);

View file

@ -457,7 +457,7 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
* are in, we can return -ETIME to signal that the timer has already * are in, we can return -ETIME to signal that the timer has already
* expired, which is true in both cases. * expired, which is true in both cases.
*/ */
if ((scheduled - now) <= 1) { if (!err && (scheduled - now) <= 1) {
err = __rtc_read_time(rtc, &tm); err = __rtc_read_time(rtc, &tm);
if (err) if (err)
return err; return err;

View file

@ -134,7 +134,7 @@ static int ds1390_read_time(struct device *dev, struct rtc_time *dt)
chip->txrx_buf[0] = DS1390_REG_SECONDS; chip->txrx_buf[0] = DS1390_REG_SECONDS;
/* do the i/o */ /* do the i/o */
status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 8); status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 7);
if (status != 0) if (status != 0)
return status; return status;

View file

@ -66,7 +66,8 @@
* According to the LS1C manual, RTC_CTRL and alarm-related registers are not defined. * According to the LS1C manual, RTC_CTRL and alarm-related registers are not defined.
* Accessing the relevant registers will cause the system to hang. * Accessing the relevant registers will cause the system to hang.
*/ */
#define LS1C_RTC_CTRL_WORKAROUND BIT(0) #define LOONGSON_RTC_CTRL_WORKAROUND BIT(0)
#define LOONGSON_RTC_ALARM_WORKAROUND BIT(1)
struct loongson_rtc_config { struct loongson_rtc_config {
u32 pm_offset; /* Offset of PM domain, for RTC alarm wakeup */ u32 pm_offset; /* Offset of PM domain, for RTC alarm wakeup */
@ -89,7 +90,7 @@ static const struct loongson_rtc_config ls1b_rtc_config = {
static const struct loongson_rtc_config ls1c_rtc_config = { static const struct loongson_rtc_config ls1c_rtc_config = {
.pm_offset = 0, .pm_offset = 0,
.flags = LS1C_RTC_CTRL_WORKAROUND, .flags = LOONGSON_RTC_CTRL_WORKAROUND | LOONGSON_RTC_ALARM_WORKAROUND,
}; };
static const struct loongson_rtc_config generic_rtc_config = { static const struct loongson_rtc_config generic_rtc_config = {
@ -97,6 +98,11 @@ static const struct loongson_rtc_config generic_rtc_config = {
.flags = 0, .flags = 0,
}; };
static const struct loongson_rtc_config ls2k0300_rtc_config = {
.pm_offset = 0x0,
.flags = LOONGSON_RTC_ALARM_WORKAROUND,
};
static const struct loongson_rtc_config ls2k1000_rtc_config = { static const struct loongson_rtc_config ls2k1000_rtc_config = {
.pm_offset = 0x800, .pm_offset = 0x800,
.flags = 0, .flags = 0,
@ -153,7 +159,7 @@ static int loongson_rtc_set_enabled(struct device *dev)
{ {
struct loongson_rtc_priv *priv = dev_get_drvdata(dev); struct loongson_rtc_priv *priv = dev_get_drvdata(dev);
if (priv->config->flags & LS1C_RTC_CTRL_WORKAROUND) if (priv->config->flags & LOONGSON_RTC_CTRL_WORKAROUND)
return 0; return 0;
/* Enable RTC TOY counters and crystal */ /* Enable RTC TOY counters and crystal */
@ -167,7 +173,7 @@ static bool loongson_rtc_get_enabled(struct device *dev)
u32 ctrl_data; u32 ctrl_data;
struct loongson_rtc_priv *priv = dev_get_drvdata(dev); struct loongson_rtc_priv *priv = dev_get_drvdata(dev);
if (priv->config->flags & LS1C_RTC_CTRL_WORKAROUND) if (priv->config->flags & LOONGSON_RTC_CTRL_WORKAROUND)
return true; return true;
ret = regmap_read(priv->regmap, RTC_CTRL_REG, &ctrl_data); ret = regmap_read(priv->regmap, RTC_CTRL_REG, &ctrl_data);
@ -299,9 +305,41 @@ static const struct rtc_class_ops loongson_rtc_ops = {
.alarm_irq_enable = loongson_rtc_alarm_irq_enable, .alarm_irq_enable = loongson_rtc_alarm_irq_enable,
}; };
static int loongson_rtc_alarm_setting(struct platform_device *pdev, void __iomem *regs)
{
int ret = 0, alarm_irq;
struct device *dev = &pdev->dev;
struct loongson_rtc_priv *priv = dev_get_drvdata(dev);
if (priv->config->flags & LOONGSON_RTC_ALARM_WORKAROUND) {
/* Loongson-1C/Loongson-2K0300 RTC does not support alarm */
clear_bit(RTC_FEATURE_ALARM, priv->rtcdev->features);
return 0;
}
/* Get RTC alarm irq */
alarm_irq = platform_get_irq(pdev, 0);
if (alarm_irq < 0)
return alarm_irq;
ret = devm_request_irq(dev, alarm_irq, loongson_rtc_isr, 0, "loongson-alarm",
priv);
if (ret < 0)
return ret;
priv->pm_base = regs - priv->config->pm_offset;
device_init_wakeup(dev, true);
if (has_acpi_companion(dev))
acpi_install_fixed_event_handler(ACPI_EVENT_RTC,
loongson_rtc_handler, priv);
return ret;
}
static int loongson_rtc_probe(struct platform_device *pdev) static int loongson_rtc_probe(struct platform_device *pdev)
{ {
int ret, alarm_irq; int ret;
void __iomem *regs; void __iomem *regs;
struct loongson_rtc_priv *priv; struct loongson_rtc_priv *priv;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -330,25 +368,9 @@ static int loongson_rtc_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(priv->rtcdev), return dev_err_probe(dev, PTR_ERR(priv->rtcdev),
"devm_rtc_allocate_device failed\n"); "devm_rtc_allocate_device failed\n");
/* Get RTC alarm irq */ ret = loongson_rtc_alarm_setting(pdev, regs);
alarm_irq = platform_get_irq(pdev, 0); if (ret)
if (alarm_irq > 0) { return ret;
ret = devm_request_irq(dev, alarm_irq, loongson_rtc_isr,
0, "loongson-alarm", priv);
if (ret < 0)
return dev_err_probe(dev, ret, "Unable to request irq %d\n",
alarm_irq);
priv->pm_base = regs - priv->config->pm_offset;
device_init_wakeup(dev, true);
if (has_acpi_companion(dev))
acpi_install_fixed_event_handler(ACPI_EVENT_RTC,
loongson_rtc_handler, priv);
} else {
/* Loongson-1C RTC does not support alarm */
clear_bit(RTC_FEATURE_ALARM, priv->rtcdev->features);
}
/* Loongson RTC does not support UIE */ /* Loongson RTC does not support UIE */
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, priv->rtcdev->features); clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, priv->rtcdev->features);
@ -379,6 +401,7 @@ static const struct of_device_id loongson_rtc_of_match[] = {
{ .compatible = "loongson,ls1b-rtc", .data = &ls1b_rtc_config }, { .compatible = "loongson,ls1b-rtc", .data = &ls1b_rtc_config },
{ .compatible = "loongson,ls1c-rtc", .data = &ls1c_rtc_config }, { .compatible = "loongson,ls1c-rtc", .data = &ls1c_rtc_config },
{ .compatible = "loongson,ls7a-rtc", .data = &generic_rtc_config }, { .compatible = "loongson,ls7a-rtc", .data = &generic_rtc_config },
{ .compatible = "loongson,ls2k0300-rtc", .data = &ls2k0300_rtc_config },
{ .compatible = "loongson,ls2k1000-rtc", .data = &ls2k1000_rtc_config }, { .compatible = "loongson,ls2k1000-rtc", .data = &ls2k1000_rtc_config },
{ /* sentinel */ } { /* sentinel */ }
}; };

View file

@ -591,7 +591,7 @@ static struct nvmem_config max31335_nvmem_cfg = {
.size = MAX31335_RAM_SIZE, .size = MAX31335_RAM_SIZE,
}; };
#if IS_REACHABLE(HWMON) #if IS_REACHABLE(CONFIG_HWMON)
static int max31335_read_temp(struct device *dev, enum hwmon_sensor_types type, static int max31335_read_temp(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val) u32 attr, int channel, long *val)
{ {
@ -672,7 +672,7 @@ static int max31335_clkout_register(struct device *dev)
static int max31335_probe(struct i2c_client *client) static int max31335_probe(struct i2c_client *client)
{ {
struct max31335_data *max31335; struct max31335_data *max31335;
#if IS_REACHABLE(HWMON) #if IS_REACHABLE(CONFIG_HWMON)
struct device *hwmon; struct device *hwmon;
#endif #endif
const struct chip_desc *match; const struct chip_desc *match;
@ -727,7 +727,7 @@ static int max31335_probe(struct i2c_client *client)
return dev_err_probe(&client->dev, ret, return dev_err_probe(&client->dev, ret,
"cannot register rtc nvmem\n"); "cannot register rtc nvmem\n");
#if IS_REACHABLE(HWMON) #if IS_REACHABLE(CONFIG_HWMON)
if (max31335->chip->temp_reg) { if (max31335->chip->temp_reg) {
hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, max31335, hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, max31335,
&max31335_chip_info, NULL); &max31335_chip_info, NULL);

View file

@ -541,10 +541,7 @@ static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
{ {
if (ver->impl_id == TEE_IMPL_ID_OPTEE) return (ver->impl_id == TEE_IMPL_ID_OPTEE);
return 1;
else
return 0;
} }
static int optee_rtc_probe(struct tee_client_device *rtc_device) static int optee_rtc_probe(struct tee_client_device *rtc_device)

View file

@ -424,7 +424,7 @@ static const struct clk_ops pcf8563_clkout_ops = {
static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
{ {
struct device_node *node = pcf8563->rtc->dev.of_node; struct device_node *node = pcf8563->rtc->dev.parent->of_node;
struct clk_init_data init; struct clk_init_data init;
struct clk *clk; struct clk *clk;
int ret; int ret;

View file

@ -18,6 +18,7 @@
#define S35390A_CMD_TIME1 2 #define S35390A_CMD_TIME1 2
#define S35390A_CMD_TIME2 3 #define S35390A_CMD_TIME2 3
#define S35390A_CMD_INT2_REG1 5 #define S35390A_CMD_INT2_REG1 5
#define S35390A_CMD_FREE_REG 7
#define S35390A_BYTE_YEAR 0 #define S35390A_BYTE_YEAR 0
#define S35390A_BYTE_MONTH 1 #define S35390A_BYTE_MONTH 1
@ -416,6 +417,23 @@ static const struct rtc_class_ops s35390a_rtc_ops = {
.ioctl = s35390a_rtc_ioctl, .ioctl = s35390a_rtc_ioctl,
}; };
static int s35390a_nvmem_read(void *priv, unsigned int offset, void *val,
size_t bytes)
{
struct s35390a *s35390a = priv;
/* The offset is ignored because the NVMEM region is only 1 byte */
return s35390a_get_reg(s35390a, S35390A_CMD_FREE_REG, val, bytes);
}
static int s35390a_nvmem_write(void *priv, unsigned int offset, void *val,
size_t bytes)
{
struct s35390a *s35390a = priv;
return s35390a_set_reg(s35390a, S35390A_CMD_FREE_REG, val, bytes);
}
static int s35390a_probe(struct i2c_client *client) static int s35390a_probe(struct i2c_client *client)
{ {
int err, err_read; int err, err_read;
@ -424,6 +442,15 @@ static int s35390a_probe(struct i2c_client *client)
struct rtc_device *rtc; struct rtc_device *rtc;
u8 buf, status1; u8 buf, status1;
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct nvmem_config nvmem_cfg = {
.name = "s35390a_nvram",
.type = NVMEM_TYPE_BATTERY_BACKED,
.word_size = 1,
.stride = 1,
.size = 1,
.reg_read = s35390a_nvmem_read,
.reg_write = s35390a_nvmem_write,
};
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV; return -ENODEV;
@ -490,6 +517,11 @@ static int s35390a_probe(struct i2c_client *client)
if (status1 & S35390A_FLAG_INT2) if (status1 & S35390A_FLAG_INT2)
rtc_update_irq(rtc, 1, RTC_AF); rtc_update_irq(rtc, 1, RTC_AF);
nvmem_cfg.priv = s35390a;
err = devm_rtc_nvmem_register(rtc, &nvmem_cfg);
if (err)
return err;
return devm_rtc_register_device(rtc); return devm_rtc_register_device(rtc);
} }

View file

@ -43,9 +43,7 @@
#define RTC_MSEC 1000 #define RTC_MSEC 1000
#define RTC_FR_MASK 0xF0000 #define RTC_FR_MASK 0xF0000
#define RTC_FR_MAX_TICKS 16 #define RTC_FR_MAX_TICKS 16
#define RTC_PPB 1000000000LL #define RTC_PPB 1000000000
#define RTC_MIN_OFFSET -32768000
#define RTC_MAX_OFFSET 32767000
struct xlnx_rtc_dev { struct xlnx_rtc_dev {
struct rtc_device *rtc; struct rtc_device *rtc;
@ -178,21 +176,28 @@ static void xlnx_init_rtc(struct xlnx_rtc_dev *xrtcdev)
static int xlnx_rtc_read_offset(struct device *dev, long *offset) static int xlnx_rtc_read_offset(struct device *dev, long *offset)
{ {
struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
unsigned long long rtc_ppb = RTC_PPB; unsigned int calibval, fract_data, fract_part;
unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); int freq = xrtcdev->freq;
unsigned int calibval; int max_tick, tick_mult;
long offset_val; long offset_val;
/* Tick to offset multiplier */
tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq);
calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD); calibval = readl(xrtcdev->reg_base + RTC_CALIB_RD);
/* Offset with seconds ticks */ /* Offset with seconds ticks */
offset_val = calibval & RTC_TICK_MASK; max_tick = calibval & RTC_TICK_MASK;
offset_val = offset_val - RTC_CALIB_DEF; offset_val = max_tick - freq;
offset_val = offset_val * tick_mult; /* Convert to ppb */
offset_val *= tick_mult;
/* Offset with fractional ticks */ /* Offset with fractional ticks */
if (calibval & RTC_FR_EN) if (calibval & RTC_FR_EN) {
offset_val += ((calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT) fract_data = (calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT;
* (tick_mult / RTC_FR_MAX_TICKS); fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS);
offset_val += (fract_part * fract_data);
}
*offset = offset_val; *offset = offset_val;
return 0; return 0;
@ -201,44 +206,38 @@ static int xlnx_rtc_read_offset(struct device *dev, long *offset)
static int xlnx_rtc_set_offset(struct device *dev, long offset) static int xlnx_rtc_set_offset(struct device *dev, long offset)
{ {
struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev); struct xlnx_rtc_dev *xrtcdev = dev_get_drvdata(dev);
unsigned long long rtc_ppb = RTC_PPB; int max_tick, tick_mult, fract_offset, fract_part;
unsigned int tick_mult = do_div(rtc_ppb, xrtcdev->freq); int freq = xrtcdev->freq;
unsigned char fract_tick = 0;
unsigned int calibval; unsigned int calibval;
short int max_tick; int fract_data = 0;
int fract_offset;
if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET) /* Tick to offset multiplier */
return -ERANGE; tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq);
/* Number ticks for given offset */ /* Number ticks for given offset */
max_tick = div_s64_rem(offset, tick_mult, &fract_offset); max_tick = div_s64_rem(offset, tick_mult, &fract_offset);
if (freq + max_tick > RTC_TICK_MASK || (freq + max_tick < 1))
return -ERANGE;
/* Number fractional ticks for given offset */ /* Number fractional ticks for given offset */
if (fract_offset) { if (fract_offset) {
if (fract_offset < 0) { fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS);
fract_offset = fract_offset + tick_mult; fract_data = fract_offset / fract_part;
/* Subtract one from max_tick while adding fract_offset */
if (fract_offset < 0 && fract_data) {
max_tick--; max_tick--;
} fract_data += RTC_FR_MAX_TICKS;
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 /* Zynqmp RTC uses second and fractional tick
* counters for compensation * counters for compensation
*/ */
calibval = max_tick + RTC_CALIB_DEF; calibval = max_tick + freq;
if (fract_tick) if (fract_data)
calibval |= RTC_FR_EN; calibval |= (RTC_FR_EN | (fract_data << RTC_FR_DATSHIFT));
calibval |= (fract_tick << RTC_FR_DATSHIFT);
writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR)); writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
@ -345,7 +344,15 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
&xrtcdev->freq); &xrtcdev->freq);
if (ret) if (ret)
xrtcdev->freq = RTC_CALIB_DEF; xrtcdev->freq = RTC_CALIB_DEF;
} else {
xrtcdev->freq--;
} }
if (xrtcdev->freq > RTC_TICK_MASK) {
dev_err(&pdev->dev, "Invalid RTC calibration value\n");
return -EINVAL;
}
ret = readl(xrtcdev->reg_base + RTC_CALIB_RD); ret = readl(xrtcdev->reg_base + RTC_CALIB_RD);
if (!ret) if (!ret)
writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR)); writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR));