From ccebca23a1e58cc82d9e2d48642ee604ebbcd3b9 Mon Sep 17 00:00:00 2001
From: Timothy Pearson <tpearson@raptorengineeringinc.com>
Date: Sat, 5 Sep 2015 17:46:38 -0500
Subject: [PATCH 008/143] southbridge/amd/sr5650: Fix boot failure on ASUS
 KGPE-D16

Change-Id: Ia13ba58118a826e830a4dc6e2378b76110fcabad
Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com>
---
 src/southbridge/amd/sr5650/acpi/sr5650.asl |  388 ++++++++++++++++++++++++++++
 src/southbridge/amd/sr5650/early_setup.c   |    7 +-
 src/southbridge/amd/sr5650/ht.c            |    3 +-
 src/southbridge/amd/sr5650/pcie.c          |   37 ++-
 src/southbridge/amd/sr5650/sr5650.c        |   51 ++--
 5 files changed, 456 insertions(+), 30 deletions(-)
 create mode 100644 src/southbridge/amd/sr5650/acpi/sr5650.asl

diff --git a/src/southbridge/amd/sr5650/acpi/sr5650.asl b/src/southbridge/amd/sr5650/acpi/sr5650.asl
new file mode 100644
index 0000000..a6ab114
--- /dev/null
+++ b/src/southbridge/amd/sr5650/acpi/sr5650.asl
@@ -0,0 +1,388 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
+ * Copyright (C) 2009 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.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+Scope(\) {
+	Name(PCBA, 0xE0000000)	/* Base address of PCIe config space */
+	Name(HPBA, 0xFED00000)	/* Base address of HPET table */
+
+	/* PIC IRQ mapping registers, C00h-C01h */
+	OperationRegion(PRQM, SystemIO, 0x00000C00, 0x00000002)
+		Field(PRQM, ByteAcc, NoLock, Preserve) {
+		PRQI, 0x00000008,
+		PRQD, 0x00000008,  /* Offset: 1h */
+	}
+	IndexField(PRQI, PRQD, ByteAcc, NoLock, Preserve) {
+		PINA, 0x00000008,	/* Index 0 */
+		PINB, 0x00000008,	/* Index 1 */
+		PINC, 0x00000008,	/* Index 2 */
+		PIND, 0x00000008,	/* Index 3 */
+		AINT, 0x00000008,	/* Index 4 */
+		SINT, 0x00000008,	/* Index 5 */
+		    , 0x00000008,	/* Index 6 */
+		AAUD, 0x00000008,	/* Index 7 */
+		AMOD, 0x00000008,	/* Index 8 */
+		PINE, 0x00000008,	/* Index 9 */
+		PINF, 0x00000008,	/* Index A */
+		PING, 0x00000008,	/* Index B */
+		PINH, 0x00000008,	/* Index C */
+	}
+
+	/* PCI Error control register */
+	OperationRegion(PERC, SystemIO, 0x00000C14, 0x00000001)
+		Field(PERC, ByteAcc, NoLock, Preserve) {
+		SENS, 0x00000001,
+		PENS, 0x00000001,
+		SENE, 0x00000001,
+		PENE, 0x00000001,
+	}
+
+	Scope(\_SB) {
+		/* PCIe Configuration Space for 16 busses */
+		OperationRegion(PCFG, SystemMemory, PCBA, 0x01000000) /* Each bus consumes 1MB */
+			Field(PCFG, ByteAcc, NoLock, Preserve) {
+			/* Byte offsets are computed using the following technique:
+			 * ((bus number + 1) * ((device number * 8) * 4096)) + register offset
+			 * The 8 comes from 8 functions per device, and 4096 bytes per function config space
+			*/
+			Offset(0x00088024),	/* Byte offset to SATA register 24h - Bus 0, Device 17, Function 0 */
+			STB5, 32,
+			Offset(0x00098042),	/* Byte offset to OHCI0 register 42h - Bus 0, Device 19, Function 0 */
+			PT0D, 1,
+			PT1D, 1,
+			PT2D, 1,
+			PT3D, 1,
+			PT4D, 1,
+			PT5D, 1,
+			PT6D, 1,
+			PT7D, 1,
+			PT8D, 1,
+			PT9D, 1,
+			Offset(0x000A0004),	/* Byte offset to SMBUS	register 4h - Bus 0, Device 20, Function 0 */
+			SBIE, 1,
+			SBME, 1,
+			Offset(0x000A0008),	/* Byte offset to SMBUS	register 8h - Bus 0, Device 20, Function 0 */
+			SBRI, 8,
+			Offset(0x000A0014),	/* Byte offset to SMBUS	register 14h - Bus 0, Device 20, Function 0 */
+			SBB1, 32,
+			Offset(0x000A0078),	/* Byte offset to SMBUS	register 78h - Bus 0, Device 20, Function 0 */
+			,14,
+			P92E, 1,		/* Port92 decode enable */
+		}
+
+		OperationRegion(SB5, SystemMemory, STB5, 0x1000)
+			Field(SB5, AnyAcc, NoLock, Preserve){
+			/* Port 0 */
+			Offset(0x120),		/* Port 0 Task file status */
+			P0ER, 1,
+			, 2,
+			P0DQ, 1,
+			, 3,
+			P0BY, 1,
+			Offset(0x128),		/* Port 0 Serial ATA status */
+			P0DD, 4,
+			, 4,
+			P0IS, 4,
+			Offset(0x12C),		/* Port 0 Serial ATA control */
+			P0DI, 4,
+			Offset(0x130),		/* Port 0 Serial ATA error */
+			, 16,
+			P0PR, 1,
+
+			/* Port 1 */
+			offset(0x1A0),		/* Port 1 Task file status */
+			P1ER, 1,
+			, 2,
+			P1DQ, 1,
+			, 3,
+			P1BY, 1,
+			Offset(0x1A8),		/* Port 1 Serial ATA status */
+			P1DD, 4,
+			, 4,
+			P1IS, 4,
+			Offset(0x1AC),		/* Port 1 Serial ATA control */
+			P1DI, 4,
+			Offset(0x1B0),		/* Port 1 Serial ATA error */
+			, 16,
+			P1PR, 1,
+
+			/* Port 2 */
+			Offset(0x220),		/* Port 2 Task file status */
+			P2ER, 1,
+			, 2,
+			P2DQ, 1,
+			, 3,
+			P2BY, 1,
+			Offset(0x228),		/* Port 2 Serial ATA status */
+			P2DD, 4,
+			, 4,
+			P2IS, 4,
+			Offset(0x22C),		/* Port 2 Serial ATA control */
+			P2DI, 4,
+			Offset(0x230),		/* Port 2 Serial ATA error */
+			, 16,
+			P2PR, 1,
+
+			/* Port 3 */
+			Offset(0x2A0),		/* Port 3 Task file status */
+			P3ER, 1,
+			, 2,
+			P3DQ, 1,
+			, 3,
+			P3BY, 1,
+			Offset(0x2A8),		/* Port 3 Serial ATA status */
+			P3DD, 4,
+			, 4,
+			P3IS, 4,
+			Offset(0x2AC),		/* Port 3 Serial ATA control */
+			P3DI, 4,
+			Offset(0x2B0),		/* Port 3 Serial ATA error */
+			, 16,
+			P3PR, 1,
+		}
+
+		Method(CIRQ, 0x00, NotSerialized){
+			Store(0, PINA)
+			Store(0, PINB)
+			Store(0, PINC)
+			Store(0, PIND)
+			Store(0, PINE)
+			Store(0, PINF)
+			Store(0, PING)
+			Store(0, PINH)
+		}
+
+		/* set "A", 8259 interrupts */
+		Name (PRSA, ResourceTemplate () {
+			IRQ(Level, ActiveLow, Exclusive) {4, 7, 10, 11, 12, 14, 15}
+		})
+
+		Method (CRSA, 1, Serialized) {
+			Name (LRTL, ResourceTemplate() {
+				IRQ(Level, ActiveLow, Shared) {15}
+			})
+			CreateWordField(LRTL, 1, LIRQ)
+			ShiftLeft(1, Arg0, LIRQ)
+			Return (LRTL)
+		}
+
+		Method (SRSA, 1, Serialized) {
+			CreateWordField(Arg0, 1, LIRQ)
+			FindSetRightBit(LIRQ, Local0)
+			if (Local0) {
+				Decrement(Local0)
+			}
+			Return (Local0)
+		}
+
+		Device(LNKA) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 1)
+			Method(_STA, 0) {
+				if (PINA) {
+					Return(0x0B) /* LNKA is invisible */
+				} else {
+					Return(0x09) /* LNKA is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PINA)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PINA))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PINA)
+			}
+		}
+
+		Device(LNKB) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 2)
+			Method(_STA, 0) {
+				if (PINB) {
+					Return(0x0B) /* LNKB is invisible */
+				} else {
+					Return(0x09) /* LNKB is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PINB)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PINB))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PINB)
+			}
+		}
+
+		Device(LNKC) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 3)
+			Method(_STA, 0) {
+				if (PINC) {
+					Return(0x0B) /* LNKC is invisible */
+				} else {
+					Return(0x09) /* LNKC is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PINC)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PINC))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PINC)
+			}
+		}
+
+		Device(LNKD) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 4)
+			Method(_STA, 0) {
+				if (PIND) {
+					Return(0x0B) /* LNKD is invisible */
+				} else {
+					Return(0x09) /* LNKD is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PIND)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PIND))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PIND)
+			}
+		}
+
+		Device(LNKE) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 5)
+			Method(_STA, 0) {
+				if (PINE) {
+					Return(0x0B) /* LNKE is invisible */
+				} else {
+					Return(0x09) /* LNKE is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PINE)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PINE))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PINE)
+			}
+		}
+
+		Device(LNKF) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 6)
+			Method(_STA, 0) {
+				if (PINF) {
+					Return(0x0B) /* LNKF is invisible */
+				} else {
+					Return(0x09) /* LNKF is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PINF)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PINF))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PINF)
+			}
+		}
+
+		Device(LNKG) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 7)
+			Method(_STA, 0) {
+				if (PING) {
+					Return(0x0B) /* LNKG is invisible */
+				} else {
+					Return(0x09) /* LNKG is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PING)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PING))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PING)
+			}
+		}
+
+		Device(LNKH) {
+			Name(_HID, EISAID("PNP0C0F"))
+			Name(_UID, 8)
+			Method(_STA, 0) {
+				if (PINH) {
+					Return(0x0B) /* LNKH is invisible */
+				} else {
+					Return(0x09) /* LNKH is disabled */
+				}
+			}
+			Method(_DIS, 0) {
+				Store(0, PINH)
+			}
+			Method(_PRS, 0) {
+				Return (PRSA)
+			}
+			Method (_CRS, 0, Serialized) {
+				Return (CRSA(PINH))
+			}
+			Method (_SRS, 1, Serialized) {
+				Store (SRSA(Arg0), PINH)
+			}
+		}
+
+	}   /* End Scope(_SB)  */
+
+}  /* End Scope(/)  */
diff --git a/src/southbridge/amd/sr5650/early_setup.c b/src/southbridge/amd/sr5650/early_setup.c
index ec555f8..664f60a 100644
--- a/src/southbridge/amd/sr5650/early_setup.c
+++ b/src/southbridge/amd/sr5650/early_setup.c
@@ -3,6 +3,7 @@
  *
  * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * 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
