18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  pci-vr41xx.c, PCI Control Unit routines for the NEC VR4100 series.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (C) 2001-2003 MontaVista Software Inc.
68c2ecf20Sopenharmony_ci *    Author: Yoichi Yuasa <source@mvista.com>
78c2ecf20Sopenharmony_ci *  Copyright (C) 2004-2008  Yoichi Yuasa <yuasa@linux-mips.org>
88c2ecf20Sopenharmony_ci *  Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * Changes:
128c2ecf20Sopenharmony_ci *  MontaVista Software Inc. <source@mvista.com>
138c2ecf20Sopenharmony_ci *  - New creation, NEC VR4122 and VR4131 are supported.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <linux/types.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <asm/cpu.h>
208c2ecf20Sopenharmony_ci#include <asm/io.h>
218c2ecf20Sopenharmony_ci#include <asm/vr41xx/pci.h>
228c2ecf20Sopenharmony_ci#include <asm/vr41xx/vr41xx.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include "pci-vr41xx.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciextern struct pci_ops vr41xx_pci_ops;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void __iomem *pciu_base;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define pciu_read(offset)		readl(pciu_base + (offset))
318c2ecf20Sopenharmony_ci#define pciu_write(offset, value)	writel((value), pciu_base + (offset))
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic struct pci_master_address_conversion pci_master_memory1 = {
348c2ecf20Sopenharmony_ci	.bus_base_address	= PCI_MASTER_MEM1_BUS_BASE_ADDRESS,
358c2ecf20Sopenharmony_ci	.address_mask		= PCI_MASTER_MEM1_ADDRESS_MASK,
368c2ecf20Sopenharmony_ci	.pci_base_address	= PCI_MASTER_MEM1_PCI_BASE_ADDRESS,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic struct pci_target_address_conversion pci_target_memory1 = {
408c2ecf20Sopenharmony_ci	.address_mask		= PCI_TARGET_MEM1_ADDRESS_MASK,
418c2ecf20Sopenharmony_ci	.bus_base_address	= PCI_TARGET_MEM1_BUS_BASE_ADDRESS,
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic struct pci_master_address_conversion pci_master_io = {
458c2ecf20Sopenharmony_ci	.bus_base_address	= PCI_MASTER_IO_BUS_BASE_ADDRESS,
468c2ecf20Sopenharmony_ci	.address_mask		= PCI_MASTER_IO_ADDRESS_MASK,
478c2ecf20Sopenharmony_ci	.pci_base_address	= PCI_MASTER_IO_PCI_BASE_ADDRESS,
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct pci_mailbox_address pci_mailbox = {
518c2ecf20Sopenharmony_ci	.base_address		= PCI_MAILBOX_BASE_ADDRESS,
528c2ecf20Sopenharmony_ci};
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic struct pci_target_address_window pci_target_window1 = {
558c2ecf20Sopenharmony_ci	.base_address		= PCI_TARGET_WINDOW1_BASE_ADDRESS,
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct resource pci_mem_resource = {
598c2ecf20Sopenharmony_ci	.name	= "PCI Memory resources",
608c2ecf20Sopenharmony_ci	.start	= PCI_MEM_RESOURCE_START,
618c2ecf20Sopenharmony_ci	.end	= PCI_MEM_RESOURCE_END,
628c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_MEM,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct resource pci_io_resource = {
668c2ecf20Sopenharmony_ci	.name	= "PCI I/O resources",
678c2ecf20Sopenharmony_ci	.start	= PCI_IO_RESOURCE_START,
688c2ecf20Sopenharmony_ci	.end	= PCI_IO_RESOURCE_END,
698c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_IO,
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic struct pci_controller_unit_setup vr41xx_pci_controller_unit_setup = {
738c2ecf20Sopenharmony_ci	.master_memory1				= &pci_master_memory1,
748c2ecf20Sopenharmony_ci	.target_memory1				= &pci_target_memory1,
758c2ecf20Sopenharmony_ci	.master_io				= &pci_master_io,
768c2ecf20Sopenharmony_ci	.exclusive_access			= CANNOT_LOCK_FROM_DEVICE,
778c2ecf20Sopenharmony_ci	.wait_time_limit_from_irdy_to_trdy	= 0,
788c2ecf20Sopenharmony_ci	.mailbox				= &pci_mailbox,
798c2ecf20Sopenharmony_ci	.target_window1				= &pci_target_window1,
808c2ecf20Sopenharmony_ci	.master_latency_timer			= 0x80,
818c2ecf20Sopenharmony_ci	.retry_limit				= 0,
828c2ecf20Sopenharmony_ci	.arbiter_priority_control		= PCI_ARBITRATION_MODE_FAIR,
838c2ecf20Sopenharmony_ci	.take_away_gnt_mode			= PCI_TAKE_AWAY_GNT_DISABLE,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct pci_controller vr41xx_pci_controller = {
878c2ecf20Sopenharmony_ci	.pci_ops	= &vr41xx_pci_ops,
888c2ecf20Sopenharmony_ci	.mem_resource	= &pci_mem_resource,
898c2ecf20Sopenharmony_ci	.io_resource	= &pci_io_resource,
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_civoid __init vr41xx_pciu_setup(struct pci_controller_unit_setup *setup)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	vr41xx_pci_controller_unit_setup = *setup;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int __init vr41xx_pciu_init(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct pci_controller_unit_setup *setup;
1008c2ecf20Sopenharmony_ci	struct pci_master_address_conversion *master;
1018c2ecf20Sopenharmony_ci	struct pci_target_address_conversion *target;
1028c2ecf20Sopenharmony_ci	struct pci_mailbox_address *mailbox;
1038c2ecf20Sopenharmony_ci	struct pci_target_address_window *window;
1048c2ecf20Sopenharmony_ci	unsigned long vtclock, pci_clock_max;
1058c2ecf20Sopenharmony_ci	uint32_t val;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	setup = &vr41xx_pci_controller_unit_setup;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (request_mem_region(PCIU_BASE, PCIU_SIZE, "PCIU") == NULL)
1108c2ecf20Sopenharmony_ci		return -EBUSY;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	pciu_base = ioremap(PCIU_BASE, PCIU_SIZE);
1138c2ecf20Sopenharmony_ci	if (pciu_base == NULL) {
1148c2ecf20Sopenharmony_ci		release_mem_region(PCIU_BASE, PCIU_SIZE);
1158c2ecf20Sopenharmony_ci		return -EBUSY;
1168c2ecf20Sopenharmony_ci	}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* Disable PCI interrupt */
1198c2ecf20Sopenharmony_ci	vr41xx_disable_pciint();
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* Supply VTClock to PCIU */
1228c2ecf20Sopenharmony_ci	vr41xx_supply_clock(PCIU_CLOCK);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	/* Dummy write, waiting for supply of VTClock. */
1258c2ecf20Sopenharmony_ci	vr41xx_disable_pciint();
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Select PCI clock */
1288c2ecf20Sopenharmony_ci	if (setup->pci_clock_max != 0)
1298c2ecf20Sopenharmony_ci		pci_clock_max = setup->pci_clock_max;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		pci_clock_max = PCI_CLOCK_MAX;
1328c2ecf20Sopenharmony_ci	vtclock = vr41xx_get_vtclock_frequency();
1338c2ecf20Sopenharmony_ci	if (vtclock < pci_clock_max)
1348c2ecf20Sopenharmony_ci		pciu_write(PCICLKSELREG, EQUAL_VTCLOCK);
1358c2ecf20Sopenharmony_ci	else if ((vtclock / 2) < pci_clock_max)
1368c2ecf20Sopenharmony_ci		pciu_write(PCICLKSELREG, HALF_VTCLOCK);
1378c2ecf20Sopenharmony_ci	else if (current_cpu_data.processor_id >= PRID_VR4131_REV2_1 &&
1388c2ecf20Sopenharmony_ci		 (vtclock / 3) < pci_clock_max)
1398c2ecf20Sopenharmony_ci		pciu_write(PCICLKSELREG, ONE_THIRD_VTCLOCK);
1408c2ecf20Sopenharmony_ci	else if ((vtclock / 4) < pci_clock_max)
1418c2ecf20Sopenharmony_ci		pciu_write(PCICLKSELREG, QUARTER_VTCLOCK);
1428c2ecf20Sopenharmony_ci	else {
1438c2ecf20Sopenharmony_ci		printk(KERN_ERR "PCI Clock is over 33MHz.\n");
1448c2ecf20Sopenharmony_ci		iounmap(pciu_base);
1458c2ecf20Sopenharmony_ci		return -EINVAL;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/* Supply PCI clock by PCI bus */
1498c2ecf20Sopenharmony_ci	vr41xx_supply_clock(PCI_CLOCK);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	if (setup->master_memory1 != NULL) {
1528c2ecf20Sopenharmony_ci		master = setup->master_memory1;
1538c2ecf20Sopenharmony_ci		val = IBA(master->bus_base_address) |
1548c2ecf20Sopenharmony_ci		      MASTER_MSK(master->address_mask) |
1558c2ecf20Sopenharmony_ci		      WINEN |
1568c2ecf20Sopenharmony_ci		      PCIA(master->pci_base_address);
1578c2ecf20Sopenharmony_ci		pciu_write(PCIMMAW1REG, val);
1588c2ecf20Sopenharmony_ci	} else {
1598c2ecf20Sopenharmony_ci		val = pciu_read(PCIMMAW1REG);
1608c2ecf20Sopenharmony_ci		val &= ~WINEN;
1618c2ecf20Sopenharmony_ci		pciu_write(PCIMMAW1REG, val);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (setup->master_memory2 != NULL) {
1658c2ecf20Sopenharmony_ci		master = setup->master_memory2;
1668c2ecf20Sopenharmony_ci		val = IBA(master->bus_base_address) |
1678c2ecf20Sopenharmony_ci		      MASTER_MSK(master->address_mask) |
1688c2ecf20Sopenharmony_ci		      WINEN |
1698c2ecf20Sopenharmony_ci		      PCIA(master->pci_base_address);
1708c2ecf20Sopenharmony_ci		pciu_write(PCIMMAW2REG, val);
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci		val = pciu_read(PCIMMAW2REG);
1738c2ecf20Sopenharmony_ci		val &= ~WINEN;
1748c2ecf20Sopenharmony_ci		pciu_write(PCIMMAW2REG, val);
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (setup->target_memory1 != NULL) {
1788c2ecf20Sopenharmony_ci		target = setup->target_memory1;
1798c2ecf20Sopenharmony_ci		val = TARGET_MSK(target->address_mask) |
1808c2ecf20Sopenharmony_ci		      WINEN |
1818c2ecf20Sopenharmony_ci		      ITA(target->bus_base_address);
1828c2ecf20Sopenharmony_ci		pciu_write(PCITAW1REG, val);
1838c2ecf20Sopenharmony_ci	} else {
1848c2ecf20Sopenharmony_ci		val = pciu_read(PCITAW1REG);
1858c2ecf20Sopenharmony_ci		val &= ~WINEN;
1868c2ecf20Sopenharmony_ci		pciu_write(PCITAW1REG, val);
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (setup->target_memory2 != NULL) {
1908c2ecf20Sopenharmony_ci		target = setup->target_memory2;
1918c2ecf20Sopenharmony_ci		val = TARGET_MSK(target->address_mask) |
1928c2ecf20Sopenharmony_ci		      WINEN |
1938c2ecf20Sopenharmony_ci		      ITA(target->bus_base_address);
1948c2ecf20Sopenharmony_ci		pciu_write(PCITAW2REG, val);
1958c2ecf20Sopenharmony_ci	} else {
1968c2ecf20Sopenharmony_ci		val = pciu_read(PCITAW2REG);
1978c2ecf20Sopenharmony_ci		val &= ~WINEN;
1988c2ecf20Sopenharmony_ci		pciu_write(PCITAW2REG, val);
1998c2ecf20Sopenharmony_ci	}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (setup->master_io != NULL) {
2028c2ecf20Sopenharmony_ci		master = setup->master_io;
2038c2ecf20Sopenharmony_ci		val = IBA(master->bus_base_address) |
2048c2ecf20Sopenharmony_ci		      MASTER_MSK(master->address_mask) |
2058c2ecf20Sopenharmony_ci		      WINEN |
2068c2ecf20Sopenharmony_ci		      PCIIA(master->pci_base_address);
2078c2ecf20Sopenharmony_ci		pciu_write(PCIMIOAWREG, val);
2088c2ecf20Sopenharmony_ci	} else {
2098c2ecf20Sopenharmony_ci		val = pciu_read(PCIMIOAWREG);
2108c2ecf20Sopenharmony_ci		val &= ~WINEN;
2118c2ecf20Sopenharmony_ci		pciu_write(PCIMIOAWREG, val);
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (setup->exclusive_access == CANNOT_LOCK_FROM_DEVICE)
2158c2ecf20Sopenharmony_ci		pciu_write(PCIEXACCREG, UNLOCK);
2168c2ecf20Sopenharmony_ci	else
2178c2ecf20Sopenharmony_ci		pciu_write(PCIEXACCREG, 0);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	if (current_cpu_type() == CPU_VR4122)
2208c2ecf20Sopenharmony_ci		pciu_write(PCITRDYVREG, TRDYV(setup->wait_time_limit_from_irdy_to_trdy));
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	pciu_write(LATTIMEREG, MLTIM(setup->master_latency_timer));
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (setup->mailbox != NULL) {
2258c2ecf20Sopenharmony_ci		mailbox = setup->mailbox;
2268c2ecf20Sopenharmony_ci		val = MBADD(mailbox->base_address) | TYPE_32BITSPACE |
2278c2ecf20Sopenharmony_ci		      MSI_MEMORY | PREF_APPROVAL;
2288c2ecf20Sopenharmony_ci		pciu_write(MAILBAREG, val);
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	if (setup->target_window1) {
2328c2ecf20Sopenharmony_ci		window = setup->target_window1;
2338c2ecf20Sopenharmony_ci		val = PMBA(window->base_address) | TYPE_32BITSPACE |
2348c2ecf20Sopenharmony_ci		      MSI_MEMORY | PREF_APPROVAL;
2358c2ecf20Sopenharmony_ci		pciu_write(PCIMBA1REG, val);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	if (setup->target_window2) {
2398c2ecf20Sopenharmony_ci		window = setup->target_window2;
2408c2ecf20Sopenharmony_ci		val = PMBA(window->base_address) | TYPE_32BITSPACE |
2418c2ecf20Sopenharmony_ci		      MSI_MEMORY | PREF_APPROVAL;
2428c2ecf20Sopenharmony_ci		pciu_write(PCIMBA2REG, val);
2438c2ecf20Sopenharmony_ci	}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	val = pciu_read(RETVALREG);
2468c2ecf20Sopenharmony_ci	val &= ~RTYVAL_MASK;
2478c2ecf20Sopenharmony_ci	val |= RTYVAL(setup->retry_limit);
2488c2ecf20Sopenharmony_ci	pciu_write(RETVALREG, val);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	val = pciu_read(PCIAPCNTREG);
2518c2ecf20Sopenharmony_ci	val &= ~(TKYGNT | PAPC);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	switch (setup->arbiter_priority_control) {
2548c2ecf20Sopenharmony_ci	case PCI_ARBITRATION_MODE_ALTERNATE_0:
2558c2ecf20Sopenharmony_ci		val |= PAPC_ALTERNATE_0;
2568c2ecf20Sopenharmony_ci		break;
2578c2ecf20Sopenharmony_ci	case PCI_ARBITRATION_MODE_ALTERNATE_B:
2588c2ecf20Sopenharmony_ci		val |= PAPC_ALTERNATE_B;
2598c2ecf20Sopenharmony_ci		break;
2608c2ecf20Sopenharmony_ci	default:
2618c2ecf20Sopenharmony_ci		val |= PAPC_FAIR;
2628c2ecf20Sopenharmony_ci		break;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	if (setup->take_away_gnt_mode == PCI_TAKE_AWAY_GNT_ENABLE)
2668c2ecf20Sopenharmony_ci		val |= TKYGNT_ENABLE;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	pciu_write(PCIAPCNTREG, val);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	pciu_write(COMMANDREG, PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
2718c2ecf20Sopenharmony_ci			       PCI_COMMAND_MASTER | PCI_COMMAND_PARITY |
2728c2ecf20Sopenharmony_ci			       PCI_COMMAND_SERR);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Clear bus error */
2758c2ecf20Sopenharmony_ci	pciu_read(BUSERRADREG);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	pciu_write(PCIENREG, PCIU_CONFIG_DONE);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (setup->mem_resource != NULL)
2808c2ecf20Sopenharmony_ci		vr41xx_pci_controller.mem_resource = setup->mem_resource;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (setup->io_resource != NULL) {
2838c2ecf20Sopenharmony_ci		vr41xx_pci_controller.io_resource = setup->io_resource;
2848c2ecf20Sopenharmony_ci	} else {
2858c2ecf20Sopenharmony_ci		set_io_port_base(IO_PORT_BASE);
2868c2ecf20Sopenharmony_ci		ioport_resource.start = IO_PORT_RESOURCE_START;
2878c2ecf20Sopenharmony_ci		ioport_resource.end = IO_PORT_RESOURCE_END;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (setup->master_io) {
2918c2ecf20Sopenharmony_ci		void __iomem *io_map_base;
2928c2ecf20Sopenharmony_ci		struct resource *res = vr41xx_pci_controller.io_resource;
2938c2ecf20Sopenharmony_ci		master = setup->master_io;
2948c2ecf20Sopenharmony_ci		io_map_base = ioremap(master->bus_base_address,
2958c2ecf20Sopenharmony_ci				      resource_size(res));
2968c2ecf20Sopenharmony_ci		if (!io_map_base)
2978c2ecf20Sopenharmony_ci			return -EBUSY;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci		vr41xx_pci_controller.io_map_base = (unsigned long)io_map_base;
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	register_pci_controller(&vr41xx_pci_controller);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ciarch_initcall(vr41xx_pciu_init);
308