OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From bda12381598c3df43f4e60362a8cd4af58b7f5b0 Mon Sep 17 00:00:00 2001 |
2 | From: Yangbo Lu <yangbo.lu@nxp.com> |
||
3 | Date: Wed, 17 Jan 2018 15:38:54 +0800 |
||
4 | Subject: [PATCH 26/30] rtc: support layerscape |
||
5 | |||
6 | This is an integrated patch for layerscape rtc support. |
||
7 | |||
8 | Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com> |
||
9 | Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> |
||
10 | --- |
||
11 | drivers/rtc/rtc-pcf85263.c | 665 +++++++++++++++++++++++++++++++++++++++++++++ |
||
12 | 1 file changed, 665 insertions(+) |
||
13 | create mode 100644 drivers/rtc/rtc-pcf85263.c |
||
14 | |||
15 | --- /dev/null |
||
16 | +++ b/drivers/rtc/rtc-pcf85263.c |
||
17 | @@ -0,0 +1,665 @@ |
||
18 | +/* |
||
19 | + * rtc-pcf85263 Driver for the NXP PCF85263 RTC |
||
20 | + * Copyright 2016 Parkeon |
||
21 | + * |
||
22 | + * This program is free software; you can redistribute it and/or modify |
||
23 | + * it under the terms of the GNU General Public License version 2 as |
||
24 | + * published by the Free Software Foundation. |
||
25 | + */ |
||
26 | + |
||
27 | +#include <linux/module.h> |
||
28 | +#include <linux/mutex.h> |
||
29 | +#include <linux/rtc.h> |
||
30 | +#include <linux/i2c.h> |
||
31 | +#include <linux/bcd.h> |
||
32 | +#include <linux/of.h> |
||
33 | +#include <linux/of_device.h> |
||
34 | +#include <linux/regmap.h> |
||
35 | + |
||
36 | + |
||
37 | +#define DRV_NAME "rtc-pcf85263" |
||
38 | + |
||
39 | +/* Quartz capacitance */ |
||
40 | +#define PCF85263_QUARTZCAP_7pF 0 |
||
41 | +#define PCF85263_QUARTZCAP_6pF 1 |
||
42 | +#define PCF85263_QUARTZCAP_12p5pF 2 |
||
43 | + |
||
44 | +/* Quartz drive strength */ |
||
45 | +#define PCF85263_QUARTZDRIVE_NORMAL 0 |
||
46 | +#define PCF85263_QUARTZDRIVE_LOW 1 |
||
47 | +#define PCF85263_QUARTZDRIVE_HIGH 2 |
||
48 | + |
||
49 | + |
||
50 | +#define PCF85263_REG_RTC_SC 0x01 /* Seconds */ |
||
51 | +#define PCF85263_REG_RTC_SC_OS BIT(7) /* Oscilator stopped flag */ |
||
52 | + |
||
53 | +#define PCF85263_REG_RTC_MN 0x02 /* Minutes */ |
||
54 | +#define PCF85263_REG_RTC_HR 0x03 /* Hours */ |
||
55 | +#define PCF85263_REG_RTC_DT 0x04 /* Day of month 1-31 */ |
||
56 | +#define PCF85263_REG_RTC_DW 0x05 /* Day of week 0-6 */ |
||
57 | +#define PCF85263_REG_RTC_MO 0x06 /* Month 1-12 */ |
||
58 | +#define PCF85263_REG_RTC_YR 0x07 /* Year 0-99 */ |
||
59 | + |
||
60 | +#define PCF85263_REG_ALM1_SC 0x08 /* Seconds */ |
||
61 | +#define PCF85263_REG_ALM1_MN 0x09 /* Minutes */ |
||
62 | +#define PCF85263_REG_ALM1_HR 0x0a /* Hours */ |
||
63 | +#define PCF85263_REG_ALM1_DT 0x0b /* Day of month 1-31 */ |
||
64 | +#define PCF85263_REG_ALM1_MO 0x0c /* Month 1-12 */ |
||
65 | + |
||
66 | +#define PCF85263_REG_ALM_CTL 0x10 |
||
67 | +#define PCF85263_REG_ALM_CTL_ALL_A1E 0x1f /* sec,min,hr,day,mon alarm 1 */ |
||
68 | + |
||
69 | +#define PCF85263_REG_OSC 0x25 |
||
70 | +#define PCF85263_REG_OSC_CL_MASK (BIT(0) | BIT(1)) |
||
71 | +#define PCF85263_REG_OSC_CL_SHIFT 0 |
||
72 | +#define PCF85263_REG_OSC_OSCD_MASK (BIT(2) | BIT(3)) |
||
73 | +#define PCF85263_REG_OSC_OSCD_SHIFT 2 |
||
74 | +#define PCF85263_REG_OSC_LOWJ BIT(4) |
||
75 | +#define PCF85263_REG_OSC_12H BIT(5) |
||
76 | + |
||
77 | +#define PCF85263_REG_PINIO 0x27 |
||
78 | +#define PCF85263_REG_PINIO_INTAPM_MASK (BIT(0) | BIT(1)) |
||
79 | +#define PCF85263_REG_PINIO_INTAPM_SHIFT 0 |
||
80 | +#define PCF85263_INTAPM_INTA (0x2 << PCF85263_REG_PINIO_INTAPM_SHIFT) |
||
81 | +#define PCF85263_INTAPM_HIGHZ (0x3 << PCF85263_REG_PINIO_INTAPM_SHIFT) |
||
82 | +#define PCF85263_REG_PINIO_TSPM_MASK (BIT(2) | BIT(3)) |
||
83 | +#define PCF85263_REG_PINIO_TSPM_SHIFT 2 |
||
84 | +#define PCF85263_TSPM_DISABLED (0x0 << PCF85263_REG_PINIO_TSPM_SHIFT) |
||
85 | +#define PCF85263_TSPM_INTB (0x1 << PCF85263_REG_PINIO_TSPM_SHIFT) |
||
86 | +#define PCF85263_REG_PINIO_CLKDISABLE BIT(7) |
||
87 | + |
||
88 | +#define PCF85263_REG_FUNCTION 0x28 |
||
89 | +#define PCF85263_REG_FUNCTION_COF_MASK 0x7 |
||
90 | +#define PCF85263_REG_FUNCTION_COF_OFF 0x7 /* No clock output */ |
||
91 | + |
||
92 | +#define PCF85263_REG_INTA_CTL 0x29 |
||
93 | +#define PCF85263_REG_INTB_CTL 0x2A |
||
94 | +#define PCF85263_REG_INTx_CTL_A1E BIT(4) /* Alarm 1 */ |
||
95 | +#define PCF85263_REG_INTx_CTL_ILP BIT(7) /* 0=pulse, 1=level */ |
||
96 | + |
||
97 | +#define PCF85263_REG_FLAGS 0x2B |
||
98 | +#define PCF85263_REG_FLAGS_A1F BIT(5) |
||
99 | + |
||
100 | +#define PCF85263_REG_RAM_BYTE 0x2c |
||
101 | + |
||
102 | +#define PCF85263_REG_STOPENABLE 0x2e |
||
103 | +#define PCF85263_REG_STOPENABLE_STOP BIT(0) |
||
104 | + |
||
105 | +#define PCF85263_REG_RESET 0x2f /* Reset command */ |
||
106 | +#define PCF85263_REG_RESET_CMD_CPR 0xa4 /* Clear prescaler */ |
||
107 | + |
||
108 | +#define PCF85263_MAX_REG 0x2f |
||
109 | + |
||
110 | +#define PCF85263_HR_PM BIT(5) |
||
111 | + |
||
112 | +enum pcf85263_irqpin { |
||
113 | + PCF85263_IRQPIN_NONE, |
||
114 | + PCF85263_IRQPIN_INTA, |
||
115 | + PCF85263_IRQPIN_INTB |
||
116 | +}; |
||
117 | + |
||
118 | +static const char *const pcf85263_irqpin_names[] = { |
||
119 | + [PCF85263_IRQPIN_NONE] = "None", |
||
120 | + [PCF85263_IRQPIN_INTA] = "INTA", |
||
121 | + [PCF85263_IRQPIN_INTB] = "INTB" |
||
122 | +}; |
||
123 | + |
||
124 | +struct pcf85263 { |
||
125 | + struct device *dev; |
||
126 | + struct rtc_device *rtc; |
||
127 | + struct regmap *regmap; |
||
128 | + enum pcf85263_irqpin irq_pin; |
||
129 | + int irq; |
||
130 | + bool mode_12h; |
||
131 | +}; |
||
132 | + |
||
133 | +/* |
||
134 | + * Helpers to convert 12h to 24h and vice versa. |
||
135 | + * Values in register are stored in BCD with a PM flag in bit 5 |
||
136 | + * |
||
137 | + * 23:00 <=> 11PM <=> 0x31 |
||
138 | + * 00:00 <=> 12AM <=> 0x12 |
||
139 | + * 01:00 <=> 1AM <=> 0x01 |
||
140 | + * 12:00 <=> 12PM <=> 0x32 |
||
141 | + * 13:00 <=> 1PM <=> 0x21 |
||
142 | + */ |
||
143 | +static int pcf85263_bcd12h_to_bin24h(int regval) |
||
144 | +{ |
||
145 | + int hr = bcd2bin(regval & 0x1f); |
||
146 | + bool pm = regval & PCF85263_HR_PM; |
||
147 | + |
||
148 | + if (hr == 12) |
||
149 | + return pm ? 12 : 0; |
||
150 | + |
||
151 | + return pm ? hr + 12 : hr; |
||
152 | +} |
||
153 | + |
||
154 | +static int pcf85263_bin24h_to_bcd12h(int hr24) |
||
155 | +{ |
||
156 | + bool pm = hr24 >= 12; |
||
157 | + int hr12 = hr24 % 12; |
||
158 | + |
||
159 | + if (!hr12) |
||
160 | + hr12++; |
||
161 | + |
||
162 | + return bin2bcd(hr12) | pm ? 0 : PCF85263_HR_PM; |
||
163 | +} |
||
164 | + |
||
165 | +static int pcf85263_read_time(struct device *dev, struct rtc_time *tm) |
||
166 | +{ |
||
167 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
168 | + const int first = PCF85263_REG_RTC_SC; |
||
169 | + const int last = PCF85263_REG_RTC_YR; |
||
170 | + const int len = last - first + 1; |
||
171 | + u8 regs[len]; |
||
172 | + u8 hr_reg; |
||
173 | + int ret; |
||
174 | + |
||
175 | + ret = regmap_bulk_read(pcf85263->regmap, first, regs, len); |
||
176 | + if (ret) |
||
177 | + return ret; |
||
178 | + |
||
179 | + if (regs[PCF85263_REG_RTC_SC - first] & PCF85263_REG_RTC_SC_OS) { |
||
180 | + dev_warn(dev, "Oscillator stop detected, date/time is not reliable.\n"); |
||
181 | + return -EINVAL; |
||
182 | + } |
||
183 | + |
||
184 | + tm->tm_sec = bcd2bin(regs[PCF85263_REG_RTC_SC - first] & 0x7f); |
||
185 | + tm->tm_min = bcd2bin(regs[PCF85263_REG_RTC_MN - first] & 0x7f); |
||
186 | + |
||
187 | + hr_reg = regs[PCF85263_REG_RTC_HR - first]; |
||
188 | + if (pcf85263->mode_12h) |
||
189 | + tm->tm_hour = pcf85263_bcd12h_to_bin24h(hr_reg); |
||
190 | + else |
||
191 | + tm->tm_hour = bcd2bin(hr_reg & 0x3f); |
||
192 | + |
||
193 | + tm->tm_mday = bcd2bin(regs[PCF85263_REG_RTC_DT - first]); |
||
194 | + tm->tm_wday = bcd2bin(regs[PCF85263_REG_RTC_DW - first]); |
||
195 | + tm->tm_mon = bcd2bin(regs[PCF85263_REG_RTC_MO - first]) - 1; |
||
196 | + tm->tm_year = bcd2bin(regs[PCF85263_REG_RTC_YR - first]); |
||
197 | + |
||
198 | + tm->tm_year += 100; /* Assume 21st century */ |
||
199 | + |
||
200 | + return 0; |
||
201 | +} |
||
202 | + |
||
203 | +static int pcf85263_set_time(struct device *dev, struct rtc_time *tm) |
||
204 | +{ |
||
205 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
206 | + |
||
207 | + /* |
||
208 | + * Before setting time need to stop RTC and disable prescaler |
||
209 | + * Do this all in a single I2C transaction exploiting wraparound |
||
210 | + * as described in data sheet. |
||
211 | + * This means that the array below must be in register order |
||
212 | + */ |
||
213 | + u8 regs[] = { |
||
214 | + PCF85263_REG_STOPENABLE_STOP, /* STOP */ |
||
215 | + PCF85263_REG_RESET_CMD_CPR, /* Disable prescaler */ |
||
216 | + /* Wrap around to register 0 (1/100s) */ |
||
217 | + 0, /* 1/100s always zero. */ |
||
218 | + bin2bcd(tm->tm_sec), |
||
219 | + bin2bcd(tm->tm_min), |
||
220 | + bin2bcd(tm->tm_hour), /* 24-hour */ |
||
221 | + bin2bcd(tm->tm_mday), |
||
222 | + bin2bcd(tm->tm_wday + 1), |
||
223 | + bin2bcd(tm->tm_mon + 1), |
||
224 | + bin2bcd(tm->tm_year % 100) |
||
225 | + }; |
||
226 | + int ret; |
||
227 | + |
||
228 | + ret = regmap_bulk_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, |
||
229 | + regs, sizeof(regs)); |
||
230 | + if (ret) |
||
231 | + return ret; |
||
232 | + |
||
233 | + /* As we have set the time in 24H update the hardware for that */ |
||
234 | + if (pcf85263->mode_12h) { |
||
235 | + pcf85263->mode_12h = false; |
||
236 | + ret = regmap_update_bits(pcf85263->regmap, PCF85263_REG_OSC, |
||
237 | + PCF85263_REG_OSC_12H, 0); |
||
238 | + if (ret) |
||
239 | + return ret; |
||
240 | + } |
||
241 | + |
||
242 | + /* Start it again */ |
||
243 | + return regmap_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, 0); |
||
244 | +} |
||
245 | + |
||
246 | +static int pcf85263_enable_alarm(struct pcf85263 *pcf85263, bool enable) |
||
247 | +{ |
||
248 | + int reg; |
||
249 | + int ret; |
||
250 | + |
||
251 | + ret = regmap_update_bits(pcf85263->regmap, PCF85263_REG_ALM_CTL, |
||
252 | + PCF85263_REG_ALM_CTL_ALL_A1E, |
||
253 | + enable ? PCF85263_REG_ALM_CTL_ALL_A1E : 0); |
||
254 | + if (ret) |
||
255 | + return ret; |
||
256 | + |
||
257 | + switch (pcf85263->irq_pin) { |
||
258 | + case PCF85263_IRQPIN_NONE: |
||
259 | + return 0; |
||
260 | + |
||
261 | + case PCF85263_IRQPIN_INTA: |
||
262 | + reg = PCF85263_REG_INTA_CTL; |
||
263 | + break; |
||
264 | + |
||
265 | + case PCF85263_IRQPIN_INTB: |
||
266 | + reg = PCF85263_REG_INTB_CTL; |
||
267 | + break; |
||
268 | + |
||
269 | + default: |
||
270 | + return -EINVAL; |
||
271 | + } |
||
272 | + |
||
273 | + return regmap_update_bits(pcf85263->regmap, reg, |
||
274 | + PCF85263_REG_INTx_CTL_A1E, |
||
275 | + enable ? PCF85263_REG_INTx_CTL_A1E : 0); |
||
276 | +} |
||
277 | + |
||
278 | +static int pcf85263_read_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
||
279 | +{ |
||
280 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
281 | + struct rtc_time *tm = &alarm->time; |
||
282 | + const int first = PCF85263_REG_ALM1_SC; |
||
283 | + const int last = PCF85263_REG_ALM1_MO; |
||
284 | + const int len = last - first + 1; |
||
285 | + u8 regs[len]; |
||
286 | + u8 hr_reg; |
||
287 | + unsigned int regval; |
||
288 | + int ret; |
||
289 | + |
||
290 | + ret = regmap_bulk_read(pcf85263->regmap, first, regs, len); |
||
291 | + if (ret) |
||
292 | + return ret; |
||
293 | + |
||
294 | + tm->tm_sec = bcd2bin(regs[PCF85263_REG_ALM1_SC - first] & 0x7f); |
||
295 | + tm->tm_min = bcd2bin(regs[PCF85263_REG_ALM1_MN - first] & 0x7f); |
||
296 | + |
||
297 | + hr_reg = regs[PCF85263_REG_ALM1_HR - first]; |
||
298 | + if (pcf85263->mode_12h) |
||
299 | + tm->tm_hour = pcf85263_bcd12h_to_bin24h(hr_reg); |
||
300 | + else |
||
301 | + tm->tm_hour = bcd2bin(hr_reg & 0x3f); |
||
302 | + |
||
303 | + tm->tm_mday = bcd2bin(regs[PCF85263_REG_ALM1_DT - first]); |
||
304 | + tm->tm_mon = bcd2bin(regs[PCF85263_REG_ALM1_MO - first]) - 1; |
||
305 | + tm->tm_year = -1; |
||
306 | + tm->tm_wday = -1; |
||
307 | + |
||
308 | + ret = regmap_read(pcf85263->regmap, PCF85263_REG_ALM_CTL, ®val); |
||
309 | + if (ret) |
||
310 | + return ret; |
||
311 | + alarm->enabled = !!(regval & PCF85263_REG_ALM_CTL_ALL_A1E); |
||
312 | + |
||
313 | + ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, ®val); |
||
314 | + if (ret) |
||
315 | + return ret; |
||
316 | + alarm->pending = !!(regval & PCF85263_REG_FLAGS_A1F); |
||
317 | + |
||
318 | + return 0; |
||
319 | +} |
||
320 | + |
||
321 | +static int pcf85263_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) |
||
322 | +{ |
||
323 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
324 | + struct rtc_time *tm = &alarm->time; |
||
325 | + const int first = PCF85263_REG_ALM1_SC; |
||
326 | + const int last = PCF85263_REG_ALM1_MO; |
||
327 | + const int len = last - first + 1; |
||
328 | + u8 regs[len]; |
||
329 | + int ret; |
||
330 | + |
||
331 | + /* Disable alarm comparison during update */ |
||
332 | + ret = pcf85263_enable_alarm(pcf85263, false); |
||
333 | + if (ret) |
||
334 | + return ret; |
||
335 | + |
||
336 | + /* Clear any pending alarm (write 0=>clr, 1=>no change) */ |
||
337 | + ret = regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS, |
||
338 | + (unsigned int)(~PCF85263_REG_FLAGS_A1F)); |
||
339 | + if (ret) |
||
340 | + return ret; |
||
341 | + |
||
342 | + /* Set the alarm time registers */ |
||
343 | + regs[PCF85263_REG_ALM1_SC - first] = bin2bcd(tm->tm_sec); |
||
344 | + regs[PCF85263_REG_ALM1_MN - first] = bin2bcd(tm->tm_min); |
||
345 | + regs[PCF85263_REG_ALM1_HR - first] = pcf85263->mode_12h ? |
||
346 | + pcf85263_bin24h_to_bcd12h(tm->tm_hour) : |
||
347 | + bin2bcd(tm->tm_hour); |
||
348 | + regs[PCF85263_REG_ALM1_DT - first] = bin2bcd(tm->tm_mday); |
||
349 | + regs[PCF85263_REG_ALM1_MO - first] = bin2bcd(tm->tm_mon + 1); |
||
350 | + |
||
351 | + ret = regmap_bulk_write(pcf85263->regmap, first, regs, sizeof(regs)); |
||
352 | + if (ret) |
||
353 | + return ret; |
||
354 | + |
||
355 | + if (alarm->enabled) |
||
356 | + ret = pcf85263_enable_alarm(pcf85263, true); |
||
357 | + |
||
358 | + return ret; |
||
359 | +} |
||
360 | + |
||
361 | +static int pcf85263_alarm_irq_enable(struct device *dev, unsigned int enable) |
||
362 | +{ |
||
363 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
364 | + |
||
365 | + return pcf85263_enable_alarm(pcf85263, !!enable); |
||
366 | +} |
||
367 | + |
||
368 | +static irqreturn_t pcf85263_irq(int irq, void *data) |
||
369 | +{ |
||
370 | + struct pcf85263 *pcf85263 = data; |
||
371 | + unsigned int regval; |
||
372 | + int ret; |
||
373 | + |
||
374 | + ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, ®val); |
||
375 | + if (ret) |
||
376 | + return IRQ_NONE; |
||
377 | + |
||
378 | + if (regval & PCF85263_REG_FLAGS_A1F) { |
||
379 | + regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS, |
||
380 | + (unsigned int)(~PCF85263_REG_FLAGS_A1F)); |
||
381 | + |
||
382 | + rtc_update_irq(pcf85263->rtc, 1, RTC_IRQF | RTC_AF); |
||
383 | + |
||
384 | + return IRQ_HANDLED; |
||
385 | + } |
||
386 | + |
||
387 | + return IRQ_NONE; |
||
388 | +} |
||
389 | + |
||
390 | +static int pcf85263_check_osc_stopped(struct pcf85263 *pcf85263) |
||
391 | +{ |
||
392 | + unsigned int regval; |
||
393 | + int ret; |
||
394 | + |
||
395 | + ret = regmap_read(pcf85263->regmap, PCF85263_REG_RTC_SC, ®val); |
||
396 | + if (ret) |
||
397 | + return ret; |
||
398 | + |
||
399 | + ret = regval & PCF85263_REG_RTC_SC_OS ? 1 : 0; |
||
400 | + if (ret) |
||
401 | + dev_warn(pcf85263->dev, "Oscillator stop detected, date/time is not reliable.\n"); |
||
402 | + |
||
403 | + return ret; |
||
404 | +} |
||
405 | + |
||
406 | +#ifdef CONFIG_RTC_INTF_DEV |
||
407 | +static int pcf85263_ioctl(struct device *dev, |
||
408 | + unsigned int cmd, unsigned long arg) |
||
409 | +{ |
||
410 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
411 | + int ret; |
||
412 | + |
||
413 | + switch (cmd) { |
||
414 | + case RTC_VL_READ: |
||
415 | + ret = pcf85263_check_osc_stopped(pcf85263); |
||
416 | + if (ret < 0) |
||
417 | + return ret; |
||
418 | + |
||
419 | + if (copy_to_user((void __user *)arg, &ret, sizeof(int))) |
||
420 | + return -EFAULT; |
||
421 | + return 0; |
||
422 | + |
||
423 | + case RTC_VL_CLR: |
||
424 | + return regmap_update_bits(pcf85263->regmap, |
||
425 | + PCF85263_REG_RTC_SC, |
||
426 | + PCF85263_REG_RTC_SC_OS, 0); |
||
427 | + default: |
||
428 | + return -ENOIOCTLCMD; |
||
429 | + } |
||
430 | +} |
||
431 | +#else |
||
432 | +#define pcf85263_ioctl NULL |
||
433 | +#endif |
||
434 | + |
||
435 | +static int pcf85263_init_hw(struct pcf85263 *pcf85263) |
||
436 | +{ |
||
437 | + struct device_node *np = pcf85263->dev->of_node; |
||
438 | + unsigned int regval; |
||
439 | + u32 propval; |
||
440 | + int ret; |
||
441 | + |
||
442 | + /* Determine if oscilator has been stopped (probably low power) */ |
||
443 | + ret = pcf85263_check_osc_stopped(pcf85263); |
||
444 | + if (ret < 0) { |
||
445 | + /* Log here since this is the first hw access on probe */ |
||
446 | + dev_err(pcf85263->dev, "Unable to read register\n"); |
||
447 | + |
||
448 | + return ret; |
||
449 | + } |
||
450 | + |
||
451 | + /* Determine 12/24H mode */ |
||
452 | + ret = regmap_read(pcf85263->regmap, PCF85263_REG_OSC, ®val); |
||
453 | + if (ret) |
||
454 | + return ret; |
||
455 | + pcf85263->mode_12h = !!(regval & PCF85263_REG_OSC_12H); |
||
456 | + |
||
457 | + /* Set oscilator register */ |
||
458 | + regval &= ~PCF85263_REG_OSC_12H; /* keep current 12/24 h setting */ |
||
459 | + |
||
460 | + propval = PCF85263_QUARTZCAP_12p5pF; |
||
461 | + of_property_read_u32(np, "quartz-load-capacitance", &propval); |
||
462 | + regval |= ((propval << PCF85263_REG_OSC_CL_SHIFT) |
||
463 | + & PCF85263_REG_OSC_CL_MASK); |
||
464 | + |
||
465 | + propval = PCF85263_QUARTZDRIVE_NORMAL; |
||
466 | + of_property_read_u32(np, "quartz-drive-strength", &propval); |
||
467 | + regval |= ((propval << PCF85263_REG_OSC_OSCD_SHIFT) |
||
468 | + & PCF85263_REG_OSC_OSCD_MASK); |
||
469 | + |
||
470 | + if (of_property_read_bool(np, "quartz-low-jitter")) |
||
471 | + regval |= PCF85263_REG_OSC_LOWJ; |
||
472 | + |
||
473 | + ret = regmap_write(pcf85263->regmap, PCF85263_REG_OSC, regval); |
||
474 | + if (ret) |
||
475 | + return ret; |
||
476 | + |
||
477 | + /* Set function register (RTC mode, 1s tick, clock output static) */ |
||
478 | + ret = regmap_write(pcf85263->regmap, PCF85263_REG_FUNCTION, |
||
479 | + PCF85263_REG_FUNCTION_COF_OFF); |
||
480 | + if (ret) |
||
481 | + return ret; |
||
482 | + |
||
483 | + /* Set all interrupts to disabled, level mode */ |
||
484 | + ret = regmap_write(pcf85263->regmap, PCF85263_REG_INTA_CTL, |
||
485 | + PCF85263_REG_INTx_CTL_ILP); |
||
486 | + if (ret) |
||
487 | + return ret; |
||
488 | + ret = regmap_write(pcf85263->regmap, PCF85263_REG_INTB_CTL, |
||
489 | + PCF85263_REG_INTx_CTL_ILP); |
||
490 | + if (ret) |
||
491 | + return ret; |
||
492 | + |
||
493 | + /* Setup IO pin config register */ |
||
494 | + regval = PCF85263_REG_PINIO_CLKDISABLE; |
||
495 | + switch (pcf85263->irq_pin) { |
||
496 | + case PCF85263_IRQPIN_INTA: |
||
497 | + regval |= (PCF85263_INTAPM_INTA | PCF85263_TSPM_DISABLED); |
||
498 | + break; |
||
499 | + case PCF85263_IRQPIN_INTB: |
||
500 | + regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_INTB); |
||
501 | + break; |
||
502 | + case PCF85263_IRQPIN_NONE: |
||
503 | + regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_DISABLED); |
||
504 | + break; |
||
505 | + } |
||
506 | + ret = regmap_write(pcf85263->regmap, PCF85263_REG_PINIO, regval); |
||
507 | + |
||
508 | + return ret; |
||
509 | +} |
||
510 | + |
||
511 | +static const struct rtc_class_ops rtc_ops = { |
||
512 | + .ioctl = pcf85263_ioctl, |
||
513 | + .read_time = pcf85263_read_time, |
||
514 | + .set_time = pcf85263_set_time, |
||
515 | + .read_alarm = pcf85263_read_alarm, |
||
516 | + .set_alarm = pcf85263_set_alarm, |
||
517 | + .alarm_irq_enable = pcf85263_alarm_irq_enable, |
||
518 | +}; |
||
519 | + |
||
520 | +static const struct regmap_config pcf85263_regmap_cfg = { |
||
521 | + .reg_bits = 8, |
||
522 | + .val_bits = 8, |
||
523 | + .max_register = PCF85263_MAX_REG, |
||
524 | +}; |
||
525 | + |
||
526 | +/* |
||
527 | + * On some boards the interrupt line may not be wired to the CPU but only to |
||
528 | + * a power supply circuit. |
||
529 | + * In that case no interrupt will be specified in the device tree but the |
||
530 | + * wakeup-source DT property may be used to enable wakeup programming in |
||
531 | + * sysfs |
||
532 | + */ |
||
533 | +static bool pcf85263_can_wakeup_machine(struct pcf85263 *pcf85263) |
||
534 | +{ |
||
535 | + return pcf85263->irq || |
||
536 | + of_property_read_bool(pcf85263->dev->of_node, "wakeup-source"); |
||
537 | +} |
||
538 | + |
||
539 | +static int pcf85263_probe(struct i2c_client *client, |
||
540 | + const struct i2c_device_id *id) |
||
541 | +{ |
||
542 | + struct device *dev = &client->dev; |
||
543 | + struct pcf85263 *pcf85263; |
||
544 | + int ret; |
||
545 | + |
||
546 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C | |
||
547 | + I2C_FUNC_SMBUS_BYTE_DATA | |
||
548 | + I2C_FUNC_SMBUS_I2C_BLOCK)) |
||
549 | + return -ENODEV; |
||
550 | + |
||
551 | + pcf85263 = devm_kzalloc(dev, sizeof(*pcf85263), GFP_KERNEL); |
||
552 | + if (!pcf85263) |
||
553 | + return -ENOMEM; |
||
554 | + |
||
555 | + pcf85263->dev = dev; |
||
556 | + pcf85263->irq = client->irq; |
||
557 | + dev_set_drvdata(dev, pcf85263); |
||
558 | + |
||
559 | + pcf85263->regmap = devm_regmap_init_i2c(client, &pcf85263_regmap_cfg); |
||
560 | + if (IS_ERR(pcf85263->regmap)) { |
||
561 | + ret = PTR_ERR(pcf85263->regmap); |
||
562 | + dev_err(dev, "regmap allocation failed (%d)\n", ret); |
||
563 | + |
||
564 | + return ret; |
||
565 | + } |
||
566 | + |
||
567 | + /* Determine which interrupt pin the board uses */ |
||
568 | + if (pcf85263_can_wakeup_machine(pcf85263)) { |
||
569 | + if (of_property_match_string(dev->of_node, |
||
570 | + "interrupt-names", "INTB") >= 0) |
||
571 | + pcf85263->irq_pin = PCF85263_IRQPIN_INTB; |
||
572 | + else |
||
573 | + pcf85263->irq_pin = PCF85263_IRQPIN_INTA; |
||
574 | + } else { |
||
575 | + pcf85263->irq_pin = PCF85263_IRQPIN_NONE; |
||
576 | + } |
||
577 | + |
||
578 | + ret = pcf85263_init_hw(pcf85263); |
||
579 | + if (ret) |
||
580 | + return ret; |
||
581 | + |
||
582 | + if (pcf85263->irq) { |
||
583 | + ret = devm_request_threaded_irq(dev, pcf85263->irq, NULL, |
||
584 | + pcf85263_irq, |
||
585 | + IRQF_ONESHOT, |
||
586 | + dev->driver->name, pcf85263); |
||
587 | + if (ret) { |
||
588 | + dev_err(dev, "irq %d unavailable (%d)\n", |
||
589 | + pcf85263->irq, ret); |
||
590 | + pcf85263->irq = 0; |
||
591 | + } |
||
592 | + } |
||
593 | + |
||
594 | + if (pcf85263_can_wakeup_machine(pcf85263)) |
||
595 | + device_init_wakeup(dev, true); |
||
596 | + |
||
597 | + pcf85263->rtc = devm_rtc_device_register(dev, dev->driver->name, |
||
598 | + &rtc_ops, THIS_MODULE); |
||
599 | + ret = PTR_ERR_OR_ZERO(pcf85263->rtc); |
||
600 | + if (ret) |
||
601 | + return ret; |
||
602 | + |
||
603 | + /* We cannot support UIE mode if we do not have an IRQ line */ |
||
604 | + if (!pcf85263->irq) |
||
605 | + pcf85263->rtc->uie_unsupported = 1; |
||
606 | + |
||
607 | + dev_info(pcf85263->dev, |
||
608 | + "PCF85263 RTC (irqpin=%s irq=%d)\n", |
||
609 | + pcf85263_irqpin_names[pcf85263->irq_pin], |
||
610 | + pcf85263->irq); |
||
611 | + |
||
612 | + return 0; |
||
613 | +} |
||
614 | + |
||
615 | +static int pcf85263_remove(struct i2c_client *client) |
||
616 | +{ |
||
617 | + struct pcf85263 *pcf85263 = i2c_get_clientdata(client); |
||
618 | + |
||
619 | + if (pcf85263_can_wakeup_machine(pcf85263)) |
||
620 | + device_init_wakeup(pcf85263->dev, false); |
||
621 | + |
||
622 | + return 0; |
||
623 | +} |
||
624 | + |
||
625 | +#ifdef CONFIG_PM_SLEEP |
||
626 | +static int pcf85263_suspend(struct device *dev) |
||
627 | +{ |
||
628 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
629 | + int ret = 0; |
||
630 | + |
||
631 | + if (device_may_wakeup(dev)) |
||
632 | + ret = enable_irq_wake(pcf85263->irq); |
||
633 | + |
||
634 | + return ret; |
||
635 | +} |
||
636 | + |
||
637 | +static int pcf85263_resume(struct device *dev) |
||
638 | +{ |
||
639 | + struct pcf85263 *pcf85263 = dev_get_drvdata(dev); |
||
640 | + int ret = 0; |
||
641 | + |
||
642 | + if (device_may_wakeup(dev)) |
||
643 | + ret = disable_irq_wake(pcf85263->irq); |
||
644 | + |
||
645 | + return ret; |
||
646 | +} |
||
647 | + |
||
648 | +#endif |
||
649 | + |
||
650 | +static const struct i2c_device_id pcf85263_id[] = { |
||
651 | + { "pcf85263", 0 }, |
||
652 | + { } |
||
653 | +}; |
||
654 | +MODULE_DEVICE_TABLE(i2c, pcf85263_id); |
||
655 | + |
||
656 | +#ifdef CONFIG_OF |
||
657 | +static const struct of_device_id pcf85263_of_match[] = { |
||
658 | + { .compatible = "nxp,pcf85263" }, |
||
659 | + {} |
||
660 | +}; |
||
661 | +MODULE_DEVICE_TABLE(of, pcf85263_of_match); |
||
662 | +#endif |
||
663 | + |
||
664 | +static SIMPLE_DEV_PM_OPS(pcf85263_pm_ops, pcf85263_suspend, pcf85263_resume); |
||
665 | + |
||
666 | +static struct i2c_driver pcf85263_driver = { |
||
667 | + .driver = { |
||
668 | + .name = "rtc-pcf85263", |
||
669 | + .of_match_table = of_match_ptr(pcf85263_of_match), |
||
670 | + .pm = &pcf85263_pm_ops, |
||
671 | + }, |
||
672 | + .probe = pcf85263_probe, |
||
673 | + .remove = pcf85263_remove, |
||
674 | + .id_table = pcf85263_id, |
||
675 | +}; |
||
676 | + |
||
677 | +module_i2c_driver(pcf85263_driver); |
||
678 | + |
||
679 | +MODULE_AUTHOR("Martin Fuzzey <mfuzzey@parkeon.com>"); |
||
680 | +MODULE_DESCRIPTION("PCF85263 RTC Driver"); |
||
681 | +MODULE_LICENSE("GPL"); |
||
682 | + |