/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2010 Advanced Micro Devices, Inc.
 *
 * 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; version 2 of the License.
 *
 * 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.
 */

/*
Scope (_SB) {
	Device(PCI0) {
		Device(IDEC) {
			Name(_ADR, 0x00140001)
			#include "ide.asl"
		}
	}
}
*/

/* Some timing tables */
Name(UDTT, Package(){ /* Udma timing table */
	120, 90, 60, 45, 30, 20, 15, 0	/* UDMA modes 0 -> 6 */
})

Name(MDTT, Package(){ /* MWDma timing table */
	480, 150, 120, 0	/* Legacy DMA modes 0 -> 2 */
})

Name(POTT, Package(){ /* Pio timing table */
	600, 390, 270, 180, 120, 0	/* PIO modes 0 -> 4 */
})

/* Some timing register value tables */
Name(MDRT, Package(){ /* MWDma timing register table */
	0x77, 0x21, 0x20, 0xFF	/* Legacy DMA modes 0 -> 2 */
})

Name(PORT, Package(){
	0x99, 0x47, 0x34, 0x22, 0x20, 0x99	/* PIO modes 0 -> 4 */
})

OperationRegion(ICRG, PCI_Config, 0x40, 0x20) /* ide control registers */
	Field(ICRG, AnyAcc, NoLock, Preserve)
{
	PPTS, 8,	/* Primary PIO Slave Timing */
	PPTM, 8,	/* Primary PIO Master Timing */
	OFFSET(0x04), PMTS, 8,	/* Primary MWDMA Slave Timing */
	PMTM, 8,	/* Primary MWDMA Master Timing */
	OFFSET(0x08), PPCR, 8,	/* Primary PIO Control */
	OFFSET(0x0A), PPMM, 4,	/* Primary PIO master Mode */
	PPSM, 4,	/* Primary PIO slave Mode */
	OFFSET(0x14), PDCR, 2,	/* Primary UDMA Control */
	OFFSET(0x16), PDMM, 4,	/* Primary UltraDMA Mode */
	PDSM, 4,	/* Primary UltraDMA Mode */
}

Method(GTTM, 1) /* get total time*/
{
	Store(And(Arg0, 0x0F), Local0)	/* Recovery Width */
	Increment(Local0)
	Store(ShiftRight(Arg0, 4), Local1)	/* Command Width */
	Increment(Local1)
	Return(Multiply(30, Add(Local0, Local1)))
}

