/** * Copyright (C) 2010-2014 Ikanos Communications. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation. * * Info : * 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 #if defined CONFIG_FUSIV_VX185 #define MAX_READ_COUNT 500 #endif /* CONFIG_FUSIV_VX185 */ #if defined CONFIG_FUSIV_VX585 #define MAX_READ_COUNT 5000 #endif /* CONFIG_FUSIV_VX585 */ #define GPIO_MODE_REG2 0xb9030010 #define GPIO_OUTPUT_SET 0xb9030004 #define GPIO_OUTPUT_CLR 0xb9030008 #if defined CONFIG_FUSIV_VX585 #define PCIE_DEBUG 0 #endif /* CONFIG_FUSIV_VX585 */ /* Enable this debug Macro for dumping RC0 & RC1 * APP_SYS_CONTROL and PHY_CONTROL once when * Link is not established */ #define PCIE_DEBUG 0 static void fusiv_pcie_block_reset(void); static void fusiv_peripheral_rst(void); #if PCIE_DEBUG static void fusiv_pcie_regdump(void); #endif /* * 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 */ static void fusiv_pcie_block_reset() { #if defined CONFIG_FUSIV_VX185 scu_regs->rst_mask = ~(1 << PCIE_RESET); scu_regs->rst_vec = 0; scu_regs->clk_gate_ctl = (1 << PCIE_CLK_GATE); /* reset pulse for PCIE controller */ mdelay(100); scu_regs->rst_vec |= (1 << PCIE_RESET); scu_regs->rst_mask |= (1 << PCIE_RESET); /*Delay after the PCIe Block Reset to stabilize */ mdelay(100); #endif /* CONFIG_FUSIV_VX185 */ #if defined CONFIG_FUSIV_VX585 volatile unsigned int tempval=0; /* PCIe RC0 Device Type Configuration */ tempval = (*((unsigned int *)(0xb90003ac))); //dkar:DEV TYPE configuration printk("(0xb90003ac)-Original = 0x%x\n", tempval); tempval &= 0xFFFFFFF4; //dd tempval |= 0x0; (*((unsigned int *)(0xb90003ac))) = tempval; printk("(0xb90003ac) - After = 0x%x\n", tempval); #ifdef CONFIG_FUSIV_PCIE_RC1 // need to be enabled when RC1 is tested /* PCIe RC1 Device Type Configuration */ tempval = (*((unsigned int *)(0xb90003ac))); //dkar:DEV TYPE configuration printk("(0xb90003ac)-Original = 0x%x\n", tempval); tempval &= 0xFFF4FFFF; tempval |= 0x10000; (*((unsigned int *)(0xb90003ac))) = tempval; #endif /* CONFIG_FUSIV_PCIE_RC1*/ #endif /* CONFIG_FUSIV_VX585 */ } #if PCIE_DEBUG static void fusiv_pcie_regdump() { unsigned int sysctrl0,sysctrl1,phyctrl0,phyctrl1; sysctrl0 = RDREG32(FUSIV_PCIE_ADDR(0,APP_SYS_CONTROL)); sysctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL)); phyctrl0 = RDREG32(FUSIV_PCIE_ADDR(0,PHY_CONTROL)); phyctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,PHY_CONTROL)); if (((sysctrl0 & 0x12000)!= 0x12000) || ((sysctrl1 & 0x12000)!= 0x12000)) { printk("Register Status Before External Clock Mode \n"); sysctrl0 = RDREG32(FUSIV_PCIE_ADDR(0,APP_SYS_CONTROL)); printk("PCIe RC0 APP_SYS_CONTROL state : %x\n",sysctrl0); sysctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL)); printk("PCIe RC1 APP_SYS_CONTROL state : %x\n",sysctrl1); phyctrl0 = RDREG32(FUSIV_PCIE_ADDR(0,PHY_CONTROL)); printk("PCIe RC0 PHY_CONTROL state : %x\n",phyctrl0); phyctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,PHY_CONTROL)); printk("PCIe RC1 PHY_CONTROL state : %x\n",phyctrl1); } } #endif static void fusiv_peripheral_rst() { *(volatile unsigned int *)GPIO_MODE_REG2 &= 0xFFF3FFFF; *(volatile unsigned int *)GPIO_MODE_REG2 |= 0x00040000; #if defined CONFIG_FUSIV_VX185 *(volatile unsigned int *)GPIO_OUTPUT_CLR = 0x02000000; #endif /* CONFIG_FUSIV_VX185 */ #if defined CONFIG_FUSIV_VX585 *(volatile unsigned int *)GPIO_OUTPUT_CLR = 0x02000000; #endif /* CONFIG_FUSIV_VX585 */ mdelay(200); #if defined CONFIG_FUSIV_VX185 *(volatile unsigned int *)GPIO_OUTPUT_SET = 0x02000000; #endif /* CONFIG_FUSIV_VX185 */ #if defined CONFIG_FUSIV_VX585 *(volatile unsigned int *)GPIO_OUTPUT_SET = 0x02000000; #endif /* CONFIG_FUSIV_VX585 */ } int __init fusiv_pcibios_init(void) { volatile unsigned int link_status,read_count; unsigned int sysctrl0,sysctrl1,phyctrl0,phyctrl1; #if defined CONFIG_FUSIV_VX585 unsigned int i = 0; #endif /* CONFIG_FUSIV_VX585 */ /* ____Initialize PCIE0 Clock and Reset in SCU ____ */ printk("PCIE Block Reset 1\n"); fusiv_pcie_block_reset(); #if defined CONFIG_FUSIV_VX185 #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); printk("PCIE Block Reset 2\n"); /* After changing the PCIe Ref Clock Selection to External Clock. Assert and De-assert Reset to teh PCIe Root Complex Block */ fusiv_pcie_block_reset(); printk("Changing the MPLL Prescale Value for External Clock Mode\n"); /* PCIe RC0 : For selecting the external clock source mpll_prescale[1.0] needs to be chnaged to 2 (1.b10) in PHY_CONTROL_OFFSET register */ phyctrl0 = RDREG32(FUSIV_PCIE_ADDR(0,PHY_CONTROL)); phyctrl0 &= ~(PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(3)); phyctrl0 |= PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(2); WRREG32(FUSIV_PCIE_ADDR(0,PHY_CONTROL),phyctrl0); /* PCIe RC1 : For selecting the external clock source mpll_prescale[1.0] needs to be chnaged to 2 (1.b10) in PHY_CONTROL_OFFSET register */ phyctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,PHY_CONTROL)); phyctrl1 &= ~(PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(3)); phyctrl1 |= PHY_CONTROL_SNPS_PHY_MPLL_PRESCALE(2); WRREG32(FUSIV_PCIE_ADDR(1,PHY_CONTROL),phyctrl1); #endif /*Delay after the enabling External Clock Mode to the PCIe PHY*/ mdelay(10); #endif /* CONFIG_FUSIV_VX185 */ #if PCIE_DEBUG fusiv_pcie_regdump(); #endif /* ____MEM space enable and BUS MASTER enable - PCIe RC0 ____ */ WRREG32(FUSIV_PCIE_ADDR(0,CMD_STAT),0x6); #ifdef CONFIG_FUSIV_PCIE_RC1 /* ____MEM space enable and BUS MASTER enable - PCIe RC1 ____ */ WRREG32(FUSIV_PCIE_ADDR(1,CMD_STAT),0x6); #endif #if defined CONFIG_FUSIV_VX185 printk("GPIO19/22 Peripheral Reset\n"); /* As per the VLSI comments : Peripheral reset is required after the PCIe Root Complex Init */ fusiv_peripheral_rst(); /* NOTE: Vx185 Chip has an issue where both LTSSMs need to be enabled for link detection */ /* Enable LTSSM for PCIe RC1*/ sysctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL)); sysctrl1 |= APP_SYS_CONTROL_APP_LTSSM_ENABLE; WRREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL),sysctrl1); /* Enable LTSSM for PCIe RC0 */ sysctrl0 = RDREG32(FUSIV_PCIE_ADDR(0,APP_SYS_CONTROL)); sysctrl0 |= APP_SYS_CONTROL_APP_LTSSM_ENABLE; WRREG32(FUSIV_PCIE_ADDR(0,APP_SYS_CONTROL),sysctrl0); #endif /* CONFIG_FUSIV_VX185 */ #if defined CONFIG_FUSIV_VX585 /* Enable LTSSM for PCIe RC0 */ sysctrl0 = (*((unsigned int *)(0xb913280c))); //dkar:Link Width and Speed Change Control Register sysctrl0 |= 0x00020000; (*((unsigned int *)(0xb913280c))) = sysctrl0; printk(" Link Width and Speed change Control Reg (0xb913280c) = 0x%x\n", sysctrl0); sysctrl0 = (*((unsigned int *)(0xb9130034))); //dkar:app_entry:core cfg sysctrl0 |= 0x00000010; (*((unsigned int *)(0xb9130034))) = sysctrl0; printk(" App Entry Core Config (0xb9130034) = 0x%x\n", sysctrl0); #ifdef CONFIG_FUSIV_PCIE_RC1 /* Enable LTSSM for PCIe RC1*/ sysctrl1 = (*((unsigned int *)(0xb914280c))); //dkar:Link Width and Speed Change Control Register sysctrl1 |= 0x00020000; (*((unsigned int *)(0xb914280c))) = sysctrl1; printk("(0xb913280c) = 0x%x\n", sysctrl1); sysctrl1 = RDREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL)); sysctrl1 |= APP_SYS_CONTROL_APP_LTSSM_ENABLE; WRREG32(FUSIV_PCIE_ADDR(1,APP_SYS_CONTROL),sysctrl1); sysctrl1 = (*((unsigned int *)(0xb9140034))); //dkar:app_entry:core cfg sysctrl1 |= 0x00000010; (*((unsigned int *)(0xb9140034))) = sysctrl1; printk("(0xb9130034) = 0x%x\n", sysctrl1); #endif #endif /* CONFIG_FUSIV_VX585 */ #if PCIE_DEBUG fusiv_pcie_regdump(); #endif printk("Fusiv PCIe RC0 Starting ...\n"); /* ___ Wait for Link Up _________ */ mdelay(100); read_count = 0; do { #if defined CONFIG_FUSIV_VX185 link_status = RDREG32(FUSIV_PCIE_ADDR(0,CORE_STATUS)); if(link_status & CORE_STATUS_RDLH_LINK_UP) { printk("PCIe RC0 Link Up !!!! \n"); break; } #endif /* CONFIG_FUSIV_VX185 */ mdelay(1); /*loop delay */ read_count++; #if defined CONFIG_FUSIV_VX585 link_status = (*((unsigned int *)(0xb9130004))); // (Bit[6] == 1'b1) // Wait for link up i++; if(( i % 1000) == 0) { printk("\r\n Polling .... value is 0x%x", link_status); } if(link_status & 0x40) { printk("\r\n2. Bit detected .. value is 0x%x, PCIe0 Link Up\n",link_status); break; } #endif /* CONFIG_FUSIV_VX585 */ } while (read_count < MAX_READ_COUNT); if (read_count == MAX_READ_COUNT) printk("No PCIe device found on RC0\n"); else { mdelay(1); /*loop delay */ 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);/*loop delay */ 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);/*loop delay */ 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);