/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2007-2009 coresystems GmbH
 *
 * 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.
 */

/* Intel Cougar Point USB support */

// EHCI Controller 0:1d.0

Device (EHCI)
{
	Name(_ADR, 0x001d0000)

	Name (PRWH, Package(){ 0x0d, 3 }) // LPT-H
	Name (PRWL, Package(){ 0x6d, 3 }) // LPT-LP

	Method (_PRW, 0) { // Power Resources for Wake
		If (\ISLP ()) {
			Return (PRWL)
		} Else {
			Return (PRWH)
		}
	}

	// Leave USB ports on for to allow Wake from USB

	Method(_S3D,0)	// Highest D State in S3 State
	{
		Return (2)
	}

	Method(_S4D,0)	// Highest D State in S4 State
	{
		Return (2)
	}

	Device (HUB7)
	{
		Name (_ADR, 0x00000000)

		Device (PRT1) { Name (_ADR, 1) } // USB Port 0
		Device (PRT2) { Name (_ADR, 2) } // USB Port 1
		Device (PRT3) { Name (_ADR, 3) } // USB Port 2
		Device (PRT4) { Name (_ADR, 4) } // USB Port 3
		Device (PRT5) { Name (_ADR, 5) } // USB Port 4
		Device (PRT6) { Name (_ADR, 6) } // USB Port 5
	}
}

// XHCI Controller 0:14.0

