diff options
Diffstat (limited to 'cpu/mpc86xx/pcie_indirect.c')
-rw-r--r-- | cpu/mpc86xx/pcie_indirect.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/cpu/mpc86xx/pcie_indirect.c b/cpu/mpc86xx/pcie_indirect.c new file mode 100644 index 0000000..b00ad76 --- /dev/null +++ b/cpu/mpc86xx/pcie_indirect.c @@ -0,0 +1,199 @@ +/* + * Support for indirect PCI bridges. + * + * Copyright (c) Freescale Semiconductor, Inc. + * 2006. All rights reserved. + * + * Jason Jin <Jason.jin@freescale.com> + * + * 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. + * + * partly derived from + * arch/powerpc/platforms/86xx/mpc86xx_pcie.c + */ + +#include <common.h> + +#ifdef CONFIG_PCI + +#include <asm/processor.h> +#include <asm/io.h> +#include <pci.h> + +#define PCI_CFG_OUT out_be32 +#define PEX_FIX out_be32(hose->cfg_addr+0x4, 0x0400ffff) + +static int +indirect_read_config_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + int len, + u32 *val) +{ + int bus = PCI_BUS(dev); + + volatile unsigned char *cfg_data; + u32 temp; + + PEX_FIX; + if (bus == 0xff) { + PCI_CFG_OUT(hose->cfg_addr, + dev | (offset & 0xfc) | 0x80000001); + } else { + PCI_CFG_OUT(hose->cfg_addr, + dev | (offset & 0xfc) | 0x80000000); + } + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ + cfg_data = hose->cfg_data; + PEX_FIX; + temp = in_le32((u32 *) cfg_data); + switch (len) { + case 1: + *val = (temp >> (((offset & 3)) * 8)) & 0xff; + break; + case 2: + *val = (temp >> (((offset & 3)) * 8)) & 0xffff; + break; + default: + *val = temp; + break; + } + + return 0; +} + +static int +indirect_write_config_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + int len, + u32 val) +{ + int bus = PCI_BUS(dev); + volatile unsigned char *cfg_data; + u32 temp; + + PEX_FIX; + if (bus == 0xff) { + PCI_CFG_OUT(hose->cfg_addr, + dev | (offset & 0xfc) | 0x80000001); + } else { + PCI_CFG_OUT(hose->cfg_addr, + dev | (offset & 0xfc) | 0x80000000); + } + + /* + * Note: the caller has already checked that offset is + * suitably aligned and that len is 1, 2 or 4. + */ + /* ERRATA PCI-Ex 12 - Configuration Address/Data Alignment */ + cfg_data = hose->cfg_data; + switch (len) { + case 1: + PEX_FIX; + temp = in_le32((u32 *) cfg_data); + temp = (temp & ~(0xff << ((offset & 3) * 8))) | + (val << ((offset & 3) * 8)); + PEX_FIX; + out_le32((u32 *) cfg_data, temp); + break; + case 2: + PEX_FIX; + temp = in_le32((u32 *) cfg_data); + temp = (temp & ~(0xffff << ((offset & 3) * 8))); + temp |= (val << ((offset & 3) * 8)); + PEX_FIX; + out_le32((u32 *) cfg_data, temp); + break; + default: + PEX_FIX; + out_le32((u32 *) cfg_data, val); + break; + } + PEX_FIX; + return 0; +} + +static int +indirect_read_config_byte_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + u8 *val) +{ + u32 val32; + indirect_read_config_pcie(hose, dev, offset, 1, &val32); + *val = (u8) val32; + return 0; +} + +static int +indirect_read_config_word_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + u16 *val) +{ + u32 val32; + indirect_read_config_pcie(hose, dev, offset, 2, &val32); + *val = (u16) val32; + return 0; +} + +static int +indirect_read_config_dword_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + u32 *val) +{ + return indirect_read_config_pcie(hose, dev, offset, 4, val); +} + +static int +indirect_write_config_byte_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + u8 val) +{ + return indirect_write_config_pcie(hose, dev, offset, 1, (u32) val); +} + +static int +indirect_write_config_word_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + unsigned short val) +{ + return indirect_write_config_pcie(hose, dev, offset, 2, (u32) val); +} + +static int +indirect_write_config_dword_pcie(struct pci_controller *hose, + pci_dev_t dev, + int offset, + u32 val) +{ + return indirect_write_config_pcie(hose, dev, offset, 4, val); +} + +void +pcie_setup_indirect(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) +{ + pci_set_ops(hose, + indirect_read_config_byte_pcie, + indirect_read_config_word_pcie, + indirect_read_config_dword_pcie, + indirect_write_config_byte_pcie, + indirect_write_config_word_pcie, + indirect_write_config_dword_pcie); + + hose->cfg_addr = (unsigned int *)cfg_addr; + hose->cfg_data = (unsigned char *)cfg_data; +} + +#endif /* CONFIG_PCI */ |