/*
- * RTC driver for Maxim MAX77686
+ * RTC driver for Maxim MAX77686 and MAX77802
*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
*
#define ALARM_ENABLE_SHIFT 7
#define ALARM_ENABLE_MASK (1 << ALARM_ENABLE_SHIFT)
+#define REG_RTC_NONE 0xdeadbeef
+
+/*
+ * MAX77802 has separate register (RTCAE1) for alarm enable instead
+ * using 1 bit from registers RTC{SEC,MIN,HOUR,DAY,MONTH,YEAR,DATE}
+ * as in done in MAX77686.
+ */
+#define MAX77802_ALARM_ENABLE_VALUE 0x77
+
enum {
RTC_SEC = 0,
RTC_MIN,
u8 mask;
/* Registers offset to I2C addresses map */
const unsigned int *map;
+ /* Has a separate alarm enable register? */
+ bool alarm_enable_reg;
+ /* Has a separate I2C regmap for the RTC? */
+ bool separate_i2c_addr;
};
struct max77686_rtc_info {
REG_ALARM2_MONTH,
REG_ALARM2_YEAR,
REG_ALARM2_DATE,
+ REG_RTC_AE1,
REG_RTC_END,
};
[REG_ALARM2_MONTH] = MAX77686_ALARM2_MONTH,
[REG_ALARM2_YEAR] = MAX77686_ALARM2_YEAR,
[REG_ALARM2_DATE] = MAX77686_ALARM2_DATE,
+ [REG_RTC_AE1] = REG_RTC_NONE,
};
static const struct max77686_rtc_driver_data max77686_drv_data = {
.delay = 16000,
.mask = 0x7f,
.map = max77686_map,
+ .alarm_enable_reg = false,
+ .separate_i2c_addr = true,
+};
+
+static const unsigned int max77802_map[REG_RTC_END] = {
+ [REG_RTC_CONTROLM] = MAX77802_RTC_CONTROLM,
+ [REG_RTC_CONTROL] = MAX77802_RTC_CONTROL,
+ [REG_RTC_UPDATE0] = MAX77802_RTC_UPDATE0,
+ [REG_WTSR_SMPL_CNTL] = MAX77802_WTSR_SMPL_CNTL,
+ [REG_RTC_SEC] = MAX77802_RTC_SEC,
+ [REG_RTC_MIN] = MAX77802_RTC_MIN,
+ [REG_RTC_HOUR] = MAX77802_RTC_HOUR,
+ [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_ALARM1_SEC] = MAX77802_ALARM1_SEC,
+ [REG_ALARM1_MIN] = MAX77802_ALARM1_MIN,
+ [REG_ALARM1_HOUR] = MAX77802_ALARM1_HOUR,
+ [REG_ALARM1_WEEKDAY] = MAX77802_ALARM1_WEEKDAY,
+ [REG_ALARM1_MONTH] = MAX77802_ALARM1_MONTH,
+ [REG_ALARM1_YEAR] = MAX77802_ALARM1_YEAR,
+ [REG_ALARM1_DATE] = MAX77802_ALARM1_DATE,
+ [REG_ALARM2_SEC] = MAX77802_ALARM2_SEC,
+ [REG_ALARM2_MIN] = MAX77802_ALARM2_MIN,
+ [REG_ALARM2_HOUR] = MAX77802_ALARM2_HOUR,
+ [REG_ALARM2_WEEKDAY] = MAX77802_ALARM2_WEEKDAY,
+ [REG_ALARM2_MONTH] = MAX77802_ALARM2_MONTH,
+ [REG_ALARM2_YEAR] = MAX77802_ALARM2_YEAR,
+ [REG_ALARM2_DATE] = MAX77802_ALARM2_DATE,
+ [REG_RTC_AE1] = MAX77802_RTC_AE1,
+};
+
+static const struct max77686_rtc_driver_data max77802_drv_data = {
+ .delay = 200,
+ .mask = 0xff,
+ .map = max77802_map,
+ .alarm_enable_reg = true,
+ .separate_i2c_addr = false,
};
static void max77686_rtc_data_to_tm(u8 *data, struct rtc_time *tm,
tm->tm_wday = ffs(data[RTC_WEEKDAY] & mask) - 1;
tm->tm_mday = data[RTC_DATE] & 0x1f;
tm->tm_mon = (data[RTC_MONTH] & 0x0f) - 1;
- tm->tm_year = (data[RTC_YEAR] & mask) + 100;
+ tm->tm_year = data[RTC_YEAR] & mask;
tm->tm_yday = 0;
tm->tm_isdst = 0;
+
+ /*
+ * MAX77686 uses 1 bit from sec/min/hour/etc RTC registers and the
+ * year values are just 0..99 so add 100 to support up to 2099.
+ */
+ if (!info->drv_data->alarm_enable_reg)
+ tm->tm_year += 100;
}
-static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data)
+static int max77686_rtc_tm_to_data(struct rtc_time *tm, u8 *data,
+ struct max77686_rtc_info *info)
{
data[RTC_SEC] = tm->tm_sec;
data[RTC_MIN] = tm->tm_min;
data[RTC_WEEKDAY] = 1 << tm->tm_wday;
data[RTC_DATE] = tm->tm_mday;
data[RTC_MONTH] = tm->tm_mon + 1;
+
+ if (info->drv_data->alarm_enable_reg) {
+ data[RTC_YEAR] = tm->tm_year;
+ return 0;
+ }
+
data[RTC_YEAR] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0;
if (tm->tm_year < 100) {
1900 + tm->tm_year);
return -EINVAL;
}
+
return 0;
}
u8 data[RTC_NR_TIME];
int ret;
- ret = max77686_rtc_tm_to_data(tm, data);
+ ret = max77686_rtc_tm_to_data(tm, data, info);
if (ret < 0)
return ret;
max77686_rtc_data_to_tm(data, &alrm->time, info);
alrm->enabled = 0;
- for (i = 0; i < ARRAY_SIZE(data); i++) {
- if (data[i] & ALARM_ENABLE_MASK) {
+
+ if (info->drv_data->alarm_enable_reg) {
+ if (map[REG_RTC_AE1] == REG_RTC_NONE) {
+ ret = -EINVAL;
+ dev_err(info->dev,
+ "alarm enable register not set(%d)\n", ret);
+ goto out;
+ }
+
+ ret = regmap_read(info->max77686->regmap,
+ map[REG_RTC_AE1], &val);
+ if (ret < 0) {
+ dev_err(info->dev,
+ "fail to read alarm enable(%d)\n", ret);
+ goto out;
+ }
+
+ if (val)
alrm->enabled = 1;
- break;
+ } else {
+ for (i = 0; i < ARRAY_SIZE(data); i++) {
+ if (data[i] & ALARM_ENABLE_MASK) {
+ alrm->enabled = 1;
+ break;
+ }
}
}
if (ret < 0)
goto out;
- ret = regmap_bulk_read(info->max77686->rtc_regmap,
- map[REG_ALARM1_SEC], data, ARRAY_SIZE(data));
- if (ret < 0) {
- dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ if (info->drv_data->alarm_enable_reg) {
+ if (map[REG_RTC_AE1] == REG_RTC_NONE) {
+ ret = -EINVAL;
+ dev_err(info->dev,
+ "alarm enable register not set(%d)\n", ret);
+ goto out;
+ }
+
+ ret = regmap_write(info->max77686->regmap, map[REG_RTC_AE1], 0);
+ } else {
+ ret = regmap_bulk_read(info->max77686->rtc_regmap,
+ map[REG_ALARM1_SEC], data,
+ ARRAY_SIZE(data));
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
- goto out;
- }
+ goto out;
+ }
- max77686_rtc_data_to_tm(data, &tm, info);
+ max77686_rtc_data_to_tm(data, &tm, info);
- for (i = 0; i < ARRAY_SIZE(data); i++)
- data[i] &= ~ALARM_ENABLE_MASK;
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ data[i] &= ~ALARM_ENABLE_MASK;
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap,
+ map[REG_ALARM1_SEC], data,
+ ARRAY_SIZE(data));
+ }
- ret = regmap_bulk_write(info->max77686->rtc_regmap,
- map[REG_ALARM1_SEC], data, ARRAY_SIZE(data));
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
if (ret < 0)
goto out;
- ret = regmap_bulk_read(info->max77686->rtc_regmap,
- map[REG_ALARM1_SEC], data, ARRAY_SIZE(data));
- if (ret < 0) {
- dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
+ if (info->drv_data->alarm_enable_reg) {
+ ret = regmap_write(info->max77686->regmap, map[REG_RTC_AE1],
+ MAX77802_ALARM_ENABLE_VALUE);
+ } else {
+ ret = regmap_bulk_read(info->max77686->rtc_regmap,
+ map[REG_ALARM1_SEC], data,
+ ARRAY_SIZE(data));
+ if (ret < 0) {
+ dev_err(info->dev, "%s: fail to read alarm reg(%d)\n",
__func__, ret);
- goto out;
- }
-
- max77686_rtc_data_to_tm(data, &tm, info);
+ goto out;
+ }
- data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
- data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
- data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
- data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
- if (data[RTC_MONTH] & 0xf)
- 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);
+ max77686_rtc_data_to_tm(data, &tm, info);
+
+ data[RTC_SEC] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_MIN] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_HOUR] |= (1 << ALARM_ENABLE_SHIFT);
+ data[RTC_WEEKDAY] &= ~ALARM_ENABLE_MASK;
+ if (data[RTC_MONTH] & 0xf)
+ 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);
+
+ ret = regmap_bulk_write(info->max77686->rtc_regmap,
+ map[REG_ALARM1_SEC], data,
+ ARRAY_SIZE(data));
+ }
- ret = regmap_bulk_write(info->max77686->rtc_regmap,
- map[REG_ALARM1_SEC], data, ARRAY_SIZE(data));
if (ret < 0) {
dev_err(info->dev, "%s: fail to write alarm reg(%d)\n",
__func__, ret);
u8 data[RTC_NR_TIME];
int ret;
- ret = max77686_rtc_tm_to_data(&alrm->time, data);
+ ret = max77686_rtc_tm_to_data(&alrm->time, data, info);
if (ret < 0)
return ret;
info->drv_data = (const struct max77686_rtc_driver_data *)
id->driver_data;
+ if (!info->drv_data->separate_i2c_addr)
+ info->max77686->rtc_regmap = info->max77686->regmap;
+
platform_set_drvdata(pdev, info);
ret = max77686_rtc_init_reg(info);
device_init_wakeup(&pdev->dev, 1);
- info->rtc_dev = devm_rtc_device_register(&pdev->dev, "max77686-rtc",
+ info->rtc_dev = devm_rtc_device_register(&pdev->dev, id->name,
&max77686_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) {
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, },
{},
};
MODULE_DEVICE_TABLE(platform, rtc_id);