|
11 | 11 | #include <linux/rtc.h>
|
12 | 12 | #include <linux/module.h>
|
13 | 13 | #include <linux/of_device.h>
|
| 14 | +#include <linux/pm_wakeirq.h> |
14 | 15 | #include <linux/regmap.h>
|
15 | 16 |
|
16 | 17 | /*
|
|
27 | 28 | #define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
|
28 | 29 | #define PCF85063_REG_CTRL1_STOP BIT(5)
|
29 | 30 |
|
| 31 | +#define PCF85063_REG_CTRL2 0x01 |
| 32 | +#define PCF85063_CTRL2_AF BIT(6) |
| 33 | +#define PCF85063_CTRL2_AIE BIT(7) |
| 34 | + |
30 | 35 | #define PCF85063_REG_SC 0x04 /* datetime */
|
31 | 36 | #define PCF85063_REG_SC_OS 0x80
|
32 | 37 |
|
| 38 | +#define PCF85063_REG_ALM_S 0x0b |
| 39 | +#define PCF85063_AEN BIT(7) |
| 40 | + |
33 | 41 | struct pcf85063_config {
|
34 | 42 | struct regmap_config regmap;
|
| 43 | + unsigned has_alarms:1; |
35 | 44 | };
|
36 | 45 |
|
37 | 46 | struct pcf85063 {
|
@@ -123,11 +132,103 @@ static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
123 | 132 | PCF85063_REG_CTRL1_STOP, 0);
|
124 | 133 | }
|
125 | 134 |
|
| 135 | +static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
| 136 | +{ |
| 137 | + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); |
| 138 | + u8 buf[4]; |
| 139 | + unsigned int val; |
| 140 | + int ret; |
| 141 | + |
| 142 | + ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S, |
| 143 | + buf, sizeof(buf)); |
| 144 | + if (ret) |
| 145 | + return ret; |
| 146 | + |
| 147 | + alrm->time.tm_sec = bcd2bin(buf[0]); |
| 148 | + alrm->time.tm_min = bcd2bin(buf[1]); |
| 149 | + alrm->time.tm_hour = bcd2bin(buf[2]); |
| 150 | + alrm->time.tm_mday = bcd2bin(buf[3]); |
| 151 | + |
| 152 | + ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val); |
| 153 | + if (ret) |
| 154 | + return ret; |
| 155 | + |
| 156 | + alrm->enabled = !!(val & PCF85063_CTRL2_AIE); |
| 157 | + |
| 158 | + return 0; |
| 159 | +} |
| 160 | + |
| 161 | +static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
| 162 | +{ |
| 163 | + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); |
| 164 | + u8 buf[5]; |
| 165 | + int ret; |
| 166 | + |
| 167 | + buf[0] = bin2bcd(alrm->time.tm_sec); |
| 168 | + buf[1] = bin2bcd(alrm->time.tm_min); |
| 169 | + buf[2] = bin2bcd(alrm->time.tm_hour); |
| 170 | + buf[3] = bin2bcd(alrm->time.tm_mday); |
| 171 | + buf[4] = PCF85063_AEN; /* Do not match on week day */ |
| 172 | + |
| 173 | + ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, |
| 174 | + PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0); |
| 175 | + if (ret) |
| 176 | + return ret; |
| 177 | + |
| 178 | + ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S, |
| 179 | + buf, sizeof(buf)); |
| 180 | + if (ret) |
| 181 | + return ret; |
| 182 | + |
| 183 | + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, |
| 184 | + PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, |
| 185 | + alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF); |
| 186 | +} |
| 187 | + |
| 188 | +static int pcf85063_rtc_alarm_irq_enable(struct device *dev, |
| 189 | + unsigned int enabled) |
| 190 | +{ |
| 191 | + struct pcf85063 *pcf85063 = dev_get_drvdata(dev); |
| 192 | + |
| 193 | + return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, |
| 194 | + PCF85063_CTRL2_AIE, |
| 195 | + enabled ? PCF85063_CTRL2_AIE : 0); |
| 196 | +} |
| 197 | + |
| 198 | +static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id) |
| 199 | +{ |
| 200 | + struct pcf85063 *pcf85063 = dev_id; |
| 201 | + unsigned int val; |
| 202 | + int err; |
| 203 | + |
| 204 | + err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val); |
| 205 | + if (err) |
| 206 | + return IRQ_NONE; |
| 207 | + |
| 208 | + if (val & PCF85063_CTRL2_AF) { |
| 209 | + rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF); |
| 210 | + regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2, |
| 211 | + PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, |
| 212 | + 0); |
| 213 | + return IRQ_HANDLED; |
| 214 | + } |
| 215 | + |
| 216 | + return IRQ_NONE; |
| 217 | +} |
| 218 | + |
126 | 219 | static const struct rtc_class_ops pcf85063_rtc_ops = {
|
127 | 220 | .read_time = pcf85063_rtc_read_time,
|
128 | 221 | .set_time = pcf85063_rtc_set_time
|
129 | 222 | };
|
130 | 223 |
|
| 224 | +static const struct rtc_class_ops pcf85063_rtc_ops_alarm = { |
| 225 | + .read_time = pcf85063_rtc_read_time, |
| 226 | + .set_time = pcf85063_rtc_set_time, |
| 227 | + .read_alarm = pcf85063_rtc_read_alarm, |
| 228 | + .set_alarm = pcf85063_rtc_set_alarm, |
| 229 | + .alarm_irq_enable = pcf85063_rtc_alarm_irq_enable, |
| 230 | +}; |
| 231 | + |
131 | 232 | static int pcf85063_load_capacitance(struct pcf85063 *pcf85063,
|
132 | 233 | const struct device_node *np)
|
133 | 234 | {
|
@@ -157,6 +258,7 @@ static const struct pcf85063_config pcf85063a_config = {
|
157 | 258 | .val_bits = 8,
|
158 | 259 | .max_register = 0x11,
|
159 | 260 | },
|
| 261 | + .has_alarms = 1, |
160 | 262 | };
|
161 | 263 |
|
162 | 264 | static const struct pcf85063_config pcf85063tp_config = {
|
@@ -209,6 +311,25 @@ static int pcf85063_probe(struct i2c_client *client)
|
209 | 311 | pcf85063->rtc->ops = &pcf85063_rtc_ops;
|
210 | 312 | pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
211 | 313 | pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
| 314 | + pcf85063->rtc->uie_unsupported = 1; |
| 315 | + |
| 316 | + if (config->has_alarms && client->irq > 0) { |
| 317 | + err = devm_request_threaded_irq(&client->dev, client->irq, |
| 318 | + NULL, pcf85063_rtc_handle_irq, |
| 319 | + IRQF_TRIGGER_LOW | IRQF_ONESHOT, |
| 320 | + "pcf85063", pcf85063); |
| 321 | + if (err) { |
| 322 | + dev_warn(&pcf85063->rtc->dev, |
| 323 | + "unable to request IRQ, alarms disabled\n"); |
| 324 | + } else { |
| 325 | + pcf85063->rtc->ops = &pcf85063_rtc_ops_alarm; |
| 326 | + device_init_wakeup(&client->dev, true); |
| 327 | + err = dev_pm_set_wake_irq(&client->dev, client->irq); |
| 328 | + if (err) |
| 329 | + dev_err(&pcf85063->rtc->dev, |
| 330 | + "failed to enable irq wake\n"); |
| 331 | + } |
| 332 | + } |
212 | 333 |
|
213 | 334 | return rtc_register_device(pcf85063->rtc);
|
214 | 335 | }
|
|
0 commit comments