Commit 3a00be19 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull RTC updates from Alexandre Belloni:
 "Here is the pull-request for the RTC subsystem for 4.13.

  Subsystem:

   - expose non volatile RAM using nvmem instead of open coding in many
     drivers. Unfortunately, this option has to be enabled by default to
     not break existing users.

   - rtctest can now test for cutoff dates, showing when an RTC will
     start failing to properly save time and date.

   - new RTC registration functions to remove race conditions in drivers

  Newly supported RTCs:

   - Broadcom STB wake-timer

   - Epson RX8130CE

   - Maxim IC DS1308

   - STMicroelectronics STM32H7

  Drivers:

   - ds1307: use regmap, use nvmem, more cleanups

   - ds3232: temperature reading support

   - gemini: renamed to ftrtc010

   - m41t80: use CCF to expose the clock

   - rv8803: use nvmem

   - s3c: many cleanups

   - st-lpc: fix y2106 bug"

* tag 'rtc-4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (51 commits)
  rtc: Remove wrong deprecation comment
  nvmem: include linux/err.h from header
  rtc: st-lpc: make it robust against y2038/2106 bug
  rtc: rtctest: add check for problematic dates
  tools: timer: add rtctest_setdate
  rtc: ds1307: remove ds1307_remove
  rtc: ds1307: use generic nvmem
  rtc: ds1307: switch to rtc_register_device
  rtc: rv8803: remove rv8803_remove
  rtc: rv8803: use generic nvmem support
  rtc: rv8803: switch to rtc_register_device
  rtc: add generic nvmem support
  rtc: at91rm9200: remove race condition
  rtc: introduce new registration method
  rtc: class separate id allocation from registration
  rtc: class separate device allocation from registration
  rtc: stm32: add STM32H7 RTC support
  dt-bindings: rtc: stm32: add support for STM32H7
  rtc: ds1307: add ds1308 variant
  rtc: ds3232: add temperature support
  ...
