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