xref: /openbmc/qemu/hw/core/gpio.c (revision 7d87775f)
1 /*
2  * qdev GPIO helpers
3  *
4  *  Copyright (c) 2009 CodeSourcery
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "hw/qdev-core.h"
22 #include "hw/irq.h"
23 #include "qapi/error.h"
24 
25 static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
26                                                const char *name)
27 {
28     NamedGPIOList *ngl;
29 
30     QLIST_FOREACH(ngl, &dev->gpios, node) {
31         /* NULL is a valid and matchable name. */
32         if (g_strcmp0(name, ngl->name) == 0) {
33             return ngl;
34         }
35     }
36 
37     ngl = g_malloc0(sizeof(*ngl));
38     ngl->name = g_strdup(name);
39     QLIST_INSERT_HEAD(&dev->gpios, ngl, node);
40     return ngl;
41 }
42 
43 void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
44                                          qemu_irq_handler handler,
45                                          void *opaque,
46                                          const char *name, int n)
47 {
48     int i;
49     NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
50 
51     assert(gpio_list->num_out == 0 || !name);
52     gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
53                                      opaque, n);
54 
55     if (!name) {
56         name = "unnamed-gpio-in";
57     }
58     for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
59         gchar *propname = g_strdup_printf("%s[%u]", name, i);
60 
61         object_property_add_child(OBJECT(dev), propname,
62                                   OBJECT(gpio_list->in[i]));
63         g_free(propname);
64     }
65 
66     gpio_list->num_in += n;
67 }
68 
69 void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
70 {
71     qdev_init_gpio_in_named(dev, handler, NULL, n);
72 }
73 
74 void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
75                               const char *name, int n)
76 {
77     int i;
78     NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
79 
80     assert(gpio_list->num_in == 0 || !name);
81 
82     if (!name) {
83         name = "unnamed-gpio-out";
84     }
85     memset(pins, 0, sizeof(*pins) * n);
86     for (i = 0; i < n; ++i) {
87         gchar *propname = g_strdup_printf("%s[%u]", name,
88                                           gpio_list->num_out + i);
89 
90         object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
91                                  (Object **)&pins[i],
92                                  object_property_allow_set_link,
93                                  OBJ_PROP_LINK_STRONG);
94         g_free(propname);
95     }
96     gpio_list->num_out += n;
97 }
98 
99 void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
100 {
101     qdev_init_gpio_out_named(dev, pins, NULL, n);
102 }
103 
104 qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n)
105 {
106     NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
107 
108     assert(n >= 0 && n < gpio_list->num_in);
109     return gpio_list->in[n];
110 }
111 
112 qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
113 {
114     return qdev_get_gpio_in_named(dev, NULL, n);
115 }
116 
117 void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
118                                  qemu_irq input_pin)
119 {
120     char *propname = g_strdup_printf("%s[%d]",
121                                      name ? name : "unnamed-gpio-out", n);
122     if (input_pin && !OBJECT(input_pin)->parent) {
123         /* We need a name for object_property_set_link to work */
124         object_property_add_child(container_get(qdev_get_machine(),
125                                                 "/unattached"),
126                                   "non-qdev-gpio[*]", OBJECT(input_pin));
127     }
128     object_property_set_link(OBJECT(dev), propname,
129                              OBJECT(input_pin), &error_abort);
130     g_free(propname);
131 }
132 
133 qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
134 {
135     g_autofree char *propname = g_strdup_printf("%s[%d]",
136                                      name ? name : "unnamed-gpio-out", n);
137 
138     qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
139                                                       NULL);
140 
141     return ret;
142 }
143 
144 /* disconnect a GPIO output, returning the disconnected input (if any) */
145 
146 static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
147                                                const char *name, int n)
148 {
149     char *propname = g_strdup_printf("%s[%d]",
150                                      name ? name : "unnamed-gpio-out", n);
151 
152     qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
153                                                       NULL);
154     if (ret) {
155         object_property_set_link(OBJECT(dev), propname, NULL, NULL);
156     }
157     g_free(propname);
158     return ret;
159 }
160 
161 qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
162                                  const char *name, int n)
163 {
164     qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n);
165     qdev_connect_gpio_out_named(dev, name, n, icpt);
166     return disconnected;
167 }
168 
169 void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq input_pin)
170 {
171     qdev_connect_gpio_out_named(dev, NULL, n, input_pin);
172 }
173 
174 void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
175                      const char *name)
176 {
177     int i;
178     NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name);
179 
180     for (i = 0; i < ngl->num_in; i++) {
181         const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in";
182         char *propname = g_strdup_printf("%s[%d]", nm, i);
183 
184         object_property_add_alias(OBJECT(container), propname,
185                                   OBJECT(dev), propname);
186         g_free(propname);
187     }
188     for (i = 0; i < ngl->num_out; i++) {
189         const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
190         char *propname = g_strdup_printf("%s[%d]", nm, i);
191 
192         object_property_add_alias(OBJECT(container), propname,
193                                   OBJECT(dev), propname);
194         g_free(propname);
195     }
196     QLIST_REMOVE(ngl, node);
197     QLIST_INSERT_HEAD(&container->gpios, ngl, node);
198 }
199