Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 1 | /* |
| 2 | * ARM Platform Bus device tree generation helpers |
| 3 | * |
| 4 | * Copyright (c) 2014 Linaro Limited |
| 5 | * |
| 6 | * Authors: |
| 7 | * Alex Graf <agraf@suse.de> |
| 8 | * Eric Auger <eric.auger@linaro.org> |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify it |
| 11 | * under the terms and conditions of the GNU General Public License, |
| 12 | * version 2 or later, as published by the Free Software Foundation. |
| 13 | * |
| 14 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 17 | * more details. |
| 18 | * |
| 19 | * You should have received a copy of the GNU General Public License along with |
| 20 | * this program. If not, see <http://www.gnu.org/licenses/>. |
| 21 | * |
| 22 | */ |
| 23 | |
Peter Maydell | 12b1672 | 2015-12-07 16:23:45 +0000 | [diff] [blame] | 24 | #include "qemu/osdep.h" |
Markus Armbruster | da34e65 | 2016-03-14 09:01:28 +0100 | [diff] [blame] | 25 | #include "qapi/error.h" |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 26 | #include <libfdt.h> |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 27 | #ifdef CONFIG_LINUX |
| 28 | #include <linux/vfio.h> |
| 29 | #endif |
Alistair Francis | d24a7bc | 2022-04-28 09:41:42 +1000 | [diff] [blame] | 30 | #include "hw/core/sysbus-fdt.h" |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 31 | #include "qemu/error-report.h" |
| 32 | #include "sysemu/device_tree.h" |
Eric Auger | c294ac3 | 2020-03-05 17:51:45 +0100 | [diff] [blame] | 33 | #include "sysemu/tpm.h" |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 34 | #include "hw/platform-bus.h" |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 35 | #include "hw/vfio/vfio-platform.h" |
| 36 | #include "hw/vfio/vfio-calxeda-xgmac.h" |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 37 | #include "hw/vfio/vfio-amd-xgbe.h" |
Gerd Hoffmann | 94692dc | 2018-06-13 14:29:46 +0200 | [diff] [blame] | 38 | #include "hw/display/ramfb.h" |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 39 | #include "hw/arm/fdt.h" |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 40 | |
| 41 | /* |
| 42 | * internal struct that contains the information to create dynamic |
| 43 | * sysbus device node |
| 44 | */ |
| 45 | typedef struct PlatformBusFDTData { |
| 46 | void *fdt; /* device tree handle */ |
| 47 | int irq_start; /* index of the first IRQ usable by platform bus devices */ |
| 48 | const char *pbus_node_name; /* name of the platform bus node */ |
| 49 | PlatformBusDevice *pbus; |
| 50 | } PlatformBusFDTData; |
| 51 | |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 52 | /* struct that allows to match a device and create its FDT node */ |
| 53 | typedef struct BindingEntry { |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 54 | const char *typename; |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 55 | const char *compat; |
| 56 | int (*add_fn)(SysBusDevice *sbdev, void *opaque); |
| 57 | bool (*match_fn)(SysBusDevice *sbdev, const struct BindingEntry *combo); |
| 58 | } BindingEntry; |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 59 | |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 60 | /* helpers */ |
| 61 | |
| 62 | typedef struct HostProperty { |
| 63 | const char *name; |
| 64 | bool optional; |
| 65 | } HostProperty; |
| 66 | |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 67 | #ifdef CONFIG_LINUX |
| 68 | |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 69 | /** |
| 70 | * copy_properties_from_host |
| 71 | * |
| 72 | * copies properties listed in an array from host device tree to |
| 73 | * guest device tree. If a non optional property is not found, the |
| 74 | * function asserts. An optional property is ignored if not found |
| 75 | * in the host device tree. |
| 76 | * @props: array of HostProperty to copy |
| 77 | * @nb_props: number of properties in the array |
| 78 | * @host_dt: host device tree blob |
| 79 | * @guest_dt: guest device tree blob |
| 80 | * @node_path: host dt node path where the property is supposed to be |
| 81 | found |
| 82 | * @nodename: guest node name the properties should be added to |
| 83 | */ |
| 84 | static void copy_properties_from_host(HostProperty *props, int nb_props, |
| 85 | void *host_fdt, void *guest_fdt, |
| 86 | char *node_path, char *nodename) |
| 87 | { |
| 88 | int i, prop_len; |
| 89 | const void *r; |
| 90 | Error *err = NULL; |
| 91 | |
| 92 | for (i = 0; i < nb_props; i++) { |
| 93 | r = qemu_fdt_getprop(host_fdt, node_path, |
| 94 | props[i].name, |
| 95 | &prop_len, |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 96 | &err); |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 97 | if (r) { |
| 98 | qemu_fdt_setprop(guest_fdt, nodename, |
| 99 | props[i].name, r, prop_len); |
| 100 | } else { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 101 | if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) { |
| 102 | /* optional property does not exist */ |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 103 | error_free(err); |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 104 | } else { |
| 105 | error_report_err(err); |
| 106 | } |
| 107 | if (!props[i].optional) { |
| 108 | /* mandatory property not found: bail out */ |
| 109 | exit(1); |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 110 | } |
Geert Uytterhoeven | d1fb710 | 2018-07-25 13:30:00 +0200 | [diff] [blame] | 111 | err = NULL; |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | /* clock properties whose values are copied/pasted from host */ |
| 117 | static HostProperty clock_copied_properties[] = { |
| 118 | {"compatible", false}, |
| 119 | {"#clock-cells", false}, |
| 120 | {"clock-frequency", true}, |
| 121 | {"clock-output-names", true}, |
| 122 | }; |
| 123 | |
| 124 | /** |
| 125 | * fdt_build_clock_node |
| 126 | * |
| 127 | * Build a guest clock node, used as a dependency from a passthrough'ed |
| 128 | * device. Most information are retrieved from the host clock node. |
| 129 | * Also check the host clock is a fixed one. |
| 130 | * |
| 131 | * @host_fdt: host device tree blob from which info are retrieved |
| 132 | * @guest_fdt: guest device tree blob where the clock node is added |
| 133 | * @host_phandle: phandle of the clock in host device tree |
| 134 | * @guest_phandle: phandle to assign to the guest node |
| 135 | */ |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 136 | static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, |
| 137 | uint32_t host_phandle, |
| 138 | uint32_t guest_phandle) |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 139 | { |
| 140 | char *node_path = NULL; |
| 141 | char *nodename; |
| 142 | const void *r; |
| 143 | int ret, node_offset, prop_len, path_len = 16; |
| 144 | |
| 145 | node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle); |
| 146 | if (node_offset <= 0) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 147 | error_report("not able to locate clock handle %d in host device tree", |
| 148 | host_phandle); |
| 149 | exit(1); |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 150 | } |
| 151 | node_path = g_malloc(path_len); |
| 152 | while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len)) |
| 153 | == -FDT_ERR_NOSPACE) { |
| 154 | path_len += 16; |
| 155 | node_path = g_realloc(node_path, path_len); |
| 156 | } |
| 157 | if (ret < 0) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 158 | error_report("not able to retrieve node path for clock handle %d", |
| 159 | host_phandle); |
| 160 | exit(1); |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len, |
| 164 | &error_fatal); |
| 165 | if (strcmp(r, "fixed-clock")) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 166 | error_report("clock handle %d is not a fixed clock", host_phandle); |
| 167 | exit(1); |
Eric Auger | 9481cf2 | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | nodename = strrchr(node_path, '/'); |
| 171 | qemu_fdt_add_subnode(guest_fdt, nodename); |
| 172 | |
| 173 | copy_properties_from_host(clock_copied_properties, |
| 174 | ARRAY_SIZE(clock_copied_properties), |
| 175 | host_fdt, guest_fdt, |
| 176 | node_path, nodename); |
| 177 | |
| 178 | qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle); |
| 179 | |
| 180 | g_free(node_path); |
| 181 | } |
| 182 | |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 183 | /** |
| 184 | * sysfs_to_dt_name: convert the name found in sysfs into the node name |
| 185 | * for instance e0900000.xgmac is converted into xgmac@e0900000 |
| 186 | * @sysfs_name: directory name in sysfs |
| 187 | * |
| 188 | * returns the device tree name upon success or NULL in case the sysfs name |
| 189 | * does not match the expected format |
| 190 | */ |
| 191 | static char *sysfs_to_dt_name(const char *sysfs_name) |
| 192 | { |
| 193 | gchar **substrings = g_strsplit(sysfs_name, ".", 2); |
| 194 | char *dt_name = NULL; |
| 195 | |
| 196 | if (!substrings || !substrings[0] || !substrings[1]) { |
| 197 | goto out; |
| 198 | } |
| 199 | dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]); |
| 200 | out: |
| 201 | g_strfreev(substrings); |
| 202 | return dt_name; |
| 203 | } |
| 204 | |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 205 | /* Device Specific Code */ |
| 206 | |
| 207 | /** |
| 208 | * add_calxeda_midway_xgmac_fdt_node |
| 209 | * |
| 210 | * Generates a simple node with following properties: |
| 211 | * compatible string, regs, interrupts, dma-coherent |
| 212 | */ |
| 213 | static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) |
| 214 | { |
| 215 | PlatformBusFDTData *data = opaque; |
| 216 | PlatformBusDevice *pbus = data->pbus; |
| 217 | void *fdt = data->fdt; |
| 218 | const char *parent_node = data->pbus_node_name; |
Eric Auger | c89e91a | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 219 | int compat_str_len, i; |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 220 | char *nodename; |
| 221 | uint32_t *irq_attr, *reg_attr; |
| 222 | uint64_t mmio_base, irq_number; |
| 223 | VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); |
| 224 | VFIODevice *vbasedev = &vdev->vbasedev; |
| 225 | |
| 226 | mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); |
| 227 | nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, |
| 228 | vbasedev->name, mmio_base); |
| 229 | qemu_fdt_add_subnode(fdt, nodename); |
| 230 | |
| 231 | compat_str_len = strlen(vdev->compat) + 1; |
| 232 | qemu_fdt_setprop(fdt, nodename, "compatible", |
| 233 | vdev->compat, compat_str_len); |
| 234 | |
| 235 | qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0); |
| 236 | |
| 237 | reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); |
| 238 | for (i = 0; i < vbasedev->num_regions; i++) { |
| 239 | mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); |
| 240 | reg_attr[2 * i] = cpu_to_be32(mmio_base); |
| 241 | reg_attr[2 * i + 1] = cpu_to_be32( |
Alex Williamson | db0da02 | 2016-03-10 09:39:07 -0700 | [diff] [blame] | 242 | memory_region_size(vdev->regions[i]->mem)); |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 243 | } |
Eric Auger | c89e91a | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 244 | qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, |
| 245 | vbasedev->num_regions * 2 * sizeof(uint32_t)); |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 246 | |
| 247 | irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); |
| 248 | for (i = 0; i < vbasedev->num_irqs; i++) { |
| 249 | irq_number = platform_bus_get_irqn(pbus, sbdev , i) |
| 250 | + data->irq_start; |
| 251 | irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); |
| 252 | irq_attr[3 * i + 1] = cpu_to_be32(irq_number); |
| 253 | irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); |
| 254 | } |
Eric Auger | c89e91a | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 255 | qemu_fdt_setprop(fdt, nodename, "interrupts", |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 256 | irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 257 | g_free(irq_attr); |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 258 | g_free(reg_attr); |
| 259 | g_free(nodename); |
Eric Auger | c89e91a | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 260 | return 0; |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 261 | } |
| 262 | |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 263 | /* AMD xgbe properties whose values are copied/pasted from host */ |
| 264 | static HostProperty amd_xgbe_copied_properties[] = { |
| 265 | {"compatible", false}, |
| 266 | {"dma-coherent", true}, |
| 267 | {"amd,per-channel-interrupt", true}, |
| 268 | {"phy-mode", false}, |
| 269 | {"mac-address", true}, |
| 270 | {"amd,speed-set", false}, |
| 271 | {"amd,serdes-blwc", true}, |
| 272 | {"amd,serdes-cdr-rate", true}, |
| 273 | {"amd,serdes-pq-skew", true}, |
| 274 | {"amd,serdes-tx-amp", true}, |
| 275 | {"amd,serdes-dfe-tap-config", true}, |
| 276 | {"amd,serdes-dfe-tap-enable", true}, |
| 277 | {"clock-names", false}, |
| 278 | }; |
| 279 | |
| 280 | /** |
| 281 | * add_amd_xgbe_fdt_node |
| 282 | * |
| 283 | * Generates the combined xgbe/phy node following kernel >=4.2 |
| 284 | * binding documentation: |
| 285 | * Documentation/devicetree/bindings/net/amd-xgbe.txt: |
| 286 | * Also 2 clock nodes are created (dma and ptp) |
| 287 | * |
| 288 | * Asserts in case of error |
| 289 | */ |
| 290 | static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) |
| 291 | { |
| 292 | PlatformBusFDTData *data = opaque; |
| 293 | PlatformBusDevice *pbus = data->pbus; |
| 294 | VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); |
| 295 | VFIODevice *vbasedev = &vdev->vbasedev; |
| 296 | VFIOINTp *intp; |
| 297 | const char *parent_node = data->pbus_node_name; |
| 298 | char **node_path, *nodename, *dt_name; |
| 299 | void *guest_fdt = data->fdt, *host_fdt; |
| 300 | const void *r; |
| 301 | int i, prop_len; |
Markus Armbruster | 2d72799 | 2022-09-23 14:00:23 +0200 | [diff] [blame] | 302 | uint32_t *irq_attr, *reg_attr; |
| 303 | const uint32_t *host_clock_phandles; |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 304 | uint64_t mmio_base, irq_number; |
| 305 | uint32_t guest_clock_phandles[2]; |
| 306 | |
| 307 | host_fdt = load_device_tree_from_sysfs(); |
| 308 | |
| 309 | dt_name = sysfs_to_dt_name(vbasedev->name); |
| 310 | if (!dt_name) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 311 | error_report("%s incorrect sysfs device name %s", |
| 312 | __func__, vbasedev->name); |
| 313 | exit(1); |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 314 | } |
| 315 | node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat, |
| 316 | &error_fatal); |
| 317 | if (!node_path || !node_path[0]) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 318 | error_report("%s unable to retrieve node path for %s/%s", |
| 319 | __func__, dt_name, vdev->compat); |
| 320 | exit(1); |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 321 | } |
| 322 | |
| 323 | if (node_path[1]) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 324 | error_report("%s more than one node matching %s/%s!", |
| 325 | __func__, dt_name, vdev->compat); |
| 326 | exit(1); |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 327 | } |
| 328 | |
| 329 | g_free(dt_name); |
| 330 | |
| 331 | if (vbasedev->num_regions != 5) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 332 | error_report("%s Does the host dt node combine XGBE/PHY?", __func__); |
| 333 | exit(1); |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 334 | } |
| 335 | |
| 336 | /* generate nodes for DMA_CLK and PTP_CLK */ |
| 337 | r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks", |
| 338 | &prop_len, &error_fatal); |
| 339 | if (prop_len != 8) { |
Philippe Mathieu-Daudé | 88bbd3f | 2018-06-29 15:11:00 +0100 | [diff] [blame] | 340 | error_report("%s clocks property should contain 2 handles", __func__); |
| 341 | exit(1); |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 342 | } |
Markus Armbruster | 2d72799 | 2022-09-23 14:00:23 +0200 | [diff] [blame] | 343 | host_clock_phandles = r; |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 344 | guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); |
| 345 | guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt); |
| 346 | |
| 347 | /** |
| 348 | * clock handles fetched from host dt are in be32 layout whereas |
| 349 | * rest of the code uses cpu layout. Also guest clock handles are |
| 350 | * in cpu layout. |
| 351 | */ |
| 352 | fdt_build_clock_node(host_fdt, guest_fdt, |
| 353 | be32_to_cpu(host_clock_phandles[0]), |
| 354 | guest_clock_phandles[0]); |
| 355 | |
| 356 | fdt_build_clock_node(host_fdt, guest_fdt, |
| 357 | be32_to_cpu(host_clock_phandles[1]), |
| 358 | guest_clock_phandles[1]); |
| 359 | |
| 360 | /* combined XGBE/PHY node */ |
| 361 | mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); |
| 362 | nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, |
| 363 | vbasedev->name, mmio_base); |
| 364 | qemu_fdt_add_subnode(guest_fdt, nodename); |
| 365 | |
| 366 | copy_properties_from_host(amd_xgbe_copied_properties, |
| 367 | ARRAY_SIZE(amd_xgbe_copied_properties), |
| 368 | host_fdt, guest_fdt, |
| 369 | node_path[0], nodename); |
| 370 | |
| 371 | qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks", |
| 372 | guest_clock_phandles[0], |
| 373 | guest_clock_phandles[1]); |
| 374 | |
| 375 | reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); |
| 376 | for (i = 0; i < vbasedev->num_regions; i++) { |
| 377 | mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); |
| 378 | reg_attr[2 * i] = cpu_to_be32(mmio_base); |
| 379 | reg_attr[2 * i + 1] = cpu_to_be32( |
Alex Williamson | db0da02 | 2016-03-10 09:39:07 -0700 | [diff] [blame] | 380 | memory_region_size(vdev->regions[i]->mem)); |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 381 | } |
| 382 | qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr, |
| 383 | vbasedev->num_regions * 2 * sizeof(uint32_t)); |
| 384 | |
| 385 | irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); |
| 386 | for (i = 0; i < vbasedev->num_irqs; i++) { |
| 387 | irq_number = platform_bus_get_irqn(pbus, sbdev , i) |
| 388 | + data->irq_start; |
| 389 | irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); |
| 390 | irq_attr[3 * i + 1] = cpu_to_be32(irq_number); |
| 391 | /* |
| 392 | * General device interrupt and PCS auto-negotiation interrupts are |
| 393 | * level-sensitive while the 4 per-channel interrupts are edge |
| 394 | * sensitive |
| 395 | */ |
| 396 | QLIST_FOREACH(intp, &vdev->intp_list, next) { |
| 397 | if (intp->pin == i) { |
| 398 | break; |
| 399 | } |
| 400 | } |
| 401 | if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) { |
| 402 | irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); |
| 403 | } else { |
| 404 | irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); |
| 405 | } |
| 406 | } |
| 407 | qemu_fdt_setprop(guest_fdt, nodename, "interrupts", |
| 408 | irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); |
| 409 | |
| 410 | g_free(host_fdt); |
| 411 | g_strfreev(node_path); |
| 412 | g_free(irq_attr); |
| 413 | g_free(reg_attr); |
| 414 | g_free(nodename); |
| 415 | return 0; |
| 416 | } |
| 417 | |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 418 | /* DT compatible matching */ |
| 419 | static bool vfio_platform_match(SysBusDevice *sbdev, |
| 420 | const BindingEntry *entry) |
| 421 | { |
| 422 | VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); |
| 423 | const char *compat; |
| 424 | unsigned int n; |
| 425 | |
| 426 | for (n = vdev->num_compat, compat = vdev->compat; n > 0; |
| 427 | n--, compat += strlen(compat) + 1) { |
| 428 | if (!strcmp(entry->compat, compat)) { |
| 429 | return true; |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | return false; |
| 434 | } |
| 435 | |
| 436 | #define VFIO_PLATFORM_BINDING(compat, add_fn) \ |
| 437 | {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match} |
| 438 | |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 439 | #endif /* CONFIG_LINUX */ |
| 440 | |
Stefan Berger | f50be48 | 2021-06-15 16:21:18 +0200 | [diff] [blame] | 441 | #ifdef CONFIG_TPM |
Eric Auger | c294ac3 | 2020-03-05 17:51:45 +0100 | [diff] [blame] | 442 | /* |
| 443 | * add_tpm_tis_fdt_node: Create a DT node for TPM TIS |
| 444 | * |
| 445 | * See kernel documentation: |
| 446 | * Documentation/devicetree/bindings/security/tpm/tpm_tis_mmio.txt |
| 447 | * Optional interrupt for command completion is not exposed |
| 448 | */ |
| 449 | static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque) |
| 450 | { |
| 451 | PlatformBusFDTData *data = opaque; |
| 452 | PlatformBusDevice *pbus = data->pbus; |
| 453 | void *fdt = data->fdt; |
| 454 | const char *parent_node = data->pbus_node_name; |
| 455 | char *nodename; |
| 456 | uint32_t reg_attr[2]; |
| 457 | uint64_t mmio_base; |
| 458 | |
| 459 | mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); |
| 460 | nodename = g_strdup_printf("%s/tpm_tis@%" PRIx64, parent_node, mmio_base); |
| 461 | qemu_fdt_add_subnode(fdt, nodename); |
| 462 | |
| 463 | qemu_fdt_setprop_string(fdt, nodename, "compatible", "tcg,tpm-tis-mmio"); |
| 464 | |
| 465 | reg_attr[0] = cpu_to_be32(mmio_base); |
| 466 | reg_attr[1] = cpu_to_be32(0x5000); |
| 467 | qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, 2 * sizeof(uint32_t)); |
| 468 | |
| 469 | g_free(nodename); |
| 470 | return 0; |
| 471 | } |
Stefan Berger | f50be48 | 2021-06-15 16:21:18 +0200 | [diff] [blame] | 472 | #endif |
Eric Auger | c294ac3 | 2020-03-05 17:51:45 +0100 | [diff] [blame] | 473 | |
Gerd Hoffmann | 94692dc | 2018-06-13 14:29:46 +0200 | [diff] [blame] | 474 | static int no_fdt_node(SysBusDevice *sbdev, void *opaque) |
| 475 | { |
| 476 | return 0; |
| 477 | } |
| 478 | |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 479 | /* Device type based matching */ |
| 480 | static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry) |
| 481 | { |
| 482 | return !strcmp(object_get_typename(OBJECT(sbdev)), entry->typename); |
| 483 | } |
| 484 | |
Eric Auger | e9ac8e8 | 2018-11-13 10:47:58 +0000 | [diff] [blame] | 485 | #define TYPE_BINDING(type, add_fn) {(type), NULL, (add_fn), NULL} |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 486 | |
| 487 | /* list of supported dynamic sysbus bindings */ |
| 488 | static const BindingEntry bindings[] = { |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 489 | #ifdef CONFIG_LINUX |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 490 | TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node), |
| 491 | TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node), |
| 492 | VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node), |
Eric Auger | cf5a13e | 2016-02-19 09:42:31 -0700 | [diff] [blame] | 493 | #endif |
Stefan Berger | f50be48 | 2021-06-15 16:21:18 +0200 | [diff] [blame] | 494 | #ifdef CONFIG_TPM |
Eric Auger | c294ac3 | 2020-03-05 17:51:45 +0100 | [diff] [blame] | 495 | TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), |
Stefan Berger | f50be48 | 2021-06-15 16:21:18 +0200 | [diff] [blame] | 496 | #endif |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 497 | TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), |
| 498 | TYPE_BINDING("", NULL), /* last element */ |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 499 | }; |
| 500 | |
Eric Auger | decf4f8 | 2015-06-19 14:17:44 +0100 | [diff] [blame] | 501 | /* Generic Code */ |
| 502 | |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 503 | /** |
| 504 | * add_fdt_node - add the device tree node of a dynamic sysbus device |
| 505 | * |
| 506 | * @sbdev: handle to the sysbus device |
| 507 | * @opaque: handle to the PlatformBusFDTData |
| 508 | * |
| 509 | * Checks the sysbus type belongs to the list of device types that |
| 510 | * are dynamically instantiable and if so call the node creation |
| 511 | * function. |
| 512 | */ |
David Gibson | 4f01a63 | 2016-09-21 15:23:53 +1000 | [diff] [blame] | 513 | static void add_fdt_node(SysBusDevice *sbdev, void *opaque) |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 514 | { |
| 515 | int i, ret; |
| 516 | |
Eric Auger | af7d64e | 2018-10-15 10:52:09 -0600 | [diff] [blame] | 517 | for (i = 0; i < ARRAY_SIZE(bindings); i++) { |
| 518 | const BindingEntry *iter = &bindings[i]; |
| 519 | |
Eric Auger | e9ac8e8 | 2018-11-13 10:47:58 +0000 | [diff] [blame] | 520 | if (type_match(sbdev, iter)) { |
| 521 | if (!iter->match_fn || iter->match_fn(sbdev, iter)) { |
| 522 | ret = iter->add_fn(sbdev, opaque); |
| 523 | assert(!ret); |
| 524 | return; |
| 525 | } |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 526 | } |
| 527 | } |
| 528 | error_report("Device %s can not be dynamically instantiated", |
| 529 | qdev_fw_name(DEVICE(sbdev))); |
| 530 | exit(1); |
| 531 | } |
| 532 | |
Igor Mammedov | 3b77f6c | 2018-05-10 18:10:56 +0100 | [diff] [blame] | 533 | void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr, |
| 534 | hwaddr bus_size, int irq_start) |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 535 | { |
| 536 | const char platcomp[] = "qemu,platform\0simple-bus"; |
| 537 | PlatformBusDevice *pbus; |
| 538 | DeviceState *dev; |
| 539 | gchar *node; |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 540 | |
| 541 | assert(fdt); |
| 542 | |
Conor Dooley | d1af787 | 2022-08-10 19:46:12 +0100 | [diff] [blame] | 543 | node = g_strdup_printf("/platform-bus@%"PRIx64, addr); |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 544 | |
| 545 | /* Create a /platform node that we can put all devices into */ |
| 546 | qemu_fdt_add_subnode(fdt, node); |
| 547 | qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp)); |
| 548 | |
| 549 | /* Our platform bus region is less than 32bits, so 1 cell is enough for |
| 550 | * address and size |
| 551 | */ |
| 552 | qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1); |
| 553 | qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1); |
Igor Mammedov | 3b77f6c | 2018-05-10 18:10:56 +0100 | [diff] [blame] | 554 | qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size); |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 555 | |
| 556 | qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc); |
| 557 | |
| 558 | dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE); |
| 559 | pbus = PLATFORM_BUS_DEVICE(dev); |
| 560 | |
Eric Auger | 11d306b | 2015-06-02 12:29:11 +0100 | [diff] [blame] | 561 | PlatformBusFDTData data = { |
| 562 | .fdt = fdt, |
| 563 | .irq_start = irq_start, |
| 564 | .pbus_node_name = node, |
| 565 | .pbus = pbus, |
| 566 | }; |
| 567 | |
| 568 | /* Loop through all dynamic sysbus devices and create their node */ |
| 569 | foreach_dynamic_sysbus_device(add_fdt_node, &data); |
| 570 | |
| 571 | g_free(node); |
| 572 | } |