diff options
Diffstat (limited to 'drivers/rtc/rtc-s35390a.c')
| -rw-r--r-- | drivers/rtc/rtc-s35390a.c | 156 | 
1 files changed, 134 insertions, 22 deletions
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index f789e002c9b..f40afdd0e5f 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -19,6 +19,8 @@  #define S35390A_CMD_STATUS1	0  #define S35390A_CMD_STATUS2	1  #define S35390A_CMD_TIME1	2 +#define S35390A_CMD_TIME2	3 +#define S35390A_CMD_INT2_REG1	5  #define S35390A_BYTE_YEAR	0  #define S35390A_BYTE_MONTH	1 @@ -28,12 +30,23 @@  #define S35390A_BYTE_MINS	5  #define S35390A_BYTE_SECS	6 +#define S35390A_ALRM_BYTE_WDAY	0 +#define S35390A_ALRM_BYTE_HOURS	1 +#define S35390A_ALRM_BYTE_MINS	2 +  #define S35390A_FLAG_POC	0x01  #define S35390A_FLAG_BLD	0x02  #define S35390A_FLAG_24H	0x40  #define S35390A_FLAG_RESET	0x80  #define S35390A_FLAG_TEST	0x01 +#define S35390A_INT2_MODE_MASK		0xF0 + +#define S35390A_INT2_MODE_NOINTR	0x00 +#define S35390A_INT2_MODE_FREQ		0x10 +#define S35390A_INT2_MODE_ALARM		0x40 +#define S35390A_INT2_MODE_PMIN_EDG	0x20 +  static const struct i2c_device_id s35390a_id[] = {  	{ "s35390a", 0 },  	{ } @@ -50,7 +63,11 @@ static int s35390a_set_reg(struct s35390a *s35390a, int reg, char *buf, int len)  {  	struct i2c_client *client = s35390a->client[reg];  	struct i2c_msg msg[] = { -		{ client->addr, 0, len, buf }, +		{ +			.addr = client->addr, +			.len = len, +			.buf = buf +		},  	};  	if ((i2c_transfer(client->adapter, msg, 1)) != 1) @@ -63,7 +80,12 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)  {  	struct i2c_client *client = s35390a->client[reg];  	struct i2c_msg msg[] = { -		{ client->addr, I2C_M_RD, len, buf }, +		{ +			.addr = client->addr, +			.flags = I2C_M_RD, +			.len = len, +			.buf = buf +		},  	};  	if ((i2c_transfer(client->adapter, msg, 1)) != 1) @@ -184,6 +206,104 @@ static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)  	return rtc_valid_tm(tm);  } +static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm) +{ +	struct s35390a *s35390a = i2c_get_clientdata(client); +	char buf[3], sts = 0; +	int err, i; + +	dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\ +		"mon=%d, year=%d, wday=%d\n", __func__, alm->time.tm_sec, +		alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday, +		alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday); + +	/* disable interrupt */ +	err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); +	if (err < 0) +		return err; + +	/* clear pending interrupt, if any */ +	err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts)); +	if (err < 0) +		return err; + +	if (alm->enabled) +		sts = S35390A_INT2_MODE_ALARM; +	else +		sts = S35390A_INT2_MODE_NOINTR; + +	/* This chip expects the bits of each byte to be in reverse order */ +	sts = bitrev8(sts); + +	/* set interupt mode*/ +	err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); +	if (err < 0) +		return err; + +	if (alm->time.tm_wday != -1) +		buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80; + +	buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a, +			alm->time.tm_hour) | 0x80; +	buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80; + +	if (alm->time.tm_hour >= 12) +		buf[S35390A_ALRM_BYTE_HOURS] |= 0x40; + +	for (i = 0; i < 3; ++i) +		buf[i] = bitrev8(buf[i]); + +	err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf, +								sizeof(buf)); + +	return err; +} + +static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm) +{ +	struct s35390a *s35390a = i2c_get_clientdata(client); +	char buf[3], sts; +	int i, err; + +	err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts)); +	if (err < 0) +		return err; + +	if (bitrev8(sts) != S35390A_INT2_MODE_ALARM) +		return -EINVAL; + +	err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf)); +	if (err < 0) +		return err; + +	/* This chip returns the bits of each byte in reverse order */ +	for (i = 0; i < 3; ++i) { +		buf[i] = bitrev8(buf[i]); +		buf[i] &= ~0x80; +	} + +	alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]); +	alm->time.tm_hour = s35390a_reg2hr(s35390a, +						buf[S35390A_ALRM_BYTE_HOURS]); +	alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]); + +	dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n", +			__func__, alm->time.tm_min, alm->time.tm_hour, +			alm->time.tm_wday); + +	return 0; +} + +static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ +	return s35390a_read_alarm(to_i2c_client(dev), alm); +} + +static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) +{ +	return s35390a_set_alarm(to_i2c_client(dev), alm); +} +  static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)  {  	return s35390a_get_datetime(to_i2c_client(dev), tm); @@ -197,6 +317,9 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)  static const struct rtc_class_ops s35390a_rtc_ops = {  	.read_time	= s35390a_rtc_read_time,  	.set_time	= s35390a_rtc_set_time, +	.set_alarm	= s35390a_rtc_set_alarm, +	.read_alarm	= s35390a_rtc_read_alarm, +  };  static struct i2c_driver s35390a_driver; @@ -215,7 +338,8 @@ static int s35390a_probe(struct i2c_client *client,  		goto exit;  	} -	s35390a = kzalloc(sizeof(struct s35390a), GFP_KERNEL); +	s35390a = devm_kzalloc(&client->dev, sizeof(struct s35390a), +				GFP_KERNEL);  	if (!s35390a) {  		err = -ENOMEM;  		goto exit; @@ -261,8 +385,11 @@ static int s35390a_probe(struct i2c_client *client,  	if (s35390a_get_datetime(client, &tm) < 0)  		dev_warn(&client->dev, "clock needs to be set\n"); -	s35390a->rtc = rtc_device_register(s35390a_driver.driver.name, -				&client->dev, &s35390a_rtc_ops, THIS_MODULE); +	device_set_wakeup_capable(&client->dev, 1); + +	s35390a->rtc = devm_rtc_device_register(&client->dev, +					s35390a_driver.driver.name, +					&s35390a_rtc_ops, THIS_MODULE);  	if (IS_ERR(s35390a->rtc)) {  		err = PTR_ERR(s35390a->rtc); @@ -274,7 +401,6 @@ exit_dummy:  	for (i = 1; i < 8; ++i)  		if (s35390a->client[i])  			i2c_unregister_device(s35390a->client[i]); -	kfree(s35390a);  exit:  	return err; @@ -283,15 +409,12 @@ exit:  static int s35390a_remove(struct i2c_client *client)  {  	unsigned int i; -  	struct s35390a *s35390a = i2c_get_clientdata(client); +  	for (i = 1; i < 8; ++i)  		if (s35390a->client[i])  			i2c_unregister_device(s35390a->client[i]); -	rtc_device_unregister(s35390a->rtc); -	kfree(s35390a); -  	return 0;  } @@ -304,19 +427,8 @@ static struct i2c_driver s35390a_driver = {  	.id_table	= s35390a_id,  }; -static int __init s35390a_rtc_init(void) -{ -	return i2c_add_driver(&s35390a_driver); -} - -static void __exit s35390a_rtc_exit(void) -{ -	i2c_del_driver(&s35390a_driver); -} +module_i2c_driver(s35390a_driver);  MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>");  MODULE_DESCRIPTION("S35390A RTC driver");  MODULE_LICENSE("GPL"); - -module_init(s35390a_rtc_init); -module_exit(s35390a_rtc_exit);  | 
