|
|
|
@ -39,6 +39,8 @@ |
|
|
|
|
#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 ISL1208_REG_09 0x09 /* reserved */ |
|
|
|
|
#define ISL1208_REG_ATR 0x0a |
|
|
|
|
#define ISL1208_REG_DTR 0x0b |
|
|
|
@ -201,6 +203,30 @@ isl1208_i2c_set_usr(struct i2c_client *client, u16 usr) |
|
|
|
|
ISL1208_USR_SECTION_LEN); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
isl1208_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 int |
|
|
|
|
isl1208_rtc_proc(struct device *dev, struct seq_file *seq) |
|
|
|
|
{ |
|
|
|
@ -288,9 +314,8 @@ isl1208_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 sr; |
|
|
|
|
int icr, yr, sr = isl1208_i2c_get_sr(client); |
|
|
|
|
|
|
|
|
|
sr = isl1208_i2c_get_sr(client); |
|
|
|
|
if (sr < 0) { |
|
|
|
|
dev_err(&client->dev, "%s: reading SR failed\n", __func__); |
|
|
|
|
return sr; |
|
|
|
@ -313,6 +338,73 @@ isl1208_i2c_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alarm) |
|
|
|
|
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 int |
|
|
|
|
isl1208_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; |
|
|
|
|
unsigned long rtc_secs, alarm_secs; |
|
|
|
|
struct rtc_time rtc_tm; |
|
|
|
|
int err, enable; |
|
|
|
|
|
|
|
|
|
err = isl1208_i2c_read_time(client, &rtc_tm); |
|
|
|
|
if (err) |
|
|
|
|
return err; |
|
|
|
|
err = rtc_tm_to_time(&rtc_tm, &rtc_secs); |
|
|
|
|
if (err) |
|
|
|
|
return err; |
|
|
|
|
err = rtc_tm_to_time(alarm_tm, &alarm_secs); |
|
|
|
|
if (err) |
|
|
|
|
return err; |
|
|
|
|
|
|
|
|
|
/* If the alarm time is before the current time disable the alarm */ |
|
|
|
|
if (!alarm->enabled || alarm_secs <= rtc_secs) |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -391,12 +483,63 @@ isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
|
|
|
|
return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int |
|
|
|
|
isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
|
|
|
|
{ |
|
|
|
|
return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static irqreturn_t |
|
|
|
|
isl1208_rtc_interrupt(int irq, void *data) |
|
|
|
|
{ |
|
|
|
|
unsigned long timeout = jiffies + msecs_to_jiffies(1000); |
|
|
|
|
struct i2c_client *client = data; |
|
|
|
|
int handled = 0, sr, err; |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* I2C reads get NAK'ed if we read straight away after an interrupt? |
|
|
|
|
* Using a mdelay/msleep didn't seem to help either, so we work around |
|
|
|
|
* this by continually trying to read the register for a short time. |
|
|
|
|
*/ |
|
|
|
|
while (1) { |
|
|
|
|
sr = isl1208_i2c_get_sr(client); |
|
|
|
|
if (sr >= 0) |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
if (time_after(jiffies, timeout)) { |
|
|
|
|
dev_err(&client->dev, "%s: reading SR failed\n", |
|
|
|
|
__func__); |
|
|
|
|
return sr; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (sr & ISL1208_REG_SR_ALM) { |
|
|
|
|
dev_dbg(&client->dev, "alarm!\n"); |
|
|
|
|
|
|
|
|
|
/* Clear the alarm */ |
|
|
|
|
sr &= ~ISL1208_REG_SR_ALM; |
|
|
|
|
sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); |
|
|
|
|
if (sr < 0) |
|
|
|
|
dev_err(&client->dev, "%s: writing SR failed\n", |
|
|
|
|
__func__); |
|
|
|
|
else |
|
|
|
|
handled = 1; |
|
|
|
|
|
|
|
|
|
/* Disable the alarm */ |
|
|
|
|
err = isl1208_rtc_toggle_alarm(client, 0); |
|
|
|
|
if (err) |
|
|
|
|
return err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return handled ? IRQ_HANDLED : IRQ_NONE; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const struct rtc_class_ops isl1208_rtc_ops = { |
|
|
|
|
.proc = isl1208_rtc_proc, |
|
|
|
|
.read_time = isl1208_rtc_read_time, |
|
|
|
|
.set_time = isl1208_rtc_set_time, |
|
|
|
|
.read_alarm = isl1208_rtc_read_alarm, |
|
|
|
|
/*.set_alarm = isl1208_rtc_set_alarm, */ |
|
|
|
|
.set_alarm = isl1208_rtc_set_alarm, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/* sysfs interface */ |
|
|
|
@ -488,11 +631,29 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) |
|
|
|
|
dev_info(&client->dev, |
|
|
|
|
"chip found, driver version " DRV_VERSION "\n"); |
|
|
|
|
|
|
|
|
|
if (client->irq > 0) { |
|
|
|
|
rc = request_threaded_irq(client->irq, NULL, |
|
|
|
|
isl1208_rtc_interrupt, |
|
|
|
|
IRQF_SHARED, |
|
|
|
|
isl1208_driver.driver.name, client); |
|
|
|
|
if (!rc) { |
|
|
|
|
device_init_wakeup(&client->dev, 1); |
|
|
|
|
enable_irq_wake(client->irq); |
|
|
|
|
} else { |
|
|
|
|
dev_err(&client->dev, |
|
|
|
|
"Unable to request irq %d, no alarm support\n", |
|
|
|
|
client->irq); |
|
|
|
|
client->irq = 0; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
rtc = rtc_device_register(isl1208_driver.driver.name, |
|
|
|
|
&client->dev, &isl1208_rtc_ops, |
|
|
|
|
THIS_MODULE); |
|
|
|
|
if (IS_ERR(rtc)) |
|
|
|
|
return PTR_ERR(rtc); |
|
|
|
|
if (IS_ERR(rtc)) { |
|
|
|
|
rc = PTR_ERR(rtc); |
|
|
|
|
goto exit_free_irq; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
i2c_set_clientdata(client, rtc); |
|
|
|
|
|
|
|
|
@ -514,6 +675,9 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) |
|
|
|
|
|
|
|
|
|
exit_unregister: |
|
|
|
|
rtc_device_unregister(rtc); |
|
|
|
|
exit_free_irq: |
|
|
|
|
if (client->irq) |
|
|
|
|
free_irq(client->irq, client); |
|
|
|
|
|
|
|
|
|
return rc; |
|
|
|
|
} |
|
|
|
@ -525,6 +689,8 @@ isl1208_remove(struct i2c_client *client) |
|
|
|
|
|
|
|
|
|
sysfs_remove_group(&client->dev.kobj, &isl1208_rtc_sysfs_files); |
|
|
|
|
rtc_device_unregister(rtc); |
|
|
|
|
if (client->irq) |
|
|
|
|
free_irq(client->irq, client); |
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|