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/zynq_gem.c | ||||
| F:	drivers/phy/phy-zynqmp.c | ||||
| F:	drivers/power/domain/zynqmp-power-domain.c | ||||
| F:	drivers/serial/serial_zynq.c | ||||
| F:	drivers/reset/reset-zynqmp.c | ||||
| F:	drivers/rtc/zynqmp_rtc.c | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| #include <common.h> | ||||
| #include <cpu_func.h> | ||||
| #include <dm.h> | ||||
| #include <dm/lists.h> | ||||
| #include <log.h> | ||||
| #include <zynqmp_firmware.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) = { | ||||
| 	.id = UCLASS_FIRMWARE, | ||||
| 	.name = "zynqmp_firmware", | ||||
| 	.of_match = zynqmp_firmware_ids, | ||||
| 	.bind = zynqmp_firmware_bind, | ||||
| }; | ||||
|  |  | |||
|  | @ -88,4 +88,13 @@ config TI_POWER_DOMAIN | |||
| 	help | ||||
| 	  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 | ||||
|  |  | |||
|  | @ -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_TI_SCI_POWER_DOMAIN) += ti-sci-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, | ||||
| 		      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_ */ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue