1001 lines · 24.9 KB · C
RawDownload
// 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[] = {	{ .name = "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 },	{ .name = "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 },	{ .name = "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 },	{ .name = "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 },	{ .name = "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 intisl1208_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 intisl1208_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 : 0;} /* simple check to see whether we have a isl1208 */static intisl1208_i2c_validate_client(struct i2c_client *client){	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };	u8 zero_mask[ISL1208_RTC_SECTION_LEN] = {		0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8	};	int i;	int ret; 	ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);	if (ret < 0)		return ret; 	for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) {		if (regs[i] & zero_mask[i])	/* check if bits are cleared */			return -ENODEV;	} 	return 0;} static int isl1208_set_xtoscb(struct i2c_client *client, int sr, int xtosb_val){	/* Do nothing if bit is already set to desired value */	if (!!(sr & ISL1208_REG_SR_XTOSCB) == xtosb_val)		return 0; 	if (xtosb_val)		sr |= ISL1208_REG_SR_XTOSCB;	else		sr &= ~ISL1208_REG_SR_XTOSCB; 	return i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);} static intisl1208_i2c_get_sr(struct i2c_client *client){	return i2c_smbus_read_byte_data(client, ISL1208_REG_SR);} static intisl1208_i2c_get_atr(struct i2c_client *client){	int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR);	if (atr < 0)		return atr; 	/* The 6bit value in the ATR register controls the load	 * capacitance C_load * in steps of 0.25pF	 *	 * bit (1<<5) of the ATR register is inverted	 *	 * C_load(ATR=0x20) =  4.50pF	 * C_load(ATR=0x00) = 12.50pF	 * C_load(ATR=0x1f) = 20.25pF	 *	 */ 	atr &= 0x3f;		/* mask out lsb */	atr ^= 1 << 5;		/* invert 6th bit */	atr += 2 * 9;		/* add offset of 4.5pF; unit[atr] = 0.25pF */ 	return atr;} /* returns adjustment value + 100 */static intisl1208_i2c_get_dtr(struct i2c_client *client){	int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR);	if (dtr < 0)		return -EIO; 	/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */	dtr = ((dtr & 0x3) * 20) * (dtr & (1 << 2) ? -1 : 1); 	return dtr + 100;} static intisl1208_i2c_get_usr(struct i2c_client *client){	u8 buf[ISL1208_USR_SECTION_LEN] = { 0, };	int ret; 	ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1, buf,				    ISL1208_USR_SECTION_LEN);	if (ret < 0)		return ret; 	return (buf[1] << 8) | buf[0];} static intisl1208_i2c_set_usr(struct i2c_client *client, u16 usr){	u8 buf[ISL1208_USR_SECTION_LEN]; 	buf[0] = usr & 0xff;	buf[1] = (usr >> 8) & 0xff; 	return isl1208_i2c_set_regs(client, ISL1208_REG_USR1, buf,				    ISL1208_USR_SECTION_LEN);} static intisl1208_rtc_toggle_alarm(struct i2c_client *client, int enable){	int icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT); 	if (icr < 0) {		dev_err(&client->dev, "%s: reading INT failed\n", __func__);		return icr;	} 	if (enable)		icr |= ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM;	else		icr &= ~(ISL1208_REG_INT_ALME | ISL1208_REG_INT_IM); 	icr = i2c_smbus_write_byte_data(client, ISL1208_REG_INT, icr);	if (icr < 0) {		dev_err(&client->dev, "%s: writing INT failed\n", __func__);		return icr;	} 	return 0;} static intisl1208_rtc_proc(struct device *dev, struct seq_file *seq){	struct i2c_client *const client = to_i2c_client(dev);	int sr, dtr, atr, usr; 	sr = isl1208_i2c_get_sr(client);	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return sr;	} 	seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n",		   (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "",		   (sr & ISL1208_REG_SR_BAT) ? " BAT" : "",		   (sr & ISL1208_REG_SR_ALM) ? " ALM" : "",		   (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "",		   (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "",		   (sr & ISL1208_REG_SR_ARST) ? " ARST" : "", sr); 	seq_printf(seq, "batt_status\t: %s\n",		   (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay"); 	dtr = isl1208_i2c_get_dtr(client);	if (dtr >= 0)		seq_printf(seq, "digital_trim\t: %d ppm\n", dtr - 100); 	atr = isl1208_i2c_get_atr(client);	if (atr >= 0)		seq_printf(seq, "analog_trim\t: %d.%.2d pF\n",			   atr >> 2, (atr & 0x3) * 25); 	usr = isl1208_i2c_get_usr(client);	if (usr >= 0)		seq_printf(seq, "user_data\t: 0x%.4x\n", usr); 	return 0;} static intisl1208_i2c_read_time(struct i2c_client *client, struct rtc_time *tm){	int sr;	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; 	sr = isl1208_i2c_get_sr(client);	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return -EIO;	} 	sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);	if (sr < 0) {		dev_err(&client->dev, "%s: reading RTC section failed\n",			__func__);		return sr;	} 	tm->tm_sec = bcd2bin(regs[ISL1208_REG_SC]);	tm->tm_min = bcd2bin(regs[ISL1208_REG_MN]); 	/* HR field has a more complex interpretation */	{		const u8 _hr = regs[ISL1208_REG_HR];		if (_hr & ISL1208_REG_HR_MIL)	/* 24h format */			tm->tm_hour = bcd2bin(_hr & 0x3f);		else {			/* 12h format */			tm->tm_hour = bcd2bin(_hr & 0x1f);			if (_hr & ISL1208_REG_HR_PM)	/* PM flag set */				tm->tm_hour += 12;		}	} 	tm->tm_mday = bcd2bin(regs[ISL1208_REG_DT]);	tm->tm_mon = bcd2bin(regs[ISL1208_REG_MO]) - 1;	/* rtc starts at 1 */	tm->tm_year = bcd2bin(regs[ISL1208_REG_YR]) + 100;	tm->tm_wday = bcd2bin(regs[ISL1208_REG_DW]); 	return 0;} static intisl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm){	struct rtc_time *const tm = &alarm->time;	u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };	int icr, yr, sr = isl1208_i2c_get_sr(client); 	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return sr;	} 	sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs,				   ISL1208_ALARM_SECTION_LEN);	if (sr < 0) {		dev_err(&client->dev, "%s: reading alarm section failed\n",			__func__);		return sr;	} 	/* MSB of each alarm register is an enable bit */	tm->tm_sec = bcd2bin(regs[ISL1208_REG_SCA - ISL1208_REG_SCA] & 0x7f);	tm->tm_min = bcd2bin(regs[ISL1208_REG_MNA - ISL1208_REG_SCA] & 0x7f);	tm->tm_hour = bcd2bin(regs[ISL1208_REG_HRA - ISL1208_REG_SCA] & 0x3f);	tm->tm_mday = bcd2bin(regs[ISL1208_REG_DTA - ISL1208_REG_SCA] & 0x3f);	tm->tm_mon =		bcd2bin(regs[ISL1208_REG_MOA - ISL1208_REG_SCA] & 0x1f) - 1;	tm->tm_wday = bcd2bin(regs[ISL1208_REG_DWA - ISL1208_REG_SCA] & 0x03); 	/* The alarm doesn't store the year so get it from the rtc section */	yr = i2c_smbus_read_byte_data(client, ISL1208_REG_YR);	if (yr < 0) {		dev_err(&client->dev, "%s: reading RTC YR failed\n", __func__);		return yr;	}	tm->tm_year = bcd2bin(yr) + 100; 	icr = i2c_smbus_read_byte_data(client, ISL1208_REG_INT);	if (icr < 0) {		dev_err(&client->dev, "%s: reading INT failed\n", __func__);		return icr;	}	alarm->enabled = !!(icr & ISL1208_REG_INT_ALME); 	return 0;} static intisl1208_i2c_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm){	struct rtc_time *alarm_tm = &alarm->time;	u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };	const int offs = ISL1208_REG_SCA;	struct rtc_time rtc_tm;	int err, enable; 	err = isl1208_i2c_read_time(client, &rtc_tm);	if (err)		return err; 	/* If the alarm time is before the current time disable the alarm */	if (!alarm->enabled || rtc_tm_sub(alarm_tm, &rtc_tm) <= 0)		enable = 0x00;	else		enable = 0x80; 	/* Program the alarm and enable it for each setting */	regs[ISL1208_REG_SCA - offs] = bin2bcd(alarm_tm->tm_sec) | enable;	regs[ISL1208_REG_MNA - offs] = bin2bcd(alarm_tm->tm_min) | enable;	regs[ISL1208_REG_HRA - offs] = bin2bcd(alarm_tm->tm_hour) |		ISL1208_REG_HR_MIL | enable; 	regs[ISL1208_REG_DTA - offs] = bin2bcd(alarm_tm->tm_mday) | enable;	regs[ISL1208_REG_MOA - offs] = bin2bcd(alarm_tm->tm_mon + 1) | enable;	regs[ISL1208_REG_DWA - offs] = bin2bcd(alarm_tm->tm_wday & 7) | enable; 	/* write ALARM registers */	err = isl1208_i2c_set_regs(client, offs, regs,				  ISL1208_ALARM_SECTION_LEN);	if (err < 0) {		dev_err(&client->dev, "%s: writing ALARM section failed\n",			__func__);		return err;	} 	err = isl1208_rtc_toggle_alarm(client, enable);	if (err)		return err; 	return 0;} static intisl1208_rtc_read_time(struct device *dev, struct rtc_time *tm){	return isl1208_i2c_read_time(to_i2c_client(dev), tm);} static intisl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm){	int sr;	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, }; 	/* The clock has an 8 bit wide bcd-coded register (they never learn)	 * for the year. tm_year is an offset from 1900 and we are interested	 * in the 2000-2099 range, so any value less than 100 is invalid.	 */	if (tm->tm_year < 100)		return -EINVAL; 	regs[ISL1208_REG_SC] = bin2bcd(tm->tm_sec);	regs[ISL1208_REG_MN] = bin2bcd(tm->tm_min);	regs[ISL1208_REG_HR] = bin2bcd(tm->tm_hour) | ISL1208_REG_HR_MIL; 	regs[ISL1208_REG_DT] = bin2bcd(tm->tm_mday);	regs[ISL1208_REG_MO] = bin2bcd(tm->tm_mon + 1);	regs[ISL1208_REG_YR] = bin2bcd(tm->tm_year - 100); 	regs[ISL1208_REG_DW] = bin2bcd(tm->tm_wday & 7); 

Lines 1–500 of 1001.