rtc: ds1343: rework interrupt handling
Rework the interrupt handling to avoid caching the values as the core is already doing that. The core also always ensures the rtc_time passed for the alarm is fully populated. The only trick is in read_alarm where status needs to be read before the alarm registers to ensure the potential irq is not cleared. Link: https://lore.kernel.org/r/20191019204941.6203-8-alexandre.belloni@bootlin.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
parent
a986429095
commit
0680a6cdab
|
@ -78,12 +78,7 @@ struct ds1343_priv {
|
||||||
struct spi_device *spi;
|
struct spi_device *spi;
|
||||||
struct rtc_device *rtc;
|
struct rtc_device *rtc;
|
||||||
struct regmap *map;
|
struct regmap *map;
|
||||||
unsigned int irqen;
|
|
||||||
int irq;
|
int irq;
|
||||||
int alarm_sec;
|
|
||||||
int alarm_min;
|
|
||||||
int alarm_hour;
|
|
||||||
int alarm_mday;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t ds1343_show_glitchfilter(struct device *dev,
|
static ssize_t ds1343_show_glitchfilter(struct device *dev,
|
||||||
|
@ -239,73 +234,35 @@ static int ds1343_set_time(struct device *dev, struct rtc_time *dt)
|
||||||
buf, sizeof(buf));
|
buf, sizeof(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds1343_update_alarm(struct device *dev)
|
|
||||||
{
|
|
||||||
struct ds1343_priv *priv = dev_get_drvdata(dev);
|
|
||||||
unsigned int control, stat;
|
|
||||||
unsigned char buf[4];
|
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
res = regmap_read(priv->map, DS1343_CONTROL_REG, &control);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
control &= ~(DS1343_A0IE);
|
|
||||||
stat &= ~(DS1343_IRQF0);
|
|
||||||
|
|
||||||
res = regmap_write(priv->map, DS1343_CONTROL_REG, control);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = regmap_write(priv->map, DS1343_STATUS_REG, stat);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
buf[0] = priv->alarm_sec < 0 || (priv->irqen & RTC_UF) ?
|
|
||||||
0x80 : bin2bcd(priv->alarm_sec) & 0x7F;
|
|
||||||
buf[1] = priv->alarm_min < 0 || (priv->irqen & RTC_UF) ?
|
|
||||||
0x80 : bin2bcd(priv->alarm_min) & 0x7F;
|
|
||||||
buf[2] = priv->alarm_hour < 0 || (priv->irqen & RTC_UF) ?
|
|
||||||
0x80 : bin2bcd(priv->alarm_hour) & 0x3F;
|
|
||||||
buf[3] = priv->alarm_mday < 0 || (priv->irqen & RTC_UF) ?
|
|
||||||
0x80 : bin2bcd(priv->alarm_mday) & 0x7F;
|
|
||||||
|
|
||||||
res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
if (priv->irqen) {
|
|
||||||
control |= DS1343_A0IE;
|
|
||||||
res = regmap_write(priv->map, DS1343_CONTROL_REG, control);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
{
|
{
|
||||||
struct ds1343_priv *priv = dev_get_drvdata(dev);
|
struct ds1343_priv *priv = dev_get_drvdata(dev);
|
||||||
|
unsigned char buf[4];
|
||||||
|
unsigned int val;
|
||||||
int res;
|
int res;
|
||||||
unsigned int stat;
|
|
||||||
|
|
||||||
if (priv->irq <= 0)
|
if (priv->irq <= 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
res = regmap_read(priv->map, DS1343_STATUS_REG, &stat);
|
res = regmap_read(priv->map, DS1343_STATUS_REG, &val);
|
||||||
if (res)
|
if (res)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
alarm->enabled = !!(priv->irqen & RTC_AF);
|
alarm->pending = !!(val & DS1343_IRQF0);
|
||||||
alarm->pending = !!(stat & DS1343_IRQF0);
|
|
||||||
|
|
||||||
alarm->time.tm_sec = priv->alarm_sec < 0 ? 0 : priv->alarm_sec;
|
res = regmap_read(priv->map, DS1343_CONTROL_REG, &val);
|
||||||
alarm->time.tm_min = priv->alarm_min < 0 ? 0 : priv->alarm_min;
|
if (res)
|
||||||
alarm->time.tm_hour = priv->alarm_hour < 0 ? 0 : priv->alarm_hour;
|
return res;
|
||||||
alarm->time.tm_mday = priv->alarm_mday < 0 ? 0 : priv->alarm_mday;
|
alarm->enabled = !!(val & DS1343_A0IE);
|
||||||
|
|
||||||
|
res = regmap_bulk_read(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
alarm->time.tm_sec = bcd2bin(buf[0]) & 0x7f;
|
||||||
|
alarm->time.tm_min = bcd2bin(buf[1]) & 0x7f;
|
||||||
|
alarm->time.tm_hour = bcd2bin(buf[2]) & 0x3f;
|
||||||
|
alarm->time.tm_mday = bcd2bin(buf[3]) & 0x3f;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -313,19 +270,30 @@ static int ds1343_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
static int ds1343_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||||
{
|
{
|
||||||
struct ds1343_priv *priv = dev_get_drvdata(dev);
|
struct ds1343_priv *priv = dev_get_drvdata(dev);
|
||||||
|
unsigned char buf[4];
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
if (priv->irq <= 0)
|
if (priv->irq <= 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
priv->alarm_sec = alarm->time.tm_sec;
|
res = regmap_update_bits(priv->map, DS1343_CONTROL_REG, DS1343_A0IE, 0);
|
||||||
priv->alarm_min = alarm->time.tm_min;
|
if (res)
|
||||||
priv->alarm_hour = alarm->time.tm_hour;
|
return res;
|
||||||
priv->alarm_mday = alarm->time.tm_mday;
|
|
||||||
|
buf[0] = bin2bcd(alarm->time.tm_sec);
|
||||||
|
buf[1] = bin2bcd(alarm->time.tm_min);
|
||||||
|
buf[2] = bin2bcd(alarm->time.tm_hour);
|
||||||
|
buf[3] = bin2bcd(alarm->time.tm_mday);
|
||||||
|
|
||||||
|
res = regmap_bulk_write(priv->map, DS1343_ALM0_SEC_REG, buf, 4);
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
if (alarm->enabled)
|
if (alarm->enabled)
|
||||||
priv->irqen |= RTC_AF;
|
res = regmap_update_bits(priv->map, DS1343_CONTROL_REG,
|
||||||
|
DS1343_A0IE, DS1343_A0IE);
|
||||||
|
|
||||||
return ds1343_update_alarm(dev);
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||||
|
@ -335,18 +303,14 @@ static int ds1343_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||||
if (priv->irq <= 0)
|
if (priv->irq <= 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (enabled)
|
return regmap_update_bits(priv->map, DS1343_CONTROL_REG,
|
||||||
priv->irqen |= RTC_AF;
|
DS1343_A0IE, enabled ? DS1343_A0IE : 0);
|
||||||
else
|
|
||||||
priv->irqen &= ~RTC_AF;
|
|
||||||
|
|
||||||
return ds1343_update_alarm(dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static irqreturn_t ds1343_thread(int irq, void *dev_id)
|
static irqreturn_t ds1343_thread(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct ds1343_priv *priv = dev_id;
|
struct ds1343_priv *priv = dev_id;
|
||||||
unsigned int stat, control;
|
unsigned int stat;
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
rtc_lock(priv->rtc);
|
rtc_lock(priv->rtc);
|
||||||
|
@ -359,14 +323,10 @@ static irqreturn_t ds1343_thread(int irq, void *dev_id)
|
||||||
stat &= ~DS1343_IRQF0;
|
stat &= ~DS1343_IRQF0;
|
||||||
regmap_write(priv->map, DS1343_STATUS_REG, stat);
|
regmap_write(priv->map, DS1343_STATUS_REG, stat);
|
||||||
|
|
||||||
res = regmap_read(priv->map, DS1343_CONTROL_REG, &control);
|
|
||||||
if (res)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
control &= ~DS1343_A0IE;
|
|
||||||
regmap_write(priv->map, DS1343_CONTROL_REG, control);
|
|
||||||
|
|
||||||
rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF);
|
rtc_update_irq(priv->rtc, 1, RTC_AF | RTC_IRQF);
|
||||||
|
|
||||||
|
regmap_update_bits(priv->map, DS1343_CONTROL_REG,
|
||||||
|
DS1343_A0IE, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -480,8 +440,6 @@ static int ds1343_remove(struct spi_device *spi)
|
||||||
struct ds1343_priv *priv = spi_get_drvdata(spi);
|
struct ds1343_priv *priv = spi_get_drvdata(spi);
|
||||||
|
|
||||||
if (spi->irq) {
|
if (spi->irq) {
|
||||||
priv->irqen &= ~RTC_AF;
|
|
||||||
|
|
||||||
dev_pm_clear_wake_irq(&spi->dev);
|
dev_pm_clear_wake_irq(&spi->dev);
|
||||||
device_init_wakeup(&spi->dev, false);
|
device_init_wakeup(&spi->dev, false);
|
||||||
devm_free_irq(&spi->dev, spi->irq, priv);
|
devm_free_irq(&spi->dev, spi->irq, priv);
|
||||||
|
|
Loading…
Reference in New Issue