Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
		 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
+8 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -290,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
@@ -448,6 +450,8 @@ int __rtc_register_device(struct module *owner, struct rtc_device *rtc)

	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));

drivers/rtc/nvmem.c

0 → 100644
+113 −0
Original line number Diff line number Diff line
/*
 * 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);
}
Loading