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