/*
 * SII680A.c : U-boot driver for SiliconImage SII680A
 *                            Y.Nishimura, 2003/10/30
 * most part is taken from siimage.c of kernel source.
 *
 * linux/drivers/ide/pci/siimage.c	Version 1.06	June 11, 2003
 *
 * Copyright (C) 2001-2002	Andre Hedrick <andre@linux-ide.org>
 * Copyright (C) 2003		Red Hat <alan@redhat.com>
 * 
 * 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
 */
#include <config.h>
#include <common.h>

#if defined (CFG_SIIMAGE_680A)

#include <asm/io.h>
#include <pci.h>
#include <ata.h>

extern int ide_register_bus(ide_bus *bus);

static ide_bus	sii680[2];
static char	devicename[] = "SiliconImage SII680A";

void setup_sii680a(struct pci_controller *host, pci_dev_t dev, struct pci_config_table *table)
{	
	unsigned char reg8 = 0;
	unsigned int class_rev	= 0;
	unsigned char reset;
	
	/* reset internal state */
	pci_read_config_byte(dev, 0xa0, &reset);
	pci_write_config_byte(dev, 0xa0, reset|0x03);
	udelay(25);
	pci_write_config_byte(dev, 0xa0, reset);
	pci_read_config_byte(dev, 0xa0, &reset);
	udelay(250);
	
	pciauto_setup_device(host, dev, 5, host->pci_mem, host->pci_io);
	pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
	class_rev &= 0xff;
	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (class_rev) ? 1 : 255);	
	pci_write_config_byte(dev, 0x80, 0x00);
	pci_write_config_byte(dev, 0x84, 0x00);
	pci_read_config_byte(dev, 0x8A, &reg8);
	switch(reg8 & 0x30) {
		case 0x00:
			pci_write_config_byte(dev, 0x8A, reg8|0x10);
		case 0x30:
			pci_write_config_byte(dev, 0x8A, reg8 & ~0x20);
		case 0x10:
			break;
		case 0x20:
			break;
	}
	pci_read_config_byte(dev,   0x8A, &reg8);
	pci_write_config_byte(dev,  0xA1, 0x72);
	pci_write_config_word(dev,  0xA2, 0x328A);
	pci_write_config_dword(dev, 0xA4, 0x62DD62DD);
	pci_write_config_dword(dev, 0xA8, 0x43924392);
	pci_write_config_dword(dev, 0xAC, 0x40094009);
	pci_write_config_byte(dev,  0xB1, 0x72);
	pci_write_config_word(dev,  0xB2, 0x328A);
	pci_write_config_dword(dev, 0xB4, 0x62DD62DD);
	pci_write_config_dword(dev, 0xB8, 0x43924392);
	pci_write_config_dword(dev, 0xBC, 0x40094009);
	
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, (u32 *)&sii680[0].bus_offset);
	sii680[0].bus_offset &= (PCI_BASE_ADDRESS_IO_MASK);
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, (u32 *)&sii680[0].alt_offset);
	sii680[0].alt_offset &= PCI_BASE_ADDRESS_IO_MASK;
	sii680[0].alt_offset = sii680[0].alt_offset - sii680[0].bus_offset;
	sii680[0].name = &devicename[0];
	sii680[0].channel = 0;
	if (ide_register_bus(&sii680[0])) {
		printf("failed to register bus 0\n");
		return;
	}	
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_2, (u32 *)&sii680[1].bus_offset);
	sii680[1].bus_offset &= (PCI_BASE_ADDRESS_IO_MASK);
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_3, (u32 *)&sii680[1].alt_offset);
	sii680[1].alt_offset &= PCI_BASE_ADDRESS_IO_MASK;
	sii680[1].alt_offset = sii680[1].alt_offset - sii680[1].bus_offset;
	sii680[1].name = &devicename[0];
	sii680[1].channel = 1;
	if (ide_register_bus(&sii680[1])) {
		printf("failed to register bus 1\n");
		return;
	}	
}
#endif	// CFG_SII_680A
