xref: /kernel/linux/linux-5.10/arch/m68k/coldfire/pci.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * pci.c -- PCI bus support for ColdFire processors
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * (C) Copyright 2012, Greg Ungerer <gerg@uclinux.com>
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
78c2ecf20Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
88c2ecf20Sopenharmony_ci * for more details.
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/irq.h>
178c2ecf20Sopenharmony_ci#include <linux/io.h>
188c2ecf20Sopenharmony_ci#include <linux/pci.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci#include <asm/coldfire.h>
218c2ecf20Sopenharmony_ci#include <asm/mcfsim.h>
228c2ecf20Sopenharmony_ci#include <asm/m54xxpci.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Memory and IO mappings. We use a 1:1 mapping for local host memory to
268c2ecf20Sopenharmony_ci * PCI bus memory (no reason not to really). IO space is mapped in its own
278c2ecf20Sopenharmony_ci * separate address region. The device configuration space is mapped over
288c2ecf20Sopenharmony_ci * the IO map space when we enable it in the PCICAR register.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistatic struct pci_bus *rootbus;
318c2ecf20Sopenharmony_cistatic unsigned long iospace;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * We need to be carefull probing on bus 0 (directly connected to host
358c2ecf20Sopenharmony_ci * bridge). We should only access the well defined possible devices in
368c2ecf20Sopenharmony_ci * use, ignore aliases and the like.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic unsigned char mcf_host_slot2sid[32] = {
398c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0,
408c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0,
418c2ecf20Sopenharmony_ci	0, 1, 2, 0, 3, 4, 0, 0,
428c2ecf20Sopenharmony_ci	0, 0, 0, 0, 0, 0, 0, 0,
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic unsigned char mcf_host_irq[] = {
468c2ecf20Sopenharmony_ci	0, 69, 69, 71, 71,
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * Configuration space access functions. Configuration space access is
518c2ecf20Sopenharmony_ci * through the IO mapping window, enabling it via the PCICAR register.
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic unsigned long mcf_mk_pcicar(int bus, unsigned int devfn, int where)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	return (bus << PCICAR_BUSN) | (devfn << PCICAR_DEVFNN) | (where & 0xfc);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int mcf_pci_readconfig(struct pci_bus *bus, unsigned int devfn,
598c2ecf20Sopenharmony_ci	int where, int size, u32 *value)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned long addr;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	*value = 0xffffffff;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (bus->number == 0) {
668c2ecf20Sopenharmony_ci		if (mcf_host_slot2sid[PCI_SLOT(devfn)] == 0)
678c2ecf20Sopenharmony_ci			return PCIBIOS_SUCCESSFUL;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	addr = mcf_mk_pcicar(bus->number, devfn, where);
718c2ecf20Sopenharmony_ci	__raw_writel(PCICAR_E | addr, PCICAR);
728c2ecf20Sopenharmony_ci	__raw_readl(PCICAR);
738c2ecf20Sopenharmony_ci	addr = iospace + (where & 0x3);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	switch (size) {
768c2ecf20Sopenharmony_ci	case 1:
778c2ecf20Sopenharmony_ci		*value = __raw_readb(addr);
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci	case 2:
808c2ecf20Sopenharmony_ci		*value = le16_to_cpu(__raw_readw(addr));
818c2ecf20Sopenharmony_ci		break;
828c2ecf20Sopenharmony_ci	default:
838c2ecf20Sopenharmony_ci		*value = le32_to_cpu(__raw_readl(addr));
848c2ecf20Sopenharmony_ci		break;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	__raw_writel(0, PCICAR);
888c2ecf20Sopenharmony_ci	__raw_readl(PCICAR);
898c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int mcf_pci_writeconfig(struct pci_bus *bus, unsigned int devfn,
938c2ecf20Sopenharmony_ci	int where, int size, u32 value)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	unsigned long addr;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (bus->number == 0) {
988c2ecf20Sopenharmony_ci		if (mcf_host_slot2sid[PCI_SLOT(devfn)] == 0)
998c2ecf20Sopenharmony_ci			return PCIBIOS_SUCCESSFUL;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	addr = mcf_mk_pcicar(bus->number, devfn, where);
1038c2ecf20Sopenharmony_ci	__raw_writel(PCICAR_E | addr, PCICAR);
1048c2ecf20Sopenharmony_ci	__raw_readl(PCICAR);
1058c2ecf20Sopenharmony_ci	addr = iospace + (where & 0x3);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	switch (size) {
1088c2ecf20Sopenharmony_ci	case 1:
1098c2ecf20Sopenharmony_ci		 __raw_writeb(value, addr);
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	case 2:
1128c2ecf20Sopenharmony_ci		__raw_writew(cpu_to_le16(value), addr);
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	default:
1158c2ecf20Sopenharmony_ci		__raw_writel(cpu_to_le32(value), addr);
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	__raw_writel(0, PCICAR);
1208c2ecf20Sopenharmony_ci	__raw_readl(PCICAR);
1218c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic struct pci_ops mcf_pci_ops = {
1258c2ecf20Sopenharmony_ci	.read	= mcf_pci_readconfig,
1268c2ecf20Sopenharmony_ci	.write	= mcf_pci_writeconfig,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci/*
1308c2ecf20Sopenharmony_ci * Initialize the PCI bus registers, and scan the bus.
1318c2ecf20Sopenharmony_ci */
1328c2ecf20Sopenharmony_cistatic struct resource mcf_pci_mem = {
1338c2ecf20Sopenharmony_ci	.name	= "PCI Memory space",
1348c2ecf20Sopenharmony_ci	.start	= PCI_MEM_PA,
1358c2ecf20Sopenharmony_ci	.end	= PCI_MEM_PA + PCI_MEM_SIZE - 1,
1368c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_MEM,
1378c2ecf20Sopenharmony_ci};
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic struct resource mcf_pci_io = {
1408c2ecf20Sopenharmony_ci	.name	= "PCI IO space",
1418c2ecf20Sopenharmony_ci	.start	= 0x400,
1428c2ecf20Sopenharmony_ci	.end	= 0x10000 - 1,
1438c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_IO,
1448c2ecf20Sopenharmony_ci};
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic struct resource busn_resource = {
1478c2ecf20Sopenharmony_ci	.name	= "PCI busn",
1488c2ecf20Sopenharmony_ci	.start	= 0,
1498c2ecf20Sopenharmony_ci	.end	= 255,
1508c2ecf20Sopenharmony_ci	.flags	= IORESOURCE_BUS,
1518c2ecf20Sopenharmony_ci};
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci/*
1548c2ecf20Sopenharmony_ci * Interrupt mapping and setting.
1558c2ecf20Sopenharmony_ci */
1568c2ecf20Sopenharmony_cistatic int mcf_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	int sid;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	sid = mcf_host_slot2sid[slot];
1618c2ecf20Sopenharmony_ci	if (sid)
1628c2ecf20Sopenharmony_ci		return mcf_host_irq[sid];
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic int __init mcf_pci_init(void)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct pci_host_bridge *bridge;
1698c2ecf20Sopenharmony_ci	int ret;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	bridge = pci_alloc_host_bridge(0);
1728c2ecf20Sopenharmony_ci	if (!bridge)
1738c2ecf20Sopenharmony_ci		return -ENOMEM;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	pr_info("ColdFire: PCI bus initialization...\n");
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/* Reset the external PCI bus */
1788c2ecf20Sopenharmony_ci	__raw_writel(PCIGSCR_RESET, PCIGSCR);
1798c2ecf20Sopenharmony_ci	__raw_writel(0, PCITCR);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	request_resource(&iomem_resource, &mcf_pci_mem);
1828c2ecf20Sopenharmony_ci	request_resource(&iomem_resource, &mcf_pci_io);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	/* Configure PCI arbiter */
1858c2ecf20Sopenharmony_ci	__raw_writel(PACR_INTMPRI | PACR_INTMINTE | PACR_EXTMPRI(0x1f) |
1868c2ecf20Sopenharmony_ci		PACR_EXTMINTE(0x1f), PACR);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* Set required multi-function pins for PCI bus use */
1898c2ecf20Sopenharmony_ci	__raw_writew(0x3ff, MCFGPIO_PAR_PCIBG);
1908c2ecf20Sopenharmony_ci	__raw_writew(0x3ff, MCFGPIO_PAR_PCIBR);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	/* Set up config space for local host bus controller */
1938c2ecf20Sopenharmony_ci	__raw_writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
1948c2ecf20Sopenharmony_ci		PCI_COMMAND_INVALIDATE, PCISCR);
1958c2ecf20Sopenharmony_ci	__raw_writel(PCICR1_LT(32) | PCICR1_CL(8), PCICR1);
1968c2ecf20Sopenharmony_ci	__raw_writel(0, PCICR2);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/*
1998c2ecf20Sopenharmony_ci	 * Set up the initiator windows for memory and IO mapping.
2008c2ecf20Sopenharmony_ci	 * These give the CPU bus access onto the PCI bus. One for each of
2018c2ecf20Sopenharmony_ci	 * PCI memory and IO address spaces.
2028c2ecf20Sopenharmony_ci	 */
2038c2ecf20Sopenharmony_ci	__raw_writel(WXBTAR(PCI_MEM_PA, PCI_MEM_BA, PCI_MEM_SIZE),
2048c2ecf20Sopenharmony_ci		PCIIW0BTAR);
2058c2ecf20Sopenharmony_ci	__raw_writel(WXBTAR(PCI_IO_PA, PCI_IO_BA, PCI_IO_SIZE),
2068c2ecf20Sopenharmony_ci		PCIIW1BTAR);
2078c2ecf20Sopenharmony_ci	__raw_writel(PCIIWCR_W0_MEM /*| PCIIWCR_W0_MRDL*/ | PCIIWCR_W0_E |
2088c2ecf20Sopenharmony_ci		PCIIWCR_W1_IO | PCIIWCR_W1_E, PCIIWCR);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	/*
2118c2ecf20Sopenharmony_ci	 * Set up the target windows for access from the PCI bus back to the
2128c2ecf20Sopenharmony_ci	 * CPU bus. All we need is access to system RAM (for mastering).
2138c2ecf20Sopenharmony_ci	 */
2148c2ecf20Sopenharmony_ci	__raw_writel(CONFIG_RAMBASE, PCIBAR1);
2158c2ecf20Sopenharmony_ci	__raw_writel(CONFIG_RAMBASE | PCITBATR1_E, PCITBATR1);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/* Keep a virtual mapping to IO/config space active */
2188c2ecf20Sopenharmony_ci	iospace = (unsigned long) ioremap(PCI_IO_PA, PCI_IO_SIZE);
2198c2ecf20Sopenharmony_ci	if (iospace == 0) {
2208c2ecf20Sopenharmony_ci		pci_free_host_bridge(bridge);
2218c2ecf20Sopenharmony_ci		return -ENODEV;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ci	pr_info("Coldfire: PCI IO/config window mapped to 0x%x\n",
2248c2ecf20Sopenharmony_ci		(u32) iospace);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Turn of PCI reset, and wait for devices to settle */
2278c2ecf20Sopenharmony_ci	__raw_writel(0, PCIGSCR);
2288c2ecf20Sopenharmony_ci	set_current_state(TASK_UNINTERRUPTIBLE);
2298c2ecf20Sopenharmony_ci	schedule_timeout(msecs_to_jiffies(200));
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	pci_add_resource(&bridge->windows, &ioport_resource);
2338c2ecf20Sopenharmony_ci	pci_add_resource(&bridge->windows, &iomem_resource);
2348c2ecf20Sopenharmony_ci	pci_add_resource(&bridge->windows, &busn_resource);
2358c2ecf20Sopenharmony_ci	bridge->dev.parent = NULL;
2368c2ecf20Sopenharmony_ci	bridge->sysdata = NULL;
2378c2ecf20Sopenharmony_ci	bridge->busnr = 0;
2388c2ecf20Sopenharmony_ci	bridge->ops = &mcf_pci_ops;
2398c2ecf20Sopenharmony_ci	bridge->swizzle_irq = pci_common_swizzle;
2408c2ecf20Sopenharmony_ci	bridge->map_irq = mcf_pci_map_irq;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ret = pci_scan_root_bus_bridge(bridge);
2438c2ecf20Sopenharmony_ci	if (ret) {
2448c2ecf20Sopenharmony_ci		pci_free_host_bridge(bridge);
2458c2ecf20Sopenharmony_ci		return ret;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	rootbus = bridge->bus;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	rootbus->resource[0] = &mcf_pci_io;
2518c2ecf20Sopenharmony_ci	rootbus->resource[1] = &mcf_pci_mem;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	pci_bus_size_bridges(rootbus);
2548c2ecf20Sopenharmony_ci	pci_bus_assign_resources(rootbus);
2558c2ecf20Sopenharmony_ci	pci_bus_add_devices(rootbus);
2568c2ecf20Sopenharmony_ci	return 0;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cisubsys_initcall(mcf_pci_init);
260