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