#include #include #include #include #include #include #include #include #include #include #include #include /* * Support for Atheros pci interrupt and core pci initialization */ /* * PCI interrupts. * roughly the interrupts flow is: * * - save flags * - CLI (disable all) * - IC->ack (mask out the source) * - EI (enable all, except the source that was masked of course) * - action (ISR) * - IC->enable (unmask the source) * * The reason we have a separate PCI IC is beacause of the following: * If we dont, then Throughout the "action" of a PCI slot, the * entire PCI "IP" on the cpu will remain disabled. Which means that we cant * prioritize between PCI interrupts. Normally this should be ok, if all PCI * interrupts are considered equal. However, creating a PCI IC gives * the flexibility to prioritize. */ static void ath_pci_irq_enable(unsigned int irq) { ATH_DECL_PCI_IM_ARR(r); ath_reg_rmw_set(r[irq - ATH_PCI_IRQ_BASE], PCIE_INT_MASK_INTAL_MASK); } static void ath_pci_irq_disable(unsigned int irq) { ATH_DECL_PCI_IM_ARR(r1); ATH_DECL_PCI_IS_ARR(r2); ath_reg_rmw_clear(r1[irq - ATH_PCI_IRQ_BASE], PCIE_INT_MASK_INTAL_MASK); ath_reg_rmw_clear(r2[irq - ATH_PCI_IRQ_BASE], PCIE_INT_STATUS_INTAL_MASK); } static unsigned int ath_pci_irq_startup(unsigned int irq) { ath_pci_irq_enable(irq); return 0; } static void ath_pci_irq_shutdown(unsigned int irq) { ath_pci_irq_disable(irq); } static void ath_pci_irq_ack(unsigned int irq) { ath_pci_irq_disable(irq); } static void ath_pci_irq_end(unsigned int irq) { if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) ath_pci_irq_enable(irq); } static int ath_pci_irq_set_affinity(unsigned int irq, const struct cpumask *mask) { /* * Only 1 CPU; ignore affinity request */ return 0; } struct irq_chip /* hw_interrupt_type */ ath_pci_irq_controller = { .name = "ATH PCI", .startup = ath_pci_irq_startup, .shutdown = ath_pci_irq_shutdown, .enable = ath_pci_irq_enable, .disable = ath_pci_irq_disable, .ack = ath_pci_irq_ack, .end = ath_pci_irq_end, .eoi = ath_pci_irq_end, .set_affinity = ath_pci_irq_set_affinity, }; void ath_pci_irq_init(int irq_base) { int i; for (i = irq_base; i < irq_base + ATH_PCI_IRQ_COUNT; i++) { irq_desc[i].status = IRQ_DISABLED; irq_desc[i].action = NULL; irq_desc[i].depth = 1; //irq_desc[i].chip = &ath_pci_irq_controller; set_irq_chip_and_handler(i, &ath_pci_irq_controller, handle_percpu_irq); } } #ifndef CONFIG_ATH_EMULATION /* * init the pci controller */ #define ATH_PCI_RES(x, n, s, e, f) \ { .name = n, .start = s, .end = e, .flags = f } #define ATH_PCI_RES_IO(x, s, e) \ ATH_PCI_RES(x, "PCI I/O - " # x, s, e, IORESOURCE_IO) #define ATH_PCI_RES_MEM(x) \ ATH_PCI_RES(x, "PCI MEM - " # x, \ ATH_PCI_MEM_BASE(x), \ ATH_PCI_MEM_BASE(x) + ATH_PCI_WINDOW - 1, \ IORESOURCE_MEM) /* * We don't use I/O transaction ability. */ static struct resource ath_io_resource[] = { ATH_DECL_PCI_IO_RES }; static struct resource ath_mem_resource[] = { ATH_DECL_PCI_MEM_RES }; extern struct pci_ops ath_pci_ops; #define ATH_PCI_CTRL_DESCRIPTOR(x) \ { \ .pci_ops = &ath_pci_ops, \ .mem_resource = &ath_mem_resource[x], \ .io_resource = &ath_io_resource[x], \ } static struct pci_controller ath_pci_controller[] = { ATH_DECL_PCI_CTRLR }; #define ATH_NUM_PCI_CONTROLLER \ (sizeof(ath_pci_controller) / sizeof(ath_pci_controller[0])) int ath_pci_link[ATH_NUM_PCI_CONTROLLER]; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __init ath_pci_init_board (void) { unsigned int cmd, i; int gpio_pcie_reset = 0; if(avm_get_hw_config(AVM_HW_CONFIG_VERSION, "gpio_avm_pcie_reset", &gpio_pcie_reset, NULL)) { printk("[%s] \n", __func__); } // common for rc1 and rc2 ath_reg_wr_nf(PCIE_PLL_DITHER_DIV_MAX_ADDRESS, PCIE_PLL_DITHER_DIV_MAX_EN_DITHER_SET(0x1) | PCIE_PLL_DITHER_DIV_MAX_USE_MAX_SET(0x1) | PCIE_PLL_DITHER_DIV_MAX_DIV_MAX_INT_SET(0x14) | PCIE_PLL_DITHER_DIV_MAX_DIV_MAX_FRAC_SET(0x3ff)); ath_reg_wr_nf(PCIE_PLL_DITHER_DIV_MIN_ADDRESS, PCIE_PLL_DITHER_DIV_MIN_DIV_MIN_INT_SET(0x14)); ath_reg_wr_nf(PCIE_PLL_CONFIG_ADDRESS, PCIE_PLL_CONFIG_REFDIV_SET(1) | PCIE_PLL_CONFIG_BYPASS_SET(1) | PCIE_PLL_CONFIG_PLLPWD_SET(1)); mdelay(10); ath_reg_rmw_clear(PCIE_PLL_CONFIG_ADDRESS, PCIE_PLL_CONFIG_PLLPWD_SET(1)); mdelay(1); ath_reg_rmw_clear(PCIE_PLL_CONFIG_ADDRESS, PCIE_PLL_CONFIG_BYPASS_SET(1)); mdelay(1); #if defined(CONFIG_ATH_HAS_PCI_RC2) if (!(ath_reg_rd(RST_BOOTSTRAP_ADDRESS) & RST_BOOTSTRAP_PCIE_RC_EP_SELECT_MASK)) { pci_rc2_init_board(); return; } #endif ath_reg_rmw_set(RST_RESET_ADDRESS, RST_RESET_PCIE_PHY_RESET_SET(1)); mdelay(10); ath_reg_rmw_set(RST_RESET_ADDRESS, RST_RESET_PCIE_RESET_SET(1)); mdelay(10); ath_reg_rmw_clear(RST_MISC2_ADDRESS, RST_MISC2_PERSTN_RCPHY_SET(1)); mdelay(10); ath_reg_wr_nf(PCIE_RESET_ADDRESS, 0); // Put endpoint in reset if (gpio_pcie_reset) ath_avm_gpio_out_bit(gpio_pcie_reset, 0); mdelay(100); ath_reg_rmw_set(RST_MISC2_ADDRESS, RST_MISC2_PERSTN_RCPHY_SET(1)); mdelay(10); ath_reg_rmw_clear(RST_RESET_ADDRESS, RST_RESET_PCIE_PHY_RESET_SET(1)); mdelay(10); ath_reg_rmw_clear(RST_RESET_ADDRESS, RST_RESET_PCIE_RESET_SET(1)); mdelay(10); ath_reg_wr_nf(PCIE_APP_ADDRESS, PCIE_APP_PCIE_BAR_MSN_SET(1) | PCIE_APP_CFG_BE_SET(0xf) | PCIE_APP_SLV_RESP_ERR_MAP_SET(0x3f) | PCIE_APP_LTSSM_ENABLE_SET(1)); cmd = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK; for (i = 0; i < ATH_NUM_PCI_CONTROLLER; i++) { ath_local_write_config(i, PCI_COMMAND, 4, cmd); ath_local_write_config(i, 0x20, 4, 0x1ff01000); ath_local_write_config(i, 0x24, 4, 0x1ff01000); } ath_reg_wr_nf(PCIE_RESET_ADDRESS, 4); // Pull endpoint out of reset if (gpio_pcie_reset) ath_avm_gpio_out_bit(gpio_pcie_reset, 1); mdelay(100); /* * Check if the WLAN PCI-E H/W is present, If the * WLAN H/W is not present, skip the PCI platform * initialization code and return */ if (((ath_reg_rd(PCIE_RESET_ADDRESS)) & 0x1) == 0x0) { printk(KERN_ERR "\n"); } } /* * We want a 1:1 mapping between PCI and DDR for inbound and outbound. * The PCI<---AHB decoding works as follows: * * 8 registers in the DDR unit provide software configurable 32 bit offsets * for each of the eight 16MB PCI windows in the 128MB. The offsets will be * added to any address in the 16MB segment before being sent to the PCI unit. * * Essentially for any AHB address generated by the CPU, * 1. the MSB four bits are stripped off, [31:28], * 2. Bit 27 is used to decide between the lower 128Mb (PCI) or the rest of * the AHB space * 3. Bits 26:24 are used to access one of the 8 window registers and are * masked off. * 4. If it is a PCI address, then the WINDOW offset in the WINDOW register * corresponding to the next 3 bits (bit 26:24) is ADDED to the address, * to generate the address to PCI unit. * * eg. CPU address = 0x100000ff * window 0 offset = 0x10000000 * This points to lowermost 16MB window in PCI space. * So the resulting address would be 0x000000ff+0x10000000 * = 0x100000ff * * eg2. CPU address = 0x120000ff * WINDOW 2 offset = 0x12000000 * resulting address would be 0x000000ff+0x12000000 * = 0x120000ff * * There is no translation for inbound access (PCI device as a master) */ static int __init ath_pcibios_init(void) { uint32_t i; ATH_DECL_PCI_RST_ARR(pcie_reset); #if defined(ATH_LOW_POWER_ENABLE) && ATH_LOW_POWER_ENABLE ATH_DECL_PCI_ASPM_SUPP_ARR(aspm_sup); ATH_DECL_PCI_ASPM_EN_ARR(aspm_en); #endif #ifdef CONFIG_MACH_AR934x if (is_ar9341()) { return 0; } #endif printk("[%s] ", __FUNCTION__); ath_pci_init_board(); printk("init done\n"); for (i = 0; i < ATH_NUM_PCI_CONTROLLER; i++) { printk("%s: bus %d\n", __func__, i); /* * Check if the WLAN PCI-E H/W is present, If the * WLAN H/W is not present, skip the PCI * initialization code and just return. */ if (!(ath_reg_rd(pcie_reset[i]) & PCIE_RESET_LINK_UP_MASK)) { printk("***** Warning PCIe %d H/W not found !!!\n", i); return 0; } else { ath_pci_link[i] = 1; #ifndef CONFIG_PCI_INIT_IN_MONITOR printk("PCI init: "); #define ATH_PCI_CMD_INIT (PCI_COMMAND_MEMORY | \ PCI_COMMAND_MASTER | \ PCI_COMMAND_INVALIDATE | \ PCI_COMMAND_PARITY | \ PCI_COMMAND_SERR | \ PCI_COMMAND_FAST_BACK) printk("PCI %d CMD write: 0x%x\n", i, ATH_PCI_CMD_INIT); ath_local_write_config(i, PCI_COMMAND, 4, ATH_PCI_CMD_INIT); /*--- # if !defined(CONFIG_PERICOM) ---*/ /*--- ath_pci_ops.write(NULL, 0, PCI_COMMAND, 4, ATH_PCI_CMD_INIT); ---*/ /*--- # endif ---*/ /* CONFIG_PERICOM */ #endif #if defined(ATH_LOW_POWER_ENABLE) && ATH_LOW_POWER_ENABLE // Enable L0 & L1 ASPM Support ath_reg_rmw_set(aspm_sup[i], ATH_PCIE_RC_SUPP_VAL); // Enable L0 & L1 ath_reg_rmw_set(aspm_en[i], ATH_PCIE_RC_EN_VAL); #endif } register_pci_controller(&ath_pci_controller[i]); } return 0; } arch_initcall(ath_pcibios_init); #endif