 
/*
 * pdc2026x.c : U-boot driver for Promise PDC20268/69
 *                            Y.Nishimura, 2004/4/30
 * most part is taken from pdc202xx_new.c of kernel source.
 *
 *  linux/drivers/ide/pdc202xx.c	Version 0.35	Mar. 30, 2002
 *  Copyright (C) 1998-2002		Andre Hedrick <andre@linux-ide.org>
 *  Portions Copyright (C) 1999 Promise Technology, Inc.
 *  Author: Frank Tiernan (frankt@promise.com)
 *  Released under terms of General Public License
 *
 * 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 <common.h>
#include <config.h>

#if defined (CFG_PROMISE_PDC2026X)

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

#define PDC2026X_DEBUG

#define XFER_PIO_4	0x0C	/* 0000|1100 */
#define XFER_PIO_3	0x0B	/* 0000|1011 */
#define XFER_PIO_2	0x0A	/* 0000|1010 */
#define XFER_PIO_1	0x09	/* 0000|1001 */
#define XFER_PIO_0	0x08	/* 0000|1000 */
#define XFER_PIO_SLOW	0x00	/* 0000|0000 */

#define REG_A		0x01
#define REG_B		0x02
#define REG_C		0x04
#define REG_D		0x08
/* Promise ATA66 PCI Device (ATA controller) */
#define PROMISE_MBAR0  0x0000a000
#define PROMISE_MBAR1  (PROMISE_MBAR0 + 0x1000)
#define PROMISE_MBAR2  (PROMISE_MBAR0 + 0x2000)
#define PROMISE_MBAR3  (PROMISE_MBAR0 + 0x3000)
#define PROMISE_MBAR4  (PROMISE_MBAR0 + 0x4000)
#define PROMISE_MBAR5  (PROMISE_MBAR0 + 0x5000)

extern int ide_register_bus(ide_bus *bus);

static ide_bus	pdc2026x[2];
static char	devicename[] = "Promise PDC20268/269";

#define set_2regs(a, b)					\
	do {						\
		outb((a + adj), indexreg);	\
		outb(b, datareg);			\
	} while(0)

#define set_pio(a, b, c)				\
	do { 						\
		set_2regs(0x0c,(a));			\
		set_2regs(0x0d,(b));			\
		set_2regs(0x13,(c));			\
	} while(0)

static void pdc2026x_new_tune_chipset (pci_dev_t dev, int drive, unsigned char speed)
{
	u32 high_16, indexreg, datareg;
	u8 adj;

	pci_read_config_dword(dev, PCI_BASE_ADDRESS_4, &high_16);
	high_16 &= 0xfffffffe;
	indexreg = high_16 + (drive/2 ? 0x09 : 0x01);
	datareg	 = (indexreg + 2);
	adj      = (drive % 2) ? 0x08 : 0x00;
	

	switch (speed) {
		case XFER_PIO_4:	set_pio(0x23, 0x09, 0x25); break;
		case XFER_PIO_3:	set_pio(0x27, 0x0d, 0x35); break;
		case XFER_PIO_2:	set_pio(0x23, 0x26, 0x64); break;
		case XFER_PIO_1:	set_pio(0x46, 0x29, 0xa4); break;
		case XFER_PIO_0:	set_pio(0xfb, 0x2b, 0xac); break;
		default:		break;
	};
}

void pci_setup_pdc2026x(struct pci_controller *hose, pci_dev_t dev, struct pci_config_table *ctable)
{
	int a;
	unsigned short	*pllword;

	pciauto_setup_device(hose, dev, 6, hose->pci_mem, hose->pci_io);
	pci_write_config_dword(dev, PCI_ROM_ADDRESS, 0);
	
#ifndef	CFG_PCI_IS_66MHZ
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_5, (unsigned int *)(&pllword) );
	pllword += (0x1202/2);
	*pllword &= 0xffbf;
#endif
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, (u32 *)&pdc2026x[0].bus_offset);
	pdc2026x[0].bus_offset &= (PCI_BASE_ADDRESS_IO_MASK);
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, (u32 *)&pdc2026x[0].alt_offset);
	pdc2026x[0].alt_offset &= PCI_BASE_ADDRESS_IO_MASK;
	pdc2026x[0].alt_offset = pdc2026x[0].alt_offset - pdc2026x[0].bus_offset;
	pdc2026x[0].name = &devicename[0];
	pdc2026x[0].channel = 0;
	if (ide_register_bus(&pdc2026x[0])) {
		printf("failed to register bus 0\n");
		return;
	}	
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_2, (u32 *)&pdc2026x[1].bus_offset);
	pdc2026x[1].bus_offset &= (PCI_BASE_ADDRESS_IO_MASK);
	pci_read_config_dword(dev, PCI_BASE_ADDRESS_3, (u32 *)&pdc2026x[1].alt_offset);
	pdc2026x[1].alt_offset &= PCI_BASE_ADDRESS_IO_MASK;
	pdc2026x[1].alt_offset = pdc2026x[1].alt_offset - pdc2026x[1].bus_offset;
	pdc2026x[1].name = &devicename[0];
	pdc2026x[1].channel = 1;
	if (ide_register_bus(&pdc2026x[1])) {
		printf("failed to register bus 1\n");
		return;
	}	
	for( a = 0; a < 4; a++)
		pdc2026x_new_tune_chipset(dev, a, XFER_PIO_4);
}
#endif