parents b5e16170 40bf6a35
Broadcom STB wake-up Timer
The Broadcom STB wake-up timer provides a 27Mhz resolution timer, with the
ability to wake up the system from low-power suspend/standby modes.
Required properties:
- compatible : should contain "brcm,brcmstb-waketimer"
- reg : the register start and length for the WKTMR block
- interrupts : The TIMER interrupt
- interrupt-parent: The phandle to the Always-On (AON) Power Management (PM) L2
interrupt controller node
- clocks : The phandle to the UPG fixed clock (27Mhz domain)
Example:
waketimer@f0411580 {
compatible = "brcm,brcmstb-waketimer";
reg = <0xf0411580 0x14>;
interrupts = <0x3>;
interrupt-parent = <&aon_pm_l2_intc>;
clocks = <&upg_fixed>;
};
* Cortina Systems Gemini RTC
Gemini SoC real-time clock.
Required properties:
- compatible : Should be "cortina,gemini-rtc"
Examples:
rtc@45000000 {
compatible = "cortina,gemini-rtc";
reg = <0x45000000 0x100>;
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
};
* Faraday Technology FTRTC010 Real Time Clock
This RTC appears in for example the Storlink Gemini family of
SoCs.
Required properties:
- compatible : Should be one of:
"faraday,ftrtc010"
"cortina,gemini-rtc", "faraday,ftrtc010"
Optional properties:
- clocks: when present should contain clock references to the
PCLK and EXTCLK clocks. Faraday calls the later CLK1HZ and
says the clock should be 1 Hz, but implementers actually seem
to choose different clocks here, like Cortina who chose
32768 Hz (a typical low-power clock).
- clock-names: should name the clocks "PCLK" and "EXTCLK"
respectively.
Examples:
rtc@45000000 {
compatible = "cortina,gemini-rtc";
reg = <0x45000000 0x100>;
interrupts = <17 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&foo 0>, <&foo 1>;
clock-names = "PCLK", "EXTCLK";
};
STM32 Real Time Clock
Required properties:
- compatible: "st,stm32-rtc".
- compatible: can be either "st,stm32-rtc" or "st,stm32h7-rtc", depending on
the device is compatible with stm32(f4/f7) or stm32h7.
- reg: address range of rtc register set.
- clocks: reference to the clock entry ck_rtc.
- clocks: can use up to two clocks, depending on part used:
- "rtc_ck": RTC clock source.
It is required on stm32(f4/f7) and stm32h7.
- "pclk": RTC APB interface clock.
It is not present on stm32(f4/f7).
It is required on stm32h7.
- clock-names: must be "rtc_ck" and "pclk".
It is required only on stm32h7.
- interrupt-parent: phandle for the interrupt controller.
- interrupts: rtc alarm interrupt.
- st,syscfg: phandle for pwrcfg, mandatory to disable/enable backup domain
(RTC registers) write protection.
Optional properties (to override default ck_rtc parent clock):
- assigned-clocks: reference to the ck_rtc clock entry.
- assigned-clock-parents: phandle of the new parent clock of ck_rtc.
Optional properties (to override default rtc_ck parent clock):
- assigned-clocks: reference to the rtc_ck clock entry.
- assigned-clock-parents: phandle of the new parent clock of rtc_ck.
Example:
......@@ -25,3 +33,17 @@ Example:
interrupts = <17 1>;
st,syscfg = <&pwrcfg>;
};
rtc: rtc@58004000 {
compatible = "st,stm32h7-rtc";
reg = <0x58004000 0x400>;
clocks = <&rcc RTCAPB_CK>, <&rcc RTC_CK>;
clock-names = "pclk", "rtc_ck";
assigned-clocks = <&rcc RTC_CK>;
assigned-clock-parents = <&rcc LSE_CK>;
interrupt-parent = <&exti>;
interrupts = <17 1>;
interrupt-names = "alarm";
st,syscfg = <&pwrcfg>;
status = "disabled";
};
Real Time Clock (RTC) Drivers for Linux
=======================================
=======================================
Real Time Clock (RTC) Drivers for Linux
=======================================
When Linux developers talk about a "Real Time Clock", they usually mean
something that tracks wall clock time and is battery backed so that it
......@@ -32,8 +32,8 @@ only issue an alarm up to 24 hours in the future, other hardware may
be able to schedule one any time in the upcoming century.
Old PC/AT-Compatible driver: /dev/rtc
--------------------------------------
Old PC/AT-Compatible driver: /dev/rtc
--------------------------------------
All PCs (even Alpha machines) have a Real Time Clock built into them.
Usually they are built into the chipset of the computer, but some may
......@@ -105,8 +105,8 @@ that will be using this driver. See the code at the end of this document.
(The original /dev/rtc driver was written by Paul Gortmaker.)
New portable "RTC Class" drivers: /dev/rtcN
--------------------------------------------
New portable "RTC Class" drivers: /dev/rtcN
--------------------------------------------
Because Linux supports many non-ACPI and non-PC platforms, some of which
have more than one RTC style clock, it needed a more portable solution
......@@ -136,35 +136,39 @@ a high functionality RTC is integrated into the SOC. That system might read
the system clock from the discrete RTC, but use the integrated one for all
other tasks, because of its greater functionality.
SYSFS INTERFACE
SYSFS interface
---------------
The sysfs interface under /sys/class/rtc/rtcN provides access to various
rtc attributes without requiring the use of ioctls. All dates and times
are in the RTC's timezone, rather than in system time.
date: RTC-provided date
hctosys: 1 if the RTC provided the system time at boot via the
================ ==============================================================
date RTC-provided date
hctosys 1 if the RTC provided the system time at boot via the
CONFIG_RTC_HCTOSYS kernel option, 0 otherwise
max_user_freq: The maximum interrupt rate an unprivileged user may request
max_user_freq The maximum interrupt rate an unprivileged user may request
from this RTC.
name: The name of the RTC corresponding to this sysfs directory
since_epoch: The number of seconds since the epoch according to the RTC
time: RTC-provided time
wakealarm: The time at which the clock will generate a system wakeup
name The name of the RTC corresponding to this sysfs directory
since_epoch The number of seconds since the epoch according to the RTC
time RTC-provided time
wakealarm The time at which the clock will generate a system wakeup
event. This is a one shot wakeup event, so must be reset
after wake if a daily wakeup is required. Format is seconds since
the epoch by default, or if there's a leading +, seconds in the
future, or if there is a leading +=, seconds ahead of the current
alarm.
offset: The amount which the rtc clock has been adjusted in firmware.
after wake if a daily wakeup is required. Format is seconds
since the epoch by default, or if there's a leading +, seconds
in the future, or if there is a leading +=, seconds ahead of
the current alarm.
offset The amount which the rtc clock has been adjusted in firmware.
Visible only if the driver supports clock offset adjustment.
The unit is parts per billion, i.e. The number of clock ticks
which are added to or removed from the rtc's base clock per
billion ticks. A positive value makes a day pass more slowly,
longer, and a negative value makes a day pass more quickly.
*/nvmem The non volatile storage exported as a raw file, as described
in Documentation/nvmem/nvmem.txt
================ ==============================================================
IOCTL INTERFACE
IOCTL interface
---------------
The ioctl() calls supported by /dev/rtc are also supported by the RTC class
......
......@@ -1255,7 +1255,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
T: git git://github.com/ulli-kroll/linux.git
S: Maintained
F: arch/arm/mach-gemini/
F: drivers/rtc/rtc-gemini.c
F: drivers/rtc/rtc-ftrtc010.c
ARM/CSR SIRFPRIMA2 MACHINE SUPPORT
M: Barry Song <baohua@kernel.org>
......
......@@ -77,6 +77,14 @@ config RTC_DEBUG
Say yes here to enable debugging support in the RTC framework
and individual RTC drivers.
config RTC_NVMEM
bool "RTC non volatile storage support"
select NVMEM
default RTC_CLASS
help
Say yes here to add support for the non volatile (often battery
backed) storage present on RTCs.
comment "RTC interfaces"
config RTC_INTF_SYSFS
......@@ -197,6 +205,17 @@ config RTC_DRV_AC100
This driver can also be built as a module. If so, the module
will be called rtc-ac100.
config RTC_DRV_BRCMSTB
tristate "Broadcom STB wake-timer"
depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
default ARCH_BRCMSTB || BMIPS_GENERIC
help
If you say yes here you get support for the wake-timer found on
Broadcom STB SoCs (BCM7xxx).
This driver can also be built as a module. If so, the module will
be called rtc-brcmstb-waketimer.
config RTC_DRV_AS3722
tristate "ams AS3722 RTC driver"
depends on MFD_AS3722
......@@ -791,6 +810,14 @@ config RTC_DRV_DS3232
This driver can also be built as a module. If so, the module
will be called rtc-ds3232.
config RTC_DRV_DS3232_HWMON
bool "HWMON support for Dallas/Maxim DS3232/DS3234"
depends on RTC_DRV_DS3232 && HWMON && !(RTC_DRV_DS3232=y && HWMON=m)
default y
help
Say Y here if you want to expose temperature sensor data on
rtc-ds3232
config RTC_DRV_PCF2127
tristate "NXP PCF2127"
depends on RTC_I2C_AND_SPI
......@@ -1484,16 +1511,16 @@ config RTC_DRV_ARMADA38X
This driver can also be built as a module. If so, the module
will be called armada38x-rtc.
config RTC_DRV_GEMINI
tristate "Gemini SoC RTC"
depends on ARCH_GEMINI || COMPILE_TEST
config RTC_DRV_FTRTC010
tristate "Faraday Technology FTRTC010 RTC"
depends on HAS_IOMEM
default ARCH_GEMINI
help
If you say Y here you will get support for the
RTC found on Gemini SoC's.
Faraday Technolog FTRTC010 found on e.g. Gemini SoC's.
This driver can also be built as a module. If so, the module
will be called rtc-gemini.
will be called rtc-ftrtc010.
config RTC_DRV_PS3
tristate "PS3 RTC"
......
......@@ -15,6 +15,7 @@ ifdef CONFIG_RTC_DRV_EFI
rtc-core-y += rtc-efi-platform.o
endif
rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
......@@ -36,6 +37,7 @@ obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
obj-$(CONFIG_RTC_DRV_BRCMSTB) += rtc-brcmstb-waketimer.o
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
......@@ -67,7 +69,7 @@ obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
obj-$(CONFIG_RTC_DRV_EM3027) += rtc-em3027.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
obj-$(CONFIG_RTC_DRV_GEMINI) += rtc-gemini.o
obj-$(CONFIG_RTC_DRV_FTRTC010) += rtc-ftrtc010.o
obj-$(CONFIG_RTC_DRV_GENERIC) += rtc-generic.o
obj-$(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o
obj-$(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o
......
......@@ -150,6 +150,63 @@ static SIMPLE_DEV_PM_OPS(rtc_class_dev_pm_ops, rtc_suspend, rtc_resume);
#define RTC_CLASS_DEV_PM_OPS NULL
#endif
/* Ensure the caller will set the id before releasing the device */
static struct rtc_device *rtc_allocate_device(void)
{
struct rtc_device *rtc;
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return NULL;
device_initialize(&rtc->dev);
rtc->irq_freq = 1;
rtc->max_user_freq = 64;
rtc->dev.class = rtc_class;
rtc->dev.groups = rtc_get_dev_attribute_groups();
rtc->dev.release = rtc_device_release;
mutex_init(&rtc->ops_lock);
spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock);
init_waitqueue_head(&rtc->irq_queue);
/* Init timerqueue */
timerqueue_init_head(&rtc->timerqueue);
INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
/* Init aie timer */
rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
/* Init uie timer */
rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
/* Init pie timer */
hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
rtc->pie_timer.function = rtc_pie_update_irq;
rtc->pie_enabled = 0;
return rtc;
}
static int rtc_device_get_id(struct device *dev)
{
int of_id = -1, id = -1;
if (dev->of_node)
of_id = of_alias_get_id(dev->of_node, "rtc");
else if (dev->parent && dev->parent->of_node)
of_id = of_alias_get_id(dev->parent->of_node, "rtc");
if (of_id >= 0) {
id = ida_simple_get(&rtc_ida, of_id, of_id + 1, GFP_KERNEL);
if (id < 0)
dev_warn(dev, "/aliases ID %d not available\n", of_id);
}
if (id < 0)
id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
return id;
}
/**
* rtc_device_register - register w/ RTC class
......@@ -166,65 +223,25 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
{
struct rtc_device *rtc;
struct rtc_wkalrm alrm;
int of_id = -1, id = -1, err;
if (dev->of_node)
of_id = of_alias_get_id(dev->of_node, "rtc");
else if (dev->parent && dev->parent->of_node)
of_id = of_alias_get_id(dev->parent->of_node, "rtc");
if (of_id >= 0) {
id = ida_simple_get(&rtc_ida, of_id, of_id + 1,
GFP_KERNEL);
if (id < 0)
dev_warn(dev, "/aliases ID %d not available\n",
of_id);
}
int id, err;
if (id < 0) {
id = ida_simple_get(&rtc_ida, 0, 0, GFP_KERNEL);
id = rtc_device_get_id(dev);
if (id < 0) {
err = id;
goto exit;
}
}
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
if (rtc == NULL) {
rtc = rtc_allocate_device();
if (!rtc) {
err = -ENOMEM;
goto exit_ida;
}
device_initialize(&rtc->dev);
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->irq_freq = 1;
rtc->max_user_freq = 64;
rtc->dev.parent = dev;
rtc->dev.class = rtc_class;
rtc->dev.groups = rtc_get_dev_attribute_groups();
rtc->dev.release = rtc_device_release;
mutex_init(&rtc->ops_lock);
spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock);
init_waitqueue_head(&rtc->irq_queue);
/* Init timerqueue */
timerqueue_init_head(&rtc->timerqueue);
INIT_WORK(&rtc->irqwork, rtc_timer_do_work);
/* Init aie timer */
rtc_timer_init(&rtc->aie_timer, rtc_aie_update_irq, (void *)rtc);
/* Init uie timer */
rtc_timer_init(&rtc->uie_rtctimer, rtc_uie_update_irq, (void *)rtc);
/* Init pie timer */
hrtimer_init(&rtc->pie_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
rtc->pie_timer.function = rtc_pie_update_irq;
rtc->pie_enabled = 0;
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
dev_set_name(&rtc->dev, "rtc%d", id);
/* Check to see if there is an ALARM already set in hw */
......@@ -238,20 +255,20 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
if (err) {
dev_warn(&rtc->dev, "%s: failed to add char device %d:%d\n",
rtc->name, MAJOR(rtc->dev.devt), rtc->id);
name, MAJOR(rtc->dev.devt), rtc->id);
/* This will free both memory and the ID */
put_device(&rtc->dev);
goto exit;
} else {
dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", rtc->name,
dev_dbg(&rtc->dev, "%s: dev (%d:%d)\n", name,
MAJOR(rtc->dev.devt), rtc->id);
}
rtc_proc_add_device(rtc);
dev_info(dev, "rtc core: registered %s as %s\n",
rtc->name, dev_name(&rtc->dev));
name, dev_name(&rtc->dev));
return rtc;
......@@ -273,6 +290,8 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
*/
void rtc_device_unregister(struct rtc_device *rtc)
{
rtc_nvmem_unregister(rtc);
mutex_lock(&rtc->ops_lock);
/*
* Remove innards of this RTC, then disable it, before
......@@ -356,6 +375,91 @@ void devm_rtc_device_unregister(struct device *dev, struct rtc_device *rtc)
}
EXPORT_SYMBOL_GPL(devm_rtc_device_unregister);
static void devm_rtc_release_device(struct device *dev, void *res)
{
struct rtc_device *rtc = *(struct rtc_device **)res;
if (rtc->registered)
rtc_device_unregister(rtc);
else
put_device(&rtc->dev);
}
struct rtc_device *devm_rtc_allocate_device(struct device *dev)
{
struct rtc_device **ptr, *rtc;
int id, err;
id = rtc_device_get_id(dev);
if (id < 0)
return ERR_PTR(id);
ptr = devres_alloc(devm_rtc_release_device, sizeof(*ptr), GFP_KERNEL);
if (!ptr) {
err = -ENOMEM;
goto exit_ida;
}
rtc = rtc_allocate_device();
if (!rtc) {
err = -ENOMEM;
goto exit_devres;
}
*ptr = rtc;
devres_add(dev, ptr);
rtc->id = id;
rtc->dev.parent = dev;
dev_set_name(&rtc->dev, "rtc%d", id);
return rtc;
exit_devres:
devres_free(ptr);
exit_ida:
ida_simple_remove(&rtc_ida, id);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(devm_rtc_allocate_device);
int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
{
struct rtc_wkalrm alrm;
int err;
if (!rtc->ops)
return -EINVAL;
rtc->owner = owner;
/* 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))
rtc_initialize_alarm(rtc, &alrm);
rtc_dev_prepare(rtc);
err = cdev_device_add(&rtc->char_dev, &rtc->dev);
if (err)
dev_warn(rtc->dev.parent, "failed to add char device %d:%d\n",
MAJOR(rtc->dev.devt), rtc->id);
else
dev_dbg(rtc->dev.parent, "char device (%d:%d)\n",
MAJOR(rtc->dev.devt), rtc->id);
rtc_proc_add_device(rtc);
rtc_nvmem_register(rtc);
rtc->registered = true;
dev_info(rtc->dev.parent, "registered as %s\n",
dev_name(&rtc->dev));
return 0;
}
EXPORT_SYMBOL_GPL(__rtc_register_device);
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
......
......@@ -227,6 +227,13 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
missing = year;
}
/* Can't proceed if alarm is still invalid after replacing
* missing fields.
*/
err = rtc_valid_tm(&alarm->time);
if (err)
goto done;
/* with luck, no rollover is needed */
t_now = rtc_tm_to_time64(&now);
t_alm = rtc_tm_to_time64(&alarm->time);
......@@ -278,9 +285,9 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
dev_warn(&rtc->dev, "alarm rollover not handled\n");
}
done:
err = rtc_valid_tm(&alarm->time);
done:
if (err) {
dev_warn(&rtc->dev, "invalid alarm value: %d-%d-%d %d:%d:%d\n",
alarm->time.tm_year + 1900, alarm->time.tm_mon + 1,
......
/*
* RTC subsystem, nvmem interface
*
* Copyright (C) 2017 Alexandre Belloni
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/err.h>
#include <linux/types.h>
#include <linux/nvmem-consumer.h>
#include <linux/rtc.h>
#include <linux/sysfs.h>
#include "rtc-core.h"
/*
* Deprecated ABI compatibility, this should be removed at some point
*/
static const char nvram_warning[] = "Deprecated ABI, please use nvmem";
static ssize_t
rtc_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct rtc_device *rtc = attr->private;
dev_warn_once(kobj_to_dev(kobj), nvram_warning);
return nvmem_device_read(rtc->nvmem, off, count, buf);
}
static ssize_t
rtc_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct rtc_device *rtc = attr->private;
dev_warn_once(kobj_to_dev(kobj), nvram_warning);
return nvmem_device_write(rtc->nvmem, off, count, buf);
}
static int rtc_nvram_register(struct rtc_device *rtc)
{
int err;
rtc->nvram = devm_kzalloc(rtc->dev.parent,
sizeof(struct bin_attribute),
GFP_KERNEL);
if (!rtc->nvram)
return -ENOMEM;
rtc->nvram->attr.name = "nvram";
rtc->nvram->attr.mode = 0644;
rtc->nvram->private = rtc;
sysfs_bin_attr_init(rtc->nvram);
rtc->nvram->read = rtc_nvram_read;
rtc->nvram->write = rtc_nvram_write;
rtc->nvram->size = rtc->nvmem_config->size;
err = sysfs_create_bin_file(&rtc->dev.parent->kobj,
rtc->nvram);
if (err) {
devm_kfree(rtc->dev.parent, rtc->nvram);
rtc->nvram = NULL;
}
return err;
}
static void rtc_nvram_unregister(struct rtc_device *rtc)
{
sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram);
}
/*
* New ABI, uses nvmem
*/
void rtc_nvmem_register(struct rtc_device *rtc)
{
if (!rtc->nvmem_config)
return;
rtc->nvmem_config->dev = &rtc->dev;
rtc->nvmem_config->owner = rtc->owner;
rtc->nvmem = nvmem_register(rtc->nvmem_config);
if (IS_ERR_OR_NULL(rtc->nvmem))
return;
/* Register the old ABI */
if (rtc->nvram_old_abi)
rtc_nvram_register(rtc);
}
void rtc_nvmem_unregister(struct rtc_device *rtc)
{
if (IS_ERR_OR_NULL(rtc->nvmem))
return;
/* unregister the old ABI */
if (rtc->nvram)
rtc_nvram_unregister(rtc);
nvmem_unregister(rtc->nvmem);
}
......@@ -409,6 +409,11 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
}
rtc = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
platform_set_drvdata(pdev, rtc);
sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk))
return PTR_ERR(sclk);
......@@ -441,13 +446,10 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
if (!device_can_wakeup(&pdev->dev))
device_init_wakeup(&pdev->dev, 1);
rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&at91_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
rtc->ops = &at91_rtc_ops;
ret = rtc_register_device(rtc);
if (ret)
goto err_clk;
}
platform_set_drvdata(pdev, rtc);
/* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy
* completion.
......
/*
* Copyright © 2014-2017 Broadcom
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irqreturn.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/pm_wakeup.h>
#include <linux/reboot.h>
#include <linux/rtc.h>
#include <linux/stat.h>
#include <linux/suspend.h>
struct brcmstb_waketmr {
struct rtc_device *rtc;
struct device *dev;
void __iomem *base;
int irq;
struct notifier_block reboot_notifier;
struct clk *clk;
u32 rate;
};
#define BRCMSTB_WKTMR_EVENT 0x00
#define BRCMSTB_WKTMR_COUNTER 0x04
#define BRCMSTB_WKTMR_ALARM 0x08
#define BRCMSTB_WKTMR_PRESCALER 0x0C
#define BRCMSTB_WKTMR_PRESCALER_VAL 0x10
#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000
static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)
{
writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT);
(void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
}
static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
unsigned int secs)
{
brcmstb_waketmr_clear_alarm(timer);
writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM);
}
static irqreturn_t brcmstb_waketmr_irq(int irq, void *data)
{
struct brcmstb_waketmr *timer = data;
pm_wakeup_event(timer->dev, 0);
return IRQ_HANDLED;
}
struct wktmr_time {
u32 sec;
u32 pre;
};
static void wktmr_read(struct brcmstb_waketmr *timer,
struct wktmr_time *t)
{
u32 tmp;
do {
t->sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
tmp = readl_relaxed(timer->base + BRCMSTB_WKTMR_PRESCALER_VAL);
} while (tmp >= timer->rate);
t->pre = timer->rate - tmp;
}
static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer)
{
struct device *dev = timer->dev;
int ret = 0;
if (device_may_wakeup(dev)) {
ret = enable_irq_wake(timer->irq);
if (ret) {
dev_err(dev, "failed to enable wake-up interrupt\n");
return ret;
}
}
return ret;
}
/* If enabled as a wakeup-source, arm the timer when powering off */
static int brcmstb_waketmr_reboot(struct notifier_block *nb,
unsigned long action, void *data)
{
struct brcmstb_waketmr *timer;
timer = container_of(nb, struct brcmstb_waketmr, reboot_notifier);
/* Set timer for cold boot */
if (action == SYS_POWER_OFF)
brcmstb_waketmr_prepare_suspend(timer);
return NOTIFY_DONE;
}
static int brcmstb_waketmr_gettime(struct device *dev,
struct rtc_time *tm)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
struct wktmr_time now;
wktmr_read(timer, &now);
rtc_time_to_tm(now.sec, tm);
return 0;
}
static int brcmstb_waketmr_settime(struct device *dev,
struct rtc_time *tm)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
time64_t sec;
sec = rtc_tm_to_time64(tm);
if (sec > U32_MAX || sec < 0)
return -EINVAL;
writel_relaxed(sec, timer->base + BRCMSTB_WKTMR_COUNTER);
return 0;
}
static int brcmstb_waketmr_getalarm(struct device *dev,
struct rtc_wkalrm *alarm)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
time64_t sec;
u32 reg;
sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM);
if (sec != 0) {
/* Alarm is enabled */
alarm->enabled = 1;
rtc_time64_to_tm(sec, &alarm->time);
}
reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
alarm->pending = !!(reg & 1);
return 0;
}
static int brcmstb_waketmr_setalarm(struct device *dev,
struct rtc_wkalrm *alarm)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
time64_t sec;
if (alarm->enabled)
sec = rtc_tm_to_time64(&alarm->time);
else
sec = 0;
if (sec > U32_MAX || sec < 0)
return -EINVAL;
brcmstb_waketmr_set_alarm(timer, sec);
return 0;
}
/*
* Does not do much but keep the RTC class happy. We always support
* alarms.
*/
static int brcmstb_waketmr_alarm_enable(struct device *dev,
unsigned int enabled)
{
return 0;
}
static const struct rtc_class_ops brcmstb_waketmr_ops = {
.read_time = brcmstb_waketmr_gettime,
.set_time = brcmstb_waketmr_settime,
.read_alarm = brcmstb_waketmr_getalarm,
.set_alarm = brcmstb_waketmr_setalarm,
.alarm_irq_enable = brcmstb_waketmr_alarm_enable,
};
static int brcmstb_waketmr_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct brcmstb_waketmr *timer;
struct resource *res;
int ret;
timer = devm_kzalloc(dev, sizeof(*timer), GFP_KERNEL);
if (!timer)
return -ENOMEM;
platform_set_drvdata(pdev, timer);
timer->dev = dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
timer->base = devm_ioremap_resource(dev, res);
if (IS_ERR(timer->base))
return PTR_ERR(timer->base);
/*
* Set wakeup capability before requesting wakeup interrupt, so we can
* process boot-time "wakeups" (e.g., from S5 soft-off)
*/
device_set_wakeup_capable(dev, true);
device_wakeup_enable(dev);
timer->irq = platform_get_irq(pdev, 0);
if (timer->irq < 0)
return -ENODEV;
timer->clk = devm_clk_get(dev, NULL);
if (!IS_ERR(timer->clk)) {
ret = clk_prepare_enable(timer->clk);
if (ret)
return ret;
timer->rate = clk_get_rate(timer->clk);
if (!timer->rate)
timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;
} else {
timer->rate = BRCMSTB_WKTMR_DEFAULT_FREQ;
timer->clk = NULL;
}
ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0,
"brcmstb-waketimer", timer);
if (ret < 0)
return ret;
timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;
register_reboot_notifier(&timer->reboot_notifier);
timer->rtc = rtc_device_register("brcmstb-waketmr", dev,
&brcmstb_waketmr_ops, THIS_MODULE);
if (IS_ERR(timer->rtc)) {
dev_err(dev, "unable to register device\n");
unregister_reboot_notifier(&timer->reboot_notifier);
return PTR_ERR(timer->rtc);
}
dev_info(dev, "registered, with irq %d\n", timer->irq);
return ret;
}
static int brcmstb_waketmr_remove(struct platform_device *pdev)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(&pdev->dev);
unregister_reboot_notifier(&timer->reboot_notifier);
rtc_device_unregister(timer->rtc);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int brcmstb_waketmr_suspend(struct device *dev)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
return brcmstb_waketmr_prepare_suspend(timer);
}
static int brcmstb_waketmr_resume(struct device *dev)
{
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
int ret;
if (!device_may_wakeup(dev))
return 0;
ret = disable_irq_wake(timer->irq);
brcmstb_waketmr_clear_alarm(timer);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(brcmstb_waketmr_pm_ops,
brcmstb_waketmr_suspend, brcmstb_waketmr_resume);
static const struct of_device_id brcmstb_waketmr_of_match[] = {
{ .compatible = "brcm,brcmstb-waketimer" },
{ /* sentinel */ },
};
static struct platform_driver brcmstb_waketmr_driver = {
.probe = brcmstb_waketmr_probe,
.remove = brcmstb_waketmr_remove,
.driver = {
.name = "brcmstb-waketimer",
.pm = &brcmstb_waketmr_pm_ops,
.of_match_table = of_match_ptr(brcmstb_waketmr_of_match),
}
};
module_platform_driver(brcmstb_waketmr_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Brian Norris");
MODULE_AUTHOR("Markus Mayer");
MODULE_DESCRIPTION("Wake-up timer driver for STB chips");
......@@ -45,3 +45,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void)
return NULL;
}
#endif
#ifdef CONFIG_RTC_NVMEM
void rtc_nvmem_register(struct rtc_device *rtc);
void rtc_nvmem_unregister(struct rtc_device *rtc);
#else
static inline void rtc_nvmem_register(struct rtc_device *rtc) {}
static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {}
#endif
......@@ -464,7 +464,7 @@ void rtc_dev_prepare(struct rtc_device *rtc)
return;
if (rtc->id >= RTC_DEV_MAX) {
dev_dbg(&rtc->dev, "%s: too many RTC devices\n", rtc->name);
dev_dbg(&rtc->dev, "too many RTC devices\n");
return;
}
......
This diff is collapsed.
......@@ -22,6 +22,7 @@
#include <linux/bcd.h>
#include <linux/slab.h>
#include <linux/regmap.h>
#include <linux/hwmon.h>
#define DS3232_REG_SECONDS 0x00
#define DS3232_REG_MINUTES 0x01
......@@ -46,6 +47,8 @@
# define DS3232_REG_SR_A2F 0x02
# define DS3232_REG_SR_A1F 0x01
#define DS3232_REG_TEMPERATURE 0x11
struct ds3232 {
struct device *dev;
struct regmap *regmap;
......@@ -275,6 +278,120 @@ static int ds3232_update_alarm(struct device *dev, unsigned int enabled)
return ret;
}
/*
* Temperature sensor support for ds3232/ds3234 devices.
* A user-initiated temperature conversion is not started by this function,
* so the temperature is updated once every 64 seconds.
*/
static int ds3232_hwmon_read_temp(struct device *dev, long int *mC)
{
struct ds3232 *ds3232 = dev_get_drvdata(dev);
u8 temp_buf[2];
s16 temp;
int ret;
ret = regmap_bulk_read(ds3232->regmap, DS3232_REG_TEMPERATURE, temp_buf,
sizeof(temp_buf));
if (ret < 0)
return ret;
/*
* Temperature is represented as a 10-bit code with a resolution of
* 0.25 degree celsius and encoded in two's complement format.
*/
temp = (temp_buf[0] << 8) | temp_buf[1];
temp >>= 6;
*mC = temp * 250;
return 0;
}
static umode_t ds3232_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
return 0444;
default:
return 0;
}
}
static int ds3232_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *temp)
{
int err;
switch (attr) {
case hwmon_temp_input:
err = ds3232_hwmon_read_temp(dev, temp);
break;
default:
err = -EOPNOTSUPP;
break;
}
return err;
}
static u32 ds3232_hwmon_chip_config[] = {
HWMON_C_REGISTER_TZ,
0
};
static const struct hwmon_channel_info ds3232_hwmon_chip = {
.type = hwmon_chip,
.config = ds3232_hwmon_chip_config,
};
static u32 ds3232_hwmon_temp_config[] = {
HWMON_T_INPUT,
0
};
static const struct hwmon_channel_info ds3232_hwmon_temp = {
.type = hwmon_temp,
.config = ds3232_hwmon_temp_config,
};
static const struct hwmon_channel_info *ds3232_hwmon_info[] = {
&ds3232_hwmon_chip,
&ds3232_hwmon_temp,
NULL
};
static const struct hwmon_ops ds3232_hwmon_hwmon_ops = {
.is_visible = ds3232_hwmon_is_visible,
.read = ds3232_hwmon_read,
};
static const struct hwmon_chip_info ds3232_hwmon_chip_info = {
.ops = &ds3232_hwmon_hwmon_ops,
.info = ds3232_hwmon_info,
};
static void ds3232_hwmon_register(struct device *dev, const char *name)
{
struct ds3232 *ds3232 = dev_get_drvdata(dev);
struct device *hwmon_dev;
if (!IS_ENABLED(CONFIG_RTC_DRV_DS3232_HWMON))
return;
hwmon_dev = devm_hwmon_device_register_with_info(dev, name, ds3232,
&ds3232_hwmon_chip_info,
NULL);
if (IS_ERR(hwmon_dev)) {
dev_err(dev, "unable to register hwmon device %ld\n",
PTR_ERR(hwmon_dev));
}
}
static int ds3232_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct ds3232 *ds3232 = dev_get_drvdata(dev);
......@@ -366,6 +483,8 @@ static int ds3232_probe(struct device *dev, struct regmap *regmap, int irq,
if (ds3232->irq > 0)
device_init_wakeup(dev, 1);
ds3232_hwmon_register(dev, name);
ds3232->rtc = devm_rtc_device_register(dev, name, &ds3232_rtc_ops,
THIS_MODULE);
if (IS_ERR(ds3232->rtc))
......
/*
* Gemini OnChip RTC
* Faraday Technology FTRTC010 driver
*
* Copyright (C) 2009 Janos Laube <janos.dev@gmail.com>
*
......@@ -26,33 +26,36 @@
#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#define DRV_NAME "rtc-gemini"
#define DRV_NAME "rtc-ftrtc010"
MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>");
MODULE_DESCRIPTION("RTC driver for Gemini SoC");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
struct gemini_rtc {
struct ftrtc010_rtc {
struct rtc_device *rtc_dev;
void __iomem *rtc_base;
int rtc_irq;
struct clk *pclk;
struct clk *extclk;
};
enum gemini_rtc_offsets {
GEMINI_RTC_SECOND = 0x00,
GEMINI_RTC_MINUTE = 0x04,
GEMINI_RTC_HOUR = 0x08,
GEMINI_RTC_DAYS = 0x0C,
GEMINI_RTC_ALARM_SECOND = 0x10,
GEMINI_RTC_ALARM_MINUTE = 0x14,
GEMINI_RTC_ALARM_HOUR = 0x18,
GEMINI_RTC_RECORD = 0x1C,
GEMINI_RTC_CR = 0x20
enum ftrtc010_rtc_offsets {
FTRTC010_RTC_SECOND = 0x00,
FTRTC010_RTC_MINUTE = 0x04,
FTRTC010_RTC_HOUR = 0x08,
FTRTC010_RTC_DAYS = 0x0C,
FTRTC010_RTC_ALARM_SECOND = 0x10,
FTRTC010_RTC_ALARM_MINUTE = 0x14,
FTRTC010_RTC_ALARM_HOUR = 0x18,
FTRTC010_RTC_RECORD = 0x1C,
FTRTC010_RTC_CR = 0x20,
};
static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev)
{
return IRQ_HANDLED;
}
......@@ -66,18 +69,18 @@ static irqreturn_t gemini_rtc_interrupt(int irq, void *dev)
* the same thing, without the rtc-lib.c calls.
*/
static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct gemini_rtc *rtc = dev_get_drvdata(dev);
struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
unsigned int days, hour, min, sec;
unsigned long offset, time;
sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
days = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
offset = readl(rtc->rtc_base + GEMINI_RTC_RECORD);
sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD);
time = offset + days * 86400 + hour * 3600 + min * 60 + sec;
......@@ -86,9 +89,9 @@ static int gemini_rtc_read_time(struct device *dev, struct rtc_time *tm)
return 0;
}
static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct gemini_rtc *rtc = dev_get_drvdata(dev);
struct ftrtc010_rtc *rtc = dev_get_drvdata(dev);
unsigned int sec, min, hour, day;
unsigned long offset, time;
......@@ -97,27 +100,27 @@ static int gemini_rtc_set_time(struct device *dev, struct rtc_time *tm)
rtc_tm_to_time(tm, &time);
sec = readl(rtc->rtc_base + GEMINI_RTC_SECOND);
min = readl(rtc->rtc_base + GEMINI_RTC_MINUTE);
hour = readl(rtc->rtc_base + GEMINI_RTC_HOUR);
day = readl(rtc->rtc_base + GEMINI_RTC_DAYS);
sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND);
min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE);
hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR);
day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS);
offset = time - (day * 86400 + hour * 3600 + min * 60 + sec);
writel(offset, rtc->rtc_base + GEMINI_RTC_RECORD);
writel(0x01, rtc->rtc_base + GEMINI_RTC_CR);
writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD);
writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR);
return 0;
}
static const struct rtc_class_ops gemini_rtc_ops = {
.read_time = gemini_rtc_read_time,
.set_time = gemini_rtc_set_time,
static const struct rtc_class_ops ftrtc010_rtc_ops = {
.read_time = ftrtc010_rtc_read_time,
.set_time = ftrtc010_rtc_set_time,
};
static int gemini_rtc_probe(struct platform_device *pdev)
static int ftrtc010_rtc_probe(struct platform_device *pdev)
{
struct gemini_rtc *rtc;
struct ftrtc010_rtc *rtc;
struct device *dev = &pdev->dev;
struct resource *res;
int ret;
......@@ -127,6 +130,27 @@ static int gemini_rtc_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, rtc);
rtc->pclk = devm_clk_get(dev, "PCLK");
if (IS_ERR(rtc->pclk)) {
dev_err(dev, "could not get PCLK\n");
} else {
ret = clk_prepare_enable(rtc->pclk);
if (ret) {
dev_err(dev, "failed to enable PCLK\n");
return ret;
}
}
rtc->extclk = devm_clk_get(dev, "EXTCLK");
if (IS_ERR(rtc->extclk)) {
dev_err(dev, "could not get EXTCLK\n");
} else {
ret = clk_prepare_enable(rtc->extclk);
if (ret) {
dev_err(dev, "failed to enable EXTCLK\n");
return ret;
}
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res)
return -ENODEV;
......@@ -142,38 +166,43 @@ static int gemini_rtc_probe(struct platform_device *pdev)
if (!rtc->rtc_base)
return -ENOMEM;
ret = devm_request_irq(dev, rtc->rtc_irq, gemini_rtc_interrupt,
ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt,
IRQF_SHARED, pdev->name, dev);
if (unlikely(ret))
return ret;
rtc->rtc_dev = rtc_device_register(pdev->name, dev,
&gemini_rtc_ops, THIS_MODULE);
&ftrtc010_rtc_ops, THIS_MODULE);
return PTR_ERR_OR_ZERO(rtc->rtc_dev);
}
static int gemini_rtc_remove(struct platform_device *pdev)
static int ftrtc010_rtc_remove(struct platform_device *pdev)
{
struct gemini_rtc *rtc = platform_get_drvdata(pdev);
struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev);
if (!IS_ERR(rtc->extclk))
clk_disable_unprepare(rtc->extclk);
if (!IS_ERR(rtc->pclk))
clk_disable_unprepare(rtc->pclk);
rtc_device_unregister(rtc->rtc_dev);
return 0;
}
static const struct of_device_id gemini_rtc_dt_match[] = {
static const struct of_device_id ftrtc010_rtc_dt_match[] = {
{ .compatible = "cortina,gemini-rtc" },
{ .compatible = "faraday,ftrtc010" },
{ }
};
MODULE_DEVICE_TABLE(of, gemini_rtc_dt_match);
MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match);
static struct platform_driver gemini_rtc_driver = {
static struct platform_driver ftrtc010_rtc_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = gemini_rtc_dt_match,
.of_match_table = ftrtc010_rtc_dt_match,
},
.probe = gemini_rtc_probe,
.remove = gemini_rtc_remove,
.probe = ftrtc010_rtc_probe,
.remove = ftrtc010_rtc_remove,
};
module_platform_driver_probe(gemini_rtc_driver, gemini_rtc_probe);
module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe);
......@@ -16,6 +16,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bcd.h>
#include <linux/clk-provider.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
......@@ -53,6 +54,8 @@
#define M41T80_ALARM_REG_SIZE \
(M41T80_REG_ALARM_SEC + 1 - M41T80_REG_ALARM_MON)
#define M41T80_SQW_MAX_FREQ 32768
#define M41T80_SEC_ST BIT(7) /* ST: Stop Bit */
#define M41T80_ALMON_AFE BIT(7) /* AFE: AF Enable Bit */
#define M41T80_ALMON_SQWE BIT(6) /* SQWE: SQW Enable Bit */
......@@ -147,7 +150,11 @@ MODULE_DEVICE_TABLE(of, m41t80_of_match);
struct m41t80_data {
unsigned long features;
struct i2c_client *client;
struct rtc_device *rtc;
#ifdef CONFIG_COMMON_CLK
struct clk_hw sqw;
#endif
};
static irqreturn_t m41t80_handle_irq(int irq, void *dev_id)
......@@ -227,6 +234,7 @@ static int m41t80_get_datetime(struct i2c_client *client,
/* Sets the given date and time to the real time clock. */
static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct m41t80_data *clientdata = i2c_get_clientdata(client);
unsigned char buf[8];
int err, flags;
......@@ -242,6 +250,17 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
buf[M41T80_REG_WDAY] = tm->tm_wday;
/* If the square wave output is controlled in the weekday register */
if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
int val;
val = i2c_smbus_read_byte_data(client, M41T80_REG_WDAY);
if (val < 0)
return val;
buf[M41T80_REG_WDAY] |= (val & 0xf0);
}
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
sizeof(buf), buf);
if (err < 0) {
......@@ -332,6 +351,9 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
return err;
}
/* Keep SQWE bit value */
alarmvals[0] |= (ret & M41T80_ALMON_SQWE);
ret = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (ret < 0)
return ret;
......@@ -431,103 +453,175 @@ static ssize_t flags_show(struct device *dev,
}
static DEVICE_ATTR_RO(flags);
static ssize_t sqwfreq_show(struct device *dev,
struct device_attribute *attr, char *buf)
static struct attribute *attrs[] = {
&dev_attr_flags.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
#ifdef CONFIG_COMMON_CLK
#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
int val, reg_sqw;
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
M41T80_REG_WDAY : M41T80_REG_SQW;
int ret = i2c_smbus_read_byte_data(client, reg_sqw);
unsigned long val = M41T80_SQW_MAX_FREQ;
if (!(clientdata->features & M41T80_FEATURE_SQ))
return -EINVAL;
if (ret < 0)
return 0;
ret >>= 4;
if (ret == 0)
val = 0;
else if (ret > 1)
val = val / (1 << ret);
reg_sqw = M41T80_REG_SQW;
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
reg_sqw = M41T80_REG_WDAY;
val = i2c_smbus_read_byte_data(client, reg_sqw);
if (val < 0)
return val;
val = (val >> 4) & 0xf;
switch (val) {
case 0:
break;
case 1:
val = 32768;
break;
default:
val = 32768 >> val;
}
return sprintf(buf, "%d\n", val);
}
static ssize_t sqwfreq_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
int almon, sqw, reg_sqw, rc;
unsigned long val;
int i, freq = M41T80_SQW_MAX_FREQ;
rc = kstrtoul(buf, 0, &val);
if (rc < 0)
return rc;
if (freq <= rate)
return freq;
if (!(clientdata->features & M41T80_FEATURE_SQ))
return -EINVAL;
for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
freq /= 1 << i;
if (freq <= rate)
return freq;
}
return 0;
}
if (val) {
if (!is_power_of_2(val))
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
M41T80_REG_WDAY : M41T80_REG_SQW;
int reg, ret, val = 0;
if (rate) {
if (!is_power_of_2(rate))
return -EINVAL;
val = ilog2(val);
if (val == 15)
val = ilog2(rate);
if (val == ilog2(M41T80_SQW_MAX_FREQ))
val = 1;
else if (val < 14)
val = 15 - val;
else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
val = ilog2(M41T80_SQW_MAX_FREQ) - val;
else
return -EINVAL;
}
/* disable SQW, set SQW frequency & re-enable */
almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (almon < 0)
return almon;
reg_sqw = M41T80_REG_SQW;
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
reg_sqw = M41T80_REG_WDAY;
sqw = i2c_smbus_read_byte_data(client, reg_sqw);
if (sqw < 0)
return sqw;
sqw = (sqw & 0x0f) | (val << 4);
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
almon & ~M41T80_ALMON_SQWE);
if (rc < 0)
return rc;
if (val) {
rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw);
if (rc < 0)
return rc;
reg = i2c_smbus_read_byte_data(client, reg_sqw);
if (reg < 0)
return reg;
rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
almon | M41T80_ALMON_SQWE);
if (rc < 0)
return rc;
}
return count;
reg = (reg & 0x0f) | (val << 4);
ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
if (ret < 0)
return ret;
return -EINVAL;
}
static DEVICE_ATTR_RW(sqwfreq);
static struct attribute *attrs[] = {
&dev_attr_flags.attr,
&dev_attr_sqwfreq.attr,
NULL,
};
static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
{
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
static struct attribute_group attr_group = {
.attrs = attrs,
if (ret < 0)
return ret;
if (enable)
ret |= M41T80_ALMON_SQWE;
else
ret &= ~M41T80_ALMON_SQWE;
return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
}
static int m41t80_sqw_prepare(struct clk_hw *hw)
{
return m41t80_sqw_control(hw, 1);
}
static void m41t80_sqw_unprepare(struct clk_hw *hw)
{
m41t80_sqw_control(hw, 0);
}
static int m41t80_sqw_is_prepared(struct clk_hw *hw)
{
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
struct i2c_client *client = m41t80->client;
int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (ret < 0)
return ret;
return !!(ret & M41T80_ALMON_SQWE);
}
static const struct clk_ops m41t80_sqw_ops = {
.prepare = m41t80_sqw_prepare,
.unprepare = m41t80_sqw_unprepare,
.is_prepared = m41t80_sqw_is_prepared,
.recalc_rate = m41t80_sqw_recalc_rate,
.round_rate = m41t80_sqw_round_rate,
.set_rate = m41t80_sqw_set_rate,
};
static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
{
struct i2c_client *client = m41t80->client;
struct device_node *node = client->dev.of_node;
struct clk *clk;
struct clk_init_data init;
int ret;
/* First disable the clock */
ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (ret < 0)
return ERR_PTR(ret);
ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
ret & ~(M41T80_ALMON_SQWE));
if (ret < 0)
return ERR_PTR(ret);
init.name = "m41t80-sqw";
init.ops = &m41t80_sqw_ops;
init.flags = 0;
init.parent_names = NULL;
init.num_parents = 0;
m41t80->sqw.init = &init;
/* optional override of the clockname */
of_property_read_string(node, "clock-output-names", &init.name);
/* register the clock */
clk = clk_register(&client->dev, &m41t80->sqw);
if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
return clk;
}
#endif
#ifdef CONFIG_RTC_DRV_M41T80_WDT
/*
*****************************************************************************
......@@ -845,6 +939,7 @@ static int m41t80_probe(struct i2c_client *client,
if (!m41t80_data)
return -ENOMEM;
m41t80_data->client = client;
if (client->dev.of_node)
m41t80_data->features = (unsigned long)
of_device_get_match_data(&client->dev);
......@@ -936,6 +1031,10 @@ static int m41t80_probe(struct i2c_client *client,
return rc;
}
}
#endif
#ifdef CONFIG_COMMON_CLK
if (m41t80_data->features & M41T80_FEATURE_SQ)
m41t80_sqw_register_clk(m41t80_data);
#endif
return 0;
}
......
......@@ -43,17 +43,6 @@
#define MAX_PIE_NUM 9
#define MAX_PIE_FREQ 512
static const u32 PIE_BIT_DEF[MAX_PIE_NUM][2] = {
{ 2, RTC_2HZ_BIT },
{ 4, RTC_SAM0_BIT },
{ 8, RTC_SAM1_BIT },
{ 16, RTC_SAM2_BIT },
{ 32, RTC_SAM3_BIT },
{ 64, RTC_SAM4_BIT },
{ 128, RTC_SAM5_BIT },
{ 256, RTC_SAM6_BIT },
{ MAX_PIE_FREQ, RTC_SAM7_BIT },
};
#define MXC_RTC_TIME 0
#define MXC_RTC_ALARM 1
......
......@@ -93,7 +93,7 @@ static int *check_rtc_access_enable(struct nuc900_rtc *nuc900_rtc)
__raw_writel(AERPOWERON, nuc900_rtc->rtc_reg + REG_RTC_AER);
while (!(__raw_readl(nuc900_rtc->rtc_reg + REG_RTC_AER) & AERRWENB)
&& timeout--)
&& --timeout)
mdelay(1);
if (!timeout)
......
......@@ -142,6 +142,16 @@ static int opal_get_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
y_m_d = be32_to_cpu(__y_m_d);
h_m_s_ms = ((u64)be32_to_cpu(__h_m) << 32);
/* check if no alarm is set */
if (y_m_d == 0 && h_m_s_ms == 0) {
pr_debug("No alarm is set\n");
rc = -ENOENT;
goto exit;
} else {
pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
}
opal_to_tm(y_m_d, h_m_s_ms, &alarm->time);
exit:
......@@ -157,7 +167,14 @@ static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
u32 y_m_d = 0;
int token, rc;
/* if alarm is enabled */
if (alarm->enabled) {
tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms);
pr_debug("Alarm set to %x %llx\n", y_m_d, h_m_s_ms);
} else {
pr_debug("Alarm getting disabled\n");
}
token = opal_async_get_token_interruptible();
if (token < 0) {
......@@ -190,6 +207,18 @@ static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm)
return rc;
}
int opal_tpo_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct rtc_wkalrm alarm = { .enabled = 0 };
/*
* TPO is automatically enabled when opal_set_tpo_time() is called with
* non-zero rtc-time. We only handle disable case which needs to be
* explicitly told to opal.
*/
return enabled ? 0 : opal_set_tpo_time(dev, &alarm);
}
static struct rtc_class_ops opal_rtc_ops = {
.read_time = opal_get_rtc_time,
.set_time = opal_set_rtc_time,
......@@ -205,6 +234,7 @@ static int opal_rtc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
opal_rtc_ops.read_alarm = opal_get_tpo_time;
opal_rtc_ops.set_alarm = opal_set_tpo_time;
opal_rtc_ops.alarm_irq_enable = opal_tpo_alarm_irq_enable;
}
rtc = devm_rtc_device_register(&pdev->dev, DRVNAME, &opal_rtc_ops,
......
......@@ -606,7 +606,7 @@ static int pcf8563_probe(struct i2c_client *client,
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf8563_irq,
IRQF_SHARED|IRQF_ONESHOT|IRQF_TRIGGER_FALLING,
pcf8563->rtc->name, client);
pcf8563_driver.driver.name, client);
if (err) {
dev_err(&client->dev, "unable to request IRQ %d\n",
client->irq);
......
......@@ -68,6 +68,7 @@ struct rv8803_data {
struct mutex flags_lock;
u8 ctrl;
enum rv8803_type type;
struct nvmem_config nvmem_cfg;
};
static int rv8803_read_reg(const struct i2c_client *client, u8 reg)
......@@ -460,48 +461,32 @@ static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
}
}
static ssize_t rv8803_nvram_write(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
static int rv8803_nvram_write(void *priv, unsigned int offset, void *val,
size_t bytes)
{
struct device *dev = kobj_to_dev(kobj);
struct i2c_client *client = to_i2c_client(dev);
int ret;
ret = rv8803_write_reg(client, RV8803_RAM, buf[0]);
ret = rv8803_write_reg(priv, RV8803_RAM, *(u8 *)val);
if (ret)
return ret;
return 1;
return 0;
}
static ssize_t rv8803_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
static int rv8803_nvram_read(void *priv, unsigned int offset,
void *val, size_t bytes)
{
struct device *dev = kobj_to_dev(kobj);
struct i2c_client *client = to_i2c_client(dev);
int ret;
ret = rv8803_read_reg(client, RV8803_RAM);
ret = rv8803_read_reg(priv, RV8803_RAM);
if (ret < 0)
return ret;
buf[0] = ret;
*(u8 *)val = ret;
return 1;
return 0;
}
static struct bin_attribute rv8803_nvram_attr = {
.attr = {
.name = "nvram",
.mode = S_IRUGO | S_IWUSR,
},
.size = 1,
.read = rv8803_nvram_read,
.write = rv8803_nvram_write,
};
static struct rtc_class_ops rv8803_rtc_ops = {
.read_time = rv8803_get_time,
.set_time = rv8803_set_time,
......@@ -577,6 +562,11 @@ static int rv8803_probe(struct i2c_client *client,
if (flags & RV8803_FLAG_AF)
dev_warn(&client->dev, "An alarm maybe have been missed.\n");
rv8803->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(rv8803->rtc)) {
return PTR_ERR(rv8803->rtc);
}
if (client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, rv8803_handle_irq,
......@@ -592,12 +582,20 @@ static int rv8803_probe(struct i2c_client *client,
}
}
rv8803->rtc = devm_rtc_device_register(&client->dev, client->name,
&rv8803_rtc_ops, THIS_MODULE);
if (IS_ERR(rv8803->rtc)) {
dev_err(&client->dev, "unable to register the class device\n");
return PTR_ERR(rv8803->rtc);
}
rv8803->nvmem_cfg.name = "rv8803_nvram",
rv8803->nvmem_cfg.word_size = 1,
rv8803->nvmem_cfg.stride = 1,
rv8803->nvmem_cfg.size = 1,
rv8803->nvmem_cfg.reg_read = rv8803_nvram_read,
rv8803->nvmem_cfg.reg_write = rv8803_nvram_write,
rv8803->nvmem_cfg.priv = client;
rv8803->rtc->ops = &rv8803_rtc_ops;
rv8803->rtc->nvmem_config = &rv8803->nvmem_cfg;
rv8803->rtc->nvram_old_abi = true;
err = rtc_register_device(rv8803->rtc);
if (err)
return err;
err = rv8803_write_reg(rv8803->client, RV8803_EXT, RV8803_EXT_WADA);
if (err)
......@@ -609,22 +607,11 @@ static int rv8803_probe(struct i2c_client *client,
return err;
}
err = device_create_bin_file(&client->dev, &rv8803_nvram_attr);
if (err)
return err;
rv8803->rtc->max_user_freq = 1;
return 0;
}
static int rv8803_remove(struct i2c_client *client)
{
device_remove_bin_file(&client->dev, &rv8803_nvram_attr);
return 0;
}
static const struct i2c_device_id rv8803_id[] = {
{ "rv8803", rv_8803 },
{ "rx8900", rx_8900 },
......@@ -651,7 +638,6 @@ static struct i2c_driver rv8803_driver = {
.of_match_table = of_match_ptr(rv8803_of_match),
},
.probe = rv8803_probe,
.remove = rv8803_remove,
.id_table = rv8803_id,
};
module_i2c_driver(rv8803_driver);
......
......@@ -41,7 +41,7 @@ struct s3c_rtc {
struct clk *rtc_src_clk;
bool clk_disabled;
struct s3c_rtc_data *data;
const struct s3c_rtc_data *data;
int irq_alarm;
int irq_tick;
......@@ -49,7 +49,8 @@ struct s3c_rtc {
spinlock_t pie_lock;
spinlock_t alarm_clk_lock;
int ticnt_save, ticnt_en_save;
int ticnt_save;
int ticnt_en_save;
bool wake_en;
};
......@@ -67,18 +68,32 @@ struct s3c_rtc_data {
void (*disable) (struct s3c_rtc *info);
};
static void s3c_rtc_enable_clk(struct s3c_rtc *info)
static int s3c_rtc_enable_clk(struct s3c_rtc *info)
{
unsigned long irq_flags;
int ret = 0;
spin_lock_irqsave(&info->alarm_clk_lock, irq_flags);
if (info->clk_disabled) {
clk_enable(info->rtc_clk);
if (info->data->needs_src_clk)
clk_enable(info->rtc_src_clk);
ret = clk_enable(info->rtc_clk);
if (ret)
goto out;
if (info->data->needs_src_clk) {
ret = clk_enable(info->rtc_src_clk);
if (ret) {
clk_disable(info->rtc_clk);
goto out;
}
}
info->clk_disabled = false;
}
out:
spin_unlock_irqrestore(&info->alarm_clk_lock, irq_flags);
return ret;
}
static void s3c_rtc_disable_clk(struct s3c_rtc *info)
......@@ -121,10 +136,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int tmp;
int ret;
dev_dbg(info->dev, "%s: aie=%d\n", __func__, enabled);
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
tmp = readb(info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
......@@ -135,10 +153,13 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
s3c_rtc_disable_clk(info);
if (enabled)
s3c_rtc_enable_clk(info);
else
if (enabled) {
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
} else {
s3c_rtc_disable_clk(info);
}
return 0;
}
......@@ -146,10 +167,14 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled)
/* Set RTC frequency */
static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq)
{
int ret;
if (!is_power_of_2(freq))
return -EINVAL;
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
spin_lock_irq(&info->pie_lock);
if (info->data->set_freq)
......@@ -166,10 +191,13 @@ static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
unsigned int have_retried = 0;
int ret;
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
retry_get_time:
retry_get_time:
rtc_tm->tm_min = readb(info->base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(info->base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(info->base + S3C2410_RTCDATE);
......@@ -211,6 +239,7 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
int year = tm->tm_year - 100;
int ret;
dev_dbg(dev, "set time %04d.%02d.%02d %02d:%02d:%02d\n",
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
......@@ -223,7 +252,9 @@ static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
return -EINVAL;
}
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
writeb(bin2bcd(tm->tm_sec), info->base + S3C2410_RTCSEC);
writeb(bin2bcd(tm->tm_min), info->base + S3C2410_RTCMIN);
......@@ -242,8 +273,11 @@ static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *alm_tm = &alrm->time;
unsigned int alm_en;
int ret;
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
alm_tm->tm_sec = readb(info->base + S3C2410_ALMSEC);
alm_tm->tm_min = readb(info->base + S3C2410_ALMMIN);
......@@ -292,6 +326,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
struct s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *tm = &alrm->time;
unsigned int alrm_en;
int ret;
int year = tm->tm_year - 100;
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
......@@ -299,7 +334,9 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
alrm_en = readb(info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
writeb(0x00, info->base + S3C2410_RTCALM);
......@@ -348,8 +385,11 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
int ret;
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
if (info->data->enable_tick)
info->data->enable_tick(info, seq);
......@@ -378,8 +418,7 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
dev_info(info->dev, "rtc disabled, re-enabling\n");
tmp = readw(info->base + S3C2410_RTCCON);
writew(tmp | S3C2410_RTCCON_RTCEN,
info->base + S3C2410_RTCCON);
writew(tmp | S3C2410_RTCCON_RTCEN, info->base + S3C2410_RTCCON);
}
if (con & S3C2410_RTCCON_CNTSEL) {
......@@ -437,12 +476,12 @@ static int s3c_rtc_remove(struct platform_device *pdev)
static const struct of_device_id s3c_rtc_dt_match[];
static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
static const struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev)
{
const struct of_device_id *match;
match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
return (struct s3c_rtc_data *)match->data;
return match->data;
}
static int s3c_rtc_probe(struct platform_device *pdev)
......@@ -498,7 +537,9 @@ static int s3c_rtc_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "probe deferred due to missing rtc clk\n");
return ret;
}
clk_prepare_enable(info->rtc_clk);
ret = clk_prepare_enable(info->rtc_clk);
if (ret)
return ret;
if (info->data->needs_src_clk) {
info->rtc_src_clk = devm_clk_get(&pdev->dev, "rtc_src");
......@@ -510,10 +551,11 @@ static int s3c_rtc_probe(struct platform_device *pdev)
else
dev_dbg(&pdev->dev,
"probe deferred due to missing rtc src clk\n");
clk_disable_unprepare(info->rtc_clk);
return ret;
goto err_src_clk;
}
clk_prepare_enable(info->rtc_src_clk);
ret = clk_prepare_enable(info->rtc_src_clk);
if (ret)
goto err_src_clk;
}
/* check to see if everything is setup correctly */
......@@ -569,12 +611,13 @@ static int s3c_rtc_probe(struct platform_device *pdev)
return 0;
err_nortc:
err_nortc:
if (info->data->disable)
info->data->disable(info);
if (info->data->needs_src_clk)
clk_disable_unprepare(info->rtc_src_clk);
err_src_clk:
clk_disable_unprepare(info->rtc_clk);
return ret;
......@@ -585,8 +628,11 @@ static int s3c_rtc_probe(struct platform_device *pdev)
static int s3c_rtc_suspend(struct device *dev)
{
struct s3c_rtc *info = dev_get_drvdata(dev);
int ret;
s3c_rtc_enable_clk(info);
ret = s3c_rtc_enable_clk(info);
if (ret)
return ret;
/* save TICNT for anyone using periodic interrupts */
if (info->data->save_tick_cnt)
......@@ -747,8 +793,7 @@ static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info)
writel(info->ticnt_save, info->base + S3C2410_TICNT);
if (info->ticnt_en_save) {
con = readw(info->base + S3C2410_RTCCON);
writew(con | info->ticnt_en_save,
info->base + S3C2410_RTCCON);
writew(con | info->ticnt_en_save, info->base + S3C2410_RTCCON);
}
}
......@@ -802,19 +847,19 @@ static struct s3c_rtc_data const s3c6410_rtc_data = {
static const struct of_device_id s3c_rtc_dt_match[] = {
{
.compatible = "samsung,s3c2410-rtc",
.data = (void *)&s3c2410_rtc_data,
.data = &s3c2410_rtc_data,
}, {
.compatible = "samsung,s3c2416-rtc",
.data = (void *)&s3c2416_rtc_data,
.data = &s3c2416_rtc_data,
}, {
.compatible = "samsung,s3c2443-rtc",
.data = (void *)&s3c2443_rtc_data,
.data = &s3c2443_rtc_data,
}, {
.compatible = "samsung,s3c6410-rtc",
.data = (void *)&s3c6410_rtc_data,
.data = &s3c6410_rtc_data,
}, {
.compatible = "samsung,exynos3250-rtc",
.data = (void *)&s3c6410_rtc_data,
.data = &s3c6410_rtc_data,
},
{ /* sentinel */ },
};
......
......@@ -99,7 +99,7 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
do_div(lpt, rtc->clkrate);
rtc_time_to_tm(lpt, tm);
rtc_time64_to_tm(lpt, tm);
return 0;
}
......@@ -107,13 +107,10 @@ static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct st_rtc *rtc = dev_get_drvdata(dev);
unsigned long long lpt;
unsigned long secs, flags;
int ret;
unsigned long long lpt, secs;
unsigned long flags;
ret = rtc_tm_to_time(tm, &secs);
if (ret)
return ret;
secs = rtc_tm_to_time64(tm);
lpt = (unsigned long long)secs * rtc->clkrate;
......@@ -161,13 +158,13 @@ static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct st_rtc *rtc = dev_get_drvdata(dev);
struct rtc_time now;
unsigned long now_secs;
unsigned long alarm_secs;
unsigned long long now_secs;
unsigned long long alarm_secs;
unsigned long long lpa;
st_rtc_read_time(dev, &now);
rtc_tm_to_time(&now, &now_secs);
rtc_tm_to_time(&t->time, &alarm_secs);
now_secs = rtc_tm_to_time64(&now);
alarm_secs = rtc_tm_to_time64(&t->time);
/* Invalid alarm time */
if (now_secs > alarm_secs)
......
......@@ -94,11 +94,17 @@
/* STM32_PWR_CR bit field */
#define PWR_CR_DBP BIT(8)
struct stm32_rtc_data {
bool has_pclk;
};
struct stm32_rtc {
struct rtc_device *rtc_dev;
void __iomem *base;
struct regmap *dbp;
struct clk *ck_rtc;
struct stm32_rtc_data *data;
struct clk *pclk;
struct clk *rtc_ck;
int irq_alarm;
};
......@@ -122,9 +128,9 @@ static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
/*
* It takes around 2 ck_rtc clock cycles to enter in
* It takes around 2 rtc_ck clock cycles to enter in
* initialization phase mode (and have INITF flag set). As
* slowest ck_rtc frequency may be 32kHz and highest should be
* slowest rtc_ck frequency may be 32kHz and highest should be
* 1MHz, we poll every 10 us with a timeout of 100ms.
*/
return readl_relaxed_poll_timeout_atomic(
......@@ -153,7 +159,7 @@ static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
/*
* Wait for RSF to be set to ensure the calendar registers are
* synchronised, it takes around 2 ck_rtc clock cycles
* synchronised, it takes around 2 rtc_ck clock cycles
*/
return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
isr,
......@@ -456,7 +462,7 @@ static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
/*
* Poll Alarm write flag to be sure that Alarm update is allowed: it
* takes around 2 ck_rtc clock cycles
* takes around 2 rtc_ck clock cycles
*/
ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
isr,
......@@ -490,8 +496,17 @@ static const struct rtc_class_ops stm32_rtc_ops = {
.alarm_irq_enable = stm32_rtc_alarm_irq_enable,
};
static const struct stm32_rtc_data stm32_rtc_data = {
.has_pclk = false,
};
static const struct stm32_rtc_data stm32h7_rtc_data = {
.has_pclk = true,
};
static const struct of_device_id stm32_rtc_of_match[] = {
{ .compatible = "st,stm32-rtc" },
{ .compatible = "st,stm32-rtc", .data = &stm32_rtc_data },
{ .compatible = "st,stm32h7-rtc", .data = &stm32h7_rtc_data },
{}
};
MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
......@@ -503,7 +518,7 @@ static int stm32_rtc_init(struct platform_device *pdev,
unsigned int rate;
int ret = 0;
rate = clk_get_rate(rtc->ck_rtc);
rate = clk_get_rate(rtc->rtc_ck);
/* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
......@@ -524,7 +539,7 @@ static int stm32_rtc_init(struct platform_device *pdev,
pred_a = pred_a_max;
pred_s = (rate / (pred_a + 1)) - 1;
dev_warn(&pdev->dev, "ck_rtc is %s\n",
dev_warn(&pdev->dev, "rtc_ck is %s\n",
(rate < ((pred_a + 1) * (pred_s + 1))) ?
"fast" : "slow");
}
......@@ -561,6 +576,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
{
struct stm32_rtc *rtc;
struct resource *res;
const struct of_device_id *match;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
......@@ -579,15 +595,34 @@ static int stm32_rtc_probe(struct platform_device *pdev)
return PTR_ERR(rtc->dbp);
}
rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(rtc->ck_rtc)) {
dev_err(&pdev->dev, "no ck_rtc clock");
return PTR_ERR(rtc->ck_rtc);
match = of_match_device(stm32_rtc_of_match, &pdev->dev);
rtc->data = (struct stm32_rtc_data *)match->data;
if (!rtc->data->has_pclk) {
rtc->pclk = NULL;
rtc->rtc_ck = devm_clk_get(&pdev->dev, NULL);
} else {
rtc->pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(rtc->pclk)) {
dev_err(&pdev->dev, "no pclk clock");
return PTR_ERR(rtc->pclk);
}
rtc->rtc_ck = devm_clk_get(&pdev->dev, "rtc_ck");
}
if (IS_ERR(rtc->rtc_ck)) {
dev_err(&pdev->dev, "no rtc_ck clock");
return PTR_ERR(rtc->rtc_ck);
}
ret = clk_prepare_enable(rtc->ck_rtc);
if (rtc->data->has_pclk) {
ret = clk_prepare_enable(rtc->pclk);
if (ret)
return ret;
}
ret = clk_prepare_enable(rtc->rtc_ck);
if (ret)
goto err;
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
......@@ -595,7 +630,7 @@ static int stm32_rtc_probe(struct platform_device *pdev)
* After a system reset, RTC_ISR.INITS flag can be read to check if
* the calendar has been initalized or not. INITS flag is reset by a
* power-on reset (no vbat, no power-supply). It is not reset if
* ck_rtc parent clock has changed (so RTC prescalers need to be
* rtc_ck parent clock has changed (so RTC prescalers need to be
* changed). That's why we cannot rely on this flag to know if RTC
* init has to be done.
*/
......@@ -646,7 +681,9 @@ static int stm32_rtc_probe(struct platform_device *pdev)
return 0;
err:
clk_disable_unprepare(rtc->ck_rtc);
if (rtc->data->has_pclk)
clk_disable_unprepare(rtc->pclk);
clk_disable_unprepare(rtc->rtc_ck);
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
......@@ -667,7 +704,9 @@ static int stm32_rtc_remove(struct platform_device *pdev)
writel_relaxed(cr, rtc->base + STM32_RTC_CR);
stm32_rtc_wpr_lock(rtc);
clk_disable_unprepare(rtc->ck_rtc);
clk_disable_unprepare(rtc->rtc_ck);
if (rtc->data->has_pclk)
clk_disable_unprepare(rtc->pclk);
/* Enable backup domain write protection */
regmap_update_bits(rtc->dbp, PWR_CR, PWR_CR_DBP, 0);
......@@ -682,6 +721,9 @@ static int stm32_rtc_suspend(struct device *dev)
{
struct stm32_rtc *rtc = dev_get_drvdata(dev);
if (rtc->data->has_pclk)
clk_disable_unprepare(rtc->pclk);
if (device_may_wakeup(dev))
return enable_irq_wake(rtc->irq_alarm);
......@@ -693,6 +735,12 @@ static int stm32_rtc_resume(struct device *dev)
struct stm32_rtc *rtc = dev_get_drvdata(dev);
int ret = 0;
if (rtc->data->has_pclk) {
ret = clk_prepare_enable(rtc->pclk);
if (ret)
return ret;
}
ret = stm32_rtc_wait_sync(rtc);
if (ret < 0)
return ret;
......
......@@ -27,7 +27,8 @@
static ssize_t
name_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent),
dev_name(dev->parent));
}
static DEVICE_ATTR_RO(name);
......
......@@ -12,6 +12,9 @@
#ifndef _LINUX_NVMEM_PROVIDER_H
#define _LINUX_NVMEM_PROVIDER_H
#include <linux/err.h>
#include <linux/errno.h>
struct nvmem_device;
struct nvmem_cell_info;
typedef int (*nvmem_reg_read_t)(void *priv, unsigned int offset,
......
......@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/nvmem-provider.h>
#include <uapi/linux/rtc.h>
extern int rtc_month_days(unsigned int month, unsigned int year);
......@@ -32,17 +33,11 @@ static inline time64_t rtc_tm_sub(struct rtc_time *lhs, struct rtc_time *rhs)
return rtc_tm_to_time64(lhs) - rtc_tm_to_time64(rhs);
}
/**
* Deprecated. Use rtc_time64_to_tm().
*/
static inline void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
{
rtc_time64_to_tm(time, tm);
}
/**
* Deprecated. Use rtc_tm_to_time64().
*/
static inline int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
{
*time = rtc_tm_to_time64(tm);
......@@ -116,7 +111,6 @@ struct rtc_device {
struct module *owner;
int id;
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops;
struct mutex ops_lock;
......@@ -143,6 +137,14 @@ struct rtc_device {
/* Some hardware can't support UIE mode */
int uie_unsupported;
bool registered;
struct nvmem_config *nvmem_config;
struct nvmem_device *nvmem;
/* Old ABI support */
bool nvram_old_abi;
struct bin_attribute *nvram;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task;
struct timer_list uie_timer;
......@@ -164,6 +166,8 @@ extern struct rtc_device *devm_rtc_device_register(struct device *dev,
const char *name,
const struct rtc_class_ops *ops,
struct module *owner);
struct rtc_device *devm_rtc_allocate_device(struct device *dev);
int __rtc_register_device(struct module *owner, struct rtc_device *rtc);
extern void rtc_device_unregister(struct rtc_device *rtc);
extern void devm_rtc_device_unregister(struct device *dev,
struct rtc_device *rtc);
......@@ -219,6 +223,9 @@ static inline bool is_leap_year(unsigned int year)
return (!(year % 4) && (year % 100)) || !(year % 400);
}
#define rtc_register_device(device) \
__rtc_register_device(THIS_MODULE, device)
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
extern int rtc_hctosys_ret;
#else
......
......@@ -9,7 +9,7 @@ TEST_GEN_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \
TEST_GEN_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \
skew_consistency clocksource-switch freq-step leap-a-day \
leapcrash set-tai set-2038 set-tz
leapcrash set-tai set-2038 set-tz rtctest_setdate
include ../lib.mk
......
......@@ -21,6 +21,9 @@
#include <stdlib.h>
#include <errno.h>
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
/*
* This expects the new RTC class driver framework, working with
......@@ -29,23 +32,84 @@
*/
static const char default_rtc[] = "/dev/rtc0";
static struct rtc_time cutoff_dates[] = {
{
.tm_year = 70, /* 1970 -1900 */
.tm_mday = 1,
},
/* signed time_t 19/01/2038 3:14:08 */
{
.tm_year = 138,
.tm_mday = 19,
},
{
.tm_year = 138,
.tm_mday = 20,
},
{
.tm_year = 199, /* 2099 -1900 */
.tm_mday = 1,
},
{
.tm_year = 200, /* 2100 -1900 */
.tm_mday = 1,
},
/* unsigned time_t 07/02/2106 7:28:15*/
{
.tm_year = 205,
.tm_mon = 1,
.tm_mday = 7,
},
{
.tm_year = 206,
.tm_mon = 1,
.tm_mday = 8,
},
/* signed time on 64bit in nanoseconds 12/04/2262 01:47:16*/
{
.tm_year = 362,
.tm_mon = 3,
.tm_mday = 12,
},
{
.tm_year = 362, /* 2262 -1900 */
.tm_mon = 3,
.tm_mday = 13,
},
};
static int compare_dates(struct rtc_time *a, struct rtc_time *b)
{
if (a->tm_year != b->tm_year ||
a->tm_mon != b->tm_mon ||
a->tm_mday != b->tm_mday ||
a->tm_hour != b->tm_hour ||
a->tm_min != b->tm_min ||
((b->tm_sec - a->tm_sec) > 1))
return 1;
return 0;
}
int main(int argc, char **argv)
{
int i, fd, retval, irqcount = 0;
int i, fd, retval, irqcount = 0, dangerous = 0;
unsigned long tmp, data;
struct rtc_time rtc_tm;
const char *rtc = default_rtc;
struct timeval start, end, diff;
switch (argc) {
case 3:
if (*argv[2] == 'd')
dangerous = 1;
case 2:
rtc = argv[1];
/* FALLTHROUGH */
case 1:
break;
default:
fprintf(stderr, "usage: rtctest [rtcdev]\n");
fprintf(stderr, "usage: rtctest [rtcdev] [d]\n");
return 1;
}
......@@ -202,7 +266,7 @@ int main(int argc, char **argv)
/* not all RTCs support periodic IRQs */
if (errno == EINVAL) {
fprintf(stderr, "\nNo periodic IRQ support\n");
goto done;
goto test_DATE;
}
perror("RTC_IRQP_READ ioctl");
exit(errno);
......@@ -221,7 +285,7 @@ int main(int argc, char **argv)
if (errno == EINVAL) {
fprintf(stderr,
"\n...Periodic IRQ rate is fixed\n");
goto done;
goto test_DATE;
}
perror("RTC_IRQP_SET ioctl");
exit(errno);
......@@ -269,6 +333,62 @@ int main(int argc, char **argv)
}
}
test_DATE:
if (!dangerous)
goto done;
fprintf(stderr, "\nTesting problematic dates\n");
for (i = 0; i < ARRAY_SIZE(cutoff_dates); i++) {
struct rtc_time current;
/* Write the new date in RTC */
retval = ioctl(fd, RTC_SET_TIME, &cutoff_dates[i]);
if (retval == -1) {
perror("RTC_SET_TIME ioctl");
close(fd);
exit(errno);
}
/* Read back */
retval = ioctl(fd, RTC_RD_TIME, &current);
if (retval == -1) {
perror("RTC_RD_TIME ioctl");
exit(errno);
}
if(compare_dates(&cutoff_dates[i], &current)) {
fprintf(stderr,"Setting date %d failed\n",
cutoff_dates[i].tm_year + 1900);
goto done;
}
cutoff_dates[i].tm_sec += 5;
/* Write the new alarm in RTC */
retval = ioctl(fd, RTC_ALM_SET, &cutoff_dates[i]);
if (retval == -1) {
perror("RTC_ALM_SET ioctl");
close(fd);
exit(errno);
}
/* Read back */
retval = ioctl(fd, RTC_ALM_READ, &current);
if (retval == -1) {
perror("RTC_ALM_READ ioctl");
exit(errno);
}
if(compare_dates(&cutoff_dates[i], &current)) {
fprintf(stderr,"Setting alarm %d failed\n",
cutoff_dates[i].tm_year + 1900);
goto done;
}
fprintf(stderr, "Setting year %d is OK \n",
cutoff_dates[i].tm_year + 1900);
}
done:
fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
......
/* Real Time Clock Driver Test
* by: Benjamin Gaignard (benjamin.gaignard@linaro.org)
*
* To build
* gcc rtctest_setdate.c -o rtctest_setdate
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <stdio.h>
#include <linux/rtc.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
static const char default_time[] = "00:00:00";
int main(int argc, char **argv)
{
int fd, retval;
struct rtc_time new, current;
const char *rtc, *date;
const char *time = default_time;
switch (argc) {
case 4:
time = argv[3];
/* FALLTHROUGH */
case 3:
date = argv[2];
rtc = argv[1];
break;
default:
fprintf(stderr, "usage: rtctest_setdate <rtcdev> <DD-MM-YYYY> [HH:MM:SS]\n");
return 1;
}
fd = open(rtc, O_RDONLY);
if (fd == -1) {
perror(rtc);
exit(errno);
}
sscanf(date, "%d-%d-%d", &new.tm_mday, &new.tm_mon, &new.tm_year);
new.tm_mon -= 1;
new.tm_year -= 1900;
sscanf(time, "%d:%d:%d", &new.tm_hour, &new.tm_min, &new.tm_sec);
fprintf(stderr, "Test will set RTC date/time to %d-%d-%d, %02d:%02d:%02d.\n",
new.tm_mday, new.tm_mon + 1, new.tm_year + 1900,
new.tm_hour, new.tm_min, new.tm_sec);
/* Write the new date in RTC */
retval = ioctl(fd, RTC_SET_TIME, &new);
if (retval == -1) {
perror("RTC_SET_TIME ioctl");
close(fd);
exit(errno);
}
/* Read back */
retval = ioctl(fd, RTC_RD_TIME, &current);
if (retval == -1) {
perror("RTC_RD_TIME ioctl");
exit(errno);
}
fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
current.tm_mday, current.tm_mon + 1, current.tm_year + 1900,
current.tm_hour, current.tm_min, current.tm_sec);
close(fd);
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment