X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=target%2Flinux%2Fbrcm2708%2Fpatches-4.19%2F950-0393-rtc-rv3028-add-new-driver.patch;fp=target%2Flinux%2Fbrcm2708%2Fpatches-4.19%2F950-0393-rtc-rv3028-add-new-driver.patch;h=0000000000000000000000000000000000000000;hb=67dcc43f3a22dc3a7ac07a7065971b426feeb043;hp=72392512b357ddf8e0194928e433f3ad54ca370d;hpb=47a93a810f78adce5a130d287f82b28e9b54313c;p=openwrt%2Fstaging%2Fchunkeey.git diff --git a/target/linux/brcm2708/patches-4.19/950-0393-rtc-rv3028-add-new-driver.patch b/target/linux/brcm2708/patches-4.19/950-0393-rtc-rv3028-add-new-driver.patch deleted file mode 100644 index 72392512b3..0000000000 --- a/target/linux/brcm2708/patches-4.19/950-0393-rtc-rv3028-add-new-driver.patch +++ /dev/null @@ -1,860 +0,0 @@ -From bb0e317bfc453877805a12f975490ad38b6413f1 Mon Sep 17 00:00:00 2001 -From: Alexandre Belloni -Date: Wed, 13 Feb 2019 00:21:36 +0100 -Subject: [PATCH 393/806] rtc: rv3028: add new driver - -upstream commit e6e7376cfd7b3f9b63de3a22792f64d9bfb2ab53. - -Add a driver for the MicroCrystal RV-3028. It is a SMT Real-Time Clock -Module that incorporates an integrated CMOS circuit together with an XTAL. -It has an i2c interface. - -The driver handles date/time, alarms, trickle charging, timestamping, -frequency offset correction, EEPROM and NVRAM. - -Signed-off-by: Alexandre Belloni ---- - Documentation/devicetree/bindings/rtc/rtc.txt | 69 ++ - drivers/rtc/Kconfig | 9 + - drivers/rtc/Makefile | 1 + - drivers/rtc/rtc-rv3028.c | 733 ++++++++++++++++++ - 4 files changed, 812 insertions(+) - create mode 100644 Documentation/devicetree/bindings/rtc/rtc.txt - create mode 100644 drivers/rtc/rtc-rv3028.c - ---- /dev/null -+++ b/Documentation/devicetree/bindings/rtc/rtc.txt -@@ -0,0 +1,69 @@ -+Generic device tree bindings for Real Time Clock devices -+======================================================== -+ -+This document describes generic bindings which can be used to describe Real Time -+Clock devices in a device tree. -+ -+Required properties -+------------------- -+ -+- compatible : name of RTC device following generic names recommended practice. -+ -+For other required properties e.g. to describe register sets, -+clocks, etc. check the binding documentation of the specific driver. -+ -+Optional properties -+------------------- -+ -+- start-year : if provided, the default hardware range supported by the RTC is -+ shifted so the first usable year is the specified one. -+ -+The following properties may not be supported by all drivers. However, if a -+driver wants to support one of the below features, it should adapt the bindings -+below. -+- trickle-resistor-ohms : Selected resistor for trickle charger. Should be given -+ if trickle charger should be enabled -+- trickle-diode-disable : Do not use internal trickle charger diode Should be -+ given if internal trickle charger diode should be -+ disabled -+- wakeup-source : Enables wake up of host system on alarm -+- quartz-load-femtofarads : The capacitive load of the quartz(x-tal), -+ expressed in femto Farad (fF). -+ The default value shall be listed (if optional), -+ and likewise all valid values. -+ -+Trivial RTCs -+------------ -+ -+This is a list of trivial RTC devices that have simple device tree -+bindings, consisting only of a compatible field, an address and -+possibly an interrupt line. -+ -+ -+Compatible Vendor / Chip -+========== ============= -+abracon,abb5zes3 AB-RTCMC-32.768kHz-B5ZE-S3: Real Time Clock/Calendar Module with I2C Interface -+dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output -+dallas,ds1672 Dallas DS1672 Real-time Clock -+dallas,ds3232 Extremely Accurate I²C RTC with Integrated Crystal and SRAM -+epson,rx8010 I2C-BUS INTERFACE REAL TIME CLOCK MODULE -+epson,rx8581 I2C-BUS INTERFACE REAL TIME CLOCK MODULE -+emmicro,em3027 EM Microelectronic EM3027 Real-time Clock -+isil,isl1208 Intersil ISL1208 Low Power RTC with Battery Backed SRAM -+isil,isl1218 Intersil ISL1218 Low Power RTC with Battery Backed SRAM -+isil,isl12022 Intersil ISL12022 Real-time Clock -+microcrystal,rv3028 Real Time Clock Module with I2C-Bus -+microcrystal,rv3029 Real Time Clock Module with I2C-Bus -+microcrystal,rv8523 Real Time Clock -+nxp,pcf2127 Real-time clock -+nxp,pcf2129 Real-time clock -+nxp,pcf8563 Real-time clock/calendar -+pericom,pt7c4338 Real-time Clock Module -+ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -+ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -+ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -+ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -+ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -+ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC -+sii,s35390a 2-wire CMOS real-time clock -+whwave,sd3078 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC ---- a/drivers/rtc/Kconfig -+++ b/drivers/rtc/Kconfig -@@ -625,6 +625,15 @@ config RTC_DRV_EM3027 - This driver can also be built as a module. If so, the module - will be called rtc-em3027. - -+config RTC_DRV_RV3028 -+ tristate "Micro Crystal RV3028" -+ help -+ If you say yes here you get support for the Micro Crystal -+ RV3028. -+ -+ This driver can also be built as a module. If so, the module -+ will be called rtc-rv3028. -+ - config RTC_DRV_RV8803 - tristate "Micro Crystal RV8803, Epson RX8900" - help ---- a/drivers/rtc/Makefile -+++ b/drivers/rtc/Makefile -@@ -136,6 +136,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5 - obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o - obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o - obj-$(CONFIG_RTC_DRV_RTD119X) += rtc-rtd119x.o -+obj-$(CONFIG_RTC_DRV_RV3028) += rtc-rv3028.o - obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o - obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o - obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o ---- /dev/null -+++ b/drivers/rtc/rtc-rv3028.c -@@ -0,0 +1,733 @@ -+// SPDX-License-Identifier: GPL-2.0 -+/* -+ * RTC driver for the Micro Crystal RV3028 -+ * -+ * Copyright (C) 2019 Micro Crystal SA -+ * -+ * Alexandre Belloni -+ * -+ */ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rtc-core.h" -+ -+#define RV3028_SEC 0x00 -+#define RV3028_MIN 0x01 -+#define RV3028_HOUR 0x02 -+#define RV3028_WDAY 0x03 -+#define RV3028_DAY 0x04 -+#define RV3028_MONTH 0x05 -+#define RV3028_YEAR 0x06 -+#define RV3028_ALARM_MIN 0x07 -+#define RV3028_ALARM_HOUR 0x08 -+#define RV3028_ALARM_DAY 0x09 -+#define RV3028_STATUS 0x0E -+#define RV3028_CTRL1 0x0F -+#define RV3028_CTRL2 0x10 -+#define RV3028_EVT_CTRL 0x13 -+#define RV3028_TS_COUNT 0x14 -+#define RV3028_TS_SEC 0x15 -+#define RV3028_RAM1 0x1F -+#define RV3028_EEPROM_ADDR 0x25 -+#define RV3028_EEPROM_DATA 0x26 -+#define RV3028_EEPROM_CMD 0x27 -+#define RV3028_CLKOUT 0x35 -+#define RV3028_OFFSET 0x36 -+#define RV3028_BACKUP 0x37 -+ -+#define RV3028_STATUS_PORF BIT(0) -+#define RV3028_STATUS_EVF BIT(1) -+#define RV3028_STATUS_AF BIT(2) -+#define RV3028_STATUS_TF BIT(3) -+#define RV3028_STATUS_UF BIT(4) -+#define RV3028_STATUS_BSF BIT(5) -+#define RV3028_STATUS_CLKF BIT(6) -+#define RV3028_STATUS_EEBUSY BIT(7) -+ -+#define RV3028_CTRL1_EERD BIT(3) -+#define RV3028_CTRL1_WADA BIT(5) -+ -+#define RV3028_CTRL2_RESET BIT(0) -+#define RV3028_CTRL2_12_24 BIT(1) -+#define RV3028_CTRL2_EIE BIT(2) -+#define RV3028_CTRL2_AIE BIT(3) -+#define RV3028_CTRL2_TIE BIT(4) -+#define RV3028_CTRL2_UIE BIT(5) -+#define RV3028_CTRL2_TSE BIT(7) -+ -+#define RV3028_EVT_CTRL_TSR BIT(2) -+ -+#define RV3028_EEPROM_CMD_WRITE 0x21 -+#define RV3028_EEPROM_CMD_READ 0x22 -+ -+#define RV3028_EEBUSY_POLL 10000 -+#define RV3028_EEBUSY_TIMEOUT 100000 -+ -+#define RV3028_BACKUP_TCE BIT(5) -+#define RV3028_BACKUP_TCR_MASK GENMASK(1,0) -+ -+#define OFFSET_STEP_PPT 953674 -+ -+enum rv3028_type { -+ rv_3028, -+}; -+ -+struct rv3028_data { -+ struct regmap *regmap; -+ struct rtc_device *rtc; -+ enum rv3028_type type; -+}; -+ -+static u16 rv3028_trickle_resistors[] = {1000, 3000, 6000, 11000}; -+ -+static ssize_t timestamp0_store(struct device *dev, -+ struct device_attribute *attr, -+ const char *buf, size_t count) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); -+ -+ regmap_update_bits(rv3028->regmap, RV3028_EVT_CTRL, RV3028_EVT_CTRL_TSR, -+ RV3028_EVT_CTRL_TSR); -+ -+ return count; -+}; -+ -+static ssize_t timestamp0_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); -+ struct rtc_time tm; -+ int ret, count; -+ u8 date[6]; -+ -+ ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); -+ if (ret) -+ return ret; -+ -+ if (!count) -+ return 0; -+ -+ ret = regmap_bulk_read(rv3028->regmap, RV3028_TS_SEC, date, -+ sizeof(date)); -+ if (ret) -+ return ret; -+ -+ tm.tm_sec = bcd2bin(date[0]); -+ tm.tm_min = bcd2bin(date[1]); -+ tm.tm_hour = bcd2bin(date[2]); -+ tm.tm_mday = bcd2bin(date[3]); -+ tm.tm_mon = bcd2bin(date[4]) - 1; -+ tm.tm_year = bcd2bin(date[5]) + 100; -+ -+ ret = rtc_valid_tm(&tm); -+ if (ret) -+ return ret; -+ -+ return sprintf(buf, "%llu\n", -+ (unsigned long long)rtc_tm_to_time64(&tm)); -+}; -+ -+static DEVICE_ATTR_RW(timestamp0); -+ -+static ssize_t timestamp0_count_show(struct device *dev, -+ struct device_attribute *attr, char *buf) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev->parent); -+ int ret, count; -+ -+ ret = regmap_read(rv3028->regmap, RV3028_TS_COUNT, &count); -+ if (ret) -+ return ret; -+ -+ return sprintf(buf, "%u\n", count); -+}; -+ -+static DEVICE_ATTR_RO(timestamp0_count); -+ -+static struct attribute *rv3028_attrs[] = { -+ &dev_attr_timestamp0.attr, -+ &dev_attr_timestamp0_count.attr, -+ NULL -+}; -+ -+static const struct attribute_group rv3028_attr_group = { -+ .attrs = rv3028_attrs, -+}; -+ -+static irqreturn_t rv3028_handle_irq(int irq, void *dev_id) -+{ -+ struct rv3028_data *rv3028 = dev_id; -+ unsigned long events = 0; -+ u32 status = 0, ctrl = 0; -+ -+ if (regmap_read(rv3028->regmap, RV3028_STATUS, &status) < 0 || -+ status == 0) { -+ return IRQ_NONE; -+ } -+ -+ if (status & RV3028_STATUS_PORF) -+ dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n"); -+ -+ if (status & RV3028_STATUS_TF) { -+ status |= RV3028_STATUS_TF; -+ ctrl |= RV3028_CTRL2_TIE; -+ events |= RTC_PF; -+ } -+ -+ if (status & RV3028_STATUS_AF) { -+ status |= RV3028_STATUS_AF; -+ ctrl |= RV3028_CTRL2_AIE; -+ events |= RTC_AF; -+ } -+ -+ if (status & RV3028_STATUS_UF) { -+ status |= RV3028_STATUS_UF; -+ ctrl |= RV3028_CTRL2_UIE; -+ events |= RTC_UF; -+ } -+ -+ if (events) { -+ rtc_update_irq(rv3028->rtc, 1, events); -+ regmap_update_bits(rv3028->regmap, RV3028_STATUS, status, 0); -+ regmap_update_bits(rv3028->regmap, RV3028_CTRL2, ctrl, 0); -+ } -+ -+ if (status & RV3028_STATUS_EVF) { -+ sysfs_notify(&rv3028->rtc->dev.kobj, NULL, -+ dev_attr_timestamp0.attr.name); -+ dev_warn(&rv3028->rtc->dev, "event detected"); -+ } -+ -+ return IRQ_HANDLED; -+} -+ -+static int rv3028_get_time(struct device *dev, struct rtc_time *tm) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ u8 date[7]; -+ int ret, status; -+ -+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); -+ if (ret < 0) -+ return ret; -+ -+ if (status & RV3028_STATUS_PORF) { -+ dev_warn(dev, "Voltage low, data is invalid.\n"); -+ return -EINVAL; -+ } -+ -+ ret = regmap_bulk_read(rv3028->regmap, RV3028_SEC, date, sizeof(date)); -+ if (ret) -+ return ret; -+ -+ tm->tm_sec = bcd2bin(date[RV3028_SEC] & 0x7f); -+ tm->tm_min = bcd2bin(date[RV3028_MIN] & 0x7f); -+ tm->tm_hour = bcd2bin(date[RV3028_HOUR] & 0x3f); -+ tm->tm_wday = ilog2(date[RV3028_WDAY] & 0x7f); -+ tm->tm_mday = bcd2bin(date[RV3028_DAY] & 0x3f); -+ tm->tm_mon = bcd2bin(date[RV3028_MONTH] & 0x1f) - 1; -+ tm->tm_year = bcd2bin(date[RV3028_YEAR]) + 100; -+ -+ return 0; -+} -+ -+static int rv3028_set_time(struct device *dev, struct rtc_time *tm) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ u8 date[7]; -+ int ret; -+ -+ date[RV3028_SEC] = bin2bcd(tm->tm_sec); -+ date[RV3028_MIN] = bin2bcd(tm->tm_min); -+ date[RV3028_HOUR] = bin2bcd(tm->tm_hour); -+ date[RV3028_WDAY] = 1 << (tm->tm_wday); -+ date[RV3028_DAY] = bin2bcd(tm->tm_mday); -+ date[RV3028_MONTH] = bin2bcd(tm->tm_mon + 1); -+ date[RV3028_YEAR] = bin2bcd(tm->tm_year - 100); -+ -+ /* -+ * Writing to the Seconds register has the same effect as setting RESET -+ * bit to 1 -+ */ -+ ret = regmap_bulk_write(rv3028->regmap, RV3028_SEC, date, -+ sizeof(date)); -+ if (ret) -+ return ret; -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, -+ RV3028_STATUS_PORF, 0); -+ -+ return ret; -+} -+ -+static int rv3028_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ u8 alarmvals[3]; -+ int status, ctrl, ret; -+ -+ ret = regmap_bulk_read(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, -+ sizeof(alarmvals)); -+ if (ret) -+ return ret; -+ -+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); -+ if (ret < 0) -+ return ret; -+ -+ ret = regmap_read(rv3028->regmap, RV3028_CTRL2, &ctrl); -+ if (ret < 0) -+ return ret; -+ -+ alrm->time.tm_sec = 0; -+ alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); -+ alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); -+ alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); -+ -+ alrm->enabled = !!(ctrl & RV3028_CTRL2_AIE); -+ alrm->pending = (status & RV3028_STATUS_AF) && alrm->enabled; -+ -+ return 0; -+} -+ -+static int rv3028_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ u8 alarmvals[3]; -+ u8 ctrl = 0; -+ int ret; -+ -+ /* The alarm has no seconds, round up to nearest minute */ -+ if (alrm->time.tm_sec) { -+ time64_t alarm_time = rtc_tm_to_time64(&alrm->time); -+ -+ alarm_time += 60 - alrm->time.tm_sec; -+ rtc_time64_to_tm(alarm_time, &alrm->time); -+ } -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, -+ RV3028_CTRL2_AIE | RV3028_CTRL2_UIE, 0); -+ if (ret) -+ return ret; -+ -+ alarmvals[0] = bin2bcd(alrm->time.tm_min); -+ alarmvals[1] = bin2bcd(alrm->time.tm_hour); -+ alarmvals[2] = bin2bcd(alrm->time.tm_mday); -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, -+ RV3028_STATUS_AF, 0); -+ if (ret) -+ return ret; -+ -+ ret = regmap_bulk_write(rv3028->regmap, RV3028_ALARM_MIN, alarmvals, -+ sizeof(alarmvals)); -+ if (ret) -+ return ret; -+ -+ if (alrm->enabled) { -+ if (rv3028->rtc->uie_rtctimer.enabled) -+ ctrl |= RV3028_CTRL2_UIE; -+ if (rv3028->rtc->aie_timer.enabled) -+ ctrl |= RV3028_CTRL2_AIE; -+ } -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, -+ RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); -+ -+ return ret; -+} -+ -+static int rv3028_alarm_irq_enable(struct device *dev, unsigned int enabled) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ int ctrl = 0, ret; -+ -+ if (enabled) { -+ if (rv3028->rtc->uie_rtctimer.enabled) -+ ctrl |= RV3028_CTRL2_UIE; -+ if (rv3028->rtc->aie_timer.enabled) -+ ctrl |= RV3028_CTRL2_AIE; -+ } -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, -+ RV3028_STATUS_AF | RV3028_STATUS_UF, 0); -+ if (ret) -+ return ret; -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, -+ RV3028_CTRL2_UIE | RV3028_CTRL2_AIE, ctrl); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int rv3028_read_offset(struct device *dev, long *offset) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ int ret, value, steps; -+ -+ ret = regmap_read(rv3028->regmap, RV3028_OFFSET, &value); -+ if (ret < 0) -+ return ret; -+ -+ steps = sign_extend32(value << 1, 8); -+ -+ ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value); -+ if (ret < 0) -+ return ret; -+ -+ steps += value >> 7; -+ -+ *offset = DIV_ROUND_CLOSEST(steps * OFFSET_STEP_PPT, 1000); -+ -+ return 0; -+} -+ -+static int rv3028_set_offset(struct device *dev, long offset) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ int ret; -+ -+ offset = clamp(offset, -244141L, 243187L) * 1000; -+ offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT); -+ -+ ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1); -+ if (ret < 0) -+ return ret; -+ -+ return regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7), -+ offset << 7); -+} -+ -+static int rv3028_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) -+{ -+ struct rv3028_data *rv3028 = dev_get_drvdata(dev); -+ int status, ret = 0; -+ -+ switch (cmd) { -+ case RTC_VL_READ: -+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); -+ if (ret < 0) -+ return ret; -+ -+ if (status & RV3028_STATUS_PORF) -+ dev_warn(&rv3028->rtc->dev, "Voltage low, data loss detected.\n"); -+ -+ status &= RV3028_STATUS_PORF; -+ -+ if (copy_to_user((void __user *)arg, &status, sizeof(int))) -+ return -EFAULT; -+ -+ return 0; -+ -+ case RTC_VL_CLR: -+ ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, -+ RV3028_STATUS_PORF, 0); -+ -+ return ret; -+ -+ default: -+ return -ENOIOCTLCMD; -+ } -+} -+ -+static int rv3028_nvram_write(void *priv, unsigned int offset, void *val, -+ size_t bytes) -+{ -+ return regmap_bulk_write(priv, RV3028_RAM1 + offset, val, bytes); -+} -+ -+static int rv3028_nvram_read(void *priv, unsigned int offset, void *val, -+ size_t bytes) -+{ -+ return regmap_bulk_read(priv, RV3028_RAM1 + offset, val, bytes); -+} -+ -+static int rv3028_eeprom_write(void *priv, unsigned int offset, void *val, -+ size_t bytes) -+{ -+ u32 status, ctrl1; -+ int i, ret, err; -+ u8 *buf = val; -+ -+ ret = regmap_read(priv, RV3028_CTRL1, &ctrl1); -+ if (ret) -+ return ret; -+ -+ if (!(ctrl1 & RV3028_CTRL1_EERD)) { -+ ret = regmap_update_bits(priv, RV3028_CTRL1, -+ RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); -+ if (ret) -+ return ret; -+ -+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, -+ !(status & RV3028_STATUS_EEBUSY), -+ RV3028_EEBUSY_POLL, -+ RV3028_EEBUSY_TIMEOUT); -+ if (ret) -+ goto restore_eerd; -+ } -+ -+ for (i = 0; i < bytes; i++) { -+ ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_write(priv, RV3028_EEPROM_DATA, buf[i]); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_write(priv, RV3028_EEPROM_CMD, -+ RV3028_EEPROM_CMD_WRITE); -+ if (ret) -+ goto restore_eerd; -+ -+ usleep_range(RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); -+ -+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, -+ !(status & RV3028_STATUS_EEBUSY), -+ RV3028_EEBUSY_POLL, -+ RV3028_EEBUSY_TIMEOUT); -+ if (ret) -+ goto restore_eerd; -+ } -+ -+restore_eerd: -+ if (!(ctrl1 & RV3028_CTRL1_EERD)) -+ { -+ err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD, -+ 0); -+ if (err && !ret) -+ ret = err; -+ } -+ -+ return ret; -+} -+ -+static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val, -+ size_t bytes) -+{ -+ u32 status, ctrl1, data; -+ int i, ret, err; -+ u8 *buf = val; -+ -+ ret = regmap_read(priv, RV3028_CTRL1, &ctrl1); -+ if (ret) -+ return ret; -+ -+ if (!(ctrl1 & RV3028_CTRL1_EERD)) { -+ ret = regmap_update_bits(priv, RV3028_CTRL1, -+ RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); -+ if (ret) -+ return ret; -+ -+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, -+ !(status & RV3028_STATUS_EEBUSY), -+ RV3028_EEBUSY_POLL, -+ RV3028_EEBUSY_TIMEOUT); -+ if (ret) -+ goto restore_eerd; -+ } -+ -+ for (i = 0; i < bytes; i++) { -+ ret = regmap_write(priv, RV3028_EEPROM_ADDR, offset + i); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_write(priv, RV3028_EEPROM_CMD, 0x0); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_write(priv, RV3028_EEPROM_CMD, -+ RV3028_EEPROM_CMD_READ); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_read_poll_timeout(priv, RV3028_STATUS, status, -+ !(status & RV3028_STATUS_EEBUSY), -+ RV3028_EEBUSY_POLL, -+ RV3028_EEBUSY_TIMEOUT); -+ if (ret) -+ goto restore_eerd; -+ -+ ret = regmap_read(priv, RV3028_EEPROM_DATA, &data); -+ if (ret) -+ goto restore_eerd; -+ buf[i] = data; -+ } -+ -+restore_eerd: -+ if (!(ctrl1 & RV3028_CTRL1_EERD)) -+ { -+ err = regmap_update_bits(priv, RV3028_CTRL1, RV3028_CTRL1_EERD, -+ 0); -+ if (err && !ret) -+ ret = err; -+ } -+ -+ return ret; -+} -+ -+static struct rtc_class_ops rv3028_rtc_ops = { -+ .read_time = rv3028_get_time, -+ .set_time = rv3028_set_time, -+ .read_offset = rv3028_read_offset, -+ .set_offset = rv3028_set_offset, -+ .ioctl = rv3028_ioctl, -+}; -+ -+static const struct regmap_config regmap_config = { -+ .reg_bits = 8, -+ .val_bits = 8, -+ .max_register = 0x37, -+}; -+ -+static int rv3028_probe(struct i2c_client *client) -+{ -+ struct rv3028_data *rv3028; -+ int ret, status; -+ u32 ohms; -+ struct nvmem_config nvmem_cfg = { -+ .name = "rv3028_nvram", -+ .word_size = 1, -+ .stride = 1, -+ .size = 2, -+ .type = NVMEM_TYPE_BATTERY_BACKED, -+ .reg_read = rv3028_nvram_read, -+ .reg_write = rv3028_nvram_write, -+ }; -+ struct nvmem_config eeprom_cfg = { -+ .name = "rv3028_eeprom", -+ .word_size = 1, -+ .stride = 1, -+ .size = 43, -+ .type = NVMEM_TYPE_EEPROM, -+ .reg_read = rv3028_eeprom_read, -+ .reg_write = rv3028_eeprom_write, -+ }; -+ -+ rv3028 = devm_kzalloc(&client->dev, sizeof(struct rv3028_data), -+ GFP_KERNEL); -+ if (!rv3028) -+ return -ENOMEM; -+ -+ rv3028->regmap = devm_regmap_init_i2c(client, ®map_config); -+ -+ i2c_set_clientdata(client, rv3028); -+ -+ ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); -+ if (ret < 0) -+ return ret; -+ -+ if (status & RV3028_STATUS_PORF) -+ dev_warn(&client->dev, "Voltage low, data loss detected.\n"); -+ -+ if (status & RV3028_STATUS_AF) -+ dev_warn(&client->dev, "An alarm may have been missed.\n"); -+ -+ rv3028->rtc = devm_rtc_allocate_device(&client->dev); -+ if (IS_ERR(rv3028->rtc)) { -+ return PTR_ERR(rv3028->rtc); -+ } -+ -+ if (client->irq > 0) { -+ ret = devm_request_threaded_irq(&client->dev, client->irq, -+ NULL, rv3028_handle_irq, -+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, -+ "rv3028", rv3028); -+ if (ret) { -+ dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); -+ client->irq = 0; -+ } else { -+ rv3028_rtc_ops.read_alarm = rv3028_get_alarm; -+ rv3028_rtc_ops.set_alarm = rv3028_set_alarm; -+ rv3028_rtc_ops.alarm_irq_enable = rv3028_alarm_irq_enable; -+ } -+ } -+ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1, -+ RV3028_CTRL1_WADA, RV3028_CTRL1_WADA); -+ if (ret) -+ return ret; -+ -+ /* setup timestamping */ -+ ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL2, -+ RV3028_CTRL2_EIE | RV3028_CTRL2_TSE, -+ RV3028_CTRL2_EIE | RV3028_CTRL2_TSE); -+ if (ret) -+ return ret; -+ -+ /* setup trickle charger */ -+ if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms", -+ &ohms)) { -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(rv3028_trickle_resistors); i++) -+ if (ohms == rv3028_trickle_resistors[i]) -+ break; -+ -+ if (i < ARRAY_SIZE(rv3028_trickle_resistors)) { -+ ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, -+ RV3028_BACKUP_TCE | -+ RV3028_BACKUP_TCR_MASK, -+ RV3028_BACKUP_TCE | i); -+ if (ret) -+ return ret; -+ } else { -+ dev_warn(&client->dev, "invalid trickle resistor value\n"); -+ } -+ } -+ -+ ret = rtc_add_group(rv3028->rtc, &rv3028_attr_group); -+ if (ret) -+ return ret; -+ -+ rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; -+ rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099; -+ rv3028->rtc->ops = &rv3028_rtc_ops; -+ ret = rtc_register_device(rv3028->rtc); -+ if (ret) -+ return ret; -+ -+ nvmem_cfg.priv = rv3028->regmap; -+ rtc_nvmem_register(rv3028->rtc, &nvmem_cfg); -+ eeprom_cfg.priv = rv3028->regmap; -+ rtc_nvmem_register(rv3028->rtc, &eeprom_cfg); -+ -+ rv3028->rtc->max_user_freq = 1; -+ -+ return 0; -+} -+ -+static const struct of_device_id rv3028_of_match[] = { -+ { .compatible = "microcrystal,rv3028", }, -+ { } -+}; -+MODULE_DEVICE_TABLE(of, rv3028_of_match); -+ -+static struct i2c_driver rv3028_driver = { -+ .driver = { -+ .name = "rtc-rv3028", -+ .of_match_table = of_match_ptr(rv3028_of_match), -+ }, -+ .probe_new = rv3028_probe, -+}; -+module_i2c_driver(rv3028_driver); -+ -+MODULE_AUTHOR("Alexandre Belloni "); -+MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver"); -+MODULE_LICENSE("GPL v2");