rtc: pcf2123: add alarm support
Allows alarm to be controlled using, e.g., the RTC_WKALM_SET ioctl. Signed-off-by: Dylan Howey <Dylan.Howey@tennantco.com> Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
parent
c33850bbc6
commit
e32e60a2d5
|
@ -242,6 +242,99 @@ static int pcf2123_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int pcf2123_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct pcf2123_plat_data *pdata = dev_get_platdata(dev);
|
||||
u8 rxbuf[4];
|
||||
int ret;
|
||||
unsigned int val = 0;
|
||||
|
||||
ret = regmap_bulk_read(pdata->map, PCF2123_REG_ALRM_MN, rxbuf,
|
||||
sizeof(rxbuf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alm->time.tm_min = bcd2bin(rxbuf[0] & 0x7F);
|
||||
alm->time.tm_hour = bcd2bin(rxbuf[1] & 0x3F);
|
||||
alm->time.tm_mday = bcd2bin(rxbuf[2] & 0x3F);
|
||||
alm->time.tm_wday = bcd2bin(rxbuf[3] & 0x07);
|
||||
|
||||
dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time);
|
||||
|
||||
ret = regmap_read(pdata->map, PCF2123_REG_CTRL2, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alm->enabled = !!(val & CTRL2_AIE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf2123_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct pcf2123_plat_data *pdata = dev_get_platdata(dev);
|
||||
u8 txbuf[4];
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s: alm is %ptR\n", __func__, &alm->time);
|
||||
|
||||
/* Ensure alarm flag is clear */
|
||||
ret = regmap_update_bits(pdata->map, PCF2123_REG_CTRL2, CTRL2_AF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Disable alarm interrupt */
|
||||
ret = regmap_update_bits(pdata->map, PCF2123_REG_CTRL2, CTRL2_AIE, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set new alarm */
|
||||
txbuf[0] = bin2bcd(alm->time.tm_min & 0x7F);
|
||||
txbuf[1] = bin2bcd(alm->time.tm_hour & 0x3F);
|
||||
txbuf[2] = bin2bcd(alm->time.tm_mday & 0x3F);
|
||||
txbuf[3] = bin2bcd(alm->time.tm_wday & 0x07);
|
||||
|
||||
ret = regmap_bulk_write(pdata->map, PCF2123_REG_ALRM_MN, txbuf,
|
||||
sizeof(txbuf));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable alarm interrupt */
|
||||
if (alm->enabled) {
|
||||
ret = regmap_update_bits(pdata->map, PCF2123_REG_CTRL2,
|
||||
CTRL2_AIE, CTRL2_AIE);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t pcf2123_rtc_irq(int irq, void *dev)
|
||||
{
|
||||
struct pcf2123_plat_data *pdata = dev_get_platdata(dev);
|
||||
struct mutex *lock = &pdata->rtc->ops_lock;
|
||||
unsigned int val = 0;
|
||||
int ret = IRQ_NONE;
|
||||
|
||||
mutex_lock(lock);
|
||||
regmap_read(pdata->map, PCF2123_REG_CTRL2, &val);
|
||||
|
||||
/* Alarm? */
|
||||
if (val & CTRL2_AF) {
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
/* Clear alarm flag */
|
||||
regmap_update_bits(pdata->map, PCF2123_REG_CTRL2, CTRL2_AF, 0);
|
||||
|
||||
rtc_update_irq(pdata->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
}
|
||||
|
||||
mutex_unlock(lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pcf2123_reset(struct device *dev)
|
||||
{
|
||||
struct pcf2123_plat_data *pdata = dev_get_platdata(dev);
|
||||
|
@ -281,7 +374,8 @@ static const struct rtc_class_ops pcf2123_rtc_ops = {
|
|||
.set_time = pcf2123_rtc_set_time,
|
||||
.read_offset = pcf2123_read_offset,
|
||||
.set_offset = pcf2123_set_offset,
|
||||
|
||||
.read_alarm = pcf2123_rtc_read_alarm,
|
||||
.set_alarm = pcf2123_rtc_set_alarm,
|
||||
};
|
||||
|
||||
static int pcf2123_probe(struct spi_device *spi)
|
||||
|
@ -289,7 +383,7 @@ static int pcf2123_probe(struct spi_device *spi)
|
|||
struct rtc_device *rtc;
|
||||
struct rtc_time tm;
|
||||
struct pcf2123_plat_data *pdata;
|
||||
int ret;
|
||||
int ret = 0;
|
||||
|
||||
pdata = devm_kzalloc(&spi->dev, sizeof(struct pcf2123_plat_data),
|
||||
GFP_KERNEL);
|
||||
|
@ -328,6 +422,24 @@ static int pcf2123_probe(struct spi_device *spi)
|
|||
|
||||
pdata->rtc = rtc;
|
||||
|
||||
/* Register alarm irq */
|
||||
if (spi->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
|
||||
pcf2123_rtc_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
pcf2123_driver.driver.name, &spi->dev);
|
||||
if (!ret)
|
||||
device_init_wakeup(&spi->dev, true);
|
||||
else
|
||||
dev_err(&spi->dev, "could not request irq.\n");
|
||||
}
|
||||
|
||||
/* The PCF2123's alarm only has minute accuracy. Must add timer
|
||||
* support to this driver to generate interrupts more than once
|
||||
* per minute.
|
||||
*/
|
||||
pdata->rtc->uie_unsupported = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
kfree_exit:
|
||||
|
|
Loading…
Reference in New Issue