@@ -504,7 +505,8 @@ void sr5650_early_setup(void)
 	/*ATINB_PrepareInit */
 	get_cpu_rev();
 
-	switch (get_nb_rev(nb_dev)) {	/* PCIEMiscInit */
+	uint8_t revno = get_nb_rev(nb_dev);
+	switch (revno) {	/* PCIEMiscInit */
 	case REV_SR5650_A11:
 		printk(BIOS_INFO, "NB Revision is A11.\n");
 		break;
@@ -514,6 +516,9 @@ void sr5650_early_setup(void)
 	case REV_SR5650_A21:
 		printk(BIOS_INFO, "NB Revision is A21.\n");
 		break;
+	default:
+		printk(BIOS_INFO, "NB Revision is %02x (Unrecognized).\n", revno);
+		break;
 	}
 
 	fam10_optimization();
diff --git a/src/southbridge/amd/sr5650/ht.c b/src/southbridge/amd/sr5650/ht.c
index c497107..02f4f7f 100644
--- a/src/southbridge/amd/sr5650/ht.c
+++ b/src/southbridge/amd/sr5650/ht.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * 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
@@ -55,7 +56,7 @@ static const apic_device_info default_apic_device_info_t [] = {
 	[13] = {4,     ABCD,       30}    /* Dev13 Grp4 [Int - 16..19] */
 };
 
-/* Their name are quite regular. So I undefine them. */
+/* These define names are common, so undefine them to avoid potential issues in other code */
 #undef ABCD
 #undef BCDA
 #undef CDAB
diff --git a/src/southbridge/amd/sr5650/pcie.c b/src/southbridge/amd/sr5650/pcie.c
index 3720a61..d306b5a 100644
--- a/src/southbridge/amd/sr5650/pcie.c
+++ b/src/southbridge/amd/sr5650/pcie.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * 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
@@ -61,8 +62,10 @@ static void ValidatePortEn(device_t nb_dev)
 *****************************************************************/
 static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port)
 {
+	printk(BIOS_DEBUG, "PciePowerOffGppPorts() port %d\n", port);
 	u32 reg;
 	u16 state_save;
+	uint8_t i;
 	struct southbridge_amd_sr5650_config *cfg =
 		(struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
 	u16 state = cfg->port_enable;
@@ -72,6 +75,28 @@ static void PciePowerOffGppPorts(device_t nb_dev, device_t dev, u32 port)
 	state = ~state;
 	state &= (1 << 4) + (1 << 5) + (1 << 6) + (1 << 7);
 	state_save = state << 17;
+	/* Disable ports any that failed training */
+	for (i = 9; i <= 13; i++) {
+		if (!(AtiPcieCfg.PortDetect & 1 << i)) {
+			if ((port >= 9) && (port <= 13)) {
+				state |= (1 << (port + 7));
+			}
+			if (port == 9)
+				state_save |= 1 << 25;
+			if (port == 10)
+				state_save |= 1 << 26;
+			if (port == 11)
+				state_save |= 1 << 6;
+			if (port == 12)
+				state_save |= 1 << 7;
+
+			if (port == 13) {
+				reg = nbmisc_read_index(nb_dev, 0x2a);
+				reg |= 1 << 4;
+				nbmisc_write_index(nb_dev, 0x2a, reg);
+			}
+		}
+	}
 	state &= !(AtiPcieCfg.PortHp);
 	reg = nbmisc_read_index(nb_dev, 0x0c);
 	reg |= state;
@@ -483,6 +508,8 @@ static void EnableLclkGating(device_t dev)
 *****************************************/
 void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
 {
+	uint8_t training_ok = 1;
+
 	u32 gpp_sb_sel = 0;
 	struct southbridge_amd_sr5650_config *cfg =
 	    (struct southbridge_amd_sr5650_config *)nb_dev->chip_info;
@@ -701,6 +728,12 @@ void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
 				printk(BIOS_DEBUG, "PcieTrainPort port=0x%x result=%d\n", port, res);
 				if (res) {
 					AtiPcieCfg.PortDetect |= 1 << port;
+				} else {
+					/* If the training failed the disable the bridge to prevent subsequent
+					 * lockup on bridge configuration register read during the PCI bus scan
+					 */
+					training_ok = 0;
+					dev->enabled = 0;
 				}
 			}
 		}
@@ -747,8 +780,8 @@ void sr5650_gpp_sb_init(device_t nb_dev, device_t dev, u32 port)
 	 * wait dev 0x6B bit3 clear
 	 */
 
-	if (port == 8){
-		PciePowerOffGppPorts(nb_dev, dev, port); /* , This should be run for all ports that are not hotplug and don't detect devices */
+	if ((port == 8) || (!training_ok)) {
+		PciePowerOffGppPorts(nb_dev, dev, port);	/* This is run for all ports that are not hotplug and don't detect devices */
 	}
 }
 
diff --git a/src/southbridge/amd/sr5650/sr5650.c b/src/southbridge/amd/sr5650/sr5650.c
index 441be66..75383de 100644
--- a/src/southbridge/amd/sr5650/sr5650.c
+++ b/src/southbridge/amd/sr5650/sr5650.c
@@ -2,6 +2,7 @@
  * This file is part of the coreboot project.
  *
  * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
  *
  * 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
@@ -95,32 +96,30 @@ void nbpcie_ind_write_index(device_t nb_dev, u32 index, u32 data)
 void ProgK8TempMmioBase(u8 in_out, u32 pcie_base_add, u32 mmio_base_add)
 {
 	/* K8 Function1 is address map */
-	device_t k8_f1;
-	device_t np = dev_find_slot(0, PCI_DEVFN(0x19, 1));
-	u16 node;
-
-	for (node = 0; node < CONFIG_MAX_PHYSICAL_CPUS; node++) {
-		k8_f1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1));
-		if (!k8_f1) {
-			break;
-		}
-
-		if (in_out) {
-			/* Fill MMIO limit/base pair. */
-			pci_write_config32(k8_f1, 0xbc,
-					   (((pcie_base_add + 0x10000000 -
-					      1) >> 8) & 0xffffff00) | 0x8 | (np ? 2 << 4 : 0 << 4));
-			pci_write_config32(k8_f1, 0xb8, (pcie_base_add >> 8) | 0x3);
-			pci_write_config32(k8_f1, 0xb4,
-					   ((mmio_base_add + 0x10000000 -
-					     1) >> 8) | (np ? 2 << 4 : 0 << 4));
-			pci_write_config32(k8_f1, 0xb0, (mmio_base_add >> 8) | 0x3);
-		} else {
-			pci_write_config32(k8_f1, 0xb8, 0);
-			pci_write_config32(k8_f1, 0xbc, 0);
-			pci_write_config32(k8_f1, 0xb0, 0);
-			pci_write_config32(k8_f1, 0xb4, 0);
-		}
+	device_t k8_f1 = dev_find_slot(0, PCI_DEVFN(0x18, 1));
+	device_t k8_f0 = dev_find_slot(0, PCI_DEVFN(0x18, 0));
+
+	if (in_out) {
+		u32 dword, sblk;
+
+		/* Get SBLink value (HyperTransport I/O Hub Link ID). */
+		dword = pci_read_config32(k8_f0, 0x64);
+		sblk = (dword >> 8) & 0x3;
+
+		/* Fill MMIO limit/base pair. */
+		pci_write_config32(k8_f1, 0xbc,
+				   (((pcie_base_add + 0x10000000 -
+				     1) >> 8) & 0xffffff00) | 0x80 | (sblk << 4));
+		pci_write_config32(k8_f1, 0xb8, (pcie_base_add >> 8) | 0x3);
+		pci_write_config32(k8_f1, 0xb4,
+				   (((mmio_base_add + 0x10000000 -
+				     1) >> 8) & 0xffffff00) | (sblk << 4));
+		pci_write_config32(k8_f1, 0xb0, (mmio_base_add >> 8) | 0x3);
+	} else {
+		pci_write_config32(k8_f1, 0xb8, 0);
+		pci_write_config32(k8_f1, 0xbc, 0);
+		pci_write_config32(k8_f1, 0xb0, 0);
+		pci_write_config32(k8_f1, 0xb4, 0);
 	}
 }
 
-- 
1.7.9.5

