| /* |
| * (C) Copyright 2011 Quantenna Communications Inc. |
| * |
| * Description : ruby PCI bus setup |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <linux/kernel.h> |
| #include <linux/types.h> |
| #include <linux/init.h> |
| #include <linux/mm.h> |
| #include <linux/slab.h> |
| #include <linux/jiffies.h> |
| #include <asm/io.h> |
| #include <linux/ctype.h> |
| #include <linux/pci.h> |
| #include <linux/io.h> |
| #include <linux/time.h> |
| #include <linux/timex.h> |
| #include <linux/interrupt.h> |
| #include <asm/uaccess.h> |
| #include <linux/delay.h> |
| #include <linux/version.h> |
| #include <common/topaz_platform.h> |
| #include "pcibios.h" |
| |
| #define BUS_SCAN_TRY_MAX 1000 |
| #ifdef TOPAZ_PLATFORM |
| #define PCIE_DEV_LINKUP_MASK TOPAZ_PCIE_LINKUP |
| #else |
| #define PCIE_DEV_LINKUP_MASK 0x700 |
| #endif |
| |
| |
| static int |
| rpcic_read_config (struct pci_bus *bus, |
| unsigned int devfn, |
| int where, |
| int size, |
| u32 *val); |
| |
| static int |
| rpcic_write_config (struct pci_bus *bus, |
| unsigned int devfn, |
| int where, |
| int size, |
| u32 val); |
| |
| |
| |
| static struct pci_ops rpcic_ops = { |
| .read = rpcic_read_config, |
| .write = rpcic_write_config, |
| }; |
| |
| typedef struct |
| { |
| //TODO: define DW_PCIE_regs? |
| } DW_PCIE_regs; |
| |
| struct ruby_pci |
| { |
| DW_PCIE_regs *regs; |
| int irq; |
| void __iomem *cfg_virt; |
| void __iomem *cfg0; |
| void __iomem *cfg1; |
| void __iomem *cfg2; |
| void __iomem *cfg3; |
| struct resource mem_res; |
| struct resource io_res; |
| struct pci_bus *bus; |
| }; |
| |
| /* RC mode or EP mode, actually only RC mode in used */ |
| int pci_mode=0; |
| |
| /* PCI bus 0 */ |
| struct ruby_pci rpcib0; |
| |
| |
| static int |
| rpcic_read_word(unsigned int busno, |
| unsigned int devfn, |
| int where, |
| u32 *val) |
| { |
| struct ruby_pci *rpcic = &rpcib0; |
| volatile uint32_t *addr; |
| |
| if (where&3) |
| panic("%s read address not aligned 0x%x\n", __FUNCTION__, where); |
| |
| addr = rpcic->cfg_virt + ((devfn&0xff)<<8) + where; |
| |
| *val = readl(addr); |
| |
| DEBUG("%s addr 0x%X val_addr 0x%X \n", __FUNCTION__, addr, *val); |
| DEBUG("%s bus %u devfn %u \n", __FUNCTION__, busno, devfn); |
| |
| return 0; |
| } |
| |
| /* |
| * if addr&0x4 we should do some workaround |
| */ |
| static int |
| rpcic_write_word(unsigned int busno, |
| unsigned int devfn, |
| int where, |
| u32 val) |
| { |
| struct ruby_pci *rpcic = &rpcib0; |
| volatile uint32_t *addr; |
| |
| if(where&3) |
| panic("%s read address not aligned 0x%x\n", __FUNCTION__, where); |
| |
| addr = rpcic->cfg_virt + ((devfn&0xff)<<8) + where; |
| #if 0 |
| writel(val, addr); |
| #else |
| if ((u32)addr & 0x4) { |
| u32 temp = readl(RUBY_PCIE_CONFIG_REGION + 0x10); |
| writel(val,RUBY_PCIE_CONFIG_REGION + 0x10); |
| writel( val,addr); |
| writel(temp,RUBY_PCIE_CONFIG_REGION + 0x10); |
| } else { |
| writel(val, addr); |
| } |
| #endif |
| |
| DEBUG("%s addr 0x%X -> 0x%X \n", __FUNCTION__, addr, val); |
| DEBUG("%s bus %u devfn %u \n", __FUNCTION__, busno, devfn); |
| |
| return 0; |
| } |
| |
| |
| /* |
| * Read ruby PCI config space |
| */ |
| static int |
| rpcic_read_config (struct pci_bus *bus, |
| unsigned int devfn, |
| int where, |
| int size, |
| u32 *value) |
| { |
| u32 val; |
| int retval = -EINVAL; |
| |
| DEBUG("%s called with bno. %d devfn %u where %d size %d \n ", |
| __FUNCTION__, bus->number, devfn, where, size); |
| |
| if (bus->number != 0) |
| return -EINVAL; |
| |
| if (PCI_SLOT(devfn) > 1) |
| return 0; |
| |
| switch (size) { |
| case 1: |
| rpcic_read_word(bus->number, devfn, where&~3, &val); |
| *value = 0xff & (val >> (8*(where & 3))); |
| retval=0; |
| break; |
| |
| case 2: |
| if (where&1) return -EINVAL; |
| rpcic_read_word(bus->number, devfn, where&~3, &val); |
| *value = 0xffff & (val >> (8*(where & 3))); |
| retval=0; |
| break; |
| |
| case 4: |
| if (where&3) return -EINVAL; |
| rpcic_read_word(bus->number, devfn, where, value); |
| retval=0; |
| break; |
| } |
| |
| DEBUG(" value=0x%x\n", *value); |
| return retval; |
| } |
| |
| |
| /* |
| * Write ruby PCI config space |
| */ |
| static int |
| rpcic_write_config (struct pci_bus *bus, |
| unsigned int devfn, |
| int where, |
| int size, |
| u32 val) |
| { |
| u32 tv; |
| |
| DEBUG("%s called with bno. %d devfn %u where 0x%x size %d val 0x%x \n", |
| __FUNCTION__, bus->number, devfn, where, size, val); |
| |
| if (bus->number != 0) |
| return -EINVAL; |
| |
| switch (size) { |
| case 1: |
| rpcic_read_word(bus->number, devfn, where&~3, &tv); |
| tv = (tv & ~(0xff << (8*(where&3)))) | ((0xff&val) << (8*(where&3))); |
| return rpcic_write_word(bus->number, devfn, where&~3, tv); |
| |
| case 2: |
| if (where&1) return -EINVAL; |
| rpcic_read_word(bus->number, devfn, where&~3, &tv); |
| tv = (tv & ~(0xffff << (8*(where&3)))) | ((0xffff&val) << (8*(where&3))); |
| return rpcic_write_word(bus->number, devfn, where&~3, tv); |
| |
| case 4: |
| if (where&3) return -EINVAL; |
| return rpcic_write_word(bus->number, devfn, where, val); |
| } |
| |
| return 0; |
| } |
| |
| #ifdef TOPAZ_PLATFORM |
| static int |
| rpcic_find_capability(int cap) |
| { |
| uint32_t pos; |
| uint32_t cap_found; |
| |
| pos = (readl(RUBY_PCIE_REG_BASE + PCI_CAPABILITY_LIST) & 0x000000ff); |
| while (pos) { |
| cap_found = (readl(RUBY_PCIE_REG_BASE + pos) & 0x0000ffff); |
| if ((cap_found & 0x000000ff)== (uint32_t)cap) |
| break; |
| |
| pos = ((cap_found >> 8) & 0x000000ff); |
| } |
| |
| return pos; |
| } |
| #endif |
| /* |
| * PCI bus scan and initialization |
| */ |
| static void |
| pci_bus_init (void) |
| { |
| struct ruby_pci *rpcic = NULL; |
| unsigned int ep_up = 0; |
| int i = 0; |
| |
| rpcic = &rpcib0; |
| rpcic->cfg_virt = (void *)RUBY_PCIE_CONFIG_REGION; |
| rpcic->mem_res.name = "RUBY PCI Memory space"; |
| rpcic->mem_res.start = RUBY_PCI_RC_MEM_START; |
| rpcic->mem_res.end = RUBY_PCI_RC_MEM_START + RUBY_PCI_RC_MEM_WINDOW -1; |
| rpcic->mem_res.flags = IORESOURCE_MEM; |
| #ifdef TOPAZ_PLATFORM |
| rpcic->irq = TOPAZ_IRQ_PCIE; |
| #else |
| rpcic->irq = RUBY_IRQ_INTA; |
| #endif |
| |
| if (request_resource(&iomem_resource, &rpcic->mem_res) < 0) { |
| printk(KERN_WARNING "WARNING: Failed to alloc IOMem!\n"); |
| goto out; |
| } |
| |
| /* waiting for end point linked up in the PCI bus */ |
| for (i = 0; i < BUS_SCAN_TRY_MAX; i++) { |
| #ifdef TOPAZ_PLATFORM |
| ep_up = readl(TOPAZ_PCIE_STAT); |
| #else |
| ep_up = readl(RUBY_SYS_CTL_CSR); |
| #endif |
| if ( (ep_up & PCIE_DEV_LINKUP_MASK) == PCIE_DEV_LINKUP_MASK ) { |
| break; |
| } |
| if (i%100 == 0) { |
| printk(KERN_INFO "PCI Bus Scan loop for device link up\n"); |
| } |
| udelay(1000); |
| } |
| |
| /* Set RC Max_Payload_Size to 256 for topaz */ |
| #ifdef TOPAZ_PLATFORM |
| int pos; |
| uint32_t dev_ctl_sts; |
| pos = rpcic_find_capability(PCI_CAP_ID_EXP); |
| if (!pos) { |
| printk(KERN_ERR "Could not find PCI Express capability in RC config space!\n"); |
| } else { |
| dev_ctl_sts = readl(RUBY_PCIE_REG_BASE + pos + PCI_EXP_DEVCTL); |
| dev_ctl_sts = ((dev_ctl_sts & ~PCI_EXP_DEVCTL_PAYLOAD) | BIT(5)); |
| writel(dev_ctl_sts, RUBY_PCIE_REG_BASE + pos + PCI_EXP_DEVCTL); |
| } |
| #endif |
| |
| if ( (ep_up & PCIE_DEV_LINKUP_MASK) == PCIE_DEV_LINKUP_MASK ) { |
| rpcic->bus = pci_scan_bus(0, &rpcic_ops, rpcic); |
| pci_assign_unassigned_resources(); |
| printk(KERN_INFO "PCI Bus Scan completed!\n"); |
| } else { |
| printk(KERN_INFO "PCI Bus Scan doesn't find any device link up!\n"); |
| } |
| |
| out: |
| return; |
| } |
| |
| /* |
| * Called after each bus is probed, but before its children are examined. |
| * Enable response for the resource of dev and assign IRQ num |
| */ |
| void __devinit |
| pcibios_fixup_bus (struct pci_bus *bus) |
| { |
| struct ruby_pci *rpcic; |
| struct pci_dev *dev; |
| int i, has_io, has_mem; |
| u32 cmd; |
| |
| printk(KERN_INFO "%s called bus name %.10s no %d flags %x next_dev %p \n", |
| __FUNCTION__, bus->name, bus->number, bus->bus_flags, bus->devices.next); |
| |
| rpcic = (struct ruby_pci *) bus->sysdata; |
| |
| bus->resource[0] = &rpcic->io_res; |
| bus->resource[1] = &rpcic->mem_res; |
| |
| if (bus->number != 0) { |
| printk(KERN_WARNING "pcibios_fixup_bus: nonzero bus 0x%x\n", bus->number); |
| return; |
| } |
| |
| list_for_each_entry(dev, &bus->devices, bus_list) { |
| has_io = has_mem = 0; |
| |
| for (i=0; i < RUBY_PCIE_BAR_NUM; i++) { |
| unsigned long f = dev->resource[i].flags; |
| if (f & IORESOURCE_IO) { |
| has_io = 1; |
| } else if (f & IORESOURCE_MEM) { |
| has_mem = 1; |
| } |
| } |
| rpcic_read_config(dev->bus, dev->devfn, PCI_COMMAND, 2, &cmd); |
| printk(KERN_INFO "%s: Device [%2x:%2x.%d] has mem %d io %d cmd %x \n", __FUNCTION__, |
| dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), has_mem, has_io, cmd); |
| |
| if (has_io && !(cmd & PCI_COMMAND_IO)) { |
| cmd |= PCI_COMMAND_IO; |
| rpcic_write_config(dev->bus, dev->devfn, PCI_COMMAND, 2, cmd); |
| printk(KERN_INFO "%s: Enabling I/O for device [%2x:%2x.%d]\n", __FUNCTION__, |
| dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
| } |
| if (has_mem && !(cmd & PCI_COMMAND_MEMORY)) { |
| cmd |= PCI_COMMAND_MEMORY; |
| rpcic_write_config(dev->bus, dev->devfn, PCI_COMMAND, 2, cmd); |
| printk(KERN_INFO "%s: Enabling memory for device [%2x:%2x.%d]\n", __FUNCTION__, |
| dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); |
| } |
| |
| dev->irq = rpcic->irq; |
| } |
| |
| } |
| |
| /* |
| * pcibios align resources() is called every time generic PCI code |
| * wants to generate a new address. The process of looking for |
| * an available address, each candidate is first "aligned" and |
| * then checked if the resource is available until a match is found. |
| * |
| */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,34) |
| resource_size_t |
| pcibios_align_resource (void *data, |
| const struct resource *res, |
| resource_size_t size, |
| resource_size_t align) |
| { |
| struct pci_dev *dev = data; |
| struct ruby_pci *rpcic = dev->sysdata; |
| resource_size_t start = res->start; |
| |
| DEBUG("%s called\n ", __FUNCTION__); |
| |
| if (res->flags & IORESOURCE_IO) { |
| /* Make sure we start at our min on all hoses */ |
| if (start < PCIBIOS_MIN_IO + rpcic->io_res.start) |
| start = PCIBIOS_MIN_IO + rpcic->io_res.start; |
| |
| /* |
| * Put everything into 0x00-0xff region modulo 0x400 |
| */ |
| if (start & 0x300) |
| start = (start + 0x3ff) & ~0x3ff; |
| |
| } else if (res->flags & IORESOURCE_MEM) { |
| /* Make sure we start at our min on all hoses */ |
| if (start < PCIBIOS_MIN_MEM + rpcic->mem_res.start) |
| start = PCIBIOS_MIN_MEM + rpcic->mem_res.start; |
| } |
| |
| return start; |
| } |
| #else |
| void |
| pcibios_align_resource (void *data, |
| struct resource *res, |
| resource_size_t size, |
| resource_size_t align) |
| { |
| struct pci_dev *dev = data; |
| struct ruby_pci *rpcic = dev->sysdata; |
| unsigned long start = res->start; |
| |
| DEBUG("%s called\n ", __FUNCTION__); |
| |
| if (res->flags & IORESOURCE_IO) { |
| /* Make sure we start at our min on all hoses */ |
| if (start < PCIBIOS_MIN_IO + rpcic->io_res.start) |
| start = PCIBIOS_MIN_IO + rpcic->io_res.start; |
| |
| /* |
| * Put everything into 0x00-0xff region modulo 0x400 |
| */ |
| if (start & 0x300) |
| start = (start + 0x3ff) & ~0x3ff; |
| |
| } else if (res->flags & IORESOURCE_MEM) { |
| /* Make sure we start at our min on all hoses */ |
| if (start < PCIBIOS_MIN_MEM + rpcic->mem_res.start) |
| start = PCIBIOS_MIN_MEM + rpcic->mem_res.start; |
| } |
| |
| res->start = start; |
| } |
| #endif |
| |
| |
| /** |
| * pcibios_enable_device - Enable I/O and memory. |
| * @dev: PCI device to be enabled |
| */ |
| int |
| pcibios_enable_device (struct pci_dev *dev, |
| int mask) |
| { |
| u16 cmd, old_cmd; |
| int idx; |
| struct resource *r; |
| int pos = 0; |
| |
| pci_read_config_word(dev, PCI_COMMAND, &cmd); |
| old_cmd = cmd; |
| |
| for (idx=0; idx < RUBY_PCIE_BAR_NUM; idx++) { |
| /* Only set up the requested stuff */ |
| if (!(mask & (1<<idx))) |
| continue; |
| |
| r = &dev->resource[idx]; |
| |
| if (!r->start && r->end) { |
| printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", |
| pci_name(dev)); |
| return -EINVAL; |
| } |
| |
| if (r->flags & IORESOURCE_IO) { |
| cmd |= PCI_COMMAND_IO; |
| printk(KERN_INFO "Enabling IO\n"); |
| } |
| |
| if (r->flags & IORESOURCE_MEM) { |
| cmd |= PCI_COMMAND_MEMORY; |
| printk(KERN_INFO "Enabling MEM\n"); |
| } |
| } |
| |
| if (dev->resource[PCI_ROM_RESOURCE].start) |
| cmd |= PCI_COMMAND_MEMORY; |
| |
| if (cmd != old_cmd) { |
| printk(KERN_INFO "PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); |
| pci_write_config_word(dev, PCI_COMMAND, cmd); |
| } |
| |
| pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_IO|PCI_COMMAND_MEMORY); |
| |
| /* Set the device's MSI capability */ |
| pos = pci_find_capability(dev, PCI_CAP_ID_MSI); |
| if (!pos) { |
| printk(KERN_ERR "Error locating MSI capability position, using INTx instead\n"); |
| } else { |
| #ifdef TOPAZ_PLATFORM |
| /* Setup msi generation info */ |
| writel(TOPAZ_PCIE_MSI_REGION, TOPAZ_MSI_ADDR_LOWER); |
| writel(0, TOPAZ_MSI_ADDR_UPPER); |
| writel(BIT(0), TOPAZ_MSI_INT_ENABLE); |
| writel(0, TOPAZ_PCIE_MSI_MASK); |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| int |
| pcibios_assign_resource (struct pci_dev *pdev, |
| int resource) |
| { |
| printk(KERN_INFO "%s called\n", __FUNCTION__); |
| return -ENXIO; |
| } |
| |
| void __init |
| pcibios_update_irq (struct pci_dev *dev, |
| int irq) |
| { |
| printk(KERN_INFO "%s called for irq %d\n", __FUNCTION__, irq); |
| pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); |
| } |
| |
| |
| char * __devinit |
| pcibios_setup (char *str) |
| { |
| printk(KERN_INFO "%s called str %s\n", __FUNCTION__, str); |
| return str; |
| } |
| |
| /** |
| * ruby_pci_init - Initial PCI bus in RC mode |
| */ |
| static int __init |
| ruby_pci_init (void) |
| { |
| ruby_pci_create_sysfs(); |
| pci_mode = RC_MODE; |
| pci_bus_init(); |
| |
| return 0; |
| } |
| |
| subsys_initcall(ruby_pci_init); |