// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Intersil ISL1208 rtc class driver
*
* Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org>
*/
#include <linux/bcd.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/rtc.h>
/* Register map */
/* rtc section */
#define ISL1208_REG_SC 0x00
#define ISL1208_REG_MN 0x01
#define ISL1208_REG_HR 0x02
#define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */
#define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
#define ISL1208_REG_DT 0x03
#define ISL1208_REG_MO 0x04
#define ISL1208_REG_YR 0x05
#define ISL1208_REG_DW 0x06
#define ISL1208_RTC_SECTION_LEN 7
/* control/status section */
#define ISL1208_REG_SR 0x07
#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */
#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */
#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */
#define ISL1208_REG_SR_EVT (1<<3) /* event */
#define ISL1208_REG_SR_ALM (1<<2) /* alarm */
#define ISL1208_REG_SR_BAT (1<<1) /* battery */
#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
#define ISL1208_REG_INT 0x08
#define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */
#define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */
#define ISL1219_REG_EV 0x09
#define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */
#define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */
#define ISL1208_REG_ATR 0x0a
#define ISL1208_REG_DTR 0x0b
/* alarm section */
#define ISL1208_REG_SCA 0x0c
#define ISL1208_REG_MNA 0x0d
#define ISL1208_REG_HRA 0x0e
#define ISL1208_REG_DTA 0x0f
#define ISL1208_REG_MOA 0x10
#define ISL1208_REG_DWA 0x11
#define ISL1208_ALARM_SECTION_LEN 6
/* user section */
#define ISL1208_REG_USR1 0x12
#define ISL1208_REG_USR2 0x13
#define ISL1208_USR_SECTION_LEN 2
/* event section */
#define ISL1219_REG_SCT 0x14
#define ISL1219_REG_MNT 0x15
#define ISL1219_REG_HRT 0x16
#define ISL1219_REG_DTT 0x17
#define ISL1219_REG_MOT 0x18
#define ISL1219_REG_YRT 0x19
#define ISL1219_EVT_SECTION_LEN 6
static struct i2c_driver isl1208_driver;
/* Chip capabilities table */
struct isl1208_config {
unsigned int nvmem_length;
unsigned has_tamper:1;
unsigned has_timestamp:1;
unsigned has_inverted_osc_bit:1;
};
static const struct isl1208_config config_isl1208 = {
.nvmem_length = 2,
.has_tamper = false,
.has_timestamp = false
};
static const struct isl1208_config config_isl1209 = {
.nvmem_length = 2,
.has_tamper = true,
.has_timestamp = false
};
static const struct isl1208_config config_isl1218 = {
.nvmem_length = 8,
.has_tamper = false,
.has_timestamp = false
};
static const struct isl1208_config config_isl1219 = {
.nvmem_length = 2,
.has_tamper = true,
.has_timestamp = true
};
static const struct isl1208_config config_raa215300_a0 = {
.nvmem_length = 2,
.has_tamper = false,
.has_timestamp = false,
.has_inverted_osc_bit = true
};
static const struct i2c_device_id isl1208_id[] = {
{ "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 },
{ "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 },
{ "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 },
{ "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 },
{ "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, isl1208_id);
static const __maybe_unused struct of_device_id isl1208_of_match[] = {
{ .compatible = "isil,isl1208", .data = &config_isl1208 },
{ .compatible = "isil,isl1209", .data = &config_isl1209 },
{ .compatible = "isil,isl1218", .data = &config_isl1218 },
{ .compatible = "isil,isl1219", .data = &config_isl1219 },
{ }
};
MODULE_DEVICE_TABLE(of, isl1208_of_match);
/* Device state */
struct isl1208_state {
struct nvmem_config nvmem_config;
struct rtc_device *rtc;
const struct isl1208_config *config;
};
/* block read */
static int
isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
unsigned len)
{
int ret;
WARN_ON(reg > ISL1219_REG_YRT);
WARN_ON(reg + len > ISL1219_REG_YRT + 1);
ret = i2c_smbus_read_i2c_block_data(client, reg, len, buf);
return (ret < 0) ? ret : 0;
}
/* block write */
static int
isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
unsigned len)
{
int ret;
WARN_ON(reg > ISL1219_REG_YRT);
WARN_ON(reg + len > ISL1219_REG_YRT + 1);
ret = i2c_smbus_write_i2c_block_data(client, reg, len, buf);
return (ret < 0) ? ret