/*
 * (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
 * Andreas Heppel <aheppel@sysgo.de>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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
 */

/*
 * PCI routines for the MPC106 CPU.
 */

#include <ppcboot.h>
#include <command.h>
#include <cmd_boot.h>
#include <mpc106.h>
#include <asm/processor.h>
#include <pci.h>

#ifdef CONFIG_PCI

/*
 * Subroutine:  pci_init
 *
 * Description: Initialises the MPC106 PCI Configuration regs.
 *
 * Inputs:      bd  board info structure
 *
 * Return:		None
 *
 */

void pci_mpc106_init(bd_t *bd, struct pci_controller *hose)
{
	unsigned short pcicmd, pcistat;
	unsigned int picr1, picr2;
	unsigned int pvr = get_pvr();
	int reg_num = 0;

	puts("PCI:   ");

	hose->first_busno = 0;
	hose->last_busno = 0xff;

	/* System memory space */
	pci_set_region(hose->regions + reg_num++, 
		       MPC106_PCI_MEMORY_BUS,
		       MPC106_PCI_MEMORY_PHYS,
		       MPC106_PCI_MEMORY_SIZE,
		       PCI_REGION_MEM | PCI_REGION_MEMORY);

	/* PCI memory space */
	pci_set_region(hose->regions + reg_num++, 
		       MPC106_PCI_MEM_BUS,
		       MPC106_PCI_MEM_PHYS,
		       MPC106_PCI_MEM_SIZE,
		       PCI_REGION_MEM);

	/* PCI I/O space */
	pci_set_region(hose->regions + reg_num++, 
		       MPC106_PCI_IO_BUS,
		       MPC106_PCI_IO_PHYS,
		       MPC106_PCI_IO_SIZE,
		       PCI_REGION_IO);

	/* ISA/PCI I/O space */
	pci_set_region(hose->regions + reg_num++, 
		       MPC106_ISA_IO_BUS,
		       MPC106_ISA_IO_PHYS,
		       MPC106_ISA_IO_SIZE,
		       PCI_REGION_IO);

#ifndef CFG_ADDRESS_MAP_A
	/* ISA/PCI memory space */
	pci_set_region(hose->regions + reg_num++, 
		       MPC106_ISA_MEM_BUS,
		       MPC106_ISA_MEM_PHYS,
		       MPC106_ISA_MEM_SIZE,
		       PCI_REGION_MEM);
#endif

	hose->region_count = reg_num;

	pci_setup_indirect(hose,
			   MPC106_REG_ADDR,
			   MPC106_REG_DATA);

	pci_register_hose(hose);

	/*
	 *
	 */

	pci_hose_read_config_dword(hose, PCIDEVID_MPC106, PCI_PICR2, &picr2);
	picr2 |= PICR2_CF_SNOOP_WS(3) |
		 PICR2_CF_FLUSH_L2 |
		 PICR2_CF_L2_HIT_DELAY(3) |
		 PICR2_CF_APHASE_WS(3);
	picr2 &= ~(PICR2_L2_EN | PICR2_L2_UPDATE_EN);
	pci_hose_write_config_dword(hose, PCIDEVID_MPC106, PCI_PICR2, picr2);

	pci_hose_read_config_word(hose, PCIDEVID_MPC106, PCI_COMMAND, &pcicmd);
	pcicmd |= (CMD_SERR | PCI_CMD_MASTER | PCI_CMD_MEMEN);
	pci_hose_write_config_word(hose, PCIDEVID_MPC106, PCI_COMMAND, pcicmd);

	/* clear non-reserved bits in status register */
	pci_hose_read_config_word(hose, PCIDEVID_MPC106, PCI_STATUS, &pcistat);
	pcistat |= PCI_STAT_NO_RSV_BITS;
	pci_hose_write_config_word(hose, PCIDEVID_MPC106, PCI_STATUS, pcistat);

	pci_hose_read_config_dword(hose, PCIDEVID_MPC106, PCI_PICR1, &picr1);
	if (PVR_VER(pvr) == 0x0008)
		picr1 |= PICR1_PROC_TYPE_603;
	else
		picr1 |= PICR1_PROC_TYPE_604;

	picr1 |= PICR1_CF_CBA(63) | PICR1_CF_BREAD_WS(2);
	picr1 |= PICR1_MCP_EN | PICR1_CF_DPARK |
		 PICR1_CF_LOOP_SNOOP | PICR1_CF_APARK;
	pci_hose_write_config_dword(hose, PCIDEVID_MPC106, PCI_PICR1, picr1);

#ifdef CONFIG_PCI_PNP
	puts("scanning bus for devices ...");
#endif  /* CONFIG_PCI_PNP */

	/*
	 * Scan the PCI bus and configure devices found.
	 */
	hose->last_busno = pci_hose_scan(hose);

	puts("OK\n");
}

/*
 *
 */

static struct pci_controller hose;

void pci_init(bd_t *bd)
{
	pci_mpc106_init(bd, &hose);
}

#endif /* CONFIG_PCI */
