18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 1999, 2000, 2004, 2005	 MIPS Technologies, Inc.
48c2ecf20Sopenharmony_ci *	All rights reserved.
58c2ecf20Sopenharmony_ci *	Authors: Carsten Langgaard <carstenl@mips.com>
68c2ecf20Sopenharmony_ci *		 Maciej W. Rozycki <macro@mips.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci * MIPS boards specific PCI support.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci#include <linux/types.h>
138c2ecf20Sopenharmony_ci#include <linux/pci.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/gt64120.h>
188c2ecf20Sopenharmony_ci#include <asm/mips-cps.h>
198c2ecf20Sopenharmony_ci#include <asm/mips-boards/generic.h>
208c2ecf20Sopenharmony_ci#include <asm/mips-boards/bonito64.h>
218c2ecf20Sopenharmony_ci#include <asm/mips-boards/msc01_pci.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic struct resource bonito64_mem_resource = {
248c2ecf20Sopenharmony_ci	.name	= "Bonito PCI MEM",
258c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_MEM,
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic struct resource bonito64_io_resource = {
298c2ecf20Sopenharmony_ci	.name	= "Bonito PCI I/O",
308c2ecf20Sopenharmony_ci	.start	= 0x00000000UL,
318c2ecf20Sopenharmony_ci	.end	= 0x000fffffUL,
328c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_IO,
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic struct resource gt64120_mem_resource = {
368c2ecf20Sopenharmony_ci	.name	= "GT-64120 PCI MEM",
378c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_MEM,
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic struct resource gt64120_io_resource = {
418c2ecf20Sopenharmony_ci	.name	= "GT-64120 PCI I/O",
428c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_IO,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic struct resource msc_mem_resource = {
468c2ecf20Sopenharmony_ci	.name	= "MSC PCI MEM",
478c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_MEM,
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct resource msc_io_resource = {
518c2ecf20Sopenharmony_ci	.name	= "MSC PCI I/O",
528c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_IO,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciextern struct pci_ops bonito64_pci_ops;
568c2ecf20Sopenharmony_ciextern struct pci_ops gt64xxx_pci0_ops;
578c2ecf20Sopenharmony_ciextern struct pci_ops msc_pci_ops;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct pci_controller bonito64_controller = {
608c2ecf20Sopenharmony_ci	.pci_ops	= &bonito64_pci_ops,
618c2ecf20Sopenharmony_ci	.io_resource	= &bonito64_io_resource,
628c2ecf20Sopenharmony_ci	.mem_resource	= &bonito64_mem_resource,
638c2ecf20Sopenharmony_ci	.io_offset	= 0x00000000UL,
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct pci_controller gt64120_controller = {
678c2ecf20Sopenharmony_ci	.pci_ops	= &gt64xxx_pci0_ops,
688c2ecf20Sopenharmony_ci	.io_resource	= &gt64120_io_resource,
698c2ecf20Sopenharmony_ci	.mem_resource	= &gt64120_mem_resource,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic struct pci_controller msc_controller = {
738c2ecf20Sopenharmony_ci	.pci_ops	= &msc_pci_ops,
748c2ecf20Sopenharmony_ci	.io_resource	= &msc_io_resource,
758c2ecf20Sopenharmony_ci	.mem_resource	= &msc_mem_resource,
768c2ecf20Sopenharmony_ci};
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_civoid __init mips_pcibios_init(void)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	struct pci_controller *controller;
818c2ecf20Sopenharmony_ci	resource_size_t start, end, map, start1, end1, map1, map2, map3, mask;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	switch (mips_revision_sconid) {
848c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_GT64120:
858c2ecf20Sopenharmony_ci		/*
868c2ecf20Sopenharmony_ci		 * Due to a bug in the Galileo system controller, we need
878c2ecf20Sopenharmony_ci		 * to setup the PCI BAR for the Galileo internal registers.
888c2ecf20Sopenharmony_ci		 * This should be done in the bios/bootprom and will be
898c2ecf20Sopenharmony_ci		 * fixed in a later revision of YAMON (the MIPS boards
908c2ecf20Sopenharmony_ci		 * boot prom).
918c2ecf20Sopenharmony_ci		 */
928c2ecf20Sopenharmony_ci		GT_WRITE(GT_PCI0_CFGADDR_OFS,
938c2ecf20Sopenharmony_ci			 (0 << GT_PCI0_CFGADDR_BUSNUM_SHF) | /* Local bus */
948c2ecf20Sopenharmony_ci			 (0 << GT_PCI0_CFGADDR_DEVNUM_SHF) | /* GT64120 dev */
958c2ecf20Sopenharmony_ci			 (0 << GT_PCI0_CFGADDR_FUNCTNUM_SHF) | /* Function 0*/
968c2ecf20Sopenharmony_ci			 ((0x20/4) << GT_PCI0_CFGADDR_REGNUM_SHF) | /* BAR 4*/
978c2ecf20Sopenharmony_ci			 GT_PCI0_CFGADDR_CONFIGEN_BIT);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci		/* Perform the write */
1008c2ecf20Sopenharmony_ci		GT_WRITE(GT_PCI0_CFGDATA_OFS, CPHYSADDR(MIPS_GT_BASE));
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		/* Set up resource ranges from the controller's registers.  */
1038c2ecf20Sopenharmony_ci		start = GT_READ(GT_PCI0M0LD_OFS);
1048c2ecf20Sopenharmony_ci		end = GT_READ(GT_PCI0M0HD_OFS);
1058c2ecf20Sopenharmony_ci		map = GT_READ(GT_PCI0M0REMAP_OFS);
1068c2ecf20Sopenharmony_ci		end = (end & GT_PCI_HD_MSK) | (start & ~GT_PCI_HD_MSK);
1078c2ecf20Sopenharmony_ci		start1 = GT_READ(GT_PCI0M1LD_OFS);
1088c2ecf20Sopenharmony_ci		end1 = GT_READ(GT_PCI0M1HD_OFS);
1098c2ecf20Sopenharmony_ci		map1 = GT_READ(GT_PCI0M1REMAP_OFS);
1108c2ecf20Sopenharmony_ci		end1 = (end1 & GT_PCI_HD_MSK) | (start1 & ~GT_PCI_HD_MSK);
1118c2ecf20Sopenharmony_ci		/* Cannot support multiple windows, use the wider.  */
1128c2ecf20Sopenharmony_ci		if (end1 - start1 > end - start) {
1138c2ecf20Sopenharmony_ci			start = start1;
1148c2ecf20Sopenharmony_ci			end = end1;
1158c2ecf20Sopenharmony_ci			map = map1;
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci		mask = ~(start ^ end);
1188c2ecf20Sopenharmony_ci		/* We don't support remapping with a discontiguous mask.  */
1198c2ecf20Sopenharmony_ci		BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
1208c2ecf20Sopenharmony_ci		       mask != ~((mask & -mask) - 1));
1218c2ecf20Sopenharmony_ci		gt64120_mem_resource.start = start;
1228c2ecf20Sopenharmony_ci		gt64120_mem_resource.end = end;
1238c2ecf20Sopenharmony_ci		gt64120_controller.mem_offset = (start & mask) - (map & mask);
1248c2ecf20Sopenharmony_ci		/* Addresses are 36-bit, so do shifts in the destinations.  */
1258c2ecf20Sopenharmony_ci		gt64120_mem_resource.start <<= GT_PCI_DCRM_SHF;
1268c2ecf20Sopenharmony_ci		gt64120_mem_resource.end <<= GT_PCI_DCRM_SHF;
1278c2ecf20Sopenharmony_ci		gt64120_mem_resource.end |= (1 << GT_PCI_DCRM_SHF) - 1;
1288c2ecf20Sopenharmony_ci		gt64120_controller.mem_offset <<= GT_PCI_DCRM_SHF;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		start = GT_READ(GT_PCI0IOLD_OFS);
1318c2ecf20Sopenharmony_ci		end = GT_READ(GT_PCI0IOHD_OFS);
1328c2ecf20Sopenharmony_ci		map = GT_READ(GT_PCI0IOREMAP_OFS);
1338c2ecf20Sopenharmony_ci		end = (end & GT_PCI_HD_MSK) | (start & ~GT_PCI_HD_MSK);
1348c2ecf20Sopenharmony_ci		mask = ~(start ^ end);
1358c2ecf20Sopenharmony_ci		/* We don't support remapping with a discontiguous mask.  */
1368c2ecf20Sopenharmony_ci		BUG_ON((start & GT_PCI_HD_MSK) != (map & GT_PCI_HD_MSK) &&
1378c2ecf20Sopenharmony_ci		       mask != ~((mask & -mask) - 1));
1388c2ecf20Sopenharmony_ci		gt64120_io_resource.start = map & mask;
1398c2ecf20Sopenharmony_ci		gt64120_io_resource.end = (map & mask) | ~mask;
1408c2ecf20Sopenharmony_ci		gt64120_controller.io_offset = 0;
1418c2ecf20Sopenharmony_ci		/* Addresses are 36-bit, so do shifts in the destinations.  */
1428c2ecf20Sopenharmony_ci		gt64120_io_resource.start <<= GT_PCI_DCRM_SHF;
1438c2ecf20Sopenharmony_ci		gt64120_io_resource.end <<= GT_PCI_DCRM_SHF;
1448c2ecf20Sopenharmony_ci		gt64120_io_resource.end |= (1 << GT_PCI_DCRM_SHF) - 1;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		controller = &gt64120_controller;
1478c2ecf20Sopenharmony_ci		break;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_BONITO:
1508c2ecf20Sopenharmony_ci		/* Set up resource ranges from the controller's registers.  */
1518c2ecf20Sopenharmony_ci		map = BONITO_PCIMAP;
1528c2ecf20Sopenharmony_ci		map1 = (BONITO_PCIMAP & BONITO_PCIMAP_PCIMAP_LO0) >>
1538c2ecf20Sopenharmony_ci		       BONITO_PCIMAP_PCIMAP_LO0_SHIFT;
1548c2ecf20Sopenharmony_ci		map2 = (BONITO_PCIMAP & BONITO_PCIMAP_PCIMAP_LO1) >>
1558c2ecf20Sopenharmony_ci		       BONITO_PCIMAP_PCIMAP_LO1_SHIFT;
1568c2ecf20Sopenharmony_ci		map3 = (BONITO_PCIMAP & BONITO_PCIMAP_PCIMAP_LO2) >>
1578c2ecf20Sopenharmony_ci		       BONITO_PCIMAP_PCIMAP_LO2_SHIFT;
1588c2ecf20Sopenharmony_ci		/* Combine as many adjacent windows as possible.  */
1598c2ecf20Sopenharmony_ci		map = map1;
1608c2ecf20Sopenharmony_ci		start = BONITO_PCILO0_BASE;
1618c2ecf20Sopenharmony_ci		end = 1;
1628c2ecf20Sopenharmony_ci		if (map3 == map2 + 1) {
1638c2ecf20Sopenharmony_ci			map = map2;
1648c2ecf20Sopenharmony_ci			start = BONITO_PCILO1_BASE;
1658c2ecf20Sopenharmony_ci			end++;
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci		if (map2 == map1 + 1) {
1688c2ecf20Sopenharmony_ci			map = map1;
1698c2ecf20Sopenharmony_ci			start = BONITO_PCILO0_BASE;
1708c2ecf20Sopenharmony_ci			end++;
1718c2ecf20Sopenharmony_ci		}
1728c2ecf20Sopenharmony_ci		bonito64_mem_resource.start = start;
1738c2ecf20Sopenharmony_ci		bonito64_mem_resource.end = start +
1748c2ecf20Sopenharmony_ci					    BONITO_PCIMAP_WINBASE(end) - 1;
1758c2ecf20Sopenharmony_ci		bonito64_controller.mem_offset = start -
1768c2ecf20Sopenharmony_ci						 BONITO_PCIMAP_WINBASE(map);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		controller = &bonito64_controller;
1798c2ecf20Sopenharmony_ci		break;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCIT:
1828c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_ROCIT:
1838c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSC:
1848c2ecf20Sopenharmony_ci	case MIPS_REVISION_SCON_SOCITSCP:
1858c2ecf20Sopenharmony_ci		/* Set up resource ranges from the controller's registers.  */
1868c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_SC2PMBASL, start);
1878c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_SC2PMMSKL, mask);
1888c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_SC2PMMAPL, map);
1898c2ecf20Sopenharmony_ci		msc_mem_resource.start = start & mask;
1908c2ecf20Sopenharmony_ci		msc_mem_resource.end = (start & mask) | ~mask;
1918c2ecf20Sopenharmony_ci		msc_controller.mem_offset = (start & mask) - (map & mask);
1928c2ecf20Sopenharmony_ci		if (mips_cps_numiocu(0)) {
1938c2ecf20Sopenharmony_ci			write_gcr_reg0_base(start);
1948c2ecf20Sopenharmony_ci			write_gcr_reg0_mask(mask |
1958c2ecf20Sopenharmony_ci					    CM_GCR_REGn_MASK_CMTGT_IOCU0);
1968c2ecf20Sopenharmony_ci		}
1978c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_SC2PIOBASL, start);
1988c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_SC2PIOMSKL, mask);
1998c2ecf20Sopenharmony_ci		MSC_READ(MSC01_PCI_SC2PIOMAPL, map);
2008c2ecf20Sopenharmony_ci		msc_io_resource.start = map & mask;
2018c2ecf20Sopenharmony_ci		msc_io_resource.end = (map & mask) | ~mask;
2028c2ecf20Sopenharmony_ci		msc_controller.io_offset = 0;
2038c2ecf20Sopenharmony_ci		ioport_resource.end = ~mask;
2048c2ecf20Sopenharmony_ci		if (mips_cps_numiocu(0)) {
2058c2ecf20Sopenharmony_ci			write_gcr_reg1_base(start);
2068c2ecf20Sopenharmony_ci			write_gcr_reg1_mask(mask |
2078c2ecf20Sopenharmony_ci					    CM_GCR_REGn_MASK_CMTGT_IOCU0);
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci		/* If ranges overlap I/O takes precedence.  */
2108c2ecf20Sopenharmony_ci		start = start & mask;
2118c2ecf20Sopenharmony_ci		end = start | ~mask;
2128c2ecf20Sopenharmony_ci		if ((start >= msc_mem_resource.start &&
2138c2ecf20Sopenharmony_ci		     start <= msc_mem_resource.end) ||
2148c2ecf20Sopenharmony_ci		    (end >= msc_mem_resource.start &&
2158c2ecf20Sopenharmony_ci		     end <= msc_mem_resource.end)) {
2168c2ecf20Sopenharmony_ci			/* Use the larger space.  */
2178c2ecf20Sopenharmony_ci			start = max(start, msc_mem_resource.start);
2188c2ecf20Sopenharmony_ci			end = min(end, msc_mem_resource.end);
2198c2ecf20Sopenharmony_ci			if (start - msc_mem_resource.start >=
2208c2ecf20Sopenharmony_ci			    msc_mem_resource.end - end)
2218c2ecf20Sopenharmony_ci				msc_mem_resource.end = start - 1;
2228c2ecf20Sopenharmony_ci			else
2238c2ecf20Sopenharmony_ci				msc_mem_resource.start = end + 1;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		controller = &msc_controller;
2278c2ecf20Sopenharmony_ci		break;
2288c2ecf20Sopenharmony_ci	default:
2298c2ecf20Sopenharmony_ci		return;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	/* PIIX4 ACPI starts at 0x1000 */
2338c2ecf20Sopenharmony_ci	if (controller->io_resource->start < 0x00001000UL)
2348c2ecf20Sopenharmony_ci		controller->io_resource->start = 0x00001000UL;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	iomem_resource.end &= 0xfffffffffULL;			/* 64 GB */
2378c2ecf20Sopenharmony_ci	ioport_resource.end = controller->io_resource->end;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	controller->io_map_base = mips_io_port_base;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	register_pci_controller(controller);
2428c2ecf20Sopenharmony_ci}
243