diff options
Diffstat (limited to 'drivers/rtc/rtc-pcf85363.c')
-rw-r--r-- | drivers/rtc/rtc-pcf85363.c | 205 |
1 files changed, 191 insertions, 14 deletions
diff --git a/drivers/rtc/rtc-pcf85363.c b/drivers/rtc/rtc-pcf85363.c index ea04e9f0930b..c04a1edcd571 100644 --- a/drivers/rtc/rtc-pcf85363.c +++ b/drivers/rtc/rtc-pcf85363.c @@ -73,6 +73,43 @@ #define CTRL_RESETS 0x2f #define CTRL_RAM 0x40 +#define ALRM_SEC_A1E BIT(0) +#define ALRM_MIN_A1E BIT(1) +#define ALRM_HR_A1E BIT(2) +#define ALRM_DAY_A1E BIT(3) +#define ALRM_MON_A1E BIT(4) +#define ALRM_MIN_A2E BIT(5) +#define ALRM_HR_A2E BIT(6) +#define ALRM_DAY_A2E BIT(7) + +#define INT_WDIE BIT(0) +#define INT_BSIE BIT(1) +#define INT_TSRIE BIT(2) +#define INT_A2IE BIT(3) +#define INT_A1IE BIT(4) +#define INT_OIE BIT(5) +#define INT_PIE BIT(6) +#define INT_ILP BIT(7) + +#define FLAGS_TSR1F BIT(0) +#define FLAGS_TSR2F BIT(1) +#define FLAGS_TSR3F BIT(2) +#define FLAGS_BSF BIT(3) +#define FLAGS_WDF BIT(4) +#define FLAGS_A1F BIT(5) +#define FLAGS_A2F BIT(6) +#define FLAGS_PIF BIT(7) + +#define PIN_IO_INTAPM GENMASK(1, 0) +#define PIN_IO_INTA_CLK 0 +#define PIN_IO_INTA_BAT 1 +#define PIN_IO_INTA_OUT 2 +#define PIN_IO_INTA_HIZ 3 + +#define STOP_EN_STOP BIT(0) + +#define RESET_CPR 0xa4 + #define NVRAM_SIZE 0x40 static struct i2c_driver pcf85363_driver; @@ -80,7 +117,6 @@ static struct i2c_driver pcf85363_driver; struct pcf85363 { struct device *dev; struct rtc_device *rtc; - struct nvmem_config nvmem_cfg; struct regmap *regmap; }; @@ -116,8 +152,12 @@ static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm) static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) { struct pcf85363 *pcf85363 = dev_get_drvdata(dev); - unsigned char buf[DT_YEARS + 1]; - int len = sizeof(buf); + unsigned char tmp[11]; + unsigned char *buf = &tmp[2]; + int ret; + + tmp[0] = STOP_EN_STOP; + tmp[1] = RESET_CPR; buf[DT_100THS] = 0; buf[DT_SECS] = bin2bcd(tm->tm_sec); @@ -128,8 +168,116 @@ static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm) buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1); buf[DT_YEARS] = bin2bcd(tm->tm_year % 100); - return regmap_bulk_write(pcf85363->regmap, DT_100THS, - buf, len); + ret = regmap_bulk_write(pcf85363->regmap, CTRL_STOP_EN, + tmp, sizeof(tmp)); + if (ret) + return ret; + + return regmap_write(pcf85363->regmap, CTRL_STOP_EN, 0); +} + +static int pcf85363_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1]; + unsigned int val; + int ret; + + ret = regmap_bulk_read(pcf85363->regmap, DT_SECOND_ALM1, buf, + sizeof(buf)); + if (ret) + return ret; + + alrm->time.tm_sec = bcd2bin(buf[0]); + alrm->time.tm_min = bcd2bin(buf[1]); + alrm->time.tm_hour = bcd2bin(buf[2]); + alrm->time.tm_mday = bcd2bin(buf[3]); + alrm->time.tm_mon = bcd2bin(buf[4]) - 1; + + ret = regmap_read(pcf85363->regmap, CTRL_INTA_EN, &val); + if (ret) + return ret; + + alrm->enabled = !!(val & INT_A1IE); + + return 0; +} + +static int _pcf85363_rtc_alarm_irq_enable(struct pcf85363 *pcf85363, unsigned + int enabled) +{ + unsigned int alarm_flags = ALRM_SEC_A1E | ALRM_MIN_A1E | ALRM_HR_A1E | + ALRM_DAY_A1E | ALRM_MON_A1E; + int ret; + + ret = regmap_update_bits(pcf85363->regmap, DT_ALARM_EN, alarm_flags, + enabled ? alarm_flags : 0); + if (ret) + return ret; + + ret = regmap_update_bits(pcf85363->regmap, CTRL_INTA_EN, + INT_A1IE, enabled ? INT_A1IE : 0); + + if (ret || enabled) + return ret; + + /* clear current flags */ + return regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0); +} + +static int pcf85363_rtc_alarm_irq_enable(struct device *dev, + unsigned int enabled) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + + return _pcf85363_rtc_alarm_irq_enable(pcf85363, enabled); +} + +static int pcf85363_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct pcf85363 *pcf85363 = dev_get_drvdata(dev); + unsigned char buf[DT_MONTH_ALM1 - DT_SECOND_ALM1 + 1]; + int ret; + + buf[0] = bin2bcd(alrm->time.tm_sec); + buf[1] = bin2bcd(alrm->time.tm_min); + buf[2] = bin2bcd(alrm->time.tm_hour); + buf[3] = bin2bcd(alrm->time.tm_mday); + buf[4] = bin2bcd(alrm->time.tm_mon + 1); + + /* + * Disable the alarm interrupt before changing the value to avoid + * spurious interrupts + */ + ret = _pcf85363_rtc_alarm_irq_enable(pcf85363, 0); + if (ret) + return ret; + + ret = regmap_bulk_write(pcf85363->regmap, DT_SECOND_ALM1, buf, + sizeof(buf)); + if (ret) + return ret; + + return _pcf85363_rtc_alarm_irq_enable(pcf85363, alrm->enabled); +} + +static irqreturn_t pcf85363_rtc_handle_irq(int irq, void *dev_id) +{ + struct pcf85363 *pcf85363 = i2c_get_clientdata(dev_id); + unsigned int flags; + int err; + + err = regmap_read(pcf85363->regmap, CTRL_FLAGS, &flags); + if (err) + return IRQ_NONE; + + if (flags & FLAGS_A1F) { + rtc_update_irq(pcf85363->rtc, 1, RTC_IRQF | RTC_AF); + regmap_update_bits(pcf85363->regmap, CTRL_FLAGS, FLAGS_A1F, 0); + return IRQ_HANDLED; + } + + return IRQ_NONE; } static const struct rtc_class_ops rtc_ops = { @@ -137,6 +285,14 @@ static const struct rtc_class_ops rtc_ops = { .set_time = pcf85363_rtc_set_time, }; +static const struct rtc_class_ops rtc_ops_alarm = { + .read_time = pcf85363_rtc_read_time, + .set_time = pcf85363_rtc_set_time, + .read_alarm = pcf85363_rtc_read_alarm, + .set_alarm = pcf85363_rtc_set_alarm, + .alarm_irq_enable = pcf85363_rtc_alarm_irq_enable, +}; + static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes) { @@ -158,12 +314,22 @@ static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val, static const struct regmap_config regmap_config = { .reg_bits = 8, .val_bits = 8, + .max_register = 0x7f, }; static int pcf85363_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct pcf85363 *pcf85363; + struct nvmem_config nvmem_cfg = { + .name = "pcf85363-", + .word_size = 1, + .stride = 1, + .size = NVRAM_SIZE, + .reg_read = pcf85363_nvram_read, + .reg_write = pcf85363_nvram_write, + }; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -186,17 +352,28 @@ static int pcf85363_probe(struct i2c_client *client, if (IS_ERR(pcf85363->rtc)) return PTR_ERR(pcf85363->rtc); - pcf85363->nvmem_cfg.name = "pcf85363-"; - pcf85363->nvmem_cfg.word_size = 1; - pcf85363->nvmem_cfg.stride = 1; - pcf85363->nvmem_cfg.size = NVRAM_SIZE; - pcf85363->nvmem_cfg.reg_read = pcf85363_nvram_read; - pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write; - pcf85363->nvmem_cfg.priv = pcf85363; - pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg; pcf85363->rtc->ops = &rtc_ops; - return rtc_register_device(pcf85363->rtc); + if (client->irq > 0) { + regmap_write(pcf85363->regmap, CTRL_FLAGS, 0); + regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO, + PIN_IO_INTA_OUT, PIN_IO_INTAPM); + ret = devm_request_threaded_irq(pcf85363->dev, client->irq, + NULL, pcf85363_rtc_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "pcf85363", client); + if (ret) + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + else + pcf85363->rtc->ops = &rtc_ops_alarm; + } + + ret = rtc_register_device(pcf85363->rtc); + + nvmem_cfg.priv = pcf85363; + rtc_nvmem_register(pcf85363->rtc, &nvmem_cfg); + + return ret; } static const struct of_device_id dev_ids[] = { |