diff options
author | Mylène Josserand <mylene.josserand@free-electrons.com> | 2016-03-21 13:06:09 -0400 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2016-03-24 21:41:13 -0400 |
commit | 59a8383adb75459c9d6766656bccc05950b783ea (patch) | |
tree | 1e05f7f7883155af28e9bc43b76cefcd0f8a083a /drivers/rtc | |
parent | 85062c9b990b1dc8bbb8971ee7d3044a999cf25f (diff) |
rtc: abx80x: handle autocalibration
The autocalibration is separated in two bits to set in Oscillator
Control register (0x1c) :
- OSEL bit to select the oscillator type (XT or RC).
- ACAL bit to select the autocalibration type.
These functionnalities are exported in sysfs entries : "oscillator"
and "autocalibration". Respectively, the values are "xtal" for XT
oscillator and "rc" for RC oscillator and 0 to disable the
autocalibration cycle, 512 for a 512 seconds autocalibration cycle
and 1024 for a cycle of 1024 seconds.
Examples :
Set to XT Oscillator
echo xtal > /sys/class/rtc/rtc0/device/oscillator
Activate an autocalibration every 512 seconds
echo 512 > /sys/class/rtc/rtc0/device/autocalibration
Signed-off-by: Mylène Josserand <mylene.josserand@free-electrons.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-abx80x.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/drivers/rtc/rtc-abx80x.c b/drivers/rtc/rtc-abx80x.c index d41bbcd653f6..0e4c9a0989d1 100644 --- a/drivers/rtc/rtc-abx80x.c +++ b/drivers/rtc/rtc-abx80x.c | |||
@@ -49,7 +49,19 @@ | |||
49 | 49 | ||
50 | #define ABX8XX_REG_CD_TIMER_CTL 0x18 | 50 | #define ABX8XX_REG_CD_TIMER_CTL 0x18 |
51 | 51 | ||
52 | #define ABX8XX_REG_OSC 0x1c | ||
53 | #define ABX8XX_OSC_FOS BIT(3) | ||
54 | #define ABX8XX_OSC_BOS BIT(4) | ||
55 | #define ABX8XX_OSC_ACAL_512 BIT(5) | ||
56 | #define ABX8XX_OSC_ACAL_1024 BIT(6) | ||
57 | |||
58 | #define ABX8XX_OSC_OSEL BIT(7) | ||
59 | |||
60 | #define ABX8XX_REG_OSS 0x1d | ||
61 | #define ABX8XX_OSS_OMODE BIT(4) | ||
62 | |||
52 | #define ABX8XX_REG_CFG_KEY 0x1f | 63 | #define ABX8XX_REG_CFG_KEY 0x1f |
64 | #define ABX8XX_CFG_KEY_OSC 0xa1 | ||
53 | #define ABX8XX_CFG_KEY_MISC 0x9d | 65 | #define ABX8XX_CFG_KEY_MISC 0x9d |
54 | 66 | ||
55 | #define ABX8XX_REG_ID0 0x28 | 67 | #define ABX8XX_REG_ID0 0x28 |
@@ -81,6 +93,20 @@ static struct abx80x_cap abx80x_caps[] = { | |||
81 | [ABX80X] = {.pn = 0} | 93 | [ABX80X] = {.pn = 0} |
82 | }; | 94 | }; |
83 | 95 | ||
96 | static int abx80x_is_rc_mode(struct i2c_client *client) | ||
97 | { | ||
98 | int flags = 0; | ||
99 | |||
100 | flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSS); | ||
101 | if (flags < 0) { | ||
102 | dev_err(&client->dev, | ||
103 | "Failed to read autocalibration attribute\n"); | ||
104 | return flags; | ||
105 | } | ||
106 | |||
107 | return (flags & ABX8XX_OSS_OMODE) ? 1 : 0; | ||
108 | } | ||
109 | |||
84 | static int abx80x_enable_trickle_charger(struct i2c_client *client, | 110 | static int abx80x_enable_trickle_charger(struct i2c_client *client, |
85 | u8 trickle_cfg) | 111 | u8 trickle_cfg) |
86 | { | 112 | { |
@@ -248,6 +274,174 @@ static int abx80x_set_alarm(struct device *dev, struct rtc_wkalrm *t) | |||
248 | return 0; | 274 | return 0; |
249 | } | 275 | } |
250 | 276 | ||
277 | static int abx80x_rtc_set_autocalibration(struct device *dev, | ||
278 | int autocalibration) | ||
279 | { | ||
280 | struct i2c_client *client = to_i2c_client(dev); | ||
281 | int retval, flags = 0; | ||
282 | |||
283 | if ((autocalibration != 0) && (autocalibration != 1024) && | ||
284 | (autocalibration != 512)) { | ||
285 | dev_err(dev, "autocalibration value outside permitted range\n"); | ||
286 | return -EINVAL; | ||
287 | } | ||
288 | |||
289 | flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); | ||
290 | if (flags < 0) | ||
291 | return flags; | ||
292 | |||
293 | if (autocalibration == 0) { | ||
294 | flags &= ~(ABX8XX_OSC_ACAL_512 | ABX8XX_OSC_ACAL_1024); | ||
295 | } else if (autocalibration == 1024) { | ||
296 | /* 1024 autocalibration is 0x10 */ | ||
297 | flags |= ABX8XX_OSC_ACAL_1024; | ||
298 | flags &= ~(ABX8XX_OSC_ACAL_512); | ||
299 | } else { | ||
300 | /* 512 autocalibration is 0x11 */ | ||
301 | flags |= (ABX8XX_OSC_ACAL_1024 | ABX8XX_OSC_ACAL_512); | ||
302 | } | ||
303 | |||
304 | /* Unlock write access to Oscillator Control Register */ | ||
305 | retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, | ||
306 | ABX8XX_CFG_KEY_OSC); | ||
307 | if (retval < 0) { | ||
308 | dev_err(dev, "Failed to write CONFIG_KEY register\n"); | ||
309 | return retval; | ||
310 | } | ||
311 | |||
312 | retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags); | ||
313 | |||
314 | return retval; | ||
315 | } | ||
316 | |||
317 | static int abx80x_rtc_get_autocalibration(struct device *dev) | ||
318 | { | ||
319 | struct i2c_client *client = to_i2c_client(dev); | ||
320 | int flags = 0, autocalibration; | ||
321 | |||
322 | flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); | ||
323 | if (flags < 0) | ||
324 | return flags; | ||
325 | |||
326 | if (flags & ABX8XX_OSC_ACAL_512) | ||
327 | autocalibration = 512; | ||
328 | else if (flags & ABX8XX_OSC_ACAL_1024) | ||
329 | autocalibration = 1024; | ||
330 | else | ||
331 | autocalibration = 0; | ||
332 | |||
333 | return autocalibration; | ||
334 | } | ||
335 | |||
336 | static ssize_t autocalibration_store(struct device *dev, | ||
337 | struct device_attribute *attr, | ||
338 | const char *buf, size_t count) | ||
339 | { | ||
340 | int retval; | ||
341 | unsigned long autocalibration = 0; | ||
342 | |||
343 | retval = kstrtoul(buf, 10, &autocalibration); | ||
344 | if (retval < 0) { | ||
345 | dev_err(dev, "Failed to store RTC autocalibration attribute\n"); | ||
346 | return -EINVAL; | ||
347 | } | ||
348 | |||
349 | retval = abx80x_rtc_set_autocalibration(dev, autocalibration); | ||
350 | |||
351 | return retval ? retval : count; | ||
352 | } | ||
353 | |||
354 | static ssize_t autocalibration_show(struct device *dev, | ||
355 | struct device_attribute *attr, char *buf) | ||
356 | { | ||
357 | int autocalibration = 0; | ||
358 | |||
359 | autocalibration = abx80x_rtc_get_autocalibration(dev); | ||
360 | if (autocalibration < 0) { | ||
361 | dev_err(dev, "Failed to read RTC autocalibration\n"); | ||
362 | sprintf(buf, "0\n"); | ||
363 | return autocalibration; | ||
364 | } | ||
365 | |||
366 | return sprintf(buf, "%d\n", autocalibration); | ||
367 | } | ||
368 | |||
369 | static DEVICE_ATTR_RW(autocalibration); | ||
370 | |||
371 | static ssize_t oscillator_store(struct device *dev, | ||
372 | struct device_attribute *attr, | ||
373 | const char *buf, size_t count) | ||
374 | { | ||
375 | struct i2c_client *client = to_i2c_client(dev); | ||
376 | int retval, flags, rc_mode = 0; | ||
377 | |||
378 | if (strncmp(buf, "rc", 2) == 0) { | ||
379 | rc_mode = 1; | ||
380 | } else if (strncmp(buf, "xtal", 4) == 0) { | ||
381 | rc_mode = 0; | ||
382 | } else { | ||
383 | dev_err(dev, "Oscillator selection value outside permitted ones\n"); | ||
384 | return -EINVAL; | ||
385 | } | ||
386 | |||
387 | flags = i2c_smbus_read_byte_data(client, ABX8XX_REG_OSC); | ||
388 | if (flags < 0) | ||
389 | return flags; | ||
390 | |||
391 | if (rc_mode == 0) | ||
392 | flags &= ~(ABX8XX_OSC_OSEL); | ||
393 | else | ||
394 | flags |= (ABX8XX_OSC_OSEL); | ||
395 | |||
396 | /* Unlock write access on Oscillator Control register */ | ||
397 | retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_CFG_KEY, | ||
398 | ABX8XX_CFG_KEY_OSC); | ||
399 | if (retval < 0) { | ||
400 | dev_err(dev, "Failed to write CONFIG_KEY register\n"); | ||
401 | return retval; | ||
402 | } | ||
403 | |||
404 | retval = i2c_smbus_write_byte_data(client, ABX8XX_REG_OSC, flags); | ||
405 | if (retval < 0) { | ||
406 | dev_err(dev, "Failed to write Oscillator Control register\n"); | ||
407 | return retval; | ||
408 | } | ||
409 | |||
410 | return retval ? retval : count; | ||
411 | } | ||
412 | |||
413 | static ssize_t oscillator_show(struct device *dev, | ||
414 | struct device_attribute *attr, char *buf) | ||
415 | { | ||
416 | int rc_mode = 0; | ||
417 | struct i2c_client *client = to_i2c_client(dev); | ||
418 | |||
419 | rc_mode = abx80x_is_rc_mode(client); | ||
420 | |||
421 | if (rc_mode < 0) { | ||
422 | dev_err(dev, "Failed to read RTC oscillator selection\n"); | ||
423 | sprintf(buf, "\n"); | ||
424 | return rc_mode; | ||
425 | } | ||
426 | |||
427 | if (rc_mode) | ||
428 | return sprintf(buf, "rc\n"); | ||
429 | else | ||
430 | return sprintf(buf, "xtal\n"); | ||
431 | } | ||
432 | |||
433 | static DEVICE_ATTR_RW(oscillator); | ||
434 | |||
435 | static struct attribute *rtc_calib_attrs[] = { | ||
436 | &dev_attr_autocalibration.attr, | ||
437 | &dev_attr_oscillator.attr, | ||
438 | NULL, | ||
439 | }; | ||
440 | |||
441 | static const struct attribute_group rtc_calib_attr_group = { | ||
442 | .attrs = rtc_calib_attrs, | ||
443 | }; | ||
444 | |||
251 | static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled) | 445 | static int abx80x_alarm_irq_enable(struct device *dev, unsigned int enabled) |
252 | { | 446 | { |
253 | struct i2c_client *client = to_i2c_client(dev); | 447 | struct i2c_client *client = to_i2c_client(dev); |
@@ -303,6 +497,13 @@ static int abx80x_dt_trickle_cfg(struct device_node *np) | |||
303 | return (trickle_cfg | i); | 497 | return (trickle_cfg | i); |
304 | } | 498 | } |
305 | 499 | ||
500 | static void rtc_calib_remove_sysfs_group(void *_dev) | ||
501 | { | ||
502 | struct device *dev = _dev; | ||
503 | |||
504 | sysfs_remove_group(&dev->kobj, &rtc_calib_attr_group); | ||
505 | } | ||
506 | |||
306 | static int abx80x_probe(struct i2c_client *client, | 507 | static int abx80x_probe(struct i2c_client *client, |
307 | const struct i2c_device_id *id) | 508 | const struct i2c_device_id *id) |
308 | { | 509 | { |
@@ -405,6 +606,24 @@ static int abx80x_probe(struct i2c_client *client, | |||
405 | } | 606 | } |
406 | } | 607 | } |
407 | 608 | ||
609 | /* Export sysfs entries */ | ||
610 | err = sysfs_create_group(&(&client->dev)->kobj, &rtc_calib_attr_group); | ||
611 | if (err) { | ||
612 | dev_err(&client->dev, "Failed to create sysfs group: %d\n", | ||
613 | err); | ||
614 | return err; | ||
615 | } | ||
616 | |||
617 | err = devm_add_action(&client->dev, rtc_calib_remove_sysfs_group, | ||
618 | &client->dev); | ||
619 | if (err) { | ||
620 | rtc_calib_remove_sysfs_group(&client->dev); | ||
621 | dev_err(&client->dev, | ||
622 | "Failed to add sysfs cleanup action: %d\n", | ||
623 | err); | ||
624 | return err; | ||
625 | } | ||
626 | |||
408 | return 0; | 627 | return 0; |
409 | } | 628 | } |
410 | 629 | ||