Skip to content

Commit a39a640

Browse files
hsdenxalexandrebelloni
authored andcommitted
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 <[email protected]> Signed-off-by: Alexandre Belloni <[email protected]>
1 parent dbb812b commit a39a640

File tree

2 files changed

+194
-1
lines changed

2 files changed

+194
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
* Philips PCF8563/Epson RTC8564 Real Time Clock
2+
3+
Philips PCF8563/Epson RTC8564 Real Time Clock
4+
5+
Required properties:
6+
see: Documentation/devicetree/bindings/i2c/trivial-devices.txt
7+
8+
Optional property:
9+
- #clock-cells: Should be 0.
10+
- clock-output-names:
11+
overwrite the default clock name "pcf8563-clkout"
12+
13+
Example:
14+
15+
pcf8563: pcf8563@51 {
16+
compatible = "nxp,pcf8563";
17+
reg = <0x51>;
18+
#clock-cells = <0>;
19+
};
20+
21+
device {
22+
...
23+
clocks = <&pcf8563>;
24+
...
25+
};

drivers/rtc/rtc-pcf8563.c

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* published by the Free Software Foundation.
1515
*/
1616

17+
#include <linux/clk-provider.h>
1718
#include <linux/i2c.h>
1819
#include <linux/bcd.h>
1920
#include <linux/rtc.h>
@@ -40,7 +41,14 @@
4041

4142
#define PCF8563_REG_AMN 0x09 /* alarm */
4243

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+
4452
#define PCF8563_REG_TMRC 0x0E /* timer control */
4553
#define PCF8563_TMRC_ENABLE BIT(7)
4654
#define PCF8563_TMRC_4096 0
@@ -76,6 +84,9 @@ struct pcf8563 {
7684
int voltage_low; /* incicates if a low_voltage was detected */
7785

7886
struct i2c_client *client;
87+
#ifdef CONFIG_COMMON_CLK
88+
struct clk_hw clkout_hw;
89+
#endif
7990
};
8091

8192
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)
390401
return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
391402
}
392403

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+
393556
static const struct rtc_class_ops pcf8563_rtc_ops = {
394557
.ioctl = pcf8563_rtc_ioctl,
395558
.read_time = pcf8563_rtc_read_time,
@@ -459,6 +622,11 @@ static int pcf8563_probe(struct i2c_client *client,
459622

460623
}
461624

625+
#ifdef CONFIG_COMMON_CLK
626+
/* register clk in common clk framework */
627+
pcf8563_clkout_register_clk(pcf8563);
628+
#endif
629+
462630
/* the pcf8563 alarm only supports a minute accuracy */
463631
pcf8563->rtc->uie_unsupported = 1;
464632

0 commit comments

Comments
 (0)