power: zynqmp: Add power domain driver for ZynqMP
Driver should be enabled by CONFIG_POWER_DOMAIN=y and CONFIG_ZYNQMP_POWER_DOMAIN=y. Power domain driver doesn't have own DT node but it uses zynqmp firmware DT node that's why there is a need to bind driver when firmware node is found. Driver itself is simple. It is sending pmufw config object overlay for enabling access to device which is done in ...domain_request(). In ...domain_on() capabilities are passed and node is requested. This should be bare minimum of required to get power domain driver working. Signed-off-by: Michal Simek <michal.simek@xilinx.com> Reviewed-by: Jaehoon Chung <jh80.chung@samsung.com> Link: https://lore.kernel.org/r/f4b9433b91c0b18c375b061c7a4e29d428f70547.1644226055.git.michal.simek@xilinx.com
This commit is contained in:
		
							parent
							
								
									2f9dd4bfc7
								
							
						
					
					
						commit
						e0283cbdfd
					
				|  | @ -634,6 +634,7 @@ F:	drivers/mtd/nand/raw/zynq_nand.c | ||||||
| F:	drivers/net/phy/xilinx_phy.c | F:	drivers/net/phy/xilinx_phy.c | ||||||
| F:	drivers/net/zynq_gem.c | F:	drivers/net/zynq_gem.c | ||||||
| F:	drivers/phy/phy-zynqmp.c | F:	drivers/phy/phy-zynqmp.c | ||||||
|  | F:	drivers/power/domain/zynqmp-power-domain.c | ||||||
| F:	drivers/serial/serial_zynq.c | F:	drivers/serial/serial_zynq.c | ||||||
| F:	drivers/reset/reset-zynqmp.c | F:	drivers/reset/reset-zynqmp.c | ||||||
| F:	drivers/rtc/zynqmp_rtc.c | F:	drivers/rtc/zynqmp_rtc.c | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ | ||||||
| #include <common.h> | #include <common.h> | ||||||
| #include <cpu_func.h> | #include <cpu_func.h> | ||||||
| #include <dm.h> | #include <dm.h> | ||||||
|  | #include <dm/lists.h> | ||||||
| #include <log.h> | #include <log.h> | ||||||
| #include <zynqmp_firmware.h> | #include <zynqmp_firmware.h> | ||||||
| #include <asm/cache.h> | #include <asm/cache.h> | ||||||
|  | @ -226,8 +227,27 @@ static const struct udevice_id zynqmp_firmware_ids[] = { | ||||||
| 	{ } | 	{ } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | static int zynqmp_firmware_bind(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	int ret; | ||||||
|  | 	struct udevice *child; | ||||||
|  | 
 | ||||||
|  | 	if (IS_ENABLED(CONFIG_ZYNQMP_POWER_DOMAIN)) { | ||||||
|  | 		ret = device_bind_driver_to_node(dev, "zynqmp_power_domain", | ||||||
|  | 						 "zynqmp_power_domain", | ||||||
|  | 						 dev_ofnode(dev), &child); | ||||||
|  | 		if (ret) { | ||||||
|  | 			printf("zynqmp power domain driver is not bound: %d\n", ret); | ||||||
|  | 			return ret; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return dm_scan_fdt_dev(dev); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| U_BOOT_DRIVER(zynqmp_firmware) = { | U_BOOT_DRIVER(zynqmp_firmware) = { | ||||||
| 	.id = UCLASS_FIRMWARE, | 	.id = UCLASS_FIRMWARE, | ||||||
| 	.name = "zynqmp_firmware", | 	.name = "zynqmp_firmware", | ||||||
| 	.of_match = zynqmp_firmware_ids, | 	.of_match = zynqmp_firmware_ids, | ||||||
|  | 	.bind = zynqmp_firmware_bind, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -88,4 +88,13 @@ config TI_POWER_DOMAIN | ||||||
| 	help | 	help | ||||||
| 	  Generic power domain implementation for TI K3 devices. | 	  Generic power domain implementation for TI K3 devices. | ||||||
| 
 | 
 | ||||||
|  | config ZYNQMP_POWER_DOMAIN | ||||||
|  | 	bool "Enable the Xilinx ZynqMP Power domain driver" | ||||||
|  | 	depends on POWER_DOMAIN && ZYNQMP_FIRMWARE | ||||||
|  | 	help | ||||||
|  | 	  Generic power domain implementation for Xilinx ZynqMP devices. | ||||||
|  | 	  The driver should be enabled when system starts in very minimal | ||||||
|  | 	  configuration and it is extended at run time. Then enabling | ||||||
|  | 	  the driver will ensure that PMUFW enable access to requested IP. | ||||||
|  | 
 | ||||||
| endmenu | endmenu | ||||||
|  |  | ||||||
|  | @ -16,3 +16,4 @@ obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o | ||||||
| obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o | obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o | ||||||
| obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o | obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o | ||||||
| obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o | obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o | ||||||
|  | obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o | ||||||
|  |  | ||||||
|  | @ -0,0 +1,89 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0
 | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2021, Xilinx. Inc. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <common.h> | ||||||
|  | #include <dm.h> | ||||||
|  | #include <log.h> | ||||||
|  | #include <malloc.h> | ||||||
|  | #include <misc.h> | ||||||
|  | #include <power-domain-uclass.h> | ||||||
|  | #include <linux/bitops.h> | ||||||
|  | 
 | ||||||
|  | #include <zynqmp_firmware.h> | ||||||
|  | 
 | ||||||
|  | #define NODE_ID_LOCATION	5 | ||||||
|  | 
 | ||||||
|  | static unsigned int xpm_configobject[] = { | ||||||
|  | 	/* HEADER */ | ||||||
|  | 	2,	/* Number of remaining words in the header */ | ||||||
|  | 	1,	/* Number of sections included in config object */ | ||||||
|  | 	PM_CONFIG_OBJECT_TYPE_OVERLAY,	/* Type of Config object as overlay */ | ||||||
|  | 	/* SLAVE SECTION */ | ||||||
|  | 
 | ||||||
|  | 	PM_CONFIG_SLAVE_SECTION_ID,	/* Section ID */ | ||||||
|  | 	1,				/* Number of slaves */ | ||||||
|  | 
 | ||||||
|  | 	0, /* Node ID which will be changed below */ | ||||||
|  | 	PM_SLAVE_FLAG_IS_SHAREABLE, | ||||||
|  | 	PM_CONFIG_IPI_PSU_CORTEXA53_0_MASK | | ||||||
|  | 	PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK | | ||||||
|  | 	PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK, /* IPI Mask */ | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int zynqmp_pm_request_node(const u32 node, const u32 capabilities, | ||||||
|  | 				  const u32 qos, const enum zynqmp_pm_request_ack ack) | ||||||
|  | { | ||||||
|  | 	return xilinx_pm_request(PM_REQUEST_NODE, node, capabilities, | ||||||
|  | 				   qos, ack, NULL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int zynqmp_power_domain_request(struct power_domain *power_domain) | ||||||
|  | { | ||||||
|  | 	/* Record power domain id */ | ||||||
|  | 	xpm_configobject[NODE_ID_LOCATION] = power_domain->id; | ||||||
|  | 
 | ||||||
|  | 	zynqmp_pmufw_load_config_object(xpm_configobject, sizeof(xpm_configobject)); | ||||||
|  | 
 | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int zynqmp_power_domain_free(struct power_domain *power_domain) | ||||||
|  | { | ||||||
|  | 	/* nop now */ | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int zynqmp_power_domain_on(struct power_domain *power_domain) | ||||||
|  | { | ||||||
|  | 	return zynqmp_pm_request_node(power_domain->id, | ||||||
|  | 				      ZYNQMP_PM_CAPABILITY_ACCESS, | ||||||
|  | 				      ZYNQMP_PM_MAX_QOS, | ||||||
|  | 				      ZYNQMP_PM_REQUEST_ACK_BLOCKING); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int zynqmp_power_domain_off(struct power_domain *power_domain) | ||||||
|  | { | ||||||
|  | 	/* nop now */ | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct power_domain_ops zynqmp_power_domain_ops = { | ||||||
|  | 	.request = zynqmp_power_domain_request, | ||||||
|  | 	.rfree = zynqmp_power_domain_free, | ||||||
|  | 	.on = zynqmp_power_domain_on, | ||||||
|  | 	.off = zynqmp_power_domain_off, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int zynqmp_power_domain_probe(struct udevice *dev) | ||||||
|  | { | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | U_BOOT_DRIVER(zynqmp_power_domain) = { | ||||||
|  | 	.name = "zynqmp_power_domain", | ||||||
|  | 	.id = UCLASS_POWER_DOMAIN, | ||||||
|  | 	.probe = zynqmp_power_domain_probe, | ||||||
|  | 	.ops = &zynqmp_power_domain_ops, | ||||||
|  | }; | ||||||
|  | @ -371,4 +371,35 @@ void zynqmp_pmufw_load_config_object(const void *cfg_obj, size_t size); | ||||||
| int xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2, | int xilinx_pm_request(u32 api_id, u32 arg0, u32 arg1, u32 arg2, | ||||||
| 		      u32 arg3, u32 *ret_payload); | 		      u32 arg3, u32 *ret_payload); | ||||||
| 
 | 
 | ||||||
|  | /* Type of Config Object */ | ||||||
|  | #define PM_CONFIG_OBJECT_TYPE_BASE	0x1U | ||||||
|  | #define PM_CONFIG_OBJECT_TYPE_OVERLAY	0x2U | ||||||
|  | 
 | ||||||
|  | /* Section Id */ | ||||||
|  | #define PM_CONFIG_SLAVE_SECTION_ID	0x102U | ||||||
|  | #define PM_CONFIG_SET_CONFIG_SECTION_ID	0x107U | ||||||
|  | 
 | ||||||
|  | /* Flag Option */ | ||||||
|  | #define PM_SLAVE_FLAG_IS_SHAREABLE	0x1U | ||||||
|  | #define PM_MASTER_USING_SLAVE_MASK	0x2U | ||||||
|  | 
 | ||||||
|  | /* IPI Mask for Master */ | ||||||
|  | #define PM_CONFIG_IPI_PSU_CORTEXA53_0_MASK	0x00000001 | ||||||
|  | #define PM_CONFIG_IPI_PSU_CORTEXR5_0_MASK	0x00000100 | ||||||
|  | #define PM_CONFIG_IPI_PSU_CORTEXR5_1_MASK	0x00000200 | ||||||
|  | 
 | ||||||
|  | enum zynqmp_pm_request_ack { | ||||||
|  | 	ZYNQMP_PM_REQUEST_ACK_NO = 1, | ||||||
|  | 	ZYNQMP_PM_REQUEST_ACK_BLOCKING = 2, | ||||||
|  | 	ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING = 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Node capabilities */ | ||||||
|  | #define ZYNQMP_PM_CAPABILITY_ACCESS	0x1U | ||||||
|  | #define ZYNQMP_PM_CAPABILITY_CONTEXT	0x2U | ||||||
|  | #define ZYNQMP_PM_CAPABILITY_WAKEUP	0x4U | ||||||
|  | #define ZYNQMP_PM_CAPABILITY_UNUSABLE	0x8U | ||||||
|  | 
 | ||||||
|  | #define ZYNQMP_PM_MAX_QOS		100U | ||||||
|  | 
 | ||||||
| #endif /* _ZYNQMP_FIRMWARE_H_ */ | #endif /* _ZYNQMP_FIRMWARE_H_ */ | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue