162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ranges.c: Handle ranges in newer proms for obio/sbus. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) 662306a36Sopenharmony_ci * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <asm/openprom.h> 1362306a36Sopenharmony_ci#include <asm/oplib.h> 1462306a36Sopenharmony_ci#include <asm/types.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX]; 1762306a36Sopenharmony_cistatic int num_obio_ranges; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* Adjust register values based upon the ranges parameters. */ 2062306a36Sopenharmony_cistatic void prom_adjust_regs(struct linux_prom_registers *regp, int nregs, 2162306a36Sopenharmony_ci struct linux_prom_ranges *rangep, int nranges) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int regc, rngc; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci for (regc = 0; regc < nregs; regc++) { 2662306a36Sopenharmony_ci for (rngc = 0; rngc < nranges; rngc++) 2762306a36Sopenharmony_ci if (regp[regc].which_io == rangep[rngc].ot_child_space) 2862306a36Sopenharmony_ci break; /* Fount it */ 2962306a36Sopenharmony_ci if (rngc == nranges) /* oops */ 3062306a36Sopenharmony_ci prom_printf("adjust_regs: Could not find range with matching bus type...\n"); 3162306a36Sopenharmony_ci regp[regc].which_io = rangep[rngc].ot_parent_space; 3262306a36Sopenharmony_ci regp[regc].phys_addr -= rangep[rngc].ot_child_base; 3362306a36Sopenharmony_ci regp[regc].phys_addr += rangep[rngc].ot_parent_base; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1, 3862306a36Sopenharmony_ci struct linux_prom_ranges *ranges2, int nranges2) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci int rng1c, rng2c; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci for (rng1c = 0; rng1c < nranges1; rng1c++) { 4362306a36Sopenharmony_ci for (rng2c = 0; rng2c < nranges2; rng2c++) 4462306a36Sopenharmony_ci if (ranges1[rng1c].ot_parent_space == ranges2[rng2c].ot_child_space && 4562306a36Sopenharmony_ci ranges1[rng1c].ot_parent_base >= ranges2[rng2c].ot_child_base && 4662306a36Sopenharmony_ci ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base > 0U) 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci if (rng2c == nranges2) /* oops */ 4962306a36Sopenharmony_ci prom_printf("adjust_ranges: Could not find matching bus type...\n"); 5062306a36Sopenharmony_ci else if (ranges1[rng1c].ot_parent_base + ranges1[rng1c].or_size > ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size) 5162306a36Sopenharmony_ci ranges1[rng1c].or_size = ranges2[rng2c].ot_child_base + ranges2[rng2c].or_size - ranges1[rng1c].ot_parent_base; 5262306a36Sopenharmony_ci ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space; 5362306a36Sopenharmony_ci ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* Apply probed obio ranges to registers passed, if no ranges return. */ 5862306a36Sopenharmony_civoid prom_apply_obio_ranges(struct linux_prom_registers *regs, int nregs) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci if (num_obio_ranges) 6162306a36Sopenharmony_ci prom_adjust_regs(regs, nregs, promlib_obio_ranges, num_obio_ranges); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ciEXPORT_SYMBOL(prom_apply_obio_ranges); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_civoid __init prom_ranges_init(void) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci phandle node, obio_node; 6862306a36Sopenharmony_ci int success; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci num_obio_ranges = 0; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Check for obio and sbus ranges. */ 7362306a36Sopenharmony_ci node = prom_getchild(prom_root_node); 7462306a36Sopenharmony_ci obio_node = prom_searchsiblings(node, "obio"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (obio_node) { 7762306a36Sopenharmony_ci success = prom_getproperty(obio_node, "ranges", 7862306a36Sopenharmony_ci (char *) promlib_obio_ranges, 7962306a36Sopenharmony_ci sizeof(promlib_obio_ranges)); 8062306a36Sopenharmony_ci if (success != -1) 8162306a36Sopenharmony_ci num_obio_ranges = (success / sizeof(struct linux_prom_ranges)); 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (num_obio_ranges) 8562306a36Sopenharmony_ci prom_printf("PROMLIB: obio_ranges %d\n", num_obio_ranges); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_civoid prom_apply_generic_ranges(phandle node, phandle parent, 8962306a36Sopenharmony_ci struct linux_prom_registers *regs, int nregs) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci int success; 9262306a36Sopenharmony_ci int num_ranges; 9362306a36Sopenharmony_ci struct linux_prom_ranges ranges[PROMREG_MAX]; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci success = prom_getproperty(node, "ranges", 9662306a36Sopenharmony_ci (char *) ranges, 9762306a36Sopenharmony_ci sizeof(ranges)); 9862306a36Sopenharmony_ci if (success != -1) { 9962306a36Sopenharmony_ci num_ranges = (success / sizeof(struct linux_prom_ranges)); 10062306a36Sopenharmony_ci if (parent) { 10162306a36Sopenharmony_ci struct linux_prom_ranges parent_ranges[PROMREG_MAX]; 10262306a36Sopenharmony_ci int num_parent_ranges; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci success = prom_getproperty(parent, "ranges", 10562306a36Sopenharmony_ci (char *) parent_ranges, 10662306a36Sopenharmony_ci sizeof(parent_ranges)); 10762306a36Sopenharmony_ci if (success != -1) { 10862306a36Sopenharmony_ci num_parent_ranges = (success / sizeof(struct linux_prom_ranges)); 10962306a36Sopenharmony_ci prom_adjust_ranges(ranges, num_ranges, parent_ranges, num_parent_ranges); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci prom_adjust_regs(regs, nregs, ranges, num_ranges); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci} 115