18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/types.h>
108c2ecf20Sopenharmony_ci#include <linux/pci.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/delay.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include "pci-bcm63xx.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * swizzle 32bits data to return only the needed part
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_cistatic int postprocess_read(u32 data, int where, unsigned int size)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	u32 ret;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	ret = 0;
258c2ecf20Sopenharmony_ci	switch (size) {
268c2ecf20Sopenharmony_ci	case 1:
278c2ecf20Sopenharmony_ci		ret = (data >> ((where & 3) << 3)) & 0xff;
288c2ecf20Sopenharmony_ci		break;
298c2ecf20Sopenharmony_ci	case 2:
308c2ecf20Sopenharmony_ci		ret = (data >> ((where & 3) << 3)) & 0xffff;
318c2ecf20Sopenharmony_ci		break;
328c2ecf20Sopenharmony_ci	case 4:
338c2ecf20Sopenharmony_ci		ret = data;
348c2ecf20Sopenharmony_ci		break;
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	return ret;
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic int preprocess_write(u32 orig_data, u32 val, int where,
408c2ecf20Sopenharmony_ci			    unsigned int size)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	u32 ret;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	ret = 0;
458c2ecf20Sopenharmony_ci	switch (size) {
468c2ecf20Sopenharmony_ci	case 1:
478c2ecf20Sopenharmony_ci		ret = (orig_data & ~(0xff << ((where & 3) << 3))) |
488c2ecf20Sopenharmony_ci			(val << ((where & 3) << 3));
498c2ecf20Sopenharmony_ci		break;
508c2ecf20Sopenharmony_ci	case 2:
518c2ecf20Sopenharmony_ci		ret = (orig_data & ~(0xffff << ((where & 3) << 3))) |
528c2ecf20Sopenharmony_ci			(val << ((where & 3) << 3));
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	case 4:
558c2ecf20Sopenharmony_ci		ret = val;
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci	return ret;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci/*
628c2ecf20Sopenharmony_ci * setup hardware for a configuration cycle with given parameters
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_cistatic int bcm63xx_setup_cfg_access(int type, unsigned int busn,
658c2ecf20Sopenharmony_ci				    unsigned int devfn, int where)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned int slot, func, reg;
688c2ecf20Sopenharmony_ci	u32 val;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	slot = PCI_SLOT(devfn);
718c2ecf20Sopenharmony_ci	func = PCI_FUNC(devfn);
728c2ecf20Sopenharmony_ci	reg = where >> 2;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	/* sanity check */
758c2ecf20Sopenharmony_ci	if (slot > (MPI_L2PCFG_DEVNUM_MASK >> MPI_L2PCFG_DEVNUM_SHIFT))
768c2ecf20Sopenharmony_ci		return 1;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (func > (MPI_L2PCFG_FUNC_MASK >> MPI_L2PCFG_FUNC_SHIFT))
798c2ecf20Sopenharmony_ci		return 1;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	if (reg > (MPI_L2PCFG_REG_MASK >> MPI_L2PCFG_REG_SHIFT))
828c2ecf20Sopenharmony_ci		return 1;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/* ok, setup config access */
858c2ecf20Sopenharmony_ci	val = (reg << MPI_L2PCFG_REG_SHIFT);
868c2ecf20Sopenharmony_ci	val |= (func << MPI_L2PCFG_FUNC_SHIFT);
878c2ecf20Sopenharmony_ci	val |= (slot << MPI_L2PCFG_DEVNUM_SHIFT);
888c2ecf20Sopenharmony_ci	val |= MPI_L2PCFG_CFG_USEREG_MASK;
898c2ecf20Sopenharmony_ci	val |= MPI_L2PCFG_CFG_SEL_MASK;
908c2ecf20Sopenharmony_ci	/* type 0 cycle for local bus, type 1 cycle for anything else */
918c2ecf20Sopenharmony_ci	if (type != 0) {
928c2ecf20Sopenharmony_ci		/* FIXME: how to specify bus ??? */
938c2ecf20Sopenharmony_ci		val |= (1 << MPI_L2PCFG_CFG_TYPE_SHIFT);
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci	bcm_mpi_writel(val, MPI_L2PCFG_REG);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic int bcm63xx_do_cfg_read(int type, unsigned int busn,
1018c2ecf20Sopenharmony_ci				unsigned int devfn, int where, int size,
1028c2ecf20Sopenharmony_ci				u32 *val)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	u32 data;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	/* two phase cycle, first we write address, then read data at
1078c2ecf20Sopenharmony_ci	 * another location, caller already has a spinlock so no need
1088c2ecf20Sopenharmony_ci	 * to add one here  */
1098c2ecf20Sopenharmony_ci	if (bcm63xx_setup_cfg_access(type, busn, devfn, where))
1108c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1118c2ecf20Sopenharmony_ci	iob();
1128c2ecf20Sopenharmony_ci	data = le32_to_cpu(__raw_readl(pci_iospace_start));
1138c2ecf20Sopenharmony_ci	/* restore IO space normal behaviour */
1148c2ecf20Sopenharmony_ci	bcm_mpi_writel(0, MPI_L2PCFG_REG);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	*val = postprocess_read(data, where, size);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int bcm63xx_do_cfg_write(int type, unsigned int busn,
1228c2ecf20Sopenharmony_ci				 unsigned int devfn, int where, int size,
1238c2ecf20Sopenharmony_ci				 u32 val)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	u32 data;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* two phase cycle, first we write address, then write data to
1288c2ecf20Sopenharmony_ci	 * another location, caller already has a spinlock so no need
1298c2ecf20Sopenharmony_ci	 * to add one here  */
1308c2ecf20Sopenharmony_ci	if (bcm63xx_setup_cfg_access(type, busn, devfn, where))
1318c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1328c2ecf20Sopenharmony_ci	iob();
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	data = le32_to_cpu(__raw_readl(pci_iospace_start));
1358c2ecf20Sopenharmony_ci	data = preprocess_write(data, val, where, size);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	__raw_writel(cpu_to_le32(data), pci_iospace_start);
1388c2ecf20Sopenharmony_ci	wmb();
1398c2ecf20Sopenharmony_ci	/* no way to know the access is done, we have to wait */
1408c2ecf20Sopenharmony_ci	udelay(500);
1418c2ecf20Sopenharmony_ci	/* restore IO space normal behaviour */
1428c2ecf20Sopenharmony_ci	bcm_mpi_writel(0, MPI_L2PCFG_REG);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic int bcm63xx_pci_read(struct pci_bus *bus, unsigned int devfn,
1488c2ecf20Sopenharmony_ci			     int where, int size, u32 *val)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int type;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	type = bus->parent ? 1 : 0;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (type == 0 && PCI_SLOT(devfn) == CARDBUS_PCI_IDSEL)
1558c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return bcm63xx_do_cfg_read(type, bus->number, devfn,
1588c2ecf20Sopenharmony_ci				    where, size, val);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int bcm63xx_pci_write(struct pci_bus *bus, unsigned int devfn,
1628c2ecf20Sopenharmony_ci			      int where, int size, u32 val)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	int type;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	type = bus->parent ? 1 : 0;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (type == 0 && PCI_SLOT(devfn) == CARDBUS_PCI_IDSEL)
1698c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return bcm63xx_do_cfg_write(type, bus->number, devfn,
1728c2ecf20Sopenharmony_ci				     where, size, val);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistruct pci_ops bcm63xx_pci_ops = {
1768c2ecf20Sopenharmony_ci	.read	= bcm63xx_pci_read,
1778c2ecf20Sopenharmony_ci	.write	= bcm63xx_pci_write
1788c2ecf20Sopenharmony_ci};
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci#ifdef CONFIG_CARDBUS
1818c2ecf20Sopenharmony_ci/*
1828c2ecf20Sopenharmony_ci * emulate configuration read access on a cardbus bridge
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_ci#define FAKE_CB_BRIDGE_SLOT	0x1e
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int fake_cb_bridge_bus_number = -1;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic struct {
1898c2ecf20Sopenharmony_ci	u16 pci_command;
1908c2ecf20Sopenharmony_ci	u8 cb_latency;
1918c2ecf20Sopenharmony_ci	u8 subordinate_busn;
1928c2ecf20Sopenharmony_ci	u8 cardbus_busn;
1938c2ecf20Sopenharmony_ci	u8 pci_busn;
1948c2ecf20Sopenharmony_ci	int bus_assigned;
1958c2ecf20Sopenharmony_ci	u16 bridge_control;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	u32 mem_base0;
1988c2ecf20Sopenharmony_ci	u32 mem_limit0;
1998c2ecf20Sopenharmony_ci	u32 mem_base1;
2008c2ecf20Sopenharmony_ci	u32 mem_limit1;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	u32 io_base0;
2038c2ecf20Sopenharmony_ci	u32 io_limit0;
2048c2ecf20Sopenharmony_ci	u32 io_base1;
2058c2ecf20Sopenharmony_ci	u32 io_limit1;
2068c2ecf20Sopenharmony_ci} fake_cb_bridge_regs;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic int fake_cb_bridge_read(int where, int size, u32 *val)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	unsigned int reg;
2118c2ecf20Sopenharmony_ci	u32 data;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	data = 0;
2148c2ecf20Sopenharmony_ci	reg = where >> 2;
2158c2ecf20Sopenharmony_ci	switch (reg) {
2168c2ecf20Sopenharmony_ci	case (PCI_VENDOR_ID >> 2):
2178c2ecf20Sopenharmony_ci	case (PCI_CB_SUBSYSTEM_VENDOR_ID >> 2):
2188c2ecf20Sopenharmony_ci		/* create dummy vendor/device id from our cpu id */
2198c2ecf20Sopenharmony_ci		data = (bcm63xx_get_cpu_id() << 16) | PCI_VENDOR_ID_BROADCOM;
2208c2ecf20Sopenharmony_ci		break;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	case (PCI_COMMAND >> 2):
2238c2ecf20Sopenharmony_ci		data = (PCI_STATUS_DEVSEL_SLOW << 16);
2248c2ecf20Sopenharmony_ci		data |= fake_cb_bridge_regs.pci_command;
2258c2ecf20Sopenharmony_ci		break;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	case (PCI_CLASS_REVISION >> 2):
2288c2ecf20Sopenharmony_ci		data = (PCI_CLASS_BRIDGE_CARDBUS << 16);
2298c2ecf20Sopenharmony_ci		break;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	case (PCI_CACHE_LINE_SIZE >> 2):
2328c2ecf20Sopenharmony_ci		data = (PCI_HEADER_TYPE_CARDBUS << 16);
2338c2ecf20Sopenharmony_ci		break;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	case (PCI_INTERRUPT_LINE >> 2):
2368c2ecf20Sopenharmony_ci		/* bridge control */
2378c2ecf20Sopenharmony_ci		data = (fake_cb_bridge_regs.bridge_control << 16);
2388c2ecf20Sopenharmony_ci		/* pin:intA line:0xff */
2398c2ecf20Sopenharmony_ci		data |= (0x1 << 8) | 0xff;
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	case (PCI_CB_PRIMARY_BUS >> 2):
2438c2ecf20Sopenharmony_ci		data = (fake_cb_bridge_regs.cb_latency << 24);
2448c2ecf20Sopenharmony_ci		data |= (fake_cb_bridge_regs.subordinate_busn << 16);
2458c2ecf20Sopenharmony_ci		data |= (fake_cb_bridge_regs.cardbus_busn << 8);
2468c2ecf20Sopenharmony_ci		data |= fake_cb_bridge_regs.pci_busn;
2478c2ecf20Sopenharmony_ci		break;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_BASE_0 >> 2):
2508c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.mem_base0;
2518c2ecf20Sopenharmony_ci		break;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_LIMIT_0 >> 2):
2548c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.mem_limit0;
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_BASE_1 >> 2):
2588c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.mem_base1;
2598c2ecf20Sopenharmony_ci		break;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_LIMIT_1 >> 2):
2628c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.mem_limit1;
2638c2ecf20Sopenharmony_ci		break;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	case (PCI_CB_IO_BASE_0 >> 2):
2668c2ecf20Sopenharmony_ci		/* | 1 for 32bits io support */
2678c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.io_base0 | 0x1;
2688c2ecf20Sopenharmony_ci		break;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	case (PCI_CB_IO_LIMIT_0 >> 2):
2718c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.io_limit0;
2728c2ecf20Sopenharmony_ci		break;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	case (PCI_CB_IO_BASE_1 >> 2):
2758c2ecf20Sopenharmony_ci		/* | 1 for 32bits io support */
2768c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.io_base1 | 0x1;
2778c2ecf20Sopenharmony_ci		break;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	case (PCI_CB_IO_LIMIT_1 >> 2):
2808c2ecf20Sopenharmony_ci		data = fake_cb_bridge_regs.io_limit1;
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	*val = postprocess_read(data, where, size);
2858c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/*
2898c2ecf20Sopenharmony_ci * emulate configuration write access on a cardbus bridge
2908c2ecf20Sopenharmony_ci */
2918c2ecf20Sopenharmony_cistatic int fake_cb_bridge_write(int where, int size, u32 val)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	unsigned int reg;
2948c2ecf20Sopenharmony_ci	u32 data, tmp;
2958c2ecf20Sopenharmony_ci	int ret;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	ret = fake_cb_bridge_read((where & ~0x3), 4, &data);
2988c2ecf20Sopenharmony_ci	if (ret != PCIBIOS_SUCCESSFUL)
2998c2ecf20Sopenharmony_ci		return ret;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	data = preprocess_write(data, val, where, size);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	reg = where >> 2;
3048c2ecf20Sopenharmony_ci	switch (reg) {
3058c2ecf20Sopenharmony_ci	case (PCI_COMMAND >> 2):
3068c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.pci_command = (data & 0xffff);
3078c2ecf20Sopenharmony_ci		break;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	case (PCI_CB_PRIMARY_BUS >> 2):
3108c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.cb_latency = (data >> 24) & 0xff;
3118c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.subordinate_busn = (data >> 16) & 0xff;
3128c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.cardbus_busn = (data >> 8) & 0xff;
3138c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.pci_busn = data & 0xff;
3148c2ecf20Sopenharmony_ci		if (fake_cb_bridge_regs.cardbus_busn)
3158c2ecf20Sopenharmony_ci			fake_cb_bridge_regs.bus_assigned = 1;
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	case (PCI_INTERRUPT_LINE >> 2):
3198c2ecf20Sopenharmony_ci		tmp = (data >> 16) & 0xffff;
3208c2ecf20Sopenharmony_ci		/* disable memory prefetch support */
3218c2ecf20Sopenharmony_ci		tmp &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM0;
3228c2ecf20Sopenharmony_ci		tmp &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
3238c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.bridge_control = tmp;
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_BASE_0 >> 2):
3278c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.mem_base0 = data;
3288c2ecf20Sopenharmony_ci		break;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_LIMIT_0 >> 2):
3318c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.mem_limit0 = data;
3328c2ecf20Sopenharmony_ci		break;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_BASE_1 >> 2):
3358c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.mem_base1 = data;
3368c2ecf20Sopenharmony_ci		break;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	case (PCI_CB_MEMORY_LIMIT_1 >> 2):
3398c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.mem_limit1 = data;
3408c2ecf20Sopenharmony_ci		break;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	case (PCI_CB_IO_BASE_0 >> 2):
3438c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.io_base0 = data;
3448c2ecf20Sopenharmony_ci		break;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	case (PCI_CB_IO_LIMIT_0 >> 2):
3478c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.io_limit0 = data;
3488c2ecf20Sopenharmony_ci		break;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	case (PCI_CB_IO_BASE_1 >> 2):
3518c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.io_base1 = data;
3528c2ecf20Sopenharmony_ci		break;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	case (PCI_CB_IO_LIMIT_1 >> 2):
3558c2ecf20Sopenharmony_ci		fake_cb_bridge_regs.io_limit1 = data;
3568c2ecf20Sopenharmony_ci		break;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
3608c2ecf20Sopenharmony_ci}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_cistatic int bcm63xx_cb_read(struct pci_bus *bus, unsigned int devfn,
3638c2ecf20Sopenharmony_ci			   int where, int size, u32 *val)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	/* snoop access to slot 0x1e on root bus, we fake a cardbus
3668c2ecf20Sopenharmony_ci	 * bridge at this location */
3678c2ecf20Sopenharmony_ci	if (!bus->parent && PCI_SLOT(devfn) == FAKE_CB_BRIDGE_SLOT) {
3688c2ecf20Sopenharmony_ci		fake_cb_bridge_bus_number = bus->number;
3698c2ecf20Sopenharmony_ci		return fake_cb_bridge_read(where, size, val);
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* a  configuration  cycle for	the  device  behind the	 cardbus
3738c2ecf20Sopenharmony_ci	 * bridge is  actually done as a  type 0 cycle	on the primary
3748c2ecf20Sopenharmony_ci	 * bus. This means that only  one device can be on the cardbus
3758c2ecf20Sopenharmony_ci	 * bus */
3768c2ecf20Sopenharmony_ci	if (fake_cb_bridge_regs.bus_assigned &&
3778c2ecf20Sopenharmony_ci	    bus->number == fake_cb_bridge_regs.cardbus_busn &&
3788c2ecf20Sopenharmony_ci	    PCI_SLOT(devfn) == 0)
3798c2ecf20Sopenharmony_ci		return bcm63xx_do_cfg_read(0, 0,
3808c2ecf20Sopenharmony_ci					   PCI_DEVFN(CARDBUS_PCI_IDSEL, 0),
3818c2ecf20Sopenharmony_ci					   where, size, val);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return PCIBIOS_DEVICE_NOT_FOUND;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic int bcm63xx_cb_write(struct pci_bus *bus, unsigned int devfn,
3878c2ecf20Sopenharmony_ci			    int where, int size, u32 val)
3888c2ecf20Sopenharmony_ci{
3898c2ecf20Sopenharmony_ci	if (!bus->parent && PCI_SLOT(devfn) == FAKE_CB_BRIDGE_SLOT) {
3908c2ecf20Sopenharmony_ci		fake_cb_bridge_bus_number = bus->number;
3918c2ecf20Sopenharmony_ci		return fake_cb_bridge_write(where, size, val);
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (fake_cb_bridge_regs.bus_assigned &&
3958c2ecf20Sopenharmony_ci	    bus->number == fake_cb_bridge_regs.cardbus_busn &&
3968c2ecf20Sopenharmony_ci	    PCI_SLOT(devfn) == 0)
3978c2ecf20Sopenharmony_ci		return bcm63xx_do_cfg_write(0, 0,
3988c2ecf20Sopenharmony_ci					    PCI_DEVFN(CARDBUS_PCI_IDSEL, 0),
3998c2ecf20Sopenharmony_ci					    where, size, val);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return PCIBIOS_DEVICE_NOT_FOUND;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistruct pci_ops bcm63xx_cb_ops = {
4058c2ecf20Sopenharmony_ci	.read	= bcm63xx_cb_read,
4068c2ecf20Sopenharmony_ci	.write	 = bcm63xx_cb_write,
4078c2ecf20Sopenharmony_ci};
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/*
4108c2ecf20Sopenharmony_ci * only one IO window, so it  cannot be shared by PCI and cardbus, use
4118c2ecf20Sopenharmony_ci * fixup to choose and detect unhandled configuration
4128c2ecf20Sopenharmony_ci */
4138c2ecf20Sopenharmony_cistatic void bcm63xx_fixup(struct pci_dev *dev)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	static int io_window = -1;
4168c2ecf20Sopenharmony_ci	int i, found, new_io_window;
4178c2ecf20Sopenharmony_ci	u32 val;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/* look for any io resource */
4208c2ecf20Sopenharmony_ci	found = 0;
4218c2ecf20Sopenharmony_ci	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
4228c2ecf20Sopenharmony_ci		if (pci_resource_flags(dev, i) & IORESOURCE_IO) {
4238c2ecf20Sopenharmony_ci			found = 1;
4248c2ecf20Sopenharmony_ci			break;
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (!found)
4298c2ecf20Sopenharmony_ci		return;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	/* skip our fake bus with only cardbus bridge on it */
4328c2ecf20Sopenharmony_ci	if (dev->bus->number == fake_cb_bridge_bus_number)
4338c2ecf20Sopenharmony_ci		return;
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	/* find on which bus the device is */
4368c2ecf20Sopenharmony_ci	if (fake_cb_bridge_regs.bus_assigned &&
4378c2ecf20Sopenharmony_ci	    dev->bus->number == fake_cb_bridge_regs.cardbus_busn &&
4388c2ecf20Sopenharmony_ci	    PCI_SLOT(dev->devfn) == 0)
4398c2ecf20Sopenharmony_ci		new_io_window = 1;
4408c2ecf20Sopenharmony_ci	else
4418c2ecf20Sopenharmony_ci		new_io_window = 0;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	if (new_io_window == io_window)
4448c2ecf20Sopenharmony_ci		return;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (io_window != -1) {
4478c2ecf20Sopenharmony_ci		printk(KERN_ERR "bcm63xx: both PCI and cardbus devices "
4488c2ecf20Sopenharmony_ci		       "need IO, which hardware cannot do\n");
4498c2ecf20Sopenharmony_ci		return;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	printk(KERN_INFO "bcm63xx: PCI IO window assigned to %s\n",
4538c2ecf20Sopenharmony_ci	       (new_io_window == 0) ? "PCI" : "cardbus");
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	val = bcm_mpi_readl(MPI_L2PIOREMAP_REG);
4568c2ecf20Sopenharmony_ci	if (io_window)
4578c2ecf20Sopenharmony_ci		val |= MPI_L2PREMAP_IS_CARDBUS_MASK;
4588c2ecf20Sopenharmony_ci	else
4598c2ecf20Sopenharmony_ci		val &= ~MPI_L2PREMAP_IS_CARDBUS_MASK;
4608c2ecf20Sopenharmony_ci	bcm_mpi_writel(val, MPI_L2PIOREMAP_REG);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	io_window = new_io_window;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, bcm63xx_fixup);
4668c2ecf20Sopenharmony_ci#endif
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_cistatic int bcm63xx_pcie_can_access(struct pci_bus *bus, int devfn)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	switch (bus->number) {
4718c2ecf20Sopenharmony_ci	case PCIE_BUS_BRIDGE:
4728c2ecf20Sopenharmony_ci		return PCI_SLOT(devfn) == 0;
4738c2ecf20Sopenharmony_ci	case PCIE_BUS_DEVICE:
4748c2ecf20Sopenharmony_ci		if (PCI_SLOT(devfn) == 0)
4758c2ecf20Sopenharmony_ci			return bcm_pcie_readl(PCIE_DLSTATUS_REG)
4768c2ecf20Sopenharmony_ci					& DLSTATUS_PHYLINKUP;
4778c2ecf20Sopenharmony_ci		fallthrough;
4788c2ecf20Sopenharmony_ci	default:
4798c2ecf20Sopenharmony_ci		return false;
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int bcm63xx_pcie_read(struct pci_bus *bus, unsigned int devfn,
4848c2ecf20Sopenharmony_ci			     int where, int size, u32 *val)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	u32 data;
4878c2ecf20Sopenharmony_ci	u32 reg = where & ~3;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (!bcm63xx_pcie_can_access(bus, devfn))
4908c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (bus->number == PCIE_BUS_DEVICE)
4938c2ecf20Sopenharmony_ci		reg += PCIE_DEVICE_OFFSET;
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci	data = bcm_pcie_readl(reg);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	*val = postprocess_read(data, where, size);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic int bcm63xx_pcie_write(struct pci_bus *bus, unsigned int devfn,
5048c2ecf20Sopenharmony_ci			      int where, int size, u32 val)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	u32 data;
5078c2ecf20Sopenharmony_ci	u32 reg = where & ~3;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (!bcm63xx_pcie_can_access(bus, devfn))
5108c2ecf20Sopenharmony_ci		return PCIBIOS_DEVICE_NOT_FOUND;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (bus->number == PCIE_BUS_DEVICE)
5138c2ecf20Sopenharmony_ci		reg += PCIE_DEVICE_OFFSET;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	data = bcm_pcie_readl(reg);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci	data = preprocess_write(data, val, where, size);
5198c2ecf20Sopenharmony_ci	bcm_pcie_writel(data, reg);
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_cistruct pci_ops bcm63xx_pcie_ops = {
5268c2ecf20Sopenharmony_ci	.read	= bcm63xx_pcie_read,
5278c2ecf20Sopenharmony_ci	.write	= bcm63xx_pcie_write
5288c2ecf20Sopenharmony_ci};
529