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