forked from mirrors/linux
		
	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>
This commit is contained in:
		
							parent
							
								
									dbb812b141
								
							
						
					
					
						commit
						a39a6405d5
					
				
					 2 changed files with 194 additions and 1 deletions
				
			
		
							
								
								
									
										25
									
								
								Documentation/devicetree/bindings/rtc/pcf8563.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								Documentation/devicetree/bindings/rtc/pcf8563.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					* 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 0.
 | 
				
			||||||
 | 
					- clock-output-names:
 | 
				
			||||||
 | 
					  overwrite the default clock name "pcf8563-clkout"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pcf8563: pcf8563@51 {
 | 
				
			||||||
 | 
						compatible = "nxp,pcf8563";
 | 
				
			||||||
 | 
						reg = <0x51>;
 | 
				
			||||||
 | 
						#clock-cells = <0>;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					device {
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
						clocks = <&pcf8563>;
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,7 @@
 | 
				
			||||||
 * published by the Free Software Foundation.
 | 
					 * published by the Free Software Foundation.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <linux/clk-provider.h>
 | 
				
			||||||
#include <linux/i2c.h>
 | 
					#include <linux/i2c.h>
 | 
				
			||||||
#include <linux/bcd.h>
 | 
					#include <linux/bcd.h>
 | 
				
			||||||
#include <linux/rtc.h>
 | 
					#include <linux/rtc.h>
 | 
				
			||||||
| 
						 | 
					@ -40,7 +41,14 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PCF8563_REG_AMN		0x09 /* alarm */
 | 
					#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_REG_TMRC	0x0E /* timer control */
 | 
				
			||||||
#define PCF8563_TMRC_ENABLE	BIT(7)
 | 
					#define PCF8563_TMRC_ENABLE	BIT(7)
 | 
				
			||||||
#define PCF8563_TMRC_4096	0
 | 
					#define PCF8563_TMRC_4096	0
 | 
				
			||||||
| 
						 | 
					@ -76,6 +84,9 @@ struct pcf8563 {
 | 
				
			||||||
	int voltage_low; /* incicates if a low_voltage was detected */
 | 
						int voltage_low; /* incicates if a low_voltage was detected */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct i2c_client *client;
 | 
						struct i2c_client *client;
 | 
				
			||||||
 | 
					#ifdef CONFIG_COMMON_CLK
 | 
				
			||||||
 | 
						struct clk_hw		clkout_hw;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg,
 | 
					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)
 | 
				
			||||||
	return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
 | 
						return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_COMMON_CLK
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 = devm_clk_register(&client->dev, &pcf8563->clkout_hw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!IS_ERR(clk))
 | 
				
			||||||
 | 
							of_clk_add_provider(node, of_clk_src_simple_get, clk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return clk;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const struct rtc_class_ops pcf8563_rtc_ops = {
 | 
					static const struct rtc_class_ops pcf8563_rtc_ops = {
 | 
				
			||||||
	.ioctl		= pcf8563_rtc_ioctl,
 | 
						.ioctl		= pcf8563_rtc_ioctl,
 | 
				
			||||||
	.read_time	= pcf8563_rtc_read_time,
 | 
						.read_time	= pcf8563_rtc_read_time,
 | 
				
			||||||
| 
						 | 
					@ -459,6 +622,11 @@ static int pcf8563_probe(struct i2c_client *client,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef CONFIG_COMMON_CLK
 | 
				
			||||||
 | 
						/* register clk in common clk framework */
 | 
				
			||||||
 | 
						pcf8563_clkout_register_clk(pcf8563);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* the pcf8563 alarm only supports a minute accuracy */
 | 
						/* the pcf8563 alarm only supports a minute accuracy */
 | 
				
			||||||
	pcf8563->rtc->uie_unsupported = 1;
 | 
						pcf8563->rtc->uie_unsupported = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue