From: Alexandre Belloni <alexandre.belloni@bootlin.com>
To: qianfanguijin@qq.com
Cc: linux-rtc@vger.kernel.org, linux-kernel@vger.kernel.org,
a.zummo@towertech.it, qianfan Zhao <qianfanguijin@163.com>
Subject: Re: [PATCH] drivers: rtc: Add new rtc-rx8025t for EPSON RX8025-T
Date: Mon, 17 Jan 2022 00:21:36 +0100 [thread overview]
Message-ID: <YeSogPnxaAFvyhVK@piout.net> (raw)
In-Reply-To: <tencent_61ABA73477AC0DA2AD8C61A8A5D57AF8E209@qq.com>
Hello,
On 21/12/2021 10:22:59+0800, qianfanguijin@qq.com wrote:
> From: qianfan Zhao <qianfanguijin@163.com>
>
> Supported features:
> - Date/time
> - Alarms
> - Low voltage detection
>
> Signed-off-by: qianfan Zhao <qianfanguijin@163.com>
> ---
> drivers/rtc/Kconfig | 9 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-rx8025t.c | 588 ++++++++++++++++++++++++++++++++++++++
> 3 files changed, 598 insertions(+)
> create mode 100644 drivers/rtc/rtc-rx8025t.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 65ad9d0b47ab..a0960f0cb7f9 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -650,6 +650,15 @@ config RTC_DRV_RX8025
> This driver can also be built as a module. If so, the module
> will be called rtc-rx8025.
>
> +config RTC_DRV_RX8025T
> + tristate "Epson RX-8025T"
> + help
> + If you say yes here you get support for the Epson
> + RX-8025T RTC chips.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-rx8025t.
> +
> config RTC_DRV_EM3027
> tristate "EM Microelectronic EM3027"
> help
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index bfb57464118d..5ecf6e7868fe 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -147,6 +147,7 @@ obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o
> obj-$(CONFIG_RTC_DRV_RX6110) += rtc-rx6110.o
> obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
> obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
> +obj-$(CONFIG_RTC_DRV_RX8025T) += rtc-rx8025t.o
> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
> obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
> diff --git a/drivers/rtc/rtc-rx8025t.c b/drivers/rtc/rtc-rx8025t.c
> new file mode 100644
> index 000000000000..093a88b1c788
> --- /dev/null
> +++ b/drivers/rtc/rtc-rx8025t.c
> @@ -0,0 +1,588 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Driver for the Epson RTC module RX-8025 T
Is the datasheet publicly available? I'm not sure this actually needs
yet another driver
> + * Based on rtc-rx8010.c
I'm not sure this is relevant. why not rtc-rx8025.c, why did you chose
to write a third driver for this RTC?
> + *
> + * Copyright(C) 2021 qianfan Zhao <qianfanguijin@163.com>
> + */
> +
> +#include <linux/bcd.h>
> +#include <linux/bitops.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/rtc.h>
> +
> +#define RX8025T_SEC 0x00
> +#define RX8025T_MIN 0x01
> +#define RX8025T_HOUR 0x02
> +#define RX8025T_WDAY 0x03
> +#define RX8025T_MDAY 0x04
> +#define RX8025T_MONTH 0x05
> +#define RX8025T_YEAR 0x06
> +#define RX8025T_RAM 0x07
> +#define RX8025T_ALMIN 0x08
> +#define RX8025T_ALHOUR 0x09
> +#define RX8025T_ALWDAY 0x0A
> +#define RX8025T_TCOUNT0 0x0B
> +#define RX8025T_TCOUNT1 0x0C
> +#define RX8025T_EXT 0x0D
> +#define RX8025T_FLAG 0x0E
> +#define RX8025T_CTRL 0x0F
> +
> +/* Extension Register (1Dh) bit positions */
> +#define RX8025T_EXT_TSEL0 BIT(0)
> +#define RX8025T_EXT_TSEL1 BIT(1)
> +#define RX8025T_EXT_FSEL0 BIT(2)
> +#define RX8025T_EXT_FSEL1 BIT(3)
> +#define RX8025T_EXT_TE BIT(4)
> +#define RX8025T_EXT_USEL BIT(5)
> +#define RX8025T_EXT_WADA BIT(6)
> +#define RX8025T_EXT_TEST BIT(7)
> +
> +/* Flag Register (1Eh) bit positions */
> +#define RX8025T_FLAG_VDET BIT(0)
> +#define RX8025T_FLAG_VLF BIT(1)
> +#define RX8025T_FLAG_AF BIT(3)
> +#define RX8025T_FLAG_TF BIT(4)
> +#define RX8025T_FLAG_UF BIT(5)
> +
> +/* Control Register (1Fh) bit positions */
> +#define RX8025T_CTRL_RESET BIT(0)
> +#define RX8025T_CTRL_AIE BIT(3)
> +#define RX8025T_CTRL_TIE BIT(4)
> +#define RX8025T_CTRL_UIE BIT(5)
> +#define RX8025T_CTRL_CSEL0 BIT(6)
> +#define RX8025T_CTRL_CSEL1 BIT(7)
1D, 1E and 1F doesn't seem to be existing on that RTC, are those
leftovers?
> +
> +#define RX8025T_ALARM_AE BIT(7)
> +
> +/* RX8025T vendor ioctls */
> +#define RX8025T_IOCTL_GET_TYPE _IOR('T', 0x10, unsigned int)
> +#define RX8025T_IOCTL_WRITE_REG _IOW('T', 0x20, unsigned int)
> +#define RX8025T_IOCTL_READ_REG _IOR('T', 0x30, unsigned int)
> +#define RX8025T_IOCTL_RD_DATE_RAW _IOR('T', 0x40, __u8[7]) /* ignore VLF */
> +
Vendor ioctls are not allowed
> +static const struct i2c_device_id rx8025t_id[] = {
> + { "rx8025t", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, rx8025t_id);
> +
> +static const struct of_device_id rx8025t_of_match[] = {
> + { .compatible = "epson,rx8025t" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, rx8025t_of_match);
> +
> +struct rx8025t_data {
> + struct regmap *regs;
> + struct rtc_device *rtc;
> +};
> +
> +static int rx8025t_set_update_irq(struct device *dev, unsigned int enable)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + int err;
> +
> + err = regmap_write_bits(rx8025t->regs, RX8025T_CTRL, RX8025T_CTRL_UIE,
> + enable ? RX8025T_CTRL_UIE : 0);
> + if (err)
> + dev_err(dev, "%sable update irq failed\n",
> + enable ? "en" : "dis");
> +
> + return err;
> +}
> +
> +static int rx8025t_set_freq_out(struct device *dev, u32 freq)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + unsigned int fsel;
> + int err;
> +
> + switch (freq) {
> + default: /* 32768 */
> + fsel = 0;
> + break;
> + case 1024:
> + fsel = RX8025T_EXT_FSEL0;
> + break;
> + case 1:
> + fsel = RX8025T_EXT_FSEL1;
> + break;
> + }
> +
> + err = regmap_write_bits(rx8025t->regs, RX8025T_EXT,
> + RX8025T_EXT_FSEL0 | RX8025T_EXT_FSEL1,
> + fsel);
> + if (err)
> + dev_err(dev, "set output frequency failed\n");
> +
> + return err;
Please use the CCF for that.
> +}
> +
> +static int rx8025t_set_compensation_interval(struct device *dev, u32 ms)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + unsigned int csel;
> + int err;
> +
> + switch (ms) {
> + default: /* 2000 */
> + csel = RX8025T_CTRL_CSEL0;
> + break;
> + case 500:
> + csel = 0;
> + break;
> + case 10000:
> + csel = RX8025T_CTRL_CSEL1;
> + break;
> + case 30000:
> + csel = RX8025T_CTRL_CSEL0 | RX8025T_CTRL_CSEL1;
> + break;
> + }
> +
> + err = regmap_write_bits(rx8025t->regs, RX8025T_CTRL,
> + RX8025T_CTRL_CSEL0 | RX8025T_CTRL_CSEL1,
> + csel);
> + if (err)
> + dev_err(dev, "set compensation interval failed\n");
> +
> + return err;
> +}
> +
> +static irqreturn_t rx8025t_irq_1_handler(int irq, void *dev_id)
> +{
> + struct i2c_client *client = dev_id;
> + struct rx8025t_data *rx8025t = i2c_get_clientdata(client);
> + int flagreg, err;
> +
> + mutex_lock(&rx8025t->rtc->ops_lock);
> +
> + err = regmap_read(rx8025t->regs, RX8025T_FLAG, &flagreg);
> + if (err) {
> + mutex_unlock(&rx8025t->rtc->ops_lock);
> + return IRQ_NONE;
> + }
> +
> + if (flagreg & RX8025T_FLAG_VLF)
> + dev_warn(&client->dev, "Frequency stop detected\n");
Is the warning useful? Who do you expect to read the kernel logs?
> +
> + if (flagreg & RX8025T_FLAG_TF) {
> + flagreg &= ~RX8025T_FLAG_TF;
> + rtc_update_irq(rx8025t->rtc, 1, RTC_PF | RTC_IRQF);
> + }
This will never happen
> +
> + if (flagreg & RX8025T_FLAG_AF) {
> + flagreg &= ~RX8025T_FLAG_AF;
> + rtc_update_irq(rx8025t->rtc, 1, RTC_AF | RTC_IRQF);
> + }
> +
> + if (flagreg & RX8025T_FLAG_UF) {
> + flagreg &= ~RX8025T_FLAG_UF;
> + rtc_update_irq(rx8025t->rtc, 1, RTC_UF | RTC_IRQF);
> + }
> +
> + err = regmap_write(rx8025t->regs, RX8025T_FLAG, flagreg);
> + mutex_unlock(&rx8025t->rtc->ops_lock);
> + return err ? IRQ_NONE : IRQ_HANDLED;
> +}
> +
> +static int rx8025t_get_time(struct device *dev, struct rtc_time *dt)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + u8 date[RX8025T_YEAR - RX8025T_SEC + 1];
> + int flagreg, err;
> +
> + err = regmap_read(rx8025t->regs, RX8025T_FLAG, &flagreg);
> + if (err)
> + return err;
> +
> + if (flagreg & RX8025T_FLAG_VLF) {
> + dev_warn(dev, "Frequency stop detected\n");
> + return -EINVAL;
> + }
> +
> + err = regmap_bulk_read(rx8025t->regs, RX8025T_SEC, date, sizeof(date));
> + if (err)
> + return err;
> +
> + dt->tm_sec = bcd2bin(date[RX8025T_SEC - RX8025T_SEC] & 0x7f);
> + dt->tm_min = bcd2bin(date[RX8025T_MIN - RX8025T_SEC] & 0x7f);
> + dt->tm_hour = bcd2bin(date[RX8025T_HOUR - RX8025T_SEC] & 0x3f);
> + dt->tm_mday = bcd2bin(date[RX8025T_MDAY - RX8025T_SEC] & 0x3f);
> + dt->tm_mon = bcd2bin(date[RX8025T_MONTH - RX8025T_SEC] & 0x1f) - 1;
> + dt->tm_year = bcd2bin(date[RX8025T_YEAR - RX8025T_SEC]) + 100;
> + dt->tm_wday = ffs(date[RX8025T_WDAY - RX8025T_SEC] & 0x7f);
> +
> + return 0;
> +}
> +
> +static int rx8025t_set_time(struct device *dev, struct rtc_time *dt)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + u8 date[RX8025T_YEAR - RX8025T_SEC + 1];
> + int err;
> +
> + date[RX8025T_SEC - RX8025T_SEC] = bin2bcd(dt->tm_sec);
> + date[RX8025T_MIN - RX8025T_SEC] = bin2bcd(dt->tm_min);
> + date[RX8025T_HOUR - RX8025T_SEC] = bin2bcd(dt->tm_hour);
> + date[RX8025T_MDAY - RX8025T_SEC] = bin2bcd(dt->tm_mday);
> + date[RX8025T_MONTH - RX8025T_SEC] = bin2bcd(dt->tm_mon + 1);
> + date[RX8025T_YEAR - RX8025T_SEC] = bin2bcd(dt->tm_year - 100);
> + date[RX8025T_WDAY - RX8025T_SEC] = bin2bcd(1 << dt->tm_wday);
> +
> + err = regmap_bulk_write(rx8025t->regs, RX8025T_SEC, date, sizeof(date));
> + if (err)
> + return err;
> +
> + err = regmap_clear_bits(rx8025t->regs, RX8025T_FLAG, RX8025T_FLAG_VLF);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static void rx8025t_patch_hardware_features(struct device *dev)
> +{
> + bool default_on;
> + u32 tmpval;
> +
> + if (!device_property_read_u32(dev, "frequency-out", &tmpval))
> + rx8025t_set_freq_out(dev, tmpval);
> +
> + if (!device_property_read_u32(dev, "compensation-interval-ms", &tmpval))
> + rx8025t_set_compensation_interval(dev, tmpval);
> +
> + default_on = device_property_present(dev, "update-irq-default-on");
> + rx8025t_set_update_irq(dev, default_on);
Custom device tree properties have to be documented and you have to have
a god reason for them.
> +}
> +
> +static int rx8025t_init(struct device *dev)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + unsigned int ctrl;
> + int need_clear = 0, err;
> +
> + err = regmap_read(rx8025t->regs, RX8025T_FLAG, &ctrl);
> + if (err)
> + return err;
> +
> + if (ctrl & RX8025T_FLAG_VLF)
> + dev_warn(dev, "Frequency stop was detected\n");
> +
> + if (ctrl & RX8025T_FLAG_AF) {
> + dev_warn(dev, "Alarm was detected\n");
> + need_clear = 1;
> + }
> +
> + if (ctrl & RX8025T_FLAG_TF)
> + need_clear = 1;
> +
> + if (ctrl & RX8025T_FLAG_UF)
> + need_clear = 1;
> +
> + if (need_clear) {
> + ctrl &= ~(RX8025T_FLAG_AF | RX8025T_FLAG_TF | RX8025T_FLAG_UF);
> + err = regmap_write(rx8025t->regs, RX8025T_FLAG, ctrl);
> + if (err)
> + return err;
> + }
> +
> + rx8025t_patch_hardware_features(dev);
> + return 0;
> +}
> +
> +static int rx8025t_read_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + u8 alarmvals[3];
> + int flagreg, ctrlreg, err;
> +
> + err = regmap_read(rx8025t->regs, RX8025T_CTRL, &ctrlreg);
> + if (err)
> + return err;
> +
> + err = regmap_bulk_read(rx8025t->regs, RX8025T_ALMIN, alarmvals, 3);
> + if (err)
> + return err;
> +
> + err = regmap_read(rx8025t->regs, RX8025T_FLAG, &flagreg);
> + if (err)
> + return err;
> +
> + t->time.tm_sec = 0;
> + t->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
> + t->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);
> +
> + if (!(alarmvals[2] & RX8025T_ALARM_AE))
> + t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f);
> +
> + t->enabled = !!(ctrlreg & RX8025T_CTRL_AIE);
> + t->pending = (flagreg & RX8025T_FLAG_AF) && t->enabled;
> +
> + return 0;
> +}
> +
> +static int rx8025t_set_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + u8 alarmvals[3];
> + int ctrlreg;
> + int err;
> +
> + err = regmap_read(rx8025t->regs, RX8025T_CTRL, &ctrlreg);
> + if (err)
> + return err;
> +
> + if (ctrlreg & (RX8025T_CTRL_AIE | RX8025T_CTRL_UIE)) {
> + ctrlreg &= ~(RX8025T_CTRL_AIE | RX8025T_CTRL_UIE);
> + err = regmap_write(rx8025t->regs, RX8025T_CTRL, ctrlreg);
> + if (err)
> + return err;
> + }
> +
> + err = regmap_clear_bits(rx8025t->regs, RX8025T_FLAG, RX8025T_FLAG_AF);
> + if (err)
> + return err;
> +
> + alarmvals[0] = bin2bcd(t->time.tm_min);
> + alarmvals[1] = bin2bcd(t->time.tm_hour);
> + alarmvals[2] = bin2bcd(t->time.tm_mday);
> +
> + err = regmap_bulk_write(rx8025t->regs, RX8025T_ALMIN, alarmvals, 2);
> + if (err)
> + return err;
> +
> + err = regmap_clear_bits(rx8025t->regs, RX8025T_EXT, RX8025T_EXT_WADA);
> + if (err)
> + return err;
> +
> + if (alarmvals[2] == 0)
> + alarmvals[2] |= RX8025T_ALARM_AE;
> +
> + err = regmap_write(rx8025t->regs, RX8025T_ALWDAY, alarmvals[2]);
> + if (err)
> + return err;
> +
> + if (t->enabled) {
> + if (rx8025t->rtc->uie_rtctimer.enabled)
> + ctrlreg |= RX8025T_CTRL_UIE;
> + if (rx8025t->rtc->aie_timer.enabled)
> + ctrlreg |= (RX8025T_CTRL_AIE | RX8025T_CTRL_UIE);
> +
> + err = regmap_write(rx8025t->regs, RX8025T_CTRL, ctrlreg);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rx8025t_alarm_irq_enable(struct device *dev,
> + unsigned int enabled)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + int ctrlreg, err;
> + u8 ctrl;
> +
> + err = regmap_read(rx8025t->regs, RX8025T_CTRL, &ctrlreg);
> + if (err)
> + return err;
> +
> + ctrl = ctrlreg;
> +
> + if (enabled) {
> + if (rx8025t->rtc->uie_rtctimer.enabled)
> + ctrl |= RX8025T_CTRL_UIE;
> + if (rx8025t->rtc->aie_timer.enabled)
> + ctrl |= (RX8025T_CTRL_AIE | RX8025T_CTRL_UIE);
> + } else {
> + if (!rx8025t->rtc->uie_rtctimer.enabled)
> + ctrl &= ~RX8025T_CTRL_UIE;
> + if (!rx8025t->rtc->aie_timer.enabled)
> + ctrl &= ~RX8025T_CTRL_AIE;
> + }
> +
> + err = regmap_clear_bits(rx8025t->regs, RX8025T_FLAG, RX8025T_FLAG_AF);
> + if (err)
> + return err;
> +
> + if (ctrl != ctrlreg) {
> + err = regmap_write(rx8025t->regs, RX8025T_CTRL, ctrl);
> + if (err)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rx8025t_ioctl_write_reg(struct device *dev, int reg, u8 mask, u8 val)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> +
> + switch (reg) {
> + case RX8025T_RAM:
> + case RX8025T_TCOUNT0:
> + case RX8025T_TCOUNT1:
> + mask = 0xff;
> + break;
> + case RX8025T_EXT:
> + mask &= ~(RX8025T_EXT_TEST | RX8025T_EXT_WADA);
> + break;
> + case RX8025T_CTRL:
> + mask &= ~(RX8025T_CTRL_AIE | RX8025T_CTRL_RESET);
> + break;
> + default:
> + /* write those register are forbidden */
> + return -EINVAL;
> + }
> +
> + if (unlikely(mask == 0))
> + return -EINVAL;
> +
> + return regmap_write_bits(rx8025t->regs, reg, mask, val);
> +}
> +
> +static int rx8025t_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
> +{
> + struct rx8025t_data *rx8025t = dev_get_drvdata(dev);
> + int tmp, flagreg, err;
> + u8 reg, date[7];
> +
> + switch (cmd) {
> + case RTC_VL_READ:
> + err = regmap_read(rx8025t->regs, RX8025T_FLAG, &flagreg);
> + if (err)
> + return err;
> +
> + tmp = flagreg & RX8025T_FLAG_VLF ? RTC_VL_DATA_INVALID : 0;
> + return put_user(tmp, (unsigned int __user *)arg);
> +
> + case RTC_VL_CLR:
> + return regmap_clear_bits(rx8025t->regs, RX8025T_FLAG,
> + RX8025T_FLAG_VLF);
> +
> + case RX8025T_IOCTL_WRITE_REG:
> + if (get_user(tmp, (unsigned int __user *)arg) < 0)
> + return -EFAULT;
> +
> + reg = (tmp >> 16) & 0xff;
> + return rx8025t_ioctl_write_reg(dev, reg,
> + (tmp >> 8) & 0xff /* mask */,
> + (tmp >> 0) & 0xff /* val */);
> +
> + case RX8025T_IOCTL_READ_REG:
> + if (get_user(tmp, (unsigned int __user *)arg) < 0)
> + return -EFAULT;
> +
> + reg = tmp & 0xff;
> + if (reg < RX8025T_SEC || reg > RX8025T_CTRL)
> + return -EINVAL;
> +
> + err = regmap_read(rx8025t->regs, reg, &tmp);
> + if (err)
> + return err;
> +
> + return put_user(tmp, (unsigned int __user *)arg);
> +
> + case RX8025T_IOCTL_GET_TYPE:
> + tmp = 0x802554;
> + return put_user(tmp, (unsigned int __user *)arg);
> +
> + case RX8025T_IOCTL_RD_DATE_RAW:
> + err = regmap_bulk_read(rx8025t->regs, RX8025T_SEC, date,
> + sizeof(date));
> + if (err)
> + return err;
> +
> + return copy_to_user((__u8 __user *)arg, date, sizeof(date));
> +
> + default:
> + return -ENOIOCTLCMD;
> + }
> +}
> +
> +static const struct rtc_class_ops rx8025t_rtc_ops_default = {
> + .read_time = rx8025t_get_time,
> + .set_time = rx8025t_set_time,
> + .ioctl = rx8025t_ioctl,
> +};
> +
> +static const struct rtc_class_ops rx8025t_rtc_ops_alarm = {
> + .read_time = rx8025t_get_time,
> + .set_time = rx8025t_set_time,
> + .ioctl = rx8025t_ioctl,
> + .read_alarm = rx8025t_read_alarm,
> + .set_alarm = rx8025t_set_alarm,
> + .alarm_irq_enable = rx8025t_alarm_irq_enable,
> +};
> +
> +static const struct regmap_config rx8025t_regmap_config = {
> + .name = "rx8025t-rtc",
> + .reg_bits = 8,
> + .val_bits = 8,
> +};
> +
> +static int rx8025t_probe(struct i2c_client *client)
> +{
> + struct device *dev = &client->dev;
> + struct rx8025t_data *rx8025t;
> + int err = 0;
> +
> + rx8025t = devm_kzalloc(dev, sizeof(*rx8025t), GFP_KERNEL);
> + if (!rx8025t)
> + return -ENOMEM;
> +
> + i2c_set_clientdata(client, rx8025t);
> +
> + rx8025t->regs = devm_regmap_init_i2c(client, &rx8025t_regmap_config);
> + if (IS_ERR(rx8025t->regs))
> + return PTR_ERR(rx8025t->regs);
> +
> + err = rx8025t_init(dev);
> + if (err)
> + return err;
> +
> + rx8025t->rtc = devm_rtc_allocate_device(dev);
> + if (IS_ERR(rx8025t->rtc))
> + return PTR_ERR(rx8025t->rtc);
> +
> + if (client->irq > 0) {
> + dev_info(dev, "IRQ %d supplied\n", client->irq);
> + err = devm_request_threaded_irq(dev, client->irq, NULL,
> + rx8025t_irq_1_handler,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "rx8025t", client);
> + if (err) {
> + dev_err(dev, "unable to request IRQ\n");
> + return err;
> + }
> +
> + rx8025t->rtc->ops = &rx8025t_rtc_ops_alarm;
> + } else {
> + rx8025t->rtc->ops = &rx8025t_rtc_ops_default;
> + }
> +
> + rx8025t->rtc->max_user_freq = 1;
> + rx8025t->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
> + rx8025t->rtc->range_max = RTC_TIMESTAMP_END_2099;
> +
> + return rtc_register_device(rx8025t->rtc);
> +}
> +
> +static struct i2c_driver rx8025t_driver = {
> + .driver = {
> + .name = "rtc-rx8025t",
> + .of_match_table = of_match_ptr(rx8025t_of_match),
> + },
> + .probe_new = rx8025t_probe,
> + .id_table = rx8025t_id,
> +};
> +
> +module_i2c_driver(rx8025t_driver);
> +
> +MODULE_AUTHOR("qianfan Zhao <qianfanguijin@163.com>");
> +MODULE_DESCRIPTION("Epson RX8025T RTC driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.17.1
>
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
parent reply other threads:[~2022-01-16 23:21 UTC|newest]
Thread overview: expand[flat|nested] mbox.gz Atom feed
[parent not found: <tencent_61ABA73477AC0DA2AD8C61A8A5D57AF8E209@qq.com>]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YeSogPnxaAFvyhVK@piout.net \
--to=alexandre.belloni@bootlin.com \
--cc=a.zummo@towertech.it \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-rtc@vger.kernel.org \
--cc=qianfanguijin@163.com \
--cc=qianfanguijin@qq.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.