xref: /rk3399_rockchip-uboot/drivers/power/domain/power-domain-uclass.c (revision 071e6293adb2573a1ca0c45f54852bdec20444b8)
161f5ddcbSStephen Warren /*
261f5ddcbSStephen Warren  * Copyright (c) 2016, NVIDIA CORPORATION.
361f5ddcbSStephen Warren  *
461f5ddcbSStephen Warren  * SPDX-License-Identifier: GPL-2.0
561f5ddcbSStephen Warren  */
661f5ddcbSStephen Warren 
761f5ddcbSStephen Warren #include <common.h>
861f5ddcbSStephen Warren #include <dm.h>
961f5ddcbSStephen Warren #include <power-domain.h>
1061f5ddcbSStephen Warren #include <power-domain-uclass.h>
1183769450SLokesh Vutla #include <dm/device-internal.h>
1261f5ddcbSStephen Warren 
1361f5ddcbSStephen Warren DECLARE_GLOBAL_DATA_PTR;
1461f5ddcbSStephen Warren 
power_domain_dev_ops(struct udevice * dev)1561f5ddcbSStephen Warren static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
1661f5ddcbSStephen Warren {
1761f5ddcbSStephen Warren 	return (struct power_domain_ops *)dev->driver->ops;
1861f5ddcbSStephen Warren }
1961f5ddcbSStephen Warren 
power_domain_of_xlate_default(struct power_domain * power_domain,struct ofnode_phandle_args * args)2061f5ddcbSStephen Warren static int power_domain_of_xlate_default(struct power_domain *power_domain,
21424b2fe9SSimon Glass 					 struct ofnode_phandle_args *args)
2261f5ddcbSStephen Warren {
2361f5ddcbSStephen Warren 	debug("%s(power_domain=%p)\n", __func__, power_domain);
2461f5ddcbSStephen Warren 
2561f5ddcbSStephen Warren 	if (args->args_count != 1) {
2661f5ddcbSStephen Warren 		debug("Invalid args_count: %d\n", args->args_count);
2761f5ddcbSStephen Warren 		return -EINVAL;
2861f5ddcbSStephen Warren 	}
2961f5ddcbSStephen Warren 
3061f5ddcbSStephen Warren 	power_domain->id = args->args[0];
3161f5ddcbSStephen Warren 
3261f5ddcbSStephen Warren 	return 0;
3361f5ddcbSStephen Warren }
3461f5ddcbSStephen Warren 
power_domain_get_by_index(struct udevice * dev,struct power_domain * power_domain,int index)3592ac3df1SLokesh Vutla int power_domain_get_by_index(struct udevice *dev,
3692ac3df1SLokesh Vutla 			      struct power_domain *power_domain, int index)
3761f5ddcbSStephen Warren {
38424b2fe9SSimon Glass 	struct ofnode_phandle_args args;
3961f5ddcbSStephen Warren 	int ret;
4061f5ddcbSStephen Warren 	struct udevice *dev_power_domain;
4161f5ddcbSStephen Warren 	struct power_domain_ops *ops;
4261f5ddcbSStephen Warren 
4361f5ddcbSStephen Warren 	debug("%s(dev=%p, power_domain=%p)\n", __func__, dev, power_domain);
4461f5ddcbSStephen Warren 
45424b2fe9SSimon Glass 	ret = dev_read_phandle_with_args(dev, "power-domains",
4692ac3df1SLokesh Vutla 					 "#power-domain-cells", 0, index,
4792ac3df1SLokesh Vutla 					 &args);
4861f5ddcbSStephen Warren 	if (ret) {
49424b2fe9SSimon Glass 		debug("%s: dev_read_phandle_with_args failed: %d\n",
5061f5ddcbSStephen Warren 		      __func__, ret);
5161f5ddcbSStephen Warren 		return ret;
5261f5ddcbSStephen Warren 	}
5361f5ddcbSStephen Warren 
54424b2fe9SSimon Glass 	ret = uclass_get_device_by_ofnode(UCLASS_POWER_DOMAIN, args.node,
5561f5ddcbSStephen Warren 					  &dev_power_domain);
5661f5ddcbSStephen Warren 	if (ret) {
57424b2fe9SSimon Glass 		debug("%s: uclass_get_device_by_ofnode failed: %d\n",
5861f5ddcbSStephen Warren 		      __func__, ret);
5961f5ddcbSStephen Warren 		return ret;
6061f5ddcbSStephen Warren 	}
6161f5ddcbSStephen Warren 	ops = power_domain_dev_ops(dev_power_domain);
6261f5ddcbSStephen Warren 
6361f5ddcbSStephen Warren 	power_domain->dev = dev_power_domain;
6461f5ddcbSStephen Warren 	if (ops->of_xlate)
6561f5ddcbSStephen Warren 		ret = ops->of_xlate(power_domain, &args);
6661f5ddcbSStephen Warren 	else
6761f5ddcbSStephen Warren 		ret = power_domain_of_xlate_default(power_domain, &args);
6861f5ddcbSStephen Warren 	if (ret) {
6961f5ddcbSStephen Warren 		debug("of_xlate() failed: %d\n", ret);
7061f5ddcbSStephen Warren 		return ret;
7161f5ddcbSStephen Warren 	}
7261f5ddcbSStephen Warren 
732ec64758SMarek Vasut 	ret = ops->request ? ops->request(power_domain) : 0;
7461f5ddcbSStephen Warren 	if (ret) {
7561f5ddcbSStephen Warren 		debug("ops->request() failed: %d\n", ret);
7661f5ddcbSStephen Warren 		return ret;
7761f5ddcbSStephen Warren 	}
7861f5ddcbSStephen Warren 
7961f5ddcbSStephen Warren 	return 0;
8061f5ddcbSStephen Warren }
8161f5ddcbSStephen Warren 
power_domain_get_by_name(struct udevice * dev,struct power_domain * power_domain,const char * name)825143d286SMarek Vasut int power_domain_get_by_name(struct udevice *dev,
835143d286SMarek Vasut 			     struct power_domain *power_domain, const char *name)
845143d286SMarek Vasut {
855143d286SMarek Vasut 	int index;
865143d286SMarek Vasut 
875143d286SMarek Vasut 	index = dev_read_stringlist_search(dev, "power-domain-names", name);
885143d286SMarek Vasut 	if (index < 0) {
895143d286SMarek Vasut 		debug("fdt_stringlist_search() failed: %d\n", index);
905143d286SMarek Vasut 		return index;
915143d286SMarek Vasut 	}
925143d286SMarek Vasut 
935143d286SMarek Vasut 	return power_domain_get_by_index(dev, power_domain, index);
945143d286SMarek Vasut }
955143d286SMarek Vasut 
power_domain_get(struct udevice * dev,struct power_domain * power_domain)9692ac3df1SLokesh Vutla int power_domain_get(struct udevice *dev, struct power_domain *power_domain)
9792ac3df1SLokesh Vutla {
9892ac3df1SLokesh Vutla 	return power_domain_get_by_index(dev, power_domain, 0);
9992ac3df1SLokesh Vutla }
10092ac3df1SLokesh Vutla 
power_domain_free(struct power_domain * power_domain)10161f5ddcbSStephen Warren int power_domain_free(struct power_domain *power_domain)
10261f5ddcbSStephen Warren {
10361f5ddcbSStephen Warren 	struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
10461f5ddcbSStephen Warren 
10561f5ddcbSStephen Warren 	debug("%s(power_domain=%p)\n", __func__, power_domain);
10661f5ddcbSStephen Warren 
1072ec64758SMarek Vasut 	return ops->rfree ? ops->rfree(power_domain) : 0;
10861f5ddcbSStephen Warren }
10961f5ddcbSStephen Warren 
power_domain_on(struct power_domain * power_domain)11061f5ddcbSStephen Warren int power_domain_on(struct power_domain *power_domain)
11161f5ddcbSStephen Warren {
11261f5ddcbSStephen Warren 	struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
11361f5ddcbSStephen Warren 
11461f5ddcbSStephen Warren 	debug("%s(power_domain=%p)\n", __func__, power_domain);
11561f5ddcbSStephen Warren 
1162ec64758SMarek Vasut 	return ops->on ? ops->on(power_domain) : 0;
11761f5ddcbSStephen Warren }
11861f5ddcbSStephen Warren 
power_domain_off(struct power_domain * power_domain)11961f5ddcbSStephen Warren int power_domain_off(struct power_domain *power_domain)
12061f5ddcbSStephen Warren {
12161f5ddcbSStephen Warren 	struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
12261f5ddcbSStephen Warren 
12361f5ddcbSStephen Warren 	debug("%s(power_domain=%p)\n", __func__, power_domain);
12461f5ddcbSStephen Warren 
1252ec64758SMarek Vasut 	return ops->off ? ops->off(power_domain) : 0;
12661f5ddcbSStephen Warren }
12761f5ddcbSStephen Warren 
12883769450SLokesh Vutla #if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA))
dev_power_domain_ctrl(struct udevice * dev,bool on)12983769450SLokesh Vutla static int dev_power_domain_ctrl(struct udevice *dev, bool on)
130f594f5d3SPeng Fan {
131f594f5d3SPeng Fan 	struct power_domain pd;
13283769450SLokesh Vutla 	int i, count, ret = 0;
133f594f5d3SPeng Fan 
134f594f5d3SPeng Fan 	count = dev_count_phandle_with_args(dev, "power-domains",
135f594f5d3SPeng Fan 					    "#power-domain-cells");
136f594f5d3SPeng Fan 	for (i = 0; i < count; i++) {
137f594f5d3SPeng Fan 		ret = power_domain_get_by_index(dev, &pd, i);
138f594f5d3SPeng Fan 		if (ret)
139f594f5d3SPeng Fan 			return ret;
14083769450SLokesh Vutla 		if (on)
141f594f5d3SPeng Fan 			ret = power_domain_on(&pd);
14283769450SLokesh Vutla 		else
14383769450SLokesh Vutla 			ret = power_domain_off(&pd);
14483769450SLokesh Vutla 	}
14583769450SLokesh Vutla 
14683769450SLokesh Vutla 	/*
1478ec57b76SAnatolij Gustschin 	 * For platforms with parent and child power-domain devices
1488ec57b76SAnatolij Gustschin 	 * we may not run device_remove() on the power-domain parent
1498ec57b76SAnatolij Gustschin 	 * because it will result in removing its children and switching
1508ec57b76SAnatolij Gustschin 	 * off their power-domain parent. So we will get here again and
1518ec57b76SAnatolij Gustschin 	 * again and will be stuck in an endless loop.
1528ec57b76SAnatolij Gustschin 	 */
153*071e6293SSean Anderson 	if (count > 0 && !on && dev_get_parent(dev) == pd.dev &&
1548ec57b76SAnatolij Gustschin 	    device_get_uclass_id(dev) == UCLASS_POWER_DOMAIN)
1558ec57b76SAnatolij Gustschin 		return ret;
1568ec57b76SAnatolij Gustschin 
1578ec57b76SAnatolij Gustschin 	/*
15883769450SLokesh Vutla 	 * power_domain_get() bound the device, thus
15983769450SLokesh Vutla 	 * we must remove it again to prevent unbinding
16083769450SLokesh Vutla 	 * active devices (which would result in unbind
16183769450SLokesh Vutla 	 * error).
16283769450SLokesh Vutla 	 */
16383769450SLokesh Vutla 	if (count > 0 && !on)
16483769450SLokesh Vutla 		device_remove(pd.dev, DM_REMOVE_NORMAL);
16583769450SLokesh Vutla 
166f594f5d3SPeng Fan 	return ret;
167f594f5d3SPeng Fan }
168f594f5d3SPeng Fan 
dev_power_domain_on(struct udevice * dev)16983769450SLokesh Vutla int dev_power_domain_on(struct udevice *dev)
17083769450SLokesh Vutla {
17183769450SLokesh Vutla 	return dev_power_domain_ctrl(dev, true);
17283769450SLokesh Vutla }
17383769450SLokesh Vutla 
dev_power_domain_off(struct udevice * dev)17483769450SLokesh Vutla int dev_power_domain_off(struct udevice *dev)
17583769450SLokesh Vutla {
17683769450SLokesh Vutla 	return dev_power_domain_ctrl(dev, false);
177f594f5d3SPeng Fan }
178f594f5d3SPeng Fan #endif
179f594f5d3SPeng Fan 
18061f5ddcbSStephen Warren UCLASS_DRIVER(power_domain) = {
18161f5ddcbSStephen Warren 	.id		= UCLASS_POWER_DOMAIN,
18261f5ddcbSStephen Warren 	.name		= "power_domain",
18361f5ddcbSStephen Warren };
184