1*dfaf1633Srin /* $NetBSD: fdt_powerdomain.c,v 1.2 2024/06/12 06:39:28 rin Exp $ */
2f80fe797Sskrll
3f80fe797Sskrll /*-
4f80fe797Sskrll * Copyright (c) 2022 The NetBSD Foundation, Inc.
5f80fe797Sskrll * All rights reserved.
6f80fe797Sskrll *
7f80fe797Sskrll * This code is derived from software contributed to The NetBSD Foundation
8f80fe797Sskrll * by Nick Hudson
9f80fe797Sskrll *
10f80fe797Sskrll * Redistribution and use in source and binary forms, with or without
11f80fe797Sskrll * modification, are permitted provided that the following conditions
12f80fe797Sskrll * are met:
13f80fe797Sskrll * 1. Redistributions of source code must retain the above copyright
14f80fe797Sskrll * notice, this list of conditions and the following disclaimer.
15f80fe797Sskrll * 2. Redistributions in binary form must reproduce the above copyright
16f80fe797Sskrll * notice, this list of conditions and the following disclaimer in the
17f80fe797Sskrll * documentation and/or other materials provided with the distribution.
18f80fe797Sskrll *
19f80fe797Sskrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20f80fe797Sskrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21f80fe797Sskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22f80fe797Sskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23f80fe797Sskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24f80fe797Sskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25f80fe797Sskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26f80fe797Sskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27f80fe797Sskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28f80fe797Sskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29f80fe797Sskrll * POSSIBILITY OF SUCH DAMAGE.
30f80fe797Sskrll */
31f80fe797Sskrll
32f80fe797Sskrll #include <sys/cdefs.h>
33*dfaf1633Srin __KERNEL_RCSID(0, "$NetBSD: fdt_powerdomain.c,v 1.2 2024/06/12 06:39:28 rin Exp $");
34f80fe797Sskrll
35f80fe797Sskrll #include <sys/param.h>
36f80fe797Sskrll
37f80fe797Sskrll #include <sys/bus.h>
38f80fe797Sskrll #include <sys/kmem.h>
39f80fe797Sskrll #include <sys/queue.h>
40f80fe797Sskrll
41f80fe797Sskrll #include <libfdt.h>
42f80fe797Sskrll #include <dev/fdt/fdtvar.h>
43f80fe797Sskrll
44f80fe797Sskrll
45f80fe797Sskrll struct fdtbus_powerdomain_controller {
46f80fe797Sskrll device_t pdc_dev;
47f80fe797Sskrll int pdc_phandle;
48f80fe797Sskrll
49f80fe797Sskrll void *pdc_cookie;
50f80fe797Sskrll
51f80fe797Sskrll int pdc_cells;
52f80fe797Sskrll
53f80fe797Sskrll const struct fdtbus_powerdomain_controller_func *pdc_funcs;
54f80fe797Sskrll
55f80fe797Sskrll LIST_ENTRY(fdtbus_powerdomain_controller) pdc_next;
56f80fe797Sskrll };
57f80fe797Sskrll
58f80fe797Sskrll static LIST_HEAD(, fdtbus_powerdomain_controller) fdtbus_powerdomain_controllers =
59f80fe797Sskrll LIST_HEAD_INITIALIZER(fdtbus_powerdomain_controllers);
60f80fe797Sskrll
61f80fe797Sskrll int
fdtbus_register_powerdomain_controller(device_t dev,int phandle,const struct fdtbus_powerdomain_controller_func * funcs)62f80fe797Sskrll fdtbus_register_powerdomain_controller(device_t dev, int phandle,
63f80fe797Sskrll const struct fdtbus_powerdomain_controller_func *funcs)
64f80fe797Sskrll {
65f80fe797Sskrll struct fdtbus_powerdomain_controller *pdc;
66f80fe797Sskrll
67f80fe797Sskrll uint32_t cells;
68f80fe797Sskrll if (of_getprop_uint32(phandle, "#power-domain-cells", &cells) != 0) {
69f80fe797Sskrll aprint_debug_dev(dev, "missing #power-domain-cells");
70f80fe797Sskrll return EINVAL;
71f80fe797Sskrll }
72f80fe797Sskrll
73f80fe797Sskrll pdc = kmem_alloc(sizeof(*pdc), KM_SLEEP);
74f80fe797Sskrll pdc->pdc_dev = dev;
75f80fe797Sskrll pdc->pdc_phandle = phandle;
76f80fe797Sskrll pdc->pdc_funcs = funcs;
77f80fe797Sskrll pdc->pdc_cells = cells;
78f80fe797Sskrll
79f80fe797Sskrll LIST_INSERT_HEAD(&fdtbus_powerdomain_controllers, pdc, pdc_next);
80f80fe797Sskrll
81f80fe797Sskrll return 0;
82f80fe797Sskrll }
83f80fe797Sskrll
84f80fe797Sskrll static struct fdtbus_powerdomain_controller *
fdtbus_powerdomain_lookup(int phandle)85f80fe797Sskrll fdtbus_powerdomain_lookup(int phandle)
86f80fe797Sskrll {
87f80fe797Sskrll struct fdtbus_powerdomain_controller *pdc;
88f80fe797Sskrll
89f80fe797Sskrll LIST_FOREACH(pdc, &fdtbus_powerdomain_controllers, pdc_next) {
90f80fe797Sskrll if (pdc->pdc_phandle == phandle)
91f80fe797Sskrll return pdc;
92f80fe797Sskrll }
93f80fe797Sskrll
94f80fe797Sskrll return NULL;
95f80fe797Sskrll }
96f80fe797Sskrll
97f80fe797Sskrll static int
fdtbus_powerdomain_enable_internal(int phandle,int index,bool enable)98f80fe797Sskrll fdtbus_powerdomain_enable_internal(int phandle, int index, bool enable)
99f80fe797Sskrll {
100f80fe797Sskrll int len;
101f80fe797Sskrll const uint32_t *pds = fdtbus_get_prop(phandle, "power-domains", &len);
102f80fe797Sskrll
103f80fe797Sskrll if (pds == NULL)
104f80fe797Sskrll return EINVAL;
105f80fe797Sskrll
106*dfaf1633Srin for (const uint32_t *pd = pds; pd < pds + len / sizeof(*pd); index--) {
107f80fe797Sskrll uint32_t pd_node =
108f80fe797Sskrll fdtbus_get_phandle_from_native(be32toh(pd[0]));
109f80fe797Sskrll struct fdtbus_powerdomain_controller *pdc =
110f80fe797Sskrll fdtbus_powerdomain_lookup(pd_node);
111f80fe797Sskrll
112f80fe797Sskrll if (pdc == NULL)
113f80fe797Sskrll return ENXIO;
114f80fe797Sskrll
115f80fe797Sskrll if (index < 0 || index == 0)
116f80fe797Sskrll pdc->pdc_funcs->pdc_enable(pdc->pdc_dev, pd, enable);
117f80fe797Sskrll if (index == 0)
118f80fe797Sskrll break;
119f80fe797Sskrll
120f80fe797Sskrll pd += pdc->pdc_cells + 1;
121f80fe797Sskrll }
122f80fe797Sskrll
123f80fe797Sskrll return 0;
124f80fe797Sskrll }
125f80fe797Sskrll
126f80fe797Sskrll int
fdtbus_powerdomain_enable_index(int phandle,int index)127f80fe797Sskrll fdtbus_powerdomain_enable_index(int phandle, int index)
128f80fe797Sskrll {
129f80fe797Sskrll return fdtbus_powerdomain_enable_internal(phandle, index, true);
130f80fe797Sskrll }
131f80fe797Sskrll
132f80fe797Sskrll int
fdtbus_powerdomain_disable_index(int phandle,int index)133f80fe797Sskrll fdtbus_powerdomain_disable_index(int phandle, int index)
134f80fe797Sskrll {
135f80fe797Sskrll return fdtbus_powerdomain_enable_internal(phandle, index, false);
136f80fe797Sskrll }
137f80fe797Sskrll
138f80fe797Sskrll int
fdtbus_powerdomain_enable(int node)139f80fe797Sskrll fdtbus_powerdomain_enable(int node)
140f80fe797Sskrll {
141f80fe797Sskrll return fdtbus_powerdomain_enable_index(node, -1);
142f80fe797Sskrll }
143f80fe797Sskrll
144f80fe797Sskrll int
fdtbus_powerdomain_disable(int node)145f80fe797Sskrll fdtbus_powerdomain_disable(int node)
146f80fe797Sskrll {
147f80fe797Sskrll return fdtbus_powerdomain_disable_index(node, -1);
148f80fe797Sskrll }
149