diff options
Diffstat (limited to 'target/linux/atheros/files/arch/mips/atheros/ar5315/pci.c')
-rw-r--r-- | target/linux/atheros/files/arch/mips/atheros/ar5315/pci.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/target/linux/atheros/files/arch/mips/atheros/ar5315/pci.c b/target/linux/atheros/files/arch/mips/atheros/ar5315/pci.c new file mode 100644 index 000000000..0213b3471 --- /dev/null +++ b/target/linux/atheros/files/arch/mips/atheros/ar5315/pci.c @@ -0,0 +1,253 @@ +/* + * 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/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/irq.h> +#include <asm/paccess.h> +#include <asm/irq_cpu.h> +#include <asm/io.h> +#include "ar531x.h" + +#define AR531X_MEM_BASE 0x80800000UL +#define AR531X_MEM_SIZE 0x00ffffffUL +#define AR531X_IO_SIZE 0x00007fffUL + +#define IDSEL_SHIFT 13 + +static spinlock_t ar531x_pci_lock = SPIN_LOCK_UNLOCKED; +static u32 cfgaddr; + +static int config_access(int busno, int dev, int func, int where, int size, u32 ptr, int write) +{ + u32 address; /* Address to read from */ + u32 reg; + unsigned long flags; + int ret = -1; + + if ((busno != 0) || (dev > 3) || (func > 2)) + return ret; + + spin_lock_irqsave(&ar531x_pci_lock, flags); + + /* Select Configuration access */ + reg = sysRegRead(AR5315_PCI_MISC_CONFIG); + reg |= AR5315_PCIMISC_CFG_SEL; + sysRegWrite(AR5315_PCI_MISC_CONFIG, reg); + (void)sysRegRead(AR5315_PCI_MISC_CONFIG); + + address = (u32)cfgaddr + (1 << (IDSEL_SHIFT + dev)) + (func << 8) + where; + + if (size == 1) + address ^= 0x3; + else if (size == 2) + address ^= 0x2; + + if (write) { + if (size == 1) + ret = put_dbe(ptr, (u8 *) address); + else if (size == 2) + ret = put_dbe(ptr, (u16 *) address); + else if (size == 4) + ret = put_dbe(ptr, (u32 *) address); + } else { + if (size == 1) + ret = get_dbe(*((u32 *)ptr), (u8 *) address); + else if (size == 2) + ret = get_dbe(*((u32 *)ptr), (u16 *) address); + else if (size == 4) + ret = get_dbe(*((u32 *)ptr), (u32 *) address); + } + + /* Select Memory access */ + reg = sysRegRead(AR5315_PCI_MISC_CONFIG); + reg &= ~AR5315_PCIMISC_CFG_SEL; + sysRegWrite(AR5315_PCI_MISC_CONFIG, reg); + (void)sysRegRead(AR5315_PCI_MISC_CONFIG); + + spin_unlock_irqrestore(&ar531x_pci_lock, flags); + + if (ret) { + *((u32 *)ptr) = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int ar531x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 * value) +{ + return config_access(bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, (u32) value, 0); +} + +static int ar531x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value) +{ + return config_access(bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, value, 1); +} + +struct pci_ops ar531x_pci_ops = { + .read = ar531x_pci_read, + .write = ar531x_pci_write, +}; + +static struct resource ar531x_mem_resource = { + .name = "AR531x PCI MEM", + .start = AR531X_MEM_BASE, + .end = AR531X_MEM_BASE + AR531X_MEM_SIZE - AR531X_IO_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct resource ar531x_io_resource = { + .name = "AR531x PCI I/O", + .start = 0, + .end = AR531X_IO_SIZE, + .flags = IORESOURCE_IO, +}; + +struct pci_controller ar531x_pci_controller = { + .pci_ops = &ar531x_pci_ops, + .mem_resource = &ar531x_mem_resource, + .io_resource = &ar531x_io_resource, +}; + +int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +{ + return AR5315_IRQ_LCBUS_PCI; +} + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + u32 reg; + + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, 5); + pci_write_config_word(dev, 0x40, 0); + + /* Clear any pending Abort or external Interrupts + * and enable interrupt processing */ + reg = sysRegRead(AR5315_PCI_INTEN_REG); + reg &= ~AR5315_PCI_INT_ENABLE; + sysRegWrite(AR5315_PCI_INTEN_REG, reg); + + reg = sysRegRead(AR5315_PCI_INT_STATUS); + reg |= (AR5315_PCI_ABORT_INT | AR5315_PCI_EXT_INT); + sysRegWrite(AR5315_PCI_INT_STATUS, reg); + + reg = sysRegRead(AR5315_PCI_INT_MASK); + reg |= (AR5315_PCI_EXT_INT | AR5315_PCI_ABORT_INT); + sysRegWrite(AR5315_PCI_INT_MASK, reg); + + reg = sysRegRead(AR5315_PCI_INTEN_REG); + reg |= AR5315_PCI_INT_ENABLE; + sysRegWrite(AR5315_PCI_INTEN_REG, reg); + + return 0; +} + +static void ar5315_pci_fixup(struct pci_dev *dev) +{ + struct pci_bus *bus = dev->bus; + + if ((PCI_SLOT(dev->devfn) != 3) || (PCI_FUNC(dev->devfn) != 0) || (bus->number != 0)) + return; + +#define _DEV bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn) + printk("PCI: fixing up device %d,%d,%d\n", _DEV); + /* fix up mbars */ + config_access(_DEV, PCI_BASE_ADDRESS_0, 4, HOST_PCI_MBAR0, 1); + config_access(_DEV, PCI_BASE_ADDRESS_1, 4, HOST_PCI_MBAR1, 1); + config_access(_DEV, PCI_BASE_ADDRESS_2, 4, HOST_PCI_MBAR2, 1); + config_access(_DEV, PCI_COMMAND, 4, + PCI_COMMAND_MEMORY|PCI_COMMAND_MASTER|PCI_COMMAND_SPECIAL| + PCI_COMMAND_INVALIDATE|PCI_COMMAND_PARITY|PCI_COMMAND_SERR| + PCI_COMMAND_FAST_BACK, 1); +#undef _DEV +} +DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, ar5315_pci_fixup); + +int __init ar5315_pci_init(void) +{ + u32 reg; + + printk("AR531x PCI init... "); + + cfgaddr = (u32) ioremap_nocache(0x80000000, 1*1024*1024); /* Remap PCI config space */ + set_io_port_base((unsigned long) ioremap_nocache(AR531X_MEM_BASE + AR531X_IO_SIZE - 1, AR531X_IO_SIZE)); /* PCI I/O space */ + + reg = sysRegRead(AR5315_RESET); + sysRegWrite(AR5315_RESET, reg | AR5315_RESET_PCIDMA); + + udelay(10*1000); + + sysRegWrite(AR5315_RESET, reg & ~AR5315_RESET_PCIDMA); + sysRegRead(AR5315_RESET); /* read after */ + + udelay(10*1000); + + reg = sysRegRead(AR5315_ENDIAN_CTL); + reg |= AR5315_CONFIG_PCIAHB | AR5315_CONFIG_PCIAHB_BRIDGE; + + sysRegWrite(AR5315_ENDIAN_CTL, reg); + + reg = sysRegRead(AR5315_PCICLK); + reg = 4; + sysRegWrite(AR5315_PCICLK, reg); + + reg = sysRegRead(AR5315_AHB_ARB_CTL); + reg |= (ARB_PCI); + sysRegWrite(AR5315_AHB_ARB_CTL, reg); + + reg = sysRegRead(AR5315_IF_CTL); + reg &= ~(IF_PCI_CLK_MASK | IF_MASK); + reg |= (IF_PCI | IF_PCI_HOST | IF_PCI_INTR | (IF_PCI_CLK_OUTPUT_CLK << IF_PCI_CLK_SHIFT)); + + sysRegWrite(AR5315_IF_CTL, reg); + + /* Reset the PCI bus by setting bits 5-4 in PCI_MCFG */ + reg = sysRegRead(AR5315_PCI_MISC_CONFIG); + reg &= ~(AR5315_PCIMISC_RST_MODE); + reg |= AR5315_PCIRST_LOW; + sysRegWrite(AR5315_PCI_MISC_CONFIG, reg); + + /* wait for 100 ms */ + udelay(100*1000); + + /* Bring the PCI out of reset */ + reg = sysRegRead(AR5315_PCI_MISC_CONFIG); + reg &= ~(AR5315_PCIMISC_RST_MODE); + reg |= (AR5315_PCIRST_HIGH | AR5315_PCICACHE_DIS | 0x8); + sysRegWrite(AR5315_PCI_MISC_CONFIG, reg); + + sysRegWrite(AR5315_PCI_UNCACHE_CFG, + 0x1E | /* 1GB uncached */ + (1 << 5) | /* Enable uncached */ + (0x2 << 30) /* Base: 0x80000000 */ + ); + (void)sysRegRead(AR5315_PCI_UNCACHE_CFG); /* flush */ + + udelay(500*1000); + + register_pci_controller(&ar531x_pci_controller); + + printk("done\n"); + return 0; +} + +arch_initcall(ar5315_pci_init); |