diff options
author | Heiko Schocher <hs@denx.de> | 2015-10-16 07:31:29 -0400 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@free-electrons.com> | 2015-11-08 08:12:29 -0500 |
commit | a39a6405d5f949bc651694028a55d74c514ef1f9 (patch) | |
tree | e4d7958c093e69a71233c55e91cf73a7c51f2239 /drivers/rtc | |
parent | dbb812b141f3bf3dbea75353da799da3d3373d53 (diff) |
rtc: pcf8563: add CLKOUT to common clock framework
Add the clkout output clk to the common clock framework.
Disable the CLKOUT of the RTC after power-up.
After power-up/reset of the RTC, CLKOUT is enabled by default,
with CLKOUT enabled the RTC chip has 2-3 times higher power
consumption.
Signed-off-by: Heiko Schocher <hs@denx.de>
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 170 |
1 files changed, 169 insertions, 1 deletions
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index e569243db57e..c8f95b8e463a 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c | |||
@@ -14,6 +14,7 @@ | |||
14 | * published by the Free Software Foundation. | 14 | * published by the Free Software Foundation. |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/clk-provider.h> | ||
17 | #include <linux/i2c.h> | 18 | #include <linux/i2c.h> |
18 | #include <linux/bcd.h> | 19 | #include <linux/bcd.h> |
19 | #include <linux/rtc.h> | 20 | #include <linux/rtc.h> |
@@ -40,7 +41,14 @@ | |||
40 | 41 | ||
41 | #define PCF8563_REG_AMN 0x09 /* alarm */ | 42 | #define PCF8563_REG_AMN 0x09 /* alarm */ |
42 | 43 | ||
43 | #define PCF8563_REG_CLKO 0x0D /* clock out */ | 44 | #define PCF8563_REG_CLKO 0x0D /* clock out */ |
45 | #define PCF8563_REG_CLKO_FE 0x80 /* clock out enabled */ | ||
46 | #define PCF8563_REG_CLKO_F_MASK 0x03 /* frequenc mask */ | ||
47 | #define PCF8563_REG_CLKO_F_32768HZ 0x00 | ||
48 | #define PCF8563_REG_CLKO_F_1024HZ 0x01 | ||
49 | #define PCF8563_REG_CLKO_F_32HZ 0x02 | ||
50 | #define PCF8563_REG_CLKO_F_1HZ 0x03 | ||
51 | |||
44 | #define PCF8563_REG_TMRC 0x0E /* timer control */ | 52 | #define PCF8563_REG_TMRC 0x0E /* timer control */ |
45 | #define PCF8563_TMRC_ENABLE BIT(7) | 53 | #define PCF8563_TMRC_ENABLE BIT(7) |
46 | #define PCF8563_TMRC_4096 0 | 54 | #define PCF8563_TMRC_4096 0 |
@@ -76,6 +84,9 @@ struct pcf8563 { | |||
76 | int voltage_low; /* incicates if a low_voltage was detected */ | 84 | int voltage_low; /* incicates if a low_voltage was detected */ |
77 | 85 | ||
78 | struct i2c_client *client; | 86 | struct i2c_client *client; |
87 | #ifdef CONFIG_COMMON_CLK | ||
88 | struct clk_hw clkout_hw; | ||
89 | #endif | ||
79 | }; | 90 | }; |
80 | 91 | ||
81 | static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, | 92 | static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, |
@@ -390,6 +401,158 @@ static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) | |||
390 | return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); | 401 | return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); |
391 | } | 402 | } |
392 | 403 | ||
404 | #ifdef CONFIG_COMMON_CLK | ||
405 | /* | ||
406 | * Handling of the clkout | ||
407 | */ | ||
408 | |||
409 | #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw) | ||
410 | |||
411 | static int clkout_rates[] = { | ||
412 | 32768, | ||
413 | 1024, | ||
414 | 32, | ||
415 | 1, | ||
416 | }; | ||
417 | |||
418 | static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, | ||
419 | unsigned long parent_rate) | ||
420 | { | ||
421 | struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); | ||
422 | struct i2c_client *client = pcf8563->client; | ||
423 | unsigned char buf; | ||
424 | int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); | ||
425 | |||
426 | if (ret < 0) | ||
427 | return 0; | ||
428 | |||
429 | buf &= PCF8563_REG_CLKO_F_MASK; | ||
430 | return clkout_rates[ret]; | ||
431 | } | ||
432 | |||
433 | static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate, | ||
434 | unsigned long *prate) | ||
435 | { | ||
436 | int i; | ||
437 | |||
438 | for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) | ||
439 | if (clkout_rates[i] <= rate) | ||
440 | return clkout_rates[i]; | ||
441 | |||
442 | return 0; | ||
443 | } | ||
444 | |||
445 | static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, | ||
446 | unsigned long parent_rate) | ||
447 | { | ||
448 | struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); | ||
449 | struct i2c_client *client = pcf8563->client; | ||
450 | unsigned char buf; | ||
451 | int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); | ||
452 | int i; | ||
453 | |||
454 | if (ret < 0) | ||
455 | return ret; | ||
456 | |||
457 | for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) | ||
458 | if (clkout_rates[i] == rate) { | ||
459 | buf &= ~PCF8563_REG_CLKO_F_MASK; | ||
460 | buf |= i; | ||
461 | ret = pcf8563_write_block_data(client, | ||
462 | PCF8563_REG_CLKO, 1, | ||
463 | &buf); | ||
464 | return ret; | ||
465 | } | ||
466 | |||
467 | return -EINVAL; | ||
468 | } | ||
469 | |||
470 | static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) | ||
471 | { | ||
472 | struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); | ||
473 | struct i2c_client *client = pcf8563->client; | ||
474 | unsigned char buf; | ||
475 | int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); | ||
476 | |||
477 | if (ret < 0) | ||
478 | return ret; | ||
479 | |||
480 | if (enable) | ||
481 | buf |= PCF8563_REG_CLKO_FE; | ||
482 | else | ||
483 | buf &= ~PCF8563_REG_CLKO_FE; | ||
484 | |||
485 | ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); | ||
486 | return ret; | ||
487 | } | ||
488 | |||
489 | static int pcf8563_clkout_prepare(struct clk_hw *hw) | ||
490 | { | ||
491 | return pcf8563_clkout_control(hw, 1); | ||
492 | } | ||
493 | |||
494 | static void pcf8563_clkout_unprepare(struct clk_hw *hw) | ||
495 | { | ||
496 | pcf8563_clkout_control(hw, 0); | ||
497 | } | ||
498 | |||
499 | static int pcf8563_clkout_is_prepared(struct clk_hw *hw) | ||
500 | { | ||
501 | struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); | ||
502 | struct i2c_client *client = pcf8563->client; | ||
503 | unsigned char buf; | ||
504 | int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); | ||
505 | |||
506 | if (ret < 0) | ||
507 | return ret; | ||
508 | |||
509 | return !!(buf & PCF8563_REG_CLKO_FE); | ||
510 | } | ||
511 | |||
512 | static const struct clk_ops pcf8563_clkout_ops = { | ||
513 | .prepare = pcf8563_clkout_prepare, | ||
514 | .unprepare = pcf8563_clkout_unprepare, | ||
515 | .is_prepared = pcf8563_clkout_is_prepared, | ||
516 | .recalc_rate = pcf8563_clkout_recalc_rate, | ||
517 | .round_rate = pcf8563_clkout_round_rate, | ||
518 | .set_rate = pcf8563_clkout_set_rate, | ||
519 | }; | ||
520 | |||
521 | static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) | ||
522 | { | ||
523 | struct i2c_client *client = pcf8563->client; | ||
524 | struct device_node *node = client->dev.of_node; | ||
525 | struct clk *clk; | ||
526 | struct clk_init_data init; | ||
527 | int ret; | ||
528 | unsigned char buf; | ||
529 | |||
530 | /* disable the clkout output */ | ||
531 | buf = 0; | ||
532 | ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); | ||
533 | if (ret < 0) | ||
534 | return ERR_PTR(ret); | ||
535 | |||
536 | init.name = "pcf8563-clkout"; | ||
537 | init.ops = &pcf8563_clkout_ops; | ||
538 | init.flags = CLK_IS_ROOT; | ||
539 | init.parent_names = NULL; | ||
540 | init.num_parents = 0; | ||
541 | pcf8563->clkout_hw.init = &init; | ||
542 | |||
543 | /* optional override of the clockname */ | ||
544 | of_property_read_string(node, "clock-output-names", &init.name); | ||
545 | |||
546 | /* register the clock */ | ||
547 | clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw); | ||
548 | |||
549 | if (!IS_ERR(clk)) | ||
550 | of_clk_add_provider(node, of_clk_src_simple_get, clk); | ||
551 | |||
552 | return clk; | ||
553 | } | ||
554 | #endif | ||
555 | |||
393 | static const struct rtc_class_ops pcf8563_rtc_ops = { | 556 | static const struct rtc_class_ops pcf8563_rtc_ops = { |
394 | .ioctl = pcf8563_rtc_ioctl, | 557 | .ioctl = pcf8563_rtc_ioctl, |
395 | .read_time = pcf8563_rtc_read_time, | 558 | .read_time = pcf8563_rtc_read_time, |
@@ -459,6 +622,11 @@ static int pcf8563_probe(struct i2c_client *client, | |||
459 | 622 | ||
460 | } | 623 | } |
461 | 624 | ||
625 | #ifdef CONFIG_COMMON_CLK | ||
626 | /* register clk in common clk framework */ | ||
627 | pcf8563_clkout_register_clk(pcf8563); | ||
628 | #endif | ||
629 | |||
462 | /* the pcf8563 alarm only supports a minute accuracy */ | 630 | /* the pcf8563 alarm only supports a minute accuracy */ |
463 | pcf8563->rtc->uie_unsupported = 1; | 631 | pcf8563->rtc->uie_unsupported = 1; |
464 | 632 | ||