|  | /* | 
|  | * 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 */ |