Device(PRID)
{
	Name (_ADR, Zero)
	Method(_GTM, 0)
	{
		NAME(OTBF, Buffer(20) { /* out buffer */
			0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
		})

		CreateDwordField(OTBF, 0, PSD0)   /* PIO spd0 */
		CreateDwordField(OTBF, 4, DSD0)   /* DMA spd0 */
		CreateDwordField(OTBF, 8, PSD1)   /* PIO spd1 */
		CreateDwordField(OTBF, 12, DSD1) /* DMA spd1 */
		CreateDwordField(OTBF, 16, BFFG) /* buffer flags */

		/* Just return if the channel is disabled */
		If(And(PPCR, 0x01)) { /* primary PIO control */
			Return(OTBF)
		}

		/* Always tell them independent timing available and IOChannelReady used on both drives */
		Or(BFFG, 0x1A, BFFG)

		Store(GTTM(PPTM), PSD0) /* save total time of primary PIO master timming  to PIO spd0 */
		Store(GTTM(PPTS), PSD1) /* save total time of primary PIO slave Timing  to PIO spd1 */

		If(And(PDCR, 0x01)) {	/* It's under UDMA mode */
			Or(BFFG, 0x01, BFFG)
			Store(DerefOf(Index(UDTT, PDMM)), DSD0)
		}
		Else {
			Store(GTTM(PMTM), DSD0) /* Primary MWDMA Master Timing,  DmaSpd0 */
		}

		If(And(PDCR, 0x02)) {	/* It's under UDMA mode */
			Or(BFFG, 0x04, BFFG)
			Store(DerefOf(Index(UDTT, PDSM)), DSD1)
		}
		Else {
			Store(GTTM(PMTS), DSD1) /* Primary MWDMA Slave Timing,  DmaSpd0 */
		}

		Return(OTBF) /* out buffer */
	}				/* End Method(_GTM) */

	Method(_STM, 3, NotSerialized)
	{
		NAME(INBF, Buffer(20) { /* in buffer */
			0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF,
			0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00
		})

		CreateDwordField(INBF, 0, PSD0)    /* PIO spd0 */
		CreateDwordField(INBF, 4, DSD0)   /* PIO spd0 */
		CreateDwordField(INBF, 8, PSD1)   /* PIO spd1 */
		CreateDwordField(INBF, 12, DSD1) /* DMA spd1 */
		CreateDwordField(INBF, 16, BFFG) /*buffer flag */

		Store(Match(POTT, MLE, PSD0, MTR, 0, 0), Local0)
		Divide(Local0, 5, PPMM,) /* Primary PIO master Mode */
		Store(Match(POTT, MLE, PSD1, MTR, 0, 0), Local1)
		Divide(Local1, 5, PPSM,) /* Primary PIO slave Mode */

		Store(DerefOf(Index(PORT, Local0)), PPTM) /* Primary PIO Master Timing */
		Store(DerefOf(Index(PORT, Local1)), PPTS) /* Primary PIO Slave Timing */

		If(And(BFFG, 0x01)) {	/* Drive 0 is under UDMA mode */
			Store(Match(UDTT, MLE, DSD0, MTR, 0, 0), Local0)
			Divide(Local0, 7, PDMM,)
			Or(PDCR, 0x01, PDCR)
		}
		Else {
			If(LNotEqual(DSD0, 0xFFFFFFFF)) {
				Store(Match(MDTT, MLE, DSD0, MTR, 0, 0), Local0)
				Store(DerefOf(Index(MDRT, Local0)), PMTM)
			}
		}

		If(And(BFFG, 0x04)) {	/* Drive 1 is under UDMA mode */
			Store(Match(UDTT, MLE, DSD1, MTR, 0, 0), Local0)
			Divide(Local0, 7, PDSM,)
			Or(PDCR, 0x02, PDCR)
		}
		Else {
			If(LNotEqual(DSD1, 0xFFFFFFFF)) {
				Store(Match(MDTT, MLE, DSD1, MTR, 0, 0), Local0)
				Store(DerefOf(Index(MDRT, Local0)), PMTS)
			}
		}
		/* Return(INBF) */
	}		/*End Method(_STM) */
	Device(MST)
	{
		Name(_ADR, 0)
		Method(_GTF) {
			Name(CMBF, Buffer(21) {
				0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
				0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5
			})
			CreateByteField(CMBF, 1, POMD)
			CreateByteField(CMBF, 8, DMMD)
			CreateByteField(CMBF, 5, CMDA)
			CreateByteField(CMBF, 12, CMDB)
			CreateByteField(CMBF, 19, CMDC)

			Store(0xA0, CMDA)
			Store(0xA0, CMDB)
			Store(0xA0, CMDC)

			Or(PPMM, 0x08, POMD)

			If(And(PDCR, 0x01)) {
				Or(PDMM, 0x40, DMMD)
			}
			Else {
				Store(Match
				      (MDTT, MLE, GTTM(PMTM),
				       MTR, 0, 0), Local0)
				If(LLess(Local0, 3)) {
					Or(0x20, Local0, DMMD)
				}
			}
			Return(CMBF)
		}
	}		/* End Device(MST) */

	Device(SLAV)
	{
		Name(_ADR, 1)
		Method(_GTF) {
			Name(CMBF, Buffer(21) {
				0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
				0x03, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xEF,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5
			})
			CreateByteField(CMBF, 1, POMD)
			CreateByteField(CMBF, 8, DMMD)
			CreateByteField(CMBF, 5, CMDA)
			CreateByteField(CMBF, 12, CMDB)
			CreateByteField(CMBF, 19, CMDC)

			Store(0xB0, CMDA)
			Store(0xB0, CMDB)
			Store(0xB0, CMDC)

			Or(PPSM, 0x08, POMD)

			If(And(PDCR, 0x02)) {
				Or(PDSM, 0x40, DMMD)
			}
			Else {
				Store(Match
				      (MDTT, MLE, GTTM(PMTS),
				       MTR, 0, 0), Local0)
				If(LLess(Local0, 3)) {
					Or(0x20, Local0, DMMD)
				}
			}
			Return(CMBF)
		}
	}			/* End Device(SLAV) */
}