18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* pci_common.c: PCI controller common support.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net)
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/string.h>
88c2ecf20Sopenharmony_ci#include <linux/slab.h>
98c2ecf20Sopenharmony_ci#include <linux/pci.h>
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/of_device.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <asm/prom.h>
148c2ecf20Sopenharmony_ci#include <asm/oplib.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "pci_impl.h"
178c2ecf20Sopenharmony_ci#include "pci_sun4v.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int config_out_of_range(struct pci_pbm_info *pbm,
208c2ecf20Sopenharmony_ci			       unsigned long bus,
218c2ecf20Sopenharmony_ci			       unsigned long devfn,
228c2ecf20Sopenharmony_ci			       unsigned long reg)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	if (bus < pbm->pci_first_busno ||
258c2ecf20Sopenharmony_ci	    bus > pbm->pci_last_busno)
268c2ecf20Sopenharmony_ci		return 1;
278c2ecf20Sopenharmony_ci	return 0;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void *sun4u_config_mkaddr(struct pci_pbm_info *pbm,
318c2ecf20Sopenharmony_ci				 unsigned long bus,
328c2ecf20Sopenharmony_ci				 unsigned long devfn,
338c2ecf20Sopenharmony_ci				 unsigned long reg)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	unsigned long rbits = pbm->config_space_reg_bits;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	if (config_out_of_range(pbm, bus, devfn, reg))
388c2ecf20Sopenharmony_ci		return NULL;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	reg = (reg & ((1 << rbits) - 1));
418c2ecf20Sopenharmony_ci	devfn <<= rbits;
428c2ecf20Sopenharmony_ci	bus <<= rbits + 8;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	return (void *)	(pbm->config_space | bus | devfn | reg);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* At least on Sabre, it is necessary to access all PCI host controller
488c2ecf20Sopenharmony_ci * registers at their natural size, otherwise zeros are returned.
498c2ecf20Sopenharmony_ci * Strange but true, and I see no language in the UltraSPARC-IIi
508c2ecf20Sopenharmony_ci * programmer's manual that mentions this even indirectly.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_cistatic int sun4u_read_pci_cfg_host(struct pci_pbm_info *pbm,
538c2ecf20Sopenharmony_ci				   unsigned char bus, unsigned int devfn,
548c2ecf20Sopenharmony_ci				   int where, int size, u32 *value)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	u32 tmp32, *addr;
578c2ecf20Sopenharmony_ci	u16 tmp16;
588c2ecf20Sopenharmony_ci	u8 tmp8;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
618c2ecf20Sopenharmony_ci	if (!addr)
628c2ecf20Sopenharmony_ci		return PCIBIOS_SUCCESSFUL;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	switch (size) {
658c2ecf20Sopenharmony_ci	case 1:
668c2ecf20Sopenharmony_ci		if (where < 8) {
678c2ecf20Sopenharmony_ci			unsigned long align = (unsigned long) addr;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci			align &= ~1;
708c2ecf20Sopenharmony_ci			pci_config_read16((u16 *)align, &tmp16);
718c2ecf20Sopenharmony_ci			if (where & 1)
728c2ecf20Sopenharmony_ci				*value = tmp16 >> 8;
738c2ecf20Sopenharmony_ci			else
748c2ecf20Sopenharmony_ci				*value = tmp16 & 0xff;
758c2ecf20Sopenharmony_ci		} else {
768c2ecf20Sopenharmony_ci			pci_config_read8((u8 *)addr, &tmp8);
778c2ecf20Sopenharmony_ci			*value = (u32) tmp8;
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	case 2:
828c2ecf20Sopenharmony_ci		if (where < 8) {
838c2ecf20Sopenharmony_ci			pci_config_read16((u16 *)addr, &tmp16);
848c2ecf20Sopenharmony_ci			*value = (u32) tmp16;
858c2ecf20Sopenharmony_ci		} else {
868c2ecf20Sopenharmony_ci			pci_config_read8((u8 *)addr, &tmp8);
878c2ecf20Sopenharmony_ci			*value = (u32) tmp8;
888c2ecf20Sopenharmony_ci			pci_config_read8(((u8 *)addr) + 1, &tmp8);
898c2ecf20Sopenharmony_ci			*value |= ((u32) tmp8) << 8;
908c2ecf20Sopenharmony_ci		}
918c2ecf20Sopenharmony_ci		break;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	case 4:
948c2ecf20Sopenharmony_ci		tmp32 = 0xffffffff;
958c2ecf20Sopenharmony_ci		sun4u_read_pci_cfg_host(pbm, bus, devfn,
968c2ecf20Sopenharmony_ci					where, 2, &tmp32);
978c2ecf20Sopenharmony_ci		*value = tmp32;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		tmp32 = 0xffffffff;
1008c2ecf20Sopenharmony_ci		sun4u_read_pci_cfg_host(pbm, bus, devfn,
1018c2ecf20Sopenharmony_ci					where + 2, 2, &tmp32);
1028c2ecf20Sopenharmony_ci		*value |= tmp32 << 16;
1038c2ecf20Sopenharmony_ci		break;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int sun4u_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
1098c2ecf20Sopenharmony_ci			      int where, int size, u32 *value)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct pci_pbm_info *pbm = bus_dev->sysdata;
1128c2ecf20Sopenharmony_ci	unsigned char bus = bus_dev->number;
1138c2ecf20Sopenharmony_ci	u32 *addr;
1148c2ecf20Sopenharmony_ci	u16 tmp16;
1158c2ecf20Sopenharmony_ci	u8 tmp8;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	switch (size) {
1188c2ecf20Sopenharmony_ci	case 1:
1198c2ecf20Sopenharmony_ci		*value = 0xff;
1208c2ecf20Sopenharmony_ci		break;
1218c2ecf20Sopenharmony_ci	case 2:
1228c2ecf20Sopenharmony_ci		*value = 0xffff;
1238c2ecf20Sopenharmony_ci		break;
1248c2ecf20Sopenharmony_ci	case 4:
1258c2ecf20Sopenharmony_ci		*value = 0xffffffff;
1268c2ecf20Sopenharmony_ci		break;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (!bus_dev->number && !PCI_SLOT(devfn))
1308c2ecf20Sopenharmony_ci		return sun4u_read_pci_cfg_host(pbm, bus, devfn, where,
1318c2ecf20Sopenharmony_ci					       size, value);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
1348c2ecf20Sopenharmony_ci	if (!addr)
1358c2ecf20Sopenharmony_ci		return PCIBIOS_SUCCESSFUL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	switch (size) {
1388c2ecf20Sopenharmony_ci	case 1:
1398c2ecf20Sopenharmony_ci		pci_config_read8((u8 *)addr, &tmp8);
1408c2ecf20Sopenharmony_ci		*value = (u32) tmp8;
1418c2ecf20Sopenharmony_ci		break;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	case 2:
1448c2ecf20Sopenharmony_ci		if (where & 0x01) {
1458c2ecf20Sopenharmony_ci			printk("pci_read_config_word: misaligned reg [%x]\n",
1468c2ecf20Sopenharmony_ci			       where);
1478c2ecf20Sopenharmony_ci			return PCIBIOS_SUCCESSFUL;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci		pci_config_read16((u16 *)addr, &tmp16);
1508c2ecf20Sopenharmony_ci		*value = (u32) tmp16;
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	case 4:
1548c2ecf20Sopenharmony_ci		if (where & 0x03) {
1558c2ecf20Sopenharmony_ci			printk("pci_read_config_dword: misaligned reg [%x]\n",
1568c2ecf20Sopenharmony_ci			       where);
1578c2ecf20Sopenharmony_ci			return PCIBIOS_SUCCESSFUL;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci		pci_config_read32(addr, value);
1608c2ecf20Sopenharmony_ci		break;
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic int sun4u_write_pci_cfg_host(struct pci_pbm_info *pbm,
1668c2ecf20Sopenharmony_ci				    unsigned char bus, unsigned int devfn,
1678c2ecf20Sopenharmony_ci				    int where, int size, u32 value)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	u32 *addr;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
1728c2ecf20Sopenharmony_ci	if (!addr)
1738c2ecf20Sopenharmony_ci		return PCIBIOS_SUCCESSFUL;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	switch (size) {
1768c2ecf20Sopenharmony_ci	case 1:
1778c2ecf20Sopenharmony_ci		if (where < 8) {
1788c2ecf20Sopenharmony_ci			unsigned long align = (unsigned long) addr;
1798c2ecf20Sopenharmony_ci			u16 tmp16;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci			align &= ~1;
1828c2ecf20Sopenharmony_ci			pci_config_read16((u16 *)align, &tmp16);
1838c2ecf20Sopenharmony_ci			if (where & 1) {
1848c2ecf20Sopenharmony_ci				tmp16 &= 0x00ff;
1858c2ecf20Sopenharmony_ci				tmp16 |= value << 8;
1868c2ecf20Sopenharmony_ci			} else {
1878c2ecf20Sopenharmony_ci				tmp16 &= 0xff00;
1888c2ecf20Sopenharmony_ci				tmp16 |= value;
1898c2ecf20Sopenharmony_ci			}
1908c2ecf20Sopenharmony_ci			pci_config_write16((u16 *)align, tmp16);
1918c2ecf20Sopenharmony_ci		} else
1928c2ecf20Sopenharmony_ci			pci_config_write8((u8 *)addr, value);
1938c2ecf20Sopenharmony_ci		break;
1948c2ecf20Sopenharmony_ci	case 2:
1958c2ecf20Sopenharmony_ci		if (where < 8) {
1968c2ecf20Sopenharmony_ci			pci_config_write16((u16 *)addr, value);
1978c2ecf20Sopenharmony_ci		} else {
1988c2ecf20Sopenharmony_ci			pci_config_write8((u8 *)addr, value & 0xff);
1998c2ecf20Sopenharmony_ci			pci_config_write8(((u8 *)addr) + 1, value >> 8);
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci		break;
2028c2ecf20Sopenharmony_ci	case 4:
2038c2ecf20Sopenharmony_ci		sun4u_write_pci_cfg_host(pbm, bus, devfn,
2048c2ecf20Sopenharmony_ci					 where, 2, value & 0xffff);
2058c2ecf20Sopenharmony_ci		sun4u_write_pci_cfg_host(pbm, bus, devfn,
2068c2ecf20Sopenharmony_ci					 where + 2, 2, value >> 16);
2078c2ecf20Sopenharmony_ci		break;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_cistatic int sun4u_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
2138c2ecf20Sopenharmony_ci			       int where, int size, u32 value)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct pci_pbm_info *pbm = bus_dev->sysdata;
2168c2ecf20Sopenharmony_ci	unsigned char bus = bus_dev->number;
2178c2ecf20Sopenharmony_ci	u32 *addr;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (!bus_dev->number && !PCI_SLOT(devfn))
2208c2ecf20Sopenharmony_ci		return sun4u_write_pci_cfg_host(pbm, bus, devfn, where,
2218c2ecf20Sopenharmony_ci						size, value);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	addr = sun4u_config_mkaddr(pbm, bus, devfn, where);
2248c2ecf20Sopenharmony_ci	if (!addr)
2258c2ecf20Sopenharmony_ci		return PCIBIOS_SUCCESSFUL;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	switch (size) {
2288c2ecf20Sopenharmony_ci	case 1:
2298c2ecf20Sopenharmony_ci		pci_config_write8((u8 *)addr, value);
2308c2ecf20Sopenharmony_ci		break;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	case 2:
2338c2ecf20Sopenharmony_ci		if (where & 0x01) {
2348c2ecf20Sopenharmony_ci			printk("pci_write_config_word: misaligned reg [%x]\n",
2358c2ecf20Sopenharmony_ci			       where);
2368c2ecf20Sopenharmony_ci			return PCIBIOS_SUCCESSFUL;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci		pci_config_write16((u16 *)addr, value);
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	case 4:
2428c2ecf20Sopenharmony_ci		if (where & 0x03) {
2438c2ecf20Sopenharmony_ci			printk("pci_write_config_dword: misaligned reg [%x]\n",
2448c2ecf20Sopenharmony_ci			       where);
2458c2ecf20Sopenharmony_ci			return PCIBIOS_SUCCESSFUL;
2468c2ecf20Sopenharmony_ci		}
2478c2ecf20Sopenharmony_ci		pci_config_write32(addr, value);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2508c2ecf20Sopenharmony_ci}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_cistruct pci_ops sun4u_pci_ops = {
2538c2ecf20Sopenharmony_ci	.read =		sun4u_read_pci_cfg,
2548c2ecf20Sopenharmony_ci	.write =	sun4u_write_pci_cfg,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic int sun4v_read_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
2588c2ecf20Sopenharmony_ci			      int where, int size, u32 *value)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct pci_pbm_info *pbm = bus_dev->sysdata;
2618c2ecf20Sopenharmony_ci	u32 devhandle = pbm->devhandle;
2628c2ecf20Sopenharmony_ci	unsigned int bus = bus_dev->number;
2638c2ecf20Sopenharmony_ci	unsigned int device = PCI_SLOT(devfn);
2648c2ecf20Sopenharmony_ci	unsigned int func = PCI_FUNC(devfn);
2658c2ecf20Sopenharmony_ci	unsigned long ret;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	if (config_out_of_range(pbm, bus, devfn, where)) {
2688c2ecf20Sopenharmony_ci		ret = ~0UL;
2698c2ecf20Sopenharmony_ci	} else {
2708c2ecf20Sopenharmony_ci		ret = pci_sun4v_config_get(devhandle,
2718c2ecf20Sopenharmony_ci				HV_PCI_DEVICE_BUILD(bus, device, func),
2728c2ecf20Sopenharmony_ci				where, size);
2738c2ecf20Sopenharmony_ci	}
2748c2ecf20Sopenharmony_ci	switch (size) {
2758c2ecf20Sopenharmony_ci	case 1:
2768c2ecf20Sopenharmony_ci		*value = ret & 0xff;
2778c2ecf20Sopenharmony_ci		break;
2788c2ecf20Sopenharmony_ci	case 2:
2798c2ecf20Sopenharmony_ci		*value = ret & 0xffff;
2808c2ecf20Sopenharmony_ci		break;
2818c2ecf20Sopenharmony_ci	case 4:
2828c2ecf20Sopenharmony_ci		*value = ret & 0xffffffff;
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2888c2ecf20Sopenharmony_ci}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int sun4v_write_pci_cfg(struct pci_bus *bus_dev, unsigned int devfn,
2918c2ecf20Sopenharmony_ci			       int where, int size, u32 value)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct pci_pbm_info *pbm = bus_dev->sysdata;
2948c2ecf20Sopenharmony_ci	u32 devhandle = pbm->devhandle;
2958c2ecf20Sopenharmony_ci	unsigned int bus = bus_dev->number;
2968c2ecf20Sopenharmony_ci	unsigned int device = PCI_SLOT(devfn);
2978c2ecf20Sopenharmony_ci	unsigned int func = PCI_FUNC(devfn);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (config_out_of_range(pbm, bus, devfn, where)) {
3008c2ecf20Sopenharmony_ci		/* Do nothing. */
3018c2ecf20Sopenharmony_ci	} else {
3028c2ecf20Sopenharmony_ci		/* We don't check for hypervisor errors here, but perhaps
3038c2ecf20Sopenharmony_ci		 * we should and influence our return value depending upon
3048c2ecf20Sopenharmony_ci		 * what kind of error is thrown.
3058c2ecf20Sopenharmony_ci		 */
3068c2ecf20Sopenharmony_ci		pci_sun4v_config_put(devhandle,
3078c2ecf20Sopenharmony_ci				     HV_PCI_DEVICE_BUILD(bus, device, func),
3088c2ecf20Sopenharmony_ci				     where, size, value);
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_cistruct pci_ops sun4v_pci_ops = {
3148c2ecf20Sopenharmony_ci	.read =		sun4v_read_pci_cfg,
3158c2ecf20Sopenharmony_ci	.write =	sun4v_write_pci_cfg,
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_civoid pci_get_pbm_props(struct pci_pbm_info *pbm)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	const u32 *val = of_get_property(pbm->op->dev.of_node, "bus-range", NULL);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	pbm->pci_first_busno = val[0];
3238c2ecf20Sopenharmony_ci	pbm->pci_last_busno = val[1];
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	val = of_get_property(pbm->op->dev.of_node, "ino-bitmap", NULL);
3268c2ecf20Sopenharmony_ci	if (val) {
3278c2ecf20Sopenharmony_ci		pbm->ino_bitmap = (((u64)val[1] << 32UL) |
3288c2ecf20Sopenharmony_ci				   ((u64)val[0] <<  0UL));
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void pci_register_iommu_region(struct pci_pbm_info *pbm)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	const u32 *vdma = of_get_property(pbm->op->dev.of_node, "virtual-dma",
3358c2ecf20Sopenharmony_ci					  NULL);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (vdma) {
3388c2ecf20Sopenharmony_ci		struct resource *rp = kzalloc(sizeof(*rp), GFP_KERNEL);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		if (!rp) {
3418c2ecf20Sopenharmony_ci			pr_info("%s: Cannot allocate IOMMU resource.\n",
3428c2ecf20Sopenharmony_ci				pbm->name);
3438c2ecf20Sopenharmony_ci			return;
3448c2ecf20Sopenharmony_ci		}
3458c2ecf20Sopenharmony_ci		rp->name = "IOMMU";
3468c2ecf20Sopenharmony_ci		rp->start = pbm->mem_space.start + (unsigned long) vdma[0];
3478c2ecf20Sopenharmony_ci		rp->end = rp->start + (unsigned long) vdma[1] - 1UL;
3488c2ecf20Sopenharmony_ci		rp->flags = IORESOURCE_BUSY;
3498c2ecf20Sopenharmony_ci		if (request_resource(&pbm->mem_space, rp)) {
3508c2ecf20Sopenharmony_ci			pr_info("%s: Unable to request IOMMU resource.\n",
3518c2ecf20Sopenharmony_ci				pbm->name);
3528c2ecf20Sopenharmony_ci			kfree(rp);
3538c2ecf20Sopenharmony_ci		}
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_civoid pci_determine_mem_io_space(struct pci_pbm_info *pbm)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	const struct linux_prom_pci_ranges *pbm_ranges;
3608c2ecf20Sopenharmony_ci	int i, saw_mem, saw_io;
3618c2ecf20Sopenharmony_ci	int num_pbm_ranges;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	/* Corresponding generic code in of_pci_get_host_bridge_resources() */
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	saw_mem = saw_io = 0;
3668c2ecf20Sopenharmony_ci	pbm_ranges = of_get_property(pbm->op->dev.of_node, "ranges", &i);
3678c2ecf20Sopenharmony_ci	if (!pbm_ranges) {
3688c2ecf20Sopenharmony_ci		prom_printf("PCI: Fatal error, missing PBM ranges property "
3698c2ecf20Sopenharmony_ci			    " for %s\n",
3708c2ecf20Sopenharmony_ci			    pbm->name);
3718c2ecf20Sopenharmony_ci		prom_halt();
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	num_pbm_ranges = i / sizeof(*pbm_ranges);
3758c2ecf20Sopenharmony_ci	memset(&pbm->mem64_space, 0, sizeof(struct resource));
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	for (i = 0; i < num_pbm_ranges; i++) {
3788c2ecf20Sopenharmony_ci		const struct linux_prom_pci_ranges *pr = &pbm_ranges[i];
3798c2ecf20Sopenharmony_ci		unsigned long a, size, region_a;
3808c2ecf20Sopenharmony_ci		u32 parent_phys_hi, parent_phys_lo;
3818c2ecf20Sopenharmony_ci		u32 child_phys_mid, child_phys_lo;
3828c2ecf20Sopenharmony_ci		u32 size_hi, size_lo;
3838c2ecf20Sopenharmony_ci		int type;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		parent_phys_hi = pr->parent_phys_hi;
3868c2ecf20Sopenharmony_ci		parent_phys_lo = pr->parent_phys_lo;
3878c2ecf20Sopenharmony_ci		child_phys_mid = pr->child_phys_mid;
3888c2ecf20Sopenharmony_ci		child_phys_lo = pr->child_phys_lo;
3898c2ecf20Sopenharmony_ci		if (tlb_type == hypervisor)
3908c2ecf20Sopenharmony_ci			parent_phys_hi &= 0x0fffffff;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		size_hi = pr->size_hi;
3938c2ecf20Sopenharmony_ci		size_lo = pr->size_lo;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci		type = (pr->child_phys_hi >> 24) & 0x3;
3968c2ecf20Sopenharmony_ci		a = (((unsigned long)parent_phys_hi << 32UL) |
3978c2ecf20Sopenharmony_ci		     ((unsigned long)parent_phys_lo  <<  0UL));
3988c2ecf20Sopenharmony_ci		region_a = (((unsigned long)child_phys_mid << 32UL) |
3998c2ecf20Sopenharmony_ci		     ((unsigned long)child_phys_lo  <<  0UL));
4008c2ecf20Sopenharmony_ci		size = (((unsigned long)size_hi << 32UL) |
4018c2ecf20Sopenharmony_ci			((unsigned long)size_lo  <<  0UL));
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		switch (type) {
4048c2ecf20Sopenharmony_ci		case 0:
4058c2ecf20Sopenharmony_ci			/* PCI config space, 16MB */
4068c2ecf20Sopenharmony_ci			pbm->config_space = a;
4078c2ecf20Sopenharmony_ci			break;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci		case 1:
4108c2ecf20Sopenharmony_ci			/* 16-bit IO space, 16MB */
4118c2ecf20Sopenharmony_ci			pbm->io_space.start = a;
4128c2ecf20Sopenharmony_ci			pbm->io_space.end = a + size - 1UL;
4138c2ecf20Sopenharmony_ci			pbm->io_space.flags = IORESOURCE_IO;
4148c2ecf20Sopenharmony_ci			pbm->io_offset = a - region_a;
4158c2ecf20Sopenharmony_ci			saw_io = 1;
4168c2ecf20Sopenharmony_ci			break;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci		case 2:
4198c2ecf20Sopenharmony_ci			/* 32-bit MEM space, 2GB */
4208c2ecf20Sopenharmony_ci			pbm->mem_space.start = a;
4218c2ecf20Sopenharmony_ci			pbm->mem_space.end = a + size - 1UL;
4228c2ecf20Sopenharmony_ci			pbm->mem_space.flags = IORESOURCE_MEM;
4238c2ecf20Sopenharmony_ci			pbm->mem_offset = a - region_a;
4248c2ecf20Sopenharmony_ci			saw_mem = 1;
4258c2ecf20Sopenharmony_ci			break;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		case 3:
4288c2ecf20Sopenharmony_ci			/* 64-bit MEM handling */
4298c2ecf20Sopenharmony_ci			pbm->mem64_space.start = a;
4308c2ecf20Sopenharmony_ci			pbm->mem64_space.end = a + size - 1UL;
4318c2ecf20Sopenharmony_ci			pbm->mem64_space.flags = IORESOURCE_MEM;
4328c2ecf20Sopenharmony_ci			pbm->mem64_offset = a - region_a;
4338c2ecf20Sopenharmony_ci			saw_mem = 1;
4348c2ecf20Sopenharmony_ci			break;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		default:
4378c2ecf20Sopenharmony_ci			break;
4388c2ecf20Sopenharmony_ci		}
4398c2ecf20Sopenharmony_ci	}
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (!saw_io || !saw_mem) {
4428c2ecf20Sopenharmony_ci		prom_printf("%s: Fatal error, missing %s PBM range.\n",
4438c2ecf20Sopenharmony_ci			    pbm->name,
4448c2ecf20Sopenharmony_ci			    (!saw_io ? "IO" : "MEM"));
4458c2ecf20Sopenharmony_ci		prom_halt();
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (pbm->io_space.flags)
4498c2ecf20Sopenharmony_ci		printk("%s: PCI IO %pR offset %llx\n",
4508c2ecf20Sopenharmony_ci		       pbm->name, &pbm->io_space, pbm->io_offset);
4518c2ecf20Sopenharmony_ci	if (pbm->mem_space.flags)
4528c2ecf20Sopenharmony_ci		printk("%s: PCI MEM %pR offset %llx\n",
4538c2ecf20Sopenharmony_ci		       pbm->name, &pbm->mem_space, pbm->mem_offset);
4548c2ecf20Sopenharmony_ci	if (pbm->mem64_space.flags && pbm->mem_space.flags) {
4558c2ecf20Sopenharmony_ci		if (pbm->mem64_space.start <= pbm->mem_space.end)
4568c2ecf20Sopenharmony_ci			pbm->mem64_space.start = pbm->mem_space.end + 1;
4578c2ecf20Sopenharmony_ci		if (pbm->mem64_space.start > pbm->mem64_space.end)
4588c2ecf20Sopenharmony_ci			pbm->mem64_space.flags = 0;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	if (pbm->mem64_space.flags)
4628c2ecf20Sopenharmony_ci		printk("%s: PCI MEM64 %pR offset %llx\n",
4638c2ecf20Sopenharmony_ci		       pbm->name, &pbm->mem64_space, pbm->mem64_offset);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	pbm->io_space.name = pbm->mem_space.name = pbm->name;
4668c2ecf20Sopenharmony_ci	pbm->mem64_space.name = pbm->name;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	request_resource(&ioport_resource, &pbm->io_space);
4698c2ecf20Sopenharmony_ci	request_resource(&iomem_resource, &pbm->mem_space);
4708c2ecf20Sopenharmony_ci	if (pbm->mem64_space.flags)
4718c2ecf20Sopenharmony_ci		request_resource(&iomem_resource, &pbm->mem64_space);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	pci_register_iommu_region(pbm);
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci/* Generic helper routines for PCI error reporting. */
4778c2ecf20Sopenharmony_civoid pci_scan_for_target_abort(struct pci_pbm_info *pbm,
4788c2ecf20Sopenharmony_ci			       struct pci_bus *pbus)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
4818c2ecf20Sopenharmony_ci	struct pci_bus *bus;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	list_for_each_entry(pdev, &pbus->devices, bus_list) {
4848c2ecf20Sopenharmony_ci		u16 status, error_bits;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		pci_read_config_word(pdev, PCI_STATUS, &status);
4878c2ecf20Sopenharmony_ci		error_bits =
4888c2ecf20Sopenharmony_ci			(status & (PCI_STATUS_SIG_TARGET_ABORT |
4898c2ecf20Sopenharmony_ci				   PCI_STATUS_REC_TARGET_ABORT));
4908c2ecf20Sopenharmony_ci		if (error_bits) {
4918c2ecf20Sopenharmony_ci			pci_write_config_word(pdev, PCI_STATUS, error_bits);
4928c2ecf20Sopenharmony_ci			pci_info(pdev, "%s: Device saw Target Abort [%016x]\n",
4938c2ecf20Sopenharmony_ci				 pbm->name, status);
4948c2ecf20Sopenharmony_ci		}
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	list_for_each_entry(bus, &pbus->children, node)
4988c2ecf20Sopenharmony_ci		pci_scan_for_target_abort(pbm, bus);
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_civoid pci_scan_for_master_abort(struct pci_pbm_info *pbm,
5028c2ecf20Sopenharmony_ci			       struct pci_bus *pbus)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
5058c2ecf20Sopenharmony_ci	struct pci_bus *bus;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	list_for_each_entry(pdev, &pbus->devices, bus_list) {
5088c2ecf20Sopenharmony_ci		u16 status, error_bits;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci		pci_read_config_word(pdev, PCI_STATUS, &status);
5118c2ecf20Sopenharmony_ci		error_bits =
5128c2ecf20Sopenharmony_ci			(status & (PCI_STATUS_REC_MASTER_ABORT));
5138c2ecf20Sopenharmony_ci		if (error_bits) {
5148c2ecf20Sopenharmony_ci			pci_write_config_word(pdev, PCI_STATUS, error_bits);
5158c2ecf20Sopenharmony_ci			pci_info(pdev, "%s: Device received Master Abort "
5168c2ecf20Sopenharmony_ci				 "[%016x]\n", pbm->name, status);
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	list_for_each_entry(bus, &pbus->children, node)
5218c2ecf20Sopenharmony_ci		pci_scan_for_master_abort(pbm, bus);
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_civoid pci_scan_for_parity_error(struct pci_pbm_info *pbm,
5258c2ecf20Sopenharmony_ci			       struct pci_bus *pbus)
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	struct pci_dev *pdev;
5288c2ecf20Sopenharmony_ci	struct pci_bus *bus;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	list_for_each_entry(pdev, &pbus->devices, bus_list) {
5318c2ecf20Sopenharmony_ci		u16 status, error_bits;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		pci_read_config_word(pdev, PCI_STATUS, &status);
5348c2ecf20Sopenharmony_ci		error_bits =
5358c2ecf20Sopenharmony_ci			(status & (PCI_STATUS_PARITY |
5368c2ecf20Sopenharmony_ci				   PCI_STATUS_DETECTED_PARITY));
5378c2ecf20Sopenharmony_ci		if (error_bits) {
5388c2ecf20Sopenharmony_ci			pci_write_config_word(pdev, PCI_STATUS, error_bits);
5398c2ecf20Sopenharmony_ci			pci_info(pdev, "%s: Device saw Parity Error [%016x]\n",
5408c2ecf20Sopenharmony_ci				 pbm->name, status);
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	list_for_each_entry(bus, &pbus->children, node)
5458c2ecf20Sopenharmony_ci		pci_scan_for_parity_error(pbm, bus);
5468c2ecf20Sopenharmony_ci}
547