Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Alexandre Belloni | 3967 | 85.53% | 11 | 64.71% |
Parthiban Nallathambi | 633 | 13.65% | 1 | 5.88% |
Wadim Egorov | 25 | 0.54% | 1 | 5.88% |
Ke Sun | 6 | 0.13% | 1 | 5.88% |
Chuhong Yuan | 4 | 0.09% | 1 | 5.88% |
Bartosz Golaszewski | 3 | 0.06% | 2 | 11.76% |
Total | 4638 | 17 |
// SPDX-License-Identifier: GPL-2.0 /* * RTC driver for the Micro Crystal RV3028 * * Copyright (C) 2019 Micro Crystal SA * * Alexandre Belloni <alexandre.belloni@bootlin.com> * */ #include <linux/clk-provider.h> #include <linux/bcd.h> #include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/log2.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/regmap.h> #include <linux/rtc.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_CLKOUT_FD_MASK GENMASK(2, 0) #define RV3028_CLKOUT_PORIE BIT(3) #define RV3028_CLKOUT_CLKSY BIT(6) #define RV3028_CLKOUT_CLKOE 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_UPDATE 0x11 #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 RV3028_BACKUP_BSM GENMASK(3,2) #define RV3028_BACKUP_BSM_DSM 0x1 #define RV3028_BACKUP_BSM_LSM 0x3 #define OFFSET_STEP_PPT 953674 enum rv3028_type { rv_3028, }; struct rv3028_data { struct regmap *regmap; struct rtc_device *rtc; enum rv3028_type type; #ifdef CONFIG_COMMON_CLK struct clk_hw clkout_hw; #endif }; static u16 rv3028_trickle_resistors[] = {3000, 5000, 9000, 15000}; 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 int rv3028_exit_eerd(struct rv3028_data *rv3028, u32 eerd) { if (eerd) return 0; return regmap_update_bits(rv3028->regmap, RV3028_CTRL1, RV3028_CTRL1_EERD, 0); } static int rv3028_enter_eerd(struct rv3028_data *rv3028, u32 *eerd) { u32 ctrl1, status; int ret; ret = regmap_read(rv3028->regmap, RV3028_CTRL1, &ctrl1); if (ret) return ret; *eerd = ctrl1 & RV3028_CTRL1_EERD; if (*eerd) return 0; ret = regmap_update_bits(rv3028->regmap, RV3028_CTRL1, RV3028_CTRL1_EERD, RV3028_CTRL1_EERD); if (ret) return ret; ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, !(status & RV3028_STATUS_EEBUSY), RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); if (ret) { rv3028_exit_eerd(rv3028, *eerd); return ret; } return 0; } static int rv3028_update_eeprom(struct rv3028_data *rv3028, u32 eerd) { u32 status; int ret; ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0); if (ret) goto exit_eerd; ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, RV3028_EEPROM_CMD_UPDATE); if (ret) goto exit_eerd; usleep_range(63000, RV3028_EEBUSY_TIMEOUT); ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, !(status & RV3028_STATUS_EEBUSY), RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); exit_eerd: rv3028_exit_eerd(rv3028, eerd); return ret; } static int rv3028_update_cfg(struct rv3028_data *rv3028, unsigned int reg, unsigned int mask, unsigned int val) { u32 eerd; int ret; ret = rv3028_enter_eerd(rv3028, &eerd); if (ret) return ret; ret = regmap_update_bits(rv3028->regmap, reg, mask, val); if (ret) { rv3028_exit_eerd(rv3028, eerd); return ret; } return rv3028_update_eeprom(rv3028, eerd); } 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; } status &= ~RV3028_STATUS_PORF; 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) 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 = 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] = 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); u32 eerd; int ret; offset = clamp(offset, -244141L, 243187L) * 1000; offset = DIV_ROUND_CLOSEST(offset, OFFSET_STEP_PPT); ret = rv3028_enter_eerd(rv3028, &eerd); if (ret) return ret; ret = regmap_write(rv3028->regmap, RV3028_OFFSET, offset >> 1); if (ret < 0) goto exit_eerd; ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP, BIT(7), offset << 7); if (ret < 0) goto exit_eerd; return rv3028_update_eeprom(rv3028, eerd); exit_eerd: rv3028_exit_eerd(rv3028, eerd); return ret; } static int rv3028_param_get(struct device *dev, struct rtc_param *param) { struct rv3028_data *rv3028 = dev_get_drvdata(dev); int ret; u32 value; switch(param->param) { case RTC_PARAM_BACKUP_SWITCH_MODE: ret = regmap_read(rv3028->regmap, RV3028_BACKUP, &value); if (ret < 0) return ret; value = FIELD_GET(RV3028_BACKUP_BSM, value); switch(value) { case RV3028_BACKUP_BSM_DSM: param->uvalue = RTC_BSM_DIRECT; break; case RV3028_BACKUP_BSM_LSM: param->uvalue = RTC_BSM_LEVEL; break; default: param->uvalue = RTC_BSM_DISABLED; } break; default: return -EINVAL; } return 0; } static int rv3028_param_set(struct device *dev, struct rtc_param *param) { struct rv3028_data *rv3028 = dev_get_drvdata(dev); u8 mode; switch(param->param) { case RTC_PARAM_BACKUP_SWITCH_MODE: switch (param->uvalue) { case RTC_BSM_DISABLED: mode = 0; break; case RTC_BSM_DIRECT: mode = RV3028_BACKUP_BSM_DSM; break; case RTC_BSM_LEVEL: mode = RV3028_BACKUP_BSM_LSM; break; default: return -EINVAL; } return rv3028_update_cfg(rv3028, RV3028_BACKUP, RV3028_BACKUP_BSM, FIELD_PREP(RV3028_BACKUP_BSM, mode)); default: return -EINVAL; } return 0; } 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; status = status & RV3028_STATUS_PORF ? RTC_VL_DATA_INVALID : 0; return put_user(status, (unsigned int __user *)arg); 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) { struct rv3028_data *rv3028 = priv; u32 status, eerd; int i, ret; u8 *buf = val; ret = rv3028_enter_eerd(rv3028, &eerd); if (ret) return ret; for (i = 0; i < bytes; i++) { ret = regmap_write(rv3028->regmap, RV3028_EEPROM_ADDR, offset + i); if (ret) goto restore_eerd; ret = regmap_write(rv3028->regmap, RV3028_EEPROM_DATA, buf[i]); if (ret) goto restore_eerd; ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0); if (ret) goto restore_eerd; ret = regmap_write(rv3028->regmap, 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(rv3028->regmap, RV3028_STATUS, status, !(status & RV3028_STATUS_EEBUSY), RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); if (ret) goto restore_eerd; } restore_eerd: rv3028_exit_eerd(rv3028, eerd); return ret; } static int rv3028_eeprom_read(void *priv, unsigned int offset, void *val, size_t bytes) { struct rv3028_data *rv3028 = priv; u32 status, eerd, data; int i, ret; u8 *buf = val; ret = rv3028_enter_eerd(rv3028, &eerd); if (ret) return ret; for (i = 0; i < bytes; i++) { ret = regmap_write(rv3028->regmap, RV3028_EEPROM_ADDR, offset + i); if (ret) goto restore_eerd; ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, 0x0); if (ret) goto restore_eerd; ret = regmap_write(rv3028->regmap, RV3028_EEPROM_CMD, RV3028_EEPROM_CMD_READ); if (ret) goto restore_eerd; ret = regmap_read_poll_timeout(rv3028->regmap, RV3028_STATUS, status, !(status & RV3028_STATUS_EEBUSY), RV3028_EEBUSY_POLL, RV3028_EEBUSY_TIMEOUT); if (ret) goto restore_eerd; ret = regmap_read(rv3028->regmap, RV3028_EEPROM_DATA, &data); if (ret) goto restore_eerd; buf[i] = data; } restore_eerd: rv3028_exit_eerd(rv3028, eerd); return ret; } #ifdef CONFIG_COMMON_CLK #define clkout_hw_to_rv3028(hw) container_of(hw, struct rv3028_data, clkout_hw) static int clkout_rates[] = { 32768, 8192, 1024, 64, 32, 1, }; static unsigned long rv3028_clkout_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { int clkout, ret; struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &clkout); if (ret < 0) return 0; clkout &= RV3028_CLKOUT_FD_MASK; return clkout_rates[clkout]; } static long rv3028_clkout_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { int i; for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) if (clkout_rates[i] <= rate) return clkout_rates[i]; return 0; } static int rv3028_clkout_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { int i, ret; u32 enabled; struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &enabled); if (ret < 0) return ret; ret = regmap_write(rv3028->regmap, RV3028_CLKOUT, 0x0); if (ret < 0) return ret; enabled &= RV3028_CLKOUT_CLKOE; for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) if (clkout_rates[i] == rate) return rv3028_update_cfg(rv3028, RV3028_CLKOUT, 0xff, RV3028_CLKOUT_CLKSY | enabled | i); return -EINVAL; } static int rv3028_clkout_prepare(struct clk_hw *hw) { struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); return regmap_write(rv3028->regmap, RV3028_CLKOUT, RV3028_CLKOUT_CLKSY | RV3028_CLKOUT_CLKOE); } static void rv3028_clkout_unprepare(struct clk_hw *hw) { struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); regmap_write(rv3028->regmap, RV3028_CLKOUT, 0x0); regmap_update_bits(rv3028->regmap, RV3028_STATUS, RV3028_STATUS_CLKF, 0); } static int rv3028_clkout_is_prepared(struct clk_hw *hw) { int clkout, ret; struct rv3028_data *rv3028 = clkout_hw_to_rv3028(hw); ret = regmap_read(rv3028->regmap, RV3028_CLKOUT, &clkout); if (ret < 0) return ret; return !!(clkout & RV3028_CLKOUT_CLKOE); } static const struct clk_ops rv3028_clkout_ops = { .prepare = rv3028_clkout_prepare, .unprepare = rv3028_clkout_unprepare, .is_prepared = rv3028_clkout_is_prepared, .recalc_rate = rv3028_clkout_recalc_rate, .round_rate = rv3028_clkout_round_rate, .set_rate = rv3028_clkout_set_rate, }; static int rv3028_clkout_register_clk(struct rv3028_data *rv3028, struct i2c_client *client) { int ret; struct clk *clk; struct clk_init_data init; struct device_node *node = client->dev.of_node; ret = regmap_update_bits(rv3028->regmap, RV3028_STATUS, RV3028_STATUS_CLKF, 0); if (ret < 0) return ret; init.name = "rv3028-clkout"; init.ops = &rv3028_clkout_ops; init.flags = 0; init.parent_names = NULL; init.num_parents = 0; rv3028->clkout_hw.init = &init; /* optional override of the clockname */ of_property_read_string(node, "clock-output-names", &init.name); /* register the clock */ clk = devm_clk_register(&client->dev, &rv3028->clkout_hw); if (!IS_ERR(clk)) of_clk_add_provider(node, of_clk_src_simple_get, clk); return 0; } #endif static const struct rtc_class_ops rv3028_rtc_ops = { .read_time = rv3028_get_time, .set_time = rv3028_set_time, .read_alarm = rv3028_get_alarm, .set_alarm = rv3028_set_alarm, .alarm_irq_enable = rv3028_alarm_irq_enable, .read_offset = rv3028_read_offset, .set_offset = rv3028_set_offset, .ioctl = rv3028_ioctl, .param_get = rv3028_param_get, .param_set = rv3028_param_set, }; 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); if (IS_ERR(rv3028->regmap)) return PTR_ERR(rv3028->regmap); i2c_set_clientdata(client, rv3028); ret = regmap_read(rv3028->regmap, RV3028_STATUS, &status); if (ret < 0) return ret; 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) { unsigned long flags; /* * If flags = 0, devm_request_threaded_irq() will use IRQ flags * obtained from device tree. */ if (dev_fwnode(&client->dev)) flags = 0; else flags = IRQF_TRIGGER_LOW; ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, rv3028_handle_irq, flags | IRQF_ONESHOT, "rv3028", rv3028); if (ret) { dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); client->irq = 0; } } if (!client->irq) clear_bit(RTC_FEATURE_ALARM, rv3028->rtc->features); 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 = rv3028_update_cfg(rv3028, 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; set_bit(RTC_FEATURE_BACKUP_SWITCH_MODE, rv3028->rtc->features); rv3028->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; rv3028->rtc->range_max = RTC_TIMESTAMP_END_2099; rv3028->rtc->ops = &rv3028_rtc_ops; ret = devm_rtc_register_device(rv3028->rtc); if (ret) return ret; nvmem_cfg.priv = rv3028->regmap; devm_rtc_nvmem_register(rv3028->rtc, &nvmem_cfg); eeprom_cfg.priv = rv3028; devm_rtc_nvmem_register(rv3028->rtc, &eeprom_cfg); rv3028->rtc->max_user_freq = 1; #ifdef CONFIG_COMMON_CLK rv3028_clkout_register_clk(rv3028, client); #endif return 0; } static const struct acpi_device_id rv3028_i2c_acpi_match[] = { { "MCRY3028" }, { } }; MODULE_DEVICE_TABLE(acpi, rv3028_i2c_acpi_match); static const __maybe_unused 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", .acpi_match_table = rv3028_i2c_acpi_match, .of_match_table = of_match_ptr(rv3028_of_match), }, .probe_new = rv3028_probe, }; module_i2c_driver(rv3028_driver); MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>"); MODULE_DESCRIPTION("Micro Crystal RV3028 RTC driver"); MODULE_LICENSE("GPL v2");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1