From: Akshay Bhat <akshay.bhat@timesys.com>
To: a.zummo@towertech.it
Cc: rtc-linux@googlegroups.com, linux-kernel@vger.kernel.org,
alexandre.belloni@free-electrons.com, justin.waters@timesys.com
Subject: [rtc-linux] Re: [PATCH] rtc: Add Epson RX8010SJ RTC driver
Date: Tue, 1 Dec 2015 11:00:16 -0500 [thread overview]
Message-ID: <565DC410.8040205@timesys.com> (raw)
In-Reply-To: <1447281118-25275-1-git-send-email-akshay.bhat@timesys.com>
Hi,
Wanted to check if there was any feedback on this patch?
Thanks,
Akshay
On 11/11/2015 05:31 PM, Akshay Bhat wrote:
> This driver supports the following functions:
> - reading and setting time
> - alarms when connected to an IRQ
> - reading and clearing the voltage low flags
>
> Datasheet:
> http://www.epsondevice.com/docs/qd/en/DownloadServlet?id=ID000956
> Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
> ---
> drivers/rtc/Kconfig | 10 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-rx8010.c | 570 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 581 insertions(+)
> create mode 100644 drivers/rtc/rtc-rx8010.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2a52424..376322f 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -558,6 +558,16 @@ config RTC_DRV_FM3130
> This driver can also be built as a module. If so the module
> will be called rtc-fm3130.
>
> +config RTC_DRV_RX8010
> + tristate "Epson RX8010SJ"
> + depends on I2C
> + help
> + If you say yes here you get support for the Epson RX8010SJ RTC
> + chip.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-rx8010.
> +
> config RTC_DRV_RX8581
> tristate "Epson RX-8581"
> help
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 231f764..62d61b2 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.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
> +obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
> obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
> diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
> new file mode 100644
> index 0000000..9b8bd76
> --- /dev/null
> +++ b/drivers/rtc/rtc-rx8010.c
> @@ -0,0 +1,570 @@
> +/*
> + * Driver for the Epson RTC module RX-8010 SJ
> + *
> + * Copyright(C) Timesys Corporation 2015
> + * Copyright(C) General Electric Company 2015
> + * Copyright(C) SEIKO EPSON CORPORATION 2013. All rights reserved.
> + *
> + * Derived from RX-8025 driver:
> + * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
> + *
> + * Copyright (C) 2005 by Digi International Inc.
> + * All rights reserved.
> + *
> + * Modified by fengjh at rising.com.cn
> + * <http://lists.lm-sensors.org/mailman/listinfo/lm-sensors>
> + * 2006.11
> + *
> + * Code cleanup by Sergei Poselenov, <sposelenov@emcraft.com>
> + * Converted to new style by Wolfgang Grandegger <wg@grandegger.com>
> + * Alarm and periodic interrupt added by Dmitry Rakhchev <rda@emcraft.com>
> + *
> + * This driver software is distributed as is, without any warranty of any kind,
> + * either express or implied as further specified in the GNU Public License.
> + * This software may be used and distributed according to the terms of the GNU
> + * Public License, version 2 as published by the Free Software Foundation.
> + * See the file COPYING in the main directory of this archive for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/bcd.h>
> +#include <linux/bitops.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +
> +#define RX8010_SEC 0x10
> +#define RX8010_MIN 0x11
> +#define RX8010_HOUR 0x12
> +#define RX8010_WDAY 0x13
> +#define RX8010_MDAY 0x14
> +#define RX8010_MONTH 0x15
> +#define RX8010_YEAR 0x16
> +#define RX8010_YEAR 0x16
> +#define RX8010_RESV17 0x17
> +#define RX8010_ALMIN 0x18
> +#define RX8010_ALHOUR 0x19
> +#define RX8010_ALWDAY 0x1A
> +#define RX8010_TCOUNT0 0x1B
> +#define RX8010_TCOUNT1 0x1C
> +#define RX8010_EXT 0x1D
> +#define RX8010_FLAG 0x1E
> +#define RX8010_CTRL 0x1F
> +/* 0x20 to 0x2F are user registers */
> +#define RX8010_RESV30 0x30
> +#define RX8010_RESV31 0x32
> +#define RX8010_IRQ 0x32
> +
> +#define RX8010_EXT_WADA BIT(3)
> +
> +#define RX8010_FLAG_VLF BIT(1)
> +#define RX8010_FLAG_AF BIT(3)
> +#define RX8010_FLAG_TF BIT(4)
> +#define RX8010_FLAG_UF BIT(5)
> +
> +#define RX8010_CTRL_AIE BIT(3)
> +#define RX8010_CTRL_UIE BIT(5)
> +#define RX8010_CTRL_STOP BIT(6)
> +#define RX8010_CTRL_TEST BIT(7)
> +
> +#define RX8010_ALARM_AE BIT(7)
> +
> +static const struct i2c_device_id rx8010_id[] = {
> + { "rx8010", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, rx8010_id);
> +
> +struct rx8010_data {
> + struct i2c_client *client;
> + struct rtc_device *rtc;
> + u8 ctrlreg;
> + spinlock_t flags_lock;
> +};
> +
> +static int rx8010_read_reg(struct i2c_client *client, int number, u8 *value)
> +{
> + int ret = i2c_smbus_read_byte_data(client, number);
> +
> + if (ret < 0)
> + return ret;
> +
> + *value = ret;
> + return 0;
> +}
> +
> +static int rx8010_read_regs(struct i2c_client *client, int number, u8 length,
> + u8 *values)
> +{
> + int ret = i2c_smbus_read_i2c_block_data(client, number, length, values);
> +
> + if (ret != length)
> + return ret < 0 ? ret : -EIO;
> +
> + return 0;
> +}
> +
> +static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
> +{
> + struct i2c_client *client = dev_id;
> + struct rx8010_data *rx8010 = i2c_get_clientdata(client);
> + u8 flagreg;
> +
> + spin_lock(&rx8010->flags_lock);
> +
> + if (rx8010_read_reg(client, RX8010_FLAG, &flagreg)) {
> + spin_unlock(&rx8010->flags_lock);
> + return IRQ_NONE;
> + }
> +
> + if (flagreg & RX8010_FLAG_VLF)
> + dev_warn(&client->dev, "Frequency stop detected\n");
> +
> + if (flagreg & RX8010_FLAG_TF) {
> + flagreg &= ~RX8010_FLAG_TF;
> + rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF);
> + }
> +
> + if (flagreg & RX8010_FLAG_AF) {
> + flagreg &= ~RX8010_FLAG_AF;
> + rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF);
> + }
> +
> + if (flagreg & RX8010_FLAG_UF) {
> + flagreg &= ~RX8010_FLAG_UF;
> + rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF);
> + }
> +
> + i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
> +
> + spin_unlock(&rx8010->flags_lock);
> + return IRQ_HANDLED;
> +}
> +
> +static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
> +{
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 date[7];
> + u8 flagreg;
> + int err;
> +
> + err = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (err)
> + return err;
> +
> + if (flagreg & RX8010_FLAG_VLF) {
> + dev_warn(dev, "Frequency stop detected\n");
> + return -EINVAL;
> + }
> +
> + err = rx8010_read_regs(rx8010->client, RX8010_SEC, 7, date);
> + if (err)
> + return err;
> +
> + dt->tm_sec = bcd2bin(date[RX8010_SEC-RX8010_SEC] & 0x7f);
> + dt->tm_min = bcd2bin(date[RX8010_MIN-RX8010_SEC] & 0x7f);
> + dt->tm_hour = bcd2bin(date[RX8010_HOUR-RX8010_SEC] & 0x3f);
> + dt->tm_mday = bcd2bin(date[RX8010_MDAY-RX8010_SEC] & 0x3f);
> + dt->tm_mon = bcd2bin(date[RX8010_MONTH-RX8010_SEC] & 0x1f) - 1;
> + dt->tm_year = bcd2bin(date[RX8010_YEAR-RX8010_SEC]);
> + dt->tm_wday = bcd2bin(date[RX8010_WDAY-RX8010_SEC] & 0x7f);
> +
> + if (dt->tm_year < 70)
> + dt->tm_year += 100;
> +
> + return rtc_valid_tm(dt);
> +}
> +
> +static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
> +{
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 date[7];
> + u8 ctrl, flagreg;
> + int ret;
> + unsigned long irqflags;
> +
> + /* BUG: The HW assumes every year that is a multiple of 4 to be a leap
> + * year. Next time this is wrong is 2100, which will not be a leap
> + * year.
> + */
> +
> + /* set STOP bit before changing clock/calendar */
> + ret = rx8010_read_reg(rx8010->client, RX8010_CTRL, &ctrl);
> + if (ret)
> + return ret;
> + rx8010->ctrlreg = ctrl | RX8010_CTRL_STOP;
> + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (ret < 0)
> + return ret;
> +
> + date[RX8010_SEC-RX8010_SEC] = bin2bcd(dt->tm_sec);
> + date[RX8010_MIN-RX8010_SEC] = bin2bcd(dt->tm_min);
> + date[RX8010_HOUR-RX8010_SEC] = bin2bcd(dt->tm_hour);
> + date[RX8010_MDAY-RX8010_SEC] = bin2bcd(dt->tm_mday);
> + date[RX8010_MONTH-RX8010_SEC] = bin2bcd(dt->tm_mon + 1);
> + date[RX8010_YEAR-RX8010_SEC] = bin2bcd(dt->tm_year % 100);
> + date[RX8010_WDAY-RX8010_SEC] = bin2bcd(dt->tm_wday);
> +
> + ret = i2c_smbus_write_i2c_block_data(rx8010->client,
> + RX8010_SEC, 7, date);
> + if (ret < 0)
> + return ret;
> +
> + /* clear STOP bit after changing clock/calendar */
> + ret = rx8010_read_reg(rx8010->client, RX8010_CTRL, &ctrl);
> + if (ret)
> + return ret;
> + rx8010->ctrlreg = ctrl & ~RX8010_CTRL_STOP;
> + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (ret < 0)
> + return ret;
> +
> + spin_lock_irqsave(&rx8010->flags_lock, irqflags);
> +
> + ret = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (ret) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return ret;
> + }
> +
> + if (flagreg & RX8010_FLAG_VLF)
> + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG,
> + flagreg & ~RX8010_FLAG_VLF);
> +
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> +
> + return 0;
> +}
> +
> +static int rx8010_init_client(struct i2c_client *client)
> +{
> + struct rx8010_data *rx8010 = i2c_get_clientdata(client);
> + u8 ctrl[3];
> + int need_clear = 0, need_reset = 0, err = 0;
> +
> + /* Initialize reserved registers as specified in datasheet */
> + err = i2c_smbus_write_byte_data(client, RX8010_RESV17, 0xD8);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(client, RX8010_RESV30, 0x00);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(client, RX8010_RESV31, 0x08);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(client, RX8010_IRQ, 0x00);
> + if (err < 0)
> + return err;
> +
> + err = rx8010_read_regs(rx8010->client, RX8010_EXT, 3, ctrl);
> + if (err)
> + return err;
> +
> + if ((ctrl[1] & RX8010_FLAG_VLF)) {
> + dev_warn(&client->dev, "Frequency stop was detected\n");
> + need_reset = 1;
> + }
> +
> + if (ctrl[1] & RX8010_FLAG_AF) {
> + dev_warn(&client->dev, "Alarm was detected\n");
> + need_clear = 1;
> + }
> +
> + if (ctrl[1] & RX8010_FLAG_TF)
> + need_clear = 1;
> +
> + if (ctrl[1] & RX8010_FLAG_UF)
> + need_clear = 1;
> +
> + if (need_reset) {
> + ctrl[0] = ctrl[1] = ctrl[2] = 0;
> + err = i2c_smbus_write_i2c_block_data(client, RX8010_EXT,
> + 3, ctrl);
> + if (err < 0)
> + return err;
> + } else if (need_clear) {
> + err = i2c_smbus_write_byte_data(client, RX8010_FLAG, 0x00);
> + if (err < 0)
> + return err;
> + }
> +
> + rx8010->ctrlreg = (ctrl[2] & ~RX8010_CTRL_TEST);
> +
> + return err;
> +}
> +
> +static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + struct i2c_client *client = rx8010->client;
> + u8 alarmvals[3];
> + u8 flagreg;
> + int err;
> +
> + err = rx8010_read_regs(client, RX8010_ALMIN, 3, alarmvals);
> + if (err)
> + return err;
> +
> + err = rx8010_read_reg(client, RX8010_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] & RX8010_ALARM_AE)
> + t->time.tm_mday = -1;
> + else
> + t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f);
> +
> + t->time.tm_wday = -1;
> + t->time.tm_mon = -1;
> + t->time.tm_year = -1;
> +
> + t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
> + t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;
> +
> + return err;
> +}
> +
> +static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 alarmvals[3];
> + u8 extreg, flagreg;
> + int err;
> + unsigned long irqflags;
> +
> + spin_lock_irqsave(&rx8010->flags_lock, irqflags);
> + err = rx8010_read_reg(client, RX8010_FLAG, &flagreg);
> + if (err) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return err;
> + }
> +
> + if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) {
> + rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (err < 0) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return err;
> + }
> + }
> +
> + flagreg &= ~RX8010_FLAG_AF;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + if (err < 0)
> + return err;
> +
> + alarmvals[0] = bin2bcd(t->time.tm_min);
> + alarmvals[1] = bin2bcd(t->time.tm_hour);
> + alarmvals[2] = bin2bcd(t->time.tm_mday);
> +
> + err = i2c_smbus_write_i2c_block_data(rx8010->client, RX8010_ALMIN,
> + 2, alarmvals);
> + if (err < 0)
> + return err;
> +
> + err = rx8010_read_reg(client, RX8010_EXT, &extreg);
> + if (err)
> + return err;
> +
> + extreg |= RX8010_EXT_WADA;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_EXT, extreg);
> + if (err < 0)
> + return err;
> +
> + if (alarmvals[2] == 0)
> + alarmvals[2] |= RX8010_ALARM_AE;
> +
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_ALWDAY,
> + alarmvals[2]);
> + if (err < 0)
> + return err;
> +
> + if (t->enabled) {
> + if (rx8010->rtc->uie_rtctimer.enabled)
> + rx8010->ctrlreg |= RX8010_CTRL_UIE;
> + if (rx8010->rtc->aie_timer.enabled)
> + rx8010->ctrlreg |=
> + (RX8010_CTRL_AIE | RX8010_CTRL_UIE);
> +
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rx8010_alarm_irq_enable(struct device *dev,
> + unsigned int enabled)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 flagreg;
> + u8 ctrl;
> + int err;
> +
> + ctrl = rx8010->ctrlreg;
> +
> + if (enabled) {
> + if (rx8010->rtc->uie_rtctimer.enabled)
> + ctrl |= RX8010_CTRL_UIE;
> + if (rx8010->rtc->aie_timer.enabled)
> + ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE);
> + } else {
> + if (!rx8010->rtc->uie_rtctimer.enabled)
> + ctrl &= ~RX8010_CTRL_UIE;
> + if (!rx8010->rtc->aie_timer.enabled)
> + ctrl &= ~RX8010_CTRL_AIE;
> + }
> +
> + err = rx8010_read_reg(client, RX8010_FLAG, &flagreg);
> + if (err)
> + return err;
> + flagreg &= ~RX8010_FLAG_AF;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
> + if (err < 0)
> + return err;
> +
> + if (ctrl != rx8010->ctrlreg) {
> + rx8010->ctrlreg = ctrl;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + int ret, tmp;
> + u8 flagreg;
> + unsigned long irqflags;
> +
> + switch (cmd) {
> + case RTC_VL_READ:
> + ret = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (ret)
> + return ret;
> +
> + tmp = !!(flagreg & RX8010_FLAG_VLF);
> + if (copy_to_user((void __user *)arg, &tmp, sizeof(int)))
> + return -EFAULT;
> +
> + return 0;
> +
> + case RTC_VL_CLR:
> + spin_lock_irqsave(&rx8010->flags_lock, irqflags);
> + ret = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (ret < 0) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return ret;
> + }
> +
> + flagreg &= ~RX8010_FLAG_VLF;
> + ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +
> + default:
> + return -ENOIOCTLCMD;
> + }
> +}
> +
> +static struct rtc_class_ops rx8010_rtc_ops = {
> + .read_time = rx8010_get_time,
> + .set_time = rx8010_set_time,
> + .ioctl = rx8010_ioctl,
> +};
> +
> +static int rx8010_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> + struct rx8010_data *rx8010;
> + int err = 0;
> +
> + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
> + | I2C_FUNC_SMBUS_I2C_BLOCK)) {
> + dev_err(&adapter->dev, "doesn't support required functionality\n");
> + return -EIO;
> + }
> +
> + rx8010 = devm_kzalloc(&client->dev, sizeof(struct rx8010_data),
> + GFP_KERNEL);
> + if (!rx8010)
> + return -ENOMEM;
> +
> + rx8010->client = client;
> + i2c_set_clientdata(client, rx8010);
> +
> + spin_lock_init(&rx8010->flags_lock);
> +
> + err = rx8010_init_client(client);
> + if (err)
> + return err;
> +
> + if (client->irq > 0) {
> + dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
> + err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> + rx8010_irq_1_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "rx8010", client);
> +
> + if (err) {
> + dev_err(&client->dev, "unable to request IRQ\n");
> + client->irq = 0;
> + } else {
> + rx8010_rtc_ops.read_alarm = rx8010_read_alarm;
> + rx8010_rtc_ops.set_alarm = rx8010_set_alarm;
> + rx8010_rtc_ops.alarm_irq_enable = rx8010_alarm_irq_enable;
> + }
> + }
> +
> + rx8010->rtc = devm_rtc_device_register(&client->dev, client->name,
> + &rx8010_rtc_ops, THIS_MODULE);
> +
> + if (IS_ERR(rx8010->rtc)) {
> + dev_err(&client->dev, "unable to register the class device\n");
> + return PTR_ERR(rx8010->rtc);
> + }
> +
> + rx8010->rtc->max_user_freq = 1;
> +
> + return err;
> +}
> +
> +static struct i2c_driver rx8010_driver = {
> + .driver = {
> + .name = "rtc-rx8010",
> + },
> + .probe = rx8010_probe,
> + .id_table = rx8010_id,
> +};
> +
> +module_i2c_driver(rx8010_driver);
> +
> +MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
> +MODULE_DESCRIPTION("Epson RX8010SJ RTC driver");
> +MODULE_LICENSE("GPL");
>
--
--
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
---
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
WARNING: multiple messages have this Message-ID (diff)
From: Akshay Bhat <akshay.bhat@timesys.com>
To: a.zummo@towertech.it
Cc: rtc-linux@googlegroups.com, linux-kernel@vger.kernel.org,
alexandre.belloni@free-electrons.com, justin.waters@timesys.com
Subject: Re: [PATCH] rtc: Add Epson RX8010SJ RTC driver
Date: Tue, 1 Dec 2015 11:00:16 -0500 [thread overview]
Message-ID: <565DC410.8040205@timesys.com> (raw)
In-Reply-To: <1447281118-25275-1-git-send-email-akshay.bhat@timesys.com>
Hi,
Wanted to check if there was any feedback on this patch?
Thanks,
Akshay
On 11/11/2015 05:31 PM, Akshay Bhat wrote:
> This driver supports the following functions:
> - reading and setting time
> - alarms when connected to an IRQ
> - reading and clearing the voltage low flags
>
> Datasheet:
> http://www.epsondevice.com/docs/qd/en/DownloadServlet?id=ID000956
> Signed-off-by: Akshay Bhat <akshay.bhat@timesys.com>
> ---
> drivers/rtc/Kconfig | 10 +
> drivers/rtc/Makefile | 1 +
> drivers/rtc/rtc-rx8010.c | 570 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 581 insertions(+)
> create mode 100644 drivers/rtc/rtc-rx8010.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 2a52424..376322f 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -558,6 +558,16 @@ config RTC_DRV_FM3130
> This driver can also be built as a module. If so the module
> will be called rtc-fm3130.
>
> +config RTC_DRV_RX8010
> + tristate "Epson RX8010SJ"
> + depends on I2C
> + help
> + If you say yes here you get support for the Epson RX8010SJ RTC
> + chip.
> +
> + This driver can also be built as a module. If so, the module
> + will be called rtc-rx8010.
> +
> config RTC_DRV_RX8581
> tristate "Epson RX-8581"
> help
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 231f764..62d61b2 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -128,6 +128,7 @@ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.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
> +obj-$(CONFIG_RTC_DRV_RX8010) += rtc-rx8010.o
> obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
> obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
> obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
> diff --git a/drivers/rtc/rtc-rx8010.c b/drivers/rtc/rtc-rx8010.c
> new file mode 100644
> index 0000000..9b8bd76
> --- /dev/null
> +++ b/drivers/rtc/rtc-rx8010.c
> @@ -0,0 +1,570 @@
> +/*
> + * Driver for the Epson RTC module RX-8010 SJ
> + *
> + * Copyright(C) Timesys Corporation 2015
> + * Copyright(C) General Electric Company 2015
> + * Copyright(C) SEIKO EPSON CORPORATION 2013. All rights reserved.
> + *
> + * Derived from RX-8025 driver:
> + * Copyright (C) 2009 Wolfgang Grandegger <wg@grandegger.com>
> + *
> + * Copyright (C) 2005 by Digi International Inc.
> + * All rights reserved.
> + *
> + * Modified by fengjh at rising.com.cn
> + * <http://lists.lm-sensors.org/mailman/listinfo/lm-sensors>
> + * 2006.11
> + *
> + * Code cleanup by Sergei Poselenov, <sposelenov@emcraft.com>
> + * Converted to new style by Wolfgang Grandegger <wg@grandegger.com>
> + * Alarm and periodic interrupt added by Dmitry Rakhchev <rda@emcraft.com>
> + *
> + * This driver software is distributed as is, without any warranty of any kind,
> + * either express or implied as further specified in the GNU Public License.
> + * This software may be used and distributed according to the terms of the GNU
> + * Public License, version 2 as published by the Free Software Foundation.
> + * See the file COPYING in the main directory of this archive for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/bcd.h>
> +#include <linux/bitops.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/rtc.h>
> +
> +#define RX8010_SEC 0x10
> +#define RX8010_MIN 0x11
> +#define RX8010_HOUR 0x12
> +#define RX8010_WDAY 0x13
> +#define RX8010_MDAY 0x14
> +#define RX8010_MONTH 0x15
> +#define RX8010_YEAR 0x16
> +#define RX8010_YEAR 0x16
> +#define RX8010_RESV17 0x17
> +#define RX8010_ALMIN 0x18
> +#define RX8010_ALHOUR 0x19
> +#define RX8010_ALWDAY 0x1A
> +#define RX8010_TCOUNT0 0x1B
> +#define RX8010_TCOUNT1 0x1C
> +#define RX8010_EXT 0x1D
> +#define RX8010_FLAG 0x1E
> +#define RX8010_CTRL 0x1F
> +/* 0x20 to 0x2F are user registers */
> +#define RX8010_RESV30 0x30
> +#define RX8010_RESV31 0x32
> +#define RX8010_IRQ 0x32
> +
> +#define RX8010_EXT_WADA BIT(3)
> +
> +#define RX8010_FLAG_VLF BIT(1)
> +#define RX8010_FLAG_AF BIT(3)
> +#define RX8010_FLAG_TF BIT(4)
> +#define RX8010_FLAG_UF BIT(5)
> +
> +#define RX8010_CTRL_AIE BIT(3)
> +#define RX8010_CTRL_UIE BIT(5)
> +#define RX8010_CTRL_STOP BIT(6)
> +#define RX8010_CTRL_TEST BIT(7)
> +
> +#define RX8010_ALARM_AE BIT(7)
> +
> +static const struct i2c_device_id rx8010_id[] = {
> + { "rx8010", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, rx8010_id);
> +
> +struct rx8010_data {
> + struct i2c_client *client;
> + struct rtc_device *rtc;
> + u8 ctrlreg;
> + spinlock_t flags_lock;
> +};
> +
> +static int rx8010_read_reg(struct i2c_client *client, int number, u8 *value)
> +{
> + int ret = i2c_smbus_read_byte_data(client, number);
> +
> + if (ret < 0)
> + return ret;
> +
> + *value = ret;
> + return 0;
> +}
> +
> +static int rx8010_read_regs(struct i2c_client *client, int number, u8 length,
> + u8 *values)
> +{
> + int ret = i2c_smbus_read_i2c_block_data(client, number, length, values);
> +
> + if (ret != length)
> + return ret < 0 ? ret : -EIO;
> +
> + return 0;
> +}
> +
> +static irqreturn_t rx8010_irq_1_handler(int irq, void *dev_id)
> +{
> + struct i2c_client *client = dev_id;
> + struct rx8010_data *rx8010 = i2c_get_clientdata(client);
> + u8 flagreg;
> +
> + spin_lock(&rx8010->flags_lock);
> +
> + if (rx8010_read_reg(client, RX8010_FLAG, &flagreg)) {
> + spin_unlock(&rx8010->flags_lock);
> + return IRQ_NONE;
> + }
> +
> + if (flagreg & RX8010_FLAG_VLF)
> + dev_warn(&client->dev, "Frequency stop detected\n");
> +
> + if (flagreg & RX8010_FLAG_TF) {
> + flagreg &= ~RX8010_FLAG_TF;
> + rtc_update_irq(rx8010->rtc, 1, RTC_PF | RTC_IRQF);
> + }
> +
> + if (flagreg & RX8010_FLAG_AF) {
> + flagreg &= ~RX8010_FLAG_AF;
> + rtc_update_irq(rx8010->rtc, 1, RTC_AF | RTC_IRQF);
> + }
> +
> + if (flagreg & RX8010_FLAG_UF) {
> + flagreg &= ~RX8010_FLAG_UF;
> + rtc_update_irq(rx8010->rtc, 1, RTC_UF | RTC_IRQF);
> + }
> +
> + i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
> +
> + spin_unlock(&rx8010->flags_lock);
> + return IRQ_HANDLED;
> +}
> +
> +static int rx8010_get_time(struct device *dev, struct rtc_time *dt)
> +{
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 date[7];
> + u8 flagreg;
> + int err;
> +
> + err = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (err)
> + return err;
> +
> + if (flagreg & RX8010_FLAG_VLF) {
> + dev_warn(dev, "Frequency stop detected\n");
> + return -EINVAL;
> + }
> +
> + err = rx8010_read_regs(rx8010->client, RX8010_SEC, 7, date);
> + if (err)
> + return err;
> +
> + dt->tm_sec = bcd2bin(date[RX8010_SEC-RX8010_SEC] & 0x7f);
> + dt->tm_min = bcd2bin(date[RX8010_MIN-RX8010_SEC] & 0x7f);
> + dt->tm_hour = bcd2bin(date[RX8010_HOUR-RX8010_SEC] & 0x3f);
> + dt->tm_mday = bcd2bin(date[RX8010_MDAY-RX8010_SEC] & 0x3f);
> + dt->tm_mon = bcd2bin(date[RX8010_MONTH-RX8010_SEC] & 0x1f) - 1;
> + dt->tm_year = bcd2bin(date[RX8010_YEAR-RX8010_SEC]);
> + dt->tm_wday = bcd2bin(date[RX8010_WDAY-RX8010_SEC] & 0x7f);
> +
> + if (dt->tm_year < 70)
> + dt->tm_year += 100;
> +
> + return rtc_valid_tm(dt);
> +}
> +
> +static int rx8010_set_time(struct device *dev, struct rtc_time *dt)
> +{
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 date[7];
> + u8 ctrl, flagreg;
> + int ret;
> + unsigned long irqflags;
> +
> + /* BUG: The HW assumes every year that is a multiple of 4 to be a leap
> + * year. Next time this is wrong is 2100, which will not be a leap
> + * year.
> + */
> +
> + /* set STOP bit before changing clock/calendar */
> + ret = rx8010_read_reg(rx8010->client, RX8010_CTRL, &ctrl);
> + if (ret)
> + return ret;
> + rx8010->ctrlreg = ctrl | RX8010_CTRL_STOP;
> + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (ret < 0)
> + return ret;
> +
> + date[RX8010_SEC-RX8010_SEC] = bin2bcd(dt->tm_sec);
> + date[RX8010_MIN-RX8010_SEC] = bin2bcd(dt->tm_min);
> + date[RX8010_HOUR-RX8010_SEC] = bin2bcd(dt->tm_hour);
> + date[RX8010_MDAY-RX8010_SEC] = bin2bcd(dt->tm_mday);
> + date[RX8010_MONTH-RX8010_SEC] = bin2bcd(dt->tm_mon + 1);
> + date[RX8010_YEAR-RX8010_SEC] = bin2bcd(dt->tm_year % 100);
> + date[RX8010_WDAY-RX8010_SEC] = bin2bcd(dt->tm_wday);
> +
> + ret = i2c_smbus_write_i2c_block_data(rx8010->client,
> + RX8010_SEC, 7, date);
> + if (ret < 0)
> + return ret;
> +
> + /* clear STOP bit after changing clock/calendar */
> + ret = rx8010_read_reg(rx8010->client, RX8010_CTRL, &ctrl);
> + if (ret)
> + return ret;
> + rx8010->ctrlreg = ctrl & ~RX8010_CTRL_STOP;
> + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (ret < 0)
> + return ret;
> +
> + spin_lock_irqsave(&rx8010->flags_lock, irqflags);
> +
> + ret = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (ret) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return ret;
> + }
> +
> + if (flagreg & RX8010_FLAG_VLF)
> + ret = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG,
> + flagreg & ~RX8010_FLAG_VLF);
> +
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> +
> + return 0;
> +}
> +
> +static int rx8010_init_client(struct i2c_client *client)
> +{
> + struct rx8010_data *rx8010 = i2c_get_clientdata(client);
> + u8 ctrl[3];
> + int need_clear = 0, need_reset = 0, err = 0;
> +
> + /* Initialize reserved registers as specified in datasheet */
> + err = i2c_smbus_write_byte_data(client, RX8010_RESV17, 0xD8);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(client, RX8010_RESV30, 0x00);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(client, RX8010_RESV31, 0x08);
> + if (err < 0)
> + return err;
> +
> + err = i2c_smbus_write_byte_data(client, RX8010_IRQ, 0x00);
> + if (err < 0)
> + return err;
> +
> + err = rx8010_read_regs(rx8010->client, RX8010_EXT, 3, ctrl);
> + if (err)
> + return err;
> +
> + if ((ctrl[1] & RX8010_FLAG_VLF)) {
> + dev_warn(&client->dev, "Frequency stop was detected\n");
> + need_reset = 1;
> + }
> +
> + if (ctrl[1] & RX8010_FLAG_AF) {
> + dev_warn(&client->dev, "Alarm was detected\n");
> + need_clear = 1;
> + }
> +
> + if (ctrl[1] & RX8010_FLAG_TF)
> + need_clear = 1;
> +
> + if (ctrl[1] & RX8010_FLAG_UF)
> + need_clear = 1;
> +
> + if (need_reset) {
> + ctrl[0] = ctrl[1] = ctrl[2] = 0;
> + err = i2c_smbus_write_i2c_block_data(client, RX8010_EXT,
> + 3, ctrl);
> + if (err < 0)
> + return err;
> + } else if (need_clear) {
> + err = i2c_smbus_write_byte_data(client, RX8010_FLAG, 0x00);
> + if (err < 0)
> + return err;
> + }
> +
> + rx8010->ctrlreg = (ctrl[2] & ~RX8010_CTRL_TEST);
> +
> + return err;
> +}
> +
> +static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + struct i2c_client *client = rx8010->client;
> + u8 alarmvals[3];
> + u8 flagreg;
> + int err;
> +
> + err = rx8010_read_regs(client, RX8010_ALMIN, 3, alarmvals);
> + if (err)
> + return err;
> +
> + err = rx8010_read_reg(client, RX8010_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] & RX8010_ALARM_AE)
> + t->time.tm_mday = -1;
> + else
> + t->time.tm_mday = bcd2bin(alarmvals[2] & 0x7f);
> +
> + t->time.tm_wday = -1;
> + t->time.tm_mon = -1;
> + t->time.tm_year = -1;
> +
> + t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
> + t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;
> +
> + return err;
> +}
> +
> +static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 alarmvals[3];
> + u8 extreg, flagreg;
> + int err;
> + unsigned long irqflags;
> +
> + spin_lock_irqsave(&rx8010->flags_lock, irqflags);
> + err = rx8010_read_reg(client, RX8010_FLAG, &flagreg);
> + if (err) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return err;
> + }
> +
> + if (rx8010->ctrlreg & (RX8010_CTRL_AIE | RX8010_CTRL_UIE)) {
> + rx8010->ctrlreg &= ~(RX8010_CTRL_AIE | RX8010_CTRL_UIE);
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (err < 0) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return err;
> + }
> + }
> +
> + flagreg &= ~RX8010_FLAG_AF;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + if (err < 0)
> + return err;
> +
> + alarmvals[0] = bin2bcd(t->time.tm_min);
> + alarmvals[1] = bin2bcd(t->time.tm_hour);
> + alarmvals[2] = bin2bcd(t->time.tm_mday);
> +
> + err = i2c_smbus_write_i2c_block_data(rx8010->client, RX8010_ALMIN,
> + 2, alarmvals);
> + if (err < 0)
> + return err;
> +
> + err = rx8010_read_reg(client, RX8010_EXT, &extreg);
> + if (err)
> + return err;
> +
> + extreg |= RX8010_EXT_WADA;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_EXT, extreg);
> + if (err < 0)
> + return err;
> +
> + if (alarmvals[2] == 0)
> + alarmvals[2] |= RX8010_ALARM_AE;
> +
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_ALWDAY,
> + alarmvals[2]);
> + if (err < 0)
> + return err;
> +
> + if (t->enabled) {
> + if (rx8010->rtc->uie_rtctimer.enabled)
> + rx8010->ctrlreg |= RX8010_CTRL_UIE;
> + if (rx8010->rtc->aie_timer.enabled)
> + rx8010->ctrlreg |=
> + (RX8010_CTRL_AIE | RX8010_CTRL_UIE);
> +
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rx8010_alarm_irq_enable(struct device *dev,
> + unsigned int enabled)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + u8 flagreg;
> + u8 ctrl;
> + int err;
> +
> + ctrl = rx8010->ctrlreg;
> +
> + if (enabled) {
> + if (rx8010->rtc->uie_rtctimer.enabled)
> + ctrl |= RX8010_CTRL_UIE;
> + if (rx8010->rtc->aie_timer.enabled)
> + ctrl |= (RX8010_CTRL_AIE | RX8010_CTRL_UIE);
> + } else {
> + if (!rx8010->rtc->uie_rtctimer.enabled)
> + ctrl &= ~RX8010_CTRL_UIE;
> + if (!rx8010->rtc->aie_timer.enabled)
> + ctrl &= ~RX8010_CTRL_AIE;
> + }
> +
> + err = rx8010_read_reg(client, RX8010_FLAG, &flagreg);
> + if (err)
> + return err;
> + flagreg &= ~RX8010_FLAG_AF;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_FLAG, flagreg);
> + if (err < 0)
> + return err;
> +
> + if (ctrl != rx8010->ctrlreg) {
> + rx8010->ctrlreg = ctrl;
> + err = i2c_smbus_write_byte_data(rx8010->client, RX8010_CTRL,
> + rx8010->ctrlreg);
> + if (err < 0)
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int rx8010_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
> +{
> + struct i2c_client *client = to_i2c_client(dev);
> + struct rx8010_data *rx8010 = dev_get_drvdata(dev);
> + int ret, tmp;
> + u8 flagreg;
> + unsigned long irqflags;
> +
> + switch (cmd) {
> + case RTC_VL_READ:
> + ret = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (ret)
> + return ret;
> +
> + tmp = !!(flagreg & RX8010_FLAG_VLF);
> + if (copy_to_user((void __user *)arg, &tmp, sizeof(int)))
> + return -EFAULT;
> +
> + return 0;
> +
> + case RTC_VL_CLR:
> + spin_lock_irqsave(&rx8010->flags_lock, irqflags);
> + ret = rx8010_read_reg(rx8010->client, RX8010_FLAG, &flagreg);
> + if (ret < 0) {
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + return ret;
> + }
> +
> + flagreg &= ~RX8010_FLAG_VLF;
> + ret = i2c_smbus_write_byte_data(client, RX8010_FLAG, flagreg);
> + spin_unlock_irqrestore(&rx8010->flags_lock, irqflags);
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +
> + default:
> + return -ENOIOCTLCMD;
> + }
> +}
> +
> +static struct rtc_class_ops rx8010_rtc_ops = {
> + .read_time = rx8010_get_time,
> + .set_time = rx8010_set_time,
> + .ioctl = rx8010_ioctl,
> +};
> +
> +static int rx8010_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> + struct rx8010_data *rx8010;
> + int err = 0;
> +
> + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
> + | I2C_FUNC_SMBUS_I2C_BLOCK)) {
> + dev_err(&adapter->dev, "doesn't support required functionality\n");
> + return -EIO;
> + }
> +
> + rx8010 = devm_kzalloc(&client->dev, sizeof(struct rx8010_data),
> + GFP_KERNEL);
> + if (!rx8010)
> + return -ENOMEM;
> +
> + rx8010->client = client;
> + i2c_set_clientdata(client, rx8010);
> +
> + spin_lock_init(&rx8010->flags_lock);
> +
> + err = rx8010_init_client(client);
> + if (err)
> + return err;
> +
> + if (client->irq > 0) {
> + dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
> + err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
> + rx8010_irq_1_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "rx8010", client);
> +
> + if (err) {
> + dev_err(&client->dev, "unable to request IRQ\n");
> + client->irq = 0;
> + } else {
> + rx8010_rtc_ops.read_alarm = rx8010_read_alarm;
> + rx8010_rtc_ops.set_alarm = rx8010_set_alarm;
> + rx8010_rtc_ops.alarm_irq_enable = rx8010_alarm_irq_enable;
> + }
> + }
> +
> + rx8010->rtc = devm_rtc_device_register(&client->dev, client->name,
> + &rx8010_rtc_ops, THIS_MODULE);
> +
> + if (IS_ERR(rx8010->rtc)) {
> + dev_err(&client->dev, "unable to register the class device\n");
> + return PTR_ERR(rx8010->rtc);
> + }
> +
> + rx8010->rtc->max_user_freq = 1;
> +
> + return err;
> +}
> +
> +static struct i2c_driver rx8010_driver = {
> + .driver = {
> + .name = "rtc-rx8010",
> + },
> + .probe = rx8010_probe,
> + .id_table = rx8010_id,
> +};
> +
> +module_i2c_driver(rx8010_driver);
> +
> +MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>");
> +MODULE_DESCRIPTION("Epson RX8010SJ RTC driver");
> +MODULE_LICENSE("GPL");
>
next prev parent reply other threads:[~2015-12-01 16:00 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-11-11 22:31 [rtc-linux] [PATCH] rtc: Add Epson RX8010SJ RTC driver Akshay Bhat
2015-11-11 22:31 ` Akshay Bhat
2015-12-01 16:00 ` Akshay Bhat [this message]
2015-12-01 16:00 ` Akshay Bhat
2015-12-01 16:11 ` [rtc-linux] " Alexandre Belloni
2015-12-01 16:11 ` Alexandre Belloni
2015-12-02 23:40 ` [rtc-linux] " Alexandre Belloni
2015-12-02 23:40 ` Alexandre Belloni
2015-12-03 19:49 ` [rtc-linux] " Akshay Bhat
2015-12-03 19:49 ` Akshay Bhat
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=565DC410.8040205@timesys.com \
--to=akshay.bhat@timesys.com \
--cc=a.zummo@towertech.it \
--cc=alexandre.belloni@free-electrons.com \
--cc=justin.waters@timesys.com \
--cc=linux-kernel@vger.kernel.org \
--cc=rtc-linux@googlegroups.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.