Device (XHCI)
{
	Name (_ADR, 0x00140000)

	Name (PLSD, 5) // Port Link State - RxDetect
	Name (PLSP, 7) // Port Link State - Polling

	OperationRegion (XPRT, PCI_Config, 0x00, 0x100)
	Field (XPRT, AnyAcc, NoLock, Preserve)
	{
		Offset (0x0),
		DVID, 16,
		Offset (0x10),
		, 16,
		XMEM, 16, // MEM_BASE
		Offset (0x74),
		D0D3, 2,
		, 6,
		PMEE, 1,  // PME_EN
		, 6,
		PMES, 1,  // PME_STS
		Offset (0xb0),
		, 13,
		MB13, 1,
		MB14, 1,
		Offset (0xd0),
		PR2R, 32,  // USB2PR
		PR2M, 32,  // USB2PRM
		PR3R, 32,  // USB3PR
		PR3M, 32,  // USB3PRM
	}

	// Clear status bits
	Method (LPCL, 0, Serialized)
	{
		OperationRegion (XREG, SystemMemory,
				 ShiftLeft (^XMEM, 16), 0x600)
		Field (XREG, DWordAcc, Lock, Preserve)
		{
			Offset (0x510), // PORTSCNUSB3[0]
			PSC0, 32,
			Offset (0x520), // PORTSCNUSB3[1]
			PSC1, 32,
			Offset (0x530), // PORTSCNUSB3[2]
			PSC2, 32,
			Offset (0x540), // PORTSCNUSB3[3]
			PSC3, 32,
		}

		// Port Enabled/Disabled (Bit 1)
		Name (PEDB, ShiftLeft (1, 1))

		// Change Status (Bits 23:17)
		Name (CHST, ShiftLeft (0x7f, 17))

		// Port 0
		And (PSC0, Not (PEDB), Local0)
		Or (Local0, CHST, PSC0)

		// Port 1
		And (PSC1, Not (PEDB), Local0)
		Or (Local0, CHST, PSC1)

		// Port 2
		And (PSC2, Not (PEDB), Local0)
		Or (Local0, CHST, PSC2)

		// Port 3
		And (PSC3, Not (PEDB), Local0)
		Or (Local0, CHST, PSC3)
	}

	Method (LPS0, 0, Serialized)
	{
		OperationRegion (XREG, SystemMemory,
				 ShiftLeft (^XMEM, 16), 0x600)
		Field (XREG, DWordAcc, Lock, Preserve)
		{
			Offset (0x510), // PORTSCNUSB3
			, 5,
			PLS1, 4,	// [8:5] Port Link State
			PPR1, 1,	// [9] Port Power
			, 7,
			CSC1, 1,	// [17] Connect Status Change
			, 1,
			WRC1, 1,	// [19] Warm Port Reset Change
			, 11,
			WPR1, 1,	// [31] Warm Port Reset
			Offset (0x520), // PORTSCNUSB3
			, 5,
			PLS2, 4,	// [8:5] Port Link State
			PPR2, 1,	// [9] Port Power
			, 7,
			CSC2, 1,	// [17] Connect Status Change
			, 1,
			WRC2, 1,	// [19] Warm Port Reset Change
			, 11,
			WPR2, 1,	// [31] Warm Port Reset
			Offset (0x530), // PORTSCNUSB3
			, 5,
			PLS3, 4,	// [8:5] Port Link State
			PPR3, 1,	// [9] Port Power
			, 7,
			CSC3, 1,	// [17] Connect Status Change
			, 1,
			WRC3, 1,	// [19] Warm Port Reset Change
			, 11,
			WPR3, 1,	// [31] Warm Port Reset
			Offset (0x540), // PORTSCNUSB3
			, 5,
			PLS4, 4,	// [8:5] Port Link State
			PPR4, 1,	// [9] Port Power
			, 7,
			CSC4, 1,	// [17] Connect Status Change
			, 1,
			WRC4, 1,	// [19] Warm Port Reset Change
			, 11,
			WPR4, 1,	// [31] Warm Port Reset
		}

		// Wait for all powered ports to finish polling
		Store (10, Local0)
		While (LOr (LOr (LAnd (LEqual (PPR1, 1), LEqual (PLS1, PLSP)),
				 LAnd (LEqual (PPR2, 1), LEqual (PLS2, PLSP))),
			    LOr (LAnd (LEqual (PPR3, 1), LEqual (PLS3, PLSP)),
				 LAnd (LEqual (PPR4, 1), LEqual (PLS4, PLSP)))))
		{
			If (LEqual (Local0, 0)) {
				Break
			}
			Decrement (Local0)
			Stall (10)
		}

		// For each USB3 Port:
		//   If port is disconnected (PLS=5 PP=1 CSC=0)
		//     1) Issue warm reset (WPR=1)
		//     2) Poll for warm reset complete (WRC=0)
		//     3) Write 1 to port status to clear

		// Local# indicate if port is reset
		Store (0, Local1)
		Store (0, Local2)
		Store (0, Local3)
		Store (0, Local4)

		If (LAnd (LEqual (PLS1, PLSD),
		          LAnd (LEqual (CSC1, 0), LEqual (PPR1, 1)))) {
			Store (1, WPR1)	      // Issue warm reset
			Store (1, Local1)
		}
		If (LAnd (LEqual (PLS2, PLSD),
		          LAnd (LEqual (CSC2, 0), LEqual (PPR2, 1)))) {
			Store (1, WPR2)	      // Issue warm reset
			Store (1, Local2)
		}
		If (LAnd (LEqual (PLS3, PLSD),
		          LAnd (LEqual (CSC3, 0), LEqual (PPR3, 1)))) {
			Store (1, WPR3)	      // Issue warm reset
			Store (1, Local3)
		}
		If (LAnd (LEqual (PLS4, PLSD),
		          LAnd (LEqual (CSC4, 0), LEqual (PPR4, 1)))) {
			Store (1, WPR4)	      // Issue warm reset
			Store (1, Local4)
		}

		// Poll for warm reset complete on all ports that were reset
		Store (10, Local0)
		While (LOr (LOr (LAnd (LEqual (Local1, 1), LEqual (WRC1, 0)),
				 LAnd (LEqual (Local2, 1), LEqual (WRC2, 0))),
			    LOr (LAnd (LEqual (Local3, 1), LEqual (WRC3, 0)),
			         LAnd (LEqual (Local4, 1), LEqual (WRC4, 0)))))
		{
			If (LEqual (Local0, 0)) {
				Break
			}
			Decrement (Local0)
			Stall (10)
		}

		// Clear status bits in all ports
		LPCL ()
	}

	Method (_PSC, 0, NotSerialized)
	{
		Return (^D0D3)
	}

	Method (_PS0, 0, Serialized)
	{
		If (LEqual (^DVID, 0xFFFF)) {
			Return ()
		}
		If (LOr (LEqual (^XMEM, 0xFFFF), LEqual (^XMEM, 0x0000))) {
			Return ()
		}

		OperationRegion (XREG, SystemMemory,
				 Add (ShiftLeft (^XMEM, 16), 0x8000), 0x200)
		Field (XREG, DWordAcc, Lock, Preserve)
		{
			Offset (0x0e0), // AUX Reset Control 1
			, 15,
			AX15, 1,
			Offset (0x154), // AUX Domain PM Control Register 2
			, 31,
			CLK2, 1,
			Offset (0x16c), // AUX Clock Control
			, 2,
			CLK0, 1,
			, 11,
			CLK1, 1, // USB3 Port Aux/Core Clock Gating Enable
		}

		// If device is in D3, set back to D0
		Store (^D0D3, Local0)
		if (LEqual (Local0, 3)) {
			Store (0, ^D0D3)
		}

		If (\ISLP ()) {
			// Clear PCI 0xB0[14:13]
			Store (0, ^MB13)
			Store (0, ^MB14)

			// Clear MMIO 0x816C[14,2]
			Store (0, CLK0)
			Store (0, CLK1)
		}

		// Set MMIO 0x8154[31]
		Store (1, CLK2)

		If (\ISLP ()) {
			// Handle per-port reset if needed
			LPS0 ()

			// Set MMIO 0x80e0[15]
			Store (1, AX15)
		}

		Return ()
	}

	Method (_PS3, 0, Serialized)
	{
		If (LEqual (^DVID, 0xFFFF)) {
			Return ()
		}
		If (LOr (LEqual (^XMEM, 0xFFFF), LEqual (^XMEM, 0x0000))) {
			Return ()
		}

		OperationRegion (XREG, SystemMemory,
				 Add (ShiftLeft (^XMEM, 16), 0x8000), 0x200)
		Field (XREG, DWordAcc, Lock, Preserve)
		{
			Offset (0x0e0), // AUX Reset Control 1
			, 15,
			AX15, 1,
			Offset (0x154), // AUX Domain PM Control Register 2
			, 31,
			CLK2, 1,
			Offset (0x16c), // AUX Clock Control
			, 2,
			CLK0, 1,
			, 11,
			CLK1, 1, // USB3 Port Aux/Core Clock Gating Enable
		}

		Store (1, ^PMES) // Clear PME Status
		Store (1, ^PMEE) // Enable PME

		// If device is in D3, set back to D0
		Store (^D0D3, Local0)
		if (LEqual (Local0, 3)) {
			Store (0, ^D0D3)
		}

		If (\ISLP ()) {
			// Set PCI 0xB0[14:13]
			Store (1, ^MB13)
			Store (1, ^MB14)

			// Set MMIO 0x816C[14,2]
			Store (1, CLK0)
			Store (1, CLK1)
		}

		// Clear MMIO 0x8154[31]
		Store (0, CLK2)

		If (\ISLP ()) {
			// Clear MMIO 0x80e0[15]
			Store (0, AX15)
		}

		// Put device in D3
		Store (3, ^D0D3)

		Return ()
	}

	Name (PRWH, Package(){ 0x0d, 3 }) // LPT-H
	Name (PRWL, Package(){ 0x6d, 3 }) // LPT-LP

	Method (_PRW, 0) { // Power Resources for Wake
		If (\ISLP ()) {
			Return (PRWL)
		} Else {
			Return (PRWH)
		}
	}

	// Leave USB ports on for to allow Wake from USB

	Method(_S3D,0)	// Highest D State in S3 State
	{
		Return (3)
	}

	Method(_S4D,0)	// Highest D State in S4 State
	{
		Return (3)
	}

	Device (HUB7)
	{
		Name (_ADR, 0x00000000)

		// GPLD: Generate Port Location Data (PLD)
		Method (GPLD, 1, Serialized) {
			Name (PCKG, Package (0x01) {
				Buffer (0x10) {}
			})

			// REV: Revision 0x02 for ACPI 5.0
			CreateField (DerefOf (Index (PCKG, Zero)), Zero, 0x07, REV)
			Store (0x02, REV)

			// VISI: Port visibility to user per port
			CreateField (DerefOf (Index (PCKG, Zero)), 0x40, One, VISI)
			Store (Arg0, VISI)
			Return (PCKG)
		}

		Device (PRT1) { Name (_ADR, 1) } // USB Port 0
		Device (PRT2) { Name (_ADR, 2) } // USB Port 1
		Device (PRT3) { Name (_ADR, 3) } // USB Port 2
		Device (PRT4) { Name (_ADR, 4) } // USB Port 3
		Device (PRT5) { Name (_ADR, 5) } // USB Port 4
		Device (PRT6) { Name (_ADR, 6) } // USB Port 5
		Device (PRT7) { Name (_ADR, 7) } // USB Port 6
		Device (SSP1) { Name (_ADR, 10) } // USB Port 10
		Device (SSP2) { Name (_ADR, 11) } // USB Port 11
		Device (SSP3) { Name (_ADR, 12) } // USB Port 12
		Device (SSP4) { Name (_ADR, 13) } // USB Port 13
	}
}