Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Alexandre Belloni <alexandre.belloni@free-electrons.com>

+143
+2
Documentation/rtc.txt
··· 164 164 which are added to or removed from the rtc's base clock per 165 165 billion ticks. A positive value makes a day pass more slowly, 166 166 longer, and a negative value makes a day pass more quickly. 167 + */nvmem The non volatile storage exported as a raw file, as described 168 + in Documentation/nvmem/nvmem.txt 167 169 ================ ============================================================== 168 170 169 171 IOCTL interface
+8
drivers/rtc/Kconfig
··· 77 77 Say yes here to enable debugging support in the RTC framework 78 78 and individual RTC drivers. 79 79 80 + config RTC_NVMEM 81 + bool "RTC non volatile storage support" 82 + select NVMEM 83 + default RTC_CLASS 84 + help 85 + Say yes here to add support for the non volatile (often battery 86 + backed) storage present on RTCs. 87 + 80 88 comment "RTC interfaces" 81 89 82 90 config RTC_INTF_SYSFS
+1
drivers/rtc/Makefile
··· 15 15 rtc-core-y += rtc-efi-platform.o 16 16 endif 17 17 18 + rtc-core-$(CONFIG_RTC_NVMEM) += nvmem.o 18 19 rtc-core-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o 19 20 rtc-core-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o 20 21 rtc-core-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
+4
drivers/rtc/class.c
··· 290 290 */ 291 291 void rtc_device_unregister(struct rtc_device *rtc) 292 292 { 293 + rtc_nvmem_unregister(rtc); 294 + 293 295 mutex_lock(&rtc->ops_lock); 294 296 /* 295 297 * Remove innards of this RTC, then disable it, before ··· 449 447 MAJOR(rtc->dev.devt), rtc->id); 450 448 451 449 rtc_proc_add_device(rtc); 450 + 451 + rtc_nvmem_register(rtc); 452 452 453 453 rtc->registered = true; 454 454 dev_info(rtc->dev.parent, "registered as %s\n",
+113
drivers/rtc/nvmem.c
··· 1 + /* 2 + * RTC subsystem, nvmem interface 3 + * 4 + * Copyright (C) 2017 Alexandre Belloni 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/err.h> 12 + #include <linux/types.h> 13 + #include <linux/nvmem-consumer.h> 14 + #include <linux/rtc.h> 15 + #include <linux/sysfs.h> 16 + 17 + #include "rtc-core.h" 18 + 19 + /* 20 + * Deprecated ABI compatibility, this should be removed at some point 21 + */ 22 + 23 + static const char nvram_warning[] = "Deprecated ABI, please use nvmem"; 24 + 25 + static ssize_t 26 + rtc_nvram_read(struct file *filp, struct kobject *kobj, 27 + struct bin_attribute *attr, 28 + char *buf, loff_t off, size_t count) 29 + { 30 + struct rtc_device *rtc = attr->private; 31 + 32 + dev_warn_once(kobj_to_dev(kobj), nvram_warning); 33 + 34 + return nvmem_device_read(rtc->nvmem, off, count, buf); 35 + } 36 + 37 + static ssize_t 38 + rtc_nvram_write(struct file *filp, struct kobject *kobj, 39 + struct bin_attribute *attr, 40 + char *buf, loff_t off, size_t count) 41 + { 42 + struct rtc_device *rtc = attr->private; 43 + 44 + dev_warn_once(kobj_to_dev(kobj), nvram_warning); 45 + 46 + return nvmem_device_write(rtc->nvmem, off, count, buf); 47 + } 48 + 49 + static int rtc_nvram_register(struct rtc_device *rtc) 50 + { 51 + int err; 52 + 53 + rtc->nvram = devm_kzalloc(rtc->dev.parent, 54 + sizeof(struct bin_attribute), 55 + GFP_KERNEL); 56 + if (!rtc->nvram) 57 + return -ENOMEM; 58 + 59 + rtc->nvram->attr.name = "nvram"; 60 + rtc->nvram->attr.mode = 0644; 61 + rtc->nvram->private = rtc; 62 + 63 + sysfs_bin_attr_init(rtc->nvram); 64 + 65 + rtc->nvram->read = rtc_nvram_read; 66 + rtc->nvram->write = rtc_nvram_write; 67 + rtc->nvram->size = rtc->nvmem_config->size; 68 + 69 + err = sysfs_create_bin_file(&rtc->dev.parent->kobj, 70 + rtc->nvram); 71 + if (err) { 72 + devm_kfree(rtc->dev.parent, rtc->nvram); 73 + rtc->nvram = NULL; 74 + } 75 + 76 + return err; 77 + } 78 + 79 + static void rtc_nvram_unregister(struct rtc_device *rtc) 80 + { 81 + sysfs_remove_bin_file(&rtc->dev.parent->kobj, rtc->nvram); 82 + } 83 + 84 + /* 85 + * New ABI, uses nvmem 86 + */ 87 + void rtc_nvmem_register(struct rtc_device *rtc) 88 + { 89 + if (!rtc->nvmem_config) 90 + return; 91 + 92 + rtc->nvmem_config->dev = &rtc->dev; 93 + rtc->nvmem_config->owner = rtc->owner; 94 + rtc->nvmem = nvmem_register(rtc->nvmem_config); 95 + if (IS_ERR_OR_NULL(rtc->nvmem)) 96 + return; 97 + 98 + /* Register the old ABI */ 99 + if (rtc->nvram_old_abi) 100 + rtc_nvram_register(rtc); 101 + } 102 + 103 + void rtc_nvmem_unregister(struct rtc_device *rtc) 104 + { 105 + if (IS_ERR_OR_NULL(rtc->nvmem)) 106 + return; 107 + 108 + /* unregister the old ABI */ 109 + if (rtc->nvram) 110 + rtc_nvram_unregister(rtc); 111 + 112 + nvmem_unregister(rtc->nvmem); 113 + }
+8
drivers/rtc/rtc-core.h
··· 45 45 return NULL; 46 46 } 47 47 #endif 48 + 49 + #ifdef CONFIG_RTC_NVMEM 50 + void rtc_nvmem_register(struct rtc_device *rtc); 51 + void rtc_nvmem_unregister(struct rtc_device *rtc); 52 + #else 53 + static inline void rtc_nvmem_register(struct rtc_device *rtc) {} 54 + static inline void rtc_nvmem_unregister(struct rtc_device *rtc) {} 55 + #endif
+7
include/linux/rtc.h
··· 14 14 15 15 #include <linux/types.h> 16 16 #include <linux/interrupt.h> 17 + #include <linux/nvmem-provider.h> 17 18 #include <uapi/linux/rtc.h> 18 19 19 20 extern int rtc_month_days(unsigned int month, unsigned int year); ··· 144 143 int uie_unsupported; 145 144 146 145 bool registered; 146 + 147 + struct nvmem_config *nvmem_config; 148 + struct nvmem_device *nvmem; 149 + /* Old ABI support */ 150 + bool nvram_old_abi; 151 + struct bin_attribute *nvram; 147 152 148 153 #ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL 149 154 struct work_struct uie_task;