18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Sonics Silicon Backplane 38c2ecf20Sopenharmony_ci * Broadcom PCI-core driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2005, Broadcom Corporation 68c2ecf20Sopenharmony_ci * Copyright 2006, 2007, Michael Buesch <m@bues.ch> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Licensed under the GNU/GPL. See COPYING for details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "ssb_private.h" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/ssb/ssb.h> 148c2ecf20Sopenharmony_ci#include <linux/pci.h> 158c2ecf20Sopenharmony_ci#include <linux/export.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/ssb/ssb_embedded.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address); 208c2ecf20Sopenharmony_cistatic void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data); 218c2ecf20Sopenharmony_cistatic u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address); 228c2ecf20Sopenharmony_cistatic void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device, 238c2ecf20Sopenharmony_ci u8 address, u16 data); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic inline 268c2ecf20Sopenharmony_ciu32 pcicore_read32(struct ssb_pcicore *pc, u16 offset) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci return ssb_read32(pc->dev, offset); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic inline 328c2ecf20Sopenharmony_civoid pcicore_write32(struct ssb_pcicore *pc, u16 offset, u32 value) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci ssb_write32(pc->dev, offset, value); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline 388c2ecf20Sopenharmony_ciu16 pcicore_read16(struct ssb_pcicore *pc, u16 offset) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return ssb_read16(pc->dev, offset); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic inline 448c2ecf20Sopenharmony_civoid pcicore_write16(struct ssb_pcicore *pc, u16 offset, u16 value) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci ssb_write16(pc->dev, offset, value); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/************************************************** 508c2ecf20Sopenharmony_ci * Code for hostmode operation. 518c2ecf20Sopenharmony_ci **************************************************/ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCICORE_HOSTMODE 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include <asm/paccess.h> 568c2ecf20Sopenharmony_ci/* Probe a 32bit value on the bus and catch bus exceptions. 578c2ecf20Sopenharmony_ci * Returns nonzero on a bus exception. 588c2ecf20Sopenharmony_ci * This is MIPS specific */ 598c2ecf20Sopenharmony_ci#define mips_busprobe32(val, addr) get_dbe((val), ((u32 *)(addr))) 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Assume one-hot slot wiring */ 628c2ecf20Sopenharmony_ci#define SSB_PCI_SLOT_MAX 16 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* Global lock is OK, as we won't have more than one extpci anyway. */ 658c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cfgspace_lock); 668c2ecf20Sopenharmony_ci/* Core to access the external PCI config space. Can only have one. */ 678c2ecf20Sopenharmony_cistatic struct ssb_pcicore *extpci_core; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic u32 get_cfgspace_addr(struct ssb_pcicore *pc, 718c2ecf20Sopenharmony_ci unsigned int bus, unsigned int dev, 728c2ecf20Sopenharmony_ci unsigned int func, unsigned int off) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci u32 addr = 0; 758c2ecf20Sopenharmony_ci u32 tmp; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* We do only have one cardbus device behind the bridge. */ 788c2ecf20Sopenharmony_ci if (pc->cardbusmode && (dev > 1)) 798c2ecf20Sopenharmony_ci goto out; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (bus == 0) { 828c2ecf20Sopenharmony_ci /* Type 0 transaction */ 838c2ecf20Sopenharmony_ci if (unlikely(dev >= SSB_PCI_SLOT_MAX)) 848c2ecf20Sopenharmony_ci goto out; 858c2ecf20Sopenharmony_ci /* Slide the window */ 868c2ecf20Sopenharmony_ci tmp = SSB_PCICORE_SBTOPCI_CFG0; 878c2ecf20Sopenharmony_ci tmp |= ((1 << (dev + 16)) & SSB_PCICORE_SBTOPCI1_MASK); 888c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, tmp); 898c2ecf20Sopenharmony_ci /* Calculate the address */ 908c2ecf20Sopenharmony_ci addr = SSB_PCI_CFG; 918c2ecf20Sopenharmony_ci addr |= ((1 << (dev + 16)) & ~SSB_PCICORE_SBTOPCI1_MASK); 928c2ecf20Sopenharmony_ci addr |= (func << 8); 938c2ecf20Sopenharmony_ci addr |= (off & ~3); 948c2ecf20Sopenharmony_ci } else { 958c2ecf20Sopenharmony_ci /* Type 1 transaction */ 968c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, 978c2ecf20Sopenharmony_ci SSB_PCICORE_SBTOPCI_CFG1); 988c2ecf20Sopenharmony_ci /* Calculate the address */ 998c2ecf20Sopenharmony_ci addr = SSB_PCI_CFG; 1008c2ecf20Sopenharmony_ci addr |= (bus << 16); 1018c2ecf20Sopenharmony_ci addr |= (dev << 11); 1028c2ecf20Sopenharmony_ci addr |= (func << 8); 1038c2ecf20Sopenharmony_ci addr |= (off & ~3); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ciout: 1068c2ecf20Sopenharmony_ci return addr; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int ssb_extpci_read_config(struct ssb_pcicore *pc, 1108c2ecf20Sopenharmony_ci unsigned int bus, unsigned int dev, 1118c2ecf20Sopenharmony_ci unsigned int func, unsigned int off, 1128c2ecf20Sopenharmony_ci void *buf, int len) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci int err = -EINVAL; 1158c2ecf20Sopenharmony_ci u32 addr, val; 1168c2ecf20Sopenharmony_ci void __iomem *mmio; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci WARN_ON(!pc->hostmode); 1198c2ecf20Sopenharmony_ci if (unlikely(len != 1 && len != 2 && len != 4)) 1208c2ecf20Sopenharmony_ci goto out; 1218c2ecf20Sopenharmony_ci addr = get_cfgspace_addr(pc, bus, dev, func, off); 1228c2ecf20Sopenharmony_ci if (unlikely(!addr)) 1238c2ecf20Sopenharmony_ci goto out; 1248c2ecf20Sopenharmony_ci err = -ENOMEM; 1258c2ecf20Sopenharmony_ci mmio = ioremap(addr, len); 1268c2ecf20Sopenharmony_ci if (!mmio) 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (mips_busprobe32(val, mmio)) { 1308c2ecf20Sopenharmony_ci val = 0xffffffff; 1318c2ecf20Sopenharmony_ci goto unmap; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci val = readl(mmio); 1358c2ecf20Sopenharmony_ci val >>= (8 * (off & 3)); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci switch (len) { 1388c2ecf20Sopenharmony_ci case 1: 1398c2ecf20Sopenharmony_ci *((u8 *)buf) = (u8)val; 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci case 2: 1428c2ecf20Sopenharmony_ci *((u16 *)buf) = (u16)val; 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci case 4: 1458c2ecf20Sopenharmony_ci *((u32 *)buf) = (u32)val; 1468c2ecf20Sopenharmony_ci break; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci err = 0; 1498c2ecf20Sopenharmony_ciunmap: 1508c2ecf20Sopenharmony_ci iounmap(mmio); 1518c2ecf20Sopenharmony_ciout: 1528c2ecf20Sopenharmony_ci return err; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int ssb_extpci_write_config(struct ssb_pcicore *pc, 1568c2ecf20Sopenharmony_ci unsigned int bus, unsigned int dev, 1578c2ecf20Sopenharmony_ci unsigned int func, unsigned int off, 1588c2ecf20Sopenharmony_ci const void *buf, int len) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci int err = -EINVAL; 1618c2ecf20Sopenharmony_ci u32 addr, val = 0; 1628c2ecf20Sopenharmony_ci void __iomem *mmio; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci WARN_ON(!pc->hostmode); 1658c2ecf20Sopenharmony_ci if (unlikely(len != 1 && len != 2 && len != 4)) 1668c2ecf20Sopenharmony_ci goto out; 1678c2ecf20Sopenharmony_ci addr = get_cfgspace_addr(pc, bus, dev, func, off); 1688c2ecf20Sopenharmony_ci if (unlikely(!addr)) 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci err = -ENOMEM; 1718c2ecf20Sopenharmony_ci mmio = ioremap(addr, len); 1728c2ecf20Sopenharmony_ci if (!mmio) 1738c2ecf20Sopenharmony_ci goto out; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (mips_busprobe32(val, mmio)) { 1768c2ecf20Sopenharmony_ci val = 0xffffffff; 1778c2ecf20Sopenharmony_ci goto unmap; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci switch (len) { 1818c2ecf20Sopenharmony_ci case 1: 1828c2ecf20Sopenharmony_ci val = readl(mmio); 1838c2ecf20Sopenharmony_ci val &= ~(0xFF << (8 * (off & 3))); 1848c2ecf20Sopenharmony_ci val |= *((const u8 *)buf) << (8 * (off & 3)); 1858c2ecf20Sopenharmony_ci break; 1868c2ecf20Sopenharmony_ci case 2: 1878c2ecf20Sopenharmony_ci val = readl(mmio); 1888c2ecf20Sopenharmony_ci val &= ~(0xFFFF << (8 * (off & 3))); 1898c2ecf20Sopenharmony_ci val |= *((const u16 *)buf) << (8 * (off & 3)); 1908c2ecf20Sopenharmony_ci break; 1918c2ecf20Sopenharmony_ci case 4: 1928c2ecf20Sopenharmony_ci val = *((const u32 *)buf); 1938c2ecf20Sopenharmony_ci break; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci writel(val, mmio); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci err = 0; 1988c2ecf20Sopenharmony_ciunmap: 1998c2ecf20Sopenharmony_ci iounmap(mmio); 2008c2ecf20Sopenharmony_ciout: 2018c2ecf20Sopenharmony_ci return err; 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic int ssb_pcicore_read_config(struct pci_bus *bus, unsigned int devfn, 2058c2ecf20Sopenharmony_ci int reg, int size, u32 *val) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci unsigned long flags; 2088c2ecf20Sopenharmony_ci int err; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci spin_lock_irqsave(&cfgspace_lock, flags); 2118c2ecf20Sopenharmony_ci err = ssb_extpci_read_config(extpci_core, bus->number, PCI_SLOT(devfn), 2128c2ecf20Sopenharmony_ci PCI_FUNC(devfn), reg, val, size); 2138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cfgspace_lock, flags); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int ssb_pcicore_write_config(struct pci_bus *bus, unsigned int devfn, 2198c2ecf20Sopenharmony_ci int reg, int size, u32 val) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci unsigned long flags; 2228c2ecf20Sopenharmony_ci int err; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci spin_lock_irqsave(&cfgspace_lock, flags); 2258c2ecf20Sopenharmony_ci err = ssb_extpci_write_config(extpci_core, bus->number, PCI_SLOT(devfn), 2268c2ecf20Sopenharmony_ci PCI_FUNC(devfn), reg, &val, size); 2278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cfgspace_lock, flags); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci return err ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic struct pci_ops ssb_pcicore_pciops = { 2338c2ecf20Sopenharmony_ci .read = ssb_pcicore_read_config, 2348c2ecf20Sopenharmony_ci .write = ssb_pcicore_write_config, 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic struct resource ssb_pcicore_mem_resource = { 2388c2ecf20Sopenharmony_ci .name = "SSB PCIcore external memory", 2398c2ecf20Sopenharmony_ci .start = SSB_PCI_DMA, 2408c2ecf20Sopenharmony_ci .end = SSB_PCI_DMA + SSB_PCI_DMA_SZ - 1, 2418c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM | IORESOURCE_PCI_FIXED, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic struct resource ssb_pcicore_io_resource = { 2458c2ecf20Sopenharmony_ci .name = "SSB PCIcore external I/O", 2468c2ecf20Sopenharmony_ci .start = 0x100, 2478c2ecf20Sopenharmony_ci .end = 0x7FF, 2488c2ecf20Sopenharmony_ci .flags = IORESOURCE_IO | IORESOURCE_PCI_FIXED, 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct pci_controller ssb_pcicore_controller = { 2528c2ecf20Sopenharmony_ci .pci_ops = &ssb_pcicore_pciops, 2538c2ecf20Sopenharmony_ci .io_resource = &ssb_pcicore_io_resource, 2548c2ecf20Sopenharmony_ci .mem_resource = &ssb_pcicore_mem_resource, 2558c2ecf20Sopenharmony_ci}; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/* This function is called when doing a pci_enable_device(). 2588c2ecf20Sopenharmony_ci * We must first check if the device is a device on the PCI-core bridge. */ 2598c2ecf20Sopenharmony_ciint ssb_pcicore_plat_dev_init(struct pci_dev *d) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci if (d->bus->ops != &ssb_pcicore_pciops) { 2628c2ecf20Sopenharmony_ci /* This is not a device on the PCI-core bridge. */ 2638c2ecf20Sopenharmony_ci return -ENODEV; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci dev_info(&d->dev, "PCI: Fixing up device %s\n", pci_name(d)); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* Fix up interrupt lines */ 2698c2ecf20Sopenharmony_ci d->irq = ssb_mips_irq(extpci_core->dev) + 2; 2708c2ecf20Sopenharmony_ci pci_write_config_byte(d, PCI_INTERRUPT_LINE, d->irq); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/* Early PCI fixup for a device on the PCI-core bridge. */ 2768c2ecf20Sopenharmony_cistatic void ssb_pcicore_fixup_pcibridge(struct pci_dev *dev) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci u8 lat; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (dev->bus->ops != &ssb_pcicore_pciops) { 2818c2ecf20Sopenharmony_ci /* This is not a device on the PCI-core bridge. */ 2828c2ecf20Sopenharmony_ci return; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci if (dev->bus->number != 0 || PCI_SLOT(dev->devfn) != 0) 2858c2ecf20Sopenharmony_ci return; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci dev_info(&dev->dev, "PCI: Fixing up bridge %s\n", pci_name(dev)); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Enable PCI bridge bus mastering and memory space */ 2908c2ecf20Sopenharmony_ci pci_set_master(dev); 2918c2ecf20Sopenharmony_ci if (pcibios_enable_device(dev, ~0) < 0) { 2928c2ecf20Sopenharmony_ci dev_err(&dev->dev, "PCI: SSB bridge enable failed\n"); 2938c2ecf20Sopenharmony_ci return; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Enable PCI bridge BAR1 prefetch and burst */ 2978c2ecf20Sopenharmony_ci pci_write_config_dword(dev, SSB_BAR1_CONTROL, 3); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* Make sure our latency is high enough to handle the devices behind us */ 3008c2ecf20Sopenharmony_ci lat = 168; 3018c2ecf20Sopenharmony_ci dev_info(&dev->dev, 3028c2ecf20Sopenharmony_ci "PCI: Fixing latency timer of device %s to %u\n", 3038c2ecf20Sopenharmony_ci pci_name(dev), lat); 3048c2ecf20Sopenharmony_ci pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat); 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, ssb_pcicore_fixup_pcibridge); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/* PCI device IRQ mapping. */ 3098c2ecf20Sopenharmony_ciint ssb_pcicore_pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci if (dev->bus->ops != &ssb_pcicore_pciops) { 3128c2ecf20Sopenharmony_ci /* This is not a device on the PCI-core bridge. */ 3138c2ecf20Sopenharmony_ci return -ENODEV; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci return ssb_mips_irq(extpci_core->dev) + 2; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic void ssb_pcicore_init_hostmode(struct ssb_pcicore *pc) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci u32 val; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (WARN_ON(extpci_core)) 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci extpci_core = pc; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci dev_dbg(pc->dev->dev, "PCIcore in host mode found\n"); 3278c2ecf20Sopenharmony_ci /* Reset devices on the external PCI bus */ 3288c2ecf20Sopenharmony_ci val = SSB_PCICORE_CTL_RST_OE; 3298c2ecf20Sopenharmony_ci val |= SSB_PCICORE_CTL_CLK_OE; 3308c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_CTL, val); 3318c2ecf20Sopenharmony_ci val |= SSB_PCICORE_CTL_CLK; /* Clock on */ 3328c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_CTL, val); 3338c2ecf20Sopenharmony_ci udelay(150); /* Assertion time demanded by the PCI standard */ 3348c2ecf20Sopenharmony_ci val |= SSB_PCICORE_CTL_RST; /* Deassert RST# */ 3358c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_CTL, val); 3368c2ecf20Sopenharmony_ci val = SSB_PCICORE_ARBCTL_INTERN; 3378c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_ARBCTL, val); 3388c2ecf20Sopenharmony_ci udelay(1); /* Assertion time demanded by the PCI standard */ 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (pc->dev->bus->has_cardbus_slot) { 3418c2ecf20Sopenharmony_ci dev_dbg(pc->dev->dev, "CardBus slot detected\n"); 3428c2ecf20Sopenharmony_ci pc->cardbusmode = 1; 3438c2ecf20Sopenharmony_ci /* GPIO 1 resets the bridge */ 3448c2ecf20Sopenharmony_ci ssb_gpio_out(pc->dev->bus, 1, 1); 3458c2ecf20Sopenharmony_ci ssb_gpio_outen(pc->dev->bus, 1, 1); 3468c2ecf20Sopenharmony_ci pcicore_write16(pc, SSB_PCICORE_SPROM(0), 3478c2ecf20Sopenharmony_ci pcicore_read16(pc, SSB_PCICORE_SPROM(0)) 3488c2ecf20Sopenharmony_ci | 0x0400); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* 64MB I/O window */ 3528c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI0, 3538c2ecf20Sopenharmony_ci SSB_PCICORE_SBTOPCI_IO); 3548c2ecf20Sopenharmony_ci /* 64MB config space */ 3558c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI1, 3568c2ecf20Sopenharmony_ci SSB_PCICORE_SBTOPCI_CFG0); 3578c2ecf20Sopenharmony_ci /* 1GB memory window */ 3588c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, 3598c2ecf20Sopenharmony_ci SSB_PCICORE_SBTOPCI_MEM | SSB_PCI_DMA); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* 3628c2ecf20Sopenharmony_ci * Accessing PCI config without a proper delay after devices reset (not 3638c2ecf20Sopenharmony_ci * GPIO reset) was causing reboots on WRT300N v1.0 (BCM4704). 3648c2ecf20Sopenharmony_ci * Tested delay 850 us lowered reboot chance to 50-80%, 1000 us fixed it 3658c2ecf20Sopenharmony_ci * completely. Flushing all writes was also tested but with no luck. 3668c2ecf20Sopenharmony_ci * The same problem was reported for WRT350N v1 (BCM4705), so we just 3678c2ecf20Sopenharmony_ci * sleep here unconditionally. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Enable PCI bridge BAR0 prefetch and burst */ 3728c2ecf20Sopenharmony_ci val = PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; 3738c2ecf20Sopenharmony_ci ssb_extpci_write_config(pc, 0, 0, 0, PCI_COMMAND, &val, 2); 3748c2ecf20Sopenharmony_ci /* Clear error conditions */ 3758c2ecf20Sopenharmony_ci val = 0; 3768c2ecf20Sopenharmony_ci ssb_extpci_write_config(pc, 0, 0, 0, PCI_STATUS, &val, 2); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Enable PCI interrupts */ 3798c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_IMASK, 3808c2ecf20Sopenharmony_ci SSB_PCICORE_IMASK_INTA); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Ok, ready to run, register it to the system. 3838c2ecf20Sopenharmony_ci * The following needs change, if we want to port hostmode 3848c2ecf20Sopenharmony_ci * to non-MIPS platform. */ 3858c2ecf20Sopenharmony_ci ssb_pcicore_controller.io_map_base = (unsigned long)ioremap(SSB_PCI_MEM, 0x04000000); 3868c2ecf20Sopenharmony_ci set_io_port_base(ssb_pcicore_controller.io_map_base); 3878c2ecf20Sopenharmony_ci /* Give some time to the PCI controller to configure itself with the new 3888c2ecf20Sopenharmony_ci * values. Not waiting at this point causes crashes of the machine. */ 3898c2ecf20Sopenharmony_ci mdelay(10); 3908c2ecf20Sopenharmony_ci register_pci_controller(&ssb_pcicore_controller); 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_cistatic int pcicore_is_in_hostmode(struct ssb_pcicore *pc) 3948c2ecf20Sopenharmony_ci{ 3958c2ecf20Sopenharmony_ci struct ssb_bus *bus = pc->dev->bus; 3968c2ecf20Sopenharmony_ci u16 chipid_top; 3978c2ecf20Sopenharmony_ci u32 tmp; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci chipid_top = (bus->chip_id & 0xFF00); 4008c2ecf20Sopenharmony_ci if (chipid_top != 0x4700 && 4018c2ecf20Sopenharmony_ci chipid_top != 0x5300) 4028c2ecf20Sopenharmony_ci return 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci if (bus->sprom.boardflags_lo & SSB_PCICORE_BFL_NOPCI) 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* The 200-pin BCM4712 package does not bond out PCI. Even when 4088c2ecf20Sopenharmony_ci * PCI is bonded out, some boards may leave the pins floating. */ 4098c2ecf20Sopenharmony_ci if (bus->chip_id == 0x4712) { 4108c2ecf20Sopenharmony_ci if (bus->chip_package == SSB_CHIPPACK_BCM4712S) 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci if (bus->chip_package == SSB_CHIPPACK_BCM4712M) 4138c2ecf20Sopenharmony_ci return 0; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci if (bus->chip_id == 0x5350) 4168c2ecf20Sopenharmony_ci return 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return !mips_busprobe32(tmp, (bus->mmio + (pc->dev->core_index * SSB_CORE_SIZE))); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCICORE_HOSTMODE */ 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci/************************************************** 4238c2ecf20Sopenharmony_ci * Workarounds. 4248c2ecf20Sopenharmony_ci **************************************************/ 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void ssb_pcicore_fix_sprom_core_index(struct ssb_pcicore *pc) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci u16 tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(0)); 4298c2ecf20Sopenharmony_ci if (((tmp & 0xF000) >> 12) != pc->dev->core_index) { 4308c2ecf20Sopenharmony_ci tmp &= ~0xF000; 4318c2ecf20Sopenharmony_ci tmp |= (pc->dev->core_index << 12); 4328c2ecf20Sopenharmony_ci pcicore_write16(pc, SSB_PCICORE_SPROM(0), tmp); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic u8 ssb_pcicore_polarity_workaround(struct ssb_pcicore *pc) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci return (ssb_pcie_read(pc, 0x204) & 0x10) ? 0xC0 : 0x80; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic void ssb_pcicore_serdes_workaround(struct ssb_pcicore *pc) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci const u8 serdes_pll_device = 0x1D; 4448c2ecf20Sopenharmony_ci const u8 serdes_rx_device = 0x1F; 4458c2ecf20Sopenharmony_ci u16 tmp; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci ssb_pcie_mdio_write(pc, serdes_rx_device, 1 /* Control */, 4488c2ecf20Sopenharmony_ci ssb_pcicore_polarity_workaround(pc)); 4498c2ecf20Sopenharmony_ci tmp = ssb_pcie_mdio_read(pc, serdes_pll_device, 1 /* Control */); 4508c2ecf20Sopenharmony_ci if (tmp & 0x4000) 4518c2ecf20Sopenharmony_ci ssb_pcie_mdio_write(pc, serdes_pll_device, 1, tmp & ~0x4000); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic void ssb_pcicore_pci_setup_workarounds(struct ssb_pcicore *pc) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct ssb_device *pdev = pc->dev; 4578c2ecf20Sopenharmony_ci struct ssb_bus *bus = pdev->bus; 4588c2ecf20Sopenharmony_ci u32 tmp; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2); 4618c2ecf20Sopenharmony_ci tmp |= SSB_PCICORE_SBTOPCI_PREF; 4628c2ecf20Sopenharmony_ci tmp |= SSB_PCICORE_SBTOPCI_BURST; 4638c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (pdev->id.revision < 5) { 4668c2ecf20Sopenharmony_ci tmp = ssb_read32(pdev, SSB_IMCFGLO); 4678c2ecf20Sopenharmony_ci tmp &= ~SSB_IMCFGLO_SERTO; 4688c2ecf20Sopenharmony_ci tmp |= 2; 4698c2ecf20Sopenharmony_ci tmp &= ~SSB_IMCFGLO_REQTO; 4708c2ecf20Sopenharmony_ci tmp |= 3 << SSB_IMCFGLO_REQTO_SHIFT; 4718c2ecf20Sopenharmony_ci ssb_write32(pdev, SSB_IMCFGLO, tmp); 4728c2ecf20Sopenharmony_ci ssb_commit_settings(bus); 4738c2ecf20Sopenharmony_ci } else if (pdev->id.revision >= 11) { 4748c2ecf20Sopenharmony_ci tmp = pcicore_read32(pc, SSB_PCICORE_SBTOPCI2); 4758c2ecf20Sopenharmony_ci tmp |= SSB_PCICORE_SBTOPCI_MRM; 4768c2ecf20Sopenharmony_ci pcicore_write32(pc, SSB_PCICORE_SBTOPCI2, tmp); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void ssb_pcicore_pcie_setup_workarounds(struct ssb_pcicore *pc) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci u32 tmp; 4838c2ecf20Sopenharmony_ci u8 rev = pc->dev->id.revision; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (rev == 0 || rev == 1) { 4868c2ecf20Sopenharmony_ci /* TLP Workaround register. */ 4878c2ecf20Sopenharmony_ci tmp = ssb_pcie_read(pc, 0x4); 4888c2ecf20Sopenharmony_ci tmp |= 0x8; 4898c2ecf20Sopenharmony_ci ssb_pcie_write(pc, 0x4, tmp); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci if (rev == 1) { 4928c2ecf20Sopenharmony_ci /* DLLP Link Control register. */ 4938c2ecf20Sopenharmony_ci tmp = ssb_pcie_read(pc, 0x100); 4948c2ecf20Sopenharmony_ci tmp |= 0x40; 4958c2ecf20Sopenharmony_ci ssb_pcie_write(pc, 0x100, tmp); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci if (rev == 0) { 4998c2ecf20Sopenharmony_ci const u8 serdes_rx_device = 0x1F; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci ssb_pcie_mdio_write(pc, serdes_rx_device, 5028c2ecf20Sopenharmony_ci 2 /* Timer */, 0x8128); 5038c2ecf20Sopenharmony_ci ssb_pcie_mdio_write(pc, serdes_rx_device, 5048c2ecf20Sopenharmony_ci 6 /* CDR */, 0x0100); 5058c2ecf20Sopenharmony_ci ssb_pcie_mdio_write(pc, serdes_rx_device, 5068c2ecf20Sopenharmony_ci 7 /* CDR BW */, 0x1466); 5078c2ecf20Sopenharmony_ci } else if (rev == 3 || rev == 4 || rev == 5) { 5088c2ecf20Sopenharmony_ci /* TODO: DLLP Power Management Threshold */ 5098c2ecf20Sopenharmony_ci ssb_pcicore_serdes_workaround(pc); 5108c2ecf20Sopenharmony_ci /* TODO: ASPM */ 5118c2ecf20Sopenharmony_ci } else if (rev == 7) { 5128c2ecf20Sopenharmony_ci /* TODO: No PLL down */ 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (rev >= 6) { 5168c2ecf20Sopenharmony_ci /* Miscellaneous Configuration Fixup */ 5178c2ecf20Sopenharmony_ci tmp = pcicore_read16(pc, SSB_PCICORE_SPROM(5)); 5188c2ecf20Sopenharmony_ci if (!(tmp & 0x8000)) 5198c2ecf20Sopenharmony_ci pcicore_write16(pc, SSB_PCICORE_SPROM(5), 5208c2ecf20Sopenharmony_ci tmp | 0x8000); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci} 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/************************************************** 5258c2ecf20Sopenharmony_ci * Generic and Clientmode operation code. 5268c2ecf20Sopenharmony_ci **************************************************/ 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic void ssb_pcicore_init_clientmode(struct ssb_pcicore *pc) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct ssb_device *pdev = pc->dev; 5318c2ecf20Sopenharmony_ci struct ssb_bus *bus = pdev->bus; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci if (bus->bustype == SSB_BUSTYPE_PCI) 5348c2ecf20Sopenharmony_ci ssb_pcicore_fix_sprom_core_index(pc); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Disable PCI interrupts. */ 5378c2ecf20Sopenharmony_ci ssb_write32(pdev, SSB_INTVEC, 0); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Additional PCIe always once-executed workarounds */ 5408c2ecf20Sopenharmony_ci if (pc->dev->id.coreid == SSB_DEV_PCIE) { 5418c2ecf20Sopenharmony_ci ssb_pcicore_serdes_workaround(pc); 5428c2ecf20Sopenharmony_ci /* TODO: ASPM */ 5438c2ecf20Sopenharmony_ci /* TODO: Clock Request Update */ 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci} 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_civoid ssb_pcicore_init(struct ssb_pcicore *pc) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct ssb_device *dev = pc->dev; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (!dev) 5528c2ecf20Sopenharmony_ci return; 5538c2ecf20Sopenharmony_ci if (!ssb_device_is_enabled(dev)) 5548c2ecf20Sopenharmony_ci ssb_device_enable(dev, 0); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci#ifdef CONFIG_SSB_PCICORE_HOSTMODE 5578c2ecf20Sopenharmony_ci pc->hostmode = pcicore_is_in_hostmode(pc); 5588c2ecf20Sopenharmony_ci if (pc->hostmode) 5598c2ecf20Sopenharmony_ci ssb_pcicore_init_hostmode(pc); 5608c2ecf20Sopenharmony_ci#endif /* CONFIG_SSB_PCICORE_HOSTMODE */ 5618c2ecf20Sopenharmony_ci if (!pc->hostmode) 5628c2ecf20Sopenharmony_ci ssb_pcicore_init_clientmode(pc); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic u32 ssb_pcie_read(struct ssb_pcicore *pc, u32 address) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci pcicore_write32(pc, 0x130, address); 5688c2ecf20Sopenharmony_ci return pcicore_read32(pc, 0x134); 5698c2ecf20Sopenharmony_ci} 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic void ssb_pcie_write(struct ssb_pcicore *pc, u32 address, u32 data) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci pcicore_write32(pc, 0x130, address); 5748c2ecf20Sopenharmony_ci pcicore_write32(pc, 0x134, data); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic void ssb_pcie_mdio_set_phy(struct ssb_pcicore *pc, u8 phy) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci const u16 mdio_control = 0x128; 5808c2ecf20Sopenharmony_ci const u16 mdio_data = 0x12C; 5818c2ecf20Sopenharmony_ci u32 v; 5828c2ecf20Sopenharmony_ci int i; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci v = (1 << 30); /* Start of Transaction */ 5858c2ecf20Sopenharmony_ci v |= (1 << 28); /* Write Transaction */ 5868c2ecf20Sopenharmony_ci v |= (1 << 17); /* Turnaround */ 5878c2ecf20Sopenharmony_ci v |= (0x1F << 18); 5888c2ecf20Sopenharmony_ci v |= (phy << 4); 5898c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_data, v); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci udelay(10); 5928c2ecf20Sopenharmony_ci for (i = 0; i < 200; i++) { 5938c2ecf20Sopenharmony_ci v = pcicore_read32(pc, mdio_control); 5948c2ecf20Sopenharmony_ci if (v & 0x100 /* Trans complete */) 5958c2ecf20Sopenharmony_ci break; 5968c2ecf20Sopenharmony_ci msleep(1); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic u16 ssb_pcie_mdio_read(struct ssb_pcicore *pc, u8 device, u8 address) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci const u16 mdio_control = 0x128; 6038c2ecf20Sopenharmony_ci const u16 mdio_data = 0x12C; 6048c2ecf20Sopenharmony_ci int max_retries = 10; 6058c2ecf20Sopenharmony_ci u16 ret = 0; 6068c2ecf20Sopenharmony_ci u32 v; 6078c2ecf20Sopenharmony_ci int i; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci v = 0x80; /* Enable Preamble Sequence */ 6108c2ecf20Sopenharmony_ci v |= 0x2; /* MDIO Clock Divisor */ 6118c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_control, v); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if (pc->dev->id.revision >= 10) { 6148c2ecf20Sopenharmony_ci max_retries = 200; 6158c2ecf20Sopenharmony_ci ssb_pcie_mdio_set_phy(pc, device); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci v = (1 << 30); /* Start of Transaction */ 6198c2ecf20Sopenharmony_ci v |= (1 << 29); /* Read Transaction */ 6208c2ecf20Sopenharmony_ci v |= (1 << 17); /* Turnaround */ 6218c2ecf20Sopenharmony_ci if (pc->dev->id.revision < 10) 6228c2ecf20Sopenharmony_ci v |= (u32)device << 22; 6238c2ecf20Sopenharmony_ci v |= (u32)address << 18; 6248c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_data, v); 6258c2ecf20Sopenharmony_ci /* Wait for the device to complete the transaction */ 6268c2ecf20Sopenharmony_ci udelay(10); 6278c2ecf20Sopenharmony_ci for (i = 0; i < max_retries; i++) { 6288c2ecf20Sopenharmony_ci v = pcicore_read32(pc, mdio_control); 6298c2ecf20Sopenharmony_ci if (v & 0x100 /* Trans complete */) { 6308c2ecf20Sopenharmony_ci udelay(10); 6318c2ecf20Sopenharmony_ci ret = pcicore_read32(pc, mdio_data); 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci msleep(1); 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_control, 0); 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic void ssb_pcie_mdio_write(struct ssb_pcicore *pc, u8 device, 6418c2ecf20Sopenharmony_ci u8 address, u16 data) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci const u16 mdio_control = 0x128; 6448c2ecf20Sopenharmony_ci const u16 mdio_data = 0x12C; 6458c2ecf20Sopenharmony_ci int max_retries = 10; 6468c2ecf20Sopenharmony_ci u32 v; 6478c2ecf20Sopenharmony_ci int i; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci v = 0x80; /* Enable Preamble Sequence */ 6508c2ecf20Sopenharmony_ci v |= 0x2; /* MDIO Clock Divisor */ 6518c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_control, v); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (pc->dev->id.revision >= 10) { 6548c2ecf20Sopenharmony_ci max_retries = 200; 6558c2ecf20Sopenharmony_ci ssb_pcie_mdio_set_phy(pc, device); 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci v = (1 << 30); /* Start of Transaction */ 6598c2ecf20Sopenharmony_ci v |= (1 << 28); /* Write Transaction */ 6608c2ecf20Sopenharmony_ci v |= (1 << 17); /* Turnaround */ 6618c2ecf20Sopenharmony_ci if (pc->dev->id.revision < 10) 6628c2ecf20Sopenharmony_ci v |= (u32)device << 22; 6638c2ecf20Sopenharmony_ci v |= (u32)address << 18; 6648c2ecf20Sopenharmony_ci v |= data; 6658c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_data, v); 6668c2ecf20Sopenharmony_ci /* Wait for the device to complete the transaction */ 6678c2ecf20Sopenharmony_ci udelay(10); 6688c2ecf20Sopenharmony_ci for (i = 0; i < max_retries; i++) { 6698c2ecf20Sopenharmony_ci v = pcicore_read32(pc, mdio_control); 6708c2ecf20Sopenharmony_ci if (v & 0x100 /* Trans complete */) 6718c2ecf20Sopenharmony_ci break; 6728c2ecf20Sopenharmony_ci msleep(1); 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci pcicore_write32(pc, mdio_control, 0); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ciint ssb_pcicore_dev_irqvecs_enable(struct ssb_pcicore *pc, 6788c2ecf20Sopenharmony_ci struct ssb_device *dev) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci struct ssb_device *pdev = pc->dev; 6818c2ecf20Sopenharmony_ci struct ssb_bus *bus; 6828c2ecf20Sopenharmony_ci int err = 0; 6838c2ecf20Sopenharmony_ci u32 tmp; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (dev->bus->bustype != SSB_BUSTYPE_PCI) { 6868c2ecf20Sopenharmony_ci /* This SSB device is not on a PCI host-bus. So the IRQs are 6878c2ecf20Sopenharmony_ci * not routed through the PCI core. 6888c2ecf20Sopenharmony_ci * So we must not enable routing through the PCI core. */ 6898c2ecf20Sopenharmony_ci goto out; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (!pdev) 6938c2ecf20Sopenharmony_ci goto out; 6948c2ecf20Sopenharmony_ci bus = pdev->bus; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci might_sleep_if(pdev->id.coreid != SSB_DEV_PCI); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci /* Enable interrupts for this device. */ 6998c2ecf20Sopenharmony_ci if ((pdev->id.revision >= 6) || (pdev->id.coreid == SSB_DEV_PCIE)) { 7008c2ecf20Sopenharmony_ci u32 coremask; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Calculate the "coremask" for the device. */ 7038c2ecf20Sopenharmony_ci coremask = (1 << dev->core_index); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci WARN_ON(bus->bustype != SSB_BUSTYPE_PCI); 7068c2ecf20Sopenharmony_ci err = pci_read_config_dword(bus->host_pci, SSB_PCI_IRQMASK, &tmp); 7078c2ecf20Sopenharmony_ci if (err) 7088c2ecf20Sopenharmony_ci goto out; 7098c2ecf20Sopenharmony_ci tmp |= coremask << 8; 7108c2ecf20Sopenharmony_ci err = pci_write_config_dword(bus->host_pci, SSB_PCI_IRQMASK, tmp); 7118c2ecf20Sopenharmony_ci if (err) 7128c2ecf20Sopenharmony_ci goto out; 7138c2ecf20Sopenharmony_ci } else { 7148c2ecf20Sopenharmony_ci u32 intvec; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci intvec = ssb_read32(pdev, SSB_INTVEC); 7178c2ecf20Sopenharmony_ci tmp = ssb_read32(dev, SSB_TPSFLAG); 7188c2ecf20Sopenharmony_ci tmp &= SSB_TPSFLAG_BPFLAG; 7198c2ecf20Sopenharmony_ci intvec |= (1 << tmp); 7208c2ecf20Sopenharmony_ci ssb_write32(pdev, SSB_INTVEC, intvec); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Setup PCIcore operation. */ 7248c2ecf20Sopenharmony_ci if (pc->setup_done) 7258c2ecf20Sopenharmony_ci goto out; 7268c2ecf20Sopenharmony_ci if (pdev->id.coreid == SSB_DEV_PCI) { 7278c2ecf20Sopenharmony_ci ssb_pcicore_pci_setup_workarounds(pc); 7288c2ecf20Sopenharmony_ci } else { 7298c2ecf20Sopenharmony_ci WARN_ON(pdev->id.coreid != SSB_DEV_PCIE); 7308c2ecf20Sopenharmony_ci ssb_pcicore_pcie_setup_workarounds(pc); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci pc->setup_done = 1; 7338c2ecf20Sopenharmony_ciout: 7348c2ecf20Sopenharmony_ci return err; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ssb_pcicore_dev_irqvecs_enable); 737