162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Low-Level PCI Support for the SH7751 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2003 - 2009 Paul Mundt 662306a36Sopenharmony_ci * Copyright (C) 2001 Dustin McIntire 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * With cleanup by Paul van Gool <pvangool@mimotech.com>, 2003. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/types.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include "pci-sh4.h" 1662306a36Sopenharmony_ci#include <asm/addrspace.h> 1762306a36Sopenharmony_ci#include <linux/sizes.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic int __init __area_sdram_check(struct pci_channel *chan, 2062306a36Sopenharmony_ci unsigned int area) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci unsigned long word; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci word = __raw_readl(SH7751_BCR1); 2562306a36Sopenharmony_ci /* check BCR for SDRAM in area */ 2662306a36Sopenharmony_ci if (((word >> area) & 1) == 0) { 2762306a36Sopenharmony_ci printk("PCI: Area %d is not configured for SDRAM. BCR1=0x%lx\n", 2862306a36Sopenharmony_ci area, word); 2962306a36Sopenharmony_ci return 0; 3062306a36Sopenharmony_ci } 3162306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIBCR1); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci word = __raw_readw(SH7751_BCR2); 3462306a36Sopenharmony_ci /* check BCR2 for 32bit SDRAM interface*/ 3562306a36Sopenharmony_ci if (((word >> (area << 1)) & 0x3) != 0x3) { 3662306a36Sopenharmony_ci printk("PCI: Area %d is not 32 bit SDRAM. BCR2=0x%lx\n", 3762306a36Sopenharmony_ci area, word); 3862306a36Sopenharmony_ci return 0; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIBCR2); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci return 1; 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic struct resource sh7751_pci_resources[] = { 4662306a36Sopenharmony_ci { 4762306a36Sopenharmony_ci .name = "SH7751_IO", 4862306a36Sopenharmony_ci .start = 0x1000, 4962306a36Sopenharmony_ci .end = SZ_4M - 1, 5062306a36Sopenharmony_ci .flags = IORESOURCE_IO 5162306a36Sopenharmony_ci }, { 5262306a36Sopenharmony_ci .name = "SH7751_mem", 5362306a36Sopenharmony_ci .start = SH7751_PCI_MEMORY_BASE, 5462306a36Sopenharmony_ci .end = SH7751_PCI_MEMORY_BASE + SH7751_PCI_MEM_SIZE - 1, 5562306a36Sopenharmony_ci .flags = IORESOURCE_MEM 5662306a36Sopenharmony_ci }, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic struct pci_channel sh7751_pci_controller = { 6062306a36Sopenharmony_ci .pci_ops = &sh4_pci_ops, 6162306a36Sopenharmony_ci .resources = sh7751_pci_resources, 6262306a36Sopenharmony_ci .nr_resources = ARRAY_SIZE(sh7751_pci_resources), 6362306a36Sopenharmony_ci .mem_offset = 0x00000000, 6462306a36Sopenharmony_ci .io_offset = 0x00000000, 6562306a36Sopenharmony_ci .io_map_base = SH7751_PCI_IO_BASE, 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic struct sh4_pci_address_map sh7751_pci_map = { 6962306a36Sopenharmony_ci .window0 = { 7062306a36Sopenharmony_ci .base = SH7751_CS3_BASE_ADDR, 7162306a36Sopenharmony_ci .size = 0x04000000, 7262306a36Sopenharmony_ci }, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int __init sh7751_pci_init(void) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci struct pci_channel *chan = &sh7751_pci_controller; 7862306a36Sopenharmony_ci unsigned int id; 7962306a36Sopenharmony_ci u32 word, reg; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci printk(KERN_NOTICE "PCI: Starting initialization.\n"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci chan->reg_base = 0xfe200000; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci /* check for SH7751/SH7751R hardware */ 8662306a36Sopenharmony_ci id = pci_read_reg(chan, SH7751_PCICONF0); 8762306a36Sopenharmony_ci if (id != ((SH7751_DEVICE_ID << 16) | SH7751_VENDOR_ID) && 8862306a36Sopenharmony_ci id != ((SH7751R_DEVICE_ID << 16) | SH7751_VENDOR_ID)) { 8962306a36Sopenharmony_ci pr_debug("PCI: This is not an SH7751(R) (%x)\n", id); 9062306a36Sopenharmony_ci return -ENODEV; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* Set the BCR's to enable PCI access */ 9462306a36Sopenharmony_ci reg = __raw_readl(SH7751_BCR1); 9562306a36Sopenharmony_ci reg |= 0x80000; 9662306a36Sopenharmony_ci __raw_writel(reg, SH7751_BCR1); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* Turn the clocks back on (not done in reset)*/ 9962306a36Sopenharmony_ci pci_write_reg(chan, 0, SH4_PCICLKR); 10062306a36Sopenharmony_ci /* Clear Powerdown IRQ's (not done in reset) */ 10162306a36Sopenharmony_ci word = SH4_PCIPINT_D3 | SH4_PCIPINT_D0; 10262306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIPINT); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* set the command/status bits to: 10562306a36Sopenharmony_ci * Wait Cycle Control + Parity Enable + Bus Master + 10662306a36Sopenharmony_ci * Mem space enable 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ci word = SH7751_PCICONF1_WCC | SH7751_PCICONF1_PER | 10962306a36Sopenharmony_ci SH7751_PCICONF1_BUM | SH7751_PCICONF1_MES; 11062306a36Sopenharmony_ci pci_write_reg(chan, word, SH7751_PCICONF1); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci /* define this host as the host bridge */ 11362306a36Sopenharmony_ci word = PCI_BASE_CLASS_BRIDGE << 24; 11462306a36Sopenharmony_ci pci_write_reg(chan, word, SH7751_PCICONF2); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci /* Set IO and Mem windows to local address 11762306a36Sopenharmony_ci * Make PCI and local address the same for easy 1 to 1 mapping 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci word = sh7751_pci_map.window0.size - 1; 12062306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCILSR0); 12162306a36Sopenharmony_ci /* Set the values on window 0 PCI config registers */ 12262306a36Sopenharmony_ci word = P2SEGADDR(sh7751_pci_map.window0.base); 12362306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCILAR0); 12462306a36Sopenharmony_ci pci_write_reg(chan, word, SH7751_PCICONF5); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Set the local 16MB PCI memory space window to 12762306a36Sopenharmony_ci * the lowest PCI mapped address 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci word = chan->resources[1].start & SH4_PCIMBR_MASK; 13062306a36Sopenharmony_ci pr_debug("PCI: Setting upper bits of Memory window to 0x%x\n", word); 13162306a36Sopenharmony_ci pci_write_reg(chan, word , SH4_PCIMBR); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Make sure the MSB's of IO window are set to access PCI space 13462306a36Sopenharmony_ci * correctly */ 13562306a36Sopenharmony_ci word = chan->resources[0].start & SH4_PCIIOBR_MASK; 13662306a36Sopenharmony_ci pr_debug("PCI: Setting upper bits of IO window to 0x%x\n", word); 13762306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIIOBR); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Set PCI WCRx, BCRx's, copy from BSC locations */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* check BCR for SDRAM in specified area */ 14262306a36Sopenharmony_ci switch (sh7751_pci_map.window0.base) { 14362306a36Sopenharmony_ci case SH7751_CS0_BASE_ADDR: word = __area_sdram_check(chan, 0); break; 14462306a36Sopenharmony_ci case SH7751_CS1_BASE_ADDR: word = __area_sdram_check(chan, 1); break; 14562306a36Sopenharmony_ci case SH7751_CS2_BASE_ADDR: word = __area_sdram_check(chan, 2); break; 14662306a36Sopenharmony_ci case SH7751_CS3_BASE_ADDR: word = __area_sdram_check(chan, 3); break; 14762306a36Sopenharmony_ci case SH7751_CS4_BASE_ADDR: word = __area_sdram_check(chan, 4); break; 14862306a36Sopenharmony_ci case SH7751_CS5_BASE_ADDR: word = __area_sdram_check(chan, 5); break; 14962306a36Sopenharmony_ci case SH7751_CS6_BASE_ADDR: word = __area_sdram_check(chan, 6); break; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!word) 15362306a36Sopenharmony_ci return -1; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* configure the wait control registers */ 15662306a36Sopenharmony_ci word = __raw_readl(SH7751_WCR1); 15762306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIWCR1); 15862306a36Sopenharmony_ci word = __raw_readl(SH7751_WCR2); 15962306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIWCR2); 16062306a36Sopenharmony_ci word = __raw_readl(SH7751_WCR3); 16162306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIWCR3); 16262306a36Sopenharmony_ci word = __raw_readl(SH7751_MCR); 16362306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCIMCR); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* NOTE: I'm ignoring the PCI error IRQs for now.. 16662306a36Sopenharmony_ci * TODO: add support for the internal error interrupts and 16762306a36Sopenharmony_ci * DMA interrupts... 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci pci_fixup_pcic(chan); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* SH7751 init done, set central function init complete */ 17362306a36Sopenharmony_ci /* use round robin mode to stop a device starving/overruning */ 17462306a36Sopenharmony_ci word = SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_ARBM; 17562306a36Sopenharmony_ci pci_write_reg(chan, word, SH4_PCICR); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return register_pci_controller(chan); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ciarch_initcall(sh7751_pci_init); 180