Simon Glass | bc3ec00 | 2024-08-07 16:47:31 -0600 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * UPL handoff parsing |
| 4 | * |
| 5 | * Copyright 2024 Google LLC |
| 6 | * Written by Simon Glass <sjg@chromium.org> |
| 7 | */ |
| 8 | |
| 9 | #define LOG_CATEGORY UCLASS_BOOTSTD |
| 10 | |
| 11 | #include <alist.h> |
| 12 | #include <bloblist.h> |
| 13 | #include <dm.h> |
| 14 | #include <image.h> |
| 15 | #include <mapmem.h> |
| 16 | #include <serial.h> |
| 17 | #include <spl.h> |
| 18 | #include <upl.h> |
| 19 | #include <video.h> |
| 20 | #include <asm/global_data.h> |
| 21 | #include <dm/read.h> |
| 22 | #include <dm/uclass-internal.h> |
| 23 | |
| 24 | DECLARE_GLOBAL_DATA_PTR; |
| 25 | |
| 26 | struct upl s_upl; |
| 27 | |
| 28 | void upl_set_fit_addr(ulong fit) |
| 29 | { |
| 30 | struct upl *upl = &s_upl; |
| 31 | |
| 32 | upl->fit = fit; |
| 33 | } |
| 34 | |
| 35 | void upl_set_fit_info(ulong fit, int conf_offset, ulong entry_addr) |
| 36 | { |
| 37 | struct upl *upl = &s_upl; |
| 38 | |
| 39 | upl->fit = fit; |
| 40 | upl->conf_offset = conf_offset; |
| 41 | log_debug("upl: add fit %lx conf %x\n", fit, conf_offset); |
| 42 | } |
| 43 | |
| 44 | int _upl_add_image(int node, ulong load_addr, ulong size, const char *desc) |
| 45 | { |
| 46 | struct upl *upl = &s_upl; |
| 47 | struct upl_image img; |
| 48 | |
| 49 | img.load = load_addr; |
| 50 | img.size = size; |
| 51 | img.offset = node; |
| 52 | img.description = desc; |
| 53 | if (!alist_add(&upl->image, img)) |
| 54 | return -ENOMEM; |
| 55 | log_debug("upl: add image %s at %lx size %lx\n", desc, load_addr, size); |
| 56 | |
| 57 | return 0; |
| 58 | } |
| 59 | |
| 60 | static int write_serial(struct upl_serial *ser) |
| 61 | { |
| 62 | struct udevice *dev = gd->cur_serial_dev; |
| 63 | struct serial_device_info info; |
| 64 | struct memregion region; |
| 65 | int ret; |
| 66 | |
| 67 | if (!dev) |
| 68 | return log_msg_ret("ser", -ENOENT); |
| 69 | ret = serial_getinfo(dev, &info); |
| 70 | if (ret) |
| 71 | return log_msg_ret("inf", ret); |
| 72 | |
| 73 | ser->compatible = ofnode_read_string(dev_ofnode(dev), "compatible"); |
| 74 | ser->clock_frequency = info.clock; |
| 75 | ser->current_speed = gd->baudrate; |
| 76 | region.base = info.addr; |
| 77 | region.size = info.size; |
| 78 | alist_init_struct(&ser->reg, struct memregion); |
| 79 | if (!alist_add(&ser->reg, region)) |
| 80 | return -ENOMEM; |
| 81 | ser->reg_io_shift = info.reg_shift; |
| 82 | ser->reg_offset = info.reg_offset; |
| 83 | ser->reg_io_width = info.reg_width; |
| 84 | ser->virtual_reg = 0; |
| 85 | ser->access_type = info.addr_space; |
| 86 | |
| 87 | return 0; |
| 88 | } |
| 89 | |
| 90 | static int write_graphics(struct upl_graphics *gra) |
| 91 | { |
| 92 | struct video_uc_plat *plat; |
| 93 | struct video_priv *priv; |
| 94 | struct memregion region; |
| 95 | struct udevice *dev; |
| 96 | |
| 97 | alist_init_struct(&gra->reg, struct memregion); |
| 98 | uclass_find_first_device(UCLASS_VIDEO, &dev); |
| 99 | if (!dev || !device_active(dev)) |
| 100 | return log_msg_ret("vid", -ENOENT); |
| 101 | |
| 102 | plat = dev_get_uclass_plat(dev); |
| 103 | region.base = plat->base; |
| 104 | region.size = plat->size; |
| 105 | if (!alist_add(&gra->reg, region)) |
| 106 | return log_msg_ret("reg", -ENOMEM); |
| 107 | |
| 108 | priv = dev_get_uclass_priv(dev); |
| 109 | gra->width = priv->xsize; |
| 110 | gra->height = priv->ysize; |
| 111 | gra->stride = priv->line_length; /* private field */ |
| 112 | switch (priv->format) { |
| 113 | case VIDEO_RGBA8888: |
| 114 | case VIDEO_X8R8G8B8: |
| 115 | gra->format = UPLGF_ARGB32; |
| 116 | break; |
| 117 | case VIDEO_X8B8G8R8: |
| 118 | gra->format = UPLGF_ABGR32; |
| 119 | break; |
| 120 | case VIDEO_X2R10G10B10: |
| 121 | log_debug("device '%s': VIDEO_X2R10G10B10 not supported\n", |
| 122 | dev->name); |
| 123 | return log_msg_ret("for", -EPROTO); |
| 124 | case VIDEO_UNKNOWN: |
| 125 | log_debug("device '%s': Unknown video format\n", dev->name); |
| 126 | return log_msg_ret("for", -EPROTO); |
| 127 | } |
| 128 | |
| 129 | return 0; |
| 130 | } |
| 131 | |
| 132 | int spl_write_upl_handoff(struct spl_image_info *spl_image) |
| 133 | { |
| 134 | struct upl *upl = &s_upl; |
| 135 | struct abuf buf; |
| 136 | ofnode root; |
| 137 | void *ptr; |
| 138 | int ret; |
| 139 | |
| 140 | log_debug("UPL: Writing handoff - image_count=%d\n", upl->image.count); |
| 141 | upl->addr_cells = IS_ENABLED(CONFIG_PHYS_64BIT) ? 2 : 1; |
| 142 | upl->size_cells = IS_ENABLED(CONFIG_PHYS_64BIT) ? 2 : 1; |
| 143 | upl->bootmode = UPLBM_DEFAULT; |
| 144 | ret = write_serial(&upl->serial); |
| 145 | if (ret) |
| 146 | return log_msg_ret("ser", ret); |
| 147 | ret = write_graphics(&upl->graphics); |
| 148 | if (ret && ret != -ENOENT) |
| 149 | return log_msg_ret("gra", ret); |
| 150 | |
| 151 | root = ofnode_root(); |
| 152 | ret = upl_write_handoff(upl, root, true); |
| 153 | if (ret) |
| 154 | return log_msg_ret("wr", ret); |
| 155 | |
| 156 | ret = oftree_to_fdt(oftree_default(), &buf); |
| 157 | if (ret) |
| 158 | return log_msg_ret("fdt", ret); |
| 159 | log_debug("FDT size %zx\n", abuf_size(&buf)); |
| 160 | |
| 161 | ptr = bloblist_add(BLOBLISTT_CONTROL_FDT, abuf_size(&buf), 0); |
| 162 | if (!ptr) |
| 163 | return log_msg_ret("blo", -ENOENT); |
| 164 | memcpy(ptr, abuf_data(&buf), abuf_size(&buf)); |
| 165 | |
| 166 | return 0; |
| 167 | } |
| 168 | |
| 169 | void spl_upl_init(void) |
| 170 | { |
| 171 | upl_init(&s_upl); |
| 172 | } |