mirror of
https://github.com/torvalds/linux.git
synced 2026-03-07 23:04:33 +01:00
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:
commit
5f2eac7767
14 changed files with 178 additions and 88 deletions
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
|
@ -23,6 +23,7 @@ properties:
|
|||
- loongson,ls1b-rtc
|
||||
- loongson,ls1c-rtc
|
||||
- loongson,ls7a-rtc
|
||||
- loongson,ls2k0300-rtc
|
||||
- loongson,ls2k1000-rtc
|
||||
- items:
|
||||
- enum:
|
||||
|
|
@ -42,6 +43,18 @@ required:
|
|||
|
||||
unevaluatedProperties: false
|
||||
|
||||
if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- loongson,ls1c-rtc
|
||||
- loongson,ls2k0300-rtc
|
||||
|
||||
then:
|
||||
properties:
|
||||
interrupts: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
...
|
||||
|
|
@ -14,6 +14,7 @@ properties:
|
|||
items:
|
||||
- enum:
|
||||
- renesas,r9a08g045-rtca3 # RZ/G3S
|
||||
- renesas,r9a09g056-rtca3 # RZ/V2N
|
||||
- renesas,r9a09g057-rtca3 # RZ/V2H
|
||||
- const: renesas,rz-rtca3
|
||||
|
||||
|
|
@ -82,7 +83,9 @@ allOf:
|
|||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
const: renesas,r9a09g057-rtca3
|
||||
enum:
|
||||
- renesas,r9a09g056-rtca3
|
||||
- renesas,r9a09g057-rtca3
|
||||
then:
|
||||
properties:
|
||||
resets:
|
||||
|
|
|
|||
|
|
@ -418,6 +418,7 @@ config RTC_DRV_SPACEMIT_P1
|
|||
|
||||
config RTC_DRV_NVIDIA_VRS10
|
||||
tristate "NVIDIA VRS10 RTC device"
|
||||
depends on ARCH_TEGRA || COMPILE_TEST
|
||||
help
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
err = __rtc_read_alarm(rtc, &alrm);
|
||||
if (!err && !rtc_valid_tm(&alrm.time))
|
||||
if (!err)
|
||||
rtc_initialize_alarm(rtc, &alrm);
|
||||
|
||||
rtc_dev_prepare(rtc);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
* expired, which is true in both cases.
|
||||
*/
|
||||
if ((scheduled - now) <= 1) {
|
||||
if (!err && (scheduled - now) <= 1) {
|
||||
err = __rtc_read_time(rtc, &tm);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ static int ds1390_read_time(struct device *dev, struct rtc_time *dt)
|
|||
chip->txrx_buf[0] = DS1390_REG_SECONDS;
|
||||
|
||||
/* 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)
|
||||
return status;
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@
|
|||
* According to the LS1C manual, RTC_CTRL and alarm-related registers are not defined.
|
||||
* 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 {
|
||||
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 = {
|
||||
.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 = {
|
||||
|
|
@ -97,6 +98,11 @@ static const struct loongson_rtc_config generic_rtc_config = {
|
|||
.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 = {
|
||||
.pm_offset = 0x800,
|
||||
.flags = 0,
|
||||
|
|
@ -153,7 +159,7 @@ static int loongson_rtc_set_enabled(struct device *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;
|
||||
|
||||
/* Enable RTC TOY counters and crystal */
|
||||
|
|
@ -167,7 +173,7 @@ static bool loongson_rtc_get_enabled(struct device *dev)
|
|||
u32 ctrl_data;
|
||||
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;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
int ret, alarm_irq;
|
||||
int ret;
|
||||
void __iomem *regs;
|
||||
struct loongson_rtc_priv *priv;
|
||||
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),
|
||||
"devm_rtc_allocate_device failed\n");
|
||||
|
||||
/* Get RTC alarm irq */
|
||||
alarm_irq = platform_get_irq(pdev, 0);
|
||||
if (alarm_irq > 0) {
|
||||
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);
|
||||
}
|
||||
ret = loongson_rtc_alarm_setting(pdev, regs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Loongson RTC does not support UIE */
|
||||
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,ls1c-rtc", .data = &ls1c_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 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -591,7 +591,7 @@ static struct nvmem_config max31335_nvmem_cfg = {
|
|||
.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,
|
||||
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)
|
||||
{
|
||||
struct max31335_data *max31335;
|
||||
#if IS_REACHABLE(HWMON)
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
struct device *hwmon;
|
||||
#endif
|
||||
const struct chip_desc *match;
|
||||
|
|
@ -727,7 +727,7 @@ static int max31335_probe(struct i2c_client *client)
|
|||
return dev_err_probe(&client->dev, ret,
|
||||
"cannot register rtc nvmem\n");
|
||||
|
||||
#if IS_REACHABLE(HWMON)
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
if (max31335->chip->temp_reg) {
|
||||
hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name, max31335,
|
||||
&max31335_chip_info, NULL);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
if (ver->impl_id == TEE_IMPL_ID_OPTEE)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
return (ver->impl_id == TEE_IMPL_ID_OPTEE);
|
||||
}
|
||||
|
||||
static int optee_rtc_probe(struct tee_client_device *rtc_device)
|
||||
|
|
|
|||
|
|
@ -424,7 +424,7 @@ static const struct clk_ops pcf8563_clkout_ops = {
|
|||
|
||||
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 *clk;
|
||||
int ret;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#define S35390A_CMD_TIME1 2
|
||||
#define S35390A_CMD_TIME2 3
|
||||
#define S35390A_CMD_INT2_REG1 5
|
||||
#define S35390A_CMD_FREE_REG 7
|
||||
|
||||
#define S35390A_BYTE_YEAR 0
|
||||
#define S35390A_BYTE_MONTH 1
|
||||
|
|
@ -416,6 +417,23 @@ static const struct rtc_class_ops s35390a_rtc_ops = {
|
|||
.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)
|
||||
{
|
||||
int err, err_read;
|
||||
|
|
@ -424,6 +442,15 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
struct rtc_device *rtc;
|
||||
u8 buf, status1;
|
||||
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))
|
||||
return -ENODEV;
|
||||
|
|
@ -490,6 +517,11 @@ static int s35390a_probe(struct i2c_client *client)
|
|||
if (status1 & S35390A_FLAG_INT2)
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,9 +43,7 @@
|
|||
#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
|
||||
#define RTC_PPB 1000000000
|
||||
|
||||
struct xlnx_rtc_dev {
|
||||
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)
|
||||
{
|
||||
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;
|
||||
unsigned int calibval, fract_data, fract_part;
|
||||
int freq = xrtcdev->freq;
|
||||
int max_tick, tick_mult;
|
||||
long offset_val;
|
||||
|
||||
/* Tick to offset multiplier */
|
||||
tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq);
|
||||
|
||||
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;
|
||||
max_tick = calibval & RTC_TICK_MASK;
|
||||
offset_val = max_tick - freq;
|
||||
/* Convert to ppb */
|
||||
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);
|
||||
if (calibval & RTC_FR_EN) {
|
||||
fract_data = (calibval & RTC_FR_MASK) >> RTC_FR_DATSHIFT;
|
||||
fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS);
|
||||
offset_val += (fract_part * fract_data);
|
||||
}
|
||||
|
||||
*offset = offset_val;
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
int max_tick, tick_mult, fract_offset, fract_part;
|
||||
int freq = xrtcdev->freq;
|
||||
unsigned int calibval;
|
||||
short int max_tick;
|
||||
int fract_offset;
|
||||
int fract_data = 0;
|
||||
|
||||
if (offset < RTC_MIN_OFFSET || offset > RTC_MAX_OFFSET)
|
||||
return -ERANGE;
|
||||
/* Tick to offset multiplier */
|
||||
tick_mult = DIV_ROUND_CLOSEST(RTC_PPB, freq);
|
||||
|
||||
/* Number ticks for given 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 */
|
||||
if (fract_offset) {
|
||||
if (fract_offset < 0) {
|
||||
fract_offset = fract_offset + tick_mult;
|
||||
fract_part = DIV_ROUND_UP(tick_mult, RTC_FR_MAX_TICKS);
|
||||
fract_data = fract_offset / fract_part;
|
||||
/* Subtract one from max_tick while adding fract_offset */
|
||||
if (fract_offset < 0 && fract_data) {
|
||||
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;
|
||||
}
|
||||
fract_data += RTC_FR_MAX_TICKS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Zynqmp RTC uses second and fractional tick
|
||||
* counters for compensation
|
||||
*/
|
||||
calibval = max_tick + RTC_CALIB_DEF;
|
||||
calibval = max_tick + freq;
|
||||
|
||||
if (fract_tick)
|
||||
calibval |= RTC_FR_EN;
|
||||
|
||||
calibval |= (fract_tick << RTC_FR_DATSHIFT);
|
||||
if (fract_data)
|
||||
calibval |= (RTC_FR_EN | (fract_data << RTC_FR_DATSHIFT));
|
||||
|
||||
writel(calibval, (xrtcdev->reg_base + RTC_CALIB_WR));
|
||||
|
||||
|
|
@ -345,7 +344,15 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
|
|||
&xrtcdev->freq);
|
||||
if (ret)
|
||||
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);
|
||||
if (!ret)
|
||||
writel(xrtcdev->freq, (xrtcdev->reg_base + RTC_CALIB_WR));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue