clk: add clock driver for SCMI agents
This change introduces a clock driver for SCMI agent devices. When SCMI agent and SCMI clock drivers are enabled, SCMI agent binds a clock device for each SCMI clock protocol devices enabled in the FDT. SCMI clock driver is embedded upon CONFIG_CLK_SCMI=y. If enabled, CONFIG_SCMI_AGENT is also enabled. SCMI Clock protocol is defined in the SCMI specification [1]. Links: [1] https://developer.arm.com/architectures/system-architectures/software-standards/scmi Signed-off-by: Etienne Carriere <etienne.carriere@linaro.org> Cc: Lukasz Majewski <lukma@denx.de> Cc: Simon Glass <sjg@chromium.org> Cc: Peng Fan <peng.fan@nxp.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Reviewed-by: Simon Glass <sjg@chromium.org>
This commit is contained in:
		
							parent
							
								
									4e5ce7ecd3
								
							
						
					
					
						commit
						6038884483
					
				|  | @ -159,6 +159,14 @@ config CLK_CDCE9XX | ||||||
| 	   Enable the clock synthesizer driver for CDCE913/925/937/949 | 	   Enable the clock synthesizer driver for CDCE913/925/937/949 | ||||||
| 	   series of chips. | 	   series of chips. | ||||||
| 
 | 
 | ||||||
|  | config CLK_SCMI | ||||||
|  | 	bool "Enable SCMI clock driver" | ||||||
|  | 	depends on SCMI_FIRMWARE | ||||||
|  | 	help | ||||||
|  | 	  Enable this option if you want to support clock devices exposed | ||||||
|  | 	  by a SCMI agent based on SCMI clock protocol communication | ||||||
|  | 	  with a SCMI server. | ||||||
|  | 
 | ||||||
| source "drivers/clk/analogbits/Kconfig" | source "drivers/clk/analogbits/Kconfig" | ||||||
| source "drivers/clk/at91/Kconfig" | source "drivers/clk/at91/Kconfig" | ||||||
| source "drivers/clk/exynos/Kconfig" | source "drivers/clk/exynos/Kconfig" | ||||||
|  |  | ||||||
|  | @ -32,6 +32,7 @@ obj-$(CONFIG_CLK_MPC83XX) += mpc83xx_clk.o | ||||||
| obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o | obj-$(CONFIG_CLK_OCTEON) += clk_octeon.o | ||||||
| obj-$(CONFIG_CLK_OWL) += owl/ | obj-$(CONFIG_CLK_OWL) += owl/ | ||||||
| obj-$(CONFIG_CLK_RENESAS) += renesas/ | obj-$(CONFIG_CLK_RENESAS) += renesas/ | ||||||
|  | obj-$(CONFIG_CLK_SCMI) += clk_scmi.o | ||||||
| obj-$(CONFIG_CLK_SIFIVE) += sifive/ | obj-$(CONFIG_CLK_SIFIVE) += sifive/ | ||||||
| obj-$(CONFIG_ARCH_SUNXI) += sunxi/ | obj-$(CONFIG_ARCH_SUNXI) += sunxi/ | ||||||
| obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o | obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o | ||||||
|  |  | ||||||
|  | @ -0,0 +1,99 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0+
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (C) 2019-2020 Linaro Limited | ||||||
|  |  */ | ||||||
|  | #include <common.h> | ||||||
|  | #include <clk-uclass.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <scmi_agent.h> | ||||||
|  | #include <scmi_protocols.h> | ||||||
|  | #include <asm/types.h> | ||||||
|  | 
 | ||||||
|  | static int scmi_clk_gate(struct clk *clk, int enable) | ||||||
|  | { | ||||||
|  | 	struct scmi_clk_state_in in = { | ||||||
|  | 		.clock_id = clk->id, | ||||||
|  | 		.attributes = enable, | ||||||
|  | 	}; | ||||||
|  | 	struct scmi_clk_state_out out; | ||||||
|  | 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, | ||||||
|  | 					  SCMI_CLOCK_CONFIG_SET, | ||||||
|  | 					  in, out); | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = devm_scmi_process_msg(clk->dev->parent, &msg); | ||||||
|  | 	if (ret) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return scmi_to_linux_errno(out.status); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int scmi_clk_enable(struct clk *clk) | ||||||
|  | { | ||||||
|  | 	return scmi_clk_gate(clk, 1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int scmi_clk_disable(struct clk *clk) | ||||||
|  | { | ||||||
|  | 	return scmi_clk_gate(clk, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ulong scmi_clk_get_rate(struct clk *clk) | ||||||
|  | { | ||||||
|  | 	struct scmi_clk_rate_get_in in = { | ||||||
|  | 		.clock_id = clk->id, | ||||||
|  | 	}; | ||||||
|  | 	struct scmi_clk_rate_get_out out; | ||||||
|  | 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, | ||||||
|  | 					  SCMI_CLOCK_RATE_GET, | ||||||
|  | 					  in, out); | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = devm_scmi_process_msg(clk->dev->parent, &msg); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	ret = scmi_to_linux_errno(out.status); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static ulong scmi_clk_set_rate(struct clk *clk, ulong rate) | ||||||
|  | { | ||||||
|  | 	struct scmi_clk_rate_set_in in = { | ||||||
|  | 		.clock_id = clk->id, | ||||||
|  | 		.flags = SCMI_CLK_RATE_ROUND_CLOSEST, | ||||||
|  | 		.rate_lsb = (u32)rate, | ||||||
|  | 		.rate_msb = (u32)((u64)rate >> 32), | ||||||
|  | 	}; | ||||||
|  | 	struct scmi_clk_rate_set_out out; | ||||||
|  | 	struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, | ||||||
|  | 					  SCMI_CLOCK_RATE_SET, | ||||||
|  | 					  in, out); | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	ret = devm_scmi_process_msg(clk->dev->parent, &msg); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	ret = scmi_to_linux_errno(out.status); | ||||||
|  | 	if (ret < 0) | ||||||
|  | 		return ret; | ||||||
|  | 
 | ||||||
|  | 	return scmi_clk_get_rate(clk); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct clk_ops scmi_clk_ops = { | ||||||
|  | 	.enable = scmi_clk_enable, | ||||||
|  | 	.disable = scmi_clk_disable, | ||||||
|  | 	.get_rate = scmi_clk_get_rate, | ||||||
|  | 	.set_rate = scmi_clk_set_rate, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(scmi_clock) = { | ||||||
|  | 	.name = "scmi_clk", | ||||||
|  | 	.id = UCLASS_CLK, | ||||||
|  | 	.ops = &scmi_clk_ops, | ||||||
|  | }; | ||||||
|  | @ -58,9 +58,9 @@ static int scmi_bind_protocols(struct udevice *dev) | ||||||
| { | { | ||||||
| 	int ret = 0; | 	int ret = 0; | ||||||
| 	ofnode node; | 	ofnode node; | ||||||
| 	struct driver *drv; |  | ||||||
| 
 | 
 | ||||||
| 	dev_for_each_subnode(node, dev) { | 	dev_for_each_subnode(node, dev) { | ||||||
|  | 		struct driver *drv = NULL; | ||||||
| 		u32 protocol_id; | 		u32 protocol_id; | ||||||
| 
 | 
 | ||||||
| 		if (!ofnode_is_available(node)) | 		if (!ofnode_is_available(node)) | ||||||
|  | @ -70,8 +70,16 @@ static int scmi_bind_protocols(struct udevice *dev) | ||||||
| 			continue; | 			continue; | ||||||
| 
 | 
 | ||||||
| 		switch (protocol_id) { | 		switch (protocol_id) { | ||||||
|  | 		case SCMI_PROTOCOL_ID_CLOCK: | ||||||
|  | 			if (IS_ENABLED(CONFIG_CLK_SCMI)) | ||||||
|  | 				drv = DM_GET_DRIVER(scmi_clock); | ||||||
|  | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			dev_info(dev, "Ignore unsupported SCMI protocol %#x\n", | 			break; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (!drv) { | ||||||
|  | 			dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n", | ||||||
| 				protocol_id); | 				protocol_id); | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
| #define _SCMI_PROTOCOLS_H | #define _SCMI_PROTOCOLS_H | ||||||
| 
 | 
 | ||||||
| #include <linux/bitops.h> | #include <linux/bitops.h> | ||||||
|  | #include <asm/types.h> | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  * Subset the SCMI protocols definition |  * Subset the SCMI protocols definition | ||||||
|  | @ -38,4 +39,81 @@ enum scmi_status_code { | ||||||
| 	SCMI_PROTOCOL_ERROR = -10, | 	SCMI_PROTOCOL_ERROR = -10, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /*
 | ||||||
|  |  * SCMI Clock Protocol | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | enum scmi_clock_message_id { | ||||||
|  | 	SCMI_CLOCK_RATE_SET = 0x5, | ||||||
|  | 	SCMI_CLOCK_RATE_GET = 0x6, | ||||||
|  | 	SCMI_CLOCK_CONFIG_SET = 0x7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #define SCMI_CLK_RATE_ASYNC_NOTIFY	BIT(0) | ||||||
|  | #define SCMI_CLK_RATE_ASYNC_NORESP	(BIT(0) | BIT(1)) | ||||||
|  | #define SCMI_CLK_RATE_ROUND_DOWN	0 | ||||||
|  | #define SCMI_CLK_RATE_ROUND_UP		BIT(2) | ||||||
|  | #define SCMI_CLK_RATE_ROUND_CLOSEST	BIT(3) | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct scmi_clk_state_in - Message payload for CLOCK_CONFIG_SET command | ||||||
|  |  * @clock_id:	SCMI clock ID | ||||||
|  |  * @attributes:	Attributes of the targets clock state | ||||||
|  |  */ | ||||||
|  | struct scmi_clk_state_in { | ||||||
|  | 	u32 clock_id; | ||||||
|  | 	u32 attributes; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct scmi_clk_state_out - Response payload for CLOCK_CONFIG_SET command | ||||||
|  |  * @status:	SCMI command status | ||||||
|  |  */ | ||||||
|  | struct scmi_clk_state_out { | ||||||
|  | 	s32 status; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct scmi_clk_state_in - Message payload for CLOCK_RATE_GET command | ||||||
|  |  * @clock_id:	SCMI clock ID | ||||||
|  |  * @attributes:	Attributes of the targets clock state | ||||||
|  |  */ | ||||||
|  | struct scmi_clk_rate_get_in { | ||||||
|  | 	u32 clock_id; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct scmi_clk_rate_get_out - Response payload for CLOCK_RATE_GET command | ||||||
|  |  * @status:	SCMI command status | ||||||
|  |  * @rate_lsb:	32bit LSB of the clock rate in Hertz | ||||||
|  |  * @rate_msb:	32bit MSB of the clock rate in Hertz | ||||||
|  |  */ | ||||||
|  | struct scmi_clk_rate_get_out { | ||||||
|  | 	s32 status; | ||||||
|  | 	u32 rate_lsb; | ||||||
|  | 	u32 rate_msb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct scmi_clk_state_in - Message payload for CLOCK_RATE_SET command | ||||||
|  |  * @clock_id:	SCMI clock ID | ||||||
|  |  * @flags:	Flags for the clock rate set request | ||||||
|  |  * @rate_lsb:	32bit LSB of the clock rate in Hertz | ||||||
|  |  * @rate_msb:	32bit MSB of the clock rate in Hertz | ||||||
|  |  */ | ||||||
|  | struct scmi_clk_rate_set_in { | ||||||
|  | 	u32 clock_id; | ||||||
|  | 	u32 flags; | ||||||
|  | 	u32 rate_lsb; | ||||||
|  | 	u32 rate_msb; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * struct scmi_clk_rate_set_out - Response payload for CLOCK_RATE_SET command | ||||||
|  |  * @status:	SCMI command status | ||||||
|  |  */ | ||||||
|  | struct scmi_clk_rate_set_out { | ||||||
|  | 	s32 status; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| #endif /* _SCMI_PROTOCOLS_H */ | #endif /* _SCMI_PROTOCOLS_H */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue