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 
upl_set_fit_addr(ulong fit)28 void upl_set_fit_addr(ulong fit)
29 {
30 	struct upl *upl = &s_upl;
31 
32 	upl->fit = fit;
33 }
34 
upl_set_fit_info(ulong fit,int conf_offset,ulong entry_addr)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 
_upl_add_image(int node,ulong load_addr,ulong size,const char * desc)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 
write_serial(struct upl_serial * ser)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 
write_graphics(struct upl_graphics * gra)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 
spl_write_upl_handoff(struct spl_image_info * spl_image)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 
spl_upl_init(void)169 void spl_upl_init(void)
170 {
171 	upl_init(&s_upl);
172 }
173