rtc-isl12057.c revision 70e123373c05e336f3f769b32c6132bdc2d6f5ac
170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/*
270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * rtc-isl12057 - Driver for Intersil ISL12057 I2C Real Time Clock
370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *
470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * Copyright (C) 2013, Arnaud EBALARD <arno@natisbad.org>
570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *
670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * This work is largely based on Intersil ISL1208 driver developed by
770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * Hebert Valerio Riedel <hvr@gnu.org>.
870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *
970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * Detailed datasheet on which this development is based is available here:
1070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *
1170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *  http://natisbad.org/NAS2/refs/ISL12057.pdf
1270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *
1370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * This program is free software; you can redistribute it and/or modify
1470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * it under the terms of the GNU General Public License as published by
1570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * the Free Software Foundation; either version 2 of the License, or
1670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * (at your option) any later version.
1770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard *
1870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * This program is distributed in the hope that it will be useful,
1970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * but WITHOUT ANY WARRANTY; without even the implied warranty of
2070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * GNU General Public License for more details.
2270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard */
2370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
2470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/module.h>
2570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/mutex.h>
2670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/rtc.h>
2770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/i2c.h>
2870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/bcd.h>
2970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/rtc.h>
3070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/of.h>
3170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/of_device.h>
3270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#include <linux/regmap.h>
3370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
3470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define DRV_NAME "rtc-isl12057"
3570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
3670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/* RTC section */
3770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_SC	0x00	/* Seconds */
3870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_MN	0x01	/* Minutes */
3970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_HR	0x02	/* Hours */
4070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_HR_PM	BIT(5)	/* AM/PM bit in 12h format */
4170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_HR_MIL BIT(6)	/* 24h/12h format */
4270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_DW	0x03	/* Day of the Week */
4370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_DT	0x04	/* Date */
4470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_MO	0x05	/* Month */
4570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_RTC_YR	0x06	/* Year */
4670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_RTC_SEC_LEN	7
4770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
4870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/* Alarm 1 section */
4970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_SC	0x07	/* Alarm 1 Seconds */
5070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_MN	0x08	/* Alarm 1 Minutes */
5170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_HR	0x09	/* Alarm 1 Hours */
5270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_HR_PM	BIT(5)	/* AM/PM bit in 12h format */
5370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_HR_MIL	BIT(6)	/* 24h/12h format */
5470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_DWDT	0x0A	/* Alarm 1 Date / Day of the week */
5570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A1_DWDT_B	BIT(6)	/* DW / DT selection bit */
5670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_A1_SEC_LEN	4
5770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
5870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/* Alarm 2 section */
5970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A2_MN	0x0B	/* Alarm 2 Minutes */
6070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A2_HR	0x0C	/* Alarm 2 Hours */
6170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_A2_DWDT	0x0D	/* Alarm 2 Date / Day of the week */
6270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_A2_SEC_LEN	3
6370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
6470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/* Control/Status registers */
6570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT	0x0E
6670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT_A1IE	BIT(0)	/* Alarm 1 interrupt enable bit */
6770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT_A2IE	BIT(1)	/* Alarm 2 interrupt enable bit */
6870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT_INTCN	BIT(2)	/* Interrupt control enable bit */
6970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT_RS1	BIT(3)	/* Freq out control bit 1 */
7070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT_RS2	BIT(4)	/* Freq out control bit 2 */
7170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_INT_EOSC	BIT(7)	/* Oscillator enable bit */
7270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
7370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_SR		0x0F
7470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_SR_A1F	BIT(0)	/* Alarm 1 interrupt bit */
7570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_SR_A2F	BIT(1)	/* Alarm 2 interrupt bit */
7670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_REG_SR_OSF	BIT(7)	/* Oscillator failure bit */
7770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
7870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/* Register memory map length */
7970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#define ISL12057_MEM_MAP_LEN	0x10
8070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
8170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstruct isl12057_rtc_data {
8270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct regmap *regmap;
8370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct mutex lock;
8470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard};
8570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
8670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic void isl12057_rtc_regs_to_tm(struct rtc_time *tm, u8 *regs)
8770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
8870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	tm->tm_sec = bcd2bin(regs[ISL12057_REG_RTC_SC]);
8970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	tm->tm_min = bcd2bin(regs[ISL12057_REG_RTC_MN]);
9070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
9170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_MIL) { /* AM/PM */
9270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x0f);
9370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		if (regs[ISL12057_REG_RTC_HR] & ISL12057_REG_RTC_HR_PM)
9470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard			tm->tm_hour += 12;
9570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	} else {					    /* 24 hour mode */
9670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		tm->tm_hour = bcd2bin(regs[ISL12057_REG_RTC_HR] & 0x3f);
9770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
9870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
9970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	tm->tm_mday = bcd2bin(regs[ISL12057_REG_RTC_DT]);
10070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	tm->tm_wday = bcd2bin(regs[ISL12057_REG_RTC_DW]) - 1; /* starts at 1 */
10170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	tm->tm_mon  = bcd2bin(regs[ISL12057_REG_RTC_MO]) - 1; /* starts at 1 */
10270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	tm->tm_year = bcd2bin(regs[ISL12057_REG_RTC_YR]) + 100;
10370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
10470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
10570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic int isl12057_rtc_tm_to_regs(u8 *regs, struct rtc_time *tm)
10670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
10770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	/*
10870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	 * The clock has an 8 bit wide bcd-coded register for the year.
10970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	 * tm_year is an offset from 1900 and we are interested in the
11070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	 * 2000-2099 range, so any value less than 100 is invalid.
11170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	 */
11270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (tm->tm_year < 100)
11370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return -EINVAL;
11470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
11570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_SC] = bin2bcd(tm->tm_sec);
11670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_MN] = bin2bcd(tm->tm_min);
11770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_HR] = bin2bcd(tm->tm_hour); /* 24-hour format */
11870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_DT] = bin2bcd(tm->tm_mday);
11970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_MO] = bin2bcd(tm->tm_mon + 1);
12070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_YR] = bin2bcd(tm->tm_year - 100);
12170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regs[ISL12057_REG_RTC_DW] = bin2bcd(tm->tm_wday + 1);
12270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
12370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	return 0;
12470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
12570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
12670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/*
12770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * Try and match register bits w/ fixed null values to see whether we
12870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * are dealing with an ISL12057. Note: this function is called early
12970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * during init and hence does need mutex protection.
13070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard */
13170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic int isl12057_i2c_validate_chip(struct regmap *regmap)
13270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
13370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	u8 regs[ISL12057_MEM_MAP_LEN];
13470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	static const u8 mask[ISL12057_MEM_MAP_LEN] = { 0x80, 0x80, 0x80, 0xf8,
13570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard						       0xc0, 0x60, 0x00, 0x00,
13670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard						       0x00, 0x00, 0x00, 0x00,
13770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard						       0x00, 0x00, 0x60, 0x7c };
13870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	int ret, i;
13970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
14070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = regmap_bulk_read(regmap, 0, regs, ISL12057_MEM_MAP_LEN);
14170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret)
14270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
14370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
14470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	for (i = 0; i < ISL12057_MEM_MAP_LEN; ++i) {
14570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		if (regs[i] & mask[i])	/* check if bits are cleared */
14670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard			return -ENODEV;
14770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
14870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
14970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	return 0;
15070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
15170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
15270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic int isl12057_rtc_read_time(struct device *dev, struct rtc_time *tm)
15370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
15470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct isl12057_rtc_data *data = dev_get_drvdata(dev);
15570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	u8 regs[ISL12057_RTC_SEC_LEN];
15670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	int ret;
15770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
15870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	mutex_lock(&data->lock);
15970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = regmap_bulk_read(data->regmap, ISL12057_REG_RTC_SC, regs,
16070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard			       ISL12057_RTC_SEC_LEN);
16170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	mutex_unlock(&data->lock);
16270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
16370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret) {
16470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		dev_err(dev, "%s: RTC read failed\n", __func__);
16570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
16670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
16770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
16870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	isl12057_rtc_regs_to_tm(tm, regs);
16970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
17070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	return rtc_valid_tm(tm);
17170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
17270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
17370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic int isl12057_rtc_set_time(struct device *dev, struct rtc_time *tm)
17470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
17570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct isl12057_rtc_data *data = dev_get_drvdata(dev);
17670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	u8 regs[ISL12057_RTC_SEC_LEN];
17770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	int ret;
17870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
17970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = isl12057_rtc_tm_to_regs(regs, tm);
18070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret)
18170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
18270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
18370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	mutex_lock(&data->lock);
18470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = regmap_bulk_write(data->regmap, ISL12057_REG_RTC_SC, regs,
18570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard				ISL12057_RTC_SEC_LEN);
18670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	mutex_unlock(&data->lock);
18770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
18870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret)
18970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		dev_err(dev, "%s: RTC write failed\n", __func__);
19070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
19170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	return ret;
19270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
19370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
19470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard/*
19570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * Check current RTC status and enable/disable what needs to be. Return 0 if
19670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * everything went ok and a negative value upon error. Note: this function
19770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard * is called early during init and hence does need mutex protection.
19870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard */
19970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap)
20070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
20170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	int ret;
20270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
20370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	/* Enable oscillator if not already running */
20470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = regmap_update_bits(regmap, ISL12057_REG_INT,
20570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard				 ISL12057_REG_INT_EOSC, 0);
20670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret < 0) {
20770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		dev_err(dev, "Unable to enable oscillator\n");
20870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
20970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
21070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
21170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	/* Clear oscillator failure bit if needed */
21270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = regmap_update_bits(regmap, ISL12057_REG_SR,
21370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard				 ISL12057_REG_SR_OSF, 0);
21470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret < 0) {
21570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		dev_err(dev, "Unable to clear oscillator failure bit\n");
21670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
21770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
21870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
21970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	/* Clear alarm bit if needed */
22070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = regmap_update_bits(regmap, ISL12057_REG_SR,
22170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard				 ISL12057_REG_SR_A1F, 0);
22270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret < 0) {
22370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		dev_err(dev, "Unable to clear alarm bit\n");
22470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
22570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
22670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
22770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	return 0;
22870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
22970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
23070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic const struct rtc_class_ops rtc_ops = {
23170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.read_time = isl12057_rtc_read_time,
23270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.set_time = isl12057_rtc_set_time,
23370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard};
23470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
23570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic struct regmap_config isl12057_rtc_regmap_config = {
23670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.reg_bits = 8,
23770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.val_bits = 8,
23870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard};
23970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
24070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic int isl12057_probe(struct i2c_client *client,
24170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard			  const struct i2c_device_id *id)
24270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard{
24370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct device *dev = &client->dev;
24470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct isl12057_rtc_data *data;
24570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct rtc_device *rtc;
24670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	struct regmap *regmap;
24770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	int ret;
24870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
24970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
25070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard				     I2C_FUNC_SMBUS_BYTE_DATA |
25170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard				     I2C_FUNC_SMBUS_I2C_BLOCK))
25270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return -ENODEV;
25370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
25470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	regmap = devm_regmap_init_i2c(client, &isl12057_rtc_regmap_config);
25570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (IS_ERR(regmap)) {
25670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		ret = PTR_ERR(regmap);
25770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		dev_err(dev, "regmap allocation failed: %d\n", ret);
25870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
25970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	}
26070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
26170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = isl12057_i2c_validate_chip(regmap);
26270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret)
26370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
26470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
26570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	ret = isl12057_check_rtc_status(dev, regmap);
26670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (ret)
26770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return ret;
26870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
26970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
27070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (!data)
27170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return -ENOMEM;
27270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
27370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	mutex_init(&data->lock);
27470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	data->regmap = regmap;
27570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	dev_set_drvdata(dev, data);
27670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
27770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	rtc = devm_rtc_device_register(dev, DRV_NAME, &rtc_ops, THIS_MODULE);
27870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	if (IS_ERR(rtc))
27970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		return PTR_ERR(rtc);
28070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
28170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	return 0;
28270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard}
28370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
28470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#ifdef CONFIG_OF
28570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic struct of_device_id isl12057_dt_match[] = {
28670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	{ .compatible = "isl,isl12057" },
28770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	{ },
28870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard};
28970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard#endif
29070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
29170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic const struct i2c_device_id isl12057_id[] = {
29270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	{ "isl12057", 0 },
29370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	{ }
29470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard};
29570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud EbalardMODULE_DEVICE_TABLE(i2c, isl12057_id);
29670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
29770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardstatic struct i2c_driver isl12057_driver = {
29870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.driver = {
29970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		.name = DRV_NAME,
30070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		.owner = THIS_MODULE,
30170e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard		.of_match_table = of_match_ptr(isl12057_dt_match),
30270e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	},
30370e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.probe	  = isl12057_probe,
30470e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard	.id_table = isl12057_id,
30570e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard};
30670e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalardmodule_i2c_driver(isl12057_driver);
30770e123373c05e336f3f769b32c6132bdc2d6f5acArnaud Ebalard
30870e123373c05e336f3f769b32c6132bdc2d6f5acArnaud EbalardMODULE_AUTHOR("Arnaud EBALARD <arno@natisbad.org>");
30970e123373c05e336f3f769b32c6132bdc2d6f5acArnaud EbalardMODULE_DESCRIPTION("Intersil ISL12057 RTC driver");
31070e123373c05e336f3f769b32c6132bdc2d6f5acArnaud EbalardMODULE_LICENSE("GPL");
311