/****************************************************************************** * * Fusiv Vx185 PCIe HAL * Copyright (c) Ikanos Communications Inc. 2010 * * All rights are reserved. Reproduction in whole or in part is prohibited * without the written consent of the copyright owner. * * The information and contents of this file are the proprietary information * of Ikanos Communication and may not be disclosed or used without the * formal written approval of Ikanos Communication Inc. * * This Copyright notice may not be removed or modified without prior * written consent of Ikanos Communications, Inc. * *******************************************************************************/ /****************************************************************************** * This module provides the glue between Linux's PCI subsystem and Fusiv PCIe core hardware. * We basically provide hardware initialization function and glue for accessing configuration space. *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_READ_COUNT 500 /* * Read/write access functions for various sizes of values * in config space. */ static int fusiv_pcibios_read_port0( struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val) { unsigned char busnum = bus->number; u32 data = 0; u32 cfgaddr = CFGADDR(busnum,devfn,where); /* We are assuming that only one Device is attached to the bus */ if(PCI_SLOT(devfn) > 0) { return PCIBIOS_DEVICE_NOT_FOUND; } #if 0 if (devfn == PCI_DEVFN(0 ,0)) { *val = 0; return PCIBIOS_SUCCESSFUL; } #endif if ((size == 2) && (where & 1)) return PCIBIOS_BAD_REGISTER_NUMBER; else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; RDCFG32(0, cfgaddr, data); if (size == 1) *val = (data >> ((where & 3) << 3)) & 0xff; else if (size == 2) *val = (data >> ((where & 3) << 3)) & 0xffff; else *val = data; return PCIBIOS_SUCCESSFUL; } static int fusiv_pcibios_write_port0( struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { unsigned char busnum = bus->number; u32 cfgaddr = CFGADDR(busnum,devfn,where); u32 data = 0; if(PCI_SLOT(devfn) > 0) { return PCIBIOS_DEVICE_NOT_FOUND; } #if 0 if (devfn == PCI_DEVFN(0, 0)) { return PCIBIOS_SUCCESSFUL; } #endif if ((size == 2) && (where & 1)) return PCIBIOS_BAD_REGISTER_NUMBER; else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; RDCFG32(0,cfgaddr,data); if (size == 1) data = (data & ~(0xff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); else if (size == 2) data = (data & ~(0xffff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); else data = val; WRCFG32(0,cfgaddr,data); return PCIBIOS_SUCCESSFUL; } #ifdef CONFIG_FUSIV_PCIE_RC1 static int fusiv_pcibios_read_port1( struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * val) { unsigned char busnum = bus->number; u32 data = 0; u32 cfgaddr = CFGADDR(busnum,devfn,where); if(PCI_SLOT(devfn) > 0) { return PCIBIOS_DEVICE_NOT_FOUND; } if ((size == 2) && (where & 1)) return PCIBIOS_BAD_REGISTER_NUMBER; else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; RDCFG32(1,cfgaddr,data); if (size == 1) *val = (data >> ((where & 3) << 3)) & 0xff; else if (size == 2) *val = (data >> ((where & 3) << 3)) & 0xffff; else *val = data; return PCIBIOS_SUCCESSFUL; } static int fusiv_pcibios_write_port1( struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val) { unsigned char busnum = bus->number; u32 cfgaddr = CFGADDR(busnum,devfn,where); if(PCI_SLOT(devfn) > 0) { return PCIBIOS_DEVICE_NOT_FOUND; } u32 data = 0; if ((size == 2) && (where & 1)) return PCIBIOS_BAD_REGISTER_NUMBER; else if ((size == 4) && (where & 3)) return PCIBIOS_BAD_REGISTER_NUMBER; RDCFG32(1, cfgaddr, data); if (size == 1) data = (data & ~(0xff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); else if (size == 2) data = (data & ~(0xffff << ((where & 3) << 3))) | (val << ((where & 3) << 3)); else data = val; WRCFG32(1,cfgaddr,data); return PCIBIOS_SUCCESSFUL; } #endif int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { struct pci_controller *controller; controller = (struct pci_controller *) dev->sysdata; /* Index Field is initialized with RC Port Number during controller init */ switch(controller->index) { case 0: /* Unmask the required INTx pin */ /* pin = 1,2,3 and 4 for INTA,INTB, INTC and INTD respectively */ #ifdef CONFIG_FUSIV_VX185_REV_A1 WRREG32(FUSIV_PCIE_ADDR(0,PCIE_INTERRUPT_MASK),(RDREG32(FUSIV_PCIE_ADDR(0,PCIE_INTERRUPT_MASK)) & ~(1 << (18 - 2*pin)))); #else WRREG32(FUSIV_PCIE_ADDR(0,PCIE_INTERRUPT_MASK),(RDREG32(FUSIV_PCIE_ADDR(0,PCIE_INTERRUPT_MASK)) & ~(1 << (17 - 2*pin)))); #endif #if defined (CONFIG_CPU_MIPSR2_IRQ_VI) return FUSIV_MIPS_INT_PCIE0; #else return PCIe0_INT; #endif case 1: #ifdef CONFIG_FUSIV_VX185_REV_A1 WRREG32(FUSIV_PCIE_ADDR(1,PCIE_INTERRUPT_MASK),(RDREG32(FUSIV_PCIE_ADDR(1,PCIE_INTERRUPT_MASK)) & ~(1 << (18 - 2*pin)))); #else WRREG32(FUSIV_PCIE_ADDR(1,PCIE_INTERRUPT_MASK),(RDREG32(FUSIV_PCIE_ADDR(1,PCIE_INTERRUPT_MASK)) & ~(1 << (17 - 2*pin)))); #endif #if defined (CONFIG_CPU_MIPSR2_IRQ_VI) return FUSIV_MIPS_INT_PCIE1; #else return PCIe1_INT; #endif default: return -1; /* Illegal */ } } struct pci_ops fusiv_pci_ops_0 = { .read = fusiv_pcibios_read_port0, .write = fusiv_pcibios_write_port0, }; static struct resource fusiv_pci_mem_resource_0 = { .name = "FUSIV PCIE0 MEM", .start = PCIE_MEM_PA_LO_0, .end = PCIE_MEM_PA_HI_0, .flags = IORESOURCE_MEM, }; static struct resource fusiv_pci_io_resource_0 = { .name = "FUSIV PCIE0 IO", .start = 0x00000100, .end = 0x7FFF, .flags = IORESOURCE_DISABLED, }; static struct pci_controller fusiv_pci_controller_0 = { .mem_resource = &fusiv_pci_mem_resource_0, .io_resource = &fusiv_pci_io_resource_0, .pci_ops = &fusiv_pci_ops_0, .index = 0, }; #ifdef CONFIG_FUSIV_PCIE_RC1 static struct pci_ops fusiv_pci_ops_1 = { .read = fusiv_pcibios_read_port1, .write = fusiv_pcibios_write_port1, }; static struct resource fusiv_pci_mem_resource_1 = { .name = "FUSIV PCIE1 MEM", .start = PCIE_MEM_PA_LO_1, .end = PCIE_MEM_PA_HI_1, .flags = IORESOURCE_MEM, }; static struct resource fusiv_pci_io_resource_1 = { .name = "FUSIV PCIE1 IO", .start = 0x000008000, .end = 0xFFFF, .flags = IORESOURCE_DISABLED, }; static struct pci_controller fusiv_pci_controller_1 = { .mem_resource = &fusiv_pci_mem_resource_1, .io_resource = &fusiv_pci_io_resource_1, .pci_ops = &fusiv_pci_ops_1, .index = 1, }; #endif /* * Initialize both the RC Controllers */ int __init fusiv_pcibios_init(void) { volatile unsigned int link_status,read_count; unsigned int temp32; int reset_pin = 0xffffffff; enum _avm_hw_param pin_param = avm_hw_param_no_param; int result; /* ____Initialize PCIE0 Clock and Reset in SCU ____ */ scu_regs->rst_mask = ~(1 << PCIE_RESET); scu_regs->rst_vec = 0; scu_regs->clk_gate_ctl = (1 << PCIE_CLK_GATE); scu_regs->rst_vec = (1 << PCIE_RESET); scu_regs->rst_mask |= (1 << PCIE_RESET); result = avm_get_hw_config(AVM_HW_CONFIG_VERSION, "gpio_avm_pcie_reset", &reset_pin, &pin_param); if(result >= 0){ if(pin_param != avm_hw_param_gpio_out_active_low && pin_param != avm_hw_param_gpio_out_active_high) { printk("[%s]: Received bogus GPIO pin parameter from avm_hw_config: %x\n", __func__, pin_param); BUG(); } printk("[%s] Releasing PCIE reset line\n", __func__); ikan_gpio_out_bit(reset_pin, pin_param == avm_hw_param_gpio_out_active_low ? 1 : 0); } printk("Fusiv PCIe RC0 Starting ...\n"); #if CONFIG_FUSIV_PCIE_EXTERNAL_CLOCK /* Change the PCIE ref clock selection from Internal Clock to External Clock */ scu_regs->cpu_ctl_mask = ~(1 << PCIE_REF_CLK_SEL); scu_regs->cpu_ctl = 0; /* This will set the bit 22 in cpu_ctl register to zero which changes PCIE source to external (100 MHz) */ scu_regs->cpu_ctl_mask |= (1 << PCIE_REF_CLK_SEL); /* For selecting the external clock source mpll_prescale[1.0] needs to be chnaged to 2 (1.b10) in PHY_CONTROL_OFFSET register */ temp32 = RDREG32(FUSIV_PCIE_ADDR(0,PHY_CONTROL)); temp32 &= ~(PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(3)); temp32 |= PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(2); WRREG32(FUSIV_PCIE_ADDR(0,PHY_CONTROL),temp32); #endif /* ____MEM space enable and BUS MASTER enable ____ */ WRREG32(FUSIV_PCIE_ADDR(0,CMD_STAT),0x6); /* ___ Enable LTSSM _________ */ temp32 = RDREG32(FUSIV_PCIE_ADDR(0,APP_SYS_CONTROL)); temp32 |= APP_SYS_CONTROL_APP_LTSSM_ENABLE; WRREG32(FUSIV_PCIE_ADDR(0,APP_SYS_CONTROL),temp32); /* NOTE: First revision of Vx185 has an issue where both LTSSMs need to be enabled for link detection */ #if CONFIG_FUSIV_PCIE_EXTERNAL_CLOCK /* For selecting the external clock source mpll_prescale[1.0] needs to be chnaged to 2 (1.b10) in PHY_CONTROL_OFFSET register */ temp32 = RDREG32(FUSIV_PCIE_ADDR(1,PHY_CONTROL)); temp32 &= ~(PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(3)); temp32 |= PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(2); WRREG32(FUSIV_PCIE_ADDR(1,PHY_CONTROL),temp32); #endif /* Do the Same Operations for PCIe RC 2nd Port */ WRREG32(FUSIV_PCIE_ADDR(1,CMD_STAT),0x6); //WRREG32(FUSIV_PCIE_ADDR(1,BAR0),DRAM_ADDR_LO); temp32 = RDREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL)); temp32 |= APP_SYS_CONTROL_APP_LTSSM_ENABLE; WRREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL),temp32); /* ___ Wait for Link Up _________ */ read_count = 0; do { link_status = RDREG32(FUSIV_PCIE_ADDR(0,CORE_STATUS)); if(link_status & CORE_STATUS_RDLH_LINK_UP) { printk("PCIe RC0 Link Up !!!! \n"); break; } mdelay(1); read_count++; } while (read_count < MAX_READ_COUNT); if (read_count == MAX_READ_COUNT) printk("No PCIe device found on RC0\n"); else { mdelay(1); register_pci_controller(&fusiv_pci_controller_0); } #ifdef CONFIG_FUSIV_PCIE_RC1 printk("Fusiv PCIe RC1 Starting ...\n"); read_count = 0; do { link_status = RDREG32(FUSIV_PCIE_ADDR(1,CORE_STATUS)); if(link_status & (1 << CORE_STATUS_RDLH_LINK_UP_SHIFT)) { printk("PCIe RC1 Link Up !!!! \n"); break; } mdelay(1); read_count++; } while (read_count < MAX_READ_COUNT); if (read_count == MAX_READ_COUNT) printk("No PCIe device found on RC1\n"); else { mdelay(1); register_pci_controller(&fusiv_pci_controller_1); } #endif return PCIBIOS_SUCCESSFUL; } /* Do platform specific device initialization at pci_enable_device() time */ int pcibios_plat_dev_init(struct pci_dev *dev) { return 0; } arch_initcall(fusiv_pcibios_init);