18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ranges.c: Handle ranges in newer proms for obio/sbus.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
68c2ecf20Sopenharmony_ci * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <asm/openprom.h>
138c2ecf20Sopenharmony_ci#include <asm/oplib.h>
148c2ecf20Sopenharmony_ci#include <asm/types.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
178c2ecf20Sopenharmony_cistatic int num_obio_ranges;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Adjust register values based upon the ranges parameters. */
208c2ecf20Sopenharmony_cistatic void prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
218c2ecf20Sopenharmony_ci			     struct linux_prom_ranges *rangep, int nranges)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	int regc, rngc;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	for (regc = 0; regc < nregs; regc++) {
268c2ecf20Sopenharmony_ci		for (rngc = 0; rngc < nranges; rngc++)
278c2ecf20Sopenharmony_ci			if (regp[regc].which_io == rangep[rngc].ot_child_space)
288c2ecf20Sopenharmony_ci				break; /* Fount it */
298c2ecf20Sopenharmony_ci		if (rngc == nranges) /* oops */
308c2ecf20Sopenharmony_ci			prom_printf("adjust_regs: Could not find range with matching bus type...\n");
318c2ecf20Sopenharmony_ci		regp[regc].which_io = rangep[rngc].ot_parent_space;
328c2ecf20Sopenharmony_ci		regp[regc].phys_addr -= rangep[rngc].ot_child_base;
338c2ecf20Sopenharmony_ci		regp[regc].phys_addr += rangep[rngc].ot_parent_base;
348c2ecf20Sopenharmony_ci	}
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
388c2ecf20Sopenharmony_ci			       struct linux_prom_ranges *ranges2, int nranges2)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int rng1c, rng2c;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	for (rng1c = 0; rng1c < nranges1; rng1c++) {
438c2ecf20Sopenharmony_ci		for (rng2c = 0; rng2c < nranges2; rng2c++)
448c2ecf20Sopenharmony_ci			if (ranges1[rng1c].ot_parent_space == ranges2[rng2c].ot_child_space &&
458c2ecf20Sopenharmony_ci			   ranges1[rng1c].ot_parent_base >= ranges2[rng2c].ot_child_base &&
468c2ecf20Sopenharmony_ci			   ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base > 0U)
478c2ecf20Sopenharmony_ci			break;
488c2ecf20Sopenharmony_ci		if (rng2c == nranges2) /* oops */
498c2ecf20Sopenharmony_ci			prom_printf("adjust_ranges: Could not find matching bus type...\n");
508c2ecf20Sopenharmony_ci		else if (ranges1[rng1c].ot_parent_base + ranges1[rng1c].or_size > ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size)
518c2ecf20Sopenharmony_ci			ranges1[rng1c].or_size = ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base;
528c2ecf20Sopenharmony_ci		ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
538c2ecf20Sopenharmony_ci		ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/* Apply probed obio ranges to registers passed, if no ranges return. */
588c2ecf20Sopenharmony_civoid prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	if (num_obio_ranges)
618c2ecf20Sopenharmony_ci		prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges);
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ciEXPORT_SYMBOL(prom_apply_obio_ranges);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_civoid __init prom_ranges_init(void)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	phandle node, obio_node;
688c2ecf20Sopenharmony_ci	int success;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	num_obio_ranges = 0;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/* Check for obio and sbus ranges. */
738c2ecf20Sopenharmony_ci	node = prom_getchild(prom_root_node);
748c2ecf20Sopenharmony_ci	obio_node = prom_searchsiblings(node, "obio");
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (obio_node) {
778c2ecf20Sopenharmony_ci		success = prom_getproperty(obio_node, "ranges",
788c2ecf20Sopenharmony_ci					   (char *) promlib_obio_ranges,
798c2ecf20Sopenharmony_ci					   sizeof(promlib_obio_ranges));
808c2ecf20Sopenharmony_ci		if (success != -1)
818c2ecf20Sopenharmony_ci			num_obio_ranges = (success / sizeof(struct linux_prom_ranges));
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	if (num_obio_ranges)
858c2ecf20Sopenharmony_ci		prom_printf("PROMLIB: obio_ranges %d\n", num_obio_ranges);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid prom_apply_generic_ranges(phandle node, phandle parent,
898c2ecf20Sopenharmony_ci			       struct linux_prom_registers *regs, int nregs)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int success;
928c2ecf20Sopenharmony_ci	int num_ranges;
938c2ecf20Sopenharmony_ci	struct linux_prom_ranges ranges[PROMREG_MAX];
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	success = prom_getproperty(node, "ranges",
968c2ecf20Sopenharmony_ci				   (char *) ranges,
978c2ecf20Sopenharmony_ci				   sizeof(ranges));
988c2ecf20Sopenharmony_ci	if (success != -1) {
998c2ecf20Sopenharmony_ci		num_ranges = (success / sizeof(struct linux_prom_ranges));
1008c2ecf20Sopenharmony_ci		if (parent) {
1018c2ecf20Sopenharmony_ci			struct linux_prom_ranges parent_ranges[PROMREG_MAX];
1028c2ecf20Sopenharmony_ci			int num_parent_ranges;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci			success = prom_getproperty(parent, "ranges",
1058c2ecf20Sopenharmony_ci						   (char *) parent_ranges,
1068c2ecf20Sopenharmony_ci						   sizeof(parent_ranges));
1078c2ecf20Sopenharmony_ci			if (success != -1) {
1088c2ecf20Sopenharmony_ci				num_parent_ranges = (success / sizeof(struct linux_prom_ranges));
1098c2ecf20Sopenharmony_ci				prom_adjust_ranges(ranges, num_ranges, parent_ranges, num_parent_ranges);
1108c2ecf20Sopenharmony_ci			}
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci		prom_adjust_regs(regs, nregs, ranges, num_ranges);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci}
115