diff mbox

[v2] rtc: pcf8563: add CLKOUT to common clock framework

Message ID 1444982369-26046-1-git-send-email-hs@denx.de
State Superseded, archived
Headers show

Commit Message

Heiko Schocher Oct. 16, 2015, 7:59 a.m. UTC
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>
---

Changes in v2:
- add comments from Alexandre Belloni
  - remove the DT property, instead
    register for the CLKOUT a clk in the common
    clk framework. The clk is disabled by default.

 Documentation/devicetree/bindings/rtc/pcf8563.txt |  28 ++++
 drivers/rtc/rtc-pcf8563.c                         | 164 +++++++++++++++++++++-
 2 files changed, 191 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/rtc/pcf8563.txt

Comments

kernel test robot Oct. 16, 2015, 10:22 a.m. UTC | #1
Hi Heiko,

[auto build test WARNING on abelloni/rtc-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Heiko-Schocher/rtc-pcf8563-add-CLKOUT-to-common-clock-framework/20151016-160221
config: parisc-allyesconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=parisc 

All warnings (new ones prefixed by >>):

   drivers/rtc/rtc-pcf8563.c:87:17: error: field 'clkout_hw' has incomplete type
     struct clk_hw  clkout_hw;
                    ^
   In file included from arch/parisc/include/asm/bug.h:4:0,
                    from include/linux/bug.h:4,
                    from include/linux/io.h:23,
                    from include/linux/clk-provider.h:14,
                    from drivers/rtc/rtc-pcf8563.c:17:
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_recalc_rate':
   include/linux/kernel.h:811:48: warning: initialization from incompatible pointer type
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:418:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
>> include/linux/kernel.h:811:48: warning: (near initialization for 'pcf8563')
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:418:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_set_rate':
   include/linux/kernel.h:811:48: warning: initialization from incompatible pointer type
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:445:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
>> include/linux/kernel.h:811:48: warning: (near initialization for 'pcf8563')
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:445:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_control':
   include/linux/kernel.h:811:48: warning: initialization from incompatible pointer type
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:469:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
>> include/linux/kernel.h:811:48: warning: (near initialization for 'pcf8563')
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:469:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_is_prepared':
   include/linux/kernel.h:811:48: warning: initialization from incompatible pointer type
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:498:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
>> include/linux/kernel.h:811:48: warning: (near initialization for 'pcf8563')
     const typeof( ((type *)0)->member ) *__mptr = (ptr); \
                                                   ^
   drivers/rtc/rtc-pcf8563.c:406:35: note: in expansion of macro 'container_of'
    #define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
                                      ^
   drivers/rtc/rtc-pcf8563.c:498:28: note: in expansion of macro 'clkout_hw_to_pcf8563'
     struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
                               ^
   drivers/rtc/rtc-pcf8563.c: At top level:
   drivers/rtc/rtc-pcf8563.c:509:21: error: variable 'pcf8563_clkout_ops' has initializer but incomplete type
    static const struct clk_ops pcf8563_clkout_ops = {
                        ^
   drivers/rtc/rtc-pcf8563.c:510:2: error: unknown field 'prepare' specified in initializer
     .prepare = pcf8563_clkout_prepare,
     ^
   drivers/rtc/rtc-pcf8563.c:510:2: warning: excess elements in struct initializer
>> drivers/rtc/rtc-pcf8563.c:510:2: warning: (near initialization for 'pcf8563_clkout_ops')
   drivers/rtc/rtc-pcf8563.c:511:2: error: unknown field 'unprepare' specified in initializer
     .unprepare = pcf8563_clkout_unprepare,
     ^
   drivers/rtc/rtc-pcf8563.c:511:2: warning: excess elements in struct initializer
   drivers/rtc/rtc-pcf8563.c:511:2: warning: (near initialization for 'pcf8563_clkout_ops')
   drivers/rtc/rtc-pcf8563.c:512:2: error: unknown field 'is_prepared' specified in initializer
     .is_prepared = pcf8563_clkout_is_prepared,
     ^
   drivers/rtc/rtc-pcf8563.c:512:2: warning: excess elements in struct initializer
   drivers/rtc/rtc-pcf8563.c:512:2: warning: (near initialization for 'pcf8563_clkout_ops')
   drivers/rtc/rtc-pcf8563.c:513:2: error: unknown field 'recalc_rate' specified in initializer
     .recalc_rate = pcf8563_clkout_recalc_rate,
     ^
   drivers/rtc/rtc-pcf8563.c:513:2: warning: excess elements in struct initializer
   drivers/rtc/rtc-pcf8563.c:513:2: warning: (near initialization for 'pcf8563_clkout_ops')
   drivers/rtc/rtc-pcf8563.c:514:2: error: unknown field 'round_rate' specified in initializer
     .round_rate = pcf8563_clkout_round_rate,
     ^
   drivers/rtc/rtc-pcf8563.c:514:2: warning: excess elements in struct initializer
   drivers/rtc/rtc-pcf8563.c:514:2: warning: (near initialization for 'pcf8563_clkout_ops')
   drivers/rtc/rtc-pcf8563.c:515:2: error: unknown field 'set_rate' specified in initializer
     .set_rate = pcf8563_clkout_set_rate,
     ^
   drivers/rtc/rtc-pcf8563.c:515:2: warning: excess elements in struct initializer
   drivers/rtc/rtc-pcf8563.c:515:2: warning: (near initialization for 'pcf8563_clkout_ops')
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_register_clk':
   drivers/rtc/rtc-pcf8563.c:523:23: error: storage size of 'init' isn't known
     struct clk_init_data init;
                          ^
   drivers/rtc/rtc-pcf8563.c:535:15: error: 'CLK_IS_ROOT' undeclared (first use in this function)
     init.flags = CLK_IS_ROOT;
                  ^
   drivers/rtc/rtc-pcf8563.c:535:15: note: each undeclared identifier is reported only once for each function it appears in
   drivers/rtc/rtc-pcf8563.c:544:2: error: implicit declaration of function 'clk_register' [-Werror=implicit-function-declaration]
     clk = clk_register(&client->dev, &pcf8563->clkout_hw);
     ^
   drivers/rtc/rtc-pcf8563.c:547:3: error: implicit declaration of function 'of_clk_add_provider' [-Werror=implicit-function-declaration]
      of_clk_add_provider(node, of_clk_src_simple_get, clk);
      ^
   drivers/rtc/rtc-pcf8563.c:547:29: error: 'of_clk_src_simple_get' undeclared (first use in this function)
      of_clk_add_provider(node, of_clk_src_simple_get, clk);
                                ^
   drivers/rtc/rtc-pcf8563.c:523:23: warning: unused variable 'init' [-Wunused-variable]
     struct clk_init_data init;
                          ^
   cc1: some warnings being treated as errors

vim +/pcf8563_clkout_ops +510 drivers/rtc/rtc-pcf8563.c

   503		if (ret < 0)
   504			return ret;
   505	
   506		return !!(buf & PCF8563_REG_CLKO_FE);
   507	}
   508	
 > 509	static const struct clk_ops pcf8563_clkout_ops = {
 > 510		.prepare = pcf8563_clkout_prepare,
   511		.unprepare = pcf8563_clkout_unprepare,
   512		.is_prepared = pcf8563_clkout_is_prepared,
   513		.recalc_rate = pcf8563_clkout_recalc_rate,

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Oct. 16, 2015, 10:46 a.m. UTC | #2
Hi Heiko,

[auto build test WARNING on abelloni/rtc-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Heiko-Schocher/rtc-pcf8563-add-CLKOUT-to-common-clock-framework/20151016-160221
config: sh-ap325rxa_defconfig (attached as .config)
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=sh 

All warnings (new ones prefixed by >>):

   drivers/rtc/rtc-pcf8563.c:87:17: error: field 'clkout_hw' has incomplete type
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_recalc_rate':
   drivers/rtc/rtc-pcf8563.c:418:28: warning: initialization from incompatible pointer type [enabled by default]
>> drivers/rtc/rtc-pcf8563.c:418:28: warning: (near initialization for 'pcf8563') [enabled by default]
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_set_rate':
   drivers/rtc/rtc-pcf8563.c:445:28: warning: initialization from incompatible pointer type [enabled by default]
   drivers/rtc/rtc-pcf8563.c:445:28: warning: (near initialization for 'pcf8563') [enabled by default]
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_control':
   drivers/rtc/rtc-pcf8563.c:469:28: warning: initialization from incompatible pointer type [enabled by default]
   drivers/rtc/rtc-pcf8563.c:469:28: warning: (near initialization for 'pcf8563') [enabled by default]
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_is_prepared':
   drivers/rtc/rtc-pcf8563.c:498:28: warning: initialization from incompatible pointer type [enabled by default]
   drivers/rtc/rtc-pcf8563.c:498:28: warning: (near initialization for 'pcf8563') [enabled by default]
   drivers/rtc/rtc-pcf8563.c: At top level:
   drivers/rtc/rtc-pcf8563.c:509:21: error: variable 'pcf8563_clkout_ops' has initializer but incomplete type
   drivers/rtc/rtc-pcf8563.c:510:2: error: unknown field 'prepare' specified in initializer
   drivers/rtc/rtc-pcf8563.c:510:2: warning: excess elements in struct initializer [enabled by default]
   drivers/rtc/rtc-pcf8563.c:510:2: warning: (near initialization for 'pcf8563_clkout_ops') [enabled by default]
   drivers/rtc/rtc-pcf8563.c:511:2: error: unknown field 'unprepare' specified in initializer
   drivers/rtc/rtc-pcf8563.c:511:2: warning: excess elements in struct initializer [enabled by default]
   drivers/rtc/rtc-pcf8563.c:511:2: warning: (near initialization for 'pcf8563_clkout_ops') [enabled by default]
   drivers/rtc/rtc-pcf8563.c:512:2: error: unknown field 'is_prepared' specified in initializer
   drivers/rtc/rtc-pcf8563.c:512:2: warning: excess elements in struct initializer [enabled by default]
   drivers/rtc/rtc-pcf8563.c:512:2: warning: (near initialization for 'pcf8563_clkout_ops') [enabled by default]
   drivers/rtc/rtc-pcf8563.c:513:2: error: unknown field 'recalc_rate' specified in initializer
   drivers/rtc/rtc-pcf8563.c:513:2: warning: excess elements in struct initializer [enabled by default]
   drivers/rtc/rtc-pcf8563.c:513:2: warning: (near initialization for 'pcf8563_clkout_ops') [enabled by default]
   drivers/rtc/rtc-pcf8563.c:514:2: error: unknown field 'round_rate' specified in initializer
   drivers/rtc/rtc-pcf8563.c:514:2: warning: excess elements in struct initializer [enabled by default]
   drivers/rtc/rtc-pcf8563.c:514:2: warning: (near initialization for 'pcf8563_clkout_ops') [enabled by default]
   drivers/rtc/rtc-pcf8563.c:515:2: error: unknown field 'set_rate' specified in initializer
   drivers/rtc/rtc-pcf8563.c:515:2: warning: excess elements in struct initializer [enabled by default]
   drivers/rtc/rtc-pcf8563.c:515:2: warning: (near initialization for 'pcf8563_clkout_ops') [enabled by default]
   drivers/rtc/rtc-pcf8563.c: In function 'pcf8563_clkout_register_clk':
   drivers/rtc/rtc-pcf8563.c:523:23: error: storage size of 'init' isn't known
   drivers/rtc/rtc-pcf8563.c:535:15: error: 'CLK_IS_ROOT' undeclared (first use in this function)
   drivers/rtc/rtc-pcf8563.c:535:15: note: each undeclared identifier is reported only once for each function it appears in
   drivers/rtc/rtc-pcf8563.c:544:2: error: implicit declaration of function 'clk_register' [-Werror=implicit-function-declaration]
   drivers/rtc/rtc-pcf8563.c:547:3: error: implicit declaration of function 'of_clk_add_provider' [-Werror=implicit-function-declaration]
   drivers/rtc/rtc-pcf8563.c:547:29: error: 'of_clk_src_simple_get' undeclared (first use in this function)
   drivers/rtc/rtc-pcf8563.c:523:23: warning: unused variable 'init' [-Wunused-variable]
   cc1: some warnings being treated as errors

vim +/pcf8563 +418 drivers/rtc/rtc-pcf8563.c

   402	/*
   403	 * Handling of the clkout
   404	 */
   405	
   406	#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
   407	
   408	static int clkout_rates[] = {
   409		32768,
   410		1024,
   411		32,
   412		1,
   413	};
   414	
   415	static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
   416							unsigned long parent_rate)
   417	{
 > 418		struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
   419		struct i2c_client *client = pcf8563->client;
   420		unsigned char buf;
   421		int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
   422	
   423		if (ret < 0)
   424			return 0;
   425	
   426		buf &= PCF8563_REG_CLKO_F_MASK;

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Alexandre Belloni Oct. 16, 2015, 10:54 a.m. UTC | #3
Hi,

On 16/10/2015 at 09:59:29 +0200, Heiko Schocher wrote :
> 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>
> ---
> 
> Changes in v2:
> - add comments from Alexandre Belloni
>   - remove the DT property, instead
>     register for the CLKOUT a clk in the common
>     clk framework. The clk is disabled by default.
> 
>  Documentation/devicetree/bindings/rtc/pcf8563.txt |  28 ++++
>  drivers/rtc/rtc-pcf8563.c                         | 164 +++++++++++++++++++++-
>  2 files changed, 191 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/rtc/pcf8563.txt
> 
> diff --git a/Documentation/devicetree/bindings/rtc/pcf8563.txt b/Documentation/devicetree/bindings/rtc/pcf8563.txt
> new file mode 100644
> index 0000000..cdb81ef
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/rtc/pcf8563.txt
> @@ -0,0 +1,28 @@
> +* Philips PCF8563/Epson RTC8564 Real Time Clock
> +
> +Philips PCF8563/Epson RTC8564 Real Time Clock
> +
> +Required properties:
> +see: Documentation/devicetree/bindings/i2c/trivial-devices.txt
> +
> +Optional property:
> +- #clock-cells: Should be 1.
> +- clocks: Reference to the clock entry.
> +- clock-output-names:
> +  overwrite the default clock name "pcf8563-clkout"
> +
> +Example:
> +
> +rtcclk: rtcclk {
> +	compatible = "fixed-clock";
> +	#clock-cells = <1>;
> +	clock-frequency = <1>;
> +	clock-output-names = "rtcclk";
> +};
> +
> +pcf8563@51 {
> +	compatible = "nxp,pcf8563";
> +	reg = <0x51>;
> +	#clock-cells = <1>;
> +	clocks = <&rtcclk 0>;
> +};

You don't seem to be using rtcclk, I would remove it from the example.

> +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
> +{
> +	struct i2c_client *client = pcf8563->client;
> +	struct device_node *node = client->dev.of_node;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +	int ret;
> +	unsigned char buf;
> +
> +	/* disable the clkout output */
> +	buf = 0;
> +	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
> +	if (ret < 0)
> +		return ERR_PTR(ret);
> +

Isn't that done automatically for an unused clock? However, I would do
that in the probe as you will have to sprinkle some #ifdef
CONFIG_COMMON_CLK after seeing the kbuild report.

>  
> +	/* register clk in common clk framework */
> +	pcf8563_clkout_register_clk(pcf8563);
> +
>  	/* the pcf8563 alarm only supports a minute accuracy */
>  	pcf8563->rtc->uie_unsupported = 1;
>  

You probably need to unregister the clock at some point. Maybe using
devm_clk_register() is the best thing to do.
Alexandre Belloni Oct. 16, 2015, 11 a.m. UTC | #4
On 16/10/2015 at 12:54:46 +0200, Alexandre Belloni wrote :
> > +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
> > +{
> > +	struct i2c_client *client = pcf8563->client;
> > +	struct device_node *node = client->dev.of_node;
> > +	struct clk *clk;
> > +	struct clk_init_data init;
> > +	int ret;
> > +	unsigned char buf;
> > +
> > +	/* disable the clkout output */
> > +	buf = 0;
> > +	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
> > +	if (ret < 0)
> > +		return ERR_PTR(ret);
> > +
> 
> Isn't that done automatically for an unused clock? However, I would do
> that in the probe as you will have to sprinkle some #ifdef
> CONFIG_COMMON_CLK after seeing the kbuild report.

I'm rethinking about your comment about breaking existing boards. I'd
say that probably nobody is using the output from the rtc but maybe
(highly unlikely) some are configuring it from the bootloader and in
that case it is not nice to undo that configuration. Let's keep that
block here as long as it works fine for you.
Heiko Schocher Oct. 16, 2015, 11:31 a.m. UTC | #5
Hello Alexandre,

Am 16.10.2015 um 13:00 schrieb Alexandre Belloni:
> On 16/10/2015 at 12:54:46 +0200, Alexandre Belloni wrote :
>>> +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
>>> +{
>>> +	struct i2c_client *client = pcf8563->client;
>>> +	struct device_node *node = client->dev.of_node;
>>> +	struct clk *clk;
>>> +	struct clk_init_data init;
>>> +	int ret;
>>> +	unsigned char buf;
>>> +
>>> +	/* disable the clkout output */
>>> +	buf = 0;
>>> +	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
>>> +	if (ret < 0)
>>> +		return ERR_PTR(ret);
>>> +
>>
>> Isn't that done automatically for an unused clock? However, I would do
>> that in the probe as you will have to sprinkle some #ifdef
>> CONFIG_COMMON_CLK after seeing the kbuild report.
>
> I'm rethinking about your comment about breaking existing boards. I'd
> say that probably nobody is using the output from the rtc but maybe
> (highly unlikely) some are configuring it from the bootloader and in
> that case it is not nice to undo that configuration. Let's keep that
> block here as long as it works fine for you.

Ok, so I have also a better feeling with it ... removing it back ;-)

bye,
Heiko
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/rtc/pcf8563.txt b/Documentation/devicetree/bindings/rtc/pcf8563.txt
new file mode 100644
index 0000000..cdb81ef
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/pcf8563.txt
@@ -0,0 +1,28 @@ 
+* Philips PCF8563/Epson RTC8564 Real Time Clock
+
+Philips PCF8563/Epson RTC8564 Real Time Clock
+
+Required properties:
+see: Documentation/devicetree/bindings/i2c/trivial-devices.txt
+
+Optional property:
+- #clock-cells: Should be 1.
+- clocks: Reference to the clock entry.
+- clock-output-names:
+  overwrite the default clock name "pcf8563-clkout"
+
+Example:
+
+rtcclk: rtcclk {
+	compatible = "fixed-clock";
+	#clock-cells = <1>;
+	clock-frequency = <1>;
+	clock-output-names = "rtcclk";
+};
+
+pcf8563@51 {
+	compatible = "nxp,pcf8563";
+	reg = <0x51>;
+	#clock-cells = <1>;
+	clocks = <&rtcclk 0>;
+};
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index e569243..0c2f85f 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -14,6 +14,7 @@ 
  * published by the Free Software Foundation.
  */
 
+#include <linux/clk-provider.h>
 #include <linux/i2c.h>
 #include <linux/bcd.h>
 #include <linux/rtc.h>
@@ -40,7 +41,14 @@ 
 
 #define PCF8563_REG_AMN		0x09 /* alarm */
 
-#define PCF8563_REG_CLKO	0x0D /* clock out */
+#define PCF8563_REG_CLKO		0x0D /* clock out */
+#define PCF8563_REG_CLKO_FE		0x80 /* clock out enabled */
+#define PCF8563_REG_CLKO_F_MASK		0x03 /* frequenc mask */
+#define PCF8563_REG_CLKO_F_32768HZ	0x00
+#define PCF8563_REG_CLKO_F_1024HZ	0x01
+#define PCF8563_REG_CLKO_F_32HZ		0x02
+#define PCF8563_REG_CLKO_F_1HZ		0x03
+
 #define PCF8563_REG_TMRC	0x0E /* timer control */
 #define PCF8563_TMRC_ENABLE	BIT(7)
 #define PCF8563_TMRC_4096	0
@@ -76,6 +84,7 @@  struct pcf8563 {
 	int voltage_low; /* incicates if a low_voltage was detected */
 
 	struct i2c_client *client;
+	struct clk_hw		clkout_hw;
 };
 
 static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
@@ -390,6 +399,156 @@  static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
 	return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
 }
 
+/*
+ * Handling of the clkout
+ */
+
+#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw)
+
+static int clkout_rates[] = {
+	32768,
+	1024,
+	32,
+	1,
+};
+
+static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
+	struct i2c_client *client = pcf8563->client;
+	unsigned char buf;
+	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
+
+	if (ret < 0)
+		return 0;
+
+	buf &= PCF8563_REG_CLKO_F_MASK;
+	return clkout_rates[ret];
+}
+
+static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long *prate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
+		if (clkout_rates[i] <= rate)
+			return clkout_rates[i];
+
+	return 0;
+}
+
+static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
+	struct i2c_client *client = pcf8563->client;
+	unsigned char buf;
+	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
+	int i;
+
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)
+		if (clkout_rates[i] == rate) {
+			buf &= ~PCF8563_REG_CLKO_F_MASK;
+			buf |= i;
+			ret = pcf8563_write_block_data(client,
+						       PCF8563_REG_CLKO, 1,
+						       &buf);
+			return ret;
+		}
+
+	return -EINVAL;
+}
+
+static int pcf8563_clkout_control(struct clk_hw *hw, bool enable)
+{
+	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
+	struct i2c_client *client = pcf8563->client;
+	unsigned char buf;
+	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
+
+	if (ret < 0)
+		return ret;
+
+	if (enable)
+		buf |= PCF8563_REG_CLKO_FE;
+	else
+		buf &= ~PCF8563_REG_CLKO_FE;
+
+	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
+	return ret;
+}
+
+static int pcf8563_clkout_prepare(struct clk_hw *hw)
+{
+	return pcf8563_clkout_control(hw, 1);
+}
+
+static void pcf8563_clkout_unprepare(struct clk_hw *hw)
+{
+	pcf8563_clkout_control(hw, 0);
+}
+
+static int pcf8563_clkout_is_prepared(struct clk_hw *hw)
+{
+	struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw);
+	struct i2c_client *client = pcf8563->client;
+	unsigned char buf;
+	int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf);
+
+	if (ret < 0)
+		return ret;
+
+	return !!(buf & PCF8563_REG_CLKO_FE);
+}
+
+static const struct clk_ops pcf8563_clkout_ops = {
+	.prepare = pcf8563_clkout_prepare,
+	.unprepare = pcf8563_clkout_unprepare,
+	.is_prepared = pcf8563_clkout_is_prepared,
+	.recalc_rate = pcf8563_clkout_recalc_rate,
+	.round_rate = pcf8563_clkout_round_rate,
+	.set_rate = pcf8563_clkout_set_rate,
+};
+
+static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
+{
+	struct i2c_client *client = pcf8563->client;
+	struct device_node *node = client->dev.of_node;
+	struct clk *clk;
+	struct clk_init_data init;
+	int ret;
+	unsigned char buf;
+
+	/* disable the clkout output */
+	buf = 0;
+	ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	init.name = "pcf8563-clkout";
+	init.ops = &pcf8563_clkout_ops;
+	init.flags = CLK_IS_ROOT;
+	init.parent_names = NULL;
+	init.num_parents = 0;
+	pcf8563->clkout_hw.init = &init;
+
+	/* optional override of the clockname */
+	of_property_read_string(node, "clock-output-names", &init.name);
+
+	/* register the clock */
+	clk = clk_register(&client->dev, &pcf8563->clkout_hw);
+
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+	return clk;
+}
+
 static const struct rtc_class_ops pcf8563_rtc_ops = {
 	.ioctl		= pcf8563_rtc_ioctl,
 	.read_time	= pcf8563_rtc_read_time,
@@ -459,6 +618,9 @@  static int pcf8563_probe(struct i2c_client *client,
 
 	}
 
+	/* register clk in common clk framework */
+	pcf8563_clkout_register_clk(pcf8563);
+
 	/* the pcf8563 alarm only supports a minute accuracy */
 	pcf8563->rtc->uie_unsupported = 1;