• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2017 Etnaviv Project
4  * Copyright (C) 2017 Zodiac Inflight Innovations
5  */
6 
7 #include "common.xml.h"
8 #include "etnaviv_gpu.h"
9 #include "etnaviv_perfmon.h"
10 #include "state_hi.xml.h"
11 
12 struct etnaviv_pm_domain;
13 
14 struct etnaviv_pm_signal {
15 	char name[64];
16 	u32 data;
17 
18 	u32 (*sample)(struct etnaviv_gpu *gpu,
19 		      const struct etnaviv_pm_domain *domain,
20 		      const struct etnaviv_pm_signal *signal);
21 };
22 
23 struct etnaviv_pm_domain {
24 	char name[64];
25 
26 	/* profile register */
27 	u32 profile_read;
28 	u32 profile_config;
29 
30 	u8 nr_signals;
31 	const struct etnaviv_pm_signal *signal;
32 };
33 
34 struct etnaviv_pm_domain_meta {
35 	unsigned int feature;
36 	const struct etnaviv_pm_domain *domains;
37 	u32 nr_domains;
38 };
39 
perf_reg_read(struct etnaviv_gpu * gpu,const struct etnaviv_pm_domain * domain,const struct etnaviv_pm_signal * signal)40 static u32 perf_reg_read(struct etnaviv_gpu *gpu,
41 	const struct etnaviv_pm_domain *domain,
42 	const struct etnaviv_pm_signal *signal)
43 {
44 	gpu_write(gpu, domain->profile_config, signal->data);
45 
46 	return gpu_read(gpu, domain->profile_read);
47 }
48 
pipe_select(struct etnaviv_gpu * gpu,u32 clock,unsigned pipe)49 static inline void pipe_select(struct etnaviv_gpu *gpu, u32 clock, unsigned pipe)
50 {
51 	clock &= ~(VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE__MASK);
52 	clock |= VIVS_HI_CLOCK_CONTROL_DEBUG_PIXEL_PIPE(pipe);
53 
54 	gpu_write(gpu, VIVS_HI_CLOCK_CONTROL, clock);
55 }
56 
pipe_perf_reg_read(struct etnaviv_gpu * gpu,const struct etnaviv_pm_domain * domain,const struct etnaviv_pm_signal * signal)57 static u32 pipe_perf_reg_read(struct etnaviv_gpu *gpu,
58 	const struct etnaviv_pm_domain *domain,
59 	const struct etnaviv_pm_signal *signal)
60 {
61 	u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
62 	u32 value = 0;
63 	unsigned i;
64 
65 	for (i = 0; i < gpu->identity.pixel_pipes; i++) {
66 		pipe_select(gpu, clock, i);
67 		value += perf_reg_read(gpu, domain, signal);
68 	}
69 
70 	/* switch back to pixel pipe 0 to prevent GPU hang */
71 	pipe_select(gpu, clock, 0);
72 
73 	return value;
74 }
75 
pipe_reg_read(struct etnaviv_gpu * gpu,const struct etnaviv_pm_domain * domain,const struct etnaviv_pm_signal * signal)76 static u32 pipe_reg_read(struct etnaviv_gpu *gpu,
77 	const struct etnaviv_pm_domain *domain,
78 	const struct etnaviv_pm_signal *signal)
79 {
80 	u32 clock = gpu_read(gpu, VIVS_HI_CLOCK_CONTROL);
81 	u32 value = 0;
82 	unsigned i;
83 
84 	for (i = 0; i < gpu->identity.pixel_pipes; i++) {
85 		pipe_select(gpu, clock, i);
86 		value += gpu_read(gpu, signal->data);
87 	}
88 
89 	/* switch back to pixel pipe 0 to prevent GPU hang */
90 	pipe_select(gpu, clock, 0);
91 
92 	return value;
93 }
94 
hi_total_cycle_read(struct etnaviv_gpu * gpu,const struct etnaviv_pm_domain * domain,const struct etnaviv_pm_signal * signal)95 static u32 hi_total_cycle_read(struct etnaviv_gpu *gpu,
96 	const struct etnaviv_pm_domain *domain,
97 	const struct etnaviv_pm_signal *signal)
98 {
99 	u32 reg = VIVS_HI_PROFILE_TOTAL_CYCLES;
100 
101 	if (gpu->identity.model == chipModel_GC880 ||
102 		gpu->identity.model == chipModel_GC2000 ||
103 		gpu->identity.model == chipModel_GC2100)
104 		reg = VIVS_MC_PROFILE_CYCLE_COUNTER;
105 
106 	return gpu_read(gpu, reg);
107 }
108 
hi_total_idle_cycle_read(struct etnaviv_gpu * gpu,const struct etnaviv_pm_domain * domain,const struct etnaviv_pm_signal * signal)109 static u32 hi_total_idle_cycle_read(struct etnaviv_gpu *gpu,
110 	const struct etnaviv_pm_domain *domain,
111 	const struct etnaviv_pm_signal *signal)
112 {
113 	u32 reg = VIVS_HI_PROFILE_IDLE_CYCLES;
114 
115 	if (gpu->identity.model == chipModel_GC880 ||
116 		gpu->identity.model == chipModel_GC2000 ||
117 		gpu->identity.model == chipModel_GC2100)
118 		reg = VIVS_HI_PROFILE_TOTAL_CYCLES;
119 
120 	return gpu_read(gpu, reg);
121 }
122 
123 static const struct etnaviv_pm_domain doms_3d[] = {
124 	{
125 		.name = "HI",
126 		.profile_read = VIVS_MC_PROFILE_HI_READ,
127 		.profile_config = VIVS_MC_PROFILE_CONFIG2,
128 		.nr_signals = 7,
129 		.signal = (const struct etnaviv_pm_signal[]) {
130 			{
131 				"TOTAL_READ_BYTES8",
132 				VIVS_HI_PROFILE_READ_BYTES8,
133 				&pipe_reg_read,
134 			},
135 			{
136 				"TOTAL_WRITE_BYTES8",
137 				VIVS_HI_PROFILE_WRITE_BYTES8,
138 				&pipe_reg_read,
139 			},
140 			{
141 				"TOTAL_CYCLES",
142 				0,
143 				&hi_total_cycle_read
144 			},
145 			{
146 				"IDLE_CYCLES",
147 				0,
148 				&hi_total_idle_cycle_read
149 			},
150 			{
151 				"AXI_CYCLES_READ_REQUEST_STALLED",
152 				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_READ_REQUEST_STALLED,
153 				&perf_reg_read
154 			},
155 			{
156 				"AXI_CYCLES_WRITE_REQUEST_STALLED",
157 				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_REQUEST_STALLED,
158 				&perf_reg_read
159 			},
160 			{
161 				"AXI_CYCLES_WRITE_DATA_STALLED",
162 				VIVS_MC_PROFILE_CONFIG2_HI_AXI_CYCLES_WRITE_DATA_STALLED,
163 				&perf_reg_read
164 			}
165 		}
166 	},
167 	{
168 		.name = "PE",
169 		.profile_read = VIVS_MC_PROFILE_PE_READ,
170 		.profile_config = VIVS_MC_PROFILE_CONFIG0,
171 		.nr_signals = 4,
172 		.signal = (const struct etnaviv_pm_signal[]) {
173 			{
174 				"PIXEL_COUNT_KILLED_BY_COLOR_PIPE",
175 				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_COLOR_PIPE,
176 				&pipe_perf_reg_read
177 			},
178 			{
179 				"PIXEL_COUNT_KILLED_BY_DEPTH_PIPE",
180 				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_KILLED_BY_DEPTH_PIPE,
181 				&pipe_perf_reg_read
182 			},
183 			{
184 				"PIXEL_COUNT_DRAWN_BY_COLOR_PIPE",
185 				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_COLOR_PIPE,
186 				&pipe_perf_reg_read
187 			},
188 			{
189 				"PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE",
190 				VIVS_MC_PROFILE_CONFIG0_PE_PIXEL_COUNT_DRAWN_BY_DEPTH_PIPE,
191 				&pipe_perf_reg_read
192 			}
193 		}
194 	},
195 	{
196 		.name = "SH",
197 		.profile_read = VIVS_MC_PROFILE_SH_READ,
198 		.profile_config = VIVS_MC_PROFILE_CONFIG0,
199 		.nr_signals = 9,
200 		.signal = (const struct etnaviv_pm_signal[]) {
201 			{
202 				"SHADER_CYCLES",
203 				VIVS_MC_PROFILE_CONFIG0_SH_SHADER_CYCLES,
204 				&perf_reg_read
205 			},
206 			{
207 				"PS_INST_COUNTER",
208 				VIVS_MC_PROFILE_CONFIG0_SH_PS_INST_COUNTER,
209 				&perf_reg_read
210 			},
211 			{
212 				"RENDERED_PIXEL_COUNTER",
213 				VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_PIXEL_COUNTER,
214 				&perf_reg_read
215 			},
216 			{
217 				"VS_INST_COUNTER",
218 				VIVS_MC_PROFILE_CONFIG0_SH_VS_INST_COUNTER,
219 				&pipe_perf_reg_read
220 			},
221 			{
222 				"RENDERED_VERTICE_COUNTER",
223 				VIVS_MC_PROFILE_CONFIG0_SH_RENDERED_VERTICE_COUNTER,
224 				&pipe_perf_reg_read
225 			},
226 			{
227 				"VTX_BRANCH_INST_COUNTER",
228 				VIVS_MC_PROFILE_CONFIG0_SH_VTX_BRANCH_INST_COUNTER,
229 				&pipe_perf_reg_read
230 			},
231 			{
232 				"VTX_TEXLD_INST_COUNTER",
233 				VIVS_MC_PROFILE_CONFIG0_SH_VTX_TEXLD_INST_COUNTER,
234 				&pipe_perf_reg_read
235 			},
236 			{
237 				"PXL_BRANCH_INST_COUNTER",
238 				VIVS_MC_PROFILE_CONFIG0_SH_PXL_BRANCH_INST_COUNTER,
239 				&pipe_perf_reg_read
240 			},
241 			{
242 				"PXL_TEXLD_INST_COUNTER",
243 				VIVS_MC_PROFILE_CONFIG0_SH_PXL_TEXLD_INST_COUNTER,
244 				&pipe_perf_reg_read
245 			}
246 		}
247 	},
248 	{
249 		.name = "PA",
250 		.profile_read = VIVS_MC_PROFILE_PA_READ,
251 		.profile_config = VIVS_MC_PROFILE_CONFIG1,
252 		.nr_signals = 6,
253 		.signal = (const struct etnaviv_pm_signal[]) {
254 			{
255 				"INPUT_VTX_COUNTER",
256 				VIVS_MC_PROFILE_CONFIG1_PA_INPUT_VTX_COUNTER,
257 				&perf_reg_read
258 			},
259 			{
260 				"INPUT_PRIM_COUNTER",
261 				VIVS_MC_PROFILE_CONFIG1_PA_INPUT_PRIM_COUNTER,
262 				&perf_reg_read
263 			},
264 			{
265 				"OUTPUT_PRIM_COUNTER",
266 				VIVS_MC_PROFILE_CONFIG1_PA_OUTPUT_PRIM_COUNTER,
267 				&perf_reg_read
268 			},
269 			{
270 				"DEPTH_CLIPPED_COUNTER",
271 				VIVS_MC_PROFILE_CONFIG1_PA_DEPTH_CLIPPED_COUNTER,
272 				&pipe_perf_reg_read
273 			},
274 			{
275 				"TRIVIAL_REJECTED_COUNTER",
276 				VIVS_MC_PROFILE_CONFIG1_PA_TRIVIAL_REJECTED_COUNTER,
277 				&pipe_perf_reg_read
278 			},
279 			{
280 				"CULLED_COUNTER",
281 				VIVS_MC_PROFILE_CONFIG1_PA_CULLED_COUNTER,
282 				&pipe_perf_reg_read
283 			}
284 		}
285 	},
286 	{
287 		.name = "SE",
288 		.profile_read = VIVS_MC_PROFILE_SE_READ,
289 		.profile_config = VIVS_MC_PROFILE_CONFIG1,
290 		.nr_signals = 2,
291 		.signal = (const struct etnaviv_pm_signal[]) {
292 			{
293 				"CULLED_TRIANGLE_COUNT",
294 				VIVS_MC_PROFILE_CONFIG1_SE_CULLED_TRIANGLE_COUNT,
295 				&perf_reg_read
296 			},
297 			{
298 				"CULLED_LINES_COUNT",
299 				VIVS_MC_PROFILE_CONFIG1_SE_CULLED_LINES_COUNT,
300 				&perf_reg_read
301 			}
302 		}
303 	},
304 	{
305 		.name = "RA",
306 		.profile_read = VIVS_MC_PROFILE_RA_READ,
307 		.profile_config = VIVS_MC_PROFILE_CONFIG1,
308 		.nr_signals = 7,
309 		.signal = (const struct etnaviv_pm_signal[]) {
310 			{
311 				"VALID_PIXEL_COUNT",
312 				VIVS_MC_PROFILE_CONFIG1_RA_VALID_PIXEL_COUNT,
313 				&perf_reg_read
314 			},
315 			{
316 				"TOTAL_QUAD_COUNT",
317 				VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_QUAD_COUNT,
318 				&perf_reg_read
319 			},
320 			{
321 				"VALID_QUAD_COUNT_AFTER_EARLY_Z",
322 				VIVS_MC_PROFILE_CONFIG1_RA_VALID_QUAD_COUNT_AFTER_EARLY_Z,
323 				&perf_reg_read
324 			},
325 			{
326 				"TOTAL_PRIMITIVE_COUNT",
327 				VIVS_MC_PROFILE_CONFIG1_RA_TOTAL_PRIMITIVE_COUNT,
328 				&perf_reg_read
329 			},
330 			{
331 				"PIPE_CACHE_MISS_COUNTER",
332 				VIVS_MC_PROFILE_CONFIG1_RA_PIPE_CACHE_MISS_COUNTER,
333 				&perf_reg_read
334 			},
335 			{
336 				"PREFETCH_CACHE_MISS_COUNTER",
337 				VIVS_MC_PROFILE_CONFIG1_RA_PREFETCH_CACHE_MISS_COUNTER,
338 				&perf_reg_read
339 			},
340 			{
341 				"CULLED_QUAD_COUNT",
342 				VIVS_MC_PROFILE_CONFIG1_RA_CULLED_QUAD_COUNT,
343 				&perf_reg_read
344 			}
345 		}
346 	},
347 	{
348 		.name = "TX",
349 		.profile_read = VIVS_MC_PROFILE_TX_READ,
350 		.profile_config = VIVS_MC_PROFILE_CONFIG1,
351 		.nr_signals = 9,
352 		.signal = (const struct etnaviv_pm_signal[]) {
353 			{
354 				"TOTAL_BILINEAR_REQUESTS",
355 				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_BILINEAR_REQUESTS,
356 				&perf_reg_read
357 			},
358 			{
359 				"TOTAL_TRILINEAR_REQUESTS",
360 				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TRILINEAR_REQUESTS,
361 				&perf_reg_read
362 			},
363 			{
364 				"TOTAL_DISCARDED_TEXTURE_REQUESTS",
365 				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_DISCARDED_TEXTURE_REQUESTS,
366 				&perf_reg_read
367 			},
368 			{
369 				"TOTAL_TEXTURE_REQUESTS",
370 				VIVS_MC_PROFILE_CONFIG1_TX_TOTAL_TEXTURE_REQUESTS,
371 				&perf_reg_read
372 			},
373 			{
374 				"MEM_READ_COUNT",
375 				VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_COUNT,
376 				&perf_reg_read
377 			},
378 			{
379 				"MEM_READ_IN_8B_COUNT",
380 				VIVS_MC_PROFILE_CONFIG1_TX_MEM_READ_IN_8B_COUNT,
381 				&perf_reg_read
382 			},
383 			{
384 				"CACHE_MISS_COUNT",
385 				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_COUNT,
386 				&perf_reg_read
387 			},
388 			{
389 				"CACHE_HIT_TEXEL_COUNT",
390 				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_HIT_TEXEL_COUNT,
391 				&perf_reg_read
392 			},
393 			{
394 				"CACHE_MISS_TEXEL_COUNT",
395 				VIVS_MC_PROFILE_CONFIG1_TX_CACHE_MISS_TEXEL_COUNT,
396 				&perf_reg_read
397 			}
398 		}
399 	},
400 	{
401 		.name = "MC",
402 		.profile_read = VIVS_MC_PROFILE_MC_READ,
403 		.profile_config = VIVS_MC_PROFILE_CONFIG2,
404 		.nr_signals = 3,
405 		.signal = (const struct etnaviv_pm_signal[]) {
406 			{
407 				"TOTAL_READ_REQ_8B_FROM_PIPELINE",
408 				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_PIPELINE,
409 				&perf_reg_read
410 			},
411 			{
412 				"TOTAL_READ_REQ_8B_FROM_IP",
413 				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_READ_REQ_8B_FROM_IP,
414 				&perf_reg_read
415 			},
416 			{
417 				"TOTAL_WRITE_REQ_8B_FROM_PIPELINE",
418 				VIVS_MC_PROFILE_CONFIG2_MC_TOTAL_WRITE_REQ_8B_FROM_PIPELINE,
419 				&perf_reg_read
420 			}
421 		}
422 	}
423 };
424 
425 static const struct etnaviv_pm_domain doms_2d[] = {
426 	{
427 		.name = "PE",
428 		.profile_read = VIVS_MC_PROFILE_PE_READ,
429 		.profile_config = VIVS_MC_PROFILE_CONFIG0,
430 		.nr_signals = 1,
431 		.signal = (const struct etnaviv_pm_signal[]) {
432 			{
433 				"PIXELS_RENDERED_2D",
434 				VIVS_MC_PROFILE_CONFIG0_PE_PIXELS_RENDERED_2D,
435 				&pipe_perf_reg_read
436 			}
437 		}
438 	}
439 };
440 
441 static const struct etnaviv_pm_domain doms_vg[] = {
442 };
443 
444 static const struct etnaviv_pm_domain_meta doms_meta[] = {
445 	{
446 		.feature = chipFeatures_PIPE_3D,
447 		.nr_domains = ARRAY_SIZE(doms_3d),
448 		.domains = &doms_3d[0]
449 	},
450 	{
451 		.feature = chipFeatures_PIPE_2D,
452 		.nr_domains = ARRAY_SIZE(doms_2d),
453 		.domains = &doms_2d[0]
454 	},
455 	{
456 		.feature = chipFeatures_PIPE_VG,
457 		.nr_domains = ARRAY_SIZE(doms_vg),
458 		.domains = &doms_vg[0]
459 	}
460 };
461 
num_pm_domains(const struct etnaviv_gpu * gpu)462 static unsigned int num_pm_domains(const struct etnaviv_gpu *gpu)
463 {
464 	unsigned int num = 0, i;
465 
466 	for (i = 0; i < ARRAY_SIZE(doms_meta); i++) {
467 		const struct etnaviv_pm_domain_meta *meta = &doms_meta[i];
468 
469 		if (gpu->identity.features & meta->feature)
470 			num += meta->nr_domains;
471 	}
472 
473 	return num;
474 }
475 
pm_domain(const struct etnaviv_gpu * gpu,unsigned int index)476 static const struct etnaviv_pm_domain *pm_domain(const struct etnaviv_gpu *gpu,
477 	unsigned int index)
478 {
479 	const struct etnaviv_pm_domain *domain = NULL;
480 	unsigned int offset = 0, i;
481 
482 	for (i = 0; i < ARRAY_SIZE(doms_meta); i++) {
483 		const struct etnaviv_pm_domain_meta *meta = &doms_meta[i];
484 
485 		if (!(gpu->identity.features & meta->feature))
486 			continue;
487 
488 		if (index - offset >= meta->nr_domains) {
489 			offset += meta->nr_domains;
490 			continue;
491 		}
492 
493 		domain = meta->domains + (index - offset);
494 	}
495 
496 	return domain;
497 }
498 
etnaviv_pm_query_dom(struct etnaviv_gpu * gpu,struct drm_etnaviv_pm_domain * domain)499 int etnaviv_pm_query_dom(struct etnaviv_gpu *gpu,
500 	struct drm_etnaviv_pm_domain *domain)
501 {
502 	const unsigned int nr_domains = num_pm_domains(gpu);
503 	const struct etnaviv_pm_domain *dom;
504 
505 	if (domain->iter >= nr_domains)
506 		return -EINVAL;
507 
508 	dom = pm_domain(gpu, domain->iter);
509 	if (!dom)
510 		return -EINVAL;
511 
512 	domain->id = domain->iter;
513 	domain->nr_signals = dom->nr_signals;
514 	strncpy(domain->name, dom->name, sizeof(domain->name));
515 
516 	domain->iter++;
517 	if (domain->iter == nr_domains)
518 		domain->iter = 0xff;
519 
520 	return 0;
521 }
522 
etnaviv_pm_query_sig(struct etnaviv_gpu * gpu,struct drm_etnaviv_pm_signal * signal)523 int etnaviv_pm_query_sig(struct etnaviv_gpu *gpu,
524 	struct drm_etnaviv_pm_signal *signal)
525 {
526 	const unsigned int nr_domains = num_pm_domains(gpu);
527 	const struct etnaviv_pm_domain *dom;
528 	const struct etnaviv_pm_signal *sig;
529 
530 	if (signal->domain >= nr_domains)
531 		return -EINVAL;
532 
533 	dom = pm_domain(gpu, signal->domain);
534 	if (!dom)
535 		return -EINVAL;
536 
537 	if (signal->iter >= dom->nr_signals)
538 		return -EINVAL;
539 
540 	sig = &dom->signal[signal->iter];
541 
542 	signal->id = signal->iter;
543 	strncpy(signal->name, sig->name, sizeof(signal->name));
544 
545 	signal->iter++;
546 	if (signal->iter == dom->nr_signals)
547 		signal->iter = 0xffff;
548 
549 	return 0;
550 }
551 
etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr * r,u32 exec_state)552 int etnaviv_pm_req_validate(const struct drm_etnaviv_gem_submit_pmr *r,
553 	u32 exec_state)
554 {
555 	const struct etnaviv_pm_domain_meta *meta = &doms_meta[exec_state];
556 	const struct etnaviv_pm_domain *dom;
557 
558 	if (r->domain >= meta->nr_domains)
559 		return -EINVAL;
560 
561 	dom = meta->domains + r->domain;
562 
563 	if (r->signal >= dom->nr_signals)
564 		return -EINVAL;
565 
566 	return 0;
567 }
568 
etnaviv_perfmon_process(struct etnaviv_gpu * gpu,const struct etnaviv_perfmon_request * pmr,u32 exec_state)569 void etnaviv_perfmon_process(struct etnaviv_gpu *gpu,
570 	const struct etnaviv_perfmon_request *pmr, u32 exec_state)
571 {
572 	const struct etnaviv_pm_domain_meta *meta = &doms_meta[exec_state];
573 	const struct etnaviv_pm_domain *dom;
574 	const struct etnaviv_pm_signal *sig;
575 	u32 *bo = pmr->bo_vma;
576 	u32 val;
577 
578 	dom = meta->domains + pmr->domain;
579 	sig = &dom->signal[pmr->signal];
580 	val = sig->sample(gpu, dom, sig);
581 
582 	*(bo + pmr->offset) = val;
583 }
584