summaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-max77686.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/rtc-max77686.c')
-rw-r--r--drivers/rtc/rtc-max77686.c201
1 files changed, 109 insertions, 92 deletions
diff --git a/drivers/rtc/rtc-max77686.c b/drivers/rtc/rtc-max77686.c
index 182fdd00e290..69ea3ce75b5a 100644
--- a/drivers/rtc/rtc-max77686.c
+++ b/drivers/rtc/rtc-max77686.c
@@ -1,16 +1,10 @@
-/*
- * RTC driver for Maxim MAX77686 and MAX77802
- *
- * Copyright (C) 2012 Samsung Electronics Co.Ltd
- *
- * based on rtc-max8997.c
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// RTC driver for Maxim MAX77686 and MAX77802
+//
+// Copyright (C) 2012 Samsung Electronics Co.Ltd
+//
+// based on rtc-max8997.c
#include <linux/i2c.h>
#include <linux/slab.h>
@@ -25,6 +19,7 @@
#define MAX77686_I2C_ADDR_RTC (0x0C >> 1)
#define MAX77620_I2C_ADDR_RTC 0x68
+#define MAX77714_I2C_ADDR_RTC 0x48
#define MAX77686_INVALID_I2C_ADDR (-1)
/* Define non existing register */
@@ -40,9 +35,6 @@
#define RTC_UDR_MASK BIT(RTC_UDR_SHIFT)
#define RTC_RBUDR_SHIFT 4
#define RTC_RBUDR_MASK BIT(RTC_RBUDR_SHIFT)
-/* RTC Hour register */
-#define HOUR_PM_SHIFT 6
-#define HOUR_PM_MASK BIT(HOUR_PM_SHIFT)
/* RTC Alarm Enable */
#define ALARM_ENABLE_SHIFT 7
#define ALARM_ENABLE_MASK BIT(ALARM_ENABLE_SHIFT)
@@ -63,32 +55,36 @@ enum {
RTC_WEEKDAY,
RTC_MONTH,
RTC_YEAR,
- RTC_DATE,
+ RTC_MONTHDAY,
RTC_NR_TIME
};
+/**
+ * struct max77686_rtc_driver_data - model-specific configuration
+ * @delay: Minimum usecs needed for a RTC update
+ * @mask: Mask used to read RTC registers value
+ * @map: Registers offset to I2C addresses map
+ * @alarm_enable_reg: Has a separate alarm enable register?
+ * @rtc_i2c_addr: I2C address for RTC block
+ * @rtc_irq_from_platform: RTC interrupt via platform resource
+ * @alarm_pending_status_reg: Pending alarm status register
+ * @rtc_irq_chip: RTC IRQ CHIP for regmap
+ * @regmap_config: regmap configuration for the chip
+ */
struct max77686_rtc_driver_data {
- /* Minimum usecs needed for a RTC update */
unsigned long delay;
- /* Mask used to read RTC registers value */
u8 mask;
- /* Registers offset to I2C addresses map */
const unsigned int *map;
- /* Has a separate alarm enable register? */
bool alarm_enable_reg;
- /* I2C address for RTC block */
int rtc_i2c_addr;
- /* RTC interrupt via platform resource */
bool rtc_irq_from_platform;
- /* Pending alarm status register */
int alarm_pending_status_reg;
- /* RTC IRQ CHIP for regmap */
const struct regmap_irq_chip *rtc_irq_chip;
+ const struct regmap_config *regmap_config;
};
struct max77686_rtc_info {
struct device *dev;
- struct i2c_client *rtc;
struct rtc_device *rtc_dev;
struct mutex lock;
@@ -100,7 +96,6 @@ struct max77686_rtc_info {
int rtc_irq;
int virq;
- int rtc_24hr_mode;
};
enum MAX77686_RTC_OP {
@@ -120,7 +115,7 @@ enum max77686_rtc_reg_offset {
REG_RTC_WEEKDAY,
REG_RTC_MONTH,
REG_RTC_YEAR,
- REG_RTC_DATE,
+ REG_RTC_MONTHDAY,
REG_ALARM1_SEC,
REG_ALARM1_MIN,
REG_ALARM1_HOUR,
@@ -151,7 +146,7 @@ static const unsigned int max77686_map[REG_RTC_END] = {
[REG_RTC_WEEKDAY] = MAX77686_RTC_WEEKDAY,
[REG_RTC_MONTH] = MAX77686_RTC_MONTH,
[REG_RTC_YEAR] = MAX77686_RTC_YEAR,
- [REG_RTC_DATE] = MAX77686_RTC_DATE,
+ [REG_RTC_MONTHDAY] = MAX77686_RTC_MONTHDAY,
[REG_ALARM1_SEC] = MAX77686_ALARM1_SEC,
[REG_ALARM1_MIN] = MAX77686_ALARM1_MIN,
[REG_ALARM1_HOUR] = MAX77686_ALARM1_HOUR,
@@ -188,6 +183,11 @@ static const struct regmap_irq_chip max77686_rtc_irq_chip = {
.num_irqs = ARRAY_SIZE(max77686_rtc_irqs),
};
+static const struct regmap_config max77686_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
static const struct max77686_rtc_driver_data max77686_drv_data = {
.delay = 16000,
.mask = 0x7f,
@@ -197,6 +197,35 @@ static const struct max77686_rtc_driver_data max77686_drv_data = {
.alarm_pending_status_reg = MAX77686_REG_STATUS2,
.rtc_i2c_addr = MAX77686_I2C_ADDR_RTC,
.rtc_irq_chip = &max77686_rtc_irq_chip,
+ .regmap_config = &max77686_rtc_regmap_config,
+};
+
+static const struct regmap_irq_chip max77714_rtc_irq_chip = {
+ .name = "max77714-rtc",
+ .status_base = MAX77686_RTC_INT,
+ .mask_base = MAX77686_RTC_INTM,
+ .num_regs = 1,
+ .irqs = max77686_rtc_irqs,
+ .num_irqs = ARRAY_SIZE(max77686_rtc_irqs) - 1, /* no WTSR on 77714 */
+};
+
+static const struct max77686_rtc_driver_data max77714_drv_data = {
+ .delay = 16000,
+ .mask = 0x7f,
+ .map = max77686_map,
+ .alarm_enable_reg = false,
+ .rtc_irq_from_platform = false,
+ /* On MAX77714 RTCA1 is BIT 1 of RTCINT (0x00). Not supported by this driver. */
+ .alarm_pending_status_reg = MAX77686_INVALID_REG,
+ .rtc_i2c_addr = MAX77714_I2C_ADDR_RTC,
+ .rtc_irq_chip = &max77714_rtc_irq_chip,
+ .regmap_config = &max77686_rtc_regmap_config,
+};
+
+static const struct regmap_config max77620_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_write = true,
};
static const struct max77686_rtc_driver_data max77620_drv_data = {
@@ -208,6 +237,7 @@ static const struct max77686_rtc_driver_data max77620_drv_data = {
.alarm_pending_status_reg = MAX77686_INVALID_REG,
.rtc_i2c_addr = MAX77620_I2C_ADDR_RTC,
.rtc_irq_chip = &max77686_rtc_irq_chip,
+ .regmap_config = &max77620_rtc_regmap_config,
};
static const unsigned int max77802_map[REG_RTC_END] = {
@@ -221,7 +251,7 @@ static const unsigned int max77802_map[REG_RTC_END] = {
[REG_RTC_WEEKDAY] = MAX77802_RTC_WEEKDAY,
[REG_RTC_MONTH] = MAX77802_RTC_MONTH,
[REG_RTC_YEAR] = MAX77802_RTC_YEAR,
- [REG_RTC_DATE] = MAX77802_RTC_DATE,
+ [REG_RTC_MONTHDAY] = MAX77802_RTC_MONTHDAY,
[REG_ALARM1_SEC] = MAX77802_ALARM1_SEC,
[REG_ALARM1_MIN] = MAX77802_ALARM1_MIN,
[REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR,
@@ -266,17 +296,11 @@ static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
tm->tm_sec = data[RTC_SEC] & mask;
tm->tm_min = data[RTC_MIN] & mask;
- if (info->rtc_24hr_mode) {
- tm->tm_hour = data[RTC_HOUR] & 0x1f;
- } else {
- tm->tm_hour = data[RTC_HOUR] & 0x0f;
- if (data[RTC_HOUR] & HOUR_PM_MASK)
- tm->tm_hour += 12;
- }
+ tm->tm_hour = data[RTC_HOUR] & 0x1f;
/* Only a single bit is set in data[], so fls() would be equivalent */
tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1;
- tm->tm_mday = data[RTC_DATE] & 0x1f;
+ tm->tm_mday = data[RTC_MONTHDAY] & 0x1f;
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
tm->tm_year = data[RTC_YEAR] & mask;
tm->tm_yday = 0;
@@ -297,7 +321,7 @@ static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data,
data[RTC_MIN] = tm->tm_min;
data[RTC_HOUR] = tm->tm_hour;
data[RTC_WEEKDAY] = 1 << tm->tm_wday;
- data[RTC_DATE] = tm->tm_mday;
+ data[RTC_MONTHDAY] = tm->tm_mday;
data[RTC_MONTH] = tm->tm_mon + 1;
if (info->drv_data->alarm_enable_reg) {
@@ -364,8 +388,6 @@ static int max77686_rtc_read_time(struct device *dev, struct rtc_time *tm)
max77686_rtc_data_to_tm(data, tm, info);
- ret = rtc_valid_tm(tm);
-
out:
mutex_unlock(&info->lock);
return ret;
@@ -555,8 +577,8 @@ static int max77686_rtc_start_alarm(struct max77686_rtc_info *info)
data[RTC_MONTH] |= (1 << ALARM_ENABLE_SHIFT);
if (data[RTC_YEAR] & info->drv_data->mask)
data[RTC_YEAR] |= (1 << ALARM_ENABLE_SHIFT);
- if (data[RTC_DATE] & 0x1f)
- data[RTC_DATE] |= (1 << ALARM_ENABLE_SHIFT);
+ if (data[RTC_MONTHDAY] & 0x1f)
+ data[RTC_MONTHDAY] |= (1 << ALARM_ENABLE_SHIFT);
ret = regmap_bulk_write(info->rtc_regmap, map[REG_ALARM1_SEC],
data, ARRAY_SIZE(data));
@@ -652,8 +674,6 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
data[0] = (1 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
data[1] = (0 << BCD_EN_SHIFT) | (1 << MODEL24_SHIFT);
- info->rtc_24hr_mode = 1;
-
ret = regmap_bulk_write(info->rtc_regmap,
info->drv_data->map[REG_RTC_CONTROLM],
data, ARRAY_SIZE(data));
@@ -666,72 +686,55 @@ static int max77686_rtc_init_reg(struct max77686_rtc_info *info)
return ret;
}
-static const struct regmap_config max77686_rtc_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-};
-
static int max77686_init_rtc_regmap(struct max77686_rtc_info *info)
{
struct device *parent = info->dev->parent;
struct i2c_client *parent_i2c = to_i2c_client(parent);
+ struct i2c_client *client;
int ret;
if (info->drv_data->rtc_irq_from_platform) {
struct platform_device *pdev = to_platform_device(info->dev);
info->rtc_irq = platform_get_irq(pdev, 0);
- if (info->rtc_irq < 0) {
- dev_err(info->dev, "Failed to get rtc interrupts: %d\n",
- info->rtc_irq);
+ if (info->rtc_irq < 0)
return info->rtc_irq;
- }
} else {
info->rtc_irq = parent_i2c->irq;
}
info->regmap = dev_get_regmap(parent, NULL);
- if (!info->regmap) {
- dev_err(info->dev, "Failed to get rtc regmap\n");
- return -ENODEV;
- }
+ if (!info->regmap)
+ return dev_err_probe(info->dev, -ENODEV,
+ "Failed to get rtc regmap\n");
if (info->drv_data->rtc_i2c_addr == MAX77686_INVALID_I2C_ADDR) {
info->rtc_regmap = info->regmap;
goto add_rtc_irq;
}
- info->rtc = i2c_new_dummy(parent_i2c->adapter,
- info->drv_data->rtc_i2c_addr);
- if (!info->rtc) {
- dev_err(info->dev, "Failed to allocate I2C device for RTC\n");
- return -ENODEV;
- }
+ client = devm_i2c_new_dummy_device(info->dev, parent_i2c->adapter,
+ info->drv_data->rtc_i2c_addr);
+ if (IS_ERR(client))
+ return dev_err_probe(info->dev, PTR_ERR(client),
+ "Failed to allocate I2C device for RTC\n");
- info->rtc_regmap = devm_regmap_init_i2c(info->rtc,
- &max77686_rtc_regmap_config);
- if (IS_ERR(info->rtc_regmap)) {
- ret = PTR_ERR(info->rtc_regmap);
- dev_err(info->dev, "Failed to allocate RTC regmap: %d\n", ret);
- goto err_unregister_i2c;
- }
+ info->rtc_regmap = devm_regmap_init_i2c(client,
+ info->drv_data->regmap_config);
+ if (IS_ERR(info->rtc_regmap))
+ return dev_err_probe(info->dev, PTR_ERR(info->rtc_regmap),
+ "Failed to allocate RTC regmap\n");
add_rtc_irq:
ret = regmap_add_irq_chip(info->rtc_regmap, info->rtc_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT |
- IRQF_SHARED, 0, info->drv_data->rtc_irq_chip,
+ IRQF_ONESHOT | IRQF_SHARED,
+ 0, info->drv_data->rtc_irq_chip,
&info->rtc_irq_data);
- if (ret < 0) {
- dev_err(info->dev, "Failed to add RTC irq chip: %d\n", ret);
- goto err_unregister_i2c;
- }
+ if (ret < 0)
+ return dev_err_probe(info->dev, ret,
+ "Failed to add RTC irq chip\n");
return 0;
-
-err_unregister_i2c:
- if (info->rtc)
- i2c_unregister_device(info->rtc);
- return ret;
}
static int max77686_rtc_probe(struct platform_device *pdev)
@@ -762,7 +765,7 @@ static int max77686_rtc_probe(struct platform_device *pdev)
goto err_rtc;
}
- device_init_wakeup(&pdev->dev, 1);
+ device_init_wakeup(&pdev->dev, true);
info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name,
&max77686_rtc_ops, THIS_MODULE);
@@ -794,38 +797,51 @@ static int max77686_rtc_probe(struct platform_device *pdev)
err_rtc:
regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
- if (info->rtc)
- i2c_unregister_device(info->rtc);
return ret;
}
-static int max77686_rtc_remove(struct platform_device *pdev)
+static void max77686_rtc_remove(struct platform_device *pdev)
{
struct max77686_rtc_info *info = platform_get_drvdata(pdev);
free_irq(info->virq, info);
regmap_del_irq_chip(info->rtc_irq, info->rtc_irq_data);
- if (info->rtc)
- i2c_unregister_device(info->rtc);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
static int max77686_rtc_suspend(struct device *dev)
{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+ int ret = 0;
+
if (device_may_wakeup(dev)) {
struct max77686_rtc_info *info = dev_get_drvdata(dev);
- return enable_irq_wake(info->virq);
+ ret = enable_irq_wake(info->virq);
}
- return 0;
+ /*
+ * If the main IRQ (not virtual) is the parent IRQ, then it must be
+ * disabled during suspend because if it happens while suspended it
+ * will be handled before resuming I2C.
+ *
+ * Since Main IRQ is shared, all its users should disable it to be sure
+ * it won't fire while one of them is still suspended.
+ */
+ if (!info->drv_data->rtc_irq_from_platform)
+ disable_irq(info->rtc_irq);
+
+ return ret;
}
static int max77686_rtc_resume(struct device *dev)
{
+ struct max77686_rtc_info *info = dev_get_drvdata(dev);
+
+ if (!info->drv_data->rtc_irq_from_platform)
+ enable_irq(info->rtc_irq);
+
if (device_may_wakeup(dev)) {
struct max77686_rtc_info *info = dev_get_drvdata(dev);
@@ -843,6 +859,7 @@ static const struct platform_device_id rtc_id[] = {
{ "max77686-rtc", .driver_data = (kernel_ulong_t)&max77686_drv_data, },
{ "max77802-rtc", .driver_data = (kernel_ulong_t)&max77802_drv_data, },
{ "max77620-rtc", .driver_data = (kernel_ulong_t)&max77620_drv_data, },
+ { "max77714-rtc", .driver_data = (kernel_ulong_t)&max77714_drv_data, },
{},
};
MODULE_DEVICE_TABLE(platform, rtc_id);