Commit 697e5a47 authored by Alexandre Belloni's avatar Alexandre Belloni

rtc: add generic nvmem support

Many RTCs have an on board non volatile storage. It can be battery backed
RAM or an EEPROM. Use the nvmem subsystem to export it to both userspace
and in-kernel consumers.

This stays compatible with the previous (non documented) ABI that was using
/sys/class/rtc/rtcx/device/nvram to export that memory. But will warn about
the deprecation.
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@free-electrons.com>
parent 735ae205
...@@ -164,6 +164,8 @@ offset The amount which the rtc clock has been adjusted in firmware. ...@@ -164,6 +164,8 @@ offset The amount which the rtc clock has been adjusted in firmware.
which are added to or removed from the rtc's base clock per which are added to or removed from the rtc's base clock per
billion ticks. A positive value makes a day pass more slowly, billion ticks. A positive value makes a day pass more slowly,
longer, and a negative value makes a day pass more quickly. 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
......
...@@ -77,6 +77,14 @@ config RTC_DEBUG ...@@ -77,6 +77,14 @@ config RTC_DEBUG
Say yes here to enable debugging support in the RTC framework Say yes here to enable debugging support in the RTC framework
and individual RTC drivers. 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" comment "RTC interfaces"
config RTC_INTF_SYSFS config RTC_INTF_SYSFS
......
...@@ -15,6 +15,7 @@ ifdef CONFIG_RTC_DRV_EFI ...@@ -15,6 +15,7 @@ ifdef CONFIG_RTC_DRV_EFI
rtc-core-y += rtc-efi-platform.o rtc-core-y += rtc-efi-platform.o
endif endif
rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o
rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
......
...@@ -290,6 +290,8 @@ EXPORT_SYMBOL_GPL(rtc_device_register); ...@@ -290,6 +290,8 @@ EXPORT_SYMBOL_GPL(rtc_device_register);
*/ */
void rtc_device_unregister(struct rtc_device *rtc) void rtc_device_unregister(struct rtc_device *rtc)
{ {
rtc_nvmem_unregister(rtc);
mutex_lock(&rtc->ops_lock); mutex_lock(&rtc->ops_lock);
/* /*
* Remove innards of this RTC, then disable it, before * Remove innards of this RTC, then disable it, before
...@@ -448,6 +450,8 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc) ...@@ -448,6 +450,8 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc)
rtc_proc_add_device(rtc); rtc_proc_add_device(rtc);
rtc_nvmem_register(rtc);
rtc->registered = true; rtc->registered = true;
dev_info(rtc->dev.parent, "registered as %s\n", dev_info(rtc->dev.parent, "registered as %s\n",
dev_name(&rtc->dev)); dev_name(&rtc->dev));
......
/*
* 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);
}
...@@ -45,3 +45,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void) ...@@ -45,3 +45,11 @@ static inline const struct attribute_group **rtc_get_dev_attribute_groups(void)
return NULL; return NULL;
} }
#endif #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
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/nvmem-provider.h>
#include <uapi/linux/rtc.h> #include <uapi/linux/rtc.h>
extern int rtc_month_days(unsigned int month, unsigned int year); extern int rtc_month_days(unsigned int month, unsigned int year);
...@@ -144,6 +145,12 @@ struct rtc_device { ...@@ -144,6 +145,12 @@ struct rtc_device {
bool registered; 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 #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
struct work_struct uie_task; struct work_struct uie_task;
struct timer_list uie_timer; struct timer_list uie_timer;
......
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